chiark / gitweb /
core: simplify drop-in writing logic a bit
[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 <dbus/dbus.h>
23
24 #include "path-util.h"
25 #include "dbus-cgroup.h"
26
27 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_cgroup_append_device_policy, cgroup_device_policy, CGroupDevicePolicy);
28
29 static int bus_cgroup_append_device_weights(DBusMessageIter *i, const char *property, void *data) {
30         DBusMessageIter sub, sub2;
31         CGroupContext *c = data;
32         CGroupBlockIODeviceWeight *w;
33
34         assert(i);
35         assert(property);
36         assert(c);
37
38         if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(st)", &sub))
39                 return -ENOMEM;
40
41         LIST_FOREACH(device_weights, w, c->blockio_device_weights) {
42
43                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
44                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &w->path) ||
45                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &w->weight) ||
46                     !dbus_message_iter_close_container(&sub, &sub2))
47                         return -ENOMEM;
48         }
49
50         if (!dbus_message_iter_close_container(i, &sub))
51                 return -ENOMEM;
52
53         return 0;
54 }
55
56 static int bus_cgroup_append_device_bandwidths(DBusMessageIter *i, const char *property, void *data) {
57         DBusMessageIter sub, sub2;
58         CGroupContext *c = data;
59         CGroupBlockIODeviceBandwidth *b;
60
61         assert(i);
62         assert(property);
63         assert(c);
64
65         if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(st)", &sub))
66                 return -ENOMEM;
67
68         LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
69
70                 if (streq(property, "BlockIOReadBandwidth") != b->read)
71                         continue;
72
73                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
74                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &b->path) ||
75                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &b->bandwidth) ||
76                     !dbus_message_iter_close_container(&sub, &sub2))
77                         return -ENOMEM;
78         }
79
80         if (!dbus_message_iter_close_container(i, &sub))
81                 return -ENOMEM;
82
83         return 0;
84 }
85
86 static int bus_cgroup_append_device_allow(DBusMessageIter *i, const char *property, void *data) {
87         DBusMessageIter sub, sub2;
88         CGroupContext *c = data;
89         CGroupDeviceAllow *a;
90
91         assert(i);
92         assert(property);
93         assert(c);
94
95         if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(ss)", &sub))
96                 return -ENOMEM;
97
98         LIST_FOREACH(device_allow, a, c->device_allow) {
99                 const char *rwm;
100                 char buf[4];
101                 unsigned k = 0;
102
103                 if (a->r)
104                         buf[k++] = 'r';
105                 if (a->w)
106                         buf[k++] = 'w';
107                 if (a->m)
108                         buf[k++] = 'm';
109
110                 buf[k] = 0;
111                 rwm = buf;
112
113                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
114                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->path) ||
115                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &rwm) ||
116                     !dbus_message_iter_close_container(&sub, &sub2))
117                         return -ENOMEM;
118         }
119
120         if (!dbus_message_iter_close_container(i, &sub))
121                 return -ENOMEM;
122
123         return 0;
124 }
125
126 const BusProperty bus_cgroup_context_properties[] = {
127         { "CPUAccounting",           bus_property_append_bool,            "b",     offsetof(CGroupContext, cpu_accounting)     },
128         { "CPUShares",               bus_property_append_ul,              "t",     offsetof(CGroupContext, cpu_shares)         },
129         { "BlockIOAccounting",       bus_property_append_bool,            "b",     offsetof(CGroupContext, blockio_accounting) },
130         { "BlockIOWeight",           bus_property_append_ul,              "t",     offsetof(CGroupContext, blockio_weight)     },
131         { "BlockIODeviceWeight",     bus_cgroup_append_device_weights,    "a(st)", 0                                           },
132         { "BlockIOReadBandwidth",    bus_cgroup_append_device_bandwidths, "a(st)", 0                                           },
133         { "BlockIOWriteBandwidth",   bus_cgroup_append_device_bandwidths, "a(st)", 0                                           },
134         { "MemoryAccounting",        bus_property_append_bool,            "b",     offsetof(CGroupContext, memory_accounting)  },
135         { "MemoryLimit",             bus_property_append_uint64,          "t",     offsetof(CGroupContext, memory_limit)       },
136         { "MemorySoftLimit",         bus_property_append_uint64,          "t",     offsetof(CGroupContext, memory_soft_limit)  },
137         { "DevicePolicy",            bus_cgroup_append_device_policy,     "s",     offsetof(CGroupContext, device_policy)      },
138         { "DeviceAllow",             bus_cgroup_append_device_allow,      "a(ss)", 0                                           },
139         {}
140 };
141
142 int bus_cgroup_set_property(
143                 Unit *u,
144                 CGroupContext *c,
145                 const char *name,
146                 DBusMessageIter *i,
147                 UnitSetPropertiesMode mode,
148                 DBusError *error) {
149
150         assert(name);
151         assert(u);
152         assert(c);
153         assert(i);
154
155         if (streq(name, "CPUAccounting")) {
156
157                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
158                         return -EINVAL;
159
160                 if (mode != UNIT_CHECK) {
161                         dbus_bool_t b;
162                         dbus_message_iter_get_basic(i, &b);
163
164                         c->cpu_accounting = b;
165                         unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no");
166                 }
167
168                 return 1;
169
170         } else if (streq(name, "CPUShares")) {
171                 uint64_t u64;
172                 unsigned long ul;
173
174                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_UINT64)
175                         return -EINVAL;
176
177                 dbus_message_iter_get_basic(i, &u64);
178                 ul = (unsigned long) u64;
179
180                 if (u64 <= 0 || u64 != (uint64_t) ul)
181                         return -EINVAL;
182
183                 if (mode != UNIT_CHECK) {
184                         c->cpu_shares = ul;
185                         unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul);
186                 }
187
188                 return 1;
189
190         } else if (streq(name, "BlockIOAccounting")) {
191
192                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
193                         return -EINVAL;
194
195                 if (mode != UNIT_CHECK) {
196                         dbus_bool_t b;
197                         dbus_message_iter_get_basic(i, &b);
198
199                         c->blockio_accounting = b;
200                         unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no");
201                 }
202
203                 return 1;
204
205         } else if (streq(name, "BlockIOWeight")) {
206                 uint64_t u64;
207                 unsigned long ul;
208
209                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_UINT64)
210                         return -EINVAL;
211
212                 dbus_message_iter_get_basic(i, &u64);
213                 ul = (unsigned long) u64;
214
215                 if (u64 < 10 || u64 > 1000)
216                         return -EINVAL;
217
218                 if (mode != UNIT_CHECK) {
219                         c->cpu_shares = ul;
220                         unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul);
221                 }
222
223                 return 1;
224
225         } else if (streq(name, "MemoryAccounting")) {
226
227                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
228                         return -EINVAL;
229
230                 if (mode != UNIT_CHECK) {
231                         dbus_bool_t b;
232                         dbus_message_iter_get_basic(i, &b);
233
234                         c->memory_accounting = b;
235                         unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no");
236                 }
237
238                 return 1;
239
240         } else if (streq(name, "MemoryLimit") || streq(name, "MemorySoftLimit")) {
241
242                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_UINT64)
243                         return -EINVAL;
244
245                 if (mode != UNIT_CHECK) {
246                         uint64_t limit;
247
248                         dbus_message_iter_get_basic(i, &limit);
249
250                         if (streq(name, "MemoryLimit"))
251                                 c->memory_limit = limit;
252                         else
253                                 c->memory_soft_limit = limit;
254
255                         unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit);
256                 }
257
258                 return 1;
259
260         } else if (streq(name, "DevicePolicy")) {
261                 const char *policy;
262                 CGroupDevicePolicy p;
263
264                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING)
265                         return -EINVAL;
266
267                 dbus_message_iter_get_basic(i, &policy);
268                 p = cgroup_device_policy_from_string(policy);
269                 if (p < 0)
270                         return -EINVAL;
271
272                 if (mode != UNIT_CHECK) {
273                         char *buf;
274
275                         c->device_policy = p;
276
277                         buf = strappenda("DevicePolicy=", policy);
278                         unit_write_drop_in_private(u, mode, name, buf);
279                 }
280
281                 return 1;
282
283         } else if (streq(name, "DeviceAllow")) {
284                 DBusMessageIter sub;
285                 unsigned n = 0;
286
287                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
288                     dbus_message_iter_get_element_type(i) != DBUS_TYPE_STRUCT)
289                         return -EINVAL;
290
291                 dbus_message_iter_recurse(i, &sub);
292                 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
293                         DBusMessageIter sub2;
294                         const char *path, *rwm;
295                         CGroupDeviceAllow *a;
296
297                         dbus_message_iter_recurse(&sub, &sub2);
298
299                         if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0 ||
300                             bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &rwm, false) < 0)
301                                 return -EINVAL;
302
303                         if (!path_startswith(path, "/dev")) {
304                                 dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "DeviceAllow= requires device node");
305                                 return -EINVAL;
306                         }
307
308                         if (isempty(rwm))
309                                 rwm = "rwm";
310
311                         if (!in_charset(rwm, "rwm")) {
312                                 dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "DeviceAllow= requires combination of rwm flags");
313                                 return -EINVAL;
314                         }
315
316                         if (mode != UNIT_CHECK) {
317                                 a = new0(CGroupDeviceAllow, 1);
318                                 if (!a)
319                                         return -ENOMEM;
320
321                                 a->path = strdup(path);
322                                 if (!a->path) {
323                                         free(a);
324                                         return -ENOMEM;
325                                 }
326
327                                 a->r = !!strchr(rwm, 'r');
328                                 a->w = !!strchr(rwm, 'w');
329                                 a->m = !!strchr(rwm, 'm');
330
331                                 LIST_PREPEND(CGroupDeviceAllow, device_allow, c->device_allow, a);
332                         }
333
334                         n++;
335                         dbus_message_iter_next(&sub);
336                 }
337
338                 if (mode != UNIT_CHECK) {
339                         _cleanup_free_ char *buf = NULL;
340                         _cleanup_fclose_ FILE *f = NULL;
341                         CGroupDeviceAllow *a;
342                         size_t size = 0;
343
344                         if (n == 0) {
345                                 while (c->device_allow)
346                                         cgroup_context_free_device_allow(c, c->device_allow);
347                         }
348
349                         f = open_memstream(&buf, &size);
350                         if (!f)
351                                 return -ENOMEM;
352
353                         fputs("DeviceAllow=\n", f);
354                         LIST_FOREACH(device_allow, a, c->device_allow)
355                                 fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
356
357                         fflush(f);
358                         unit_write_drop_in_private(u, mode, name, buf);
359                 }
360
361                 return 1;
362         }
363
364         return 0;
365 }