s6

Mirror/fork of https://skarnet.org/software/s6/
git clone https://ccx.te2000.cz/git/s6
Log | Files | Refs | README | LICENSE

s6-ioconnect.c (6131B)


      1 /* ISC license. */
      2 
      3 #include <errno.h>
      4 #include <signal.h>
      5 #include <sys/stat.h>
      6 
      7 #include <skalibs/types.h>
      8 #include <skalibs/allreadwrite.h>
      9 #include <skalibs/sgetopt.h>
     10 #include <skalibs/error.h>
     11 #include <skalibs/buffer.h>
     12 #include <skalibs/sig.h>
     13 #include <skalibs/selfpipe.h>
     14 #include <skalibs/strerr.h>
     15 #include <skalibs/tai.h>
     16 #include <skalibs/iopause.h>
     17 #include <skalibs/djbunix.h>
     18 
     19 #define USAGE "s6-ioconnect [ -t timeout ] [ -r fdr ] [ -w fdw ]"
     20 #define dieusage() strerr_dieusage(100, USAGE)
     21 
     22 #define BSIZE 8192
     23 
     24 typedef struct ioblah_s ioblah, *ioblah_ref ;
     25 struct ioblah_s
     26 {
     27   buffer b ;
     28   unsigned int xindex ;
     29   unsigned int flagsocket : 1 ;
     30 } ;
     31 
     32 static char buf[2][BSIZE] = { { '\0' }, { '\0' } } ;
     33 static ioblah a[2][2] =
     34 {
     35   {
     36     { .b = BUFFER_INIT(&buffer_read, 0, buf[0], BSIZE), .xindex = 5 },
     37     { .b = BUFFER_INIT(&buffer_write, 7, buf[0], BSIZE), .xindex = 5 }
     38   },
     39   {
     40     { .b = BUFFER_INIT(&buffer_read, 6, buf[1], BSIZE), .xindex = 5 },
     41     { .b = BUFFER_INIT(&buffer_write, 1, buf[1], BSIZE), .xindex = 5 }
     42   }
     43 } ;
     44 static iopause_fd x[5] = { [0] = { .fd = -1, .events = IOPAUSE_READ } } ;
     45 
     46 static void closeit (unsigned int i, unsigned int j)
     47 {
     48   int fd = buffer_fd(&a[i][j].b) ;
     49   if (a[i][j].flagsocket) fd_shutdown(fd, j) ;
     50   fd_close(fd) ;
     51   buffer_fd(&a[i][j].b) = -1 ;
     52   a[i][j].xindex = 5 ;
     53 }
     54 
     55 static void handle_signals (void)
     56 {
     57   for (;;)
     58   {
     59     switch (selfpipe_read())
     60     {
     61       case -1 : strerr_diefu1sys(111, "selfpipe_read") ;
     62       case 0 : return ;
     63       case SIGTERM :
     64       {
     65         if (a[0][0].xindex < 5) x[a[0][0].xindex].revents |= IOPAUSE_EXCEPT ;
     66         if (a[1][0].xindex < 5) x[a[1][0].xindex].revents |= IOPAUSE_EXCEPT ;
     67         break ;
     68       }
     69       default :
     70         strerr_dief1x(101, "internal error: inconsistent signal state. Please submit a bug-report.") ;
     71     }
     72   }
     73 }
     74 
     75 static int flushit (unsigned int i)
     76 {
     77   int r = buffer_flush(&a[i][1].b) ;
     78   a[i][0].b.c.p = a[i][1].b.c.p ; /* XXX: abstraction leak */
     79   return r ;
     80 }
     81 
     82 static int fillit (unsigned int i)
     83 {
     84   ssize_t r = sanitize_read(buffer_fill(&a[i][0].b)) ;
     85   a[i][1].b.c.n = a[i][0].b.c.n ; /* XXX: abstraction leak */
     86   return r >= 0 ;
     87 }
     88 
     89 int main (int argc, char const *const *argv)
     90 {
     91   tain tto ;
     92   unsigned int i, j ;
     93   PROG = "s6-ioconnect" ;
     94   {
     95     subgetopt l = SUBGETOPT_ZERO ;
     96     unsigned int t = 0 ;
     97     for (;;)
     98     {
     99       int opt = subgetopt_r(argc, argv, "0167t:r:w:", &l) ;
    100       if (opt < 0) break ;
    101       switch (opt)
    102       {
    103         case '0' : break ;  /* these options are deprecated */
    104         case '1' : break ;  /* only there for compatibility */
    105         case '6' : break ;  /* flagsocket is autodetected now */
    106         case '7' : break ;
    107         case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ;
    108         case 'r' : if (!int0_scan(l.arg, &buffer_fd(&a[1][0].b))) dieusage() ; break ;
    109         case 'w' : if (!int0_scan(l.arg, &buffer_fd(&a[0][1].b))) dieusage() ; break ;
    110         default : dieusage() ;
    111       }
    112     }
    113     if (t) tain_from_millisecs(&tto, t) ; else tto = tain_infinite_relative ;
    114     argc -= l.ind ; argv += l.ind ;
    115   }
    116   if ((buffer_fd(&a[0][1].b) < 3) || (buffer_fd(&a[1][0].b) < 3)) dieusage() ;
    117   for (i = 0 ; i < 2 ; i++) for (j = 0 ; j < 2 ; j++)
    118   {
    119     int fd = buffer_fd(&a[i][j].b) ;
    120     struct stat st ;
    121     if (fstat(fd, &st) == -1)
    122     {
    123       char fmt[INT_FMT] ;
    124       fmt[int_fmt(fmt, fd)] = 0 ;
    125       strerr_diefu2sys(111, "fstat fd ", fmt) ;
    126     }
    127     a[i][j].flagsocket = !!S_ISSOCK(st.st_mode) ;
    128     if (ndelay_on(fd) == -1)
    129     {
    130       char fmt[INT_FMT] ;
    131       fmt[int_fmt(fmt, fd)] = 0 ;
    132       strerr_diefu3sys(111, "set fd ", fmt, " non-blocking") ;
    133     }
    134   }
    135   if (!sig_ignore(SIGPIPE)) strerr_diefu1sys(111, "ignore SIGPIPE") ;
    136   tain_now_set_stopwatch_g() ;
    137   x[0].fd = selfpipe_init() ;
    138   if (x[0].fd < 0) strerr_diefu1sys(111, "selfpipe_init") ;
    139   if (!selfpipe_trap(SIGTERM))
    140     strerr_diefu1sys(111, "trap SIGTERM") ;
    141 
    142   for (;;)
    143   {
    144     tain deadline ;
    145     unsigned int xlen = 1 ;
    146     tain_add_g(&deadline, buffer_isempty(&a[0][1].b) && buffer_isempty(&a[1][1].b) ? &tto : &tain_infinite_relative) ;
    147 
    148     for (i = 0 ; i < 2 ; i++)
    149     {
    150       if (buffer_fd(&a[i][0].b) >= 0 && buffer_isreadable(&a[i][0].b))
    151       {
    152         x[xlen].fd = buffer_fd(&a[i][0].b) ;
    153         x[xlen].events = IOPAUSE_READ ;
    154         a[i][0].xindex = xlen++ ;
    155       }
    156       else a[i][0].xindex = 5 ;
    157       if (buffer_fd(&a[i][1].b) >= 0)
    158       {
    159         x[xlen].fd = buffer_fd(&a[i][1].b) ;
    160         x[xlen].events = IOPAUSE_EXCEPT | (buffer_iswritable(&a[i][1].b) ? IOPAUSE_WRITE : 0) ;
    161         a[i][1].xindex = xlen++ ;
    162       }
    163       else a[i][1].xindex = 5 ;
    164     }
    165     if (xlen == 1) break ;
    166 
    167     {
    168       int r = iopause_g(x, xlen, &deadline) ;
    169       if (r < 0) strerr_diefu1sys(111, "iopause") ;
    170       else if (!r) return 1 ;
    171     }
    172 
    173     if (x[0].revents & IOPAUSE_READ) handle_signals() ;
    174 
    175     for (i = 0 ; i < 2 ; i++) if (a[i][1].xindex < 5)
    176     {
    177       int dead = 0 ;
    178       if (x[a[i][1].xindex].revents & IOPAUSE_EXCEPT)
    179       {
    180         if (!buffer_isempty(&a[i][1].b))
    181         {
    182           char fmt[INT_FMT] ;
    183           fmt[int_fmt(fmt, buffer_fd(&a[i][1].b))] = 0 ;
    184           flushit(i) ; /* sets errno */
    185           strerr_warnwu2sys("write to fd ", fmt) ;
    186         }
    187         dead = 1 ;
    188       }
    189       else if (x[a[i][1].xindex].revents & IOPAUSE_WRITE)
    190       {
    191         if (!flushit(i))
    192         {
    193           if (!error_isagain(errno)) dead = 1 ;
    194         }
    195         else if (buffer_fd(&a[i][0].b) == -1) dead = 1 ;
    196       }
    197       if (dead)
    198       {
    199         if (buffer_fd(&a[i][0].b) >= 0) closeit(i, 0) ;
    200         closeit(i, 1) ;
    201       }
    202     }
    203 
    204     for (i = 0 ; i < 2 ; i++) if (a[i][0].xindex < 5)
    205     {
    206       if (x[a[i][0].xindex].revents & (IOPAUSE_READ | IOPAUSE_EXCEPT))
    207       {
    208         if (!fillit(i))
    209         {
    210           if (errno != EPIPE)
    211           {
    212             char fmt[INT_FMT] ;
    213             fmt[int_fmt(fmt, buffer_fd(&a[i][0].b))] = 0 ;
    214             strerr_warnwu2sys("read from fd ", fmt) ;
    215           }
    216           closeit(i, 0) ;
    217           if (buffer_isempty(&a[i][1].b)) closeit(i, 1) ;
    218         }
    219       }
    220     }
    221   }
    222   return 0 ;
    223 }