]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-path/sd-path.c
tree-wide: drop license boilerplate
[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_LIBRARY_PRIVATE,
339 SD_PATH_SEARCH_LIBRARY_ARCH,
340 SD_PATH_SEARCH_SHARED,
341 SD_PATH_SEARCH_CONFIGURATION_FACTORY,
342 SD_PATH_SEARCH_STATE_FACTORY,
343 SD_PATH_SEARCH_CONFIGURATION)) {
344
345 _cleanup_strv_free_ char **l = NULL;
346
347 r = sd_path_search(type, suffix, &l);
348 if (r < 0)
349 return r;
350
351 buffer = strv_join(l, ":");
352 if (!buffer)
353 return -ENOMEM;
354
355 *path = buffer;
356 return 0;
357 }
358
359 r = get_path(type, &buffer, &ret);
360 if (r < 0)
361 return r;
362
363 if (!suffix) {
364 if (!buffer) {
365 buffer = strdup(ret);
366 if (!buffer)
367 return -ENOMEM;
368 }
369
370 *path = buffer;
371 return 0;
372 }
373
374 suffix += strspn(suffix, "/");
375
376 if (endswith(ret, "/"))
377 cc = strappend(ret, suffix);
378 else
379 cc = strjoin(ret, "/", suffix);
380
381 free(buffer);
382
383 if (!cc)
384 return -ENOMEM;
385
386 *path = cc;
387 return 0;
388 }
389
390 static int search_from_environment(
391 char ***list,
392 const char *env_home,
393 const char *home_suffix,
394 const char *env_search,
395 bool env_search_sufficient,
396 const char *first, ...) {
397
398 const char *e;
399 char *h = NULL;
400 char **l = NULL;
401 int r;
402
403 assert(list);
404
405 if (env_search) {
406 e = secure_getenv(env_search);
407 if (e) {
408 l = strv_split(e, ":");
409 if (!l)
410 return -ENOMEM;
411
412 if (env_search_sufficient) {
413 *list = l;
414 return 0;
415 }
416 }
417 }
418
419 if (!l && first) {
420 va_list ap;
421
422 va_start(ap, first);
423 l = strv_new_ap(first, ap);
424 va_end(ap);
425
426 if (!l)
427 return -ENOMEM;
428 }
429
430 if (env_home) {
431 e = secure_getenv(env_home);
432 if (e && path_is_absolute(e)) {
433 h = strdup(e);
434 if (!h) {
435 strv_free(l);
436 return -ENOMEM;
437 }
438 }
439 }
440
441 if (!h && home_suffix) {
442 e = secure_getenv("HOME");
443 if (e && path_is_absolute(e)) {
444 if (endswith(e, "/"))
445 h = strappend(e, home_suffix);
446 else
447 h = strjoin(e, "/", home_suffix);
448
449 if (!h) {
450 strv_free(l);
451 return -ENOMEM;
452 }
453 }
454 }
455
456 if (h) {
457 r = strv_consume_prepend(&l, h);
458 if (r < 0) {
459 strv_free(l);
460 return -ENOMEM;
461 }
462 }
463
464 *list = l;
465 return 0;
466 }
467
468 #if HAVE_SPLIT_BIN
469 # define ARRAY_SBIN_BIN(x) x "sbin", x "bin"
470 #else
471 # define ARRAY_SBIN_BIN(x) x "bin"
472 #endif
473
474 static int get_search(uint64_t type, char ***list) {
475
476 assert(list);
477
478 switch(type) {
479
480 case SD_PATH_SEARCH_BINARIES:
481 return search_from_environment(list,
482 NULL,
483 ".local/bin",
484 "PATH",
485 true,
486 ARRAY_SBIN_BIN("/usr/local/"),
487 ARRAY_SBIN_BIN("/usr/"),
488 #if HAVE_SPLIT_USR
489 ARRAY_SBIN_BIN("/"),
490 #endif
491 NULL);
492
493 case SD_PATH_SEARCH_LIBRARY_PRIVATE:
494 return search_from_environment(list,
495 NULL,
496 ".local/lib",
497 NULL,
498 false,
499 "/usr/local/lib",
500 "/usr/lib",
501 #if HAVE_SPLIT_USR
502 "/lib",
503 #endif
504 NULL);
505
506 case SD_PATH_SEARCH_LIBRARY_ARCH:
507 return search_from_environment(list,
508 NULL,
509 ".local/lib/" LIB_ARCH_TUPLE,
510 "LD_LIBRARY_PATH",
511 true,
512 LIBDIR,
513 #if HAVE_SPLIT_USR
514 ROOTLIBDIR,
515 #endif
516 NULL);
517
518 case SD_PATH_SEARCH_SHARED:
519 return search_from_environment(list,
520 "XDG_DATA_HOME",
521 ".local/share",
522 "XDG_DATA_DIRS",
523 false,
524 "/usr/local/share",
525 "/usr/share",
526 NULL);
527
528 case SD_PATH_SEARCH_CONFIGURATION_FACTORY:
529 return search_from_environment(list,
530 NULL,
531 NULL,
532 NULL,
533 false,
534 "/usr/local/share/factory/etc",
535 "/usr/share/factory/etc",
536 NULL);
537
538 case SD_PATH_SEARCH_STATE_FACTORY:
539 return search_from_environment(list,
540 NULL,
541 NULL,
542 NULL,
543 false,
544 "/usr/local/share/factory/var",
545 "/usr/share/factory/var",
546 NULL);
547
548 case SD_PATH_SEARCH_CONFIGURATION:
549 return search_from_environment(list,
550 "XDG_CONFIG_HOME",
551 ".config",
552 "XDG_CONFIG_DIRS",
553 false,
554 "/etc",
555 NULL);
556 }
557
558 return -EOPNOTSUPP;
559 }
560
561 _public_ int sd_path_search(uint64_t type, const char *suffix, char ***paths) {
562 char **l, **i, **j, **n;
563 int r;
564
565 assert_return(paths, -EINVAL);
566
567 if (!IN_SET(type,
568 SD_PATH_SEARCH_BINARIES,
569 SD_PATH_SEARCH_LIBRARY_PRIVATE,
570 SD_PATH_SEARCH_LIBRARY_ARCH,
571 SD_PATH_SEARCH_SHARED,
572 SD_PATH_SEARCH_CONFIGURATION_FACTORY,
573 SD_PATH_SEARCH_STATE_FACTORY,
574 SD_PATH_SEARCH_CONFIGURATION)) {
575
576 char *p;
577
578 r = sd_path_home(type, suffix, &p);
579 if (r < 0)
580 return r;
581
582 l = new(char*, 2);
583 if (!l) {
584 free(p);
585 return -ENOMEM;
586 }
587
588 l[0] = p;
589 l[1] = NULL;
590
591 *paths = l;
592 return 0;
593 }
594
595 r = get_search(type, &l);
596 if (r < 0)
597 return r;
598
599 if (!suffix) {
600 *paths = l;
601 return 0;
602 }
603
604 n = new(char*, strv_length(l)+1);
605 if (!n) {
606 strv_free(l);
607 return -ENOMEM;
608 }
609
610 j = n;
611 STRV_FOREACH(i, l) {
612
613 if (endswith(*i, "/"))
614 *j = strappend(*i, suffix);
615 else
616 *j = strjoin(*i, "/", suffix);
617
618 if (!*j) {
619 strv_free(l);
620 strv_free(n);
621 return -ENOMEM;
622 }
623
624 j++;
625 }
626
627 *j = NULL;
628 *paths = n;
629 return 0;
630 }