| // vsockexec opens vsock connections for the specified stdio descriptors and |
| // then execs the specified process. |
| |
| #include <errno.h> |
| #include <sys/socket.h> |
| #include <linux/vm_sockets.h> |
| #include <netinet/in.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #ifdef USE_TCP |
| static const int tcpmode = 1; |
| #else |
| static const int tcpmode; |
| #endif |
| |
| static int openvsock(unsigned int cid, unsigned int port) |
| { |
| int s = socket(AF_VSOCK, SOCK_STREAM, 0); |
| if (s < 0) { |
| perror("socket: AF_VSOCK"); |
| return -1; |
| } |
| |
| struct sockaddr_vm addr = {0}; |
| addr.svm_family = AF_VSOCK; |
| addr.svm_port = port; |
| addr.svm_cid = cid; |
| if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { |
| fprintf(stderr, "connect: port %u: %s", port, strerror(errno)); |
| return -1; |
| } |
| |
| return s; |
| } |
| |
| static int opentcp(unsigned short port) |
| { |
| int s = socket(AF_INET, SOCK_STREAM, 0); |
| if (s < 0) { |
| perror("socket: AF_INET"); |
| return -1; |
| } |
| |
| struct sockaddr_in addr = {0}; |
| addr.sin_family = AF_INET; |
| addr.sin_port = htons(port); |
| addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
| if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { |
| fprintf(stderr, "connect: port %u: %s\n", port, strerror(errno)); |
| return -1; |
| } |
| |
| return s; |
| } |
| |
| _Noreturn static void usage(const char *argv0) |
| { |
| fprintf(stderr, "%s [-i port] [-o port] [-e port] -- program [args...]\n", argv0); |
| exit(1); |
| } |
| |
| int main(int argc, char **argv) |
| { |
| unsigned int ports[3] = {0}; |
| int sockets[3] = {-1, -1, -1}; |
| int c; |
| while ((c = getopt(argc, argv, "+i:o:e:")) != -1) { |
| switch (c) { |
| case 'i': |
| ports[0] = strtoul(optarg, NULL, 10); |
| break; |
| |
| case 'o': |
| ports[1] = strtoul(optarg, NULL, 10); |
| break; |
| |
| case 'e': |
| ports[2] = strtoul(optarg, NULL, 10); |
| break; |
| |
| default: |
| usage(argv[0]); |
| } |
| } |
| |
| if (optind == argc) { |
| fprintf(stderr, "%s: missing program argument\n", argv[0]); |
| usage(argv[0]); |
| } |
| |
| for (int i = 0; i < 3; i++) { |
| if (ports[i] != 0) { |
| int j; |
| for (j = 0; j < i; j++) { |
| if (ports[i] == ports[j]) { |
| int s = dup(sockets[j]); |
| if (s < 0) { |
| perror("dup"); |
| return 1; |
| } |
| sockets[i] = s; |
| break; |
| } |
| } |
| |
| if (j == i) { |
| int s = tcpmode ? opentcp(ports[i]) : openvsock(VMADDR_CID_HOST, ports[i]); |
| if (s < 0) { |
| return 1; |
| } |
| sockets[i] = s; |
| } |
| } |
| } |
| |
| for (int i = 0; i < 3; i++) { |
| if (sockets[i] >= 0) { |
| dup2(sockets[i], i); |
| close(sockets[i]); |
| } |
| } |
| |
| execvp(argv[optind], argv + optind); |
| fprintf(stderr, "execvp: %s: %s\n", argv[optind], strerror(errno)); |
| return 1; |
| } |