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