// \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 #include #include #include #include #include #include #include #include #include #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 { std::mutex Service::objectMutex; Service::Service() : pauseReceived(false), resumeReceived(false), restartReceived(false), stopReceived(false) { } Service &Service::getInstance() { std::lock_guard scopeMutex(objectMutex); static Service service; return service; } 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(); // Send a Stop message so that messageThread exits. pthread_kill(messageThread.native_handle(), SIGTERM); 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 &Service::arguments() { return cmdLineArgs; } void Service::onPauseCall(Callback &pauseFunctor) { std::lock_guard scopeMutex(objectMutex); pauseCallbacks.push_back(&pauseFunctor); } void Service::onResumeCall(Callback &resumeFunctor) { std::lock_guard scopeMutex(objectMutex); resumeCallbacks.push_back(&resumeFunctor); } void Service::onRestartCall(Callback &restartFunctor) { std::lock_guard scopeMutex(objectMutex); restartCallbacks.push_back(&restartFunctor); } void Service::onStopCall(Callback &stopFunctor) { std::lock_guard scopeMutex(objectMutex); stopCallbacks.push_back(&stopFunctor); } bool Service::receivedPause() { return (pauseReceived); } bool Service::receivedResume() { return (resumeReceived); } bool Service::receivedRestart() { return (restartReceived); } bool Service::receivedStop() { return (stopReceived); } void Service::clearReceivedPause() { pauseReceived = false; } void Service::clearReceivedResume() { resumeReceived = false; } void Service::clearReceivedRestart() { restartReceived = false; } void Service::clearReceivedStop() { stopReceived = false; } 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: pauseReceived = true; for (auto callback : pauseCallbacks) { (*callback)(); } break; case SIGCONT: resumeReceived = true; for (auto callback : resumeCallbacks) { (*callback)(); } break; case SIGHUP: restartReceived = true; for (auto callback : restartCallbacks) { (*callback)(); } break; case SIGTERM: stopReceived = true; for (auto callback : stopCallbacks) { (*callback)(); } // Exit. return; break; default: ; } // switch. } // while. } }