diff --git a/src/basic/path-lookup.c b/src/basic/path-lookup.c index 4e3d59fc56..068054512b 100644 --- a/src/basic/path-lookup.c +++ b/src/basic/path-lookup.c @@ -167,19 +167,13 @@ static char** user_dirs( return NULL; /* Now merge everything we found. */ - if (strv_extend(&res, persistent_control) < 0) - return NULL; - - if (strv_extend(&res, runtime_control) < 0) - return NULL; - - if (strv_extend(&res, transient) < 0) - return NULL; - - if (strv_extend(&res, generator_early) < 0) - return NULL; - - if (strv_extend(&res, persistent_config) < 0) + if (strv_extend_many( + &res, + persistent_control, + runtime_control, + transient, + generator_early, + persistent_config) < 0) return NULL; if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0) @@ -192,16 +186,12 @@ static char** user_dirs( if (strv_extend_strv(&res, (char**) user_config_unit_paths, false) < 0) return NULL; - if (strv_extend(&res, runtime_config) < 0) - return NULL; - - if (strv_extend(&res, global_runtime_config) < 0) - return NULL; - - if (strv_extend(&res, generator) < 0) - return NULL; - - if (strv_extend(&res, data_home) < 0) + if (strv_extend_many( + &res, + runtime_config, + global_runtime_config, + generator, + data_home) < 0) return NULL; if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0) diff --git a/src/basic/strv.c b/src/basic/strv.c index fa17631aa0..e32653818b 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -505,29 +505,31 @@ int strv_insert(char ***l, size_t position, char *value) { char **c; size_t n, m; + assert(l); + if (!value) return 0; n = strv_length(*l); position = MIN(position, n); - /* increase and check for overflow */ - m = n + 2; - if (m < n) + /* check for overflow and increase*/ + if (n > SIZE_MAX - 2) return -ENOMEM; + m = n + 2; - c = new(char*, m); + c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m), sizeof(char*)); if (!c) return -ENOMEM; - for (size_t i = 0; i < position; i++) - c[i] = (*l)[i]; - c[position] = value; - for (size_t i = position; i < n; i++) - c[i+1] = (*l)[i]; - c[n+1] = NULL; + if (n > position) + memmove(c + position + 1, c + position, (n - position) * sizeof(char*)); - return free_and_replace(*l, c); + c[position] = value; + c[n + 1] = NULL; + + *l = c; + return 0; } int strv_consume_with_size(char ***l, size_t *n, char *value) { @@ -588,39 +590,63 @@ int strv_extend_with_size(char ***l, size_t *n, const char *value) { return strv_consume_with_size(l, n, v); } -int strv_extend_front(char ***l, const char *value) { +int strv_extend_many_internal(char ***l, const char *value, ...) { + va_list ap; size_t n, m; - char *v, **c; + int r; assert(l); - /* Like strv_extend(), but prepends rather than appends the new entry */ + m = n = strv_length(*l); - if (!value) - return 0; + r = 0; + va_start(ap, value); + for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) { + if (!s) + continue; - n = strv_length(*l); + if (m > SIZE_MAX-1) { /* overflow */ + r = -ENOMEM; + break; + } + m++; + } + va_end(ap); - /* Increase and overflow check. */ - m = n + 2; - if (m < n) + if (r < 0) + return r; + if (m > SIZE_MAX-1) return -ENOMEM; - v = strdup(value); - if (!v) + char **c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m+1), sizeof(char*)); + if (!c) return -ENOMEM; + *l = c; - c = reallocarray(*l, m, sizeof(char*)); - if (!c) { - free(v); - return -ENOMEM; + r = 0; + size_t i = n; + va_start(ap, value); + for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) { + if (!s) + continue; + + c[i] = strdup(s); + if (!c[i]) { + r = -ENOMEM; + break; + } + i++; + } + va_end(ap); + + if (r < 0) { + /* rollback on error */ + for (size_t j = n; j < i; j++) + c[j] = mfree(c[j]); + return r; } - memmove(c+1, c, n * sizeof(char*)); - c[0] = v; - c[n+1] = NULL; - - *l = c; + c[i] = NULL; return 0; } diff --git a/src/basic/strv.h b/src/basic/strv.h index 7361faed84..91337b9287 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -55,8 +55,10 @@ static inline int strv_extend(char ***l, const char *value) { return strv_extend_with_size(l, NULL, value); } +int strv_extend_many_internal(char ***l, const char *value, ...); +#define strv_extend_many(l, ...) strv_extend_many_internal(l, __VA_ARGS__, POINTER_MAX) + int strv_extendf(char ***l, const char *format, ...) _printf_(2,3); -int strv_extend_front(char ***l, const char *value); int strv_push_with_size(char ***l, size_t *n, char *value); static inline int strv_push(char ***l, char *value) { diff --git a/src/basic/time-util.c b/src/basic/time-util.c index f9014dc560..02123dc591 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -1517,7 +1517,7 @@ int get_timezones(char ***ret) { /* Always include UTC */ r = strv_extend(&zones, "UTC"); if (r < 0) - return -ENOMEM; + return r; strv_sort(zones); strv_uniq(zones); diff --git a/src/boot/measure.c b/src/boot/measure.c index 262ef89f1a..dea112add0 100644 --- a/src/boot/measure.c +++ b/src/boot/measure.c @@ -300,12 +300,11 @@ static int parse_argv(int argc, char *argv[]) { if (strv_isempty(arg_phase)) { /* If no phases are specifically selected, pick everything from the beginning of the initrd * to the beginning of shutdown. */ - if (strv_extend_strv(&arg_phase, - STRV_MAKE("enter-initrd", - "enter-initrd:leave-initrd", - "enter-initrd:leave-initrd:sysinit", - "enter-initrd:leave-initrd:sysinit:ready"), - /* filter_duplicates= */ false) < 0) + if (strv_extend_many(&arg_phase, + "enter-initrd", + "enter-initrd:leave-initrd", + "enter-initrd:leave-initrd:sysinit", + "enter-initrd:leave-initrd:sysinit:ready") < 0) return log_oom(); } else { strv_sort(arg_phase); diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 1830d69784..602fec1314 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -1949,7 +1949,7 @@ int bus_exec_context_set_transient_property( r = strv_extend_strv(&c->supplementary_groups, l, true); if (r < 0) - return -ENOMEM; + return r; joined = strv_join(c->supplementary_groups, " "); if (!joined) @@ -3174,7 +3174,7 @@ int bus_exec_context_set_transient_property( r = strv_extend_strv(dirs, l, true); if (r < 0) - return -ENOMEM; + return r; unit_write_settingf(u, flags, name, "%s=%s", name, joined); } @@ -3201,7 +3201,7 @@ int bus_exec_context_set_transient_property( _cleanup_free_ char *joined = NULL; r = strv_extend_strv(&c->exec_search_path, l, true); if (r < 0) - return -ENOMEM; + return r; joined = strv_join(c->exec_search_path, ":"); if (!joined) return log_oom(); diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 3aeb85e452..7aa44b2393 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -2271,7 +2271,9 @@ static int bus_unit_set_transient_property( u->documentation = strv_free(u->documentation); unit_write_settingf(u, flags, name, "%s=", name); } else { - strv_extend_strv(&u->documentation, l, false); + r = strv_extend_strv(&u->documentation, l, /* filter_duplicates= */ false); + if (r < 0) + return r; STRV_FOREACH(p, l) unit_write_settingf(u, flags, name, "%s=%s", name, *p); diff --git a/src/core/exec-credential.c b/src/core/exec-credential.c index 4aa3e35bd8..41c0fce13b 100644 --- a/src/core/exec-credential.c +++ b/src/core/exec-credential.c @@ -244,9 +244,8 @@ static char **credential_search_path(const ExecParameters *params, CredentialSea } if (IN_SET(path, CREDENTIAL_SEARCH_PATH_TRUSTED, CREDENTIAL_SEARCH_PATH_ALL)) { - if (params->received_credentials_directory) - if (strv_extend(&l, params->received_credentials_directory) < 0) - return NULL; + if (strv_extend(&l, params->received_credentials_directory) < 0) + return NULL; if (strv_extend_strv(&l, CONF_PATHS_STRV("credstore"), /* filter_duplicates= */ true) < 0) return NULL; diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c index f39b532800..1ae766d93e 100644 --- a/src/core/exec-invoke.c +++ b/src/core/exec-invoke.c @@ -2811,11 +2811,10 @@ static int compile_symlinks( * absolute, when they are processed in namespace.c they will be made relative automatically, i.e.: * 'os-release -> .os-release-stage/os-release' is what will be created. */ if (setup_os_release_symlink) { - r = strv_extend(&symlinks, "/run/host/.os-release-stage/os-release"); - if (r < 0) - return r; - - r = strv_extend(&symlinks, "/run/host/os-release"); + r = strv_extend_many( + &symlinks, + "/run/host/.os-release-stage/os-release", + "/run/host/os-release"); if (r < 0) return r; } diff --git a/src/core/manager-serialize.c b/src/core/manager-serialize.c index e9d567a97b..0d91faee6f 100644 --- a/src/core/manager-serialize.c +++ b/src/core/manager-serialize.c @@ -494,8 +494,9 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { (void) exec_shared_runtime_deserialize_one(m, val, fds); else if ((val = startswith(l, "subscribed="))) { - if (strv_extend(&m->deserialized_subscribed, val) < 0) - return -ENOMEM; + r = strv_extend(&m->deserialized_subscribed, val); + if (r < 0) + return r; } else if ((val = startswith(l, "varlink-server-socket-address="))) { if (!m->varlink_server && MANAGER_IS_SYSTEM(m)) { r = manager_varlink_init(m); diff --git a/src/core/path.c b/src/core/path.c index f6b5683467..8bfdd56116 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -994,11 +994,7 @@ static int activation_details_path_append_pair(ActivationDetails *details, char if (isempty(p->trigger_path_filename)) return 0; - r = strv_extend(strv, "trigger_path"); - if (r < 0) - return r; - - r = strv_extend(strv, p->trigger_path_filename); + r = strv_extend_many(strv, "trigger_path", p->trigger_path_filename); if (r < 0) return r; diff --git a/src/core/service.c b/src/core/service.c index 9a26bc1744..a4c9f80e04 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -3098,7 +3098,7 @@ int service_deserialize_exec_command( case STATE_EXEC_COMMAND_ARGS: r = strv_extend(&argv, arg); if (r < 0) - return -ENOMEM; + return r; break; default: assert_not_reached(); diff --git a/src/core/unit.c b/src/core/unit.c index 663e7e1737..49b45b2140 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -6530,11 +6530,7 @@ int activation_details_append_pair(ActivationDetails *details, char ***strv) { return 0; if (!isempty(details->trigger_unit_name)) { - r = strv_extend(strv, "trigger_unit"); - if (r < 0) - return r; - - r = strv_extend(strv, details->trigger_unit_name); + r = strv_extend_many(strv, "trigger_unit", details->trigger_unit_name); if (r < 0) return r; } diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index 53370c9f6e..90b2fe4a7a 100644 --- a/src/coredump/coredumpctl.c +++ b/src/coredump/coredumpctl.c @@ -1242,7 +1242,7 @@ static int run_debug(int argc, char **argv, void *userdata) { if (r < 0) return r; - r = strv_extend_strv(&debugger_call, STRV_MAKE(exe, "-c", path), false); + r = strv_extend_many(&debugger_call, exe, "-c", path); if (r < 0) return log_oom(); @@ -1251,14 +1251,14 @@ static int run_debug(int argc, char **argv, void *userdata) { const char *sysroot_cmd; sysroot_cmd = strjoina("set sysroot ", arg_root); - r = strv_extend_strv(&debugger_call, STRV_MAKE("-iex", sysroot_cmd), false); + r = strv_extend_many(&debugger_call, "-iex", sysroot_cmd); if (r < 0) return log_oom(); } else if (streq(arg_debugger, "lldb")) { const char *sysroot_cmd; sysroot_cmd = strjoina("platform select --sysroot ", arg_root, " host"); - r = strv_extend_strv(&debugger_call, STRV_MAKE("-O", sysroot_cmd), false); + r = strv_extend_many(&debugger_call, "-O", sysroot_cmd); if (r < 0) return log_oom(); } diff --git a/src/environment-d-generator/environment-d-generator.c b/src/environment-d-generator/environment-d-generator.c index 90e31c98ef..fa2c54af31 100644 --- a/src/environment-d-generator/environment-d-generator.c +++ b/src/environment-d-generator/environment-d-generator.c @@ -26,7 +26,7 @@ static int environment_dirs(char ***ret) { if (r < 0) return r; - r = strv_extend_front(&dirs, c); + r = strv_consume_prepend(&dirs, TAKE_PTR(c)); if (r < 0) return r; diff --git a/src/kernel-install/kernel-install.c b/src/kernel-install/kernel-install.c index 2fbaecf596..2523d43944 100644 --- a/src/kernel-install/kernel-install.c +++ b/src/kernel-install/kernel-install.c @@ -978,11 +978,10 @@ static int context_build_arguments(Context *c) { return log_oom(); } else if (c->action == ACTION_INSPECT) { - r = strv_extend(&a, c->kernel ?: "[KERNEL_IMAGE]"); - if (r < 0) - return log_oom(); - - r = strv_extend(&a, "[INITRD...]"); + r = strv_extend_many( + &a, + c->kernel ?: "[KERNEL_IMAGE]", + "[INITRD...]"); if (r < 0) return log_oom(); } diff --git a/src/nspawn/nspawn-oci.c b/src/nspawn/nspawn-oci.c index 62cf331a4a..8926e275a3 100644 --- a/src/nspawn/nspawn-oci.c +++ b/src/nspawn/nspawn-oci.c @@ -1588,7 +1588,7 @@ static int oci_sysctl(const char *name, JsonVariant *v, JsonDispatchFlags flags, return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), "sysctl key invalid, refusing: %s", k); - r = strv_extend_strv(&s->sysctl, STRV_MAKE(k, m), false); + r = strv_extend_many(&s->sysctl, k, m); if (r < 0) return log_oom(); } diff --git a/src/shared/mkfs-util.c b/src/shared/mkfs-util.c index 19b119b4af..4d440127e5 100644 --- a/src/shared/mkfs-util.c +++ b/src/shared/mkfs-util.c @@ -425,7 +425,7 @@ int make_filesystem( "-T", "default", node); - if (root && strv_extend_strv(&argv, STRV_MAKE("-d", root), false) < 0) + if (root && strv_extend_many(&argv, "-d", root) < 0) return log_oom(); if (quiet && strv_extend(&argv, "-q") < 0) @@ -450,7 +450,7 @@ int make_filesystem( if (!discard && strv_extend(&argv, "--nodiscard") < 0) return log_oom(); - if (root && strv_extend_strv(&argv, STRV_MAKE("-r", root), false) < 0) + if (root && strv_extend_many(&argv, "-r", root) < 0) return log_oom(); if (quiet && strv_extend(&argv, "-q") < 0) @@ -514,7 +514,7 @@ int make_filesystem( if (!protofile_with_opt) return -ENOMEM; - if (strv_extend_strv(&argv, STRV_MAKE("-p", protofile_with_opt), false) < 0) + if (strv_extend_many(&argv, "-p", protofile_with_opt) < 0) return log_oom(); } diff --git a/src/shared/module-util.c b/src/shared/module-util.c index 951701d497..907990d96e 100644 --- a/src/shared/module-util.c +++ b/src/shared/module-util.c @@ -8,6 +8,7 @@ static int denylist_modules(const char *p, char ***denylist) { _cleanup_strv_free_ char **k = NULL; + int r; assert(p); assert(denylist); @@ -16,8 +17,9 @@ static int denylist_modules(const char *p, char ***denylist) { if (!k) return -ENOMEM; - if (strv_extend_strv(denylist, k, true) < 0) - return -ENOMEM; + r = strv_extend_strv(denylist, k, /* filter_duplicates= */ true); + if (r < 0) + return r; return 0; } diff --git a/src/systemctl/systemctl-show.c b/src/systemctl/systemctl-show.c index c74bcf2642..5bb8ad9fa0 100644 --- a/src/systemctl/systemctl-show.c +++ b/src/systemctl/systemctl-show.c @@ -932,11 +932,7 @@ static int map_listen(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0) { - r = strv_extend(p, type); - if (r < 0) - return r; - - r = strv_extend(p, path); + r = strv_extend_many(p, type, path); if (r < 0) return r; } diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index e7dcb865c5..4d5d61c691 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -347,6 +347,7 @@ static int putgrent_with_members( FILE *group) { char **a; + int r; assert(c); assert(gr); @@ -365,15 +366,15 @@ static int putgrent_with_members( if (strv_contains(l, *i)) continue; - if (strv_extend(&l, *i) < 0) - return -ENOMEM; + r = strv_extend(&l, *i); + if (r < 0) + return r; added = true; } if (added) { struct group t; - int r; strv_uniq(l); strv_sort(l); @@ -396,6 +397,7 @@ static int putsgent_with_members( FILE *gshadow) { char **a; + int r; assert(sg); assert(gshadow); @@ -413,15 +415,15 @@ static int putsgent_with_members( if (strv_contains(l, *i)) continue; - if (strv_extend(&l, *i) < 0) - return -ENOMEM; + r = strv_extend(&l, *i); + if (r < 0) + return r; added = true; } if (added) { struct sgrp t; - int r; strv_uniq(l); strv_sort(l); diff --git a/src/test/test-strv.c b/src/test/test-strv.c index f70e2aa862..da8721214a 100644 --- a/src/test/test-strv.c +++ b/src/test/test-strv.c @@ -1014,4 +1014,29 @@ TEST(endswith_strv) { assert_se(streq_ptr(endswith_strv("waldo", STRV_MAKE("knurz", "", "waldo")), "")); } +TEST(strv_extend_many) { + _cleanup_strv_free_ char **l = NULL; + + assert_se(strv_extend_many(&l, NULL) >= 0); + assert_se(strv_isempty(l)); + + assert_se(strv_extend_many(&l, NULL, NULL, NULL) >= 0); + assert_se(strv_isempty(l)); + + assert_se(strv_extend_many(&l, "foo") >= 0); + assert_se(strv_equal(l, STRV_MAKE("foo"))); + + assert_se(strv_extend_many(&l, NULL, "bar", NULL) >= 0); + assert_se(strv_equal(l, STRV_MAKE("foo", "bar"))); + + assert_se(strv_extend_many(&l, "waldo", "quux") >= 0); + assert_se(strv_equal(l, STRV_MAKE("foo", "bar", "waldo", "quux"))); + + assert_se(strv_extend_many(&l, "1", "2", "3", "4") >= 0); + assert_se(strv_equal(l, STRV_MAKE("foo", "bar", "waldo", "quux", "1", "2", "3", "4"))); + + assert_se(strv_extend_many(&l, "yes", NULL, "no") >= 0); + assert_se(strv_equal(l, STRV_MAKE("foo", "bar", "waldo", "quux", "1", "2", "3", "4", "yes", "no"))); +} + DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index de785370ed..9601c538bc 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -355,15 +355,11 @@ static int user_config_paths(char*** ret) { if (r < 0) return r; - r = strv_extend(&res, persistent_config); - if (r < 0) - return r; - - r = strv_extend(&res, runtime_config); - if (r < 0) - return r; - - r = strv_extend(&res, data_home); + r = strv_extend_many( + &res, + persistent_config, + runtime_config, + data_home); if (r < 0) return r; @@ -3983,16 +3979,16 @@ static int exclude_default_prefixes(void) { * likely over-mounted if the root directory is actually used, and it wouldbe less than ideal to have * all kinds of files created/adjusted underneath these mount points. */ - r = strv_extend_strv( + r = strv_extend_many( &arg_exclude_prefixes, - STRV_MAKE("/dev", - "/proc", - "/run", - "/sys"), - true); + "/dev", + "/proc", + "/run", + "/sys"); if (r < 0) return log_oom(); + strv_uniq(arg_exclude_prefixes); return 0; } diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c index 74732988d9..e2d45d514a 100644 --- a/src/vmspawn/vmspawn.c +++ b/src/vmspawn/vmspawn.c @@ -516,25 +516,25 @@ static int run_virtual_machine(void) { return log_oom(); } - r = strv_extend_strv(&cmdline, STRV_MAKE("-cpu", "max"), /* filter_duplicates= */ false); + r = strv_extend_many(&cmdline, "-cpu", "max"); if (r < 0) return log_oom(); - if (arg_qemu_gui) { - r = strv_extend_strv(&cmdline, STRV_MAKE("-vga", "virtio"), /* filter_duplicates= */ false); - if (r < 0) - return log_oom(); - } else { - r = strv_extend_strv(&cmdline, STRV_MAKE( - "-nographic", - "-nodefaults", - "-chardev", "stdio,mux=on,id=console,signal=off", - "-serial", "chardev:console", - "-mon", "console" - ), /* filter_duplicates= */ false); - if (r < 0) - return log_oom(); - } + if (arg_qemu_gui) + r = strv_extend_many( + &cmdline, + "-vga", + "virtio"); + else + r = strv_extend_many( + &cmdline, + "-nographic", + "-nodefaults", + "-chardev", "stdio,mux=on,id=console,signal=off", + "-serial", "chardev:console", + "-mon", "console"); + if (r < 0) + return log_oom(); if (ARCHITECTURE_SUPPORTS_SMBIOS) FOREACH_ARRAY(cred, arg_credentials.credentials, arg_credentials.n_credentials) { @@ -588,11 +588,11 @@ static int run_virtual_machine(void) { (void) copy_access(source_fd, target_fd); (void) copy_times(source_fd, target_fd, 0); - r = strv_extend_strv(&cmdline, STRV_MAKE( - "-global", "ICH9-LPC.disable_s3=1", - "-global", "driver=cfi.pflash01,property=secure,value=on", - "-drive" - ), /* filter_duplicates= */ false); + r = strv_extend_many( + &cmdline, + "-global", "ICH9-LPC.disable_s3=1", + "-global", "driver=cfi.pflash01,property=secure,value=on", + "-drive"); if (r < 0) return log_oom(); @@ -609,10 +609,10 @@ static int run_virtual_machine(void) { if (r < 0) return log_oom(); - r = strv_extend_strv(&cmdline, STRV_MAKE( - "-device", "virtio-scsi-pci,id=scsi", - "-device", "scsi-hd,drive=mkosi,bootindex=1" - ), /* filter_duplicates= */ false); + r = strv_extend_many( + &cmdline, + "-device", "virtio-scsi-pci,id=scsi", + "-device", "scsi-hd,drive=mkosi,bootindex=1"); if (r < 0) return log_oom();