1/*
2Copyright (c) 2001-2006, Gerrit Pape
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7
8   1. Redistributions of source code must retain the above copyright notice,
9      this list of conditions and the following disclaimer.
10   2. Redistributions in binary form must reproduce the above copyright
11      notice, this list of conditions and the following disclaimer in the
12      documentation and/or other materials provided with the distribution.
13   3. The name of the author may not be used to endorse or promote products
14      derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
29/* TODO: depends on runit_lib.c - review and reduce/eliminate */
30
31#include <sys/poll.h>
32#include <sys/file.h>
33#include "libbb.h"
34#include "runit_lib.h"
35
36#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
37
38#define FMT_PTIME 30
39
40static unsigned verbose;
41static int linemax = 1000;
42////static int buflen = 1024;
43static int linelen;
44
45static char **fndir;
46static int fdwdir;
47static int wstat;
48static unsigned nearest_rotate;
49
50static char *line;
51static smallint exitasap;
52static smallint rotateasap;
53static smallint reopenasap;
54static smallint linecomplete = 1;
55
56static smallint tmaxflag;
57
58static char repl;
59static const char *replace = "";
60
61static sigset_t *blocked_sigset;
62static int fl_flag_0;
63
64static struct logdir {
65	////char *btmp;
66	/* pattern list to match, in "aa\0bb\0\cc\0\0" form */
67	char *inst;
68	char *processor;
69	char *name;
70	unsigned size;
71	unsigned sizemax;
72	unsigned nmax;
73	unsigned nmin;
74	unsigned rotate_period;
75	int ppid;
76	int fddir;
77	int fdcur;
78	FILE* filecur; ////
79	int fdlock;
80	unsigned next_rotate;
81	char fnsave[FMT_PTIME];
82	char match;
83	char matcherr;
84} *dir;
85static unsigned dirn;
86
87#define FATAL "fatal: "
88#define WARNING "warning: "
89#define PAUSE "pausing: "
90#define INFO "info: "
91
92#define usage() bb_show_usage()
93static void fatalx(const char *m0)
94{
95	bb_error_msg_and_die(FATAL"%s", m0);
96}
97static void warn(const char *m0)
98{
99	bb_perror_msg(WARNING"%s", m0);
100}
101static void warn2(const char *m0, const char *m1)
102{
103	bb_perror_msg(WARNING"%s: %s", m0, m1);
104}
105static void warnx(const char *m0, const char *m1)
106{
107	bb_error_msg(WARNING"%s: %s", m0, m1);
108}
109static void pause_nomem(void)
110{
111	bb_error_msg(PAUSE"out of memory");
112	sleep(3);
113}
114static void pause1cannot(const char *m0)
115{
116	bb_perror_msg(PAUSE"cannot %s", m0);
117	sleep(3);
118}
119static void pause2cannot(const char *m0, const char *m1)
120{
121	bb_perror_msg(PAUSE"cannot %s %s", m0, m1);
122	sleep(3);
123}
124
125static char* wstrdup(const char *str)
126{
127	char *s;
128	while (!(s = strdup(str)))
129		pause_nomem();
130	return s;
131}
132
133/*** ex fmt_ptime.[ch] ***/
134
135/* NUL terminated */
136static void fmt_time_human_30nul(char *s)
137{
138	struct tm *t;
139	struct timeval tv;
140
141	gettimeofday(&tv, NULL);
142	t = gmtime(&(tv.tv_sec));
143	sprintf(s, "%04u-%02u-%02u_%02u:%02u:%02u.%06u000",
144		(unsigned)(1900 + t->tm_year),
145		(unsigned)(t->tm_mon + 1),
146		(unsigned)(t->tm_mday),
147		(unsigned)(t->tm_hour),
148		(unsigned)(t->tm_min),
149		(unsigned)(t->tm_sec),
150		(unsigned)(tv.tv_usec)
151	);
152	/* 4+1 + 2+1 + 2+1 + 2+1 + 2+1 + 2+1 + 9 = */
153	/* 5   + 3   + 3   + 3   + 3   + 3   + 9 = */
154	/* 20 (up to '.' inclusive) + 9 (not including '\0') */
155}
156
157/* NOT terminated! */
158static void fmt_time_bernstein_25(char *s)
159{
160	uint32_t pack[3];
161	struct timeval tv;
162	unsigned sec_hi;
163
164	gettimeofday(&tv, NULL);
165	sec_hi = (0x400000000000000aULL + tv.tv_sec) >> 32;
166	tv.tv_sec = (time_t)(0x400000000000000aULL) + tv.tv_sec;
167	tv.tv_usec *= 1000;
168	/* Network order is big-endian: most significant byte first.
169	 * This is exactly what we want here */
170	pack[0] = htonl(sec_hi);
171	pack[1] = htonl(tv.tv_sec);
172	pack[2] = htonl(tv.tv_usec);
173	*s++ = '@';
174	bin2hex(s, (char*)pack, 12);
175}
176
177static unsigned processorstart(struct logdir *ld)
178{
179	int pid;
180
181	if (!ld->processor) return 0;
182	if (ld->ppid) {
183		warnx("processor already running", ld->name);
184		return 0;
185	}
186	while ((pid = fork()) == -1)
187		pause2cannot("fork for processor", ld->name);
188	if (!pid) {
189		char *prog[4];
190		int fd;
191
192		/* child */
193		signal(SIGTERM, SIG_DFL);
194		signal(SIGALRM, SIG_DFL);
195		signal(SIGHUP, SIG_DFL);
196		sig_unblock(SIGTERM);
197		sig_unblock(SIGALRM);
198		sig_unblock(SIGHUP);
199
200		if (verbose)
201			bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
202		fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
203		xmove_fd(fd, 0);
204		ld->fnsave[26] = 't';
205		fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
206		xmove_fd(fd, 1);
207		fd = open_read("state");
208		if (fd == -1) {
209			if (errno != ENOENT)
210				bb_perror_msg_and_die(FATAL"cannot %s processor %s", "open state for", ld->name);
211			close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
212			fd = xopen("state", O_RDONLY|O_NDELAY);
213		}
214		xmove_fd(fd, 4);
215		fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
216		xmove_fd(fd, 5);
217
218// getenv("SHELL")?
219		prog[0] = (char*)"sh";
220		prog[1] = (char*)"-c";
221		prog[2] = ld->processor;
222		prog[3] = NULL;
223		execve("/bin/sh", prog, environ);
224		bb_perror_msg_and_die(FATAL"cannot %s processor %s", "run", ld->name);
225	}
226	ld->ppid = pid;
227	return 1;
228}
229
230static unsigned processorstop(struct logdir *ld)
231{
232	char f[28];
233
234	if (ld->ppid) {
235		sig_unblock(SIGHUP);
236		while (wait_pid(&wstat, ld->ppid) == -1)
237			pause2cannot("wait for processor", ld->name);
238		sig_block(SIGHUP);
239		ld->ppid = 0;
240	}
241	if (ld->fddir == -1) return 1;
242	while (fchdir(ld->fddir) == -1)
243		pause2cannot("change directory, want processor", ld->name);
244	if (wait_exitcode(wstat) != 0) {
245		warnx("processor failed, restart", ld->name);
246		ld->fnsave[26] = 't';
247		unlink(ld->fnsave);
248		ld->fnsave[26] = 'u';
249		processorstart(ld);
250		while (fchdir(fdwdir) == -1)
251			pause1cannot("change to initial working directory");
252		return ld->processor ? 0 : 1;
253	}
254	ld->fnsave[26] = 't';
255	memcpy(f, ld->fnsave, 26);
256	f[26] = 's';
257	f[27] = '\0';
258	while (rename(ld->fnsave, f) == -1)
259		pause2cannot("rename processed", ld->name);
260	while (chmod(f, 0744) == -1)
261		pause2cannot("set mode of processed", ld->name);
262	ld->fnsave[26] = 'u';
263	if (unlink(ld->fnsave) == -1)
264		bb_error_msg(WARNING"cannot unlink: %s/%s", ld->name, ld->fnsave);
265	while (rename("newstate", "state") == -1)
266		pause2cannot("rename state", ld->name);
267	if (verbose)
268		bb_error_msg(INFO"processed: %s/%s", ld->name, f);
269	while (fchdir(fdwdir) == -1)
270		pause1cannot("change to initial working directory");
271	return 1;
272}
273
274static void rmoldest(struct logdir *ld)
275{
276	DIR *d;
277	struct dirent *f;
278	char oldest[FMT_PTIME];
279	int n = 0;
280
281	oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
282	while (!(d = opendir(".")))
283		pause2cannot("open directory, want rotate", ld->name);
284	errno = 0;
285	while ((f = readdir(d))) {
286		if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
287			if (f->d_name[26] == 't') {
288				if (unlink(f->d_name) == -1)
289					warn2("cannot unlink processor leftover", f->d_name);
290			} else {
291				++n;
292				if (strcmp(f->d_name, oldest) < 0)
293					memcpy(oldest, f->d_name, 27);
294			}
295			errno = 0;
296		}
297	}
298	if (errno)
299		warn2("cannot read directory", ld->name);
300	closedir(d);
301
302	if (ld->nmax && (n > ld->nmax)) {
303		if (verbose)
304			bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
305		if ((*oldest == '@') && (unlink(oldest) == -1))
306			warn2("cannot unlink oldest logfile", ld->name);
307	}
308}
309
310static unsigned rotate(struct logdir *ld)
311{
312	struct stat st;
313	unsigned now;
314
315	if (ld->fddir == -1) {
316		ld->rotate_period = 0;
317		return 0;
318	}
319	if (ld->ppid)
320		while (!processorstop(ld))
321			continue;
322
323	while (fchdir(ld->fddir) == -1)
324		pause2cannot("change directory, want rotate", ld->name);
325
326	/* create new filename */
327	ld->fnsave[25] = '.';
328	ld->fnsave[26] = 's';
329	if (ld->processor)
330		ld->fnsave[26] = 'u';
331	ld->fnsave[27] = '\0';
332	do {
333		fmt_time_bernstein_25(ld->fnsave);
334		errno = 0;
335		stat(ld->fnsave, &st);
336	} while (errno != ENOENT);
337
338	now = monotonic_sec();
339	if (ld->rotate_period && LESS(ld->next_rotate, now)) {
340		ld->next_rotate = now + ld->rotate_period;
341		if (LESS(ld->next_rotate, nearest_rotate))
342			nearest_rotate = ld->next_rotate;
343	}
344
345	if (ld->size > 0) {
346		while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
347			pause2cannot("fsync current logfile", ld->name);
348		while (fchmod(ld->fdcur, 0744) == -1)
349			pause2cannot("set mode of current", ld->name);
350		////close(ld->fdcur);
351		fclose(ld->filecur);
352
353		if (verbose) {
354			bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
355					ld->fnsave, ld->size);
356		}
357		while (rename("current", ld->fnsave) == -1)
358			pause2cannot("rename current", ld->name);
359		while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
360			pause2cannot("create new current", ld->name);
361		/* we presume this cannot fail */
362		ld->filecur = fdopen(ld->fdcur, "a"); ////
363		setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
364		coe(ld->fdcur);
365		ld->size = 0;
366		while (fchmod(ld->fdcur, 0644) == -1)
367			pause2cannot("set mode of current", ld->name);
368		rmoldest(ld);
369		processorstart(ld);
370	}
371
372	while (fchdir(fdwdir) == -1)
373		pause1cannot("change to initial working directory");
374	return 1;
375}
376
377static int buffer_pwrite(int n, char *s, unsigned len)
378{
379	int i;
380	struct logdir *ld = &dir[n];
381
382	if (ld->sizemax) {
383		if (ld->size >= ld->sizemax)
384			rotate(ld);
385		if (len > (ld->sizemax - ld->size))
386			len = ld->sizemax - ld->size;
387	}
388	while (1) {
389		////i = full_write(ld->fdcur, s, len);
390		////if (i != -1) break;
391		i = fwrite(s, 1, len, ld->filecur);
392		if (i == len) break;
393
394		if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
395			DIR *d;
396			struct dirent *f;
397			char oldest[FMT_PTIME];
398			int j = 0;
399
400			while (fchdir(ld->fddir) == -1)
401				pause2cannot("change directory, want remove old logfile",
402							 ld->name);
403			oldest[0] = 'A';
404			oldest[1] = oldest[27] = '\0';
405			while (!(d = opendir(".")))
406				pause2cannot("open directory, want remove old logfile",
407							 ld->name);
408			errno = 0;
409			while ((f = readdir(d)))
410				if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
411					++j;
412					if (strcmp(f->d_name, oldest) < 0)
413						memcpy(oldest, f->d_name, 27);
414				}
415			if (errno) warn2("cannot read directory, want remove old logfile",
416					ld->name);
417			closedir(d);
418			errno = ENOSPC;
419			if (j > ld->nmin) {
420				if (*oldest == '@') {
421					bb_error_msg(WARNING"out of disk space, delete: %s/%s",
422							ld->name, oldest);
423					errno = 0;
424					if (unlink(oldest) == -1) {
425						warn2("cannot unlink oldest logfile", ld->name);
426						errno = ENOSPC;
427					}
428					while (fchdir(fdwdir) == -1)
429						pause1cannot("change to initial working directory");
430				}
431			}
432		}
433		if (errno)
434			pause2cannot("write to current", ld->name);
435	}
436
437	ld->size += i;
438	if (ld->sizemax)
439		if (s[i-1] == '\n')
440			if (ld->size >= (ld->sizemax - linemax))
441				rotate(ld);
442	return i;
443}
444
445static void logdir_close(struct logdir *ld)
446{
447	if (ld->fddir == -1)
448		return;
449	if (verbose)
450		bb_error_msg(INFO"close: %s", ld->name);
451	close(ld->fddir);
452	ld->fddir = -1;
453	if (ld->fdcur == -1)
454		return; /* impossible */
455	while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
456		pause2cannot("fsync current logfile", ld->name);
457	while (fchmod(ld->fdcur, 0744) == -1)
458		pause2cannot("set mode of current", ld->name);
459	////close(ld->fdcur);
460	fclose(ld->filecur);
461	ld->fdcur = -1;
462	if (ld->fdlock == -1)
463		return; /* impossible */
464	close(ld->fdlock);
465	ld->fdlock = -1;
466	free(ld->processor);
467	ld->processor = NULL;
468}
469
470static unsigned logdir_open(struct logdir *ld, const char *fn)
471{
472	char buf[128];
473	unsigned now;
474	char *new, *s, *np;
475	int i;
476	struct stat st;
477
478	now = monotonic_sec();
479
480	ld->fddir = open(fn, O_RDONLY|O_NDELAY);
481	if (ld->fddir == -1) {
482		warn2("cannot open log directory", (char*)fn);
483		return 0;
484	}
485	coe(ld->fddir);
486	if (fchdir(ld->fddir) == -1) {
487		logdir_close(ld);
488		warn2("cannot change directory", (char*)fn);
489		return 0;
490	}
491	ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
492	if ((ld->fdlock == -1)
493	 || (lock_exnb(ld->fdlock) == -1)
494	) {
495		logdir_close(ld);
496		warn2("cannot lock directory", (char*)fn);
497		while (fchdir(fdwdir) == -1)
498			pause1cannot("change to initial working directory");
499		return 0;
500	}
501	coe(ld->fdlock);
502
503	ld->size = 0;
504	ld->sizemax = 1000000;
505	ld->nmax = ld->nmin = 10;
506	ld->rotate_period = 0;
507	ld->name = (char*)fn;
508	ld->ppid = 0;
509	ld->match = '+';
510	free(ld->inst); ld->inst = NULL;
511	free(ld->processor); ld->processor = NULL;
512
513	/* read config */
514	i = open_read_close("config", buf, sizeof(buf));
515	if (i < 0 && errno != ENOENT)
516		bb_perror_msg(WARNING"%s/config", ld->name);
517	if (i > 0) {
518		if (verbose)
519			bb_error_msg(INFO"read: %s/config", ld->name);
520		s = buf;
521		while (s) {
522			np = strchr(s, '\n');
523			if (np)
524				*np++ = '\0';
525			switch (s[0]) {
526			case '+':
527			case '-':
528			case 'e':
529			case 'E':
530				/* Add '\n'-terminated line to ld->inst */
531				while (1) {
532					int l = asprintf(&new, "%s%s\n", ld->inst ? : "", s);
533					if (l >= 0 && new)
534						break;
535					pause_nomem();
536				}
537				free(ld->inst);
538				ld->inst = new;
539				break;
540			case 's': {
541				static const struct suffix_mult km_suffixes[] = {
542					{ "k", 1024 },
543					{ "m", 1024*1024 },
544					{ }
545				};
546				ld->sizemax = xatou_sfx(&s[1], km_suffixes);
547				break;
548			}
549			case 'n':
550				ld->nmax = xatoi_u(&s[1]);
551				break;
552			case 'N':
553				ld->nmin = xatoi_u(&s[1]);
554				break;
555			case 't': {
556				static const struct suffix_mult mh_suffixes[] = {
557					{ "m", 60 },
558					{ "h", 60*60 },
559					/*{ "d", 24*60*60 },*/
560					{ }
561				};
562				ld->rotate_period = xatou_sfx(&s[1], mh_suffixes);
563				if (ld->rotate_period) {
564					ld->next_rotate = now + ld->rotate_period;
565					if (!tmaxflag || LESS(ld->next_rotate, nearest_rotate))
566						nearest_rotate = ld->next_rotate;
567					tmaxflag = 1;
568				}
569				break;
570			}
571			case '!':
572				if (s[1]) {
573					free(ld->processor);
574					ld->processor = wstrdup(s);
575				}
576				break;
577			}
578			s = np;
579		}
580		/* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
581		s = ld->inst;
582		while (s) {
583			np = strchr(s, '\n');
584			if (np)
585				*np++ = '\0';
586			s = np;
587		}
588	}
589
590	/* open current */
591	i = stat("current", &st);
592	if (i != -1) {
593		if (st.st_size && !(st.st_mode & S_IXUSR)) {
594			ld->fnsave[25] = '.';
595			ld->fnsave[26] = 'u';
596			ld->fnsave[27] = '\0';
597			do {
598				fmt_time_bernstein_25(ld->fnsave);
599				errno = 0;
600				stat(ld->fnsave, &st);
601			} while (errno != ENOENT);
602			while (rename("current", ld->fnsave) == -1)
603				pause2cannot("rename current", ld->name);
604			rmoldest(ld);
605			i = -1;
606		} else {
607			/* st.st_size can be not just bigger, but WIDER!
608			 * This code is safe: if st.st_size > 4GB, we select
609			 * ld->sizemax (because it's "unsigned") */
610			ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
611		}
612	} else {
613		if (errno != ENOENT) {
614			logdir_close(ld);
615			warn2("cannot stat current", ld->name);
616			while (fchdir(fdwdir) == -1)
617				pause1cannot("change to initial working directory");
618			return 0;
619		}
620	}
621	while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
622		pause2cannot("open current", ld->name);
623	/* we presume this cannot fail */
624	ld->filecur = fdopen(ld->fdcur, "a"); ////
625	setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
626
627	coe(ld->fdcur);
628	while (fchmod(ld->fdcur, 0644) == -1)
629		pause2cannot("set mode of current", ld->name);
630
631	if (verbose) {
632		if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
633		else bb_error_msg(INFO"new: %s/current", ld->name);
634	}
635
636	while (fchdir(fdwdir) == -1)
637		pause1cannot("change to initial working directory");
638	return 1;
639}
640
641static void logdirs_reopen(void)
642{
643	int l;
644	int ok = 0;
645
646	tmaxflag = 0;
647	for (l = 0; l < dirn; ++l) {
648		logdir_close(&dir[l]);
649		if (logdir_open(&dir[l], fndir[l]))
650			ok = 1;
651	}
652	if (!ok)
653		fatalx("no functional log directories");
654}
655
656/* Will look good in libbb one day */
657static ssize_t ndelay_read(int fd, void *buf, size_t count)
658{
659	if (!(fl_flag_0 & O_NONBLOCK))
660		fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
661	count = safe_read(fd, buf, count);
662	if (!(fl_flag_0 & O_NONBLOCK))
663		fcntl(fd, F_SETFL, fl_flag_0);
664	return count;
665}
666
667/* Used for reading stdin */
668static int buffer_pread(int fd, char *s, unsigned len)
669{
670	unsigned now;
671	struct pollfd input;
672	int i;
673
674	input.fd = 0;
675	input.events = POLLIN;
676
677	do {
678		if (rotateasap) {
679			for (i = 0; i < dirn; ++i)
680				rotate(dir + i);
681			rotateasap = 0;
682		}
683		if (exitasap) {
684			if (linecomplete)
685				return 0;
686			len = 1;
687		}
688		if (reopenasap) {
689			logdirs_reopen();
690			reopenasap = 0;
691		}
692		now = monotonic_sec();
693		nearest_rotate = now + (45 * 60 + 45);
694		for (i = 0; i < dirn; ++i) {
695			if (dir[i].rotate_period) {
696				if (LESS(dir[i].next_rotate, now))
697					rotate(dir + i);
698				if (LESS(dir[i].next_rotate, nearest_rotate))
699					nearest_rotate = dir[i].next_rotate;
700			}
701		}
702
703		sigprocmask(SIG_UNBLOCK, blocked_sigset, NULL);
704		i = nearest_rotate - now;
705		if (i > 1000000)
706			i = 1000000;
707		if (i <= 0)
708			i = 1;
709		poll(&input, 1, i * 1000);
710		sigprocmask(SIG_BLOCK, blocked_sigset, NULL);
711
712		i = ndelay_read(fd, s, len);
713		if (i >= 0)
714			break;
715		if (errno == EINTR)
716			continue;
717		if (errno != EAGAIN) {
718			warn("cannot read standard input");
719			break;
720		}
721		/* else: EAGAIN - normal, repeat silently */
722	} while (!exitasap);
723
724	if (i > 0) {
725		int cnt;
726		linecomplete = (s[i-1] == '\n');
727		if (!repl)
728			return i;
729
730		cnt = i;
731		while (--cnt >= 0) {
732			char ch = *s;
733			if (ch != '\n') {
734				if (ch < 32 || ch > 126)
735					*s = repl;
736				else {
737					int j;
738					for (j = 0; replace[j]; ++j) {
739						if (ch == replace[j]) {
740							*s = repl;
741							break;
742						}
743					}
744				}
745			}
746			s++;
747		}
748	}
749	return i;
750}
751
752static void sig_term_handler(int sig_no)
753{
754	if (verbose)
755		bb_error_msg(INFO"sig%s received", "term");
756	exitasap = 1;
757}
758
759static void sig_child_handler(int sig_no)
760{
761	int pid, l;
762
763	if (verbose)
764		bb_error_msg(INFO"sig%s received", "child");
765	while ((pid = wait_nohang(&wstat)) > 0) {
766		for (l = 0; l < dirn; ++l) {
767			if (dir[l].ppid == pid) {
768				dir[l].ppid = 0;
769				processorstop(&dir[l]);
770				break;
771			}
772		}
773	}
774}
775
776static void sig_alarm_handler(int sig_no)
777{
778	if (verbose)
779		bb_error_msg(INFO"sig%s received", "alarm");
780	rotateasap = 1;
781}
782
783static void sig_hangup_handler(int sig_no)
784{
785	if (verbose)
786		bb_error_msg(INFO"sig%s received", "hangup");
787	reopenasap = 1;
788}
789
790static void logmatch(struct logdir *ld)
791{
792	char *s;
793
794	ld->match = '+';
795	ld->matcherr = 'E';
796	s = ld->inst;
797	while (s && s[0]) {
798		switch (s[0]) {
799		case '+':
800		case '-':
801			if (pmatch(s+1, line, linelen))
802				ld->match = s[0];
803			break;
804		case 'e':
805		case 'E':
806			if (pmatch(s+1, line, linelen))
807				ld->matcherr = s[0];
808			break;
809		}
810		s += strlen(s) + 1;
811	}
812}
813
814int svlogd_main(int argc, char **argv);
815int svlogd_main(int argc, char **argv)
816{
817	sigset_t ss;
818	char *r,*l,*b;
819	ssize_t stdin_cnt = 0;
820	int i;
821	unsigned opt;
822	unsigned timestamp = 0;
823	void* (*memRchr)(const void *, int, size_t) = memchr;
824
825#define line bb_common_bufsiz1
826
827	opt_complementary = "tt:vv";
828	opt = getopt32(argv, "r:R:l:b:tv",
829			&r, &replace, &l, &b, &timestamp, &verbose);
830	if (opt & 1) { // -r
831		repl = r[0];
832		if (!repl || r[1]) usage();
833	}
834	if (opt & 2) if (!repl) repl = '_'; // -R
835	if (opt & 4) { // -l
836		linemax = xatou_range(l, 0, BUFSIZ-26);
837		if (linemax == 0) linemax = BUFSIZ-26;
838		if (linemax < 256) linemax = 256;
839	}
840	////if (opt & 8) { // -b
841	////	buflen = xatoi_u(b);
842	////	if (buflen == 0) buflen = 1024;
843	////}
844	//if (opt & 0x10) timestamp++; // -t
845	//if (opt & 0x20) verbose++; // -v
846	//if (timestamp > 2) timestamp = 2;
847	argv += optind;
848	argc -= optind;
849
850	dirn = argc;
851	if (dirn <= 0) usage();
852	////if (buflen <= linemax) usage();
853	fdwdir = xopen(".", O_RDONLY|O_NDELAY);
854	coe(fdwdir);
855	dir = xzalloc(dirn * sizeof(struct logdir));
856	for (i = 0; i < dirn; ++i) {
857		dir[i].fddir = -1;
858		dir[i].fdcur = -1;
859		////dir[i].btmp = xmalloc(buflen);
860		/*dir[i].ppid = 0;*/
861	}
862	/* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
863	fndir = argv;
864	/* We cannot set NONBLOCK on fd #0 permanently - this setting
865	 * _isn't_ per-process! It is shared among all other processes
866	 * with the same stdin */
867	fl_flag_0 = fcntl(0, F_GETFL);
868
869	blocked_sigset = &ss;
870	sigemptyset(&ss);
871	sigaddset(&ss, SIGTERM);
872	sigaddset(&ss, SIGCHLD);
873	sigaddset(&ss, SIGALRM);
874	sigaddset(&ss, SIGHUP);
875	sigprocmask(SIG_BLOCK, &ss, NULL);
876	sig_catch(SIGTERM, sig_term_handler);
877	sig_catch(SIGCHLD, sig_child_handler);
878	sig_catch(SIGALRM, sig_alarm_handler);
879	sig_catch(SIGHUP, sig_hangup_handler);
880
881	logdirs_reopen();
882
883	/* Without timestamps, we don't have to print each line
884	 * separately, so we can look for _last_ newline, not first,
885	 * thus batching writes */
886	if (!timestamp)
887		memRchr = memrchr;
888
889	setvbuf(stderr, NULL, _IOFBF, linelen);
890
891	/* Each iteration processes one or more lines */
892	while (1) {
893		char stamp[FMT_PTIME];
894		char *lineptr;
895		char *printptr;
896		char *np;
897		int printlen;
898		char ch;
899
900		lineptr = line;
901		if (timestamp)
902			lineptr += 26;
903
904		/* lineptr[0..linemax-1] - buffer for stdin */
905		/* (possibly has some unprocessed data from prev loop) */
906
907		/* Refill the buffer if needed */
908		np = memRchr(lineptr, '\n', stdin_cnt);
909		if (!np && !exitasap) {
910			i = linemax - stdin_cnt; /* avail. bytes at tail */
911			if (i >= 128) {
912				i = buffer_pread(0, lineptr + stdin_cnt, i);
913				if (i <= 0) /* EOF or error on stdin */
914					exitasap = 1;
915				else {
916					np = memRchr(lineptr + stdin_cnt, '\n', i);
917					stdin_cnt += i;
918				}
919			}
920		}
921		if (stdin_cnt <= 0 && exitasap)
922			break;
923
924		/* Search for '\n' (in fact, np already holds the result) */
925		linelen = stdin_cnt;
926		if (np) {
927 print_to_nl:		/* NB: starting from here lineptr may point
928			 * farther out into line[] */
929			linelen = np - lineptr + 1;
930		}
931		/* linelen == no of chars incl. '\n' (or == stdin_cnt) */
932		ch = lineptr[linelen-1];
933
934		/* Biggest performance hit was coming from the fact
935		 * that we did not buffer writes. We were reading many lines
936		 * in one read() above, but wrote one line per write().
937		 * We are using stdio to fix that */
938
939		/* write out lineptr[0..linelen-1] to each log destination
940		 * (or lineptr[-26..linelen-1] if timestamping) */
941		printlen = linelen;
942		printptr = lineptr;
943		if (timestamp) {
944			if (timestamp == 1)
945				fmt_time_bernstein_25(stamp);
946			else /* 2: */
947				fmt_time_human_30nul(stamp);
948			printlen += 26;
949			printptr -= 26;
950			memcpy(printptr, stamp, 25);
951			printptr[25] = ' ';
952		}
953		for (i = 0; i < dirn; ++i) {
954			struct logdir *ld = &dir[i];
955			if (ld->fddir == -1) continue;
956			if (ld->inst)
957				logmatch(ld);
958			if (ld->matcherr == 'e')
959				////full_write(2, printptr, printlen);
960				fwrite(lineptr, 1, linelen, stderr);
961			if (ld->match != '+') continue;
962			buffer_pwrite(i, printptr, printlen);
963		}
964
965		/* If we didn't see '\n' (long input line), */
966		/* read/write repeatedly until we see it */
967		while (ch != '\n') {
968			/* lineptr is emptied now, safe to use as buffer */
969			stdin_cnt = exitasap ? -1 : buffer_pread(0, lineptr, linemax);
970			if (stdin_cnt <= 0) { /* EOF or error on stdin */
971				exitasap = 1;
972				lineptr[0] = ch = '\n';
973				linelen = 1;
974				stdin_cnt = 1;
975			} else {
976				linelen = stdin_cnt;
977				np = memRchr(lineptr, '\n', stdin_cnt);
978				if (np)
979					linelen = np - lineptr + 1;
980				ch = lineptr[linelen-1];
981			}
982			/* linelen == no of chars incl. '\n' (or == stdin_cnt) */
983			for (i = 0; i < dirn; ++i) {
984				if (dir[i].fddir == -1) continue;
985				if (dir[i].matcherr == 'e')
986					////full_write(2, lineptr, linelen);
987					fwrite(lineptr, 1, linelen, stderr);
988				if (dir[i].match != '+') continue;
989				buffer_pwrite(i, lineptr, linelen);
990			}
991		}
992
993		stdin_cnt -= linelen;
994		if (stdin_cnt > 0) {
995			lineptr += linelen;
996			/* If we see another '\n', we don't need to read
997			 * next piece of input: can print what we have */
998			np = memRchr(lineptr, '\n', stdin_cnt);
999			if (np)
1000				goto print_to_nl;
1001			/* Move unprocessed data to the front of line */
1002			memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
1003		}
1004		fflush(NULL);////
1005	}
1006
1007	for (i = 0; i < dirn; ++i) {
1008		if (dir[i].ppid)
1009			while (!processorstop(&dir[i]))
1010				/* repeat */;
1011		logdir_close(&dir[i]);
1012	}
1013	return 0;
1014}
1015