diff --git a/kadmin/kadmind.c b/kadmin/kadmind.c index 12abaa598..f2ddac2f6 100644 --- a/kadmin/kadmind.c +++ b/kadmin/kadmind.c @@ -123,7 +123,7 @@ main(int argc, char **argv) } if (detach_from_console > 0 && daemon_child == -1) - roken_detach_prep(argc, argv, "--daemon-child"); + daemon_child = roken_detach_prep(argc, argv, "--daemon-child"); ret = krb5_init_context(&context); if (ret) diff --git a/kcm/main.c b/kcm/main.c index ec992d8bb..955769521 100644 --- a/kcm/main.c +++ b/kcm/main.c @@ -87,7 +87,7 @@ main(int argc, char **argv) signal(SIGPIPE, SIG_IGN); #endif if (detach_from_console && !launchd_flag && daemon_child == -1) - roken_detach_prep(argc, argv, "--daemon-child"); + daemon_child = roken_detach_prep(argc, argv, "--daemon-child"); rk_pidfile(NULL); if (launchd_flag) { diff --git a/kdc/config.c b/kdc/config.c index e8e0ee4a5..71b0cf23c 100644 --- a/kdc/config.c +++ b/kdc/config.c @@ -189,7 +189,7 @@ configure(krb5_context context, int argc, char **argv, int *optidx) "detach", NULL); if (detach_from_console && daemon_child == -1) - roken_detach_prep(argc, argv, "--daemon-child"); + daemon_child = roken_detach_prep(argc, argv, "--daemon-child"); { char **files; diff --git a/kpasswd/kpasswdd.c b/kpasswd/kpasswdd.c index cc617dd71..af4d51363 100644 --- a/kpasswd/kpasswdd.c +++ b/kpasswd/kpasswdd.c @@ -798,7 +798,7 @@ main(int argc, char **argv) } if (detach_from_console > 0 && daemon_child == -1) - roken_detach_prep(argc, argv, "--daemon-child"); + daemon_child = roken_detach_prep(argc, argv, "--daemon-child"); if (config_file == NULL) { aret = asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context)); diff --git a/lib/kadm5/ipropd_master.c b/lib/kadm5/ipropd_master.c index 697f2087a..3e6be3d23 100644 --- a/lib/kadm5/ipropd_master.c +++ b/lib/kadm5/ipropd_master.c @@ -1152,7 +1152,7 @@ main(int argc, char **argv) } if (detach_from_console && daemon_child == -1) - roken_detach_prep(argc, argv, "--daemon-child"); + daemon_child = roken_detach_prep(argc, argv, "--daemon-child"); rk_pidfile(NULL); ret = krb5_init_context(&context); diff --git a/lib/kadm5/ipropd_slave.c b/lib/kadm5/ipropd_slave.c index 01d3a907a..75ac5a225 100644 --- a/lib/kadm5/ipropd_slave.c +++ b/lib/kadm5/ipropd_slave.c @@ -719,7 +719,7 @@ main(int argc, char **argv) } if (detach_from_console && daemon_child == -1) - roken_detach_prep(argc, argv, "--daemon-child"); + daemon_child = roken_detach_prep(argc, argv, "--daemon-child"); rk_pidfile(NULL); ret = krb5_init_context(&context); diff --git a/lib/roken/detach.c b/lib/roken/detach.c index 7d0f24d32..8783df9c2 100644 --- a/lib/roken/detach.c +++ b/lib/roken/detach.c @@ -44,54 +44,57 @@ static int pipefds[2] = {-1, -1}; -ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL +ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL roken_detach_prep(int argc, char **argv, char *special_arg) { - pid_t child; - char buf[1]; ssize_t bytes; + size_t i; + pid_t child; + char **new_argv; + char buf[1]; + char *fildes; int status; pipefds[0] = -1; pipefds[1] = -1; #ifdef WIN32 - if (_pipe(pipefds, 4, O_BINARY) == -1) + if (_pipe(pipefds, 4, _O_NOINHERIT | O_BINARY) == -1) err(1, "failed to setup to detach daemon (_pipe failed)"); #else if (pipe(pipefds) == -1) err(1, "failed to setup to detach daemon (pipe failed)"); #endif + new_argv = calloc(argc + 3, sizeof(*new_argv)); + if (new_argv == NULL) + err(1, "Out of memory"); + +#ifdef WIN32 + pipefds[1] = _dup(pipefds[1]); /* The new fd will be inherited */ + if (pipefds[1] == -1) + err(1, "Out of memory"); +#else + fcntl(pipefds[1], F_SETFD, fcntl(pipefds[1], F_GETFD & ~(O_CLOEXEC))); +#endif + + if (asprintf(&fildes, "%d", pipefds[1]) == -1 || + fildes == NULL) + err(1, "failed to setup to detach daemon (_dup failed)"); + + new_argv[0] = argv[0]; + new_argv[1] = special_arg; + new_argv[2] = fildes; + for (i = 1; argv[i] != NULL; i++) + new_argv[i + 2] = argv[i]; + new_argv[argc + 2] = NULL; + #ifndef WIN32 fflush(stdout); child = fork(); #else { intptr_t child_handle; - int write_side; - size_t i; - char *fildes; - char **new_argv; - - new_argv = calloc(argc + 2, sizeof(*new_argv)); - if (new_argv == NULL) - err(1, "Out of memory"); - - write_side = _dup(pipefds[1]); /* The new fd will be inherited */ - if (write_side == -1) - err(1, "Out of memory"); - - if (asprintf(&fildes, "%d", write_side) == -1 || - fildes == NULL) - err(1, "failed to setup to detach daemon (_dup failed)"); - - new_argv[0] = argv[0]; - new_argv[1] = special_arg; - new_argv[2] = fildes; - for (i = 1; argv[i] != NULL; i++) - new_argv[i + 1] = argv[i]; - new_argv[argc + 2] = NULL; _flushall(); child_handle = spawnvp(_P_NOWAIT, argv[0], new_argv); @@ -120,10 +123,15 @@ roken_detach_prep(int argc, char **argv, char *special_arg) (void) dup2(fd, STDIN_FILENO); if (fd > STDERR_FILENO) (void) close(fd); - return; + if (getenv("ROKEN_DETACH_USE_EXEC")) { + (void) execvp(argv[0], new_argv); + err(1, "failed to self-re-exec"); + } + return pipefds[1]; } #endif + /* Parent */ (void) close(pipefds[1]); pipefds[1] = -1; do { @@ -149,6 +157,8 @@ roken_detach_prep(int argc, char **argv, char *special_arg) "daemon child preparation failed (child exited)"); } _exit(0); + /* NOTREACHED */ + return -1; } #ifdef WIN32 diff --git a/lib/roken/roken.h.in b/lib/roken/roken.h.in index 729caabeb..2a3feb74a 100644 --- a/lib/roken/roken.h.in +++ b/lib/roken/roken.h.in @@ -836,7 +836,7 @@ ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL roken_vconcat (char *, size_t, va_list); ROKEN_LIB_FUNCTION size_t ROKEN_LIB_CALL roken_vmconcat (char **, size_t, va_list); -ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL roken_detach_prep(int, char **, char *); +ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL roken_detach_prep(int, char **, char *); ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL roken_detach_finish(const char *, int); ROKEN_LIB_FUNCTION ssize_t ROKEN_LIB_CALL diff --git a/lib/roken/test-detach.c b/lib/roken/test-detach.c index 09434d8ab..a84a3f656 100644 --- a/lib/roken/test-detach.c +++ b/lib/roken/test-detach.c @@ -53,11 +53,13 @@ int main(int argc, char **argv) char *ends; long n; int fd = -1; + pid_t parent = getpid(); + pid_t child; - if (argc > 1) { - if (argc != 3) - errx(1, "Usage: test-detach [--daemon-child fd]"); - fprintf(stderr, "Child started (argv[1] = %s, argv[2] = %s)!\n", argv[1], argv[2]); + if (argc == 2 && strcmp(argv[1], "--reexec")) + errx(1, "Usage: test-detach [--reexec] [--daemon-child FD]"); + if (argc == 3 || argc == 4) { + parent = getppid(); errno = 0; n = strtol(argv[2], &ends, 10); fd = n; @@ -66,11 +68,19 @@ int main(int argc, char **argv) if (n < 0 || ends == NULL || *ends != '\0' || n != fd) errx(1, "Usage: test-detach [--daemon-child fd]"); } else { - fprintf(stderr, "Parent started as %ld\n", (long)getpid()); - roken_detach_prep(argc, argv, "--daemon-child"); + if (argc == 2) + /* Make sure we re-exec on the child-side of fork() (not WIN32) */ + putenv("ROKEN_DETACH_USE_EXEC=1"); + fd = roken_detach_prep(argc, argv, "--daemon-child"); + if (fd == -1) + errx(1, "bad"); } - fprintf(stderr, "Now should be the child: %ld\n", (long)getpid()); + if (parent == getpid()) + errx(1, "detach prep failed"); + child = getpid(); roken_detach_finish(NULL, fd); + if (child != getpid()) + errx(1, "detach finish failed"); /* * These printfs will not appear: stderr will have been replaced * with /dev/null.