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