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