summaryrefslogtreecommitdiff
path: root/src/journal/journalctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/journal/journalctl.c')
-rw-r--r--src/journal/journalctl.c162
1 files changed, 139 insertions, 23 deletions
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index db11421e7a..20f7082175 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -69,6 +69,8 @@
#include "strv.h"
#include "syslog-util.h"
#include "terminal-util.h"
+#include "udev.h"
+#include "udev-util.h"
#include "unit-name.h"
#include "user-util.h"
@@ -136,6 +138,8 @@ static enum {
ACTION_SYNC,
ACTION_ROTATE,
ACTION_VACUUM,
+ ACTION_LIST_FIELDS,
+ ACTION_LIST_FIELD_NAMES,
} arg_action = ACTION_SHOW;
typedef struct BootId {
@@ -145,6 +149,84 @@ typedef struct BootId {
LIST_FIELDS(struct BootId, boot_list);
} BootId;
+static int add_matches_for_device(sd_journal *j, const char *devpath) {
+ int r;
+ _cleanup_udev_unref_ struct udev *udev = NULL;
+ _cleanup_udev_device_unref_ struct udev_device *device = NULL;
+ struct udev_device *d = NULL;
+ struct stat st;
+
+ assert(j);
+ assert(devpath);
+
+ if (!path_startswith(devpath, "/dev/")) {
+ log_error("Devpath does not start with /dev/");
+ return -EINVAL;
+ }
+
+ udev = udev_new();
+ if (!udev)
+ return log_oom();
+
+ r = stat(devpath, &st);
+ if (r < 0)
+ log_error_errno(errno, "Couldn't stat file: %m");
+
+ d = device = udev_device_new_from_devnum(udev, S_ISBLK(st.st_mode) ? 'b' : 'c', st.st_rdev);
+ if (!device)
+ return log_error_errno(errno, "Failed to get udev device from devnum %u:%u: %m", major(st.st_rdev), minor(st.st_rdev));
+
+ while (d) {
+ _cleanup_free_ char *match = NULL;
+ const char *subsys, *sysname, *devnode;
+
+ subsys = udev_device_get_subsystem(d);
+ if (!subsys) {
+ d = udev_device_get_parent(d);
+ continue;
+ }
+
+ sysname = udev_device_get_sysname(d);
+ if (!sysname) {
+ d = udev_device_get_parent(d);
+ continue;
+ }
+
+ match = strjoin("_KERNEL_DEVICE=+", subsys, ":", sysname, NULL);
+ if (!match)
+ return log_oom();
+
+ r = sd_journal_add_match(j, match, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add match: %m");
+
+ devnode = udev_device_get_devnode(d);
+ if (devnode) {
+ _cleanup_free_ char *match1 = NULL;
+
+ r = stat(devnode, &st);
+ if (r < 0)
+ return log_error_errno(r, "Failed to stat() device node \"%s\": %m", devnode);
+
+ r = asprintf(&match1, "_KERNEL_DEVICE=%c%u:%u", S_ISBLK(st.st_mode) ? 'b' : 'c', major(st.st_rdev), minor(st.st_rdev));
+ if (r < 0)
+ return log_oom();
+
+ r = sd_journal_add_match(j, match1, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add match: %m");
+ }
+
+ d = udev_device_get_parent(d);
+ }
+
+ r = add_match_this_boot(j, arg_machine);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add match for the current boot: %m");
+
+ return 0;
+}
+
static void pager_open_if_enabled(void) {
if (arg_no_pager)
@@ -244,6 +326,7 @@ static void help(void) {
"\nCommands:\n"
" -h --help Show this help text\n"
" --version Show package version\n"
+ " -N --fields List all field names currently used\n"
" -F --field=FIELD List all values that a specified field takes\n"
" --disk-usage Show total disk usage of all journal files\n"
" --vacuum-size=BYTES Reduce disk usage below specified size\n"
@@ -340,6 +423,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "unit", required_argument, NULL, 'u' },
{ "user-unit", required_argument, NULL, ARG_USER_UNIT },
{ "field", required_argument, NULL, 'F' },
+ { "fields", no_argument, NULL, 'N' },
{ "catalog", no_argument, NULL, 'x' },
{ "list-catalog", no_argument, NULL, ARG_LIST_CATALOG },
{ "dump-catalog", no_argument, NULL, ARG_DUMP_CATALOG },
@@ -361,7 +445,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:S:U:t:u:F:xrM:", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:c:S:U:t:u:NF:xrM:", options, NULL)) >= 0)
switch (c) {
@@ -698,9 +782,14 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'F':
+ arg_action = ACTION_LIST_FIELDS;
arg_field = optarg;
break;
+ case 'N':
+ arg_action = ACTION_LIST_FIELD_NAMES;
+ break;
+
case 'x':
arg_catalog = true;
break;
@@ -825,13 +914,12 @@ static int add_matches(sd_journal *j, char **args) {
have_term = false;
} else if (path_is_absolute(*i)) {
- _cleanup_free_ char *p, *t = NULL, *t2 = NULL;
+ _cleanup_free_ char *p, *t = NULL, *t2 = NULL, *interpreter = NULL;
const char *path;
- _cleanup_free_ char *interpreter = NULL;
struct stat st;
p = canonicalize_file_name(*i);
- path = p ? p : *i;
+ path = p ?: *i;
if (lstat(path, &st) < 0)
return log_error_errno(errno, "Couldn't stat file: %m");
@@ -845,34 +933,37 @@ static int add_matches(sd_journal *j, char **args) {
return log_oom();
t = strappend("_COMM=", comm);
+ if (!t)
+ return log_oom();
/* Append _EXE only if the interpreter is not a link.
Otherwise, it might be outdated often. */
- if (lstat(interpreter, &st) == 0 &&
- !S_ISLNK(st.st_mode)) {
+ if (lstat(interpreter, &st) == 0 && !S_ISLNK(st.st_mode)) {
t2 = strappend("_EXE=", interpreter);
if (!t2)
return log_oom();
}
- } else
+ } else {
t = strappend("_EXE=", path);
- } else if (S_ISCHR(st.st_mode))
- (void) asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
- else if (S_ISBLK(st.st_mode))
- (void) asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
- else {
+ if (!t)
+ return log_oom();
+ }
+
+ r = sd_journal_add_match(j, t, 0);
+
+ if (r >=0 && t2)
+ r = sd_journal_add_match(j, t2, 0);
+
+ } else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
+ r = add_matches_for_device(j, path);
+ if (r < 0)
+ return r;
+ } else {
log_error("File is neither a device node, nor regular file, nor executable: %s", *i);
return -EINVAL;
}
- if (!t)
- return log_oom();
-
- r = sd_journal_add_match(j, t, 0);
- if (t2)
- r = sd_journal_add_match(j, t2, 0);
have_term = true;
-
} else {
r = sd_journal_add_match(j, *i, 0);
have_term = true;
@@ -1136,10 +1227,11 @@ static int add_boot(sd_journal *j) {
const char *reason = (r == 0) ? "No such boot ID in journal" : strerror(-r);
if (sd_id128_is_null(arg_boot_id))
- log_error("Failed to look up boot %+i: %s", arg_boot_offset, reason);
+ log_error("Data from the specified boot (%+i) is not available: %s",
+ arg_boot_offset, reason);
else
- log_error("Failed to look up boot ID "SD_ID128_FORMAT_STR"%+i: %s",
- SD_ID128_FORMAT_VAL(arg_boot_id), arg_boot_offset, reason);
+ log_error("Data from the specified boot ("SD_ID128_FORMAT_STR") is not available: %s",
+ SD_ID128_FORMAT_VAL(arg_boot_id), reason);
return r == 0 ? -ENODATA : r;
}
@@ -2002,6 +2094,8 @@ int main(int argc, char *argv[]) {
case ACTION_DISK_USAGE:
case ACTION_LIST_BOOTS:
case ACTION_VACUUM:
+ case ACTION_LIST_FIELDS:
+ case ACTION_LIST_FIELD_NAMES:
/* These ones require access to the journal files, continue below. */
break;
@@ -2084,13 +2178,33 @@ int main(int argc, char *argv[]) {
goto finish;
}
+ case ACTION_LIST_FIELD_NAMES: {
+ const char *field;
+
+ SD_JOURNAL_FOREACH_FIELD(j, field) {
+ printf("%s\n", field);
+ n_shown ++;
+ }
+
+ r = 0;
+ goto finish;
+ }
+
case ACTION_SHOW:
+ case ACTION_LIST_FIELDS:
break;
default:
assert_not_reached("Unknown action");
}
+ if (arg_boot_offset != 0 &&
+ sd_journal_has_runtime_files(j) > 0 &&
+ sd_journal_has_persistent_files(j) == 0) {
+ log_info("Specifying boot ID has no effect, no persistent journal was found");
+ r = 0;
+ goto finish;
+ }
/* add_boot() must be called first!
* It may need to seek the journal to find parent boot IDs. */
r = add_boot(j);
@@ -2131,10 +2245,12 @@ int main(int argc, char *argv[]) {
log_debug("Journal filter: %s", filter);
}
- if (arg_field) {
+ if (arg_action == ACTION_LIST_FIELDS) {
const void *data;
size_t size;
+ assert(arg_field);
+
r = sd_journal_set_data_threshold(j, 0);
if (r < 0) {
log_error_errno(r, "Failed to unset data size threshold: %m");