chiark / gitweb /
cgroups: simplify CPUQuota= logic
[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 static int property_get_cpu_quota_per_sec_usec(
137                 sd_bus *bus,
138                 const char *path,
139                 const char *interface,
140                 const char *property,
141                 sd_bus_message *reply,
142                 void *userdata,
143                 sd_bus_error *error) {
144
145         CGroupContext *c = userdata;
146
147         assert(bus);
148         assert(reply);
149         assert(c);
150
151         return sd_bus_message_append(reply, "t", c->cpu_quota_per_sec_usec);
152 }
153
154 static int property_get_ulong_as_u64(
155                 sd_bus *bus,
156                 const char *path,
157                 const char *interface,
158                 const char *property,
159                 sd_bus_message *reply,
160                 void *userdata,
161                 sd_bus_error *error) {
162
163         unsigned long *ul = userdata;
164
165         assert(bus);
166         assert(reply);
167         assert(ul);
168
169         return sd_bus_message_append(reply, "t", *ul == (unsigned long) -1 ? (uint64_t) -1 : (uint64_t) *ul);
170 }
171
172 const sd_bus_vtable bus_cgroup_vtable[] = {
173         SD_BUS_VTABLE_START(0),
174         SD_BUS_PROPERTY("CPUAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, cpu_accounting), 0),
175         SD_BUS_PROPERTY("CPUShares", "t", property_get_ulong_as_u64, offsetof(CGroupContext, cpu_shares), 0),
176         SD_BUS_PROPERTY("StartupCPUShares", "t", property_get_ulong_as_u64, offsetof(CGroupContext, startup_cpu_shares), 0),
177         SD_BUS_PROPERTY("CPUQuotaPerSecUSec", "t", property_get_cpu_quota_per_sec_usec, 0, 0),
178         SD_BUS_PROPERTY("BlockIOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, blockio_accounting), 0),
179         SD_BUS_PROPERTY("BlockIOWeight", "t", property_get_ulong_as_u64, offsetof(CGroupContext, blockio_weight), 0),
180         SD_BUS_PROPERTY("StartupBlockIOWeight", "t", property_get_ulong_as_u64, offsetof(CGroupContext, startup_blockio_weight), 0),
181         SD_BUS_PROPERTY("BlockIODeviceWeight", "a(st)", property_get_blockio_device_weight, 0, 0),
182         SD_BUS_PROPERTY("BlockIOReadBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
183         SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
184         SD_BUS_PROPERTY("MemoryAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, memory_accounting), 0),
185         SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0),
186         SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0),
187         SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0),
188         SD_BUS_VTABLE_END
189 };
190
191 int bus_cgroup_set_property(
192                 Unit *u,
193                 CGroupContext *c,
194                 const char *name,
195                 sd_bus_message *message,
196                 UnitSetPropertiesMode mode,
197                 sd_bus_error *error) {
198
199         int r;
200
201         assert(u);
202         assert(c);
203         assert(name);
204         assert(message);
205
206         if (streq(name, "CPUAccounting")) {
207                 int b;
208
209                 r = sd_bus_message_read(message, "b", &b);
210                 if (r < 0)
211                         return r;
212
213                 if (mode != UNIT_CHECK) {
214                         c->cpu_accounting = b;
215                         u->cgroup_realized_mask &= ~CGROUP_CPUACCT;
216                         unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no");
217                 }
218
219                 return 1;
220
221         } else if (streq(name, "CPUShares")) {
222                 uint64_t u64;
223                 unsigned long ul;
224
225                 r = sd_bus_message_read(message, "t", &u64);
226                 if (r < 0)
227                         return r;
228
229                 if (u64 == (uint64_t) -1)
230                         ul = (unsigned long) -1;
231                 else {
232                         ul = (unsigned long) u64;
233                         if (ul <= 0 || (uint64_t) ul != u64)
234                                 return sd_bus_error_set_errnof(error, EINVAL, "CPUShares value out of range");
235                 }
236
237                 if (mode != UNIT_CHECK) {
238                         c->cpu_shares = ul;
239                         u->cgroup_realized_mask &= ~CGROUP_CPU;
240                         unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul);
241                 }
242
243                 return 1;
244
245         } else if (streq(name, "StartupCPUShares")) {
246                 uint64_t u64;
247                 unsigned long ul;
248
249                 r = sd_bus_message_read(message, "t", &u64);
250                 if (r < 0)
251                         return r;
252
253                 if (u64 == (uint64_t) -1)
254                         ul = (unsigned long) -1;
255                 else {
256                         ul = (unsigned long) u64;
257                         if (ul <= 0 || (uint64_t) ul != u64)
258                                 return sd_bus_error_set_errnof(error, EINVAL, "StartupCPUShares value out of range");
259                 }
260
261                 if (mode != UNIT_CHECK) {
262                         c->startup_cpu_shares = ul;
263                         u->cgroup_realized_mask &= ~CGROUP_CPU;
264                         unit_write_drop_in_private_format(u, mode, name, "StartupCPUShares=%lu", ul);
265                 }
266
267                 return 1;
268
269         } else if (streq(name, "CPUQuotaPerSecUSec")) {
270                 uint64_t u64;
271
272                 r = sd_bus_message_read(message, "t", &u64);
273                 if (r < 0)
274                         return r;
275
276                 if (u64 <= 0)
277                         return sd_bus_error_set_errnof(error, EINVAL, "CPUQuotaPerSecUSec value out of range");
278
279                 if (mode != UNIT_CHECK) {
280                         c->cpu_quota_per_sec_usec = u64;
281                         u->cgroup_realized_mask &= ~CGROUP_CPU;
282                         unit_write_drop_in_private_format(u, mode, "CPUQuota", "CPUQuota=%0.f%%", (double) (c->cpu_quota_per_sec_usec / 10000));
283                 }
284
285                 return 1;
286
287         } else if (streq(name, "BlockIOAccounting")) {
288                 int b;
289
290                 r = sd_bus_message_read(message, "b", &b);
291                 if (r < 0)
292                         return r;
293
294                 if (mode != UNIT_CHECK) {
295                         c->blockio_accounting = b;
296                         u->cgroup_realized_mask &= ~CGROUP_BLKIO;
297                         unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no");
298                 }
299
300                 return 1;
301
302         } else if (streq(name, "BlockIOWeight")) {
303                 uint64_t u64;
304                 unsigned long ul;
305
306                 r = sd_bus_message_read(message, "t", &u64);
307                 if (r < 0)
308                         return r;
309
310                 if (u64 == (uint64_t) -1)
311                         ul = (unsigned long) -1;
312                 else  {
313                         ul = (unsigned long) u64;
314                         if (ul < 10 || ul > 1000)
315                                 return sd_bus_error_set_errnof(error, EINVAL, "BlockIOWeight value out of range");
316                 }
317
318                 if (mode != UNIT_CHECK) {
319                         c->blockio_weight = ul;
320                         u->cgroup_realized_mask &= ~CGROUP_BLKIO;
321                         unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul);
322                 }
323
324                 return 1;
325
326         } else if (streq(name, "StartupBlockIOWeight")) {
327                 uint64_t u64;
328                 unsigned long ul;
329
330                 r = sd_bus_message_read(message, "t", &u64);
331                 if (r < 0)
332                         return r;
333
334                 if (u64 == (uint64_t) -1)
335                         ul = (unsigned long) -1;
336                 else  {
337                         ul = (unsigned long) u64;
338                         if (ul < 10 || ul > 1000)
339                                 return sd_bus_error_set_errnof(error, EINVAL, "StartupBlockIOWeight value out of range");
340                 }
341
342                 if (mode != UNIT_CHECK) {
343                         c->startup_blockio_weight = ul;
344                         u->cgroup_realized_mask &= ~CGROUP_BLKIO;
345                         unit_write_drop_in_private_format(u, mode, name, "StartupBlockIOWeight=%lu", ul);
346                 }
347
348                 return 1;
349
350         } else if (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth")) {
351                 const char *path;
352                 bool read = true;
353                 unsigned n = 0;
354                 uint64_t u64;
355
356                 if (streq(name, "BlockIOWriteBandwidth"))
357                         read = false;
358
359                 r = sd_bus_message_enter_container(message, 'a', "(st)");
360                 if (r < 0)
361                         return r;
362
363                 while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
364
365                         if (mode != UNIT_CHECK) {
366                                 CGroupBlockIODeviceBandwidth *a = NULL, *b;
367
368                                 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
369                                         if (path_equal(path, b->path) && read == b->read) {
370                                                 a = b;
371                                                 break;
372                                         }
373                                 }
374
375                                 if (!a) {
376                                         a = new0(CGroupBlockIODeviceBandwidth, 1);
377                                         if (!a)
378                                                 return -ENOMEM;
379
380                                         a->read = read;
381                                         a->path = strdup(path);
382                                         if (!a->path) {
383                                                 free(a);
384                                                 return -ENOMEM;
385                                         }
386
387                                         LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, a);
388                                 }
389
390                                 a->bandwidth = u64;
391                         }
392
393                         n++;
394                 }
395                 if (r < 0)
396                         return r;
397
398                 r = sd_bus_message_exit_container(message);
399                 if (r < 0)
400                         return r;
401
402                 if (mode != UNIT_CHECK) {
403                         CGroupBlockIODeviceBandwidth *a, *next;
404                         _cleanup_free_ char *buf = NULL;
405                         _cleanup_fclose_ FILE *f = NULL;
406                         size_t size = 0;
407
408                         if (n == 0) {
409                                 LIST_FOREACH_SAFE(device_bandwidths, a, next, c->blockio_device_bandwidths)
410                                         if (a->read == read)
411                                                 cgroup_context_free_blockio_device_bandwidth(c, a);
412                         }
413
414                         u->cgroup_realized_mask &= ~CGROUP_BLKIO;
415
416                         f = open_memstream(&buf, &size);
417                         if (!f)
418                                 return -ENOMEM;
419
420                          if (read) {
421                                 fputs("BlockIOReadBandwidth=\n", f);
422                                  LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
423                                         if (a->read)
424                                                 fprintf(f, "BlockIOReadBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
425                         } else {
426                                 fputs("BlockIOWriteBandwidth=\n", f);
427                                 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
428                                         if (!a->read)
429                                                 fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
430                         }
431
432                         fflush(f);
433                         unit_write_drop_in_private(u, mode, name, buf);
434                 }
435
436                 return 1;
437
438         } else if (streq(name, "BlockIODeviceWeight")) {
439                 const char *path;
440                 uint64_t u64;
441                 unsigned n = 0;
442
443                 r = sd_bus_message_enter_container(message, 'a', "(st)");
444                 if (r < 0)
445                         return r;
446
447                 while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
448                         unsigned long ul = u64;
449
450                         if (ul < 10 || ul > 1000)
451                                 return sd_bus_error_set_errnof(error, EINVAL, "BlockIODeviceWeight out of range");
452
453                         if (mode != UNIT_CHECK) {
454                                 CGroupBlockIODeviceWeight *a = NULL, *b;
455
456                                 LIST_FOREACH(device_weights, b, c->blockio_device_weights) {
457                                         if (path_equal(b->path, path)) {
458                                                 a = b;
459                                                 break;
460                                         }
461                                 }
462
463                                 if (!a) {
464                                         a = new0(CGroupBlockIODeviceWeight, 1);
465                                         if (!a)
466                                                 return -ENOMEM;
467
468                                         a->path = strdup(path);
469                                         if (!a->path) {
470                                                 free(a);
471                                                 return -ENOMEM;
472                                         }
473                                         LIST_PREPEND(device_weights,c->blockio_device_weights, a);
474                                 }
475
476                                 a->weight = ul;
477                         }
478
479                         n++;
480                 }
481
482                 r = sd_bus_message_exit_container(message);
483                 if (r < 0)
484                         return r;
485
486                 if (mode != UNIT_CHECK) {
487                         _cleanup_free_ char *buf = NULL;
488                         _cleanup_fclose_ FILE *f = NULL;
489                         CGroupBlockIODeviceWeight *a;
490                         size_t size = 0;
491
492                         if (n == 0) {
493                                 while (c->blockio_device_weights)
494                                         cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
495                         }
496
497                         u->cgroup_realized_mask &= ~CGROUP_BLKIO;
498
499                         f = open_memstream(&buf, &size);
500                         if (!f)
501                                 return -ENOMEM;
502
503                         fputs("BlockIODeviceWeight=\n", f);
504                         LIST_FOREACH(device_weights, a, c->blockio_device_weights)
505                                 fprintf(f, "BlockIODeviceWeight=%s %lu\n", a->path, a->weight);
506
507                         fflush(f);
508                         unit_write_drop_in_private(u, mode, name, buf);
509                 }
510
511                 return 1;
512
513         } else if (streq(name, "MemoryAccounting")) {
514                 int b;
515
516                 r = sd_bus_message_read(message, "b", &b);
517                 if (r < 0)
518                         return r;
519
520                 if (mode != UNIT_CHECK) {
521                         c->memory_accounting = b;
522                         u->cgroup_realized_mask &= ~CGROUP_MEMORY;
523                         unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no");
524                 }
525
526                 return 1;
527
528         } else if (streq(name, "MemoryLimit")) {
529                 uint64_t limit;
530
531                 r = sd_bus_message_read(message, "t", &limit);
532                 if (r < 0)
533                         return r;
534
535                 if (mode != UNIT_CHECK) {
536                         c->memory_limit = limit;
537                         u->cgroup_realized_mask &= ~CGROUP_MEMORY;
538                         unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit);
539                 }
540
541                 return 1;
542
543         } else if (streq(name, "DevicePolicy")) {
544                 const char *policy;
545                 CGroupDevicePolicy p;
546
547                 r = sd_bus_message_read(message, "s", &policy);
548                 if (r < 0)
549                         return r;
550
551                 p = cgroup_device_policy_from_string(policy);
552                 if (p < 0)
553                         return -EINVAL;
554
555                 if (mode != UNIT_CHECK) {
556                         char *buf;
557
558                         c->device_policy = p;
559                         u->cgroup_realized_mask &= ~CGROUP_DEVICE;
560
561                         buf = strappenda("DevicePolicy=", policy);
562                         unit_write_drop_in_private(u, mode, name, buf);
563                 }
564
565                 return 1;
566
567         } else if (streq(name, "DeviceAllow")) {
568                 const char *path, *rwm;
569                 unsigned n = 0;
570
571                 r = sd_bus_message_enter_container(message, 'a', "(ss)");
572                 if (r < 0)
573                         return r;
574
575                 while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) {
576
577                         if ((!startswith(path, "/dev/") &&
578                              !startswith(path, "block-") &&
579                              !startswith(path, "char-")) ||
580                             strpbrk(path, WHITESPACE))
581                             return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires device node");
582
583                         if (isempty(rwm))
584                                 rwm = "rwm";
585
586                         if (!in_charset(rwm, "rwm"))
587                                 return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires combination of rwm flags");
588
589                         if (mode != UNIT_CHECK) {
590                                 CGroupDeviceAllow *a = NULL, *b;
591
592                                 LIST_FOREACH(device_allow, b, c->device_allow) {
593                                         if (path_equal(b->path, path)) {
594                                                 a = b;
595                                                 break;
596                                         }
597                                 }
598
599                                 if (!a) {
600                                         a = new0(CGroupDeviceAllow, 1);
601                                         if (!a)
602                                                 return -ENOMEM;
603
604                                         a->path = strdup(path);
605                                         if (!a->path) {
606                                                 free(a);
607                                                 return -ENOMEM;
608                                         }
609
610                                         LIST_PREPEND(device_allow, c->device_allow, a);
611                                 }
612
613                                 a->r = !!strchr(rwm, 'r');
614                                 a->w = !!strchr(rwm, 'w');
615                                 a->m = !!strchr(rwm, 'm');
616                         }
617
618                         n++;
619                 }
620                 if (r < 0)
621                         return r;
622
623                 r = sd_bus_message_exit_container(message);
624                 if (r < 0)
625                         return r;
626
627                 if (mode != UNIT_CHECK) {
628                         _cleanup_free_ char *buf = NULL;
629                         _cleanup_fclose_ FILE *f = NULL;
630                         CGroupDeviceAllow *a;
631                         size_t size = 0;
632
633                         if (n == 0) {
634                                 while (c->device_allow)
635                                         cgroup_context_free_device_allow(c, c->device_allow);
636                         }
637
638                         u->cgroup_realized_mask &= ~CGROUP_DEVICE;
639
640                         f = open_memstream(&buf, &size);
641                         if (!f)
642                                 return -ENOMEM;
643
644                         fputs("DeviceAllow=\n", f);
645                         LIST_FOREACH(device_allow, a, c->device_allow)
646                                 fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
647
648                         fflush(f);
649                         unit_write_drop_in_private(u, mode, name, buf);
650                 }
651
652                 return 1;
653         }
654
655         return 0;
656 }