chiark / gitweb /
snapshot: downgrade automatic dependencies from Requires to Wants
[elogind.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 }