]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-path/sd-path.c
sd-path: drop "-dir" and "-path" suffixes from path enums
[thirdparty/systemd.git] / src / libsystemd / sd-path / sd-path.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
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 "/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 "/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 "/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 "/lib/systemd/system-sleep";
361 return 0;
362
363 case SD_PATH_SYSTEMD_SHUTDOWN:
364 *ret = ROOTPREFIX "/lib/systemd/system-shutdown";
365 return 0;
366
367 /* FIXME: systemd.pc uses ${prefix}, but CONF_PATHS_NULSTR doesn't.
368 * Should ${prefix} use in systemd.pc be removed? */
369 case SD_PATH_TMPFILES:
370 *ret = "/usr/lib/tmpfiles.d";
371 return 0;
372
373 case SD_PATH_SYSUSERS:
374 *ret = ROOTPREFIX "/lib/sysusers.d";
375 return 0;
376
377 case SD_PATH_SYSCTL:
378 *ret = ROOTPREFIX "/lib/sysctl.d";
379 return 0;
380
381 case SD_PATH_BINFMT:
382 *ret = ROOTPREFIX "/lib/binfmt.d";
383 return 0;
384
385 case SD_PATH_MODULES_LOAD:
386 *ret = ROOTPREFIX "/lib/modules-load.d";
387 return 0;
388
389 case SD_PATH_CATALOG:
390 *ret = "/usr/lib/systemd/catalog";
391 return 0;
392 }
393
394 return -EOPNOTSUPP;
395 }
396
397 static int get_path_alloc(uint64_t type, const char *suffix, char **path) {
398 _cleanup_free_ char *buffer = NULL;
399 char *buffer2 = NULL;
400 const char *ret;
401 int r;
402
403 assert(path);
404
405 r = get_path(type, &buffer, &ret);
406 if (r < 0)
407 return r;
408
409 if (suffix) {
410 suffix += strspn(suffix, "/");
411 buffer2 = path_join(ret, suffix);
412 if (!buffer2)
413 return -ENOMEM;
414 } else if (!buffer) {
415 buffer = strdup(ret);
416 if (!buffer)
417 return -ENOMEM;
418 }
419
420 *path = buffer2 ?: TAKE_PTR(buffer);
421 return 0;
422 }
423
424 _public_ int sd_path_lookup(uint64_t type, const char *suffix, char **path) {
425 int r;
426
427 assert_return(path, -EINVAL);
428
429 r = get_path_alloc(type, suffix, path);
430 if (r != -EOPNOTSUPP)
431 return r;
432
433 /* Fall back to sd_path_lookup_strv */
434 _cleanup_strv_free_ char **l = NULL;
435 char *buffer;
436
437 r = sd_path_lookup_strv(type, suffix, &l);
438 if (r < 0)
439 return r;
440
441 buffer = strv_join(l, ":");
442 if (!buffer)
443 return -ENOMEM;
444
445 *path = buffer;
446 return 0;
447 }
448
449 static int search_from_environment(
450 char ***list,
451 const char *env_home,
452 const char *home_suffix,
453 const char *env_search,
454 bool env_search_sufficient,
455 const char *first, ...) {
456
457 _cleanup_strv_free_ char **l = NULL;
458 const char *e;
459 char *h = NULL;
460 int r;
461
462 assert(list);
463
464 if (env_search) {
465 e = secure_getenv(env_search);
466 if (e) {
467 l = strv_split(e, ":");
468 if (!l)
469 return -ENOMEM;
470
471 if (env_search_sufficient) {
472 *list = TAKE_PTR(l);
473 return 0;
474 }
475 }
476 }
477
478 if (!l && first) {
479 va_list ap;
480
481 va_start(ap, first);
482 l = strv_new_ap(first, ap);
483 va_end(ap);
484
485 if (!l)
486 return -ENOMEM;
487 }
488
489 if (env_home) {
490 e = secure_getenv(env_home);
491 if (e && path_is_absolute(e)) {
492 h = strdup(e);
493 if (!h)
494 return -ENOMEM;
495 }
496 }
497
498 if (!h && home_suffix) {
499 e = secure_getenv("HOME");
500 if (e && path_is_absolute(e)) {
501 h = path_join(e, home_suffix);
502 if (!h)
503 return -ENOMEM;
504 }
505 }
506
507 if (h) {
508 r = strv_consume_prepend(&l, h);
509 if (r < 0)
510 return -ENOMEM;
511 }
512
513 *list = TAKE_PTR(l);
514 return 0;
515 }
516
517 #if HAVE_SPLIT_BIN
518 # define ARRAY_SBIN_BIN(x) x "sbin", x "bin"
519 #else
520 # define ARRAY_SBIN_BIN(x) x "bin"
521 #endif
522
523 static int get_search(uint64_t type, char ***list) {
524 int r;
525
526 assert(list);
527
528 switch(type) {
529
530 case SD_PATH_SEARCH_BINARIES:
531 return search_from_environment(list,
532 NULL,
533 ".local/bin",
534 "PATH",
535 true,
536 ARRAY_SBIN_BIN("/usr/local/"),
537 ARRAY_SBIN_BIN("/usr/"),
538 #if HAVE_SPLIT_USR
539 ARRAY_SBIN_BIN("/"),
540 #endif
541 NULL);
542
543 case SD_PATH_SEARCH_LIBRARY_PRIVATE:
544 return search_from_environment(list,
545 NULL,
546 ".local/lib",
547 NULL,
548 false,
549 "/usr/local/lib",
550 "/usr/lib",
551 #if HAVE_SPLIT_USR
552 "/lib",
553 #endif
554 NULL);
555
556 case SD_PATH_SEARCH_LIBRARY_ARCH:
557 return search_from_environment(list,
558 NULL,
559 ".local/lib/" LIB_ARCH_TUPLE,
560 "LD_LIBRARY_PATH",
561 true,
562 LIBDIR,
563 #if HAVE_SPLIT_USR
564 ROOTLIBDIR,
565 #endif
566 NULL);
567
568 case SD_PATH_SEARCH_SHARED:
569 return search_from_environment(list,
570 "XDG_DATA_HOME",
571 ".local/share",
572 "XDG_DATA_DIRS",
573 false,
574 "/usr/local/share",
575 "/usr/share",
576 NULL);
577
578 case SD_PATH_SEARCH_CONFIGURATION_FACTORY:
579 return search_from_environment(list,
580 NULL,
581 NULL,
582 NULL,
583 false,
584 "/usr/local/share/factory/etc",
585 "/usr/share/factory/etc",
586 NULL);
587
588 case SD_PATH_SEARCH_STATE_FACTORY:
589 return search_from_environment(list,
590 NULL,
591 NULL,
592 NULL,
593 false,
594 "/usr/local/share/factory/var",
595 "/usr/share/factory/var",
596 NULL);
597
598 case SD_PATH_SEARCH_CONFIGURATION:
599 return search_from_environment(list,
600 "XDG_CONFIG_HOME",
601 ".config",
602 "XDG_CONFIG_DIRS",
603 false,
604 "/etc",
605 NULL);
606
607 case SD_PATH_SEARCH_BINARIES_DEFAULT:
608 return strv_from_nulstr(list, DEFAULT_PATH_NULSTR);
609
610 case SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT:
611 case SD_PATH_SYSTEMD_SEARCH_USER_UNIT: {
612 _cleanup_(lookup_paths_free) LookupPaths lp = {};
613 const UnitFileScope scope = type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT ?
614 UNIT_FILE_SYSTEM : UNIT_FILE_USER;
615
616 r = lookup_paths_init(&lp, scope, 0, NULL);
617 if (r < 0)
618 return r;
619
620 *list = TAKE_PTR(lp.search_path);
621 return 0;
622 }
623
624 case SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR:
625 case SD_PATH_SYSTEMD_SEARCH_USER_GENERATOR: {
626 char **t;
627 const UnitFileScope scope = type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR ?
628 UNIT_FILE_SYSTEM : UNIT_FILE_USER;
629
630 t = generator_binary_paths(scope);
631 if (!t)
632 return -ENOMEM;
633
634 *list = t;
635 return 0;
636 }
637
638 case SD_PATH_SYSTEMD_SEARCH_NETWORK:
639 return strv_from_nulstr(list, NETWORK_DIRS_NULSTR);
640
641 }
642
643 return -EOPNOTSUPP;
644 }
645
646 _public_ int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***paths) {
647 _cleanup_strv_free_ char **l = NULL, **n = NULL;
648 int r;
649
650 assert_return(paths, -EINVAL);
651
652 r = get_search(type, &l);
653 if (r == -EOPNOTSUPP) {
654 _cleanup_free_ char *t = NULL;
655
656 r = get_path_alloc(type, suffix, &t);
657 if (r < 0)
658 return r;
659
660 l = new(char*, 2);
661 if (!l)
662 return -ENOMEM;
663 l[0] = TAKE_PTR(t);
664 l[1] = NULL;
665
666 *paths = TAKE_PTR(l);
667 return 0;
668
669 } else if (r < 0)
670 return r;
671
672 if (!suffix) {
673 *paths = TAKE_PTR(l);
674 return 0;
675 }
676
677 n = new(char*, strv_length(l)+1);
678 if (!n)
679 return -ENOMEM;
680
681 char **i, **j = n;
682 STRV_FOREACH(i, l) {
683 *j = path_join(*i, suffix);
684 if (!*j)
685 return -ENOMEM;
686
687 j++;
688 }
689 *j = NULL;
690
691 *paths = TAKE_PTR(n);
692 return 0;
693 }