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