]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
fundamental/cleanup: add CLEANUP_ELEMENTS() and DEFINE_POINTER_ARRAY_CLEAR_FUNC()
authorDaan De Meyer <daan@amutable.com>
Mon, 27 Apr 2026 18:03:51 +0000 (18:03 +0000)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 30 Apr 2026 07:15:34 +0000 (09:15 +0200)
DEFINE_POINTER_ARRAY_CLEAR_FUNC() generates a helper of the form
helper_array_clear(T *array, size_t n) that drops each element but does
not free the array itself, parallel to DEFINE_POINTER_ARRAY_FREE_FUNC()
for cases where the array has automatic storage duration.

CLEANUP_ELEMENTS() pairs with these helpers to provide a _cleanup_-like
attribute for fixed-size arrays: the bound is taken from ELEMENTSOF(),
and the helper is invoked across the elements at scope exit. Compared to
CLEANUP_ARRAY(), the storage is neither freed nor zeroed.

Migrate various logic across the tree over to the new macros.

sd-device: use DEFINE_POINTER_ARRAY_CLEAR_FUNC() for sd_device_unref_array_clear()

Replace the local device_unref_many() helper with the macro-generated
equivalent.

format-table: switch help-table arrays to CLEANUP_ELEMENTS()

Generate table_unref_array_clear() via DEFINE_POINTER_ARRAY_CLEAR_FUNC()
and convert the help-table arrays in bootctl, cryptenroll, nspawn,
repart and vmspawn to CLEANUP_ELEMENTS(). The arrays no longer need a
trailing NULL slot, so the size matches ELEMENTSOF() of the groups
array.

firewall-util: switch netlink message arrays to CLEANUP_ELEMENTS()

Generate sd_netlink_message_unref_array_clear() via
DEFINE_POINTER_ARRAY_CLEAR_FUNC() in place of the NULL-terminated
sd_netlink_message_unref_many(), and convert the two stack arrays of
sd_netlink_message pointers to CLEANUP_ELEMENTS().

src/bootctl/bootctl.c
src/cryptenroll/cryptenroll.c
src/fundamental/cleanup-fundamental.h
src/libsystemd/sd-device/device-enumerator.c
src/nspawn/nspawn.c
src/repart/repart.c
src/run/run.c
src/shared/firewall-util.c
src/shared/format-table.h
src/vmspawn/vmspawn.c

index 04213dc8e17aa47f33f69934969246fe959101d1..967c21458d9eed733231e72736955465a928e38f 100644 (file)
@@ -298,8 +298,10 @@ static int help(void) {
                 "Options",
         };
 
