1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 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/>.
23 #include "path-util.h"
24 #include "cgroup-util.h"
26 #include "dbus-cgroup.h"
28 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_cgroup_device_policy, cgroup_device_policy, CGroupDevicePolicy);
30 static int property_get_blockio_device_weight(
33 const char *interface,
35 sd_bus_message *reply,
37 sd_bus_error *error) {
39 CGroupContext *c = userdata;
40 CGroupBlockIODeviceWeight *w;
47 r = sd_bus_message_open_container(reply, 'a', "(st)");
51 LIST_FOREACH(device_weights, w, c->blockio_device_weights) {
52 r = sd_bus_message_append(reply, "(st)", w->path, w->weight);
57 return sd_bus_message_close_container(reply);
60 static int property_get_blockio_device_bandwidths(
63 const char *interface,
65 sd_bus_message *reply,
67 sd_bus_error *error) {
69 CGroupContext *c = userdata;
70 CGroupBlockIODeviceBandwidth *b;
77 r = sd_bus_message_open_container(reply, 'a', "(st)");
81 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
83 if (streq(property, "BlockIOReadBandwidth") != b->read)
86 r = sd_bus_message_append(reply, "(st)", b->path, b->bandwidth);
91 return sd_bus_message_close_container(reply);
94 static int property_get_device_allow(
97 const char *interface,
99 sd_bus_message *reply,
101 sd_bus_error *error) {
103 CGroupContext *c = userdata;
104 CGroupDeviceAllow *a;
111 r = sd_bus_message_open_container(reply, 'a', "(ss)");
115 LIST_FOREACH(device_allow, a, c->device_allow) {
128 r = sd_bus_message_append(reply, "(ss)", a->path, rwm);
133 return sd_bus_message_close_container(reply);
136 static int property_get_ulong_as_u64(
139 const char *interface,
140 const char *property,
141 sd_bus_message *reply,
143 sd_bus_error *error) {
145 unsigned long *ul = userdata;
151 return sd_bus_message_append(reply, "t", *ul == (unsigned long) -1 ? (uint64_t) -1 : (uint64_t) *ul);
154 const sd_bus_vtable bus_cgroup_vtable[] = {
155 SD_BUS_VTABLE_START(0),
156 SD_BUS_PROPERTY("CPUAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, cpu_accounting), 0),
157 SD_BUS_PROPERTY("CPUShares", "t", property_get_ulong_as_u64, offsetof(CGroupContext, cpu_shares), 0),
158 SD_BUS_PROPERTY("StartupCPUShares", "t", property_get_ulong_as_u64, offsetof(CGroupContext, startup_cpu_shares), 0),
159 SD_BUS_PROPERTY("CPUQuotaPerSecUSec", "t", bus_property_get_usec, offsetof(CGroupContext, cpu_quota_per_sec_usec), 0),
160 SD_BUS_PROPERTY("BlockIOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, blockio_accounting), 0),
161 SD_BUS_PROPERTY("BlockIOWeight", "t", property_get_ulong_as_u64, offsetof(CGroupContext, blockio_weight), 0),
162 SD_BUS_PROPERTY("StartupBlockIOWeight", "t", property_get_ulong_as_u64, offsetof(CGroupContext, startup_blockio_weight), 0),
163 SD_BUS_PROPERTY("BlockIODeviceWeight", "a(st)", property_get_blockio_device_weight, 0, 0),
164 SD_BUS_PROPERTY("BlockIOReadBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
165 SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
166 SD_BUS_PROPERTY("MemoryAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, memory_accounting), 0),
167 SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0),
168 SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0),
169 SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0),
173 int bus_cgroup_set_property(
177 sd_bus_message *message,
178 UnitSetPropertiesMode mode,
179 sd_bus_error *error) {
188 if (streq(name, "CPUAccounting")) {
191 r = sd_bus_message_read(message, "b", &b);
195 if (mode != UNIT_CHECK) {
196 c->cpu_accounting = b;
197 u->cgroup_realized_mask &= ~CGROUP_CPUACCT;
198 unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no");
203 } else if (streq(name, "CPUShares")) {
207 r = sd_bus_message_read(message, "t", &u64);
211 if (u64 == (uint64_t) -1)
212 ul = (unsigned long) -1;
214 ul = (unsigned long) u64;
215 if (ul <= 0 || (uint64_t) ul != u64)
216 return sd_bus_error_set_errnof(error, EINVAL, "CPUShares value out of range");
219 if (mode != UNIT_CHECK) {
221 u->cgroup_realized_mask &= ~CGROUP_CPU;
222 unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul);
227 } else if (streq(name, "StartupCPUShares")) {
231 r = sd_bus_message_read(message, "t", &u64);
235 if (u64 == (uint64_t) -1)
236 ul = (unsigned long) -1;
238 ul = (unsigned long) u64;
239 if (ul <= 0 || (uint64_t) ul != u64)
240 return sd_bus_error_set_errnof(error, EINVAL, "StartupCPUShares value out of range");
243 if (mode != UNIT_CHECK) {
244 c->startup_cpu_shares = ul;
245 u->cgroup_realized_mask &= ~CGROUP_CPU;
246 unit_write_drop_in_private_format(u, mode, name, "StartupCPUShares=%lu", ul);
251 } else if (streq(name, "CPUQuotaPerSecUSec")) {
254 r = sd_bus_message_read(message, "t", &u64);
259 return sd_bus_error_set_errnof(error, EINVAL, "CPUQuotaPerSecUSec value out of range");
261 if (mode != UNIT_CHECK) {
262 c->cpu_quota_per_sec_usec = u64;
263 u->cgroup_realized_mask &= ~CGROUP_CPU;
264 unit_write_drop_in_private_format(u, mode, "CPUQuota", "CPUQuota=%0.f%%", (double) (c->cpu_quota_per_sec_usec / 10000));
269 } else if (streq(name, "BlockIOAccounting")) {
272 r = sd_bus_message_read(message, "b", &b);
276 if (mode != UNIT_CHECK) {
277 c->blockio_accounting = b;
278 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
279 unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no");
284 } else if (streq(name, "BlockIOWeight")) {
288 r = sd_bus_message_read(message, "t", &u64);
292 if (u64 == (uint64_t) -1)
293 ul = (unsigned long) -1;
295 ul = (unsigned long) u64;
296 if (ul < 10 || ul > 1000)
297 return sd_bus_error_set_errnof(error, EINVAL, "BlockIOWeight value out of range");
300 if (mode != UNIT_CHECK) {
301 c->blockio_weight = ul;
302 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
303 unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul);
308 } else if (streq(name, "StartupBlockIOWeight")) {
312 r = sd_bus_message_read(message, "t", &u64);
316 if (u64 == (uint64_t) -1)
317 ul = (unsigned long) -1;
319 ul = (unsigned long) u64;
320 if (ul < 10 || ul > 1000)
321 return sd_bus_error_set_errnof(error, EINVAL, "StartupBlockIOWeight value out of range");
324 if (mode != UNIT_CHECK) {
325 c->startup_blockio_weight = ul;
326 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
327 unit_write_drop_in_private_format(u, mode, name, "StartupBlockIOWeight=%lu", ul);
332 } else if (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth")) {
338 if (streq(name, "BlockIOWriteBandwidth"))
341 r = sd_bus_message_enter_container(message, 'a', "(st)");
345 while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
347 if (mode != UNIT_CHECK) {
348 CGroupBlockIODeviceBandwidth *a = NULL, *b;
350 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
351 if (path_equal(path, b->path) && read == b->read) {
358 a = new0(CGroupBlockIODeviceBandwidth, 1);
363 a->path = strdup(path);
369 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, a);
380 r = sd_bus_message_exit_container(message);
384 if (mode != UNIT_CHECK) {
385 CGroupBlockIODeviceBandwidth *a, *next;
386 _cleanup_free_ char *buf = NULL;
387 _cleanup_fclose_ FILE *f = NULL;
391 LIST_FOREACH_SAFE(device_bandwidths, a, next, c->blockio_device_bandwidths)
393 cgroup_context_free_blockio_device_bandwidth(c, a);
396 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
398 f = open_memstream(&buf, &size);
403 fputs("BlockIOReadBandwidth=\n", f);
404 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
406 fprintf(f, "BlockIOReadBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
408 fputs("BlockIOWriteBandwidth=\n", f);
409 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
411 fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
415 unit_write_drop_in_private(u, mode, name, buf);
420 } else if (streq(name, "BlockIODeviceWeight")) {
425 r = sd_bus_message_enter_container(message, 'a', "(st)");
429 while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
430 unsigned long ul = u64;
432 if (ul < 10 || ul > 1000)
433 return sd_bus_error_set_errnof(error, EINVAL, "BlockIODeviceWeight out of range");
435 if (mode != UNIT_CHECK) {
436 CGroupBlockIODeviceWeight *a = NULL, *b;
438 LIST_FOREACH(device_weights, b, c->blockio_device_weights) {
439 if (path_equal(b->path, path)) {
446 a = new0(CGroupBlockIODeviceWeight, 1);
450 a->path = strdup(path);
455 LIST_PREPEND(device_weights,c->blockio_device_weights, a);
464 r = sd_bus_message_exit_container(message);
468 if (mode != UNIT_CHECK) {
469 _cleanup_free_ char *buf = NULL;
470 _cleanup_fclose_ FILE *f = NULL;
471 CGroupBlockIODeviceWeight *a;
475 while (c->blockio_device_weights)
476 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
479 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
481 f = open_memstream(&buf, &size);
485 fputs("BlockIODeviceWeight=\n", f);
486 LIST_FOREACH(device_weights, a, c->blockio_device_weights)
487 fprintf(f, "BlockIODeviceWeight=%s %lu\n", a->path, a->weight);
490 unit_write_drop_in_private(u, mode, name, buf);
495 } else if (streq(name, "MemoryAccounting")) {
498 r = sd_bus_message_read(message, "b", &b);
502 if (mode != UNIT_CHECK) {
503 c->memory_accounting = b;
504 u->cgroup_realized_mask &= ~CGROUP_MEMORY;
505 unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no");
510 } else if (streq(name, "MemoryLimit")) {
513 r = sd_bus_message_read(message, "t", &limit);
517 if (mode != UNIT_CHECK) {
518 c->memory_limit = limit;
519 u->cgroup_realized_mask &= ~CGROUP_MEMORY;
520 unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit);
525 } else if (streq(name, "DevicePolicy")) {
527 CGroupDevicePolicy p;
529 r = sd_bus_message_read(message, "s", &policy);
533 p = cgroup_device_policy_from_string(policy);
537 if (mode != UNIT_CHECK) {
540 c->device_policy = p;
541 u->cgroup_realized_mask &= ~CGROUP_DEVICE;
543 buf = strappenda("DevicePolicy=", policy);
544 unit_write_drop_in_private(u, mode, name, buf);
549 } else if (streq(name, "DeviceAllow")) {
550 const char *path, *rwm;
553 r = sd_bus_message_enter_container(message, 'a', "(ss)");
557 while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) {
559 if ((!startswith(path, "/dev/") &&
560 !startswith(path, "block-") &&
561 !startswith(path, "char-")) ||
562 strpbrk(path, WHITESPACE))
563 return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires device node");
568 if (!in_charset(rwm, "rwm"))
569 return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires combination of rwm flags");
571 if (mode != UNIT_CHECK) {
572 CGroupDeviceAllow *a = NULL, *b;
574 LIST_FOREACH(device_allow, b, c->device_allow) {
575 if (path_equal(b->path, path)) {
582 a = new0(CGroupDeviceAllow, 1);
586 a->path = strdup(path);
592 LIST_PREPEND(device_allow, c->device_allow, a);
595 a->r = !!strchr(rwm, 'r');
596 a->w = !!strchr(rwm, 'w');
597 a->m = !!strchr(rwm, 'm');
605 r = sd_bus_message_exit_container(message);
609 if (mode != UNIT_CHECK) {
610 _cleanup_free_ char *buf = NULL;
611 _cleanup_fclose_ FILE *f = NULL;
612 CGroupDeviceAllow *a;
616 while (c->device_allow)
617 cgroup_context_free_device_allow(c, c->device_allow);
620 u->cgroup_realized_mask &= ~CGROUP_DEVICE;
622 f = open_memstream(&buf, &size);
626 fputs("DeviceAllow=\n", f);
627 LIST_FOREACH(device_allow, a, c->device_allow)
628 fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
631 unit_write_drop_in_private(u, mode, name, buf);