chiark / gitweb /
Add utility function to append root to path
[elogind.git] / src / journal / journalctl.c
index cb252eb0b89eaf282e0cbd2ce7c2efa7a58e821d..78495f8c9d31903b0485cb3f101feb78638956a4 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <locale.h>
 #include <fcntl.h>
 
 #include <locale.h>
 #include <fcntl.h>
+#include <fnmatch.h>
 #include <errno.h>
 #include <stddef.h>
 #include <string.h>
 #include <errno.h>
 #include <stddef.h>
 #include <string.h>
@@ -49,6 +50,7 @@
 #include "build.h"
 #include "pager.h"
 #include "strv.h"
 #include "build.h"
 #include "pager.h"
 #include "strv.h"
+#include "set.h"
 #include "journal-internal.h"
 #include "journal-def.h"
 #include "journal-verify.h"
 #include "journal-internal.h"
 #include "journal-def.h"
 #include "journal-verify.h"
@@ -71,7 +73,8 @@ static bool arg_no_tail = false;
 static bool arg_quiet = false;
 static bool arg_merge = false;
 static bool arg_boot = false;
 static bool arg_quiet = false;
 static bool arg_merge = false;
 static bool arg_boot = false;
-static char *arg_boot_descriptor = NULL;
+static sd_id128_t arg_boot_id = {};
+static int arg_boot_offset = 0;
 static bool arg_dmesg = false;
 static const char *arg_cursor = NULL;
 static const char *arg_after_cursor = NULL;
 static bool arg_dmesg = false;
 static const char *arg_cursor = NULL;
 static const char *arg_after_cursor = NULL;
@@ -122,6 +125,41 @@ static void pager_open_if_enabled(void) {
         pager_open(arg_pager_end);
 }
 
         pager_open(arg_pager_end);
 }
 
