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_cpu_quota_usec(
139 const char *interface,
140 const char *property,
141 sd_bus_message *reply,
143 sd_bus_error *error) {
145 CGroupContext *c = userdata;
151 return sd_bus_message_append(reply, "t", cgroup_context_get_cpu_quota_usec(c));
154 static int property_get_cpu_quota_per_sec_usec(
157 const char *interface,
158 const char *property,
159 sd_bus_message *reply,
161 sd_bus_error *error) {
163 CGroupContext *c = userdata;
169 return sd_bus_message_append(reply, "t", cgroup_context_get_cpu_quota_per_sec_usec(c));
172 const sd_bus_vtable bus_cgroup_vtable[] = {
173 SD_BUS_VTABLE_START(0),
174 SD_BUS_PROPERTY("CPUAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, cpu_accounting), 0),
175 SD_BUS_PROPERTY("CPUShares", "t", bus_property_get_ulong, offsetof(CGroupContext, cpu_shares), 0),
176 SD_BUS_PROPERTY("StartupCPUShares", "t", bus_property_get_ulong, offsetof(CGroupContext, startup_cpu_shares), 0),
177 SD_BUS_PROPERTY("CPUQuotaPerSecUSec", "t", property_get_cpu_quota_per_sec_usec, 0, 0),
178 SD_BUS_PROPERTY("CPUQuotaUSec", "t", property_get_cpu_quota_usec, 0, 0),
179 SD_BUS_PROPERTY("CPUQuotaPeriodUSec", "t", bus_property_get_usec, offsetof(CGroupContext, cpu_quota_period_usec), 0),
180 SD_BUS_PROPERTY("BlockIOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, blockio_accounting), 0),
181 SD_BUS_PROPERTY("BlockIOWeight", "t", bus_property_get_ulong, offsetof(CGroupContext, blockio_weight), 0),
182 SD_BUS_PROPERTY("StartupBlockIOWeight", "t", bus_property_get_ulong, offsetof(CGroupContext, startup_blockio_weight), 0),
183 SD_BUS_PROPERTY("BlockIODeviceWeight", "a(st)", property_get_blockio_device_weight, 0, 0),
184 SD_BUS_PROPERTY("BlockIOReadBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
185 SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
186 SD_BUS_PROPERTY("MemoryAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, memory_accounting), 0),
187 SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0),
188 SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0),
189 SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0),
193 int bus_cgroup_set_property(
197 sd_bus_message *message,
198 UnitSetPropertiesMode mode,
199 sd_bus_error *error) {
208 if (streq(name, "CPUAccounting")) {
211 r = sd_bus_message_read(message, "b", &b);
215 if (mode != UNIT_CHECK) {
216 c->cpu_accounting = b;
217 u->cgroup_realized_mask &= ~CGROUP_CPUACCT;
218 unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no");
223 } else if (streq(name, "CPUShares")) {
227 r = sd_bus_message_read(message, "t", &u64);
231 ul = (unsigned long) u64;
232 if (ul <= 0 || (uint64_t) ul != u64)
233 return sd_bus_error_set_errnof(error, EINVAL, "CPUShares value out of range");
235 if (mode != UNIT_CHECK) {
237 u->cgroup_realized_mask &= ~CGROUP_CPU;
238 unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul);
243 } else if (streq(name, "StartupCPUShares")) {
247 r = sd_bus_message_read(message, "t", &u64);
251 ul = (unsigned long) u64;
252 if (ul <= 0 || (uint64_t) ul != u64)
253 return sd_bus_error_set_errnof(error, EINVAL, "StartupCPUShares value out of range");
255 if (mode != UNIT_CHECK) {
256 c->startup_cpu_shares = ul;
257 c->startup_cpu_shares_set = true;
258 unit_write_drop_in_private_format(u, mode, name, "StartupCPUShares=%lu", ul);
263 } else if (streq(name, "CPUQuotaPerSecUSec")) {
266 r = sd_bus_message_read(message, "t", &u64);
271 return sd_bus_error_set_errnof(error, EINVAL, "CPUQuotaPerSecUSec value out of range");
273 if (mode != UNIT_CHECK) {
274 c->cpu_quota_per_sec_usec = u64;
275 c->cpu_quota_usec = (uint64_t) -1;
276 u->cgroup_realized_mask &= ~CGROUP_CPU;
277 unit_write_drop_in_private_format(u, mode, "CPUQuota", "CPUQuota=%0.f%%", (double) (c->cpu_quota_per_sec_usec / 10000));
282 } else if (streq(name, "CPUQuotaUSec")) {
285 r = sd_bus_message_read(message, "t", &u64);
290 return sd_bus_error_set_errnof(error, EINVAL, "CPUQuotaUSec value out of range");
292 if (mode != UNIT_CHECK) {
293 c->cpu_quota_usec = u64;
294 c->cpu_quota_per_sec_usec = (uint64_t) -1;
295 u->cgroup_realized_mask &= ~CGROUP_CPU;
296 unit_write_drop_in_private_format(u, mode, "CPUQuota", "CPUQuota=%" PRIu64 "us", u64);
301 } else if (streq(name, "CPUQuotaPeriodUSec")) {
305 r = sd_bus_message_read(message, "t", &u64);
309 if (u64 <= 0 || u64 >= (usec_t) -1)
310 return sd_bus_error_set_errnof(error, EINVAL, "CPUQuotaPeriodUSec value out of range");
312 if (mode != UNIT_CHECK) {
313 c->cpu_quota_period_usec = u64;
314 u->cgroup_realized_mask &= ~CGROUP_CPU;
315 unit_write_drop_in_private_format(u, mode, name, "CPUQuotaPeriodSec=%" PRIu64 "us", c->cpu_quota_period_usec);
320 } else if (streq(name, "BlockIOAccounting")) {
323 r = sd_bus_message_read(message, "b", &b);
327 if (mode != UNIT_CHECK) {
328 c->blockio_accounting = b;
329 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
330 unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no");
335 } else if (streq(name, "BlockIOWeight")) {
339 r = sd_bus_message_read(message, "t", &u64);
343 ul = (unsigned long) u64;
344 if (ul < 10 || ul > 1000)
345 return sd_bus_error_set_errnof(error, EINVAL, "BlockIOWeight value out of range");
347 if (mode != UNIT_CHECK) {
348 c->blockio_weight = ul;
349 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
350 unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul);
355 } else if (streq(name, "StartupBlockIOWeight")) {
359 r = sd_bus_message_read(message, "t", &u64);
363 ul = (unsigned long) u64;
364 if (ul < 10 || ul > 1000)
365 return sd_bus_error_set_errnof(error, EINVAL, "StartupBlockIOWeight value out of range");
367 if (mode != UNIT_CHECK) {
368 c->startup_blockio_weight = ul;
369 c->startup_blockio_weight_set = true;
370 unit_write_drop_in_private_format(u, mode, name, "StartupBlockIOWeight=%lu", ul);
375 } else if (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth")) {
381 if (streq(name, "BlockIOWriteBandwidth"))
384 r = sd_bus_message_enter_container(message, 'a', "(st)");
388 while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
390 if (mode != UNIT_CHECK) {
391 CGroupBlockIODeviceBandwidth *a = NULL, *b;
393 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
394 if (path_equal(path, b->path) && read == b->read) {
401 a = new0(CGroupBlockIODeviceBandwidth, 1);
406 a->path = strdup(path);
412 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, a);
423 r = sd_bus_message_exit_container(message);
427 if (mode != UNIT_CHECK) {
428 CGroupBlockIODeviceBandwidth *a, *next;
429 _cleanup_free_ char *buf = NULL;
430 _cleanup_fclose_ FILE *f = NULL;
434 LIST_FOREACH_SAFE(device_bandwidths, a, next, c->blockio_device_bandwidths)
436 cgroup_context_free_blockio_device_bandwidth(c, a);
439 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
441 f = open_memstream(&buf, &size);
446 fputs("BlockIOReadBandwidth=\n", f);
447 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
449 fprintf(f, "BlockIOReadBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
451 fputs("BlockIOWriteBandwidth=\n", f);
452 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
454 fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
458 unit_write_drop_in_private(u, mode, name, buf);
463 } else if (streq(name, "BlockIODeviceWeight")) {
468 r = sd_bus_message_enter_container(message, 'a', "(st)");
472 while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
473 unsigned long ul = u64;
475 if (ul < 10 || ul > 1000)
476 return sd_bus_error_set_errnof(error, EINVAL, "BlockIODeviceWeight out of range");
478 if (mode != UNIT_CHECK) {
479 CGroupBlockIODeviceWeight *a = NULL, *b;
481 LIST_FOREACH(device_weights, b, c->blockio_device_weights) {
482 if (path_equal(b->path, path)) {
489 a = new0(CGroupBlockIODeviceWeight, 1);
493 a->path = strdup(path);
498 LIST_PREPEND(device_weights,c->blockio_device_weights, a);
507 r = sd_bus_message_exit_container(message);
511 if (mode != UNIT_CHECK) {
512 _cleanup_free_ char *buf = NULL;
513 _cleanup_fclose_ FILE *f = NULL;
514 CGroupBlockIODeviceWeight *a;
518 while (c->blockio_device_weights)
519 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
522 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
524 f = open_memstream(&buf, &size);
528 fputs("BlockIODeviceWeight=\n", f);
529 LIST_FOREACH(device_weights, a, c->blockio_device_weights)
530 fprintf(f, "BlockIODeviceWeight=%s %lu\n", a->path, a->weight);
533 unit_write_drop_in_private(u, mode, name, buf);
538 } else if (streq(name, "MemoryAccounting")) {
541 r = sd_bus_message_read(message, "b", &b);
545 if (mode != UNIT_CHECK) {
546 c->memory_accounting = b;
547 u->cgroup_realized_mask &= ~CGROUP_MEMORY;
548 unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no");
553 } else if (streq(name, "MemoryLimit")) {
556 r = sd_bus_message_read(message, "t", &limit);
560 if (mode != UNIT_CHECK) {
561 c->memory_limit = limit;
562 u->cgroup_realized_mask &= ~CGROUP_MEMORY;
563 unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit);
568 } else if (streq(name, "DevicePolicy")) {
570 CGroupDevicePolicy p;
572 r = sd_bus_message_read(message, "s", &policy);
576 p = cgroup_device_policy_from_string(policy);
580 if (mode != UNIT_CHECK) {
583 c->device_policy = p;
584 u->cgroup_realized_mask &= ~CGROUP_DEVICE;
586 buf = strappenda("DevicePolicy=", policy);
587 unit_write_drop_in_private(u, mode, name, buf);
592 } else if (streq(name, "DeviceAllow")) {
593 const char *path, *rwm;
596 r = sd_bus_message_enter_container(message, 'a', "(ss)");
600 while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) {
602 if ((!startswith(path, "/dev/") &&
603 !startswith(path, "block-") &&
604 !startswith(path, "char-")) ||
605 strpbrk(path, WHITESPACE))
606 return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires device node");
611 if (!in_charset(rwm, "rwm"))
612 return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires combination of rwm flags");
614 if (mode != UNIT_CHECK) {
615 CGroupDeviceAllow *a = NULL, *b;
617 LIST_FOREACH(device_allow, b, c->device_allow) {
618 if (path_equal(b->path, path)) {
625 a = new0(CGroupDeviceAllow, 1);
629 a->path = strdup(path);
635 LIST_PREPEND(device_allow, c->device_allow, a);
638 a->r = !!strchr(rwm, 'r');
639 a->w = !!strchr(rwm, 'w');
640 a->m = !!strchr(rwm, 'm');
648 r = sd_bus_message_exit_container(message);
652 if (mode != UNIT_CHECK) {
653 _cleanup_free_ char *buf = NULL;
654 _cleanup_fclose_ FILE *f = NULL;
655 CGroupDeviceAllow *a;
659 while (c->device_allow)
660 cgroup_context_free_device_allow(c, c->device_allow);
663 u->cgroup_realized_mask &= ~CGROUP_DEVICE;
665 f = open_memstream(&buf, &size);
669 fputs("DeviceAllow=\n", f);
670 LIST_FOREACH(device_allow, a, c->device_allow)
671 fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
674 unit_write_drop_in_private(u, mode, name, buf);