2 Copyright (c) 2001-2006, Gerrit Pape
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
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.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 /* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
29 /* TODO: depends on runit_lib.c - review and reduce/eliminate */
34 #include "runit_lib.h"
36 static unsigned verbose;
37 static int linemax = 1000;
38 ////static int buflen = 1024;
44 static struct taia trotate;
47 static smallint exitasap;
48 static smallint rotateasap;
49 static smallint reopenasap;
50 static smallint linecomplete = 1;
52 static smallint tmaxflag;
55 static const char *replace = "";
57 static sigset_t *blocked_sigset;
58 static iopause_fd input;
61 static struct logdir {
63 /* pattern list to match, in "aa\0bb\0\cc\0\0" form */
71 /* int (not long) because of taia_uint() usage: */
79 char fnsave[FMT_PTIME];
85 #define FATAL "fatal: "
86 #define WARNING "warning: "
87 #define PAUSE "pausing: "
90 #define usage() bb_show_usage()
91 static void fatalx(const char *m0)
93 bb_error_msg_and_die(FATAL"%s", m0);
95 static void warn(const char *m0)
97 bb_perror_msg(WARNING"%s", m0);
99 static void warn2(const char *m0, const char *m1)
101 bb_perror_msg(WARNING"%s: %s", m0, m1);
103 static void warnx(const char *m0, const char *m1)
105 bb_error_msg(WARNING"%s: %s", m0, m1);
107 static void pause_nomem(void)
109 bb_error_msg(PAUSE"out of memory");
112 static void pause1cannot(const char *m0)
114 bb_perror_msg(PAUSE"cannot %s", m0);
117 static void pause2cannot(const char *m0, const char *m1)
119 bb_perror_msg(PAUSE"cannot %s %s", m0, m1);
123 static char* wstrdup(const char *str)
126 while (!(s = strdup(str)))
131 static unsigned processorstart(struct logdir *ld)
135 if (!ld->processor) return 0;
137 warnx("processor already running", ld->name);
140 while ((pid = fork()) == -1)
141 pause2cannot("fork for processor", ld->name);
147 signal(SIGTERM, SIG_DFL);
148 signal(SIGALRM, SIG_DFL);
149 signal(SIGHUP, SIG_DFL);
150 sig_unblock(SIGTERM);
151 sig_unblock(SIGALRM);
155 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
156 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
158 ld->fnsave[26] = 't';
159 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
161 fd = open_read("state");
164 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "open state for", ld->name);
165 close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
166 fd = xopen("state", O_RDONLY|O_NDELAY);
169 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
173 prog[0] = (char*)"sh";
174 prog[1] = (char*)"-c";
175 prog[2] = ld->processor;
177 execve("/bin/sh", prog, environ);
178 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "run", ld->name);
184 static unsigned processorstop(struct logdir *ld)
190 while (wait_pid(&wstat, ld->ppid) == -1)
191 pause2cannot("wait for processor", ld->name);
195 if (ld->fddir == -1) return 1;
196 while (fchdir(ld->fddir) == -1)
197 pause2cannot("change directory, want processor", ld->name);
198 if (wait_exitcode(wstat) != 0) {
199 warnx("processor failed, restart", ld->name);
200 ld->fnsave[26] = 't';
202 ld->fnsave[26] = 'u';
204 while (fchdir(fdwdir) == -1)
205 pause1cannot("change to initial working directory");
206 return ld->processor ? 0 : 1;
208 ld->fnsave[26] = 't';
209 memcpy(f, ld->fnsave, 26);
212 while (rename(ld->fnsave, f) == -1)
213 pause2cannot("rename processed", ld->name);
214 while (chmod(f, 0744) == -1)
215 pause2cannot("set mode of processed", ld->name);
216 ld->fnsave[26] = 'u';
217 if (unlink(ld->fnsave) == -1)
218 bb_error_msg(WARNING"cannot unlink: %s/%s", ld->name, ld->fnsave);
219 while (rename("newstate", "state") == -1)
220 pause2cannot("rename state", ld->name);
222 bb_error_msg(INFO"processed: %s/%s", ld->name, f);
223 while (fchdir(fdwdir) == -1)
224 pause1cannot("change to initial working directory");
228 static void rmoldest(struct logdir *ld)
232 char oldest[FMT_PTIME];
235 oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
236 while (!(d = opendir(".")))
237 pause2cannot("open directory, want rotate", ld->name);
239 while ((f = readdir(d))) {
240 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
241 if (f->d_name[26] == 't') {
242 if (unlink(f->d_name) == -1)
243 warn2("cannot unlink processor leftover", f->d_name);
246 if (strcmp(f->d_name, oldest) < 0)
247 memcpy(oldest, f->d_name, 27);
253 warn2("cannot read directory", ld->name);
256 if (ld->nmax && (n > ld->nmax)) {
258 bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
259 if ((*oldest == '@') && (unlink(oldest) == -1))
260 warn2("cannot unlink oldest logfile", ld->name);
264 static unsigned rotate(struct logdir *ld)
269 if (ld->fddir == -1) {
274 while (!processorstop(ld))
277 while (fchdir(ld->fddir) == -1)
278 pause2cannot("change directory, want rotate", ld->name);
280 /* create new filename */
281 ld->fnsave[25] = '.';
282 ld->fnsave[26] = 's';
284 ld->fnsave[26] = 'u';
285 ld->fnsave[27] = '\0';
288 fmt_taia25(ld->fnsave, &now);
290 stat(ld->fnsave, &st);
291 } while (errno != ENOENT);
293 if (ld->tmax && taia_less(&ld->trotate, &now)) {
294 taia_uint(&ld->trotate, ld->tmax);
295 taia_add(&ld->trotate, &now, &ld->trotate);
296 if (taia_less(&ld->trotate, &trotate))
297 trotate = ld->trotate;
301 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
302 pause2cannot("fsync current logfile", ld->name);
303 while (fchmod(ld->fdcur, 0744) == -1)
304 pause2cannot("set mode of current", ld->name);
305 ////close(ld->fdcur);
309 bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
310 ld->fnsave, ld->size);
312 while (rename("current", ld->fnsave) == -1)
313 pause2cannot("rename current", ld->name);
314 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
315 pause2cannot("create new current", ld->name);
316 /* we presume this cannot fail */
317 ld->filecur = fdopen(ld->fdcur, "a"); ////
318 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
321 while (fchmod(ld->fdcur, 0644) == -1)
322 pause2cannot("set mode of current", ld->name);
327 while (fchdir(fdwdir) == -1)
328 pause1cannot("change to initial working directory");
332 static int buffer_pwrite(int n, char *s, unsigned len)
335 struct logdir *ld = &dir[n];
338 if (ld->size >= ld->sizemax)
340 if (len > (ld->sizemax - ld->size))
341 len = ld->sizemax - ld->size;
344 ////i = full_write(ld->fdcur, s, len);
345 ////if (i != -1) break;
346 i = fwrite(s, 1, len, ld->filecur);
349 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
352 char oldest[FMT_PTIME];
355 while (fchdir(ld->fddir) == -1)
356 pause2cannot("change directory, want remove old logfile",
359 oldest[1] = oldest[27] = '\0';
360 while (!(d = opendir(".")))
361 pause2cannot("open directory, want remove old logfile",
364 while ((f = readdir(d)))
365 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
367 if (strcmp(f->d_name, oldest) < 0)
368 memcpy(oldest, f->d_name, 27);
370 if (errno) warn2("cannot read directory, want remove old logfile",
375 if (*oldest == '@') {
376 bb_error_msg(WARNING"out of disk space, delete: %s/%s",
379 if (unlink(oldest) == -1) {
380 warn2("cannot unlink oldest logfile", ld->name);
383 while (fchdir(fdwdir) == -1)
384 pause1cannot("change to initial working directory");
389 pause2cannot("write to current", ld->name);
395 if (ld->size >= (ld->sizemax - linemax))
400 static void logdir_close(struct logdir *ld)
405 bb_error_msg(INFO"close: %s", ld->name);
409 return; /* impossible */
410 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
411 pause2cannot("fsync current logfile", ld->name);
412 while (fchmod(ld->fdcur, 0744) == -1)
413 pause2cannot("set mode of current", ld->name);
414 ////close(ld->fdcur);
417 if (ld->fdlock == -1)
418 return; /* impossible */
422 ld->processor = NULL;
425 static unsigned logdir_open(struct logdir *ld, const char *fn)
433 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
434 if (ld->fddir == -1) {
435 warn2("cannot open log directory", (char*)fn);
439 if (fchdir(ld->fddir) == -1) {
441 warn2("cannot change directory", (char*)fn);
444 ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
445 if ((ld->fdlock == -1)
446 || (lock_exnb(ld->fdlock) == -1)
449 warn2("cannot lock directory", (char*)fn);
450 while (fchdir(fdwdir) == -1)
451 pause1cannot("change to initial working directory");
457 ld->sizemax = 1000000;
458 ld->nmax = ld->nmin = 10;
460 ld->name = (char*)fn;
463 free(ld->inst); ld->inst = NULL;
464 free(ld->processor); ld->processor = NULL;
467 i = open_read_close("config", buf, sizeof(buf));
469 warn2("cannot read config", ld->name);
471 if (verbose) bb_error_msg(INFO"read: %s/config", ld->name);
474 np = strchr(s, '\n');
475 if (np) *np++ = '\0';
482 int l = asprintf(&new, "%s%s\n", ld->inst?:"", s);
483 if (l >= 0 && new) break;
490 static const struct suffix_mult km_suffixes[] = {
495 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
499 ld->nmax = xatoi_u(&s[1]);
502 ld->nmin = xatoi_u(&s[1]);
505 static const struct suffix_mult mh_suffixes[] = {
508 /*{ "d", 24*60*60 },*/
511 ld->tmax = xatou_sfx(&s[1], mh_suffixes);
513 taia_uint(&ld->trotate, ld->tmax);
514 taia_add(&ld->trotate, &now, &ld->trotate);
515 if (!tmaxflag || taia_less(&ld->trotate, &trotate))
516 trotate = ld->trotate;
524 ld->processor = wstrdup(s);
530 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
533 np = strchr(s, '\n');
534 if (np) *np++ = '\0';
540 i = stat("current", &st);
542 if (st.st_size && ! (st.st_mode & S_IXUSR)) {
543 ld->fnsave[25] = '.';
544 ld->fnsave[26] = 'u';
545 ld->fnsave[27] = '\0';
548 fmt_taia25(ld->fnsave, &now);
550 stat(ld->fnsave, &st);
551 } while (errno != ENOENT);
552 while (rename("current", ld->fnsave) == -1)
553 pause2cannot("rename current", ld->name);
557 /* Be paranoid: st.st_size can be not just bigger, but WIDER! */
558 /* (bug in original svlogd. remove this comment when fixed there) */
559 ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
562 if (errno != ENOENT) {
564 warn2("cannot stat current", ld->name);
565 while (fchdir(fdwdir) == -1)
566 pause1cannot("change to initial working directory");
570 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
571 pause2cannot("open current", ld->name);
572 /* we presume this cannot fail */
573 ld->filecur = fdopen(ld->fdcur, "a"); ////
574 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
577 while (fchmod(ld->fdcur, 0644) == -1)
578 pause2cannot("set mode of current", ld->name);
581 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
582 else bb_error_msg(INFO"new: %s/current", ld->name);
585 while (fchdir(fdwdir) == -1)
586 pause1cannot("change to initial working directory");
590 static void logdirs_reopen(void)
598 for (l = 0; l < dirn; ++l) {
599 logdir_close(&dir[l]);
600 if (logdir_open(&dir[l], fndir[l])) ok = 1;
602 if (!ok) fatalx("no functional log directories");
605 /* Will look good in libbb one day */
606 static ssize_t ndelay_read(int fd, void *buf, size_t count)
608 if (!(fl_flag_0 & O_NONBLOCK))
609 fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
610 count = safe_read(fd, buf, count);
611 if (!(fl_flag_0 & O_NONBLOCK))
612 fcntl(fd, F_SETFL, fl_flag_0);
616 /* Used for reading stdin */
617 static int buffer_pread(int fd, char *s, unsigned len, struct taia *now)
622 for (i = 0; i < dirn; ++i)
635 taia_uint(&trotate, 2744);
636 taia_add(&trotate, now, &trotate);
637 for (i = 0; i < dirn; ++i)
639 if (taia_less(&dir[i].trotate, now))
641 if (taia_less(&dir[i].trotate, &trotate))
642 trotate = dir[i].trotate;
646 sigprocmask(SIG_UNBLOCK, blocked_sigset, NULL);
647 iopause(&input, 1, &trotate, now);
648 sigprocmask(SIG_BLOCK, blocked_sigset, NULL);
649 i = ndelay_read(fd, s, len);
651 if (errno != EAGAIN) {
652 warn("cannot read standard input");
655 /* else: EAGAIN - normal, repeat silently */
660 linecomplete = (s[i-1] == '\n');
667 if (ch < 32 || ch > 126)
671 for (j = 0; replace[j]; ++j) {
672 if (ch == replace[j]) {
686 static void sig_term_handler(int sig_no)
689 bb_error_msg(INFO"sig%s received", "term");
693 static void sig_child_handler(int sig_no)
698 bb_error_msg(INFO"sig%s received", "child");
699 while ((pid = wait_nohang(&wstat)) > 0)
700 for (l = 0; l < dirn; ++l)
701 if (dir[l].ppid == pid) {
703 processorstop(&dir[l]);
708 static void sig_alarm_handler(int sig_no)
711 bb_error_msg(INFO"sig%s received", "alarm");
715 static void sig_hangup_handler(int sig_no)
718 bb_error_msg(INFO"sig%s received", "hangup");
722 static void logmatch(struct logdir *ld)
733 if (pmatch(s+1, line, linelen))
738 if (pmatch(s+1, line, linelen))
746 int svlogd_main(int argc, char **argv);
747 int svlogd_main(int argc, char **argv)
751 ssize_t stdin_cnt = 0;
754 unsigned timestamp = 0;
755 void* (*memRchr)(const void *, int, size_t) = memchr;
757 #define line bb_common_bufsiz1
759 opt_complementary = "tt:vv";
760 opt = getopt32(argc, argv, "r:R:l:b:tv",
761 &r, &replace, &l, &b, ×tamp, &verbose);
764 if (!repl || r[1]) usage();
766 if (opt & 2) if (!repl) repl = '_'; // -R
768 linemax = xatou_range(l, 0, BUFSIZ-26);
769 if (linemax == 0) linemax = BUFSIZ-26;
770 if (linemax < 256) linemax = 256;
772 ////if (opt & 8) { // -b
773 //// buflen = xatoi_u(b);
774 //// if (buflen == 0) buflen = 1024;
776 //if (opt & 0x10) timestamp++; // -t
777 //if (opt & 0x20) verbose++; // -v
778 //if (timestamp > 2) timestamp = 2;
783 if (dirn <= 0) usage();
784 ////if (buflen <= linemax) usage();
785 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
787 dir = xmalloc(dirn * sizeof(struct logdir));
788 for (i = 0; i < dirn; ++i) {
791 ////dir[i].btmp = xmalloc(buflen);
794 /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
797 input.events = IOPAUSE_READ;
798 /* We cannot set NONBLOCK on fd #0 permanently - this setting
799 * _isn't_ per-process! It is shared among all other processes
800 * with the same stdin */
801 fl_flag_0 = fcntl(0, F_GETFL, 0);
803 blocked_sigset = &ss;
805 sigaddset(&ss, SIGTERM);
806 sigaddset(&ss, SIGCHLD);
807 sigaddset(&ss, SIGALRM);
808 sigaddset(&ss, SIGHUP);
809 sigprocmask(SIG_BLOCK, &ss, NULL);
810 sig_catch(SIGTERM, sig_term_handler);
811 sig_catch(SIGCHLD, sig_child_handler);
812 sig_catch(SIGALRM, sig_alarm_handler);
813 sig_catch(SIGHUP, sig_hangup_handler);
817 /* Without timestamps, we don't have to print each line
818 * separately, so we can look for _last_ newline, not first,
819 * thus batching writes */
823 setvbuf(stderr, NULL, _IOFBF, linelen);
825 /* Each iteration processes one or more lines */
828 char stamp[FMT_PTIME];
837 /* Prepare timestamp if needed */
841 fmt_taia25(stamp, &now);
843 default: /* case 2: */
844 fmt_ptime30nul(stamp, &now);
850 /* lineptr[0..linemax-1] - buffer for stdin */
851 /* (possibly has some unprocessed data from prev loop) */
853 /* Refill the buffer if needed */
854 np = memRchr(lineptr, '\n', stdin_cnt);
855 if (!np && !exitasap) {
856 i = linemax - stdin_cnt; /* avail. bytes at tail */
858 i = buffer_pread(0, lineptr + stdin_cnt, i, &now);
859 if (i <= 0) /* EOF or error on stdin */
862 np = memRchr(lineptr + stdin_cnt, '\n', i);
867 if (stdin_cnt <= 0 && exitasap)
870 /* Search for '\n' (in fact, np already holds the result) */
873 print_to_nl: /* NB: starting from here lineptr may point
874 * farther out into line[] */
875 linelen = np - lineptr + 1;
877 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
878 ch = lineptr[linelen-1];
880 /* Biggest performance hit was coming from the fact
881 * that we did not buffer writes. We were reading many lines
882 * in one read() above, but wrote one line per write().
883 * We are using stdio to fix that */
885 /* write out lineptr[0..linelen-1] to each log destination
886 * (or lineptr[-26..linelen-1] if timestamping) */
892 memcpy(printptr, stamp, 25);
895 for (i = 0; i < dirn; ++i) {
896 struct logdir *ld = &dir[i];
897 if (ld->fddir == -1) continue;
900 if (ld->matcherr == 'e')
901 ////full_write(2, printptr, printlen);
902 fwrite(lineptr, 1, linelen, stderr);
903 if (ld->match != '+') continue;
904 buffer_pwrite(i, printptr, printlen);
907 /* If we didn't see '\n' (long input line), */
908 /* read/write repeatedly until we see it */
910 /* lineptr is emptied now, safe to use as buffer */
912 stdin_cnt = exitasap ? -1 : buffer_pread(0, lineptr, linemax, &now);
913 if (stdin_cnt <= 0) { /* EOF or error on stdin */
915 lineptr[0] = ch = '\n';
920 np = memRchr(lineptr, '\n', stdin_cnt);
922 linelen = np - lineptr + 1;
923 ch = lineptr[linelen-1];
925 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
926 for (i = 0; i < dirn; ++i) {
927 if (dir[i].fddir == -1) continue;
928 if (dir[i].matcherr == 'e')
929 ////full_write(2, lineptr, linelen);
930 fwrite(lineptr, 1, linelen, stderr);
931 if (dir[i].match != '+') continue;
932 buffer_pwrite(i, lineptr, linelen);
936 stdin_cnt -= linelen;
939 /* If we see another '\n', we don't need to read
940 * next piece of input: can print what we have */
941 np = memRchr(lineptr, '\n', stdin_cnt);
944 /* Move unprocessed data to the front of line */
945 memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
950 for (i = 0; i < dirn; ++i) {
952 while (!processorstop(&dir[i]))
954 logdir_close(&dir[i]);