aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2014-01-18 12:01:09 +0100
committerMax Kellermann <max@duempel.org>2014-01-18 12:42:30 +0100
commitbe47320a0589a84eb942790c3884bb6e25c38a2f (patch)
treebe474aac8c82dfdd9d00b8893a1c4f11caecb6c1
parent9f3ce7551a481f757feeb6837961f565a686c945 (diff)
downloadmpd-be47320a0589a84eb942790c3884bb6e25c38a2f.tar.gz
mpd-be47320a0589a84eb942790c3884bb6e25c38a2f.tar.xz
mpd-be47320a0589a84eb942790c3884bb6e25c38a2f.zip
Daemon: fork as early as possible
Keep the parent process around until MPD has finished initializing. This is important for libraries that are allergic to fork(), such as libupnp.
-rw-r--r--src/Daemon.cxx101
-rw-r--r--src/Daemon.hxx12
-rw-r--r--src/Main.cxx3
-rw-r--r--src/PidFile.hxx19
4 files changed, 107 insertions, 28 deletions
diff --git a/src/Daemon.cxx b/src/Daemon.cxx
index f11a6e547..490b2def5 100644
--- a/src/Daemon.cxx
+++ b/src/Daemon.cxx
@@ -31,6 +31,7 @@
#include <fcntl.h>
#ifndef WIN32
+#include <sys/wait.h>
#include <signal.h>
#include <pwd.h>
#include <grp.h>
@@ -55,6 +56,11 @@ static AllocatedPath pidfile = AllocatedPath::Null();
/* whether "group" conf. option was given */
static bool had_group = false;
+/**
+ * The write end of a pipe that is used to notify the parent process
+ * that initialization has finished and that it should detach.
+ */
+static int detach_fd = -1;
void
daemonize_kill(void)
@@ -130,48 +136,93 @@ daemonize_set_user(void)
}
}
-static void
-daemonize_detach(void)
+void
+daemonize_begin(bool detach)
{
+ /* release the current working directory */
+ if (chdir("/") < 0)
+ FatalError("problems changing to root directory");
+
+ if (!detach)
+ /* the rest of this function deals with detaching the
+ process */
+ return;
+
+ /* do this before daemonizing so we can fail gracefully if we
+ can't write to the pid file */
+ PidFile pidfile2(pidfile);
+
/* flush all file handles before duplicating the buffers */
fflush(nullptr);
- /* detach from parent process */
+ /* create a pipe to synchronize the parent and the child */
+
+ int fds[2];
+ if (pipe(fds) < 0)
+ FatalSystemError("pipe() failed");
- switch (fork()) {
- case -1:
+ /* move to a child process */
+
+ pid_t pid = fork();
+ if (pid < 0)
FatalSystemError("fork() failed");
- case 0:
- break;
- default:
- /* exit the parent process */
- _exit(EXIT_SUCCESS);
+
+ if (pid == 0) {
+ /* in the child process */
+
+ pidfile2.Close();
+ close(fds[0]);
+ detach_fd = fds[1];
+
+ /* detach from the current session */
+ setsid();
+
+ /* continue starting MPD */
+ return;
}
- /* release the current working directory */
+ /* in the parent process */
- if (chdir("/") < 0)
- FatalError("problems changing to root directory");
+ close(fds[1]);
+
+ int result;
+ ssize_t nbytes = read(fds[0], &result, sizeof(result));
+ if (nbytes == (ssize_t)sizeof(result)) {
+ /* the child process was successful */
+ pidfile2.Write(pid);
+ exit(EXIT_SUCCESS);
+ }
+
+ /* something bad happened in the child process */
- /* detach from the current session */
+ pidfile2.Delete(pidfile);
- setsid();
+ int status;
+ pid_t pid2 = waitpid(pid, &status, 0);
+ if (pid2 < 0)
+ FatalSystemError("waitpid() failed");
- LogDebug(daemon_domain, "daemonized");
+ if (WIFSIGNALED(status))
+ FormatFatalError("MPD died from signal %d%s", WTERMSIG(status),
+ WCOREDUMP(status) ? " (core dumped)" : "");
+
+ exit(WEXITSTATUS(status));
}
void
-daemonize(bool detach)
+daemonize_commit()
{
- /* do this before daemon'izing so we can fail gracefully if we
- can't write to the pid file */
- PidFile pidfile2(pidfile);
-
- if (detach)
- daemonize_detach();
-
- pidfile2.Write();
+ if (detach_fd >= 0) {
+ /* tell the parent process to let go of us and exit
+ indicating success */
+ int result = 0;
+ write(detach_fd, &result, sizeof(result));
+ close(detach_fd);
+ } else
+ /* the pidfile was not written by the parent because
+ there is no parent - do it now */
+ PidFile(pidfile).Write();
}
void
diff --git a/src/Daemon.hxx b/src/Daemon.hxx
index d1ce7d0f8..fe5681511 100644
--- a/src/Daemon.hxx
+++ b/src/Daemon.hxx
@@ -81,11 +81,19 @@ daemonize_set_user(void)
#ifndef WIN32
void
-daemonize(bool detach);
+daemonize_begin(bool detach);
#else
static inline void
-daemonize(bool detach)
+daemonize_begin(bool detach)
{ (void)detach; }
#endif
+#ifndef WIN32
+void
+daemonize_commit();
+#else
+static inline void
+daemonize_commit() {}
+#endif
+
#endif
diff --git a/src/Main.cxx b/src/Main.cxx
index bc5667755..9cbefdc56 100644
--- a/src/Main.cxx
+++ b/src/Main.cxx
@@ -403,6 +403,7 @@ int mpd_main(int argc, char *argv[])
}
daemonize_set_user();
+ daemonize_begin(options.daemon);
GlobalEvents::Initialize(*main_loop);
GlobalEvents::Register(GlobalEvents::IDLE, idle_event_emitted);
@@ -451,7 +452,7 @@ int mpd_main(int argc, char *argv[])
playlist_list_global_init();
- daemonize(options.daemon);
+ daemonize_commit();
setup_log_output(options.log_stderr);
diff --git a/src/PidFile.hxx b/src/PidFile.hxx
index c06ed6b61..a242c7810 100644
--- a/src/PidFile.hxx
+++ b/src/PidFile.hxx
@@ -47,6 +47,25 @@ public:
PidFile(const PidFile &) = delete;
+ void Close() {
+ if (file == nullptr)
+ return;
+
+ fclose(file);
+ }
+
+ void Delete(const AllocatedPath &path) {
+ if (file == nullptr) {
+ assert(path.IsNull());
+ return;
+ }
+
+ assert(!path.IsNull());
+
+ fclose(file);
+ RemoveFile(path);
+ }
+
void Write(pid_t pid) {
if (file == nullptr)
return;