chiark / gitweb /
systemctl,verbs: Introduce SYSTEMD_OFFLINE environment variable
[elogind.git] / src / basic / verbs.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   This file is part of systemd.
4
5   Copyright 2014 Lennart Poettering
6
7   systemd is free software; you can redistribute it and/or modify it
8   under the terms of the GNU Lesser General Public License as published by
9   the Free Software Foundation; either version 2.1 of the License, or
10   (at your option) any later version.
11
12   systemd is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   Lesser General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22 #include <getopt.h>
23 #include <stdbool.h>
24 #include <stddef.h>
25
26 //#include "env-util.h"
27 #include "log.h"
28 #include "macro.h"
29 #include "string-util.h"
30 #include "verbs.h"
31 #include "virt.h"
32
33 /* Wraps running_in_chroot() which is used in various places,
34  * but also adds an environment variable check so external processes
35  * can reliably force this on.
36  */
37 bool running_in_chroot_or_offline(void) {
38         int r;
39
40         /* Added to support use cases like rpm-ostree, where from %post
41          * scripts we only want to execute "preset", but not "start"/"restart"
42          * for example.
43          *
44          * See ENVIRONMENT.md for docs.
45          */
46         r = getenv_bool("SYSTEMD_OFFLINE");
47         if (r < 0)
48                 log_debug_errno(r, "Parsing SYSTEMD_OFFLINE: %m");
49         else if (r == 0)
50                 return false;
51         else
52                 return true;
53
54         /* We've had this condition check for a long time which basically
55          * checks for legacy chroot case like Fedora's
56          * "mock", which is used for package builds.  We don't want
57          * to try to start systemd services there, since without --new-chroot
58          * we don't even have systemd running, and even if we did, adding
59          * a concept of background daemons to builds would be an enormous change,
60          * requiring considering things like how the journal output is handled, etc.
61          * And there's really not a use case today for a build talking to a service.
62          *
63          * Note this call itself also looks for a different variable SYSTEMD_IGNORE_CHROOT=1.
64          */
65         r = running_in_chroot();
66         if (r < 0)
67                 log_debug_errno(r, "running_in_chroot(): %m");
68         else if (r > 0)
69                 return true;
70
71         return false;
72 }
73
74 int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
75         const Verb *verb;
76         const char *name;
77         unsigned i;
78         int left, r;
79
80         assert(verbs);
81         assert(verbs[0].dispatch);
82         assert(argc >= 0);
83         assert(argv);
84         assert(argc >= optind);
85
86         left = argc - optind;
87         name = argv[optind];
88
89         for (i = 0;; i++) {
90                 bool found;
91
92                 /* At the end of the list? */
93                 if (!verbs[i].dispatch) {
94                         if (name)
95                                 log_error("Unknown operation %s.", name);
96                         else
97                                 log_error("Requires operation parameter.");
98                         return -EINVAL;
99                 }
100
101                 if (name)
102                         found = streq(name, verbs[i].verb);
103                 else
104                         found = !!(verbs[i].flags & VERB_DEFAULT);
105
106                 if (found) {
107                         verb = &verbs[i];
108                         break;
109                 }
110         }
111
112         assert(verb);
113
114         if (!name)
115                 left = 1;
116
117         if (verb->min_args != VERB_ANY &&
118             (unsigned) left < verb->min_args) {
119                 log_error("Too few arguments.");
120                 return -EINVAL;
121         }
122
123         if (verb->max_args != VERB_ANY &&
124             (unsigned) left > verb->max_args) {
125                 log_error("Too many arguments.");
126                 return -EINVAL;
127         }
128
129         if ((verb->flags & VERB_NOCHROOT) && running_in_chroot_or_offline()) {
130                 if (name)
131                         log_info("Running in chroot, ignoring request: %s", name);
132                 else
133                         log_info("Running in chroot, ignoring request.");
134                 return 0;
135         }
136
137         if (verb->flags & VERB_MUSTBEROOT) {
138                 r = must_be_root();
139                 if (r < 0)
140                         return r;
141         }
142
143         if (name)
144                 return verb->dispatch(left, argv + optind, userdata);
145         else {
146                 char* fake[2] = {
147                         (char*) verb->verb,
148                         NULL
149                 };
150
151                 return verb->dispatch(1, fake, userdata);
152         }
153 }