svlogd: new applet. +9k. Still too big, but it was 12k yesterday.
No binary (build failed)
—
(log yok)
| @@ -10,7 +10,7 @@ | |
| #include "busybox.h" | |
| #include <sys/kd.h> | |
| - | enum{ |
| + | enum { |
| PSF_MAGIC1 = 0x36, | |
| PSF_MAGIC2 = 0x04, | |
| @@ -270,6 +270,7 @@ USE_STTY(APPLET(stty, _BB_DIR_BIN, _BB_SUID_NEVER)) | |
| USE_SU(APPLET(su, _BB_DIR_BIN, _BB_SUID_ALWAYS)) | |
| USE_SULOGIN(APPLET(sulogin, _BB_DIR_SBIN, _BB_SUID_NEVER)) | |
| USE_SUM(APPLET(sum, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) | |
| + | USE_SVLOGD(APPLET(svlogd, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) |
| USE_SWAPONOFF(APPLET_ODDNAME(swapoff, swap_on_off, _BB_DIR_SBIN, _BB_SUID_NEVER,swapoff)) | |
| USE_SWAPONOFF(APPLET_ODDNAME(swapon, swap_on_off, _BB_DIR_SBIN, _BB_SUID_NEVER, swapon)) | |
| USE_SWITCH_ROOT(APPLET(switch_root, _BB_DIR_SBIN, _BB_SUID_NEVER)) | |
| @@ -335,6 +335,8 @@ long xatol_range(const char *numstr, long lower, long upper); | |
| long xatol_sfx(const char *numstr, const struct suffix_mult *suffixes); | |
| long xatol(const char *numstr); | |
| /* Specialized: */ | |
| + | unsigned xatou_range(const char *numstr, unsigned lower, unsigned upper); |
| + | unsigned xatou_sfx(const char *numstr, const struct suffix_mult *suffixes); |
| unsigned xatou(const char *numstr); | |
| int xatoi_range(const char *numstr, int lower, int upper); | |
| int xatoi(const char *numstr); | |
| @@ -2841,6 +2841,13 @@ USE_FEATURE_START_STOP_DAEMON_FANCY( \ | |
| "\t-r\tuse BSD sum algorithm (1K blocks)\n" \ | |
| "\t-s\tuse System V sum algorithm (512byte blocks)" | |
| + | #define svlogd_trivial_usage \ |
| + | "[-ttv] [-r c] [-R abc] [-l len] [-b buflen] dir..." |
| + | #define svlogd_full_usage \ |
| + | "Continuously read log data from standard input, optionally " \ |
| + | "filter log messages, and write the data to one or more automatically " \ |
| + | "rotated logs." |
| + | |
| #define swapoff_trivial_usage \ | |
| "[-a] [DEVICE]" | |
| #define swapoff_full_usage \ | |
| @@ -195,6 +195,16 @@ long xatol(const char *numstr) | |
| /* Others */ | |
| + | unsigned xatou_range(const char *numstr, unsigned lower, unsigned upper) |
| + | { |
| + | return xstrtoul_range_sfx(numstr, 10, lower, upper, NULL); |
| + | } |
| + | |
| + | unsigned xatou_sfx(const char *numstr, const struct suffix_mult *suffixes) |
| + | { |
| + | return xstrtoul_range_sfx(numstr, 10, 0, UINT_MAX, suffixes); |
| + | } |
| + | |
| unsigned xatou(const char *numstr) | |
| { | |
| return xatoul_range(numstr, 0, UINT_MAX); | |
| @@ -5,6 +5,14 @@ | |
| menu "Runit Utilities" | |
| + | config SVLOGD |
| + | bool "svlogd" |
| + | default n |
| + | help |
| + | svlogd continuously reads log data from its standard input, optionally |
| + | filters log messages, and writes the data to one or more automatically |
| + | rotated logs. |
| + | |
| config CHPST | |
| bool "chpst" | |
| default n | |
| @@ -6,3 +6,4 @@ | |
| lib-y:= | |
| lib-$(CONFIG_CHPST) += chpst.o | |
| + | lib-$(CONFIG_SVLOGD) += svlogd.o runit_lib.o |
| @@ -26,6 +26,7 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| */ | |
| /* Busyboxed by Denis Vlasenko <[email protected]> */ | |
| + | /* Dependencies on runit_lib.c removed */ |
| #include "busybox.h" | |
| @@ -94,7 +95,9 @@ static void edir(const char *directory_name) | |
| errno = 0; | |
| d = readdir(dir); | |
| if (!d) { | |
| - | if (errno) bb_perror_msg_and_die("readdir %s", directory_name); |
| + | if (errno) |
| + | bb_perror_msg_and_die("readdir %s", |
| + | directory_name); |
| break; | |
| } | |
| if (d->d_name[0] == '.') continue; | |
| @@ -102,12 +105,12 @@ static void edir(const char *directory_name) | |
| if (fd < 0) { | |
| if ((errno == EISDIR) && env_dir) { | |
| if (OPT_verbose) | |
| - | bb_perror_msg("warning: %s/%s is a directory", directory_name, |
| - | d->d_name); |
| + | bb_perror_msg("warning: %s/%s is a directory", |
| + | directory_name, d->d_name); |
| continue; | |
| } else | |
| - | bb_perror_msg_and_die("open %s/%s", directory_name, /* was exiting 111 */ |
| - | d->d_name); |
| + | bb_perror_msg_and_die("open %s/%s", |
| + | directory_name, d->d_name); |
| } | |
| if (fd >= 0) { | |
| char buf[256]; | |
| @@ -116,8 +119,8 @@ static void edir(const char *directory_name) | |
| size = safe_read(fd, buf, sizeof(buf)-1); | |
| if (size < 0) | |
| - | bb_perror_msg_and_die("read %s/%s", directory_name, /* was exiting 111 */ |
| - | d->d_name); |
| + | bb_perror_msg_and_die("read %s/%s", |
| + | directory_name, d->d_name); |
| if (size == 0) { | |
| unsetenv(d->d_name); | |
| continue; | |
| @@ -158,21 +161,24 @@ static void slimit(void) | |
| #ifdef RLIMIT_DATA | |
| limit(RLIMIT_DATA, limitd); | |
| #else | |
| - | if (OPT_verbose) bb_error_msg("system does not support %s", "RLIMIT_DATA"); |
| + | if (OPT_verbose) bb_error_msg("system does not support %s", |
| + | "RLIMIT_DATA"); |
| #endif | |
| } | |
| if (limits >= -1) { | |
| #ifdef RLIMIT_STACK | |
| limit(RLIMIT_STACK, limits); | |
| #else | |
| - | if (OPT_verbose) bb_error_msg("system does not support %s", "RLIMIT_STACK"); |
| + | if (OPT_verbose) bb_error_msg("system does not support %s", |
| + | "RLIMIT_STACK"); |
| #endif | |
| } | |
| if (limitl >= -1) { | |
| #ifdef RLIMIT_MEMLOCK | |
| limit(RLIMIT_MEMLOCK, limitl); | |
| #else | |
| - | if (OPT_verbose) bb_error_msg("system does not support %s", "RLIMIT_MEMLOCK"); |
| + | if (OPT_verbose) bb_error_msg("system does not support %s", |
| + | "RLIMIT_MEMLOCK"); |
| #endif | |
| } | |
| if (limita >= -1) { | |
| @@ -183,7 +189,8 @@ static void slimit(void) | |
| limit(RLIMIT_AS, limita); | |
| #else | |
| if (OPT_verbose) | |
| - | bb_error_msg("system does not support %s", "RLIMIT_VMEM"); |
| + | bb_error_msg("system does not support %s", |
| + | "RLIMIT_VMEM"); |
| #endif | |
| #endif | |
| } | |
| @@ -195,7 +202,8 @@ static void slimit(void) | |
| limit(RLIMIT_OFILE, limito); | |
| #else | |
| if (OPT_verbose) | |
| - | bb_error_msg("system does not support %s", "RLIMIT_NOFILE"); |
| + | bb_error_msg("system does not support %s", |
| + | "RLIMIT_NOFILE"); |
| #endif | |
| #endif | |
| } | |
| @@ -203,35 +211,40 @@ static void slimit(void) | |
| #ifdef RLIMIT_NPROC | |
| limit(RLIMIT_NPROC, limitp); | |
| #else | |
| - | if (OPT_verbose) bb_error_msg("system does not support %s", "RLIMIT_NPROC"); |
| + | if (OPT_verbose) bb_error_msg("system does not support %s", |
| + | "RLIMIT_NPROC"); |
| #endif | |
| } | |
| if (limitf >= -1) { | |
| #ifdef RLIMIT_FSIZE | |
| limit(RLIMIT_FSIZE, limitf); | |
| #else | |
| - | if (OPT_verbose) bb_error_msg("system does not support %s", "RLIMIT_FSIZE"); |
| + | if (OPT_verbose) bb_error_msg("system does not support %s", |
| + | "RLIMIT_FSIZE"); |
| #endif | |
| } | |
| if (limitc >= -1) { | |
| #ifdef RLIMIT_CORE | |
| limit(RLIMIT_CORE, limitc); | |
| #else | |
| - | if (OPT_verbose) bb_error_msg("system does not support %s", "RLIMIT_CORE"); |
| + | if (OPT_verbose) bb_error_msg("system does not support %s", |
| + | "RLIMIT_CORE"); |
| #endif | |
| } | |
| if (limitr >= -1) { | |
| #ifdef RLIMIT_RSS | |
| limit(RLIMIT_RSS, limitr); | |
| #else | |
| - | if (OPT_verbose) bb_error_msg("system does not support %s", "RLIMIT_RSS"); |
| + | if (OPT_verbose) bb_error_msg("system does not support %s", |
| + | "RLIMIT_RSS"); |
| #endif | |
| } | |
| if (limitt >= -1) { | |
| #ifdef RLIMIT_CPU | |
| limit(RLIMIT_CPU, limitt); | |
| #else | |
| - | if (OPT_verbose) bb_error_msg("system does not support %s", "RLIMIT_CPU"); |
| + | if (OPT_verbose) bb_error_msg("system does not support %s", |
| + | "RLIMIT_CPU"); |
| #endif | |
| } | |
| } | |
| @@ -0,0 +1,999 @@ | |
| + | /* |
| + | Copyright (c) 2001-2006, Gerrit Pape |
| + | All rights reserved. |
| + | |
| + | Redistribution and use in source and binary forms, with or without |
| + | modification, are permitted provided that the following conditions are met: |
| + | |
| + | 1. Redistributions of source code must retain the above copyright notice, |
| + | this list of conditions and the following disclaimer. |
| + | 2. Redistributions in binary form must reproduce the above copyright |
| + | notice, this list of conditions and the following disclaimer in the |
| + | documentation and/or other materials provided with the distribution. |
| + | 3. The name of the author may not be used to endorse or promote products |
| + | derived from this software without specific prior written permission. |
| + | |
| + | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| + | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| + | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
| + | EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| + | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| + | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| + | OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| + | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| + | OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| + | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| + | */ |
| + | |
| + | /* Busyboxed by Denis Vlasenko <[email protected]> */ |
| + | /* Collected into one file from runit's many tiny files */ |
| + | /* TODO: review, eliminate unneeded stuff, move good stuff to libbb */ |
| + | |
| + | #include <sys/poll.h> |
| + | #include <sys/file.h> |
| + | #include "libbb.h" |
| + | #include "runit_lib.h" |
| + | |
| + | #ifndef O_NONBLOCK |
| + | #define O_NONBLOCK O_NDELAY |
| + | #endif |
| + | |
| + | /*** buffer.c ***/ |
| + | |
| + | void buffer_init(buffer *s,int (*op)(int fd,char *buf,unsigned len),int fd,char *buf,unsigned len) |
| + | { |
| + | s->x = buf; |
| + | s->fd = fd; |
| + | s->op = op; |
| + | s->p = 0; |
| + | s->n = len; |
| + | } |
| + | |
| + | |
| + | /*** buffer_get.c ***/ |
| + | |
| + | static int oneread(int (*op)(int fd,char *buf,unsigned len),int fd,char *buf,unsigned len) |
| + | { |
| + | int r; |
| + | |
| + | for (;;) { |
| + | r = op(fd,buf,len); |
| + | if (r == -1) if (errno == EINTR) continue; |
| + | return r; |
| + | } |
| + | } |
| + | |
| + | static int getthis(buffer *s,char *buf,unsigned len) |
| + | { |
| + | if (len > s->p) len = s->p; |
| + | s->p -= len; |
| + | memcpy(buf,s->x + s->n,len); |
| + | s->n += len; |
| + | return len; |
| + | } |
| + | |
| + | int buffer_feed(buffer *s) |
| + | { |
| + | int r; |
| + | |
| + | if (s->p) return s->p; |
| + | r = oneread(s->op,s->fd,s->x,s->n); |
| + | if (r <= 0) return r; |
| + | s->p = r; |
| + | s->n -= r; |
| + | if (s->n > 0) memmove(s->x + s->n,s->x,r); |
| + | return r; |
| + | } |
| + | |
| + | int buffer_bget(buffer *s,char *buf,unsigned len) |
| + | { |
| + | int r; |
| + | |
| + | if (s->p > 0) return getthis(s,buf,len); |
| + | if (s->n <= len) return oneread(s->op,s->fd,buf,s->n); |
| + | r = buffer_feed(s); if (r <= 0) return r; |
| + | return getthis(s,buf,len); |
| + | } |
| + | |
| + | int buffer_get(buffer *s,char *buf,unsigned len) |
| + | { |
| + | int r; |
| + | |
| + | if (s->p > 0) return getthis(s,buf,len); |
| + | if (s->n <= len) return oneread(s->op,s->fd,buf,len); |
| + | r = buffer_feed(s); if (r <= 0) return r; |
| + | return getthis(s,buf,len); |
| + | } |
| + | |
| + | char *buffer_peek(buffer *s) |
| + | { |
| + | return s->x + s->n; |
| + | } |
| + | |
| + | void buffer_seek(buffer *s,unsigned len) |
| + | { |
| + | s->n += len; |
| + | s->p -= len; |
| + | } |
| + | |
| + | |
| + | /*** buffer_put.c ***/ |
| + | |
| + | static int allwrite(int (*op)(int fd,char *buf,unsigned len),int fd,const char *buf,unsigned len) |
| + | { |
| + | int w; |
| + | |
| + | while (len) { |
| + | w = op(fd,(char*)buf,len); |
| + | if (w == -1) { |
| + | if (errno == EINTR) continue; |
| + | return -1; /* note that some data may have been written */ |
| + | } |
| + | if (w == 0) ; /* luser's fault */ |
| + | buf += w; |
| + | len -= w; |
| + | } |
| + | return 0; |
| + | } |
| + | |
| + | int buffer_flush(buffer *s) |
| + | { |
| + | int p; |
| + | |
| + | p = s->p; |
| + | if (!p) return 0; |
| + | s->p = 0; |
| + | return allwrite(s->op,s->fd,s->x,p); |
| + | } |
| + | |
| + | int buffer_putalign(buffer *s,const char *buf,unsigned len) |
| + | { |
| + | unsigned n; |
| + | |
| + | while (len > (n = s->n - s->p)) { |
| + | memcpy(s->x + s->p,buf,n); |
| + | s->p += n; |
| + | buf += n; |
| + | len -= n; |
| + | if (buffer_flush(s) == -1) return -1; |
| + | } |
| + | /* now len <= s->n - s->p */ |
| + | memcpy(s->x + s->p,buf,len); |
| + | s->p += len; |
| + | return 0; |
| + | } |
| + | |
| + | int buffer_put(buffer *s,const char *buf,unsigned len) |
| + | { |
| + | unsigned n; |
| + | |
| + | n = s->n; |
| + | if (len > n - s->p) { |
| + | if (buffer_flush(s) == -1) return -1; |
| + | /* now s->p == 0 */ |
| + | if (n < BUFFER_OUTSIZE) n = BUFFER_OUTSIZE; |
| + | while (len > s->n) { |
| + | if (n > len) n = len; |
| + | if (allwrite(s->op,s->fd,buf,n) == -1) return -1; |
| + | buf += n; |
| + | len -= n; |
| + | } |
| + | } |
| + | /* now len <= s->n - s->p */ |
| + | memcpy(s->x + s->p,buf,len); |
| + | s->p += len; |
| + | return 0; |
| + | } |
| + | |
| + | int buffer_putflush(buffer *s,const char *buf,unsigned len) |
| + | { |
| + | if (buffer_flush(s) == -1) return -1; |
| + | return allwrite(s->op,s->fd,buf,len); |
| + | } |
| + | |
| + | int buffer_putsalign(buffer *s,const char *buf) |
| + | { |
| + | return buffer_putalign(s,buf,strlen(buf)); |
| + | } |
| + | |
| + | int buffer_puts(buffer *s,const char *buf) |
| + | { |
| + | return buffer_put(s,buf,strlen(buf)); |
| + | } |
| + | |
| + | int buffer_putsflush(buffer *s,const char *buf) |
| + | { |
| + | return buffer_putflush(s,buf,strlen(buf)); |
| + | } |
| + | |
| + | |
| + | /*** buffer_read.c ***/ |
| + | |
| + | int buffer_unixread(int fd,char *buf,unsigned len) |
| + | { |
| + | return read(fd,buf,len); |
| + | } |
| + | |
| + | |
| + | /*** buffer_write.c ***/ |
| + | |
| + | int buffer_unixwrite(int fd,char *buf,unsigned len) |
| + | { |
| + | return write(fd,buf,len); |
| + | } |
| + | |
| + | |
| + | /*** byte_chr.c ***/ |
| + | |
| + | unsigned byte_chr(char *s,unsigned n,int c) |
| + | { |
| + | char ch; |
| + | char *t; |
| + | |
| + | ch = c; |
| + | t = s; |
| + | for (;;) { |
| + | if (!n) break; if (*t == ch) break; ++t; --n; |
| + | if (!n) break; if (*t == ch) break; ++t; --n; |
| + | if (!n) break; if (*t == ch) break; ++t; --n; |
| + | if (!n) break; if (*t == ch) break; ++t; --n; |
| + | } |
| + | return t - s; |
| + | } |
| + | |
| + | |
| + | /*** coe.c ***/ |
| + | |
| + | int coe(int fd) |
| + | { |
| + | return fcntl(fd,F_SETFD,FD_CLOEXEC); |
| + | } |
| + | |
| + | |
| + | /*** fd_copy.c ***/ |
| + | |
| + | int fd_copy(int to,int from) |
| + | { |
| + | if (to == from) return 0; |
| + | if (fcntl(from,F_GETFL,0) == -1) return -1; |
| + | close(to); |
| + | if (fcntl(from,F_DUPFD,to) == -1) return -1; |
| + | return 0; |
| + | } |
| + | |
| + | |
| + | /*** fd_move.c ***/ |
| + | |
| + | int fd_move(int to,int from) |
| + | { |
| + | if (to == from) return 0; |
| + | if (fd_copy(to,from) == -1) return -1; |
| + | close(from); |
| + | return 0; |
| + | } |
| + | |
| + | |
| + | /*** fifo.c ***/ |
| + | |
| + | int fifo_make(const char *fn,int mode) { return mkfifo(fn,mode); } |
| + | |
| + | |
| + | /*** fmt_ptime.c ***/ |
| + | |
| + | unsigned fmt_ptime(char *s, struct taia *ta) { |
| + | struct tm *t; |
| + | unsigned long u; |
| + | |
| + | if (ta->sec.x < 4611686018427387914ULL) return 0; /* impossible? */ |
| + | u = ta->sec.x -4611686018427387914ULL; |
| + | if (!(t = gmtime((time_t*)&u))) return 0; |
| + | fmt_ulong(s, 1900 + t->tm_year); |
| + | s[4] = '-'; fmt_uint0(&s[5], t->tm_mon+1, 2); |
| + | s[7] = '-'; fmt_uint0(&s[8], t->tm_mday, 2); |
| + | s[10] = '_'; fmt_uint0(&s[11], t->tm_hour, 2); |
| + | s[13] = ':'; fmt_uint0(&s[14], t->tm_min, 2); |
| + | s[16] = ':'; fmt_uint0(&s[17], t->tm_sec, 2); |
| + | s[19] = '.'; fmt_uint0(&s[20], ta->nano, 9); |
| + | return 25; |
| + | } |
| + | |
| + | unsigned fmt_taia(char *s, struct taia *t) { |
| + | static char hex[16] = "0123456789abcdef"; |
| + | static char pack[TAIA_PACK]; |
| + | int i; |
| + | |
| + | taia_pack(pack, t); |
| + | s[0] = '@'; |
| + | for (i = 0; i < 12; ++i) { |
| + | s[i*2+1] = hex[(pack[i] >> 4) &15]; |
| + | s[i*2+2] = hex[pack[i] &15]; |
| + | } |
| + | return 25; |
| + | } |
| + | |
| + | |
| + | /*** fmt_uint.c ***/ |
| + | |
| + | unsigned fmt_uint(char *s,unsigned u) |
| + | { |
| + | return fmt_ulong(s,u); |
| + | } |
| + | |
| + | |
| + | /*** fmt_uint0.c ***/ |
| + | |
| + | unsigned fmt_uint0(char *s,unsigned u,unsigned n) |
| + | { |
| + | unsigned len; |
| + | len = fmt_uint(FMT_LEN,u); |
| + | while (len < n) { if (s) *s++ = '0'; ++len; } |
| + | if (s) fmt_uint(s,u); |
| + | return len; |
| + | } |
| + | |
| + | |
| + | /*** fmt_ulong.c ***/ |
| + | |
| + | unsigned fmt_ulong(char *s,unsigned long u) |
| + | { |
| + | unsigned len; unsigned long q; |
| + | len = 1; q = u; |
| + | while (q > 9) { ++len; q /= 10; } |
| + | if (s) { |
| + | s += len; |
| + | do { *--s = '0' + (u % 10); u /= 10; } while(u); /* handles u == 0 */ |
| + | } |
| + | return len; |
| + | } |
| + | |
| + | |
| + | /*** tai_now.c ***/ |
| + | |
| + | void tai_now(struct tai *t) |
| + | { |
| + | tai_unix(t,time((time_t *) 0)); |
| + | } |
| + | |
| + | |
| + | /*** tai_pack.c ***/ |
| + | |
| + | void tai_pack(char *s,const struct tai *t) |
| + | { |
| + | uint64_t x; |
| + | |
| + | x = t->x; |
| + | s[7] = x & 255; x >>= 8; |
| + | s[6] = x & 255; x >>= 8; |
| + | s[5] = x & 255; x >>= 8; |
| + | s[4] = x & 255; x >>= 8; |
| + | s[3] = x & 255; x >>= 8; |
| + | s[2] = x & 255; x >>= 8; |
| + | s[1] = x & 255; x >>= 8; |
| + | s[0] = x; |
| + | } |
| + | |
| + | |
| + | /*** tai_sub.c ***/ |
| + | |
| + | void tai_sub(struct tai *t,const struct tai *u,const struct tai *v) |
| + | { |
| + | t->x = u->x - v->x; |
| + | } |
| + | |
| + | |
| + | /*** tai_unpack.c ***/ |
| + | |
| + | void tai_unpack(const char *s,struct tai *t) |
| + | { |
| + | uint64_t x; |
| + | |
| + | x = (unsigned char) s[0]; |
| + | x <<= 8; x += (unsigned char) s[1]; |
| + | x <<= 8; x += (unsigned char) s[2]; |
| + | x <<= 8; x += (unsigned char) s[3]; |
| + | x <<= 8; x += (unsigned char) s[4]; |
| + | x <<= 8; x += (unsigned char) s[5]; |
| + | x <<= 8; x += (unsigned char) s[6]; |
| + | x <<= 8; x += (unsigned char) s[7]; |
| + | t->x = x; |
| + | } |
| + | |
| + | |
| + | /*** taia_add.c ***/ |
| + | |
| + | /* XXX: breaks tai encapsulation */ |
| + | |
| + | void taia_add(struct taia *t,const struct taia *u,const struct taia *v) |
| + | { |
| + | t->sec.x = u->sec.x + v->sec.x; |
| + | t->nano = u->nano + v->nano; |
| + | t->atto = u->atto + v->atto; |
| + | if (t->atto > 999999999UL) { |
| + | t->atto -= 1000000000UL; |
| + | ++t->nano; |
| + | } |
| + | if (t->nano > 999999999UL) { |
| + | t->nano -= 1000000000UL; |
| + | ++t->sec.x; |
| + | } |
| + | } |
| + | |
| + | |
| + | /*** taia_approx.c ***/ |
| + | |
| + | double taia_approx(const struct taia *t) |
| + | { |
| + | return tai_approx(&t->sec) + taia_frac(t); |
| + | } |
| + | |
| + | |
| + | /*** taia_frac.c ***/ |
| + | |
| + | double taia_frac(const struct taia *t) |
| + | { |
| + | return (t->atto * 0.000000001 + t->nano) * 0.000000001; |
| + | } |
| + | |
| + | |
| + | /*** taia_less.c ***/ |
| + | |
| + | /* XXX: breaks tai encapsulation */ |
| + | |
| + | int taia_less(const struct taia *t,const struct taia *u) |
| + | { |
| + | if (t->sec.x < u->sec.x) return 1; |
| + | if (t->sec.x > u->sec.x) return 0; |
| + | if (t->nano < u->nano) return 1; |
| + | if (t->nano > u->nano) return 0; |
| + | return t->atto < u->atto; |
| + | } |
| + | |
| + | |
| + | /*** taia_now.c ***/ |
| + | |
| + | void taia_now(struct taia *t) |
| + | { |
| + | struct timeval now; |
| + | gettimeofday(&now,(struct timezone *) 0); |
| + | tai_unix(&t->sec,now.tv_sec); |
| + | t->nano = 1000 * now.tv_usec + 500; |
| + | t->atto = 0; |
| + | } |
| + | |
| + | |
| + | /*** taia_pack.c ***/ |
| + | |
| + | void taia_pack(char *s,const struct taia *t) |
| + | { |
| + | unsigned long x; |
| + | |
| + | tai_pack(s,&t->sec); |
| + | s += 8; |
| + | |
| + | x = t->atto; |
| + | s[7] = x & 255; x >>= 8; |
| + | s[6] = x & 255; x >>= 8; |
| + | s[5] = x & 255; x >>= 8; |
| + | s[4] = x; |
| + | x = t->nano; |
| + | s[3] = x & 255; x >>= 8; |
| + | s[2] = x & 255; x >>= 8; |
| + | s[1] = x & 255; x >>= 8; |
| + | s[0] = x; |
| + | } |
| + | |
| + | |
| + | /*** taia_sub.c ***/ |
| + | |
| + | /* XXX: breaks tai encapsulation */ |
| + | |
| + | void taia_sub(struct taia *t,const struct taia *u,const struct taia *v) |
| + | { |
| + | unsigned long unano = u->nano; |
| + | unsigned long uatto = u->atto; |
| + | |
| + | t->sec.x = u->sec.x - v->sec.x; |
| + | t->nano = unano - v->nano; |
| + | t->atto = uatto - v->atto; |
| + | if (t->atto > uatto) { |
| + | t->atto += 1000000000UL; |
| + | --t->nano; |
| + | } |
| + | if (t->nano > unano) { |
| + | t->nano += 1000000000UL; |
| + | --t->sec.x; |
| + | } |
| + | } |
| + | |
| + | |
| + | /*** taia_uint.c ***/ |
| + | |
| + | /* XXX: breaks tai encapsulation */ |
| + | |
| + | void taia_uint(struct taia *t,unsigned s) |
| + | { |
| + | t->sec.x = s; |
| + | t->nano = 0; |
| + | t->atto = 0; |
| + | } |
| + | |
| + | |
| + | /*** stralloc_cat.c ***/ |
| + | |
| + | int stralloc_cat(stralloc *sato,const stralloc *safrom) |
| + | { |
| + | return stralloc_catb(sato,safrom->s,safrom->len); |
| + | } |
| + | |
| + | |
| + | /*** stralloc_catb.c ***/ |
| + | |
| + | int stralloc_catb(stralloc *sa,const char *s,unsigned n) |
| + | { |
| + | if (!sa->s) return stralloc_copyb(sa,s,n); |
| + | if (!stralloc_readyplus(sa,n + 1)) return 0; |
| + | memcpy(sa->s + sa->len,s,n); |
| + | sa->len += n; |
| + | sa->s[sa->len] = 'Z'; /* ``offensive programming'' */ |
| + | return 1; |
| + | } |
| + | |
| + | |
| + | /*** stralloc_cats.c ***/ |
| + | |
| + | int stralloc_cats(stralloc *sa,const char *s) |
| + | { |
| + | return stralloc_catb(sa,s,strlen(s)); |
| + | } |
| + | |
| + | |
| + | /*** stralloc_eady.c ***/ |
| + | |
| + | GEN_ALLOC_ready(stralloc,char,s,len,a,i,n,x,30,stralloc_ready) |
| + | GEN_ALLOC_readyplus(stralloc,char,s,len,a,i,n,x,30,stralloc_readyplus) |
| + | |
| + | |
| + | /*** stralloc_opyb.c ***/ |
| + | |
| + | int stralloc_copyb(stralloc *sa,const char *s,unsigned n) |
| + | { |
| + | if (!stralloc_ready(sa,n + 1)) return 0; |
| + | memcpy(sa->s,s,n); |
| + | sa->len = n; |
| + | sa->s[n] = 'Z'; /* ``offensive programming'' */ |
| + | return 1; |
| + | } |
| + | |
| + | |
| + | /*** stralloc_opys.c ***/ |
| + | |
| + | int stralloc_copys(stralloc *sa,const char *s) |
| + | { |
| + | return stralloc_copyb(sa,s,strlen(s)); |
| + | } |
| + | |
| + | |
| + | /*** stralloc_pend.c ***/ |
| + | |
| + | GEN_ALLOC_append(stralloc,char,s,len,a,i,n,x,30,stralloc_readyplus,stralloc_append) |
| + | |
| + | |
| + | /*** iopause.c ***/ |
| + | |
| + | void iopause(iopause_fd *x,unsigned len,struct taia *deadline,struct taia *stamp) |
| + | { |
| + | struct taia t; |
| + | int millisecs; |
| + | double d; |
| + | int i; |
| + | |
| + | if (taia_less(deadline,stamp)) |
| + | millisecs = 0; |
| + | else { |
| + | t = *stamp; |
| + | taia_sub(&t,deadline,&t); |
| + | d = taia_approx(&t); |
| + | if (d > 1000.0) d = 1000.0; |
| + | millisecs = d * 1000.0 + 20.0; |
| + | } |
| + | |
| + | for (i = 0;i < len;++i) |
| + | x[i].revents = 0; |
| + | |
| + | poll(x,len,millisecs); |
| + | /* XXX: some kernels apparently need x[0] even if len is 0 */ |
| + | /* XXX: how to handle EAGAIN? are kernels really this dumb? */ |
| + | /* XXX: how to handle EINVAL? when exactly can this happen? */ |
| + | } |
| + | |
| + | |
| + | /*** lock_ex.c ***/ |
| + | |
| + | int lock_ex(int fd) |
| + | { |
| + | return flock(fd,LOCK_EX); |
| + | } |
| + | |
| + | |
| + | /*** lock_exnb.c ***/ |
| + | |
| + | int lock_exnb(int fd) |
| + | { |
| + | return flock(fd,LOCK_EX | LOCK_NB); |
| + | } |
| + | |
| + | |
| + | /*** ndelay_off.c ***/ |
| + | |
| + | int ndelay_off(int fd) |
| + | { |
| + | return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) & ~O_NONBLOCK); |
| + | } |
| + | |
| + | |
| + | /*** ndelay_on.c ***/ |
| + | |
| + | int ndelay_on(int fd) |
| + | { |
| + | return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) | O_NONBLOCK); |
| + | } |
| + | |
| + | |
| + | /*** open_append.c ***/ |
| + | |
| + | int open_append(const char *fn) |
| + | { |
| + | return open(fn,O_WRONLY | O_NDELAY | O_APPEND | O_CREAT,0600); |
| + | } |
| + | |
| + | |
| + | /*** open_read.c ***/ |
| + | |
| + | int open_read(const char *fn) |
| + | { |
| + | return open(fn,O_RDONLY | O_NDELAY); |
| + | } |
| + | |
| + | |
| + | /*** open_trunc.c ***/ |
| + | |
| + | int open_trunc(const char *fn) |
| + | { |
| + | return open(fn,O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT,0644); |
| + | } |
| + | |
| + | |
| + | /*** open_write.c ***/ |
| + | |
| + | int open_write(const char *fn) |
| + | { |
| + | return open(fn,O_WRONLY | O_NDELAY); |
| + | } |
| + | |
| + | |
| + | /*** openreadclose.c ***/ |
| + | |
| + | int openreadclose(const char *fn,stralloc *sa,unsigned bufsize) |
| + | { |
| + | int fd; |
| + | fd = open_read(fn); |
| + | if (fd == -1) { |
| + | if (errno == ENOENT) return 0; |
| + | return -1; |
| + | } |
| + | if (readclose(fd,sa,bufsize) == -1) return -1; |
| + | return 1; |
| + | } |
| + | |
| + | |
| + | /*** pathexec_env.c ***/ |
| + | |
| + | static stralloc plus; |
| + | static stralloc tmp; |
| + | |
| + | int pathexec_env(const char *s,const char *t) |
| + | { |
| + | if (!s) return 1; |
| + | if (!stralloc_copys(&tmp,s)) return 0; |
| + | if (t) { |
| + | if (!stralloc_cats(&tmp,"=")) return 0; |
| + | if (!stralloc_cats(&tmp,t)) return 0; |
| + | } |
| + | if (!stralloc_0(&tmp)) return 0; |
| + | return stralloc_cat(&plus,&tmp); |
| + | } |
| + | |
| + | void pathexec(char **argv) |
| + | { |
| + | char **e; |
| + | unsigned elen; |
| + | unsigned i; |
| + | unsigned j; |
| + | unsigned split; |
| + | unsigned t; |
| + | |
| + | if (!stralloc_cats(&plus,"")) return; |
| + | |
| + | elen = 0; |
| + | for (i = 0;environ[i];++i) |
| + | ++elen; |
| + | for (i = 0;i < plus.len;++i) |
| + | if (!plus.s[i]) |
| + | ++elen; |
| + | |
| + | e = malloc((elen + 1) * sizeof(char *)); |
| + | if (!e) return; |
| + | |
| + | elen = 0; |
| + | for (i = 0;environ[i];++i) |
| + | e[elen++] = environ[i]; |
| + | |
| + | j = 0; |
| + | for (i = 0;i < plus.len;++i) |
| + | if (!plus.s[i]) { |
| + | split = str_chr(plus.s + j,'='); |
| + | for (t = 0;t < elen;++t) |
| + | if (memcmp(plus.s + j,e[t],split) == 0) |
| + | if (e[t][split] == '=') { |
| + | --elen; |
| + | e[t] = e[elen]; |
| + | break; |
| + | } |
| + | if (plus.s[j + split]) |
| + | e[elen++] = plus.s + j; |
| + | j = i + 1; |
| + | } |
| + | e[elen] = 0; |
| + | |
| + | pathexec_run(*argv,argv,e); |
| + | free(e); |
| + | } |
| + | |
| + | |
| + | /*** pathexec_run.c ***/ |
| + | |
| + | static stralloc tmp; |
| + | |
| + | void pathexec_run(const char *file,char *const *argv,char *const *envp) |
| + | { |
| + | const char *path; |
| + | unsigned split; |
| + | int savederrno; |
| + | |
| + | if (file[str_chr(file,'/')]) { |
| + | execve(file,argv,envp); |
| + | return; |
| + | } |
| + | |
| + | path = getenv("PATH"); |
| + | if (!path) path = "/bin:/usr/bin"; |
| + | |
| + | savederrno = 0; |
| + | for (;;) { |
| + | split = str_chr(path,':'); |
| + | if (!stralloc_copyb(&tmp,path,split)) return; |
| + | if (!split) |
| + | if (!stralloc_cats(&tmp,".")) return; |
| + | if (!stralloc_cats(&tmp,"/")) return; |
| + | if (!stralloc_cats(&tmp,file)) return; |
| + | if (!stralloc_0(&tmp)) return; |
| + | |
| + | execve(tmp.s,argv,envp); |
| + | if (errno != ENOENT) { |
| + | savederrno = errno; |
| + | if ((errno != EACCES) && (errno != EPERM) && (errno != EISDIR)) return; |
| + | } |
| + | |
| + | if (!path[split]) { |
| + | if (savederrno) errno = savederrno; |
| + | return; |
| + | } |
| + | path += split; |
| + | path += 1; |
| + | } |
| + | } |
| + | |
| + | |
| + | /*** pmatch.c ***/ |
| + | |
| + | unsigned pmatch(const char *p, const char *s, unsigned len) { |
| + | for (;;) { |
| + | char c = *p++; |
| + | if (!c) return !len; |
| + | switch (c) { |
| + | case '*': |
| + | if (!(c = *p)) return 1; |
| + | for (;;) { |
| + | if (!len) return 0; |
| + | if (*s == c) break; |
| + | ++s; --len; |
| + | } |
| + | continue; |
| + | case '+': |
| + | if ((c = *p++) != *s) return 0; |
| + | for (;;) { |
| + | if (!len) return 1; |
| + | if (*s != c) break; |
| + | ++s; --len; |
| + | } |
| + | continue; |
| + | /* |
| + | case '?': |
| + | if (*p == '?') { |
| + | if (*s != '?') return 0; |
| + | ++p; |
| + | } |
| + | ++s; --len; |
| + | continue; |
| + | */ |
| + | default: |
| + | if (!len) return 0; |
| + | if (*s != c) return 0; |
| + | ++s; --len; |
| + | continue; |
| + | } |
| + | } |
| + | return 0; |
| + | } |
| + | |
| + | |
| + | /*** prot.c ***/ |
| + | |
| + | int prot_gid(int gid) |
| + | { |
| + | gid_t x = gid; |
| + | if (setgroups(1,&x) == -1) return -1; |
| + | return setgid(gid); /* _should_ be redundant, but on some systems it isn't */ |
| + | } |
| + | |
| + | int prot_uid(int uid) |
| + | { |
| + | return setuid(uid); |
| + | } |
| + | |
| + | |
| + | /*** readclose.c ***/ |
| + | |
| + | int readclose_append(int fd,stralloc *sa,unsigned bufsize) |
| + | { |
| + | int r; |
| + | for (;;) { |
| + | if (!stralloc_readyplus(sa,bufsize)) { close(fd); return -1; } |
| + | r = read(fd,sa->s + sa->len,bufsize); |
| + | if (r == -1) if (errno == EINTR) continue; |
| + | if (r <= 0) { close(fd); return r; } |
| + | sa->len += r; |
| + | } |
| + | } |
| + | |
| + | int readclose(int fd,stralloc *sa,unsigned bufsize) |
| + | { |
| + | if (!stralloc_copys(sa,"")) { close(fd); return -1; } |
| + | return readclose_append(fd,sa,bufsize); |
| + | } |
| + | |
| + | |
| + | /*** scan_ulong.c ***/ |
| + | |
| + | unsigned scan_ulong(const char *s,unsigned long *u) |
| + | { |
| + | unsigned pos = 0; |
| + | unsigned long result = 0; |
| + | unsigned long c; |
| + | while ((c = (unsigned long) (unsigned char) (s[pos] - '0')) < 10) { |
| + | result = result * 10 + c; |
| + | ++pos; |
| + | } |
| + | *u = result; |
| + | return pos; |
| + | } |
| + | |
| + | |
| + | /*** seek_set.c ***/ |
| + | |
| + | int seek_set(int fd,seek_pos pos) |
| + | { |
| + | if (lseek(fd,(off_t) pos,SEEK_SET) == -1) return -1; return 0; |
| + | } |
| + | |
| + | |
| + | /*** sig.c ***/ |
| + | |
| + | int sig_alarm = SIGALRM; |
| + | int sig_child = SIGCHLD; |
| + | int sig_cont = SIGCONT; |
| + | int sig_hangup = SIGHUP; |
| + | int sig_int = SIGINT; |
| + | int sig_pipe = SIGPIPE; |
| + | int sig_term = SIGTERM; |
| + | |
| + | void (*sig_defaulthandler)(int) = SIG_DFL; |
| + | void (*sig_ignorehandler)(int) = SIG_IGN; |
| + | |
| + | |
| + | /*** sig_block.c ***/ |
| + | |
| + | void sig_block(int sig) |
| + | { |
| + | sigset_t ss; |
| + | sigemptyset(&ss); |
| + | sigaddset(&ss,sig); |
| + | sigprocmask(SIG_BLOCK,&ss,(sigset_t *) 0); |
| + | } |
| + | |
| + | void sig_unblock(int sig) |
| + | { |
| + | sigset_t ss; |
| + | sigemptyset(&ss); |
| + | sigaddset(&ss,sig); |
| + | sigprocmask(SIG_UNBLOCK,&ss,(sigset_t *) 0); |
| + | } |
| + | |
| + | void sig_blocknone(void) |
| + | { |
| + | sigset_t ss; |
| + | sigemptyset(&ss); |
| + | sigprocmask(SIG_SETMASK,&ss,(sigset_t *) 0); |
| + | } |
| + | |
| + | |
| + | /*** sig_catch.c ***/ |
| + | |
| + | void sig_catch(int sig,void (*f)(int)) |
| + | { |
| + | struct sigaction sa; |
| + | sa.sa_handler = f; |
| + | sa.sa_flags = 0; |
| + | sigemptyset(&sa.sa_mask); |
| + | sigaction(sig,&sa,(struct sigaction *) 0); |
| + | } |
| + | |
| + | |
| + | /*** sig_pause.c ***/ |
| + | |
| + | void sig_pause(void) |
| + | { |
| + | sigset_t ss; |
| + | sigemptyset(&ss); |
| + | sigsuspend(&ss); |
| + | } |
| + | |
| + | |
| + | /*** str_chr.c ***/ |
| + | |
| + | unsigned str_chr(const char *s,int c) |
| + | { |
| + | char ch; |
| + | const char *t; |
| + | |
| + | ch = c; |
| + | t = s; |
| + | for (;;) { |
| + | if (!*t) break; if (*t == ch) break; ++t; |
| + | if (!*t) break; if (*t == ch) break; ++t; |
| + | if (!*t) break; if (*t == ch) break; ++t; |
| + | if (!*t) break; if (*t == ch) break; ++t; |
| + | } |
| + | return t - s; |
| + | } |
| + | |
| + | |
| + | /*** wait_nohang.c ***/ |
| + | |
| + | int wait_nohang(int *wstat) |
| + | { |
| + | return waitpid(-1,wstat,WNOHANG); |
| + | } |
| + | |
| + | |
| + | /*** wait_pid.c ***/ |
| + | |
| + | int wait_pid(int *wstat, int pid) |
| + | { |
| + | int r; |
| + | |
| + | do |
| + | r = waitpid(pid,wstat,0); |
| + | while ((r == -1) && (errno == EINTR)); |
| + | return r; |
| + | } |
| @@ -0,0 +1,403 @@ | |
| + | /* |
| + | Copyright (c) 2001-2006, Gerrit Pape |
| + | All rights reserved. |
| + | |
| + | Redistribution and use in source and binary forms, with or without |
| + | modification, are permitted provided that the following conditions are met: |
| + | |
| + | 1. Redistributions of source code must retain the above copyright notice, |
| + | this list of conditions and the following disclaimer. |
| + | 2. Redistributions in binary form must reproduce the above copyright |
| + | notice, this list of conditions and the following disclaimer in the |
| + | documentation and/or other materials provided with the distribution. |
| + | 3. The name of the author may not be used to endorse or promote products |
| + | derived from this software without specific prior written permission. |
| + | |
| + | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| + | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| + | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
| + | EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| + | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| + | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| + | OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| + | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| + | OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| + | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| + | */ |
| + | |
| + | /*** buffer.h ***/ |
| + | |
| + | typedef struct buffer { |
| + | char *x; |
| + | unsigned p; |
| + | unsigned n; |
| + | int fd; |
| + | int (*op)(int fd,char *buf,unsigned len); |
| + | } buffer; |
| + | |
| + | #define BUFFER_INIT(op,fd,buf,len) { (buf), 0, (len), (fd), (op) } |
| + | #define BUFFER_INSIZE 8192 |
| + | #define BUFFER_OUTSIZE 8192 |
| + | |
| + | extern void buffer_init(buffer *,int (*)(int fd,char *buf,unsigned len),int,char *,unsigned); |
| + | |
| + | extern int buffer_flush(buffer *); |
| + | extern int buffer_put(buffer *,const char *,unsigned); |
| + | extern int buffer_putalign(buffer *,const char *,unsigned); |
| + | extern int buffer_putflush(buffer *,const char *,unsigned); |
| + | extern int buffer_puts(buffer *,const char *); |
| + | extern int buffer_putsalign(buffer *,const char *); |
| + | extern int buffer_putsflush(buffer *,const char *); |
| + | |
| + | #define buffer_PUTC(s,c) \ |
| + | ( ((s)->n != (s)->p) \ |
| + | ? ( (s)->x[(s)->p++] = (c), 0 ) \ |
| + | : buffer_put((s),&(c),1) \ |
| + | ) |
| + | |
| + | extern int buffer_get(buffer *,char *,unsigned); |
| + | extern int buffer_bget(buffer *,char *,unsigned); |
| + | extern int buffer_feed(buffer *); |
| + | |
| + | extern char *buffer_peek(buffer *); |
| + | extern void buffer_seek(buffer *,unsigned); |
| + | |
| + | #define buffer_PEEK(s) ( (s)->x + (s)->n ) |
| + | #define buffer_SEEK(s,len) ( ( (s)->p -= (len) ) , ( (s)->n += (len) ) ) |
| + | |
| + | #define buffer_GETC(s,c) \ |
| + | ( ((s)->p > 0) \ |
| + | ? ( *(c) = (s)->x[(s)->n], buffer_SEEK((s),1), 1 ) \ |
| + | : buffer_get((s),(c),1) \ |
| + | ) |
| + | |
| + | extern int buffer_copy(buffer *,buffer *); |
| + | |
| + | extern int buffer_unixread(int,char *,unsigned); |
| + | /* Actually, int buffer_unixwrite(int,const char *,unsigned), |
| + | but that 'const' will produce warnings... oh well */ |
| + | extern int buffer_unixwrite(int,char *,unsigned); |
| + | |
| + | |
| + | /*** byte.h ***/ |
| + | |
| + | extern unsigned byte_chr(char *s,unsigned n,int c); |
| + | |
| + | |
| + | /*** coe.h ***/ |
| + | |
| + | extern int coe(int); |
| + | |
| + | |
| + | /*** direntry.h ***/ |
| + | |
| + | #define direntry struct dirent |
| + | |
| + | |
| + | /*** fd.h ***/ |
| + | |
| + | extern int fd_copy(int,int); |
| + | extern int fd_move(int,int); |
| + | |
| + | |
| + | /*** fifo.h ***/ |
| + | |
| + | extern int fifo_make(const char *,int); |
| + | |
| + | |
| + | /*** fmt.h ***/ |
| + | |
| + | #define FMT_ULONG 40 /* enough space to hold 2^128 - 1 in decimal, plus \0 */ |
| + | #define FMT_LEN ((char *) 0) /* convenient abbreviation */ |
| + | |
| + | extern unsigned fmt_uint(char *,unsigned); |
| + | extern unsigned fmt_uint0(char *,unsigned,unsigned); |
| + | extern unsigned fmt_xint(char *,unsigned); |
| + | extern unsigned fmt_nbbint(char *,unsigned,unsigned,unsigned,unsigned); |
| + | extern unsigned fmt_ushort(char *,unsigned short); |
| + | extern unsigned fmt_xshort(char *,unsigned short); |
| + | extern unsigned fmt_nbbshort(char *,unsigned,unsigned,unsigned,unsigned short); |
| + | extern unsigned fmt_ulong(char *,unsigned long); |
| + | extern unsigned fmt_xlong(char *,unsigned long); |
| + | extern unsigned fmt_nbblong(char *,unsigned,unsigned,unsigned,unsigned long); |
| + | |
| + | extern unsigned fmt_plusminus(char *,int); |
| + | extern unsigned fmt_minus(char *,int); |
| + | extern unsigned fmt_0x(char *,int); |
| + | |
| + | extern unsigned fmt_str(char *,const char *); |
| + | extern unsigned fmt_strn(char *,const char *,unsigned); |
| + | |
| + | |
| + | /*** tai.h ***/ |
| + | |
| + | struct tai { |
| + | uint64_t x; |
| + | } ; |
| + | |
| + | #define tai_unix(t,u) ((void) ((t)->x = 4611686018427387914ULL + (uint64_t) (u))) |
| + | |
| + | extern void tai_now(struct tai *); |
| + | |
| + | #define tai_approx(t) ((double) ((t)->x)) |
| + | |
| + | extern void tai_add(struct tai *,const struct tai *,const struct tai *); |
| + | extern void tai_sub(struct tai *,const struct tai *,const struct tai *); |
| + | #define tai_less(t,u) ((t)->x < (u)->x) |
| + | |
| + | #define TAI_PACK 8 |
| + | extern void tai_pack(char *,const struct tai *); |
| + | extern void tai_unpack(const char *,struct tai *); |
| + | |
| + | extern void tai_uint(struct tai *,unsigned); |
| + | |
| + | |
| + | /*** taia.h ***/ |
| + | |
| + | struct taia { |
| + | struct tai sec; |
| + | unsigned long nano; /* 0...999999999 */ |
| + | unsigned long atto; /* 0...999999999 */ |
| + | } ; |
| + | |
| + | extern void taia_tai(const struct taia *,struct tai *); |
| + | |
| + | extern void taia_now(struct taia *); |
| + | |
| + | extern double taia_approx(const struct taia *); |
| + | extern double taia_frac(const struct taia *); |
| + | |
| + | extern void taia_add(struct taia *,const struct taia *,const struct taia *); |
| + | extern void taia_addsec(struct taia *,const struct taia *,int); |
| + | extern void taia_sub(struct taia *,const struct taia *,const struct taia *); |
| + | extern void taia_half(struct taia *,const struct taia *); |
| + | extern int taia_less(const struct taia *,const struct taia *); |
| + | |
| + | #define TAIA_PACK 16 |
| + | extern void taia_pack(char *,const struct taia *); |
| + | extern void taia_unpack(const char *,struct taia *); |
| + | |
| + | #define TAIA_FMTFRAC 19 |
| + | extern unsigned taia_fmtfrac(char *,const struct taia *); |
| + | |
| + | extern void taia_uint(struct taia *,unsigned); |
| + | |
| + | |
| + | /*** fmt_ptime.h ***/ |
| + | |
| + | #define FMT_PTIME 30 |
| + | |
| + | extern unsigned fmt_ptime(char *, struct taia *); |
| + | extern unsigned fmt_taia(char *, struct taia *); |
| + | |
| + | |
| + | /*** gen_alloc.h ***/ |
| + | |
| + | #define GEN_ALLOC_typedef(ta,type,field,len,a) \ |
| + | typedef struct ta { type *field; unsigned len; unsigned a; } ta; |
| + | |
| + | |
| + | /*** gen_allocdefs.h ***/ |
| + | |
| + | #define GEN_ALLOC_ready(ta,type,field,len,a,i,n,x,base,ta_ready) \ |
| + | int ta_ready(ta *x,unsigned n) \ |
| + | { unsigned i; \ |
| + | if (x->field) { \ |
| + | i = x->a; \ |
| + | if (n > i) { \ |
| + | x->a = base + n + (n >> 3); \ |
| + | x->field = realloc(x->field,x->a * sizeof(type)); \ |
| + | if (x->field) return 1; \ |
| + | x->a = i; return 0; } \ |
| + | return 1; } \ |
| + | x->len = 0; \ |
| + | return !!(x->field = malloc((x->a = n) * sizeof(type))); } |
| + | |
| + | #define GEN_ALLOC_readyplus(ta,type,field,len,a,i,n,x,base,ta_rplus) \ |
| + | int ta_rplus(ta *x,unsigned n) \ |
| + | { unsigned i; \ |
| + | if (x->field) { \ |
| + | i = x->a; n += x->len; \ |
| + | if (n > i) { \ |
| + | x->a = base + n + (n >> 3); \ |
| + | x->field = realloc(x->field,x->a * sizeof(type)); \ |
| + | if (x->field) return 1; \ |
| + | x->a = i; return 0; } \ |
| + | return 1; } \ |
| + | x->len = 0; \ |
| + | return !!(x->field = malloc((x->a = n) * sizeof(type))); } |
| + | |
| + | #define GEN_ALLOC_append(ta,type,field,len,a,i,n,x,base,ta_rplus,ta_append) \ |
| + | int ta_append(ta *x,const type *i) \ |
| + | { if (!ta_rplus(x,1)) return 0; x->field[x->len++] = *i; return 1; } |
| + | |
| + | |
| + | /*** stralloc.h ***/ |
| + | |
| + | GEN_ALLOC_typedef(stralloc,char,s,len,a) |
| + | |
| + | extern int stralloc_ready(stralloc *,unsigned); |
| + | extern int stralloc_readyplus(stralloc *,unsigned); |
| + | extern int stralloc_copy(stralloc *,const stralloc *); |
| + | extern int stralloc_cat(stralloc *,const stralloc *); |
| + | extern int stralloc_copys(stralloc *,const char *); |
| + | extern int stralloc_cats(stralloc *,const char *); |
| + | extern int stralloc_copyb(stralloc *,const char *,unsigned); |
| + | extern int stralloc_catb(stralloc *,const char *,unsigned); |
| + | extern int stralloc_append(stralloc *,const char *); /* beware: this takes a pointer to 1 char */ |
| + | extern int stralloc_starts(stralloc *,const char *); |
| + | |
| + | #define stralloc_0(sa) stralloc_append(sa,"") |
| + | |
| + | extern int stralloc_catulong0(stralloc *,unsigned long,unsigned); |
| + | extern int stralloc_catlong0(stralloc *,long,unsigned); |
| + | |
| + | #define stralloc_catlong(sa,l) (stralloc_catlong0((sa),(l),0)) |
| + | #define stralloc_catuint0(sa,i,n) (stralloc_catulong0((sa),(i),(n))) |
| + | #define stralloc_catint0(sa,i,n) (stralloc_catlong0((sa),(i),(n))) |
| + | #define stralloc_catint(sa,i) (stralloc_catlong0((sa),(i),0)) |
| + | |
| + | |
| + | /*** iopause.h ***/ |
| + | |
| + | typedef struct pollfd iopause_fd; |
| + | #define IOPAUSE_READ POLLIN |
| + | #define IOPAUSE_WRITE POLLOUT |
| + | |
| + | extern void iopause(iopause_fd *,unsigned,struct taia *,struct taia *); |
| + | |
| + | |
| + | /*** lock.h ***/ |
| + | |
| + | extern int lock_ex(int); |
| + | extern int lock_un(int); |
| + | extern int lock_exnb(int); |
| + | |
| + | |
| + | /*** ndelay.h ***/ |
| + | |
| + | extern int ndelay_on(int); |
| + | extern int ndelay_off(int); |
| + | |
| + | |
| + | /*** open.h ***/ |
| + | |
| + | extern int open_read(const char *); |
| + | extern int open_excl(const char *); |
| + | extern int open_append(const char *); |
| + | extern int open_trunc(const char *); |
| + | extern int open_write(const char *); |
| + | |
| + | |
| + | /*** openreadclose.h ***/ |
| + | |
| + | extern int openreadclose(const char *,stralloc *,unsigned); |
| + | |
| + | |
| + | /*** pathexec.h ***/ |
| + | |
| + | extern void pathexec_run(const char *,char *const *,char *const *); |
| + | extern int pathexec_env(const char *,const char *); |
| + | extern void pathexec(char **); |
| + | |
| + | |
| + | /*** pmatch.h ***/ |
| + | |
| + | extern unsigned pmatch(const char *, const char *, unsigned); |
| + | |
| + | |
| + | /*** prot.h ***/ |
| + | |
| + | extern int prot_gid(int); |
| + | extern int prot_uid(int); |
| + | |
| + | |
| + | /*** readclose.h ***/ |
| + | |
| + | extern int readclose_append(int,stralloc *,unsigned); |
| + | extern int readclose(int,stralloc *,unsigned); |
| + | |
| + | |
| + | /*** scan.h ***/ |
| + | |
| + | extern unsigned scan_uint(const char *,unsigned *); |
| + | extern unsigned scan_xint(const char *,unsigned *); |
| + | extern unsigned scan_nbbint(const char *,unsigned,unsigned,unsigned,unsigned *); |
| + | extern unsigned scan_ushort(const char *,unsigned short *); |
| + | extern unsigned scan_xshort(const char *,unsigned short *); |
| + | extern unsigned scan_nbbshort(const char *,unsigned,unsigned,unsigned,unsigned short *); |
| + | extern unsigned scan_ulong(const char *,unsigned long *); |
| + | extern unsigned scan_xlong(const char *,unsigned long *); |
| + | extern unsigned scan_nbblong(const char *,unsigned,unsigned,unsigned,unsigned long *); |
| + | |
| + | extern unsigned scan_plusminus(const char *,int *); |
| + | extern unsigned scan_0x(const char *,unsigned *); |
| + | |
| + | extern unsigned scan_whitenskip(const char *,unsigned); |
| + | extern unsigned scan_nonwhitenskip(const char *,unsigned); |
| + | extern unsigned scan_charsetnskip(const char *,const char *,unsigned); |
| + | extern unsigned scan_noncharsetnskip(const char *,const char *,unsigned); |
| + | |
| + | extern unsigned scan_strncmp(const char *,const char *,unsigned); |
| + | extern unsigned scan_memcmp(const char *,const char *,unsigned); |
| + | |
| + | extern unsigned scan_long(const char *,long *); |
| + | extern unsigned scan_8long(const char *,unsigned long *); |
| + | |
| + | |
| + | /*** seek.h ***/ |
| + | |
| + | typedef unsigned long seek_pos; |
| + | |
| + | extern seek_pos seek_cur(int); |
| + | |
| + | extern int seek_set(int,seek_pos); |
| + | extern int seek_end(int); |
| + | |
| + | extern int seek_trunc(int,seek_pos); |
| + | |
| + | #define seek_begin(fd) (seek_set((fd),(seek_pos) 0)) |
| + | |
| + | |
| + | /*** sig.h ***/ |
| + | |
| + | extern int sig_alarm; |
| + | extern int sig_child; |
| + | extern int sig_cont; |
| + | extern int sig_hangup; |
| + | extern int sig_int; |
| + | extern int sig_pipe; |
| + | extern int sig_term; |
| + | |
| + | extern void (*sig_defaulthandler)(int); |
| + | extern void (*sig_ignorehandler)(int); |
| + | |
| + | extern void sig_catch(int,void (*)(int)); |
| + | #define sig_ignore(s) (sig_catch((s),sig_ignorehandler)) |
| + | #define sig_uncatch(s) (sig_catch((s),sig_defaulthandler)) |
| + | |
| + | extern void sig_block(int); |
| + | extern void sig_unblock(int); |
| + | extern void sig_blocknone(void); |
| + | extern void sig_pause(void); |
| + | |
| + | extern void sig_dfl(int); |
| + | |
| + | |
| + | /*** str.h ***/ |
| + | |
| + | extern unsigned str_chr(const char *,int); /* never returns NULL */ |
| + | |
| + | #define str_diff(s,t) strcmp((s),(t)) |
| + | #define str_equal(s,t) (!strcmp((s),(t))) |
| + | |
| + | |
| + | /*** wait.h ***/ |
| + | |
| + | extern int wait_pid(int *wstat, int pid); |
| + | extern int wait_nohang(int *wstat); |
| + | |
| + | #define wait_crashed(w) ((w) & 127) |
| + | #define wait_exitcode(w) ((w) >> 8) |
| + | #define wait_stopsig(w) ((w) >> 8) |
| + | #define wait_stopped(w) (((w) & 127) == 127) |
| @@ -0,0 +1,880 @@ | |
| + | /* |
| + | Copyright (c) 2001-2006, Gerrit Pape |
| + | All rights reserved. |
| + | |
| + | Redistribution and use in source and binary forms, with or without |
| + | modification, are permitted provided that the following conditions are met: |
| + | |
| + | 1. Redistributions of source code must retain the above copyright notice, |
| + | this list of conditions and the following disclaimer. |
| + | 2. Redistributions in binary form must reproduce the above copyright |
| + | notice, this list of conditions and the following disclaimer in the |
| + | documentation and/or other materials provided with the distribution. |
| + | 3. The name of the author may not be used to endorse or promote products |
| + | derived from this software without specific prior written permission. |
| + | |
| + | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| + | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| + | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
| + | EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| + | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| + | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| + | OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| + | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| + | OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| + | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| + | */ |
| + | |
| + | /* Busyboxed by Denis Vlasenko <[email protected]> */ |
| + | /* TODO: depends on runit_lib.c - review and reduce/eliminate */ |
| + | |
| + | #include <sys/poll.h> |
| + | #include <sys/file.h> |
| + | #include "busybox.h" |
| + | #include "runit_lib.h" |
| + | |
| + | static unsigned verbose; |
| + | static int linemax = 1000; |
| + | static int buflen = 1024; |
| + | static int linelen; |
| + | |
| + | static char **fndir; |
| + | static int fdwdir; |
| + | static int wstat; |
| + | static struct taia trotate; |
| + | |
| + | static char *line; |
| + | static unsigned exitasap; |
| + | static unsigned rotateasap; |
| + | static unsigned reopenasap; |
| + | static unsigned linecomplete = 1; |
| + | static unsigned tmaxflag; |
| + | static iopause_fd in; |
| + | |
| + | static const char *replace = ""; |
| + | static char repl; |
| + | |
| + | static struct logdir { |
| + | char *btmp; |
| + | /* pattern list to match, in "aa\0bb\0\cc\0\0" form */ |
| + | char *inst; |
| + | char *processor; |
| + | char *name; |
| + | unsigned size; |
| + | unsigned sizemax; |
| + | unsigned nmax; |
| + | unsigned nmin; |
| + | /* int (not long) because of taia_uint() usage: */ |
| + | unsigned tmax; |
| + | int ppid; |
| + | int fddir; |
| + | int fdcur; |
| + | int fdlock; |
| + | struct taia trotate; |
| + | char fnsave[FMT_PTIME]; |
| + | char match; |
| + | char matcherr; |
| + | } *dir; |
| + | static unsigned dirn = 0; |
| + | |
| + | #define FATAL "fatal: " |
| + | #define WARNING "warning: " |
| + | #define PAUSE "pausing: " |
| + | #define INFO "info: " |
| + | |
| + | #define usage() bb_show_usage() |
| + | static void fatalx(char *m0) |
| + | { |
| + | bb_error_msg_and_die(FATAL"%s", m0); |
| + | } |
| + | static void warn(char *m0) { |
| + | bb_perror_msg(WARNING"%s", m0); |
| + | } |
| + | static void warn2(char *m0, char *m1) |
| + | { |
| + | bb_perror_msg(WARNING"%s: %s", m0, m1); |
| + | } |
| + | static void warnx(char *m0, char *m1) |
| + | { |
| + | bb_error_msg(WARNING"%s: %s", m0, m1); |
| + | } |
| + | static void pause_nomem(void) |
| + | { |
| + | bb_error_msg(PAUSE"out of memory"); sleep(3); |
| + | } |
| + | static void pause1cannot(char *m0) |
| + | { |
| + | bb_perror_msg(PAUSE"cannot %s", m0); sleep(3); |
| + | } |
| + | static void pause2cannot(char *m0, char *m1) |
| + | { |
| + | bb_perror_msg(PAUSE"cannot %s %s", m0, m1); |
| + | sleep(3); |
| + | } |
| + | |
| + | static char* wstrdup(const char *str) |
| + | { |
| + | char *s; |
| + | while (!(s = strdup(str))) pause_nomem(); |
| + | return s; |
| + | } |
| + | |
| + | static unsigned processorstart(struct logdir *ld) |
| + | { |
| + | int pid; |
| + | |
| + | if (!ld->processor) return 0; |
| + | if (ld->ppid) { |
| + | warnx("processor already running", ld->name); |
| + | return 0; |
| + | } |
| + | while ((pid = fork()) == -1) |
| + | pause2cannot("fork for processor", ld->name); |
| + | if (!pid) { |
| + | char *prog[4]; |
| + | int fd; |
| + | |
| + | /* child */ |
| + | sig_uncatch(sig_term); |
| + | sig_uncatch(sig_alarm); |
| + | sig_uncatch(sig_hangup); |
| + | sig_unblock(sig_term); |
| + | sig_unblock(sig_alarm); |
| + | sig_unblock(sig_hangup); |
| + | |
| + | if (verbose) |
| + | bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave); |
| + | fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY); |
| + | if (fd_move(0, fd) == -1) |
| + | bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name); |
| + | ld->fnsave[26] = 't'; |
| + | fd = xopen3(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT, 0644); |
| + | if (fd_move(1, fd) == -1) |
| + | bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name); |
| + | fd = open_read("state"); |
| + | if (fd == -1) { |
| + | if (errno != ENOENT) |
| + | bb_perror_msg_and_die(FATAL"cannot %s processor %s", "open state for", ld->name); |
| + | close(xopen3("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT, 0644)); |
| + | fd = xopen("state", O_RDONLY|O_NDELAY); |
| + | } |
| + | if (fd_move(4, fd) == -1) |
| + | bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name); |
| + | fd = xopen3("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT, 0644); |
| + | if (fd_move(5, fd) == -1) |
| + | bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name); |
| + | |
| + | prog[0] = "sh"; |
| + | prog[1] = "-c"; |
| + | prog[2] = ld->processor; |
| + | prog[3] = '\0'; |
| + | execve("/bin/sh", prog, environ); |
| + | bb_perror_msg_and_die(FATAL"cannot %s processor %s", "run", ld->name); |
| + | } |
| + | ld->ppid = pid; |
| + | return 1; |
| + | } |
| + | |
| + | static unsigned processorstop(struct logdir *ld) |
| + | { |
| + | char f[28]; |
| + | |
| + | if (ld->ppid) { |
| + | sig_unblock(sig_hangup); |
| + | while (wait_pid(&wstat, ld->ppid) == -1) |
| + | pause2cannot("wait for processor", ld->name); |
| + | sig_block(sig_hangup); |
| + | ld->ppid = 0; |
| + | } |
| + | if (ld->fddir == -1) return 1; |
| + | while (fchdir(ld->fddir) == -1) |
| + | pause2cannot("change directory, want processor", ld->name); |
| + | if (wait_exitcode(wstat) != 0) { |
| + | warnx("processor failed, restart", ld->name); |
| + | ld->fnsave[26] = 't'; |
| + | unlink(ld->fnsave); |
| + | ld->fnsave[26] = 'u'; |
| + | processorstart(ld); |
| + | while (fchdir(fdwdir) == -1) |
| + | pause1cannot("change to initial working directory"); |
| + | return ld->processor ? 0 : 1; |
| + | } |
| + | ld->fnsave[26] = 't'; |
| + | memcpy(f, ld->fnsave, 26); |
| + | f[26] = 's'; |
| + | f[27] = '\0'; |
| + | while (rename(ld->fnsave, f) == -1) |
| + | pause2cannot("rename processed", ld->name); |
| + | while (chmod(f, 0744) == -1) |
| + | pause2cannot("set mode of processed", ld->name); |
| + | ld->fnsave[26] = 'u'; |
| + | if (unlink(ld->fnsave) == -1) |
| + | bb_error_msg(WARNING"cannot unlink: %s/%s", ld->name, ld->fnsave); |
| + | while (rename("newstate", "state") == -1) |
| + | pause2cannot("rename state", ld->name); |
| + | if (verbose) bb_error_msg(INFO"processed: %s/%s", ld->name, f); |
| + | while (fchdir(fdwdir) == -1) |
| + | pause1cannot("change to initial working directory"); |
| + | return 1; |
| + | } |
| + | |
| + | static void rmoldest(struct logdir *ld) |
| + | { |
| + | DIR *d; |
| + | struct dirent *f; |
| + | char oldest[FMT_PTIME]; |
| + | int n = 0; |
| + | |
| + | oldest[0] = 'A'; oldest[1] = oldest[27] = 0; |
| + | while (!(d = opendir("."))) |
| + | pause2cannot("open directory, want rotate", ld->name); |
| + | errno = 0; |
| + | while ((f = readdir(d))) { |
| + | if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) { |
| + | if (f->d_name[26] == 't') { |
| + | if (unlink(f->d_name) == -1) |
| + | warn2("cannot unlink processor leftover", f->d_name); |
| + | } else { |
| + | ++n; |
| + | if (strcmp(f->d_name, oldest) < 0) |
| + | memcpy(oldest, f->d_name, 27); |
| + | } |
| + | errno = 0; |
| + | } |
| + | } |
| + | if (errno) warn2("cannot read directory", ld->name); |
| + | closedir(d); |
| + | |
| + | if (ld->nmax && (n > ld->nmax)) { |
| + | if (verbose) bb_error_msg(INFO"delete: %s/%s", ld->name, oldest); |
| + | if ((*oldest == '@') && (unlink(oldest) == -1)) |
| + | warn2("cannot unlink oldest logfile", ld->name); |
| + | } |
| + | } |
| + | |
| + | static unsigned rotate(struct logdir *ld) |
| + | { |
| + | struct stat st; |
| + | struct taia now; |
| + | |
| + | if (ld->fddir == -1) { |
| + | ld->tmax = 0; |
| + | return 0; |
| + | } |
| + | if (ld->ppid) |
| + | while(!processorstop(ld)) |
| + | /* wait */; |
| + | |
| + | while (fchdir(ld->fddir) == -1) |
| + | pause2cannot("change directory, want rotate", ld->name); |
| + | |
| + | /* create new filename */ |
| + | ld->fnsave[25] = '.'; |
| + | ld->fnsave[26] = 's'; |
| + | if (ld->processor) |
| + | ld->fnsave[26] = 'u'; |
| + | ld->fnsave[27] = '\0'; |
| + | do { |
| + | taia_now(&now); |
| + | fmt_taia(ld->fnsave, &now); |
| + | errno = 0; |
| + | } while ((stat(ld->fnsave, &st) != -1) || (errno != ENOENT)); |
| + | |
| + | if (ld->tmax && taia_less(&ld->trotate, &now)) { |
| + | taia_uint(&ld->trotate, ld->tmax); |
| + | taia_add(&ld->trotate, &now, &ld->trotate); |
| + | if (taia_less(&ld->trotate, &trotate)) |
| + | trotate = ld->trotate; |
| + | } |
| + | |
| + | if (ld->size > 0) { |
| + | while (fsync(ld->fdcur) == -1) |
| + | pause2cannot("fsync current logfile", ld->name); |
| + | while (fchmod(ld->fdcur, 0744) == -1) |
| + | pause2cannot("set mode of current", ld->name); |
| + | close(ld->fdcur); |
| + | if (verbose) { |
| + | bb_error_msg(INFO"rename: %s/current %s %u", ld->name, |
| + | ld->fnsave, ld->size); |
| + | } |
| + | while (rename("current", ld->fnsave) == -1) |
| + | pause2cannot("rename current", ld->name); |
| + | while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1) |
| + | pause2cannot("create new current", ld->name); |
| + | coe(ld->fdcur); |
| + | ld->size = 0; |
| + | while (fchmod(ld->fdcur, 0644) == -1) |
| + | pause2cannot("set mode of current", ld->name); |
| + | rmoldest(ld); |
| + | processorstart(ld); |
| + | } |
| + | |
| + | while (fchdir(fdwdir) == -1) |
| + | pause1cannot("change to initial working directory"); |
| + | return 1; |
| + | } |
| + | |
| + | static int buffer_pwrite(int n, char *s, unsigned len) |
| + | { |
| + | int i; |
| + | struct logdir *ld = &dir[n]; |
| + | |
| + | if (ld->sizemax) { |
| + | if (ld->size >= ld->sizemax) |
| + | rotate(ld); |
| + | if (len > (ld->sizemax - ld->size)) |
| + | len = ld->sizemax - ld->size; |
| + | } |
| + | while ((i = write(ld->fdcur, s, len)) == -1) { |
| + | if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) { |
| + | DIR *d; |
| + | struct dirent *f; |
| + | char oldest[FMT_PTIME]; |
| + | int j = 0; |
| + | |
| + | while (fchdir(ld->fddir) == -1) |
| + | pause2cannot("change directory, want remove old logfile", |
| + | ld->name); |
| + | oldest[0] = 'A'; |
| + | oldest[1] = oldest[27] = '\0'; |
| + | while (!(d = opendir("."))) |
| + | pause2cannot("open directory, want remove old logfile", |
| + | ld->name); |
| + | errno = 0; |
| + | while ((f = readdir(d))) |
| + | if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) { |
| + | ++j; |
| + | if (strcmp(f->d_name, oldest) < 0) |
| + | memcpy(oldest, f->d_name, 27); |
| + | } |
| + | if (errno) warn2("cannot read directory, want remove old logfile", |
| + | ld->name); |
| + | closedir(d); |
| + | errno = ENOSPC; |
| + | if (j > ld->nmin) { |
| + | if (*oldest == '@') { |
| + | bb_error_msg(WARNING"out of disk space, delete: %s/%s", |
| + | ld->name, oldest); |
| + | errno = 0; |
| + | if (unlink(oldest) == -1) { |
| + | warn2("cannot unlink oldest logfile", ld->name); |
| + | errno = ENOSPC; |
| + | } |
| + | while (fchdir(fdwdir) == -1) |
| + | pause1cannot("change to initial working directory"); |
| + | } |
| + | } |
| + | } |
| + | if (errno) pause2cannot("write to current", ld->name); |
| + | } |
| + | |
| + | ld->size += i; |
| + | if (ld->sizemax) |
| + | if (s[i-1] == '\n') |
| + | if (ld->size >= (ld->sizemax - linemax)) |
| + | rotate(ld); |
| + | return i; |
| + | } |
| + | |
| + | static void logdir_close(struct logdir *ld) |
| + | { |
| + | if (ld->fddir == -1) |
| + | return; |
| + | if (verbose) |
| + | bb_error_msg(INFO"close: %s", ld->name); |
| + | close(ld->fddir); |
| + | ld->fddir = -1; |
| + | if (ld->fdcur == -1) |
| + | return; /* impossible */ |
| + | while (fsync(ld->fdcur) == -1) |
| + | pause2cannot("fsync current logfile", ld->name); |
| + | while (fchmod(ld->fdcur, 0744) == -1) |
| + | pause2cannot("set mode of current", ld->name); |
| + | close(ld->fdcur); |
| + | ld->fdcur = -1; |
| + | if (ld->fdlock == -1) |
| + | return; /* impossible */ |
| + | close(ld->fdlock); |
| + | ld->fdlock = -1; |
| + | free(ld->processor); |
| + | ld->processor = NULL; |
| + | } |
| + | |
| + | static unsigned logdir_open(struct logdir *ld, const char *fn) |
| + | { |
| + | char buf[128]; |
| + | struct taia now; |
| + | char *new, *s, *np; |
| + | int i; |
| + | struct stat st; |
| + | |
| + | ld->fddir = open(fn, O_RDONLY|O_NDELAY); |
| + | if (ld->fddir == -1) { |
| + | warn2("cannot open log directory", (char*)fn); |
| + | return 0; |
| + | } |
| + | coe(ld->fddir); |
| + | if (fchdir(ld->fddir) == -1) { |
| + | logdir_close(ld); |
| + | warn2("cannot change directory", (char*)fn); |
| + | return 0; |
| + | } |
| + | ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600); |
| + | if ((ld->fdlock == -1) |
| + | || (lock_exnb(ld->fdlock) == -1) |
| + | ) { |
| + | logdir_close(ld); |
| + | warn2("cannot lock directory", (char*)fn); |
| + | while (fchdir(fdwdir) == -1) |
| + | pause1cannot("change to initial working directory"); |
| + | return 0; |
| + | } |
| + | coe(ld->fdlock); |
| + | |
| + | ld->size = 0; |
| + | ld->sizemax = 1000000; |
| + | ld->nmax = ld->nmin = 10; |
| + | ld->tmax = 0; |
| + | ld->name = (char*)fn; |
| + | ld->ppid = 0; |
| + | ld->match = '+'; |
| + | free(ld->inst); ld->inst = NULL; |
| + | free(ld->processor); ld->processor = NULL; |
| + | |
| + | /* read config */ |
| + | i = open_read_close("config", buf, sizeof(buf)); |
| + | if (i < 0) |
| + | warn2("cannot read config", ld->name); |
| + | if (i > 0) { |
| + | if (verbose) bb_error_msg(INFO"read: %s/config", ld->name); |
| + | s = buf; |
| + | while (s) { |
| + | np = strchr(s, '\n'); |
| + | if (np) *np++ = '\0'; |
| + | switch (s[0]) { |
| + | case '+': |
| + | case '-': |
| + | case 'e': |
| + | case 'E': |
| + | while (1) { |
| + | int l = asprintf(&new, "%s%s\n", ld->inst?:"", s); |
| + | if (l >= 0 && new) break; |
| + | pause_nomem(); |
| + | } |
| + | free(ld->inst); |
| + | ld->inst = new; |
| + | break; |
| + | case 's': { |
| + | static const struct suffix_mult km_suffixes[] = { |
| + | { "k", 1024 }, |
| + | { "m", 1024*1024 }, |
| + | { NULL, 0 } |
| + | }; |
| + | ld->sizemax = xatou_sfx(&s[1], km_suffixes); |
| + | break; |
| + | } |
| + | case 'n': |
| + | ld->nmax = xatoi_u(&s[1]); |
| + | break; |
| + | case 'N': |
| + | ld->nmin = xatoi_u(&s[1]); |
| + | break; |
| + | case 't': { |
| + | static const struct suffix_mult mh_suffixes[] = { |
| + | { "m", 60 }, |
| + | { "h", 60*60 }, |
| + | /*{ "d", 24*60*60 },*/ |
| + | { NULL, 0 } |
| + | }; |
| + | ld->tmax = xatou_sfx(&s[1], mh_suffixes); |
| + | if (ld->tmax) { |
| + | taia_uint(&ld->trotate, ld->tmax); |
| + | taia_add(&ld->trotate, &now, &ld->trotate); |
| + | if (!tmaxflag || taia_less(&ld->trotate, &trotate)) |
| + | trotate = ld->trotate; |
| + | tmaxflag = 1; |
| + | } |
| + | break; |
| + | } |
| + | case '!': |
| + | if (s[1]) { |
| + | free(ld->processor); |
| + | ld->processor = wstrdup(s); |
| + | } |
| + | break; |
| + | } |
| + | s = np; |
| + | } |
| + | /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */ |
| + | s = ld->inst; |
| + | while (s) { |
| + | np = strchr(s, '\n'); |
| + | if (np) *np++ = '\0'; |
| + | s = np; |
| + | } |
| + | } |
| + | |
| + | /* open current */ |
| + | i = stat("current", &st); |
| + | if (i != -1) { |
| + | if (st.st_size && ! (st.st_mode & S_IXUSR)) { |
| + | ld->fnsave[25] = '.'; |
| + | ld->fnsave[26] = 'u'; |
| + | ld->fnsave[27] = '\0'; |
| + | do { |
| + | taia_now(&now); |
| + | fmt_taia(ld->fnsave, &now); |
| + | errno = 0; |
| + | } while ((stat(ld->fnsave, &st) != -1) || (errno != ENOENT)); |
| + | while (rename("current", ld->fnsave) == -1) |
| + | pause2cannot("rename current", ld->name); |
| + | rmoldest(ld); |
| + | i = -1; |
| + | } else { |
| + | /* Be paranoid: st.st_size can be not just bigger, but WIDER! */ |
| + | /* (bug in original svlogd. remove this comment when fixed there) */ |
| + | ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size; |
| + | } |
| + | } else { |
| + | if (errno != ENOENT) { |
| + | logdir_close(ld); |
| + | warn2("cannot stat current", ld->name); |
| + | while (fchdir(fdwdir) == -1) |
| + | pause1cannot("change to initial working directory"); |
| + | return 0; |
| + | } |
| + | } |
| + | while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1) |
| + | pause2cannot("open current", ld->name); |
| + | coe(ld->fdcur); |
| + | while (fchmod(ld->fdcur, 0644) == -1) |
| + | pause2cannot("set mode of current", ld->name); |
| + | |
| + | if (verbose) { |
| + | if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name); |
| + | else bb_error_msg(INFO"new: %s/current", ld->name); |
| + | } |
| + | |
| + | while (fchdir(fdwdir) == -1) |
| + | pause1cannot("change to initial working directory"); |
| + | return 1; |
| + | } |
| + | |
| + | static void logdirs_reopen(void) |
| + | { |
| + | struct taia now; |
| + | int l; |
| + | int ok = 0; |
| + | |
| + | tmaxflag = 0; |
| + | taia_now(&now); |
| + | for (l = 0; l < dirn; ++l) { |
| + | logdir_close(&dir[l]); |
| + | if (logdir_open(&dir[l], fndir[l])) ok = 1; |
| + | } |
| + | if (!ok) fatalx("no functional log directories"); |
| + | } |
| + | |
| + | /* Used for reading stdin */ |
| + | static int buffer_pread(int fd, char *s, unsigned len) |
| + | { |
| + | struct taia now; |
| + | int i; |
| + | |
| + | if (rotateasap) { |
| + | for (i = 0; i < dirn; ++i) |
| + | rotate(dir+i); |
| + | rotateasap = 0; |
| + | } |
| + | if (exitasap) { |
| + | if (linecomplete) |
| + | return 0; |
| + | len = 1; |
| + | } |
| + | if (reopenasap) { |
| + | logdirs_reopen(); |
| + | reopenasap = 0; |
| + | } |
| + | taia_now(&now); |
| + | taia_uint(&trotate, 2744); |
| + | taia_add(&trotate, &now, &trotate); |
| + | for (i = 0; i < dirn; ++i) |
| + | if (dir[i].tmax) { |
| + | if (taia_less(&dir[i].trotate, &now)) |
| + | rotate(dir+i); |
| + | if (taia_less(&dir[i].trotate, &trotate)) |
| + | trotate = dir[i].trotate; |
| + | } |
| + | |
| + | while (1) { |
| + | /* Comment? */ |
| + | sig_unblock(sig_term); |
| + | sig_unblock(sig_child); |
| + | sig_unblock(sig_alarm); |
| + | sig_unblock(sig_hangup); |
| + | iopause(&in, 1, &trotate, &now); |
| + | sig_block(sig_term); |
| + | sig_block(sig_child); |
| + | sig_block(sig_alarm); |
| + | sig_block(sig_hangup); |
| + | i = safe_read(fd, s, len); |
| + | if (i >= 0) break; |
| + | if (errno != EAGAIN) { |
| + | warn("cannot read standard input"); |
| + | break; |
| + | } |
| + | /* else: EAGAIN - normal, repeat silently */ |
| + | } |
| + | |
| + | if (i > 0) { |
| + | int cnt; |
| + | linecomplete = (s[i-1] == '\n'); |
| + | if (!repl) return i; |
| + | |
| + | cnt = i; |
| + | while (--cnt >= 0) { |
| + | char ch = *s; |
| + | if (ch != '\n') { |
| + | if (ch < 32 || ch > 126) |
| + | *s = repl; |
| + | else { |
| + | int j; |
| + | for (j = 0; replace[j]; ++j) { |
| + | if (ch == replace[j]) { |
| + | *s = repl; |
| + | break; |
| + | } |
| + | } |
| + | } |
| + | } |
| + | s++; |
| + | } |
| + | } |
| + | return i; |
| + | } |
| + | |
| + | |
| + | static void sig_term_handler(int sig_no) |
| + | { |
| + | if (verbose) |
| + | bb_error_msg(INFO"sig%s received", "term"); |
| + | exitasap = 1; |
| + | } |
| + | |
| + | static void sig_child_handler(int sig_no) |
| + | { |
| + | int pid, l; |
| + | |
| + | if (verbose) |
| + | bb_error_msg(INFO"sig%s received", "child"); |
| + | while ((pid = wait_nohang(&wstat)) > 0) |
| + | for (l = 0; l < dirn; ++l) |
| + | if (dir[l].ppid == pid) { |
| + | dir[l].ppid = 0; |
| + | processorstop(&dir[l]); |
| + | break; |
| + | } |
| + | } |
| + | |
| + | static void sig_alarm_handler(int sig_no) |
| + | { |
| + | if (verbose) |
| + | bb_error_msg(INFO"sig%s received", "alarm"); |
| + | rotateasap = 1; |
| + | } |
| + | |
| + | static void sig_hangup_handler(int sig_no) |
| + | { |
| + | if (verbose) |
| + | bb_error_msg(INFO"sig%s received", "hangup"); |
| + | reopenasap = 1; |
| + | } |
| + | |
| + | static void logmatch(struct logdir *ld) |
| + | { |
| + | char *s; |
| + | |
| + | ld->match = '+'; |
| + | ld->matcherr = 'E'; |
| + | s = ld->inst; |
| + | while (s && s[0]) { |
| + | switch (s[0]) { |
| + | case '+': |
| + | case '-': |
| + | if (pmatch(s+1, line, linelen)) |
| + | ld->match = s[0]; |
| + | break; |
| + | case 'e': |
| + | case 'E': |
| + | if (pmatch(s+1, line, linelen)) |
| + | ld->matcherr = s[0]; |
| + | break; |
| + | } |
| + | s += strlen(s) + 1; |
| + | } |
| + | } |
| + | |
| + | int svlogd_main(int argc, char **argv) |
| + | { |
| + | struct taia now; |
| + | char *r,*l,*b; |
| + | ssize_t stdin_cnt = 0; |
| + | int i; |
| + | unsigned opt; |
| + | unsigned timestamp = 0; |
| + | |
| + | opt_complementary = "tt:vv"; |
| + | opt = getopt32(argc, argv, "r:R:l:b:tv", |
| + | &r, &replace, &l, &b, ×tamp, &verbose); |
| + | if (opt & 1) { // -r |
| + | repl = r[0]; |
| + | if (!repl || r[1]) usage(); |
| + | } |
| + | if (opt & 2) if (!repl) repl = '_'; // -R |
| + | if (opt & 4) { // -l |
| + | linemax = xatou_range(l, 0, 1000); |
| + | if (linemax == 0) linemax = 1000; |
| + | if (linemax < 256) linemax = 256; |
| + | } |
| + | if (opt & 8) { // -b |
| + | buflen = xatoi_u(b); |
| + | if (buflen == 0) buflen = 1024; |
| + | } |
| + | //if (opt & 0x10) timestamp++; // -t |
| + | //if (opt & 0x20) verbose++; // -v |
| + | if (timestamp > 2) timestamp = 2; |
| + | argv += optind; |
| + | argc -= optind; |
| + | |
| + | dirn = argc; |
| + | if (dirn <= 0) usage(); |
| + | if (buflen <= linemax) usage(); |
| + | fdwdir = xopen(".", O_RDONLY|O_NDELAY); |
| + | coe(fdwdir); |
| + | dir = xmalloc(dirn * sizeof(struct logdir)); |
| + | for (i = 0; i < dirn; ++i) { |
| + | dir[i].fddir = -1; |
| + | dir[i].fdcur = -1; |
| + | dir[i].btmp = xmalloc(buflen); |
| + | dir[i].ppid = 0; |
| + | } |
| + | line = xmalloc(linemax + (timestamp ? 26 : 0)); |
| + | fndir = argv; |
| + | in.fd = 0; |
| + | in.events = IOPAUSE_READ; |
| + | ndelay_on(in.fd); |
| + | |
| + | sig_block(sig_term); |
| + | sig_block(sig_child); |
| + | sig_block(sig_alarm); |
| + | sig_block(sig_hangup); |
| + | sig_catch(sig_term, sig_term_handler); |
| + | sig_catch(sig_child, sig_child_handler); |
| + | sig_catch(sig_alarm, sig_alarm_handler); |
| + | sig_catch(sig_hangup, sig_hangup_handler); |
| + | |
| + | logdirs_reopen(); |
| + | |
| + | /* Each iteration processes one line */ |
| + | while (1) { |
| + | int printlen; |
| + | char *lineptr = line; |
| + | char *np; |
| + | char ch; |
| + | |
| + | /* Prepare timestamp if needed */ |
| + | if (timestamp) { |
| + | char stamp[FMT_PTIME]; |
| + | taia_now(&now); |
| + | switch (timestamp) { |
| + | case 1: |
| + | fmt_taia(stamp, &now); |
| + | break; |
| + | default: /* case 2: */ |
| + | fmt_ptime(stamp, &now); |
| + | break; |
| + | } |
| + | memcpy(line, stamp, 25); |
| + | line[25] = ' '; |
| + | lineptr += 26; |
| + | } |
| + | |
| + | /* lineptr[0..linemax-1] - buffer for stdin */ |
| + | /* (possibly has some unprocessed data from prev loop) */ |
| + | |
| + | /* Refill the buffer if needed */ |
| + | np = memchr(lineptr, '\n', stdin_cnt); |
| + | i = linemax - stdin_cnt; /* avail. bytes at tail */ |
| + | if (i >= 128 && !exitasap && !np) { |
| + | int sz = buffer_pread(0, lineptr + stdin_cnt, i); |
| + | if (sz <= 0) /* EOF or error on stdin */ |
| + | exitasap = 1; |
| + | else { |
| + | stdin_cnt += sz; |
| + | np = memchr(lineptr, '\n', stdin_cnt); |
| + | } |
| + | } |
| + | if (stdin_cnt <= 0 && exitasap) |
| + | break; |
| + | |
| + | /* Search for '\n' (in fact, np already holds the result) */ |
| + | linelen = stdin_cnt; |
| + | if (np) linelen = np - lineptr + 1; |
| + | /* linelen == no of chars incl. '\n' (or == stdin_cnt) */ |
| + | ch = lineptr[linelen-1]; |
| + | |
| + | printlen = linelen + (timestamp ? 26 : 0); |
| + | /* write out line[0..printlen-1] to each log destination */ |
| + | for (i = 0; i < dirn; ++i) { |
| + | struct logdir *ld = &dir[i]; |
| + | if (ld->fddir == -1) continue; |
| + | if (ld->inst) |
| + | logmatch(ld); |
| + | if (ld->matcherr == 'e') { |
| + | fprintf(stderr, "%.*s%s", |
| + | printlen, line, |
| + | (ch != '\n') ? "...\n" : "" |
| + | ); |
| + | } |
| + | if (ld->match != '+') continue; |
| + | buffer_pwrite(i, line, printlen); |
| + | } |
| + | |
| + | /* If we didn't see '\n' (long input line), */ |
| + | /* read/write repeatedly until we see it */ |
| + | while (ch != '\n') { |
| + | /* lineptr is emptied now, safe to use as buffer */ |
| + | stdin_cnt = exitasap ? -1 : buffer_pread(0, lineptr, linemax); |
| + | if (stdin_cnt <= 0) { /* EOF or error on stdin */ |
| + | lineptr[0] = ch = '\n'; |
| + | linelen = 1; |
| + | exitasap = 1; |
| + | stdin_cnt = 1; |
| + | } else { |
| + | linelen = stdin_cnt; |
| + | np = memchr(lineptr, '\n', stdin_cnt); |
| + | if (np) linelen = np - lineptr + 1; |
| + | ch = lineptr[linelen-1]; |
| + | } |
| + | /* linelen == no of chars incl. '\n' (or == stdin_cnt) */ |
| + | for (i = 0; i < dirn; ++i) { |
| + | if (dir[i].fddir == -1) continue; |
| + | if (dir[i].match != '+') continue; |
| + | buffer_pwrite(i, lineptr, linelen); |
| + | } |
| + | } |
| + | |
| + | /* Move unprocessed data to the front of line */ |
| + | stdin_cnt -= linelen; |
| + | if (stdin_cnt > 0) /* TODO: slow if buffer is big */ |
| + | memmove(lineptr, &lineptr[linelen], stdin_cnt); |
| + | } |
| + | |
| + | for (i = 0; i < dirn; ++i) { |
| + | if (dir[i].ppid) |
| + | while (!processorstop(&dir[i])) |
| + | /* repeat */; |
| + | logdir_close(&dir[i]); |
| + | } |
| + | _exit(0); |
| + | } |