]> git.pond.sub.org Git - empserver/blobdiff - src/server/main.c
Fix PRNG seeding to resist guessing
[empserver] / src / server / main.c
index 44ab031eeb20426bd32c8a0bf377543aa7044e89..52c4c8cc3fb2eb72494b8cf155e96979aaf9683c 100644 (file)
@@ -1,11 +1,11 @@
 /*
  *  Empire - A multi-player, client/server Internet based war game.
- *  Copyright (C) 1986-2004, Dave Pare, Jeff Bailey, Thomas Ruschak,
- *                           Ken Stevens, Steve McClure
+ *  Copyright (C) 1986-2013, Dave Pare, Jeff Bailey, Thomas Ruschak,
+ *                Ken Stevens, Steve McClure, Markus Armbruster
  *
- *  This program is free software; you can redistribute it and/or modify
+ *  Empire is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
+ *  the Free Software Foundation, either version 3 of the License, or
  *  (at your option) any later version.
  *
  *  This program is distributed in the hope that it will be useful,
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  *  ---
  *
- *  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.
  *
  *  ---
  *
- *  main.c: Thread and signal initialization for Empire Server
- * 
+ *  main.c: Empire Server main, startup and shutdown
+ *
  *  Known contributors to this file:
  *     Dave Pare, 1994
  *     Steve McClure, 1996, 1998
  *     Doug Hay, 1998
+ *     Ron Koenderink, 2004-2009
+ *     Markus Armbruster, 2005-2012
  */
 
-#if defined(aix) || defined(linux)
-#include <unistd.h>
-#endif /* aix or linux */
+#include <config.h>
 
+#include <errno.h>
 #include <signal.h>
-#if !defined(_WIN32)
-#include <sys/ioctl.h>
-#endif
-#include <fcntl.h>
 #include <stdio.h>
-#include <string.h>
+#ifndef _WIN32
+#include <sys/wait.h>
+#endif
+#include <unistd.h>
 
 #if defined(_WIN32)
-#include <winsock2.h>
 #include <process.h>
-#include "../lib/gen/getopt.h"
 #include "service.h"
+#include "sys/socket.h"
 #endif
 
-#include "misc.h"
-#include "nat.h"
-#include "file.h"
-#include "player.h"
+#include "chance.h"
+#include "empio.h"
 #include "empthread.h"
+#include "file.h"
+#include "journal.h"
+#include "match.h"
+#include "misc.h"
+#include "optlist.h"
 #include "plane.h"
-#include "nuke.h"
-#include "land.h"
-#include "ship.h"
-#include "sect.h"
+#include "player.h"
 #include "product.h"
-#include "optlist.h"
-#include "global.h"
-#include "server.h"
 #include "prototypes.h"
+#include "sect.h"
+#include "server.h"
+#include "version.h"
 
-static void nullify_objects(void);
-static void init_files(void);
+static void ignore(void);
+static void crash_dump(void);
+static void init_server(unsigned, int);
+static void create_pidfile(char *, pid_t);
 
 #if defined(_WIN32)
 static void loc_NTInit(void);
 #endif
 
-static int mainpid = 0;
-
 /*
- * Debugging?
- * If yes, don't fork into background, don't catch certain signals,
- * call abort() on internal error.
+ * Lock to synchronize player threads with update.
+ * Update holds it exclusive, commands hold it shared.
  */
