]> git.ipfire.org Git - people/ms/systemd.git/blob - cgroup.c
cgroup: add cgroupsification
[people/ms/systemd.git] / cgroup.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 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 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <assert.h>
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <signal.h>
27 #include <sys/mount.h>
28
29 #include "cgroup.h"
30 #include "log.h"
31
32 static int translate_error(int error, int _errno) {
33
34 switch (error) {
35
36 case ECGROUPNOTCOMPILED:
37 case ECGROUPNOTMOUNTED:
38 case ECGROUPNOTEXIST:
39 case ECGROUPNOTCREATED:
40 return -ENOENT;
41
42 case ECGINVAL:
43 return -EINVAL;
44
45 case ECGROUPNOTALLOWED:
46 return -EPERM;
47
48 case ECGOTHER:
49 return -_errno;
50 }
51
52 return -EIO;
53 }
54
55 int cgroup_bonding_realize(CGroupBonding *b) {
56 int r;
57
58 assert(b);
59 assert(b->path);
60 assert(b->controller);
61
62 if (b->cgroup)
63 return 0;
64
65 if (!(b->cgroup = cgroup_new_cgroup(b->path)))
66 return -ENOMEM;
67
68 if (!cgroup_add_controller(b->cgroup, b->controller)) {
69 r = -ENOMEM;
70 goto fail;
71 }
72
73 if (b->inherit)
74 r = cgroup_create_cgroup_from_parent(b->cgroup, true);
75 else
76 r = cgroup_create_cgroup(b->cgroup, true);
77
78 if (r != 0) {
79 r = translate_error(r, errno);
80 goto fail;
81 }
82
83 return 0;
84
85 fail:
86 cgroup_free(&b->cgroup);
87 b->cgroup = NULL;
88 return r;
89 }
90
91 int cgroup_bonding_realize_list(CGroupBonding *first) {
92 CGroupBonding *b;
93
94 LIST_FOREACH(by_unit, b, first) {
95 int r;
96
97 if ((r = cgroup_bonding_realize(b)) < 0)
98 return r;
99 }
100
101 return 0;
102 }
103
104 void cgroup_bonding_free(CGroupBonding *b) {
105 assert(b);
106
107 if (b->unit) {
108 CGroupBonding *f;
109
110 LIST_REMOVE(CGroupBonding, by_unit, b->unit->meta.cgroup_bondings, b);
111
112 assert_se(f = hashmap_get(b->unit->meta.manager->cgroup_bondings, b->path));
113 LIST_REMOVE(CGroupBonding, by_path, f, b);
114
115 if (f)
116 hashmap_replace(b->unit->meta.manager->cgroup_bondings, b->path, f);
117 else
118 hashmap_remove(b->unit->meta.manager->cgroup_bondings, b->path);
119 }
120
121 free(b->controller);
122 free(b->path);
123
124 if (b->cgroup) {
125
126 if (b->only_us && b->clean_up)
127 cgroup_delete_cgroup(b->cgroup, true);
128
129 cgroup_free(&b->cgroup);
130 }
131
132 free(b);
133 }
134
135 void cgroup_bonding_free_list(CGroupBonding *first) {
136 CGroupBonding *b, *n;
137
138 LIST_FOREACH_SAFE(by_unit, b, n, first)
139 cgroup_bonding_free(b);
140 }
141
142 int cgroup_bonding_install(CGroupBonding *b, pid_t pid) {
143 int r;
144
145 assert(b);
146 assert(pid >= 0);
147
148 if (pid == 0)
149 pid = getpid();
150
151 if (!b->cgroup)
152 return -ENOENT;
153
154 if ((r = cgroup_attach_task_pid(b->cgroup, pid)))
155 return translate_error(r, errno);
156
157 return 0;
158 }
159
160 int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid) {
161 CGroupBonding *b;
162
163 LIST_FOREACH(by_unit, b, first) {
164 int r;
165
166 if ((r = cgroup_bonding_install(b, pid)) < 0)
167 return r;
168 }
169
170 return 0;
171 }
172
173 int cgroup_bonding_kill(CGroupBonding *b, int sig) {
174 int r;
175 Set *s;
176 bool done;
177
178 assert(b);
179 assert(sig > 0);
180
181 if (!(s = set_new(trivial_hash_func, trivial_compare_func)))
182 return -ENOMEM;
183
184 do {
185 void *iterator;
186 pid_t pid;
187
188 done = true;
189
190 if ((r = cgroup_get_task_begin(b->path, b->controller, &iterator, &pid)) != 0) {
191 if (r == ECGEOF) {
192 r = 0;
193 goto kill_done;
194 } else {
195 r = translate_error(r, errno);
196 break;
197 }
198 }
199
200 for (;;) {
201 if (set_get(s, INT_TO_PTR(pid)) != INT_TO_PTR(pid)) {
202
203 /* If we haven't killed this process
204 * yet, kill it */
205
206 if (kill(pid, sig) < 0 && errno != ESRCH) {
207 r = -errno;
208 break;
209 }
210
211 done = false;
212
213 if ((r = set_put(s, INT_TO_PTR(pid))) < 0)
214 break;
215 }
216
217 if ((r = cgroup_get_task_next(&iterator, &pid)) != 0) {
218
219 if (r == ECGEOF)
220 r = 0;
221 else
222 r = translate_error(r, errno);
223
224 break;
225 }
226 }
227
228 kill_done:
229 assert_se(cgroup_get_task_end(&iterator) == 0);
230
231 /* To avoid racing against processes which fork
232 * quicker than we can kill them we repeat this until
233 * no new pids need to be killed. */
234
235 } while (!done && r >= 0);
236
237 set_free(s);
238 return r;
239 }
240
241 int cgroup_bonding_kill_list(CGroupBonding *first, int sig) {
242 CGroupBonding *b;
243
244 LIST_FOREACH(by_unit, b, first) {
245 int r;
246
247 if ((r = cgroup_bonding_kill(b, sig)) < 0)
248 return r;
249 }
250
251 return 0;
252 }
253
254 /* Returns 1 if the group is empty, 0 if it is not, -EAGAIN if we
255 * cannot know */
256 int cgroup_bonding_is_empty(CGroupBonding *b) {
257 void *iterator;
258 pid_t pid;
259 int r;
260
261 assert(b);
262
263 r = cgroup_get_task_begin(b->path, b->controller, &iterator, &pid);
264
265 if (r == 0 || r == ECGEOF)
266 cgroup_get_task_end(&iterator);
267
268 /* Hmm, no PID in this group? Then it is definitely empty */
269 if (r == ECGEOF)
270 return 1;
271
272 /* Some error? Let's return it */
273 if (r != 0)
274 return translate_error(r, errno);
275
276 /* It's not empty, and we are the only user, then it is
277 * definitely not empty */
278 if (b->only_us)
279 return 0;
280
281 /* There are PIDs in the group but we aren't the only users,
282 * hence we cannot say */
283 return -EAGAIN;
284 }
285
286 int cgroup_bonding_is_empty_list(CGroupBonding *first) {
287 CGroupBonding *b;
288
289 LIST_FOREACH(by_unit, b, first) {
290 int r;
291
292 if ((r = cgroup_bonding_is_empty(b)) < 0) {
293 /* If this returned -EAGAIN, then we don't know if the
294 * group is empty, so let's see if another group can
295 * tell us */
296
297 if (r != -EAGAIN)
298 return r;
299 } else
300 return r;
301 }
302
303 return -EAGAIN;
304 }
305
306 static int install_release_agent(Manager *m, const char *mount_point) {
307 char *p, *c, *sc;
308 int r;
309
310 assert(m);
311 assert(mount_point);
312
313 if (asprintf(&p, "%s/release_agent", mount_point) < 0)
314 return -ENOMEM;
315
316 if ((r = read_one_line_file(p, &c)) < 0) {
317 free(p);
318 return r;
319 }
320
321 sc = strstrip(c);
322
323 if (sc[0] == 0) {
324 if ((r = write_one_line_file(p, CGROUP_AGENT_PATH "\n" )) < 0) {
325 free(p);
326 free(c);
327 return r;
328 }
329 } else if (!streq(sc, CGROUP_AGENT_PATH)) {
330 free(p);
331 free(c);
332 return -EEXIST;
333 }
334
335 free(c);
336 free(p);
337
338 if (asprintf(&p, "%s/notify_on_release", mount_point) < 0)
339 return -ENOMEM;
340
341 if ((r = read_one_line_file(p, &c)) < 0) {
342 free(p);
343 return r;
344 }
345
346 sc = strstrip(c);
347
348 if (streq(sc, "0")) {
349 if ((r = write_one_line_file(p, "1\n")) < 0) {
350 free(p);
351 free(c);
352 return r;
353 }
354 } else if (!streq(sc, "1")) {
355 free(p);
356 free(c);
357 return -EIO;
358 }
359
360 return 0;
361 }
362
363 static int create_hierarchy_cgroup(Manager *m) {
364 struct cgroup *cg;
365 int r;
366
367 assert(m);
368
369 if (!(cg = cgroup_new_cgroup(m->cgroup_hierarchy)))
370 return -ENOMEM;
371
372 if (!(cgroup_add_controller(cg, m->cgroup_controller))) {
373 r = -ENOMEM;
374 goto finish;
375 }
376
377 if ((r = cgroup_create_cgroup(cg, true)) != 0) {
378 log_error("Failed to create cgroup hierarchy group: %s", cgroup_strerror(r));
379 r = translate_error(r, errno);
380 goto finish;
381 }
382
383 if ((r = cgroup_attach_task(cg)) != 0) {
384 log_error("Failed to add ourselves to hierarchy group: %s", cgroup_strerror(r));
385 r = translate_error(r, errno);
386 goto finish;
387 }
388
389 r = 0;
390
391 finish:
392 cgroup_free(&cg);
393 return r;
394 }
395
396 int manager_setup_cgroup(Manager *m) {
397 char *mp, *cp;
398 int r;
399 pid_t pid;
400
401 assert(m);
402
403 if ((r = cgroup_init()) != 0) {
404 log_error("Failed to initialize libcg: %s", cgroup_strerror(r));
405 return translate_error(r, errno);
406 }
407
408 free(m->cgroup_controller);
409 if (!(m->cgroup_controller = strdup("debug")))
410 return -ENOMEM;
411
412 if ((r = cgroup_get_subsys_mount_point(m->cgroup_controller, &mp)))
413 return translate_error(r, errno);
414
415 pid = getpid();
416
417 if ((r = cgroup_get_current_controller_path(pid, m->cgroup_controller, &cp))) {
418 free(mp);
419 return translate_error(r, errno);
420 }
421
422 free(m->cgroup_hierarchy);
423 m->cgroup_hierarchy = NULL;
424 if (asprintf(&m->cgroup_hierarchy, "%s/systemd-%llu", strcmp(cp, "/") == 0 ? "" : cp, (unsigned long long) pid) < 0) {
425 free(cp);
426 free(mp);
427 return -ENOMEM;
428 }
429
430 log_info("Using cgroup controller <%s>, hierarchy mounted at <%s>, using root group <%s>.",
431 m->cgroup_controller,
432 mp,
433 m->cgroup_hierarchy);
434
435 if ((r = install_release_agent(m, mp)) < 0)
436 log_warning("Failed to install release agent, ignoring: %s", strerror(-r));
437 else
438 log_info("Installed release agent, or already installed.");
439
440 free(mp);
441 free(cp);
442
443 if ((r = create_hierarchy_cgroup(m)) < 0)
444 log_error("Failed to create root cgroup hierarchy: %s", strerror(-r));
445 else
446 log_info("Created root group.");
447
448 return r;
449 }
450
451 int manager_shutdown_cgroup(Manager *m) {
452 struct cgroup *cg;
453 int r;
454
455 assert(m);
456
457 if (!m->cgroup_hierarchy)
458 return 0;
459
460 if (!(cg = cgroup_new_cgroup(m->cgroup_hierarchy)))
461 return -ENOMEM;
462
463 if (!(cgroup_add_controller(cg, m->cgroup_controller))) {
464 r = -ENOMEM;
465 goto finish;
466 }
467
468 if ((r = cgroup_delete_cgroup_ext(cg, CGFLAG_DELETE_IGNORE_MIGRATION|CGFLAG_DELETE_RECURSIVE)) != 0) {
469 log_error("Failed to delete cgroup hierarchy group: %s", cgroup_strerror(r));
470 r = translate_error(r, errno);
471 goto finish;
472 }
473 r = 0;
474
475 finish:
476 cgroup_free(&cg);
477 return r;
478
479 }
480
481 int cgroup_notify_empty(Manager *m, const char *group) {
482 CGroupBonding *l, *b;
483
484 assert(m);
485 assert(group);
486
487 if (!(l = hashmap_get(m->cgroup_bondings, group)))
488 return 0;
489
490 LIST_FOREACH(by_path, b, l) {
491 int t;
492
493 if (!b->unit)
494 continue;
495
496 if ((t = cgroup_bonding_is_empty_list(b)) < 0) {
497
498 /* If we don't know, we don't know */
499 if (t != -EAGAIN)
500 log_warning("Failed to check whether cgroup is empty: %s", strerror(errno));
501
502 continue;
503 }
504
505 if (t > 0)
506 if (UNIT_VTABLE(b->unit)->cgroup_notify_empty)
507 UNIT_VTABLE(b->unit)->cgroup_notify_empty(b->unit);
508 }
509
510 return 0;
511 }
512
513 CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) {
514 CGroupBonding *b;
515
516 assert(controller);
517
518 LIST_FOREACH(by_unit, b, first)
519 if (streq(b->controller, controller))
520 return b;
521
522 return NULL;
523 }