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