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