Merge pull request #7767 from poettering/fork-wait
A variety of smaller improvements all over the place
This commit is contained in:
+31
-11
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
+7
-18
@@ -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;
|
||||
|
||||
|
||||
+17
-3
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
+69
-31
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
+22
-20
@@ -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;
|
||||
}
|
||||
|
||||
+49
-27
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user