github.com/blixtra/rkt@v0.8.1-0.20160204105720-ab0d1add1a43/stage1/prepare-app/prepare-app.c (about) 1 // Copyright 2015 The rkt Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include <errno.h> 16 #include <string.h> 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <sys/mount.h> 20 #include <sys/stat.h> 21 #include <sys/types.h> 22 #include <unistd.h> 23 #include <fcntl.h> 24 25 #define err_out(_fmt, _args...) \ 26 fprintf(stderr, "Error: " _fmt "\n", ##_args); 27 static int exit_err; 28 #define exit_if(_cond, _fmt, _args...) \ 29 exit_err++; \ 30 if(_cond) { \ 31 err_out(_fmt, ##_args); \ 32 exit(exit_err); \ 33 } 34 #define pexit_if(_cond, _fmt, _args...) \ 35 exit_if(_cond, _fmt ": %s", ##_args, strerror(errno)) 36 37 #define goto_if(_cond, _lbl, _fmt, _args...) \ 38 if(_cond) { \ 39 err_out(_fmt, ##_args); \ 40 goto _lbl; \ 41 } 42 #define pgoto_if(_cond, _lbl, _fmt, _args...) \ 43 goto_if(_cond, _lbl, _fmt ": %s", ##_args, strerror(errno)); 44 45 #define nelems(_array) \ 46 (sizeof(_array) / sizeof(_array[0])) 47 #define lenof(_str) \ 48 (sizeof(_str) - 1) 49 50 #define MACHINE_ID_LEN lenof("0123456789abcdef0123456789ab") 51 #define MACHINE_NAME_LEN lenof("rkt-01234567-89ab-cdef-0123-456789ab") 52 53 typedef struct _dir_op_t { 54 const char *name; 55 mode_t mode; 56 } dir_op_t; 57 58 typedef struct _mount_point_t { 59 const char *source; 60 const char *target; 61 const char *type; 62 const char *options; 63 unsigned long flags; 64 } mount_point; 65 66 #define dir(_name, _mode) \ 67 { .name = _name, .mode = _mode } 68 69 static int get_machine_name(char *out, int out_len) { 70 int fd; 71 char buf[MACHINE_ID_LEN + 1]; 72 73 pgoto_if((fd = open("/etc/machine-id", O_RDONLY)) == -1, 74 _fail, "Error opening \"/etc/machine-id\""); 75 pgoto_if(read(fd, buf, MACHINE_ID_LEN) == -1, 76 _fail_fd, "Error reading \"/etc/machine-id\""); 77 pgoto_if(close(fd) != 0, 78 _fail, "Error closing \"/etc/machine-id\""); 79 goto_if(snprintf(out, out_len, 80 "rkt-%.8s-%.4s-%.4s-%.4s-%.8s", 81 buf, buf+8, buf+12, buf+16, buf+20) >= out_len, 82 _fail, "Error constructing machine name"); 83 84 return 1; 85 86 _fail_fd: 87 close(fd); 88 _fail: 89 return 0; 90 } 91 92 static int ensure_etc_hosts_exists(const char *root, int rootfd) { 93 char name[MACHINE_NAME_LEN + 1]; 94 char hosts[128]; 95 int fd, len; 96 97 if(faccessat(rootfd, "etc/hosts", F_OK, AT_EACCESS) == 0) 98 return 1; 99 100 goto_if(!get_machine_name(name, sizeof(name)), 101 _fail, "Failed to get machine name"); 102 goto_if((len = snprintf(hosts, sizeof(hosts), 103 "%s\t%s\t%s\t%s\n", 104 "127.0.0.1", name, 105 "localhost", "localhost.localdomain")) >= sizeof(hosts), 106 _fail, "/etc/hosts line too long: \"%s\"", hosts); 107 pgoto_if((fd = openat(rootfd, "etc/hosts", O_WRONLY|O_CREAT, 0644)) == -1, 108 _fail, "Failed to create \"%s/etc/hosts\"", root); 109 pgoto_if(write(fd, hosts, len) != len, 110 _fail_fd, "Failed to write \"%s/etc/hosts\"", root); 111 pgoto_if(close(fd) != 0, 112 _fail, "Failed to close \"%s/etc/hosts\"", root); 113 114 return 1; 115 116 _fail_fd: 117 close(fd); 118 _fail: 119 return 0; 120 } 121 122 int main(int argc, char *argv[]) 123 { 124 static const char *unlink_paths[] = { 125 "dev/shm", 126 "dev/ptmx", 127 NULL 128 }; 129 static const dir_op_t dirs[] = { 130 dir("dev", 0755), 131 dir("dev/net", 0755), 132 dir("dev/shm", 0755), 133 dir("etc", 0755), 134 dir("proc", 0755), 135 dir("sys", 0755), 136 dir("tmp", 01777), 137 dir("dev/pts", 0755), 138 }; 139 static const char *devnodes[] = { 140 "/dev/null", 141 "/dev/zero", 142 "/dev/full", 143 "/dev/random", 144 "/dev/urandom", 145 "/dev/tty", 146 "/dev/net/tun", 147 "/dev/console", 148 NULL 149 }; 150 static const mount_point dirs_mount_table[] = { 151 { "/proc", "/proc", "bind", NULL, MS_BIND|MS_REC }, 152 { "/sys", "/sys", "bind", NULL, MS_BIND|MS_REC }, 153 { "/dev/shm", "/dev/shm", "bind", NULL, MS_BIND }, 154 { "/dev/pts", "/dev/pts", "bind", NULL, MS_BIND }, 155 }; 156 static const mount_point files_mount_table[] = { 157 { "/etc/rkt-resolv.conf", "/etc/resolv.conf", "bind", NULL, MS_BIND }, 158 }; 159 const char *root; 160 int rootfd; 161 char to[4096]; 162 int i; 163 164 exit_if(argc < 2, 165 "Usage: %s /path/to/root", argv[0]); 166 167 root = argv[1]; 168 169 /* Make stage2's root a mount point. Chrooting an application in a 170 * directory which is not a mount point is not nice because the 171 * application would not be able to remount "/" it as private mount. 172 * This allows Docker to run inside rkt. 173 * The recursive flag is to preserve volumes mounted previously by 174 * systemd-nspawn via "rkt run -volume". 175 * */ 176 pexit_if(mount(root, root, "bind", MS_BIND | MS_REC, NULL) == -1, 177 "Make / a mount point failed"); 178 179 rootfd = open(root, O_DIRECTORY | O_CLOEXEC); 180 pexit_if(rootfd < 0, 181 "Failed to open directory \"%s\"", root); 182 183 /* Some images have annoying symlinks that are resolved as dangling 184 * links before the chroot in stage1. E.g. "/dev/shm" -> "/run/shm" 185 * Just remove the symlinks. 186 */ 187 for (i = 0; unlink_paths[i]; i++) { 188 pexit_if(unlinkat(rootfd, unlink_paths[i], 0) != 0 189 && errno != ENOENT && errno != EISDIR, 190 "Failed to unlink \"%s\"", unlink_paths[i]) 191 } 192 193 /* Create the directories */ 194 umask(0); 195 for (i = 0; i < nelems(dirs); i++) { 196 const dir_op_t *d = &dirs[i]; 197 pexit_if(mkdirat(rootfd, d->name, d->mode) == -1 && 198 errno != EEXIST, 199 "Failed to create directory \"%s/%s\"", root, d->name); 200 } 201 202 exit_if(!ensure_etc_hosts_exists(root, rootfd), 203 "Failed to ensure \"%s/etc/hosts\" exists", root); 204 205 close(rootfd); 206 207 /* systemd-nspawn already creates few /dev entries in the container 208 * namespace: copy_devnodes() 209 * http://cgit.freedesktop.org/systemd/systemd/tree/src/nspawn/nspawn.c?h=v219#n1345 210 * 211 * But they are not visible by the apps because they are "protected" by 212 * the chroot. 213 * 214 * Bind mount them individually over the chroot border. 215 * 216 * Do NOT bind mount the whole directory /dev because it would shadow 217 * potential individual bind mount by stage0 ("rkt run --volume..."). 218 * 219 * Do NOT use mknod, it would not work for /dev/console because it is 220 * a bind mount to a pts and pts device nodes only work when they live 221 * on a devpts filesystem. 222 */ 223 for (i = 0; devnodes[i]; i++) { 224 const char *from = devnodes[i]; 225 int fd; 226 227 /* If the file does not exist, skip it. It might be because 228 * the kernel does not provide it (e.g. kernel compiled without 229 * CONFIG_TUN) or because systemd-nspawn does not provide it 230 * (/dev/net/tun is not available with systemd-nspawn < v217 231 */ 232 if (access(from, F_OK) != 0) 233 continue; 234 235 exit_if(snprintf(to, sizeof(to), "%s%s", root, from) >= sizeof(to), 236 "Path too long: \"%s\"", to); 237 238 /* The mode does not matter: it will be bind-mounted over. 239 */ 240 fd = open(to, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0644); 241 if (fd != -1) 242 close(fd); 243 244 pexit_if(mount(from, to, "bind", MS_BIND, NULL) == -1, 245 "Mounting \"%s\" on \"%s\" failed", from, to); 246 } 247 248 /* Bind mount directories */ 249 for (i = 0; i < nelems(dirs_mount_table); i++) { 250 const mount_point *mnt = &dirs_mount_table[i]; 251 252 exit_if(snprintf(to, sizeof(to), "%s/%s", root, mnt->target) >= sizeof(to), 253 "Path too long: \"%s\"", to); 254 pexit_if(mount(mnt->source, to, mnt->type, 255 mnt->flags, mnt->options) == -1, 256 "Mounting \"%s\" on \"%s\" failed", mnt->source, to); 257 } 258 259 /* Bind mount files, if the source exists */ 260 for (i = 0; i < nelems(files_mount_table); i++) { 261 const mount_point *mnt = &files_mount_table[i]; 262 int fd; 263 264 exit_if(snprintf(to, sizeof(to), "%s/%s", root, mnt->target) >= sizeof(to), 265 "Path too long: \"%s\"", to); 266 if (access(mnt->source, F_OK) != 0) 267 continue; 268 if (access(to, F_OK) != 0) { 269 pexit_if((fd = creat(to, 0644)) == -1, 270 "Cannot create file: \"%s\"", to); 271 pexit_if(close(fd) == -1, 272 "Cannot close file: \"%s\"", to); 273 } 274 pexit_if(mount(mnt->source, to, mnt->type, 275 mnt->flags, mnt->options) == -1, 276 "Mounting \"%s\" on \"%s\" failed", mnt->source, to); 277 } 278 279 /* /dev/ptmx -> /dev/pts/ptmx */ 280 exit_if(snprintf(to, sizeof(to), "%s/dev/ptmx", root) >= sizeof(to), 281 "Path too long: \"%s\"", to); 282 pexit_if(symlink("/dev/pts/ptmx", to) == -1, 283 "Failed to create /dev/ptmx symlink"); 284 285 return EXIT_SUCCESS; 286 }