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_per_sec_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", c->cpu_quota_per_sec_usec);
154 static int property_get_ulong_as_u64(
157 const char *interface,
158 const char *property,
159 sd_bus_message *reply,
161 sd_bus_error *error) {
163 unsigned long *ul = userdata;
169 return sd_bus_message_append(reply, "t", *ul == (unsigned long) -1 ? (uint64_t) -1 : (uint64_t) *ul);
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", property_get_ulong_as_u64, offsetof(CGroupContext, cpu_shares), 0),
176 SD_BUS_PROPERTY("StartupCPUShares", "t", property_get_ulong_as_u64, 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("BlockIOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, blockio_accounting), 0),
179 SD_BUS_PROPERTY("BlockIOWeight", "t", property_get_ulong_as_u64, offsetof(CGroupContext, blockio_weight), 0),
180 SD_BUS_PROPERTY("StartupBlockIOWeight", "t", property_get_ulong_as_u64, offsetof(CGroupContext, startup_blockio_weight), 0),
181 SD_BUS_PROPERTY("BlockIODeviceWeight", "a(st)", property_get_blockio_device_weight, 0, 0),
182 SD_BUS_PROPERTY("BlockIOReadBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
183 SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
184 SD_BUS_PROPERTY("MemoryAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, memory_accounting), 0),
185 SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0),
186 SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0),
187 SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0),
191 int bus_cgroup_set_property(
195 sd_bus_message *message,
196 UnitSetPropertiesMode mode,
197 sd_bus_error *error) {
206 if (streq(name, "CPUAccounting")) {
209 r = sd_bus_message_read(message, "b", &b);
213 if (mode != UNIT_CHECK) {
214 c->cpu_accounting = b;
215 u->cgroup_realized_mask &= ~CGROUP_CPUACCT;
216 unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no");
221 } else if (streq(name, "CPUShares")) {
225 r = sd_bus_message_read(message, "t", &u64);
229 if (u64 == (uint64_t) -1)
230 ul = (unsigned long) -1;
232 ul = (unsigned long) u64;
233 if (ul <= 0 || (uint64_t) ul != u64)
234 return sd_bus_error_set_errnof(error, EINVAL, "CPUShares value out of range");
237 if (mode != UNIT_CHECK) {
239 u->cgroup_realized_mask &= ~CGROUP_CPU;
240 unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul);
245 } else if (streq(name, "StartupCPUShares")) {
249 r = sd_bus_message_read(message, "t", &u64);
253 if (u64 == (uint64_t) -1)
254 ul = (unsigned long) -1;
256 ul = (unsigned long) u64;
257 if (ul <= 0 || (uint64_t) ul != u64)
258 return sd_bus_error_set_errnof(error, EINVAL, "StartupCPUShares value out of range");
261 if (mode != UNIT_CHECK) {
262 c->startup_cpu_shares = ul;
263 u->cgroup_realized_mask &= ~CGROUP_CPU;
264 unit_write_drop_in_private_format(u, mode, name, "StartupCPUShares=%lu", ul);
269 } else if (streq(name, "CPUQuotaPerSecUSec")) {
272 r = sd_bus_message_read(message, "t", &u64);
277 return sd_bus_error_set_errnof(error, EINVAL, "CPUQuotaPerSecUSec value out of range");
279 if (mode != UNIT_CHECK) {
280 c->cpu_quota_per_sec_usec = u64;
281 u->cgroup_realized_mask &= ~CGROUP_CPU;
282 unit_write_drop_in_private_format(u, mode, "CPUQuota", "CPUQuota=%0.f%%", (double) (c->cpu_quota_per_sec_usec / 10000));
287 } else if (streq(name, "BlockIOAccounting")) {
290 r = sd_bus_message_read(message, "b", &b);
294 if (mode != UNIT_CHECK) {
295 c->blockio_accounting = b;
296 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
297 unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no");
302 } else if (streq(name, "BlockIOWeight")) {
306 r = sd_bus_message_read(message, "t", &u64);
310 if (u64 == (uint64_t) -1)
311 ul = (unsigned long) -1;
313 ul = (unsigned long) u64;
314 if (ul < 10 || ul > 1000)
315 return sd_bus_error_set_errnof(error, EINVAL, "BlockIOWeight value out of range");
318 if (mode != UNIT_CHECK) {
319 c->blockio_weight = ul;
320 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
321 unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul);
326 } else if (streq(name, "StartupBlockIOWeight")) {
330 r = sd_bus_message_read(message, "t", &u64);
334 if (u64 == (uint64_t) -1)
335 ul = (unsigned long) -1;
337 ul = (unsigned long) u64;
338 if (ul < 10 || ul > 1000)
339 return sd_bus_error_set_errnof(error, EINVAL, "StartupBlockIOWeight value out of range");
342 if (mode != UNIT_CHECK) {
343 c->startup_blockio_weight = ul;
344 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
345 unit_write_drop_in_private_format(u, mode, name, "StartupBlockIOWeight=%lu", ul);
350 } else if (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth")) {
356 if (streq(name, "BlockIOWriteBandwidth"))
359 r = sd_bus_message_enter_container(message, 'a', "(st)");
363 while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
365 if (mode != UNIT_CHECK) {
366 CGroupBlockIODeviceBandwidth *a = NULL, *b;
368 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
369 if (path_equal(path, b->path) && read == b->read) {
376 a = new0(CGroupBlockIODeviceBandwidth, 1);
381 a->path = strdup(path);
387 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, a);
398 r = sd_bus_message_exit_container(message);
402 if (mode != UNIT_CHECK) {
403 CGroupBlockIODeviceBandwidth *a, *next;
404 _cleanup_free_ char *buf = NULL;
405 _cleanup_fclose_ FILE *f = NULL;
409 LIST_FOREACH_SAFE(device_bandwidths, a, next, c->blockio_device_bandwidths)
411 cgroup_context_free_blockio_device_bandwidth(c, a);
414 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
416 f = open_memstream(&buf, &size);
421 fputs("BlockIOReadBandwidth=\n", f);
422 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
424 fprintf(f, "BlockIOReadBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
426 fputs("BlockIOWriteBandwidth=\n", f);
427 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
429 fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
433 unit_write_drop_in_private(u, mode, name, buf);
438 } else if (streq(name, "BlockIODeviceWeight")) {
443 r = sd_bus_message_enter_container(message, 'a', "(st)");
447 while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
448 unsigned long ul = u64;
450 if (ul < 10 || ul > 1000)
451 return sd_bus_error_set_errnof(error, EINVAL, "BlockIODeviceWeight out of range");
453 if (mode != UNIT_CHECK) {
454 CGroupBlockIODeviceWeight *a = NULL, *b;
456 LIST_FOREACH(device_weights, b, c->blockio_device_weights) {
457 if (path_equal(b->path, path)) {
464 a = new0(CGroupBlockIODeviceWeight, 1);
468 a->path = strdup(path);
473 LIST_PREPEND(device_weights,c->blockio_device_weights, a);
482 r = sd_bus_message_exit_container(message);
486 if (mode != UNIT_CHECK) {
487 _cleanup_free_ char *buf = NULL;
488 _cleanup_fclose_ FILE *f = NULL;
489 CGroupBlockIODeviceWeight *a;
493 while (c->blockio_device_weights)
494 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
497 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
499 f = open_memstream(&buf, &size);
503 fputs("BlockIODeviceWeight=\n", f);
504 LIST_FOREACH(device_weights, a, c->blockio_device_weights)
505 fprintf(f, "BlockIODeviceWeight=%s %lu\n", a->path, a->weight);
508 unit_write_drop_in_private(u, mode, name, buf);
513 } else if (streq(name, "MemoryAccounting")) {
516 r = sd_bus_message_read(message, "b", &b);
520 if (mode != UNIT_CHECK) {
521 c->memory_accounting = b;
522 u->cgroup_realized_mask &= ~CGROUP_MEMORY;
523 unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no");
528 } else if (streq(name, "MemoryLimit")) {
531 r = sd_bus_message_read(message, "t", &limit);
535 if (mode != UNIT_CHECK) {
536 c->memory_limit = limit;
537 u->cgroup_realized_mask &= ~CGROUP_MEMORY;
538 unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit);
543 } else if (streq(name, "DevicePolicy")) {
545 CGroupDevicePolicy p;
547 r = sd_bus_message_read(message, "s", &policy);
551 p = cgroup_device_policy_from_string(policy);
555 if (mode != UNIT_CHECK) {
558 c->device_policy = p;
559 u->cgroup_realized_mask &= ~CGROUP_DEVICE;
561 buf = strappenda("DevicePolicy=", policy);
562 unit_write_drop_in_private(u, mode, name, buf);
567 } else if (streq(name, "DeviceAllow")) {
568 const char *path, *rwm;
571 r = sd_bus_message_enter_container(message, 'a', "(ss)");
575 while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) {
577 if ((!startswith(path, "/dev/") &&
578 !startswith(path, "block-") &&
579 !startswith(path, "char-")) ||
580 strpbrk(path, WHITESPACE))
581 return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires device node");
586 if (!in_charset(rwm, "rwm"))
587 return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires combination of rwm flags");
589 if (mode != UNIT_CHECK) {
590 CGroupDeviceAllow *a = NULL, *b;
592 LIST_FOREACH(device_allow, b, c->device_allow) {
593 if (path_equal(b->path, path)) {
600 a = new0(CGroupDeviceAllow, 1);
604 a->path = strdup(path);
610 LIST_PREPEND(device_allow, c->device_allow, a);
613 a->r = !!strchr(rwm, 'r');
614 a->w = !!strchr(rwm, 'w');
615 a->m = !!strchr(rwm, 'm');
623 r = sd_bus_message_exit_container(message);
627 if (mode != UNIT_CHECK) {
628 _cleanup_free_ char *buf = NULL;
629 _cleanup_fclose_ FILE *f = NULL;
630 CGroupDeviceAllow *a;
634 while (c->device_allow)
635 cgroup_context_free_device_allow(c, c->device_allow);
638 u->cgroup_realized_mask &= ~CGROUP_DEVICE;
640 f = open_memstream(&buf, &size);
644 fputs("DeviceAllow=\n", f);
645 LIST_FOREACH(device_allow, a, c->device_allow)
646 fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
649 unit_write_drop_in_private(u, mode, name, buf);