github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/cmd/libsnap-confine-private/cgroup-freezer-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-freezer-support.h" 22 23 #include <errno.h> 24 #include <fcntl.h> 25 #include <limits.h> 26 #include <string.h> 27 #include <sys/stat.h> 28 #include <sys/types.h> 29 #include <unistd.h> 30 31 #include "cgroup-support.h" 32 #include "cleanup-funcs.h" 33 #include "string-utils.h" 34 #include "utils.h" 35 36 static const char *freezer_cgroup_dir = "/sys/fs/cgroup/freezer"; 37 38 void sc_cgroup_freezer_join(const char *snap_name, pid_t pid) 39 { 40 char buf[PATH_MAX] = { 0 }; 41 sc_must_snprintf(buf, sizeof buf, "snap.%s", snap_name); 42 sc_cgroup_create_and_join(freezer_cgroup_dir, buf, pid); 43 } 44 45 bool sc_cgroup_freezer_occupied(const char *snap_name) 46 { 47 // Format the name of the cgroup hierarchy. 48 char buf[PATH_MAX] = { 0 }; 49 sc_must_snprintf(buf, sizeof buf, "snap.%s", snap_name); 50 51 // Open the freezer cgroup directory. 52 int cgroup_fd SC_CLEANUP(sc_cleanup_close) = -1; 53 cgroup_fd = open(freezer_cgroup_dir, 54 O_PATH | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); 55 if (cgroup_fd < 0) { 56 die("cannot open freezer cgroup (%s)", freezer_cgroup_dir); 57 } 58 // Open the proc directory. 59 int proc_fd SC_CLEANUP(sc_cleanup_close) = -1; 60 proc_fd = open("/proc", O_PATH | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); 61 if (proc_fd < 0) { 62 die("cannot open /proc"); 63 } 64 // Open the hierarchy directory for the given snap. 65 int hierarchy_fd SC_CLEANUP(sc_cleanup_close) = -1; 66 hierarchy_fd = openat(cgroup_fd, buf, 67 O_PATH | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); 68 if (hierarchy_fd < 0) { 69 if (errno == ENOENT) { 70 return false; 71 } 72 die("cannot open freezer cgroup hierarchy for snap %s", 73 snap_name); 74 } 75 // Open the "cgroup.procs" file. Alternatively we could open the "tasks" 76 // file and see per-thread data but we don't need that. 77 int cgroup_procs_fd SC_CLEANUP(sc_cleanup_close) = -1; 78 cgroup_procs_fd = openat(hierarchy_fd, "cgroup.procs", 79 O_RDONLY | O_NOFOLLOW | O_CLOEXEC); 80 if (cgroup_procs_fd < 0) { 81 die("cannot open cgroup.procs file for freezer cgroup hierarchy for snap %s", snap_name); 82 } 83 84 FILE *cgroup_procs SC_CLEANUP(sc_cleanup_file) = NULL; 85 cgroup_procs = fdopen(cgroup_procs_fd, "r"); 86 if (cgroup_procs == NULL) { 87 die("cannot convert cgroups.procs file descriptor to FILE"); 88 } 89 cgroup_procs_fd = -1; // cgroup_procs_fd will now be closed by fclose. 90 91 char *line_buf SC_CLEANUP(sc_cleanup_string) = NULL; 92 size_t line_buf_size = 0; 93 ssize_t num_read; 94 struct stat statbuf; 95 for (;;) { 96 num_read = getline(&line_buf, &line_buf_size, cgroup_procs); 97 if (num_read < 0 && errno != 0) { 98 die("cannot read next PID belonging to snap %s", 99 snap_name); 100 } 101 if (num_read <= 0) { 102 break; 103 } else { 104 if (line_buf[num_read - 1] == '\n') { 105 line_buf[num_read - 1] = '\0'; 106 } else { 107 die("could not find newline in cgroup.procs"); 108 } 109 } 110 debug("found process id: %s\n", line_buf); 111 112 if (fstatat(proc_fd, line_buf, &statbuf, AT_SYMLINK_NOFOLLOW) < 113 0) { 114 // The process may have died already. 115 if (errno != ENOENT) { 116 die("cannot stat /proc/%s", line_buf); 117 } 118 } 119 debug("found process %s belonging to user %d", 120 line_buf, statbuf.st_uid); 121 return true; 122 } 123 124 return false; 125 }