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