]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/path-lookup.c
tree-wide: sort includes
[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 "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 int lookup_paths_init(
239 LookupPaths *p,
240 ManagerRunningAs running_as,
241 bool personal,
242 const char *root_dir,
243 const char *generator,
244 const char *generator_early,
245 const char *generator_late) {
246
247 const char *e;
248 bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
249 int r;
250
251 assert(p);
252
253 /* First priority is whatever has been passed to us via env
254 * vars */
255 e = getenv("SYSTEMD_UNIT_PATH");
256 if (e) {
257 if (endswith(e, ":")) {
258 e = strndupa(e, strlen(e) - 1);
259 append = true;
260 }
261
262 /* FIXME: empty components in other places should be
263 * rejected. */
264
265 r = path_split_and_make_absolute(e, &p->unit_path);
266 if (r < 0)
267 return r;
268 } else
269 p->unit_path = NULL;
270
271 if (!p->unit_path || append) {
272 /* Let's figure something out. */
273
274 _cleanup_strv_free_ char **unit_path;
275
276 /* For the user units we include share/ in the search
277 * path in order to comply with the XDG basedir spec.
278 * For the system stuff we avoid such nonsense. OTOH
279 * we include /lib in the search path for the system
280 * stuff but avoid it for user stuff. */
281
282 if (running_as == MANAGER_USER) {
283 if (personal)
284 unit_path = user_dirs(generator, generator_early, generator_late);
285 else
286 unit_path = strv_new(
287 /* If you modify this you also want to modify
288 * systemduserunitpath= in systemd.pc.in, and
289 * the arrays in user_dirs() above! */
290 STRV_IFNOTNULL(generator_early),
291 USER_CONFIG_UNIT_PATH,
292 "/etc/systemd/user",
293 "/run/systemd/user",
294 STRV_IFNOTNULL(generator),
295 "/usr/local/lib/systemd/user",
296 "/usr/local/share/systemd/user",
297 USER_DATA_UNIT_PATH,
298 "/usr/lib/systemd/user",
299 "/usr/share/systemd/user",
300 STRV_IFNOTNULL(generator_late),
301 NULL);
302 } else
303 unit_path = strv_new(
304 /* If you modify this you also want to modify
305 * systemdsystemunitpath= in systemd.pc.in! */
306 STRV_IFNOTNULL(generator_early),
307 SYSTEM_CONFIG_UNIT_PATH,
308 "/etc/systemd/system",
309 "/run/systemd/system",
310 STRV_IFNOTNULL(generator),
311 "/usr/local/lib/systemd/system",
312 SYSTEM_DATA_UNIT_PATH,
313 "/usr/lib/systemd/system",
314 #ifdef HAVE_SPLIT_USR
315 "/lib/systemd/system",
316 #endif
317 STRV_IFNOTNULL(generator_late),
318 NULL);
319
320 if (!unit_path)
321 return -ENOMEM;
322
323 r = strv_extend_strv(&p->unit_path, unit_path, false);
324 if (r < 0)
325 return r;
326 }
327
328 if (!path_strv_resolve_uniq(p->unit_path, root_dir))
329 return -ENOMEM;
330
331 if (!strv_isempty(p->unit_path)) {
332 _cleanup_free_ char *t = strv_join(p->unit_path, "\n\t");
333 if (!t)
334 return -ENOMEM;
335 log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
336 } else {
337 log_debug("Ignoring unit files.");
338 p->unit_path = strv_free(p->unit_path);
339 }
340
341 if (running_as == MANAGER_SYSTEM) {
342 #ifdef HAVE_SYSV_COMPAT
343 /* /etc/init.d/ compatibility does not matter to users */
344
345 e = getenv("SYSTEMD_SYSVINIT_PATH");
346 if (e) {
347 r = path_split_and_make_absolute(e, &p->sysvinit_path);
348 if (r < 0)
349 return r;
350 } else
351 p->sysvinit_path = NULL;
352
353 if (strv_isempty(p->sysvinit_path)) {
354 strv_free(p->sysvinit_path);
355
356 p->sysvinit_path = strv_new(
357 SYSTEM_SYSVINIT_PATH, /* /etc/init.d/ */
358 NULL);
359 if (!p->sysvinit_path)
360 return -ENOMEM;
361 }
362
363 e = getenv("SYSTEMD_SYSVRCND_PATH");
364 if (e) {
365 r = path_split_and_make_absolute(e, &p->sysvrcnd_path);
366 if (r < 0)
367 return r;
368 } else
369 p->sysvrcnd_path = NULL;
370
371 if (strv_isempty(p->sysvrcnd_path)) {
372 strv_free(p->sysvrcnd_path);
373
374 p->sysvrcnd_path = strv_new(
375 SYSTEM_SYSVRCND_PATH, /* /etc/rcN.d/ */
376 NULL);
377 if (!p->sysvrcnd_path)
378 return -ENOMEM;
379 }
380
381 if (!path_strv_resolve_uniq(p->sysvinit_path, root_dir))
382 return -ENOMEM;
383
384 if (!path_strv_resolve_uniq(p->sysvrcnd_path, root_dir))
385 return -ENOMEM;
386
387 if (!strv_isempty(p->sysvinit_path)) {
388 _cleanup_free_ char *t = strv_join(p->sysvinit_path, "\n\t");
389 if (!t)
390 return -ENOMEM;
391 log_debug("Looking for SysV init scripts in:\n\t%s", t);
392 } else {
393 log_debug("Ignoring SysV init scripts.");
394 p->sysvinit_path = strv_free(p->sysvinit_path);
395 }
396
397 if (!strv_isempty(p->sysvrcnd_path)) {
398 _cleanup_free_ char *t =
399 strv_join(p->sysvrcnd_path, "\n\t");
400 if (!t)
401 return -ENOMEM;
402
403 log_debug("Looking for SysV rcN.d links in:\n\t%s", t);
404 } else {
405 log_debug("Ignoring SysV rcN.d links.");
406 p->sysvrcnd_path = strv_free(p->sysvrcnd_path);
407 }
408 #else
409 log_debug("SysV init scripts and rcN.d links support disabled");
410 #endif
411 }
412
413 return 0;
414 }
415
416 void lookup_paths_free(LookupPaths *p) {
417 assert(p);
418
419 p->unit_path = strv_free(p->unit_path);
420
421 #ifdef HAVE_SYSV_COMPAT
422 p->sysvinit_path = strv_free(p->sysvinit_path);
423 p->sysvrcnd_path = strv_free(p->sysvrcnd_path);
424 #endif
425 }
426
427 int lookup_paths_init_from_scope(LookupPaths *paths,
428 UnitFileScope scope,
429 const char *root_dir) {
430 assert(paths);
431 assert(scope >= 0);
432 assert(scope < _UNIT_FILE_SCOPE_MAX);
433
434 zero(*paths);
435
436 return lookup_paths_init(paths,
437 scope == UNIT_FILE_SYSTEM ? MANAGER_SYSTEM : MANAGER_USER,
438 scope == UNIT_FILE_USER,
439 root_dir,
440 NULL, NULL, NULL);
441 }