From e2227c8a207e7afbaa07cd796aac5728d423bf4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 8 Apr 2026 16:41:06 +0200 Subject: [PATCH 01/13] nspawn,vmspawn: fixups for recent changes Nits found in post-merge review for bf5bc9a7b26191ff4254836bd909cbb92eafe480 and 4c778c51c07db3eb0e07dd875f10eb85f086f096. --- src/nspawn/nspawn.c | 4 ++-- src/vmspawn/vmspawn.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 211f124893..10fda88c48 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -379,7 +379,7 @@ static int help(void) { if (r < 0) return log_oom(); - static const char *groups[] = { + static const char* const groups[] = { NULL, "Image", "Execution", @@ -1372,7 +1372,7 @@ static int parse_argv(int argc, char *argv[]) { * been repurposed to optionally set the runtime scope, with --uid= replacing * the old container user functionality. To maintain backwards compatibility * with the space-separated form (--user NAME), if the next arg does not look - * like an option, interpret it a user name. */ + * like an option, interpret it as a user name. */ const char *t = option_parser_next_arg(&state); if (t && t[0] != '-') { arg = option_parser_consume_next_arg(&state); diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c index 93c8fbbbbd..f5dc4f17a8 100644 --- a/src/vmspawn/vmspawn.c +++ b/src/vmspawn/vmspawn.c @@ -218,7 +218,7 @@ static int help(void) { if (r < 0) return log_oom(); - static const char *groups[] = { + static const char* const groups[] = { NULL, "Image", "Host Configuration", From 74573799ca6d11b6f945bd900b2cb5f818b4a976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 8 Apr 2026 17:17:32 +0200 Subject: [PATCH 02/13] Add DEFINE_ARRAY_DONE_FUNC This is a helper macro that defines a function to drop elements of an array but not the array itself. I used the "_many" suffix because it most closely matches what happens here: we are calling the cleanup function a bunch of times. --- src/fundamental/cleanup-fundamental.h | 8 ++++++++ src/nspawn/nspawn.c | 7 +------ src/shared/format-table.h | 1 + src/vmspawn/vmspawn.c | 7 +------ 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/fundamental/cleanup-fundamental.h b/src/fundamental/cleanup-fundamental.h index 86b9851fd5..91d7cdd0e8 100644 --- a/src/fundamental/cleanup-fundamental.h +++ b/src/fundamental/cleanup-fundamental.h @@ -45,6 +45,14 @@ #define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_MACRO(type, macro, empty) \ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_MACRO_RENAME(type, macro, macro##p, empty) +/* Clean up a NULL-terminated array by dropping all the items in it (up to the first NULL). + * The array itself is not deallocated. */ +#define DEFINE_ARRAY_DONE_FUNC(type, helper) \ + void helper ## _many(type (*p)[]) { \ + for (type *t = *ASSERT_PTR(p); *t; t++) \ + *t = helper(*t); \ + } + typedef void (*free_array_func_t)(void *p, size_t n); /* An automatic _cleanup_-like logic for destroy arrays (i.e. pointers + size) when leaving scope */ diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 10fda88c48..accf448ea9 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -364,11 +364,6 @@ static int parse_private_users( return 0; } -static void unref_many_tables(Table* (*tablesp)[]) { - for (Table **t = *ASSERT_PTR(tablesp); *t; t++) - *t = table_unref(*t); -} - static int help(void) { _cleanup_free_ char *link = NULL; int r; @@ -396,7 +391,7 @@ static int help(void) { "Other", }; - _cleanup_(unref_many_tables) Table* tables[ELEMENTSOF(groups) + 1] = {}; + _cleanup_(table_unref_many) Table* tables[ELEMENTSOF(groups) + 1] = {}; for (size_t i = 0; i < ELEMENTSOF(groups); i++) { r = option_parser_get_help_table_group(groups[i], &tables[i]); diff --git a/src/shared/format-table.h b/src/shared/format-table.h index 0f52f80293..7665c93e59 100644 --- a/src/shared/format-table.h +++ b/src/shared/format-table.h @@ -100,6 +100,7 @@ Table* table_new_vertical(void); Table* table_unref(Table *t); DEFINE_TRIVIAL_CLEANUP_FUNC(Table*, table_unref); +static inline DEFINE_ARRAY_DONE_FUNC(Table*, table_unref); int table_add_cell_full(Table *t, TableCell **ret_cell, TableDataType dt, const void *data, size_t minimum_width, size_t maximum_width, unsigned weight, unsigned align_percent, unsigned ellipsize_percent); static inline int table_add_cell(Table *t, TableCell **ret_cell, TableDataType dt, const void *data) { diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c index f5dc4f17a8..5064f09c7d 100644 --- a/src/vmspawn/vmspawn.c +++ b/src/vmspawn/vmspawn.c @@ -203,11 +203,6 @@ STATIC_DESTRUCTOR_REGISTER(arg_bind_user, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_bind_user_shell, freep); STATIC_DESTRUCTOR_REGISTER(arg_bind_user_groups, strv_freep); -static void unref_many_tables(Table* (*tablesp)[]) { - for (Table **t = *ASSERT_PTR(tablesp); *t; t++) - *t = table_unref(*t); -} - static int help(void) { _cleanup_free_ char *link = NULL; int r; @@ -232,7 +227,7 @@ static int help(void) { "Credentials", }; - _cleanup_(unref_many_tables) Table* tables[ELEMENTSOF(groups) + 1] = {}; + _cleanup_(table_unref_many) Table* tables[ELEMENTSOF(groups) + 1] = {}; for (size_t i = 0; i < ELEMENTSOF(groups); i++) { r = option_parser_get_help_table_group(groups[i], &tables[i]); From 3ed9e7cd23185780b41a57bdc1e0e4950cb7dee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 8 Apr 2026 17:37:41 +0200 Subject: [PATCH 03/13] firewall-util: use DEFINE_ARRAY_DONE_FUNC for netlink message cleanup Replace the open-coded netlink_message_unref_many() function and its DEFINE_TRIVIAL_CLEANUP_FUNC wrapper with DEFINE_ARRAY_DONE_FUNC. Co-developed-by: Claude Opus 4.6 --- src/shared/firewall-util.c | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/src/shared/firewall-util.c b/src/shared/firewall-util.c index 93eef4eecf..651870e369 100644 --- a/src/shared/firewall-util.c +++ b/src/shared/firewall-util.c @@ -50,19 +50,7 @@ static const char* dnat_map_name(void) { return cached; } -static sd_netlink_message** netlink_message_unref_many(sd_netlink_message **m) { - if (!m) - return NULL; - - /* This does not free array. The end of the array must be NULL. */ - - for (sd_netlink_message **p = m; *p; p++) - *p = sd_netlink_message_unref(*p); - - return m; -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(sd_netlink_message**, netlink_message_unref_many); +static DEFINE_ARRAY_DONE_FUNC(sd_netlink_message*, sd_netlink_message_unref); static int nfnl_open_expr_container(sd_netlink_message *m, const char *name) { int r; @@ -736,8 +724,7 @@ static uint32_t concat_types2(enum nft_key_types a, enum nft_key_types b) { } static int fw_nftables_init_family(sd_netlink *nfnl, int family) { - sd_netlink_message *messages[10] = {}; - _unused_ _cleanup_(netlink_message_unref_manyp) sd_netlink_message **unref = messages; + _cleanup_(sd_netlink_message_unref_many) sd_netlink_message *messages[10] = {}; size_t msgcnt = 0, ip_type_size; uint32_t set_id = 0; int ip_type, r; @@ -1058,8 +1045,7 @@ static int fw_nftables_add_local_dnat_internal( uint16_t remote_port, const union in_addr_union *previous_remote) { - sd_netlink_message *messages[3] = {}; - _unused_ _cleanup_(netlink_message_unref_manyp) sd_netlink_message **unref = messages; + _cleanup_(sd_netlink_message_unref_many) sd_netlink_message *messages[3] = {}; uint32_t data[5], key[2], dlen; size_t msgcnt = 0; int r; From bbc6af9ce3e2fe1c18c88fab3b6d1218ad389cac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 8 Apr 2026 17:56:09 +0200 Subject: [PATCH 04/13] Add DEFINE_POINTER_ARRAY_FREE_FUNC and conf_file_free_array As mentioned in the grandfather commit, I want to use the _many suffix for freeing of the contents of an array, so the functions to free the array to get the suffix _array. --- src/basic/conf-files.c | 11 +++-------- src/basic/conf-files.h | 2 +- src/fundamental/cleanup-fundamental.h | 11 +++++++++++ src/libsystemd/sd-journal/catalog.c | 2 +- src/modules-load/modules-load.c | 2 +- src/resolve/resolved-static-records.c | 2 +- src/shared/hwdb-util.c | 2 +- src/shared/pretty-print.c | 2 +- src/sysupdate/sysupdate.c | 6 +++--- src/udev/udev-rules.c | 2 +- src/udev/udevadm-cat.c | 2 +- src/udev/udevadm-util.c | 6 +++--- src/udev/udevadm-verify.c | 2 +- 13 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c index cd91c5bc1a..4db486d8ad 100644 --- a/src/basic/conf-files.c +++ b/src/basic/conf-files.c @@ -33,12 +33,7 @@ ConfFile* conf_file_free(ConfFile *c) { return mfree(c); } -void conf_file_free_many(ConfFile **array, size_t n) { - FOREACH_ARRAY(i, array, n) - conf_file_free(*i); - - free(array); -} +DEFINE_POINTER_ARRAY_FREE_FUNC(ConfFile*, conf_file_free); static int conf_files_log_level(ConfFilesFlags flags) { return FLAGS_SET(flags, CONF_FILES_WARN) ? LOG_WARNING : LOG_DEBUG; @@ -485,7 +480,7 @@ static int dump_files(Hashmap *fh, const char *root, ConfFilesFlags flags, ConfF size_t n_files = 0; int r; - CLEANUP_ARRAY(files, n_files, conf_file_free_many); + CLEANUP_ARRAY(files, n_files, conf_file_free_array); assert(ret_files); assert(ret_n_files); @@ -528,7 +523,7 @@ static int copy_and_sort_files_from_hashmap( int log_level = conf_files_log_level(flags); /* The entries in the array given by hashmap_dump_sorted() are still owned by the hashmap. - * Hence, do not use conf_file_free_many() for 'entries' */ + * Hence, do not use conf_file_free_array() for 'entries' */ r = hashmap_dump_sorted(fh, (void***) &files, &n_files); if (r < 0) return log_oom_full(log_level); diff --git a/src/basic/conf-files.h b/src/basic/conf-files.h index 031463172a..e6c248a59a 100644 --- a/src/basic/conf-files.h +++ b/src/basic/conf-files.h @@ -29,7 +29,7 @@ typedef struct ConfFile { ConfFile* conf_file_free(ConfFile *c); DEFINE_TRIVIAL_CLEANUP_FUNC(ConfFile*, conf_file_free); -void conf_file_free_many(ConfFile **array, size_t n); +void conf_file_free_array(ConfFile **array, size_t n); int conf_file_new_at(const char *path, const char *root, int rfd, ConfFilesFlags flags, ConfFile **ret); int conf_file_new(const char *path, const char *root, ConfFilesFlags flags, ConfFile **ret); diff --git a/src/fundamental/cleanup-fundamental.h b/src/fundamental/cleanup-fundamental.h index 91d7cdd0e8..b4f23a5920 100644 --- a/src/fundamental/cleanup-fundamental.h +++ b/src/fundamental/cleanup-fundamental.h @@ -53,6 +53,17 @@ *t = helper(*t); \ } +/* Clean up an array of pointers to objects by dropping all the items in it. + * The size of the array is passed in as a parameter, so NULL items may appear in the middle of the array. + * Free the array itself afterwards. */ +#define DEFINE_POINTER_ARRAY_FREE_FUNC(type, helper) \ + void helper ## _array(type *array, size_t n) { \ + assert(array || n == 0); \ + FOREACH_ARRAY(item, array, n) \ + helper(*item); \ + free(array); \ + } + typedef void (*free_array_func_t)(void *p, size_t n); /* An automatic _cleanup_-like logic for destroy arrays (i.e. pointers + size) when leaving scope */ diff --git a/src/libsystemd/sd-journal/catalog.c b/src/libsystemd/sd-journal/catalog.c index a44ffe5585..5d91b6a0e5 100644 --- a/src/libsystemd/sd-journal/catalog.c +++ b/src/libsystemd/sd-journal/catalog.c @@ -451,7 +451,7 @@ int catalog_update(const char *database, const char *root, const char* const *di ConfFile **files = NULL; size_t n_files = 0; - CLEANUP_ARRAY(files, n_files, conf_file_free_many); + CLEANUP_ARRAY(files, n_files, conf_file_free_array); r = conf_files_list_strv_full(".catalog", root, CONF_FILES_REGULAR | CONF_FILES_FILTER_MASKED | CONF_FILES_WARN, diff --git a/src/modules-load/modules-load.c b/src/modules-load/modules-load.c index f644c9f0f0..e8aeb67f1f 100644 --- a/src/modules-load/modules-load.c +++ b/src/modules-load/modules-load.c @@ -417,7 +417,7 @@ static int run(int argc, char *argv[]) { ConfFile **files = NULL; size_t n_files = 0; - CLEANUP_ARRAY(files, n_files, conf_file_free_many); + CLEANUP_ARRAY(files, n_files, conf_file_free_array); STRV_FOREACH(i, arg_proc_cmdline_modules) RET_GATHER(ret, modules_list_append_dup(&module_set, *i)); diff --git a/src/resolve/resolved-static-records.c b/src/resolve/resolved-static-records.c index d905d507d6..d4d284d4f7 100644 --- a/src/resolve/resolved-static-records.c +++ b/src/resolve/resolved-static-records.c @@ -145,7 +145,7 @@ static int manager_static_records_read(Manager *m) { ConfFile **files = NULL; size_t n_files = 0; - CLEANUP_ARRAY(files, n_files, conf_file_free_many); + CLEANUP_ARRAY(files, n_files, conf_file_free_array); r = conf_files_list_nulstr_full( ".rr", diff --git a/src/shared/hwdb-util.c b/src/shared/hwdb-util.c index 29942254f3..55579c2cf4 100644 --- a/src/shared/hwdb-util.c +++ b/src/shared/hwdb-util.c @@ -608,7 +608,7 @@ int hwdb_update(const char *root, const char *hwdb_bin_dir, bool strict, bool co ConfFile **files = NULL; size_t n_files = 0; - CLEANUP_ARRAY(files, n_files, conf_file_free_many); + CLEANUP_ARRAY(files, n_files, conf_file_free_array); r = conf_files_list_strv_full(".hwdb", root, CONF_FILES_REGULAR | CONF_FILES_FILTER_MASKED | CONF_FILES_WARN, diff --git a/src/shared/pretty-print.c b/src/shared/pretty-print.c index 6899618051..76330cbb65 100644 --- a/src/shared/pretty-print.c +++ b/src/shared/pretty-print.c @@ -486,7 +486,7 @@ int conf_files_cat(const char *root, const char *name, CatFlags flags) { /* Then locate the drop-ins, if any */ ConfFile **dropins = NULL; size_t n_dropins = 0; - CLEANUP_ARRAY(dropins, n_dropins, conf_file_free_many); + CLEANUP_ARRAY(dropins, n_dropins, conf_file_free_array); r = conf_files_list_strv_full(extension, root, CONF_FILES_REGULAR | CONF_FILES_FILTER_MASKED | CONF_FILES_WARN, (const char* const*) dirs, &dropins, &n_dropins); if (r < 0) return log_error_errno(r, "Failed to query file list: %m"); diff --git a/src/sysupdate/sysupdate.c b/src/sysupdate/sysupdate.c index 57b2125f92..77cbb3a238 100644 --- a/src/sysupdate/sysupdate.c +++ b/src/sysupdate/sysupdate.c @@ -128,7 +128,7 @@ static int read_definitions( size_t n_files = 0, n_transfers = 0, n_disabled = 0; int r; - CLEANUP_ARRAY(files, n_files, conf_file_free_many); + CLEANUP_ARRAY(files, n_files, conf_file_free_array); CLEANUP_ARRAY(transfers, n_transfers, free_transfers); CLEANUP_ARRAY(disabled, n_disabled, free_transfers); @@ -208,7 +208,7 @@ static int context_read_definitions(Context *c, const char* node, bool requires_ ConfFile **files = NULL; size_t n_files = 0; - CLEANUP_ARRAY(files, n_files, conf_file_free_many); + CLEANUP_ARRAY(files, n_files, conf_file_free_array); r = conf_files_list_strv_full(".feature", arg_root, CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED|CONF_FILES_WARN, @@ -1718,7 +1718,7 @@ static int verb_components(int argc, char *argv[], uintptr_t _data, void *userda ConfFile **directories = NULL; size_t n_directories = 0; - CLEANUP_ARRAY(directories, n_directories, conf_file_free_many); + CLEANUP_ARRAY(directories, n_directories, conf_file_free_array); r = conf_files_list_strv_full(".d", arg_root, CONF_FILES_DIRECTORY|CONF_FILES_WARN, (const char * const *) CONF_PATHS_STRV(""), &directories, &n_directories); diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index 691230d753..70238b9b54 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -1841,7 +1841,7 @@ int udev_rules_load(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing ConfFile **files = NULL; size_t n_files = 0; - CLEANUP_ARRAY(files, n_files, conf_file_free_many); + CLEANUP_ARRAY(files, n_files, conf_file_free_array); r = conf_files_list_strv_full(".rules", /* root= */ NULL, CONF_FILES_REGULAR | CONF_FILES_FILTER_MASKED, (const char* const*) directories, &files, &n_files); diff --git a/src/udev/udevadm-cat.c b/src/udev/udevadm-cat.c index d6c488ff3f..9d94f5a86c 100644 --- a/src/udev/udevadm-cat.c +++ b/src/udev/udevadm-cat.c @@ -105,7 +105,7 @@ int verb_cat_main(int argc, char *argv[], uintptr_t _data, void *userdata) { ConfFile **files = NULL; size_t n_files = 0; - CLEANUP_ARRAY(files, n_files, conf_file_free_many); + CLEANUP_ARRAY(files, n_files, conf_file_free_array); r = search_rules_files(strv_skip(argv, optind), arg_root, &files, &n_files); if (r < 0) diff --git a/src/udev/udevadm-util.c b/src/udev/udevadm-util.c index 14b508007f..c30af47ff7 100644 --- a/src/udev/udevadm-util.c +++ b/src/udev/udevadm-util.c @@ -302,7 +302,7 @@ static int search_rules_file(const char *s, const char *root, ConfFile ***files, ConfFile **f = NULL; size_t n = 0; - CLEANUP_ARRAY(f, n, conf_file_free_many); + CLEANUP_ARRAY(f, n, conf_file_free_array); r = conf_files_list_strv_full(".rules", root, CONF_FILES_REGULAR | CONF_FILES_WARN, (const char* const*) STRV_MAKE_CONST(s), &f, &n); if (r < 0) @@ -311,7 +311,7 @@ static int search_rules_file(const char *s, const char *root, ConfFile ***files, if (!GREEDY_REALLOC_APPEND(*files, *n_files, f, n)) return log_oom(); - f = mfree(f); /* The array elements are owned by 'files'. So, conf_file_free_many() must not be called. */ + f = mfree(f); /* The array elements are owned by 'files'. So, conf_file_free_array() must not be called. */ n = 0; return 0; } @@ -321,7 +321,7 @@ int search_rules_files(char * const *a, const char *root, ConfFile ***ret_files, size_t n_files = 0; int r; - CLEANUP_ARRAY(files, n_files, conf_file_free_many); + CLEANUP_ARRAY(files, n_files, conf_file_free_array); assert(ret_files); assert(ret_n_files); diff --git a/src/udev/udevadm-verify.c b/src/udev/udevadm-verify.c index 03fed60d7e..6af7f06ab0 100644 --- a/src/udev/udevadm-verify.c +++ b/src/udev/udevadm-verify.c @@ -171,7 +171,7 @@ int verb_verify_main(int argc, char *argv[], uintptr_t _data, void *userdata) { ConfFile **files = NULL; size_t n_files = 0; - CLEANUP_ARRAY(files, n_files, conf_file_free_many); + CLEANUP_ARRAY(files, n_files, conf_file_free_array); r = search_rules_files(strv_skip(argv, optind), arg_root, &files, &n_files); if (r < 0) From 81d2d0270bb38d5a4087ef4eebb771b37b70b75e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 8 Apr 2026 18:27:34 +0200 Subject: [PATCH 05/13] Add DEFINE_ARRAY_FREE_FUNC and mount_image_free_array This is similar to DEFINE_POINTER_ARRAY_FREE_FUNC, but one pointer chase less. The name of the outer and inner functions are specified separately. The inner function does not free, so it'll be generally something like 'foo_done', but the outer function does free, so it can be called 'foo_array_free'. --- src/core/dbus-execute.c | 8 ++++---- src/core/execute.c | 4 ++-- src/core/load-fragment.c | 4 ++-- src/core/namespace.c | 17 +++++++---------- src/core/namespace.h | 2 +- src/fundamental/cleanup-fundamental.h | 10 ++++++++++ 6 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 9e6077c7e1..9843836eaf 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -4055,7 +4055,7 @@ int bus_exec_context_set_transient_property( MountImage *mount_images = NULL; size_t n_mount_images = 0; - CLEANUP_ARRAY(mount_images, n_mount_images, mount_image_free_many); + CLEANUP_ARRAY(mount_images, n_mount_images, mount_image_free_array); r = sd_bus_message_enter_container(message, 'a', "(ssba(ss))"); if (r < 0) @@ -4127,7 +4127,7 @@ int bus_exec_context_set_transient_property( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { if (n_mount_images == 0) { - mount_image_free_many(c->mount_images, c->n_mount_images); + mount_image_free_array(c->mount_images, c->n_mount_images); c->mount_images = NULL; c->n_mount_images = 0; @@ -4158,7 +4158,7 @@ int bus_exec_context_set_transient_property( MountImage *extension_images = NULL; size_t n_extension_images = 0; - CLEANUP_ARRAY(extension_images, n_extension_images, mount_image_free_many); + CLEANUP_ARRAY(extension_images, n_extension_images, mount_image_free_array); r = sd_bus_message_enter_container(message, 'a', "(sba(ss))"); if (r < 0) @@ -4220,7 +4220,7 @@ int bus_exec_context_set_transient_property( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { if (n_extension_images == 0) { - mount_image_free_many(c->extension_images, c->n_extension_images); + mount_image_free_array(c->extension_images, c->n_extension_images); c->extension_images = NULL; c->n_extension_images = 0; diff --git a/src/core/execute.c b/src/core/execute.c index 3661b8c98f..e6cb9e5cc8 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -696,10 +696,10 @@ void exec_context_done(ExecContext *c) { bind_mount_free_many(c->bind_mounts, c->n_bind_mounts); c->bind_mounts = NULL; c->n_bind_mounts = 0; - mount_image_free_many(c->mount_images, c->n_mount_images); + mount_image_free_array(c->mount_images, c->n_mount_images); c->mount_images = NULL; c->n_mount_images = 0; - mount_image_free_many(c->extension_images, c->n_extension_images); + mount_image_free_array(c->extension_images, c->n_extension_images); c->extension_images = NULL; c->n_extension_images = 0; c->extension_directories = strv_free(c->extension_directories); diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 840804fcf8..98e63b6cc8 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -5235,7 +5235,7 @@ int config_parse_mount_images( if (isempty(rvalue)) { /* Empty assignment resets the list */ - mount_image_free_many(c->mount_images, c->n_mount_images); + mount_image_free_array(c->mount_images, c->n_mount_images); c->mount_images = NULL; c->n_mount_images = 0; return 0; @@ -5385,7 +5385,7 @@ int config_parse_extension_images( if (isempty(rvalue)) { /* Empty assignment resets the list */ - mount_image_free_many(c->extension_images, c->n_extension_images); + mount_image_free_array(c->extension_images, c->n_extension_images); c->extension_images = NULL; c->n_extension_images = 0; return 0; diff --git a/src/core/namespace.c b/src/core/namespace.c index 6e4ec80dc9..ff4592b8b9 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -3250,18 +3250,15 @@ int bind_mount_add(BindMount **b, size_t *n, const BindMount *item) { return 0; } -void mount_image_free_many(MountImage *m, size_t n) { - assert(m || n == 0); - - FOREACH_ARRAY(i, m, n) { - free(i->source); - free(i->destination); - mount_options_free_all(i->mount_options); - } - - free(m); +static void mount_image_done(MountImage *m) { + assert(m); + m->source = mfree(m->source); + m->destination = mfree(m->destination); + m->mount_options = mount_options_free_all(m->mount_options); } +DEFINE_ARRAY_FREE_FUNC(mount_image_free_array, MountImage, mount_image_done); + int mount_image_add(MountImage **m, size_t *n, const MountImage *item) { _cleanup_free_ char *s = NULL, *d = NULL; _cleanup_(mount_options_free_allp) MountOptions *o = NULL; diff --git a/src/core/namespace.h b/src/core/namespace.h index f5d792dd77..d25859f17a 100644 --- a/src/core/namespace.h +++ b/src/core/namespace.h @@ -303,7 +303,7 @@ DECLARE_STRING_TABLE_LOOKUP(private_pids, PrivatePIDs); void bind_mount_free_many(BindMount *b, size_t n); int bind_mount_add(BindMount **b, size_t *n, const BindMount *item); -void mount_image_free_many(MountImage *m, size_t n); +void mount_image_free_array(MountImage *array, size_t n); int mount_image_add(MountImage **m, size_t *n, const MountImage *item); void temporary_filesystem_free_many(TemporaryFileSystem *t, size_t n); diff --git a/src/fundamental/cleanup-fundamental.h b/src/fundamental/cleanup-fundamental.h index b4f23a5920..9094cff233 100644 --- a/src/fundamental/cleanup-fundamental.h +++ b/src/fundamental/cleanup-fundamental.h @@ -64,6 +64,16 @@ free(array); \ } +/* Clean up an array of objects of known size by dropping all the items in it. + * Then free the array itself. */ +#define DEFINE_ARRAY_FREE_FUNC(name, type, helper) \ + void name(type *array, size_t n) { \ + assert(array || n == 0); \ + FOREACH_ARRAY(item, array, n) \ + helper(item); \ + free(array); \ + } + typedef void (*free_array_func_t)(void *p, size_t n); /* An automatic _cleanup_-like logic for destroy arrays (i.e. pointers + size) when leaving scope */ From 3b478198e906fa4fa12f8b8f38a5fe2fbb9893e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 8 Apr 2026 18:30:37 +0200 Subject: [PATCH 06/13] stub: use DEFINE_ARRAY_FREE_FUNC --- src/boot/stub.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/boot/stub.c b/src/boot/stub.c index 3c8318a2ad..e2a8569cb3 100644 --- a/src/boot/stub.c +++ b/src/boot/stub.c @@ -412,14 +412,7 @@ static void named_addon_done(NamedAddon *a) { iovec_done(&a->blob); } -static void named_addon_free_many(NamedAddon *a, size_t n) { - assert(a || n == 0); - - FOREACH_ARRAY(i, a, n) - named_addon_done(i); - - free(a); -} +static DEFINE_ARRAY_FREE_FUNC(named_addon_free_array, NamedAddon, named_addon_done); static void install_addon_devicetrees( struct devicetree_state *dt_state, @@ -1294,9 +1287,9 @@ static EFI_STATUS run(EFI_HANDLE image) { /* Now that we have the UKI sections loaded, also load global first and then local (per-UKI) * addons. The data is loaded at once, and then used later. */ - CLEANUP_ARRAY(dt_addons, n_dt_addons, named_addon_free_many); - CLEANUP_ARRAY(initrd_addons, n_initrd_addons, named_addon_free_many); - CLEANUP_ARRAY(ucode_addons, n_ucode_addons, named_addon_free_many); + CLEANUP_ARRAY(dt_addons, n_dt_addons, named_addon_free_array); + CLEANUP_ARRAY(initrd_addons, n_initrd_addons, named_addon_free_array); + CLEANUP_ARRAY(ucode_addons, n_ucode_addons, named_addon_free_array); load_all_addons(image, loaded_image, uname, &cmdline_addons, &dt_addons, &n_dt_addons, &initrd_addons, &n_initrd_addons, &ucode_addons, &n_ucode_addons); /* If we have any extra command line to add via PE addons, load them now and append, and measure the From 954243f2bc6c17832252c91c926bd1e37116cd46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 8 Apr 2026 22:34:34 +0200 Subject: [PATCH 07/13] libsystemd-network: use DEFINE_ARRAY_FREE_FUNC, rename cleanup func --- src/libsystemd-network/dns-resolver-internal.h | 2 +- src/libsystemd-network/network-internal.c | 2 +- src/libsystemd-network/sd-dhcp-lease.c | 6 +++--- src/libsystemd-network/sd-dhcp6-lease.c | 2 +- src/libsystemd-network/sd-dns-resolver.c | 9 +-------- 5 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/libsystemd-network/dns-resolver-internal.h b/src/libsystemd-network/dns-resolver-internal.h index c8221421d1..c9b3103cdb 100644 --- a/src/libsystemd-network/dns-resolver-internal.h +++ b/src/libsystemd-network/dns-resolver-internal.h @@ -34,4 +34,4 @@ int dns_resolvers_to_dot_strv(const sd_dns_resolver *resolvers, size_t n_resolve void sd_dns_resolver_done(sd_dns_resolver *res); -void dns_resolver_done_many(sd_dns_resolver *resolvers, size_t n); +void dns_resolver_free_array(sd_dns_resolver *array, size_t n); diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index c8aa6b63e0..c1eaa361af 100644 --- a/src/libsystemd-network/network-internal.c +++ b/src/libsystemd-network/network-internal.c @@ -169,7 +169,7 @@ int deserialize_dnr(sd_dns_resolver **ret, const char *string) { sd_dns_resolver *dnr = NULL; size_t n = 0; - CLEANUP_ARRAY(dnr, n, dns_resolver_done_many); + CLEANUP_ARRAY(dnr, n, dns_resolver_free_array); int priority = 0; for (;;) { diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index 0623bc0e4b..fa41d293da 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -434,7 +434,7 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) { for (sd_dhcp_lease_server_type_t i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++) free(lease->servers[i].addr); - dns_resolver_done_many(lease->dnr, lease->n_dnr); + dns_resolver_free_array(lease->dnr, lease->n_dnr); free(lease->static_routes); free(lease->classless_routes); free(lease->vendor_specific); @@ -642,7 +642,7 @@ static int lease_parse_dnr(const uint8_t *option, size_t len, sd_dns_resolver ** int r; sd_dns_resolver *res_list = NULL; size_t n_resolvers = 0; - CLEANUP_ARRAY(res_list, n_resolvers, dns_resolver_done_many); + CLEANUP_ARRAY(res_list, n_resolvers, dns_resolver_free_array); assert(option || len == 0); assert(dnr); @@ -747,7 +747,7 @@ static int lease_parse_dnr(const uint8_t *option, size_t len, sd_dns_resolver ** typesafe_qsort(res_list, n_resolvers, dns_resolver_prio_compare); - dns_resolver_done_many(*dnr, *n_dnr); + dns_resolver_free_array(*dnr, *n_dnr); *dnr = TAKE_PTR(res_list); *n_dnr = n_resolvers; diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 835f6c2d65..4e745bbc3b 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -1076,7 +1076,7 @@ static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) { dhcp6_ia_free(lease->ia_na); dhcp6_ia_free(lease->ia_pd); free(lease->dns); - dns_resolver_done_many(lease->dnr, lease->n_dnr); + dns_resolver_free_array(lease->dnr, lease->n_dnr); free(lease->fqdn); free(lease->captive_portal); strv_free(lease->domains); diff --git a/src/libsystemd-network/sd-dns-resolver.c b/src/libsystemd-network/sd-dns-resolver.c index c8c6618c72..94941170ae 100644 --- a/src/libsystemd-network/sd-dns-resolver.c +++ b/src/libsystemd-network/sd-dns-resolver.c @@ -26,14 +26,7 @@ sd_dns_resolver *sd_dns_resolver_unref(sd_dns_resolver *res) { return mfree(res); } -void dns_resolver_done_many(sd_dns_resolver resolvers[], size_t n) { - assert(resolvers || n == 0); - - FOREACH_ARRAY(res, resolvers, n) - sd_dns_resolver_done(res); - - free(resolvers); -} +DEFINE_ARRAY_FREE_FUNC(dns_resolver_free_array, sd_dns_resolver, sd_dns_resolver_done); int dns_resolver_prio_compare(const sd_dns_resolver *a, const sd_dns_resolver *b) { return CMP(ASSERT_PTR(a)->priority, ASSERT_PTR(b)->priority); From b8514cff52d20283fced0fc302484443fa4a0b56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 8 Apr 2026 22:40:21 +0200 Subject: [PATCH 08/13] libsystemd-network: use DEFINE_POINTER_ARRAY_FREE_FUNC, rename cleanup function --- src/libsystemd-network/sd-dns-resolver.c | 4 ++-- src/network/networkd-state-file.c | 6 +++--- src/network/networkd-wwan.c | 2 +- src/shared/socket-netlink.c | 11 ++--------- src/shared/socket-netlink.h | 4 ++-- 5 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/libsystemd-network/sd-dns-resolver.c b/src/libsystemd-network/sd-dns-resolver.c index 94941170ae..605397cf97 100644 --- a/src/libsystemd-network/sd-dns-resolver.c +++ b/src/libsystemd-network/sd-dns-resolver.c @@ -299,7 +299,7 @@ int dns_resolvers_to_dot_addrs(const sd_dns_resolver *resolvers, size_t n_resolv struct in_addr_full **addrs = NULL; size_t n = 0; - CLEANUP_ARRAY(addrs, n, in_addr_full_array_free); + CLEANUP_ARRAY(addrs, n, in_addr_full_free_array); FOREACH_ARRAY(res, resolvers, n_resolvers) { if (!FLAGS_SET(res->transports, SD_DNS_ALPN_DOT)) @@ -340,7 +340,7 @@ int dns_resolvers_to_dot_strv(const sd_dns_resolver *resolvers, size_t n_resolve struct in_addr_full **addrs = NULL; size_t n = 0; - CLEANUP_ARRAY(addrs, n, in_addr_full_array_free); + CLEANUP_ARRAY(addrs, n, in_addr_full_free_array); r = dns_resolvers_to_dot_addrs(resolvers, n_resolvers, &addrs, &n); if (r < 0) diff --git a/src/network/networkd-state-file.c b/src/network/networkd-state-file.c index b3b85f18f5..7943314b06 100644 --- a/src/network/networkd-state-file.c +++ b/src/network/networkd-state-file.c @@ -136,7 +136,7 @@ static int link_put_dns(Link *link, OrderedSet **s) { if (r >= 0) { struct in_addr_full **dot_servers; size_t n = 0; - CLEANUP_ARRAY(dot_servers, n, in_addr_full_array_free); + CLEANUP_ARRAY(dot_servers, n, in_addr_full_free_array); r = dns_resolvers_to_dot_addrs(resolvers, r, &dot_servers, &n); if (r < 0) @@ -165,7 +165,7 @@ static int link_put_dns(Link *link, OrderedSet **s) { if (r >= 0) { struct in_addr_full **dot_servers; size_t n = 0; - CLEANUP_ARRAY(dot_servers, n, in_addr_full_array_free); + CLEANUP_ARRAY(dot_servers, n, in_addr_full_free_array); r = dns_resolvers_to_dot_addrs(resolvers, r, &dot_servers, &n); if (r < 0) @@ -193,7 +193,7 @@ static int link_put_dns(Link *link, OrderedSet **s) { SET_FOREACH(a, link->ndisc_dnr) { struct in_addr_full **dot_servers = NULL; size_t n = 0; - CLEANUP_ARRAY(dot_servers, n, in_addr_full_array_free); + CLEANUP_ARRAY(dot_servers, n, in_addr_full_free_array); r = dns_resolvers_to_dot_addrs(&a->resolver, 1, &dot_servers, &n); if (r < 0) diff --git a/src/network/networkd-wwan.c b/src/network/networkd-wwan.c index d5094ce55c..ddc5ca38b4 100644 --- a/src/network/networkd-wwan.c +++ b/src/network/networkd-wwan.c @@ -37,7 +37,7 @@ Bearer* bearer_free(Bearer *b) { free(b->name); free(b->apn); - in_addr_full_array_free(b->dns, b->n_dns); + in_addr_full_free_array(b->dns, b->n_dns); return mfree(b); } diff --git a/src/shared/socket-netlink.c b/src/shared/socket-netlink.c index ca9c259b26..1d369353b9 100644 --- a/src/shared/socket-netlink.c +++ b/src/shared/socket-netlink.c @@ -350,7 +350,7 @@ int in_addr_port_ifindex_name_from_string_auto( return r; } -struct in_addr_full *in_addr_full_free(struct in_addr_full *a) { +struct in_addr_full* in_addr_full_free(struct in_addr_full *a) { if (!a) return NULL; @@ -359,14 +359,7 @@ struct in_addr_full *in_addr_full_free(struct in_addr_full *a) { return mfree(a); } -void in_addr_full_array_free(struct in_addr_full *addrs[], size_t n) { - assert(addrs || n == 0); - - FOREACH_ARRAY(a, addrs, n) - in_addr_full_freep(a); - - free(addrs); -} +DEFINE_POINTER_ARRAY_FREE_FUNC(struct in_addr_full*, in_addr_full_free); int in_addr_full_new( int family, diff --git a/src/shared/socket-netlink.h b/src/shared/socket-netlink.h index f371967c69..be15398ba5 100644 --- a/src/shared/socket-netlink.h +++ b/src/shared/socket-netlink.h @@ -36,9 +36,9 @@ struct in_addr_full { char *cached_server_string; /* Should not be handled directly, but through in_addr_full_to_string(). */ }; -struct in_addr_full *in_addr_full_free(struct in_addr_full *a); +struct in_addr_full* in_addr_full_free(struct in_addr_full *a); DEFINE_TRIVIAL_CLEANUP_FUNC(struct in_addr_full*, in_addr_full_free); -void in_addr_full_array_free(struct in_addr_full *addrs[], size_t n); +void in_addr_full_free_array(struct in_addr_full **array, size_t n); int in_addr_full_new(int family, const union in_addr_union *a, uint16_t port, int ifindex, const char *server_name, struct in_addr_full **ret); int in_addr_full_new_from_string(const char *s, struct in_addr_full **ret); const char* in_addr_full_to_string(struct in_addr_full *a); From 5ceaab3d4fb527388da68448a7bda1e1101120b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 8 Apr 2026 22:49:41 +0200 Subject: [PATCH 09/13] nsresourced: use DEFINE_ARRAY_FREE_FUNC, make func static and rename --- src/nsresourced/userns-registry.c | 19 +++++-------------- src/nsresourced/userns-registry.h | 1 - 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/nsresourced/userns-registry.c b/src/nsresourced/userns-registry.c index 371a35086f..50efcd1153 100644 --- a/src/nsresourced/userns-registry.c +++ b/src/nsresourced/userns-registry.c @@ -65,14 +65,7 @@ void delegated_userns_info_done(DelegatedUserNamespaceInfo *info) { info->n_ancestor_userns = 0; } -void delegated_userns_info_done_many(DelegatedUserNamespaceInfo infos[], size_t n) { - assert(infos || n == 0); - - FOREACH_ARRAY(info, infos, n) - delegated_userns_info_done(info); - - free(infos); -} +static DEFINE_ARRAY_FREE_FUNC(delegated_userns_info_free_array, DelegatedUserNamespaceInfo, delegated_userns_info_done); UserNamespaceInfo* userns_info_new(void) { UserNamespaceInfo *info = new(UserNamespaceInfo, 1); @@ -97,7 +90,7 @@ UserNamespaceInfo *userns_info_free(UserNamespaceInfo *userns) { free(userns->cgroups); free(userns->name); - delegated_userns_info_done_many(userns->delegates, userns->n_delegates); + delegated_userns_info_free_array(userns->delegates, userns->n_delegates); strv_free(userns->netifs); @@ -154,12 +147,10 @@ static int dispatch_delegates_array(const char *name, sd_json_variant *variant, size_t n = 0; int r; - CLEANUP_ARRAY(delegates, n, delegated_userns_info_done_many); + CLEANUP_ARRAY(delegates, n, delegated_userns_info_free_array); if (sd_json_variant_is_null(variant)) { - delegated_userns_info_done_many(info->delegates, info->n_delegates); - info->delegates = NULL; - info->n_delegates = 0; + CLEANUP_ARRAY(info->delegates, info->n_delegates, delegated_userns_info_free_array); return 0; } @@ -199,7 +190,7 @@ static int dispatch_delegates_array(const char *name, sd_json_variant *variant, n++; } - delegated_userns_info_done_many(info->delegates, info->n_delegates); + delegated_userns_info_free_array(info->delegates, info->n_delegates); info->delegates = TAKE_PTR(delegates); info->n_delegates = n; diff --git a/src/nsresourced/userns-registry.h b/src/nsresourced/userns-registry.h index f08b238861..77ff2d6d20 100644 --- a/src/nsresourced/userns-registry.h +++ b/src/nsresourced/userns-registry.h @@ -24,7 +24,6 @@ typedef struct DelegatedUserNamespaceInfo { } void delegated_userns_info_done(DelegatedUserNamespaceInfo *info); -void delegated_userns_info_done_many(DelegatedUserNamespaceInfo infos[], size_t n); typedef struct UserNamespaceInfo { uid_t owner; From 90c61571002926bb68a552da24f547d7e6b68c5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 8 Apr 2026 22:56:07 +0200 Subject: [PATCH 10/13] sd-journal: use NormalCasing for struct --- src/libsystemd/sd-journal/journal-vacuum.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libsystemd/sd-journal/journal-vacuum.c b/src/libsystemd/sd-journal/journal-vacuum.c index fade159820..e88ba0e539 100644 --- a/src/libsystemd/sd-journal/journal-vacuum.c +++ b/src/libsystemd/sd-journal/journal-vacuum.c @@ -21,7 +21,7 @@ #include "time-util.h" #include "xattr-util.h" -typedef struct vacuum_info { +typedef struct VacuumInfo { uint64_t usage; char *filename; @@ -30,9 +30,9 @@ typedef struct vacuum_info { sd_id128_t seqnum_id; uint64_t seqnum; bool have_seqnum; -} vacuum_info; +} VacuumInfo; -static int vacuum_info_compare(const vacuum_info *a, const vacuum_info *b) { +static int vacuum_info_compare(const VacuumInfo *a, const VacuumInfo *b) { int r; if (a->have_seqnum && b->have_seqnum && @@ -49,7 +49,7 @@ static int vacuum_info_compare(const vacuum_info *a, const vacuum_info *b) { return strcmp(a->filename, b->filename); } -static void vacuum_info_array_free(vacuum_info *list, size_t n) { +static void vacuum_info_array_free(VacuumInfo *list, size_t n) { if (!list) return; @@ -137,7 +137,7 @@ int journal_directory_vacuum( uint64_t sum = 0, freed = 0, n_active_files = 0; size_t n_list = 0, i; _cleanup_closedir_ DIR *d = NULL; - vacuum_info *list = NULL; + VacuumInfo *list = NULL; usec_t retention_limit = 0; int r; @@ -280,7 +280,7 @@ int journal_directory_vacuum( if (!GREEDY_REALLOC(list, n_list + 1)) return -ENOMEM; - list[n_list++] = (vacuum_info) { + list[n_list++] = (VacuumInfo) { .filename = TAKE_PTR(p), .usage = size, .seqnum = seqnum, From 7024c0c38c8ac9418e264db94b69264016ab3dc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 8 Apr 2026 23:03:47 +0200 Subject: [PATCH 11/13] shared/tar-util: use DEFINE_ARRAY_FREE_FUNC, rename funcs --- src/shared/tar-util.c | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/src/shared/tar-util.c b/src/shared/tar-util.c index 33395e96bc..823fb9ac2b 100644 --- a/src/shared/tar-util.c +++ b/src/shared/tar-util.c @@ -69,14 +69,7 @@ static void xattr_done(XAttr *xa) { iovec_done(&xa->data); } -static void xattr_done_many(XAttr *xa, size_t n) { - assert(xa || n == 0); - - FOREACH_ARRAY(i, xa, n) - xattr_done(i); - - free(xa); -} +static DEFINE_ARRAY_FREE_FUNC(xattr_free_array, XAttr, xattr_done); static void open_inode_done(OpenInode *of) { assert(of); @@ -87,7 +80,7 @@ static void open_inode_done(OpenInode *of) { of->fd = safe_close(of->fd); of->path = mfree(of->path); } - xattr_done_many(of->xattr, of->n_xattr); + xattr_free_array(of->xattr, of->n_xattr); #if HAVE_ACL if (of->acl_access) sym_acl_free(of->acl_access); @@ -96,14 +89,7 @@ static void open_inode_done(OpenInode *of) { #endif } -static void open_inode_done_many(OpenInode *array, size_t n) { - assert(array || n == 0); - - FOREACH_ARRAY(i, array, n) - open_inode_done(i); - - free(array); -} +static DEFINE_ARRAY_FREE_FUNC(open_inode_free_array, OpenInode, open_inode_done); static int open_inode_apply_acl(OpenInode *of) { int r = 0; @@ -792,7 +778,7 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) { return log_oom(); size_t n_open_inodes = 0; - CLEANUP_ARRAY(open_inodes, n_open_inodes, open_inode_done_many); + CLEANUP_ARRAY(open_inodes, n_open_inodes, open_inode_free_array); /* Fill in the root inode. (Note: we leave the .path field as NULL to mark it as root inode.) */ open_inodes[0] = (OpenInode) { @@ -913,7 +899,7 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) { acl_t acl_access = NULL, acl_default = NULL; XAttr *xa = NULL; size_t n_xa = 0; - CLEANUP_ARRAY(xa, n_xa, xattr_done_many); + CLEANUP_ARRAY(xa, n_xa, xattr_free_array); if (isempty(rest)) { /* This is the final node in the path, create it */ From 2a23e43f707b308c1d74951eddb02f4ad4fca6da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 8 Apr 2026 23:04:19 +0200 Subject: [PATCH 12/13] sysupdate: use DEFINE_POINTER_ARRAY_FREE_FUNC, rename func --- src/sysupdate/sysupdate.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/sysupdate/sysupdate.c b/src/sysupdate/sysupdate.c index 77cbb3a238..9c469fbe85 100644 --- a/src/sysupdate/sysupdate.c +++ b/src/sysupdate/sysupdate.c @@ -111,11 +111,7 @@ static Context* context_new(void) { return new0(Context, 1); } -static void free_transfers(Transfer **array, size_t n) { - FOREACH_ARRAY(t, array, n) - transfer_free(*t); - free(array); -} +static DEFINE_POINTER_ARRAY_FREE_FUNC(Transfer*, transfer_free); static int read_definitions( Context *c, @@ -129,8 +125,8 @@ static int read_definitions( int r; CLEANUP_ARRAY(files, n_files, conf_file_free_array); - CLEANUP_ARRAY(transfers, n_transfers, free_transfers); - CLEANUP_ARRAY(disabled, n_disabled, free_transfers); + CLEANUP_ARRAY(transfers, n_transfers, transfer_free_array); + CLEANUP_ARRAY(disabled, n_disabled, transfer_free_array); assert(c); assert(dirs); From 79f90f4ae0d09a2748870646cb924cb5c74704e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 8 Apr 2026 23:04:47 +0200 Subject: [PATCH 13/13] various: use DEFINE_ARRAY_FREE_FUNC --- src/core/exec-invoke.c | 11 ++++------- src/libsystemd/sd-journal/journal-vacuum.c | 13 +++++-------- src/portable/portable.c | 17 ++++++----------- src/portable/portable.h | 2 +- src/shared/install.c | 14 ++++++-------- src/shared/install.h | 2 +- src/shared/mount-util.c | 9 +-------- src/shared/mount-util.h | 2 +- 8 files changed, 25 insertions(+), 45 deletions(-) diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c index b91a964cdd..fef2c3a0f2 100644 --- a/src/core/exec-invoke.c +++ b/src/core/exec-invoke.c @@ -1086,15 +1086,12 @@ static int enforce_user( #if HAVE_PAM -static void pam_response_free_array(struct pam_response *responses, size_t n_responses) { - assert(responses || n_responses == 0); - - FOREACH_ARRAY(resp, responses, n_responses) - erase_and_free(resp->resp); - - free(responses); +static void pam_response_done(struct pam_response *response) { + erase_and_free(ASSERT_PTR(response)->resp); } +static DEFINE_ARRAY_FREE_FUNC(pam_response_free_array, struct pam_response, pam_response_done); + typedef struct AskPasswordConvData { const ExecContext *context; const ExecParameters *params; diff --git a/src/libsystemd/sd-journal/journal-vacuum.c b/src/libsystemd/sd-journal/journal-vacuum.c index e88ba0e539..a49c073b3e 100644 --- a/src/libsystemd/sd-journal/journal-vacuum.c +++ b/src/libsystemd/sd-journal/journal-vacuum.c @@ -49,16 +49,13 @@ static int vacuum_info_compare(const VacuumInfo *a, const VacuumInfo *b) { return strcmp(a->filename, b->filename); } -static void vacuum_info_array_free(VacuumInfo *list, size_t n) { - if (!list) - return; - - FOREACH_ARRAY(i, list, n) - free(i->filename); - - free(list); +static void vacuum_info_done(VacuumInfo *info) { + assert(info); + info->filename = mfree(info->filename); } +static DEFINE_ARRAY_FREE_FUNC(vacuum_info_array_free, VacuumInfo, vacuum_info_done); + static void patch_realtime( int fd, const char *fn, diff --git a/src/portable/portable.c b/src/portable/portable.c index bae23b1a51..12a14cbf42 100644 --- a/src/portable/portable.c +++ b/src/portable/portable.c @@ -1271,19 +1271,14 @@ static int portable_changes_add_with_prefix( return portable_changes_add(changes, n_changes, type_or_errno, path, source); } -void portable_changes_free(PortableChange *changes, size_t n_changes) { - size_t i; - - assert(changes || n_changes == 0); - - for (i = 0; i < n_changes; i++) { - free(changes[i].path); - free(changes[i].source); - } - - free(changes); +static void portable_change_done(PortableChange *change) { + assert(change); + change->path = mfree(change->path); + change->source = mfree(change->source); } +DEFINE_ARRAY_FREE_FUNC(portable_changes_free, PortableChange, portable_change_done); + static const char *root_setting_from_image(ImageType type) { switch (type) { case IMAGE_DIRECTORY: diff --git a/src/portable/portable.h b/src/portable/portable.h index 5065babaab..af5ef3ba65 100644 --- a/src/portable/portable.h +++ b/src/portable/portable.h @@ -112,7 +112,7 @@ int portable_get_state( int portable_get_profiles(RuntimeScope scope, char ***ret); -void portable_changes_free(PortableChange *changes, size_t n_changes); +void portable_changes_free(PortableChange *array, size_t n); DECLARE_STRING_TABLE_LOOKUP(portable_change_type, int); diff --git a/src/shared/install.c b/src/shared/install.c index 2398224176..149faed711 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -319,17 +319,15 @@ InstallChangeType install_changes_add( return type; } -void install_changes_free(InstallChange *changes, size_t n_changes) { - assert(changes || n_changes == 0); +static void install_change_done(InstallChange *change) { + assert(change); - FOREACH_ARRAY(i, changes, n_changes) { - free(i->path); - free(i->source); - } - - free(changes); + change->path = mfree(change->path); + change->source = mfree(change->source); } +DEFINE_ARRAY_FREE_FUNC(install_changes_free, InstallChange, install_change_done); + static void install_change_dump_success(const InstallChange *change) { assert(change); assert(change->path); diff --git a/src/shared/install.h b/src/shared/install.h index ad5803e321..f184b7191c 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -202,7 +202,7 @@ static inline int unit_file_exists(RuntimeScope scope, const LookupPaths *lp, co int unit_file_get_list(RuntimeScope scope, const char *root_dir, char * const *states, char * const *patterns, Hashmap **ret); InstallChangeType install_changes_add(InstallChange **changes, size_t *n_changes, InstallChangeType type, const char *path, const char *source); -void install_changes_free(InstallChange *changes, size_t n_changes); +void install_changes_free(InstallChange *array, size_t n); int install_change_dump_error(const InstallChange *change, char **ret_errmsg, const char **ret_bus_error); int install_changes_dump( diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c index f7bba5d080..45ffd9113b 100644 --- a/src/shared/mount-util.c +++ b/src/shared/mount-util.c @@ -1748,14 +1748,7 @@ static void sub_mount_clear(SubMount *s) { s->mount_fd = safe_close(s->mount_fd); } -void sub_mount_array_free(SubMount *s, size_t n) { - assert(s || n == 0); - - for (size_t i = 0; i < n; i++) - sub_mount_clear(s + i); - - free(s); -} +DEFINE_ARRAY_FREE_FUNC(sub_mount_array_free, SubMount, sub_mount_clear); #if HAVE_LIBMOUNT static int sub_mount_compare(const SubMount *a, const SubMount *b) { diff --git a/src/shared/mount-util.h b/src/shared/mount-util.h index 768aacf09c..b178bf7bac 100644 --- a/src/shared/mount-util.h +++ b/src/shared/mount-util.h @@ -8,7 +8,7 @@ typedef struct SubMount { int mount_fd; } SubMount; -void sub_mount_array_free(SubMount *s, size_t n); +void sub_mount_array_free(SubMount *array, size_t n); int get_sub_mounts(const char *prefix, SubMount **ret_mounts, size_t *ret_n_mounts); int bind_mount_submounts(