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