]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/condition.c
util: fix tty_is_vc_resolve() in a container where /sys/class/tty/console/active...
[thirdparty/systemd.git] / src / core / condition.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 <stdlib.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <sys/capability.h>
27 #include <sys/statvfs.h>
28
29 #ifdef HAVE_SELINUX
30 #include <selinux/selinux.h>
31 #endif
32
33 #include "util.h"
34 #include "condition.h"
35 #include "virt.h"
36
37 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
38 Condition *c;
39
40 assert(type < _CONDITION_TYPE_MAX);
41
42 c = new0(Condition, 1);
43 if (!c)
44 return NULL;
45
46 c->type = type;
47 c->trigger = trigger;
48 c->negate = negate;
49
50 if (parameter) {
51 c->parameter = strdup(parameter);
52 if (!c->parameter) {
53 free(c);
54 return NULL;
55 }
56 }
57
58 return c;
59 }
60
61 void condition_free(Condition *c) {
62 assert(c);
63
64 free(c->parameter);
65 free(c);
66 }
67
68 void condition_free_list(Condition *first) {
69 Condition *c, *n;
70
71 LIST_FOREACH_SAFE(conditions, c, n, first)
72 condition_free(c);
73 }
74
75 static bool test_kernel_command_line(const char *parameter) {
76 char *line, *w, *state, *word = NULL;
77 bool equal;
78 int r;
79 size_t l, pl;
80 bool found = false;
81
82 assert(parameter);
83
84 if (detect_container(NULL) > 0)
85 return false;
86
87 r = read_one_line_file("/proc/cmdline", &line);
88 if (r < 0) {
89 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
90 return false;
91 }
92
93 equal = !!strchr(parameter, '=');
94 pl = strlen(parameter);
95
96 FOREACH_WORD_QUOTED(w, l, line, state) {
97
98 free(word);
99 word = strndup(w, l);
100 if (!word)
101 break;
102
103 if (equal) {
104 if (streq(word, parameter)) {
105 found = true;
106 break;
107 }
108 } else {
109 if (startswith(word, parameter) && (word[pl] == '=' || word[pl] == 0)) {
110 found = true;
111 break;
112 }
113 }
114
115 }
116
117 free(word);
118 free(line);
119
120 return found;
121 }
122
123 static bool test_virtualization(const char *parameter) {
124 int b;
125 Virtualization v;
126 const char *id;
127
128 assert(parameter);
129
130 v = detect_virtualization(&id);
131 if (v < 0) {
132 log_warning("Failed to detect virtualization, ignoring: %s", strerror(-v));
133 return false;
134 }
135
136 /* First, compare with yes/no */
137 b = parse_boolean(parameter);
138
139 if (v > 0 && b > 0)
140 return true;
141
142 if (v == 0 && b == 0)
143 return true;
144
145 /* Then, compare categorization */
146 if (v == VIRTUALIZATION_VM && streq(parameter, "vm"))
147 return true;
148
149 if (v == VIRTUALIZATION_CONTAINER && streq(parameter, "container"))
150 return true;
151
152 /* Finally compare id */
153 return v > 0 && streq(parameter, id);
154 }
155
156 static bool test_security(const char *parameter) {
157 #ifdef HAVE_SELINUX
158 if (streq(parameter, "selinux"))
159 return is_selinux_enabled() > 0;
160 #endif
161 return false;
162 }
163
164 static bool test_capability(const char *parameter) {
165 cap_value_t value;
166 FILE *f;
167 char line[LINE_MAX];
168 unsigned long long capabilities = (unsigned long long) -1;
169
170 /* If it's an invalid capability, we don't have it */
171
172 if (cap_from_name(parameter, &value) < 0)
173 return false;
174
175 /* If it's a valid capability we default to assume
176 * that we have it */
177
178 f = fopen("/proc/self/status", "re");
179 if (!f)
180 return true;
181
182 while (fgets(line, sizeof(line), f)) {
183 truncate_nl(line);
184
185 if (startswith(line, "CapBnd:")) {
186 (void) sscanf(line+7, "%llx", &capabilities);
187 break;
188 }
189 }
190
191 fclose(f);
192
193 return !!(capabilities & (1ULL << value));
194 }
195
196 bool condition_test(Condition *c) {
197 assert(c);
198
199 switch(c->type) {
200
201 case CONDITION_PATH_EXISTS:
202 return (access(c->parameter, F_OK) >= 0) == !c->negate;
203
204 case CONDITION_PATH_EXISTS_GLOB:
205 return (glob_exists(c->parameter) > 0) == !c->negate;
206
207 case CONDITION_PATH_IS_DIRECTORY: {
208 struct stat st;
209
210 if (stat(c->parameter, &st) < 0)
211 return c->negate;
212 return S_ISDIR(st.st_mode) == !c->negate;
213 }
214
215 case CONDITION_PATH_IS_SYMBOLIC_LINK: {
216 struct stat st;
217
218 if (lstat(c->parameter, &st) < 0)
219 return c->negate;
220 return S_ISLNK(st.st_mode) == !c->negate;
221 }
222
223 case CONDITION_PATH_IS_MOUNT_POINT:
224 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
225
226 case CONDITION_PATH_IS_READ_WRITE:
227 return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
228
229 case CONDITION_DIRECTORY_NOT_EMPTY: {
230 int k;
231
232 k = dir_is_empty(c->parameter);
233 return !(k == -ENOENT || k > 0) == !c->negate;
234 }
235
236 case CONDITION_FILE_IS_EXECUTABLE: {
237 struct stat st;
238
239 if (stat(c->parameter, &st) < 0)
240 return c->negate;
241
242 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
243 }
244
245 case CONDITION_KERNEL_COMMAND_LINE:
246 return test_kernel_command_line(c->parameter) == !c->negate;
247
248 case CONDITION_VIRTUALIZATION:
249 return test_virtualization(c->parameter) == !c->negate;
250
251 case CONDITION_SECURITY:
252 return test_security(c->parameter) == !c->negate;
253
254 case CONDITION_CAPABILITY:
255 return test_capability(c->parameter) == !c->negate;
256
257 case CONDITION_NULL:
258 return !c->negate;
259
260 default:
261 assert_not_reached("Invalid condition type.");
262 }
263 }
264
265 bool condition_test_list(Condition *first) {
266 Condition *c;
267 int triggered = -1;
268
269 /* If the condition list is empty, then it is true */
270 if (!first)
271 return true;
272
273 /* Otherwise, if all of the non-trigger conditions apply and
274 * if any of the trigger conditions apply (unless there are
275 * none) we return true */
276 LIST_FOREACH(conditions, c, first) {
277 bool b;
278
279 b = condition_test(c);
280
281 if (!c->trigger && !b)
282 return false;
283
284 if (c->trigger && triggered <= 0)
285 triggered = b;
286 }
287
288 return triggered != 0;
289 }
290
291 void condition_dump(Condition *c, FILE *f, const char *prefix) {
292 assert(c);
293 assert(f);
294
295 if (!prefix)
296 prefix = "";
297
298 fprintf(f,
299 "%s\t%s: %s%s%s\n",
300 prefix,
301 condition_type_to_string(c->type),
302 c->trigger ? "|" : "",
303 c->negate ? "!" : "",
304 c->parameter);
305 }
306
307 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
308 Condition *c;
309
310 LIST_FOREACH(conditions, c, first)
311 condition_dump(c, f, prefix);
312 }
313
314 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
315 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
316 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
317 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
318 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
319 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
320 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
321 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
322 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
323 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
324 [CONDITION_SECURITY] = "ConditionSecurity",
325 [CONDITION_NULL] = "ConditionNull"
326 };
327
328 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);