diff --git a/include/prototypes.h b/include/prototypes.h index 3df917ca..3caf3ba6 100644 --- a/include/prototypes.h +++ b/include/prototypes.h @@ -48,8 +48,13 @@ #include "commodity.h" /* src/server/main.c */ +extern void close_files(void); extern void panic(int sig); extern void shutdwn(int sig); +extern void start_server(int, char *); +#if defined(_WIN32) +extern void loc_NTTerm(void); +#endif /***************************************************************************** * src/lib/ * / *.c @@ -270,6 +275,10 @@ extern int command(void); /* recvclient.c */ extern int recvclient(s_char *, int); +/* service.c */ +extern int service_stopped(void); +/* more in service.h */ + /* * src/lib/subs/ *.c */ diff --git a/include/service.h b/include/service.h new file mode 100644 index 00000000..b31d9860 --- /dev/null +++ b/include/service.h @@ -0,0 +1,40 @@ +/* + * Empire - A multi-player, client/server Internet based war game. + * Copyright (C) 1986-2004, 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 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. + * + * --- + * + * server.h: Windows services support + * + * Known contributors to this file: + * Ron Koenderink, 2004 + */ + +#ifndef SERVICE_H +#define SERVICE_H + +extern int install_service(char *program_name); +extern int remove_service(void); +extern void WINAPI service_main(DWORD argc, LPTSTR *argv); +#endif diff --git a/src/lib/empthread/ntthread.c b/src/lib/empthread/ntthread.c index 0d3f66bb..5ef4ae4e 100644 --- a/src/lib/empthread/ntthread.c +++ b/src/lib/empthread/ntthread.c @@ -468,13 +468,19 @@ empth_exit(void) loc_debug("empth_exit"); if (pThread->bMainThread) { - char buf[20]; /* The main line. Wait forever. */ while (1) { - printf("\nEmpire Server>"); - fgets(buf, sizeof(buf), stdin); - if (!strnicmp(buf, "quit", 4)) - shutdwn(0); + if (!debug) { + if (service_stopped()) + shutdwn(0); + Sleep(3); + } else { + char buf[20]; + printf("\nEmpire Server>"); + fgets(buf, sizeof(buf), stdin); + if (!strnicmp(buf, "quit", 4)) + shutdwn(0); + } } } else { TlsSetValue(loc_GVAR.dwTLSIndex, NULL); diff --git a/src/lib/gen/Makefile b/src/lib/gen/Makefile index 98970838..b5b6b285 100644 --- a/src/lib/gen/Makefile +++ b/src/lib/gen/Makefile @@ -45,7 +45,7 @@ NTOBJS = chance.obj copy.obj disassoc.obj \ emp_config.obj getstarg.obj getstring.obj \ io.obj ioqueue.obj mapdist.obj minmax.obj \ numstr.obj onearg.obj parse.obj plur.obj queue.obj round.obj \ - scthash.obj strdup.obj getopt.obj + scthash.obj strdup.obj getopt.obj service.obj all: $(LIB) diff --git a/src/lib/gen/service.c b/src/lib/gen/service.c new file mode 100644 index 00000000..0bf8026f --- /dev/null +++ b/src/lib/gen/service.c @@ -0,0 +1,270 @@ +/* + * Empire - A multi-player, client/server Internet based war game. + * Copyright (C) 1986-2004, 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 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. + * + * --- + * + * service.c: Windows services support + * + * Known contributors to this file: + * Ron Koenderink, 2004 + */ + +#include +#include +#include +#include + +#include "prototypes.h" +#include "service.h" +#include "../gen/getopt.h" +#include "optlist.h" + +#define SERVICE_NAME "Empire Server" + +int +install_service(char *program_name) +{ + char strDir[1024]; + HANDLE schSCManager,schService; + LPCTSTR lpszBinaryPathName; + SERVICE_DESCRIPTION sdBuf; + + if (strrchr(program_name,'\\') == NULL) { + GetCurrentDirectory(1024,strDir); + strcat(strDir, "\\"); + strcat(strDir, program_name); + } else + strcpy(strDir, program_name); + + schSCManager = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS); + + if (schSCManager == NULL) { + logerror("install_service failed to open Service Control Manager"); + printf("Install service: failed to open Service Control Manager.\n"); + return EXIT_FAILURE; + } + + lpszBinaryPathName = strDir; + + schService = CreateService(schSCManager, + SERVICE_NAME, + SERVICE_NAME, /* service name to display */ + SERVICE_ALL_ACCESS, /* desired access */ + SERVICE_WIN32_OWN_PROCESS, /* service type */ + SERVICE_AUTO_START, /* start type */ + SERVICE_ERROR_NORMAL, /* error control type */ + lpszBinaryPathName, /* service's binary */ + NULL, /* no load ordering group */ + NULL, /* no tag identifier */ + NULL, /* database service dependency */ + NULL, /* LocalSystem account */ + NULL); /* no password */ + + if (schService == NULL) { + logerror("install_service failed to create service"); + printf("Install service: failed to create service.\n"); + return EXIT_FAILURE; + } + sdBuf.lpDescription = "Server for Empire game"; + + if(!ChangeServiceConfig2( + schService, /* handle to service */ + SERVICE_CONFIG_DESCRIPTION, /* change: description */ + &sdBuf)) { /* value: new description */ + logerror("install_service failed to set the description"); + printf("Install service: failed to set the description.\n"); + } + + logerror("install_service successfully created the service"); + printf("Service installed.\n"); + CloseServiceHandle(schService); + return EXIT_SUCCESS; +} + +int +remove_service(void) +{ + HANDLE schSCManager; + SC_HANDLE hService; + + schSCManager = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS); + + if (schSCManager == NULL) { + logerror("remove_service failed to open Service Control Manager"); + printf("remove service: failed to open Service Control Manager.\n"); + return EXIT_FAILURE; + } + + hService = OpenService(schSCManager,SERVICE_NAME,SERVICE_ALL_ACCESS); + + if (hService == NULL) { + logerror("remove_service failed to open service"); + printf("Remove service: failed to open service.\n"); + return EXIT_FAILURE; + } + + if (DeleteService(hService) == 0) { + logerror("remove_service failed to remove service"); + printf("Remove service: failed to remove service.\n"); + return EXIT_FAILURE; + } + + if (CloseServiceHandle(hService) == 0) { + logerror("remove_service failed to close service"); + printf("Remove service: failed to close service.\n"); + return EXIT_FAILURE; + } else { + logerror("remove_service successfully removed service"); + printf("Service removed.\n"); + return EXIT_SUCCESS; + } +} + +static SERVICE_STATUS service_status; +static SERVICE_STATUS_HANDLE service_status_handle; + +void WINAPI +service_ctrl_handler(DWORD Opcode) +{ + DWORD status; + + switch(Opcode) + { + case SERVICE_CONTROL_PAUSE: + service_status.dwCurrentState = SERVICE_PAUSED; + logerror("Pausing the service not supported"); + break; + + case SERVICE_CONTROL_CONTINUE: + logerror("Continuing the service not supported"); + service_status.dwCurrentState = SERVICE_RUNNING; + break; + + case SERVICE_CONTROL_STOP: + service_status.dwWin32ExitCode = 0; + service_status.dwCurrentState = SERVICE_STOPPED; + service_status.dwCheckPoint = 0; + service_status.dwWaitHint = 0; + + if (!SetServiceStatus (service_status_handle, + &service_status)) { + status = GetLastError(); + logerror("Error while stopping service SetServiceStatus" + " error %ld", status); + } + + logerror("Service stopped"); + return; + + case SERVICE_CONTROL_INTERROGATE: + /* Fall through to send current status. */ + break; + + default: + logerror("Unrecognized opcode %ld in ServiceCtrlHandler", + Opcode); + } + + /* Send current status. */ + if (!SetServiceStatus (service_status_handle, &service_status)) { + status = GetLastError(); + logerror("SetServiceStatus error %ld",status); + } + return; +} + +void WINAPI +service_main(DWORD argc, LPTSTR *argv) +{ + char *config_file = NULL; + int op; + s_char tbuf[256]; + DWORD status; + + while ((op = getopt(argc, argv, "D:e:")) != EOF) { + switch (op) { + case 'D': + datadir = optarg; + break; + case 'e': + config_file = optarg; + break; + } + } + + if (config_file == NULL) { + sprintf(tbuf, "%s/econfig", datadir); + config_file = tbuf; + } + + service_status.dwServiceType = SERVICE_WIN32; + service_status.dwCurrentState = SERVICE_START_PENDING; + service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP; + service_status.dwWin32ExitCode = 0; + service_status.dwServiceSpecificExitCode = 0; + service_status.dwCheckPoint = 0; + service_status.dwWaitHint = 0; + + service_status_handle = RegisterServiceCtrlHandler( + SERVICE_NAME, service_ctrl_handler); + + if (service_status_handle == (SERVICE_STATUS_HANDLE)0) { + logerror("RegisterServiceCtrlHandler failed %d\n", GetLastError()); + return; + } + + /* Initialization code goes here. */ + start_server(0, config_file); + + /* Initialization complete - report running status. */ + service_status.dwCurrentState = SERVICE_RUNNING; + service_status.dwCheckPoint = 0; + service_status.dwWaitHint = 0; + + if (!SetServiceStatus (service_status_handle, &service_status)) { + status = GetLastError(); + logerror("SetServiceStatus error %ld\n",status); + } + + empth_exit(); + +/* We should never get here. But, just in case... */ + close_files(); + + loc_NTTerm(); + + // This is where the service does its work. + logerror("Returning the Main Thread \n",0); + return; +} + +int +service_stopped(void) +{ + if (service_status.dwCurrentState == SERVICE_STOPPED) + return 1; + else + return 0; +} diff --git a/src/server/main.c b/src/server/main.c index f7e76f52..a3a3264f 100644 --- a/src/server/main.c +++ b/src/server/main.c @@ -49,6 +49,7 @@ #include #include #include "../lib/gen/getopt.h" +#include "service.h" #endif #include "misc.h" @@ -69,11 +70,9 @@ static void nullify_objects(void); static void init_files(void); -static void close_files(void); #if defined(_WIN32) static void loc_NTInit(void); -static void loc_NTTerm(void); #endif static int mainpid = 0; @@ -89,7 +88,9 @@ static void print_usage(char *program_name) { #if defined(_WIN32) - printf("Usage: %s -D datadir -e config_file -d\n", program_name); + 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"); @@ -102,25 +103,30 @@ int main(int argc, char **argv) { int flags = 0; +#if defined(_WIN32) + int install_service_set = 0; + int remove_service_set = 0; + int datadir_set = 0; +#endif int op; char *config_file = NULL; s_char tbuf[256]; -#ifdef POSIXSIGNALS - struct sigaction act; -#endif /* POSIXSIGNALS */ loginit("server"); mainpid = getpid(); #if defined(_WIN32) - while ((op = getopt(argc, argv, "D:de:h")) != EOF) { + while ((op = getopt(argc, argv, "D:de:irh")) != EOF) { #else while ((op = getopt(argc, argv, "D:de:psh")) != EOF) { #endif switch (op) { case 'D': datadir = optarg; +#if defined(_WIN32) + datadir_set++; +#endif break; case 'd': debug++; @@ -128,7 +134,14 @@ main(int argc, char **argv) case 'e': config_file = optarg; break; -#if !defined(_WIN32) +#if defined(_WIN32) + case 'i': + install_service_set++; + break; + case 'r': + remove_service_set++; + break; +#else case 'p': flags |= EMPTH_PRINT; break; @@ -139,10 +152,31 @@ main(int argc, char **argv) case 'h': default: print_usage(argv[0]); - return 0; + 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"); + 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"); + exit(EXIT_FAILURE); + } + if (install_service_set) + return install_service(argv[0]); + if (remove_service_set) + return remove_service(); +#endif /* _WIN32 */ + if (config_file == NULL) { sprintf(tbuf, "%s/econfig", datadir); config_file = tbuf; @@ -151,6 +185,46 @@ main(int argc, char **argv) logerror("------------------------------------------------------"); logerror("Empire server (pid %d) started", (int)getpid()); +#if defined(_WIN32) + if (debug == 0) { + SERVICE_TABLE_ENTRY DispatchTable[]={{"Empire Server", service_main},{NULL, NULL}}; + if (StartServiceCtrlDispatcher(DispatchTable)) + return 0; + else + if (GetLastError() != ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) { + logerror("Failed to dispatch service (%d)", GetLastError()); + printf("Failed to dispatch service (%d)\n", GetLastError()); + exit(EXIT_FAILURE); + } else /* start in the foreground */ + debug = 1; + } +#else + if (debug == 0 && flags == 0) { + disassoc(); + } +#endif + + start_server(flags, config_file); + + empth_exit(); + +/* We should never get here. But, just in case... */ + close_files(); + +#if defined(_WIN32) + loc_NTTerm(); +#endif + return EXIT_SUCCESS; +} + + +void +start_server(int flags, char *config_file) +{ +#ifdef POSIXSIGNALS + struct sigaction act; +#endif /* POSIXSIGNALS */ + #if defined(_WIN32) loc_NTInit(); #endif @@ -167,9 +241,6 @@ main(int argc, char **argv) act.sa_flags = SA_SIGINFO; #endif sigemptyset(&act.sa_mask); - if (debug == 0 && flags == 0) { - disassoc(); - } act.sa_handler = shutdwn; /* pthreads on Linux use SIGUSR1 (*shrug*) so only catch it if not on a Linux box running POSIX threads -- STM */ @@ -187,7 +258,6 @@ main(int argc, char **argv) sigaction(SIGPIPE, &act, NULL); #else if (debug == 0 && flags == 0) { - disassoc(); /* 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)) @@ -243,16 +313,6 @@ main(int argc, char **argv) } 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 - return 0; } static void @@ -275,7 +335,7 @@ init_files(void) ef_open(EF_LOST, O_RDWR, 0); } -static void +void close_files(void) { ef_close(EF_NATION); @@ -483,7 +543,7 @@ loc_NTInit() #endif #if defined(_WIN32) -static void +void loc_NTTerm() { WSACleanup();