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