]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/cgroup.c
util: split-out hwclock.[ch]
[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"
8e274523 34
8e274523
LP
35int cgroup_bonding_realize(CGroupBonding *b) {
36 int r;
37
38 assert(b);
39 assert(b->path);
40 assert(b->controller);
41
ab1f0633
LP
42 r = cg_create(b->controller, b->path);
43 if (r < 0) {
44 log_warning("Failed to create cgroup %s:%s: %s", b->controller, b->path, strerror(-r));
8c6db833 45 return r;
ab1f0633 46 }
8e274523 47
8c6db833 48 b->realized = true;
8e274523 49
8e274523 50 return 0;
8e274523
LP
51}
52
53int cgroup_bonding_realize_list(CGroupBonding *first) {
54 CGroupBonding *b;
8c6db833 55 int r;
8e274523 56
8c6db833 57 LIST_FOREACH(by_unit, b, first)
d686d8a9 58 if ((r = cgroup_bonding_realize(b)) < 0 && b->essential)
8e274523 59 return r;
8e274523
LP
60
61 return 0;
62}
63
8d53b453 64void cgroup_bonding_free(CGroupBonding *b, bool trim) {
8e274523
LP
65 assert(b);
66
67 if (b->unit) {
68 CGroupBonding *f;
69
ac155bb8 70 LIST_REMOVE(CGroupBonding, by_unit, b->unit->cgroup_bondings, b);
8e274523 71
d686d8a9 72 if (streq(b->controller, SYSTEMD_CGROUP_CONTROLLER)) {
ac155bb8 73 assert_se(f = hashmap_get(b->unit->manager->cgroup_bondings, b->path));
d686d8a9 74 LIST_REMOVE(CGroupBonding, by_path, f, b);
8e274523 75
d686d8a9 76 if (f)
ac155bb8 77 hashmap_replace(b->unit->manager->cgroup_bondings, b->path, f);
d686d8a9 78 else
ac155bb8 79 hashmap_remove(b->unit->manager->cgroup_bondings, b->path);
d686d8a9 80 }
8e274523
LP
81 }
82
8d53b453
LP
83 if (b->realized && b->ours && trim)
84 cg_trim(b->controller, b->path, false);
8e274523 85
c9106f61
LP
86 free(b->controller);
87 free(b->path);
8e274523
LP
88 free(b);
89}
90
38c52d46 91void cgroup_bonding_free_list(CGroupBonding *first, bool remove_or_trim) {
8e274523
LP
92 CGroupBonding *b, *n;
93
94 LIST_FOREACH_SAFE(by_unit, b, n, first)
38c52d46 95 cgroup_bonding_free(b, remove_or_trim);
8e274523
LP
96}
97
fb385181
LP
98void cgroup_bonding_trim(CGroupBonding *b, bool delete_root) {
99 assert(b);
100
d686d8a9 101 if (b->realized && b->ours)
fb385181
LP
102 cg_trim(b->controller, b->path, delete_root);
103}
104
105void cgroup_bonding_trim_list(CGroupBonding *first, bool delete_root) {
106 CGroupBonding *b;
107
108 LIST_FOREACH(by_unit, b, first)
109 cgroup_bonding_trim(b, delete_root);
110}
111
ecedd90f
LP
112
113int cgroup_bonding_install(CGroupBonding *b, pid_t pid, const char *cgroup_suffix) {
114 char *p = NULL;
115 const char *path;
8e274523
LP
116 int r;
117
118 assert(b);
119 assert(pid >= 0);
120
ecedd90f
LP
121 if (cgroup_suffix) {
122 p = join(b->path, "/", cgroup_suffix, NULL);
123 if (!p)
124 return -ENOMEM;
125
126 path = p;
127 } else
128 path = b->path;
129
130 r = cg_create_and_attach(b->controller, path, pid);
131 free(p);
132
133 if (r < 0)
8c6db833 134 return r;
8e274523 135
8c6db833 136 b->realized = true;
8e274523
LP
137 return 0;
138}
139
ecedd90f 140int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid, const char *cgroup_suffix) {
8e274523 141 CGroupBonding *b;
8c6db833 142 int r;
8e274523 143
ecedd90f
LP
144 LIST_FOREACH(by_unit, b, first) {
145 r = cgroup_bonding_install(b, pid, cgroup_suffix);
146 if (r < 0 && b->essential)
8e274523 147 return r;
ecedd90f 148 }
8e274523
LP
149
150 return 0;
151}
152
64747e2d
LP
153int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid) {
154 assert(b);
155
156 if (!b->realized)
157 return -EINVAL;
158
159 return cg_set_group_access(b->controller, b->path, mode, uid, gid);
160}
161
162int cgroup_bonding_set_group_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid) {
163 CGroupBonding *b;
164 int r;
165
166 LIST_FOREACH(by_unit, b, first) {
167 r = cgroup_bonding_set_group_access(b, mode, uid, gid);
168 if (r < 0)
169 return r;
170 }
171
172 return 0;
173}
174
8d53b453 175int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky) {
64747e2d
LP
176 assert(b);
177
178 if (!b->realized)
179 return -EINVAL;
180
8d53b453 181 return cg_set_task_access(b->controller, b->path, mode, uid, gid, sticky);
64747e2d
LP
182}
183
8d53b453 184int cgroup_bonding_set_task_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid, int sticky) {
64747e2d
LP
185 CGroupBonding *b;
186 int r;
187
188 LIST_FOREACH(by_unit, b, first) {
8d53b453 189 r = cgroup_bonding_set_task_access(b, mode, uid, gid, sticky);
64747e2d
LP
190 if (r < 0)
191 return r;
192 }
193
194 return 0;
195}
196
88f3e0c9 197int cgroup_bonding_kill(CGroupBonding *b, int sig, bool sigcont, bool rem, Set *s, const char *cgroup_suffix) {
ecedd90f
LP
198 char *p = NULL;
199 const char *path;
200 int r;
201
8e274523 202 assert(b);
8c6db833 203 assert(sig >= 0);
8e274523 204
d686d8a9 205 /* Don't kill cgroups that aren't ours */
31e54cc8 206 if (!b->ours)
d686d8a9 207 return 0;
8c6db833 208
ecedd90f
LP
209 if (cgroup_suffix) {
210 p = join(b->path, "/", cgroup_suffix, NULL);
211 if (!p)
212 return -ENOMEM;
213
214 path = p;
215 } else
216 path = b->path;
217
88f3e0c9 218 r = cg_kill_recursive(b->controller, path, sig, sigcont, true, rem, s);
ecedd90f
LP
219 free(p);
220
221 return r;
8e274523
LP
222}
223
88f3e0c9 224int cgroup_bonding_kill_list(CGroupBonding *first, int sig, bool sigcont, bool rem, Set *s, const char *cgroup_suffix) {
8e274523 225 CGroupBonding *b;
ca949c9d
LP
226 Set *allocated_set = NULL;
227 int ret = -EAGAIN, r;
228
8f53a7b8
LP
229 if (!first)
230 return 0;
231
ca949c9d
LP
232 if (!s)
233 if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func)))
234 return -ENOMEM;
8e274523
LP
235
236 LIST_FOREACH(by_unit, b, first) {
88f3e0c9 237 r = cgroup_bonding_kill(b, sig, sigcont, rem, s, cgroup_suffix);
ecedd90f 238 if (r < 0) {
8c6db833 239 if (r == -EAGAIN || r == -ESRCH)
50159e6a 240 continue;
8e274523 241
ca949c9d
LP
242 ret = r;
243 goto finish;
50159e6a
LP
244 }
245
ca949c9d
LP
246 if (ret < 0 || r > 0)
247 ret = r;
8e274523
LP
248 }
249
ca949c9d
LP
250finish:
251 if (allocated_set)
252 set_free(allocated_set);
253
254 return ret;
8e274523
LP
255}
256
257/* Returns 1 if the group is empty, 0 if it is not, -EAGAIN if we
258 * cannot know */
259int cgroup_bonding_is_empty(CGroupBonding *b) {
8e274523
LP
260 int r;
261
262 assert(b);
263
8c6db833
LP
264 if ((r = cg_is_empty_recursive(b->controller, b->path, true)) < 0)
265 return r;
8e274523 266
8c6db833
LP
267 /* If it is empty it is empty */
268 if (r > 0)
8e274523
LP
269 return 1;
270
8c6db833 271 /* It's not only us using this cgroup, so we just don't know */
d686d8a9 272 return b->ours ? 0 : -EAGAIN;
8e274523
LP
273}
274
275int cgroup_bonding_is_empty_list(CGroupBonding *first) {
276 CGroupBonding *b;
277
278 LIST_FOREACH(by_unit, b, first) {
279 int r;
280
281 if ((r = cgroup_bonding_is_empty(b)) < 0) {
282 /* If this returned -EAGAIN, then we don't know if the
283 * group is empty, so let's see if another group can
284 * tell us */
285
286 if (r != -EAGAIN)
287 return r;
288 } else
289 return r;
290 }
291
292 return -EAGAIN;
293}
294
8e274523 295int manager_setup_cgroup(Manager *m) {
c6c18be3 296 char *current = NULL, *path = NULL;
8e274523 297 int r;
7ccfb64a 298 char suffix[32];
8e274523
LP
299
300 assert(m);
301
e5a53dc7 302 /* 0. Be nice to Ingo Molnar #628004 */
0c85a4f3 303 if (path_is_mount_point("/sys/fs/cgroup/systemd", false) <= 0) {
e5a53dc7
LP
304 log_warning("No control group support available, not creating root group.");
305 return 0;
306 }
307
35d2e7ec 308 /* 1. Determine hierarchy */
9156e799
LP
309 r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 0, &current);
310 if (r < 0) {
12235040 311 log_error("Cannot determine cgroup we are running in: %s", strerror(-r));
c6c18be3 312 goto finish;
12235040 313 }
8e274523 314
0baf24dd
LP
315 if (m->running_as == MANAGER_SYSTEM)
316 strcpy(suffix, "/system");
317 else {
318 snprintf(suffix, sizeof(suffix), "/systemd-%lu", (unsigned long) getpid());
319 char_array_0(suffix);
320 }
7ccfb64a 321
8e274523 322 free(m->cgroup_hierarchy);
c6c18be3 323 if (endswith(current, suffix)) {
7ccfb64a 324 /* We probably got reexecuted and can continue to use our root cgroup */
c6c18be3
LP
325 m->cgroup_hierarchy = current;
326 current = NULL;
7ccfb64a 327
c6c18be3
LP
328 } else {
329 /* We need a new root cgroup */
7ccfb64a 330 m->cgroup_hierarchy = NULL;
e364ad06 331 if (asprintf(&m->cgroup_hierarchy, "%s%s", streq(current, "/") ? "" : current, suffix) < 0) {
12235040 332 log_error("Out of memory");
c6c18be3
LP
333 r = -ENOMEM;
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}