s6-sudod.c (7008B)
1 /* ISC license. */ 2 3 #include <string.h> 4 #include <stdint.h> 5 #include <unistd.h> 6 #include <errno.h> 7 #include <fcntl.h> 8 #include <signal.h> 9 10 #include <skalibs/types.h> 11 #include <skalibs/allreadwrite.h> 12 #include <skalibs/sgetopt.h> 13 #include <skalibs/bytestr.h> 14 #include <skalibs/buffer.h> 15 #include <skalibs/stralloc.h> 16 #include <skalibs/strerr.h> 17 #include <skalibs/tai.h> 18 #include <skalibs/iopause.h> 19 #include <skalibs/selfpipe.h> 20 #include <skalibs/env.h> 21 #include <skalibs/cspawn.h> 22 #include <skalibs/djbunix.h> 23 #include <skalibs/unix-timed.h> 24 #include <skalibs/unixmessage.h> 25 26 #include "s6-sudo.h" 27 28 #define USAGE "s6-sudod [ -0 ] [ -1 ] [ -2 ] [ -d ] [ -t timeout ] args..." 29 #define dieusage() strerr_dieusage(100, USAGE) 30 #define dienomem() strerr_diefu1sys(111, "stralloc_catb") 31 32 static int handle_signals (pid_t pid, int *wstat) 33 { 34 int done = 0 ; 35 for (;;) 36 { 37 int sig = selfpipe_read() ; 38 switch (sig) 39 { 40 case -1 : strerr_diefu1sys(111, "read from selfpipe") ; 41 case 0 : return done ; 42 case SIGCHLD : 43 { 44 int w ; 45 pid_t r = wait_pid_nohang(pid, &w) ; 46 if ((r < 0) && (errno != ECHILD)) 47 strerr_diefu1sys(111, "wait_pid_nohang") ; 48 else if (r > 0) 49 { 50 done = 1 ; 51 *wstat = w ; 52 } 53 break ; 54 } 55 default : 56 strerr_dief1sys(101, "internal inconsistency, please submit a bug-report") ; 57 } 58 } 59 } 60 61 int main (int argc, char const *const *argv, char const *const *envp) 62 { 63 iopause_fd x[2] = { { .events = IOPAUSE_READ }, { .fd = 0, .events = 0, .revents = 0 } } ; 64 unixmessage m ; 65 unsigned int nullfds = 0 ; 66 pid_t pid ; 67 int wstat ; 68 size_t envc = env_len(envp) ; 69 uint32_t cargc, cenvc, carglen, cenvlen ; 70 tain deadline = TAIN_INFINITE_RELATIVE ; 71 PROG = "s6-sudod" ; 72 73 { 74 subgetopt l = SUBGETOPT_ZERO ; 75 unsigned int t = 0 ; 76 for (;;) 77 { 78 int opt = subgetopt_r(argc, argv, "012dt:", &l) ; 79 if (opt < 0) break ; 80 switch (opt) 81 { 82 case '0' : nullfds |= 1 ; break ; 83 case '1' : nullfds |= 2 ; break ; 84 case '2' : nullfds |= 4 ; break ; 85 case 'd' : nullfds |= 15 ; break ; 86 case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; 87 default : dieusage() ; 88 } 89 } 90 argc -= l.ind ; argv += l.ind ; 91 if (t) tain_from_millisecs(&deadline, t) ; 92 } 93 94 if ((ndelay_on(0) < 0) || (ndelay_on(1) < 0)) 95 strerr_diefu1sys(111, "make socket non-blocking") ; 96 tain_now_set_stopwatch_g() ; 97 tain_add_g(&deadline, &deadline) ; 98 buffer_putnoflush(buffer_1small, S6_SUDO_BANNERB, S6_SUDO_BANNERB_LEN) ; 99 if (!buffer_timed_flush_g(buffer_1small, &deadline)) 100 strerr_diefu1sys(111, "write banner to client") ; 101 if (unixmessage_timed_receive_g(unixmessage_receiver_0, &m, &deadline) <= 0) 102 strerr_diefu1sys(111, "read message from client") ; 103 if (m.nfds != 3) 104 strerr_dief1x(100, "client did not send 3 fds") ; 105 if (m.len < 16 + S6_SUDO_BANNERA_LEN) 106 strerr_dief1x(100, "wrong client message") ; 107 if (strncmp(m.s, S6_SUDO_BANNERA, S6_SUDO_BANNERA_LEN)) 108 strerr_dief1x(100, "wrong client banner") ; 109 uint32_unpack_big(m.s + S6_SUDO_BANNERA_LEN, &cargc) ; 110 uint32_unpack_big(m.s + S6_SUDO_BANNERA_LEN + 4, &cenvc) ; 111 uint32_unpack_big(m.s + S6_SUDO_BANNERA_LEN + 8, &carglen) ; 112 uint32_unpack_big(m.s + S6_SUDO_BANNERA_LEN + 12, &cenvlen) ; 113 if (S6_SUDO_BANNERA_LEN + 16 + carglen + cenvlen != m.len) 114 strerr_dief1x(100, "wrong client argc/envlen") ; 115 if ((cargc > 131072) || (cenvc > 131072)) 116 strerr_dief1x(100, "too many args/envvars from client") ; 117 if (argc + cargc == 0) strerr_dief1x(100, "client and server args both empty") ; 118 119 if (nullfds & 1) 120 { 121 close(m.fds[0]) ; 122 m.fds[0] = open2("/dev/null", O_RDONLY) ; 123 if (m.fds[0] < 0) strerr_diefu2sys(111, "open /dev/null for ", "reading") ; 124 } 125 if (nullfds & 2) 126 { 127 close(m.fds[1]) ; 128 m.fds[1] = open2("/dev/null", O_WRONLY) ; 129 if (m.fds[1] < 0) strerr_diefu2sys(111, "open /dev/null for ", "writing") ; 130 } 131 if (nullfds & 4) 132 { 133 close(m.fds[2]) ; 134 m.fds[2] = 2 ; 135 } 136 137 { 138 cspawn_fileaction fa[3] = 139 { 140 [0] = { .type = CSPAWN_FA_MOVE, .x = { .fd2 = { [0] = 0, [1] = m.fds[0] } } }, 141 [1] = { .type = CSPAWN_FA_MOVE, .x = { .fd2 = { [0] = 1, [1] = m.fds[1] } } }, 142 [2] = { .type = CSPAWN_FA_MOVE, .x = { .fd2 = { [0] = 2, [1] = m.fds[2] } } } 143 } ; 144 char const *targv[argc + 1 + cargc] ; 145 char const *tenvp[envc + 1 + cenvc] ; 146 unsigned int i = 0 ; 147 for (; i < (unsigned int)argc ; i++) targv[i] = argv[i] ; 148 for (i = 0 ; i <= envc ; i++) tenvp[i] = envp[i] ; 149 if (!env_make(targv + argc, cargc, m.s + S6_SUDO_BANNERA_LEN + 16, carglen)) 150 { 151 char c = errno ; 152 buffer_putnoflush(buffer_1small, &c, 1) ; 153 buffer_timed_flush_g(buffer_1small, &deadline) ; 154 errno = c ; 155 strerr_diefu1sys(111, "make child argv") ; 156 } 157 if (!env_make(tenvp + envc + 1, cenvc, m.s + S6_SUDO_BANNERA_LEN + 16 + carglen, cenvlen)) 158 { 159 char c = errno ; 160 buffer_putnoflush(buffer_1small, &c, 1) ; 161 buffer_timed_flush_g(buffer_1small, &deadline) ; 162 errno = c ; 163 strerr_diefu1sys(111, "make child envp") ; 164 } 165 targv[argc + cargc] = 0 ; 166 167 for (i = 0 ; i < cenvc ; i++) 168 { 169 char const *var = tenvp[envc + 1 + i] ; 170 unsigned int j = 0 ; 171 size_t len = str_chr(var, '=') ; 172 if (!var[len]) 173 { 174 char c = EINVAL ; 175 buffer_putnoflush(buffer_1small, &c, 1) ; 176 buffer_timed_flush_g(buffer_1small, &deadline) ; 177 strerr_dief1x(100, "bad environment from client") ; 178 } 179 for (; j < envc ; j++) if (!strncmp(var, tenvp[j], len+1)) break ; 180 if ((j < envc) && !tenvp[j][len+1]) tenvp[j] = var ; 181 } 182 183 x[0].fd = selfpipe_init() ; 184 if (x[0].fd < 0) strerr_diefu1sys(111, "selfpipe_init") ; 185 if (!selfpipe_trap(SIGCHLD)) strerr_diefu1sys(111, "trap SIGCHLD") ; 186 187 pid = cspawn(targv[0], targv, tenvp, CSPAWN_FLAGS_SELFPIPE_FINISH, fa, 3) ; 188 if (!pid) strerr_diefu2sys(111, "spawn ", targv[0]) ; 189 } 190 191 fd_close(m.fds[0]) ; 192 fd_close(m.fds[1]) ; 193 if (!(nullfds & 4)) fd_close(m.fds[2]) ; 194 unixmessage_receiver_free(unixmessage_receiver_0) ; 195 buffer_putnoflush(buffer_1small, "", 1) ; 196 if (!buffer_timed_flush_g(buffer_1small, &deadline)) 197 strerr_diefu1sys(111, "send confirmation to client") ; 198 199 for (;;) 200 { 201 if (iopause_g(x, 1 + !x[1].revents, 0) < 0) strerr_diefu1sys(111, "iopause") ; 202 if (x[0].revents && handle_signals(pid, &wstat)) break ; 203 if (x[1].revents && !(nullfds & 8)) 204 { 205 kill(pid, SIGTERM) ; 206 kill(pid, SIGCONT) ; 207 return 1 ; 208 } 209 } 210 211 if (!x[1].revents) 212 { 213 char pack[UINT_PACK] ; 214 uint_pack_big(pack, (unsigned int)wstat) ; 215 buffer_putnoflush(buffer_1small, pack, UINT_PACK) ; 216 if (ndelay_off(1) < 0) 217 strerr_diefu1sys(111, "set stdout blocking") ; 218 if (!buffer_flush(buffer_1small)) 219 strerr_diefu1sys(111, "write status to client") ; 220 } 221 return 0 ; 222 }