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