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 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: */
78 char fnsave[FMT_PTIME];
84 #define FATAL "fatal: "
85 #define WARNING "warning: "
86 #define PAUSE "pausing: "
89 #define usage() bb_show_usage()
90 static void fatalx(const char *m0)
92 bb_error_msg_and_die(FATAL"%s", m0);
94 static void warn(const char *m0) {
95 bb_perror_msg(WARNING"%s", m0);
97 static void warn2(const char *m0, const char *m1)
99 bb_perror_msg(WARNING"%s: %s", m0, m1);
101 static void warnx(const char *m0, const char *m1)
103 bb_error_msg(WARNING"%s: %s", m0, m1);
105 static void pause_nomem(void)
107 bb_error_msg(PAUSE"out of memory");
110 static void pause1cannot(const char *m0)
112 bb_perror_msg(PAUSE"cannot %s", m0);
115 static void pause2cannot(const char *m0, const char *m1)
117 bb_perror_msg(PAUSE"cannot %s %s", m0, m1);
121 static char* wstrdup(const char *str)
124 while (!(s = strdup(str)))
129 static unsigned processorstart(struct logdir *ld)
133 if (!ld->processor) return 0;
135 warnx("processor already running", ld->name);
138 while ((pid = fork()) == -1)
139 pause2cannot("fork for processor", ld->name);
145 sig_uncatch(SIGTERM);
146 sig_uncatch(SIGALRM);
148 sig_unblock(SIGTERM);
149 sig_unblock(SIGALRM);
153 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
154 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
155 if (fd_move(0, fd) == -1)
156 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
157 ld->fnsave[26] = 't';
158 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
159 if (fd_move(1, fd) == -1)
160 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
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);
168 if (fd_move(4, fd) == -1)
169 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
170 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
171 if (fd_move(5, fd) == -1)
172 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
175 prog[0] = (char*)"sh";
176 prog[1] = (char*)"-c";
177 prog[2] = ld->processor;
179 execve("/bin/sh", prog, environ);
180 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "run", ld->name);
186 static unsigned processorstop(struct logdir *ld)
192 while (wait_pid(&wstat, ld->ppid) == -1)
193 pause2cannot("wait for processor", ld->name);
197 if (ld->fddir == -1) return 1;
198 while (fchdir(ld->fddir) == -1)
199 pause2cannot("change directory, want processor", ld->name);
200 if (wait_exitcode(wstat) != 0) {
201 warnx("processor failed, restart", ld->name);
202 ld->fnsave[26] = 't';
204 ld->fnsave[26] = 'u';
206 while (fchdir(fdwdir) == -1)
207 pause1cannot("change to initial working directory");
208 return ld->processor ? 0 : 1;
210 ld->fnsave[26] = 't';
211 memcpy(f, ld->fnsave, 26);
214 while (rename(ld->fnsave, f) == -1)
215 pause2cannot("rename processed", ld->name);
216 while (chmod(f, 0744) == -1)
217 pause2cannot("set mode of processed", ld->name);
218 ld->fnsave[26] = 'u';
219 if (unlink(ld->fnsave) == -1)
220 bb_error_msg(WARNING"cannot unlink: %s/%s", ld->name, ld->fnsave);
221 while (rename("newstate", "state") == -1)
222 pause2cannot("rename state", ld->name);
224 bb_error_msg(INFO"processed: %s/%s", ld->name, f);
225 while (fchdir(fdwdir) == -1)
226 pause1cannot("change to initial working directory");
230 static void rmoldest(struct logdir *ld)
234 char oldest[FMT_PTIME];
237 oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
238 while (!(d = opendir(".")))
239 pause2cannot("open directory, want rotate", ld->name);
241 while ((f = readdir(d))) {
242 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
243 if (f->d_name[26] == 't') {
244 if (unlink(f->d_name) == -1)
245 warn2("cannot unlink processor leftover", f->d_name);
248 if (strcmp(f->d_name, oldest) < 0)
249 memcpy(oldest, f->d_name, 27);
255 warn2("cannot read directory", ld->name);
258 if (ld->nmax && (n > ld->nmax)) {
260 bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
261 if ((*oldest == '@') && (unlink(oldest) == -1))
262 warn2("cannot unlink oldest logfile", ld->name);
266 static unsigned rotate(struct logdir *ld)
271 if (ld->fddir == -1) {
276 while (!processorstop(ld))
279 while (fchdir(ld->fddir) == -1)
280 pause2cannot("change directory, want rotate", ld->name);
282 /* create new filename */
283 ld->fnsave[25] = '.';
284 ld->fnsave[26] = 's';
286 ld->fnsave[26] = 'u';
287 ld->fnsave[27] = '\0';
290 fmt_taia25(ld->fnsave, &now);
292 stat(ld->fnsave, &st);
293 } while (errno != ENOENT);
295 if (ld->tmax && taia_less(&ld->trotate, &now)) {
296 taia_uint(&ld->trotate, ld->tmax);
297 taia_add(&ld->trotate, &now, &ld->trotate);
298 if (taia_less(&ld->trotate, &trotate))
299 trotate = ld->trotate;
303 while (fsync(ld->fdcur) == -1)
304 pause2cannot("fsync current logfile", ld->name);
305 while (fchmod(ld->fdcur, 0744) == -1)
306 pause2cannot("set mode of current", ld->name);
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);
318 while (fchmod(ld->fdcur, 0644) == -1)
319 pause2cannot("set mode of current", ld->name);
324 while (fchdir(fdwdir) == -1)
325 pause1cannot("change to initial working directory");
329 static int buffer_pwrite(int n, char *s, unsigned len)
332 struct logdir *ld = &dir[n];
335 if (ld->size >= ld->sizemax)
337 if (len > (ld->sizemax - ld->size))
338 len = ld->sizemax - ld->size;
340 while ((i = full_write(ld->fdcur, s, len)) == -1) {
341 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
344 char oldest[FMT_PTIME];
347 while (fchdir(ld->fddir) == -1)
348 pause2cannot("change directory, want remove old logfile",
351 oldest[1] = oldest[27] = '\0';
352 while (!(d = opendir(".")))
353 pause2cannot("open directory, want remove old logfile",
356 while ((f = readdir(d)))
357 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
359 if (strcmp(f->d_name, oldest) < 0)
360 memcpy(oldest, f->d_name, 27);
362 if (errno) warn2("cannot read directory, want remove old logfile",
367 if (*oldest == '@') {
368 bb_error_msg(WARNING"out of disk space, delete: %s/%s",
371 if (unlink(oldest) == -1) {
372 warn2("cannot unlink oldest logfile", ld->name);
375 while (fchdir(fdwdir) == -1)
376 pause1cannot("change to initial working directory");
381 pause2cannot("write to current", ld->name);
387 if (ld->size >= (ld->sizemax - linemax))
392 static void logdir_close(struct logdir *ld)
397 bb_error_msg(INFO"close: %s", ld->name);
401 return; /* impossible */
402 while (fsync(ld->fdcur) == -1)
403 pause2cannot("fsync current logfile", ld->name);
404 while (fchmod(ld->fdcur, 0744) == -1)
405 pause2cannot("set mode of current", ld->name);
408 if (ld->fdlock == -1)
409 return; /* impossible */
413 ld->processor = NULL;
416 static unsigned logdir_open(struct logdir *ld, const char *fn)
424 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
425 if (ld->fddir == -1) {
426 warn2("cannot open log directory", (char*)fn);
430 if (fchdir(ld->fddir) == -1) {
432 warn2("cannot change directory", (char*)fn);
435 ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
436 if ((ld->fdlock == -1)
437 || (lock_exnb(ld->fdlock) == -1)
440 warn2("cannot lock directory", (char*)fn);
441 while (fchdir(fdwdir) == -1)
442 pause1cannot("change to initial working directory");
448 ld->sizemax = 1000000;
449 ld->nmax = ld->nmin = 10;
451 ld->name = (char*)fn;
454 free(ld->inst); ld->inst = NULL;
455 free(ld->processor); ld->processor = NULL;
458 i = open_read_close("config", buf, sizeof(buf));
460 warn2("cannot read config", ld->name);
462 if (verbose) bb_error_msg(INFO"read: %s/config", ld->name);
465 np = strchr(s, '\n');
466 if (np) *np++ = '\0';
473 int l = asprintf(&new, "%s%s\n", ld->inst?:"", s);
474 if (l >= 0 && new) break;
481 static const struct suffix_mult km_suffixes[] = {
486 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
490 ld->nmax = xatoi_u(&s[1]);
493 ld->nmin = xatoi_u(&s[1]);
496 static const struct suffix_mult mh_suffixes[] = {
499 /*{ "d", 24*60*60 },*/
502 ld->tmax = xatou_sfx(&s[1], mh_suffixes);
504 taia_uint(&ld->trotate, ld->tmax);
505 taia_add(&ld->trotate, &now, &ld->trotate);
506 if (!tmaxflag || taia_less(&ld->trotate, &trotate))
507 trotate = ld->trotate;
515 ld->processor = wstrdup(s);
521 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
524 np = strchr(s, '\n');
525 if (np) *np++ = '\0';
531 i = stat("current", &st);
533 if (st.st_size && ! (st.st_mode & S_IXUSR)) {
534 ld->fnsave[25] = '.';
535 ld->fnsave[26] = 'u';
536 ld->fnsave[27] = '\0';
539 fmt_taia25(ld->fnsave, &now);
541 stat(ld->fnsave, &st);
542 } while (errno != ENOENT);
543 while (rename("current", ld->fnsave) == -1)
544 pause2cannot("rename current", ld->name);
548 /* Be paranoid: st.st_size can be not just bigger, but WIDER! */
549 /* (bug in original svlogd. remove this comment when fixed there) */
550 ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
553 if (errno != ENOENT) {
555 warn2("cannot stat current", ld->name);
556 while (fchdir(fdwdir) == -1)
557 pause1cannot("change to initial working directory");
561 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
562 pause2cannot("open current", ld->name);
564 while (fchmod(ld->fdcur, 0644) == -1)
565 pause2cannot("set mode of current", ld->name);
568 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
569 else bb_error_msg(INFO"new: %s/current", ld->name);
572 while (fchdir(fdwdir) == -1)
573 pause1cannot("change to initial working directory");
577 static void logdirs_reopen(void)
585 for (l = 0; l < dirn; ++l) {
586 logdir_close(&dir[l]);
587 if (logdir_open(&dir[l], fndir[l])) ok = 1;
589 if (!ok) fatalx("no functional log directories");
592 /* Will look good in libbb one day */
593 static ssize_t ndelay_read(int fd, void *buf, size_t count)
595 if (!(fl_flag_0 & O_NONBLOCK))
596 fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
597 count = safe_read(fd, buf, count);
598 if (!(fl_flag_0 & O_NONBLOCK))
599 fcntl(fd, F_SETFL, fl_flag_0);
603 /* Used for reading stdin */
604 static int buffer_pread(int fd, char *s, unsigned len)
610 for (i = 0; i < dirn; ++i)
624 taia_uint(&trotate, 2744);
625 taia_add(&trotate, &now, &trotate);
626 for (i = 0; i < dirn; ++i)
628 if (taia_less(&dir[i].trotate, &now))
630 if (taia_less(&dir[i].trotate, &trotate))
631 trotate = dir[i].trotate;
635 sigprocmask(SIG_UNBLOCK, &blocked_sigset, NULL);
636 iopause(&input, 1, &trotate, &now);
637 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
638 i = ndelay_read(fd, s, len);
640 if (errno != EAGAIN) {
641 warn("cannot read standard input");
644 /* else: EAGAIN - normal, repeat silently */
649 linecomplete = (s[i-1] == '\n');
656 if (ch < 32 || ch > 126)
660 for (j = 0; replace[j]; ++j) {
661 if (ch == replace[j]) {
675 static void sig_term_handler(int sig_no)
678 bb_error_msg(INFO"sig%s received", "term");
682 static void sig_child_handler(int sig_no)
687 bb_error_msg(INFO"sig%s received", "child");
688 while ((pid = wait_nohang(&wstat)) > 0)
689 for (l = 0; l < dirn; ++l)
690 if (dir[l].ppid == pid) {
692 processorstop(&dir[l]);
697 static void sig_alarm_handler(int sig_no)
700 bb_error_msg(INFO"sig%s received", "alarm");
704 static void sig_hangup_handler(int sig_no)
707 bb_error_msg(INFO"sig%s received", "hangup");
711 static void logmatch(struct logdir *ld)
722 if (pmatch(s+1, line, linelen))
727 if (pmatch(s+1, line, linelen))
735 int svlogd_main(int argc, char **argv)
739 ssize_t stdin_cnt = 0;
742 unsigned timestamp = 0;
743 void* (*memRchr)(const void *, int, size_t) = memchr;
745 #define line bb_common_bufsiz1
747 opt_complementary = "tt:vv";
748 opt = getopt32(argc, argv, "r:R:l:b:tv",
749 &r, &replace, &l, &b, ×tamp, &verbose);
752 if (!repl || r[1]) usage();
754 if (opt & 2) if (!repl) repl = '_'; // -R
756 linemax = xatou_range(l, 0, BUFSIZ-26);
757 if (linemax == 0) linemax = BUFSIZ-26;
758 if (linemax < 256) linemax = 256;
760 //// if (opt & 8) { // -b
761 //// buflen = xatoi_u(b);
762 //// if (buflen == 0) buflen = 1024;
764 //if (opt & 0x10) timestamp++; // -t
765 //if (opt & 0x20) verbose++; // -v
766 //if (timestamp > 2) timestamp = 2;
771 if (dirn <= 0) usage();
772 ////if (buflen <= linemax) usage();
773 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
775 dir = xmalloc(dirn * sizeof(struct logdir));
776 for (i = 0; i < dirn; ++i) {
779 ////dir[i].btmp = xmalloc(buflen);
782 /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
785 input.events = IOPAUSE_READ;
786 /* We cannot set NONBLOCK on fd #0 permanently - this setting
787 * _isn't_ per-process! It is shared among all other processes
788 * with the same stdin */
789 fl_flag_0 = fcntl(0, F_GETFL, 0);
791 sigemptyset(&blocked_sigset);
792 sigaddset(&blocked_sigset, SIGTERM);
793 sigaddset(&blocked_sigset, SIGCHLD);
794 sigaddset(&blocked_sigset, SIGALRM);
795 sigaddset(&blocked_sigset, SIGHUP);
796 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
797 sig_catch(SIGTERM, sig_term_handler);
798 sig_catch(SIGCHLD, sig_child_handler);
799 sig_catch(SIGALRM, sig_alarm_handler);
800 sig_catch(SIGHUP, sig_hangup_handler);
804 /* Without timestamps, we don't have to print each line
805 * separately, so we can look for _last_ newline, not first,
806 * thus batching writes */
810 /* Each iteration processes one or more lines */
812 char stamp[FMT_PTIME];
820 /* Prepare timestamp if needed */
825 fmt_taia25(stamp, &now);
827 default: /* case 2: */
828 fmt_ptime30nul(stamp, &now);
834 /* lineptr[0..linemax-1] - buffer for stdin */
835 /* (possibly has some unprocessed data from prev loop) */
837 /* Refill the buffer if needed */
838 np = memRchr(lineptr, '\n', stdin_cnt);
839 if (!np && !exitasap) {
840 i = linemax - stdin_cnt; /* avail. bytes at tail */
842 i = buffer_pread(0, lineptr + stdin_cnt, i);
843 if (i <= 0) /* EOF or error on stdin */
846 np = memRchr(lineptr + stdin_cnt, '\n', i);
851 if (stdin_cnt <= 0 && exitasap)
854 /* Search for '\n' (in fact, np already holds the result) */
857 print_to_nl: /* NB: starting from here lineptr may point
858 * farther out into line[] */
859 linelen = np - lineptr + 1;
861 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
862 ch = lineptr[linelen-1];
864 /* TODO: biggest performance hit is coming from the fact
865 * that we do not buffer writes. We may read many lines
866 * in one read() above, but will do one write()
867 * per line below. Should we use stdio? */
869 /* write out lineptr[0..linelen-1] to each log destination
870 * (or lineptr[-26..linelen-1] if timestamping) */
876 memcpy(printptr, stamp, 25);
879 for (i = 0; i < dirn; ++i) {
880 struct logdir *ld = &dir[i];
881 if (ld->fddir == -1) continue;
884 if (ld->matcherr == 'e')
885 full_write(2, printptr, printlen);
886 if (ld->match != '+') continue;
887 buffer_pwrite(i, printptr, printlen);
890 /* If we didn't see '\n' (long input line), */
891 /* read/write repeatedly until we see it */
893 /* lineptr is emptied now, safe to use as buffer */
894 stdin_cnt = exitasap ? -1 : buffer_pread(0, lineptr, linemax);
895 if (stdin_cnt <= 0) { /* EOF or error on stdin */
897 lineptr[0] = ch = '\n';
902 np = memRchr(lineptr, '\n', stdin_cnt);
904 linelen = np - lineptr + 1;
905 ch = lineptr[linelen-1];
907 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
908 for (i = 0; i < dirn; ++i) {
909 if (dir[i].fddir == -1) continue;
910 if (dir[i].matcherr == 'e')
911 full_write(2, lineptr, linelen);
912 if (dir[i].match != '+') continue;
913 buffer_pwrite(i, lineptr, linelen);
917 stdin_cnt -= linelen;
920 /* If we see another '\n', we don't need to read
921 * next piece of input: can print what we have */
922 np = memRchr(lineptr, '\n', stdin_cnt);
925 /* Move unprocessed data to the front of line */
926 memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
930 for (i = 0; i < dirn; ++i) {
932 while (!processorstop(&dir[i]))
934 logdir_close(&dir[i]);