From 9bc4156cdf75dffdb9b5217e353921cd4a7660ae Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 20 Oct 2022 23:08:45 +0200 Subject: [PATCH 1/5] json: add build helpers to insert id128 in uuid formatting into json object --- src/shared/format-table.c | 4 ++-- src/shared/json.c | 11 +++++++++-- src/shared/json.h | 4 ++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/shared/format-table.c b/src/shared/format-table.c index 0549c9f12e..bf2c88d893 100644 --- a/src/shared/format-table.c +++ b/src/shared/format-table.c @@ -2573,10 +2573,10 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) { return json_variant_new_array_bytes(ret, &d->address, FAMILY_ADDRESS_SIZE(AF_INET6)); case TABLE_ID128: - return json_variant_new_string(ret, SD_ID128_TO_STRING(d->id128)); + return json_variant_new_id128(ret, d->id128); case TABLE_UUID: - return json_variant_new_string(ret, SD_ID128_TO_UUID_STRING(d->id128)); + return json_variant_new_uuid(ret, d->id128); case TABLE_UID: if (!uid_is_valid(d->uid)) diff --git a/src/shared/json.c b/src/shared/json.c index eda7bb1956..94d7b31557 100644 --- a/src/shared/json.c +++ b/src/shared/json.c @@ -481,6 +481,10 @@ int json_variant_new_id128(JsonVariant **ret, sd_id128_t id) { return json_variant_new_string(ret, SD_ID128_TO_STRING(id)); } +int json_variant_new_uuid(JsonVariant **ret, sd_id128_t id) { + return json_variant_new_string(ret, SD_ID128_TO_UUID_STRING(id)); +} + static void json_variant_set(JsonVariant *a, JsonVariant *b) { assert(a); @@ -3609,7 +3613,8 @@ int json_buildv(JsonVariant **ret, va_list ap) { break; } - case _JSON_BUILD_ID128: { + case _JSON_BUILD_ID128: + case _JSON_BUILD_UUID: { const sd_id128_t *id; if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { @@ -3620,7 +3625,9 @@ int json_buildv(JsonVariant **ret, va_list ap) { assert_se(id = va_arg(ap, sd_id128_t*)); if (current->n_suppress == 0) { - r = json_variant_new_id128(&add, *id); + r = command == _JSON_BUILD_ID128 ? + json_variant_new_id128(&add, *id) : + json_variant_new_uuid(&add, *id); if (r < 0) goto finish; } diff --git a/src/shared/json.h b/src/shared/json.h index 5993e05299..c5f052a9d5 100644 --- a/src/shared/json.h +++ b/src/shared/json.h @@ -75,6 +75,7 @@ int json_variant_new_array_strv(JsonVariant **ret, char **l); int json_variant_new_object(JsonVariant **ret, JsonVariant **array, size_t n); int json_variant_new_null(JsonVariant **ret); int json_variant_new_id128(JsonVariant **ret, sd_id128_t id); +int json_variant_new_uuid(JsonVariant **ret, sd_id128_t id); static inline int json_variant_new_string(JsonVariant **ret, const char *s) { return json_variant_new_stringn(ret, s, SIZE_MAX); @@ -251,6 +252,7 @@ enum { _JSON_BUILD_HEX, _JSON_BUILD_OCTESCAPE, _JSON_BUILD_ID128, + _JSON_BUILD_UUID, _JSON_BUILD_BYTE_ARRAY, _JSON_BUILD_HW_ADDR, _JSON_BUILD_PAIR_UNSIGNED_NON_ZERO, @@ -288,6 +290,7 @@ enum { #define JSON_BUILD_HEX(p, n) _JSON_BUILD_HEX, (const void*) { p }, (size_t) { n } #define JSON_BUILD_OCTESCAPE(p, n) _JSON_BUILD_OCTESCAPE, (const void*) { p }, (size_t) { n } #define JSON_BUILD_ID128(id) _JSON_BUILD_ID128, (const sd_id128_t*) { &(id) } +#define JSON_BUILD_UUID(id) _JSON_BUILD_UUID, (const sd_id128_t*) { &(id) } #define JSON_BUILD_BYTE_ARRAY(v, n) _JSON_BUILD_BYTE_ARRAY, (const void*) { v }, (size_t) { n } #define JSON_BUILD_CONST_STRING(s) _JSON_BUILD_VARIANT, JSON_VARIANT_STRING_CONST(s) #define JSON_BUILD_IN4_ADDR(v) JSON_BUILD_BYTE_ARRAY((const struct in_addr*) { v }, sizeof(struct in_addr)) @@ -313,6 +316,7 @@ enum { #define JSON_BUILD_PAIR_BASE64(name, p, n) JSON_BUILD_PAIR(name, JSON_BUILD_BASE64(p, n)) #define JSON_BUILD_PAIR_HEX(name, p, n) JSON_BUILD_PAIR(name, JSON_BUILD_HEX(p, n)) #define JSON_BUILD_PAIR_ID128(name, id) JSON_BUILD_PAIR(name, JSON_BUILD_ID128(id)) +#define JSON_BUILD_PAIR_UUID(name, id) JSON_BUILD_PAIR(name, JSON_BUILD_UUID(id)) #define JSON_BUILD_PAIR_BYTE_ARRAY(name, v, n) JSON_BUILD_PAIR(name, JSON_BUILD_BYTE_ARRAY(v, n)) #define JSON_BUILD_PAIR_IN4_ADDR(name, v) JSON_BUILD_PAIR(name, JSON_BUILD_IN4_ADDR(v)) #define JSON_BUILD_PAIR_IN6_ADDR(name, v) JSON_BUILD_PAIR(name, JSON_BUILD_IN6_ADDR(v)) From b387778c5b8a1e0d1b3f957f0c5866defbfbe4d4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 20 Oct 2022 23:14:28 +0200 Subject: [PATCH 2/5] dissect: also parse out the top-level GPT table uuid and expose this as image UUID systemd-repart generates this in a suitably stable fashion, hence let's actually use it as an identifier for the image. As a first step parse it, and show it. --- src/dissect/dissect.c | 4 ++++ src/shared/dissect-image.c | 26 +++++++++++++++++++++++--- src/shared/dissect-image.h | 1 + 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index 81764690e9..2512cd0f0b 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -594,6 +594,9 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { else if (arg_json_format_flags & JSON_FORMAT_OFF) { _cleanup_strv_free_ char **sysext_scopes = NULL; + if (!sd_id128_is_null(m->image_uuid)) + printf("Image UUID: %s\n", SD_ID128_TO_UUID_STRING(m->image_uuid)); + if (m->hostname) printf(" Hostname: %s\n", m->hostname); @@ -673,6 +676,7 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { r = json_build(&v, JSON_BUILD_OBJECT( JSON_BUILD_PAIR("name", JSON_BUILD_STRING(bn)), + JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(m->image_uuid), "imageUuid", JSON_BUILD_UUID(m->image_uuid)), JSON_BUILD_PAIR("size", JSON_BUILD_INTEGER(size)), JSON_BUILD_PAIR_CONDITION(m->hostname, "hostname", JSON_BUILD_STRING(m->hostname)), JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(m->machine_id), "machineId", JSON_BUILD_ID128(m->machine_id)), diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 2f19031216..1d338fe352 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -349,7 +349,7 @@ static int dissect_image( _cleanup_(blkid_free_probep) blkid_probe b = NULL; _cleanup_free_ char *generic_node = NULL; sd_id128_t generic_uuid = SD_ID128_NULL; - const char *pttype = NULL; + const char *pttype = NULL, *sptuuid = NULL; blkid_partlist pl; int r, generic_nr = -1, n_partitions; @@ -410,7 +410,7 @@ static int dissect_image( if ((flags & DISSECT_IMAGE_GPT_ONLY) == 0) { /* Look for file system superblocks, unless we only shall look for GPT partition tables */ blkid_probe_enable_superblocks(b, 1); - blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_USAGE); + blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_USAGE|BLKID_SUBLKS_UUID); } blkid_probe_enable_partitions(b, 1); @@ -433,8 +433,9 @@ static int dissect_image( (void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL); if (STRPTR_IN_SET(usage, "filesystem", "crypto")) { _cleanup_free_ char *t = NULL, *n = NULL, *o = NULL; - const char *fstype = NULL, *options = NULL; + const char *fstype = NULL, *options = NULL, *suuid = NULL; _cleanup_close_ int mount_node_fd = -1; + sd_id128_t uuid = SD_ID128_NULL; if (FLAGS_SET(flags, DISSECT_IMAGE_OPEN_PARTITION_DEVICES)) { mount_node_fd = open_partition(devname, /* is_partition = */ false, m->loop); @@ -444,6 +445,7 @@ static int dissect_image( /* OK, we have found a file system, that's our root partition then. */ (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL); + (void) blkid_probe_lookup_value(b, "UUID", &suuid, NULL); if (fstype) { t = strdup(fstype); @@ -451,6 +453,15 @@ static int dissect_image( return -ENOMEM; } + if (suuid) { + /* blkid will return FAT's serial number as UUID, hence it is quite possible + * that parsing this will fail. We'll ignore the ID, since it's just too + * short to be useful as tru identifier. */ + r = sd_id128_from_string(suuid, &uuid); + if (r < 0) + log_debug_errno(r, "Failed to parse file system UUID '%s', ignoring: %m", suuid); + } + n = strdup(devname); if (!n) return -ENOMEM; @@ -467,6 +478,8 @@ static int dissect_image( m->verity_sig_ready = m->verity_ready && verity->root_hash_sig; + m->image_uuid = uuid; + options = mount_options_from_designator(mount_options, PARTITION_ROOT); if (options) { o = strdup(options); @@ -515,6 +528,13 @@ static int dissect_image( return -EPROTONOSUPPORT; } + (void) blkid_probe_lookup_value(b, "PTUUID", &sptuuid, NULL); + if (sptuuid) { + r = sd_id128_from_string(sptuuid, &m->image_uuid); + if (r < 0) + log_debug_errno(r, "Failed to parse partition table UUID '%s', ignoring: %m", sptuuid); + } + errno = 0; pl = blkid_probe_get_partitions(b); if (!pl) diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index 46675d22ab..ccdc4d6f35 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -229,6 +229,7 @@ struct DissectedImage { /* Meta information extracted from /etc/os-release and similar */ char *image_name; + sd_id128_t image_uuid; char *hostname; sd_id128_t machine_id; char **machine_info; From 5db8b0bcadd17708c0cc8268df844221fe88f7a8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 9 Nov 2022 22:51:42 +0100 Subject: [PATCH 3/5] dissect: make image name bold --- src/dissect/dissect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index 2512cd0f0b..1ee4119d4f 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -568,7 +568,7 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { pager_open(arg_pager_flags); if (arg_json_format_flags & JSON_FORMAT_OFF) - printf(" Name: %s\n", bn); + printf(" Name: %s%s%s\n", ansi_highlight(), bn, ansi_normal()); if (ioctl(d->fd, BLKGETSIZE64, &size) < 0) log_debug_errno(errno, "Failed to query size of loopback device: %m"); From 71ea84369e458eb6b4f4664dd7daa47bc96c7d32 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 10 Nov 2022 17:18:50 +0100 Subject: [PATCH 4/5] locale: honour new env var $SYSTEMD_UTF8= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is useful to force off fancy unicode glyph use (i.e. use "->" instead of "→"), which is useful in tests where locales might be missing, and thus control via $LC_CTYPE is not reliable. Use this in TEST-58, to ensure the output checks we do aren't confused by missing these glyphs being unicode or not. --- docs/ENVIRONMENT.md | 6 ++++++ src/basic/locale-util.c | 10 +++++++++- test/units/testsuite-58.sh | 19 +++++++++++-------- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index ab3add6031..61ad075085 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -85,6 +85,12 @@ All tools: * `$SYSTEMD_MEMPOOL=0` — if set, the internal memory caching logic employed by hash tables is turned off, and libc `malloc()` is used for all allocations. +* `$SYSTEMD_UTF8=` — takes a boolean value, and overrides whether to generate + non-ASCII special glyphs at various places (i.e. "→" instead of + "->"). Usually this is deterined automatically, based on $LC_CTYPE, but in + scenarios where locale definitions are not installed it might make sense to + override this check explicitly. + * `$SYSTEMD_EMOJI=0` — if set, tools such as `systemd-analyze security` will not output graphical smiley emojis, but ASCII alternatives instead. Note that this only controls use of Unicode emoji glyphs, and has no effect on other diff --git a/src/basic/locale-util.c b/src/basic/locale-util.c index 2f486a4d92..40c6e46ab8 100644 --- a/src/basic/locale-util.c +++ b/src/basic/locale-util.c @@ -286,8 +286,9 @@ void init_gettext(void) { } bool is_locale_utf8(void) { - const char *set; static int cached_answer = -1; + const char *set; + int r; /* Note that we default to 'true' here, since today UTF8 is * pretty much supported everywhere. */ @@ -295,6 +296,13 @@ bool is_locale_utf8(void) { if (cached_answer >= 0) goto out; + r = getenv_bool_secure("SYSTEMD_UTF8"); + if (r >= 0) { + cached_answer = r; + goto out; + } else if (r != -ENXIO) + log_debug_errno(r, "Failed to parse $SYSTEMD_UTF8, ignoring: %m"); + if (!setlocale(LC_ALL, "")) { cached_answer = true; goto out; diff --git a/test/units/testsuite-58.sh b/test/units/testsuite-58.sh index f41069ee04..8787a24c0e 100755 --- a/test/units/testsuite-58.sh +++ b/test/units/testsuite-58.sh @@ -14,6 +14,9 @@ fi export SYSTEMD_LOG_LEVEL=debug export PAGER=cat +# Disable use of special glyphs such as → +export SYSTEMD_UTF8=0 + seed=750b6cd5c4ae4012a15e7be3c29e6a47 if ! systemd-detect-virt --quiet --container; then @@ -327,7 +330,7 @@ EOF output=$(systemd-repart --definitions="$defs" --empty=create --size=100M --json=pretty "$imgs/zzz") - diff <(echo "$output") - < 32.0M", "old_padding" : 0, "raw_padding" : 0, - "padding" : "→ 0B", + "padding" : "-> 0B", "activity" : "create", "drop-in_files" : [ "$defs/root.conf.d/override1.conf", @@ -382,7 +385,7 @@ EOF output=$(systemd-repart --definitions="$defs/1" --definitions="$defs/2" --empty=create --size=100M --json=pretty "$imgs/zzz") - diff <(echo "$output") - < 32.0M", "old_padding" : 0, "raw_padding" : 0, - "padding" : "→ 0B", + "padding" : "-> 0B", "activity" : "create" }, { @@ -408,10 +411,10 @@ EOF "offset" : 34603008, "old_size" : 0, "raw_size" : 33554432, - "size" : "→ 32.0M", + "size" : "-> 32.0M", "old_padding" : 0, "raw_padding" : 0, - "padding" : "→ 0B", + "padding" : "-> 0B", "activity" : "create" } ] From 63b130a6fcf6029c2a96e61b03912e78b1f9c6b0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 10 Nov 2022 17:33:31 +0100 Subject: [PATCH 5/5] test: add quick test for ensuring image UUID is deterministic based on the seed passed to repart --- test/units/testsuite-58.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/units/testsuite-58.sh b/test/units/testsuite-58.sh index 8787a24c0e..5a515d9c01 100755 --- a/test/units/testsuite-58.sh +++ b/test/units/testsuite-58.sh @@ -769,9 +769,10 @@ EOF roothash=$(jq -r ".[] | select(.type == \"root-${architecture}-verity\") | .roothash" <<< "$output") - # Check that we can dissect, mount and unmount a repart verity image. + # Check that we can dissect, mount and unmount a repart verity image. (and that the image UUID is deterministic) systemd-dissect "$imgs/verity" --root-hash "$roothash" + systemd-dissect "$imgs/verity" --root-hash "$roothash" --json=short | grep -q '"imageUuid":"1d2ce291-7cce-4f7d-bc83-fdb49ad74ebd"' systemd-dissect "$imgs/verity" --root-hash "$roothash" -M "$imgs/mnt" systemd-dissect -U "$imgs/mnt" }