]> git.pond.sub.org Git - empserver/blob - src/server/main.c
(main): Fix -p to imply -d as documented, not just !daemonize.
[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 #include <signal.h>
37 #if !defined(_WIN32)
38 #include <sys/ioctl.h>
39 #endif
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <stdio.h>
43 #include <string.h>
44
45 #if defined(_WIN32)
46 #include <winsock2.h>
47 #undef NS_ALL
48 #include <process.h>
49 #include "../lib/gen/getopt.h"
50 #include "service.h"
51 #include "direct.h"
52 #endif
53
54 #include "misc.h"
55 #include "nat.h"
56 #include "file.h"
57 #include "player.h"
58 #include "empthread.h"
59 #include "plane.h"
60 #include "nuke.h"
61 #include "land.h"
62 #include "ship.h"
63 #include "sect.h"
64 #include "product.h"
65 #include "optlist.h"
66 #include "server.h"
67 #include "prototypes.h"
68
69 static void nullify_objects(void);
70 static void init_files(void);
71 static void close_files(void);
72 static void create_pidfile(char *, pid_t);
73
74 #if defined(_WIN32)
75 static void loc_NTInit(void);
76 static void loc_NTTerm(void);
77 #endif
78
79 static char pidfname[] = "server.pid";
80
81 /* Debugging?  If yes call abort() on internal error.  */
82 int debug = 0;
83 /* Run as daemon?  If yes, detach from controlling terminal etc. */
84 int daemonize = 1;
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 -p\n",
91         program_name);
92     printf("-i install service with the default name %s\n", DEFAULT_SERVICE_NAME);
93     printf("-r remove service with the default name %s\n", DEFAULT_SERVICE_NAME);
94
95 #else
96     printf("Usage: %s -D datadir -e config_file -d -p -s\n", program_name);
97     printf("-s stack check flag (include print flag)\n");
98 #endif
99     printf("-p print flag\n");
100     printf("-d debug mode\n");
101 }
102
103 int
104 main(int argc, char **argv)
105 {
106     int flags = 0;
107 #if defined(_WIN32)
108     int install_service_set = 0;
109     char *service_name = NULL;
110     int remove_service_set = 0;
111     int datadir_set = 0;
112 #endif
113     char *config_file = NULL;
114     int op;
115
116 #ifdef _WIN32
117 # define XOPTS "iI:rR:"
118 #else
119 # define XOPTS
120 #endif
121     while ((op = getopt(argc, argv, "D:de:psh" XOPTS)) != EOF) {
122         switch (op) {
123         case 'D':
124             datadir = optarg;
125 #if defined(_WIN32)
126             datadir_set++;
127 #endif
128             break;
129         case 'p':
130             flags |= EMPTH_PRINT;
131             /* fall through */
132         case 'd':
133             debug = 1;
134             daemonize = 0;
135             break;
136         case 'e':
137             config_file = optarg;
138             break;
139 #if defined(_WIN32)
140         case 'I':
141             service_name = optarg;
142             /*
143              * fall out
144              */
145         case 'i':
146             install_service_set++;
147             break;
148         case 'R':
149             service_name = optarg;
150             /*
151              * fall out
152              */
153         case 'r':
154             remove_service_set++;
155             break;
156 #endif
157         case 's':
158             flags |= EMPTH_STACKCHECK;
159             break;
160         case 'h':
161         default:
162             print_usage(argv[0]);
163             return EXIT_FAILURE;
164         }
165     }
166
167 #if defined(_WIN32)
168     if ((debug || flags || datadir_set || config_file != NULL) &&
169         remove_service_set) {
170         fprintf(stderr, "Can't use -p, -s, -d, -D or -e with either "
171             "-r or -R options\n");
172         exit(EXIT_FAILURE);
173     }
174     if ((debug || flags) && install_service_set) {
175         fprintf(stderr, "Can't use -d, -p or -s with either "
176             "-i or -I options\n");
177         exit(EXIT_FAILURE);
178     }
179     if (install_service_set && remove_service_set) {
180         fprintf(stderr, "Can't use both -r or -R and -i or -I "
181             "options\n");
182         exit(EXIT_FAILURE);
183     }
184 #endif  /* _WIN32 */
185
186
187 #if defined(_WIN32)
188     if (remove_service_set)
189         return remove_service(service_name);
190 #endif  /* _WIN32 */
191
192     if (emp_config(config_file) < 0)
193         exit(EXIT_FAILURE);
194     if (chdir(datadir)) {
195         fprintf(stderr, "Can't chdir to %s (%s)\n", datadir, strerror(errno));
196         exit(EXIT_FAILURE);
197     }
198
199 #if defined(_WIN32)
200     if (install_service_set)
201         return install_service(argv[0], service_name, datadir_set, config_file);
202 #endif  /* _WIN32 */
203
204     init_server();
205
206 #if defined(_WIN32)
207     if (daemonize != 0) {
208         SERVICE_TABLE_ENTRY DispatchTable[]={{"Empire Server", service_main},{NULL, NULL}};
209         if (StartServiceCtrlDispatcher(DispatchTable))
210             return 0;
211         else {
212             /*
213              * If it is service startup error then exit otherwise
214              * start server in the foreground
215              */
216             if (GetLastError() != ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
217                 logerror("Failed to dispatch service (%d)", GetLastError());
218                 finish_server();
219                 exit(EXIT_FAILURE);
220             }
221         }
222     }
223     daemonize = 0;
224 #else  /* !_WIN32 */
225     if (daemonize)
226         disassoc();
227 #endif /* !_WIN32 */
228     start_server(flags);
229
230     empth_exit();
231
232     CANT_HAPPEN("main thread terminated");
233     finish_server();
234     return EXIT_SUCCESS;
235 }
236
237
238 /*
239  * Initialize for serving, acquire resources.
240  */
241 void
242 init_server(void)
243 {
244     srandom(time(NULL));
245 #if defined(_WIN32)
246     loc_NTInit();
247 #endif
248     update_policy_check();
249     nullify_objects();
250     global_init();
251     shutdown_init();
252     player_init();
253     ef_init();
254     init_files();
255     io_init();
256     init_nreport();
257
258     if (opt_MOB_ACCESS) {
259         /* This fixes up mobility upon restart */
260         mobility_init();
261     }
262
263     loginit("server");
264 }
265
266 /*
267  * Start serving.
268  */
269 void
270 start_server(int flags)
271 {
272     pid_t pid;
273 #if !defined(_WIN32)
274     struct sigaction act;
275 #endif
276
277     pid = getpid();
278     create_pidfile(pidfname, pid);
279     logerror("------------------------------------------------------");
280     logerror("Empire server (pid %d) started", (int)pid);
281
282 #if !defined(_WIN32)
283     /* signal() should not be used with mit pthreads. Anyway if u
284        have a posix threads u definitly have posix signals -- Sasha */
285     sigemptyset(&act.sa_mask);
286     act.sa_handler = shutdwn;
287     sigaction(SIGTERM, &act, NULL);
288     sigaction(SIGINT, &act, NULL);
289     act.sa_handler = panic;
290     sigaction(SIGBUS, &act, NULL);
291     sigaction(SIGSEGV, &act, NULL);
292     sigaction(SIGILL, &act, NULL);
293     sigaction(SIGFPE, &act, NULL);
294     act.sa_handler = SIG_IGN;
295     sigaction(SIGPIPE, &act, NULL);
296 #endif /* !_WIN32 */
297
298     empth_init((char **)&player, flags);
299
300     empth_create(PP_ACCEPT, player_accept, (50 * 1024), flags,
301                  "AcceptPlayers", "Accept network connections", 0);
302     empth_create(PP_KILLIDLE, player_kill_idle, (50 * 1024), flags,
303                  "KillIdle", "Kills idle player connections", 0);
304     empth_create(PP_SCHED, update_sched, (50 * 1024), flags, "UpdateSched",
305                  "Schedules updates to occur", 0);
306     empth_create(PP_TIMESTAMP, delete_lostitems, (50 * 1024), flags,
307                  "DeleteItems", "Deletes old lost items", 0);
308     if (opt_MOB_ACCESS) {
309         /* Start the mobility access check thread */
310         empth_create(PP_TIMESTAMP, mobility_check, (50 * 1024), flags,
311                      "MobilityCheck", "Writes the timestamp file", 0);
312     }
313
314     if (opt_MARKET) {
315         empth_create(PP_TIMESTAMP, market_update, (50 * 1024), flags,
316                      "MarketUpdate", "Updates the market", 0);
317     }
318 }
319
320 /*
321  * Finish serving, release resources.
322  */
323 void
324 finish_server(void)
325 {
326     close_files();
327 #if defined(_WIN32)
328     loc_NTTerm();
329 #endif
330     remove(pidfname);
331 }
332
333 static void
334 create_pidfile(char *fname, pid_t pid)
335 {
336     FILE *pidf = fopen(fname, "w");
337     if (!pidf
338         || fprintf(pidf, "%d\n", pid) < 0
339         || fclose(pidf)) {
340         logerror("Can't write PID file (%s)", strerror(errno));
341         exit(1);
342     }
343 }
344
345 static void
346 init_files(void)
347 {
348     int failed = 0;
349     failed |= !ef_open(EF_NATION, O_RDWR, EFF_MEM);
350     failed |= !ef_open(EF_SECTOR, O_RDWR, EFF_MEM);
351     failed |= !ef_open(EF_SHIP, O_RDWR, EFF_MEM);
352     failed |= !ef_open(EF_PLANE, O_RDWR, EFF_MEM);
353     failed |= !ef_open(EF_LAND, O_RDWR, EFF_MEM);
354     failed |= !ef_open(EF_NEWS, O_RDWR, 0);
355     failed |= !ef_open(EF_LOAN, O_RDWR, 0);
356     failed |= !ef_open(EF_TREATY, O_RDWR, 0);
357     failed |= !ef_open(EF_NUKE, O_RDWR, EFF_MEM);
358     failed |= !ef_open(EF_POWER, O_RDWR, 0);
359     failed |= !ef_open(EF_TRADE, O_RDWR, 0);
360     failed |= !ef_open(EF_MAP, O_RDWR, EFF_MEM);
361     failed |= !ef_open(EF_BMAP, O_RDWR, EFF_MEM);
362     failed |= !ef_open(EF_COMM, O_RDWR, 0);
363     failed |= !ef_open(EF_LOST, O_RDWR, 0);
364     if (failed) {
365         logerror("Missing files, giving up");
366         exit(EXIT_FAILURE);
367     }
368 }
369
370 static void
371 close_files(void)
372 {
373     ef_close(EF_NATION);
374     ef_close(EF_SECTOR);
375     ef_close(EF_SHIP);
376     ef_close(EF_PLANE);
377     ef_close(EF_LAND);
378     ef_close(EF_NEWS);
379     ef_close(EF_LOAN);
380     ef_close(EF_TREATY);
381     ef_close(EF_NUKE);
382     ef_close(EF_POWER);
383     ef_close(EF_TRADE);
384     ef_close(EF_MAP);
385     ef_close(EF_COMM);
386     ef_close(EF_BMAP);
387     ef_close(EF_LOST);
388 }
389
390 /* we're going down.  try to close the files at least */
391 #if !defined(_WIN32)
392 void
393 panic(int sig)
394 {
395     struct sigaction act;
396
397     act.sa_flags = 0;
398     sigemptyset(&act.sa_mask);
399     act.sa_handler = SIG_DFL;
400     sigaction(SIGBUS, &act, NULL);
401     sigaction(SIGSEGV, &act, NULL);
402     sigaction(SIGILL, &act, NULL);
403     sigaction(SIGFPE, &act, NULL);
404     logerror("server received fatal signal %d", sig);
405     log_last_commands();
406     close_files();
407     if (CANT_HAPPEN(sig != SIGBUS && sig != SIGSEGV
408                     && sig != SIGILL && sig != SIGFPE))
409         _exit(1);
410     if (raise(sig))
411         _exit(1);
412 }
413 #endif /* _WIN32 */
414
415 void
416 shutdwn(int sig)
417 {
418     struct player *p;
419     time_t now;
420
421     logerror("Shutdown commencing (cleaning up threads.)");
422
423     for (p = player_next(0); p != 0; p = player_next(p)) {
424         if (p->state != PS_PLAYING)
425             continue;
426         pr_flash(p, "Server shutting down...\n");
427         p->state = PS_SHUTDOWN;
428         p->aborted++;
429         if (p->command) {
430             pr_flash(p, "Shutdown aborting command\n");
431         }
432         empth_wakeup(p->proc);
433     }
434
435     if (!sig) {
436         /* Sleep and let some cleanup happen - note this doesn't work
437            when called from a signal handler, since we may or may not
438            be in the right thread.  So we just pass by and kill 'em
439            all. */
440         time(&now);
441         empth_sleep(now + 1);
442     }
443
444     for (p = player_next(0); p != 0; p = player_next(p)) {
445         p->state = PS_KILL;
446         p->aborted++;
447         empth_terminate(p->proc);
448         p = player_delete(p);
449     }
450     if (sig)
451         logerror("Server shutting down on signal %d", sig);
452     else
453         logerror("Server shutting down at deity's request");
454     finish_server();
455
456 #if defined(_WIN32)
457     if (daemonize) {
458         stop_service();
459         return;
460     }
461 #endif
462     _exit(0);
463 }
464
465
466 static void
467 nullify_objects(void)
468 {
469     int i, j;
470
471     if (opt_BIG_CITY)
472         dchr[SCT_CAPIT] = bigcity_dchr;
473     if (opt_NO_LCMS)
474         dchr[SCT_LIGHT].d_cost = -1;
475     if (opt_NO_HCMS)
476         dchr[SCT_HEAVY].d_cost = -1;
477     if (opt_NO_OIL) {
478         dchr[SCT_OIL].d_cost = -1;
479         dchr[SCT_REFINE].d_cost = -1;
480     }
481     for (i = 0; i < pln_maxno; i++) {
482         if (opt_NO_HCMS)
483             plchr[i].pl_hcm = 0;
484         if (opt_NO_LCMS)
485             plchr[i].pl_lcm = 0;
486         if (opt_NO_OIL)
487             plchr[i].pl_fuel = 0;
488     }
489     for (i = 0; i < lnd_maxno; i++) {
490         if (opt_NO_HCMS)
491             lchr[i].l_hcm = 0;
492         if (opt_NO_LCMS)
493             lchr[i].l_lcm = 0;
494         /* Fix up the military values */
495         lchr[i].l_mil = lchr[i].l_item[I_MILIT];
496     }
497     for (i = 0; i < shp_maxno; i++) {
498         if (opt_NO_HCMS)
499             mchr[i].m_hcm = 0;
500         if (opt_NO_LCMS)
501             mchr[i].m_lcm = 0;
502         if (opt_NO_OIL) {
503             if (mchr[i].m_flags & M_OIL)
504                 mchr[i].m_name = 0;
505         }
506     }
507     for (i = 0; i < nuk_maxno; i++) {
508         if (opt_NO_HCMS)
509             nchr[i].n_hcm = 0;
510         if (opt_NO_LCMS)
511             nchr[i].n_lcm = 0;
512     }
513     for (i = 0; i <= SCT_MAXDEF; i++) {
514         if (opt_NO_HCMS)
515             dchr[i].d_hcms = 0;
516         if (opt_NO_LCMS)
517             dchr[i].d_lcms = 0;
518     }
519     for (i = 0; i < prd_maxno; i++) {
520         for (j = 0; j < MAXPRCON; j++) {
521             if (opt_NO_HCMS && pchr[i].p_ctype[j] == I_HCM)
522                 pchr[i].p_camt[j] = 0;
523             if (opt_NO_LCMS && pchr[i].p_ctype[j] == I_LCM)
524                 pchr[i].p_camt[j] = 0;
525             if (opt_NO_OIL && pchr[i].p_ctype[j] == I_OIL)
526                 pchr[i].p_camt[j] = 0;
527         }
528     }
529 }
530
531 #if defined(_WIN32)
532 static void
533 loc_NTInit(void)
534 {
535     int rc;
536     WORD wVersionRequested;
537     WSADATA wsaData;
538
539     wVersionRequested = MAKEWORD(2, 0);
540     rc = WSAStartup(wVersionRequested, &wsaData);
541     if (rc != 0) {
542         logerror("WSAStartup failed.  %d", rc);
543         _exit(1);
544     }
545 }
546
547 static void
548 loc_NTTerm(void)
549 {
550     WSACleanup();
551 }
552 #endif