-        _cleanup_(table_unref_many) Table *verb_tables[ELEMENTSOF(verb_groups) + 1] = {};
-        _cleanup_(table_unref_many) Table *option_tables[ELEMENTSOF(option_groups) + 1] = {};
+        Table *verb_tables[ELEMENTSOF(verb_groups)] = {};
+        CLEANUP_ELEMENTS(verb_tables, table_unref_array_clear);
+        Table *option_tables[ELEMENTSOF(option_groups)] = {};
+        CLEANUP_ELEMENTS(option_tables, table_unref_array_clear);
 
         for (size_t i = 0; i < ELEMENTSOF(verb_groups); i++) {
                 r = verbs_get_help_table_group(verb_groups[i], &verb_tables[i]);
index f7e7ff121804a478c5b2af0c4b518d17020a10d2..6561d861078431fda58f6144219263868c59dd9b 100644 (file)
@@ -241,7 +241,8 @@ static int help(void) {
                 "TPM2 Enrollment",
         };
 
-        _cleanup_(table_unref_many) Table *tables[ELEMENTSOF(groups) + 1] = {};
+        Table *tables[ELEMENTSOF(groups)] = {};
+        CLEANUP_ELEMENTS(tables, table_unref_array_clear);
 
         for (size_t i = 0; i < ELEMENTSOF(groups); i++) {
                 r = option_parser_get_help_table_group(groups[i], &tables[i]);
index 9094cff2331e00730b7b6eb019c887671e76350e..8d499e5c3498bb22074a26e758ddea666747f246 100644 (file)
                 free(array);                                    \
         }
 
+/* Like DEFINE_POINTER_ARRAY_FREE_FUNC() but does not deallocate the array itself, useful for
+ * arrays with automatic storage duration (e.g. on the stack). */
+#define DEFINE_POINTER_ARRAY_CLEAR_FUNC(type, helper)           \
+        void helper ## _array_clear(type *array, size_t n) {    \
+                assert(array || n == 0);                        \
+                FOREACH_ARRAY(item, array, n)                   \
+                        *item = helper(*item);                  \
+        }
+
 /* 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)              \
@@ -108,3 +117,33 @@ static inline void array_cleanup(const ArrayCleanup *c) {
                                 _f;                                     \
                          }),                                            \
         }
+
+/* An automatic _cleanup_-like logic for fixed-size arrays where the bound is known via
+ * ELEMENTSOF(). Unlike CLEANUP_ARRAY() this neither frees the storage nor zeroes it: it just
+ * invokes func() across the elements when leaving scope. */
+typedef struct ElementsCleanup {
+        void *array;
+        size_t n;
+        free_array_func_t pfunc;
+} ElementsCleanup;
+
+static inline void elements_cleanup(const ElementsCleanup *c) {
+        assert(c);
+
+        if (c->n == 0)
+                return;
+
+        assert(c->array);
+        assert(c->pfunc);
+        c->pfunc(c->array, c->n);
+}
+
+#define CLEANUP_ELEMENTS(_array, _func)                                   \
+        _cleanup_(elements_cleanup) _unused_ const ElementsCleanup CONCATENATE(_cleanup_elements_, UNIQ) = { \
+                .array = (_array),                                       \
+                .n = ELEMENTSOF(_array),                                 \
+                .pfunc = (free_array_func_t) ({                          \
+                                void (*_f)(typeof((_array)[0]) *a, size_t b) = _func; \
+                                _f;                                      \
+                         }),                                             \
+        }
index b3fe85a9761675649a69246e2c4cf371156910d8..d1a48defe906c844e2b8df062413ba008fea169a 100644 (file)
@@ -82,18 +82,13 @@ _public_ int sd_device_enumerator_new(sd_device_enumerator **ret) {
         return 0;
 }
 
-static void device_unref_many(sd_device **devices, size_t n) {
-        assert(devices || n == 0);
-
-        for (size_t i = 0; i < n; i++)
-                sd_device_unref(devices[i]);
-}
+static DEFINE_POINTER_ARRAY_CLEAR_FUNC(sd_device*, sd_device_unref);
 
 static void device_enumerator_unref_devices(sd_device_enumerator *enumerator) {
         assert(enumerator);
 
         hashmap_clear(enumerator->devices_by_syspath);
-        device_unref_many(enumerator->devices, enumerator->n_devices);
+        sd_device_unref_array_clear(enumerator->devices, enumerator->n_devices);
         enumerator->devices = mfree(enumerator->devices);
         enumerator->n_devices = 0;
 }
