From: Yu Watanabe Date: Thu, 25 Apr 2024 06:28:19 +0000 (+0900) Subject: logs-show: extend journal_get_boots() and friends to find invocation IDs X-Git-Tag: v257-rc1~786^2~2 X-Git-Url: http://git-history.diyao.me/?a=commitdiff_plain;h=82721be7d60c337374b1b75c153d9d12791f7f43;p=systemd%2F.git logs-show: extend journal_get_boots() and friends to find invocation IDs Currently the extended features are not used, but will be used later. --- diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index 7f8171af49..96955497a2 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -1799,8 +1799,117 @@ int show_journal_by_unit( 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) { @@ -1809,6 +1918,8 @@ static int discover_next_boot( 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 @@ -1820,7 +1931,9 @@ static int discover_next_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; @@ -1835,15 +1948,22 @@ static int discover_next_boot( 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. */ @@ -1853,16 +1973,17 @@ static int discover_next_boot( 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); @@ -1875,9 +1996,11 @@ static int discover_next_boot( 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; } @@ -1885,7 +2008,7 @@ static int discover_next_boot( 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 @@ -1897,9 +2020,11 @@ static int discover_next_boot( 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; } @@ -1912,7 +2037,7 @@ static int discover_next_boot( 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; @@ -1922,13 +2047,9 @@ static int discover_next_boot( 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); @@ -1942,31 +2063,41 @@ static int discover_next_boot( 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 @@ -1983,9 +2114,9 @@ int journal_find_boot(sd_journal *j, sd_id128_t boot_id, int offset, sd_id128_t 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; } @@ -2001,14 +2132,14 @@ int journal_find_boot(sd_journal *j, sd_id128_t boot_id, int offset, sd_id128_t 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) { @@ -2016,18 +2147,22 @@ int journal_find_boot(sd_journal *j, sd_id128_t boot_id, int offset, sd_id128_t 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, @@ -2038,6 +2173,8 @@ int journal_get_boots( 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); @@ -2062,7 +2199,7 @@ int journal_get_boots( 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) @@ -2072,8 +2209,9 @@ int journal_get_boots( 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)) diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h index 7d7a918179..d000978c04 100644 --- a/src/shared/logs-show.h +++ b/src/shared/logs-show.h @@ -20,6 +20,14 @@ typedef struct LogId { usec_t last_usec; } LogId; +typedef enum LogIdType { + LOG_BOOT_ID, + LOG_SYSTEM_UNIT_INVOCATION_ID, + LOG_USER_UNIT_INVOCATION_ID, + _LOG_ID_TYPE_MAX, + _LOG_ID_TYPE_INVALID = -EINVAL, +} LogIdType; + int show_journal_entry( FILE *f, sd_journal *j, @@ -74,10 +82,45 @@ void json_escape( size_t l, OutputFlags flags); -int journal_find_boot(sd_journal *j, sd_id128_t boot_id, int offset, sd_id128_t *ret); -int journal_get_boots( +int journal_find_log_id( + sd_journal *j, + LogIdType type, + sd_id128_t boot_id, + const char *unit, + sd_id128_t id, + int offset, + sd_id128_t *ret); + +static inline int journal_find_boot( + sd_journal *j, + sd_id128_t id, + int offset, + sd_id128_t *ret) { + + return journal_find_log_id(j, LOG_BOOT_ID, + /* boot_id = */ SD_ID128_NULL, /* unit = */ NULL, + id, offset, ret); +} + +int journal_get_log_ids( sd_journal *j, + LogIdType type, + sd_id128_t boot_id, + const char *unit, bool advance_older, size_t max_ids, LogId **ret_ids, size_t *ret_n_ids); + +static inline int journal_get_boots( + sd_journal *j, + bool advance_older, + size_t max_ids, + LogId **ret_ids, + size_t *ret_n_ids) { + + return journal_get_log_ids(j, LOG_BOOT_ID, + /* boot_id = */ SD_ID128_NULL, /* unit = */ NULL, + advance_older, max_ids, + ret_ids, ret_n_ids); +}