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