summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--doc/install.html12
-rw-r--r--doc/replaceinit.html4
-rwxr-xr-xetc/22
-rw-r--r--man/chpst.813
-rw-r--r--man/runsv.84
-rw-r--r--man/svlogd.852
-rw-r--r--package/CHANGES12
-rwxr-xr-xpackage/upgrade4
-rw-r--r--package/versions1
-rw-r--r--src/chpst.c37
-rw-r--r--src/runsv.c113
-rw-r--r--src/svlogd.c438
-rwxr-xr-xsrc/svlogd.check6
14 files changed, 433 insertions, 267 deletions
diff --git a/Makefile b/Makefile
index f60561a..be63ecf 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 DESTDIR=
 
-PACKAGE=runit-1.0.5
+PACKAGE=runit-1.1.0
 DIRS=doc man etc package src
 MANPAGES=runit.8 runit-init.8 runsvdir.8 runsv.8 svwaitdown.8 svwaitup.8 \
 utmpset.8 runsvchdir.8 runsvstat.8 runsvctrl.8 svlogd.8 chpst.8
diff --git a/doc/install.html b/doc/install.html
index 8a86e88..8e59a3a 100644
--- a/doc/install.html
+++ b/doc/install.html
@@ -17,14 +17,14 @@ If you don't have a <tt>/package</tt> directory, create it now:
   # chmod 1755 /package
 </pre>
 Download
-<a href="runit-1.0.5.tar.gz">runit-1.0.5.tar.gz</a> into <tt>/package</tt>
+<a href="runit-1.1.0.tar.gz">runit-1.1.0.tar.gz</a> into <tt>/package</tt>
 and unpack the archive
 <pre>
   # cd /package
-  # gunzip runit-1.0.5.tar
-  # tar -xpf runit-1.0.5.tar
-  # rm runit-1.0.5.tar
-  # cd admin/runit-1.0.5
+  # gunzip runit-1.1.0.tar
+  # tar -xpf runit-1.1.0.tar
+  # rm runit-1.1.0.tar
+  # cd admin/runit-1.1.0
 </pre>
 On MacOSX, do
 <pre>
@@ -43,7 +43,7 @@ hierarchy, do:
 </pre>
 To report success:
 <pre>
-  # mail pape-runit-1.0.5@smarden.org &lt;compile/sysdeps
+  # mail pape-runit-1.1.0@smarden.org &lt;compile/sysdeps
 </pre>
 If you use <i>runit</i> regularly, please
 <a href="http://smarden.org/pape/#contribution">contribute</a> to the project.
diff --git a/doc/replaceinit.html b/doc/replaceinit.html
index 43c1296..b97627d 100644
--- a/doc/replaceinit.html
+++ b/doc/replaceinit.html
@@ -117,7 +117,7 @@ default Unix process no 1 <i>runit</i>.
 </pre>
 To report success:
 <pre>
- # ( uname -a ; cat /etc/runit/[123] ) |mail pape-runit-1.0.5@smarden.org
+ # ( uname -a ; cat /etc/runit/[123] ) |mail pape-runit-1.1.0@smarden.org
 </pre>
 <hr>
 
@@ -206,7 +206,7 @@ This will cause <i>runit</i> to enter stage 3 which runs
 <p>
 To report success:
 <pre>
- # ( uname -a ; cat /etc/runit/[123] ) |mail pape-runit-1.0.5@smarden.org
+ # ( uname -a ; cat /etc/runit/[123] ) |mail pape-runit-1.1.0@smarden.org
 </pre>
 <h3>Step 5: Service migration</h3>
 The goal is to migrate all services from <i>/etc/rc.*</i> scheme to the
diff --git a/etc/2 b/etc/2
index 233ad00..9720c3e 100755
--- a/etc/2
+++ b/etc/2
@@ -3,4 +3,4 @@
 PATH=/command:/usr/local/bin:/usr/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/X11R6/bin
 
 exec env - PATH=$PATH \
-runsvdir /var/service 'log: ...........................................................................................................................................................................................................................................................................................................................................................................................................'
+runsvdir -P /var/service 'log: ...........................................................................................................................................................................................................................................................................................................................................................................................................'
diff --git a/man/chpst.8 b/man/chpst.8
index 6ca593f..ae158cb 100644
--- a/man/chpst.8
+++ b/man/chpst.8
@@ -12,6 +12,8 @@ chpst \- runs a program with a changed process state
 .IR dir ]
 [\-/
 .IR root ]
+[\-n
+.IR inc ]
 [-l|-L
 .IR lock ]
 [-m
@@ -105,6 +107,17 @@ Change the root directory to
 before starting
 .IR prog .
 .TP
+.B \-n \fIinc
+nice.
+Add
+.I inc
+to the
+.BR nice (2)
+value before starting
+.IR prog .
+.I inc
+must be an integer, and may start with a minus or plus.
+.TP
 .B \-l \fIlock
 lock.
 Open the file
diff --git a/man/runsv.8 b/man/runsv.8
index 769c7f2..eafad0c 100644
--- a/man/runsv.8
+++ b/man/runsv.8
@@ -79,7 +79,7 @@ If the service stops, restart it.
 .TP
 .B d
 Down.
-If the service is running, send it a TERM signal.
+If the service is running, send it a TERM signal, and then a CONT signal.
 If ./run exits, start ./finish if it exists.
 After it stops, do not restart service.
 .TP
@@ -129,7 +129,7 @@ If the service is running, send it a KILL signal.
 .TP
 .B x \fRor \fBe
 Exit.
-If the service is running, send it a TERM signal.
+If the service is running, send it a TERM signal, and then a CONT signal.
 Do not restart the service.
 If the service is down, and no log service exists,
 .B runsv
diff --git a/man/svlogd.8 b/man/svlogd.8
index 25319bf..86a901f 100644
--- a/man/svlogd.8
+++ b/man/svlogd.8
@@ -73,7 +73,8 @@ bytes or more (or there is a new-line within the last
 .I len
 of
 .I size
-bytes)
+bytes), or is older than a specified amount of
+.IR time ,
 .I current
 is rotated:
 .P
