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 unsigned exitasap;
48 static unsigned rotateasap;
49 static unsigned reopenasap;
50 static unsigned linecomplete = 1;
51 static unsigned tmaxflag;
54 static const char *replace = "";
57 static struct logdir {
59 /* pattern list to match, in "aa\0bb\0\cc\0\0" form */
67 /* int (not long) because of taia_uint() usage: */
74 char fnsave[FMT_PTIME];
78 static unsigned dirn = 0;
80 #define FATAL "fatal: "
81 #define WARNING "warning: "
82 #define PAUSE "pausing: "
85 #define usage() bb_show_usage()
86 static void fatalx(char *m0)
88 bb_error_msg_and_die(FATAL"%s", m0);
90 static void warn(char *m0) {
91 bb_perror_msg(WARNING"%s", m0);
93 static void warn2(char *m0, char *m1)
95 bb_perror_msg(WARNING"%s: %s", m0, m1);
97 static void warnx(char *m0, char *m1)
99 bb_error_msg(WARNING"%s: %s", m0, m1);
101 static void pause_nomem(void)
103 bb_error_msg(PAUSE"out of memory"); sleep(3);
105 static void pause1cannot(char *m0)
107 bb_perror_msg(PAUSE"cannot %s", m0); sleep(3);
109 static void pause2cannot(char *m0, char *m1)
111 bb_perror_msg(PAUSE"cannot %s %s", m0, m1);
115 static char* wstrdup(const char *str)
118 while (!(s = strdup(str))) pause_nomem();
122 static unsigned processorstart(struct logdir *ld)
126 if (!ld->processor) return 0;
128 warnx("processor already running", ld->name);
131 while ((pid = fork()) == -1)
132 pause2cannot("fork for processor", ld->name);
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);
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';
151 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
152 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");
157 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "open state for", ld->name);
158 close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
159 fd = xopen("state", O_RDONLY|O_NDELAY);
161 if (fd_move(4, fd) == -1)
162 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
163 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
164 if (fd_move(5, fd) == -1)
165 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
169 prog[2] = ld->processor;
171 execve("/bin/sh", prog, environ);
172 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "run", ld->name);
178 static unsigned processorstop(struct logdir *ld)
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);
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';
196 ld->fnsave[26] = 'u';
198 while (fchdir(fdwdir) == -1)
199 pause1cannot("change to initial working directory");
200 return ld->processor ? 0 : 1;
202 ld->fnsave[26] = 't';
203 memcpy(f, ld->fnsave, 26);
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");
221 static void rmoldest(struct logdir *ld)
225 char oldest[FMT_PTIME];
228 oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
229 while (!(d = opendir(".")))
230 pause2cannot("open directory, want rotate", ld->name);
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);
239 if (strcmp(f->d_name, oldest) < 0)
240 memcpy(oldest, f->d_name, 27);
245 if (errno) warn2("cannot read directory", ld->name);
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);
255 static unsigned rotate(struct logdir *ld)
260 if (ld->fddir == -1) {
265 while (!processorstop(ld))
268 while (fchdir(ld->fddir) == -1)
269 pause2cannot("change directory, want rotate", ld->name);
271 /* create new filename */
272 ld->fnsave[25] = '.';
273 ld->fnsave[26] = 's';
275 ld->fnsave[26] = 'u';
276 ld->fnsave[27] = '\0';
279 fmt_taia(ld->fnsave, &now);
281 } while ((stat(ld->fnsave, &st) != -1) || (errno != ENOENT));
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;
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);
297 bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
298 ld->fnsave, ld->size);
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);
306 while (fchmod(ld->fdcur, 0644) == -1)
307 pause2cannot("set mode of current", ld->name);
312 while (fchdir(fdwdir) == -1)
313 pause1cannot("change to initial working directory");
317 static int buffer_pwrite(int n, char *s, unsigned len)
320 struct logdir *ld = &dir[n];
323 if (ld->size >= ld->sizemax)
325 if (len > (ld->sizemax - ld->size))
326 len = ld->sizemax - ld->size;
328 while ((i = write(ld->fdcur, s, len)) == -1) {
329 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
332 char oldest[FMT_PTIME];
335 while (fchdir(ld->fddir) == -1)
336 pause2cannot("change directory, want remove old logfile",
339 oldest[1] = oldest[27] = '\0';
340 while (!(d = opendir(".")))
341 pause2cannot("open directory, want remove old logfile",
344 while ((f = readdir(d)))
345 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
347 if (strcmp(f->d_name, oldest) < 0)
348 memcpy(oldest, f->d_name, 27);
350 if (errno) warn2("cannot read directory, want remove old logfile",
355 if (*oldest == '@') {
356 bb_error_msg(WARNING"out of disk space, delete: %s/%s",
359 if (unlink(oldest) == -1) {
360 warn2("cannot unlink oldest logfile", ld->name);
363 while (fchdir(fdwdir) == -1)
364 pause1cannot("change to initial working directory");
368 if (errno) pause2cannot("write to current", ld->name);
374 if (ld->size >= (ld->sizemax - linemax))
379 static void logdir_close(struct logdir *ld)
384 bb_error_msg(INFO"close: %s", ld->name);
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);
395 if (ld->fdlock == -1)
396 return; /* impossible */
400 ld->processor = NULL;
403 static unsigned logdir_open(struct logdir *ld, const char *fn)
411 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
412 if (ld->fddir == -1) {
413 warn2("cannot open log directory", (char*)fn);
417 if (fchdir(ld->fddir) == -1) {
419 warn2("cannot change directory", (char*)fn);
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)
427 warn2("cannot lock directory", (char*)fn);
428 while (fchdir(fdwdir) == -1)
429 pause1cannot("change to initial working directory");
435 ld->sizemax = 1000000;
436 ld->nmax = ld->nmin = 10;
438 ld->name = (char*)fn;
441 free(ld->inst); ld->inst = NULL;
442 free(ld->processor); ld->processor = NULL;
445 i = open_read_close("config", buf, sizeof(buf));
447 warn2("cannot read config", ld->name);
449 if (verbose) bb_error_msg(INFO"read: %s/config", ld->name);
452 np = strchr(s, '\n');
453 if (np) *np++ = '\0';
460 int l = asprintf(&new, "%s%s\n", ld->inst?:"", s);
461 if (l >= 0 && new) break;
468 static const struct suffix_mult km_suffixes[] = {
473 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
477 ld->nmax = xatoi_u(&s[1]);
480 ld->nmin = xatoi_u(&s[1]);
483 static const struct suffix_mult mh_suffixes[] = {
486 /*{ "d", 24*60*60 },*/
489 ld->tmax = xatou_sfx(&s[1], mh_suffixes);
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;
502 ld->processor = wstrdup(s);
508 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
511 np = strchr(s, '\n');
512 if (np) *np++ = '\0';
518 i = stat("current", &st);
520 if (st.st_size && ! (st.st_mode & S_IXUSR)) {
521 ld->fnsave[25] = '.';
522 ld->fnsave[26] = 'u';
523 ld->fnsave[27] = '\0';
526 fmt_taia(ld->fnsave, &now);
528 } while ((stat(ld->fnsave, &st) != -1) || (errno != ENOENT));
529 while (rename("current", ld->fnsave) == -1)
530 pause2cannot("rename current", ld->name);
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;
539 if (errno != ENOENT) {
541 warn2("cannot stat current", ld->name);
542 while (fchdir(fdwdir) == -1)
543 pause1cannot("change to initial working directory");
547 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
548 pause2cannot("open current", ld->name);
550 while (fchmod(ld->fdcur, 0644) == -1)
551 pause2cannot("set mode of current", ld->name);
554 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
555 else bb_error_msg(INFO"new: %s/current", ld->name);
558 while (fchdir(fdwdir) == -1)
559 pause1cannot("change to initial working directory");
563 static void logdirs_reopen(void)
571 for (l = 0; l < dirn; ++l) {
572 logdir_close(&dir[l]);
573 if (logdir_open(&dir[l], fndir[l])) ok = 1;
575 if (!ok) fatalx("no functional log directories");
578 /* Used for reading stdin */
579 static int buffer_pread(int fd, char *s, unsigned len)
585 for (i = 0; i < dirn; ++i)
599 taia_uint(&trotate, 2744);
600 taia_add(&trotate, &now, &trotate);
601 for (i = 0; i < dirn; ++i)
603 if (taia_less(&dir[i].trotate, &now))
605 if (taia_less(&dir[i].trotate, &trotate))
606 trotate = dir[i].trotate;
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);
617 sig_block(sig_child);
618 sig_block(sig_alarm);
619 sig_block(sig_hangup);
620 i = safe_read(fd, s, len);
622 if (errno != EAGAIN) {
623 warn("cannot read standard input");
626 /* else: EAGAIN - normal, repeat silently */
631 linecomplete = (s[i-1] == '\n');
638 if (ch < 32 || ch > 126)
642 for (j = 0; replace[j]; ++j) {
643 if (ch == replace[j]) {
657 static void sig_term_handler(int sig_no)
660 bb_error_msg(INFO"sig%s received", "term");
664 static void sig_child_handler(int sig_no)
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) {
674 processorstop(&dir[l]);
679 static void sig_alarm_handler(int sig_no)
682 bb_error_msg(INFO"sig%s received", "alarm");
686 static void sig_hangup_handler(int sig_no)
689 bb_error_msg(INFO"sig%s received", "hangup");
693 static void logmatch(struct logdir *ld)
704 if (pmatch(s+1, line, linelen))
709 if (pmatch(s+1, line, linelen))
717 int svlogd_main(int argc, char **argv)
721 ssize_t stdin_cnt = 0;
724 unsigned timestamp = 0;
726 opt_complementary = "tt:vv";
727 opt = getopt32(argc, argv, "r:R:l:b:tv",
728 &r, &replace, &l, &b, ×tamp, &verbose);
731 if (!repl || r[1]) usage();
733 if (opt & 2) if (!repl) repl = '_'; // -R
735 linemax = xatou_range(l, 0, 1000);
736 if (linemax == 0) linemax = 1000;
737 if (linemax < 256) linemax = 256;
741 if (buflen == 0) buflen = 1024;
743 //if (opt & 0x10) timestamp++; // -t
744 //if (opt & 0x20) verbose++; // -v
745 if (timestamp > 2) timestamp = 2;
750 if (dirn <= 0) usage();
751 if (buflen <= linemax) usage();
752 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
754 dir = xmalloc(dirn * sizeof(struct logdir));
755 for (i = 0; i < dirn; ++i) {
758 dir[i].btmp = xmalloc(buflen);
761 line = xmalloc(linemax + (timestamp ? 26 : 0));
764 in.events = IOPAUSE_READ;
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);
778 /* Each iteration processes one line */
781 char *lineptr = line;
785 /* Prepare timestamp if needed */
787 char stamp[FMT_PTIME];
791 fmt_taia(stamp, &now);
793 default: /* case 2: */
794 fmt_ptime(stamp, &now);
797 memcpy(line, stamp, 25);
802 /* lineptr[0..linemax-1] - buffer for stdin */
803 /* (possibly has some unprocessed data from prev loop) */
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 */
814 np = memchr(lineptr, '\n', stdin_cnt);
817 if (stdin_cnt <= 0 && exitasap)
820 /* Search for '\n' (in fact, np already holds the result) */
822 if (np) linelen = np - lineptr + 1;
823 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
824 ch = lineptr[linelen-1];
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;
833 if (ld->matcherr == 'e')
834 full_write(2, line, printlen);
835 if (ld->match != '+') continue;
836 buffer_pwrite(i, line, printlen);
839 /* If we didn't see '\n' (long input line), */
840 /* read/write repeatedly until we see it */
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';
851 np = memchr(lineptr, '\n', stdin_cnt);
852 if (np) linelen = np - lineptr + 1;
853 ch = lineptr[linelen-1];
855 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
856 for (i = 0; i < dirn; ++i) {
857 if (dir[i].fddir == -1) continue;
858 if (dir[i].matcherr == 'e')
859 full_write(2, lineptr, linelen);
860 if (dir[i].match != '+') continue;
861 buffer_pwrite(i, lineptr, linelen);
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);
871 for (i = 0; i < dirn; ++i) {
873 while (!processorstop(&dir[i]))
875 logdir_close(&dir[i]);