-int debug = 0;
+empth_rwlock_t *update_lock;
+/*
+ * Lock to synchronize player threads with shutdown.
+ * Shutdown holds it exclusive, player threads in state PS_PLAYING
+ * hold it shared.
+ */
+empth_rwlock_t *shutdown_lock;
+
+static char pidfname[] = "server.pid";
+
+/* Run as daemon?  If yes, detach from controlling terminal etc. */
+static int daemonize = 1;
+
+static void
+help(char *program_name, char *complaint)
+{
+    if (complaint)
+       fprintf(stderr, "%s: %s\n", program_name, complaint);
+    fprintf(stderr, "Try -h for help.\n");
+}
 
 static void
 print_usage(char *program_name)
 {
-#if defined(_WIN32)
-    printf("Usage: %s -i -I service_name -r -R service_name -D datadir -e config_file -d\n", program_name);
-    printf("-i install service with the default name %s\n", DEFAULT_SERVICE_NAME);
-    printf("-r remove service with the default name %s\n", DEFAULT_SERVICE_NAME);
-#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, implies -E abort\n"
+          "  -e CONFIG-FILE  configuration file\n"
+          "                  (default %s)\n"
+          "  -E ACTION       what to do on oops: abort, crash-dump, nothing (default)\n"
+#ifdef _WIN32
+          "  -i              install service `%s'\n"
+          "  -I NAME         install service NAME\n"
+#endif
+          "  -p              threading debug mode, implies -d\n"
+#ifdef _WIN32
+          "  -u              uninstall service `%s'\n"
+          "  -U NAME         uninstall service NAME\n"
+#endif
+          "  -s              enable stack checking\n"
+          "  -R RANDOM-SEED  random seed\n"
+          "  -h              display this help and exit\n"
+          "  -v              display version information and exit\n",
+          program_name, dflt_econfig
+#ifdef _WIN32
+          , DEFAULT_SERVICE_NAME, DEFAULT_SERVICE_NAME
 #endif
-    printf("-d debug mode\n");
+       );
 }
 
 int
 main(int argc, char **argv)
 {
+    static char *oops_key[] = { "abort", "crash-dump", "nothing", NULL };
+    static void (*oops_hndlr[])(void) = { abort, crash_dump, ignore };
     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;
-#else
-    char *config_file = NULL;
 #endif
-    int op;
-    s_char tbuf[256];
-
-#if defined(_WIN32)
-    _fmode = _O_BINARY;
-#endif
-
-    loginit("server");
+    char *config_file = NULL;
+    int force_bad_state = 0;
+    int op, idx, sig;
+    unsigned seed = 0;
+    int seed_set = 0;
 
-    mainpid = getpid();
+    oops_handler = ignore;
 
-#if defined(_WIN32)
-    while ((op = getopt(argc, argv, "D:de:iI:rR:h")) != EOF) {
+#ifdef _WIN32
+# define XOPTS "iI:uU:"
 #else
-    while ((op = getopt(argc, argv, "D:de:psh")) != EOF) {
+# define XOPTS
 #endif
+    while ((op = getopt(argc, argv, "de:E:FhpsR:v" 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++;
+           oops_handler = abort;
+           daemonize = 0;
            break;
        case 'e':
            config_file = optarg;
            break;
+       case 'E':
+           idx = stmtch(optarg, oops_key, 0, sizeof(*oops_key));
+           if (idx < 0) {
+               help(argv[0], "invalid argument for -E");
+               return EXIT_FAILURE;
+           }
+           oops_handler = oops_hndlr[idx];
+           break;
+       case 'F':
+           force_bad_state = 1;
+           break;
 #if defined(_WIN32)
        case 'I':
            service_name = optarg;
-           /*
-            * fall out
-            */
+           /* fall through */
        case 'i':
            install_service_set++;
            break;
-           break;
-       case 'R':
+       case 'U':
            service_name = optarg;
-           /*
-            * fall out
-            */
-       case 'r':
+           /* fall through */
+       case 'u':
            remove_service_set++;
            break;
-#else
-       case 'p':
-           flags |= EMPTH_PRINT;
-           break;
+#endif /* _WIN32 */
        case 's':
-           flags |= EMPTH_PRINT | EMPTH_STACKCHECK;
+           flags |= EMPTH_STACKCHECK;
            break;
-#endif
+       case 'R':
+           seed = strtoul(optarg, NULL, 10);
+           seed_set = 1;
+           break;
+       case 'v':
+           printf("%s\n\n%s", version, legal);
+           return EXIT_SUCCESS;
        case 'h':
-       default:
            print_usage(argv[0]);
+           return EXIT_SUCCESS;
+       default:
+           help(argv[0], NULL);
            return EXIT_FAILURE;
        }
     }
 
+    /* silently ignore operands for backward compatibility */
+
 #if defined(_WIN32)
-    if ((debug || datadir_set || config_file != NULL) &&
-       remove_service_set) {
-       logerror("Can't use -d, -D or -e with either "
-           "-r or -R options when starting the server");
-       printf("Can't use -d, -D or -e with either -r "
-           "or -R options\n");
+    if ((!daemonize || flags || config_file != NULL)
+       && remove_service_set) {
+       fprintf(stderr, "Can't use -p, -s, -d or -e with either "
+           "-u or -U options\n");
        exit(EXIT_FAILURE);
     }
-    if (debug && install_service_set) {
-       logerror("Can't use -d with either "
-           "-i or -I options when starting the server");
-       printf("Can't use -d with either -i "
-           "or -I options\n");
+    if ((!daemonize || 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 or -R and -i or -I options when starting "
-           "the server");
-       printf("Can't use both -r or -R and -i or -I options\n");
+       fprintf(stderr, "Can't use both -u or -U and -i or -I options\n");
        exit(EXIT_FAILURE);
     }
-    if (install_service_set)
-        return install_service(argv[0], service_name, datadir_set);
+
     if (remove_service_set)
-        return remove_service(service_name);
+       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;
+    empfile_init();
+    if (emp_config(config_file) < 0)
+       exit(EXIT_FAILURE);
+    empfile_fixup();
+    if (read_builtin_tables() < 0)
+       exit(EXIT_FAILURE);
+    if (read_custom_tables() < 0)
+       exit(EXIT_FAILURE);
+    if (chdir(gamedir)) {
+       fprintf(stderr, "Can't chdir to %s (%s)\n",
+               gamedir, 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 */
+
+    if (!seed_set)
+       seed = pick_seed();
+    init_server(seed, force_bad_state);
 
 #if defined(_WIN32)
-    if (debug == 0) {
-       SERVICE_TABLE_ENTRY DispatchTable[]={{"Empire Server", service_main},{NULL, NULL}};
+    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();
+    daemonize = 0;
+#else  /* !_WIN32 */
+    if (daemonize) {
+       if (disassoc() < 0) {
+           logerror("Can't become daemon (%s)", strerror(errno));
+           exit(1);
+       }
     }
+#endif /* !_WIN32 */
+    start_server(flags);
+
+    for (;;) {
+       sig = empth_wait_for_signal();
+#ifdef SIGHUP
+       if (sig == SIGHUP) {
+           /* if you make changes here, also update relo() */
+           journal_reopen();
+           update_reschedule();
+           logreopen();
+           continue;
+       }
 #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]);
+       break;
     }
-    sprintf(argv[0], "%s (main pid: %d)", tbuf, getpid());
-#endif
-
-    empth_exit();
-
-/* We should never get here.  But, just in case... */
-    close_files();
 
-#if defined(_WIN32)
-    loc_NTTerm();
-#endif
+    shutdwn(sig);
+    CANT_REACH();
+    finish_server();
     return EXIT_SUCCESS;
 }
 
+static void
+ignore(void)
+{
+}
 
-void
-start_server(int flags, char *config_file)
+static void
+crash_dump(void)
 {
-#ifdef POSIXSIGNALS
-    struct sigaction act;
-#endif /* POSIXSIGNALS */
+#ifdef _WIN32
+    logerror("Crash dump is not implemented");
+#else
+    pid_t pid;
+    int status;
 
-#if defined(_WIN32)
-    loc_NTInit();
+    fflush(NULL);
+    pid = fork();
+    if (pid < 0) {
+       logerror("Can't fork for crash dump (%s)", strerror(errno));
+       return;
+    }
+    if (pid == 0)
+       raise(SIGABRT);         /* child */
+
+    /* parent */
+    while (waitpid(pid, &status, 0) < 0) {
+       if (errno != EINTR) {
+           logerror("Can't get crash dumping child's status (%s)",
+                    strerror(errno));
+           return;
+       }
+    }
+    run_hook(post_crash_dump_hook, "post-crash-dump");
+    logerror("Crash dump complete");
 #endif
-    emp_config(config_file);
-    update_policy_check();
-
-    nullify_objects();
+}
 
-#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
-    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;
-    sigaction(SIGBUS, &act, NULL);
-    sigaction(SIGSEGV, &act, NULL);
-    sigaction(SIGILL, &act, NULL);
-    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);
+/*
+ * Initialize for serving, acquire resources.
+ */
+static void
+init_server(unsigned seed, int force_bad_state)
+{
+    seed_prng(seed);
+#if defined(_WIN32)
+    loc_NTInit();
 #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();
+    ef_init_srv(force_bad_state);
     io_init();
+    init_nreport();
 
-    if (opt_MOB_ACCESS) {
-       /* This fixes up mobility upon restart */
-       mobility_init();
-    }
-
-    empth_create(PP_ACCEPT, player_accept, (50 * 1024), flags,
-                "AcceptPlayers", "Accept network connections", 0);
-    empth_create(PP_KILLIDLE, player_kill_idle, (50 * 1024), flags,
-                "KillIdle", "Kills idle player connections", 0);
-    empth_create(PP_SCHED, update_sched, (50 * 1024), flags, "UpdateSched",
-                "Schedules updates to occur", 0);
-    empth_create(PP_TIMESTAMP, delete_lostitems, (50 * 1024), flags,
-                "DeleteItems", "Deletes old lost items", 0);
-    if (opt_MOB_ACCESS) {
-       /* Start the mobility access check thread */
-       empth_create(PP_TIMESTAMP, mobility_check, (50 * 1024), flags,
-                    "MobilityCheck", "Writes the timestamp file", 0);
-    }
-
-    if (opt_MARKET) {
-       empth_create(PP_TIMESTAMP, market_update, (50 * 1024), flags,
-                    "MarketUpdate", "Updates the market", 0);
-    }
+    if (journal_startup() < 0)
+       exit(1);
+    journal_prng(seed);
+    loginit("server");
 }
 
-static void
-init_files(void)
+/*
+ * Start serving.
+ */
+void
+start_server(int flags)
 {
-    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);
+    pid_t pid;
+
+    pid = getpid();
+    create_pidfile(pidfname, pid);
+    logerror("------------------------------------------------------");
+    logerror("Empire server (pid %d) started", (int)pid);
+
+    empth_init((void **)&player, flags);
+
+    update_lock = empth_rwlock_create("Update");
+    shutdown_lock = empth_rwlock_create("Shutdown");
+    if (!update_lock || !shutdown_lock)
+       exit_nomem();
+
+    market_init();
+    update_init();
+    empth_create(player_accept, 50 * 1024, flags, "AcceptPlayers", NULL);
 }
 
+/*
+ * Finish serving, release resources.
+ */
 void
-close_files(void)
+finish_server(void)
 {
-    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);
+    ef_fin_srv();
+    journal_shutdown();
+    remove(pidfname);
 }
 
-/* we're going down.  try to close the files at least */
-#if !defined(_WIN32)
-void
-panic(int sig)
+static void
+create_pidfile(char *fname, pid_t pid)
 {
-#ifdef POSIXSIGNALS
-    struct sigaction act;
-
-    act.sa_flags = 0;
-    sigemptyset(&act.sa_mask);
-    act.sa_handler = SIG_DFL;
-    sigaction(SIGBUS, &act, NULL);
-    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();
-    if (CANT_HAPPEN(sig != SIGBUS && sig != SIGSEGV
-                   && sig != SIGILL && sig != SIGFPE))
-       _exit(1);
-    if (raise(sig))
-       _exit(1);
+    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);
+    }
 }
-#endif /* _WIN32 */
 
 void
 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
+    time_t now = time(NULL);
+    int i;
 
     logerror("Shutdown commencing (cleaning up threads.)");
 
-    for (p = player_next(0); p != 0; p = player_next(p)) {
+    for (p = player_next(NULL); p; p = player_next(p)) {
        if (p->state != PS_PLAYING)
            continue;
        pr_flash(p, "Server shutting down...\n");
-       p->state = PS_SHUTDOWN;
-       p->aborted++;
+       io_set_eof(p->iop);
+       p->aborted = 1;
+       p->may_sleep = PLAYER_SLEEP_NEVER;
        if (p->command) {
            pr_flash(p, "Shutdown aborting command\n");
        }
        empth_wakeup(p->proc);
     }
 
-    if (!sig) {
-       /* Sleep and let some cleanup happen - note this doesn't work
-          when called from a signal handler, since we may or may not
-          be in the right thread.  So we just pass by and kill 'em
-          all. */
-       time(&now);
-       empth_sleep(now + 1);
-    }
+    empth_rwlock_wrlock(shutdown_lock);
+    empth_yield();
 
-    for (p = player_next(0); p != 0; p = player_next(p)) {
-       p->state = PS_KILL;
-       p->aborted++;
-       empth_terminate(p->proc);
-       p = player_delete(p);
+    for (i = 1; i <= 3 && player_next(NULL); i++) {
+       logerror("Waiting for player threads to terminate\n");
+       empth_sleep(now + i);
     }
+
+    for (p = player_next(NULL); p; p = player_next(p))
+       logerror("Player %d lingers, output might be lost", p->cnum);
+
     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();
+#endif
+    exit(0);
 }
 
 #if defined(_WIN32)
 static void
-loc_NTInit()
+loc_NTInit(void)
 {
     int rc;
-    WORD wVersionRequested;
-    WSADATA wsaData;
 
-    wVersionRequested = MAKEWORD(2, 0);
-    rc = WSAStartup(wVersionRequested, &wsaData);
+    rc = w32_socket_init();
     if (rc != 0) {
-       logerror("WSAStartup failed.  %d", rc);
-       _exit(1);
+       logerror("WSAStartup Failed, error code %d\n", rc);
+       exit(1);
     }
 }
 #endif
-
-#if defined(_WIN32)
-void
-loc_NTTerm()
-{
-    WSACleanup();
-}
-#endif