github.com/rigado/snapd@v2.42.5-go-mod+incompatible/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 tasks 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  	do {
    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  	} while (num_read > 0);
   123  
   124  	return false;
   125  }