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