879 lines
21 KiB
C
879 lines
21 KiB
C
/*
|
|
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')
|
|
full_write(2, line, printlen);
|
|
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].matcherr == 'e')
|
|
full_write(2, lineptr, linelen);
|
|
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);
|
|
}
|