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