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 }