X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fshared%2Funit-name.c;h=b23e47a2e6b2b2d4b04d201219507dcac7f4fed4;hp=832b9268133f2e1b06a5c3c499cb1be850df9500;hb=6ec9b87c4ecf5144b5ea845a53a352dd9f2d173a;hpb=e3e0314b56012f7febc279d268f2cadc1fcc0f25 diff --git a/src/shared/unit-name.c b/src/shared/unit-name.c index 832b92681..b23e47a2e 100644 --- a/src/shared/unit-name.c +++ b/src/shared/unit-name.c @@ -21,57 +21,28 @@ #include #include -#include -#include "sd-bus.h" #include "path-util.h" +#include "bus-label.h" #include "util.h" #include "unit-name.h" #include "def.h" +#include "strv.h" #define VALID_CHARS \ 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, bool 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(n); + if (_unlikely_(flags == 0)) + return false; + + if (isempty(n)) + return false; if (strlen(n) >= UNIT_NAME_MAX) return false; @@ -92,48 +63,83 @@ bool unit_name_is_valid(const char *n, bool template_ok) { return false; } - if (at) { - if (at == n) - return false; + if (at == n) + return false; - if (!template_ok && 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) { - assert(i); /* The max length depends on the length of the string, so we * don't really check this here. */ - if (i[0] == 0) + if (isempty(i)) return false; /* We allow additional @ in the instance string, we do not * allow them in the prefix! */ - for (; *i; i++) - if (!strchr("@" VALID_CHARS, *i)) - return false; + return in_charset(i, "@" VALID_CHARS); +} + +bool unit_suffix_is_valid(const char *s) { + if (isempty(s)) + return false; + + if (s[0] != '.') + return false; + + if (unit_type_from_string(s + 1) < 0) + return false; return true; } -bool unit_prefix_is_valid(const char *p) { +int unit_name_to_prefix(const char *n, char **ret) { + const char *p; + char *s; - /* We don't allow additional @ in the instance string */ + assert(n); + assert(ret); - if (p[0] == 0) - return false; + if (!unit_name_is_valid(n, UNIT_NAME_ANY)) + return -EINVAL; - for (; *p; p++) - if (!strchr(VALID_CHARS, *p)) - return false; + p = strchr(n, '@'); + if (!p) + p = strrchr(n, '.'); - return true; + 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) { @@ -143,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) { @@ -150,77 +159,119 @@ int unit_name_to_instance(const char *n, char **instance) { return 0; } - assert_se(d = strrchr(n, '.')); - assert(p < d); + p++; + + d = strrchr(p, '.'); + if (!d) + return -EINVAL; - i = strndup(p+1, d-p-1); + i = strndup(p, d-p); if (!i) return -ENOMEM; *instance = i; - return 0; + 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; - assert_se(d = strrchr(n, '.')); + d = strrchr(n, '.'); + if (!d) + return -EINVAL; - return strndup(n, d - n); + s = strndup(n, d - n); + if (!s) + return -ENOMEM; + + *ret = s; + return 0; } -char *unit_name_to_prefix(const char *n) { - const char *p; +UnitType unit_name_to_type(const char *n) { + const char *e; - p = strchr(n, '@'); - if (p) - return strndup(n, p - n); + assert(n); - return unit_name_to_prefix_and_instance(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_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(unit_name_is_valid(n, true)); 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; - memcpy(r, n, a); - memcpy(r+a, suffix, b+1); + strcpy(mempcpy(s, n, a), suffix); + *ret = s; - return r; + 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(unit_prefix_is_valid(prefix)); - assert(!instance || unit_instance_is_valid(instance)); 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) { + assert(t); + *(t++) = '\\'; *(t++) = 'x'; *(t++) = hexchar(c >> 4); *(t++) = hexchar(c); + return t; } @@ -249,6 +300,8 @@ static char *do_escape(const char *f, char *t) { char *unit_name_escape(const char *f) { char *r, *t; + assert(f); + r = new(char, strlen(f)*4+1); if (!r) return NULL; @@ -259,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 == '-') @@ -274,183 +328,234 @@ 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) (((uint8_t) a << 4U) | (uint8_t) b); + f += 3; } else *(t++) = *f; } *t = 0; - return r; + *ret = r; + r = NULL; + + return 0; } -char *unit_name_path_escape(const char *f) { - char *p, *e; +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 (streq(p, "/") || streq(p, "")) { - free(p); - return strdup("-"); - } - - e = unit_name_escape(p[0] == '/' ? p + 1 : p); - free(p); - - return e; -} - -char *unit_name_path_unescape(const char *f) { - char *e; - - assert(f); + if (STR_IN_SET(p, "/", "")) + s = strdup("-"); + else { + char *e; - e = unit_name_unescape(f); - if (!e) - return NULL; + if (!path_is_safe(p)) + return -EINVAL; - if (e[0] != '/') { - char *w; + /* Truncate trailing slashes */ + e = endswith(p, "/"); + if (e) + *e = 0; - w = strappend("/", e); - free(e); + /* Truncate leading slashes */ + if (p[0] == '/') + p++; - return w; + s = unit_name_escape(p); } + if (!s) + return -ENOMEM; - return e; + *ret = s; + return 0; } -bool unit_name_is_template(const char *n) { - const char *p; +int unit_name_path_unescape(const char *f, char **ret) { + char *s; + int r; - assert(n); + assert(f); - p = strchr(n, '@'); - if (!p) - return false; + if (isempty(f)) + return -EINVAL; - return p[1] == '.'; -} + if (streq(f, "-")) { + s = strdup("/"); + if (!s) + return -ENOMEM; + } else { + char *w; -bool unit_name_is_instance(const char *n) { - const char *p; + r = unit_name_unescape(f, &w); + if (r < 0) + return r; - assert(n); + /* Don't accept trailing or leading slashes */ + if (startswith(w, "/") || endswith(w, "/")) { + free(w); + return -EINVAL; + } - p = strchr(n, '@'); - if (!p) - return false; + /* Prefix a slash again */ + s = strappend("/", w); + free(w); + if (!s) + return -ENOMEM; + + if (!path_is_safe(s)) { + free(s); + return -EINVAL; + } + } + + if (ret) + *ret = s; + else + free(s); - return p[1] != '.'; + return 0; } -char *unit_name_replace_instance(const char *f, const char *i) { +int unit_name_replace_instance(const char *f, const char *i, char **ret) { const char *p, *e; - char *r, *k; + 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) - assert_se(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; - k = mempcpy(r, f, a + 1); - k = mempcpy(k, i, b); - strcpy(k, e); + strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e); - return r; + *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; - p = strchr(f, '@'); - if (!p) - return strdup(f); + assert(f); + assert(ret); + + if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) + return -EINVAL; + assert_se(p = strchr(f, '@')); assert_se(e = strrchr(f, '.')); - a = p - f + 1; - r = new(char, a + strlen(e) + 1); - if (!r) - return NULL; + a = p - f; - strcpy(mempcpy(r, f, a), e); - return r; + s = new(char, a + 1 + strlen(e) + 1); + if (!s) + return -ENOMEM; + + strcpy(mempcpy(s, f, a + 1), e); + + *ret = s; + return 0; } -char *unit_name_from_path(const char *path, const char *suffix) { - char *p, *r; +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; - r = strappend(p, suffix); - free(p); + r = unit_name_path_escape(path, &p); + if (r < 0) + return r; - 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) { - char *p, *r; +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; - r = strjoin(prefix, "@", p, suffix, NULL); - free(p); + if (!unit_suffix_is_valid(suffix)) + return -EINVAL; - return r; + 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) { @@ -458,7 +563,7 @@ char *unit_dbus_path_from_name(const char *name) { assert(name); - e = sd_bus_label_escape(name); + e = bus_label_escape(name); if (!e) return NULL; @@ -473,7 +578,7 @@ int unit_name_from_dbus_path(const char *path, char **name) { if (!e) return -EINVAL; - n = sd_bus_label_unescape(e); + n = bus_label_unescape(e); if (!n) return -ENOMEM; @@ -481,32 +586,19 @@ 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; -/** - * Try to turn a string that might not be a unit name into a - * sensible unit name. - */ -char *unit_name_mangle(const char *name, bool allow_globs) { - char *r, *t; - const char *f; - const char* valid_chars = allow_globs ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS; - - assert(name); - - if (is_device_path(name)) - return unit_name_from_path(name, ".device"); - - if (path_is_absolute(name)) - return unit_name_from_path(name, ".mount"); + 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. */ - r = new(char, strlen(name) * 4 + 1 + sizeof(".service")-1); - if (!r) - return NULL; + valid_chars = allow_globs == UNIT_NAME_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS; - for (f = name, t = r; *f; f++) { + for (; *f; f++) { if (*f == '/') *(t++) = '-'; else if (!strchr(valid_chars, *f)) @@ -515,83 +607,226 @@ char *unit_name_mangle(const char *name, bool allow_globs) { *(t++) = *f; } - if (unit_name_to_type(name) < 0) - strcpy(t, ".service"); - else - *t = 0; - - return r; + return t; } - /** - * Similar to unit_name_mangle(), but is called when we know - * that this is about a specific unit type. + * 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, + * except that @suffix is appended if a valid unit suffix is not present. + * + * If @allow_globs, globs characters are preserved. Otherwise they are escaped. */ -char *unit_name_mangle_with_suffix(const char *name, bool allow_globs, const char *suffix) { - char *r, *t; - const char *f; +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); - r = new(char, strlen(name) * 4 + strlen(suffix) + 1); - if (!r) - return NULL; + if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */ + return -EINVAL; - for (f = name, t = r; *f; f++) { - if (*f == '/') - *(t++) = '-'; - else if (!strchr(VALID_CHARS, *f)) - t = do_escape_char(*f, t); - else - *(t++) = *f; + if (!unit_suffix_is_valid(suffix)) + return -EINVAL; + + if (unit_name_is_valid(name, UNIT_NAME_ANY)) { + /* No mangling necessary... */ + s = strdup(name); + if (!s) + return -ENOMEM; + + *ret = s; + return 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; } - if (!endswith(name, suffix)) + 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; +int slice_build_parent_slice(const char *slice, char **ret) { + char *s, *dash; - assert(n); + assert(slice); + assert(ret); - e = strrchr(n, '.'); - if (!e) - return _UNIT_TYPE_INVALID; + if (!slice_name_is_valid(slice)) + return -EINVAL; - return unit_type_from_string(e + 1); + if (streq(slice, "-.slice")) { + *ret = NULL; + return 0; + } + + s = strdup(slice); + if (!s) + return -ENOMEM; + + dash = strrchr(s, '-'); + if (dash) + strcpy(dash, ".slice"); + else { + free(s); + + s = strdup("-.slice"); + if (!s) + return -ENOMEM; + } + + *ret = s; + return 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 (!slice_name_is_valid(slice)) + 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; - e = endswith(slice, ".slice"); - if (!e) - return -EINVAL; + assert_se(e = endswith(slice, ".slice")); - 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; } + +bool slice_name_is_valid(const char *name) { + const char *p, *e; + bool dash = false; + + if (!unit_name_is_valid(name, UNIT_NAME_PLAIN)) + return false; + + if (streq(name, "-.slice")) + return true; + + e = endswith(name, ".slice"); + if (!e) + return false; + + for (p = name; p < e; p++) { + + if (*p == '-') { + + /* Don't allow initial dash */ + if (p == name) + return false; + + /* Don't allow multiple dashes */ + if (dash) + return false; + + dash = true; + } else + dash = false; + } + + /* Don't allow trailing hash */ + if (dash) + return false; + + return true; +} + +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", + [UNIT_REQUISITE] = "Requisite", + [UNIT_REQUISITE_OVERRIDABLE] = "RequisiteOverridable", + [UNIT_WANTS] = "Wants", + [UNIT_BINDS_TO] = "BindsTo", + [UNIT_PART_OF] = "PartOf", + [UNIT_REQUIRED_BY] = "RequiredBy", + [UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable", + [UNIT_WANTED_BY] = "WantedBy", + [UNIT_BOUND_BY] = "BoundBy", + [UNIT_CONSISTS_OF] = "ConsistsOf", + [UNIT_CONFLICTS] = "Conflicts", + [UNIT_CONFLICTED_BY] = "ConflictedBy", + [UNIT_BEFORE] = "Before", + [UNIT_AFTER] = "After", + [UNIT_ON_FAILURE] = "OnFailure", + [UNIT_TRIGGERS] = "Triggers", + [UNIT_TRIGGERED_BY] = "TriggeredBy", + [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo", + [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom", + [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf", + [UNIT_REFERENCES] = "References", + [UNIT_REFERENCED_BY] = "ReferencedBy", +}; + +DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);