blob: f0e9aaf6f4300b932b7cd7582e188597477bd99c [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;
Denis Vlasenkoca549c52007-01-27 22:24:59 +000038////static int buflen = 1024;
Denis Vlasenko83ea6432006-11-16 02:27:24 +000039static int linelen;
40
41static char **fndir;
42static int fdwdir;
43static int wstat;
44static struct taia trotate;
45
46static char *line;
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +000047static smallint exitasap;
48static smallint rotateasap;
49static smallint reopenasap;
50static smallint linecomplete = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +000051
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +000052static smallint tmaxflag;
53
Denis Vlasenko83ea6432006-11-16 02:27:24 +000054static char repl;
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +000055static const char *replace = "";
56
57sigset_t blocked_sigset;
58static iopause_fd input;
59static int fl_flag_0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +000060
61static struct logdir {
Denis Vlasenko64392902007-02-03 00:53:43 +000062 ////char *btmp;
Denis Vlasenko83ea6432006-11-16 02:27:24 +000063 /* pattern list to match, in "aa\0bb\0\cc\0\0" form */
64 char *inst;
65 char *processor;
66 char *name;
67 unsigned size;
68 unsigned sizemax;
69 unsigned nmax;
70 unsigned nmin;
71 /* int (not long) because of taia_uint() usage: */
72 unsigned tmax;
73 int ppid;
74 int fddir;
75 int fdcur;
Denis Vlasenko64392902007-02-03 00:53:43 +000076 FILE* filecur; ////
Denis Vlasenko83ea6432006-11-16 02:27:24 +000077 int fdlock;
78 struct taia trotate;
79 char fnsave[FMT_PTIME];
80 char match;
81 char matcherr;
82} *dir;
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000083static unsigned dirn;
Denis Vlasenko83ea6432006-11-16 02:27:24 +000084
85#define FATAL "fatal: "
86#define WARNING "warning: "
87#define PAUSE "pausing: "
88#define INFO "info: "
89
90#define usage() bb_show_usage()
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000091static void fatalx(const char *m0)
Denis Vlasenko83ea6432006-11-16 02:27:24 +000092{
93 bb_error_msg_and_die(FATAL"%s", m0);
94}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000095static void warn(const char *m0) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +000096 bb_perror_msg(WARNING"%s", m0);
97}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000098static void warn2(const char *m0, const char *m1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +000099{
100 bb_perror_msg(WARNING"%s: %s", m0, m1);
101}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000102static void warnx(const char *m0, const char *m1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000103{
104 bb_error_msg(WARNING"%s: %s", m0, m1);
105}
106static void pause_nomem(void)
107{
Denis Vlasenko8c783952007-01-27 22:21:52 +0000108 bb_error_msg(PAUSE"out of memory");
109 sleep(3);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000110}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000111static void pause1cannot(const char *m0)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000112{
Denis Vlasenko8c783952007-01-27 22:21:52 +0000113 bb_perror_msg(PAUSE"cannot %s", m0);
114 sleep(3);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000115}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000116static void pause2cannot(const char *m0, const char *m1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000117{
118 bb_perror_msg(PAUSE"cannot %s %s", m0, m1);
119 sleep(3);
120}
121
122static char* wstrdup(const char *str)
123{
124 char *s;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000125 while (!(s = strdup(str)))
126 pause_nomem();
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000127 return s;
128}
129
130static unsigned processorstart(struct logdir *ld)
131{
132 int pid;
133
134 if (!ld->processor) return 0;
135 if (ld->ppid) {
136 warnx("processor already running", ld->name);
137 return 0;
138 }
139 while ((pid = fork()) == -1)
140 pause2cannot("fork for processor", ld->name);
141 if (!pid) {
142 char *prog[4];
143 int fd;
144
145 /* child */
Denis Vlasenko8c783952007-01-27 22:21:52 +0000146 sig_uncatch(SIGTERM);
147 sig_uncatch(SIGALRM);
148 sig_uncatch(SIGHUP);
149 sig_unblock(SIGTERM);
150 sig_unblock(SIGALRM);
151 sig_unblock(SIGHUP);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000152
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000153 if (verbose)
154 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
155 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
156 if (fd_move(0, fd) == -1)
157 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
158 ld->fnsave[26] = 't';
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000159 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000160 if (fd_move(1, fd) == -1)
161 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
162 fd = open_read("state");
163 if (fd == -1) {
164 if (errno != ENOENT)
165 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "open state for", ld->name);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000166 close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000167 fd = xopen("state", O_RDONLY|O_NDELAY);
168 }
169 if (fd_move(4, fd) == -1)
170 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000171 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000172 if (fd_move(5, fd) == -1)
173 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
174
Denis Vlasenko8c783952007-01-27 22:21:52 +0000175// getenv("SHELL")?
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000176 prog[0] = (char*)"sh";
177 prog[1] = (char*)"-c";
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000178 prog[2] = ld->processor;
179 prog[3] = '\0';
180 execve("/bin/sh", prog, environ);
181 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "run", ld->name);
182 }
183 ld->ppid = pid;
184 return 1;
185}
186
187static unsigned processorstop(struct logdir *ld)
188{
189 char f[28];
190
191 if (ld->ppid) {
Denis Vlasenko8c783952007-01-27 22:21:52 +0000192 sig_unblock(SIGHUP);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000193 while (wait_pid(&wstat, ld->ppid) == -1)
194 pause2cannot("wait for processor", ld->name);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000195 sig_block(SIGHUP);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000196 ld->ppid = 0;
197 }
198 if (ld->fddir == -1) return 1;
199 while (fchdir(ld->fddir) == -1)
200 pause2cannot("change directory, want processor", ld->name);
201 if (wait_exitcode(wstat) != 0) {
202 warnx("processor failed, restart", ld->name);
203 ld->fnsave[26] = 't';
204 unlink(ld->fnsave);
205 ld->fnsave[26] = 'u';
206 processorstart(ld);
207 while (fchdir(fdwdir) == -1)
208 pause1cannot("change to initial working directory");
209 return ld->processor ? 0 : 1;
210 }
211 ld->fnsave[26] = 't';
212 memcpy(f, ld->fnsave, 26);
213 f[26] = 's';
214 f[27] = '\0';
215 while (rename(ld->fnsave, f) == -1)
216 pause2cannot("rename processed", ld->name);
217 while (chmod(f, 0744) == -1)
218 pause2cannot("set mode of processed", ld->name);
219 ld->fnsave[26] = 'u';
220 if (unlink(ld->fnsave) == -1)
221 bb_error_msg(WARNING"cannot unlink: %s/%s", ld->name, ld->fnsave);
222 while (rename("newstate", "state") == -1)
223 pause2cannot("rename state", ld->name);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000224 if (verbose)
225 bb_error_msg(INFO"processed: %s/%s", ld->name, f);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000226 while (fchdir(fdwdir) == -1)
227 pause1cannot("change to initial working directory");
228 return 1;
229}
230
231static void rmoldest(struct logdir *ld)
232{
233 DIR *d;
234 struct dirent *f;
235 char oldest[FMT_PTIME];
236 int n = 0;
237
238 oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
239 while (!(d = opendir(".")))
240 pause2cannot("open directory, want rotate", ld->name);
241 errno = 0;
242 while ((f = readdir(d))) {
243 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
244 if (f->d_name[26] == 't') {
245 if (unlink(f->d_name) == -1)
246 warn2("cannot unlink processor leftover", f->d_name);
247 } else {
248 ++n;
249 if (strcmp(f->d_name, oldest) < 0)
250 memcpy(oldest, f->d_name, 27);
251 }
252 errno = 0;
253 }
254 }
Denis Vlasenko8c783952007-01-27 22:21:52 +0000255 if (errno)
256 warn2("cannot read directory", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000257 closedir(d);
258
259 if (ld->nmax && (n > ld->nmax)) {
Denis Vlasenko8c783952007-01-27 22:21:52 +0000260 if (verbose)
261 bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000262 if ((*oldest == '@') && (unlink(oldest) == -1))
263 warn2("cannot unlink oldest logfile", ld->name);
264 }
265}
266
267static unsigned rotate(struct logdir *ld)
268{
269 struct stat st;
270 struct taia now;
271
272 if (ld->fddir == -1) {
273 ld->tmax = 0;
274 return 0;
275 }
276 if (ld->ppid)
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000277 while (!processorstop(ld))
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000278 /* wait */;
279
280 while (fchdir(ld->fddir) == -1)
281 pause2cannot("change directory, want rotate", ld->name);
282
283 /* create new filename */
284 ld->fnsave[25] = '.';
285 ld->fnsave[26] = 's';
286 if (ld->processor)
287 ld->fnsave[26] = 'u';
288 ld->fnsave[27] = '\0';
289 do {
290 taia_now(&now);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000291 fmt_taia25(ld->fnsave, &now);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000292 errno = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000293 stat(ld->fnsave, &st);
294 } while (errno != ENOENT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000295
296 if (ld->tmax && taia_less(&ld->trotate, &now)) {
297 taia_uint(&ld->trotate, ld->tmax);
298 taia_add(&ld->trotate, &now, &ld->trotate);
299 if (taia_less(&ld->trotate, &trotate))
300 trotate = ld->trotate;
301 }
302
303 if (ld->size > 0) {
Denis Vlasenko64392902007-02-03 00:53:43 +0000304 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000305 pause2cannot("fsync current logfile", ld->name);
306 while (fchmod(ld->fdcur, 0744) == -1)
307 pause2cannot("set mode of current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000308 ////close(ld->fdcur);
309 fclose(ld->filecur);
310
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000311 if (verbose) {
312 bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
313 ld->fnsave, ld->size);
314 }
315 while (rename("current", ld->fnsave) == -1)
316 pause2cannot("rename current", ld->name);
317 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
318 pause2cannot("create new current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000319 /* we presume this cannot fail */
320 ld->filecur = fdopen(ld->fdcur, "a"); ////
321 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000322 coe(ld->fdcur);
323 ld->size = 0;
324 while (fchmod(ld->fdcur, 0644) == -1)
325 pause2cannot("set mode of current", ld->name);
326 rmoldest(ld);
327 processorstart(ld);
328 }
329
330 while (fchdir(fdwdir) == -1)
331 pause1cannot("change to initial working directory");
332 return 1;
333}
334
335static int buffer_pwrite(int n, char *s, unsigned len)
336{
337 int i;
338 struct logdir *ld = &dir[n];
339
340 if (ld->sizemax) {
341 if (ld->size >= ld->sizemax)
342 rotate(ld);
343 if (len > (ld->sizemax - ld->size))
344 len = ld->sizemax - ld->size;
345 }
Denis Vlasenko64392902007-02-03 00:53:43 +0000346 while (1) {
347 ////i = full_write(ld->fdcur, s, len);
348 ////if (i != -1) break;
349 i = fwrite(s, 1, len, ld->filecur);
350 if (i == len) break;
351
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000352 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
353 DIR *d;
354 struct dirent *f;
355 char oldest[FMT_PTIME];
356 int j = 0;
357
358 while (fchdir(ld->fddir) == -1)
359 pause2cannot("change directory, want remove old logfile",
360 ld->name);
361 oldest[0] = 'A';
362 oldest[1] = oldest[27] = '\0';
363 while (!(d = opendir(".")))
364 pause2cannot("open directory, want remove old logfile",
365 ld->name);
366 errno = 0;
367 while ((f = readdir(d)))
368 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
369 ++j;
370 if (strcmp(f->d_name, oldest) < 0)
371 memcpy(oldest, f->d_name, 27);
372 }
373 if (errno) warn2("cannot read directory, want remove old logfile",
374 ld->name);
375 closedir(d);
376 errno = ENOSPC;
377 if (j > ld->nmin) {
378 if (*oldest == '@') {
379 bb_error_msg(WARNING"out of disk space, delete: %s/%s",
380 ld->name, oldest);
381 errno = 0;
382 if (unlink(oldest) == -1) {
383 warn2("cannot unlink oldest logfile", ld->name);
384 errno = ENOSPC;
385 }
386 while (fchdir(fdwdir) == -1)
387 pause1cannot("change to initial working directory");
388 }
389 }
390 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000391 if (errno)
392 pause2cannot("write to current", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000393 }
394
395 ld->size += i;
396 if (ld->sizemax)
397 if (s[i-1] == '\n')
398 if (ld->size >= (ld->sizemax - linemax))
399 rotate(ld);
400 return i;
401}
402
403static void logdir_close(struct logdir *ld)
404{
405 if (ld->fddir == -1)
406 return;
407 if (verbose)
408 bb_error_msg(INFO"close: %s", ld->name);
409 close(ld->fddir);
410 ld->fddir = -1;
411 if (ld->fdcur == -1)
412 return; /* impossible */
Denis Vlasenko64392902007-02-03 00:53:43 +0000413 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000414 pause2cannot("fsync current logfile", ld->name);
415 while (fchmod(ld->fdcur, 0744) == -1)
416 pause2cannot("set mode of current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000417 ////close(ld->fdcur);
418 fclose(ld->filecur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000419 ld->fdcur = -1;
420 if (ld->fdlock == -1)
421 return; /* impossible */
422 close(ld->fdlock);
423 ld->fdlock = -1;
424 free(ld->processor);
425 ld->processor = NULL;
426}
427
428static unsigned logdir_open(struct logdir *ld, const char *fn)
429{
430 char buf[128];
431 struct taia now;
432 char *new, *s, *np;
433 int i;
434 struct stat st;
435
436 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
437 if (ld->fddir == -1) {
438 warn2("cannot open log directory", (char*)fn);
439 return 0;
440 }
441 coe(ld->fddir);
442 if (fchdir(ld->fddir) == -1) {
443 logdir_close(ld);
444 warn2("cannot change directory", (char*)fn);
445 return 0;
446 }
447 ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
448 if ((ld->fdlock == -1)
449 || (lock_exnb(ld->fdlock) == -1)
450 ) {
451 logdir_close(ld);
452 warn2("cannot lock directory", (char*)fn);
453 while (fchdir(fdwdir) == -1)
454 pause1cannot("change to initial working directory");
455 return 0;
456 }
457 coe(ld->fdlock);
458
459 ld->size = 0;
460 ld->sizemax = 1000000;
461 ld->nmax = ld->nmin = 10;
462 ld->tmax = 0;
463 ld->name = (char*)fn;
464 ld->ppid = 0;
465 ld->match = '+';
466 free(ld->inst); ld->inst = NULL;
467 free(ld->processor); ld->processor = NULL;
468
469 /* read config */
470 i = open_read_close("config", buf, sizeof(buf));
471 if (i < 0)
472 warn2("cannot read config", ld->name);
473 if (i > 0) {
474 if (verbose) bb_error_msg(INFO"read: %s/config", ld->name);
475 s = buf;
476 while (s) {
477 np = strchr(s, '\n');
478 if (np) *np++ = '\0';
479 switch (s[0]) {
480 case '+':
481 case '-':
482 case 'e':
483 case 'E':
484 while (1) {
485 int l = asprintf(&new, "%s%s\n", ld->inst?:"", s);
486 if (l >= 0 && new) break;
487 pause_nomem();
488 }
489 free(ld->inst);
490 ld->inst = new;
491 break;
492 case 's': {
493 static const struct suffix_mult km_suffixes[] = {
494 { "k", 1024 },
495 { "m", 1024*1024 },
496 { NULL, 0 }
497 };
498 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
499 break;
500 }
501 case 'n':
502 ld->nmax = xatoi_u(&s[1]);
503 break;
504 case 'N':
505 ld->nmin = xatoi_u(&s[1]);
506 break;
507 case 't': {
508 static const struct suffix_mult mh_suffixes[] = {
509 { "m", 60 },
510 { "h", 60*60 },
511 /*{ "d", 24*60*60 },*/
512 { NULL, 0 }
513 };
514 ld->tmax = xatou_sfx(&s[1], mh_suffixes);
515 if (ld->tmax) {
516 taia_uint(&ld->trotate, ld->tmax);
517 taia_add(&ld->trotate, &now, &ld->trotate);
518 if (!tmaxflag || taia_less(&ld->trotate, &trotate))
519 trotate = ld->trotate;
520 tmaxflag = 1;
521 }
522 break;
523 }
524 case '!':
525 if (s[1]) {
526 free(ld->processor);
527 ld->processor = wstrdup(s);
528 }
529 break;
530 }
531 s = np;
532 }
533 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
534 s = ld->inst;
535 while (s) {
536 np = strchr(s, '\n');
537 if (np) *np++ = '\0';
538 s = np;
539 }
540 }
541
542 /* open current */
543 i = stat("current", &st);
544 if (i != -1) {
545 if (st.st_size && ! (st.st_mode & S_IXUSR)) {
546 ld->fnsave[25] = '.';
547 ld->fnsave[26] = 'u';
548 ld->fnsave[27] = '\0';
549 do {
550 taia_now(&now);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000551 fmt_taia25(ld->fnsave, &now);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000552 errno = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000553 stat(ld->fnsave, &st);
554 } while (errno != ENOENT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000555 while (rename("current", ld->fnsave) == -1)
556 pause2cannot("rename current", ld->name);
557 rmoldest(ld);
558 i = -1;
559 } else {
560 /* Be paranoid: st.st_size can be not just bigger, but WIDER! */
561 /* (bug in original svlogd. remove this comment when fixed there) */
562 ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
563 }
564 } else {
565 if (errno != ENOENT) {
566 logdir_close(ld);
567 warn2("cannot stat current", ld->name);
568 while (fchdir(fdwdir) == -1)
569 pause1cannot("change to initial working directory");
570 return 0;
571 }
572 }
573 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
574 pause2cannot("open current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000575 /* we presume this cannot fail */
576 ld->filecur = fdopen(ld->fdcur, "a"); ////
577 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
578
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000579 coe(ld->fdcur);
580 while (fchmod(ld->fdcur, 0644) == -1)
581 pause2cannot("set mode of current", ld->name);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000582
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000583 if (verbose) {
584 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
585 else bb_error_msg(INFO"new: %s/current", ld->name);
586 }
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000587
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000588 while (fchdir(fdwdir) == -1)
589 pause1cannot("change to initial working directory");
590 return 1;
591}
592
593static void logdirs_reopen(void)
594{
595 struct taia now;
596 int l;
597 int ok = 0;
598
599 tmaxflag = 0;
600 taia_now(&now);
601 for (l = 0; l < dirn; ++l) {
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000602 logdir_close(&dir[l]);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000603 if (logdir_open(&dir[l], fndir[l])) ok = 1;
604 }
605 if (!ok) fatalx("no functional log directories");
606}
607
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +0000608/* Will look good in libbb one day */
609static ssize_t ndelay_read(int fd, void *buf, size_t count)
610{
611 if (!(fl_flag_0 & O_NONBLOCK))
612 fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
613 count = safe_read(fd, buf, count);
614 if (!(fl_flag_0 & O_NONBLOCK))
615 fcntl(fd, F_SETFL, fl_flag_0);
616 return count;
617}
618
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000619/* Used for reading stdin */
Denis Vlasenko64392902007-02-03 00:53:43 +0000620static int buffer_pread(int fd, char *s, unsigned len, struct taia *now)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000621{
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000622 int i;
623
624 if (rotateasap) {
625 for (i = 0; i < dirn; ++i)
626 rotate(dir+i);
627 rotateasap = 0;
628 }
629 if (exitasap) {
630 if (linecomplete)
631 return 0;
632 len = 1;
633 }
634 if (reopenasap) {
635 logdirs_reopen();
636 reopenasap = 0;
637 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000638 taia_uint(&trotate, 2744);
Denis Vlasenko64392902007-02-03 00:53:43 +0000639 taia_add(&trotate, now, &trotate);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000640 for (i = 0; i < dirn; ++i)
641 if (dir[i].tmax) {
Denis Vlasenko64392902007-02-03 00:53:43 +0000642 if (taia_less(&dir[i].trotate, now))
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000643 rotate(dir+i);
644 if (taia_less(&dir[i].trotate, &trotate))
645 trotate = dir[i].trotate;
646 }
647
648 while (1) {
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +0000649 sigprocmask(SIG_UNBLOCK, &blocked_sigset, NULL);
Denis Vlasenko64392902007-02-03 00:53:43 +0000650 iopause(&input, 1, &trotate, now);
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +0000651 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
652 i = ndelay_read(fd, s, len);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000653 if (i >= 0) break;
654 if (errno != EAGAIN) {
655 warn("cannot read standard input");
656 break;
657 }
658 /* else: EAGAIN - normal, repeat silently */
659 }
660
661 if (i > 0) {
662 int cnt;
663 linecomplete = (s[i-1] == '\n');
664 if (!repl) return i;
665
666 cnt = i;
667 while (--cnt >= 0) {
668 char ch = *s;
669 if (ch != '\n') {
670 if (ch < 32 || ch > 126)
671 *s = repl;
672 else {
673 int j;
674 for (j = 0; replace[j]; ++j) {
675 if (ch == replace[j]) {
676 *s = repl;
677 break;
678 }
679 }
680 }
681 }
682 s++;
683 }
684 }
685 return i;
686}
687
688
689static void sig_term_handler(int sig_no)
690{
691 if (verbose)
692 bb_error_msg(INFO"sig%s received", "term");
693 exitasap = 1;
694}
695
696static void sig_child_handler(int sig_no)
697{
698 int pid, l;
699
700 if (verbose)
701 bb_error_msg(INFO"sig%s received", "child");
702 while ((pid = wait_nohang(&wstat)) > 0)
703 for (l = 0; l < dirn; ++l)
704 if (dir[l].ppid == pid) {
705 dir[l].ppid = 0;
706 processorstop(&dir[l]);
707 break;
708 }
709}
710
711static void sig_alarm_handler(int sig_no)
712{
713 if (verbose)
714 bb_error_msg(INFO"sig%s received", "alarm");
715 rotateasap = 1;
716}
717
718static void sig_hangup_handler(int sig_no)
719{
720 if (verbose)
721 bb_error_msg(INFO"sig%s received", "hangup");
722 reopenasap = 1;
723}
724
725static void logmatch(struct logdir *ld)
726{
727 char *s;
728
729 ld->match = '+';
730 ld->matcherr = 'E';
731 s = ld->inst;
732 while (s && s[0]) {
733 switch (s[0]) {
734 case '+':
735 case '-':
736 if (pmatch(s+1, line, linelen))
737 ld->match = s[0];
738 break;
739 case 'e':
740 case 'E':
741 if (pmatch(s+1, line, linelen))
742 ld->matcherr = s[0];
743 break;
744 }
745 s += strlen(s) + 1;
746 }
747}
748
749int svlogd_main(int argc, char **argv)
750{
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000751 char *r,*l,*b;
752 ssize_t stdin_cnt = 0;
753 int i;
754 unsigned opt;
755 unsigned timestamp = 0;
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000756 void* (*memRchr)(const void *, int, size_t) = memchr;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000757
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000758#define line bb_common_bufsiz1
759
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000760 opt_complementary = "tt:vv";
761 opt = getopt32(argc, argv, "r:R:l:b:tv",
762 &r, &replace, &l, &b, &timestamp, &verbose);
763 if (opt & 1) { // -r
764 repl = r[0];
765 if (!repl || r[1]) usage();
766 }
767 if (opt & 2) if (!repl) repl = '_'; // -R
768 if (opt & 4) { // -l
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000769 linemax = xatou_range(l, 0, BUFSIZ-26);
770 if (linemax == 0) linemax = BUFSIZ-26;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000771 if (linemax < 256) linemax = 256;
772 }
Denis Vlasenko64392902007-02-03 00:53:43 +0000773 ////if (opt & 8) { // -b
774 //// buflen = xatoi_u(b);
775 //// if (buflen == 0) buflen = 1024;
776 ////}
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000777 //if (opt & 0x10) timestamp++; // -t
778 //if (opt & 0x20) verbose++; // -v
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000779 //if (timestamp > 2) timestamp = 2;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000780 argv += optind;
781 argc -= optind;
782
783 dirn = argc;
784 if (dirn <= 0) usage();
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000785 ////if (buflen <= linemax) usage();
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000786 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
787 coe(fdwdir);
788 dir = xmalloc(dirn * sizeof(struct logdir));
789 for (i = 0; i < dirn; ++i) {
790 dir[i].fddir = -1;
791 dir[i].fdcur = -1;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000792 ////dir[i].btmp = xmalloc(buflen);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000793 dir[i].ppid = 0;
794 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000795 /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000796 fndir = argv;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000797 input.fd = 0;
798 input.events = IOPAUSE_READ;
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +0000799 /* We cannot set NONBLOCK on fd #0 permanently - this setting
800 * _isn't_ per-process! It is shared among all other processes
801 * with the same stdin */
802 fl_flag_0 = fcntl(0, F_GETFL, 0);
803
804 sigemptyset(&blocked_sigset);
805 sigaddset(&blocked_sigset, SIGTERM);
806 sigaddset(&blocked_sigset, SIGCHLD);
807 sigaddset(&blocked_sigset, SIGALRM);
808 sigaddset(&blocked_sigset, SIGHUP);
809 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000810 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);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000814
815 logdirs_reopen();
816
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000817 /* 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 */
820 if (!timestamp)
821 memRchr = memrchr;
822
Denis Vlasenko64392902007-02-03 00:53:43 +0000823 setvbuf(stderr, NULL, _IOFBF, linelen);
824
Denis Vlasenko4e1715f2007-01-28 14:51:32 +0000825 /* Each iteration processes one or more lines */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000826 while (1) {
Denis Vlasenko64392902007-02-03 00:53:43 +0000827 struct taia now;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000828 char stamp[FMT_PTIME];
829 char *lineptr;
830 char *printptr;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000831 char *np;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000832 int printlen;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000833 char ch;
834
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000835 lineptr = line;
Denis Vlasenko64392902007-02-03 00:53:43 +0000836 taia_now(&now);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000837 /* Prepare timestamp if needed */
838 if (timestamp) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000839 switch (timestamp) {
840 case 1:
Denis Vlasenko8c783952007-01-27 22:21:52 +0000841 fmt_taia25(stamp, &now);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000842 break;
843 default: /* case 2: */
Denis Vlasenko8c783952007-01-27 22:21:52 +0000844 fmt_ptime30nul(stamp, &now);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000845 break;
846 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000847 lineptr += 26;
848 }
849
850 /* lineptr[0..linemax-1] - buffer for stdin */
851 /* (possibly has some unprocessed data from prev loop) */
852
853 /* Refill the buffer if needed */
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000854 np = memRchr(lineptr, '\n', stdin_cnt);
855 if (!np && !exitasap) {
856 i = linemax - stdin_cnt; /* avail. bytes at tail */
857 if (i >= 128) {
Denis Vlasenko64392902007-02-03 00:53:43 +0000858 i = buffer_pread(0, lineptr + stdin_cnt, i, &now);
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000859 if (i <= 0) /* EOF or error on stdin */
860 exitasap = 1;
861 else {
862 np = memRchr(lineptr + stdin_cnt, '\n', i);
863 stdin_cnt += i;
864 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000865 }
866 }
867 if (stdin_cnt <= 0 && exitasap)
868 break;
869
870 /* Search for '\n' (in fact, np already holds the result) */
871 linelen = stdin_cnt;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000872 if (np) {
873 print_to_nl: /* NB: starting from here lineptr may point
874 * farther out into line[] */
875 linelen = np - lineptr + 1;
876 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000877 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
878 ch = lineptr[linelen-1];
879
Denis Vlasenko64392902007-02-03 00:53:43 +0000880 /* 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 */
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +0000884
885 /* write out lineptr[0..linelen-1] to each log destination
886 * (or lineptr[-26..linelen-1] if timestamping) */
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000887 printlen = linelen;
888 printptr = lineptr;
889 if (timestamp) {
890 printlen += 26;
891 printptr -= 26;
892 memcpy(printptr, stamp, 25);
893 printptr[25] = ' ';
894 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000895 for (i = 0; i < dirn; ++i) {
896 struct logdir *ld = &dir[i];
897 if (ld->fddir == -1) continue;
898 if (ld->inst)
899 logmatch(ld);
Denis Vlasenko83edaf32006-11-19 17:33:54 +0000900 if (ld->matcherr == 'e')
Denis Vlasenko64392902007-02-03 00:53:43 +0000901 ////full_write(2, printptr, printlen);
902 fwrite(lineptr, 1, linelen, stderr);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000903 if (ld->match != '+') continue;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000904 buffer_pwrite(i, printptr, printlen);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000905 }
906
907 /* If we didn't see '\n' (long input line), */
908 /* read/write repeatedly until we see it */
909 while (ch != '\n') {
910 /* lineptr is emptied now, safe to use as buffer */
Denis Vlasenko64392902007-02-03 00:53:43 +0000911 taia_now(&now);
912 stdin_cnt = exitasap ? -1 : buffer_pread(0, lineptr, linemax, &now);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000913 if (stdin_cnt <= 0) { /* EOF or error on stdin */
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000914 exitasap = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000915 lineptr[0] = ch = '\n';
916 linelen = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000917 stdin_cnt = 1;
918 } else {
919 linelen = stdin_cnt;
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000920 np = memRchr(lineptr, '\n', stdin_cnt);
921 if (np)
922 linelen = np - lineptr + 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000923 ch = lineptr[linelen-1];
924 }
925 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
926 for (i = 0; i < dirn; ++i) {
927 if (dir[i].fddir == -1) continue;
Denis Vlasenko83edaf32006-11-19 17:33:54 +0000928 if (dir[i].matcherr == 'e')
Denis Vlasenko64392902007-02-03 00:53:43 +0000929 ////full_write(2, lineptr, linelen);
930 fwrite(lineptr, 1, linelen, stderr);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000931 if (dir[i].match != '+') continue;
932 buffer_pwrite(i, lineptr, linelen);
933 }
934 }
935
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000936 stdin_cnt -= linelen;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000937 if (stdin_cnt > 0) {
938 lineptr += linelen;
939 /* If we see another '\n', we don't need to read
940 * next piece of input: can print what we have */
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000941 np = memRchr(lineptr, '\n', stdin_cnt);
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000942 if (np)
943 goto print_to_nl;
944 /* Move unprocessed data to the front of line */
945 memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
946 }
Denis Vlasenko64392902007-02-03 00:53:43 +0000947 fflush(NULL);////
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000948 }
949
950 for (i = 0; i < dirn; ++i) {
951 if (dir[i].ppid)
952 while (!processorstop(&dir[i]))
953 /* repeat */;
954 logdir_close(&dir[i]);
955 }
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000956 return 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000957}