]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-path/sd-path.c
Add SPDX license identifiers to source files under the LGPL
[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 static int get_search(uint64_t type, char ***list) {
482
483 assert(list);
484
485 switch(type) {
486
487 case SD_PATH_SEARCH_BINARIES:
488 return search_from_environment(list,
489 NULL,
490 ".local/bin",
491 "PATH",
492 true,
493 "/usr/local/sbin",
494 "/usr/local/bin",
495 "/usr/sbin",
496 "/usr/bin",
497 #if HAVE_SPLIT_USR
498 "/sbin",
499 "/bin",
500 #endif
501 NULL);
502
503 case SD_PATH_SEARCH_LIBRARY_PRIVATE:
504 return search_from_environment(list,
505 NULL,
506 ".local/lib",
507 NULL,
508 false,
509 "/usr/local/lib",
510 "/usr/lib",
511 #if HAVE_SPLIT_USR
512 "/lib",
513 #endif
514 NULL);
515
516 case SD_PATH_SEARCH_LIBRARY_ARCH:
517 return search_from_environment(list,
518 NULL,
519 ".local/lib/" LIB_ARCH_TUPLE,
520 "LD_LIBRARY_PATH",
521 true,
522 LIBDIR,
523 #if HAVE_SPLIT_USR
524 ROOTLIBDIR,
525 #endif
526 NULL);
527
528 case SD_PATH_SEARCH_SHARED:
529 return search_from_environment(list,
530 "XDG_DATA_HOME",
531 ".local/share",
532 "XDG_DATA_DIRS",
533 false,
534 "/usr/local/share",
535 "/usr/share",
536 NULL);
537
538 case SD_PATH_SEARCH_CONFIGURATION_FACTORY:
539 return search_from_environment(list,
540 NULL,
541 NULL,
542 NULL,
543 false,
544 "/usr/local/share/factory/etc",
545 "/usr/share/factory/etc",
546 NULL);
547
548 case SD_PATH_SEARCH_STATE_FACTORY:
549 return search_from_environment(list,
550 NULL,
551 NULL,
552 NULL,
553 false,
554 "/usr/local/share/factory/var",
555 "/usr/share/factory/var",
556 NULL);
557
558 case SD_PATH_SEARCH_CONFIGURATION:
559 return search_from_environment(list,
560 "XDG_CONFIG_HOME",
561 ".config",
562 "XDG_CONFIG_DIRS",
563 false,
564 "/etc",
565 NULL);
566 }
567
568 return -EOPNOTSUPP;
569 }
570
571 _public_ int sd_path_search(uint64_t type, const char *suffix, char ***paths) {
572 char **l, **i, **j, **n;
573 int r;
574
575 assert_return(paths, -EINVAL);
576
577 if (!IN_SET(type,
578 SD_PATH_SEARCH_BINARIES,
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 = 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 = l;
611 return 0;
612 }
613
614 n = new(char*, strv_length(l)+1);
615 if (!n) {
616 strv_free(l);
617 return -ENOMEM;
618 }
619
620 j = n;
621 STRV_FOREACH(i, l) {
622
623 if (endswith(*i, "/"))
624 *j = strappend(*i, suffix);
625 else
626 *j = strjoin(*i, "/", suffix);
627
628 if (!*j) {
629 strv_free(l);
630 strv_free(n);
631 return -ENOMEM;
632 }
633
634 j++;
635 }
636
637 *j = NULL;
638 *paths = n;
639 return 0;
640 }