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