chiark / gitweb /
journal: add ability to browse journals of running OS containers
authorLennart Poettering <lennart@poettering.net>
Wed, 11 Dec 2013 21:04:03 +0000 (22:04 +0100)
committerLennart Poettering <lennart@poettering.net>
Wed, 11 Dec 2013 21:04:03 +0000 (22:04 +0100)
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
man/journalctl.xml
man/sd_journal_open.xml
src/journal/journalctl.c
src/journal/libsystemd-journal.sym
src/journal/sd-journal.c
src/libsystemd-bus/bus-container.c
src/libsystemd-bus/sd-bus.c
src/shared/logs-show.c
src/shared/logs-show.h
src/systemd/sd-journal.h

diff --git a/TODO b/TODO
index dae1056f28a31e05ce3a1b0ab53dfa4540d5bd9d..a4cffb3bb380812b40d3e6834a7c30759ad0a3d7 100644 (file)
--- 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
index 011fea28004e9f32168aa6f400518076b2a1686b..952b37c531b5453af4fb8674bcd0fb0618300a73 100644 (file)
                                 </para></listitem>
                         </varlistentry>
 
+                        <varlistentry>
+                                <term><option>-M</option></term>
+                                <term><option>--machine=</option></term>
+
+                                <listitem><para>Show messages from a
+                                running, local container. Specify a
+                                container name to connect
+                                to.</para></listitem>
+                        </varlistentry>
+
                         <varlistentry>
                                 <term><option>-D <replaceable>DIR</replaceable></option></term>
                                 <term><option>--directory=<replaceable>DIR</replaceable></option></term>
index bb3703d015d62bae8342cf40db6a67a4cd6dd6a3..b0686309827ac9a6dd0a67b37f82d047adadb4a3 100644 (file)
@@ -46,6 +46,7 @@
                 <refname>sd_journal_open</refname>
                 <refname>sd_journal_open_directory</refname>
                 <refname>sd_journal_open_files</refname>
+                <refname>sd_journal_open_container</refname>
                 <refname>sd_journal_close</refname>
                 <refname>sd_journal</refname>
                 <refname>SD_JOURNAL_LOCAL_ONLY</refname>
                                 <paramdef>int <parameter>flags</parameter></paramdef>
                         </funcprototype>
 
+                        <funcprototype>
+                                <funcdef>int <function>sd_journal_open_container</function></funcdef>
+                                <paramdef>sd_journal** <parameter>ret</parameter></paramdef>
+                                <paramdef>const char* <parameter>machine</parameter></paramdef>
+                                <paramdef>int <parameter>flags</parameter></paramdef>
+                        </funcprototype>
+
                         <funcprototype>
                                 <funcdef>void <function>sd_journal_close</function></funcdef>
                                 <paramdef>sd_journal* <parameter>j</parameter></paramdef>
                 can be rotated at any moment, and the opening of
                 specific files is inherently racy.</para>
 
+                <para><function>sd_journal_open_container()</function>
+                is similar to <function>sd_journal_open()</function>
+                but opens the journal files of a running
+                OS container. The specified machine name refers to a
+                container that is registered with
+                <citerefentry><refentrytitle>systemd-machined</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+
                 <para><varname>sd_journal</varname> objects cannot be
                 used in the child after a fork. Functions which take a
                 journal object as an argument
                         <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
                         <citerefentry><refentrytitle>sd-journal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
                         <citerefentry><refentrytitle>sd_journal_next</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-                        <citerefentry><refentrytitle>sd_journal_get_data</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+                        <citerefentry><refentrytitle>sd_journal_get_data</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+                        <citerefentry><refentrytitle>systemd-machined</refentrytitle><manvolnum>8</manvolnum></citerefentry>
                 </para>
         </refsect1>
 
index f74dde9c485e443e3e05354f1c57d24875cc1c54..5d12c2b162ad83cb3dae2e7ead3707324ff6f338 100644 (file)
@@ -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) {
index 4eb15910d2ebb096ee63db4bbbd4407e684b295c..7f10f633f64e004c8c25ee9d071d2a8147945723 100644 (file)
@@ -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;
index 2ba7ca4559988a9573f8358bf23b7e2217a7b49b..b55cf37e50d93c609b0726e065d5bab4d7a990c2 100644 (file)
@@ -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;
 
index 25ea471a08475085d00b0c2166f554a02e429b18..33478c02decdc925242eb8d476d7ae05b1d15d1e 100644 (file)
@@ -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;
index 0a2ce3eb8dda1efc48af91a189e4315fda52f898..f991a09279eb7efef10ca18834c3dd84ef3fce96 100644 (file)
@@ -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;
index 7bb19b4006bce9524e066ddf840123ad1b4be1b5..c99fc7569429d94324b1c5874baefc349591160e 100644 (file)
 #include <errno.h>
 #include <sys/poll.h>
 #include <string.h>
+#include <fcntl.h>
 
 #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;
 
index 11b3b59b7bef814e8ac53f2f89c3faeb781f2dd4..3a99160f1eec91ba468dcbec071c09fa75257602 100644 (file)
@@ -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,
index 751e7f9ea7a2cf99b26eb0315eeb0ecd464df887..ae9b91a6f9f028e5093593c1a21ebfde7d803300 100644 (file)
@@ -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);