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