]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-path/sd-path.c
license: LGPL-2.1+ -> LGPL-2.1-or-later
[thirdparty/systemd.git] / src / libsystemd / sd-path / sd-path.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "sd-path.h"
4
5 #include "alloc-util.h"
6 #include "architecture.h"
7 #include "fd-util.h"
8 #include "fileio.h"
9 #include "fs-util.h"
10 #include "path-lookup.h"
11 #include "path-util.h"
12 #include "string-util.h"
13 #include "strv.h"
14 #include "user-util.h"
15 #include "util.h"
16
17 static int from_environment(const char *envname, const char *fallback, const char **ret) {
18 assert(ret);
19
20 if (envname) {
21 const char *e;
22
23 e = secure_getenv(envname);
24 if (e && path_is_absolute(e)) {
25 *ret = e;
26 return 0;
27 }
28 }
29
30 if (fallback) {
31 *ret = fallback;
32 return 0;
33 }
34
35 return -ENXIO;
36 }
37
38 static int from_home_dir(const char *envname, const char *suffix, char **buffer, const char **ret) {
39 _cleanup_free_ char *h = NULL;
40 char *cc = NULL;
41 int r;
42
43 assert(suffix);
44 assert(buffer);
45 assert(ret);
46
47 if (envname) {
48 const char *e = NULL;
49
50 e = secure_getenv(envname);
51 if (e && path_is_absolute(e)) {
52 *ret = e;
53 return 0;
54 }
55 }
56
57 r = get_home_dir(&h);
58 if (r < 0)
59 return r;
60
61 cc = path_join(h, suffix);
62 if (!cc)
63 return -ENOMEM;
64
65 *buffer = cc;
66 *ret = cc;
67 return 0;
68 }
69
70 static int from_user_dir(const char *field, char **buffer, const char **ret) {
71 _cleanup_fclose_ FILE *f = NULL;
72 _cleanup_free_ char *b = NULL;
73 _cleanup_free_ const char *fn = NULL;
74 const char *c = NULL;
75 size_t n;
76 int r;
77
78 assert(field);
79 assert(buffer);
80 assert(ret);
81
82 r = from_home_dir("XDG_CONFIG_HOME", ".config", &b, &c);
83 if (r < 0)
84 return r;
85
86 fn = path_join(c, "user-dirs.dirs");
87 if (!fn)
88 return -ENOMEM;
89
90 f = fopen(fn, "re");
91 if (!f) {
92 if (errno == ENOENT)
93 goto fallback;
94
95 return -errno;
96 }
97
98 /* This is an awful parse, but it follows closely what
99 * xdg-user-dirs does upstream */
100
101 n = strlen(field);
102 for (;;) {
103 _cleanup_free_ char *line = NULL;
104 char *l, *p, *e;
105
106 r = read_line(f, LONG_LINE_MAX, &line);
107 if (r < 0)
108 return r;
109 if (r == 0)
110 break;
111
112 l = strstrip(line);
113
114 if (!strneq(l, field, n))
115 continue;
116
117 p = l + n;
118 p += strspn(p, WHITESPACE);
119
120 if (*p != '=')
121 continue;
122 p++;
123
124 p += strspn(p, WHITESPACE);
125
126 if (*p != '"')
127 continue;
128 p++;
129
130 e = strrchr(p, '"');
131 if (!e)
132 continue;
133 *e = 0;
134
135 /* Three syntaxes permitted: relative to $HOME, $HOME itself, and absolute path */
136 if (startswith(p, "$HOME/")) {
137 _cleanup_free_ char *h = NULL;
138 char *cc;
139
140 r = get_home_dir(&h);
141 if (r < 0)
142 return r;
143
144 cc = path_join(h, p+5);
145 if (!cc)
146 return -ENOMEM;
147
148 *buffer = cc;
149 *ret = cc;
150 return 0;
151 } else if (streq(p, "$HOME")) {
152
153 r = get_home_dir(buffer);
154 if (r < 0)
155 return r;
156
157 *ret = *buffer;
158 return 0;
159 } else if (path_is_absolute(p)) {
160 char *copy;
161
162 copy = strdup(p);
163 if (!copy)
164 return -ENOMEM;
165
166 *buffer = copy;
167 *ret = copy;
168 return 0;
169 }
170 }
171
172 fallback:
173 /* The desktop directory defaults to $HOME/Desktop, the others to $HOME */
174 if (streq(field, "XDG_DESKTOP_DIR")) {
175 _cleanup_free_ char *h = NULL;
176 char *cc;
177
178 r = get_home_dir(&h);
179 if (r < 0)
180 return r;
181
182 cc = path_join(h, "Desktop");
183 if (!cc)
184 return -ENOMEM;
185
186 *buffer = cc;
187 *ret = cc;
188 } else {
189
190 r = get_home_dir(buffer);
191 if (r < 0)
192 return r;
193
194 *ret = *buffer;
195 }
196
197 return 0;
198 }
199
200 static int get_path(uint64_t type, char **buffer, const char **ret) {
201 int r;
202
203 assert(buffer);
204 assert(ret);
205
206 switch (type) {
207
208 case SD_PATH_TEMPORARY:
209 return tmp_dir(ret);
210
211 case SD_PATH_TEMPORARY_LARGE:
212 return var_tmp_dir(ret);
213
214 case SD_PATH_SYSTEM_BINARIES:
215 *ret = "/usr/bin";
216 return 0;
217
218 case SD_PATH_SYSTEM_INCLUDE:
219 *ret = "/usr/include";
220 return 0;
221
222 case SD_PATH_SYSTEM_LIBRARY_PRIVATE:
223 *ret = "/usr/lib";
224 return 0;
225
226 case SD_PATH_SYSTEM_LIBRARY_ARCH:
227 *ret = LIBDIR;
228 return 0;
229
230 case SD_PATH_SYSTEM_SHARED:
231 *ret = "/usr/share";
232 return 0;
233
234 case SD_PATH_SYSTEM_CONFIGURATION_FACTORY:
235 *ret = "/usr/share/factory/etc";
236 return 0;
237
238 case SD_PATH_SYSTEM_STATE_FACTORY:
239 *ret = "/usr/share/factory/var";
240 return 0;
241
242 case SD_PATH_SYSTEM_CONFIGURATION:
243 *ret = "/etc";
244 return 0;
245
246 case SD_PATH_SYSTEM_RUNTIME:
247 *ret = "/run";
248 return 0;
249
250 case SD_PATH_SYSTEM_RUNTIME_LOGS:
251 *ret = "/run/log";
252 return 0;
253
254 case SD_PATH_SYSTEM_STATE_PRIVATE:
255 *ret = "/var/lib";
256 return 0;
257
258 case SD_PATH_SYSTEM_STATE_LOGS:
259 *ret = "/var/log";
260 return 0;
261
262 case SD_PATH_SYSTEM_STATE_CACHE:
263 *ret = "/var/cache";
264 return 0;
265
266 case SD_PATH_SYSTEM_STATE_SPOOL:
267 *ret = "/var/spool";
268 return 0;
269
270 case SD_PATH_USER_BINARIES:
271 return from_home_dir(NULL, ".local/bin", buffer, ret);
272
273 case SD_PATH_USER_LIBRARY_PRIVATE:
274 return from_home_dir(NULL, ".local/lib", buffer, ret);
275
276 case SD_PATH_USER_LIBRARY_ARCH:
277 return from_home_dir(NULL, ".local/lib/" LIB_ARCH_TUPLE, buffer, ret);
278
279 case SD_PATH_USER_SHARED:
280 return from_home_dir("XDG_DATA_HOME", ".local/share", buffer, ret);
281
282 case SD_PATH_USER_CONFIGURATION:
283 return from_home_dir("XDG_CONFIG_HOME", ".config", buffer, ret);
284
285 case SD_PATH_USER_RUNTIME:
286 return from_environment("XDG_RUNTIME_DIR", NULL, ret);
287
288 case SD_PATH_USER_STATE_CACHE:
289 return from_home_dir("XDG_CACHE_HOME", ".cache", buffer, ret);
290
291 case SD_PATH_USER:
292 r = get_home_dir(buffer);
293 if (r < 0)
294 return r;
295
296 *ret = *buffer;
297 return 0;
298
299 case SD_PATH_USER_DOCUMENTS:
300 return from_user_dir("XDG_DOCUMENTS_DIR", buffer, ret);
301
302 case SD_PATH_USER_MUSIC:
303 return from_user_dir("XDG_MUSIC_DIR", buffer, ret);
304
305 case SD_PATH_USER_PICTURES:
306 return from_user_dir("XDG_PICTURES_DIR", buffer, ret);
307
308 case SD_PATH_USER_VIDEOS:
309 return from_user_dir("XDG_VIDEOS_DIR", buffer, ret);
310
311 case SD_PATH_USER_DOWNLOAD:
312 return from_user_dir("XDG_DOWNLOAD_DIR", buffer, ret);
313
314 case SD_PATH_USER_PUBLIC:
315 return from_user_dir("XDG_PUBLICSHARE_DIR", buffer, ret);
316
317 case SD_PATH_USER_TEMPLATES:
318 return from_user_dir("XDG_TEMPLATES_DIR", buffer, ret);
319
320 case SD_PATH_USER_DESKTOP:
321 return from_user_dir("XDG_DESKTOP_DIR", buffer, ret);
322
323 case SD_PATH_SYSTEMD_UTIL:
324 *ret = ROOTPREFIX_NOSLASH "/lib/systemd";
325 return 0;
326
327 case SD_PATH_SYSTEMD_SYSTEM_UNIT:
328 *ret = SYSTEM_DATA_UNIT_PATH;
329 return 0;
330
331 case SD_PATH_SYSTEMD_SYSTEM_PRESET:
332 *ret = ROOTPREFIX_NOSLASH "/lib/systemd/system-preset";
333 return 0;
334
335 case SD_PATH_SYSTEMD_USER_UNIT:
336 *ret = USER_DATA_UNIT_DIR;
337 return 0;
338
339 case SD_PATH_SYSTEMD_USER_PRESET:
340 *ret = ROOTPREFIX_NOSLASH "/lib/systemd/user-preset";
341 return 0;
342
343 case SD_PATH_SYSTEMD_SYSTEM_CONF:
344 *ret = SYSTEM_CONFIG_UNIT_DIR;
345 return 0;
346
347 case SD_PATH_SYSTEMD_USER_CONF:
348 *ret = USER_CONFIG_UNIT_DIR;
349 return 0;
350
351 case SD_PATH_SYSTEMD_SYSTEM_GENERATOR:
352 *ret = SYSTEM_GENERATOR_DIR;
353 return 0;
354
355 case SD_PATH_SYSTEMD_USER_GENERATOR:
356 *ret = USER_GENERATOR_DIR;
357 return 0;
358
359 case SD_PATH_SYSTEMD_SLEEP:
360 *ret = ROOTPREFIX_NOSLASH "/lib/systemd/system-sleep";
361 return 0;
362
363 case SD_PATH_SYSTEMD_SHUTDOWN:
364 *ret = ROOTPREFIX_NOSLASH "/lib/systemd/system-shutdown";
365 return 0;
366
367 case SD_PATH_TMPFILES:
368 *ret = "/usr/lib/tmpfiles.d";
369 return 0;
370
371 case SD_PATH_SYSUSERS:
372 *ret = ROOTPREFIX_NOSLASH "/lib/sysusers.d";
373 return 0;
374
375 case SD_PATH_SYSCTL:
376 *ret = ROOTPREFIX_NOSLASH "/lib/sysctl.d";
377 return 0;
378
379 case SD_PATH_BINFMT:
380 *ret = ROOTPREFIX_NOSLASH "/lib/binfmt.d";
381 return 0;
382
383 case SD_PATH_MODULES_LOAD:
384 *ret = ROOTPREFIX_NOSLASH "/lib/modules-load.d";
385 return 0;
386
387 case SD_PATH_CATALOG:
388 *ret = "/usr/lib/systemd/catalog";
389 return 0;
390 }
391
392 return -EOPNOTSUPP;
393 }
394
395 static int get_path_alloc(uint64_t type, const char *suffix, char **path) {
396 _cleanup_free_ char *buffer = NULL;
397 char *buffer2 = NULL;
398 const char *ret;
399 int r;
400
401 assert(path);
402
403 r = get_path(type, &buffer, &ret);
404 if (r < 0)
405 return r;
406
407 if (suffix) {
408 suffix += strspn(suffix, "/");
409 buffer2 = path_join(ret, suffix);
410 if (!buffer2)
411 return -ENOMEM;
412 } else if (!buffer) {
413 buffer = strdup(ret);
414 if (!buffer)
415 return -ENOMEM;
416 }
417
418 *path = buffer2 ?: TAKE_PTR(buffer);
419 return 0;
420 }
421
422 _public_ int sd_path_lookup(uint64_t type, const char *suffix, char **path) {
423 int r;
424
425 assert_return(path, -EINVAL);
426
427 r = get_path_alloc(type, suffix, path);
428 if (r != -EOPNOTSUPP)
429 return r;
430
431 /* Fall back to sd_path_lookup_strv */
432 _cleanup_strv_free_ char **l = NULL;
433 char *buffer;
434
435 r = sd_path_lookup_strv(type, suffix, &l);
436 if (r < 0)
437 return r;
438
439 buffer = strv_join(l, ":");
440 if (!buffer)
441 return -ENOMEM;
442
443 *path = buffer;
444 return 0;
445 }
446
447 static int search_from_environment(
448 char ***list,
449 const char *env_home,
450 const char *home_suffix,
451 const char *env_search,
452 bool env_search_sufficient,
453 const char *first, ...) {
454
455 _cleanup_strv_free_ char **l = NULL;
456 const char *e;
457 char *h = NULL;
458 int r;
459
460 assert(list);
461
462 if (env_search) {
463 e = secure_getenv(env_search);
464 if (e) {
465 l = strv_split(e, ":");
466 if (!l)
467 return -ENOMEM;
468
469 if (env_search_sufficient) {
470 *list = TAKE_PTR(l);
471 return 0;
472 }
473 }
474 }
475
476 if (!l && first) {
477 va_list ap;
478
479 va_start(ap, first);
480 l = strv_new_ap(first, ap);
481 va_end(ap);
482
483 if (!l)
484 return -ENOMEM;
485 }
486
487 if (env_home) {
488 e = secure_getenv(env_home);
489 if (e && path_is_absolute(e)) {
490 h = strdup(e);
491 if (!h)
492 return -ENOMEM;
493 }
494 }
495
496 if (!h && home_suffix) {
497 e = secure_getenv("HOME");
498 if (e && path_is_absolute(e)) {
499 h = path_join(e, home_suffix);
500 if (!h)
501 return -ENOMEM;
502 }
503 }
504
505 if (h) {
506 r = strv_consume_prepend(&l, h);
507 if (r < 0)
508 return -ENOMEM;
509 }
510
511 *list = TAKE_PTR(l);
512 return 0;
513 }
514
515 #if HAVE_SPLIT_BIN
516 # define ARRAY_SBIN_BIN(x) x "sbin", x "bin"
517 #else
518 # define ARRAY_SBIN_BIN(x) x "bin"
519 #endif
520
521 static int get_search(uint64_t type, char ***list) {
522 int r;
523
524 assert(list);
525
526 switch(type) {
527
528 case SD_PATH_SEARCH_BINARIES:
529 return search_from_environment(list,
530 NULL,
531 ".local/bin",
532 "PATH",
533 true,
534 ARRAY_SBIN_BIN("/usr/local/"),
535 ARRAY_SBIN_BIN("/usr/"),
536 #if HAVE_SPLIT_USR
537 ARRAY_SBIN_BIN("/"),
538 #endif
539 NULL);
540
541 case SD_PATH_SEARCH_LIBRARY_PRIVATE:
542 return search_from_environment(list,
543 NULL,
544 ".local/lib",
545 NULL,
546 false,
547 "/usr/local/lib",
548 "/usr/lib",
549 #if HAVE_SPLIT_USR
550 "/lib",
551 #endif
552 NULL);
553
554 case SD_PATH_SEARCH_LIBRARY_ARCH:
555 return search_from_environment(list,
556 NULL,
557 ".local/lib/" LIB_ARCH_TUPLE,
558 "LD_LIBRARY_PATH",
559 true,
560 LIBDIR,
561 #if HAVE_SPLIT_USR
562 ROOTLIBDIR,
563 #endif
564 NULL);
565
566 case SD_PATH_SEARCH_SHARED:
567 return search_from_environment(list,
568 "XDG_DATA_HOME",
569 ".local/share",
570 "XDG_DATA_DIRS",
571 false,
572 "/usr/local/share",
573 "/usr/share",
574 NULL);
575
576 case SD_PATH_SEARCH_CONFIGURATION_FACTORY:
577 return search_from_environment(list,
578 NULL,
579 NULL,
580 NULL,
581 false,
582 "/usr/local/share/factory/etc",
583 "/usr/share/factory/etc",
584 NULL);
585
586 case SD_PATH_SEARCH_STATE_FACTORY:
587 return search_from_environment(list,
588 NULL,
589 NULL,
590 NULL,
591 false,
592 "/usr/local/share/factory/var",
593 "/usr/share/factory/var",
594 NULL);
595
596 case SD_PATH_SEARCH_CONFIGURATION:
597 return search_from_environment(list,
598 "XDG_CONFIG_HOME",
599 ".config",
600 "XDG_CONFIG_DIRS",
601 false,
602 "/etc",
603 NULL);
604
605 case SD_PATH_SEARCH_BINARIES_DEFAULT:
606 return strv_from_nulstr(list, DEFAULT_PATH_NULSTR);
607
608 case SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT:
609 case SD_PATH_SYSTEMD_SEARCH_USER_UNIT: {
610 _cleanup_(lookup_paths_free) LookupPaths lp = {};
611 const UnitFileScope scope = type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT ?
612 UNIT_FILE_SYSTEM : UNIT_FILE_USER;
613
614 r = lookup_paths_init(&lp, scope, 0, NULL);
615 if (r < 0)
616 return r;
617
618 *list = TAKE_PTR(lp.search_path);
619 return 0;
620 }
621
622 case SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR:
623 case SD_PATH_SYSTEMD_SEARCH_USER_GENERATOR: {
624 char **t;
625 const UnitFileScope scope = type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR ?
626 UNIT_FILE_SYSTEM : UNIT_FILE_USER;
627
628 t = generator_binary_paths(scope);
629 if (!t)
630 return -ENOMEM;
631
632 *list = t;
633 return 0;
634 }
635
636 case SD_PATH_SYSTEMD_SEARCH_NETWORK:
637 return strv_from_nulstr(list, NETWORK_DIRS_NULSTR);
638
639 }
640
641 return -EOPNOTSUPP;
642 }
643
644 _public_ int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***paths) {
645 _cleanup_strv_free_ char **l = NULL, **n = NULL;
646 int r;
647
648 assert_return(paths, -EINVAL);
649
650 r = get_search(type, &l);
651 if (r == -EOPNOTSUPP) {
652 _cleanup_free_ char *t = NULL;
653
654 r = get_path_alloc(type, suffix, &t);
655 if (r < 0)
656 return r;
657
658 l = new(char*, 2);
659 if (!l)
660 return -ENOMEM;
661 l[0] = TAKE_PTR(t);
662 l[1] = NULL;
663
664 *paths = TAKE_PTR(l);
665 return 0;
666
667 } else if (r < 0)
668 return r;
669
670 if (!suffix) {
671 *paths = TAKE_PTR(l);
672 return 0;
673 }
674
675 n = new(char*, strv_length(l)+1);
676 if (!n)
677 return -ENOMEM;
678
679 char **i, **j = n;
680 STRV_FOREACH(i, l) {
681 *j = path_join(*i, suffix);
682 if (!*j)
683 return -ENOMEM;
684
685 j++;
686 }
687 *j = NULL;
688
689 *paths = TAKE_PTR(n);
690 return 0;
691 }