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