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