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