along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <locale.h>
#include <fcntl.h>
#include <errno.h>
#include <stddef.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
-#include <locale.h>
-#include <langinfo.h>
#include <systemd/sd-journal.h>
#include "journal-authenticate.h"
#include "journal-qrcode.h"
#include "fsprg.h"
+#include "unit-name.h"
+#include "catalog.h"
#define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
static OutputMode arg_output = OUTPUT_SHORT;
static bool arg_follow = false;
-static bool arg_show_all = false;
+static bool arg_all = false;
static bool arg_no_pager = false;
static unsigned arg_lines = 0;
static bool arg_no_tail = false;
#endif
static usec_t arg_since, arg_until;
static bool arg_since_set = false, arg_until_set = false;
+static const char *arg_unit = NULL;
+static const char *arg_field = NULL;
+static bool arg_catalog = false;
static enum {
ACTION_SHOW,
ACTION_SETUP_KEYS,
ACTION_VERIFY,
ACTION_DISK_USAGE,
+ ACTION_LIST_CATALOG,
+ ACTION_UPDATE_CATALOG
} arg_action = ACTION_SHOW;
static int help(void) {
- printf("%s [OPTIONS...] [MATCH]\n\n"
- "Send control commands to or query the journal.\n\n"
- " -h --help Show this help\n"
- " --version Show package version\n"
- " --no-pager Do not pipe output into a pager\n"
- " -a --all Show all fields, including long and unprintable\n"
- " -c --cursor=CURSOR Start showing entries from specified cursor\n"
+ printf("%s [OPTIONS...] [MATCHES...]\n\n"
+ "Query the journal.\n\n"
+ "Flags:\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"
+ " -b --this-boot Show data only from current boot\n"
+ " -u --unit=UNIT Show data only from the specified unit\n"
+ " -p --priority=RANGE Show only messages within the specified priority range\n"
" -f --follow Follow journal\n"
" -n --lines[=INTEGER] Number of journal entries to show\n"
" --no-tail Show all lines, even in follow mode\n"
" -o --output=STRING Change journal output mode (short, short-monotonic,\n"
" verbose, export, json, json-pretty, json-sse, cat)\n"
+ " -x --catalog Add message explanations where available\n"
+ " -a --all Show all fields, including long and unprintable\n"
" -q --quiet Don't show privilege warning\n"
+ " --no-pager Do not pipe output into a pager\n"
" -m --merge Show entries from all available journals\n"
- " -b --this-boot Show data only from current boot\n"
" -D --directory=PATH Show journal files from directory\n"
- " -p --priority=RANGE Show only messages within the specified priority range\n\n"
- "Commands:\n"
+#ifdef HAVE_GCRYPT
+ " --interval=TIME Time interval for changing the FSS sealing key\n"
+ " --verify-key=KEY Specify FSS verification key\n"
+#endif
+ "\nCommands:\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
" --new-id128 Generate a new 128 Bit ID\n"
" --header Show journal header information\n"
" --disk-usage Show total disk usage\n"
+ " -F --field=FIELD List all values a certain field takes\n"
+ " --list-catalog Show message IDs of all entries in the message catalog\n"
+ " --update-catalog Update the message catalog database\n"
#ifdef HAVE_GCRYPT
" --setup-keys Generate new FSS key pair\n"
- " --interval=TIME Time interval for changing the FSS sealing key\n"
" --verify Verify journal file consistency\n"
- " --verify-key=KEY Specify FSS verification key\n"
#endif
, program_invocation_short_name);
ARG_VERIFY_KEY,
ARG_DISK_USAGE,
ARG_SINCE,
- ARG_UNTIL
+ ARG_UNTIL,
+ ARG_LIST_CATALOG,
+ ARG_UPDATE_CATALOG
};
static const struct option options[] = {
{ "this-boot", no_argument, NULL, 'b' },
{ "directory", required_argument, NULL, 'D' },
{ "header", no_argument, NULL, ARG_HEADER },
- { "priority", no_argument, NULL, 'p' },
+ { "priority", required_argument, NULL, 'p' },
{ "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
{ "interval", required_argument, NULL, ARG_INTERVAL },
{ "verify", no_argument, NULL, ARG_VERIFY },
{ "cursor", required_argument, NULL, 'c' },
{ "since", required_argument, NULL, ARG_SINCE },
{ "until", required_argument, NULL, ARG_UNTIL },
+ { "unit", required_argument, NULL, 'u' },
+ { "field", required_argument, NULL, 'F' },
+ { "catalog", no_argument, NULL, 'x' },
+ { "list-catalog", no_argument, NULL, ARG_LIST_CATALOG },
+ { "update-catalog",no_argument, NULL, ARG_UPDATE_CATALOG },
{ NULL, 0, NULL, 0 }
};
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "hfo:an::qmbD:p:c:", options, NULL)) >= 0) {
+ while ((c = getopt_long(argc, argv, "hfo:an::qmbD:p:c:u:F:x", options, NULL)) >= 0) {
switch (c) {
case 'f':
arg_follow = true;
- signal(SIGWINCH, columns_cache_reset);
break;
case 'o':
arg_output = output_mode_from_string(optarg);
if (arg_output < 0) {
- log_error("Unknown output '%s'.", optarg);
+ log_error("Unknown output format '%s'.", optarg);
return -EINVAL;
}
+ if (arg_output == OUTPUT_EXPORT ||
+ arg_output == OUTPUT_JSON ||
+ arg_output == OUTPUT_JSON_PRETTY ||
+ arg_output == OUTPUT_JSON_SSE ||
+ arg_output == OUTPUT_CAT)
+ arg_quiet = true;
+
break;
case 'a':
- arg_show_all = true;
+ arg_all = true;
break;
case 'n':
arg_until_set = true;
break;
+ case 'u':
+ arg_unit = optarg;
+ break;
+
case '?':
return -EINVAL;
+ case 'F':
+ arg_field = optarg;
+ break;
+
+ case 'x':
+ arg_catalog = true;
+ break;
+
+ case ARG_LIST_CATALOG:
+ arg_action = ACTION_LIST_CATALOG;
+ break;
+
+ case ARG_UPDATE_CATALOG:
+ arg_action = ACTION_UPDATE_CATALOG;
+ break;
+
default:
log_error("Unknown option code %c", c);
return -EINVAL;
return 1;
}
-static bool on_tty(void) {
- static int t = -1;
-
- /* Note that this is invoked relatively early, before we start
- * the pager. That means the value we return reflects whether
- * we originally were started on a tty, not if we currently
- * are. But this is intended, since we want colour and so on
- * when run in our own pager. */
-
- if (_unlikely_(t < 0))
- t = isatty(STDOUT_FILENO) > 0;
-
- return t;
-}
-
static int generate_new_id128(void) {
sd_id128_t id;
int r;
return 0;
}
+static int add_unit(sd_journal *j) {
+ _cleanup_free_ char *m = NULL, *u = NULL;
+ int r;
+
+ assert(j);
+
+ if (isempty(arg_unit))
+ return 0;
+
+ u = unit_name_mangle(arg_unit);
+ if (!u)
+ return log_oom();
+
+ m = strappend("_SYSTEMD_UNIT=", u);
+ if (!m)
+ return log_oom();
+
+ r = sd_journal_add_match(j, m, strlen(m));
+ if (r < 0) {
+ log_error("Failed to add match: %s", strerror(-r));
+ return r;
+ }
+
+ return 0;
+}
+
static int add_priorities(sd_journal *j) {
char match[] = "PRIORITY=0";
int i, r;
if (arg_priorities & (1 << i)) {
match[sizeof(match)-2] = '0' + i;
- log_info("adding match %s", match);
-
r = sd_journal_add_match(j, match, strlen(match));
if (r < 0) {
log_error("Failed to add match: %s", strerror(-r));
goto finish;
}
- if (isatty(STDOUT_FILENO)) {
+ if (on_tty()) {
fprintf(stderr,
"\n"
"The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
- if (isatty(STDOUT_FILENO)) {
+ if (on_tty()) {
char tsb[FORMAT_TIMESPAN_MAX], *hn;
fprintf(stderr,
#ifdef HAVE_QRENCODE
/* If this is not an UTF-8 system don't print any QR codes */
- setlocale(LC_CTYPE, "");
-
- if (streq_ptr(nl_langinfo(CODESET), "UTF-8")) {
+ if (is_locale_utf8()) {
fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr);
print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
}
return r;
}
+static int access_check(void) {
+
+#ifdef HAVE_ACL
+ if (access("/var/log/journal", F_OK) < 0 && geteuid() != 0 && in_group("adm") <= 0) {
+ log_error("Unprivileged users can't see messages unless persistent log storage is enabled. Users in the group 'adm' can always see messages.");
+ return -EACCES;
+ }
+
+ if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
+ log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this notice off.");
+#else
+ if (geteuid() != 0 && in_group("adm") <= 0) {
+ log_error("No access to messages. Only users in the group 'adm' can see messages.");
+ return -EACCES;
+ }
+#endif
+
+ return 0;
+}
+
int main(int argc, char *argv[]) {
int r;
sd_journal *j = NULL;
bool need_seek = false;
sd_id128_t previous_boot_id;
bool previous_boot_id_valid = false;
- bool have_pager;
unsigned n_shown = 0;
+ setlocale(LC_ALL, "");
log_parse_environment();
log_open();
if (r <= 0)
goto finish;
+ signal(SIGWINCH, columns_lines_cache_reset);
+
if (arg_action == ACTION_NEW_ID128) {
r = generate_new_id128();
goto finish;
goto finish;
}
+ if (arg_action == ACTION_LIST_CATALOG) {
+ r = catalog_list(stdout);
+ if (r < 0)
+ log_error("Failed to list catalog: %s", strerror(-r));
+ goto finish;
+ }
+
+ if (arg_action == ACTION_UPDATE_CATALOG) {
+ r = catalog_update();
+ goto finish;
+ }
+
+ r = access_check();
+ if (r < 0)
+ goto finish;
+
if (arg_directory)
r = sd_journal_open_directory(&j, arg_directory, 0);
else
r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY);
-
if (r < 0) {
log_error("Failed to open journal: %s", strerror(-r));
goto finish;
goto finish;
}
-#ifdef HAVE_ACL
- if (access("/var/log/journal", F_OK) < 0 && geteuid() != 0 && in_group("adm") <= 0) {
- log_error("Unprivileged users can't see messages unless persistent log storage is enabled. Users in the group 'adm' can always see messages.");
- r = -EACCES;
- goto finish;
- }
-
- if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
- log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this notice off.");
-#else
- if (geteuid() != 0 && in_group("adm") <= 0) {
- log_error("No access to messages. Only users in the group 'adm' can see messages.");
- r = -EACCES;
+ r = add_this_boot(j);
+ if (r < 0)
goto finish;
- }
-#endif
- r = add_this_boot(j);
+ r = add_unit(j);
if (r < 0)
goto finish;
if (r < 0)
goto finish;
+ if (arg_field) {
+ const void *data;
+ size_t size;
+
+ r = sd_journal_query_unique(j, arg_field);
+ if (r < 0) {
+ log_error("Failed to query unique data objects: %s", strerror(-r));
+ goto finish;
+ }
+
+ SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
+ const void *eq;
+
+ if (arg_lines > 0 && n_shown >= arg_lines)
+ break;
+
+ eq = memchr(data, '=', size);
+ if (eq)
+ printf("%.*s\n", (int) (size - ((const uint8_t*) eq - (const uint8_t*) data + 1)), (const char*) eq + 1);
+ else
+ printf("%.*s\n", (int) size, (const char*) data);
+
+ n_shown ++;
+ }
+
+ r = 0;
+ goto finish;
+ }
+
if (arg_cursor) {
r = sd_journal_seek_cursor(j, arg_cursor);
if (r < 0) {
goto finish;
}
- on_tty();
- have_pager = !arg_no_pager && !arg_follow && pager_open();
+ if (!arg_no_pager && !arg_follow)
+ pager_open();
if (!arg_quiet) {
usec_t start, end;
if (r > 0) {
if (arg_follow)
- printf("---- Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
+ printf("-- Logs begin at %s. --\n",
+ format_timestamp(start_buf, sizeof(start_buf), start));
else
- printf("---- Logs begin at %s, end at %s.\n",
+ printf("-- Logs begin at %s, end at %s. --\n",
format_timestamp(start_buf, sizeof(start_buf), start),
format_timestamp(end_buf, sizeof(end_buf), end));
}
if (r >= 0) {
if (previous_boot_id_valid &&
!sd_id128_equal(boot_id, previous_boot_id))
- printf(ANSI_HIGHLIGHT_ON "---- Reboot ----" ANSI_HIGHLIGHT_OFF "\n");
+ printf(ANSI_HIGHLIGHT_ON "-- Reboot --" ANSI_HIGHLIGHT_OFF "\n");
previous_boot_id = boot_id;
previous_boot_id_valid = true;
}
flags =
- arg_show_all * OUTPUT_SHOW_ALL |
- have_pager * OUTPUT_FULL_WIDTH |
- on_tty() * OUTPUT_COLOR;
+ arg_all * OUTPUT_SHOW_ALL |
+ (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
+ on_tty() * OUTPUT_COLOR |
+ arg_catalog * OUTPUT_CATALOG;
r = output_journal(stdout, j, arg_output, 0, flags);
if (r < 0)