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