s6

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

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 }