diff --git a/src/shared/misc.c b/src/shared/misc.c index c5d387567..a192d4b0b 100644 --- a/src/shared/misc.c +++ b/src/shared/misc.c @@ -521,6 +521,46 @@ pid_t get_pid(const char *applet,const char *pidfile) return pid; } +struct ready ready_parse(const char *applet, const char *ready_string) +{ + struct ready ready = {0}; + if (sscanf(ready_string, "fd:%d", &ready.fd) != 1) + eerrorx("%s: invalid ready '%s'.", applet, optarg); + + ready.type = READY_FD; + + if (pipe(ready.pipe) == -1) + eerrorx("%s: pipe failed: %s", applet, strerror(errno)); + + return ready; +} + +bool ready_wait(const char *applet, struct ready ready) +{ + if (ready.type == READY_NONE) + return true; + + close(ready.pipe[1]); + ready.fd = ready.pipe[0]; + + for (;;) { + char buf[BUFSIZ]; + ssize_t bytes = read(ready.fd, buf, BUFSIZ); + if (bytes == -1) { + if (errno != EINTR) { + eerror("%s: read failed '%s'\n", applet, strerror(errno)); + return false; + } + continue; + } + + if (memchr(buf, '\n', bytes)) + break; + } + + return true; +} + #ifndef HAVE_CLOSE_RANGE static inline int close_range(int first RC_UNUSED, int last RC_UNUSED, diff --git a/src/shared/misc.h b/src/shared/misc.h index 3fafe6755..89d02ab39 100644 --- a/src/shared/misc.h +++ b/src/shared/misc.h @@ -66,4 +66,17 @@ pid_t get_pid(const char *applet, const char *pidfile); void cloexec_fds_from(int); +struct ready { + enum { + READY_NONE = 0, + READY_FD, + } type; + + int pipe[2]; + int fd; +}; + +struct ready ready_parse(const char *applet, const char *ready_string); +bool ready_wait(const char *applet, struct ready ready); + #endif diff --git a/src/start-stop-daemon/start-stop-daemon.c b/src/start-stop-daemon/start-stop-daemon.c index 5868dd377..e3a31e9ee 100644 --- a/src/start-stop-daemon/start-stop-daemon.c +++ b/src/start-stop-daemon/start-stop-daemon.c @@ -89,6 +89,7 @@ enum { LONGOPT_SCHEDULER, LONGOPT_SCHEDULER_PRIO, LONGOPT_SECBITS, + LONGOPT_READY, }; const char *applet = NULL; @@ -130,6 +131,7 @@ const struct option longopts[] = { { "progress", 0, NULL, 'P'}, { "scheduler", 1, NULL, LONGOPT_SCHEDULER}, { "scheduler-priority", 1, NULL, LONGOPT_SCHEDULER_PRIO}, + { "ready", 1, NULL, LONGOPT_READY}, longopts_COMMON }; const char * const longopts_help[] = { @@ -353,6 +355,8 @@ int main(int argc, char **argv) int pipefd[2]; char readbuf[1]; ssize_t ss; + struct ready ready; + int ret = EXIT_SUCCESS; applet = basename_c(argv[0]); atexit(cleanup); @@ -618,6 +622,10 @@ int main(int argc, char **argv) sscanf(optarg, "%d", &sched_prio); break; + case LONGOPT_READY: + ready = ready_parse(applet, optarg); + break; + case_RC_COMMON_GETOPT } @@ -1100,6 +1108,11 @@ int main(int argc, char **argv) cloexec_fds_from(3); + if (ready.type == READY_FD) { + close(ready.pipe[0]); + dup2(ready.pipe[1], ready.fd); + } + if (scheduler != NULL) { int scheduler_index; struct sched_param sched = {.sched_priority = sched_prio}; @@ -1184,7 +1197,11 @@ int main(int argc, char **argv) start_wait = 0; } - if (start_wait > 0) { + /* TODO: timeout on waiting readiness. */ + if (ready.type != READY_NONE) { + if (!ready_wait(applet, ready)) + ret = EXIT_FAILURE; + } else if (start_wait > 0) { struct timespec ts; bool alive = false; @@ -1224,6 +1241,6 @@ int main(int argc, char **argv) rc_service_daemon_set(svcname, exec, (const char *const *)margv, pidfile, true); - exit(EXIT_SUCCESS); + exit(ret); /* NOTREACHED */ } diff --git a/src/supervise-daemon/supervise-daemon.c b/src/supervise-daemon/supervise-daemon.c index ed60f5e46..e4f0b805d 100644 --- a/src/supervise-daemon/supervise-daemon.c +++ b/src/supervise-daemon/supervise-daemon.c @@ -82,6 +82,7 @@ enum { LONGOPT_SECBITS, LONGOPT_STDERR_LOGGER, LONGOPT_STDOUT_LOGGER, + LONGOPT_READY, }; const char *applet = NULL; @@ -116,6 +117,7 @@ const struct option longopts[] = { { "stdout-logger",1, NULL, LONGOPT_STDOUT_LOGGER}, { "stderr-logger",1, NULL, LONGOPT_STDERR_LOGGER}, { "reexec", 0, NULL, '3'}, + { "ready", 1, NULL, LONGOPT_READY}, longopts_COMMON }; const char * const longopts_help[] = { @@ -165,6 +167,7 @@ static int devnull_fd = -1; static int stdin_fd; static int stdout_fd; static int stderr_fd; +static struct ready ready; static char *redirect_stderr = NULL; static char *redirect_stdout = NULL; static char *stderr_process = NULL; @@ -588,6 +591,9 @@ RC_NORETURN static void child_process(char *exec, char **argv) cloexec_fds_from(3); + if (ready.type == READY_FD) + dup2(ready.pipe[1], ready.fd); + cmdline = make_cmdline(argv); syslog(LOG_INFO, "Child command line: %s", cmdline); free(cmdline); @@ -1068,6 +1074,10 @@ int main(int argc, char **argv) stderr_process = optarg; break; + case LONGOPT_READY: + ready = ready_parse(applet, optarg); + break; + case_RC_COMMON_GETOPT } @@ -1205,8 +1215,8 @@ int main(int argc, char **argv) if (child_pid == -1) eerrorx("%s: fork: %s", applet, strerror(errno)); if (child_pid != 0) - /* first parent process, do nothing. */ - exit(EXIT_SUCCESS); + exit(ready_wait(applet, ready) ? EXIT_SUCCESS : EXIT_FAILURE); + #ifdef TIOCNOTTY tty_fd = open("/dev/tty", O_RDWR); #endif @@ -1214,10 +1224,16 @@ int main(int argc, char **argv) dup2(devnull_fd, STDIN_FILENO); dup2(devnull_fd, STDOUT_FILENO); dup2(devnull_fd, STDERR_FILENO); + + if (ready.type == READY_FD) + close(ready.pipe[0]); + child_pid = fork(); if (child_pid == -1) eerrorx("%s: fork: %s", applet, strerror(errno)); else if (child_pid != 0) { + if (ready.type == READY_FD) + close(ready.pipe[1]); c = argv; x = 0; while (c && *c) {