@@ -461,7 +456,7 @@ static int enumerator_sort_devices(sd_device_enumerator *enumerator) {
 
         typesafe_qsort(devices + n_sorted, n - n_sorted, device_compare);
 
-        device_unref_many(enumerator->devices, enumerator->n_devices);
+        sd_device_unref_array_clear(enumerator->devices, enumerator->n_devices);
 
         enumerator->n_devices = n;
         free_and_replace(enumerator->devices, devices);
@@ -470,7 +465,7 @@ static int enumerator_sort_devices(sd_device_enumerator *enumerator) {
         return 0;
 
 failed:
-        device_unref_many(devices, n);
+        sd_device_unref_array_clear(devices, n);
         free(devices);
         return r;
 }
index 6c9c1050c692140e9ce782b76ef7c1477a9d183d..f96a6b08b981cba568c088727da8a97c9c7ae66a 100644 (file)
@@ -405,7 +405,8 @@ static int help(void) {
                 "Other",
         };
 
-        _cleanup_(table_unref_many) Table* tables[ELEMENTSOF(groups) + 1] = {};
+        Table* tables[ELEMENTSOF(groups)] = {};
+        CLEANUP_ELEMENTS(tables, table_unref_array_clear);
 
         for (size_t i = 0; i < ELEMENTSOF(groups); i++) {
                 r = option_parser_get_help_table_group(groups[i], &tables[i]);
index b82827f869ee30062c8cbecdce36ab13f9cb1dac..26588c6242b4d79420eb8f525f54cbae0cce3141 100644 (file)
@@ -9648,7 +9648,8 @@ static int help(void) {
                 "El Torito boot catalog",
         };
 
-        _cleanup_(table_unref_many) Table *option_tables[ELEMENTSOF(option_groups) + 1] = {};
+        Table *option_tables[ELEMENTSOF(option_groups)] = {};
+        CLEANUP_ELEMENTS(option_tables, table_unref_array_clear);
 
         for (size_t i = 0; i < ELEMENTSOF(option_groups); i++) {
                 r = option_parser_get_help_table_group(option_groups[i], &option_tables[i]);
index 5827d91e1f9e4a19437140e3265894aa9fedd43d..9d1042e845a331779a2a7abf4b54eb2147a6b9bb 100644 (file)
@@ -151,7 +151,8 @@ static int help(void) {
                 "Timer options",
         };
 
-        _cleanup_(table_unref_many) Table *tables[ELEMENTSOF(groups) + 1] = {};
+        Table *tables[ELEMENTSOF(groups)] = {};
+        CLEANUP_ELEMENTS(tables, table_unref_array_clear);
 
         for (size_t i = 0; i < ELEMENTSOF(groups); i++) {
                 r = option_parser_get_help_table_full("systemd-run", groups[i], &tables[i]);
index 651870e369889d8ebd142025537d0ab0515d74e1..4693972ff2752ea134a86804a4b7cfc382aba02e 100644 (file)
@@ -50,7 +50,7 @@ static const char* dnat_map_name(void) {
         return cached;
 }
 
-static DEFINE_ARRAY_DONE_FUNC(sd_netlink_message*, sd_netlink_message_unref);
+static DEFINE_POINTER_ARRAY_CLEAR_FUNC(sd_netlink_message*, sd_netlink_message_unref);
 
 static int nfnl_open_expr_container(sd_netlink_message *m, const char *name) {
         int r;
@@ -724,7 +724,8 @@ 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) {
-        _cleanup_(sd_netlink_message_unref_many) sd_netlink_message *messages[10] = {};
+        sd_netlink_message *messages[10] = {};
+        CLEANUP_ELEMENTS(messages, sd_netlink_message_unref_array_clear);
         size_t msgcnt = 0, ip_type_size;
         uint32_t set_id = 0;
         int ip_type, r;
@@ -1045,7 +1046,8 @@ static int fw_nftables_add_local_dnat_internal(
                 uint16_t remote_port,
                 const union in_addr_union *previous_remote) {
 
-        _cleanup_(sd_netlink_message_unref_many) sd_netlink_message *messages[3] = {};
+        sd_netlink_message *messages[3] = {};
+        CLEANUP_ELEMENTS(messages, sd_netlink_message_unref_array_clear);
         uint32_t data[5], key[2], dlen;
         size_t msgcnt = 0;
         int r;
index 5b98d4901752420abc2e28b961721d5ab20dd461..ba4d33cfc67190379258175a1267e5988494c79f 100644 (file)
@@ -101,7 +101,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);
+static inline DEFINE_POINTER_ARRAY_CLEAR_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) {
index 14df0fc989f6582e7752ff4d1426ce5ec392f7f9..8e4cbf3e80611f671a3148d88294f4b09b657470 100644 (file)
@@ -235,7 +235,8 @@ static int help(void) {
                 "Credentials",
         };
 
-        _cleanup_(table_unref_many) Table* tables[ELEMENTSOF(groups) + 1] = {};
+        Table* tables[ELEMENTSOF(groups)] = {};
+        CLEANUP_ELEMENTS(tables, table_unref_array_clear);
 
         for (size_t i = 0; i < ELEMENTSOF(groups); i++) {
                 r = option_parser_get_help_table_group(groups[i], &tables[i]);