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