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