chiark / gitweb /
Remove SysV compat
[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(SystemdRunningAs running_as) {
222         if (running_as == SYSTEMD_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                 SystemdRunningAs 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 == SYSTEMD_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 == SYSTEMD_SYSTEM) {
341                 log_debug("SysV init scripts and rcN.d links support disabled");
342         }
343
344         return 0;
345 }
346
347 void lookup_paths_free(LookupPaths *p) {
348         assert(p);
349
350         strv_free(p->unit_path);
351         p->unit_path = NULL;
352 }
353
354 int lookup_paths_init_from_scope(LookupPaths *paths,
355                                  UnitFileScope scope,
356                                  const char *root_dir) {
357         assert(paths);
358         assert(scope >= 0);
359         assert(scope < _UNIT_FILE_SCOPE_MAX);
360
361         zero(*paths);
362
363         return lookup_paths_init(paths,
364                                  scope == UNIT_FILE_SYSTEM ? SYSTEMD_SYSTEM : SYSTEMD_USER,
365                                  scope == UNIT_FILE_USER,
366                                  root_dir,
367                                  NULL, NULL, NULL);
368 }