github.com/rigado/snapd@v2.42.5-go-mod+incompatible/cmd/libsnap-confine-private/cgroup-support.c (about)

     1  /*
     2   * Copyright (C) 2019 Canonical Ltd
     3   *
     4   * This program is free software: you can redistribute it and/or modify
     5   * it under the terms of the GNU General Public License version 3 as
     6   * published by the Free Software Foundation.
     7   *
     8   * This program is distributed in the hope that it will be useful,
     9   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11   * GNU General Public License for more details.
    12   *
    13   * You should have received a copy of the GNU General Public License
    14   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15   *
    16   */
    17  
    18  // For AT_EMPTY_PATH and O_PATH
    19  #define _GNU_SOURCE
    20  
    21  #include "cgroup-support.h"
    22  
    23  #include <errno.h>
    24  #include <stdio.h>
    25  #include <string.h>
    26  #include <sys/stat.h>
    27  #include <sys/types.h>
    28  #include <sys/vfs.h>
    29  #include <unistd.h>
    30  
    31  #include "cleanup-funcs.h"
    32  #include "string-utils.h"
    33  #include "utils.h"
    34  
    35  void sc_cgroup_create_and_join(const char *parent, const char *name, pid_t pid) {
    36      int parent_fd SC_CLEANUP(sc_cleanup_close) = -1;
    37      parent_fd = open(parent, O_PATH | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
    38      if (parent_fd < 0) {
    39          die("cannot open cgroup hierarchy %s", parent);
    40      }
    41      if (mkdirat(parent_fd, name, 0755) < 0 && errno != EEXIST) {
    42          die("cannot create cgroup hierarchy %s/%s", parent, name);
    43      }
    44      int hierarchy_fd SC_CLEANUP(sc_cleanup_close) = -1;
    45      hierarchy_fd = openat(parent_fd, name, O_PATH | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
    46      if (hierarchy_fd < 0) {
    47          die("cannot open cgroup hierarchy %s/%s", parent, name);
    48      }
    49      // Since we may be running from a setuid but not setgid executable, ensure
    50      // that the group and owner of the hierarchy directory is root.root.
    51      if (fchownat(hierarchy_fd, "", 0, 0, AT_EMPTY_PATH) < 0) {
    52          die("cannot change owner of cgroup hierarchy %s/%s to root.root", parent, name);
    53      }
    54      // Open the tasks file.
    55      int tasks_fd SC_CLEANUP(sc_cleanup_close) = -1;
    56      tasks_fd = openat(hierarchy_fd, "tasks", O_WRONLY | O_NOFOLLOW | O_CLOEXEC);
    57      if (tasks_fd < 0) {
    58          die("cannot open file %s/%s/tasks", parent, name);
    59      }
    60      // Write the process (task) number to the tasks file. Linux task IDs are
    61      // limited to 2^29 so a long int is enough to represent it.
    62      // See include/linux/threads.h in the kernel source tree for details.
    63      char buf[22] = {0};  // 2^64 base10 + 2 for NUL and '-' for long
    64      int n = sc_must_snprintf(buf, sizeof buf, "%ld", (long)pid);
    65      if (write(tasks_fd, buf, n) < n) {
    66          die("cannot move process %ld to cgroup hierarchy %s/%s", (long)pid, parent, name);
    67      }
    68      debug("moved process %ld to cgroup hierarchy %s/%s", (long)pid, parent, name);
    69  }
    70  
    71  static const char *cgroup_dir = "/sys/fs/cgroup";
    72  
    73  // from statfs(2)
    74  #ifndef CGRUOP2_SUPER_MAGIC
    75  #define CGROUP2_SUPER_MAGIC 0x63677270
    76  #endif
    77  
    78  // Detect if we are running in cgroup v2 unified mode (as opposed to
    79  // hybrid or legacy) The algorithm is described in
    80  // https://systemd.io/CGROUP_DELEGATION.html
    81  bool sc_cgroup_is_v2() {
    82      static bool did_warn = false;
    83      struct statfs buf;
    84  
    85      if (statfs(cgroup_dir, &buf) != 0) {
    86          if (errno == ENOENT) {
    87              return false;
    88          }
    89          die("cannot statfs %s", cgroup_dir);
    90      }
    91      if (buf.f_type == CGROUP2_SUPER_MAGIC) {
    92          if (!did_warn) {
    93              fprintf(stderr, "WARNING: cgroup v2 is not fully supported yet, proceeding with partial confinement\n");
    94              did_warn = true;
    95          }
    96          return true;
    97      }
    98      return false;
    99  }