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) {
96 bb_perror_msg(WARNING"%s", m0);
98 static void warn2(const char *m0, const char *m1)
100 bb_perror_msg(WARNING"%s: %s", m0, m1);
102 static void warnx(const char *m0, const char *m1)
104 bb_error_msg(WARNING"%s: %s", m0, m1);
106 static void pause_nomem(void)
108 bb_error_msg(PAUSE"out of memory");
111 static void pause1cannot(const char *m0)
113 bb_perror_msg(PAUSE"cannot %s", m0);
116 static void pause2cannot(const char *m0, const char *m1)
118 bb_perror_msg(PAUSE"cannot %s %s", m0, m1);
122 static char* wstrdup(const char *str)
125 while (!(s = strdup(str)))
130 static unsigned processorstart(struct logdir *ld)
134 if (!ld->processor) return 0;
136 warnx("processor already running", ld->name);
139 while ((pid = fork()) == -1)
140 pause2cannot("fork for processor", ld->name);
146 sig_uncatch(SIGTERM);
147 sig_uncatch(SIGALRM);
149 sig_unblock(SIGTERM);
150 sig_unblock(SIGALRM);
154 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
155 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
157 ld->fnsave[26] = 't';
158 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
160 fd = open_read("state");
163 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "open state for", ld->name);
164 close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
165 fd = xopen("state", O_RDONLY|O_NDELAY);
168 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
172 prog[0] = (char*)"sh";
173 prog[1] = (char*)"-c";
174 prog[2] = ld->processor;
176 execve("/bin/sh", prog, environ);
177 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "run", ld->name);
183 static unsigned processorstop(struct logdir *ld)
189 while (wait_pid(&wstat, ld->ppid) == -1)
190 pause2cannot("wait for processor", ld->name);
194 if (ld->fddir == -1) return 1;
195 while (fchdir(ld->fddir) == -1)
196 pause2cannot("change directory, want processor", ld->name);
197 if (wait_exitcode(wstat) != 0) {
198 warnx("processor failed, restart", ld->name);
199 ld->fnsave[26] = 't';
201 ld->fnsave[26] = 'u';
203 while (fchdir(fdwdir) == -1)
204 pause1cannot("change to initial working directory");
205 return ld->processor ? 0 : 1;
207 ld->fnsave[26] = 't';
208 memcpy(f, ld->fnsave, 26);
211 while (rename(ld->fnsave, f) == -1)
212 pause2cannot("rename processed", ld->name);
213 while (chmod(f, 0744) == -1)
214 pause2cannot("set mode of processed", ld->name);
215 ld->fnsave[26] = 'u';
216 if (unlink(ld->fnsave) == -1)
217 bb_error_msg(WARNING"cannot unlink: %s/%s", ld->name, ld->fnsave);
218 while (rename("newstate", "state") == -1)
219 pause2cannot("rename state", ld->name);
221 bb_error_msg(INFO"processed: %s/%s", ld->name, f);
222 while (fchdir(fdwdir) == -1)
223 pause1cannot("change to initial working directory");
227 static void rmoldest(struct logdir *ld)
231 char oldest[FMT_PTIME];
234 oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
235 while (!(d = opendir(".")))
236 pause2cannot("open directory, want rotate", ld->name);
238 while ((f = readdir(d))) {
239 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
240 if (f->d_name[26] == 't') {
241 if (unlink(f->d_name) == -1)
242 warn2("cannot unlink processor leftover", f->d_name);
245 if (strcmp(f->d_name, oldest) < 0)
246 memcpy(oldest, f->d_name, 27);
252 warn2("cannot read directory", ld->name);
255 if (ld->nmax && (n > ld->nmax)) {
257 bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
258 if ((*oldest == '@') && (unlink(oldest) == -1))
259 warn2("cannot unlink oldest logfile", ld->name);
263 static unsigned rotate(struct logdir *ld)
268 if (ld->fddir == -1) {
273 while (!processorstop(ld))
276 while (fchdir(ld->fddir) == -1)
277 pause2cannot("change directory, want rotate", ld->name);
279 /* create new filename */
280 ld->fnsave[25] = '.';
281 ld->fnsave[26] = 's';
283 ld->fnsave[26] = 'u';
284 ld->fnsave[27] = '\0';
287 fmt_taia25(ld->fnsave, &now);
289 stat(ld->fnsave, &st);
290 } while (errno != ENOENT);
292 if (ld->tmax && taia_less(&ld->trotate, &now)) {
293 taia_uint(&ld->trotate, ld->tmax);
294 taia_add(&ld->trotate, &now, &ld->trotate);
295 if (taia_less(&ld->trotate, &trotate))
296 trotate = ld->trotate;
300 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
301 pause2cannot("fsync current logfile", ld->name);
302 while (fchmod(ld->fdcur, 0744) == -1)
303 pause2cannot("set mode of current", ld->name);
304 ////close(ld->fdcur);
308 bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
309 ld->fnsave, ld->size);
311 while (rename("current", ld->fnsave) == -1)
312 pause2cannot("rename current", ld->name);
313 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
314 pause2cannot("create new current", ld->name);
315 /* we presume this cannot fail */
316 ld->filecur = fdopen(ld->fdcur, "a"); ////
317 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
320 while (fchmod(ld->fdcur, 0644) == -1)
321 pause2cannot("set mode of current", ld->name);
326 while (fchdir(fdwdir) == -1)
327 pause1cannot("change to initial working directory");
331 static int buffer_pwrite(int n, char *s, unsigned len)
334 struct logdir *ld = &dir[n];
337 if (ld->size >= ld->sizemax)
339 if (len > (ld->sizemax - ld->size))
340 len = ld->sizemax - ld->size;
343 ////i = full_write(ld->fdcur, s, len);
344 ////if (i != -1) break;
345 i = fwrite(s, 1, len, ld->filecur);
348 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
351 char oldest[FMT_PTIME];
354 while (fchdir(ld->fddir) == -1)
355 pause2cannot("change directory, want remove old logfile",
358 oldest[1] = oldest[27] = '\0';
359 while (!(d = opendir(".")))
360 pause2cannot("open directory, want remove old logfile",
363 while ((f = readdir(d)))
364 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
366 if (strcmp(f->d_name, oldest) < 0)
367 memcpy(oldest, f->d_name, 27);
369 if (errno) warn2("cannot read directory, want remove old logfile",
374 if (*oldest == '@') {
375 bb_error_msg(WARNING"out of disk space, delete: %s/%s",
378 if (unlink(oldest) == -1) {
379 warn2("cannot unlink oldest logfile", ld->name);
382 while (fchdir(fdwdir) == -1)
383 pause1cannot("change to initial working directory");
388 pause2cannot("write to current", ld->name);
394 if (ld->size >= (ld->sizemax - linemax))
399 static void logdir_close(struct logdir *ld)
404 bb_error_msg(INFO"close: %s", ld->name);
408 return; /* impossible */
409 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
410 pause2cannot("fsync current logfile", ld->name);
411 while (fchmod(ld->fdcur, 0744) == -1)
412 pause2cannot("set mode of current", ld->name);
413 ////close(ld->fdcur);
416 if (ld->fdlock == -1)
417 return; /* impossible */
421 ld->processor = NULL;
424 static unsigned logdir_open(struct logdir *ld, const char *fn)
432 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
433 if (ld->fddir == -1) {
434 warn2("cannot open log directory", (char*)fn);
438 if (fchdir(ld->fddir) == -1) {
440 warn2("cannot change directory", (char*)fn);
443 ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
444 if ((ld->fdlock == -1)
445 || (lock_exnb(ld->fdlock) == -1)
448 warn2("cannot lock directory", (char*)fn);
449 while (fchdir(fdwdir) == -1)
450 pause1cannot("change to initial working directory");
456 ld->sizemax = 1000000;
457 ld->nmax = ld->nmin = 10;
459 ld->name = (char*)fn;
462 free(ld->inst); ld->inst = NULL;
463 free(ld->processor); ld->processor = NULL;
466 i = open_read_close("config", buf, sizeof(buf));
468 warn2("cannot read config", ld->name);
470 if (verbose) bb_error_msg(INFO"read: %s/config", ld->name);
473 np = strchr(s, '\n');
474 if (np) *np++ = '\0';
481 int l = asprintf(&new, "%s%s\n", ld->inst?:"", s);
482 if (l >= 0 && new) break;
489 static const struct suffix_mult km_suffixes[] = {
494 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
498 ld->nmax = xatoi_u(&s[1]);
501 ld->nmin = xatoi_u(&s[1]);
504 static const struct suffix_mult mh_suffixes[] = {
507 /*{ "d", 24*60*60 },*/
510 ld->tmax = xatou_sfx(&s[1], mh_suffixes);
512 taia_uint(&ld->trotate, ld->tmax);
513 taia_add(&ld->trotate, &now, &ld->trotate);
514 if (!tmaxflag || taia_less(&ld->trotate, &trotate))
515 trotate = ld->trotate;
523 ld->processor = wstrdup(s);
529 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
532 np = strchr(s, '\n');
533 if (np) *np++ = '\0';
539 i = stat("current", &st);
541 if (st.st_size && ! (st.st_mode & S_IXUSR)) {
542 ld->fnsave[25] = '.';
543 ld->fnsave[26] = 'u';
544 ld->fnsave[27] = '\0';
547 fmt_taia25(ld->fnsave, &now);
549 stat(ld->fnsave, &st);
550 } while (errno != ENOENT);
551 while (rename("current", ld->fnsave) == -1)
552 pause2cannot("rename current", ld->name);
556 /* Be paranoid: st.st_size can be not just bigger, but WIDER! */
557 /* (bug in original svlogd. remove this comment when fixed there) */
558 ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
561 if (errno != ENOENT) {
563 warn2("cannot stat current", ld->name);
564 while (fchdir(fdwdir) == -1)
565 pause1cannot("change to initial working directory");
569 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
570 pause2cannot("open current", ld->name);
571 /* we presume this cannot fail */
572 ld->filecur = fdopen(ld->fdcur, "a"); ////
573 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
576 while (fchmod(ld->fdcur, 0644) == -1)
577 pause2cannot("set mode of current", ld->name);
580 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
581 else bb_error_msg(INFO"new: %s/current", ld->name);
584 while (fchdir(fdwdir) == -1)
585 pause1cannot("change to initial working directory");
589 static void logdirs_reopen(void)
597 for (l = 0; l < dirn; ++l) {
598 logdir_close(&dir[l]);
599 if (logdir_open(&dir[l], fndir[l])) ok = 1;
601 if (!ok) fatalx("no functional log directories");
604 /* Will look good in libbb one day */
605 static ssize_t ndelay_read(int fd, void *buf, size_t count)
607 if (!(fl_flag_0 & O_NONBLOCK))
608 fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
609 count = safe_read(fd, buf, count);
610 if (!(fl_flag_0 & O_NONBLOCK))
611 fcntl(fd, F_SETFL, fl_flag_0);
615 /* Used for reading stdin */
616 static int buffer_pread(int fd, char *s, unsigned len, struct taia *now)
621 for (i = 0; i < dirn; ++i)
634 taia_uint(&trotate, 2744);
635 taia_add(&trotate, now, &trotate);
636 for (i = 0; i < dirn; ++i)
638 if (taia_less(&dir[i].trotate, now))
640 if (taia_less(&dir[i].trotate, &trotate))
641 trotate = dir[i].trotate;
645 sigprocmask(SIG_UNBLOCK, blocked_sigset, NULL);
646 iopause(&input, 1, &trotate, now);
647 sigprocmask(SIG_BLOCK, blocked_sigset, NULL);
648 i = ndelay_read(fd, s, len);
650 if (errno != EAGAIN) {
651 warn("cannot read standard input");
654 /* else: EAGAIN - normal, repeat silently */
659 linecomplete = (s[i-1] == '\n');
666 if (ch < 32 || ch > 126)
670 for (j = 0; replace[j]; ++j) {
671 if (ch == replace[j]) {
685 static void sig_term_handler(int sig_no)
688 bb_error_msg(INFO"sig%s received", "term");
692 static void sig_child_handler(int sig_no)
697 bb_error_msg(INFO"sig%s received", "child");
698 while ((pid = wait_nohang(&wstat)) > 0)
699 for (l = 0; l < dirn; ++l)
700 if (dir[l].ppid == pid) {
702 processorstop(&dir[l]);
707 static void sig_alarm_handler(int sig_no)
710 bb_error_msg(INFO"sig%s received", "alarm");
714 static void sig_hangup_handler(int sig_no)
717 bb_error_msg(INFO"sig%s received", "hangup");
721 static void logmatch(struct logdir *ld)
732 if (pmatch(s+1, line, linelen))
737 if (pmatch(s+1, line, linelen))
745 int svlogd_main(int argc, char **argv);
746 int svlogd_main(int argc, char **argv)
750 ssize_t stdin_cnt = 0;
753 unsigned timestamp = 0;
754 void* (*memRchr)(const void *, int, size_t) = memchr;
756 #define line bb_common_bufsiz1
758 opt_complementary = "tt:vv";
759 opt = getopt32(argc, argv, "r:R:l:b:tv",
760 &r, &replace, &l, &b, ×tamp, &verbose);
763 if (!repl || r[1]) usage();
765 if (opt & 2) if (!repl) repl = '_'; // -R
767 linemax = xatou_range(l, 0, BUFSIZ-26);
768 if (linemax == 0) linemax = BUFSIZ-26;
769 if (linemax < 256) linemax = 256;
771 ////if (opt & 8) { // -b
772 //// buflen = xatoi_u(b);
773 //// if (buflen == 0) buflen = 1024;
775 //if (opt & 0x10) timestamp++; // -t
776 //if (opt & 0x20) verbose++; // -v
777 //if (timestamp > 2) timestamp = 2;
782 if (dirn <= 0) usage();
783 ////if (buflen <= linemax) usage();
784 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
786 dir = xmalloc(dirn * sizeof(struct logdir));
787 for (i = 0; i < dirn; ++i) {
790 ////dir[i].btmp = xmalloc(buflen);
793 /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
796 input.events = IOPAUSE_READ;
797 /* We cannot set NONBLOCK on fd #0 permanently - this setting
798 * _isn't_ per-process! It is shared among all other processes
799 * with the same stdin */
800 fl_flag_0 = fcntl(0, F_GETFL, 0);
802 blocked_sigset = &ss;
804 sigaddset(&ss, SIGTERM);
805 sigaddset(&ss, SIGCHLD);
806 sigaddset(&ss, SIGALRM);
807 sigaddset(&ss, SIGHUP);
808 sigprocmask(SIG_BLOCK, &ss, NULL);
809 sig_catch(SIGTERM, sig_term_handler);
810 sig_catch(SIGCHLD, sig_child_handler);
811 sig_catch(SIGALRM, sig_alarm_handler);
812 sig_catch(SIGHUP, sig_hangup_handler);
816 /* Without timestamps, we don't have to print each line
817 * separately, so we can look for _last_ newline, not first,
818 * thus batching writes */
822 setvbuf(stderr, NULL, _IOFBF, linelen);
824 /* Each iteration processes one or more lines */
827 char stamp[FMT_PTIME];
836 /* Prepare timestamp if needed */
840 fmt_taia25(stamp, &now);
842 default: /* case 2: */
843 fmt_ptime30nul(stamp, &now);
849 /* lineptr[0..linemax-1] - buffer for stdin */
850 /* (possibly has some unprocessed data from prev loop) */
852 /* Refill the buffer if needed */
853 np = memRchr(lineptr, '\n', stdin_cnt);
854 if (!np && !exitasap) {
855 i = linemax - stdin_cnt; /* avail. bytes at tail */
857 i = buffer_pread(0, lineptr + stdin_cnt, i, &now);
858 if (i <= 0) /* EOF or error on stdin */
861 np = memRchr(lineptr + stdin_cnt, '\n', i);
866 if (stdin_cnt <= 0 && exitasap)
869 /* Search for '\n' (in fact, np already holds the result) */
872 print_to_nl: /* NB: starting from here lineptr may point
873 * farther out into line[] */
874 linelen = np - lineptr + 1;
876 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
877 ch = lineptr[linelen-1];
879 /* Biggest performance hit was coming from the fact
880 * that we did not buffer writes. We were reading many lines
881 * in one read() above, but wrote one line per write().
882 * We are using stdio to fix that */
884 /* write out lineptr[0..linelen-1] to each log destination
885 * (or lineptr[-26..linelen-1] if timestamping) */
891 memcpy(printptr, stamp, 25);
894 for (i = 0; i < dirn; ++i) {
895 struct logdir *ld = &dir[i];
896 if (ld->fddir == -1) continue;
899 if (ld->matcherr == 'e')
900 ////full_write(2, printptr, printlen);
901 fwrite(lineptr, 1, linelen, stderr);
902 if (ld->match != '+') continue;
903 buffer_pwrite(i, printptr, printlen);
906 /* If we didn't see '\n' (long input line), */
907 /* read/write repeatedly until we see it */
909 /* lineptr is emptied now, safe to use as buffer */
911 stdin_cnt = exitasap ? -1 : buffer_pread(0, lineptr, linemax, &now);
912 if (stdin_cnt <= 0) { /* EOF or error on stdin */
914 lineptr[0] = ch = '\n';
919 np = memRchr(lineptr, '\n', stdin_cnt);
921 linelen = np - lineptr + 1;
922 ch = lineptr[linelen-1];
924 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
925 for (i = 0; i < dirn; ++i) {
926 if (dir[i].fddir == -1) continue;
927 if (dir[i].matcherr == 'e')
928 ////full_write(2, lineptr, linelen);
929 fwrite(lineptr, 1, linelen, stderr);
930 if (dir[i].match != '+') continue;
931 buffer_pwrite(i, lineptr, linelen);
935 stdin_cnt -= linelen;
938 /* If we see another '\n', we don't need to read
939 * next piece of input: can print what we have */
940 np = memRchr(lineptr, '\n', stdin_cnt);
943 /* Move unprocessed data to the front of line */
944 memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
949 for (i = 0; i < dirn; ++i) {
951 while (!processorstop(&dir[i]))
953 logdir_close(&dir[i]);