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