2020-07-15 12:39:10 +02:00
|
|
|
#include "spawn.h"
|
|
|
|
|
|
2021-01-31 14:40:27 +01:00
|
|
|
#include <string.h>
|
2020-07-15 12:39:10 +02:00
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <errno.h>
|
2021-02-21 11:57:38 +01:00
|
|
|
#include <signal.h>
|
2020-07-15 12:39:10 +02:00
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
|
|
#define LOG_MODULE "spawn"
|
|
|
|
|
#define LOG_ENABLE_DBG 0
|
|
|
|
|
#include "log.h"
|
2021-01-15 20:39:45 +00:00
|
|
|
#include "debug.h"
|
2021-01-31 14:40:27 +01:00
|
|
|
#include "xmalloc.h"
|
2020-07-15 12:39:10 +02:00
|
|
|
|
|
|
|
|
bool
|
2020-07-15 13:33:56 +02:00
|
|
|
spawn(struct reaper *reaper, const char *cwd, char *const argv[],
|
|
|
|
|
int stdin_fd, int stdout_fd, int stderr_fd)
|
2020-07-15 12:39:10 +02:00
|
|
|
{
|
|
|
|
|
int pipe_fds[2] = {-1, -1};
|
|
|
|
|
if (pipe2(pipe_fds, O_CLOEXEC) < 0) {
|
|
|
|
|
LOG_ERRNO("failed to create pipe");
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pid_t pid = fork();
|
|
|
|
|
if (pid < 0) {
|
|
|
|
|
LOG_ERRNO("failed to fork");
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pid == 0) {
|
|
|
|
|
/* Child */
|
|
|
|
|
close(pipe_fds[0]);
|
2020-07-15 13:33:56 +02:00
|
|
|
|
2021-02-25 21:09:59 +01:00
|
|
|
if (setsid() < 0)
|
|
|
|
|
goto child_err;
|
|
|
|
|
|
2021-02-21 11:57:38 +01:00
|
|
|
/* Clear signal mask */
|
|
|
|
|
sigset_t mask;
|
|
|
|
|
sigemptyset(&mask);
|
2021-02-25 21:09:59 +01:00
|
|
|
if (sigprocmask(SIG_SETMASK, &mask, NULL) < 0)
|
|
|
|
|
goto child_err;
|
2021-02-21 11:57:38 +01:00
|
|
|
|
2021-06-02 17:48:57 +02:00
|
|
|
/* Restore ignored (SIG_IGN) signals */
|
|
|
|
|
if (sigaction(SIGHUP, &(struct sigaction){.sa_handler = SIG_DFL}, NULL) < 0)
|
|
|
|
|
goto child_err;
|
|
|
|
|
|
2021-02-24 21:29:34 +01:00
|
|
|
bool close_stderr = stderr_fd >= 0;
|
|
|
|
|
bool close_stdout = stdout_fd >= 0 && stdout_fd != stderr_fd;
|
|
|
|
|
bool close_stdin = stdin_fd >= 0 && stdin_fd != stdout_fd && stdin_fd != stderr_fd;
|
|
|
|
|
|
|
|
|
|
if ((stdin_fd >= 0 && (dup2(stdin_fd, STDIN_FILENO) < 0
|
|
|
|
|
|| (close_stdin && close(stdin_fd) < 0))) ||
|
|
|
|
|
(stdout_fd >= 0 && (dup2(stdout_fd, STDOUT_FILENO) < 0
|
|
|
|
|
|| (close_stdout && close(stdout_fd) < 0))) ||
|
|
|
|
|
(stderr_fd >= 0 && (dup2(stderr_fd, STDERR_FILENO) < 0
|
|
|
|
|
|| (close_stderr && close(stderr_fd) < 0))) ||
|
2020-07-15 13:33:56 +02:00
|
|
|
(cwd != NULL && chdir(cwd) < 0) ||
|
2020-07-15 12:39:10 +02:00
|
|
|
execvp(argv[0], argv) < 0)
|
|
|
|
|
{
|
2021-02-25 21:09:59 +01:00
|
|
|
goto child_err;
|
2020-07-15 12:39:10 +02:00
|
|
|
}
|
2021-02-25 21:09:59 +01:00
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
xassert(false);
|
2020-07-15 12:39:10 +02:00
|
|
|
_exit(errno);
|
2021-02-25 21:09:59 +01:00
|
|
|
|
|
|
|
|
child_err:
|
|
|
|
|
;
|
|
|
|
|
const int errno_copy = errno;
|
|
|
|
|
(void)!write(pipe_fds[1], &errno_copy, sizeof(errno_copy));
|
|
|
|
|
_exit(errno_copy);
|
2020-07-15 12:39:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Parent */
|
|
|
|
|
close(pipe_fds[1]);
|
|
|
|
|
|
2021-02-21 20:33:07 +01:00
|
|
|
int errno_copy;
|
|
|
|
|
static_assert(sizeof(errno_copy) == sizeof(errno), "errno size mismatch");
|
2020-07-15 12:39:10 +02:00
|
|
|
|
2021-02-21 20:33:07 +01:00
|
|
|
ssize_t ret = read(pipe_fds[0], &errno_copy, sizeof(errno_copy));
|
2020-07-15 12:39:10 +02:00
|
|
|
close(pipe_fds[0]);
|
|
|
|
|
|
|
|
|
|
if (ret == 0) {
|
2020-12-26 01:26:54 +01:00
|
|
|
reaper_add(reaper, pid, NULL, NULL);
|
2020-07-15 12:39:10 +02:00
|
|
|
return true;
|
|
|
|
|
} else if (ret < 0) {
|
|
|
|
|
LOG_ERRNO("failed to read from pipe");
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
2021-02-21 20:33:07 +01:00
|
|
|
LOG_ERRNO_P(errno_copy, "%s: failed to spawn", argv[0]);
|
|
|
|
|
errno = errno_copy;
|
2020-07-15 12:39:10 +02:00
|
|
|
waitpid(pid, NULL, 0);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err:
|
|
|
|
|
if (pipe_fds[0] != -1)
|
|
|
|
|
close(pipe_fds[0]);
|
|
|
|
|
if (pipe_fds[1] != -1)
|
|
|
|
|
close(pipe_fds[1]);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-01-31 14:40:27 +01:00
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
spawn_expand_template(const struct config_spawn_template *template,
|
|
|
|
|
size_t key_count,
|
|
|
|
|
const char *key_names[static key_count],
|
|
|
|
|
const char *key_values[static key_count],
|
|
|
|
|
size_t *argc, char ***argv)
|
|
|
|
|
{
|
|
|
|
|
*argc = 0;
|
|
|
|
|
*argv = NULL;
|
|
|
|
|
|
2021-06-18 16:18:41 +02:00
|
|
|
for (; template->argv.args[*argc] != NULL; (*argc)++)
|
2021-01-31 14:40:27 +01:00
|
|
|
;
|
|
|
|
|
|
|
|
|
|
#define append(s, n) \
|
|
|
|
|
do { \
|
|
|
|
|
expanded = xrealloc(expanded, len + (n) + 1); \
|
|
|
|
|
memcpy(&expanded[len], s, n); \
|
|
|
|
|
len += n; \
|
|
|
|
|
expanded[len] = '\0'; \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
*argv = malloc((*argc + 1) * sizeof((*argv)[0]));
|
|
|
|
|
|
|
|
|
|
/* Expand the provided keys */
|
|
|
|
|
for (size_t i = 0; i < *argc; i++) {
|
|
|
|
|
size_t len = 0;
|
|
|
|
|
char *expanded = NULL;
|
|
|
|
|
|
|
|
|
|
char *start = NULL;
|
2021-06-18 16:18:41 +02:00
|
|
|
char *last_end = template->argv.args[i];
|
2021-01-31 14:40:27 +01:00
|
|
|
|
|
|
|
|
while ((start = strstr(last_end, "${")) != NULL) {
|
|
|
|
|
/* Append everything from the last template's end to this
|
|
|
|
|
* one's beginning */
|
|
|
|
|
append(last_end, start - last_end);
|
|
|
|
|
|
|
|
|
|
/* Find end of template */
|
|
|
|
|
start += 2;
|
|
|
|
|
char *end = strstr(start, "}");
|
|
|
|
|
|
|
|
|
|
if (end == NULL) {
|
|
|
|
|
/* Ensure final append() copies the unclosed '${' */
|
|
|
|
|
last_end = start - 2;
|
|
|
|
|
LOG_WARN("notify: unclosed template: %s", last_end);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Expand template */
|
|
|
|
|
bool valid_key = false;
|
|
|
|
|
for (size_t j = 0; j < key_count; j++) {
|
|
|
|
|
if (strncmp(start, key_names[j], end - start) != 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
append(key_values[j], strlen(key_values[j]));
|
|
|
|
|
valid_key = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!valid_key) {
|
|
|
|
|
/* Unrecognized template - append it as-is */
|
|
|
|
|
start -= 2;
|
|
|
|
|
append(start, end + 1 - start);
|
|
|
|
|
LOG_WARN("notify: unrecognized template: %.*s",
|
|
|
|
|
(int)(end + 1 - start), start);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
last_end = end + 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-18 16:18:41 +02:00
|
|
|
append(
|
|
|
|
|
last_end,
|
|
|
|
|
template->argv.args[i] + strlen(template->argv.args[i]) - last_end);
|
2021-01-31 14:40:27 +01:00
|
|
|
(*argv)[i] = expanded;
|
|
|
|
|
}
|
|
|
|
|
(*argv)[*argc] = NULL;
|
|
|
|
|
|
|
|
|
|
#undef append
|
|
|
|
|
return true;
|
|
|
|
|
}
|