]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/dbus-cgroup.c
systemcl: add support for setting BlockIORead/WriteBandwidth for unit
[thirdparty/systemd.git] / src / core / dbus-cgroup.c
CommitLineData
4ad49000
LP
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
7041efe9 24#include "path-util.h"
4ad49000
LP
25#include "dbus-cgroup.h"
26
27static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_cgroup_append_device_policy, cgroup_device_policy, CGroupDevicePolicy);
28
29static 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
56static 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
86static 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
126const 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 { "MemorySoftLimit", bus_property_append_uint64, "t", offsetof(CGroupContext, memory_soft_limit) },
137 { "DevicePolicy", bus_cgroup_append_device_policy, "s", offsetof(CGroupContext, device_policy) },
138 { "DeviceAllow", bus_cgroup_append_device_allow, "a(ss)", 0 },
139 {}
140};
8e2af478
LP
141
142int bus_cgroup_set_property(
143 Unit *u,
144 CGroupContext *c,
145 const char *name,
146 DBusMessageIter *i,
147 UnitSetPropertiesMode mode,
148 DBusError *error) {
149
150 assert(name);
151 assert(u);
152 assert(c);
153 assert(i);
154
155 if (streq(name, "CPUAccounting")) {
8e2af478
LP
156
157 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
158 return -EINVAL;
159
160 if (mode != UNIT_CHECK) {
b42defe3 161 dbus_bool_t b;
8e2af478
LP
162 dbus_message_iter_get_basic(i, &b);
163
164 c->cpu_accounting = b;
b9ec9359 165 unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no");
b42defe3
LP
166 }
167
168 return 1;
169
170 } else if (streq(name, "CPUShares")) {
171 uint64_t u64;
172 unsigned long ul;
173
174 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_UINT64)
175 return -EINVAL;
176
177 dbus_message_iter_get_basic(i, &u64);
178 ul = (unsigned long) u64;
179
180 if (u64 <= 0 || u64 != (uint64_t) ul)
181 return -EINVAL;
182
183 if (mode != UNIT_CHECK) {
b42defe3 184 c->cpu_shares = ul;
b9ec9359 185 unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul);
8e2af478
LP
186 }
187
188 return 1;
189
190 } else if (streq(name, "BlockIOAccounting")) {
8e2af478
LP
191
192 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
193 return -EINVAL;
194
195 if (mode != UNIT_CHECK) {
b42defe3 196 dbus_bool_t b;
8e2af478
LP
197 dbus_message_iter_get_basic(i, &b);
198
199 c->blockio_accounting = b;
b9ec9359 200 unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no");
8e2af478
LP
201 }
202
203 return 1;
b42defe3
LP
204
205 } else if (streq(name, "BlockIOWeight")) {
206 uint64_t u64;
207 unsigned long ul;
208
209 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_UINT64)
210 return -EINVAL;
211
212 dbus_message_iter_get_basic(i, &u64);
213 ul = (unsigned long) u64;
214
215 if (u64 < 10 || u64 > 1000)
216 return -EINVAL;
217
218 if (mode != UNIT_CHECK) {
f2369103 219 c->blockio_weight = ul;
b9ec9359 220 unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul);
b42defe3
LP
221 }
222
223 return 1;
224
f004c2ca
G
225 } else if (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth")) {
226 DBusMessageIter sub;
227 unsigned n = 0;
228 bool read = true;
229
230 if (streq(name, "BlockIOWriteBandwidth"))
231 read = false;
232
233 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
234 dbus_message_iter_get_element_type(i) != DBUS_TYPE_STRUCT)
235 return -EINVAL;
236
237 dbus_message_iter_recurse(i, &sub);
238 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
239 DBusMessageIter sub2;
240 const char *path;
241 uint64_t u64;
242 CGroupBlockIODeviceBandwidth *a;
243
244 dbus_message_iter_recurse(&sub, &sub2);
245 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0 ||
246 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &u64, false) < 0)
247 return -EINVAL;
248
249 if (mode != UNIT_CHECK) {
250 CGroupBlockIODeviceBandwidth *b;
251 bool exist = false;
252
253 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
254 if (path_equal(path, b->path) && read == b->read) {
255 a = b;
256 exist = true;
257 break;
258 }
259 }
260
261 if (!exist) {
262 a = new0(CGroupBlockIODeviceBandwidth, 1);
263 if (!a)
264 return -ENOMEM;
265
266 a->read = read;
267 a->path = strdup(path);
268 if (!a->path) {
269 free(a);
270 return -ENOMEM;
271 }
272 }
273
274 a->bandwidth = u64;
275
276 if (!exist)
277 LIST_PREPEND(CGroupBlockIODeviceBandwidth, device_bandwidths,
278 c->blockio_device_bandwidths, a);
279 }
280
281 n++;
282 dbus_message_iter_next(&sub);
283 }
284
285 if (mode != UNIT_CHECK) {
286 _cleanup_free_ char *buf = NULL;
287 _cleanup_fclose_ FILE *f = NULL;
288 CGroupBlockIODeviceBandwidth *a;
289 CGroupBlockIODeviceBandwidth *next;
290 size_t size = 0;
291
292 if (n == 0) {
293 LIST_FOREACH_SAFE(device_bandwidths, a, next, c->blockio_device_bandwidths)
294 if (a->read == read)
295 cgroup_context_free_blockio_device_bandwidth(c, a);
296 }
297
298 f = open_memstream(&buf, &size);
299 if (!f)
300 return -ENOMEM;
301
302 if (read) {
303 fputs("BlockIOReadBandwidth=\n", f);
304 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
305 if (a->read)
306 fprintf(f, "BlockIOReadBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
307 } else {
308 fputs("BlockIOWriteBandwidth=\n", f);
309 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
310 if (!a->read)
311 fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
312 }
313
314 fflush(f);
315 unit_write_drop_in_private(u, mode, name, buf);
316 }
317
318 return 1;
319
8e2af478 320 } else if (streq(name, "MemoryAccounting")) {
8e2af478
LP
321
322 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
323 return -EINVAL;
324
325 if (mode != UNIT_CHECK) {
b42defe3 326 dbus_bool_t b;
8e2af478
LP
327 dbus_message_iter_get_basic(i, &b);
328
b42defe3 329 c->memory_accounting = b;
b9ec9359 330 unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no");
8e2af478
LP
331 }
332
333 return 1;
8e2af478 334
b42defe3
LP
335 } else if (streq(name, "MemoryLimit") || streq(name, "MemorySoftLimit")) {
336
337 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_UINT64)
338 return -EINVAL;
339
340 if (mode != UNIT_CHECK) {
341 uint64_t limit;
b42defe3
LP
342
343 dbus_message_iter_get_basic(i, &limit);
344
b9ec9359 345 if (streq(name, "MemoryLimit"))
b42defe3 346 c->memory_limit = limit;
b9ec9359 347 else
b42defe3 348 c->memory_soft_limit = limit;
b9ec9359
LP
349
350 unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit);
b42defe3
LP
351 }
352
7041efe9
LP
353 return 1;
354
355 } else if (streq(name, "DevicePolicy")) {
356 const char *policy;
357 CGroupDevicePolicy p;
358
359 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING)
360 return -EINVAL;
361
362 dbus_message_iter_get_basic(i, &policy);
363 p = cgroup_device_policy_from_string(policy);
364 if (p < 0)
365 return -EINVAL;
366
367 if (mode != UNIT_CHECK) {
368 char *buf;
369
370 c->device_policy = p;
371
372 buf = strappenda("DevicePolicy=", policy);
b9ec9359 373 unit_write_drop_in_private(u, mode, name, buf);
7041efe9
LP
374 }
375
376 return 1;
377
378 } else if (streq(name, "DeviceAllow")) {
379 DBusMessageIter sub;
380 unsigned n = 0;
381
382 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
383 dbus_message_iter_get_element_type(i) != DBUS_TYPE_STRUCT)
384 return -EINVAL;
385
386 dbus_message_iter_recurse(i, &sub);
387 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
388 DBusMessageIter sub2;
389 const char *path, *rwm;
390 CGroupDeviceAllow *a;
391
392 dbus_message_iter_recurse(&sub, &sub2);
393
394 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0 ||
395 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &rwm, false) < 0)
396 return -EINVAL;
397
398 if (!path_startswith(path, "/dev")) {
399 dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "DeviceAllow= requires device node");
400 return -EINVAL;
401 }
402
403 if (isempty(rwm))
404 rwm = "rwm";
405
406 if (!in_charset(rwm, "rwm")) {
407 dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "DeviceAllow= requires combination of rwm flags");
408 return -EINVAL;
409 }
410
7041efe9 411 if (mode != UNIT_CHECK) {
ad7bfffd
G
412 CGroupDeviceAllow *b;
413 bool exist = false;
414
415 LIST_FOREACH(device_allow, b, c->device_allow) {
416 if (streq(b->path, path)) {
417 a = b;
418 exist = true;
419 break;
420 }
421 }
422
423 if (!exist) {
424 a = new0(CGroupDeviceAllow, 1);
425 if (!a)
426 return -ENOMEM;
427
428 a->path = strdup(path);
429 if (!a->path) {
430 free(a);
431 return -ENOMEM;
432 }
7041efe9
LP
433 }
434
435 a->r = !!strchr(rwm, 'r');
436 a->w = !!strchr(rwm, 'w');
437 a->m = !!strchr(rwm, 'm');
438
ad7bfffd
G
439 if (!exist)
440 LIST_PREPEND(CGroupDeviceAllow, device_allow, c->device_allow, a);
7041efe9
LP
441 }
442
c2756a68 443 n++;
7041efe9
LP
444 dbus_message_iter_next(&sub);
445 }
446
447 if (mode != UNIT_CHECK) {
448 _cleanup_free_ char *buf = NULL;
449 _cleanup_fclose_ FILE *f = NULL;
450 CGroupDeviceAllow *a;
451 size_t size = 0;
452
453 if (n == 0) {
454 while (c->device_allow)
455 cgroup_context_free_device_allow(c, c->device_allow);
456 }
457
458 f = open_memstream(&buf, &size);
459 if (!f)
460 return -ENOMEM;
461
462 fputs("DeviceAllow=\n", f);
463 LIST_FOREACH(device_allow, a, c->device_allow)
464 fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
465
466 fflush(f);
b9ec9359 467 unit_write_drop_in_private(u, mode, name, buf);
7041efe9
LP
468 }
469
b42defe3
LP
470 return 1;
471 }
8e2af478
LP
472
473 return 0;
474}