chiark / gitweb /
util: add a bit of syntactic sugar to run short code fragments with a different umask
[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         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         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_migrate(CGroupBonding *b, CGroupBonding *list) {
154         CGroupBonding *q;
155         int ret = 0;
156
157         LIST_FOREACH(by_unit, q, list) {
158                 int r;
159
160                 if (q == b)
161                         continue;
162
163                 if (!q->ours)
164                         continue;
165
166                 r = cg_migrate_recursive(q->controller, q->path, b->controller, b->path, true, false);
167                 if (r < 0 && ret == 0)
168                         ret = r;
169         }
170
171         return ret;
172 }
173
174 int cgroup_bonding_migrate_to(CGroupBonding *b, const char *target, bool rem) {
175         assert(b);
176         assert(target);
177
178         return cg_migrate_recursive(b->controller, b->path, b->controller, target, true, rem);
179 }
180
181 int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid) {
182         assert(b);
183
184         if (!b->realized)
185                 return -EINVAL;
186
187         return cg_set_group_access(b->controller, b->path, mode, uid, gid);
188 }
189
190 int cgroup_bonding_set_group_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid) {
191         CGroupBonding *b;
192         int r;
193
194         LIST_FOREACH(by_unit, b, first) {
195                 r = cgroup_bonding_set_group_access(b, mode, uid, gid);
196                 if (r < 0)
197                         return r;
198         }
199
200         return 0;
201 }
202
203 int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky) {
204         assert(b);
205
206         if (!b->realized)
207                 return -EINVAL;
208
209         return cg_set_task_access(b->controller, b->path, mode, uid, gid, sticky);
210 }
211
212 int cgroup_bonding_set_task_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid, int sticky) {
213         CGroupBonding *b;
214         int r;
215
216         LIST_FOREACH(by_unit, b, first) {
217                 r = cgroup_bonding_set_task_access(b, mode, uid, gid, sticky);
218                 if (r < 0)
219                         return r;
220         }
221
222         return 0;
223 }
224
225 int cgroup_bonding_kill(CGroupBonding *b, int sig, bool sigcont, bool rem, Set *s, const char *cgroup_suffix) {
226         char *p = NULL;
227         const char *path;
228         int r;
229
230         assert(b);
231         assert(sig >= 0);
232
233         /* Don't kill cgroups that aren't ours */
234         if (!b->ours)
235                 return 0;
236
237         if (cgroup_suffix) {
238                 p = strjoin(b->path, "/", cgroup_suffix, NULL);
239                 if (!p)
240                         return -ENOMEM;
241
242                 path = p;
243         } else
244                 path = b->path;
245
246         r = cg_kill_recursive(b->controller, path, sig, sigcont, true, rem, s);
247         free(p);
248
249         return r;
250 }
251
252 int cgroup_bonding_kill_list(CGroupBonding *first, int sig, bool sigcont, bool rem, Set *s, const char *cgroup_suffix) {
253         CGroupBonding *b;
254         Set *allocated_set = NULL;
255         int ret = -EAGAIN, r;
256
257         if (!first)
258                 return 0;
259
260         if (!s)
261                 if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func)))
262                         return -ENOMEM;
263
264         LIST_FOREACH(by_unit, b, first) {
265                 r = cgroup_bonding_kill(b, sig, sigcont, rem, s, cgroup_suffix);
266                 if (r < 0) {
267                         if (r == -EAGAIN || r == -ESRCH)
268                                 continue;
269
270                         ret = r;
271                         goto finish;
272                 }
273
274                 if (ret < 0 || r > 0)
275                         ret = r;
276         }
277
278 finish:
279         if (allocated_set)
280                 set_free(allocated_set);
281
282         return ret;
283 }
284
285 /* Returns 1 if the group is empty, 0 if it is not, -EAGAIN if we
286  * cannot know */
287 int cgroup_bonding_is_empty(CGroupBonding *b) {
288         int r;
289
290         assert(b);
291
292         if ((r = cg_is_empty_recursive(b->controller, b->path, true)) < 0)
293                 return r;
294
295         /* If it is empty it is empty */
296         if (r > 0)
297                 return 1;
298
299         /* It's not only us using this cgroup, so we just don't know */
300         return b->ours ? 0 : -EAGAIN;
301 }
302
303 int cgroup_bonding_is_empty_list(CGroupBonding *first) {
304         CGroupBonding *b;
305
306         LIST_FOREACH(by_unit, b, first) {
307                 int r;
308
309                 if ((r = cgroup_bonding_is_empty(b)) < 0) {
310                         /* If this returned -EAGAIN, then we don't know if the
311                          * group is empty, so let's see if another group can
312                          * tell us */
313
314                         if (r != -EAGAIN)
315                                 return r;
316                 } else
317                         return r;
318         }
319
320         return -EAGAIN;
321 }
322
323 int manager_setup_cgroup(Manager *m) {
324         char *current = NULL, *path = NULL;
325         int r;
326         char suffix[32];
327
328         assert(m);
329
330         /* 0. Be nice to Ingo Molnar #628004 */
331         if (path_is_mount_point("/sys/fs/cgroup/systemd", false) <= 0) {
332                 log_warning("No control group support available, not creating root group.");
333                 return 0;
334         }
335
336         /* 1. Determine hierarchy */
337         r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 0, &current);
338         if (r < 0) {
339                 log_error("Cannot determine cgroup we are running in: %s", strerror(-r));
340                 goto finish;
341         }
342
343         if (m->running_as == SYSTEMD_SYSTEM)
344                 strcpy(suffix, "/system");
345         else {
346                 snprintf(suffix, sizeof(suffix), "/systemd-%lu", (unsigned long) getpid());
347                 char_array_0(suffix);
348         }
349
350         free(m->cgroup_hierarchy);
351         if (endswith(current, suffix)) {
352                 /* We probably got reexecuted and can continue to use our root cgroup */
353                 m->cgroup_hierarchy = current;
354                 current = NULL;
355
356         } else {
357                 /* We need a new root cgroup */
358                 m->cgroup_hierarchy = NULL;
359                 if (asprintf(&m->cgroup_hierarchy, "%s%s", streq(current, "/") ? "" : current, suffix) < 0) {
360                         r = log_oom();
361                         goto finish;
362                 }
363         }
364
365         /* 2. Show data */
366         r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, NULL, &path);
367         if (r < 0) {
368                 log_error("Cannot find cgroup mount point: %s", strerror(-r));
369                 goto finish;
370         }
371
372         log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
373
374         /* 3. Install agent */
375         r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH);
376         if (r < 0)
377                 log_warning("Failed to install release agent, ignoring: %s", strerror(-r));
378         else if (r > 0)
379                 log_debug("Installed release agent.");
380         else
381                 log_debug("Release agent already installed.");
382
383         /* 4. Realize the group */
384         r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, 0);
385         if (r < 0) {
386                 log_error("Failed to create root cgroup hierarchy: %s", strerror(-r));
387                 goto finish;
388         }
389
390         /* 5. And pin it, so that it cannot be unmounted */
391         if (m->pin_cgroupfs_fd >= 0)
392                 close_nointr_nofail(m->pin_cgroupfs_fd);
393
394         m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK);
395         if (r < 0) {
396                 log_error("Failed to open pin file: %m");
397                 r = -errno;
398                 goto finish;
399         }
400
401         log_debug("Created root group.");
402
403         cg_shorten_controllers(m->default_controllers);
404
405 finish:
406         free(current);
407         free(path);
408
409         return r;
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 }