summaryrefslogtreecommitdiff
path: root/cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp')
-rw-r--r--cpp/README2
-rw-r--r--cpp/qpidc.spec.in4
-rw-r--r--cpp/src/Makefile.am11
-rw-r--r--cpp/src/qpid/broker/Daemon.cpp144
-rw-r--r--cpp/src/qpid/broker/Daemon.h91
-rw-r--r--cpp/src/qpidd.cpp101
-rw-r--r--cpp/src/tests/Makefile.am2
-rwxr-xr-xcpp/src/tests/daemon_test24
-rwxr-xr-xcpp/src/tests/test_env2
9 files changed, 351 insertions, 30 deletions
diff --git a/cpp/README b/cpp/README
index 932c6944ce..4b415ed719 100644
--- a/cpp/README
+++ b/cpp/README
@@ -38,7 +38,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/qpidc.spec.in b/cpp/qpidc.spec.in
index 4bfe0c5dca..e38e966f67 100644
--- a/cpp/qpidc.spec.in
+++ b/cpp/qpidc.spec.in
@@ -20,11 +20,13 @@ BuildRequires: doxygen
BuildRequires: e2fsprogs-devel
BuildRequires: graphviz
BuildRequires: help2man
+BuildRequires: libdaemon-devel
BuildRequires: libtool
BuildRequires: pkgconfig
-Requires: boost
Requires: apr
+Requires: boost
+Requires: libdaemon
Requires(post):/sbin/chkconfig
Requires(preun):/sbin/chkconfig
diff --git a/cpp/src/Makefile.am b/cpp/src/Makefile.am
index 99250e15ad..0d104c1470 100644
--- a/cpp/src/Makefile.am
+++ b/cpp/src/Makefile.am
@@ -5,10 +5,9 @@ AM_CXXFLAGS = $(WARNING_CFLAGS) $(APR_CXXFLAGS)
# -I top_builddir for config.h
INCLUDES = -I$(top_builddir) -I$(top_srcdir)/gen
-qpidd_LDADD = \
- libqpidbroker.la \
- libqpidcommon.la \
- -lboost_program_options
+qpidd_LDADD = \
+ libqpidbroker.la \
+ libqpidcommon.la
sbin_PROGRAMS = qpidd
qpidd_SOURCES = qpidd.cpp
@@ -104,7 +103,7 @@ libqpidcommon_la_SOURCES = \
qpid/CommonOptions.cpp
-libqpidbroker_la_LIBADD = libqpidcommon.la
+libqpidbroker_la_LIBADD = libqpidcommon.la -ldaemon -lboost_filesystem
libqpidbroker_la_LDFLAGS = -version-info $(LIBTOOL_VERSION_INFO_ARG)
libqpidbroker_la_SOURCES = \
qpid/broker/AccumulatedAck.cpp \
@@ -118,6 +117,7 @@ libqpidbroker_la_SOURCES = \
qpid/broker/BrokerQueue.cpp \
qpid/broker/Connection.cpp \
qpid/broker/ConnectionFactory.cpp \
+ qpid/broker/Daemon.cpp \
qpid/broker/DeliverableMessage.cpp \
qpid/broker/DeliveryRecord.cpp \
qpid/broker/DirectExchange.cpp \
@@ -204,6 +204,7 @@ nobase_include_HEADERS = \
qpid/broker/ConnectionFactory.h \
qpid/broker/ConnectionToken.h \
qpid/broker/Content.h \
+ qpid/broker/Daemon.h \
qpid/broker/DeliveryRecord.h \
qpid/broker/HeadersExchange.h \
qpid/broker/LazyLoadedContent.h \
diff --git a/cpp/src/qpid/broker/Daemon.cpp b/cpp/src/qpid/broker/Daemon.cpp
new file mode 100644
index 0000000000..729718b9d1
--- /dev/null
+++ b/cpp/src/qpid/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 "Daemon.h"
+#include "qpid/QpidError.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/src/qpid/broker/Daemon.h b/cpp/src/qpid/broker/Daemon.h
new file mode 100644
index 0000000000..cf9bf13764
--- /dev/null
+++ b/cpp/src/qpid/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/src/qpidd.cpp b/cpp/src/qpidd.cpp
index 0bcc2c42fb..38289ca333 100644
--- a/cpp/src/qpidd.cpp
+++ b/cpp/src/qpidd.cpp
@@ -24,29 +24,51 @@
#include <signal.h>
#include "config.h"
#include "qpid/sys/posix/check.h"
+#include "qpid/broker/Daemon.h"
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")
@@ -86,43 +108,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=(boost::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;
+ }
+ }
+ else if (pid > 0) { // Parent
+ if (config.ppid)
+ cout << pid << endl;
+ return 0;
}
- brokerPtr->run();
+ 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/src/tests/Makefile.am b/cpp/src/tests/Makefile.am
index 5c4cb3e712..805f8d8e32 100644
--- a/cpp/src/tests/Makefile.am
+++ b/cpp/src/tests/Makefile.am
@@ -57,7 +57,7 @@ noinst_PROGRAMS = $(testprogs)
TESTS_ENVIRONMENT = VALGRIND=$(VALGRIND) srcdir=$(srcdir)
CLIENT_TESTS = client_test quick_topictest
-TESTS = run-unit-tests start_broker $(CLIENT_TESTS) python_tests kill_broker
+TESTS = run-unit-tests start_broker $(CLIENT_TESTS) python_tests kill_broker daemon_test
EXTRA_DIST = \
test_env \
diff --git a/cpp/src/tests/daemon_test b/cpp/src/tests/daemon_test
new file mode 100755
index 0000000000..ab87a2f848
--- /dev/null
+++ b/cpp/src/tests/daemon_test
@@ -0,0 +1,24 @@
+#!/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`
+qpidd=../qpidd
+client_tests=./client_test
+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
diff --git a/cpp/src/tests/test_env b/cpp/src/tests/test_env
index fdfc5f0369..c73cac43ea 100755
--- a/cpp/src/tests/test_env
+++ b/cpp/src/tests/test_env
@@ -6,7 +6,7 @@
# Use VALGRIND if set, else look on path.
test -z "$VALGRIND" && VALGRIND=`which valgrind 2>/dev/null`
# No valgrind, just run the command
-test -z "$VALGRIND" && exec libtool --mode=execute "$@"
+test -z "$VALGRIND" && { exec libtool --mode=execute "$@"; exit $?; }
test "$VERBOSE" = yes && set -x
test -z "$vg_log" && vg_log=valgrind.out