From: g0dil Date: Fri, 9 Nov 2007 13:46:24 +0000 (+0000) Subject: Scheduler: Daemon class: pidfile creation X-Git-Url: http://g0dil.de/git?a=commitdiff_plain;h=b22425f29e4e101079eb18b97ab70ba186ae4a87;p=senf.git Scheduler: Daemon class: pidfile creation Scheduler: Daemon class: Fix DaemonWatcher to not drop info from stdout/stderr when the child terminates Scheduler: Daemon class: Replace all ::exit() calls in the daemon watcher with ::_exit() git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@502 270642c3-0616-0410-b53a-bc976706d245 --- diff --git a/Scheduler/Daemon.cc b/Scheduler/Daemon.cc index 5252dcf..5c94256 100644 --- a/Scheduler/Daemon.cc +++ b/Scheduler/Daemon.cc @@ -34,6 +34,7 @@ #include #include #include +#include #include "../Utils/Exception.hh" #include "../Utils/membind.hh" @@ -48,7 +49,10 @@ // senf::Daemon prefix_ senf::Daemon::~Daemon() -{} +{ + if (! pidfile_.empty()) + LIBC_CALL( ::unlink, (pidfile_.c_str()) ); +} prefix_ void senf::Daemon::daemonize(bool v) { @@ -82,18 +86,19 @@ prefix_ void senf::Daemon::consoleLog(std::string path, StdStream which) } } -prefix_ void senf::Daemon::pidFile(std::string f, bool unique) +prefix_ void senf::Daemon::pidFile(std::string f) { pidfile_ = f; - unique_ = unique; } prefix_ void senf::Daemon::detach() { - LIBC_CALL_RV( nul, ::open, ("/dev/null", O_WRONLY) ); - LIBC_CALL( ::dup2, (nul, 1) ); - LIBC_CALL( ::dup2, (nul, 2) ); - LIBC_CALL( ::close, (nul) ); + if (daemonize_) { + LIBC_CALL_RV( nul, ::open, ("/dev/null", O_WRONLY) ); + LIBC_CALL( ::dup2, (nul, 1) ); + LIBC_CALL( ::dup2, (nul, 2) ); + LIBC_CALL( ::close, (nul) ); + } } prefix_ int senf::Daemon::start(int argc, char const ** argv) @@ -105,12 +110,17 @@ prefix_ int senf::Daemon::start(int argc, char const ** argv) try { # endif - configure(); - if (daemonize_) - fork(); - if (! pidfile_.empty()) - pidfileCreate(); - main(); + configure(); + + if (daemonize_) + fork(); + if (! pidfile_.empty() && ! pidfileCreate()) { + std::cerr << "\n*** PID file '" << pidfile_ << "' creation failed. Daemon running ?" + << std::endl; + return 1; + } + + main(); # ifdef NDEBUG } @@ -131,7 +141,7 @@ prefix_ int senf::Daemon::start(int argc, char const ** argv) // protected members prefix_ senf::Daemon::Daemon() - : argc_(0), argv_(0), daemonize_(true), stdout_(-1), stderr_(-1), pidfile_(""), unique_(true), + : argc_(0), argv_(0), daemonize_(true), stdout_(-1), stderr_(-1), pidfile_(""), detached_(false) {} @@ -189,46 +199,139 @@ prefix_ void senf::Daemon::fork() return; } + // Ouch ... ensure, the daemon watcher does not remove the pidfile ... + pidfile_ = ""; + LIBC_CALL( ::close, (coutpipe[1]) ); LIBC_CALL( ::close, (cerrpipe[1]) ); detail::DaemonWatcher watcher (pid, coutpipe[0], cerrpipe[0]); watcher.run(); - ::exit(0); - + ::_exit(0); } -prefix_ void senf::Daemon::pidfileCreate() -{} +prefix_ bool senf::Daemon::pidfileCreate() +{ + // Create temporary file pidfile_.hostname.pid and hard-link it to pidfile_ If the hardlink + // fails, the pidfile exists. If the link count of the temporary file is not 2 after this, there + // was some race condition, probably over NFS. + + std::string tempname; + + { + char hostname[HOST_NAME_MAX+1]; + LIBC_CALL( ::gethostname, (hostname, HOST_NAME_MAX+1) ); + hostname[HOST_NAME_MAX] = 0; + std::stringstream tempname_s; + tempname_s << pidfile_ << "." << hostname << "." << ::getpid(); + tempname = tempname_s.str(); + } + + while (1) { + { + std::ofstream pidf (tempname.c_str()); + pidf << ::getpid() << std::endl; + } + + if (::link(tempname.c_str(), pidfile_.c_str()) < 0) { + if (errno != EEXIST) + throwErrno("::link()"); + } + else { + struct ::stat s; + LIBC_CALL( ::stat, (tempname.c_str(), &s) ); + LIBC_CALL( ::unlink, (tempname.c_str()) ); + return s.st_nlink == 2; + } + + // pidfile exists. Check, whether the pid in the pidfile still exists. + { + int old_pid (-1); + std::ifstream pidf (pidfile_.c_str()); + if ( ! (pidf >> old_pid) + || old_pid < 0 + || ::kill(old_pid, 0) >= 0 + || errno == EPERM ) + return false; + } + + // If we reach this point, the pid file exists but the process mentioned within the + // pid file does *not* exists. We assume, the pid file to be stale. + + // I hope, the following procedure is without race condition: We remove our generated + // temporary pid file and recreate it as hard-link to the old pid file. Now we check, that + // the hard-link count of this file is 2. If it is not, we terminate, since someone else + // must have already created his hardlink. We then truncate the file and write our pid. + + LIBC_CALL( ::unlink, (tempname.c_str() )); + if (::link(pidfile_.c_str(), tempname.c_str()) < 0) { + if (errno != ENOENT) throwErrno("::link()"); + // Hmm ... the pidfile mysteriously disappeared ... try again. + continue; + } + + { + struct ::stat s; + LIBC_CALL( ::stat, (tempname.c_str(), &s) ); + if (s.st_nlink != 2) { + LIBC_CALL( ::unlink, (tempname.c_str()) ); + return false; + } + } + + { + std::ofstream pidf (tempname.c_str()); + pidf << ::getpid() << std::endl; + } + + LIBC_CALL( ::unlink, (tempname.c_str()) ); + break; + } + return true; +} /////////////////////////////////////////////////////////////////////////// // senf::detail::DaemonWatcher prefix_ senf::detail::DaemonWatcher::DaemonWatcher(int pid, int coutpipe, int cerrpipe) - : childPid_(pid), coutpipe_(coutpipe), cerrpipe_(cerrpipe), - coutForwarder_(coutpipe_, 1, senf::membind(&DaemonWatcher::pipeClosed, this)), - cerrForwarder_(cerrpipe_, 2, senf::membind(&DaemonWatcher::pipeClosed, this)) + : childPid_(pid), coutpipe_(coutpipe), cerrpipe_(cerrpipe), sigChld_(false), + coutForwarder_(coutpipe_, 1, boost::bind(&DaemonWatcher::pipeClosed, this, 1)), + cerrForwarder_(cerrpipe_, 2, boost::bind(&DaemonWatcher::pipeClosed, this, 2)) {} prefix_ void senf::detail::DaemonWatcher::run() { - Scheduler::instance().registerSignal(SIGCHLD, senf::membind(&DaemonWatcher::childDied, this)); + Scheduler::instance().registerSignal(SIGCHLD, senf::membind(&DaemonWatcher::sigChld, this)); Scheduler::instance().process(); } //////////////////////////////////////// // private members -prefix_ void senf::detail::DaemonWatcher::pipeClosed() +prefix_ void senf::detail::DaemonWatcher::pipeClosed(int id) { - if (! timerRunning_) { - Scheduler::instance().timeout(Scheduler::instance().eventTime() + ClockService::seconds(1), - senf::membind(&DaemonWatcher::childOk, this)); - timerRunning_ = true; + switch (id) { + case 1 : coutpipe_ = -1; break; + case 2 : cerrpipe_ = -1; break; + } + + if (coutpipe_ == -1 && cerrpipe_ == -1) { + if (sigChld_) + childDied(); // does not return + Scheduler::instance().timeout( + Scheduler::instance().eventTime() + ClockService::seconds(1), + senf::membind(&DaemonWatcher::childOk, this)); } } +prefix_ void senf::detail::DaemonWatcher::sigChld() +{ + sigChld_ = true; + if (coutpipe_ == -1 && cerrpipe_ == -1) + childDied(); // does not return +} + prefix_ void senf::detail::DaemonWatcher::childDied() { int status (0); @@ -237,11 +340,11 @@ prefix_ void senf::detail::DaemonWatcher::childDied() ::signal(WTERMSIG(status),SIG_DFL); ::kill(::getpid(), WTERMSIG(status)); // should not be reached - ::exit(1); + ::_exit(1); } if (WEXITSTATUS(status) == 0) - ::exit(1); - ::exit(WEXITSTATUS(status)); + ::_exit(1); + ::_exit(WEXITSTATUS(status)); } prefix_ void senf::detail::DaemonWatcher::childOk() diff --git a/Scheduler/Daemon.hh b/Scheduler/Daemon.hh index 205dcad..38aed48 100644 --- a/Scheduler/Daemon.hh +++ b/Scheduler/Daemon.hh @@ -106,7 +106,7 @@ namespace senf { When running in the foreground, the log files will be ignored. */ - void pidFile(std::string, bool unique = true); ///< Configure pid file + void pidFile(std::string); ///< Configure pid file ///\} ///\name Auxiliary helpers @@ -161,7 +161,7 @@ namespace senf { private: void fork(); - void pidfileCreate(); + bool pidfileCreate(); int argc_; char const ** argv_; @@ -170,7 +170,6 @@ namespace senf { int stdout_; int stderr_; std::string pidfile_; - bool unique_; bool detached_; }; diff --git a/Scheduler/Daemon.ih b/Scheduler/Daemon.ih index 23af6ce..6f49232 100644 --- a/Scheduler/Daemon.ih +++ b/Scheduler/Daemon.ih @@ -69,18 +69,18 @@ namespace detail { Callback cb_; }; - void pipeClosed(); + void pipeClosed(int id); + void sigChld(); void childDied(); void childOk(); int childPid_; int coutpipe_; int cerrpipe_; + bool sigChld_; Forwarder coutForwarder_; Forwarder cerrForwarder_; - - bool timerRunning_; }; }} diff --git a/Scheduler/Daemon.test.cc b/Scheduler/Daemon.test.cc index 418ea9f..9c6b22f 100644 --- a/Scheduler/Daemon.test.cc +++ b/Scheduler/Daemon.test.cc @@ -31,6 +31,7 @@ #include #include #include +#include #include "Daemon.hh" #include "../Utils/Exception.hh" @@ -52,11 +53,18 @@ namespace { class MyDaemon : public senf::Daemon { - void configure() { std::cout << "Running configure()" << std::endl; } - void init() { std::cout << "Running init()" << std::endl; } + void configure() { + std::cout << "Running configure()" << std::endl; + pidFile("testDaemon.pid"); + } + + void init() { + std::cout << "Running init()" << std::endl; + } + void run() { - delay(2000); std::cout << "Running run()" << std::endl; + delay(1500); } }; @@ -84,6 +92,10 @@ BOOST_AUTO_UNIT_TEST(testDaemon) { char const * args[] = { "run", 0 }; BOOST_CHECK_EQUAL( run(1,args), 0 ); + + BOOST_CHECK( boost::filesystem::exists("testDaemon.pid") ); + delay(1000); + BOOST_CHECK( ! boost::filesystem::exists("testDaemon.pid") ); } ///////////////////////////////cc.e////////////////////////////////////////