blob: fb834a403d96af19ad195f55444c7da64f13a00c [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
Denis Vlasenko0aa84902007-02-03 01:47:56 +000057static sigset_t *blocked_sigset;
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +000058static 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);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000156 xmove_fd(fd, 0);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000157 ld->fnsave[26] = 't';
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000158 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000159 xmove_fd(fd, 1);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000160 fd = open_read("state");
161 if (fd == -1) {
162 if (errno != ENOENT)
163 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "open state for", ld->name);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000164 close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000165 fd = xopen("state", O_RDONLY|O_NDELAY);
166 }
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000167 xmove_fd(fd, 4);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000168 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000169 xmove_fd(fd, 5);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000170
Denis Vlasenko8c783952007-01-27 22:21:52 +0000171// getenv("SHELL")?
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000172 prog[0] = (char*)"sh";
173 prog[1] = (char*)"-c";
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000174 prog[2] = ld->processor;
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000175 prog[3] = NULL;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000176 execve("/bin/sh", prog, environ);
177 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "run", ld->name);
178 }
179 ld->ppid = pid;
180 return 1;
181}
182
183static unsigned processorstop(struct logdir *ld)
184{
185 char f[28];
186
187 if (ld->ppid) {
Denis Vlasenko8c783952007-01-27 22:21:52 +0000188 sig_unblock(SIGHUP);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000189 while (wait_pid(&wstat, ld->ppid) == -1)
190 pause2cannot("wait for processor", ld->name);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000191 sig_block(SIGHUP);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000192 ld->ppid = 0;
193 }
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';
200 unlink(ld->fnsave);
201 ld->fnsave[26] = 'u';
202 processorstart(ld);
203 while (fchdir(fdwdir) == -1)
204 pause1cannot("change to initial working directory");
205 return ld->processor ? 0 : 1;
206 }
207 ld->fnsave[26] = 't';
208 memcpy(f, ld->fnsave, 26);
209 f[26] = 's';
210 f[27] = '\0';
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);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000220 if (verbose)
221 bb_error_msg(INFO"processed: %s/%s", ld->name, f);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000222 while (fchdir(fdwdir) == -1)
223 pause1cannot("change to initial working directory");
224 return 1;
225}
226
227static void rmoldest(struct logdir *ld)
228{
229 DIR *d;
230 struct dirent *f;
231 char oldest[FMT_PTIME];
232 int n = 0;
233
234 oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
235 while (!(d = opendir(".")))
236 pause2cannot("open directory, want rotate", ld->name);
237 errno = 0;
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);
243 } else {
244 ++n;
245 if (strcmp(f->d_name, oldest) < 0)
246 memcpy(oldest, f->d_name, 27);
247 }
248 errno = 0;
249 }
250 }
Denis Vlasenko8c783952007-01-27 22:21:52 +0000251 if (errno)
252 warn2("cannot read directory", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000253 closedir(d);
254
255 if (ld->nmax && (n > ld->nmax)) {
Denis Vlasenko8c783952007-01-27 22:21:52 +0000256 if (verbose)
257 bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000258 if ((*oldest == '@') && (unlink(oldest) == -1))
259 warn2("cannot unlink oldest logfile", ld->name);
260 }
261}
262
263static unsigned rotate(struct logdir *ld)
264{
265 struct stat st;
266 struct taia now;
267
268 if (ld->fddir == -1) {
269 ld->tmax = 0;
270 return 0;
271 }
272 if (ld->ppid)
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000273 while (!processorstop(ld))
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000274 /* wait */;
275
276 while (fchdir(ld->fddir) == -1)
277 pause2cannot("change directory, want rotate", ld->name);
278
279 /* create new filename */
280 ld->fnsave[25] = '.';
281 ld->fnsave[26] = 's';
282 if (ld->processor)
283 ld->fnsave[26] = 'u';
284 ld->fnsave[27] = '\0';
285 do {
286 taia_now(&now);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000287 fmt_taia25(ld->fnsave, &now);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000288 errno = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000289 stat(ld->fnsave, &st);
290 } while (errno != ENOENT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000291
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;
297 }
298
299 if (ld->size > 0) {
Denis Vlasenko64392902007-02-03 00:53:43 +0000300 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000301 pause2cannot("fsync current logfile", ld->name);
302 while (fchmod(ld->fdcur, 0744) == -1)
303 pause2cannot("set mode of current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000304 ////close(ld->fdcur);
305 fclose(ld->filecur);
306
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000307 if (verbose) {
308 bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
309 ld->fnsave, ld->size);
310 }
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);
Denis Vlasenko64392902007-02-03 00:53:43 +0000315 /* we presume this cannot fail */
316 ld->filecur = fdopen(ld->fdcur, "a"); ////
317 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000318 coe(ld->fdcur);
319 ld->size = 0;
320 while (fchmod(ld->fdcur, 0644) == -1)
321 pause2cannot("set mode of current", ld->name);
322 rmoldest(ld);
323 processorstart(ld);
324 }
325
326 while (fchdir(fdwdir) == -1)
327 pause1cannot("change to initial working directory");
328 return 1;
329}
330
331static int buffer_pwrite(int n, char *s, unsigned len)
332{
333 int i;
334 struct logdir *ld = &dir[n];
335
336 if (ld->sizemax) {
337 if (ld->size >= ld->sizemax)
338 rotate(ld);
339 if (len > (ld->sizemax - ld->size))
340 len = ld->sizemax - ld->size;
341 }
Denis Vlasenko64392902007-02-03 00:53:43 +0000342 while (1) {
343 ////i = full_write(ld->fdcur, s, len);
344 ////if (i != -1) break;
345 i = fwrite(s, 1, len, ld->filecur);
346 if (i == len) break;
347
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000348 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
349 DIR *d;
350 struct dirent *f;
351 char oldest[FMT_PTIME];
352 int j = 0;
353
354 while (fchdir(ld->fddir) == -1)
355 pause2cannot("change directory, want remove old logfile",
356 ld->name);
357 oldest[0] = 'A';
358 oldest[1] = oldest[27] = '\0';
359 while (!(d = opendir(".")))
360 pause2cannot("open directory, want remove old logfile",
361 ld->name);
362 errno = 0;
363 while ((f = readdir(d)))
364 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
365 ++j;
366 if (strcmp(f->d_name, oldest) < 0)
367 memcpy(oldest, f->d_name, 27);
368 }
369 if (errno) warn2("cannot read directory, want remove old logfile",
370 ld->name);
371 closedir(d);
372 errno = ENOSPC;
373 if (j > ld->nmin) {
374 if (*oldest == '@') {
375 bb_error_msg(WARNING"out of disk space, delete: %s/%s",
376 ld->name, oldest);
377 errno = 0;
378 if (unlink(oldest) == -1) {
379 warn2("cannot unlink oldest logfile", ld->name);
380 errno = ENOSPC;
381 }
382 while (fchdir(fdwdir) == -1)
383 pause1cannot("change to initial working directory");
384 }
385 }
386 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000387 if (errno)
388 pause2cannot("write to current", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000389 }
390
391 ld->size += i;
392 if (ld->sizemax)
393 if (s[i-1] == '\n')
394 if (ld->size >= (ld->sizemax - linemax))
395 rotate(ld);
396 return i;
397}
398
399static void logdir_close(struct logdir *ld)
400{
401 if (ld->fddir == -1)
402 return;
403 if (verbose)
404 bb_error_msg(INFO"close: %s", ld->name);
405 close(ld->fddir);
406 ld->fddir = -1;
407 if (ld->fdcur == -1)
408 return; /* impossible */
Denis Vlasenko64392902007-02-03 00:53:43 +0000409 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000410 pause2cannot("fsync current logfile", ld->name);
411 while (fchmod(ld->fdcur, 0744) == -1)
412 pause2cannot("set mode of current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000413 ////close(ld->fdcur);
414 fclose(ld->filecur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000415 ld->fdcur = -1;
416 if (ld->fdlock == -1)
417 return; /* impossible */
418 close(ld->fdlock);
419 ld->fdlock = -1;
420 free(ld->processor);
421 ld->processor = NULL;
422}
423
424static unsigned logdir_open(struct logdir *ld, const char *fn)
425{
426 char buf[128];
427 struct taia now;
428 char *new, *s, *np;
429 int i;
430 struct stat st;
431
432 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
433 if (ld->fddir == -1) {
434 warn2("cannot open log directory", (char*)fn);
435 return 0;
436 }
437 coe(ld->fddir);
438 if (fchdir(ld->fddir) == -1) {
439 logdir_close(ld);
440 warn2("cannot change directory", (char*)fn);
441 return 0;
442 }
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)
446 ) {
447 logdir_close(ld);
448 warn2("cannot lock directory", (char*)fn);
449 while (fchdir(fdwdir) == -1)
450 pause1cannot("change to initial working directory");
451 return 0;
452 }
453 coe(ld->fdlock);
454
455 ld->size = 0;
456 ld->sizemax = 1000000;
457 ld->nmax = ld->nmin = 10;
458 ld->tmax = 0;
459 ld->name = (char*)fn;
460 ld->ppid = 0;
461 ld->match = '+';
462 free(ld->inst); ld->inst = NULL;
463 free(ld->processor); ld->processor = NULL;
464
465 /* read config */
466 i = open_read_close("config", buf, sizeof(buf));
467 if (i < 0)
468 warn2("cannot read config", ld->name);
469 if (i > 0) {
470 if (verbose) bb_error_msg(INFO"read: %s/config", ld->name);
471 s = buf;
472 while (s) {
473 np = strchr(s, '\n');
474 if (np) *np++ = '\0';
475 switch (s[0]) {
476 case '+':
477 case '-':
478 case 'e':
479 case 'E':
480 while (1) {
481 int l = asprintf(&new, "%s%s\n", ld->inst?:"", s);
482 if (l >= 0 && new) break;
483 pause_nomem();
484 }
485 free(ld->inst);
486 ld->inst = new;
487 break;
488 case 's': {
489 static const struct suffix_mult km_suffixes[] = {
490 { "k", 1024 },
491 { "m", 1024*1024 },
492 { NULL, 0 }
493 };
494 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
495 break;
496 }
497 case 'n':
498 ld->nmax = xatoi_u(&s[1]);
499 break;
500 case 'N':
501 ld->nmin = xatoi_u(&s[1]);
502 break;
503 case 't': {
504 static const struct suffix_mult mh_suffixes[] = {
505 { "m", 60 },
506 { "h", 60*60 },
507 /*{ "d", 24*60*60 },*/
508 { NULL, 0 }
509 };
510 ld->tmax = xatou_sfx(&s[1], mh_suffixes);
511 if (ld->tmax) {
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;
516 tmaxflag = 1;
517 }
518 break;
519 }
520 case '!':
521 if (s[1]) {
522 free(ld->processor);
523 ld->processor = wstrdup(s);
524 }
525 break;
526 }
527 s = np;
528 }
529 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
530 s = ld->inst;
531 while (s) {
532 np = strchr(s, '\n');
533 if (np) *np++ = '\0';
534 s = np;
535 }
536 }
537
538 /* open current */
539 i = stat("current", &st);
540 if (i != -1) {
541 if (st.st_size && ! (st.st_mode & S_IXUSR)) {
542 ld->fnsave[25] = '.';
543 ld->fnsave[26] = 'u';
544 ld->fnsave[27] = '\0';
545 do {
546 taia_now(&now);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000547 fmt_taia25(ld->fnsave, &now);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000548 errno = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000549 stat(ld->fnsave, &st);
550 } while (errno != ENOENT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000551 while (rename("current", ld->fnsave) == -1)
552 pause2cannot("rename current", ld->name);
553 rmoldest(ld);
554 i = -1;
555 } else {
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;
559 }
560 } else {
561 if (errno != ENOENT) {
562 logdir_close(ld);
563 warn2("cannot stat current", ld->name);
564 while (fchdir(fdwdir) == -1)
565 pause1cannot("change to initial working directory");
566 return 0;
567 }
568 }
569 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
570 pause2cannot("open current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000571 /* we presume this cannot fail */
572 ld->filecur = fdopen(ld->fdcur, "a"); ////
573 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
574
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000575 coe(ld->fdcur);
576 while (fchmod(ld->fdcur, 0644) == -1)
577 pause2cannot("set mode of current", ld->name);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000578
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000579 if (verbose) {
580 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
581 else bb_error_msg(INFO"new: %s/current", ld->name);
582 }
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000583
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000584 while (fchdir(fdwdir) == -1)
585 pause1cannot("change to initial working directory");
586 return 1;
587}
588
589static void logdirs_reopen(void)
590{
591 struct taia now;
592 int l;
593 int ok = 0;
594
595 tmaxflag = 0;
596 taia_now(&now);
597 for (l = 0; l < dirn; ++l) {
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000598 logdir_close(&dir[l]);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000599 if (logdir_open(&dir[l], fndir[l])) ok = 1;
600 }
601 if (!ok) fatalx("no functional log directories");
602}
603
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +0000604/* Will look good in libbb one day */
605static ssize_t ndelay_read(int fd, void *buf, size_t count)
606{
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);
612 return count;
613}
614
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000615/* Used for reading stdin */
Denis Vlasenko64392902007-02-03 00:53:43 +0000616static int buffer_pread(int fd, char *s, unsigned len, struct taia *now)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000617{
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000618 int i;
619
620 if (rotateasap) {
621 for (i = 0; i < dirn; ++i)
622 rotate(dir+i);
623 rotateasap = 0;
624 }
625 if (exitasap) {
626 if (linecomplete)
627 return 0;
628 len = 1;
629 }
630 if (reopenasap) {
631 logdirs_reopen();
632 reopenasap = 0;
633 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000634 taia_uint(&trotate, 2744);
Denis Vlasenko64392902007-02-03 00:53:43 +0000635 taia_add(&trotate, now, &trotate);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000636 for (i = 0; i < dirn; ++i)
637 if (dir[i].tmax) {
Denis Vlasenko64392902007-02-03 00:53:43 +0000638 if (taia_less(&dir[i].trotate, now))
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000639 rotate(dir+i);
640 if (taia_less(&dir[i].trotate, &trotate))
641 trotate = dir[i].trotate;
642 }
643
644 while (1) {
Denis Vlasenko0aa84902007-02-03 01:47:56 +0000645 sigprocmask(SIG_UNBLOCK, blocked_sigset, NULL);
Denis Vlasenko64392902007-02-03 00:53:43 +0000646 iopause(&input, 1, &trotate, now);
Denis Vlasenko0aa84902007-02-03 01:47:56 +0000647 sigprocmask(SIG_BLOCK, blocked_sigset, NULL);
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +0000648 i = ndelay_read(fd, s, len);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000649 if (i >= 0) break;
650 if (errno != EAGAIN) {
651 warn("cannot read standard input");
652 break;
653 }
654 /* else: EAGAIN - normal, repeat silently */
655 }
656
657 if (i > 0) {
658 int cnt;
659 linecomplete = (s[i-1] == '\n');
660 if (!repl) return i;
661
662 cnt = i;
663 while (--cnt >= 0) {
664 char ch = *s;
665 if (ch != '\n') {
666 if (ch < 32 || ch > 126)
667 *s = repl;
668 else {
669 int j;
670 for (j = 0; replace[j]; ++j) {
671 if (ch == replace[j]) {
672 *s = repl;
673 break;
674 }
675 }
676 }
677 }
678 s++;
679 }
680 }
681 return i;
682}
683
684
685static void sig_term_handler(int sig_no)
686{
687 if (verbose)
688 bb_error_msg(INFO"sig%s received", "term");
689 exitasap = 1;
690}
691
692static void sig_child_handler(int sig_no)
693{
694 int pid, l;
695
696 if (verbose)
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) {
701 dir[l].ppid = 0;
702 processorstop(&dir[l]);
703 break;
704 }
705}
706
707static void sig_alarm_handler(int sig_no)
708{
709 if (verbose)
710 bb_error_msg(INFO"sig%s received", "alarm");
711 rotateasap = 1;
712}
713
714static void sig_hangup_handler(int sig_no)
715{
716 if (verbose)
717 bb_error_msg(INFO"sig%s received", "hangup");
718 reopenasap = 1;
719}
720
721static void logmatch(struct logdir *ld)
722{
723 char *s;
724
725 ld->match = '+';
726 ld->matcherr = 'E';
727 s = ld->inst;
728 while (s && s[0]) {
729 switch (s[0]) {
730 case '+':
731 case '-':
732 if (pmatch(s+1, line, linelen))
733 ld->match = s[0];
734 break;
735 case 'e':
736 case 'E':
737 if (pmatch(s+1, line, linelen))
738 ld->matcherr = s[0];
739 break;
740 }
741 s += strlen(s) + 1;
742 }
743}
744
Denis Vlasenko06af2162007-02-03 17:28:39 +0000745int svlogd_main(int argc, char **argv);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000746int svlogd_main(int argc, char **argv)
747{
Denis Vlasenko0aa84902007-02-03 01:47:56 +0000748 sigset_t ss;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000749 char *r,*l,*b;
750 ssize_t stdin_cnt = 0;
751 int i;
752 unsigned opt;
753 unsigned timestamp = 0;
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000754 void* (*memRchr)(const void *, int, size_t) = memchr;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000755
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000756#define line bb_common_bufsiz1
757
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000758 opt_complementary = "tt:vv";
759 opt = getopt32(argc, argv, "r:R:l:b:tv",
760 &r, &replace, &l, &b, &timestamp, &verbose);
761 if (opt & 1) { // -r
762 repl = r[0];
763 if (!repl || r[1]) usage();
764 }
765 if (opt & 2) if (!repl) repl = '_'; // -R
766 if (opt & 4) { // -l
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000767 linemax = xatou_range(l, 0, BUFSIZ-26);
768 if (linemax == 0) linemax = BUFSIZ-26;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000769 if (linemax < 256) linemax = 256;
770 }
Denis Vlasenko64392902007-02-03 00:53:43 +0000771 ////if (opt & 8) { // -b
772 //// buflen = xatoi_u(b);
773 //// if (buflen == 0) buflen = 1024;
774 ////}
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000775 //if (opt & 0x10) timestamp++; // -t
776 //if (opt & 0x20) verbose++; // -v
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000777 //if (timestamp > 2) timestamp = 2;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000778 argv += optind;
779 argc -= optind;
780
781 dirn = argc;
782 if (dirn <= 0) usage();
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000783 ////if (buflen <= linemax) usage();
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000784 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
785 coe(fdwdir);
786 dir = xmalloc(dirn * sizeof(struct logdir));
787 for (i = 0; i < dirn; ++i) {
788 dir[i].fddir = -1;
789 dir[i].fdcur = -1;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000790 ////dir[i].btmp = xmalloc(buflen);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000791 dir[i].ppid = 0;
792 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000793 /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000794 fndir = argv;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000795 input.fd = 0;
796 input.events = IOPAUSE_READ;
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +0000797 /* 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);
801
Denis Vlasenko0aa84902007-02-03 01:47:56 +0000802 blocked_sigset = &ss;
803 sigemptyset(&ss);
804 sigaddset(&ss, SIGTERM);
805 sigaddset(&ss, SIGCHLD);
806 sigaddset(&ss, SIGALRM);
807 sigaddset(&ss, SIGHUP);
808 sigprocmask(SIG_BLOCK, &ss, NULL);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000809 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);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000813
814 logdirs_reopen();
815
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000816 /* 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 */
819 if (!timestamp)
820 memRchr = memrchr;
821
Denis Vlasenko64392902007-02-03 00:53:43 +0000822 setvbuf(stderr, NULL, _IOFBF, linelen);
823
Denis Vlasenko4e1715f2007-01-28 14:51:32 +0000824 /* Each iteration processes one or more lines */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000825 while (1) {
Denis Vlasenko64392902007-02-03 00:53:43 +0000826 struct taia now;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000827 char stamp[FMT_PTIME];
828 char *lineptr;
829 char *printptr;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000830 char *np;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000831 int printlen;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000832 char ch;
833
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000834 lineptr = line;
Denis Vlasenko64392902007-02-03 00:53:43 +0000835 taia_now(&now);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000836 /* Prepare timestamp if needed */
837 if (timestamp) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000838 switch (timestamp) {
839 case 1:
Denis Vlasenko8c783952007-01-27 22:21:52 +0000840 fmt_taia25(stamp, &now);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000841 break;
842 default: /* case 2: */
Denis Vlasenko8c783952007-01-27 22:21:52 +0000843 fmt_ptime30nul(stamp, &now);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000844 break;
845 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000846 lineptr += 26;
847 }
848
849 /* lineptr[0..linemax-1] - buffer for stdin */
850 /* (possibly has some unprocessed data from prev loop) */
851
852 /* Refill the buffer if needed */
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000853 np = memRchr(lineptr, '\n', stdin_cnt);
854 if (!np && !exitasap) {
855 i = linemax - stdin_cnt; /* avail. bytes at tail */
856 if (i >= 128) {
Denis Vlasenko64392902007-02-03 00:53:43 +0000857 i = buffer_pread(0, lineptr + stdin_cnt, i, &now);
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000858 if (i <= 0) /* EOF or error on stdin */
859 exitasap = 1;
860 else {
861 np = memRchr(lineptr + stdin_cnt, '\n', i);
862 stdin_cnt += i;
863 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000864 }
865 }
866 if (stdin_cnt <= 0 && exitasap)
867 break;
868
869 /* Search for '\n' (in fact, np already holds the result) */
870 linelen = stdin_cnt;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000871 if (np) {
872 print_to_nl: /* NB: starting from here lineptr may point
873 * farther out into line[] */
874 linelen = np - lineptr + 1;
875 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000876 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
877 ch = lineptr[linelen-1];
878
Denis Vlasenko64392902007-02-03 00:53:43 +0000879 /* 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 */
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +0000883
884 /* write out lineptr[0..linelen-1] to each log destination
885 * (or lineptr[-26..linelen-1] if timestamping) */
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000886 printlen = linelen;
887 printptr = lineptr;
888 if (timestamp) {
889 printlen += 26;
890 printptr -= 26;
891 memcpy(printptr, stamp, 25);
892 printptr[25] = ' ';
893 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000894 for (i = 0; i < dirn; ++i) {
895 struct logdir *ld = &dir[i];
896 if (ld->fddir == -1) continue;
897 if (ld->inst)
898 logmatch(ld);
Denis Vlasenko83edaf32006-11-19 17:33:54 +0000899 if (ld->matcherr == 'e')
Denis Vlasenko64392902007-02-03 00:53:43 +0000900 ////full_write(2, printptr, printlen);
901 fwrite(lineptr, 1, linelen, stderr);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000902 if (ld->match != '+') continue;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000903 buffer_pwrite(i, printptr, printlen);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000904 }
905
906 /* If we didn't see '\n' (long input line), */
907 /* read/write repeatedly until we see it */
908 while (ch != '\n') {
909 /* lineptr is emptied now, safe to use as buffer */
Denis Vlasenko64392902007-02-03 00:53:43 +0000910 taia_now(&now);
911 stdin_cnt = exitasap ? -1 : buffer_pread(0, lineptr, linemax, &now);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000912 if (stdin_cnt <= 0) { /* EOF or error on stdin */
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000913 exitasap = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000914 lineptr[0] = ch = '\n';
915 linelen = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000916 stdin_cnt = 1;
917 } else {
918 linelen = stdin_cnt;
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000919 np = memRchr(lineptr, '\n', stdin_cnt);
920 if (np)
921 linelen = np - lineptr + 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000922 ch = lineptr[linelen-1];
923 }
924 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
925 for (i = 0; i < dirn; ++i) {
926 if (dir[i].fddir == -1) continue;
Denis Vlasenko83edaf32006-11-19 17:33:54 +0000927 if (dir[i].matcherr == 'e')
Denis Vlasenko64392902007-02-03 00:53:43 +0000928 ////full_write(2, lineptr, linelen);
929 fwrite(lineptr, 1, linelen, stderr);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000930 if (dir[i].match != '+') continue;
931 buffer_pwrite(i, lineptr, linelen);
932 }
933 }
934
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000935 stdin_cnt -= linelen;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000936 if (stdin_cnt > 0) {
937 lineptr += linelen;
938 /* If we see another '\n', we don't need to read
939 * next piece of input: can print what we have */
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000940 np = memRchr(lineptr, '\n', stdin_cnt);
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000941 if (np)
942 goto print_to_nl;
943 /* Move unprocessed data to the front of line */
944 memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
945 }
Denis Vlasenko64392902007-02-03 00:53:43 +0000946 fflush(NULL);////
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000947 }
948
949 for (i = 0; i < dirn; ++i) {
950 if (dir[i].ppid)
951 while (!processorstop(&dir[i]))
952 /* repeat */;
953 logdir_close(&dir[i]);
954 }
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000955 return 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000956}