]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-path/sd-path.c
Merge pull request #9274 from poettering/comment-header-cleanup
[thirdparty/systemd.git] / src / libsystemd / sd-path / sd-path.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 ***/
4
5 #include "sd-path.h"
6
7 #include "alloc-util.h"
8 #include "architecture.h"
9 #include "fd-util.h"
10 #include "fileio.h"
11 #include "fs-util.h"
12 #include "missing.h"
13 #include "path-util.h"
14 #include "string-util.h"
15 #include "strv.h"
16 #include "user-util.h"
17 #include "util.h"
18
19 static int from_environment(const char *envname, const char *fallback, const char **ret) {
20 assert(ret);
21
22 if (envname) {
23 const char *e;
24
25 e = secure_getenv(envname);
26 if (e && path_is_absolute(e)) {
27 *ret = e;
28 return 0;
29 }
30 }
31
32 if (fallback) {
33 *ret = fallback;
34 return 0;
35 }
36
37 return -ENXIO;
38 }
39
40 static int from_home_dir(const char *envname, const char *suffix, char **buffer, const char **ret) {
41 _cleanup_free_ char *h = NULL;
42 char *cc = NULL;
43 int r;
44
45 assert(suffix);
46 assert(buffer);
47 assert(ret);
48
49 if (envname) {
50 const char *e = NULL;
51
52 e = secure_getenv(envname);
53 if (e && path_is_absolute(e)) {
54 *ret = e;
55 return 0;
56 }
57 }
58
59 r = get_home_dir(&h);
60 if (r < 0)
61 return r;
62
63 if (endswith(h, "/"))
64 cc = strappend(h, suffix);
65 else
66 cc = strjoin(h, "/", suffix);
67 if (!cc)
68 return -ENOMEM;
69
70 *buffer = cc;
71 *ret = cc;
72 return 0;
73 }
74
75 static int from_user_dir(const char *field, char **buffer, const char **ret) {
76 _cleanup_fclose_ FILE *f = NULL;
77 _cleanup_free_ char *b = NULL;
78 _cleanup_free_ const char *fn = NULL;
79 const char *c = NULL;
80 char line[LINE_MAX];
81 size_t n;
82 int r;
83
84 assert(field);
85 assert(buffer);
86 assert(ret);
87
88 r = from_home_dir("XDG_CONFIG_HOME", ".config", &b, &c);
89 if (r < 0)
90 return r;
91
92 fn = strappend(c, "/user-dirs.dirs");
93 if (!fn)
94 return -ENOMEM;
95
96 f = fopen(fn, "re");
97 if (!f) {
98 if (errno == ENOENT)
99 goto fallback;
100
101 return -errno;
102 }
103
104 /* This is an awful parse, but it follows closely what
105 * xdg-user-dirs does upstream */
106
107 n = strlen(field);
108 FOREACH_LINE(line, f, return -errno) {
109 char *l, *p, *e;
110
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
143 cc = strappend(h, p+5);
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
171 fallback:
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
181 cc = strappend(h, "/Desktop");
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
199 static 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:
208 return tmp_dir(ret);
209
210 case SD_PATH_TEMPORARY_LARGE:
211 return var_tmp_dir(ret);
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:
276 return from_home_dir(NULL, ".local/lib/" LIB_ARCH_TUPLE, buffer, ret);
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
323 return -EOPNOTSUPP;
324 }
325
326 _public_ int sd_path_home(uint64_t type, const char *suffix, char **path) {
327 char *buffer = NULL, *cc;
328 const char *ret;
329 int r;
330
331 assert_return(path, -EINVAL);
332
333 if (IN_SET(type,
334 SD_PATH_SEARCH_BINARIES,
335 SD_PATH_SEARCH_BINARIES_DEFAULT,
336 SD_PATH_SEARCH_LIBRARY_PRIVATE,
337 SD_PATH_SEARCH_LIBRARY_ARCH,
338 SD_PATH_SEARCH_SHARED,
339 SD_PATH_SEARCH_CONFIGURATION_FACTORY,
340 SD_PATH_SEARCH_STATE_FACTORY,
341 SD_PATH_SEARCH_CONFIGURATION)) {
342
343 _cleanup_strv_free_ char **l = NULL;
344
345 r = sd_path_search(type, suffix, &l);
346 if (r < 0)
347 return r;
348
349 buffer = strv_join(l, ":");
350 if (!buffer)
351 return -ENOMEM;
352
353 *path = buffer;
354 return 0;
355 }
356
357 r = get_path(type, &buffer, &ret);
358 if (r < 0)
359 return r;
360
361 if (!suffix) {
362 if (!buffer) {
363 buffer = strdup(ret);
364 if (!buffer)
365 return -ENOMEM;
366 }
367
368 *path = buffer;
369 return 0;
370 }
371
372 suffix += strspn(suffix, "/");
373
374 if (endswith(ret, "/"))
375 cc = strappend(ret, suffix);
376 else
377 cc = strjoin(ret, "/", suffix);
378
379 free(buffer);
380
381 if (!cc)
382 return -ENOMEM;
383
384 *path = cc;
385 return 0;
386 }
387
388 static int search_from_environment(
389 char ***list,
390 const char *env_home,
391 const char *home_suffix,
392 const char *env_search,
393 bool env_search_sufficient,
394 const char *first, ...) {
395
396 const char *e;
397 char *h = NULL;
398 char **l = NULL;
399 int r;
400
401 assert(list);
402
403 if (env_search) {
404 e = secure_getenv(env_search);
405 if (e) {
406 l = strv_split(e, ":");
407 if (!l)
408 return -ENOMEM;
409
410 if (env_search_sufficient) {
411 *list = l;
412 return 0;
413 }
414 }
415 }
416
417 if (!l && first) {
418 va_list ap;
419
420 va_start(ap, first);
421 l = strv_new_ap(first, ap);
422 va_end(ap);
423
424 if (!l)
425 return -ENOMEM;
426 }
427
428 if (env_home) {
429 e = secure_getenv(env_home);
430 if (e && path_is_absolute(e)) {
431 h = strdup(e);
432 if (!h) {
433 strv_free(l);
434 return -ENOMEM;
435 }
436 }
437 }
438
439 if (!h && home_suffix) {
440 e = secure_getenv("HOME");
441 if (e && path_is_absolute(e)) {
442 if (endswith(e, "/"))
443 h = strappend(e, home_suffix);
444 else
445 h = strjoin(e, "/", home_suffix);
446
447 if (!h) {
448 strv_free(l);
449 return -ENOMEM;
450 }
451 }
452 }
453
454 if (h) {
455 r = strv_consume_prepend(&l, h);
456 if (r < 0) {
457 strv_free(l);
458 return -ENOMEM;
459 }
460 }
461
462 *list = l;
463 return 0;
464 }
465
466 #if HAVE_SPLIT_BIN
467 # define ARRAY_SBIN_BIN(x) x "sbin", x "bin"
468 #else
469 # define ARRAY_SBIN_BIN(x) x "bin"
470 #endif
471
472 static int get_search(uint64_t type, char ***list) {
473
474 assert(list);
475
476 switch(type) {
477
478 case SD_PATH_SEARCH_BINARIES:
479 return search_from_environment(list,
480 NULL,
481 ".local/bin",
482 "PATH",
483 true,
484 ARRAY_SBIN_BIN("/usr/local/"),
485 ARRAY_SBIN_BIN("/usr/"),
486 #if HAVE_SPLIT_USR
487 ARRAY_SBIN_BIN("/"),
488 #endif
489 NULL);
490
491 case SD_PATH_SEARCH_LIBRARY_PRIVATE:
492 return search_from_environment(list,
493 NULL,
494 ".local/lib",
495 NULL,
496 false,
497 "/usr/local/lib",
498 "/usr/lib",
499 #if HAVE_SPLIT_USR
500 "/lib",
501 #endif
502 NULL);
503
504 case SD_PATH_SEARCH_LIBRARY_ARCH:
505 return search_from_environment(list,
506 NULL,
507 ".local/lib/" LIB_ARCH_TUPLE,
508 "LD_LIBRARY_PATH",
509 true,
510 LIBDIR,
511 #if HAVE_SPLIT_USR
512 ROOTLIBDIR,
513 #endif
514 NULL);
515
516 case SD_PATH_SEARCH_SHARED:
517 return search_from_environment(list,
518 "XDG_DATA_HOME",
519 ".local/share",
520 "XDG_DATA_DIRS",
521 false,
522 "/usr/local/share",
523 "/usr/share",
524 NULL);
525
526 case SD_PATH_SEARCH_CONFIGURATION_FACTORY:
527 return search_from_environment(list,
528 NULL,
529 NULL,
530 NULL,
531 false,
532 "/usr/local/share/factory/etc",
533 "/usr/share/factory/etc",
534 NULL);
535
536 case SD_PATH_SEARCH_STATE_FACTORY:
537 return search_from_environment(list,
538 NULL,
539 NULL,
540 NULL,
541 false,
542 "/usr/local/share/factory/var",
543 "/usr/share/factory/var",
544 NULL);
545
546 case SD_PATH_SEARCH_CONFIGURATION:
547 return search_from_environment(list,
548 "XDG_CONFIG_HOME",
549 ".config",
550 "XDG_CONFIG_DIRS",
551 false,
552 "/etc",
553 NULL);
554
555 case SD_PATH_SEARCH_BINARIES_DEFAULT: {
556 char **t;
557
558 t = strv_split_nulstr(DEFAULT_PATH_NULSTR);
559 if (!t)
560 return -ENOMEM;
561
562 *list = t;
563 return 0;
564 }}
565
566 return -EOPNOTSUPP;
567 }
568
569 _public_ int sd_path_search(uint64_t type, const char *suffix, char ***paths) {
570 char **i, **j;
571 _cleanup_strv_free_ char **l = NULL, **n = NULL;
572 int r;
573
574 assert_return(paths, -EINVAL);
575
576 if (!IN_SET(type,
577 SD_PATH_SEARCH_BINARIES,
578 SD_PATH_SEARCH_BINARIES_DEFAULT,
579 SD_PATH_SEARCH_LIBRARY_PRIVATE,
580 SD_PATH_SEARCH_LIBRARY_ARCH,
581 SD_PATH_SEARCH_SHARED,
582 SD_PATH_SEARCH_CONFIGURATION_FACTORY,
583 SD_PATH_SEARCH_STATE_FACTORY,
584 SD_PATH_SEARCH_CONFIGURATION)) {
585
586 char *p;
587
588 r = sd_path_home(type, suffix, &p);
589 if (r < 0)
590 return r;
591
592 l = new(char*, 2);
593 if (!l) {
594 free(p);
595 return -ENOMEM;
596 }
597
598 l[0] = p;
599 l[1] = NULL;
600
601 *paths = TAKE_PTR(l);
602 return 0;
603 }
604
605 r = get_search(type, &l);
606 if (r < 0)
607 return r;
608
609 if (!suffix) {
610 *paths = TAKE_PTR(l);
611 return 0;
612 }
613
614 n = new(char*, strv_length(l)+1);
615 if (!n)
616 return -ENOMEM;
617
618 j = n;
619 STRV_FOREACH(i, l) {
620
621 if (endswith(*i, "/"))
622 *j = strappend(*i, suffix);
623 else
624 *j = strjoin(*i, "/", suffix);
625
626 if (!*j)
627 return -ENOMEM;
628
629 j++;
630 }
631
632 *j = NULL;
633 *paths = TAKE_PTR(n);
634 return 0;
635 }