svlogd: new applet. +9k. Still too big, but it was 12k yesterday.

This commit is contained in:
Denis Vlasenko 2006-11-16 02:27:24 +00:00
parent 3672fe9e91
commit 83ea643d8d
11 changed files with 2342 additions and 18 deletions

View File

@ -270,6 +270,7 @@ USE_STTY(APPLET(stty, _BB_DIR_BIN, _BB_SUID_NEVER))
USE_SU(APPLET(su, _BB_DIR_BIN, _BB_SUID_ALWAYS))
USE_SULOGIN(APPLET(sulogin, _BB_DIR_SBIN, _BB_SUID_NEVER))
USE_SUM(APPLET(sum, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
USE_SVLOGD(APPLET(svlogd, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
USE_SWAPONOFF(APPLET_ODDNAME(swapoff, swap_on_off, _BB_DIR_SBIN, _BB_SUID_NEVER,swapoff))
USE_SWAPONOFF(APPLET_ODDNAME(swapon, swap_on_off, _BB_DIR_SBIN, _BB_SUID_NEVER, swapon))
USE_SWITCH_ROOT(APPLET(switch_root, _BB_DIR_SBIN, _BB_SUID_NEVER))

View File

@ -335,6 +335,8 @@ long xatol_range(const char *numstr, long lower, long upper);
long xatol_sfx(const char *numstr, const struct suffix_mult *suffixes);
long xatol(const char *numstr);
/* Specialized: */
unsigned xatou_range(const char *numstr, unsigned lower, unsigned upper);
unsigned xatou_sfx(const char *numstr, const struct suffix_mult *suffixes);
unsigned xatou(const char *numstr);
int xatoi_range(const char *numstr, int lower, int upper);
int xatoi(const char *numstr);

View File

@ -2841,6 +2841,13 @@ USE_FEATURE_START_STOP_DAEMON_FANCY( \
"\t-r\tuse BSD sum algorithm (1K blocks)\n" \
"\t-s\tuse System V sum algorithm (512byte blocks)"
#define svlogd_trivial_usage \
"[-ttv] [-r c] [-R abc] [-l len] [-b buflen] dir..."
#define svlogd_full_usage \
"Continuously read log data from standard input, optionally " \
"filter log messages, and write the data to one or more automatically " \
"rotated logs."
#define swapoff_trivial_usage \
"[-a] [DEVICE]"
#define swapoff_full_usage \

View File

@ -195,6 +195,16 @@ long xatol(const char *numstr)
/* Others */
unsigned xatou_range(const char *numstr, unsigned lower, unsigned upper)
{
return xstrtoul_range_sfx(numstr, 10, lower, upper, NULL);
}
unsigned xatou_sfx(const char *numstr, const struct suffix_mult *suffixes)
{
return xstrtoul_range_sfx(numstr, 10, 0, UINT_MAX, suffixes);
}
unsigned xatou(const char *numstr)
{
return xatoul_range(numstr, 0, UINT_MAX);

View File

@ -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

View File

@ -6,3 +6,4 @@
lib-y:=
lib-$(CONFIG_CHPST) += chpst.o
lib-$(CONFIG_SVLOGD) += svlogd.o runit_lib.o

View File

@ -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
}
}

999
runit/runit_lib.c Normal file
View File

@ -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;
}

403
runit/runit_lib.h Normal file
View File

@ -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)

880
runit/svlogd.c Normal file
View File

@ -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, &timestamp, &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);
}