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