]>
Commit | Line | Data |
---|---|---|
52661efd LP |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
f23c09b0 | 6 | Copyright 2010 Lennart Poettering |
52661efd LP |
7 | |
8 | systemd is free software; you can redistribute it and/or modify it | |
5430f7f2 LP |
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 | |
52661efd LP |
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 | |
5430f7f2 | 16 | Lesser General Public License for more details. |
52661efd | 17 | |
5430f7f2 | 18 | You should have received a copy of the GNU Lesser General Public License |
52661efd LP |
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> | |
62590f23 | 26 | #include <sys/capability.h> |
d0516109 | 27 | #include <sys/statvfs.h> |
c0d6e764 | 28 | #include <fnmatch.h> |
52661efd | 29 | |
dc92e62c | 30 | #include "sd-id128.h" |
52661efd LP |
31 | #include "util.h" |
32 | #include "condition.h" | |
8095200d | 33 | #include "virt.h" |
9eb977db | 34 | #include "path-util.h" |
a5c32cff | 35 | #include "fileio.h" |
4b744dfa | 36 | #include "unit.h" |
d682b3a7 LP |
37 | #include "smack-util.h" |
38 | #include "apparmor-util.h" | |
39 | #include "ima-util.h" | |
40 | #include "selinux-util.h" | |
52661efd | 41 | |
afc50ea8 TG |
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; | |
dc92e62c | 55 | |
afc50ea8 | 56 | return c->negate; |
07e833bc MS |
57 | } |
58 | ||
afc50ea8 | 59 | static bool condition_test_capability(Condition *c) { |
dc92e62c | 60 | _cleanup_fclose_ FILE *f = NULL; |
62590f23 | 61 | cap_value_t value; |
62590f23 | 62 | char line[LINE_MAX]; |
de0671ee | 63 | unsigned long long capabilities = -1; |
62590f23 | 64 | |
afc50ea8 TG |
65 | assert(c); |
66 | assert(c->parameter); | |
67 | assert(c->type == CONDITION_CAPABILITY); | |
68 | ||
62590f23 LP |
69 | /* If it's an invalid capability, we don't have it */ |
70 | ||
afc50ea8 TG |
71 | if (cap_from_name(c->parameter, &value) < 0) |
72 | return c->negate; | |
62590f23 LP |
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) | |
afc50ea8 | 79 | return !c->negate; |
62590f23 LP |
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 | ||
afc50ea8 | 90 | return !!(capabilities & (1ULL << value)) == !c->negate; |
62590f23 LP |
91 | } |
92 | ||
a55654d5 LP |
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 | ||
e2680723 LP |
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 | ||
52990c2e | 137 | static bool condition_test(Condition *c) { |
52661efd LP |
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 | ||
8092a428 LP |
145 | case CONDITION_PATH_EXISTS_GLOB: |
146 | return (glob_exists(c->parameter) > 0) == !c->negate; | |
147 | ||
2b583ce6 KS |
148 | case CONDITION_PATH_IS_DIRECTORY: { |
149 | struct stat st; | |
150 | ||
8571962c | 151 | if (stat(c->parameter, &st) < 0) |
1f8fef5a | 152 | return c->negate; |
2b583ce6 KS |
153 | return S_ISDIR(st.st_mode) == !c->negate; |
154 | } | |
155 | ||
0d60602c MS |
156 | case CONDITION_PATH_IS_SYMBOLIC_LINK: { |
157 | struct stat st; | |
158 | ||
159 | if (lstat(c->parameter, &st) < 0) | |
1f8fef5a | 160 | return c->negate; |
0d60602c MS |
161 | return S_ISLNK(st.st_mode) == !c->negate; |
162 | } | |
163 | ||
ab7f148f LP |
164 | case CONDITION_PATH_IS_MOUNT_POINT: |
165 | return (path_is_mount_point(c->parameter, true) > 0) == !c->negate; | |
166 | ||
3d9a4122 LP |
167 | case CONDITION_PATH_IS_READ_WRITE: |
168 | return (path_is_read_only_fs(c->parameter) > 0) == c->negate; | |
d0516109 | 169 | |
36af55d9 LP |
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 | ||
742a862b LP |
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 | ||
82e487c5 LP |
186 | case CONDITION_FILE_IS_EXECUTABLE: { |
187 | struct stat st; | |
188 | ||
34a2dc4b | 189 | if (stat(c->parameter, &st) < 0) |
1f8fef5a | 190 | return c->negate; |
82e487c5 LP |
191 | |
192 | return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate; | |
193 | } | |
194 | ||
52661efd | 195 | case CONDITION_KERNEL_COMMAND_LINE: |
afc50ea8 | 196 | return condition_test_kernel_command_line(c); |
52661efd | 197 | |
039655a4 | 198 | case CONDITION_VIRTUALIZATION: |
afc50ea8 | 199 | return condition_test_virtualization(c); |
039655a4 | 200 | |
07e833bc | 201 | case CONDITION_SECURITY: |
afc50ea8 | 202 | return condition_test_security(c); |
07e833bc | 203 | |
62590f23 | 204 | case CONDITION_CAPABILITY: |
afc50ea8 | 205 | return condition_test_capability(c); |
62590f23 | 206 | |
c0d6e764 | 207 | case CONDITION_HOST: |
afc50ea8 | 208 | return condition_test_host(c); |
c0d6e764 | 209 | |
240dbaa4 | 210 | case CONDITION_AC_POWER: |
afc50ea8 | 211 | return condition_test_ac_power(c); |
240dbaa4 | 212 | |
099524d7 LP |
213 | case CONDITION_ARCHITECTURE: |
214 | return condition_test_architecture(c); | |
215 | ||
a55654d5 LP |
216 | case CONDITION_NEEDS_UPDATE: |
217 | return condition_test_needs_update(c); | |
218 | ||
e2680723 LP |
219 | case CONDITION_FIRST_BOOT: |
220 | return condition_test_first_boot(c); | |
221 | ||
d257ddef LP |
222 | case CONDITION_NULL: |
223 | return !c->negate; | |
224 | ||
52661efd LP |
225 | default: |
226 | assert_not_reached("Invalid condition type."); | |
227 | } | |
228 | } | |
229 | ||
4b744dfa | 230 | bool condition_test_list(const char *unit, Condition *first) { |
52661efd | 231 | Condition *c; |
267632f0 | 232 | int triggered = -1; |
52661efd LP |
233 | |
234 | /* If the condition list is empty, then it is true */ | |
235 | if (!first) | |
236 | return true; | |
237 | ||
267632f0 LP |
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); | |
4b744dfa ZJS |
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); | |
52990c2e | 254 | c->state = b ? 1 : -1; |
267632f0 LP |
255 | |
256 | if (!c->trigger && !b) | |
257 | return false; | |
258 | ||
259 | if (c->trigger && triggered <= 0) | |
260 | triggered = b; | |
261 | } | |
52661efd | 262 | |
267632f0 | 263 | return triggered != 0; |
52661efd | 264 | } |