openrc-run: fix rc_parallel race in svc_exec

svc_exec waits until SIGCHLD comes in to close its input, but in
rc_parallel case the SIGCHLD might be unrelated.

Checking the proper pid is found in signal handler and only signaling
signal_pipe the status code directly avoids this problem.
This commit is contained in:
Dominique Martinet 2023-02-02 13:11:59 +09:00 committed by William Hubbs
parent 0b9c3c0803
commit 863e1a5c87

View File

@ -107,7 +107,8 @@ static RC_STRINGLIST *deptypes_mwua; /* need+want+use+after deps for stopping */
static void static void
handle_signal(int sig) handle_signal(int sig)
{ {
int serrno = errno; int serrno = errno, status;
pid_t pid;
const char *signame = NULL; const char *signame = NULL;
struct winsize ws; struct winsize ws;
@ -117,12 +118,13 @@ handle_signal(int sig)
break; break;
case SIGCHLD: case SIGCHLD:
if (signal_pipe[1] > -1) { while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
if (write(signal_pipe[1], &sig, sizeof(sig)) == -1) if (signal_pipe[1] > -1 && pid == service_pid) {
eerror("%s: send: %s", if (write(signal_pipe[1], &status, sizeof(status)) == -1)
service, strerror(errno)); eerror("%s: send: %s",
} else service, strerror(errno));
rc_waitpid(-1); }
}
break; break;
case SIGWINCH: case SIGWINCH:
@ -438,6 +440,7 @@ svc_exec(const char *arg1, const char *arg2)
if (errno != EINTR) { if (errno != EINTR) {
eerror("%s: poll: %s", eerror("%s: poll: %s",
service, strerror(errno)); service, strerror(errno));
ret = -1;
break; break;
} }
} }
@ -448,9 +451,20 @@ svc_exec(const char *arg1, const char *arg2)
write_prefix(buffer, bytes, &prefixed); write_prefix(buffer, bytes, &prefixed);
} }
/* Only SIGCHLD signals come down this pipe */ /* signal_pipe receives service_pid's exit status */
if (fd[0].revents & (POLLIN | POLLHUP)) if (fd[0].revents & (POLLIN | POLLHUP)) {
if ((s = read(signal_pipe[0], &ret, sizeof(ret))) != sizeof(ret)) {
eerror("%s: receive failed: %s", service,
s < 0 ? strerror(errno) : "short read");
ret = -1;
break;
}
ret = WEXITSTATUS(ret);
if (ret != 0 && errno == ECHILD)
/* killall5 -9 could cause this */
ret = 0;
break; break;
}
} }
} }
@ -473,11 +487,6 @@ svc_exec(const char *arg1, const char *arg2)
master_tty = -1; master_tty = -1;
} }
ret = rc_waitpid(service_pid);
ret = WEXITSTATUS(ret);
if (ret != 0 && errno == ECHILD)
/* killall5 -9 could cause this */
ret = 0;
service_pid = 0; service_pid = 0;
return ret; return ret;