]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/cgroup.c
service: explicitly remove control/ subcgroup after each control command
[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
35 int cgroup_bonding_realize(CGroupBonding *b) {
36 int r;
37
38 assert(b);
39 assert(b->path);
40 assert(b->controller);
41
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));
45 return r;
46 }
47
48 b->realized = true;
49
50 return 0;
51 }
52
53 int cgroup_bonding_realize_list(CGroupBonding *first) {
54 CGroupBonding *b;
55 int r;
56
57 LIST_FOREACH(by_unit, b, first)
58 if ((r = cgroup_bonding_realize(b)) < 0 && b->essential)
59 return r;
60
61 return 0;
62 }
63
64 void cgroup_bonding_free(CGroupBonding *b, bool trim) {
65 assert(b);
66
67 if (b->unit) {
68 CGroupBonding *f;
69
70 LIST_REMOVE(CGroupBonding, by_unit, b->unit->cgroup_bondings, b);
71
72 if (streq(b->controller, SYSTEMD_CGROUP_CONTROLLER)) {
73 assert_se(f = hashmap_get(b->unit->manager->cgroup_bondings, b->path));
74 LIST_REMOVE(CGroupBonding, by_path, f, b);
75
76 if (f)
77 hashmap_replace(b->unit->manager->cgroup_bondings, b->path, f);
78 else
79 hashmap_remove(b->unit->manager->cgroup_bondings, b->path);
80 }
81 }
82
83 if (b->realized && b->ours && trim)
84 cg_trim(b->controller, b->path, false);
85
86 free(b->controller);
87 free(b->path);
88 free(b);
89 }
90
91 void cgroup_bonding_free_list(CGroupBonding *first, bool remove_or_trim) {
92 CGroupBonding *b, *n;
93
94 LIST_FOREACH_SAFE(by_unit, b, n, first)
95 cgroup_bonding_free(b, remove_or_trim);
96 }
97
98 void cgroup_bonding_trim(CGroupBonding *b, bool delete_root) {
99 assert(b);
100
101 if (b->realized && b->ours)
102 cg_trim(b->controller, b->path, delete_root);
103 }
104
105 void 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
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 = 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)
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_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
162 int 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
175 int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky) {
176 assert(b);
177
178 if (!b->realized)
179 return -EINVAL;
180
181 return cg_set_task_access(b->controller, b->path, mode, uid, gid, sticky);
182 }
183
184 int cgroup_bonding_set_task_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid, int sticky) {
185 CGroupBonding *b;
186 int r;
187
188 LIST_FOREACH(by_unit, b, first) {
189 r = cgroup_bonding_set_task_access(b, mode, uid, gid, sticky);
190 if (r < 0)
191 return r;
192 }
193
194 return 0;
195 }
196
197 int cgroup_bonding_kill(CGroupBonding *b, int sig, bool sigcont, bool rem, Set *s, const char *cgroup_suffix) {
198 char *p = NULL;
199 const char *path;
200 int r;
201
202 assert(b);
203 assert(sig >= 0);
204
205 /* Don't kill cgroups that aren't ours */
206 if (!b->ours)
207 return 0;
208
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
218 r = cg_kill_recursive(b->controller, path, sig, sigcont, true, rem, s);
219 free(p);
220
221 return r;
222 }
223
224 int cgroup_bonding_kill_list(CGroupBonding *first, int sig, bool sigcont, bool rem, Set *s, const char *cgroup_suffix) {
225 CGroupBonding *b;
226 Set *allocated_set = NULL;
227 int ret = -EAGAIN, r;
228
229 if (!first)
230 return 0;
231
232 if (!s)
233 if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func)))
234 return -ENOMEM;
235
236 LIST_FOREACH(by_unit, b, first) {
237 r = cgroup_bonding_kill(b, sig, sigcont, rem, s, cgroup_suffix);
238 if (r < 0) {
239 if (r == -EAGAIN || r == -ESRCH)
240 continue;
241
242 ret = r;
243 goto finish;
244 }
245
246 if (ret < 0 || r > 0)
247 ret = r;
248 }
249
250 finish:
251 if (allocated_set)
252 set_free(allocated_set);
253
254 return ret;
255 }
256
257 /* Returns 1 if the group is empty, 0 if it is not, -EAGAIN if we
258 * cannot know */
259 int cgroup_bonding_is_empty(CGroupBonding *b) {
260 int r;
261
262 assert(b);
263
264 if ((r = cg_is_empty_recursive(b->controller, b->path, true)) < 0)
265 return r;
266
267 /* If it is empty it is empty */
268 if (r > 0)
269 return 1;
270
271 /* It's not only us using this cgroup, so we just don't know */
272 return b->ours ? 0 : -EAGAIN;
273 }
274
275 int 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
295 int manager_setup_cgroup(Manager *m) {
296 char *current = NULL, *path = NULL;
297 int r;
298 char suffix[32];
299
300 assert(m);
301
302 /* 0. Be nice to Ingo Molnar #628004 */
303 if (path_is_mount_point("/sys/fs/cgroup/systemd", false) <= 0) {
304 log_warning("No control group support available, not creating root group.");
305 return 0;
306 }
307
308 /* 1. Determine hierarchy */
309 r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 0, &current);
310 if (r < 0) {
311 log_error("Cannot determine cgroup we are running in: %s", strerror(-r));
312 goto finish;
313 }
314
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 }
321
322 free(m->cgroup_hierarchy);
323 if (endswith(current, suffix)) {
324 /* We probably got reexecuted and can continue to use our root cgroup */
325 m->cgroup_hierarchy = current;
326 current = NULL;
327
328 } else {
329 /* We need a new root cgroup */
330 m->cgroup_hierarchy = NULL;
331 if (asprintf(&m->cgroup_hierarchy, "%s%s", streq(current, "/") ? "" : current, suffix) < 0) {
332 log_error("Out of memory");
333 r = -ENOMEM;
334 goto finish;
335 }
336 }
337
338 /* 2. Show data */
339 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, NULL, &path);
340 if (r < 0) {
341 log_error("Cannot find cgroup mount point: %s", strerror(-r));
342 goto finish;
343 }
344
345 log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
346
347 /* 3. Install agent */
348 r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH);
349 if (r < 0)
350 log_warning("Failed to install release agent, ignoring: %s", strerror(-r));
351 else if (r > 0)
352 log_debug("Installed release agent.");
353 else
354 log_debug("Release agent already installed.");
355
356 /* 4. Realize the group */
357 r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, 0);
358 if (r < 0) {
359 log_error("Failed to create root cgroup hierarchy: %s", strerror(-r));
360 goto finish;
361 }
362
363 /* 5. And pin it, so that it cannot be unmounted */
364 if (m->pin_cgroupfs_fd >= 0)
365 close_nointr_nofail(m->pin_cgroupfs_fd);
366
367 m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK);
368 if (r < 0) {
369 log_error("Failed to open pin file: %m");
370 r = -errno;
371 goto finish;
372 }
373
374 log_debug("Created root group.");
375
376 cg_shorten_controllers(m->default_controllers);
377
378 finish:
379 free(current);
380 free(path);
381
382 return r;
383 }
384
385 void manager_shutdown_cgroup(Manager *m, bool delete) {
386 assert(m);
387
388 if (delete && m->cgroup_hierarchy)
389 cg_delete(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy);
390
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;
398 }
399
400 int 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);
409 if (b) {
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
439 int cgroup_notify_empty(Manager *m, const char *group) {
440 CGroupBonding *l, *b;
441 int r;
442
443 assert(m);
444 assert(group);
445
446 r = cgroup_bonding_get(m, group, &l);
447 if (r <= 0)
448 return r;
449
450 LIST_FOREACH(by_path, b, l) {
451 int t;
452
453 if (!b->unit)
454 continue;
455
456 t = cgroup_bonding_is_empty_list(b);
457 if (t < 0) {
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
466 if (t > 0) {
467 /* If it is empty, let's delete it */
468 cgroup_bonding_trim_list(b->unit->cgroup_bondings, true);
469
470 if (UNIT_VTABLE(b->unit)->cgroup_notify_empty)
471 UNIT_VTABLE(b->unit)->cgroup_notify_empty(b->unit);
472 }
473 }
474
475 return 0;
476 }
477
478 Unit* cgroup_unit_by_pid(Manager *m, pid_t pid) {
479 CGroupBonding *l, *b;
480 char *group = NULL;
481
482 assert(m);
483
484 if (pid <= 1)
485 return NULL;
486
487 if (cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &group) < 0)
488 return NULL;
489
490 l = hashmap_get(m->cgroup_bondings, group);
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
506 free(group);
507
508 LIST_FOREACH(by_path, b, l) {
509
510 if (!b->unit)
511 continue;
512
513 if (b->ours)
514 return b->unit;
515 }
516
517 return NULL;
518 }
519
520 CGroupBonding *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 }
531
532 char *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 }
542
543 pid_t cgroup_bonding_search_main_pid(CGroupBonding *b) {
544 FILE *f;
545 pid_t pid = 0, npid, mypid;
546
547 assert(b);
548
549 if (!b->ours)
550 return 0;
551
552 if (cg_enumerate_processes(b->controller, b->path, &f) < 0)
553 return 0;
554
555 mypid = getpid();
556
557 while (cg_read_pid(f, &npid) > 0) {
558 pid_t ppid;
559
560 if (npid == pid)
561 continue;
562
563 /* Ignore processes that aren't our kids */
564 if (get_parent_of_pid(npid, &ppid) >= 0 && ppid != mypid)
565 continue;
566
567 if (pid != 0) {
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. */
571 pid = 0;
572 break;
573 }
574
575 pid = npid;
576 }
577
578 fclose(f);
579
580 return pid;
581 }
582
583 pid_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 }