]>
Commit | Line | Data |
---|---|---|
4ad49000 LP |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright 2013 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 | ||
718db961 | 22 | #include "bus-util.h" |
7041efe9 | 23 | #include "path-util.h" |
718db961 LP |
24 | #include "cgroup-util.h" |
25 | #include "cgroup.h" | |
4ad49000 LP |
26 | #include "dbus-cgroup.h" |
27 | ||
718db961 LP |
28 | static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_cgroup_device_policy, cgroup_device_policy, CGroupDevicePolicy); |
29 | ||
30 | static int property_get_blockio_device_weight( | |
31 | sd_bus *bus, | |
32 | const char *path, | |
33 | const char *interface, | |
34 | const char *property, | |
35 | sd_bus_message *reply, | |
ebcf1f97 LP |
36 | void *userdata, |
37 | sd_bus_error *error) { | |
4ad49000 | 38 | |
718db961 | 39 | CGroupContext *c = userdata; |
4ad49000 | 40 | CGroupBlockIODeviceWeight *w; |
718db961 | 41 | int r; |
4ad49000 | 42 | |
718db961 LP |
43 | assert(bus); |
44 | assert(reply); | |
4ad49000 LP |
45 | assert(c); |
46 | ||
718db961 LP |
47 | r = sd_bus_message_open_container(reply, 'a', "(st)"); |
48 | if (r < 0) | |
49 | return r; | |
4ad49000 LP |
50 | |
51 | LIST_FOREACH(device_weights, w, c->blockio_device_weights) { | |
718db961 LP |
52 | r = sd_bus_message_append(reply, "(st)", w->path, w->weight); |
53 | if (r < 0) | |
54 | return r; | |
4ad49000 LP |
55 | } |
56 | ||
718db961 | 57 | return sd_bus_message_close_container(reply); |
4ad49000 LP |
58 | } |
59 | ||
718db961 LP |
60 | static int property_get_blockio_device_bandwidths( |
61 | sd_bus *bus, | |
62 | const char *path, | |
63 | const char *interface, | |
64 | const char *property, | |
65 | sd_bus_message *reply, | |
ebcf1f97 LP |
66 | void *userdata, |
67 | sd_bus_error *error) { | |
718db961 LP |
68 | |
69 | CGroupContext *c = userdata; | |
4ad49000 | 70 | CGroupBlockIODeviceBandwidth *b; |
718db961 | 71 | int r; |
4ad49000 | 72 | |
718db961 LP |
73 | assert(bus); |
74 | assert(reply); | |
4ad49000 LP |
75 | assert(c); |
76 | ||
718db961 LP |
77 | r = sd_bus_message_open_container(reply, 'a', "(st)"); |
78 | if (r < 0) | |
79 | return r; | |
4ad49000 LP |
80 | |
81 | LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) { | |
82 | ||
83 | if (streq(property, "BlockIOReadBandwidth") != b->read) | |
84 | continue; | |
85 | ||
718db961 LP |
86 | r = sd_bus_message_append(reply, "(st)", b->path, b->bandwidth); |
87 | if (r < 0) | |
88 | return r; | |
4ad49000 LP |
89 | } |
90 | ||
718db961 | 91 | return sd_bus_message_close_container(reply); |
4ad49000 LP |
92 | } |
93 | ||
718db961 LP |
94 | static int property_get_device_allow( |
95 | sd_bus *bus, | |
96 | const char *path, | |
97 | const char *interface, | |
98 | const char *property, | |
99 | sd_bus_message *reply, | |
ebcf1f97 LP |
100 | void *userdata, |
101 | sd_bus_error *error) { | |
718db961 LP |
102 | |
103 | CGroupContext *c = userdata; | |
4ad49000 | 104 | CGroupDeviceAllow *a; |
718db961 | 105 | int r; |
4ad49000 | 106 | |
718db961 LP |
107 | assert(bus); |
108 | assert(reply); | |
4ad49000 LP |
109 | assert(c); |
110 | ||
718db961 LP |
111 | r = sd_bus_message_open_container(reply, 'a', "(ss)"); |
112 | if (r < 0) | |
113 | return r; | |
4ad49000 LP |
114 | |
115 | LIST_FOREACH(device_allow, a, c->device_allow) { | |
4ad49000 | 116 | unsigned k = 0; |
718db961 | 117 | char rwm[4]; |
4ad49000 LP |
118 | |
119 | if (a->r) | |
718db961 | 120 | rwm[k++] = 'r'; |
4ad49000 | 121 | if (a->w) |
718db961 | 122 | rwm[k++] = 'w'; |
4ad49000 | 123 | if (a->m) |
718db961 | 124 | rwm[k++] = 'm'; |
4ad49000 | 125 | |
718db961 | 126 | rwm[k] = 0; |
4ad49000 | 127 | |
718db961 LP |
128 | r = sd_bus_message_append(reply, "(ss)", a->path, rwm); |
129 | if (r < 0) | |
130 | return r; | |
4ad49000 LP |
131 | } |
132 | ||
718db961 | 133 | return sd_bus_message_close_container(reply); |
4ad49000 LP |
134 | } |
135 | ||
718db961 LP |
136 | const sd_bus_vtable bus_cgroup_vtable[] = { |
137 | SD_BUS_VTABLE_START(0), | |
610f780c LP |
138 | SD_BUS_PROPERTY("CPUAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, cpu_accounting), 0), |
139 | SD_BUS_PROPERTY("CPUShares", "t", bus_property_get_ulong, offsetof(CGroupContext, cpu_shares), 0), | |
140 | SD_BUS_PROPERTY("BlockIOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, blockio_accounting), 0), | |
141 | SD_BUS_PROPERTY("BlockIOWeight", "t", bus_property_get_ulong, offsetof(CGroupContext, blockio_weight), 0), | |
142 | SD_BUS_PROPERTY("BlockIODeviceWeight", "a(st)", property_get_blockio_device_weight, 0, 0), | |
143 | SD_BUS_PROPERTY("BlockIOReadBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0), | |
144 | SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0), | |
145 | SD_BUS_PROPERTY("MemoryAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, memory_accounting), 0), | |
146 | SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0), | |
147 | SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0), | |
148 | SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0), | |
718db961 | 149 | SD_BUS_VTABLE_END |
4ad49000 | 150 | }; |
8e2af478 LP |
151 | |
152 | int bus_cgroup_set_property( | |
153 | Unit *u, | |
154 | CGroupContext *c, | |
155 | const char *name, | |
718db961 | 156 | sd_bus_message *message, |
8e2af478 | 157 | UnitSetPropertiesMode mode, |
718db961 LP |
158 | sd_bus_error *error) { |
159 | ||
160 | int r; | |
8e2af478 | 161 | |
8e2af478 LP |
162 | assert(u); |
163 | assert(c); | |
718db961 LP |
164 | assert(name); |
165 | assert(message); | |
8e2af478 LP |
166 | |
167 | if (streq(name, "CPUAccounting")) { | |
718db961 | 168 | int b; |
8e2af478 | 169 | |
718db961 LP |
170 | r = sd_bus_message_read(message, "b", &b); |
171 | if (r < 0) | |
172 | return r; | |
8e2af478 LP |
173 | |
174 | if (mode != UNIT_CHECK) { | |
8e2af478 | 175 | c->cpu_accounting = b; |
b9ec9359 | 176 | unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no"); |
b42defe3 LP |
177 | } |
178 | ||
179 | return 1; | |
180 | ||
181 | } else if (streq(name, "CPUShares")) { | |
182 | uint64_t u64; | |
183 | unsigned long ul; | |
184 | ||
718db961 LP |
185 | r = sd_bus_message_read(message, "t", &u64); |
186 | if (r < 0) | |
187 | return r; | |
b42defe3 | 188 | |
b42defe3 | 189 | ul = (unsigned long) u64; |
718db961 LP |
190 | if (ul <= 0 || (uint64_t) ul != u64) |
191 | return sd_bus_error_set_errnof(error, EINVAL, "CPUShares value out of range"); | |
b42defe3 LP |
192 | |
193 | if (mode != UNIT_CHECK) { | |
b42defe3 | 194 | c->cpu_shares = ul; |
b9ec9359 | 195 | unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul); |
8e2af478 LP |
196 | } |
197 | ||
198 | return 1; | |
199 | ||
200 | } else if (streq(name, "BlockIOAccounting")) { | |
718db961 | 201 | int b; |
8e2af478 | 202 | |
718db961 LP |
203 | r = sd_bus_message_read(message, "b", &b); |
204 | if (r < 0) | |
205 | return r; | |
8e2af478 LP |
206 | |
207 | if (mode != UNIT_CHECK) { | |
8e2af478 | 208 | c->blockio_accounting = b; |
b9ec9359 | 209 | unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no"); |
8e2af478 LP |
210 | } |
211 | ||
212 | return 1; | |
b42defe3 LP |
213 | |
214 | } else if (streq(name, "BlockIOWeight")) { | |
215 | uint64_t u64; | |
216 | unsigned long ul; | |
217 | ||
718db961 LP |
218 | r = sd_bus_message_read(message, "t", &u64); |
219 | if (r < 0) | |
220 | return r; | |
b42defe3 | 221 | |
b42defe3 | 222 | ul = (unsigned long) u64; |
718db961 LP |
223 | if (ul < 10 || ul > 1000) |
224 | return sd_bus_error_set_errnof(error, EINVAL, "BlockIOWeight value out of range"); | |
b42defe3 LP |
225 | |
226 | if (mode != UNIT_CHECK) { | |
f2369103 | 227 | c->blockio_weight = ul; |
b9ec9359 | 228 | unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul); |
b42defe3 LP |
229 | } |
230 | ||
231 | return 1; | |
232 | ||
f004c2ca | 233 | } else if (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth")) { |
718db961 | 234 | const char *path; |
f004c2ca | 235 | bool read = true; |
718db961 LP |
236 | unsigned n = 0; |
237 | uint64_t u64; | |
f004c2ca G |
238 | |
239 | if (streq(name, "BlockIOWriteBandwidth")) | |
240 | read = false; | |
241 | ||
718db961 LP |
242 | r = sd_bus_message_enter_container(message, 'a', "(st)"); |
243 | if (r < 0) | |
244 | return r; | |
f004c2ca | 245 | |
718db961 | 246 | while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) { |
f004c2ca G |
247 | |
248 | if (mode != UNIT_CHECK) { | |
718db961 | 249 | CGroupBlockIODeviceBandwidth *a = NULL, *b; |
f004c2ca G |
250 | |
251 | LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) { | |
252 | if (path_equal(path, b->path) && read == b->read) { | |
253 | a = b; | |
f004c2ca G |
254 | break; |
255 | } | |
256 | } | |
257 | ||
718db961 | 258 | if (!a) { |
f004c2ca G |
259 | a = new0(CGroupBlockIODeviceBandwidth, 1); |
260 | if (!a) | |
261 | return -ENOMEM; | |
262 | ||
263 | a->read = read; | |
264 | a->path = strdup(path); | |
265 | if (!a->path) { | |
266 | free(a); | |
267 | return -ENOMEM; | |
268 | } | |
718db961 LP |
269 | |
270 | LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, a); | |
f004c2ca G |
271 | } |
272 | ||
273 | a->bandwidth = u64; | |
f004c2ca G |
274 | } |
275 | ||
276 | n++; | |
f004c2ca | 277 | } |
718db961 LP |
278 | if (r < 0) |
279 | return r; | |
f004c2ca G |
280 | |
281 | if (mode != UNIT_CHECK) { | |
718db961 | 282 | CGroupBlockIODeviceBandwidth *a, *next; |
f004c2ca G |
283 | _cleanup_free_ char *buf = NULL; |
284 | _cleanup_fclose_ FILE *f = NULL; | |
f004c2ca G |
285 | size_t size = 0; |
286 | ||
287 | if (n == 0) { | |
288 | LIST_FOREACH_SAFE(device_bandwidths, a, next, c->blockio_device_bandwidths) | |
289 | if (a->read == read) | |
290 | cgroup_context_free_blockio_device_bandwidth(c, a); | |
291 | } | |
292 | ||
293 | f = open_memstream(&buf, &size); | |
294 | if (!f) | |
295 | return -ENOMEM; | |
296 | ||
297 | if (read) { | |
298 | fputs("BlockIOReadBandwidth=\n", f); | |
299 | LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths) | |
300 | if (a->read) | |
301 | fprintf(f, "BlockIOReadBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth); | |
302 | } else { | |
303 | fputs("BlockIOWriteBandwidth=\n", f); | |
304 | LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths) | |
305 | if (!a->read) | |
306 | fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth); | |
307 | } | |
308 | ||
6f68ecb4 G |
309 | fflush(f); |
310 | unit_write_drop_in_private(u, mode, name, buf); | |
311 | } | |
312 | ||
313 | return 1; | |
314 | ||
315 | } else if (streq(name, "BlockIODeviceWeight")) { | |
718db961 LP |
316 | const char *path; |
317 | uint64_t u64; | |
6f68ecb4 G |
318 | unsigned n = 0; |
319 | ||
718db961 LP |
320 | r = sd_bus_message_enter_container(message, 'a', "(st)"); |
321 | if (r < 0) | |
322 | return r; | |
6f68ecb4 | 323 | |
ccd06097 ZJS |
324 | while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) { |
325 | unsigned long ul = u64; | |
6f68ecb4 | 326 | |
6f68ecb4 | 327 | if (ul < 10 || ul > 1000) |
718db961 | 328 | return sd_bus_error_set_errnof(error, EINVAL, "BlockIODeviceWeight out of range"); |
6f68ecb4 G |
329 | |
330 | if (mode != UNIT_CHECK) { | |
718db961 | 331 | CGroupBlockIODeviceWeight *a = NULL, *b; |
6f68ecb4 G |
332 | |
333 | LIST_FOREACH(device_weights, b, c->blockio_device_weights) { | |
334 | if (path_equal(b->path, path)) { | |
335 | a = b; | |
6f68ecb4 G |
336 | break; |
337 | } | |
338 | } | |
339 | ||
718db961 | 340 | if (!a) { |
6f68ecb4 G |
341 | a = new0(CGroupBlockIODeviceWeight, 1); |
342 | if (!a) | |
343 | return -ENOMEM; | |
344 | ||
345 | a->path = strdup(path); | |
346 | if (!a->path) { | |
347 | free(a); | |
348 | return -ENOMEM; | |
349 | } | |
718db961 | 350 | LIST_PREPEND(device_weights,c->blockio_device_weights, a); |
6f68ecb4 G |
351 | } |
352 | ||
353 | a->weight = ul; | |
6f68ecb4 G |
354 | } |
355 | ||
356 | n++; | |
6f68ecb4 G |
357 | } |
358 | ||
359 | if (mode != UNIT_CHECK) { | |
360 | _cleanup_free_ char *buf = NULL; | |
361 | _cleanup_fclose_ FILE *f = NULL; | |
362 | CGroupBlockIODeviceWeight *a; | |
363 | size_t size = 0; | |
364 | ||
365 | if (n == 0) { | |
366 | while (c->blockio_device_weights) | |
367 | cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights); | |
368 | } | |
369 | ||
370 | f = open_memstream(&buf, &size); | |
371 | if (!f) | |
372 | return -ENOMEM; | |
373 | ||
374 | fputs("BlockIODeviceWeight=\n", f); | |
375 | LIST_FOREACH(device_weights, a, c->blockio_device_weights) | |
376 | fprintf(f, "BlockIODeviceWeight=%s %lu\n", a->path, a->weight); | |
377 | ||
f004c2ca G |
378 | fflush(f); |
379 | unit_write_drop_in_private(u, mode, name, buf); | |
380 | } | |
381 | ||
382 | return 1; | |
383 | ||
8e2af478 | 384 | } else if (streq(name, "MemoryAccounting")) { |
718db961 | 385 | int b; |
8e2af478 | 386 | |
718db961 LP |
387 | r = sd_bus_message_read(message, "b", &b); |
388 | if (r < 0) | |
389 | return r; | |
8e2af478 LP |
390 | |
391 | if (mode != UNIT_CHECK) { | |
b42defe3 | 392 | c->memory_accounting = b; |
b9ec9359 | 393 | unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no"); |
8e2af478 LP |
394 | } |
395 | ||
396 | return 1; | |
8e2af478 | 397 | |
ddca82ac | 398 | } else if (streq(name, "MemoryLimit")) { |
718db961 | 399 | uint64_t limit; |
b42defe3 | 400 | |
718db961 LP |
401 | r = sd_bus_message_read(message, "t", &limit); |
402 | if (r < 0) | |
403 | return r; | |
b42defe3 LP |
404 | |
405 | if (mode != UNIT_CHECK) { | |
ddca82ac | 406 | c->memory_limit = limit; |
b9ec9359 | 407 | unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit); |
b42defe3 LP |
408 | } |
409 | ||
7041efe9 LP |
410 | return 1; |
411 | ||
412 | } else if (streq(name, "DevicePolicy")) { | |
413 | const char *policy; | |
414 | CGroupDevicePolicy p; | |
415 | ||
718db961 LP |
416 | r = sd_bus_message_read(message, "s", &policy); |
417 | if (r < 0) | |
418 | return r; | |
7041efe9 | 419 | |
7041efe9 LP |
420 | p = cgroup_device_policy_from_string(policy); |
421 | if (p < 0) | |
422 | return -EINVAL; | |
423 | ||
424 | if (mode != UNIT_CHECK) { | |
425 | char *buf; | |
426 | ||
427 | c->device_policy = p; | |
428 | ||
429 | buf = strappenda("DevicePolicy=", policy); | |
b9ec9359 | 430 | unit_write_drop_in_private(u, mode, name, buf); |
7041efe9 LP |
431 | } |
432 | ||
433 | return 1; | |
434 | ||
435 | } else if (streq(name, "DeviceAllow")) { | |
718db961 | 436 | const char *path, *rwm; |
7041efe9 LP |
437 | unsigned n = 0; |
438 | ||
718db961 LP |
439 | r = sd_bus_message_enter_container(message, 'a', "(ss)"); |
440 | if (r < 0) | |
441 | return r; | |
7041efe9 | 442 | |
718db961 | 443 | while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) { |
7041efe9 | 444 | |
90060676 LP |
445 | if ((!startswith(path, "/dev/") && |
446 | !startswith(path, "block-") && | |
447 | !startswith(path, "char-")) || | |
448 | strpbrk(path, WHITESPACE)) | |
449 | return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires device node"); | |
7041efe9 LP |
450 | |
451 | if (isempty(rwm)) | |
452 | rwm = "rwm"; | |
453 | ||
718db961 LP |
454 | if (!in_charset(rwm, "rwm")) |
455 | return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires combination of rwm flags"); | |
7041efe9 | 456 | |
7041efe9 | 457 | if (mode != UNIT_CHECK) { |
718db961 | 458 | CGroupDeviceAllow *a = NULL, *b; |
ad7bfffd G |
459 | |
460 | LIST_FOREACH(device_allow, b, c->device_allow) { | |
06eb4e3b | 461 | if (path_equal(b->path, path)) { |
ad7bfffd | 462 | a = b; |
ad7bfffd G |
463 | break; |
464 | } | |
465 | } | |
466 | ||
718db961 | 467 | if (!a) { |
ad7bfffd G |
468 | a = new0(CGroupDeviceAllow, 1); |
469 | if (!a) | |
470 | return -ENOMEM; | |
471 | ||
472 | a->path = strdup(path); | |
473 | if (!a->path) { | |
474 | free(a); | |
475 | return -ENOMEM; | |
476 | } | |
718db961 LP |
477 | |
478 | LIST_PREPEND(device_allow, c->device_allow, a); | |
7041efe9 LP |
479 | } |
480 | ||
481 | a->r = !!strchr(rwm, 'r'); | |
482 | a->w = !!strchr(rwm, 'w'); | |
483 | a->m = !!strchr(rwm, 'm'); | |
484 | ||
7041efe9 LP |
485 | } |
486 | ||
c2756a68 | 487 | n++; |
7041efe9 | 488 | } |
43a99a7a LP |
489 | if (r < 0) |
490 | return r; | |
7041efe9 LP |
491 | |
492 | if (mode != UNIT_CHECK) { | |
493 | _cleanup_free_ char *buf = NULL; | |
494 | _cleanup_fclose_ FILE *f = NULL; | |
495 | CGroupDeviceAllow *a; | |
496 | size_t size = 0; | |
497 | ||
498 | if (n == 0) { | |
499 | while (c->device_allow) | |
500 | cgroup_context_free_device_allow(c, c->device_allow); | |
501 | } | |
502 | ||
503 | f = open_memstream(&buf, &size); | |
504 | if (!f) | |
505 | return -ENOMEM; | |
506 | ||
507 | fputs("DeviceAllow=\n", f); | |
508 | LIST_FOREACH(device_allow, a, c->device_allow) | |
509 | fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : ""); | |
510 | ||
511 | fflush(f); | |
b9ec9359 | 512 | unit_write_drop_in_private(u, mode, name, buf); |
7041efe9 LP |
513 | } |
514 | ||
b42defe3 LP |
515 | return 1; |
516 | } | |
8e2af478 LP |
517 | |
518 | return 0; | |
519 | } |