github.com/schwarzm/garden-linux@v0.0.0-20150507151835-33bca2147c47/old/linux_backend/src/wsh/wsh.c (about) 1 #define _GNU_SOURCE 2 3 #include <assert.h> 4 #include <errno.h> 5 #include <fcntl.h> 6 #include <signal.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <sys/ioctl.h> 11 #include <termios.h> 12 #include <unistd.h> 13 14 #include "msg.h" 15 #include "pump.h" 16 #include "un.h" 17 18 typedef struct wsh_s wsh_t; 19 20 struct wsh_s { 21 /* Path and args to execute */ 22 int argc; 23 char **argv; 24 25 /* Environment variables for running process */ 26 char **environment_variables; 27 size_t environment_variable_count; 28 29 /* Path to socket */ 30 const char *socket_path; 31 32 /* User to change to */ 33 const char *user; 34 35 /* Working directory of process */ 36 const char *dir; 37 38 /* File to save container-namespaced pid of spawned process in to */ 39 const char *pid_file; 40 }; 41 42 int wsh__usage(wsh_t *w) { 43 fprintf(stderr, "Usage: %s OPTION...\n", w->argv[0]); 44 fprintf(stderr, "\n"); 45 46 fprintf(stderr, " --socket PATH " 47 "Path to socket" 48 "\n"); 49 50 fprintf(stderr, " --user USER " 51 "User to change to" 52 "\n"); 53 54 fprintf(stderr, " --env KEY=VALUE " 55 "Environment variables to set for the command. " 56 "You can specify multiple --env arguments" 57 "\n"); 58 59 fprintf(stderr, " --dir PATH " 60 "Working directory for the running process" 61 "\n"); 62 63 fprintf(stderr, " --pidfile PIDFILE " 64 "File to save container-namespaced pid of spawned process to" 65 "\n"); 66 67 fprintf(stderr, " --rsh " 68 "RSH compatibility mode" 69 "\n"); 70 return 0; 71 } 72 73 int wsh__getopt(wsh_t *w) { 74 int i = 1; 75 int j = w->argc - i; 76 77 w->pid_file = 0; 78 79 while (i < w->argc) { 80 if (w->argv[i][0] != '-') { 81 break; 82 } 83 84 if (j >= 1 && ((strcmp(w->argv[i], "-h") == 0) || (strcmp(w->argv[i], "--help") == 0))) { 85 wsh__usage(w); 86 return -1; 87 } else if (j >= 2 && strcmp(w->argv[i], "--socket") == 0) { 88 w->socket_path = strdup(w->argv[i+1]); 89 i += 2; 90 j -= 2; 91 } else if (j >= 2 && strcmp(w->argv[i], "--user") == 0) { 92 w->user = strdup(w->argv[i+1]); 93 i += 2; 94 j -= 2; 95 } else if (j >= 2 && strcmp(w->argv[i], "--dir") == 0) { 96 w->dir = strdup(w->argv[i+1]); 97 i += 2; 98 j -= 2; 99 } else if (j >= 2 && strcmp(w->argv[i], "--pidfile") == 0) { 100 w->pid_file = strdup(w->argv[i+1]); 101 i += 2; 102 j -= 2; 103 } else if (j >= 2 && strcmp(w->argv[i], "--env") == 0) { 104 w->environment_variable_count++; 105 w->environment_variables = realloc(w->environment_variables, w->environment_variable_count * sizeof(char *)); 106 w->environment_variables[w->environment_variable_count - 1] = strdup(w->argv[i+1]); 107 i += 2; 108 j -= 2; 109 } else if (j >= 1 && strcmp(w->argv[i], "--rsh") == 0) { 110 i += 1; 111 j -= 1; 112 113 /* rsh [-46dn] [-l username] [-t timeout] host [command] */ 114 while (i < w->argc) { 115 if (w->argv[i][0] != '-') { 116 break; 117 } 118 119 if (j >= 1 && strlen(w->argv[i]) == 2 && strchr("46dn", w->argv[i][1])) { 120 /* Ignore */ 121 i += 1; 122 j -= 1; 123 } else if (j >= 2 && strlen(w->argv[i]) == 2 && w->argv[i][1] == 'l') { 124 w->user = strdup(w->argv[i+1]); 125 i += 2; 126 j -= 2; 127 } else if (j >= 2 && strlen(w->argv[i]) == 2 && w->argv[i][1] == 't') { 128 /* Ignore */ 129 i += 2; 130 j -= 2; 131 } else { 132 goto invalid; 133 } 134 } 135 136 /* Skip over host */ 137 assert(i < w->argc); 138 i += 1; 139 j -= 1; 140 } else { 141 goto invalid; 142 } 143 } 144 145 w->argc = w->argc - i; 146 if (w->argc) { 147 w->argv = &w->argv[i]; 148 } else { 149 w->argv = NULL; 150 } 151 152 return 0; 153 154 invalid: 155 fprintf(stderr, "%s: invalid option -- %s\n", w->argv[0], w->argv[i]); 156 fprintf(stderr, "Try `%s --help' for more information.\n", w->argv[0]); 157 return -1; 158 } 159 160 static pid_t pid; 161 162 void cleanup_pidfile(const char *pidfile, int pid_fd) { 163 int rv; 164 165 if (pidfile) { 166 rv = unlink(pidfile); 167 if (rv != 0) { 168 perror("unlink pidfile"); 169 exit(255); 170 } 171 } 172 } 173 174 void pump_loop(const char *pid_file, pump_t *p, int pid_fd, int exit_status_fd, pump_pair_t *pp, int pplen) { 175 int i, rv, pidfd; 176 char pidstr[10]; 177 178 rv = read(pid_fd, &pid, sizeof(pid)); 179 assert(rv >= 0); 180 181 if (pid_file) { 182 pidfd = open(pid_file, O_RDWR|O_CREAT, 0600); 183 if (pidfd == -1 ) { 184 perror("open pidfile"); 185 exit(1); 186 } 187 188 sprintf(pidstr, "%d\n", pid); 189 write(pidfd, pidstr, strlen(pidstr)); 190 191 rv = close(pid_fd); 192 if (rv != 0) { 193 perror("close pidfile"); 194 } 195 } 196 197 for (;;) { 198 pump_init(p); 199 200 for (i = 0; i < pplen; i++) { 201 pump_add_pair(p, &pp[i]); 202 } 203 204 if (exit_status_fd >= 0) { 205 pump_add_fd(p, exit_status_fd, PUMP_READ | PUMP_EXCEPT); 206 } 207 208 do { 209 rv = pump_select(p); 210 } while (rv == -1 && errno == EINTR); 211 212 if (rv == -1) { 213 perror("select"); 214 cleanup_pidfile(pid_file, pidfd); 215 abort(); 216 } 217 218 for (i = 0; i < pplen; i++) { 219 pump_pair_copy(&pp[i]); 220 } 221 222 if (pump_ready(p, exit_status_fd, PUMP_READ | PUMP_EXCEPT)) { 223 int status; 224 225 rv = read(exit_status_fd, &status, sizeof(status)); 226 assert(rv >= 0); 227 228 /* One more splice to make sure kernel buffers are emptied */ 229 for (i = 0; i < pplen; i++) { 230 pump_pair_copy(&pp[i]); 231 } 232 233 if (rv == 0) { 234 /* EOF: process terminated by signal */ 235 cleanup_pidfile(pid_file, pidfd); 236 exit(255); 237 } 238 239 assert(rv == sizeof(status)); 240 cleanup_pidfile(pid_file, pidfd); 241 exit(status); 242 } 243 } 244 } 245 246 static int pty_local_fd, pty_remote_fd; 247 static struct termios told, tnew; 248 static struct winsize wsz; 249 250 void tty_reset(void) { 251 int rv; 252 253 rv = tcsetattr(pty_local_fd, TCSANOW, &told); 254 assert(rv != -1); 255 } 256 257 void tty__atexit(void) { 258 tty_reset(); 259 } 260 261 void tty_raw(void) { 262 int rv; 263 264 rv = tcgetattr(pty_local_fd, &told); 265 assert(rv != -1); 266 267 rv = atexit(tty__atexit); 268 assert(rv != -1); 269 270 tnew = told; 271 cfmakeraw(&tnew); 272 273 rv = tcsetattr(pty_local_fd, TCSANOW, &tnew); 274 assert(rv != -1); 275 } 276 277 void tty_gwinsz(void) { 278 int rv; 279 280 rv = ioctl(pty_local_fd, TIOCGWINSZ, &wsz); 281 assert(rv != -1); 282 } 283 284 void tty_swinsz(void) { 285 int rv; 286 287 rv = ioctl(pty_remote_fd, TIOCSWINSZ, &wsz); 288 assert(rv != -1); 289 } 290 291 void tty__sigwinch(int sig) { 292 tty_gwinsz(); 293 tty_swinsz(); 294 } 295 296 void tty_winsz(void) { 297 sighandler_t s; 298 299 /* Setup handler for window size */ 300 s = signal(SIGWINCH, tty__sigwinch); 301 assert(s != SIG_ERR); 302 303 /* Figure out window size and forward it to the remote pty */ 304 tty_gwinsz(); 305 tty_swinsz(); 306 } 307 308 void loop_interactive(const char *pidfile, int fd) { 309 msg_response_t res; 310 int fds[3]; 311 size_t fdslen = sizeof(fds)/sizeof(fds[0]); 312 int rv; 313 314 rv = un_recv_fds(fd, (char *)&res, sizeof(res), fds, fdslen); 315 if (rv <= 0) { 316 perror("recvmsg"); 317 exit(255); 318 } 319 320 assert(rv == sizeof(res)); 321 322 pty_remote_fd = fds[0]; 323 pty_local_fd = STDIN_FILENO; 324 325 tty_raw(); 326 tty_winsz(); 327 328 pump_t p; 329 pump_pair_t pp[2]; 330 331 /* Use duplicates to decouple input/output */ 332 pump_pair_init(&pp[0], &p, STDIN_FILENO, dup(fds[0])); 333 pump_pair_init(&pp[1], &p, dup(fds[0]), STDOUT_FILENO); 334 335 pump_loop(pidfile, &p, fds[2], fds[1], pp, 2); 336 } 337 338 void loop_noninteractive(const char* pidfile, int fd) { 339 msg_response_t res; 340 int fds[5]; 341 size_t fdslen = sizeof(fds)/sizeof(fds[0]); 342 int rv; 343 344 rv = un_recv_fds(fd, (char *)&res, sizeof(res), fds, fdslen); 345 if (rv <= 0) { 346 perror("recvmsg"); 347 exit(255); 348 } 349 350 assert(rv == sizeof(res)); 351 352 pump_t p; 353 pump_pair_t pp[3]; 354 355 pump_pair_init(&pp[0], &p, STDIN_FILENO, fds[0]); 356 pump_pair_init(&pp[1], &p, fds[1], STDOUT_FILENO); 357 pump_pair_init(&pp[2], &p, fds[2], STDERR_FILENO); 358 359 pump_loop(pidfile, &p, fds[4], fds[3], pp, 3); 360 } 361 362 int main(int argc, char **argv) { 363 wsh_t *w; 364 int rv; 365 int fd; 366 msg_request_t req; 367 368 signal(SIGPIPE, SIG_IGN); 369 370 w = calloc(1, sizeof(*w)); 371 assert(w != NULL); 372 373 w->argc = argc; 374 w->argv = argv; 375 376 rv = wsh__getopt(w); 377 if (rv == -1) { 378 exit(1); 379 } 380 381 if (w->socket_path == NULL) { 382 w->socket_path = "run/wshd.sock"; 383 } 384 385 rv = un_connect(w->socket_path); 386 if (rv < 0) { 387 perror("connect"); 388 exit(255); 389 } 390 391 fd = rv; 392 393 msg_request_init(&req); 394 395 msg_dir_import(&req.dir, w->dir); 396 397 if (isatty(STDIN_FILENO)) { 398 req.tty = 1; 399 } else { 400 req.tty = 0; 401 } 402 403 rv = msg_array_import(&req.arg, w->argc, (const char **)w->argv); 404 if (rv == -1) { 405 fprintf(stderr, "msg_import_array: Too much data in args\n"); 406 exit(255); 407 } 408 409 rv = msg_array_import(&req.env, w->environment_variable_count, (const char **)w->environment_variables); 410 if (rv == -1) { 411 fprintf(stderr, "msg_import_array: Too much data in environment variables\n"); 412 exit(255); 413 } 414 415 rv = msg_rlimit_import(&req.rlim); 416 if (rv == -1) { 417 fprintf(stderr, "msg_rlimit_import: %s\n", strerror(errno)); 418 exit(255); 419 } 420 421 rv = msg_user_import(&req.user, w->user); 422 if (rv == -1) { 423 fprintf(stderr, "msg_user_import: %s\n", strerror(errno)); 424 exit(255); 425 } 426 427 rv = un_send_fds(fd, (char *)&req, sizeof(req), NULL, 0); 428 if (rv <= 0) { 429 perror("sendmsg"); 430 exit(255); 431 } 432 433 if (req.tty) { 434 loop_interactive(w->pid_file, fd); 435 } else { 436 loop_noninteractive(w->pid_file, fd); 437 } 438 439 perror("unreachable"); 440 exit(255); 441 }