In daemon mode, we must acquire the semaphore lock in the child process
because the child does not inherit semaphore adjustments. Currently
the parent exits successfully after the fork, so the command appears
to succeed even if the child dies immediately because it was unable
to acquire the lock because another dss process is holding the lock.
This commit introduces a mechanism which enables the parent to tell
whether the child completed its setup successfully. We create a
pipe prior to calling fork(2), and let the child write to one end
of the pipe after setup is complete and just before it enters the
main select loop. The parent reads from the other end of the pipe
and exits once the read(2) call returns. If the child dies early,
read(2) returns zero, indicating failure.
*
* \sa fork(2), setsid(2), dup(2).
*/
*
* \sa fork(2), setsid(2), dup(2).
*/
DSS_INFO_LOG(("daemonizing\n"));
DSS_INFO_LOG(("daemonizing\n"));
+ if (pipe(fd) < 0)
+ goto err;
pid = fork();
if (pid < 0)
goto err;
pid = fork();
if (pid < 0)
goto err;
- if (pid)
- exit(EXIT_SUCCESS); /* parent exits */
+ if (pid) {
+ /*
+ * The parent process exits once it has received one byte from
+ * the reading end of the pipe. If the child exits before it
+ * was able to complete its setup (acquire the lock on the
+ * semaphore), the read() below will return zero. In this case
+ * we let the parent die unsuccessfully.
+ */
+ char c;
+ int ret;
+ close(fd[1]);
+ ret = read(fd[0], &c, 1);
+ if (ret <= 0) {
+ DSS_EMERG_LOG(("child terminated unexpectedly\n"));
+ exit(EXIT_FAILURE);
+ }
+ exit(EXIT_SUCCESS);
+ }
+ close(fd[0]);
/* become session leader */
if (setsid() < 0)
goto err;
/* become session leader */
if (setsid() < 0)
goto err;
if (dup2(null, STDERR_FILENO) < 0)
goto err;
close(null);
if (dup2(null, STDERR_FILENO) < 0)
goto err;
close(null);
err:
DSS_EMERG_LOG(("fatal: %s\n", strerror(errno)));
exit(EXIT_FAILURE);
err:
DSS_EMERG_LOG(("fatal: %s\n", strerror(errno)));
exit(EXIT_FAILURE);
/** \file daemon.h exported symbols from daemon.c */
/** \file daemon.h exported symbols from daemon.c */
FILE *open_log(const char *logfile_name);
void close_log(FILE* logfile);
void log_welcome(int loglevel);
FILE *open_log(const char *logfile_name);
void close_log(FILE* logfile);
void log_welcome(int loglevel);
static int com_run(void)
{
static int com_run(void)
{
if (OPT_GIVEN(DSS, DRY_RUN)) {
DSS_ERROR_LOG(("dry run not supported by this command\n"));
return -E_SYNTAX;
}
if (OPT_GIVEN(RUN, DAEMON)) {
if (OPT_GIVEN(DSS, DRY_RUN)) {
DSS_ERROR_LOG(("dry run not supported by this command\n"));
return -E_SYNTAX;
}
if (OPT_GIVEN(RUN, DAEMON)) {
daemonized = true;
logfile = open_log(OPT_STRING_VAL(RUN, LOGFILE));
}
daemonized = true;
logfile = open_log(OPT_STRING_VAL(RUN, LOGFILE));
}
ret = install_sighandler(SIGHUP);
if (ret < 0)
return ret;
ret = install_sighandler(SIGHUP);
if (ret < 0)
return ret;
+ if (fd >= 0) {
+ ret = write(fd, "\0", 1);
+ if (ret != 1) {
+ DSS_ERROR_LOG(("write to daemon pipe returned %d\n",
+ ret));
+ if (ret < 0)
+ return -ERRNO_TO_DSS_ERROR(errno);
+ return -E_BUG;
+ }
+ }
ret = select_loop();
if (ret >= 0) /* impossible */
ret = -E_BUG;
ret = select_loop();
if (ret >= 0) /* impossible */
ret = -E_BUG;