chiark / gitweb /
cf05f04ea1d5ccf83f5388f5f9e16ef22a6894c8
[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_section(u, mode, "cpu-accounting", 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                         char buf[sizeof("CPUShares=") + DECIMAL_STR_MAX(ul)];
185                         c->cpu_shares = ul;
186
187                         sprintf(buf, "CPUShares=%lu", ul);
188                         unit_write_drop_in_private_section(u, mode, "cpu-shares", buf);
189                 }
190
191                 return 1;
192
193         } else if (streq(name, "BlockIOAccounting")) {
194
195                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
196                         return -EINVAL;
197
198                 if (mode != UNIT_CHECK) {
199                         dbus_bool_t b;
200                         dbus_message_iter_get_basic(i, &b);
201
202                         c->blockio_accounting = b;
203                         unit_write_drop_in_private_section(u, mode, "block-io-accounting", b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no");
204                 }
205
206                 return 1;
207
208         } else if (streq(name, "BlockIOWeight")) {
209                 uint64_t u64;
210                 unsigned long ul;
211
212                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_UINT64)
213                         return -EINVAL;
214
215                 dbus_message_iter_get_basic(i, &u64);
216                 ul = (unsigned long) u64;
217
218                 if (u64 < 10 || u64 > 1000)
219                         return -EINVAL;
220
221                 if (mode != UNIT_CHECK) {
222                         char buf[sizeof("BlockIOWeight=") + DECIMAL_STR_MAX(ul)];
223                         c->cpu_shares = ul;
224
225                         sprintf(buf, "BlockIOWeight=%lu", ul);
226                         unit_write_drop_in_private_section(u, mode, "blockio-weight", buf);
227                 }
228
229                 return 1;
230
231         } else if (streq(name, "MemoryAccounting")) {
232
233                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
234                         return -EINVAL;
235
236                 if (mode != UNIT_CHECK) {
237                         dbus_bool_t b;
238                         dbus_message_iter_get_basic(i, &b);
239
240                         c->memory_accounting = b;
241                         unit_write_drop_in_private_section(u, mode, "memory-accounting", b ? "MemoryAccounting=yes" : "MemoryAccounting=no");
242                 }
243
244                 return 1;
245
246         } else if (streq(name, "MemoryLimit") || streq(name, "MemorySoftLimit")) {
247
248                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_UINT64)
249                         return -EINVAL;
250
251                 if (mode != UNIT_CHECK) {
252                         uint64_t limit;
253                         char buf[sizeof("MemorySoftLimit=") + DECIMAL_STR_MAX(limit)];
254
255                         dbus_message_iter_get_basic(i, &limit);
256
257                         if (streq(name, "MemoryLimit")) {
258                                 c->memory_limit = limit;
259                                 sprintf(buf, "MemoryLimit=%" PRIu64, limit);
260                                 unit_write_drop_in_private_section(u, mode, "memory-limit", buf);
261                         } else {
262                                 c->memory_soft_limit = limit;
263                                 sprintf(buf, "MemorySoftLimit=%" PRIu64, limit);
264                                 unit_write_drop_in_private_section(u, mode, "memory-soft-limit", buf);
265                         }
266                 }
267
268                 return 1;
269
270         } else if (streq(name, "DevicePolicy")) {
271                 const char *policy;
272                 CGroupDevicePolicy p;
273
274                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING)
275                         return -EINVAL;
276
277                 dbus_message_iter_get_basic(i, &policy);
278                 p = cgroup_device_policy_from_string(policy);
279                 if (p < 0)
280                         return -EINVAL;
281
282                 if (mode != UNIT_CHECK) {
283                         char *buf;
284
285                         c->device_policy = p;
286
287                         buf = strappenda("DevicePolicy=", policy);
288                         unit_write_drop_in_private_section(u, mode, "device-policy", buf);
289                 }
290
291                 return 1;
292
293         } else if (streq(name, "DeviceAllow")) {
294                 DBusMessageIter sub;
295                 unsigned n = 0;
296
297                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
298                     dbus_message_iter_get_element_type(i) != DBUS_TYPE_STRUCT)
299                         return -EINVAL;
300
301                 dbus_message_iter_recurse(i, &sub);
302                 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
303                         DBusMessageIter sub2;
304                         const char *path, *rwm;
305                         CGroupDeviceAllow *a;
306
307                         dbus_message_iter_recurse(&sub, &sub2);
308
309                         if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0 ||
310                             bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &rwm, false) < 0)
311                                 return -EINVAL;
312
313                         if (!path_startswith(path, "/dev")) {
314                                 dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "DeviceAllow= requires device node");
315                                 return -EINVAL;
316                         }
317
318                         if (isempty(rwm))
319                                 rwm = "rwm";
320
321                         if (!in_charset(rwm, "rwm")) {
322                                 dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "DeviceAllow= requires combination of rwm flags");
323                                 return -EINVAL;
324                         }
325
326                         n++;
327
328                         if (mode != UNIT_CHECK) {
329                                 a = new0(CGroupDeviceAllow, 1);
330                                 if (!a)
331                                         return -ENOMEM;
332
333                                 a->path = strdup(path);
334                                 if (!a->path) {
335                                         free(a);
336                                         return -ENOMEM;
337                                 }
338
339                                 a->r = !!strchr(rwm, 'r');
340                                 a->w = !!strchr(rwm, 'w');
341                                 a->m = !!strchr(rwm, 'm');
342
343                                 LIST_PREPEND(CGroupDeviceAllow, device_allow, c->device_allow, a);
344                         }
345
346                         dbus_message_iter_next(&sub);
347                 }
348
349                 if (mode != UNIT_CHECK) {
350                         _cleanup_free_ char *buf = NULL;
351                         _cleanup_fclose_ FILE *f = NULL;
352                         CGroupDeviceAllow *a;
353                         size_t size = 0;
354
355                         if (n == 0) {
356                                 while (c->device_allow)
357                                         cgroup_context_free_device_allow(c, c->device_allow);
358                         }
359
360                         f = open_memstream(&buf, &size);
361                         if (!f)
362                                 return -ENOMEM;
363
364                         fputs("DeviceAllow=\n", f);
365                         LIST_FOREACH(device_allow, a, c->device_allow)
366                                 fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
367
368                         fflush(f);
369                         unit_write_drop_in_private_section(u, mode, "device-allow", buf);
370                 }
371
372                 return 1;
373         }
374
375         return 0;
376 }