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("CPUQuotaPerSecUSec", "t", property_get_cpu_quota_per_sec_usec, 0, 0),
177 SD_BUS_PROPERTY("CPUQuotaUSec", "t", property_get_cpu_quota_usec, 0, 0),
178 SD_BUS_PROPERTY("CPUQuotaPeriodUSec", "t", bus_property_get_usec, offsetof(CGroupContext, cpu_quota_period_usec), 0),
179 SD_BUS_PROPERTY("BlockIOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, blockio_accounting), 0),
180 SD_BUS_PROPERTY("BlockIOWeight", "t", bus_property_get_ulong, offsetof(CGroupContext, 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 ul = (unsigned long) u64;
230 if (ul <= 0 || (uint64_t) ul != u64)
231 return sd_bus_error_set_errnof(error, EINVAL, "CPUShares value out of range");
233 if (mode != UNIT_CHECK) {
235 u->cgroup_realized_mask &= ~CGROUP_CPU;
236 unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul);
241 } else if (streq(name, "CPUQuotaPerSecUSec")) {
244 r = sd_bus_message_read(message, "t", &u64);
249 return sd_bus_error_set_errnof(error, EINVAL, "CPUQuotaPerSecUSec value out of range");
251 if (mode != UNIT_CHECK) {
252 c->cpu_quota_per_sec_usec = u64;
253 c->cpu_quota_usec = (uint64_t) -1;
254 u->cgroup_realized_mask &= ~CGROUP_CPU;
255 unit_write_drop_in_private_format(u, mode, "CPUQuota", "CPUQuota=%0.f%%", (double) (c->cpu_quota_per_sec_usec / 10000));
260 } else if (streq(name, "CPUQuotaUSec")) {
263 r = sd_bus_message_read(message, "t", &u64);
268 return sd_bus_error_set_errnof(error, EINVAL, "CPUQuotaUSec value out of range");
270 if (mode != UNIT_CHECK) {
271 c->cpu_quota_usec = u64;
272 c->cpu_quota_per_sec_usec = (uint64_t) -1;
273 u->cgroup_realized_mask &= ~CGROUP_CPU;
274 unit_write_drop_in_private_format(u, mode, "CPUQuota", "CPUQuota=%" PRIu64 "us", u64);
279 } else if (streq(name, "CPUQuotaPeriodUSec")) {
283 r = sd_bus_message_read(message, "t", &u64);
287 if (u64 <= 0 || u64 >= (usec_t) -1)
288 return sd_bus_error_set_errnof(error, EINVAL, "CPUQuotaPeriodUSec value out of range");
290 if (mode != UNIT_CHECK) {
291 c->cpu_quota_period_usec = u64;
292 u->cgroup_realized_mask &= ~CGROUP_CPU;
293 unit_write_drop_in_private_format(u, mode, name, "CPUQuotaPeriodSec=%" PRIu64 "us", c->cpu_quota_period_usec);
298 } else if (streq(name, "BlockIOAccounting")) {
301 r = sd_bus_message_read(message, "b", &b);
305 if (mode != UNIT_CHECK) {
306 c->blockio_accounting = b;
307 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
308 unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no");
313 } else if (streq(name, "BlockIOWeight")) {
317 r = sd_bus_message_read(message, "t", &u64);
321 ul = (unsigned long) u64;
322 if (ul < 10 || ul > 1000)
323 return sd_bus_error_set_errnof(error, EINVAL, "BlockIOWeight value out of range");
325 if (mode != UNIT_CHECK) {
326 c->blockio_weight = ul;
327 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
328 unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul);
333 } else if (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth")) {
339 if (streq(name, "BlockIOWriteBandwidth"))
342 r = sd_bus_message_enter_container(message, 'a', "(st)");
346 while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
348 if (mode != UNIT_CHECK) {
349 CGroupBlockIODeviceBandwidth *a = NULL, *b;
351 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
352 if (path_equal(path, b->path) && read == b->read) {
359 a = new0(CGroupBlockIODeviceBandwidth, 1);
364 a->path = strdup(path);
370 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, a);
381 r = sd_bus_message_exit_container(message);
385 if (mode != UNIT_CHECK) {
386 CGroupBlockIODeviceBandwidth *a, *next;
387 _cleanup_free_ char *buf = NULL;
388 _cleanup_fclose_ FILE *f = NULL;
392 LIST_FOREACH_SAFE(device_bandwidths, a, next, c->blockio_device_bandwidths)
394 cgroup_context_free_blockio_device_bandwidth(c, a);
397 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
399 f = open_memstream(&buf, &size);
404 fputs("BlockIOReadBandwidth=\n", f);
405 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
407 fprintf(f, "BlockIOReadBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
409 fputs("BlockIOWriteBandwidth=\n", f);
410 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
412 fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
416 unit_write_drop_in_private(u, mode, name, buf);
421 } else if (streq(name, "BlockIODeviceWeight")) {
426 r = sd_bus_message_enter_container(message, 'a', "(st)");
430 while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
431 unsigned long ul = u64;
433 if (ul < 10 || ul > 1000)
434 return sd_bus_error_set_errnof(error, EINVAL, "BlockIODeviceWeight out of range");
436 if (mode != UNIT_CHECK) {
437 CGroupBlockIODeviceWeight *a = NULL, *b;
439 LIST_FOREACH(device_weights, b, c->blockio_device_weights) {
440 if (path_equal(b->path, path)) {
447 a = new0(CGroupBlockIODeviceWeight, 1);
451 a->path = strdup(path);
456 LIST_PREPEND(device_weights,c->blockio_device_weights, a);
465 r = sd_bus_message_exit_container(message);
469 if (mode != UNIT_CHECK) {
470 _cleanup_free_ char *buf = NULL;
471 _cleanup_fclose_ FILE *f = NULL;
472 CGroupBlockIODeviceWeight *a;
476 while (c->blockio_device_weights)
477 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
480 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
482 f = open_memstream(&buf, &size);
486 fputs("BlockIODeviceWeight=\n", f);
487 LIST_FOREACH(device_weights, a, c->blockio_device_weights)
488 fprintf(f, "BlockIODeviceWeight=%s %lu\n", a->path, a->weight);
491 unit_write_drop_in_private(u, mode, name, buf);
496 } else if (streq(name, "MemoryAccounting")) {
499 r = sd_bus_message_read(message, "b", &b);
503 if (mode != UNIT_CHECK) {
504 c->memory_accounting = b;
505 u->cgroup_realized_mask &= ~CGROUP_MEMORY;
506 unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no");
511 } else if (streq(name, "MemoryLimit")) {
514 r = sd_bus_message_read(message, "t", &limit);
518 if (mode != UNIT_CHECK) {
519 c->memory_limit = limit;
520 u->cgroup_realized_mask &= ~CGROUP_MEMORY;
521 unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit);
526 } else if (streq(name, "DevicePolicy")) {
528 CGroupDevicePolicy p;
530 r = sd_bus_message_read(message, "s", &policy);
534 p = cgroup_device_policy_from_string(policy);
538 if (mode != UNIT_CHECK) {
541 c->device_policy = p;
542 u->cgroup_realized_mask &= ~CGROUP_DEVICE;
544 buf = strappenda("DevicePolicy=", policy);
545 unit_write_drop_in_private(u, mode, name, buf);
550 } else if (streq(name, "DeviceAllow")) {
551 const char *path, *rwm;
554 r = sd_bus_message_enter_container(message, 'a', "(ss)");
558 while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) {
560 if ((!startswith(path, "/dev/") &&
561 !startswith(path, "block-") &&
562 !startswith(path, "char-")) ||
563 strpbrk(path, WHITESPACE))
564 return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires device node");
569 if (!in_charset(rwm, "rwm"))
570 return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires combination of rwm flags");
572 if (mode != UNIT_CHECK) {
573 CGroupDeviceAllow *a = NULL, *b;
575 LIST_FOREACH(device_allow, b, c->device_allow) {
576 if (path_equal(b->path, path)) {
583 a = new0(CGroupDeviceAllow, 1);
587 a->path = strdup(path);
593 LIST_PREPEND(device_allow, c->device_allow, a);
596 a->r = !!strchr(rwm, 'r');
597 a->w = !!strchr(rwm, 'w');
598 a->m = !!strchr(rwm, 'm');
606 r = sd_bus_message_exit_container(message);
610 if (mode != UNIT_CHECK) {
611 _cleanup_free_ char *buf = NULL;
612 _cleanup_fclose_ FILE *f = NULL;
613 CGroupDeviceAllow *a;
617 while (c->device_allow)
618 cgroup_context_free_device_allow(c, c->device_allow);
621 u->cgroup_realized_mask &= ~CGROUP_DEVICE;
623 f = open_memstream(&buf, &size);
627 fputs("DeviceAllow=\n", f);
628 LIST_FOREACH(device_allow, a, c->device_allow)
629 fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
632 unit_write_drop_in_private(u, mode, name, buf);