chiark / gitweb /
cgroup: add missing equals for BlockIOWeight
[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, "BlockIODeviceWeight")) {
321                 DBusMessageIter sub;
322                 unsigned n = 0;
323
324                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
325                     dbus_message_iter_get_element_type(i) != DBUS_TYPE_STRUCT)
326                         return -EINVAL;
327
328                 dbus_message_iter_recurse(i, &sub);
329                 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
330                         DBusMessageIter sub2;
331                         const char *path;
332                         uint64_t u64;
333                         unsigned long ul;
334                         CGroupBlockIODeviceWeight *a;
335
336                         dbus_message_iter_recurse(&sub, &sub2);
337
338                         if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0 ||
339                             bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &u64, false) < 0)
340                                 return -EINVAL;
341
342                         ul = (unsigned long) u64;
343                         if (ul < 10 || ul > 1000)
344                                 return -EINVAL;
345
346                         if (mode != UNIT_CHECK) {
347                                 CGroupBlockIODeviceWeight *b;
348                                 bool exist = false;
349
350                                 LIST_FOREACH(device_weights, b, c->blockio_device_weights) {
351                                         if (path_equal(b->path, path)) {
352                                                 a = b;
353                                                 exist = true;
354                                                 break;
355                                         }
356                                 }
357
358                                 if (!exist) {
359                                         a = new0(CGroupBlockIODeviceWeight, 1);
360                                         if (!a)
361                                                 return -ENOMEM;
362
363                                         a->path = strdup(path);
364                                         if (!a->path) {
365                                                 free(a);
366                                                 return -ENOMEM;
367                                         }
368                                 }
369
370                                 a->weight = ul;
371
372                                 if (!exist)
373                                         LIST_PREPEND(CGroupBlockIODeviceWeight, device_weights,
374                                                      c->blockio_device_weights, a);
375                         }
376
377                         n++;
378                         dbus_message_iter_next(&sub);
379                 }
380
381                 if (mode != UNIT_CHECK) {
382                         _cleanup_free_ char *buf = NULL;
383                         _cleanup_fclose_ FILE *f = NULL;
384                         CGroupBlockIODeviceWeight *a;
385                         size_t size = 0;
386
387                         if (n == 0) {
388                                 while (c->blockio_device_weights)
389                                         cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
390                         }
391
392                         f = open_memstream(&buf, &size);
393                         if (!f)
394                                 return -ENOMEM;
395
396                         fputs("BlockIODeviceWeight=\n", f);
397                         LIST_FOREACH(device_weights, a, c->blockio_device_weights)
398                                 fprintf(f, "BlockIODeviceWeight=%s %lu\n", a->path, a->weight);
399
400                         fflush(f);
401                         unit_write_drop_in_private(u, mode, name, buf);
402                 }
403
404                 return 1;
405
406         } else if (streq(name, "MemoryAccounting")) {
407
408                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
409                         return -EINVAL;
410
411                 if (mode != UNIT_CHECK) {
412                         dbus_bool_t b;
413                         dbus_message_iter_get_basic(i, &b);
414
415                         c->memory_accounting = b;
416                         unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no");
417                 }
418
419                 return 1;
420
421         } else if (streq(name, "MemoryLimit") || streq(name, "MemorySoftLimit")) {
422
423                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_UINT64)
424                         return -EINVAL;
425
426                 if (mode != UNIT_CHECK) {
427                         uint64_t limit;
428
429                         dbus_message_iter_get_basic(i, &limit);
430
431                         if (streq(name, "MemoryLimit"))
432                                 c->memory_limit = limit;
433                         else
434                                 c->memory_soft_limit = limit;
435
436                         unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit);
437                 }
438
439                 return 1;
440
441         } else if (streq(name, "DevicePolicy")) {
442                 const char *policy;
443                 CGroupDevicePolicy p;
444
445                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING)
446                         return -EINVAL;
447
448                 dbus_message_iter_get_basic(i, &policy);
449                 p = cgroup_device_policy_from_string(policy);
450                 if (p < 0)
451                         return -EINVAL;
452
453                 if (mode != UNIT_CHECK) {
454                         char *buf;
455
456                         c->device_policy = p;
457
458                         buf = strappenda("DevicePolicy=", policy);
459                         unit_write_drop_in_private(u, mode, name, buf);
460                 }
461
462                 return 1;
463
464         } else if (streq(name, "DeviceAllow")) {
465                 DBusMessageIter sub;
466                 unsigned n = 0;
467
468                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
469                     dbus_message_iter_get_element_type(i) != DBUS_TYPE_STRUCT)
470                         return -EINVAL;
471
472                 dbus_message_iter_recurse(i, &sub);
473                 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
474                         DBusMessageIter sub2;
475                         const char *path, *rwm;
476                         CGroupDeviceAllow *a;
477
478                         dbus_message_iter_recurse(&sub, &sub2);
479
480                         if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0 ||
481                             bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &rwm, false) < 0)
482                                 return -EINVAL;
483
484                         if (!path_startswith(path, "/dev")) {
485                                 dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "DeviceAllow= requires device node");
486                                 return -EINVAL;
487                         }
488
489                         if (isempty(rwm))
490                                 rwm = "rwm";
491
492                         if (!in_charset(rwm, "rwm")) {
493                                 dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "DeviceAllow= requires combination of rwm flags");
494                                 return -EINVAL;
495                         }
496
497                         if (mode != UNIT_CHECK) {
498                                 CGroupDeviceAllow *b;
499                                 bool exist = false;
500
501                                 LIST_FOREACH(device_allow, b, c->device_allow) {
502                                         if (path_equal(b->path, path)) {
503                                                 a = b;
504                                                 exist = true;
505                                                 break;
506                                         }
507                                 }
508
509                                 if (!exist) {
510                                         a = new0(CGroupDeviceAllow, 1);
511                                         if (!a)
512                                                 return -ENOMEM;
513
514                                         a->path = strdup(path);
515                                         if (!a->path) {
516                                                 free(a);
517                                                 return -ENOMEM;
518                                         }
519                                 }
520
521                                 a->r = !!strchr(rwm, 'r');
522                                 a->w = !!strchr(rwm, 'w');
523                                 a->m = !!strchr(rwm, 'm');
524
525                                 if (!exist)
526                                         LIST_PREPEND(CGroupDeviceAllow, device_allow, c->device_allow, a);
527                         }
528
529                         n++;
530                         dbus_message_iter_next(&sub);
531                 }
532
533                 if (mode != UNIT_CHECK) {
534                         _cleanup_free_ char *buf = NULL;
535                         _cleanup_fclose_ FILE *f = NULL;
536                         CGroupDeviceAllow *a;
537                         size_t size = 0;
538
539                         if (n == 0) {
540                                 while (c->device_allow)
541                                         cgroup_context_free_device_allow(c, c->device_allow);
542                         }
543
544                         f = open_memstream(&buf, &size);
545                         if (!f)
546                                 return -ENOMEM;
547
548                         fputs("DeviceAllow=\n", f);
549                         LIST_FOREACH(device_allow, a, c->device_allow)
550                                 fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
551
552                         fflush(f);
553                         unit_write_drop_in_private(u, mode, name, buf);
554                 }
555
556                 return 1;
557         }
558
559         return 0;
560 }