github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/cmd/snapd-generator/main.c (about)

     1  /*
     2   * Copyright (C) 2018 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  #include <stdio.h>
    19  #include <stdlib.h>
    20  #include <string.h>
    21  #include <sys/stat.h>
    22  #include <dirent.h>
    23  #include <errno.h>
    24  #include <unistd.h>
    25  
    26  #include "config.h"
    27  
    28  #include "../libsnap-confine-private/cleanup-funcs.h"
    29  #include "../libsnap-confine-private/mountinfo.h"
    30  #include "../libsnap-confine-private/string-utils.h"
    31  
    32  static sc_mountinfo_entry *find_root_mountinfo(sc_mountinfo * mounts)
    33  {
    34  	sc_mountinfo_entry *cur, *root = NULL;
    35  	for (cur = sc_first_mountinfo_entry(mounts); cur != NULL;
    36  	     cur = sc_next_mountinfo_entry(cur)) {
    37  		// Look for the mount info entry for the root file-system.
    38  		if (sc_streq("/", cur->mount_dir)) {
    39  			root = cur;
    40  		}
    41  	}
    42  	return root;
    43  }
    44  
    45  int ensure_root_fs_shared(const char *normal_dir)
    46  {
    47  	// Load /proc/self/mountinfo so that we can inspect the root filesystem.
    48  	sc_mountinfo *mounts SC_CLEANUP(sc_cleanup_mountinfo) = NULL;
    49  	mounts = sc_parse_mountinfo(NULL);
    50  	if (!mounts) {
    51  		fprintf(stderr, "cannot open or parse /proc/self/mountinfo\n");
    52  		return 1;
    53  	}
    54  
    55  	sc_mountinfo_entry *root = find_root_mountinfo(mounts);
    56  	if (!root) {
    57  		fprintf(stderr,
    58  			"cannot find mountinfo entry of the root filesystem\n");
    59  		return 1;
    60  	}
    61  	// Check if the root file-system is mounted with shared option.
    62  	if (strstr(root->optional_fields, "shared:") != NULL) {
    63  		// The workaround is not needed, everything is good as-is.
    64  		return 0;
    65  	}
    66  	// Construct the file name for a new systemd mount unit.
    67  	char fname[PATH_MAX + 1] = { 0 };
    68  	sc_must_snprintf(fname, sizeof fname,
    69  			 "%s/" SNAP_MOUNT_DIR_SYSTEMD_UNIT ".mount", normal_dir);
    70  
    71  	// Open the mount unit and write the contents.
    72  	FILE *f SC_CLEANUP(sc_cleanup_file) = NULL;
    73  	f = fopen(fname, "wt");
    74  	if (!f) {
    75  		fprintf(stderr, "cannot open %s: %m\n", fname);
    76  		return 1;
    77  	}
    78  	fprintf(f,
    79  		"# Ensure that snap mount directory is mounted \"shared\" "
    80  		"so snaps can be refreshed correctly (LP: #1668759).\n");
    81  	fprintf(f, "[Unit]\n");
    82  	fprintf(f,
    83  		"Description=Ensure that the snap directory "
    84  		"shares mount events.\n");
    85  	fprintf(f, "[Mount]\n");
    86  	fprintf(f, "What=" SNAP_MOUNT_DIR "\n");
    87  	fprintf(f, "Where=" SNAP_MOUNT_DIR "\n");
    88  	fprintf(f, "Type=none\n");
    89  	fprintf(f, "Options=bind,shared\n");
    90  	fprintf(f, "[Install]\n");
    91  	fprintf(f, "WantedBy=local-fs.target\n");
    92  	return 0;
    93  }
    94  
    95  static bool file_exists(const char *path) {
    96  	struct stat buf;
    97  	// Not using lstat to automatically resolve symbolic links,
    98  	// including handling, as an error, dangling symbolic links.
    99  	return stat(path, &buf) == 0 && (buf.st_mode & S_IFMT) == S_IFREG;
   100  }
   101  
   102  // PATH may not be set (the case on 16.04), in which case this is the fallback
   103  // for looking up squashfuse / snapfuse executable.
   104  // Based on what systemd uses when compiled for systems with "unmerged /usr"
   105  // (see man systemd.exec).
   106  static const char * const path_fallback = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
   107  
   108  static bool executable_exists(const char *name) {
   109  	char *path = getenv("PATH");
   110  	char *path_copy SC_CLEANUP(sc_cleanup_string) = NULL;
   111  	if (path == NULL) {
   112  		path_copy = sc_strdup(path_fallback);
   113  	} else {
   114  		path_copy = sc_strdup(path);
   115  	}
   116  
   117  	char *ptr = NULL;
   118  	char *token = strtok_r(path_copy, ":", &ptr);
   119  	char fname[PATH_MAX + 1] = { 0 };
   120  	while (token) {
   121  		sc_must_snprintf(fname, sizeof fname, "%s/%s", token, name);
   122  		if (access(fname, X_OK) == 0) {
   123  			return true;
   124  		}
   125  		token = strtok_r(NULL, ":", &ptr);
   126  	}
   127  	return false;
   128  }
   129  
   130  int ensure_fusesquashfs_inside_container(const char *normal_dir)
   131  {
   132  	// check if we are running inside a container, systemd
   133  	// provides this file all the way back to trusty if run in a
   134  	// container
   135  	if (!file_exists("/run/systemd/container")) {
   136  		return 0;
   137  	}
   138  
   139  	const char *fstype;
   140  	if (executable_exists("squashfuse")) {
   141  		fstype = "fuse.squashfuse";
   142  	} else if (executable_exists("snapfuse")) {
   143  		fstype = "fuse.snapfuse";
   144  	} else {
   145  		fprintf(stderr,
   146  			"cannot find squashfuse or snapfuse executable\n");
   147  		return 2;
   148  	}
   149  
   150  	DIR *units_dir SC_CLEANUP(sc_cleanup_closedir) = NULL;
   151  	units_dir = opendir("/etc/systemd/system");
   152  	if (units_dir == NULL) {
   153  		// nothing to do
   154  		return 0;
   155  	}
   156  
   157  	char fname[PATH_MAX + 1] = { 0 };
   158  
   159  	struct dirent *ent;
   160  	while (ent = readdir(units_dir)) {
   161  		// find snap mount units, i.e:
   162  		// snap-somename.mount or var-lib-snapd-snap-somename.mount
   163  		if (!sc_endswith(ent->d_name, ".mount")) {
   164  			continue;
   165  		}
   166  		if (!(sc_startswith(ent->d_name, "snap-") || sc_startswith(ent->d_name, "var-lib-snapd-snap-"))) {
   167  			continue;
   168  		}
   169  		sc_must_snprintf(fname, sizeof fname,
   170  			"%s/%s.d", normal_dir, ent->d_name);
   171  		if (mkdir(fname, 0755) != 0) {
   172  			if (errno != EEXIST) {
   173  				fprintf(stderr,
   174  					"cannot create %s directory: %m\n", fname);
   175  				return 2;
   176  			}
   177  		}
   178  
   179  		sc_must_snprintf(fname, sizeof fname,
   180  			"%s/%s.d/container.conf", normal_dir, ent->d_name);
   181  
   182  		FILE *f SC_CLEANUP(sc_cleanup_file) = NULL;
   183  		f = fopen(fname, "w");
   184  		if (!f) {
   185  			fprintf(stderr, "cannot open %s: %m\n", fname);
   186  			return 2;
   187  		}
   188  		fprintf(f, "[Mount]\nType=%s\n", fstype);
   189  	}
   190  	
   191  	return 0;
   192  }
   193  
   194  int main(int argc, char **argv)
   195  {
   196  	if (argc != 4) {
   197  		printf("usage: snapd-generator normal-dir early-dir late-dir\n");
   198  		return 1;
   199  	}
   200  	const char *normal_dir = argv[1];
   201  	// For reference, but we don't use those variables here.
   202  	// const char *early_dir = argv[2];
   203  	// const char *late_dir = argv[3];
   204  
   205  	int status = 0;
   206  	status = ensure_root_fs_shared(normal_dir);
   207  	status |= ensure_fusesquashfs_inside_container(normal_dir);
   208  
   209  	return status;
   210  }