@@ -192,6 +193,40 @@ old log files in
 after log file rotation, it deletes the oldest one.
 Default is 10.
 .TP
+.RI N min
+sets the minimum number of old log files
+.B svlogd
+should maintain to
+.IR min .
+.I min
+must be less than
+.IR num .
+If
+.I min
+is set, and
+.B svlogd
+cannot write to
+.I current
+because the filesystem is full, and it sees more than
+.I min
+old log files, it deletes the oldest one.
+.TP
+.RI t timeout
+sets the maximum age of the
+.I current
+log file when
+.B svlogd
+should rotate the current log file to
+.I timeout
+seconds.
+If
+.I current
+is
+.I timeout
+seconds old, and is not empty,
+.B svlogd
+forces log file rotation.
+.TP
 .RI ! processor
 tells
 .B svlogd
@@ -248,21 +283,20 @@ and acts accordingly:
 the log message is deselected.
 .TP
 .RI + pattern
-the log message is selected
+the log message is selected.
 .TP
 .RI e pattern
-log messages matching
-.I pattern
-are printed to standard error.
+the log message is selected to be printed to standard error.
 .TP
 .RI E pattern
-log messages not matching
-.I pattern
-are printed to standard error.
+the log message is deselected to be printed to standard error.
 .P
-Initially each line is selected.
+Initially each line is selected to be written to
+.IR log/current .
 Deselected log messages are discarded from
 .IR log .
+Initially each line is deselected to be written to standard err.
+Log messages selected for standard error are written to standard error.
 .SH PATTERN MATCHING
 .B svlogd
 matches a log message against the string
