]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/dbus-cgroup.c
Set $NOTIFY_SOCKET for control procs if NotifyAccess=all
[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("CPUAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, cpu_accounting), 0),
157 SD_BUS_PROPERTY("CPUShares", "t", property_get_ulong_as_u64, offsetof(CGroupContext, cpu_shares), 0),
158 SD_BUS_PROPERTY("StartupCPUShares", "t", property_get_ulong_as_u64, offsetof(CGroupContext, startup_cpu_shares), 0),
159 SD_BUS_PROPERTY("CPUQuotaPerSecUSec", "t", bus_property_get_usec, offsetof(CGroupContext, cpu_quota_per_sec_usec), 0),
160 SD_BUS_PROPERTY("BlockIOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, blockio_accounting), 0),
161 SD_BUS_PROPERTY("BlockIOWeight", "t", property_get_ulong_as_u64, offsetof(CGroupContext, blockio_weight), 0),
162 SD_BUS_PROPERTY("StartupBlockIOWeight", "t", property_get_ulong_as_u64, offsetof(CGroupContext, startup_blockio_weight), 0),
163 SD_BUS_PROPERTY("BlockIODeviceWeight", "a(st)", property_get_blockio_device_weight, 0, 0),
164 SD_BUS_PROPERTY("BlockIOReadBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
165 SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
166 SD_BUS_PROPERTY("MemoryAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, memory_accounting), 0),
167 SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0),
168 SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0),
169 SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0),
170 SD_BUS_VTABLE_END
171 };
172
173 int bus_cgroup_set_property(
174 Unit *u,
175 CGroupContext *c,
176 const char *name,
177 sd_bus_message *message,
178 UnitSetPropertiesMode mode,
179 sd_bus_error *error) {
180
181 int r;
182
183 assert(u);
184 assert(c);
185 assert(name);
186 assert(message);
187
188 if (streq(name, "CPUAccounting")) {
189 int b;
190
191 r = sd_bus_message_read(message, "b", &b);
192 if (r < 0)
193 return r;
194
195 if (mode != UNIT_CHECK) {
196 c->cpu_accounting = b;
197 u->cgroup_realized_mask &= ~CGROUP_CPUACCT;
198 unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no");
199 }
200
201 return 1;
202
203 } else if (streq(name, "CPUShares")) {
204 uint64_t u64;
205 unsigned long ul;
206
207 r = sd_bus_message_read(message, "t", &u64);
208 if (r < 0)
209 return r;
210
211 if (u64 == (uint64_t) -1)
212 ul = (unsigned long) -1;
213 else {
214 ul = (unsigned long) u64;
215 if (ul <= 0 || (uint64_t) ul != u64)
216 return sd_bus_error_set_errnof(error, EINVAL, "CPUShares value out of range");
217 }
218
219 if (mode != UNIT_CHECK) {
220 c->cpu_shares = ul;
221 u->cgroup_realized_mask &= ~CGROUP_CPU;
222 unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul);
223 }
224
225 return 1;
226
227 } else if (streq(name, "StartupCPUShares")) {
228 uint64_t u64;
229 unsigned long ul;
230
231 r = sd_bus_message_read(message, "t", &u64);
232 if (r < 0)
233 return r;
234
235 if (u64 == (uint64_t) -1)
236 ul = (unsigned long) -1;
237 else {
238 ul = (unsigned long) u64;
239 if (ul <= 0 || (uint64_t) ul != u64)
240 return sd_bus_error_set_errnof(error, EINVAL, "StartupCPUShares value out of range");
241 }
242
243 if (mode != UNIT_CHECK) {
244 c->startup_cpu_shares = ul;
245 u->cgroup_realized_mask &= ~CGROUP_CPU;
246 unit_write_drop_in_private_format(u, mode, name, "StartupCPUShares=%lu", ul);
247 }
248
249 return 1;
250
251 } else if (streq(name, "CPUQuotaPerSecUSec")) {
252 uint64_t u64;
253
254 r = sd_bus_message_read(message, "t", &u64);
255 if (r < 0)
256 return r;
257
258 if (u64 <= 0)
259 return sd_bus_error_set_errnof(error, EINVAL, "CPUQuotaPerSecUSec value out of range");
260
261 if (mode != UNIT_CHECK) {
262 c->cpu_quota_per_sec_usec = u64;
263 u->cgroup_realized_mask &= ~CGROUP_CPU;
264 unit_write_drop_in_private_format(u, mode, "CPUQuota", "CPUQuota=%0.f%%", (double) (c->cpu_quota_per_sec_usec / 10000));
265 }
266
267 return 1;
268
269 } else if (streq(name, "BlockIOAccounting")) {
270 int b;
271
272 r = sd_bus_message_read(message, "b", &b);
273 if (r < 0)
274 return r;
275
276 if (mode != UNIT_CHECK) {
277 c->blockio_accounting = b;
278 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
279 unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no");
280 }
281
282 return 1;
283
284 } else if (streq(name, "BlockIOWeight")) {
285 uint64_t u64;
286 unsigned long ul;
287
288 r = sd_bus_message_read(message, "t", &u64);
289 if (r < 0)
290 return r;
291
292 if (u64 == (uint64_t) -1)
293 ul = (unsigned long) -1;
294 else {
295 ul = (unsigned long) u64;
296 if (ul < 10 || ul > 1000)
297 return sd_bus_error_set_errnof(error, EINVAL, "BlockIOWeight value out of range");
298 }
299
300 if (mode != UNIT_CHECK) {
301 c->blockio_weight = ul;
302 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
303 unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul);
304 }
305
306 return 1;
307
308 } else if (streq(name, "StartupBlockIOWeight")) {
309 uint64_t u64;
310 unsigned long ul;
311
312 r = sd_bus_message_read(message, "t", &u64);
313 if (r < 0)
314 return r;
315
316 if (u64 == (uint64_t) -1)
317 ul = (unsigned long) -1;
318 else {
319 ul = (unsigned long) u64;
320 if (ul < 10 || ul > 1000)
321 return sd_bus_error_set_errnof(error, EINVAL, "StartupBlockIOWeight value out of range");
322 }
323
324 if (mode != UNIT_CHECK) {
325 c->startup_blockio_weight = ul;
326 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
327 unit_write_drop_in_private_format(u, mode, name, "StartupBlockIOWeight=%lu", ul);
328 }
329
330 return 1;
331
332 } else if (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth")) {
333 const char *path;
334 bool read = true;
335 unsigned n = 0;
336 uint64_t u64;
337
338 if (streq(name, "BlockIOWriteBandwidth"))
339 read = false;
340
341 r = sd_bus_message_enter_container(message, 'a', "(st)");
342 if (r < 0)
343 return r;
344
345 while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
346
347 if (mode != UNIT_CHECK) {
348 CGroupBlockIODeviceBandwidth *a = NULL, *b;
349
350 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
351 if (path_equal(path, b->path) && read == b->read) {
352 a = b;
353 break;
354 }
355 }
356
357 if (!a) {
358 a = new0(CGroupBlockIODeviceBandwidth, 1);
359 if (!a)
360 return -ENOMEM;
361
362 a->read = read;
363 a->path = strdup(path);
364 if (!a->path) {
365 free(a);
366 return -ENOMEM;
367 }
368
369 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, a);
370 }
371
372 a->bandwidth = u64;
373 }
374
375 n++;
376 }
377 if (r < 0)
378 return r;
379
380 r = sd_bus_message_exit_container(message);
381 if (r < 0)
382 return r;
383
384 if (mode != UNIT_CHECK) {
385 CGroupBlockIODeviceBandwidth *a, *next;
386 _cleanup_free_ char *buf = NULL;
387 _cleanup_fclose_ FILE *f = NULL;
388 size_t size = 0;
389
390 if (n == 0) {
391 LIST_FOREACH_SAFE(device_bandwidths, a, next, c->blockio_device_bandwidths)
392 if (a->read == read)
393 cgroup_context_free_blockio_device_bandwidth(c, a);
394 }
395
396 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
397
398 f = open_memstream(&buf, &size);
399 if (!f)
400 return -ENOMEM;
401
402 if (read) {
403 fputs("BlockIOReadBandwidth=\n", f);
404 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
405 if (a->read)
406 fprintf(f, "BlockIOReadBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
407 } else {
408 fputs("BlockIOWriteBandwidth=\n", f);
409 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
410 if (!a->read)
411 fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
412 }
413
414 fflush(f);
415 unit_write_drop_in_private(u, mode, name, buf);
416 }
417
418 return 1;
419
420 } else if (streq(name, "BlockIODeviceWeight")) {
421 const char *path;
422 uint64_t u64;
423 unsigned n = 0;
424
425 r = sd_bus_message_enter_container(message, 'a', "(st)");
426 if (r < 0)
427 return r;
428
429 while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
430 unsigned long ul = u64;
431
432 if (ul < 10 || ul > 1000)
433 return sd_bus_error_set_errnof(error, EINVAL, "BlockIODeviceWeight out of range");
434
435 if (mode != UNIT_CHECK) {
436 CGroupBlockIODeviceWeight *a = NULL, *b;
437
438 LIST_FOREACH(device_weights, b, c->blockio_device_weights) {
439 if (path_equal(b->path, path)) {
440 a = b;
441 break;
442 }
443 }
444
445 if (!a) {
446 a = new0(CGroupBlockIODeviceWeight, 1);
447 if (!a)
448 return -ENOMEM;
449
450 a->path = strdup(path);
451 if (!a->path) {
452 free(a);
453 return -ENOMEM;
454 }
455 LIST_PREPEND(device_weights,c->blockio_device_weights, a);
456 }
457
458 a->weight = ul;
459 }
460
461 n++;
462 }
463
464 r = sd_bus_message_exit_container(message);
465 if (r < 0)
466 return r;
467
468 if (mode != UNIT_CHECK) {
469 _cleanup_free_ char *buf = NULL;
470 _cleanup_fclose_ FILE *f = NULL;
471 CGroupBlockIODeviceWeight *a;
472 size_t size = 0;
473
474 if (n == 0) {
475 while (c->blockio_device_weights)
476 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
477 }
478
479 u->cgroup_realized_mask &= ~CGROUP_BLKIO;
480
481 f = open_memstream(&buf, &size);
482 if (!f)
483 return -ENOMEM;
484
485 fputs("BlockIODeviceWeight=\n", f);
486 LIST_FOREACH(device_weights, a, c->blockio_device_weights)
487 fprintf(f, "BlockIODeviceWeight=%s %lu\n", a->path, a->weight);
488
489 fflush(f);
490 unit_write_drop_in_private(u, mode, name, buf);
491 }
492
493 return 1;
494
495 } else if (streq(name, "MemoryAccounting")) {
496 int b;
497
498 r = sd_bus_message_read(message, "b", &b);
499 if (r < 0)
500 return r;
501
502 if (mode != UNIT_CHECK) {
503 c->memory_accounting = b;
504 u->cgroup_realized_mask &= ~CGROUP_MEMORY;
505 unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no");
506 }
507
508 return 1;
509
510 } else if (streq(name, "MemoryLimit")) {
511 uint64_t limit;
512
513 r = sd_bus_message_read(message, "t", &limit);
514 if (r < 0)
515 return r;
516
517 if (mode != UNIT_CHECK) {
518 c->memory_limit = limit;
519 u->cgroup_realized_mask &= ~CGROUP_MEMORY;
520 unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit);
521 }
522
523 return 1;
524
525 } else if (streq(name, "DevicePolicy")) {
526 const char *policy;
527 CGroupDevicePolicy p;
528
529 r = sd_bus_message_read(message, "s", &policy);
530 if (r < 0)
531 return r;
532
533 p = cgroup_device_policy_from_string(policy);
534 if (p < 0)
535 return -EINVAL;
536
537 if (mode != UNIT_CHECK) {
538 char *buf;
539
540 c->device_policy = p;
541 u->cgroup_realized_mask &= ~CGROUP_DEVICE;
542
543 buf = strappenda("DevicePolicy=", policy);
544 unit_write_drop_in_private(u, mode, name, buf);
545 }
546
547 return 1;
548
549 } else if (streq(name, "DeviceAllow")) {
550 const char *path, *rwm;
551 unsigned n = 0;
552
553 r = sd_bus_message_enter_container(message, 'a', "(ss)");
554 if (r < 0)
555 return r;
556
557 while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) {
558
559 if ((!startswith(path, "/dev/") &&
560 !startswith(path, "block-") &&
561 !startswith(path, "char-")) ||
562 strpbrk(path, WHITESPACE))
563 return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires device node");
564
565 if (isempty(rwm))
566 rwm = "rwm";
567
568 if (!in_charset(rwm, "rwm"))
569 return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires combination of rwm flags");
570
571 if (mode != UNIT_CHECK) {
572 CGroupDeviceAllow *a = NULL, *b;
573
574 LIST_FOREACH(device_allow, b, c->device_allow) {
575 if (path_equal(b->path, path)) {
576 a = b;
577 break;
578 }
579 }
580
581 if (!a) {
582 a = new0(CGroupDeviceAllow, 1);
583 if (!a)
584 return -ENOMEM;
585
586 a->path = strdup(path);
587 if (!a->path) {
588 free(a);
589 return -ENOMEM;
590 }
591
592 LIST_PREPEND(device_allow, c->device_allow, a);
593 }
594
595 a->r = !!strchr(rwm, 'r');
596 a->w = !!strchr(rwm, 'w');
597 a->m = !!strchr(rwm, 'm');
598 }
599
600 n++;
601 }
602 if (r < 0)
603 return r;
604
605 r = sd_bus_message_exit_container(message);
606 if (r < 0)
607 return r;
608
609 if (mode != UNIT_CHECK) {
610 _cleanup_free_ char *buf = NULL;
611 _cleanup_fclose_ FILE *f = NULL;
612 CGroupDeviceAllow *a;
613 size_t size = 0;
614
615 if (n == 0) {
616 while (c->device_allow)
617 cgroup_context_free_device_allow(c, c->device_allow);
618 }
619
620 u->cgroup_realized_mask &= ~CGROUP_DEVICE;
621
622 f = open_memstream(&buf, &size);
623 if (!f)
624 return -ENOMEM;
625
626 fputs("DeviceAllow=\n", f);
627 LIST_FOREACH(device_allow, a, c->device_allow)
628 fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
629
630 fflush(f);
631 unit_write_drop_in_private(u, mode, name, buf);
632 }
633
634 return 1;
635 }
636
637 return 0;
638 }