]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/condition.c
353e0c97f1d64c50bf15f24a1076af748e1ca508
[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 #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 }