]> git.proxmox.com Git - systemd.git/blob - src/core/dbus-cgroup.c
Imported Upstream version 208
[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 <dbus/dbus.h>
23
24 #include "path-util.h"
25 #include "dbus-cgroup.h"
26
27 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_cgroup_append_device_policy, cgroup_device_policy, CGroupDevicePolicy);
28
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;
33
34 assert(i);
35 assert(property);
36 assert(c);
37
38 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(st)", &sub))
39 return -ENOMEM;
40
41 LIST_FOREACH(device_weights, w, c->blockio_device_weights) {
42
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))
47 return -ENOMEM;
48 }
49
50 if (!dbus_message_iter_close_container(i, &sub))
51 return -ENOMEM;
52
53 return 0;
54 }
55
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;
60
61 assert(i);
62 assert(property);
63 assert(c);
64
65 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(st)", &sub))
66 return -ENOMEM;
67
68 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
69
70 if (streq(property, "BlockIOReadBandwidth") != b->read)
71 continue;
72
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))
77 return -ENOMEM;
78 }
79
80 if (!dbus_message_iter_close_container(i, &sub))
81 return -ENOMEM;
82
83 return 0;
84 }
85
86 static int bus_cgroup_append_device_allow(DBusMessageIter *i, const char *property, void *data) {
87 DBusMessageIter sub, sub2;
88 CGroupContext *c = data;
89 CGroupDeviceAllow *a;
90
91 assert(i);
92 assert(property);
93 assert(c);
94
95 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(ss)", &sub))
96 return -ENOMEM;
97
98 LIST_FOREACH(device_allow, a, c->device_allow) {
99 const char *rwm;
100 char buf[4];
101 unsigned k = 0;
102
103 if (a->r)
104 buf[k++] = 'r';
105 if (a->w)
106 buf[k++] = 'w';
107 if (a->m)
108 buf[k++] = 'm';
109
110 buf[k] = 0;
111 rwm = buf;
112
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))
117 return -ENOMEM;
118 }
119
120 if (!dbus_message_iter_close_container(i, &sub))
121 return -ENOMEM;
122
123 return 0;
124 }
125
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 { "DevicePolicy", bus_cgroup_append_device_policy, "s", offsetof(CGroupContext, device_policy) },
137 { "DeviceAllow", bus_cgroup_append_device_allow, "a(ss)", 0 },
138 {}
139 };
140
141 int bus_cgroup_set_property(
142 Unit *u,
143 CGroupContext *c,
144 const char *name,
145 DBusMessageIter *i,
146 UnitSetPropertiesMode mode,
147 DBusError *error) {
148
149 assert(name);
150 assert(u);
151 assert(c);
152 assert(i);
153
154 if (streq(name, "CPUAccounting")) {
155
156 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
157 return -EINVAL;
158
159 if (mode != UNIT_CHECK) {
160 dbus_bool_t b;
161 dbus_message_iter_get_basic(i, &b);
162
163 c->cpu_accounting = b;
164 unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no");
165 }
166
167 return 1;
168
169 } else if (streq(name, "CPUShares")) {
170 uint64_t u64;
171 unsigned long ul;
172
173 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_UINT64)
174 return -EINVAL;
175
176 dbus_message_iter_get_basic(i, &u64);
177 ul = (unsigned long) u64;
178
179 if (u64 <= 0 || u64 != (uint64_t) ul)
180 return -EINVAL;
181
182 if (mode != UNIT_CHECK) {
183 c->cpu_shares = ul;
184 unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul);
185 }
186
187 return 1;
188
189 } else if (streq(name, "BlockIOAccounting")) {
190
191 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
192 return -EINVAL;
193
194 if (mode != UNIT_CHECK) {
195 dbus_bool_t b;
196 dbus_message_iter_get_basic(i, &b);
197
198 c->blockio_accounting = b;
199 unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no");
200 }
201
202 return 1;
203
204 } else if (streq(name, "BlockIOWeight")) {
205 uint64_t u64;
206 unsigned long ul;
207
208 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_UINT64)
209 return -EINVAL;
210
211 dbus_message_iter_get_basic(i, &u64);
212 ul = (unsigned long) u64;
213
214 if (u64 < 10 || u64 > 1000)
215 return -EINVAL;
216
217 if (mode != UNIT_CHECK) {
218 c->blockio_weight = ul;
219 unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul);
220 }
221
222 return 1;
223
224 } else if (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth")) {
225 DBusMessageIter sub;
226 unsigned n = 0;
227 bool read = true;
228
229 if (streq(name, "BlockIOWriteBandwidth"))
230 read = false;
231
232 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
233 dbus_message_iter_get_element_type(i) != DBUS_TYPE_STRUCT)
234 return -EINVAL;
235
236 dbus_message_iter_recurse(i, &sub);
237 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
238 DBusMessageIter sub2;
239 const char *path;
240 uint64_t u64;
241 CGroupBlockIODeviceBandwidth *a;
242
243 dbus_message_iter_recurse(&sub, &sub2);
244 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0 ||
245 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &u64, false) < 0)
246 return -EINVAL;
247
248 if (mode != UNIT_CHECK) {
249 CGroupBlockIODeviceBandwidth *b;
250 bool exist = false;
251
252 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
253 if (path_equal(path, b->path) && read == b->read) {
254 a = b;
255 exist = true;
256 break;
257 }
258 }
259
260 if (!exist) {
261 a = new0(CGroupBlockIODeviceBandwidth, 1);
262 if (!a)
263 return -ENOMEM;
264
265 a->read = read;
266 a->path = strdup(path);
267 if (!a->path) {
268 free(a);
269 return -ENOMEM;
270 }
271 }
272
273 a->bandwidth = u64;
274
275 if (!exist)
276 LIST_PREPEND(CGroupBlockIODeviceBandwidth, device_bandwidths,
277 c->blockio_device_bandwidths, a);
278 }
279
280 n++;
281 dbus_message_iter_next(&sub);
282 }
283
284 if (mode != UNIT_CHECK) {
285 _cleanup_free_ char *buf = NULL;
286 _cleanup_fclose_ FILE *f = NULL;
287 CGroupBlockIODeviceBandwidth *a;
288 CGroupBlockIODeviceBandwidth *next;
289 size_t size = 0;
290
291 if (n == 0) {
292 LIST_FOREACH_SAFE(device_bandwidths, a, next, c->blockio_device_bandwidths)
293 if (a->read == read)
294 cgroup_context_free_blockio_device_bandwidth(c, a);
295 }
296
297 f = open_memstream(&buf, &size);
298 if (!f)
299 return -ENOMEM;
300
301 if (read) {
302 fputs("BlockIOReadBandwidth=\n", f);
303 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
304 if (a->read)
305 fprintf(f, "BlockIOReadBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
306 } else {
307 fputs("BlockIOWriteBandwidth=\n", f);
308 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
309 if (!a->read)
310 fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
311 }
312
313 fflush(f);
314 unit_write_drop_in_private(u, mode, name, buf);
315 }
316
317 return 1;
318
319 } else if (streq(name, "BlockIODeviceWeight")) {
320 DBusMessageIter sub;
321 unsigned n = 0;
322
323 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
324 dbus_message_iter_get_element_type(i) != DBUS_TYPE_STRUCT)
325 return -EINVAL;
326
327 dbus_message_iter_recurse(i, &sub);
328 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
329 DBusMessageIter sub2;
330 const char *path;
331 uint64_t u64;
332 unsigned long ul;
333 CGroupBlockIODeviceWeight *a;
334
335 dbus_message_iter_recurse(&sub, &sub2);
336
337 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0 ||
338 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &u64, false) < 0)
339 return -EINVAL;
340
341 ul = (unsigned long) u64;
342 if (ul < 10 || ul > 1000)
343 return -EINVAL;
344
345 if (mode != UNIT_CHECK) {
346 CGroupBlockIODeviceWeight *b;
347 bool exist = false;
348
349 LIST_FOREACH(device_weights, b, c->blockio_device_weights) {
350 if (path_equal(b->path, path)) {
351 a = b;
352 exist = true;
353 break;
354 }
355 }
356
357 if (!exist) {
358 a = new0(CGroupBlockIODeviceWeight, 1);
359 if (!a)
360 return -ENOMEM;
361
362 a->path = strdup(path);
363 if (!a->path) {
364 free(a);
365 return -ENOMEM;
366 }
367 }
368
369 a->weight = ul;
370
371 if (!exist)
372 LIST_PREPEND(CGroupBlockIODeviceWeight, device_weights,
373 c->blockio_device_weights, a);
374 }
375
376 n++;
377 dbus_message_iter_next(&sub);
378 }
379
380 if (mode != UNIT_CHECK) {
381 _cleanup_free_ char *buf = NULL;
382 _cleanup_fclose_ FILE *f = NULL;
383 CGroupBlockIODeviceWeight *a;
384 size_t size = 0;
385
386 if (n == 0) {
387 while (c->blockio_device_weights)
388 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
389 }
390
391 f = open_memstream(&buf, &size);
392 if (!f)
393 return -ENOMEM;
394
395 fputs("BlockIODeviceWeight=\n", f);
396 LIST_FOREACH(device_weights, a, c->blockio_device_weights)
397 fprintf(f, "BlockIODeviceWeight=%s %lu\n", a->path, a->weight);
398
399 fflush(f);
400 unit_write_drop_in_private(u, mode, name, buf);
401 }
402
403 return 1;
404
405 } else if (streq(name, "MemoryAccounting")) {
406
407 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
408 return -EINVAL;
409
410 if (mode != UNIT_CHECK) {
411 dbus_bool_t b;
412 dbus_message_iter_get_basic(i, &b);
413
414 c->memory_accounting = b;
415 unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no");
416 }
417
418 return 1;
419
420 } else if (streq(name, "MemoryLimit")) {
421
422 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_UINT64)
423 return -EINVAL;
424
425 if (mode != UNIT_CHECK) {
426 uint64_t limit;
427 dbus_message_iter_get_basic(i, &limit);
428
429 c->memory_limit = limit;
430 unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit);
431 }
432
433 return 1;
434
435 } else if (streq(name, "DevicePolicy")) {
436 const char *policy;
437 CGroupDevicePolicy p;
438
439 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING)
440 return -EINVAL;
441
442 dbus_message_iter_get_basic(i, &policy);
443 p = cgroup_device_policy_from_string(policy);
444 if (p < 0)
445 return -EINVAL;
446
447 if (mode != UNIT_CHECK) {
448 char *buf;
449
450 c->device_policy = p;
451
452 buf = strappenda("DevicePolicy=", policy);
453 unit_write_drop_in_private(u, mode, name, buf);
454 }
455
456 return 1;
457
458 } else if (streq(name, "DeviceAllow")) {
459 DBusMessageIter sub;
460 unsigned n = 0;
461
462 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
463 dbus_message_iter_get_element_type(i) != DBUS_TYPE_STRUCT)
464 return -EINVAL;
465
466 dbus_message_iter_recurse(i, &sub);
467 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
468 DBusMessageIter sub2;
469 const char *path, *rwm;
470 CGroupDeviceAllow *a;
471
472 dbus_message_iter_recurse(&sub, &sub2);
473
474 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0 ||
475 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &rwm, false) < 0)
476 return -EINVAL;
477
478 if (!path_startswith(path, "/dev")) {
479 dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "DeviceAllow= requires device node");
480 return -EINVAL;
481 }
482
483 if (isempty(rwm))
484 rwm = "rwm";
485
486 if (!in_charset(rwm, "rwm")) {
487 dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "DeviceAllow= requires combination of rwm flags");
488 return -EINVAL;
489 }
490
491 if (mode != UNIT_CHECK) {
492 CGroupDeviceAllow *b;
493 bool exist = false;
494
495 LIST_FOREACH(device_allow, b, c->device_allow) {
496 if (path_equal(b->path, path)) {
497 a = b;
498 exist = true;
499 break;
500 }
501 }
502
503 if (!exist) {
504 a = new0(CGroupDeviceAllow, 1);
505 if (!a)
506 return -ENOMEM;
507
508 a->path = strdup(path);
509 if (!a->path) {
510 free(a);
511 return -ENOMEM;
512 }
513 }
514
515 a->r = !!strchr(rwm, 'r');
516 a->w = !!strchr(rwm, 'w');
517 a->m = !!strchr(rwm, 'm');
518
519 if (!exist)
520 LIST_PREPEND(CGroupDeviceAllow, device_allow, c->device_allow, a);
521 }
522
523 n++;
524 dbus_message_iter_next(&sub);
525 }
526
527 if (mode != UNIT_CHECK) {
528 _cleanup_free_ char *buf = NULL;
529 _cleanup_fclose_ FILE *f = NULL;
530 CGroupDeviceAllow *a;
531 size_t size = 0;
532
533 if (n == 0) {
534 while (c->device_allow)
535 cgroup_context_free_device_allow(c, c->device_allow);
536 }
537
538 f = open_memstream(&buf, &size);
539 if (!f)
540 return -ENOMEM;
541
542 fputs("DeviceAllow=\n", f);
543 LIST_FOREACH(device_allow, a, c->device_allow)
544 fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
545
546 fflush(f);
547 unit_write_drop_in_private(u, mode, name, buf);
548 }
549
550 return 1;
551 }
552
553 return 0;
554 }