diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2006-11-16 05:27:24 +0300 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2006-11-16 05:27:24 +0300 |
commit | 83ea643d8dc9b6f53706ba30bc4b53338f4f7994 (patch) | |
tree | 3a5f790604219213666cbf5cccd747c02528912c /runit | |
parent | 3672fe9e9141c0684cae1e72e84cb2704f2a8702 (diff) |
svlogd: new applet. +9k. Still too big, but it was 12k yesterday.
Diffstat (limited to 'runit')
-rw-r--r-- | runit/Config.in | 8 | ||||
-rw-r--r-- | runit/Kbuild | 1 | ||||
-rw-r--r-- | runit/chpst.c | 47 | ||||
-rw-r--r-- | runit/runit_lib.c | 999 | ||||
-rw-r--r-- | runit/runit_lib.h | 403 | ||||
-rw-r--r-- | runit/svlogd.c | 880 |
6 files changed, 2321 insertions, 17 deletions
diff --git a/runit/Config.in b/runit/Config.in index b90b0232e..c849c074c 100644 --- a/runit/Config.in +++ b/runit/Config.in @@ -5,6 +5,14 @@ menu "Runit Utilities" +config SVLOGD + bool "svlogd" + default n + help + svlogd continuously reads log data from its standard input, optionally + filters log messages, and writes the data to one or more automatically + rotated logs. + config CHPST bool "chpst" default n diff --git a/runit/Kbuild b/runit/Kbuild index 39a9b0229..730035632 100644 --- a/runit/Kbuild +++ b/runit/Kbuild @@ -6,3 +6,4 @@ lib-y:= lib-$(CONFIG_CHPST) += chpst.o +lib-$(CONFIG_SVLOGD) += svlogd.o runit_lib.o diff --git a/runit/chpst.c b/runit/chpst.c index de6a33764..3fcef8eec 100644 --- a/runit/chpst.c +++ b/runit/chpst.c @@ -26,6 +26,7 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */ +/* Dependencies on runit_lib.c removed */ #include "busybox.h" @@ -94,7 +95,9 @@ static void edir(const char *directory_name) errno = 0; d = readdir(dir); if (!d) { - if (errno) bb_perror_msg_and_die("readdir %s", directory_name); + if (errno) + bb_perror_msg_and_die("readdir %s", + directory_name); break; } if (d->d_name[0] == '.') continue; @@ -102,12 +105,12 @@ static void edir(const char *directory_name) if (fd < 0) { if ((errno == EISDIR) && env_dir) { if (OPT_verbose) - bb_perror_msg("warning: %s/%s is a directory", directory_name, - d->d_name); + bb_perror_msg("warning: %s/%s is a directory", + directory_name, d->d_name); continue; } else - bb_perror_msg_and_die("open %s/%s", directory_name, /* was exiting 111 */ - d->d_name); + bb_perror_msg_and_die("open %s/%s", + directory_name, d->d_name); } if (fd >= 0) { char buf[256]; @@ -116,8 +119,8 @@ static void edir(const char *directory_name) size = safe_read(fd, buf, sizeof(buf)-1); if (size < 0) - bb_perror_msg_and_die("read %s/%s", directory_name, /* was exiting 111 */ - d->d_name); + bb_perror_msg_and_die("read %s/%s", + directory_name, d->d_name); if (size == 0) { unsetenv(d->d_name); continue; @@ -158,21 +161,24 @@ static void slimit(void) #ifdef RLIMIT_DATA limit(RLIMIT_DATA, limitd); #else - if (OPT_verbose) bb_error_msg("system does not support %s", "RLIMIT_DATA"); + if (OPT_verbose) bb_error_msg("system does not support %s", + "RLIMIT_DATA"); #endif } if (limits >= -1) { #ifdef RLIMIT_STACK limit(RLIMIT_STACK, limits); #else - if (OPT_verbose) bb_error_msg("system does not support %s", "RLIMIT_STACK"); + if (OPT_verbose) bb_error_msg("system does not support %s", + "RLIMIT_STACK"); #endif } if (limitl >= -1) { #ifdef RLIMIT_MEMLOCK limit(RLIMIT_MEMLOCK, limitl); #else - if (OPT_verbose) bb_error_msg("system does not support %s", "RLIMIT_MEMLOCK"); + if (OPT_verbose) bb_error_msg("system does not support %s", + "RLIMIT_MEMLOCK"); #endif } if (limita >= -1) { @@ -183,7 +189,8 @@ static void slimit(void) limit(RLIMIT_AS, limita); #else if (OPT_verbose) - bb_error_msg("system does not support %s", "RLIMIT_VMEM"); + bb_error_msg("system does not support %s", + "RLIMIT_VMEM"); #endif #endif } @@ -195,7 +202,8 @@ static void slimit(void) limit(RLIMIT_OFILE, limito); #else if (OPT_verbose) - bb_error_msg("system does not support %s", "RLIMIT_NOFILE"); + bb_error_msg("system does not support %s", + "RLIMIT_NOFILE"); #endif #endif } @@ -203,35 +211,40 @@ static void slimit(void) #ifdef RLIMIT_NPROC limit(RLIMIT_NPROC, limitp); #else - if (OPT_verbose) bb_error_msg("system does not support %s", "RLIMIT_NPROC"); + if (OPT_verbose) bb_error_msg("system does not support %s", + "RLIMIT_NPROC"); #endif } if (limitf >= -1) { #ifdef RLIMIT_FSIZE limit(RLIMIT_FSIZE, limitf); #else - if (OPT_verbose) bb_error_msg("system does not support %s", "RLIMIT_FSIZE"); + if (OPT_verbose) bb_error_msg("system does not support %s", + "RLIMIT_FSIZE"); #endif } if (limitc >= -1) { #ifdef RLIMIT_CORE limit(RLIMIT_CORE, limitc); #else - if (OPT_verbose) bb_error_msg("system does not support %s", "RLIMIT_CORE"); + if (OPT_verbose) bb_error_msg("system does not support %s", + "RLIMIT_CORE"); #endif } if (limitr >= -1) { #ifdef RLIMIT_RSS limit(RLIMIT_RSS, limitr); #else - if (OPT_verbose) bb_error_msg("system does not support %s", "RLIMIT_RSS"); + if (OPT_verbose) bb_error_msg("system does not support %s", + "RLIMIT_RSS"); #endif } if (limitt >= -1) { #ifdef RLIMIT_CPU limit(RLIMIT_CPU, limitt); #else - if (OPT_verbose) bb_error_msg("system does not support %s", "RLIMIT_CPU"); + if (OPT_verbose) bb_error_msg("system does not support %s", + "RLIMIT_CPU"); #endif } } diff --git a/runit/runit_lib.c b/runit/runit_lib.c new file mode 100644 index 000000000..322bef858 --- /dev/null +++ b/runit/runit_lib.c @@ -0,0 +1,999 @@ +/* +Copyright (c) 2001-2006, Gerrit Pape +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */ +/* Collected into one file from runit's many tiny files */ +/* TODO: review, eliminate unneeded stuff, move good stuff to libbb */ + +#include <sys/poll.h> +#include <sys/file.h> +#include "libbb.h" +#include "runit_lib.h" + +#ifndef O_NONBLOCK +#define O_NONBLOCK O_NDELAY +#endif + +/*** buffer.c ***/ + +void buffer_init(buffer *s,int (*op)(int fd,char *buf,unsigned len),int fd,char *buf,unsigned len) +{ + s->x = buf; + s->fd = fd; + s->op = op; + s->p = 0; + s->n = len; +} + + +/*** buffer_get.c ***/ + +static int oneread(int (*op)(int fd,char *buf,unsigned len),int fd,char *buf,unsigned len) +{ + int r; + + for (;;) { + r = op(fd,buf,len); + if (r == -1) if (errno == EINTR) continue; + return r; + } +} + +static int getthis(buffer *s,char *buf,unsigned len) +{ + if (len > s->p) len = s->p; + s->p -= len; + memcpy(buf,s->x + s->n,len); + s->n += len; + return len; +} + +int buffer_feed(buffer *s) +{ + int r; + + if (s->p) return s->p; + r = oneread(s->op,s->fd,s->x,s->n); + if (r <= 0) return r; + s->p = r; + s->n -= r; + if (s->n > 0) memmove(s->x + s->n,s->x,r); + return r; +} + +int buffer_bget(buffer *s,char *buf,unsigned len) +{ + int r; + + if (s->p > 0) return getthis(s,buf,len); + if (s->n <= len) return oneread(s->op,s->fd,buf,s->n); + r = buffer_feed(s); if (r <= 0) return r; + return getthis(s,buf,len); +} + +int buffer_get(buffer *s,char *buf,unsigned len) +{ + int r; + + if (s->p > 0) return getthis(s,buf,len); + if (s->n <= len) return oneread(s->op,s->fd,buf,len); + r = buffer_feed(s); if (r <= 0) return r; + return getthis(s,buf,len); +} + +char *buffer_peek(buffer *s) +{ + return s->x + s->n; +} + +void buffer_seek(buffer *s,unsigned len) +{ + s->n += len; + s->p -= len; +} + + +/*** buffer_put.c ***/ + +static int allwrite(int (*op)(int fd,char *buf,unsigned len),int fd,const char *buf,unsigned len) +{ + int w; + + while (len) { + w = op(fd,(char*)buf,len); + if (w == -1) { + if (errno == EINTR) continue; + return -1; /* note that some data may have been written */ + } + if (w == 0) ; /* luser's fault */ + buf += w; + len -= w; + } + return 0; +} + +int buffer_flush(buffer *s) +{ + int p; + + p = s->p; + if (!p) return 0; + s->p = 0; + return allwrite(s->op,s->fd,s->x,p); +} + +int buffer_putalign(buffer *s,const char *buf,unsigned len) +{ + unsigned n; + + while (len > (n = s->n - s->p)) { + memcpy(s->x + s->p,buf,n); + s->p += n; + buf += n; + len -= n; + if (buffer_flush(s) == -1) return -1; + } + /* now len <= s->n - s->p */ + memcpy(s->x + s->p,buf,len); + s->p += len; + return 0; +} + +int buffer_put(buffer *s,const char *buf,unsigned len) +{ + unsigned n; + + n = s->n; + if (len > n - s->p) { + if (buffer_flush(s) == -1) return -1; + /* now s->p == 0 */ + if (n < BUFFER_OUTSIZE) n = BUFFER_OUTSIZE; + while (len > s->n) { + if (n > len) n = len; + if (allwrite(s->op,s->fd,buf,n) == -1) return -1; + buf += n; + len -= n; + } + } + /* now len <= s->n - s->p */ + memcpy(s->x + s->p,buf,len); + s->p += len; + return 0; +} + +int buffer_putflush(buffer *s,const char *buf,unsigned len) +{ + if (buffer_flush(s) == -1) return -1; + return allwrite(s->op,s->fd,buf,len); +} + +int buffer_putsalign(buffer *s,const char *buf) +{ + return buffer_putalign(s,buf,strlen(buf)); +} + +int buffer_puts(buffer *s,const char *buf) +{ + return buffer_put(s,buf,strlen(buf)); +} + +int buffer_putsflush(buffer *s,const char *buf) +{ + return buffer_putflush(s,buf,strlen(buf)); +} + + +/*** buffer_read.c ***/ + +int buffer_unixread(int fd,char *buf,unsigned len) +{ + return read(fd,buf,len); +} + + +/*** buffer_write.c ***/ + +int buffer_unixwrite(int fd,char *buf,unsigned len) +{ + return write(fd,buf,len); +} + + +/*** byte_chr.c ***/ + +unsigned byte_chr(char *s,unsigned n,int c) +{ + char ch; + char *t; + + ch = c; + t = s; + for (;;) { + if (!n) break; if (*t == ch) break; ++t; --n; + if (!n) break; if (*t == ch) break; ++t; --n; + if (!n) break; if (*t == ch) break; ++t; --n; + if (!n) break; if (*t == ch) break; ++t; --n; + } + return t - s; +} + + +/*** coe.c ***/ + +int coe(int fd) +{ + return fcntl(fd,F_SETFD,FD_CLOEXEC); +} + + +/*** fd_copy.c ***/ + +int fd_copy(int to,int from) +{ + if (to == from) return 0; + if (fcntl(from,F_GETFL,0) == -1) return -1; + close(to); + if (fcntl(from,F_DUPFD,to) == -1) return -1; + return 0; +} + + +/*** fd_move.c ***/ + +int fd_move(int to,int from) +{ + if (to == from) return 0; + if (fd_copy(to,from) == -1) return -1; + close(from); + return 0; +} + + +/*** fifo.c ***/ + +int fifo_make(const char *fn,int mode) { return mkfifo(fn,mode); } + + +/*** fmt_ptime.c ***/ + +unsigned fmt_ptime(char *s, struct taia *ta) { + struct tm *t; + unsigned long u; + + if (ta->sec.x < 4611686018427387914ULL) return 0; /* impossible? */ + u = ta->sec.x -4611686018427387914ULL; + if (!(t = gmtime((time_t*)&u))) return 0; + fmt_ulong(s, 1900 + t->tm_year); + s[4] = '-'; fmt_uint0(&s[5], t->tm_mon+1, 2); + s[7] = '-'; fmt_uint0(&s[8], t->tm_mday, 2); + s[10] = '_'; fmt_uint0(&s[11], t->tm_hour, 2); + s[13] = ':'; fmt_uint0(&s[14], t->tm_min, 2); + s[16] = ':'; fmt_uint0(&s[17], t->tm_sec, 2); + s[19] = '.'; fmt_uint0(&s[20], ta->nano, 9); + return 25; +} + +unsigned fmt_taia(char *s, struct taia *t) { + static char hex[16] = "0123456789abcdef"; + static char pack[TAIA_PACK]; + int i; + + taia_pack(pack, t); + s[0] = '@'; + for (i = 0; i < 12; ++i) { + s[i*2+1] = hex[(pack[i] >> 4) &15]; + s[i*2+2] = hex[pack[i] &15]; + } + return 25; +} + + +/*** fmt_uint.c ***/ + +unsigned fmt_uint(char *s,unsigned u) +{ + return fmt_ulong(s,u); +} + + +/*** fmt_uint0.c ***/ + +unsigned fmt_uint0(char *s,unsigned u,unsigned n) +{ + unsigned len; + len = fmt_uint(FMT_LEN,u); + while (len < n) { if (s) *s++ = '0'; ++len; } + if (s) fmt_uint(s,u); + return len; +} + + +/*** fmt_ulong.c ***/ + +unsigned fmt_ulong(char *s,unsigned long u) +{ + unsigned len; unsigned long q; + len = 1; q = u; + while (q > 9) { ++len; q /= 10; } + if (s) { + s += len; + do { *--s = '0' + (u % 10); u /= 10; } while(u); /* handles u == 0 */ + } + return len; +} + + +/*** tai_now.c ***/ + +void tai_now(struct tai *t) +{ + tai_unix(t,time((time_t *) 0)); +} + + +/*** tai_pack.c ***/ + +void tai_pack(char *s,const struct tai *t) +{ + uint64_t x; + + x = t->x; + s[7] = x & 255; x >>= 8; + s[6] = x & 255; x >>= 8; + s[5] = x & 255; x >>= 8; + s[4] = x & 255; x >>= 8; + s[3] = x & 255; x >>= 8; + s[2] = x & 255; x >>= 8; + s[1] = x & 255; x >>= 8; + s[0] = x; +} + + +/*** tai_sub.c ***/ + +void tai_sub(struct tai *t,const struct tai *u,const struct tai *v) +{ + t->x = u->x - v->x; +} + + +/*** tai_unpack.c ***/ + +void tai_unpack(const char *s,struct tai *t) +{ + uint64_t x; + + x = (unsigned char) s[0]; + x <<= 8; x += (unsigned char) s[1]; + x <<= 8; x += (unsigned char) s[2]; + x <<= 8; x += (unsigned char) s[3]; + x <<= 8; x += (unsigned char) s[4]; + x <<= 8; x += (unsigned char) s[5]; + x <<= 8; x += (unsigned char) s[6]; + x <<= 8; x += (unsigned char) s[7]; + t->x = x; +} + + +/*** taia_add.c ***/ + +/* XXX: breaks tai encapsulation */ + +void taia_add(struct taia *t,const struct taia *u,const struct taia *v) +{ + t->sec.x = u->sec.x + v->sec.x; + t->nano = u->nano + v->nano; + t->atto = u->atto + v->atto; + if (t->atto > 999999999UL) { + t->atto -= 1000000000UL; + ++t->nano; + } + if (t->nano > 999999999UL) { + t->nano -= 1000000000UL; + ++t->sec.x; + } +} + + +/*** taia_approx.c ***/ + +double taia_approx(const struct taia *t) +{ + return tai_approx(&t->sec) + taia_frac(t); +} + + +/*** taia_frac.c ***/ + +double taia_frac(const struct taia *t) +{ + return (t->atto * 0.000000001 + t->nano) * 0.000000001; +} + + +/*** taia_less.c ***/ + +/* XXX: breaks tai encapsulation */ + +int taia_less(const struct taia *t,const struct taia *u) +{ + if (t->sec.x < u->sec.x) return 1; + if (t->sec.x > u->sec.x) return 0; + if (t->nano < u->nano) return 1; + if (t->nano > u->nano) return 0; + return t->atto < u->atto; +} + + +/*** taia_now.c ***/ + +void taia_now(struct taia *t) +{ + struct timeval now; + gettimeofday(&now,(struct timezone *) 0); + tai_unix(&t->sec,now.tv_sec); + t->nano = 1000 * now.tv_usec + 500; + t->atto = 0; +} + + +/*** taia_pack.c ***/ + +void taia_pack(char *s,const struct taia *t) +{ + unsigned long x; + + tai_pack(s,&t->sec); + s += 8; + + x = t->atto; + s[7] = x & 255; x >>= 8; + s[6] = x & 255; x >>= 8; + s[5] = x & 255; x >>= 8; + s[4] = x; + x = t->nano; + s[3] = x & 255; x >>= 8; + s[2] = x & 255; x >>= 8; + s[1] = x & 255; x >>= 8; + s[0] = x; +} + + +/*** taia_sub.c ***/ + +/* XXX: breaks tai encapsulation */ + +void taia_sub(struct taia *t,const struct taia *u,const struct taia *v) +{ + unsigned long unano = u->nano; + unsigned long uatto = u->atto; + + t->sec.x = u->sec.x - v->sec.x; + t->nano = unano - v->nano; + t->atto = uatto - v->atto; + if (t->atto > uatto) { + t->atto += 1000000000UL; + --t->nano; + } + if (t->nano > unano) { + t->nano += 1000000000UL; + --t->sec.x; + } +} + + +/*** taia_uint.c ***/ + +/* XXX: breaks tai encapsulation */ + +void taia_uint(struct taia *t,unsigned s) +{ + t->sec.x = s; + t->nano = 0; + t->atto = 0; +} + + +/*** stralloc_cat.c ***/ + +int stralloc_cat(stralloc *sato,const stralloc *safrom) +{ + return stralloc_catb(sato,safrom->s,safrom->len); +} + + +/*** stralloc_catb.c ***/ + +int stralloc_catb(stralloc *sa,const char *s,unsigned n) +{ + if (!sa->s) return stralloc_copyb(sa,s,n); + if (!stralloc_readyplus(sa,n + 1)) return 0; + memcpy(sa->s + sa->len,s,n); + sa->len += n; + sa->s[sa->len] = 'Z'; /* ``offensive programming'' */ + return 1; +} + + +/*** stralloc_cats.c ***/ + +int stralloc_cats(stralloc *sa,const char *s) +{ + return stralloc_catb(sa,s,strlen(s)); +} + + +/*** stralloc_eady.c ***/ + +GEN_ALLOC_ready(stralloc,char,s,len,a,i,n,x,30,stralloc_ready) +GEN_ALLOC_readyplus(stralloc,char,s,len,a,i,n,x,30,stralloc_readyplus) + + +/*** stralloc_opyb.c ***/ + +int stralloc_copyb(stralloc *sa,const char *s,unsigned n) +{ + if (!stralloc_ready(sa,n + 1)) return 0; + memcpy(sa->s,s,n); + sa->len = n; + sa->s[n] = 'Z'; /* ``offensive programming'' */ + return 1; +} + + +/*** stralloc_opys.c ***/ + +int stralloc_copys(stralloc *sa,const char *s) +{ + return stralloc_copyb(sa,s,strlen(s)); +} + + +/*** stralloc_pend.c ***/ + +GEN_ALLOC_append(stralloc,char,s,len,a,i,n,x,30,stralloc_readyplus,stralloc_append) + + +/*** iopause.c ***/ + +void iopause(iopause_fd *x,unsigned len,struct taia *deadline,struct taia *stamp) +{ + struct taia t; + int millisecs; + double d; + int i; + + if (taia_less(deadline,stamp)) + millisecs = 0; + else { + t = *stamp; + taia_sub(&t,deadline,&t); + d = taia_approx(&t); + if (d > 1000.0) d = 1000.0; + millisecs = d * 1000.0 + 20.0; + } + + for (i = 0;i < len;++i) + x[i].revents = 0; + + poll(x,len,millisecs); + /* XXX: some kernels apparently need x[0] even if len is 0 */ + /* XXX: how to handle EAGAIN? are kernels really this dumb? */ + /* XXX: how to handle EINVAL? when exactly can this happen? */ +} + + +/*** lock_ex.c ***/ + +int lock_ex(int fd) +{ + return flock(fd,LOCK_EX); +} + + +/*** lock_exnb.c ***/ + +int lock_exnb(int fd) +{ + return flock(fd,LOCK_EX | LOCK_NB); +} + + +/*** ndelay_off.c ***/ + +int ndelay_off(int fd) +{ + return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) & ~O_NONBLOCK); +} + + +/*** ndelay_on.c ***/ + +int ndelay_on(int fd) +{ + return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) | O_NONBLOCK); +} + + +/*** open_append.c ***/ + +int open_append(const char *fn) +{ + return open(fn,O_WRONLY | O_NDELAY | O_APPEND | O_CREAT,0600); +} + + +/*** open_read.c ***/ + +int open_read(const char *fn) +{ + return open(fn,O_RDONLY | O_NDELAY); +} + + +/*** open_trunc.c ***/ + +int open_trunc(const char *fn) +{ + return open(fn,O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT,0644); +} + + +/*** open_write.c ***/ + +int open_write(const char *fn) +{ + return open(fn,O_WRONLY | O_NDELAY); +} + + +/*** openreadclose.c ***/ + +int openreadclose(const char *fn,stralloc *sa,unsigned bufsize) +{ + int fd; + fd = open_read(fn); + if (fd == -1) { + if (errno == ENOENT) return 0; + return -1; + } + if (readclose(fd,sa,bufsize) == -1) return -1; + return 1; +} + + +/*** pathexec_env.c ***/ + +static stralloc plus; +static stralloc tmp; + +int pathexec_env(const char *s,const char *t) +{ + if (!s) return 1; + if (!stralloc_copys(&tmp,s)) return 0; + if (t) { + if (!stralloc_cats(&tmp,"=")) return 0; + if (!stralloc_cats(&tmp,t)) return 0; + } + if (!stralloc_0(&tmp)) return 0; + return stralloc_cat(&plus,&tmp); +} + +void pathexec(char **argv) +{ + char **e; + unsigned elen; + unsigned i; + unsigned j; + unsigned split; + unsigned t; + + if (!stralloc_cats(&plus,"")) return; + + elen = 0; + for (i = 0;environ[i];++i) + ++elen; + for (i = 0;i < plus.len;++i) + if (!plus.s[i]) + ++elen; + + e = malloc((elen + 1) * sizeof(char *)); + if (!e) return; + + elen = 0; + for (i = 0;environ[i];++i) + e[elen++] = environ[i]; + + j = 0; + for (i = 0;i < plus.len;++i) + if (!plus.s[i]) { + split = str_chr(plus.s + j,'='); + for (t = 0;t < elen;++t) + if (memcmp(plus.s + j,e[t],split) == 0) + if (e[t][split] == '=') { + --elen; + e[t] = e[elen]; + break; + } + if (plus.s[j + split]) + e[elen++] = plus.s + j; + j = i + 1; + } + e[elen] = 0; + + pathexec_run(*argv,argv,e); + free(e); +} + + +/*** pathexec_run.c ***/ + +static stralloc tmp; + +void pathexec_run(const char *file,char *const *argv,char *const *envp) +{ + const char *path; + unsigned split; + int savederrno; + + if (file[str_chr(file,'/')]) { + execve(file,argv,envp); + return; + } + + path = getenv("PATH"); + if (!path) path = "/bin:/usr/bin"; + + savederrno = 0; + for (;;) { + split = str_chr(path,':'); + if (!stralloc_copyb(&tmp,path,split)) return; + if (!split) + if (!stralloc_cats(&tmp,".")) return; + if (!stralloc_cats(&tmp,"/")) return; + if (!stralloc_cats(&tmp,file)) return; + if (!stralloc_0(&tmp)) return; + + execve(tmp.s,argv,envp); + if (errno != ENOENT) { + savederrno = errno; + if ((errno != EACCES) && (errno != EPERM) && (errno != EISDIR)) return; + } + + if (!path[split]) { + if (savederrno) errno = savederrno; + return; + } + path += split; + path += 1; + } +} + + +/*** pmatch.c ***/ + +unsigned pmatch(const char *p, const char *s, unsigned len) { + for (;;) { + char c = *p++; + if (!c) return !len; + switch (c) { + case '*': + if (!(c = *p)) return 1; + for (;;) { + if (!len) return 0; + if (*s == c) break; + ++s; --len; + } + continue; + case '+': + if ((c = *p++) != *s) return 0; + for (;;) { + if (!len) return 1; + if (*s != c) break; + ++s; --len; + } + continue; + /* + case '?': + if (*p == '?') { + if (*s != '?') return 0; + ++p; + } + ++s; --len; + continue; + */ + default: + if (!len) return 0; + if (*s != c) return 0; + ++s; --len; + continue; + } + } + return 0; +} + + +/*** prot.c ***/ + +int prot_gid(int gid) +{ + gid_t x = gid; + if (setgroups(1,&x) == -1) return -1; + return setgid(gid); /* _should_ be redundant, but on some systems it isn't */ +} + +int prot_uid(int uid) +{ + return setuid(uid); +} + + +/*** readclose.c ***/ + +int readclose_append(int fd,stralloc *sa,unsigned bufsize) +{ + int r; + for (;;) { + if (!stralloc_readyplus(sa,bufsize)) { close(fd); return -1; } + r = read(fd,sa->s + sa->len,bufsize); + if (r == -1) if (errno == EINTR) continue; + if (r <= 0) { close(fd); return r; } + sa->len += r; + } +} + +int readclose(int fd,stralloc *sa,unsigned bufsize) +{ + if (!stralloc_copys(sa,"")) { close(fd); return -1; } + return readclose_append(fd,sa,bufsize); +} + + +/*** scan_ulong.c ***/ + +unsigned scan_ulong(const char *s,unsigned long *u) +{ + unsigned pos = 0; + unsigned long result = 0; + unsigned long c; + while ((c = (unsigned long) (unsigned char) (s[pos] - '0')) < 10) { + result = result * 10 + c; + ++pos; + } + *u = result; + return pos; +} + + +/*** seek_set.c ***/ + +int seek_set(int fd,seek_pos pos) +{ + if (lseek(fd,(off_t) pos,SEEK_SET) == -1) return -1; return 0; +} + + +/*** sig.c ***/ + +int sig_alarm = SIGALRM; +int sig_child = SIGCHLD; +int sig_cont = SIGCONT; +int sig_hangup = SIGHUP; +int sig_int = SIGINT; +int sig_pipe = SIGPIPE; +int sig_term = SIGTERM; + +void (*sig_defaulthandler)(int) = SIG_DFL; +void (*sig_ignorehandler)(int) = SIG_IGN; + + +/*** sig_block.c ***/ + +void sig_block(int sig) +{ + sigset_t ss; + sigemptyset(&ss); + sigaddset(&ss,sig); + sigprocmask(SIG_BLOCK,&ss,(sigset_t *) 0); +} + +void sig_unblock(int sig) +{ + sigset_t ss; + sigemptyset(&ss); + sigaddset(&ss,sig); + sigprocmask(SIG_UNBLOCK,&ss,(sigset_t *) 0); +} + +void sig_blocknone(void) +{ + sigset_t ss; + sigemptyset(&ss); + sigprocmask(SIG_SETMASK,&ss,(sigset_t *) 0); +} + + +/*** sig_catch.c ***/ + +void sig_catch(int sig,void (*f)(int)) +{ + struct sigaction sa; + sa.sa_handler = f; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sigaction(sig,&sa,(struct sigaction *) 0); +} + + +/*** sig_pause.c ***/ + +void sig_pause(void) +{ + sigset_t ss; + sigemptyset(&ss); + sigsuspend(&ss); +} + + +/*** str_chr.c ***/ + +unsigned str_chr(const char *s,int c) +{ + char ch; + const char *t; + + ch = c; + t = s; + for (;;) { + if (!*t) break; if (*t == ch) break; ++t; + if (!*t) break; if (*t == ch) break; ++t; + if (!*t) break; if (*t == ch) break; ++t; + if (!*t) break; if (*t == ch) break; ++t; + } + return t - s; +} + + +/*** wait_nohang.c ***/ + +int wait_nohang(int *wstat) +{ + return waitpid(-1,wstat,WNOHANG); +} + + +/*** wait_pid.c ***/ + +int wait_pid(int *wstat, int pid) +{ + int r; + + do + r = waitpid(pid,wstat,0); + while ((r == -1) && (errno == EINTR)); + return r; +} diff --git a/runit/runit_lib.h b/runit/runit_lib.h new file mode 100644 index 000000000..d306164ea --- /dev/null +++ b/runit/runit_lib.h @@ -0,0 +1,403 @@ +/* +Copyright (c) 2001-2006, Gerrit Pape +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*** buffer.h ***/ + +typedef struct buffer { + char *x; + unsigned p; + unsigned n; + int fd; + int (*op)(int fd,char *buf,unsigned len); +} buffer; + +#define BUFFER_INIT(op,fd,buf,len) { (buf), 0, (len), (fd), (op) } +#define BUFFER_INSIZE 8192 +#define BUFFER_OUTSIZE 8192 + +extern void buffer_init(buffer *,int (*)(int fd,char *buf,unsigned len),int,char *,unsigned); + +extern int buffer_flush(buffer *); +extern int buffer_put(buffer *,const char *,unsigned); +extern int buffer_putalign(buffer *,const char *,unsigned); +extern int buffer_putflush(buffer *,const char *,unsigned); +extern int buffer_puts(buffer *,const char *); +extern int buffer_putsalign(buffer *,const char *); +extern int buffer_putsflush(buffer *,const char *); + +#define buffer_PUTC(s,c) \ + ( ((s)->n != (s)->p) \ + ? ( (s)->x[(s)->p++] = (c), 0 ) \ + : buffer_put((s),&(c),1) \ + ) + +extern int buffer_get(buffer *,char *,unsigned); +extern int buffer_bget(buffer *,char *,unsigned); +extern int buffer_feed(buffer *); + +extern char *buffer_peek(buffer *); +extern void buffer_seek(buffer *,unsigned); + +#define buffer_PEEK(s) ( (s)->x + (s)->n ) +#define buffer_SEEK(s,len) ( ( (s)->p -= (len) ) , ( (s)->n += (len) ) ) + +#define buffer_GETC(s,c) \ + ( ((s)->p > 0) \ + ? ( *(c) = (s)->x[(s)->n], buffer_SEEK((s),1), 1 ) \ + : buffer_get((s),(c),1) \ + ) + +extern int buffer_copy(buffer *,buffer *); + +extern int buffer_unixread(int,char *,unsigned); +/* Actually, int buffer_unixwrite(int,const char *,unsigned), + but that 'const' will produce warnings... oh well */ +extern int buffer_unixwrite(int,char *,unsigned); + + +/*** byte.h ***/ + +extern unsigned byte_chr(char *s,unsigned n,int c); + + +/*** coe.h ***/ + +extern int coe(int); + + +/*** direntry.h ***/ + +#define direntry struct dirent + + +/*** fd.h ***/ + +extern int fd_copy(int,int); +extern int fd_move(int,int); + + +/*** fifo.h ***/ + +extern int fifo_make(const char *,int); + + +/*** fmt.h ***/ + +#define FMT_ULONG 40 /* enough space to hold 2^128 - 1 in decimal, plus \0 */ +#define FMT_LEN ((char *) 0) /* convenient abbreviation */ + +extern unsigned fmt_uint(char *,unsigned); +extern unsigned fmt_uint0(char *,unsigned,unsigned); +extern unsigned fmt_xint(char *,unsigned); +extern unsigned fmt_nbbint(char *,unsigned,unsigned,unsigned,unsigned); +extern unsigned fmt_ushort(char *,unsigned short); +extern unsigned fmt_xshort(char *,unsigned short); +extern unsigned fmt_nbbshort(char *,unsigned,unsigned,unsigned,unsigned short); +extern unsigned fmt_ulong(char *,unsigned long); +extern unsigned fmt_xlong(char *,unsigned long); +extern unsigned fmt_nbblong(char *,unsigned,unsigned,unsigned,unsigned long); + +extern unsigned fmt_plusminus(char *,int); +extern unsigned fmt_minus(char *,int); +extern unsigned fmt_0x(char *,int); + +extern unsigned fmt_str(char *,const char *); +extern unsigned fmt_strn(char *,const char *,unsigned); + + +/*** tai.h ***/ + +struct tai { + uint64_t x; +} ; + +#define tai_unix(t,u) ((void) ((t)->x = 4611686018427387914ULL + (uint64_t) (u))) + +extern void tai_now(struct tai *); + +#define tai_approx(t) ((double) ((t)->x)) + +extern void tai_add(struct tai *,const struct tai *,const struct tai *); +extern void tai_sub(struct tai *,const struct tai *,const struct tai *); +#define tai_less(t,u) ((t)->x < (u)->x) + +#define TAI_PACK 8 +extern void tai_pack(char *,const struct tai *); +extern void tai_unpack(const char *,struct tai *); + +extern void tai_uint(struct tai *,unsigned); + + +/*** taia.h ***/ + +struct taia { + struct tai sec; + unsigned long nano; /* 0...999999999 */ + unsigned long atto; /* 0...999999999 */ +} ; + +extern void taia_tai(const struct taia *,struct tai *); + +extern void taia_now(struct taia *); + +extern double taia_approx(const struct taia *); +extern double taia_frac(const struct taia *); + +extern void taia_add(struct taia *,const struct taia *,const struct taia *); +extern void taia_addsec(struct taia *,const struct taia *,int); +extern void taia_sub(struct taia *,const struct taia *,const struct taia *); +extern void taia_half(struct taia *,const struct taia *); +extern int taia_less(const struct taia *,const struct taia *); + +#define TAIA_PACK 16 +extern void taia_pack(char *,const struct taia *); +extern void taia_unpack(const char *,struct taia *); + +#define TAIA_FMTFRAC 19 +extern unsigned taia_fmtfrac(char *,const struct taia *); + +extern void taia_uint(struct taia *,unsigned); + + +/*** fmt_ptime.h ***/ + +#define FMT_PTIME 30 + +extern unsigned fmt_ptime(char *, struct taia *); +extern unsigned fmt_taia(char *, struct taia *); + + +/*** gen_alloc.h ***/ + +#define GEN_ALLOC_typedef(ta,type,field,len,a) \ + typedef struct ta { type *field; unsigned len; unsigned a; } ta; + + +/*** gen_allocdefs.h ***/ + +#define GEN_ALLOC_ready(ta,type,field,len,a,i,n,x,base,ta_ready) \ +int ta_ready(ta *x,unsigned n) \ +{ unsigned i; \ + if (x->field) { \ + i = x->a; \ + if (n > i) { \ + x->a = base + n + (n >> 3); \ + x->field = realloc(x->field,x->a * sizeof(type)); \ + if (x->field) return 1; \ + x->a = i; return 0; } \ + return 1; } \ + x->len = 0; \ + return !!(x->field = malloc((x->a = n) * sizeof(type))); } + +#define GEN_ALLOC_readyplus(ta,type,field,len,a,i,n,x,base,ta_rplus) \ +int ta_rplus(ta *x,unsigned n) \ +{ unsigned i; \ + if (x->field) { \ + i = x->a; n += x->len; \ + if (n > i) { \ + x->a = base + n + (n >> 3); \ + x->field = realloc(x->field,x->a * sizeof(type)); \ + if (x->field) return 1; \ + x->a = i; return 0; } \ + return 1; } \ + x->len = 0; \ + return !!(x->field = malloc((x->a = n) * sizeof(type))); } + +#define GEN_ALLOC_append(ta,type,field,len,a,i,n,x,base,ta_rplus,ta_append) \ +int ta_append(ta *x,const type *i) \ +{ if (!ta_rplus(x,1)) return 0; x->field[x->len++] = *i; return 1; } + + +/*** stralloc.h ***/ + +GEN_ALLOC_typedef(stralloc,char,s,len,a) + +extern int stralloc_ready(stralloc *,unsigned); +extern int stralloc_readyplus(stralloc *,unsigned); +extern int stralloc_copy(stralloc *,const stralloc *); +extern int stralloc_cat(stralloc *,const stralloc *); +extern int stralloc_copys(stralloc *,const char *); +extern int stralloc_cats(stralloc *,const char *); +extern int stralloc_copyb(stralloc *,const char *,unsigned); +extern int stralloc_catb(stralloc *,const char *,unsigned); +extern int stralloc_append(stralloc *,const char *); /* beware: this takes a pointer to 1 char */ +extern int stralloc_starts(stralloc *,const char *); + +#define stralloc_0(sa) stralloc_append(sa,"") + +extern int stralloc_catulong0(stralloc *,unsigned long,unsigned); +extern int stralloc_catlong0(stralloc *,long,unsigned); + +#define stralloc_catlong(sa,l) (stralloc_catlong0((sa),(l),0)) +#define stralloc_catuint0(sa,i,n) (stralloc_catulong0((sa),(i),(n))) +#define stralloc_catint0(sa,i,n) (stralloc_catlong0((sa),(i),(n))) +#define stralloc_catint(sa,i) (stralloc_catlong0((sa),(i),0)) + + +/*** iopause.h ***/ + +typedef struct pollfd iopause_fd; +#define IOPAUSE_READ POLLIN +#define IOPAUSE_WRITE POLLOUT + +extern void iopause(iopause_fd *,unsigned,struct taia *,struct taia *); + + +/*** lock.h ***/ + +extern int lock_ex(int); +extern int lock_un(int); +extern int lock_exnb(int); + + +/*** ndelay.h ***/ + +extern int ndelay_on(int); +extern int ndelay_off(int); + + +/*** open.h ***/ + +extern int open_read(const char *); +extern int open_excl(const char *); +extern int open_append(const char *); +extern int open_trunc(const char *); +extern int open_write(const char *); + + +/*** openreadclose.h ***/ + +extern int openreadclose(const char *,stralloc *,unsigned); + + +/*** pathexec.h ***/ + +extern void pathexec_run(const char *,char *const *,char *const *); +extern int pathexec_env(const char *,const char *); +extern void pathexec(char **); + + +/*** pmatch.h ***/ + +extern unsigned pmatch(const char *, const char *, unsigned); + + +/*** prot.h ***/ + +extern int prot_gid(int); +extern int prot_uid(int); + + +/*** readclose.h ***/ + +extern int readclose_append(int,stralloc *,unsigned); +extern int readclose(int,stralloc *,unsigned); + + +/*** scan.h ***/ + +extern unsigned scan_uint(const char *,unsigned *); +extern unsigned scan_xint(const char *,unsigned *); +extern unsigned scan_nbbint(const char *,unsigned,unsigned,unsigned,unsigned *); +extern unsigned scan_ushort(const char *,unsigned short *); +extern unsigned scan_xshort(const char *,unsigned short *); +extern unsigned scan_nbbshort(const char *,unsigned,unsigned,unsigned,unsigned short *); +extern unsigned scan_ulong(const char *,unsigned long *); +extern unsigned scan_xlong(const char *,unsigned long *); +extern unsigned scan_nbblong(const char *,unsigned,unsigned,unsigned,unsigned long *); + +extern unsigned scan_plusminus(const char *,int *); +extern unsigned scan_0x(const char *,unsigned *); + +extern unsigned scan_whitenskip(const char *,unsigned); +extern unsigned scan_nonwhitenskip(const char *,unsigned); +extern unsigned scan_charsetnskip(const char *,const char *,unsigned); +extern unsigned scan_noncharsetnskip(const char *,const char *,unsigned); + +extern unsigned scan_strncmp(const char *,const char *,unsigned); +extern unsigned scan_memcmp(const char *,const char *,unsigned); + +extern unsigned scan_long(const char *,long *); +extern unsigned scan_8long(const char *,unsigned long *); + + +/*** seek.h ***/ + +typedef unsigned long seek_pos; + +extern seek_pos seek_cur(int); + +extern int seek_set(int,seek_pos); +extern int seek_end(int); + +extern int seek_trunc(int,seek_pos); + +#define seek_begin(fd) (seek_set((fd),(seek_pos) 0)) + + +/*** sig.h ***/ + +extern int sig_alarm; +extern int sig_child; +extern int sig_cont; +extern int sig_hangup; +extern int sig_int; +extern int sig_pipe; +extern int sig_term; + +extern void (*sig_defaulthandler)(int); +extern void (*sig_ignorehandler)(int); + +extern void sig_catch(int,void (*)(int)); +#define sig_ignore(s) (sig_catch((s),sig_ignorehandler)) +#define sig_uncatch(s) (sig_catch((s),sig_defaulthandler)) + +extern void sig_block(int); +extern void sig_unblock(int); +extern void sig_blocknone(void); +extern void sig_pause(void); + +extern void sig_dfl(int); + + +/*** str.h ***/ + +extern unsigned str_chr(const char *,int); /* never returns NULL */ + +#define str_diff(s,t) strcmp((s),(t)) +#define str_equal(s,t) (!strcmp((s),(t))) + + +/*** wait.h ***/ + +extern int wait_pid(int *wstat, int pid); +extern int wait_nohang(int *wstat); + +#define wait_crashed(w) ((w) & 127) +#define wait_exitcode(w) ((w) >> 8) +#define wait_stopsig(w) ((w) >> 8) +#define wait_stopped(w) (((w) & 127) == 127) diff --git a/runit/svlogd.c b/runit/svlogd.c new file mode 100644 index 000000000..8e012d239 --- /dev/null +++ b/runit/svlogd.c @@ -0,0 +1,880 @@ +/* +Copyright (c) 2001-2006, Gerrit Pape +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */ +/* TODO: depends on runit_lib.c - review and reduce/eliminate */ + +#include <sys/poll.h> +#include <sys/file.h> +#include "busybox.h" +#include "runit_lib.h" + +static unsigned verbose; +static int linemax = 1000; +static int buflen = 1024; +static int linelen; + +static char **fndir; +static int fdwdir; +static int wstat; +static struct taia trotate; + +static char *line; +static unsigned exitasap; +static unsigned rotateasap; +static unsigned reopenasap; +static unsigned linecomplete = 1; +static unsigned tmaxflag; +static iopause_fd in; + +static const char *replace = ""; +static char repl; + +static struct logdir { + char *btmp; + /* pattern list to match, in "aa\0bb\0\cc\0\0" form */ + char *inst; + char *processor; + char *name; + unsigned size; + unsigned sizemax; + unsigned nmax; + unsigned nmin; + /* int (not long) because of taia_uint() usage: */ + unsigned tmax; + int ppid; + int fddir; + int fdcur; + int fdlock; + struct taia trotate; + char fnsave[FMT_PTIME]; + char match; + char matcherr; +} *dir; +static unsigned dirn = 0; + +#define FATAL "fatal: " +#define WARNING "warning: " +#define PAUSE "pausing: " +#define INFO "info: " + +#define usage() bb_show_usage() +static void fatalx(char *m0) +{ + bb_error_msg_and_die(FATAL"%s", m0); +} +static void warn(char *m0) { + bb_perror_msg(WARNING"%s", m0); +} +static void warn2(char *m0, char *m1) +{ + bb_perror_msg(WARNING"%s: %s", m0, m1); +} +static void warnx(char *m0, char *m1) +{ + bb_error_msg(WARNING"%s: %s", m0, m1); +} +static void pause_nomem(void) +{ + bb_error_msg(PAUSE"out of memory"); sleep(3); +} +static void pause1cannot(char *m0) +{ + bb_perror_msg(PAUSE"cannot %s", m0); sleep(3); +} +static void pause2cannot(char *m0, char *m1) +{ + bb_perror_msg(PAUSE"cannot %s %s", m0, m1); + sleep(3); +} + +static char* wstrdup(const char *str) +{ + char *s; + while (!(s = strdup(str))) pause_nomem(); + return s; +} + +static unsigned processorstart(struct logdir *ld) +{ + int pid; + + if (!ld->processor) return 0; + if (ld->ppid) { + warnx("processor already running", ld->name); + return 0; + } + while ((pid = fork()) == -1) + pause2cannot("fork for processor", ld->name); + if (!pid) { + char *prog[4]; + int fd; + + /* child */ + sig_uncatch(sig_term); + sig_uncatch(sig_alarm); + sig_uncatch(sig_hangup); + sig_unblock(sig_term); + sig_unblock(sig_alarm); + sig_unblock(sig_hangup); + + if (verbose) + bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave); + fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY); + if (fd_move(0, fd) == -1) + bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name); + ld->fnsave[26] = 't'; + fd = xopen3(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT, 0644); + if (fd_move(1, fd) == -1) + bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name); + fd = open_read("state"); + if (fd == -1) { + if (errno != ENOENT) + bb_perror_msg_and_die(FATAL"cannot %s processor %s", "open state for", ld->name); + close(xopen3("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT, 0644)); + fd = xopen("state", O_RDONLY|O_NDELAY); + } + if (fd_move(4, fd) == -1) + bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name); + fd = xopen3("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT, 0644); + if (fd_move(5, fd) == -1) + bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name); + + prog[0] = "sh"; + prog[1] = "-c"; + prog[2] = ld->processor; + prog[3] = '\0'; + execve("/bin/sh", prog, environ); + bb_perror_msg_and_die(FATAL"cannot %s processor %s", "run", ld->name); + } + ld->ppid = pid; + return 1; +} + +static unsigned processorstop(struct logdir *ld) +{ + char f[28]; + + if (ld->ppid) { + sig_unblock(sig_hangup); + while (wait_pid(&wstat, ld->ppid) == -1) + pause2cannot("wait for processor", ld->name); + sig_block(sig_hangup); + ld->ppid = 0; + } + if (ld->fddir == -1) return 1; + while (fchdir(ld->fddir) == -1) + pause2cannot("change directory, want processor", ld->name); + if (wait_exitcode(wstat) != 0) { + warnx("processor failed, restart", ld->name); + ld->fnsave[26] = 't'; + unlink(ld->fnsave); + ld->fnsave[26] = 'u'; + processorstart(ld); + while (fchdir(fdwdir) == -1) + pause1cannot("change to initial working directory"); + return ld->processor ? 0 : 1; + } + ld->fnsave[26] = 't'; + memcpy(f, ld->fnsave, 26); + f[26] = 's'; + f[27] = '\0'; + while (rename(ld->fnsave, f) == -1) + pause2cannot("rename processed", ld->name); + while (chmod(f, 0744) == -1) + pause2cannot("set mode of processed", ld->name); + ld->fnsave[26] = 'u'; + if (unlink(ld->fnsave) == -1) + bb_error_msg(WARNING"cannot unlink: %s/%s", ld->name, ld->fnsave); + while (rename("newstate", "state") == -1) + pause2cannot("rename state", ld->name); + if (verbose) bb_error_msg(INFO"processed: %s/%s", ld->name, f); + while (fchdir(fdwdir) == -1) + pause1cannot("change to initial working directory"); + return 1; +} + +static void rmoldest(struct logdir *ld) +{ + DIR *d; + struct dirent *f; + char oldest[FMT_PTIME]; + int n = 0; + + oldest[0] = 'A'; oldest[1] = oldest[27] = 0; + while (!(d = opendir("."))) + pause2cannot("open directory, want rotate", ld->name); + errno = 0; + while ((f = readdir(d))) { + if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) { + if (f->d_name[26] == 't') { + if (unlink(f->d_name) == -1) + warn2("cannot unlink processor leftover", f->d_name); + } else { + ++n; + if (strcmp(f->d_name, oldest) < 0) + memcpy(oldest, f->d_name, 27); + } + errno = 0; + } + } + if (errno) warn2("cannot read directory", ld->name); + closedir(d); + + if (ld->nmax && (n > ld->nmax)) { + if (verbose) bb_error_msg(INFO"delete: %s/%s", ld->name, oldest); + if ((*oldest == '@') && (unlink(oldest) == -1)) + warn2("cannot unlink oldest logfile", ld->name); + } +} + +static unsigned rotate(struct logdir *ld) +{ + struct stat st; + struct taia now; + + if (ld->fddir == -1) { + ld->tmax = 0; + return 0; + } + if (ld->ppid) + while(!processorstop(ld)) + /* wait */; + + while (fchdir(ld->fddir) == -1) + pause2cannot("change directory, want rotate", ld->name); + + /* create new filename */ + ld->fnsave[25] = '.'; + ld->fnsave[26] = 's'; + if (ld->processor) + ld->fnsave[26] = 'u'; + ld->fnsave[27] = '\0'; + do { + taia_now(&now); + fmt_taia(ld->fnsave, &now); + errno = 0; + } while ((stat(ld->fnsave, &st) != -1) || (errno != ENOENT)); + + if (ld->tmax && taia_less(&ld->trotate, &now)) { + taia_uint(&ld->trotate, ld->tmax); + taia_add(&ld->trotate, &now, &ld->trotate); + if (taia_less(&ld->trotate, &trotate)) + trotate = ld->trotate; + } + + if (ld->size > 0) { + while (fsync(ld->fdcur) == -1) + pause2cannot("fsync current logfile", ld->name); + while (fchmod(ld->fdcur, 0744) == -1) + pause2cannot("set mode of current", ld->name); + close(ld->fdcur); + if (verbose) { + bb_error_msg(INFO"rename: %s/current %s %u", ld->name, + ld->fnsave, ld->size); + } + while (rename("current", ld->fnsave) == -1) + pause2cannot("rename current", ld->name); + while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1) + pause2cannot("create new current", ld->name); + coe(ld->fdcur); + ld->size = 0; + while (fchmod(ld->fdcur, 0644) == -1) + pause2cannot("set mode of current", ld->name); + rmoldest(ld); + processorstart(ld); + } + + while (fchdir(fdwdir) == -1) + pause1cannot("change to initial working directory"); + return 1; +} + +static int buffer_pwrite(int n, char *s, unsigned len) +{ + int i; + struct logdir *ld = &dir[n]; + + if (ld->sizemax) { + if (ld->size >= ld->sizemax) + rotate(ld); + if (len > (ld->sizemax - ld->size)) + len = ld->sizemax - ld->size; + } + while ((i = write(ld->fdcur, s, len)) == -1) { + if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) { + DIR *d; + struct dirent *f; + char oldest[FMT_PTIME]; + int j = 0; + + while (fchdir(ld->fddir) == -1) + pause2cannot("change directory, want remove old logfile", + ld->name); + oldest[0] = 'A'; + oldest[1] = oldest[27] = '\0'; + while (!(d = opendir("."))) + pause2cannot("open directory, want remove old logfile", + ld->name); + errno = 0; + while ((f = readdir(d))) + if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) { + ++j; + if (strcmp(f->d_name, oldest) < 0) + memcpy(oldest, f->d_name, 27); + } + if (errno) warn2("cannot read directory, want remove old logfile", + ld->name); + closedir(d); + errno = ENOSPC; + if (j > ld->nmin) { + if (*oldest == '@') { + bb_error_msg(WARNING"out of disk space, delete: %s/%s", + ld->name, oldest); + errno = 0; + if (unlink(oldest) == -1) { + warn2("cannot unlink oldest logfile", ld->name); + errno = ENOSPC; + } + while (fchdir(fdwdir) == -1) + pause1cannot("change to initial working directory"); + } + } + } + if (errno) pause2cannot("write to current", ld->name); + } + + ld->size += i; + if (ld->sizemax) + if (s[i-1] == '\n') + if (ld->size >= (ld->sizemax - linemax)) + rotate(ld); + return i; +} + +static void logdir_close(struct logdir *ld) +{ + if (ld->fddir == -1) + return; + if (verbose) + bb_error_msg(INFO"close: %s", ld->name); + close(ld->fddir); + ld->fddir = -1; + if (ld->fdcur == -1) + return; /* impossible */ + while (fsync(ld->fdcur) == -1) + pause2cannot("fsync current logfile", ld->name); + while (fchmod(ld->fdcur, 0744) == -1) + pause2cannot("set mode of current", ld->name); + close(ld->fdcur); + ld->fdcur = -1; + if (ld->fdlock == -1) + return; /* impossible */ + close(ld->fdlock); + ld->fdlock = -1; + free(ld->processor); + ld->processor = NULL; +} + +static unsigned logdir_open(struct logdir *ld, const char *fn) +{ + char buf[128]; + struct taia now; + char *new, *s, *np; + int i; + struct stat st; + + ld->fddir = open(fn, O_RDONLY|O_NDELAY); + if (ld->fddir == -1) { + warn2("cannot open log directory", (char*)fn); + return 0; + } + coe(ld->fddir); + if (fchdir(ld->fddir) == -1) { + logdir_close(ld); + warn2("cannot change directory", (char*)fn); + return 0; + } + ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600); + if ((ld->fdlock == -1) + || (lock_exnb(ld->fdlock) == -1) + ) { + logdir_close(ld); + warn2("cannot lock directory", (char*)fn); + while (fchdir(fdwdir) == -1) + pause1cannot("change to initial working directory"); + return 0; + } + coe(ld->fdlock); + + ld->size = 0; + ld->sizemax = 1000000; + ld->nmax = ld->nmin = 10; + ld->tmax = 0; + ld->name = (char*)fn; + ld->ppid = 0; + ld->match = '+'; + free(ld->inst); ld->inst = NULL; + free(ld->processor); ld->processor = NULL; + + /* read config */ + i = open_read_close("config", buf, sizeof(buf)); + if (i < 0) + warn2("cannot read config", ld->name); + if (i > 0) { + if (verbose) bb_error_msg(INFO"read: %s/config", ld->name); + s = buf; + while (s) { + np = strchr(s, '\n'); + if (np) *np++ = '\0'; + switch (s[0]) { + case '+': + case '-': + case 'e': + case 'E': + while (1) { + int l = asprintf(&new, "%s%s\n", ld->inst?:"", s); + if (l >= 0 && new) break; + pause_nomem(); + } + free(ld->inst); + ld->inst = new; + break; + case 's': { + static const struct suffix_mult km_suffixes[] = { + { "k", 1024 }, + { "m", 1024*1024 }, + { NULL, 0 } + }; + ld->sizemax = xatou_sfx(&s[1], km_suffixes); + break; + } + case 'n': + ld->nmax = xatoi_u(&s[1]); + break; + case 'N': + ld->nmin = xatoi_u(&s[1]); + break; + case 't': { + static const struct suffix_mult mh_suffixes[] = { + { "m", 60 }, + { "h", 60*60 }, + /*{ "d", 24*60*60 },*/ + { NULL, 0 } + }; + ld->tmax = xatou_sfx(&s[1], mh_suffixes); + if (ld->tmax) { + taia_uint(&ld->trotate, ld->tmax); + taia_add(&ld->trotate, &now, &ld->trotate); + if (!tmaxflag || taia_less(&ld->trotate, &trotate)) + trotate = ld->trotate; + tmaxflag = 1; + } + break; + } + case '!': + if (s[1]) { + free(ld->processor); + ld->processor = wstrdup(s); + } + break; + } + s = np; + } + /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */ + s = ld->inst; + while (s) { + np = strchr(s, '\n'); + if (np) *np++ = '\0'; + s = np; + } + } + + /* open current */ + i = stat("current", &st); + if (i != -1) { + if (st.st_size && ! (st.st_mode & S_IXUSR)) { + ld->fnsave[25] = '.'; + ld->fnsave[26] = 'u'; + ld->fnsave[27] = '\0'; + do { + taia_now(&now); + fmt_taia(ld->fnsave, &now); + errno = 0; + } while ((stat(ld->fnsave, &st) != -1) || (errno != ENOENT)); + while (rename("current", ld->fnsave) == -1) + pause2cannot("rename current", ld->name); + rmoldest(ld); + i = -1; + } else { + /* Be paranoid: st.st_size can be not just bigger, but WIDER! */ + /* (bug in original svlogd. remove this comment when fixed there) */ + ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size; + } + } else { + if (errno != ENOENT) { + logdir_close(ld); + warn2("cannot stat current", ld->name); + while (fchdir(fdwdir) == -1) + pause1cannot("change to initial working directory"); + return 0; + } + } + while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1) + pause2cannot("open current", ld->name); + coe(ld->fdcur); + while (fchmod(ld->fdcur, 0644) == -1) + pause2cannot("set mode of current", ld->name); + + if (verbose) { + if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name); + else bb_error_msg(INFO"new: %s/current", ld->name); + } + + while (fchdir(fdwdir) == -1) + pause1cannot("change to initial working directory"); + return 1; +} + +static void logdirs_reopen(void) +{ + struct taia now; + int l; + int ok = 0; + + tmaxflag = 0; + taia_now(&now); + for (l = 0; l < dirn; ++l) { + logdir_close(&dir[l]); + if (logdir_open(&dir[l], fndir[l])) ok = 1; + } + if (!ok) fatalx("no functional log directories"); +} + +/* Used for reading stdin */ +static int buffer_pread(int fd, char *s, unsigned len) +{ + struct taia now; + int i; + + if (rotateasap) { + for (i = 0; i < dirn; ++i) + rotate(dir+i); + rotateasap = 0; + } + if (exitasap) { + if (linecomplete) + return 0; + len = 1; + } + if (reopenasap) { + logdirs_reopen(); + reopenasap = 0; + } + taia_now(&now); + taia_uint(&trotate, 2744); + taia_add(&trotate, &now, &trotate); + for (i = 0; i < dirn; ++i) + if (dir[i].tmax) { + if (taia_less(&dir[i].trotate, &now)) + rotate(dir+i); + if (taia_less(&dir[i].trotate, &trotate)) + trotate = dir[i].trotate; + } + + while (1) { + /* Comment? */ + sig_unblock(sig_term); + sig_unblock(sig_child); + sig_unblock(sig_alarm); + sig_unblock(sig_hangup); + iopause(&in, 1, &trotate, &now); + sig_block(sig_term); + sig_block(sig_child); + sig_block(sig_alarm); + sig_block(sig_hangup); + i = safe_read(fd, s, len); + if (i >= 0) break; + if (errno != EAGAIN) { + warn("cannot read standard input"); + break; + } + /* else: EAGAIN - normal, repeat silently */ + } + + if (i > 0) { + int cnt; + linecomplete = (s[i-1] == '\n'); + if (!repl) return i; + + cnt = i; + while (--cnt >= 0) { + char ch = *s; + if (ch != '\n') { + if (ch < 32 || ch > 126) + *s = repl; + else { + int j; + for (j = 0; replace[j]; ++j) { + if (ch == replace[j]) { + *s = repl; + break; + } + } + } + } + s++; + } + } + return i; +} + + +static void sig_term_handler(int sig_no) +{ + if (verbose) + bb_error_msg(INFO"sig%s received", "term"); + exitasap = 1; +} + +static void sig_child_handler(int sig_no) +{ + int pid, l; + + if (verbose) + bb_error_msg(INFO"sig%s received", "child"); + while ((pid = wait_nohang(&wstat)) > 0) + for (l = 0; l < dirn; ++l) + if (dir[l].ppid == pid) { + dir[l].ppid = 0; + processorstop(&dir[l]); + break; + } +} + +static void sig_alarm_handler(int sig_no) +{ + if (verbose) + bb_error_msg(INFO"sig%s received", "alarm"); + rotateasap = 1; +} + +static void sig_hangup_handler(int sig_no) +{ + if (verbose) + bb_error_msg(INFO"sig%s received", "hangup"); + reopenasap = 1; +} + +static void logmatch(struct logdir *ld) +{ + char *s; + + ld->match = '+'; + ld->matcherr = 'E'; + s = ld->inst; + while (s && s[0]) { + switch (s[0]) { + case '+': + case '-': + if (pmatch(s+1, line, linelen)) + ld->match = s[0]; + break; + case 'e': + case 'E': + if (pmatch(s+1, line, linelen)) + ld->matcherr = s[0]; + break; + } + s += strlen(s) + 1; + } +} + +int svlogd_main(int argc, char **argv) +{ + struct taia now; + char *r,*l,*b; + ssize_t stdin_cnt = 0; + int i; + unsigned opt; + unsigned timestamp = 0; + + opt_complementary = "tt:vv"; + opt = getopt32(argc, argv, "r:R:l:b:tv", + &r, &replace, &l, &b, ×tamp, &verbose); + if (opt & 1) { // -r + repl = r[0]; + if (!repl || r[1]) usage(); + } + if (opt & 2) if (!repl) repl = '_'; // -R + if (opt & 4) { // -l + linemax = xatou_range(l, 0, 1000); + if (linemax == 0) linemax = 1000; + if (linemax < 256) linemax = 256; + } + if (opt & 8) { // -b + buflen = xatoi_u(b); + if (buflen == 0) buflen = 1024; + } + //if (opt & 0x10) timestamp++; // -t + //if (opt & 0x20) verbose++; // -v + if (timestamp > 2) timestamp = 2; + argv += optind; + argc -= optind; + + dirn = argc; + if (dirn <= 0) usage(); + if (buflen <= linemax) usage(); + fdwdir = xopen(".", O_RDONLY|O_NDELAY); + coe(fdwdir); + dir = xmalloc(dirn * sizeof(struct logdir)); + for (i = 0; i < dirn; ++i) { + dir[i].fddir = -1; + dir[i].fdcur = -1; + dir[i].btmp = xmalloc(buflen); + dir[i].ppid = 0; + } + line = xmalloc(linemax + (timestamp ? 26 : 0)); + fndir = argv; + in.fd = 0; + in.events = IOPAUSE_READ; + ndelay_on(in.fd); + + sig_block(sig_term); + sig_block(sig_child); + sig_block(sig_alarm); + sig_block(sig_hangup); + sig_catch(sig_term, sig_term_handler); + sig_catch(sig_child, sig_child_handler); + sig_catch(sig_alarm, sig_alarm_handler); + sig_catch(sig_hangup, sig_hangup_handler); + + logdirs_reopen(); + + /* Each iteration processes one line */ + while (1) { + int printlen; + char *lineptr = line; + char *np; + char ch; + + /* Prepare timestamp if needed */ + if (timestamp) { + char stamp[FMT_PTIME]; + taia_now(&now); + switch (timestamp) { + case 1: + fmt_taia(stamp, &now); + break; + default: /* case 2: */ + fmt_ptime(stamp, &now); + break; + } + memcpy(line, stamp, 25); + line[25] = ' '; + lineptr += 26; + } + + /* lineptr[0..linemax-1] - buffer for stdin */ + /* (possibly has some unprocessed data from prev loop) */ + + /* Refill the buffer if needed */ + np = memchr(lineptr, '\n', stdin_cnt); + i = linemax - stdin_cnt; /* avail. bytes at tail */ + if (i >= 128 && !exitasap && !np) { + int sz = buffer_pread(0, lineptr + stdin_cnt, i); + if (sz <= 0) /* EOF or error on stdin */ + exitasap = 1; + else { + stdin_cnt += sz; + np = memchr(lineptr, '\n', stdin_cnt); + } + } + if (stdin_cnt <= 0 && exitasap) + break; + + /* Search for '\n' (in fact, np already holds the result) */ + linelen = stdin_cnt; + if (np) linelen = np - lineptr + 1; + /* linelen == no of chars incl. '\n' (or == stdin_cnt) */ + ch = lineptr[linelen-1]; + + printlen = linelen + (timestamp ? 26 : 0); + /* write out line[0..printlen-1] to each log destination */ + for (i = 0; i < dirn; ++i) { + struct logdir *ld = &dir[i]; + if (ld->fddir == -1) continue; + if (ld->inst) + logmatch(ld); + if (ld->matcherr == 'e') { + fprintf(stderr, "%.*s%s", + printlen, line, + (ch != '\n') ? "...\n" : "" + ); + } + if (ld->match != '+') continue; + buffer_pwrite(i, line, printlen); + } + + /* If we didn't see '\n' (long input line), */ + /* read/write repeatedly until we see it */ + while (ch != '\n') { + /* lineptr is emptied now, safe to use as buffer */ + stdin_cnt = exitasap ? -1 : buffer_pread(0, lineptr, linemax); + if (stdin_cnt <= 0) { /* EOF or error on stdin */ + lineptr[0] = ch = '\n'; + linelen = 1; + exitasap = 1; + stdin_cnt = 1; + } else { + linelen = stdin_cnt; + np = memchr(lineptr, '\n', stdin_cnt); + if (np) linelen = np - lineptr + 1; + ch = lineptr[linelen-1]; + } + /* linelen == no of chars incl. '\n' (or == stdin_cnt) */ + for (i = 0; i < dirn; ++i) { + if (dir[i].fddir == -1) continue; + if (dir[i].match != '+') continue; + buffer_pwrite(i, lineptr, linelen); + } + } + + /* Move unprocessed data to the front of line */ + stdin_cnt -= linelen; + if (stdin_cnt > 0) /* TODO: slow if buffer is big */ + memmove(lineptr, &lineptr[linelen], stdin_cnt); + } + + for (i = 0; i < dirn; ++i) { + if (dir[i].ppid) + while (!processorstop(&dir[i])) + /* repeat */; + logdir_close(&dir[i]); + } + _exit(0); +} |