diff options
Diffstat (limited to 'src/libsystemd/sd-path/sd-path.c')
-rw-r--r-- | src/libsystemd/sd-path/sd-path.c | 693 |
1 files changed, 693 insertions, 0 deletions
diff --git a/src/libsystemd/sd-path/sd-path.c b/src/libsystemd/sd-path/sd-path.c new file mode 100644 index 0000000..7290d1c --- /dev/null +++ b/src/libsystemd/sd-path/sd-path.c @@ -0,0 +1,693 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "sd-path.h" + +#include "alloc-util.h" +#include "architecture.h" +#include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" +#include "nulstr-util.h" +#include "path-lookup.h" +#include "path-util.h" +#include "string-util.h" +#include "strv.h" +#include "user-util.h" + +static int from_environment(const char *envname, const char *fallback, const char **ret) { + assert(ret); + + if (envname) { + const char *e; + + e = secure_getenv(envname); + if (e && path_is_absolute(e)) { + *ret = e; + return 0; + } + } + + if (fallback) { + *ret = fallback; + return 0; + } + + return -ENXIO; +} + +static int from_home_dir(const char *envname, const char *suffix, char **buffer, const char **ret) { + _cleanup_free_ char *h = NULL; + int r; + + assert(suffix); + assert(buffer); + assert(ret); + + if (envname) { + const char *e = NULL; + + e = secure_getenv(envname); + if (e && path_is_absolute(e)) { + *ret = e; + return 0; + } + } + + r = get_home_dir(&h); + if (r < 0) + return r; + + if (!path_extend(&h, suffix)) + return -ENOMEM; + + *buffer = h; + *ret = TAKE_PTR(h); + return 0; +} + +static int from_user_dir(const char *field, char **buffer, const char **ret) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *b = NULL; + _cleanup_free_ const char *fn = NULL; + const char *c = NULL; + int r; + + assert(field); + assert(buffer); + assert(ret); + + r = from_home_dir("XDG_CONFIG_HOME", ".config", &b, &c); + if (r < 0) + return r; + + fn = path_join(c, "user-dirs.dirs"); + if (!fn) + return -ENOMEM; + + f = fopen(fn, "re"); + if (!f) { + if (errno == ENOENT) + goto fallback; + + return -errno; + } + + /* This is an awful parse, but it follows closely what xdg-user-dirs does upstream */ + for (;;) { + _cleanup_free_ char *line = NULL; + char *p, *e; + + r = read_stripped_line(f, LONG_LINE_MAX, &line); + if (r < 0) + return r; + if (r == 0) + break; + + p = startswith(line, field); + if (!p) + continue; + + p += strspn(p, WHITESPACE); + + if (*p != '=') + continue; + p++; + + p += strspn(p, WHITESPACE); + + if (*p != '"') + continue; + p++; + + e = strrchr(p, '"'); + if (!e) + continue; + *e = 0; + + /* Three syntaxes permitted: relative to $HOME, $HOME itself, and absolute path */ + if (startswith(p, "$HOME/")) { + _cleanup_free_ char *h = NULL; + + r = get_home_dir(&h); + if (r < 0) + return r; + + if (!path_extend(&h, p+5)) + return -ENOMEM; + + *buffer = h; + *ret = TAKE_PTR(h); + return 0; + } else if (streq(p, "$HOME")) { + + r = get_home_dir(buffer); + if (r < 0) + return r; + + *ret = *buffer; + return 0; + } else if (path_is_absolute(p)) { + char *copy; + + copy = strdup(p); + if (!copy) + return -ENOMEM; + + *buffer = copy; + *ret = copy; + return 0; + } + } + +fallback: + /* The desktop directory defaults to $HOME/Desktop, the others to $HOME */ + if (streq(field, "XDG_DESKTOP_DIR")) { + _cleanup_free_ char *h = NULL; + + r = get_home_dir(&h); + if (r < 0) + return r; + + if (!path_extend(&h, "Desktop")) + return -ENOMEM; + + *buffer = h; + *ret = TAKE_PTR(h); + } else { + r = get_home_dir(buffer); + if (r < 0) + return r; + + *ret = *buffer; + } + + return 0; +} + +static int get_path(uint64_t type, char **buffer, const char **ret) { + int r; + + assert(buffer); + assert(ret); + + switch (type) { + + case SD_PATH_TEMPORARY: + return tmp_dir(ret); + + case SD_PATH_TEMPORARY_LARGE: + return var_tmp_dir(ret); + + case SD_PATH_SYSTEM_BINARIES: + *ret = "/usr/bin"; + return 0; + + case SD_PATH_SYSTEM_INCLUDE: + *ret = "/usr/include"; + return 0; + + case SD_PATH_SYSTEM_LIBRARY_PRIVATE: + *ret = "/usr/lib"; + return 0; + + case SD_PATH_SYSTEM_LIBRARY_ARCH: + *ret = LIBDIR; + return 0; + + case SD_PATH_SYSTEM_SHARED: + *ret = "/usr/share"; + return 0; + + case SD_PATH_SYSTEM_CONFIGURATION_FACTORY: + *ret = "/usr/share/factory/etc"; + return 0; + + case SD_PATH_SYSTEM_STATE_FACTORY: + *ret = "/usr/share/factory/var"; + return 0; + + case SD_PATH_SYSTEM_CONFIGURATION: + *ret = "/etc"; + return 0; + + case SD_PATH_SYSTEM_RUNTIME: + *ret = "/run"; + return 0; + + case SD_PATH_SYSTEM_RUNTIME_LOGS: + *ret = "/run/log"; + return 0; + + case SD_PATH_SYSTEM_STATE_PRIVATE: + *ret = "/var/lib"; + return 0; + + case SD_PATH_SYSTEM_STATE_LOGS: + *ret = "/var/log"; + return 0; + + case SD_PATH_SYSTEM_STATE_CACHE: + *ret = "/var/cache"; + return 0; + + case SD_PATH_SYSTEM_STATE_SPOOL: + *ret = "/var/spool"; + return 0; + + case SD_PATH_USER_BINARIES: + return from_home_dir(NULL, ".local/bin", buffer, ret); + + case SD_PATH_USER_LIBRARY_PRIVATE: + return from_home_dir(NULL, ".local/lib", buffer, ret); + + case SD_PATH_USER_LIBRARY_ARCH: + return from_home_dir(NULL, ".local/lib/" LIB_ARCH_TUPLE, buffer, ret); + + case SD_PATH_USER_SHARED: + return from_home_dir("XDG_DATA_HOME", ".local/share", buffer, ret); + + case SD_PATH_USER_CONFIGURATION: + return from_home_dir("XDG_CONFIG_HOME", ".config", buffer, ret); + + case SD_PATH_USER_RUNTIME: + return from_environment("XDG_RUNTIME_DIR", NULL, ret); + + case SD_PATH_USER_STATE_CACHE: + return from_home_dir("XDG_CACHE_HOME", ".cache", buffer, ret); + + case SD_PATH_USER_STATE_PRIVATE: + return from_home_dir("XDG_STATE_HOME", ".local/state", buffer, ret); + + case SD_PATH_USER: + r = get_home_dir(buffer); + if (r < 0) + return r; + + *ret = *buffer; + return 0; + + case SD_PATH_USER_DOCUMENTS: + return from_user_dir("XDG_DOCUMENTS_DIR", buffer, ret); + + case SD_PATH_USER_MUSIC: + return from_user_dir("XDG_MUSIC_DIR", buffer, ret); + + case SD_PATH_USER_PICTURES: + return from_user_dir("XDG_PICTURES_DIR", buffer, ret); + + case SD_PATH_USER_VIDEOS: + return from_user_dir("XDG_VIDEOS_DIR", buffer, ret); + + case SD_PATH_USER_DOWNLOAD: + return from_user_dir("XDG_DOWNLOAD_DIR", buffer, ret); + + case SD_PATH_USER_PUBLIC: + return from_user_dir("XDG_PUBLICSHARE_DIR", buffer, ret); + + case SD_PATH_USER_TEMPLATES: + return from_user_dir("XDG_TEMPLATES_DIR", buffer, ret); + + case SD_PATH_USER_DESKTOP: + return from_user_dir("XDG_DESKTOP_DIR", buffer, ret); + + case SD_PATH_SYSTEMD_UTIL: + *ret = PREFIX_NOSLASH "/lib/systemd"; + return 0; + + case SD_PATH_SYSTEMD_SYSTEM_UNIT: + *ret = SYSTEM_DATA_UNIT_DIR; + return 0; + + case SD_PATH_SYSTEMD_SYSTEM_PRESET: + *ret = PREFIX_NOSLASH "/lib/systemd/system-preset"; + return 0; + + case SD_PATH_SYSTEMD_USER_UNIT: + *ret = USER_DATA_UNIT_DIR; + return 0; + + case SD_PATH_SYSTEMD_USER_PRESET: + *ret = PREFIX_NOSLASH "/lib/systemd/user-preset"; + return 0; + + case SD_PATH_SYSTEMD_SYSTEM_CONF: + *ret = SYSTEM_CONFIG_UNIT_DIR; + return 0; + + case SD_PATH_SYSTEMD_USER_CONF: + *ret = USER_CONFIG_UNIT_DIR; + return 0; + + case SD_PATH_SYSTEMD_SYSTEM_GENERATOR: + *ret = SYSTEM_GENERATOR_DIR; + return 0; + + case SD_PATH_SYSTEMD_USER_GENERATOR: + *ret = USER_GENERATOR_DIR; + return 0; + + case SD_PATH_SYSTEMD_SLEEP: + *ret = PREFIX_NOSLASH "/lib/systemd/system-sleep"; + return 0; + + case SD_PATH_SYSTEMD_SHUTDOWN: + *ret = PREFIX_NOSLASH "/lib/systemd/system-shutdown"; + return 0; + + case SD_PATH_TMPFILES: + *ret = "/usr/lib/tmpfiles.d"; + return 0; + + case SD_PATH_SYSUSERS: + *ret = PREFIX_NOSLASH "/lib/sysusers.d"; + return 0; + + case SD_PATH_SYSCTL: + *ret = PREFIX_NOSLASH "/lib/sysctl.d"; + return 0; + + case SD_PATH_BINFMT: + *ret = PREFIX_NOSLASH "/lib/binfmt.d"; + return 0; + + case SD_PATH_MODULES_LOAD: + *ret = PREFIX_NOSLASH "/lib/modules-load.d"; + return 0; + + case SD_PATH_CATALOG: + *ret = "/usr/lib/systemd/catalog"; + return 0; + + case SD_PATH_SYSTEMD_SYSTEM_ENVIRONMENT_GENERATOR: + *ret = SYSTEM_ENV_GENERATOR_DIR; + return 0; + + case SD_PATH_SYSTEMD_USER_ENVIRONMENT_GENERATOR: + *ret = USER_ENV_GENERATOR_DIR; + return 0; + } + + return -EOPNOTSUPP; +} + +static int get_path_alloc(uint64_t type, const char *suffix, char **path) { + _cleanup_free_ char *buffer = NULL; + char *buffer2 = NULL; + const char *ret; + int r; + + assert(path); + + r = get_path(type, &buffer, &ret); + if (r < 0) + return r; + + if (suffix) { + suffix += strspn(suffix, "/"); + buffer2 = path_join(ret, suffix); + if (!buffer2) + return -ENOMEM; + } else if (!buffer) { + buffer = strdup(ret); + if (!buffer) + return -ENOMEM; + } + + *path = buffer2 ?: TAKE_PTR(buffer); + return 0; +} + +_public_ int sd_path_lookup(uint64_t type, const char *suffix, char **path) { + int r; + + assert_return(path, -EINVAL); + + r = get_path_alloc(type, suffix, path); + if (r != -EOPNOTSUPP) + return r; + + /* Fall back to sd_path_lookup_strv */ + _cleanup_strv_free_ char **l = NULL; + char *buffer; + + r = sd_path_lookup_strv(type, suffix, &l); + if (r < 0) + return r; + + buffer = strv_join(l, ":"); + if (!buffer) + return -ENOMEM; + + *path = buffer; + return 0; +} + +static int search_from_environment( + char ***list, + const char *env_home, + const char *home_suffix, + const char *env_search, + bool env_search_sufficient, + const char *first, ...) { + + _cleanup_strv_free_ char **l = NULL; + const char *e; + char *h = NULL; + int r; + + assert(list); + + if (env_search) { + e = secure_getenv(env_search); + if (e) { + l = strv_split(e, ":"); + if (!l) + return -ENOMEM; + + if (env_search_sufficient) { + *list = TAKE_PTR(l); + return 0; + } + } + } + + if (!l && first) { + va_list ap; + + va_start(ap, first); + l = strv_new_ap(first, ap); + va_end(ap); + + if (!l) + return -ENOMEM; + } + + if (env_home) { + e = secure_getenv(env_home); + if (e && path_is_absolute(e)) { + h = strdup(e); + if (!h) + return -ENOMEM; + } + } + + if (!h && home_suffix) { + e = secure_getenv("HOME"); + if (e && path_is_absolute(e)) { + h = path_join(e, home_suffix); + if (!h) + return -ENOMEM; + } + } + + if (h) { + r = strv_consume_prepend(&l, h); + if (r < 0) + return -ENOMEM; + } + + *list = TAKE_PTR(l); + return 0; +} + +#if HAVE_SPLIT_BIN +# define ARRAY_SBIN_BIN(x) x "sbin", x "bin" +#else +# define ARRAY_SBIN_BIN(x) x "bin" +#endif + +static int get_search(uint64_t type, char ***list) { + int r; + + assert(list); + + switch (type) { + + case SD_PATH_SEARCH_BINARIES: + return search_from_environment(list, + NULL, + ".local/bin", + "PATH", + true, + ARRAY_SBIN_BIN("/usr/local/"), + ARRAY_SBIN_BIN("/usr/"), + NULL); + + case SD_PATH_SEARCH_LIBRARY_PRIVATE: + return search_from_environment(list, + NULL, + ".local/lib", + NULL, + false, + "/usr/local/lib", + "/usr/lib", + NULL); + + case SD_PATH_SEARCH_LIBRARY_ARCH: + return search_from_environment(list, + NULL, + ".local/lib/" LIB_ARCH_TUPLE, + "LD_LIBRARY_PATH", + true, + LIBDIR, + NULL); + + case SD_PATH_SEARCH_SHARED: + return search_from_environment(list, + "XDG_DATA_HOME", + ".local/share", + "XDG_DATA_DIRS", + false, + "/usr/local/share", + "/usr/share", + NULL); + + case SD_PATH_SEARCH_CONFIGURATION_FACTORY: + return search_from_environment(list, + NULL, + NULL, + NULL, + false, + "/usr/local/share/factory/etc", + "/usr/share/factory/etc", + NULL); + + case SD_PATH_SEARCH_STATE_FACTORY: + return search_from_environment(list, + NULL, + NULL, + NULL, + false, + "/usr/local/share/factory/var", + "/usr/share/factory/var", + NULL); + + case SD_PATH_SEARCH_CONFIGURATION: + return search_from_environment(list, + "XDG_CONFIG_HOME", + ".config", + "XDG_CONFIG_DIRS", + false, + "/etc", + NULL); + + case SD_PATH_SEARCH_BINARIES_DEFAULT: + return strv_from_nulstr(list, DEFAULT_PATH_NULSTR); + + case SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT: + case SD_PATH_SYSTEMD_SEARCH_USER_UNIT: { + _cleanup_(lookup_paths_free) LookupPaths lp = {}; + RuntimeScope scope = type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT ? + RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER; + + r = lookup_paths_init(&lp, scope, 0, NULL); + if (r < 0) + return r; + + *list = TAKE_PTR(lp.search_path); + return 0; + } + + case SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR: + case SD_PATH_SYSTEMD_SEARCH_USER_GENERATOR: { + RuntimeScope scope = type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR ? + RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER; + char **t; + + t = generator_binary_paths(scope); + if (!t) + return -ENOMEM; + + *list = t; + return 0; + } + + case SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR: + case SD_PATH_SYSTEMD_SEARCH_USER_ENVIRONMENT_GENERATOR: { + char **t; + + t = env_generator_binary_paths(type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR ? + RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER); + if (!t) + return -ENOMEM; + + *list = t; + return 0; + } + + case SD_PATH_SYSTEMD_SEARCH_NETWORK: + return strv_from_nulstr(list, NETWORK_DIRS_NULSTR); + + } + + return -EOPNOTSUPP; +} + +_public_ int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***paths) { + _cleanup_strv_free_ char **l = NULL, **n = NULL; + int r; + + assert_return(paths, -EINVAL); + + r = get_search(type, &l); + if (r == -EOPNOTSUPP) { + _cleanup_free_ char *t = NULL; + + r = get_path_alloc(type, suffix, &t); + if (r < 0) + return r; + + l = new(char*, 2); + if (!l) + return -ENOMEM; + l[0] = TAKE_PTR(t); + l[1] = NULL; + + *paths = TAKE_PTR(l); + return 0; + + } else if (r < 0) + return r; + + if (!suffix) { + *paths = TAKE_PTR(l); + return 0; + } + + n = new(char*, strv_length(l)+1); + if (!n) + return -ENOMEM; + + char **j = n; + STRV_FOREACH(i, l) { + *j = path_join(*i, suffix); + if (!*j) + return -ENOMEM; + + j++; + } + *j = NULL; + + *paths = TAKE_PTR(n); + return 0; +} |