]> git.pond.sub.org Git - empserver/blob - src/server/main.c
e9604a8be2637edbf133dc1d924c9b16742a266f
[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 #include <errno.h>
42 #if !defined(_WIN32)
43 #include <sys/ioctl.h>
44 #endif
45 #include <fcntl.h>
46 #include <stdio.h>
47 #include <string.h>
48
49 #if defined(_WIN32)
50 #include <winsock.h>
51 #endif
52
53 #include "misc.h"
54 #include "nat.h"
55 #include "file.h"
56 #include "player.h"
57 #include "empthread.h"
58 #include "plane.h"
59 #include "nuke.h"
60 #include "land.h"
61 #include "ship.h"
62 #include "sect.h"
63 #include "product.h"
64 #include "optlist.h"
65 #include "global.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
73 #if defined(_WIN32)
74 static void loc_NTInit(void);
75 static void loc_NTTerm(void);
76 #endif
77
78 #if !defined(_WIN32)
79 static int mainpid = 0;
80 #endif
81
82 /*
83  * Debugging?
84  * If yes, don't fork into background, don't catch certain signals,
85  * call abort() on internal error.
86  */
87 int debug = 0;
88
89 int
90 main(int argc, char **argv)
91 {
92     time_t now;
93     int flags = 0;
94     int op;
95     char *config_file = NULL;
96     extern char *optarg;
97     s_char tbuf[256];
98 #ifdef POSIXSIGNALS
99     struct sigaction act;
100 #endif /* POSIXSIGNALS */
101
102     loginit("server");
103
104 #if !defined(_WIN32)
105     mainpid = getpid();
106
107     while ((op = getopt(argc, argv, "D:de:psh")) != EOF) {
108         switch (op) {
109         case 'D':
110             datadir = optarg;
111             break;
112         case 'd':
113             debug++;
114             break;
115         case 'e':
116             config_file = optarg;
117             break;
118         case 'p':
119             flags |= EMPTH_PRINT;
120             break;
121         case 's':
122             flags |= EMPTH_PRINT | EMPTH_STACKCHECK;
123             break;
124         case 'h':
125         default:
126             printf("Usage: %s -d -p -s\n", argv[0]);
127             return 0;
128         }
129     }
130 #endif
131     if (config_file == NULL) {
132         sprintf(tbuf, "%s/econfig", datadir);
133         config_file = tbuf;
134     }
135
136     logerror("------------------------------------------------------");
137 #if !defined(_WIN32)
138     logerror("Empire server (pid %d) started", getpid());
139 #else
140     logerror("Empire server started");
141 #endif /* _WIN32 */
142
143 #if defined(_WIN32)
144     loc_NTInit();
145 #endif
146     emp_config(config_file);
147     update_policy_check();
148
149     nullify_objects();
150
151 #if !defined(_WIN32)
152     /* signal() should not be used with mit pthreads. Anyway if u
153        have a posix threads u definitly have posix signals -- Sasha */
154 #if defined (POSIXSIGNALS) || defined (_EMPTH_POSIX)
155 #ifdef SA_SIGINFO
156     act.sa_flags = SA_SIGINFO;
157 #endif
158     sigemptyset(&act.sa_mask);
159     if (debug == 0 && flags == 0) {
160         disassoc();
161     }
162     act.sa_handler = shutdwn;
163     /* pthreads on Linux use SIGUSR1 (*shrug*) so only catch it if not on
164        a Linux box running POSIX threads -- STM */
165 #if !(defined(__linux__) && defined(_EMPTH_POSIX))
166     sigaction(SIGUSR1, &act, NULL);
167 #endif
168     sigaction(SIGTERM, &act, NULL);
169     sigaction(SIGINT, &act, NULL);
170     act.sa_handler = panic;
171     sigaction(SIGBUS, &act, NULL);
172     sigaction(SIGSEGV, &act, NULL);
173     sigaction(SIGILL, &act, NULL);
174     sigaction(SIGFPE, &act, NULL);
175     act.sa_handler = SIG_IGN;
176     sigaction(SIGPIPE, &act, NULL);
177 #else
178     if (debug == 0 && flags == 0) {
179         disassoc();
180         /* pthreads on Linux use SIGUSR1 (*shrug*) so only catch it if not on
181            a Linux box running POSIX threads -- STM */
182 #if !(defined(__linux__) && defined(_EMPTH_POSIX))
183         signal(SIGUSR1, shutdwn);
184 #endif
185         signal(SIGTERM, shutdwn);
186         signal(SIGBUS, panic);
187         signal(SIGSEGV, panic);
188         signal(SIGILL, panic);
189         signal(SIGFPE, panic);
190         signal(SIGINT, shutdwn);
191     }
192     signal(SIGPIPE, SIG_IGN);
193 #endif /* POSIXSIGNALS */
194 #endif /* _WIN32 */
195     empth_init((char **)&player, flags);
196     time(&now);
197 #if !defined(_WIN32)
198     srandom(now);
199 #else
200     srand(now);
201 #endif /* _WIN32 */
202     global_init();
203     shutdown_init();
204     player_init();
205     ef_init();
206     init_files();
207     io_init();
208
209     if (opt_MOB_ACCESS) {
210         /* This fixes up mobility upon restart */
211         mobility_init();
212     }
213
214     empth_create(PP_ACCEPT, player_accept, (50 * 1024), flags,
215                  "AcceptPlayers", "Accept network connections", 0);
216     empth_create(PP_KILLIDLE, player_kill_idle, (50 * 1024), flags,
217                  "KillIdle", "Kills idle player connections", 0);
218     empth_create(PP_SCHED, update_sched, (50 * 1024), flags, "UpdateSched",
219                  "Schedules updates to occur", 0);
220     empth_create(PP_TIMESTAMP, delete_lostitems, (50 * 1024), flags,
221                  "DeleteItems", "Deletes old lost items", 0);
222     if (opt_MOB_ACCESS) {
223         /* Start the mobility access check thread */
224         empth_create(PP_TIMESTAMP, mobility_check, (50 * 1024), flags,
225                      "MobilityCheck", "Writes the timestamp file", 0);
226     }
227
228     if (opt_MARKET) {
229         empth_create(PP_TIMESTAMP, market_update, (50 * 1024), flags,
230                      "MarketUpdate", "Updates the market", 0);
231     }
232 #if defined(__linux__) && defined(_EMPTH_POSIX)
233     strcpy(tbuf, argv[0]);
234     for (op = 1; op < argc; op++) {
235         strcat(tbuf, " ");
236         strcat(tbuf, argv[op]);
237     }
238     sprintf(argv[0], "%s (main pid: %d)", tbuf, getpid());
239 #endif
240
241     empth_exit();
242
243 /* We should never get here.  But, just in case... */
244     close_files();
245
246 #if defined(_WIN32)
247     loc_NTTerm();
248 #endif
249     return 0;
250 }
251
252 static void
253 init_files(void)
254 {
255     ef_open(EF_NATION, O_RDWR, EFF_MEM);
256     ef_open(EF_SECTOR, O_RDWR, EFF_MEM);
257     ef_open(EF_SHIP, O_RDWR, EFF_MEM);
258     ef_open(EF_PLANE, O_RDWR, EFF_MEM);
259     ef_open(EF_LAND, O_RDWR, EFF_MEM);
260     ef_open(EF_NEWS, O_RDWR, 0);
261     ef_open(EF_LOAN, O_RDWR, 0);
262     ef_open(EF_TREATY, O_RDWR, 0);
263     ef_open(EF_NUKE, O_RDWR, EFF_MEM);
264     ef_open(EF_POWER, O_RDWR, 0);
265     ef_open(EF_TRADE, O_RDWR, 0);
266     ef_open(EF_MAP, O_RDWR, EFF_MEM);
267     ef_open(EF_BMAP, O_RDWR, EFF_MEM);
268     ef_open(EF_COMM, O_RDWR, 0);
269     ef_open(EF_LOST, O_RDWR, 0);
270 }
271
272 static void
273 close_files(void)
274 {
275     ef_close(EF_NATION);
276     ef_close(EF_SECTOR);
277     ef_close(EF_SHIP);
278     ef_close(EF_PLANE);
279     ef_close(EF_LAND);
280     ef_close(EF_NEWS);
281     ef_close(EF_LOAN);
282     ef_close(EF_TREATY);
283     ef_close(EF_NUKE);
284     ef_close(EF_POWER);
285     ef_close(EF_TRADE);
286     ef_close(EF_MAP);
287     ef_close(EF_COMM);
288     ef_close(EF_BMAP);
289     ef_close(EF_LOST);
290 }
291
292 /* we're going down.  try to close the files at least */
293 void
294 panic(int sig)
295 {
296 #if !defined(_WIN32)
297 #ifdef POSIXSIGNALS
298     struct sigaction act;
299
300     act.sa_flags = 0;
301     sigemptyset(&act.sa_mask);
302     act.sa_handler = SIG_DFL;
303     sigaction(SIGBUS, &act, NULL);
304     sigaction(SIGSEGV, &act, NULL);
305     sigaction(SIGILL, &act, NULL);
306     sigaction(SIGFPE, &act, NULL);
307 #else
308     signal(SIGBUS, SIG_DFL);
309     signal(SIGSEGV, SIG_DFL);
310     signal(SIGILL, SIG_DFL);
311     signal(SIGFPE, SIG_DFL);
312 #endif /* POSIXSIGNALS */
313 #endif /* _WIN32 */
314     logerror("server received fatal signal %d", sig);
315     log_last_commands();
316     close_files();
317     _exit(0);
318 }
319
320 void
321 shutdwn(int sig)
322 {
323     struct player *p;
324     time_t now;
325
326 #if defined(__linux__) && defined(_EMPTH_POSIX)
327 /* This is a hack to get around the way pthreads work on Linux.  This
328    may be useful on other platforms too where threads are turned into
329    processes. */
330     if (getpid() != mainpid) {
331         empth_t *me;
332
333         me = empth_self();
334         if (me && me->name) {
335             if (strlen(me->name) > 5) {
336                 /* Player threads are cleaned up below, so just have
337                    them return.  This should work. */
338                 if (!strncmp("Player", me->name, 6)) {
339                     return;
340                 }
341             }
342         }
343         /* Not a player thread - must be server thread, so exit */
344         empth_exit();
345         return;
346     }
347 #endif
348
349     logerror("Shutdown commencing (cleaning up threads.)");
350
351     for (p = player_next(0); p != 0; p = player_next(p)) {
352         if (p->state != PS_PLAYING)
353             continue;
354         pr_flash(p, "Server shutting down...\n");
355         p->state = PS_SHUTDOWN;
356         p->aborted++;
357         if (p->command) {
358             pr_flash(p, "Shutdown aborting command\n");
359         }
360         empth_wakeup(p->proc);
361     }
362
363     if (!sig) {
364         /* Sleep and let some cleanup happen - note this doesn't work
365            when called from a signal handler, since we may or may not
366            be in the right thread.  So we just pass by and kill 'em
367            all. */
368         time(&now);
369         empth_sleep(now + 1);
370     }
371
372     for (p = player_next(0); p != 0; p = player_next(p)) {
373         p->state = PS_KILL;
374         p->aborted++;
375         empth_terminate(p->proc);
376         p = player_delete(p);
377     }
378     if (sig)
379         logerror("Server shutting down on signal %d", sig);
380     else
381         logerror("Server shutting down at Deity's request");
382     close_files();
383     _exit(0);
384 }
385
386
387 static void
388 nullify_objects(void)
389 {
390     int i, j;
391
392     if (opt_BIG_CITY) {
393         dchr[SCT_CAPIT].d_flg = bigcity_dchr.d_flg;
394         dchr[SCT_CAPIT].d_pkg = bigcity_dchr.d_pkg;
395         dchr[SCT_CAPIT].d_build = bigcity_dchr.d_build;
396         dchr[SCT_CAPIT].d_lcms = bigcity_dchr.d_lcms;
397         dchr[SCT_CAPIT].d_hcms = bigcity_dchr.d_hcms;
398         dchr[SCT_CAPIT].d_name = bigcity_dchr.d_name;
399     }
400     if (opt_NO_LCMS)\r
401         dchr[SCT_LIGHT].d_cost = -1;\r
402     if (opt_NO_HCMS)\r
403         dchr[SCT_HEAVY].d_cost = -1;\r
404     if (opt_NO_OIL) {\r
405         dchr[SCT_OIL].d_cost = -1;\r
406         dchr[SCT_REFINE].d_cost = -1;\r
407     }
408     for (i = 0; i < pln_maxno; i++) {
409         if (opt_NO_HCMS)
410             plchr[i].pl_hcm = 0;
411         if (opt_NO_LCMS)
412             plchr[i].pl_lcm = 0;
413         if (opt_NO_OIL)
414             plchr[i].pl_fuel = 0;
415     }
416     for (i = 0; i < lnd_maxno; i++) {
417         if (opt_NO_HCMS)
418             lchr[i].l_hcm = 0;
419         if (opt_NO_LCMS)
420             lchr[i].l_lcm = 0;
421         /* Fix up the military values */
422         lchr[i].l_mil = lchr[i].l_item[I_MILIT];
423     }
424     for (i = 0; i < shp_maxno; i++) {
425         if (opt_NO_HCMS)
426             mchr[i].m_hcm = 0;
427         if (opt_NO_LCMS)
428             mchr[i].m_lcm = 0;
429         if (opt_NO_OIL) {
430             if (mchr[i].m_flags & M_OIL)
431                 mchr[i].m_name = 0;
432         }
433     }
434     for (i = 0; i < nuk_maxno; i++) {
435         if (opt_NO_HCMS)
436             nchr[i].n_hcm = 0;
437         if (opt_NO_LCMS)
438             nchr[i].n_lcm = 0;
439     }
440     for (i = 0; i <= SCT_MAXDEF; i++) {
441         if (opt_NO_HCMS)
442             dchr[i].d_hcms = 0;
443         if (opt_NO_LCMS)
444             dchr[i].d_lcms = 0;
445     }
446     for (i = 0; i < prd_maxno; i++) {
447         for (j = 0; j < MAXPRCON; j++) {
448             if (opt_NO_HCMS && pchr[i].p_ctype[j] == I_HCM)
449                 pchr[i].p_camt[j] = 0;
450             if (opt_NO_LCMS && pchr[i].p_ctype[j] == I_LCM)
451                 pchr[i].p_camt[j] = 0;
452             if (opt_NO_OIL && pchr[i].p_ctype[j] == I_OIL)
453                 pchr[i].p_camt[j] = 0;
454         }
455     }
456 }
457
458 #if defined(_WIN32)
459 static void
460 loc_NTInit()
461 {
462     int rc;
463     WORD wVersionRequested;
464     WSADATA wsaData;
465
466     wVersionRequested = MAKEWORD(2, 0);
467     rc = WSAStartup(wVersionRequested, &wsaData);
468     if (rc != 0) {
469         logerror("WSAStartup failed.  %d", rc);
470         _exit(1);
471     }
472 }
473 #endif
474
475 #if defined(_WIN32)
476 static void
477 loc_NTTerm()
478 {
479     WSACleanup();
480 }
481 #endif