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