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 static int property_get_ulong_as_u64(
175 const char *interface,
176 const char *property,
177 sd_bus_message *reply,
179 sd_bus_error *error) {
181 unsigned long *ul = userdata;
187 return sd_bus_message_append(reply, "t", *ul == (unsigned long) -1 ? (uint64_t) -1 : (uint64_t) *ul);
190 const sd_bus_vtable bus_cgroup_vtable[] = {
191 SD_BUS_VTABLE_START(0),
192 SD_BUS_PROPERTY("CPUAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, cpu_accounting), 0),
193 SD_BUS_PROPERTY("CPUShares", "t", property_get_ulong_as_u64, offsetof(CGroupContext, cpu_shares), 0),
194 SD_BUS_PROPERTY("StartupCPUShares", "t", property_get_ulong_as_u64, offsetof(CGroupContext, startup_cpu_shares), 0),
195 SD_BUS_PROPERTY("CPUQuotaPerSecUSec", "t", property_get_cpu_quota_per_sec_usec, 0, 0),
196 SD_BUS_PROPERTY("CPUQuotaUSec", "t", property_get_cpu_quota_usec, 0, 0),
197 SD_BUS_PROPERTY("CPUQuotaPeriodUSec", "t", bus_property_get_usec, offsetof(CGroupContext, cpu_quota_period_usec), 0),
198 SD_BUS_PROPERTY("BlockIOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, blockio_accounting), 0),
199 SD_BUS_PROPERTY("BlockIOWeight", "t", property_get_ulong_as_u64, offsetof(CGroupContext, blockio_weight), 0),
200 SD_BUS_PROPERTY("StartupBlockIOWeight", "t", property_get_ulong_as_u64, offsetof(CGroupContext, startup_blockio_weight), 0),
201 SD_BUS_PROPERTY("BlockIODeviceWeight", "a(st)", property_get_blockio_device_weight, 0, 0),
202 SD_BUS_PROPERTY("BlockIOReadBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
203 SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
204 SD_BUS_PROPERTY("MemoryAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, memory_accounting), 0),
205 SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0),
206 SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0),
207 SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0),
211 int bus_cgroup_set_property(
215 sd_bus_message *message,
216 UnitSetPropertiesMode mode,
217 sd_bus_error *error) {
226 if (streq(name, "CPUAccounting")) {
229 r = sd_bus_message_read(message, "b", &b);
233 if (mode != UNIT_CHECK) {
234 c->cpu_accounting = b;
235 u->cgroup_realized_mask &= ~CGROUP_CPUACCT;
236 unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no");
241 } else if (streq(name, "CPUShares")) {
245 r = sd_bus_message_read(message, "t", &u64);
249 if (u64 == (uint64_t) -1)
250 ul = (unsigned long) -1;
252 ul = (unsigned long) u64;
253 if (ul <= 0 || (uint64_t) ul != u64)
254 return sd_bus_error_set_errnof(error, EINVAL, "CPUShares value out of range");
257 if (mode != UNIT_CHECK) {
259 u->cgroup_realized_mask &= ~CGROUP_CPU;
260 unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul);
265 } else if (streq(name, "StartupCPUShares")) {
269 r = sd_bus_message_read(message, "t", &u64);
273 if (u64 == (uint64_t) -1)
274 ul = (unsigned long) -1;
276 ul = (unsigned long) u64;
277 if (ul <= 0 || (uint64_t) ul != u64)
278 return sd_bus_error_set_errnof(error, EINVAL, "StartupCPUShares value out of range");
281 if (mode != UNIT_CHECK) {
282 c->startup_cpu_shares = ul;
283 u->cgroup_realized_mask &= ~CGROUP_CPU;
284 unit_write_drop_in_private_format(u, mode, name, "StartupCPUShares=%lu", ul);
289 } else if (streq(name, "CPUQuotaPerSecUSec")) {
292 r = sd_bus_message_read(message, "t", &u64);
297 return sd_bus_error_set_errnof(error, EINVAL, "CPUQuotaPerSecUSec value out of range");
299 if (mode != UNIT_CHECK) {
300 c->cpu_quota_per_sec_usec = u64;
301 c->cpu_quota_usec = (uint64_t) -1;
302 u->cgroup_realized_mask &= ~CGROUP_CPU;
303 unit_write_drop_in_private_format(u, mode, "CPUQuota", "CPUQuota=%0.f%%", (double) (c->cpu_quota_per_sec_usec / 10000));
308 } else if (streq(name, "CPUQuotaUSec")) {
311 r = sd_bus_message_read(message, "t", &u64);
316 return sd_bus_error_set_errnof(error, EINVAL, "CPUQuotaUSec value out of range");
318 if (mode != UNIT_CHECK) {
319 c->cpu_quota_usec = u64;
320 c->cpu_quota_per_sec_usec = (uint64_t) -1;
321 u->cgroup_realized_mask &= ~CGROUP_CPU;
322 unit_write_drop_in_private_format(u, mode, "CPUQuota", "CPUQuota=%" PRIu64 "us", u64);
327 } else if (streq(name, "CPUQuotaPeriodUSec")) {
331 r = sd_bus_message_read(message, "t", &u64);
335 if (u64 <= 0 || u64 >= (usec_t) -1)
336 return sd_bus_error_set_errnof(error, EINVAL, "CPUQuotaPeriodUSec value out of range");
338 if (mode != UNIT_CHECK) {
339 c->cpu_quota_period_usec = u64;
340 u->cgroup_realized_mask &= ~CGROUP_CPU;
341 unit_write_drop_in_private_format(u, mode, name, "CPUQuotaPeriodSec=%" PRIu64 "us", c->cpu_quota_period_usec);
346 } else if (streq(name, "BlockIOAccounting")) {
349 r = sd_bus_message_read(message, "b", &b);
353 if (mode != UNIT_CHECK) {
354 c->blockio_accounting = b;
355 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
356 unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no");
361 } else if (streq(name, "BlockIOWeight")) {
365 r = sd_bus_message_read(message, "t", &u64);
369 if (u64 == (uint64_t) -1)
370 ul = (unsigned long) -1;
372 ul = (unsigned long) u64;
373 if (ul < 10 || ul > 1000)
374 return sd_bus_error_set_errnof(error, EINVAL, "BlockIOWeight value out of range");
377 if (mode != UNIT_CHECK) {
378 c->blockio_weight = ul;
379 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
380 unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul);
385 } else if (streq(name, "StartupBlockIOWeight")) {
389 r = sd_bus_message_read(message, "t", &u64);
393 if (u64 == (uint64_t) -1)
394 ul = (unsigned long) -1;
396 ul = (unsigned long) u64;
397 if (ul < 10 || ul > 1000)
398 return sd_bus_error_set_errnof(error, EINVAL, "StartupBlockIOWeight value out of range");
401 if (mode != UNIT_CHECK) {
402 c->startup_blockio_weight = ul;
403 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
404 unit_write_drop_in_private_format(u, mode, name, "StartupBlockIOWeight=%lu", ul);
409 } else if (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth")) {
415 if (streq(name, "BlockIOWriteBandwidth"))
418 r = sd_bus_message_enter_container(message, 'a', "(st)");
422 while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
424 if (mode != UNIT_CHECK) {
425 CGroupBlockIODeviceBandwidth *a = NULL, *b;
427 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
428 if (path_equal(path, b->path) && read == b->read) {
435 a = new0(CGroupBlockIODeviceBandwidth, 1);
440 a->path = strdup(path);
446 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, a);
457 r = sd_bus_message_exit_container(message);
461 if (mode != UNIT_CHECK) {
462 CGroupBlockIODeviceBandwidth *a, *next;
463 _cleanup_free_ char *buf = NULL;
464 _cleanup_fclose_ FILE *f = NULL;
468 LIST_FOREACH_SAFE(device_bandwidths, a, next, c->blockio_device_bandwidths)
470 cgroup_context_free_blockio_device_bandwidth(c, a);
473 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
475 f = open_memstream(&buf, &size);
480 fputs("BlockIOReadBandwidth=\n", f);
481 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
483 fprintf(f, "BlockIOReadBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
485 fputs("BlockIOWriteBandwidth=\n", f);
486 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
488 fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
492 unit_write_drop_in_private(u, mode, name, buf);
497 } else if (streq(name, "BlockIODeviceWeight")) {
502 r = sd_bus_message_enter_container(message, 'a', "(st)");
506 while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
507 unsigned long ul = u64;
509 if (ul < 10 || ul > 1000)
510 return sd_bus_error_set_errnof(error, EINVAL, "BlockIODeviceWeight out of range");
512 if (mode != UNIT_CHECK) {
513 CGroupBlockIODeviceWeight *a = NULL, *b;
515 LIST_FOREACH(device_weights, b, c->blockio_device_weights) {
516 if (path_equal(b->path, path)) {
523 a = new0(CGroupBlockIODeviceWeight, 1);
527 a->path = strdup(path);
532 LIST_PREPEND(device_weights,c->blockio_device_weights, a);
541 r = sd_bus_message_exit_container(message);
545 if (mode != UNIT_CHECK) {
546 _cleanup_free_ char *buf = NULL;
547 _cleanup_fclose_ FILE *f = NULL;
548 CGroupBlockIODeviceWeight *a;
552 while (c->blockio_device_weights)
553 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
556 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
558 f = open_memstream(&buf, &size);
562 fputs("BlockIODeviceWeight=\n", f);
563 LIST_FOREACH(device_weights, a, c->blockio_device_weights)
564 fprintf(f, "BlockIODeviceWeight=%s %lu\n", a->path, a->weight);
567 unit_write_drop_in_private(u, mode, name, buf);
572 } else if (streq(name, "MemoryAccounting")) {
575 r = sd_bus_message_read(message, "b", &b);
579 if (mode != UNIT_CHECK) {
580 c->memory_accounting = b;
581 u->cgroup_realized_mask &= ~CGROUP_MEMORY;
582 unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no");
587 } else if (streq(name, "MemoryLimit")) {
590 r = sd_bus_message_read(message, "t", &limit);
594 if (mode != UNIT_CHECK) {
595 c->memory_limit = limit;
596 u->cgroup_realized_mask &= ~CGROUP_MEMORY;
597 unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit);
602 } else if (streq(name, "DevicePolicy")) {
604 CGroupDevicePolicy p;
606 r = sd_bus_message_read(message, "s", &policy);
610 p = cgroup_device_policy_from_string(policy);
614 if (mode != UNIT_CHECK) {
617 c->device_policy = p;
618 u->cgroup_realized_mask &= ~CGROUP_DEVICE;
620 buf = strappenda("DevicePolicy=", policy);
621 unit_write_drop_in_private(u, mode, name, buf);
626 } else if (streq(name, "DeviceAllow")) {
627 const char *path, *rwm;
630 r = sd_bus_message_enter_container(message, 'a', "(ss)");
634 while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) {
636 if ((!startswith(path, "/dev/") &&
637 !startswith(path, "block-") &&
638 !startswith(path, "char-")) ||
639 strpbrk(path, WHITESPACE))
640 return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires device node");
645 if (!in_charset(rwm, "rwm"))
646 return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires combination of rwm flags");
648 if (mode != UNIT_CHECK) {
649 CGroupDeviceAllow *a = NULL, *b;
651 LIST_FOREACH(device_allow, b, c->device_allow) {
652 if (path_equal(b->path, path)) {
659 a = new0(CGroupDeviceAllow, 1);
663 a->path = strdup(path);
669 LIST_PREPEND(device_allow, c->device_allow, a);
672 a->r = !!strchr(rwm, 'r');
673 a->w = !!strchr(rwm, 'w');
674 a->m = !!strchr(rwm, 'm');
682 r = sd_bus_message_exit_container(message);
686 if (mode != UNIT_CHECK) {
687 _cleanup_free_ char *buf = NULL;
688 _cleanup_fclose_ FILE *f = NULL;
689 CGroupDeviceAllow *a;
693 while (c->device_allow)
694 cgroup_context_free_device_allow(c, c->device_allow);
697 u->cgroup_realized_mask &= ~CGROUP_DEVICE;
699 f = open_memstream(&buf, &size);
703 fputs("DeviceAllow=\n", f);
704 LIST_FOREACH(device_allow, a, c->device_allow)
705 fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
708 unit_write_drop_in_private(u, mode, name, buf);