diff --git a/src/bootchart.c b/src/bootchart.c index 15aaa2e..dfe7258 100644 --- a/src/bootchart.c +++ b/src/bootchart.c @@ -44,6 +44,8 @@ #include #include #include +#include +#include #ifdef HAVE_LIBSYSTEMD #include @@ -310,6 +312,38 @@ static int do_journal_append(char *file) { return 0; } + +static int is_mountpoint(const char *path) { + struct stat s_path, s_parent; + char *path_aux, *parent_path; + + // Root is always a mountpoint + if (strcmp(path, "/") == 0) { + return 1; + } + + if (stat(path, &s_path) == -1) { + return -1; + } + + // Duplicate path as dirname can modify it + path_aux = strdup(path); + if (!path_aux) { + return -1; + } + + parent_path = dirname(path_aux); + if (stat(parent_path, &s_parent) == -1) { + free(path_aux); + return -1; + } + + free(path_aux); + + // mount point if the directory and its parent are on different devices (filesystems) + return (s_path.st_dev != s_parent.st_dev); +} + int main(int argc, char *argv[]) { static struct list_sample_data *sampledata; _cleanup_closedir_ DIR *proc = NULL; @@ -333,6 +367,7 @@ int main(int argc, char *argv[]) { .sa_handler = signal_handler, }; bool has_procfs = false; + bool sysroot_mounted = false; parse_conf(); @@ -433,6 +468,25 @@ int main(int argc, char *argv[]) { if (r < 0) return EXIT_FAILURE; } + if (!sysroot_mounted) { + // sysroot is where systemd will switch root when + // switching from initramfs to the root fs. We chroot + // there so we can write later the svg file. + // + // TODO instead of detecting this way, use signal from initramfs service. + sysroot_mounted = is_mountpoint("/sysroot") == 1; + if (sysroot_mounted) { + // chroot does not change the cwd of the process, so we do that first + if (chdir("/sysroot") != 0) { + log_info_errno(errno, "cannot chdir to /sysroot: %m\n"); + return EXIT_FAILURE; + } + if (chroot(".") != 0) { + log_info_errno(errno, "cannot chroot: %m\n"); + return EXIT_FAILURE; + } + } + } sample_stop = gettime_ns(); 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");