| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 | // \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
#ifdef WIN32
#include <windows.h>
#else
#include <chrono>
#endif
#include <string>
#include <vector>
#include <mutex>
#ifdef WIN32
int main(int argc, char *argv[]);
VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv);
VOID WINAPI ServiceCtrlHandler(DWORD message);
#else
int main(int argc, char *argv[]);
#endif
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>Stop.  This is the posix TERM signal or Windows Stop
	    or Shutdown 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:
    /// Callback functor interface.
    class Callback {
    public:
      /// Callback method.
      virtual void operator()() = 0;
    };
    /// Register a callback for receipt of Pause.
    //
    // \param[in] pauseFunctor is the function object to invoke when
    // Pause is received.
    //
    static void onPauseCall(Callback &pauseFunctor);
    /// Register a callback for receipt of Resume.
    //
    // \param[in] resumeFunctor is the function object to invoke when
    // Resume is received.
    //
    static void onResumeCall(Callback &resumeFunctor);
    /// Register a callback for receipt of Stop.
    //
    // \param[in] stopFunctor is the function object to invoke when
    // Stop is received.
    //
    static void onStopCall(Callback &stopFunctor);
    /// Check whether Pause was received.
    //
    // \returns if the Pause message was received, false otherwise.
    //
    static bool receivedPause();
    /// Check whether Resume was received.
    //
    // \returns if the Resume message was received, false otherwise.
    //
    static bool receivedResume();
    /// Check whether the last message received was Stop.
    //
    // \returns true if Stop was the most recent message received,
    // false otherwise.
    //
    static bool receivedStop();
    /// Clear receiving the Pause message.
    static void clearReceivedPause();
    /// Clear receiving the Resume message.
    static void clearReceivedResume();
    /// Clear receiving the Stop message.
    static void clearReceivedStop();
    /// 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.
    //
    static const std::vector<std::string> &arguments();
    /// Set the timeout for executing one Stop callback.
    //
    // If a Stop callback takes longer than the specified timeout, the
    // service aborts.
    //
    // \param[in] timeout_ms is the timeout in milliseconds.  If a
    // value less than 1 is specified, a value of 1 is used.
    //
    static void setStopCallbackTimeout_ms(int timeout_ms);
  private:
    /// Private constructor prevents instantiation.
    Service();
    /// Prevent copying.
    Service(Service const&) {}
    /// Prevent assignment.
    void operator=(Service const&) {}
    /// Get the instance of the singleton.
    //
    // \returns a reference to the singleton.
    //
    static Service& getInstance();
    /// 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[]);
#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
    /// 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();
    /// Mutex to serialize access to the object.
    static std::mutex objectMutex;
#ifdef WIN32
    /// Process a control message.
    //
    // \param[in] message is the message to process.
    //
    void processCtrlMessage(DWORD message);
#else
    /// Thread start function to receive and process messages.
    void processMessages();
#endif
    /// Thread start function for the watchdog thread.
    //
    // This thread sleeps until timeoutTime, and then causes the
    // process to exit.
    void watchdog();
    /// Command-line arguments.
    static std::vector<std::string> cmdLineArgs;
    /// True if Pause message was received.
    static bool pauseReceived;
    /// True if Resume message was received.
    static bool resumeReceived;
    /// True if Stop message was received.
    static bool stopReceived;
    /// Functions to invoke when the Pause is received.
    static std::vector<Callback *> pauseCallbacks;
    /// Functions to invoke when the Resume is received.
    static std::vector<Callback *> resumeCallbacks;
    /// Functions to invoke when the Stop is received.
    static std::vector<Callback *> stopCallbacks;
    /// Stop message callback timeout.
    static int stopCallbackTimeout_ms;
    /// Absolute time at which the Stop callback timeout expires.
    std::chrono::time_point<std::chrono::steady_clock> timeoutTime;
    /// True if the Stop callbacks are being invoked.
    bool stopCallbacksActive;
#ifdef WIN32
    /// Status of the service.
    SERVICE_STATUS serviceStatus;
    /// Handle for accessing service status on the OS.
    SERVICE_STATUS_HANDLE serviceStatusHandle = NULL;
    friend int ::main(int argc, char *argv[]);
    friend VOID WINAPI ::ServiceMain(DWORD argc, LPTSTR *argv);
    friend VOID WINAPI ::ServiceCtrlHandler(DWORD message);
#else
    /// Set of signals to wait for.
    sigset_t signalSet;
    friend int ::main(int argc, char *argv[]);
#endif
  };
}
#endif // SERVICE_HPP
 |