diff --git a/package/CHANGES b/package/CHANGES
index a5a19f7..287f5ef 100644
--- a/package/CHANGES
+++ b/package/CHANGES
@@ -1,3 +1,15 @@
+runit 1.1.0
+Sat, 06 Nov 2004 15:38:54 +0000
+  * svlogd.c: new config option t timeout; config options e and E select and
+    deselect lines for stderr respectively; new config option N.
+  * man/svlogd.8: adapt.
+  * runsv.c: on commands down and exit send CONT after TERM.
+  * man/runsv.8: adapt.
+  * etc/2: use -P option to runsvdir.
+  * src/svlogd.check: end check for t config option.
+  * chpst.c: new option -n: adjust nice level.
+  * man/chpst.8: adapt.
+
 runit 1.0.5
 Tue, 21 Sep 2004 18:18:14 +0000
   * svlogd.c: fix line buffer handling for pattern matching (thx Enrico
diff --git a/package/upgrade b/package/upgrade
index 3f2f54b..0b03113 100755
--- a/package/upgrade
+++ b/package/upgrade
@@ -8,9 +8,9 @@ test -d src || sh -cx '! : Wrong working directory.'
 here=`env - PATH=$PATH pwd`
 parent=`dirname $here`
 
-echo 'Creating symlink runit -> runit-1.0.5...'
+echo 'Creating symlink runit -> runit-1.1.0...'
 rm -f runit
-ln -s runit-1.0.5 runit
+ln -s runit-1.1.0 runit
 mv -f runit ..
 
 echo 'Making command links in /command...'
diff --git a/package/versions b/package/versions
index fe836f0..69d30a0 100644
--- a/package/versions
+++ b/package/versions
@@ -29,3 +29,4 @@
 1.0.3
 1.0.4
 1.0.5
+1.1.0
diff --git a/src/chpst.c b/src/chpst.c
index 1b0e33e..5ab405a 100644
--- a/src/chpst.c
+++ b/src/chpst.c
@@ -20,7 +20,7 @@
 #include "openreadclose.h"
 #include "direntry.h"
 
-#define USAGE_MAIN " [-vP012] [-u user[:group]] [-U user[:group]] [-e dir] [-/ root] [-l|-L lock] [-m n] [-o n] [-p n] [-f n] [-c n] prog"
+#define USAGE_MAIN " [-vP012] [-u user[:group]] [-U user[:group]] [-e dir] [-/ root] [-n nice] [-l|-L lock] [-m n] [-o n] [-p n] [-f n] [-c n] prog"
 #define FATAL "chpst: fatal: "
 #define WARNING "chpst: warning: "
 
@@ -56,6 +56,7 @@ long limitf =-2;
 long limitc =-2;
 long limitr =-2;
 long limitt =-2;
+long nicelvl =0;
 const char *lock =0;
 const char *root =0;
 unsigned int lockdelay;
@@ -109,19 +110,19 @@ void edir(const char *dirname) {
     if (d->d_name[0] == '.') continue;
     if (openreadclose(d->d_name, &sa, 256) == -1) {
       if ((errno == error_isdir) && env_dir) {
-	if (verbose)
-	  strerr_warn6(WARNING, "unable to read ", dirname, "/",
-	               d->d_name, ": ", &strerr_sys);
-	continue;
+        if (verbose)
+          strerr_warn6(WARNING, "unable to read ", dirname, "/",
+                       d->d_name, ": ", &strerr_sys);
+        continue;
       }
       else
         strerr_die6sys(111, FATAL, "unable to read ", dirname, "/",
-	      	       d->d_name, ": ");
+                             d->d_name, ": ");
     }
     if (sa.len) {
       sa.len =byte_chr(sa.s, sa.len, '\n');
       while (sa.len && (sa.s[sa.len -1] == ' ' || sa.s[sa.len -1] == '\t'))
-	--sa.len;
+        --sa.len;
       for (i =0; i < sa.len; ++i) if (! sa.s[i]) sa.s[i] ='\n';
       if (! stralloc_0(&sa)) die_nomem();
       if (! pathexec_env(d->d_name, sa.s)) die_nomem();
@@ -271,8 +272,8 @@ int main(int argc, const char *const *argv) {
   if (str_equal(progname, "setlock")) setlock(argc, argv);
   if (str_equal(progname, "softlimit")) softlimit(argc, argv);
 
-  while ((opt =getopt(argc, argv, "u:U:e:m:o:p:f:c:r:t:/:l:L:vP012V"))
-	 != opteof)
+  while ((opt =getopt(argc, argv, "u:U:e:m:o:p:f:c:r:t:/:n:l:L:vP012V"))
+         != opteof)
     switch(opt) {
     case 'u': set_user =(char*)optarg; break;
     case 'U': env_user =(char*)optarg; break;
@@ -288,6 +289,18 @@ int main(int argc, const char *const *argv) {
     case 'r': if (optarg[scan_ulong(optarg, &limitr)]) usage(); break;
     case 't': if (optarg[scan_ulong(optarg, &limitt)]) usage(); break;
     case '/': root =optarg; break;
+    case 'n':
+      switch (*optarg) {
+        case '-':
+          if (optarg[scan_ulong(++optarg, &nicelvl)]) usage();
+          nicelvl *=-1;
+          break;
+        case '+': ++optarg;
+        default:
+          if (optarg[scan_ulong(optarg, &nicelvl)]) usage();
+          break;
+      }
+      break;
     case 'l': if (lock) usage(); lock =optarg; lockdelay =1; break;
     case 'L': if (lock) usage(); lock =optarg; lockdelay =0; break;
     case 'v': verbose =1; break;
@@ -314,7 +327,11 @@ int main(int argc, const char *const *argv) {
   if (nostdin) if (close(0) == -1) fatal("unable to close stdin");
   if (nostdout) if (close(1) == -1) fatal("unable to close stdout");
   if (nostderr) if (close(2) == -1) fatal("unable to close stderr");
-
+  if (nicelvl) {
+    errno =0;
+    if (nice(nicelvl) == -1)
+      if (errno) fatal("unable to set nice level");
+  }
   pathexec(argv);
   fatal2("unable to run", *argv);
   return(0);
diff --git a/src/runsv.c b/src/runsv.c
index 6072664..3e826aa 100644
--- a/src/runsv.c
+++ b/src/runsv.c
@@ -105,14 +105,14 @@ void update_status(struct svdir *s) {
     close(fd);
     if (s->islog) {
       if (rename("supervise/pid.new", "log/supervise/pid") == -1) {
-	warn("unable to rename supervise/pid.new to log/supervise/pid");
-	return;
+        warn("unable to rename supervise/pid.new to log/supervise/pid");
+        return;
       }
     }
     else {
       if (rename("supervise/pid.new", "supervise/pid") == -1) {
-	warn("unable to rename supervise/pid.new to supervise/pid");
-	return;
+        warn("unable to rename supervise/pid.new to supervise/pid");
+        return;
       }
     }
     pidchanged =0;
@@ -205,6 +205,7 @@ void stopservice(struct svdir *s) {
   if (s->pid) kill(s->pid, SIGTERM);
   s->ctrl |=C_TERM;
   update_status(s);
+  if ((s->want == W_DOWN) || (s->want == W_EXIT)) kill(s->pid, SIGCONT);
 }
 
 void startservice(struct svdir *s) {
@@ -226,16 +227,16 @@ void startservice(struct svdir *s) {
     /* child */
     if (haslog) {
       if (s->islog) {
-	if (fd_copy(0, logpipe[0]) == -1)
-	  fatal("unable to setup filedescriptor for ./log/run");
-	close(logpipe[1]);
-	if (chdir("./log") == -1)
-	  fatal("unable to change directory to ./log");	
+        if (fd_copy(0, logpipe[0]) == -1)
+          fatal("unable to setup filedescriptor for ./log/run");
+        close(logpipe[1]);
+        if (chdir("./log") == -1)
+          fatal("unable to change directory to ./log");        
       }
       else {
-	if (fd_copy(1, logpipe[1]) == -1)
-	  fatal("unable to setup filedescriptor for ./run");
-	close(logpipe[0]);
+        if (fd_copy(1, logpipe[1]) == -1)
+          fatal("unable to setup filedescriptor for ./run");
+        close(logpipe[0]);
       }
     }
     sig_uncatch(sig_child);
@@ -367,9 +368,9 @@ int main(int argc, char **argv) {
       svd[1].islog =1;
       taia_now(&svd[1].start);
       if (stat("log/down", &s) != -1)
-	svd[1].want =W_DOWN;
+        svd[1].want =W_DOWN;
       if (pipe(logpipe) == -1)
-	fatal("unable to create log pipe");
+        fatal("unable to create log pipe");
       coe(logpipe[0]);
       coe(logpipe[1]);
     }
@@ -378,13 +379,13 @@ int main(int argc, char **argv) {
   if (mkdir("supervise", 0700) == -1) {
     if ((r =readlink("supervise", buf, 256)) != -1) {
       if (r == 256)
-	fatalx("unable to readlink ./supervise: ", "name too long");
+        fatalx("unable to readlink ./supervise: ", "name too long");
       buf[r] =0;
       mkdir(buf, 0700);
     }
     else {
       if ((errno != ENOENT) && (errno != EINVAL))
-	fatal("unable to readlink ./supervise");
+        fatal("unable to readlink ./supervise");
     }
   }
   if ((svd[0].fdlock =open_append("supervise/lock")) == -1)
@@ -394,21 +395,21 @@ int main(int argc, char **argv) {
   if (haslog) {
     if (mkdir("log/supervise", 0700) == -1) {
       if ((r =readlink("log/supervise", buf, 256)) != -1) {
-	if (r == 256)
-	  fatalx("unable to readlink ./log/supervise: ", "name too long");
-	buf[r] =0;
-	if ((fd =open_read(".")) == -1)
-	  fatal("unable to open current directory");
-	if (chdir("./log") == -1)
-	  fatal("unable to change directory to ./log"); 
-	mkdir(buf, 0700);
-	if (fchdir(fd) == -1)
-	  fatal("unable to change back to service directory");
-	close(fd);
+        if (r == 256)
+          fatalx("unable to readlink ./log/supervise: ", "name too long");
+        buf[r] =0;
+        if ((fd =open_read(".")) == -1)
+          fatal("unable to open current directory");
+        if (chdir("./log") == -1)
+          fatal("unable to change directory to ./log"); 
+        mkdir(buf, 0700);
+        if (fchdir(fd) == -1)
+          fatal("unable to change back to service directory");
+        close(fd);
       }
       else {
-	if ((errno != ENOENT) && (errno != EINVAL))
-	  fatal("unable to readlink ./log/supervise");
+        if ((errno != ENOENT) && (errno != EINVAL))
+          fatal("unable to readlink ./log/supervise");
       }
     }
     if ((svd[1].fdlock =open_append("log/supervise/lock")) == -1)
@@ -456,7 +457,7 @@ int main(int argc, char **argv) {
       if (! svd[1].pid && (svd[1].want == W_UP)) startservice(&svd[1]);
     if (! svd[0].pid)
       if ((svd[0].want == W_UP) || (svd[0].state == S_FINISH))
-	startservice(&svd[0]);
+        startservice(&svd[0]);
 
     x[0].fd =selfpipe[0];
     x[0].events =IOPAUSE_READ;
@@ -486,29 +487,29 @@ int main(int argc, char **argv) {
       if (!child) break;
       if ((child == -1) && (errno != error_intr)) break;
       if (child == svd[0].pid) {
-	svd[0].pid =0;
-	pidchanged =1;
-	svd[0].ctrl &=~C_TERM;
-	taia_now(&svd[0].start);
-	if (svd[0].state != S_FINISH)
-	  if ((fd =open_read("finish")) != -1) {
-	    close(fd);
-	    svd[0].state =S_FINISH;
-	    update_status(&svd[0]);
-	    break;
-	  }
-	svd[0].state =S_DOWN;
-	update_status(&svd[0]);
+        svd[0].pid =0;
+        pidchanged =1;
+        svd[0].ctrl &=~C_TERM;
+        taia_now(&svd[0].start);
+        if (svd[0].state != S_FINISH)
+          if ((fd =open_read("finish")) != -1) {
+            close(fd);
+            svd[0].state =S_FINISH;
+            update_status(&svd[0]);
+            break;
+          }
+        svd[0].state =S_DOWN;
+        update_status(&svd[0]);
       }
       if (haslog) {
-	if (child == svd[1].pid) {
-	  svd[1].pid =0;
-	  pidchanged =1;
-	  svd[1].state =S_DOWN;
-	  svd[1].ctrl &=~C_TERM;
-	  taia_now(&svd[1].start);
-	  update_status(&svd[1]);
-	}
+        if (child == svd[1].pid) {
+          svd[1].pid =0;
+          pidchanged =1;
+          svd[1].state =S_DOWN;
+          svd[1].ctrl &=~C_TERM;
+          taia_now(&svd[1].start);
+          update_status(&svd[1]);
+        }
       }
     }
     if (read(svd[0].fdcontrol, &ch, 1) == 1) ctrl(&svd[0], ch);
@@ -518,11 +519,11 @@ int main(int argc, char **argv) {
     if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
       if (svd[1].pid == 0) _exit(0);
       if (svd[1].want != W_EXIT) {
-	svd[1].want =W_EXIT;
-	/* stopservice(&svd[1]); */
-	update_status(&svd[1]);
-	if (close(logpipe[1]) == -1) warn("unable to close logpipe[1]");
-	if (close(logpipe[0]) == -1) warn("unable to close logpipe[0]");
+        svd[1].want =W_EXIT;
+        /* stopservice(&svd[1]); */
+        update_status(&svd[1]);
+        if (close(logpipe[1]) == -1) warn("unable to close logpipe[1]");
+        if (close(logpipe[0]) == -1) warn("unable to close logpipe[0]");
       }
     }
   }
diff --git a/src/svlogd.c b/src/svlogd.c
index 7b9275a..f6cf68b 100644
--- a/src/svlogd.c
+++ b/src/svlogd.c
@@ -31,8 +31,9 @@
 #include "taia.h"
 #include "fmt.h"
 #include "ndelay.h"
+#include "iopause.h"
 
-#define USAGE " [-tv] [-r c] [-R abc] [-l n ] [-b n] dir ..."
+#define USAGE " [-ttv] [-r c] [-R abc] [-l len] [-b buflen] dir ..."
 #define VERSION "$Id$"
 
 #define FATAL "svlogd: fatal: "
@@ -57,6 +58,7 @@ struct stat st;
 stralloc sa;
 int wstat;
 struct taia now;
+struct taia trotate;
 
 char *databuf;
 buffer data;
@@ -66,7 +68,9 @@ unsigned int exitasap =0;
 unsigned int rotateasap =0;
 unsigned int reopenasap =0;
 unsigned int linecomplete =1;
+unsigned int tmaxflag =0;
 int fdudp =-1;
+iopause_fd in;
 
 struct logdir {
   int fddir;
@@ -76,6 +80,9 @@ struct logdir {
   unsigned long size;
   unsigned long sizemax;
   unsigned long nmax;
+  unsigned long nmin;
+  unsigned long tmax;
+  struct taia trotate;
   stralloc processor;
   int ppid;
   char fnsave[FMT_PTIME];
@@ -83,6 +90,7 @@ struct logdir {
   int fdcur;
   int fdlock;
   char match;
+  char matcherr;
   struct sockaddr_in udpaddr;
   unsigned int udponly;
 } *dir;
@@ -142,14 +150,14 @@ unsigned int processorstart(struct logdir *ld) {
       fatal2("unable to move filedescriptor for processor", ld->name);
     if ((fd =open_read("state")) == -1) {
       if (errno == error_noent) {
-	if ((fd =open_trunc("state")) == -1)
-	  fatal2("unable to create empty state for processor", ld->name);
-	close(fd);
-	if ((fd =open_read("state")) == -1)
-	  fatal2("unable to open state for processor", ld->name);
+        if ((fd =open_trunc("state")) == -1)
+          fatal2("unable to create empty state for processor", ld->name);
+        close(fd);
+        if ((fd =open_read("state")) == -1)
+          fatal2("unable to open state for processor", ld->name);
       }
       else
-	fatal2("unable to open state for processor", ld->name);
+        fatal2("unable to open state for processor", ld->name);
     }
     if (fd_move(4, fd) == -1)
       fatal2("unable to move filedescriptor for processor", ld->name);
@@ -215,8 +223,7 @@ unsigned int rotate(struct logdir *ld) {
   char tmp[FMT_ULONG +1];
   char oldest[FMT_PTIME];
 
-  if (ld->fddir == -1) return(0);
-  if (ld->size <= 0) return(1);
+  if (ld->fddir == -1) { ld->tmax =0; return(0); }
   if (ld->ppid) while(! processorstop(ld));
 
   while (fchdir(ld->fddir) == -1)
@@ -235,45 +242,53 @@ unsigned int rotate(struct logdir *ld) {
     errno =0;
   } while ((stat(ld->fnsave, &st) != -1) || (errno != error_noent));
 
-  buffer_flush(&ld->b);
-  while (fsync(ld->fdcur) == -1)
-    pause2("unable to fsync current logfile", ld->name);
-  while (fchmod(ld->fdcur, 0744) == -1)
-    pause2("unable to set mode of current", ld->name);
-  close(ld->fdcur);
-  if (verbose) {
-    tmp[0] =' '; tmp[fmt_ulong(tmp +1, ld->size) +1] =0;
-    strerr_warn6(INFO, "rename: ", ld->name, "/current ",
-		 ld->fnsave, tmp, 0);
+  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;
   }
-  while (rename("current", ld->fnsave) == -1)
-    pause2("unable to rename current", ld->name);
-  while ((ld->fdcur =open_append("current")) == -1)
-    pause2("unable to create new current", ld->name);
-  coe(ld->fdcur);
-  ld->size =0;
-  while (fchmod(ld->fdcur, 0644) == -1)
-    pause2("unable to set mode of current", ld->name);
 
-  oldest[0] ='A'; oldest[1] =oldest[27] =0;
-  while (! (d =opendir(".")))
-    pause2("unable to open directory, want rotate", ld->name);
-  errno =0;
-  while ((f =readdir(d)))
-    if ((f->d_name[0] == '@') && (str_len(f->d_name) == 27)) {
-      ++n;
-      if (str_diff(f->d_name, oldest) < 0) byte_copy(oldest, 27, f->d_name);
+  if (ld->size > 0) {
+    buffer_flush(&ld->b);
+    while (fsync(ld->fdcur) == -1)
+      pause2("unable to fsync current logfile", ld->name);
+    while (fchmod(ld->fdcur, 0744) == -1)
+      pause2("unable to set mode of current", ld->name);
+    close(ld->fdcur);
+    if (verbose) {
+      tmp[0] =' '; tmp[fmt_ulong(tmp +1, ld->size) +1] =0;
+      strerr_warn6(INFO, "rename: ", ld->name, "/current ",
+                   ld->fnsave, tmp, 0);
     }
-  if (errno) warn2("unable to read directory", ld->name);
-  closedir(d);
-  
-  if (ld->nmax && (n >= ld->nmax)) {
-    if (verbose) strerr_warn5(INFO, "delete: ", ld->name, "/", oldest, 0);
-    if ((*oldest == '@') && (unlink(oldest) == -1))
-      warn2("unable to unlink oldest logfile", ld->name);
+    while (rename("current", ld->fnsave) == -1)
+      pause2("unable to rename current", ld->name);
+    while ((ld->fdcur =open_append("current")) == -1)
+      pause2("unable to create new current", ld->name);
+    coe(ld->fdcur);
+    ld->size =0;
+    while (fchmod(ld->fdcur, 0644) == -1)
+      pause2("unable to set mode of current", ld->name);
+    
+    oldest[0] ='A'; oldest[1] =oldest[27] =0;
+    while (! (d =opendir(".")))
+      pause2("unable to open directory, want rotate", ld->name);
+    errno =0;
+    while ((f =readdir(d)))
+      if ((f->d_name[0] == '@') && (str_len(f->d_name) == 27)) {
+        ++n;
+        if (str_diff(f->d_name, oldest) < 0) byte_copy(oldest, 27, f->d_name);
+      }
+    if (errno) warn2("unable to read directory", ld->name);
+    closedir(d);
+    
+    if (ld->nmax && (n >= ld->nmax)) {
+      if (verbose) strerr_warn5(INFO, "delete: ", ld->name, "/", oldest, 0);
+      if ((*oldest == '@') && (unlink(oldest) == -1))
+        warn2("unable to unlink oldest logfile", ld->name);
+    }
+    processorstart(ld);
   }
 
-  processorstart(ld);
   while (fchdir(fdwdir) == -1)
     pause1("unable to change to initial working directory");
   return(1);
@@ -287,8 +302,47 @@ int buffer_pwrite(int n, char *s, unsigned int len) {
     if (len > ((dir +n)->sizemax -(dir +n)->size))
       len =(dir +n)->sizemax -(dir +n)->size;
   }
-  while ((i =write((dir +n)->fdcur, s, len)) == -1)
-    pause2("unable to write to current", (dir +n)->name);
+  while ((i =write((dir +n)->fdcur, s, len)) == -1) {
+    if ((errno == ENOSPC) && ((dir +n)->nmin < (dir +n)->nmax)) {
+      DIR *d;
+      direntry *f;
+      char oldest[FMT_PTIME];
+      int j =0;
+
+      while (fchdir((dir +n)->fddir) == -1)
+        pause2("unable to change directory, want remove old logfile",
+               (dir +n)->name);
+      oldest[0] ='A'; oldest[1] =oldest[27] =0;
+      while (! (d =opendir(".")))
+        pause2("unable to open directory, want remove old logfile",
+               (dir +n)->name);
+      errno =0;
+      while ((f =readdir(d)))
+        if ((f->d_name[0] == '@') && (str_len(f->d_name) == 27)) {
+          ++j;
+          if (str_diff(f->d_name, oldest) < 0)
+            byte_copy(oldest, 27, f->d_name);
+        }
+      if (errno) warn2("unable to read directory, want remove old logfile",
+                       (dir +n)->name);
+      closedir(d);
+      errno =ENOSPC;
+      if (j > (dir +n)->nmin)
+        if (*oldest == '@') {
+          strerr_warn5(WARNING, "out of disk space, delete: ", (dir +n)->name,
+                       "/", oldest, 0);
+          errno =0;
+          if (unlink(oldest) == -1) {
+            warn2("unable to unlink oldest logfile", (dir +n)->name);
+            errno =ENOSPC;
+          }
+          while (fchdir(fdwdir) == -1)
+            pause1("unable to change to initial working directory");
+        }
+    }
+    if (errno) pause2("unable to write to current", (dir +n)->name);
+  }
+
   (dir +n)->size +=i;
   if ((dir +n)->sizemax)
     if (s[i -1] == '\n')
@@ -358,7 +412,8 @@ unsigned int logdir_open(struct logdir *ld, const char *fn) {
 
   ld->size =0;
   ld->sizemax =1000000;
-  ld->nmax =10;
+  ld->nmax =ld->nmin =10;
+  ld->tmax =0;
   ld->name =(char*)fn;
   ld->ppid =0;
   ld->match ='+';
@@ -377,57 +432,76 @@ unsigned int logdir_open(struct logdir *ld, const char *fn) {
     if (verbose) strerr_warn4(INFO, "read: ", ld->name, "/config", 0);
     for (i =0; i +1 < sa.len; ++i) {
       if ((len =byte_chr(&sa.s[i], sa.len -i, '\n')) == 1) {
-	++i; continue;
+        ++i; continue;
       }
       sa.s[len +i] =0;
       switch(sa.s[i]) {
       case '\n':
       case '#':
-	 break;
+         break;
       case '+':
       case '-':
       case 'e':
       case 'E':
-	while (! stralloc_catb(&ld->inst, &sa.s[i], len)) pause_nomem();
-	while (! stralloc_0(&ld->inst)) pause_nomem();
-	break;
+        while (! stralloc_catb(&ld->inst, &sa.s[i], len)) pause_nomem();
+        while (! stralloc_0(&ld->inst)) pause_nomem();
+        break;
       case 's':
-	scan_ulong(&sa.s[i +1], &ld->sizemax);
-	break;
+        switch (sa.s[scan_ulong(&sa.s[i +1], &ld->sizemax) +i +1]) {
+        case 'm': ld->sizemax *=1024;
+        case 'k': ld->sizemax *=1024;
+        }
+        break;
       case 'n':
-	scan_ulong(&sa.s[i +1], &ld->nmax);
-	if (ld->nmax == 1) ld->nmax =2;
-	break;
+        scan_ulong(&sa.s[i +1], &ld->nmax);
+        break;
+      case 'N':
+        scan_ulong(&sa.s[i +1], &ld->nmin);
+        break;
+      case 't':
+        switch (sa.s[scan_ulong(&sa.s[i +1], &ld->tmax) +i +1]) {
+        /* case 'd': ld->tmax *=24; */
+        case 'h': ld->tmax *=60;
+        case 'm': ld->tmax *=60;
+        }
+        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 '!':
-	while (! stralloc_copys(&ld->processor, &sa.s[i +1])) pause_nomem();
-	while (! stralloc_0(&ld->processor)) pause_nomem();
-	break;
+        while (! stralloc_copys(&ld->processor, &sa.s[i +1])) pause_nomem();
+        while (! stralloc_0(&ld->processor)) pause_nomem();
+        break;
       case 'U':
-	ld->udponly =1;
+        ld->udponly =1;
       case 'u':
-	if (! (c =ip4_scan(sa.s +i +1, (char *)&ld->udpaddr.sin_addr))) {
-	  warnx("unable to scan ip address", sa.s +i +1);
-	  break;
-	}
-	if (sa.s[i +1 +c] == ':') {
-	  scan_ulong(sa.s +i +c +2, &port);
-	  if (port == 0) {
-	    warnx("unable to scan port number", sa.s +i +c +2);
-	    break;
-	  }
-	}
-	else
-	  port =514;
-	ld->udpaddr.sin_port =htons(port);
-	if (fdudp == -1) {
-       	  fdudp =socket(AF_INET, SOCK_DGRAM, 0);
-	  if (fdudp)
-	    if (ndelay_on(fdudp) == -1) {
-	      close(fdudp);
-	      fdudp =-1;
-	    }
-	}
-	break;
+        if (! (c =ip4_scan(sa.s +i +1, (char *)&ld->udpaddr.sin_addr))) {
+          warnx("unable to scan ip address", sa.s +i +1);
+          break;
+        }
+        if (sa.s[i +1 +c] == ':') {
+          scan_ulong(sa.s +i +c +2, &port);
+          if (port == 0) {
+            warnx("unable to scan port number", sa.s +i +c +2);
+            break;
+          }
+        }
+        else
+          port =514;
+        ld->udpaddr.sin_port =htons(port);
+        if (fdudp == -1) {
+                 fdudp =socket(AF_INET, SOCK_DGRAM, 0);
+          if (fdudp)
+            if (ndelay_on(fdudp) == -1) {
+              close(fdudp);
+              fdudp =-1;
+            }
+        }
+        break;
       }
       i +=len;
     }
@@ -438,12 +512,12 @@ unsigned int logdir_open(struct logdir *ld, const char *fn) {
     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;
+        taia_now(&now);
+        fmt_taia(ld->fnsave, &now);
+        errno =0;
       } while ((stat(ld->fnsave, &st) != -1) || (errno != error_noent));
       while (rename("current", ld->fnsave) == -1)
-	pause2("unable to rename current", ld->name);
+        pause2("unable to rename current", ld->name);
       i =-1;
     }
     else
@@ -454,7 +528,7 @@ unsigned int logdir_open(struct logdir *ld, const char *fn) {
       logdir_close(ld);
       warn2("unable to stat current", ld->name);
       while (fchdir(fdwdir) == -1)
-	pause1("unable to change to initial working directory");
+        pause1("unable to change to initial working directory");
       return(0);
     }
   while ((ld->fdcur =open_append("current")) == -1)
@@ -478,6 +552,8 @@ void logdirs_reopen(void) {
   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;
@@ -501,16 +577,28 @@ int buffer_pread(int fd, char *s, unsigned int len) {
     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;
+    }
   sig_unblock(sig_term);
   sig_unblock(sig_child);
   sig_unblock(sig_alarm);
   sig_unblock(sig_hangup);
-  i =read(fd, s, len);
+  iopause(&in, 1, &trotate, &now);
   sig_block(sig_term);
   sig_block(sig_child);
   sig_block(sig_alarm);
   sig_block(sig_hangup);
-  if (i == -1) if (errno != error_intr) warn("unable to read standard input");
+  i =read(fd, s, len);
+  if (i == -1) {
+    if (errno == error_again) errno =error_intr;
+    if (errno != error_intr) warn("unable to read standard input");
+  }
   if (i > 0) linecomplete =(s[i -1] == '\n');
   return(i);
 }
@@ -525,9 +613,9 @@ void sig_child_handler(void) {
   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;
+        dir[l].ppid =0;
+        processorstop(&dir[l]);
+        break;
       }
 }
 void sig_alarm_handler(void) {
@@ -543,28 +631,18 @@ void logmatch(struct logdir *ld) {
   int i;
 
   ld->match ='+';
+  ld->matcherr ='E';
   for (i =0; i < ld->inst.len; ++i) {
     switch(ld->inst.s[i]) {
     case '+':
     case '-':
       if (pmatch(&ld->inst.s[i +1], line, linelen))
-	ld->match =ld->inst.s[i];
+        ld->match =ld->inst.s[i];
       break;
     case 'e':
-      if (pmatch(&ld->inst.s[i +1], line, linelen)) {
-	if (timestamp) buffer_puts(buffer_2, stamp);
-	buffer_put(buffer_2, line, linelen);
-	if (linelen == linemax) buffer_puts(buffer_2, "...");
-	buffer_put(buffer_2, "\n", 1); buffer_flush(buffer_2);
-      }
-      break;
     case 'E':
-      if (! pmatch(&ld->inst.s[i +1], line, linelen)) {
-	if (timestamp) buffer_puts(buffer_2, stamp);
-	buffer_put(buffer_2, line, linelen);
-	if (linelen == linemax) buffer_puts(buffer_2, "...");
-	buffer_put(buffer_2, "\n", 1); buffer_flush(buffer_2);
-      }
+      if (pmatch(&ld->inst.s[i +1], line, linelen))
+        ld->matcherr =ld->inst.s[i];
       break;
     }
     i +=byte_chr(&ld->inst.s[i], ld->inst.len -i, 0);
@@ -595,7 +673,7 @@ int main(int argc, const char **argv) {
       if (buflen == 0) buflen =1024;
       break;
     case 't':
-      ++timestamp;
+      if (++timestamp > 2) timestamp =2;
       break;
     case 'v':
       ++verbose;
@@ -626,6 +704,9 @@ int main(int argc, const char **argv) {
   line =(char*)alloc(linemax *sizeof(char));
   if (! line) die_nomem();
   fndir =argv;
+  in.fd =0;
+  in.events =IOPAUSE_READ;
+  ndelay_on(in.fd);
 
   sig_block(sig_term);
   sig_block(sig_child);
@@ -645,93 +726,94 @@ int main(int argc, const char **argv) {
     if (exitasap && ! data.p) break; /* data buffer is empty */
     for (linelen =0; linelen < linemax; ++linelen) {
       if (buffer_GETC(&data, &ch) <= 0) {
-	exitasap =1;
-	break;
+        exitasap =1;
+        break;
       }
       if (! linelen && timestamp) {
-	taia_now(&now);
-	switch (timestamp) {
-	case 1:
-	  stamp[fmt_taia(stamp, &now)] =' ';
-	  stamp[26] =0;
-	  break;
-	case 2:
-	  stamp[fmt_ptime(stamp, &now)] =' ';
-	  stamp[26] =0;
-	  break;
-	case 3:
-	  stamp[fmt_ptime(stamp, &now)] =0;
-	  stamp[19] =' '; stamp[20] =0;
-	  break;
-	}
+        taia_now(&now);
+        switch (timestamp) {
+        case 1:
+          stamp[fmt_taia(stamp, &now)] =' ';
+          stamp[26] =0;
+          break;
+        case 2:
+          stamp[fmt_ptime(stamp, &now)] =' ';
+          stamp[26] =0;
+          break;
+        }
       }
       if (ch == '\n') break;
       if (repl) {
-	if ((ch < 32) || (ch > 126))
-	  ch =repl;
-	else
-	  for (i =0; replace[i]; ++i)
-	    if (ch == replace[i]) {
-	      ch =repl;
-	      break;
-	    }
+        if ((ch < 32) || (ch > 126))
+          ch =repl;
+        else
+          for (i =0; replace[i]; ++i)
+            if (ch == replace[i]) {
+              ch =repl;
+              break;
+            }
       }
       line[linelen] =ch;
     }
     if (! linelen) continue;
     for (i =0; i < dirn; ++i)
       if (dir[i].fddir != -1) {
-	if (dir[i].inst.len) logmatch(&dir[i]);
-	if (dir[i].match != '+') continue;
-	if (! dir[i].udponly) {
-	  if (timestamp) buffer_puts(&dir[i].b, stamp);
-	  buffer_put(&dir[i].b, line, linelen);
-	}
-	if (dir[i].udpaddr.sin_port != 0) {
-	  if (fdudp == -1) {
-	    buffer_puts(&dir[i].b, "warning: no udp socket available: ");
-	    buffer_put(&dir[i].b, line, linelen);
-	    buffer_put(&dir[i].b, "\n", 1);
-	    buffer_flush(&dir[i].b);
-	  }
-	  else {
-	    if (linelen >= linemax -1) {
-	      line[linemax -4] =line[linemax -3] =line[linemax -2] ='.';
-	      linelen =linemax -1;
-	    }
-	    if (line[linelen -1] != '\n') line[linelen++] ='\n';
-	    if (sendto(fdudp, line, linelen, 0,
-		       (struct sockaddr *)&dir[i].udpaddr,
-		       sizeof(dir[i].udpaddr)) != linelen) {
-	      buffer_puts(&dir[i].b, "warning: failure sending through udp: ");
-	      buffer_put(&dir[i].b, line, linelen);
-	      buffer_put(&dir[i].b, "\n", 1);
-	      buffer_flush(&dir[i].b);
-	    }
-	  }
-	}
+        if (dir[i].inst.len) logmatch(&dir[i]);
+        if (dir[i].matcherr == 'e') {
+          buffer_put(buffer_2, line, linelen);
+          if (linelen == linemax) buffer_puts(buffer_2, "...");
+          buffer_put(buffer_2, "\n", 1); buffer_flush(buffer_2);
+        }
+        if (dir[i].match != '+') continue;
+        if (! dir[i].udponly) {
+          if (timestamp) buffer_puts(&dir[i].b, stamp);
+          buffer_put(&dir[i].b, line, linelen);
+        }
+        if (dir[i].udpaddr.sin_port != 0) {
+          if (fdudp == -1) {
+            buffer_puts(&dir[i].b, "warning: no udp socket available: ");
+            buffer_put(&dir[i].b, line, linelen);
+            buffer_put(&dir[i].b, "\n", 1);
+            buffer_flush(&dir[i].b);
+          }
+          else {
+            if (linelen >= linemax -1) {
+              line[linemax -4] =line[linemax -3] =line[linemax -2] ='.';
+              linelen =linemax -1;
+            }
+            if (line[linelen -1] != '\n') line[linelen++] ='\n';
+            if (sendto(fdudp, line, linelen, 0,
+                       (struct sockaddr *)&dir[i].udpaddr,
+                       sizeof(dir[i].udpaddr)) != linelen) {
+              buffer_puts(&dir[i].b, "warning: failure sending through udp: ");
+              buffer_put(&dir[i].b, line, linelen);
+              buffer_put(&dir[i].b, "\n", 1);
+              buffer_flush(&dir[i].b);
+            }
+          }
+        }
       }
     if (linelen == linemax)
       for (;;) {
-	if (buffer_GETC(&data, &ch) <= 0) {
-	  exitasap =1;
-	  break;
-	}
-	if (ch == '\n') break;
-	for (i =0; i < dirn; ++i)
-	  if (dir[i].fddir != -1) {
-	    if (dir[i].match != '+') continue;
-	    if (! dir[i].udponly) buffer_PUTC(&dir[i].b, ch);
-	  }
+        if (buffer_GETC(&data, &ch) <= 0) {
+          exitasap =1;
+          break;
+        }
+        if (ch == '\n') break;
+        for (i =0; i < dirn; ++i)
+          if (dir[i].fddir != -1) {
+            if (dir[i].match != '+') continue;
+            if (! dir[i].udponly) buffer_PUTC(&dir[i].b, ch);
+          }
       }
     for (i =0; i < dirn; ++i)
       if (dir[i].fddir != -1) {
-	if (dir[i].match != '+') continue;
-	if (! dir[i].udponly) {
-	  ch ='\n';
-	  buffer_PUTC(&dir[i].b, ch);
-	  buffer_flush(&dir[i].b);
-	}
+        if (dir[i].match != '+') continue;
+        if (! dir[i].udponly) {
+          ch ='\n';
+          buffer_PUTC(&dir[i].b, ch);
+          buffer_flush(&dir[i].b);
+        }
       }
   }
   
diff --git a/src/svlogd.check b/src/svlogd.check
index ea8e3f2..7d926ef 100755
--- a/src/svlogd.check
+++ b/src/svlogd.check
@@ -18,4 +18,10 @@ cat "${ctmp}"/current
 ( echo foo; echo bar; echo baz ) |svlogd -r: -R fb "${ctmp}"
 echo $?
 cat "${ctmp}"/current
+
+echo t2 >"${ctmp}"/config
+( echo foo; sleep 3 ) |svlogd "${ctmp}"
+echo $?
+cat "${ctmp}"/current
+
 rm -rf "${ctmp}"