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