diff options
Diffstat (limited to 'cpp/src')
-rw-r--r-- | cpp/src/Makefile.am | 2 | ||||
-rw-r--r-- | cpp/src/qpid/broker/Daemon.cpp | 218 | ||||
-rw-r--r-- | cpp/src/qpid/broker/Daemon.h | 80 | ||||
-rw-r--r-- | cpp/src/qpidd.cpp | 99 |
4 files changed, 197 insertions, 202 deletions
diff --git a/cpp/src/Makefile.am b/cpp/src/Makefile.am index 3399d861f2..e215272bec 100644 --- a/cpp/src/Makefile.am +++ b/cpp/src/Makefile.am @@ -183,7 +183,7 @@ libqpidcommon_la_SOURCES = \ qpid/log/Statement.h \ qpid/memory.h -libqpidbroker_la_LIBADD = libqpidcommon.la -ldaemon -lboost_filesystem +libqpidbroker_la_LIBADD = libqpidcommon.la -lboost_iostreams libqpidbroker_la_SOURCES = \ qpid/broker/AccumulatedAck.cpp \ qpid/broker/Broker.cpp \ 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 diff --git a/cpp/src/qpid/broker/Daemon.h b/cpp/src/qpid/broker/Daemon.h index cf7ad37b3a..821334c11e 100644 --- a/cpp/src/qpid/broker/Daemon.h +++ b/cpp/src/qpid/broker/Daemon.h @@ -22,6 +22,7 @@ #include <string> #include <boost/scoped_ptr.hpp> #include <boost/function.hpp> +#include <boost/noncopyable.hpp> namespace qpid { namespace broker { @@ -30,72 +31,49 @@ namespace broker { * Tools for forking and managing a daemon process. * NB: Only one Daemon instance is allowed in a process. */ -class Daemon +class Daemon : private boost::noncopyable { public: - /** Utility function to create pid file name in a standard place - * (may require root acces) using identifier as the file name. - */ - static std::string defaultPidFile(const std::string& identifier); - - /** - * Daemon control object. - *@param pidFileFn Function that will comupte a PID file name. - * Called when pid file is created in ready() - *@param timeout in seconds for any operations that wait. - */ - Daemon(boost::function<std::string()> pidFileFn, int timeout); + /** Check daemon is running on port, throw exception if not */ + static pid_t getPid(uint16_t port); - ~Daemon(); + Daemon(); - typedef boost::function<void(Daemon&)> Function; + virtual ~Daemon(); - /** Fork the daemon. - *@param parent called in the parent process. - *@param child called in the child process. + /** + * Fork a daemon process. + * Call parent() in the parent process, child() in the child. */ - pid_t fork(Function parent, Function child); + void fork(); - /** Parent only: wait for child to indicate it is ready. - * @return value child passed to ready() */ - int wait(); + protected: - /** Child only. Notify the parent we are ready and write the - * PID file. - *@param value returned by parent call to wait(). -1 is reserved - * for signalling an error. - */ - void ready(int value); - - /** Child only, send failed signal so parent fork() will throw. */ - void failed(); - - /** Kill the daemon with SIGINT. */ - void quit(); - - /** Kill the daemon with SIGKILL. */ - void kill(); + /** Called in parent process */ + virtual void parent() = 0; - /** Check daemon is running, throw exception if not */ - pid_t check(); + /** Called in child process */ + virtual void child() = 0; - bool isParent() { return pid > 0; } + /** Call from parent(): wait for child to indicate it is ready. + * @timeout in seconds to wait for response. + * @return port passed by child to ready(). + */ + uint16_t wait(int timeout); - bool isChild() { return pid == 0; } + /** Call from child(): Notify the parent we are ready and write the + * PID file. + *@param port returned by parent call to wait(). + */ + void ready(uint16_t port); - pid_t getPid() const {return pid; } - private: - class Retval; + static std::string dir(); + static std::string pidFile(uint16_t port); - static boost::function<std::string()> pidFileFn; - static const char* realPidFileFn(); - void notify(int); - - static std::string identifier; - boost::scoped_ptr<Retval> retval; pid_t pid; - int timeout; + int pipeFds[2]; + std::string lockFile; }; }} // namespace qpid::broker diff --git a/cpp/src/qpidd.cpp b/cpp/src/qpidd.cpp index e17deeee2e..7ab730bf52 100644 --- a/cpp/src/qpidd.cpp +++ b/cpp/src/qpidd.cpp @@ -26,7 +26,6 @@ #include "qpid/log/Logger.h" #include "config.h" #include <boost/filesystem/path.hpp> -#include <boost/filesystem/operations.hpp> #include <iostream> #include <fstream> #include <signal.h> @@ -77,53 +76,31 @@ struct QpiddOptions : public qpid::Options { shared_ptr<Broker> brokerPtr; QpiddOptions options; -void handle_signal(int /*signal*/){ - QPID_LOG(notice, "Shutting down..."); +void shutdownHandler(int signal){ + QPID_LOG(notice, "Shutting down on signal " << signal); brokerPtr->shutdown(); } -/** Compute a name for the pid file */ -std::string pidFileFn() { - uint16_t port=brokerPtr ? brokerPtr->getPort() : options.broker.port; - string file=(boost::format("qpidd.%d.pid") % port).str(); - string pidPath; - if (getuid() == 0) // Use standard pid file for root. - pidPath=Daemon::defaultPidFile(file); - else { // Use $HOME/.qpidd for non-root. - const char* home=getenv("HOME"); - if (!home) - throw(Exception("$HOME is not set, cant create $HOME/.qpidd.")); - namespace fs=boost::filesystem; - fs::path dir = fs::path(home,fs::native) / fs::path(".qpidd", fs::native); - fs::create_directory(dir); - dir /= file; - pidPath=dir.string(); +struct QpiddDaemon : public Daemon { + /** Code for parent process */ + void parent() { + uint16_t port = wait(options.daemon.wait); + if (options.broker.port == 0) + cout << port << endl; } - QPID_LOG(debug, "PID file name=" << pidPath); - return pidPath; -} - -/** Code for forked parent */ -void parent(Daemon& demon) { - uint16_t realPort = demon.wait(); - if (options.broker.port == 0) - cout << realPort << endl; -} -/** Code for forked child */ -void child(Daemon& demon) { - brokerPtr.reset(new Broker(options.broker)); - uint16_t realPort=brokerPtr->getPort(); - demon.ready(realPort); // Notify parent. - brokerPtr->run(); -} + /** Code for forked child process */ + void child() { + brokerPtr.reset(new Broker(options.broker)); + uint16_t port=brokerPtr->getPort(); + ready(port); // Notify parent. + brokerPtr->run(); + } +}; int main(int argc, char* argv[]) { - // Spelled 'demon' to avoid clash with daemon.h function. - Daemon demon(pidFileFn, options.daemon.wait); - try { options.parse(argc, argv, options.common.config); qpid::log::Logger::instance().configure(options.log, argv[0]); @@ -138,27 +115,34 @@ int main(int argc, char* argv[]) return 0; } - // Stop running daemon - if (options.daemon.quit) { - demon.quit(); - return 0; - } - - // Query running daemon - if (options.daemon.check) { - pid_t pid = demon.check(); + // Options that affect a running daemon. + if (options.daemon.check || options.daemon.quit) { + pid_t pid = Daemon::getPid(options.broker.port); if (pid < 0) return 1; - else { + if (options.daemon.check) cout << pid << endl; - return 0; - } + if (options.daemon.quit && kill(pid, SIGINT) < 0) + throw Exception("Failed to stop daemon: " + strError(errno)); + return 0; } - // Starting the broker: - signal(SIGINT, handle_signal); - if (options.daemon.daemon) { // Daemon broker - demon.fork(parent, child); + // Starting the broker. + + // Signal handling + signal(SIGINT,shutdownHandler); + signal(SIGTERM,shutdownHandler); + signal(SIGHUP,SIG_IGN); // TODO aconway 2007-07-18: reload config. + + signal(SIGCHLD,SIG_IGN); + signal(SIGTSTP,SIG_IGN); + signal(SIGTTOU,SIG_IGN); + signal(SIGTTIN,SIG_IGN); + + if (options.daemon.daemon) { + // Fork the daemon + QpiddDaemon d; + d.fork(); } else { // Non-daemon broker. brokerPtr.reset(new Broker(options.broker)); @@ -169,10 +153,7 @@ int main(int argc, char* argv[]) return 0; } catch(const exception& e) { - if (demon.isParent()) - cerr << e.what() << endl; - else - QPID_LOG(critical, e.what()); + cerr << e.what() << endl; } return 1; } |