ccx-utils

Miscellaneous utilities written in C
git clone https://ccx.te2000.cz/git/ccx-utils
Log | Files | Refs

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 }