You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ac...@apache.org on 2007/06/11 18:05:07 UTC

svn commit: r546180 - in /incubator/qpid/trunk/qpid/cpp: qpidc.spec.in src/qpid/Exception.cpp src/qpid/broker/Daemon.cpp src/qpid/broker/Daemon.h src/qpidd.cpp src/tests/daemon_test

Author: aconway
Date: Mon Jun 11 09:05:06 2007
New Revision: 546180

URL: http://svn.apache.org/viewvc?view=rev&rev=546180
Log:

QPID-504: Print bound port if --port 0 is specified. Not yet used by tests.

	* qpidd.cpp:
	 - With --port 0 print the bound port number to stdout.
	 - Removed --ppid, --check now prints pid.

	* Daemon.cpp/h: Move pid-file generation to caller (qpidd.cpp)

	* Exception.cpp: Log a debug message in exception constructors.
	  Helps to show what exceptions were thrown even if they aren't
	  logged at a higher level. 

	* daemon_test: Test new daemon options.

Modified:
    incubator/qpid/trunk/qpid/cpp/qpidc.spec.in
    incubator/qpid/trunk/qpid/cpp/src/qpid/Exception.cpp
    incubator/qpid/trunk/qpid/cpp/src/qpid/broker/Daemon.cpp
    incubator/qpid/trunk/qpid/cpp/src/qpid/broker/Daemon.h
    incubator/qpid/trunk/qpid/cpp/src/qpidd.cpp
    incubator/qpid/trunk/qpid/cpp/src/tests/daemon_test

Modified: incubator/qpid/trunk/qpid/cpp/qpidc.spec.in
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/qpidc.spec.in?view=diff&rev=546180&r1=546179&r2=546180
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/qpidc.spec.in (original)
+++ incubator/qpid/trunk/qpid/cpp/qpidc.spec.in Mon Jun 11 09:05:06 2007
@@ -114,6 +114,8 @@
 %defattr(-,root,root,-)
 %_libdir/libqpidbroker.so.0
 %_libdir/libqpidbroker.so.0.1.0
+%_libdir/libqpidcluster.so.0
+%_libdir/libqpidcluster.so.0.1.0
 %_sbindir/%{qpidd}
 %{_initrddir}/%{qpidd}
 %doc %_mandir/man1/%{qpidd}.*

Modified: incubator/qpid/trunk/qpid/cpp/src/qpid/Exception.cpp
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/src/qpid/Exception.cpp?view=diff&rev=546180&r1=546179&r2=546180
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/src/qpid/Exception.cpp (original)
+++ incubator/qpid/trunk/qpid/cpp/src/qpid/Exception.cpp Mon Jun 11 09:05:06 2007
@@ -19,7 +19,9 @@
  *
  */
 
+#include "qpid/log/Statement.h"
 #include "Exception.h"
+#include <typeinfo>
 #include <errno.h>
 
 namespace qpid {
@@ -28,12 +30,17 @@
     char buf[512];
     return std::string(strerror_r(err, buf, sizeof(buf)));
 }
+
+static void ctorLog(const std::exception* e) {
+    QPID_LOG(trace, "Exception constructor " << typeid(e).name() << ": " << e->what());
+}
     
-Exception::Exception() throw() {}
+Exception::Exception() throw() { ctorLog(this); }
 
-Exception::Exception(const std::string& str) throw() : whatStr(str) {}
+Exception::Exception(const std::string& str) throw()
+    : whatStr(str) { ctorLog(this); }
 
-Exception::Exception(const char* str) throw() : whatStr(str) {}
+Exception::Exception(const char* str) throw() : whatStr(str) { ctorLog(this); }
 
 Exception::~Exception() throw() {}
 

