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