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 "alloc-util.h"
24 #include "cgroup-util.h"
26 #include "dbus-cgroup.h"
29 #include "path-util.h"
31 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_cgroup_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
);
33 static int property_get_blockio_device_weight(
36 const char *interface
,
38 sd_bus_message
*reply
,
40 sd_bus_error
*error
) {
42 CGroupContext
*c
= userdata
;
43 CGroupBlockIODeviceWeight
*w
;
50 r
= sd_bus_message_open_container(reply
, 'a', "(st)");
54 LIST_FOREACH(device_weights
, w
, c
->blockio_device_weights
) {
55 r
= sd_bus_message_append(reply
, "(st)", w
->path
, w
->weight
);
60 return sd_bus_message_close_container(reply
);
63 static int property_get_blockio_device_bandwidths(
66 const char *interface
,
68 sd_bus_message
*reply
,
70 sd_bus_error
*error
) {
72 CGroupContext
*c
= userdata
;
73 CGroupBlockIODeviceBandwidth
*b
;
80 r
= sd_bus_message_open_container(reply
, 'a', "(st)");
84 LIST_FOREACH(device_bandwidths
, b
, c
->blockio_device_bandwidths
) {
86 if (streq(property
, "BlockIOReadBandwidth") != b
->read
)
89 r
= sd_bus_message_append(reply
, "(st)", b
->path
, b
->bandwidth
);
94 return sd_bus_message_close_container(reply
);
97 static int property_get_device_allow(
100 const char *interface
,
101 const char *property
,
102 sd_bus_message
*reply
,
104 sd_bus_error
*error
) {
106 CGroupContext
*c
= userdata
;
107 CGroupDeviceAllow
*a
;
114 r
= sd_bus_message_open_container(reply
, 'a', "(ss)");
118 LIST_FOREACH(device_allow
, a
, c
->device_allow
) {
131 r
= sd_bus_message_append(reply
, "(ss)", a
->path
, rwm
);
136 return sd_bus_message_close_container(reply
);
139 const sd_bus_vtable bus_cgroup_vtable
[] = {
140 SD_BUS_VTABLE_START(0),
141 SD_BUS_PROPERTY("Delegate", "b", bus_property_get_bool
, offsetof(CGroupContext
, delegate
), 0),
142 SD_BUS_PROPERTY("CPUAccounting", "b", bus_property_get_bool
, offsetof(CGroupContext
, cpu_accounting
), 0),
143 SD_BUS_PROPERTY("CPUShares", "t", NULL
, offsetof(CGroupContext
, cpu_shares
), 0),
144 SD_BUS_PROPERTY("StartupCPUShares", "t", NULL
, offsetof(CGroupContext
, startup_cpu_shares
), 0),
145 SD_BUS_PROPERTY("CPUQuotaPerSecUSec", "t", bus_property_get_usec
, offsetof(CGroupContext
, cpu_quota_per_sec_usec
), 0),
146 SD_BUS_PROPERTY("BlockIOAccounting", "b", bus_property_get_bool
, offsetof(CGroupContext
, blockio_accounting
), 0),
147 SD_BUS_PROPERTY("BlockIOWeight", "t", NULL
, offsetof(CGroupContext
, blockio_weight
), 0),
148 SD_BUS_PROPERTY("StartupBlockIOWeight", "t", NULL
, offsetof(CGroupContext
, startup_blockio_weight
), 0),
149 SD_BUS_PROPERTY("BlockIODeviceWeight", "a(st)", property_get_blockio_device_weight
, 0, 0),
150 SD_BUS_PROPERTY("BlockIOReadBandwidth", "a(st)", property_get_blockio_device_bandwidths
, 0, 0),
151 SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths
, 0, 0),
152 SD_BUS_PROPERTY("MemoryAccounting", "b", bus_property_get_bool
, offsetof(CGroupContext
, memory_accounting
), 0),
153 SD_BUS_PROPERTY("MemoryLimit", "t", NULL
, offsetof(CGroupContext
, memory_limit
), 0),
154 SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy
, offsetof(CGroupContext
, device_policy
), 0),
155 SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow
, 0, 0),
156 SD_BUS_PROPERTY("TasksAccounting", "b", bus_property_get_bool
, offsetof(CGroupContext
, tasks_accounting
), 0),
157 SD_BUS_PROPERTY("TasksMax", "t", NULL
, offsetof(CGroupContext
, tasks_max
), 0),
161 static int bus_cgroup_set_transient_property(
165 sd_bus_message
*message
,
166 UnitSetPropertiesMode mode
,
167 sd_bus_error
*error
) {
176 if (streq(name
, "Delegate")) {
179 r
= sd_bus_message_read(message
, "b", &b
);
183 if (mode
!= UNIT_CHECK
) {
185 unit_write_drop_in_private(u
, mode
, name
, b
? "Delegate=yes" : "Delegate=no");
194 int bus_cgroup_set_property(
198 sd_bus_message
*message
,
199 UnitSetPropertiesMode mode
,
200 sd_bus_error
*error
) {
209 if (streq(name
, "CPUAccounting")) {
212 r
= sd_bus_message_read(message
, "b", &b
);
216 if (mode
!= UNIT_CHECK
) {
217 c
->cpu_accounting
= b
;
218 unit_invalidate_cgroup(u
, CGROUP_MASK_CPUACCT
|CGROUP_MASK_CPU
);
219 unit_write_drop_in_private(u
, mode
, name
, b
? "CPUAccounting=yes" : "CPUAccounting=no");
224 } else if (streq(name
, "CPUShares")) {
227 r
= sd_bus_message_read(message
, "t", &shares
);
231 if (!CGROUP_CPU_SHARES_IS_OK(shares
))
232 return sd_bus_error_set_errnof(error
, EINVAL
, "CPUShares value out of range");
234 if (mode
!= UNIT_CHECK
) {
235 c
->cpu_shares
= shares
;
236 unit_invalidate_cgroup(u
, CGROUP_MASK_CPU
);
238 if (shares
== CGROUP_CPU_SHARES_INVALID
)
239 unit_write_drop_in_private(u
, mode
, name
, "CPUShares=");
241 unit_write_drop_in_private_format(u
, mode
, name
, "CPUShares=%" PRIu64
, shares
);
246 } else if (streq(name
, "StartupCPUShares")) {
249 r
= sd_bus_message_read(message
, "t", &shares
);
253 if (!CGROUP_CPU_SHARES_IS_OK(shares
))
254 return sd_bus_error_set_errnof(error
, EINVAL
, "StartupCPUShares value out of range");
256 if (mode
!= UNIT_CHECK
) {
257 c
->startup_cpu_shares
= shares
;
258 unit_invalidate_cgroup(u
, CGROUP_MASK_CPU
);
260 if (shares
== CGROUP_CPU_SHARES_INVALID
)
261 unit_write_drop_in_private(u
, mode
, name
, "StartupCPUShares=");
263 unit_write_drop_in_private_format(u
, mode
, name
, "StartupCPUShares=%" PRIu64
, shares
);
268 } else if (streq(name
, "CPUQuotaPerSecUSec")) {
271 r
= sd_bus_message_read(message
, "t", &u64
);
276 return sd_bus_error_set_errnof(error
, EINVAL
, "CPUQuotaPerSecUSec value out of range");
278 if (mode
!= UNIT_CHECK
) {
279 c
->cpu_quota_per_sec_usec
= u64
;
280 unit_invalidate_cgroup(u
, CGROUP_MASK_CPU
);
281 unit_write_drop_in_private_format(u
, mode
, "CPUQuota", "CPUQuota=%0.f%%", (double) (c
->cpu_quota_per_sec_usec
/ 10000));
286 } else if (streq(name
, "BlockIOAccounting")) {
289 r
= sd_bus_message_read(message
, "b", &b
);
293 if (mode
!= UNIT_CHECK
) {
294 c
->blockio_accounting
= b
;
295 unit_invalidate_cgroup(u
, CGROUP_MASK_BLKIO
);
296 unit_write_drop_in_private(u
, mode
, name
, b
? "BlockIOAccounting=yes" : "BlockIOAccounting=no");
301 } else if (streq(name
, "BlockIOWeight")) {
304 r
= sd_bus_message_read(message
, "t", &weight
);
308 if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight
))
309 return sd_bus_error_set_errnof(error
, EINVAL
, "BlockIOWeight value out of range");
311 if (mode
!= UNIT_CHECK
) {
312 c
->blockio_weight
= weight
;
313 unit_invalidate_cgroup(u
, CGROUP_MASK_BLKIO
);
315 if (weight
== CGROUP_BLKIO_WEIGHT_INVALID
)
316 unit_write_drop_in_private(u
, mode
, name
, "BlockIOWeight=");
318 unit_write_drop_in_private_format(u
, mode
, name
, "BlockIOWeight=%" PRIu64
, weight
);
323 } else if (streq(name
, "StartupBlockIOWeight")) {
326 r
= sd_bus_message_read(message
, "t", &weight
);
330 if (CGROUP_BLKIO_WEIGHT_IS_OK(weight
))
331 return sd_bus_error_set_errnof(error
, EINVAL
, "StartupBlockIOWeight value out of range");
333 if (mode
!= UNIT_CHECK
) {
334 c
->startup_blockio_weight
= weight
;
335 unit_invalidate_cgroup(u
, CGROUP_MASK_BLKIO
);
337 if (weight
== CGROUP_BLKIO_WEIGHT_INVALID
)
338 unit_write_drop_in_private(u
, mode
, name
, "StartupBlockIOWeight=");
340 unit_write_drop_in_private_format(u
, mode
, name
, "StartupBlockIOWeight=%" PRIu64
, weight
);
345 } else if (streq(name
, "BlockIOReadBandwidth") || streq(name
, "BlockIOWriteBandwidth")) {
351 if (streq(name
, "BlockIOWriteBandwidth"))
354 r
= sd_bus_message_enter_container(message
, 'a', "(st)");
358 while ((r
= sd_bus_message_read(message
, "(st)", &path
, &u64
)) > 0) {
360 if (mode
!= UNIT_CHECK
) {
361 CGroupBlockIODeviceBandwidth
*a
= NULL
, *b
;
363 LIST_FOREACH(device_bandwidths
, b
, c
->blockio_device_bandwidths
) {
364 if (path_equal(path
, b
->path
) && read
== b
->read
) {
371 a
= new0(CGroupBlockIODeviceBandwidth
, 1);
376 a
->path
= strdup(path
);
382 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, a
);
393 r
= sd_bus_message_exit_container(message
);
397 if (mode
!= UNIT_CHECK
) {
398 CGroupBlockIODeviceBandwidth
*a
, *next
;
399 _cleanup_free_
char *buf
= NULL
;
400 _cleanup_fclose_
FILE *f
= NULL
;
404 LIST_FOREACH_SAFE(device_bandwidths
, a
, next
, c
->blockio_device_bandwidths
)
406 cgroup_context_free_blockio_device_bandwidth(c
, a
);
409 unit_invalidate_cgroup(u
, CGROUP_MASK_BLKIO
);
411 f
= open_memstream(&buf
, &size
);
416 fputs("BlockIOReadBandwidth=\n", f
);
417 LIST_FOREACH(device_bandwidths
, a
, c
->blockio_device_bandwidths
)
419 fprintf(f
, "BlockIOReadBandwidth=%s %" PRIu64
"\n", a
->path
, a
->bandwidth
);
421 fputs("BlockIOWriteBandwidth=\n", f
);
422 LIST_FOREACH(device_bandwidths
, a
, c
->blockio_device_bandwidths
)
424 fprintf(f
, "BlockIOWriteBandwidth=%s %" PRIu64
"\n", a
->path
, a
->bandwidth
);
427 r
= fflush_and_check(f
);
430 unit_write_drop_in_private(u
, mode
, name
, buf
);
435 } else if (streq(name
, "BlockIODeviceWeight")) {
440 r
= sd_bus_message_enter_container(message
, 'a', "(st)");
444 while ((r
= sd_bus_message_read(message
, "(st)", &path
, &weight
)) > 0) {
446 if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight
) || weight
== CGROUP_BLKIO_WEIGHT_INVALID
)
447 return sd_bus_error_set_errnof(error
, EINVAL
, "BlockIODeviceWeight out of range");
449 if (mode
!= UNIT_CHECK
) {
450 CGroupBlockIODeviceWeight
*a
= NULL
, *b
;
452 LIST_FOREACH(device_weights
, b
, c
->blockio_device_weights
) {
453 if (path_equal(b
->path
, path
)) {
460 a
= new0(CGroupBlockIODeviceWeight
, 1);
464 a
->path
= strdup(path
);
469 LIST_PREPEND(device_weights
,c
->blockio_device_weights
, a
);
478 r
= sd_bus_message_exit_container(message
);
482 if (mode
!= UNIT_CHECK
) {
483 _cleanup_free_
char *buf
= NULL
;
484 _cleanup_fclose_
FILE *f
= NULL
;
485 CGroupBlockIODeviceWeight
*a
;
489 while (c
->blockio_device_weights
)
490 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
493 unit_invalidate_cgroup(u
, CGROUP_MASK_BLKIO
);
495 f
= open_memstream(&buf
, &size
);
499 fputs("BlockIODeviceWeight=\n", f
);
500 LIST_FOREACH(device_weights
, a
, c
->blockio_device_weights
)
501 fprintf(f
, "BlockIODeviceWeight=%s %" PRIu64
"\n", a
->path
, a
->weight
);
503 r
= fflush_and_check(f
);
506 unit_write_drop_in_private(u
, mode
, name
, buf
);
511 } else if (streq(name
, "MemoryAccounting")) {
514 r
= sd_bus_message_read(message
, "b", &b
);
518 if (mode
!= UNIT_CHECK
) {
519 c
->memory_accounting
= b
;
520 unit_invalidate_cgroup(u
, CGROUP_MASK_MEMORY
);
521 unit_write_drop_in_private(u
, mode
, name
, b
? "MemoryAccounting=yes" : "MemoryAccounting=no");
526 } else if (streq(name
, "MemoryLimit")) {
529 r
= sd_bus_message_read(message
, "t", &limit
);
533 if (mode
!= UNIT_CHECK
) {
534 c
->memory_limit
= limit
;
535 unit_invalidate_cgroup(u
, CGROUP_MASK_MEMORY
);
537 if (limit
== (uint64_t) -1)
538 unit_write_drop_in_private(u
, mode
, name
, "MemoryLimit=infinity");
540 unit_write_drop_in_private_format(u
, mode
, name
, "MemoryLimit=%" PRIu64
, limit
);
545 } else if (streq(name
, "DevicePolicy")) {
547 CGroupDevicePolicy p
;
549 r
= sd_bus_message_read(message
, "s", &policy
);
553 p
= cgroup_device_policy_from_string(policy
);
557 if (mode
!= UNIT_CHECK
) {
560 c
->device_policy
= p
;
561 unit_invalidate_cgroup(u
, CGROUP_MASK_DEVICES
);
563 buf
= strjoina("DevicePolicy=", policy
);
564 unit_write_drop_in_private(u
, mode
, name
, buf
);
569 } else if (streq(name
, "DeviceAllow")) {
570 const char *path
, *rwm
;
573 r
= sd_bus_message_enter_container(message
, 'a', "(ss)");
577 while ((r
= sd_bus_message_read(message
, "(ss)", &path
, &rwm
)) > 0) {
579 if ((!startswith(path
, "/dev/") &&
580 !startswith(path
, "block-") &&
581 !startswith(path
, "char-")) ||
582 strpbrk(path
, WHITESPACE
))
583 return sd_bus_error_set_errnof(error
, EINVAL
, "DeviceAllow= requires device node");
588 if (!in_charset(rwm
, "rwm"))
589 return sd_bus_error_set_errnof(error
, EINVAL
, "DeviceAllow= requires combination of rwm flags");
591 if (mode
!= UNIT_CHECK
) {
592 CGroupDeviceAllow
*a
= NULL
, *b
;
594 LIST_FOREACH(device_allow
, b
, c
->device_allow
) {
595 if (path_equal(b
->path
, path
)) {
602 a
= new0(CGroupDeviceAllow
, 1);
606 a
->path
= strdup(path
);
612 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
615 a
->r
= !!strchr(rwm
, 'r');
616 a
->w
= !!strchr(rwm
, 'w');
617 a
->m
= !!strchr(rwm
, 'm');
625 r
= sd_bus_message_exit_container(message
);
629 if (mode
!= UNIT_CHECK
) {
630 _cleanup_free_
char *buf
= NULL
;
631 _cleanup_fclose_
FILE *f
= NULL
;
632 CGroupDeviceAllow
*a
;
636 while (c
->device_allow
)
637 cgroup_context_free_device_allow(c
, c
->device_allow
);
640 unit_invalidate_cgroup(u
, CGROUP_MASK_DEVICES
);
642 f
= open_memstream(&buf
, &size
);
646 fputs("DeviceAllow=\n", f
);
647 LIST_FOREACH(device_allow
, a
, c
->device_allow
)
648 fprintf(f
, "DeviceAllow=%s %s%s%s\n", a
->path
, a
->r
? "r" : "", a
->w
? "w" : "", a
->m
? "m" : "");
650 r
= fflush_and_check(f
);
653 unit_write_drop_in_private(u
, mode
, name
, buf
);
658 } else if (streq(name
, "TasksAccounting")) {
661 r
= sd_bus_message_read(message
, "b", &b
);
665 if (mode
!= UNIT_CHECK
) {
666 c
->tasks_accounting
= b
;
667 unit_invalidate_cgroup(u
, CGROUP_MASK_PIDS
);
668 unit_write_drop_in_private(u
, mode
, name
, b
? "TasksAccounting=yes" : "TasksAccounting=no");
673 } else if (streq(name
, "TasksMax")) {
676 r
= sd_bus_message_read(message
, "t", &limit
);
680 if (mode
!= UNIT_CHECK
) {
681 c
->tasks_max
= limit
;
682 unit_invalidate_cgroup(u
, CGROUP_MASK_PIDS
);
684 if (limit
== (uint64_t) -1)
685 unit_write_drop_in_private(u
, mode
, name
, "TasksMax=infinity");
687 unit_write_drop_in_private_format(u
, mode
, name
, "TasksMax=%" PRIu64
, limit
);
693 if (u
->transient
&& u
->load_state
== UNIT_STUB
) {
694 r
= bus_cgroup_set_transient_property(u
, c
, name
, message
, mode
, error
);