]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/cgroup.c
log.h: new log_oom() -> int -ENOMEM, use it
[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
114 int cgroup_bonding_install(CGroupBonding *b, pid_t pid, const char *cgroup_suffix) {
115 char *p = NULL;
116 const char *path;
117 int r;
118
119 assert(b);
120 assert(pid >= 0);
121
122 if (cgroup_suffix) {
123 p = strjoin(b->path, "/", cgroup_suffix, NULL);
124 if (!p)
125 return -ENOMEM;
126
127 path = p;
128 } else
129 path = b->path;
130
131 r = cg_create_and_attach(b->controller, path, pid);
132 free(p);
133
134 if (r < 0)
135 return r;
136
137 b->realized = true;
138 return 0;
139 }
140
141 int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid, const char *cgroup_suffix) {
142 CGroupBonding *b;
143 int r;
144
145 LIST_FOREACH(by_unit, b, first) {
146 r = cgroup_bonding_install(b, pid, cgroup_suffix);
147 if (r < 0 && b->essential)
148 return r;
149 }
150
151 return 0;
152 }
153
154 int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid) {
155 assert(b);
156
157 if (!b->realized)
158 return -EINVAL;
159
160 return cg_set_group_access(b->controller, b->path, mode, uid, gid);
161 }
162
163 int cgroup_bonding_set_group_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid) {
164 CGroupBonding *b;
165 int r;
166
167 LIST_FOREACH(by_unit, b, first) {
168 r = cgroup_bonding_set_group_access(b, mode, uid, gid);
169 if (r < 0)
170 return r;
171 }
172
173 return 0;
174 }
175
176 int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky) {
177 assert(b);
178
179 if (!b->realized)
180 return -EINVAL;
181
182 return cg_set_task_access(b->controller, b->path, mode, uid, gid, sticky);
183 }
184
185 int cgroup_bonding_set_task_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid, int sticky) {
186 CGroupBonding *b;
187 int r;
188
189 LIST_FOREACH(by_unit, b, first) {
190 r = cgroup_bonding_set_task_access(b, mode, uid, gid, sticky);
191 if (r < 0)
192 return r;
193 }
194
195 return 0;
196 }
197
198 int cgroup_bonding_kill(CGroupBonding *b, int sig, bool sigcont, bool rem, Set *s, const char *cgroup_suffix) {
199 char *p = NULL;
200 const char *path;
201 int r;
202
203 assert(b);
204 assert(sig >= 0);
205
206 /* Don't kill cgroups that aren't ours */
207 if (!b->ours)
208 return 0;
209
210 if (cgroup_suffix) {
211 p = strjoin(b->path, "/", cgroup_suffix, NULL);
212 if (!p)
213 return -ENOMEM;
214
215 path = p;
216 } else
217 path = b->path;
218
219 r = cg_kill_recursive(b->controller, path, sig, sigcont, true, rem, s);
220 free(p);
221
222 return r;
223 }
224
225 int cgroup_bonding_kill_list(CGroupBonding *first, int sig, bool sigcont, bool rem, Set *s, const char *cgroup_suffix) {
226 CGroupBonding *b;
227 Set *allocated_set = NULL;
228 int ret = -EAGAIN, r;
229
230 if (!first)
231 return 0;
232
233 if (!s)
234 if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func)))
235 return -ENOMEM;
236
237 LIST_FOREACH(by_unit, b, first) {
238 r = cgroup_bonding_kill(b, sig, sigcont, rem, s, cgroup_suffix);
239 if (r < 0) {
240 if (r == -EAGAIN || r == -ESRCH)
241 continue;
242
243 ret = r;
244 goto finish;
245 }
246
247 if (ret < 0 || r > 0)
248 ret = r;
249 }
250
251 finish:
252 if (allocated_set)
253 set_free(allocated_set);
254
255 return ret;
256 }
257
258 /* Returns 1 if the group is empty, 0 if it is not, -EAGAIN if we
259 * cannot know */
260 int cgroup_bonding_is_empty(CGroupBonding *b) {
261 int r;
262
263 assert(b);
264
265 if ((r = cg_is_empty_recursive(b->controller, b->path, true)) < 0)
266 return r;
267
268 /* If it is empty it is empty */
269 if (r > 0)
270 return 1;
271
272 /* It's not only us using this cgroup, so we just don't know */
273 return b->ours ? 0 : -EAGAIN;
274 }
275
276 int cgroup_bonding_is_empty_list(CGroupBonding *first) {
277 CGroupBonding *b;
278
279 LIST_FOREACH(by_unit, b, first) {
280 int r;
281
282 if ((r = cgroup_bonding_is_empty(b)) < 0) {
283 /* If this returned -EAGAIN, then we don't know if the
284 * group is empty, so let's see if another group can
285 * tell us */
286
287 if (r != -EAGAIN)
288 return r;
289 } else
290 return r;
291 }
292
293 return -EAGAIN;
294 }
295
296 int manager_setup_cgroup(Manager *m) {
297 char *current = NULL, *path = NULL;
298 int r;
299 char suffix[32];
300
301 assert(m);
302
303 /* 0. Be nice to Ingo Molnar #628004 */
304 if (path_is_mount_point("/sys/fs/cgroup/systemd", false) <= 0) {
305 log_warning("No control group support available, not creating root group.");
306 return 0;
307 }
308
309 /* 1. Determine hierarchy */
310 r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 0, &current);
311 if (r < 0) {
312 log_error("Cannot determine cgroup we are running in: %s", strerror(-r));
313 goto finish;
314 }
315
316 if (m->running_as == MANAGER_SYSTEM)
317 strcpy(suffix, "/system");
318 else {
319 snprintf(suffix, sizeof(suffix), "/systemd-%lu", (unsigned long) getpid());
320 char_array_0(suffix);
321 }
322
323 free(m->cgroup_hierarchy);
324 if (endswith(current, suffix)) {
325 /* We probably got reexecuted and can continue to use our root cgroup */
326 m->cgroup_hierarchy = current;
327 current = NULL;
328
329 } else {
330 /* We need a new root cgroup */
331 m->cgroup_hierarchy = NULL;
332 if (asprintf(&m->cgroup_hierarchy, "%s%s", streq(current, "/") ? "" : current, suffix) < 0) {
333 r = log_oom();
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 }