summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--man/journalctl.xml28
-rw-r--r--src/journal/journalctl.c175
2 files changed, 179 insertions, 24 deletions
diff --git a/man/journalctl.xml b/man/journalctl.xml
index 55474c53d9..3b05e80fc1 100644
--- a/man/journalctl.xml
+++ b/man/journalctl.xml
@@ -509,17 +509,27 @@
<varlistentry>
<term><option>-u</option></term>
- <term><option>--unit=</option></term>
+ <term><option>--unit=<replaceable>UNIT</replaceable>|<replaceable>PATTERN</replaceable></option></term>
<listitem><para>Show messages for the
- specified systemd unit. This will add
- a match for messages from the unit
- (<literal>_SYSTEMD_UNIT=</literal>)
- and additional matches for messages
- from systemd and messages about
- coredumps for the specified unit.</para>
- <para>This parameter can be specified multiple times.
- </para></listitem>
+ specified systemd unit
+ <replaceable>UNIT</replaceable>, or
+ for any of the units matched by
+ <replaceable>PATTERN</replaceable>.
+ If a pattern is specified, a list of
+ unit names found in the journal is
+ compared with the specified pattern
+ and all that match are used. For each
+ unit name a match is added for
+ messages from the unit
+ (<literal>_SYSTEMD_UNIT=<replaceable>UNIT</replaceable></literal>)
+ along with additional matches for
+ messages from systemd and messages
+ about coredumps for the specified
+ unit.</para>
+
+ <para>This parameter can be specified
+ multiple times. </para></listitem>
</varlistentry>
<varlistentry>
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 42c16f67ec..2d99ade117 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -21,6 +21,7 @@
#include <locale.h>
#include <fcntl.h>
+#include <fnmatch.h>
#include <errno.h>
#include <stddef.h>
#include <string.h>
@@ -49,6 +50,7 @@
#include "build.h"
#include "pager.h"
#include "strv.h"
+#include "set.h"
#include "journal-internal.h"
#include "journal-def.h"
#include "journal-verify.h"
@@ -983,40 +985,177 @@ static int add_dmesg(sd_journal *j) {
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;
+
+ 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) {
- u = unit_name_mangle(*i, MANGLE_NOGLOB);
+ _cleanup_free_ char *u = NULL;
+
+ u = unit_name_mangle(*i, MANGLE_GLOB);
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;
+
+ 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) {
- u = unit_name_mangle(*i, MANGLE_NOGLOB);
+ _cleanup_free_ char *u = NULL;
+
+ u = unit_name_mangle(*i, MANGLE_GLOB);
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 ++;
+ }
+ }
+
+ if (!strv_isempty(patterns)) {
+ _cleanup_set_free_free_ Set *units = NULL;
+ Iterator it;
+ char *u;
- r = sd_journal_add_disjunction(j);
+ r = get_possible_units(j, USER_UNITS, patterns, &units);
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;
@@ -1543,16 +1682,22 @@ int main(int argc, char *argv[]) {
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;
+ }
r = add_priorities(j);
- if (r < 0)
+ if (r < 0) {
+ log_error("Failed to add filter for priorities: %s", strerror(-r));
return EXIT_FAILURE;
+ }
r = add_matches(j, argv + optind);
- if (r < 0)
+ if (r < 0) {
+ log_error("Failed to add filters: %s", strerror(-r));
return EXIT_FAILURE;
+ }
if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
_cleanup_free_ char *filter;