blob: c2cff96790d9693ad984e05dea4d5f311ca89468 [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 Vlasenkoac678ec2007-04-16 22:32:04 +000095static void warn(const char *m0)
96{
Denis Vlasenko83ea6432006-11-16 02:27:24 +000097 bb_perror_msg(WARNING"%s", m0);
98}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000099static void warn2(const char *m0, const char *m1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000100{
101 bb_perror_msg(WARNING"%s: %s", m0, m1);
102}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000103static void warnx(const char *m0, const char *m1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000104{
105 bb_error_msg(WARNING"%s: %s", m0, m1);
106}
107static void pause_nomem(void)
108{
Denis Vlasenko8c783952007-01-27 22:21:52 +0000109 bb_error_msg(PAUSE"out of memory");
110 sleep(3);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000111}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000112static void pause1cannot(const char *m0)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000113{
Denis Vlasenko8c783952007-01-27 22:21:52 +0000114 bb_perror_msg(PAUSE"cannot %s", m0);
115 sleep(3);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000116}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000117static void pause2cannot(const char *m0, const char *m1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000118{
119 bb_perror_msg(PAUSE"cannot %s %s", m0, m1);
120 sleep(3);
121}
122
123static char* wstrdup(const char *str)
124{
125 char *s;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000126 while (!(s = strdup(str)))
127 pause_nomem();
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000128 return s;
129}
130
131static unsigned processorstart(struct logdir *ld)
132{
133 int pid;
134
135 if (!ld->processor) return 0;
136 if (ld->ppid) {
137 warnx("processor already running", ld->name);
138 return 0;
139 }
140 while ((pid = fork()) == -1)
141 pause2cannot("fork for processor", ld->name);
142 if (!pid) {
143 char *prog[4];
144 int fd;
145
146 /* child */
Denis Vlasenko2856dab2007-04-01 01:18:20 +0000147 signal(SIGTERM, SIG_DFL);
148 signal(SIGALRM, SIG_DFL);
149 signal(SIGHUP, SIG_DFL);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000150 sig_unblock(SIGTERM);
151 sig_unblock(SIGALRM);
152 sig_unblock(SIGHUP);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000153
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000154 if (verbose)
155 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
156 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000157 xmove_fd(fd, 0);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000158 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 Vlasenkocad04ef2007-03-25 23:21:05 +0000160 xmove_fd(fd, 1);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000161 fd = open_read("state");
162 if (fd == -1) {
163 if (errno != ENOENT)
164 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "open state for", ld->name);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000165 close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000166 fd = xopen("state", O_RDONLY|O_NDELAY);
167 }
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000168 xmove_fd(fd, 4);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000169 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000170 xmove_fd(fd, 5);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000171
Denis Vlasenko8c783952007-01-27 22:21:52 +0000172// getenv("SHELL")?
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000173 prog[0] = (char*)"sh";
174 prog[1] = (char*)"-c";
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000175 prog[2] = ld->processor;
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000176 prog[3] = NULL;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000177 execve("/bin/sh", prog, environ);
178 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "run", ld->name);
179 }
180 ld->ppid = pid;
181 return 1;
182}
183
184static unsigned processorstop(struct logdir *ld)
185{
186 char f[28];
187
188 if (ld->ppid) {
Denis Vlasenko8c783952007-01-27 22:21:52 +0000189 sig_unblock(SIGHUP);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000190 while (wait_pid(&wstat, ld->ppid) == -1)
191 pause2cannot("wait for processor", ld->name);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000192 sig_block(SIGHUP);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000193 ld->ppid = 0;
194 }
195 if (ld->fddir == -1) return 1;
196 while (fchdir(ld->fddir) == -1)
197 pause2cannot("change directory, want processor", ld->name);
198 if (wait_exitcode(wstat) != 0) {
199 warnx("processor failed, restart", ld->name);
200 ld->fnsave[26] = 't';
201 unlink(ld->fnsave);
202 ld->fnsave[26] = 'u';
203 processorstart(ld);
204 while (fchdir(fdwdir) == -1)
205 pause1cannot("change to initial working directory");
206 return ld->processor ? 0 : 1;
207 }
208 ld->fnsave[26] = 't';
209 memcpy(f, ld->fnsave, 26);
210 f[26] = 's';
211 f[27] = '\0';
212 while (rename(ld->fnsave, f) == -1)
213 pause2cannot("rename processed", ld->name);
214 while (chmod(f, 0744) == -1)
215 pause2cannot("set mode of processed", ld->name);
216 ld->fnsave[26] = 'u';
217 if (unlink(ld->fnsave) == -1)
218 bb_error_msg(WARNING"cannot unlink: %s/%s", ld->name, ld->fnsave);
219 while (rename("newstate", "state") == -1)
220 pause2cannot("rename state", ld->name);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000221 if (verbose)
222 bb_error_msg(INFO"processed: %s/%s", ld->name, f);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000223 while (fchdir(fdwdir) == -1)
224 pause1cannot("change to initial working directory");
225 return 1;
226}
227
228static void rmoldest(struct logdir *ld)
229{
230 DIR *d;
231 struct dirent *f;
232 char oldest[FMT_PTIME];
233 int n = 0;
234
235 oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
236 while (!(d = opendir(".")))
237 pause2cannot("open directory, want rotate", ld->name);
238 errno = 0;
239 while ((f = readdir(d))) {
240 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
241 if (f->d_name[26] == 't') {
242 if (unlink(f->d_name) == -1)
243 warn2("cannot unlink processor leftover", f->d_name);
244 } else {
245 ++n;
246 if (strcmp(f->d_name, oldest) < 0)
247 memcpy(oldest, f->d_name, 27);
248 }
249 errno = 0;
250 }
251 }
Denis Vlasenko8c783952007-01-27 22:21:52 +0000252 if (errno)
253 warn2("cannot read directory", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000254 closedir(d);
255
256 if (ld->nmax && (n > ld->nmax)) {
Denis Vlasenko8c783952007-01-27 22:21:52 +0000257 if (verbose)
258 bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000259 if ((*oldest == '@') && (unlink(oldest) == -1))
260 warn2("cannot unlink oldest logfile", ld->name);
261 }
262}
263
264static unsigned rotate(struct logdir *ld)
265{
266 struct stat st;
267 struct taia now;
268
269 if (ld->fddir == -1) {
270 ld->tmax = 0;
271 return 0;
272 }
273 if (ld->ppid)
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000274 while (!processorstop(ld))
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000275 /* wait */;
276
277 while (fchdir(ld->fddir) == -1)
278 pause2cannot("change directory, want rotate", ld->name);
279
280 /* create new filename */
281 ld->fnsave[25] = '.';
282 ld->fnsave[26] = 's';
283 if (ld->processor)
284 ld->fnsave[26] = 'u';
285 ld->fnsave[27] = '\0';
286 do {
287 taia_now(&now);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000288 fmt_taia25(ld->fnsave, &now);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000289 errno = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000290 stat(ld->fnsave, &st);
291 } while (errno != ENOENT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000292
293 if (ld->tmax && taia_less(&ld->trotate, &now)) {
294 taia_uint(&ld->trotate, ld->tmax);
295 taia_add(&ld->trotate, &now, &ld->trotate);
296 if (taia_less(&ld->trotate, &trotate))
297 trotate = ld->trotate;
298 }
299
300 if (ld->size > 0) {
Denis Vlasenko64392902007-02-03 00:53:43 +0000301 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000302 pause2cannot("fsync current logfile", ld->name);
303 while (fchmod(ld->fdcur, 0744) == -1)
304 pause2cannot("set mode of current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000305 ////close(ld->fdcur);
306 fclose(ld->filecur);
307
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000308 if (verbose) {
309 bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
310 ld->fnsave, ld->size);
311 }
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);
Denis Vlasenko64392902007-02-03 00:53:43 +0000316 /* we presume this cannot fail */
317 ld->filecur = fdopen(ld->fdcur, "a"); ////
318 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000319 coe(ld->fdcur);
320 ld->size = 0;
321 while (fchmod(ld->fdcur, 0644) == -1)
322 pause2cannot("set mode of current", ld->name);
323 rmoldest(ld);
324 processorstart(ld);
325 }
326
327 while (fchdir(fdwdir) == -1)
328 pause1cannot("change to initial working directory");
329 return 1;
330}
331
332static int buffer_pwrite(int n, char *s, unsigned len)
333{
334 int i;
335 struct logdir *ld = &dir[n];
336
337 if (ld->sizemax) {
338 if (ld->size >= ld->sizemax)
339 rotate(ld);
340 if (len > (ld->sizemax - ld->size))
341 len = ld->sizemax - ld->size;
342 }
Denis Vlasenko64392902007-02-03 00:53:43 +0000343 while (1) {
344 ////i = full_write(ld->fdcur, s, len);
345 ////if (i != -1) break;
346 i = fwrite(s, 1, len, ld->filecur);
347 if (i == len) break;
348
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000349 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
350 DIR *d;
351 struct dirent *f;
352 char oldest[FMT_PTIME];
353 int j = 0;
354
355 while (fchdir(ld->fddir) == -1)
356 pause2cannot("change directory, want remove old logfile",
357 ld->name);
358 oldest[0] = 'A';
359 oldest[1] = oldest[27] = '\0';
360 while (!(d = opendir(".")))
361 pause2cannot("open directory, want remove old logfile",
362 ld->name);
363 errno = 0;
364 while ((f = readdir(d)))
365 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
366 ++j;
367 if (strcmp(f->d_name, oldest) < 0)
368 memcpy(oldest, f->d_name, 27);
369 }
370 if (errno) warn2("cannot read directory, want remove old logfile",
371 ld->name);
372 closedir(d);
373 errno = ENOSPC;
374 if (j > ld->nmin) {
375 if (*oldest == '@') {
376 bb_error_msg(WARNING"out of disk space, delete: %s/%s",
377 ld->name, oldest);
378 errno = 0;
379 if (unlink(oldest) == -1) {
380 warn2("cannot unlink oldest logfile", ld->name);
381 errno = ENOSPC;
382 }
383 while (fchdir(fdwdir) == -1)
384 pause1cannot("change to initial working directory");
385 }
386 }
387 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000388 if (errno)
389 pause2cannot("write to current", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000390 }
391
392 ld->size += i;
393 if (ld->sizemax)
394 if (s[i-1] == '\n')
395 if (ld->size >= (ld->sizemax - linemax))
396 rotate(ld);
397 return i;
398}
399
400static void logdir_close(struct logdir *ld)
401{
402 if (ld->fddir == -1)
403 return;
404 if (verbose)
405 bb_error_msg(INFO"close: %s", ld->name);
406 close(ld->fddir);
407 ld->fddir = -1;
408 if (ld->fdcur == -1)
409 return; /* impossible */
Denis Vlasenko64392902007-02-03 00:53:43 +0000410 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000411 pause2cannot("fsync current logfile", ld->name);
412 while (fchmod(ld->fdcur, 0744) == -1)
413 pause2cannot("set mode of current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000414 ////close(ld->fdcur);
415 fclose(ld->filecur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000416 ld->fdcur = -1;
417 if (ld->fdlock == -1)
418 return; /* impossible */
419 close(ld->fdlock);
420 ld->fdlock = -1;
421 free(ld->processor);
422 ld->processor = NULL;
423}
424
425static unsigned logdir_open(struct logdir *ld, const char *fn)
426{
427 char buf[128];
428 struct taia now;
429 char *new, *s, *np;
430 int i;
431 struct stat st;
432
433 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
434 if (ld->fddir == -1) {
435 warn2("cannot open log directory", (char*)fn);
436 return 0;
437 }
438 coe(ld->fddir);
439 if (fchdir(ld->fddir) == -1) {
440 logdir_close(ld);
441 warn2("cannot change directory", (char*)fn);
442 return 0;
443 }
444 ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
445 if ((ld->fdlock == -1)
446 || (lock_exnb(ld->fdlock) == -1)
447 ) {
448 logdir_close(ld);
449 warn2("cannot lock directory", (char*)fn);
450 while (fchdir(fdwdir) == -1)
451 pause1cannot("change to initial working directory");
452 return 0;
453 }
454 coe(ld->fdlock);
455
456 ld->size = 0;
457 ld->sizemax = 1000000;
458 ld->nmax = ld->nmin = 10;
459 ld->tmax = 0;
460 ld->name = (char*)fn;
461 ld->ppid = 0;
462 ld->match = '+';
463 free(ld->inst); ld->inst = NULL;
464 free(ld->processor); ld->processor = NULL;
465
466 /* read config */
467 i = open_read_close("config", buf, sizeof(buf));
468 if (i < 0)
469 warn2("cannot read config", ld->name);
470 if (i > 0) {
471 if (verbose) bb_error_msg(INFO"read: %s/config", ld->name);
472 s = buf;
473 while (s) {
474 np = strchr(s, '\n');
475 if (np) *np++ = '\0';
476 switch (s[0]) {
477 case '+':
478 case '-':
479 case 'e':
480 case 'E':
481 while (1) {
482 int l = asprintf(&new, "%s%s\n", ld->inst?:"", s);
483 if (l >= 0 && new) break;
484 pause_nomem();
485 }
486 free(ld->inst);
487 ld->inst = new;
488 break;
489 case 's': {
490 static const struct suffix_mult km_suffixes[] = {
491 { "k", 1024 },
492 { "m", 1024*1024 },
493 { NULL, 0 }
494 };
495 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
496 break;
497 }
498 case 'n':
499 ld->nmax = xatoi_u(&s[1]);
500 break;
501 case 'N':
502 ld->nmin = xatoi_u(&s[1]);
503 break;
504 case 't': {
505 static const struct suffix_mult mh_suffixes[] = {
506 { "m", 60 },
507 { "h", 60*60 },
508 /*{ "d", 24*60*60 },*/
509 { NULL, 0 }
510 };
511 ld->tmax = xatou_sfx(&s[1], mh_suffixes);
512 if (ld->tmax) {
513 taia_uint(&ld->trotate, ld->tmax);
514 taia_add(&ld->trotate, &now, &ld->trotate);
515 if (!tmaxflag || taia_less(&ld->trotate, &trotate))
516 trotate = ld->trotate;
517 tmaxflag = 1;
518 }
519 break;
520 }
521 case '!':
522 if (s[1]) {
523 free(ld->processor);
524 ld->processor = wstrdup(s);
525 }
526 break;
527 }
528 s = np;
529 }
530 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
531 s = ld->inst;
532 while (s) {
533 np = strchr(s, '\n');
534 if (np) *np++ = '\0';
535 s = np;
536 }
537 }
538
539 /* open current */
540 i = stat("current", &st);
541 if (i != -1) {
542 if (st.st_size && ! (st.st_mode & S_IXUSR)) {
543 ld->fnsave[25] = '.';
544 ld->fnsave[26] = 'u';
545 ld->fnsave[27] = '\0';
546 do {
547 taia_now(&now);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000548 fmt_taia25(ld->fnsave, &now);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000549 errno = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000550 stat(ld->fnsave, &st);
551 } while (errno != ENOENT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000552 while (rename("current", ld->fnsave) == -1)
553 pause2cannot("rename current", ld->name);
554 rmoldest(ld);
555 i = -1;
556 } else {
557 /* Be paranoid: st.st_size can be not just bigger, but WIDER! */
558 /* (bug in original svlogd. remove this comment when fixed there) */
559 ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
560 }
561 } else {
562 if (errno != ENOENT) {
563 logdir_close(ld);
564 warn2("cannot stat current", ld->name);
565 while (fchdir(fdwdir) == -1)
566 pause1cannot("change to initial working directory");
567 return 0;
568 }
569 }
570 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
571 pause2cannot("open current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000572 /* we presume this cannot fail */
573 ld->filecur = fdopen(ld->fdcur, "a"); ////
574 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
575
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000576 coe(ld->fdcur);
577 while (fchmod(ld->fdcur, 0644) == -1)
578 pause2cannot("set mode of current", ld->name);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000579
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000580 if (verbose) {
581 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
582 else bb_error_msg(INFO"new: %s/current", ld->name);
583 }
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000584
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000585 while (fchdir(fdwdir) == -1)
586 pause1cannot("change to initial working directory");
587 return 1;
588}
589
590static void logdirs_reopen(void)
591{
592 struct taia now;
593 int l;
594 int ok = 0;
595
596 tmaxflag = 0;
597 taia_now(&now);
598 for (l = 0; l < dirn; ++l) {
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000599 logdir_close(&dir[l]);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000600 if (logdir_open(&dir[l], fndir[l])) ok = 1;
601 }
602 if (!ok) fatalx("no functional log directories");
603}
604
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +0000605/* Will look good in libbb one day */
606static ssize_t ndelay_read(int fd, void *buf, size_t count)
607{
608 if (!(fl_flag_0 & O_NONBLOCK))
609 fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
610 count = safe_read(fd, buf, count);
611 if (!(fl_flag_0 & O_NONBLOCK))
612 fcntl(fd, F_SETFL, fl_flag_0);
613 return count;
614}
615
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000616/* Used for reading stdin */
Denis Vlasenko64392902007-02-03 00:53:43 +0000617static int buffer_pread(int fd, char *s, unsigned len, struct taia *now)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000618{
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000619 int i;
620
621 if (rotateasap) {
622 for (i = 0; i < dirn; ++i)
623 rotate(dir+i);
624 rotateasap = 0;
625 }
626 if (exitasap) {
627 if (linecomplete)
628 return 0;
629 len = 1;
630 }
631 if (reopenasap) {
632 logdirs_reopen();
633 reopenasap = 0;
634 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000635 taia_uint(&trotate, 2744);
Denis Vlasenko64392902007-02-03 00:53:43 +0000636 taia_add(&trotate, now, &trotate);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000637 for (i = 0; i < dirn; ++i)
638 if (dir[i].tmax) {
Denis Vlasenko64392902007-02-03 00:53:43 +0000639 if (taia_less(&dir[i].trotate, now))
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000640 rotate(dir+i);
641 if (taia_less(&dir[i].trotate, &trotate))
642 trotate = dir[i].trotate;
643 }
644
645 while (1) {
Denis Vlasenko0aa84902007-02-03 01:47:56 +0000646 sigprocmask(SIG_UNBLOCK, blocked_sigset, NULL);
Denis Vlasenko64392902007-02-03 00:53:43 +0000647 iopause(&input, 1, &trotate, now);
Denis Vlasenko0aa84902007-02-03 01:47:56 +0000648 sigprocmask(SIG_BLOCK, blocked_sigset, NULL);
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +0000649 i = ndelay_read(fd, s, len);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000650 if (i >= 0) break;
651 if (errno != EAGAIN) {
652 warn("cannot read standard input");
653 break;
654 }
655 /* else: EAGAIN - normal, repeat silently */
656 }
657
658 if (i > 0) {
659 int cnt;
660 linecomplete = (s[i-1] == '\n');
661 if (!repl) return i;
662
663 cnt = i;
664 while (--cnt >= 0) {
665 char ch = *s;
666 if (ch != '\n') {
667 if (ch < 32 || ch > 126)
668 *s = repl;
669 else {
670 int j;
671 for (j = 0; replace[j]; ++j) {
672 if (ch == replace[j]) {
673 *s = repl;
674 break;
675 }
676 }
677 }
678 }
679 s++;
680 }
681 }
682 return i;
683}
684
685
686static void sig_term_handler(int sig_no)
687{
688 if (verbose)
689 bb_error_msg(INFO"sig%s received", "term");
690 exitasap = 1;
691}
692
693static void sig_child_handler(int sig_no)
694{
695 int pid, l;
696
697 if (verbose)
698 bb_error_msg(INFO"sig%s received", "child");
699 while ((pid = wait_nohang(&wstat)) > 0)
700 for (l = 0; l < dirn; ++l)
701 if (dir[l].ppid == pid) {
702 dir[l].ppid = 0;
703 processorstop(&dir[l]);
704 break;
705 }
706}
707
708static void sig_alarm_handler(int sig_no)
709{
710 if (verbose)
711 bb_error_msg(INFO"sig%s received", "alarm");
712 rotateasap = 1;
713}
714
715static void sig_hangup_handler(int sig_no)
716{
717 if (verbose)
718 bb_error_msg(INFO"sig%s received", "hangup");
719 reopenasap = 1;
720}
721
722static void logmatch(struct logdir *ld)
723{
724 char *s;
725
726 ld->match = '+';
727 ld->matcherr = 'E';
728 s = ld->inst;
729 while (s && s[0]) {
730 switch (s[0]) {
731 case '+':
732 case '-':
733 if (pmatch(s+1, line, linelen))
734 ld->match = s[0];
735 break;
736 case 'e':
737 case 'E':
738 if (pmatch(s+1, line, linelen))
739 ld->matcherr = s[0];
740 break;
741 }
742 s += strlen(s) + 1;
743 }
744}
745
Denis Vlasenko06af2162007-02-03 17:28:39 +0000746int svlogd_main(int argc, char **argv);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000747int svlogd_main(int argc, char **argv)
748{
Denis Vlasenko0aa84902007-02-03 01:47:56 +0000749 sigset_t ss;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000750 char *r,*l,*b;
751 ssize_t stdin_cnt = 0;
752 int i;
753 unsigned opt;
754 unsigned timestamp = 0;
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000755 void* (*memRchr)(const void *, int, size_t) = memchr;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000756
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000757#define line bb_common_bufsiz1
758
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000759 opt_complementary = "tt:vv";
760 opt = getopt32(argc, argv, "r:R:l:b:tv",
761 &r, &replace, &l, &b, &timestamp, &verbose);
762 if (opt & 1) { // -r
763 repl = r[0];
764 if (!repl || r[1]) usage();
765 }
766 if (opt & 2) if (!repl) repl = '_'; // -R
767 if (opt & 4) { // -l
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000768 linemax = xatou_range(l, 0, BUFSIZ-26);
769 if (linemax == 0) linemax = BUFSIZ-26;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000770 if (linemax < 256) linemax = 256;
771 }
Denis Vlasenko64392902007-02-03 00:53:43 +0000772 ////if (opt & 8) { // -b
773 //// buflen = xatoi_u(b);
774 //// if (buflen == 0) buflen = 1024;
775 ////}
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000776 //if (opt & 0x10) timestamp++; // -t
777 //if (opt & 0x20) verbose++; // -v
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000778 //if (timestamp > 2) timestamp = 2;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000779 argv += optind;
780 argc -= optind;
781
782 dirn = argc;
783 if (dirn <= 0) usage();
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000784 ////if (buflen <= linemax) usage();
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000785 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
786 coe(fdwdir);
787 dir = xmalloc(dirn * sizeof(struct logdir));
788 for (i = 0; i < dirn; ++i) {
789 dir[i].fddir = -1;
790 dir[i].fdcur = -1;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000791 ////dir[i].btmp = xmalloc(buflen);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000792 dir[i].ppid = 0;
793 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000794 /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000795 fndir = argv;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000796 input.fd = 0;
797 input.events = IOPAUSE_READ;
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +0000798 /* We cannot set NONBLOCK on fd #0 permanently - this setting
799 * _isn't_ per-process! It is shared among all other processes
800 * with the same stdin */
801 fl_flag_0 = fcntl(0, F_GETFL, 0);
802
Denis Vlasenko0aa84902007-02-03 01:47:56 +0000803 blocked_sigset = &ss;
804 sigemptyset(&ss);
805 sigaddset(&ss, SIGTERM);
806 sigaddset(&ss, SIGCHLD);
807 sigaddset(&ss, SIGALRM);
808 sigaddset(&ss, SIGHUP);
809 sigprocmask(SIG_BLOCK, &ss, 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}