diff options
Diffstat (limited to 'cpp/src/qpid/broker/Daemon.cpp')
-rw-r--r-- | cpp/src/qpid/broker/Daemon.cpp | 218 |
1 files changed, 127 insertions, 91 deletions
diff --git a/cpp/src/qpid/broker/Daemon.cpp b/cpp/src/qpid/broker/Daemon.cpp index 2b64e7761b..27ac72a1e2 100644 --- a/cpp/src/qpid/broker/Daemon.cpp +++ b/cpp/src/qpid/broker/Daemon.cpp @@ -18,126 +18,162 @@ #include "Daemon.h" #include "qpid/log/Statement.h" #include "qpid/QpidError.h" -#include <libdaemon/daemon.h> + +#include <boost/iostreams/stream.hpp> +#include <boost/iostreams/device/file_descriptor.hpp> + #include <errno.h> -#include <unistd.h> -#include <sys/stat.h> +#include <fcntl.h> #include <signal.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> namespace qpid { namespace broker { using namespace std; +typedef boost::iostreams::stream<boost::iostreams::file_descriptor> fdstream; -boost::function<std::string()> qpid::broker::Daemon::pidFileFn; - -std::string Daemon::defaultPidFile(const std::string& identifier) { - daemon_pid_file_ident=identifier.c_str(); - return daemon_pid_file_proc_default(); +namespace { +/** Throw an exception containing msg and strerror if throwIf is true. + * Name is supposed to be reminiscent of perror(). + */ +void terror(bool throwIf, const string& msg, int errNo=errno) { + if (throwIf) + throw Exception(msg + (errNo? ": "+strError(errNo) : string("."))); } -const char* Daemon::realPidFileFn() { - static std::string str = pidFileFn(); - return str.c_str(); -} -Daemon::Daemon(boost::function<std::string()> fn, int secs) : pid(-1), timeout(secs) -{ - pidFileFn = fn; - daemon_pid_file_proc = &realPidFileFn; -} +struct LockFile : public fdstream { -Daemon::~Daemon() { - if (isChild()) - daemon_pid_file_remove(); -} - -class Daemon::Retval { - public: - Retval(); - ~Retval(); - int send(int s); - int wait(int timeout); - private: - bool completed; -}; + LockFile(const std::string& path_, bool create) + : path(path_), fd(-1), created(create) + { + errno = 0; + int flags=create ? O_WRONLY|O_CREAT|O_NOFOLLOW : O_RDWR; + fd = ::open(path.c_str(), flags, 0644); + terror(fd < 0,"Cannot open "+path); + terror(::lockf(fd, F_TLOCK, 0) < 0, "Cannot lock "+path); + open(boost::iostreams::file_descriptor(fd)); + } -pid_t Daemon::fork(Function parent, Function child) { - retval.reset(new Retval()); - pid = daemon_fork(); - if (pid < 0) - throw Exception("Failed to fork daemon: "+strError(errno)); - else if (pid == 0) { - try { - child(*this); - } catch (const exception& e) { - QPID_LOG(debug, "Rethrowing: " << e.what()); - failed(); // Notify parent - throw; + ~LockFile() { + if (fd >= 0) { + ::lockf(fd, F_ULOCK, 0); + close(); } } - else - parent(*this); - return pid; -} -int Daemon::wait() { // parent - assert(retval); - errno = 0; // Clear errno. - int ret = retval->wait(timeout); // wait for child. - if (ret == -1) { - if (errno) - throw Exception("Error waiting for daemon startup:" - +strError(errno)); - else - throw Exception("Error waiting for daemon startup, check logs."); - } - return ret; -} + std::string path; + int fd; + bool created; +}; -void Daemon::notify(int value) { // child - assert(retval); - if (retval->send(value)) - throw Exception("Failed to notify parent: "+strError(errno)); -} +} // namespace -void Daemon::ready(int value) { // child - if (value==-1) - throw Exception("Invalid value in Dameon::notify"); - errno = 0; - if (daemon_pid_file_create() != 0) - throw Exception(string("Failed to create PID file ") + - daemon_pid_file_proc()+": "+strError(errno)); - notify(value); +Daemon::Daemon() { + pid = -1; + pipeFds[0] = pipeFds[1] = -1; } -void Daemon::failed() { notify(-1); } - -void Daemon::quit() { - if (daemon_pid_file_kill_wait(SIGINT, timeout)) - throw Exception("Failed to stop daemon: " + strError(errno)); +string Daemon::dir() { + return (getuid() == 0 ? "/var/run" : "/tmp"); } -void Daemon::kill() { - if (daemon_pid_file_kill_wait(SIGKILL, timeout) < 0) - throw Exception("Failed to stop daemon: " + strError(errno)); +string Daemon::pidFile(uint16_t port) { + ostringstream path; + path << dir() << "/qpidd." << port << ".pid"; + return path.str(); } -pid_t Daemon::check() { - return daemon_pid_file_is_running(); +void Daemon::fork() +{ + terror(pipe(pipeFds) < 0, "Can't create pipe"); + terror((pid = ::fork()) < 0, "Daemon fork failed"); + if (pid == 0) { // Child + try { + // File descriptors + terror(::close(pipeFds[0])<0, "Cannot close read pipe"); + terror(::close(0)<0, "Cannot close stdin"); + terror(::close(1)<0, "Cannot close stdout"); + terror(::close(2)<0, "Cannot close stderr"); + int fd=::open("/dev/null",O_RDWR); // stdin + terror(fd != 0, "Cannot re-open stdin"); + terror(::dup(fd)<0, "Cannot re-open stdout"); + terror(::dup(fd)<0, "Cannot re-open stderror"); + + // Misc + terror(setsid()<0, "Cannot set session ID"); + terror(chdir(dir().c_str()) < 0, "Cannot change directory to "+dir()); + umask(027); + + // Child behavior + child(); + } + catch (const exception& e) { + QPID_LOG(critical, "Daemon startup failed: " << e.what()); + fdstream pipe(pipeFds[1]); + assert(pipe.is_open()); + pipe << "0 " << e.what() << endl; + } + } + else { // Parent + close(pipeFds[1]); // Write side. + parent(); + } } -Daemon::Retval::Retval() : completed(false) { - daemon_retval_init(); +Daemon::~Daemon() { + if (!lockFile.empty()) + unlink(lockFile.c_str()); } -Daemon::Retval::~Retval() { - if (!completed) daemon_retval_done(); + +uint16_t Daemon::wait(int timeout) { // parent waits for child. + errno = 0; + struct timeval tv; + tv.tv_sec = timeout; + tv.tv_usec = 0; + + fd_set fds; + FD_ZERO(&fds); + FD_SET(pipeFds[0], &fds); + terror(1 != select(FD_SETSIZE, &fds, 0, 0, &tv), "No response from daemon process"); + + fdstream pipe(pipeFds[0]); + uint16_t value = 0; + pipe >> value >> skipws; + if (value == 0) { + string errmsg; + getline(pipe, errmsg); + throw Exception("Daemon startup failed"+ (errmsg.empty() ? string(".") : ": " + errmsg)); + } + return value; } -int Daemon::Retval::send(int s) { - return daemon_retval_send(s); + +void Daemon::ready(uint16_t port) { // child + lockFile = pidFile(port); + LockFile lf(lockFile, true); + lf << getpid() << endl; + if (lf.fail()) + throw Exception("Cannot write lock file "+lockFile); + fdstream pipe(pipeFds[1]); + pipe << port << endl;; } -int Daemon::Retval::wait(int timeout) { - return daemon_retval_wait(timeout); + +pid_t Daemon::getPid(uint16_t port) { + string name = pidFile(port); + LockFile lockFile(name, false); + pid_t pid; + lockFile >> pid; + if (lockFile.fail()) + throw Exception("Cannot read lock file "+name); + if (kill(pid, 0) < 0 && errno != EPERM) { + unlink(name.c_str()); + throw Exception("Removing stale lock file "+name); + } + return pid; } + }} // namespace qpid::broker |