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, ¬if)) 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 }