]> git.pond.sub.org Git - empserver/blobdiff - src/server/main.c
COPYING duplicates information from README. Remove. Move GPL from
[empserver] / src / server / main.c
index 95e401e72d4cdec34e189d3dae3edc4086995f91..841b5904bee7648fbd649f770f7282969cfb18c1 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Empire - A multi-player, client/server Internet based war game.
- *  Copyright (C) 1986-2004, Dave Pare, Jeff Bailey, Thomas Ruschak,
+ *  Copyright (C) 1986-2006, Dave Pare, Jeff Bailey, Thomas Ruschak,
  *                           Ken Stevens, Steve McClure
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -19,9 +19,9 @@
  *
  *  ---
  *
- *  See the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
- *  related information and legal notices. It is expected that any future
- *  projects/authors will amend these files as needed.
+ *  See files README, COPYING and CREDITS in the root of the source
+ *  tree for related information and legal notices.  It is expected
+ *  that future projects/authors will amend these files as needed.
  *
  *  ---
  *
  *     Doug Hay, 1998
  */
 
-#if defined(aix) || defined(linux)
-#include <unistd.h>
-#endif /* aix or linux */
+#include <config.h>
 
 #include <signal.h>
 #if !defined(_WIN32)
 #include <sys/ioctl.h>
 #endif
-#include <fcntl.h>
+#include <errno.h>
 #include <stdio.h>
 #include <string.h>
 
 #if defined(_WIN32)
-#include <winsock.h>
+#define WIN32
+#include <winsock2.h>
+#undef NS_ALL
 #include <process.h>
 #include "../lib/gen/getopt.h"
 #include "service.h"
+#include "direct.h"
 #endif
 
 #include "misc.h"
 #include "sect.h"
 #include "product.h"
 #include "optlist.h"
-#include "global.h"
 #include "server.h"
+#include "version.h"
 #include "prototypes.h"
 
-static void nullify_objects(void);
-static void init_files(void);
+static void create_pidfile(char *, pid_t);
 
 #if defined(_WIN32)
 static void loc_NTInit(void);
+static void loc_NTTerm(void);
 #endif
 
-static int mainpid = 0;
+static char pidfname[] = "server.pid";
 
-/*
- * Debugging?
- * If yes, don't fork into background, don't catch certain signals,
- * call abort() on internal error.
- */
-int debug = 0;
+/* Run as daemon?  If yes, detach from controlling terminal etc. */
+int daemonize = 1;
 
 static void
 print_usage(char *program_name)
 {
-#if defined(_WIN32)
-    printf("Usage: %s -i -r -D datadir -e config_file -d\n", program_name);
-    printf("-i install service\n");
-    printf("-r remove service\n");
-#else
-    printf("Usage: %s -D datadir -e config_file -d -p -s\n", program_name);
-    printf("-p print flag\n");
-    printf("-s stack check flag (include print flag)\n");
+    printf("Usage: %s [OPTION]...\n"
+          "  -d              debug mode\n"
+          "  -e CONFIG-FILE  configuration file\n"
+          "                  (default %s)\n"
+          "  -h              display this help and exit\n"
+#ifdef _WIN32
+          "  -i              install service `%s'\n"
+          "  -I NAME         install service NAME\n"
+#endif
+          "  -p              threading debug mode, implies -d\n"
+#ifdef _WIN32
+          "  -r              remove service `%s'\n"
+          "  -R NAME         remove service NAME\n"
 #endif
-    printf("-d debug mode\n");
+          "  -s              enable stack checking\n"
+          "  -v              display version information and exit\n",
+          program_name, dflt_econfig
+#ifdef _WIN32
+          , DEFAULT_SERVICE_NAME, DEFAULT_SERVICE_NAME
+#endif
+       );
 }
 
 int
@@ -105,157 +113,191 @@ main(int argc, char **argv)
     int flags = 0;
 #if defined(_WIN32)
     int install_service_set = 0;
+    char *program_name = NULL;
+    char *service_name = NULL;
     int remove_service_set = 0;
-    int datadir_set = 0;
 #endif
-    int op;
     char *config_file = NULL;
-    s_char tbuf[256];
-
-    loginit("server");
-
-    mainpid = getpid();
+    int op;
 
