chiark / gitweb /
410fb36797808757f39bcf71fca52af994a6629f
[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(Condition *c) {
124         assert(c);
125
126         switch(c->type) {
127
128         case CONDITION_PATH_EXISTS:
129                 return (access(c->parameter, F_OK) >= 0) == !c->negate;
130
131         case CONDITION_PATH_EXISTS_GLOB:
132                 return (glob_exists(c->parameter) > 0) == !c->negate;
133
134         case CONDITION_PATH_IS_DIRECTORY: {
135                 struct stat st;
136
137                 if (stat(c->parameter, &st) < 0)
138                         return c->negate;
139                 return S_ISDIR(st.st_mode) == !c->negate;
140         }
141
142         case CONDITION_PATH_IS_SYMBOLIC_LINK: {
143                 struct stat st;
144
145                 if (lstat(c->parameter, &st) < 0)
146                         return c->negate;
147                 return S_ISLNK(st.st_mode) == !c->negate;
148         }
149
150         case CONDITION_PATH_IS_MOUNT_POINT:
151                 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
152
153         case CONDITION_PATH_IS_READ_WRITE:
154                 return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
155
156         case CONDITION_DIRECTORY_NOT_EMPTY: {
157                 int k;
158
159                 k = dir_is_empty(c->parameter);
160                 return !(k == -ENOENT || k > 0) == !c->negate;
161         }
162
163         case CONDITION_FILE_NOT_EMPTY: {
164                 struct stat st;
165
166                 if (stat(c->parameter, &st) < 0)
167                         return c->negate;
168
169                 return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate;
170         }
171
172         case CONDITION_FILE_IS_EXECUTABLE: {
173                 struct stat st;
174
175                 if (stat(c->parameter, &st) < 0)
176                         return c->negate;
177
178                 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
179         }
180
181         case CONDITION_KERNEL_COMMAND_LINE:
182                 return condition_test_kernel_command_line(c);
183
184         case CONDITION_VIRTUALIZATION:
185                 return condition_test_virtualization(c);
186
187         case CONDITION_SECURITY:
188                 return condition_test_security(c);
189
190         case CONDITION_CAPABILITY:
191                 return condition_test_capability(c);
192
193         case CONDITION_HOST:
194                 return condition_test_host(c);
195
196         case CONDITION_AC_POWER:
197                 return condition_test_ac_power(c);
198
199         case CONDITION_ARCHITECTURE:
200                 return condition_test_architecture(c);
201
202         case CONDITION_NEEDS_UPDATE:
203                 return condition_test_needs_update(c);
204
205         case CONDITION_NULL:
206                 return !c->negate;
207
208         default:
209                 assert_not_reached("Invalid condition type.");
210         }
211 }
212
213 bool condition_test_list(const char *unit, Condition *first) {
214         Condition *c;
215         int triggered = -1;
216
217         /* If the condition list is empty, then it is true */
218         if (!first)
219                 return true;
220
221         /* Otherwise, if all of the non-trigger conditions apply and
222          * if any of the trigger conditions apply (unless there are
223          * none) we return true */
224         LIST_FOREACH(conditions, c, first) {
225                 bool b;
226
227                 b = condition_test(c);
228                 if (unit)
229                         log_debug_unit(unit,
230                                        "%s=%s%s%s %s for %s.",
231                                        condition_type_to_string(c->type),
232                                        c->trigger ? "|" : "",
233                                        c->negate ? "!" : "",
234                                        c->parameter,
235                                        b ? "succeeded" : "failed",
236                                        unit);
237                 c->state = b ? 1 : -1;
238
239                 if (!c->trigger && !b)
240                         return false;
241
242                 if (c->trigger && triggered <= 0)
243                         triggered = b;
244         }
245
246         return triggered != 0;
247 }