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