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