chiark / gitweb /
cgroup: rework startup logic
[elogind.git] / src / core / dbus-cgroup.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include "bus-util.h"
23 #include "path-util.h"
24 #include "cgroup-util.h"
25 #include "cgroup.h"
26 #include "dbus-cgroup.h"
27
28 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_cgroup_device_policy, cgroup_device_policy, CGroupDevicePolicy);
29
30 static int property_get_blockio_device_weight(
31                 sd_bus *bus,
32                 const char *path,
33                 const char *interface,
34                 const char *property,
35                 sd_bus_message *reply,
36                 void *userdata,
37                 sd_bus_error *error) {
38
39         CGroupContext *c = userdata;
40         CGroupBlockIODeviceWeight *w;
41         int r;
42
43         assert(bus);
44         assert(reply);
45         assert(c);
46
47         r = sd_bus_message_open_container(reply, 'a', "(st)");
48         if (r < 0)
49                 return r;
50
51         LIST_FOREACH(device_weights, w, c->blockio_device_weights) {
52                 r = sd_bus_message_append(reply, "(st)", w->path, w->weight);
53                 if (r < 0)
54                         return r;
55         }
56
57         return sd_bus_message_close_container(reply);
58 }
59
60 static int property_get_blockio_device_bandwidths(
61                 sd_bus *bus,
62                 const char *path,
63                 const char *interface,
64                 const char *property,
65                 sd_bus_message *reply,
66                 void *userdata,
67                 sd_bus_error *error) {
68
69         CGroupContext *c = userdata;
70         CGroupBlockIODeviceBandwidth *b;
71         int r;
72
73         assert(bus);
74         assert(reply);
75         assert(c);
76
77         r = sd_bus_message_open_container(reply, 'a', "(st)");
78         if (r < 0)
79                 return r;
80
81         LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
82
83                 if (streq(property, "BlockIOReadBandwidth") != b->read)
84                         continue;
85
86                 r = sd_bus_message_append(reply, "(st)", b->path, b->bandwidth);
87                 if (r < 0)
88                         return r;
89         }
90
91         return sd_bus_message_close_container(reply);
92 }
93
94 static int property_get_device_allow(
95                 sd_bus *bus,
96                 const char *path,
97                 const char *interface,
98                 const char *property,
99                 sd_bus_message *reply,
100                 void *userdata,
101                 sd_bus_error *error) {
102
103         CGroupContext *c = userdata;
104         CGroupDeviceAllow *a;
105         int r;
106
107         assert(bus);
108         assert(reply);
109         assert(c);
110
111         r = sd_bus_message_open_container(reply, 'a', "(ss)");
112         if (r < 0)
113                 return r;
114
115         LIST_FOREACH(device_allow, a, c->device_allow) {
116                 unsigned k = 0;
117                 char rwm[4];
118
119                 if (a->r)
120                         rwm[k++] = 'r';
121                 if (a->w)
122                         rwm[k++] = 'w';
123                 if (a->m)
124                         rwm[k++] = 'm';
125
126                 rwm[k] = 0;
127
128                 r = sd_bus_message_append(reply, "(ss)", a->path, rwm);
129                 if (r < 0)
130                         return r;
131         }
132
133         return sd_bus_message_close_container(reply);
134 }
135
136 static int property_get_cpu_quota_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 static int property_get_ulong_as_u64(
173                 sd_bus *bus,
174                 const char *path,
175                 const char *interface,
176                 const char *property,
177                 sd_bus_message *reply,
178                 void *userdata,
179                 sd_bus_error *error) {
180
181         unsigned long *ul = userdata;
182
183         assert(bus);
184         assert(reply);
185         assert(ul);
186
187         return sd_bus_message_append(reply, "t", *ul == (unsigned long) -1 ? (uint64_t) -1 : (uint64_t) *ul);
188 }
189
190 const sd_bus_vtable bus_cgroup_vtable[] = {
191         SD_BUS_VTABLE_START(0),
192         SD_BUS_PROPERTY("CPUAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, cpu_accounting), 0),
193         SD_BUS_PROPERTY("CPUShares", "t", property_get_ulong_as_u64, offsetof(CGroupContext, cpu_shares), 0),
194         SD_BUS_PROPERTY("StartupCPUShares", "t", property_get_ulong_as_u64, offsetof(CGroupContext, startup_cpu_shares), 0),
195         SD_BUS_PROPERTY("CPUQuotaPerSecUSec", "t", property_get_cpu_quota_per_sec_usec, 0, 0),
196         SD_BUS_PROPERTY("CPUQuotaUSec", "t", property_get_cpu_quota_usec, 0, 0),
197         SD_BUS_PROPERTY("CPUQuotaPeriodUSec", "t", bus_property_get_usec, offsetof(CGroupContext, cpu_quota_period_usec), 0),
198         SD_BUS_PROPERTY("BlockIOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, blockio_accounting), 0),
199         SD_BUS_PROPERTY("BlockIOWeight", "t", property_get_ulong_as_u64, offsetof(CGroupContext, blockio_weight), 0),
200         SD_BUS_PROPERTY("StartupBlockIOWeight", "t", property_get_ulong_as_u64, offsetof(CGroupContext, startup_blockio_weight), 0),
201         SD_BUS_PROPERTY("BlockIODeviceWeight", "a(st)", property_get_blockio_device_weight, 0, 0),
202         SD_BUS_PROPERTY("BlockIOReadBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
203         SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
204         SD_BUS_PROPERTY("MemoryAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, memory_accounting), 0),
205         SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0),
206         SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0),
207         SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0),
208         SD_BUS_VTABLE_END
209 };
210
211 int bus_cgroup_set_property(
212                 Unit *u,
213                 CGroupContext *c,
214                 const char *name,
215                 sd_bus_message *message,
216                 UnitSetPropertiesMode mode,
217                 sd_bus_error *error) {
218
219         int r;
220
221         assert(u);
222         assert(c);
223         assert(name);
224         assert(message);
225
226         if (streq(name, "CPUAccounting")) {
227                 int b;
228
229                 r = sd_bus_message_read(message, "b", &b);
230                 if (r < 0)
231                         return r;
232
233                 if (mode != UNIT_CHECK) {
234                         c->cpu_accounting = b;
235                         u->cgroup_realized_mask &= ~CGROUP_CPUACCT;
236                         unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no");
237                 }
238
239                 return 1;
240
241         } else if (streq(name, "CPUShares")) {
242                 uint64_t u64;
243                 unsigned long ul;
244
245                 r = sd_bus_message_read(message, "t", &u64);
246                 if (r < 0)
247                         return r;
248
249                 if (u64 == (uint64_t) -1)
250                         ul = (unsigned long) -1;
251                 else {
252                         ul = (unsigned long) u64;
253                         if (ul <= 0 || (uint64_t) ul != u64)
254                                 return sd_bus_error_set_errnof(error, EINVAL, "CPUShares value out of range");
255                 }
256
257                 if (mode != UNIT_CHECK) {
258                         c->cpu_shares = ul;
259                         u->cgroup_realized_mask &= ~CGROUP_CPU;
260                         unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul);
261                 }
262
263                 return 1;
264
265         } else if (streq(name, "StartupCPUShares")) {
266                 uint64_t u64;
267                 unsigned long ul;
268
269                 r = sd_bus_message_read(message, "t", &u64);
270                 if (r < 0)
271                         return r;
272
273                 if (u64 == (uint64_t) -1)
274                         ul = (unsigned long) -1;
275                 else {
276                         ul = (unsigned long) u64;
277                         if (ul <= 0 || (uint64_t) ul != u64)
278                                 return sd_bus_error_set_errnof(error, EINVAL, "StartupCPUShares value out of range");
279                 }
280
281                 if (mode != UNIT_CHECK) {
282                         c->startup_cpu_shares = ul;
283                         u->cgroup_realized_mask &= ~CGROUP_CPU;
284                         unit_write_drop_in_private_format(u, mode, name, "StartupCPUShares=%lu", ul);
285                 }
286
287                 return 1;
288
289         } else if (streq(name, "CPUQuotaPerSecUSec")) {
290                 uint64_t u64;
291
292                 r = sd_bus_message_read(message, "t", &u64);
293                 if (r < 0)
294                         return r;
295
296                 if (u64 <= 0)
297                         return sd_bus_error_set_errnof(error, EINVAL, "CPUQuotaPerSecUSec value out of range");
298
299                 if (mode != UNIT_CHECK) {
300                         c->cpu_quota_per_sec_usec = u64;
301                         c->cpu_quota_usec = (uint64_t) -1;
302                         u->cgroup_realized_mask &= ~CGROUP_CPU;
303                         unit_write_drop_in_private_format(u, mode, "CPUQuota", "CPUQuota=%0.f%%", (double) (c->cpu_quota_per_sec_usec / 10000));
304                 }
305
306                 return 1;
307
308         } else if (streq(name, "CPUQuotaUSec")) {
309                 uint64_t u64;
310
311                 r = sd_bus_message_read(message, "t", &u64);
312                 if (r < 0)
313                         return r;
314
315                 if (u64 <= 0)
316                         return sd_bus_error_set_errnof(error, EINVAL, "CPUQuotaUSec value out of range");
317
318                 if (mode != UNIT_CHECK) {
319                         c->cpu_quota_usec = u64;
320                         c->cpu_quota_per_sec_usec = (uint64_t) -1;
321                         u->cgroup_realized_mask &= ~CGROUP_CPU;
322                         unit_write_drop_in_private_format(u, mode, "CPUQuota", "CPUQuota=%" PRIu64 "us", u64);
323                 }
324
325                 return 1;
326
327         } else if (streq(name, "CPUQuotaPeriodUSec")) {
328
329                 uint64_t u64;
330
331                 r = sd_bus_message_read(message, "t", &u64);
332                 if (r < 0)
333                         return r;
334
335                 if (u64 <= 0 || u64 >= (usec_t) -1)
336                         return sd_bus_error_set_errnof(error, EINVAL, "CPUQuotaPeriodUSec value out of range");
337
338                 if (mode != UNIT_CHECK) {
339                         c->cpu_quota_period_usec = u64;
340                         u->cgroup_realized_mask &= ~CGROUP_CPU;
341                         unit_write_drop_in_private_format(u, mode, name, "CPUQuotaPeriodSec=%" PRIu64 "us", c->cpu_quota_period_usec);
342                 }
343
344                 return 1;
345
346         } else if (streq(name, "BlockIOAccounting")) {
347                 int b;
348
349                 r = sd_bus_message_read(message, "b", &b);
350                 if (r < 0)
351                         return r;
352
353                 if (mode != UNIT_CHECK) {
354                         c->blockio_accounting = b;
355                         u->cgroup_realized_mask &= ~CGROUP_BLKIO;
356                         unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no");
357                 }
358
359                 return 1;
360
361         } else if (streq(name, "BlockIOWeight")) {
362                 uint64_t u64;
363                 unsigned long ul;
364
365                 r = sd_bus_message_read(message, "t", &u64);
366                 if (r < 0)
367                         return r;
368
369                 if (u64 == (uint64_t) -1)
370                         ul = (unsigned long) -1;
371                 else  {
372                         ul = (unsigned long) u64;
373                         if (ul < 10 || ul > 1000)
374                                 return sd_bus_error_set_errnof(error, EINVAL, "BlockIOWeight value out of range");
375                 }
376
377                 if (mode != UNIT_CHECK) {
378                         c->blockio_weight = ul;
379                         u->cgroup_realized_mask &= ~CGROUP_BLKIO;
380                         unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul);
381                 }
382
383                 return 1;
384
385         } else if (streq(name, "StartupBlockIOWeight")) {
386                 uint64_t u64;
387                 unsigned long ul;
388
389                 r = sd_bus_message_read(message, "t", &u64);
390                 if (r < 0)
391                         return r;
392
393                 if (u64 == (uint64_t) -1)
394                         ul = (unsigned long) -1;
395                 else  {
396                         ul = (unsigned long) u64;
397                         if (ul < 10 || ul > 1000)
398                                 return sd_bus_error_set_errnof(error, EINVAL, "StartupBlockIOWeight value out of range");
399                 }
400
401                 if (mode != UNIT_CHECK) {
402                         c->startup_blockio_weight = ul;
403                         u->cgroup_realized_mask &= ~CGROUP_BLKIO;
404                         unit_write_drop_in_private_format(u, mode, name, "StartupBlockIOWeight=%lu", ul);
405                 }
406
407                 return 1;
408
409         } else if (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth")) {
410                 const char *path;
411                 bool read = true;
412                 unsigned n = 0;
413                 uint64_t u64;
414
415                 if (streq(name, "BlockIOWriteBandwidth"))
416                         read = false;
417
418                 r = sd_bus_message_enter_container(message, 'a', "(st)");
419                 if (r < 0)
420                         return r;
421
422                 while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
423
424                         if (mode != UNIT_CHECK) {
425                                 CGroupBlockIODeviceBandwidth *a = NULL, *b;
426
427                                 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
428                                         if (path_equal(path, b->path) && read == b->read) {
429                                                 a = b;
430                                                 break;
431                                         }
432                                 }
433
434                                 if (!a) {
435                                         a = new0(CGroupBlockIODeviceBandwidth, 1);
436                                         if (!a)
437                                                 return -ENOMEM;
438
439                                         a->read = read;
440                                         a->path = strdup(path);
441                                         if (!a->path) {
442                                                 free(a);
443                                                 return -ENOMEM;
444                                         }
445
446                                         LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, a);
447                                 }
448
449                                 a->bandwidth = u64;
450                         }
451
452                         n++;
453                 }
454                 if (r < 0)
455                         return r;
456
457                 r = sd_bus_message_exit_container(message);
458                 if (r < 0)
459                         return r;
460
461                 if (mode != UNIT_CHECK) {
462                         CGroupBlockIODeviceBandwidth *a, *next;
463                         _cleanup_free_ char *buf = NULL;
464                         _cleanup_fclose_ FILE *f = NULL;
465                         size_t size = 0;
466
467                         if (n == 0) {
468                                 LIST_FOREACH_SAFE(device_bandwidths, a, next, c->blockio_device_bandwidths)
469                                         if (a->read == read)
470                                                 cgroup_context_free_blockio_device_bandwidth(c, a);
471                         }
472
473                         u->cgroup_realized_mask &= ~CGROUP_BLKIO;
474
475                         f = open_memstream(&buf, &size);
476                         if (!f)
477                                 return -ENOMEM;
478
479                          if (read) {
480                                 fputs("BlockIOReadBandwidth=\n", f);
481                                  LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
482                                         if (a->read)
483                                                 fprintf(f, "BlockIOReadBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
484                         } else {
485                                 fputs("BlockIOWriteBandwidth=\n", f);
486                                 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
487                                         if (!a->read)
488                                                 fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
489                         }
490
491                         fflush(f);
492                         unit_write_drop_in_private(u, mode, name, buf);
493                 }
494
495                 return 1;
496
497         } else if (streq(name, "BlockIODeviceWeight")) {
498                 const char *path;
499                 uint64_t u64;
500                 unsigned n = 0;
501
502                 r = sd_bus_message_enter_container(message, 'a', "(st)");
503                 if (r < 0)
504                         return r;
505
506                 while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
507                         unsigned long ul = u64;
508
509                         if (ul < 10 || ul > 1000)
510                                 return sd_bus_error_set_errnof(error, EINVAL, "BlockIODeviceWeight out of range");
511
512                         if (mode != UNIT_CHECK) {
513                                 CGroupBlockIODeviceWeight *a = NULL, *b;
514
515                                 LIST_FOREACH(device_weights, b, c->blockio_device_weights) {
516                                         if (path_equal(b->path, path)) {
517                                                 a = b;
518                                                 break;
519                                         }
520                                 }
521
522                                 if (!a) {
523                                         a = new0(CGroupBlockIODeviceWeight, 1);
524                                         if (!a)
525                                                 return -ENOMEM;
526
527                                         a->path = strdup(path);
528                                         if (!a->path) {
529                                                 free(a);
530                                                 return -ENOMEM;
531                                         }
532                                         LIST_PREPEND(device_weights,c->blockio_device_weights, a);
533                                 }
534
535                                 a->weight = ul;
536                         }
537
538                         n++;
539                 }
540
541                 r = sd_bus_message_exit_container(message);
542                 if (r < 0)
543                         return r;
544
545                 if (mode != UNIT_CHECK) {
546                         _cleanup_free_ char *buf = NULL;
547                         _cleanup_fclose_ FILE *f = NULL;
548                         CGroupBlockIODeviceWeight *a;
549                         size_t size = 0;
550
551                         if (n == 0) {
552                                 while (c->blockio_device_weights)
553                                         cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
554                         }
555
556                         u->cgroup_realized_mask &= ~CGROUP_BLKIO;
557
558                         f = open_memstream(&buf, &size);
559                         if (!f)
560                                 return -ENOMEM;
561
562                         fputs("BlockIODeviceWeight=\n", f);
563                         LIST_FOREACH(device_weights, a, c->blockio_device_weights)
564                                 fprintf(f, "BlockIODeviceWeight=%s %lu\n", a->path, a->weight);
565
566                         fflush(f);
567                         unit_write_drop_in_private(u, mode, name, buf);
568                 }
569
570                 return 1;
571
572         } else if (streq(name, "MemoryAccounting")) {
573                 int b;
574
575                 r = sd_bus_message_read(message, "b", &b);
576                 if (r < 0)
577                         return r;
578
579                 if (mode != UNIT_CHECK) {
580                         c->memory_accounting = b;
581                         u->cgroup_realized_mask &= ~CGROUP_MEMORY;
582                         unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no");
583                 }
584
585                 return 1;
586
587         } else if (streq(name, "MemoryLimit")) {
588                 uint64_t limit;
589
590                 r = sd_bus_message_read(message, "t", &limit);
591                 if (r < 0)
592                         return r;
593
594                 if (mode != UNIT_CHECK) {
595                         c->memory_limit = limit;
596                         u->cgroup_realized_mask &= ~CGROUP_MEMORY;
597                         unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit);
598                 }
599
600                 return 1;
601
602         } else if (streq(name, "DevicePolicy")) {
603                 const char *policy;
604                 CGroupDevicePolicy p;
605
606                 r = sd_bus_message_read(message, "s", &policy);
607                 if (r < 0)
608                         return r;
609
610                 p = cgroup_device_policy_from_string(policy);
611                 if (p < 0)
612                         return -EINVAL;
613
614                 if (mode != UNIT_CHECK) {
615                         char *buf;
616
617                         c->device_policy = p;
618                         u->cgroup_realized_mask &= ~CGROUP_DEVICE;
619
620                         buf = strappenda("DevicePolicy=", policy);
621                         unit_write_drop_in_private(u, mode, name, buf);
622                 }
623
624                 return 1;
625
626         } else if (streq(name, "DeviceAllow")) {
627                 const char *path, *rwm;
628                 unsigned n = 0;
629
630                 r = sd_bus_message_enter_container(message, 'a', "(ss)");
631                 if (r < 0)
632                         return r;
633
634                 while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) {
635
636                         if ((!startswith(path, "/dev/") &&
637                              !startswith(path, "block-") &&
638                              !startswith(path, "char-")) ||
639                             strpbrk(path, WHITESPACE))
640                             return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires device node");
641
642                         if (isempty(rwm))
643                                 rwm = "rwm";
644
645                         if (!in_charset(rwm, "rwm"))
646                                 return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires combination of rwm flags");
647
648                         if (mode != UNIT_CHECK) {
649                                 CGroupDeviceAllow *a = NULL, *b;
650
651                                 LIST_FOREACH(device_allow, b, c->device_allow) {
652                                         if (path_equal(b->path, path)) {
653                                                 a = b;
654                                                 break;
655                                         }
656                                 }
657
658                                 if (!a) {
659                                         a = new0(CGroupDeviceAllow, 1);
660                                         if (!a)
661                                                 return -ENOMEM;
662
663                                         a->path = strdup(path);
664                                         if (!a->path) {
665                                                 free(a);
666                                                 return -ENOMEM;
667                                         }
668
669                                         LIST_PREPEND(device_allow, c->device_allow, a);
670                                 }
671
672                                 a->r = !!strchr(rwm, 'r');
673                                 a->w = !!strchr(rwm, 'w');
674                                 a->m = !!strchr(rwm, 'm');
675                         }
676
677                         n++;
678                 }
679                 if (r < 0)
680                         return r;
681
682                 r = sd_bus_message_exit_container(message);
683                 if (r < 0)
684                         return r;
685
686                 if (mode != UNIT_CHECK) {
687                         _cleanup_free_ char *buf = NULL;
688                         _cleanup_fclose_ FILE *f = NULL;
689                         CGroupDeviceAllow *a;
690                         size_t size = 0;
691
692                         if (n == 0) {
693                                 while (c->device_allow)
694                                         cgroup_context_free_device_allow(c, c->device_allow);
695                         }
696
697                         u->cgroup_realized_mask &= ~CGROUP_DEVICE;
698
699                         f = open_memstream(&buf, &size);
700                         if (!f)
701                                 return -ENOMEM;
702
703                         fputs("DeviceAllow=\n", f);
704                         LIST_FOREACH(device_allow, a, c->device_allow)
705                                 fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
706
707                         fflush(f);
708                         unit_write_drop_in_private(u, mode, name, buf);
709                 }
710
711                 return 1;
712         }
713
714         return 0;
715 }