chiark / gitweb /
Merge systemd-verify with systemd-analyze
[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 <assert.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <errno.h>
28
29 #include "util.h"
30 #include "mkdir.h"
31 #include "strv.h"
32 #include "path-util.h"
33 #include "path-lookup.h"
34
35 int user_config_home(char **config_home) {
36         const char *e;
37         char *r;
38
39         e = getenv("XDG_CONFIG_HOME");
40         if (e) {
41                 r = strappend(e, "/systemd/user");
42                 if (!r)
43                         return -ENOMEM;
44
45                 *config_home = r;
46                 return 1;
47         } else {
48                 const char *home;
49
50                 home = getenv("HOME");
51                 if (home) {
52                         r = strappend(home, "/.config/systemd/user");
53                         if (!r)
54                                 return -ENOMEM;
55
56                         *config_home = r;
57                         return 1;
58                 }
59         }
60
61         return 0;
62 }
63
64 static char** user_dirs(
65                 const char *generator,
66                 const char *generator_early,
67                 const char *generator_late) {
68
69         const char * const config_unit_paths[] = {
70                 USER_CONFIG_UNIT_PATH,
71                 "/etc/systemd/user",
72                 "/run/systemd/user",
73                 NULL
74         };
75
76         const char * const data_unit_paths[] = {
77                 "/usr/local/lib/systemd/user",
78                 "/usr/local/share/systemd/user",
79                 USER_DATA_UNIT_PATH,
80                 "/usr/lib/systemd/user",
81                 "/usr/share/systemd/user",
82                 NULL
83         };
84
85         const char *home, *e;
86         _cleanup_free_ char *config_home = NULL, *data_home = NULL;
87         _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
88         char **r = NULL;
89
90         /* Implement the mechanisms defined in
91          *
92          * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
93          *
94          * We look in both the config and the data dirs because we
95          * want to encourage that distributors ship their unit files
96          * as data, and allow overriding as configuration.
97          */
98
99         if (user_config_home(&config_home) < 0)
100                 goto fail;
101
102         home = getenv("HOME");
103
104         e = getenv("XDG_CONFIG_DIRS");
105         if (e) {
106                 config_dirs = strv_split(e, ":");
107                 if (!config_dirs)
108                         goto fail;
109         }
110
111         /* We don't treat /etc/xdg/systemd here as the spec
112          * suggests because we assume that that is a link to
113          * /etc/systemd/ anyway. */
114
115         e = getenv("XDG_DATA_HOME");
116         if (e) {
117                 if (asprintf(&data_home, "%s/systemd/user", e) < 0)
118                         goto fail;
119
120         } else if (home) {
121                 if (asprintf(&data_home, "%s/.local/share/systemd/user", home) < 0)
122                         goto fail;
123         }
124
125         e = getenv("XDG_DATA_DIRS");
126         if (e)
127                 data_dirs = strv_split(e, ":");
128         else
129                 data_dirs = strv_new("/usr/local/share",
130                                      "/usr/share",
131                                      NULL);
132         if (!data_dirs)
133                 goto fail;
134
135         /* Now merge everything we found. */
136         if (generator_early)
137                 if (strv_extend(&r, generator_early) < 0)
138                         goto fail;
139
140         if (config_home)
141                 if (strv_extend(&r, config_home) < 0)
142                         goto fail;
143
144         if (!strv_isempty(config_dirs))
145                 if (strv_extend_strv_concat(&r, config_dirs, "/systemd/user") < 0)
146                         goto fail;
147
148         if (strv_extend_strv(&r, (char**) config_unit_paths) < 0)
149                 goto fail;
150
151         if (generator)
152                 if (strv_extend(&r, generator) < 0)
153                         goto fail;
154
155         if (data_home)
156                 if (strv_extend(&r, data_home) < 0)
157                         goto fail;
158
159         if (!strv_isempty(data_dirs))
160                 if (strv_extend_strv_concat(&r, data_dirs, "/systemd/user") < 0)
161                         goto fail;
162
163         if (strv_extend_strv(&r, (char**) data_unit_paths) < 0)
164                 goto fail;
165
166         if (generator_late)
167                 if (strv_extend(&r, generator_late) < 0)
168                         goto fail;
169
170         if (!path_strv_make_absolute_cwd(r))
171                 goto fail;
172
173         return r;
174
175 fail:
176         strv_free(r);
177         return NULL;
178 }
179
180 int lookup_paths_init(
181                 LookupPaths *p,
182                 SystemdRunningAs running_as,
183                 bool personal,
184                 const char *root_dir,
185                 const char *generator,
186                 const char *generator_early,
187                 const char *generator_late) {
188
189         const char *e;
190         bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
191
192         assert(p);
193
194         /* First priority is whatever has been passed to us via env
195          * vars */
196         e = getenv("SYSTEMD_UNIT_PATH");
197         if (e) {
198                 if (endswith(e, ":")) {
199                         e = strndupa(e, strlen(e) - 1);
200                         append = true;
201                 }
202
203                 /* FIXME: empty components in other places should be
204                  * rejected. */
205
206                 p->unit_path = path_split_and_make_absolute(e);
207                 if (!p->unit_path)
208                         return -ENOMEM;
209         } else
210                 p->unit_path = NULL;
211
212         if (!p->unit_path || append) {
213                 /* Let's figure something out. */
214
215                 _cleanup_strv_free_ char **unit_path;
216                 int r;
217
218                 /* For the user units we include share/ in the search
219                  * path in order to comply with the XDG basedir spec.
220                  * For the system stuff we avoid such nonsense. OTOH
221                  * we include /lib in the search path for the system
222                  * stuff but avoid it for user stuff. */
223
224                 if (running_as == SYSTEMD_USER) {
225                         if (personal)
226                                 unit_path = user_dirs(generator, generator_early, generator_late);
227                         else
228                                 unit_path = strv_new(
229                                         /* If you modify this you also want to modify
230                                          * systemduserunitpath= in systemd.pc.in, and
231                                          * the arrays in user_dirs() above! */
232                                         STRV_IFNOTNULL(generator_early),
233                                         USER_CONFIG_UNIT_PATH,
234                                         "/etc/systemd/user",
235                                         "/run/systemd/user",
236                                         STRV_IFNOTNULL(generator),
237                                         "/usr/local/lib/systemd/user",
238                                         "/usr/local/share/systemd/user",
239                                         USER_DATA_UNIT_PATH,
240                                         "/usr/lib/systemd/user",
241                                         "/usr/share/systemd/user",
242                                         STRV_IFNOTNULL(generator_late),
243                                         NULL);
244                 } else
245                         unit_path = strv_new(
246                                 /* If you modify this you also want to modify
247                                  * systemdsystemunitpath= in systemd.pc.in! */
248                                 STRV_IFNOTNULL(generator_early),
249                                 SYSTEM_CONFIG_UNIT_PATH,
250                                 "/etc/systemd/system",
251                                 "/run/systemd/system",
252                                 STRV_IFNOTNULL(generator),
253                                 "/usr/local/lib/systemd/system",
254                                 SYSTEM_DATA_UNIT_PATH,
255                                 "/usr/lib/systemd/system",
256 #ifdef HAVE_SPLIT_USR
257                                 "/lib/systemd/system",
258 #endif
259                                 STRV_IFNOTNULL(generator_late),
260                                 NULL);
261
262                 if (!unit_path)
263                         return -ENOMEM;
264
265                 r = strv_extend_strv(&p->unit_path, unit_path);
266                 if (r < 0)
267                         return r;
268         }
269
270         if (!path_strv_resolve_uniq(p->unit_path, root_dir))
271                 return -ENOMEM;
272
273         if (!strv_isempty(p->unit_path)) {
274                 _cleanup_free_ char *t = strv_join(p->unit_path, "\n\t");
275                 if (!t)
276                         return -ENOMEM;
277                 log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
278         } else {
279                 log_debug("Ignoring unit files.");
280                 strv_free(p->unit_path);
281                 p->unit_path = NULL;
282         }
283
284         if (running_as == SYSTEMD_SYSTEM) {
285 #ifdef HAVE_SYSV_COMPAT
286                 /* /etc/init.d/ compatibility does not matter to users */
287
288                 e = getenv("SYSTEMD_SYSVINIT_PATH");
289                 if (e) {
290                         p->sysvinit_path = path_split_and_make_absolute(e);
291                         if (!p->sysvinit_path)
292                                 return -ENOMEM;
293                 } else
294                         p->sysvinit_path = NULL;
295
296                 if (strv_isempty(p->sysvinit_path)) {
297                         strv_free(p->sysvinit_path);
298
299                         p->sysvinit_path = strv_new(
300                                         SYSTEM_SYSVINIT_PATH,     /* /etc/init.d/ */
301                                         NULL);
302                         if (!p->sysvinit_path)
303                                 return -ENOMEM;
304                 }
305
306                 e = getenv("SYSTEMD_SYSVRCND_PATH");
307                 if (e) {
308                         p->sysvrcnd_path = path_split_and_make_absolute(e);
309                         if (!p->sysvrcnd_path)
310                                 return -ENOMEM;
311                 } else
312                         p->sysvrcnd_path = NULL;
313
314                 if (strv_isempty(p->sysvrcnd_path)) {
315                         strv_free(p->sysvrcnd_path);
316
317                         p->sysvrcnd_path = strv_new(
318                                         SYSTEM_SYSVRCND_PATH,     /* /etc/rcN.d/ */
319                                         NULL);
320                         if (!p->sysvrcnd_path)
321                                 return -ENOMEM;
322                 }
323
324                 if (!path_strv_resolve_uniq(p->sysvinit_path, root_dir))
325                         return -ENOMEM;
326
327                 if (!path_strv_resolve_uniq(p->sysvrcnd_path, root_dir))
328                         return -ENOMEM;
329
330                 if (!strv_isempty(p->sysvinit_path)) {
331                         _cleanup_free_ char *t = strv_join(p->sysvinit_path, "\n\t");
332                         if (!t)
333                                 return -ENOMEM;
334                         log_debug("Looking for SysV init scripts in:\n\t%s", t);
335                 } else {
336                         log_debug("Ignoring SysV init scripts.");
337                         strv_free(p->sysvinit_path);
338                         p->sysvinit_path = NULL;
339                 }
340
341                 if (!strv_isempty(p->sysvrcnd_path)) {
342                         _cleanup_free_ char *t =
343                                 strv_join(p->sysvrcnd_path, "\n\t");
344                         if (!t)
345                                 return -ENOMEM;
346
347                         log_debug("Looking for SysV rcN.d links in:\n\t%s", t);
348                 } else {
349                         log_debug("Ignoring SysV rcN.d links.");
350                         strv_free(p->sysvrcnd_path);
351                         p->sysvrcnd_path = NULL;
352                 }
353 #else
354                 log_debug("SysV init scripts and rcN.d links support disabled");
355 #endif
356         }
357
358         return 0;
359 }
360
361 void lookup_paths_free(LookupPaths *p) {
362         assert(p);
363
364         strv_free(p->unit_path);
365         p->unit_path = NULL;
366
367 #ifdef HAVE_SYSV_COMPAT
368         strv_free(p->sysvinit_path);
369         strv_free(p->sysvrcnd_path);
370         p->sysvinit_path = p->sysvrcnd_path = NULL;
371 #endif
372 }