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)
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 ***********************************************************************/
15 #include <fc_config.h>
18 #include "fc_prehdrs.h"
32 #ifdef GENERATING_MAC /* mac header(s) */
42 #include "deprecations.h"
43 #include "fc_cmdline.h"
52 #include "fc_cmdhelp.h"
64 static void Mac_options(int argc
); /* don't need argv */
68 # define USE_INTERRUPT_HANDLERS
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); \
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
;
88 if (timer
&& timer_read_seconds(timer
) <= 1.0) {
89 save_and_exit(SIGINT
);
91 if (game
.info
.timeout
== -1) {
92 log_normal(_("Setting timeout to 0. Autogame will stop."));
93 game
.info
.timeout
= 0;
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
);
106 save_and_exit(SIGHUP
);
111 save_and_exit(SIGTERM
);
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");
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
[])
139 bool showhelp
= FALSE
;
140 bool showvers
= FALSE
;
143 /* Load win32 post-crash debugger */
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 /* WIN32_NATIVE */
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()));
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()));
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()));
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()));
184 #endif /* USE_INTERRUPT_HANDLERS */
186 /* initialize server */
189 /* parse command-line arguments... */
191 #ifdef GENERATING_MAC
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? */
203 if ((option
= get_option_malloc("--file", argv
, &inx
, argc
,
205 sz_strlcpy(srvarg
.load_filename
, option
);
207 } else if (is_option("--help", argv
[inx
])) {
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
)) {
219 fc_fprintf(stderr
, _("Invalid signal number \"%s\".\n"),
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
;
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
]);
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
);
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
);
246 } else if ((option
= get_option_malloc("--port", argv
, &inx
, argc
, FALSE
))) {
247 if (!str_to_int(option
, &srvarg
.port
)) {
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
;
257 } else if ((option
= get_option_malloc("--type", argv
, &inx
, argc
, FALSE
))) {
258 sz_strlcpy(game
.server
.meta_info
.type
, 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
)) {
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
)) {
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
);
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 (is_option("--version", argv
[inx
])) {
298 } else if ((option
= get_option_malloc("--Announce", argv
, &inx
, argc
, FALSE
))) {
299 if (!strcasecmp(option
, "ipv4")) {
300 srvarg
.announce
= ANNOUNCE_IPV4
;
301 } else if(!strcasecmp(option
, "none")) {
302 srvarg
.announce
= ANNOUNCE_NONE
;
303 #ifdef FREECIV_IPV6_SUPPORT
304 } else if (!strcasecmp(option
, "ipv6")) {
305 srvarg
.announce
= ANNOUNCE_IPV6
;
306 #endif /* IPv6 support */
308 log_error(_("Illegal value \"%s\" for --Announce"), option
);
311 } else if (is_option("--warnings", argv
[inx
])) {
312 deprecation_warnings_enable();
314 } else if ((option
= get_option_malloc("--LoadAI", argv
, &inx
, argc
, FALSE
))) {
315 if (!load_ai_module(option
)) {
316 fc_fprintf(stderr
, _("Failed to load AI module \"%s\"\n"), option
);
320 #endif /* AI_MODULES */
322 fc_fprintf(stderr
, _("Error: unknown option '%s'\n"), argv
[inx
]);
329 if (showvers
&& !showhelp
) {
330 fc_fprintf(stderr
, "%s \n", freeciv_name_version());
333 con_write(C_VERSION
, _("This is the server for %s"), freeciv_name_version());
334 /* TRANS: No full stop after the URL, could cause confusion. */
335 con_write(C_COMMENT
, _("You can learn a lot about Freeciv at %s"),
339 struct cmdhelp
*help
= cmdhelp_new(argv
[0]);
341 cmdhelp_add(help
, "A",
342 /* TRANS: "Announce" is exactly what user must type, do not translate. */
344 _("Announce game in LAN using protocol PROTO "
345 "(IPv4/IPv6/none)"));
347 cmdhelp_add(help
, "D",
348 /* TRANS: "Database" is exactly what user must type, do not translate. */
350 _("Enable database connection with configuration from "
352 cmdhelp_add(help
, "a", "auth",
353 _("Enable server authentication (requires --Database)."));
354 cmdhelp_add(help
, "G", "Guests",
355 _("Allow guests to login if auth is enabled."));
356 cmdhelp_add(help
, "N", "Newusers",
357 _("Allow new users to login if auth is enabled."));
358 #endif /* HAVE_FCDB */
359 cmdhelp_add(help
, "b",
360 /* TRANS: "bind" is exactly what user must type, do not translate. */
362 _("Listen for clients on ADDR"));
363 cmdhelp_add(help
, "B", "Bind-meta ADDR",
364 _("Connect to metaserver from this address"));
366 cmdhelp_add(help
, "d",
367 /* TRANS: "debug" is exactly what user must type, do not translate. */
369 _("Set debug log level (%d to %d, or %d:file1,min,max:...)"),
370 LOG_FATAL
, LOG_DEBUG
, LOG_DEBUG
);
371 #else /* FREECIV_DEBUG */
372 cmdhelp_add(help
, "d",
373 /* TRANS: "debug" is exactly what user must type, do not translate. */
375 _("Set debug log level (%d to %d)"), LOG_FATAL
, LOG_VERBOSE
);
376 #endif /* FREECIV_DEBUG */
377 #ifndef FREECIV_NDEBUG
378 cmdhelp_add(help
, "F",
379 /* TRANS: "Fatal" is exactly what user must type, do not translate. */
381 _("Raise a signal on failed assertion"));
382 #endif /* FREECIV_NDEBUG */
383 cmdhelp_add(help
, "f",
384 /* TRANS: "file" is exactly what user must type, do not translate. */
386 _("Load saved game FILE"));
387 cmdhelp_add(help
, "h", "help",
388 _("Print a summary of the options"));
389 cmdhelp_add(help
, "i",
390 /* TRANS: "identity" is exactly what user must type, do not translate. */
392 _("Be known as ADDR at metaserver or LAN client"));
393 cmdhelp_add(help
, "l",
394 /* TRANS: "log" is exactly what user must type, do not translate. */
396 _("Use FILE as logfile"));
397 cmdhelp_add(help
, "m", "meta",
398 _("Notify metaserver and send server's info"));
399 cmdhelp_add(help
, "M",
400 /* TRANS: "Metaserver" is exactly what user must type, do not translate. */
401 _("Metaserver ADDR"),
402 _("Set ADDR as metaserver address"));
404 cmdhelp_add(help
, "t",
405 /* TRANS: "type" is exactly what user must type, do not translate. */
407 _("Set TYPE as server type in metaserver"));
408 #endif /* FREECIV_WEB */
409 cmdhelp_add(help
, "k", "keep",
410 _("Keep updating game information on metaserver even after "
412 cmdhelp_add(help
, "p",
413 /* TRANS: "port" is exactly what user must type, do not translate. */
415 _("Listen for clients on port PORT"));
416 cmdhelp_add(help
, "q",
417 /* TRANS: "quitidle" is exactly what user must type, do not translate. */
419 _("Quit if no players for TIME seconds"));
420 cmdhelp_add(help
, "e", "exit-on-end",
421 _("When a game ends, exit instead of restarting"));
422 cmdhelp_add(help
, "s",
423 /* TRANS: "saves" is exactly what user must type, do not translate. */
425 _("Save games to directory DIR"));
426 cmdhelp_add(help
, NULL
,
427 /* TRANS: "scenarios" is exactly what user must type, do not translate. */
429 _("Save scenarios to directory DIR"));
430 cmdhelp_add(help
, "S",
431 /* TRANS: "Serverid" is exactly what user must type, do not translate. */
433 _("Sets the server id to ID"));
434 cmdhelp_add(help
, "r",
435 /* TRANS: "read" is exactly what user must type, do not translate. */
437 _("Read startup script FILE"));
438 cmdhelp_add(help
, "R",
439 /* TRANS: "Ranklog" is exactly what user must type, do not translate. */
441 _("Use FILE as ranking logfile"));
443 cmdhelp_add(help
, "L",
444 /* TRANS: "LoadAI" is exactly what user must type, do not translate. */
446 _("Load ai module MODULE. Can appear multiple times"));
447 #endif /* AI_MODULES */
448 cmdhelp_add(help
, "v", "version",
449 _("Print the version number"));
450 cmdhelp_add(help
, "w", "warnings",
451 _("Warn about deprecated modpack constructs"));
453 /* The function below prints a header and footer for the options.
454 * Furthermore, the options are sorted. */
455 cmdhelp_display(help
, TRUE
, FALSE
, TRUE
);
456 cmdhelp_destroy(help
);
462 if (srvarg
.auth_enabled
&& !srvarg
.fcdb_enabled
) {
464 _("Requested authentication with --auth, "
465 "but no --Database given\n"));
468 #endif /* HAVE_FCDB */
470 /* disallow running as root -- too dangerous */
471 dont_run_as_root(argv
[0], "freeciv_server");
473 init_our_capability();
475 /* have arguments, call the main server loop... */
478 /* Technically, we won't ever get here. We exit via server_quit. */
484 #ifdef GENERATING_MAC
485 /*************************************************************************
486 generate an option dialog if no options have been passed in
487 *************************************************************************/
488 static void Mac_options(int argc
)
490 #define HARDCODED_OPT
491 /*temporary hack since GetNewDialog() doesn't want to work*/
493 srvarg
.log_filename
= "log.out";
494 srvarg
.loglevel
= LOG_DEBUG
;
495 #else /* HARDCODED_OPT */
505 short the_item
, old_item
=16;
509 /* load/init the stuff for the dialog */
510 storage
= NewPtr(sizeof(DialogRecord
));
514 ditl
= Get1Resource('DITL',200);
515 if ((ditl
== 0) || (ResError())) {
518 dlog
= Get1Resource('DLOG',200);
519 if ((dlog
== 0) || (ResError())) {
522 /* make the dialog */
523 optptr
= GetNewDialog(200, storage
, (WindowPtr
)-1L);
524 /* setup the dialog */
525 err
= SetDialogDefaultItem(optptr
, 1);
529 /* insert default highlight draw code? */
530 err
=SetDialogCancelItem(optptr
, 2);
534 err
=SetDialogTracksCursor(optptr
, true);
538 GetDItem(optptr
, 16/*normal radio button*/, &the_type
, &the_handle
, &the_rect
);
539 SetCtlValue((ControlHandle
)the_handle
, true);
541 while (!done
) /* loop */
543 ModalDialog(0L, &the_item
);/* don't feed 0 where a upp is expected? */
544 /* old book suggests using OL(NIL) as the first argument, but
545 It doesn't include anything about UPPs either, so... */
554 GetDItem(optptr
, 13, &the_type
, &the_handle
, &the_rect
);
555 srvarg
.metaserver_no_send
=GetCtlValue((ControlHandle
)the_handle
);
556 SetCtlValue((ControlHandle
)the_handle
, !srvarg
.metaserver_no_send
);
561 GetDItem(optptr
, old_item
, &the_type
, &the_handle
, &the_rect
);
562 SetCtlValue((ControlHandle
)the_handle
, false);
564 GetDItem(optptr
, the_item
, &the_type
, &the_handle
, &the_rect
);
565 SetCtlValue((ControlHandle
)the_handle
, true);
569 /* now, load the dialog items into the correct variables interpritation */
570 GetDItem( optptr
, 4, &the_type
, &the_handle
, &the_rect
);
571 GetIText( the_handle
, (unsigned char *)srvarg
.load_filename
);
572 GetDItem( optptr
, 8, &the_type
, &the_handle
, &the_rect
);
573 GetIText( the_handle
, (unsigned char *)srvarg
.log_filename
);
574 GetDItem( optptr
, 12, &the_type
, &the_handle
, &the_rect
);
575 GetIText( the_handle
, the_string
);
576 sscanf(the_string
, "%d", srvarg
.port
);
577 GetDItem( optptr
, 10, &the_type
, &the_handle
, &the_rect
);
578 GetIText( the_handle
, (unsigned char *)srvarg
.script_filename
);
579 GetDItem(optptr
, 15, &the_type
, &the_handle
, &the_rect
);
580 if (GetControlValue((ControlHandle
)the_handle
)) {
581 srvarg
.loglevel
= LOG_FATAL
;
583 GetDItem(optptr
, 16, &the_type
, &the_handle
, &the_rect
);
584 if (GetControlValue((ControlHandle
)the_handle
)) {
585 srvarg
.loglevel
= LOG_NORMAL
;
587 GetDItem(optptr
, 17, &the_type
, &the_handle
, &the_rect
);
588 if (GetControlValue((ControlHandle
)the_handle
)) {
589 srvarg
.loglevel
= LOG_VERBOSE
;
591 DisposeDialog(optptr
);/*get rid of the dialog after sorting out the options*/
592 DisposePtr(storage
);/*clean up the allocated memory*/
594 #endif /* HARDCODED_OPT */
597 #endif /* GENERATING_MAC */