]> git.pond.sub.org Git - empserver/blob - src/server/main.c
40042d21fc5f1d028c9ab9ef8e37bb708a47482f
[empserver] / src / server / main.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2009, 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 files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  main.c: Empire Server main, startup and shutdown
29  *
30  *  Known contributors to this file:
31  *     Dave Pare, 1994
32  *     Steve McClure, 1996, 1998
33  *     Doug Hay, 1998
34  *     Ron Koenderink, 2004-2005
35  *     Markus Armbruster, 2005-2008
36  */
37
38 #include <config.h>
39
40 #include <errno.h>
41 #include <signal.h>
42 #include <stdio.h>
43 #ifndef _WIN32
44 #include <sys/wait.h>
45 #endif
46 #include <unistd.h>
47
48 #if defined(_WIN32)
49 #include <winsock2.h>
50 #undef NS_ALL
51 #include <process.h>
52 #include "service.h"
53 #endif
54
55 #include "empio.h"
56 #include "empthread.h"
57 #include "file.h"
58 #include "journal.h"
59 #include "land.h"
60 #include "match.h"
61 #include "misc.h"
62 #include "nat.h"
63 #include "nuke.h"
64 #include "optlist.h"
65 #include "plane.h"
66 #include "player.h"
67 #include "product.h"
68 #include "prototypes.h"
69 #include "sect.h"
70 #include "server.h"
71 #include "ship.h"
72 #include "version.h"
73
74 static void ignore(void);
75 static void crash_dump(void);
76 static void create_pidfile(char *, pid_t);
77
78 #if defined(_WIN32)
79 static void loc_NTInit(void);
80 static void loc_NTTerm(void);
81 #endif
82
83 /*
84  * Lock to synchronize player threads with update and shutdown.
85  * Update and shutdown takes it exclusive, commands take it shared.
86  */
87 empth_rwlock_t *play_lock;
88
89 /*
90  * Is a thread attempting to take an exclusive play_lock?
91  * Threads holding a shared play_lock must not sleep while this is
92  * true.
93  */
94 int play_wrlock_wanted;
95
96 static char pidfname[] = "server.pid";
97
98 /* Run as daemon?  If yes, detach from controlling terminal etc. */
99 static int daemonize = 1;
100
101 static void
102 help(char *program_name, char *complaint)
103 {
104     if (complaint)
105         fprintf(stderr, "%s: %s\n", program_name, complaint);
106     fprintf(stderr, "Try -h for help.\n");
107 }
108
109 static void
110 print_usage(char *program_name)
111 {
112     printf("Usage: %s [OPTION]...\n"
113            "  -d              debug mode, implies -E abort\n"
114            "  -e CONFIG-FILE  configuration file\n"
115            "                  (default %s)\n"
116            "  -E ACTION       what to do on oops: abort, crash-dump, nothing (default)\n"
117            "  -h              display this help and exit\n"
118 #ifdef _WIN32
119            "  -i              install service `%s'\n"
120            "  -I NAME         install service NAME\n"
121 #endif
122            "  -p              threading debug mode, implies -d\n"
123 #ifdef _WIN32
124            "  -u              uninstall service `%s'\n"
125            "  -U NAME         uninstall service NAME\n"
126 #endif
127            "  -s              enable stack checking\n"
128            "  -R RANDOM-SEED  random seed\n"
129            "  -v              display version information and exit\n",
130            program_name, dflt_econfig
131 #ifdef _WIN32
132            , DEFAULT_SERVICE_NAME, DEFAULT_SERVICE_NAME
133 #endif
134         );
135 }
136
137 int
138 main(int argc, char **argv)
139 {
140     static char *oops_key[] = { "abort", "crash-dump", "nothing", NULL };
141     static void (*oops_hndlr[])(void) = { abort, crash_dump, ignore };
142     int flags = 0;
143 #if defined(_WIN32)
144     int install_service_set = 0;
145     char *program_name = NULL;
146     char *service_name = NULL;
147     int remove_service_set = 0;
148 #endif
149     char *config_file = NULL;
150     int op, idx, sig;
151     unsigned seed = time(NULL);
152
153     oops_handler = ignore;
154
155 #ifdef _WIN32
156 # define XOPTS "iI:uU:"
157 #else
158 # define XOPTS
159 #endif
160     while ((op = getopt(argc, argv, "de:E:hpsR:v" XOPTS)) != EOF) {
161         switch (op) {
162         case 'p':
163             flags |= EMPTH_PRINT;
164             /* fall through */
165         case 'd':
166             oops_handler = abort;
167             daemonize = 0;
168             break;
169         case 'e':
170             config_file = optarg;
171             break;
172         case 'E':
173             idx = stmtch(optarg, oops_key, 0, sizeof(*oops_key));
174             if (idx < 0) {
175                 help(argv[0], "invalid argument for -E");
176                 return EXIT_FAILURE;
177             }
178             oops_handler = oops_hndlr[idx];
179             break;
180 #if defined(_WIN32)
181         case 'I':
182             service_name = optarg;
183             /* fall through */
184         case 'i':
185             install_service_set++;
186             break;
187         case 'U':
188             service_name = optarg;
189             /* fall through */
190         case 'u':
191             remove_service_set++;
192             break;
193 #endif  /* _WIN32 */
194         case 's':
195             flags |= EMPTH_STACKCHECK;
196             break;
197         case 'R':
198             seed = strtoul(optarg, NULL, 10);
199             break;
200         case 'v':
201             printf("%s\n\n%s", version, legal);
202             return EXIT_SUCCESS;
203         case 'h':
204             print_usage(argv[0]);
205             return EXIT_SUCCESS;
206         default:
207             help(argv[0], NULL);
208             return EXIT_FAILURE;
209         }
210     }
211
212 #if defined(_WIN32)
213     if ((!daemonize || flags || config_file != NULL) &&
214         remove_service_set) {
215         fprintf(stderr, "Can't use -p, -s, -d or -e with either "
216             "-u or -U options\n");
217         exit(EXIT_FAILURE);
218     }
219     if ((!daemonize || flags) && install_service_set) {
220         fprintf(stderr, "Can't use -d, -p or -s with either "
221             "-i or -I options\n");
222         exit(EXIT_FAILURE);
223     }
224     if (install_service_set && remove_service_set) {
225         fprintf(stderr, "Can't use both -u or -U and -i or -I "
226             "options\n");
227         exit(EXIT_FAILURE);
228     }
229
230     if (remove_service_set)
231         return remove_service(service_name);
232     if (install_service_set) {
233         program_name = _fullpath(NULL, argv[0], 0);
234         if (config_file != NULL)
235             config_file = _fullpath(NULL, config_file, 0);
236     }
237 #endif  /* _WIN32 */
238
239     empfile_init();
240     if (emp_config(config_file) < 0)
241         exit(EXIT_FAILURE);
242     empfile_fixup();
243     if (read_builtin_tables() < 0)
244         exit(EXIT_FAILURE);
245     if (read_custom_tables() < 0)
246         exit(EXIT_FAILURE);
247     if (chdir(gamedir)) {
248         fprintf(stderr, "Can't chdir to %s (%s)\n",
249                 gamedir, strerror(errno));
250         exit(EXIT_FAILURE);
251     }
252
253 #if defined(_WIN32)
254     if (install_service_set)
255         return install_service(program_name, service_name, config_file);
256 #endif  /* _WIN32 */
257
258     init_server(seed);
259
260 #if defined(_WIN32)
261     if (daemonize != 0) {
262         SERVICE_TABLE_ENTRY DispatchTable[]={
263             {"Empire Server", service_main},
264             {NULL, NULL}
265         };
266         if (StartServiceCtrlDispatcher(DispatchTable))
267             return 0;
268         else {
269             /*
270              * If it is service startup error then exit otherwise
271              * start server in the foreground
272              */
273             if (GetLastError() != ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
274                 logerror("Failed to dispatch service (%lu)",
275                          GetLastError());
276                 finish_server();
277                 exit(EXIT_FAILURE);
278             }
279         }
280     }
281     daemonize = 0;
282 #else  /* !_WIN32 */
283     if (daemonize) {
284         if (disassoc() < 0) {
285             logerror("Can't become daemon (%s)", strerror(errno));
286             exit(1);
287         }
288     }
289 #endif /* !_WIN32 */
290     start_server(flags);
291     journal_prng(seed);
292
293     for (;;) {
294         sig = empth_wait_for_signal();
295 #ifdef SIGHUP
296         if (sig == SIGHUP) {
297             /* if you make changes here, also update relo() */
298             journal_reopen();
299             update_reschedule();
300             logreopen();
301             continue;
302         }
303 #endif
304         break;
305     }
306
307     shutdwn(sig);
308     CANT_REACH();
309     finish_server();
310     return EXIT_SUCCESS;
311 }
312
313 static void
314 ignore(void)
315 {
316 }
317
318 static void
319 crash_dump(void)
320 {
321 #ifdef _WIN32
322     logerror("Crash dump is not implemented");
323 #else
324     pid_t pid;
325     int status;
326
327     pid = fork();
328     if (pid < 0) {
329         logerror("Can't fork for crash dump (%s)", strerror(errno));
330         return;
331     }
332     if (pid == 0)
333         abort();                /* child */
334
335     /* parent */
336     while (waitpid(pid, &status, 0) < 0) {
337         if (errno != EINTR) {
338             logerror("Can't get crash dumping child's status (%s)",
339                      strerror(errno));
340             return;
341         }
342     }
343     run_hook(post_crash_dump_hook, "post-crash-dump");
344     logerror("Crash dump complete");
345 #endif
346 }
347
348 /*
349  * Initialize for serving, acquire resources.
350  */
351 void
352 init_server(unsigned seed)
353 {
354     srandom(seed);
355 #if defined(_WIN32)
356     loc_NTInit();
357 #endif
358     player_init();
359     ef_init_srv();
360     io_init();
361     init_nreport();
362
363     loginit("server");
364 }
365
366 /*
367  * Start serving.
368  */
369 void
370 start_server(int flags)
371 {
372     pid_t pid;
373
374     pid = getpid();
375     create_pidfile(pidfname, pid);
376     logerror("------------------------------------------------------");
377     logerror("Empire server (pid %d) started", (int)pid);
378
379     empth_init((void **)&player, flags);
380
381     if (journal_startup() < 0)
382         exit(1);
383
384     empth_create(player_accept, 50 * 1024, flags, "AcceptPlayers", 0);
385
386     market_init();
387     update_init();
388 }
389
390 /*
391  * Finish serving, release resources.
392  */
393 void
394 finish_server(void)
395 {
396     ef_fin_srv();
397 #if defined(_WIN32)
398     loc_NTTerm();
399 #endif
400     journal_shutdown();
401     remove(pidfname);
402 }
403
404 static void
405 create_pidfile(char *fname, pid_t pid)
406 {
407     FILE *pidf = fopen(fname, "w");
408     if (!pidf
409         || fprintf(pidf, "%d\n", (int)pid) < 0
410         || fclose(pidf)) {
411         logerror("Can't write PID file (%s)", strerror(errno));
412         exit(1);
413     }
414 }
415
416 void
417 shutdwn(int sig)
418 {
419     struct player *p;
420
421     logerror("Shutdown commencing (cleaning up threads.)");
422
423     play_wrlock_wanted = 1;
424     for (p = player_next(0); p != 0; p = player_next(p)) {
425         if (p->state != PS_PLAYING)
426             continue;
427         pr_flash(p, "Server shutting down...\n");
428         p->state = PS_SHUTDOWN;
429         p->aborted++;
430         if (p->command) {
431             pr_flash(p, "Shutdown aborting command\n");
432         }
433         empth_wakeup(p->proc);
434     }
435     empth_rwlock_wrlock(play_lock);
436     if (sig)
437         logerror("Server shutting down on signal %d", sig);
438     else
439         logerror("Server shutting down at deity's request");
440     finish_server();
441
442 #if defined(_WIN32)
443     if (daemonize)
444         stop_service();
445 #endif
446     exit(0);
447 }
448
449 #if defined(_WIN32)
450 static void
451 loc_NTInit(void)
452 {
453     int rc;
454     WORD wVersionRequested;
455     WSADATA wsaData;
456
457     wVersionRequested = MAKEWORD(2, 0);
458     rc = WSAStartup(wVersionRequested, &wsaData);
459     if (rc != 0) {
460         logerror("WSAStartup failed.  %d", rc);
461         exit(1);
462     }
463 }
464
465 static void
466 loc_NTTerm(void)
467 {
468     WSACleanup();
469 }
470 #endif