chiark / gitweb /
core: make sure we always write changed cgroup attributes to the cgroupfs
[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                         u->cgroup_realized_mask &= ~CGROUP_CPUACCT;
177                         unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no");
178                 }
179
180                 return 1;
181
182         } else if (streq(name, "CPUShares")) {
183                 uint64_t u64;
184                 unsigned long ul;
185
186                 r = sd_bus_message_read(message, "t", &u64);
187                 if (r < 0)
188                         return r;
189
190                 ul = (unsigned long) u64;
191                 if (ul <= 0 || (uint64_t) ul != u64)
192                         return sd_bus_error_set_errnof(error, EINVAL, "CPUShares value out of range");
193
194                 if (mode != UNIT_CHECK) {
195                         c->cpu_shares = ul;
196                         u->cgroup_realized_mask &= ~CGROUP_CPU;
197                         unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul);
198                 }
199
200                 return 1;
201
202         } else if (streq(name, "BlockIOAccounting")) {
203                 int b;
204
205                 r = sd_bus_message_read(message, "b", &b);
206                 if (r < 0)
207                         return r;
208
209                 if (mode != UNIT_CHECK) {
210                         c->blockio_accounting = b;
211                         u->cgroup_realized_mask &= ~CGROUP_BLKIO;
212                         unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no");
213                 }
214
215                 return 1;
216
217         } else if (streq(name, "BlockIOWeight")) {
218                 uint64_t u64;
219                 unsigned long ul;
220
221                 r = sd_bus_message_read(message, "t", &u64);
222                 if (r < 0)
223                         return r;
224
225                 ul = (unsigned long) u64;
226                 if (ul < 10 || ul > 1000)
227                         return sd_bus_error_set_errnof(error, EINVAL, "BlockIOWeight value out of range");
228
229                 if (mode != UNIT_CHECK) {
230                         c->blockio_weight = ul;
231                         u->cgroup_realized_mask &= ~CGROUP_BLKIO;
232                         unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul);
233                 }
234
235                 return 1;
236
237         } else if (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth")) {
238                 const char *path;
239                 bool read = true;
240                 unsigned n = 0;
241                 uint64_t u64;
242
243                 if (streq(name, "BlockIOWriteBandwidth"))
244                         read = false;
245
246                 r = sd_bus_message_enter_container(message, 'a', "(st)");
247                 if (r < 0)
248                         return r;
249
250                 while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
251
252                         if (mode != UNIT_CHECK) {
253                                 CGroupBlockIODeviceBandwidth *a = NULL, *b;
254
255                                 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
256                                         if (path_equal(path, b->path) && read == b->read) {
257                                                 a = b;
258                                                 break;
259                                         }
260                                 }
261
262                                 if (!a) {
263                                         a = new0(CGroupBlockIODeviceBandwidth, 1);
264                                         if (!a)
265                                                 return -ENOMEM;
266
267                                         a->read = read;
268                                         a->path = strdup(path);
269                                         if (!a->path) {
270                                                 free(a);
271                                                 return -ENOMEM;
272                                         }
273
274                                         LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, a);
275                                 }
276
277                                 a->bandwidth = u64;
278                         }
279
280                         n++;
281                 }
282                 if (r < 0)
283                         return r;
284
285                 r = sd_bus_message_exit_container(message);
286                 if (r < 0)
287                         return r;
288
289                 if (mode != UNIT_CHECK) {
290                         CGroupBlockIODeviceBandwidth *a, *next;
291                         _cleanup_free_ char *buf = NULL;
292                         _cleanup_fclose_ FILE *f = NULL;
293                         size_t size = 0;
294
295                         if (n == 0) {
296                                 LIST_FOREACH_SAFE(device_bandwidths, a, next, c->blockio_device_bandwidths)
297                                         if (a->read == read)
298                                                 cgroup_context_free_blockio_device_bandwidth(c, a);
299                         }
300
301                         u->cgroup_realized_mask &= ~CGROUP_BLKIO;
302
303                         f = open_memstream(&buf, &size);
304                         if (!f)
305                                 return -ENOMEM;
306
307                          if (read) {
308                                 fputs("BlockIOReadBandwidth=\n", f);
309                                  LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
310                                         if (a->read)
311                                                 fprintf(f, "BlockIOReadBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
312                         } else {
313                                 fputs("BlockIOWriteBandwidth=\n", f);
314                                 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
315                                         if (!a->read)
316                                                 fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
317                         }
318
319                         fflush(f);
320                         unit_write_drop_in_private(u, mode, name, buf);
321                 }
322
323                 return 1;
324
325         } else if (streq(name, "BlockIODeviceWeight")) {
326                 const char *path;
327                 uint64_t u64;
328                 unsigned n = 0;
329
330                 r = sd_bus_message_enter_container(message, 'a', "(st)");
331                 if (r < 0)
332                         return r;
333
334                 while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
335                         unsigned long ul = u64;
336
337                         if (ul < 10 || ul > 1000)
338                                 return sd_bus_error_set_errnof(error, EINVAL, "BlockIODeviceWeight out of range");
339
340                         if (mode != UNIT_CHECK) {
341                                 CGroupBlockIODeviceWeight *a = NULL, *b;
342
343                                 LIST_FOREACH(device_weights, b, c->blockio_device_weights) {
344                                         if (path_equal(b->path, path)) {
345                                                 a = b;
346                                                 break;
347                                         }
348                                 }
349
350                                 if (!a) {
351                                         a = new0(CGroupBlockIODeviceWeight, 1);
352                                         if (!a)
353                                                 return -ENOMEM;
354
355                                         a->path = strdup(path);
356                                         if (!a->path) {
357                                                 free(a);
358                                                 return -ENOMEM;
359                                         }
360                                         LIST_PREPEND(device_weights,c->blockio_device_weights, a);
361                                 }
362
363                                 a->weight = ul;
364                         }
365
366                         n++;
367                 }
368
369                 r = sd_bus_message_exit_container(message);
370                 if (r < 0)
371                         return r;
372
373                 if (mode != UNIT_CHECK) {
374                         _cleanup_free_ char *buf = NULL;
375                         _cleanup_fclose_ FILE *f = NULL;
376                         CGroupBlockIODeviceWeight *a;
377                         size_t size = 0;
378
379                         if (n == 0) {
380                                 while (c->blockio_device_weights)
381                                         cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
382                         }
383
384                         u->cgroup_realized_mask &= ~CGROUP_BLKIO;
385
386                         f = open_memstream(&buf, &size);
387                         if (!f)
388                                 return -ENOMEM;
389
390                         fputs("BlockIODeviceWeight=\n", f);
391                         LIST_FOREACH(device_weights, a, c->blockio_device_weights)
392                                 fprintf(f, "BlockIODeviceWeight=%s %lu\n", a->path, a->weight);
393
394                         fflush(f);
395                         unit_write_drop_in_private(u, mode, name, buf);
396                 }
397
398                 return 1;
399
400         } else if (streq(name, "MemoryAccounting")) {
401                 int b;
402
403                 r = sd_bus_message_read(message, "b", &b);
404                 if (r < 0)
405                         return r;
406
407                 if (mode != UNIT_CHECK) {
408                         c->memory_accounting = b;
409                         u->cgroup_realized_mask &= ~CGROUP_MEMORY;
410                         unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no");
411                 }
412
413                 return 1;
414
415         } else if (streq(name, "MemoryLimit")) {
416                 uint64_t limit;
417
418                 r = sd_bus_message_read(message, "t", &limit);
419                 if (r < 0)
420                         return r;
421
422                 if (mode != UNIT_CHECK) {
423                         c->memory_limit = limit;
424                         u->cgroup_realized_mask &= ~CGROUP_MEMORY;
425                         unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit);
426                 }
427
428                 return 1;
429
430         } else if (streq(name, "DevicePolicy")) {
431                 const char *policy;
432                 CGroupDevicePolicy p;
433
434                 r = sd_bus_message_read(message, "s", &policy);
435                 if (r < 0)
436                         return r;
437
438                 p = cgroup_device_policy_from_string(policy);
439                 if (p < 0)
440                         return -EINVAL;
441
442                 if (mode != UNIT_CHECK) {
443                         char *buf;
444
445                         c->device_policy = p;
446                         u->cgroup_realized_mask &= ~CGROUP_DEVICE;
447
448                         buf = strappenda("DevicePolicy=", policy);
449                         unit_write_drop_in_private(u, mode, name, buf);
450                 }
451
452                 return 1;
453
454         } else if (streq(name, "DeviceAllow")) {
455                 const char *path, *rwm;
456                 unsigned n = 0;
457
458                 r = sd_bus_message_enter_container(message, 'a', "(ss)");
459                 if (r < 0)
460                         return r;
461
462                 while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) {
463
464                         if ((!startswith(path, "/dev/") &&
465                              !startswith(path, "block-") &&
466                              !startswith(path, "char-")) ||
467                             strpbrk(path, WHITESPACE))
468                             return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires device node");
469
470                         if (isempty(rwm))
471                                 rwm = "rwm";
472
473                         if (!in_charset(rwm, "rwm"))
474                                 return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires combination of rwm flags");
475
476                         if (mode != UNIT_CHECK) {
477                                 CGroupDeviceAllow *a = NULL, *b;
478
479                                 LIST_FOREACH(device_allow, b, c->device_allow) {
480                                         if (path_equal(b->path, path)) {
481                                                 a = b;
482                                                 break;
483                                         }
484                                 }
485
486                                 if (!a) {
487                                         a = new0(CGroupDeviceAllow, 1);
488                                         if (!a)
489                                                 return -ENOMEM;
490
491                                         a->path = strdup(path);
492                                         if (!a->path) {
493                                                 free(a);
494                                                 return -ENOMEM;
495                                         }
496
497                                         LIST_PREPEND(device_allow, c->device_allow, a);
498                                 }
499
500                                 a->r = !!strchr(rwm, 'r');
501                                 a->w = !!strchr(rwm, 'w');
502                                 a->m = !!strchr(rwm, 'm');
503                         }
504
505                         n++;
506                 }
507                 if (r < 0)
508                         return r;
509
510                 r = sd_bus_message_exit_container(message);
511                 if (r < 0)
512                         return r;
513
514                 if (mode != UNIT_CHECK) {
515                         _cleanup_free_ char *buf = NULL;
516                         _cleanup_fclose_ FILE *f = NULL;
517                         CGroupDeviceAllow *a;
518                         size_t size = 0;
519
520                         if (n == 0) {
521                                 while (c->device_allow)
522                                         cgroup_context_free_device_allow(c, c->device_allow);
523                         }
524
525                         u->cgroup_realized_mask &= ~CGROUP_DEVICE;
526
527                         f = open_memstream(&buf, &size);
528                         if (!f)
529                                 return -ENOMEM;
530
531                         fputs("DeviceAllow=\n", f);
532                         LIST_FOREACH(device_allow, a, c->device_allow)
533                                 fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
534
535                         fflush(f);
536                         unit_write_drop_in_private(u, mode, name, buf);
537                 }
538
539                 return 1;
540         }
541
542         return 0;
543 }