summaryrefslogtreecommitdiff
path: root/qpid/cpp/src/qpid/broker/Daemon.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'qpid/cpp/src/qpid/broker/Daemon.cpp')
-rw-r--r--qpid/cpp/src/qpid/broker/Daemon.cpp219
1 files changed, 219 insertions, 0 deletions
diff --git a/qpid/cpp/src/qpid/broker/Daemon.cpp b/qpid/cpp/src/qpid/broker/Daemon.cpp
new file mode 100644
index 0000000000..b30e5f18cb
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Daemon.cpp
@@ -0,0 +1,219 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+/*
+ * TODO: Note this is really a Posix specific implementation and so should be
+ * refactored together with windows/QpiddBroker into a more coherent daemon driver/
+ * platform specific split
+ */
+#include "qpid/broker/Daemon.h"
+#include "qpid/log/Statement.h"
+#include "qpid/Exception.h"
+#include "qpid/sys/posix/PidFile.h"
+
+#include <errno.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;
+using qpid::sys::PidFile;
+
+Daemon::Daemon(std::string _pidDir) : pidDir(_pidDir) {
+ struct stat s;
+ pid = -1;
+ pipeFds[0] = pipeFds[1] = -1;
+
+ if (::stat(pidDir.c_str(), &s)) {
+ if (errno == ENOENT) {
+ if (::mkdir(pidDir.c_str(), 0755))
+ throw Exception ("Can't create PID directory: " + pidDir);
+ }
+ else
+ throw Exception ("PID directory not found: " + pidDir);
+ }
+}
+
+string Daemon::pidFile(string pidDir, uint16_t port) {
+ ostringstream path;
+ path << pidDir << "/qpidd." << port << ".pid";
+ return path.str();
+}
+
+/*
+ * Rewritten using low-level IO, for compatibility
+ * with earlier Boost versions, i.e. 103200.
+ */
+void Daemon::fork()
+{
+ if(::pipe(pipeFds) < 0) throw ErrnoException("Can't create pipe");
+ if ((pid = ::fork()) < 0) throw ErrnoException("Daemon fork failed");
+ if (pid == 0) { // Child
+ try {
+ QPID_LOG(debug, "Forked daemon child process");
+
+ // File descriptors
+ if(::close(pipeFds[0])<0) throw ErrnoException("Cannot close read pipe");
+ if(::close(0)<0) throw ErrnoException("Cannot close stdin");
+ if(::close(1)<0) throw ErrnoException("Cannot close stdout");
+ if(::close(2)<0) throw ErrnoException("Cannot close stderr");
+ int fd=::open("/dev/null",O_RDWR); // stdin
+ if(fd != 0) throw ErrnoException("Cannot re-open stdin");
+ if(::dup(fd)<0) throw ErrnoException("Cannot re-open stdout");
+ if(::dup(fd)<0) throw ErrnoException("Cannot re-open stderror");
+
+ // Misc
+ if(setsid()<0) throw ErrnoException("Cannot set session ID");
+ if(chdir(pidDir.c_str()) < 0) throw ErrnoException("Cannot change directory to "+pidDir);
+ umask(027);
+
+ // Child behavior
+ child();
+ }
+ catch (const exception& e) {
+ QPID_LOG(critical, "Unexpected error: " << e.what());
+ uint16_t port = 0;
+ int unused_ret; //Supress warning about ignoring return value.
+ unused_ret = write(pipeFds[1], &port, sizeof(uint16_t));
+
+ std::string pipeFailureMessage = e.what();
+ unused_ret = write ( pipeFds[1],
+ pipeFailureMessage.c_str(),
+ strlen(pipeFailureMessage.c_str())
+ );
+ }
+ }
+ else { // Parent
+ close(pipeFds[1]); // Write side.
+ parent();
+ }
+}
+
+Daemon::~Daemon() {
+ if (!lockFile.empty())
+ unlink(lockFile.c_str());
+}
+
+uint16_t Daemon::wait(int timeout) { // parent waits for child.
+ try {
+ errno = 0;
+ struct timeval tv;
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ /*
+ * Rewritten using low-level IO, for compatibility
+ * with earlier Boost versions, i.e. 103200.
+ */
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(pipeFds[0], &fds);
+ int n=select(FD_SETSIZE, &fds, 0, 0, &tv);
+ if(n==0) throw Exception("Timed out waiting for daemon (If store recovery is in progress, use longer wait time)");
+ if(n<0) throw ErrnoException("Error waiting for daemon");
+ uint16_t port = 0;
+ /*
+ * Read the child's port number from the pipe.
+ */
+ int desired_read = sizeof(uint16_t);
+ if ( desired_read > ::read(pipeFds[0], & port, desired_read) )
+ throw Exception("Cannot read from child process.");
+
+ /*
+ * If the port number is 0, the child has put an error message
+ * on the pipe. Get it and throw it.
+ */
+ if ( 0 == port ) {
+ // Skip whitespace
+ char c = ' ';
+ while ( isspace(c) ) {
+ if ( 1 > ::read(pipeFds[0], &c, 1) )
+ throw Exception("Child port == 0, and no error message on pipe.");
+ }
+
+ // Get Message
+ string errmsg;
+ do {
+ errmsg += c;
+ } while (::read(pipeFds[0], &c, 1));
+ throw Exception("Daemon startup failed"+
+ (errmsg.empty() ? string(".") : ": " + errmsg));
+ }
+ return port;
+ }
+ catch (const std::exception& e) {
+ // Print directly to cerr. The caller will catch and log the
+ // exception, but in the case of a daemon parent process we
+ // also need to be sure the error goes to stderr. A
+ // dameon's logging configuration normally does not log to
+ // stderr.
+ std::cerr << e.what() << endl;
+ throw;
+ }
+}
+
+
+/*
+ * When the child is ready, it writes its pid to the
+ * lockfile and its port number on the pipe back to
+ * its parent process. This indicates that the
+ * child has successfully daemonized. When the parent
+ * hears the good news, it ill exit.
+ */
+void Daemon::ready(uint16_t port) { // child
+ lockFile = pidFile(pidDir, port);
+ PidFile lf(lockFile, true);
+
+ /*
+ * Write the PID to the lockfile.
+ */
+ lf.writePid();
+
+ /*
+ * Write the port number to the parent.
+ */
+ int desired_write = sizeof(uint16_t);
+ if ( desired_write > ::write(pipeFds[1], & port, desired_write) ) {
+ throw Exception("Error writing to parent." );
+ }
+
+ QPID_LOG(debug, "Daemon ready on port: " << port);
+}
+
+/*
+ * The parent process reads the child's pid
+ * from the lockfile.
+ */
+pid_t Daemon::getPid(string _pidDir, uint16_t port) {
+ string name = pidFile(_pidDir, port);
+ PidFile lf(name, false);
+ pid_t pid = lf.readPid();
+ if (kill(pid, 0) < 0 && errno != EPERM) {
+ unlink(name.c_str());
+ throw Exception("Removing stale lock file "+name);
+ }
+ return pid;
+}
+
+
+}} // namespace qpid::broker