s6

Mirror/fork of https://skarnet.org/software/s6/
git clone https://ccx.te2000.cz/git/s6
Log | Files | Refs | README | LICENSE

commit 502bfd940bfc39fe4eb256d8bd06cc5a15c098d3
parent 60cb96c3ce59e7a39000ab173a04792daea2b9c4
Author: Laurent Bercot <ska-skaware@skarnet.org>
Date:   Sat,  7 Feb 2015 14:51:06 +0000

 - s6-log overhaul (more stack, less heap), with new directives
 - related doc update
 - version: 2.1.1.0 rc

Diffstat:
MINSTALL | 4++--
Mdoc/index.html | 6+++---
Mdoc/s6-log.html | 70+++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Mdoc/upgrade.html | 9+++++++++
Mpackage/info | 2+-
Msrc/daemontools-extras/s6-log.c | 927+++++++++++++++++++++++++++++++++++++++++--------------------------------------
6 files changed, 548 insertions(+), 470 deletions(-)

diff --git a/INSTALL b/INSTALL @@ -6,8 +6,8 @@ Build Instructions - A POSIX-compliant C development environment - GNU make version 4.0 or later - - skalibs version 2.2.1.0 or later: http://skarnet.org/software/skalibs/ - - execline version 2.0.2.0 or later: http://skarnet.org/software/execline/ + - skalibs version 2.3.0.0 or later: http://skarnet.org/software/skalibs/ + - execline version 2.0.2.1 or later: http://skarnet.org/software/execline/ This software will run on any operating system that implements POSIX.1-2008, available at: diff --git a/doc/index.html b/doc/index.html @@ -82,11 +82,11 @@ with s6</a> </li> <li> GNU make, version 4.0 or later. Please be aware that s6 will not build with an earlier version. </li> <li> <a href="http://skarnet.org/software/skalibs/">skalibs</a> version -2.2.1.0 or later. It's a build-time requirement. It's also a run-time +2.3.0.0 or later. It's a build-time requirement. It's also a run-time requirement if you link against the shared version of the skalibs library. </li> <li> <a href="http://skarnet.org/software/execline/">execline</a> version -2.0.2.0 or later. It's a build-time and run-time requirement. </li> +2.0.2.1 or later. It's a build-time and run-time requirement. </li> </ul> <h3> Licensing </h3> @@ -99,7 +99,7 @@ library. </li> <h3> Download </h3> <ul> - <li> The current released version of s6 is <a href="s6-2.1.0.1.tar.gz">2.1.0.1</a>. </li> + <li> The current released version of s6 is <a href="s6-2.1.1.0.tar.gz">2.1.1.0</a>. </li> <li> Alternatively, you can checkout a copy of the s6 git repository: <pre> git clone git://git.skarnet.org/s6 </pre> </li> </ul> diff --git a/doc/s6-log.html b/doc/s6-log.html @@ -53,9 +53,9 @@ exit when reading EOF on stdin. </li> <li> <tt>-t</tt>&nbsp;: timestamp. Prepends every log line that is written to a logging directory with a <a href="http://skarnet.org/software/skalibs/libstddjb/tai.html">TAI64N</a> -timestamp. </li> +timestamp. <strong>This option is now deprecated.</strong> </li> <li> <tt>-e</tt>&nbsp;: timestamp alerts. Prepends every "alert" line with a -TAI64N timestamp. </li> +TAI64N timestamp. <strong>This option is now deprecated. </strong> </li> <li> <tt>-q | -v</tt>&nbsp;: quiet | verbose. Decreases | increases s6-log's verbosity, i.e. which messages are sent to stderr. The default verbosity is 1. Currently supported verbosity levels: @@ -220,8 +220,29 @@ an unpadded, unlimited status file. By default, <em>statussize</em> is 1001. </l <tt>execlineb</tt> must be found in s6-log's PATH. If <em>processor</em> is empty, no processor will be set for the next logdirs. By default, no processor is set. </li> + <li> <strong>t</strong>: the logged line will be prepended with a +<a href="http://skarnet.org/software/skalibs/libstddjb/tai.html">TAI64N +timestamp</a> (and a space) before being processed by the next action +directive. Giving the <tt>t</tt> directive several times before an action +directive has no effect. </li> + <li> <strong>T</strong>: the selected line will be prepended with a +<a href="http://en.wikipedia.org/wiki/ISO_8601">ISO 8601 +timestamp</a> for combined date and time representing <em>local time</em> +according to the system's timezone, with a space (not a 'T') +between the date and the time and two spaces after the time, before being +processed by the next action directive. Giving the <tt>T</tt> directive +several times before an action directive has no effect. </li> </ul> +<p> + Note that unlike the other control directives, the <tt>t</tt> and +<tt>T</tt> directives are <em>not sticky</em>: their effect will +disappear after the next action directive, and they need to be +reapplied if necessary. If both a <tt>t</tt> and a <tt>T</tt> +directives are given before an action directive, the TAI64N timestamp +will always appear before the ISO 8601 timestamp. +</p> + <h3> Action directives </h3> <p> @@ -230,9 +251,14 @@ selected lines. </p> <ul> - <li> <strong>e</strong>: alert. s6-log will print "s6-log: alert: ", + <li> <strong>2</strong> or <strong>e</strong>: alert. s6-log will print "s6-log: alert: ", possibly prepended with a timestamp, followed by the first -<em>alertsize</em> bytes of the line. </li> +<em>alertsize</em> bytes of the line, to its standard error. +<strong>The <tt>e</tt> form is deprecated.</strong> </li> + <li> <strong>1</strong>: forward to stdout. s6-log will print the selected +line to its stdout. If any error occurs, e.g. if stdout was a pipe and the +reading end closed, this directive will be ignored for the rest of +s6-log's lifetime. </li> <li> <strong>=<em>statusfile</em></strong>: status. s6-log will atomically update the <em>statusfile</em> file with the first <em>statussize</em> bytes of the line, and pad it with newlines. s6-log must have the right @@ -257,25 +283,26 @@ limit. </li> <h2> Examples </h2> <pre> - s6-log -bt n20 s1000000 /var/log/services/stuff + s6-log -b n20 s1000000 t /var/log/services/stuff </pre> <p> - Logs all of stdin, prepending every line with a timestamp, into the + Logs all of stdin, prepending every line with a TAI64N timestamp, into the <tt>/var/log/services/stuff</tt> logdir, with a maximum archive of 20 log files of 1 MB each; makes sure every line has been written before reading the next one. </p> <pre> - s6-log -t n30 E500 - +fatal: e - +^STAT =/var/log/foobard/status f s10000000 S15000000 !"gzip -nq9" /var/log/foobard + s6-log n30 E500 - +fatal: e - +^STAT =/var/log/foobard/status f s10000000 S15000000 T !"gzip -nq9" /var/log/foobard </pre> <ul> <li> Sends alerts to stderr with the 500 first bytes of lines containing "fatal:". </li> <li> Maintains the <tt>/var/log/foobard/status</tt> file at 1001 bytes, updating it when it finds a log line starting with "STAT". </li> - <li> Logs all other lines to logdir <tt>/var/log/foobard</tt>. When <tt>current</tt> + <li> Logs all other lines to logdir <tt>/var/log/foobard</tt>, prepending +them with an ISO 8601 timestamp. When <tt>current</tt> reaches at least 9998 kB (i.e. 10 MB filesise minus 2kB tolerance), pipe it through <tt>gzip -nq9</tt> and save the result into a timestamped archive file, with a maximum of 30 such files or a total of 15 MB of compressed archive files. </li> @@ -301,14 +328,15 @@ is overpowered for this. We don't need a complete shell script interpreter: most <em>processor</em> commands will be very simple, with only two or three words, and we only need a way to turn a string into an <em>argv</em>, i.e. a command line. </li> - <li> <a href="http://www.skarnet.org/software/execline/execlineb.html">execlineb</a> + <li> <a href="http://skarnet.org/software/execline/execlineb.html">execlineb</a> was designed just for this: to turn simple strings into command lines. It is a very fast and lightweight script launcher, that does not do any heavy startup initialization like <tt>/bin/sh</tt> does. It happens to be the perfect tool for the job. </li> - <li> Yes, I also did this on purpose so people have a reason to use the -<a href="http://www.skarnet.org/software/execline/">execline</a> language. Do not -look at me like that: it <em>really</em> is the perfect tool for the job. </li> + <li> To be perfectly honest: I also did this on purpose so people have a +reason to use the +<a href="http://skarnet.org/software/execline/">execline</a> language. But +seriously, it <em>really</em> is the perfect tool for the job. </li> </ul> <h2> Why have another logging mechanism ? </h2> @@ -318,7 +346,9 @@ look at me like that: it <em>really</em> is the perfect tool for the job. </li> I'm not being judgmental; I'm just stating the obvious. </p> -<a name="diesyslogdiedie"><h3> The syslog design is flawed from the start </h3></a> +<a name="diesyslogdiedie"> +<h3> The syslog design is flawed from the start </h3> +</a> <p> <a href="http://blog.gerhards.net/2007/08/why-does-world-need-another-syslogd.html">When @@ -381,7 +411,9 @@ serious alternative yet, except maybe s6-log improves upon. </p> -<a name="loggingchain"><h3> A not-so-modest proposal: the logging chain </h3></a> +<a name="loggingchain"> +<h3> A not-so-modest proposal: the logging chain </h3> +</a> <p> Unix distributions already do this to some extent, but it's at best @@ -446,7 +478,9 @@ error messages, and maybe process 1's error messages, are sent to the system console. </li> </ul> -<a name="#howtouse"><h3> What does s6-log have to do with all this ? </h3></a> +<a name="howtouse"> +<h3> What does s6-log have to do with all this ? </h3> +</a> <p> In a <em>logging chain</em> situation, every service must have @@ -473,8 +507,10 @@ your system's loggers. </li> </ul> -<a name="#network"><h3> You're wrong about being as powerful as -syslogd: s6-log does not do remote logging. </h3></a> +<a name="network"> +<h3> You're wrong about being as powerful as +syslogd: s6-log does not do remote logging. </h3> +</a> <p> You mean you want to send, <em>live</em>, every <em>log line</em> diff --git a/doc/upgrade.html b/doc/upgrade.html @@ -17,6 +17,15 @@ <h1> What has changed in s6 </h1> +<ul> + <li> skalibs dependency bumped to 2.3.0.0. </li> + <li> execline dependency bumped to 2.0.2.1. </li> + <li> New functionality added to <a href="s6-log.html">s6-log</a>. Its +syntax has been extended; some options are now marked as obsolescent. </li> + <li> Access control instated in <a href="s6-fdholderd.html">s6-fdholderd</a> +for <a href="s6-fdholder-listc.html">listing</a> </li> +</ul> + <h2> in 2.1.0.1 </h2> <p> diff --git a/package/info b/package/info @@ -1,4 +1,4 @@ package=s6 -version=2.1.0.1 +version=2.1.1.0 category=admin package_macro_name=S6 diff --git a/src/daemontools-extras/s6-log.c b/src/daemontools-extras/s6-log.c @@ -6,8 +6,8 @@ #include <errno.h> #include <signal.h> #include <unistd.h> -#include <stdio.h> /* for rename() */ -#include <stdlib.h> /* for qsort() */ +#include <stdio.h> +#include <stdlib.h> #include <regex.h> #include <skalibs/uint32.h> #include <skalibs/uint64.h> @@ -15,28 +15,29 @@ #include <skalibs/allreadwrite.h> #include <skalibs/buffer.h> #include <skalibs/bytestr.h> +#include <skalibs/error.h> #include <skalibs/sgetopt.h> #include <skalibs/strerr2.h> -#include <skalibs/fmtscan.h> #include <skalibs/bufalloc.h> #include <skalibs/stralloc.h> -#include <skalibs/genalloc.h> #include <skalibs/tai.h> -#include <skalibs/error.h> +#include <skalibs/djbtime.h> #include <skalibs/iopause.h> #include <skalibs/djbunix.h> #include <skalibs/direntry.h> #include <skalibs/sig.h> #include <skalibs/selfpipe.h> +#include <skalibs/siovec.h> #include <skalibs/skamisc.h> #include <skalibs/environ.h> #include <execline/config.h> -#define USAGE "s6-log [ -q | -v ] [ -b ] [ -p ] [ -t ] [ -e ] logging_script" +#define USAGE "s6-log [ -q | -v ] [ -b ] [ -p ] [ -t ] [ -e ] [ -l linelimit ] logging_script" +#define dieusage() strerr_dieusage(100, USAGE) #define dienomem() strerr_diefu1sys(111, "stralloc_catb") -static int flagstampalert = 0 ; -static int flagstamp = 0 ; +#define LINELIMIT_MIN 48 + static int flagprotect = 0 ; static int flagexiting = 0 ; static unsigned int verbosity = 1 ; @@ -44,7 +45,7 @@ static unsigned int verbosity = 1 ; static stralloc indata = STRALLOC_ZERO ; -/* Begin datatypes. Get ready for some lulz. */ + /* Data types */ typedef int qcmpfunc_t (void const *, void const *) ; typedef qcmpfunc_t *qcmpfunc_t_ref ; @@ -87,52 +88,30 @@ struct sel_s #define SEL_ZERO { .type = SELTYPE_PHAIL } -static void sel_free (sel_t_ref s) -{ - if (s->type != SELTYPE_DEFAULT) regfree(&s->re) ; - s->type = SELTYPE_PHAIL ; -} - typedef enum acttype_e acttype_t, *acttype_t_ref ; enum acttype_e { + ACTTYPE_NOTHING, + ACTTYPE_FD1, ACTTYPE_FD2, ACTTYPE_STATUS, ACTTYPE_DIR, ACTTYPE_PHAIL } ; -typedef struct as_fd2_s as_fd2_t, *as_fd2_t_ref ; -struct as_fd2_s -{ - unsigned int size ; -} ; - typedef struct as_status_s as_status_t, *as_status_t_ref ; struct as_status_s { - stralloc content ; char const *file ; -} ; - -static void as_status_free (as_status_t_ref ap) -{ - stralloc_free(&ap->content) ; - ap->file = 0 ; -} - -typedef struct as_dir_s as_dir_t, *as_dir_t_ref ; -struct as_dir_s -{ - unsigned int lindex ; + unsigned int filelen ; } ; typedef union actstuff_u actstuff_t, *actstuff_t_ref ; union actstuff_u { - as_fd2_t fd2 ; + unsigned int fd2_size ; as_status_t status ; - as_dir_t dir ; + unsigned int ld ; } ; typedef struct act_s act_t, *act_t_ref ; @@ -140,48 +119,26 @@ struct act_s { acttype_t type ; actstuff_t data ; + unsigned int flags ; } ; -static void act_free (act_t_ref ap) -{ - switch (ap->type) - { - case ACTTYPE_FD2 : - break ; - case ACTTYPE_STATUS : - as_status_free(&ap->data.status) ; - break ; - case ACTTYPE_DIR : - break ; - default : break ; - } - ap->type = ACTTYPE_PHAIL ; -} - typedef struct scriptelem_s scriptelem_t, *scriptelem_t_ref ; struct scriptelem_s { - genalloc selections ; /* array of sel_t */ - genalloc actions ; /* array of act_t */ + sel_t const *sels ; + unsigned int sellen ; + act_t const *acts ; + unsigned int actlen ; } ; -#define SCRIPTELEM_ZERO { .selections = GENALLOC_ZERO, .actions = GENALLOC_ZERO } - -static void scriptelem_free (scriptelem_t_ref se) -{ - scriptelem_t zero = SCRIPTELEM_ZERO ; - genalloc_deepfree(sel_t, &se->selections, &sel_free) ; - genalloc_deepfree(act_t, &se->actions, &act_free) ; - *se = zero ; -} - -typedef void inputprocfunc_t (scriptelem_t const *, unsigned int) ; +typedef void inputprocfunc_t (scriptelem_t const *, unsigned int, unsigned int, unsigned int) ; typedef inputprocfunc_t *inputprocfunc_t_ref ; typedef struct logdir_s logdir_t, *logdir_t_ref ; struct logdir_s { bufalloc out ; + unsigned int xindex ; tain_t retrytto ; tain_t deadline ; uint64 maxdirsize ; @@ -192,6 +149,7 @@ struct logdir_s unsigned int pid ; char const *dir ; char const *processor ; + unsigned int flags ; int fd ; int fdlock ; rotstate_t rstate ; @@ -199,6 +157,7 @@ struct logdir_s #define LOGDIR_ZERO { \ .out = BUFALLOC_ZERO, \ + .xindex = 0, \ .retrytto = TAIN_ZERO, \ .deadline = TAIN_ZERO, \ .maxdirsize = 0, \ @@ -214,19 +173,6 @@ struct logdir_s .rstate = ROTSTATE_WRITABLE \ } - /* If freeing a logdir before exiting is ever needed: -static void logdir_free (logdir_t_ref ldp) -{ - bufalloc_free(&ldp->out) ; - fd_close(ldp->fd) ; ldp->fd = -1 ; - fd_close(ldp->fdlock) ; ldp->fdlock = -1 ; -} - */ - -/* End datatypes. All of this was just to optimize the script interpretation. :-) */ - -static genalloc logdirs = GENALLOC_ZERO ; /* array of logdir_t */ - typedef struct filesize_s filesize_t, *filesize_t_ref ; struct filesize_s { @@ -234,6 +180,12 @@ struct filesize_s char name[28] ; } ; + + /* Logdirs */ + +static logdir_t *logdirs ; +static unsigned int llen = 0 ; + static int filesize_cmp (filesize_t const *a, filesize_t const *b) { return byte_diff(a->name+1, 26, b->name+1) ; @@ -241,18 +193,15 @@ static int filesize_cmp (filesize_t const *a, filesize_t const *b) static int name_is_relevant (char const *name) { - if (name[0] != '@') return 0 ; + tain_t dummy ; if (str_len(name) != 27) return 0 ; - { - char tmp[12] ; - if (!ucharn_scan(name+1, tmp, 12)) return 0 ; - } + if (!timestamp_scan(name, &dummy)) return 0 ; if (name[25] != '.') return 0 ; if ((name[26] != 's') && (name[26] != 'u')) return 0 ; return 1 ; } -static inline int logdir_trim (logdir_t_ref ldp) +static inline int logdir_trim (logdir_t *ldp) { unsigned int n = 0 ; DIR *dir = opendir(ldp->dir) ; @@ -273,11 +222,12 @@ static inline int logdir_trim (logdir_t_ref ldp) return -1 ; } rewinddir(dir) ; + if (n) { - filesize_t blurgh[n] ; uint64 totalsize = 0 ; unsigned int dirlen = str_len(ldp->dir) ; unsigned int i = 0 ; + filesize_t blurgh[n] ; char fullname[dirlen + 29] ; byte_copy(fullname, dirlen, ldp->dir) ; fullname[dirlen] = '/' ; @@ -337,7 +287,7 @@ static int finish (logdir_t *ldp, char const *name, char suffix) byte_copy(x, dirlen, ldp->dir) ; x[dirlen] = '/' ; byte_copy(x + dirlen + 1, namelen + 1, name) ; - if (stat(x, &st) < 0) return (errno == ENOENT) ; + if (stat(x, &st) < 0) return errno == ENOENT ; if (st.st_nlink == 1) { char y[dirlen + 29] ; @@ -347,13 +297,13 @@ static int finish (logdir_t *ldp, char const *name, char suffix) y[dirlen + 26] = '.' ; y[dirlen + 27] = suffix ; y[dirlen + 28] = 0 ; - if (link(x, y) < 0) return 0 ; + if (link(x, y) < 0) return -1 ; } - if (unlink(x) < 0) return 0 ; + if (unlink(x) < 0) return -1 ; return logdir_trim(ldp) ; } -static inline void exec_processor (logdir_t_ref ldp) +static inline void exec_processor (logdir_t *ldp) { char const *cargv[4] = { EXECLINE_EXTBINPREFIX "execlineb", "-Pc", ldp->processor, 0 } ; unsigned int dirlen = str_len(ldp->dir) ; @@ -383,13 +333,12 @@ static inline void exec_processor (logdir_t_ref ldp) strerr_dieexec(111, cargv[0]) ; } -static int rotator (logdir_t_ref ldp) +static int rotator (logdir_t *ldp) { unsigned int dirlen = str_len(ldp->dir) ; switch (ldp->rstate) { case ROTSTATE_START : - { if (fd_sync(ldp->fd) < 0) { if (verbosity) strerr_warnwu3sys("fd_sync ", ldp->dir, "/current") ; @@ -397,7 +346,6 @@ static int rotator (logdir_t_ref ldp) } tain_now_g() ; ldp->rstate = ROTSTATE_RENAME ; - } case ROTSTATE_RENAME : { char current[dirlen + 9] ; @@ -460,7 +408,6 @@ static int rotator (logdir_t_ref ldp) ldp->rstate = ROTSTATE_FINISHPREVIOUS ; } case ROTSTATE_FINISHPREVIOUS : - { if (finish(ldp, "previous", 's') < 0) { if (verbosity) strerr_warnwu2sys("finish previous .s to logdir ", ldp->dir) ; @@ -469,8 +416,7 @@ static int rotator (logdir_t_ref ldp) tain_copynow(&ldp->deadline) ; ldp->rstate = ROTSTATE_WRITABLE ; break ; - } - runprocessor : + runprocessor: ldp->rstate = ROTSTATE_RUNPROCESSOR ; case ROTSTATE_RUNPROCESSOR : { @@ -486,9 +432,7 @@ static int rotator (logdir_t_ref ldp) ldp->rstate = ROTSTATE_WAITPROCESSOR ; } case ROTSTATE_WAITPROCESSOR : - { return (errno = EAGAIN, 0) ; - } case ROTSTATE_SYNCPROCESSED : { int fd ; @@ -528,7 +472,7 @@ static int rotator (logdir_t_ref ldp) byte_copy(x, dirlen, ldp->dir) ; byte_copy(x + dirlen, 10, "/newstate") ; fd = open_append(x) ; - if (ldp->fd < 0) + if (fd < 0) { if (verbosity) strerr_warnwu2sys("open_append ", x) ; goto fail ; @@ -570,7 +514,6 @@ static int rotator (logdir_t_ref ldp) ldp->rstate = ROTSTATE_FINISHPROCESSED ; } case ROTSTATE_FINISHPROCESSED : - { if (finish(ldp, "processed", 's') < 0) { if (verbosity) strerr_warnwu2sys("finish processed .s to logdir ", ldp->dir) ; @@ -579,7 +522,6 @@ static int rotator (logdir_t_ref ldp) tain_copynow(&ldp->deadline) ; ldp->rstate = ROTSTATE_WRITABLE ; break ; - } default : strerr_dief1x(101, "inconsistent state in rotator()") ; } return 1 ; @@ -590,7 +532,7 @@ static int rotator (logdir_t_ref ldp) static int logdir_write (int i, char const *s, unsigned int len) { - logdir_t_ref ldp = genalloc_s(logdir_t, &logdirs) + (unsigned int)i ; + logdir_t *ldp = logdirs + i ; int r ; unsigned int n = len ; { @@ -627,30 +569,32 @@ static inline void rotate_or_flush (logdir_t *ldp) bufalloc_flush(&ldp->out) ; } -static inline void logdir_init (logdir_t *ap, uint32 s, uint32 n, uint32 tolerance, uint64 maxdirsize, tain_t const *retrytto, char const *processor, char const *name, unsigned int index) +static inline void logdir_init (unsigned int index, uint32 s, uint32 n, uint32 tolerance, uint64 maxdirsize, tain_t const *retrytto, char const *processor, char const *name, unsigned int flags) { + logdir_t *ldp = logdirs + index ; struct stat st ; unsigned int dirlen = str_len(name) ; int r ; char x[dirlen + 11] ; - ap->s = s ; - ap->n = n ; - ap->pid = 0 ; - ap->tolerance = tolerance ; - ap->maxdirsize = maxdirsize ; - ap->retrytto = *retrytto ; - ap->processor = processor ; - ap->dir = name ; - ap->fd = -1 ; - ap->rstate = ROTSTATE_WRITABLE ; - r = mkdir(ap->dir, S_IRWXU | S_ISGID) ; + ldp->s = s ; + ldp->n = n ; + ldp->pid = 0 ; + ldp->tolerance = tolerance ; + ldp->maxdirsize = maxdirsize ; + ldp->retrytto = *retrytto ; + ldp->processor = processor ; + ldp->flags = flags ; + ldp->dir = name ; + ldp->fd = -1 ; + ldp->rstate = ROTSTATE_WRITABLE ; + r = mkdir(ldp->dir, S_IRWXU | S_ISGID) ; if ((r < 0) && (errno != EEXIST)) strerr_diefu2sys(111, "mkdir ", name) ; byte_copy(x, dirlen, name) ; byte_copy(x + dirlen, 6, "/lock") ; - ap->fdlock = open_append(x) ; - if ((ap->fdlock) < 0) strerr_diefu2sys(111, "open_append ", x) ; - if (lock_exnb(ap->fdlock) < 0) strerr_diefu2sys(111, "lock_exnb ", x) ; - if (coe(ap->fdlock) < 0) strerr_diefu2sys(111, "coe ", x) ; + ldp->fdlock = open_append(x) ; + if ((ldp->fdlock) < 0) strerr_diefu2sys(111, "open_append ", x) ; + if (lock_exnb(ldp->fdlock) < 0) strerr_diefu2sys(111, "lock_exnb ", x) ; + if (coe(ldp->fdlock) < 0) strerr_diefu2sys(111, "coe ", x) ; byte_copy(x + dirlen + 1, 8, "current") ; if (stat(x, &st) < 0) { @@ -673,177 +617,279 @@ static inline void logdir_init (logdir_t *ap, uint32 s, uint32 n, uint32 toleran { byte_copy(x + dirlen + 1, 9, "previous") ; unlink(x) ; - if (finish(ap, "processed", 's') < 0) - strerr_diefu2sys(111, "finish processed .s for logdir ", ap->dir) ; + if (finish(ldp, "processed", 's') < 0) + strerr_diefu2sys(111, "finish processed .s for logdir ", ldp->dir) ; } else { unlink(x) ; - if (finish(ap, "previous", 'u') < 0) - strerr_diefu2sys(111, "finish previous .u for logdir ", ap->dir) ; + if (finish(ldp, "previous", 'u') < 0) + strerr_diefu2sys(111, "finish previous .u for logdir ", ldp->dir) ; } } - if (finish(ap, "current", 'u') < 0) - strerr_diefu2sys(111, "finish current .u for logdir ", ap->dir) ; + if (finish(ldp, "current", 'u') < 0) + strerr_diefu2sys(111, "finish current .u for logdir ", ldp->dir) ; byte_copy(x + dirlen + 1, 6, "state") ; - ap->fd = open_trunc(x) ; - if (ap->fd < 0) strerr_diefu2sys(111, "open_trunc ", x) ; - fd_close(ap->fd) ; + ldp->fd = open_trunc(x) ; + if (ldp->fd < 0) strerr_diefu2sys(111, "open_trunc ", x) ; + fd_close(ldp->fd) ; st.st_size = 0 ; byte_copy(x + dirlen + 1, 8, "current") ; opencurrent: - ap->fd = open_append(x) ; - if (ap->fd < 0) strerr_diefu2sys(111, "open_append ", x) ; - if (fd_chmod(ap->fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) + ldp->fd = open_append(x) ; + if (ldp->fd < 0) strerr_diefu2sys(111, "open_append ", x) ; + if (fd_chmod(ldp->fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) strerr_diefu2sys(111, "fd_chmod ", x) ; - if (coe(ap->fd) < 0) strerr_diefu2sys(111, "coe ", x) ; - ap->b = st.st_size ; - tain_copynow(&ap->deadline) ; - bufalloc_init(&ap->out, &logdir_write, (int)index) ; + if (coe(ldp->fd) < 0) strerr_diefu2sys(111, "coe ", x) ; + ldp->b = st.st_size ; + tain_copynow(&ldp->deadline) ; + bufalloc_init(&ldp->out, &logdir_write, index) ; } - - /* Script */ - -static int script_update (genalloc *sc, genalloc *sa, genalloc *aa) +static inline int logdir_finalize (logdir_t *ldp) { - scriptelem_t foo ; - genalloc_shrink(sel_t, sa) ; - genalloc_shrink(act_t, aa) ; - foo.selections = *sa ; - foo.actions = *aa ; - if (!genalloc_append(scriptelem_t, sc, &foo)) return 0 ; - *sa = genalloc_zero ; - *aa = genalloc_zero ; + switch (ldp->rstate) + { + case ROTSTATE_WRITABLE : + { + if (fd_sync(ldp->fd) < 0) + { + if (verbosity) strerr_warnwu3sys("fd_sync ", ldp->dir, "/current") ; + goto fail ; + } + tain_now_g() ; + ldp->rstate = ROTSTATE_ENDFCHMOD ; + } + case ROTSTATE_ENDFCHMOD : + { + if (fd_chmod(ldp->fd, S_IRWXU | S_IRGRP | S_IROTH) < 0) + { + if (verbosity) strerr_warnwu3sys("fd_chmod ", ldp->dir, "/current to 0744") ; + goto fail ; + } + ldp->rstate = ROTSTATE_END ; + break ; + } + default : strerr_dief1x(101, "inconsistent state in logdir_finalize()") ; + } return 1 ; + fail: + tain_add_g(&ldp->deadline, &ldp->retrytto) ; + return 0 ; } -static inline int script_init (genalloc *sc, char const *const *argv) +static inline void finalize (void) +{ + unsigned int n = llen ; + for (;;) + { + unsigned int i = 0 ; + tain_t deadline ; + tain_addsec_g(&deadline, 2) ; + for (; i < llen ; i++) + if (logdirs[i].rstate != ROTSTATE_END) + { + if (logdir_finalize(logdirs + i)) n-- ; + else if (tain_less(&logdirs[i].deadline, &deadline)) + deadline = logdirs[i].deadline ; + } + if (!n) break ; + { + iopause_fd x ; + iopause_g(&x, 0, &deadline) ; + } + } +} + + + /* Script */ + +static inline void script_firstpass (char const *const *argv, unsigned int *sellen, unsigned int *actlen, unsigned int *scriptlen, unsigned int *gflags) { - tain_t cur_retrytto ; - unsigned int cur_fd2_size = 200 ; - unsigned int cur_status_size = 1001 ; - uint32 cur_s = 99999 ; - uint32 cur_n = 10 ; - uint32 cur_tolerance = 2000 ; - uint64 cur_maxdirsize = 0 ; - genalloc cur_selections = GENALLOC_ZERO ; /* sel_t */ - genalloc cur_actions = GENALLOC_ZERO ; /* act_t */ - char const *cur_processor = 0 ; + unsigned int se = 0, ac = 0, sc = 0, gf = *gflags ; int flagacted = 0 ; - tain_uint(&cur_retrytto, 2) ; - for (; *argv ; argv++) { - switch (**argv) + switch ((*argv)[0]) { case 'f' : - { - sel_t selitem ; + if ((*argv)[1]) goto fail ; + case '+' : + case '-' : if (flagacted) { - if (!script_update(sc, &cur_selections, &cur_actions)) return 0 ; + sc++ ; flagacted = 0 ; } - selitem.type = SELTYPE_DEFAULT ; - if (!genalloc_append(sel_t, &cur_selections, &selitem)) return 0 ; + se++ ; + case 'n' : + case 's' : + case 'S' : + case 'l' : + case 'r' : + case 'E' : + case '^' : + case '!' : break ; - } + case 't' : + if ((*argv)[1]) goto fail ; + gf |= 1 ; + break ; + case 'T' : + if ((*argv)[1]) goto fail ; + gf |= 2 ; + break ; + case 'e' : + if (verbosity) strerr_warnw1x("directive e is deprecated, use 2 instead") ; + case '1' : + case '2' : + if ((*argv)[1]) goto fail ; + flagacted = 1 ; + ac++ ; + break ; + case '.' : + case '/' : + llen++ ; + flagacted = 1 ; + ac++ ; + break ; + case '=' : + if (!(*argv)[1]) goto fail ; + flagacted = 1 ; + ac++ ; + break ; + default : strerr_dief2x(100, "unrecognized directive: ", *argv) ; + } + } + if (flagacted) sc++ ; + else if (sc) + { + if (verbosity) + strerr_warnw1x("ignoring extraneous non-action directives") ; + } + else strerr_dief1x(100, "no action directive specified") ; + *sellen = se ; + *actlen = ac ; + *scriptlen = sc ; + *gflags = gf ; + return ; + fail : + strerr_dief2x(100, "syntax error at directive: ", *argv) ; +} + +static inline void script_secondpass (char const *const *argv, scriptelem_t *script, sel_t *selections, act_t *actions, unsigned int compat_gflags) +{ + tain_t retrytto ; + unsigned int fd2_size = 200 ; + unsigned int status_size = 1001 ; + uint32 s = 99999 ; + uint32 n = 10 ; + uint32 tolerance = 2000 ; + uint64 maxdirsize = 0 ; + char const *processor = 0 ; + unsigned int sel = 0, act = 0, lidx = 0, flags = 0 ; + int flagacted = 0 ; + tain_uint(&retrytto, 2) ; + + for (; *argv ; argv++) + { + switch (**argv) + { + case 'f' : case '+' : case '-' : { - sel_t selitem ; - int r ; + sel_t selitem = { .type = (*argv)[0] != 'f' ? (*argv)[0] == '+' ? SELTYPE_PLUS : SELTYPE_MINUS : SELTYPE_DEFAULT } ; + if ((*argv)[0] != 'f') + { + int r = regcomp(&selitem.re, *argv + 1, REG_EXTENDED | REG_NOSUB | REG_NEWLINE) ; + if (r == REG_ESPACE) + { + errno = ENOMEM ; + strerr_diefu1sys(111, "initialize script") ; + } + if (!r) goto fail ; + } + selections[sel++] = selitem ; if (flagacted) { - if (!script_update(sc, &cur_selections, &cur_actions)) return 0 ; flagacted = 0 ; + script->sels = selections ; + script->sellen = sel ; + script->acts = actions ; + script->actlen = act ; + selections += sel ; sel = 0 ; + actions += act ; act = 0 ; + script++ ; } - selitem.type = (**argv == '+') ? SELTYPE_PLUS : SELTYPE_MINUS ; - r = regcomp(&selitem.re, *argv+1, REG_EXTENDED | REG_NOSUB | REG_NEWLINE) ; - if (r == REG_ESPACE) return (errno = ENOMEM, 0) ; - if (r) goto fail ; - if (!genalloc_append(sel_t, &cur_selections, &selitem)) return 0 ; break ; } case 'n' : - { - if (!uint320_scan(*argv + 1, &cur_n)) goto fail ; + if (!uint320_scan(*argv + 1, &n)) goto fail ; break ; - } case 's' : - { - if (!uint320_scan(*argv + 1, &cur_s)) goto fail ; - if (cur_s < 4096) cur_s = 4096 ; - if (cur_s > 16777215) cur_s = 16777215 ; + if (!uint320_scan(*argv + 1, &s)) goto fail ; + if (s < 4096) s = 4096 ; + if (s > 16777215) s = 16777215 ; break ; - } case 'S' : - { - if (!uint640_scan(*argv + 1, &cur_maxdirsize)) goto fail ; + if (!uint640_scan(*argv + 1, &maxdirsize)) goto fail ; break ; - } case 'l' : - { - if (!uint320_scan(*argv + 1, &cur_tolerance)) goto fail ; - if (cur_tolerance > (cur_s >> 1)) + if (!uint320_scan(*argv + 1, &tolerance)) goto fail ; + if (tolerance > (s >> 1)) strerr_dief3x(100, "directive ", *argv, " conflicts with previous s directive") ; break ; - } case 'r' : { uint32 t ; if (!uint320_scan(*argv + 1, &t)) goto fail ; - if (!tain_from_millisecs(&cur_retrytto, (int)t)) return (errno = EINVAL, 0) ; + if (!tain_from_millisecs(&retrytto, t)) goto fail ; break ; } case 'E' : - { - if (!uint0_scan(*argv + 1, &cur_fd2_size)) goto fail ; + if (!uint0_scan(*argv + 1, &fd2_size)) goto fail ; break ; - } case '^' : - { - if (!uint0_scan(*argv + 1, &cur_status_size)) goto fail ; + if (!uint0_scan(*argv + 1, &status_size)) goto fail ; break ; - } case '!' : + processor = (*argv)[1] ? *argv + 1 : 0 ; + break ; + case 't' : + flags |= 1 ; + break ; + case 'T' : + flags |= 2 ; + break ; + case '1' : { - cur_processor = (*argv)[1] ? *argv + 1 : 0 ; + act_t a = { .type = ACTTYPE_FD1, .flags = flags } ; + actions[act++] = a ; flagacted = 1 ; flags = 0 ; break ; } case 'e' : + case '2' : { - act_t a ; - flagacted = 1 ; - a.type = ACTTYPE_FD2 ; - a.data.fd2.size = cur_fd2_size ; - if (!genalloc_append(act_t, &cur_actions, &a)) return 0 ; + act_t a = { .type = ACTTYPE_FD2, .flags = flags, .data = { .fd2_size = fd2_size } } ; + if (compat_gflags & 2) a.flags |= 1 ; + actions[act++] = a ; flagacted = 1 ; flags = 0 ; break ; } case '=' : { - act_t a ; - flagacted = 1 ; - a.type = ACTTYPE_STATUS ; - a.data.status.file = *argv + 1 ; - a.data.status.content = stralloc_zero ; - if (cur_status_size && !stralloc_ready_tuned(&a.data.status.content, cur_status_size, 0, 0, 1)) return 0 ; - a.data.status.content.len = cur_status_size ; - if (!genalloc_append(act_t, &cur_actions, &a)) return 0 ; + act_t a = { .type = ACTTYPE_STATUS, .flags = flags, .data = { .status = { .file = *argv + 1, .filelen = status_size } } } ; + actions[act++] = a ; flagacted = 1 ; flags = 0 ; break ; } case '.' : case '/' : { - act_t a ; - logdir_t ld = LOGDIR_ZERO ; - flagacted = 1 ; - a.type = ACTTYPE_DIR ; - a.data.dir.lindex = genalloc_len(logdir_t, &logdirs) ; - if (!genalloc_append(act_t, &cur_actions, &a)) return 0 ; - logdir_init(&ld, cur_s, cur_n, cur_tolerance, cur_maxdirsize, &cur_retrytto, cur_processor, *argv, genalloc_len(logdir_t, &logdirs)) ; - if (!genalloc_append(logdir_t, &logdirs, &ld)) return 0 ; + act_t a = { .type = ACTTYPE_DIR, .flags = flags, .data = { .ld = lidx } } ; + if (compat_gflags & 1) a.flags |= 1 ; + logdir_init(lidx, s, n, tolerance, maxdirsize, &retrytto, processor, *argv, flags) ; + lidx++ ; + actions[act++] = a ; flagacted = 1 ; flags = 0 ; break ; } default : goto fail ; @@ -851,87 +897,51 @@ static inline int script_init (genalloc *sc, char const *const *argv) } if (flagacted) { - if (!script_update(sc, &cur_selections, &cur_actions)) return 0 ; + script->sels = selections ; + script->sellen = sel ; + script->acts = actions ; + script->actlen = act ; } - else - { - genalloc_deepfree(sel_t, &cur_selections, &sel_free) ; - if (verbosity) strerr_warnw1x("ignoring extraneous non-action directives") ; - } - genalloc_shrink(logdir_t, &logdirs) ; - genalloc_shrink(scriptelem_t, sc) ; - if (!genalloc_len(scriptelem_t, sc)) - strerr_dief1x(100, "no action directive specified") ; - return 1 ; + return ; fail: strerr_dief2x(100, "unrecognized directive: ", *argv) ; } -static inline void doit_fd2 (as_fd2_t const *ap, char const *s, unsigned int len) +static void script_run (scriptelem_t const *script, unsigned int scriptlen, char const *s, unsigned int len, unsigned int gflags) { - if (flagstampalert) + int flagselected = 1, flagacted = 0 ; + unsigned int i = 0, hlen = 0 ; + char hstamp[32] ; + char tstamp[TIMESTAMP] ; + if (gflags & 1) { - char fmt[TIMESTAMP+1] ; - tain_now_g() ; - timestamp_g(fmt) ; - fmt[TIMESTAMP] = ' ' ; - buffer_put(buffer_2, fmt, TIMESTAMP+1) ; + timestamp_g(tstamp) ; + tstamp[TIMESTAMP-1] = ' ' ; } - buffer_puts(buffer_2, PROG) ; - buffer_puts(buffer_2, ": alert: ") ; - if (ap->size && len > ap->size) len = ap->size ; - buffer_put(buffer_2, s, len) ; - if (len == ap->size) buffer_puts(buffer_2, "...") ; - buffer_putflush(buffer_2, "\n", 1) ; -} - -static inline void doit_status (as_status_t const *ap, char const *s, unsigned int len) -{ - if (ap->content.len) + if (gflags & 2) { - register unsigned int i ; - if (len > ap->content.len) len = ap->content.len ; - byte_copy(ap->content.s, len, s) ; - for (i = len ; i < ap->content.len ; i++) ap->content.s[i] = '\n' ; - if (!openwritenclose_suffix_sync(ap->file, ap->content.s, ap->content.len, ".new")) - strerr_warnwu2sys("openwritenclose ", ap->file) ; + localtmn_t l ; + localtmn_from_tain_g(&l, 1) ; + hlen = localtmn_fmt(hstamp, &l) ; + hstamp[hlen++] = ' ' ; + hstamp[hlen++] = ' ' ; } - else if (!openwritenclose_suffix_sync(ap->file, s, len, ".new")) - strerr_warnwu2sys("openwritenclose ", ap->file) ; -} - -static inline void doit_dir (as_dir_t const *ap, char const *s, unsigned int len) -{ - logdir_t_ref ldp = genalloc_s(logdir_t, &logdirs) + ap->lindex ; - if (!bufalloc_put(&ldp->out, s, len) || !bufalloc_put(&ldp->out, "\n", 1)) - strerr_diefu1sys(111, "bufalloc_put") ; -} - - - /* The script interpreter. */ - -static inline void doit (scriptelem_t const *se, unsigned int n, char const *s, unsigned int len) -{ - int flagselected = 1 ; - int flagacted = 0 ; - unsigned int i = 0 ; - for (; i < n ; i++) + + for (; i < scriptlen ; i++) { - unsigned int sellen = genalloc_len(sel_t, &se[i].selections) ; - sel_t *sels = genalloc_s(sel_t, &se[i].selections) ; unsigned int j = 0 ; - for (; j < sellen ; j++) + for (; j < script[i].sellen ; j++) { - switch (sels[j].type) + switch (script[i].sels[j].type) { case SELTYPE_DEFAULT : flagselected = !flagacted ; break ; case SELTYPE_PLUS : - if (!flagselected && !regexec(&sels[j].re, flagstamp ? s+TIMESTAMP+1 : s, 0, 0, 0)) flagselected = 1 ; + if (!flagselected && !regexec(&script[i].sels[j].re, s, 0, 0, 0)) flagselected = 1 ; break ; case SELTYPE_MINUS : - if (flagselected && !regexec(&sels[j].re, flagstamp ? s+TIMESTAMP+1 : s, 0, 0, 0)) flagselected = 0 ; + if (flagselected && !regexec(&script[i].sels[j].re, s, 0, 0, 0)) flagselected = 0 ; break ; default : strerr_dief2x(101, "internal consistency error in ", "selection type") ; @@ -939,52 +949,67 @@ static inline void doit (scriptelem_t const *se, unsigned int n, char const *s, } if (flagselected) { - unsigned int actlen = genalloc_len(act_t, &se[i].actions) ; - act_t *acts = genalloc_s(act_t, &se[i].actions) ; flagacted = 1 ; - for (j = 0 ; j < actlen ; j++) + for (j = 0 ; j < script[i].actlen ; j++) { - switch (acts[j].type) + act_t const *act = script[i].acts + j ; + siovec_t v[4] = { { .s = tstamp, .len = act->flags & 1 ? TIMESTAMP : 0 }, { .s = hstamp, .len = act->flags & 2 ? hlen : 0 }, { .s = (char *)s, .len = len }, { .s = "\n", .len = 1 } } ; + switch (act->type) { + case ACTTYPE_FD1 : + if (!bufalloc_putv(bufalloc_1, v, 4)) dienomem() ; + case ACTTYPE_NOTHING : + break ; + case ACTTYPE_FD2 : - doit_fd2(&acts[j].data.fd2, s, len) ; + buffer_puts(buffer_2, PROG) ; + buffer_puts(buffer_2, ": alert: ") ; + if (act->data.fd2_size && act->data.fd2_size + 3 < len) + { + v[2].len = act->data.fd2_size ; + v[3].s = "...\n" ; + v[3].len = 4 ; + } + buffer_putv(buffer_2, v, 4) ; + buffer_flush(buffer_2) ; /* if it blocks, too bad */ break ; + case ACTTYPE_STATUS : - doit_status(&acts[j].data.status, s, len) ; + if (act->data.status.filelen) + { + unsigned int reallen = siovec_len(v, 4) ; + if (reallen > act->data.status.filelen) + siovec_trunc(v, 4, act->data.status.filelen) ; + else + { + register unsigned int k = act->data.status.filelen - reallen + 1 ; + char pad[k] ; + v[3].s = pad ; + v[3].len = k ; + while (k--) pad[k] = '\n' ; + if (!openwritevnclose_suffix(act->data.status.file, v, 4, ".new") && verbosity) + strerr_warnwu2sys("write status file ", act->data.status.file) ; + break ; + } + } + if (!openwritevnclose_suffix(act->data.status.file, v, 4, ".new") && verbosity) + strerr_warnwu2sys("write status file ", act->data.status.file) ; break ; + case ACTTYPE_DIR : - doit_dir(&acts[j].data.dir, s, len) ; + if (!bufalloc_putv(&logdirs[act->data.ld].out, v, 4)) dienomem() ; break ; + default : strerr_dief2x(101, "internal consistency error in ", "action type") ; } } } } - if (flagstamp) tain_now_g() ; } -static inline void processor_died (logdir_t_ref ldp, int wstat) -{ - ldp->pid = 0 ; - if (WIFSIGNALED(wstat)) - { - if (verbosity) strerr_warnw2x("processor crashed in ", ldp->dir) ; - tain_add_g(&ldp->deadline, &ldp->retrytto) ; - ldp->rstate = ROTSTATE_RUNPROCESSOR ; - } - else if (WEXITSTATUS(wstat)) - { - if (verbosity) strerr_warnw2x("processor failed in ", ldp->dir) ; - tain_add_g(&ldp->deadline, &ldp->retrytto) ; - ldp->rstate = ROTSTATE_RUNPROCESSOR ; - } - else - { - ldp->rstate = ROTSTATE_SYNCPROCESSED ; - rotator(ldp) ; - } -} + + /* Input */ static void prepare_to_exit (void) { @@ -992,15 +1017,7 @@ static void prepare_to_exit (void) flagexiting = 1 ; } -static void stampanddoit (scriptelem_t const *se, unsigned int n) -{ - if (flagstamp) indata.s[timestamp_g(indata.s)] = ' ' ; - indata.s[indata.len] = 0 ; - doit(se, n, indata.s, indata.len-1) ; - indata.len = flagstamp ? TIMESTAMP+1 : 0 ; -} - -static void normal_stdin (scriptelem_t const *se, unsigned int selen) +static void normal_stdin (scriptelem_t const *script, unsigned int scriptlen, unsigned int linelimit, unsigned int gflags) { int r = sanitize_read(buffer_fill(buffer_0)) ; if (r < 0) @@ -1009,11 +1026,24 @@ static void normal_stdin (scriptelem_t const *se, unsigned int selen) prepare_to_exit() ; } else if (r) + { while (skagetln_nofill(buffer_0, &indata, '\n') > 0) - stampanddoit(se, selen) ; + { + indata.s[indata.len - 1] = 0 ; + script_run(script, scriptlen, indata.s, indata.len - 1, gflags) ; + indata.len = 0 ; + } + if (linelimit && indata.len > linelimit) + { + if (!stralloc_0(&indata)) dienomem() ; + if (verbosity) strerr_warnw2x("input line too long, ", "inserting a newline") ; + script_run(script, scriptlen, indata.s, indata.len - 1, gflags) ; + indata.len = 0 ; + } + } } -static void last_stdin (scriptelem_t const *se, unsigned int selen) +static void last_stdin (scriptelem_t const *script, unsigned int scriptlen, unsigned int linelimit, unsigned int gflags) { int cont = 1 ; while (cont) @@ -1026,21 +1056,27 @@ static void last_stdin (scriptelem_t const *se, unsigned int selen) break ; case -1 : if ((errno != EPIPE) && verbosity) strerr_warnwu1sys("read from stdin") ; - if (indata.len <= (flagstamp ? TIMESTAMP+1 : 0)) + if (!indata.len) { prepare_to_exit() ; cont = 0 ; break ; } + addfinalnewline: c = '\n' ; case 1 : if (!stralloc_catb(&indata, &c, 1)) dienomem() ; if (c == '\n') { - stampanddoit(se, selen) ; + script_run(script, scriptlen, indata.s, indata.len - 1, gflags) ; prepare_to_exit() ; cont = 0 ; } + else if (linelimit && indata.len > linelimit) + { + if (verbosity) strerr_warnw2x("input line too long, ", "stopping before the end") ; + goto addfinalnewline ; + } break ; } } @@ -1048,6 +1084,31 @@ static void last_stdin (scriptelem_t const *se, unsigned int selen) static inputprocfunc_t_ref handle_stdin = &normal_stdin ; + + /* Signals */ + +static inline void processor_died (logdir_t *ldp, int wstat) +{ + ldp->pid = 0 ; + if (WIFSIGNALED(wstat)) + { + if (verbosity) strerr_warnw2x("processor crashed in ", ldp->dir) ; + tain_add_g(&ldp->deadline, &ldp->retrytto) ; + ldp->rstate = ROTSTATE_RUNPROCESSOR ; + } + else if (WEXITSTATUS(wstat)) + { + if (verbosity) strerr_warnw2x("processor failed in ", ldp->dir) ; + tain_add_g(&ldp->deadline, &ldp->retrytto) ; + ldp->rstate = ROTSTATE_RUNPROCESSOR ; + } + else + { + ldp->rstate = ROTSTATE_SYNCPROCESSED ; + rotator(ldp) ; + } +} + static inline void handle_signals (void) { for (;;) @@ -1058,14 +1119,12 @@ static inline void handle_signals (void) case 0 : return ; case SIGALRM : { - unsigned int llen = genalloc_len(logdir_t, &logdirs) ; - logdir_t *ls = genalloc_s(logdir_t, &logdirs) ; register unsigned int i = 0 ; for (i = 0 ; i < llen ; i++) - if ((ls[i].rstate == ROTSTATE_WRITABLE) && ls[i].b) + if ((logdirs[i].rstate == ROTSTATE_WRITABLE) && logdirs[i].b) { - ls[i].rstate = ROTSTATE_START ; - rotator(ls + i) ; + logdirs[i].rstate = ROTSTATE_START ; + rotator(logdirs + i) ; } break ; } @@ -1073,21 +1132,19 @@ static inline void handle_signals (void) { if (flagprotect) break ; handle_stdin = &last_stdin ; - if (indata.len <= (flagstamp ? TIMESTAMP+1 : 0)) prepare_to_exit() ; + if (!indata.len) prepare_to_exit() ; break ; } case SIGCHLD : { - unsigned int llen = genalloc_len(logdir_t, &logdirs) ; - logdir_t *ls = genalloc_s(logdir_t, &logdirs) ; for (;;) { int wstat ; register unsigned int i = 0 ; register int r = wait_nohang(&wstat) ; if (r <= 0) break ; - for (; i < llen ; i++) if ((unsigned int)r == ls[i].pid) break ; - if (i < llen) processor_died(ls + i, wstat) ; + for (; i < llen ; i++) if ((unsigned int)r == logdirs[i].pid) break ; + if (i < llen) processor_died(logdirs + i, wstat) ; } break ; } @@ -1096,73 +1153,20 @@ static inline void handle_signals (void) } } -static inline int logdir_finalize (logdir_t_ref ldp) -{ - switch (ldp->rstate) - { - case ROTSTATE_WRITABLE : - { - if (fd_sync(ldp->fd) < 0) - { - if (verbosity) strerr_warnwu3sys("fd_sync ", ldp->dir, "/current") ; - goto fail ; - } - tain_now_g() ; - ldp->rstate = ROTSTATE_ENDFCHMOD ; - } - case ROTSTATE_ENDFCHMOD : - { - if (fd_chmod(ldp->fd, S_IRWXU | S_IRGRP | S_IROTH) < 0) - { - if (verbosity) strerr_warnwu3sys("fd_chmod ", ldp->dir, "/current to 0744") ; - goto fail ; - } - ldp->rstate = ROTSTATE_END ; - break ; - } - default : strerr_dief1x(101, "inconsistent state in logdir_finalize()") ; - } - return 1 ; - fail: - tain_add_g(&ldp->deadline, &ldp->retrytto) ; - return 0 ; -} -static inline void finalize (void) -{ - unsigned int llen = genalloc_len(logdir_t, &logdirs) ; - logdir_t *ls = genalloc_s(logdir_t, &logdirs) ; - unsigned int n = llen ; - for (;;) - { - unsigned int i = 0 ; - tain_t deadline ; - tain_addsec_g(&deadline, 2) ; - for (; i < llen ; i++) - if (ls[i].rstate != ROTSTATE_END) - { - if (logdir_finalize(ls + i)) n-- ; - else if (tain_less(&ls[i].deadline, &deadline)) - deadline = ls[i].deadline ; - } - if (!n) break ; - { - iopause_fd x ; - iopause_g(&x, 0, &deadline) ; - } - } -} + /* Main */ int main (int argc, char const *const *argv) { - genalloc logscript = GENALLOC_ZERO ; /* array of scriptelem_t */ + unsigned int sellen, actlen, scriptlen ; + unsigned int linelimit = 0, gflags = 0, compat_gflags = 0 ; int flagblock = 0 ; PROG = "s6-log" ; { subgetopt_t l = SUBGETOPT_ZERO ; for (;;) { - register int opt = subgetopt_r(argc, argv, "qvbpte", &l) ; + register int opt = subgetopt_r(argc, argv, "qvbptel:", &l) ; if (opt == -1) break ; switch (opt) { @@ -1170,97 +1174,126 @@ int main (int argc, char const *const *argv) case 'v' : verbosity++ ; break ; case 'b' : flagblock = 1 ; break ; case 'p' : flagprotect = 1 ; break ; - case 't' : flagstamp = 1 ; break ; - case 'e' : flagstampalert = 1 ; break ; - default : strerr_dieusage(100, USAGE) ; + case 't' : gflags |= 1 ; compat_gflags |= 1 ; break ; + case 'e' : gflags |= 1 ; compat_gflags |= 2 ; break ; + case 'l' : if (!uint0_scan(l.arg, &linelimit)) dieusage() ; break ; + default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; } - if (argc < 1) strerr_dieusage(100, USAGE) ; - - fd_close(1) ; + if (!argc) dieusage() ; + if (linelimit && linelimit < LINELIMIT_MIN) linelimit = LINELIMIT_MIN ; + if (compat_gflags && verbosity) strerr_warnw1x("options -t and -e are deprecated") ; + if (!fd_sanitize()) strerr_diefu1sys(111, "ensure stdin/stdout/stderr are open") ; + if (!tain_now_g() && verbosity) strerr_warnwu1sys("read current time - timestamps may be wrong for a while") ; + if (ndelay_on(0) < 0) strerr_diefu3sys(111, "set std", "in", " non-blocking") ; + if (ndelay_on(1) < 0) strerr_diefu3sys(111, "set std", "out", " non-blocking") ; + script_firstpass(argv, &sellen, &actlen, &scriptlen, &gflags) ; { - int r = tain_now_g() ; - if (flagstamp) - { - char fmt[TIMESTAMP+1] ; - if (!stralloc_catb(&indata, fmt, TIMESTAMP+1)) dienomem() ; - if (!r) strerr_warnwu1sys("read current time - timestamps may be wrong for a while") ; - } - } - if (!script_init(&logscript, argv)) strerr_diefu1sys(111, "initialize logging script") ; - if (ndelay_on(0) < 0) strerr_diefu1sys(111, "ndelay_on(0)") ; - - { - unsigned int llen = genalloc_len(logdir_t, &logdirs) ; - logdir_t *ls = genalloc_s(logdir_t, &logdirs) ; - iopause_fd x[2 + llen] ; - unsigned int active[llen] ; - x[0].fd = 0 ; - x[1].fd = selfpipe_init() ; - if (x[1].fd < 0) strerr_diefu1sys(111, "selfpipe_init") ; + sel_t selections[sellen] ; + act_t actions[actlen] ; + scriptelem_t script[scriptlen] ; + logdir_t logdirblob[llen] ; + iopause_fd x[3 + llen] ; + logdirs = logdirblob ; + script_secondpass(argv, script, selections, actions, compat_gflags) ; + x[0].fd = selfpipe_init() ; + if (x[0].fd < 0) strerr_diefu1sys(111, "selfpipe_init") ; if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "sig_ignore(SIGPIPE)") ; { sigset_t set ; sigemptyset(&set) ; - sigaddset(&set, SIGTERM) ; sigaddset(&set, SIGALRM) ; sigaddset(&set, SIGCHLD) ; - if (selfpipe_trapset(&set) < 0) strerr_diefu1sys(111, "selfpipe_trapset") ; + sigaddset(&set, SIGTERM) ; + sigaddset(&set, SIGALRM) ; + sigaddset(&set, SIGCHLD) ; + if (selfpipe_trapset(&set) < 0) + strerr_diefu1sys(111, "selfpipe_trapset") ; } - x[1].events = IOPAUSE_READ ; + x[0].events = IOPAUSE_READ ; for (;;) { tain_t deadline ; int r ; - unsigned int j = 0 ; - unsigned int i = 0 ; - int allflushed = 1 ; + unsigned int xindex0, xindex1 ; + unsigned int i = 0, j = 1 ; tain_add_g(&deadline, &tain_infinite_relative) ; + if (bufalloc_1->fd == 1 && bufalloc_len(bufalloc_1)) + { + x[j].fd = 1 ; + x[j].events = IOPAUSE_EXCEPT | (bufalloc_len(bufalloc_1) ? IOPAUSE_WRITE : 0) ; + xindex1 = j++ ; + } + else xindex1 = 0 ; + for (; i < llen ; i++) { - if (bufalloc_len(&ls[i].out) || (ls[i].rstate != ROTSTATE_WRITABLE)) + logdirs[i].xindex = 0 ; + if (bufalloc_len(&logdirs[i].out) || (logdirs[i].rstate != ROTSTATE_WRITABLE)) { - allflushed = 0 ; - if (!tain_future(&ls[i].deadline)) + if (!tain_future(&logdirs[i].deadline)) { - x[2+j].fd = ls[i].fd ; - x[2+j].events = IOPAUSE_WRITE ; - active[j++] = i ; + x[j].fd = logdirs[i].fd ; + x[j].events = IOPAUSE_WRITE ; + logdirs[i].xindex = j++ ; } - else if (tain_less(&ls[i].deadline, &deadline)) - deadline = ls[i].deadline ; + else if (tain_less(&logdirs[i].deadline, &deadline)) + deadline = logdirs[i].deadline ; } } - if (flagexiting && allflushed) break ; - x[0].events = (allflushed || !flagblock) ? IOPAUSE_READ : 0 ; - r = iopause_g(x + flagexiting, 2 - flagexiting + j, &deadline) ; + if (!flagexiting && (!flagblock || j == 1)) + { + x[j].fd = 0 ; + x[j].events = IOPAUSE_READ ; + xindex0 = j++ ; + } + else xindex0 = 0 ; + + if (flagexiting && j == 1) break ; + + r = iopause_g(x, j, &deadline) ; if (r < 0) strerr_diefu1sys(111, "iopause") ; - else if (r) + else if (!r) continue ; + + if (x[0].revents & IOPAUSE_READ) handle_signals() ; + else if (x[0].revents & IOPAUSE_EXCEPT) strerr_dief1sys(111, "trouble with selfpipe") ; + + if (xindex1 && x[xindex1].revents) { - if (x[1].revents & IOPAUSE_READ) handle_signals() ; - else if (x[1].revents & IOPAUSE_EXCEPT) strerr_dief1sys(111, "trouble with selfpipe") ; - for (i = 0 ; i < j ; i++) - if (x[2+i].revents & IOPAUSE_WRITE) - rotate_or_flush(ls + active[i]) ; - if (!flagexiting) + if (!bufalloc_flush(bufalloc_1) && !error_isagain(errno)) { - if (x[0].revents & IOPAUSE_READ) - (*handle_stdin)(genalloc_s(scriptelem_t, &logscript), genalloc_len(scriptelem_t, &logscript)) ; - else if (x[0].revents & IOPAUSE_EXCEPT) + unsigned int i = actlen ; + fd_close(1) ; + bufalloc_1->fd = -1 ; + bufalloc_free(bufalloc_1) ; + while (i--) + if (actions[i].type == ACTTYPE_FD1) + actions[i].type = ACTTYPE_NOTHING ; + } + } + + for (i = 0 ; i < llen ; i++) + if (logdirs[i].xindex && x[logdirs[i].xindex].revents & IOPAUSE_WRITE) + rotate_or_flush(logdirs + i) ; + + if (xindex0 && x[xindex0].revents) + { + if (x[xindex0].revents & IOPAUSE_READ) + (*handle_stdin)(script, scriptlen, linelimit, gflags) ; + else + { + prepare_to_exit() ; + if (indata.len) { - prepare_to_exit() ; - if (indata.len > (flagstamp ? TIMESTAMP+1 : 0)) - { - if (!stralloc_0(&indata)) dienomem() ; - stampanddoit(genalloc_s(scriptelem_t, &logscript), genalloc_len(scriptelem_t, &logscript)) ; - } + if (!stralloc_0(&indata)) dienomem() ; + script_run(script, scriptlen, indata.s, indata.len-1, gflags) ; + indata.len = 0 ; } } } } + finalize() ; } - genalloc_deepfree(scriptelem_t, &logscript, &scriptelem_free) ; - finalize() ; return 0 ; }