Line data Source code
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 <unistd.h>
24 : #include <signal.h>
25 : #include <string.h>
26 : #include <stdlib.h>
27 : #include <dirent.h>
28 : #include <sys/stat.h>
29 : #include <sys/types.h>
30 : #include <ftw.h>
31 :
32 : #include "cgroup-util.h"
33 : #include "set.h"
34 : #include "macro.h"
35 : #include "util.h"
36 : #include "formats-util.h"
37 : #include "process-util.h"
38 : #include "path-util.h"
39 : #include "unit-name.h"
40 : #include "fileio.h"
41 : #include "special.h"
42 : #include "mkdir.h"
43 : #include "login-util.h"
44 :
45 0 : int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
46 0 : _cleanup_free_ char *fs = NULL;
47 : FILE *f;
48 : int r;
49 :
50 0 : assert(_f);
51 :
52 0 : r = cg_get_path(controller, path, "cgroup.procs", &fs);
53 0 : if (r < 0)
54 0 : return r;
55 :
56 0 : f = fopen(fs, "re");
57 0 : if (!f)
58 0 : return -errno;
59 :
60 0 : *_f = f;
61 0 : return 0;
62 : }
63 :
64 0 : int cg_read_pid(FILE *f, pid_t *_pid) {
65 : unsigned long ul;
66 :
67 : /* Note that the cgroup.procs might contain duplicates! See
68 : * cgroups.txt for details. */
69 :
70 0 : assert(f);
71 0 : assert(_pid);
72 :
73 0 : errno = 0;
74 0 : if (fscanf(f, "%lu", &ul) != 1) {
75 :
76 0 : if (feof(f))
77 0 : return 0;
78 :
79 0 : return errno ? -errno : -EIO;
80 : }
81 :
82 0 : if (ul <= 0)
83 0 : return -EIO;
84 :
85 0 : *_pid = (pid_t) ul;
86 0 : return 1;
87 : }
88 :
89 0 : int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
90 0 : _cleanup_free_ char *fs = NULL;
91 : int r;
92 : DIR *d;
93 :
94 0 : assert(_d);
95 :
96 : /* This is not recursive! */
97 :
98 0 : r = cg_get_path(controller, path, NULL, &fs);
99 0 : if (r < 0)
100 0 : return r;
101 :
102 0 : d = opendir(fs);
103 0 : if (!d)
104 0 : return -errno;
105 :
106 0 : *_d = d;
107 0 : return 0;
108 : }
109 :
110 0 : int cg_read_subgroup(DIR *d, char **fn) {
111 : struct dirent *de;
112 :
113 0 : assert(d);
114 0 : assert(fn);
115 :
116 0 : FOREACH_DIRENT(de, d, return -errno) {
117 : char *b;
118 :
119 0 : if (de->d_type != DT_DIR)
120 0 : continue;
121 :
122 0 : if (streq(de->d_name, ".") ||
123 0 : streq(de->d_name, ".."))
124 0 : continue;
125 :
126 0 : b = strdup(de->d_name);
127 0 : if (!b)
128 0 : return -ENOMEM;
129 :
130 0 : *fn = b;
131 0 : return 1;
132 0 : }
133 :
134 0 : return 0;
135 : }
136 :
137 0 : int cg_rmdir(const char *controller, const char *path) {
138 0 : _cleanup_free_ char *p = NULL;
139 : int r;
140 :
141 0 : r = cg_get_path(controller, path, NULL, &p);
142 0 : if (r < 0)
143 0 : return r;
144 :
145 0 : r = rmdir(p);
146 0 : if (r < 0 && errno != ENOENT)
147 0 : return -errno;
148 :
149 0 : return 0;
150 : }
151 :
152 0 : int cg_kill(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, Set *s) {
153 0 : _cleanup_set_free_ Set *allocated_set = NULL;
154 0 : bool done = false;
155 0 : int r, ret = 0;
156 : pid_t my_pid;
157 :
158 0 : assert(sig >= 0);
159 :
160 : /* This goes through the tasks list and kills them all. This
161 : * is repeated until no further processes are added to the
162 : * tasks list, to properly handle forking processes */
163 :
164 0 : if (!s) {
165 0 : s = allocated_set = set_new(NULL);
166 0 : if (!s)
167 0 : return -ENOMEM;
168 : }
169 :
170 0 : my_pid = getpid();
171 :
172 : do {
173 0 : _cleanup_fclose_ FILE *f = NULL;
174 0 : pid_t pid = 0;
175 0 : done = true;
176 :
177 0 : r = cg_enumerate_processes(controller, path, &f);
178 0 : if (r < 0) {
179 0 : if (ret >= 0 && r != -ENOENT)
180 0 : return r;
181 :
182 0 : return ret;
183 : }
184 :
185 0 : while ((r = cg_read_pid(f, &pid)) > 0) {
186 :
187 0 : if (ignore_self && pid == my_pid)
188 0 : continue;
189 :
190 0 : if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
191 0 : continue;
192 :
193 : /* If we haven't killed this process yet, kill
194 : * it */
195 0 : if (kill(pid, sig) < 0) {
196 0 : if (ret >= 0 && errno != ESRCH)
197 0 : ret = -errno;
198 : } else {
199 0 : if (sigcont && sig != SIGKILL)
200 0 : kill(pid, SIGCONT);
201 :
202 0 : if (ret == 0)
203 0 : ret = 1;
204 : }
205 :
206 0 : done = false;
207 :
208 0 : r = set_put(s, LONG_TO_PTR(pid));
209 0 : if (r < 0) {
210 0 : if (ret >= 0)
211 0 : return r;
212 :
213 0 : return ret;
214 : }
215 : }
216 :
217 0 : if (r < 0) {
218 0 : if (ret >= 0)
219 0 : return r;
220 :
221 0 : return ret;
222 : }
223 :
224 : /* To avoid racing against processes which fork
225 : * quicker than we can kill them we repeat this until
226 : * no new pids need to be killed. */
227 :
228 0 : } while (!done);
229 :
230 0 : return ret;
231 : }
232 :
233 0 : int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool rem, Set *s) {
234 0 : _cleanup_set_free_ Set *allocated_set = NULL;
235 0 : _cleanup_closedir_ DIR *d = NULL;
236 0 : int r, ret = 0;
237 : char *fn;
238 :
239 0 : assert(path);
240 0 : assert(sig >= 0);
241 :
242 0 : if (!s) {
243 0 : s = allocated_set = set_new(NULL);
244 0 : if (!s)
245 0 : return -ENOMEM;
246 : }
247 :
248 0 : ret = cg_kill(controller, path, sig, sigcont, ignore_self, s);
249 :
250 0 : r = cg_enumerate_subgroups(controller, path, &d);
251 0 : if (r < 0) {
252 0 : if (ret >= 0 && r != -ENOENT)
253 0 : return r;
254 :
255 0 : return ret;
256 : }
257 :
258 0 : while ((r = cg_read_subgroup(d, &fn)) > 0) {
259 0 : _cleanup_free_ char *p = NULL;
260 :
261 0 : p = strjoin(path, "/", fn, NULL);
262 0 : free(fn);
263 0 : if (!p)
264 0 : return -ENOMEM;
265 :
266 0 : r = cg_kill_recursive(controller, p, sig, sigcont, ignore_self, rem, s);
267 0 : if (ret >= 0 && r != 0)
268 0 : ret = r;
269 : }
270 :
271 0 : if (ret >= 0 && r < 0)
272 0 : ret = r;
273 :
274 0 : if (rem) {
275 0 : r = cg_rmdir(controller, path);
276 0 : if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY)
277 0 : return r;
278 : }
279 :
280 0 : return ret;
281 : }
282 :
283 0 : int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self) {
284 0 : bool done = false;
285 0 : _cleanup_set_free_ Set *s = NULL;
286 0 : int r, ret = 0;
287 : pid_t my_pid;
288 :
289 0 : assert(cfrom);
290 0 : assert(pfrom);
291 0 : assert(cto);
292 0 : assert(pto);
293 :
294 0 : s = set_new(NULL);
295 0 : if (!s)
296 0 : return -ENOMEM;
297 :
298 0 : my_pid = getpid();
299 :
300 : do {
301 0 : _cleanup_fclose_ FILE *f = NULL;
302 0 : pid_t pid = 0;
303 0 : done = true;
304 :
305 0 : r = cg_enumerate_processes(cfrom, pfrom, &f);
306 0 : if (r < 0) {
307 0 : if (ret >= 0 && r != -ENOENT)
308 0 : return r;
309 :
310 0 : return ret;
311 : }
312 :
313 0 : while ((r = cg_read_pid(f, &pid)) > 0) {
314 :
315 : /* This might do weird stuff if we aren't a
316 : * single-threaded program. However, we
317 : * luckily know we are not */
318 0 : if (ignore_self && pid == my_pid)
319 0 : continue;
320 :
321 0 : if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
322 0 : continue;
323 :
324 0 : r = cg_attach(cto, pto, pid);
325 0 : if (r < 0) {
326 0 : if (ret >= 0 && r != -ESRCH)
327 0 : ret = r;
328 0 : } else if (ret == 0)
329 0 : ret = 1;
330 :
331 0 : done = false;
332 :
333 0 : r = set_put(s, LONG_TO_PTR(pid));
334 0 : if (r < 0) {
335 0 : if (ret >= 0)
336 0 : return r;
337 :
338 0 : return ret;
339 : }
340 : }
341 :
342 0 : if (r < 0) {
343 0 : if (ret >= 0)
344 0 : return r;
345 :
346 0 : return ret;
347 : }
348 0 : } while (!done);
349 :
350 0 : return ret;
351 : }
352 :
353 0 : int cg_migrate_recursive(
354 : const char *cfrom,
355 : const char *pfrom,
356 : const char *cto,
357 : const char *pto,
358 : bool ignore_self,
359 : bool rem) {
360 :
361 0 : _cleanup_closedir_ DIR *d = NULL;
362 0 : int r, ret = 0;
363 : char *fn;
364 :
365 0 : assert(cfrom);
366 0 : assert(pfrom);
367 0 : assert(cto);
368 0 : assert(pto);
369 :
370 0 : ret = cg_migrate(cfrom, pfrom, cto, pto, ignore_self);
371 :
372 0 : r = cg_enumerate_subgroups(cfrom, pfrom, &d);
373 0 : if (r < 0) {
374 0 : if (ret >= 0 && r != -ENOENT)
375 0 : return r;
376 :
377 0 : return ret;
378 : }
379 :
380 0 : while ((r = cg_read_subgroup(d, &fn)) > 0) {
381 0 : _cleanup_free_ char *p = NULL;
382 :
383 0 : p = strjoin(pfrom, "/", fn, NULL);
384 0 : free(fn);
385 0 : if (!p) {
386 0 : if (ret >= 0)
387 0 : return -ENOMEM;
388 :
389 0 : return ret;
390 : }
391 :
392 0 : r = cg_migrate_recursive(cfrom, p, cto, pto, ignore_self, rem);
393 0 : if (r != 0 && ret >= 0)
394 0 : ret = r;
395 : }
396 :
397 0 : if (r < 0 && ret >= 0)
398 0 : ret = r;
399 :
400 0 : if (rem) {
401 0 : r = cg_rmdir(cfrom, pfrom);
402 0 : if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY)
403 0 : return r;
404 : }
405 :
406 0 : return ret;
407 : }
408 :
409 0 : int cg_migrate_recursive_fallback(
410 : const char *cfrom,
411 : const char *pfrom,
412 : const char *cto,
413 : const char *pto,
414 : bool ignore_self,
415 : bool rem) {
416 :
417 : int r;
418 :
419 0 : assert(cfrom);
420 0 : assert(pfrom);
421 0 : assert(cto);
422 0 : assert(pto);
423 :
424 0 : r = cg_migrate_recursive(cfrom, pfrom, cto, pto, ignore_self, rem);
425 0 : if (r < 0) {
426 0 : char prefix[strlen(pto) + 1];
427 :
428 : /* This didn't work? Then let's try all prefixes of the destination */
429 :
430 0 : PATH_FOREACH_PREFIX(prefix, pto) {
431 0 : r = cg_migrate_recursive(cfrom, pfrom, cto, prefix, ignore_self, rem);
432 0 : if (r >= 0)
433 0 : break;
434 : }
435 : }
436 :
437 0 : return 0;
438 : }
439 :
440 1586 : static const char *normalize_controller(const char *controller) {
441 :
442 1586 : assert(controller);
443 :
444 1586 : if (startswith(controller, "name="))
445 0 : return controller + 5;
446 : else
447 1586 : return controller;
448 : }
449 :
450 70 : static int join_path(const char *controller, const char *path, const char *suffix, char **fs) {
451 70 : char *t = NULL;
452 :
453 70 : if (!isempty(controller)) {
454 70 : if (!isempty(path) && !isempty(suffix))
455 6 : t = strjoin("/sys/fs/cgroup/", controller, "/", path, "/", suffix, NULL);
456 64 : else if (!isempty(path))
457 64 : t = strjoin("/sys/fs/cgroup/", controller, "/", path, NULL);
458 0 : else if (!isempty(suffix))
459 0 : t = strjoin("/sys/fs/cgroup/", controller, "/", suffix, NULL);
460 : else
461 0 : t = strappend("/sys/fs/cgroup/", controller);
462 : } else {
463 0 : if (!isempty(path) && !isempty(suffix))
464 0 : t = strjoin(path, "/", suffix, NULL);
465 0 : else if (!isempty(path))
466 0 : t = strdup(path);
467 : else
468 0 : return -EINVAL;
469 : }
470 :
471 70 : if (!t)
472 0 : return -ENOMEM;
473 :
474 70 : *fs = path_kill_slashes(t);
475 70 : return 0;
476 : }
477 :
478 52 : int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
479 : const char *p;
480 : static thread_local bool good = false;
481 :
482 52 : assert(fs);
483 :
484 52 : if (controller && !cg_controller_is_valid(controller))
485 0 : return -EINVAL;
486 :
487 52 : if (_unlikely_(!good)) {
488 : int r;
489 :
490 5 : r = path_is_mount_point("/sys/fs/cgroup", 0);
491 5 : if (r < 0)
492 0 : return r;
493 5 : if (r == 0)
494 0 : return -ENOENT;
495 :
496 : /* Cache this to save a few stat()s */
497 5 : good = true;
498 : }
499 :
500 52 : p = controller ? normalize_controller(controller) : NULL;
501 :
502 52 : return join_path(p, path, suffix, fs);
503 : }
504 :
505 91 : static int check_hierarchy(const char *p) {
506 : const char *cc;
507 :
508 91 : assert(p);
509 :
510 91 : if (!filename_is_valid(p))
511 0 : return 0;
512 :
513 : /* Check if this controller actually really exists */
514 91 : cc = strjoina("/sys/fs/cgroup/", p);
515 91 : if (laccess(cc, F_OK) < 0)
516 17 : return -errno;
517 :
518 74 : return 0;
519 : }
520 :
521 18 : int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) {
522 : const char *p;
523 : int r;
524 :
525 18 : assert(fs);
526 :
527 18 : if (!cg_controller_is_valid(controller))
528 0 : return -EINVAL;
529 :
530 : /* Normalize the controller syntax */
531 18 : p = normalize_controller(controller);
532 :
533 : /* Check if this controller actually really exists */
534 18 : r = check_hierarchy(p);
535 18 : if (r < 0)
536 0 : return r;
537 :
538 18 : return join_path(p, path, suffix, fs);
539 : }
540 :
541 55 : static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
542 55 : assert(path);
543 55 : assert(sb);
544 55 : assert(ftwbuf);
545 :
546 55 : if (typeflag != FTW_DP)
547 44 : return 0;
548 :
549 11 : if (ftwbuf->level < 1)
550 11 : return 0;
551 :
552 0 : rmdir(path);
553 0 : return 0;
554 : }
555 :
556 41 : int cg_trim(const char *controller, const char *path, bool delete_root) {
557 82 : _cleanup_free_ char *fs = NULL;
558 41 : int r = 0;
559 :
560 41 : assert(path);
561 :
562 41 : r = cg_get_path(controller, path, NULL, &fs);
563 41 : if (r < 0)
564 0 : return r;
565 :
566 41 : errno = 0;
567 41 : if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0)
568 30 : r = errno ? -errno : -EIO;
569 :
570 41 : if (delete_root) {
571 30 : if (rmdir(fs) < 0 && errno != ENOENT)
572 0 : return -errno;
573 : }
574 :
575 41 : return r;
576 : }
577 :
578 0 : int cg_delete(const char *controller, const char *path) {
579 0 : _cleanup_free_ char *parent = NULL;
580 : int r;
581 :
582 0 : assert(path);
583 :
584 0 : r = path_get_parent(path, &parent);
585 0 : if (r < 0)
586 0 : return r;
587 :
588 0 : r = cg_migrate_recursive(controller, path, controller, parent, false, true);
589 0 : return r == -ENOENT ? 0 : r;
590 : }
591 :
592 12 : int cg_create(const char *controller, const char *path) {
593 24 : _cleanup_free_ char *fs = NULL;
594 : int r;
595 :
596 12 : r = cg_get_path_and_check(controller, path, NULL, &fs);
597 12 : if (r < 0)
598 0 : return r;
599 :
600 12 : r = mkdir_parents(fs, 0755);
601 12 : if (r < 0)
602 0 : return r;
603 :
604 12 : if (mkdir(fs, 0755) < 0) {
605 :
606 12 : if (errno == EEXIST)
607 6 : return 0;
608 :
609 6 : return -errno;
610 : }
611 :
612 0 : return 1;
613 : }
614 :
615 0 : int cg_create_and_attach(const char *controller, const char *path, pid_t pid) {
616 : int r, q;
617 :
618 0 : assert(pid >= 0);
619 :
620 0 : r = cg_create(controller, path);
621 0 : if (r < 0)
622 0 : return r;
623 :
624 0 : q = cg_attach(controller, path, pid);
625 0 : if (q < 0)
626 0 : return q;
627 :
628 : /* This does not remove the cgroup on failure */
629 0 : return r;
630 : }
631 :
632 6 : int cg_attach(const char *controller, const char *path, pid_t pid) {
633 12 : _cleanup_free_ char *fs = NULL;
634 : char c[DECIMAL_STR_MAX(pid_t) + 2];
635 : int r;
636 :
637 6 : assert(path);
638 6 : assert(pid >= 0);
639 :
640 6 : r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs);
641 6 : if (r < 0)
642 0 : return r;
643 :
644 6 : if (pid == 0)
645 0 : pid = getpid();
646 :
647 6 : snprintf(c, sizeof(c), PID_FMT"\n", pid);
648 :
649 6 : return write_string_file(fs, c, 0);
650 : }
651 :
652 0 : int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
653 : int r;
654 :
655 0 : assert(controller);
656 0 : assert(path);
657 0 : assert(pid >= 0);
658 :
659 0 : r = cg_attach(controller, path, pid);
660 0 : if (r < 0) {
661 0 : char prefix[strlen(path) + 1];
662 :
663 : /* This didn't work? Then let's try all prefixes of
664 : * the destination */
665 :
666 0 : PATH_FOREACH_PREFIX(prefix, path) {
667 0 : r = cg_attach(controller, prefix, pid);
668 0 : if (r >= 0)
669 0 : break;
670 : }
671 : }
672 :
673 0 : return 0;
674 : }
675 :
676 0 : int cg_set_group_access(
677 : const char *controller,
678 : const char *path,
679 : mode_t mode,
680 : uid_t uid,
681 : gid_t gid) {
682 :
683 0 : _cleanup_free_ char *fs = NULL;
684 : int r;
685 :
686 0 : assert(path);
687 :
688 0 : if (mode != MODE_INVALID)
689 0 : mode &= 0777;
690 :
691 0 : r = cg_get_path(controller, path, NULL, &fs);
692 0 : if (r < 0)
693 0 : return r;
694 :
695 0 : return chmod_and_chown(fs, mode, uid, gid);
696 : }
697 :
698 0 : int cg_set_task_access(
699 : const char *controller,
700 : const char *path,
701 : mode_t mode,
702 : uid_t uid,
703 : gid_t gid) {
704 :
705 0 : _cleanup_free_ char *fs = NULL, *procs = NULL;
706 : int r;
707 :
708 0 : assert(path);
709 :
710 0 : if (mode == MODE_INVALID && uid == UID_INVALID && gid == GID_INVALID)
711 0 : return 0;
712 :
713 0 : if (mode != MODE_INVALID)
714 0 : mode &= 0666;
715 :
716 0 : r = cg_get_path(controller, path, "cgroup.procs", &fs);
717 0 : if (r < 0)
718 0 : return r;
719 :
720 0 : r = chmod_and_chown(fs, mode, uid, gid);
721 0 : if (r < 0)
722 0 : return r;
723 :
724 : /* Compatibility, Always keep values for "tasks" in sync with
725 : * "cgroup.procs" */
726 0 : r = cg_get_path(controller, path, "tasks", &procs);
727 0 : if (r < 0)
728 0 : return r;
729 :
730 0 : return chmod_and_chown(procs, mode, uid, gid);
731 : }
732 :
733 1518 : int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
734 3036 : _cleanup_fclose_ FILE *f = NULL;
735 : char line[LINE_MAX];
736 : const char *fs;
737 : size_t cs;
738 :
739 1518 : assert(path);
740 1518 : assert(pid >= 0);
741 :
742 1518 : if (controller) {
743 1516 : if (!cg_controller_is_valid(controller))
744 0 : return -EINVAL;
745 :
746 1516 : controller = normalize_controller(controller);
747 : } else
748 2 : controller = SYSTEMD_CGROUP_CONTROLLER;
749 :
750 1518 : fs = procfs_file_alloca(pid, "cgroup");
751 :
752 1518 : f = fopen(fs, "re");
753 1518 : if (!f)
754 0 : return errno == ENOENT ? -ESRCH : -errno;
755 :
756 1518 : cs = strlen(controller);
757 :
758 12144 : FOREACH_LINE(line, f, return -errno) {
759 : char *l, *p, *e;
760 : size_t k;
761 : const char *word, *state;
762 12144 : bool found = false;
763 :
764 12144 : truncate_nl(line);
765 :
766 12144 : l = strchr(line, ':');
767 12144 : if (!l)
768 10626 : continue;
769 :
770 12144 : l++;
771 12144 : e = strchr(l, ':');
772 12144 : if (!e)
773 0 : continue;
774 :
775 12144 : *e = 0;
776 :
777 24288 : FOREACH_WORD_SEPARATOR(word, k, l, ",", state) {
778 :
779 13662 : if (k == cs && memcmp(word, controller, cs) == 0) {
780 0 : found = true;
781 0 : break;
782 : }
783 :
784 15180 : if (k == 5 + cs &&
785 3036 : memcmp(word, "name=", 5) == 0 &&
786 1518 : memcmp(word+5, controller, cs) == 0) {
787 1518 : found = true;
788 1518 : break;
789 : }
790 : }
791 :
792 12144 : if (!found)
793 10626 : continue;
794 :
795 1518 : p = strdup(e + 1);
796 1518 : if (!p)
797 1518 : return -ENOMEM;
798 :
799 1518 : *path = p;
800 1518 : return 0;
801 10626 : }
802 :
803 0 : return -ENOENT;
804 : }
805 :
806 0 : int cg_install_release_agent(const char *controller, const char *agent) {
807 0 : _cleanup_free_ char *fs = NULL, *contents = NULL;
808 : char *sc;
809 : int r;
810 :
811 0 : assert(agent);
812 :
813 0 : r = cg_get_path(controller, NULL, "release_agent", &fs);
814 0 : if (r < 0)
815 0 : return r;
816 :
817 0 : r = read_one_line_file(fs, &contents);
818 0 : if (r < 0)
819 0 : return r;
820 :
821 0 : sc = strstrip(contents);
822 0 : if (sc[0] == 0) {
823 0 : r = write_string_file(fs, agent, 0);
824 0 : if (r < 0)
825 0 : return r;
826 0 : } else if (!streq(sc, agent))
827 0 : return -EEXIST;
828 :
829 0 : free(fs);
830 0 : fs = NULL;
831 0 : r = cg_get_path(controller, NULL, "notify_on_release", &fs);
832 0 : if (r < 0)
833 0 : return r;
834 :
835 0 : free(contents);
836 0 : contents = NULL;
837 0 : r = read_one_line_file(fs, &contents);
838 0 : if (r < 0)
839 0 : return r;
840 :
841 0 : sc = strstrip(contents);
842 0 : if (streq(sc, "0")) {
843 0 : r = write_string_file(fs, "1", 0);
844 0 : if (r < 0)
845 0 : return r;
846 :
847 0 : return 1;
848 : }
849 :
850 0 : if (!streq(sc, "1"))
851 0 : return -EIO;
852 :
853 0 : return 0;
854 : }
855 :
856 0 : int cg_uninstall_release_agent(const char *controller) {
857 0 : _cleanup_free_ char *fs = NULL;
858 : int r;
859 :
860 0 : r = cg_get_path(controller, NULL, "notify_on_release", &fs);
861 0 : if (r < 0)
862 0 : return r;
863 :
864 0 : r = write_string_file(fs, "0", 0);
865 0 : if (r < 0)
866 0 : return r;
867 :
868 0 : free(fs);
869 0 : fs = NULL;
870 :
871 0 : r = cg_get_path(controller, NULL, "release_agent", &fs);
872 0 : if (r < 0)
873 0 : return r;
874 :
875 0 : r = write_string_file(fs, "", 0);
876 0 : if (r < 0)
877 0 : return r;
878 :
879 0 : return 0;
880 : }
881 :
882 0 : int cg_is_empty(const char *controller, const char *path, bool ignore_self) {
883 0 : _cleanup_fclose_ FILE *f = NULL;
884 0 : pid_t pid = 0, self_pid;
885 0 : bool found = false;
886 : int r;
887 :
888 0 : assert(path);
889 :
890 0 : r = cg_enumerate_processes(controller, path, &f);
891 0 : if (r < 0)
892 0 : return r == -ENOENT ? 1 : r;
893 :
894 0 : self_pid = getpid();
895 :
896 0 : while ((r = cg_read_pid(f, &pid)) > 0) {
897 :
898 0 : if (ignore_self && pid == self_pid)
899 0 : continue;
900 :
901 0 : found = true;
902 0 : break;
903 : }
904 :
905 0 : if (r < 0)
906 0 : return r;
907 :
908 0 : return !found;
909 : }
910 :
911 0 : int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self) {
912 0 : _cleanup_closedir_ DIR *d = NULL;
913 : char *fn;
914 : int r;
915 :
916 0 : assert(path);
917 :
918 0 : r = cg_is_empty(controller, path, ignore_self);
919 0 : if (r <= 0)
920 0 : return r;
921 :
922 0 : r = cg_enumerate_subgroups(controller, path, &d);
923 0 : if (r < 0)
924 0 : return r == -ENOENT ? 1 : r;
925 :
926 0 : while ((r = cg_read_subgroup(d, &fn)) > 0) {
927 0 : _cleanup_free_ char *p = NULL;
928 :
929 0 : p = strjoin(path, "/", fn, NULL);
930 0 : free(fn);
931 0 : if (!p)
932 0 : return -ENOMEM;
933 :
934 0 : r = cg_is_empty_recursive(controller, p, ignore_self);
935 0 : if (r <= 0)
936 0 : return r;
937 : }
938 :
939 0 : if (r < 0)
940 0 : return r;
941 :
942 0 : return 1;
943 : }
944 :
945 0 : int cg_split_spec(const char *spec, char **controller, char **path) {
946 : const char *e;
947 0 : char *t = NULL, *u = NULL;
948 0 : _cleanup_free_ char *v = NULL;
949 :
950 0 : assert(spec);
951 :
952 0 : if (*spec == '/') {
953 0 : if (!path_is_safe(spec))
954 0 : return -EINVAL;
955 :
956 0 : if (path) {
957 0 : t = strdup(spec);
958 0 : if (!t)
959 0 : return -ENOMEM;
960 :
961 0 : *path = path_kill_slashes(t);
962 : }
963 :
964 0 : if (controller)
965 0 : *controller = NULL;
966 :
967 0 : return 0;
968 : }
969 :
970 0 : e = strchr(spec, ':');
971 0 : if (!e) {
972 0 : if (!cg_controller_is_valid(spec))
973 0 : return -EINVAL;
974 :
975 0 : if (controller) {
976 0 : t = strdup(normalize_controller(spec));
977 0 : if (!t)
978 0 : return -ENOMEM;
979 :
980 0 : *controller = t;
981 : }
982 :
983 0 : if (path)
984 0 : *path = NULL;
985 :
986 0 : return 0;
987 : }
988 :
989 0 : v = strndup(spec, e-spec);
990 0 : if (!v)
991 0 : return -ENOMEM;
992 0 : t = strdup(normalize_controller(v));
993 0 : if (!t)
994 0 : return -ENOMEM;
995 0 : if (!cg_controller_is_valid(t)) {
996 0 : free(t);
997 0 : return -EINVAL;
998 : }
999 :
1000 0 : if (streq(e+1, "")) {
1001 0 : u = strdup("/");
1002 0 : if (!u) {
1003 0 : free(t);
1004 0 : return -ENOMEM;
1005 : }
1006 : } else {
1007 0 : u = strdup(e+1);
1008 0 : if (!u) {
1009 0 : free(t);
1010 0 : return -ENOMEM;
1011 : }
1012 :
1013 0 : if (!path_is_safe(u) ||
1014 0 : !path_is_absolute(u)) {
1015 0 : free(t);
1016 0 : free(u);
1017 0 : return -EINVAL;
1018 : }
1019 :
1020 0 : path_kill_slashes(u);
1021 : }
1022 :
1023 0 : if (controller)
1024 0 : *controller = t;
1025 : else
1026 0 : free(t);
1027 :
1028 0 : if (path)
1029 0 : *path = u;
1030 : else
1031 0 : free(u);
1032 :
1033 0 : return 0;
1034 : }
1035 :
1036 0 : int cg_mangle_path(const char *path, char **result) {
1037 0 : _cleanup_free_ char *c = NULL, *p = NULL;
1038 : char *t;
1039 : int r;
1040 :
1041 0 : assert(path);
1042 0 : assert(result);
1043 :
1044 : /* First, check if it already is a filesystem path */
1045 0 : if (path_startswith(path, "/sys/fs/cgroup")) {
1046 :
1047 0 : t = strdup(path);
1048 0 : if (!t)
1049 0 : return -ENOMEM;
1050 :
1051 0 : *result = path_kill_slashes(t);
1052 0 : return 0;
1053 : }
1054 :
1055 : /* Otherwise, treat it as cg spec */
1056 0 : r = cg_split_spec(path, &c, &p);
1057 0 : if (r < 0)
1058 0 : return r;
1059 :
1060 0 : return cg_get_path(c ? c : SYSTEMD_CGROUP_CONTROLLER, p ? p : "/", NULL, result);
1061 : }
1062 :
1063 705 : int cg_get_root_path(char **path) {
1064 : char *p, *e;
1065 : int r;
1066 :
1067 705 : assert(path);
1068 :
1069 705 : r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p);
1070 705 : if (r < 0)
1071 0 : return r;
1072 :
1073 705 : e = endswith(p, "/" SPECIAL_SYSTEM_SLICE);
1074 705 : if (e)
1075 0 : *e = 0;
1076 :
1077 705 : *path = p;
1078 705 : return 0;
1079 : }
1080 :
1081 726 : int cg_shift_path(const char *cgroup, const char *root, const char **shifted) {
1082 1452 : _cleanup_free_ char *rt = NULL;
1083 : char *p;
1084 : int r;
1085 :
1086 726 : assert(cgroup);
1087 726 : assert(shifted);
1088 :
1089 726 : if (!root) {
1090 : /* If the root was specified let's use that, otherwise
1091 : * let's determine it from PID 1 */
1092 :
1093 700 : r = cg_get_root_path(&rt);
1094 700 : if (r < 0)
1095 0 : return r;
1096 :
1097 700 : root = rt;
1098 : }
1099 :
1100 726 : p = path_startswith(cgroup, root);
1101 726 : if (p)
1102 724 : *shifted = p - 1;
1103 : else
1104 2 : *shifted = cgroup;
1105 :
1106 726 : return 0;
1107 : }
1108 :
1109 700 : int cg_pid_get_path_shifted(pid_t pid, const char *root, char **cgroup) {
1110 1400 : _cleanup_free_ char *raw = NULL;
1111 : const char *c;
1112 : int r;
1113 :
1114 700 : assert(pid >= 0);
1115 700 : assert(cgroup);
1116 :
1117 700 : r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &raw);
1118 700 : if (r < 0)
1119 0 : return r;
1120 :
1121 700 : r = cg_shift_path(raw, root, &c);
1122 700 : if (r < 0)
1123 0 : return r;
1124 :
1125 700 : if (c == raw) {
1126 700 : *cgroup = raw;
1127 700 : raw = NULL;
1128 : } else {
1129 : char *n;
1130 :
1131 0 : n = strdup(c);
1132 0 : if (!n)
1133 0 : return -ENOMEM;
1134 :
1135 0 : *cgroup = n;
1136 : }
1137 :
1138 700 : return 0;
1139 : }
1140 :
1141 643 : int cg_path_decode_unit(const char *cgroup, char **unit){
1142 : char *c, *s;
1143 : size_t n;
1144 :
1145 643 : assert(cgroup);
1146 643 : assert(unit);
1147 :
1148 643 : n = strcspn(cgroup, "/");
1149 643 : if (n < 3)
1150 91 : return -ENXIO;
1151 :
1152 552 : c = strndupa(cgroup, n);
1153 552 : c = cg_unescape(c);
1154 :
1155 552 : if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
1156 8 : return -ENXIO;
1157 :
1158 544 : s = strdup(c);
1159 544 : if (!s)
1160 0 : return -ENOMEM;
1161 :
1162 544 : *unit = s;
1163 544 : return 0;
1164 : }
1165 :
1166 1990 : static bool valid_slice_name(const char *p, size_t n) {
1167 :
1168 1990 : if (!p)
1169 0 : return false;
1170 :
1171 1990 : if (n < strlen("x.slice"))
1172 117 : return false;
1173 :
1174 1873 : if (memcmp(p + n - 6, ".slice", 6) == 0) {
1175 1211 : char buf[n+1], *c;
1176 :
1177 1211 : memcpy(buf, p, n);
1178 1211 : buf[n] = 0;
1179 :
1180 1211 : c = cg_unescape(buf);
1181 :
1182 1211 : return unit_name_is_valid(c, UNIT_NAME_PLAIN);
1183 : }
1184 :
1185 662 : return false;
1186 : }
1187 :
1188 554 : static const char *skip_slices(const char *p) {
1189 554 : assert(p);
1190 :
1191 : /* Skips over all slice assignments */
1192 :
1193 : for (;;) {
1194 : size_t n;
1195 :
1196 1370 : p += strspn(p, "/");
1197 :
1198 1370 : n = strcspn(p, "/");
1199 1370 : if (!valid_slice_name(p, n))
1200 1108 : return p;
1201 :
1202 816 : p += n;
1203 816 : }
1204 : }
1205 :
1206 422 : int cg_path_get_unit(const char *path, char **ret) {
1207 : const char *e;
1208 : char *unit;
1209 : int r;
1210 :
1211 422 : assert(path);
1212 422 : assert(ret);
1213 :
1214 422 : e = skip_slices(path);
1215 :
1216 422 : r = cg_path_decode_unit(e, &unit);
1217 422 : if (r < 0)
1218 95 : return r;
1219 :
1220 : /* We skipped over the slices, don't accept any now */
1221 327 : if (endswith(unit, ".slice")) {
1222 0 : free(unit);
1223 0 : return -ENXIO;
1224 : }
1225 :
1226 327 : *ret = unit;
1227 327 : return 0;
1228 : }
1229 :
1230 100 : int cg_pid_get_unit(pid_t pid, char **unit) {
1231 200 : _cleanup_free_ char *cgroup = NULL;
1232 : int r;
1233 :
1234 100 : assert(unit);
1235 :
1236 100 : r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1237 100 : if (r < 0)
1238 0 : return r;
1239 :
1240 100 : return cg_path_get_unit(cgroup, unit);
1241 : }
1242 :
1243 : /**
1244 : * Skip session-*.scope, but require it to be there.
1245 : */
1246 119 : static const char *skip_session(const char *p) {
1247 : size_t n;
1248 :
1249 119 : if (isempty(p))
1250 7 : return NULL;
1251 :
1252 112 : p += strspn(p, "/");
1253 :
1254 112 : n = strcspn(p, "/");
1255 112 : if (n < strlen("session-x.scope"))
1256 7 : return NULL;
1257 :
1258 105 : if (memcmp(p, "session-", 8) == 0 && memcmp(p + n - 6, ".scope", 6) == 0) {
1259 93 : char buf[n - 8 - 6 + 1];
1260 :
1261 93 : memcpy(buf, p + 8, n - 8 - 6);
1262 93 : buf[n - 8 - 6] = 0;
1263 :
1264 : /* Note that session scopes never need unescaping,
1265 : * since they cannot conflict with the kernel's own
1266 : * names, hence we don't need to call cg_unescape()
1267 : * here. */
1268 :
1269 93 : if (!session_id_valid(buf))
1270 0 : return false;
1271 :
1272 93 : p += n;
1273 93 : p += strspn(p, "/");
1274 93 : return p;
1275 : }
1276 :
1277 12 : return NULL;
1278 : }
1279 :
1280 : /**
1281 : * Skip user@*.service, but require it to be there.
1282 : */
1283 132 : static const char *skip_user_manager(const char *p) {
1284 : size_t n;
1285 :
1286 132 : if (isempty(p))
1287 7 : return NULL;
1288 :
1289 125 : p += strspn(p, "/");
1290 :
1291 125 : n = strcspn(p, "/");
1292 125 : if (n < strlen("user@x.service"))
1293 6 : return NULL;
1294 :
1295 119 : if (memcmp(p, "user@", 5) == 0 && memcmp(p + n - 8, ".service", 8) == 0) {
1296 13 : char buf[n - 5 - 8 + 1];
1297 :
1298 13 : memcpy(buf, p + 5, n - 5 - 8);
1299 13 : buf[n - 5 - 8] = 0;
1300 :
1301 : /* Note that user manager services never need unescaping,
1302 : * since they cannot conflict with the kernel's own
1303 : * names, hence we don't need to call cg_unescape()
1304 : * here. */
1305 :
1306 13 : if (parse_uid(buf, NULL) < 0)
1307 0 : return NULL;
1308 :
1309 13 : p += n;
1310 13 : p += strspn(p, "/");
1311 :
1312 13 : return p;
1313 : }
1314 :
1315 106 : return NULL;
1316 : }
1317 :
1318 132 : static const char *skip_user_prefix(const char *path) {
1319 : const char *e, *t;
1320 :
1321 132 : assert(path);
1322 :
1323 : /* Skip slices, if there are any */
1324 132 : e = skip_slices(path);
1325 :
1326 : /* Skip the user manager, if it's in the path now... */
1327 132 : t = skip_user_manager(e);
1328 132 : if (t)
1329 13 : return t;
1330 :
1331 : /* Alternatively skip the user session if it is in the path... */
1332 119 : return skip_session(e);
1333 : }
1334 :
1335 117 : int cg_path_get_user_unit(const char *path, char **ret) {
1336 : const char *t;
1337 :
1338 117 : assert(path);
1339 117 : assert(ret);
1340 :
1341 117 : t = skip_user_prefix(path);
1342 117 : if (!t)
1343 18 : return -ENXIO;
1344 :
1345 : /* And from here on it looks pretty much the same as for a
1346 : * system unit, hence let's use the same parser from here
1347 : * on. */
1348 99 : return cg_path_get_unit(t, ret);
1349 : }
1350 :
1351 100 : int cg_pid_get_user_unit(pid_t pid, char **unit) {
1352 200 : _cleanup_free_ char *cgroup = NULL;
1353 : int r;
1354 :
1355 100 : assert(unit);
1356 :
1357 100 : r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1358 100 : if (r < 0)
1359 0 : return r;
1360 :
1361 100 : return cg_path_get_user_unit(cgroup, unit);
1362 : }
1363 :
1364 100 : int cg_path_get_machine_name(const char *path, char **machine) {
1365 200 : _cleanup_free_ char *u = NULL, *sl = NULL;
1366 : int r;
1367 :
1368 100 : r = cg_path_get_unit(path, &u);
1369 100 : if (r < 0)
1370 1 : return r;
1371 :
1372 99 : sl = strjoin("/run/systemd/machines/unit:", u, NULL);
1373 99 : if (!sl)
1374 0 : return -ENOMEM;
1375 :
1376 99 : return readlink_malloc(sl, machine);
1377 : }
1378 :
1379 100 : int cg_pid_get_machine_name(pid_t pid, char **machine) {
1380 200 : _cleanup_free_ char *cgroup = NULL;
1381 : int r;
1382 :
1383 100 : assert(machine);
1384 :
1385 100 : r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1386 100 : if (r < 0)
1387 0 : return r;
1388 :
1389 100 : return cg_path_get_machine_name(cgroup, machine);
1390 : }
1391 :
1392 108 : int cg_path_get_session(const char *path, char **session) {
1393 216 : _cleanup_free_ char *unit = NULL;
1394 : char *start, *end;
1395 : int r;
1396 :
1397 108 : assert(path);
1398 :
1399 108 : r = cg_path_get_unit(path, &unit);
1400 108 : if (r < 0)
1401 3 : return r;
1402 :
1403 105 : start = startswith(unit, "session-");
1404 105 : if (!start)
1405 19 : return -ENXIO;
1406 86 : end = endswith(start, ".scope");
1407 86 : if (!end)
1408 0 : return -ENXIO;
1409 :
1410 86 : *end = 0;
1411 86 : if (!session_id_valid(start))
1412 1 : return -ENXIO;
1413 :
1414 85 : if (session) {
1415 : char *rr;
1416 :
1417 85 : rr = strdup(start);
1418 85 : if (!rr)
1419 0 : return -ENOMEM;
1420 :
1421 85 : *session = rr;
1422 : }
1423 :
1424 85 : return 0;
1425 : }
1426 :
1427 100 : int cg_pid_get_session(pid_t pid, char **session) {
1428 200 : _cleanup_free_ char *cgroup = NULL;
1429 : int r;
1430 :
1431 100 : r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1432 100 : if (r < 0)
1433 0 : return r;
1434 :
1435 100 : return cg_path_get_session(cgroup, session);
1436 : }
1437 :
1438 107 : int cg_path_get_owner_uid(const char *path, uid_t *uid) {
1439 214 : _cleanup_free_ char *slice = NULL;
1440 : char *start, *end;
1441 : int r;
1442 :
1443 107 : assert(path);
1444 :
1445 107 : r = cg_path_get_slice(path, &slice);
1446 107 : if (r < 0)
1447 0 : return r;
1448 :
1449 107 : start = startswith(slice, "user-");
1450 107 : if (!start)
1451 17 : return -ENXIO;
1452 90 : end = endswith(start, ".slice");
1453 90 : if (!end)
1454 0 : return -ENXIO;
1455 :
1456 90 : *end = 0;
1457 90 : if (parse_uid(start, uid) < 0)
1458 0 : return -ENXIO;
1459 :
1460 90 : return 0;
1461 : }
1462 :
1463 100 : int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
1464 200 : _cleanup_free_ char *cgroup = NULL;
1465 : int r;
1466 :
1467 100 : r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1468 100 : if (r < 0)
1469 0 : return r;
1470 :
1471 100 : return cg_path_get_owner_uid(cgroup, uid);
1472 : }
1473 :
1474 225 : int cg_path_get_slice(const char *p, char **slice) {
1475 225 : const char *e = NULL;
1476 :
1477 225 : assert(p);
1478 225 : assert(slice);
1479 :
1480 : /* Finds the right-most slice unit from the beginning, but
1481 : * stops before we come to the first non-slice unit. */
1482 :
1483 : for (;;) {
1484 : size_t n;
1485 :
1486 620 : p += strspn(p, "/");
1487 :
1488 620 : n = strcspn(p, "/");
1489 620 : if (!valid_slice_name(p, n)) {
1490 :
1491 225 : if (!e) {
1492 : char *s;
1493 :
1494 13 : s = strdup("-.slice");
1495 13 : if (!s)
1496 0 : return -ENOMEM;
1497 :
1498 13 : *slice = s;
1499 13 : return 0;
1500 : }
1501 :
1502 212 : return cg_path_decode_unit(e, slice);
1503 : }
1504 :
1505 395 : e = p;
1506 395 : p += n;
1507 395 : }
1508 : }
1509 :
1510 100 : int cg_pid_get_slice(pid_t pid, char **slice) {
1511 200 : _cleanup_free_ char *cgroup = NULL;
1512 : int r;
1513 :
1514 100 : assert(slice);
1515 :
1516 100 : r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1517 100 : if (r < 0)
1518 0 : return r;
1519 :
1520 100 : return cg_path_get_slice(cgroup, slice);
1521 : }
1522 :
1523 15 : int cg_path_get_user_slice(const char *p, char **slice) {
1524 : const char *t;
1525 15 : assert(p);
1526 15 : assert(slice);
1527 :
1528 15 : t = skip_user_prefix(p);
1529 15 : if (!t)
1530 8 : return -ENXIO;
1531 :
1532 : /* And now it looks pretty much the same as for a system
1533 : * slice, so let's just use the same parser from here on. */
1534 7 : return cg_path_get_slice(t, slice);
1535 : }
1536 :
1537 0 : int cg_pid_get_user_slice(pid_t pid, char **slice) {
1538 0 : _cleanup_free_ char *cgroup = NULL;
1539 : int r;
1540 :
1541 0 : assert(slice);
1542 :
1543 0 : r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1544 0 : if (r < 0)
1545 0 : return r;
1546 :
1547 0 : return cg_path_get_user_slice(cgroup, slice);
1548 : }
1549 :
1550 26 : char *cg_escape(const char *p) {
1551 26 : bool need_prefix = false;
1552 :
1553 : /* This implements very minimal escaping for names to be used
1554 : * as file names in the cgroup tree: any name which might
1555 : * conflict with a kernel name or is prefixed with '_' is
1556 : * prefixed with a '_'. That way, when reading cgroup names it
1557 : * is sufficient to remove a single prefixing underscore if
1558 : * there is one. */
1559 :
1560 : /* The return value of this function (unlike cg_unescape())
1561 : * needs free()! */
1562 :
1563 51 : if (p[0] == 0 ||
1564 48 : p[0] == '_' ||
1565 44 : p[0] == '.' ||
1566 42 : streq(p, "notify_on_release") ||
1567 42 : streq(p, "release_agent") ||
1568 21 : streq(p, "tasks"))
1569 6 : need_prefix = true;
1570 : else {
1571 : const char *dot;
1572 :
1573 20 : dot = strrchr(p, '.');
1574 20 : if (dot) {
1575 :
1576 19 : if (dot - p == 6 && memcmp(p, "cgroup", 6) == 0)
1577 1 : need_prefix = true;
1578 : else {
1579 : char *n;
1580 :
1581 18 : n = strndupa(p, dot - p);
1582 :
1583 18 : if (check_hierarchy(n) >= 0)
1584 1 : need_prefix = true;
1585 : }
1586 : }
1587 : }
1588 :
1589 26 : if (need_prefix)
1590 8 : return strappend("_", p);
1591 : else
1592 18 : return strdup(p);
1593 : }
1594 :
1595 1773 : char *cg_unescape(const char *p) {
1596 1773 : assert(p);
1597 :
1598 : /* The return value of this function (unlike cg_escape())
1599 : * doesn't need free()! */
1600 :
1601 1773 : if (p[0] == '_')
1602 11 : return (char*) p+1;
1603 :
1604 1762 : return (char*) p;
1605 : }
1606 :
1607 : #define CONTROLLER_VALID \
1608 : DIGITS LETTERS \
1609 : "_"
1610 :
1611 1596 : bool cg_controller_is_valid(const char *p) {
1612 : const char *t, *s;
1613 :
1614 1596 : if (!p)
1615 0 : return false;
1616 :
1617 1596 : s = startswith(p, "name=");
1618 1596 : if (s)
1619 2 : p = s;
1620 :
1621 1596 : if (*p == 0 || *p == '_')
1622 4 : return false;
1623 :
1624 12674 : for (t = p; *t; t++)
1625 11085 : if (!strchr(CONTROLLER_VALID, *t))
1626 3 : return false;
1627 :
1628 1589 : if (t - p > FILENAME_MAX)
1629 0 : return false;
1630 :
1631 1589 : return true;
1632 : }
1633 :
1634 13 : int cg_slice_to_path(const char *unit, char **ret) {
1635 26 : _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
1636 : const char *dash;
1637 : int r;
1638 :
1639 13 : assert(unit);
1640 13 : assert(ret);
1641 :
1642 13 : if (streq(unit, "-.slice")) {
1643 : char *x;
1644 :
1645 1 : x = strdup("");
1646 1 : if (!x)
1647 0 : return -ENOMEM;
1648 1 : *ret = x;
1649 1 : return 0;
1650 : }
1651 :
1652 12 : if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN))
1653 2 : return -EINVAL;
1654 :
1655 10 : if (!endswith(unit, ".slice"))
1656 1 : return -EINVAL;
1657 :
1658 9 : r = unit_name_to_prefix(unit, &p);
1659 9 : if (r < 0)
1660 0 : return r;
1661 :
1662 9 : dash = strchr(p, '-');
1663 :
1664 : /* Don't allow initial dashes */
1665 9 : if (dash == p)
1666 3 : return -EINVAL;
1667 :
1668 18 : while (dash) {
1669 16 : _cleanup_free_ char *escaped = NULL;
1670 8 : char n[dash - p + sizeof(".slice")];
1671 :
1672 : /* Don't allow trailing or double dashes */
1673 8 : if (dash[1] == 0 || dash[1] == '-')
1674 2 : return -EINVAL;
1675 :
1676 6 : strcpy(stpncpy(n, p, dash - p), ".slice");
1677 6 : if (!unit_name_is_valid(n, UNIT_NAME_PLAIN))
1678 0 : return -EINVAL;
1679 :
1680 6 : escaped = cg_escape(n);
1681 6 : if (!escaped)
1682 0 : return -ENOMEM;
1683 :
1684 6 : if (!strextend(&s, escaped, "/", NULL))
1685 0 : return -ENOMEM;
1686 :
1687 6 : dash = strchr(dash+1, '-');
1688 : }
1689 :
1690 4 : e = cg_escape(unit);
1691 4 : if (!e)
1692 0 : return -ENOMEM;
1693 :
1694 4 : if (!strextend(&s, e, NULL))
1695 0 : return -ENOMEM;
1696 :
1697 4 : *ret = s;
1698 4 : s = NULL;
1699 :
1700 4 : return 0;
1701 : }
1702 :
1703 0 : int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
1704 0 : _cleanup_free_ char *p = NULL;
1705 : int r;
1706 :
1707 0 : r = cg_get_path(controller, path, attribute, &p);
1708 0 : if (r < 0)
1709 0 : return r;
1710 :
1711 0 : return write_string_file(p, value, 0);
1712 : }
1713 :
1714 0 : int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret) {
1715 0 : _cleanup_free_ char *p = NULL;
1716 : int r;
1717 :
1718 0 : r = cg_get_path(controller, path, attribute, &p);
1719 0 : if (r < 0)
1720 0 : return r;
1721 :
1722 0 : return read_one_line_file(p, ret);
1723 : }
1724 :
1725 : static const char mask_names[] =
1726 : "cpu\0"
1727 : "cpuacct\0"
1728 : "blkio\0"
1729 : "memory\0"
1730 : "devices\0";
1731 :
1732 12 : int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path) {
1733 12 : CGroupControllerMask bit = 1;
1734 : const char *n;
1735 : int r;
1736 :
1737 : /* This one will create a cgroup in our private tree, but also
1738 : * duplicate it in the trees specified in mask, and remove it
1739 : * in all others */
1740 :
1741 : /* First create the cgroup in our own hierarchy. */
1742 12 : r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
1743 12 : if (r < 0)
1744 6 : return r;
1745 :
1746 : /* Then, do the same in the other hierarchies */
1747 36 : NULSTR_FOREACH(n, mask_names) {
1748 30 : if (mask & bit)
1749 0 : cg_create(n, path);
1750 30 : else if (supported & bit)
1751 30 : cg_trim(n, path, true);
1752 :
1753 30 : bit <<= 1;
1754 : }
1755 :
1756 6 : return 0;
1757 : }
1758 :
1759 0 : int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
1760 0 : CGroupControllerMask bit = 1;
1761 : const char *n;
1762 : int r;
1763 :
1764 0 : r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
1765 0 : if (r < 0)
1766 0 : return r;
1767 :
1768 0 : NULSTR_FOREACH(n, mask_names) {
1769 :
1770 0 : if (supported & bit) {
1771 0 : const char *p = NULL;
1772 :
1773 0 : if (path_callback)
1774 0 : p = path_callback(bit, userdata);
1775 :
1776 0 : if (!p)
1777 0 : p = path;
1778 :
1779 0 : cg_attach_fallback(n, p, pid);
1780 : }
1781 :
1782 0 : bit <<= 1;
1783 : }
1784 :
1785 0 : return 0;
1786 : }
1787 :
1788 0 : int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
1789 : Iterator i;
1790 : void *pidp;
1791 0 : int r = 0;
1792 :
1793 0 : SET_FOREACH(pidp, pids, i) {
1794 0 : pid_t pid = PTR_TO_LONG(pidp);
1795 : int q;
1796 :
1797 0 : q = cg_attach_everywhere(supported, path, pid, path_callback, userdata);
1798 0 : if (q < 0)
1799 0 : r = q;
1800 : }
1801 :
1802 0 : return r;
1803 : }
1804 :
1805 0 : int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
1806 0 : CGroupControllerMask bit = 1;
1807 : const char *n;
1808 : int r;
1809 :
1810 0 : if (!path_equal(from, to)) {
1811 0 : r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, false, true);
1812 0 : if (r < 0)
1813 0 : return r;
1814 : }
1815 :
1816 0 : NULSTR_FOREACH(n, mask_names) {
1817 0 : if (supported & bit) {
1818 0 : const char *p = NULL;
1819 :
1820 0 : if (to_callback)
1821 0 : p = to_callback(bit, userdata);
1822 :
1823 0 : if (!p)
1824 0 : p = to;
1825 :
1826 0 : cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, n, p, false, false);
1827 : }
1828 :
1829 0 : bit <<= 1;
1830 : }
1831 :
1832 0 : return 0;
1833 : }
1834 :
1835 0 : int cg_trim_everywhere(CGroupControllerMask supported, const char *path, bool delete_root) {
1836 0 : CGroupControllerMask bit = 1;
1837 : const char *n;
1838 : int r;
1839 :
1840 0 : r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
1841 0 : if (r < 0)
1842 0 : return r;
1843 :
1844 0 : NULSTR_FOREACH(n, mask_names) {
1845 0 : if (supported & bit)
1846 0 : cg_trim(n, path, delete_root);
1847 :
1848 0 : bit <<= 1;
1849 : }
1850 :
1851 0 : return 0;
1852 : }
1853 :
1854 11 : CGroupControllerMask cg_mask_supported(void) {
1855 11 : CGroupControllerMask bit = 1, mask = 0;
1856 : const char *n;
1857 :
1858 66 : NULSTR_FOREACH(n, mask_names) {
1859 55 : if (check_hierarchy(n) >= 0)
1860 55 : mask |= bit;
1861 :
1862 55 : bit <<= 1;
1863 : }
1864 :
1865 11 : return mask;
1866 : }
1867 :
1868 0 : int cg_kernel_controllers(Set *controllers) {
1869 0 : _cleanup_fclose_ FILE *f = NULL;
1870 : char buf[LINE_MAX];
1871 : int r;
1872 :
1873 0 : assert(controllers);
1874 :
1875 0 : f = fopen("/proc/cgroups", "re");
1876 0 : if (!f) {
1877 0 : if (errno == ENOENT)
1878 0 : return 0;
1879 0 : return -errno;
1880 : }
1881 :
1882 : /* Ignore the header line */
1883 0 : (void) fgets(buf, sizeof(buf), f);
1884 :
1885 : for (;;) {
1886 : char *controller;
1887 0 : int enabled = 0;
1888 :
1889 0 : errno = 0;
1890 0 : if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
1891 :
1892 0 : if (feof(f))
1893 0 : break;
1894 :
1895 0 : if (ferror(f) && errno)
1896 0 : return -errno;
1897 :
1898 0 : return -EBADMSG;
1899 : }
1900 :
1901 0 : if (!enabled) {
1902 0 : free(controller);
1903 0 : continue;
1904 : }
1905 :
1906 0 : if (!filename_is_valid(controller)) {
1907 0 : free(controller);
1908 0 : return -EBADMSG;
1909 : }
1910 :
1911 0 : r = set_consume(controllers, controller);
1912 0 : if (r < 0)
1913 0 : return r;
1914 0 : }
1915 :
1916 0 : return 0;
1917 : }
|