diff --git a/src/basic/audit-util.c b/src/basic/audit-util.c index 4f50339d6d..0d664b0d8b 100644 --- a/src/basic/audit-util.c +++ b/src/basic/audit-util.c @@ -37,7 +37,7 @@ static int audit_read_field(const PidRef *pid, const char *field, char **ret) { _cleanup_free_ char *s = NULL; bool enoent = false; - r = read_virtual_file(p, SIZE_MAX, &s, /* ret_size= */ NULL); + r = read_full_virtual_file(p, &s, /* ret_size= */ NULL); if (r == -ENOENT) { if (proc_mounted() == 0) return -ENOSYS; diff --git a/src/basic/fileio.c b/src/basic/fileio.c index b3fdc05c12..c5eedc312b 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -889,74 +889,46 @@ int script_get_shebang_interpreter(const char *path, char **ret) { return 0; } -/** - * Retrieve one field from a file like /proc/self/status. pattern - * should not include whitespace or the delimiter (':'). pattern matches only - * the beginning of a line. Whitespace before ':' is skipped. Whitespace and - * zeros after the ':' will be skipped. field must be freed afterwards. - * terminator specifies the terminating characters of the field value (not - * included in the value). - */ -int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field) { - _cleanup_free_ char *status = NULL; - char *t, *f; +int get_proc_field(const char *path, const char *key, char **ret) { + _cleanup_fclose_ FILE *f = NULL; int r; - assert(terminator); - assert(filename); - assert(pattern); - assert(field); + /* Retrieve one field from a file like /proc/self/status. "key" matches the beginning of the line + * and should not include whitespace or the delimiter (':'). + * Whitespaces after the ':' will be skipped. Only the first element is returned + * (i.e. for /proc/meminfo line "MemTotal: 1024 kB" -> return "1024"). */ - r = read_full_virtual_file(filename, &status, NULL); + assert(path); + assert(key); + + r = fopen_unlocked(path, "re", &f); + if (r == -ENOENT && proc_mounted() == 0) + return -ENOSYS; if (r < 0) return r; - t = status; + for (;;) { + _cleanup_free_ char *line = NULL; - do { - bool pattern_ok; + r = read_line(f, LONG_LINE_MAX, &line); + if (r < 0) + return r; + if (r == 0) + return -ENODATA; - do { - t = strstr(t, pattern); - if (!t) - return -ENOENT; + char *l = startswith(line, key); + if (l && *l == ':') { + if (ret) { + char *s = strdupcspn(skip_leading_chars(l + 1, " \t"), WHITESPACE); + if (!s) + return -ENOMEM; - /* Check that pattern occurs in beginning of line. */ - pattern_ok = (t == status || t[-1] == '\n'); + *ret = s; + } - t += strlen(pattern); - - } while (!pattern_ok); - - t += strspn(t, " \t"); - if (!*t) - return -ENOENT; - - } while (*t != ':'); - - t++; - - if (*t) { - t += strspn(t, " \t"); - - /* Also skip zeros, because when this is used for - * capabilities, we don't want the zeros. This way the - * same capability set always maps to the same string, - * irrespective of the total capability set size. For - * other numbers it shouldn't matter. */ - t += strspn(t, "0"); - /* Back off one char if there's nothing but whitespace - and zeros */ - if (!*t || isspace(*t)) - t--; + return 0; + } } - - f = strdupcspn(t, terminator); - if (!f) - return -ENOMEM; - - *field = f; - return 0; } DIR* xopendirat(int dir_fd, const char *name, int flags) { diff --git a/src/basic/fileio.h b/src/basic/fileio.h index 49da9a677c..eff9880292 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -98,7 +98,7 @@ int verify_file_at(int dir_fd, const char *fn, const char *blob, bool accept_ext int script_get_shebang_interpreter(const char *path, char **ret); -int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field); +int get_proc_field(const char *path, const char *key, char **ret); DIR* xopendirat(int dir_fd, const char *name, int flags); diff --git a/src/basic/mountpoint-util.c b/src/basic/mountpoint-util.c index 6fc5e52a8e..23653160a0 100644 --- a/src/basic/mountpoint-util.c +++ b/src/basic/mountpoint-util.c @@ -141,7 +141,6 @@ int name_to_handle_at_try_fid( static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *ret_mnt_id) { char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)]; - _cleanup_free_ char *fdinfo = NULL; _cleanup_close_ int subfd = -EBADF; int r; @@ -158,19 +157,13 @@ static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *ret_mn xsprintf(path, "/proc/self/fdinfo/%i", subfd); } - r = read_full_virtual_file(path, &fdinfo, NULL); + _cleanup_free_ char *p = NULL; + r = get_proc_field(path, "mnt_id", &p); if (r == -ENOENT) - return proc_fd_enoent_errno(); + return -EBADF; if (r < 0) return r; - char *p = find_line_startswith(fdinfo, "mnt_id:"); - if (!p) - return -EBADMSG; - - p = skip_leading_chars(p, /* bad = */ NULL); - p[strcspn(p, WHITESPACE)] = 0; - return safe_atoi(p, ret_mnt_id); } diff --git a/src/basic/pidfd-util.c b/src/basic/pidfd-util.c index edd2848d07..52a371ba1a 100644 --- a/src/basic/pidfd-util.c +++ b/src/basic/pidfd-util.c @@ -89,26 +89,21 @@ static int pidfd_get_info(int fd, struct pidfd_info *info) { static int pidfd_get_pid_fdinfo(int fd, pid_t *ret) { char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)]; - _cleanup_free_ char *fdinfo = NULL; + _cleanup_free_ char *p = NULL; int r; assert(fd >= 0); xsprintf(path, "/proc/self/fdinfo/%i", fd); - r = read_full_virtual_file(path, &fdinfo, NULL); + r = get_proc_field(path, "Pid", &p); if (r == -ENOENT) - return proc_fd_enoent_errno(); + return -EBADF; + if (r == -ENODATA) /* not a pidfd? */ + return -ENOTTY; if (r < 0) return r; - char *p = find_line_startswith(fdinfo, "Pid:"); - if (!p) - return -ENOTTY; /* not a pidfd? */ - - p = skip_leading_chars(p, /* bad = */ NULL); - p[strcspn(p, WHITESPACE)] = 0; - if (streq(p, "0")) return -EREMOTE; /* PID is in foreign PID namespace? */ if (streq(p, "-1")) diff --git a/src/basic/process-util.c b/src/basic/process-util.c index d3c43c3ba4..945d95d7d6 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -507,48 +507,10 @@ int get_process_exe(pid_t pid, char **ret) { return 0; } -static int get_process_id(pid_t pid, const char *field, uid_t *ret) { - _cleanup_fclose_ FILE *f = NULL; - const char *p; +int pid_get_uid(pid_t pid, uid_t *ret) { int r; - assert(field); - assert(ret); - - if (pid < 0) - return -EINVAL; - - p = procfs_file_alloca(pid, "status"); - r = fopen_unlocked(p, "re", &f); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - - for (;;) { - _cleanup_free_ char *line = NULL; - char *l; - - r = read_stripped_line(f, LONG_LINE_MAX, &line); - if (r < 0) - return r; - if (r == 0) - break; - - l = startswith(line, field); - if (l) { - l += strspn(l, WHITESPACE); - - l[strcspn(l, WHITESPACE)] = 0; - - return parse_uid(l, ret); - } - } - - return -EIO; -} - -int pid_get_uid(pid_t pid, uid_t *ret) { + assert(pid >= 0); assert(ret); if (pid == 0 || pid == getpid_cached()) { @@ -556,7 +518,14 @@ int pid_get_uid(pid_t pid, uid_t *ret) { return 0; } - return get_process_id(pid, "Uid:", ret); + _cleanup_free_ char *v = NULL; + r = procfs_file_get_field(pid, "status", "Uid", &v); + if (r == -ENOENT) + return -ESRCH; + if (r < 0) + return r; + + return parse_uid(v, ret); } int pidref_get_uid(const PidRef *pid, uid_t *ret) { @@ -589,14 +558,24 @@ int pidref_get_uid(const PidRef *pid, uid_t *ret) { } int get_process_gid(pid_t pid, gid_t *ret) { + int r; + + assert(pid >= 0); + assert(ret); if (pid == 0 || pid == getpid_cached()) { *ret = getgid(); return 0; } - assert_cc(sizeof(uid_t) == sizeof(gid_t)); - return get_process_id(pid, "Gid:", ret); + _cleanup_free_ char *v = NULL; + r = procfs_file_get_field(pid, "status", "Gid", &v); + if (r == -ENOENT) + return -ESRCH; + if (r < 0) + return r; + + return parse_gid(v, ret); } int get_process_cwd(pid_t pid, char **ret) { @@ -862,15 +841,12 @@ int pidref_get_start_time(const PidRef *pid, usec_t *ret) { int get_process_umask(pid_t pid, mode_t *ret) { _cleanup_free_ char *m = NULL; - const char *p; int r; assert(pid >= 0); assert(ret); - p = procfs_file_alloca(pid, "status"); - - r = get_proc_field(p, "Umask", WHITESPACE, &m); + r = procfs_file_get_field(pid, "status", "Umask", &m); if (r == -ENOENT) return -ESRCH; if (r < 0) @@ -2071,17 +2047,14 @@ _noreturn_ void freeze(void) { 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); + r = procfs_file_get_field(pid, "status", "Threads", &t); if (r == -ENOENT) - return proc_mounted() == 0 ? -ENOSYS : -ESRCH; + return -ESRCH; if (r < 0) return r; diff --git a/src/basic/process-util.h b/src/basic/process-util.h index fd24ee41ce..9ae36cf609 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -12,6 +12,8 @@ #include #include "alloc-util.h" +#include "assert-util.h" +#include "fileio.h" #include "format-util.h" #include "macro.h" #include "pidref.h" @@ -22,16 +24,20 @@ pid_t _pid_ = (pid); \ const char *_field_ = (field); \ char *_r_; \ - if (_pid_ == 0) { \ - _r_ = newa(char, STRLEN("/proc/self/") + strlen(_field_) + 1); \ - strcpy(stpcpy(_r_, "/proc/self/"), _field_); \ - } else { \ + if (_pid_ == 0) \ + _r_ = strjoina("/proc/self/", _field_); \ + else { \ + assert(_pid_ > 0); \ _r_ = newa(char, STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + strlen(_field_) + 1); \ sprintf(_r_, "/proc/" PID_FMT "/%s", _pid_, _field_); \ } \ (const char*) _r_; \ }) +static inline int procfs_file_get_field(pid_t pid, const char *name, const char *key, char **ret) { + return get_proc_field(procfs_file_alloca(pid, name), key, ret); +} + typedef enum ProcessCmdlineFlags { PROCESS_CMDLINE_COMM_FALLBACK = 1 << 0, PROCESS_CMDLINE_USE_LOCALE = 1 << 1, diff --git a/src/basic/rlimit-util.c b/src/basic/rlimit-util.c index 5dc8b56a07..0032f23c26 100644 --- a/src/basic/rlimit-util.c +++ b/src/basic/rlimit-util.c @@ -471,12 +471,12 @@ int pid_getrlimit(pid_t pid, int resource, struct rlimit *ret) { const char *p = procfs_file_alloca(pid, "limits"); _cleanup_free_ char *limits = NULL; - r = read_full_virtual_file(p, &limits, NULL); + r = read_full_file(p, &limits, /* ret_size = */ NULL); if (r < 0) return -EPERM; /* propagate original permission error if we can't access the limits file */ _cleanup_strv_free_ char **l = NULL; - l = strv_split(limits, "\n"); + l = strv_split_newlines(limits); if (!l) return -ENOMEM; diff --git a/src/basic/virt.c b/src/basic/virt.c index 575ac917b0..4c5c5b3b9a 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -432,8 +432,8 @@ static Virtualization detect_vm_zvm(void) { _cleanup_free_ char *t = NULL; int r; - r = get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE, &t); - if (r == -ENOENT) + r = get_proc_field("/proc/sysinfo", "VM00 Control Program", &t); + if (IN_SET(r, -ENOENT, -ENODATA)) return VIRTUALIZATION_NONE; if (r < 0) return r; @@ -655,7 +655,7 @@ Virtualization detect_container(void) { /* proot doesn't use PID namespacing, so we can just check if we have a matching tracer for this * invocation without worrying about it being elsewhere. */ - r = get_proc_field("/proc/self/status", "TracerPid", WHITESPACE, &p); + r = get_proc_field("/proc/self/status", "TracerPid", &p); if (r < 0) log_debug_errno(r, "Failed to read our own trace PID, ignoring: %m"); else if (!streq(p, "0")) { diff --git a/src/libsystemd/sd-bus/bus-creds.c b/src/libsystemd/sd-bus/bus-creds.c index 1c6c5a545a..9e93712bfd 100644 --- a/src/libsystemd/sd-bus/bus-creds.c +++ b/src/libsystemd/sd-bus/bus-creds.c @@ -1060,7 +1060,7 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, PidRef *pidref, pid_t tid const char *p; p = procfs_file_alloca(pidref->pid, "cmdline"); - r = read_full_virtual_file(p, &c->cmdline, &c->cmdline_size); + r = read_full_file(p, &c->cmdline, &c->cmdline_size); if (r == -ENOENT) return -ESRCH; if (r < 0) { diff --git a/src/oom/oomd.c b/src/oom/oomd.c index f49accd779..15cbb3a2c3 100644 --- a/src/oom/oomd.c +++ b/src/oom/oomd.c @@ -123,7 +123,7 @@ static int run(int argc, char *argv[]) { int fd = n == 1 ? SD_LISTEN_FDS_START : -EBADF; /* SwapTotal is always available in /proc/meminfo and defaults to 0, even on swap-disabled kernels. */ - r = get_proc_field("/proc/meminfo", "SwapTotal", WHITESPACE, &swap); + r = get_proc_field("/proc/meminfo", "SwapTotal", &swap); if (r < 0) return log_error_errno(r, "Failed to get SwapTotal from /proc/meminfo: %m"); diff --git a/src/shared/hibernate-util.c b/src/shared/hibernate-util.c index 2aca788fb8..55ca4d5cf0 100644 --- a/src/shared/hibernate-util.c +++ b/src/shared/hibernate-util.c @@ -428,7 +428,7 @@ static int get_proc_meminfo_active(unsigned long long *ret) { assert(ret); - r = get_proc_field("/proc/meminfo", "Active(anon)", WHITESPACE, &active_str); + r = get_proc_field("/proc/meminfo", "Active(anon)", &active_str); if (r < 0) return log_debug_errno(r, "Failed to retrieve Active(anon) from /proc/meminfo: %m"); diff --git a/src/socket-activate/socket-activate.c b/src/socket-activate/socket-activate.c index e55f921c87..336d6fa754 100644 --- a/src/socket-activate/socket-activate.c +++ b/src/socket-activate/socket-activate.c @@ -49,7 +49,7 @@ static int add_epoll(int epoll_fd, int fd) { return 0; } -static int open_sockets(int *ret_epoll_fd, bool accept) { +static int open_sockets(int *ret_epoll_fd) { _cleanup_close_ int epoll_fd = -EBADF; int n, r, count = 0; @@ -463,7 +463,7 @@ static int run(int argc, char **argv) { assert(!strv_isempty(exec_argv)); - n = open_sockets(&epoll_fd, arg_accept); + n = open_sockets(&epoll_fd); if (n < 0) return n; if (n == 0) diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c index 67943b65ba..97eeb59afb 100644 --- a/src/test/test-fileio.c +++ b/src/test/test-fileio.c @@ -333,19 +333,19 @@ TEST(script_get_shebang_interpreter) { } TEST(status_field) { - _cleanup_free_ char *p = NULL, *s = NULL, *z = NULL; + _cleanup_free_ char *p = NULL, *s = NULL; unsigned long long total = 0, buffers = 0; int r; - r = get_proc_field("/proc/meminfo", "MemTotal", WHITESPACE, &p); - if (r != -ENOENT) { + r = get_proc_field("/proc/meminfo", "MemTotal", &p); + if (!IN_SET(r, -ENOENT, -ENOSYS)) { assert_se(r == 0); puts(p); assert_se(safe_atollu(p, &total) == 0); } - r = get_proc_field("/proc/meminfo", "Buffers", WHITESPACE, &s); - if (r != -ENOENT) { + r = get_proc_field("/proc/meminfo", "Buffers", &s); + if (!IN_SET(r, -ENOENT, -ENOSYS)) { assert_se(r == 0); puts(s); assert_se(safe_atollu(s, &buffers) == 0); @@ -353,14 +353,6 @@ TEST(status_field) { if (p) assert_se(buffers < total); - - /* Seccomp should be a good test for field full of zeros. */ - r = get_proc_field("/proc/meminfo", "Seccomp", WHITESPACE, &z); - if (r != -ENOENT) { - assert_se(r == 0); - puts(z); - assert_se(safe_atollu(z, &buffers) == 0); - } } TEST(read_one_line_file) { diff --git a/src/test/test-seccomp.c b/src/test/test-seccomp.c index d006e8b7b0..39a89d1e8d 100644 --- a/src/test/test-seccomp.c +++ b/src/test/test-seccomp.c @@ -382,7 +382,7 @@ TEST(protect_sysctl) { return; } - assert_se(get_proc_field("/proc/self/status", "Seccomp", WHITESPACE, &seccomp) == 0); + assert_se(get_proc_field("/proc/self/status", "Seccomp", &seccomp) == 0); if (!streq(seccomp, "0")) log_warning("Warning: seccomp filter detected, results may be unreliable for %s", __func__);