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