chiark / gitweb /
bus: introduce "trusted" bus concept and encode access control in object vtables
[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 "bus-util.h"
23 #include "path-util.h"
24 #include "cgroup-util.h"
25 #include "cgroup.h"
26 #include "dbus-cgroup.h"
27
28 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_cgroup_device_policy, cgroup_device_policy, CGroupDevicePolicy);
29
30 static int property_get_blockio_device_weight(
31                 sd_bus *bus,
32                 const char *path,
33                 const char *interface,
34                 const char *property,
35                 sd_bus_message *reply,
36                 void *userdata,
37                 sd_bus_error *error) {
38
39         CGroupContext *c = userdata;
40         CGroupBlockIODeviceWeight *w;
41         int r;
42
43         assert(bus);
44         assert(reply);
45         assert(c);
46
47         r = sd_bus_message_open_container(reply, 'a', "(st)");
48         if (r < 0)
49                 return r;
50
51         LIST_FOREACH(device_weights, w, c->blockio_device_weights) {
52                 r = sd_bus_message_append(reply, "(st)", w->path, w->weight);
53                 if (r < 0)
54                         return r;
55         }
56
57         return sd_bus_message_close_container(reply);
58 }
59
60 static int property_get_blockio_device_bandwidths(
61                 sd_bus *bus,
62                 const char *path,
63                 const char *interface,
64                 const char *property,
65                 sd_bus_message *reply,
66                 void *userdata,
67                 sd_bus_error *error) {
68
69         CGroupContext *c = userdata;
70         CGroupBlockIODeviceBandwidth *b;
71         int r;
72
73         assert(bus);
74         assert(reply);
75         assert(c);
76
77         r = sd_bus_message_open_container(reply, 'a', "(st)");
78         if (r < 0)
79                 return r;
80
81         LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
82
83                 if (streq(property, "BlockIOReadBandwidth") != b->read)
84                         continue;
85
86                 r = sd_bus_message_append(reply, "(st)", b->path, b->bandwidth);
87                 if (r < 0)
88                         return r;
89         }
90
91         return sd_bus_message_close_container(reply);
92 }
93
94 static int property_get_device_allow(
95                 sd_bus *bus,
96                 const char *path,
97                 const char *interface,
98                 const char *property,
99                 sd_bus_message *reply,
100                 void *userdata,
101                 sd_bus_error *error) {
102
103         CGroupContext *c = userdata;
104         CGroupDeviceAllow *a;
105         int r;
106
107         assert(bus);
108         assert(reply);
109         assert(c);
110
111         r = sd_bus_message_open_container(reply, 'a', "(ss)");
112         if (r < 0)
113                 return r;
114
115         LIST_FOREACH(device_allow, a, c->device_allow) {
116                 unsigned k = 0;
117                 char rwm[4];
118
119                 if (a->r)
120                         rwm[k++] = 'r';
121                 if (a->w)
122                         rwm[k++] = 'w';
123                 if (a->m)
124                         rwm[k++] = 'm';
125
126                 rwm[k] = 0;
127
128                 r = sd_bus_message_append(reply, "(ss)", a->path, rwm);
129                 if (r < 0)
130                         return r;
131         }
132
133         return sd_bus_message_close_container(reply);
134 }
135
136 const sd_bus_vtable bus_cgroup_vtable[] = {
137         SD_BUS_VTABLE_START(0),
138         SD_BUS_PROPERTY("CPUAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, cpu_accounting), 0),
139         SD_BUS_PROPERTY("CPUShares", "t", bus_property_get_ulong, offsetof(CGroupContext, cpu_shares), 0),
140         SD_BUS_PROPERTY("BlockIOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, blockio_accounting), 0),
141         SD_BUS_PROPERTY("BlockIOWeight", "t", bus_property_get_ulong, offsetof(CGroupContext, blockio_weight), 0),
142         SD_BUS_PROPERTY("BlockIODeviceWeight", "a(st)", property_get_blockio_device_weight, 0, 0),
143         SD_BUS_PROPERTY("BlockIOReadBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
144         SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
145         SD_BUS_PROPERTY("MemoryAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, memory_accounting), 0),
146         SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0),
147         SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0),
148         SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0),
149         SD_BUS_VTABLE_END
150 };
151
152 int bus_cgroup_set_property(
153                 Unit *u,
154                 CGroupContext *c,
155                 const char *name,
156                 sd_bus_message *message,
157                 UnitSetPropertiesMode mode,
158                 sd_bus_error *error) {
159
160         int r;
161
162         assert(u);
163         assert(c);
164         assert(name);
165         assert(message);
166
167         if (streq(name, "CPUAccounting")) {
168                 int b;
169
170                 r = sd_bus_message_read(message, "b", &b);
171                 if (r < 0)
172                         return r;
173
174                 if (mode != UNIT_CHECK) {
175                         c->cpu_accounting = b;
176                         unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no");
177                 }
178
179                 return 1;
180
181         } else if (streq(name, "CPUShares")) {
182                 uint64_t u64;
183                 unsigned long ul;
184
185                 r = sd_bus_message_read(message, "t", &u64);
186                 if (r < 0)
187                         return r;
188
189                 ul = (unsigned long) u64;
190                 if (ul <= 0 || (uint64_t) ul != u64)
191                         return sd_bus_error_set_errnof(error, EINVAL, "CPUShares value out of range");
192
193                 if (mode != UNIT_CHECK) {
194                         c->cpu_shares = ul;
195                         unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul);
196                 }
197
198                 return 1;
199
200         } else if (streq(name, "BlockIOAccounting")) {
201                 int b;
202
203                 r = sd_bus_message_read(message, "b", &b);
204                 if (r < 0)
205                         return r;
206
207                 if (mode != UNIT_CHECK) {
208                         c->blockio_accounting = b;
209                         unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no");
210                 }
211
212                 return 1;
213
214         } else if (streq(name, "BlockIOWeight")) {
215                 uint64_t u64;
216                 unsigned long ul;
217
218                 r = sd_bus_message_read(message, "t", &u64);
219                 if (r < 0)
220                         return r;
221
222                 ul = (unsigned long) u64;
223                 if (ul < 10 || ul > 1000)
224                         return sd_bus_error_set_errnof(error, EINVAL, "BlockIOWeight value out of range");
225
226                 if (mode != UNIT_CHECK) {
227                         c->blockio_weight = ul;
228                         unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul);
229                 }
230
231                 return 1;
232
233         } else if (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth")) {
234                 const char *path;
235                 bool read = true;
236                 unsigned n = 0;
237                 uint64_t u64;
238
239                 if (streq(name, "BlockIOWriteBandwidth"))
240                         read = false;
241
242                 r = sd_bus_message_enter_container(message, 'a', "(st)");
243                 if (r < 0)
244                         return r;
245
246                 while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
247
248                         if (mode != UNIT_CHECK) {
249                                 CGroupBlockIODeviceBandwidth *a = NULL, *b;
250
251                                 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
252                                         if (path_equal(path, b->path) && read == b->read) {
253                                                 a = b;
254                                                 break;
255                                         }
256                                 }
257
258                                 if (!a) {
259                                         a = new0(CGroupBlockIODeviceBandwidth, 1);
260                                         if (!a)
261                                                 return -ENOMEM;
262
263                                         a->read = read;
264                                         a->path = strdup(path);
265                                         if (!a->path) {
266                                                 free(a);
267                                                 return -ENOMEM;
268                                         }
269
270                                         LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, a);
271                                 }
272
273                                 a->bandwidth = u64;
274                         }
275
276                         n++;
277                 }
278                 if (r < 0)
279                         return r;
280
281                 if (mode != UNIT_CHECK) {
282                         CGroupBlockIODeviceBandwidth *a, *next;
283                         _cleanup_free_ char *buf = NULL;
284                         _cleanup_fclose_ FILE *f = NULL;
285                         size_t size = 0;
286
287                         if (n == 0) {
288                                 LIST_FOREACH_SAFE(device_bandwidths, a, next, c->blockio_device_bandwidths)
289                                         if (a->read == read)
290                                                 cgroup_context_free_blockio_device_bandwidth(c, a);
291                         }
292
293                         f = open_memstream(&buf, &size);
294                         if (!f)
295                                 return -ENOMEM;
296
297                          if (read) {
298                                 fputs("BlockIOReadBandwidth=\n", f);
299                                  LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
300                                         if (a->read)
301                                                 fprintf(f, "BlockIOReadBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
302                         } else {
303                                 fputs("BlockIOWriteBandwidth=\n", f);
304                                 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
305                                         if (!a->read)
306                                                 fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
307                         }
308
309                         fflush(f);
310                         unit_write_drop_in_private(u, mode, name, buf);
311                 }
312
313                 return 1;
314
315         } else if (streq(name, "BlockIODeviceWeight")) {
316                 const char *path;
317                 uint64_t u64;
318                 unsigned n = 0;
319
320                 r = sd_bus_message_enter_container(message, 'a', "(st)");
321                 if (r < 0)
322                         return r;
323
324                 while (( r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
325                         unsigned long ul;
326
327                         ul = (unsigned long) u64;
328                         if (ul < 10 || ul > 1000)
329                                 return sd_bus_error_set_errnof(error, EINVAL, "BlockIODeviceWeight out of range");
330
331                         if (mode != UNIT_CHECK) {
332                                 CGroupBlockIODeviceWeight *a = NULL, *b;
333
334                                 LIST_FOREACH(device_weights, b, c->blockio_device_weights) {
335                                         if (path_equal(b->path, path)) {
336                                                 a = b;
337                                                 break;
338                                         }
339                                 }
340
341                                 if (!a) {
342                                         a = new0(CGroupBlockIODeviceWeight, 1);
343                                         if (!a)
344                                                 return -ENOMEM;
345
346                                         a->path = strdup(path);
347                                         if (!a->path) {
348                                                 free(a);
349                                                 return -ENOMEM;
350                                         }
351                                         LIST_PREPEND(device_weights,c->blockio_device_weights, a);
352                                 }
353
354                                 a->weight = ul;
355                         }
356
357                         n++;
358                 }
359
360                 if (mode != UNIT_CHECK) {
361                         _cleanup_free_ char *buf = NULL;
362                         _cleanup_fclose_ FILE *f = NULL;
363                         CGroupBlockIODeviceWeight *a;
364                         size_t size = 0;
365
366                         if (n == 0) {
367                                 while (c->blockio_device_weights)
368                                         cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
369                         }
370
371                         f = open_memstream(&buf, &size);
372                         if (!f)
373                                 return -ENOMEM;
374
375                         fputs("BlockIODeviceWeight=\n", f);
376                         LIST_FOREACH(device_weights, a, c->blockio_device_weights)
377                                 fprintf(f, "BlockIODeviceWeight=%s %lu\n", a->path, a->weight);
378
379                         fflush(f);
380                         unit_write_drop_in_private(u, mode, name, buf);
381                 }
382
383                 return 1;
384
385         } else if (streq(name, "MemoryAccounting")) {
386                 int b;
387
388                 r = sd_bus_message_read(message, "b", &b);
389                 if (r < 0)
390                         return r;
391
392                 if (mode != UNIT_CHECK) {
393                         c->memory_accounting = b;
394                         unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no");
395                 }
396
397                 return 1;
398
399         } else if (streq(name, "MemoryLimit")) {
400                 uint64_t limit;
401
402                 r = sd_bus_message_read(message, "t", &limit);
403                 if (r < 0)
404                         return r;
405
406                 if (mode != UNIT_CHECK) {
407                         c->memory_limit = limit;
408                         unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit);
409                 }
410
411                 return 1;
412
413         } else if (streq(name, "DevicePolicy")) {
414                 const char *policy;
415                 CGroupDevicePolicy p;
416
417                 r = sd_bus_message_read(message, "s", &policy);
418                 if (r < 0)
419                         return r;
420
421                 p = cgroup_device_policy_from_string(policy);
422                 if (p < 0)
423                         return -EINVAL;
424
425                 if (mode != UNIT_CHECK) {
426                         char *buf;
427
428                         c->device_policy = p;
429
430                         buf = strappenda("DevicePolicy=", policy);
431                         unit_write_drop_in_private(u, mode, name, buf);
432                 }
433
434                 return 1;
435
436         } else if (streq(name, "DeviceAllow")) {
437                 const char *path, *rwm;
438                 unsigned n = 0;
439
440                 r = sd_bus_message_enter_container(message, 'a', "(ss)");
441                 if (r < 0)
442                         return r;
443
444                 while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) {
445
446                         if (!path_startswith(path, "/dev"))
447                                 return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires device node");
448
449                         if (isempty(rwm))
450                                 rwm = "rwm";
451
452                         if (!in_charset(rwm, "rwm"))
453                                 return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires combination of rwm flags");
454
455                         if (mode != UNIT_CHECK) {
456                                 CGroupDeviceAllow *a = NULL, *b;
457
458                                 LIST_FOREACH(device_allow, b, c->device_allow) {
459                                         if (path_equal(b->path, path)) {
460                                                 a = b;
461                                                 break;
462                                         }
463                                 }
464
465                                 if (!a) {
466                                         a = new0(CGroupDeviceAllow, 1);
467                                         if (!a)
468                                                 return -ENOMEM;
469
470                                         a->path = strdup(path);
471                                         if (!a->path) {
472                                                 free(a);
473                                                 return -ENOMEM;
474                                         }
475
476                                         LIST_PREPEND(device_allow, c->device_allow, a);
477                                 }
478
479                                 a->r = !!strchr(rwm, 'r');
480                                 a->w = !!strchr(rwm, 'w');
481                                 a->m = !!strchr(rwm, 'm');
482
483                         }
484
485                         n++;
486                 }
487
488                 if (mode != UNIT_CHECK) {
489                         _cleanup_free_ char *buf = NULL;
490                         _cleanup_fclose_ FILE *f = NULL;
491                         CGroupDeviceAllow *a;
492                         size_t size = 0;
493
494                         if (n == 0) {
495                                 while (c->device_allow)
496                                         cgroup_context_free_device_allow(c, c->device_allow);
497                         }
498
499                         f = open_memstream(&buf, &size);
500                         if (!f)
501                                 return -ENOMEM;
502
503                         fputs("DeviceAllow=\n", f);
504                         LIST_FOREACH(device_allow, a, c->device_allow)
505                                 fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
506
507                         fflush(f);
508                         unit_write_drop_in_private(u, mode, name, buf);
509                 }
510
511                 return 1;
512         }
513
514         return 0;
515 }