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