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 }