diff --git a/src/store.c b/src/store.c index 785d408..9670042 100644 --- a/src/store.c +++ b/src/store.c @@ -20,6 +20,8 @@ along with systemd; If not, see . ***/ +#define _POSIX_C_SOURCE + #include #include #include @@ -29,6 +31,7 @@ #include #include #include +#include #include "alloc-util.h" #include "bootchart.h" @@ -119,6 +122,68 @@ static void garbage_collect_dead_processes(struct ps_struct *ps_first) { } } +static int proc_read_full_file(int procfd, const char *fn, char **contents, size_t *size) { + int fd = -1; + size_t n, l; + _cleanup_free_ char *buf = NULL; + struct stat st; + + fd = openat(procfd, fn, O_RDONLY|O_CLOEXEC); + if (fd < 0) + return log_error_errno(errno, "Failed to openat /proc/%s: %m", fn); + + if (fstat(fd, &st) < 0) + return log_error_errno(errno, "Failed to fstat /proc/%s: %m", fn); + + n = LINE_MAX; + if (S_ISREG(st.st_mode)) { + + /* Safety check */ + if (st.st_size > 4*1024*1024) + return -E2BIG; + + /* Start with the right file size, but be prepared for + * files from /proc which generally report a file size + * of 0 */ + if (st.st_size > 0) + n = st.st_size; + } + + l = 0; + for (;;) { + char *t; + ssize_t k; + + t = realloc(buf, n+1); + if (!t) + return -ENOMEM; + + buf = t; + k = pread(fd, buf + l, n - l, l); + + if (k < 0) + return log_error_errno(errno, "Failed to pread /proc/%s: %m", fn); + if (k == 0) + break; + + l += k; + n *= 2; + + /* Safety check */ + if (n > 4*1024*1024) + return -E2BIG; + } + + buf[l] = 0; + *contents = buf; + buf = NULL; /* do not free */ + + if (size) + *size = l; + + safe_close(fd); + return 0; +} int log_sample(DIR *proc, int sample, @@ -189,7 +254,7 @@ int log_sample(DIR *proc, } /* Parse "/proc/schedstat" for overall CPU utilization */ - r = read_full_file("/proc/schedstat", &buf_schedstat, NULL); + r = proc_read_full_file(procfd, "schedstat", &buf_schedstat, NULL); if (r < 0) return log_error_errno(r, "Unable to read schedstat: %m");