From 0437b33e54fd72060d17908d6abf96bfabaacad2 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Tue, 2 May 2000 09:56:41 +1000 Subject: - Add Andre Lucas' patch to read entropy gathering commands from a text file --- ChangeLog | 2 + Makefile.in | 8 ++- acconfig.h | 17 ----- aclocal.m4 | 15 ++++ configure.in | 83 +++++++--------------- entropy.c | 207 +++++++++++++++++++++++++++++++++++++------------------ log-server.c | 4 +- ssh.h | 5 +- ssh_prng_cmds.in | 50 ++++++++++++++ 9 files changed, 245 insertions(+), 146 deletions(-) create mode 100644 aclocal.m4 create mode 100644 ssh_prng_cmds.in diff --git a/ChangeLog b/ChangeLog index 1c924fb2a..d7143d5d4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,6 +14,8 @@ - unlink pid file, ok niels@ [auth2.c] - Add missing #ifdefs; ok - markus + - Add Andre Lucas' patch to read entropy + gathering commands from a text file - Release 2.0.0beta1 20000501 diff --git a/Makefile.in b/Makefile.in index d5e3fde6c..646201c04 100644 --- a/Makefile.in +++ b/Makefile.in @@ -29,6 +29,8 @@ INSTALL=@INSTALL@ PERL=@PERL@ LDFLAGS=-L. @LDFLAGS@ +INSTALL_SSH_PRNG_CMDS=@INSTALL_SSH_PRNG_CMDS@ + TARGETS=ssh sshd ssh-add ssh-keygen ssh-agent scp $(EXTRA_TARGETS) LIBOBJS= atomicio.o authfd.o authfile.o bsd-bindresvport.o bsd-daemon.o bsd-misc.o bsd-mktemp.o bsd-rresvport.o bsd-setenv.o bsd-snprintf.o bsd-strlcat.o bsd-strlcpy.o bufaux.o buffer.o canohost.o channels.o cipher.o compat.o compress.o crc32.o deattack.o dispatch.o dsa.o fake-getaddrinfo.o fake-getnameinfo.o fingerprint.o hmac.o hostfile.o key.o kex.o log.o match.o mpaux.o nchan.o packet.o radix.o entropy.o readpass.o rsa.o tildexpand.o ttymodes.o uidswap.o uuencode.o xmalloc.o @@ -80,7 +82,7 @@ $(MANPAGES) $(CONFIGFILES):: clean: rm -f *.o *.a $(TARGETS) config.status config.cache config.log - rm -f *.out core + rm -f *.out ssh_prng_cmds core distclean: clean rm -f Makefile config.h core *~ @@ -125,6 +127,9 @@ install: manpages $(TARGETS) $(INSTALL) -m 644 ssh_config.out $(DESTDIR)$(sysconfdir)/ssh_config; \ $(INSTALL) -m 644 sshd_config.out $(DESTDIR)$(sysconfdir)/sshd_config; \ fi + if [ -f ssh_prng_cmds -a ! -z "$(INSTALL_SSH_PRNG_CMDS)" ]; then \ + $(INSTALL) -m 644 ssh_prng_cmds $(DESTDIR)$(sysconfdir)/ssh_prng_cmds; \ + fi host-key: ssh-keygen ./ssh-keygen -b 1024 -f $(sysconfdir)/ssh_host_key -N '' @@ -132,6 +137,7 @@ host-key: ssh-keygen uninstallall: uninstall -rm -f $(DESTDIR)$(sysconfdir)/ssh_config -rm -f $(DESTDIR)$(sysconfdir)/sshd_config + -rm -f $(DESTDIR)$(sysconfdir)/ssh_prng_cmds -rmdir $(DESTDIR)$(sysconfdir) -rmdir $(DESTDIR)$(bindir) -rmdir $(DESTDIR)$(sbindir) diff --git a/acconfig.h b/acconfig.h index d07fd4fe7..99e70d17b 100644 --- a/acconfig.h +++ b/acconfig.h @@ -153,23 +153,6 @@ /* Detect IPv4 in IPv6 mapped addresses and treat as IPv4 */ #undef IPV4_IN_IPV6 -/* Programs used in entropy collection */ -#undef PROG_LS -#undef PROG_NETSTAT -#undef PROG_ARP -#undef PROG_IFCONFIG -#undef PROG_PS -#undef PROG_W -#undef PROG_WHO -#undef PROG_LAST -#undef PROG_LASTLOG -#undef PROG_DF -#undef PROG_VMSTAT -#undef PROG_UPTIME -#undef PROG_IPCS -#undef PROG_TAIL - - @BOTTOM@ /* ******************* Shouldn't need to edit below this line ************** */ diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 000000000..65783346b --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,15 @@ +dnl $Id: aclocal.m4,v 1.1 2000/05/01 23:57:51 damien Exp $ +dnl +dnl OpenSSH-specific autoconf macros +dnl + +dnl AC_PATH_ENTROPY_PROG(variablename, command): +dnl Tidiness function, sets 'undef' if not found, and does the AC_SUBST +AC_DEFUN(AC_PATH_ENTROPY_PROG, [ + AC_PATH_PROG([$1], [$2]) + if test -z "[$]$1" ; then + $1="undef" + fi + AC_SUBST([$1]) +]) + diff --git a/configure.in b/configure.in index 8046fa3e1..be2a63df1 100644 --- a/configure.in +++ b/configure.in @@ -637,66 +637,31 @@ AC_ARG_WITH(egd-pool, ] ) +# detect pathnames for entropy gathering commands, if we need them +INSTALL_SSH_PRNG_CMDS="" +rm -f prng_commands if test -z "$RANDOM_POOL" -a -z "$EGD_SOCKET" ; then # Use these commands to collect entropy - AC_PATH_PROG(PROG_LS, ls) - AC_PATH_PROG(PROG_NETSTAT, netstat) - AC_PATH_PROG(PROG_ARP, arp) - AC_PATH_PROG(PROG_IFCONFIG, ifconfig) - AC_PATH_PROG(PROG_PS, ps) - AC_PATH_PROG(PROG_W, w) - AC_PATH_PROG(PROG_WHO, who) - AC_PATH_PROG(PROG_LAST, last) - AC_PATH_PROG(PROG_LASTLOG, lastlog) - AC_PATH_PROG(PROG_DF, df) - AC_PATH_PROG(PROG_VMSTAT, vmstat) - AC_PATH_PROG(PROG_UPTIME, uptime) - AC_PATH_PROG(PROG_IPCS, ipcs) - AC_PATH_PROG(PROG_TAIL, tail) - AC_PATH_PROG(PROG_LS, ls) - if test ! -z "$PROG_LS" ; then - AC_DEFINE_UNQUOTED(PROG_LS, "$PROG_LS") - fi - if test ! -z "$PROG_NETSTAT" ; then - AC_DEFINE_UNQUOTED(PROG_NETSTAT, "$PROG_NETSTAT") - fi - if test ! -z "$PROG_ARP" ; then - AC_DEFINE_UNQUOTED(PROG_ARP, "$PROG_ARP") - fi - if test ! -z "$PROG_IFCONFIG" ; then - AC_DEFINE_UNQUOTED(PROG_IFCONFIG, "$PROG_IFCONFIG") - fi - if test ! -z "$PROG_PS" ; then - AC_DEFINE_UNQUOTED(PROG_PS, "$PROG_PS") - fi - if test ! -z "$PROG_W" ; then - AC_DEFINE_UNQUOTED(PROG_W, "$PROG_W") - fi - if test ! -z "$PROG_WHO" ; then - AC_DEFINE_UNQUOTED(PROG_WHO, "$PROG_WHO") - fi - if test ! -z "$PROG_LAST" ; then - AC_DEFINE_UNQUOTED(PROG_LAST, "$PROG_LAST") - fi - if test ! -z "$PROG_LASTLOG" ; then - AC_DEFINE_UNQUOTED(PROG_LASTLOG, "$PROG_LASTLOG") - fi - if test ! -z "$PROG_DF" ; then - AC_DEFINE_UNQUOTED(PROG_DF, "$PROG_DF") - fi - if test ! -z "$PROG_VMSTAT" ; then - AC_DEFINE_UNQUOTED(PROG_VMSTAT, "$PROG_VMSTAT") - fi - if test ! -z "$PROG_UPTIME" ; then - AC_DEFINE_UNQUOTED(PROG_UPTIME, "$PROG_UPTIME") - fi - if test ! -z "$PROG_UPTIME" ; then - AC_DEFINE_UNQUOTED(PROG_UPTIME, "$PROG_UPTIME") - fi - if test ! -z "$PROG_TAIL" ; then - AC_DEFINE_UNQUOTED(PROG_TAIL, "$PROG_TAIL") - fi + AC_PATH_ENTROPY_PROG(PROG_LS, ls) + AC_PATH_ENTROPY_PROG(PROG_NETSTAT, netstat) + AC_PATH_ENTROPY_PROG(PROG_ARP, arp) + AC_PATH_ENTROPY_PROG(PROG_IFCONFIG, ifconfig) + AC_PATH_ENTROPY_PROG(PROG_PS, ps) + AC_PATH_ENTROPY_PROG(PROG_W, w) + AC_PATH_ENTROPY_PROG(PROG_WHO, who) + AC_PATH_ENTROPY_PROG(PROG_LAST, last) + AC_PATH_ENTROPY_PROG(PROG_LASTLOG, lastlog) + AC_PATH_ENTROPY_PROG(PROG_DF, df) + AC_PATH_ENTROPY_PROG(PROG_VMSTAT, vmstat) + AC_PATH_ENTROPY_PROG(PROG_UPTIME, uptime) + AC_PATH_ENTROPY_PROG(PROG_IPCS, ipcs) + AC_PATH_ENTROPY_PROG(PROG_TAIL, tail) + AC_PATH_ENTROPY_PROG(PROG_LS, ls) + + INSTALL_SSH_PRNG_CMDS="yes" fi +AC_SUBST(INSTALL_SSH_PRNG_CMDS) + AC_ARG_WITH(catman, [ --with-catman=man|cat Install preformatted manpages[no]], @@ -932,4 +897,6 @@ if test ! -z "$blibpath" ; then AC_MSG_WARN([Please check and edit -blibpath in LDFLAGS in Makefile]) fi -AC_OUTPUT(Makefile) +AC_OUTPUT(Makefile ssh_prng_cmds) + + diff --git a/entropy.c b/entropy.c index 2766d90b6..9782e0f32 100644 --- a/entropy.c +++ b/entropy.c @@ -35,7 +35,7 @@ #include #include -RCSID("$Id: entropy.c,v 1.7 2000/05/01 14:03:56 damien Exp $"); +RCSID("$Id: entropy.c,v 1.8 2000/05/01 23:56:41 damien Exp $"); #ifdef EGD_SOCKET #ifndef offsetof @@ -111,8 +111,6 @@ void get_random_bytes(unsigned char *buf, int len) * FIXME: proper entropy estimations. All current values are guesses * FIXME: (ATL) do estimates at compile time? * FIXME: More entropy sources - * FIXME: (ATL) bring in entropy sources from file - * FIXME: (ATL) add heuristic to increase the timeout if needed */ /* slow command timeouts (all in milliseconds) */ @@ -120,7 +118,8 @@ void get_random_bytes(unsigned char *buf, int len) static int entropy_timeout_current = ENTROPY_TIMEOUT_MSEC; static int prng_seed_loaded = 0; -static int prng_seed_saved = 0; +static int prng_seed_saved = 0; +static int prng_commands_loaded = 0; typedef struct { @@ -131,9 +130,9 @@ typedef struct /* Increases by factor of two each timeout */ unsigned int sticky_badness; /* Path to executable */ - const char *path; + char *path; /* argv to pass to executable */ - const char *args[5]; + char *args[5]; } entropy_source_t; double stir_from_system(void); @@ -143,67 +142,10 @@ double stir_clock(double entropy_estimate); double stir_rusage(int who, double entropy_estimate); double hash_output_from_command(entropy_source_t *src, char *hash); -entropy_source_t entropy_sources[] = { -#ifdef PROG_LS - { 0.002, 0, 1, PROG_LS, { "ls", "-alni", "/var/log", NULL } }, - { 0.002, 0, 1, PROG_LS, { "ls", "-alni", "/var/adm", NULL } }, - { 0.002, 0, 1, PROG_LS, { "ls", "-alni", "/var/mail", NULL } }, - { 0.002, 0, 1, PROG_LS, { "ls", "-alni", "/var/spool/mail", NULL } }, - { 0.002, 0, 1, PROG_LS, { "ls", "-alni", "/proc", NULL } }, - { 0.002, 0, 1, PROG_LS, { "ls", "-alni", "/tmp", NULL } }, -#endif -#ifdef PROG_NETSTAT - { 0.005, 0, 1, PROG_NETSTAT, { "netstat","-an", NULL, NULL } }, - { 0.010, 0, 1, PROG_NETSTAT, { "netstat","-in", NULL, NULL } }, - { 0.002, 0, 1, PROG_NETSTAT, { "netstat","-rn", NULL, NULL } }, - { 0.002, 0, 1, PROG_NETSTAT, { "netstat","-s", NULL, NULL } }, -#endif -#ifdef PROG_ARP - { 0.002, 0, 1, PROG_ARP, { "arp","-a","-n", NULL } }, -#endif -#ifdef PROG_IFCONFIG - { 0.002, 0, 1, PROG_IFCONFIG, { "ifconfig", "-a", NULL, NULL } }, -#endif -#ifdef PROG_PS - { 0.003, 0, 1, PROG_PS, { "ps", "laxww", NULL, NULL } }, - { 0.003, 0, 1, PROG_PS, { "ps", "-al", NULL, NULL } }, - { 0.003, 0, 1, PROG_PS, { "ps", "-efl", NULL, NULL } }, -#endif -#ifdef PROG_W - { 0.005, 0, 1, PROG_W, { "w", NULL, NULL, NULL } }, -#endif -#ifdef PROG_WHO - { 0.001, 0, 1, PROG_WHO, { "who","-i", NULL, NULL } }, -#endif -#ifdef PROG_LAST - { 0.001, 0, 1, PROG_LAST, { "last", NULL, NULL, NULL } }, -#endif -#ifdef PROG_LASTLOG - { 0.001, 0, 1, PROG_LASTLOG, { "lastlog", NULL, NULL, NULL } }, -#endif -#ifdef PROG_DF - { 0.010, 0, 1, PROG_DF, { "df", NULL, NULL, NULL } }, - { 0.010, 0, 1, PROG_DF, { "df", "-i", NULL, NULL } }, -#endif -#ifdef PROG_VMSTAT - { 0.010, 0, 1, PROG_VMSTAT, { "vmstat", NULL, NULL, NULL } }, -#endif -#ifdef PROG_UPTIME - { 0.001, 0, 1, PROG_UPTIME, { "uptime", NULL, NULL, NULL } }, -#endif -#ifdef PROG_IPCS - { 0.001, 0, 1, PROG_IPCS, { "-a", NULL, NULL, NULL } }, -#endif -#ifdef PROG_TAIL - { 0.001, 0, 1, PROG_TAIL, { "tail", "-200", "/var/log/messages", NULL, NULL } }, - { 0.001, 0, 1, PROG_TAIL, { "tail", "-200", "/var/log/syslog", NULL, NULL } }, - { 0.001, 0, 1, PROG_TAIL, { "tail", "-200", "/var/adm/messages", NULL, NULL } }, - { 0.001, 0, 1, PROG_TAIL, { "tail", "-200", "/var/adm/syslog", NULL, NULL } }, - { 0.001, 0, 1, PROG_TAIL, { "tail", "-200", "/var/log/maillog", NULL, NULL } }, - { 0.001, 0, 1, PROG_TAIL, { "tail", "-200", "/var/adm/maillog", NULL, NULL } }, -#endif - { 0.000, 0, 0, NULL, { NULL, NULL, NULL, NULL, NULL } }, -}; +/* this is initialised from a file, by prng_read_commands() */ +entropy_source_t *entropy_sources = NULL; +#define MIN_ENTROPY_SOURCES 16 + double stir_from_system(void) @@ -597,6 +539,131 @@ prng_read_seedfile(void) { RAND_add(&seed, sizeof(seed), 0.0); } + +/* + * entropy command initialisation functions + */ +#define WHITESPACE " \t\n" + +int +prng_read_commands(char *cmdfilename) +{ + FILE *f; + char line[1024]; + char cmd[1024], path[256]; + double est; + char *cp; + int linenum; + entropy_source_t *entcmd; + int num_cmds = 64; + int cur_cmd = 0; + + f = fopen(cmdfilename, "r"); + if (!f) { + fatal("couldn't read entropy commands file %.100s: %.100s", + cmdfilename, strerror(errno)); + } + + linenum = 0; + + entcmd = (entropy_source_t *)xmalloc(num_cmds * sizeof(entropy_source_t)); + memset(entcmd, '\0', num_cmds * sizeof(entropy_source_t)); + + while (fgets(line, sizeof(line), f)) { + linenum++; + + /* skip leading whitespace, test for blank line or comment */ + cp = line + strspn(line, WHITESPACE); + if ((*cp == 0) || (*cp == '#')) + continue; /* done with this line */ + + switch (*cp) { + int arg; + char *argv; + + case '"': + /* first token, command args (incl. argv[0]) in double quotes */ + cp = strtok(cp, "\""); + if (cp==NULL) { + error("missing or bad command string, %.100s line %d -- ignored", + cmdfilename, linenum); + continue; + } + strncpy(cmd, cp, sizeof(cmd)); + /* second token, full command path */ + if ((cp = strtok(NULL, WHITESPACE)) == NULL) { + error("missing command path, %.100s line %d -- ignored", + cmdfilename, linenum); + continue; + } + if (strncmp("undef", cp, 5)==0) /* did configure mark this as dead? */ + continue; + + strncpy(path, cp, sizeof(path)); + /* third token, entropy rate estimate for this command */ + if ( (cp = strtok(NULL, WHITESPACE)) == NULL) { + error("missing entropy estimate, %.100s line %d -- ignored", + cmdfilename, linenum); + continue; + } + est = strtod(cp, &argv);/* FIXME: (ATL) no error checking here */ + + /* end of line */ + if ((cp = strtok(NULL, WHITESPACE)) != NULL) { + error("garbage at end of line %d in %.100s -- ignored", + linenum, cmdfilename); + continue; + } + + /* split the command args */ + cp = strtok(cmd, WHITESPACE); + arg = 0; argv = NULL; + do { + char *s = (char*)xmalloc(strlen(cp)+1); + strncpy(s, cp, strlen(cp)+1); + entcmd[cur_cmd].args[arg] = s; + arg++; + } while ((arg < 5) && (cp = strtok(NULL, WHITESPACE))); + if (strtok(NULL, WHITESPACE)) + error("ignored extra command elements (max 5), %.100s line %d", + cmdfilename, linenum); + + /* copy the command path and rate estimate */ + entcmd[cur_cmd].path = (char *)xmalloc(strlen(path)+1); + strncpy(entcmd[cur_cmd].path, path, strlen(path)+1); + entcmd[cur_cmd].rate = est; + /* initialise other values */ + entcmd[cur_cmd].sticky_badness = 1; + + cur_cmd++; + + /* If we've filled the array, reallocate it twice the size */ + /* Do this now because even if this we're on the last command, + we need another slot to mark the last entry */ + if (cur_cmd == num_cmds) { + num_cmds *= 2; + entcmd = xrealloc(entcmd, num_cmds * sizeof(entropy_source_t)); + } + break; + + default: + error("bad entropy command, %.100s line %d", cmdfilename, + linenum); + continue; + } + } + + /* zero the last entry */ + memset(&entcmd[cur_cmd], '\0', sizeof(entropy_source_t)); + /* trim to size */ + entropy_sources = xrealloc(entcmd, (cur_cmd+1) * sizeof(entropy_source_t)); + + debug("loaded %d entropy commands from %.100s", cur_cmd, cmdfilename); + + return (cur_cmd >= MIN_ENTROPY_SOURCES); +} + + #endif /* defined(EGD_SOCKET) || defined(RANDOM_POOL) */ #if defined(EGD_SOCKET) || defined(RANDOM_POOL) @@ -634,6 +701,12 @@ prng_seed_cleanup(void *junk) void seed_rng(void) { + if (!prng_commands_loaded) { + if (!prng_read_commands(SSH_PRNG_COMMAND_FILE)) + fatal("PRNG initialisation failed -- exiting."); + prng_commands_loaded = 1; + } + debug("Seeding random number generator."); debug("OpenSSL random status is now %i\n", RAND_status()); debug("%i bytes from system calls", (int)stir_from_system()); diff --git a/log-server.c b/log-server.c index 9070b6530..57c7b3715 100644 --- a/log-server.c +++ b/log-server.c @@ -15,7 +15,7 @@ */ #include "includes.h" -RCSID("$Id: log-server.c,v 1.9 2000/04/16 01:18:43 damien Exp $"); +RCSID("$Id: log-server.c,v 1.10 2000/05/01 23:56:42 damien Exp $"); #include #include "packet.h" @@ -25,7 +25,7 @@ RCSID("$Id: log-server.c,v 1.9 2000/04/16 01:18:43 damien Exp $"); #ifdef HAVE___PROGNAME extern char *__progname; #else /* HAVE___PROGNAME */ -const char *__progname = "sshd"; +static const char *__progname = "sshd"; #endif /* HAVE___PROGNAME */ static LogLevel log_level = SYSLOG_LEVEL_INFO; diff --git a/ssh.h b/ssh.h index 14e575ec9..b45350d12 100644 --- a/ssh.h +++ b/ssh.h @@ -13,7 +13,7 @@ * */ -/* RCSID("$Id: ssh.h,v 1.36 2000/04/29 23:30:46 damien Exp $"); */ +/* RCSID("$Id: ssh.h,v 1.37 2000/05/01 23:56:42 damien Exp $"); */ #ifndef SSH_H #define SSH_H @@ -129,6 +129,9 @@ #ifndef SSH_PRNG_SEED_FILE # define SSH_PRNG_SEED_FILE SSH_USER_DIR"/prng_seed" #endif /* SSH_PRNG_SEED_FILE */ +#ifndef SSH_PRNG_COMMAND_FILE +# define SSH_PRNG_COMMAND_FILE ETCDIR "/ssh_prng_cmds" +#endif /* SSH_PRNG_COMMAND_FILE */ /* * Per-user file containing host keys of known hosts. This file need not be diff --git a/ssh_prng_cmds.in b/ssh_prng_cmds.in new file mode 100644 index 000000000..16ecb30bb --- /dev/null +++ b/ssh_prng_cmds.in @@ -0,0 +1,50 @@ +# entropy gathering commands + +# Format is: "program-name args" path rate + +# The "rate" represents the number of bits of usuable entropy per +# byte of command output. Be conservative. + +"ls -alni /var/log" @PROG_LS@ 0.002 +"ls -alni /var/adm" @PROG_LS@ 0.002 +"ls -alni /var/mail" @PROG_LS@ 0.002 +"ls -alni /var/spool/mail" @PROG_LS@ 0.002 +"ls -alni /proc" @PROG_LS@ 0.002 +"ls -alni /tmp" @PROG_LS@ 0.002 + +"netstat -an" @PROG_NETSTAT@ 0.005 +"netstat -in" @PROG_NETSTAT@ 0.010 +"netstat -rn" @PROG_NETSTAT@ 0.002 +"netstat -s" @PROG_NETSTAT@ 0.002 + +"arp -a -n" @PROG_ARP@ 0.002 + +"ifconfig -a" @PROG_IFCONFIG@ 0.002 + +"ps laxww" @PROG_PS@ 0.003 +"ps -al" @PROG_PS@ 0.003 +"ps -efl" @PROG_PS@ 0.003 + +"w" @PROG_W@ 0.005 + +"who -i" @PROG_WHO@ 0.001 + +"last" @PROG_LAST@ 0.001 + +"lastlog" @PROG_LASTLOG@ 0.001 + +"df" @PROG_DF@ 0.010 +"df -i" @PROG_DF@ 0.010 + +"vmstat" @PROG_VMSTAT@ 0.010 +"uptime" @PROG_UPTIME@ 0.001 + +"ipcs -a" @PROG_IPCS@ 0.001 + +"tail -200 /var/log/messages" @PROG_TAIL@ 0.001 +"tail -200 /var/log/syslog" @PROG_TAIL@ 0.001 +"tail -200 /var/adm/messages" @PROG_TAIL@ 0.001 +"tail -200 /var/adm/syslog" @PROG_TAIL@ 0.001 +"tail -200 /var/adm/syslog/syslog.log" @PROG_TAIL@ 0.001 +"tail -200 /var/log/maillog" @PROG_TAIL@ 0.001 +"tail -200 /var/adm/maillog" @PROG_TAIL@ 0.001 -- cgit v1.2.3