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