From 7410616cd9dbbec97cf98d75324da5cda2b2f7a2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 30 Apr 2015 20:21:00 +0200 Subject: [PATCH] core: rework unit name validation and manipulation logic A variety of changes: - Make sure all our calls distuingish OOM from other errors if OOM is not the only error possible. - Be much stricter when parsing escaped paths, do not accept trailing or leading escaped slashes. - Change unit validation to take a bit mask for allowing plain names, instance names or template names or an combination thereof. - Refuse manipulating invalid unit name --- src/core/automount.c | 19 +- src/core/busname.c | 6 +- src/core/dbus-unit.c | 4 +- src/core/device.c | 19 +- src/core/load-fragment.c | 16 +- src/core/manager.c | 17 +- src/core/mount.c | 23 +- src/core/snapshot.c | 4 +- src/core/socket.c | 23 +- src/core/swap.c | 39 +- src/core/unit-printf.c | 55 +- src/core/unit.c | 162 ++--- src/cryptsetup/cryptsetup-generator.c | 20 +- src/dbus1-generator/dbus1-generator.c | 2 +- src/debug-generator/debug-generator.c | 12 +- src/escape/escape.c | 40 +- src/fstab-generator/fstab-generator.c | 18 +- src/getty-generator/getty-generator.c | 22 +- src/gpt-auto-generator/gpt-auto-generator.c | 25 +- .../hibernate-resume-generator.c | 8 +- src/journal/journalctl.c | 17 +- src/login/logind-user.c | 8 +- src/machine/machinectl.c | 55 +- src/run/run.c | 44 +- src/shared/cgroup-util.c | 15 +- src/shared/dropin.c | 15 +- src/shared/generator.c | 18 +- src/shared/install-printf.c | 16 +- src/shared/install.c | 59 +- src/shared/unit-name.c | 636 +++++++++++------- src/shared/unit-name.h | 106 +-- src/systemctl/systemctl.c | 100 +-- src/sysv-generator/sysv-generator.c | 8 +- src/test/test-unit-name.c | 302 +++++---- 34 files changed, 1066 insertions(+), 867 deletions(-) diff --git a/src/core/automount.c b/src/core/automount.c index 73b75f163e..1806fa39d3 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -167,8 +167,9 @@ static int automount_add_default_dependencies(Automount *a) { } static int automount_verify(Automount *a) { - bool b; _cleanup_free_ char *e = NULL; + int r; + assert(a); if (UNIT(a)->load_state != UNIT_LOADED) @@ -179,13 +180,11 @@ static int automount_verify(Automount *a) { return -EINVAL; } - e = unit_name_from_path(a->where, ".automount"); - if (!e) - return -ENOMEM; + r = unit_name_from_path(a->where, ".automount", &e); + if (r < 0) + return log_unit_error(UNIT(a)->id, "Failed to generate unit name from path: %m"); - b = unit_has_name(UNIT(a), e); - - if (!b) { + if (!unit_has_name(UNIT(a), e)) { log_unit_error(UNIT(a)->id, "%s's Where setting doesn't match unit name. Refusing.", UNIT(a)->id); return -EINVAL; } @@ -209,9 +208,9 @@ static int automount_load(Unit *u) { Unit *x; if (!a->where) { - a->where = unit_name_to_path(u->id); - if (!a->where) - return -ENOMEM; + r = unit_name_to_path(u->id, &a->where); + if (r < 0) + return r; } path_kill_slashes(a->where); diff --git a/src/core/busname.c b/src/core/busname.c index 20d49fe3e9..8e92fb47fe 100644 --- a/src/core/busname.c +++ b/src/core/busname.c @@ -163,9 +163,9 @@ static int busname_add_extras(BusName *n) { assert(n); if (!n->name) { - n->name = unit_name_to_prefix(u->id); - if (!n->name) - return -ENOMEM; + r = unit_name_to_prefix(u->id, &n->name); + if (r < 0) + return r; } if (!u->description) { diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 2699e81492..056c7117b2 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -940,7 +940,7 @@ static int bus_unit_set_transient_property( if (r < 0) return r; - if (!unit_name_is_valid(s, TEMPLATE_INVALID) || !endswith(s, ".slice")) + if (!unit_name_is_valid(s, UNIT_NAME_PLAIN) || !endswith(s, ".slice")) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid slice name %s", s); if (isempty(s)) { @@ -988,7 +988,7 @@ static int bus_unit_set_transient_property( return r; while ((r = sd_bus_message_read(message, "s", &other)) > 0) { - if (!unit_name_is_valid(other, TEMPLATE_INVALID)) + if (!unit_name_is_valid(other, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name %s", other); if (mode != UNIT_CHECK) { diff --git a/src/core/device.c b/src/core/device.c index f8e65b876f..2c56c5a2d2 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -279,9 +279,9 @@ static int device_add_udev_wants(Unit *u, struct udev_device *dev) { memcpy(e, word, l); e[l] = 0; - n = unit_name_mangle(e, MANGLE_NOGLOB); - if (!n) - return log_oom(); + r = unit_name_mangle(e, UNIT_NAME_NOGLOB, &n); + if (r < 0) + return log_unit_error_errno(u->id, r, "Failed to mangle unit name: %m"); r = unit_add_dependency_by_name(u, UNIT_WANTS, n, NULL, true); if (r < 0) @@ -308,9 +308,9 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa if (!sysfs) return 0; - e = unit_name_from_path(path, ".device"); - if (!e) - return log_oom(); + r = unit_name_from_path(path, ".device", &e); + if (r < 0) + return log_unit_error_errno(u->id, r, "Failed to generate device name: %m"); u = manager_get_unit(m, e); @@ -491,6 +491,7 @@ static int device_update_found_by_sysfs(Manager *m, const char *sysfs, bool add, static int device_update_found_by_name(Manager *m, const char *path, bool add, DeviceFound found, bool now) { _cleanup_free_ char *e = NULL; Unit *u; + int r; assert(m); assert(path); @@ -498,9 +499,9 @@ static int device_update_found_by_name(Manager *m, const char *path, bool add, D if (found == DEVICE_NOT_FOUND) return 0; - e = unit_name_from_path(path, ".device"); - if (!e) - return log_oom(); + r = unit_name_from_path(path, ".device", &e); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name from device path: %m"); u = manager_get_unit(m, e); if (!u) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index b76656b931..970120ab70 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -3394,7 +3394,7 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { * unit name. */ name = basename(*filename); - if (unit_name_is_valid(name, TEMPLATE_VALID)) { + if (unit_name_is_valid(name, UNIT_NAME_ANY)) { id = set_get(names, name); if (!id) { @@ -3642,11 +3642,11 @@ int unit_load_fragment(Unit *u) { /* Look for a template */ if (u->load_state == UNIT_STUB && u->instance) { - _cleanup_free_ char *k; + _cleanup_free_ char *k = NULL; - k = unit_name_template(u->id); - if (!k) - return -ENOMEM; + r = unit_name_template(u->id, &k); + if (r < 0) + return r; r = load_from_path(u, k); if (r < 0) @@ -3659,9 +3659,9 @@ int unit_load_fragment(Unit *u) { if (t == u->id) continue; - z = unit_name_template(t); - if (!z) - return -ENOMEM; + r = unit_name_template(t, &z); + if (r < 0) + return r; r = load_from_path(u, z); if (r < 0) diff --git a/src/core/manager.c b/src/core/manager.c index b49452151b..8c8645b68d 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -1306,7 +1306,7 @@ int manager_load_unit_prepare( t = unit_name_to_type(name); - if (t == _UNIT_TYPE_INVALID || !unit_name_is_valid(name, TEMPLATE_INVALID)) + if (t == _UNIT_TYPE_INVALID || !unit_name_is_valid(name, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is not valid.", name); ret = manager_get_unit(m, name); @@ -2093,7 +2093,7 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { #ifdef HAVE_AUDIT _cleanup_free_ char *p = NULL; const char *msg; - int audit_fd; + int audit_fd, r; audit_fd = get_audit_fd(); if (audit_fd < 0) @@ -2110,9 +2110,9 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { if (u->type != UNIT_SERVICE) return; - p = unit_name_to_prefix_and_instance(u->id); - if (!p) { - log_oom(); + r = unit_name_to_prefix_and_instance(u->id, &p); + if (r < 0) { + log_error_errno(r, "Failed to extract prefix and instance of unit name: %m"); return; } @@ -3027,15 +3027,16 @@ void manager_status_printf(Manager *m, StatusType type, const char *status, cons int manager_get_unit_by_path(Manager *m, const char *path, const char *suffix, Unit **_found) { _cleanup_free_ char *p = NULL; Unit *found; + int r; assert(m); assert(path); assert(suffix); assert(_found); - p = unit_name_from_path(path, suffix); - if (!p) - return -ENOMEM; + r = unit_name_from_path(path, suffix, &p); + if (r < 0) + return r; found = manager_get_unit(m, p); if (!found) { diff --git a/src/core/mount.c b/src/core/mount.c index d0c41a73a9..65a66b468f 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -435,7 +435,7 @@ static int mount_add_default_dependencies(Mount *m) { static int mount_verify(Mount *m) { _cleanup_free_ char *e = NULL; - bool b; + int r; assert(m); @@ -445,12 +445,11 @@ static int mount_verify(Mount *m) { if (!m->from_fragment && !m->from_proc_self_mountinfo) return -ENOENT; - e = unit_name_from_path(m->where, ".mount"); - if (!e) - return -ENOMEM; + r = unit_name_from_path(m->where, ".mount", &e); + if (r < 0) + return log_unit_error_errno(UNIT(m)->id, r, "Failed to generate unit name from mount path: %m"); - b = unit_has_name(UNIT(m), e); - if (!b) { + if (!unit_has_name(UNIT(m), e)) { log_unit_error(UNIT(m)->id, "%s's Where= setting doesn't match unit name. Refusing.", UNIT(m)->id); return -EINVAL; } @@ -483,9 +482,9 @@ static int mount_add_extras(Mount *m) { m->from_fragment = true; if (!m->where) { - m->where = unit_name_to_path(u->id); - if (!m->where) - return -ENOMEM; + r = unit_name_to_path(u->id, &m->where); + if (r < 0) + return r; } path_kill_slashes(m->where); @@ -1419,9 +1418,9 @@ static int mount_setup_unit( if (!is_path(where)) return 0; - e = unit_name_from_path(where, ".mount"); - if (!e) - return -ENOMEM; + r = unit_name_from_path(where, ".mount", &e); + if (r < 0) + return r; u = manager_get_unit(m, e); if (!u) { diff --git a/src/core/snapshot.c b/src/core/snapshot.c index b70c3beb60..2c00680f16 100644 --- a/src/core/snapshot.c +++ b/src/core/snapshot.c @@ -201,10 +201,10 @@ int snapshot_create(Manager *m, const char *name, bool cleanup, sd_bus_error *e, assert(_s); if (name) { - if (!unit_name_is_valid(name, TEMPLATE_INVALID)) + if (!unit_name_is_valid(name, UNIT_NAME_PLAIN)) return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is not valid.", name); - if (unit_name_to_type(name) != UNIT_SNAPSHOT) + if (!endswith(name, ".snapshot")) return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s lacks snapshot suffix.", name); if (manager_get_unit(m, name)) diff --git a/src/core/socket.c b/src/core/socket.c index 67beda44d6..55334e4a54 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -196,12 +196,15 @@ int socket_instantiate_service(Socket *s) { * here. For Accept=no this is mostly a NOP since the service * is figured out at load time anyway. */ - if (UNIT_DEREF(s->service) || !s->accept) + if (UNIT_DEREF(s->service)) return 0; - prefix = unit_name_to_prefix(UNIT(s)->id); - if (!prefix) - return -ENOMEM; + if (!s->accept) + return 0; + + r = unit_name_to_prefix(UNIT(s)->id, &prefix); + if (r < 0) + return r; if (asprintf(&name, "%s@%u.service", prefix, s->n_accepted) < 0) return -ENOMEM; @@ -1836,17 +1839,13 @@ static void socket_enter_running(Socket *s, int cfd) { return; } - prefix = unit_name_to_prefix(UNIT(s)->id); - if (!prefix) { - r = -ENOMEM; + r = unit_name_to_prefix(UNIT(s)->id, &prefix); + if (r < 0) goto fail; - } - name = unit_name_build(prefix, instance, ".service"); - if (!name) { - r = -ENOMEM; + r = unit_name_build(prefix, instance, ".service", &name); + if (r < 0) goto fail; - } r = unit_add_name(UNIT_DEREF(s->service), name); if (r < 0) diff --git a/src/core/swap.c b/src/core/swap.c index a9834a7fe5..3327cd958f 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -222,18 +222,17 @@ static int swap_add_default_dependencies(Swap *s) { } static int swap_verify(Swap *s) { - bool b; _cleanup_free_ char *e = NULL; + int r; if (UNIT(s)->load_state != UNIT_LOADED) return 0; - e = unit_name_from_path(s->what, ".swap"); - if (!e) - return log_oom(); + r = unit_name_from_path(s->what, ".swap", &e); + if (r < 0) + return log_unit_error_errno(UNIT(s)->id, r, "%s: failed to generate unit name from path: %m", UNIT(s)->id); - b = unit_has_name(UNIT(s), e); - if (!b) { + if (!unit_has_name(UNIT(s), e)) { log_unit_error(UNIT(s)->id, "%s: Value of \"What\" and unit name do not match, not loading.", UNIT(s)->id); return -EINVAL; } @@ -289,8 +288,11 @@ static int swap_load(Unit *u) { s->what = strdup(s->parameters_fragment.what); else if (s->parameters_proc_swaps.what) s->what = strdup(s->parameters_proc_swaps.what); - else - s->what = unit_name_to_path(u->id); + else { + r = unit_name_to_path(u->id, &s->what); + if (r < 0) + return r; + } if (!s->what) return -ENOMEM; @@ -355,9 +357,9 @@ static int swap_setup_unit( assert(what); assert(what_proc_swaps); - e = unit_name_from_path(what, ".swap"); - if (!e) - return log_oom(); + r = unit_name_from_path(what, ".swap", &e); + if (r < 0) + return log_unit_error_errno(u->id, r, "Failed to generate unit name from path: %m"); u = manager_get_unit(m, e); @@ -1329,9 +1331,9 @@ int swap_process_device_new(Manager *m, struct udev_device *dev) { if (!dn) return 0; - e = unit_name_from_path(dn, ".swap"); - if (!e) - return -ENOMEM; + r = unit_name_from_path(dn, ".swap", &e); + if (r < 0) + return r; s = hashmap_get(m->units, e); if (s) @@ -1340,15 +1342,14 @@ int swap_process_device_new(Manager *m, struct udev_device *dev) { first = udev_device_get_devlinks_list_entry(dev); udev_list_entry_foreach(item, first) { _cleanup_free_ char *n = NULL; + int q; - n = unit_name_from_path(udev_list_entry_get_name(item), ".swap"); - if (!n) - return -ENOMEM; + q = unit_name_from_path(udev_list_entry_get_name(item), ".swap", &n); + if (q < 0) + return q; s = hashmap_get(m->units, n); if (s) { - int q; - q = swap_set_devnode(s, dn); if (q < 0) r = q; diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c index 5513fe7d9c..8050e2fd66 100644 --- a/src/core/unit-printf.c +++ b/src/core/unit-printf.c @@ -30,83 +30,54 @@ static int specifier_prefix_and_instance(char specifier, void *data, void *userdata, char **ret) { Unit *u = userdata; - char *n; assert(u); - n = unit_name_to_prefix_and_instance(u->id); - if (!n) - return -ENOMEM; - - *ret = n; - return 0; + return unit_name_to_prefix_and_instance(u->id, ret); } static int specifier_prefix(char specifier, void *data, void *userdata, char **ret) { Unit *u = userdata; - char *n; assert(u); - n = unit_name_to_prefix(u->id); - if (!n) - return -ENOMEM; - - *ret = n; - return 0; + return unit_name_to_prefix(u->id, ret); } static int specifier_prefix_unescaped(char specifier, void *data, void *userdata, char **ret) { - Unit *u = userdata; _cleanup_free_ char *p = NULL; - char *n; + Unit *u = userdata; + int r; assert(u); - p = unit_name_to_prefix(u->id); - if (!p) - return -ENOMEM; + r = unit_name_to_prefix(u->id, &p); + if (r < 0) + return r; - n = unit_name_unescape(p); - if (!n) - return -ENOMEM; - - *ret = n; - return 0; + return unit_name_unescape(p, ret); } static int specifier_instance_unescaped(char specifier, void *data, void *userdata, char **ret) { Unit *u = userdata; - char *n; assert(u); if (!u->instance) - return -EOPNOTSUPP; + return -EINVAL; - n = unit_name_unescape(u->instance); - if (!n) - return -ENOMEM; - - *ret = n; - return 0; + return unit_name_unescape(u->instance, ret); } static int specifier_filename(char specifier, void *data, void *userdata, char **ret) { Unit *u = userdata; - char *n; assert(u); if (u->instance) - n = unit_name_path_unescape(u->instance); + return unit_name_path_unescape(u->instance, ret); else - n = unit_name_to_path(u->id); - if (!n) - return -ENOMEM; - - *ret = n; - return 0; + return unit_name_to_path(u->id, ret); } static int specifier_cgroup(char specifier, void *data, void *userdata, char **ret) { @@ -195,7 +166,7 @@ static int specifier_user_name(char specifier, void *data, void *userdata, char c = unit_get_exec_context(u); if (!c) - return -EOPNOTSUPP; + return -EINVAL; if (u->manager->running_as == SYSTEMD_SYSTEM) { diff --git a/src/core/unit.c b/src/core/unit.c index 6071bd51b1..6513bcb1cb 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -143,21 +143,31 @@ int unit_add_name(Unit *u, const char *text) { assert(u); assert(text); - if (unit_name_is_template(text)) { + if (unit_name_is_valid(text, UNIT_NAME_TEMPLATE)) { if (!u->instance) return -EINVAL; - s = unit_name_replace_instance(text, u->instance); - } else + r = unit_name_replace_instance(text, u->instance, &s); + if (r < 0) + return r; + } else { s = strdup(text); - if (!s) - return -ENOMEM; + if (!s) + return -ENOMEM; + } - if (!unit_name_is_valid(s, TEMPLATE_INVALID)) + if (set_contains(u->names, s)) + return 0; + if (hashmap_contains(u->manager->units, s)) + return -EEXIST; + + if (!unit_name_is_valid(s, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) return -EINVAL; - assert_se((t = unit_name_to_type(s)) >= 0); + t = unit_name_to_type(s); + if (t < 0) + return -EINVAL; if (u->type != _UNIT_TYPE_INVALID && t != u->type) return -EINVAL; @@ -170,25 +180,25 @@ int unit_add_name(Unit *u, const char *text) { return -EINVAL; /* Ensure that this unit is either instanced or not instanced, - * but not both. */ + * but not both. Note that we do allow names with different + * instance names however! */ if (u->type != _UNIT_TYPE_INVALID && !u->instance != !i) return -EINVAL; - if (unit_vtable[t]->no_alias && - !set_isempty(u->names) && - !set_get(u->names, s)) + if (unit_vtable[t]->no_alias && !set_isempty(u->names)) return -EEXIST; if (hashmap_size(u->manager->units) >= MANAGER_MAX_NAMES) return -E2BIG; r = set_put(u->names, s); - if (r <= 0) + if (r < 0) return r; + assert(r > 0); r = hashmap_put(u->manager->units, s, u); if (r < 0) { - set_remove(u->names, s); + (void) set_remove(u->names, s); return r; } @@ -218,14 +228,14 @@ int unit_choose_id(Unit *u, const char *name) { assert(u); assert(name); - if (unit_name_is_template(name)) { + if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) { if (!u->instance) return -EINVAL; - t = unit_name_replace_instance(name, u->instance); - if (!t) - return -ENOMEM; + r = unit_name_replace_instance(name, u->instance, &t); + if (r < 0) + return r; name = t; } @@ -235,6 +245,7 @@ int unit_choose_id(Unit *u, const char *name) { if (!s) return -ENOENT; + /* Determine the new instance from the new id */ r = unit_name_to_instance(s, &i); if (r < 0) return r; @@ -748,24 +759,22 @@ int unit_merge_by_name(Unit *u, const char *name) { assert(u); assert(name); - if (unit_name_is_template(name)) { + if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) { if (!u->instance) return -EINVAL; - s = unit_name_replace_instance(name, u->instance); - if (!s) - return -ENOMEM; + r = unit_name_replace_instance(name, u->instance, &s); + if (r < 0) + return r; name = s; } other = manager_get_unit(u->manager, name); - if (!other) - r = unit_add_name(u, name); - else - r = unit_merge(u, other); + if (other) + return unit_merge(u, other); - return r; + return unit_add_name(u, name); } Unit* unit_follow_merge(Unit *u) { @@ -2294,58 +2303,55 @@ int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit if (r < 0) return r; - r = unit_add_dependency(u, e, other, add_reference); - if (r < 0) - return r; - - return 0; + return unit_add_dependency(u, e, other, add_reference); } -static const char *resolve_template(Unit *u, const char *name, const char*path, char **p) { - char *s; +static int resolve_template(Unit *u, const char *name, const char*path, char **buf, const char **ret) { + int r; assert(u); assert(name || path); - assert(p); + assert(buf); + assert(ret); if (!name) name = basename(path); - if (!unit_name_is_template(name)) { - *p = NULL; - return name; + if (!unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) { + *buf = NULL; + *ret = name; + return 0; } if (u->instance) - s = unit_name_replace_instance(name, u->instance); + r = unit_name_replace_instance(name, u->instance, buf); else { _cleanup_free_ char *i = NULL; - i = unit_name_to_prefix(u->id); - if (!i) - return NULL; + r = unit_name_to_prefix(u->id, &i); + if (r < 0) + return r; - s = unit_name_replace_instance(name, i); + r = unit_name_replace_instance(name, i, buf); } + if (r < 0) + return r; - if (!s) - return NULL; - - *p = s; - return s; + *ret = *buf; + return 0; } int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) { + _cleanup_free_ char *buf = NULL; Unit *other; int r; - _cleanup_free_ char *s = NULL; assert(u); assert(name || path); - name = resolve_template(u, name, path, &s); - if (!name) - return -ENOMEM; + r = resolve_template(u, name, path, &buf, &name); + if (r < 0) + return r; r = manager_load_unit(u->manager, name, path, NULL, &other); if (r < 0) @@ -2355,16 +2361,16 @@ int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, con } int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) { - _cleanup_free_ char *s = NULL; + _cleanup_free_ char *buf = NULL; Unit *other; int r; assert(u); assert(name || path); - name = resolve_template(u, name, path, &s); - if (!name) - return -ENOMEM; + r = resolve_template(u, name, path, &buf, &name); + if (r < 0) + return r; r = manager_load_unit(u->manager, name, path, NULL, &other); if (r < 0) @@ -2374,16 +2380,16 @@ int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency } int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) { + _cleanup_free_ char *buf = NULL; Unit *other; int r; - _cleanup_free_ char *s = NULL; assert(u); assert(name || path); - name = resolve_template(u, name, path, &s); - if (!name) - return -ENOMEM; + r = resolve_template(u, name, path, &buf, &name); + if (r < 0) + return r; r = manager_load_unit(u->manager, name, path, NULL, &other); if (r < 0) @@ -2393,26 +2399,22 @@ int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *n } int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) { + _cleanup_free_ char *buf = NULL; Unit *other; int r; - _cleanup_free_ char *s = NULL; assert(u); assert(name || path); - name = resolve_template(u, name, path, &s); - if (!name) - return -ENOMEM; + r = resolve_template(u, name, path, &buf, &name); + if (r < 0) + return r; r = manager_load_unit(u->manager, name, path, NULL, &other); if (r < 0) return r; - r = unit_add_two_dependencies(other, d, e, u, add_reference); - if (r < 0) - return r; - - return r; + return unit_add_two_dependencies(other, d, e, u, add_reference); } int set_unit_path(const char *p) { @@ -2475,14 +2477,14 @@ int unit_add_default_slice(Unit *u, CGroupContext *c) { /* Implicitly place all instantiated units in their * own per-template slice */ - prefix = unit_name_to_prefix(u->id); - if (!prefix) - return -ENOMEM; + r = unit_name_to_prefix(u->id, &prefix); + if (r < 0) + return r; /* The prefix is already escaped, but it might include * "-" which has a special meaning for slice units, * hence escape it here extra. */ - escaped = strreplace(prefix, "-", "\\x2d"); + escaped = unit_name_escape(prefix); if (!escaped) return -ENOMEM; @@ -2525,11 +2527,11 @@ int unit_load_related_unit(Unit *u, const char *type, Unit **_found) { assert(type); assert(_found); - t = unit_name_change_suffix(u->id, type); - if (!t) - return -ENOMEM; - - assert(!unit_has_name(u, t)); + r = unit_name_change_suffix(u->id, type, &t); + if (r < 0) + return r; + if (unit_has_name(u, t)) + return -EINVAL; r = manager_load_unit(u->manager, t, NULL, NULL, _found); assert(r < 0 || *_found != u); @@ -2858,9 +2860,9 @@ int unit_add_node_link(Unit *u, const char *what, bool wants) { if (!unit_type_supported(UNIT_DEVICE)) return 0; - e = unit_name_from_path(what, ".device"); - if (!e) - return -ENOMEM; + r = unit_name_from_path(what, ".device", &e); + if (r < 0) + return r; r = manager_load_unit(u->manager, e, NULL, NULL, &device); if (r < 0) diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c index 5d234e6a5a..755ee5d88d 100644 --- a/src/cryptsetup/cryptsetup-generator.c +++ b/src/cryptsetup/cryptsetup-generator.c @@ -78,9 +78,9 @@ static int create_disk( if (!e) return log_oom(); - n = unit_name_build("systemd-cryptsetup", e, ".service"); - if (!n) - return log_oom(); + r = unit_name_build("systemd-cryptsetup", e, ".service", &n); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); p = strjoin(arg_dest, "/", n, NULL); if (!p) @@ -90,9 +90,9 @@ static int create_disk( if (!u) return log_oom(); - d = unit_name_from_path(u, ".device"); - if (!d) - return log_oom(); + r = unit_name_from_path(u, ".device", &d); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); f = fopen(p, "wxe"); if (!f) @@ -128,11 +128,11 @@ static int create_disk( if (!path_equal(uu, "/dev/null")) { if (is_device_path(uu)) { - _cleanup_free_ char *dd; + _cleanup_free_ char *dd = NULL; - dd = unit_name_from_path(uu, ".device"); - if (!dd) - return log_oom(); + r = unit_name_from_path(uu, ".device", &dd); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); fprintf(f, "After=%1$s\nRequires=%1$s\n", dd); } else diff --git a/src/dbus1-generator/dbus1-generator.c b/src/dbus1-generator/dbus1-generator.c index 2e08af2df2..c909a4b1da 100644 --- a/src/dbus1-generator/dbus1-generator.c +++ b/src/dbus1-generator/dbus1-generator.c @@ -188,7 +188,7 @@ static int add_dbus(const char *path, const char *fname, const char *type) { } if (service) { - if (!unit_name_is_valid(service, TEMPLATE_INVALID)) { + if (!unit_name_is_valid(service, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) { log_warning("Unit name %s is not valid, ignoring.", service); return 0; } diff --git a/src/debug-generator/debug-generator.c b/src/debug-generator/debug-generator.c index 1b9019325c..9d0ab06e2f 100644 --- a/src/debug-generator/debug-generator.c +++ b/src/debug-generator/debug-generator.c @@ -41,9 +41,9 @@ static int parse_proc_cmdline_item(const char *key, const char *value) { else { char *n; - n = unit_name_mangle(value, MANGLE_NOGLOB); - if (!n) - return log_oom(); + r = unit_name_mangle(value, UNIT_NAME_NOGLOB, &n); + if (r < 0) + return log_error_errno(r, "Failed to glob unit name: %m"); r = strv_consume(&arg_mask, n); if (r < 0) @@ -57,9 +57,9 @@ static int parse_proc_cmdline_item(const char *key, const char *value) { else { char *n; - n = unit_name_mangle(value, MANGLE_NOGLOB); - if (!n) - return log_oom(); + r = unit_name_mangle(value, UNIT_NAME_NOGLOB, &n); + if (r < 0) + return log_error_errno(r, "Failed to glob unit name: %m"); r = strv_consume(&arg_wants, n); if (r < 0) diff --git a/src/escape/escape.c b/src/escape/escape.c index f2a0721861..9ccb015538 100644 --- a/src/escape/escape.c +++ b/src/escape/escape.c @@ -99,7 +99,7 @@ static int parse_argv(int argc, char *argv[]) { case ARG_TEMPLATE: - if (!unit_name_is_valid(optarg, true) || !unit_name_is_template(optarg)) { + if (!unit_name_is_valid(optarg, UNIT_NAME_TEMPLATE)) { log_error("Template name %s is not valid.", optarg); return -EINVAL; } @@ -166,22 +166,26 @@ int main(int argc, char *argv[]) { switch (arg_action) { case ACTION_ESCAPE: - if (arg_path) - e = unit_name_path_escape(*i); - else + if (arg_path) { + r = unit_name_path_escape(*i, &e); + if (r < 0) { + log_error_errno(r, "Failed to escape string: %m"); + goto finish; + } + } else { e = unit_name_escape(*i); - - if (!e) { - r = log_oom(); - goto finish; + if (!e) { + r = log_oom(); + goto finish; + } } if (arg_template) { char *x; - x = unit_name_replace_instance(arg_template, e); - if (!x) { - r = log_oom(); + r = unit_name_replace_instance(arg_template, e, &x); + if (r < 0) { + log_error_errno(r, "Failed to replace instance: %m"); goto finish; } @@ -204,20 +208,20 @@ int main(int argc, char *argv[]) { case ACTION_UNESCAPE: if (arg_path) - e = unit_name_path_unescape(*i); + r = unit_name_path_unescape(*i, &e); else - e = unit_name_unescape(*i); + r = unit_name_unescape(*i, &e); - if (!e) { - r = log_oom(); + if (r < 0) { + log_error_errno(r, "Failed to unescape string: %m"); goto finish; } break; case ACTION_MANGLE: - e = unit_name_mangle(*i, MANGLE_NOGLOB); - if (!e) { - r = log_oom(); + r = unit_name_mangle(*i, UNIT_NAME_NOGLOB, &e); + if (r < 0) { + log_error_errno(r, "Failed to mangle name: %m"); goto finish; } break; diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index 664ee2aa6f..167ec60dc5 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -83,9 +83,9 @@ static int add_swap( opts = filtered; } - name = unit_name_from_path(what, ".swap"); - if (!name) - return log_oom(); + r = unit_name_from_path(what, ".swap", &name); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); unit = strjoin(arg_dest, "/", name, NULL); if (!unit) @@ -224,9 +224,9 @@ static int add_mount( noauto = nofail = automount = false; } - name = unit_name_from_path(where, ".mount"); - if (!name) - return log_oom(); + r = unit_name_from_path(where, ".mount", &name); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); unit = strjoin(arg_dest, "/", name, NULL); if (!unit) @@ -290,9 +290,9 @@ static int add_mount( } if (automount) { - automount_name = unit_name_from_path(where, ".automount"); - if (!automount_name) - return log_oom(); + r = unit_name_from_path(where, ".automount", &automount_name); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); automount_unit = strjoin(arg_dest, "/", automount_name, NULL); if (!automount_unit) diff --git a/src/getty-generator/getty-generator.c b/src/getty-generator/getty-generator.c index d4688d304f..d23caab44a 100644 --- a/src/getty-generator/getty-generator.c +++ b/src/getty-generator/getty-generator.c @@ -50,13 +50,11 @@ static int add_symlink(const char *fservice, const char *tservice) { r = symlink(from, to); if (r < 0) { + /* In case console=hvc0 is passed this will very likely result in EEXIST */ if (errno == EEXIST) - /* In case console=hvc0 is passed this will very likely result in EEXIST */ return 0; - else { - log_error_errno(errno, "Failed to create symlink %s: %m", to); - return -errno; - } + + return log_error_errno(errno, "Failed to create symlink %s: %m", to); } return 0; @@ -64,28 +62,30 @@ static int add_symlink(const char *fservice, const char *tservice) { static int add_serial_getty(const char *tty) { _cleanup_free_ char *n = NULL; + int r; assert(tty); log_debug("Automatically adding serial getty for /dev/%s.", tty); - n = unit_name_from_path_instance("serial-getty", tty, ".service"); - if (!n) - return log_oom(); + r = unit_name_from_path_instance("serial-getty", tty, ".service", &n); + if (r < 0) + return log_error_errno(r, "Failed to generate service name: %m"); return add_symlink("serial-getty@.service", n); } static int add_container_getty(const char *tty) { _cleanup_free_ char *n = NULL; + int r; assert(tty); log_debug("Automatically adding container getty for /dev/pts/%s.", tty); - n = unit_name_from_path_instance("container-getty", tty, ".service"); - if (!n) - return log_oom(); + r = unit_name_from_path_instance("container-getty", tty, ".service", &n); + if (r < 0) + return log_error_errno(r, "Failed to generate service name: %m"); return add_symlink("container-getty@.service", n); } diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index 96a8447efd..b192526186 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -49,14 +49,15 @@ static bool arg_root_rw = false; static int add_swap(const char *path) { _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL; _cleanup_fclose_ FILE *f = NULL; + int r; assert(path); log_debug("Adding swap: %s", path); - name = unit_name_from_path(path, ".swap"); - if (!name) - return log_oom(); + r = unit_name_from_path(path, ".swap", &name); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); unit = strjoin(arg_dest, "/", name, NULL); if (!unit) @@ -100,17 +101,17 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi assert(what); assert(device); - d = unit_name_from_path(what, ".device"); - if (!d) - return log_oom(); + r = unit_name_from_path(what, ".device", &d); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); e = unit_name_escape(id); if (!e) return log_oom(); - n = unit_name_build("systemd-cryptsetup", e, ".service"); - if (!n) - return log_oom(); + r = unit_name_build("systemd-cryptsetup", e, ".service", &n); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); p = strjoin(arg_dest, "/", n, NULL); if (!p) @@ -224,9 +225,9 @@ static int add_mount( fstype = NULL; } - unit = unit_name_from_path(where, ".mount"); - if (!unit) - return log_oom(); + r = unit_name_from_path(where, ".mount", &unit); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); p = strjoin(arg_dest, "/", unit, NULL); if (!p) diff --git a/src/hibernate-resume/hibernate-resume-generator.c b/src/hibernate-resume/hibernate-resume-generator.c index 0207346b28..9fb6233336 100644 --- a/src/hibernate-resume/hibernate-resume-generator.c +++ b/src/hibernate-resume/hibernate-resume-generator.c @@ -32,6 +32,7 @@ static const char *arg_dest = "/tmp"; static char *arg_resume_dev = NULL; static int parse_proc_cmdline_item(const char *key, const char *value) { + if (streq(key, "resume") && value) { free(arg_resume_dev); arg_resume_dev = fstab_node_to_udev_node(value); @@ -44,13 +45,14 @@ static int parse_proc_cmdline_item(const char *key, const char *value) { static int process_resume(void) { _cleanup_free_ char *name = NULL, *lnk = NULL; + int r; if (!arg_resume_dev) return 0; - name = unit_name_from_path_instance("systemd-hibernate-resume", arg_resume_dev, ".service"); - if (!name) - return log_oom(); + r = unit_name_from_path_instance("systemd-hibernate-resume", arg_resume_dev, ".service", &name); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); lnk = strjoin(arg_dest, "/" SPECIAL_SYSINIT_TARGET ".wants/", name, NULL); if (!lnk) diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 666aa20480..627e43ba35 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -1135,9 +1135,9 @@ static int add_units(sd_journal *j) { STRV_FOREACH(i, arg_system_units) { _cleanup_free_ char *u = NULL; - u = unit_name_mangle(*i, MANGLE_GLOB); - if (!u) - return log_oom(); + r = unit_name_mangle(*i, UNIT_NAME_GLOB, &u); + if (r < 0) + return r; if (string_is_glob(u)) { r = strv_push(&patterns, u); @@ -1181,9 +1181,9 @@ static int add_units(sd_journal *j) { STRV_FOREACH(i, arg_user_units) { _cleanup_free_ char *u = NULL; - u = unit_name_mangle(*i, MANGLE_GLOB); - if (!u) - return log_oom(); + r = unit_name_mangle(*i, UNIT_NAME_GLOB, &u); + if (r < 0) + return r; if (string_is_glob(u)) { r = strv_push(&patterns, u); @@ -1840,9 +1840,8 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; r = add_units(j); - strv_free(arg_system_units); - strv_free(arg_user_units); - + arg_system_units = strv_free(arg_system_units); + arg_user_units = strv_free(arg_user_units); if (r < 0) { log_error_errno(r, "Failed to add filter for units: %m"); return EXIT_FAILURE; diff --git a/src/login/logind-user.c b/src/login/logind-user.c index b00e9b17dc..71bff96728 100644 --- a/src/login/logind-user.c +++ b/src/login/logind-user.c @@ -378,7 +378,7 @@ static int user_start_slice(User *u) { char lu[DECIMAL_STR_MAX(uid_t) + 1], *slice; sprintf(lu, UID_FMT, u->uid); - r = build_subslice(SPECIAL_USER_SLICE, lu, &slice); + r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &slice); if (r < 0) return r; @@ -411,9 +411,9 @@ static int user_start_service(User *u) { char lu[DECIMAL_STR_MAX(uid_t) + 1], *service; sprintf(lu, UID_FMT, u->uid); - service = unit_name_build("user", lu, ".service"); - if (!service) - return log_oom(); + r = unit_name_build("user", lu, ".service", &service); + if (r < 0) + return log_error_errno(r, "Failed to build service name: %m"); r = manager_start_unit(u->manager, service, &error, &job); if (r < 0) { diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index ed7221800f..9931e4f1fd 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -1340,6 +1340,29 @@ static int read_only_image(int argc, char *argv[], void *userdata) { return 0; } +static int make_service_name(const char *name, char **ret) { + _cleanup_free_ char *e = NULL; + int r; + + assert(name); + assert(ret); + + if (!machine_name_is_valid(name)) { + log_error("Invalid machine name %s.", name); + return -EINVAL; + } + + e = unit_name_escape(name); + if (!e) + return log_oom(); + + r = unit_name_build("systemd-nspawn", e, ".service", ret); + if (r < 0) + return log_error_errno(r, "Failed to build unit name: %m"); + + return 0; +} + static int start_machine(int argc, char *argv[], void *userdata) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL; @@ -1356,21 +1379,12 @@ static int start_machine(int argc, char *argv[], void *userdata) { for (i = 1; i < argc; i++) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_free_ char *e = NULL, *unit = NULL; + _cleanup_free_ char *unit = NULL; const char *object; - if (!machine_name_is_valid(argv[i])) { - log_error("Invalid machine name %s.", argv[i]); - return -EINVAL; - } - - e = unit_name_escape(argv[i]); - if (!e) - return log_oom(); - - unit = unit_name_build("systemd-nspawn", e, ".service"); - if (!unit) - return log_oom(); + r = make_service_name(argv[i], &unit); + if (r < 0) + return r; r = sd_bus_call_method( bus, @@ -1433,18 +1447,9 @@ static int enable_machine(int argc, char *argv[], void *userdata) { for (i = 1; i < argc; i++) { _cleanup_free_ char *e = NULL, *unit = NULL; - if (!machine_name_is_valid(argv[i])) { - log_error("Invalid machine name %s.", argv[i]); - return -EINVAL; - } - - e = unit_name_escape(argv[i]); - if (!e) - return log_oom(); - - unit = unit_name_build("systemd-nspawn", e, ".service"); - if (!unit) - return log_oom(); + r = make_service_name(argv[i], &unit); + if (r < 0) + return r; r = sd_bus_message_append(m, "s", unit); if (r < 0) diff --git a/src/run/run.c b/src/run/run.c index db15ab6219..fcd6b06f7d 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -414,9 +414,9 @@ static int transient_cgroup_set_properties(sd_bus_message *m) { if (!isempty(arg_slice)) { _cleanup_free_ char *slice; - slice = unit_name_mangle_with_suffix(arg_slice, MANGLE_NOGLOB, ".slice"); - if (!slice) - return -ENOMEM; + r = unit_name_mangle_with_suffix(arg_slice, UNIT_NAME_NOGLOB, ".slice", &slice); + if (r < 0) + return r; r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice); if (r < 0) @@ -728,9 +728,9 @@ static int start_transient_service( } if (arg_unit) { - service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service"); - if (!service) - return log_oom(); + r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".service", &service); + if (r < 0) + return log_error_errno(r, "Failed to mangle unit name: %m"); } else if (asprintf(&service, "run-"PID_FMT".service", getpid()) < 0) return log_oom(); @@ -846,9 +846,9 @@ static int start_transient_scope( return log_oom(); if (arg_unit) { - scope = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".scope"); - if (!scope) - return log_oom(); + r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".scope", &scope); + if (r < 0) + return log_error_errno(r, "Failed to mangle scope name: %m"); } else if (asprintf(&scope, "run-"PID_FMT".scope", getpid()) < 0) return log_oom(); @@ -980,16 +980,16 @@ static int start_transient_timer( return log_oom(); if (arg_unit) { - switch(unit_name_to_type(arg_unit)) { + switch (unit_name_to_type(arg_unit)) { case UNIT_SERVICE: service = strdup(arg_unit); if (!service) return log_oom(); - timer = unit_name_change_suffix(service, ".timer"); - if (!timer) - return log_oom(); + r = unit_name_change_suffix(service, ".timer", &timer); + if (r < 0) + return log_error_errno(r, "Failed to change unit suffix: %m"); break; case UNIT_TIMER: @@ -997,19 +997,19 @@ static int start_transient_timer( if (!timer) return log_oom(); - service = unit_name_change_suffix(timer, ".service"); - if (!service) - return log_oom(); + r = unit_name_change_suffix(timer, ".service", &service); + if (r < 0) + return log_error_errno(r, "Failed to change unit suffix: %m"); break; default: - service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service"); - if (!service) - return log_oom(); + r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".service", &service); + if (r < 0) + return log_error_errno(r, "Failed to mangle unit name: %m"); - timer = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".timer"); - if (!timer) - return log_oom(); + r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".timer", &timer); + if (r < 0) + return log_error_errno(r, "Failed to mangle unit name: %m"); break; } diff --git a/src/shared/cgroup-util.c b/src/shared/cgroup-util.c index b5e4094f4e..c0b0ca4cf2 100644 --- a/src/shared/cgroup-util.c +++ b/src/shared/cgroup-util.c @@ -1154,7 +1154,7 @@ int cg_path_decode_unit(const char *cgroup, char **unit){ c = strndupa(cgroup, n); c = cg_unescape(c); - if (!unit_name_is_valid(c, TEMPLATE_INVALID)) + if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) return -ENXIO; s = strdup(c); @@ -1181,7 +1181,7 @@ static bool valid_slice_name(const char *p, size_t n) { c = cg_unescape(buf); - return unit_name_is_valid(c, TEMPLATE_INVALID); + return unit_name_is_valid(c, UNIT_NAME_PLAIN); } return false; @@ -1638,6 +1638,7 @@ bool cg_controller_is_valid(const char *p, bool allow_named) { int cg_slice_to_path(const char *unit, char **ret) { _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL; const char *dash; + int r; assert(unit); assert(ret); @@ -1652,15 +1653,15 @@ int cg_slice_to_path(const char *unit, char **ret) { return 0; } - if (!unit_name_is_valid(unit, TEMPLATE_INVALID)) + if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN)) return -EINVAL; if (!endswith(unit, ".slice")) return -EINVAL; - p = unit_name_to_prefix(unit); - if (!p) - return -ENOMEM; + r = unit_name_to_prefix(unit, &p); + if (r < 0) + return r; dash = strchr(p, '-'); @@ -1677,7 +1678,7 @@ int cg_slice_to_path(const char *unit, char **ret) { return -EINVAL; strcpy(stpncpy(n, p, dash - p), ".slice"); - if (!unit_name_is_valid(n, TEMPLATE_INVALID)) + if (!unit_name_is_valid(n, UNIT_NAME_PLAIN)) return -EINVAL; escaped = cg_escape(n); diff --git a/src/shared/dropin.c b/src/shared/dropin.c index d1baad6192..963d05d32e 100644 --- a/src/shared/dropin.c +++ b/src/shared/dropin.c @@ -164,7 +164,7 @@ static int iterate_dir( } int unit_file_process_dir( - Set * unit_path_cache, + Set *unit_path_cache, const char *unit_path, const char *name, const char *suffix, @@ -174,6 +174,7 @@ int unit_file_process_dir( char ***strv) { _cleanup_free_ char *path = NULL; + int r; assert(unit_path); assert(name); @@ -184,22 +185,22 @@ int unit_file_process_dir( return log_oom(); if (!unit_path_cache || set_get(unit_path_cache, path)) - iterate_dir(path, dependency, consumer, arg, strv); + (void) iterate_dir(path, dependency, consumer, arg, strv); - if (unit_name_is_instance(name)) { + if (unit_name_is_valid(name, UNIT_NAME_INSTANCE)) { _cleanup_free_ char *template = NULL, *p = NULL; /* Also try the template dir */ - template = unit_name_template(name); - if (!template) - return log_oom(); + r = unit_name_template(name, &template); + if (r < 0) + return log_error_errno(r, "Failed to generate template from unit name: %m"); p = strjoin(unit_path, "/", template, suffix, NULL); if (!p) return log_oom(); if (!unit_path_cache || set_get(unit_path_cache, p)) - iterate_dir(p, dependency, consumer, arg, strv); + (void) iterate_dir(p, dependency, consumer, arg, strv); } return 0; diff --git a/src/shared/generator.c b/src/shared/generator.c index 7b2f846175..2dc34bf738 100644 --- a/src/shared/generator.c +++ b/src/shared/generator.c @@ -37,6 +37,8 @@ int generator_write_fsck_deps( const char *where, const char *fstype) { + int r; + assert(f); assert(dir); assert(what); @@ -48,7 +50,6 @@ int generator_write_fsck_deps( } if (!isempty(fstype) && !streq(fstype, "auto")) { - int r; r = fsck_exists(fstype); if (r == -ENOENT) { /* treat missing check as essentially OK */ @@ -70,9 +71,9 @@ int generator_write_fsck_deps( } else { _cleanup_free_ char *fsck = NULL; - fsck = unit_name_from_path_instance("systemd-fsck", what, ".service"); - if (!fsck) - return log_oom(); + r = unit_name_from_path_instance("systemd-fsck", what, ".service", &fsck); + if (r < 0) + return log_error_errno(r, "Failed to create fsck service name: %m"); fprintf(f, "RequiresOverridable=%1$s\n" @@ -106,8 +107,7 @@ int generator_write_timeouts( r = parse_sec(timeout, &u); if (r < 0) { - log_warning("Failed to parse timeout for %s, ignoring: %s", - where, timeout); + log_warning("Failed to parse timeout for %s, ignoring: %s", where, timeout); return 0; } @@ -115,9 +115,9 @@ int generator_write_timeouts( if (!node) return log_oom(); - unit = unit_name_from_path(node, ".device"); - if (!unit) - return log_oom(); + r = unit_name_from_path(node, ".device", &unit); + if (r < 0) + return log_error_errno(r, "Failed to make unit name from path: %m"); return write_drop_in_format(dir, unit, 50, "device-timeout", "# Automatically generated by %s\n\n" diff --git a/src/shared/install-printf.c b/src/shared/install-printf.c index 9996649424..7c25d36931 100644 --- a/src/shared/install-printf.c +++ b/src/shared/install-printf.c @@ -29,30 +29,18 @@ static int specifier_prefix_and_instance(char specifier, void *data, void *userdata, char **ret) { InstallInfo *i = userdata; - char *n; assert(i); - n = unit_name_to_prefix_and_instance(i->name); - if (!n) - return -ENOMEM; - - *ret = n; - return 0; + return unit_name_to_prefix_and_instance(i->name, ret); } static int specifier_prefix(char specifier, void *data, void *userdata, char **ret) { InstallInfo *i = userdata; - char *n; assert(i); - n = unit_name_to_prefix(i->name); - if (!n) - return -ENOMEM; - - *ret = n; - return 0; + return unit_name_to_prefix(i->name, ret); } static int specifier_instance(char specifier, void *data, void *userdata, char **ret) { diff --git a/src/shared/install.c b/src/shared/install.c index b121018e97..9648c641d7 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -258,10 +258,10 @@ static int remove_marked_symlinks_fd( int q; bool found; - if (!unit_name_is_valid(de->d_name, TEMPLATE_VALID)) + if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) continue; - if (unit_name_is_instance(de->d_name) && + if (unit_name_is_valid(de->d_name, UNIT_NAME_INSTANCE) && instance_whitelist && !strv_contains(instance_whitelist, de->d_name)) { @@ -272,9 +272,9 @@ static int remove_marked_symlinks_fd( * the template of it might be * listed. */ - w = unit_name_template(de->d_name); - if (!w) - return -ENOMEM; + r = unit_name_template(de->d_name, &w); + if (r < 0) + return r; if (!strv_contains(instance_whitelist, w)) continue; @@ -583,7 +583,7 @@ int unit_file_mask( STRV_FOREACH(i, files) { _cleanup_free_ char *path = NULL; - if (!unit_name_is_valid(*i, TEMPLATE_VALID)) { + if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) { if (r == 0) r = -EINVAL; continue; @@ -646,7 +646,7 @@ int unit_file_unmask( STRV_FOREACH(i, files) { _cleanup_free_ char *path = NULL; - if (!unit_name_is_valid(*i, TEMPLATE_VALID)) { + if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) { if (r == 0) r = -EINVAL; continue; @@ -717,7 +717,7 @@ int unit_file_link( fn = basename(*i); if (!path_is_absolute(*i) || - !unit_name_is_valid(fn, TEMPLATE_VALID)) { + !unit_name_is_valid(fn, UNIT_NAME_ANY)) { if (r == 0) r = -EINVAL; continue; @@ -856,7 +856,7 @@ static int install_info_add( if (!name) name = basename(path); - if (!unit_name_is_valid(name, TEMPLATE_VALID)) + if (!unit_name_is_valid(name, UNIT_NAME_ANY)) return -EINVAL; if (ordered_hashmap_get(c->have_installed, name) || @@ -1118,7 +1118,7 @@ static int unit_file_search( return r; } - if (unit_name_is_instance(info->name)) { + if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) { /* Unit file doesn't exist, however instance * enablement was requested. We will check if it is @@ -1126,9 +1126,9 @@ static int unit_file_search( _cleanup_free_ char *template = NULL; - template = unit_name_template(info->name); - if (!template) - return -ENOMEM; + r = unit_name_template(info->name, &template); + if (r < 0) + return r; STRV_FOREACH(p, paths->unit_path) { _cleanup_free_ char *path = NULL; @@ -1274,7 +1274,7 @@ static int install_info_symlink_wants( assert(i); assert(config_path); - if (unit_name_is_template(i->name)) { + if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) { /* Don't install any symlink if there's no default * instance configured */ @@ -1282,9 +1282,9 @@ static int install_info_symlink_wants( if (!i->default_instance) return 0; - buf = unit_name_replace_instance(i->name, i->default_instance); - if (!buf) - return -ENOMEM; + r = unit_name_replace_instance(i->name, i->default_instance, &buf); + if (r < 0) + return r; n = buf; } else @@ -1297,7 +1297,7 @@ static int install_info_symlink_wants( if (q < 0) return q; - if (!unit_name_is_valid(dst, TEMPLATE_VALID)) { + if (!unit_name_is_valid(dst, UNIT_NAME_ANY)) { r = -EINVAL; continue; } @@ -1462,13 +1462,13 @@ static int install_context_mark_for_removal( } else if (r >= 0) r += q; - if (unit_name_is_instance(i->name)) { + if (unit_name_is_valid(i->name, UNIT_NAME_INSTANCE)) { char *unit_file; if (i->path) { unit_file = basename(i->path); - if (unit_name_is_instance(unit_file)) + if (unit_name_is_valid(unit_file, UNIT_NAME_INSTANCE)) /* unit file named as instance exists, thus all symlinks * pointing to it will be removed */ q = mark_symlink_for_removal(remove_symlinks_to, i->name); @@ -1480,9 +1480,9 @@ static int install_context_mark_for_removal( /* If i->path is not set, it means that we didn't actually find * the unit file. But we can still remove symlinks to the * nonexistent template. */ - unit_file = unit_name_template(i->name); - if (!unit_file) - return log_oom(); + r = unit_name_template(i->name, &unit_file); + if (r < 0) + return r; q = mark_symlink_for_removal(remove_symlinks_to, unit_file); free(unit_file); @@ -1797,7 +1797,7 @@ UnitFileState unit_file_lookup_state( assert(paths); - if (!unit_name_is_valid(name, TEMPLATE_VALID)) + if (!unit_name_is_valid(name, UNIT_NAME_ANY)) return -EINVAL; STRV_FOREACH(i, paths->unit_path) { @@ -1824,7 +1824,7 @@ UnitFileState unit_file_lookup_state( if (errno != ENOENT) return r; - if (!unit_name_is_instance(name)) + if (!unit_name_is_valid(name, UNIT_NAME_INSTANCE)) continue; } else { if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) @@ -1834,8 +1834,7 @@ UnitFileState unit_file_lookup_state( if (r < 0 && r != -ENOENT) return r; else if (r > 0) { - state = path_startswith(*i, "/run") ? - UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; + state = path_startswith(*i, "/run") ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; return state; } } @@ -1995,7 +1994,7 @@ int unit_file_preset( STRV_FOREACH(i, files) { - if (!unit_name_is_valid(*i, TEMPLATE_VALID)) + if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) return -EINVAL; r = unit_file_query_preset(scope, root_dir, *i); @@ -2091,7 +2090,7 @@ int unit_file_preset_all( if (hidden_file(de->d_name)) continue; - if (!unit_name_is_valid(de->d_name, TEMPLATE_VALID)) + if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) continue; dirent_ensure_type(d, de); @@ -2203,7 +2202,7 @@ int unit_file_get_list( if (hidden_file(de->d_name)) continue; - if (!unit_name_is_valid(de->d_name, TEMPLATE_VALID)) + if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) continue; if (hashmap_get(h, de->d_name)) diff --git a/src/shared/unit-name.c b/src/shared/unit-name.c index 86d6ce3df1..c41d7d86a7 100644 --- a/src/shared/unit-name.c +++ b/src/shared/unit-name.c @@ -33,45 +33,13 @@ DIGITS LETTERS \ ":-_.\\" -static const char* const unit_type_table[_UNIT_TYPE_MAX] = { - [UNIT_SERVICE] = "service", - [UNIT_SOCKET] = "socket", - [UNIT_BUSNAME] = "busname", - [UNIT_TARGET] = "target", - [UNIT_SNAPSHOT] = "snapshot", - [UNIT_DEVICE] = "device", - [UNIT_MOUNT] = "mount", - [UNIT_AUTOMOUNT] = "automount", - [UNIT_SWAP] = "swap", - [UNIT_TIMER] = "timer", - [UNIT_PATH] = "path", - [UNIT_SLICE] = "slice", - [UNIT_SCOPE] = "scope" -}; - -DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType); - -static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = { - [UNIT_STUB] = "stub", - [UNIT_LOADED] = "loaded", - [UNIT_NOT_FOUND] = "not-found", - [UNIT_ERROR] = "error", - [UNIT_MERGED] = "merged", - [UNIT_MASKED] = "masked" -}; - -DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState); - -bool unit_name_is_valid(const char *n, enum template_valid template_ok) { +bool unit_name_is_valid(const char *n, UnitNameFlags flags) { const char *e, *i, *at; - /* Valid formats: - * - * string@instance.suffix - * string.suffix - */ + assert((flags & ~(UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) == 0); - assert(IN_SET(template_ok, TEMPLATE_VALID, TEMPLATE_INVALID)); + if (_unlikely_(flags == 0)) + return false; if (isempty(n)) return false; @@ -95,15 +63,32 @@ bool unit_name_is_valid(const char *n, enum template_valid template_ok) { return false; } - if (at) { - if (at == n) - return false; + if (at == n) + return false; - if (template_ok != TEMPLATE_VALID && at+1 == e) - return false; - } + if (flags & UNIT_NAME_PLAIN) + if (!at) + return true; - return true; + if (flags & UNIT_NAME_INSTANCE) + if (at && e > at + 1) + return true; + + if (flags & UNIT_NAME_TEMPLATE) + if (at && e == at + 1) + return true; + + return false; +} + +bool unit_prefix_is_valid(const char *p) { + + /* We don't allow additional @ in the prefix string */ + + if (isempty(p)) + return false; + + return in_charset(p, VALID_CHARS); } bool unit_instance_is_valid(const char *i) { @@ -120,14 +105,41 @@ bool unit_instance_is_valid(const char *i) { return in_charset(i, "@" VALID_CHARS); } -bool unit_prefix_is_valid(const char *p) { - - /* We don't allow additional @ in the instance string */ - - if (isempty(p)) +bool unit_suffix_is_valid(const char *s) { + if (isempty(s)) return false; - return in_charset(p, VALID_CHARS); + if (s[0] != '.') + return false; + + if (unit_type_from_string(s + 1) < 0) + return false; + + return true; +} + +int unit_name_to_prefix(const char *n, char **ret) { + const char *p; + char *s; + + assert(n); + assert(ret); + + if (!unit_name_is_valid(n, UNIT_NAME_ANY)) + return -EINVAL; + + p = strchr(n, '@'); + if (!p) + p = strrchr(n, '.'); + + assert_se(p); + + s = strndup(n, p - n); + if (!s) + return -ENOMEM; + + *ret = s; + return 0; } int unit_name_to_instance(const char *n, char **instance) { @@ -137,6 +149,9 @@ int unit_name_to_instance(const char *n, char **instance) { assert(n); assert(instance); + if (!unit_name_is_valid(n, UNIT_NAME_ANY)) + return -EINVAL; + /* Everything past the first @ and before the last . is the instance */ p = strchr(n, '@'); if (!p) { @@ -144,13 +159,13 @@ int unit_name_to_instance(const char *n, char **instance) { return 0; } - d = strrchr(n, '.'); + p++; + + d = strrchr(p, '.'); if (!d) return -EINVAL; - if (d < p) - return -EINVAL; - i = strndup(p+1, d-p-1); + i = strndup(p, d-p); if (!i) return -ENOMEM; @@ -158,55 +173,95 @@ int unit_name_to_instance(const char *n, char **instance) { return 1; } -char *unit_name_to_prefix_and_instance(const char *n) { +int unit_name_to_prefix_and_instance(const char *n, char **ret) { const char *d; + char *s; + + assert(n); + assert(ret); + + if (!unit_name_is_valid(n, UNIT_NAME_ANY)) + return -EINVAL; + + d = strrchr(n, '.'); + if (!d) + return -EINVAL; + + s = strndup(n, d - n); + if (!s) + return -ENOMEM; + + *ret = s; + return 0; +} + +UnitType unit_name_to_type(const char *n) { + const char *e; assert(n); - assert_se(d = strrchr(n, '.')); - return strndup(n, d - n); + if (!unit_name_is_valid(n, UNIT_NAME_ANY)) + return _UNIT_TYPE_INVALID; + + assert_se(e = strrchr(n, '.')); + + return unit_type_from_string(e + 1); } -char *unit_name_to_prefix(const char *n) { - const char *p; - - assert(n); - - p = strchr(n, '@'); - if (p) - return strndup(n, p - n); - - return unit_name_to_prefix_and_instance(n); -} - -char *unit_name_change_suffix(const char *n, const char *suffix) { - char *e, *r; +int unit_name_change_suffix(const char *n, const char *suffix, char **ret) { + char *e, *s; size_t a, b; assert(n); assert(suffix); - assert(suffix[0] == '.'); + assert(ret); + + if (!unit_name_is_valid(n, UNIT_NAME_ANY)) + return -EINVAL; + + if (!unit_suffix_is_valid(suffix)) + return -EINVAL; assert_se(e = strrchr(n, '.')); + a = e - n; b = strlen(suffix); - r = new(char, a + b + 1); - if (!r) - return NULL; + s = new(char, a + b + 1); + if (!s) + return -ENOMEM; - strcpy(mempcpy(r, n, a), suffix); - return r; + strcpy(mempcpy(s, n, a), suffix); + *ret = s; + + return 0; } -char *unit_name_build(const char *prefix, const char *instance, const char *suffix) { +int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret) { + char *s; + assert(prefix); assert(suffix); + assert(ret); + + if (!unit_prefix_is_valid(prefix)) + return -EINVAL; + + if (instance && !unit_instance_is_valid(instance)) + return -EINVAL; + + if (!unit_suffix_is_valid(suffix)) + return -EINVAL; if (!instance) - return strappend(prefix, suffix); + s = strappend(prefix, suffix); + else + s = strjoin(prefix, "@", instance, suffix, NULL); + if (!s) + return -ENOMEM; - return strjoin(prefix, "@", instance, suffix, NULL); + *ret = s; + return 0; } static char *do_escape_char(char c, char *t) { @@ -242,30 +297,6 @@ static char *do_escape(const char *f, char *t) { return t; } -static char *do_escape_mangle(const char *f, enum unit_name_mangle allow_globs, char *t) { - const char *valid_chars; - - assert(f); - assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB)); - assert(t); - - /* We'll only escape the obvious characters here, to play - * safe. */ - - valid_chars = allow_globs == MANGLE_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS; - - for (; *f; f++) { - if (*f == '/') - *(t++) = '-'; - else if (!strchr(valid_chars, *f)) - t = do_escape_char(*f, t); - else - *(t++) = *f; - } - - return t; -} - char *unit_name_escape(const char *f) { char *r, *t; @@ -281,14 +312,15 @@ char *unit_name_escape(const char *f) { return r; } -char *unit_name_unescape(const char *f) { - char *r, *t; +int unit_name_unescape(const char *f, char **ret) { + _cleanup_free_ char *r = NULL; + char *t; assert(f); r = strdup(f); if (!r) - return NULL; + return -ENOMEM; for (t = r; *f; f++) { if (*f == '-') @@ -296,180 +328,228 @@ char *unit_name_unescape(const char *f) { else if (*f == '\\') { int a, b; - if (f[1] != 'x' || - (a = unhexchar(f[2])) < 0 || - (b = unhexchar(f[3])) < 0) { - /* Invalid escape code, let's take it literal then */ - *(t++) = '\\'; - } else { - *(t++) = (char) ((a << 4) | b); - f += 3; - } + if (f[1] != 'x') + return -EINVAL; + + a = unhexchar(f[2]); + if (a < 0) + return -EINVAL; + + b = unhexchar(f[3]); + if (b < 0) + return -EINVAL; + + *(t++) = (char) ((a << 4) | b); + f += 3; } else *(t++) = *f; } *t = 0; - return r; + *ret = r; + r = NULL; + + return 0; } -char *unit_name_path_escape(const char *f) { - _cleanup_free_ char *p = NULL; +int unit_name_path_escape(const char *f, char **ret) { + char *p, *s; assert(f); + assert(ret); - p = strdup(f); + p = strdupa(f); if (!p) - return NULL; + return -ENOMEM; path_kill_slashes(p); if (STR_IN_SET(p, "/", "")) - return strdup("-"); + s = strdup("-"); + else { + char *e; - return unit_name_escape(p[0] == '/' ? p + 1 : p); + if (!path_is_safe(p)) + return -EINVAL; + + /* Truncate trailing slashes */ + e = endswith(p, "/"); + if (e) + *e = 0; + + /* Truncate leading slashes */ + if (p[0] == '/') + p++; + + s = unit_name_escape(p); + } + if (!s) + return -ENOMEM; + + *ret = s; + return 0; } -char *unit_name_path_unescape(const char *f) { - char *e, *w; +int unit_name_path_unescape(const char *f, char **ret) { + char *s, *w; + int r; assert(f); - e = unit_name_unescape(f); - if (!e) - return NULL; + if (streq(f, "-")) { + s = strdup("/"); + if (!s) + return -ENOMEM; - if (e[0] != '/') { - w = strappend("/", e); - free(e); - return w; + *ret = s; + return 0; } - return e; + r = unit_name_unescape(f, &s); + if (r < 0) + return r; + + /* Don't accept trailing or leading slashes */ + if (startswith(s, "/") || endswith(s, "/")) { + free(s); + return -EINVAL; + } + + /* Prefix a slash again */ + w = strappend("/", s); + free(s); + if (!w) + return -ENOMEM; + + if (!path_is_safe(w)) { + free(w); + return -EINVAL; + } + + *ret = w; + return 0; } -bool unit_name_is_template(const char *n) { +int unit_name_replace_instance(const char *f, const char *i, char **ret) { const char *p, *e; - - assert(n); - - p = strchr(n, '@'); - if (!p) - return false; - - e = strrchr(p+1, '.'); - if (!e) - return false; - - return e == p + 1; -} - -bool unit_name_is_instance(const char *n) { - const char *p, *e; - - assert(n); - - p = strchr(n, '@'); - if (!p) - return false; - - e = strrchr(p+1, '.'); - if (!e) - return false; - - return e > p + 1; -} - -char *unit_name_replace_instance(const char *f, const char *i) { - const char *p, *e; - char *r; + char *s; size_t a, b; assert(f); assert(i); + assert(ret); - p = strchr(f, '@'); - if (!p) - return strdup(f); + if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) + return -EINVAL; + if (!unit_instance_is_valid(i)) + return -EINVAL; - e = strrchr(f, '.'); - if (!e) - e = strchr(f, 0); + assert_se(p = strchr(f, '@')); + assert_se(e = strrchr(f, '.')); a = p - f; b = strlen(i); - r = new(char, a + 1 + b + strlen(e) + 1); - if (!r) - return NULL; + s = new(char, a + 1 + b + strlen(e) + 1); + if (!s) + return -ENOMEM; - strcpy(mempcpy(mempcpy(r, f, a + 1), i, b), e); - return r; + strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e); + + *ret = s; + return 0; } -char *unit_name_template(const char *f) { +int unit_name_template(const char *f, char **ret) { const char *p, *e; - char *r; + char *s; size_t a; assert(f); + assert(ret); - p = strchr(f, '@'); - if (!p) - return strdup(f); + if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) + return -EINVAL; - e = strrchr(f, '.'); - if (!e) - e = strchr(f, 0); + assert_se(p = strchr(f, '@')); + assert_se(e = strrchr(f, '.')); a = p - f; - r = new(char, a + 1 + strlen(e) + 1); - if (!r) - return NULL; + s = new(char, a + 1 + strlen(e) + 1); + if (!s) + return -ENOMEM; - strcpy(mempcpy(r, f, a + 1), e); - return r; + strcpy(mempcpy(s, f, a + 1), e); + + *ret = s; + return 0; } -char *unit_name_from_path(const char *path, const char *suffix) { +int unit_name_from_path(const char *path, const char *suffix, char **ret) { _cleanup_free_ char *p = NULL; + char *s = NULL; + int r; assert(path); assert(suffix); + assert(ret); - p = unit_name_path_escape(path); - if (!p) - return NULL; + if (!unit_suffix_is_valid(suffix)) + return -EINVAL; - return strappend(p, suffix); + r = unit_name_path_escape(path, &p); + if (r < 0) + return r; + + s = strappend(p, suffix); + if (!s) + return -ENOMEM; + + *ret = s; + return 0; } -char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) { +int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) { _cleanup_free_ char *p = NULL; + char *s; + int r; assert(prefix); assert(path); assert(suffix); + assert(ret); - p = unit_name_path_escape(path); - if (!p) - return NULL; + if (!unit_prefix_is_valid(prefix)) + return -EINVAL; - return strjoin(prefix, "@", p, suffix, NULL); + if (!unit_suffix_is_valid(suffix)) + return -EINVAL; + + r = unit_name_path_escape(path, &p); + if (r < 0) + return r; + + s = strjoin(prefix, "@", p, suffix, NULL); + if (!s) + return -ENOMEM; + + *ret = s; + return 0; } -char *unit_name_to_path(const char *name) { - _cleanup_free_ char *w = NULL; +int unit_name_to_path(const char *name, char **ret) { + _cleanup_free_ char *prefix = NULL; + int r; assert(name); - w = unit_name_to_prefix(name); - if (!w) - return NULL; + r = unit_name_to_prefix(name, &prefix); + if (r < 0) + return r; - return unit_name_path_unescape(w); + return unit_name_path_unescape(prefix, ret); } char *unit_dbus_path_from_name(const char *name) { @@ -500,6 +580,30 @@ int unit_name_from_dbus_path(const char *path, char **name) { return 0; } +static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t) { + const char *valid_chars; + + assert(f); + assert(IN_SET(allow_globs, UNIT_NAME_GLOB, UNIT_NAME_NOGLOB)); + assert(t); + + /* We'll only escape the obvious characters here, to play + * safe. */ + + valid_chars = allow_globs == UNIT_NAME_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS; + + for (; *f; f++) { + if (*f == '/') + *(t++) = '-'; + else if (!strchr(valid_chars, *f)) + t = do_escape_char(*f, t); + else + *(t++) = *f; + } + + return t; +} + /** * Convert a string to a unit name. /dev/blah is converted to dev-blah.device, * /blah/blah is converted to blah-blah.mount, anything else is left alone, @@ -507,54 +611,75 @@ int unit_name_from_dbus_path(const char *path, char **name) { * * If @allow_globs, globs characters are preserved. Otherwise they are escaped. */ -char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow_globs, const char *suffix) { - char *r, *t; +int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret) { + char *s, *t; + int r; assert(name); assert(suffix); - assert(suffix[0] == '.'); + assert(ret); - if (is_device_path(name)) - return unit_name_from_path(name, ".device"); + if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */ + return -EINVAL; - if (path_is_absolute(name)) - return unit_name_from_path(name, ".mount"); + if (!unit_suffix_is_valid(suffix)) + return -EINVAL; - r = new(char, strlen(name) * 4 + strlen(suffix) + 1); - if (!r) - return NULL; + if (unit_name_is_valid(name, UNIT_NAME_ANY)) { + /* No mangling necessary... */ + s = strdup(name); + if (!s) + return -ENOMEM; - t = do_escape_mangle(name, allow_globs, r); + *ret = s; + return 0; + } - if (unit_name_to_type(name) < 0) + if (is_device_path(name)) { + r = unit_name_from_path(name, ".device", ret); + if (r >= 0) + return 1; + if (r != -EINVAL) + return r; + } + + if (path_is_absolute(name)) { + r = unit_name_from_path(name, ".mount", ret); + if (r >= 0) + return 1; + if (r != -EINVAL) + return r; + } + + s = new(char, strlen(name) * 4 + strlen(suffix) + 1); + if (!s) + return -ENOMEM; + + t = do_escape_mangle(name, allow_globs, s); + *t = 0; + + if (unit_name_to_type(s) < 0) strcpy(t, suffix); - else - *t = 0; - return r; + *ret = s; + return 1; } -UnitType unit_name_to_type(const char *n) { - const char *e; - - assert(n); - - e = strrchr(n, '.'); - if (!e) - return _UNIT_TYPE_INVALID; - - return unit_type_from_string(e + 1); -} - -int build_subslice(const char *slice, const char*name, char **subslice) { - char *ret; +int slice_build_subslice(const char *slice, const char*name, char **ret) { + char *subslice; assert(slice); assert(name); - assert(subslice); + assert(ret); + + if (!unit_name_is_valid(slice, UNIT_NAME_PLAIN)) + return -EINVAL; + + if (!unit_prefix_is_valid(name)) + return -EINVAL; if (streq(slice, "-.slice")) - ret = strappend(name, ".slice"); + subslice = strappend(name, ".slice"); else { char *e; @@ -562,17 +687,46 @@ int build_subslice(const char *slice, const char*name, char **subslice) { if (!e) return -EINVAL; - ret = new(char, (e - slice) + 1 + strlen(name) + 6 + 1); - if (!ret) + subslice = new(char, (e - slice) + 1 + strlen(name) + 6 + 1); + if (!subslice) return -ENOMEM; - stpcpy(stpcpy(stpcpy(mempcpy(ret, slice, e - slice), "-"), name), ".slice"); + stpcpy(stpcpy(stpcpy(mempcpy(subslice, slice, e - slice), "-"), name), ".slice"); } - *subslice = ret; + *ret = subslice; return 0; } +static const char* const unit_type_table[_UNIT_TYPE_MAX] = { + [UNIT_SERVICE] = "service", + [UNIT_SOCKET] = "socket", + [UNIT_BUSNAME] = "busname", + [UNIT_TARGET] = "target", + [UNIT_SNAPSHOT] = "snapshot", + [UNIT_DEVICE] = "device", + [UNIT_MOUNT] = "mount", + [UNIT_AUTOMOUNT] = "automount", + [UNIT_SWAP] = "swap", + [UNIT_TIMER] = "timer", + [UNIT_PATH] = "path", + [UNIT_SLICE] = "slice", + [UNIT_SCOPE] = "scope" +}; + +DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType); + +static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = { + [UNIT_STUB] = "stub", + [UNIT_LOADED] = "loaded", + [UNIT_NOT_FOUND] = "not-found", + [UNIT_ERROR] = "error", + [UNIT_MERGED] = "merged", + [UNIT_MASKED] = "masked" +}; + +DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState); + static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = { [UNIT_REQUIRES] = "Requires", [UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable", diff --git a/src/shared/unit-name.h b/src/shared/unit-name.h index 6f139cc4c4..057512cbd3 100644 --- a/src/shared/unit-name.h +++ b/src/shared/unit-name.h @@ -107,61 +107,67 @@ enum UnitDependency { _UNIT_DEPENDENCY_INVALID = -1 }; +typedef enum UnitNameFlags { + UNIT_NAME_PLAIN = 1, /* Allow foo.service */ + UNIT_NAME_INSTANCE = 2, /* Allow foo@bar.service */ + UNIT_NAME_TEMPLATE = 4, /* Allow foo@.service */ + UNIT_NAME_ANY = UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE, +} UnitNameFlags; + +bool unit_name_is_valid(const char *n, UnitNameFlags flags) _pure_; +bool unit_prefix_is_valid(const char *p) _pure_; +bool unit_instance_is_valid(const char *i) _pure_; +bool unit_suffix_is_valid(const char *s) _pure_; + +static inline int unit_prefix_and_instance_is_valid(const char *p) { + /* For prefix+instance and instance the same rules apply */ + return unit_instance_is_valid(p); +} + +int unit_name_to_prefix(const char *n, char **prefix); +int unit_name_to_instance(const char *n, char **instance); +int unit_name_to_prefix_and_instance(const char *n, char **ret); + +UnitType unit_name_to_type(const char *n) _pure_; + +int unit_name_change_suffix(const char *n, const char *suffix, char **ret); + +int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret); + +char *unit_name_escape(const char *f); +int unit_name_unescape(const char *f, char **ret); +int unit_name_path_escape(const char *f, char **ret); +int unit_name_path_unescape(const char *f, char **ret); + +int unit_name_replace_instance(const char *f, const char *i, char **ret); + +int unit_name_template(const char *f, char **ret); + +int unit_name_from_path(const char *path, const char *suffix, char **ret); +int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret); +int unit_name_to_path(const char *name, char **ret); + +char *unit_dbus_path_from_name(const char *name); +int unit_name_from_dbus_path(const char *path, char **name); + +typedef enum UnitNameMangle { + UNIT_NAME_NOGLOB, + UNIT_NAME_GLOB, +} UnitNameMangle; + +int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret); + +static inline int unit_name_mangle(const char *name, UnitNameMangle allow_globs, char **ret) { + return unit_name_mangle_with_suffix(name, allow_globs, ".service", ret); +} + +int slice_build_subslice(const char *slice, const char*name, char **subslice); + const char *unit_type_to_string(UnitType i) _const_; UnitType unit_type_from_string(const char *s) _pure_; const char *unit_load_state_to_string(UnitLoadState i) _const_; UnitLoadState unit_load_state_from_string(const char *s) _pure_; -int unit_name_to_instance(const char *n, char **instance); -char* unit_name_to_prefix(const char *n); -char* unit_name_to_prefix_and_instance(const char *n); - -enum template_valid { - TEMPLATE_INVALID, - TEMPLATE_VALID, -}; - -bool unit_name_is_valid(const char *n, enum template_valid template_ok) _pure_; -bool unit_prefix_is_valid(const char *p) _pure_; -bool unit_instance_is_valid(const char *i) _pure_; - -UnitType unit_name_to_type(const char *n) _pure_; - -char *unit_name_change_suffix(const char *n, const char *suffix); - -char *unit_name_build(const char *prefix, const char *instance, const char *suffix); - -char *unit_name_escape(const char *f); -char *unit_name_unescape(const char *f); -char *unit_name_path_escape(const char *f); -char *unit_name_path_unescape(const char *f); - -bool unit_name_is_template(const char *n) _pure_; -bool unit_name_is_instance(const char *n) _pure_; - -char *unit_name_replace_instance(const char *f, const char *i); - -char *unit_name_template(const char *f); - -char *unit_name_from_path(const char *path, const char *suffix); -char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix); -char *unit_name_to_path(const char *name); - -char *unit_dbus_path_from_name(const char *name); -int unit_name_from_dbus_path(const char *path, char **name); - -enum unit_name_mangle { - MANGLE_NOGLOB, - MANGLE_GLOB, -}; - -char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow_globs, const char *suffix); -static inline char *unit_name_mangle(const char *name, enum unit_name_mangle allow_globs) { - return unit_name_mangle_with_suffix(name, allow_globs, ".service"); -} - -int build_subslice(const char *slice, const char*name, char **subslice); - const char *unit_dependency_to_string(UnitDependency i) _const_; UnitDependency unit_dependency_from_string(const char *s) _pure_; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index eb9737d577..4f36ba8dc3 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -1636,13 +1636,15 @@ static int list_dependencies(sd_bus *bus, char **args) { _cleanup_strv_free_ char **units = NULL; _cleanup_free_ char *unit = NULL; const char *u; + int r; assert(bus); if (args[1]) { - unit = unit_name_mangle(args[1], MANGLE_NOGLOB); - if (!unit) - return log_oom(); + r = unit_name_mangle(args[1], UNIT_NAME_NOGLOB, &unit); + if (r < 0) + return log_error_errno(r, "Failed to mangle unit name: %m"); + u = unit; } else u = SPECIAL_DEFAULT_TARGET; @@ -1938,9 +1940,9 @@ static int set_default(sd_bus *bus, char **args) { unsigned n_changes = 0; int r; - unit = unit_name_mangle_with_suffix(args[1], MANGLE_NOGLOB, ".target"); - if (!unit) - return log_oom(); + r = unit_name_mangle_with_suffix(args[1], UNIT_NAME_NOGLOB, ".target", &unit); + if (r < 0) + return log_error_errno(r, "Failed to mangle unit name: %m"); if (!bus || avoid_bus()) { r = unit_file_set_default(arg_scope, arg_root, unit, true, &changes, &n_changes); @@ -2257,7 +2259,7 @@ static int unit_find_paths( assert(fragment_path); assert(lp); - if (!avoid_bus_cache && !unit_name_is_template(unit_name)) { + if (!avoid_bus_cache && !unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_message_unref_ sd_bus_message *unit_load_error = NULL; _cleanup_free_ char *unit = NULL; @@ -2323,24 +2325,23 @@ static int unit_find_paths( names = set_new(NULL); if (!names) - return -ENOMEM; + return log_oom(); r = set_put(names, unit_name); if (r < 0) - return r; + return log_error_errno(r, "Failed to add unit name: %m"); r = unit_file_find_path(lp, unit_name, &path); if (r < 0) return r; if (r == 0) { - _cleanup_free_ char *template; + _cleanup_free_ char *template = NULL; - template = unit_name_template(unit_name); - if (!template) - return log_oom(); - - if (!streq(template, unit_name)) { + r = unit_name_template(unit_name, &template); + if (r != -EINVAL) + return log_error_errno(r, "Failed to determine template name: %m"); + if (r >= 0) { r = unit_file_find_path(lp, template, &path); if (r < 0) return r; @@ -2382,9 +2383,9 @@ static int check_one_unit(sd_bus *bus, const char *name, const char *good_states assert(name); - n = unit_name_mangle(name, MANGLE_NOGLOB); - if (!n) - return log_oom(); + r = unit_name_mangle(name, UNIT_NAME_NOGLOB, &n); + if (r < 0) + return log_error_errno(r, "Failed to mangle unit name: %m"); /* We don't use unit_dbus_path_from_name() directly since we * don't want to load the unit if it isn't loaded. */ @@ -2439,9 +2440,9 @@ static int check_triggering_units( char **i; int r; - n = unit_name_mangle(name, MANGLE_NOGLOB); - if (!n) - return log_oom(); + r = unit_name_mangle(name, UNIT_NAME_NOGLOB, &n); + if (r < 0) + return log_error_errno(r, "Failed to mangle unit name: %m"); path = unit_dbus_path_from_name(n); if (!path) @@ -2595,17 +2596,17 @@ static int expand_names(sd_bus *bus, char **names, const char* suffix, char ***r _cleanup_strv_free_ char **mangled = NULL, **globs = NULL; char **name; - int r = 0, i; + int r, i; STRV_FOREACH(name, names) { char *t; if (suffix) - t = unit_name_mangle_with_suffix(*name, MANGLE_GLOB, suffix); + r = unit_name_mangle_with_suffix(*name, UNIT_NAME_GLOB, suffix, &t); else - t = unit_name_mangle(*name, MANGLE_GLOB); - if (!t) - return log_oom(); + r = unit_name_mangle(*name, UNIT_NAME_GLOB, &t); + if (r < 0) + return log_error_errno(r, "Failed to mangle name: %m"); if (string_is_glob(t)) r = strv_consume(&globs, t); @@ -4673,9 +4674,9 @@ static int set_property(sd_bus *bus, char **args) { if (r < 0) return bus_log_create_error(r); - n = unit_name_mangle(args[1], MANGLE_NOGLOB); - if (!n) - return log_oom(); + r = unit_name_mangle(args[1], UNIT_NAME_NOGLOB, &n); + if (r < 0) + return log_error_errno(r, "Failed to mangle unit name: %m"); r = sd_bus_message_append(m, "sb", n, arg_runtime); if (r < 0) @@ -4721,12 +4722,15 @@ static int snapshot(sd_bus *bus, char **args) { polkit_agent_open_if_enabled(); - if (strv_length(args) > 1) - n = unit_name_mangle_with_suffix(args[1], MANGLE_NOGLOB, ".snapshot"); - else + if (strv_length(args) > 1) { + r = unit_name_mangle_with_suffix(args[1], UNIT_NAME_NOGLOB, ".snapshot", &n); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + } else { n = strdup(""); - if (!n) - return log_oom(); + if (!n) + return log_oom(); + } r = sd_bus_call_method( bus, @@ -5217,25 +5221,29 @@ static int enable_sysv_units(const char *verb, char **args) { static int mangle_names(char **original_names, char ***mangled_names) { char **i, **l, **name; + int r; - l = new(char*, strv_length(original_names) + 1); + l = i = new(char*, strv_length(original_names) + 1); if (!l) return log_oom(); - i = l; STRV_FOREACH(name, original_names) { /* When enabling units qualified path names are OK, * too, hence allow them explicitly. */ - if (is_path(*name)) + if (is_path(*name)) { *i = strdup(*name); - else - *i = unit_name_mangle(*name, MANGLE_NOGLOB); - - if (!*i) { - strv_free(l); - return log_oom(); + if (!*i) { + strv_free(l); + return log_oom(); + } + } else { + r = unit_name_mangle(*name, UNIT_NAME_NOGLOB, i); + if (r < 0) { + strv_free(l); + return log_error_errno(r, "Failed to mangle unit name: %m"); + } } i++; @@ -5418,9 +5426,9 @@ static int add_dependency(sd_bus *bus, char **args) { if (!args[1]) return 0; - target = unit_name_mangle_with_suffix(args[1], MANGLE_NOGLOB, ".target"); - if (!target) - return log_oom(); + r = unit_name_mangle_with_suffix(args[1], UNIT_NAME_NOGLOB, ".target", &target); + if (r < 0) + return log_error_errno(r, "Failed to mangle unit name: %m"); r = mangle_names(args+2, &names); if (r < 0) diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c index 714ce8f6cb..0c839c2c53 100644 --- a/src/sysv-generator/sysv-generator.c +++ b/src/sysv-generator/sysv-generator.c @@ -310,11 +310,13 @@ static int sysv_translate_facility(const char *name, const char *filename, char * out whether something is a target or a service alias. */ if (*name == '$') { - if (!unit_prefix_is_valid(n)) - return -EINVAL; + int k; /* Facilities starting with $ are most likely targets */ - r = unit_name_build(n, NULL, ".target"); + k = unit_name_build(n, NULL, ".target", &r); + if (k < 0) + return k; + } else if (streq_ptr(n, filename)) /* Names equaling the file name of the services are redundant */ return 0; diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c index 7d882aebd6..5e34f18e31 100644 --- a/src/test/test-unit-name.c +++ b/src/test/test-unit-name.c @@ -33,79 +33,156 @@ #include "specifier.h" #include "util.h" #include "macro.h" +#include "path-util.h" #include "test-helper.h" -static void test_replacements(void) { -#define expect(pattern, repl, expected) \ - { \ - _cleanup_free_ char *t = \ - unit_name_replace_instance(pattern, repl); \ - puts(t); \ - assert_se(streq(t, expected)); \ - } +static void test_unit_name_is_valid(void) { + assert_se(unit_name_is_valid("foo.service", UNIT_NAME_ANY)); + assert_se(unit_name_is_valid("foo.service", UNIT_NAME_PLAIN)); + assert_se(!unit_name_is_valid("foo.service", UNIT_NAME_INSTANCE)); + assert_se(!unit_name_is_valid("foo.service", UNIT_NAME_TEMPLATE)); + assert_se(!unit_name_is_valid("foo.service", UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)); - expect("foo@.service", "waldo", "foo@waldo.service"); - expect("foo@xyz.service", "waldo", "foo@waldo.service"); - expect("xyz", "waldo", "xyz"); - expect("", "waldo", ""); - expect("foo.service", "waldo", "foo.service"); - expect(".service", "waldo", ".service"); - expect("foo@", "waldo", "foo@waldo"); - expect("@bar", "waldo", "@waldo"); + assert_se(unit_name_is_valid("foo@bar.service", UNIT_NAME_ANY)); + assert_se(!unit_name_is_valid("foo@bar.service", UNIT_NAME_PLAIN)); + assert_se(unit_name_is_valid("foo@bar.service", UNIT_NAME_INSTANCE)); + assert_se(!unit_name_is_valid("foo@bar.service", UNIT_NAME_TEMPLATE)); + assert_se(unit_name_is_valid("foo@bar.service", UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)); + assert_se(unit_name_is_valid("foo@.service", UNIT_NAME_ANY)); + assert_se(!unit_name_is_valid("foo@.service", UNIT_NAME_PLAIN)); + assert_se(!unit_name_is_valid("foo@.service", UNIT_NAME_INSTANCE)); + assert_se(unit_name_is_valid("foo@.service", UNIT_NAME_TEMPLATE)); + assert_se(unit_name_is_valid("foo@.service", UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)); + + assert_se(!unit_name_is_valid(".service", UNIT_NAME_ANY)); + assert_se(!unit_name_is_valid("", UNIT_NAME_ANY)); + assert_se(!unit_name_is_valid("foo.waldo", UNIT_NAME_ANY)); + assert_se(!unit_name_is_valid("@.service", UNIT_NAME_ANY)); + assert_se(!unit_name_is_valid("@piep.service", UNIT_NAME_ANY)); +} + +static void test_u_n_r_i_one(const char *pattern, const char *repl, const char *expected, int ret) { + _cleanup_free_ char *t = NULL; + assert_se(unit_name_replace_instance(pattern, repl, &t) == ret); + puts(strna(t)); + assert_se(streq_ptr(t, expected)); +} + +static void test_u_n_r_i(void) { puts("-------------------------------------------------"); -#undef expect -#define expect(path, suffix, expected) \ - { \ - _cleanup_free_ char *k, *t = \ - unit_name_from_path(path, suffix); \ - puts(t); \ - k = unit_name_to_path(t); \ - puts(k); \ - assert_se(streq(k, expected ? expected : path)); \ + test_u_n_r_i_one("foo@.service", "waldo", "foo@waldo.service", 0); + test_u_n_r_i_one("foo@xyz.service", "waldo", "foo@waldo.service", 0); + test_u_n_r_i_one("xyz", "waldo", NULL, -EINVAL); + test_u_n_r_i_one("", "waldo", NULL, -EINVAL); + test_u_n_r_i_one("foo.service", "waldo", NULL, -EINVAL); + test_u_n_r_i_one(".service", "waldo", NULL, -EINVAL); + test_u_n_r_i_one("foo@", "waldo", NULL, -EINVAL); + test_u_n_r_i_one("@bar", "waldo", NULL, -EINVAL); +} + +static void test_u_n_f_p_one(const char *path, const char *suffix, const char *expected, int ret) { + _cleanup_free_ char *t = NULL; + + assert_se(unit_name_from_path(path, suffix, &t) == ret); + puts(strna(t)); + assert_se(streq_ptr(t, expected)); + + if (t) { + _cleanup_free_ char *k = NULL; + assert_se(unit_name_to_path(t, &k) == 0); + puts(strna(k)); + assert_se(path_equal(k, isempty(path) ? "/" : path)); } +} - expect("/waldo", ".mount", NULL); - expect("/waldo/quuix", ".mount", NULL); - expect("/waldo/quuix/", ".mount", "/waldo/quuix"); - expect("/", ".mount", NULL); - expect("///", ".mount", "/"); - +static void test_u_n_f_p(void) { puts("-------------------------------------------------"); -#undef expect -#define expect(pattern, path, suffix, expected) \ - { \ - _cleanup_free_ char *t = \ - unit_name_from_path_instance(pattern, path, suffix); \ - puts(t); \ - assert_se(streq(t, expected)); \ + test_u_n_f_p_one("/waldo", ".mount", "waldo.mount", 0); + test_u_n_f_p_one("/waldo/quuix", ".mount", "waldo-quuix.mount", 0); + test_u_n_f_p_one("/waldo/quuix/", ".mount", "waldo-quuix.mount", 0); + test_u_n_f_p_one("", ".mount", "-.mount", 0); + test_u_n_f_p_one("/", ".mount", "-.mount", 0); + test_u_n_f_p_one("///", ".mount", "-.mount", 0); + test_u_n_f_p_one("/foo/../bar", ".mount", NULL, -EINVAL); + test_u_n_f_p_one("/foo/./bar", ".mount", NULL, -EINVAL); +} + +static void test_u_n_f_p_i_one(const char *pattern, const char *path, const char *suffix, const char *expected, int ret) { + _cleanup_free_ char *t = NULL; + + assert_se(unit_name_from_path_instance(pattern, path, suffix, &t) == ret); + puts(strna(t)); + assert_se(streq_ptr(t, expected)); + + if (t) { + _cleanup_free_ char *k = NULL, *v = NULL; + + assert_se(unit_name_to_instance(t, &k) > 0); + assert_se(unit_name_path_unescape(k, &v) == 0); + assert_se(path_equal(v, isempty(path) ? "/" : path)); } +} - expect("waldo", "/waldo", ".mount", "waldo@waldo.mount"); - expect("waldo", "/waldo////quuix////", ".mount", "waldo@waldo-quuix.mount"); - expect("waldo", "/", ".mount", "waldo@-.mount"); - expect("wa--ldo", "/--", ".mount", "wa--ldo@\\x2d\\x2d.mount"); - +static void test_u_n_f_p_i(void) { puts("-------------------------------------------------"); -#undef expect -#define expect(pattern) \ - { \ - _cleanup_free_ char *k, *t; \ - assert_se(t = unit_name_mangle(pattern, MANGLE_NOGLOB)); \ - assert_se(k = unit_name_mangle(t, MANGLE_NOGLOB)); \ - puts(t); \ - assert_se(streq(t, k)); \ + + test_u_n_f_p_i_one("waldo", "/waldo", ".mount", "waldo@waldo.mount", 0); + test_u_n_f_p_i_one("waldo", "/waldo////quuix////", ".mount", "waldo@waldo-quuix.mount", 0); + test_u_n_f_p_i_one("waldo", "/", ".mount", "waldo@-.mount", 0); + test_u_n_f_p_i_one("waldo", "", ".mount", "waldo@-.mount", 0); + test_u_n_f_p_i_one("waldo", "///", ".mount", "waldo@-.mount", 0); + test_u_n_f_p_i_one("waldo", "..", ".mount", NULL, -EINVAL); + test_u_n_f_p_i_one("waldo", "/foo", ".waldi", NULL, -EINVAL); + test_u_n_f_p_i_one("wa--ldo", "/--", ".mount", "wa--ldo@\\x2d\\x2d.mount", 0); +} + +static void test_u_n_t_p_one(const char *unit, const char *path, int ret) { + _cleanup_free_ char *p = NULL; + + assert_se(unit_name_to_path(unit, &p) == ret); + assert_se(streq_ptr(path, p)); +} + +static void test_u_n_t_p(void) { + test_u_n_t_p_one("home.mount", "/home", 0); + test_u_n_t_p_one("home-lennart.mount", "/home/lennart", 0); + test_u_n_t_p_one("home-lennart-.mount", NULL, -EINVAL); + test_u_n_t_p_one("-home-lennart.mount", NULL, -EINVAL); + test_u_n_t_p_one("-home--lennart.mount", NULL, -EINVAL); + test_u_n_t_p_one("home-..-lennart.mount", NULL, -EINVAL); + test_u_n_t_p_one("", NULL, -EINVAL); + test_u_n_t_p_one("home/foo", NULL, -EINVAL); +} + +static void test_u_n_m_one(const char *pattern, const char *expect, int ret) { + _cleanup_free_ char *t = NULL; + + assert_se(unit_name_mangle(pattern, UNIT_NAME_NOGLOB, &t) == ret); + puts(strna(t)); + assert_se(streq_ptr(t, expect)); + + if (t) { + _cleanup_free_ char *k = NULL; + + assert_se(unit_name_is_valid(t, UNIT_NAME_ANY)); + + assert_se(unit_name_mangle(t, UNIT_NAME_NOGLOB, &k) == 0); + assert_se(streq_ptr(t, k)); } +} - expect("/home"); - expect("/dev/sda"); - expect("üxknürz.service"); - expect("foobar-meh...waldi.service"); - expect("_____####----.....service"); - expect("_____##@;;;,,,##----.....service"); - expect("xxx@@@@/////\\\\\\\\\\yyy.service"); - -#undef expect +static void test_u_n_m(void) { + puts("-------------------------------------------------"); + test_u_n_m_one("foo.service", "foo.service", 0); + test_u_n_m_one("/home", "home.mount", 1); + test_u_n_m_one("/dev/sda", "dev-sda.device", 1); + test_u_n_m_one("üxknürz.service", "\\xc3\\xbcxkn\\xc3\\xbcrz.service", 1); + test_u_n_m_one("foobar-meh...waldi.service", "foobar-meh...waldi.service", 0); + test_u_n_m_one("_____####----.....service", "_____\\x23\\x23\\x23\\x23----.....service", 1); + test_u_n_m_one("_____##@;;;,,,##----.....service", "_____\\x23\\x23@\\x3b\\x3b\\x3b\\x2c\\x2c\\x2c\\x23\\x23----.....service", 1); + test_u_n_m_one("xxx@@@@/////\\\\\\\\\\yyy.service", "xxx@@@@-----\\\\\\\\\\yyy.service", 1); + test_u_n_m_one("", NULL, -EINVAL); } static int test_unit_printf(void) { @@ -224,64 +301,49 @@ static void test_unit_prefix_is_valid(void) { } static void test_unit_name_change_suffix(void) { - char *r; + char *t; - r = unit_name_change_suffix("foo.bar", ".service"); - assert_se(r); - assert_se(streq(r, "foo.service")); - free(r); + assert_se(unit_name_change_suffix("foo.mount", ".service", &t) == 0); + assert_se(streq(t, "foo.service")); + free(t); - r = unit_name_change_suffix("foo@stuff.bar", ".boo"); - assert_se(r); - assert_se(streq(r, "foo@stuff.boo")); - free(r); + assert_se(unit_name_change_suffix("foo@stuff.service", ".socket", &t) == 0); + assert_se(streq(t, "foo@stuff.socket")); + free(t); } static void test_unit_name_build(void) { - char *r; + char *t; - r = unit_name_build("foo", "bar", ".service"); - assert_se(r); - assert_se(streq(r, "foo@bar.service")); - free(r); + assert_se(unit_name_build("foo", "bar", ".service", &t) == 0); + assert_se(streq(t, "foo@bar.service")); + free(t); - r = unit_name_build("fo0-stUff_b", "bar", ".mount"); - assert_se(r); - assert_se(streq(r, "fo0-stUff_b@bar.mount")); - free(r); + assert_se(unit_name_build("fo0-stUff_b", "bar", ".mount", &t) == 0); + assert_se(streq(t, "fo0-stUff_b@bar.mount")); + free(t); - r = unit_name_build("foo", NULL, ".service"); - assert_se(r); - assert_se(streq(r, "foo.service")); - free(r); -} - -static void test_unit_name_is_instance(void) { - assert_se(unit_name_is_instance("a@b.service")); - assert_se(unit_name_is_instance("a-c_c01Aj@b05Dii_-oioi.service")); - - assert_se(!unit_name_is_instance("a.service")); - assert_se(!unit_name_is_instance("a@.service")); - assert_se(!unit_name_is_instance("junk")); - assert_se(!unit_name_is_instance("")); + assert_se(unit_name_build("foo", NULL, ".service", &t) == 0); + assert_se(streq(t, "foo.service")); + free(t); } static void test_build_subslice(void) { char *a; char *b; - assert_se(build_subslice("-.slice", "foo", &a) >= 0); - assert_se(build_subslice(a, "bar", &b) >= 0); + assert_se(slice_build_subslice("-.slice", "foo", &a) >= 0); + assert_se(slice_build_subslice(a, "bar", &b) >= 0); free(a); - assert_se(build_subslice(b, "barfoo", &a) >= 0); + assert_se(slice_build_subslice(b, "barfoo", &a) >= 0); free(b); - assert_se(build_subslice(a, "foobar", &b) >= 0); + assert_se(slice_build_subslice(a, "foobar", &b) >= 0); free(a); assert_se(streq(b, "foo-bar-barfoo-foobar.slice")); free(b); - assert_se(build_subslice("foo.service", "bar", &a) < 0); - assert_se(build_subslice("foo", "bar", &a) < 0); + assert_se(slice_build_subslice("foo.service", "bar", &a) < 0); + assert_se(slice_build_subslice("foo", "bar", &a) < 0); } static void test_unit_name_to_instance(void) { @@ -298,13 +360,13 @@ static void test_unit_name_to_instance(void) { assert_se(streq(instance, "")); free(instance); - r = unit_name_to_instance("fo0-stUff_b@b.e", &instance); + r = unit_name_to_instance("fo0-stUff_b@b.service", &instance); assert_se(r >= 0); assert_se(streq(instance, "b")); free(instance); - r = unit_name_to_instance("foo.bar", &instance); - assert_se(r >= 0); + r = unit_name_to_instance("foo.service", &instance); + assert_se(r == 0); assert_se(!instance); r = unit_name_to_instance("fooj@unk", &instance); @@ -322,43 +384,37 @@ static void test_unit_name_escape(void) { assert_se(streq(r, "ab\\x2b\\x2dc.a-bc\\x40foo.service")); } -static void test_unit_name_template(void) { -#define expect(name, expected) \ - { \ - _cleanup_free_ char *f = NULL; \ - f = unit_name_template(name); \ - assert_se(f); \ - printf("got: %s, expected: %s\n", f, expected); \ - assert_se(streq(f, expected)); \ - } - expect("foo@bar.service", "foo@.service") - expect("foo.mount", "foo.mount") -#undef expect + +static void test_u_n_t_one(const char *name, const char *expected, int ret) { + _cleanup_free_ char *f = NULL; + + assert_se(unit_name_template(name, &f) == ret); + printf("got: %s, expected: %s\n", strna(f), strna(expected)); + assert_se(streq_ptr(f, expected)); } -static void test_unit_name_is_template(void) { - assert_se(unit_name_is_template("foo@.service")); - assert_se(unit_name_is_template("bar@.path")); - - assert_se(!unit_name_is_template("bar@i.mount")); - assert_se(!unit_name_is_template("bar@foobbbb.service")); - assert_se(!unit_name_is_template("barfoo.service")); +static void test_unit_name_template(void) { + test_u_n_t_one("foo@bar.service", "foo@.service", 0); + test_u_n_t_one("foo.mount", NULL, -EINVAL); } int main(int argc, char* argv[]) { int rc = 0; - test_replacements(); + test_unit_name_is_valid(); + test_u_n_r_i(); + test_u_n_f_p(); + test_u_n_f_p_i(); + test_u_n_m(); + test_u_n_t_p(); TEST_REQ_RUNNING_SYSTEMD(rc = test_unit_printf()); test_unit_instance_is_valid(); test_unit_prefix_is_valid(); test_unit_name_change_suffix(); test_unit_name_build(); - test_unit_name_is_instance(); test_build_subslice(); test_unit_name_to_instance(); test_unit_name_escape(); test_unit_name_template(); - test_unit_name_is_template(); return rc; }