diff options
Diffstat (limited to 'src/libnm-systemd-shared/src/basic/process-util.c')
-rw-r--r-- | src/libnm-systemd-shared/src/basic/process-util.c | 144 |
1 files changed, 110 insertions, 34 deletions
diff --git a/src/libnm-systemd-shared/src/basic/process-util.c b/src/libnm-systemd-shared/src/basic/process-util.c index a45e32bcc1..8601e0da54 100644 --- a/src/libnm-systemd-shared/src/basic/process-util.c +++ b/src/libnm-systemd-shared/src/basic/process-util.c @@ -40,6 +40,7 @@ #include "memory-util.h" #include "missing_sched.h" #include "missing_syscall.h" +#include "missing_threads.h" #include "mountpoint-util.h" #include "namespace-util.h" #include "nulstr-util.h" @@ -227,18 +228,12 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags _cleanup_strv_free_ char **args = NULL; - args = strv_parse_nulstr(t, k); + /* Drop trailing NULs, otherwise strv_parse_nulstr() adds additional empty strings at the end. + * See also issue #21186. */ + args = strv_parse_nulstr_full(t, k, /* drop_trailing_nuls = */ true); if (!args) return -ENOMEM; - /* Drop trailing empty strings. See issue #21186. */ - STRV_FOREACH_BACKWARDS(p, args) { - if (!isempty(*p)) - break; - - *p = mfree(*p); - } - ans = quote_command_line(args, shflags); if (!ans) return -ENOMEM; @@ -264,6 +259,28 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags return 0; } +int get_process_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret) { + _cleanup_free_ char *t = NULL; + char **args; + size_t k; + int r; + + assert(pid >= 0); + assert((flags & ~PROCESS_CMDLINE_COMM_FALLBACK) == 0); + assert(ret); + + r = get_process_cmdline_nulstr(pid, SIZE_MAX, flags, &t, &k); + if (r < 0) + return r; + + args = strv_parse_nulstr_full(t, k, /* drop_trailing_nuls = */ true); + if (!args) + return -ENOMEM; + + *ret = args; + return 0; +} + int container_get_leader(const char *machine, pid_t *pid) { _cleanup_free_ char *s = NULL, *class = NULL; const char *p; @@ -602,6 +619,8 @@ int get_process_umask(pid_t pid, mode_t *ret) { r = get_proc_field(p, "Umask", WHITESPACE, &m); if (r == -ENOENT) return -ESRCH; + if (r < 0) + return r; return parse_mode(m, ret); } @@ -933,7 +952,7 @@ int pid_from_same_root_fs(pid_t pid) { root = procfs_file_alloca(pid, "root"); - return files_same(root, "/proc/1/root", 0); + return inode_same(root, "/proc/1/root", 0); } #endif /* NM_IGNORED */ @@ -1142,6 +1161,7 @@ static void restore_sigsetp(sigset_t **ssp) { int safe_fork_full( const char *name, + const int stdio_fds[3], const int except_fds[], size_t n_except_fds, ForkFlags flags, @@ -1193,7 +1213,7 @@ int safe_fork_full( else pid = fork(); if (pid < 0) - return log_full_errno(prio, errno, "Failed to fork: %m"); + return log_full_errno(prio, errno, "Failed to fork off '%s': %m", strna(name)); if (pid > 0) { /* We are in the parent process */ @@ -1229,6 +1249,7 @@ int safe_fork_full( /* Close the logs if requested, before we log anything. And make sure we reopen it if needed. */ log_close(); log_set_open_when_needed(true); + log_settle_target(); } if (name) { @@ -1300,6 +1321,27 @@ int safe_fork_full( } } + if (flags & FORK_REARRANGE_STDIO) { + if (stdio_fds) { + r = rearrange_stdio(stdio_fds[0], stdio_fds[1], stdio_fds[2]); + if (r < 0) { + log_full_errno(prio, r, "Failed to rearrange stdio fds: %m"); + _exit(EXIT_FAILURE); + } + } else { + r = make_null_stdio(); + if (r < 0) { + log_full_errno(prio, r, "Failed to connect stdin/stdout to /dev/null: %m"); + _exit(EXIT_FAILURE); + } + } + } else if (flags & FORK_STDOUT_TO_STDERR) { + if (dup2(STDERR_FILENO, STDOUT_FILENO) < 0) { + log_full_errno(prio, errno, "Failed to connect stdout to stderr: %m"); + _exit(EXIT_FAILURE); + } + } + if (flags & FORK_CLOSE_ALL_FDS) { /* Close the logs here in case it got reopened above, as close_all_fds() would close them for us */ log_close(); @@ -1325,24 +1367,18 @@ int safe_fork_full( log_set_open_when_needed(false); } - if (flags & FORK_NULL_STDIO) { - r = make_null_stdio(); + if (flags & FORK_RLIMIT_NOFILE_SAFE) { + r = rlimit_nofile_safe(); if (r < 0) { - log_full_errno(prio, r, "Failed to connect stdin/stdout to /dev/null: %m"); - _exit(EXIT_FAILURE); - } - - } else if (flags & FORK_STDOUT_TO_STDERR) { - if (dup2(STDERR_FILENO, STDOUT_FILENO) < 0) { - log_full_errno(prio, errno, "Failed to connect stdout to stderr: %m"); + log_full_errno(prio, r, "Failed to lower RLIMIT_NOFILE's soft limit to 1K: %m"); _exit(EXIT_FAILURE); } } - if (flags & FORK_RLIMIT_NOFILE_SAFE) { - r = rlimit_nofile_safe(); + if (!FLAGS_SET(flags, FORK_KEEP_NOTIFY_SOCKET)) { + r = RET_NERRNO(unsetenv("NOTIFY_SOCKET")); if (r < 0) { - log_full_errno(prio, r, "Failed to lower RLIMIT_NOFILE's soft limit to 1K: %m"); + log_full_errno(prio, r, "Failed to unset $NOTIFY_SOCKET: %m"); _exit(EXIT_FAILURE); } } @@ -1372,7 +1408,10 @@ int namespace_fork( * process. This ensures that we are fully a member of the destination namespace, with pidns an all, so that * /proc/self/fd works correctly. */ - r = safe_fork_full(outer_name, except_fds, n_except_fds, (flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid); + r = safe_fork_full(outer_name, + NULL, + except_fds, n_except_fds, + (flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid); if (r < 0) return r; if (r == 0) { @@ -1387,7 +1426,10 @@ int namespace_fork( } /* We mask a few flags here that either make no sense for the grandchild, or that we don't have to do again */ - r = safe_fork_full(inner_name, except_fds, n_except_fds, flags & ~(FORK_WAIT|FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_NULL_STDIO), &pid); + r = safe_fork_full(inner_name, + NULL, + except_fds, n_except_fds, + flags & ~(FORK_WAIT|FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_REARRANGE_STDIO), &pid); if (r < 0) _exit(EXIT_FAILURE); if (r == 0) { @@ -1440,6 +1482,15 @@ int pidfd_get_pid(int fd, pid_t *ret) { char *p; int r; + /* Converts a pidfd into a pid. Well known errors: + * + * -EBADF → fd invalid + * -ENOSYS → /proc/ not mounted + * -ENOTTY → fd valid, but not a pidfd + * -EREMOTE → fd valid, but pid is in another namespace we cannot translate to the local one + * -ESRCH → fd valid, but process is already reaped + */ + if (fd < 0) return -EBADF; @@ -1447,22 +1498,22 @@ int pidfd_get_pid(int fd, pid_t *ret) { r = read_full_virtual_file(path, &fdinfo, NULL); if (r == -ENOENT) /* if fdinfo doesn't exist we assume the process does not exist */ - return -ESRCH; + return proc_mounted() > 0 ? -EBADF : -ENOSYS; if (r < 0) return r; - p = startswith(fdinfo, "Pid:"); - if (!p) { - p = strstr(fdinfo, "\nPid:"); - if (!p) - return -ENOTTY; /* not a pidfd? */ - - p += 5; - } + p = find_line_startswith(fdinfo, "Pid:"); + if (!p) + return -ENOTTY; /* not a pidfd? */ p += strspn(p, WHITESPACE); p[strcspn(p, WHITESPACE)] = 0; + if (streq(p, "0")) + return -EREMOTE; /* PID is in foreign PID namespace? */ + if (streq(p, "-1")) + return -ESRCH; /* refers to reaped process? */ + return parse_pid(p, ret); } @@ -1557,6 +1608,31 @@ _noreturn_ void freeze(void) { pause(); } +int get_process_threads(pid_t pid) { + _cleanup_free_ char *t = NULL; + const char *p; + int n, r; + + if (pid < 0) + return -EINVAL; + + p = procfs_file_alloca(pid, "status"); + + r = get_proc_field(p, "Threads", WHITESPACE, &t); + if (r == -ENOENT) + return proc_mounted() == 0 ? -ENOSYS : -ESRCH; + if (r < 0) + return r; + + r = safe_atoi(t, &n); + if (r < 0) + return r; + if (n < 0) + return -EINVAL; + + return n; +} + static const char *const sigchld_code_table[] = { [CLD_EXITED] = "exited", [CLD_KILLED] = "killed", |