git-svn-id: https://svn.microneil.com/svn/CodeDweller/branches/adeniz_1@35 d34b734f-a00e-4b39-a726-e4eeb87269abadeniz_1
@@ -0,0 +1,230 @@ | |||
// \file service.cpp | |||
// | |||
// Copyright (C) 2014 MicroNeil Research Corporation. | |||
// | |||
// This program is part of the MicroNeil Research Open Library Project. For | |||
// more information go to http://www.microneil.com/OpenLibrary/index.html | |||
// | |||
// 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 | |||
//============================================================================== | |||
#include <unistd.h> | |||
#include <sys/types.h> | |||
#include <sys/stat.h> | |||
#include <fcntl.h> | |||
#include <signal.h> | |||
#include <iostream> | |||
#include <cstdio> | |||
#include <cstdlib> | |||
#include <thread> | |||
#include <chrono> | |||
#include "service.hpp" | |||
// Main program for *nix daemon. | |||
int main(int argc, char *argv[]) { | |||
CodeDweller::Service &service = CodeDweller::Service::getInstance(); | |||
return service.main(argc, argv); | |||
} | |||
namespace CodeDweller { | |||
Service::Service() : | |||
lastMessage(Message::None) { | |||
} | |||
int Service::main(int argc, char *argv[]) { | |||
// Load the arguments. | |||
loadArguments(argc, argv); | |||
pid_t pid; | |||
// Fork the process. | |||
pid = fork(); | |||
if (pid < 0) { | |||
perror("Error from first fork"); | |||
return(EXIT_FAILURE); | |||
} | |||
// Terminate the parent. | |||
if (pid > 0) { | |||
return(EXIT_SUCCESS); | |||
} | |||
// This is the child. Become the session leader. | |||
if (setsid() < 0) { | |||
perror("Error from setsid"); | |||
return(EXIT_FAILURE); | |||
} | |||
// Fork again. | |||
pid = fork(); | |||
if (pid < 0) { | |||
perror("Error from second fork"); | |||
return(EXIT_FAILURE); | |||
} | |||
// Terminate the parent. | |||
if (pid > 0) { | |||
return(EXIT_SUCCESS); | |||
} | |||
// Set default file permissions. This call always succeeds. | |||
umask(0); | |||
// Disassociate the process from the default directory of the | |||
// grandparent. | |||
if (chdir("/") != 0) { | |||
perror("Error from chdir"); | |||
return(EXIT_FAILURE); | |||
} | |||
// Initialize the set of signals to wait for. | |||
if (sigemptyset(&signalSet) != 0) { | |||
perror("Error from sigemptyset"); | |||
return(EXIT_FAILURE); | |||
} | |||
if ((sigaddset(&signalSet, SIGTSTP) != 0) || | |||
(sigaddset(&signalSet, SIGCONT) != 0) || | |||
(sigaddset(&signalSet, SIGHUP) != 0) || | |||
(sigaddset(&signalSet, SIGTERM) != 0)) { | |||
perror("Error from sigaddset"); | |||
return(EXIT_FAILURE); | |||
} | |||
// Block the signals. | |||
if (sigprocmask(SIG_BLOCK, &signalSet, NULL) != 0) { | |||
perror("Error from sigprocmask"); | |||
return(EXIT_FAILURE); | |||
} | |||
// Close all open file descriptors. | |||
for (int fd = 2; fd >= 0; fd--) { | |||
if (close(fd) != 0) { | |||
// There is no controlling terminal to send any message to. | |||
return(EXIT_FAILURE); | |||
} | |||
} | |||
// Connect standard I/O to the null device. | |||
int fd; | |||
// stdin. | |||
fd = open("/dev/null", O_RDONLY); | |||
if (fd < 0) { | |||
return(EXIT_FAILURE); | |||
} | |||
// stdout. | |||
fd = open("/dev/null", O_WRONLY); | |||
if (fd < 0) { | |||
return(EXIT_FAILURE); | |||
} | |||
// stderr. | |||
fd = open("/dev/null", O_WRONLY); | |||
if (fd < 0) { | |||
return(EXIT_FAILURE); | |||
} | |||
// Start the thread to process messages. | |||
std::thread messageThread(&Service::processMessages, this); | |||
// Run the service. | |||
int status; | |||
status = run(); | |||
messageThread.join(); | |||
return(status); | |||
} | |||
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() { | |||
return cmdLineArgs; | |||
} | |||
void Service::onStopCall(Callback *stopFunctor) { | |||
stopCallbacks.push_back(stopFunctor); | |||
} | |||
bool Service::receivedStop() { | |||
return (Message::Stop == lastMessage); | |||
} | |||
void Service::processMessages() { | |||
int sigNum; | |||
int status; | |||
while (true) { | |||
// Wait for a signal. | |||
status = sigwait(&signalSet, &sigNum); | |||
if (0 != status) { | |||
perror("Error from sigwait"); | |||
// TODO: Implement recovery. | |||
; | |||
} | |||
// Process the message. | |||
switch (sigNum) { | |||
case SIGTSTP: | |||
lastMessage = Message::Pause; | |||
break; | |||
case SIGCONT: | |||
lastMessage = Message::Resume; | |||
break; | |||
case SIGHUP: | |||
lastMessage = Message::Restart; | |||
break; | |||
case SIGTERM: | |||
lastMessage = Message::Stop; | |||
for (auto callback : stopCallbacks) { | |||
(*callback)(); | |||
} | |||
// Exit. | |||
return; | |||
break; | |||
default: | |||
; | |||
} // switch. | |||
} // while. | |||
} | |||
} |
@@ -0,0 +1,198 @@ | |||
// \file service.hpp | |||
// | |||
// Copyright (C) 2014 MicroNeil Research Corporation. | |||
// | |||
// This program is part of the MicroNeil Research Open Library Project. For | |||
// more information go to http://www.microneil.com/OpenLibrary/index.html | |||
// | |||
// 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 | |||
//============================================================================== | |||
/* | |||
\brief The service module provides a framework for implementing *nix | |||
daemons and Windows services. | |||
*/ | |||
#ifndef SERVICE_HPP | |||
#define SERVICE_HPP | |||
#include <string> | |||
#include <vector> | |||
namespace CodeDweller { | |||
/** Singleton class that implements a daemon (*nix) or service | |||
(Windows). | |||
This class implements a *nix daemon or a Windows service. | |||
To implement a daemon or service, implement the required methods | |||
of this class, and link with service.cpp. When compiled under | |||
*nix, the file service.cpp contains the definition of main for | |||
the *nix daemon. When compiled under Windows, the file | |||
service.cpp contains the entry points for the Windows service. | |||
Nomenclature: | |||
<ol> | |||
<li>Service is a *nix daemon or Windows service.</li> | |||
<li>Message is a posix signal or Windows message. This class | |||
supports the following messages: | |||
<ol> | |||
<li>Pause. This is the posix TSTP signal or Windows Pause | |||
message. This instructs the service to temporarily stop | |||
running.</li> | |||
<li>Resume. This is the posix CONT signal or Windows | |||
Resume message. This instructs the service to resume | |||
running after receiving a Pause message. If the service | |||
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> | |||
</ol> | |||
</li> | |||
<li>Callback. This is a function that is executed when a | |||
message is received.</li> | |||
</ol> | |||
*/ | |||
class Service | |||
{ | |||
public: | |||
/// Get the instance of the singleton. | |||
static Service& getInstance() | |||
{ | |||
static Service service; | |||
return service; | |||
} | |||
/// Callback functor interface. | |||
class Callback { | |||
public: | |||
/// Callback method. | |||
virtual void operator()() = 0; | |||
}; | |||
/// Main entry point. | |||
// | |||
// \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. | |||
// | |||
// \returns exit code of the service. | |||
// | |||
int main(int argc, char *argv[]); | |||
/// Register a callback for receipt of Stop. | |||
// | |||
// \param[in] stopFunctor is the function object to invoke when | |||
// Stop is received. | |||
// | |||
void onStopCall(Callback *stopFunctor); | |||
/// Check whether the last message received was Stop. | |||
// | |||
// \returns true if Stop was the most recent message received, | |||
// false otherwise. | |||
// | |||
bool receivedStop(); | |||
/// Get a reference to the command-line arguments. | |||
// | |||
// \returns a reference to the vector of command-line arguments of | |||
// the application. Index i corresponds to command-line argument | |||
// i. | |||
// | |||
const std::vector<std::string> &arguments(); | |||
private: | |||
/// Private constructor prevents instantiation. | |||
Service(); | |||
/// Prevent copying. | |||
Service(Service const&) {} | |||
/// Prevent assignment. | |||
void operator=(Service const&) {} | |||
/// Load the command-line arguments. | |||
// | |||
// This method loads the object with the command-line parameters. | |||
// | |||
// \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 loadArguments(int argc, char *argv[]); | |||
/// Initialize and run the application. | |||
// | |||
// \returns the exit status of the service. | |||
// | |||
int run(); | |||
/// Thread start function to receive messages. | |||
void processMessages(); | |||
/// Command-line arguments. | |||
std::vector<std::string> cmdLineArgs; | |||
/// Enumeration specifying the most recent message received. | |||
enum class Message { | |||
Pause, | |||
Resume, | |||
Restart, | |||
Stop, | |||
None | |||
}; | |||
/// Most recent message received. | |||
Message lastMessage; | |||
/// Functions to invoke when the Stop is received. | |||
std::vector<Callback *> stopCallbacks; | |||
/// Set of signals to wait for. | |||
sigset_t signalSet; | |||
}; | |||
} | |||
#endif // SERVICE_HPP |