blob: 08e0b64469b33fa707828e9418161db025151bd6 [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>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000033#include "libbb.h"
Denis Vlasenko83ea6432006-11-16 02:27:24 +000034#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
Denis Vlasenkob9528352007-05-06 01:37:21 +0000645 do {
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 Vlasenkob9528352007-05-06 01:37:21 +0000648// TODO: do not unblock/block, but use sigpending after iopause
649// to see whether there was any sig? (one syscall less...)
Denis Vlasenko0aa84902007-02-03 01:47:56 +0000650 sigprocmask(SIG_BLOCK, blocked_sigset, NULL);
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +0000651 i = ndelay_read(fd, s, len);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000652 if (i >= 0) break;
653 if (errno != EAGAIN) {
654 warn("cannot read standard input");
655 break;
656 }
657 /* else: EAGAIN - normal, repeat silently */
Denis Vlasenkob9528352007-05-06 01:37:21 +0000658 } while (!exitasap);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000659
660 if (i > 0) {
661 int cnt;
662 linecomplete = (s[i-1] == '\n');
663 if (!repl) return i;
664
665 cnt = i;
666 while (--cnt >= 0) {
667 char ch = *s;
668 if (ch != '\n') {
669 if (ch < 32 || ch > 126)
670 *s = repl;
671 else {
672 int j;
673 for (j = 0; replace[j]; ++j) {
674 if (ch == replace[j]) {
675 *s = repl;
676 break;
677 }
678 }
679 }
680 }
681 s++;
682 }
683 }
684 return i;
685}
686
687
688static void sig_term_handler(int sig_no)
689{
690 if (verbose)
691 bb_error_msg(INFO"sig%s received", "term");
692 exitasap = 1;
693}
694
695static void sig_child_handler(int sig_no)
696{
697 int pid, l;
698
699 if (verbose)
700 bb_error_msg(INFO"sig%s received", "child");
701 while ((pid = wait_nohang(&wstat)) > 0)
702 for (l = 0; l < dirn; ++l)
703 if (dir[l].ppid == pid) {
704 dir[l].ppid = 0;
705 processorstop(&dir[l]);
706 break;
707 }
708}
709
710static void sig_alarm_handler(int sig_no)
711{
712 if (verbose)
713 bb_error_msg(INFO"sig%s received", "alarm");
714 rotateasap = 1;
715}
716
717static void sig_hangup_handler(int sig_no)
718{
719 if (verbose)
720 bb_error_msg(INFO"sig%s received", "hangup");
721 reopenasap = 1;
722}
723
724static void logmatch(struct logdir *ld)
725{
726 char *s;
727
728 ld->match = '+';
729 ld->matcherr = 'E';
730 s = ld->inst;
731 while (s && s[0]) {
732 switch (s[0]) {
733 case '+':
734 case '-':
735 if (pmatch(s+1, line, linelen))
736 ld->match = s[0];
737 break;
738 case 'e':
739 case 'E':
740 if (pmatch(s+1, line, linelen))
741 ld->matcherr = s[0];
742 break;
743 }
744 s += strlen(s) + 1;
745 }
746}
747
Denis Vlasenko06af2162007-02-03 17:28:39 +0000748int svlogd_main(int argc, char **argv);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000749int svlogd_main(int argc, char **argv)
750{
Denis Vlasenko0aa84902007-02-03 01:47:56 +0000751 sigset_t ss;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000752 char *r,*l,*b;
753 ssize_t stdin_cnt = 0;
754 int i;
755 unsigned opt;
756 unsigned timestamp = 0;
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000757 void* (*memRchr)(const void *, int, size_t) = memchr;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000758
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000759#define line bb_common_bufsiz1
760
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000761 opt_complementary = "tt:vv";
762 opt = getopt32(argc, argv, "r:R:l:b:tv",
763 &r, &replace, &l, &b, &timestamp, &verbose);
764 if (opt & 1) { // -r
765 repl = r[0];
766 if (!repl || r[1]) usage();
767 }
768 if (opt & 2) if (!repl) repl = '_'; // -R
769 if (opt & 4) { // -l
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000770 linemax = xatou_range(l, 0, BUFSIZ-26);
771 if (linemax == 0) linemax = BUFSIZ-26;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000772 if (linemax < 256) linemax = 256;
773 }
Denis Vlasenko64392902007-02-03 00:53:43 +0000774 ////if (opt & 8) { // -b
775 //// buflen = xatoi_u(b);
776 //// if (buflen == 0) buflen = 1024;
777 ////}
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000778 //if (opt & 0x10) timestamp++; // -t
779 //if (opt & 0x20) verbose++; // -v
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000780 //if (timestamp > 2) timestamp = 2;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000781 argv += optind;
782 argc -= optind;
783
784 dirn = argc;
785 if (dirn <= 0) usage();
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000786 ////if (buflen <= linemax) usage();
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000787 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
788 coe(fdwdir);
Denis Vlasenkob9528352007-05-06 01:37:21 +0000789 dir = xzalloc(dirn * sizeof(struct logdir));
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000790 for (i = 0; i < dirn; ++i) {
791 dir[i].fddir = -1;
792 dir[i].fdcur = -1;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000793 ////dir[i].btmp = xmalloc(buflen);
Denis Vlasenkob9528352007-05-06 01:37:21 +0000794 /*dir[i].ppid = 0;*/
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000795 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000796 /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000797 fndir = argv;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000798 input.fd = 0;
799 input.events = IOPAUSE_READ;
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +0000800 /* We cannot set NONBLOCK on fd #0 permanently - this setting
801 * _isn't_ per-process! It is shared among all other processes
802 * with the same stdin */
803 fl_flag_0 = fcntl(0, F_GETFL, 0);
804
Denis Vlasenko0aa84902007-02-03 01:47:56 +0000805 blocked_sigset = &ss;
806 sigemptyset(&ss);
807 sigaddset(&ss, SIGTERM);
808 sigaddset(&ss, SIGCHLD);
809 sigaddset(&ss, SIGALRM);
810 sigaddset(&ss, SIGHUP);
811 sigprocmask(SIG_BLOCK, &ss, NULL);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000812 sig_catch(SIGTERM, sig_term_handler);
813 sig_catch(SIGCHLD, sig_child_handler);
814 sig_catch(SIGALRM, sig_alarm_handler);
815 sig_catch(SIGHUP, sig_hangup_handler);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000816
817 logdirs_reopen();
818
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000819 /* Without timestamps, we don't have to print each line
820 * separately, so we can look for _last_ newline, not first,
821 * thus batching writes */
822 if (!timestamp)
823 memRchr = memrchr;
824
Denis Vlasenko64392902007-02-03 00:53:43 +0000825 setvbuf(stderr, NULL, _IOFBF, linelen);
826
Denis Vlasenko4e1715f2007-01-28 14:51:32 +0000827 /* Each iteration processes one or more lines */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000828 while (1) {
Denis Vlasenko64392902007-02-03 00:53:43 +0000829 struct taia now;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000830 char stamp[FMT_PTIME];
831 char *lineptr;
832 char *printptr;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000833 char *np;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000834 int printlen;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000835 char ch;
836
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000837 lineptr = line;
Denis Vlasenko64392902007-02-03 00:53:43 +0000838 taia_now(&now);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000839 /* Prepare timestamp if needed */
840 if (timestamp) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000841 switch (timestamp) {
842 case 1:
Denis Vlasenko8c783952007-01-27 22:21:52 +0000843 fmt_taia25(stamp, &now);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000844 break;
845 default: /* case 2: */
Denis Vlasenko8c783952007-01-27 22:21:52 +0000846 fmt_ptime30nul(stamp, &now);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000847 break;
848 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000849 lineptr += 26;
850 }
851
852 /* lineptr[0..linemax-1] - buffer for stdin */
853 /* (possibly has some unprocessed data from prev loop) */
854
855 /* Refill the buffer if needed */
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000856 np = memRchr(lineptr, '\n', stdin_cnt);
857 if (!np && !exitasap) {
858 i = linemax - stdin_cnt; /* avail. bytes at tail */
859 if (i >= 128) {
Denis Vlasenko64392902007-02-03 00:53:43 +0000860 i = buffer_pread(0, lineptr + stdin_cnt, i, &now);
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000861 if (i <= 0) /* EOF or error on stdin */
862 exitasap = 1;
863 else {
864 np = memRchr(lineptr + stdin_cnt, '\n', i);
865 stdin_cnt += i;
866 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000867 }
868 }
869 if (stdin_cnt <= 0 && exitasap)
870 break;
871
872 /* Search for '\n' (in fact, np already holds the result) */
873 linelen = stdin_cnt;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000874 if (np) {
875 print_to_nl: /* NB: starting from here lineptr may point
876 * farther out into line[] */
877 linelen = np - lineptr + 1;
878 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000879 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
880 ch = lineptr[linelen-1];
881
Denis Vlasenko64392902007-02-03 00:53:43 +0000882 /* Biggest performance hit was coming from the fact
883 * that we did not buffer writes. We were reading many lines
884 * in one read() above, but wrote one line per write().
885 * We are using stdio to fix that */
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +0000886
887 /* write out lineptr[0..linelen-1] to each log destination
888 * (or lineptr[-26..linelen-1] if timestamping) */
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000889 printlen = linelen;
890 printptr = lineptr;
891 if (timestamp) {
892 printlen += 26;
893 printptr -= 26;
894 memcpy(printptr, stamp, 25);
895 printptr[25] = ' ';
896 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000897 for (i = 0; i < dirn; ++i) {
898 struct logdir *ld = &dir[i];
899 if (ld->fddir == -1) continue;
900 if (ld->inst)
901 logmatch(ld);
Denis Vlasenko83edaf32006-11-19 17:33:54 +0000902 if (ld->matcherr == 'e')
Denis Vlasenko64392902007-02-03 00:53:43 +0000903 ////full_write(2, printptr, printlen);
904 fwrite(lineptr, 1, linelen, stderr);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000905 if (ld->match != '+') continue;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000906 buffer_pwrite(i, printptr, printlen);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000907 }
908
909 /* If we didn't see '\n' (long input line), */
910 /* read/write repeatedly until we see it */
911 while (ch != '\n') {
912 /* lineptr is emptied now, safe to use as buffer */
Denis Vlasenko64392902007-02-03 00:53:43 +0000913 taia_now(&now);
914 stdin_cnt = exitasap ? -1 : buffer_pread(0, lineptr, linemax, &now);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000915 if (stdin_cnt <= 0) { /* EOF or error on stdin */
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000916 exitasap = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000917 lineptr[0] = ch = '\n';
918 linelen = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000919 stdin_cnt = 1;
920 } else {
921 linelen = stdin_cnt;
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000922 np = memRchr(lineptr, '\n', stdin_cnt);
923 if (np)
924 linelen = np - lineptr + 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000925 ch = lineptr[linelen-1];
926 }
927 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
928 for (i = 0; i < dirn; ++i) {
929 if (dir[i].fddir == -1) continue;
Denis Vlasenko83edaf32006-11-19 17:33:54 +0000930 if (dir[i].matcherr == 'e')
Denis Vlasenko64392902007-02-03 00:53:43 +0000931 ////full_write(2, lineptr, linelen);
932 fwrite(lineptr, 1, linelen, stderr);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000933 if (dir[i].match != '+') continue;
934 buffer_pwrite(i, lineptr, linelen);
935 }
936 }
937
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000938 stdin_cnt -= linelen;
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000939 if (stdin_cnt > 0) {
940 lineptr += linelen;
941 /* If we see another '\n', we don't need to read
942 * next piece of input: can print what we have */
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000943 np = memRchr(lineptr, '\n', stdin_cnt);
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000944 if (np)
945 goto print_to_nl;
946 /* Move unprocessed data to the front of line */
947 memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
948 }
Denis Vlasenko64392902007-02-03 00:53:43 +0000949 fflush(NULL);////
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000950 }
951
952 for (i = 0; i < dirn; ++i) {
953 if (dir[i].ppid)
954 while (!processorstop(&dir[i]))
955 /* repeat */;
956 logdir_close(&dir[i]);
957 }
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +0000958 return 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000959}