pidns_run.c (3135B)
1 #include <fcntl.h> 2 #include <errno.h> 3 #include <sched.h> /* Definition of CLONE_* constants & unshare */ 4 #include <unistd.h> /* fork(), getpid() */ 5 #include <sys/wait.h> 6 #include <sys/select.h> 7 #include <assert.h> 8 #include <stdlib.h> /* exit() */ 9 10 #include <skalibs/exec.h> 11 #include <skalibs/djbunix.h> 12 #include <skalibs/strerr.h> 13 14 #define PROG "pidns_run" 15 16 void fd_cloexec(int fd) { 17 int flags = fcntl(fd, F_GETFD); 18 if(flags == -1) { 19 strerr_dief1sys(111, "fcntl() getfd"); 20 } 21 if(fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) { 22 strerr_dief1sys(111, "fcntl() setfd"); 23 } 24 } 25 26 void fd_nonblock(int fd) { 27 int flags = fcntl(fd, F_GETFL); 28 if(flags == -1) { 29 strerr_dief1sys(111, "fcntl() getfd"); 30 } 31 if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { 32 strerr_dief1sys(111, "fcntl() setfd"); 33 } 34 } 35 36 void nonblock_cloexec(int fd) { 37 fd_cloexec(fd); 38 fd_nonblock(fd); 39 } 40 41 int main(const int argc, const char **argv) { 42 int piperw[2]; 43 #define parent_rfd piperw[0] 44 #define parent_wfd piperw[1] 45 /* returns EINVAL for some reason 46 if(pipe2(piperw, O_NONBLOCK | FD_CLOEXEC) != 0) { 47 strerr_dief1sys(111, "pipe2()"); 48 } 49 */ 50 if(pipe(piperw) != 0) { 51 strerr_dief1sys(111, "pipe()"); 52 } 53 nonblock_cloexec(parent_rfd); 54 nonblock_cloexec(parent_wfd); 55 if(unshare(CLONE_NEWPID) != 0) { 56 strerr_dief1sys(111, "unshare()"); 57 } 58 int fork1_pid = fork(); 59 if(fork1_pid < 0) { 60 strerr_dief1sys(111, "first fork()"); 61 } 62 if(fork1_pid == 0) { 63 /* child */ 64 assert(getpid() == 1); 65 if(close(parent_wfd) != 0) { 66 strerr_dief1sys(111, "close(parent_wfd)"); 67 } 68 int fork2_pid = fork(); 69 if(fork2_pid < 0) { 70 strerr_dief1sys(111, "second fork()"); 71 } 72 if(fork2_pid == 0) { 73 /* child */ 74 exec(&argv[1]); 75 } else { 76 /* parent */ 77 fd_set rfds; 78 struct timeval tv = {1, 0}; 79 int retval, wstatus; 80 pid_t pid; 81 FD_ZERO(&rfds); 82 FD_SET(parent_rfd, &rfds); 83 while(1) { 84 pid = waitpid(0, &wstatus, WNOHANG); 85 if(pid == fork2_pid) { 86 exit(wait_estatus(wstatus)); 87 } 88 retval = select(1, &rfds, NULL, NULL, &tv); 89 if (retval == -1 && errno != EINTR) { 90 strerr_dief1sys(111, "select()"); 91 } 92 if(retval > 0) { 93 const char term_msg[] = "pidns_run: parent died, terminating\n"; 94 write(2, "pidns_run: parent died, terminating\n", sizeof(term_msg)); 95 exit(111); 96 } 97 tv.tv_sec = 1; 98 tv.tv_usec = 0; 99 } 100 } 101 } else { 102 /* parent */ 103 if(close(parent_rfd) != 0) { 104 strerr_dief1sys(111, "close(parent_rfd)"); 105 } 106 int wstatus; 107 pid_t pid = waitpid(fork1_pid, &wstatus, 0); 108 exit(wait_estatus(wstatus)); 109 } 110 }