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