-#if defined(_WIN32)
-    while ((op = getopt(argc, argv, "D:de:irh")) != EOF) {
+#ifdef _WIN32
+# define XOPTS "iI:rR:"
 #else
-    while ((op = getopt(argc, argv, "D:de:psh")) != EOF) {
+# define XOPTS
 #endif
+    while ((op = getopt(argc, argv, "de:hpsv" XOPTS)) != EOF) {
        switch (op) {
-       case 'D':
-           datadir = optarg;
-#if defined(_WIN32)
-           datadir_set++;
-#endif
-           break;
+       case 'p':
+           flags |= EMPTH_PRINT;
+           /* fall through */
        case 'd':
-           debug++;
+           debug = 1;
+           daemonize = 0;
            break;
        case 'e':
            config_file = optarg;
            break;
 #if defined(_WIN32)
+       case 'I':
+           service_name = optarg;
+           /*
+            * fall out
+            */
        case 'i':
            install_service_set++;
            break;
+       case 'R':
+           service_name = optarg;
+           /*
+            * fall out
+            */
        case 'r':
            remove_service_set++;
            break;
-#else
-       case 'p':
-           flags |= EMPTH_PRINT;
-           break;
+#endif
        case 's':
-           flags |= EMPTH_PRINT | EMPTH_STACKCHECK;
+           flags |= EMPTH_STACKCHECK;
            break;
-#endif
+       case 'v':
+           printf("Wolfpack Empire %d.%d.%d\n",
+                  EMP_VERS_MAJOR, EMP_VERS_MINOR, EMP_VERS_PATCH);
+           return EXIT_SUCCESS;
        case 'h':
-       default:
            print_usage(argv[0]);
+           return EXIT_SUCCESS;
+       default:
+           fprintf(stderr, "Try -h for help.\n");
            return EXIT_FAILURE;
        }
     }
 
 #if defined(_WIN32)
-    if ((debug || datadir_set || config_file != NULL) &&
-       (install_service_set || remove_service_set)) {
-       logerror("Can't use -d or -D or -e with either "
-           "-r or -i options when starting the server");
-       printf("Can't use -d or -D or -e with either -r "
-           "or -i options\n");
+    if ((debug || flags || config_file != NULL) &&
+       remove_service_set) {
+       fprintf(stderr, "Can't use -p, -s, -d or -e with either "
+           "-r or -R options\n");
+       exit(EXIT_FAILURE);
+    }
+    if ((debug || flags) && install_service_set) {
+       fprintf(stderr, "Can't use -d, -p or -s with either "
+           "-i or -I options\n");
        exit(EXIT_FAILURE);
     }
     if (install_service_set && remove_service_set) {
-       logerror("Can't use both -r and -i options when starting "
-           "the server");
-       printf("Can't use both -r and -i options\n");
+       fprintf(stderr, "Can't use both -r or -R and -i or -I "
+           "options\n");
        exit(EXIT_FAILURE);
     }
-    if (install_service_set)
-        return install_service(argv[0]);
+#endif /* _WIN32 */
+
+
+#if defined(_WIN32)
     if (remove_service_set)
-        return remove_service();
+        return remove_service(service_name);
+    if (install_service_set) {
+       program_name = _fullpath(NULL, argv[0], 0);
+       if (config_file != NULL)
+           config_file = _fullpath(NULL, config_file, 0);
+    }
 #endif /* _WIN32 */
 
-    if (config_file == NULL) {
-       sprintf(tbuf, "%s/econfig", datadir);
-       config_file = tbuf;
+    if (emp_config(config_file) < 0)
+       exit(EXIT_FAILURE);
+    if (chdir(datadir)) {
+       fprintf(stderr, "Can't chdir to %s (%s)\n", datadir, strerror(errno));
+       exit(EXIT_FAILURE);
     }
 
-    logerror("------------------------------------------------------");
-    logerror("Empire server (pid %d) started", (int)getpid());
+#if defined(_WIN32)
+    if (install_service_set)
+       return install_service(program_name, service_name, config_file);
+#endif /* _WIN32 */
+
+    init_server();
 
 #if defined(_WIN32)
-    if (debug == 0) {
+    if (daemonize != 0) {
        SERVICE_TABLE_ENTRY DispatchTable[]={{"Empire Server", service_main},{NULL, NULL}};
        if (StartServiceCtrlDispatcher(DispatchTable))
            return 0;
-       else
+       else {
+           /*
+            * If it is service startup error then exit otherwise
+            * start server in the foreground
+            */
            if (GetLastError() != ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
-               logerror("Failed to dispatch service (%d)", GetLastError());
-               printf("Failed to dispatch service (%d)\n", GetLastError());
+               logerror("Failed to dispatch service (%lu)", GetLastError());
+               finish_server();
                exit(EXIT_FAILURE);
-           } else  /* start in the foreground */
-               debug = 1;
-    }
-#else
-    if (debug == 0 && flags == 0) {
-       disassoc();
+           }
+       }
     }
-#endif
-
-    start_server(flags, config_file);
-
-#if defined(__linux__) && defined(_EMPTH_POSIX)
-    strcpy(tbuf, argv[0]);
-    for (op = 1; op < argc; op++) {
-       strcat(tbuf, " ");
-       strcat(tbuf, argv[op]);
+    daemonize = 0;
+#else  /* !_WIN32 */
+    if (daemonize) {
+       if (disassoc() < 0) {
+           logerror("Can't become daemon (%s)", strerror(errno));
+           _exit(1);
+       }
     }
-    sprintf(argv[0], "%s (main pid: %d)", tbuf, getpid());
-#endif
+#endif /* !_WIN32 */
+    start_server(flags);
 
     empth_exit();
 
-/* We should never get here.  But, just in case... */
-    close_files();
-
-#if defined(_WIN32)
-    loc_NTTerm();
-#endif
+    CANT_HAPPEN("main thread terminated");
+    finish_server();
     return EXIT_SUCCESS;
 }
 
 
+/*
+ * Initialize for serving, acquire resources.
+ */
 void
-start_server(int flags, char *config_file)
+init_server(void)
 {
-#ifdef POSIXSIGNALS
-    struct sigaction act;
-#endif /* POSIXSIGNALS */
-
+    srandom(time(NULL));
 #if defined(_WIN32)
     loc_NTInit();
 #endif
-    emp_config(config_file);
     update_policy_check();
+    shutdown_init();
+    player_init();
+    ef_init_srv();
+    io_init();
+    init_nreport();
+
+    if (opt_MOB_ACCESS) {
+       /* This fixes up mobility upon restart */
+       mobility_init();
+    }
 
-    nullify_objects();
+    loginit("server");
+}
+
+/*
+ * Start serving.
+ */
+void
+start_server(int flags)
+{
+    pid_t pid;
+#if !defined(_WIN32)
+    struct sigaction act;
+#endif
+
+    pid = getpid();
+    create_pidfile(pidfname, pid);
+    logerror("------------------------------------------------------");
+    logerror("Empire server (pid %d) started", (int)pid);
 
 #if !defined(_WIN32)
     /* signal() should not be used with mit pthreads. Anyway if u
        have a posix threads u definitly have posix signals -- Sasha */
-#if defined (POSIXSIGNALS) || defined (_EMPTH_POSIX)
-#ifdef SA_SIGINFO
-    act.sa_flags = SA_SIGINFO;
-#endif
+    act.sa_flags = 0;
     sigemptyset(&act.sa_mask);
     act.sa_handler = shutdwn;
-    /* pthreads on Linux use SIGUSR1 (*shrug*) so only catch it if not on
-       a Linux box running POSIX threads -- STM */
-#if !(defined(__linux__) && defined(_EMPTH_POSIX))
-    sigaction(SIGUSR1, &act, NULL);
-#endif
     sigaction(SIGTERM, &act, NULL);
     sigaction(SIGINT, &act, NULL);
     act.sa_handler = panic;
@@ -265,36 +307,9 @@ start_server(int flags, char *config_file)
     sigaction(SIGFPE, &act, NULL);
     act.sa_handler = SIG_IGN;
     sigaction(SIGPIPE, &act, NULL);
-#else
-    if (debug == 0 && flags == 0) {
-       /* pthreads on Linux use SIGUSR1 (*shrug*) so only catch it if not on
-          a Linux box running POSIX threads -- STM */
-#if !(defined(__linux__) && defined(_EMPTH_POSIX))
-       signal(SIGUSR1, shutdwn);
-#endif
-       signal(SIGTERM, shutdwn);
-       signal(SIGBUS, panic);
-       signal(SIGSEGV, panic);
-       signal(SIGILL, panic);
-       signal(SIGFPE, panic);
-       signal(SIGINT, shutdwn);
-    }
-    signal(SIGPIPE, SIG_IGN);
-#endif /* POSIXSIGNALS */
-#endif /* _WIN32 */
-    empth_init((char **)&player, flags);
-    srand(time(NULL));
-    global_init();
-    shutdown_init();
-    player_init();
-    ef_init();
-    init_files();
-    io_init();
+#endif /* !_WIN32 */
 
-    if (opt_MOB_ACCESS) {
-       /* This fixes up mobility upon restart */
-       mobility_init();
-    }
+    empth_init((void **)&player, flags);
 
     empth_create(PP_ACCEPT, player_accept, (50 * 1024), flags,
                 "AcceptPlayers", "Accept network connections", 0);
@@ -316,44 +331,29 @@ start_server(int flags, char *config_file)
     }
 }
 
-static void
-init_files(void)
+/*
+ * Finish serving, release resources.
+ */
+void
+finish_server(void)
 {
-    ef_open(EF_NATION, O_RDWR, EFF_MEM);
-    ef_open(EF_SECTOR, O_RDWR, EFF_MEM);
-    ef_open(EF_SHIP, O_RDWR, EFF_MEM);
-    ef_open(EF_PLANE, O_RDWR, EFF_MEM);
-    ef_open(EF_LAND, O_RDWR, EFF_MEM);
-    ef_open(EF_NEWS, O_RDWR, 0);
-    ef_open(EF_LOAN, O_RDWR, 0);
-    ef_open(EF_TREATY, O_RDWR, 0);
-    ef_open(EF_NUKE, O_RDWR, EFF_MEM);
-    ef_open(EF_POWER, O_RDWR, 0);
-    ef_open(EF_TRADE, O_RDWR, 0);
-    ef_open(EF_MAP, O_RDWR, EFF_MEM);
-    ef_open(EF_BMAP, O_RDWR, EFF_MEM);
-    ef_open(EF_COMM, O_RDWR, 0);
-    ef_open(EF_LOST, O_RDWR, 0);
+    ef_fin_srv();
+#if defined(_WIN32)
+    loc_NTTerm();
+#endif
+    remove(pidfname);
 }
 
-void
-close_files(void)
+static void
+create_pidfile(char *fname, pid_t pid)
 {
-    ef_close(EF_NATION);
-    ef_close(EF_SECTOR);
-    ef_close(EF_SHIP);
-    ef_close(EF_PLANE);
-    ef_close(EF_LAND);
-    ef_close(EF_NEWS);
-    ef_close(EF_LOAN);
-    ef_close(EF_TREATY);
-    ef_close(EF_NUKE);
-    ef_close(EF_POWER);
-    ef_close(EF_TRADE);
-    ef_close(EF_MAP);
-    ef_close(EF_COMM);
-    ef_close(EF_BMAP);
-    ef_close(EF_LOST);
+    FILE *pidf = fopen(fname, "w");
+    if (!pidf
+       || fprintf(pidf, "%d\n", (int)pid) < 0
+       || fclose(pidf)) {
+       logerror("Can't write PID file (%s)", strerror(errno));
+       exit(1);
+    }
 }
 
 /* we're going down.  try to close the files at least */
@@ -361,7 +361,6 @@ close_files(void)
 void
 panic(int sig)
 {
-#ifdef POSIXSIGNALS
     struct sigaction act;
 
     act.sa_flags = 0;
@@ -371,15 +370,9 @@ panic(int sig)
     sigaction(SIGSEGV, &act, NULL);
     sigaction(SIGILL, &act, NULL);
     sigaction(SIGFPE, &act, NULL);
-#else
-    signal(SIGBUS, SIG_DFL);
-    signal(SIGSEGV, SIG_DFL);
-    signal(SIGILL, SIG_DFL);
-    signal(SIGFPE, SIG_DFL);
-#endif /* POSIXSIGNALS */
     logerror("server received fatal signal %d", sig);
     log_last_commands();
-    close_files();
+    ef_fin_srv();
     if (CANT_HAPPEN(sig != SIGBUS && sig != SIGSEGV
                    && sig != SIGILL && sig != SIGFPE))
        _exit(1);
@@ -394,29 +387,6 @@ shutdwn(int sig)
     struct player *p;
     time_t now;
 
-#if defined(__linux__) && defined(_EMPTH_POSIX)
-/* This is a hack to get around the way pthreads work on Linux.  This
-   may be useful on other platforms too where threads are turned into
-   processes. */
-    if (getpid() != mainpid) {
-       empth_t *me;
-
-       me = empth_self();
-       if (me && me->name) {
-           if (strlen(me->name) > 5) {
-               /* Player threads are cleaned up below, so just have
-                  them return.  This should work. */
-               if (!strncmp("Player", me->name, 6)) {
-                   return;
-               }
-           }
-       }
-       /* Not a player thread - must be server thread, so exit */
-       empth_exit();
-       return;
-    }
-#endif
-
     logerror("Shutdown commencing (cleaning up threads.)");
 
     for (p = player_next(0); p != 0; p = player_next(p)) {
@@ -449,86 +419,21 @@ shutdwn(int sig)
     if (sig)
        logerror("Server shutting down on signal %d", sig);
     else
-       logerror("Server shutting down at Deity's request");
-    close_files();
-    _exit(0);
-}
+       logerror("Server shutting down at deity's request");
+    finish_server();
 
-
-static void
-nullify_objects(void)
-{
-    int i, j;
-
-    if (opt_BIG_CITY) {
-       dchr[SCT_CAPIT].d_flg = bigcity_dchr.d_flg;
-       dchr[SCT_CAPIT].d_pkg = bigcity_dchr.d_pkg;
-       dchr[SCT_CAPIT].d_build = bigcity_dchr.d_build;
-       dchr[SCT_CAPIT].d_lcms = bigcity_dchr.d_lcms;
-       dchr[SCT_CAPIT].d_hcms = bigcity_dchr.d_hcms;
-       dchr[SCT_CAPIT].d_name = bigcity_dchr.d_name;
-    }
-    if (opt_NO_LCMS)
-       dchr[SCT_LIGHT].d_cost = -1;
-    if (opt_NO_HCMS)
-       dchr[SCT_HEAVY].d_cost = -1;
-    if (opt_NO_OIL) {
-       dchr[SCT_OIL].d_cost = -1;
-       dchr[SCT_REFINE].d_cost = -1;
-    }
-    for (i = 0; i < pln_maxno; i++) {
-       if (opt_NO_HCMS)
-           plchr[i].pl_hcm = 0;
-       if (opt_NO_LCMS)
-           plchr[i].pl_lcm = 0;
-       if (opt_NO_OIL)
-           plchr[i].pl_fuel = 0;
-    }
-    for (i = 0; i < lnd_maxno; i++) {
-       if (opt_NO_HCMS)
-           lchr[i].l_hcm = 0;
-       if (opt_NO_LCMS)
-           lchr[i].l_lcm = 0;
-       /* Fix up the military values */
-       lchr[i].l_mil = lchr[i].l_item[I_MILIT];
-    }
-    for (i = 0; i < shp_maxno; i++) {
-       if (opt_NO_HCMS)
-           mchr[i].m_hcm = 0;
-       if (opt_NO_LCMS)
-           mchr[i].m_lcm = 0;
-       if (opt_NO_OIL) {
-           if (mchr[i].m_flags & M_OIL)
-               mchr[i].m_name = 0;
-       }
-    }
-    for (i = 0; i < nuk_maxno; i++) {
-       if (opt_NO_HCMS)
-           nchr[i].n_hcm = 0;
-       if (opt_NO_LCMS)
-           nchr[i].n_lcm = 0;
-    }
-    for (i = 0; i <= SCT_MAXDEF; i++) {
-       if (opt_NO_HCMS)
-           dchr[i].d_hcms = 0;
-       if (opt_NO_LCMS)
-           dchr[i].d_lcms = 0;
-    }
-    for (i = 0; i < prd_maxno; i++) {
-       for (j = 0; j < MAXPRCON; j++) {
-           if (opt_NO_HCMS && pchr[i].p_ctype[j] == I_HCM)
-               pchr[i].p_camt[j] = 0;
-           if (opt_NO_LCMS && pchr[i].p_ctype[j] == I_LCM)
-               pchr[i].p_camt[j] = 0;
-           if (opt_NO_OIL && pchr[i].p_ctype[j] == I_OIL)
-               pchr[i].p_camt[j] = 0;
-       }
+#if defined(_WIN32)
+    if (daemonize) {
+        stop_service();
+       return;
     }
+#endif
+    _exit(0);
 }
 
 #if defined(_WIN32)
 static void
-loc_NTInit()
+loc_NTInit(void)
 {
     int rc;
     WORD wVersionRequested;
@@ -541,11 +446,9 @@ loc_NTInit()
        _exit(1);
     }
 }
-#endif
 
-#if defined(_WIN32)
-void
-loc_NTTerm()
+static void
+loc_NTTerm(void)
 {
     WSACleanup();
 }