]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/path-lookup.c
util: split out escaping code into escape.[ch]
[thirdparty/systemd.git] / src / shared / path-lookup.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
84e3543e
LP
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
5430f7f2
LP
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
84e3543e
LP
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
5430f7f2 16 Lesser General Public License for more details.
84e3543e 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
84e3543e
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
84e3543e
LP
22#include <stdlib.h>
23#include <stdio.h>
67445f4e 24#include <string.h>
84e3543e
LP
25#include <errno.h>
26
27#include "util.h"
28#include "strv.h"
9eb977db 29#include "path-util.h"
84e3543e 30#include "path-lookup.h"
a8ffe6fb 31#include "install.h"
84e3543e 32
af2d49f7 33int user_config_home(char **config_home) {
10e87ee7 34 const char *e;
26d04f86 35 char *r;
10e87ee7 36
07719a21
LP
37 e = getenv("XDG_CONFIG_HOME");
38 if (e) {
26d04f86
LP
39 r = strappend(e, "/systemd/user");
40 if (!r)
10e87ee7
LP
41 return -ENOMEM;
42
26d04f86 43 *config_home = r;
10e87ee7
LP
44 return 1;
45 } else {
46 const char *home;
47
07719a21
LP
48 home = getenv("HOME");
49 if (home) {
26d04f86
LP
50 r = strappend(home, "/.config/systemd/user");
51 if (!r)
10e87ee7
LP
52 return -ENOMEM;
53
26d04f86 54 *config_home = r;
10e87ee7
LP
55 return 1;
56 }
57 }
58
59 return 0;
60}
61
4d5dec23 62int user_runtime_dir(char **runtime_dir) {
718880ba
SA
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
4d5dec23 72 *runtime_dir = r;
718880ba
SA
73 return 1;
74 }
75
76 return 0;
77}
78
e801700e
ZJS
79static 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
07719a21
LP
106static char** user_dirs(
107 const char *generator,
108 const char *generator_early,
109 const char *generator_late) {
110
f437d5d2 111 const char * const config_unit_paths[] = {
f437d5d2 112 USER_CONFIG_UNIT_PATH,
4bf2bbb6
LP
113 "/etc/systemd/user",
114 NULL
f437d5d2
LP
115 };
116
718880ba
SA
117 const char * const runtime_unit_path = "/run/systemd/user";
118
f437d5d2
LP
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",
4bf2bbb6
LP
124 "/usr/share/systemd/user",
125 NULL
f437d5d2
LP
126 };
127
e801700e 128 const char *e;
4d5dec23 129 _cleanup_free_ char *config_home = NULL, *runtime_dir = NULL, *data_home = NULL;
e3e45d4f 130 _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
e801700e
ZJS
131 _cleanup_free_ char **res = NULL;
132 char **tmp;
133 int r;
84e3543e
LP
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
af2d49f7 144 if (user_config_home(&config_home) < 0)
e801700e 145 return NULL;
84e3543e 146
4d5dec23 147 if (user_runtime_dir(&runtime_dir) < 0)
e801700e 148 return NULL;
84e3543e 149
07719a21
LP
150 e = getenv("XDG_CONFIG_DIRS");
151 if (e) {
152 config_dirs = strv_split(e, ":");
153 if (!config_dirs)
e801700e 154 return NULL;
07719a21 155 }
84e3543e 156
e801700e
ZJS
157 r = user_data_home_dir(&data_home, "/systemd/user");
158 if (r < 0)
159 return NULL;
84e3543e 160
07719a21
LP
161 e = getenv("XDG_DATA_DIRS");
162 if (e)
84e3543e
LP
163 data_dirs = strv_split(e, ":");
164 else
ef3102bf 165 data_dirs = strv_new("/usr/local/share",
ef3102bf 166 "/usr/share",
ef3102bf 167 NULL);
84e3543e 168 if (!data_dirs)
e801700e 169 return NULL;
84e3543e
LP
170
171 /* Now merge everything we found. */
e3e45d4f 172 if (generator_early)
e801700e
ZJS
173 if (strv_extend(&res, generator_early) < 0)
174 return NULL;
07719a21 175
e3e45d4f 176 if (config_home)
e801700e
ZJS
177 if (strv_extend(&res, config_home) < 0)
178 return NULL;
84e3543e 179
aa08982d 180 if (!strv_isempty(config_dirs))
e801700e
ZJS
181 if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0)
182 return NULL;
718880ba 183
e287086b 184 if (strv_extend_strv(&res, (char**) config_unit_paths, false) < 0)
e801700e 185 return NULL;
718880ba 186
aa08982d 187 if (runtime_dir)
e801700e
ZJS
188 if (strv_extend(&res, runtime_dir) < 0)
189 return NULL;
84e3543e 190
e801700e
ZJS
191 if (strv_extend(&res, runtime_unit_path) < 0)
192 return NULL;
84e3543e 193
e3e45d4f 194 if (generator)
e801700e
ZJS
195 if (strv_extend(&res, generator) < 0)
196 return NULL;
07719a21 197
e3e45d4f 198 if (data_home)
e801700e
ZJS
199 if (strv_extend(&res, data_home) < 0)
200 return NULL;
84e3543e 201
e3e45d4f 202 if (!strv_isempty(data_dirs))
e801700e
ZJS
203 if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0)
204 return NULL;
84e3543e 205
e287086b 206 if (strv_extend_strv(&res, (char**) data_unit_paths, false) < 0)
e801700e 207 return NULL;
84e3543e 208
e3e45d4f 209 if (generator_late)
e801700e
ZJS
210 if (strv_extend(&res, generator_late) < 0)
211 return NULL;
07719a21 212
0f474365 213 if (path_strv_make_absolute_cwd(res) < 0)
e801700e 214 return NULL;
84e3543e 215
e801700e
ZJS
216 tmp = res;
217 res = NULL;
218 return tmp;
219}
84e3543e 220
b2c23da8
LP
221char **generator_paths(ManagerRunningAs running_as) {
222 if (running_as == MANAGER_USER)
33e1e5a7
ZJS
223 return strv_new("/run/systemd/user-generators",
224 "/etc/systemd/user-generators",
e801700e
ZJS
225 "/usr/local/lib/systemd/user-generators",
226 USER_GENERATOR_PATH,
227 NULL);
228 else
33e1e5a7
ZJS
229 return strv_new("/run/systemd/system-generators",
230 "/etc/systemd/system-generators",
e801700e
ZJS
231 "/usr/local/lib/systemd/system-generators",
232 SYSTEM_GENERATOR_PATH,
233 NULL);
84e3543e
LP
234}
235
07719a21
LP
236int lookup_paths_init(
237 LookupPaths *p,
b2c23da8 238 ManagerRunningAs running_as,
07719a21 239 bool personal,
12ed81d9 240 const char *root_dir,
07719a21
LP
241 const char *generator,
242 const char *generator_early,
243 const char *generator_late) {
244
84e3543e 245 const char *e;
cf7d80a5 246 bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
0f474365 247 int r;
84e3543e
LP
248
249 assert(p);
250
251 /* First priority is whatever has been passed to us via env
252 * vars */
07719a21
LP
253 e = getenv("SYSTEMD_UNIT_PATH");
254 if (e) {
cf7d80a5
ZJS
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
0f474365
LP
263 r = path_split_and_make_absolute(e, &p->unit_path);
264 if (r < 0)
265 return r;
07719a21
LP
266 } else
267 p->unit_path = NULL;
84e3543e 268
cf7d80a5
ZJS
269 if (!p->unit_path || append) {
270 /* Let's figure something out. */
271
1d3bc017 272 _cleanup_strv_free_ char **unit_path;
84e3543e 273
07719a21 274 /* For the user units we include share/ in the search
cf7d80a5
ZJS
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. */
07719a21 279
b2c23da8 280 if (running_as == MANAGER_USER) {
c800e483 281 if (personal)
cf7d80a5 282 unit_path = user_dirs(generator, generator_early, generator_late);
c800e483 283 else
cf7d80a5 284 unit_path = strv_new(
07719a21 285 /* If you modify this you also want to modify
cf7d80a5
ZJS
286 * systemduserunitpath= in systemd.pc.in, and
287 * the arrays in user_dirs() above! */
07719a21 288 STRV_IFNOTNULL(generator_early),
cf7d80a5
ZJS
289 USER_CONFIG_UNIT_PATH,
290 "/etc/systemd/user",
291 "/run/systemd/user",
07719a21 292 STRV_IFNOTNULL(generator),
cf7d80a5
ZJS
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",
07719a21
LP
298 STRV_IFNOTNULL(generator_late),
299 NULL);
cf7d80a5
ZJS
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);
07719a21 317
cf7d80a5
ZJS
318 if (!unit_path)
319 return -ENOMEM;
320
e287086b 321 r = strv_extend_strv(&p->unit_path, unit_path, false);
cf7d80a5
ZJS
322 if (r < 0)
323 return r;
84e3543e
LP
324 }
325
7d8da2c9 326 if (!path_strv_resolve_uniq(p->unit_path, root_dir))
07719a21 327 return -ENOMEM;
07459bb6 328
07459bb6 329 if (!strv_isempty(p->unit_path)) {
7fd1b19b 330 _cleanup_free_ char *t = strv_join(p->unit_path, "\n\t");
07719a21 331 if (!t)
07459bb6 332 return -ENOMEM;
242c4e1c 333 log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
07459bb6 334 } else {
242c4e1c 335 log_debug("Ignoring unit files.");
6796073e 336 p->unit_path = strv_free(p->unit_path);
07459bb6
FF
337 }
338
b2c23da8 339 if (running_as == MANAGER_SYSTEM) {
07459bb6 340#ifdef HAVE_SYSV_COMPAT
84e3543e
LP
341 /* /etc/init.d/ compatibility does not matter to users */
342
07719a21
LP
343 e = getenv("SYSTEMD_SYSVINIT_PATH");
344 if (e) {
0f474365
LP
345 r = path_split_and_make_absolute(e, &p->sysvinit_path);
346 if (r < 0)
347 return r;
07719a21
LP
348 } else
349 p->sysvinit_path = NULL;
84e3543e
LP
350
351 if (strv_isempty(p->sysvinit_path)) {
352 strv_free(p->sysvinit_path);
353
07719a21
LP
354 p->sysvinit_path = strv_new(
355 SYSTEM_SYSVINIT_PATH, /* /etc/init.d/ */
356 NULL);
357 if (!p->sysvinit_path)
84e3543e
LP
358 return -ENOMEM;
359 }
360
07719a21
LP
361 e = getenv("SYSTEMD_SYSVRCND_PATH");
362 if (e) {
0f474365
LP
363 r = path_split_and_make_absolute(e, &p->sysvrcnd_path);
364 if (r < 0)
365 return r;
07719a21
LP
366 } else
367 p->sysvrcnd_path = NULL;
84e3543e
LP
368
369 if (strv_isempty(p->sysvrcnd_path)) {
370 strv_free(p->sysvrcnd_path);
371
07719a21
LP
372 p->sysvrcnd_path = strv_new(
373 SYSTEM_SYSVRCND_PATH, /* /etc/rcN.d/ */
374 NULL);
375 if (!p->sysvrcnd_path)
84e3543e
LP
376 return -ENOMEM;
377 }
84e3543e 378
7d8da2c9 379 if (!path_strv_resolve_uniq(p->sysvinit_path, root_dir))
07719a21 380 return -ENOMEM;
84e3543e 381
7d8da2c9 382 if (!path_strv_resolve_uniq(p->sysvrcnd_path, root_dir))
07719a21 383 return -ENOMEM;
84e3543e 384
07459bb6 385 if (!strv_isempty(p->sysvinit_path)) {
7fd1b19b 386 _cleanup_free_ char *t = strv_join(p->sysvinit_path, "\n\t");
07719a21 387 if (!t)
07459bb6 388 return -ENOMEM;
242c4e1c 389 log_debug("Looking for SysV init scripts in:\n\t%s", t);
07459bb6 390 } else {
242c4e1c 391 log_debug("Ignoring SysV init scripts.");
6796073e 392 p->sysvinit_path = strv_free(p->sysvinit_path);
07459bb6 393 }
84e3543e 394
07459bb6 395 if (!strv_isempty(p->sysvrcnd_path)) {
7fd1b19b 396 _cleanup_free_ char *t =
7ad94c71 397 strv_join(p->sysvrcnd_path, "\n\t");
07719a21 398 if (!t)
07459bb6 399 return -ENOMEM;
84e3543e 400
07459bb6 401 log_debug("Looking for SysV rcN.d links in:\n\t%s", t);
07459bb6
FF
402 } else {
403 log_debug("Ignoring SysV rcN.d links.");
6796073e 404 p->sysvrcnd_path = strv_free(p->sysvrcnd_path);
07459bb6
FF
405 }
406#else
242c4e1c 407 log_debug("SysV init scripts and rcN.d links support disabled");
07459bb6 408#endif
84e3543e
LP
409 }
410
411 return 0;
412}
413
414void lookup_paths_free(LookupPaths *p) {
415 assert(p);
416
6796073e 417 p->unit_path = strv_free(p->unit_path);
07459bb6
FF
418
419#ifdef HAVE_SYSV_COMPAT
0f474365
LP
420 p->sysvinit_path = strv_free(p->sysvinit_path);
421 p->sysvrcnd_path = strv_free(p->sysvrcnd_path);
07459bb6 422#endif
84e3543e 423}
60d27f19
IS
424
425int 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,
b2c23da8 435 scope == UNIT_FILE_SYSTEM ? MANAGER_SYSTEM : MANAGER_USER,
60d27f19
IS
436 scope == UNIT_FILE_USER,
437 root_dir,
438 NULL, NULL, NULL);
439}