summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Conway <aconway@apache.org>2007-04-30 18:08:17 +0000
committerAlan Conway <aconway@apache.org>2007-04-30 18:08:17 +0000
commitb16914a34d89e4b99eceb0a3f5b277d454c4e39f (patch)
tree669e6c913361f3fb1ce27e5159534f1290238720
parent80b492223507dbb4a64a3e1c5e6e3f65ac53afa1 (diff)
downloadqpid-python-b16914a34d89e4b99eceb0a3f5b277d454c4e39f.tar.gz
* README: added new dependency, libdaemon.
* qpidc.spec.in: libdaemon dependencies. * broker/Daemon.h|cpp: Daemon and pid file management, wrapper for libdaemon. * qpidd.cpp modifid flags -d [ --daemon ] - waits till deamon is listening before returning. * qpidd.cpp: new flags -q [ --quit ] Stop the running daemon politely. -k [ --kill ] Kill the running daemon harshly. -c [ --check ] If daemon is running return 0. --wait SECONDS (=10) Maximum wait for daemon response. --ppid Print daemon pid to stdout * tests/dameon_test: Test daemon startup, shutdown. * deleted tests/broker: obsolete script. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/branches/M2@533819 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--cpp/README2
-rw-r--r--cpp/lib/broker/Daemon.cpp144
-rw-r--r--cpp/lib/broker/Daemon.h91
-rw-r--r--cpp/lib/broker/Makefile.am8
-rw-r--r--cpp/qpidc.spec.in8
-rw-r--r--cpp/src/qpidd.cpp106
-rw-r--r--cpp/tests/Makefile.am4
-rwxr-xr-xcpp/tests/broker45
-rwxr-xr-xcpp/tests/daemon_test22
9 files changed, 355 insertions, 75 deletions
diff --git a/cpp/README b/cpp/README
index e0a6aa2ecf..15af440c2c 100644
--- a/cpp/README
+++ b/cpp/README
@@ -33,7 +33,7 @@ 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)
Using tools:
* boost-jam <http://boost.sourceforge.net/> (3.1.13)
diff --git a/cpp/lib/broker/Daemon.cpp b/cpp/lib/broker/Daemon.cpp
new file mode 100644
index 0000000000..d54882a274
--- /dev/null
+++ b/cpp/lib/broker/Daemon.cpp
@@ -0,0 +1,144 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include "QpidError.h"
+#include "Daemon.h"
+#include <libdaemon/daemon.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <boost/filesystem/path.hpp>
+#include <boost/filesystem/operations.hpp>
+
+namespace qpid {
+namespace broker {
+
+using namespace std;
+
+string Daemon::pidFile;
+string Daemon::name;
+
+string Daemon::nameFromArgv0(const char* argv0) {
+ return string(daemon_ident_from_argv0(const_cast<char*>(argv0)));
+}
+
+const char* Daemon::getPidFile() {
+ if (pidFile.empty()) {
+ const char* home=getenv("HOME");
+ if (!home)
+ throw(Exception("$HOME is not set, cant create $HOME/.qpidd."));
+ using namespace boost::filesystem;
+ path dir = path(home,native) / path(".qpidd", native);
+ create_directory(dir);
+ dir /= name;
+ pidFile = dir.string();
+ }
+ return pidFile.c_str();
+}
+
+Daemon::Daemon(const string& name_, int secs) : timeout(secs)
+{
+ name = name_;
+ daemon_pid_file_ident = daemon_log_ident = name.c_str();
+ if (getuid() != 0) {
+ // For normal users put pid file under $HOME/.qpid
+ daemon_pid_file_proc = getPidFile;
+ }
+ // For root use the libdaemon default: /var/run.
+}
+
+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;
+};
+
+pid_t Daemon::fork() {
+ retval.reset(new Retval());
+ pid = daemon_fork();
+ if (pid < 0)
+ throw Exception("Failed to fork daemon: "+strError(errno));
+ else if (pid > 0) {
+ int ret = retval->wait(timeout); // parent, wait for child.
+ if (ret != 0) {
+ string err;
+ if (ret > 0)
+ err = strError(ret);
+ else if (ret == -1)
+ err= strError(errno);
+ else
+ err= "unknown error";
+ throw Exception("Deamon startup failed: "+err);
+ }
+ }
+ else if (pid == 0) { // child.
+ // TODO aconway 2007-04-26: Should log failures.
+ if (daemon_pid_file_create())
+ failed();
+ }
+ return pid;
+}
+
+void Daemon::notify(int i) {
+ assert(retval);
+ if (retval->send(i))
+ throw Exception("Failed to notify parent: "+strError(errno));
+}
+
+void Daemon::ready() { notify(0); }
+
+// NB: Not -1, confused with failure of fork() on the parent side.
+void Daemon::failed() { notify(errno? errno:-2); }
+
+void Daemon::quit() {
+ if (daemon_pid_file_kill_wait(SIGINT, timeout))
+ throw Exception("Failed to stop daemon: " + strError(errno));
+}
+
+void Daemon::kill() {
+ if (daemon_pid_file_kill_wait(SIGKILL, timeout) < 0)
+ throw Exception("Failed to stop daemon: " + strError(errno));
+}
+
+pid_t Daemon::check() {
+ return daemon_pid_file_is_running();
+}
+
+Daemon::Retval::Retval() : completed(false) {
+ daemon_retval_init();
+}
+Daemon::Retval::~Retval() {
+ if (!completed) daemon_retval_done();
+}
+int Daemon::Retval::send(int s) {
+ return daemon_retval_send(s);
+}
+int Daemon::Retval::wait(int timeout) {
+ return daemon_retval_wait(timeout);
+}
+
+}} // namespace qpid::broker
diff --git a/cpp/lib/broker/Daemon.h b/cpp/lib/broker/Daemon.h
new file mode 100644
index 0000000000..cf9bf13764
--- /dev/null
+++ b/cpp/lib/broker/Daemon.h
@@ -0,0 +1,91 @@
+#ifndef _broker_Daemon_h
+#define _broker_Daemon_h
+
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <string>
+#include <boost/scoped_ptr.hpp>
+
+namespace qpid {
+namespace broker {
+
+/**
+ * Tools for forking and managing a daemon process.
+ * NB: Only one Daemon instance is allowed in a process.
+ */
+class Daemon
+{
+ public:
+
+ /** Extract the daemon's name from argv[0] */
+ static std::string nameFromArgv0(const char* argv0);
+
+ /**
+ * Creating a Daemon instance forks a daemon process.
+ *@param name used to create pid files etc.
+ *@param timeout in seconds for all operations that wait.
+ */
+ Daemon(const std::string& name, int timeout);
+
+ ~Daemon();
+
+ /** Fork the daemon, wait till it signals readiness */
+ pid_t fork();
+
+ /** Child only, send ready signal so parent fork() will return. */
+ void ready();
+
+ /** 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();
+
+ /** Check daemon is running, throw exception if not */
+ pid_t check();
+
+ bool isParent() { return pid > 0; }
+
+ bool isChild() { return pid == 0; }
+
+ std::string getName() const { return name; }
+
+ pid_t getPid() const {return pid; }
+
+ private:
+ class Retval;
+
+ void notify(int);
+
+ static std::string name;
+ static std::string pidFile;
+ static const char* getPidFile();
+ boost::scoped_ptr<Retval> retval;
+ pid_t pid;
+ int timeout;
+};
+
+}} // namespace qpid::broker
+
+
+
+#endif /*!_broker_Daemon_h*/
diff --git a/cpp/lib/broker/Makefile.am b/cpp/lib/broker/Makefile.am
index dfc0315902..5da38adc33 100644
--- a/cpp/lib/broker/Makefile.am
+++ b/cpp/lib/broker/Makefile.am
@@ -7,7 +7,11 @@ INCLUDES = \
$(APR_CXXFLAGS)
lib_LTLIBRARIES = libqpidbroker.la
-libqpidbroker_la_LIBADD = ../common/libqpidcommon.la
+libqpidbroker_la_LIBADD = \
+ ../common/libqpidcommon.la \
+ -ldaemon \
+ -lboost_filesystem
+
libqpidbroker_la_LDFLAGS = -version-info $(LIBTOOL_VERSION_INFO_ARG)
libqpidbroker_la_SOURCES = \
AccumulatedAck.cpp \
@@ -27,6 +31,8 @@ libqpidbroker_la_SOURCES = \
ConnectionToken.h \
Consumer.h \
Content.h \
+ Daemon.cpp \
+ Daemon.h \
DeletingTxOp.cpp \
DeletingTxOp.h \
Deliverable.h \
diff --git a/cpp/qpidc.spec.in b/cpp/qpidc.spec.in
index 9b7cb4ef91..871e8b9c86 100644
--- a/cpp/qpidc.spec.in
+++ b/cpp/qpidc.spec.in
@@ -13,17 +13,19 @@ URL: http://rhm.et.redhat.com/qpidc/
Source0: http://rhm.et.redhat.com/download/%{name}-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
-BuildRequires: libtool
+BuildRequires: apr-devel
BuildRequires: boost-devel
BuildRequires: cppunit-devel
BuildRequires: doxygen
+BuildRequires: e2fsprogs-devel
BuildRequires: graphviz
BuildRequires: help2man
+BuildRequires: libdaemon-devel
+BuildRequires: libtool
BuildRequires: pkgconfig
-BuildRequires: e2fsprogs-devel
-BuildRequires: apr-devel
Requires: boost
+Requires: libdaemon
Requires(post):/sbin/chkconfig
Requires(preun):/sbin/chkconfig
diff --git a/cpp/src/qpidd.cpp b/cpp/src/qpidd.cpp
index ec8fe570d3..e199c72683 100644
--- a/cpp/src/qpidd.cpp
+++ b/cpp/src/qpidd.cpp
@@ -19,35 +19,58 @@
*
*/
#include <Broker.h>
-#include <signal.h>
#include <iostream>
#include <memory>
#include <config.h>
-#include <unistd.h>
#include <fstream>
+#include <signal.h>
+#include <Daemon.h>
+#include <boost/format.hpp>
+using boost::format;
using namespace qpid;
using namespace qpid::broker;
using namespace qpid::sys;
using namespace std;
+Broker::shared_ptr brokerPtr;
+
+void handle_signal(int /*signal*/){
+ std::cerr << "Shutting down..." << std::endl;
+ brokerPtr->shutdown();
+}
+
+
/** Command line options */
struct QpiddOptions : public Broker::Options
{
bool help;
bool version;
bool daemon;
+ bool quit;
+ bool kill;
+ bool check;
+ bool ppid;
+ int wait;
string config;
po::options_description desc;
QpiddOptions() :
help(false), version(false), daemon(false),
+ quit(false), kill(false), check(false), ppid(false), wait(10),
config("/etc/qpidd.conf"),
desc("Options")
{
using namespace po;
desc.add_options()
- ("daemon,d", optValue(daemon), "Run as a daemon");
+ ("daemon,d", optValue(daemon), "Run as a daemon.")
+ ("quit,q", optValue(quit), "Stop the running daemon politely.")
+ ("kill,k", optValue(kill), "Kill the running daemon harshly.")
+ ("check,c", optValue(check), "If daemon is running return 0.")
+ ("wait", optValue(wait, "SECONDS"),
+ "Maximum wait for daemon response.")
+ ("ppid", optValue(ppid), "Print daemon pid to stdout" );
+ po::options_description brokerOpts;
Broker::Options::addTo(desc);
desc.add_options()
("config", optValue(config, "FILE"), "Configuation file")
@@ -87,43 +110,80 @@ ostream& operator<<(ostream& out, const QpiddOptions& config) {
config.usage(out); return out;
}
-Broker::shared_ptr brokerPtr;
-
-void handle_signal(int /*signal*/){
- if (brokerPtr) {
- cerr << "Shutting down..." << endl;
- brokerPtr->shutdown();
- }
-}
-
int main(int argc, char* argv[])
{
QpiddOptions config;
try {
config.parse(argc, argv);
+ string name=(format("%s.%d")
+ % Daemon::nameFromArgv0(argv[0])
+ % (config.port)).str();
+ // Spelled 'demon' to avoid clash with daemon.h function.
+ Daemon demon(name, config.wait);
+
+ // Options that just print information.
if(config.help) {
config.usage(cout);
+ return 0;
}
- else if (config.version) {
+ if (config.version) {
cout << "qpidd (" << PACKAGE_NAME << ") version "
<< PACKAGE_VERSION << endl;
+ return 0;
}
- else {
- brokerPtr=Broker::create(config);
- signal(SIGINT, handle_signal);
- if (config.daemon) {
- if (daemon(0, 0) < 0) // daemon(nochdir, noclose)
- throw QPID_ERROR(
- INTERNAL_ERROR,
- "Failed to detach as daemon: "+ strError(errno));
+
+ // Options that affect an already running daemon.
+ if (config.quit || config.kill || config.check) {
+ pid_t pid = demon.check();
+ if (config.ppid && pid > 0)
+ cout << pid << endl;
+ if (config.kill)
+ demon.kill();
+ else if (config.quit)
+ demon.quit();
+ if (config.check && pid <= 0)
+ return 1;
+ return 0;
+ }
+
+ // Starting the broker:
+ signal(SIGINT, handle_signal);
+ if (config.daemon) {
+ pid_t pid = demon.fork();
+ if (pid == 0) { // Child
+ try {
+ brokerPtr=Broker::create(config);
+ demon.ready(); // Notify parent we're ready.
+ brokerPtr->run();
+ } catch (const exception& e) {
+ // TODO aconway 2007-04-26: Log this, cerr is lost.
+ cerr << "Broker daemon failed: " << e.what() << endl;
+ demon.failed(); // Notify parent we failed.
+ return 1;
+ }
}
- brokerPtr->run();
+ else if (pid > 0) { // Parent
+ if (config.ppid)
+ cout << pid << endl;
+ return 0;
+ }
+ else { // pid < 0
+ throw Exception("fork failed"+strError(errno));
+ }
+ } // Non-daemon broker.
+ else {
+ brokerPtr = Broker::create(config);
+ brokerPtr->run();
}
return 0;
}
- catch(const exception& e) {
+ catch(const po::error& e) {
+ // Command line parsing error.
cerr << "Error: " << e.what() << endl
<< "Type 'qpidd --help' for usage." << endl;
}
+ catch(const exception& e) {
+ cerr << "Error: " << e.what() << endl;
+ }
return 1;
}
diff --git a/cpp/tests/Makefile.am b/cpp/tests/Makefile.am
index 7f6de0d28e..4a9eda4864 100644
--- a/cpp/tests/Makefile.am
+++ b/cpp/tests/Makefile.am
@@ -71,10 +71,10 @@ abs_srcdir = @abs_srcdir@
TESTS_ENVIRONMENT = \
VALGRIND=$(VALGRIND) \
abs_builddir='$(abs_builddir)' \
- PATH="$(abs_builddir)/../src$(PATH_SEPARATOR)$$PATH" \
+ PATH="$(abs_srcdir)$(PATH_SEPARATOR)$(abs_builddir)/../src$(PATH_SEPARATOR)$$PATH" \
abs_srcdir='$(abs_srcdir)'
-TESTS = run-unit-tests run-python-tests
+TESTS = run-unit-tests run-python-tests daemon_test
EXTRA_DIST += $(TESTS)
CLEANFILES=qpidd.log
diff --git a/cpp/tests/broker b/cpp/tests/broker
deleted file mode 100755
index c49e967466..0000000000
--- a/cpp/tests/broker
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/bin/sh
-. `dirname $0`/env
-
-brokerpid() {
- netstat -tpl 2> /dev/null | awk '/amqp/ {print gensub("/.*$","","g",$7) }'
-}
-
-killbroker () {
- PID=`brokerpid`
- if [ -n "$PID" ] ; then kill $PID ; fi
- for ((i=5;i--;)) {
- if [ -z "`brokerpid`" ] ; then exit 0 ; fi
- sleep 1
- }
- echo "Broker `brokerpid` refuses to die."
-}
-
-waitbroker () {
- while [ -z `brokerpid` ] ; do sleep 1 ; done
-}
-
-startbroker() {
- case $1 in
- j)
- export AMQJ_LOGGING_LEVEL=fatal
- export JDPA_OPTS=
- export QPID_OPTS=-Xmx1024M
- export debug=1
- CMD="qpid-server"
- qpid-run -run:print-command # Show the command line.
- ;;
- c) CMD=qpidd ;;
- esac
- nohup $CMD > /dev/null 2>&1 &
- waitbroker
- echo Broker started: $CMD
-}
-
-
-case $1 in
- j|c) startbroker $1 ;;
- stop|kill) killbroker ;;
- wait) waitbroker ;;
- pid) brokerpid ;;
-esac
diff --git a/cpp/tests/daemon_test b/cpp/tests/daemon_test
new file mode 100755
index 0000000000..23f7f7d687
--- /dev/null
+++ b/cpp/tests/daemon_test
@@ -0,0 +1,22 @@
+#!/bin/sh
+# Without arguments run all daemon tests, exit status is number of failures.
+# With arguments run just the test named by $1.
+#
+
+TEMP=`mktemp`
+trap 'rm -f $TEMP' 0
+
+fail() { echo FAIL: $0:$* 1>&2; exit 1; }
+
+# Start and stop daemon.
+PID=`qpidd --check --ppid` && fail $LINENO: qpidd already running $PID
+qpidd -d || $LINENO: qpidd -d failed
+qpidd --check || fail $LINENO: qpidd --check says qpidd didnt start
+client_test > $TEMP || fail $LINENO: client_test: `cat $TEMP`
+qpidd -q || fail $LINENO: qpidd -q failed
+qpidd -d || fail $LINENO: restart after quit failed.
+qpidd -k || fail $LINENO: qpidd -k failed
+# Supress expected message re. cleanup of old PID file.
+PID=`qpidd --check --ppid 2>/dev/null` && fail $LINENO: $PID still running after kill.
+
+true