github.com/stackdocker/rkt@v0.10.1-0.20151109095037-1aa827478248/stage1/enter/enter.c (about) 1 // Copyright 2014 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 #define _GNU_SOURCE 16 #include <errno.h> 17 #include <fcntl.h> 18 #include <getopt.h> 19 #include <limits.h> 20 #include <sched.h> 21 #include <signal.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <sys/stat.h> 26 #include <sys/types.h> 27 #include <sys/wait.h> 28 #include <unistd.h> 29 30 #ifdef NO_SETNS_AVAILABLE 31 32 #include <linux/unistd.h> 33 34 static int setns(int fd, int nstype) { 35 return syscall(__NR_setns, fd, nstype); 36 } 37 38 #endif /* NO_SETNS_AVAILABLE */ 39 40 static int errornum; 41 #define exit_if(_cond, _fmt, _args...) \ 42 errornum++; \ 43 if(_cond) { \ 44 fprintf(stderr, _fmt "\n", ##_args); \ 45 exit(errornum); \ 46 } 47 #define pexit_if(_cond, _fmt, _args...) \ 48 exit_if(_cond, _fmt ": %s", ##_args, strerror(errno)) 49 50 static int openpidfd(int pid, char *which) { 51 char path[PATH_MAX]; 52 int fd; 53 exit_if(snprintf(path, sizeof(path), 54 "/proc/%i/%s", pid, which) == sizeof(path), 55 "Path overflow"); 56 pexit_if((fd = open(path, O_RDONLY|O_CLOEXEC)) == -1, 57 "Unable to open \"%s\"", path); 58 return fd; 59 } 60 61 int main(int argc, char *argv[]) 62 { 63 int fd; 64 int pid = 0; 65 char *appname = NULL; 66 pid_t child; 67 int status; 68 int root_fd; 69 70 int c; 71 72 /* The parameters list is specified in 73 * Documentation/devel/stage1-implementors-guide.md */ 74 while (1) { 75 int option_index = 0; 76 static struct option long_options[] = { 77 {"pid", required_argument, 0, 'p' }, 78 {"appname", required_argument, 0, 'a' }, 79 {0, 0, 0, 0 } 80 }; 81 82 c = getopt_long(argc, argv, "p:a:", 83 long_options, &option_index); 84 if (c == -1) 85 break; 86 87 switch (c) { 88 case 'p': 89 pid = atoi(optarg); 90 break; 91 case 'a': 92 appname = optarg; 93 break; 94 case 0: 95 break; 96 case ':': /* missing option argument */ 97 case '?': 98 default: 99 fprintf(stderr, "Usage: %s --pid=1234 " 100 "--appname=name -- cmd [args...]", 101 argv[0]); 102 exit(1); 103 } 104 } 105 106 root_fd = openpidfd(pid, "root"); 107 108 #define ns(_typ, _nam) \ 109 fd = openpidfd(pid, _nam); \ 110 pexit_if(setns(fd, _typ), "Unable to enter " _nam " namespace"); 111 112 #if 0 113 /* TODO(vc): Nspawn isn't employing CLONE_NEWUSER, disabled for now */ 114 ns(CLONE_NEWUSER, "ns/user"); 115 #endif 116 ns(CLONE_NEWIPC, "ns/ipc"); 117 ns(CLONE_NEWUTS, "ns/uts"); 118 ns(CLONE_NEWNET, "ns/net"); 119 ns(CLONE_NEWPID, "ns/pid"); 120 ns(CLONE_NEWNS, "ns/mnt"); 121 122 pexit_if(fchdir(root_fd) < 0, 123 "Unable to chdir to pod root"); 124 pexit_if(chroot(".") < 0, 125 "Unable to chroot"); 126 pexit_if(close(root_fd) == -1, 127 "Unable to close root_fd"); 128 129 /* Fork is required to realize consequence of CLONE_NEWPID */ 130 pexit_if(((child = fork()) == -1), 131 "Unable to fork"); 132 133 /* some stuff make the argv->args copy less cryptic */ 134 #define APPEXEC_ARGV_FWD_OFFSET 6 135 136 if(child == 0) { 137 char root[PATH_MAX]; 138 char env[PATH_MAX]; 139 char *args[APPEXEC_ARGV_FWD_OFFSET + argc - optind + 1 /* NULL terminator */]; 140 int argsind; 141 142 /* Child goes on to execute /appexec */ 143 144 exit_if(snprintf(root, sizeof(root), 145 "/opt/stage2/%s/rootfs", appname) == sizeof(root), 146 "Root path overflow"); 147 148 exit_if(snprintf(env, sizeof(env), 149 "/rkt/env/%s", appname) == sizeof(env), 150 "Env path overflow"); 151 152 args[0] = "/appexec"; 153 args[1] = root; 154 args[2] = "/"; /* TODO(vc): plumb this into app.WorkingDirectory */ 155 args[3] = env; 156 args[4] = "0"; /* uid */ 157 args[5] = "0"; /* gid */ 158 argsind = APPEXEC_ARGV_FWD_OFFSET; 159 while (optind < argc) 160 args[argsind++] = argv[optind++]; 161 162 args[argsind] = NULL; 163 164 pexit_if(execv(args[0], args) == -1, 165 "Exec failed"); 166 } 167 168 /* Wait for child, nsenter-like */ 169 for(;;) { 170 if(waitpid(child, &status, WUNTRACED) == pid && 171 (WIFSTOPPED(status))) { 172 kill(getpid(), SIGSTOP); 173 /* the above stops us, upon receiving SIGCONT we'll 174 * continue here and inform our child */ 175 kill(child, SIGCONT); 176 } else { 177 break; 178 } 179 } 180 181 if(WIFEXITED(status)) { 182 exit(WEXITSTATUS(status)); 183 } else if(WIFSIGNALED(status)) { 184 kill(getpid(), WTERMSIG(status)); 185 } 186 187 return EXIT_FAILURE; 188 }