various: convert more readers of /proc/ to plain read_full_file() (#37299)
Continuation of #36734 Apparently I was wrong about everything under `/proc/` being seq_file, but at least there're some more to convert and we can leverage our helper func while doing so.
This commit is contained in:
@@ -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;
|
||||
|
||||
+29
-57
@@ -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) {
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+5
-10
@@ -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"))
|
||||
|
||||
+25
-52
@@ -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;
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
+3
-3
@@ -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")) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+1
-1
@@ -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");
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
+5
-13
@@ -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) {
|
||||
|
||||
@@ -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__);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user