s6

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

s6-socklog.c (7215B)


      1 /* ISC license. */
      2 
      3 #include <stdint.h>
      4 #include <unistd.h>
      5 #include <stdlib.h>
      6 #include <fcntl.h>
      7 #include <signal.h>
      8 #include <errno.h>
      9 #include <limits.h>
     10 
     11 #include <skalibs/types.h>
     12 #include <skalibs/sgetopt.h>
     13 #include <skalibs/allreadwrite.h>
     14 #include <skalibs/buffer.h>
     15 #include <skalibs/error.h>
     16 #include <skalibs/strerr.h>
     17 #include <skalibs/tai.h>
     18 #include <skalibs/iopause.h>
     19 #include <skalibs/djbunix.h>
     20 #include <skalibs/socket.h>
     21 #include <skalibs/ip46.h>
     22 #include <skalibs/setgroups.h>
     23 #include <skalibs/sig.h>
     24 #include <skalibs/selfpipe.h>
     25 
     26 #include "lolsyslog.h"
     27 
     28 #define USAGE "s6-socklog [ -d notif ] [ -r ] [ -U | -u uid -g gid -G gidlist ] [ -l linelen ] [ -t lameducktimeout ] [ -x socket | -i ip_port ]"
     29 #define dieusage() strerr_dieusage(100, USAGE)
     30 
     31 static tain lameducktto = TAIN_INFINITE_RELATIVE ;
     32 static int cont = 1 ;
     33 
     34 static inline void handle_signals (void)
     35 {
     36   for (;;) switch (selfpipe_read())
     37   {
     38     case -1 : strerr_diefu1sys(111, "selfpipe_read()") ;
     39     case 0 : return ;
     40     case SIGTERM :
     41       cont = 0 ;
     42       tain_add_g(&lameducktto, &lameducktto) ;
     43       break ;
     44     default : break ;
     45   }
     46 }
     47 
     48 int main (int argc, char const *const *argv)
     49 {
     50   iopause_fd x[3] = { { .events = IOPAUSE_READ }, { .fd = 1 } } ;
     51   int flagraw = 0 ;
     52   int is6 = 0 ;
     53   char const *usock = "/dev/log" ;
     54   unsigned int linelen = 1024 ;
     55   PROG = "s6-socklog" ;
     56   {
     57     subgetopt l = SUBGETOPT_ZERO ;
     58     unsigned int notif = 0 ;
     59     unsigned int t = 0 ;
     60     uid_t uid = 0 ;
     61     gid_t gid = 0 ;
     62     gid_t gids[NGROUPS_MAX + 1] ;
     63     size_t gidn = (size_t)-1 ;
     64     ip46 ip ;
     65     uint16_t port = 514 ;
     66     for (;;)
     67     {
     68       int opt = subgetopt_r(argc, argv, "rd:l:t:u:g:G:Ux:i:", &l) ;
     69       if (opt == -1) break ;
     70       switch (opt)
     71       {
     72         case 'r' : flagraw = 1 ; break ;
     73         case 'd' : if (!uint0_scan(l.arg, &notif)) dieusage() ; break ;
     74         case 'l' : if (!uint0_scan(l.arg, &linelen)) dieusage() ; break ;
     75         case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ;
     76         case 'u' : if (!uid0_scan(l.arg, &uid)) dieusage() ; break ;
     77         case 'g' : if (!gid0_scan(l.arg, &gid)) dieusage() ; break ;
     78         case 'G' : if (!gid_scanlist(gids, NGROUPS_MAX, l.arg, &gidn) && *l.arg) dieusage() ; break ;
     79         case 'U' :
     80         {
     81           char const *x = getenv("UID") ;
     82           if (!x) strerr_dienotset(100, "UID") ;
     83           if (!uid0_scan(x, &uid)) strerr_dieinvalid(100, "UID") ;
     84           x = getenv("GID") ;
     85           if (!x) strerr_dienotset(100, "GID") ;
     86           if (!gid0_scan(x, &gid)) strerr_dieinvalid(100, "GID") ;
     87           x = getenv("GIDLIST") ;
     88           if (!x) strerr_dienotset(100, "GIDLIST") ;
     89           if (!gid_scanlist(gids, NGROUPS_MAX+1, x, &gidn) && *x)
     90             strerr_dieinvalid(100, "GIDLIST") ;
     91           break ;
     92         }
     93         case 'x' : usock = l.arg ; break ;
     94         case 'i' :
     95         {
     96           size_t pos = ip46_scan(l.arg, &ip) ;
     97           if (!pos) dieusage() ;
     98           if (l.arg[pos] == '_' || (!ip46_is6(&ip) && l.arg[pos] == ':'))
     99             if (!uint160_scan(l.arg + pos + 1, &port)) dieusage() ;
    100           usock = 0 ;
    101           break ;
    102         }
    103         default : dieusage() ;
    104       }
    105     }
    106     argc -= l.ind ; argv += l.ind ;
    107 
    108     if (linelen < 76) linelen = 76 ;
    109     if (linelen > 1048576) linelen = 1048576 ;
    110     if (t) tain_from_millisecs(&lameducktto, t) ;
    111     if (notif)
    112     {
    113       if (notif < 3) strerr_dief1x(100, "notification fd must be 3 or more") ;
    114       if (fcntl(notif, F_GETFD) < 0) strerr_dief1sys(100, "invalid notification fd") ;
    115     }
    116 
    117     close(0) ;
    118     if (fcntl(1, F_GETFD) < 0) strerr_dief2sys(100, "invalid std", "out") ;
    119     if (fcntl(2, F_GETFD) < 0) strerr_dief2sys(100, "invalid std", "err") ;
    120     if (usock)
    121     {
    122       x[2].fd = ipc_datagram_nbcoe() ;
    123       if (x[2].fd == -1) strerr_diefu1sys(111, "create socket") ;
    124       if (ipc_bind_reuse_perms(x[2].fd, usock, 0777) == -1)
    125         strerr_diefu2sys(111, "bind socket to ", usock) ;
    126     }
    127     else
    128     {
    129       x[2].fd = socket_udp46_nbcoe(ip46_is6(&ip)) ;
    130       if (x[2].fd == -1) strerr_diefu1sys(111, "create socket") ;
    131       if (socket_bind46_reuse(x[2].fd, &ip, port) == -1)
    132       {
    133         char fmti[IP46_FMT] ;
    134         char fmtp[UINT16_FMT] ;
    135         fmti[ip46_fmt(fmti, &ip)] = 0 ;
    136         fmtp[uint16_fmt(fmtp, port)] = 0 ;
    137         strerr_diefu5sys(111, "bind socket to ", "ip ", fmti, " port ", fmtp) ;
    138       }
    139       is6 = ip46_is6(&ip) ;
    140     }
    141 
    142     if (gidn != (size_t)-1 && setgroups_and_gid(gid ? gid : getegid(), gidn, gids) < 0)
    143       strerr_diefu1sys(111, "set supplementary group list") ;
    144     if (gid && setgid(gid) < 0)
    145       strerr_diefu1sys(111, "setgid") ;
    146     if (uid && setuid(uid) < 0)
    147       strerr_diefu1sys(111, "setuid") ;
    148 
    149     x[0].fd = selfpipe_init() ;
    150     if (x[0].fd == -1) strerr_diefu1sys(111, "init selfpipe") ;
    151     if (!sig_altignore(SIGPIPE)) strerr_diefu1sys(111, "ignore SIGPIPE") ;
    152     if (!selfpipe_trap(SIGTERM)) strerr_diefu1sys(111, "trap signals") ;
    153 
    154     tain_now_set_stopwatch_g() ;
    155 
    156     if (notif)
    157     {
    158       fd_write(notif, "\n", 1) ;
    159       fd_close(notif) ;
    160     }
    161   }
    162 
    163   {
    164     char outbuf[linelen << 2] ;
    165     buffer b1 = BUFFER_INIT(&buffer_write, 1, outbuf, linelen << 2) ;
    166     char line[linelen + 1] ;
    167     while (cont || buffer_len(&b1))
    168     {
    169       ssize_t r ;
    170       x[1].events = buffer_len(&b1) ? IOPAUSE_WRITE : 0 ;
    171       x[2].events = cont && buffer_available(&b1) >= linelen + 80 ? IOPAUSE_READ : 0 ;
    172       r = iopause_g(x, 3, cont ? &tain_infinite : &lameducktto) ;
    173       if (r == -1) strerr_diefu1sys(111, "iopause") ;
    174       if (!r) return 99 ;
    175       if (x[0].revents & IOPAUSE_READ) handle_signals() ;
    176       if (x[1].events & x[1].revents & IOPAUSE_WRITE)
    177         if (!buffer_flush(&b1) && !error_isagain(errno))
    178           strerr_diefu1sys(111, "write to stdout") ;
    179       if (x[2].events & x[2].revents & IOPAUSE_READ)
    180       {
    181         if (usock)
    182         {
    183           r = sanitize_read(fd_recv(x[2].fd, line, linelen + 1, 0)) ;
    184           if (r == -1) strerr_diefu1sys(111, "recv") ;
    185         }
    186         else
    187         {
    188           ip46 ip ;
    189           uint16_t port ;
    190           r = sanitize_read(socket_recv46(x[2].fd, line, linelen + 1, &ip, &port, is6)) ;
    191           if (r == -1) strerr_diefu1sys(111, "recv") ;
    192           if (r)
    193           {
    194             char fmt[IP46_FMT + UINT16_FMT + 3] ;
    195             size_t m = ip46_fmt(fmt, &ip) ;
    196             fmt[m++] = '_' ;
    197             m += uint16_fmt(fmt, port) ;
    198             fmt[m++] = ':' ; fmt[m++] = ' ' ;
    199             buffer_putnoflush(&b1, fmt, m) ;
    200           }
    201         }
    202         if (r)
    203         {
    204           size_t len = r ;
    205           size_t pos = 0 ;
    206           while (r && (!line[r-1] || line[r-1] == '\n')) r-- ;
    207           for (size_t i = 0 ; i < r ; i++)
    208             if (!line[i] || line[i] == '\n') line[i] = '~' ;
    209           if (!flagraw)
    210           {
    211             char sbuf[LOLSYSLOG_STRING] ;
    212             pos = lolsyslog_string(sbuf, line) ;
    213             if (pos) buffer_putsnoflush(&b1, sbuf) ;
    214           }
    215           buffer_putnoflush(&b1, line + pos, r - pos) ;
    216           if (len == linelen+1) buffer_putnoflush(&b1, "...", 3) ;
    217           buffer_putnoflush(&b1, "\n", 1) ;
    218         }
    219       }
    220     }
    221   }
    222   return 0 ;
    223 }