]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/cgroup.c
a248252bcdb26c1eb12000237410ae200d0587df
[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, NULL);
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 _cleanup_free_ 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 if (r < 0)
132 return r;
133
134 b->realized = true;
135 return 0;
136 }
137
138 int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid, const char *cgroup_suffix) {
139 CGroupBonding *b;
140 int r;
141
142 LIST_FOREACH(by_unit, b, first) {
143 r = cgroup_bonding_install(b, pid, cgroup_suffix);
144 if (r < 0 && b->essential)
145 return r;
146 }
147
148 return 0;
149 }
150
151 int cgroup_bonding_migrate(CGroupBonding *b, CGroupBonding *list) {
152 CGroupBonding *q;
153 int ret = 0;
154
155 LIST_FOREACH(by_unit, q, list) {
156 int r;
157
158 if (q == b)
159 continue;
160
161 if (!q->ours)
162 continue;
163
164 r = cg_migrate_recursive(q->controller, q->path, b->controller, b->path, true, false);
165 if (r < 0 && ret == 0)
166 ret = r;
167 }
168
169 return ret;
170 }
171
172 int cgroup_bonding_migrate_to(CGroupBonding *b, const char *target, bool rem) {
173 assert(b);
174 assert(target);
175
176 return cg_migrate_recursive(b->controller, b->path, b->controller, target, true, rem);
177 }
178
179 int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid) {
180 assert(b);
181
182 if (!b->realized)
183 return -EINVAL;
184
185 return cg_set_group_access(b->controller, b->path, mode, uid, gid);
186 }
187
188 int cgroup_bonding_set_group_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid) {
189 CGroupBonding *b;
190 int r;
191
192 LIST_FOREACH(by_unit, b, first) {
193 r = cgroup_bonding_set_group_access(b, mode, uid, gid);
194 if (r < 0)
195 return r;
196 }
197
198 return 0;
199 }
200
201 int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky) {
202 assert(b);
203
204 if (!b->realized)
205 return -EINVAL;
206
207 return cg_set_task_access(b->controller, b->path, mode, uid, gid, sticky);
208 }
209
210 int cgroup_bonding_set_task_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid, int sticky) {
211 CGroupBonding *b;
212 int r;
213
214 LIST_FOREACH(by_unit, b, first) {
215 r = cgroup_bonding_set_task_access(b, mode, uid, gid, sticky);
216 if (r < 0)
217 return r;
218 }
219
220 return 0;
221 }
222
223 int cgroup_bonding_kill(CGroupBonding *b, int sig, bool sigcont, bool rem, Set *s, const char *cgroup_suffix) {
224 char *p = NULL;
225 const char *path;
226 int r;
227
228 assert(b);
229 assert(sig >= 0);
230
231 /* Don't kill cgroups that aren't ours */
232 if (!b->ours)
233 return 0;
234
235 if (cgroup_suffix) {
236 p = strjoin(b->path, "/", cgroup_suffix, NULL);
237 if (!p)
238 return -ENOMEM;
239
240 path = p;
241 } else
242 path = b->path;
243
244 r = cg_kill_recursive(b->controller, path, sig, sigcont, true, rem, s);
245 free(p);
246
247 return r;
248 }
249
250 int cgroup_bonding_kill_list(CGroupBonding *first, int sig, bool sigcont, bool rem, Set *s, const char *cgroup_suffix) {
251 CGroupBonding *b;
252 Set *allocated_set = NULL;
253 int ret = -EAGAIN, r;
254
255 if (!first)
256 return 0;
257
258 if (!s)
259 if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func)))
260 return -ENOMEM;
261
262 LIST_FOREACH(by_unit, b, first) {
263 r = cgroup_bonding_kill(b, sig, sigcont, rem, s, cgroup_suffix);
264 if (r < 0) {
265 if (r == -EAGAIN || r == -ESRCH)
266 continue;
267
268 ret = r;
269 goto finish;
270 }
271
272 if (ret < 0 || r > 0)
273 ret = r;
274 }
275
276 finish:
277 if (allocated_set)
278 set_free(allocated_set);
279
280 return ret;
281 }
282
283 /* Returns 1 if the group is empty, 0 if it is not, -EAGAIN if we
284 * cannot know */
285 int cgroup_bonding_is_empty(CGroupBonding *b) {
286 int r;
287
288 assert(b);
289
290 if ((r = cg_is_empty_recursive(b->controller, b->path, true)) < 0)
291 return r;
292
293 /* If it is empty it is empty */
294 if (r > 0)
295 return 1;
296
297 /* It's not only us using this cgroup, so we just don't know */
298 return b->ours ? 0 : -EAGAIN;
299 }
300
301 int cgroup_bonding_is_empty_list(CGroupBonding *first) {
302 CGroupBonding *b;
303
304 LIST_FOREACH(by_unit, b, first) {
305 int r;
306
307 if ((r = cgroup_bonding_is_empty(b)) < 0) {
308 /* If this returned -EAGAIN, then we don't know if the
309 * group is empty, so let's see if another group can
310 * tell us */
311
312 if (r != -EAGAIN)
313 return r;
314 } else
315 return r;
316 }
317
318 return -EAGAIN;
319 }
320
321 int manager_setup_cgroup(Manager *m) {
322 _cleanup_free_ char *current = NULL, *path = NULL;
323 int r;
324 char suffix[sizeof("/systemd-") + DECIMAL_STR_MAX(pid_t)];
325
326 assert(m);
327
328 /* 0. Be nice to Ingo Molnar #628004 */
329 if (path_is_mount_point("/sys/fs/cgroup/systemd", false) <= 0) {
330 log_warning("No control group support available, not creating root group.");
331 return 0;
332 }
333
334 /* 1. Determine hierarchy */
335 r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 0, &current);
336 if (r < 0) {
337 log_error("Cannot determine cgroup we are running in: %s", strerror(-r));
338 return r;
339 }
340
341 if (m->running_as == SYSTEMD_SYSTEM)
342 strcpy(suffix, "/system");
343 else {
344 snprintf(suffix, sizeof(suffix), "/systemd-%lu", (unsigned long) getpid());
345 char_array_0(suffix);
346 }
347
348 free(m->cgroup_hierarchy);
349 if (endswith(current, suffix)) {
350 /* We probably got reexecuted and can continue to use our root cgroup */
351 m->cgroup_hierarchy = current;
352 current = NULL;
353
354 } else {
355 /* We need a new root cgroup */
356 m->cgroup_hierarchy = NULL;
357 if (asprintf(&m->cgroup_hierarchy, "%s%s", streq(current, "/") ? "" : current, suffix) < 0)
358 return log_oom();
359 }
360
361 /* 2. Show data */
362 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, NULL, &path);
363 if (r < 0) {
364 log_error("Cannot find cgroup mount point: %s", strerror(-r));
365 return r;
366 }
367
368 log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
369
370 /* 3. Install agent */
371 if (m->running_as == SYSTEMD_SYSTEM) {
372 r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH);
373 if (r < 0)
374 log_warning("Failed to install release agent, ignoring: %s", strerror(-r));
375 else if (r > 0)
376 log_debug("Installed release agent.");
377 else
378 log_debug("Release agent already installed.");
379 }
380
381 /* 4. Realize the group */
382 r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, 0);
383 if (r < 0) {
384 log_error("Failed to create root cgroup hierarchy: %s", strerror(-r));
385 return r;
386 }
387
388 /* 5. And pin it, so that it cannot be unmounted */
389 if (m->pin_cgroupfs_fd >= 0)
390 close_nointr_nofail(m->pin_cgroupfs_fd);
391
392 m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK);
393 if (r < 0) {
394 log_error("Failed to open pin file: %m");
395 return -errno;
396 }
397
398 /* 6. Remove non-existing controllers from the default controllers list */
399 cg_shorten_controllers(m->default_controllers);
400
401 /* 7. Let's create the user and machine hierarchies
402 * right-away, so that people can inotify on them, if they
403 * wish, without this being racy. */
404 if (m->running_as == SYSTEMD_SYSTEM) {
405 cg_create(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, "../user");
406 cg_create(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, "../machine");
407 }
408
409 return 0;
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 = strdupa(cgroup);
442 if (!p)
443 return -ENOMEM;
444
445 for (;;) {
446 char *e;
447
448 e = strrchr(p, '/');
449 if (e == p || !e) {
450 *bonding = NULL;
451 return 0;
452 }
453
454 *e = 0;
455
456 b = hashmap_get(m->cgroup_bondings, p);
457 if (b) {
458 *bonding = b;
459 return 1;
460 }
461 }
462 }
463
464 int cgroup_notify_empty(Manager *m, const char *group) {
465 CGroupBonding *l, *b;
466 int r;
467
468 assert(m);
469 assert(group);
470
471 r = cgroup_bonding_get(m, group, &l);
472 if (r <= 0)
473 return r;
474
475 LIST_FOREACH(by_path, b, l) {
476 int t;
477
478 if (!b->unit)
479 continue;
480
481 t = cgroup_bonding_is_empty_list(b);
482 if (t < 0) {
483
484 /* If we don't know, we don't know */
485 if (t != -EAGAIN)
486 log_warning("Failed to check whether cgroup is empty: %s", strerror(errno));
487
488 continue;
489 }
490
491 if (t > 0) {
492 /* If it is empty, let's delete it */
493 cgroup_bonding_trim_list(b->unit->cgroup_bondings, true);
494
495 if (UNIT_VTABLE(b->unit)->cgroup_notify_empty)
496 UNIT_VTABLE(b->unit)->cgroup_notify_empty(b->unit);
497 }
498 }
499
500 return 0;
501 }
502
503 Unit* cgroup_unit_by_pid(Manager *m, pid_t pid) {
504 CGroupBonding *l, *b;
505 char *group = NULL;
506
507 assert(m);
508
509 if (pid <= 1)
510 return NULL;
511
512 if (cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &group) < 0)
513 return NULL;
514
515 l = hashmap_get(m->cgroup_bondings, group);
516
517 if (!l) {
518 char *slash;
519
520 while ((slash = strrchr(group, '/'))) {
521 if (slash == group)
522 break;
523
524 *slash = 0;
525
526 if ((l = hashmap_get(m->cgroup_bondings, group)))
527 break;
528 }
529 }
530
531 free(group);
532
533 LIST_FOREACH(by_path, b, l) {
534
535 if (!b->unit)
536 continue;
537
538 if (b->ours)
539 return b->unit;
540 }
541
542 return NULL;
543 }
544
545 CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) {
546 CGroupBonding *b;
547
548 if (!controller)
549 controller = SYSTEMD_CGROUP_CONTROLLER;
550
551 LIST_FOREACH(by_unit, b, first)
552 if (streq(b->controller, controller))
553 return b;
554
555 return NULL;
556 }
557
558 char *cgroup_bonding_to_string(CGroupBonding *b) {
559 char *r;
560
561 assert(b);
562
563 if (asprintf(&r, "%s:%s", b->controller, b->path) < 0)
564 return NULL;
565
566 return r;
567 }
568
569 pid_t cgroup_bonding_search_main_pid(CGroupBonding *b) {
570 FILE *f;
571 pid_t pid = 0, npid, mypid;
572
573 assert(b);
574
575 if (!b->ours)
576 return 0;
577
578 if (cg_enumerate_processes(b->controller, b->path, &f) < 0)
579 return 0;
580
581 mypid = getpid();
582
583 while (cg_read_pid(f, &npid) > 0) {
584 pid_t ppid;
585
586 if (npid == pid)
587 continue;
588
589 /* Ignore processes that aren't our kids */
590 if (get_parent_of_pid(npid, &ppid) >= 0 && ppid != mypid)
591 continue;
592
593 if (pid != 0) {
594 /* Dang, there's more than one daemonized PID
595 in this group, so we don't know what process
596 is the main process. */
597 pid = 0;
598 break;
599 }
600
601 pid = npid;
602 }
603
604 fclose(f);
605
606 return pid;
607 }
608
609 pid_t cgroup_bonding_search_main_pid_list(CGroupBonding *first) {
610 CGroupBonding *b;
611 pid_t pid;
612
613 /* Try to find a main pid from this cgroup, but checking if
614 * there's only one PID in the cgroup and returning it. Later
615 * on we might want to add additional, smarter heuristics
616 * here. */
617
618 LIST_FOREACH(by_unit, b, first)
619 if ((pid = cgroup_bonding_search_main_pid(b)) != 0)
620 return pid;
621
622 return 0;
623
624 }