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