]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/dbus-cgroup.c
core: add support for the "pids" cgroup controller
[thirdparty/systemd.git] / src / core / dbus-cgroup.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2013 Lennart Poettering
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include "bus-util.h"
23 #include "path-util.h"
24 #include "cgroup-util.h"
25 #include "cgroup.h"
26 #include "dbus-cgroup.h"
27
28 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_cgroup_device_policy, cgroup_device_policy, CGroupDevicePolicy);
29
30 static int property_get_blockio_device_weight(
31 sd_bus *bus,
32 const char *path,
33 const char *interface,
34 const char *property,
35 sd_bus_message *reply,
36 void *userdata,
37 sd_bus_error *error) {
38
39 CGroupContext *c = userdata;
40 CGroupBlockIODeviceWeight *w;
41 int r;
42
43 assert(bus);
44 assert(reply);
45 assert(c);
46
47 r = sd_bus_message_open_container(reply, 'a', "(st)");
48 if (r < 0)
49 return r;
50
51 LIST_FOREACH(device_weights, w, c->blockio_device_weights) {
52 r = sd_bus_message_append(reply, "(st)", w->path, w->weight);
53 if (r < 0)
54 return r;
55 }
56
57 return sd_bus_message_close_container(reply);
58 }
59
60 static int property_get_blockio_device_bandwidths(
61 sd_bus *bus,
62 const char *path,
63 const char *interface,
64 const char *property,
65 sd_bus_message *reply,
66 void *userdata,
67 sd_bus_error *error) {
68
69 CGroupContext *c = userdata;
70 CGroupBlockIODeviceBandwidth *b;
71 int r;
72
73 assert(bus);
74 assert(reply);
75 assert(c);
76
77 r = sd_bus_message_open_container(reply, 'a', "(st)");
78 if (r < 0)
79 return r;
80
81 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
82
83 if (streq(property, "BlockIOReadBandwidth") != b->read)
84 continue;
85
86 r = sd_bus_message_append(reply, "(st)", b->path, b->bandwidth);
87 if (r < 0)
88 return r;
89 }
90
91 return sd_bus_message_close_container(reply);
92 }
93
94 static int property_get_device_allow(
95 sd_bus *bus,
96 const char *path,
97 const char *interface,
98 const char *property,
99 sd_bus_message *reply,
100 void *userdata,
101 sd_bus_error *error) {
102
103 CGroupContext *c = userdata;
104 CGroupDeviceAllow *a;
105 int r;
106
107 assert(bus);
108 assert(reply);
109 assert(c);
110
111 r = sd_bus_message_open_container(reply, 'a', "(ss)");
112 if (r < 0)
113 return r;
114
115 LIST_FOREACH(device_allow, a, c->device_allow) {
116 unsigned k = 0;
117 char rwm[4];
118
119 if (a->r)
120 rwm[k++] = 'r';
121 if (a->w)
122 rwm[k++] = 'w';
123 if (a->m)
124 rwm[k++] = 'm';
125
126 rwm[k] = 0;
127
128 r = sd_bus_message_append(reply, "(ss)", a->path, rwm);
129 if (r < 0)
130 return r;
131 }
132
133 return sd_bus_message_close_container(reply);
134 }
135
136 static int property_get_ulong_as_u64(
137 sd_bus *bus,
138 const char *path,
139 const char *interface,
140 const char *property,
141 sd_bus_message *reply,
142 void *userdata,
143 sd_bus_error *error) {
144
145 unsigned long *ul = userdata;
146
147 assert(bus);
148 assert(reply);
149 assert(ul);
150
151 return sd_bus_message_append(reply, "t", *ul == (unsigned long) -1 ? (uint64_t) -1 : (uint64_t) *ul);
152 }
153
154 const sd_bus_vtable bus_cgroup_vtable[] = {
155 SD_BUS_VTABLE_START(0),
156 SD_BUS_PROPERTY("Delegate", "b", bus_property_get_bool, offsetof(CGroupContext, delegate), 0),
157 SD_BUS_PROPERTY("CPUAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, cpu_accounting), 0),
158 SD_BUS_PROPERTY("CPUShares", "t", property_get_ulong_as_u64, offsetof(CGroupContext, cpu_shares), 0),
159 SD_BUS_PROPERTY("StartupCPUShares", "t", property_get_ulong_as_u64, offsetof(CGroupContext, startup_cpu_shares), 0),
160 SD_BUS_PROPERTY("CPUQuotaPerSecUSec", "t", bus_property_get_usec, offsetof(CGroupContext, cpu_quota_per_sec_usec), 0),
161 SD_BUS_PROPERTY("BlockIOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, blockio_accounting), 0),
162 SD_BUS_PROPERTY("BlockIOWeight", "t", property_get_ulong_as_u64, offsetof(CGroupContext, blockio_weight), 0),
163 SD_BUS_PROPERTY("StartupBlockIOWeight", "t", property_get_ulong_as_u64, offsetof(CGroupContext, startup_blockio_weight), 0),
164 SD_BUS_PROPERTY("BlockIODeviceWeight", "a(st)", property_get_blockio_device_weight, 0, 0),
165 SD_BUS_PROPERTY("BlockIOReadBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
166 SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
167 SD_BUS_PROPERTY("MemoryAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, memory_accounting), 0),
168 SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0),
169 SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0),
170 SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0),
171 SD_BUS_PROPERTY("TasksAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, tasks_accounting), 0),
172 SD_BUS_PROPERTY("TasksMax", "t", NULL, offsetof(CGroupContext, tasks_max), 0),
173 SD_BUS_VTABLE_END
174 };
175
176 static int bus_cgroup_set_transient_property(
177 Unit *u,
178 CGroupContext *c,
179 const char *name,
180 sd_bus_message *message,
181 UnitSetPropertiesMode mode,
182 sd_bus_error *error) {
183
184 int r;
185
186 assert(u);
187 assert(c);
188 assert(name);
189 assert(message);
190
191 if (streq(name, "Delegate")) {
192 int b;
193
194 r = sd_bus_message_read(message, "b", &b);
195 if (r < 0)
196 return r;
197
198 if (mode != UNIT_CHECK) {
199 c->delegate = b;
200 unit_write_drop_in_private(u, mode, name, b ? "Delegate=yes" : "Delegate=no");
201 }
202
203 return 1;
204 }
205
206 return 0;
207 }
208
209 int bus_cgroup_set_property(
210 Unit *u,
211 CGroupContext *c,
212 const char *name,
213 sd_bus_message *message,
214 UnitSetPropertiesMode mode,
215 sd_bus_error *error) {
216
217 int r;
218
219 assert(u);
220 assert(c);
221 assert(name);
222 assert(message);
223
224 if (streq(name, "CPUAccounting")) {
225 int b;
226
227 r = sd_bus_message_read(message, "b", &b);
228 if (r < 0)
229 return r;
230
231 if (mode != UNIT_CHECK) {
232 c->cpu_accounting = b;
233 u->cgroup_realized_mask &= ~CGROUP_MASK_CPUACCT;
234 unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no");
235 }
236
237 return 1;
238
239 } else if (streq(name, "CPUShares")) {
240 uint64_t u64;
241 unsigned long ul;
242
243 r = sd_bus_message_read(message, "t", &u64);
244 if (r < 0)
245 return r;
246
247 if (u64 == (uint64_t) -1)
248 ul = (unsigned long) -1;
249 else {
250 ul = (unsigned long) u64;
251 if (ul <= 0 || (uint64_t) ul != u64)
252 return sd_bus_error_set_errnof(error, EINVAL, "CPUShares value out of range");
253 }
254
255 if (mode != UNIT_CHECK) {
256 c->cpu_shares = ul;
257 u->cgroup_realized_mask &= ~CGROUP_MASK_CPU;
258 unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul);
259 }
260
261 return 1;
262
263 } else if (streq(name, "StartupCPUShares")) {
264 uint64_t u64;
265 unsigned long ul;
266
267 r = sd_bus_message_read(message, "t", &u64);
268 if (r < 0)
269 return r;
270
271 if (u64 == (uint64_t) -1)
272 ul = (unsigned long) -1;
273 else {
274 ul = (unsigned long) u64;
275 if (ul <= 0 || (uint64_t) ul != u64)
276 return sd_bus_error_set_errnof(error, EINVAL, "StartupCPUShares value out of range");
277 }
278
279 if (mode != UNIT_CHECK) {
280 c->startup_cpu_shares = ul;
281 u->cgroup_realized_mask &= ~CGROUP_MASK_CPU;
282 unit_write_drop_in_private_format(u, mode, name, "StartupCPUShares=%lu", ul);
283 }
284
285 return 1;
286
287 } else if (streq(name, "CPUQuotaPerSecUSec")) {
288 uint64_t u64;
289
290 r = sd_bus_message_read(message, "t", &u64);
291 if (r < 0)
292 return r;
293
294 if (u64 <= 0)
295 return sd_bus_error_set_errnof(error, EINVAL, "CPUQuotaPerSecUSec value out of range");
296
297 if (mode != UNIT_CHECK) {
298 c->cpu_quota_per_sec_usec = u64;
299 u->cgroup_realized_mask &= ~CGROUP_MASK_CPU;
300 unit_write_drop_in_private_format(u, mode, "CPUQuota", "CPUQuota=%0.f%%", (double) (c->cpu_quota_per_sec_usec / 10000));
301 }
302
303 return 1;
304
305 } else if (streq(name, "BlockIOAccounting")) {
306 int b;
307
308 r = sd_bus_message_read(message, "b", &b);
309 if (r < 0)
310 return r;
311
312 if (mode != UNIT_CHECK) {
313 c->blockio_accounting = b;
314 u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO;
315 unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no");
316 }
317
318 return 1;
319
320 } else if (streq(name, "BlockIOWeight")) {
321 uint64_t u64;
322 unsigned long ul;
323
324 r = sd_bus_message_read(message, "t", &u64);
325 if (r < 0)
326 return r;
327
328 if (u64 == (uint64_t) -1)
329 ul = (unsigned long) -1;
330 else {
331 ul = (unsigned long) u64;
332 if (ul < 10 || ul > 1000)
333 return sd_bus_error_set_errnof(error, EINVAL, "BlockIOWeight value out of range");
334 }
335
336 if (mode != UNIT_CHECK) {
337 c->blockio_weight = ul;
338 u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO;
339 unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul);
340 }
341
342 return 1;
343
344 } else if (streq(name, "StartupBlockIOWeight")) {
345 uint64_t u64;
346 unsigned long ul;
347
348 r = sd_bus_message_read(message, "t", &u64);
349 if (r < 0)
350 return r;
351
352 if (u64 == (uint64_t) -1)
353 ul = (unsigned long) -1;
354 else {
355 ul = (unsigned long) u64;
356 if (ul < 10 || ul > 1000)
357 return sd_bus_error_set_errnof(error, EINVAL, "StartupBlockIOWeight value out of range");
358 }
359
360 if (mode != UNIT_CHECK) {
361 c->startup_blockio_weight = ul;
362 u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO;
363 unit_write_drop_in_private_format(u, mode, name, "StartupBlockIOWeight=%lu", ul);
364 }
365
366 return 1;
367
368 } else if (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth")) {
369 const char *path;
370 bool read = true;
371 unsigned n = 0;
372 uint64_t u64;
373
374 if (streq(name, "BlockIOWriteBandwidth"))
375 read = false;
376
377 r = sd_bus_message_enter_container(message, 'a', "(st)");
378 if (r < 0)
379 return r;
380
381 while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
382
383 if (mode != UNIT_CHECK) {
384 CGroupBlockIODeviceBandwidth *a = NULL, *b;
385
386 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
387 if (path_equal(path, b->path) && read == b->read) {
388 a = b;
389 break;
390 }
391 }
392
393 if (!a) {
394 a = new0(CGroupBlockIODeviceBandwidth, 1);
395 if (!a)
396 return -ENOMEM;
397
398 a->read = read;
399 a->path = strdup(path);
400 if (!a->path) {
401 free(a);
402 return -ENOMEM;
403 }
404
405 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, a);
406 }
407
408 a->bandwidth = u64;
409 }
410
411 n++;
412 }
413 if (r < 0)
414 return r;
415
416 r = sd_bus_message_exit_container(message);
417 if (r < 0)
418 return r;
419
420 if (mode != UNIT_CHECK) {
421 CGroupBlockIODeviceBandwidth *a, *next;
422 _cleanup_free_ char *buf = NULL;
423 _cleanup_fclose_ FILE *f = NULL;
424 size_t size = 0;
425
426 if (n == 0) {
427 LIST_FOREACH_SAFE(device_bandwidths, a, next, c->blockio_device_bandwidths)
428 if (a->read == read)
429 cgroup_context_free_blockio_device_bandwidth(c, a);
430 }
431
432 u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO;
433
434 f = open_memstream(&buf, &size);
435 if (!f)
436 return -ENOMEM;
437
438 if (read) {
439 fputs("BlockIOReadBandwidth=\n", f);
440 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
441 if (a->read)
442 fprintf(f, "BlockIOReadBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
443 } else {
444 fputs("BlockIOWriteBandwidth=\n", f);
445 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
446 if (!a->read)
447 fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
448 }
449
450 fflush(f);
451 unit_write_drop_in_private(u, mode, name, buf);
452 }
453
454 return 1;
455
456 } else if (streq(name, "BlockIODeviceWeight")) {
457 const char *path;
458 uint64_t u64;
459 unsigned n = 0;
460
461 r = sd_bus_message_enter_container(message, 'a', "(st)");
462 if (r < 0)
463 return r;
464
465 while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
466 unsigned long ul = u64;
467
468 if (ul < 10 || ul > 1000)
469 return sd_bus_error_set_errnof(error, EINVAL, "BlockIODeviceWeight out of range");
470
471 if (mode != UNIT_CHECK) {
472 CGroupBlockIODeviceWeight *a = NULL, *b;
473
474 LIST_FOREACH(device_weights, b, c->blockio_device_weights) {
475 if (path_equal(b->path, path)) {
476 a = b;
477 break;
478 }
479 }
480
481 if (!a) {
482 a = new0(CGroupBlockIODeviceWeight, 1);
483 if (!a)
484 return -ENOMEM;
485
486 a->path = strdup(path);
487 if (!a->path) {
488 free(a);
489 return -ENOMEM;
490 }
491 LIST_PREPEND(device_weights,c->blockio_device_weights, a);
492 }
493
494 a->weight = ul;
495 }
496
497 n++;
498 }
499
500 r = sd_bus_message_exit_container(message);
501 if (r < 0)
502 return r;
503
504 if (mode != UNIT_CHECK) {
505 _cleanup_free_ char *buf = NULL;
506 _cleanup_fclose_ FILE *f = NULL;
507 CGroupBlockIODeviceWeight *a;
508 size_t size = 0;
509
510 if (n == 0) {
511 while (c->blockio_device_weights)
512 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
513 }
514
515 u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO;
516
517 f = open_memstream(&buf, &size);
518 if (!f)
519 return -ENOMEM;
520
521 fputs("BlockIODeviceWeight=\n", f);
522 LIST_FOREACH(device_weights, a, c->blockio_device_weights)
523 fprintf(f, "BlockIODeviceWeight=%s %lu\n", a->path, a->weight);
524
525 fflush(f);
526 unit_write_drop_in_private(u, mode, name, buf);
527 }
528
529 return 1;
530
531 } else if (streq(name, "MemoryAccounting")) {
532 int b;
533
534 r = sd_bus_message_read(message, "b", &b);
535 if (r < 0)
536 return r;
537
538 if (mode != UNIT_CHECK) {
539 c->memory_accounting = b;
540 u->cgroup_realized_mask &= ~CGROUP_MASK_MEMORY;
541 unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no");
542 }
543
544 return 1;
545
546 } else if (streq(name, "MemoryLimit")) {
547 uint64_t limit;
548
549 r = sd_bus_message_read(message, "t", &limit);
550 if (r < 0)
551 return r;
552
553 if (mode != UNIT_CHECK) {
554 c->memory_limit = limit;
555 u->cgroup_realized_mask &= ~CGROUP_MASK_MEMORY;
556
557 if (limit == (uint64_t) -1)
558 unit_write_drop_in_private(u, mode, name, "MemoryLimit=infinity");
559 else
560 unit_write_drop_in_private_format(u, mode, name, "MemoryLimit=%" PRIu64, limit);
561 }
562
563 return 1;
564
565 } else if (streq(name, "DevicePolicy")) {
566 const char *policy;
567 CGroupDevicePolicy p;
568
569 r = sd_bus_message_read(message, "s", &policy);
570 if (r < 0)
571 return r;
572
573 p = cgroup_device_policy_from_string(policy);
574 if (p < 0)
575 return -EINVAL;
576
577 if (mode != UNIT_CHECK) {
578 char *buf;
579
580 c->device_policy = p;
581 u->cgroup_realized_mask &= ~CGROUP_MASK_DEVICES;
582
583 buf = strjoina("DevicePolicy=", policy);
584 unit_write_drop_in_private(u, mode, name, buf);
585 }
586
587 return 1;
588
589 } else if (streq(name, "DeviceAllow")) {
590 const char *path, *rwm;
591 unsigned n = 0;
592
593 r = sd_bus_message_enter_container(message, 'a', "(ss)");
594 if (r < 0)
595 return r;
596
597 while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) {
598
599 if ((!startswith(path, "/dev/") &&
600 !startswith(path, "block-") &&
601 !startswith(path, "char-")) ||
602 strpbrk(path, WHITESPACE))
603 return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires device node");
604
605 if (isempty(rwm))
606 rwm = "rwm";
607
608 if (!in_charset(rwm, "rwm"))
609 return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires combination of rwm flags");
610
611 if (mode != UNIT_CHECK) {
612 CGroupDeviceAllow *a = NULL, *b;
613
614 LIST_FOREACH(device_allow, b, c->device_allow) {
615 if (path_equal(b->path, path)) {
616 a = b;
617 break;
618 }
619 }
620
621 if (!a) {
622 a = new0(CGroupDeviceAllow, 1);
623 if (!a)
624 return -ENOMEM;
625
626 a->path = strdup(path);
627 if (!a->path) {
628 free(a);
629 return -ENOMEM;
630 }
631
632 LIST_PREPEND(device_allow, c->device_allow, a);
633 }
634
635 a->r = !!strchr(rwm, 'r');
636 a->w = !!strchr(rwm, 'w');
637 a->m = !!strchr(rwm, 'm');
638 }
639
640 n++;
641 }
642 if (r < 0)
643 return r;
644
645 r = sd_bus_message_exit_container(message);
646 if (r < 0)
647 return r;
648
649 if (mode != UNIT_CHECK) {
650 _cleanup_free_ char *buf = NULL;
651 _cleanup_fclose_ FILE *f = NULL;
652 CGroupDeviceAllow *a;
653 size_t size = 0;
654
655 if (n == 0) {
656 while (c->device_allow)
657 cgroup_context_free_device_allow(c, c->device_allow);
658 }
659
660 u->cgroup_realized_mask &= ~CGROUP_MASK_DEVICES;
661
662 f = open_memstream(&buf, &size);
663 if (!f)
664 return -ENOMEM;
665
666 fputs("DeviceAllow=\n", f);
667 LIST_FOREACH(device_allow, a, c->device_allow)
668 fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
669
670 fflush(f);
671 unit_write_drop_in_private(u, mode, name, buf);
672 }
673
674 return 1;
675
676 } else if (streq(name, "TasksAccounting")) {
677 int b;
678
679 r = sd_bus_message_read(message, "b", &b);
680 if (r < 0)
681 return r;
682
683 if (mode != UNIT_CHECK) {
684 c->tasks_accounting = b;
685 u->cgroup_realized_mask &= ~CGROUP_MASK_PIDS;
686 unit_write_drop_in_private(u, mode, name, b ? "TasksAccounting=yes" : "TasksAccounting=no");
687 }
688
689 return 1;
690
691 } else if (streq(name, "TasksMax")) {
692 uint64_t limit;
693
694 r = sd_bus_message_read(message, "t", &limit);
695 if (r < 0)
696 return r;
697
698 if (mode != UNIT_CHECK) {
699 c->tasks_max = limit;
700 u->cgroup_realized_mask &= ~CGROUP_MASK_PIDS;
701
702 if (limit == (uint64_t) -1)
703 unit_write_drop_in_private(u, mode, name, "TasksMax=infinity");
704 else
705 unit_write_drop_in_private_format(u, mode, name, "TasksMax=%" PRIu64, limit);
706 }
707
708 return 1;
709 }
710
711 if (u->transient && u->load_state == UNIT_STUB) {
712 r = bus_cgroup_set_transient_property(u, c, name, message, mode, error);
713 if (r != 0)
714 return r;
715
716 }
717
718 return 0;
719 }