chiark / gitweb /
util: rework rm_rf() logic
[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 int lookup_paths_init(
222                 LookupPaths *p,
223                 SystemdRunningAs running_as,
224                 bool personal,
225                 const char *root_dir,
226                 const char *generator,
227                 const char *generator_early,
228                 const char *generator_late) {
229
230         const char *e;
231         bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
232
233         assert(p);
234
235         /* First priority is whatever has been passed to us via env
236          * vars */
237         e = getenv("SYSTEMD_UNIT_PATH");
238         if (e) {
239                 if (endswith(e, ":")) {
240                         e = strndupa(e, strlen(e) - 1);
241                         append = true;
242                 }
243
244                 /* FIXME: empty components in other places should be
245                  * rejected. */
246
247                 p->unit_path = path_split_and_make_absolute(e);
248                 if (!p->unit_path)
249                         return -ENOMEM;
250         } else
251                 p->unit_path = NULL;
252
253         if (!p->unit_path || append) {
254                 /* Let's figure something out. */
255
256                 _cleanup_strv_free_ char **unit_path;
257                 int r;
258
259                 /* For the user units we include share/ in the search
260                  * path in order to comply with the XDG basedir spec.
261                  * For the system stuff we avoid such nonsense. OTOH
262                  * we include /lib in the search path for the system
263                  * stuff but avoid it for user stuff. */
264
265                 if (running_as == SYSTEMD_USER) {
266                         if (personal)
267                                 unit_path = user_dirs(generator, generator_early, generator_late);
268                         else
269                                 unit_path = strv_new(
270                                         /* If you modify this you also want to modify
271                                          * systemduserunitpath= in systemd.pc.in, and
272                                          * the arrays in user_dirs() above! */
273                                         STRV_IFNOTNULL(generator_early),
274                                         USER_CONFIG_UNIT_PATH,
275                                         "/etc/systemd/user",
276                                         "/run/systemd/user",
277                                         STRV_IFNOTNULL(generator),
278                                         "/usr/local/lib/systemd/user",
279                                         "/usr/local/share/systemd/user",
280                                         USER_DATA_UNIT_PATH,
281                                         "/usr/lib/systemd/user",
282                                         "/usr/share/systemd/user",
283                                         STRV_IFNOTNULL(generator_late),
284                                         NULL);
285                 } else
286                         unit_path = strv_new(
287                                 /* If you modify this you also want to modify
288                                  * systemdsystemunitpath= in systemd.pc.in! */
289                                 STRV_IFNOTNULL(generator_early),
290                                 SYSTEM_CONFIG_UNIT_PATH,
291                                 "/etc/systemd/system",
292                                 "/run/systemd/system",
293                                 STRV_IFNOTNULL(generator),
294                                 "/usr/local/lib/systemd/system",
295                                 SYSTEM_DATA_UNIT_PATH,
296                                 "/usr/lib/systemd/system",
297 #ifdef HAVE_SPLIT_USR
298                                 "/lib/systemd/system",
299 #endif
300                                 STRV_IFNOTNULL(generator_late),
301                                 NULL);
302
303                 if (!unit_path)
304                         return -ENOMEM;
305
306                 r = strv_extend_strv(&p->unit_path, unit_path);
307                 if (r < 0)
308                         return r;
309         }
310
311         if (!path_strv_resolve_uniq(p->unit_path, root_dir))
312                 return -ENOMEM;
313
314         if (!strv_isempty(p->unit_path)) {
315                 _cleanup_free_ char *t = strv_join(p->unit_path, "\n\t");
316                 if (!t)
317                         return -ENOMEM;
318                 log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
319         } else {
320                 log_debug("Ignoring unit files.");
321                 strv_free(p->unit_path);
322                 p->unit_path = NULL;
323         }
324
325         if (running_as == SYSTEMD_SYSTEM) {
326                 log_debug("SysV init scripts and rcN.d links support disabled");
327         }
328
329         return 0;
330 }
331
332 void lookup_paths_free(LookupPaths *p) {
333         assert(p);
334
335         strv_free(p->unit_path);
336         p->unit_path = NULL;
337 }
338
339 int lookup_paths_init_from_scope(LookupPaths *paths,
340                                  UnitFileScope scope,
341                                  const char *root_dir) {
342         assert(paths);
343         assert(scope >= 0);
344         assert(scope < _UNIT_FILE_SCOPE_MAX);
345
346         zero(*paths);
347
348         return lookup_paths_init(paths,
349                                  scope == UNIT_FILE_SYSTEM ? SYSTEMD_SYSTEM : SYSTEMD_USER,
350                                  scope == UNIT_FILE_USER,
351                                  root_dir,
352                                  NULL, NULL, NULL);
353 }