diff options
author | Alan Conway <aconway@apache.org> | 2007-07-19 02:19:17 +0000 |
---|---|---|
committer | Alan Conway <aconway@apache.org> | 2007-07-19 02:19:17 +0000 |
commit | ac530f5770150abe56acf270dfcea6287afb189d (patch) | |
tree | 11bef494467fab36b05d9f0420198a78a18ada79 /cpp | |
parent | a51da643a383c5c55fcd33861e379607037983f2 (diff) | |
download | qpid-python-ac530f5770150abe56acf270dfcea6287afb189d.tar.gz |
* src/qpid/broker/Daemon.cpp, .h
- Rewrote to remove libdaemon dependency.
- PID file stored in /var/run if root, /tmp otherwise.
* src/qpidd.cpp: Use new Daemon.cpp.
* lib/broker/Makefile.am (libqpidbroker_la_LIBADD):
- Daemon.cpp now needs -lboost_iostreams
* NOTICE, README, qpidc.spec.in: Removed mention of libdaemon.
* configure.ac:
- removed libdaemon
- cluster off by default - no ais dependencies.
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid@557455 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'cpp')
-rw-r--r-- | cpp/NOTICE | 8 | ||||
-rw-r--r-- | cpp/README | 3 | ||||
-rw-r--r-- | cpp/configure.ac | 47 | ||||
-rw-r--r-- | cpp/qpidc.spec.in | 1 | ||||
-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 |
8 files changed, 222 insertions, 236 deletions
diff --git a/cpp/NOTICE b/cpp/NOTICE index c316e13250..bf10fea3f7 100644 --- a/cpp/NOTICE +++ b/cpp/NOTICE @@ -23,13 +23,5 @@ Project dependancies: can be downloaded from http://www.boost.org - Included in most OS platfroms by defualt. -Optional modules: - - * libdaemon version 0.10 under LGPL and can be downloaded from - http://www.stud.uni-hamburg.de/users/lennart/projects/libdaemon - -Optional module detected by configure scripts if present to create - daemon process. - - diff --git a/cpp/README b/cpp/README index ed740cdcc8..f0d1358345 100644 --- a/cpp/README +++ b/cpp/README @@ -38,7 +38,6 @@ Qpid is compiled against libraries: * apr <http://apr.apache.org> (1.2.7) * boost <http://www.boost.org> (1.33.1) * cppunit <http://cppunit.sourceforge.net> (1.11.4) - * libdaemon <http://www.stud.uni-hamburg.de/users/lennart/projects/libdaemon> (0.10) * uuid <http://e2fsprogs.sourceforge.net/> (1.39) Using tools: @@ -74,7 +73,7 @@ If building from a source distribution you do not need: On linux most packages can be installed using your distribution's package management tool. For example on Fedora: - # yum install apr-devel boost-devel cppunit-devel libdaemon-devel e2fsprogs-devel + # yum install apr-devel boost-devel cppunit-devel e2fsprogs-devel # yum install pkgconfig doxygen graphviz help2man Follow the manual installation instruction below for any packages not diff --git a/cpp/configure.ac b/cpp/configure.ac index 63ec993a56..07366a4a59 100644 --- a/cpp/configure.ac +++ b/cpp/configure.ac @@ -156,30 +156,31 @@ DOWNLOAD_URL=http://rhm.et.redhat.com/download AC_SUBST(DOWNLOAD_URL) # Check for headers from required devel kits. -missing="" -AC_CHECK_HEADERS([libdaemon/daemon.h],,[missing="$missing libdaemon"]) -AC_CHECK_HEADERS([boost/shared_ptr.hpp],,[missing="$missing boost"]) -AC_CHECK_HEADERS([uuid/uuid.h],,[missing="$missing libuuid"]) -test -z "$missing" || - AC_MSG_ERROR([Missing required headers. Install the following packages or -devel rpms: $missing.]) - -# Enable/disable cluster functionality based on presence of usable openais -# and devel libs. -# cpg_local_get is not yet in a packaged release as of 2007-06-20 -LDFLAGS_save=$LDFLAGS -LDFLAGS="$LDFLAGS -L/usr/lib/openais -L/usr/lib64/openais" -AC_CHECK_LIB([cpg], [cpg_local_get], [libcpg=yes], [libcpg=no]) -AC_CHECK_HEADER([openais/cpg.h],[cpg_h=yes],[cpg_h=no]) -if test x$libcpg = xyes -a x$cpg_h = xyes; then - AM_CONDITIONAL([CLUSTER], true) - CPPFLAGS+=" -DCLUSTER" -else - AM_CONDITIONAL([CLUSTER], false) - LDFLAGS=$LDFLAGS_save -fi -if test x$libcpg = xno -a x$cpg_h = xyes; then - AC_MSG_WARN([Found cpg.h but libcpg is missing or does not contain cpg_local_get. Need build of openais whitetank branch head as of 2007-06-20]) +AC_CHECK_HEADERS([boost/shared_ptr.hpp uuid/uuid.h],, + AC_MSG_ERROR([Missing required header files.])) + +# Enable cluster functionality. +AC_ARG_ENABLE([cluster], + [AS_HELP_STRING([--enable-cluster], + [Enable cluster functionality, requires openais (default no)])], + [case $enableval in + yes|no) enable_CLUSTER=$enableval;; + *) AC_MSG_ERROR([Invalid value for --enable-apr-cluster: $enableval]);; + esac], + [enable_CLUSTER=no]) + +AM_CONDITIONAL([CLUSTER], [test x$enable_CLUSTER = xyes]) +if test x$enable_CLUSTER = xyes; then + CPPFLAGS+=" -DCLUSTER" + LDFLAGS="$LDFLAGS -L/usr/lib/openais -L/usr/lib64/openais" + # cpg_local_get is not yet in a packaged release as of 2007-06-20 + AC_CHECK_LIB([cpg],[cpg_local_get],, + AC_MSG_ERROR([cpg_local_get not available. openais missing/too old.])) + AC_CHECK_HEADERS([openais/cpg.h],, + AC_MSG_ERROR([Required header files not found.],[])) fi + + AC_CONFIG_FILES([ qpidc.spec Makefile diff --git a/cpp/qpidc.spec.in b/cpp/qpidc.spec.in index 8ec013f0d6..b20a6b185e 100644 --- a/cpp/qpidc.spec.in +++ b/cpp/qpidc.spec.in @@ -20,7 +20,6 @@ BuildRequires: doxygen BuildRequires: e2fsprogs-devel BuildRequires: graphviz BuildRequires: help2man -BuildRequires: libdaemon-devel BuildRequires: libtool BuildRequires: pkgconfig 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; } |