*: add optimization barrier to all "G trick" locations
[platform/upstream/busybox.git] / runit / svlogd.c
1 /*
2 Copyright (c) 2001-2006, Gerrit Pape
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, 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
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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 "libbb.h"
34 #include "runit_lib.h"
35
36 #define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
37
38 #define FMT_PTIME 30
39
40 struct logdir {
41         ////char *btmp;
42         /* pattern list to match, in "aa\0bb\0\cc\0\0" form */
43         char *inst;
44         char *processor;
45         char *name;
46         unsigned size;
47         unsigned sizemax;
48         unsigned nmax;
49         unsigned nmin;
50         unsigned rotate_period;
51         int ppid;
52         int fddir;
53         int fdcur;
54         FILE* filecur; ////
55         int fdlock;
56         unsigned next_rotate;
57         char fnsave[FMT_PTIME];
58         char match;
59         char matcherr;
60 };
61
62
63 struct globals {
64         struct logdir *dir;
65         unsigned verbose;
66         int linemax;
67         ////int buflen;
68         int linelen;
69
70         int fdwdir;
71         char **fndir;
72         int wstat;
73         unsigned nearest_rotate;
74
75         smallint exitasap;
76         smallint rotateasap;
77         smallint reopenasap;
78         smallint linecomplete;
79         smallint tmaxflag;
80
81         char repl;
82         const char *replace;
83         int fl_flag_0;
84         unsigned dirn;
85
86         sigset_t blocked_sigset;
87 };
88 #define G (*(struct globals*)ptr_to_globals)
89 #define dir            (G.dir           )
90 #define verbose        (G.verbose       )
91 #define linemax        (G.linemax       )
92 #define buflen         (G.buflen        )
93 #define linelen        (G.linelen       )
94 #define fndir          (G.fndir         )
95 #define fdwdir         (G.fdwdir        )
96 #define wstat          (G.wstat         )
97 #define nearest_rotate (G.nearest_rotate)
98 #define exitasap       (G.exitasap      )
99 #define rotateasap     (G.rotateasap    )
100 #define reopenasap     (G.reopenasap    )
101 #define linecomplete   (G.linecomplete  )
102 #define tmaxflag       (G.tmaxflag      )
103 #define repl           (G.repl          )
104 #define replace        (G.replace       )
105 #define blocked_sigset (G.blocked_sigset)
106 #define fl_flag_0      (G.fl_flag_0     )
107 #define dirn           (G.dirn          )
108 #define INIT_G() do { \
109         SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
110         linemax = 1000; \
111         /*buflen = 1024;*/ \
112         linecomplete = 1; \
113         replace = ""; \
114 } while (0)
115
116 #define line bb_common_bufsiz1
117
118
119 #define FATAL "fatal: "
120 #define WARNING "warning: "
121 #define PAUSE "pausing: "
122 #define INFO "info: "
123
124 #define usage() bb_show_usage()
125 static void fatalx(const char *m0)
126 {
127         bb_error_msg_and_die(FATAL"%s", m0);
128 }
129 static void warn(const char *m0)
130 {
131         bb_perror_msg(WARNING"%s", m0);
132 }
133 static void warn2(const char *m0, const char *m1)
134 {
135         bb_perror_msg(WARNING"%s: %s", m0, m1);
136 }
137 static void warnx(const char *m0, const char *m1)
138 {
139         bb_error_msg(WARNING"%s: %s", m0, m1);
140 }
141 static void pause_nomem(void)
142 {
143         bb_error_msg(PAUSE"out of memory");
144         sleep(3);
145 }
146 static void pause1cannot(const char *m0)
147 {
148         bb_perror_msg(PAUSE"cannot %s", m0);
149         sleep(3);
150 }
151 static void pause2cannot(const char *m0, const char *m1)
152 {
153         bb_perror_msg(PAUSE"cannot %s %s", m0, m1);
154         sleep(3);
155 }
156
157 static char* wstrdup(const char *str)
158 {
159         char *s;
160         while (!(s = strdup(str)))
161                 pause_nomem();
162         return s;
163 }
164
165 /*** ex fmt_ptime.[ch] ***/
166
167 /* NUL terminated */
168 static void fmt_time_human_30nul(char *s)
169 {
170         struct tm *t;
171         struct timeval tv;
172
173         gettimeofday(&tv, NULL);
174         t = gmtime(&(tv.tv_sec));
175         sprintf(s, "%04u-%02u-%02u_%02u:%02u:%02u.%06u000",
176                 (unsigned)(1900 + t->tm_year),
177                 (unsigned)(t->tm_mon + 1),
178                 (unsigned)(t->tm_mday),
179                 (unsigned)(t->tm_hour),
180                 (unsigned)(t->tm_min),
181                 (unsigned)(t->tm_sec),
182                 (unsigned)(tv.tv_usec)
183         );
184         /* 4+1 + 2+1 + 2+1 + 2+1 + 2+1 + 2+1 + 9 = */
185         /* 5   + 3   + 3   + 3   + 3   + 3   + 9 = */
186         /* 20 (up to '.' inclusive) + 9 (not including '\0') */
187 }
188
189 /* NOT terminated! */
190 static void fmt_time_bernstein_25(char *s)
191 {
192         uint32_t pack[3];
193         struct timeval tv;
194         unsigned sec_hi;
195
196         gettimeofday(&tv, NULL);
197         sec_hi = (0x400000000000000aULL + tv.tv_sec) >> 32;
198         tv.tv_sec = (time_t)(0x400000000000000aULL) + tv.tv_sec;
199         tv.tv_usec *= 1000;
200         /* Network order is big-endian: most significant byte first.
201          * This is exactly what we want here */
202         pack[0] = htonl(sec_hi);
203         pack[1] = htonl(tv.tv_sec);
204         pack[2] = htonl(tv.tv_usec);
205         *s++ = '@';
206         bin2hex(s, (char*)pack, 12);
207 }
208
209 static unsigned processorstart(struct logdir *ld)
210 {
211         int pid;
212
213         if (!ld->processor) return 0;
214         if (ld->ppid) {
215                 warnx("processor already running", ld->name);
216                 return 0;
217         }
218         while ((pid = fork()) == -1)
219                 pause2cannot("fork for processor", ld->name);
220         if (!pid) {
221                 char *prog[4];
222                 int fd;
223
224                 /* child */
225                 bb_signals(0
226                         + (1 << SIGTERM)
227                         + (1 << SIGALRM)
228                         + (1 << SIGHUP)
229                         , SIG_DFL);
230                 sig_unblock(SIGTERM);
231                 sig_unblock(SIGALRM);
232                 sig_unblock(SIGHUP);
233
234                 if (verbose)
235                         bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
236                 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
237                 xmove_fd(fd, 0);
238                 ld->fnsave[26] = 't';
239                 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
240                 xmove_fd(fd, 1);
241                 fd = open_read("state");
242                 if (fd == -1) {
243                         if (errno != ENOENT)
244                                 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "open state for", ld->name);
245                         close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
246                         fd = xopen("state", O_RDONLY|O_NDELAY);
247                 }
248                 xmove_fd(fd, 4);
249                 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
250                 xmove_fd(fd, 5);
251
252 // getenv("SHELL")?
253                 prog[0] = (char*)"sh";
254                 prog[1] = (char*)"-c";
255                 prog[2] = ld->processor;
256                 prog[3] = NULL;
257                 execv("/bin/sh", prog);
258                 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "run", ld->name);
259         }
260         ld->ppid = pid;
261         return 1;
262 }
263
264 static unsigned processorstop(struct logdir *ld)
265 {
266         char f[28];
267
268         if (ld->ppid) {
269                 sig_unblock(SIGHUP);
270                 while (safe_waitpid(ld->ppid, &wstat, 0) == -1)
271                         pause2cannot("wait for processor", ld->name);
272                 sig_block(SIGHUP);
273                 ld->ppid = 0;
274         }
275         if (ld->fddir == -1) return 1;
276         while (fchdir(ld->fddir) == -1)
277                 pause2cannot("change directory, want processor", ld->name);
278         if (wait_exitcode(wstat) != 0) {
279                 warnx("processor failed, restart", ld->name);
280                 ld->fnsave[26] = 't';
281                 unlink(ld->fnsave);
282                 ld->fnsave[26] = 'u';
283                 processorstart(ld);
284                 while (fchdir(fdwdir) == -1)
285                         pause1cannot("change to initial working directory");
286                 return ld->processor ? 0 : 1;
287         }
288         ld->fnsave[26] = 't';
289         memcpy(f, ld->fnsave, 26);
290         f[26] = 's';
291         f[27] = '\0';
292         while (rename(ld->fnsave, f) == -1)
293                 pause2cannot("rename processed", ld->name);
294         while (chmod(f, 0744) == -1)
295                 pause2cannot("set mode of processed", ld->name);
296         ld->fnsave[26] = 'u';
297         if (unlink(ld->fnsave) == -1)
298                 bb_error_msg(WARNING"cannot unlink: %s/%s", ld->name, ld->fnsave);
299         while (rename("newstate", "state") == -1)
300                 pause2cannot("rename state", ld->name);
301         if (verbose)
302                 bb_error_msg(INFO"processed: %s/%s", ld->name, f);
303         while (fchdir(fdwdir) == -1)
304                 pause1cannot("change to initial working directory");
305         return 1;
306 }
307
308 static void rmoldest(struct logdir *ld)
309 {
310         DIR *d;
311         struct dirent *f;
312         char oldest[FMT_PTIME];
313         int n = 0;
314
315         oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
316         while (!(d = opendir(".")))
317                 pause2cannot("open directory, want rotate", ld->name);
318         errno = 0;
319         while ((f = readdir(d))) {
320                 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
321                         if (f->d_name[26] == 't') {
322                                 if (unlink(f->d_name) == -1)
323                                         warn2("cannot unlink processor leftover", f->d_name);
324                         } else {
325                                 ++n;
326                                 if (strcmp(f->d_name, oldest) < 0)
327                                         memcpy(oldest, f->d_name, 27);
328                         }
329                         errno = 0;
330                 }
331         }
332         if (errno)
333                 warn2("cannot read directory", ld->name);
334         closedir(d);
335
336         if (ld->nmax && (n > ld->nmax)) {
337                 if (verbose)
338                         bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
339                 if ((*oldest == '@') && (unlink(oldest) == -1))
340                         warn2("cannot unlink oldest logfile", ld->name);
341         }
342 }
343
344 static unsigned rotate(struct logdir *ld)
345 {
346         struct stat st;
347         unsigned now;
348
349         if (ld->fddir == -1) {
350                 ld->rotate_period = 0;
351                 return 0;
352         }
353         if (ld->ppid)
354                 while (!processorstop(ld))
355                         continue;
356
357         while (fchdir(ld->fddir) == -1)
358                 pause2cannot("change directory, want rotate", ld->name);
359
360         /* create new filename */
361         ld->fnsave[25] = '.';
362         ld->fnsave[26] = 's';
363         if (ld->processor)
364                 ld->fnsave[26] = 'u';
365         ld->fnsave[27] = '\0';
366         do {
367                 fmt_time_bernstein_25(ld->fnsave);
368                 errno = 0;
369                 stat(ld->fnsave, &st);
370         } while (errno != ENOENT);
371
372         now = monotonic_sec();
373         if (ld->rotate_period && LESS(ld->next_rotate, now)) {
374                 ld->next_rotate = now + ld->rotate_period;
375                 if (LESS(ld->next_rotate, nearest_rotate))
376                         nearest_rotate = ld->next_rotate;
377         }
378
379         if (ld->size > 0) {
380                 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
381                         pause2cannot("fsync current logfile", ld->name);
382                 while (fchmod(ld->fdcur, 0744) == -1)
383                         pause2cannot("set mode of current", ld->name);
384                 ////close(ld->fdcur);
385                 fclose(ld->filecur);
386
387                 if (verbose) {
388                         bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
389                                         ld->fnsave, ld->size);
390                 }
391                 while (rename("current", ld->fnsave) == -1)
392                         pause2cannot("rename current", ld->name);
393                 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
394                         pause2cannot("create new current", ld->name);
395                 /* we presume this cannot fail */
396                 ld->filecur = fdopen(ld->fdcur, "a"); ////
397                 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
398                 close_on_exec_on(ld->fdcur);
399                 ld->size = 0;
400                 while (fchmod(ld->fdcur, 0644) == -1)
401                         pause2cannot("set mode of current", ld->name);
402                 rmoldest(ld);
403                 processorstart(ld);
404         }
405
406         while (fchdir(fdwdir) == -1)
407                 pause1cannot("change to initial working directory");
408         return 1;
409 }
410
411 static int buffer_pwrite(int n, char *s, unsigned len)
412 {
413         int i;
414         struct logdir *ld = &dir[n];
415
416         if (ld->sizemax) {
417                 if (ld->size >= ld->sizemax)
418                         rotate(ld);
419                 if (len > (ld->sizemax - ld->size))
420                         len = ld->sizemax - ld->size;
421         }
422         while (1) {
423                 ////i = full_write(ld->fdcur, s, len);
424                 ////if (i != -1) break;
425                 i = fwrite(s, 1, len, ld->filecur);
426                 if (i == len) break;
427
428                 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
429                         DIR *d;
430                         struct dirent *f;
431                         char oldest[FMT_PTIME];
432                         int j = 0;
433
434                         while (fchdir(ld->fddir) == -1)
435                                 pause2cannot("change directory, want remove old logfile",
436                                                          ld->name);
437                         oldest[0] = 'A';
438                         oldest[1] = oldest[27] = '\0';
439                         while (!(d = opendir(".")))
440                                 pause2cannot("open directory, want remove old logfile",
441                                                          ld->name);
442                         errno = 0;
443                         while ((f = readdir(d)))
444                                 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
445                                         ++j;
446                                         if (strcmp(f->d_name, oldest) < 0)
447                                                 memcpy(oldest, f->d_name, 27);
448                                 }
449                         if (errno) warn2("cannot read directory, want remove old logfile",
450                                         ld->name);
451                         closedir(d);
452                         errno = ENOSPC;
453                         if (j > ld->nmin) {
454                                 if (*oldest == '@') {
455                                         bb_error_msg(WARNING"out of disk space, delete: %s/%s",
456                                                         ld->name, oldest);
457                                         errno = 0;
458                                         if (unlink(oldest) == -1) {
459                                                 warn2("cannot unlink oldest logfile", ld->name);
460                                                 errno = ENOSPC;
461                                         }
462                                         while (fchdir(fdwdir) == -1)
463                                                 pause1cannot("change to initial working directory");
464                                 }
465                         }
466                 }
467                 if (errno)
468                         pause2cannot("write to current", ld->name);
469         }
470
471         ld->size += i;
472         if (ld->sizemax)
473                 if (s[i-1] == '\n')
474                         if (ld->size >= (ld->sizemax - linemax))
475                                 rotate(ld);
476         return i;
477 }
478
479 static void logdir_close(struct logdir *ld)
480 {
481         if (ld->fddir == -1)
482                 return;
483         if (verbose)
484                 bb_error_msg(INFO"close: %s", ld->name);
485         close(ld->fddir);
486         ld->fddir = -1;
487         if (ld->fdcur == -1)
488                 return; /* impossible */
489         while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
490                 pause2cannot("fsync current logfile", ld->name);
491         while (fchmod(ld->fdcur, 0744) == -1)
492                 pause2cannot("set mode of current", ld->name);
493         ////close(ld->fdcur);
494         fclose(ld->filecur);
495         ld->fdcur = -1;
496         if (ld->fdlock == -1)
497                 return; /* impossible */
498         close(ld->fdlock);
499         ld->fdlock = -1;
500         free(ld->processor);
501         ld->processor = NULL;
502 }
503
504 static unsigned logdir_open(struct logdir *ld, const char *fn)
505 {
506         char buf[128];
507         unsigned now;
508         char *new, *s, *np;
509         int i;
510         struct stat st;
511
512         now = monotonic_sec();
513
514         ld->fddir = open(fn, O_RDONLY|O_NDELAY);
515         if (ld->fddir == -1) {
516                 warn2("cannot open log directory", (char*)fn);
517                 return 0;
518         }
519         close_on_exec_on(ld->fddir);
520         if (fchdir(ld->fddir) == -1) {
521                 logdir_close(ld);
522                 warn2("cannot change directory", (char*)fn);
523                 return 0;
524         }
525         ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
526         if ((ld->fdlock == -1)
527          || (lock_exnb(ld->fdlock) == -1)
528         ) {
529                 logdir_close(ld);
530                 warn2("cannot lock directory", (char*)fn);
531                 while (fchdir(fdwdir) == -1)
532                         pause1cannot("change to initial working directory");
533                 return 0;
534         }
535         close_on_exec_on(ld->fdlock);
536
537         ld->size = 0;
538         ld->sizemax = 1000000;
539         ld->nmax = ld->nmin = 10;
540         ld->rotate_period = 0;
541         ld->name = (char*)fn;
542         ld->ppid = 0;
543         ld->match = '+';
544         free(ld->inst); ld->inst = NULL;
545         free(ld->processor); ld->processor = NULL;
546
547         /* read config */
548         i = open_read_close("config", buf, sizeof(buf));
549         if (i < 0 && errno != ENOENT)
550                 bb_perror_msg(WARNING"%s/config", ld->name);
551         if (i > 0) {
552                 if (verbose)
553                         bb_error_msg(INFO"read: %s/config", ld->name);
554                 s = buf;
555                 while (s) {
556                         np = strchr(s, '\n');
557                         if (np)
558                                 *np++ = '\0';
559                         switch (s[0]) {
560                         case '+':
561                         case '-':
562                         case 'e':
563                         case 'E':
564                                 /* Add '\n'-terminated line to ld->inst */
565                                 while (1) {
566                                         int l = asprintf(&new, "%s%s\n", ld->inst ? : "", s);
567                                         if (l >= 0 && new)
568                                                 break;
569                                         pause_nomem();
570                                 }
571                                 free(ld->inst);
572                                 ld->inst = new;
573                                 break;
574                         case 's': {
575                                 static const struct suffix_mult km_suffixes[] = {
576                                         { "k", 1024 },
577                                         { "m", 1024*1024 },
578                                         { }
579                                 };
580                                 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
581                                 break;
582                         }
583                         case 'n':
584                                 ld->nmax = xatoi_u(&s[1]);
585                                 break;
586                         case 'N':
587                                 ld->nmin = xatoi_u(&s[1]);
588                                 break;
589                         case 't': {
590                                 static const struct suffix_mult mh_suffixes[] = {
591                                         { "m", 60 },
592                                         { "h", 60*60 },
593                                         /*{ "d", 24*60*60 },*/
594                                         { }
595                                 };
596                                 ld->rotate_period = xatou_sfx(&s[1], mh_suffixes);
597                                 if (ld->rotate_period) {
598                                         ld->next_rotate = now + ld->rotate_period;
599                                         if (!tmaxflag || LESS(ld->next_rotate, nearest_rotate))
600                                                 nearest_rotate = ld->next_rotate;
601                                         tmaxflag = 1;
602                                 }
603                                 break;
604                         }
605                         case '!':
606                                 if (s[1]) {
607                                         free(ld->processor);
608                                         ld->processor = wstrdup(s);
609                                 }
610                                 break;
611                         }
612                         s = np;
613                 }
614                 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
615                 s = ld->inst;
616                 while (s) {
617                         np = strchr(s, '\n');
618                         if (np)
619                                 *np++ = '\0';
620                         s = np;
621                 }
622         }
623
624         /* open current */
625         i = stat("current", &st);
626         if (i != -1) {
627                 if (st.st_size && !(st.st_mode & S_IXUSR)) {
628                         ld->fnsave[25] = '.';
629                         ld->fnsave[26] = 'u';
630                         ld->fnsave[27] = '\0';
631                         do {
632                                 fmt_time_bernstein_25(ld->fnsave);
633                                 errno = 0;
634                                 stat(ld->fnsave, &st);
635                         } while (errno != ENOENT);
636                         while (rename("current", ld->fnsave) == -1)
637                                 pause2cannot("rename current", ld->name);
638                         rmoldest(ld);
639                         i = -1;
640                 } else {
641                         /* st.st_size can be not just bigger, but WIDER!
642                          * This code is safe: if st.st_size > 4GB, we select
643                          * ld->sizemax (because it's "unsigned") */
644                         ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
645                 }
646         } else {
647                 if (errno != ENOENT) {
648                         logdir_close(ld);
649                         warn2("cannot stat current", ld->name);
650                         while (fchdir(fdwdir) == -1)
651                                 pause1cannot("change to initial working directory");
652                         return 0;
653                 }
654         }
655         while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
656                 pause2cannot("open current", ld->name);
657         /* we presume this cannot fail */
658         ld->filecur = fdopen(ld->fdcur, "a"); ////
659         setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
660
661         close_on_exec_on(ld->fdcur);
662         while (fchmod(ld->fdcur, 0644) == -1)
663                 pause2cannot("set mode of current", ld->name);
664
665         if (verbose) {
666                 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
667                 else bb_error_msg(INFO"new: %s/current", ld->name);
668         }
669
670         while (fchdir(fdwdir) == -1)
671                 pause1cannot("change to initial working directory");
672         return 1;
673 }
674
675 static void logdirs_reopen(void)
676 {
677         int l;
678         int ok = 0;
679
680         tmaxflag = 0;
681         for (l = 0; l < dirn; ++l) {
682                 logdir_close(&dir[l]);
683                 if (logdir_open(&dir[l], fndir[l]))
684                         ok = 1;
685         }
686         if (!ok)
687                 fatalx("no functional log directories");
688 }
689
690 /* Will look good in libbb one day */
691 static ssize_t ndelay_read(int fd, void *buf, size_t count)
692 {
693         if (!(fl_flag_0 & O_NONBLOCK))
694                 fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
695         count = safe_read(fd, buf, count);
696         if (!(fl_flag_0 & O_NONBLOCK))
697                 fcntl(fd, F_SETFL, fl_flag_0);
698         return count;
699 }
700
701 /* Used for reading stdin */
702 static int buffer_pread(/*int fd, */char *s, unsigned len)
703 {
704         unsigned now;
705         struct pollfd input;
706         int i;
707
708         input.fd = 0;
709         input.events = POLLIN;
710
711         do {
712                 if (rotateasap) {
713                         for (i = 0; i < dirn; ++i)
714                                 rotate(dir + i);
715                         rotateasap = 0;
716                 }
717                 if (exitasap) {
718                         if (linecomplete)
719                                 return 0;
720                         len = 1;
721                 }
722                 if (reopenasap) {
723                         logdirs_reopen();
724                         reopenasap = 0;
725                 }
726                 now = monotonic_sec();
727                 nearest_rotate = now + (45 * 60 + 45);
728                 for (i = 0; i < dirn; ++i) {
729                         if (dir[i].rotate_period) {
730                                 if (LESS(dir[i].next_rotate, now))
731                                         rotate(dir + i);
732                                 if (LESS(dir[i].next_rotate, nearest_rotate))
733                                         nearest_rotate = dir[i].next_rotate;
734                         }
735                 }
736
737                 sigprocmask(SIG_UNBLOCK, &blocked_sigset, NULL);
738                 i = nearest_rotate - now;
739                 if (i > 1000000)
740                         i = 1000000;
741                 if (i <= 0)
742                         i = 1;
743                 poll(&input, 1, i * 1000);
744                 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
745
746                 i = ndelay_read(0, s, len);
747                 if (i >= 0)
748                         break;
749                 if (errno == EINTR)
750                         continue;
751                 if (errno != EAGAIN) {
752                         warn("cannot read standard input");
753                         break;
754                 }
755                 /* else: EAGAIN - normal, repeat silently */
756         } while (!exitasap);
757
758         if (i > 0) {
759                 int cnt;
760                 linecomplete = (s[i-1] == '\n');
761                 if (!repl)
762                         return i;
763
764                 cnt = i;
765                 while (--cnt >= 0) {
766                         char ch = *s;
767                         if (ch != '\n') {
768                                 if (ch < 32 || ch > 126)
769                                         *s = repl;
770                                 else {
771                                         int j;
772                                         for (j = 0; replace[j]; ++j) {
773                                                 if (ch == replace[j]) {
774                                                         *s = repl;
775                                                         break;
776                                                 }
777                                         }
778                                 }
779                         }
780                         s++;
781                 }
782         }
783         return i;
784 }
785
786 static void sig_term_handler(int sig_no)
787 {
788         if (verbose)
789                 bb_error_msg(INFO"sig%s received", "term");
790         exitasap = 1;
791 }
792
793 static void sig_child_handler(int sig_no)
794 {
795         int pid, l;
796
797         if (verbose)
798                 bb_error_msg(INFO"sig%s received", "child");
799         while ((pid = wait_any_nohang(&wstat)) > 0) {
800                 for (l = 0; l < dirn; ++l) {
801                         if (dir[l].ppid == pid) {
802                                 dir[l].ppid = 0;
803                                 processorstop(&dir[l]);
804                                 break;
805                         }
806                 }
807         }
808 }
809
810 static void sig_alarm_handler(int sig_no)
811 {
812         if (verbose)
813                 bb_error_msg(INFO"sig%s received", "alarm");
814         rotateasap = 1;
815 }
816
817 static void sig_hangup_handler(int sig_no)
818 {
819         if (verbose)
820                 bb_error_msg(INFO"sig%s received", "hangup");
821         reopenasap = 1;
822 }
823
824 static void logmatch(struct logdir *ld)
825 {
826         char *s;
827
828         ld->match = '+';
829         ld->matcherr = 'E';
830         s = ld->inst;
831         while (s && s[0]) {
832                 switch (s[0]) {
833                 case '+':
834                 case '-':
835                         if (pmatch(s+1, line, linelen))
836                                 ld->match = s[0];
837                         break;
838                 case 'e':
839                 case 'E':
840                         if (pmatch(s+1, line, linelen))
841                                 ld->matcherr = s[0];
842                         break;
843                 }
844                 s += strlen(s) + 1;
845         }
846 }
847
848 int svlogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
849 int svlogd_main(int argc, char **argv)
850 {
851         char *r,*l,*b;
852         ssize_t stdin_cnt = 0;
853         int i;
854         unsigned opt;
855         unsigned timestamp = 0;
856         void* (*memRchr)(const void *, int, size_t) = memchr;
857
858         INIT_G();
859
860         opt_complementary = "tt:vv";
861         opt = getopt32(argv, "r:R:l:b:tv",
862                         &r, &replace, &l, &b, &timestamp, &verbose);
863         if (opt & 1) { // -r
864                 repl = r[0];
865                 if (!repl || r[1]) usage();
866         }
867         if (opt & 2) if (!repl) repl = '_'; // -R
868         if (opt & 4) { // -l
869                 linemax = xatou_range(l, 0, BUFSIZ-26);
870                 if (linemax == 0) linemax = BUFSIZ-26;
871                 if (linemax < 256) linemax = 256;
872         }
873         ////if (opt & 8) { // -b
874         ////    buflen = xatoi_u(b);
875         ////    if (buflen == 0) buflen = 1024;
876         ////}
877         //if (opt & 0x10) timestamp++; // -t
878         //if (opt & 0x20) verbose++; // -v
879         //if (timestamp > 2) timestamp = 2;
880         argv += optind;
881         argc -= optind;
882
883         dirn = argc;
884         if (dirn <= 0) usage();
885         ////if (buflen <= linemax) usage();
886         fdwdir = xopen(".", O_RDONLY|O_NDELAY);
887         close_on_exec_on(fdwdir);
888         dir = xzalloc(dirn * sizeof(struct logdir));
889         for (i = 0; i < dirn; ++i) {
890                 dir[i].fddir = -1;
891                 dir[i].fdcur = -1;
892                 ////dir[i].btmp = xmalloc(buflen);
893                 /*dir[i].ppid = 0;*/
894         }
895         /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
896         fndir = argv;
897         /* We cannot set NONBLOCK on fd #0 permanently - this setting
898          * _isn't_ per-process! It is shared among all other processes
899          * with the same stdin */
900         fl_flag_0 = fcntl(0, F_GETFL);
901
902         sigemptyset(&blocked_sigset);
903         sigaddset(&blocked_sigset, SIGTERM);
904         sigaddset(&blocked_sigset, SIGCHLD);
905         sigaddset(&blocked_sigset, SIGALRM);
906         sigaddset(&blocked_sigset, SIGHUP);
907         sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
908         bb_signals_recursive(1 << SIGTERM, sig_term_handler);
909         bb_signals_recursive(1 << SIGCHLD, sig_child_handler);
910         bb_signals_recursive(1 << SIGALRM, sig_alarm_handler);
911         bb_signals_recursive(1 << SIGHUP, sig_hangup_handler);
912
913         logdirs_reopen();
914
915         /* Without timestamps, we don't have to print each line
916          * separately, so we can look for _last_ newline, not first,
917          * thus batching writes */
918         if (!timestamp)
919                 memRchr = memrchr;
920
921         setvbuf(stderr, NULL, _IOFBF, linelen);
922
923         /* Each iteration processes one or more lines */
924         while (1) {
925                 char stamp[FMT_PTIME];
926                 char *lineptr;
927                 char *printptr;
928                 char *np;
929                 int printlen;
930                 char ch;
931
932                 lineptr = line;
933                 if (timestamp)
934                         lineptr += 26;
935
936                 /* lineptr[0..linemax-1] - buffer for stdin */
937                 /* (possibly has some unprocessed data from prev loop) */
938
939                 /* Refill the buffer if needed */
940                 np = memRchr(lineptr, '\n', stdin_cnt);
941                 if (!np && !exitasap) {
942                         i = linemax - stdin_cnt; /* avail. bytes at tail */
943                         if (i >= 128) {
944                                 i = buffer_pread(/*0, */lineptr + stdin_cnt, i);
945                                 if (i <= 0) /* EOF or error on stdin */
946                                         exitasap = 1;
947                                 else {
948                                         np = memRchr(lineptr + stdin_cnt, '\n', i);
949                                         stdin_cnt += i;
950                                 }
951                         }
952                 }
953                 if (stdin_cnt <= 0 && exitasap)
954                         break;
955
956                 /* Search for '\n' (in fact, np already holds the result) */
957                 linelen = stdin_cnt;
958                 if (np) {
959  print_to_nl:           /* NB: starting from here lineptr may point
960                          * farther out into line[] */
961                         linelen = np - lineptr + 1;
962                 }
963                 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
964                 ch = lineptr[linelen-1];
965
966                 /* Biggest performance hit was coming from the fact
967                  * that we did not buffer writes. We were reading many lines
968                  * in one read() above, but wrote one line per write().
969                  * We are using stdio to fix that */
970
971                 /* write out lineptr[0..linelen-1] to each log destination
972                  * (or lineptr[-26..linelen-1] if timestamping) */
973                 printlen = linelen;
974                 printptr = lineptr;
975                 if (timestamp) {
976                         if (timestamp == 1)
977                                 fmt_time_bernstein_25(stamp);
978                         else /* 2: */
979                                 fmt_time_human_30nul(stamp);
980                         printlen += 26;
981                         printptr -= 26;
982                         memcpy(printptr, stamp, 25);
983                         printptr[25] = ' ';
984                 }
985                 for (i = 0; i < dirn; ++i) {
986                         struct logdir *ld = &dir[i];
987                         if (ld->fddir == -1) continue;
988                         if (ld->inst)
989                                 logmatch(ld);
990                         if (ld->matcherr == 'e') {
991                                 /* runit-1.8.0 compat: if timestamping, do it on stderr too */
992                                 ////full_write(2, printptr, printlen);
993                                 fwrite(printptr, 1, printlen, stderr);
994                         }
995                         if (ld->match != '+') continue;
996                         buffer_pwrite(i, printptr, printlen);
997                 }
998
999                 /* If we didn't see '\n' (long input line), */
1000                 /* read/write repeatedly until we see it */
1001                 while (ch != '\n') {
1002                         /* lineptr is emptied now, safe to use as buffer */
1003                         stdin_cnt = exitasap ? -1 : buffer_pread(/*0, */lineptr, linemax);
1004                         if (stdin_cnt <= 0) { /* EOF or error on stdin */
1005                                 exitasap = 1;
1006                                 lineptr[0] = ch = '\n';
1007                                 linelen = 1;
1008                                 stdin_cnt = 1;
1009                         } else {
1010                                 linelen = stdin_cnt;
1011                                 np = memRchr(lineptr, '\n', stdin_cnt);
1012                                 if (np)
1013                                         linelen = np - lineptr + 1;
1014                                 ch = lineptr[linelen-1];
1015                         }
1016                         /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1017                         for (i = 0; i < dirn; ++i) {
1018                                 if (dir[i].fddir == -1) continue;
1019                                 if (dir[i].matcherr == 'e') {
1020                                         ////full_write(2, lineptr, linelen);
1021                                         fwrite(lineptr, 1, linelen, stderr);
1022                                 }
1023                                 if (dir[i].match != '+') continue;
1024                                 buffer_pwrite(i, lineptr, linelen);
1025                         }
1026                 }
1027
1028                 stdin_cnt -= linelen;
1029                 if (stdin_cnt > 0) {
1030                         lineptr += linelen;
1031                         /* If we see another '\n', we don't need to read
1032                          * next piece of input: can print what we have */
1033                         np = memRchr(lineptr, '\n', stdin_cnt);
1034                         if (np)
1035                                 goto print_to_nl;
1036                         /* Move unprocessed data to the front of line */
1037                         memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
1038                 }
1039                 fflush(NULL);////
1040         }
1041
1042         for (i = 0; i < dirn; ++i) {
1043                 if (dir[i].ppid)
1044                         while (!processorstop(&dir[i]))
1045                                 /* repeat */;
1046                 logdir_close(&dir[i]);
1047         }
1048         return 0;
1049 }