sd-path.c revision 9a00f57a5ba7ed431e6bac8d8b36518708503b4e
499b34cea04a46823d003d4c0520c8b03e8513cbBrian Wellington/***
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence This file is part of systemd.
4c1132f34493327abc632196f5876a89aa573687Bob Halley
4c1132f34493327abc632196f5876a89aa573687Bob Halley Copyright 2014 Lennart Poettering
4c1132f34493327abc632196f5876a89aa573687Bob Halley
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence systemd is free software; you can redistribute it and/or modify it
15a44745412679c30a6d022733925af70a38b715David Lawrence under the terms of the GNU Lesser General Public License as published by
15a44745412679c30a6d022733925af70a38b715David Lawrence the Free Software Foundation; either version 2.1 of the License, or
15a44745412679c30a6d022733925af70a38b715David Lawrence (at your option) any later version.
15a44745412679c30a6d022733925af70a38b715David Lawrence
15a44745412679c30a6d022733925af70a38b715David Lawrence systemd is distributed in the hope that it will be useful, but
15a44745412679c30a6d022733925af70a38b715David Lawrence WITHOUT ANY WARRANTY; without even the implied warranty of
15a44745412679c30a6d022733925af70a38b715David Lawrence MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15a44745412679c30a6d022733925af70a38b715David Lawrence Lesser General Public License for more details.
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley
499b34cea04a46823d003d4c0520c8b03e8513cbBrian Wellington You should have received a copy of the GNU Lesser General Public License
9c3531d72aeaad6c5f01efe6a1c82023e1379e4dDavid Lawrence along with systemd; If not, see <http://www.gnu.org/licenses/>.
f41f183f628a148860a6d1f0070208cddd45b0c6Bob Halley***/
f41f183f628a148860a6d1f0070208cddd45b0c6Bob Halley
f41f183f628a148860a6d1f0070208cddd45b0c6Bob Halley#include "util.h"
f41f183f628a148860a6d1f0070208cddd45b0c6Bob Halley#include "architecture.h"
d0ad0044249ab08201ce8a1029253f2c6ef41147Bob Halley#include "path-util.h"
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley#include "strv.h"
da114656ce2ef6df516180f8c21a0d891069fe27Andreas Gustafsson#include "sd-path.h"
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halleystatic int from_environment(const char *envname, const char *fallback, const char **ret) {
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley assert(ret);
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley
93593fa445f77a5517103be29544f9ecef0e2d2dBob Halley if (envname) {
2f2e3e1c38aabeef784566870d885adfa7f00a48David Lawrence const char *e;
2f2e3e1c38aabeef784566870d885adfa7f00a48David Lawrence
2f2e3e1c38aabeef784566870d885adfa7f00a48David Lawrence e = secure_getenv(envname);
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley if (e && path_is_absolute(e)) {
280747fa7c1d4597d47f7be8ec5fb7c8980c1952Andreas Gustafsson *ret = e;
922e6a3c2ac4ef900dd9dc99f0cc137f18372583Andreas Gustafsson return 0;
280747fa7c1d4597d47f7be8ec5fb7c8980c1952Andreas Gustafsson }
280747fa7c1d4597d47f7be8ec5fb7c8980c1952Andreas Gustafsson }
3ee5e4d6a40fdc413c6216048e7c162eb5e6b295Brian Wellington
3ee5e4d6a40fdc413c6216048e7c162eb5e6b295Brian Wellington if (fallback) {
93593fa445f77a5517103be29544f9ecef0e2d2dBob Halley *ret = fallback;
93593fa445f77a5517103be29544f9ecef0e2d2dBob Halley return 0;
8e06cea14c857429ab7e7299af2dce5eeeaa5ff0Michael Graff }
a0d172f105ac277a9e56e1b61e6863d279f2ff75Andreas Gustafsson
a0d172f105ac277a9e56e1b61e6863d279f2ff75Andreas Gustafsson return -ENXIO;
a0d172f105ac277a9e56e1b61e6863d279f2ff75Andreas Gustafsson}
a0d172f105ac277a9e56e1b61e6863d279f2ff75Andreas Gustafsson
a0d172f105ac277a9e56e1b61e6863d279f2ff75Andreas Gustafssonstatic int from_home_dir(const char *envname, const char *suffix, char **buffer, const char **ret) {
_cleanup_free_ char *h = NULL;
char *cc = 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 (endswith(h, "/"))
cc = strappend(h, suffix);
else
cc = strjoin(h, "/", suffix, NULL);
if (!cc)
return -ENOMEM;
*buffer = cc;
*ret = cc;
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;
const char *fn = NULL;
char line[LINE_MAX];
size_t n;
int r;
assert(field);
assert(buffer);
assert(ret);
r = from_home_dir(NULL, ".config/user-dirs.dirs", &b, &fn);
if (r < 0)
return r;
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 */
n = strlen(field);
FOREACH_LINE(line, f, return -errno) {
char *l, *p, *e;
l = strstrip(line);
if (!strneq(l, field, n))
continue;
p = l + n;
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;
char *cc;
r = get_home_dir(&h);
if (r < 0)
return r;
cc = strappend(h, p+5);
if (!cc)
return -ENOMEM;
*buffer = cc;
*ret = cc;
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;
char *cc;
r = get_home_dir(&h);
if (r < 0)
return r;
cc = strappend(h, "/Desktop");
if (!cc)
return -ENOMEM;
*buffer = cc;
*ret = cc;
} 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 from_environment("TMPDIR", "/tmp", ret);
case SD_PATH_TEMPORARY_LARGE:
return from_environment("TMPDIR", "/var/tmp", 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/" 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:
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);
}
return -ENOTSUP;
}
int sd_path_home(uint64_t type, const char *suffix, char **path) {
char *buffer = NULL, *cc;
const char *ret;
int r;
assert_return(path, -EINVAL);
if (IN_SET(type,
SD_PATH_SEARCH_BINARIES,
SD_PATH_SEARCH_LIBRARY_PRIVATE,
SD_PATH_SEARCH_LIBRARY_ARCH,
SD_PATH_SEARCH_SHARED,
SD_PATH_SEARCH_CONFIGURATION_FACTORY,
SD_PATH_SEARCH_STATE_FACTORY,
SD_PATH_SEARCH_CONFIGURATION)) {
_cleanup_strv_free_ char **l = NULL;
r = sd_path_search(type, suffix, &l);
if (r < 0)
return r;
buffer = strv_join(l, ":");
if (!buffer)
return -ENOMEM;
*path = buffer;
return 0;
}
r = get_path(type, &buffer, &ret);
if (r < 0)
return r;
if (!suffix) {
if (!buffer) {
buffer = strdup(ret);
if (!buffer)
return -ENOMEM;
}
*path = buffer;
return 0;
}
suffix += strspn(suffix, "/");
if (endswith(ret, "/"))
cc = strappend(ret, suffix);
else
cc = strjoin(ret, "/", suffix, NULL);
free(buffer);
if (!cc)
return -ENOMEM;
*path = cc;
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, ...) {
const char *e;
char *h = NULL;
char **l = 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 = 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) {
strv_free(l);
return -ENOMEM;
}
}
}
if (!h && home_suffix) {
e = secure_getenv("HOME");
if (e && path_is_absolute(e)) {
if (endswith(e, "/"))
h = strappend(e, home_suffix);
else
h = strjoin(e, "/", home_suffix, NULL);
if (!h) {
strv_free(l);
return -ENOMEM;
}
}
}
if (h) {
r = strv_consume_prepend(&l, h);
if (r < 0) {
strv_free(l);
return -ENOMEM;
}
}
*list = l;
return 0;
}
static int get_search(uint64_t type, char ***list) {
assert(list);
switch(type) {
case SD_PATH_SEARCH_BINARIES:
return search_from_environment(list,
NULL,
".local/bin",
"PATH",
true,
"/usr/local/sbin",
"/usr/local/bin",
"/usr/sbin",
"/usr/bin",
#ifdef HAVE_SPLIT_USR
"/sbin",
"/bin",
#endif
NULL);
case SD_PATH_SEARCH_LIBRARY_PRIVATE:
return search_from_environment(list,
NULL,
".local/lib",
NULL,
false,
"/usr/local/lib",
"/usr/lib",
#ifdef HAVE_SPLIT_USR
"/lib",
#endif
NULL);
case SD_PATH_SEARCH_LIBRARY_ARCH:
return search_from_environment(list,
NULL,
".local/lib/" ARCH_TUPLE,
"LD_LIBRARY_PATH",
true,
LIBDIR,
#ifdef HAVE_SPLIT_USR
ROOTLIBDIR,
#endif
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);
}
return -ENOTSUP;
}
int sd_path_search(uint64_t type, const char *suffix, char ***paths) {
char **l, **i, **j, **n;
int r;
assert_return(paths, -EINVAL);
if (!IN_SET(type,
SD_PATH_SEARCH_BINARIES,
SD_PATH_SEARCH_LIBRARY_PRIVATE,
SD_PATH_SEARCH_LIBRARY_ARCH,
SD_PATH_SEARCH_SHARED,
SD_PATH_SEARCH_CONFIGURATION_FACTORY,
SD_PATH_SEARCH_STATE_FACTORY,
SD_PATH_SEARCH_CONFIGURATION)) {
char *p;
r = sd_path_home(type, suffix, &p);
if (r < 0)
return r;
l = new(char*, 2);
if (!l) {
free(p);
return -ENOMEM;
}
l[0] = p;
l[1] = NULL;
*paths = l;
return 0;
}
r = get_search(type, &l);
if (r < 0)
return r;
if (!suffix) {
*paths = l;
return 0;
}
n = new(char*, strv_length(l)+1);
if (!n) {
strv_free(l);
return -ENOMEM;
}
j = n;
STRV_FOREACH(i, l) {
if (endswith(*i, "/"))
*j = strappend(*i, suffix);
else
*j = strjoin(*i, "/", suffix, NULL);
if (!*j) {
strv_free(l);
strv_free(n);
return -ENOMEM;
}
j++;
}
*j = NULL;
*paths = n;
return 0;
}