+ return log_oom();
+
+ strv_sort(g);
+ strv_uniq(g);
+
+ s = strv_join(g, "', '");
+ if (!s)
+ return log_oom();
+
+ log_notice("Hint: You are currently not seeing messages from other users and the system.\n"
+ " Users in the groups '%s' can see all messages.\n"
+ " Pass -q to turn off this notice.", s);
+ }
+ }
+
+ return 0;
+}
+#endif
+
+static int access_check(sd_journal *j) {
+ Iterator it;
+ void *code;
+ int r = 0;
+
+ assert(j);
+
+ if (set_isempty(j->errors)) {
+ if (hashmap_isempty(j->files))
+ log_notice("No journal files were found.");
+ return 0;
+ }
+
+ if (set_contains(j->errors, INT_TO_PTR(-EACCES))) {
+#ifdef HAVE_ACL
+ /* If /var/log/journal doesn't even exist,
+ * unprivileged users have no access at all */
+ if (access("/var/log/journal", F_OK) < 0 &&
+ geteuid() != 0 &&
+ in_group("systemd-journal") <= 0) {
+ log_error("Unprivileged users cannot access messages, unless persistent log storage is\n"
+ "enabled. Users in the 'systemd-journal' group may always access messages.");
+ return -EACCES;
+ }
+
+ /* If /var/log/journal exists, try to pring a nice
+ notice if the user lacks access to it */
+ if (!arg_quiet && geteuid() != 0) {
+ r = access_check_var_log_journal(j);
+ if (r < 0)
+ return r;
+ }
+#else
+ if (geteuid() != 0 && in_group("systemd-journal") <= 0) {
+ log_error("Unprivileged users cannot access messages. Users in the 'systemd-journal' group\n"
+ "group may access messages.");
+ return -EACCES;
+ }
+#endif
+
+ if (hashmap_isempty(j->files)) {
+ log_error("No journal files were opened due to insufficient permissions.");
+ r = -EACCES;
+ }
+ }
+
+ SET_FOREACH(code, j->errors, it) {
+ int err;
+
+ err = -PTR_TO_INT(code);
+ assert(err > 0);
+
+ if (err != EACCES)
+ log_warning("Error was encountered while opening journal files: %s",
+ strerror(err));
+ }
+
+ return r;
+}
+
+int main(int argc, char *argv[]) {
+ int r;
+ _cleanup_journal_close_ sd_journal *j = NULL;
+ bool need_seek = false;
+ sd_id128_t previous_boot_id;
+ bool previous_boot_id_valid = false, first_line = true;
+ int n_shown = 0;
+ bool ellipsized = false;
+
+ setlocale(LC_ALL, "");
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ goto finish;
+
+ signal(SIGWINCH, columns_lines_cache_reset);
+
+ if (arg_action == ACTION_NEW_ID128) {
+ r = generate_new_id128();
+ goto finish;
+ }
+
+ if (arg_action == ACTION_SETUP_KEYS) {
+ r = setup_keys();
+ goto finish;
+ }
+
+ if (arg_action == ACTION_UPDATE_CATALOG ||
+ arg_action == ACTION_LIST_CATALOG ||
+ arg_action == ACTION_DUMP_CATALOG) {
+
+ _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) {
+ r = catalog_update(database, arg_root, catalog_file_dirs);
+ if (r < 0)
+ log_error("Failed to list catalog: %s", strerror(-r));
+ } else {
+ bool oneline = arg_action == ACTION_LIST_CATALOG;
+
+ if (optind < argc)
+ r = catalog_list_items(stdout, database,
+ oneline, argv + optind);
+ else
+ r = catalog_list(stdout, database, oneline);
+ if (r < 0)
+ log_error("Failed to list catalog: %s", strerror(-r));
+ }
+
+ goto finish;
+ }
+
+ if (arg_directory)
+ 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) {
+ log_error("Failed to open %s: %s",
+ arg_directory ? arg_directory : arg_file ? "files" : "journal",
+ strerror(-r));
+ return EXIT_FAILURE;
+ }
+
+ r = access_check(j);
+ if (r < 0)
+ return EXIT_FAILURE;
+
+ if (arg_action == ACTION_VERIFY) {
+ r = verify(j);
+ goto finish;
+ }
+
+ if (arg_action == ACTION_PRINT_HEADER) {
+ journal_print_header(j);
+ return EXIT_SUCCESS;
+ }
+
+ if (arg_action == ACTION_DISK_USAGE) {
+ uint64_t bytes = 0;
+ char sbytes[FORMAT_BYTES_MAX];
+
+ r = sd_journal_get_usage(j, &bytes);
+ if (r < 0)
+ return EXIT_FAILURE;
+
+ printf("Journals take up %s on disk.\n",
+ format_bytes(sbytes, sizeof(sbytes), bytes));
+ return EXIT_SUCCESS;
+ }
+
+ if (arg_action == ACTION_LIST_BOOTS) {
+ r = list_boots(j);
+ goto finish;
+ }
+
+ /* add_boot() must be called first!
+ * It may need to seek the journal to find parent boot IDs. */
+ r = add_boot(j);
+ if (r < 0)
+ return EXIT_FAILURE;
+
+ r = add_dmesg(j);
+ if (r < 0)
+ return EXIT_FAILURE;
+
+ r = add_units(j);
+ strv_free(arg_system_units);
+ strv_free(arg_user_units);
+
+ if (r < 0) {
+ log_error("Failed to add filter for units: %s", strerror(-r));
+ return EXIT_FAILURE;
+ }
+
+ r = add_syslog_identifier(j);
+ if (r < 0) {
+ log_error("Failed to add filter for syslog identifiers: %s", strerror(-r));
+ return EXIT_FAILURE;
+ }
+
+ r = add_priorities(j);
+ 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) {
+ 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;
+
+ filter = journal_make_match_string(j);
+ log_debug("Journal filter: %s", filter);
+ }
+
+ if (arg_field) {
+ const void *data;
+ size_t size;
+
+ r = sd_journal_set_data_threshold(j, 0);
+ if (r < 0) {
+ log_error("Failed to unset data size threshold");
+ return EXIT_FAILURE;
+ }
+
+ r = sd_journal_query_unique(j, arg_field);
+ if (r < 0) {
+ log_error("Failed to query unique data objects: %s", strerror(-r));
+ return EXIT_FAILURE;
+ }
+
+ 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 ++;
+ }
+
+ return EXIT_SUCCESS;
+ }
+
+ /* Opening the fd now means the first sd_journal_wait() will actually wait */
+ if (arg_follow) {
+ r = sd_journal_get_fd(j);
+ if (r < 0)
+ return EXIT_FAILURE;
+ }
+
+ if (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 (!arg_reverse)
+ r = sd_journal_next_skip(j, 1 + !!arg_after_cursor);
+ else
+ r = sd_journal_previous_skip(j, 1 + !!arg_after_cursor);
+
+ if (arg_after_cursor && r < 2 && !arg_follow)
+ /* We couldn't find the next entry after the cursor. */
+ arg_lines = 0;
+
+ } else if (arg_since_set && !arg_reverse) {
+ r = sd_journal_seek_realtime_usec(j, arg_since);
+ if (r < 0) {
+ log_error("Failed to seek to date: %s", strerror(-r));
+ return EXIT_FAILURE;
+ }
+ r = sd_journal_next(j);
+
+ } else if (arg_until_set && arg_reverse) {
+ r = sd_journal_seek_realtime_usec(j, arg_until);
+ if (r < 0) {
+ log_error("Failed to seek to date: %s", strerror(-r));
+ return EXIT_FAILURE;
+ }
+ r = sd_journal_previous(j);
+
+ } else if (arg_lines >= 0) {
+ r = sd_journal_seek_tail(j);
+ if (r < 0) {
+ log_error("Failed to seek to tail: %s", strerror(-r));
+ return EXIT_FAILURE;
+ }
+
+ r = sd_journal_previous_skip(j, arg_lines);
+
+ } else if (arg_reverse) {
+ r = sd_journal_seek_tail(j);
+ if (r < 0) {
+ log_error("Failed to seek to tail: %s", strerror(-r));
+ return EXIT_FAILURE;
+ }
+
+ r = sd_journal_previous(j);
+
+ } else {
+ r = sd_journal_seek_head(j);
+ if (r < 0) {
+ log_error("Failed to seek to head: %s", strerror(-r));
+ return EXIT_FAILURE;
+ }
+
+ r = sd_journal_next(j);
+ }
+
+ if (r < 0) {
+ log_error("Failed to iterate through journal: %s", strerror(-r));
+ return EXIT_FAILURE;
+ }