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