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