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("Delegate", "b", bus_property_get_bool, offsetof(CGroupContext, delegate), 0),
157 SD_BUS_PROPERTY("CPUAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, cpu_accounting), 0),
158 SD_BUS_PROPERTY("CPUShares", "t", property_get_ulong_as_u64, offsetof(CGroupContext, cpu_shares), 0),
159 SD_BUS_PROPERTY("StartupCPUShares", "t", property_get_ulong_as_u64, offsetof(CGroupContext, startup_cpu_shares), 0),
160 SD_BUS_PROPERTY("CPUQuotaPerSecUSec", "t", bus_property_get_usec, offsetof(CGroupContext, cpu_quota_per_sec_usec), 0),
161 SD_BUS_PROPERTY("BlockIOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, blockio_accounting), 0),
162 SD_BUS_PROPERTY("BlockIOWeight", "t", property_get_ulong_as_u64, offsetof(CGroupContext, blockio_weight), 0),
163 SD_BUS_PROPERTY("StartupBlockIOWeight", "t", property_get_ulong_as_u64, offsetof(CGroupContext, startup_blockio_weight), 0),
164 SD_BUS_PROPERTY("BlockIODeviceWeight", "a(st)", property_get_blockio_device_weight, 0, 0),
165 SD_BUS_PROPERTY("BlockIOReadBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
166 SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
167 SD_BUS_PROPERTY("MemoryAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, memory_accounting), 0),
168 SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0),
169 SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0),
170 SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0),
174 static int bus_cgroup_set_transient_property(
178 sd_bus_message *message,
179 UnitSetPropertiesMode mode,
180 sd_bus_error *error) {
189 if (streq(name, "Delegate")) {
192 r = sd_bus_message_read(message, "b", &b);
196 if (mode != UNIT_CHECK) {
198 unit_write_drop_in_private(u, mode, name, b ? "Delegate=yes" : "Delegate=no");
207 int bus_cgroup_set_property(
211 sd_bus_message *message,
212 UnitSetPropertiesMode mode,
213 sd_bus_error *error) {
222 if (streq(name, "CPUAccounting")) {
225 r = sd_bus_message_read(message, "b", &b);
229 if (mode != UNIT_CHECK) {
230 c->cpu_accounting = b;
231 u->cgroup_realized_mask &= ~CGROUP_CPUACCT;
232 unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no");
237 } else if (streq(name, "CPUShares")) {
241 r = sd_bus_message_read(message, "t", &u64);
245 if (u64 == (uint64_t) -1)
246 ul = (unsigned long) -1;
248 ul = (unsigned long) u64;
249 if (ul <= 0 || (uint64_t) ul != u64)
250 return sd_bus_error_set_errnof(error, EINVAL, "CPUShares value out of range");
253 if (mode != UNIT_CHECK) {
255 u->cgroup_realized_mask &= ~CGROUP_CPU;
256 unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul);
261 } else if (streq(name, "StartupCPUShares")) {
265 r = sd_bus_message_read(message, "t", &u64);
269 if (u64 == (uint64_t) -1)
270 ul = (unsigned long) -1;
272 ul = (unsigned long) u64;
273 if (ul <= 0 || (uint64_t) ul != u64)
274 return sd_bus_error_set_errnof(error, EINVAL, "StartupCPUShares value out of range");
277 if (mode != UNIT_CHECK) {
278 c->startup_cpu_shares = ul;
279 u->cgroup_realized_mask &= ~CGROUP_CPU;
280 unit_write_drop_in_private_format(u, mode, name, "StartupCPUShares=%lu", ul);
285 } else if (streq(name, "CPUQuotaPerSecUSec")) {
288 r = sd_bus_message_read(message, "t", &u64);
293 return sd_bus_error_set_errnof(error, EINVAL, "CPUQuotaPerSecUSec value out of range");
295 if (mode != UNIT_CHECK) {
296 c->cpu_quota_per_sec_usec = u64;
297 u->cgroup_realized_mask &= ~CGROUP_CPU;
298 unit_write_drop_in_private_format(u, mode, "CPUQuota", "CPUQuota=%0.f%%", (double) (c->cpu_quota_per_sec_usec / 10000));
303 } else if (streq(name, "BlockIOAccounting")) {
306 r = sd_bus_message_read(message, "b", &b);
310 if (mode != UNIT_CHECK) {
311 c->blockio_accounting = b;
312 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
313 unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no");
318 } else if (streq(name, "BlockIOWeight")) {
322 r = sd_bus_message_read(message, "t", &u64);
326 if (u64 == (uint64_t) -1)
327 ul = (unsigned long) -1;
329 ul = (unsigned long) u64;
330 if (ul < 10 || ul > 1000)
331 return sd_bus_error_set_errnof(error, EINVAL, "BlockIOWeight value out of range");
334 if (mode != UNIT_CHECK) {
335 c->blockio_weight = ul;
336 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
337 unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul);
342 } else if (streq(name, "StartupBlockIOWeight")) {
346 r = sd_bus_message_read(message, "t", &u64);
350 if (u64 == (uint64_t) -1)
351 ul = (unsigned long) -1;
353 ul = (unsigned long) u64;
354 if (ul < 10 || ul > 1000)
355 return sd_bus_error_set_errnof(error, EINVAL, "StartupBlockIOWeight value out of range");
358 if (mode != UNIT_CHECK) {
359 c->startup_blockio_weight = ul;
360 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
361 unit_write_drop_in_private_format(u, mode, name, "StartupBlockIOWeight=%lu", ul);
366 } else if (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth")) {
372 if (streq(name, "BlockIOWriteBandwidth"))
375 r = sd_bus_message_enter_container(message, 'a', "(st)");
379 while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
381 if (mode != UNIT_CHECK) {
382 CGroupBlockIODeviceBandwidth *a = NULL, *b;
384 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
385 if (path_equal(path, b->path) && read == b->read) {
392 a = new0(CGroupBlockIODeviceBandwidth, 1);
397 a->path = strdup(path);
403 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, a);
414 r = sd_bus_message_exit_container(message);
418 if (mode != UNIT_CHECK) {
419 CGroupBlockIODeviceBandwidth *a, *next;
420 _cleanup_free_ char *buf = NULL;
421 _cleanup_fclose_ FILE *f = NULL;
425 LIST_FOREACH_SAFE(device_bandwidths, a, next, c->blockio_device_bandwidths)
427 cgroup_context_free_blockio_device_bandwidth(c, a);
430 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
432 f = open_memstream(&buf, &size);
437 fputs("BlockIOReadBandwidth=\n", f);
438 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
440 fprintf(f, "BlockIOReadBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
442 fputs("BlockIOWriteBandwidth=\n", f);
443 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
445 fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
449 unit_write_drop_in_private(u, mode, name, buf);
454 } else if (streq(name, "BlockIODeviceWeight")) {
459 r = sd_bus_message_enter_container(message, 'a', "(st)");
463 while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
464 unsigned long ul = u64;
466 if (ul < 10 || ul > 1000)
467 return sd_bus_error_set_errnof(error, EINVAL, "BlockIODeviceWeight out of range");
469 if (mode != UNIT_CHECK) {
470 CGroupBlockIODeviceWeight *a = NULL, *b;
472 LIST_FOREACH(device_weights, b, c->blockio_device_weights) {
473 if (path_equal(b->path, path)) {
480 a = new0(CGroupBlockIODeviceWeight, 1);
484 a->path = strdup(path);
489 LIST_PREPEND(device_weights,c->blockio_device_weights, a);
498 r = sd_bus_message_exit_container(message);
502 if (mode != UNIT_CHECK) {
503 _cleanup_free_ char *buf = NULL;
504 _cleanup_fclose_ FILE *f = NULL;
505 CGroupBlockIODeviceWeight *a;
509 while (c->blockio_device_weights)
510 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
513 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
515 f = open_memstream(&buf, &size);
519 fputs("BlockIODeviceWeight=\n", f);
520 LIST_FOREACH(device_weights, a, c->blockio_device_weights)
521 fprintf(f, "BlockIODeviceWeight=%s %lu\n", a->path, a->weight);
524 unit_write_drop_in_private(u, mode, name, buf);
529 } else if (streq(name, "MemoryAccounting")) {
532 r = sd_bus_message_read(message, "b", &b);
536 if (mode != UNIT_CHECK) {
537 c->memory_accounting = b;
538 u->cgroup_realized_mask &= ~CGROUP_MEMORY;
539 unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no");
544 } else if (streq(name, "MemoryLimit")) {
547 r = sd_bus_message_read(message, "t", &limit);
551 if (mode != UNIT_CHECK) {
552 c->memory_limit = limit;
553 u->cgroup_realized_mask &= ~CGROUP_MEMORY;
554 unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit);
559 } else if (streq(name, "DevicePolicy")) {
561 CGroupDevicePolicy p;
563 r = sd_bus_message_read(message, "s", &policy);
567 p = cgroup_device_policy_from_string(policy);
571 if (mode != UNIT_CHECK) {
574 c->device_policy = p;
575 u->cgroup_realized_mask &= ~CGROUP_DEVICE;
577 buf = strappenda("DevicePolicy=", policy);
578 unit_write_drop_in_private(u, mode, name, buf);
583 } else if (streq(name, "DeviceAllow")) {
584 const char *path, *rwm;
587 r = sd_bus_message_enter_container(message, 'a', "(ss)");
591 while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) {
593 if ((!startswith(path, "/dev/") &&
594 !startswith(path, "block-") &&
595 !startswith(path, "char-")) ||
596 strpbrk(path, WHITESPACE))
597 return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires device node");
602 if (!in_charset(rwm, "rwm"))
603 return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires combination of rwm flags");
605 if (mode != UNIT_CHECK) {
606 CGroupDeviceAllow *a = NULL, *b;
608 LIST_FOREACH(device_allow, b, c->device_allow) {
609 if (path_equal(b->path, path)) {
616 a = new0(CGroupDeviceAllow, 1);
620 a->path = strdup(path);
626 LIST_PREPEND(device_allow, c->device_allow, a);
629 a->r = !!strchr(rwm, 'r');
630 a->w = !!strchr(rwm, 'w');
631 a->m = !!strchr(rwm, 'm');
639 r = sd_bus_message_exit_container(message);
643 if (mode != UNIT_CHECK) {
644 _cleanup_free_ char *buf = NULL;
645 _cleanup_fclose_ FILE *f = NULL;
646 CGroupDeviceAllow *a;
650 while (c->device_allow)
651 cgroup_context_free_device_allow(c, c->device_allow);
654 u->cgroup_realized_mask &= ~CGROUP_DEVICE;
656 f = open_memstream(&buf, &size);
660 fputs("DeviceAllow=\n", f);
661 LIST_FOREACH(device_allow, a, c->device_allow)
662 fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
665 unit_write_drop_in_private(u, mode, name, buf);
672 if (u->transient && u->load_state == UNIT_STUB) {
673 r = bus_cgroup_set_transient_property(u, c, name, message, mode, error);