webperimental: killstack decides stack protects.
[freeciv.git] / server / civserver.c
blobca4eda23532c7c11c0588865a695293834886bb2
1 /***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 #include "fc_prehdrs.h"
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
24 #ifdef HAVE_UNISTD_H
25 #include <unistd.h>
26 #endif
28 #ifdef HAVE_SIGNAL_H
29 #include <signal.h>
30 #endif
32 #ifdef GENERATING_MAC /* mac header(s) */
33 #include <Controls.h>
34 #include <Dialogs.h>
35 #endif
37 #ifdef FREECIV_MSWINDOWS
38 #include <windows.h>
39 #endif
41 /* utility */
42 #include "deprecations.h"
43 #include "fc_cmdline.h"
44 #include "fciconv.h"
45 #include "fcintl.h"
46 #include "log.h"
47 #include "support.h"
48 #include "timing.h"
50 /* common */
51 #include "capstr.h"
52 #include "fc_cmdhelp.h"
53 #include "game.h"
54 #include "version.h"
56 /* server */
57 #include "aiiface.h"
58 #include "console.h"
59 #include "meta.h"
60 #include "sernet.h"
61 #include "srv_main.h"
63 #ifdef GENERATING_MAC
64 static void Mac_options(int argc); /* don't need argv */
65 #endif
67 #ifdef HAVE_SIGNAL_H
68 # define USE_INTERRUPT_HANDLERS
69 #endif
71 #ifdef USE_INTERRUPT_HANDLERS
72 #define save_and_exit(sig) \
73 if (S_S_RUNNING == server_state()) { \
74 save_game_auto(#sig, AS_INTERRUPT); \
75 } \
76 exit(EXIT_SUCCESS);
78 /**************************************************************************
79 This function is called when a SIGINT (ctrl-c) is received. It will exit
80 only if two SIGINTs are received within a second.
81 **************************************************************************/
82 static void signal_handler(int sig)
84 static struct timer *timer = NULL;
86 switch (sig) {
87 case SIGINT:
88 if (timer && timer_read_seconds(timer) <= 1.0) {
89 save_and_exit(SIGINT);
90 } else {
91 if (game.info.timeout == -1) {
92 log_normal(_("Setting timeout to 0. Autogame will stop."));
93 game.info.timeout = 0;
95 if (!timer) {
96 log_normal(_("You must interrupt Freeciv twice "
97 "within one second to make it exit."));
100 timer = timer_renew(timer, TIMER_USER, TIMER_ACTIVE);
101 timer_start(timer);
102 break;
104 #ifdef SIGHUP
105 case SIGHUP:
106 save_and_exit(SIGHUP);
107 break;
108 #endif /* SIGHUP */
110 case SIGTERM:
111 save_and_exit(SIGTERM);
112 break;
114 #ifdef SIGPIPE
115 case SIGPIPE:
116 if (signal(SIGPIPE, signal_handler) == SIG_ERR) {
117 /* Because the signal may have interrupted arbitrary code, we use
118 * fprintf() and _exit() here instead of log_*() and exit() so
119 * that we don't accidentally call any "unsafe" functions here
120 * (see the manual page for the signal function). */
121 fprintf(stderr, "\nFailed to reset SIGPIPE handler "
122 "while handling SIGPIPE.\n");
123 _exit(EXIT_FAILURE);
125 break;
126 #endif /* SIGPIPE */
129 #endif /* USE_INTERRUPT_HANDLERS */
131 /**************************************************************************
132 Entry point for Freeciv server. Basically, does two things:
133 1. Parses command-line arguments (possibly dialog, on mac).
134 2. Calls the main server-loop routine.
135 **************************************************************************/
136 int main(int argc, char *argv[])
138 int inx;
139 bool showhelp = FALSE;
140 bool showvers = FALSE;
141 char *option = NULL;
143 /* Load win32 post-crash debugger */
144 #ifdef FREECIV_MSWINDOWS
145 # ifndef FREECIV_NDEBUG
146 if (LoadLibrary("exchndl.dll") == NULL) {
147 # ifdef FREECIV_DEBUG
148 fprintf(stderr, "exchndl.dll could not be loaded, no crash debugger\n");
149 # endif /* FREECIV_DEBUG */
151 # endif /* FREECIV_NDEBUG */
152 #endif /* FREECIV_MSWINDOWS */
154 #ifdef USE_INTERRUPT_HANDLERS
155 if (SIG_ERR == signal(SIGINT, signal_handler)) {
156 fc_fprintf(stderr, _("Failed to install SIGINT handler: %s\n"),
157 fc_strerror(fc_get_errno()));
158 exit(EXIT_FAILURE);
161 #ifdef SIGHUP
162 if (SIG_ERR == signal(SIGHUP, signal_handler)) {
163 fc_fprintf(stderr, _("Failed to install SIGHUP handler: %s\n"),
164 fc_strerror(fc_get_errno()));
165 exit(EXIT_FAILURE);
167 #endif /* SIGHUP */
169 if (SIG_ERR == signal(SIGTERM, signal_handler)) {
170 fc_fprintf(stderr, _("Failed to install SIGTERM handler: %s\n"),
171 fc_strerror(fc_get_errno()));
172 exit(EXIT_FAILURE);
175 #ifdef SIGPIPE
176 /* Ignore SIGPIPE, the error is handled by the return value
177 * of the write call. */
178 if (SIG_ERR == signal(SIGPIPE, signal_handler)) {
179 fc_fprintf(stderr, _("Failed to ignore SIGPIPE: %s\n"),
180 fc_strerror(fc_get_errno()));
181 exit(EXIT_FAILURE);
183 #endif /* SIGPIPE */
184 #endif /* USE_INTERRUPT_HANDLERS */
186 /* initialize server */
187 srv_init();
189 /* parse command-line arguments... */
191 #ifdef GENERATING_MAC
192 Mac_options(argc);
193 #endif
194 srvarg.announce = ANNOUNCE_DEFAULT;
196 game.server.meta_info.type[0] = '\0';
198 /* no we don't use GNU's getopt or even the "standard" getopt */
199 /* yes we do have reasons ;) */
200 /* FIXME: and that are? */
201 inx = 1;
202 while (inx < argc) {
203 if ((option = get_option_malloc("--file", argv, &inx, argc,
204 FALSE))) {
205 sz_strlcpy(srvarg.load_filename, option);
206 free(option);
207 } else if (is_option("--help", argv[inx])) {
208 showhelp = TRUE;
209 break;
210 } else if ((option = get_option_malloc("--log", argv, &inx, argc, TRUE))) {
211 srvarg.log_filename = option;
212 #ifndef FREECIV_NDEBUG
213 } else if (is_option("--Fatal", argv[inx])) {
214 if (inx + 1 >= argc || '-' == argv[inx + 1][0]) {
215 srvarg.fatal_assertions = SIGABRT;
216 } else if (str_to_int(argv[inx + 1], &srvarg.fatal_assertions)) {
217 inx++;
218 } else {
219 fc_fprintf(stderr, _("Invalid signal number \"%s\".\n"),
220 argv[inx + 1]);
221 inx++;
222 showhelp = TRUE;
224 #endif /* FREECIV_NDEBUG */
225 } else if ((option = get_option_malloc("--Ranklog", argv, &inx, argc, TRUE))) {
226 srvarg.ranklog_filename = option;
227 } else if (is_option("--keep", argv[inx])) {
228 srvarg.metaconnection_persistent = TRUE;
229 /* Implies --meta */
230 srvarg.metaserver_no_send = FALSE;
231 } else if (is_option("--nometa", argv[inx])) {
232 fc_fprintf(stderr, _("Warning: the %s option is obsolete. "
233 "Use -m to enable the metaserver.\n"), argv[inx]);
234 showhelp = TRUE;
235 } else if (is_option("--meta", argv[inx])) {
236 srvarg.metaserver_no_send = FALSE;
237 } else if ((option = get_option_malloc("--Metaserver",
238 argv, &inx, argc, FALSE))) {
239 sz_strlcpy(srvarg.metaserver_addr, option);
240 free(option);
241 srvarg.metaserver_no_send = FALSE; /* --Metaserver implies --meta */
242 } else if ((option = get_option_malloc("--identity",
243 argv, &inx, argc, FALSE))) {
244 sz_strlcpy(srvarg.identity_name, option);
245 free(option);
246 } else if ((option = get_option_malloc("--port", argv, &inx, argc, FALSE))) {
247 if (!str_to_int(option, &srvarg.port)) {
248 showhelp = TRUE;
249 break;
251 free(option);
252 } else if ((option = get_option_malloc("--bind", argv, &inx, argc, TRUE))) {
253 srvarg.bind_addr = option;
254 } else if ((option = get_option_malloc("--Bind-meta", argv, &inx, argc, TRUE))) {
255 srvarg.bind_meta_addr = option;
256 #ifdef FREECIV_WEB
257 } else if ((option = get_option_malloc("--type", argv, &inx, argc, FALSE))) {
258 sz_strlcpy(game.server.meta_info.type, option);
259 free(option);
260 #endif /* FREECIV_WEB */
261 } else if ((option = get_option_malloc("--read", argv, &inx, argc, TRUE)))
262 srvarg.script_filename = option;
263 else if ((option = get_option_malloc("--quitidle", argv, &inx, argc, FALSE))) {
264 if (!str_to_int(option, &srvarg.quitidle)) {
265 showhelp = TRUE;
266 break;
268 free(option);
269 } else if (is_option("--exit-on-end", argv[inx])) {
270 srvarg.exit_on_end = TRUE;
271 } else if ((option = get_option_malloc("--debug", argv, &inx, argc, FALSE))) {
272 if (!log_parse_level_str(option, &srvarg.loglevel)) {
273 showhelp = TRUE;
274 break;
276 free(option);
277 #ifdef HAVE_FCDB
278 } else if ((option = get_option_malloc("--Database", argv, &inx, argc, FALSE))) {
279 /* Freed after file has been loaded - not here nor in server quit */
280 srvarg.fcdb_enabled = TRUE;
281 srvarg.fcdb_conf = option;
282 } else if (is_option("--auth", argv[inx])) {
283 srvarg.auth_enabled = TRUE;
284 } else if (is_option("--Guests", argv[inx])) {
285 srvarg.auth_allow_guests = TRUE;
286 } else if (is_option("--Newusers", argv[inx])) {
287 srvarg.auth_allow_newusers = TRUE;
288 #endif /* HAVE_FCDB */
289 } else if ((option = get_option_malloc("--Serverid", argv, &inx, argc, FALSE))) {
290 sz_strlcpy(srvarg.serverid, option);
291 free(option);
292 } else if ((option = get_option_malloc("--saves", argv, &inx, argc, TRUE))) {
293 srvarg.saves_pathname = option;
294 } else if ((option = get_option_malloc("--scenarios", argv, &inx, argc, TRUE))) {
295 srvarg.scenarios_pathname = option;
296 } else if ((option = get_option_malloc("--ruleset", argv, &inx, argc, TRUE))) {
297 srvarg.ruleset = option;
298 } else if (is_option("--version", argv[inx])) {
299 showvers = TRUE;
300 } else if ((option = get_option_malloc("--Announce", argv, &inx, argc, FALSE))) {
301 if (!strcasecmp(option, "ipv4")) {
302 srvarg.announce = ANNOUNCE_IPV4;
303 } else if (!strcasecmp(option, "none")) {
304 srvarg.announce = ANNOUNCE_NONE;
305 #ifdef FREECIV_IPV6_SUPPORT
306 } else if (!strcasecmp(option, "ipv6")) {
307 srvarg.announce = ANNOUNCE_IPV6;
308 #endif /* IPv6 support */
309 } else {
310 log_error(_("Illegal value \"%s\" for --Announce"), option);
312 free(option);
313 } else if (is_option("--warnings", argv[inx])) {
314 deprecation_warnings_enable();
315 #ifdef AI_MODULES
316 } else if ((option = get_option_malloc("--LoadAI", argv, &inx, argc, FALSE))) {
317 if (!load_ai_module(option)) {
318 fc_fprintf(stderr, _("Failed to load AI module \"%s\"\n"), option);
319 exit(EXIT_FAILURE);
321 free(option);
322 #endif /* AI_MODULES */
323 } else {
324 fc_fprintf(stderr, _("Error: unknown option '%s'\n"), argv[inx]);
325 showhelp = TRUE;
326 break;
328 inx++;
331 if (showvers && !showhelp) {
332 fc_fprintf(stderr, "%s \n", freeciv_name_version());
333 exit(EXIT_SUCCESS);
335 con_write(C_VERSION, _("This is the server for %s"), freeciv_name_version());
336 /* TRANS: No full stop after the URL, could cause confusion. */
337 con_write(C_COMMENT, _("You can learn a lot about Freeciv at %s"),
338 WIKI_URL);
340 if (showhelp) {
341 struct cmdhelp *help = cmdhelp_new(argv[0]);
343 cmdhelp_add(help, "A",
344 /* TRANS: "Announce" is exactly what user must type, do not translate. */
345 _("Announce PROTO"),
346 _("Announce game in LAN using protocol PROTO "
347 "(IPv4/IPv6/none)"));
348 #ifdef HAVE_FCDB
349 cmdhelp_add(help, "D",
350 /* TRANS: "Database" is exactly what user must type, do not translate. */
351 _("Database FILE"),
352 _("Enable database connection with configuration from "
353 "FILE."));
354 cmdhelp_add(help, "a", "auth",
355 _("Enable server authentication (requires --Database)."));
356 cmdhelp_add(help, "G", "Guests",
357 _("Allow guests to login if auth is enabled."));
358 cmdhelp_add(help, "N", "Newusers",
359 _("Allow new users to login if auth is enabled."));
360 #endif /* HAVE_FCDB */
361 cmdhelp_add(help, "b",
362 /* TRANS: "bind" is exactly what user must type, do not translate. */
363 _("bind ADDR"),
364 _("Listen for clients on ADDR"));
365 cmdhelp_add(help, "B", "Bind-meta ADDR",
366 _("Connect to metaserver from this address"));
367 #ifdef FREECIV_DEBUG
368 cmdhelp_add(help, "d",
369 /* TRANS: "debug" is exactly what user must type, do not translate. */
370 _("debug LEVEL"),
371 _("Set debug log level (%d to %d, or %d:file1,min,max:...)"),
372 LOG_FATAL, LOG_DEBUG, LOG_DEBUG);
373 #else /* FREECIV_DEBUG */
374 cmdhelp_add(help, "d",
375 /* TRANS: "debug" is exactly what user must type, do not translate. */
376 _("debug LEVEL"),
377 _("Set debug log level (%d to %d)"), LOG_FATAL, LOG_VERBOSE);
378 #endif /* FREECIV_DEBUG */
379 #ifndef FREECIV_NDEBUG
380 cmdhelp_add(help, "F",
381 /* TRANS: "Fatal" is exactly what user must type, do not translate. */
382 _("Fatal [SIGNAL]"),
383 _("Raise a signal on failed assertion"));
384 #endif /* FREECIV_NDEBUG */
385 cmdhelp_add(help, "f",
386 /* TRANS: "file" is exactly what user must type, do not translate. */
387 _("file FILE"),
388 _("Load saved game FILE"));
389 cmdhelp_add(help, "h", "help",
390 _("Print a summary of the options"));
391 cmdhelp_add(help, "i",
392 /* TRANS: "identity" is exactly what user must type, do not translate. */
393 _("identity ADDR"),
394 _("Be known as ADDR at metaserver or LAN client"));
395 cmdhelp_add(help, "l",
396 /* TRANS: "log" is exactly what user must type, do not translate. */
397 _("log FILE"),
398 _("Use FILE as logfile"));
399 cmdhelp_add(help, "m", "meta",
400 _("Notify metaserver and send server's info"));
401 cmdhelp_add(help, "M",
402 /* TRANS: "Metaserver" is exactly what user must type, do not translate. */
403 _("Metaserver ADDR"),
404 _("Set ADDR as metaserver address"));
405 #ifdef FREECIV_WEB
406 cmdhelp_add(help, "t",
407 /* TRANS: "type" is exactly what user must type, do not translate. */
408 _("type TYPE"),
409 _("Set TYPE as server type in metaserver"));
410 #endif /* FREECIV_WEB */
411 cmdhelp_add(help, "k", "keep",
412 _("Keep updating game information on metaserver even after "
413 "failure")),
414 cmdhelp_add(help, "p",
415 /* TRANS: "port" is exactly what user must type, do not translate. */
416 _("port PORT"),
417 _("Listen for clients on port PORT"));
418 cmdhelp_add(help, "q",
419 /* TRANS: "quitidle" is exactly what user must type, do not translate. */
420 _("quitidle TIME"),
421 _("Quit if no players for TIME seconds"));
422 cmdhelp_add(help, "e", "exit-on-end",
423 _("When a game ends, exit instead of restarting"));
424 cmdhelp_add(help, "s",
425 /* TRANS: "saves" is exactly what user must type, do not translate. */
426 _("saves DIR"),
427 _("Save games to directory DIR"));
428 cmdhelp_add(help, NULL,
429 /* TRANS: "scenarios" is exactly what user must type, do not translate. */
430 _("scenarios DIR"),
431 _("Save scenarios to directory DIR"));
432 cmdhelp_add(help, "S",
433 /* TRANS: "Serverid" is exactly what user must type, do not translate. */
434 _("Serverid ID"),
435 _("Sets the server id to ID"));
436 cmdhelp_add(help, "r",
437 /* TRANS: "read" is exactly what user must type, do not translate. */
438 _("read FILE"),
439 _("Read startup script FILE"));
440 cmdhelp_add(help, "R",
441 /* TRANS: "Ranklog" is exactly what user must type, do not translate. */
442 _("Ranklog FILE"),
443 _("Use FILE as ranking logfile"));
444 cmdhelp_add(help, NULL,
445 /* TRANS: "ruleset" is exactly what user must type, do not translate. */
446 _("ruleset RULESET"),
447 _("Load ruleset RULESET"));
448 #ifdef AI_MODULES
449 cmdhelp_add(help, "L",
450 /* TRANS: "LoadAI" is exactly what user must type, do not translate. */
451 _("LoadAI MODULE"),
452 _("Load ai module MODULE. Can appear multiple times"));
453 #endif /* AI_MODULES */
454 cmdhelp_add(help, "v", "version",
455 _("Print the version number"));
456 cmdhelp_add(help, "w", "warnings",
457 _("Warn about deprecated modpack constructs"));
459 /* The function below prints a header and footer for the options.
460 * Furthermore, the options are sorted. */
461 cmdhelp_display(help, TRUE, FALSE, TRUE);
462 cmdhelp_destroy(help);
464 exit(EXIT_SUCCESS);
467 #ifdef HAVE_FCDB
468 if (srvarg.auth_enabled && !srvarg.fcdb_enabled) {
469 fc_fprintf(stderr,
470 _("Requested authentication with --auth, "
471 "but no --Database given\n"));
472 exit(EXIT_FAILURE);
474 #endif /* HAVE_FCDB */
476 /* disallow running as root -- too dangerous */
477 dont_run_as_root(argv[0], "freeciv_server");
479 init_our_capability();
481 /* have arguments, call the main server loop... */
482 srv_main();
484 /* Technically, we won't ever get here. We exit via server_quit. */
486 /* done */
487 exit(EXIT_SUCCESS);
490 #ifdef GENERATING_MAC
491 /*************************************************************************
492 generate an option dialog if no options have been passed in
493 *************************************************************************/
494 static void Mac_options(int argc)
496 #define HARDCODED_OPT
497 /*temporary hack since GetNewDialog() doesn't want to work*/
498 #ifdef HARDCODED_OPT
499 srvarg.log_filename = "log.out";
500 srvarg.loglevel = LOG_DEBUG;
501 #else /* HARDCODED_OPT */
502 if (argc == 0) {
503 OSErr err;
504 DialogPtr optptr;
505 Ptr storage;
506 Handle ditl;
507 Handle dlog;
508 short the_type;
509 Handle the_handle;
510 Rect the_rect;
511 short the_item, old_item=16;
512 int done = false;
513 Str255 the_string;
515 /* load/init the stuff for the dialog */
516 storage = NewPtr(sizeof(DialogRecord));
517 if (storage == 0) {
518 exit(EXIT_FAILURE);
520 ditl = Get1Resource('DITL',200);
521 if ((ditl == 0) || (ResError())) {
522 exit(EXIT_FAILURE);
524 dlog = Get1Resource('DLOG',200);
525 if ((dlog == 0) || (ResError())) {
526 exit(EXIT_FAILURE);
528 /* make the dialog */
529 optptr = GetNewDialog(200, storage, (WindowPtr)-1L);
530 /* setup the dialog */
531 err = SetDialogDefaultItem(optptr, 1);
532 if (err != 0) {
533 exit(EXIT_FAILURE);
535 /* insert default highlight draw code? */
536 err=SetDialogCancelItem(optptr, 2);
537 if (err != 0) {
538 exit(EXIT_FAILURE);
540 err=SetDialogTracksCursor(optptr, true);
541 if (err != 0) {
542 exit(EXIT_FAILURE);
544 GetDItem(optptr, 16/*normal radio button*/, &the_type, &the_handle, &the_rect);
545 SetCtlValue((ControlHandle)the_handle, true);
547 while (!done) /* loop */
549 ModalDialog(0L, &the_item);/* don't feed 0 where a upp is expected? */
550 /* old book suggests using OL(NIL) as the first argument, but
551 It doesn't include anything about UPPs either, so... */
552 switch (the_item) {
553 case 1:
554 done = true;
555 break;
556 case 2:
557 exit(EXIT_SUCCESS);
558 break;
559 case 13:
560 GetDItem(optptr, 13, &the_type, &the_handle, &the_rect);
561 srvarg.metaserver_no_send=GetCtlValue((ControlHandle)the_handle);
562 SetCtlValue((ControlHandle)the_handle, !srvarg.metaserver_no_send);
563 break;
564 case 15:
565 case 16:
566 case 17:
567 GetDItem(optptr, old_item, &the_type, &the_handle, &the_rect);
568 SetCtlValue((ControlHandle)the_handle, false);
569 old_item=the_item;
570 GetDItem(optptr, the_item, &the_type, &the_handle, &the_rect);
571 SetCtlValue((ControlHandle)the_handle, true);
572 break;
575 /* now, load the dialog items into the correct variables interpritation */
576 GetDItem( optptr, 4, &the_type, &the_handle, &the_rect);
577 GetIText( the_handle, (unsigned char *)srvarg.load_filename);
578 GetDItem( optptr, 8, &the_type, &the_handle, &the_rect);
579 GetIText( the_handle, (unsigned char *)srvarg.log_filename);
580 GetDItem( optptr, 12, &the_type, &the_handle, &the_rect);
581 GetIText( the_handle, the_string);
582 sscanf(the_string, "%d", srvarg.port);
583 GetDItem( optptr, 10, &the_type, &the_handle, &the_rect);
584 GetIText( the_handle, (unsigned char *)srvarg.script_filename);
585 GetDItem(optptr, 15, &the_type, &the_handle, &the_rect);
586 if (GetControlValue((ControlHandle)the_handle)) {
587 srvarg.loglevel = LOG_FATAL;
589 GetDItem(optptr, 16, &the_type, &the_handle, &the_rect);
590 if (GetControlValue((ControlHandle)the_handle)) {
591 srvarg.loglevel = LOG_NORMAL;
593 GetDItem(optptr, 17, &the_type, &the_handle, &the_rect);
594 if (GetControlValue((ControlHandle)the_handle)) {
595 srvarg.loglevel = LOG_VERBOSE;
597 DisposeDialog(optptr);/*get rid of the dialog after sorting out the options*/
598 DisposePtr(storage);/*clean up the allocated memory*/
600 #endif /* HARDCODED_OPT */
601 #undef HARDCODED_OPT
603 #endif /* GENERATING_MAC */