Systemd/src/path-lookup.c
Lennart Poettering 5430f7f2bc relicense to LGPLv2.1 (with exceptions)
We finally got the OK from all contributors with non-trivial commits to
relicense systemd from GPL2+ to LGPL2.1+.

Some udev bits continue to be GPL2+ for now, but we are looking into
relicensing them too, to allow free copy/paste of all code within
systemd.

The bits that used to be MIT continue to be MIT.

The big benefit of the relicensing is that closed source code may now
link against libsystemd-login.so and friends.
2012-04-12 00:24:39 +02:00

349 lines
12 KiB
C

/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include "util.h"
#include "mkdir.h"
#include "strv.h"
#include "path-lookup.h"
int user_config_home(char **config_home) {
const char *e;
if ((e = getenv("XDG_CONFIG_HOME"))) {
if (asprintf(config_home, "%s/systemd/user", e) < 0)
return -ENOMEM;
return 1;
} else {
const char *home;
if ((home = getenv("HOME"))) {
if (asprintf(config_home, "%s/.config/systemd/user", home) < 0)
return -ENOMEM;
return 1;
}
}
return 0;
}
static char** user_dirs(void) {
const char * const config_unit_paths[] = {
USER_CONFIG_UNIT_PATH,
"/etc/systemd/user",
"/run/systemd/user",
NULL
};
const char * const data_unit_paths[] = {
"/usr/local/lib/systemd/user",
"/usr/local/share/systemd/user",
USER_DATA_UNIT_PATH,
"/usr/lib/systemd/user",
"/usr/share/systemd/user",
NULL
};
const char *home, *e;
char *config_home = NULL, *data_home = NULL;
char **config_dirs = NULL, **data_dirs = NULL;
char **r = NULL, **t;
/* Implement the mechanisms defined in
*
* http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
*
* We look in both the config and the data dirs because we
* want to encourage that distributors ship their unit files
* as data, and allow overriding as configuration.
*/
if (user_config_home(&config_home) < 0)
goto fail;
home = getenv("HOME");
if ((e = getenv("XDG_CONFIG_DIRS")))
if (!(config_dirs = strv_split(e, ":")))
goto fail;
/* We don't treat /etc/xdg/systemd here as the spec
* suggests because we assume that that is a link to
* /etc/systemd/ anyway. */
if ((e = getenv("XDG_DATA_HOME"))) {
if (asprintf(&data_home, "%s/systemd/user", e) < 0)
goto fail;
} else if (home) {
if (asprintf(&data_home, "%s/.local/share/systemd/user", home) < 0)
goto fail;
/* There is really no need for two unit dirs in $HOME,
* except to be fully compliant with the XDG spec. We
* now try to link the two dirs, so that we can
* minimize disk seeks a little. Further down we'll
* then filter out this link, if it is actually is
* one. */
mkdir_parents(data_home, 0777);
(void) symlink("../../../.config/systemd/user", data_home);
}
if ((e = getenv("XDG_DATA_DIRS")))
data_dirs = strv_split(e, ":");
else
data_dirs = strv_new("/usr/local/share",
"/usr/share",
NULL);
if (!data_dirs)
goto fail;
/* Now merge everything we found. */
if (config_home) {
if (!(t = strv_append(r, config_home)))
goto fail;
strv_free(r);
r = t;
}
if (!strv_isempty(config_dirs)) {
if (!(t = strv_merge_concat(r, config_dirs, "/systemd/user")))
goto finish;
strv_free(r);
r = t;
}
if (!(t = strv_merge(r, (char**) config_unit_paths)))
goto fail;
strv_free(r);
r = t;
if (data_home) {
if (!(t = strv_append(r, data_home)))
goto fail;
strv_free(r);
r = t;
}
if (!strv_isempty(data_dirs)) {
if (!(t = strv_merge_concat(r, data_dirs, "/systemd/user")))
goto fail;
strv_free(r);
r = t;
}
if (!(t = strv_merge(r, (char**) data_unit_paths)))
goto fail;
strv_free(r);
r = t;
if (!strv_path_make_absolute_cwd(r))
goto fail;
finish:
free(config_home);
strv_free(config_dirs);
free(data_home);
strv_free(data_dirs);
return r;
fail:
strv_free(r);
r = NULL;
goto finish;
}
int lookup_paths_init(LookupPaths *p, ManagerRunningAs running_as, bool personal) {
const char *e;
char *t;
assert(p);
/* First priority is whatever has been passed to us via env
* vars */
if ((e = getenv("SYSTEMD_UNIT_PATH")))
if (!(p->unit_path = split_path_and_make_absolute(e)))
return -ENOMEM;
if (strv_isempty(p->unit_path)) {
/* Nothing is set, so let's figure something out. */
strv_free(p->unit_path);
if (running_as == MANAGER_USER) {
if (personal)
p->unit_path = user_dirs();
else
p->unit_path = strv_new(
/* If you modify this you also want to modify
* systemduserunitpath= in systemd.pc.in, and
* the arrays in user_dirs() above! */
USER_CONFIG_UNIT_PATH,
"/etc/systemd/user",
"/run/systemd/user",
"/usr/local/lib/systemd/user",
"/usr/local/share/systemd/user",
USER_DATA_UNIT_PATH,
"/usr/lib/systemd/user",
"/usr/share/systemd/user",
NULL);
if (!p->unit_path)
return -ENOMEM;
} else
if (!(p->unit_path = strv_new(
/* If you modify this you also want to modify
* systemdsystemunitpath= in systemd.pc.in! */
SYSTEM_CONFIG_UNIT_PATH,
"/etc/systemd/system",
"/run/systemd/system",
"/usr/local/lib/systemd/system",
SYSTEM_DATA_UNIT_PATH,
"/usr/lib/systemd/system",
#ifdef HAVE_SPLIT_USR
"/lib/systemd/system",
#endif
NULL)))
return -ENOMEM;
}
if (p->unit_path)
if (!strv_path_canonicalize(p->unit_path))
return -ENOMEM;
strv_uniq(p->unit_path);
strv_path_remove_empty(p->unit_path);
if (!strv_isempty(p->unit_path)) {
if (!(t = strv_join(p->unit_path, "\n\t")))
return -ENOMEM;
log_debug("Looking for unit files in:\n\t%s", t);
free(t);
} else {
log_debug("Ignoring unit files.");
strv_free(p->unit_path);
p->unit_path = NULL;
}
if (running_as == MANAGER_SYSTEM) {
#ifdef HAVE_SYSV_COMPAT
/* /etc/init.d/ compatibility does not matter to users */
if ((e = getenv("SYSTEMD_SYSVINIT_PATH")))
if (!(p->sysvinit_path = split_path_and_make_absolute(e)))
return -ENOMEM;
if (strv_isempty(p->sysvinit_path)) {
strv_free(p->sysvinit_path);
if (!(p->sysvinit_path = strv_new(
SYSTEM_SYSVINIT_PATH, /* /etc/init.d/ */
NULL)))
return -ENOMEM;
}
if ((e = getenv("SYSTEMD_SYSVRCND_PATH")))
if (!(p->sysvrcnd_path = split_path_and_make_absolute(e)))
return -ENOMEM;
if (strv_isempty(p->sysvrcnd_path)) {
strv_free(p->sysvrcnd_path);
if (!(p->sysvrcnd_path = strv_new(
SYSTEM_SYSVRCND_PATH, /* /etc/rcN.d/ */
NULL)))
return -ENOMEM;
}
if (p->sysvinit_path)
if (!strv_path_canonicalize(p->sysvinit_path))
return -ENOMEM;
if (p->sysvrcnd_path)
if (!strv_path_canonicalize(p->sysvrcnd_path))
return -ENOMEM;
strv_uniq(p->sysvinit_path);
strv_uniq(p->sysvrcnd_path);
strv_path_remove_empty(p->sysvinit_path);
strv_path_remove_empty(p->sysvrcnd_path);
if (!strv_isempty(p->sysvinit_path)) {
if (!(t = strv_join(p->sysvinit_path, "\n\t")))
return -ENOMEM;
log_debug("Looking for SysV init scripts in:\n\t%s", t);
free(t);
} else {
log_debug("Ignoring SysV init scripts.");
strv_free(p->sysvinit_path);
p->sysvinit_path = NULL;
}
if (!strv_isempty(p->sysvrcnd_path)) {
if (!(t = strv_join(p->sysvrcnd_path, "\n\t")))
return -ENOMEM;
log_debug("Looking for SysV rcN.d links in:\n\t%s", t);
free(t);
} else {
log_debug("Ignoring SysV rcN.d links.");
strv_free(p->sysvrcnd_path);
p->sysvrcnd_path = NULL;
}
#else
log_debug("Disabled SysV init scripts and rcN.d links support");
#endif
}
return 0;
}
void lookup_paths_free(LookupPaths *p) {
assert(p);
strv_free(p->unit_path);
p->unit_path = NULL;
#ifdef HAVE_SYSV_COMPAT
strv_free(p->sysvinit_path);
strv_free(p->sysvrcnd_path);
p->sysvinit_path = p->sysvrcnd_path = NULL;
#endif
}