s6

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

s6-ipcserverd.c (10430B)


      1 /* ISC license. */
      2 
      3 #include <sys/types.h>
      4 #include <sys/stat.h>
      5 #include <sys/wait.h>
      6 #include <errno.h>
      7 #include <string.h>
      8 #include <unistd.h>
      9 #include <fcntl.h>
     10 #include <signal.h>
     11 
     12 #include <skalibs/posixplz.h>
     13 #include <skalibs/types.h>
     14 #include <skalibs/gccattributes.h>
     15 #include <skalibs/allreadwrite.h>
     16 #include <skalibs/sgetopt.h>
     17 #include <skalibs/strerr.h>
     18 #include <skalibs/djbunix.h>
     19 #include <skalibs/sig.h>
     20 #include <skalibs/selfpipe.h>
     21 #include <skalibs/iopause.h>
     22 #include <skalibs/socket.h>
     23 #include <skalibs/env.h>
     24 #include <skalibs/cspawn.h>
     25 
     26 #define USAGE "s6-ipcserverd [ -v verbosity ] [ -1 ] [ -P | -p ] [ -c maxconn ] [ -C localmaxconn ] prog..."
     27 
     28 #define ABSOLUTE_MAXCONN 16384
     29 
     30 static unsigned int maxconn = 40 ;
     31 static unsigned int localmaxconn = 40 ;
     32 static char fmtmaxconn[UINT_FMT+1] = "/" ;
     33 static char fmtlocalmaxconn[UINT_FMT+1] = "/" ;
     34 static int flaglookup = 1 ;
     35 static unsigned int verbosity = 1 ;
     36 static int cont = 1 ;
     37 
     38 typedef struct piduid_s piduid_t, *piduid_t_ref ;
     39 struct piduid_s
     40 {
     41   pid_t left ;
     42   uid_t right ;
     43 } ;
     44 
     45 typedef struct uidnum_s uidnum_t, *uidnum_t_ref ;
     46 struct uidnum_s
     47 {
     48   uid_t left ;
     49   unsigned int right ;
     50 } ;
     51 
     52 static piduid_t *piduid ;
     53 static unsigned int numconn = 0 ;
     54 static uidnum_t *uidnum ;
     55 static unsigned int uidlen = 0 ;
     56 
     57 static inline void dieusage ()
     58 {
     59   strerr_dieusage(100, USAGE) ;
     60 }
     61 
     62 static inline void X (void)
     63 {
     64   strerr_dief1x(101, "internal inconsistency. Please submit a bug-report.") ;
     65 }
     66 
     67 static unsigned int lookup_pid (pid_t pid)
     68 {
     69   unsigned int i = 0 ;
     70   for (; i < numconn ; i++) if (pid == piduid[i].left) break ;
     71   return i ;
     72 }
     73 
     74 static inline unsigned int lookup_uid (uid_t uid)
     75 {
     76   unsigned int i = 0 ;
     77   for (; i < uidlen ; i++) if (uid == uidnum[i].left) break ;
     78   return i ;
     79 }
     80 
     81 static inline void log_start (void)
     82 {
     83   strerr_warni1x("starting") ;
     84 }
     85 
     86 static inline void log_exit (void)
     87 {
     88   strerr_warni1x("exiting") ;
     89 }
     90 
     91 static void log_status (void)
     92 {
     93   char fmt[UINT_FMT] ;
     94   fmt[uint_fmt(fmt, numconn)] = 0 ;
     95   strerr_warni3x("status: ", fmt, fmtmaxconn) ;
     96 }
     97 
     98 static inline void log_deny (uid_t uid, gid_t gid, unsigned int num)
     99 {
    100   char fmtuid[UID_FMT] = "?" ;
    101   char fmtgid[GID_FMT] = "?" ;
    102   char fmtnum[UINT_FMT] = "?" ;
    103   if (flaglookup)
    104   {
    105     fmtuid[uid_fmt(fmtuid, uid)] = 0 ;
    106     fmtgid[gid_fmt(fmtgid, gid)] = 0 ;
    107     fmtnum[uint_fmt(fmtnum, num)] = 0 ;
    108   }
    109   strerr_warni7sys("deny ", fmtuid, ":", fmtgid, " count ", fmtnum, fmtlocalmaxconn) ;
    110 }
    111 
    112 static inline void log_accept (pid_t pid, uid_t uid, gid_t gid, unsigned int num)
    113 {
    114   char fmtuidgid[UID_FMT + GID_FMT + 1] = "?:?" ;
    115   char fmtpid[UINT_FMT] ;
    116   char fmtnum[UINT_FMT] = "?" ;
    117   if (flaglookup)
    118   {
    119     size_t n = uid_fmt(fmtuidgid, uid) ;
    120     fmtuidgid[n++] = ':' ;
    121     n += gid_fmt(fmtuidgid + n, gid) ;
    122     fmtuidgid[n] = 0 ;
    123     fmtnum[uint_fmt(fmtnum, num)] = 0 ;
    124   }
    125   fmtpid[pid_fmt(fmtpid, pid)] = 0 ;
    126   strerr_warni7x("allow ", fmtuidgid, " pid ", fmtpid, " count ", fmtnum, fmtlocalmaxconn) ;
    127 }
    128 
    129 static inline void log_close (pid_t pid, uid_t uid, int w)
    130 {
    131   char fmtpid[PID_FMT] ;
    132   char fmtuid[UID_FMT] = "?" ;
    133   char fmtw[UINT_FMT] ;
    134   fmtpid[pid_fmt(fmtpid, pid)] = 0 ;
    135   if (flaglookup) fmtuid[uid_fmt(fmtuid, uid)] = 0 ;
    136   fmtw[uint_fmt(fmtw, WIFSIGNALED(w) ? WTERMSIG(w) : WEXITSTATUS(w))] = 0 ;
    137   strerr_warni6x("end pid ", fmtpid, " uid ", fmtuid, WIFSIGNALED(w) ? " signal " : " exitcode ", fmtw) ;
    138 }
    139 
    140 static void killthem (int sig)
    141 {
    142   unsigned int i = 0 ;
    143   for (; i < numconn ; i++) kill(piduid[i].left, sig) ;
    144 }
    145 
    146 static inline void wait_children (void)
    147 {
    148   for (;;)
    149   {
    150     unsigned int i ;
    151     int w ;
    152     pid_t pid = wait_nohang(&w) ;
    153     if (pid < 0)
    154       if (errno != ECHILD) strerr_diefu1sys(111, "wait_nohang") ;
    155       else break ;
    156     else if (!pid) break ;
    157     i = lookup_pid(pid) ;
    158     if (i < numconn)
    159     {
    160       uid_t uid = piduid[i].right ;
    161       unsigned int j = lookup_uid(uid) ;
    162       if (j >= uidlen) X() ;
    163       if (!--uidnum[j].right) uidnum[j] = uidnum[--uidlen] ;
    164       piduid[i] = piduid[--numconn] ;
    165       if (verbosity >= 2)
    166       {
    167         log_close(pid, uid, w) ;
    168         log_status() ;
    169       }
    170     }
    171   }
    172 }
    173 
    174 static inline void handle_signals (void)
    175 {
    176   for (;;) switch (selfpipe_read())
    177   {
    178     case -1 : strerr_diefu1sys(111, "read selfpipe") ;
    179     case 0 : return ;
    180     case SIGCHLD : wait_children() ; break ;
    181     case SIGTERM :
    182     {
    183       if (verbosity >= 2)
    184         strerr_warni3x("received ", "SIGTERM,", " quitting") ;
    185       cont = 0 ;
    186       break ;
    187     }
    188     case SIGHUP :
    189     {
    190       if (verbosity >= 2)
    191         strerr_warni5x("received ", "SIGHUP,", " sending ", "SIGTERM+SIGCONT", " to all connections") ;
    192       killthem(SIGTERM) ;
    193       killthem(SIGCONT) ;
    194       break ;
    195     }
    196     case SIGQUIT :
    197     {
    198       if (verbosity >= 2)
    199         strerr_warni6x("received ", "SIGQUIT,", " sending ", "SIGTERM+SIGCONT", " to all connections", " and quitting") ;
    200       cont = 0 ;
    201       killthem(SIGTERM) ;
    202       killthem(SIGCONT) ;
    203       break ;
    204     }
    205     case SIGABRT :
    206     {
    207       if (verbosity >= 2)
    208         strerr_warni6x("received ", "SIGABRT,", " sending ", "SIGKILL", " to all connections", " and quitting") ;
    209       cont = 0 ;
    210       killthem(SIGKILL) ;
    211       break ;
    212     }
    213     default : X() ;
    214   }
    215 }
    216 
    217 static void new_connection (int s, char const *remotepath, char const *const *argv, char const *const *envp, size_t envlen)
    218 {
    219   uid_t uid = 0 ;
    220   gid_t gid = 0 ;
    221   size_t m = 0 ;
    222   size_t rplen = strlen(remotepath) + 1 ;
    223   pid_t pid ;
    224   unsigned int num, i ;
    225   cspawn_fileaction fa[2] =
    226   {
    227     [0] = { .type = CSPAWN_FA_MOVE, .x = { .fd2 = { [0] = 0, [1] = s } } },
    228     [1] = { .type = CSPAWN_FA_COPY, .x = { .fd2 = { [0] = 1, [1] = 0 } } }
    229   } ;
    230   char const *newenvp[envlen + 6] ;
    231   char fmt[65 + UID_FMT + GID_FMT + UINT_FMT + rplen] ;
    232 
    233   if (flaglookup && (getpeereid(s, &uid, &gid) < 0))
    234   {
    235     if (verbosity) strerr_warnwu1sys("getpeereid") ;
    236     return ;
    237   }
    238   i = lookup_uid(uid) ;
    239   num = (i < uidlen) ? uidnum[i].right : 0 ;
    240   if (num >= localmaxconn)
    241   {
    242     log_deny(uid, gid, num) ;
    243     return ;
    244   }
    245 
    246   memcpy(fmt + m, "PROTO=IPC\0IPCREMOTEEUID", 23) ; m += 23 ;
    247   if (flaglookup)
    248   {
    249     fmt[m++] = '=' ;
    250     m += uid_fmt(fmt + m, uid) ;
    251   }
    252   fmt[m++] = 0 ;
    253   memcpy(fmt + m, "IPCREMOTEEGID", 13) ; m += 13 ;
    254   if (flaglookup)
    255   {
    256     fmt[m++] = '=' ;
    257     m += gid_fmt(fmt + m, gid) ;
    258   }
    259   fmt[m++] = 0 ;
    260   memcpy(fmt + m, "IPCCONNNUM=", 11) ; m += 11 ;
    261   if (flaglookup) m += uint_fmt(fmt + m, num) ;
    262   fmt[m++] = 0 ;
    263   memcpy(fmt + m, "IPCREMOTEPATH=", 14) ; m += 14 ;
    264   memcpy(fmt + m, remotepath, rplen) ; m += rplen ;
    265   env_mergen(newenvp, envlen + 6, envp, envlen, fmt, m, 5) ;
    266   pid = cspawn(argv[0], argv, newenvp, CSPAWN_FLAGS_SELFPIPE_FINISH, fa, 2) ;
    267   if (!pid)
    268   {
    269     if (verbosity) strerr_warnwu2sys("spawn ", argv[0]) ;
    270     return ;
    271   }
    272 
    273   if (i < uidlen) uidnum[i].right = num + 1 ;
    274   else
    275   {
    276     uidnum[uidlen].left = uid ;
    277     uidnum[uidlen++].right = 1 ;
    278   }
    279   piduid[numconn].left = pid ;
    280   piduid[numconn++].right = uid ;
    281   if (verbosity >= 2)
    282   {
    283     log_accept(pid, uid, gid, uidnum[i].right) ;
    284     log_status() ;
    285   }
    286 }
    287 
    288 int main (int argc, char const *const *argv)
    289 {
    290   iopause_fd x[2] = { { .events = IOPAUSE_READ }, { .fd = 0, .events = IOPAUSE_READ | IOPAUSE_EXCEPT } } ;
    291   PROG = "s6-ipcserverd" ;
    292   {
    293     subgetopt l = SUBGETOPT_ZERO ;
    294     int flag1 = 0 ;
    295     for (;;)
    296     {
    297       int opt = subgetopt_r(argc, argv, "Pp1c:C:v:", &l) ;
    298       if (opt == -1) break ;
    299       switch (opt)
    300       {
    301         case 'P' : flaglookup = 0 ; break ;
    302         case 'p' : flaglookup = 1 ; break ;
    303         case '1' : flag1 = 1 ; break ;
    304         case 'c' : if (!uint0_scan(l.arg, &maxconn)) dieusage() ; break ;
    305         case 'C' : if (!uint0_scan(l.arg, &localmaxconn)) dieusage() ; break ;
    306         case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ;
    307         default : dieusage() ;
    308       }
    309     }
    310     argc -= l.ind ; argv += l.ind ;
    311     if (!argc || !*argv[0]) dieusage() ;
    312     {
    313       struct stat st ;
    314       if (fstat(0, &st) < 0) strerr_diefu1sys(111, "fstat stdin") ;
    315       if (!S_ISSOCK(st.st_mode)) strerr_dief1x(100, "stdin is not a socket") ;
    316     }
    317     if (coe(0) < 0) strerr_diefu1sys(111, "make socket close-on-exec") ;
    318     if (flag1)
    319     {
    320       if (fcntl(1, F_GETFD) < 0)
    321         strerr_dief1sys(100, "called with option -1 but stdout said") ;
    322     }
    323     else close(1) ;
    324     if (!maxconn) maxconn = 1 ;
    325     if (maxconn > ABSOLUTE_MAXCONN) maxconn = ABSOLUTE_MAXCONN ;
    326     if (!flaglookup || (localmaxconn > maxconn)) localmaxconn = maxconn ;
    327 
    328     x[0].fd = selfpipe_init() ;
    329     if (x[0].fd == -1) strerr_diefu1sys(111, "create selfpipe") ;
    330     if (!sig_altignore(SIGPIPE)) strerr_diefu1sys(111, "ignore SIGPIPE") ;
    331     {
    332       sigset_t set ;
    333       sigemptyset(&set) ;
    334       sigaddset(&set, SIGCHLD) ;
    335       sigaddset(&set, SIGTERM) ;
    336       sigaddset(&set, SIGHUP) ;
    337       sigaddset(&set, SIGQUIT) ;
    338       sigaddset(&set, SIGABRT) ;
    339       if (!selfpipe_trapset(&set)) strerr_diefu1sys(111, "trap signals") ;
    340     }
    341 
    342     fmtlocalmaxconn[1+uint_fmt(fmtlocalmaxconn+1, localmaxconn)] = 0 ;
    343     if (verbosity >= 2)
    344     {
    345       fmtmaxconn[1+uint_fmt(fmtmaxconn+1, maxconn)] = 0 ;
    346       log_start() ;
    347       log_status() ;
    348     }
    349     if (flag1)
    350     {
    351       fd_write(1, "\n", 1) ;
    352       fd_close(1) ;
    353     }
    354   }
    355 
    356   {
    357     piduid_t inyostack0[maxconn] ;
    358     uidnum_t inyostack1[flaglookup ? maxconn : 1] ;
    359     size_t envlen = env_len((char const *const *)environ) ;
    360     piduid = inyostack0 ;
    361     uidnum = inyostack1 ;
    362 
    363     while (cont)
    364     {
    365       if (iopause_g(x, 1 + (numconn < maxconn), 0) < 0)
    366         strerr_diefu1sys(111, "iopause") ;
    367 
    368       if (x[0].revents & IOPAUSE_EXCEPT) strerr_dief1x(111, "trouble with selfpipe") ;
    369       if (x[0].revents & IOPAUSE_READ) { handle_signals() ; continue ; }
    370       if (numconn < maxconn)
    371       {
    372         if (x[1].revents & IOPAUSE_EXCEPT) strerr_dief1x(111, "trouble with socket") ;
    373         if (x[1].revents & IOPAUSE_READ)
    374         {
    375           int dummy ;
    376           char remotepath[IPCPATH_MAX+1] ;
    377           int sock = ipc_accept(x[1].fd, remotepath, IPCPATH_MAX+1, &dummy) ;
    378           if (sock < 0)
    379           {
    380             if (verbosity) strerr_warnwu1sys("accept") ;
    381           }
    382           else
    383           {
    384             new_connection(sock, remotepath, argv, (char const *const *)environ, envlen) ;
    385             fd_close(sock) ;
    386           }
    387         }
    388       }
    389     }
    390   }
    391   if (verbosity >= 2) log_exit() ;
    392   return 0 ;
    393 }