// code for running the server as a background process/daemon/service
struct servercontroller
{
virtual void start() = 0;
virtual void keepalive() = 0;
virtual void stop() = 0;
virtual ~servercontroller() {}
int argc;
char **argv;
};
#ifdef WIN32
struct winservice : servercontroller
{
SERVICE_STATUS_HANDLE statushandle;
SERVICE_STATUS status;
HANDLE stopevent;
const char *name;
winservice(const char *name) : name(name)
{
callbacks::svc = this;
statushandle = 0;
};
~winservice()
{
if(status.dwCurrentState != SERVICE_STOPPED) stop();
callbacks::svc = NULL;
}
void start() // starts the server again on a new thread and returns once the windows service has stopped
{
SERVICE_TABLE_ENTRY dispatchtable[] = { { (LPSTR)name, (LPSERVICE_MAIN_FUNCTION)callbacks::main }, { NULL, NULL } };
if(StartServiceCtrlDispatcher(dispatchtable)) exit(EXIT_SUCCESS);
else fatal("an error occurred running the ACR server as a Windows service. make sure you start the server from the service control manager and not from the command line.");
}
void keepalive()
{
if(statushandle)
{
report(SERVICE_RUNNING, 0);
handleevents();
}
};
void stop()
{
if(statushandle)
{
report(SERVICE_STOP_PENDING, 0);
if(stopevent) CloseHandle(stopevent);
status.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
report(SERVICE_STOPPED, 0);
}
}
void handleevents()
{
if(WaitForSingleObject(stopevent, 0) == WAIT_OBJECT_0) stop();
}
void WINAPI requesthandler(DWORD ctrl)
{
switch(ctrl)
{
case SERVICE_CONTROL_STOP:
report(SERVICE_STOP_PENDING, 0);
SetEvent(stopevent);
return;
default: break;
}
report(status.dwCurrentState, 0);
}
void report(DWORD state, DWORD wait)
{
status.dwCurrentState = state;
status.dwWaitHint = wait;
status.dwWin32ExitCode = NO_ERROR;
status.dwControlsAccepted = SERVICE_START_PENDING == state || SERVICE_STOP_PENDING == state ? 0 : SERVICE_ACCEPT_STOP;
if(state == SERVICE_RUNNING || state == SERVICE_STOPPED) status.dwCheckPoint = 0;
else status.dwCheckPoint++;
SetServiceStatus(statushandle, &status);
}
int WINAPI svcmain() // new server thread's entry point
{
// fix working directory to make relative paths work
if(argv && argv[0])
{
string procpath;
copystring(procpath, parentdir(argv[0]));
copystring(procpath, parentdir(procpath));
SetCurrentDirectory((LPSTR)procpath);
}
statushandle = RegisterServiceCtrlHandler(name, (LPHANDLER_FUNCTION)callbacks::requesthandler);
if(!statushandle) return EXIT_FAILURE;
status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
status.dwServiceSpecificExitCode = 0;
report(SERVICE_START_PENDING, 3000);
stopevent = CreateEvent(NULL, true, false, NULL);
if(!stopevent) { stop(); return EXIT_FAILURE; }
extern int main(int argc, char **argv);
return main(argc, argv);
}
struct callbacks
{
static winservice *svc;
static int WINAPI main() { return svc->svcmain(); };
static void WINAPI requesthandler(DWORD request) { svc->requesthandler(request); };
};
/*void log(const char *msg, bool error)
{
HANDLE eventsrc = RegisterEventSource(NULL, "ACR Server");
if(eventsrc)
{
int eventid = ((error ? 0x11 : 0x1) << 10) & (0x1 << 9) & (FACILITY_NULL << 6) & 0x1; // TODO: create event definitions
LPCTSTR msgs[1] = { msg };
int r = ReportEvent(eventsrc, error ? EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE, 0, 4, NULL, 1, 0, msgs, NULL);
DeregisterEventSource(eventsrc);
}
}*/
};
winservice *winservice::callbacks::svc = (winservice *)NULL;
#endif
153
pages