chiark / gitweb /
logind: remove unused session->closing field
[elogind.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 #include <fnmatch.h>
29
30 #include <systemd/sd-id128.h>
31 #include "util.h"
32 #include "condition.h"
33 #include "virt.h"
34 #include "path-util.h"
35 #include "fileio.h"
36 #include "unit.h"
37 #include "smack-util.h"
38 #include "apparmor-util.h"
39 #include "ima-util.h"
40 #include "selinux-util.h"
41
42 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
43         Condition *c;
44
45         assert(type < _CONDITION_TYPE_MAX);
46
47         c = new0(Condition, 1);
48         if (!c)
49                 return NULL;
50
51         c->type = type;
52         c->trigger = trigger;
53         c->negate = negate;
54
55         if (parameter) {
56                 c->parameter = strdup(parameter);
57                 if (!c->parameter) {
58                         free(c);
59                         return NULL;
60                 }
61         }
62
63         return c;
64 }
65
66 void condition_free(Condition *c) {
67         assert(c);
68
69         free(c->parameter);
70         free(c);
71 }
72
73 void condition_free_list(Condition *first) {
74         Condition *c, *n;
75
76         LIST_FOREACH_SAFE(conditions, c, n, first)
77                 condition_free(c);
78 }
79
80 static bool test_kernel_command_line(const char *parameter) {
81         char *line, *w, *state, *word = NULL;
82         bool equal;
83         int r;
84         size_t l, pl;
85         bool found = false;
86
87         assert(parameter);
88
89         r = proc_cmdline(&line);
90         if (r < 0)
91                 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
92         if (r <= 0)
93                 return false;
94
95         equal = !!strchr(parameter, '=');
96         pl = strlen(parameter);
97
98         FOREACH_WORD_QUOTED(w, l, line, state) {
99
100                 free(word);
101                 word = strndup(w, l);
102                 if (!word)
103                         break;
104
105                 if (equal) {
106                         if (streq(word, parameter)) {
107                                 found = true;
108                                 break;
109                         }
110                 } else {
111                         if (startswith(word, parameter) && (word[pl] == '=' || word[pl] == 0)) {
112                                 found = true;
113                                 break;
114                         }
115                 }
116
117         }
118
119         free(word);
120         free(line);
121
122         return found;
123 }
124
125 static bool test_virtualization(const char *parameter) {
126         int b;
127         Virtualization v;
128         const char *id;
129
130         assert(parameter);
131
132         v = detect_virtualization(&id);
133         if (v < 0) {
134                 log_warning("Failed to detect virtualization, ignoring: %s", strerror(-v));
135                 return false;
136         }
137
138         /* First, compare with yes/no */
139         b = parse_boolean(parameter);
140
141         if (v > 0 && b > 0)
142                 return true;
143
144         if (v == 0 && b == 0)
145                 return true;
146
147         /* Then, compare categorization */
148         if (v == VIRTUALIZATION_VM && streq(parameter, "vm"))
149                 return true;
150
151         if (v == VIRTUALIZATION_CONTAINER && streq(parameter, "container"))
152                 return true;
153
154         /* Finally compare id */
155         return v > 0 && streq(parameter, id);
156 }
157
158 static bool test_security(const char *parameter) {
159         if (streq(parameter, "selinux"))
160                 return use_selinux();
161         if (streq(parameter, "apparmor"))
162                 return use_apparmor();
163         if (streq(parameter, "ima"))
164                 return use_ima();
165         if (streq(parameter, "smack"))
166                 return use_smack();
167         return false;
168 }
169
170 static bool test_capability(const char *parameter) {
171         cap_value_t value;
172         FILE *f;
173         char line[LINE_MAX];
174         unsigned long long capabilities = (unsigned long long) -1;
175
176         /* If it's an invalid capability, we don't have it */
177
178         if (cap_from_name(parameter, &value) < 0)
179                 return false;
180
181         /* If it's a valid capability we default to assume
182          * that we have it */
183
184         f = fopen("/proc/self/status", "re");
185         if (!f)
186                 return true;
187
188         while (fgets(line, sizeof(line), f)) {
189                 truncate_nl(line);
190
191                 if (startswith(line, "CapBnd:")) {
192                         (void) sscanf(line+7, "%llx", &capabilities);
193                         break;
194                 }
195         }
196
197         fclose(f);
198
199         return !!(capabilities & (1ULL << value));
200 }
201
202 static bool test_host(const char *parameter) {
203         sd_id128_t x, y;
204         char *h;
205         int r;
206         bool b;
207
208         if (sd_id128_from_string(parameter, &x) >= 0) {
209
210                 r = sd_id128_get_machine(&y);
211                 if (r < 0)
212                         return false;
213
214                 return sd_id128_equal(x, y);
215         }
216
217         h = gethostname_malloc();
218         if (!h)
219                 return false;
220
221         b = fnmatch(parameter, h, FNM_CASEFOLD) == 0;
222         free(h);
223
224         return b;
225 }
226
227 static bool test_ac_power(const char *parameter) {
228         int r;
229
230         r = parse_boolean(parameter);
231         if (r < 0)
232                 return true;
233
234         return (on_ac_power() != 0) == !!r;
235 }
236
237 static bool condition_test(Condition *c) {
238         assert(c);
239
240         switch(c->type) {
241
242         case CONDITION_PATH_EXISTS:
243                 return (access(c->parameter, F_OK) >= 0) == !c->negate;
244
245         case CONDITION_PATH_EXISTS_GLOB:
246                 return (glob_exists(c->parameter) > 0) == !c->negate;
247
248         case CONDITION_PATH_IS_DIRECTORY: {
249                 struct stat st;
250
251                 if (stat(c->parameter, &st) < 0)
252                         return c->negate;
253                 return S_ISDIR(st.st_mode) == !c->negate;
254         }
255
256         case CONDITION_PATH_IS_SYMBOLIC_LINK: {
257                 struct stat st;
258
259                 if (lstat(c->parameter, &st) < 0)
260                         return c->negate;
261                 return S_ISLNK(st.st_mode) == !c->negate;
262         }
263
264         case CONDITION_PATH_IS_MOUNT_POINT:
265                 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
266
267         case CONDITION_PATH_IS_READ_WRITE:
268                 return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
269
270         case CONDITION_DIRECTORY_NOT_EMPTY: {
271                 int k;
272
273                 k = dir_is_empty(c->parameter);
274                 return !(k == -ENOENT || k > 0) == !c->negate;
275         }
276
277         case CONDITION_FILE_NOT_EMPTY: {
278                 struct stat st;
279
280                 if (stat(c->parameter, &st) < 0)
281                         return c->negate;
282
283                 return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate;
284         }
285
286         case CONDITION_FILE_IS_EXECUTABLE: {
287                 struct stat st;
288
289                 if (stat(c->parameter, &st) < 0)
290                         return c->negate;
291
292                 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
293         }
294
295         case CONDITION_KERNEL_COMMAND_LINE:
296                 return test_kernel_command_line(c->parameter) == !c->negate;
297
298         case CONDITION_VIRTUALIZATION:
299                 return test_virtualization(c->parameter) == !c->negate;
300
301         case CONDITION_SECURITY:
302                 return test_security(c->parameter) == !c->negate;
303
304         case CONDITION_CAPABILITY:
305                 return test_capability(c->parameter) == !c->negate;
306
307         case CONDITION_HOST:
308                 return test_host(c->parameter) == !c->negate;
309
310         case CONDITION_AC_POWER:
311                 return test_ac_power(c->parameter) == !c->negate;
312
313         case CONDITION_NULL:
314                 return !c->negate;
315
316         default:
317                 assert_not_reached("Invalid condition type.");
318         }
319 }
320
321 bool condition_test_list(const char *unit, Condition *first) {
322         Condition *c;
323         int triggered = -1;
324
325         /* If the condition list is empty, then it is true */
326         if (!first)
327                 return true;
328
329         /* Otherwise, if all of the non-trigger conditions apply and
330          * if any of the trigger conditions apply (unless there are
331          * none) we return true */
332         LIST_FOREACH(conditions, c, first) {
333                 bool b;
334
335                 b = condition_test(c);
336                 if (unit)
337                         log_debug_unit(unit,
338                                        "%s=%s%s%s %s for %s.",
339                                        condition_type_to_string(c->type),
340                                        c->trigger ? "|" : "",
341                                        c->negate ? "!" : "",
342                                        c->parameter,
343                                        b ? "succeeded" : "failed",
344                                        unit);
345                 c->state = b ? 1 : -1;
346
347                 if (!c->trigger && !b)
348                         return false;
349
350                 if (c->trigger && triggered <= 0)
351                         triggered = b;
352         }
353
354         return triggered != 0;
355 }
356
357 void condition_dump(Condition *c, FILE *f, const char *prefix) {
358         assert(c);
359         assert(f);
360
361         if (!prefix)
362                 prefix = "";
363
364         fprintf(f,
365                 "%s\t%s: %s%s%s %s\n",
366                 prefix,
367                 condition_type_to_string(c->type),
368                 c->trigger ? "|" : "",
369                 c->negate ? "!" : "",
370                 c->parameter,
371                 c->state < 0 ? "failed" : c->state > 0 ? "succeeded" : "untested");
372 }
373
374 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
375         Condition *c;
376
377         LIST_FOREACH(conditions, c, first)
378                 condition_dump(c, f, prefix);
379 }
380
381 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
382         [CONDITION_PATH_EXISTS] = "ConditionPathExists",
383         [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
384         [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
385         [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
386         [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
387         [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
388         [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
389         [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
390         [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
391         [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
392         [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
393         [CONDITION_SECURITY] = "ConditionSecurity",
394         [CONDITION_CAPABILITY] = "ConditionCapability",
395         [CONDITION_HOST] = "ConditionHost",
396         [CONDITION_AC_POWER] = "ConditionACPower",
397         [CONDITION_NULL] = "ConditionNull"
398 };
399
400 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);