diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Daemon.cxx | 101 | ||||
-rw-r--r-- | src/Daemon.hxx | 12 | ||||
-rw-r--r-- | src/Main.cxx | 3 | ||||
-rw-r--r-- | src/PidFile.hxx | 19 |
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; |