1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
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.
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.
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/>.
26 #include <sys/capability.h>
27 #include <sys/statvfs.h>
32 #include "condition.h"
34 #include "path-util.h"
37 #include "smack-util.h"
38 #include "apparmor-util.h"
40 #include "selinux-util.h"
42 static bool condition_test_security(Condition *c) {
45 assert(c->type == CONDITION_SECURITY);
47 if (streq(c->parameter, "selinux"))
48 return mac_selinux_use() == !c->negate;
49 if (streq(c->parameter, "smack"))
50 return mac_smack_use() == !c->negate;
51 if (streq(c->parameter, "apparmor"))
52 return mac_apparmor_use() == !c->negate;
53 if (streq(c->parameter, "ima"))
54 return use_ima() == !c->negate;
59 static bool condition_test_capability(Condition *c) {
60 _cleanup_fclose_ FILE *f = NULL;
63 unsigned long long capabilities = -1;
67 assert(c->type == CONDITION_CAPABILITY);
69 /* If it's an invalid capability, we don't have it */
71 if (cap_from_name(c->parameter, &value) < 0)
74 /* If it's a valid capability we default to assume
77 f = fopen("/proc/self/status", "re");
81 while (fgets(line, sizeof(line), f)) {
84 if (startswith(line, "CapBnd:")) {
85 (void) sscanf(line+7, "%llx", &capabilities);
90 return !!(capabilities & (1ULL << value)) == !c->negate;
93 static bool condition_test_needs_update(Condition *c) {
95 struct stat usr, other;
99 assert(c->type == CONDITION_NEEDS_UPDATE);
101 /* If the file system is read-only we shouldn't suggest an update */
102 if (path_is_read_only_fs(c->parameter) > 0)
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
109 if (!path_is_absolute(c->parameter))
112 p = strappenda(c->parameter, "/.updated");
113 if (lstat(p, &other) < 0)
116 if (lstat("/usr/", &usr) < 0)
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;
123 static bool condition_test_first_boot(Condition *c) {
127 assert(c->parameter);
128 assert(c->type == CONDITION_FIRST_BOOT);
130 r = parse_boolean(c->parameter);
134 return ((access("/run/systemd/first-boot", F_OK) >= 0) == !!r) == !c->negate;
137 static bool condition_test(Condition *c) {
142 case CONDITION_PATH_EXISTS:
143 return (access(c->parameter, F_OK) >= 0) == !c->negate;
145 case CONDITION_PATH_EXISTS_GLOB:
146 return (glob_exists(c->parameter) > 0) == !c->negate;
148 case CONDITION_PATH_IS_DIRECTORY: {
151 if (stat(c->parameter, &st) < 0)
153 return S_ISDIR(st.st_mode) == !c->negate;
156 case CONDITION_PATH_IS_SYMBOLIC_LINK: {
159 if (lstat(c->parameter, &st) < 0)
161 return S_ISLNK(st.st_mode) == !c->negate;
164 case CONDITION_PATH_IS_MOUNT_POINT:
165 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
167 case CONDITION_PATH_IS_READ_WRITE:
168 return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
170 case CONDITION_DIRECTORY_NOT_EMPTY: {
173 k = dir_is_empty(c->parameter);
174 return !(k == -ENOENT || k > 0) == !c->negate;
177 case CONDITION_FILE_NOT_EMPTY: {
180 if (stat(c->parameter, &st) < 0)
183 return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate;
186 case CONDITION_FILE_IS_EXECUTABLE: {
189 if (stat(c->parameter, &st) < 0)
192 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
195 case CONDITION_KERNEL_COMMAND_LINE:
196 return condition_test_kernel_command_line(c);
198 case CONDITION_VIRTUALIZATION:
199 return condition_test_virtualization(c);
201 case CONDITION_SECURITY:
202 return condition_test_security(c);
204 case CONDITION_CAPABILITY:
205 return condition_test_capability(c);
208 return condition_test_host(c);
210 case CONDITION_AC_POWER:
211 return condition_test_ac_power(c);
213 case CONDITION_ARCHITECTURE:
214 return condition_test_architecture(c);
216 case CONDITION_NEEDS_UPDATE:
217 return condition_test_needs_update(c);
219 case CONDITION_FIRST_BOOT:
220 return condition_test_first_boot(c);
226 assert_not_reached("Invalid condition type.");
230 bool condition_test_list(const char *unit, Condition *first) {
234 /* If the condition list is empty, then it is true */
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) {
244 b = condition_test(c);
247 "%s=%s%s%s %s for %s.",
248 condition_type_to_string(c->type),
249 c->trigger ? "|" : "",
250 c->negate ? "!" : "",
252 b ? "succeeded" : "failed",
254 c->state = b ? 1 : -1;
256 if (!c->trigger && !b)
259 if (c->trigger && triggered <= 0)
263 return triggered != 0;