chiark / gitweb /
udev: fix a few issues detected by the llvm static analyzer
[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
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 }