]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-path/sd-path.c
tree-wide: replace strjoin() with path_join()
[thirdparty/systemd.git] / src / libsystemd / sd-path / sd-path.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
9a00f57a 2
07630cea
LP
3#include "sd-path.h"
4
b5efdb8a 5#include "alloc-util.h"
9a00f57a 6#include "architecture.h"
3ffd4af2 7#include "fd-util.h"
0d39fa9c 8#include "fileio.h"
fccb4486 9#include "fs-util.h"
07630cea 10#include "missing.h"
9a00f57a 11#include "path-util.h"
07630cea 12#include "string-util.h"
9a00f57a 13#include "strv.h"
b1d4f8e1 14#include "user-util.h"
07630cea 15#include "util.h"
9a00f57a
LP
16
17static 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
38static 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
657ee2d8 61 cc = path_join(h, suffix);
9a00f57a
LP
62 if (!cc)
63 return -ENOMEM;
64
65 *buffer = cc;
66 *ret = cc;
67 return 0;
68}
69
70static int from_user_dir(const char *field, char **buffer, const char **ret) {
71 _cleanup_fclose_ FILE *f = NULL;
72 _cleanup_free_ char *b = NULL;
b6b0cfaa
EGP
73 _cleanup_free_ const char *fn = NULL;
74 const char *c = NULL;
9a00f57a
LP
75 size_t n;
76 int r;
77
78 assert(field);
79 assert(buffer);
80 assert(ret);
81
b6b0cfaa 82 r = from_home_dir("XDG_CONFIG_HOME", ".config", &b, &c);
9a00f57a
LP
83 if (r < 0)
84 return r;
85
b6b0cfaa
EGP
86 fn = strappend(c, "/user-dirs.dirs");
87 if (!fn)
88 return -ENOMEM;
89
9a00f57a
LP
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);
710bf2ae
LP
102 for (;;) {
103 _cleanup_free_ char *line = NULL;
9a00f57a
LP
104 char *l, *p, *e;
105
710bf2ae
LP
106 r = read_line(f, LONG_LINE_MAX, &line);
107 if (r < 0)
108 return r;
109 if (r == 0)
110 break;
111
9a00f57a
LP
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 = strappend(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
172fallback:
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 = strappend(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
200static 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:
fccb4486 209 return tmp_dir(ret);
9a00f57a
LP
210
211 case SD_PATH_TEMPORARY_LARGE:
fccb4486 212 return var_tmp_dir(ret);
9a00f57a
LP
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:
613e3a26 277 return from_home_dir(NULL, ".local/lib/" LIB_ARCH_TUPLE, buffer, ret);
9a00f57a
LP
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
15411c0c 324 return -EOPNOTSUPP;
9a00f57a
LP
325}
326
2de30868 327_public_ int sd_path_home(uint64_t type, const char *suffix, char **path) {
9a00f57a
LP
328 char *buffer = NULL, *cc;
329 const char *ret;
330 int r;
331
332 assert_return(path, -EINVAL);
333
334 if (IN_SET(type,
335 SD_PATH_SEARCH_BINARIES,
e12d446b 336 SD_PATH_SEARCH_BINARIES_DEFAULT,
9a00f57a
LP
337 SD_PATH_SEARCH_LIBRARY_PRIVATE,
338 SD_PATH_SEARCH_LIBRARY_ARCH,
339 SD_PATH_SEARCH_SHARED,
340 SD_PATH_SEARCH_CONFIGURATION_FACTORY,
341 SD_PATH_SEARCH_STATE_FACTORY,
342 SD_PATH_SEARCH_CONFIGURATION)) {
343
344 _cleanup_strv_free_ char **l = NULL;
345
346 r = sd_path_search(type, suffix, &l);
347 if (r < 0)
348 return r;
349
350 buffer = strv_join(l, ":");
351 if (!buffer)
352 return -ENOMEM;
353
354 *path = buffer;
355 return 0;
356 }
357
358 r = get_path(type, &buffer, &ret);
359 if (r < 0)
360 return r;
361
362 if (!suffix) {
363 if (!buffer) {
364 buffer = strdup(ret);
365 if (!buffer)
366 return -ENOMEM;
367 }
368
369 *path = buffer;
370 return 0;
371 }
372
373 suffix += strspn(suffix, "/");
657ee2d8 374 cc = path_join(ret, suffix);
9a00f57a
LP
375
376 free(buffer);
377
378 if (!cc)
379 return -ENOMEM;
380
381 *path = cc;
382 return 0;
383}
384
385static int search_from_environment(
386 char ***list,
387 const char *env_home,
388 const char *home_suffix,
389 const char *env_search,
390 bool env_search_sufficient,
391 const char *first, ...) {
392
393 const char *e;
394 char *h = NULL;
395 char **l = NULL;
396 int r;
397
398 assert(list);
399
400 if (env_search) {
401 e = secure_getenv(env_search);
402 if (e) {
403 l = strv_split(e, ":");
404 if (!l)
405 return -ENOMEM;
406
407 if (env_search_sufficient) {
408 *list = l;
409 return 0;
410 }
411 }
412 }
413
414 if (!l && first) {
415 va_list ap;
416
417 va_start(ap, first);
418 l = strv_new_ap(first, ap);
419 va_end(ap);
420
421 if (!l)
422 return -ENOMEM;
423 }
424
425 if (env_home) {
426 e = secure_getenv(env_home);
427 if (e && path_is_absolute(e)) {
428 h = strdup(e);
429 if (!h) {
430 strv_free(l);
431 return -ENOMEM;
432 }
433 }
434 }
435
436 if (!h && home_suffix) {
437 e = secure_getenv("HOME");
438 if (e && path_is_absolute(e)) {
657ee2d8 439 h = path_join(e, home_suffix);
9a00f57a
LP
440
441 if (!h) {
442 strv_free(l);
443 return -ENOMEM;
444 }
445 }
446 }
447
448 if (h) {
449 r = strv_consume_prepend(&l, h);
450 if (r < 0) {
451 strv_free(l);
452 return -ENOMEM;
453 }
454 }
455
456 *list = l;
457 return 0;
458}
459
671f0f8d
ZJS
460#if HAVE_SPLIT_BIN
461# define ARRAY_SBIN_BIN(x) x "sbin", x "bin"
462#else
463# define ARRAY_SBIN_BIN(x) x "bin"
464#endif
465
9a00f57a
LP
466static int get_search(uint64_t type, char ***list) {
467
468 assert(list);
469
470 switch(type) {
471
472 case SD_PATH_SEARCH_BINARIES:
473 return search_from_environment(list,
474 NULL,
475 ".local/bin",
476 "PATH",
477 true,
671f0f8d
ZJS
478 ARRAY_SBIN_BIN("/usr/local/"),
479 ARRAY_SBIN_BIN("/usr/"),
349cc4a5 480#if HAVE_SPLIT_USR
671f0f8d 481 ARRAY_SBIN_BIN("/"),
9a00f57a
LP
482#endif
483 NULL);
484
485 case SD_PATH_SEARCH_LIBRARY_PRIVATE:
486 return search_from_environment(list,
487 NULL,
488 ".local/lib",
489 NULL,
490 false,
491 "/usr/local/lib",
492 "/usr/lib",
349cc4a5 493#if HAVE_SPLIT_USR
9a00f57a
LP
494 "/lib",
495#endif
496 NULL);
497
498 case SD_PATH_SEARCH_LIBRARY_ARCH:
499 return search_from_environment(list,
500 NULL,
613e3a26 501 ".local/lib/" LIB_ARCH_TUPLE,
9a00f57a
LP
502 "LD_LIBRARY_PATH",
503 true,
504 LIBDIR,
349cc4a5 505#if HAVE_SPLIT_USR
9a00f57a
LP
506 ROOTLIBDIR,
507#endif
508 NULL);
509
510 case SD_PATH_SEARCH_SHARED:
511 return search_from_environment(list,
512 "XDG_DATA_HOME",
513 ".local/share",
514 "XDG_DATA_DIRS",
515 false,
516 "/usr/local/share",
517 "/usr/share",
518 NULL);
519
520 case SD_PATH_SEARCH_CONFIGURATION_FACTORY:
521 return search_from_environment(list,
522 NULL,
523 NULL,
524 NULL,
525 false,
526 "/usr/local/share/factory/etc",
527 "/usr/share/factory/etc",
528 NULL);
529
530 case SD_PATH_SEARCH_STATE_FACTORY:
531 return search_from_environment(list,
532 NULL,
533 NULL,
534 NULL,
535 false,
536 "/usr/local/share/factory/var",
537 "/usr/share/factory/var",
538 NULL);
539
540 case SD_PATH_SEARCH_CONFIGURATION:
541 return search_from_environment(list,
542 "XDG_CONFIG_HOME",
543 ".config",
544 "XDG_CONFIG_DIRS",
545 false,
546 "/etc",
547 NULL);
e12d446b
ZJS
548
549 case SD_PATH_SEARCH_BINARIES_DEFAULT: {
550 char **t;
551
552 t = strv_split_nulstr(DEFAULT_PATH_NULSTR);
553 if (!t)
554 return -ENOMEM;
555
556 *list = t;
557 return 0;
558 }}
9a00f57a 559
15411c0c 560 return -EOPNOTSUPP;
9a00f57a
LP
561}
562
2de30868 563_public_ int sd_path_search(uint64_t type, const char *suffix, char ***paths) {
a605e46f
FB
564 char **i, **j;
565 _cleanup_strv_free_ char **l = NULL, **n = NULL;
9a00f57a
LP
566 int r;
567
568 assert_return(paths, -EINVAL);
569
570 if (!IN_SET(type,
571 SD_PATH_SEARCH_BINARIES,
e12d446b 572 SD_PATH_SEARCH_BINARIES_DEFAULT,
9a00f57a
LP
573 SD_PATH_SEARCH_LIBRARY_PRIVATE,
574 SD_PATH_SEARCH_LIBRARY_ARCH,
575 SD_PATH_SEARCH_SHARED,
576 SD_PATH_SEARCH_CONFIGURATION_FACTORY,
577 SD_PATH_SEARCH_STATE_FACTORY,
578 SD_PATH_SEARCH_CONFIGURATION)) {
579
580 char *p;
581
582 r = sd_path_home(type, suffix, &p);
583 if (r < 0)
584 return r;
585
586 l = new(char*, 2);
587 if (!l) {
588 free(p);
589 return -ENOMEM;
590 }
591
592 l[0] = p;
593 l[1] = NULL;
594
a605e46f 595 *paths = TAKE_PTR(l);
9a00f57a
LP
596 return 0;
597 }
598
599 r = get_search(type, &l);
600 if (r < 0)
601 return r;
602
603 if (!suffix) {
a605e46f 604 *paths = TAKE_PTR(l);
9a00f57a
LP
605 return 0;
606 }
607
608 n = new(char*, strv_length(l)+1);
a605e46f 609 if (!n)
9a00f57a 610 return -ENOMEM;
9a00f57a
LP
611
612 j = n;
613 STRV_FOREACH(i, l) {
657ee2d8 614 *j = path_join(*i, suffix);
a605e46f 615 if (!*j)
9a00f57a 616 return -ENOMEM;
9a00f57a
LP
617
618 j++;
619 }
620
621 *j = NULL;
a605e46f 622 *paths = TAKE_PTR(n);
9a00f57a
LP
623 return 0;
624}