]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cgroup-util.c
pam: implement systemd PAM module and generelize cgroup API for that a bit
[thirdparty/systemd.git] / src / cgroup-util.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
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 General Public License as published by
10 the Free Software Foundation; either version 2 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 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <unistd.h>
24 #include <signal.h>
25 #include <string.h>
26 #include <stdlib.h>
27
28 #include <libcgroup.h>
29
30 #include "cgroup-util.h"
31 #include "log.h"
32 #include "set.h"
33 #include "macro.h"
34 #include "util.h"
35
36 int cg_translate_error(int error, int _errno) {
37
38 switch (error) {
39
40 case ECGROUPNOTCOMPILED:
41 case ECGROUPNOTMOUNTED:
42 case ECGROUPNOTEXIST:
43 case ECGROUPNOTCREATED:
44 return -ENOENT;
45
46 case ECGINVAL:
47 return -EINVAL;
48
49 case ECGROUPNOTALLOWED:
50 return -EPERM;
51
52 case ECGOTHER:
53 return -_errno;
54 }
55
56 return -EIO;
57 }
58
59 static struct cgroup* cg_new(const char *controller, const char *path) {
60 struct cgroup *cgroup;
61
62 assert(path);
63 assert(controller);
64
65 if (!(cgroup = cgroup_new_cgroup(path)))
66 return NULL;
67
68 if (!cgroup_add_controller(cgroup, controller)) {
69 cgroup_free(&cgroup);
70 return NULL;
71 }
72
73 return cgroup;
74 }
75
76 int cg_kill(const char *controller, const char *path, int sig, bool ignore_self) {
77 bool killed = false, done = false;
78 Set *s;
79 pid_t my_pid;
80 int r, ret = 0;
81
82 assert(controller);
83 assert(path);
84 assert(sig >= 0);
85
86 /* This goes through the tasks list and kills them all. This
87 * is repeated until no further processes are added to the
88 * tasks list, to properly handle forking processes */
89
90 if (!(s = set_new(trivial_hash_func, trivial_compare_func)))
91 return -ENOMEM;
92
93 my_pid = getpid();
94
95 do {
96 void *iterator = NULL;
97 pid_t pid = 0;
98
99 done = true;
100
101 r = cgroup_get_task_begin(path, controller, &iterator, &pid);
102 while (r == 0) {
103
104 if (pid == my_pid && ignore_self)
105 goto next;
106
107 if (set_get(s, INT_TO_PTR(pid)) == INT_TO_PTR(pid))
108 goto next;
109
110 /* If we haven't killed this process yet, kill
111 * it */
112
113 if (kill(pid, sig) < 0 && errno != ESRCH) {
114 if (ret == 0)
115 ret = -errno;
116 }
117
118 killed = true;
119 done = false;
120
121 if ((r = set_put(s, INT_TO_PTR(pid))) < 0)
122 goto loop_exit;
123
124 next:
125 r = cgroup_get_task_next(&iterator, &pid);
126 }
127
128 if (r == 0 || r == ECGEOF)
129 r = 0;
130 else if (r == ECGOTHER && errno == ENOENT)
131 r = -ESRCH;
132 else
133 r = cg_translate_error(r, errno);
134
135 loop_exit:
136 assert_se(cgroup_get_task_end(&iterator) == 0);
137
138 /* To avoid racing against processes which fork
139 * quicker than we can kill them we repeat this until
140 * no new pids need to be killed. */
141
142 } while (!done && r >= 0);
143
144 set_free(s);
145
146 if (ret < 0)
147 return ret;
148
149 if (r < 0)
150 return r;
151
152 return !!killed;
153 }
154
155 int cg_kill_recursive(const char *controller, const char *path, int sig, bool ignore_self) {
156 struct cgroup_file_info info;
157 int level = 0, r, ret = 0;
158 void *iterator = NULL;
159 bool killed = false;
160
161 assert(path);
162 assert(controller);
163 assert(sig >= 0);
164
165 zero(info);
166
167 r = cgroup_walk_tree_begin(controller, path, 0, &iterator, &info, &level);
168 while (r == 0) {
169 int k;
170 char *p;
171
172 if (info.type != CGROUP_FILE_TYPE_DIR)
173 goto next;
174
175 if (asprintf(&p, "%s/%s", path, info.path) < 0) {
176 ret = -ENOMEM;
177 break;
178 }
179
180 k = cg_kill(controller, p, sig, ignore_self);
181 free(p);
182
183 if (k < 0) {
184 if (ret == 0)
185 ret = k;
186 } else if (k > 0)
187 killed = true;
188
189 next:
190
191 r = cgroup_walk_tree_next(0, &iterator, &info, level);
192 }
193
194 if (ret == 0) {
195 if (r == 0 || r == ECGEOF)
196 ret = !!killed;
197 else if (r == ECGOTHER && errno == ENOENT)
198 ret = -ESRCH;
199 else
200 ret = cg_translate_error(r, errno);
201 }
202
203 assert_se(cgroup_walk_tree_end(&iterator) == 0);
204
205 return ret;
206 }
207
208 int cg_kill_recursive_and_wait(const char *controller, const char *path) {
209 unsigned i;
210
211 assert(path);
212 assert(controller);
213
214 /* This safely kills all processes; first it sends a SIGTERM,
215 * then checks 8 times after 50ms whether the group is
216 * now empty, and finally kills everything that is left with
217 * SIGKILL */
218
219 for (i = 0; i < 10; i++) {
220 int sig, r;
221
222 if (i <= 0)
223 sig = SIGTERM;
224 else if (i >= 9)
225 sig = SIGKILL;
226 else
227 sig = 0;
228
229 if ((r = cg_kill_recursive(controller, path, sig, true)) <= 0)
230 return r;
231
232 usleep(50 * USEC_PER_MSEC);
233 }
234
235 return 0;
236 }
237
238 int cg_migrate(const char *controller, const char *from, const char *to, bool ignore_self) {
239 bool migrated = false, done = false;
240 struct cgroup *dest;
241 int r, ret = 0;
242 pid_t my_pid;
243
244 assert(controller);
245 assert(from);
246 assert(to);
247
248 if (!(dest = cg_new(controller, to)))
249 return -ENOMEM;
250
251 my_pid = getpid();
252
253 do {
254 void *iterator = NULL;
255 pid_t pid = 0;
256
257 done = true;
258
259 r = cgroup_get_task_begin(from, controller, &iterator, &pid);
260 while (r == 0) {
261
262 if (pid == my_pid && ignore_self)
263 goto next;
264
265 if ((r = cgroup_attach_task_pid(dest, pid)) != 0) {
266 if (ret == 0)
267 r = cg_translate_error(r, errno);
268 }
269
270 migrated = true;
271 done = false;
272
273 next:
274
275 r = cgroup_get_task_next(&iterator, &pid);
276 }
277
278 if (r == 0 || r == ECGEOF)
279 r = 0;
280 else if (r == ECGOTHER && errno == ENOENT)
281 r = -ESRCH;
282 else
283 r = cg_translate_error(r, errno);
284
285 assert_se(cgroup_get_task_end(&iterator) == 0);
286
287 } while (!done && r >= 0);
288
289 cgroup_free(&dest);
290
291 if (ret < 0)
292 return ret;
293
294 if (r < 0)
295 return r;
296
297 return !!migrated;
298 }
299
300 int cg_migrate_recursive(const char *controller, const char *from, const char *to, bool ignore_self) {
301 struct cgroup_file_info info;
302 int level = 0, r, ret = 0;
303 void *iterator = NULL;
304 bool migrated = false;
305
306 assert(controller);
307 assert(from);
308 assert(to);
309
310 zero(info);
311
312 r = cgroup_walk_tree_begin(controller, from, 0, &iterator, &info, &level);
313 while (r == 0) {
314 int k;
315 char *p;
316
317 if (info.type != CGROUP_FILE_TYPE_DIR)
318 goto next;
319
320 if (asprintf(&p, "%s/%s", from, info.path) < 0) {
321 ret = -ENOMEM;
322 break;
323 }
324
325 k = cg_migrate(controller, p, to, ignore_self);
326 free(p);
327
328 if (k < 0) {
329 if (ret == 0)
330 ret = k;
331 } else if (k > 0)
332 migrated = true;
333
334 next:
335 r = cgroup_walk_tree_next(0, &iterator, &info, level);
336 }
337
338 if (ret == 0) {
339 if (r == 0 || r == ECGEOF)
340 r = !!migrated;
341 else if (r == ECGOTHER && errno == ENOENT)
342 r = -ESRCH;
343 else
344 r = cg_translate_error(r, errno);
345 }
346
347 assert_se(cgroup_walk_tree_end(&iterator) == 0);
348
349 return ret;
350 }
351
352 int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
353 char *mp;
354 int r;
355
356 assert(controller);
357 assert(path);
358
359 if ((r = cgroup_get_subsys_mount_point(controller, &mp)) != 0)
360 return cg_translate_error(r, errno);
361
362 if (suffix)
363 r = asprintf(fs, "%s/%s/%s", mp, path, suffix);
364 else
365 r = asprintf(fs, "%s/%s", mp, path);
366
367 free(mp);
368
369 return r < 0 ? -ENOMEM : 0;
370 }
371
372 int cg_trim(const char *controller, const char *path, bool delete_root) {
373 char *fs;
374 int r;
375
376 assert(controller);
377 assert(path);
378
379 if ((r = cg_get_path(controller, path, NULL, &fs)) < 0)
380 return r;
381
382 r = rm_rf(fs, true, delete_root);
383 free(fs);
384
385 return r;
386 }
387
388 int cg_delete(const char *controller, const char *path) {
389 struct cgroup *cg;
390 int r;
391
392 assert(controller);
393 assert(path);
394
395 if (!(cg = cg_new(controller, path)))
396 return -ENOMEM;
397
398 if ((r = cgroup_delete_cgroup_ext(cg, CGFLAG_DELETE_RECURSIVE|CGFLAG_DELETE_IGNORE_MIGRATION)) != 0) {
399 r = cg_translate_error(r, errno);
400 goto finish;
401 }
402
403 r = 0;
404
405 finish:
406 cgroup_free(&cg);
407
408 return r;
409 }
410
411 int cg_create(const char *controller, const char *path) {
412 struct cgroup *cg;
413 int r;
414
415 assert(controller);
416 assert(path);
417
418 if (!(cg = cg_new(controller, path)))
419 return -ENOMEM;
420
421 if ((r = cgroup_create_cgroup(cg, 1)) != 0) {
422 r = cg_translate_error(r, errno);
423 goto finish;
424 }
425
426 r = 0;
427
428 finish:
429 cgroup_free(&cg);
430
431 return r;
432 }
433
434 int cg_attach(const char *controller, const char *path, pid_t pid) {
435 struct cgroup *cg;
436 int r;
437
438 assert(controller);
439 assert(path);
440 assert(pid >= 0);
441
442 if (!(cg = cg_new(controller, path)))
443 return -ENOMEM;
444
445 if (pid == 0)
446 pid = getpid();
447
448 if ((r = cgroup_attach_task_pid(cg, pid))) {
449 r = cg_translate_error(r, errno);
450 goto finish;
451 }
452
453 r = 0;
454
455 finish:
456 cgroup_free(&cg);
457
458 return r;
459 }
460
461 int cg_create_and_attach(const char *controller, const char *path, pid_t pid) {
462 struct cgroup *cg;
463 int r;
464
465 assert(controller);
466 assert(path);
467 assert(pid >= 0);
468
469 if (!(cg = cg_new(controller, path)))
470 return -ENOMEM;
471
472 if ((r = cgroup_create_cgroup(cg, 1)) != 0) {
473 r = cg_translate_error(r, errno);
474 goto finish;
475 }
476
477 if (pid == 0)
478 pid = getpid();
479
480 if ((r = cgroup_attach_task_pid(cg, pid))) {
481 r = cg_translate_error(r, errno);
482 goto finish;
483 }
484
485 r = 0;
486
487 finish:
488 cgroup_free(&cg);
489
490 return r;
491 }
492
493 int cg_set_group_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid) {
494 char *fs;
495 int r;
496
497 assert(controller);
498 assert(path);
499
500 if ((r = cg_get_path(controller, path, NULL, &fs)) < 0)
501 return r;
502
503 r = chmod_and_chown(fs, mode, uid, gid);
504 free(fs);
505
506 return r;
507 }
508
509 int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid) {
510 char *fs;
511 int r;
512
513 assert(controller);
514 assert(path);
515
516 if ((r = cg_get_path(controller, path, "tasks", &fs)) < 0)
517 return r;
518
519 r = chmod_and_chown(fs, mode, uid, gid);
520 free(fs);
521
522 return r;
523 }
524
525 int cg_get_by_pid(const char *controller, pid_t pid, char **path) {
526 int r;
527 char *p = NULL;
528
529 assert(controller);
530 assert(pid > 0);
531 assert(path);
532
533 if ((r = cgroup_get_current_controller_path(pid, controller, &p)) != 0)
534 return cg_translate_error(r, errno);
535
536 assert(p);
537
538 *path = p;
539 return 0;
540 }
541
542 int cg_install_release_agent(const char *controller, const char *agent) {
543 char *mp = NULL, *path = NULL, *contents = NULL, *line = NULL, *sc;
544 int r;
545
546 assert(controller);
547 assert(agent);
548
549 if ((r = cgroup_get_subsys_mount_point(controller, &mp)) != 0)
550 return cg_translate_error(r, errno);
551
552 if (asprintf(&path, "%s/release_agent", mp) < 0) {
553 r = -ENOMEM;
554 goto finish;
555 }
556
557 if ((r = read_one_line_file(path, &contents)) < 0)
558 goto finish;
559
560 sc = strstrip(contents);
561
562 if (sc[0] == 0) {
563
564 if (asprintf(&line, "%s\n", agent) < 0) {
565 r = -ENOMEM;
566 goto finish;
567 }
568
569 if ((r = write_one_line_file(path, line)) < 0)
570 goto finish;
571
572 } else if (!streq(sc, agent)) {
573 r = -EEXIST;
574 goto finish;
575 }
576
577 free(path);
578 path = NULL;
579 if (asprintf(&path, "%s/notify_on_release", mp) < 0) {
580 r = -ENOMEM;
581 goto finish;
582 }
583
584 free(contents);
585 contents = NULL;
586 if ((r = read_one_line_file(path, &contents)) < 0)
587 goto finish;
588
589 sc = strstrip(contents);
590
591 if (streq(sc, "0")) {
592 if ((r = write_one_line_file(path, "1\n")) < 0)
593 goto finish;
594 } else if (!streq(sc, "1")) {
595 r = -EIO;
596 goto finish;
597 }
598
599 r = 0;
600
601 finish:
602 free(mp);
603 free(path);
604 free(contents);
605 free(line);
606
607 return r;
608 }
609
610 int cg_is_empty(const char *controller, const char *path, bool ignore_self) {
611 void *iterator = NULL;
612 pid_t pid = 0;
613 int r;
614
615 assert(controller);
616 assert(path);
617
618 r = cgroup_get_task_begin(path, controller, &iterator, &pid);
619 while (r == 0) {
620
621 if (ignore_self&& pid == getpid())
622 goto next;
623
624 break;
625
626 next:
627 r = cgroup_get_task_next(&iterator, &pid);
628 }
629
630
631 if (r == ECGEOF)
632 r = 1;
633 else if (r != 0)
634 r = cg_translate_error(r, errno);
635 else
636 r = 0;
637
638 assert_se(cgroup_get_task_end(&iterator) == 0);
639
640 return r;
641 }
642
643 int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self) {
644 struct cgroup_file_info info;
645 int level = 0, r, ret = 0;
646 void *iterator = NULL;
647 bool empty = true;
648
649 assert(controller);
650 assert(path);
651
652 zero(info);
653
654 r = cgroup_walk_tree_begin(controller, path, 0, &iterator, &info, &level);
655 while (r == 0) {
656 int k;
657 char *p;
658
659 if (info.type != CGROUP_FILE_TYPE_DIR)
660 goto next;
661
662 if (asprintf(&p, "%s/%s", path, info.path) < 0) {
663 ret = -ENOMEM;
664 break;
665 }
666
667 k = cg_is_empty(controller, p, ignore_self);
668 free(p);
669
670 if (k < 0) {
671 ret = k;
672 break;
673 } else if (k == 0) {
674 empty = false;
675 break;
676 }
677
678 next:
679 r = cgroup_walk_tree_next(0, &iterator, &info, level);
680 }
681
682 if (ret == 0) {
683 if (r == 0 || r == ECGEOF)
684 ret = !!empty;
685 else if (r == ECGOTHER && errno == ENOENT)
686 ret = -ESRCH;
687 else
688 ret = cg_translate_error(r, errno);
689 }
690
691 assert_se(cgroup_walk_tree_end(&iterator) == 0);
692
693 return ret;
694 }