]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/path-lookup.c
ec3f5b53f4151332e2d39423be647834587188cd
[thirdparty/systemd.git] / src / shared / path-lookup.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2010 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <errno.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "alloc-util.h"
26 #include "install.h"
27 #include "log.h"
28 #include "macro.h"
29 #include "path-lookup.h"
30 #include "path-util.h"
31 #include "string-util.h"
32 #include "strv.h"
33 #include "util.h"
34
35 int user_config_home(char **config_home) {
36 const char *e;
37 char *r;
38
39 e = getenv("XDG_CONFIG_HOME");
40 if (e) {
41 r = strappend(e, "/systemd/user");
42 if (!r)
43 return -ENOMEM;
44
45 *config_home = r;
46 return 1;
47 } else {
48 const char *home;
49
50 home = getenv("HOME");
51 if (home) {
52 r = strappend(home, "/.config/systemd/user");
53 if (!r)
54 return -ENOMEM;
55
56 *config_home = r;
57 return 1;
58 }
59 }
60
61 return 0;
62 }
63
64 int user_runtime_dir(char **runtime_dir) {
65 const char *e;
66 char *r;
67
68 e = getenv("XDG_RUNTIME_DIR");
69 if (e) {
70 r = strappend(e, "/systemd/user");
71 if (!r)
72 return -ENOMEM;
73
74 *runtime_dir = r;
75 return 1;
76 }
77
78 return 0;
79 }
80
81 static int user_data_home_dir(char **dir, const char *suffix) {
82 const char *e;
83 char *res;
84
85 /* We don't treat /etc/xdg/systemd here as the spec
86 * suggests because we assume that that is a link to
87 * /etc/systemd/ anyway. */
88
89 e = getenv("XDG_DATA_HOME");
90 if (e)
91 res = strappend(e, suffix);
92 else {
93 const char *home;
94
95 home = getenv("HOME");
96 if (home)
97 res = strjoin(home, "/.local/share", suffix, NULL);
98 else
99 return 0;
100 }
101 if (!res)
102 return -ENOMEM;
103
104 *dir = res;
105 return 0;
106 }
107
108 static char** user_dirs(
109 const char *generator,
110 const char *generator_early,
111 const char *generator_late) {
112
113 const char * const config_unit_paths[] = {
114 USER_CONFIG_UNIT_PATH,
115 "/etc/systemd/user",
116 NULL
117 };
118
119 const char * const runtime_unit_path = "/run/systemd/user";
120
121 const char * const data_unit_paths[] = {
122 "/usr/local/lib/systemd/user",
123 "/usr/local/share/systemd/user",
124 USER_DATA_UNIT_PATH,
125 "/usr/lib/systemd/user",
126 "/usr/share/systemd/user",
127 NULL
128 };
129
130 const char *e;
131 _cleanup_free_ char *config_home = NULL, *runtime_dir = NULL, *data_home = NULL;
132 _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
133 _cleanup_free_ char **res = NULL;
134 char **tmp;
135 int r;
136
137 /* Implement the mechanisms defined in
138 *
139 * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
140 *
141 * We look in both the config and the data dirs because we
142 * want to encourage that distributors ship their unit files
143 * as data, and allow overriding as configuration.
144 */
145
146 if (user_config_home(&config_home) < 0)
147 return NULL;
148
149 if (user_runtime_dir(&runtime_dir) < 0)
150 return NULL;
151
152 e = getenv("XDG_CONFIG_DIRS");
153 if (e) {
154 config_dirs = strv_split(e, ":");
155 if (!config_dirs)
156 return NULL;
157 }
158
159 r = user_data_home_dir(&data_home, "/systemd/user");
160 if (r < 0)
161 return NULL;
162
163 e = getenv("XDG_DATA_DIRS");
164 if (e)
165 data_dirs = strv_split(e, ":");
166 else
167 data_dirs = strv_new("/usr/local/share",
168 "/usr/share",
169 NULL);
170 if (!data_dirs)
171 return NULL;
172
173 /* Now merge everything we found. */
174 if (generator_early)
175 if (strv_extend(&res, generator_early) < 0)
176 return NULL;
177
178 if (config_home)
179 if (strv_extend(&res, config_home) < 0)
180 return NULL;
181
182 if (!strv_isempty(config_dirs))
183 if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0)
184 return NULL;
185
186 if (strv_extend_strv(&res, (char**) config_unit_paths, false) < 0)
187 return NULL;
188
189 if (runtime_dir)
190 if (strv_extend(&res, runtime_dir) < 0)
191 return NULL;
192
193 if (strv_extend(&res, runtime_unit_path) < 0)
194 return NULL;
195
196 if (generator)
197 if (strv_extend(&res, generator) < 0)
198 return NULL;
199
200 if (data_home)
201 if (strv_extend(&res, data_home) < 0)
202 return NULL;
203
204 if (!strv_isempty(data_dirs))
205 if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0)
206 return NULL;
207
208 if (strv_extend_strv(&res, (char**) data_unit_paths, false) < 0)
209 return NULL;
210
211 if (generator_late)
212 if (strv_extend(&res, generator_late) < 0)
213 return NULL;
214
215 if (path_strv_make_absolute_cwd(res) < 0)
216 return NULL;
217
218 tmp = res;
219 res = NULL;
220 return tmp;
221 }
222
223 char **generator_paths(ManagerRunningAs running_as) {
224 if (running_as == MANAGER_USER)
225 return strv_new("/run/systemd/user-generators",
226 "/etc/systemd/user-generators",
227 "/usr/local/lib/systemd/user-generators",
228 USER_GENERATOR_PATH,
229 NULL);
230 else
231 return strv_new("/run/systemd/system-generators",
232 "/etc/systemd/system-generators",
233 "/usr/local/lib/systemd/system-generators",
234 SYSTEM_GENERATOR_PATH,
235 NULL);
236 }
237
238 static int acquire_generator_dirs(
239 ManagerRunningAs running_as,
240 char **generator,
241 char **generator_early,
242 char **generator_late) {
243
244 _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL;
245 const char *prefix;
246
247 assert(generator);
248 assert(generator_early);
249 assert(generator_late);
250
251 if (running_as == MANAGER_SYSTEM)
252 prefix = "/run/systemd/";
253 else {
254 const char *e;
255
256 assert(running_as == MANAGER_USER);
257
258 e = getenv("XDG_RUNTIME_DIR");
259 if (!e)
260 return -EINVAL;
261
262 prefix = strjoina(e, "/systemd/", NULL);
263 }
264
265 x = strappend(prefix, "generator");
266 if (!x)
267 return -ENOMEM;
268
269 y = strappend(prefix, "generator.early");
270 if (!y)
271 return -ENOMEM;
272
273 z = strappend(prefix, "generator.late");
274 if (!z)
275 return -ENOMEM;
276
277 *generator = x;
278 *generator_early = y;
279 *generator_late = z;
280
281 x = y = z = NULL;
282 return 0;
283 }
284
285 static int patch_root_prefix(char **p, const char *root_dir) {
286 char *c;
287
288 assert(p);
289
290 if (!*p)
291 return 0;
292
293 if (isempty(root_dir) || path_equal(root_dir, "/"))
294 return 0;
295
296 c = prefix_root(root_dir, *p);
297 if (!c)
298 return -ENOMEM;
299
300 free(*p);
301 *p = c;
302
303 return 0;
304 }
305
306 int lookup_paths_init(
307 LookupPaths *p,
308 ManagerRunningAs running_as,
309 bool personal,
310 const char *root_dir) {
311
312 _cleanup_free_ char *generator = NULL, *generator_early = NULL, *generator_late = NULL;
313 bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
314 char **l = NULL;
315 const char *e;
316 int r;
317
318 assert(p);
319 assert(running_as >= 0);
320 assert(running_as < _MANAGER_RUNNING_AS_MAX);
321
322 r = acquire_generator_dirs(running_as, &generator, &generator_early, &generator_late);
323 if (r < 0)
324 return r;
325
326 /* First priority is whatever has been passed to us via env
327 * vars */
328 e = getenv("SYSTEMD_UNIT_PATH");
329 if (e) {
330 const char *k;
331
332 k = endswith(e, ":");
333 if (k) {
334 e = strndupa(e, k - e);
335 append = true;
336 }
337
338 /* FIXME: empty components in other places should be
339 * rejected. */
340
341 r = path_split_and_make_absolute(e, &l);
342 if (r < 0)
343 return r;
344 } else
345 l = NULL;
346
347 if (!l || append) {
348 /* Let's figure something out. */
349
350 _cleanup_strv_free_ char **add = NULL;
351
352 /* For the user units we include share/ in the search
353 * path in order to comply with the XDG basedir spec.
354 * For the system stuff we avoid such nonsense. OTOH
355 * we include /lib in the search path for the system
356 * stuff but avoid it for user stuff. */
357
358 if (running_as == MANAGER_USER) {
359 if (personal)
360 add = user_dirs(generator, generator_early, generator_late);
361 else
362 add = strv_new(
363 /* If you modify this you also want to modify
364 * systemduserunitpath= in systemd.pc.in, and
365 * the arrays in user_dirs() above! */
366 generator_early,
367 USER_CONFIG_UNIT_PATH,
368 "/etc/systemd/user",
369 "/run/systemd/user",
370 generator,
371 "/usr/local/lib/systemd/user",
372 "/usr/local/share/systemd/user",
373 USER_DATA_UNIT_PATH,
374 "/usr/lib/systemd/user",
375 "/usr/share/systemd/user",
376 generator_late,
377 NULL);
378 } else
379 add = strv_new(
380 /* If you modify this you also want to modify
381 * systemdsystemunitpath= in systemd.pc.in! */
382 generator_early,
383 SYSTEM_CONFIG_UNIT_PATH,
384 "/etc/systemd/system",
385 "/run/systemd/system",
386 generator,
387 "/usr/local/lib/systemd/system",
388 SYSTEM_DATA_UNIT_PATH,
389 "/usr/lib/systemd/system",
390 #ifdef HAVE_SPLIT_USR
391 "/lib/systemd/system",
392 #endif
393 generator_late,
394 NULL);
395
396 if (!add)
397 return -ENOMEM;
398
399 if (l) {
400 r = strv_extend_strv(&l, add, false);
401 if (r < 0)
402 return r;
403 } else {
404 l = add;
405 add = NULL;
406 }
407 }
408
409 r = patch_root_prefix(&generator, root_dir);
410 if (r < 0)
411 return r;
412 r = patch_root_prefix(&generator_early, root_dir);
413 if (r < 0)
414 return r;
415 r = patch_root_prefix(&generator_late, root_dir);
416 if (r < 0)
417 return r;
418
419 if (!path_strv_resolve_uniq(l, root_dir))
420 return -ENOMEM;
421
422 if (strv_isempty(l)) {
423 log_debug("Ignoring unit files.");
424 l = strv_free(l);
425 } else {
426 _cleanup_free_ char *t;
427
428 t = strv_join(l, "\n\t");
429 if (!t)
430 return -ENOMEM;
431
432 log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
433 }
434
435 p->search_path = l;
436 l = NULL;
437
438 p->generator = generator;
439 p->generator_early = generator_early;
440 p->generator_late = generator_late;
441 generator = generator_early = generator_late = NULL;
442
443 return 0;
444 }
445
446 void lookup_paths_free(LookupPaths *p) {
447 if (!p)
448 return;
449
450 p->search_path = strv_free(p->search_path);
451 p->generator = mfree(p->generator);
452 p->generator_early = mfree(p->generator_early);
453 p->generator_late = mfree(p->generator_late);
454 }
455
456 int lookup_paths_init_from_scope(
457 LookupPaths *p,
458 UnitFileScope scope,
459 const char *root_dir) {
460
461 assert(p);
462 assert(scope >= 0);
463 assert(scope < _UNIT_FILE_SCOPE_MAX);
464
465 return lookup_paths_init(
466 p,
467 scope == UNIT_FILE_SYSTEM ? MANAGER_SYSTEM : MANAGER_USER,
468 scope == UNIT_FILE_USER,
469 root_dir);
470 }