]> git.pond.sub.org Git - empserver/blob - src/server/main.c
f7b511e6a04cd8f1b37afe738efa13dbeb1842c3
[empserver] / src / server / main.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2004, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
23  *  related information and legal notices. It is expected that any future
24  *  projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  main.c: Thread and signal initialization for Empire Server
29  * 
30  *  Known contributors to this file:
31  *     Dave Pare, 1994
32  *     Steve McClure, 1996, 1998
33  *     Doug Hay, 1998
34  */
35
36 #if defined(aix) || defined(linux)
37 #include <unistd.h>
38 #endif /* aix or linux */
39
40 #include <signal.h>
41 #if !defined(_WIN32)
42 #include <sys/ioctl.h>
43 #endif
44 #include <fcntl.h>
45 #include <stdio.h>
46 #include <string.h>
47
48 #if defined(_WIN32)
49 #include <winsock2.h>
50 #include <process.h>
51 #include "../lib/gen/getopt.h"
52 #include "service.h"
53 #endif
54
55 #include "misc.h"
56 #include "nat.h"
57 #include "file.h"
58 #include "player.h"
59 #include "empthread.h"
60 #include "plane.h"
61 #include "nuke.h"
62 #include "land.h"
63 #include "ship.h"
64 #include "sect.h"
65 #include "product.h"
66 #include "optlist.h"
67 #include "server.h"
68 #include "prototypes.h"
69
70 static void nullify_objects(void);
71 static void init_files(void);
72
73 #if defined(_WIN32)
74 static void loc_NTInit(void);
75 #endif
76
77 static int mainpid = 0;
78
79 /*
80  * Debugging?
81  * If yes, don't fork into background, don't catch certain signals,
82  * call abort() on internal error.
83  */
84 int debug = 0;
85
86 static void
87 print_usage(char *program_name)
88 {
89 #if defined(_WIN32)
90     printf("Usage: %s -i -I service_name -r -R service_name -D datadir -e config_file -d\n", program_name);
91     printf("-i install service with the default name %s\n", DEFAULT_SERVICE_NAME);
92     printf("-r remove service with the default name %s\n", DEFAULT_SERVICE_NAME);
93 #else
94     printf("Usage: %s -D datadir -e config_file -d -p -s\n", program_name);
95     printf("-p print flag\n");
96     printf("-s stack check flag (include print flag)\n");
97 #endif
98     printf("-d debug mode\n");
99 }
100
101 int
102 main(int argc, char **argv)
103 {
104     int flags = 0;
105 #if defined(_WIN32)
106     int install_service_set = 0;
107     char *service_name = NULL;
108     int remove_service_set = 0;
109     int datadir_set = 0;
110 #else
111     char *config_file = NULL;
112 #endif
113     int op;
114     s_char tbuf[256];
115
116     mainpid = getpid();
117
118 #if defined(_WIN32)
119     while ((op = getopt(argc, argv, "D:de:iI:rR:h")) != EOF) {
120 #else
121     while ((op = getopt(argc, argv, "D:de:psh")) != EOF) {
122 #endif
123         switch (op) {
124         case 'D':
125             datadir = optarg;
126 #if defined(_WIN32)
127             datadir_set++;
128 #endif
129             break;
130         case 'd':
131             debug++;
132             break;
133         case 'e':
134             config_file = optarg;
135             break;
136 #if defined(_WIN32)
137         case 'I':
138             service_name = optarg;
139             /*
140              * fall out
141              */
142         case 'i':
143             install_service_set++;
144             break;
145             break;
146         case 'R':
147             service_name = optarg;
148             /*
149              * fall out
150              */
151         case 'r':
152             remove_service_set++;
153             break;
154 #else
155         case 'p':
156             flags |= EMPTH_PRINT;
157             break;
158         case 's':
159             flags |= EMPTH_PRINT | EMPTH_STACKCHECK;
160             break;
161 #endif
162         case 'h':
163         default:
164             print_usage(argv[0]);
165             return EXIT_FAILURE;
166         }
167     }
168
169     loginit("server");
170
171 #if defined(_WIN32)
172     if ((debug || datadir_set || config_file != NULL) &&
173         remove_service_set) {
174         logerror("Can't use -d, -D or -e with either "
175             "-r or -R options when starting the server");
176         printf("Can't use -d, -D or -e with either -r "
177             "or -R options\n");
178         exit(EXIT_FAILURE);
179     }
180     if (debug && install_service_set) {
181         logerror("Can't use -d with either "
182             "-i or -I options when starting the server");
183         printf("Can't use -d with either -i "
184             "or -I options\n");
185         exit(EXIT_FAILURE);
186     }
187     if (install_service_set && remove_service_set) {
188         logerror("Can't use both -r or -R and -i or -I options when starting "
189             "the server");
190         printf("Can't use both -r or -R and -i or -I options\n");
191         exit(EXIT_FAILURE);
192     }
193     if (install_service_set)
194         return install_service(argv[0], service_name, datadir_set);
195     if (remove_service_set)
196         return remove_service(service_name);
197 #endif  /* _WIN32 */
198
199     if (config_file == NULL) {
200         sprintf(tbuf, "%s/econfig", datadir);
201         config_file = tbuf;
202     }
203
204     logerror("------------------------------------------------------");
205     logerror("Empire server (pid %d) started", (int)getpid());
206
207 #if defined(_WIN32)
208     if (debug == 0) {
209         SERVICE_TABLE_ENTRY DispatchTable[]={{"Empire Server", service_main},{NULL, NULL}};
210         if (StartServiceCtrlDispatcher(DispatchTable))
211             return 0;
212         else
213             if (GetLastError() != ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
214                 logerror("Failed to dispatch service (%d)", GetLastError());
215                 printf("Failed to dispatch service (%d)\n", GetLastError());
216                 exit(EXIT_FAILURE);
217             } else  /* start in the foreground */
218                 debug = 1;
219     }
220 #else
221     if (debug == 0 && flags == 0) {
222         disassoc();
223     }
224 #endif
225
226     start_server(flags, config_file);
227
228 #if defined(__linux__) && defined(_EMPTH_POSIX)
229     strcpy(tbuf, argv[0]);
230     for (op = 1; op < argc; op++) {
231         strcat(tbuf, " ");
232         strcat(tbuf, argv[op]);
233     }
234     sprintf(argv[0], "%s (main pid: %d)", tbuf, getpid());
235 #endif
236
237     empth_exit();
238
239 /* We should never get here.  But, just in case... */
240     close_files();
241
242 #if defined(_WIN32)
243     loc_NTTerm();
244 #endif
245     return EXIT_SUCCESS;
246 }
247
248
249 void
250 start_server(int flags, char *config_file)
251 {
252 #ifdef POSIXSIGNALS
253     struct sigaction act;
254 #endif /* POSIXSIGNALS */
255
256 #if defined(_WIN32)
257     loc_NTInit();
258 #endif
259     emp_config(config_file);
260     update_policy_check();
261
262     nullify_objects();
263
264 #if !defined(_WIN32)
265     /* signal() should not be used with mit pthreads. Anyway if u
266        have a posix threads u definitly have posix signals -- Sasha */
267 #if defined (POSIXSIGNALS) || defined (_EMPTH_POSIX)
268 #ifdef SA_SIGINFO
269     act.sa_flags = SA_SIGINFO;
270 #endif
271     sigemptyset(&act.sa_mask);
272     act.sa_handler = shutdwn;
273     /* pthreads on Linux use SIGUSR1 (*shrug*) so only catch it if not on
274        a Linux box running POSIX threads -- STM */
275 #if !(defined(__linux__) && defined(_EMPTH_POSIX))
276     sigaction(SIGUSR1, &act, NULL);
277 #endif
278     sigaction(SIGTERM, &act, NULL);
279     sigaction(SIGINT, &act, NULL);
280     act.sa_handler = panic;
281     sigaction(SIGBUS, &act, NULL);
282     sigaction(SIGSEGV, &act, NULL);
283     sigaction(SIGILL, &act, NULL);
284     sigaction(SIGFPE, &act, NULL);
285     act.sa_handler = SIG_IGN;
286     sigaction(SIGPIPE, &act, NULL);
287 #else
288     if (debug == 0 && flags == 0) {
289         /* pthreads on Linux use SIGUSR1 (*shrug*) so only catch it if not on
290            a Linux box running POSIX threads -- STM */
291 #if !(defined(__linux__) && defined(_EMPTH_POSIX))
292         signal(SIGUSR1, shutdwn);
293 #endif
294         signal(SIGTERM, shutdwn);
295         signal(SIGBUS, panic);
296         signal(SIGSEGV, panic);
297         signal(SIGILL, panic);
298         signal(SIGFPE, panic);
299         signal(SIGINT, shutdwn);
300     }
301     signal(SIGPIPE, SIG_IGN);
302 #endif /* POSIXSIGNALS */
303 #endif /* _WIN32 */
304     empth_init((char **)&player, flags);
305     srand(time(NULL));
306     global_init();
307     shutdown_init();
308     player_init();
309     ef_init();
310     init_files();
311     io_init();
312     init_nreport();
313
314     if (opt_MOB_ACCESS) {
315         /* This fixes up mobility upon restart */
316         mobility_init();
317     }
318
319     empth_create(PP_ACCEPT, player_accept, (50 * 1024), flags,
320                  "AcceptPlayers", "Accept network connections", 0);
321     empth_create(PP_KILLIDLE, player_kill_idle, (50 * 1024), flags,
322                  "KillIdle", "Kills idle player connections", 0);
323     empth_create(PP_SCHED, update_sched, (50 * 1024), flags, "UpdateSched",
324                  "Schedules updates to occur", 0);
325     empth_create(PP_TIMESTAMP, delete_lostitems, (50 * 1024), flags,
326                  "DeleteItems", "Deletes old lost items", 0);
327     if (opt_MOB_ACCESS) {
328         /* Start the mobility access check thread */
329         empth_create(PP_TIMESTAMP, mobility_check, (50 * 1024), flags,
330                      "MobilityCheck", "Writes the timestamp file", 0);
331     }
332
333     if (opt_MARKET) {
334         empth_create(PP_TIMESTAMP, market_update, (50 * 1024), flags,
335                      "MarketUpdate", "Updates the market", 0);
336     }
337 }
338
339 static void
340 init_files(void)
341 {
342     ef_open(EF_NATION, O_RDWR, EFF_MEM);
343     ef_open(EF_SECTOR, O_RDWR, EFF_MEM);
344     ef_open(EF_SHIP, O_RDWR, EFF_MEM);
345     ef_open(EF_PLANE, O_RDWR, EFF_MEM);
346     ef_open(EF_LAND, O_RDWR, EFF_MEM);
347     ef_open(EF_NEWS, O_RDWR, 0);
348     ef_open(EF_LOAN, O_RDWR, 0);
349     ef_open(EF_TREATY, O_RDWR, 0);
350     ef_open(EF_NUKE, O_RDWR, EFF_MEM);
351     ef_open(EF_POWER, O_RDWR, 0);
352     ef_open(EF_TRADE, O_RDWR, 0);
353     ef_open(EF_MAP, O_RDWR, EFF_MEM);
354     ef_open(EF_BMAP, O_RDWR, EFF_MEM);
355     ef_open(EF_COMM, O_RDWR, 0);
356     ef_open(EF_LOST, O_RDWR, 0);
357 }
358
359 void
360 close_files(void)
361 {
362     ef_close(EF_NATION);
363     ef_close(EF_SECTOR);
364     ef_close(EF_SHIP);
365     ef_close(EF_PLANE);
366     ef_close(EF_LAND);
367     ef_close(EF_NEWS);
368     ef_close(EF_LOAN);
369     ef_close(EF_TREATY);
370     ef_close(EF_NUKE);
371     ef_close(EF_POWER);
372     ef_close(EF_TRADE);
373     ef_close(EF_MAP);
374     ef_close(EF_COMM);
375     ef_close(EF_BMAP);
376     ef_close(EF_LOST);
377 }
378
379 /* we're going down.  try to close the files at least */
380 #if !defined(_WIN32)
381 void
382 panic(int sig)
383 {
384 #ifdef POSIXSIGNALS
385     struct sigaction act;
386
387     act.sa_flags = 0;
388     sigemptyset(&act.sa_mask);
389     act.sa_handler = SIG_DFL;
390     sigaction(SIGBUS, &act, NULL);
391     sigaction(SIGSEGV, &act, NULL);
392     sigaction(SIGILL, &act, NULL);
393     sigaction(SIGFPE, &act, NULL);
394 #else
395     signal(SIGBUS, SIG_DFL);
396     signal(SIGSEGV, SIG_DFL);
397     signal(SIGILL, SIG_DFL);
398     signal(SIGFPE, SIG_DFL);
399 #endif /* POSIXSIGNALS */
400     logerror("server received fatal signal %d", sig);
401     log_last_commands();
402     close_files();
403     if (CANT_HAPPEN(sig != SIGBUS && sig != SIGSEGV
404                     && sig != SIGILL && sig != SIGFPE))
405         _exit(1);
406     if (raise(sig))
407         _exit(1);
408 }
409 #endif /* _WIN32 */
410
411 void
412 shutdwn(int sig)
413 {
414     struct player *p;
415     time_t now;
416
417 #if defined(__linux__) && defined(_EMPTH_POSIX)
418 /* This is a hack to get around the way pthreads work on Linux.  This
419    may be useful on other platforms too where threads are turned into
420    processes. */
421     if (getpid() != mainpid) {
422         empth_t *me;
423
424         me = empth_self();
425         if (me && me->name) {
426             if (strlen(me->name) > 5) {
427                 /* Player threads are cleaned up below, so just have
428                    them return.  This should work. */
429                 if (!strncmp("Player", me->name, 6)) {
430                     return;
431                 }
432             }
433         }
434         /* Not a player thread - must be server thread, so exit */
435         empth_exit();
436         return;
437     }
438 #endif
439
440     logerror("Shutdown commencing (cleaning up threads.)");
441
442     for (p = player_next(0); p != 0; p = player_next(p)) {
443         if (p->state != PS_PLAYING)
444             continue;
445         pr_flash(p, "Server shutting down...\n");
446         p->state = PS_SHUTDOWN;
447         p->aborted++;
448         if (p->command) {
449             pr_flash(p, "Shutdown aborting command\n");
450         }
451         empth_wakeup(p->proc);
452     }
453
454     if (!sig) {
455         /* Sleep and let some cleanup happen - note this doesn't work
456            when called from a signal handler, since we may or may not
457            be in the right thread.  So we just pass by and kill 'em
458            all. */
459         time(&now);
460         empth_sleep(now + 1);
461     }
462
463     for (p = player_next(0); p != 0; p = player_next(p)) {
464         p->state = PS_KILL;
465         p->aborted++;
466         empth_terminate(p->proc);
467         p = player_delete(p);
468     }
469     if (sig)
470         logerror("Server shutting down on signal %d", sig);
471     else
472         logerror("Server shutting down at Deity's request");
473     close_files();
474     _exit(0);
475 }
476
477
478 static void
479 nullify_objects(void)
480 {
481     int i, j;
482
483     if (opt_BIG_CITY)
484         dchr[SCT_CAPIT] = bigcity_dchr;
485     if (opt_NO_LCMS)
486         dchr[SCT_LIGHT].d_cost = -1;
487     if (opt_NO_HCMS)
488         dchr[SCT_HEAVY].d_cost = -1;
489     if (opt_NO_OIL) {
490         dchr[SCT_OIL].d_cost = -1;
491         dchr[SCT_REFINE].d_cost = -1;
492     }
493     for (i = 0; i < pln_maxno; i++) {
494         if (opt_NO_HCMS)
495             plchr[i].pl_hcm = 0;
496         if (opt_NO_LCMS)
497             plchr[i].pl_lcm = 0;
498         if (opt_NO_OIL)
499             plchr[i].pl_fuel = 0;
500     }
501     for (i = 0; i < lnd_maxno; i++) {
502         if (opt_NO_HCMS)
503             lchr[i].l_hcm = 0;
504         if (opt_NO_LCMS)
505             lchr[i].l_lcm = 0;
506         /* Fix up the military values */
507         lchr[i].l_mil = lchr[i].l_item[I_MILIT];
508     }
509     for (i = 0; i < shp_maxno; i++) {
510         if (opt_NO_HCMS)
511             mchr[i].m_hcm = 0;
512         if (opt_NO_LCMS)
513             mchr[i].m_lcm = 0;
514         if (opt_NO_OIL) {
515             if (mchr[i].m_flags & M_OIL)
516                 mchr[i].m_name = 0;
517         }
518     }
519     for (i = 0; i < nuk_maxno; i++) {
520         if (opt_NO_HCMS)
521             nchr[i].n_hcm = 0;
522         if (opt_NO_LCMS)
523             nchr[i].n_lcm = 0;
524     }
525     for (i = 0; i <= SCT_MAXDEF; i++) {
526         if (opt_NO_HCMS)
527             dchr[i].d_hcms = 0;
528         if (opt_NO_LCMS)
529             dchr[i].d_lcms = 0;
530     }
531     for (i = 0; i < prd_maxno; i++) {
532         for (j = 0; j < MAXPRCON; j++) {
533             if (opt_NO_HCMS && pchr[i].p_ctype[j] == I_HCM)
534                 pchr[i].p_camt[j] = 0;
535             if (opt_NO_LCMS && pchr[i].p_ctype[j] == I_LCM)
536                 pchr[i].p_camt[j] = 0;
537             if (opt_NO_OIL && pchr[i].p_ctype[j] == I_OIL)
538                 pchr[i].p_camt[j] = 0;
539         }
540     }
541 }
542
543 #if defined(_WIN32)
544 static void
545 loc_NTInit()
546 {
547     int rc;
548     WORD wVersionRequested;
549     WSADATA wsaData;
550
551     wVersionRequested = MAKEWORD(2, 0);
552     rc = WSAStartup(wVersionRequested, &wsaData);
553     if (rc != 0) {
554         logerror("WSAStartup failed.  %d", rc);
555         _exit(1);
556     }
557 }
558 #endif
559
560 #if defined(_WIN32)
561 void
562 loc_NTTerm()
563 {
564     WSACleanup();
565 }
566 #endif