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