Modified: incubator/qpid/trunk/qpid/cpp/src/qpid/broker/Daemon.cpp
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/src/qpid/broker/Daemon.cpp?view=diff&rev=546180&r1=546179&r2=546180
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/src/qpid/broker/Daemon.cpp (original)
+++ incubator/qpid/trunk/qpid/cpp/src/qpid/broker/Daemon.cpp Mon Jun 11 09:05:06 2007
@@ -16,50 +16,35 @@
  *
  */
 #include "Daemon.h"
+#include "qpid/log/Statement.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;
+boost::function<std::string()> qpid::broker::Daemon::pidFileFn;
 
-string Daemon::nameFromArgv0(const char* argv0) {
-    return string(daemon_ident_from_argv0(const_cast<char*>(argv0)));
+std::string Daemon::defaultPidFile(const std::string& identifier) {
+    daemon_pid_file_ident=identifier.c_str();
+    return daemon_pid_file_proc_default();
 }
 
-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();
+const char* Daemon::realPidFileFn() {
+    static std::string str = pidFileFn();
+    return str.c_str();
 }
 
-Daemon::Daemon(const string& name_, int secs) : pid(-1), timeout(secs)
+Daemon::Daemon(boost::function<std::string()> fn, int secs) : pid(-1), 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.    
+    pidFileFn = fn;
+    daemon_pid_file_proc = &realPidFileFn;
 }
 
 Daemon::~Daemon() {
@@ -77,44 +62,58 @@
     bool completed;
 };
 
-pid_t Daemon::fork() {
+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) {
-        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);
+        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;
         }
     }
-    else if (pid == 0) { // child.
-        // TODO aconway 2007-04-26: Should log failures.
-        if (daemon_pid_file_create())
-            failed();
-    }
+    else
+        parent(*this);
     return pid;
 }
 
-void Daemon::notify(int i) {
+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;
+}
+
+void Daemon::notify(int value) { // child
     assert(retval);
-    if (retval->send(i)) 
+    if (retval->send(value)) 
         throw Exception("Failed to notify parent: "+strError(errno));
 }
 
-void Daemon::ready() { notify(0); }
+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);
+}
 
-// NB: Not -1, confused with failure of fork() on the parent side.
-void Daemon::failed() { notify(errno? errno:-2); }
+void Daemon::failed() { notify(-1); }
 
