Commit be47320a authored by Max Kellermann's avatar Max Kellermann

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.
parent 9f3ce755
......@@ -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");
/* move to a child process */
switch (fork()) {
case -1:
pid_t pid = fork();
if (pid < 0)
FatalSystemError("fork() failed");
case 0:
break;
default:
/* exit the parent process */
_exit(EXIT_SUCCESS);
}
/* release the current working directory */
if (pid == 0) {
/* in the child process */
if (chdir("/") < 0)
FatalError("problems changing to root directory");
pidfile2.Close();
close(fds[0]);
detach_fd = fds[1];
/* detach from the current session */
setsid();
LogDebug(daemon_domain, "daemonized");
/* continue starting MPD */
return;
}
/* in the parent process */
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 */
pidfile2.Delete(pidfile);
int status;
pid_t pid2 = waitpid(pid, &status, 0);
if (pid2 < 0)
FatalSystemError("waitpid() failed");
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
......
......@@ -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
......@@ -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);
......
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment