chiark / gitweb /
353e0c97f1d64c50bf15f24a1076af748e1ca508
[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 "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 static bool condition_test_security(Condition *c) {
43         assert(c);
44         assert(c->parameter);
45         assert(c->type == CONDITION_SECURITY);
46
47         if (streq(c->parameter, "selinux"))
48                 return use_selinux() == !c->negate;
49         if (streq(c->parameter, "apparmor"))
50                 return use_apparmor() == !c->negate;
51         if (streq(c->parameter, "ima"))
52                 return use_ima() == !c->negate;
53         if (streq(c->parameter, "smack"))
54                 return use_smack() == !c->negate;
55
56         return c->negate;
57 }
58
59 static bool condition_test_capability(Condition *c) {
60         _cleanup_fclose_ FILE *f = NULL;
61         cap_value_t value;
62         char line[LINE_MAX];
63         unsigned long long capabilities = -1;
64
65         assert(c);
66         assert(c->parameter);
67         assert(c->type == CONDITION_CAPABILITY);
68
69         /* If it's an invalid capability, we don't have it */
70
71         if (cap_from_name(c->parameter, &value) < 0)
72                 return c->negate;
73
74         /* If it's a valid capability we default to assume
75          * that we have it */
76
77         f = fopen("/proc/self/status", "re");
78         if (!f)
79                 return !c->negate;
80
81         while (fgets(line, sizeof(line), f)) {
82                 truncate_nl(line);
83
84                 if (startswith(line, "CapBnd:")) {
85                         (void) sscanf(line+7, "%llx", &capabilities);
86                         break;
87                 }
88         }
89
90         return !!(capabilities & (1ULL << value)) == !c->negate;
91 }
92
93 static bool condition_test_needs_update(Condition *c) {
94         const char *p;
95         struct stat usr, other;
96
97         assert(c);
98         assert(c->parameter);
99         assert(c->type == CONDITION_NEEDS_UPDATE);
100
101         /* If the file system is read-only we shouldn't suggest an update */
102         if (path_is_read_only_fs(c->parameter) > 0)
103                 return c->negate;
104
105         /* Any other failure means we should allow the condition to be true,
106          * so that we rather invoke too many update tools then too
107          * few. */
108
109         if (!path_is_absolute(c->parameter))
110                 return !c->negate;
111
112         p = strappenda(c->parameter, "/.updated");
113         if (lstat(p, &other) < 0)
114                 return !c->negate;
115
116         if (lstat("/usr/", &usr) < 0)
117                 return !c->negate;
118
119         return (usr.st_mtim.tv_sec > other.st_mtim.tv_sec ||
120                 (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec)) == !c->negate;
121 }
122
123 static bool condition_test_first_boot(Condition *c) {
124         int r;
125
126         assert(c);
127         assert(c->parameter);
128         assert(c->type == CONDITION_FIRST_BOOT);
129
130         r = parse_boolean(c->parameter);
131         if (r < 0)
132                 return c->negate;
133
134         return ((access("/run/systemd/first-boot", F_OK) >= 0) == !!r) == !c->negate;
135 }
136
137 static bool condition_test(Condition *c) {
138         assert(c);
139
140         switch(c->type) {
141
142         case CONDITION_PATH_EXISTS:
143                 return (access(c->parameter, F_OK) >= 0) == !c->negate;
144
145         case CONDITION_PATH_EXISTS_GLOB:
146                 return (glob_exists(c->parameter) > 0) == !c->negate;
147
148         case CONDITION_PATH_IS_DIRECTORY: {
149                 struct stat st;
150
151                 if (stat(c->parameter, &st) < 0)
152                         return c->negate;
153                 return S_ISDIR(st.st_mode) == !c->negate;
154         }
155
156         case CONDITION_PATH_IS_SYMBOLIC_LINK: {
157                 struct stat st;
158
159                 if (lstat(c->parameter, &st) < 0)
160                         return c->negate;
161                 return S_ISLNK(st.st_mode) == !c->negate;
162         }
163
164         case CONDITION_PATH_IS_MOUNT_POINT:
165                 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
166
167         case CONDITION_PATH_IS_READ_WRITE:
168                 return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
169
170         case CONDITION_DIRECTORY_NOT_EMPTY: {
171                 int k;
172
173                 k = dir_is_empty(c->parameter);
174                 return !(k == -ENOENT || k > 0) == !c->negate;
175         }
176
177         case CONDITION_FILE_NOT_EMPTY: {
178                 struct stat st;
179
180                 if (stat(c->parameter, &st) < 0)
181                         return c->negate;
182
183                 return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate;
184         }
185
186         case CONDITION_FILE_IS_EXECUTABLE: {
187                 struct stat st;
188
189                 if (stat(c->parameter, &st) < 0)
190                         return c->negate;
191
192                 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
193         }
194
195         case CONDITION_KERNEL_COMMAND_LINE:
196                 return condition_test_kernel_command_line(c);
197
198         case CONDITION_VIRTUALIZATION:
199                 return condition_test_virtualization(c);
200
201         case CONDITION_SECURITY:
202                 return condition_test_security(c);
203
204         case CONDITION_CAPABILITY:
205                 return condition_test_capability(c);
206
207         case CONDITION_HOST:
208                 return condition_test_host(c);
209
210         case CONDITION_AC_POWER:
211                 return condition_test_ac_power(c);
212
213         case CONDITION_ARCHITECTURE:
214                 return condition_test_architecture(c);
215
216         case CONDITION_NEEDS_UPDATE:
217                 return condition_test_needs_update(c);
218
219         case CONDITION_FIRST_BOOT:
220                 return condition_test_first_boot(c);
221
222         case CONDITION_NULL:
223                 return !c->negate;
224
225         default:
226                 assert_not_reached("Invalid condition type.");
227         }
228 }
229
230 bool condition_test_list(const char *unit, Condition *first) {
231         Condition *c;
232         int triggered = -1;
233
234         /* If the condition list is empty, then it is true */
235         if (!first)
236                 return true;
237
238         /* Otherwise, if all of the non-trigger conditions apply and
239          * if any of the trigger conditions apply (unless there are
240          * none) we return true */
241         LIST_FOREACH(conditions, c, first) {
242                 bool b;
243
244                 b = condition_test(c);
245                 if (unit)
246                         log_debug_unit(unit,
247                                        "%s=%s%s%s %s for %s.",
248                                        condition_type_to_string(c->type),
249                                        c->trigger ? "|" : "",
250                                        c->negate ? "!" : "",
251                                        c->parameter,
252                                        b ? "succeeded" : "failed",
253                                        unit);
254                 c->state = b ? 1 : -1;
255
256                 if (!c->trigger && !b)
257                         return false;
258
259                 if (c->trigger && triggered <= 0)
260                         triggered = b;
261         }
262
263         return triggered != 0;
264 }