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.
This commit is contained in:
parent
9f3ce7551a
commit
be47320a05
101
src/Daemon.cxx
101
src/Daemon.cxx
|
@ -31,6 +31,7 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
|
#include <sys/wait.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <grp.h>
|
#include <grp.h>
|
||||||
|
@ -55,6 +56,11 @@ static AllocatedPath pidfile = AllocatedPath::Null();
|
||||||
/* whether "group" conf. option was given */
|
/* whether "group" conf. option was given */
|
||||||
static bool had_group = false;
|
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
|
void
|
||||||
daemonize_kill(void)
|
daemonize_kill(void)
|
||||||
|
@ -130,48 +136,93 @@ daemonize_set_user(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void
|
||||||
daemonize_detach(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 */
|
/* flush all file handles before duplicating the buffers */
|
||||||
|
|
||||||
fflush(nullptr);
|
fflush(nullptr);
|
||||||
|
|
||||||
/* detach from parent process */
|
/* create a pipe to synchronize the parent and the child */
|
||||||
|
|
||||||
switch (fork()) {
|
int fds[2];
|
||||||
case -1:
|
if (pipe(fds) < 0)
|
||||||
|
FatalSystemError("pipe() failed");
|
||||||
|
|
||||||
|
/* move to a child process */
|
||||||
|
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid < 0)
|
||||||
FatalSystemError("fork() failed");
|
FatalSystemError("fork() failed");
|
||||||
case 0:
|
|
||||||
break;
|
if (pid == 0) {
|
||||||
default:
|
/* in the child process */
|
||||||
/* exit the parent process */
|
|
||||||
_exit(EXIT_SUCCESS);
|
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)
|
close(fds[1]);
|
||||||
FatalError("problems changing to root directory");
|
|
||||||
|
|
||||||
/* detach from the current session */
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
setsid();
|
/* something bad happened in the child process */
|
||||||
|
|
||||||
LogDebug(daemon_domain, "daemonized");
|
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
|
void
|
||||||
daemonize(bool detach)
|
daemonize_commit()
|
||||||
{
|
{
|
||||||
/* do this before daemon'izing so we can fail gracefully if we
|
if (detach_fd >= 0) {
|
||||||
can't write to the pid file */
|
/* tell the parent process to let go of us and exit
|
||||||
PidFile pidfile2(pidfile);
|
indicating success */
|
||||||
|
int result = 0;
|
||||||
if (detach)
|
write(detach_fd, &result, sizeof(result));
|
||||||
daemonize_detach();
|
close(detach_fd);
|
||||||
|
} else
|
||||||
pidfile2.Write();
|
/* the pidfile was not written by the parent because
|
||||||
|
there is no parent - do it now */
|
||||||
|
PidFile(pidfile).Write();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -81,11 +81,19 @@ daemonize_set_user(void)
|
||||||
|
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
void
|
void
|
||||||
daemonize(bool detach);
|
daemonize_begin(bool detach);
|
||||||
#else
|
#else
|
||||||
static inline void
|
static inline void
|
||||||
daemonize(bool detach)
|
daemonize_begin(bool detach)
|
||||||
{ (void)detach; }
|
{ (void)detach; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
void
|
||||||
|
daemonize_commit();
|
||||||
|
#else
|
||||||
|
static inline void
|
||||||
|
daemonize_commit() {}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -403,6 +403,7 @@ int mpd_main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
daemonize_set_user();
|
daemonize_set_user();
|
||||||
|
daemonize_begin(options.daemon);
|
||||||
|
|
||||||
GlobalEvents::Initialize(*main_loop);
|
GlobalEvents::Initialize(*main_loop);
|
||||||
GlobalEvents::Register(GlobalEvents::IDLE, idle_event_emitted);
|
GlobalEvents::Register(GlobalEvents::IDLE, idle_event_emitted);
|
||||||
|
@ -451,7 +452,7 @@ int mpd_main(int argc, char *argv[])
|
||||||
|
|
||||||
playlist_list_global_init();
|
playlist_list_global_init();
|
||||||
|
|
||||||
daemonize(options.daemon);
|
daemonize_commit();
|
||||||
|
|
||||||
setup_log_output(options.log_stderr);
|
setup_log_output(options.log_stderr);
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,25 @@ public:
|
||||||
|
|
||||||
PidFile(const PidFile &) = delete;
|
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) {
|
void Write(pid_t pid) {
|
||||||
if (file == nullptr)
|
if (file == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
Loading…
Reference in New Issue