]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/cgroup.c
cgroup: if a controller is not available don't try to create cgroups in its hierarchy
[thirdparty/systemd.git] / src / core / cgroup.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
8e274523
LP
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
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
8e274523
LP
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
5430f7f2 16 Lesser General Public License for more details.
8e274523 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
8e274523
LP
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>
c6c18be3 28#include <fcntl.h>
8c6db833 29
8e274523 30#include "cgroup.h"
8c6db833 31#include "cgroup-util.h"
8e274523
LP
32#include "log.h"
33
8e274523
LP
34int cgroup_bonding_realize(CGroupBonding *b) {
35 int r;
36
37 assert(b);
38 assert(b->path);
39 assert(b->controller);
40
ab1f0633
LP
41 r = cg_create(b->controller, b->path);
42 if (r < 0) {
43 log_warning("Failed to create cgroup %s:%s: %s", b->controller, b->path, strerror(-r));
8c6db833 44 return r;
ab1f0633 45 }
8e274523 46
8c6db833 47 b->realized = true;
8e274523 48
8e274523 49 return 0;
8e274523
LP
50}
51
52int cgroup_bonding_realize_list(CGroupBonding *first) {
53 CGroupBonding *b;
8c6db833 54 int r;
8e274523 55
8c6db833 56 LIST_FOREACH(by_unit, b, first)
d686d8a9 57 if ((r = cgroup_bonding_realize(b)) < 0 && b->essential)
8e274523 58 return r;
8e274523
LP
59
60 return 0;
61}
62
8d53b453 63void cgroup_bonding_free(CGroupBonding *b, bool trim) {
8e274523
LP
64 assert(b);
65
66 if (b->unit) {
67 CGroupBonding *f;
68
ac155bb8 69 LIST_REMOVE(CGroupBonding, by_unit, b->unit->cgroup_bondings, b);
8e274523 70
d686d8a9 71 if (streq(b->controller, SYSTEMD_CGROUP_CONTROLLER)) {
ac155bb8 72 assert_se(f = hashmap_get(b->unit->manager->cgroup_bondings, b->path));
d686d8a9 73 LIST_REMOVE(CGroupBonding, by_path, f, b);
8e274523 74
d686d8a9 75 if (f)
ac155bb8 76 hashmap_replace(b->unit->manager->cgroup_bondings, b->path, f);
d686d8a9 77 else
ac155bb8 78 hashmap_remove(b->unit->manager->cgroup_bondings, b->path);
d686d8a9 79 }
8e274523
LP
80 }
81
8d53b453
LP
82 if (b->realized && b->ours && trim)
83 cg_trim(b->controller, b->path, false);
8e274523 84
c9106f61
LP
85 free(b->controller);
86 free(b->path);
8e274523
LP
87 free(b);
88}
89
38c52d46 90void cgroup_bonding_free_list(CGroupBonding *first, bool remove_or_trim) {
8e274523
LP
91 CGroupBonding *b, *n;
92
93 LIST_FOREACH_SAFE(by_unit, b, n, first)
38c52d46 94 cgroup_bonding_free(b, remove_or_trim);
8e274523
LP
95}
96
fb385181
LP
97void cgroup_bonding_trim(CGroupBonding *b, bool delete_root) {
98 assert(b);
99
d686d8a9 100 if (b->realized && b->ours)
fb385181
LP
101 cg_trim(b->controller, b->path, delete_root);
102}
103
104void cgroup_bonding_trim_list(CGroupBonding *first, bool delete_root) {
105 CGroupBonding *b;
106
107 LIST_FOREACH(by_unit, b, first)
108 cgroup_bonding_trim(b, delete_root);
109}
110
ecedd90f
LP
111
112int cgroup_bonding_install(CGroupBonding *b, pid_t pid, const char *cgroup_suffix) {
113 char *p = NULL;
114 const char *path;
8e274523
LP
115 int r;
116
117 assert(b);
118 assert(pid >= 0);
119
ecedd90f
LP
120 if (cgroup_suffix) {
121 p = join(b->path, "/", cgroup_suffix, NULL);
122 if (!p)
123 return -ENOMEM;
124
125 path = p;
126 } else
127 path = b->path;
128
129 r = cg_create_and_attach(b->controller, path, pid);
130 free(p);
131
132 if (r < 0)
8c6db833 133 return r;
8e274523 134
8c6db833 135 b->realized = true;
8e274523
LP
136 return 0;
137}
138
ecedd90f 139int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid, const char *cgroup_suffix) {
8e274523 140 CGroupBonding *b;
8c6db833 141 int r;
8e274523 142
ecedd90f
LP
143 LIST_FOREACH(by_unit, b, first) {
144 r = cgroup_bonding_install(b, pid, cgroup_suffix);
145 if (r < 0 && b->essential)
8e274523 146 return r;
ecedd90f 147 }
8e274523
LP
148
149 return 0;
150}
151
64747e2d
LP
152int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid) {
153 assert(b);
154
155 if (!b->realized)
156 return -EINVAL;
157
158 return cg_set_group_access(b->controller, b->path, mode, uid, gid);
159}
160
161int cgroup_bonding_set_group_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid) {
162 CGroupBonding *b;
163 int r;
164
165 LIST_FOREACH(by_unit, b, first) {
166 r = cgroup_bonding_set_group_access(b, mode, uid, gid);
167 if (r < 0)
168 return r;
169 }
170
171 return 0;
172}
173
8d53b453 174int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky) {
64747e2d
LP
175 assert(b);
176
177 if (!b->realized)
178 return -EINVAL;
179
8d53b453 180 return cg_set_task_access(b->controller, b->path, mode, uid, gid, sticky);
64747e2d
LP
181}
182
8d53b453 183int cgroup_bonding_set_task_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid, int sticky) {
64747e2d
LP
184 CGroupBonding *b;
185 int r;
186
187 LIST_FOREACH(by_unit, b, first) {
8d53b453 188 r = cgroup_bonding_set_task_access(b, mode, uid, gid, sticky);
64747e2d
LP
189 if (r < 0)
190 return r;
191 }
192
193 return 0;
194}
195
ecedd90f
LP
196int cgroup_bonding_kill(CGroupBonding *b, int sig, bool sigcont, Set *s, const char *cgroup_suffix) {
197 char *p = NULL;
198 const char *path;
199 int r;
200
8e274523 201 assert(b);
8c6db833 202 assert(sig >= 0);
8e274523 203
d686d8a9 204 /* Don't kill cgroups that aren't ours */
31e54cc8 205 if (!b->ours)
d686d8a9 206 return 0;
8c6db833 207
ecedd90f
LP
208 if (cgroup_suffix) {
209 p = join(b->path, "/", cgroup_suffix, NULL);
210 if (!p)
211 return -ENOMEM;
212
213 path = p;
214 } else
215 path = b->path;
216
217 r = cg_kill_recursive(b->controller, path, sig, sigcont, true, false, s);
218 free(p);
219
220 return r;
8e274523
LP
221}
222
ecedd90f 223int cgroup_bonding_kill_list(CGroupBonding *first, int sig, bool sigcont, Set *s, const char *cgroup_suffix) {
8e274523 224 CGroupBonding *b;
ca949c9d
LP
225 Set *allocated_set = NULL;
226 int ret = -EAGAIN, r;
227
8f53a7b8
LP
228 if (!first)
229 return 0;
230
ca949c9d
LP
231 if (!s)
232 if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func)))
233 return -ENOMEM;
8e274523
LP
234
235 LIST_FOREACH(by_unit, b, first) {
ecedd90f
LP
236 r = cgroup_bonding_kill(b, sig, sigcont, s, cgroup_suffix);
237 if (r < 0) {
8c6db833 238 if (r == -EAGAIN || r == -ESRCH)
50159e6a 239 continue;
8e274523 240
ca949c9d
LP
241 ret = r;
242 goto finish;
50159e6a
LP
243 }
244
ca949c9d
LP
245 if (ret < 0 || r > 0)
246 ret = r;
8e274523
LP
247 }
248
ca949c9d
LP
249finish:
250 if (allocated_set)
251 set_free(allocated_set);
252
253 return ret;
8e274523
LP
254}
255
256/* Returns 1 if the group is empty, 0 if it is not, -EAGAIN if we
257 * cannot know */
258int cgroup_bonding_is_empty(CGroupBonding *b) {
8e274523
LP
259 int r;
260
261 assert(b);
262
8c6db833
LP
263 if ((r = cg_is_empty_recursive(b->controller, b->path, true)) < 0)
264 return r;
8e274523 265
8c6db833
LP
266 /* If it is empty it is empty */
267 if (r > 0)
8e274523
LP
268 return 1;
269
8c6db833 270 /* It's not only us using this cgroup, so we just don't know */
d686d8a9 271 return b->ours ? 0 : -EAGAIN;
8e274523
LP
272}
273
274int cgroup_bonding_is_empty_list(CGroupBonding *first) {
275 CGroupBonding *b;
276
277 LIST_FOREACH(by_unit, b, first) {
278 int r;
279
280 if ((r = cgroup_bonding_is_empty(b)) < 0) {
281 /* If this returned -EAGAIN, then we don't know if the
282 * group is empty, so let's see if another group can
283 * tell us */
284
285 if (r != -EAGAIN)
286 return r;
287 } else
288 return r;
289 }
290
291 return -EAGAIN;
292}
293
8e274523 294int manager_setup_cgroup(Manager *m) {
c6c18be3 295 char *current = NULL, *path = NULL;
8e274523 296 int r;
7ccfb64a 297 char suffix[32];
8e274523
LP
298
299 assert(m);
300
e5a53dc7 301 /* 0. Be nice to Ingo Molnar #628004 */
0c85a4f3 302 if (path_is_mount_point("/sys/fs/cgroup/systemd", false) <= 0) {
e5a53dc7
LP
303 log_warning("No control group support available, not creating root group.");
304 return 0;
305 }
306
35d2e7ec 307 /* 1. Determine hierarchy */
12235040
LP
308 if ((r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 0, &current)) < 0) {
309 log_error("Cannot determine cgroup we are running in: %s", strerror(-r));
c6c18be3 310 goto finish;
12235040 311 }
8e274523 312
0baf24dd
LP
313 if (m->running_as == MANAGER_SYSTEM)
314 strcpy(suffix, "/system");
315 else {
316 snprintf(suffix, sizeof(suffix), "/systemd-%lu", (unsigned long) getpid());
317 char_array_0(suffix);
318 }
7ccfb64a 319
8e274523 320 free(m->cgroup_hierarchy);
c6c18be3 321 if (endswith(current, suffix)) {
7ccfb64a 322 /* We probably got reexecuted and can continue to use our root cgroup */
c6c18be3
LP
323 m->cgroup_hierarchy = current;
324 current = NULL;
7ccfb64a 325
c6c18be3
LP
326 } else {
327 /* We need a new root cgroup */
7ccfb64a 328 m->cgroup_hierarchy = NULL;
e364ad06 329 if (asprintf(&m->cgroup_hierarchy, "%s%s", streq(current, "/") ? "" : current, suffix) < 0) {
12235040 330 log_error("Out of memory");
c6c18be3
LP
331 r = -ENOMEM;
332 goto finish;
333 }
8e274523
LP
334 }
335
35d2e7ec 336 /* 2. Show data */
3474ae3c
LP
337 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, NULL, &path);
338 if (r < 0) {
12235040 339 log_error("Cannot find cgroup mount point: %s", strerror(-r));
c6c18be3 340 goto finish;
12235040 341 }
8e274523 342
c6c18be3
LP
343 log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
344
35d2e7ec 345 /* 3. Install agent */
3474ae3c
LP
346 r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH);
347 if (r < 0)
8e274523 348 log_warning("Failed to install release agent, ignoring: %s", strerror(-r));
c6c18be3
LP
349 else if (r > 0)
350 log_debug("Installed release agent.");
8e274523 351 else
c6c18be3 352 log_debug("Release agent already installed.");
8e274523 353
35d2e7ec 354 /* 4. Realize the group */
c6c18be3 355 if ((r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, 0)) < 0) {
8e274523 356 log_error("Failed to create root cgroup hierarchy: %s", strerror(-r));
c6c18be3
LP
357 goto finish;
358 }
359
35d2e7ec 360 /* 5. And pin it, so that it cannot be unmounted */
c6c18be3
LP
361 if (m->pin_cgroupfs_fd >= 0)
362 close_nointr_nofail(m->pin_cgroupfs_fd);
363
364 if ((m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK)) < 0) {
12235040 365 log_error("Failed to open pin file: %m");
c6c18be3
LP
366 r = -errno;
367 goto finish;
368 }
369
370 log_debug("Created root group.");
371
372finish:
373 free(current);
374 free(path);
8e274523
LP
375
376 return r;
377}
378
c6c18be3 379void manager_shutdown_cgroup(Manager *m, bool delete) {
8e274523
LP
380 assert(m);
381
c6c18be3
LP
382 if (delete && m->cgroup_hierarchy)
383 cg_delete(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy);
8e274523 384
c6c18be3
LP
385 if (m->pin_cgroupfs_fd >= 0) {
386 close_nointr_nofail(m->pin_cgroupfs_fd);
387 m->pin_cgroupfs_fd = -1;
388 }
389
390 free(m->cgroup_hierarchy);
391 m->cgroup_hierarchy = NULL;
8e274523
LP
392}
393
acb14d31
LP
394int cgroup_bonding_get(Manager *m, const char *cgroup, CGroupBonding **bonding) {
395 CGroupBonding *b;
396 char *p;
397
398 assert(m);
399 assert(cgroup);
400 assert(bonding);
401
402 b = hashmap_get(m->cgroup_bondings, cgroup);
5c72face 403 if (b) {
acb14d31
LP
404 *bonding = b;
405 return 1;
406 }
407
408 p = strdup(cgroup);
409 if (!p)
410 return -ENOMEM;
411
412 for (;;) {
413 char *e;
414
415 e = strrchr(p, '/');
416 if (!e || e == p) {
417 free(p);
418 *bonding = NULL;
419 return 0;
420 }
421
422 *e = 0;
423
424 b = hashmap_get(m->cgroup_bondings, p);
425 if (b) {
426 free(p);
427 *bonding = b;
428 return 1;
429 }
430 }
431}
432
8e274523
LP
433int cgroup_notify_empty(Manager *m, const char *group) {
434 CGroupBonding *l, *b;
acb14d31 435 int r;
8e274523
LP
436
437 assert(m);
438 assert(group);
439
acb14d31
LP
440 r = cgroup_bonding_get(m, group, &l);
441 if (r <= 0)
442 return r;
8e274523
LP
443
444 LIST_FOREACH(by_path, b, l) {
445 int t;
446
447 if (!b->unit)
448 continue;
449
353fa6a2
LP
450 t = cgroup_bonding_is_empty_list(b);
451 if (t < 0) {
8e274523
LP
452
453 /* If we don't know, we don't know */
454 if (t != -EAGAIN)
455 log_warning("Failed to check whether cgroup is empty: %s", strerror(errno));
456
457 continue;
458 }
459
353fa6a2
LP
460 if (t > 0) {
461 /* If it is empty, let's delete it */
ac155bb8 462 cgroup_bonding_trim_list(b->unit->cgroup_bondings, true);
353fa6a2 463
8e274523
LP
464 if (UNIT_VTABLE(b->unit)->cgroup_notify_empty)
465 UNIT_VTABLE(b->unit)->cgroup_notify_empty(b->unit);
353fa6a2 466 }
8e274523
LP
467 }
468
469 return 0;
470}
471
8c47c732
LP
472Unit* cgroup_unit_by_pid(Manager *m, pid_t pid) {
473 CGroupBonding *l, *b;
474 char *group = NULL;
8c47c732
LP
475
476 assert(m);
477
478 if (pid <= 1)
479 return NULL;
480
e364ad06 481 if (cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &group) < 0)
8c47c732
LP
482 return NULL;
483
484 l = hashmap_get(m->cgroup_bondings, group);
4455bcd0
LP
485
486 if (!l) {
487 char *slash;
488
489 while ((slash = strrchr(group, '/'))) {
490 if (slash == group)
491 break;
492
493 *slash = 0;
494
495 if ((l = hashmap_get(m->cgroup_bondings, group)))
496 break;
497 }
498 }
499
8c47c732
LP
500 free(group);
501
8c47c732
LP
502 LIST_FOREACH(by_path, b, l) {
503
504 if (!b->unit)
505 continue;
506
d686d8a9 507 if (b->ours)
8c47c732
LP
508 return b->unit;
509 }
510
511 return NULL;
512}
513
8e274523
LP
514CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) {
515 CGroupBonding *b;
516
517 assert(controller);
518
519 LIST_FOREACH(by_unit, b, first)
520 if (streq(b->controller, controller))
521 return b;
522
523 return NULL;
524}
6dde1f33
LP
525
526char *cgroup_bonding_to_string(CGroupBonding *b) {
527 char *r;
528
529 assert(b);
530
531 if (asprintf(&r, "%s:%s", b->controller, b->path) < 0)
532 return NULL;
533
534 return r;
535}
4fbf50b3
LP
536
537pid_t cgroup_bonding_search_main_pid(CGroupBonding *b) {
538 FILE *f;
2633eb83 539 pid_t pid = 0, npid, mypid;
4fbf50b3
LP
540
541 assert(b);
542
d686d8a9 543 if (!b->ours)
4fbf50b3
LP
544 return 0;
545
bd40a2d8 546 if (cg_enumerate_processes(b->controller, b->path, &f) < 0)
4fbf50b3
LP
547 return 0;
548
2633eb83
LP
549 mypid = getpid();
550
bd40a2d8 551 while (cg_read_pid(f, &npid) > 0) {
2633eb83 552 pid_t ppid;
4fbf50b3
LP
553
554 if (npid == pid)
555 continue;
556
2633eb83
LP
557 /* Ignore processes that aren't our kids */
558 if (get_parent_of_pid(npid, &ppid) >= 0 && ppid != mypid)
559 continue;
560
4fbf50b3 561 if (pid != 0) {
2633eb83
LP
562 /* Dang, there's more than one daemonized PID
563 in this group, so we don't know what process
564 is the main process. */
4fbf50b3
LP
565 pid = 0;
566 break;
567 }
568
569 pid = npid;
570 }
571
572 fclose(f);
573
574 return pid;
575}
576
577pid_t cgroup_bonding_search_main_pid_list(CGroupBonding *first) {
578 CGroupBonding *b;
579 pid_t pid;
580
581 /* Try to find a main pid from this cgroup, but checking if
582 * there's only one PID in the cgroup and returning it. Later
583 * on we might want to add additional, smarter heuristics
584 * here. */
585
586 LIST_FOREACH(by_unit, b, first)
587 if ((pid = cgroup_bonding_search_main_pid(b)) != 0)
588 return pid;
589
590 return 0;
591
592}