-void  Daemon::quit() {
+void  Daemon::quit() { 
     if (daemon_pid_file_kill_wait(SIGINT, timeout))
         throw Exception("Failed to stop daemon: " + strError(errno));
 }

Modified: incubator/qpid/trunk/qpid/cpp/src/qpid/broker/Daemon.h
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/src/qpid/broker/Daemon.h?view=diff&rev=546180&r1=546179&r2=546180
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/src/qpid/broker/Daemon.h (original)
+++ incubator/qpid/trunk/qpid/cpp/src/qpid/broker/Daemon.h Mon Jun 11 09:05:06 2007
@@ -21,6 +21,7 @@
 
 #include <string>
 #include <boost/scoped_ptr.hpp>
+#include <boost/function.hpp>
 
 namespace qpid {
 namespace broker {
@@ -32,24 +33,39 @@
 class Daemon
 {
   public:
-
-    /** Extract the daemon's name from argv[0] */
-    static std::string nameFromArgv0(const char* argv0);
+    /** 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);
     
     /**
-     * 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 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(const std::string& name, int timeout);
+    Daemon(boost::function<std::string()> pidFileFn, 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();
+    typedef boost::function<void(Daemon&)> Function;
+
+    /** Fork the daemon.
+     *@param parent called in the parent process.
+     *@param child called in the child process.
+     */
+    pid_t fork(Function parent, Function child);
+
+    /** Parent only: wait for child to indicate it is ready.
+     * @return value child passed to ready() */
+    int wait();
+
+    /** 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();
@@ -67,25 +83,21 @@
 
     bool isChild() { return pid == 0; }
     
-    std::string getName() const { return name; }
-
     pid_t getPid() const {return pid; }
 
   private:
     class Retval;
 
+    static boost::function<std::string()> pidFileFn;
+    static const char* realPidFileFn();
     void notify(int);
     
-    static std::string name;
-    static std::string pidFile;
-    static const char* getPidFile();
+    static std::string identifier;
     boost::scoped_ptr<Retval> retval;
     pid_t pid;
     int timeout;
 };
 
 }} // namespace qpid::broker
-
-
 
 #endif  /*!_broker_Daemon_h*/

Modified: incubator/qpid/trunk/qpid/cpp/src/qpidd.cpp
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/src/qpidd.cpp?view=diff&rev=546180&r1=546179&r2=546180
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/src/qpidd.cpp (original)
+++ incubator/qpid/trunk/qpid/cpp/src/qpidd.cpp Mon Jun 11 09:05:06 2007
@@ -25,23 +25,18 @@
 #include "qpid/log/Options.h"
 #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>
+#include <unistd.h>
 
 using namespace qpid;
 using namespace qpid::broker;
 using namespace qpid::sys;
 using namespace std;
 
-Broker::shared_ptr brokerPtr;
-
-void handle_signal(int /*signal*/){
-    QPID_LOG(notice, "Shutting down...");
-    brokerPtr->shutdown();
-}
-
-
 /** Command line options */
 struct QpiddOptions : public Broker::Options, public log::Options
 {
@@ -52,39 +47,42 @@
     bool quit;
     bool kill;
     bool check;
-    bool ppid;
     int wait;
     string config;
     po::options_description mainOpts;
     po::options_description allOpts;
-    po::options_description logOpts;
     
     QpiddOptions() :
         help(false), version(false), daemon(false),
-        quit(false), kill(false), check(false), ppid(false), wait(10),
+        quit(false), check(false), 
+        wait(10),
         config("/etc/qpidd.conf"),
-        mainOpts("Options"),
-        logOpts("Logging Options")
+        mainOpts("Broker Options")
     {
         using namespace po;
-        mainOpts.add_options()
-            ("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;
+        // First set up the sub-option groups.
+        options_description daemonOpts("Daemon Options");
+        daemonOpts.add_options()
+            ("daemon,d", optValue(daemon), "Run as a daemon. With --port 0 print actual listening port.")
+            ("wait,w", optValue(wait, "SECONDS"), "Maximum wait for daemon response.")
+            ("check,c", optValue(check), "If a daemon is running print its pid to stdout and return 0.")
+            ("quit,q", optValue(quit), "Stop the running daemon politely.");
+
+        options_description logOpts("Logging Options");
+        log::Options::addTo(logOpts);
+
+        // Populate the main options group for --help
         Broker::Options::addTo(mainOpts);
         mainOpts.add_options()
             ("config", optValue(config, "FILE"), "Configuation file.")
             ("help,h", optValue(help), "Print help message.")
             ("long-help", optValue(longHelp), "Show complete list of options.")
             ("version,v", optValue(version), "Print version information.");
+        mainOpts.add(daemonOpts);
 
-        log::Options::addTo(logOpts);
+        // Populate the all options group
         allOpts.add(mainOpts).add(logOpts);
+
     }
 
     void parse(int argc, char* argv[]) {
@@ -97,19 +95,62 @@
     };
 };
 
+// Globals
+Broker::shared_ptr brokerPtr;
+QpiddOptions config;
+
+void handle_signal(int /*signal*/){
+    QPID_LOG(notice, "Shutting down...");
+    brokerPtr->shutdown();
+}
+
+/** Compute a name for the pid file */
+std::string pidFileFn() {
+    uint16_t port=brokerPtr ? brokerPtr->getPort() : config.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();
+    }
+    QPID_LOG(debug, "PID file name=" << pidPath);
+    return pidPath;
+}
+
+/** Code for forked parent */
+void parent(Daemon& demon) {
+    uint16_t realPort = demon.wait();
+    if (config.port == 0)
+        cout << realPort << endl; 
+}
+
+/** Code for forked child */
+void child(Daemon& demon) {
+    brokerPtr=Broker::create(config);
+    uint16_t realPort=brokerPtr->getPort();
+    demon.ready(realPort);   // Notify parent.
+    brokerPtr->run();
+}
+
+
 int main(int argc, char* argv[])
 {
-    QpiddOptions config;
+    // Spelled 'demon' to avoid clash with daemon.h function.
+    Daemon demon(pidFileFn, config.wait);
+
     try {
         config.parse(argc, argv);
         if (config.trace)
             config.selectors.push_back("trace+");
         log::Logger::instance().configure(config, argv[0]);
-        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.longHelp || config.version) {
@@ -123,46 +164,32 @@
             return 0;
         }
 
-        // Options that act on an already running daemon.
-        if (config.quit || config.kill || config.check) {
+        // Stop running daemon
+        if (config.quit) {
+            demon.quit();
+            return 0;
+        }
+
+        // Query running daemon
+        if (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)
+            if (pid < 0) 
                 return 1;
-            return 0;
+            else {
+                cout << pid << endl;
+                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) {
-                    QPID_LOG(critical, "Broker daemon startup failed: " << e.what());
-                    demon.failed(); // Notify parent we failed.
-                    return 1;
-                }
-            }
-            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 {
+        if (config.daemon) {    // Daemon broker
+            demon.fork(parent, child);
+        } 
+        else {                  // Non-daemon broker.
             brokerPtr = Broker::create(config);
+            if (config.port == 0)
+                cout << uint16_t(brokerPtr->getPort()) << endl; 
             brokerPtr->run(); 
         }
         return 0;
@@ -170,12 +197,13 @@
     catch(const po::error& e) {
         // Command line parsing error.
         cerr << "Error: " << e.what() << endl
-             << "Type 'qpidd --help' for usage." << endl;
+             << "Type 'qpidd --long-help' for full usage." << endl;
     }
     catch(const exception& e) {
-        // Could be child or parent so log and print.
-        QPID_LOG(error, e.what());
-        cerr << "Error: " << e.what() << endl;
+        if (demon.isParent())
+            cerr << "Error: " << e.what() << endl;
+        else
+            QPID_LOG(critical, e.what());
     }
     return 1;
 }

Modified: incubator/qpid/trunk/qpid/cpp/src/tests/daemon_test
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/src/tests/daemon_test?view=diff&rev=546180&r1=546179&r2=546180
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/src/tests/daemon_test (original)
+++ incubator/qpid/trunk/qpid/cpp/src/tests/daemon_test Mon Jun 11 09:05:06 2007
@@ -4,21 +4,28 @@
 #
 
 TEMP=`mktemp`
-qpidd=../qpidd
+qpidd="../qpidd --log.output qpidd.log"
 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
+# Start and stop daemon on default port.
+PID=`$qpidd --check` && fail $LINENO: qpidd already running pid=$PID
+$qpidd -d || fail $LINENO: $qpidd -d failed
+$qpidd -c >/dev/null || fail $LINENO: qpidd --check says qpidd did not 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. 
+$qpidd -q || fail $LINENO: qpidd -q failed
+$qpidd -c >/dev/null && fail $LINENO: Still running after quit.
+
+# Start and stop daemon on dynamic port.
+export QPID_PORT=`$qpidd -dp0`
+# Note: QPID_PORT fom environment will be used below here:
+$qpidd -c >/dev/null || fail $LINENO: qpidd did not start. QPID_PORT=$QPID_PORT
+$qpidd -q || fail $LINENO: qpidd -q failed. QPID_PORT=$QPID_PORT
+$qpidd -c >/dev/null && fail $LINENO: Still running after start. QPID_PORT=$QPID_PORT
+
+# FIXME aconway 2007-06-11: run client test, needs a --port option.
+
 
 true