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/>.
22 #include <dbus/dbus.h>
24 #include "path-util.h"
25 #include "dbus-cgroup.h"
27 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_cgroup_append_device_policy, cgroup_device_policy, CGroupDevicePolicy);
29 static int bus_cgroup_append_device_weights(DBusMessageIter *i, const char *property, void *data) {
30 DBusMessageIter sub, sub2;
31 CGroupContext *c = data;
32 CGroupBlockIODeviceWeight *w;
38 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(st)", &sub))
41 LIST_FOREACH(device_weights, w, c->blockio_device_weights) {
43 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
44 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &w->path) ||
45 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &w->weight) ||
46 !dbus_message_iter_close_container(&sub, &sub2))
50 if (!dbus_message_iter_close_container(i, &sub))
56 static int bus_cgroup_append_device_bandwidths(DBusMessageIter *i, const char *property, void *data) {
57 DBusMessageIter sub, sub2;
58 CGroupContext *c = data;
59 CGroupBlockIODeviceBandwidth *b;
65 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(st)", &sub))
68 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
70 if (streq(property, "BlockIOReadBandwidth") != b->read)
73 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
74 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &b->path) ||
75 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &b->bandwidth) ||
76 !dbus_message_iter_close_container(&sub, &sub2))
80 if (!dbus_message_iter_close_container(i, &sub))
86 static int bus_cgroup_append_device_allow(DBusMessageIter *i, const char *property, void *data) {
87 DBusMessageIter sub, sub2;
88 CGroupContext *c = data;
95 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(ss)", &sub))
98 LIST_FOREACH(device_allow, a, c->device_allow) {
113 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
114 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->path) ||
115 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &rwm) ||
116 !dbus_message_iter_close_container(&sub, &sub2))
120 if (!dbus_message_iter_close_container(i, &sub))
126 const BusProperty bus_cgroup_context_properties[] = {
127 { "CPUAccounting", bus_property_append_bool, "b", offsetof(CGroupContext, cpu_accounting) },
128 { "CPUShares", bus_property_append_ul, "t", offsetof(CGroupContext, cpu_shares) },
129 { "BlockIOAccounting", bus_property_append_bool, "b", offsetof(CGroupContext, blockio_accounting) },
130 { "BlockIOWeight", bus_property_append_ul, "t", offsetof(CGroupContext, blockio_weight) },
131 { "BlockIODeviceWeight", bus_cgroup_append_device_weights, "a(st)", 0 },
132 { "BlockIOReadBandwidth", bus_cgroup_append_device_bandwidths, "a(st)", 0 },
133 { "BlockIOWriteBandwidth", bus_cgroup_append_device_bandwidths, "a(st)", 0 },
134 { "MemoryAccounting", bus_property_append_bool, "b", offsetof(CGroupContext, memory_accounting) },
135 { "MemoryLimit", bus_property_append_uint64, "t", offsetof(CGroupContext, memory_limit) },
136 { "MemorySoftLimit", bus_property_append_uint64, "t", offsetof(CGroupContext, memory_soft_limit) },
137 { "DevicePolicy", bus_cgroup_append_device_policy, "s", offsetof(CGroupContext, device_policy) },
138 { "DeviceAllow", bus_cgroup_append_device_allow, "a(ss)", 0 },
142 int bus_cgroup_set_property(
147 UnitSetPropertiesMode mode,
155 if (streq(name, "CPUAccounting")) {
157 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
160 if (mode != UNIT_CHECK) {
162 dbus_message_iter_get_basic(i, &b);
164 c->cpu_accounting = b;
165 unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no");
170 } else if (streq(name, "CPUShares")) {
174 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_UINT64)
177 dbus_message_iter_get_basic(i, &u64);
178 ul = (unsigned long) u64;
180 if (u64 <= 0 || u64 != (uint64_t) ul)
183 if (mode != UNIT_CHECK) {
185 unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul);
190 } else if (streq(name, "BlockIOAccounting")) {
192 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
195 if (mode != UNIT_CHECK) {
197 dbus_message_iter_get_basic(i, &b);
199 c->blockio_accounting = b;
200 unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no");
205 } else if (streq(name, "BlockIOWeight")) {
209 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_UINT64)
212 dbus_message_iter_get_basic(i, &u64);
213 ul = (unsigned long) u64;
215 if (u64 < 10 || u64 > 1000)
218 if (mode != UNIT_CHECK) {
219 c->blockio_weight = ul;
220 unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul);
225 } else if (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth")) {
230 if (streq(name, "BlockIOWriteBandwidth"))
233 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
234 dbus_message_iter_get_element_type(i) != DBUS_TYPE_STRUCT)
237 dbus_message_iter_recurse(i, &sub);
238 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
239 DBusMessageIter sub2;
242 CGroupBlockIODeviceBandwidth *a;
244 dbus_message_iter_recurse(&sub, &sub2);
245 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0 ||
246 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &u64, false) < 0)
249 if (mode != UNIT_CHECK) {
250 CGroupBlockIODeviceBandwidth *b;
253 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
254 if (path_equal(path, b->path) && read == b->read) {
262 a = new0(CGroupBlockIODeviceBandwidth, 1);
267 a->path = strdup(path);
277 LIST_PREPEND(CGroupBlockIODeviceBandwidth, device_bandwidths,
278 c->blockio_device_bandwidths, a);
282 dbus_message_iter_next(&sub);
285 if (mode != UNIT_CHECK) {
286 _cleanup_free_ char *buf = NULL;
287 _cleanup_fclose_ FILE *f = NULL;
288 CGroupBlockIODeviceBandwidth *a;
289 CGroupBlockIODeviceBandwidth *next;
293 LIST_FOREACH_SAFE(device_bandwidths, a, next, c->blockio_device_bandwidths)
295 cgroup_context_free_blockio_device_bandwidth(c, a);
298 f = open_memstream(&buf, &size);
303 fputs("BlockIOReadBandwidth=\n", f);
304 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
306 fprintf(f, "BlockIOReadBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
308 fputs("BlockIOWriteBandwidth=\n", f);
309 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
311 fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
315 unit_write_drop_in_private(u, mode, name, buf);
320 } else if (streq(name, "BlockIODeviceWeight")) {
324 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
325 dbus_message_iter_get_element_type(i) != DBUS_TYPE_STRUCT)
328 dbus_message_iter_recurse(i, &sub);
329 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
330 DBusMessageIter sub2;
334 CGroupBlockIODeviceWeight *a;
336 dbus_message_iter_recurse(&sub, &sub2);
338 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0 ||
339 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &u64, false) < 0)
342 ul = (unsigned long) u64;
343 if (ul < 10 || ul > 1000)
346 if (mode != UNIT_CHECK) {
347 CGroupBlockIODeviceWeight *b;
350 LIST_FOREACH(device_weights, b, c->blockio_device_weights) {
351 if (path_equal(b->path, path)) {
359 a = new0(CGroupBlockIODeviceWeight, 1);
363 a->path = strdup(path);
373 LIST_PREPEND(CGroupBlockIODeviceWeight, device_weights,
374 c->blockio_device_weights, a);
378 dbus_message_iter_next(&sub);
381 if (mode != UNIT_CHECK) {
382 _cleanup_free_ char *buf = NULL;
383 _cleanup_fclose_ FILE *f = NULL;
384 CGroupBlockIODeviceWeight *a;
388 while (c->blockio_device_weights)
389 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
392 f = open_memstream(&buf, &size);
396 fputs("BlockIODeviceWeight=\n", f);
397 LIST_FOREACH(device_weights, a, c->blockio_device_weights)
398 fprintf(f, "BlockIODeviceWeight=%s %lu\n", a->path, a->weight);
401 unit_write_drop_in_private(u, mode, name, buf);
406 } else if (streq(name, "MemoryAccounting")) {
408 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
411 if (mode != UNIT_CHECK) {
413 dbus_message_iter_get_basic(i, &b);
415 c->memory_accounting = b;
416 unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no");
421 } else if (streq(name, "MemoryLimit") || streq(name, "MemorySoftLimit")) {
423 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_UINT64)
426 if (mode != UNIT_CHECK) {
429 dbus_message_iter_get_basic(i, &limit);
431 if (streq(name, "MemoryLimit"))
432 c->memory_limit = limit;
434 c->memory_soft_limit = limit;
436 unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit);
441 } else if (streq(name, "DevicePolicy")) {
443 CGroupDevicePolicy p;
445 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING)
448 dbus_message_iter_get_basic(i, &policy);
449 p = cgroup_device_policy_from_string(policy);
453 if (mode != UNIT_CHECK) {
456 c->device_policy = p;
458 buf = strappenda("DevicePolicy=", policy);
459 unit_write_drop_in_private(u, mode, name, buf);
464 } else if (streq(name, "DeviceAllow")) {
468 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
469 dbus_message_iter_get_element_type(i) != DBUS_TYPE_STRUCT)
472 dbus_message_iter_recurse(i, &sub);
473 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
474 DBusMessageIter sub2;
475 const char *path, *rwm;
476 CGroupDeviceAllow *a;
478 dbus_message_iter_recurse(&sub, &sub2);
480 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0 ||
481 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &rwm, false) < 0)
484 if (!path_startswith(path, "/dev")) {
485 dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "DeviceAllow= requires device node");
492 if (!in_charset(rwm, "rwm")) {
493 dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "DeviceAllow= requires combination of rwm flags");
497 if (mode != UNIT_CHECK) {
498 CGroupDeviceAllow *b;
501 LIST_FOREACH(device_allow, b, c->device_allow) {
502 if (path_equal(b->path, path)) {
510 a = new0(CGroupDeviceAllow, 1);
514 a->path = strdup(path);
521 a->r = !!strchr(rwm, 'r');
522 a->w = !!strchr(rwm, 'w');
523 a->m = !!strchr(rwm, 'm');
526 LIST_PREPEND(CGroupDeviceAllow, device_allow, c->device_allow, a);
530 dbus_message_iter_next(&sub);
533 if (mode != UNIT_CHECK) {
534 _cleanup_free_ char *buf = NULL;
535 _cleanup_fclose_ FILE *f = NULL;
536 CGroupDeviceAllow *a;
540 while (c->device_allow)
541 cgroup_context_free_device_allow(c, c->device_allow);
544 f = open_memstream(&buf, &size);
548 fputs("DeviceAllow=\n", f);
549 LIST_FOREACH(device_allow, a, c->device_allow)
550 fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
553 unit_write_drop_in_private(u, mode, name, buf);