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