]> git.ipfire.org Git - people/ms/systemd.git/blob - cgroup.c
cgroup: add cgroup_bonding_to_string() call
[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 bool killed = false;
178
179 assert(b);
180 assert(sig > 0);
181
182 if (!b->only_us)
183 return -EAGAIN;
184
185 if (!(s = set_new(trivial_hash_func, trivial_compare_func)))
186 return -ENOMEM;
187
188 log_debug("Killing processes from process group %s:%s", b->controller, b->path);
189
190 do {
191 void *iterator;
192 pid_t pid;
193
194 done = true;
195
196 if ((r = cgroup_get_task_begin(b->path, b->controller, &iterator, &pid)) != 0) {
197 if (r == ECGEOF) {
198 r = 0;
199 goto kill_done;
200 } else {
201 r = translate_error(r, errno);
202 break;
203 }
204 }
205
206 for (;;) {
207 if (set_get(s, INT_TO_PTR(pid)) != INT_TO_PTR(pid)) {
208
209 /* If we haven't killed this process
210 * yet, kill it */
211
212 if (kill(pid, sig) < 0 && errno != ESRCH) {
213 r = -errno;
214 break;
215 }
216
217 killed = true;
218 done = false;
219
220 if ((r = set_put(s, INT_TO_PTR(pid))) < 0)
221 break;
222 }
223
224 if ((r = cgroup_get_task_next(&iterator, &pid)) != 0) {
225
226 if (r == ECGEOF)
227 r = 0;
228 else
229 r = translate_error(r, errno);
230
231 break;
232 }
233 }
234
235 kill_done:
236 assert_se(cgroup_get_task_end(&iterator) == 0);
237
238 /* To avoid racing against processes which fork
239 * quicker than we can kill them we repeat this until
240 * no new pids need to be killed. */
241
242 } while (!done && r >= 0);
243
244 set_free(s);
245
246 if (r < 0)
247 return r;
248
249 return killed ? 0 : -ESRCH;
250 }
251
252 int cgroup_bonding_kill_list(CGroupBonding *first, int sig) {
253 CGroupBonding *b;
254 int r = -EAGAIN;
255
256 LIST_FOREACH(by_unit, b, first) {
257 if ((r = cgroup_bonding_kill(b, sig)) < 0) {
258 if (r == -EAGAIN || -ESRCH)
259 continue;
260
261 return r;
262 }
263
264 return 0;
265 }
266
267 return r;
268 }
269
270 /* Returns 1 if the group is empty, 0 if it is not, -EAGAIN if we
271 * cannot know */
272 int cgroup_bonding_is_empty(CGroupBonding *b) {
273 void *iterator;
274 pid_t pid;
275 int r;
276
277 assert(b);
278
279 r = cgroup_get_task_begin(b->path, b->controller, &iterator, &pid);
280
281 if (r == 0 || r == ECGEOF)
282 cgroup_get_task_end(&iterator);
283
284 /* Hmm, no PID in this group? Then it is definitely empty */
285 if (r == ECGEOF)
286 return 1;
287
288 /* Some error? Let's return it */
289 if (r != 0)
290 return translate_error(r, errno);
291
292 /* It's not empty, and we are the only user, then it is
293 * definitely not empty */
294 if (b->only_us)
295 return 0;
296
297 /* There are PIDs in the group but we aren't the only users,
298 * hence we cannot say */
299 return -EAGAIN;
300 }
301
302 int cgroup_bonding_is_empty_list(CGroupBonding *first) {
303 CGroupBonding *b;
304
305 LIST_FOREACH(by_unit, b, first) {
306 int r;
307
308 if ((r = cgroup_bonding_is_empty(b)) < 0) {
309 /* If this returned -EAGAIN, then we don't know if the
310 * group is empty, so let's see if another group can
311 * tell us */
312
313 if (r != -EAGAIN)
314 return r;
315 } else
316 return r;
317 }
318
319 return -EAGAIN;
320 }
321
322 static int install_release_agent(Manager *m, const char *mount_point) {
323 char *p, *c, *sc;
324 int r;
325
326 assert(m);
327 assert(mount_point);
328
329 if (asprintf(&p, "%s/release_agent", mount_point) < 0)
330 return -ENOMEM;
331
332 if ((r = read_one_line_file(p, &c)) < 0) {
333 free(p);
334 return r;
335 }
336
337 sc = strstrip(c);
338
339 if (sc[0] == 0) {
340 if ((r = write_one_line_file(p, CGROUP_AGENT_PATH "\n" )) < 0) {
341 free(p);
342 free(c);
343 return r;
344 }
345 } else if (!streq(sc, CGROUP_AGENT_PATH)) {
346 free(p);
347 free(c);
348 return -EEXIST;
349 }
350
351 free(c);
352 free(p);
353
354 if (asprintf(&p, "%s/notify_on_release", mount_point) < 0)
355 return -ENOMEM;
356
357 if ((r = read_one_line_file(p, &c)) < 0) {
358 free(p);
359 return r;
360 }
361
362 sc = strstrip(c);
363
364 if (streq(sc, "0")) {
365 if ((r = write_one_line_file(p, "1\n")) < 0) {
366 free(p);
367 free(c);
368 return r;
369 }
370 } else if (!streq(sc, "1")) {
371 free(p);
372 free(c);
373 return -EIO;
374 }
375
376 free(p);
377 free(c);
378
379 return 0;
380 }
381
382 static int create_hierarchy_cgroup(Manager *m) {
383 struct cgroup *cg;
384 int r;
385
386 assert(m);
387
388 if (!(cg = cgroup_new_cgroup(m->cgroup_hierarchy)))
389 return -ENOMEM;
390
391 if (!(cgroup_add_controller(cg, m->cgroup_controller))) {
392 r = -ENOMEM;
393 goto finish;
394 }
395
396 if ((r = cgroup_create_cgroup(cg, true)) != 0) {
397 log_error("Failed to create cgroup hierarchy group: %s", cgroup_strerror(r));
398 r = translate_error(r, errno);
399 goto finish;
400 }
401
402 if ((r = cgroup_attach_task(cg)) != 0) {
403 log_error("Failed to add ourselves to hierarchy group: %s", cgroup_strerror(r));
404 r = translate_error(r, errno);
405 goto finish;
406 }
407
408 r = 0;
409
410 finish:
411 cgroup_free(&cg);
412 return r;
413 }
414
415 int manager_setup_cgroup(Manager *m) {
416 char *mp, *cp;
417 int r;
418 pid_t pid;
419
420 assert(m);
421
422 if ((r = cgroup_init()) != 0) {
423 log_error("Failed to initialize libcg: %s", cgroup_strerror(r));
424 return translate_error(r, errno);
425 }
426
427 free(m->cgroup_controller);
428 if (!(m->cgroup_controller = strdup("debug")))
429 return -ENOMEM;
430
431 if ((r = cgroup_get_subsys_mount_point(m->cgroup_controller, &mp)))
432 return translate_error(r, errno);
433
434 pid = getpid();
435
436 if ((r = cgroup_get_current_controller_path(pid, m->cgroup_controller, &cp))) {
437 free(mp);
438 return translate_error(r, errno);
439 }
440
441 free(m->cgroup_hierarchy);
442 m->cgroup_hierarchy = NULL;
443 if (asprintf(&m->cgroup_hierarchy, "%s/systemd-%llu", strcmp(cp, "/") == 0 ? "" : cp, (unsigned long long) pid) < 0) {
444 free(cp);
445 free(mp);
446 return -ENOMEM;
447 }
448
449 log_info("Using cgroup controller <%s>, hierarchy mounted at <%s>, using root group <%s>.",
450 m->cgroup_controller,
451 mp,
452 m->cgroup_hierarchy);
453
454 if ((r = install_release_agent(m, mp)) < 0)
455 log_warning("Failed to install release agent, ignoring: %s", strerror(-r));
456 else
457 log_info("Installed release agent, or already installed.");
458
459 free(mp);
460 free(cp);
461
462 if ((r = create_hierarchy_cgroup(m)) < 0)
463 log_error("Failed to create root cgroup hierarchy: %s", strerror(-r));
464 else
465 log_info("Created root group.");
466
467 return r;
468 }
469
470 int manager_shutdown_cgroup(Manager *m) {
471 struct cgroup *cg;
472 int r;
473
474 assert(m);
475
476 if (!m->cgroup_hierarchy)
477 return 0;
478
479 if (!(cg = cgroup_new_cgroup(m->cgroup_hierarchy)))
480 return -ENOMEM;
481
482 if (!(cgroup_add_controller(cg, m->cgroup_controller))) {
483 r = -ENOMEM;
484 goto finish;
485 }
486
487 if ((r = cgroup_delete_cgroup_ext(cg, CGFLAG_DELETE_IGNORE_MIGRATION|CGFLAG_DELETE_RECURSIVE)) != 0) {
488 log_error("Failed to delete cgroup hierarchy group: %s", cgroup_strerror(r));
489 r = translate_error(r, errno);
490 goto finish;
491 }
492 r = 0;
493
494 finish:
495 cgroup_free(&cg);
496 return r;
497
498 }
499
500 int cgroup_notify_empty(Manager *m, const char *group) {
501 CGroupBonding *l, *b;
502
503 assert(m);
504 assert(group);
505
506 if (!(l = hashmap_get(m->cgroup_bondings, group)))
507 return 0;
508
509 LIST_FOREACH(by_path, b, l) {
510 int t;
511
512 if (!b->unit)
513 continue;
514
515 if ((t = cgroup_bonding_is_empty_list(b)) < 0) {
516
517 /* If we don't know, we don't know */
518 if (t != -EAGAIN)
519 log_warning("Failed to check whether cgroup is empty: %s", strerror(errno));
520
521 continue;
522 }
523
524 if (t > 0)
525 if (UNIT_VTABLE(b->unit)->cgroup_notify_empty)
526 UNIT_VTABLE(b->unit)->cgroup_notify_empty(b->unit);
527 }
528
529 return 0;
530 }
531
532 CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) {
533 CGroupBonding *b;
534
535 assert(controller);
536
537 LIST_FOREACH(by_unit, b, first)
538 if (streq(b->controller, controller))
539 return b;
540
541 return NULL;
542 }
543
544 char *cgroup_bonding_to_string(CGroupBonding *b) {
545 char *r;
546
547 assert(b);
548
549 if (asprintf(&r, "%s:%s", b->controller, b->path) < 0)
550 return NULL;
551
552 return r;
553 }