runit

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit fbf4645e9fe82f2a0963a5877838c1d2bcdad611
parent 64a9f5e19e10c1e91d83bd743898f7e17d0a00e6
Author: Gerrit Pape <pape@smarden.org>
Date:   Sun, 20 Apr 2003 17:38:31 +0000

  * svlogd.c: new; runit's service logging daemon.
  * fmt_ptime.h, fmt_ptime.c, pmatch.h, pmatch.c: new.
  * man/svlogd.8, doc/svlogd.8.html: new.
  * man/runsv.8, man/runsvstat.8, man/utmpset.8: minor cleanup.

Diffstat:
MMakefile | 4++--
Mdebian/changelog | 6++++++
Mdebian/rules | 28++++++++++++++--------------
Mdoc/index.html | 2++
Mdoc/install.html | 10+++++-----
Mdoc/replaceinit.html | 4++--
Adoc/svlogd.8.html | 175+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mman/runsv.8 | 6+++---
Mman/runsvstat.8 | 2+-
Aman/svlogd.8 | 295+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mman/utmpset.8 | 2+-
Mpackage/CHANGES | 7+++++++
Mpackage/upgrade | 4++--
Msrc/Makefile | 17++++++++++++++++-
Msrc/TARGETS | 4++++
Asrc/fmt_ptime.c | 30++++++++++++++++++++++++++++++
Asrc/fmt_ptime.h | 13+++++++++++++
Asrc/pmatch.c | 40++++++++++++++++++++++++++++++++++++++++
Asrc/pmatch.h | 6++++++
Asrc/svlogd.c | 654+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
20 files changed, 1278 insertions(+), 31 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,9 +1,9 @@ DESTDIR= -PACKAGE=runit-0.8.1 +PACKAGE=runit-0.8.4 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 +utmpset.8 runsvchdir.8 runsvstat.8 runsvctrl.8 svlogd.8 all: clean .manpages $(PACKAGE).tar.gz diff --git a/debian/changelog b/debian/changelog @@ -1,3 +1,9 @@ +runit (0.8.4-1) unstable; urgency=low + + * new upstream version. + + -- Gerrit Pape <pape@smarden.org> Sun, 20 Apr 2003 19:34:37 +0200 + runit (0.8.1-1) unstable; urgency=low * new upstream version. diff --git a/debian/rules b/debian/rules @@ -26,11 +26,11 @@ build-arch-stamp: # Add here command to compile/build the package. # $(MAKE) - tar xfzvp runit-0.8.1.tar.gz + tar xfzvp runit-0.8.4.tar.gz ( \ set -e; \ - cd admin/runit-0.8.1/src; \ - ln -s runit-0.8.1 runit; mv runit ../..; \ + cd admin/runit-0.8.4/src; \ + ln -s runit-0.8.4 runit; mv runit ../..; \ echo 'diet -v -Os gcc -O2 -Wall' >conf-cc; \ echo 'diet -v -Os gcc -s -Os -pipe' >conf-ld; \ $(MAKE); \ @@ -74,26 +74,26 @@ install: build # $(MAKE) install DESTDIR=$(CURDIR)/debian/runit # runit - install -m0500 admin/runit-0.8.1/src/runit \ + install -m0500 admin/runit-0.8.4/src/runit \ $(CURDIR)/debian/runit/sbin/runit - install -m0500 admin/runit-0.8.1/src/runit-init \ + install -m0500 admin/runit-0.8.4/src/runit-init \ $(CURDIR)/debian/runit/sbin/runit-init - install -m0755 admin/runit-0.8.1/src/runsvdir \ + install -m0755 admin/runit-0.8.4/src/runsvdir \ $(CURDIR)/debian/runit/usr/bin/runsvdir - install -m0755 admin/runit-0.8.1/src/runsv \ + install -m0755 admin/runit-0.8.4/src/runsv \ $(CURDIR)/debian/runit/usr/bin/runsv - install -m0755 admin/runit-0.8.1/src/runsvchdir \ + install -m0755 admin/runit-0.8.4/src/runsvchdir \ $(CURDIR)/debian/runit/usr/sbin/runsvchdir - install -m0755 admin/runit-0.8.1/src/svwaitdown \ + install -m0755 admin/runit-0.8.4/src/svwaitdown \ $(CURDIR)/debian/runit/usr/bin/svwaitdown - install -m0755 admin/runit-0.8.1/src/svwaitup \ + install -m0755 admin/runit-0.8.4/src/svwaitup \ $(CURDIR)/debian/runit/usr/bin/svwaitup - install -m0755 admin/runit-0.8.1/src/utmpset \ + install -m0755 admin/runit-0.8.4/src/utmpset \ $(CURDIR)/debian/runit/usr/sbin/utmpset - install -m0755 admin/runit-0.8.1/src/runsvstat \ + install -m0755 admin/runit-0.8.4/src/runsvstat \ $(CURDIR)/debian/runit/usr/bin/runsvstat - install -m0755 admin/runit-0.8.1/src/runsvctrl \ + install -m0755 admin/runit-0.8.4/src/runsvctrl \ $(CURDIR)/debian/runit/usr/bin/runsvctrl # install -m0700 debian/1 \ @@ -146,7 +146,7 @@ binary-arch: build install # dh_installexamples -a dh_installman -a admin/runit/man/*.8 # dh_undocumented -a - dh_installchangelogs -a admin/runit-0.8.1/package/CHANGES + dh_installchangelogs -a admin/runit-0.8.4/package/CHANGES dh_strip -a # dh_link -a dh_compress -a diff --git a/doc/index.html b/doc/index.html @@ -27,6 +27,8 @@ Dependencies</a><br> <a href="runsv.8.html">The <tt>runsv</tt> program</a><br> <a href="runsvchdir.8.html">The <tt>runsvchdir</tt> program</a><br> <br> +<a href="svlogd.8.html">The <tt>svlogd</tt> program</a><br> +<br> <a href="utmpset.8.html">The <tt>utmpset</tt> program</a><br> <a href="svwaitdown.8.html">The <tt>svwaitdown</tt> program</a><br> <a href="svwaitup.8.html">The <tt>svwaitup</tt> program</a><br> diff --git a/doc/install.html b/doc/install.html @@ -13,14 +13,14 @@ Check that you have the recent version of <a href="http://cr.yp.to/daemontools.html">daemontools</a> installed. <p> Download -<a href="runit-0.8.1.tar.gz">runit-0.8.1.tar.gz</a> into <tt>/package</tt> +<a href="runit-0.8.4.tar.gz">runit-0.8.4.tar.gz</a> into <tt>/package</tt> and unpack the archive <pre> # cd /package - # gunzip runit-0.8.1.tar - # tar -xpf runit-0.8.1.tar - # rm runit-0.8.1.tar - # cd admin/runit-0.8.1 + # gunzip runit-0.8.4.tar + # tar -xpf runit-0.8.4.tar + # rm runit-0.8.4.tar + # cd admin/runit-0.8.4 </pre> Compile and install the <i>runit</i> programs <pre> diff --git a/doc/replaceinit.html b/doc/replaceinit.html @@ -107,7 +107,7 @@ default Unix process no 1 <i>runit</i>. </pre> To report success: <pre> - # ( uname -a ; cat /etc/runit/[123] ) | mail pape-runit-0.8.1@smarden.org + # ( uname -a ; cat /etc/runit/[123] ) | mail pape-runit-0.8.4@smarden.org </pre> <hr> @@ -188,7 +188,7 @@ Use <b>init 6</b> to reboot and <b>init 0</b> to halt a system that runs <p> To report success: <pre> - # ( uname -a ; cat /etc/runit/[123] ) | mail pape-runit-0.8.1@smarden.org + # ( uname -a ; cat /etc/runit/[123] ) | mail pape-runit-0.8.4@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/doc/svlogd.8.html b/doc/svlogd.8.html @@ -0,0 +1,175 @@ + + + +<HTML> +<HEAD> +<TITLE>svlogd(8) manual page</TITLE> +</HEAD> +<BODY bgcolor=white> +<a href="http://smarden.org/pape/">G. Pape</a><br><A HREF="index.html">runit</A><hr><P> + +<H2><A NAME="sect0">Name</A></H2> +svlogd - runit's service logging daemon +<H2><A NAME="sect1">Synopsis</A></H2> +<B>svlogd</B> [-tv] [-r <I>c]</I> [-R +<I>abc]</I> [-l <I>n]</I> [-b <I>n]</I> <I>logs</I> +<H2><A NAME="sect2">Description</A></H2> +<I>logs</I> consists of one or more arguments, +each specifying a directory. <P> +<B>svlogd</B> continuously reads log data from its +standard input, optionally filters log messages, and writes the data to +one or more automatically rotated <I>logs</I>. <P> +Recent log files can automatically +be processed by an arbitrary processor program when they are rotated, and +<B>svlogd</B> can be told to alert selected log messages to standard error. <P> +<B>svlogd</B> +runs until it sees end-of-file on standard input or is sent a TERM signal, +see below. <P> + +<H3><A NAME="sect3">Log Directory</A></H3> +A log directory <I>log</I> contains some number of old +log files, and the current log file <I>current</I>. Old log files have a file name +starting with <I>@</I> followed by a precise timestamp (see <B><I>tai64n</B>(8)</I>), indicating +when <I>current</I> was rotated and renamed to this file. <P> +A log directory additionally +contains the lock file <I>lock</I>, maybe <I>state</I> and <I>newstate</I>, and optionally the +file <I>config</I>. <B>svlogd</B> creates necessary files if they don't exist. +<H3><A NAME="sect4">Log File +Rotation</A></H3> +<B>svlogd</B> appends selected log messages to the <I>current</I> log file. If +<I>current</I> has <I>size</I> bytes or more size (or there is a new-line withing the +last <I>len</I> of <I>size</I> bytes) <I>current</I> is rotated: <P> +<B>svlogd</B> closes <I>current</I>, changes +permission of <I>current</I> to 0755, renames <I>current</I> to @<I>timestamp.s,</I> and starts +with a new empty <I>current</I>. If <B>svlogd</B> sees <I>num</I> or more old log files in <I>dir</I>, +it removes the oldest. +<H3><A NAME="sect5">Processor</A></H3> +If <B>svlogd</B> is told to process recent log +files, it saves <I>current</I> to @<I>timestamp.u,</I> feeds @<I>timestamp.u</I> through ``sh -c +"<I>processor</I>"'' and writes the output to @<I>timestamp.t.</I> If the <I>processor</I> finishes +successfully, @<I>timestamp.u</I> is deleted and @<I>timestamp.t</I> is renamed to @<I>timestamp.s,</I> +otherwise @<I>timestamp.t</I> is deleted and the <I>processor</I> is started again. <B>svlogd</B> +also saves any output that the <I>processor</I> writes to filedescriptor 5, and +make that output available on filedescriptor 4 when it runs <I>processor</I> on +the next log file rotation. <P> +A <I>processor</I> is run in the background. If <B>svlogd</B> +sees a previously started <I>processor</I> still running when trying to start +a new one for the same <I>log</I>, it blocks until the currently running <I>processor</I> +has finished successfully. Only the HUP signal works in that situation. Note +that this may block any program feeding its log data to <B>svlogd.</B> <P> + +<H3><A NAME="sect6">Config</A></H3> +On +startup, and after receiving a HUP signal, <B>svlogd</B> checks for each <I>log</I> if +the configuration file <I>log/config</I> exists, and if so, reads the file line +by line and adjusts configuration for <I>log</I> as follows: <P> +If the line is empty, +less than two characters long, or starts with a ``#'', it is ignored. A line +of the form +<DL> + +<DT>s<I>size</I> </DT> +<DD>sets the maximum file size of <I>current</I> when <B>svlogd</B> should +rotate the current log file to <I>size</I> bytes. Default is 1000000. </DD> + +<DT>n<I>num</I> </DT> +<DD>sets +the maximum number of old log files <B>svlogd</B> should maintain to <I>num</I>. If <B>svlogd</B> +sees more that <I>num</I> old log files in <I>log</I> after log file rotation, it deletes +the oldest one. Default is 10. </DD> + +<DT>!<I>processor</I> </DT> +<DD>tells <B>svlogd</B> to feed each recent +log file through <I>processor</I> (see above) on log file rotation. By default +log files are not processed. </DD> +</DL> +<P> +If a line starts with a <I>-</I>, <I>+</I>, <I>e</I>, or <I>E</I>, <B>svlogd</B> +matches the first <I>len</I> characters of each log message against <I>pattern</I> and +acts accordingly: +<DL> + +<DT>-<I>pattern</I> </DT> +<DD>the log message is deselected. </DD> + +<DT>+<I>pattern</I> </DT> +<DD>the log +message is selected </DD> + +<DT>e<I>pattern</I> </DT> +<DD>log messages matching <I>pattern</I> are printed +to standard error. </DD> + +<DT>E<I>pattern</I> </DT> +<DD>log messages not matching <I>pattern</I> are printed +to standard error. </DD> +</DL> +<P> +Initially each line is selected. Deselected log messages +are discarded from <I>log</I>. +<H2><A NAME="sect7">Options</A></H2> + +<DL> + +<DT><B>-t</B> </DT> +<DD>timestamp. Prefix each selected line with +a precise timestamp (see <B><I>tai64n</B>(8)</I>) when writing to <I>log</I> or to standard +error. </DD> + +<DT><B>-r <I>c</B> </I></DT> +<DD>replace. <I>c</I> must be a single character. Replace non-printable characters +in log messages with <I>c</I>. Character are replaced before pattern matching is +applied. </DD> + +<DT><B>-R <I>abc</B> </I></DT> +<DD>replace characters. Additionally to non-printable characters, +replace all characters found in <I>abc</I> with <I>c</I> (default ``_''). </DD> + +<DT><B>-l <I>len</B> </I></DT> +<DD>line length. +Pattern matching applies to the first <I>len</I> characters of a log message only. +Default is 1000. </DD> + +<DT><B>-b <I>buflen</B> </I></DT> +<DD>buffer size. Set the size of the buffer <B>svlogd</B> +uses when reading from standard input and writing to <I>logs</I> to <I>buflen</I>. Default +is 1024. <I>buflen</I> must be greater than <I>len</I>. </DD> +</DL> + +<H2><A NAME="sect8">Signals</A></H2> +If <B>svlogd</B> is sent a HUP +signal, it closes and reopens all <I>logs</I>, and updates their configuration +according to <I>log/config</I>. <P> +If <B>svlogd</B> is sent a TERM signal, or if it sees +end-of-file on standard input, it closes standard input, waits for all <I>processor</I> +subprocesses to finish if any, and exits 0 as soon as possible. <P> +If <B>svlogd</B> +is sent an ALRM signal, it tries for all <I>logs</I> to rotate the current log +file, if it is not empty. +<H2><A NAME="sect9">See Also</A></H2> +<I>runsvstat(8)</I>, <I>runit(8)</I>, <I>runit-init(8)</I>, +<I>runsv(8)</I>, <I>runsvstat(8)</I>, <I>runsvctrl(8)</I>, <I>runsvdir(8)</I>, <I>runsvchdir(8)</I>, <I>svwaitdown(8)</I>, +<I>svwaitup(8)</I> <P> +<I>http://smarden.org/runit/</I> +<H2><A NAME="sect10">Author</A></H2> +Gerrit Pape &lt;pape@smarden.org&gt; +<P> + +<HR><P> +<A NAME="toc"><B>Table of Contents</B></A><P> +<UL> +<LI><A NAME="toc0" HREF="#sect0">Name</A></LI> +<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI> +<LI><A NAME="toc2" HREF="#sect2">Description</A></LI> +<UL> +<LI><A NAME="toc3" HREF="#sect3">Log Directory</A></LI> +<LI><A NAME="toc4" HREF="#sect4">Log File Rotation</A></LI> +<LI><A NAME="toc5" HREF="#sect5">Processor</A></LI> +<LI><A NAME="toc6" HREF="#sect6">Config</A></LI> +</UL> +<LI><A NAME="toc7" HREF="#sect7">Options</A></LI> +<LI><A NAME="toc8" HREF="#sect8">Signals</A></LI> +<LI><A NAME="toc9" HREF="#sect9">See Also</A></LI> +<LI><A NAME="toc10" HREF="#sect10">Author</A></LI> +</UL> +</BODY></HTML> diff --git a/man/runsv.8 b/man/runsv.8 @@ -125,13 +125,13 @@ This command is ignored if it is given to .IR service /log/supervise/control. .P For example, to send a TERM signal to /service/socklog-unix, either do - # svc -t /service/socklog-unix + # svc \-t /service/socklog-unix or - # echo -n t >/service/socklog-unix/supervise/control + # echo \-n t >/service/socklog-unix/supervise/control .P If .BR echo (1) -on your systems does not provide the -n option, leave it out, +on your systems does not provide the \-n option, leave it out, .B runsv ignores unknown characters written to the control pipe. .BR echo (1) diff --git a/man/runsvstat.8 b/man/runsvstat.8 @@ -27,7 +27,7 @@ of the corresponding .IR service . .SH OPTIONS .TP -.B -l +.B \-l log service. Check for each .I service if there exists a corresponding diff --git a/man/svlogd.8 b/man/svlogd.8 @@ -0,0 +1,295 @@ +.TH svlogd 8 +.SH NAME +svlogd \- runit's service logging daemon +.SH SYNOPSIS +.B svlogd +[\-tv] [\-r +.I c\fR] [\-R +.I abc\fR] [\-l +.I n\fR] [\-b +.I n\fR] +.I logs +.SH DESCRIPTION +.I logs +consists of one or more arguments, each specifying a directory. +.P +.B svlogd +continuously reads log data from its standard input, optionally filters log +messages, and writes the data to one or more automatically rotated +.IR logs . +.P +Recent log files can automatically be processed by an arbitrary processor +program when they are rotated, and +.B svlogd +can be told to alert selected log messages to standard error. +.P +.B svlogd +runs until it sees end-of-file on standard input or is sent a TERM signal, +see below. + +.SS LOG DIRECTORY +A log directory +.I log +contains some number of old log files, and the current log file +.IR current . +Old log files have a file name starting with +.I @ +followed by a precise timestamp (see +.BR tai64n (8)), +indicating when +.I current +was rotated and renamed to this file. +.P +A log directory additionally contains the lock file +.IR lock , +maybe +.I state +and +.IR newstate , +and optionally the file +.IR config . +.B svlogd +creates necessary files if they don't exist. +.SS LOG FILE ROTATION +.B svlogd +appends selected log messages to the +.I current +log file. +If +.I current +has +.I size +bytes or more size (or there is a new-line withing the last +.I len +of +.I size +bytes) +.I current +is rotated: +.P +.B svlogd +closes +.IR current , +changes permission of +.I current +to 0755, renames +.I current +to +.RI @ timestamp\fR.s, +and starts with a new empty +.IR current . +If +.B svlogd +sees +.I num +or more old log files in +.IR dir , +it removes the oldest. +.SS PROCESSOR +If +.B svlogd +is told to process recent log files, it saves +.I current +to +.RI @ timestamp\fR.u, +feeds +.RI @ timestamp\fR.u +through ``sh \-c "\fIprocessor\fR"'' +and writes the output to +.RI @ timestamp\fR.t. +If the +.I processor +finishes successfully, +.RI @ timestamp\fR.u +is deleted and +.RI @ timestamp\fR.t +is renamed to +.RI @ timestamp\fR.s, +otherwise +.RI @ timestamp\fR.t +is deleted and the +.I processor +is started again. +.B svlogd +also saves any output that the +.I processor +writes to filedescriptor 5, and make that output available on +filedescriptor 4 when it runs +.I processor +on the next log file rotation. +.P +A +.I processor +is run in the background. +If +.B svlogd +sees a previously started +.I processor +still running when trying to start a new one for the same +.IR log , +it blocks until the currently running +.I processor +has finished successfully. +Only the HUP signal works in that situation. +Note that this may block any program feeding its log data to +.BR svlogd. + +.SS CONFIG +On startup, and after receiving a HUP signal, +.B svlogd +checks for each +.I log +if the configuration file +.I log/config +exists, and if so, reads the file line by line and adjusts configuration for +.I log +as follows: +.P +If the line is empty, less than two characters long, or starts with a ``#'', +it is ignored. +A line of the form +.TP +.RI s size +sets the maximum file size of +.I current +when +.B svlogd +should rotate the current log file to +.I size +bytes. +Default is 1000000. +.TP +.RI n num +sets the maximum number of old log files +.B svlogd +should maintain to +.IR num . +If +.B svlogd +sees more that +.I num +old log files in +.I log +after log file rotation, it deletes the oldest one. +Default is 10. +.TP +.RI ! processor +tells +.B svlogd +to feed each recent log file through +.I processor +(see above) on log file rotation. +By default log files are not processed. +.P +If a line starts with a +.IR \- , +.IR + , +.IR e , +or +.IR E , +.B svlogd +matches the first +.I len +characters of each log message against +.I pattern +and acts accordingly: +.TP +.RI \- pattern +the log message is deselected. +.TP +.RI + pattern +the log message is selected +.TP +.RI e pattern +log messages matching +.I pattern +are printed to standard error. +.TP +.RI E pattern +log messages not matching +.I pattern +are printed to standard error. +.P +Initially each line is selected. +Deselected log messages are discarded from +.IR log . +.SH OPTIONS +.TP +.B \-t +timestamp. +Prefix each selected line with a precise timestamp (see +.BR tai64n (8)) +when writing to +.I log +or to standard error. +.TP +.B \-r \fIc +replace. +.I c +must be a single character. +Replace non-printable characters in log messages with +.IR c . +Character are replaced before pattern matching is applied. +.TP +.B \-R \fIabc +replace characters. +Additionally to non-printable characters, replace all characters found in +.I abc +with +.I c +(default ``_''). +.TP +.B \-l \fIlen +line length. +Pattern matching applies to the first +.I len +characters of a log message only. +Default is 1000. +.TP +.B \-b \fIbuflen +buffer size. +Set the size of the buffer +.B svlogd +uses when reading from standard input and writing to +.I logs +to +.IR buflen . +Default is 1024. +.I buflen +must be greater than +.IR len . +.SH SIGNALS +If +.B svlogd +is sent a HUP signal, it closes and reopens all +.IR logs , +and updates their configuration according to +.IR log/config . +.P +If +.B svlogd +is sent a TERM signal, or if it sees end-of-file on standard input, it +closes standard input, waits for all +.I processor +subprocesses to finish if any, and exits 0 as soon as possible. +.P +If +.B svlogd +is sent an ALRM signal, it tries for all +.I logs +to rotate the current log file, if it is not empty. +.SH SEE ALSO +runsvstat(8), +runit(8), +runit-init(8), +runsv(8), +runsvstat(8), +runsvctrl(8), +runsvdir(8), +runsvchdir(8), +svwaitdown(8), +svwaitup(8) +.P +http://smarden.org/runit/ +.SH AUTHOR +Gerrit Pape <pape@smarden.org> diff --git a/man/utmpset.8 b/man/utmpset.8 @@ -33,7 +33,7 @@ to the run scripts, e.g.: .P #!/bin/sh - /command/utmpset -w tty5 + /command/utmpset \-w tty5 exec /sbin/getty 38400 tty5 linux .SH OPTIONS .TP diff --git a/package/CHANGES b/package/CHANGES @@ -1,3 +1,10 @@ +runit 0.8.4 +Sun, 20 Apr 2003 19:31:24 +0200 + * svlogd.c: new; runit's service logging daemon. + * fmt_ptime.h, fmt_ptime.c, pmatch.h, pmatch.c: new. + * man/svlogd.8, doc/svlogd.8.html: new. + * man/runsv.8, man/runsvstat.8, man/utmpset.8: minor cleanup. + runit 0.8.1 Wed, 12 Mar 2003 15:10:04 +0100 * runsvdir.c, runsv.c: close-on-exec file descriptors of current dir and diff --git a/package/upgrade b/package/upgrade @@ -7,9 +7,9 @@ test -d src || ( echo 'Wrong working directory.'; exit 1 ) here=`env - PATH=$PATH pwd` parent=`dirname $here` -echo 'Creating symlink runit -> runit-0.8.1...' +echo 'Creating symlink runit -> runit-0.8.4...' rm -f runit -ln -s runit-0.8.1 runit +ln -s runit-0.8.4 runit mv -f runit .. echo 'Making command links in /command...' diff --git a/src/Makefile b/src/Makefile @@ -1,5 +1,5 @@ IT=sysdeps runit runit-init runsv runsvdir runsvchdir svwaitup svwaitdown \ -utmpset +utmpset svlogd default: $(IT) @@ -33,6 +33,9 @@ utmpset: load utmpset.o unix.a byte.a runsvchdir: load runsvchdir.o unix.a byte.a ./load runsvchdir unix.a byte.a +svlogd: load svlogd.o pmatch.o fmt_ptime.o unix.a byte.a time.a + ./load svlogd pmatch.o fmt_ptime.o unix.a byte.a time.a + runit.o: compile sysdeps runit.c ./compile runit.c @@ -63,6 +66,17 @@ utmpset.o: compile utmpset.c runsvchdir.o: compile runsvchdir.c ./compile runsvchdir.c +svlogd.o: compile svlogd.c + ./compile svlogd.c + + +pmatch.o: compile pmatch.c + ./compile pmatch.c + +fmt_ptime.o: compile fmt_ptime.c + ./compile fmt_ptime.c + + clean: find . -name \*~ -exec rm -f {} \; find . -name .??*~ -exec rm -f {} \; @@ -337,6 +351,7 @@ uint64.h grep sysdep hasmkffo.h >> sysdeps grep sysdep hasflock.h >> sysdeps grep sysdep hasshsgr.h >> sysdeps + cat sysdeps systype: find-systype.sh trycpp.c x86cpuid.c sh find-systype.sh > systype diff --git a/src/TARGETS b/src/TARGETS @@ -18,6 +18,10 @@ utmpset utmpset.o runsvchdir runsvchdir.o +svlogd +svlogd.o +pmatch.o +fmt_ptime.o alloc.o alloc_re.o buffer.o diff --git a/src/fmt_ptime.c b/src/fmt_ptime.c @@ -0,0 +1,30 @@ +#include "fmt_ptime.h" +#include "fmt.h" + +unsigned int fmt_ptime(char *s, struct taia *ta) { + struct tm *t; + + if (! (t =localtime((time_t*)&ta->sec.x))) 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 int 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); +} diff --git a/src/fmt_ptime.h b/src/fmt_ptime.h @@ -0,0 +1,13 @@ +#ifndef FMT_PTIME_H +#define FMT_PTIME_H + +#define FMT_PTIME 30 + +#include <time.h> +#include <sys/time.h> +#include "taia.h" + +extern unsigned int fmt_ptime(char *, struct taia *); +extern unsigned int fmt_taia(char *, struct taia *); + +#endif diff --git a/src/pmatch.c b/src/pmatch.c @@ -0,0 +1,40 @@ + +int pmatch(const char *p, const char *s, unsigned int 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); +} diff --git a/src/pmatch.h b/src/pmatch.h @@ -0,0 +1,6 @@ +#ifndef PMATCH_H +#define PMATCH_H + +extern unsigned int pmatch(const char *, const char *, unsigned int); + +#endif diff --git a/src/svlogd.c b/src/svlogd.c @@ -0,0 +1,654 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <time.h> +#include <sys/time.h> +#include <dirent.h> +#include <unistd.h> +#include <dirent.h> +#include <stdio.h> +#include "pmatch.h" +#include "fmt_ptime.h" +#include "alloc.h" +#include "stralloc.h" +#include "strerr.h" +#include "buffer.h" +#include "sig.h" +#include "env.h" +#include "fd.h" +#include "wait.h" +#include "error.h" +#include "sgetopt.h" +#include "open.h" +#include "openreadclose.h" +#include "coe.h" +#include "lock.h" +#include "str.h" +#include "byte.h" +#include "scan.h" +#include "direntry.h" +#include "taia.h" +#include "fmt.h" + +#define USAGE " [-tv] [-r c] [-R abc] [-l n ] [-b n] dir ..." +#define VERSION "$Id$" + +#define FATAL "svlogd: fatal: " +#define WARNING "svlogd: warning: " +#define PAUSE "svlogd: pausing: " +#define INFO "svlogd: info: " + +const char *progname; + +unsigned int verbose =0; +unsigned int timestamp =0; +unsigned long linelen =1000; +unsigned long buflen =1024; +const char *replace =""; +char repl =0; + +const char **fndir; +int fdwdir; +struct stat st; +int i; +stralloc sa; +int wstat; +struct taia now; + +char *databuf; +buffer data; +char *line; +char stamp[FMT_PTIME]; +unsigned int exitasap =0; + +struct logdir { + int fddir; + char *btmp; + buffer b; + stralloc inst; + unsigned long size; + unsigned long sizemax; + unsigned long nmax; + stralloc processor; + int ppid; + char fnsave[FMT_PTIME]; + char *name; + int fdcur; + int fdlock; + unsigned int match; +} *dir; +unsigned int dirn =0; + +void usage() { strerr_die4x(111, "usage: ", progname, USAGE, "\n"); } +void die_nomem() { strerr_die2x(111, FATAL, "out of memory."); } +void fatal(char *m0) { strerr_die3sys(111, FATAL, m0, ": "); } +void fatal2(char *m0, char *m1) { + strerr_die5sys(111, FATAL, m0, ": ", m1, ": "); +} +void warn(char *m0) { strerr_warn3(WARNING, m0, ": ", &strerr_sys); } +void warn2(char *m0, char *m1) { + strerr_warn5(WARNING, m0, ": ", m1, ": ", &strerr_sys); +} +void pause_nomem() { strerr_warn2(PAUSE, "out of memory.", 0); sleep(3); } +void pause1(char *m0) { strerr_warn3(PAUSE, m0, ": ", &strerr_sys); sleep(3); } +void pause2(char *m0, char *m1) { + strerr_warn5(PAUSE, m0, ": ", m1, ": ", &strerr_sys); + sleep(3); +} + +int processorstart(struct logdir *ld) { + int pid; + + if (! ld->processor.len) return(0); + if (ld->ppid) { + warn2("processor already running", ld->name); + return(0); + } + while ((pid =fork()) == -1) + pause2("unable to 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 ((fd =open_read(ld->fnsave)) == -1) + fatal2("unable to open input for processor", ld->name); + if (fd_move(0, fd) == -1) + fatal2("unable to move filedescriptor for processor", ld->name); + ld->fnsave[26] ='t'; + if ((fd =open_trunc(ld->fnsave)) == -1) + fatal2("unable to open output for processor", ld->name); + if (fd_move(1, fd) == -1) + fatal2("unable to move filedescriptor for processor", ld->name); + if ((fd =open_read("state")) == -1) + fatal2("unable to open state for processor", ld->name); + if (fd_move(4, fd) == -1) + fatal2("unable to move filedescriptor for processor", ld->name); + if ((fd =open_trunc("newstate")) == -1) + fatal2("unable to open newstate for processor", ld->name); + if (fd_move(5, fd) == -1) + fatal2("unable to move filedescriptor for processor", ld->name); + + prog[0] = "sh"; + prog[1] = "-c"; + prog[2] = ld->processor.s; + prog[3] = 0; + execve("/bin/sh", prog, environ); + fatal2("unable to run processor", ld->name); + } + ld->ppid =pid; + return(1); +} +int processorstop(struct logdir *ld) { + char f[28]; + + if (ld->ppid) { + sig_unblock(sig_hangup); + while (wait_pid(&wstat, ld->ppid) == -1) + pause2("error waiting for processor", ld->name); + sig_block(sig_hangup); + ld->ppid =0; + } + while (fchdir(ld->fddir) == -1) + pause2("unable to change directory, want processor", ld->name); + if (wait_exitcode(wstat) != 0) { + warn2("processor crashed, restart", ld->name); + ld->fnsave[26] ='t'; + unlink(ld->fnsave); + ld->fnsave[26] ='u'; + if (ld->processor.len) processorstart(ld); + while (fchdir(fdwdir) == -1) + pause1("unable to change to initial working directory"); + return(ld->processor.len ? 0 : 1); + } + if (unlink(ld->fnsave) == -1) + strerr_warn5(WARNING, "unable to unlink: ", ld->name, "/", ld->fnsave, 0); + ld->fnsave[26] ='t'; + byte_copy(f, 26, ld->fnsave); + f[26] ='s'; f[27] =0; + while (rename(ld->fnsave, f) == .1) + pause2("unable to rename processed", ld->name); + while (chmod(f, 0744) == -1) + pause2("unable to set mode of processed", ld->name); + while (fchdir(fdwdir) == -1) + pause1("unable to change to initial working directory"); + return(1); +} + +int rotate(struct logdir *ld) { + DIR *d; + direntry *f; + int n =0; + char tmp[FMT_ULONG +1]; + char oldest[FMT_PTIME]; + + if (ld->size <= 0) return(1); + if (ld->ppid) while(! processorstop(ld)); + + while (fchdir(ld->fddir) == -1) + pause2("unable to change directory, want rotate", ld->name); + + /* create new filename */ + ld->fnsave[25] ='.'; + if (ld->processor.len) + ld->fnsave[26] ='u'; + else + ld->fnsave[26] ='s'; + ld->fnsave[27] =0; + do { + taia_now(&now); + fmt_taia(ld->fnsave, &now); + 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); + } + 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); + buffer_init(&ld->b, buffer_unixwrite, ld->fdcur, ld->btmp, sizeof ld->btmp); + + oldest[0] ='A'; oldest[1] =oldest[27] =0; errno =0; + while (! (d =opendir("."))) + pause2("unable to open directory, want rotate", ld->name); + 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); + } + + /* + start processor + */ + if (ld->processor.len) { + processorstart(ld); + } + + while (fchdir(fdwdir) == -1) + pause1("unable to change to initial working directory"); + return(1); +} + +void logdir_close(struct logdir *ld) { + if (ld->fddir == -1) return; + if (verbose) strerr_warn3(INFO, "close: ", ld->name, 0); + close(ld->fddir); + ld->fddir =-1; + if (ld->fdcur) { + 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 (ld->fdlock) close(ld->fdlock); + ld->fdlock =-1; +} + +int logdir_open(struct logdir *ld, const char *fn) { + if ((ld->fddir =open_read(fn)) == -1) { + warn2("unable to open log directory", (char*)fn); + return(0); + } + coe(ld->fddir); + if (fchdir(ld->fddir) == -1) { + logdir_close(ld); + warn2("unable to change directory", (char*)fn); + return(0); + } + ld->fdlock =open_append("lock"); + if ((ld->fdlock == -1) || (lock_exnb(ld->fdlock) == -1)) { + logdir_close(ld); + warn2("unable to lock directory", (char*)fn); + while (fchdir(fdwdir) == -1) + pause1("unable to change to initial working directory"); + return(0); + } + coe(ld->fdlock); + + ld->size =0; + ld->sizemax =9000; + ld->nmax =10; + ld->name =(char*)fn; + ld->match =0; + while (! stralloc_copys(&ld->inst, "")) pause_nomem(); + while (! stralloc_copys(&ld->processor, "")) pause_nomem(); + + /* read config */ + if ((i =openreadclose("config", &sa, 128)) == -1) + warn2("unable to read config", ld->name); + if (i != 0) { + int len; + + if (verbose) strerr_warn4(INFO, "read: ", ld->name, "/config", 0); + for (i =0; i < sa.len -1; ++i) { + if ((len =byte_chr(&sa.s[i], sa.len -i, '\n')) == 1) { + ++i; continue; + } + sa.s[len +i] =0; + switch(sa.s[i]) { + case '\n': + case '#': + 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; + case 's': + scan_ulong(&sa.s[i +1], &ld->sizemax); + if (ld->sizemax < 2048) ld->sizemax =2048; + break; + case 'n': + scan_ulong(&sa.s[i +1], &ld->nmax); + break; + case '!': + while (! stralloc_copys(&ld->processor, &sa.s[i +1])) + pause_nomem(); + while (! stralloc_0(&ld->processor)) pause_nomem(); + break; + } + i +=len; + } + } + + /* open current */ + if ((i =stat("current", &st)) != -1) { + if (st.st_size && ((st.st_mode & S_IRWXU) != 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 != error_noent)); + while (rename("current", ld->fnsave) == -1) + pause2("unable to rename current", ld->name); + } + else + ld->size =st.st_size; + } + else + if (errno != error_noent) { + logdir_close(ld); + warn2("unable to stat current", ld->name); + while (fchdir(fdwdir) == -1) + pause1("unable to change to initial working directory"); + return(0); + } + while ((ld->fdcur =open_append("current")) == -1) + pause2("unable to open current", ld->name); + coe(ld->fdcur); + while (fchmod(ld->fdcur, 0644) == -1) + pause2("unable to set mode of current", ld->name); + if (! ld->btmp) + while (! (ld->btmp =(char*)alloc(buflen *sizeof(char)))) pause_nomem(); + buffer_init(&ld->b, buffer_unixwrite, ld->fdcur, ld->btmp, sizeof ld->btmp); + + if (verbose) { + if (i == 0) strerr_warn4(INFO, "append: ", ld->name, "/current", 0); + else strerr_warn4(INFO, "new: ", ld->name, "/current", 0); + } + + while (fchdir(fdwdir) == -1) + pause1("unable to change to initial working directory"); + return(1); +} + +void logdirs_reopen(void) { + int l; + + for (l =0; l < dirn; ++l) { + logdir_close(&dir[l]); + logdir_open(&dir[l], fndir[l]); + } +} + +int linestart(struct logdir *ld, char *s, int len) { + /* check inst, set match */ + ld->match ='+'; + if (ld->inst.len) { + for (i =0; i < ld->inst.len; ++i) { + switch(ld->inst.s[i]) { + case '+': + case '-': + if (pmatch(&ld->inst.s[i +1], s, len)) + ld->match =ld->inst.s[i]; + break; + case 'e': + if (pmatch(&ld->inst.s[i +1], s, len)) { + if (timestamp) buffer_puts(buffer_2, stamp); + buffer_put(buffer_2, s, len); + if (len == linelen) buffer_puts(buffer_2, "..."); + buffer_putflush(buffer_2, "\n", 1); + } + break; + case 'E': + if (! pmatch(&ld->inst.s[i +1], s, len)) { + if (timestamp) buffer_puts(buffer_2, stamp); + buffer_put(buffer_2, s, len); + if (len == linelen) buffer_puts(buffer_2, "..."); + buffer_putflush(buffer_2, "\n", 1); + } + break; + } + i +=byte_chr(&ld->inst.s[i], ld->inst.len -i, 0); + } + } + if (ld->match == '-') return(0); + if (timestamp) { + buffer_puts(&ld->b, stamp); + ld->size +=26; + } + buffer_put(&ld->b, s, len); + ld->size +=len; + return(1); +} +int lineadd(struct logdir *ld, char *s, int len) { + if (ld->match != '+') return(0); + buffer_put(&ld->b, s, len); + ld->size +=len; + if (ld->sizemax && (ld->size >= ld->sizemax)) rotate(ld); + return(1); +} +int lineflush(struct logdir *ld, char *s, int len) { + switch(ld->match) { + case '-': + return(0); + case 0: + linestart(ld, s, len); + break; + case '+': + buffer_put(&ld->b, s, len); + ld->size +=len; + break; + } + if (ld->match == '+') { + buffer_putflush(&ld->b, "\n", 1); + ld->size +=1; + ld->match =0; + if (ld->sizemax && (ld->size >= (ld->sizemax -linelen))) rotate(ld); + return(1); + } + return(0); +} +int buffer_pread(int fd, char *s, unsigned int len) { + len =read(fd, s, len); + if ((len == -1) && (errno == error_intr)) return(0); + return(len); +} +void sig_term_handler(void) { + exitasap =1; + strerr_warn2(INFO, "sigterm received.", 0); +} +void sig_child_handler(void) { + int pid, l; + + 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; + } + strerr_warn2(INFO, "sigchild received.", 0); +} +void sig_alarm_handler(void) { + int l; + + for (l =0; l < dirn; ++l) + if (dir[l].fddir != -1) + if (dir[l].size > 0) + rotate(&dir[l]); + strerr_warn2(INFO, "sigalarm received.", 0); +} +void sig_hangup_handler(void) { + logdirs_reopen(); + strerr_warn2(INFO, "sighangup received.", 0); +} + +int main(int argc, const char **argv) { + int opt; + unsigned int eol; + + progname =*argv; + + while ((opt =getopt(argc, argv, "R:r:l:b:tvV")) != opteof) { + switch(opt) { + case 'R': + replace =optarg; + if (! repl) repl ='_'; + break; + case 'r': + repl =*optarg; + if (! repl || *(optarg +1)) usage(); + break; + case 'l': + scan_ulong(optarg, &linelen); + if (linelen == 0) linelen =1000; + break; + case 'b': + scan_ulong(optarg, &buflen); + if (buflen == 0) buflen =1024; + break; + case 't': + ++timestamp; + break; + case 'v': + ++verbose; + break; + case 'V': strerr_warn1(VERSION, 0); + case '?': usage(); + } + } + argv +=optind; + + dirn =argc -optind; + if (dirn <= 0) usage(); + if (buflen <= linelen) usage(); + if ((fdwdir =open_read(".")) == -1) + fatal("unable to open current working directory"); + coe(fdwdir); + dir =(struct logdir*)alloc(dirn *sizeof(struct logdir)); + if (! dir) die_nomem(); + for (i =0; i < dirn; ++i) { + dir[i].fddir =-1; + dir[i].btmp =0; /* grm */ + } + databuf =(char*)alloc(buflen *sizeof(char)); + if (! databuf) die_nomem(); + buffer_init(&data, buffer_pread, 0, databuf, buflen); + line =(char*)alloc(linelen *sizeof(char)); + if (! line) die_nomem(); + fndir =argv; + + 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(); + + for(eol =0;;) { + int r, len; + char *ch; + + if (exitasap) { + /* check for processors */ + for (i =0; i < dirn; ++i) + if (dir[i].ppid) while (! processorstop(&dir[i])); + break; + } + + sig_unblock(sig_term); + sig_unblock(sig_child); + sig_unblock(sig_alarm); + sig_unblock(sig_hangup); + errno =0; + r =buffer_feed(&data); + sig_block(sig_term); + sig_block(sig_child); + sig_block(sig_alarm); + sig_block(sig_hangup); + + if (r == -1) { + warn("unable to read standard input"); + continue; + } + if (r == 0) { + if (errno == error_intr) continue; + break; /* eof */ + } + if (r > linelen) r =linelen; + if (timestamp && (eol == 0)) { + 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)] =' '; + stamp[19] =' '; stamp[20] =0; + break; + } + } + ch =buffer_peek(&data); + for (len =0; len < r; ++len, ++ch) { + if (*ch == '\n') { + eol =2; + break; + } + line[len] =*ch; + + if (repl) { + if ((line[len] < 33) || (line[len] > 126)) + line[len] =repl; + else + for (i =0; replace[i]; ++i) + if (line[len] == replace[i]) { + line[len] =repl; + break; + } + } + } + buffer_seek(&data, len); + for (i =0; i < dirn; ++i) + if (dir[i].fddir != -1) { + switch(eol) { + case 0: linestart(&dir[i], line, len); break; + case 1: lineadd(&dir[i], line, len); break; + case 2: lineflush(&dir[i], line, len); break; + } + } + if (eol == 0) eol =1; + if (eol == 2) { eol =0; buffer_seek(&data, 1); } + } + + for (i =0; i < dirn; ++i) { + if (dir[i].fddir != -1) + buffer_flush(&dir[i].b); + logdir_close(&dir[i]); + } + _exit(0); +}