From b6741478e7661c7e580e5dcfd6a6fccd1899c1d0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 11 Dec 2013 22:04:03 +0100 Subject: [PATCH] journal: add ability to browse journals of running OS containers This adds the new library call sd_journal_open_container() and a new "-M" switch to journalctl. Particular care is taken that journalctl's "-b" switch resolves to the current boot ID of the container, not the host. --- TODO | 2 + man/journalctl.xml | 10 +++ man/sd_journal_open.xml | 18 ++++- src/journal/journalctl.c | 17 +++-- src/journal/libsystemd-journal.sym | 5 ++ src/journal/sd-journal.c | 66 +++++++++++++---- src/libsystemd-bus/bus-container.c | 8 +-- src/libsystemd-bus/sd-bus.c | 3 + src/shared/logs-show.c | 110 +++++++++++++++++++++++++++-- src/shared/logs-show.h | 2 +- src/systemd/sd-journal.h | 1 + 11 files changed, 211 insertions(+), 31 deletions(-) diff --git a/TODO b/TODO index dae1056f2..a4cffb3bb 100644 --- a/TODO +++ b/TODO @@ -30,6 +30,8 @@ External: Features: +* libsystemd-journal returns the object created as first param in sd_journal_new(), sd_bus_new() and suchlike as last... + * cgroups: - implement system-wide DefaultCPUAccounting=1 switch (and similar for blockio, memory?) - implement per-slice CPUFairScheduling=1 switch diff --git a/man/journalctl.xml b/man/journalctl.xml index 011fea280..952b37c53 100644 --- a/man/journalctl.xml +++ b/man/journalctl.xml @@ -647,6 +647,16 @@ + + + + + Show messages from a + running, local container. Specify a + container name to connect + to. + + diff --git a/man/sd_journal_open.xml b/man/sd_journal_open.xml index bb3703d01..b06863098 100644 --- a/man/sd_journal_open.xml +++ b/man/sd_journal_open.xml @@ -46,6 +46,7 @@ sd_journal_open sd_journal_open_directory sd_journal_open_files + sd_journal_open_container sd_journal_close sd_journal SD_JOURNAL_LOCAL_ONLY @@ -79,6 +80,13 @@ int flags + + int sd_journal_open_container + sd_journal** ret + const char* machine + int flags + + void sd_journal_close sd_journal* j @@ -131,6 +139,13 @@ can be rotated at any moment, and the opening of specific files is inherently racy. + sd_journal_open_container() + is similar to sd_journal_open() + but opens the journal files of a running + OS container. The specified machine name refers to a + container that is registered with + systemd-machined8. + sd_journal objects cannot be used in the child after a fork. Functions which take a journal object as an argument @@ -230,7 +245,8 @@ systemd1, sd-journal3, sd_journal_next3, - sd_journal_get_data3 + sd_journal_get_data3, + systemd-machined8 diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index f74dde9c4..5d12c2b16 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -93,6 +93,7 @@ static bool arg_catalog = false; static bool arg_reverse = false; static int arg_journal_type = 0; static const char *arg_root = NULL; +static const char *arg_machine = NULL; static enum { ACTION_SHOW, @@ -120,6 +121,7 @@ static int help(void) { "Flags:\n" " --system Show only the system journal\n" " --user Show only the user journal for current user\n" + " -M --machine=CONTAINER Operate on local container\n" " --since=DATE Start showing entries newer or of the specified date\n" " --until=DATE Stop showing entries older or of the specified date\n" " -c --cursor=CURSOR Start showing entries from specified cursor\n" @@ -247,6 +249,7 @@ static int parse_argv(int argc, char *argv[]) { { "dump-catalog", no_argument, NULL, ARG_DUMP_CATALOG }, { "update-catalog", no_argument, NULL, ARG_UPDATE_CATALOG }, { "reverse", no_argument, NULL, 'r' }, + { "machine", required_argument, NULL, 'M' }, {} }; @@ -255,7 +258,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:c:u:F:xr", options, NULL)) >= 0) { + while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:c:u:F:xrM:", options, NULL)) >= 0) { switch (c) { @@ -389,6 +392,10 @@ static int parse_argv(int argc, char *argv[]) { arg_journal_type |= SD_JOURNAL_CURRENT_USER; break; + case 'M': + arg_machine = optarg; + break; + case 'D': arg_directory = optarg; break; @@ -576,8 +583,8 @@ static int parse_argv(int argc, char *argv[]) { if (arg_follow && !arg_no_tail && arg_lines < 0) arg_lines = 10; - if (arg_directory && arg_file) { - log_error("Please specify either -D/--directory= or --file=, not both."); + if (!!arg_directory + !!arg_file + !!arg_machine > 1) { + log_error("Please specify either -D/--directory= or --file= or -M/--machine=, not more than one."); return -EINVAL; } @@ -881,7 +888,7 @@ static int add_boot(sd_journal *j) { return 0; if (!arg_boot_descriptor) - return add_match_this_boot(j); + return add_match_this_boot(j, arg_machine); if (strlen(arg_boot_descriptor) >= 32) { char tmp = arg_boot_descriptor[32]; @@ -1460,6 +1467,8 @@ int main(int argc, char *argv[]) { r = sd_journal_open_directory(&j, arg_directory, arg_journal_type); else if (arg_file) r = sd_journal_open_files(&j, (const char**) arg_file, 0); + else if (arg_machine) + r = sd_journal_open_container(&j, arg_machine, 0); else r = sd_journal_open(&j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type); if (r < 0) { diff --git a/src/journal/libsystemd-journal.sym b/src/journal/libsystemd-journal.sym index 4eb15910d..7f10f633f 100644 --- a/src/journal/libsystemd-journal.sym +++ b/src/journal/libsystemd-journal.sym @@ -109,3 +109,8 @@ LIBSYSTEMD_JOURNAL_205 { global: sd_journal_open_files; } LIBSYSTEMD_JOURNAL_202; + +LIBSYSTEMD_JOURNAL_209 { +global: + sd_journal_open_container; +} LIBSYSTEMD_JOURNAL_205; diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 2ba7ca455..b55cf37e5 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -41,6 +41,7 @@ #include "missing.h" #include "catalog.h" #include "replace-var.h" +#include "fileio.h" #define JOURNAL_FILES_MAX 1024 @@ -1465,7 +1466,7 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) return 0; } -static int add_root_directory(sd_journal *j, const char *p) { +static int add_root_directory(sd_journal *j, const char *p, const char *prefix) { _cleanup_closedir_ DIR *d = NULL; Directory *m; int r; @@ -1477,6 +1478,9 @@ static int add_root_directory(sd_journal *j, const char *p) { !path_startswith(p, "/run")) return -EINVAL; + if (prefix) + p = strappenda(prefix, p); + d = opendir(p); if (!d) return -errno; @@ -1576,7 +1580,7 @@ static int remove_directory(sd_journal *j, Directory *d) { return 0; } -static int add_search_paths(sd_journal *j) { +static int add_search_paths(sd_journal *j, const char *prefix) { int r; const char search_paths[] = "/run/log/journal\0" @@ -1589,7 +1593,7 @@ static int add_search_paths(sd_journal *j) { * what's actually accessible, and ignore the rest. */ NULSTR_FOREACH(p, search_paths) { - r = add_root_directory(j, p); + r = add_root_directory(j, p, prefix); if (r < 0 && r != -ENOENT) { r = set_put_error(j, r); if (r < 0) @@ -1619,7 +1623,7 @@ static int add_current_paths(sd_journal *j) { if (!dir) return -ENOMEM; - r = add_root_directory(j, dir); + r = add_root_directory(j, dir, NULL); if (r < 0) { set_put_error(j, r); return r; @@ -1684,18 +1688,13 @@ _public_ int sd_journal_open(sd_journal **ret, int flags) { int r; assert_return(ret, -EINVAL); - - if (flags & ~(SD_JOURNAL_LOCAL_ONLY| - SD_JOURNAL_RUNTIME_ONLY| - SD_JOURNAL_SYSTEM| - SD_JOURNAL_CURRENT_USER)) - return -EINVAL; + assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL); j = journal_new(flags, NULL); if (!j) return -ENOMEM; - r = add_search_paths(j); + r = add_search_paths(j, NULL); if (r < 0) goto fail; @@ -1708,6 +1707,45 @@ fail: return r; } +_public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) { + _cleanup_free_ char *root = NULL, *class = NULL; + sd_journal *j; + char *p; + int r; + + assert_return(machine, -EINVAL); + assert_return(ret, -EINVAL); + assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL); + assert_return(filename_is_safe(machine), -EINVAL); + + p = strappenda("/run/systemd/machines/", machine); + r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL); + if (r == -ENOENT) + return -EHOSTDOWN; + if (r < 0) + return r; + if (!root) + return -ENODATA; + + if (!streq_ptr(class, "container")) + return -EIO; + + j = journal_new(flags, NULL); + if (!j) + return -ENOMEM; + + r = add_search_paths(j, root); + if (r < 0) + goto fail; + + *ret = j; + return 0; + +fail: + sd_journal_close(j); + return r; +} + _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) { sd_journal *j; int r; @@ -1720,7 +1758,7 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f if (!j) return -ENOMEM; - r = add_root_directory(j, path); + r = add_root_directory(j, path, NULL); if (r < 0) { set_put_error(j, r); goto fail; @@ -2083,9 +2121,9 @@ _public_ int sd_journal_get_fd(sd_journal *j) { if (j->no_new_files) r = add_current_paths(j); else if (j->path) - r = add_root_directory(j, j->path); + r = add_root_directory(j, j->path, NULL); else - r = add_search_paths(j); + r = add_search_paths(j, NULL); if (r < 0) return r; diff --git a/src/libsystemd-bus/bus-container.c b/src/libsystemd-bus/bus-container.c index 25ea471a0..33478c02d 100644 --- a/src/libsystemd-bus/bus-container.c +++ b/src/libsystemd-bus/bus-container.c @@ -29,8 +29,9 @@ #include "bus-container.h" int bus_container_connect(sd_bus *b) { - _cleanup_free_ char *p = NULL, *s = NULL, *ns = NULL, *root = NULL, *class = NULL; + _cleanup_free_ char *s = NULL, *ns = NULL, *root = NULL, *class = NULL; _cleanup_close_ int nsfd = -1, rootfd = -1; + char *p; siginfo_t si; pid_t leader, child; int r; @@ -39,10 +40,7 @@ int bus_container_connect(sd_bus *b) { assert(b->input_fd < 0); assert(b->output_fd < 0); - p = strappend("/run/systemd/machines/", b->machine); - if (!p) - return -ENOMEM; - + p = strappenda("/run/systemd/machines/", b->machine); r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL); if (r == -ENOENT) return -EHOSTDOWN; diff --git a/src/libsystemd-bus/sd-bus.c b/src/libsystemd-bus/sd-bus.c index 0a2ce3eb8..f991a0927 100644 --- a/src/libsystemd-bus/sd-bus.c +++ b/src/libsystemd-bus/sd-bus.c @@ -764,6 +764,9 @@ static int parse_container_address(sd_bus *b, const char **p, char **guid) { if (!machine) return -EINVAL; + if (!filename_is_safe(machine)) + return -EINVAL; + free(b->machine); b->machine = machine; machine = NULL; diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index 7bb19b400..c99fc7569 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -24,12 +24,14 @@ #include #include #include +#include #include "logs-show.h" #include "log.h" #include "util.h" #include "utf8.h" #include "hashmap.h" +#include "fileio.h" #include "journal-internal.h" /* up to three lines (each up to 100 characters), @@ -1112,17 +1114,113 @@ int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) { return r; } -int add_match_this_boot(sd_journal *j) { +static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) { + _cleanup_free_ char *leader = NULL, *class = NULL; + _cleanup_close_pipe_ int sock[2] = { -1, -1 }; + _cleanup_close_ int nsfd = -1; + const char *p, *ns; + pid_t pid, child; + siginfo_t si; + char buf[37]; + ssize_t k; + int r; + + assert(machine); + assert(boot_id); + + if (!filename_is_safe(machine)) + return -EINVAL; + + p = strappenda("/run/systemd/machines/", machine); + + r = parse_env_file(p, NEWLINE, "LEADER", &leader, "CLASS", &class, NULL); + if (r < 0) + return r; + if (!leader) + return -ENODATA; + if (!streq_ptr(class, "container")) + return -EIO; + r = parse_pid(leader, &pid); + if (r < 0) + return r; + + ns = procfs_file_alloca(pid, "ns/mnt"); + + nsfd = open(ns, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (nsfd < 0) + return -errno; + + if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sock) < 0) + return -errno; + + child = fork(); + if (child < 0) + return -errno; + + if (child == 0) { + int fd; + + close_nointr_nofail(sock[0]); + sock[0] = -1; + + r = setns(nsfd, CLONE_NEWNS); + if (r < 0) + _exit(EXIT_FAILURE); + + fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + _exit(EXIT_FAILURE); + + k = loop_read(fd, buf, 36, false); + close_nointr_nofail(fd); + if (k != 36) + _exit(EXIT_FAILURE); + + k = send(sock[1], buf, 36, MSG_NOSIGNAL); + if (k != 36) + _exit(EXIT_FAILURE); + + _exit(EXIT_SUCCESS); + } + + close_nointr_nofail(sock[1]); + sock[1] = -1; + + k = recv(sock[0], buf, 36, 0); + if (k != 36) + return -EIO; + + r = wait_for_terminate(child, &si); + if (r < 0 || si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + return r < 0 ? r : -EIO; + + buf[36] = 0; + r = sd_id128_from_string(buf, boot_id); + if (r < 0) + return r; + + return 0; +} + +int add_match_this_boot(sd_journal *j, const char *machine) { char match[9+32+1] = "_BOOT_ID="; sd_id128_t boot_id; int r; assert(j); - r = sd_id128_get_boot(&boot_id); - if (r < 0) { - log_error("Failed to get boot id: %s", strerror(-r)); - return r; + if (machine) { + r = get_boot_id_for_machine(machine, &boot_id); + if (r < 0) { + log_error("Failed to get boot id of container %s: %s", machine, strerror(-r)); + return r; + } + } else { + r = sd_id128_get_boot(&boot_id); + if (r < 0) { + log_error("Failed to get boot id: %s", strerror(-r)); + return r; + } } sd_id128_to_string(boot_id, match + 9); @@ -1166,7 +1264,7 @@ int show_journal_by_unit( if (r < 0) return r; - r = add_match_this_boot(j); + r = add_match_this_boot(j, NULL); if (r < 0) return r; diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h index 11b3b59b7..3a99160f1 100644 --- a/src/shared/logs-show.h +++ b/src/shared/logs-show.h @@ -38,7 +38,7 @@ int output_journal( OutputFlags flags, bool *ellipsized); -int add_match_this_boot(sd_journal *j); +int add_match_this_boot(sd_journal *j, const char *machine); int add_matches_for_unit( sd_journal *j, diff --git a/src/systemd/sd-journal.h b/src/systemd/sd-journal.h index 751e7f9ea..ae9b91a6f 100644 --- a/src/systemd/sd-journal.h +++ b/src/systemd/sd-journal.h @@ -86,6 +86,7 @@ enum { int sd_journal_open(sd_journal **ret, int flags); int sd_journal_open_directory(sd_journal **ret, const char *path, int flags); int sd_journal_open_files(sd_journal **ret, const char **paths, int flags); +int sd_journal_open_container(sd_journal **ret, const char *machine, int flags); void sd_journal_close(sd_journal *j); int sd_journal_previous(sd_journal *j); -- 2.30.2