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