diff --git a/TODO b/TODO index b181dbf09c..251ce4f8ec 100644 --- a/TODO +++ b/TODO @@ -24,6 +24,13 @@ Janitorial Clean-ups: Features: +* be stricter with fds we receive for the fdstore: close them asynchronously + +* be stricter with pid file and notify pids: don't allow them to be outside of cgroup, except if sender is privileged + +* calenderspec: add support for week numbers and day numbers within a + year. This would allow us to define "bi-weekly" triggers safely. + * add support for recursive bpf firewalling as supported by the newest kernel * add bpf-based implementation of devices cgroup controller logic for compat with cgroupsv2 as supported by newest kernel @@ -49,8 +56,6 @@ Features: the runtime dir as we maintain for the fdstore: i.e. keep it around as long as the unit is running or has a job queued. -* hook up sd-bus' creds stuff with SO_PEERGROUPS - * add async version of sd_bus_add_match and make use of that * support projid-based quota in machinectl for containers, and then drop diff --git a/src/activate/activate.c b/src/activate/activate.c index 67067a8f32..c07dcb8626 100644 --- a/src/activate/activate.c +++ b/src/activate/activate.c @@ -274,9 +274,9 @@ static int fork_and_exec_process(const char* child, char** argv, char **env, int if (!joined) return log_oom(); - r = safe_fork("(activate)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child_pid); + r = safe_fork("(activate)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &child_pid); if (r < 0) - return log_error_errno(r, "Failed to fork: %m"); + return r; if (r == 0) { /* In the child */ exec_process(child, argv, env, fd, 1); diff --git a/src/basic/async.c b/src/basic/async.c index ccd9eeef52..b6c6d6a80b 100644 --- a/src/basic/async.c +++ b/src/basic/async.c @@ -32,32 +32,52 @@ #include "util.h" int asynchronous_job(void* (*func)(void *p), void *arg) { + sigset_t ss, saved_ss; pthread_attr_t a; pthread_t t; - int r; + int r, k; - /* It kinda sucks that we have to resort to threads to - * implement an asynchronous sync(), but well, such is - * life. - * - * Note that issuing this command right before exiting a - * process will cause the process to wait for the sync() to - * complete. This function hence is nicely asynchronous really - * only in long running processes. */ + /* It kinda sucks that we have to resort to threads to implement an asynchronous close(), but well, such is + * life. */ r = pthread_attr_init(&a); if (r > 0) return -r; r = pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED); - if (r > 0) + if (r > 0) { + r = -r; goto finish; + } + + if (sigfillset(&ss) < 0) { + r = -errno; + goto finish; + } + + /* Block all signals before forking off the thread, so that the new thread is started with all signals + * blocked. This way the existence of the new thread won't affect signal handling in other threads. */ + + r = pthread_sigmask(SIG_BLOCK, &ss, &saved_ss); + if (r > 0) { + r = -r; + goto finish; + } r = pthread_create(&t, &a, func, arg); + k = pthread_sigmask(SIG_SETMASK, &saved_ss, NULL); + + if (r > 0) + r = -r; + else if (k > 0) + r = -k; + else + r = 0; + finish: pthread_attr_destroy(&a); - return -r; + return r; } int asynchronous_sync(pid_t *ret_pid) { diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c index dbd7fb1d76..d1ba1681fb 100644 --- a/src/basic/calendarspec.c +++ b/src/basic/calendarspec.c @@ -156,6 +156,11 @@ static void fix_year(CalendarComponent *c) { int calendar_spec_normalize(CalendarSpec *c) { assert(c); + if (streq_ptr(c->timezone, "UTC")) { + c->utc = true; + c->timezone = mfree(c->timezone); + } + if (c->weekdays_bits <= 0 || c->weekdays_bits >= BITS_WEEKDAYS) c->weekdays_bits = -1; @@ -1349,7 +1354,6 @@ typedef struct SpecNextResult { int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) { SpecNextResult *shared, tmp; - pid_t pid; int r; if (isempty(spec->timezone)) @@ -1359,7 +1363,7 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) if (shared == MAP_FAILED) return negative_errno(); - r = safe_fork("(sd-calendar)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG, &pid); + r = safe_fork("(sd-calendar)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL); if (r < 0) { (void) munmap(shared, sizeof *shared); return r; @@ -1377,14 +1381,8 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) _exit(EXIT_SUCCESS); } - r = wait_for_terminate(pid, NULL); - if (r < 0) { - (void) munmap(shared, sizeof *shared); - return r; - } - tmp = *shared; - if (munmap(shared, sizeof *shared) != 0) + if (munmap(shared, sizeof *shared) < 0) return negative_errno(); if (tmp.return_value == 0) diff --git a/src/basic/exec-util.c b/src/basic/exec-util.c index afad636db5..a97295a509 100644 --- a/src/basic/exec-util.c +++ b/src/basic/exec-util.c @@ -55,9 +55,9 @@ static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) { return 0; } - r = safe_fork("(direxec)", FORK_DEATHSIG, &_pid); + r = safe_fork("(direxec)", FORK_DEATHSIG|FORK_LOG, &_pid); if (r < 0) - return log_error_errno(r, "Failed to fork: %m"); + return r; if (r == 0) { char *_argv[2]; @@ -82,7 +82,6 @@ static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) { _exit(EXIT_FAILURE); } - log_debug("Spawned %s as " PID_FMT ".", path, _pid); *pid = _pid; return 1; } @@ -147,7 +146,7 @@ static int do_execute( return log_oom(); t = NULL; } else { - r = wait_for_terminate_and_warn(t, pid, true); + r = wait_for_terminate_and_check(t, pid, WAIT_LOG); if (r < 0) continue; @@ -177,7 +176,7 @@ static int do_execute( t = hashmap_remove(pids, PID_TO_PTR(pid)); assert(t); - wait_for_terminate_and_warn(t, pid, true); + (void) wait_for_terminate_and_check(t, pid, WAIT_LOG); } return 0; @@ -190,10 +189,9 @@ int execute_directories( void* const callback_args[_STDOUT_CONSUME_MAX], char *argv[]) { - pid_t executor_pid; - char *name; char **dirs = (char**) directories; _cleanup_close_ int fd = -1; + char *name; int r; assert(!strv_isempty(dirs)); @@ -216,23 +214,14 @@ int execute_directories( * them to finish. Optionally a timeout is applied. If a file with the same name * exists in more than one directory, the earliest one wins. */ - r = safe_fork("(sd-executor)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &executor_pid); + r = safe_fork("(sd-executor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); if (r < 0) - return log_error_errno(r, "Failed to fork: %m"); + return r; if (r == 0) { r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv); _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); } - r = wait_for_terminate_and_warn(name, executor_pid, true); - if (r < 0) - return log_error_errno(r, "Execution failed: %m"); - if (r > 0) { - /* non-zero return code from child */ - log_error("Forker process failed."); - return -EREMOTEIO; - } - if (!callbacks) return 0; diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 71c404bdd0..430851cc8c 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -62,16 +62,28 @@ int write_string_stream_ts( WriteStringFileFlags flags, struct timespec *ts) { + bool needs_nl; + assert(f); assert(line); if (ferror(f)) return -EIO; + needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n"); + + if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) { + /* If STDIO buffering was disabled, then let's append the newline character to the string itself, so + * that the write goes out in one go, instead of two */ + + line = strjoina(line, "\n"); + needs_nl = false; + } + if (fputs(line, f) == EOF) return -errno; - if (!(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n")) + if (needs_nl) if (fputc('\n', f) == EOF) return -errno; @@ -914,14 +926,16 @@ int write_env_file(const char *fname, char **l) { } int executable_is_script(const char *path, char **interpreter) { - int r; _cleanup_free_ char *line = NULL; - int len; + size_t len; char *ans; + int r; assert(path); r = read_one_line_file(path, &line); + if (r == -ENOBUFS) /* First line overly long? if so, then it's not a script */ + return 0; if (r < 0) return r; diff --git a/src/basic/missing.h b/src/basic/missing.h index a6b10b16bb..1280e6c410 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -621,6 +621,10 @@ struct btrfs_ioctl_quota_ctl_args { # define SO_REUSEPORT 15 #endif +#ifndef SO_PEERGROUPS +# define SO_PEERGROUPS 59 +#endif + #ifndef EVIOCREVOKE # define EVIOCREVOKE _IOW('E', 0x91, int) #endif diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 7da22f4a9d..d74813dada 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -687,32 +687,43 @@ int wait_for_terminate(pid_t pid, siginfo_t *status) { * A warning is emitted if the process terminates abnormally, * and also if it returns non-zero unless check_exit_code is true. */ -int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code) { - int r; +int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) { + _cleanup_free_ char *buffer = NULL; siginfo_t status; + int r, prio; - assert(name); assert(pid > 1); + if (!name) { + r = get_process_comm(pid, &buffer); + if (r < 0) + log_debug_errno(r, "Failed to acquire process name of " PID_FMT ", ignoring: %m", pid); + else + name = buffer; + } + + prio = flags & WAIT_LOG_ABNORMAL ? LOG_ERR : LOG_DEBUG; + r = wait_for_terminate(pid, &status); if (r < 0) - return log_warning_errno(r, "Failed to wait for %s: %m", name); + return log_full_errno(prio, r, "Failed to wait for %s: %m", strna(name)); if (status.si_code == CLD_EXITED) { - if (status.si_status != 0) - log_full(check_exit_code ? LOG_WARNING : LOG_DEBUG, - "%s failed with error code %i.", name, status.si_status); + if (status.si_status != EXIT_SUCCESS) + log_full(flags & WAIT_LOG_NON_ZERO_EXIT_STATUS ? LOG_ERR : LOG_DEBUG, + "%s failed with exit status %i.", strna(name), status.si_status); else log_debug("%s succeeded.", name); return status.si_status; + } else if (IN_SET(status.si_code, CLD_KILLED, CLD_DUMPED)) { - log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status)); + log_full(prio, "%s terminated by signal %s.", strna(name), signal_to_string(status.si_status)); return -EPROTO; } - log_warning("%s failed due to unknown reason.", name); + log_full(prio, "%s failed due to unknown reason.", strna(name)); return -EPROTO; } @@ -1083,7 +1094,7 @@ int ioprio_parse_priority(const char *s, int *ret) { static pid_t cached_pid = CACHED_PID_UNSET; -static void reset_cached_pid(void) { +void reset_cached_pid(void) { /* Invoked in the child after a fork(), i.e. at the first moment the PID changed */ cached_pid = CACHED_PID_UNSET; } @@ -1150,46 +1161,72 @@ int safe_fork_full( pid_t *ret_pid) { pid_t original_pid, pid; - sigset_t saved_ss; + sigset_t saved_ss, ss; bool block_signals; - int r; + int prio, r; /* A wrapper around fork(), that does a couple of important initializations in addition to mere forking. Always * returns the child's PID in *ret_pid. Returns == 0 in the child, and > 0 in the parent. */ + prio = flags & FORK_LOG ? LOG_ERR : LOG_DEBUG; + original_pid = getpid_cached(); - block_signals = flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG); + if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG)) { - if (block_signals) { - sigset_t ss; + /* We temporarily block all signals, so that the new child has them blocked initially. This way, we can + * be sure that SIGTERMs are not lost we might send to the child. */ - /* We temporarily block all signals, so that the new child has them blocked initially. This way, we can be sure - * that SIGTERMs are not lost we might send to the child. */ if (sigfillset(&ss) < 0) - return log_debug_errno(errno, "Failed to reset signal set: %m"); + return log_full_errno(prio, errno, "Failed to reset signal set: %m"); - if (sigprocmask(SIG_SETMASK, &ss, &saved_ss) < 0) - return log_debug_errno(errno, "Failed to reset signal mask: %m"); + block_signals = true; + + } else if (flags & FORK_WAIT) { + + /* Let's block SIGCHLD at least, so that we can safely watch for the child process */ + + if (sigemptyset(&ss) < 0) + return log_full_errno(prio, errno, "Failed to clear signal set: %m"); + + if (sigaddset(&ss, SIGCHLD) < 0) + return log_full_errno(prio, errno, "Failed to add SIGCHLD to signal set: %m"); + + block_signals = true; } - pid = fork(); + if (block_signals) + if (sigprocmask(SIG_SETMASK, &ss, &saved_ss) < 0) + return log_full_errno(prio, errno, "Failed to set signal mask: %m"); + + if (flags & FORK_NEW_MOUNTNS) + pid = raw_clone(SIGCHLD|CLONE_NEWNS); + else + pid = fork(); if (pid < 0) { r = -errno; if (block_signals) /* undo what we did above */ (void) sigprocmask(SIG_SETMASK, &saved_ss, NULL); - return log_debug_errno(r, "Failed to fork: %m"); + return log_full_errno(prio, r, "Failed to fork: %m"); } if (pid > 0) { /* We are in the parent process */ + log_debug("Successfully forked off '%s' as PID " PID_FMT ".", strna(name), pid); + + if (flags & FORK_WAIT) { + r = wait_for_terminate_and_check(name, pid, (flags & FORK_LOG ? WAIT_LOG : 0)); + if (r < 0) + return r; + if (r != EXIT_SUCCESS) /* exit status > 0 should be treated as failure, too */ + return -EPROTO; + } + if (block_signals) /* undo what we did above */ (void) sigprocmask(SIG_SETMASK, &saved_ss, NULL); - log_debug("Sucessfully forked off '%s' as PID " PID_FMT ".", strna(name), pid); - if (ret_pid) *ret_pid = pid; @@ -1207,31 +1244,32 @@ int safe_fork_full( if (name) { r = rename_process(name); if (r < 0) - log_debug_errno(r, "Failed to rename process, ignoring: %m"); + log_full_errno(flags & FORK_LOG ? LOG_WARNING : LOG_DEBUG, + r, "Failed to rename process, ignoring: %m"); } if (flags & FORK_DEATHSIG) if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) { - log_debug_errno(errno, "Failed to set death signal: %m"); + log_full_errno(prio, errno, "Failed to set death signal: %m"); _exit(EXIT_FAILURE); } if (flags & FORK_RESET_SIGNALS) { r = reset_all_signal_handlers(); if (r < 0) { - log_debug_errno(r, "Failed to reset signal handlers: %m"); + log_full_errno(prio, r, "Failed to reset signal handlers: %m"); _exit(EXIT_FAILURE); } /* This implicitly undoes the signal mask stuff we did before the fork()ing above */ r = reset_signal_mask(); if (r < 0) { - log_debug_errno(r, "Failed to reset signal mask: %m"); + log_full_errno(prio, r, "Failed to reset signal mask: %m"); _exit(EXIT_FAILURE); } } else if (block_signals) { /* undo what we did above */ if (sigprocmask(SIG_SETMASK, &saved_ss, NULL) < 0) { - log_debug_errno(errno, "Failed to restore signal mask: %m"); + log_full_errno(prio, errno, "Failed to restore signal mask: %m"); _exit(EXIT_FAILURE); } } @@ -1257,7 +1295,7 @@ int safe_fork_full( r = close_all_fds(except_fds, n_except_fds); if (r < 0) { - log_debug_errno(r, "Failed to close all file descriptors: %m"); + log_full_errno(prio, r, "Failed to close all file descriptors: %m"); _exit(EXIT_FAILURE); } } @@ -1271,7 +1309,7 @@ int safe_fork_full( if (flags & FORK_NULL_STDIO) { r = make_null_stdio(); if (r < 0) { - log_debug_errno(r, "Failed to connect stdin/stdout to /dev/null: %m"); + log_full_errno(prio, r, "Failed to connect stdin/stdout to /dev/null: %m"); _exit(EXIT_FAILURE); } } diff --git a/src/basic/process-util.h b/src/basic/process-util.h index b20e527af7..fdb1790b2e 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -61,7 +61,16 @@ int get_process_environ(pid_t pid, char **environ); int get_process_ppid(pid_t pid, pid_t *ppid); int wait_for_terminate(pid_t pid, siginfo_t *status); -int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code); + +typedef enum WaitFlags { + WAIT_LOG_ABNORMAL = 1U << 0, + WAIT_LOG_NON_ZERO_EXIT_STATUS = 1U << 1, + + /* A shortcut for requesting the most complete logging */ + WAIT_LOG = WAIT_LOG_ABNORMAL|WAIT_LOG_NON_ZERO_EXIT_STATUS, +} WaitFlags; + +int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags); int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout); void sigkill_wait(pid_t pid); @@ -147,6 +156,7 @@ static inline int sched_policy_to_string_alloc_with_check(int n, char **s) { int ioprio_parse_priority(const char *s, int *ret); pid_t getpid_cached(void); +void reset_cached_pid(void); int must_be_root(void); @@ -156,6 +166,9 @@ typedef enum ForkFlags { FORK_DEATHSIG = 1U << 2, FORK_NULL_STDIO = 1U << 3, FORK_REOPEN_LOG = 1U << 4, + FORK_LOG = 1U << 5, + FORK_WAIT = 1U << 6, + FORK_NEW_MOUNTNS = 1U << 7, } ForkFlags; int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid); diff --git a/src/basic/raw-clone.h b/src/basic/raw-clone.h index f01b73a8fe..8c95380305 100644 --- a/src/basic/raw-clone.h +++ b/src/basic/raw-clone.h @@ -30,28 +30,27 @@ * raw_clone() - uses clone to create a new process with clone flags * @flags: Flags to pass to the clone system call * - * Uses the clone system call to create a new process with the cloning - * flags and termination signal passed in the flags parameter. Opposed - * to glibc's clone funtion, using this function does not set up a - * separate stack for the child, but relies on copy-on-write semantics - * on the one stack at a common virtual address, just as fork does. + * Uses the clone system call to create a new process with the cloning flags and termination signal passed in the flags + * parameter. Opposed to glibc's clone funtion, using this function does not set up a separate stack for the child, but + * relies on copy-on-write semantics on the one stack at a common virtual address, just as fork does. * - * To obtain copy-on-write semantics, flags must not contain CLONE_VM, - * and thus CLONE_THREAD and CLONE_SIGHAND (which require CLONE_VM) are - * not usabale. - * Additionally, as this function does not pass the ptid, newtls and ctid - * parameters to the kernel, flags must not contain CLONE_PARENT_SETTID, - * CLONE_CHILD_SETTID, CLONE_CHILD_CLEARTID or CLONE_SETTLS. + * To obtain copy-on-write semantics, flags must not contain CLONE_VM, and thus CLONE_THREAD and CLONE_SIGHAND (which + * require CLONE_VM) are not usable. + * + * Additionally, as this function does not pass the ptid, newtls and ctid parameters to the kernel, flags must not + * contain CLONE_PARENT_SETTID, CLONE_CHILD_SETTID, CLONE_CHILD_CLEARTID or CLONE_SETTLS. * * Returns: 0 in the child process and the child process id in the parent. */ -static inline int raw_clone(unsigned long flags) { +static inline pid_t raw_clone(unsigned long flags) { + pid_t ret; + assert((flags & (CLONE_VM|CLONE_PARENT_SETTID|CLONE_CHILD_SETTID| CLONE_CHILD_CLEARTID|CLONE_SETTLS)) == 0); #if defined(__s390x__) || defined(__s390__) || defined(__CRIS__) /* On s390/s390x and cris the order of the first and second arguments * of the raw clone() system call is reversed. */ - return (int) syscall(__NR_clone, NULL, flags); + ret = (pid_t) syscall(__NR_clone, NULL, flags); #elif defined(__sparc__) && defined(__arch64__) { /** @@ -60,8 +59,8 @@ static inline int raw_clone(unsigned long flags) { * %o1. Inline assembly is needed to get the flag returned * in %o1. */ - int in_child; - int child_pid; + int in_child, child_pid; + asm volatile("mov %2, %%g1\n\t" "mov %3, %%o0\n\t" "mov 0 , %%o1\n\t" @@ -71,12 +70,15 @@ static inline int raw_clone(unsigned long flags) { "=r"(in_child), "=r"(child_pid) : "i"(__NR_clone), "r"(flags) : "%o1", "%o0", "%g1" ); - if (in_child) - return 0; - else - return child_pid; + + ret = in_child ? 0 : child_pid; } #else - return (int) syscall(__NR_clone, flags, NULL); + ret = (pid_t) syscall(__NR_clone, flags, NULL); #endif + + if (ret == 0) + reset_cached_pid(); + + return ret; } diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index 54d526a521..cb10a1dd07 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -967,58 +967,80 @@ int getpeercred(int fd, struct ucred *ucred) { if (n != sizeof(struct ucred)) return -EIO; - /* Check if the data is actually useful and not suppressed due - * to namespacing issues */ - if (u.pid <= 0) - return -ENODATA; - if (u.uid == UID_INVALID) - return -ENODATA; - if (u.gid == GID_INVALID) + /* Check if the data is actually useful and not suppressed due to namespacing issues */ + if (!pid_is_valid(u.pid)) return -ENODATA; + /* Note that we don't check UID/GID here, as namespace translation works differently there: instead of + * receiving in "invalid" user/group we get the overflow UID/GID. */ + *ucred = u; return 0; } int getpeersec(int fd, char **ret) { + _cleanup_free_ char *s = NULL; socklen_t n = 64; - char *s; - int r; assert(fd >= 0); assert(ret); - s = new0(char, n); - if (!s) - return -ENOMEM; + for (;;) { + s = new0(char, n+1); + if (!s) + return -ENOMEM; - r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n); - if (r < 0) { - free(s); + if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0) + break; if (errno != ERANGE) return -errno; - s = new0(char, n); - if (!s) - return -ENOMEM; - - r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n); - if (r < 0) { - free(s); - return -errno; - } + s = mfree(s); } - if (isempty(s)) { - free(s); + if (isempty(s)) return -EOPNOTSUPP; - } *ret = s; + s = NULL; + return 0; } +int getpeergroups(int fd, gid_t **ret) { + socklen_t n = sizeof(gid_t) * 64; + _cleanup_free_ gid_t *d = NULL; + + assert(fd); + assert(ret); + + for (;;) { + d = malloc(n); + if (!d) + return -ENOMEM; + + if (getsockopt(fd, SOL_SOCKET, SO_PEERGROUPS, d, &n) >= 0) + break; + + if (errno != ERANGE) + return -errno; + + d = mfree(d); + } + + assert_se(n % sizeof(gid_t) == 0); + n /= sizeof(gid_t); + + if ((socklen_t) (int) n != n) + return -E2BIG; + + *ret = d; + d = NULL; + + return (int) n; +} + int send_one_fd_sa( int transport_fd, int fd, diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index ba5be9b67d..83af91dbef 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -138,6 +138,7 @@ bool address_label_valid(const char *p); int getpeercred(int fd, struct ucred *ucred); int getpeersec(int fd, char **ret); +int getpeergroups(int fd, gid_t **ret); int send_one_fd_sa(int transport_fd, int fd, diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index d15fa38207..42336e8fdf 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -1093,7 +1093,6 @@ int ptsname_namespace(int pty, char **ret) { int openpt_in_namespace(pid_t pid, int flags) { _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1; _cleanup_close_pair_ int pair[2] = { -1, -1 }; - siginfo_t si; pid_t child; int r; @@ -1133,10 +1132,10 @@ int openpt_in_namespace(pid_t pid, int flags) { pair[1] = safe_close(pair[1]); - r = wait_for_terminate(child, &si); + r = wait_for_terminate_and_check("(sd-openpt)", child, 0); if (r < 0) return r; - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + if (r != EXIT_SUCCESS) return -EIO; return receive_one_fd(pair[0], 0); @@ -1145,7 +1144,6 @@ int openpt_in_namespace(pid_t pid, int flags) { int open_terminal_in_namespace(pid_t pid, const char *name, int mode) { _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1; _cleanup_close_pair_ int pair[2] = { -1, -1 }; - siginfo_t si; pid_t child; int r; @@ -1180,10 +1178,10 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) { pair[1] = safe_close(pair[1]); - r = wait_for_terminate(child, &si); + r = wait_for_terminate_and_check("(sd-terminal)", child, 0); if (r < 0) return r; - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + if (r != EXIT_SUCCESS) return -EIO; return receive_one_fd(pair[0], 0); diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 95358f8e9f..cbb5285018 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -886,7 +886,6 @@ typedef struct ParseTimestampResult { int parse_timestamp(const char *t, usec_t *usec) { char *last_space, *tz = NULL; ParseTimestampResult *shared, tmp; - pid_t pid; int r; last_space = strrchr(t, ' '); @@ -900,7 +899,7 @@ int parse_timestamp(const char *t, usec_t *usec) { if (shared == MAP_FAILED) return negative_errno(); - r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG, &pid); + r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL); if (r < 0) { (void) munmap(shared, sizeof *shared); return r; @@ -928,12 +927,6 @@ int parse_timestamp(const char *t, usec_t *usec) { _exit(EXIT_SUCCESS); } - r = wait_for_terminate(pid, NULL); - if (r < 0) { - (void) munmap(shared, sizeof *shared); - return r; - } - tmp = *shared; if (munmap(shared, sizeof *shared) != 0) return negative_errno(); diff --git a/src/basic/user-util.c b/src/basic/user-util.c index abb0b76866..b99775c18f 100644 --- a/src/basic/user-util.c +++ b/src/basic/user-util.c @@ -358,8 +358,9 @@ char* gid_to_name(gid_t gid) { } int in_gid(gid_t gid) { + long ngroups_max; gid_t *gids; - int ngroups_max, r, i; + int r, i; if (getgid() == gid) return 1; @@ -373,7 +374,7 @@ int in_gid(gid_t gid) { ngroups_max = sysconf(_SC_NGROUPS_MAX); assert(ngroups_max > 0); - gids = alloca(sizeof(gid_t) * ngroups_max); + gids = newa(gid_t, ngroups_max); r = getgroups(ngroups_max, gids); if (r < 0) diff --git a/src/core/execute.c b/src/core/execute.c index 3f3d73272e..6b01d00d6e 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -1823,7 +1823,6 @@ static int setup_private_users(uid_t uid, gid_t gid) { _cleanup_close_ int unshare_ready_fd = -1; _cleanup_(sigkill_waitp) pid_t pid = 0; uint64_t c = 1; - siginfo_t si; ssize_t n; int r; @@ -1963,13 +1962,11 @@ static int setup_private_users(uid_t uid, gid_t gid) { if (n != 0) /* on success we should have read 0 bytes */ return -EIO; - r = wait_for_terminate(pid, &si); + r = wait_for_terminate_and_check("(sd-userns)", pid, 0); + pid = 0; if (r < 0) return r; - pid = 0; - - /* If something strange happened with the child, let's consider this fatal, too */ - if (si.si_code != CLD_EXITED || si.si_status != 0) + if (r != EXIT_SUCCESS) /* If something strange happened with the child, let's consider this fatal, too */ return -EIO; return 0; diff --git a/src/core/shutdown.c b/src/core/shutdown.c index efcc264039..ffab4de101 100644 --- a/src/core/shutdown.c +++ b/src/core/shutdown.c @@ -483,15 +483,10 @@ int main(int argc, char *argv[]) { if (!in_container) { /* We cheat and exec kexec to avoid doing all its work */ - pid_t pid; - log_info("Rebooting with kexec."); - r = safe_fork("(sd-kexec)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS, &pid); - if (r < 0) - log_error_errno(r, "Failed to fork: %m"); + r = safe_fork("(sd-kexec)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_WAIT, NULL); if (r == 0) { - const char * const args[] = { KEXEC, "-e", NULL }; @@ -502,7 +497,7 @@ int main(int argc, char *argv[]) { _exit(EXIT_FAILURE); } - (void) wait_for_terminate_and_warn("kexec", pid, true); + /* If we are still running, then the kexec can't have worked, let's fall through */ } cmd = RB_AUTOBOOT; diff --git a/src/core/socket.c b/src/core/socket.c index 8e796e9e2c..74cdebbe81 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -1554,7 +1554,7 @@ static int socket_address_listen_in_cgroup( fd = receive_one_fd(pair[0], 0); /* We synchronously wait for the helper, as it shouldn't be slow */ - r = wait_for_terminate_and_warn("listen-cgroup-helper", pid, false); + r = wait_for_terminate_and_check("(sd-listen)", pid, WAIT_LOG_ABNORMAL); if (r < 0) { safe_close(fd); return r; @@ -2898,7 +2898,7 @@ static int socket_accept_in_cgroup(Socket *s, SocketPort *p, int fd) { cfd = receive_one_fd(pair[0], 0); /* We synchronously wait for the helper, as it shouldn't be slow */ - r = wait_for_terminate_and_warn("accept-cgroup-helper", pid, false); + r = wait_for_terminate_and_check("(sd-accept)", pid, WAIT_LOG_ABNORMAL); if (r < 0) { safe_close(cfd); return r; diff --git a/src/core/umount.c b/src/core/umount.c index b39181639a..6b0100fb44 100644 --- a/src/core/umount.c +++ b/src/core/umount.c @@ -389,9 +389,9 @@ static int remount_with_timeout(MountPoint *m, char *options, int *n_failed) { * fork a child process and set a timeout. If the timeout * lapses, the assumption is that that particular remount * failed. */ - r = safe_fork("(sd-remount)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS, &pid); + r = safe_fork("(sd-remount)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid); if (r < 0) - return log_error_errno(r, "Failed to fork: %m"); + return r; if (r == 0) { log_info("Remounting '%s' read-only in with options '%s'.", m->path, options); @@ -423,9 +423,9 @@ static int umount_with_timeout(MountPoint *m, bool *changed) { * fork a child process and set a timeout. If the timeout * lapses, the assumption is that that particular umount * failed. */ - r = safe_fork("(sd-umount)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS, &pid); + r = safe_fork("(sd-umount)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid); if (r < 0) - return log_error_errno(r, "Failed to fork: %m"); + return r; if (r == 0) { log_info("Unmounting '%s'.", m->path); diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index a3f86aa550..96e4a3e7e2 100644 --- a/src/coredump/coredumpctl.c +++ b/src/coredump/coredumpctl.c @@ -885,7 +885,6 @@ static int run_gdb(sd_journal *j) { _cleanup_free_ char *exe = NULL, *path = NULL; bool unlink_path = false; const char *data; - siginfo_t st; size_t len; pid_t pid; int r; @@ -928,24 +927,16 @@ static int run_gdb(sd_journal *j) { /* Don't interfere with gdb and its handling of SIGINT. */ (void) ignore_signals(SIGINT, -1); - r = safe_fork("(gdb)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS, &pid); - if (r < 0) { - log_error_errno(r, "Failed to fork(): %m"); + r = safe_fork("(gdb)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid); + if (r < 0) goto finish; - } if (r == 0) { execlp("gdb", "gdb", exe, path, NULL); log_error_errno(errno, "Failed to invoke gdb: %m"); _exit(EXIT_FAILURE); } - r = wait_for_terminate(pid, &st); - if (r < 0) { - log_error_errno(r, "Failed to wait for gdb: %m"); - goto finish; - } - - r = st.si_code == CLD_EXITED ? st.si_status : 255; + r = wait_for_terminate_and_check("gdb", pid, WAIT_LOG_ABNORMAL); finish: (void) default_signals(SIGINT, -1); diff --git a/src/delta/delta.c b/src/delta/delta.c index 8ebfbf441a..d94ae02754 100644 --- a/src/delta/delta.c +++ b/src/delta/delta.c @@ -186,16 +186,16 @@ static int found_override(const char *top, const char *bottom) { fflush(stdout); - r = safe_fork("(diff)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS, &pid); + r = safe_fork("(diff)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid); if (r < 0) - return log_error_errno(pid, "Failed to fork off diff: %m"); + return r; if (r == 0) { execlp("diff", "diff", "-us", "--", bottom, top, NULL); log_error_errno(errno, "Failed to execute diff: %m"); _exit(EXIT_FAILURE); } - wait_for_terminate_and_warn("diff", pid, false); + (void) wait_for_terminate_and_check("diff", pid, WAIT_LOG_ABNORMAL); putchar('\n'); return r; diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c index 818d581c30..97d824aca4 100644 --- a/src/fsck/fsck.c +++ b/src/fsck/fsck.c @@ -284,9 +284,8 @@ int main(int argc, char *argv[]) { _cleanup_(sd_device_unrefp) sd_device *dev = NULL; const char *device, *type; bool root_directory; - siginfo_t status; struct stat st; - int r; + int r, exit_status; pid_t pid; if (argc > 2) { @@ -392,11 +391,9 @@ int main(int argc, char *argv[]) { } } - r = safe_fork("(fsck)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &pid); - if (r < 0) { - log_error_errno(r, "fork(): %m"); + r = safe_fork("(fsck)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); + if (r < 0) goto finish; - } if (r == 0) { char dash_c[STRLEN("-C") + DECIMAL_STR_MAX(int) + 1]; int progress_socket = -1; @@ -451,38 +448,30 @@ int main(int argc, char *argv[]) { (void) process_progress(progress_pipe[0]); progress_pipe[0] = -1; - r = wait_for_terminate(pid, &status); - if (r < 0) { - log_error_errno(r, "waitid(): %m"); + exit_status = wait_for_terminate_and_check("fsck", pid, WAIT_LOG_ABNORMAL); + if (exit_status < 0) { + r = exit_status; goto finish; } + if (exit_status & ~1) { + log_error("fsck failed with exit status %i.", exit_status); - if (status.si_code != CLD_EXITED || (status.si_status & ~1)) { - - if (IN_SET(status.si_code, CLD_KILLED, CLD_DUMPED)) - log_error("fsck terminated by signal %s.", signal_to_string(status.si_status)); - else if (status.si_code == CLD_EXITED) - log_error("fsck failed with error code %i.", status.si_status); - else - log_error("fsck failed due to unknown reason."); - - r = -EINVAL; - - if (status.si_code == CLD_EXITED && (status.si_status & FSCK_SYSTEM_SHOULD_REBOOT) && root_directory) + if ((exit_status & FSCK_SYSTEM_SHOULD_REBOOT) && root_directory) { /* System should be rebooted. */ start_target(SPECIAL_REBOOT_TARGET, "replace-irreversibly"); - else if (status.si_code == CLD_EXITED && (status.si_status & (FSCK_SYSTEM_SHOULD_REBOOT | FSCK_ERRORS_LEFT_UNCORRECTED))) + r = -EINVAL; + } else if (exit_status & (FSCK_SYSTEM_SHOULD_REBOOT | FSCK_ERRORS_LEFT_UNCORRECTED)) { /* Some other problem */ start_target(SPECIAL_EMERGENCY_TARGET, "replace"); - else { + r = -EINVAL; + } else { log_warning("Ignoring error."); r = 0; } - } else r = 0; - if (status.si_code == CLD_EXITED && (status.si_status & FSCK_ERROR_CORRECTED)) + if (exit_status & FSCK_ERROR_CORRECTED) (void) touch("/run/systemd/quotacheck"); finish: diff --git a/src/import/import-common.c b/src/import/import-common.c index 1efbda9787..aa7ab974d2 100644 --- a/src/import/import-common.c +++ b/src/import/import-common.c @@ -82,9 +82,9 @@ int import_fork_tar_x(const char *path, pid_t *ret) { if (pipe2(pipefd, O_CLOEXEC) < 0) return log_error_errno(errno, "Failed to create pipe for tar: %m"); - r = safe_fork("(tar)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &pid); + r = safe_fork("(tar)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); if (r < 0) - return log_error_errno(r, "Failed to fork off tar: %m"); + return r; if (r == 0) { int null_fd; uint64_t retain = @@ -151,9 +151,9 @@ int import_fork_tar_c(const char *path, pid_t *ret) { if (pipe2(pipefd, O_CLOEXEC) < 0) return log_error_errno(errno, "Failed to create pipe for tar: %m"); - r = safe_fork("(tar)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &pid); + r = safe_fork("(tar)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); if (r < 0) - return log_error_errno(r, "Failed to fork off tar: %m"); + return r; if (r == 0) { int null_fd; uint64_t retain = (1ULL << CAP_DAC_OVERRIDE); diff --git a/src/import/import-tar.c b/src/import/import-tar.c index c5014499ac..09c7654adc 100644 --- a/src/import/import-tar.c +++ b/src/import/import-tar.c @@ -190,7 +190,7 @@ static int tar_import_finish(TarImport *i) { i->tar_fd = safe_close(i->tar_fd); if (i->tar_pid > 0) { - r = wait_for_terminate_and_warn("tar", i->tar_pid, true); + r = wait_for_terminate_and_check("tar", i->tar_pid, WAIT_LOG); i->tar_pid = 0; if (r < 0) return r; diff --git a/src/import/pull-common.c b/src/import/pull-common.c index 6acd264af5..ecdcbd2dc2 100644 --- a/src/import/pull-common.c +++ b/src/import/pull-common.c @@ -463,9 +463,9 @@ int pull_verify(PullJob *main_job, gpg_home_created = true; - r = safe_fork("(gpg)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &pid); + r = safe_fork("(gpg)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); if (r < 0) - return log_error_errno(r, "Failed to fork off gpg: %m"); + return r; if (r == 0) { const char *cmd[] = { "gpg", @@ -542,11 +542,11 @@ int pull_verify(PullJob *main_job, gpg_pipe[1] = safe_close(gpg_pipe[1]); - r = wait_for_terminate_and_warn("gpg", pid, true); + r = wait_for_terminate_and_check("gpg", pid, WAIT_LOG_ABNORMAL); pid = 0; if (r < 0) goto finish; - if (r > 0) { + if (r != EXIT_SUCCESS) { log_error("DOWNLOAD INVALID: Signature verification failed."); r = -EBADMSG; } else { diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c index ed91511245..6ee63bdad5 100644 --- a/src/import/pull-tar.c +++ b/src/import/pull-tar.c @@ -333,11 +333,11 @@ static void tar_pull_job_on_finished(PullJob *j) { goto finish; if (i->tar_pid > 0) { - r = wait_for_terminate_and_warn("tar", i->tar_pid, true); + r = wait_for_terminate_and_check("tar", i->tar_pid, WAIT_LOG); i->tar_pid = 0; if (r < 0) goto finish; - if (r > 0) { + if (r != EXIT_SUCCESS) { r = -EIO; goto finish; } diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c index 32416493fd..a8755751ee 100644 --- a/src/journal-remote/journal-remote.c +++ b/src/journal-remote/journal-remote.c @@ -87,10 +87,10 @@ static int spawn_child(const char* child, char** argv) { if (pipe(fd) < 0) return log_error_errno(errno, "Failed to create pager pipe: %m"); - r = safe_fork("(remote)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child_pid); + r = safe_fork("(remote)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &child_pid); if (r < 0) { safe_close_pair(fd); - return log_error_errno(r, "Failed to fork: %m"); + return r; } /* In the child */ diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index 844a94f1b0..ae15f4b76a 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -247,11 +247,25 @@ int journal_file_set_offline(JournalFile *f, bool wait) { if (wait) /* Without using a thread if waiting. */ journal_file_set_offline_internal(f); else { + sigset_t ss, saved_ss; + int k; + + if (sigfillset(&ss) < 0) + return -errno; + + r = pthread_sigmask(SIG_BLOCK, &ss, &saved_ss); + if (r > 0) + return -r; + r = pthread_create(&f->offline_thread, NULL, journal_file_set_offline_thread, f); + + k = pthread_sigmask(SIG_SETMASK, &saved_ss, NULL); if (r > 0) { f->offline_state = OFFLINE_JOINED; return -r; } + if (k > 0) + return -k; } return 0; diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index d68e3a8d51..278f38f24e 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -241,6 +241,13 @@ void server_space_usage_message(Server *s, JournalStorage *storage) { NULL); } +static bool uid_for_system_journal(uid_t uid) { + + /* Returns true if the specified UID shall get its data stored in the system journal*/ + + return uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY; +} + static void server_add_acls(JournalFile *f, uid_t uid) { #if HAVE_ACL int r; @@ -248,7 +255,7 @@ static void server_add_acls(JournalFile *f, uid_t uid) { assert(f); #if HAVE_ACL - if (uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY) + if (uid_for_system_journal(uid)) return; r = add_acls_for_user(f->fd, uid); @@ -406,7 +413,7 @@ static JournalFile* find_journal(Server *s, uid_t uid) { if (s->runtime_journal) return s->runtime_journal; - if (uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY) + if (uid_for_system_journal(uid)) return s->system_journal; r = sd_id128_get_machine(&machine); diff --git a/src/libsystemd/sd-bus/bus-container.c b/src/libsystemd/sd-bus/bus-container.c index a1242c5678..16156d8823 100644 --- a/src/libsystemd/sd-bus/bus-container.c +++ b/src/libsystemd/sd-bus/bus-container.c @@ -31,9 +31,8 @@ int bus_container_connect_socket(sd_bus *b) { _cleanup_close_pair_ int pair[2] = { -1, -1 }; _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1; - pid_t child; - siginfo_t si; int r, error_buf = 0; + pid_t child; ssize_t n; assert(b); @@ -97,21 +96,20 @@ int bus_container_connect_socket(sd_bus *b) { _exit(EXIT_SUCCESS); } - r = wait_for_terminate(grandchild, &si); + r = wait_for_terminate_and_check("(sd-buscntr2)", grandchild, 0); if (r < 0) _exit(EXIT_FAILURE); - if (si.si_code != CLD_EXITED) - _exit(EXIT_FAILURE); - - _exit(si.si_status); + _exit(r); } pair[1] = safe_close(pair[1]); - r = wait_for_terminate(child, &si); + r = wait_for_terminate_and_check("(sd-buscntr)", child, 0); if (r < 0) return r; + if (r != EXIT_SUCCESS) + return -EPROTO; n = read(pair[0], &error_buf, sizeof(error_buf)); if (n < 0) @@ -131,11 +129,5 @@ int bus_container_connect_socket(sd_bus *b) { return -error_buf; } - if (si.si_code != CLD_EXITED) - return -EIO; - - if (si.si_status != EXIT_SUCCESS) - return -EIO; - return bus_socket_start_auth(b); } diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c index 12478e7cc6..0b39115d16 100644 --- a/src/libsystemd/sd-bus/bus-control.c +++ b/src/libsystemd/sd-bus/bus-control.c @@ -555,15 +555,16 @@ _public_ int sd_bus_get_name_creds( static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; pid_t pid = 0; - bool do_label; + bool do_label, do_groups; int r; assert(bus); do_label = bus->label && (mask & SD_BUS_CREDS_SELINUX_CONTEXT); + do_groups = bus->n_groups != (size_t) -1 && (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS); /* Avoid allocating anything if we have no chance of returning useful data */ - if (!bus->ucred_valid && !do_label) + if (!bus->ucred_valid && !do_label && !do_groups) return -ENODATA; c = bus_creds_new(); @@ -571,17 +572,17 @@ static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds ** return -ENOMEM; if (bus->ucred_valid) { - if (bus->ucred.pid > 0) { + if (pid_is_valid(bus->ucred.pid)) { pid = c->pid = bus->ucred.pid; c->mask |= SD_BUS_CREDS_PID & mask; } - if (bus->ucred.uid != UID_INVALID) { + if (uid_is_valid(bus->ucred.uid)) { c->euid = bus->ucred.uid; c->mask |= SD_BUS_CREDS_EUID & mask; } - if (bus->ucred.gid != GID_INVALID) { + if (gid_is_valid(bus->ucred.gid)) { c->egid = bus->ucred.gid; c->mask |= SD_BUS_CREDS_EGID & mask; } @@ -595,6 +596,16 @@ static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds ** c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; } + if (do_groups) { + c->supplementary_gids = newdup(gid_t, bus->groups, bus->n_groups); + if (!c->supplementary_gids) + return -ENOMEM; + + c->n_supplementary_gids = bus->n_groups; + + c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; + } + r = bus_creds_add_more(c, mask, pid, 0); if (r < 0) return r; diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h index 378c408ea3..4175ca3efe 100644 --- a/src/libsystemd/sd-bus/bus-internal.h +++ b/src/libsystemd/sd-bus/bus-internal.h @@ -261,6 +261,8 @@ struct sd_bus { struct ucred ucred; char *label; + gid_t *groups; + size_t n_groups; uint64_t creds_mask; diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c index 013bdb05a0..0a1d36a9d3 100644 --- a/src/libsystemd/sd-bus/bus-socket.c +++ b/src/libsystemd/sd-bus/bus-socket.c @@ -603,14 +603,27 @@ static void bus_get_peercred(sd_bus *b) { int r; assert(b); + assert(!b->ucred_valid); + assert(!b->label); + assert(b->n_groups == (size_t) -1); /* Get the peer for socketpair() sockets */ b->ucred_valid = getpeercred(b->input_fd, &b->ucred) >= 0; /* Get the SELinux context of the peer */ r = getpeersec(b->input_fd, &b->label); - if (r < 0 && r != -EOPNOTSUPP) + if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENOPROTOOPT)) log_debug_errno(r, "Failed to determine peer security context: %m"); + + /* Get the list of auxiliary groups of the peer */ + r = getpeergroups(b->input_fd, &b->groups); + if (r < 0) { + if (!IN_SET(r, -EOPNOTSUPP, -ENOPROTOOPT)) + log_debug_errno(r, "Failed to determine peer groups list: %m"); + + b->n_groups = (size_t) -1; + } else + b->n_groups = (size_t) r; } static int bus_socket_start_auth_client(sd_bus *b) { diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index 5fb15a7aaf..3cfc60fe86 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -134,6 +134,7 @@ static void bus_free(sd_bus *b) { bus_close_fds(b); free(b->label); + free(b->groups); free(b->rbuffer); free(b->unique_name); free(b->auth_buffer); @@ -185,6 +186,7 @@ _public_ int sd_bus_new(sd_bus **ret) { r->hello_flags |= KDBUS_HELLO_ACCEPT_FD; r->attach_flags |= KDBUS_ATTACH_NAMES; r->original_pid = getpid_cached(); + r->n_groups = (size_t) -1; assert_se(pthread_mutex_init(&r->memfd_cache_mutex, NULL) == 0); @@ -3483,8 +3485,7 @@ _public_ int sd_bus_path_decode_many(const char *path, const char *path_template } va_end(list); - free(labels); - labels = NULL; + labels = mfree(labels); return 1; } diff --git a/src/libsystemd/sd-resolve/sd-resolve.c b/src/libsystemd/sd-resolve/sd-resolve.c index 0f369f4917..6ed50b4e4a 100644 --- a/src/libsystemd/sd-resolve/sd-resolve.c +++ b/src/libsystemd/sd-resolve/sd-resolve.c @@ -398,11 +398,6 @@ static int handle_request(int out_fd, const Packet *packet, size_t length) { static void* thread_worker(void *p) { sd_resolve *resolve = p; - sigset_t fullset; - - /* No signals in this thread please */ - assert_se(sigfillset(&fullset) == 0); - assert_se(pthread_sigmask(SIG_BLOCK, &fullset, NULL) == 0); /* Assign a pretty name to this thread */ (void) pthread_setname_np(pthread_self(), "sd-resolve"); @@ -437,8 +432,18 @@ static void* thread_worker(void *p) { } static int start_threads(sd_resolve *resolve, unsigned extra) { + sigset_t ss, saved_ss; unsigned n; - int r; + int r, k; + + if (sigfillset(&ss) < 0) + return -errno; + + /* No signals in forked off threads please. We set the mask before forking, so that the threads never exist + * with a different mask than a fully blocked one */ + r = pthread_sigmask(SIG_BLOCK, &ss, &saved_ss); + if (r > 0) + return -r; n = resolve->n_outstanding + extra; n = CLAMP(n, WORKERS_MIN, WORKERS_MAX); @@ -446,13 +451,22 @@ static int start_threads(sd_resolve *resolve, unsigned extra) { while (resolve->n_valid_workers < n) { r = pthread_create(&resolve->workers[resolve->n_valid_workers], NULL, thread_worker, resolve); - if (r != 0) - return -r; + if (r > 0) { + r = -r; + goto finish; + } resolve->n_valid_workers++; } - return 0; + r = 0; + +finish: + k = pthread_sigmask(SIG_SETMASK, &saved_ss, NULL); + if (k > 0 && r >= 0) + r = -k; + + return r; } static bool resolve_pid_changed(sd_resolve *r) { diff --git a/src/login/inhibit.c b/src/login/inhibit.c index 9117a70481..22657f9eda 100644 --- a/src/login/inhibit.c +++ b/src/login/inhibit.c @@ -266,11 +266,9 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - r = safe_fork("(inhibit)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS, &pid); - if (r < 0) { - log_error_errno(r, "Failed to fork: %m"); + r = safe_fork("(inhibit)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid); + if (r < 0) return EXIT_FAILURE; - } if (r == 0) { /* Child */ execvp(argv[optind], argv + optind); @@ -278,7 +276,7 @@ int main(int argc, char *argv[]) { _exit(EXIT_FAILURE); } - r = wait_for_terminate_and_warn(argv[optind], pid, true); + r = wait_for_terminate_and_check(argv[optind], pid, WAIT_LOG); return r < 0 ? EXIT_FAILURE : r; } diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index 68e0b7edcc..0ca2e54ed9 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -228,7 +228,6 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd _cleanup_free_ char *us = NULL, *them = NULL; _cleanup_close_ int netns_fd = -1; const char *p; - siginfo_t si; pid_t child; r = readlink_malloc("/proc/self/ns/net", &us); @@ -337,10 +336,10 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd return r; } - r = wait_for_terminate(child, &si); + r = wait_for_terminate_and_check("(sd-addr)", child, 0); if (r < 0) return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m"); - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + if (r != EXIT_SUCCESS) return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally."); break; } @@ -379,7 +378,6 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s _cleanup_close_ int mntns_fd = -1, root_fd = -1; _cleanup_close_pair_ int pair[2] = { -1, -1 }; _cleanup_fclose_ FILE *f = NULL; - siginfo_t si; pid_t child; r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd); @@ -429,12 +427,12 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s if (r < 0) return r; - r = wait_for_terminate(child, &si); + r = wait_for_terminate_and_check("(sd-osrel)", child, 0); if (r < 0) return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m"); - if (si.si_code == CLD_EXITED && si.si_status == EXIT_NOT_FOUND) + if (r == EXIT_NOT_FOUND) return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Machine does not contain OS release information"); - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + if (r != EXIT_SUCCESS) return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally."); break; @@ -840,7 +838,6 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu const char *dest, *src; Machine *m = userdata; struct stat st; - siginfo_t si; pid_t child; uid_t uid; int r; @@ -1046,17 +1043,12 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); - r = wait_for_terminate(child, &si); + r = wait_for_terminate_and_check("(sd-bindmnt)", child, 0); if (r < 0) { r = sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m"); goto finish; } - if (si.si_code != CLD_EXITED) { - r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally."); - goto finish; - } - if (si.si_status != EXIT_SUCCESS) { - + if (r != EXIT_SUCCESS) { if (read(errno_pipe_fd[0], &r, sizeof(r)) == sizeof(r)) r = sd_bus_error_set_errnof(error, r, "Failed to mount: %m"); else @@ -1268,7 +1260,6 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda case MACHINE_CONTAINER: { _cleanup_close_ int mntns_fd = -1, root_fd = -1; _cleanup_close_pair_ int pair[2] = { -1, -1 }; - siginfo_t si; pid_t child; r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd); @@ -1304,10 +1295,10 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda pair[1] = safe_close(pair[1]); - r = wait_for_terminate(child, &si); + r = wait_for_terminate_and_check("(sd-openroot)", child, 0); if (r < 0) return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m"); - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + if (r != EXIT_SUCCESS) return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally."); fd = receive_one_fd(pair[0], MSG_DONTWAIT); diff --git a/src/nspawn/nspawn-setuid.c b/src/nspawn/nspawn-setuid.c index 3d9e23ec1f..cb1be273f4 100644 --- a/src/nspawn/nspawn-setuid.c +++ b/src/nspawn/nspawn-setuid.c @@ -43,9 +43,9 @@ static int spawn_getent(const char *database, const char *key, pid_t *rpid) { if (pipe2(pipe_fds, O_CLOEXEC) < 0) return log_error_errno(errno, "Failed to allocate pipe: %m"); - r = safe_fork("(getent)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &pid); + r = safe_fork("(getent)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); if (r < 0) - return log_error_errno(r, "Failed to fork getent child: %m"); + return r; if (r == 0) { int nullfd; char *empty_env = NULL; @@ -133,7 +133,7 @@ int change_uid_gid(const char *user, char **_home) { truncate_nl(line); - wait_for_terminate_and_warn("getent passwd", pid, true); + (void) wait_for_terminate_and_check("getent passwd", pid, WAIT_LOG); x = strchr(line, ':'); if (!x) { @@ -216,7 +216,7 @@ int change_uid_gid(const char *user, char **_home) { truncate_nl(line); - wait_for_terminate_and_warn("getent initgroups", pid, true); + (void) wait_for_terminate_and_check("getent initgroups", pid, WAIT_LOG); /* Skip over the username and subsequent separator whitespace */ x = line; diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index f646971c30..383047acc7 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -3516,9 +3516,11 @@ static int run(int master, } /* Wait for the outer child. */ - r = wait_for_terminate_and_warn("namespace helper", *pid, NULL); - if (r != 0) - return r < 0 ? r : -EIO; + r = wait_for_terminate_and_check("(sd-namespace)", *pid, WAIT_LOG_ABNORMAL); + if (r < 0) + return r; + if (r != EXIT_SUCCESS) + return -EIO; /* And now retrieve the PID of the inner child. */ l = recv(pid_socket_pair[0], pid, sizeof *pid, 0); diff --git a/src/partition/makefs.c b/src/partition/makefs.c index 872cf0dfd1..2ed31ac7ab 100644 --- a/src/partition/makefs.c +++ b/src/partition/makefs.c @@ -43,9 +43,9 @@ static int makefs(const char *type, const char *device) { if (access(mkfs, X_OK) != 0) return log_error_errno(errno, "%s is not executable: %m", mkfs); - r = safe_fork("(fsck)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &pid); + r = safe_fork("(fsck)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); if (r < 0) - return log_error_errno(r, "fork(): %m"); + return r; if (r == 0) { const char *cmdline[3] = { mkfs, device, NULL }; @@ -55,7 +55,7 @@ static int makefs(const char *type, const char *device) { _exit(EXIT_FAILURE); } - return wait_for_terminate_and_warn(mkfs, pid, true); + return wait_for_terminate_and_check(mkfs, pid, WAIT_LOG); } int main(int argc, char *argv[]) { diff --git a/src/quotacheck/quotacheck.c b/src/quotacheck/quotacheck.c index cd441675dc..1bf718e4f6 100644 --- a/src/quotacheck/quotacheck.c +++ b/src/quotacheck/quotacheck.c @@ -71,14 +71,6 @@ static void test_files(void) { } int main(int argc, char *argv[]) { - - static const char * const cmdline[] = { - QUOTACHECK, - "-anug", - NULL - }; - - pid_t pid; int r; if (argc > 1) { @@ -106,12 +98,15 @@ int main(int argc, char *argv[]) { return EXIT_SUCCESS; } - r = safe_fork("(quotacheck)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &pid); - if (r < 0) { - log_error_errno(r, "fork(): %m"); + r = safe_fork("(quotacheck)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); + if (r < 0) goto finish; - } if (r == 0) { + static const char * const cmdline[] = { + QUOTACHECK, + "-anug", + NULL + }; /* Child */ @@ -119,8 +114,6 @@ int main(int argc, char *argv[]) { _exit(EXIT_FAILURE); /* Operational error */ } - r = wait_for_terminate_and_warn("quotacheck", pid, true); - finish: return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/remount-fs/remount-fs.c b/src/remount-fs/remount-fs.c index 1f0397cf6f..c61777c3fe 100644 --- a/src/remount-fs/remount-fs.c +++ b/src/remount-fs/remount-fs.c @@ -87,11 +87,9 @@ int main(int argc, char *argv[]) { log_debug("Remounting %s", me->mnt_dir); - r = safe_fork("(remount)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &pid); - if (r < 0) { - log_error_errno(r, "Failed to fork: %m"); + r = safe_fork("(remount)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); + if (r < 0) goto finish; - } if (r == 0) { /* Child */ diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 7a185461a3..50731fa5ff 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -560,8 +560,7 @@ void bus_verify_polkit_async_registry_free(Hashmap *registry) { int bus_check_peercred(sd_bus *c) { struct ucred ucred; - socklen_t l; - int fd; + int fd, r; assert(c); @@ -569,12 +568,9 @@ int bus_check_peercred(sd_bus *c) { if (fd < 0) return fd; - l = sizeof(struct ucred); - if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0) - return -errno; - - if (l != sizeof(struct ucred)) - return -E2BIG; + r = getpeercred(fd, &ucred); + if (r < 0) + return r; if (ucred.uid != 0 && ucred.uid != geteuid()) return -EPERM; diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 8fc9e97bbd..35b2c56555 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -1244,7 +1244,6 @@ int dissected_image_acquire_metadata(DissectedImage *m) { _cleanup_free_ char *hostname = NULL; unsigned n_meta_initialized = 0, k; int fds[2 * _META_MAX], r; - siginfo_t si; BLOCK_SIGNALS(SIGCHLD); @@ -1260,18 +1259,10 @@ int dissected_image_acquire_metadata(DissectedImage *m) { if (r < 0) goto finish; - child = raw_clone(SIGCHLD|CLONE_NEWNS); - if (child < 0) { - r = -errno; + r = safe_fork("(sd-dissect)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_NEW_MOUNTNS, &child); + if (r < 0) goto finish; - } - - if (child == 0) { - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - + if (r == 0) { /* Make sure we never propagate to the host */ if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) _exit(EXIT_FAILURE); @@ -1366,15 +1357,12 @@ int dissected_image_acquire_metadata(DissectedImage *m) { } } - r = wait_for_terminate(child, &si); + r = wait_for_terminate_and_check("(sd-dissect)", child, 0); + child = 0; if (r < 0) goto finish; - child = 0; - - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) { - r = -EPROTO; - goto finish; - } + if (r != EXIT_SUCCESS) + return -EPROTO; free_and_replace(m->hostname, hostname); m->machine_id = machine_id; diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index ffb921e4a8..a861be7b32 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -1256,7 +1256,6 @@ static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) { _cleanup_close_pair_ int pair[2] = { -1, -1 }; _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1; pid_t pid, child; - siginfo_t si; char buf[37]; ssize_t k; int r; @@ -1308,9 +1307,11 @@ static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) { pair[1] = safe_close(pair[1]); - r = wait_for_terminate(child, &si); - if (r < 0 || si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) - return r < 0 ? r : -EIO; + r = wait_for_terminate_and_check("(sd-bootid)", child, 0); + if (r < 0) + return r; + if (r != EXIT_SUCCESS) + return -EIO; k = recv(pair[0], buf, 36, 0); if (k != 36) diff --git a/src/shared/machine-pool.c b/src/shared/machine-pool.c index fb8a6e2fd7..031d443e9e 100644 --- a/src/shared/machine-pool.c +++ b/src/shared/machine-pool.c @@ -79,7 +79,6 @@ static int setup_machine_raw(uint64_t size, sd_bus_error *error) { _cleanup_close_ int fd = -1; struct statvfs ss; pid_t pid = 0; - siginfo_t si; int r; /* We want to be able to make use of btrfs-specific file @@ -141,24 +140,19 @@ static int setup_machine_raw(uint64_t size, sd_bus_error *error) { _exit(EXIT_FAILURE); } - r = wait_for_terminate(pid, &si); + r = wait_for_terminate_and_check("mkfs", pid, 0); + pid = 0; + if (r < 0) { sd_bus_error_set_errnof(error, r, "Failed to wait for mkfs.btrfs: %m"); goto fail; } - - pid = 0; - - if (si.si_code != CLD_EXITED) { - r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs died abnormally."); - goto fail; - } - if (si.si_status == 99) { + if (r == 99) { r = sd_bus_error_set_errnof(error, ENOENT, "Cannot set up /var/lib/machines, mkfs.btrfs is missing"); goto fail; } - if (si.si_status != 0) { - r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs failed with error code %i", si.si_status); + if (r != EXIT_SUCCESS) { + r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs failed with error code %i", r); goto fail; } diff --git a/src/shared/pager.c b/src/shared/pager.c index 17e0121a66..75db3c985b 100644 --- a/src/shared/pager.c +++ b/src/shared/pager.c @@ -42,6 +42,11 @@ static pid_t pager_pid = 0; +static int stored_stdout = -1; +static int stored_stderr = -1; +static bool stdout_redirected = false; +static bool stderr_redirected = false; + noreturn static void pager_fallback(void) { int r; @@ -54,11 +59,6 @@ noreturn static void pager_fallback(void) { _exit(EXIT_SUCCESS); } -static int stored_stdout = -1; -static int stored_stderr = -1; -static bool stdout_redirected = false; -static bool stderr_redirected = false; - int pager_open(bool no_pager, bool jump_to_end) { _cleanup_close_pair_ int fd[2] = { -1, -1 }; const char *pager; @@ -73,6 +73,9 @@ int pager_open(bool no_pager, bool jump_to_end) { if (terminal_is_dumb()) return 0; + if (!is_main_thread()) + return -EPERM; + pager = getenv("SYSTEMD_PAGER"); if (!pager) pager = getenv("PAGER"); @@ -89,9 +92,9 @@ int pager_open(bool no_pager, bool jump_to_end) { if (pipe2(fd, O_CLOEXEC) < 0) return log_error_errno(errno, "Failed to create pager pipe: %m"); - r = safe_fork("(pager)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &pager_pid); + r = safe_fork("(pager)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pager_pid); if (r < 0) - return log_error_errno(r, "Failed to fork pager: %m"); + return r; if (r == 0) { const char* less_opts, *less_charset; @@ -190,7 +193,6 @@ int show_man_page(const char *desc, bool null_stdio) { pid_t pid; size_t k; int r; - siginfo_t status; k = strlen(desc); @@ -208,9 +210,9 @@ int show_man_page(const char *desc, bool null_stdio) { } else args[1] = desc; - r = safe_fork("(man)", FORK_RESET_SIGNALS|FORK_DEATHSIG|(null_stdio ? FORK_NULL_STDIO : 0), &pid); + r = safe_fork("(man)", FORK_RESET_SIGNALS|FORK_DEATHSIG|(null_stdio ? FORK_NULL_STDIO : 0)|FORK_LOG, &pid); if (r < 0) - return log_error_errno(r, "Failed to fork: %m"); + return r; if (r == 0) { /* Child */ execvp(args[0], (char**) args); @@ -218,10 +220,5 @@ int show_man_page(const char *desc, bool null_stdio) { _exit(EXIT_FAILURE); } - r = wait_for_terminate(pid, &status); - if (r < 0) - return r; - - log_debug("Exit code %i status %i", status.si_code, status.si_status); - return status.si_status; + return wait_for_terminate_and_check(NULL, pid, 0); } diff --git a/src/shared/spawn-ask-password-agent.c b/src/shared/spawn-ask-password-agent.c index 17785fd153..f78167c25c 100644 --- a/src/shared/spawn-ask-password-agent.c +++ b/src/shared/spawn-ask-password-agent.c @@ -40,6 +40,9 @@ int ask_password_agent_open(void) { if (!isatty(STDIN_FILENO)) return 0; + if (!is_main_thread()) + return -EPERM; + r = fork_agent("(sd-askpwagent)", NULL, 0, &agent_pid, diff --git a/src/shared/spawn-polkit-agent.c b/src/shared/spawn-polkit-agent.c index 886248b9f9..3c93c79ceb 100644 --- a/src/shared/spawn-polkit-agent.c +++ b/src/shared/spawn-polkit-agent.c @@ -52,6 +52,9 @@ int polkit_agent_open(void) { if (!isatty(STDIN_FILENO)) return 0; + if (!is_main_thread()) + return -EPERM; + if (pipe2(pipe_fd, 0) < 0) return -errno; diff --git a/src/sulogin-shell/sulogin-shell.c b/src/sulogin-shell/sulogin-shell.c index a6e0e0476f..33dc07c5bd 100644 --- a/src/sulogin-shell/sulogin-shell.c +++ b/src/sulogin-shell/sulogin-shell.c @@ -83,9 +83,9 @@ static int fork_wait(const char* const cmdline[]) { pid_t pid; int r; - r = safe_fork("(sulogin)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &pid); + r = safe_fork("(sulogin)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); if (r < 0) - return log_error_errno(r, "fork(): %m"); + return r; if (r == 0) { /* Child */ execv(cmdline[0], (char**) cmdline); @@ -93,7 +93,7 @@ static int fork_wait(const char* const cmdline[]) { _exit(EXIT_FAILURE); /* Operational error */ } - return wait_for_terminate_and_warn(cmdline[0], pid, false); + return wait_for_terminate_and_check(cmdline[0], pid, WAIT_LOG_ABNORMAL); } static void print_mode(const char* mode) { diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 61ce1d0290..1376497f8d 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -3539,9 +3539,9 @@ static int load_kexec_kernel(void) { if (arg_dry_run) return 0; - r = safe_fork("(kexec)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &pid); + r = safe_fork("(kexec)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); if (r < 0) - return log_error_errno(r, "Failed to fork: %m"); + return r; if (r == 0) { const char* const args[] = { @@ -3556,7 +3556,7 @@ static int load_kexec_kernel(void) { _exit(EXIT_FAILURE); } - return wait_for_terminate_and_warn("kexec", pid, true); + return wait_for_terminate_and_check("kexec", pid, WAIT_LOG); } static int set_exit_code(uint8_t code) { @@ -6065,7 +6065,6 @@ static int enable_sysv_units(const char *verb, char **args) { _cleanup_free_ char *p = NULL, *q = NULL, *l = NULL; bool found_native = false, found_sysv; - siginfo_t status; const char *name; unsigned c = 1; pid_t pid; @@ -6119,9 +6118,9 @@ static int enable_sysv_units(const char *verb, char **args) { if (!arg_quiet) log_info("Executing: %s", l); - j = safe_fork("(sysv)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &pid); + j = safe_fork("(sysv-install)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); if (j < 0) - return log_error_errno(j, "Failed to fork: %m"); + return j; if (j == 0) { /* Child */ execv(argv[0], (char**) argv); @@ -6129,27 +6128,21 @@ static int enable_sysv_units(const char *verb, char **args) { _exit(EXIT_FAILURE); } - j = wait_for_terminate(pid, &status); + j = wait_for_terminate_and_check("sysv-install", pid, WAIT_LOG_ABNORMAL); if (j < 0) - return log_error_errno(j, "Failed to wait for child: %m"); + return j; + if (streq(verb, "is-enabled")) { + if (j == EXIT_SUCCESS) { + if (!arg_quiet) + puts("enabled"); + r = 1; + } else { + if (!arg_quiet) + puts("disabled"); + } - if (status.si_code == CLD_EXITED) { - if (streq(verb, "is-enabled")) { - if (status.si_status == 0) { - if (!arg_quiet) - puts("enabled"); - r = 1; - } else { - if (!arg_quiet) - puts("disabled"); - } - - } else if (status.si_status != 0) - return -EBADE; /* We don't warn here, under the assumption the script already showed an explanation */ - } else { - log_error("Unexpected waitid() result."); - return -EPROTO; - } + } else if (j != EXIT_SUCCESS) + return -EBADE; /* We don't warn here, under the assumption the script already showed an explanation */ if (found_native) continue; @@ -6987,14 +6980,13 @@ static int unit_file_create_copy( } static int run_editor(char **paths) { - pid_t pid; int r; assert(paths); - r = safe_fork("(editor)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &pid); + r = safe_fork("(editor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); if (r < 0) - return log_error_errno(r, "Failed to fork: %m"); + return r; if (r == 0) { const char **args; char *editor, **editor_args = NULL; @@ -7057,10 +7049,6 @@ static int run_editor(char **paths) { _exit(EXIT_FAILURE); } - r = wait_for_terminate_and_warn("editor", pid, true); - if (r < 0) - return log_error_errno(r, "Failed to wait for child: %m"); - return 0; } diff --git a/src/test/test-seccomp.c b/src/test/test-seccomp.c index 36b49ebc71..aed307077e 100644 --- a/src/test/test-seccomp.c +++ b/src/test/test-seccomp.c @@ -141,7 +141,7 @@ static void test_filter_sets(void) { _exit(EXIT_SUCCESS); } - assert_se(wait_for_terminate_and_warn(syscall_filter_sets[i].name, pid, true) == EXIT_SUCCESS); + assert_se(wait_for_terminate_and_check(syscall_filter_sets[i].name, pid, WAIT_LOG) == EXIT_SUCCESS); } } @@ -227,7 +227,7 @@ static void test_restrict_namespace(void) { _exit(EXIT_SUCCESS); } - assert_se(wait_for_terminate_and_warn("nsseccomp", pid, true) == EXIT_SUCCESS); + assert_se(wait_for_terminate_and_check("nsseccomp", pid, WAIT_LOG) == EXIT_SUCCESS); } static void test_protect_sysctl(void) { @@ -260,7 +260,7 @@ static void test_protect_sysctl(void) { _exit(EXIT_SUCCESS); } - assert_se(wait_for_terminate_and_warn("sysctlseccomp", pid, true) == EXIT_SUCCESS); + assert_se(wait_for_terminate_and_check("sysctlseccomp", pid, WAIT_LOG) == EXIT_SUCCESS); } static void test_restrict_address_families(void) { @@ -343,7 +343,7 @@ static void test_restrict_address_families(void) { _exit(EXIT_SUCCESS); } - assert_se(wait_for_terminate_and_warn("socketseccomp", pid, true) == EXIT_SUCCESS); + assert_se(wait_for_terminate_and_check("socketseccomp", pid, WAIT_LOG) == EXIT_SUCCESS); } static void test_restrict_realtime(void) { @@ -381,7 +381,7 @@ static void test_restrict_realtime(void) { _exit(EXIT_SUCCESS); } - assert_se(wait_for_terminate_and_warn("realtimeseccomp", pid, true) == EXIT_SUCCESS); + assert_se(wait_for_terminate_and_check("realtimeseccomp", pid, WAIT_LOG) == EXIT_SUCCESS); } static void test_memory_deny_write_execute_mmap(void) { @@ -424,7 +424,7 @@ static void test_memory_deny_write_execute_mmap(void) { _exit(EXIT_SUCCESS); } - assert_se(wait_for_terminate_and_warn("memoryseccomp-mmap", pid, true) == EXIT_SUCCESS); + assert_se(wait_for_terminate_and_check("memoryseccomp-mmap", pid, WAIT_LOG) == EXIT_SUCCESS); } static void test_memory_deny_write_execute_shmat(void) { @@ -471,7 +471,7 @@ static void test_memory_deny_write_execute_shmat(void) { _exit(EXIT_SUCCESS); } - assert_se(wait_for_terminate_and_warn("memoryseccomp-shmat", pid, true) == EXIT_SUCCESS); + assert_se(wait_for_terminate_and_check("memoryseccomp-shmat", pid, WAIT_LOG) == EXIT_SUCCESS); } static void test_restrict_archs(void) { @@ -505,7 +505,7 @@ static void test_restrict_archs(void) { _exit(EXIT_SUCCESS); } - assert_se(wait_for_terminate_and_warn("archseccomp", pid, true) == EXIT_SUCCESS); + assert_se(wait_for_terminate_and_check("archseccomp", pid, WAIT_LOG) == EXIT_SUCCESS); } static void test_load_syscall_filter_set_raw(void) { @@ -596,7 +596,7 @@ static void test_load_syscall_filter_set_raw(void) { _exit(EXIT_SUCCESS); } - assert_se(wait_for_terminate_and_warn("syscallrawseccomp", pid, true) == EXIT_SUCCESS); + assert_se(wait_for_terminate_and_check("syscallrawseccomp", pid, WAIT_LOG) == EXIT_SUCCESS); } static void test_lock_personality(void) { @@ -643,7 +643,7 @@ static void test_lock_personality(void) { _exit(EXIT_SUCCESS); } - assert_se(wait_for_terminate_and_warn("lockpersonalityseccomp", pid, true) == EXIT_SUCCESS); + assert_se(wait_for_terminate_and_check("lockpersonalityseccomp", pid, WAIT_LOG) == EXIT_SUCCESS); } static void test_filter_sets_ordered(void) { diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c index 6a91baf6d2..201ce6667d 100644 --- a/src/test/test-socket-util.c +++ b/src/test/test-socket-util.c @@ -18,6 +18,10 @@ along with systemd; If not, see . ***/ +#include +#include +#include + #include "alloc-util.h" #include "async.h" #include "fd-util.h" @@ -474,6 +478,68 @@ static void test_in_addr_is_multicast(void) { assert_se(in_addr_is_multicast(f, &b) == 0); } +static void test_getpeercred_getpeergroups(void) { + int r; + + r = safe_fork("(getpeercred)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); + assert_se(r >= 0); + + if (r == 0) { + static const gid_t gids[] = { 3, 4, 5, 6, 7 }; + gid_t *test_gids; + gid_t *peer_groups = NULL; + size_t n_test_gids; + uid_t test_uid; + gid_t test_gid; + struct ucred ucred; + int pair[2]; + + if (geteuid() == 0) { + test_uid = 1; + test_gid = 2; + test_gids = (gid_t*) gids; + n_test_gids = ELEMENTSOF(gids); + + assert_se(setgroups(n_test_gids, test_gids) >= 0); + assert_se(setresgid(test_gid, test_gid, test_gid) >= 0); + assert_se(setresuid(test_uid, test_uid, test_uid) >= 0); + + } else { + long ngroups_max; + + test_uid = getuid(); + test_gid = getgid(); + + ngroups_max = sysconf(_SC_NGROUPS_MAX); + assert(ngroups_max > 0); + + test_gids = newa(gid_t, ngroups_max); + + r = getgroups(ngroups_max, test_gids); + assert_se(r >= 0); + n_test_gids = (size_t) r; + } + + assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) >= 0); + + assert_se(getpeercred(pair[0], &ucred) >= 0); + + assert_se(ucred.uid == test_uid); + assert_se(ucred.gid == test_gid); + assert_se(ucred.pid == getpid_cached()); + + r = getpeergroups(pair[0], &peer_groups); + assert_se(r >= 0 || IN_SET(r, -EOPNOTSUPP, -ENOPROTOOPT)); + + if (r >= 0) { + assert_se((size_t) r == n_test_gids); + assert_se(memcmp(peer_groups, test_gids, sizeof(gid_t) * n_test_gids) == 0); + } + + safe_close_pair(pair); + } +} + int main(int argc, char *argv[]) { log_set_max_level(LOG_DEBUG); @@ -502,5 +568,7 @@ int main(int argc, char *argv[]) { test_in_addr_is_multicast(); + test_getpeercred_getpeergroups(); + return 0; } diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index 8edd891c86..6e9c10aeb0 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -709,9 +709,9 @@ static int ask_on_this_console(const char *tty, pid_t *ret_pid, int argc, char * sig.sa_handler = SIG_DFL; assert_se(sigaction(SIGHUP, &sig, NULL) >= 0); - r = safe_fork("(sd-passwd)", FORK_RESET_SIGNALS, &pid); + r = safe_fork("(sd-passwd)", FORK_RESET_SIGNALS|FORK_LOG, &pid); if (r < 0) - return log_error_errno(r, "Failed to fork process: %m"); + return r; if (r == 0) { int ac; diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index 8506e67d6e..d0befba29c 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -774,11 +774,9 @@ int udev_event_spawn(struct udev_event *event, } } - err = safe_fork("(spawn)", FORK_RESET_SIGNALS, &pid); - if (err < 0) { - log_error_errno(err, "fork of '%s' failed: %m", cmd); + err = safe_fork("(spawn)", FORK_RESET_SIGNALS|FORK_LOG, &pid); + if (err < 0) goto out; - } if (err == 0) { char arg[UTIL_PATH_SIZE]; char *argv[128]; diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index fdc7c5f974..2e0e09d843 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -153,15 +153,15 @@ static int keyboard_load_and_wait(const char *vc, const char *map, const char *m log_debug("Executing \"%s\"...", strnull((cmd = strv_join((char**) args, " ")))); - r = safe_fork("(loadkeys)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS, &pid); + r = safe_fork("(loadkeys)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid); if (r < 0) - return log_error_errno(r, "Failed to fork: %m"); + return r; if (r == 0) { execv(args[0], (char **) args); _exit(EXIT_FAILURE); } - return wait_for_terminate_and_warn(KBD_LOADKEYS, pid, true); + return wait_for_terminate_and_check(KBD_LOADKEYS, pid, WAIT_LOG); } static int font_load_and_wait(const char *vc, const char *font, const char *map, const char *unimap) { @@ -193,15 +193,15 @@ static int font_load_and_wait(const char *vc, const char *font, const char *map, log_debug("Executing \"%s\"...", strnull((cmd = strv_join((char**) args, " ")))); - r = safe_fork("(setfont)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS, &pid); + r = safe_fork("(setfont)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid); if (r < 0) - return log_error_errno(r, "Failed to fork: %m"); + return r; if (r == 0) { execv(args[0], (char **) args); _exit(EXIT_FAILURE); } - return wait_for_terminate_and_warn(KBD_SETFONT, pid, true); + return wait_for_terminate_and_check(KBD_SETFONT, pid, WAIT_LOG); } /* @@ -452,8 +452,8 @@ int main(int argc, char **argv) { log_warning_errno(r, "Failed to read /proc/cmdline: %m"); } - toggle_utf8_sysfs(utf8); - toggle_utf8(vc, fd, utf8); + (void) toggle_utf8_sysfs(utf8); + (void) toggle_utf8(vc, fd, utf8); r = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap); keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) == 0;