Browse Source

Implementing and testing CodeDweller::Service for *nix.


git-svn-id: https://svn.microneil.com/svn/CodeDweller/branches/adeniz_1@35 d34b734f-a00e-4b39-a726-e4eeb87269ab
adeniz_1
adeniz 10 years ago
parent
commit
587f0e1df9
2 changed files with 428 additions and 0 deletions
  1. 230
    0
      service.cpp
  2. 198
    0
      service.hpp

+ 230
- 0
service.cpp View File

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

+ 198
- 0
service.hpp View File

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

Loading…
Cancel
Save