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