return show_journal(f, j, mode, n_columns, not_before, how_many, flags, ellipsized);
}
-static int discover_next_boot(
+static int journal_get_id128(sd_journal *j, const char *field, sd_id128_t *ret) {
+ const char *data, *str;
+ size_t len, fl;
+ sd_id128_t v;
+ int r;
+
+ assert(j);
+ assert(field);
+
+ /* This returns 1 when non-null ID is found, 0 when null ID is found, -ENOENT when the field not
+ * found, -EINVAL when the field found but not a valid ID, -EBADMSG or -EADDRNOTAVAIL when journal
+ * corruption detected. On a critical issue, other negative errno may be returned. */
+
+ r = sd_journal_get_data(j, field, (const void**) &data, &len);
+ if (r < 0)
+ return r;
+
+ fl = strlen(field);
+ assert(len > fl);
+ assert(data[fl] == '=');
+
+ if (len > fl + 1 + SD_ID128_UUID_STRING_MAX)
+ return -EINVAL;
+
+ str = memdupa_suffix0(data + fl + 1, len - fl - 1);
+
+ r = sd_id128_from_string(str, ret ?: &v);
+ if (r < 0)
+ return r;
+
+ return !sd_id128_is_null(ret ? *ret : v);
+}
+
+static int journal_get_invocation_id(sd_journal *j, sd_id128_t *ret) {
+ int r;
+
+ assert(j);
+
+ FOREACH_STRING(s,
+ "_SYSTEMD_INVOCATION_ID", /* By the systemd unit. */
+ "OBJECT_SYSTEMD_INVOCATION_ID", /* Added by journald. */
+ "INVOCATION_ID", /* By the system service manager (PID 1). */
+ "USER_INVOCATION_ID") { /* By the user session service manager. */
+
+ r = journal_get_id128(j, s, ret);
+ if (!IN_SET(r, 0, -ENOENT, -EINVAL, -EBADMSG, -EADDRNOTAVAIL))
+ return r;
+ }
+
+ /* No invocation ID found in the entry. */
+ if (ret)
+ *ret = SD_ID128_NULL;
+ return 0;
+}
+
+static const char* const log_id_type_table[_LOG_ID_TYPE_MAX] = {
+ [LOG_BOOT_ID] = "boot ID",
+ [LOG_SYSTEM_UNIT_INVOCATION_ID] = "system unit invocation ID",
+ [LOG_USER_UNIT_INVOCATION_ID] = "user unit invocation ID",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(log_id_type, LogIdType);
+
+static int set_matches_for_discover_id(
sd_journal *j,
+ LogIdType type,
+ sd_id128_t boot_id,
+ const char *unit,
+ sd_id128_t id) {
+
+ int r;
+
+ assert(j);
+ assert(type >= 0 && type < _LOG_ID_TYPE_MAX);
+
+ sd_journal_flush_matches(j);
+
+ if (type == LOG_BOOT_ID) {
+ if (sd_id128_is_null(id))
+ return 0;
+
+ return add_match_boot_id(j, id);
+ }
+
+ if (!sd_id128_is_null(boot_id)) {
+ r = add_match_boot_id(j, boot_id);
+ if (r < 0)
+ return r;
+
+ r = sd_journal_add_conjunction(j);
+ if (r < 0)
+ return r;
+ }
+
+ if (!sd_id128_is_null(id))
+ return add_matches_for_invocation_id(j, id);
+
+ if (type == LOG_SYSTEM_UNIT_INVOCATION_ID)
+ return add_matches_for_unit_full(j, /* all = */ false, unit);
+
+ if (type == LOG_USER_UNIT_INVOCATION_ID)
+ return add_matches_for_user_unit_full(j, /* all = */ false, unit);
+
+ return -EINVAL;
+}
+
+static int discover_next_id(
+ sd_journal *j,
+ LogIdType type,
+ sd_id128_t boot_id, /* optional, used when type == JOURNAL_{SYSTEM,USER}_UNIT_INVOCATION_ID */
+ const char *unit, /* mandatory when type == JOURNAL_{SYSTEM,USER}_UNIT_INVOCATION_ID */
sd_id128_t previous_id,
bool advance_older,
LogId *ret) {
int r;
assert(j);
+ assert(type >= 0 && type < _LOG_ID_TYPE_MAX);
+ assert(type == LOG_BOOT_ID || unit);
assert(ret);
/* We expect the journal to be on the last position of a boot
/* Make sure we aren't restricted by any _BOOT_ID matches, so that
* we can actually advance to a *different* boot. */
- sd_journal_flush_matches(j);
+ r = set_matches_for_discover_id(j, type, boot_id, unit, SD_ID128_NULL);
+ if (r < 0)
+ return r;
for (;;) {
sd_id128_t *id_dup;
return 0; /* End of journal, yay. */
}
- r = sd_journal_get_monotonic_usec(j, NULL, &id.id);
+ if (type == LOG_BOOT_ID)
+ r = sd_journal_get_monotonic_usec(j, NULL, &id.id);
+ else
+ r = journal_get_invocation_id(j, &id.id);
if (r < 0)
return r;
- /* We iterate through this in a loop, until the boot ID differs from the previous one. Note that
- * normally, this will only require a single iteration, as we moved to the last entry of the previous
- * boot entry already. However, it might happen that the per-journal-field entry arrays are less
- * complete than the main entry array, and hence might reference an entry that's not actually the last
- * one of the boot ID as last one. Let's hence use the per-field array is initial seek position to
+ if (sd_id128_is_null(id.id))
+ continue;
+
+ /* We iterate through this in a loop, until the boot or invocation ID differs from the
+ * previous one. Note that normally, this will only require a single iteration, as we moved
+ * to the last entry of the previous boot or invocation entry already. However, it might
+ * happen that the per-journal-field entry arrays are less complete than the main entry
+ * array, and hence might reference an entry that's not actually the last one of the boot or
+ * invocation ID as last one. Let's hence use the per-field array is initial seek position to
* speed things up, but let's not trust that it is complete, and hence, manually advance as
* necessary. */
if (set_contains(broken_ids, &id.id))
continue;
- /* Yay, we found a new boot ID from the entry object. Let's check there exist corresponding
- * entries matching with the _BOOT_ID= data. */
+ /* Yay, we found a new boot or invocation ID from the entry object. Let's check there exist
+ * corresponding entries matching with the _BOOT_ID=, INVOCATION_ID= or friends data. */
- r = add_match_boot_id(j, id.id);
+ r = set_matches_for_discover_id(j, type, boot_id, unit, id.id);
if (r < 0)
return r;
- /* First, seek to the first (or the last when we are going upwards) occurrence of this boot ID.
- * You may think this is redundant. Yes, that's redundant unless the journal is corrupted.
- * But when the journal is corrupted, especially, badly 'truncated', then the below may fail.
+ /* First, seek to the first (or the last when we are going upwards) occurrence of this boot
+ * or invocation ID. You may think this is redundant. Yes, that's redundant unless the
+ * journal is corrupted. But when the journal is corrupted, especially, badly 'truncated',
+ * then the below may fail.
* See https://github.com/systemd/systemd/pull/29334#issuecomment-1736567951. */
if (advance_older)
r = sd_journal_seek_tail(j);
if (r < 0)
return r;
if (r == 0) {
- log_debug("Whoopsie! We found a boot ID %s but can't read its first entry. "
- "The journal seems to be corrupted. Ignoring the boot ID.",
- SD_ID128_TO_STRING(id.id));
+ log_debug("Whoopsie! We found a %s %s but can't read its first entry. "
+ "The journal seems to be corrupted. Ignoring the %s.",
+ log_id_type_to_string(type),
+ SD_ID128_TO_STRING(id.id),
+ log_id_type_to_string(type));
goto try_again;
}
if (r < 0)
return r;
- /* Next, seek to the last occurrence of this boot ID. */
+ /* Next, seek to the last occurrence of this boot or invocation ID. */
if (advance_older)
r = sd_journal_seek_head(j);
else
if (r < 0)
return r;
if (r == 0) {
- log_debug("Whoopsie! We found a boot ID %s but can't read its last entry. "
- "The journal seems to be corrupted. Ignoring the boot ID.",
- SD_ID128_TO_STRING(id.id));
+ log_debug("Whoopsie! We found a %s %s but can't read its last entry. "
+ "The journal seems to be corrupted. Ignoring the %s.",
+ log_id_type_to_string(type),
+ SD_ID128_TO_STRING(id.id),
+ log_id_type_to_string(type));
goto try_again;
}
return 1;
try_again:
- /* Save the bad boot ID. */
+ /* Save the bad boot or invocation ID. */
id_dup = newdup(sd_id128_t, &id.id, 1);
if (!id_dup)
return -ENOMEM;
return r;
/* Move to the previous position again. */
- sd_journal_flush_matches(j);
-
- if (!sd_id128_is_null(previous_id)) {
- r = add_match_boot_id(j, previous_id);
- if (r < 0)
- return r;
- }
+ r = set_matches_for_discover_id(j, type, boot_id, unit, previous_id);
+ if (r < 0)
+ return r;
if (advance_older)
r = sd_journal_seek_head(j);
return r;
if (r == 0)
return log_debug_errno(SYNTHETIC_ERRNO(ENODATA),
- "Whoopsie! Cannot seek to the last entry of boot %s.",
+ "Whoopsie! Cannot seek to the last entry of %s %s.",
+ log_id_type_to_string(type),
SD_ID128_TO_STRING(previous_id));
sd_journal_flush_matches(j);
}
}
-int journal_find_boot(sd_journal *j, sd_id128_t boot_id, int offset, sd_id128_t *ret) {
+int journal_find_log_id(
+ sd_journal *j,
+ LogIdType type,
+ sd_id128_t boot_id, /* optional, used when type == JOURNAL_{SYSTEM,USER}_UNIT_INVOCATION_ID */
+ const char *unit, /* mandatory when type == JOURNAL_{SYSTEM,USER}_UNIT_INVOCATION_ID
+ * unless an invocation ID is explicitly specified without an offset. */
+ sd_id128_t previous_id,
+ int offset,
+ sd_id128_t *ret) {
+
bool advance_older;
int r, offset_start;
assert(j);
+ assert(type >= 0 && type < _LOG_ID_TYPE_MAX);
+ assert(type == LOG_BOOT_ID || (!sd_id128_is_null(previous_id) && offset == 0) || unit);
assert(ret);
- /* Adjust for the asymmetry that offset 0 is the last (and current) boot, while 1 is considered the
- * (chronological) first boot in the journal. */
+ /* Adjust for the asymmetry that offset 0 is the last (and current) boot or invocation, while 1 is
+ * considered the (chronological) first boot or invocation in the journal. */
advance_older = offset <= 0;
- sd_journal_flush_matches(j);
-
- if (!sd_id128_is_null(boot_id)) {
- r = add_match_boot_id(j, boot_id);
- if (r < 0)
- return r;
+ r = set_matches_for_discover_id(j, type, boot_id, unit, previous_id);
+ if (r < 0)
+ return r;
+ if (!sd_id128_is_null(previous_id)) {
if (advance_older)
r = sd_journal_seek_head(j); /* seek to oldest */
else
return false;
}
if (offset == 0) {
- /* If boot ID is specified without an offset, then let's short cut the loop below. */
+ /* If a non-null ID is specified without an offset, then let's short cut the loop below. */
sd_journal_flush_matches(j);
- *ret = boot_id;
+ *ret = previous_id;
return true;
}
offset_start = advance_older ? 0 : 1;
}
- /* At this point the cursor is positioned at the newest/oldest entry of the reference boot ID if
- * specified, or whole journal otherwise. The next invocation of _previous()/_next() will hence
- * position us at the newest/oldest entry we have. */
+ /* At this point the cursor is positioned at the newest/oldest entry of the reference boot or
+ * invocation ID if specified, or whole journal otherwise. The next invocation of _previous()/_next()
+ * will hence position us at the newest/oldest entry we have. */
for (int off = offset_start; ; off += advance_older ? -1 : 1) {
LogId id;
- r = discover_next_boot(j, boot_id, advance_older, &id);
+ r = discover_next_id(j, type, boot_id, unit, previous_id, advance_older, &id);
if (r < 0)
return r;
if (r == 0) {
return false;
}
- boot_id = id.id;
- log_debug("Found boot ID %s by offset %i", SD_ID128_TO_STRING(boot_id), off);
+ previous_id = id.id;
+ log_debug("Found %s %s by offset %i.",
+ log_id_type_to_string(type), SD_ID128_TO_STRING(previous_id), off);
if (off == offset) {
- *ret = boot_id;
+ *ret = previous_id;
return true;
}
}
}
-int journal_get_boots(
+int journal_get_log_ids(
sd_journal *j,
+ LogIdType type,
+ sd_id128_t boot_id, /* optional, used when type == JOURNAL_{SYSTEM,USER}_UNIT_INVOCATION_ID */
+ const char *unit, /* mandatory when type == JOURNAL_{SYSTEM,USER}_UNIT_INVOCATION_ID */
bool advance_older,
size_t max_ids,
LogId **ret_ids,
int r;
assert(j);
+ assert(type >= 0 && type < _LOG_ID_TYPE_MAX);
+ assert(type == LOG_BOOT_ID || unit);
assert(ret_ids);
assert(ret_n_ids);
if (n_ids >= max_ids)
break;
- r = discover_next_boot(j, previous_id, advance_older, &id);
+ r = discover_next_id(j, type, boot_id, unit, previous_id, advance_older, &id);
if (r < 0)
return r;
if (r == 0)
FOREACH_ARRAY(i, ids, n_ids)
if (sd_id128_equal(i->id, id.id))
- /* The boot id is already stored, something wrong with the journal files.
- * Exiting as otherwise this problem would cause an infinite loop. */
+ /* The boot or invocation ID is already stored, something wrong with the
+ * journal files. Exiting as otherwise this problem would cause an infinite
+ * loop. */
goto finish;
if (!GREEDY_REALLOC_APPEND(ids, n_ids, &id, 1))