chiark / gitweb /
journalctl: clean up how we log errors
[elogind.git] / src / shared / path-lookup.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <errno.h>
26
27 #include "util.h"
28 #include "strv.h"
29 #include "path-util.h"
30 #include "path-lookup.h"
31 #include "install.h"
32
33 int user_config_home(char **config_home) {
34         const char *e;
35         char *r;
36
37         e = getenv("XDG_CONFIG_HOME");
38         if (e) {
39                 r = strappend(e, "/systemd/user");
40                 if (!r)
41                         return -ENOMEM;
42
43                 *config_home = r;
44                 return 1;
45         } else {
46                 const char *home;
47
48                 home = getenv("HOME");
49                 if (home) {
50                         r = strappend(home, "/.config/systemd/user");
51                         if (!r)
52                                 return -ENOMEM;
53
54                         *config_home = r;
55                         return 1;
56                 }
57         }
58
59         return 0;
60 }
61
62 int user_runtime_dir(char **runtime_dir) {
63         const char *e;
64         char *r;
65
66         e = getenv("XDG_RUNTIME_DIR");
67         if (e) {
68                 r = strappend(e, "/systemd/user");
69                 if (!r)
70                         return -ENOMEM;
71
72                 *runtime_dir = r;
73                 return 1;
74         }
75
76         return 0;
77 }
78
79 static int user_data_home_dir(char **dir, const char *suffix) {
80         const char *e;
81         char *res;
82
83         /* We don't treat /etc/xdg/systemd here as the spec
84          * suggests because we assume that that is a link to
85          * /etc/systemd/ anyway. */
86
87         e = getenv("XDG_DATA_HOME");
88         if (e)
89                 res = strappend(e, suffix);
90         else {
91                 const char *home;
92
93                 home = getenv("HOME");
94                 if (home)
95                         res = strjoin(home, "/.local/share", suffix, NULL);
96                 else
97                         return 0;
98         }
99         if (!res)
100                 return -ENOMEM;
101
102         *dir = res;
103         return 0;
104 }
105
106 static char** user_dirs(
107                 const char *generator,
108                 const char *generator_early,
109                 const char *generator_late) {
110
111         const char * const config_unit_paths[] = {
112                 USER_CONFIG_UNIT_PATH,
113                 "/etc/systemd/user",
114                 NULL
115         };
116
117         const char * const runtime_unit_path = "/run/systemd/user";
118
119         const char * const data_unit_paths[] = {
120                 "/usr/local/lib/systemd/user",
121                 "/usr/local/share/systemd/user",
122                 USER_DATA_UNIT_PATH,
123                 "/usr/lib/systemd/user",
124                 "/usr/share/systemd/user",
125                 NULL
126         };
127
128         const char *e;
129         _cleanup_free_ char *config_home = NULL, *runtime_dir = NULL, *data_home = NULL;
130         _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
131         _cleanup_free_ char **res = NULL;
132         char **tmp;
133         int r;
134
135         /* Implement the mechanisms defined in
136          *
137          * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
138          *
139          * We look in both the config and the data dirs because we
140          * want to encourage that distributors ship their unit files
141          * as data, and allow overriding as configuration.
142          */
143
144         if (user_config_home(&config_home) < 0)
145                 return NULL;
146
147         if (user_runtime_dir(&runtime_dir) < 0)
148                 return NULL;
149
150         e = getenv("XDG_CONFIG_DIRS");
151         if (e) {
152                 config_dirs = strv_split(e, ":");
153                 if (!config_dirs)
154                         return NULL;
155         }
156
157         r = user_data_home_dir(&data_home, "/systemd/user");
158         if (r < 0)
159                 return NULL;
160
161         e = getenv("XDG_DATA_DIRS");
162         if (e)
163                 data_dirs = strv_split(e, ":");
164         else
165                 data_dirs = strv_new("/usr/local/share",
166                                      "/usr/share",
167                                      NULL);
168         if (!data_dirs)
169                 return NULL;
170
171         /* Now merge everything we found. */
172         if (generator_early)
173                 if (strv_extend(&res, generator_early) < 0)
174                         return NULL;
175
176         if (config_home)
177                 if (strv_extend(&res, config_home) < 0)
178                         return NULL;
179
180         if (!strv_isempty(config_dirs))
181                 if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0)
182                         return NULL;
183
184         if (strv_extend_strv(&res, (char**) config_unit_paths) < 0)
185                 return NULL;
186
187         if (runtime_dir)
188                 if (strv_extend(&res, runtime_dir) < 0)
189                         return NULL;
190
191         if (strv_extend(&res, runtime_unit_path) < 0)
192                 return NULL;
193
194         if (generator)
195                 if (strv_extend(&res, generator) < 0)
196                         return NULL;
197
198         if (data_home)
199                 if (strv_extend(&res, data_home) < 0)
200                         return NULL;
201
202         if (!strv_isempty(data_dirs))
203                 if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0)
204                         return NULL;
205
206         if (strv_extend_strv(&res, (char**) data_unit_paths) < 0)
207                 return NULL;
208
209         if (generator_late)
210                 if (strv_extend(&res, generator_late) < 0)
211                         return NULL;
212
213         if (!path_strv_make_absolute_cwd(res))
214                 return NULL;
215
216         tmp = res;
217         res = NULL;
218         return tmp;
219 }
220
221 char **generator_paths(ManagerRunningAs running_as) {
222         if (running_as == MANAGER_USER)
223                 return strv_new("/run/systemd/user-generators",
224                                 "/etc/systemd/user-generators",
225                                 "/usr/local/lib/systemd/user-generators",
226                                 USER_GENERATOR_PATH,
227                                 NULL);
228         else
229                 return strv_new("/run/systemd/system-generators",
230                                 "/etc/systemd/system-generators",
231                                 "/usr/local/lib/systemd/system-generators",
232                                 SYSTEM_GENERATOR_PATH,
233                                 NULL);
234 }
235
236 int lookup_paths_init(
237                 LookupPaths *p,
238                 ManagerRunningAs running_as,
239                 bool personal,
240                 const char *root_dir,
241                 const char *generator,
242                 const char *generator_early,
243                 const char *generator_late) {
244
245         const char *e;
246         bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
247
248         assert(p);
249
250         /* First priority is whatever has been passed to us via env
251          * vars */
252         e = getenv("SYSTEMD_UNIT_PATH");
253         if (e) {
254                 if (endswith(e, ":")) {
255                         e = strndupa(e, strlen(e) - 1);
256                         append = true;
257                 }
258
259                 /* FIXME: empty components in other places should be
260                  * rejected. */
261
262                 p->unit_path = path_split_and_make_absolute(e);
263                 if (!p->unit_path)
264                         return -ENOMEM;
265         } else
266                 p->unit_path = NULL;
267
268         if (!p->unit_path || append) {
269                 /* Let's figure something out. */
270
271                 _cleanup_strv_free_ char **unit_path;
272                 int r;
273
274                 /* For the user units we include share/ in the search
275                  * path in order to comply with the XDG basedir spec.
276                  * For the system stuff we avoid such nonsense. OTOH
277                  * we include /lib in the search path for the system
278                  * stuff but avoid it for user stuff. */
279
280                 if (running_as == MANAGER_USER) {
281                         if (personal)
282                                 unit_path = user_dirs(generator, generator_early, generator_late);
283                         else
284                                 unit_path = strv_new(
285                                         /* If you modify this you also want to modify
286                                          * systemduserunitpath= in systemd.pc.in, and
287                                          * the arrays in user_dirs() above! */
288                                         STRV_IFNOTNULL(generator_early),
289                                         USER_CONFIG_UNIT_PATH,
290                                         "/etc/systemd/user",
291                                         "/run/systemd/user",
292                                         STRV_IFNOTNULL(generator),
293                                         "/usr/local/lib/systemd/user",
294                                         "/usr/local/share/systemd/user",
295                                         USER_DATA_UNIT_PATH,
296                                         "/usr/lib/systemd/user",
297                                         "/usr/share/systemd/user",
298                                         STRV_IFNOTNULL(generator_late),
299                                         NULL);
300                 } else
301                         unit_path = strv_new(
302                                 /* If you modify this you also want to modify
303                                  * systemdsystemunitpath= in systemd.pc.in! */
304                                 STRV_IFNOTNULL(generator_early),
305                                 SYSTEM_CONFIG_UNIT_PATH,
306                                 "/etc/systemd/system",
307                                 "/run/systemd/system",
308                                 STRV_IFNOTNULL(generator),
309                                 "/usr/local/lib/systemd/system",
310                                 SYSTEM_DATA_UNIT_PATH,
311                                 "/usr/lib/systemd/system",
312 #ifdef HAVE_SPLIT_USR
313                                 "/lib/systemd/system",
314 #endif
315                                 STRV_IFNOTNULL(generator_late),
316                                 NULL);
317
318                 if (!unit_path)
319                         return -ENOMEM;
320
321                 r = strv_extend_strv(&p->unit_path, unit_path);
322                 if (r < 0)
323                         return r;
324         }
325
326         if (!path_strv_resolve_uniq(p->unit_path, root_dir))
327                 return -ENOMEM;
328
329         if (!strv_isempty(p->unit_path)) {
330                 _cleanup_free_ char *t = strv_join(p->unit_path, "\n\t");
331                 if (!t)
332                         return -ENOMEM;
333                 log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
334         } else {
335                 log_debug("Ignoring unit files.");
336                 strv_free(p->unit_path);
337                 p->unit_path = NULL;
338         }
339
340         if (running_as == MANAGER_SYSTEM) {
341 #ifdef HAVE_SYSV_COMPAT
342                 /* /etc/init.d/ compatibility does not matter to users */
343
344                 e = getenv("SYSTEMD_SYSVINIT_PATH");
345                 if (e) {
346                         p->sysvinit_path = path_split_and_make_absolute(e);
347                         if (!p->sysvinit_path)
348                                 return -ENOMEM;
349                 } else
350                         p->sysvinit_path = NULL;
351
352                 if (strv_isempty(p->sysvinit_path)) {
353                         strv_free(p->sysvinit_path);
354
355                         p->sysvinit_path = strv_new(
356                                         SYSTEM_SYSVINIT_PATH,     /* /etc/init.d/ */
357                                         NULL);
358                         if (!p->sysvinit_path)
359                                 return -ENOMEM;
360                 }
361
362                 e = getenv("SYSTEMD_SYSVRCND_PATH");
363                 if (e) {
364                         p->sysvrcnd_path = path_split_and_make_absolute(e);
365                         if (!p->sysvrcnd_path)
366                                 return -ENOMEM;
367                 } else
368                         p->sysvrcnd_path = NULL;
369
370                 if (strv_isempty(p->sysvrcnd_path)) {
371                         strv_free(p->sysvrcnd_path);
372
373                         p->sysvrcnd_path = strv_new(
374                                         SYSTEM_SYSVRCND_PATH,     /* /etc/rcN.d/ */
375                                         NULL);
376                         if (!p->sysvrcnd_path)
377                                 return -ENOMEM;
378                 }
379
380                 if (!path_strv_resolve_uniq(p->sysvinit_path, root_dir))
381                         return -ENOMEM;
382
383                 if (!path_strv_resolve_uniq(p->sysvrcnd_path, root_dir))
384                         return -ENOMEM;
385
386                 if (!strv_isempty(p->sysvinit_path)) {
387                         _cleanup_free_ char *t = strv_join(p->sysvinit_path, "\n\t");
388                         if (!t)
389                                 return -ENOMEM;
390                         log_debug("Looking for SysV init scripts in:\n\t%s", t);
391                 } else {
392                         log_debug("Ignoring SysV init scripts.");
393                         strv_free(p->sysvinit_path);
394                         p->sysvinit_path = NULL;
395                 }
396
397                 if (!strv_isempty(p->sysvrcnd_path)) {
398                         _cleanup_free_ char *t =
399                                 strv_join(p->sysvrcnd_path, "\n\t");
400                         if (!t)
401                                 return -ENOMEM;
402
403                         log_debug("Looking for SysV rcN.d links in:\n\t%s", t);
404                 } else {
405                         log_debug("Ignoring SysV rcN.d links.");
406                         strv_free(p->sysvrcnd_path);
407                         p->sysvrcnd_path = NULL;
408                 }
409 #else
410                 log_debug("SysV init scripts and rcN.d links support disabled");
411 #endif
412         }
413
414         return 0;
415 }
416
417 void lookup_paths_free(LookupPaths *p) {
418         assert(p);
419
420         strv_free(p->unit_path);
421         p->unit_path = NULL;
422
423 #ifdef HAVE_SYSV_COMPAT
424         strv_free(p->sysvinit_path);
425         strv_free(p->sysvrcnd_path);
426         p->sysvinit_path = p->sysvrcnd_path = NULL;
427 #endif
428 }
429
430 int lookup_paths_init_from_scope(LookupPaths *paths,
431                                  UnitFileScope scope,
432                                  const char *root_dir) {
433         assert(paths);
434         assert(scope >= 0);
435         assert(scope < _UNIT_FILE_SCOPE_MAX);
436
437         zero(*paths);
438
439         return lookup_paths_init(paths,
440                                  scope == UNIT_FILE_SYSTEM ? MANAGER_SYSTEM : MANAGER_USER,
441                                  scope == UNIT_FILE_USER,
442                                  root_dir,
443                                  NULL, NULL, NULL);
444 }