LCOV - code coverage report
Current view: top level - basic - cgroup-util.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 642 1529 42.0 %
Date: 2019-08-23 13:36:53 Functions: 65 95 68.4 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 371 1267 29.3 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <dirent.h>
       4                 :            : #include <errno.h>
       5                 :            : #include <ftw.h>
       6                 :            : #include <limits.h>
       7                 :            : #include <signal.h>
       8                 :            : #include <stddef.h>
       9                 :            : #include <stdlib.h>
      10                 :            : #include <string.h>
      11                 :            : #include <sys/stat.h>
      12                 :            : #include <sys/statfs.h>
      13                 :            : #include <sys/types.h>
      14                 :            : #include <sys/utsname.h>
      15                 :            : #include <sys/xattr.h>
      16                 :            : #include <unistd.h>
      17                 :            : 
      18                 :            : #include "alloc-util.h"
      19                 :            : #include "cgroup-util.h"
      20                 :            : #include "def.h"
      21                 :            : #include "dirent-util.h"
      22                 :            : #include "extract-word.h"
      23                 :            : #include "fd-util.h"
      24                 :            : #include "fileio.h"
      25                 :            : #include "format-util.h"
      26                 :            : #include "fs-util.h"
      27                 :            : #include "log.h"
      28                 :            : #include "login-util.h"
      29                 :            : #include "macro.h"
      30                 :            : #include "missing.h"
      31                 :            : #include "mkdir.h"
      32                 :            : #include "parse-util.h"
      33                 :            : #include "path-util.h"
      34                 :            : #include "proc-cmdline.h"
      35                 :            : #include "process-util.h"
      36                 :            : #include "set.h"
      37                 :            : #include "special.h"
      38                 :            : #include "stat-util.h"
      39                 :            : #include "stdio-util.h"
      40                 :            : #include "string-table.h"
      41                 :            : #include "string-util.h"
      42                 :            : #include "strv.h"
      43                 :            : #include "unit-name.h"
      44                 :            : #include "user-util.h"
      45                 :            : 
      46                 :         24 : static int cg_enumerate_items(const char *controller, const char *path, FILE **_f, const char *item) {
      47                 :         24 :         _cleanup_free_ char *fs = NULL;
      48                 :            :         FILE *f;
      49                 :            :         int r;
      50                 :            : 
      51         [ -  + ]:         24 :         assert(_f);
      52                 :            : 
      53                 :         24 :         r = cg_get_path(controller, path, item, &fs);
      54         [ -  + ]:         24 :         if (r < 0)
      55                 :          0 :                 return r;
      56                 :            : 
      57                 :         24 :         f = fopen(fs, "re");
      58         [ +  - ]:         24 :         if (!f)
      59                 :         24 :                 return -errno;
      60                 :            : 
      61                 :          0 :         *_f = f;
      62                 :          0 :         return 0;
      63                 :            : }
      64                 :            : 
      65                 :          0 : int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
      66                 :          0 :         return cg_enumerate_items(controller, path, _f, "cgroup.procs");
      67                 :            : }
      68                 :            : 
      69                 :          0 : int cg_read_pid(FILE *f, pid_t *_pid) {
      70                 :            :         unsigned long ul;
      71                 :            : 
      72                 :            :         /* Note that the cgroup.procs might contain duplicates! See
      73                 :            :          * cgroups.txt for details. */
      74                 :            : 
      75         [ #  # ]:          0 :         assert(f);
      76         [ #  # ]:          0 :         assert(_pid);
      77                 :            : 
      78                 :          0 :         errno = 0;
      79         [ #  # ]:          0 :         if (fscanf(f, "%lu", &ul) != 1) {
      80                 :            : 
      81         [ #  # ]:          0 :                 if (feof(f))
      82                 :          0 :                         return 0;
      83                 :            : 
      84                 :          0 :                 return errno_or_else(EIO);
      85                 :            :         }
      86                 :            : 
      87         [ #  # ]:          0 :         if (ul <= 0)
      88                 :          0 :                 return -EIO;
      89                 :            : 
      90                 :          0 :         *_pid = (pid_t) ul;
      91                 :          0 :         return 1;
      92                 :            : }
      93                 :            : 
      94                 :          0 : int cg_read_event(
      95                 :            :                 const char *controller,
      96                 :            :                 const char *path,
      97                 :            :                 const char *event,
      98                 :            :                 char **ret) {
      99                 :            : 
     100                 :          0 :         _cleanup_free_ char *events = NULL, *content = NULL;
     101                 :            :         int r;
     102                 :            : 
     103                 :          0 :         r = cg_get_path(controller, path, "cgroup.events", &events);
     104         [ #  # ]:          0 :         if (r < 0)
     105                 :          0 :                 return r;
     106                 :            : 
     107                 :          0 :         r = read_full_file(events, &content, NULL);
     108         [ #  # ]:          0 :         if (r < 0)
     109                 :          0 :                 return r;
     110                 :            : 
     111                 :          0 :         for (const char *p = content;;) {
     112   [ #  #  #  #  :          0 :                 _cleanup_free_ char *line = NULL, *key = NULL, *val = NULL;
                   #  # ]
     113                 :            :                 const char *q;
     114                 :            : 
     115                 :          0 :                 r = extract_first_word(&p, &line, "\n", 0);
     116         [ #  # ]:          0 :                 if (r < 0)
     117                 :          0 :                         return r;
     118         [ #  # ]:          0 :                 if (r == 0)
     119                 :          0 :                         return -ENOENT;
     120                 :            : 
     121                 :          0 :                 q = line;
     122                 :          0 :                 r = extract_first_word(&q, &key, " ", 0);
     123         [ #  # ]:          0 :                 if (r < 0)
     124                 :          0 :                         return r;
     125         [ #  # ]:          0 :                 if (r == 0)
     126                 :          0 :                         return -EINVAL;
     127                 :            : 
     128         [ #  # ]:          0 :                 if (!streq(key, event))
     129                 :          0 :                         continue;
     130                 :            : 
     131                 :          0 :                 val = strdup(q);
     132         [ #  # ]:          0 :                 if (!val)
     133                 :          0 :                         return -ENOMEM;
     134                 :            : 
     135                 :          0 :                 *ret = TAKE_PTR(val);
     136                 :          0 :                 return 0;
     137                 :            :         }
     138                 :            : }
     139                 :            : 
     140                 :          0 : bool cg_ns_supported(void) {
     141                 :            :         static thread_local int enabled = -1;
     142                 :            : 
     143         [ #  # ]:          0 :         if (enabled >= 0)
     144                 :          0 :                 return enabled;
     145                 :            : 
     146         [ #  # ]:          0 :         if (access("/proc/self/ns/cgroup", F_OK) < 0) {
     147         [ #  # ]:          0 :                 if (errno != ENOENT)
     148         [ #  # ]:          0 :                         log_debug_errno(errno, "Failed to check whether /proc/self/ns/cgroup is available, assuming not: %m");
     149                 :          0 :                 enabled = false;
     150                 :            :         } else
     151                 :          0 :                 enabled = true;
     152                 :            : 
     153                 :          0 :         return enabled;
     154                 :            : }
     155                 :            : 
     156                 :         24 : int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
     157                 :         24 :         _cleanup_free_ char *fs = NULL;
     158                 :            :         int r;
     159                 :            :         DIR *d;
     160                 :            : 
     161         [ -  + ]:         24 :         assert(_d);
     162                 :            : 
     163                 :            :         /* This is not recursive! */
     164                 :            : 
     165                 :         24 :         r = cg_get_path(controller, path, NULL, &fs);
     166         [ -  + ]:         24 :         if (r < 0)
     167                 :          0 :                 return r;
     168                 :            : 
     169                 :         24 :         d = opendir(fs);
     170         [ +  - ]:         24 :         if (!d)
     171                 :         24 :                 return -errno;
     172                 :            : 
     173                 :          0 :         *_d = d;
     174                 :          0 :         return 0;
     175                 :            : }
     176                 :            : 
     177                 :          0 : int cg_read_subgroup(DIR *d, char **fn) {
     178                 :            :         struct dirent *de;
     179                 :            : 
     180         [ #  # ]:          0 :         assert(d);
     181         [ #  # ]:          0 :         assert(fn);
     182                 :            : 
     183   [ #  #  #  # ]:          0 :         FOREACH_DIRENT_ALL(de, d, return -errno) {
     184                 :            :                 char *b;
     185                 :            : 
     186         [ #  # ]:          0 :                 if (de->d_type != DT_DIR)
     187                 :          0 :                         continue;
     188                 :            : 
     189         [ #  # ]:          0 :                 if (dot_or_dot_dot(de->d_name))
     190                 :          0 :                         continue;
     191                 :            : 
     192                 :          0 :                 b = strdup(de->d_name);
     193         [ #  # ]:          0 :                 if (!b)
     194                 :          0 :                         return -ENOMEM;
     195                 :            : 
     196                 :          0 :                 *fn = b;
     197                 :          0 :                 return 1;
     198                 :            :         }
     199                 :            : 
     200                 :          0 :         return 0;
     201                 :            : }
     202                 :            : 
     203                 :          0 : int cg_rmdir(const char *controller, const char *path) {
     204                 :          0 :         _cleanup_free_ char *p = NULL;
     205                 :            :         int r;
     206                 :            : 
     207                 :          0 :         r = cg_get_path(controller, path, NULL, &p);
     208         [ #  # ]:          0 :         if (r < 0)
     209                 :          0 :                 return r;
     210                 :            : 
     211                 :          0 :         r = rmdir(p);
     212   [ #  #  #  # ]:          0 :         if (r < 0 && errno != ENOENT)
     213                 :          0 :                 return -errno;
     214                 :            : 
     215                 :          0 :         r = cg_hybrid_unified();
     216         [ #  # ]:          0 :         if (r <= 0)
     217                 :          0 :                 return r;
     218                 :            : 
     219         [ #  # ]:          0 :         if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
     220                 :          0 :                 r = cg_rmdir(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path);
     221         [ #  # ]:          0 :                 if (r < 0)
     222         [ #  # ]:          0 :                         log_warning_errno(r, "Failed to remove compat systemd cgroup %s: %m", path);
     223                 :            :         }
     224                 :            : 
     225                 :          0 :         return 0;
     226                 :            : }
     227                 :            : 
     228                 :         24 : static int cg_kill_items(
     229                 :            :                 const char *controller,
     230                 :            :                 const char *path,
     231                 :            :                 int sig,
     232                 :            :                 CGroupFlags flags,
     233                 :            :                 Set *s,
     234                 :            :                 cg_kill_log_func_t log_kill,
     235                 :            :                 void *userdata,
     236                 :            :                 const char *item) {
     237                 :            : 
     238                 :         24 :         _cleanup_set_free_ Set *allocated_set = NULL;
     239                 :         24 :         bool done = false;
     240                 :         24 :         int r, ret = 0, ret_log_kill = 0;
     241                 :            :         pid_t my_pid;
     242                 :            : 
     243         [ -  + ]:         24 :         assert(sig >= 0);
     244                 :            : 
     245                 :            :          /* Don't send SIGCONT twice. Also, SIGKILL always works even when process is suspended, hence don't send
     246                 :            :           * SIGCONT on SIGKILL. */
     247   [ -  +  -  + ]:         24 :         if (IN_SET(sig, SIGCONT, SIGKILL))
     248                 :          0 :                 flags &= ~CGROUP_SIGCONT;
     249                 :            : 
     250                 :            :         /* This goes through the tasks list and kills them all. This
     251                 :            :          * is repeated until no further processes are added to the
     252                 :            :          * tasks list, to properly handle forking processes */
     253                 :            : 
     254         [ -  + ]:         24 :         if (!s) {
     255                 :          0 :                 s = allocated_set = set_new(NULL);
     256         [ #  # ]:          0 :                 if (!s)
     257                 :          0 :                         return -ENOMEM;
     258                 :            :         }
     259                 :            : 
     260                 :         24 :         my_pid = getpid_cached();
     261                 :            : 
     262                 :            :         do {
     263         [ -  + ]:         24 :                 _cleanup_fclose_ FILE *f = NULL;
     264                 :         24 :                 pid_t pid = 0;
     265                 :         24 :                 done = true;
     266                 :            : 
     267                 :         24 :                 r = cg_enumerate_items(controller, path, &f, item);
     268         [ +  - ]:         24 :                 if (r < 0) {
     269   [ +  -  -  + ]:         24 :                         if (ret >= 0 && r != -ENOENT)
     270                 :          0 :                                 return r;
     271                 :            : 
     272                 :         24 :                         return ret;
     273                 :            :                 }
     274                 :            : 
     275         [ #  # ]:          0 :                 while ((r = cg_read_pid(f, &pid)) > 0) {
     276                 :            : 
     277   [ #  #  #  # ]:          0 :                         if ((flags & CGROUP_IGNORE_SELF) && pid == my_pid)
     278                 :          0 :                                 continue;
     279                 :            : 
     280         [ #  # ]:          0 :                         if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid))
     281                 :          0 :                                 continue;
     282                 :            : 
     283         [ #  # ]:          0 :                         if (log_kill)
     284                 :          0 :                                 ret_log_kill = log_kill(pid, sig, userdata);
     285                 :            : 
     286                 :            :                         /* If we haven't killed this process yet, kill
     287                 :            :                          * it */
     288         [ #  # ]:          0 :                         if (kill(pid, sig) < 0) {
     289   [ #  #  #  # ]:          0 :                                 if (ret >= 0 && errno != ESRCH)
     290                 :          0 :                                         ret = -errno;
     291                 :            :                         } else {
     292         [ #  # ]:          0 :                                 if (flags & CGROUP_SIGCONT)
     293                 :          0 :                                         (void) kill(pid, SIGCONT);
     294                 :            : 
     295         [ #  # ]:          0 :                                 if (ret == 0) {
     296         [ #  # ]:          0 :                                         if (log_kill)
     297                 :          0 :                                                 ret = ret_log_kill;
     298                 :            :                                         else
     299                 :          0 :                                                 ret = 1;
     300                 :            :                                 }
     301                 :            :                         }
     302                 :            : 
     303                 :          0 :                         done = false;
     304                 :            : 
     305                 :          0 :                         r = set_put(s, PID_TO_PTR(pid));
     306         [ #  # ]:          0 :                         if (r < 0) {
     307         [ #  # ]:          0 :                                 if (ret >= 0)
     308                 :          0 :                                         return r;
     309                 :            : 
     310                 :          0 :                                 return ret;
     311                 :            :                         }
     312                 :            :                 }
     313                 :            : 
     314         [ #  # ]:          0 :                 if (r < 0) {
     315         [ #  # ]:          0 :                         if (ret >= 0)
     316                 :          0 :                                 return r;
     317                 :            : 
     318                 :          0 :                         return ret;
     319                 :            :                 }
     320                 :            : 
     321                 :            :                 /* To avoid racing against processes which fork
     322                 :            :                  * quicker than we can kill them we repeat this until
     323                 :            :                  * no new pids need to be killed. */
     324                 :            : 
     325         [ #  # ]:          0 :         } while (!done);
     326                 :            : 
     327                 :          0 :         return ret;
     328                 :            : }
     329                 :            : 
     330                 :         24 : int cg_kill(
     331                 :            :                 const char *controller,
     332                 :            :                 const char *path,
     333                 :            :                 int sig,
     334                 :            :                 CGroupFlags flags,
     335                 :            :                 Set *s,
     336                 :            :                 cg_kill_log_func_t log_kill,
     337                 :            :                 void *userdata) {
     338                 :            :         int r;
     339                 :            : 
     340                 :         24 :         r = cg_kill_items(controller, path, sig, flags, s, log_kill, userdata, "cgroup.procs");
     341   [ +  -  +  - ]:         24 :         if (r < 0 || sig != SIGKILL)
     342                 :         24 :                 return r;
     343                 :            : 
     344                 :            :         /* Only in case of killing with SIGKILL and when using cgroupsv2, kill remaining threads manually as
     345                 :            :            a workaround for kernel bug. It was fixed in 5.2-rc5 (c03cd7738a83), backported to 4.19.66
     346                 :            :            (4340d175b898) and 4.14.138 (feb6b123b7dd). */
     347                 :          0 :         r = cg_unified_controller(controller);
     348         [ #  # ]:          0 :         if (r < 0)
     349                 :          0 :                 return r;
     350         [ #  # ]:          0 :         if (r == 0) /* doesn't apply to legacy hierarchy */
     351                 :          0 :                 return 0;
     352                 :            : 
     353                 :          0 :         return cg_kill_items(controller, path, sig, flags, s, log_kill, userdata, "cgroup.threads");
     354                 :            : }
     355                 :            : 
     356                 :         24 : int cg_kill_recursive(
     357                 :            :                 const char *controller,
     358                 :            :                 const char *path,
     359                 :            :                 int sig,
     360                 :            :                 CGroupFlags flags,
     361                 :            :                 Set *s,
     362                 :            :                 cg_kill_log_func_t log_kill,
     363                 :            :                 void *userdata) {
     364                 :            : 
     365                 :         24 :         _cleanup_set_free_ Set *allocated_set = NULL;
     366                 :         24 :         _cleanup_closedir_ DIR *d = NULL;
     367                 :            :         int r, ret;
     368                 :            :         char *fn;
     369                 :            : 
     370         [ -  + ]:         24 :         assert(path);
     371         [ -  + ]:         24 :         assert(sig >= 0);
     372                 :            : 
     373         [ +  - ]:         24 :         if (!s) {
     374                 :         24 :                 s = allocated_set = set_new(NULL);
     375         [ -  + ]:         24 :                 if (!s)
     376                 :          0 :                         return -ENOMEM;
     377                 :            :         }
     378                 :            : 
     379                 :         24 :         ret = cg_kill(controller, path, sig, flags, s, log_kill, userdata);
     380                 :            : 
     381                 :         24 :         r = cg_enumerate_subgroups(controller, path, &d);
     382         [ +  - ]:         24 :         if (r < 0) {
     383   [ +  -  -  + ]:         24 :                 if (ret >= 0 && r != -ENOENT)
     384                 :          0 :                         return r;
     385                 :            : 
     386                 :         24 :                 return ret;
     387                 :            :         }
     388                 :            : 
     389         [ #  # ]:          0 :         while ((r = cg_read_subgroup(d, &fn)) > 0) {
     390         [ #  # ]:          0 :                 _cleanup_free_ char *p = NULL;
     391                 :            : 
     392                 :          0 :                 p = path_join(empty_to_root(path), fn);
     393                 :          0 :                 free(fn);
     394         [ #  # ]:          0 :                 if (!p)
     395                 :          0 :                         return -ENOMEM;
     396                 :            : 
     397                 :          0 :                 r = cg_kill_recursive(controller, p, sig, flags, s, log_kill, userdata);
     398   [ #  #  #  # ]:          0 :                 if (r != 0 && ret >= 0)
     399                 :          0 :                         ret = r;
     400                 :            :         }
     401   [ #  #  #  # ]:          0 :         if (ret >= 0 && r < 0)
     402                 :          0 :                 ret = r;
     403                 :            : 
     404         [ #  # ]:          0 :         if (flags & CGROUP_REMOVE) {
     405                 :          0 :                 r = cg_rmdir(controller, path);
     406   [ #  #  #  #  :          0 :                 if (r < 0 && ret >= 0 && !IN_SET(r, -ENOENT, -EBUSY))
             #  #  #  # ]
     407                 :          0 :                         return r;
     408                 :            :         }
     409                 :            : 
     410                 :          0 :         return ret;
     411                 :            : }
     412                 :            : 
     413                 :          0 : int cg_migrate(
     414                 :            :                 const char *cfrom,
     415                 :            :                 const char *pfrom,
     416                 :            :                 const char *cto,
     417                 :            :                 const char *pto,
     418                 :            :                 CGroupFlags flags) {
     419                 :            : 
     420                 :          0 :         bool done = false;
     421                 :          0 :         _cleanup_set_free_ Set *s = NULL;
     422                 :          0 :         int r, ret = 0;
     423                 :            :         pid_t my_pid;
     424                 :            : 
     425         [ #  # ]:          0 :         assert(cfrom);
     426         [ #  # ]:          0 :         assert(pfrom);
     427         [ #  # ]:          0 :         assert(cto);
     428         [ #  # ]:          0 :         assert(pto);
     429                 :            : 
     430                 :          0 :         s = set_new(NULL);
     431         [ #  # ]:          0 :         if (!s)
     432                 :          0 :                 return -ENOMEM;
     433                 :            : 
     434                 :          0 :         my_pid = getpid_cached();
     435                 :            : 
     436                 :            :         do {
     437         [ #  # ]:          0 :                 _cleanup_fclose_ FILE *f = NULL;
     438                 :          0 :                 pid_t pid = 0;
     439                 :          0 :                 done = true;
     440                 :            : 
     441                 :          0 :                 r = cg_enumerate_processes(cfrom, pfrom, &f);
     442         [ #  # ]:          0 :                 if (r < 0) {
     443   [ #  #  #  # ]:          0 :                         if (ret >= 0 && r != -ENOENT)
     444                 :          0 :                                 return r;
     445                 :            : 
     446                 :          0 :                         return ret;
     447                 :            :                 }
     448                 :            : 
     449         [ #  # ]:          0 :                 while ((r = cg_read_pid(f, &pid)) > 0) {
     450                 :            : 
     451                 :            :                         /* This might do weird stuff if we aren't a
     452                 :            :                          * single-threaded program. However, we
     453                 :            :                          * luckily know we are not */
     454   [ #  #  #  # ]:          0 :                         if ((flags & CGROUP_IGNORE_SELF) && pid == my_pid)
     455                 :          0 :                                 continue;
     456                 :            : 
     457         [ #  # ]:          0 :                         if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid))
     458                 :          0 :                                 continue;
     459                 :            : 
     460                 :            :                         /* Ignore kernel threads. Since they can only
     461                 :            :                          * exist in the root cgroup, we only check for
     462                 :            :                          * them there. */
     463   [ #  #  #  # ]:          0 :                         if (cfrom &&
     464         [ #  # ]:          0 :                             empty_or_root(pfrom) &&
     465                 :          0 :                             is_kernel_thread(pid) > 0)
     466                 :          0 :                                 continue;
     467                 :            : 
     468                 :          0 :                         r = cg_attach(cto, pto, pid);
     469         [ #  # ]:          0 :                         if (r < 0) {
     470   [ #  #  #  # ]:          0 :                                 if (ret >= 0 && r != -ESRCH)
     471                 :          0 :                                         ret = r;
     472         [ #  # ]:          0 :                         } else if (ret == 0)
     473                 :          0 :                                 ret = 1;
     474                 :            : 
     475                 :          0 :                         done = false;
     476                 :            : 
     477                 :          0 :                         r = set_put(s, PID_TO_PTR(pid));
     478         [ #  # ]:          0 :                         if (r < 0) {
     479         [ #  # ]:          0 :                                 if (ret >= 0)
     480                 :          0 :                                         return r;
     481                 :            : 
     482                 :          0 :                                 return ret;
     483                 :            :                         }
     484                 :            :                 }
     485                 :            : 
     486         [ #  # ]:          0 :                 if (r < 0) {
     487         [ #  # ]:          0 :                         if (ret >= 0)
     488                 :          0 :                                 return r;
     489                 :            : 
     490                 :          0 :                         return ret;
     491                 :            :                 }
     492         [ #  # ]:          0 :         } while (!done);
     493                 :            : 
     494                 :          0 :         return ret;
     495                 :            : }
     496                 :            : 
     497                 :          0 : int cg_migrate_recursive(
     498                 :            :                 const char *cfrom,
     499                 :            :                 const char *pfrom,
     500                 :            :                 const char *cto,
     501                 :            :                 const char *pto,
     502                 :            :                 CGroupFlags flags) {
     503                 :            : 
     504                 :          0 :         _cleanup_closedir_ DIR *d = NULL;
     505                 :          0 :         int r, ret = 0;
     506                 :            :         char *fn;
     507                 :            : 
     508         [ #  # ]:          0 :         assert(cfrom);
     509         [ #  # ]:          0 :         assert(pfrom);
     510         [ #  # ]:          0 :         assert(cto);
     511         [ #  # ]:          0 :         assert(pto);
     512                 :            : 
     513                 :          0 :         ret = cg_migrate(cfrom, pfrom, cto, pto, flags);
     514                 :            : 
     515                 :          0 :         r = cg_enumerate_subgroups(cfrom, pfrom, &d);
     516         [ #  # ]:          0 :         if (r < 0) {
     517   [ #  #  #  # ]:          0 :                 if (ret >= 0 && r != -ENOENT)
     518                 :          0 :                         return r;
     519                 :            : 
     520                 :          0 :                 return ret;
     521                 :            :         }
     522                 :            : 
     523         [ #  # ]:          0 :         while ((r = cg_read_subgroup(d, &fn)) > 0) {
     524         [ #  # ]:          0 :                 _cleanup_free_ char *p = NULL;
     525                 :            : 
     526                 :          0 :                 p = path_join(empty_to_root(pfrom), fn);
     527                 :          0 :                 free(fn);
     528         [ #  # ]:          0 :                 if (!p)
     529                 :          0 :                         return -ENOMEM;
     530                 :            : 
     531                 :          0 :                 r = cg_migrate_recursive(cfrom, p, cto, pto, flags);
     532   [ #  #  #  # ]:          0 :                 if (r != 0 && ret >= 0)
     533                 :          0 :                         ret = r;
     534                 :            :         }
     535                 :            : 
     536   [ #  #  #  # ]:          0 :         if (r < 0 && ret >= 0)
     537                 :          0 :                 ret = r;
     538                 :            : 
     539         [ #  # ]:          0 :         if (flags & CGROUP_REMOVE) {
     540                 :          0 :                 r = cg_rmdir(cfrom, pfrom);
     541   [ #  #  #  #  :          0 :                 if (r < 0 && ret >= 0 && !IN_SET(r, -ENOENT, -EBUSY))
             #  #  #  # ]
     542                 :          0 :                         return r;
     543                 :            :         }
     544                 :            : 
     545                 :          0 :         return ret;
     546                 :            : }
     547                 :            : 
     548                 :          0 : int cg_migrate_recursive_fallback(
     549                 :            :                 const char *cfrom,
     550                 :            :                 const char *pfrom,
     551                 :            :                 const char *cto,
     552                 :            :                 const char *pto,
     553                 :            :                 CGroupFlags flags) {
     554                 :            : 
     555                 :            :         int r;
     556                 :            : 
     557         [ #  # ]:          0 :         assert(cfrom);
     558         [ #  # ]:          0 :         assert(pfrom);
     559         [ #  # ]:          0 :         assert(cto);
     560         [ #  # ]:          0 :         assert(pto);
     561                 :            : 
     562                 :          0 :         r = cg_migrate_recursive(cfrom, pfrom, cto, pto, flags);
     563         [ #  # ]:          0 :         if (r < 0) {
     564                 :          0 :                 char prefix[strlen(pto) + 1];
     565                 :            : 
     566                 :            :                 /* This didn't work? Then let's try all prefixes of the destination */
     567                 :            : 
     568   [ #  #  #  # ]:          0 :                 PATH_FOREACH_PREFIX(prefix, pto) {
     569                 :            :                         int q;
     570                 :            : 
     571                 :          0 :                         q = cg_migrate_recursive(cfrom, pfrom, cto, prefix, flags);
     572         [ #  # ]:          0 :                         if (q >= 0)
     573                 :          0 :                                 return q;
     574                 :            :                 }
     575                 :            :         }
     576                 :            : 
     577                 :          0 :         return r;
     578                 :            : }
     579                 :            : 
     580                 :       1968 : static const char *controller_to_dirname(const char *controller) {
     581                 :            :         const char *e;
     582                 :            : 
     583         [ -  + ]:       1968 :         assert(controller);
     584                 :            : 
     585                 :            :         /* Converts a controller name to the directory name below
     586                 :            :          * /sys/fs/cgroup/ we want to mount it to. Effectively, this
     587                 :            :          * just cuts off the name= prefixed used for named
     588                 :            :          * hierarchies, if it is specified. */
     589                 :            : 
     590         [ +  + ]:       1968 :         if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
     591         [ +  - ]:        428 :                 if (cg_hybrid_unified() > 0)
     592                 :        428 :                         controller = SYSTEMD_CGROUP_CONTROLLER_HYBRID;
     593                 :            :                 else
     594                 :          0 :                         controller = SYSTEMD_CGROUP_CONTROLLER_LEGACY;
     595                 :            :         }
     596                 :            : 
     597                 :       1968 :         e = startswith(controller, "name=");
     598         [ +  + ]:       1968 :         if (e)
     599                 :        428 :                 return e;
     600                 :            : 
     601                 :       1540 :         return controller;
     602                 :            : }
     603                 :            : 
     604                 :        600 : static int join_path_legacy(const char *controller, const char *path, const char *suffix, char **fs) {
     605                 :            :         const char *dn;
     606                 :        600 :         char *t = NULL;
     607                 :            : 
     608         [ -  + ]:        600 :         assert(fs);
     609         [ -  + ]:        600 :         assert(controller);
     610                 :            : 
     611                 :        600 :         dn = controller_to_dirname(controller);
     612                 :            : 
     613   [ +  +  -  + ]:        600 :         if (isempty(path) && isempty(suffix))
     614                 :          0 :                 t = path_join("/sys/fs/cgroup", dn);
     615         [ +  + ]:        600 :         else if (isempty(path))
     616                 :        216 :                 t = path_join("/sys/fs/cgroup", dn, suffix);
     617         [ +  + ]:        384 :         else if (isempty(suffix))
     618                 :        332 :                 t = path_join("/sys/fs/cgroup", dn, path);
     619                 :            :         else
     620                 :         52 :                 t = path_join("/sys/fs/cgroup", dn, path, suffix);
     621         [ -  + ]:        600 :         if (!t)
     622                 :          0 :                 return -ENOMEM;
     623                 :            : 
     624                 :        600 :         *fs = t;
     625                 :        600 :         return 0;
     626                 :            : }
     627                 :            : 
     628                 :          0 : static int join_path_unified(const char *path, const char *suffix, char **fs) {
     629                 :            :         char *t;
     630                 :            : 
     631         [ #  # ]:          0 :         assert(fs);
     632                 :            : 
     633   [ #  #  #  # ]:          0 :         if (isempty(path) && isempty(suffix))
     634                 :          0 :                 t = strdup("/sys/fs/cgroup");
     635         [ #  # ]:          0 :         else if (isempty(path))
     636                 :          0 :                 t = path_join("/sys/fs/cgroup", suffix);
     637         [ #  # ]:          0 :         else if (isempty(suffix))
     638                 :          0 :                 t = path_join("/sys/fs/cgroup", path);
     639                 :            :         else
     640                 :          0 :                 t = path_join("/sys/fs/cgroup", path, suffix);
     641         [ #  # ]:          0 :         if (!t)
     642                 :          0 :                 return -ENOMEM;
     643                 :            : 
     644                 :          0 :         *fs = t;
     645                 :          0 :         return 0;
     646                 :            : }
     647                 :            : 
     648                 :        600 : int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
     649                 :            :         int r;
     650                 :            : 
     651         [ -  + ]:        600 :         assert(fs);
     652                 :            : 
     653         [ -  + ]:        600 :         if (!controller) {
     654                 :            :                 char *t;
     655                 :            : 
     656                 :            :                 /* If no controller is specified, we return the path
     657                 :            :                  * *below* the controllers, without any prefix. */
     658                 :            : 
     659   [ #  #  #  # ]:          0 :                 if (!path && !suffix)
     660                 :          0 :                         return -EINVAL;
     661                 :            : 
     662         [ #  # ]:          0 :                 if (!suffix)
     663                 :          0 :                         t = strdup(path);
     664         [ #  # ]:          0 :                 else if (!path)
     665                 :          0 :                         t = strdup(suffix);
     666                 :            :                 else
     667                 :          0 :                         t = path_join(path, suffix);
     668         [ #  # ]:          0 :                 if (!t)
     669                 :          0 :                         return -ENOMEM;
     670                 :            : 
     671                 :          0 :                 *fs = path_simplify(t, false);
     672                 :          0 :                 return 0;
     673                 :            :         }
     674                 :            : 
     675         [ -  + ]:        600 :         if (!cg_controller_is_valid(controller))
     676                 :          0 :                 return -EINVAL;
     677                 :            : 
     678                 :        600 :         r = cg_all_unified();
     679         [ -  + ]:        600 :         if (r < 0)
     680                 :          0 :                 return r;
     681         [ -  + ]:        600 :         if (r > 0)
     682                 :          0 :                 r = join_path_unified(path, suffix, fs);
     683                 :            :         else
     684                 :        600 :                 r = join_path_legacy(controller, path, suffix, fs);
     685         [ -  + ]:        600 :         if (r < 0)
     686                 :          0 :                 return r;
     687                 :            : 
     688                 :        600 :         path_simplify(*fs, false);
     689                 :        600 :         return 0;
     690                 :            : }
     691                 :            : 
     692                 :       1368 : static int controller_is_accessible(const char *controller) {
     693                 :            :         int r;
     694                 :            : 
     695         [ -  + ]:       1368 :         assert(controller);
     696                 :            : 
     697                 :            :         /* Checks whether a specific controller is accessible,
     698                 :            :          * i.e. its hierarchy mounted. In the unified hierarchy all
     699                 :            :          * controllers are considered accessible, except for the named
     700                 :            :          * hierarchies */
     701                 :            : 
     702         [ -  + ]:       1368 :         if (!cg_controller_is_valid(controller))
     703                 :          0 :                 return -EINVAL;
     704                 :            : 
     705                 :       1368 :         r = cg_all_unified();
     706         [ -  + ]:       1368 :         if (r < 0)
     707                 :          0 :                 return r;
     708         [ -  + ]:       1368 :         if (r > 0) {
     709                 :            :                 /* We don't support named hierarchies if we are using
     710                 :            :                  * the unified hierarchy. */
     711                 :            : 
     712         [ #  # ]:          0 :                 if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
     713                 :          0 :                         return 0;
     714                 :            : 
     715         [ #  # ]:          0 :                 if (startswith(controller, "name="))
     716                 :          0 :                         return -EOPNOTSUPP;
     717                 :            : 
     718                 :            :         } else {
     719                 :            :                 const char *cc, *dn;
     720                 :            : 
     721                 :       1368 :                 dn = controller_to_dirname(controller);
     722   [ +  +  +  -  :       6840 :                 cc = strjoina("/sys/fs/cgroup/", dn);
          -  +  -  +  +  
                +  +  - ]
     723                 :            : 
     724         [ -  + ]:       1368 :                 if (laccess(cc, F_OK) < 0)
     725                 :          0 :                         return -errno;
     726                 :            :         }
     727                 :            : 
     728                 :       1368 :         return 0;
     729                 :            : }
     730                 :            : 
     731                 :        216 : int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) {
     732                 :            :         int r;
     733                 :            : 
     734         [ -  + ]:        216 :         assert(controller);
     735         [ -  + ]:        216 :         assert(fs);
     736                 :            : 
     737                 :            :         /* Check if the specified controller is actually accessible */
     738                 :        216 :         r = controller_is_accessible(controller);
     739         [ -  + ]:        216 :         if (r < 0)
     740                 :          0 :                 return r;
     741                 :            : 
     742                 :        216 :         return cg_get_path(controller, path, suffix, fs);
     743                 :            : }
     744                 :            : 
     745                 :          0 : static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
     746         [ #  # ]:          0 :         assert(path);
     747         [ #  # ]:          0 :         assert(sb);
     748         [ #  # ]:          0 :         assert(ftwbuf);
     749                 :            : 
     750         [ #  # ]:          0 :         if (typeflag != FTW_DP)
     751                 :          0 :                 return 0;
     752                 :            : 
     753         [ #  # ]:          0 :         if (ftwbuf->level < 1)
     754                 :          0 :                 return 0;
     755                 :            : 
     756                 :          0 :         (void) rmdir(path);
     757                 :          0 :         return 0;
     758                 :            : }
     759                 :            : 
     760                 :         72 : int cg_trim(const char *controller, const char *path, bool delete_root) {
     761                 :         72 :         _cleanup_free_ char *fs = NULL;
     762                 :         72 :         int r = 0, q;
     763                 :            : 
     764         [ -  + ]:         72 :         assert(path);
     765                 :            : 
     766                 :         72 :         r = cg_get_path(controller, path, NULL, &fs);
     767         [ -  + ]:         72 :         if (r < 0)
     768                 :          0 :                 return r;
     769                 :            : 
     770                 :         72 :         errno = 0;
     771         [ +  - ]:         72 :         if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0) {
     772         [ +  - ]:         72 :                 if (errno == ENOENT)
     773                 :         72 :                         r = 0;
     774                 :            :                 else
     775                 :          0 :                         r = errno_or_else(EIO);
     776                 :            :         }
     777                 :            : 
     778         [ +  - ]:         72 :         if (delete_root) {
     779   [ +  -  -  + ]:         72 :                 if (rmdir(fs) < 0 && errno != ENOENT)
     780                 :          0 :                         return -errno;
     781                 :            :         }
     782                 :            : 
     783                 :         72 :         q = cg_hybrid_unified();
     784         [ -  + ]:         72 :         if (q < 0)
     785                 :          0 :                 return q;
     786   [ +  -  -  + ]:         72 :         if (q > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
     787                 :          0 :                 q = cg_trim(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, delete_root);
     788         [ #  # ]:          0 :                 if (q < 0)
     789         [ #  # ]:          0 :                         log_warning_errno(q, "Failed to trim compat systemd cgroup %s: %m", path);
     790                 :            :         }
     791                 :            : 
     792                 :         72 :         return r;
     793                 :            : }
     794                 :            : 
     795                 :            : /* Create a cgroup in the hierarchy of controller.
     796                 :            :  * Returns 0 if the group already existed, 1 on success, negative otherwise.
     797                 :            :  */
     798                 :        192 : int cg_create(const char *controller, const char *path) {
     799                 :        192 :         _cleanup_free_ char *fs = NULL;
     800                 :            :         int r;
     801                 :            : 
     802                 :        192 :         r = cg_get_path_and_check(controller, path, NULL, &fs);
     803         [ -  + ]:        192 :         if (r < 0)
     804                 :          0 :                 return r;
     805                 :            : 
     806                 :        192 :         r = mkdir_parents(fs, 0755);
     807         [ -  + ]:        192 :         if (r < 0)
     808                 :          0 :                 return r;
     809                 :            : 
     810                 :        192 :         r = mkdir_errno_wrapper(fs, 0755);
     811         [ +  + ]:        192 :         if (r == -EEXIST)
     812                 :         72 :                 return 0;
     813         [ +  - ]:        120 :         if (r < 0)
     814                 :        120 :                 return r;
     815                 :            : 
     816                 :          0 :         r = cg_hybrid_unified();
     817         [ #  # ]:          0 :         if (r < 0)
     818                 :          0 :                 return r;
     819                 :            : 
     820   [ #  #  #  # ]:          0 :         if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
     821                 :          0 :                 r = cg_create(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path);
     822         [ #  # ]:          0 :                 if (r < 0)
     823         [ #  # ]:          0 :                         log_warning_errno(r, "Failed to create compat systemd cgroup %s: %m", path);
     824                 :            :         }
     825                 :            : 
     826                 :          0 :         return 1;
     827                 :            : }
     828                 :            : 
     829                 :         44 : int cg_create_and_attach(const char *controller, const char *path, pid_t pid) {
     830                 :            :         int r, q;
     831                 :            : 
     832         [ -  + ]:         44 :         assert(pid >= 0);
     833                 :            : 
     834                 :         44 :         r = cg_create(controller, path);
     835         [ +  - ]:         44 :         if (r < 0)
     836                 :         44 :                 return r;
     837                 :            : 
     838                 :          0 :         q = cg_attach(controller, path, pid);
     839         [ #  # ]:          0 :         if (q < 0)
     840                 :          0 :                 return q;
     841                 :            : 
     842                 :            :         /* This does not remove the cgroup on failure */
     843                 :          0 :         return r;
     844                 :            : }
     845                 :            : 
     846                 :         24 : int cg_attach(const char *controller, const char *path, pid_t pid) {
     847                 :         24 :         _cleanup_free_ char *fs = NULL;
     848                 :            :         char c[DECIMAL_STR_MAX(pid_t) + 2];
     849                 :            :         int r;
     850                 :            : 
     851         [ -  + ]:         24 :         assert(path);
     852         [ -  + ]:         24 :         assert(pid >= 0);
     853                 :            : 
     854                 :         24 :         r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs);
     855         [ -  + ]:         24 :         if (r < 0)
     856                 :          0 :                 return r;
     857                 :            : 
     858         [ -  + ]:         24 :         if (pid == 0)
     859                 :          0 :                 pid = getpid_cached();
     860                 :            : 
     861         [ -  + ]:         24 :         xsprintf(c, PID_FMT "\n", pid);
     862                 :            : 
     863                 :         24 :         r = write_string_file(fs, c, WRITE_STRING_FILE_DISABLE_BUFFER);
     864         [ +  - ]:         24 :         if (r < 0)
     865                 :         24 :                 return r;
     866                 :            : 
     867                 :          0 :         r = cg_hybrid_unified();
     868         [ #  # ]:          0 :         if (r < 0)
     869                 :          0 :                 return r;
     870                 :            : 
     871   [ #  #  #  # ]:          0 :         if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
     872                 :          0 :                 r = cg_attach(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, pid);
     873         [ #  # ]:          0 :                 if (r < 0)
     874         [ #  # ]:          0 :                         log_warning_errno(r, "Failed to attach "PID_FMT" to compat systemd cgroup %s: %m", pid, path);
     875                 :            :         }
     876                 :            : 
     877                 :          0 :         return 0;
     878                 :            : }
     879                 :            : 
     880                 :          0 : int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
     881                 :            :         int r;
     882                 :            : 
     883         [ #  # ]:          0 :         assert(controller);
     884         [ #  # ]:          0 :         assert(path);
     885         [ #  # ]:          0 :         assert(pid >= 0);
     886                 :            : 
     887                 :          0 :         r = cg_attach(controller, path, pid);
     888         [ #  # ]:          0 :         if (r < 0) {
     889                 :          0 :                 char prefix[strlen(path) + 1];
     890                 :            : 
     891                 :            :                 /* This didn't work? Then let's try all prefixes of
     892                 :            :                  * the destination */
     893                 :            : 
     894   [ #  #  #  # ]:          0 :                 PATH_FOREACH_PREFIX(prefix, path) {
     895                 :            :                         int q;
     896                 :            : 
     897                 :          0 :                         q = cg_attach(controller, prefix, pid);
     898         [ #  # ]:          0 :                         if (q >= 0)
     899                 :          0 :                                 return q;
     900                 :            :                 }
     901                 :            :         }
     902                 :            : 
     903                 :          0 :         return r;
     904                 :            : }
     905                 :            : 
     906                 :          0 : int cg_set_access(
     907                 :            :                 const char *controller,
     908                 :            :                 const char *path,
     909                 :            :                 uid_t uid,
     910                 :            :                 gid_t gid) {
     911                 :            : 
     912                 :            :         struct Attribute {
     913                 :            :                 const char *name;
     914                 :            :                 bool fatal;
     915                 :            :         };
     916                 :            : 
     917                 :            :         /* cgroup v1, aka legacy/non-unified */
     918                 :            :         static const struct Attribute legacy_attributes[] = {
     919                 :            :                 { "cgroup.procs",           true  },
     920                 :            :                 { "tasks",                  false },
     921                 :            :                 { "cgroup.clone_children",  false },
     922                 :            :                 {},
     923                 :            :         };
     924                 :            : 
     925                 :            :         /* cgroup v2, aka unified */
     926                 :            :         static const struct Attribute unified_attributes[] = {
     927                 :            :                 { "cgroup.procs",           true  },
     928                 :            :                 { "cgroup.subtree_control", true  },
     929                 :            :                 { "cgroup.threads",         false },
     930                 :            :                 {},
     931                 :            :         };
     932                 :            : 
     933                 :            :         static const struct Attribute* const attributes[] = {
     934                 :            :                 [false] = legacy_attributes,
     935                 :            :                 [true]  = unified_attributes,
     936                 :            :         };
     937                 :            : 
     938                 :          0 :         _cleanup_free_ char *fs = NULL;
     939                 :            :         const struct Attribute *i;
     940                 :            :         int r, unified;
     941                 :            : 
     942         [ #  # ]:          0 :         assert(path);
     943                 :            : 
     944   [ #  #  #  # ]:          0 :         if (uid == UID_INVALID && gid == GID_INVALID)
     945                 :          0 :                 return 0;
     946                 :            : 
     947                 :          0 :         unified = cg_unified_controller(controller);
     948         [ #  # ]:          0 :         if (unified < 0)
     949                 :          0 :                 return unified;
     950                 :            : 
     951                 :            :         /* Configure access to the cgroup itself */
     952                 :          0 :         r = cg_get_path(controller, path, NULL, &fs);
     953         [ #  # ]:          0 :         if (r < 0)
     954                 :          0 :                 return r;
     955                 :            : 
     956                 :          0 :         r = chmod_and_chown(fs, 0755, uid, gid);
     957         [ #  # ]:          0 :         if (r < 0)
     958                 :          0 :                 return r;
     959                 :            : 
     960                 :            :         /* Configure access to the cgroup's attributes */
     961         [ #  # ]:          0 :         for (i = attributes[unified]; i->name; i++) {
     962                 :          0 :                 fs = mfree(fs);
     963                 :            : 
     964                 :          0 :                 r = cg_get_path(controller, path, i->name, &fs);
     965         [ #  # ]:          0 :                 if (r < 0)
     966                 :          0 :                         return r;
     967                 :            : 
     968                 :          0 :                 r = chmod_and_chown(fs, 0644, uid, gid);
     969         [ #  # ]:          0 :                 if (r < 0) {
     970         [ #  # ]:          0 :                         if (i->fatal)
     971                 :          0 :                                 return r;
     972                 :            : 
     973         [ #  # ]:          0 :                         log_debug_errno(r, "Failed to set access on cgroup %s, ignoring: %m", fs);
     974                 :            :                 }
     975                 :            :         }
     976                 :            : 
     977         [ #  # ]:          0 :         if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
     978                 :          0 :                 r = cg_hybrid_unified();
     979         [ #  # ]:          0 :                 if (r < 0)
     980                 :          0 :                         return r;
     981         [ #  # ]:          0 :                 if (r > 0) {
     982                 :            :                         /* Always propagate access mode from unified to legacy controller */
     983                 :          0 :                         r = cg_set_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, uid, gid);
     984         [ #  # ]:          0 :                         if (r < 0)
     985         [ #  # ]:          0 :                                 log_debug_errno(r, "Failed to set access on compatibility systemd cgroup %s, ignoring: %m", path);
     986                 :            :                 }
     987                 :            :         }
     988                 :            : 
     989                 :          0 :         return 0;
     990                 :            : }
     991                 :            : 
     992                 :          0 : int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags) {
     993                 :          0 :         _cleanup_free_ char *fs = NULL;
     994                 :            :         int r;
     995                 :            : 
     996         [ #  # ]:          0 :         assert(path);
     997         [ #  # ]:          0 :         assert(name);
     998   [ #  #  #  # ]:          0 :         assert(value || size <= 0);
     999                 :            : 
    1000                 :          0 :         r = cg_get_path(controller, path, NULL, &fs);
    1001         [ #  # ]:          0 :         if (r < 0)
    1002                 :          0 :                 return r;
    1003                 :            : 
    1004         [ #  # ]:          0 :         if (setxattr(fs, name, value, size, flags) < 0)
    1005                 :          0 :                 return -errno;
    1006                 :            : 
    1007                 :          0 :         return 0;
    1008                 :            : }
    1009                 :            : 
    1010                 :          0 : int cg_get_xattr(const char *controller, const char *path, const char *name, void *value, size_t size) {
    1011                 :          0 :         _cleanup_free_ char *fs = NULL;
    1012                 :            :         ssize_t n;
    1013                 :            :         int r;
    1014                 :            : 
    1015         [ #  # ]:          0 :         assert(path);
    1016         [ #  # ]:          0 :         assert(name);
    1017                 :            : 
    1018                 :          0 :         r = cg_get_path(controller, path, NULL, &fs);
    1019         [ #  # ]:          0 :         if (r < 0)
    1020                 :          0 :                 return r;
    1021                 :            : 
    1022                 :          0 :         n = getxattr(fs, name, value, size);
    1023         [ #  # ]:          0 :         if (n < 0)
    1024                 :          0 :                 return -errno;
    1025                 :            : 
    1026                 :          0 :         return (int) n;
    1027                 :            : }
    1028                 :            : 
    1029                 :      11028 : int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
    1030                 :      11028 :         _cleanup_fclose_ FILE *f = NULL;
    1031                 :            :         const char *fs, *controller_str;
    1032                 :            :         int unified, r;
    1033                 :      11028 :         size_t cs = 0;
    1034                 :            : 
    1035         [ -  + ]:      11028 :         assert(path);
    1036         [ -  + ]:      11028 :         assert(pid >= 0);
    1037                 :            : 
    1038         [ +  + ]:      11028 :         if (controller) {
    1039         [ -  + ]:      10968 :                 if (!cg_controller_is_valid(controller))
    1040                 :          0 :                         return -EINVAL;
    1041                 :            :         } else
    1042                 :         60 :                 controller = SYSTEMD_CGROUP_CONTROLLER;
    1043                 :            : 
    1044                 :      11028 :         unified = cg_unified_controller(controller);
    1045         [ -  + ]:      11028 :         if (unified < 0)
    1046                 :          0 :                 return unified;
    1047         [ -  + ]:      11028 :         if (unified == 0) {
    1048         [ #  # ]:          0 :                 if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
    1049                 :          0 :                         controller_str = SYSTEMD_CGROUP_CONTROLLER_LEGACY;
    1050                 :            :                 else
    1051                 :          0 :                         controller_str = controller;
    1052                 :            : 
    1053                 :          0 :                 cs = strlen(controller_str);
    1054                 :            :         }
    1055                 :            : 
    1056   [ +  +  -  +  :      11028 :         fs = procfs_file_alloca(pid, "cgroup");
                   -  + ]
    1057                 :      11028 :         r = fopen_unlocked(fs, "re", &f);
    1058         [ -  + ]:      11028 :         if (r == -ENOENT)
    1059                 :          0 :                 return -ESRCH;
    1060         [ -  + ]:      11028 :         if (r < 0)
    1061                 :          0 :                 return r;
    1062                 :            : 
    1063                 :     121308 :         for (;;) {
    1064      [ +  -  + ]:     132336 :                 _cleanup_free_ char *line = NULL;
    1065                 :            :                 char *e, *p;
    1066                 :            : 
    1067                 :     132336 :                 r = read_line(f, LONG_LINE_MAX, &line);
    1068         [ -  + ]:     132336 :                 if (r < 0)
    1069                 :          0 :                         return r;
    1070         [ -  + ]:     132336 :                 if (r == 0)
    1071                 :          0 :                         break;
    1072                 :            : 
    1073         [ +  - ]:     132336 :                 if (unified) {
    1074                 :     132336 :                         e = startswith(line, "0:");
    1075         [ +  + ]:     132336 :                         if (!e)
    1076                 :     121308 :                                 continue;
    1077                 :            : 
    1078                 :      11028 :                         e = strchr(e, ':');
    1079         [ -  + ]:      11028 :                         if (!e)
    1080                 :          0 :                                 continue;
    1081                 :            :                 } else {
    1082                 :            :                         char *l;
    1083                 :            :                         size_t k;
    1084                 :            :                         const char *word, *state;
    1085                 :          0 :                         bool found = false;
    1086                 :            : 
    1087                 :          0 :                         l = strchr(line, ':');
    1088         [ #  # ]:          0 :                         if (!l)
    1089                 :          0 :                                 continue;
    1090                 :            : 
    1091                 :          0 :                         l++;
    1092                 :          0 :                         e = strchr(l, ':');
    1093         [ #  # ]:          0 :                         if (!e)
    1094                 :          0 :                                 continue;
    1095                 :            : 
    1096                 :          0 :                         *e = 0;
    1097         [ #  # ]:          0 :                         FOREACH_WORD_SEPARATOR(word, k, l, ",", state)
    1098   [ #  #  #  # ]:          0 :                                 if (k == cs && memcmp(word, controller_str, cs) == 0) {
    1099                 :          0 :                                         found = true;
    1100                 :          0 :                                         break;
    1101                 :            :                                 }
    1102         [ #  # ]:          0 :                         if (!found)
    1103                 :          0 :                                 continue;
    1104                 :            :                 }
    1105                 :            : 
    1106                 :      11028 :                 p = strdup(e + 1);
    1107         [ -  + ]:      11028 :                 if (!p)
    1108                 :          0 :                         return -ENOMEM;
    1109                 :            : 
    1110                 :            :                 /* Truncate suffix indicating the process is a zombie */
    1111                 :      11028 :                 e = endswith(p, " (deleted)");
    1112         [ -  + ]:      11028 :                 if (e)
    1113                 :          0 :                         *e = 0;
    1114                 :            : 
    1115                 :      11028 :                 *path = p;
    1116                 :      11028 :                 return 0;
    1117                 :            :         }
    1118                 :            : 
    1119                 :          0 :         return -ENODATA;
    1120                 :            : }
    1121                 :            : 
    1122                 :          0 : int cg_install_release_agent(const char *controller, const char *agent) {
    1123                 :          0 :         _cleanup_free_ char *fs = NULL, *contents = NULL;
    1124                 :            :         const char *sc;
    1125                 :            :         int r;
    1126                 :            : 
    1127         [ #  # ]:          0 :         assert(agent);
    1128                 :            : 
    1129                 :          0 :         r = cg_unified_controller(controller);
    1130         [ #  # ]:          0 :         if (r < 0)
    1131                 :          0 :                 return r;
    1132         [ #  # ]:          0 :         if (r > 0) /* doesn't apply to unified hierarchy */
    1133                 :          0 :                 return -EOPNOTSUPP;
    1134                 :            : 
    1135                 :          0 :         r = cg_get_path(controller, NULL, "release_agent", &fs);
    1136         [ #  # ]:          0 :         if (r < 0)
    1137                 :          0 :                 return r;
    1138                 :            : 
    1139                 :          0 :         r = read_one_line_file(fs, &contents);
    1140         [ #  # ]:          0 :         if (r < 0)
    1141                 :          0 :                 return r;
    1142                 :            : 
    1143                 :          0 :         sc = strstrip(contents);
    1144         [ #  # ]:          0 :         if (isempty(sc)) {
    1145                 :          0 :                 r = write_string_file(fs, agent, WRITE_STRING_FILE_DISABLE_BUFFER);
    1146         [ #  # ]:          0 :                 if (r < 0)
    1147                 :          0 :                         return r;
    1148         [ #  # ]:          0 :         } else if (!path_equal(sc, agent))
    1149                 :          0 :                 return -EEXIST;
    1150                 :            : 
    1151                 :          0 :         fs = mfree(fs);
    1152                 :          0 :         r = cg_get_path(controller, NULL, "notify_on_release", &fs);
    1153         [ #  # ]:          0 :         if (r < 0)
    1154                 :          0 :                 return r;
    1155                 :            : 
    1156                 :          0 :         contents = mfree(contents);
    1157                 :          0 :         r = read_one_line_file(fs, &contents);
    1158         [ #  # ]:          0 :         if (r < 0)
    1159                 :          0 :                 return r;
    1160                 :            : 
    1161                 :          0 :         sc = strstrip(contents);
    1162         [ #  # ]:          0 :         if (streq(sc, "0")) {
    1163                 :          0 :                 r = write_string_file(fs, "1", WRITE_STRING_FILE_DISABLE_BUFFER);
    1164         [ #  # ]:          0 :                 if (r < 0)
    1165                 :          0 :                         return r;
    1166                 :            : 
    1167                 :          0 :                 return 1;
    1168                 :            :         }
    1169                 :            : 
    1170         [ #  # ]:          0 :         if (!streq(sc, "1"))
    1171                 :          0 :                 return -EIO;
    1172                 :            : 
    1173                 :          0 :         return 0;
    1174                 :            : }
    1175                 :            : 
    1176                 :          0 : int cg_uninstall_release_agent(const char *controller) {
    1177                 :          0 :         _cleanup_free_ char *fs = NULL;
    1178                 :            :         int r;
    1179                 :            : 
    1180                 :          0 :         r = cg_unified_controller(controller);
    1181         [ #  # ]:          0 :         if (r < 0)
    1182                 :          0 :                 return r;
    1183         [ #  # ]:          0 :         if (r > 0) /* Doesn't apply to unified hierarchy */
    1184                 :          0 :                 return -EOPNOTSUPP;
    1185                 :            : 
    1186                 :          0 :         r = cg_get_path(controller, NULL, "notify_on_release", &fs);
    1187         [ #  # ]:          0 :         if (r < 0)
    1188                 :          0 :                 return r;
    1189                 :            : 
    1190                 :          0 :         r = write_string_file(fs, "0", WRITE_STRING_FILE_DISABLE_BUFFER);
    1191         [ #  # ]:          0 :         if (r < 0)
    1192                 :          0 :                 return r;
    1193                 :            : 
    1194                 :          0 :         fs = mfree(fs);
    1195                 :            : 
    1196                 :          0 :         r = cg_get_path(controller, NULL, "release_agent", &fs);
    1197         [ #  # ]:          0 :         if (r < 0)
    1198                 :          0 :                 return r;
    1199                 :            : 
    1200                 :          0 :         r = write_string_file(fs, "", WRITE_STRING_FILE_DISABLE_BUFFER);
    1201         [ #  # ]:          0 :         if (r < 0)
    1202                 :          0 :                 return r;
    1203                 :            : 
    1204                 :          0 :         return 0;
    1205                 :            : }
    1206                 :            : 
    1207                 :          0 : int cg_is_empty(const char *controller, const char *path) {
    1208                 :          0 :         _cleanup_fclose_ FILE *f = NULL;
    1209                 :            :         pid_t pid;
    1210                 :            :         int r;
    1211                 :            : 
    1212         [ #  # ]:          0 :         assert(path);
    1213                 :            : 
    1214                 :          0 :         r = cg_enumerate_processes(controller, path, &f);
    1215         [ #  # ]:          0 :         if (r == -ENOENT)
    1216                 :          0 :                 return true;
    1217         [ #  # ]:          0 :         if (r < 0)
    1218                 :          0 :                 return r;
    1219                 :            : 
    1220                 :          0 :         r = cg_read_pid(f, &pid);
    1221         [ #  # ]:          0 :         if (r < 0)
    1222                 :          0 :                 return r;
    1223                 :            : 
    1224                 :          0 :         return r == 0;
    1225                 :            : }
    1226                 :            : 
    1227                 :          0 : int cg_is_empty_recursive(const char *controller, const char *path) {
    1228                 :            :         int r;
    1229                 :            : 
    1230         [ #  # ]:          0 :         assert(path);
    1231                 :            : 
    1232                 :            :         /* The root cgroup is always populated */
    1233   [ #  #  #  # ]:          0 :         if (controller && empty_or_root(path))
    1234                 :          0 :                 return false;
    1235                 :            : 
    1236                 :          0 :         r = cg_unified_controller(controller);
    1237         [ #  # ]:          0 :         if (r < 0)
    1238                 :          0 :                 return r;
    1239         [ #  # ]:          0 :         if (r > 0) {
    1240                 :          0 :                 _cleanup_free_ char *t = NULL;
    1241                 :            : 
    1242                 :            :                 /* On the unified hierarchy we can check empty state
    1243                 :            :                  * via the "populated" attribute of "cgroup.events". */
    1244                 :            : 
    1245                 :          0 :                 r = cg_read_event(controller, path, "populated", &t);
    1246         [ #  # ]:          0 :                 if (r == -ENOENT)
    1247                 :          0 :                         return true;
    1248         [ #  # ]:          0 :                 if (r < 0)
    1249                 :          0 :                         return r;
    1250                 :            : 
    1251                 :          0 :                 return streq(t, "0");
    1252                 :            :         } else {
    1253                 :          0 :                 _cleanup_closedir_ DIR *d = NULL;
    1254                 :            :                 char *fn;
    1255                 :            : 
    1256                 :          0 :                 r = cg_is_empty(controller, path);
    1257         [ #  # ]:          0 :                 if (r <= 0)
    1258                 :          0 :                         return r;
    1259                 :            : 
    1260                 :          0 :                 r = cg_enumerate_subgroups(controller, path, &d);
    1261         [ #  # ]:          0 :                 if (r == -ENOENT)
    1262                 :          0 :                         return true;
    1263         [ #  # ]:          0 :                 if (r < 0)
    1264                 :          0 :                         return r;
    1265                 :            : 
    1266         [ #  # ]:          0 :                 while ((r = cg_read_subgroup(d, &fn)) > 0) {
    1267         [ #  # ]:          0 :                         _cleanup_free_ char *p = NULL;
    1268                 :            : 
    1269                 :          0 :                         p = path_join(path, fn);
    1270                 :          0 :                         free(fn);
    1271         [ #  # ]:          0 :                         if (!p)
    1272                 :          0 :                                 return -ENOMEM;
    1273                 :            : 
    1274                 :          0 :                         r = cg_is_empty_recursive(controller, p);
    1275         [ #  # ]:          0 :                         if (r <= 0)
    1276                 :          0 :                                 return r;
    1277                 :            :                 }
    1278         [ #  # ]:          0 :                 if (r < 0)
    1279                 :          0 :                         return r;
    1280                 :            : 
    1281                 :          0 :                 return true;
    1282                 :            :         }
    1283                 :            : }
    1284                 :            : 
    1285                 :          0 : int cg_split_spec(const char *spec, char **controller, char **path) {
    1286                 :          0 :         char *t = NULL, *u = NULL;
    1287                 :            :         const char *e;
    1288                 :            : 
    1289         [ #  # ]:          0 :         assert(spec);
    1290                 :            : 
    1291         [ #  # ]:          0 :         if (*spec == '/') {
    1292         [ #  # ]:          0 :                 if (!path_is_normalized(spec))
    1293                 :          0 :                         return -EINVAL;
    1294                 :            : 
    1295         [ #  # ]:          0 :                 if (path) {
    1296                 :          0 :                         t = strdup(spec);
    1297         [ #  # ]:          0 :                         if (!t)
    1298                 :          0 :                                 return -ENOMEM;
    1299                 :            : 
    1300                 :          0 :                         *path = path_simplify(t, false);
    1301                 :            :                 }
    1302                 :            : 
    1303         [ #  # ]:          0 :                 if (controller)
    1304                 :          0 :                         *controller = NULL;
    1305                 :            : 
    1306                 :          0 :                 return 0;
    1307                 :            :         }
    1308                 :            : 
    1309                 :          0 :         e = strchr(spec, ':');
    1310         [ #  # ]:          0 :         if (!e) {
    1311         [ #  # ]:          0 :                 if (!cg_controller_is_valid(spec))
    1312                 :          0 :                         return -EINVAL;
    1313                 :            : 
    1314         [ #  # ]:          0 :                 if (controller) {
    1315                 :          0 :                         t = strdup(spec);
    1316         [ #  # ]:          0 :                         if (!t)
    1317                 :          0 :                                 return -ENOMEM;
    1318                 :            : 
    1319                 :          0 :                         *controller = t;
    1320                 :            :                 }
    1321                 :            : 
    1322         [ #  # ]:          0 :                 if (path)
    1323                 :          0 :                         *path = NULL;
    1324                 :            : 
    1325                 :          0 :                 return 0;
    1326                 :            :         }
    1327                 :            : 
    1328                 :          0 :         t = strndup(spec, e-spec);
    1329         [ #  # ]:          0 :         if (!t)
    1330                 :          0 :                 return -ENOMEM;
    1331         [ #  # ]:          0 :         if (!cg_controller_is_valid(t)) {
    1332                 :          0 :                 free(t);
    1333                 :          0 :                 return -EINVAL;
    1334                 :            :         }
    1335                 :            : 
    1336         [ #  # ]:          0 :         if (isempty(e+1))
    1337                 :          0 :                 u = NULL;
    1338                 :            :         else {
    1339                 :          0 :                 u = strdup(e+1);
    1340         [ #  # ]:          0 :                 if (!u) {
    1341                 :          0 :                         free(t);
    1342                 :          0 :                         return -ENOMEM;
    1343                 :            :                 }
    1344                 :            : 
    1345         [ #  # ]:          0 :                 if (!path_is_normalized(u) ||
    1346         [ #  # ]:          0 :                     !path_is_absolute(u)) {
    1347                 :          0 :                         free(t);
    1348                 :          0 :                         free(u);
    1349                 :          0 :                         return -EINVAL;
    1350                 :            :                 }
    1351                 :            : 
    1352                 :          0 :                 path_simplify(u, false);
    1353                 :            :         }
    1354                 :            : 
    1355         [ #  # ]:          0 :         if (controller)
    1356                 :          0 :                 *controller = t;
    1357                 :            :         else
    1358                 :          0 :                 free(t);
    1359                 :            : 
    1360         [ #  # ]:          0 :         if (path)
    1361                 :          0 :                 *path = u;
    1362                 :            :         else
    1363                 :          0 :                 free(u);
    1364                 :            : 
    1365                 :          0 :         return 0;
    1366                 :            : }
    1367                 :            : 
    1368                 :          0 : int cg_mangle_path(const char *path, char **result) {
    1369                 :          0 :         _cleanup_free_ char *c = NULL, *p = NULL;
    1370                 :            :         char *t;
    1371                 :            :         int r;
    1372                 :            : 
    1373         [ #  # ]:          0 :         assert(path);
    1374         [ #  # ]:          0 :         assert(result);
    1375                 :            : 
    1376                 :            :         /* First, check if it already is a filesystem path */
    1377         [ #  # ]:          0 :         if (path_startswith(path, "/sys/fs/cgroup")) {
    1378                 :            : 
    1379                 :          0 :                 t = strdup(path);
    1380         [ #  # ]:          0 :                 if (!t)
    1381                 :          0 :                         return -ENOMEM;
    1382                 :            : 
    1383                 :          0 :                 *result = path_simplify(t, false);
    1384                 :          0 :                 return 0;
    1385                 :            :         }
    1386                 :            : 
    1387                 :            :         /* Otherwise, treat it as cg spec */
    1388                 :          0 :         r = cg_split_spec(path, &c, &p);
    1389         [ #  # ]:          0 :         if (r < 0)
    1390                 :          0 :                 return r;
    1391                 :            : 
    1392   [ #  #  #  # ]:          0 :         return cg_get_path(c ?: SYSTEMD_CGROUP_CONTROLLER, p ?: "/", NULL, result);
    1393                 :            : }
    1394                 :            : 
    1395                 :       5216 : int cg_get_root_path(char **path) {
    1396                 :            :         char *p, *e;
    1397                 :            :         int r;
    1398                 :            : 
    1399         [ -  + ]:       5216 :         assert(path);
    1400                 :            : 
    1401                 :       5216 :         r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p);
    1402         [ -  + ]:       5216 :         if (r < 0)
    1403                 :          0 :                 return r;
    1404                 :            : 
    1405                 :       5216 :         e = endswith(p, "/" SPECIAL_INIT_SCOPE);
    1406         [ -  + ]:       5216 :         if (!e)
    1407                 :          0 :                 e = endswith(p, "/" SPECIAL_SYSTEM_SLICE); /* legacy */
    1408         [ -  + ]:       5216 :         if (!e)
    1409                 :          0 :                 e = endswith(p, "/system"); /* even more legacy */
    1410         [ +  - ]:       5216 :         if (e)
    1411                 :       5216 :                 *e = 0;
    1412                 :            : 
    1413                 :       5216 :         *path = p;
    1414                 :       5216 :         return 0;
    1415                 :            : }
    1416                 :            : 
    1417                 :       5052 : int cg_shift_path(const char *cgroup, const char *root, const char **shifted) {
    1418                 :       5052 :         _cleanup_free_ char *rt = NULL;
    1419                 :            :         char *p;
    1420                 :            :         int r;
    1421                 :            : 
    1422         [ -  + ]:       5052 :         assert(cgroup);
    1423         [ -  + ]:       5052 :         assert(shifted);
    1424                 :            : 
    1425         [ +  + ]:       5052 :         if (!root) {
    1426                 :            :                 /* If the root was specified let's use that, otherwise
    1427                 :            :                  * let's determine it from PID 1 */
    1428                 :            : 
    1429                 :       4988 :                 r = cg_get_root_path(&rt);
    1430         [ -  + ]:       4988 :                 if (r < 0)
    1431                 :          0 :                         return r;
    1432                 :            : 
    1433                 :       4988 :                 root = rt;
    1434                 :            :         }
    1435                 :            : 
    1436                 :       5052 :         p = path_startswith(cgroup, root);
    1437   [ +  +  +  - ]:       5052 :         if (p && p > cgroup)
    1438                 :          8 :                 *shifted = p - 1;
    1439                 :            :         else
    1440                 :       5044 :                 *shifted = cgroup;
    1441                 :            : 
    1442                 :       5052 :         return 0;
    1443                 :            : }
    1444                 :            : 
    1445                 :       4988 : int cg_pid_get_path_shifted(pid_t pid, const char *root, char **cgroup) {
    1446                 :       4988 :         _cleanup_free_ char *raw = NULL;
    1447                 :            :         const char *c;
    1448                 :            :         int r;
    1449                 :            : 
    1450         [ -  + ]:       4988 :         assert(pid >= 0);
    1451         [ -  + ]:       4988 :         assert(cgroup);
    1452                 :            : 
    1453                 :       4988 :         r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &raw);
    1454         [ -  + ]:       4988 :         if (r < 0)
    1455                 :          0 :                 return r;
    1456                 :            : 
    1457                 :       4988 :         r = cg_shift_path(raw, root, &c);
    1458         [ -  + ]:       4988 :         if (r < 0)
    1459                 :          0 :                 return r;
    1460                 :            : 
    1461         [ +  - ]:       4988 :         if (c == raw)
    1462                 :       4988 :                 *cgroup = TAKE_PTR(raw);
    1463                 :            :         else {
    1464                 :            :                 char *n;
    1465                 :            : 
    1466                 :          0 :                 n = strdup(c);
    1467         [ #  # ]:          0 :                 if (!n)
    1468                 :          0 :                         return -ENOMEM;
    1469                 :            : 
    1470                 :          0 :                 *cgroup = n;
    1471                 :            :         }
    1472                 :            : 
    1473                 :       4988 :         return 0;
    1474                 :            : }
    1475                 :            : 
    1476                 :       4236 : int cg_path_decode_unit(const char *cgroup, char **unit) {
    1477                 :            :         char *c, *s;
    1478                 :            :         size_t n;
    1479                 :            : 
    1480         [ -  + ]:       4236 :         assert(cgroup);
    1481         [ -  + ]:       4236 :         assert(unit);
    1482                 :            : 
    1483                 :       4236 :         n = strcspn(cgroup, "/");
    1484         [ +  + ]:       4236 :         if (n < 3)
    1485                 :        332 :                 return -ENXIO;
    1486                 :            : 
    1487                 :       3904 :         c = strndupa(cgroup, n);
    1488                 :       3904 :         c = cg_unescape(c);
    1489                 :            : 
    1490         [ +  + ]:       3904 :         if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
    1491                 :         32 :                 return -ENXIO;
    1492                 :            : 
    1493                 :       3872 :         s = strdup(c);
    1494         [ -  + ]:       3872 :         if (!s)
    1495                 :          0 :                 return -ENOMEM;
    1496                 :            : 
    1497                 :       3872 :         *unit = s;
    1498                 :       3872 :         return 0;
    1499                 :            : }
    1500                 :            : 
    1501                 :      12576 : static bool valid_slice_name(const char *p, size_t n) {
    1502                 :            : 
    1503         [ -  + ]:      12576 :         if (!p)
    1504                 :          0 :                 return false;
    1505                 :            : 
    1506         [ +  + ]:      12576 :         if (n < STRLEN("x.slice"))
    1507                 :        408 :                 return false;
    1508                 :            : 
    1509         [ +  + ]:      12168 :         if (memcmp(p + n - 6, ".slice", 6) == 0) {
    1510                 :       7492 :                 char buf[n+1], *c;
    1511                 :            : 
    1512                 :       7492 :                 memcpy(buf, p, n);
    1513                 :       7492 :                 buf[n] = 0;
    1514                 :            : 
    1515                 :       7492 :                 c = cg_unescape(buf);
    1516                 :            : 
    1517                 :       7492 :                 return unit_name_is_valid(c, UNIT_NAME_PLAIN);
    1518                 :            :         }
    1519                 :            : 
    1520                 :       4676 :         return false;
    1521                 :            : }
    1522                 :            : 
    1523                 :       3576 : static const char *skip_slices(const char *p) {
    1524         [ -  + ]:       3576 :         assert(p);
    1525                 :            : 
    1526                 :            :         /* Skips over all slice assignments */
    1527                 :            : 
    1528                 :       5040 :         for (;;) {
    1529                 :            :                 size_t n;
    1530                 :            : 
    1531                 :       8616 :                 p += strspn(p, "/");
    1532                 :            : 
    1533                 :       8616 :                 n = strcspn(p, "/");
    1534         [ +  + ]:       8616 :                 if (!valid_slice_name(p, n))
    1535                 :       3576 :                         return p;
    1536                 :            : 
    1537                 :       5040 :                 p += n;
    1538                 :            :         }
    1539                 :            : }
    1540                 :            : 
    1541                 :       2744 : int cg_path_get_unit(const char *path, char **ret) {
    1542                 :            :         const char *e;
    1543                 :            :         char *unit;
    1544                 :            :         int r;
    1545                 :            : 
    1546         [ -  + ]:       2744 :         assert(path);
    1547         [ -  + ]:       2744 :         assert(ret);
    1548                 :            : 
    1549                 :       2744 :         e = skip_slices(path);
    1550                 :            : 
    1551                 :       2744 :         r = cg_path_decode_unit(e, &unit);
    1552         [ +  + ]:       2744 :         if (r < 0)
    1553                 :        348 :                 return r;
    1554                 :            : 
    1555                 :            :         /* We skipped over the slices, don't accept any now */
    1556         [ -  + ]:       2396 :         if (endswith(unit, ".slice")) {
    1557                 :          0 :                 free(unit);
    1558                 :          0 :                 return -ENXIO;
    1559                 :            :         }
    1560                 :            : 
    1561                 :       2396 :         *ret = unit;
    1562                 :       2396 :         return 0;
    1563                 :            : }
    1564                 :            : 
    1565                 :        712 : int cg_pid_get_unit(pid_t pid, char **unit) {
    1566                 :        712 :         _cleanup_free_ char *cgroup = NULL;
    1567                 :            :         int r;
    1568                 :            : 
    1569         [ -  + ]:        712 :         assert(unit);
    1570                 :            : 
    1571                 :        712 :         r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
    1572         [ -  + ]:        712 :         if (r < 0)
    1573                 :          0 :                 return r;
    1574                 :            : 
    1575                 :        712 :         return cg_path_get_unit(cgroup, unit);
    1576                 :            : }
    1577                 :            : 
    1578                 :            : /**
    1579                 :            :  * Skip session-*.scope, but require it to be there.
    1580                 :            :  */
    1581                 :        648 : static const char *skip_session(const char *p) {
    1582                 :            :         size_t n;
    1583                 :            : 
    1584         [ +  + ]:        648 :         if (isempty(p))
    1585                 :         16 :                 return NULL;
    1586                 :            : 
    1587                 :        632 :         p += strspn(p, "/");
    1588                 :            : 
    1589                 :        632 :         n = strcspn(p, "/");
    1590         [ +  + ]:        632 :         if (n < STRLEN("session-x.scope"))
    1591                 :        140 :                 return NULL;
    1592                 :            : 
    1593   [ +  +  +  - ]:        492 :         if (memcmp(p, "session-", 8) == 0 && memcmp(p + n - 6, ".scope", 6) == 0) {
    1594                 :        368 :                 char buf[n - 8 - 6 + 1];
    1595                 :            : 
    1596                 :        368 :                 memcpy(buf, p + 8, n - 8 - 6);
    1597                 :        368 :                 buf[n - 8 - 6] = 0;
    1598                 :            : 
    1599                 :            :                 /* Note that session scopes never need unescaping,
    1600                 :            :                  * since they cannot conflict with the kernel's own
    1601                 :            :                  * names, hence we don't need to call cg_unescape()
    1602                 :            :                  * here. */
    1603                 :            : 
    1604         [ -  + ]:        368 :                 if (!session_id_valid(buf))
    1605                 :          0 :                         return false;
    1606                 :            : 
    1607                 :        368 :                 p += n;
    1608                 :        368 :                 p += strspn(p, "/");
    1609                 :        368 :                 return p;
    1610                 :            :         }
    1611                 :            : 
    1612                 :        124 :         return NULL;
    1613                 :            : }
    1614                 :            : 
    1615                 :            : /**
    1616                 :            :  * Skip user@*.service, but require it to be there.
    1617                 :            :  */
    1618                 :        832 : static const char *skip_user_manager(const char *p) {
    1619                 :            :         size_t n;
    1620                 :            : 
    1621         [ +  + ]:        832 :         if (isempty(p))
    1622                 :         16 :                 return NULL;
    1623                 :            : 
    1624                 :        816 :         p += strspn(p, "/");
    1625                 :            : 
    1626                 :        816 :         n = strcspn(p, "/");
    1627         [ +  + ]:        816 :         if (n < STRLEN("user@x.service"))
    1628                 :        116 :                 return NULL;
    1629                 :            : 
    1630   [ +  +  +  - ]:        700 :         if (memcmp(p, "user@", 5) == 0 && memcmp(p + n - 8, ".service", 8) == 0) {
    1631                 :        184 :                 char buf[n - 5 - 8 + 1];
    1632                 :            : 
    1633                 :        184 :                 memcpy(buf, p + 5, n - 5 - 8);
    1634                 :        184 :                 buf[n - 5 - 8] = 0;
    1635                 :            : 
    1636                 :            :                 /* Note that user manager services never need unescaping,
    1637                 :            :                  * since they cannot conflict with the kernel's own
    1638                 :            :                  * names, hence we don't need to call cg_unescape()
    1639                 :            :                  * here. */
    1640                 :            : 
    1641         [ -  + ]:        184 :                 if (parse_uid(buf, NULL) < 0)
    1642                 :          0 :                         return NULL;
    1643                 :            : 
    1644                 :        184 :                 p += n;
    1645                 :        184 :                 p += strspn(p, "/");
    1646                 :            : 
    1647                 :        184 :                 return p;
    1648                 :            :         }
    1649                 :            : 
    1650                 :        516 :         return NULL;
    1651                 :            : }
    1652                 :            : 
    1653                 :        832 : static const char *skip_user_prefix(const char *path) {
    1654                 :            :         const char *e, *t;
    1655                 :            : 
    1656         [ -  + ]:        832 :         assert(path);
    1657                 :            : 
    1658                 :            :         /* Skip slices, if there are any */
    1659                 :        832 :         e = skip_slices(path);
    1660                 :            : 
    1661                 :            :         /* Skip the user manager, if it's in the path now... */
    1662                 :        832 :         t = skip_user_manager(e);
    1663         [ +  + ]:        832 :         if (t)
    1664                 :        184 :                 return t;
    1665                 :            : 
    1666                 :            :         /* Alternatively skip the user session if it is in the path... */
    1667                 :        648 :         return skip_session(e);
    1668                 :            : }
    1669                 :            : 
    1670                 :        772 : int cg_path_get_user_unit(const char *path, char **ret) {
    1671                 :            :         const char *t;
    1672                 :            : 
    1673         [ -  + ]:        772 :         assert(path);
    1674         [ -  + ]:        772 :         assert(ret);
    1675                 :            : 
    1676                 :        772 :         t = skip_user_prefix(path);
    1677         [ +  + ]:        772 :         if (!t)
    1678                 :        248 :                 return -ENXIO;
    1679                 :            : 
    1680                 :            :         /* And from here on it looks pretty much the same as for a system unit, hence let's use the same
    1681                 :            :          * parser. */
    1682                 :        524 :         return cg_path_get_unit(t, ret);
    1683                 :            : }
    1684                 :            : 
    1685                 :        712 : int cg_pid_get_user_unit(pid_t pid, char **unit) {
    1686                 :        712 :         _cleanup_free_ char *cgroup = NULL;
    1687                 :            :         int r;
    1688                 :            : 
    1689         [ -  + ]:        712 :         assert(unit);
    1690                 :            : 
    1691                 :        712 :         r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
    1692         [ -  + ]:        712 :         if (r < 0)
    1693                 :          0 :                 return r;
    1694                 :            : 
    1695                 :        712 :         return cg_path_get_user_unit(cgroup, unit);
    1696                 :            : }
    1697                 :            : 
    1698                 :        708 : int cg_path_get_machine_name(const char *path, char **machine) {
    1699                 :        708 :         _cleanup_free_ char *u = NULL;
    1700                 :            :         const char *sl;
    1701                 :            :         int r;
    1702                 :            : 
    1703                 :        708 :         r = cg_path_get_unit(path, &u);
    1704         [ -  + ]:        708 :         if (r < 0)
    1705                 :          0 :                 return r;
    1706                 :            : 
    1707   [ +  +  +  -  :       3540 :         sl = strjoina("/run/systemd/machines/unit:", u);
          -  +  -  +  +  
                +  +  - ]
    1708                 :        708 :         return readlink_malloc(sl, machine);
    1709                 :            : }
    1710                 :            : 
    1711                 :        708 : int cg_pid_get_machine_name(pid_t pid, char **machine) {
    1712                 :        708 :         _cleanup_free_ char *cgroup = NULL;
    1713                 :            :         int r;
    1714                 :            : 
    1715         [ -  + ]:        708 :         assert(machine);
    1716                 :            : 
    1717                 :        708 :         r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
    1718         [ -  + ]:        708 :         if (r < 0)
    1719                 :          0 :                 return r;
    1720                 :            : 
    1721                 :        708 :         return cg_path_get_machine_name(cgroup, machine);
    1722                 :            : }
    1723                 :            : 
    1724                 :        748 : int cg_path_get_session(const char *path, char **session) {
    1725                 :        748 :         _cleanup_free_ char *unit = NULL;
    1726                 :            :         char *start, *end;
    1727                 :            :         int r;
    1728                 :            : 
    1729         [ -  + ]:        748 :         assert(path);
    1730                 :            : 
    1731                 :        748 :         r = cg_path_get_unit(path, &unit);
    1732         [ +  + ]:        748 :         if (r < 0)
    1733                 :          4 :                 return r;
    1734                 :            : 
    1735                 :        744 :         start = startswith(unit, "session-");
    1736         [ +  + ]:        744 :         if (!start)
    1737                 :        392 :                 return -ENXIO;
    1738                 :        352 :         end = endswith(start, ".scope");
    1739         [ -  + ]:        352 :         if (!end)
    1740                 :          0 :                 return -ENXIO;
    1741                 :            : 
    1742                 :        352 :         *end = 0;
    1743         [ +  + ]:        352 :         if (!session_id_valid(start))
    1744                 :          4 :                 return -ENXIO;
    1745                 :            : 
    1746         [ +  - ]:        348 :         if (session) {
    1747                 :            :                 char *rr;
    1748                 :            : 
    1749                 :        348 :                 rr = strdup(start);
    1750         [ -  + ]:        348 :                 if (!rr)
    1751                 :          0 :                         return -ENOMEM;
    1752                 :            : 
    1753                 :        348 :                 *session = rr;
    1754                 :            :         }
    1755                 :            : 
    1756                 :        348 :         return 0;
    1757                 :            : }
    1758                 :            : 
    1759                 :        724 : int cg_pid_get_session(pid_t pid, char **session) {
    1760                 :        724 :         _cleanup_free_ char *cgroup = NULL;
    1761                 :            :         int r;
    1762                 :            : 
    1763                 :        724 :         r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
    1764         [ -  + ]:        724 :         if (r < 0)
    1765                 :          0 :                 return r;
    1766                 :            : 
    1767                 :        724 :         return cg_path_get_session(cgroup, session);
    1768                 :            : }
    1769                 :            : 
    1770                 :        732 : int cg_path_get_owner_uid(const char *path, uid_t *uid) {
    1771                 :        732 :         _cleanup_free_ char *slice = NULL;
    1772                 :            :         char *start, *end;
    1773                 :            :         int r;
    1774                 :            : 
    1775         [ -  + ]:        732 :         assert(path);
    1776                 :            : 
    1777                 :        732 :         r = cg_path_get_slice(path, &slice);
    1778         [ -  + ]:        732 :         if (r < 0)
    1779                 :          0 :                 return r;
    1780                 :            : 
    1781                 :        732 :         start = startswith(slice, "user-");
    1782         [ +  + ]:        732 :         if (!start)
    1783                 :        244 :                 return -ENXIO;
    1784                 :        488 :         end = endswith(start, ".slice");
    1785         [ -  + ]:        488 :         if (!end)
    1786                 :          0 :                 return -ENXIO;
    1787                 :            : 
    1788                 :        488 :         *end = 0;
    1789         [ -  + ]:        488 :         if (parse_uid(start, uid) < 0)
    1790                 :          0 :                 return -ENXIO;
    1791                 :            : 
    1792                 :        488 :         return 0;
    1793                 :            : }
    1794                 :            : 
    1795                 :        712 : int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
    1796                 :        712 :         _cleanup_free_ char *cgroup = NULL;
    1797                 :            :         int r;
    1798                 :            : 
    1799                 :        712 :         r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
    1800         [ -  + ]:        712 :         if (r < 0)
    1801                 :          0 :                 return r;
    1802                 :            : 
    1803                 :        712 :         return cg_path_get_owner_uid(cgroup, uid);
    1804                 :            : }
    1805                 :            : 
    1806                 :       1508 : int cg_path_get_slice(const char *p, char **slice) {
    1807                 :       1508 :         const char *e = NULL;
    1808                 :            : 
    1809         [ -  + ]:       1508 :         assert(p);
    1810         [ -  + ]:       1508 :         assert(slice);
    1811                 :            : 
    1812                 :            :         /* Finds the right-most slice unit from the beginning, but
    1813                 :            :          * stops before we come to the first non-slice unit. */
    1814                 :            : 
    1815                 :       2452 :         for (;;) {
    1816                 :            :                 size_t n;
    1817                 :            : 
    1818                 :       3960 :                 p += strspn(p, "/");
    1819                 :            : 
    1820                 :       3960 :                 n = strcspn(p, "/");
    1821         [ +  + ]:       3960 :                 if (!valid_slice_name(p, n)) {
    1822                 :            : 
    1823         [ +  + ]:       1508 :                         if (!e) {
    1824                 :            :                                 char *s;
    1825                 :            : 
    1826                 :         52 :                                 s = strdup(SPECIAL_ROOT_SLICE);
    1827         [ -  + ]:         52 :                                 if (!s)
    1828                 :          0 :                                         return -ENOMEM;
    1829                 :            : 
    1830                 :         52 :                                 *slice = s;
    1831                 :         52 :                                 return 0;
    1832                 :            :                         }
    1833                 :            : 
    1834                 :       1456 :                         return cg_path_decode_unit(e, slice);
    1835                 :            :                 }
    1836                 :            : 
    1837                 :       2452 :                 e = p;
    1838                 :       2452 :                 p += n;
    1839                 :            :         }
    1840                 :            : }
    1841                 :            : 
    1842                 :        712 : int cg_pid_get_slice(pid_t pid, char **slice) {
    1843                 :        712 :         _cleanup_free_ char *cgroup = NULL;
    1844                 :            :         int r;
    1845                 :            : 
    1846         [ -  + ]:        712 :         assert(slice);
    1847                 :            : 
    1848                 :        712 :         r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
    1849         [ -  + ]:        712 :         if (r < 0)
    1850                 :          0 :                 return r;
    1851                 :            : 
    1852                 :        712 :         return cg_path_get_slice(cgroup, slice);
    1853                 :            : }
    1854                 :            : 
    1855                 :         60 : int cg_path_get_user_slice(const char *p, char **slice) {
    1856                 :            :         const char *t;
    1857         [ -  + ]:         60 :         assert(p);
    1858         [ -  + ]:         60 :         assert(slice);
    1859                 :            : 
    1860                 :         60 :         t = skip_user_prefix(p);
    1861         [ +  + ]:         60 :         if (!t)
    1862                 :         32 :                 return -ENXIO;
    1863                 :            : 
    1864                 :            :         /* And now it looks pretty much the same as for a system
    1865                 :            :          * slice, so let's just use the same parser from here on. */
    1866                 :         28 :         return cg_path_get_slice(t, slice);
    1867                 :            : }
    1868                 :            : 
    1869                 :          0 : int cg_pid_get_user_slice(pid_t pid, char **slice) {
    1870                 :          0 :         _cleanup_free_ char *cgroup = NULL;
    1871                 :            :         int r;
    1872                 :            : 
    1873         [ #  # ]:          0 :         assert(slice);
    1874                 :            : 
    1875                 :          0 :         r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
    1876         [ #  # ]:          0 :         if (r < 0)
    1877                 :          0 :                 return r;
    1878                 :            : 
    1879                 :          0 :         return cg_path_get_user_slice(cgroup, slice);
    1880                 :            : }
    1881                 :            : 
    1882                 :        104 : char *cg_escape(const char *p) {
    1883                 :        104 :         bool need_prefix = false;
    1884                 :            : 
    1885                 :            :         /* This implements very minimal escaping for names to be used
    1886                 :            :          * as file names in the cgroup tree: any name which might
    1887                 :            :          * conflict with a kernel name or is prefixed with '_' is
    1888                 :            :          * prefixed with a '_'. That way, when reading cgroup names it
    1889                 :            :          * is sufficient to remove a single prefixing underscore if
    1890                 :            :          * there is one. */
    1891                 :            : 
    1892                 :            :         /* The return value of this function (unlike cg_unescape())
    1893                 :            :          * needs free()! */
    1894                 :            : 
    1895   [ +  +  +  + ]:        104 :         if (IN_SET(p[0], 0, '_', '.') ||
    1896   [ +  +  +  + ]:        164 :             STR_IN_SET(p, "notify_on_release", "release_agent", "tasks") ||
    1897                 :         80 :             startswith(p, "cgroup."))
    1898                 :         28 :                 need_prefix = true;
    1899                 :            :         else {
    1900                 :            :                 const char *dot;
    1901                 :            : 
    1902                 :         76 :                 dot = strrchr(p, '.');
    1903         [ +  + ]:         76 :                 if (dot) {
    1904                 :            :                         CGroupController c;
    1905                 :         72 :                         size_t l = dot - p;
    1906                 :            : 
    1907         [ +  + ]:        684 :                         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
    1908                 :            :                                 const char *n;
    1909                 :            : 
    1910                 :        616 :                                 n = cgroup_controller_to_string(c);
    1911                 :            : 
    1912         [ +  + ]:        616 :                                 if (l != strlen(n))
    1913                 :        556 :                                         continue;
    1914                 :            : 
    1915         [ +  + ]:         60 :                                 if (memcmp(p, n, l) != 0)
    1916                 :         56 :                                         continue;
    1917                 :            : 
    1918                 :          4 :                                 need_prefix = true;
    1919                 :          4 :                                 break;
    1920                 :            :                         }
    1921                 :            :                 }
    1922                 :            :         }
    1923                 :            : 
    1924         [ +  + ]:        104 :         if (need_prefix)
    1925                 :         32 :                 return strjoin("_", p);
    1926                 :            : 
    1927                 :         72 :         return strdup(p);
    1928                 :            : }
    1929                 :            : 
    1930                 :      11436 : char *cg_unescape(const char *p) {
    1931         [ -  + ]:      11436 :         assert(p);
    1932                 :            : 
    1933                 :            :         /* The return value of this function (unlike cg_escape())
    1934                 :            :          * doesn't need free()! */
    1935                 :            : 
    1936         [ +  + ]:      11436 :         if (p[0] == '_')
    1937                 :         44 :                 return (char*) p+1;
    1938                 :            : 
    1939                 :      11392 :         return (char*) p;
    1940                 :            : }
    1941                 :            : 
    1942                 :            : #define CONTROLLER_VALID                        \
    1943                 :            :         DIGITS LETTERS                          \
    1944                 :            :         "_"
    1945                 :            : 
    1946                 :      12976 : bool cg_controller_is_valid(const char *p) {
    1947                 :            :         const char *t, *s;
    1948                 :            : 
    1949         [ -  + ]:      12976 :         if (!p)
    1950                 :          0 :                 return false;
    1951                 :            : 
    1952         [ +  + ]:      12976 :         if (streq(p, SYSTEMD_CGROUP_CONTROLLER))
    1953                 :      11396 :                 return true;
    1954                 :            : 
    1955                 :       1580 :         s = startswith(p, "name=");
    1956         [ +  + ]:       1580 :         if (s)
    1957                 :          8 :                 p = s;
    1958                 :            : 
    1959   [ +  +  +  + ]:       1580 :         if (IN_SET(*p, 0, '_'))
    1960                 :         16 :                 return false;
    1961                 :            : 
    1962         [ +  + ]:       9816 :         for (t = p; *t; t++)
    1963         [ +  + ]:       8264 :                 if (!strchr(CONTROLLER_VALID, *t))
    1964                 :         12 :                         return false;
    1965                 :            : 
    1966         [ -  + ]:       1552 :         if (t - p > FILENAME_MAX)
    1967                 :          0 :                 return false;
    1968                 :            : 
    1969                 :       1552 :         return true;
    1970                 :            : }
    1971                 :            : 
    1972                 :         84 : int cg_slice_to_path(const char *unit, char **ret) {
    1973                 :         84 :         _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
    1974                 :            :         const char *dash;
    1975                 :            :         int r;
    1976                 :            : 
    1977         [ -  + ]:         84 :         assert(unit);
    1978         [ -  + ]:         84 :         assert(ret);
    1979                 :            : 
    1980         [ +  + ]:         84 :         if (streq(unit, SPECIAL_ROOT_SLICE)) {
    1981                 :            :                 char *x;
    1982                 :            : 
    1983                 :          4 :                 x = strdup("");
    1984         [ -  + ]:          4 :                 if (!x)
    1985                 :          0 :                         return -ENOMEM;
    1986                 :          4 :                 *ret = x;
    1987                 :          4 :                 return 0;
    1988                 :            :         }
    1989                 :            : 
    1990         [ +  + ]:         80 :         if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN))
    1991                 :         40 :                 return -EINVAL;
    1992                 :            : 
    1993         [ +  + ]:         40 :         if (!endswith(unit, ".slice"))
    1994                 :          4 :                 return -EINVAL;
    1995                 :            : 
    1996                 :         36 :         r = unit_name_to_prefix(unit, &p);
    1997         [ -  + ]:         36 :         if (r < 0)
    1998                 :          0 :                 return r;
    1999                 :            : 
    2000                 :         36 :         dash = strchr(p, '-');
    2001                 :            : 
    2002                 :            :         /* Don't allow initial dashes */
    2003         [ +  + ]:         36 :         if (dash == p)
    2004                 :         12 :                 return -EINVAL;
    2005                 :            : 
    2006         [ +  + ]:         48 :         while (dash) {
    2007         [ +  + ]:         32 :                 _cleanup_free_ char *escaped = NULL;
    2008                 :         32 :                 char n[dash - p + sizeof(".slice")];
    2009                 :            : 
    2010                 :            : #if HAS_FEATURE_MEMORY_SANITIZER
    2011                 :            :                 /* msan doesn't instrument stpncpy, so it thinks
    2012                 :            :                  * n is later used uninitialized:
    2013                 :            :                  * https://github.com/google/sanitizers/issues/926
    2014                 :            :                  */
    2015                 :            :                 zero(n);
    2016                 :            : #endif
    2017                 :            : 
    2018                 :            :                 /* Don't allow trailing or double dashes */
    2019   [ +  +  +  + ]:         32 :                 if (IN_SET(dash[1], 0, '-'))
    2020                 :          8 :                         return -EINVAL;
    2021                 :            : 
    2022                 :         24 :                 strcpy(stpncpy(n, p, dash - p), ".slice");
    2023         [ -  + ]:         24 :                 if (!unit_name_is_valid(n, UNIT_NAME_PLAIN))
    2024                 :          0 :                         return -EINVAL;
    2025                 :            : 
    2026                 :         24 :                 escaped = cg_escape(n);
    2027         [ -  + ]:         24 :                 if (!escaped)
    2028                 :          0 :                         return -ENOMEM;
    2029                 :            : 
    2030         [ -  + ]:         24 :                 if (!strextend(&s, escaped, "/", NULL))
    2031                 :          0 :                         return -ENOMEM;
    2032                 :            : 
    2033                 :         24 :                 dash = strchr(dash+1, '-');
    2034                 :            :         }
    2035                 :            : 
    2036                 :         16 :         e = cg_escape(unit);
    2037         [ -  + ]:         16 :         if (!e)
    2038                 :          0 :                 return -ENOMEM;
    2039                 :            : 
    2040         [ -  + ]:         16 :         if (!strextend(&s, e, NULL))
    2041                 :          0 :                 return -ENOMEM;
    2042                 :            : 
    2043                 :         16 :         *ret = TAKE_PTR(s);
    2044                 :            : 
    2045                 :         16 :         return 0;
    2046                 :            : }
    2047                 :            : 
    2048                 :          0 : int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
    2049                 :          0 :         _cleanup_free_ char *p = NULL;
    2050                 :            :         int r;
    2051                 :            : 
    2052                 :          0 :         r = cg_get_path(controller, path, attribute, &p);
    2053         [ #  # ]:          0 :         if (r < 0)
    2054                 :          0 :                 return r;
    2055                 :            : 
    2056                 :          0 :         return write_string_file(p, value, WRITE_STRING_FILE_DISABLE_BUFFER);
    2057                 :            : }
    2058                 :            : 
    2059                 :        216 : int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret) {
    2060                 :        216 :         _cleanup_free_ char *p = NULL;
    2061                 :            :         int r;
    2062                 :            : 
    2063                 :        216 :         r = cg_get_path(controller, path, attribute, &p);
    2064         [ -  + ]:        216 :         if (r < 0)
    2065                 :          0 :                 return r;
    2066                 :            : 
    2067                 :        216 :         return read_one_line_file(p, ret);
    2068                 :            : }
    2069                 :            : 
    2070                 :          4 : int cg_get_keyed_attribute(
    2071                 :            :                 const char *controller,
    2072                 :            :                 const char *path,
    2073                 :            :                 const char *attribute,
    2074                 :            :                 char **keys,
    2075                 :            :                 char **ret_values) {
    2076                 :            : 
    2077                 :          4 :         _cleanup_free_ char *filename = NULL, *contents = NULL;
    2078                 :            :         const char *p;
    2079                 :          4 :         size_t n, i, n_done = 0;
    2080                 :            :         char **v;
    2081                 :            :         int r;
    2082                 :            : 
    2083                 :            :         /* Reads one or more fields of a cgroup v2 keyed attribute file. The 'keys' parameter should be an strv with
    2084                 :            :          * all keys to retrieve. The 'ret_values' parameter should be passed as string size with the same number of
    2085                 :            :          * entries as 'keys'. On success each entry will be set to the value of the matching key.
    2086                 :            :          *
    2087                 :            :          * If the attribute file doesn't exist at all returns ENOENT, if any key is not found returns ENXIO. */
    2088                 :            : 
    2089                 :          4 :         r = cg_get_path(controller, path, attribute, &filename);
    2090         [ -  + ]:          4 :         if (r < 0)
    2091                 :          0 :                 return r;
    2092                 :            : 
    2093                 :          4 :         r = read_full_file(filename, &contents, NULL);
    2094         [ +  - ]:          4 :         if (r < 0)
    2095                 :          4 :                 return r;
    2096                 :            : 
    2097                 :          0 :         n = strv_length(keys);
    2098         [ #  # ]:          0 :         if (n == 0) /* No keys to retrieve? That's easy, we are done then */
    2099                 :          0 :                 return 0;
    2100                 :            : 
    2101                 :            :         /* Let's build this up in a temporary array for now in order not to clobber the return parameter on failure */
    2102   [ #  #  #  #  :          0 :         v = newa0(char*, n);
                   #  # ]
    2103                 :            : 
    2104         [ #  # ]:          0 :         for (p = contents; *p;) {
    2105                 :          0 :                 const char *w = NULL;
    2106                 :            : 
    2107         [ #  # ]:          0 :                 for (i = 0; i < n; i++)
    2108         [ #  # ]:          0 :                         if (!v[i]) {
    2109                 :          0 :                                 w = first_word(p, keys[i]);
    2110         [ #  # ]:          0 :                                 if (w)
    2111                 :          0 :                                         break;
    2112                 :            :                         }
    2113                 :            : 
    2114         [ #  # ]:          0 :                 if (w) {
    2115                 :            :                         size_t l;
    2116                 :            : 
    2117                 :          0 :                         l = strcspn(w, NEWLINE);
    2118                 :          0 :                         v[i] = strndup(w, l);
    2119         [ #  # ]:          0 :                         if (!v[i]) {
    2120                 :          0 :                                 r = -ENOMEM;
    2121                 :          0 :                                 goto fail;
    2122                 :            :                         }
    2123                 :            : 
    2124                 :          0 :                         n_done++;
    2125         [ #  # ]:          0 :                         if (n_done >= n)
    2126                 :          0 :                                 goto done;
    2127                 :            : 
    2128                 :          0 :                         p = w + l;
    2129                 :            :                 } else
    2130                 :          0 :                         p += strcspn(p, NEWLINE);
    2131                 :            : 
    2132                 :          0 :                 p += strspn(p, NEWLINE);
    2133                 :            :         }
    2134                 :            : 
    2135                 :          0 :         r = -ENXIO;
    2136                 :            : 
    2137                 :          0 : fail:
    2138         [ #  # ]:          0 :         for (i = 0; i < n; i++)
    2139                 :          0 :                 free(v[i]);
    2140                 :            : 
    2141                 :          0 :         return r;
    2142                 :            : 
    2143                 :          0 : done:
    2144                 :          0 :         memcpy(ret_values, v, sizeof(char*) * n);
    2145                 :          0 :         return 0;
    2146                 :            : 
    2147                 :            : }
    2148                 :            : 
    2149                 :        100 : int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) {
    2150                 :            :         CGroupController c;
    2151                 :            :         CGroupMask done;
    2152                 :            :         bool created;
    2153                 :            :         int r;
    2154                 :            : 
    2155                 :            :         /* This one will create a cgroup in our private tree, but also
    2156                 :            :          * duplicate it in the trees specified in mask, and remove it
    2157                 :            :          * in all others.
    2158                 :            :          *
    2159                 :            :          * Returns 0 if the group already existed in the systemd hierarchy,
    2160                 :            :          * 1 on success, negative otherwise.
    2161                 :            :          */
    2162                 :            : 
    2163                 :            :         /* First create the cgroup in our own hierarchy. */
    2164                 :        100 :         r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
    2165         [ +  + ]:        100 :         if (r < 0)
    2166                 :         76 :                 return r;
    2167                 :         24 :         created = r;
    2168                 :            : 
    2169                 :            :         /* If we are in the unified hierarchy, we are done now */
    2170                 :         24 :         r = cg_all_unified();
    2171         [ -  + ]:         24 :         if (r < 0)
    2172                 :          0 :                 return r;
    2173         [ -  + ]:         24 :         if (r > 0)
    2174                 :          0 :                 return created;
    2175                 :            : 
    2176                 :         24 :         supported &= CGROUP_MASK_V1;
    2177                 :         24 :         mask = CGROUP_MASK_EXTEND_JOINED(mask);
    2178                 :         24 :         done = 0;
    2179                 :            : 
    2180                 :            :         /* Otherwise, do the same in the other hierarchies */
    2181         [ +  + ]:        240 :         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
    2182                 :        216 :                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
    2183                 :            :                 const char *n;
    2184                 :            : 
    2185         [ +  + ]:        216 :                 if (!FLAGS_SET(supported, bit))
    2186                 :         72 :                         continue;
    2187                 :            : 
    2188         [ +  + ]:        144 :                 if (FLAGS_SET(done, bit))
    2189                 :         24 :                         continue;
    2190                 :            : 
    2191                 :        120 :                 n = cgroup_controller_to_string(c);
    2192         [ +  + ]:        120 :                 if (FLAGS_SET(mask, bit))
    2193                 :         48 :                         (void) cg_create(n, path);
    2194                 :            :                 else
    2195                 :         72 :                         (void) cg_trim(n, path, true);
    2196                 :            : 
    2197                 :        120 :                 done |= CGROUP_MASK_EXTEND_JOINED(bit);
    2198                 :            :         }
    2199                 :            : 
    2200                 :         24 :         return created;
    2201                 :            : }
    2202                 :            : 
    2203                 :          0 : int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
    2204                 :            :         CGroupController c;
    2205                 :            :         CGroupMask done;
    2206                 :            :         int r;
    2207                 :            : 
    2208                 :          0 :         r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
    2209         [ #  # ]:          0 :         if (r < 0)
    2210                 :          0 :                 return r;
    2211                 :            : 
    2212                 :          0 :         r = cg_all_unified();
    2213         [ #  # ]:          0 :         if (r < 0)
    2214                 :          0 :                 return r;
    2215         [ #  # ]:          0 :         if (r > 0)
    2216                 :          0 :                 return 0;
    2217                 :            : 
    2218                 :          0 :         supported &= CGROUP_MASK_V1;
    2219                 :          0 :         done = 0;
    2220                 :            : 
    2221         [ #  # ]:          0 :         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
    2222                 :          0 :                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
    2223                 :          0 :                 const char *p = NULL;
    2224                 :            : 
    2225         [ #  # ]:          0 :                 if (!FLAGS_SET(supported, bit))
    2226                 :          0 :                         continue;
    2227                 :            : 
    2228         [ #  # ]:          0 :                 if (FLAGS_SET(done, bit))
    2229                 :          0 :                         continue;
    2230                 :            : 
    2231         [ #  # ]:          0 :                 if (path_callback)
    2232                 :          0 :                         p = path_callback(bit, userdata);
    2233         [ #  # ]:          0 :                 if (!p)
    2234                 :          0 :                         p = path;
    2235                 :            : 
    2236                 :          0 :                 (void) cg_attach_fallback(cgroup_controller_to_string(c), p, pid);
    2237                 :          0 :                 done |= CGROUP_MASK_EXTEND_JOINED(bit);
    2238                 :            :         }
    2239                 :            : 
    2240                 :          0 :         return 0;
    2241                 :            : }
    2242                 :            : 
    2243                 :          0 : int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
    2244                 :            :         Iterator i;
    2245                 :            :         void *pidp;
    2246                 :          0 :         int r = 0;
    2247                 :            : 
    2248         [ #  # ]:          0 :         SET_FOREACH(pidp, pids, i) {
    2249                 :          0 :                 pid_t pid = PTR_TO_PID(pidp);
    2250                 :            :                 int q;
    2251                 :            : 
    2252                 :          0 :                 q = cg_attach_everywhere(supported, path, pid, path_callback, userdata);
    2253   [ #  #  #  # ]:          0 :                 if (q < 0 && r >= 0)
    2254                 :          0 :                         r = q;
    2255                 :            :         }
    2256                 :            : 
    2257                 :          0 :         return r;
    2258                 :            : }
    2259                 :            : 
    2260                 :          0 : int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
    2261                 :            :         CGroupController c;
    2262                 :            :         CGroupMask done;
    2263                 :          0 :         int r = 0, q;
    2264                 :            : 
    2265         [ #  # ]:          0 :         if (!path_equal(from, to))  {
    2266                 :          0 :                 r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, CGROUP_REMOVE);
    2267         [ #  # ]:          0 :                 if (r < 0)
    2268                 :          0 :                         return r;
    2269                 :            :         }
    2270                 :            : 
    2271                 :          0 :         q = cg_all_unified();
    2272         [ #  # ]:          0 :         if (q < 0)
    2273                 :          0 :                 return q;
    2274         [ #  # ]:          0 :         if (q > 0)
    2275                 :          0 :                 return r;
    2276                 :            : 
    2277                 :          0 :         supported &= CGROUP_MASK_V1;
    2278                 :          0 :         done = 0;
    2279                 :            : 
    2280         [ #  # ]:          0 :         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
    2281                 :          0 :                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
    2282                 :          0 :                 const char *p = NULL;
    2283                 :            : 
    2284         [ #  # ]:          0 :                 if (!FLAGS_SET(supported, bit))
    2285                 :          0 :                         continue;
    2286                 :            : 
    2287         [ #  # ]:          0 :                 if (FLAGS_SET(done, bit))
    2288                 :          0 :                         continue;
    2289                 :            : 
    2290         [ #  # ]:          0 :                 if (to_callback)
    2291                 :          0 :                         p = to_callback(bit, userdata);
    2292         [ #  # ]:          0 :                 if (!p)
    2293                 :          0 :                         p = to;
    2294                 :            : 
    2295                 :          0 :                 (void) cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, cgroup_controller_to_string(c), p, 0);
    2296                 :          0 :                 done |= CGROUP_MASK_EXTEND_JOINED(bit);
    2297                 :            :         }
    2298                 :            : 
    2299                 :          0 :         return r;
    2300                 :            : }
    2301                 :            : 
    2302                 :          0 : int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) {
    2303                 :            :         CGroupController c;
    2304                 :            :         CGroupMask done;
    2305                 :            :         int r, q;
    2306                 :            : 
    2307                 :          0 :         r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
    2308         [ #  # ]:          0 :         if (r < 0)
    2309                 :          0 :                 return r;
    2310                 :            : 
    2311                 :          0 :         q = cg_all_unified();
    2312         [ #  # ]:          0 :         if (q < 0)
    2313                 :          0 :                 return q;
    2314         [ #  # ]:          0 :         if (q > 0)
    2315                 :          0 :                 return r;
    2316                 :            : 
    2317                 :          0 :         supported &= CGROUP_MASK_V1;
    2318                 :          0 :         done = 0;
    2319                 :            : 
    2320         [ #  # ]:          0 :         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
    2321                 :          0 :                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
    2322                 :            : 
    2323         [ #  # ]:          0 :                 if (!FLAGS_SET(supported, bit))
    2324                 :          0 :                         continue;
    2325                 :            : 
    2326         [ #  # ]:          0 :                 if (FLAGS_SET(done, bit))
    2327                 :          0 :                         continue;
    2328                 :            : 
    2329                 :          0 :                 (void) cg_trim(cgroup_controller_to_string(c), path, delete_root);
    2330                 :          0 :                 done |= CGROUP_MASK_EXTEND_JOINED(bit);
    2331                 :            :         }
    2332                 :            : 
    2333                 :          0 :         return r;
    2334                 :            : }
    2335                 :            : 
    2336                 :       1796 : int cg_mask_to_string(CGroupMask mask, char **ret) {
    2337                 :       1796 :         _cleanup_free_ char *s = NULL;
    2338                 :       1796 :         size_t n = 0, allocated = 0;
    2339                 :       1796 :         bool space = false;
    2340                 :            :         CGroupController c;
    2341                 :            : 
    2342         [ -  + ]:       1796 :         assert(ret);
    2343                 :            : 
    2344         [ +  + ]:       1796 :         if (mask == 0) {
    2345                 :        812 :                 *ret = NULL;
    2346                 :        812 :                 return 0;
    2347                 :            :         }
    2348                 :            : 
    2349         [ +  + ]:       9840 :         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
    2350                 :            :                 const char *k;
    2351                 :            :                 size_t l;
    2352                 :            : 
    2353         [ +  + ]:       8856 :                 if (!FLAGS_SET(mask, CGROUP_CONTROLLER_TO_MASK(c)))
    2354                 :       6696 :                         continue;
    2355                 :            : 
    2356                 :       2160 :                 k = cgroup_controller_to_string(c);
    2357                 :       2160 :                 l = strlen(k);
    2358                 :            : 
    2359         [ -  + ]:       2160 :                 if (!GREEDY_REALLOC(s, allocated, n + space + l + 1))
    2360                 :          0 :                         return -ENOMEM;
    2361                 :            : 
    2362         [ +  + ]:       2160 :                 if (space)
    2363                 :       1176 :                         s[n] = ' ';
    2364                 :       2160 :                 memcpy(s + n + space, k, l);
    2365                 :       2160 :                 n += space + l;
    2366                 :            : 
    2367                 :       2160 :                 space = true;
    2368                 :            :         }
    2369                 :            : 
    2370         [ -  + ]:        984 :         assert(s);
    2371                 :            : 
    2372                 :        984 :         s[n] = 0;
    2373                 :        984 :         *ret = TAKE_PTR(s);
    2374                 :            : 
    2375                 :        984 :         return 0;
    2376                 :            : }
    2377                 :            : 
    2378                 :         92 : int cg_mask_from_string(const char *value, CGroupMask *ret) {
    2379                 :         92 :         CGroupMask m = 0;
    2380                 :            : 
    2381         [ -  + ]:         92 :         assert(ret);
    2382         [ -  + ]:         92 :         assert(value);
    2383                 :            : 
    2384                 :        132 :         for (;;) {
    2385   [ +  -  +  + ]:        224 :                 _cleanup_free_ char *n = NULL;
    2386                 :            :                 CGroupController v;
    2387                 :            :                 int r;
    2388                 :            : 
    2389                 :        224 :                 r = extract_first_word(&value, &n, NULL, 0);
    2390         [ -  + ]:        224 :                 if (r < 0)
    2391                 :          0 :                         return r;
    2392         [ +  + ]:        224 :                 if (r == 0)
    2393                 :         92 :                         break;
    2394                 :            : 
    2395                 :        132 :                 v = cgroup_controller_from_string(n);
    2396         [ +  + ]:        132 :                 if (v < 0)
    2397                 :          8 :                         continue;
    2398                 :            : 
    2399                 :        124 :                 m |= CGROUP_CONTROLLER_TO_MASK(v);
    2400                 :            :         }
    2401                 :            : 
    2402                 :         92 :         *ret = m;
    2403                 :         92 :         return 0;
    2404                 :            : }
    2405                 :            : 
    2406                 :        192 : int cg_mask_supported(CGroupMask *ret) {
    2407                 :            :         CGroupMask mask;
    2408                 :            :         int r;
    2409                 :            : 
    2410                 :            :         /* Determines the mask of supported cgroup controllers. Only includes controllers we can make sense of and that
    2411                 :            :          * are actually accessible. Only covers real controllers, i.e. not the CGROUP_CONTROLLER_BPF_xyz
    2412                 :            :          * pseudo-controllers. */
    2413                 :            : 
    2414                 :        192 :         r = cg_all_unified();
    2415         [ -  + ]:        192 :         if (r < 0)
    2416                 :          0 :                 return r;
    2417         [ -  + ]:        192 :         if (r > 0) {
    2418   [ #  #  #  #  :          0 :                 _cleanup_free_ char *root = NULL, *controllers = NULL, *path = NULL;
                   #  # ]
    2419                 :            : 
    2420                 :            :                 /* In the unified hierarchy we can read the supported
    2421                 :            :                  * and accessible controllers from a the top-level
    2422                 :            :                  * cgroup attribute */
    2423                 :            : 
    2424                 :          0 :                 r = cg_get_root_path(&root);
    2425         [ #  # ]:          0 :                 if (r < 0)
    2426                 :          0 :                         return r;
    2427                 :            : 
    2428                 :          0 :                 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, root, "cgroup.controllers", &path);
    2429         [ #  # ]:          0 :                 if (r < 0)
    2430                 :          0 :                         return r;
    2431                 :            : 
    2432                 :          0 :                 r = read_one_line_file(path, &controllers);
    2433         [ #  # ]:          0 :                 if (r < 0)
    2434                 :          0 :                         return r;
    2435                 :            : 
    2436                 :          0 :                 r = cg_mask_from_string(controllers, &mask);
    2437         [ #  # ]:          0 :                 if (r < 0)
    2438                 :          0 :                         return r;
    2439                 :            : 
    2440                 :            :                 /* Currently, we support the cpu, memory, io and pids controller in the unified hierarchy, mask
    2441                 :            :                  * everything else off. */
    2442                 :          0 :                 mask &= CGROUP_MASK_V2;
    2443                 :            : 
    2444                 :            :         } else {
    2445                 :            :                 CGroupController c;
    2446                 :            : 
    2447                 :            :                 /* In the legacy hierarchy, we check which hierarchies are mounted. */
    2448                 :            : 
    2449                 :        192 :                 mask = 0;
    2450         [ +  + ]:       1920 :                 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
    2451                 :       1728 :                         CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
    2452                 :            :                         const char *n;
    2453                 :            : 
    2454         [ +  + ]:       1728 :                         if (!FLAGS_SET(CGROUP_MASK_V1, bit))
    2455                 :        576 :                                 continue;
    2456                 :            : 
    2457                 :       1152 :                         n = cgroup_controller_to_string(c);
    2458         [ +  - ]:       1152 :                         if (controller_is_accessible(n) >= 0)
    2459                 :       1152 :                                 mask |= bit;
    2460                 :            :                 }
    2461                 :            :         }
    2462                 :            : 
    2463                 :        192 :         *ret = mask;
    2464                 :        192 :         return 0;
    2465                 :            : }
    2466                 :            : 
    2467                 :          0 : int cg_kernel_controllers(Set **ret) {
    2468                 :          0 :         _cleanup_set_free_free_ Set *controllers = NULL;
    2469                 :          0 :         _cleanup_fclose_ FILE *f = NULL;
    2470                 :            :         int r;
    2471                 :            : 
    2472         [ #  # ]:          0 :         assert(ret);
    2473                 :            : 
    2474                 :            :         /* Determines the full list of kernel-known controllers. Might include controllers we don't actually support
    2475                 :            :          * and controllers that aren't currently accessible (because not mounted). This does not include "name="
    2476                 :            :          * pseudo-controllers. */
    2477                 :            : 
    2478                 :          0 :         controllers = set_new(&string_hash_ops);
    2479         [ #  # ]:          0 :         if (!controllers)
    2480                 :          0 :                 return -ENOMEM;
    2481                 :            : 
    2482                 :          0 :         r = fopen_unlocked("/proc/cgroups", "re", &f);
    2483         [ #  # ]:          0 :         if (r == -ENOENT) {
    2484                 :          0 :                 *ret = NULL;
    2485                 :          0 :                 return 0;
    2486                 :            :         }
    2487         [ #  # ]:          0 :         if (r < 0)
    2488                 :          0 :                 return r;
    2489                 :            : 
    2490                 :            :         /* Ignore the header line */
    2491                 :          0 :         (void) read_line(f, (size_t) -1, NULL);
    2492                 :            : 
    2493                 :          0 :         for (;;) {
    2494                 :            :                 char *controller;
    2495                 :          0 :                 int enabled = 0;
    2496                 :            : 
    2497                 :          0 :                 errno = 0;
    2498         [ #  # ]:          0 :                 if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
    2499                 :            : 
    2500         [ #  # ]:          0 :                         if (feof(f))
    2501                 :          0 :                                 break;
    2502                 :            : 
    2503         [ #  # ]:          0 :                         if (ferror(f))
    2504                 :          0 :                                 return errno_or_else(EIO);
    2505                 :            : 
    2506                 :          0 :                         return -EBADMSG;
    2507                 :            :                 }
    2508                 :            : 
    2509         [ #  # ]:          0 :                 if (!enabled) {
    2510                 :          0 :                         free(controller);
    2511                 :          0 :                         continue;
    2512                 :            :                 }
    2513                 :            : 
    2514         [ #  # ]:          0 :                 if (!cg_controller_is_valid(controller)) {
    2515                 :          0 :                         free(controller);
    2516                 :          0 :                         return -EBADMSG;
    2517                 :            :                 }
    2518                 :            : 
    2519                 :          0 :                 r = set_consume(controllers, controller);
    2520         [ #  # ]:          0 :                 if (r < 0)
    2521                 :          0 :                         return r;
    2522                 :            :         }
    2523                 :            : 
    2524                 :          0 :         *ret = TAKE_PTR(controllers);
    2525                 :            : 
    2526                 :          0 :         return 0;
    2527                 :            : }
    2528                 :            : 
    2529                 :            : static thread_local CGroupUnified unified_cache = CGROUP_UNIFIED_UNKNOWN;
    2530                 :            : 
    2531                 :            : /* The hybrid mode was initially implemented in v232 and simply mounted cgroup2 on /sys/fs/cgroup/systemd.  This
    2532                 :            :  * unfortunately broke other tools (such as docker) which expected the v1 "name=systemd" hierarchy on
    2533                 :            :  * /sys/fs/cgroup/systemd.  From v233 and on, the hybrid mode mountnbs v2 on /sys/fs/cgroup/unified and maintains
    2534                 :            :  * "name=systemd" hierarchy on /sys/fs/cgroup/systemd for compatibility with other tools.
    2535                 :            :  *
    2536                 :            :  * To keep live upgrade working, we detect and support v232 layout.  When v232 layout is detected, to keep cgroup v2
    2537                 :            :  * process management but disable the compat dual layout, we return %true on
    2538                 :            :  * cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) and %false on cg_hybrid_unified().
    2539                 :            :  */
    2540                 :            : static thread_local bool unified_systemd_v232;
    2541                 :            : 
    2542                 :      14176 : static int cg_unified_update(void) {
    2543                 :            : 
    2544                 :            :         struct statfs fs;
    2545                 :            : 
    2546                 :            :         /* Checks if we support the unified hierarchy. Returns an
    2547                 :            :          * error when the cgroup hierarchies aren't mounted yet or we
    2548                 :            :          * have any other trouble determining if the unified hierarchy
    2549                 :            :          * is supported. */
    2550                 :            : 
    2551         [ +  + ]:      14176 :         if (unified_cache >= CGROUP_UNIFIED_NONE)
    2552                 :      14068 :                 return 0;
    2553                 :            : 
    2554         [ -  + ]:        108 :         if (statfs("/sys/fs/cgroup/", &fs) < 0)
    2555         [ #  # ]:          0 :                 return log_debug_errno(errno, "statfs(\"/sys/fs/cgroup/\") failed: %m");
    2556                 :            : 
    2557         [ -  + ]:        108 :         if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
    2558         [ #  # ]:          0 :                 log_debug("Found cgroup2 on /sys/fs/cgroup/, full unified hierarchy");
    2559                 :          0 :                 unified_cache = CGROUP_UNIFIED_ALL;
    2560         [ +  - ]:        108 :         } else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) {
    2561         [ +  - ]:        108 :                 if (statfs("/sys/fs/cgroup/unified/", &fs) == 0 &&
    2562         [ +  - ]:        108 :                     F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
    2563         [ +  + ]:        108 :                         log_debug("Found cgroup2 on /sys/fs/cgroup/unified, unified hierarchy for systemd controller");
    2564                 :        108 :                         unified_cache = CGROUP_UNIFIED_SYSTEMD;
    2565                 :        108 :                         unified_systemd_v232 = false;
    2566                 :            :                 } else {
    2567         [ #  # ]:          0 :                         if (statfs("/sys/fs/cgroup/systemd/", &fs) < 0)
    2568         [ #  # ]:          0 :                                 return log_debug_errno(errno, "statfs(\"/sys/fs/cgroup/systemd\" failed: %m");
    2569                 :            : 
    2570         [ #  # ]:          0 :                         if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
    2571         [ #  # ]:          0 :                                 log_debug("Found cgroup2 on /sys/fs/cgroup/systemd, unified hierarchy for systemd controller (v232 variant)");
    2572                 :          0 :                                 unified_cache = CGROUP_UNIFIED_SYSTEMD;
    2573                 :          0 :                                 unified_systemd_v232 = true;
    2574         [ #  # ]:          0 :                         } else if (F_TYPE_EQUAL(fs.f_type, CGROUP_SUPER_MAGIC)) {
    2575         [ #  # ]:          0 :                                 log_debug("Found cgroup on /sys/fs/cgroup/systemd, legacy hierarchy");
    2576                 :          0 :                                 unified_cache = CGROUP_UNIFIED_NONE;
    2577                 :            :                         } else {
    2578         [ #  # ]:          0 :                                 log_debug("Unexpected filesystem type %llx mounted on /sys/fs/cgroup/systemd, assuming legacy hierarchy",
    2579                 :            :                                           (unsigned long long) fs.f_type);
    2580                 :          0 :                                 unified_cache = CGROUP_UNIFIED_NONE;
    2581                 :            :                         }
    2582                 :            :                 }
    2583                 :            :         } else
    2584         [ #  # ]:          0 :                 return log_debug_errno(SYNTHETIC_ERRNO(ENOMEDIUM),
    2585                 :            :                                        "Unknown filesystem type %llx mounted on /sys/fs/cgroup.",
    2586                 :            :                                        (unsigned long long)fs.f_type);
    2587                 :            : 
    2588                 :        108 :         return 0;
    2589                 :            : }
    2590                 :            : 
    2591                 :      11164 : int cg_unified_controller(const char *controller) {
    2592                 :            :         int r;
    2593                 :            : 
    2594                 :      11164 :         r = cg_unified_update();
    2595         [ -  + ]:      11164 :         if (r < 0)
    2596                 :          0 :                 return r;
    2597                 :            : 
    2598         [ -  + ]:      11164 :         if (unified_cache == CGROUP_UNIFIED_NONE)
    2599                 :          0 :                 return false;
    2600                 :            : 
    2601         [ -  + ]:      11164 :         if (unified_cache >= CGROUP_UNIFIED_ALL)
    2602                 :          0 :                 return true;
    2603                 :            : 
    2604                 :      11164 :         return streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER);
    2605                 :            : }
    2606                 :            : 
    2607                 :       2440 : int cg_all_unified(void) {
    2608                 :            :         int r;
    2609                 :            : 
    2610                 :       2440 :         r = cg_unified_update();
    2611         [ -  + ]:       2440 :         if (r < 0)
    2612                 :          0 :                 return r;
    2613                 :            : 
    2614                 :       2440 :         return unified_cache >= CGROUP_UNIFIED_ALL;
    2615                 :            : }
    2616                 :            : 
    2617                 :        504 : int cg_hybrid_unified(void) {
    2618                 :            :         int r;
    2619                 :            : 
    2620                 :        504 :         r = cg_unified_update();
    2621         [ -  + ]:        504 :         if (r < 0)
    2622                 :          0 :                 return r;
    2623                 :            : 
    2624   [ +  -  +  - ]:        504 :         return unified_cache == CGROUP_UNIFIED_SYSTEMD && !unified_systemd_v232;
    2625                 :            : }
    2626                 :            : 
    2627                 :         68 : int cg_unified_flush(void) {
    2628                 :         68 :         unified_cache = CGROUP_UNIFIED_UNKNOWN;
    2629                 :            : 
    2630                 :         68 :         return cg_unified_update();
    2631                 :            : }
    2632                 :            : 
    2633                 :         24 : int cg_enable_everywhere(
    2634                 :            :                 CGroupMask supported,
    2635                 :            :                 CGroupMask mask,
    2636                 :            :                 const char *p,
    2637                 :            :                 CGroupMask *ret_result_mask) {
    2638                 :            : 
    2639                 :         24 :         _cleanup_fclose_ FILE *f = NULL;
    2640                 :         24 :         _cleanup_free_ char *fs = NULL;
    2641                 :            :         CGroupController c;
    2642                 :         24 :         CGroupMask ret = 0;
    2643                 :            :         int r;
    2644                 :            : 
    2645         [ -  + ]:         24 :         assert(p);
    2646                 :            : 
    2647         [ -  + ]:         24 :         if (supported == 0) {
    2648         [ #  # ]:          0 :                 if (ret_result_mask)
    2649                 :          0 :                         *ret_result_mask = 0;
    2650                 :          0 :                 return 0;
    2651                 :            :         }
    2652                 :            : 
    2653                 :         24 :         r = cg_all_unified();
    2654         [ -  + ]:         24 :         if (r < 0)
    2655                 :          0 :                 return r;
    2656         [ +  - ]:         24 :         if (r == 0) {
    2657                 :            :                 /* On the legacy hierarchy there's no concept of "enabling" controllers in cgroups defined. Let's claim
    2658                 :            :                  * complete success right away. (If you wonder why we return the full mask here, rather than zero: the
    2659                 :            :                  * caller tends to use the returned mask later on to compare if all controllers where properly joined,
    2660                 :            :                  * and if not requeues realization. This use is the primary purpose of the return value, hence let's
    2661                 :            :                  * minimize surprises here and reduce triggers for re-realization by always saying we fully
    2662                 :            :                  * succeeded.) */
    2663         [ +  - ]:         24 :                 if (ret_result_mask)
    2664                 :         24 :                         *ret_result_mask = mask & supported & CGROUP_MASK_V2; /* If you wonder why we mask this with
    2665                 :            :                                                                                * CGROUP_MASK_V2: The 'supported' mask
    2666                 :            :                                                                                * might contain pure-V1 or BPF
    2667                 :            :                                                                                * controllers, and we never want to
    2668                 :            :                                                                                * claim that we could enable those with
    2669                 :            :                                                                                * cgroup.subtree_control */
    2670                 :         24 :                 return 0;
    2671                 :            :         }
    2672                 :            : 
    2673                 :          0 :         r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, p, "cgroup.subtree_control", &fs);
    2674         [ #  # ]:          0 :         if (r < 0)
    2675                 :          0 :                 return r;
    2676                 :            : 
    2677         [ #  # ]:          0 :         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
    2678                 :          0 :                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
    2679                 :            :                 const char *n;
    2680                 :            : 
    2681         [ #  # ]:          0 :                 if (!FLAGS_SET(CGROUP_MASK_V2, bit))
    2682                 :          0 :                         continue;
    2683                 :            : 
    2684         [ #  # ]:          0 :                 if (!FLAGS_SET(supported, bit))
    2685                 :          0 :                         continue;
    2686                 :            : 
    2687                 :          0 :                 n = cgroup_controller_to_string(c);
    2688                 :          0 :                 {
    2689                 :          0 :                         char s[1 + strlen(n) + 1];
    2690                 :            : 
    2691         [ #  # ]:          0 :                         s[0] = FLAGS_SET(mask, bit) ? '+' : '-';
    2692                 :          0 :                         strcpy(s + 1, n);
    2693                 :            : 
    2694         [ #  # ]:          0 :                         if (!f) {
    2695                 :          0 :                                 f = fopen(fs, "we");
    2696         [ #  # ]:          0 :                                 if (!f)
    2697         [ #  # ]:          0 :                                         return log_debug_errno(errno, "Failed to open cgroup.subtree_control file of %s: %m", p);
    2698                 :            :                         }
    2699                 :            : 
    2700                 :          0 :                         r = write_string_stream(f, s, WRITE_STRING_FILE_DISABLE_BUFFER);
    2701         [ #  # ]:          0 :                         if (r < 0) {
    2702   [ #  #  #  # ]:          0 :                                 log_debug_errno(r, "Failed to %s controller %s for %s (%s): %m",
    2703                 :            :                                                 FLAGS_SET(mask, bit) ? "enable" : "disable", n, p, fs);
    2704                 :          0 :                                 clearerr(f);
    2705                 :            : 
    2706                 :            :                                 /* If we can't turn off a controller, leave it on in the reported resulting mask. This
    2707                 :            :                                  * happens for example when we attempt to turn off a controller up in the tree that is
    2708                 :            :                                  * used down in the tree. */
    2709   [ #  #  #  # ]:          0 :                                 if (!FLAGS_SET(mask, bit) && r == -EBUSY) /* You might wonder why we check for EBUSY
    2710                 :            :                                                                            * only here, and not follow the same logic
    2711                 :            :                                                                            * for other errors such as EINVAL or
    2712                 :            :                                                                            * EOPNOTSUPP or anything else. That's
    2713                 :            :                                                                            * because EBUSY indicates that the
    2714                 :            :                                                                            * controllers is currently enabled and
    2715                 :            :                                                                            * cannot be disabled because something down
    2716                 :            :                                                                            * the hierarchy is still using it. Any other
    2717                 :            :                                                                            * error most likely means something like "I
    2718                 :            :                                                                            * never heard of this controller" or
    2719                 :            :                                                                            * similar. In the former case it's hence
    2720                 :            :                                                                            * safe to assume the controller is still on
    2721                 :            :                                                                            * after the failed operation, while in the
    2722                 :            :                                                                            * latter case it's safer to assume the
    2723                 :            :                                                                            * controller is unknown and hence certainly
    2724                 :            :                                                                            * not enabled. */
    2725                 :          0 :                                         ret |= bit;
    2726                 :            :                         } else {
    2727                 :            :                                 /* Otherwise, if we managed to turn on a controller, set the bit reflecting that. */
    2728         [ #  # ]:          0 :                                 if (FLAGS_SET(mask, bit))
    2729                 :          0 :                                         ret |= bit;
    2730                 :            :                         }
    2731                 :            :                 }
    2732                 :            :         }
    2733                 :            : 
    2734                 :            :         /* Let's return the precise set of controllers now enabled for the cgroup. */
    2735         [ #  # ]:          0 :         if (ret_result_mask)
    2736                 :          0 :                 *ret_result_mask = ret;
    2737                 :            : 
    2738                 :          0 :         return 0;
    2739                 :            : }
    2740                 :            : 
    2741                 :         32 : bool cg_is_unified_wanted(void) {
    2742                 :            :         static thread_local int wanted = -1;
    2743                 :            :         int r;
    2744                 :            :         bool b;
    2745                 :         32 :         const bool is_default = DEFAULT_HIERARCHY == CGROUP_UNIFIED_ALL;
    2746                 :         32 :         _cleanup_free_ char *c = NULL;
    2747                 :            : 
    2748                 :            :         /* If we have a cached value, return that. */
    2749         [ +  + ]:         32 :         if (wanted >= 0)
    2750                 :         28 :                 return wanted;
    2751                 :            : 
    2752                 :            :         /* If the hierarchy is already mounted, then follow whatever
    2753                 :            :          * was chosen for it. */
    2754         [ +  - ]:          4 :         if (cg_unified_flush() >= 0)
    2755                 :          4 :                 return (wanted = unified_cache >= CGROUP_UNIFIED_ALL);
    2756                 :            : 
    2757                 :            :         /* If we were explicitly passed systemd.unified_cgroup_hierarchy,
    2758                 :            :          * respect that. */
    2759                 :          0 :         r = proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", &b);
    2760         [ #  # ]:          0 :         if (r > 0)
    2761                 :          0 :                 return (wanted = b);
    2762                 :            : 
    2763                 :            :         /* If we passed cgroup_no_v1=all with no other instructions, it seems
    2764                 :            :          * highly unlikely that we want to use hybrid or legacy hierarchy. */
    2765                 :          0 :         r = proc_cmdline_get_key("cgroup_no_v1", 0, &c);
    2766   [ #  #  #  # ]:          0 :         if (r > 0 && streq_ptr(c, "all"))
    2767                 :          0 :                 return (wanted = true);
    2768                 :            : 
    2769                 :          0 :         return (wanted = is_default);
    2770                 :            : }
    2771                 :            : 
    2772                 :         32 : bool cg_is_legacy_wanted(void) {
    2773                 :            :         static thread_local int wanted = -1;
    2774                 :            : 
    2775                 :            :         /* If we have a cached value, return that. */
    2776         [ +  + ]:         32 :         if (wanted >= 0)
    2777                 :         28 :                 return wanted;
    2778                 :            : 
    2779                 :            :         /* Check if we have cgroup v2 already mounted. */
    2780         [ +  - ]:          4 :         if (cg_unified_flush() >= 0 &&
    2781         [ -  + ]:          4 :             unified_cache == CGROUP_UNIFIED_ALL)
    2782                 :          0 :                 return (wanted = false);
    2783                 :            : 
    2784                 :            :         /* Otherwise, assume that at least partial legacy is wanted,
    2785                 :            :          * since cgroup v2 should already be mounted at this point. */
    2786                 :          4 :         return (wanted = true);
    2787                 :            : }
    2788                 :            : 
    2789                 :         32 : bool cg_is_hybrid_wanted(void) {
    2790                 :            :         static thread_local int wanted = -1;
    2791                 :            :         int r;
    2792                 :            :         bool b;
    2793                 :         32 :         const bool is_default = DEFAULT_HIERARCHY >= CGROUP_UNIFIED_SYSTEMD;
    2794                 :            :         /* We default to true if the default is "hybrid", obviously,
    2795                 :            :          * but also when the default is "unified", because if we get
    2796                 :            :          * called, it means that unified hierarchy was not mounted. */
    2797                 :            : 
    2798                 :            :         /* If we have a cached value, return that. */
    2799         [ +  + ]:         32 :         if (wanted >= 0)
    2800                 :         28 :                 return wanted;
    2801                 :            : 
    2802                 :            :         /* If the hierarchy is already mounted, then follow whatever
    2803                 :            :          * was chosen for it. */
    2804         [ +  - ]:          4 :         if (cg_unified_flush() >= 0 &&
    2805         [ -  + ]:          4 :             unified_cache == CGROUP_UNIFIED_ALL)
    2806                 :          0 :                 return (wanted = false);
    2807                 :            : 
    2808                 :            :         /* Otherwise, let's see what the kernel command line has to say.
    2809                 :            :          * Since checking is expensive, cache a non-error result. */
    2810                 :          4 :         r = proc_cmdline_get_bool("systemd.legacy_systemd_cgroup_controller", &b);
    2811                 :            : 
    2812                 :            :         /* The meaning of the kernel option is reversed wrt. to the return value
    2813                 :            :          * of this function, hence the negation. */
    2814         [ -  + ]:          4 :         return (wanted = r > 0 ? !b : is_default);
    2815                 :            : }
    2816                 :            : 
    2817                 :          8 : int cg_weight_parse(const char *s, uint64_t *ret) {
    2818                 :            :         uint64_t u;
    2819                 :            :         int r;
    2820                 :            : 
    2821         [ -  + ]:          8 :         if (isempty(s)) {
    2822                 :          0 :                 *ret = CGROUP_WEIGHT_INVALID;
    2823                 :          0 :                 return 0;
    2824                 :            :         }
    2825                 :            : 
    2826                 :          8 :         r = safe_atou64(s, &u);
    2827         [ -  + ]:          8 :         if (r < 0)
    2828                 :          0 :                 return r;
    2829                 :            : 
    2830   [ +  -  -  + ]:          8 :         if (u < CGROUP_WEIGHT_MIN || u > CGROUP_WEIGHT_MAX)
    2831                 :          0 :                 return -ERANGE;
    2832                 :            : 
    2833                 :          8 :         *ret = u;
    2834                 :          8 :         return 0;
    2835                 :            : }
    2836                 :            : 
    2837                 :            : const uint64_t cgroup_io_limit_defaults[_CGROUP_IO_LIMIT_TYPE_MAX] = {
    2838                 :            :         [CGROUP_IO_RBPS_MAX]    = CGROUP_LIMIT_MAX,
    2839                 :            :         [CGROUP_IO_WBPS_MAX]    = CGROUP_LIMIT_MAX,
    2840                 :            :         [CGROUP_IO_RIOPS_MAX]   = CGROUP_LIMIT_MAX,
    2841                 :            :         [CGROUP_IO_WIOPS_MAX]   = CGROUP_LIMIT_MAX,
    2842                 :            : };
    2843                 :            : 
    2844                 :            : static const char* const cgroup_io_limit_type_table[_CGROUP_IO_LIMIT_TYPE_MAX] = {
    2845                 :            :         [CGROUP_IO_RBPS_MAX]    = "IOReadBandwidthMax",
    2846                 :            :         [CGROUP_IO_WBPS_MAX]    = "IOWriteBandwidthMax",
    2847                 :            :         [CGROUP_IO_RIOPS_MAX]   = "IOReadIOPSMax",
    2848                 :            :         [CGROUP_IO_WIOPS_MAX]   = "IOWriteIOPSMax",
    2849                 :            : };
    2850                 :            : 
    2851   [ +  +  +  + ]:         48 : DEFINE_STRING_TABLE_LOOKUP(cgroup_io_limit_type, CGroupIOLimitType);
    2852                 :            : 
    2853                 :          4 : int cg_cpu_shares_parse(const char *s, uint64_t *ret) {
    2854                 :            :         uint64_t u;
    2855                 :            :         int r;
    2856                 :            : 
    2857         [ -  + ]:          4 :         if (isempty(s)) {
    2858                 :          0 :                 *ret = CGROUP_CPU_SHARES_INVALID;
    2859                 :          0 :                 return 0;
    2860                 :            :         }
    2861                 :            : 
    2862                 :          4 :         r = safe_atou64(s, &u);
    2863         [ -  + ]:          4 :         if (r < 0)
    2864                 :          0 :                 return r;
    2865                 :            : 
    2866   [ +  -  -  + ]:          4 :         if (u < CGROUP_CPU_SHARES_MIN || u > CGROUP_CPU_SHARES_MAX)
    2867                 :          0 :                 return -ERANGE;
    2868                 :            : 
    2869                 :          4 :         *ret = u;
    2870                 :          4 :         return 0;
    2871                 :            : }
    2872                 :            : 
    2873                 :          0 : int cg_blkio_weight_parse(const char *s, uint64_t *ret) {
    2874                 :            :         uint64_t u;
    2875                 :            :         int r;
    2876                 :            : 
    2877         [ #  # ]:          0 :         if (isempty(s)) {
    2878                 :          0 :                 *ret = CGROUP_BLKIO_WEIGHT_INVALID;
    2879                 :          0 :                 return 0;
    2880                 :            :         }
    2881                 :            : 
    2882                 :          0 :         r = safe_atou64(s, &u);
    2883         [ #  # ]:          0 :         if (r < 0)
    2884                 :          0 :                 return r;
    2885                 :            : 
    2886   [ #  #  #  # ]:          0 :         if (u < CGROUP_BLKIO_WEIGHT_MIN || u > CGROUP_BLKIO_WEIGHT_MAX)
    2887                 :          0 :                 return -ERANGE;
    2888                 :            : 
    2889                 :          0 :         *ret = u;
    2890                 :          0 :         return 0;
    2891                 :            : }
    2892                 :            : 
    2893                 :          8 : bool is_cgroup_fs(const struct statfs *s) {
    2894         [ -  + ]:          8 :         return is_fs_type(s, CGROUP_SUPER_MAGIC) ||
    2895         [ #  # ]:          0 :                is_fs_type(s, CGROUP2_SUPER_MAGIC);
    2896                 :            : }
    2897                 :            : 
    2898                 :          4 : bool fd_is_cgroup_fs(int fd) {
    2899                 :            :         struct statfs s;
    2900                 :            : 
    2901         [ -  + ]:          4 :         if (fstatfs(fd, &s) < 0)
    2902                 :          0 :                 return -errno;
    2903                 :            : 
    2904                 :          4 :         return is_cgroup_fs(&s);
    2905                 :            : }
    2906                 :            : 
    2907                 :            : static const char *const cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
    2908                 :            :         [CGROUP_CONTROLLER_CPU] = "cpu",
    2909                 :            :         [CGROUP_CONTROLLER_CPUACCT] = "cpuacct",
    2910                 :            :         [CGROUP_CONTROLLER_IO] = "io",
    2911                 :            :         [CGROUP_CONTROLLER_BLKIO] = "blkio",
    2912                 :            :         [CGROUP_CONTROLLER_MEMORY] = "memory",
    2913                 :            :         [CGROUP_CONTROLLER_DEVICES] = "devices",
    2914                 :            :         [CGROUP_CONTROLLER_PIDS] = "pids",
    2915                 :            :         [CGROUP_CONTROLLER_BPF_FIREWALL] = "bpf-firewall",
    2916                 :            :         [CGROUP_CONTROLLER_BPF_DEVICES] = "bpf-devices",
    2917                 :            : };
    2918                 :            : 
    2919   [ +  +  +  + ]:       4448 : DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController);
    2920                 :            : 
    2921                 :         48 : CGroupMask get_cpu_accounting_mask(void) {
    2922                 :            :         static CGroupMask needed_mask = (CGroupMask) -1;
    2923                 :            : 
    2924                 :            :         /* On kernel ≥4.15 with unified hierarchy, cpu.stat's usage_usec is
    2925                 :            :          * provided externally from the CPU controller, which means we don't
    2926                 :            :          * need to enable the CPU controller just to get metrics. This is good,
    2927                 :            :          * because enabling the CPU controller comes at a minor performance
    2928                 :            :          * hit, especially when it's propagated deep into large hierarchies.
    2929                 :            :          * There's also no separate CPU accounting controller available within
    2930                 :            :          * a unified hierarchy.
    2931                 :            :          *
    2932                 :            :          * This combination of factors results in the desired cgroup mask to
    2933                 :            :          * enable for CPU accounting varying as follows:
    2934                 :            :          *
    2935                 :            :          *                   ╔═════════════════════╤═════════════════════╗
    2936                 :            :          *                   ║     Linux ≥4.15     │     Linux <4.15     ║
    2937                 :            :          *   ╔═══════════════╬═════════════════════╪═════════════════════╣
    2938                 :            :          *   ║ Unified       ║ nothing             │ CGROUP_MASK_CPU     ║
    2939                 :            :          *   ╟───────────────╫─────────────────────┼─────────────────────╢
    2940                 :            :          *   ║ Hybrid/Legacy ║ CGROUP_MASK_CPUACCT │ CGROUP_MASK_CPUACCT ║
    2941                 :            :          *   ╚═══════════════╩═════════════════════╧═════════════════════╝
    2942                 :            :          *
    2943                 :            :          * We check kernel version here instead of manually checking whether
    2944                 :            :          * cpu.stat is present for every cgroup, as that check in itself would
    2945                 :            :          * already be fairly expensive.
    2946                 :            :          *
    2947                 :            :          * Kernels where this patch has been backported will therefore have the
    2948                 :            :          * CPU controller enabled unnecessarily. This is more expensive than
    2949                 :            :          * necessary, but harmless. ☺️
    2950                 :            :          */
    2951                 :            : 
    2952         [ +  + ]:         48 :         if (needed_mask == (CGroupMask) -1) {
    2953         [ -  + ]:          8 :                 if (cg_all_unified()) {
    2954                 :            :                         struct utsname u;
    2955         [ #  # ]:          0 :                         assert_se(uname(&u) >= 0);
    2956                 :            : 
    2957         [ #  # ]:          0 :                         if (str_verscmp(u.release, "4.15") < 0)
    2958                 :          0 :                                 needed_mask = CGROUP_MASK_CPU;
    2959                 :            :                         else
    2960                 :          0 :                                 needed_mask = 0;
    2961                 :            :                 } else
    2962                 :          8 :                         needed_mask = CGROUP_MASK_CPUACCT;
    2963                 :            :         }
    2964                 :            : 
    2965                 :         48 :         return needed_mask;
    2966                 :            : }
    2967                 :            : 
    2968                 :          0 : bool cpu_accounting_is_cheap(void) {
    2969                 :          0 :         return get_cpu_accounting_mask() == 0;
    2970                 :            : }

Generated by: LCOV version 1.14