+static int parse_boot_descriptor(const char *x, sd_id128_t *boot_id, int *offset) {
+        sd_id128_t id = SD_ID128_NULL;
+        int off = 0, r;
+
+        if (strlen(x) >= 32) {
+                char *t;
+
+                t = strndupa(x, 32);
+                r = sd_id128_from_string(t, &id);
+                if (r >= 0)
+                        x += 32;
+
+                if (*x != '-' && *x != '+' && *x != 0)
+                        return -EINVAL;
+
+                if (*x != 0) {
+                        r = safe_atoi(x, &off);
+                        if (r < 0)
+                                return r;
+                }
+        } else {
+                r = safe_atoi(x, &off);
+                if (r < 0)
+                        return r;
+        }
+
+        if (boot_id)
+                *boot_id = id;
+
+        if (offset)
+                *offset = off;
+
+        return 0;
+}
+
 static int help(void) {
 
         pager_open_if_enabled();
 static int help(void) {
 
         pager_open_if_enabled();
@@ -129,8 +167,8 @@ static int help(void) {
         printf("%s [OPTIONS...] [MATCHES...]\n\n"
                "Query the journal.\n\n"
                "Flags:\n"
         printf("%s [OPTIONS...] [MATCHES...]\n\n"
                "Query the journal.\n\n"
                "Flags:\n"
-               "     --system              Show only the system journal\n"
-               "     --user                Show only the user journal for the current user\n"
+               "     --system              Show the system journal\n"
+               "     --user                Show the user journal for the current user\n"
                "  -M --machine=CONTAINER   Operate on local container\n"
                "     --since=DATE          Start showing entries on or newer than the specified date\n"
                "     --until=DATE          Stop showing entries on or older than the specified date\n"
                "  -M --machine=CONTAINER   Operate on local container\n"
                "     --since=DATE          Start showing entries on or newer than the specified date\n"
                "     --until=DATE          Stop showing entries on or older than the specified date\n"
@@ -372,16 +410,23 @@ static int parse_argv(int argc, char *argv[]) {
                 case 'b':
                         arg_boot = true;
 
                 case 'b':
                         arg_boot = true;
 
-                        if (optarg)
-                                arg_boot_descriptor = optarg;
-                        else if (optind < argc) {
-                                int boot;
+                        if (optarg) {
+                                r =  parse_boot_descriptor(optarg, &arg_boot_id, &arg_boot_offset);
+                                if (r < 0) {
+                                        log_error("Failed to parse boot descriptor '%s'", optarg);
+                                        return -EINVAL;
+                                }
+                        } else {
 
 
-                                if (argv[optind][0] != '-' ||
-                                    safe_atoi(argv[optind], &boot) >= 0) {
-                                        arg_boot_descriptor = argv[optind];
+                                /* Hmm, no argument? Maybe the next
+                                 * word on the command line is
+                                 * supposed to be the argument? Let's
+                                 * see if there is one and is parsable
+                                 * as a boot descriptor... */
+
+                                if (optind < argc &&
+                                    parse_boot_descriptor(argv[optind], &arg_boot_id, &arg_boot_offset) >= 0)
                                         optind++;
                                         optind++;
-                                }
                         }
 
                         break;
                         }
 
                         break;
@@ -613,6 +658,11 @@ static int parse_argv(int argc, char *argv[]) {
                 return -EINVAL;
         }
 
                 return -EINVAL;
         }
 
+        if (arg_action != ACTION_SHOW && optind < argc) {
+                log_error("Extraneous arguments starting with '%s'", argv[optind]);
+                return -EINVAL;
+        }
+
         return 1;
 }
 
         return 1;
 }
 
@@ -649,15 +699,20 @@ static int generate_new_id128(void) {
 
 static int add_matches(sd_journal *j, char **args) {
         char **i;
 
 static int add_matches(sd_journal *j, char **args) {
         char **i;
+        bool have_term = false;
 
         assert(j);
 
         STRV_FOREACH(i, args) {
                 int r;
 
 
         assert(j);
 
         STRV_FOREACH(i, args) {
                 int r;
 
-                if (streq(*i, "+"))
+                if (streq(*i, "+")) {
+                        if (!have_term)
+                                break;
                         r = sd_journal_add_disjunction(j);
                         r = sd_journal_add_disjunction(j);
-                else if (path_is_absolute(*i)) {
+                        have_term = false;
+
+                } else if (path_is_absolute(*i)) {
                         _cleanup_free_ char *p, *t = NULL, *t2 = NULL;
                         const char *path;
                         _cleanup_free_ char *interpreter = NULL;
                         _cleanup_free_ char *p, *t = NULL, *t2 = NULL;
                         const char *path;
                         _cleanup_free_ char *interpreter = NULL;
@@ -682,7 +737,7 @@ static int add_matches(sd_journal *j, char **args) {
                                         t = strappend("_COMM=", comm);
 
                                         /* Append _EXE only if the interpreter is not a link.
                                         t = strappend("_COMM=", comm);
 
                                         /* Append _EXE only if the interpreter is not a link.
-                                           Otherwise it might be outdated often. */
+                                           Otherwise, it might be outdated often. */
                                         if (lstat(interpreter, &st) == 0 &&
                                             !S_ISLNK(st.st_mode)) {
                                                 t2 = strappend("_EXE=", interpreter);
                                         if (lstat(interpreter, &st) == 0 &&
                                             !S_ISLNK(st.st_mode)) {
                                                 t2 = strappend("_EXE=", interpreter);
@@ -691,11 +746,17 @@ static int add_matches(sd_journal *j, char **args) {
                                         }
                                 } else
                                         t = strappend("_EXE=", path);
                                         }
                                 } else
                                         t = strappend("_EXE=", path);
-                        } else if (S_ISCHR(st.st_mode))
-                                asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
-                        else if (S_ISBLK(st.st_mode))
-                                asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
-                        else {
+                        } else if (S_ISCHR(st.st_mode)) {
+                                if (asprintf(&t, "_KERNEL_DEVICE=c%u:%u",
+                                             major(st.st_rdev),
+                                             minor(st.st_rdev)) < 0)
+                                        return -ENOMEM;
+                        } else if (S_ISBLK(st.st_mode)) {
+                                if (asprintf(&t, "_KERNEL_DEVICE=b%u:%u",
+                                             major(st.st_rdev),
+                                             minor(st.st_rdev)) < 0)
+                                        return -ENOMEM;
+                        } else {
                                 log_error("File is neither a device node, nor regular file, nor executable: %s", *i);
                                 return -EINVAL;
                         }
                                 log_error("File is neither a device node, nor regular file, nor executable: %s", *i);
                                 return -EINVAL;
                         }
@@ -706,8 +767,12 @@ static int add_matches(sd_journal *j, char **args) {
                         r = sd_journal_add_match(j, t, 0);
                         if (t2)
                                 r = sd_journal_add_match(j, t2, 0);
                         r = sd_journal_add_match(j, t, 0);
                         if (t2)
                                 r = sd_journal_add_match(j, t2, 0);
-                } else
+                        have_term = true;
+
+                } else {
                         r = sd_journal_add_match(j, *i, 0);
                         r = sd_journal_add_match(j, *i, 0);
+                        have_term = true;
+                }
 
                 if (r < 0) {
                         log_error("Failed to add match '%s': %s", *i, strerror(-r));
 
                 if (r < 0) {
                         log_error("Failed to add match '%s': %s", *i, strerror(-r));
@@ -715,6 +780,11 @@ static int add_matches(sd_journal *j, char **args) {
                 }
         }
 
                 }
         }
 
+        if (!strv_isempty(args) && !have_term) {
+                log_error("\"+\" can only be used between terms");
+                return -EINVAL;
+        }
+
         return 0;
 }
 
         return 0;
 }
 
@@ -819,9 +889,6 @@ static int get_relative_boot_id(sd_journal *j, sd_id128_t *boot_id, int relative
         assert(j);
         assert(boot_id);
 
         assert(j);
         assert(boot_id);
 
-        if (relative == 0 && !sd_id128_equal(*boot_id, SD_ID128_NULL))
-                return 0;
-
         r = sd_journal_query_unique(j, "_BOOT_ID");
         if (r < 0)
                 return r;
         r = sd_journal_query_unique(j, "_BOOT_ID");
         if (r < 0)
                 return r;
@@ -888,58 +955,27 @@ static int get_relative_boot_id(sd_journal *j, sd_id128_t *boot_id, int relative
 
 static int add_boot(sd_journal *j) {
         char match[9+32+1] = "_BOOT_ID=";
 
 static int add_boot(sd_journal *j) {
         char match[9+32+1] = "_BOOT_ID=";
-        char *offset;
-        sd_id128_t boot_id = SD_ID128_NULL;
-        int r, relative = 0;
+        int r;
 
         assert(j);
 
         if (!arg_boot)
                 return 0;
 
 
         assert(j);
 
         if (!arg_boot)
                 return 0;
 
-        if (!arg_boot_descriptor)
+        if (arg_boot_offset == 0 && sd_id128_equal(arg_boot_id, SD_ID128_NULL))
                 return add_match_this_boot(j, arg_machine);
 
                 return add_match_this_boot(j, arg_machine);
 
-        if (strlen(arg_boot_descriptor) >= 32) {
-                char tmp = arg_boot_descriptor[32];
-                arg_boot_descriptor[32] = '\0';
-                r = sd_id128_from_string(arg_boot_descriptor, &boot_id);
-                arg_boot_descriptor[32] = tmp;
-
-                if (r < 0) {
-                        log_error("Failed to parse boot ID '%.32s': %s",
-                                  arg_boot_descriptor, strerror(-r));
-                        return r;
-                }
-
-                offset = arg_boot_descriptor + 32;
-
-                if (*offset && *offset != '-' && *offset != '+') {
-                        log_error("Relative boot ID offset must start with a '+' or a '-', found '%s' ", offset);
-                        return -EINVAL;
-                }
-        } else
-                offset = arg_boot_descriptor;
-
-        if (*offset) {
-                r = safe_atoi(offset, &relative);
-                if (r < 0) {
-                        log_error("Failed to parse relative boot ID number '%s'", offset);
-                        return -EINVAL;
-                }
-        }
-
-        r = get_relative_boot_id(j, &boot_id, relative);
+        r = get_relative_boot_id(j, &arg_boot_id, arg_boot_offset);
         if (r < 0) {
         if (r < 0) {
-                if (sd_id128_equal(boot_id, SD_ID128_NULL))
-                        log_error("Failed to look up boot %+d: %s", relative, strerror(-r));
+                if (sd_id128_equal(arg_boot_id, SD_ID128_NULL))
+                        log_error("Failed to look up boot %+i: %s", arg_boot_offset, strerror(-r));
                 else
                 else
-                        log_error("Failed to look up boot ID "SD_ID128_FORMAT_STR"%+d: %s",
-                                  SD_ID128_FORMAT_VAL(boot_id), relative, strerror(-r));
+                        log_error("Failed to look up boot ID "SD_ID128_FORMAT_STR"%+i: %s",
+                                  SD_ID128_FORMAT_VAL(arg_boot_id), arg_boot_offset, strerror(-r));
                 return r;
         }
 
                 return r;
         }
 
-        sd_id128_to_string(boot_id, match + 9);
+        sd_id128_to_string(arg_boot_id, match + 9);
 
         r = sd_journal_add_match(j, match, sizeof(match) - 1);
         if (r < 0) {
 
         r = sd_journal_add_match(j, match, sizeof(match) - 1);
         if (r < 0) {
@@ -974,40 +1010,177 @@ static int add_dmesg(sd_journal *j) {
         return 0;
 }
 
         return 0;
 }
 
-static int add_units(sd_journal *j) {
-        _cleanup_free_ char *u = NULL;
+static int get_possible_units(sd_journal *j,
+                              const char *fields,
+                              char **patterns,
+                              Set **units) {
+        _cleanup_set_free_free_ Set *found;
+        const char *field;
         int r;
         int r;
+
+        found = set_new(string_hash_func, string_compare_func);
+        if (!found)
+                return log_oom();
+
+        NULSTR_FOREACH(field, fields) {
+                const void *data;
+                size_t size;
+
+                r = sd_journal_query_unique(j, field);
+                if (r < 0)
+                        return r;
+
+                SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
+                        char **pattern, *eq;
+                        size_t prefix;
+                        _cleanup_free_ char *u = NULL;
+
+                        eq = memchr(data, '=', size);
+                        if (eq)
+                                prefix = eq - (char*) data + 1;
+                        else
+                                prefix = 0;
+
+                        u = strndup((char*) data + prefix, size - prefix);
+                        if (!u)
+                                return log_oom();
+
+                        STRV_FOREACH(pattern, patterns)
+                                if (fnmatch(*pattern, u, FNM_NOESCAPE) == 0) {
+                                        log_debug("Matched %s with pattern %s=%s", u, field, *pattern);
+
+                                        r = set_consume(found, u);
+                                        u = NULL;
+                                        if (r < 0 && r != -EEXIST)
+                                                return r;
+
+                                        break;
+                                }
+                }
+        }
+
+        *units = found;
+        found = NULL;
+        return 0;
+}
+
+/* This list is supposed to return the superset of unit names
+ * possibly matched by rules added with add_matches_for_unit... */
+#define SYSTEM_UNITS                 \
+        "_SYSTEMD_UNIT\0"            \
+        "COREDUMP_UNIT\0"            \
+        "UNIT\0"                     \
+        "OBJECT_SYSTEMD_UNIT\0"      \
+        "_SYSTEMD_SLICE\0"
+
+/* ... and add_matches_for_user_unit */
+#define USER_UNITS                   \
+        "_SYSTEMD_USER_UNIT\0"       \
+        "USER_UNIT\0"                \
+        "COREDUMP_USER_UNIT\0"       \
+        "OBJECT_SYSTEMD_USER_UNIT\0"
+
+static int add_units(sd_journal *j) {
+        _cleanup_strv_free_ char **patterns = NULL;
+        int r, count = 0;
         char **i;
 
         assert(j);
 
         STRV_FOREACH(i, arg_system_units) {
         char **i;
 
         assert(j);
 
         STRV_FOREACH(i, arg_system_units) {
-                u = unit_name_mangle(*i);
+                _cleanup_free_ char *u = NULL;
+
+                u = unit_name_mangle(*i, MANGLE_GLOB);
                 if (!u)
                         return log_oom();
                 if (!u)
                         return log_oom();
-                r = add_matches_for_unit(j, u);
-                if (r < 0)
-                        return r;
-                r = sd_journal_add_disjunction(j);
+
+                if (string_is_glob(u)) {
+                        r = strv_push(&patterns, u);
+                        if (r < 0)
+                                return r;
+                        u = NULL;
+                } else {
+                        r = add_matches_for_unit(j, u);
+                        if (r < 0)
+                                return r;
+                        r = sd_journal_add_disjunction(j);
+                        if (r < 0)
+                                return r;
+                        count ++;
+                }
+        }
+
+        if (!strv_isempty(patterns)) {
+                _cleanup_set_free_free_ Set *units = NULL;
+                Iterator it;
+                char *u;
+
+                r = get_possible_units(j, SYSTEM_UNITS, patterns, &units);
                 if (r < 0)
                         return r;
                 if (r < 0)
                         return r;
+
+                SET_FOREACH(u, units, it) {
+                        r = add_matches_for_unit(j, u);
+                        if (r < 0)
+                                return r;
+                        r = sd_journal_add_disjunction(j);
+                        if (r < 0)
+                                return r;
+                        count ++;
+                }
         }
 
         }
 
+        strv_free(patterns);
+        patterns = NULL;
+
         STRV_FOREACH(i, arg_user_units) {
         STRV_FOREACH(i, arg_user_units) {
-                u = unit_name_mangle(*i);
+                _cleanup_free_ char *u = NULL;
+
+                u = unit_name_mangle(*i, MANGLE_GLOB);
                 if (!u)
                         return log_oom();
 
                 if (!u)
                         return log_oom();
 
-                r = add_matches_for_user_unit(j, u, getuid());
-                if (r < 0)
-                        return r;
+                if (string_is_glob(u)) {
+                        r = strv_push(&patterns, u);
+                        if (r < 0)
+                                return r;
+                        u = NULL;
+                } else {
+                        r = add_matches_for_user_unit(j, u, getuid());
+                        if (r < 0)
+                                return r;
+                        r = sd_journal_add_disjunction(j);
+                        if (r < 0)
+                                return r;
+                        count ++;
+                }
+        }
 
 
-                r = sd_journal_add_disjunction(j);
+        if (!strv_isempty(patterns)) {
+                _cleanup_set_free_free_ Set *units = NULL;
+                Iterator it;
+                char *u;
+
+                r = get_possible_units(j, USER_UNITS, patterns, &units);
                 if (r < 0)
                         return r;
 
                 if (r < 0)
                         return r;
 
+                SET_FOREACH(u, units, it) {
+                        r = add_matches_for_user_unit(j, u, getuid());
+                        if (r < 0)
+                                return r;
+                        r = sd_journal_add_disjunction(j);
+                        if (r < 0)
+                                return r;
+                        count ++;
+                }
         }
 
         }
 
+        /* Complain if the user request matches but nothing whatsoever was
+         * found, since otherwise everything would be matched. */
+        if (!(strv_isempty(arg_system_units) && strv_isempty(arg_user_units)) && count == 0)
+                return -ENODATA;
+
         r = sd_journal_add_conjunction(j);
         if (r < 0)
                 return r;
         r = sd_journal_add_conjunction(j);
         if (r < 0)
                 return r;
@@ -1137,8 +1310,8 @@ static int setup_keys(void) {
         n = now(CLOCK_REALTIME);
         n /= arg_interval;
 
         n = now(CLOCK_REALTIME);
         n /= arg_interval;
 
-        close_nointr_nofail(fd);
-        fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
+        safe_close(fd);
+        fd = mkostemp_safe(k, O_WRONLY|O_CLOEXEC);
         if (fd < 0) {
                 log_error("Failed to open %s: %m", k);
                 r = -errno;
         if (fd < 0) {
                 log_error("Failed to open %s: %m", k);
                 r = -errno;
@@ -1236,8 +1409,7 @@ static int setup_keys(void) {
         r = 0;
 
 finish:
         r = 0;
 
 finish:
-        if (fd >= 0)
-                close_nointr_nofail(fd);
+        safe_close(fd);
 
         if (k) {
                 unlink(k);
 
         if (k) {
                 unlink(k);
@@ -1442,16 +1614,12 @@ int main(int argc, char *argv[]) {
             arg_action == ACTION_LIST_CATALOG ||
             arg_action == ACTION_DUMP_CATALOG) {
 
             arg_action == ACTION_LIST_CATALOG ||
             arg_action == ACTION_DUMP_CATALOG) {
 
-                const char* database = CATALOG_DATABASE;
-                _cleanup_free_ char *copy = NULL;
-                if (arg_root) {
-                        copy = strjoin(arg_root, "/", CATALOG_DATABASE, NULL);
-                        if (!copy) {
-                                r = log_oom();
-                                goto finish;
-                        }
-                        path_kill_slashes(copy);
-                        database = copy;
+                _cleanup_free_ char *database;
+
+                database = path_join(arg_root, CATALOG_DATABASE, NULL);
+                if (!database) {
+                        r = log_oom();
+                        goto finish;
                 }
 
                 if (arg_action == ACTION_UPDATE_CATALOG) {
                 }
 
                 if (arg_action == ACTION_UPDATE_CATALOG) {
@@ -1503,7 +1671,7 @@ int main(int argc, char *argv[]) {
         }
 
         if (arg_action == ACTION_DISK_USAGE) {
         }
 
         if (arg_action == ACTION_DISK_USAGE) {
-                uint64_t bytes;
+                uint64_t bytes = 0;
                 char sbytes[FORMAT_BYTES_MAX];
 
                 r = sd_journal_get_usage(j, &bytes);
                 char sbytes[FORMAT_BYTES_MAX];
 
                 r = sd_journal_get_usage(j, &bytes);
@@ -1534,16 +1702,22 @@ int main(int argc, char *argv[]) {
         strv_free(arg_system_units);
         strv_free(arg_user_units);
 
         strv_free(arg_system_units);
         strv_free(arg_user_units);
 
-        if (r < 0)
+        if (r < 0) {
+                log_error("Failed to add filter for units: %s", strerror(-r));
                 return EXIT_FAILURE;
                 return EXIT_FAILURE;
+        }
 
         r = add_priorities(j);
 
         r = add_priorities(j);
-        if (r < 0)
+        if (r < 0) {
+                log_error("Failed to add filter for priorities: %s", strerror(-r));
                 return EXIT_FAILURE;
                 return EXIT_FAILURE;
+        }
 
         r = add_matches(j, argv + optind);
 
         r = add_matches(j, argv + optind);
-        if (r < 0)
+        if (r < 0) {
+                log_error("Failed to add filters: %s", strerror(-r));
                 return EXIT_FAILURE;
                 return EXIT_FAILURE;
+        }
 
         if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
                 _cleanup_free_ char *filter;
 
         if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
                 _cleanup_free_ char *filter;
@@ -1594,7 +1768,7 @@ int main(int argc, char *argv[]) {
         }
 
         if (arg_cursor || arg_after_cursor) {
         }
 
         if (arg_cursor || arg_after_cursor) {
-                r = sd_journal_seek_cursor(j, arg_cursor ? arg_cursor : arg_after_cursor);
+                r = sd_journal_seek_cursor(j, arg_cursor ?: arg_after_cursor);
                 if (r < 0) {
                         log_error("Failed to seek to cursor: %s", strerror(-r));
                         return EXIT_FAILURE;
                 if (r < 0) {
                         log_error("Failed to seek to cursor: %s", strerror(-r));
                         return EXIT_FAILURE;
@@ -1779,5 +1953,7 @@ int main(int argc, char *argv[]) {
 finish:
         pager_close();
 
 finish:
         pager_close();
 
+        strv_free(arg_file);
+
         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
 }
         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
 }