Browse 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 10 years ago
parent
commit
6549201405
2 changed files with 333 additions and 54 deletions
  1. 288
    27
      service.cpp
  2. 45
    27
      service.hpp

+ 288
- 27
service.cpp View File

// Place, Suite 330, Boston, MA 02111-1307 USA // Place, Suite 330, Boston, MA 02111-1307 USA
//============================================================================== //==============================================================================
#ifdef WIN32
#include <windows.h>
#include <tchar.h>
#include <strsafe.h>
#else
#include <unistd.h> #include <unistd.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
#include <signal.h> #include <signal.h>
#include <iostream>
#endif
#ifdef DEBUG_LOG_FILE
#include <fstream>
#endif
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <thread> #include <thread>
#include <chrono>
#include "service.hpp" #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. // Main program for *nix daemon.
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
} }
#endif
namespace CodeDweller { namespace CodeDweller {
std::mutex Service::objectMutex; std::mutex Service::objectMutex;
Service::Service() : Service::Service() :
pauseReceived(false), pauseReceived(false),
resumeReceived(false), resumeReceived(false),
restartReceived(false),
stopReceived(false) { stopReceived(false) {
} }
int Service::main(int argc, char *argv[]) { 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; pid_t pid;
messageThread.join(); messageThread.join();
return(status); 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[]) { void Service::loadArguments(int argc, char *argv[]) {
for (int i = 0; i < argc; i++) { for (int i = 0; i < argc; i++) {
cmdLineArgs.push_back(argv[i]); cmdLineArgs.push_back(argv[i]);
} }
} }
const std::vector<std::string> &Service::arguments() { const std::vector<std::string> &Service::arguments() {
resumeCallbacks.push_back(&resumeFunctor); 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) { void Service::onStopCall(Callback &stopFunctor) {
std::lock_guard<std::mutex> scopeMutex(objectMutex); std::lock_guard<std::mutex> scopeMutex(objectMutex);
stopCallbacks.push_back(&stopFunctor); stopCallbacks.push_back(&stopFunctor);
return (resumeReceived); return (resumeReceived);
} }
bool Service::receivedRestart() {
return (restartReceived);
}
bool Service::receivedStop() { bool Service::receivedStop() {
return (stopReceived); return (stopReceived);
} }
resumeReceived = false; resumeReceived = false;
} }
void Service::clearReceivedRestart() {
restartReceived = false;
}
void Service::clearReceivedStop() { void Service::clearReceivedStop() {
stopReceived = false; 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() { void Service::processMessages() {
int sigNum; int sigNum;
break; break;
case SIGHUP:
restartReceived = true;
for (auto callback : restartCallbacks) {
(*callback)();
}
break;
case SIGTERM: case SIGTERM:
stopReceived = true; stopReceived = true;
} }
#endif
} }

+ 45
- 27
service.hpp View File

#ifndef SERVICE_HPP #ifndef SERVICE_HPP
#define SERVICE_HPP #define SERVICE_HPP


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

#include <string> #include <string>
#include <vector> #include <vector>
#include <mutex> #include <mutex>
isn't temporarily stopped after receiving a Pause message, isn't temporarily stopped after receiving a Pause message,
the Resume message has no effect.</li> 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 <li>Stop. This is the posix TERM signal or Windows Stop
message. This instructs the service to shut down and message. This instructs the service to shut down and
exit.</li> exit.</li>
// //
int main(int argc, char *argv[]); 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. /// Register a callback for receipt of Pause.
// //
// \param[in] pauseFunctor is the function object to invoke when // \param[in] pauseFunctor is the function object to invoke when
// //
void onResumeCall(Callback &resumeFunctor); 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. /// Register a callback for receipt of Stop.
// //
// \param[in] stopFunctor is the function object to invoke when // \param[in] stopFunctor is the function object to invoke when
// //
bool receivedResume(); 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. /// Check whether the last message received was Stop.
// //
// \returns true if Stop was the most recent message received, // \returns true if Stop was the most recent message received,
/// Clear receiving the Resume message. /// Clear receiving the Resume message.
void clearReceivedResume(); void clearReceivedResume();


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

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


/// Prevent assignment. /// Prevent assignment.
void operator=(Service const&) {} void operator=(Service const&) {}


public:
/// Load the command-line arguments. /// Load the command-line arguments.
// //
// This method loads the object with the command-line parameters. // This method loads the object with the command-line parameters.
// //
int run(); int run();


private:
/// Mutex to serialize access to the object. /// Mutex to serialize access to the object.
static std::mutex objectMutex; 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(); void processMessages();
#endif


/// Command-line arguments. /// Command-line arguments.
std::vector<std::string> cmdLineArgs; std::vector<std::string> cmdLineArgs;
/// True if Resume message was received. /// True if Resume message was received.
bool resumeReceived; bool resumeReceived;


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

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


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


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

/// Functions to invoke when the Stop is received. /// Functions to invoke when the Stop is received.
std::vector<Callback *> stopCallbacks; 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. /// Set of signals to wait for.
sigset_t signalSet; sigset_t signalSet;
#endif


}; };



Loading…
Cancel
Save