empserver/src/server/main.c
Markus Armbruster d8fbfda100 (shutdwn): Take exclusive update lock for shutdown. This makes the
shutdown block until all aborted commands terminated and gave up their
lock.  Remove the cheesy one second wait for commands to finish.  This
also locks out further commands to execute during shutdown.  However,
it also makes it impossible to kill hung player threads here.  Rely on
player_kill_idle() for that.  That doesn't quite work right now,
because it leaves a stale shared lock behind, which blocks shutdown.
2007-01-21 12:04:16 +00:00

419 lines
9.3 KiB
C

/*
* Empire - A multi-player, client/server Internet based war game.
* Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
* Ken Stevens, Steve McClure
*
* This program 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
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* 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
*
* ---
*
* 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: Empire Server main, startup and shutdown
*
* Known contributors to this file:
* Dave Pare, 1994
* Steve McClure, 1996, 1998
* Doug Hay, 1998
* Ron Koenderink, 2004-2005
* Markus Armbruster, 2005-2006
*/
#include <config.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#if defined(_WIN32)
#define WIN32
#include <winsock2.h>
#undef NS_ALL
#include <process.h>
#include <direct.h>
#include "service.h"
#include "../lib/gen/getopt.h"
#else
#include <sys/types.h>
#include <unistd.h>
#endif
#include "empio.h"
#include "empthread.h"
#include "file.h"
#include "journal.h"
#include "land.h"
#include "misc.h"
#include "nat.h"
#include "nuke.h"
#include "optlist.h"
#include "plane.h"
#include "player.h"
#include "product.h"
#include "prototypes.h"
#include "sect.h"
#include "server.h"
#include "ship.h"
#include "version.h"
static void create_pidfile(char *, pid_t);
#if defined(_WIN32)
static void loc_NTInit(void);
static void loc_NTTerm(void);
#endif
static char pidfname[] = "server.pid";
/* Run as daemon? If yes, detach from controlling terminal etc. */
static int daemonize = 1;
static void
print_usage(char *program_name)
{
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
" -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
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;
#endif
char *config_file = NULL;
int op, sig;
#ifdef _WIN32
# define XOPTS "iI:rR:"
#else
# define XOPTS
#endif
while ((op = getopt(argc, argv, "de:hpsv" XOPTS)) != EOF) {
switch (op) {
case 'p':
flags |= EMPTH_PRINT;
/* fall through */
case 'd':
debug = 1;
daemonize = 0;
break;
case 'e':
config_file = optarg;
break;
#if defined(_WIN32)
case 'I':
service_name = optarg;
/* fall through */
case 'i':
install_service_set++;
break;
case 'R':
service_name = optarg;
/* fall through */
case 'r':
remove_service_set++;
break;
#endif /* _WIN32 */
case 's':
flags |= EMPTH_STACKCHECK;
break;
case 'v':
printf("%s\n\n%s", version, legal);
return EXIT_SUCCESS;
case 'h':
print_usage(argv[0]);
return EXIT_SUCCESS;
default:
fprintf(stderr, "Try -h for help.\n");
return EXIT_FAILURE;
}
}
#if defined(_WIN32)
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) {
fprintf(stderr, "Can't use both -r or -R and -i or -I "
"options\n");
exit(EXIT_FAILURE);
}
if (remove_service_set)
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 (emp_config(config_file) < 0)
exit(EXIT_FAILURE);
ef_init();
if (chdir(configdir)) {
fprintf(stderr, "Can't chdir to %s (%s)\n",
configdir, strerror(errno));
exit(EXIT_FAILURE);
}
if (chdir(builtindir)) {
fprintf(stderr, "Can't chdir to %s (%s)\n",
builtindir, strerror(errno));
exit(EXIT_FAILURE);
}
if (read_builtin_tables() < 0)
exit(EXIT_FAILURE);
if (chdir(configdir)) {
fprintf(stderr, "Can't chdir to %s (%s)\n",
configdir, strerror(errno));
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);
}
#if defined(_WIN32)
if (install_service_set)
return install_service(program_name, service_name, config_file);
#endif /* _WIN32 */
init_server();
#if defined(_WIN32)
if (daemonize != 0) {
SERVICE_TABLE_ENTRY DispatchTable[]={
{"Empire Server", service_main},
{NULL, NULL}
};
if (StartServiceCtrlDispatcher(DispatchTable))
return 0;
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 (%lu)",
GetLastError());
finish_server();
exit(EXIT_FAILURE);
}
}
}
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) {
journal_close();
journal_open();
continue;
}
#endif
break;
}
shutdwn(sig);
CANT_REACH();
finish_server();
return EXIT_SUCCESS;
}
/*
* Initialize for serving, acquire resources.
*/
void
init_server(void)
{
srandom(time(NULL));
#if defined(_WIN32)
loc_NTInit();
#endif
update_policy_check();
player_init();
ef_init_srv();
io_init();
init_nreport();
if (opt_MOB_ACCESS) {
/* This fixes up mobility upon restart */
mobility_init();
}
loginit("server");
}
/*
* Start serving.
*/
void
start_server(int flags)
{
pid_t pid;
pid = getpid();
create_pidfile(pidfname, pid);
logerror("------------------------------------------------------");
logerror("Empire server (pid %d) started", (int)pid);
empth_init((void **)&player, flags);
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_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);
}
market_init();
update_init();
}
/*
* Finish serving, release resources.
*/
void
finish_server(void)
{
ef_fin_srv();
#if defined(_WIN32)
loc_NTTerm();
#endif
remove(pidfname);
}
static void
create_pidfile(char *fname, pid_t pid)
{
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);
}
}
void
shutdwn(int sig)
{
struct player *p;
logerror("Shutdown commencing (cleaning up threads.)");
for (p = player_next(0); p != 0; p = player_next(p)) {
if (p->state != PS_PLAYING)
continue;
pr_flash(p, "Server shutting down...\n");
p->state = PS_SHUTDOWN;
p->aborted++;
if (p->command) {
pr_flash(p, "Shutdown aborting command\n");
}
empth_wakeup(p->proc);
}
empth_rwlock_wrlock(update_lock);
/* rely on player_kill_idle() for killing hung player threads */
if (sig)
logerror("Server shutting down on signal %d", sig);
else
logerror("Server shutting down at deity's request");
finish_server();
#if defined(_WIN32)
if (daemonize)
stop_service();
#endif
exit(0);
}
#if defined(_WIN32)
static void
loc_NTInit(void)
{
int rc;
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 0);
rc = WSAStartup(wVersionRequested, &wsaData);
if (rc != 0) {
logerror("WSAStartup failed. %d", rc);
exit(1);
}
}
static void
loc_NTTerm(void)
{
WSACleanup();
}
#endif