chiark / gitweb /
41ebc7f5b3e38c7eafe96437d262cb6067df2a65
[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 <unistd.h>
26 #include <errno.h>
27
28 #include "util.h"
29 #include "mkdir.h"
30 #include "strv.h"
31 #include "path-util.h"
32 #include "path-lookup.h"
33
34 int user_config_home(char **config_home) {
35         const char *e;
36
37         if ((e = getenv("XDG_CONFIG_HOME"))) {
38                 if (asprintf(config_home, "%s/systemd/user", e) < 0)
39                         return -ENOMEM;
40
41                 return 1;
42         } else {
43                 const char *home;
44
45                 if ((home = getenv("HOME"))) {
46                         if (asprintf(config_home, "%s/.config/systemd/user", home) < 0)
47                                 return -ENOMEM;
48
49                         return 1;
50                 }
51         }
52
53         return 0;
54 }
55
56 static char** user_dirs(void) {
57         const char * const config_unit_paths[] = {
58                 USER_CONFIG_UNIT_PATH,
59                 "/etc/systemd/user",
60                 "/run/systemd/user",
61                 NULL
62         };
63
64         const char * const data_unit_paths[] = {
65                 "/usr/local/lib/systemd/user",
66                 "/usr/local/share/systemd/user",
67                 USER_DATA_UNIT_PATH,
68                 "/usr/lib/systemd/user",
69                 "/usr/share/systemd/user",
70                 NULL
71         };
72
73         const char *home, *e;
74         char *config_home = NULL, *data_home = NULL;
75         char **config_dirs = NULL, **data_dirs = NULL;
76         char **r = NULL, **t;
77
78         /* Implement the mechanisms defined in
79          *
80          * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
81          *
82          * We look in both the config and the data dirs because we
83          * want to encourage that distributors ship their unit files
84          * as data, and allow overriding as configuration.
85          */
86
87         if (user_config_home(&config_home) < 0)
88                 goto fail;
89
90         home = getenv("HOME");
91
92         if ((e = getenv("XDG_CONFIG_DIRS")))
93                 if (!(config_dirs = strv_split(e, ":")))
94                         goto fail;
95
96         /* We don't treat /etc/xdg/systemd here as the spec
97          * suggests because we assume that that is a link to
98          * /etc/systemd/ anyway. */
99
100         if ((e = getenv("XDG_DATA_HOME"))) {
101                 if (asprintf(&data_home, "%s/systemd/user", e) < 0)
102                         goto fail;
103
104         } else if (home) {
105                 if (asprintf(&data_home, "%s/.local/share/systemd/user", home) < 0)
106                         goto fail;
107
108                 /* There is really no need for two unit dirs in $HOME,
109                  * except to be fully compliant with the XDG spec. We
110                  * now try to link the two dirs, so that we can
111                  * minimize disk seeks a little. Further down we'll
112                  * then filter out this link, if it is actually is
113                  * one. */
114
115                 mkdir_parents(data_home, 0777);
116                 (void) symlink("../../../.config/systemd/user", data_home);
117         }
118
119         if ((e = getenv("XDG_DATA_DIRS")))
120                 data_dirs = strv_split(e, ":");
121         else
122                 data_dirs = strv_new("/usr/local/share",
123                                      "/usr/share",
124                                      NULL);
125
126         if (!data_dirs)
127                 goto fail;
128
129         /* Now merge everything we found. */
130         if (config_home) {
131                 if (!(t = strv_append(r, config_home)))
132                         goto fail;
133                 strv_free(r);
134                 r = t;
135         }
136
137         if (!strv_isempty(config_dirs)) {
138                 if (!(t = strv_merge_concat(r, config_dirs, "/systemd/user")))
139                         goto finish;
140                 strv_free(r);
141                 r = t;
142         }
143
144         if (!(t = strv_merge(r, (char**) config_unit_paths)))
145                 goto fail;
146         strv_free(r);
147         r = t;
148
149         if (data_home) {
150                 if (!(t = strv_append(r, data_home)))
151                         goto fail;
152                 strv_free(r);
153                 r = t;
154         }
155
156         if (!strv_isempty(data_dirs)) {
157                 if (!(t = strv_merge_concat(r, data_dirs, "/systemd/user")))
158                         goto fail;
159                 strv_free(r);
160                 r = t;
161         }
162
163         if (!(t = strv_merge(r, (char**) data_unit_paths)))
164                 goto fail;
165         strv_free(r);
166         r = t;
167
168         if (!path_strv_make_absolute_cwd(r))
169             goto fail;
170
171 finish:
172         free(config_home);
173         strv_free(config_dirs);
174         free(data_home);
175         strv_free(data_dirs);
176
177         return r;
178
179 fail:
180         strv_free(r);
181         r = NULL;
182         goto finish;
183 }
184
185 int lookup_paths_init(LookupPaths *p, ManagerRunningAs running_as, bool personal) {
186         const char *e;
187         char *t;
188
189         assert(p);
190
191         /* First priority is whatever has been passed to us via env
192          * vars */
193         if ((e = getenv("SYSTEMD_UNIT_PATH")))
194                 if (!(p->unit_path = path_split_and_make_absolute(e)))
195                         return -ENOMEM;
196
197         if (strv_isempty(p->unit_path)) {
198
199                 /* Nothing is set, so let's figure something out. */
200                 strv_free(p->unit_path);
201
202                 if (running_as == MANAGER_USER) {
203
204                         if (personal)
205                                 p->unit_path = user_dirs();
206                         else
207                                 p->unit_path = strv_new(
208                                                 /* If you modify this you also want to modify
209                                                  * systemduserunitpath= in systemd.pc.in, and
210                                                  * the arrays in user_dirs() above! */
211                                                 USER_CONFIG_UNIT_PATH,
212                                                 "/etc/systemd/user",
213                                                 "/run/systemd/user",
214                                                 "/usr/local/lib/systemd/user",
215                                                 "/usr/local/share/systemd/user",
216                                                 USER_DATA_UNIT_PATH,
217                                                 "/usr/lib/systemd/user",
218                                                 "/usr/share/systemd/user",
219                                                 NULL);
220
221                         if (!p->unit_path)
222                                 return -ENOMEM;
223
224                 } else
225                         if (!(p->unit_path = strv_new(
226                                               /* If you modify this you also want to modify
227                                                * systemdsystemunitpath= in systemd.pc.in! */
228                                               SYSTEM_CONFIG_UNIT_PATH,
229                                               "/etc/systemd/system",
230                                               "/run/systemd/system",
231                                               "/usr/local/lib/systemd/system",
232                                               SYSTEM_DATA_UNIT_PATH,
233                                               "/usr/lib/systemd/system",
234 #ifdef HAVE_SPLIT_USR
235                                               "/lib/systemd/system",
236 #endif
237                                               NULL)))
238                                 return -ENOMEM;
239         }
240
241         if (p->unit_path)
242                 if (!path_strv_canonicalize(p->unit_path))
243                         return -ENOMEM;
244
245         strv_uniq(p->unit_path);
246         path_strv_remove_empty(p->unit_path);
247
248         if (!strv_isempty(p->unit_path)) {
249
250                 if (!(t = strv_join(p->unit_path, "\n\t")))
251                         return -ENOMEM;
252                 log_debug("Looking for unit files in:\n\t%s", t);
253                 free(t);
254         } else {
255                 log_debug("Ignoring unit files.");
256                 strv_free(p->unit_path);
257                 p->unit_path = NULL;
258         }
259
260         if (running_as == MANAGER_SYSTEM) {
261 #ifdef HAVE_SYSV_COMPAT
262                 /* /etc/init.d/ compatibility does not matter to users */
263
264                 if ((e = getenv("SYSTEMD_SYSVINIT_PATH")))
265                         if (!(p->sysvinit_path = path_split_and_make_absolute(e)))
266                                 return -ENOMEM;
267
268                 if (strv_isempty(p->sysvinit_path)) {
269                         strv_free(p->sysvinit_path);
270
271                         if (!(p->sysvinit_path = strv_new(
272                                               SYSTEM_SYSVINIT_PATH,     /* /etc/init.d/ */
273                                               NULL)))
274                                 return -ENOMEM;
275                 }
276
277                 if ((e = getenv("SYSTEMD_SYSVRCND_PATH")))
278                         if (!(p->sysvrcnd_path = path_split_and_make_absolute(e)))
279                                 return -ENOMEM;
280
281                 if (strv_isempty(p->sysvrcnd_path)) {
282                         strv_free(p->sysvrcnd_path);
283
284                         if (!(p->sysvrcnd_path = strv_new(
285                                               SYSTEM_SYSVRCND_PATH,     /* /etc/rcN.d/ */
286                                               NULL)))
287                                 return -ENOMEM;
288                 }
289
290                 if (p->sysvinit_path)
291                         if (!path_strv_canonicalize(p->sysvinit_path))
292                                 return -ENOMEM;
293
294                 if (p->sysvrcnd_path)
295                         if (!path_strv_canonicalize(p->sysvrcnd_path))
296                                 return -ENOMEM;
297
298                 strv_uniq(p->sysvinit_path);
299                 strv_uniq(p->sysvrcnd_path);
300
301                 path_strv_remove_empty(p->sysvinit_path);
302                 path_strv_remove_empty(p->sysvrcnd_path);
303
304                 if (!strv_isempty(p->sysvinit_path)) {
305
306                         if (!(t = strv_join(p->sysvinit_path, "\n\t")))
307                                 return -ENOMEM;
308
309                         log_debug("Looking for SysV init scripts in:\n\t%s", t);
310                         free(t);
311                 } else {
312                         log_debug("Ignoring SysV init scripts.");
313                         strv_free(p->sysvinit_path);
314                         p->sysvinit_path = NULL;
315                 }
316
317                 if (!strv_isempty(p->sysvrcnd_path)) {
318
319                         if (!(t = strv_join(p->sysvrcnd_path, "\n\t")))
320                                 return -ENOMEM;
321
322                         log_debug("Looking for SysV rcN.d links in:\n\t%s", t);
323                         free(t);
324                 } else {
325                         log_debug("Ignoring SysV rcN.d links.");
326                         strv_free(p->sysvrcnd_path);
327                         p->sysvrcnd_path = NULL;
328                 }
329 #else
330                 log_debug("Disabled SysV init scripts and rcN.d links support");
331 #endif
332         }
333
334         return 0;
335 }
336
337 void lookup_paths_free(LookupPaths *p) {
338         assert(p);
339
340         strv_free(p->unit_path);
341         p->unit_path = NULL;
342
343 #ifdef HAVE_SYSV_COMPAT
344         strv_free(p->sysvinit_path);
345         strv_free(p->sysvrcnd_path);
346         p->sysvinit_path = p->sysvrcnd_path = NULL;
347 #endif
348 }