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 16 : static int from_environment(const char *envname, const char *fallback, const char **ret) {
18 16 : assert(ret);
19 :
20 16 : if (envname) {
21 : const char *e;
22 :
23 16 : e = secure_getenv(envname);
24 16 : if (e && path_is_absolute(e)) {
25 15 : *ret = e;
26 15 : return 0;
27 : }
28 : }
29 :
30 1 : if (fallback) {
31 0 : *ret = fallback;
32 0 : return 0;
33 : }
34 :
35 1 : return -ENXIO;
36 : }
37 :
38 56 : static int from_home_dir(const char *envname, const char *suffix, char **buffer, const char **ret) {
39 56 : _cleanup_free_ char *h = NULL;
40 56 : char *cc = NULL;
41 : int r;
42 :
43 56 : assert(suffix);
44 56 : assert(buffer);
45 56 : assert(ret);
46 :
47 56 : if (envname) {
48 56 : const char *e = NULL;
49 :
50 56 : e = secure_getenv(envname);
51 56 : if (e && path_is_absolute(e)) {
52 0 : *ret = e;
53 0 : return 0;
54 : }
55 : }
56 :
57 56 : r = get_home_dir(&h);
58 56 : if (r < 0)
59 0 : return r;
60 :
61 56 : cc = path_join(h, suffix);
62 56 : if (!cc)
63 0 : return -ENOMEM;
64 :
65 56 : *buffer = cc;
66 56 : *ret = cc;
67 56 : 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 76 : static int get_path(uint64_t type, char **buffer, const char **ret) {
201 : int r;
202 :
203 76 : assert(buffer);
204 76 : assert(ret);
205 :
206 76 : 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 1 : case SD_PATH_SYSTEM_RUNTIME:
247 1 : *ret = "/run";
248 1 : return 0;
249 :
250 0 : case SD_PATH_SYSTEM_RUNTIME_LOGS:
251 0 : *ret = "/run/log";
252 0 : return 0;
253 :
254 1 : case SD_PATH_SYSTEM_STATE_PRIVATE:
255 1 : *ret = "/var/lib";
256 1 : return 0;
257 :
258 1 : case SD_PATH_SYSTEM_STATE_LOGS:
259 1 : *ret = "/var/log";
260 1 : return 0;
261 :
262 1 : case SD_PATH_SYSTEM_STATE_CACHE:
263 1 : *ret = "/var/cache";
264 1 : 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 42 : case SD_PATH_USER_CONFIGURATION:
283 42 : return from_home_dir("XDG_CONFIG_HOME", ".config", buffer, ret);
284 :
285 16 : case SD_PATH_USER_RUNTIME:
286 16 : return from_environment("XDG_RUNTIME_DIR", NULL, ret);
287 :
288 14 : case SD_PATH_USER_STATE_CACHE:
289 14 : 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 76 : _public_ int sd_path_home(uint64_t type, const char *suffix, char **path) {
328 76 : _cleanup_free_ char *buffer = NULL;
329 : const char *ret;
330 : char *cc;
331 : int r;
332 :
333 76 : assert_return(path, -EINVAL);
334 :
335 76 : 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 76 : r = get_path(type, &buffer, &ret);
360 76 : if (r < 0)
361 1 : return r;
362 :
363 75 : if (!suffix) {
364 61 : if (!buffer) {
365 19 : buffer = strdup(ret);
366 19 : if (!buffer)
367 0 : return -ENOMEM;
368 : }
369 :
370 61 : *path = TAKE_PTR(buffer);
371 61 : return 0;
372 : }
373 :
374 14 : suffix += strspn(suffix, "/");
375 14 : cc = path_join(ret, suffix);
376 14 : if (!cc)
377 0 : return -ENOMEM;
378 :
379 14 : *path = TAKE_PTR(cc);
380 14 : 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 : }
|