chiark / gitweb /
cgroup: setup BlockIORead/WriteBandwidth in bus_cgroup_set_property
[elogind.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         { "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 };
141
142 int 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")) {
156
157                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
158                         return -EINVAL;
159
160                 if (mode != UNIT_CHECK) {
161                         dbus_bool_t b;
162                         dbus_message_iter_get_basic(i, &b);
163
164                         c->cpu_accounting = b;
165                         unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no");
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) {
184                         c->cpu_shares = ul;
185                         unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul);
186                 }
187
188                 return 1;
189
190         } else if (streq(name, "BlockIOAccounting")) {
191
192                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
193                         return -EINVAL;
194
195                 if (mode != UNIT_CHECK) {
196                         dbus_bool_t b;
197                         dbus_message_iter_get_basic(i, &b);
198
199                         c->blockio_accounting = b;
200                         unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no");
201                 }
202
203                 return 1;
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) {
219                         c->blockio_weight = ul;
220                         unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul);
221                 }
222
223                 return 1;
224
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
320         } else if (streq(name, "MemoryAccounting")) {
321
322                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
323                         return -EINVAL;
324
325                 if (mode != UNIT_CHECK) {
326                         dbus_bool_t b;
327                         dbus_message_iter_get_basic(i, &b);
328
329                         c->memory_accounting = b;
330                         unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no");
331                 }
332
333                 return 1;
334
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;
342
343                         dbus_message_iter_get_basic(i, &limit);
344
345                         if (streq(name, "MemoryLimit"))
346                                 c->memory_limit = limit;
347                         else
348                                 c->memory_soft_limit = limit;
349
350                         unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit);
351                 }
352
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);
373                         unit_write_drop_in_private(u, mode, name, buf);
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
411                         if (mode != UNIT_CHECK) {
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                                         }
433                                 }
434
435                                 a->r = !!strchr(rwm, 'r');
436                                 a->w = !!strchr(rwm, 'w');
437                                 a->m = !!strchr(rwm, 'm');
438
439                                 if (!exist)
440                                         LIST_PREPEND(CGroupDeviceAllow, device_allow, c->device_allow, a);
441                         }
442
443                         n++;
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);
467                         unit_write_drop_in_private(u, mode, name, buf);
468                 }
469
470                 return 1;
471         }
472
473         return 0;
474 }