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