blob: cf29e8e9f69f89ae6778477f5d97b27a5c1c822a [file] [log] [blame]
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001/*
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 "busybox.h"
34#include "runit_lib.h"
35
36static unsigned verbose;
37static int linemax = 1000;
Denis Vlasenkoca549c52007-01-27 22:24:59 +000038////static int buflen = 1024;
Denis Vlasenko83ea6432006-11-16 02:27:24 +000039static int linelen;
40
41static char **fndir;
42static int fdwdir;
43static int wstat;
44static struct taia trotate;
45
46static char *line;
47static unsigned exitasap;
48static unsigned rotateasap;
49static unsigned reopenasap;
50static unsigned linecomplete = 1;
51static unsigned tmaxflag;
Denis Vlasenkoca549c52007-01-27 22:24:59 +000052static iopause_fd input;
Denis Vlasenko83ea6432006-11-16 02:27:24 +000053
54static const char *replace = "";
55static char repl;
56
57static struct logdir {
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +000058//// char *btmp;
Denis Vlasenko83ea6432006-11-16 02:27:24 +000059 /* pattern list to match, in "aa\0bb\0\cc\0\0" form */
60 char *inst;
61 char *processor;
62 char *name;
63 unsigned size;
64 unsigned sizemax;
65 unsigned nmax;
66 unsigned nmin;
67 /* int (not long) because of taia_uint() usage: */
68 unsigned tmax;
69 int ppid;
70 int fddir;
71 int fdcur;
72 int fdlock;
73 struct taia trotate;
74 char fnsave[FMT_PTIME];
75 char match;
76 char matcherr;
77} *dir;
78static unsigned dirn = 0;
79
80#define FATAL "fatal: "
81#define WARNING "warning: "
82#define PAUSE "pausing: "
83#define INFO "info: "
84
85#define usage() bb_show_usage()
86static void fatalx(char *m0)
87{
88 bb_error_msg_and_die(FATAL"%s", m0);
89}
90static void warn(char *m0) {
91 bb_perror_msg(WARNING"%s", m0);
92}
93static void warn2(char *m0, char *m1)
94{
95 bb_perror_msg(WARNING"%s: %s", m0, m1);
96}
97static void warnx(char *m0, char *m1)
98{
99 bb_error_msg(WARNING"%s: %s", m0, m1);
100}
101static void pause_nomem(void)
102{
Denis Vlasenko8c783952007-01-27 22:21:52 +0000103 bb_error_msg(PAUSE"out of memory");
104 sleep(3);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000105}
106static void pause1cannot(char *m0)
107{
Denis Vlasenko8c783952007-01-27 22:21:52 +0000108 bb_perror_msg(PAUSE"cannot %s", m0);
109 sleep(3);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000110}
111static void pause2cannot(char *m0, char *m1)
112{
113 bb_perror_msg(PAUSE"cannot %s %s", m0, m1);
114 sleep(3);
115}
116
117static char* wstrdup(const char *str)
118{
119 char *s;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000120 while (!(s = strdup(str)))
121 pause_nomem();
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000122 return s;
123}
124
125static unsigned processorstart(struct logdir *ld)
126{
127 int pid;
128
129 if (!ld->processor) return 0;
130 if (ld->ppid) {
131 warnx("processor already running", ld->name);
132 return 0;
133 }
134 while ((pid = fork()) == -1)
135 pause2cannot("fork for processor", ld->name);
136 if (!pid) {
137 char *prog[4];
138 int fd;
139
140 /* child */
Denis Vlasenko8c783952007-01-27 22:21:52 +0000141 sig_uncatch(SIGTERM);
142 sig_uncatch(SIGALRM);
143 sig_uncatch(SIGHUP);
144 sig_unblock(SIGTERM);
145 sig_unblock(SIGALRM);
146 sig_unblock(SIGHUP);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000147
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000148 if (verbose)
149 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
150 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
151 if (fd_move(0, fd) == -1)
152 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
153 ld->fnsave[26] = 't';
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000154 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000155 if (fd_move(1, fd) == -1)
156 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
157 fd = open_read("state");
158 if (fd == -1) {
159 if (errno != ENOENT)
160 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "open state for", ld->name);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000161 close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000162 fd = xopen("state", O_RDONLY|O_NDELAY);
163 }
164 if (fd_move(4, fd) == -1)
165 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000166 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000167 if (fd_move(5, fd) == -1)
168 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
169
Denis Vlasenko8c783952007-01-27 22:21:52 +0000170// getenv("SHELL")?
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000171 prog[0] = "sh";
172 prog[1] = "-c";
173 prog[2] = ld->processor;
174 prog[3] = '\0';
175 execve("/bin/sh", prog, environ);
176 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "run", ld->name);
177 }
178 ld->ppid = pid;
179 return 1;
180}
181
182static unsigned processorstop(struct logdir *ld)
183{
184 char f[28];
185
186 if (ld->ppid) {
Denis Vlasenko8c783952007-01-27 22:21:52 +0000187 sig_unblock(SIGHUP);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000188 while (wait_pid(&wstat, ld->ppid) == -1)
189 pause2cannot("wait for processor", ld->name);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000190 sig_block(SIGHUP);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000191 ld->ppid = 0;
192 }
193 if (ld->fddir == -1) return 1;
194 while (fchdir(ld->fddir) == -1)
195 pause2cannot("change directory, want processor", ld->name);
196 if (wait_exitcode(wstat) != 0) {
197 warnx("processor failed, restart", ld->name);
198 ld->fnsave[26] = 't';
199 unlink(ld->fnsave);
200 ld->fnsave[26] = 'u';
201 processorstart(ld);
202 while (fchdir(fdwdir) == -1)
203 pause1cannot("change to initial working directory");
204 return ld->processor ? 0 : 1;
205 }
206 ld->fnsave[26] = 't';
207 memcpy(f, ld->fnsave, 26);
208 f[26] = 's';
209 f[27] = '\0';
210 while (rename(ld->fnsave, f) == -1)
211 pause2cannot("rename processed", ld->name);
212 while (chmod(f, 0744) == -1)
213 pause2cannot("set mode of processed", ld->name);
214 ld->fnsave[26] = 'u';
215 if (unlink(ld->fnsave) == -1)
216 bb_error_msg(WARNING"cannot unlink: %s/%s", ld->name, ld->fnsave);
217 while (rename("newstate", "state") == -1)
218 pause2cannot("rename state", ld->name);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000219 if (verbose)
220 bb_error_msg(INFO"processed: %s/%s", ld->name, f);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000221 while (fchdir(fdwdir) == -1)
222 pause1cannot("change to initial working directory");
223 return 1;
224}
225
226static void rmoldest(struct logdir *ld)
227{
228 DIR *d;
229 struct dirent *f;
230 char oldest[FMT_PTIME];
231 int n = 0;
232
233 oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
234 while (!(d = opendir(".")))
235 pause2cannot("open directory, want rotate", ld->name);
236 errno = 0;
237 while ((f = readdir(d))) {
238 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
239 if (f->d_name[26] == 't') {
240 if (unlink(f->d_name) == -1)
241 warn2("cannot unlink processor leftover", f->d_name);
242 } else {
243 ++n;
244 if (strcmp(f->d_name, oldest) < 0)
245 memcpy(oldest, f->d_name, 27);
246 }
247 errno = 0;
248 }
249 }
Denis Vlasenko8c783952007-01-27 22:21:52 +0000250 if (errno)
251 warn2("cannot read directory", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000252 closedir(d);
253
254 if (ld->nmax && (n > ld->nmax)) {
Denis Vlasenko8c783952007-01-27 22:21:52 +0000255 if (verbose)
256 bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000257 if ((*oldest == '@') && (unlink(oldest) == -1))
258 warn2("cannot unlink oldest logfile", ld->name);
259 }
260}
261
262static unsigned rotate(struct logdir *ld)
263{
264 struct stat st;
265 struct taia now;
266
267 if (ld->fddir == -1) {
268 ld->tmax = 0;
269 return 0;
270 }
271 if (ld->ppid)
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000272 while (!processorstop(ld))
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000273 /* wait */;
274
275 while (fchdir(ld->fddir) == -1)
276 pause2cannot("change directory, want rotate", ld->name);
277
278 /* create new filename */
279 ld->fnsave[25] = '.';
280 ld->fnsave[26] = 's';
281 if (ld->processor)
282 ld->fnsave[26] = 'u';
283 ld->fnsave[27] = '\0';
284 do {
285 taia_now(&now);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000286 fmt_taia25(ld->fnsave, &now);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000287 errno = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000288 stat(ld->fnsave, &st);
289 } while (errno != ENOENT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000290
291 if (ld->tmax && taia_less(&ld->trotate, &now)) {
292 taia_uint(&ld->trotate, ld->tmax);
293 taia_add(&ld->trotate, &now, &ld->trotate);
294 if (taia_less(&ld->trotate, &trotate))
295 trotate = ld->trotate;
296 }
297
298 if (ld->size > 0) {
299 while (fsync(ld->fdcur) == -1)
300 pause2cannot("fsync current logfile", ld->name);
301 while (fchmod(ld->fdcur, 0744) == -1)
302 pause2cannot("set mode of current", ld->name);
303 close(ld->fdcur);
304 if (verbose) {
305 bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
306 ld->fnsave, ld->size);
307 }
308 while (rename("current", ld->fnsave) == -1)
309 pause2cannot("rename current", ld->name);
310 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
311 pause2cannot("create new current", ld->name);
312 coe(ld->fdcur);
313 ld->size = 0;
314 while (fchmod(ld->fdcur, 0644) == -1)
315 pause2cannot("set mode of current", ld->name);
316 rmoldest(ld);
317 processorstart(ld);
318 }
319
320 while (fchdir(fdwdir) == -1)
321 pause1cannot("change to initial working directory");
322 return 1;
323}
324
325static int buffer_pwrite(int n, char *s, unsigned len)
326{
327 int i;
328 struct logdir *ld = &dir[n];
329
330 if (ld->sizemax) {
331 if (ld->size >= ld->sizemax)
332 rotate(ld);
333 if (len > (ld->sizemax - ld->size))
334 len = ld->sizemax - ld->size;
335 }
336 while ((i = write(ld->fdcur, s, len)) == -1) {
337 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
338 DIR *d;
339 struct dirent *f;
340 char oldest[FMT_PTIME];
341 int j = 0;
342
343 while (fchdir(ld->fddir) == -1)
344 pause2cannot("change directory, want remove old logfile",
345 ld->name);
346 oldest[0] = 'A';
347 oldest[1] = oldest[27] = '\0';
348 while (!(d = opendir(".")))
349 pause2cannot("open directory, want remove old logfile",
350 ld->name);
351 errno = 0;
352 while ((f = readdir(d)))
353 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
354 ++j;
355 if (strcmp(f->d_name, oldest) < 0)
356 memcpy(oldest, f->d_name, 27);
357 }
358 if (errno) warn2("cannot read directory, want remove old logfile",
359 ld->name);
360 closedir(d);
361 errno = ENOSPC;
362 if (j > ld->nmin) {
363 if (*oldest == '@') {
364 bb_error_msg(WARNING"out of disk space, delete: %s/%s",
365 ld->name, oldest);
366 errno = 0;
367 if (unlink(oldest) == -1) {
368 warn2("cannot unlink oldest logfile", ld->name);
369 errno = ENOSPC;
370 }
371 while (fchdir(fdwdir) == -1)
372 pause1cannot("change to initial working directory");
373 }
374 }
375 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000376 if (errno)
377 pause2cannot("write to current", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000378 }
379
380 ld->size += i;
381 if (ld->sizemax)
382 if (s[i-1] == '\n')
383 if (ld->size >= (ld->sizemax - linemax))
384 rotate(ld);
385 return i;
386}
387
388static void logdir_close(struct logdir *ld)
389{
390 if (ld->fddir == -1)
391 return;
392 if (verbose)
393 bb_error_msg(INFO"close: %s", ld->name);
394 close(ld->fddir);
395 ld->fddir = -1;
396 if (ld->fdcur == -1)
397 return; /* impossible */
398 while (fsync(ld->fdcur) == -1)
399 pause2cannot("fsync current logfile", ld->name);
400 while (fchmod(ld->fdcur, 0744) == -1)
401 pause2cannot("set mode of current", ld->name);
402 close(ld->fdcur);
403 ld->fdcur = -1;
404 if (ld->fdlock == -1)
405 return; /* impossible */
406 close(ld->fdlock);
407 ld->fdlock = -1;
408 free(ld->processor);
409 ld->processor = NULL;
410}
411
412static unsigned logdir_open(struct logdir *ld, const char *fn)
413{
414 char buf[128];
415 struct taia now;
416 char *new, *s, *np;
417 int i;
418 struct stat st;
419
420 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
421 if (ld->fddir == -1) {
422 warn2("cannot open log directory", (char*)fn);
423 return 0;
424 }
425 coe(ld->fddir);
426 if (fchdir(ld->fddir) == -1) {
427 logdir_close(ld);
428 warn2("cannot change directory", (char*)fn);
429 return 0;
430 }
431 ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
432 if ((ld->fdlock == -1)
433 || (lock_exnb(ld->fdlock) == -1)
434 ) {
435 logdir_close(ld);
436 warn2("cannot lock directory", (char*)fn);
437 while (fchdir(fdwdir) == -1)
438 pause1cannot("change to initial working directory");
439 return 0;
440 }
441 coe(ld->fdlock);
442
443 ld->size = 0;
444 ld->sizemax = 1000000;
445 ld->nmax = ld->nmin = 10;
446 ld->tmax = 0;
447 ld->name = (char*)fn;
448 ld->ppid = 0;
449 ld->match = '+';
450 free(ld->inst); ld->inst = NULL;
451 free(ld->processor); ld->processor = NULL;
452
453 /* read config */
454 i = open_read_close("config", buf, sizeof(buf));
455 if (i < 0)
456 warn2("cannot read config", ld->name);
457 if (i > 0) {
458 if (verbose) bb_error_msg(INFO"read: %s/config", ld->name);
459 s = buf;
460 while (s) {
461 np = strchr(s, '\n');
462 if (np) *np++ = '\0';
463 switch (s[0]) {
464 case '+':
465 case '-':
466 case 'e':
467 case 'E':
468 while (1) {
469 int l = asprintf(&new, "%s%s\n", ld->inst?:"", s);
470 if (l >= 0 && new) break;
471 pause_nomem();
472 }
473 free(ld->inst);
474 ld->inst = new;
475 break;
476 case 's': {
477 static const struct suffix_mult km_suffixes[] = {
478 { "k", 1024 },
479 { "m", 1024*1024 },
480 { NULL, 0 }
481 };
482 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
483 break;
484 }
485 case 'n':
486 ld->nmax = xatoi_u(&s[1]);
487 break;
488 case 'N':
489 ld->nmin = xatoi_u(&s[1]);
490 break;
491 case 't': {
492 static const struct suffix_mult mh_suffixes[] = {
493 { "m", 60 },
494 { "h", 60*60 },
495 /*{ "d", 24*60*60 },*/
496 { NULL, 0 }
497 };
498 ld->tmax = xatou_sfx(&s[1], mh_suffixes);
499 if (ld->tmax) {
500 taia_uint(&ld->trotate, ld->tmax);
501 taia_add(&ld->trotate, &now, &ld->trotate);
502 if (!tmaxflag || taia_less(&ld->trotate, &trotate))
503 trotate = ld->trotate;
504 tmaxflag = 1;
505 }
506 break;
507 }
508 case '!':
509 if (s[1]) {
510 free(ld->processor);
511 ld->processor = wstrdup(s);
512 }
513 break;
514 }
515 s = np;
516 }
517 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
518 s = ld->inst;
519 while (s) {
520 np = strchr(s, '\n');
521 if (np) *np++ = '\0';
522 s = np;
523 }
524 }
525
526 /* open current */
527 i = stat("current", &st);
528 if (i != -1) {
529 if (st.st_size && ! (st.st_mode & S_IXUSR)) {
530 ld->fnsave[25] = '.';
531 ld->fnsave[26] = 'u';
532 ld->fnsave[27] = '\0';
533 do {
534 taia_now(&now);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000535 fmt_taia25(ld->fnsave, &now);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000536 errno = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000537 stat(ld->fnsave, &st);
538 } while (errno != ENOENT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000539 while (rename("current", ld->fnsave) == -1)
540 pause2cannot("rename current", ld->name);
541 rmoldest(ld);
542 i = -1;
543 } else {
544 /* Be paranoid: st.st_size can be not just bigger, but WIDER! */
545 /* (bug in original svlogd. remove this comment when fixed there) */
546 ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
547 }
548 } else {
549 if (errno != ENOENT) {
550 logdir_close(ld);
551 warn2("cannot stat current", ld->name);
552 while (fchdir(fdwdir) == -1)
553 pause1cannot("change to initial working directory");
554 return 0;
555 }
556 }
557 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
558 pause2cannot("open current", ld->name);
559 coe(ld->fdcur);
560 while (fchmod(ld->fdcur, 0644) == -1)
561 pause2cannot("set mode of current", ld->name);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000562
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000563 if (verbose) {
564 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
565 else bb_error_msg(INFO"new: %s/current", ld->name);
566 }
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000567
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000568 while (fchdir(fdwdir) == -1)
569 pause1cannot("change to initial working directory");
570 return 1;
571}
572
573static void logdirs_reopen(void)
574{
575 struct taia now;
576 int l;
577 int ok = 0;
578
579 tmaxflag = 0;
580 taia_now(&now);
581 for (l = 0; l < dirn; ++l) {
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000582 logdir_close(&dir[l]);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000583 if (logdir_open(&dir[l], fndir[l])) ok = 1;
584 }
585 if (!ok) fatalx("no functional log directories");
586}
587
588/* Used for reading stdin */
589static int buffer_pread(int fd, char *s, unsigned len)
590{
591 struct taia now;
592 int i;
593
594 if (rotateasap) {
595 for (i = 0; i < dirn; ++i)
596 rotate(dir+i);
597 rotateasap = 0;
598 }
599 if (exitasap) {
600 if (linecomplete)
601 return 0;
602 len = 1;
603 }
604 if (reopenasap) {
605 logdirs_reopen();
606 reopenasap = 0;
607 }
608 taia_now(&now);
609 taia_uint(&trotate, 2744);
610 taia_add(&trotate, &now, &trotate);
611 for (i = 0; i < dirn; ++i)
612 if (dir[i].tmax) {
613 if (taia_less(&dir[i].trotate, &now))
614 rotate(dir+i);
615 if (taia_less(&dir[i].trotate, &trotate))
616 trotate = dir[i].trotate;
617 }
618
619 while (1) {
620 /* Comment? */
Denis Vlasenko8c783952007-01-27 22:21:52 +0000621 sig_unblock(SIGTERM);
622 sig_unblock(SIGCHLD);
623 sig_unblock(SIGALRM);
624 sig_unblock(SIGHUP);
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000625 iopause(&input, 1, &trotate, &now);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000626 sig_block(SIGTERM);
627 sig_block(SIGCHLD);
628 sig_block(SIGALRM);
629 sig_block(SIGHUP);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000630 i = safe_read(fd, s, len);
631 if (i >= 0) break;
632 if (errno != EAGAIN) {
633 warn("cannot read standard input");
634 break;
635 }
636 /* else: EAGAIN - normal, repeat silently */
637 }
638
639 if (i > 0) {
640 int cnt;
641 linecomplete = (s[i-1] == '\n');
642 if (!repl) return i;
643
644 cnt = i;
645 while (--cnt >= 0) {
646 char ch = *s;
647 if (ch != '\n') {
648 if (ch < 32 || ch > 126)
649 *s = repl;
650 else {
651 int j;
652 for (j = 0; replace[j]; ++j) {
653 if (ch == replace[j]) {
654 *s = repl;
655 break;
656 }
657 }
658 }
659 }
660 s++;
661 }
662 }
663 return i;
664}
665
666
667static void sig_term_handler(int sig_no)
668{
669 if (verbose)
670 bb_error_msg(INFO"sig%s received", "term");
671 exitasap = 1;
672}
673
674static void sig_child_handler(int sig_no)
675{
676 int pid, l;
677
678 if (verbose)
679 bb_error_msg(INFO"sig%s received", "child");
680 while ((pid = wait_nohang(&wstat)) > 0)
681 for (l = 0; l < dirn; ++l)
682 if (dir[l].ppid == pid) {
683 dir[l].ppid = 0;
684 processorstop(&dir[l]);
685 break;
686 }
687}
688
689static void sig_alarm_handler(int sig_no)
690{
691 if (verbose)
692 bb_error_msg(INFO"sig%s received", "alarm");
693 rotateasap = 1;
694}
695
696static void sig_hangup_handler(int sig_no)
697{
698 if (verbose)
699 bb_error_msg(INFO"sig%s received", "hangup");
700 reopenasap = 1;
701}
702
703static void logmatch(struct logdir *ld)
704{
705 char *s;
706
707 ld->match = '+';
708 ld->matcherr = 'E';
709 s = ld->inst;
710 while (s && s[0]) {
711 switch (s[0]) {
712 case '+':
713 case '-':
714 if (pmatch(s+1, line, linelen))
715 ld->match = s[0];
716 break;
717 case 'e':
718 case 'E':
719 if (pmatch(s+1, line, linelen))
720 ld->matcherr = s[0];
721 break;
722 }
723 s += strlen(s) + 1;
724 }
725}
726
727int svlogd_main(int argc, char **argv)
728{
729 struct taia now;
730 char *r,*l,*b;
731 ssize_t stdin_cnt = 0;
732 int i;
733 unsigned opt;
734 unsigned timestamp = 0;
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000735 void* (*memRchr)(const void *, int, size_t) = memchr;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000736
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000737#define line bb_common_bufsiz1
738
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000739 opt_complementary = "tt:vv";
740 opt = getopt32(argc, argv, "r:R:l:b:tv",
741 &r, &replace, &l, &b, &timestamp, &verbose);
742 if (opt & 1) { // -r
743 repl = r[0];
744 if (!repl || r[1]) usage();
745 }
746 if (opt & 2) if (!repl) repl = '_'; // -R
747 if (opt & 4) { // -l
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000748 linemax = xatou_range(l, 0, BUFSIZ-26);
749 if (linemax == 0) linemax = BUFSIZ-26;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000750 if (linemax < 256) linemax = 256;
751 }
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000752//// if (opt & 8) { // -b
753//// buflen = xatoi_u(b);
754//// if (buflen == 0) buflen = 1024;
755//// }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000756 //if (opt & 0x10) timestamp++; // -t
757 //if (opt & 0x20) verbose++; // -v
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000758 //if (timestamp > 2) timestamp = 2;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000759 argv += optind;
760 argc -= optind;
761
762 dirn = argc;
763 if (dirn <= 0) usage();
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000764 ////if (buflen <= linemax) usage();
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000765 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
766 coe(fdwdir);
767 dir = xmalloc(dirn * sizeof(struct logdir));
768 for (i = 0; i < dirn; ++i) {
769 dir[i].fddir = -1;
770 dir[i].fdcur = -1;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000771 ////dir[i].btmp = xmalloc(buflen);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000772 dir[i].ppid = 0;
773 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000774 /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000775 fndir = argv;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000776 input.fd = 0;
777 input.events = IOPAUSE_READ;
778 /* I be damned. Linux 2.6.18: this somehow affects
779 * OTHER processes! Konsole starts to redraw itself much slower!
780 * This persists even after svlogd exits */
781 ndelay_on(input.fd);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000782 sig_block(SIGTERM);
783 sig_block(SIGCHLD);
784 sig_block(SIGALRM);
785 sig_block(SIGHUP);
786 sig_catch(SIGTERM, sig_term_handler);
787 sig_catch(SIGCHLD, sig_child_handler);
788 sig_catch(SIGALRM, sig_alarm_handler);
789 sig_catch(SIGHUP, sig_hangup_handler);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000790
791 logdirs_reopen();
792
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000793 /* Without timestamps, we don't have to print each line
794 * separately, so we can look for _last_ newline, not first,
795 * thus batching writes */
796 if (!timestamp)
797 memRchr = memrchr;
798
799 /* Each iteration processes one line or more lines */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000800 while (1) {
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000801 char stamp[FMT_PTIME];
802 char *lineptr;
803 char *printptr;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000804 char *np;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000805 int printlen;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000806 char ch;
807
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000808 lineptr = line;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000809 /* Prepare timestamp if needed */
810 if (timestamp) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000811 taia_now(&now);
812 switch (timestamp) {
813 case 1:
Denis Vlasenko8c783952007-01-27 22:21:52 +0000814 fmt_taia25(stamp, &now);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000815 break;
816 default: /* case 2: */
Denis Vlasenko8c783952007-01-27 22:21:52 +0000817 fmt_ptime30nul(stamp, &now);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000818 break;
819 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000820 lineptr += 26;
821 }
822
823 /* lineptr[0..linemax-1] - buffer for stdin */
824 /* (possibly has some unprocessed data from prev loop) */
825
826 /* Refill the buffer if needed */
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000827 np = memRchr(lineptr, '\n', stdin_cnt);
828 if (!np && !exitasap) {
829 i = linemax - stdin_cnt; /* avail. bytes at tail */
830 if (i >= 128) {
831 i = buffer_pread(0, lineptr + stdin_cnt, i);
832 if (i <= 0) /* EOF or error on stdin */
833 exitasap = 1;
834 else {
835 np = memRchr(lineptr + stdin_cnt, '\n', i);
836 stdin_cnt += i;
837 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000838 }
839 }
840 if (stdin_cnt <= 0 && exitasap)
841 break;
842
843 /* Search for '\n' (in fact, np already holds the result) */
844 linelen = stdin_cnt;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000845 if (np) {
846 print_to_nl: /* NB: starting from here lineptr may point
847 * farther out into line[] */
848 linelen = np - lineptr + 1;
849 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000850 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
851 ch = lineptr[linelen-1];
852
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000853 /* write out lineptr[0..linelen-1] to each log destination */
854 /* (or lineptr[-26..linelen-1] if timestamping) */
855 printlen = linelen;
856 printptr = lineptr;
857 if (timestamp) {
858 printlen += 26;
859 printptr -= 26;
860 memcpy(printptr, stamp, 25);
861 printptr[25] = ' ';
862 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000863 for (i = 0; i < dirn; ++i) {
864 struct logdir *ld = &dir[i];
865 if (ld->fddir == -1) continue;
866 if (ld->inst)
867 logmatch(ld);
Denis Vlasenko83edaf32006-11-19 17:33:54 +0000868 if (ld->matcherr == 'e')
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000869 full_write(2, printptr, printlen);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000870 if (ld->match != '+') continue;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000871 buffer_pwrite(i, printptr, printlen);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000872 }
873
874 /* If we didn't see '\n' (long input line), */
875 /* read/write repeatedly until we see it */
876 while (ch != '\n') {
877 /* lineptr is emptied now, safe to use as buffer */
878 stdin_cnt = exitasap ? -1 : buffer_pread(0, lineptr, linemax);
879 if (stdin_cnt <= 0) { /* EOF or error on stdin */
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000880 exitasap = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000881 lineptr[0] = ch = '\n';
882 linelen = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000883 stdin_cnt = 1;
884 } else {
885 linelen = stdin_cnt;
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000886 np = memRchr(lineptr, '\n', stdin_cnt);
887 if (np)
888 linelen = np - lineptr + 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000889 ch = lineptr[linelen-1];
890 }
891 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
892 for (i = 0; i < dirn; ++i) {
893 if (dir[i].fddir == -1) continue;
Denis Vlasenko83edaf32006-11-19 17:33:54 +0000894 if (dir[i].matcherr == 'e')
895 full_write(2, lineptr, linelen);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000896 if (dir[i].match != '+') continue;
897 buffer_pwrite(i, lineptr, linelen);
898 }
899 }
900
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000901 stdin_cnt -= linelen;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000902 if (stdin_cnt > 0) {
903 lineptr += linelen;
904 /* If we see another '\n', we don't need to read
905 * next piece of input: can print what we have */
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000906 np = memRchr(lineptr, '\n', stdin_cnt);
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000907 if (np)
908 goto print_to_nl;
909 /* Move unprocessed data to the front of line */
910 memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
911 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000912 }
913
914 for (i = 0; i < dirn; ++i) {
915 if (dir[i].ppid)
916 while (!processorstop(&dir[i]))
917 /* repeat */;
918 logdir_close(&dir[i]);
919 }
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000920 return 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000921}