]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/path-lookup.c
relicense to LGPLv2.1 (with exceptions)
[thirdparty/systemd.git] / src / path-lookup.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
84e3543e
LP
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
84e3543e
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
84e3543e 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
84e3543e
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <assert.h>
23#include <stdlib.h>
24#include <stdio.h>
25#include <unistd.h>
26#include <errno.h>
27
28#include "util.h"
49e942b2 29#include "mkdir.h"
84e3543e
LP
30#include "strv.h"
31
32#include "path-lookup.h"
33
af2d49f7 34int user_config_home(char **config_home) {
10e87ee7
LP
35 const char *e;
36
37 if ((e = getenv("XDG_CONFIG_HOME"))) {
af2d49f7 38 if (asprintf(config_home, "%s/systemd/user", e) < 0)
10e87ee7
LP
39 return -ENOMEM;
40
41 return 1;
42 } else {
43 const char *home;
44
45 if ((home = getenv("HOME"))) {
af2d49f7 46 if (asprintf(config_home, "%s/.config/systemd/user", home) < 0)
10e87ee7
LP
47 return -ENOMEM;
48
49 return 1;
50 }
51 }
52
53 return 0;
54}
55
af2d49f7 56static char** user_dirs(void) {
f437d5d2 57 const char * const config_unit_paths[] = {
f437d5d2 58 USER_CONFIG_UNIT_PATH,
4bf2bbb6 59 "/etc/systemd/user",
fc1a2e06 60 "/run/systemd/user",
4bf2bbb6 61 NULL
f437d5d2
LP
62 };
63
64 const char * const data_unit_paths[] = {
65 "/usr/local/lib/systemd/user",
66 "/usr/local/share/systemd/user",
67 USER_DATA_UNIT_PATH,
68 "/usr/lib/systemd/user",
4bf2bbb6
LP
69 "/usr/share/systemd/user",
70 NULL
f437d5d2
LP
71 };
72
84e3543e
LP
73 const char *home, *e;
74 char *config_home = NULL, *data_home = NULL;
75 char **config_dirs = NULL, **data_dirs = NULL;
76 char **r = NULL, **t;
77
78 /* Implement the mechanisms defined in
79 *
80 * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
81 *
82 * We look in both the config and the data dirs because we
83 * want to encourage that distributors ship their unit files
84 * as data, and allow overriding as configuration.
85 */
86
af2d49f7 87 if (user_config_home(&config_home) < 0)
10e87ee7 88 goto fail;
84e3543e 89
10e87ee7 90 home = getenv("HOME");
84e3543e
LP
91
92 if ((e = getenv("XDG_CONFIG_DIRS")))
93 if (!(config_dirs = strv_split(e, ":")))
94 goto fail;
95
96 /* We don't treat /etc/xdg/systemd here as the spec
97 * suggests because we assume that that is a link to
98 * /etc/systemd/ anyway. */
99
100 if ((e = getenv("XDG_DATA_HOME"))) {
af2d49f7 101 if (asprintf(&data_home, "%s/systemd/user", e) < 0)
84e3543e
LP
102 goto fail;
103
104 } else if (home) {
af2d49f7 105 if (asprintf(&data_home, "%s/.local/share/systemd/user", home) < 0)
84e3543e
LP
106 goto fail;
107
108 /* There is really no need for two unit dirs in $HOME,
109 * except to be fully compliant with the XDG spec. We
110 * now try to link the two dirs, so that we can
111 * minimize disk seeks a little. Further down we'll
112 * then filter out this link, if it is actually is
113 * one. */
114
115 mkdir_parents(data_home, 0777);
af2d49f7 116 (void) symlink("../../../.config/systemd/user", data_home);
84e3543e
LP
117 }
118
119 if ((e = getenv("XDG_DATA_DIRS")))
120 data_dirs = strv_split(e, ":");
121 else
ef3102bf 122 data_dirs = strv_new("/usr/local/share",
ef3102bf 123 "/usr/share",
ef3102bf 124 NULL);
84e3543e
LP
125
126 if (!data_dirs)
127 goto fail;
128
129 /* Now merge everything we found. */
130 if (config_home) {
131 if (!(t = strv_append(r, config_home)))
132 goto fail;
133 strv_free(r);
134 r = t;
135 }
136
f437d5d2
LP
137 if (!strv_isempty(config_dirs)) {
138 if (!(t = strv_merge_concat(r, config_dirs, "/systemd/user")))
139 goto finish;
140 strv_free(r);
141 r = t;
142 }
84e3543e 143
f437d5d2 144 if (!(t = strv_merge(r, (char**) config_unit_paths)))
84e3543e
LP
145 goto fail;
146 strv_free(r);
147 r = t;
148
149 if (data_home) {
150 if (!(t = strv_append(r, data_home)))
151 goto fail;
152 strv_free(r);
153 r = t;
154 }
155
f437d5d2
LP
156 if (!strv_isempty(data_dirs)) {
157 if (!(t = strv_merge_concat(r, data_dirs, "/systemd/user")))
158 goto fail;
159 strv_free(r);
160 r = t;
161 }
84e3543e 162
f437d5d2 163 if (!(t = strv_merge(r, (char**) data_unit_paths)))
84e3543e
LP
164 goto fail;
165 strv_free(r);
166 r = t;
167
168 if (!strv_path_make_absolute_cwd(r))
169 goto fail;
170
171finish:
172 free(config_home);
173 strv_free(config_dirs);
174 free(data_home);
175 strv_free(data_dirs);
176
177 return r;
178
179fail:
180 strv_free(r);
181 r = NULL;
182 goto finish;
183}
184
c800e483 185int lookup_paths_init(LookupPaths *p, ManagerRunningAs running_as, bool personal) {
84e3543e
LP
186 const char *e;
187 char *t;
188
189 assert(p);
190
191 /* First priority is whatever has been passed to us via env
192 * vars */
193 if ((e = getenv("SYSTEMD_UNIT_PATH")))
194 if (!(p->unit_path = split_path_and_make_absolute(e)))
195 return -ENOMEM;
196
197 if (strv_isempty(p->unit_path)) {
198
199 /* Nothing is set, so let's figure something out. */
200 strv_free(p->unit_path);
201
af2d49f7 202 if (running_as == MANAGER_USER) {
c800e483
LP
203
204 if (personal)
205 p->unit_path = user_dirs();
206 else
207 p->unit_path = strv_new(
208 /* If you modify this you also want to modify
209 * systemduserunitpath= in systemd.pc.in, and
210 * the arrays in user_dirs() above! */
c800e483 211 USER_CONFIG_UNIT_PATH,
a5afffa1 212 "/etc/systemd/user",
fc1a2e06 213 "/run/systemd/user",
c800e483
LP
214 "/usr/local/lib/systemd/user",
215 "/usr/local/share/systemd/user",
216 USER_DATA_UNIT_PATH,
217 "/usr/lib/systemd/user",
218 "/usr/share/systemd/user",
219 NULL);
220
221 if (!p->unit_path)
84e3543e 222 return -ENOMEM;
c800e483 223
84e3543e
LP
224 } else
225 if (!(p->unit_path = strv_new(
607df95b
LP
226 /* If you modify this you also want to modify
227 * systemdsystemunitpath= in systemd.pc.in! */
c0115b1f
LP
228 SYSTEM_CONFIG_UNIT_PATH,
229 "/etc/systemd/system",
fc1a2e06 230 "/run/systemd/system",
ef3102bf 231 "/usr/local/lib/systemd/system",
0b5b0ffb 232 SYSTEM_DATA_UNIT_PATH,
b412ab3b 233 "/usr/lib/systemd/system",
283b73b3 234#ifdef HAVE_SPLIT_USR
c800e483 235 "/lib/systemd/system",
283b73b3 236#endif
84e3543e
LP
237 NULL)))
238 return -ENOMEM;
239 }
240
07459bb6
FF
241 if (p->unit_path)
242 if (!strv_path_canonicalize(p->unit_path))
243 return -ENOMEM;
244
245 strv_uniq(p->unit_path);
a9dd2082 246 strv_path_remove_empty(p->unit_path);
07459bb6
FF
247
248 if (!strv_isempty(p->unit_path)) {
249
250 if (!(t = strv_join(p->unit_path, "\n\t")))
251 return -ENOMEM;
252 log_debug("Looking for unit files in:\n\t%s", t);
253 free(t);
254 } else {
255 log_debug("Ignoring unit files.");
256 strv_free(p->unit_path);
257 p->unit_path = NULL;
258 }
259
a3d4e06d 260 if (running_as == MANAGER_SYSTEM) {
07459bb6 261#ifdef HAVE_SYSV_COMPAT
84e3543e
LP
262 /* /etc/init.d/ compatibility does not matter to users */
263
264 if ((e = getenv("SYSTEMD_SYSVINIT_PATH")))
265 if (!(p->sysvinit_path = split_path_and_make_absolute(e)))
266 return -ENOMEM;
267
268 if (strv_isempty(p->sysvinit_path)) {
269 strv_free(p->sysvinit_path);
270
271 if (!(p->sysvinit_path = strv_new(
272 SYSTEM_SYSVINIT_PATH, /* /etc/init.d/ */
273 NULL)))
274 return -ENOMEM;
275 }
276
277 if ((e = getenv("SYSTEMD_SYSVRCND_PATH")))
278 if (!(p->sysvrcnd_path = split_path_and_make_absolute(e)))
279 return -ENOMEM;
280
281 if (strv_isempty(p->sysvrcnd_path)) {
282 strv_free(p->sysvrcnd_path);
283
284 if (!(p->sysvrcnd_path = strv_new(
285 SYSTEM_SYSVRCND_PATH, /* /etc/rcN.d/ */
286 NULL)))
287 return -ENOMEM;
288 }
84e3543e 289
07459bb6
FF
290 if (p->sysvinit_path)
291 if (!strv_path_canonicalize(p->sysvinit_path))
292 return -ENOMEM;
84e3543e 293
07459bb6
FF
294 if (p->sysvrcnd_path)
295 if (!strv_path_canonicalize(p->sysvrcnd_path))
296 return -ENOMEM;
84e3543e 297
07459bb6
FF
298 strv_uniq(p->sysvinit_path);
299 strv_uniq(p->sysvrcnd_path);
84e3543e 300
a9dd2082
LP
301 strv_path_remove_empty(p->sysvinit_path);
302 strv_path_remove_empty(p->sysvrcnd_path);
303
07459bb6 304 if (!strv_isempty(p->sysvinit_path)) {
84e3543e 305
07459bb6
FF
306 if (!(t = strv_join(p->sysvinit_path, "\n\t")))
307 return -ENOMEM;
84e3543e 308
07459bb6
FF
309 log_debug("Looking for SysV init scripts in:\n\t%s", t);
310 free(t);
311 } else {
312 log_debug("Ignoring SysV init scripts.");
313 strv_free(p->sysvinit_path);
314 p->sysvinit_path = NULL;
315 }
84e3543e 316
07459bb6 317 if (!strv_isempty(p->sysvrcnd_path)) {
84e3543e 318
07459bb6
FF
319 if (!(t = strv_join(p->sysvrcnd_path, "\n\t")))
320 return -ENOMEM;
84e3543e 321
07459bb6
FF
322 log_debug("Looking for SysV rcN.d links in:\n\t%s", t);
323 free(t);
324 } else {
325 log_debug("Ignoring SysV rcN.d links.");
326 strv_free(p->sysvrcnd_path);
327 p->sysvrcnd_path = NULL;
328 }
329#else
330 log_debug("Disabled SysV init scripts and rcN.d links support");
331#endif
84e3543e
LP
332 }
333
334 return 0;
335}
336
337void lookup_paths_free(LookupPaths *p) {
338 assert(p);
339
340 strv_free(p->unit_path);
07459bb6
FF
341 p->unit_path = NULL;
342
343#ifdef HAVE_SYSV_COMPAT
84e3543e
LP
344 strv_free(p->sysvinit_path);
345 strv_free(p->sysvrcnd_path);
07459bb6
FF
346 p->sysvinit_path = p->sysvrcnd_path = NULL;
347#endif
84e3543e 348}