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