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