]> git.ipfire.org Git - people/ms/systemd.git/blob - cgroup.c
9e1b0de379a2f4d29de4aeef5753461eba0ed2e8
[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 free(p);
361 free(c);
362
363 return 0;
364 }
365
366 static int create_hierarchy_cgroup(Manager *m) {
367 struct cgroup *cg;
368 int r;
369
370 assert(m);
371
372 if (!(cg = cgroup_new_cgroup(m->cgroup_hierarchy)))
373 return -ENOMEM;
374
375 if (!(cgroup_add_controller(cg, m->cgroup_controller))) {
376 r = -ENOMEM;
377 goto finish;
378 }
379
380 if ((r = cgroup_create_cgroup(cg, true)) != 0) {
381 log_error("Failed to create cgroup hierarchy group: %s", cgroup_strerror(r));
382 r = translate_error(r, errno);
383 goto finish;
384 }
385
386 if ((r = cgroup_attach_task(cg)) != 0) {
387 log_error("Failed to add ourselves to hierarchy group: %s", cgroup_strerror(r));
388 r = translate_error(r, errno);
389 goto finish;
390 }
391
392 r = 0;
393
394 finish:
395 cgroup_free(&cg);
396 return r;
397 }
398
399 int manager_setup_cgroup(Manager *m) {
400 char *mp, *cp;
401 int r;
402 pid_t pid;
403
404 assert(m);
405
406 if ((r = cgroup_init()) != 0) {
407 log_error("Failed to initialize libcg: %s", cgroup_strerror(r));
408 return translate_error(r, errno);
409 }
410
411 free(m->cgroup_controller);
412 if (!(m->cgroup_controller = strdup("debug")))
413 return -ENOMEM;
414
415 if ((r = cgroup_get_subsys_mount_point(m->cgroup_controller, &mp)))
416 return translate_error(r, errno);
417
418 pid = getpid();
419
420 if ((r = cgroup_get_current_controller_path(pid, m->cgroup_controller, &cp))) {
421 free(mp);
422 return translate_error(r, errno);
423 }
424
425 free(m->cgroup_hierarchy);
426 m->cgroup_hierarchy = NULL;
427 if (asprintf(&m->cgroup_hierarchy, "%s/systemd-%llu", strcmp(cp, "/") == 0 ? "" : cp, (unsigned long long) pid) < 0) {
428 free(cp);
429 free(mp);
430 return -ENOMEM;
431 }
432
433 log_info("Using cgroup controller <%s>, hierarchy mounted at <%s>, using root group <%s>.",
434 m->cgroup_controller,
435 mp,
436 m->cgroup_hierarchy);
437
438 if ((r = install_release_agent(m, mp)) < 0)
439 log_warning("Failed to install release agent, ignoring: %s", strerror(-r));
440 else
441 log_info("Installed release agent, or already installed.");
442
443 free(mp);
444 free(cp);
445
446 if ((r = create_hierarchy_cgroup(m)) < 0)
447 log_error("Failed to create root cgroup hierarchy: %s", strerror(-r));
448 else
449 log_info("Created root group.");
450
451 return r;
452 }
453
454 int manager_shutdown_cgroup(Manager *m) {
455 struct cgroup *cg;
456 int r;
457
458 assert(m);
459
460 if (!m->cgroup_hierarchy)
461 return 0;
462
463 if (!(cg = cgroup_new_cgroup(m->cgroup_hierarchy)))
464 return -ENOMEM;
465
466 if (!(cgroup_add_controller(cg, m->cgroup_controller))) {
467 r = -ENOMEM;
468 goto finish;
469 }
470
471 if ((r = cgroup_delete_cgroup_ext(cg, CGFLAG_DELETE_IGNORE_MIGRATION|CGFLAG_DELETE_RECURSIVE)) != 0) {
472 log_error("Failed to delete cgroup hierarchy group: %s", cgroup_strerror(r));
473 r = translate_error(r, errno);
474 goto finish;
475 }
476 r = 0;
477
478 finish:
479 cgroup_free(&cg);
480 return r;
481
482 }
483
484 int cgroup_notify_empty(Manager *m, const char *group) {
485 CGroupBonding *l, *b;
486
487 assert(m);
488 assert(group);
489
490 if (!(l = hashmap_get(m->cgroup_bondings, group)))
491 return 0;
492
493 LIST_FOREACH(by_path, b, l) {
494 int t;
495
496 if (!b->unit)
497 continue;
498
499 if ((t = cgroup_bonding_is_empty_list(b)) < 0) {
500
501 /* If we don't know, we don't know */
502 if (t != -EAGAIN)
503 log_warning("Failed to check whether cgroup is empty: %s", strerror(errno));
504
505 continue;
506 }
507
508 if (t > 0)
509 if (UNIT_VTABLE(b->unit)->cgroup_notify_empty)
510 UNIT_VTABLE(b->unit)->cgroup_notify_empty(b->unit);
511 }
512
513 return 0;
514 }
515
516 CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) {
517 CGroupBonding *b;
518
519 assert(controller);
520
521 LIST_FOREACH(by_unit, b, first)
522 if (streq(b->controller, controller))
523 return b;
524
525 return NULL;
526 }