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