chiark / gitweb /
event: implement quit handlers
[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         { "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 }