chiark / gitweb /
update TODO
[elogind.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         _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         char *current = NULL, *path = NULL;
323         int r;
324         char suffix[32];
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                 goto finish;
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                         r = log_oom();
359                         goto finish;
360                 }
361         }
362
363         /* 2. Show data */
364         r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, NULL, &path);
365         if (r < 0) {
366                 log_error("Cannot find cgroup mount point: %s", strerror(-r));
367                 goto finish;
368         }
369
370         log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
371
372         /* 3. Install agent */
373         r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH);
374         if (r < 0)
375                 log_warning("Failed to install release agent, ignoring: %s", strerror(-r));
376         else if (r > 0)
377                 log_debug("Installed release agent.");
378         else
379                 log_debug("Release agent already installed.");
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                 goto finish;
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                 r = -errno;
396                 goto finish;
397         }
398
399         log_debug("Created root group.");
400
401         cg_shorten_controllers(m->default_controllers);
402
403 finish:
404         free(current);
405         free(path);
406
407         return r;
408 }
409
410 void manager_shutdown_cgroup(Manager *m, bool delete) {
411         assert(m);
412
413         if (delete && m->cgroup_hierarchy)
414                 cg_delete(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy);
415
416         if (m->pin_cgroupfs_fd >= 0) {
417                 close_nointr_nofail(m->pin_cgroupfs_fd);
418                 m->pin_cgroupfs_fd = -1;
419         }
420
421         free(m->cgroup_hierarchy);
422         m->cgroup_hierarchy = NULL;
423 }
424
425 int cgroup_bonding_get(Manager *m, const char *cgroup, CGroupBonding **bonding) {
426         CGroupBonding *b;
427         char *p;
428
429         assert(m);
430         assert(cgroup);
431         assert(bonding);
432
433         b = hashmap_get(m->cgroup_bondings, cgroup);
434         if (b) {
435                 *bonding = b;
436                 return 1;
437         }
438
439         p = strdupa(cgroup);
440         if (!p)
441                 return -ENOMEM;
442
443         for (;;) {
444                 char *e;
445
446                 e = strrchr(p, '/');
447                 if (e == p || !e) {
448                         *bonding = NULL;
449                         return 0;
450                 }
451
452                 *e = 0;
453
454                 b = hashmap_get(m->cgroup_bondings, p);
455                 if (b) {
456                         *bonding = b;
457                         return 1;
458                 }
459         }
460 }
461
462 int cgroup_notify_empty(Manager *m, const char *group) {
463         CGroupBonding *l, *b;
464         int r;
465
466         assert(m);
467         assert(group);
468
469         r = cgroup_bonding_get(m, group, &l);
470         if (r <= 0)
471                 return r;
472
473         LIST_FOREACH(by_path, b, l) {
474                 int t;
475
476                 if (!b->unit)
477                         continue;
478
479                 t = cgroup_bonding_is_empty_list(b);
480                 if (t < 0) {
481
482                         /* If we don't know, we don't know */
483                         if (t != -EAGAIN)
484                                 log_warning("Failed to check whether cgroup is empty: %s", strerror(errno));
485
486                         continue;
487                 }
488
489                 if (t > 0) {
490                         /* If it is empty, let's delete it */
491                         cgroup_bonding_trim_list(b->unit->cgroup_bondings, true);
492
493                         if (UNIT_VTABLE(b->unit)->cgroup_notify_empty)
494                                 UNIT_VTABLE(b->unit)->cgroup_notify_empty(b->unit);
495                 }
496         }
497
498         return 0;
499 }
500
501 Unit* cgroup_unit_by_pid(Manager *m, pid_t pid) {
502         CGroupBonding *l, *b;
503         char *group = NULL;
504
505         assert(m);
506
507         if (pid <= 1)
508                 return NULL;
509
510         if (cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &group) < 0)
511                 return NULL;
512
513         l = hashmap_get(m->cgroup_bondings, group);
514
515         if (!l) {
516                 char *slash;
517
518                 while ((slash = strrchr(group, '/'))) {
519                         if (slash == group)
520                                 break;
521
522                         *slash = 0;
523
524                         if ((l = hashmap_get(m->cgroup_bondings, group)))
525                                 break;
526                 }
527         }
528
529         free(group);
530
531         LIST_FOREACH(by_path, b, l) {
532
533                 if (!b->unit)
534                         continue;
535
536                 if (b->ours)
537                         return b->unit;
538         }
539
540         return NULL;
541 }
542
543 CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) {
544         CGroupBonding *b;
545
546         if (!controller)
547                 controller = SYSTEMD_CGROUP_CONTROLLER;
548
549         LIST_FOREACH(by_unit, b, first)
550                 if (streq(b->controller, controller))
551                         return b;
552
553         return NULL;
554 }
555
556 char *cgroup_bonding_to_string(CGroupBonding *b) {
557         char *r;
558
559         assert(b);
560
561         if (asprintf(&r, "%s:%s", b->controller, b->path) < 0)
562                 return NULL;
563
564         return r;
565 }
566
567 pid_t cgroup_bonding_search_main_pid(CGroupBonding *b) {
568         FILE *f;
569         pid_t pid = 0, npid, mypid;
570
571         assert(b);
572
573         if (!b->ours)
574                 return 0;
575
576         if (cg_enumerate_processes(b->controller, b->path, &f) < 0)
577                 return 0;
578
579         mypid = getpid();
580
581         while (cg_read_pid(f, &npid) > 0)  {
582                 pid_t ppid;
583
584                 if (npid == pid)
585                         continue;
586
587                 /* Ignore processes that aren't our kids */
588                 if (get_parent_of_pid(npid, &ppid) >= 0 && ppid != mypid)
589                         continue;
590
591                 if (pid != 0) {
592                         /* Dang, there's more than one daemonized PID
593                         in this group, so we don't know what process
594                         is the main process. */
595                         pid = 0;
596                         break;
597                 }
598
599                 pid = npid;
600         }
601
602         fclose(f);
603
604         return pid;
605 }
606
607 pid_t cgroup_bonding_search_main_pid_list(CGroupBonding *first) {
608         CGroupBonding *b;
609         pid_t pid;
610
611         /* Try to find a main pid from this cgroup, but checking if
612          * there's only one PID in the cgroup and returning it. Later
613          * on we might want to add additional, smarter heuristics
614          * here. */
615
616         LIST_FOREACH(by_unit, b, first)
617                 if ((pid = cgroup_bonding_search_main_pid(b)) != 0)
618                         return pid;
619
620         return 0;
621
622 }