chiark / gitweb /
core: add a setting to globally control the default for timer unit accuracy
[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                 r = sd_bus_message_exit_container(message);
282                 if (r < 0)
283                         return r;
284
285                 if (mode != UNIT_CHECK) {
286                         CGroupBlockIODeviceBandwidth *a, *next;
287                         _cleanup_free_ char *buf = NULL;
288                         _cleanup_fclose_ FILE *f = NULL;
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                 const char *path;
321                 uint64_t u64;
322                 unsigned n = 0;
323
324                 r = sd_bus_message_enter_container(message, 'a', "(st)");
325                 if (r < 0)
326                         return r;
327
328                 while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
329                         unsigned long ul = u64;
330
331                         if (ul < 10 || ul > 1000)
332                                 return sd_bus_error_set_errnof(error, EINVAL, "BlockIODeviceWeight out of range");
333
334                         if (mode != UNIT_CHECK) {
335                                 CGroupBlockIODeviceWeight *a = NULL, *b;
336
337                                 LIST_FOREACH(device_weights, b, c->blockio_device_weights) {
338                                         if (path_equal(b->path, path)) {
339                                                 a = b;
340                                                 break;
341                                         }
342                                 }
343
344                                 if (!a) {
345                                         a = new0(CGroupBlockIODeviceWeight, 1);
346                                         if (!a)
347                                                 return -ENOMEM;
348
349                                         a->path = strdup(path);
350                                         if (!a->path) {
351                                                 free(a);
352                                                 return -ENOMEM;
353                                         }
354                                         LIST_PREPEND(device_weights,c->blockio_device_weights, a);
355                                 }
356
357                                 a->weight = ul;
358                         }
359
360                         n++;
361                 }
362
363                 r = sd_bus_message_exit_container(message);
364                 if (r < 0)
365                         return r;
366
367                 if (mode != UNIT_CHECK) {
368                         _cleanup_free_ char *buf = NULL;
369                         _cleanup_fclose_ FILE *f = NULL;
370                         CGroupBlockIODeviceWeight *a;
371                         size_t size = 0;
372
373                         if (n == 0) {
374                                 while (c->blockio_device_weights)
375                                         cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
376                         }
377
378                         f = open_memstream(&buf, &size);
379                         if (!f)
380                                 return -ENOMEM;
381
382                         fputs("BlockIODeviceWeight=\n", f);
383                         LIST_FOREACH(device_weights, a, c->blockio_device_weights)
384                                 fprintf(f, "BlockIODeviceWeight=%s %lu\n", a->path, a->weight);
385
386                         fflush(f);
387                         unit_write_drop_in_private(u, mode, name, buf);
388                 }
389
390                 return 1;
391
392         } else if (streq(name, "MemoryAccounting")) {
393                 int b;
394
395                 r = sd_bus_message_read(message, "b", &b);
396                 if (r < 0)
397                         return r;
398
399                 if (mode != UNIT_CHECK) {
400                         c->memory_accounting = b;
401                         unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no");
402                 }
403
404                 return 1;
405
406         } else if (streq(name, "MemoryLimit")) {
407                 uint64_t limit;
408
409                 r = sd_bus_message_read(message, "t", &limit);
410                 if (r < 0)
411                         return r;
412
413                 if (mode != UNIT_CHECK) {
414                         c->memory_limit = limit;
415                         unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit);
416                 }
417
418                 return 1;
419
420         } else if (streq(name, "DevicePolicy")) {
421                 const char *policy;
422                 CGroupDevicePolicy p;
423
424                 r = sd_bus_message_read(message, "s", &policy);
425                 if (r < 0)
426                         return r;
427
428                 p = cgroup_device_policy_from_string(policy);
429                 if (p < 0)
430                         return -EINVAL;
431
432                 if (mode != UNIT_CHECK) {
433                         char *buf;
434
435                         c->device_policy = p;
436
437                         buf = strappenda("DevicePolicy=", policy);
438                         unit_write_drop_in_private(u, mode, name, buf);
439                 }
440
441                 return 1;
442
443         } else if (streq(name, "DeviceAllow")) {
444                 const char *path, *rwm;
445                 unsigned n = 0;
446
447                 r = sd_bus_message_enter_container(message, 'a', "(ss)");
448                 if (r < 0)
449                         return r;
450
451                 while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) {
452
453                         if ((!startswith(path, "/dev/") &&
454                              !startswith(path, "block-") &&
455                              !startswith(path, "char-")) ||
456                             strpbrk(path, WHITESPACE))
457                             return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires device node");
458
459                         if (isempty(rwm))
460                                 rwm = "rwm";
461
462                         if (!in_charset(rwm, "rwm"))
463                                 return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires combination of rwm flags");
464
465                         if (mode != UNIT_CHECK) {
466                                 CGroupDeviceAllow *a = NULL, *b;
467
468                                 LIST_FOREACH(device_allow, b, c->device_allow) {
469                                         if (path_equal(b->path, path)) {
470                                                 a = b;
471                                                 break;
472                                         }
473                                 }
474
475                                 if (!a) {
476                                         a = new0(CGroupDeviceAllow, 1);
477                                         if (!a)
478                                                 return -ENOMEM;
479
480                                         a->path = strdup(path);
481                                         if (!a->path) {
482                                                 free(a);
483                                                 return -ENOMEM;
484                                         }
485
486                                         LIST_PREPEND(device_allow, c->device_allow, a);
487                                 }
488
489                                 a->r = !!strchr(rwm, 'r');
490                                 a->w = !!strchr(rwm, 'w');
491                                 a->m = !!strchr(rwm, 'm');
492                         }
493
494                         n++;
495                 }
496                 if (r < 0)
497                         return r;
498
499                 r = sd_bus_message_exit_container(message);
500                 if (r < 0)
501                         return r;
502
503                 if (mode != UNIT_CHECK) {
504                         _cleanup_free_ char *buf = NULL;
505                         _cleanup_fclose_ FILE *f = NULL;
506                         CGroupDeviceAllow *a;
507                         size_t size = 0;
508
509                         if (n == 0) {
510                                 while (c->device_allow)
511                                         cgroup_context_free_device_allow(c, c->device_allow);
512                         }
513
514                         f = open_memstream(&buf, &size);
515                         if (!f)
516                                 return -ENOMEM;
517
518                         fputs("DeviceAllow=\n", f);
519                         LIST_FOREACH(device_allow, a, c->device_allow)
520                                 fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
521
522                         fflush(f);
523                         unit_write_drop_in_private(u, mode, name, buf);
524                 }
525
526                 return 1;
527         }
528
529         return 0;
530 }