Parcourir la source

Implemented and tested on Windows (built with Visual Studio 2013 Express).

git-svn-id: https://svn.microneil.com/svn/CodeDweller/branches/adeniz_1@39 d34b734f-a00e-4b39-a726-e4eeb87269ab
adeniz_1
adeniz il y a 10 ans
Parent
révision
6549201405
2 fichiers modifiés avec 333 ajouts et 54 suppressions
  1. 288
    27
      service.cpp
  2. 45
    27
      service.hpp

+ 288
- 27
service.cpp Voir le fichier

@@ -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
}

+ 45
- 27
service.hpp Voir le fichier

@@ -29,6 +29,10 @@
#ifndef SERVICE_HPP
#define SERVICE_HPP

#ifdef WIN32
#include <windows.h>
#endif

#include <string>
#include <vector>
#include <mutex>
@@ -66,10 +70,6 @@ namespace CodeDweller {
isn't temporarily stopped after receiving a Pause message,
the Resume message has no effect.</li>

<li>Restart. This is the posix HUP signal or Windows
Restart message. This instructs the service to shut down
and restart.</li>

<li>Stop. This is the posix TERM signal or Windows Stop
message. This instructs the service to shut down and
exit.</li>
@@ -116,6 +116,19 @@ namespace CodeDweller {
//
int main(int argc, char *argv[]);

#ifdef WIN32

/// Service entry point for Windows.
// \param[in] argc is the number of arguments.
//
// \param[in] argv is an array of strings containing the
// command-line arguments. The end of the array is indicated by a
// null pointer.
//
void serviceMain(DWORD argc, LPTSTR *argv);

#endif

/// Register a callback for receipt of Pause.
//
// \param[in] pauseFunctor is the function object to invoke when
@@ -130,13 +143,6 @@ namespace CodeDweller {
//
void onResumeCall(Callback &resumeFunctor);

/// Register a callback for receipt of Restart.
//
// \param[in] restartFunctor is the function object to invoke when
// Restart is received.
//
void onRestartCall(Callback &restartFunctor);

/// Register a callback for receipt of Stop.
//
// \param[in] stopFunctor is the function object to invoke when
@@ -156,12 +162,6 @@ namespace CodeDweller {
//
bool receivedResume();

/// Check whether Restart was received.
//
// \returns if the Restart message was received, false otherwise.
//
bool receivedRestart();

/// Check whether the last message received was Stop.
//
// \returns true if Stop was the most recent message received,
@@ -175,9 +175,6 @@ namespace CodeDweller {
/// Clear receiving the Resume message.
void clearReceivedResume();

/// Clear receiving the Restart message.
void clearReceivedRestart();

/// Clear receiving the Stop message.
void clearReceivedStop();

@@ -200,6 +197,7 @@ namespace CodeDweller {
/// Prevent assignment.
void operator=(Service const&) {}

public:
/// Load the command-line arguments.
//
// This method loads the object with the command-line parameters.
@@ -218,11 +216,25 @@ namespace CodeDweller {
//
int run();

private:
/// Mutex to serialize access to the object.
static std::mutex objectMutex;

/// Thread start function to receive messages.
#ifdef WIN32

public:
/// Process messages.
//
// \param[in] message is the message to process.
//
void processMessages(DWORD message);

private:

#else
/// Thread start function to receive and process messages.
void processMessages();
#endif

/// Command-line arguments.
std::vector<std::string> cmdLineArgs;
@@ -233,9 +245,6 @@ namespace CodeDweller {
/// True if Resume message was received.
bool resumeReceived;

/// True if Restart message was received.
bool restartReceived;

/// True if Stop message was received.
bool stopReceived;

@@ -245,14 +254,23 @@ namespace CodeDweller {
/// Functions to invoke when the Resume is received.
std::vector<Callback *> resumeCallbacks;

/// Functions to invoke when the Restart is received.
std::vector<Callback *> restartCallbacks;

/// Functions to invoke when the Stop is received.
std::vector<Callback *> stopCallbacks;

#ifdef WIN32
/// Name of service.
std::string serviceName;

/// Status of the service.
SERVICE_STATUS serviceStatus;

/// Handle for accessing service status on the OS.
SERVICE_STATUS_HANDLE serviceStatusHandle = NULL;

#else
/// Set of signals to wait for.
sigset_t signalSet;
#endif

};


Chargement…
Annuler
Enregistrer