blob: 7024c3db49ac957fb4b8134a70d53b8ddd35f611 [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;
38static int buflen = 1024;
39static 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;
52static iopause_fd in;
53
54static const char *replace = "";
55static char repl;
56
57static struct logdir {
58 char *btmp;
59 /* 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{
103 bb_error_msg(PAUSE"out of memory"); sleep(3);
104}
105static void pause1cannot(char *m0)
106{
107 bb_perror_msg(PAUSE"cannot %s", m0); sleep(3);
108}
109static void pause2cannot(char *m0, char *m1)
110{
111 bb_perror_msg(PAUSE"cannot %s %s", m0, m1);
112 sleep(3);
113}
114
115static char* wstrdup(const char *str)
116{
117 char *s;
118 while (!(s = strdup(str))) pause_nomem();
119 return s;
120}
121
122static unsigned processorstart(struct logdir *ld)
123{
124 int pid;
125
126 if (!ld->processor) return 0;
127 if (ld->ppid) {
128 warnx("processor already running", ld->name);
129 return 0;
130 }
131 while ((pid = fork()) == -1)
132 pause2cannot("fork for processor", ld->name);
133 if (!pid) {
134 char *prog[4];
135 int fd;
136
137 /* child */
138 sig_uncatch(sig_term);
139 sig_uncatch(sig_alarm);
140 sig_uncatch(sig_hangup);
141 sig_unblock(sig_term);
142 sig_unblock(sig_alarm);
143 sig_unblock(sig_hangup);
144
145 if (verbose)
146 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
147 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
148 if (fd_move(0, fd) == -1)
149 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
150 ld->fnsave[26] = 't';
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000151 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000152 if (fd_move(1, fd) == -1)
153 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
154 fd = open_read("state");
155 if (fd == -1) {
156 if (errno != ENOENT)
157 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "open state for", ld->name);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000158 close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000159 fd = xopen("state", O_RDONLY|O_NDELAY);
160 }
161 if (fd_move(4, fd) == -1)
162 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000163 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000164 if (fd_move(5, fd) == -1)
165 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
166
167 prog[0] = "sh";
168 prog[1] = "-c";
169 prog[2] = ld->processor;
170 prog[3] = '\0';
171 execve("/bin/sh", prog, environ);
172 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "run", ld->name);
173 }
174 ld->ppid = pid;
175 return 1;
176}
177
178static unsigned processorstop(struct logdir *ld)
179{
180 char f[28];
181
182 if (ld->ppid) {
183 sig_unblock(sig_hangup);
184 while (wait_pid(&wstat, ld->ppid) == -1)
185 pause2cannot("wait for processor", ld->name);
186 sig_block(sig_hangup);
187 ld->ppid = 0;
188 }
189 if (ld->fddir == -1) return 1;
190 while (fchdir(ld->fddir) == -1)
191 pause2cannot("change directory, want processor", ld->name);
192 if (wait_exitcode(wstat) != 0) {
193 warnx("processor failed, restart", ld->name);
194 ld->fnsave[26] = 't';
195 unlink(ld->fnsave);
196 ld->fnsave[26] = 'u';
197 processorstart(ld);
198 while (fchdir(fdwdir) == -1)
199 pause1cannot("change to initial working directory");
200 return ld->processor ? 0 : 1;
201 }
202 ld->fnsave[26] = 't';
203 memcpy(f, ld->fnsave, 26);
204 f[26] = 's';
205 f[27] = '\0';
206 while (rename(ld->fnsave, f) == -1)
207 pause2cannot("rename processed", ld->name);
208 while (chmod(f, 0744) == -1)
209 pause2cannot("set mode of processed", ld->name);
210 ld->fnsave[26] = 'u';
211 if (unlink(ld->fnsave) == -1)
212 bb_error_msg(WARNING"cannot unlink: %s/%s", ld->name, ld->fnsave);
213 while (rename("newstate", "state") == -1)
214 pause2cannot("rename state", ld->name);
215 if (verbose) bb_error_msg(INFO"processed: %s/%s", ld->name, f);
216 while (fchdir(fdwdir) == -1)
217 pause1cannot("change to initial working directory");
218 return 1;
219}
220
221static void rmoldest(struct logdir *ld)
222{
223 DIR *d;
224 struct dirent *f;
225 char oldest[FMT_PTIME];
226 int n = 0;
227
228 oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
229 while (!(d = opendir(".")))
230 pause2cannot("open directory, want rotate", ld->name);
231 errno = 0;
232 while ((f = readdir(d))) {
233 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
234 if (f->d_name[26] == 't') {
235 if (unlink(f->d_name) == -1)
236 warn2("cannot unlink processor leftover", f->d_name);
237 } else {
238 ++n;
239 if (strcmp(f->d_name, oldest) < 0)
240 memcpy(oldest, f->d_name, 27);
241 }
242 errno = 0;
243 }
244 }
245 if (errno) warn2("cannot read directory", ld->name);
246 closedir(d);
247
248 if (ld->nmax && (n > ld->nmax)) {
249 if (verbose) bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
250 if ((*oldest == '@') && (unlink(oldest) == -1))
251 warn2("cannot unlink oldest logfile", ld->name);
252 }
253}
254
255static unsigned rotate(struct logdir *ld)
256{
257 struct stat st;
258 struct taia now;
259
260 if (ld->fddir == -1) {
261 ld->tmax = 0;
262 return 0;
263 }
264 if (ld->ppid)
265 while(!processorstop(ld))
266 /* wait */;
267
268 while (fchdir(ld->fddir) == -1)
269 pause2cannot("change directory, want rotate", ld->name);
270
271 /* create new filename */
272 ld->fnsave[25] = '.';
273 ld->fnsave[26] = 's';
274 if (ld->processor)
275 ld->fnsave[26] = 'u';
276 ld->fnsave[27] = '\0';
277 do {
278 taia_now(&now);
279 fmt_taia(ld->fnsave, &now);
280 errno = 0;
281 } while ((stat(ld->fnsave, &st) != -1) || (errno != ENOENT));
282
283 if (ld->tmax && taia_less(&ld->trotate, &now)) {
284 taia_uint(&ld->trotate, ld->tmax);
285 taia_add(&ld->trotate, &now, &ld->trotate);
286 if (taia_less(&ld->trotate, &trotate))
287 trotate = ld->trotate;
288 }
289
290 if (ld->size > 0) {
291 while (fsync(ld->fdcur) == -1)
292 pause2cannot("fsync current logfile", ld->name);
293 while (fchmod(ld->fdcur, 0744) == -1)
294 pause2cannot("set mode of current", ld->name);
295 close(ld->fdcur);
296 if (verbose) {
297 bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
298 ld->fnsave, ld->size);
299 }
300 while (rename("current", ld->fnsave) == -1)
301 pause2cannot("rename current", ld->name);
302 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
303 pause2cannot("create new current", ld->name);
304 coe(ld->fdcur);
305 ld->size = 0;
306 while (fchmod(ld->fdcur, 0644) == -1)
307 pause2cannot("set mode of current", ld->name);
308 rmoldest(ld);
309 processorstart(ld);
310 }
311
312 while (fchdir(fdwdir) == -1)
313 pause1cannot("change to initial working directory");
314 return 1;
315}
316
317static int buffer_pwrite(int n, char *s, unsigned len)
318{
319 int i;
320 struct logdir *ld = &dir[n];
321
322 if (ld->sizemax) {
323 if (ld->size >= ld->sizemax)
324 rotate(ld);
325 if (len > (ld->sizemax - ld->size))
326 len = ld->sizemax - ld->size;
327 }
328 while ((i = write(ld->fdcur, s, len)) == -1) {
329 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
330 DIR *d;
331 struct dirent *f;
332 char oldest[FMT_PTIME];
333 int j = 0;
334
335 while (fchdir(ld->fddir) == -1)
336 pause2cannot("change directory, want remove old logfile",
337 ld->name);
338 oldest[0] = 'A';
339 oldest[1] = oldest[27] = '\0';
340 while (!(d = opendir(".")))
341 pause2cannot("open directory, want remove old logfile",
342 ld->name);
343 errno = 0;
344 while ((f = readdir(d)))
345 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
346 ++j;
347 if (strcmp(f->d_name, oldest) < 0)
348 memcpy(oldest, f->d_name, 27);
349 }
350 if (errno) warn2("cannot read directory, want remove old logfile",
351 ld->name);
352 closedir(d);
353 errno = ENOSPC;
354 if (j > ld->nmin) {
355 if (*oldest == '@') {
356 bb_error_msg(WARNING"out of disk space, delete: %s/%s",
357 ld->name, oldest);
358 errno = 0;
359 if (unlink(oldest) == -1) {
360 warn2("cannot unlink oldest logfile", ld->name);
361 errno = ENOSPC;
362 }
363 while (fchdir(fdwdir) == -1)
364 pause1cannot("change to initial working directory");
365 }
366 }
367 }
368 if (errno) pause2cannot("write to current", ld->name);
369 }
370
371 ld->size += i;
372 if (ld->sizemax)
373 if (s[i-1] == '\n')
374 if (ld->size >= (ld->sizemax - linemax))
375 rotate(ld);
376 return i;
377}
378
379static void logdir_close(struct logdir *ld)
380{
381 if (ld->fddir == -1)
382 return;
383 if (verbose)
384 bb_error_msg(INFO"close: %s", ld->name);
385 close(ld->fddir);
386 ld->fddir = -1;
387 if (ld->fdcur == -1)
388 return; /* impossible */
389 while (fsync(ld->fdcur) == -1)
390 pause2cannot("fsync current logfile", ld->name);
391 while (fchmod(ld->fdcur, 0744) == -1)
392 pause2cannot("set mode of current", ld->name);
393 close(ld->fdcur);
394 ld->fdcur = -1;
395 if (ld->fdlock == -1)
396 return; /* impossible */
397 close(ld->fdlock);
398 ld->fdlock = -1;
399 free(ld->processor);
400 ld->processor = NULL;
401}
402
403static unsigned logdir_open(struct logdir *ld, const char *fn)
404{
405 char buf[128];
406 struct taia now;
407 char *new, *s, *np;
408 int i;
409 struct stat st;
410
411 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
412 if (ld->fddir == -1) {
413 warn2("cannot open log directory", (char*)fn);
414 return 0;
415 }
416 coe(ld->fddir);
417 if (fchdir(ld->fddir) == -1) {
418 logdir_close(ld);
419 warn2("cannot change directory", (char*)fn);
420 return 0;
421 }
422 ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
423 if ((ld->fdlock == -1)
424 || (lock_exnb(ld->fdlock) == -1)
425 ) {
426 logdir_close(ld);
427 warn2("cannot lock directory", (char*)fn);
428 while (fchdir(fdwdir) == -1)
429 pause1cannot("change to initial working directory");
430 return 0;
431 }
432 coe(ld->fdlock);
433
434 ld->size = 0;
435 ld->sizemax = 1000000;
436 ld->nmax = ld->nmin = 10;
437 ld->tmax = 0;
438 ld->name = (char*)fn;
439 ld->ppid = 0;
440 ld->match = '+';
441 free(ld->inst); ld->inst = NULL;
442 free(ld->processor); ld->processor = NULL;
443
444 /* read config */
445 i = open_read_close("config", buf, sizeof(buf));
446 if (i < 0)
447 warn2("cannot read config", ld->name);
448 if (i > 0) {
449 if (verbose) bb_error_msg(INFO"read: %s/config", ld->name);
450 s = buf;
451 while (s) {
452 np = strchr(s, '\n');
453 if (np) *np++ = '\0';
454 switch (s[0]) {
455 case '+':
456 case '-':
457 case 'e':
458 case 'E':
459 while (1) {
460 int l = asprintf(&new, "%s%s\n", ld->inst?:"", s);
461 if (l >= 0 && new) break;
462 pause_nomem();
463 }
464 free(ld->inst);
465 ld->inst = new;
466 break;
467 case 's': {
468 static const struct suffix_mult km_suffixes[] = {
469 { "k", 1024 },
470 { "m", 1024*1024 },
471 { NULL, 0 }
472 };
473 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
474 break;
475 }
476 case 'n':
477 ld->nmax = xatoi_u(&s[1]);
478 break;
479 case 'N':
480 ld->nmin = xatoi_u(&s[1]);
481 break;
482 case 't': {
483 static const struct suffix_mult mh_suffixes[] = {
484 { "m", 60 },
485 { "h", 60*60 },
486 /*{ "d", 24*60*60 },*/
487 { NULL, 0 }
488 };
489 ld->tmax = xatou_sfx(&s[1], mh_suffixes);
490 if (ld->tmax) {
491 taia_uint(&ld->trotate, ld->tmax);
492 taia_add(&ld->trotate, &now, &ld->trotate);
493 if (!tmaxflag || taia_less(&ld->trotate, &trotate))
494 trotate = ld->trotate;
495 tmaxflag = 1;
496 }
497 break;
498 }
499 case '!':
500 if (s[1]) {
501 free(ld->processor);
502 ld->processor = wstrdup(s);
503 }
504 break;
505 }
506 s = np;
507 }
508 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
509 s = ld->inst;
510 while (s) {
511 np = strchr(s, '\n');
512 if (np) *np++ = '\0';
513 s = np;
514 }
515 }
516
517 /* open current */
518 i = stat("current", &st);
519 if (i != -1) {
520 if (st.st_size && ! (st.st_mode & S_IXUSR)) {
521 ld->fnsave[25] = '.';
522 ld->fnsave[26] = 'u';
523 ld->fnsave[27] = '\0';
524 do {
525 taia_now(&now);
526 fmt_taia(ld->fnsave, &now);
527 errno = 0;
528 } while ((stat(ld->fnsave, &st) != -1) || (errno != ENOENT));
529 while (rename("current", ld->fnsave) == -1)
530 pause2cannot("rename current", ld->name);
531 rmoldest(ld);
532 i = -1;
533 } else {
534 /* Be paranoid: st.st_size can be not just bigger, but WIDER! */
535 /* (bug in original svlogd. remove this comment when fixed there) */
536 ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
537 }
538 } else {
539 if (errno != ENOENT) {
540 logdir_close(ld);
541 warn2("cannot stat current", ld->name);
542 while (fchdir(fdwdir) == -1)
543 pause1cannot("change to initial working directory");
544 return 0;
545 }
546 }
547 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
548 pause2cannot("open current", ld->name);
549 coe(ld->fdcur);
550 while (fchmod(ld->fdcur, 0644) == -1)
551 pause2cannot("set mode of current", ld->name);
552
553 if (verbose) {
554 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
555 else bb_error_msg(INFO"new: %s/current", ld->name);
556 }
557
558 while (fchdir(fdwdir) == -1)
559 pause1cannot("change to initial working directory");
560 return 1;
561}
562
563static void logdirs_reopen(void)
564{
565 struct taia now;
566 int l;
567 int ok = 0;
568
569 tmaxflag = 0;
570 taia_now(&now);
571 for (l = 0; l < dirn; ++l) {
572 logdir_close(&dir[l]);
573 if (logdir_open(&dir[l], fndir[l])) ok = 1;
574 }
575 if (!ok) fatalx("no functional log directories");
576}
577
578/* Used for reading stdin */
579static int buffer_pread(int fd, char *s, unsigned len)
580{
581 struct taia now;
582 int i;
583
584 if (rotateasap) {
585 for (i = 0; i < dirn; ++i)
586 rotate(dir+i);
587 rotateasap = 0;
588 }
589 if (exitasap) {
590 if (linecomplete)
591 return 0;
592 len = 1;
593 }
594 if (reopenasap) {
595 logdirs_reopen();
596 reopenasap = 0;
597 }
598 taia_now(&now);
599 taia_uint(&trotate, 2744);
600 taia_add(&trotate, &now, &trotate);
601 for (i = 0; i < dirn; ++i)
602 if (dir[i].tmax) {
603 if (taia_less(&dir[i].trotate, &now))
604 rotate(dir+i);
605 if (taia_less(&dir[i].trotate, &trotate))
606 trotate = dir[i].trotate;
607 }
608
609 while (1) {
610 /* Comment? */
611 sig_unblock(sig_term);
612 sig_unblock(sig_child);
613 sig_unblock(sig_alarm);
614 sig_unblock(sig_hangup);
615 iopause(&in, 1, &trotate, &now);
616 sig_block(sig_term);
617 sig_block(sig_child);
618 sig_block(sig_alarm);
619 sig_block(sig_hangup);
620 i = safe_read(fd, s, len);
621 if (i >= 0) break;
622 if (errno != EAGAIN) {
623 warn("cannot read standard input");
624 break;
625 }
626 /* else: EAGAIN - normal, repeat silently */
627 }
628
629 if (i > 0) {
630 int cnt;
631 linecomplete = (s[i-1] == '\n');
632 if (!repl) return i;
633
634 cnt = i;
635 while (--cnt >= 0) {
636 char ch = *s;
637 if (ch != '\n') {
638 if (ch < 32 || ch > 126)
639 *s = repl;
640 else {
641 int j;
642 for (j = 0; replace[j]; ++j) {
643 if (ch == replace[j]) {
644 *s = repl;
645 break;
646 }
647 }
648 }
649 }
650 s++;
651 }
652 }
653 return i;
654}
655
656
657static void sig_term_handler(int sig_no)
658{
659 if (verbose)
660 bb_error_msg(INFO"sig%s received", "term");
661 exitasap = 1;
662}
663
664static void sig_child_handler(int sig_no)
665{
666 int pid, l;
667
668 if (verbose)
669 bb_error_msg(INFO"sig%s received", "child");
670 while ((pid = wait_nohang(&wstat)) > 0)
671 for (l = 0; l < dirn; ++l)
672 if (dir[l].ppid == pid) {
673 dir[l].ppid = 0;
674 processorstop(&dir[l]);
675 break;
676 }
677}
678
679static void sig_alarm_handler(int sig_no)
680{
681 if (verbose)
682 bb_error_msg(INFO"sig%s received", "alarm");
683 rotateasap = 1;
684}
685
686static void sig_hangup_handler(int sig_no)
687{
688 if (verbose)
689 bb_error_msg(INFO"sig%s received", "hangup");
690 reopenasap = 1;
691}
692
693static void logmatch(struct logdir *ld)
694{
695 char *s;
696
697 ld->match = '+';
698 ld->matcherr = 'E';
699 s = ld->inst;
700 while (s && s[0]) {
701 switch (s[0]) {
702 case '+':
703 case '-':
704 if (pmatch(s+1, line, linelen))
705 ld->match = s[0];
706 break;
707 case 'e':
708 case 'E':
709 if (pmatch(s+1, line, linelen))
710 ld->matcherr = s[0];
711 break;
712 }
713 s += strlen(s) + 1;
714 }
715}
716
717int svlogd_main(int argc, char **argv)
718{
719 struct taia now;
720 char *r,*l,*b;
721 ssize_t stdin_cnt = 0;
722 int i;
723 unsigned opt;
724 unsigned timestamp = 0;
725
726 opt_complementary = "tt:vv";
727 opt = getopt32(argc, argv, "r:R:l:b:tv",
728 &r, &replace, &l, &b, &timestamp, &verbose);
729 if (opt & 1) { // -r
730 repl = r[0];
731 if (!repl || r[1]) usage();
732 }
733 if (opt & 2) if (!repl) repl = '_'; // -R
734 if (opt & 4) { // -l
735 linemax = xatou_range(l, 0, 1000);
736 if (linemax == 0) linemax = 1000;
737 if (linemax < 256) linemax = 256;
738 }
739 if (opt & 8) { // -b
740 buflen = xatoi_u(b);
741 if (buflen == 0) buflen = 1024;
742 }
743 //if (opt & 0x10) timestamp++; // -t
744 //if (opt & 0x20) verbose++; // -v
745 if (timestamp > 2) timestamp = 2;
746 argv += optind;
747 argc -= optind;
748
749 dirn = argc;
750 if (dirn <= 0) usage();
751 if (buflen <= linemax) usage();
752 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
753 coe(fdwdir);
754 dir = xmalloc(dirn * sizeof(struct logdir));
755 for (i = 0; i < dirn; ++i) {
756 dir[i].fddir = -1;
757 dir[i].fdcur = -1;
758 dir[i].btmp = xmalloc(buflen);
759 dir[i].ppid = 0;
760 }
761 line = xmalloc(linemax + (timestamp ? 26 : 0));
762 fndir = argv;
763 in.fd = 0;
764 in.events = IOPAUSE_READ;
765 ndelay_on(in.fd);
766
767 sig_block(sig_term);
768 sig_block(sig_child);
769 sig_block(sig_alarm);
770 sig_block(sig_hangup);
771 sig_catch(sig_term, sig_term_handler);
772 sig_catch(sig_child, sig_child_handler);
773 sig_catch(sig_alarm, sig_alarm_handler);
774 sig_catch(sig_hangup, sig_hangup_handler);
775
776 logdirs_reopen();
777
778 /* Each iteration processes one line */
779 while (1) {
780 int printlen;
781 char *lineptr = line;
782 char *np;
783 char ch;
784
785 /* Prepare timestamp if needed */
786 if (timestamp) {
787 char stamp[FMT_PTIME];
788 taia_now(&now);
789 switch (timestamp) {
790 case 1:
791 fmt_taia(stamp, &now);
792 break;
793 default: /* case 2: */
794 fmt_ptime(stamp, &now);
795 break;
796 }
797 memcpy(line, stamp, 25);
798 line[25] = ' ';
799 lineptr += 26;
800 }
801
802 /* lineptr[0..linemax-1] - buffer for stdin */
803 /* (possibly has some unprocessed data from prev loop) */
804
805 /* Refill the buffer if needed */
806 np = memchr(lineptr, '\n', stdin_cnt);
807 i = linemax - stdin_cnt; /* avail. bytes at tail */
808 if (i >= 128 && !exitasap && !np) {
809 int sz = buffer_pread(0, lineptr + stdin_cnt, i);
810 if (sz <= 0) /* EOF or error on stdin */
811 exitasap = 1;
812 else {
813 stdin_cnt += sz;
814 np = memchr(lineptr, '\n', stdin_cnt);
815 }
816 }
817 if (stdin_cnt <= 0 && exitasap)
818 break;
819
820 /* Search for '\n' (in fact, np already holds the result) */
821 linelen = stdin_cnt;
822 if (np) linelen = np - lineptr + 1;
823 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
824 ch = lineptr[linelen-1];
825
826 printlen = linelen + (timestamp ? 26 : 0);
827 /* write out line[0..printlen-1] to each log destination */
828 for (i = 0; i < dirn; ++i) {
829 struct logdir *ld = &dir[i];
830 if (ld->fddir == -1) continue;
831 if (ld->inst)
832 logmatch(ld);
Denis Vlasenko83edaf32006-11-19 17:33:54 +0000833 if (ld->matcherr == 'e')
834 full_write(2, line, printlen);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000835 if (ld->match != '+') continue;
836 buffer_pwrite(i, line, printlen);
837 }
838
839 /* If we didn't see '\n' (long input line), */
840 /* read/write repeatedly until we see it */
841 while (ch != '\n') {
842 /* lineptr is emptied now, safe to use as buffer */
843 stdin_cnt = exitasap ? -1 : buffer_pread(0, lineptr, linemax);
844 if (stdin_cnt <= 0) { /* EOF or error on stdin */
845 lineptr[0] = ch = '\n';
846 linelen = 1;
847 exitasap = 1;
848 stdin_cnt = 1;
849 } else {
850 linelen = stdin_cnt;
851 np = memchr(lineptr, '\n', stdin_cnt);
852 if (np) linelen = np - lineptr + 1;
853 ch = lineptr[linelen-1];
854 }
855 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
856 for (i = 0; i < dirn; ++i) {
857 if (dir[i].fddir == -1) continue;
Denis Vlasenko83edaf32006-11-19 17:33:54 +0000858 if (dir[i].matcherr == 'e')
859 full_write(2, lineptr, linelen);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000860 if (dir[i].match != '+') continue;
861 buffer_pwrite(i, lineptr, linelen);
862 }
863 }
864
865 /* Move unprocessed data to the front of line */
866 stdin_cnt -= linelen;
867 if (stdin_cnt > 0) /* TODO: slow if buffer is big */
868 memmove(lineptr, &lineptr[linelen], stdin_cnt);
869 }
870
871 for (i = 0; i < dirn; ++i) {
872 if (dir[i].ppid)
873 while (!processorstop(&dir[i]))
874 /* repeat */;
875 logdir_close(&dir[i]);
876 }
877 _exit(0);
878}