|
|
@@ -20,20 +20,78 @@ |
|
|
|
// Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
//==============================================================================
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
|
|
|
|
#include <windows.h>
|
|
|
|
#include <tchar.h>
|
|
|
|
#include <strsafe.h>
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <signal.h>
|
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef DEBUG_LOG_FILE
|
|
|
|
#include <fstream>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <cstdio>
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <thread>
|
|
|
|
#include <chrono>
|
|
|
|
|
|
|
|
#include "service.hpp"
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
|
|
|
|
// Application main entry point for Windows.
|
|
|
|
int _tmain(int argc, TCHAR *argv[]) {
|
|
|
|
|
|
|
|
CodeDweller::Service &service = CodeDweller::Service::getInstance();
|
|
|
|
|
|
|
|
return service.main(argc, (char **) argv);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Service entry point for Windows.
|
|
|
|
VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv) {
|
|
|
|
|
|
|
|
CodeDweller::Service &service = CodeDweller::Service::getInstance();
|
|
|
|
|
|
|
|
return service.serviceMain(argc, argv);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Control message handler for Windows.
|
|
|
|
VOID WINAPI ServiceCtrlHandler(DWORD message) {
|
|
|
|
|
|
|
|
#ifdef DEBUG_LOG_FILE
|
|
|
|
std::ofstream logStream;
|
|
|
|
|
|
|
|
logStream.open(DEBUG_LOG_FILE, std::fstream::app);
|
|
|
|
logStream << "ServiceCtrlHandler. message: " << message
|
|
|
|
<< ". Calling processMessages" << std::endl;
|
|
|
|
logStream.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
CodeDweller::Service &service = CodeDweller::Service::getInstance();
|
|
|
|
|
|
|
|
service.processMessages(message);
|
|
|
|
|
|
|
|
#ifdef DEBUG_LOG_FILE
|
|
|
|
logStream.open(DEBUG_LOG_FILE, std::fstream::app);
|
|
|
|
logStream << "ServiceCtrlHandler. Done." << std::endl;
|
|
|
|
logStream.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
// Main program for *nix daemon.
|
|
|
|
int main(int argc, char *argv[]) {
|
|
|
|
|
|
|
@@ -43,6 +101,8 @@ int main(int argc, char *argv[]) { |
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
namespace CodeDweller {
|
|
|
|
|
|
|
|
std::mutex Service::objectMutex;
|
|
|
@@ -50,7 +110,6 @@ namespace CodeDweller { |
|
|
|
Service::Service() :
|
|
|
|
pauseReceived(false),
|
|
|
|
resumeReceived(false),
|
|
|
|
restartReceived(false),
|
|
|
|
stopReceived(false) {
|
|
|
|
}
|
|
|
|
|
|
|
@@ -62,8 +121,42 @@ namespace CodeDweller { |
|
|
|
|
|
|
|
int Service::main(int argc, char *argv[]) {
|
|
|
|
|
|
|
|
// Load the arguments.
|
|
|
|
loadArguments(argc, argv);
|
|
|
|
// Under Windows, only arg 0 is passed. Under *nix, all arguments
|
|
|
|
// are passed.
|
|
|
|
loadArguments(argc, (char **) argv);
|
|
|
|
|
|
|
|
#ifdef DEBUG_LOG_FILE
|
|
|
|
std::ofstream logStream;
|
|
|
|
|
|
|
|
logStream.open(DEBUG_LOG_FILE, std::fstream::app);
|
|
|
|
logStream << "main. argc: " << argc << std::endl;
|
|
|
|
for (int i = 0; i < argc; i++) {
|
|
|
|
logStream << "arg " << i << ": '" << argv[i] << "'" << std::endl;
|
|
|
|
}
|
|
|
|
logStream.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
|
|
|
|
serviceName = argv[0];
|
|
|
|
std::string::size_type indx = serviceName.find_last_of("/\\");
|
|
|
|
|
|
|
|
if (std::string::npos != indx) {
|
|
|
|
serviceName = serviceName.substr(indx + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
SERVICE_TABLE_ENTRY ServiceTable[] = {
|
|
|
|
{"", (LPSERVICE_MAIN_FUNCTION) ServiceMain},
|
|
|
|
{NULL, NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
if (StartServiceCtrlDispatcher(ServiceTable) == FALSE) {
|
|
|
|
return GetLastError();
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
pid_t pid;
|
|
|
|
|
|
|
@@ -168,14 +261,97 @@ namespace CodeDweller { |
|
|
|
messageThread.join();
|
|
|
|
|
|
|
|
return(status);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
|
|
|
|
void Service::serviceMain(DWORD argc, LPTSTR *argv) {
|
|
|
|
|
|
|
|
// Arg 0 contains the service name; skip it. The remaining
|
|
|
|
// arguments contain the command-line arguments, excluding the
|
|
|
|
// executable name; append them to the object's argument list.
|
|
|
|
loadArguments(argc - 1, (char **) argv + 1);
|
|
|
|
|
|
|
|
DWORD Status = E_FAIL;
|
|
|
|
|
|
|
|
// Register the service control handler with the SCM.
|
|
|
|
serviceStatusHandle =
|
|
|
|
RegisterServiceCtrlHandler("", ServiceCtrlHandler);
|
|
|
|
|
|
|
|
#ifdef DEBUG_LOG_FILE
|
|
|
|
std::ofstream logStream;
|
|
|
|
|
|
|
|
logStream.open(DEBUG_LOG_FILE, std::fstream::app);
|
|
|
|
logStream << "ServiceMain. argc: " << argc << std::endl;
|
|
|
|
for (DWORD i = 0; i < argc; i++) {
|
|
|
|
logStream << "arg " << i << ": '" << argv[i] << "'" << std::endl;
|
|
|
|
}
|
|
|
|
logStream << " serviceStatusHandle == NULL: " <<
|
|
|
|
(NULL == serviceStatusHandle) << std::endl;
|
|
|
|
logStream.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (serviceStatusHandle == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ZeroMemory(&serviceStatus, sizeof(serviceStatus));
|
|
|
|
serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
|
|
|
serviceStatus.dwControlsAccepted = 0;
|
|
|
|
serviceStatus.dwCurrentState = SERVICE_START_PENDING;
|
|
|
|
serviceStatus.dwWin32ExitCode = NO_ERROR;
|
|
|
|
serviceStatus.dwServiceSpecificExitCode = 0;
|
|
|
|
serviceStatus.dwCheckPoint = 0;
|
|
|
|
serviceStatus.dwWaitHint = 5000;
|
|
|
|
|
|
|
|
(void) SetServiceStatus(serviceStatusHandle, &serviceStatus);
|
|
|
|
|
|
|
|
// Tell the service controller that the service has started.
|
|
|
|
serviceStatus.dwControlsAccepted =
|
|
|
|
SERVICE_ACCEPT_STOP |
|
|
|
|
SERVICE_ACCEPT_PAUSE_CONTINUE;
|
|
|
|
serviceStatus.dwCurrentState = SERVICE_RUNNING;
|
|
|
|
serviceStatus.dwWin32ExitCode = 0;
|
|
|
|
serviceStatus.dwCheckPoint = 0;
|
|
|
|
(void) SetServiceStatus(serviceStatusHandle, &serviceStatus);
|
|
|
|
|
|
|
|
#ifdef DEBUG_LOG_FILE
|
|
|
|
logStream.open(DEBUG_LOG_FILE, std::fstream::app);
|
|
|
|
logStream << "Starting application thread." << std::endl;
|
|
|
|
logStream.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Start the application thread.
|
|
|
|
int status;
|
|
|
|
|
|
|
|
status = run();
|
|
|
|
|
|
|
|
// Change status to stopped.
|
|
|
|
serviceStatus.dwControlsAccepted = 0;
|
|
|
|
serviceStatus.dwCurrentState = SERVICE_STOPPED;
|
|
|
|
serviceStatus.dwWin32ExitCode = NO_ERROR;
|
|
|
|
serviceStatus.dwCheckPoint = 0;
|
|
|
|
(void) SetServiceStatus(serviceStatusHandle, &serviceStatus);
|
|
|
|
|
|
|
|
#ifdef DEBUG_LOG_FILE
|
|
|
|
logStream.open(DEBUG_LOG_FILE, std::fstream::app);
|
|
|
|
logStream << "Exiting." << std::endl;
|
|
|
|
logStream.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void Service::loadArguments(int argc, char *argv[]) {
|
|
|
|
|
|
|
|
for (int i = 0; i < argc; i++) {
|
|
|
|
cmdLineArgs.push_back(argv[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::vector<std::string> &Service::arguments() {
|
|
|
@@ -192,11 +368,6 @@ namespace CodeDweller { |
|
|
|
resumeCallbacks.push_back(&resumeFunctor);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Service::onRestartCall(Callback &restartFunctor) {
|
|
|
|
std::lock_guard<std::mutex> scopeMutex(objectMutex);
|
|
|
|
restartCallbacks.push_back(&restartFunctor);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Service::onStopCall(Callback &stopFunctor) {
|
|
|
|
std::lock_guard<std::mutex> scopeMutex(objectMutex);
|
|
|
|
stopCallbacks.push_back(&stopFunctor);
|
|
|
@@ -210,10 +381,6 @@ namespace CodeDweller { |
|
|
|
return (resumeReceived);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Service::receivedRestart() {
|
|
|
|
return (restartReceived);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Service::receivedStop() {
|
|
|
|
return (stopReceived);
|
|
|
|
}
|
|
|
@@ -226,14 +393,116 @@ namespace CodeDweller { |
|
|
|
resumeReceived = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Service::clearReceivedRestart() {
|
|
|
|
restartReceived = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Service::clearReceivedStop() {
|
|
|
|
stopReceived = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
|
|
|
|
void Service::processMessages(DWORD message) {
|
|
|
|
|
|
|
|
#ifdef DEBUG_LOG_FILE
|
|
|
|
std::ofstream logStream;
|
|
|
|
logStream.open(DEBUG_LOG_FILE, std::fstream::app);
|
|
|
|
logStream << "processMessages. message: " << message << std::endl;
|
|
|
|
logStream.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
switch (message) {
|
|
|
|
|
|
|
|
case SERVICE_CONTROL_PAUSE:
|
|
|
|
|
|
|
|
#ifdef DEBUG_LOG_FILE
|
|
|
|
logStream.open(DEBUG_LOG_FILE, std::fstream::app);
|
|
|
|
logStream << "processMessages. Received Pause" << std::endl;
|
|
|
|
logStream.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
serviceStatus.dwControlsAccepted = 0;
|
|
|
|
serviceStatus.dwCurrentState = SERVICE_PAUSE_PENDING;
|
|
|
|
serviceStatus.dwWin32ExitCode = NO_ERROR;
|
|
|
|
serviceStatus.dwCheckPoint = 1;
|
|
|
|
serviceStatus.dwWaitHint = 5000;
|
|
|
|
(void) SetServiceStatus(serviceStatusHandle, &serviceStatus);
|
|
|
|
|
|
|
|
pauseReceived = true;
|
|
|
|
|
|
|
|
for (auto callback : pauseCallbacks) {
|
|
|
|
(*callback)();
|
|
|
|
}
|
|
|
|
|
|
|
|
serviceStatus.dwControlsAccepted =
|
|
|
|
SERVICE_ACCEPT_STOP |
|
|
|
|
SERVICE_ACCEPT_PAUSE_CONTINUE;
|
|
|
|
serviceStatus.dwCurrentState = SERVICE_PAUSED;
|
|
|
|
serviceStatus.dwWin32ExitCode = NO_ERROR;
|
|
|
|
serviceStatus.dwCheckPoint = 0;
|
|
|
|
(void) SetServiceStatus(serviceStatusHandle, &serviceStatus);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SERVICE_CONTROL_CONTINUE:
|
|
|
|
|
|
|
|
#ifdef DEBUG_LOG_FILE
|
|
|
|
logStream.open(DEBUG_LOG_FILE, std::fstream::app);
|
|
|
|
logStream << "processMessages. Received Resume" << std::endl;
|
|
|
|
logStream.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
serviceStatus.dwControlsAccepted = 0;
|
|
|
|
serviceStatus.dwCurrentState = SERVICE_CONTINUE_PENDING;
|
|
|
|
serviceStatus.dwWin32ExitCode = NO_ERROR;
|
|
|
|
serviceStatus.dwCheckPoint = 1;
|
|
|
|
serviceStatus.dwWaitHint = 5000;
|
|
|
|
(void) SetServiceStatus(serviceStatusHandle, &serviceStatus);
|
|
|
|
|
|
|
|
resumeReceived = true;
|
|
|
|
|
|
|
|
for (auto callback : resumeCallbacks) {
|
|
|
|
(*callback)();
|
|
|
|
}
|
|
|
|
|
|
|
|
serviceStatus.dwControlsAccepted =
|
|
|
|
SERVICE_ACCEPT_STOP |
|
|
|
|
SERVICE_ACCEPT_PAUSE_CONTINUE;
|
|
|
|
serviceStatus.dwCurrentState = SERVICE_RUNNING;
|
|
|
|
serviceStatus.dwWin32ExitCode = NO_ERROR;
|
|
|
|
serviceStatus.dwCheckPoint = 0;
|
|
|
|
(void) SetServiceStatus(serviceStatusHandle, &serviceStatus);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SERVICE_CONTROL_STOP:
|
|
|
|
|
|
|
|
#ifdef DEBUG_LOG_FILE
|
|
|
|
logStream.open(DEBUG_LOG_FILE, std::fstream::app);
|
|
|
|
logStream << "processMessages. Received STOP" << std::endl;
|
|
|
|
logStream.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
serviceStatus.dwControlsAccepted = 0;
|
|
|
|
serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
|
|
|
serviceStatus.dwWin32ExitCode = NO_ERROR;
|
|
|
|
serviceStatus.dwCheckPoint = 1;
|
|
|
|
serviceStatus.dwWaitHint = 5000;
|
|
|
|
(void) SetServiceStatus(serviceStatusHandle, &serviceStatus);
|
|
|
|
|
|
|
|
stopReceived = true;
|
|
|
|
|
|
|
|
for (auto callback : stopCallbacks) {
|
|
|
|
(*callback)();
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
void Service::processMessages() {
|
|
|
|
|
|
|
|
int sigNum;
|
|
|
@@ -270,15 +539,6 @@ namespace CodeDweller { |
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SIGHUP:
|
|
|
|
restartReceived = true;
|
|
|
|
|
|
|
|
for (auto callback : restartCallbacks) {
|
|
|
|
(*callback)();
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SIGTERM:
|
|
|
|
stopReceived = true;
|
|
|
|
|
|
|
@@ -300,4 +560,5 @@ namespace CodeDweller { |
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
}
|