瀏覽代碼

Add ./tarball_archive/mon-2.0.tar.gz

Wictor Lund 3 年之前
父節點
當前提交
cc08376b6f
共有 57 個文件被更改,包括 6705 次插入0 次删除
  1. 70 0
      mon/INSTALL
  2. 33 0
      mon/Makefile
  3. 30 0
      mon/Makefile.inc
  4. 19 0
      mon/lib/Makefile
  5. 752 0
      mon/lib/data.c
  6. 240 0
      mon/lib/data.h
  7. 139 0
      mon/lib/error.c
  8. 49 0
      mon/lib/error.h
  9. 330 0
      mon/lib/lex.c
  10. 104 0
      mon/lib/lex.h
  11. 110 0
      mon/lib/net.c
  12. 48 0
      mon/lib/net.h
  13. 80 0
      mon/lib/xmalloc.c
  14. 29 0
      mon/lib/xmalloc.h
  15. 25 0
      mon/mon/Makefile
  16. 150 0
      mon/mon/cpu.c
  17. 129 0
      mon/mon/if.c
  18. 106 0
      mon/mon/io.c
  19. 134 0
      mon/mon/mem.c
  20. 132 0
      mon/mon/mon.8
  21. 257 0
      mon/mon/mon.c
  22. 17 0
      mon/mon/mon.conf
  23. 89 0
      mon/mon/mon.h
  24. 121 0
      mon/mon/monnet.c
  25. 46 0
      mon/mon/monnet.h
  26. 219 0
      mon/mon/readconf.c
  27. 53 0
      mon/mon/readconf.h
  28. 74 0
      mon/mon2web/class_cpu.inc
  29. 234 0
      mon/mon2web/class_graph.inc
  30. 211 0
      mon/mon2web/class_if.inc
  31. 55 0
      mon/mon2web/class_io.inc
  32. 75 0
      mon/mon2web/class_mem.inc
  33. 7 0
      mon/mon2web/datasources.inc
  34. 7 0
      mon/mon2web/graph_cpu.php
  35. 7 0
      mon/mon2web/graph_if.php
  36. 7 0
      mon/mon2web/graph_io.php
  37. 7 0
      mon/mon2web/graph_mem.php
  38. 219 0
      mon/mon2web/index.php
  39. 245 0
      mon/mon2web/mon.css
  40. 二進制
      mon/mon2web/mon.png
  41. 二進制
      mon/mon2web/spacer.png
  42. 30 0
      mon/monmux/Makefile
  43. 194 0
      mon/monmux/c_monrrds.sh
  44. 246 0
      mon/monmux/monmux.8
  45. 310 0
      mon/monmux/monmux.c
  46. 62 0
      mon/monmux/monmux.conf
  47. 48 0
      mon/monmux/monmux.h
  48. 235 0
      mon/monmux/muxnet.c
  49. 53 0
      mon/monmux/muxnet.h
  50. 316 0
      mon/monmux/readconf.c
  51. 45 0
      mon/monmux/readconf.h
  52. 395 0
      mon/monmux/share.c
  53. 67 0
      mon/monmux/share.h
  54. 26 0
      mon/ports/mon/Makefile
  55. 3 0
      mon/ports/mon/pkg/DESCR
  56. 7 0
      mon/ports/mon/pkg/MESSAGE
  57. 9 0
      mon/ports/mon/pkg/PLIST

+ 70 - 0
mon/INSTALL

@@ -0,0 +1,70 @@
+Installation notes
+==================
+
+Real quick 
+==========
+make && make install && vi /etc/monmux.conf /etc/mon.conf && 
+	~mon/monmux/c_monrrds.sh all && 
+	/usr/local/libexec/monmux && 
+	/usr/local/libexec/mon
+
+Slightly less quick but with pretty pictures
+============================================
+- Install rrdtool. cd /usr/ports/net/rrdtool && make install should do the
+  trick.
+
+- Check Makefile.inc for settings. Things to watch out for are
+
+     + PREFIX = Where does the installation tree start. Defaults to
+                '/usr/local'.
+
+     + BINDIR = Where should the daemons be put, relative to $PREFIX. Defaults
+                to 'libexec'.
+
+     + MANDIR = Where should the manuals be installed, relative to
+                $PREFIX. Defaults to 'man'.
+
+     + SHRDIR = Where are the example configurations to be installed. Defaults
+                to 'share/mon'.
+
+- Run make && make install
+
+- Create an '/etc/mon.conf'. Note that there is an example configuration in
+  $PREFIX/$SHRDIR. 
+
+- Create a monmux.conf on the gatherer host. You can specify alternative
+  locations than '/etc/monmux.conf' using 'monmux -f <monmux.conf>'.
+
+- Create the rrd files where the incoming mon data is to be
+  stored. $PREFIX/$SHRDIR/c_monrrds.sh is your friend. Note that 'mon2web'
+  expects an '.../machine/*.rrd' style directory structure.
+
+- Fire up mon, monmux. Check system logs for any failures.
+
+- only if you need the webinterface: mon2web is not installed anywhere by
+  default. Copy the files to a convenient apache directory and modify the
+  mon2web/datasources.inc to reflect your filelocations. Note that it uses php,
+  so you may need to (cd /usr/ports/www/php4; make install).
+
+Installing mon on many machines
+===============================
+
+- Copy the mon-version.tar.gz to /usr/ports/distfiles/
+
+- Use the ports directory to create a mon package. 
+  cd ~mon/ports/mon
+  make package
+- The binary package can be found at 
+  /usr/ports/packages/<arch>/All/mon-2.0.tgz
+
+- You can transfer the resulting package to many machines and install it by
+  pkg_add it.
+
+This package is an OpenBSD application. You might be able to make monmux and
+mon2web run on POSIX compliant unixes, but the monitoring application itself
+(mon) is OpenBSD specific.
+
+Willem Dijkstra <wpd@xs4all.nl>
+
+$Id: INSTALL,v 1.1 2002/08/16 13:55:42 dijkstra Exp $
+

+ 33 - 0
mon/Makefile

@@ -0,0 +1,33 @@
+# $Id: Makefile,v 1.4 2002/08/16 13:55:42 dijkstra Exp $
+
+SUBDIR=	lib mon monmux 
+V=2.0
+
+.if make(clean)
+SUBDIR+= ports/mon
+.endif
+
+.include "Makefile.inc"
+
+all: _SUBDIRUSE
+clean: _SUBDIRUSE
+install: _SUBDIRUSE
+
+# Not all the stuff that I'm working on is ready for release
+dist: clean
+	@workdir=`basename ${.CURDIR}`; \
+	cd ..; \
+	echo Exporting mon-${V}.tar.gz; \
+	find $${workdir} -type f -print | egrep -v 'CVS|doc|clients|README|TODO|regress|#'| \
+		tar -czvf mon-${V}.tar.gz -I -
+
+
+_SUBDIRUSE: .USE
+.if defined(SUBDIR)
+	@for entry in ${SUBDIR}; do \
+		echo "===> $${entry}"; \
+		cd ${.CURDIR}/$${entry}; \
+		${MAKE} ${.MAKEFLAGS} ${.TARGET}; \
+	done
+.endif
+

+ 30 - 0
mon/Makefile.inc

@@ -0,0 +1,30 @@
+# $Id: Makefile.inc,v 1.2 2002/07/25 09:51:41 dijkstra Exp $
+
+AR=	ar
+CC=	cc
+.ifdef DEBUG
+CFLAGS= -g -Wall
+.else
+CFLAGS= -O2 -Wall
+.endif
+INSTALL=install
+LORDER=	lorder
+NROFF=	nroff
+RANLIB= ranlib
+STRIP=  strip
+
+BINDIR= libexec
+MANDIR=	man
+SHRDIR= share/mon
+
+.ifndef PREFIX
+PREFIX=/usr/local
+.endif
+
+.SUFFIXES: .c .o .8 .cat8
+
+.c.o:
+	${CC} ${CFLAGS} -c $<
+
+.8.cat8:
+	${NROFF} -mandoc $< > $@ || (rm -f $@; false)

+ 19 - 0
mon/lib/Makefile

@@ -0,0 +1,19 @@
+# $Id: Makefile,v 1.6 2002/07/20 14:28:29 dijkstra Exp $
+.include "../Makefile.inc"
+
+SRCS=   error.c lex.c xmalloc.c net.c data.c
+OBJS+=	${SRCS:R:S/$/.o/g}
+
+all: libmon.a
+
+libmon.a: ${OBJS}
+	@echo building standard mon library
+	@rm -f libmon.a
+	@${AR} cq libmon.a `${LORDER} ${OBJS} | tsort -q`
+	${RANLIB} libmon.a
+
+clean:
+	rm -f libmon.a ${OBJS}
+
+install: libmon.a
+

+ 752 - 0
mon/lib/data.c

@@ -0,0 +1,752 @@
+/* $Id: data.c,v 1.12 2002/07/25 09:51:42 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * The crc routine is from Rob Warnock <rpw3@sgi.com>, from the
+ * comp.compression FAQ.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * */
+
+/* Terminology: 
+ * 
+ * A host carrying a 'mon' is considered a 'source' of information. A single
+ * data 'stream' of information has a particular type: <cpu|mem|if|io>. A
+ * source can provide multiple 'streams' simultaniously. A source spools
+ * information towards a 'mux'. A 'stream' that has been converted to network
+ * representation is called a 'packedstream'.
+ */
+#include <assert.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "data.h"
+#include "error.h"
+#include "lex.h"
+#include "xmalloc.h"
+
+__BEGIN_DECLS
+int            bytelenvar(char);
+int            checklen(int, int, int);
+struct stream *create_stream(int, char *);
+char          *formatstrvar(char);
+char          *rrdstrvar(char);
+int            strlenvar(char);
+__END_DECLS
+
+/* Stream formats
+ * 
+ * Format specifications are strings of characters:
+ *
+ * L = u_int64 
+ * l = u_int32
+ * c = 3.2f <= u_int14 <= u_int16  (used in percentages)
+ */
+struct {
+    char type;
+    char *rrdformat;
+    char *strformat;
+    int  strlen;
+    int  bytelen;
+    u_int64_t max;
+} streamvar[] = {
+    {'L', ":%qu",    " %20qu",   22, sizeof(u_int64_t), (u_int64_t) 0xffffffffffffffff},
+    {'l', ":%lu",    " %10lu",   12, sizeof(u_int32_t), (u_int64_t) 0xffffffff},
+    {'c', ":%3.2f", "  %3.2f",    7, sizeof(u_int16_t), (u_int64_t) 100},
+    {'\0', NULL,         NULL,    0,                 0,             0}
+};
+/* streams of <type> have the packedstream <form> */
+struct {
+    int type;
+    char *form;
+} streamform[] = {
+    {MT_IO,  "LLL"},
+    {MT_CPU, "ccccc"},
+    {MT_MEM, "lllll"},
+    {MT_IF,  "llllllllll"},
+    {MT_EOT, ""}
+};
+
+struct {
+    int type;
+    int token;
+} streamtoken[] = {
+    {MT_IO,  LXT_IO},
+    {MT_CPU, LXT_CPU},
+    {MT_MEM, LXT_MEM},
+    {MT_IF,  LXT_IF},
+    {MT_EOT, LXT_BADTOKEN}
+};
+/* parallel crc32 table */
+u_int32_t 
+crc32_table[256];
+
+
+/* Convert lexical entities to stream entities */
+const int 
+token2type(const int token)
+{
+    int i;
+
+    for (i=0; streamtoken[i].type < MT_EOT; i++) 
+	if (streamtoken[i].token == token) 
+	    return streamtoken[i].type;
+
+    fatal("%s:%d: internal error: token (%d) could not be translated into a stream type", 
+	  __FILE__, __LINE__, token);
+
+    /* NOT REACHED */
+    return 0;
+}
+/* Convert stream entities to their ascii representation */
+const char *
+type2str(const int streamtype)
+{
+    int i;
+
+    for (i=0; streamtoken[i].type < MT_EOT; i++) 
+	if (streamtoken[i].type == streamtype) 
+	    return parse_opcode(streamtoken[i].token);
+
+    fatal("%s:%d: internal error: type (%d) could not be translated into ascii representation", 
+	  __FILE__, __LINE__, streamtype);
+
+    /* NOT REACHED */
+    return 0;
+}
+/* Return the maximum lenght of the ascii representation of type <type> */
+int 
+strlentype(int type) 
+{
+    int i = 0;
+    int sum = 0;
+
+    while (streamform[type].form[i])
+	sum += strlenvar(streamform[type].form[i++]);
+
+    return sum;
+}
+/* Return the maximum lenght of the ascii representation of streamvar <var> */
+int 
+strlenvar(char var)
+{
+    int i;
+
+    for (i=0; streamvar[i].type > '\0'; i++)
+	if (streamvar[i].type == var)
+	    return streamvar[i].strlen;
+    
+    fatal("%s:%d: internal error: type spefication for stream var '%c' not found", 
+	  __FILE__, __LINE__, var);
+    
+    /* NOT REACHED */
+    return 0;
+}
+/* Return the maximum lenght of the network representation of streamvar <var> */
+int 
+bytelenvar(char var)
+{
+    int i;
+
+    for (i=0; streamvar[i].type > '\0'; i++)
+	if (streamvar[i].type == var)
+	    return streamvar[i].bytelen;
+    
+    fatal("%s:%d: internal error: type spefication for stream var '%c' not found", 
+	  __FILE__, __LINE__, var);
+    
+    /* NOT REACHED */
+    return 0;
+}
+/* Return the ascii format string for streamvar <var> */
+char *
+formatstrvar(char var)
+{
+    int i;
+
+    for (i=0; streamvar[i].type > '\0'; i++)
+	if (streamvar[i].type == var)
+	    return streamvar[i].strformat;
+    
+    fatal("%s:%d: internal error: type spefication for stream var '%c' not found", 
+	  __FILE__, __LINE__, var);
+    
+    /* NOT REACHED */
+    return "";
+}
+/* Return the rrd format string for streamvar <var> */
+char *
+rrdstrvar(char var)
+{
+    int i;
+
+    for (i=0; streamvar[i].type > '\0'; i++)
+	if (streamvar[i].type == var)
+	    return streamvar[i].rrdformat;
+    
+    fatal("internal error: type spefication for stream var '%c' not found", var);
+    
+    /* NOT REACHED */
+    return "";
+}
+/* Check whether <extra> more bytes fit in <maxlen> when we are already at <start> */
+int 
+checklen(int maxlen, int current, int extra) 
+{
+    if ((current + extra) < maxlen) {
+	return 0;
+    } else {
+	warning("buffer overflow: max=%d, current=%d, extra=%d",
+		maxlen, current, extra);
+	return 1;
+    }
+}
+/* 
+ * Pack multiple arguments of a MT_TYPE into a network order bytestream.
+ * snpack returns the number of bytes actually stored.  
+ */
+int 
+snpack(char *buf, int maxlen, char *id, int type, ...)
+{
+    va_list ap;
+    u_int16_t c;
+    u_int32_t l;
+    u_int64_t q;
+    int i = 0;
+    int offset = 0;
+
+    if (type > MT_EOT) {
+	warning("stream type (%d) out of range", type);
+	return 0;
+    }
+
+    if ( maxlen < 2 ) {
+	fatal("%s:%d: maxlen too small", __FILE__, __LINE__);
+    } else {
+	buf[offset++] = type & 0xff;
+    }
+
+    if (id) {
+	if ((strlen(id) + 1) >= maxlen) {
+	    return 0;
+	} else {
+	    strncpy(&buf[offset], id, maxlen-1);
+	    offset += strlen(id);
+	}
+    }
+    buf[offset++] = '\0';
+	        	    
+    va_start(ap, type);
+    while (streamform[type].form[i] != '\0'){
+	/* check for buffer overflow */
+	if (checklen(maxlen, offset, bytelenvar(streamform[type].form[i])))
+	    return offset;
+		     
+	switch (streamform[type].form[i]) {
+	case 'c':
+	    c = va_arg(ap, u_int16_t);
+	    c = htons(c);
+	    bcopy(&c, buf + offset, sizeof(u_int16_t));
+	    offset += sizeof(u_int16_t);
+	    break;
+
+	case 'l': 
+	    l = va_arg(ap, u_int32_t);
+	    l = htonl(l);
+	    bcopy(&l, buf + offset, sizeof(u_int32_t));
+	    offset += sizeof(u_int32_t);
+	    break;
+
+	case 'L': 
+	    q = va_arg(ap, u_int64_t);
+	    q = htonq(q);
+	    bcopy(&q, buf + offset, sizeof(u_int64_t));
+	    offset += sizeof(u_int64_t);
+	    break;
+
+	default:
+	    warning("unknown stream format identifier");
+	    return 0;
+	}
+	i++;
+    }
+    va_end(ap);
+
+    return offset;
+}
+/* 
+ * Unpack a packedstream in buf into a struct packetstream. Returns the number
+ * of bytes actually read.  
+ * 
+ * Note that this function does "automatic" bounds checking; it uses a
+ * description of the packedstream (streamform) to parse the actual bytes. This
+ * description corresponds to the amount of bytes that will fit inside the
+ * packedstream structure.  */
+int 
+sunpack(char *buf, struct packedstream *ps)
+{
+    char *in, *out;
+    int i=0;
+    int type;
+    u_int16_t c;
+    u_int32_t l;
+    u_int64_t q;
+    
+    bzero(ps, sizeof(struct packedstream));
+
+    in = buf;
+
+    if ((*in) > MT_EOT) {
+	warning("unpack failure: stream type (%d) out of range", (*in));
+	return -1;
+    }
+
+    type = ps->type = (*in);
+    in++;
+    if ((*in) != '\0') {
+	strncpy(ps->args, in, sizeof(ps->args));
+	ps->args[sizeof(ps->args)-1]='\0';
+	in += strlen(ps->args);
+    } else {
+	ps->args[0] = '\0';
+    }
+
+    in++;
+
+    out = (char *)(&ps->data);
+
+    while (streamform[type].form[i] != '\0') {
+	switch (streamform[type].form[i]) {
+	case 'c':
+	    c = *((u_int16_t *)in);
+	    c = ntohs(c);
+	    bcopy(&c, (void *)out, sizeof(u_int16_t));
+	    in  += sizeof(u_int16_t);
+	    out += sizeof(u_int16_t);
+	    break;
+
+	case 'l': 
+	    l = *((u_int32_t *)in);
+	    l = ntohl(l);
+	    bcopy(&l, (void *)out, sizeof(u_int32_t));
+	    in  += sizeof(u_int32_t);
+	    out += sizeof(u_int32_t);
+	    break;
+
+	case 'L': 
+	    q = *((u_int64_t *)in);
+	    q = ntohq(q);
+	    bcopy(&q, (void *)out, sizeof(u_int64_t));
+	    in  += sizeof(u_int64_t);
+	    out += sizeof(u_int64_t);
+	    break;
+
+	default:
+	    warning("unknown stream format identifier");
+	    return 0;
+	}
+	i++;
+    }
+    return (in - buf);
+}
+/* Get the RRD or 'pretty' ascii representation of packedstream */
+int 
+ps2strn(struct packedstream *ps, char *buf, const int maxlen, int pretty)
+{
+    float c;
+    u_int64_t q;
+    u_int32_t l;
+    int i=0;
+    char *formatstr;
+    char *in, *out;
+    char vartype;
+    
+    in = (char *)(&ps->data);
+    out = (char *)buf;
+
+    while ((vartype = streamform[ps->type].form[i]) != '\0') {
+	/* check buffer overflow */
+	if (checklen(maxlen, (out-buf), strlenvar(vartype)))
+	    return 0;
+	
+	switch (pretty) {
+	case PS2STR_PRETTY:
+	    formatstr = formatstrvar(vartype);
+	    break;
+	case PS2STR_RRD:
+	    formatstr = rrdstrvar(vartype);
+	    break;
+	default:
+	    warning("%s:%d: unknown pretty identifier", __FILE__, __LINE__);
+	    return 0;
+	}
+
+	switch (vartype) {
+	case 'c':
+	    c = (*((u_int16_t *)in) / 10);
+	    snprintf(out, strlenvar(vartype), formatstr, c); 
+	    in  += sizeof(u_int16_t);
+	    break;
+
+	case 'l': 
+	    l = *((u_int32_t *)in);
+	    snprintf(out, strlenvar(vartype), formatstr, l); 
+	    in  += sizeof(u_int32_t);
+	    break;
+
+	case 'L': 
+	    q = *((u_int64_t *)in);
+	    snprintf(out, strlenvar(vartype), formatstr, q); 
+	    in  += sizeof(u_int64_t);
+	    break;
+
+	default:
+	    warning("Unknown stream format identifier");
+	    return 0;
+	}
+	out += strlen(out);
+	i++;
+    }
+    return (out - buf);
+}
+struct stream *
+create_stream(int type, char *args)
+{
+    struct stream *p;
+
+    if (type < 0 || type >= MT_EOT)
+	fatal("%s:%d: internal error: stream type unknown", __FILE__, __LINE__);
+
+    p = (struct stream *)xmalloc(sizeof(struct stream));
+    bzero(p, sizeof(struct stream));
+    p->type = type;
+
+    if (args != NULL)
+	p->args = xstrdup(args);
+    
+    return p;
+}
+/* Find the stream handle in a source */
+struct stream *
+find_source_stream(struct source *source, int type, char *args) 
+{
+    struct stream *p;
+
+    if (source == NULL)
+	return NULL;
+
+    SLIST_FOREACH(p, &source->sl, streams) {
+	if ((p->type == type) 
+	    && (((void *)args != (void *)p != NULL) 
+		&& strncmp(args, p->args, _POSIX2_LINE_MAX) == 0))
+	    return p;
+    }
+
+    return NULL;
+}
+/* Add a stream to a source */
+struct stream *
+add_source_stream(struct source *source, int type, char *args) 
+{
+    struct stream *p;
+
+    if (source == NULL)
+	return NULL;
+
+    if (find_source_stream(source, type, args) != NULL)
+	return NULL;
+
+    p = create_stream(type, args);
+
+    SLIST_INSERT_HEAD(&source->sl, p, streams);
+
+    return p;
+}
+/* Find a stream in a mux */
+struct stream *
+find_mux_stream(struct mux *mux, int type, char *args) 
+{
+    struct stream *p;
+
+    if (mux == NULL)
+	return NULL;
+
+    SLIST_FOREACH(p, &mux->sl, streams) {
+	if ((p->type == type) 
+	    && (((void *)args != (void *)p != NULL) 
+		&& strncmp(args, p->args, _POSIX2_LINE_MAX) == 0))
+	    return p;
+    }
+
+    return NULL;
+}
+/* Add a stream to a mux */
+struct stream *
+add_mux_stream(struct mux *mux, int type, char *args) 
+{
+    struct stream *p;
+
+    if (mux == NULL)
+	return NULL;
+
+    if (find_mux_stream(mux, type, args) != NULL)
+	return NULL;
+
+    p = create_stream(type, args);
+
+    SLIST_INSERT_HEAD(&mux->sl, p, streams);
+
+    return p;
+}
+/* Find a source by name in a sourcelist */
+struct source *
+find_source(struct sourcelist *sol, char *name) 
+{
+    struct source *p;
+
+    if (sol == NULL || SLIST_EMPTY(sol))
+	return NULL;
+
+    SLIST_FOREACH(p, sol, sources) {
+	if (((void *)name != (void *)p != NULL) 
+	    && strncmp(name, p->name, _POSIX2_LINE_MAX) == 0)
+	    return p;
+    }
+
+    return NULL;
+}
+/* Find a source by ip in a sourcelist */
+struct source *
+find_source_ip(struct sourcelist *sol, u_int32_t ip) 
+{
+    struct source *p;
+
+    if (sol == NULL || SLIST_EMPTY(sol))
+	return NULL;
+
+    SLIST_FOREACH(p, sol, sources) {
+	if (p->ip == ip)
+	    return p;
+    }
+
+    return NULL;
+}
+/* Add a source with to a sourcelist */
+struct source *
+add_source(struct sourcelist *sol, char *name)
+{
+    struct source* p;
+
+    if (sol == NULL)
+	return NULL;
+
+    if (find_source(sol, name) != NULL)
+	return NULL;
+
+    p = (struct source *)xmalloc(sizeof(struct source));
+    bzero(p, sizeof(struct source));
+    p->name = xstrdup(name);
+    SLIST_INSERT_HEAD(sol, p, sources);
+
+    return p;
+}
+/* Find a mux by name in a muxlist */
+struct mux *
+find_mux(struct muxlist *mul, char *name) 
+{
+    struct mux *p;
+
+    if (mul == NULL || SLIST_EMPTY(mul))
+	return NULL;
+
+    SLIST_FOREACH(p, mul, muxes) {
+	if (((void *)name != (void *)p != NULL) 
+	    && strncmp(name, p->name, _POSIX2_LINE_MAX) == 0)
+	    return p;
+    }
+
+    return NULL;
+}
+/* Add a mux to a muxlist */
+struct mux *
+add_mux(struct muxlist *mul, char *name)
+{
+    struct mux* p;
+
+    if (mul == NULL)
+	return NULL;
+
+    if (find_mux(mul, name) != NULL)
+	return NULL;
+
+    p = (struct mux *)xmalloc(sizeof(struct mux));
+    bzero(p, sizeof(struct mux));
+    p->name = xstrdup(name);
+    SLIST_INSERT_HEAD(mul, p, muxes);
+
+    return p;
+}
+/* Rename a mux */
+struct mux *
+rename_mux(struct muxlist *mul, struct mux *mux, char *name)
+{
+    if (mul == NULL || mux == NULL)
+	return NULL;
+
+    if (find_mux(mul, name) != NULL)
+	return NULL;
+
+    if (mux->name != NULL)
+	xfree(mux->name);
+
+    mux->name = xstrdup(name);
+
+    return mux;
+}
+void 
+free_muxlist(struct muxlist *mul) 
+{
+    struct mux *p, *np;
+
+    if (mul == NULL || SLIST_EMPTY(mul))
+	return;
+
+    p = SLIST_FIRST(mul);
+
+    while ( p ) {
+	np = SLIST_NEXT(p, muxes);
+
+	if (p->name != NULL) xfree(p->name);
+	close(p->clientsocket);
+	close(p->monsocket);
+	free_streamlist(&p->sl);
+	xfree(p);
+
+	p = np;
+    }
+}
+void 
+free_streamlist(struct streamlist *sl) 
+{
+    struct stream *p, *np;
+
+    if (sl == NULL || SLIST_EMPTY(sl))
+	return;
+    
+    p = SLIST_FIRST(sl);
+
+    while ( p ) {
+	np = SLIST_NEXT(p, streams);
+
+	if (p->args != NULL) xfree(p->args);
+	if (p->file != NULL) xfree(p->file);
+	xfree(p);
+
+	p = np;
+    }
+}
+void 
+free_sourcelist(struct sourcelist *sol) 
+{
+    struct source *p, *np;
+
+    if (sol == NULL || SLIST_EMPTY(sol))
+	return;
+    
+    p = SLIST_FIRST(sol);
+
+    while ( p ) {
+	np = SLIST_NEXT(p, sources);
+
+	if (p->name != NULL) xfree(p->name);
+	free_streamlist(&p->sl);
+	xfree(p);
+
+	p = np;
+    }
+}
+/* Calculate maximum buffer space needed for a single mon hit */
+int 
+calculate_churnbuffer(struct sourcelist *sol) { 
+    struct source *source; 
+    struct stream *stream; 
+    int prefixlen;
+    int maxlen;
+    int len; 
+    int n;
+    
+    /* determine length of a timestamp + ip as strings */
+    prefixlen = (sizeof(time_t)*3) + strlen(":") + 15 + strlen(":");
+    
+    len = n = 0; 
+    source = NULL; 
+    stream = NULL;
+    maxlen = 0;
+    /* determine maximum string size for a single source */
+    SLIST_FOREACH(source, sol, sources) {
+	len = prefixlen;
+	SLIST_FOREACH(stream, &source->sl, streams) {
+	    len += strlen(type2str(stream->type)) + strlen(":");
+	    len += strlen(stream->args) + strlen(":");
+	    len += strlentype(stream->type);
+	    n++;
+	}
+	if (len > maxlen) maxlen = len;
+    }
+    return maxlen;
+}
+/* Big endian CRC32 */
+u_int32_t
+crc32(const void *buf, unsigned int len)
+{
+    u_int8_t  *p;
+    u_int32_t crc;
+    
+    crc = 0xffffffff;
+    for (p = (u_int8_t *) buf; len > 0; ++p, --len)
+	crc = (crc << 8) ^ crc32_table[(crc >> 24) ^ *p];
+
+    return ~crc;
+}
+/* Init table for CRC32 */
+void
+init_crc32()
+{
+    unsigned int i, j;
+    u_int32_t c;
+
+    for (i = 0; i < 256; ++i) {
+	c = i << 24;
+	for (j = 8; j > 0; --j)
+	    c = c & 0x80000000 ? (c << 1) ^ MON_CRCPOLY : (c << 1);
+	crc32_table[i] = c;
+    }
+}

+ 240 - 0
mon/lib/data.h

@@ -0,0 +1,240 @@
+/* $Id: data.h,v 1.12 2002/08/11 20:02:53 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * A host carrying a 'mon' is considered a 'source' of information. A single
+ * data 'stream' of information has a particular type: <cpu|mem|if|io>. A
+ * source can provide multiple 'streams' simultaneously. A source spools
+ * information towards a 'mux'. A 'stream' that has been converted to network
+ * representation is called a 'packedstream'.
+ */
+#ifndef _MON_LIB_DATA_H
+#define _MON_LIB_DATA_H
+
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <limits.h>
+
+#include "lex.h"
+
+/* Polynominal to use for CRC generation */
+#define MON_CRCPOLY  0x04c11db7
+
+#ifdef WORDS_BIGENDIAN
+#define htonq(n) n
+#define ntohq(n) n
+#else
+static inline u_int64_t
+htonq (u_int64_t v)
+{
+   return htonl ((u_int32_t) (v >> 32))
+      | (u_int64_t) ntohl ((u_int32_t) v) << 32;
+}
+static inline u_int64_t
+ntohq (u_int64_t v) 
+{
+   return ntohl ((u_int32_t) (v >>32))
+      | (u_int64_t) ntohl ((u_int32_t) v) << 32;
+}
+#endif
+
+/* Mon packet version 
+ * version 1:
+ * mon_version:timestamp:length:crc:n*packedstream
+ * 
+ * Note that the data portion is limited. The current (31/03/2002) largest
+ * streamtype (if) needs 42 bytes without arguments. My _POSIX2_LINE_MAX is 2k,
+ * so that works out to about 38 packedstreams in a single mon packet.  
+ */
+#define MON_PACKET_VER  1
+struct monpacket {
+    struct {
+	u_int8_t mon_version;
+	u_int64_t timestamp;
+	u_int16_t length;
+	u_int32_t crc;
+    } header;
+    char data[_POSIX2_LINE_MAX];
+};  
+  
+/* The difference between a stream and a packed stream:
+ * - A stream ties stream information to a file. 
+ * - A packed stream is the measured data itself 
+ *
+ * A stream is meta data describing properties, a packed stream is the data itself.
+ */
+struct stream {
+    int type;
+    char *args;
+    char *file;
+    SLIST_ENTRY(stream) streams;
+};
+SLIST_HEAD(streamlist, stream);
+
+struct source {
+    char *name;
+    u_int32_t ip;
+    u_int16_t port;
+    struct streamlist sl;
+    SLIST_ENTRY(source) sources;
+};
+SLIST_HEAD(sourcelist, source);
+
+struct mux {
+    char *name;
+    int offset;
+    int clientsocket;
+    int monsocket;
+    struct monpacket packet;
+    struct sockaddr_in sockaddr;
+    struct streamlist sl;
+    u_int32_t senderr;
+    u_int32_t ip;
+    u_int16_t port;
+    SLIST_ENTRY(mux) muxes;
+};
+SLIST_HEAD(muxlist, mux);
+
+/* ps2str types */
+#define PS2STR_PRETTY 0
+#define PS2STR_RRD    1
+
+/* Stream types */
+#define MT_IO     0
+#define MT_CPU    1
+#define MT_MEM    2
+#define MT_IF     3
+#define MT_EOT    4
+
+/* NOTE: struct packetstream
+ *
+ * Unpacking of incoming packets is done via a packedstream structure. This
+ * structure defines the maximum amount of data that can be contained in a
+ * single network representation of a stream. 
+ *
+ * This sucks in the way of portability (e.g. adding multiple cpu types) and
+ * maintainabilty. This code will need to be refactored when I port it to other
+ * oses.  
+ */
+#define MON_PS_ARGLEN    16
+struct packedstream {
+    int type;
+    int padding;
+    char args[MON_PS_ARGLEN];
+    union {
+	struct { 
+	    u_int64_t mtotal_transfers;
+	    u_int64_t mtotal_seeks;
+	    u_int64_t mtotal_bytes;
+	} ps_io;
+	struct {
+	    u_int16_t muser;
+	    u_int16_t mnice;
+	    u_int16_t msystem;
+	    u_int16_t minterrupt;
+	    u_int16_t midle;
+	} ps_cpu;
+	struct {
+	    u_int32_t mreal_active; 
+	    u_int32_t mreal_total;
+	    u_int32_t mfree;
+	    u_int32_t mswap_used;
+	    u_int32_t mswap_total;
+	} ps_mem;
+	struct {
+	    u_int32_t mipackets;
+	    u_int32_t mopackets;
+	    u_int32_t mibytes; 
+	    u_int32_t mobytes;
+	    u_int32_t mimcasts; 
+	    u_int32_t momcasts;
+	    u_int32_t mierrors; 
+	    u_int32_t moerrors;
+	    u_int32_t mcolls;
+	    u_int32_t mdrops;
+	} ps_if;
+    } data;
+};
+
+#define mio_total_transfers  data.ps_io.mtotal_transfers
+#define mio_total_seeks      data.ps_io.mtotal_seeks
+#define mio_total_bytes      data.ps_io.mtotal_bytes
+#define mcpm_user            data.ps_cpu.uuser
+#define mcpm_nice            data.ps_cpu.unice
+#define mcpm_system          data.ps_cpu.usystem
+#define mcpm_interrupt       data.ps_cpu.uinterrupt
+#define mcpm_idle            data.ps_cpu.uidle
+#define mmem_real_active     data.ps_mem.mreal_active
+#define mmem_real_total      data.ps_mem.mreal_total
+#define mmem_free            data.ps_mem.mfree
+#define mmem_swap_msed       data.ps_mem.uswap_used
+#define mmem_swap_total      data.ps_mem.mswap_total
+#define mif_ipackets         data.ps_if.mipackets
+#define mif_opackets         data.ps_if.mopackets
+#define mif_ibytes           data.ps_if.mibytes
+#define mif_obytes           data.ps_if.mobytes
+#define mif_imcasts          data.ps_if.mimcasts
+#define mif_omcasts          data.ps_if.momcasts
+#define mif_ierrors          data.ps_if.mierrors
+#define mif_oerrors          data.ps_if.moerrors
+#define mif_colls            data.ps_if.mcolls
+#define mif_drops            data.ps_if.mdrops
+
+
+/* prototypes */
+__BEGIN_DECLS
+const char    *type2str(const int);
+const int      token2type(const int);
+int            calculate_churnbuffer(struct sourcelist *);
+int            ps2strn(struct packedstream *, char *, int, int);
+int            snpack(char *, int, char*, int, ...);
+int            strlentype(int);
+int            sunpack(char *, struct packedstream *);
+struct mux    *add_mux(struct muxlist *, char *);
+struct mux    *find_mux(struct muxlist *, char *);
+struct mux *   rename_mux(struct muxlist *, struct mux *, char *);
+struct source *add_source(struct sourcelist *, char *);
+struct source *find_source(struct sourcelist *, char *);
+struct source *find_source_ip(struct sourcelist *, u_int32_t);
+struct stream *add_mux_stream(struct mux *, int, char *);
+struct stream *add_source_stream(struct source *, int, char *); 
+struct stream *find_mux_stream(struct mux *,  int, char *);
+struct stream *find_source_stream(struct source *, int, char *);
+u_int32_t      crc32(const void*, unsigned int);
+void           free_muxlist(struct muxlist *);
+void           free_sourcelist(struct sourcelist *);
+void           free_streamlist(struct streamlist *);
+void           init_crc32();
+__END_DECLS
+#endif /*_MON_LIB_DATA_H*/

+ 139 - 0
mon/lib/error.c

@@ -0,0 +1,139 @@
+/* $Id: error.c,v 1.7 2002/07/20 14:28:29 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#include <sys/cdefs.h>
+
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <syslog.h>
+
+#include "error.h"
+
+__BEGIN_DECLS
+void output_message(int, char *, va_list); 
+__END_DECLS
+
+int flag_daemon = 0;
+int flag_debug = 0;
+
+enum { MON_LOG_FATAL, 
+       MON_LOG_WARNING,
+       MON_LOG_INFO,
+       MON_LOG_DEBUG } loglevels;
+
+struct {
+    int type;
+    int priority;
+    char *errtxt;
+    FILE *stream;
+} logmapping[] = {
+    {MON_LOG_FATAL,   LOG_ERR,     "fatal",   stderr},
+    {MON_LOG_WARNING, LOG_WARNING, "warning", stderr},
+    {MON_LOG_INFO,    LOG_INFO,    "",        stdout},
+    {MON_LOG_DEBUG,   LOG_DEBUG,   "debug",   stdout},
+    {-1,              0,           "",        NULL}
+};
+/* 
+ * Internal helper that actually outputs every 
+ * (fatal|warning|info|debug) message 
+ */
+void
+output_message(int level, char *fmt, va_list args) 
+{
+    char msgbuf[_POSIX2_LINE_MAX];
+    int loglevel;
+
+    if (level == MON_LOG_DEBUG && flag_debug == 0)
+	return;
+
+    vsnprintf(msgbuf, sizeof(msgbuf), fmt, args);
+    
+    for (loglevel = 0; logmapping[loglevel].type != -1; loglevel++) {
+	if (logmapping[loglevel].type == level)
+	    break;
+    }
+
+    if (logmapping[loglevel].type == -1)
+	fatal("%s:%d:internal error: illegal loglevel encountered",
+	      __FILE__, __LINE__);
+
+    if (flag_daemon) {
+	syslog(logmapping[loglevel].priority, msgbuf);
+    } else {
+	if (strlen(logmapping[loglevel].errtxt) > 0) {
+	    fprintf(logmapping[loglevel].stream, "%s: %s\n", 
+		    logmapping[loglevel].errtxt, msgbuf);
+	} else 
+	    fprintf(logmapping[loglevel].stream, "%s\n", msgbuf);
+
+	fflush(logmapping[loglevel].stream);
+    }
+}
+/* Output error and exit */
+__dead void 
+fatal(char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    output_message(MON_LOG_FATAL, fmt, ap);
+    va_end(ap);
+        
+    exit( 1 );
+}
+/* Warn and continue */
+void 
+warning(char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    output_message(MON_LOG_WARNING, fmt, ap);
+    va_end(ap);
+}
+/* Inform and continue */
+void 
+info(char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    output_message(MON_LOG_INFO, fmt, ap);
+    va_end(ap);
+}
+/* Debug statements only */
+void
+debug(char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    output_message(MON_LOG_DEBUG, fmt, ap);
+    va_end(ap);
+}
+

+ 49 - 0
mon/lib/error.h

@@ -0,0 +1,49 @@
+/* $Id: error.h,v 1.5 2002/04/01 14:44:16 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _MON_LIB_ERROR_H
+#define _MON_LIB_ERROR_H
+
+#include <sys/cdefs.h>
+
+extern char *__progname;
+extern int flag_debug;
+extern int flag_daemon;
+
+__BEGIN_DECLS
+__dead void fatal(char *, ...);
+void        warning(char *, ...);
+void        info(char *, ...);
+void        debug(char *, ...);
+__END_DECLS
+
+#endif /*_MON_LIB_ERROR_H*/

+ 330 - 0
mon/lib/lex.c

@@ -0,0 +1,330 @@
+/* $Id: lex.c,v 1.10 2002/07/25 09:51:42 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * This lexical analyser was written to be smaller than flex and with less
+ * features. Its attributes in random order: capable of multiple instances, one
+ * token lookahead, strings delimited by ' or ", comments can start anywhere
+ * with # and last until eol, max token size = _POSIX2_LINE_LENGTH. Tokens are
+ * defined in lex.h, the mapping of tokens to ascii happens here.
+ *
+ * Usage:
+ *
+ *    l = open_lex(filename); 
+ *    while (lex_nexttoken(l)) {
+ *       use l->token, l->op, l->value
+ *    }
+ *    close_lex(l); 
+ */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "xmalloc.h"
+#include "lex.h"
+#include "error.h"
+
+static struct {
+    const char *name;
+    int opcode;
+} keywords[] = {
+    { "{", LXT_BEGIN },
+    { "}", LXT_END },
+    { "(", LXT_OPEN },
+    { ")", LXT_CLOSE },
+    { ",", LXT_COMMA },
+    { ":", LXT_COLON },
+    { "accept", LXT_ACCEPT },
+    { "cpu", LXT_CPU },
+    { "if", LXT_IF },
+    { "in", LXT_IN },
+    { "io", LXT_IO },
+    { "mem", LXT_MEM },
+    { "monitor", LXT_MONITOR },
+    { "mux", LXT_MUX },
+    { "port", LXT_PORT },
+    { "source", LXT_SOURCE },
+    { "stream", LXT_STREAM },
+    { "to", LXT_TO },
+    { "write", LXT_WRITE },
+    { NULL, 0 }
+};
+#define KW_OPS "{},():"
+
+/* Return the number of the token pointed to by cp or LXT_BADTOKEN */
+int 
+parse_token(const char *cp)
+{
+    u_int i;
+
+    for (i = 0; keywords[i].name; i++)
+	if (strcasecmp(cp, keywords[i].name) == 0)
+	    return keywords[i].opcode;
+        
+    return LXT_BADTOKEN;
+}
+/* Return the ascii representation of an opcode */
+const char* 
+parse_opcode(const int op)
+{
+    u_int i;
+    
+    for (i=0; keywords[i].name; i++)
+	if (keywords[i].opcode == op)
+	    return keywords[i].name;
+
+    return NULL;
+}
+/* Read a line and increase buffer if needed */
+int 
+lex_readline(struct lex *l)
+{
+    char *bp;
+
+    bp = l->buffer;
+    
+    if (l->buffer) {
+	if ((l->curpos < l->endpos) && 
+	    ((l->bsize - l->endpos) < _POSIX2_LINE_MAX)) {
+	    l->bsize += _POSIX2_LINE_MAX;
+	    l->buffer = xrealloc(l->buffer, l->bsize);
+	    bp += l->endpos;
+	} else {
+	    l->curpos = 0;
+	    l->endpos = 0;
+	}
+    } else {
+	l->bsize = _POSIX2_LINE_MAX;
+	l->buffer = xmalloc(l->bsize);
+	bp = l->buffer;
+    }
+    
+    if (!fgets(bp, (l->buffer+l->bsize)-bp, l->fh))
+	return 0;
+    else {
+	l->endpos += strlen(bp) - 1;
+	return 1;
+    }
+}
+/* Copy char out of input stream */
+void 
+lex_copychar(struct lex *l) 
+{
+    l->token[l->tokpos]=l->buffer[l->curpos];
+    
+    if (++l->tokpos >= _POSIX2_LINE_MAX) {
+	l->token[_POSIX2_LINE_MAX-1] = '\0';
+	fatal("%s:%d: parse error at '%s'", l->filename, l->cline, l->token);
+	/* NOT REACHED */
+    }
+}
+/* Get next char, read next line if needed */
+int 
+lex_nextchar(struct lex *l)
+{
+    l->curpos++;
+
+    if (l->curpos > l->endpos)
+	if (!lex_readline(l))
+	    return 0;
+
+    if (l->buffer[l->curpos] == '\n') l->cline++;
+    
+    return 1;
+}
+/* Close of current token with a '\0' */
+void 
+lex_termtoken(struct lex *l)
+{
+    l->token[l->tokpos] = l->token[_POSIX2_LINE_MAX-1] = '\0';
+    l->tokpos=0;
+}
+/* Unget token; the lexer allows 1 look a head. */
+void 
+lex_ungettoken(struct lex *l) 
+{
+    l->unget = 1;
+}
+/* Get the next token in lex->token. return 0 if no more tokens found. */
+int
+lex_nexttoken(struct lex *l)
+{
+    /* return same token as last time if it has been pushed back */
+    if (l->unget) {
+	l->unget = 0;
+	return 1;
+    }
+	
+    l->op = LXT_BADTOKEN;
+    l->value = 0;
+    l->type = LXY_UNKNOWN;
+
+    /* find first non whitespace */
+    while (l->buffer[l->curpos] == ' ' || 
+	   l->buffer[l->curpos] == '\t' || 
+	   l->buffer[l->curpos] == '\r' ||
+	   l->buffer[l->curpos] == '\n' ||
+	   l->buffer[l->curpos] == '\0' ||
+	   l->buffer[l->curpos] == '#') {
+	/* flush rest of line if comment */
+	if (l->buffer[l->curpos] == '#') {
+	    while (l->buffer[l->curpos] != '\n') 
+		if (!lex_nextchar(l))
+		    return 0;
+	} else
+	    if (!lex_nextchar(l))
+		return 0;
+    }
+    
+    l->type = LXY_STRING;
+
+    /* "delimited string" */
+    if (l->buffer[l->curpos] == '"') {
+	if (!lex_nextchar(l)) {
+	    warning("%s:%d: unbalanced '\"'", l->filename, l->cline);
+	    return 0;
+	}
+	while (l->buffer[l->curpos] != '"') {
+	    lex_copychar(l);
+	    if (!lex_nextchar(l)) {
+		warning("%s:%d: unbalanced '\"'", l->filename, l->cline);
+		return 0;
+	    }
+	}
+	lex_termtoken(l);
+	lex_nextchar(l);
+	return 1;
+    }
+    
+    /* 'delimited string' */
+    if (l->buffer[l->curpos] == '\'') {
+	if (!lex_nextchar(l)) {
+	    warning("%s:%d: unbalanced \"\'\"", l->filename, l->cline);
+	    return 0;
+	}
+	while (l->buffer[l->curpos] != '\'') {
+	    lex_copychar(l);
+	    if (!lex_nextchar(l)) {
+		warning("%s:%d: unbalanced \"\'\"", l->filename, l->cline);
+		return 0;
+	    }
+	}
+	lex_termtoken(l);
+	lex_nextchar(l);
+	return 1;
+    }
+    
+    /* one char keyword */
+    if (strchr(KW_OPS, l->buffer[l->curpos])) {
+	lex_copychar(l);
+	lex_termtoken(l);
+	l->op = parse_token(l->token);
+	lex_nextchar(l);
+	return 1;
+    }
+
+    /* single keyword */
+    while (l->buffer[l->curpos] != ' ' &&
+	   l->buffer[l->curpos] != '\t' && 
+	   l->buffer[l->curpos] != '\r' &&
+	   l->buffer[l->curpos] != '\n' &&
+	   l->buffer[l->curpos] != '\0' &&
+	   l->buffer[l->curpos] != '#' &&
+	   (strchr(KW_OPS, l->buffer[l->curpos]) == NULL)) {
+	lex_copychar(l);
+	if (!lex_nextchar(l))
+	    break;
+    }
+    lex_termtoken(l);
+    l->op = parse_token(l->token);
+
+    /* number */
+    if (l->token[0] >= '0' && l->token[0] <= '9' ) {
+	if (strlen(l->token) == strspn(l->token, "0123456789")) {
+	    l->type = LXY_NUMBER;
+	    l->value = strtol(l->token, NULL , 10);
+	}
+    }
+    return 1;
+}    
+/* Create and initialize a lexical analyser */
+struct lex *
+open_lex(const char *filename)
+{
+    struct lex *l;
+    
+    l = xmalloc(sizeof(struct lex));
+    l->buffer = NULL;
+    l->cline = 1;
+    l->curpos = 0;
+    l->endpos = 0;
+    l->filename = filename;
+    l->op = LXT_BADTOKEN;
+    l->token = xmalloc(_POSIX2_LINE_MAX);
+    l->tokpos = 0;
+    l->type = LXY_UNKNOWN;
+    l->unget = 0;
+    l->value = 0;
+
+    if ((l->fh = fopen(l->filename, "r")) == NULL) {
+	warning("could not open file '%s':%s", 
+		l->filename, strerror(errno));
+	xfree(l);
+	return NULL;
+    }
+
+    lex_nextchar(l);
+    return l;
+}
+/* Destroy a lexical analyser */
+void 
+close_lex(struct lex *l)
+{
+    if (l == NULL) return;
+    if (l->fh) fclose(l->fh);
+    if (l->buffer) xfree(l->buffer);
+    if (l->token) xfree(l->token);
+    xfree(l);
+}
+/* Signal a parse error */
+void
+parse_error(struct lex *l, const char *s)
+{
+    warning("%s:%d: expected %s (found '%.8s')", 
+	    l->filename, l->cline, s, l->token);
+}
+

+ 104 - 0
mon/lib/lex.h

@@ -0,0 +1,104 @@
+/* $Id: lex.h,v 1.10 2002/07/25 09:51:42 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * This file defines the keyword tokens and lexer structure for the simple
+ * lexical analyser. 
+ */
+
+#ifndef _MON_LIB_LEX_H
+#define _MON_LIB_LEX_H
+
+#include <sys/cdefs.h>
+
+#include <stdio.h>
+
+/* Tokens known to lex. */
+#define LXT_BADTOKEN   0
+#define LXT_ACCEPT     1
+#define LXT_BEGIN      2
+#define LXT_CLOSE      3
+#define LXT_COLON      4
+#define LXT_COMMA      5
+#define LXT_CPU        6
+#define LXT_END        7
+#define LXT_IF         8
+#define LXT_IN         9
+#define LXT_IO        10
+#define LXT_MEM       11
+#define LXT_MONITOR   12
+#define LXT_MUX       13
+#define LXT_OPEN      14
+#define LXT_PORT      15
+#define LXT_SOURCE    16
+#define LXT_STREAM    17
+#define LXT_TO        18
+#define LXT_WRITE     19
+
+struct lex {
+    char *buffer;          /* current line(s) */
+    const char *filename;
+    FILE *fh;
+    char *token;           /* last token seen */
+    long value;            /* value of last token seen, if num */
+    int bsize;             /* size of buffer  */
+    int cline;             /* current lineno */
+    int curpos;            /* current position in buffer */
+    int endpos;            /* current maxpos in buffer */
+    int op;                /* opcode of token, if string */
+    int unget;             /* bool; token pushed back */
+    int tokpos;            /* current position in token buffer */
+    enum { LXY_STRING, LXY_NUMBER, LXY_UNKNOWN } 
+        type;              /* type of token in buffer */
+};
+
+__BEGIN_DECLS
+struct lex *open_lex(const char *);
+void        close_lex(struct lex *);
+int         lex_nexttoken(struct lex *);
+void        lex_ungettoken(struct lex *);
+const char* parse_opcode(int);
+int         parse_token(const char *);
+void        parse_error(struct lex *, const char *);
+__END_DECLS
+
+/* EXPECT(l,x) = next token in l must be opcode x or error.  */
+#define EXPECT(l, x)    do {                      \
+	lex_nexttoken((l));                       \
+	if ((l)->op != (x)) {                     \
+	    parse_error((l), parse_opcode((x)));  \
+	    return 0;                             \
+	}                                         \
+    } while (0);
+
+#endif /*_MON_LIB_LEX_H*/
+

+ 110 - 0
mon/lib/net.c

@@ -0,0 +1,110 @@
+/* $Id: net.c,v 1.5 2002/03/31 14:27:46 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <sys/types.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+/*
+ * lookup( hostname ) - hostname resolver
+ *
+ * Lookup returns 1 if hostname could be resolved. Resolved data is
+ * stored in the globals lookup_hostname, lookup_address and lookup_ip below.  
+ */
+char lookup_hostname[_POSIX2_LINE_MAX];
+char lookup_address[_POSIX2_LINE_MAX];
+u_int32_t lookup_ip;
+
+int 
+lookup(char *name)
+{
+    struct in_addr addr;
+    struct hostent *host;
+    extern int h_errno;
+    char *chostname, *sptr;
+    int i, bestl, newl;
+	
+    strcpy(lookup_hostname, "unresolved.");
+    strcpy(lookup_address, "unresolved.");
+    strncat(lookup_hostname, name, (_POSIX2_LINE_MAX - 1 - sizeof("unresolved")));
+    strncat(lookup_address, name, (_POSIX2_LINE_MAX - 1 - sizeof("unresolved")));
+
+    if (4 == sscanf(name, "%4d.%4d.%4d.%4d", &i, &i, &i, &i)) {
+	addr.s_addr = inet_addr(name);
+	if (addr.s_addr == 0xffffffff) 
+	    return 0;
+	    
+	host = gethostbyaddr((char *)&addr.s_addr, 4, AF_INET);
+    } else {
+	host = gethostbyname(name);
+    }
+    
+    if (host == NULL) {
+	return 0;
+    } else {
+	i = 0;
+	newl = 0;
+	bestl = 0;
+	sptr = (char *)(host->h_name ? host->h_name : host->h_aliases[0]);
+	chostname = NULL;
+
+	while (sptr) {
+	    newl = strlen(sptr);
+	    if (newl > bestl) {
+		bestl = newl;
+		chostname = sptr;
+	    }
+
+	    sptr = host->h_aliases[i++];
+	}
+	
+	if (chostname)
+	    snprintf(lookup_hostname, (_POSIX2_LINE_MAX - 1), "%s", chostname);
+	
+	if (*host->h_addr_list) {
+	    lookup_ip = ntohl(*(unsigned long *) *(char **) host->h_addr_list);
+
+	    snprintf(lookup_address, (_POSIX2_LINE_MAX - 1),"%u.%u.%u.%u", 
+		    (lookup_ip >> 24), (lookup_ip >> 16) & 0xff,
+		    (lookup_ip >> 8) & 0xff, lookup_ip & 0xff);
+	}
+    }
+
+    return 1;
+}

+ 48 - 0
mon/lib/net.h

@@ -0,0 +1,48 @@
+/* $Id: net.h,v 1.5 2002/04/04 20:49:58 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _MON_LIB_NET_H
+#define _MON_LIB_NET_H
+
+#include <sys/types.h>
+
+#define MONMUX_PORT  2100                     /* default monmux port */
+
+extern char lookup_hostname[];
+extern char lookup_address[];
+extern u_int32_t lookup_ip;
+
+__BEGIN_DECLS
+int lookup(char *);
+__END_DECLS
+
+#endif /* _MON_LIB_NET_H */

+ 80 - 0
mon/lib/xmalloc.c

@@ -0,0 +1,80 @@
+/* $Id: xmalloc.c,v 1.4 2002/03/31 14:27:46 dijkstra Exp $ */
+
+/*
+ * Author: Tatu Ylonen <ylo@cs.hut.fi>
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ *                    All rights reserved
+ * Versions of malloc and friends that check their results, and never return
+ * failure (they call fatal if they encounter an error).
+ *
+ * As far as I am concerned, the code I have written for this software
+ * can be used freely for any purpose.  Any derived versions of this
+ * software must be clearly marked as such, and if the derived work is
+ * incompatible with the protocol description in the RFC file, it must be
+ * called by a name other than "ssh" or "Secure Shell".
+ */
+
+#include <strings.h>
+#include <stdlib.h>
+
+#include "xmalloc.h"
+#include "error.h"
+
+void *
+xmalloc(size_t size)
+{
+    void *ptr;
+    
+    if (size == 0)
+	fatal("xmalloc: zero size");
+    
+    ptr = malloc(size);
+    
+    if (ptr == NULL)
+	fatal("xmalloc: out of memory (allocating %lu bytes)", (u_long) size);
+    
+    return ptr;
+}
+
+void *
+xrealloc(void *ptr, size_t new_size)
+{
+    void *new_ptr;
+    
+    if (new_size == 0)
+	fatal("xrealloc: zero size");
+
+    if (ptr == NULL)
+	new_ptr = malloc(new_size);
+    else
+	new_ptr = realloc(ptr, new_size);
+
+    if (new_ptr == NULL)
+	fatal("xrealloc: out of memory (new_size %lu bytes)", (u_long) new_size);
+
+    return new_ptr;
+}
+
+void 
+xfree(void *ptr)
+{
+    if (ptr == NULL)
+	fatal("xfree: NULL pointer given as argument");
+
+    free(ptr);
+}
+
+char 
+*xstrdup(const char *str)
+{
+    size_t len = strlen(str) + 1;
+    char *cp;
+
+    if (len == 0)
+	fatal("xstrdup: zero size");
+
+    cp = xmalloc(len);
+    strlcpy(cp, str, len);
+
+    return cp;
+}

+ 29 - 0
mon/lib/xmalloc.h

@@ -0,0 +1,29 @@
+/* $Id: xmalloc.h,v 1.4 2002/03/31 14:27:46 dijkstra Exp $ */
+
+/*
+ * Author: Tatu Ylonen <ylo@cs.hut.fi>
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ *                    All rights reserved
+ * Versions of malloc and friends that check their results, and never return
+ * failure (they call fatal if they encounter an error).
+ *
+ * As far as I am concerned, the code I have written for this software
+ * can be used freely for any purpose.  Any derived versions of this
+ * software must be clearly marked as such, and if the derived work is
+ * incompatible with the protocol description in the RFC file, it must be
+ * called by a name other than "ssh" or "Secure Shell".
+ */
+
+#ifndef _MON_LIB_XMALLOC_H
+#define _MON_LIB_XMALLOC_H
+
+#include <sys/types.h>
+
+/* Like malloc and friends, but these call fatal if something breaks */
+__BEGIN_DECLS
+void   *xmalloc(size_t);
+void   *xrealloc(void *, size_t);
+void    xfree(void *);
+char   *xstrdup(const char *);
+__END_DECLS
+#endif /*_MON_LIB_XMALLOC_H*/

+ 25 - 0
mon/mon/Makefile

@@ -0,0 +1,25 @@
+# $Id: Makefile,v 1.22 2002/07/25 14:25:29 dijkstra Exp $
+.include "../Makefile.inc"
+
+SRCS=	mon.c cpu.c mem.c if.c io.c readconf.c monnet.c
+OBJS+=	${SRCS:R:S/$/.o/g}
+LIBS=  -lkvm -L../lib -lmon
+CFLAGS+=-DMON_KVM -DNET_INET6 -I../lib
+
+all: mon mon.cat8
+
+mon: ${OBJS}
+	${CC} -o $@ ${OBJS} ${LIBS}
+.ifndef DEBUG
+	${STRIP} $@
+.endif
+
+clean:
+	rm -f mon mon.cat8 ${OBJS}
+
+install: mon mon.cat8 mon.conf
+	${INSTALL} -c -m 2555 -g kmem  -o root mon      ${PREFIX}/${BINDIR}
+	${INSTALL} -c -m 444  -g wheel -o root mon.cat8 ${PREFIX}/${MANDIR}/cat8/mon.0
+	${INSTALL} -d -m 555  -g bin   -o root ${PREFIX}/${SHRDIR}
+	${INSTALL} -c -m 444  -g bin   -o root mon.conf ${PREFIX}/${SHRDIR}
+

+ 150 - 0
mon/mon/cpu.c

@@ -0,0 +1,150 @@
+/* $Id: cpu.c,v 1.11 2002/05/09 12:39:50 dijkstra Exp $ */
+
+/* The author of this code is Willem Dijkstra (wpd@xs4all.nl).
+ * 
+ * The percentages function was written by William LeFebvre and is part of the
+ * 'top' utility. His copyright statement is below.
+ *
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *  */
+
+/*
+ *  Top users/processes display for Unix
+ *  Version 3
+ *
+ *  This program may be freely redistributed,
+ *  but this entire comment MUST remain intact.
+ *
+ *  Copyright (c) 1984, 1989, William LeFebvre, Rice University
+ *  Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
+ */
+
+/*
+ * Get current cpu statistics in percentages (total of all counts = 100.0)
+ * and returns them in mon_buf as
+ *
+ * user : nice : system : interrupt : idle
+ *
+ * This code is not re-entrant and UP only.
+ *
+ * This module uses the sysctl interface and can run as any user.
+ */
+#include <sys/dkstat.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+
+#include "error.h"
+#include "mon.h"
+
+__BEGIN_DECLS
+int percentages(int, float *, long *, long *, long *);
+__END_DECLS
+
+/* Globals for this module all start with cp_ */
+static int cp_time_mib[] = { CTL_KERN, KERN_CPTIME };
+static size_t cp_size;
+static long cp_time[CPUSTATES];
+static long cp_old[CPUSTATES];
+static long cp_diff[CPUSTATES];
+static float cp_states[CPUSTATES];
+/*
+ *  percentages(cnt, out, new, old, diffs) - calculate percentage change
+ *      between array "old" and "new", putting the percentages i "out".
+ *      "cnt" is size of each array and "diffs" is used for scratch space.
+ *      The array "old" is updated on each call.
+ *      The routine assumes modulo arithmetic.  This function is especially
+ *      useful on BSD mchines for calculating cpu state percentages.
+ */
+int 
+percentages(int cnt, float *out, register long *new, register long *old, long *diffs)
+{
+    register int i;
+    register long change;
+    register long total_change;
+    register long *dp;
+    long half_total;
+
+    /* initialization */
+    total_change = 0;
+    dp = diffs;
+
+    /* calculate changes for each state and the overall change */
+    for (i = 0; i < cnt; i++) {
+	if ((change = *new - *old) < 0) {
+	    /* this only happens when the counter wraps */
+	    change = ((unsigned int)*new-(unsigned int)*old);
+        }
+	total_change += (*dp++ = change);
+	*old++ = *new++;
+    }
+  
+    /* avoid divide by zero potential */
+    if (total_change == 0)
+	total_change = 1;
+  
+    /* calculate percentages based on overall change, rounding up */
+    half_total = total_change / 2l;
+    for (i = 0; i < cnt; i++)
+	*out++ = ((*diffs++ * 1000 + half_total) / total_change);
+  
+    /* return the total in case the caller wants to use it */
+    return total_change;
+}
+/* Prepare cpu module for use */
+void 
+init_cpu(char *s) 
+{
+    char buf[_POSIX2_LINE_MAX];
+
+    cp_size = sizeof(cp_time);
+    /* Call get_cpu once to fill the cp_old structure */
+    get_cpu(buf, sizeof(buf), NULL);
+
+    info("started module cpu(%s)", s);
+}
+/* Get new cpu measurements */
+int 
+get_cpu(char *mon_buf, int maxlen, char *s) 
+{
+    int total;
+
+    if (sysctl(cp_time_mib, 2, &cp_time, &cp_size, NULL, 0) < 0) {
+	warning("%s:%d: sysctl kern.cp_time failed", __FILE__, __LINE__);
+	return 0;
+    }
+
+    /* convert cp_time counts to percentages */
+    total = percentages(CPUSTATES, cp_states, cp_time, cp_old, cp_diff);
+    
+    return snpack(mon_buf, maxlen, s, MT_CPU,
+		  (u_int16_t) cp_states[CP_USER], 
+		  (u_int16_t) cp_states[CP_NICE], 
+		  (u_int16_t) cp_states[CP_SYS], 
+		  (u_int16_t) cp_states[CP_INTR], 
+		  (u_int16_t) cp_states[CP_IDLE]);
+}

+ 129 - 0
mon/mon/if.c

@@ -0,0 +1,129 @@
+/* $Id: if.c,v 1.4 2002/08/09 08:21:31 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * Get current interface statistics from kernel and return them in mon_buf as
+ *
+ * ipackets : opackets : ibytes : obytes : imcasts : omcasts : ierrors : oerrors : colls : drops
+ *
+ * This module uses the kvm interface to read kernel values directly. It needs
+ * a valid kvm handle and will only work if this has been obtained by someone
+ * belonging to group kmem. Note that these priviledges (sgid kmem) can be
+ * dropped right after the handle has been obtained.  
+ * 
+ * Re-entrant code: globals are used to speedup calculations for all interfaces.
+ */
+
+#include <sys/types.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#include <netipx/ipx.h>
+#include <netipx/ipx_if.h>
+#include <netiso/iso.h>
+#include <netiso/iso_var.h>
+
+#include <string.h>
+#include <limits.h>
+
+#include "error.h"
+#include "mon.h"
+
+/* Globals for this module start with if_ */
+struct ifnet if_ifnet;
+size_t if_size_ifnet;
+struct ifnet_head if_ifhead;   
+size_t if_size_ifhead;
+union {
+    struct ifaddr   ifa;
+    struct in_ifaddr in;
+    struct in6_ifaddr in6;
+    struct ns_ifaddr ns;
+    struct ipx_ifaddr ipx;
+    struct iso_ifaddr iso;
+} if_ifaddr;
+size_t if_size_ifaddr;
+char if_name[IFNAMSIZ];
+/* Prepare if module for first use */
+void 
+init_if(char *s) 
+{
+    if_size_ifnet = sizeof if_ifnet;
+    if_size_ifhead = sizeof if_ifhead;
+    if_size_ifaddr = sizeof if_ifaddr;
+
+    info("started module if(%s)", s);
+}
+/* Get interface statistics */
+int 
+get_if(char *mon_buf, int maxlen, char *interface) 
+{
+    u_long ifnetptr;
+
+    ifnetptr = mon_nl[MON_IFNET].n_value;
+    if (ifnetptr == 0)
+	fatal("%s:%d: ifnet = symbol not defined", __FILE__, __LINE__);
+
+    /* obtain first ifnet structure in kernel memory */
+    if (kread(ifnetptr, (char *) &if_ifhead, if_size_ifhead)) {
+	warning("if(%s) failed", interface);
+	return 0;
+    }
+
+    ifnetptr = (u_long) if_ifhead.tqh_first;
+  
+    while (ifnetptr) {
+	kread(ifnetptr, (char *) &if_ifnet, if_size_ifnet);
+	bcopy(if_ifnet.if_xname, (char *) &if_name, IFNAMSIZ);
+	if_name[IFNAMSIZ - 1] = '\0';
+	ifnetptr = (u_long) if_ifnet.if_list.tqe_next;
+
+	if (interface != 0 && strncmp(if_name, interface, IFNAMSIZ) == 0) {
+	    return snpack(mon_buf, maxlen, interface, MT_IF,
+		      if_ifnet.if_ipackets, if_ifnet.if_opackets, 
+		      if_ifnet.if_ibytes, if_ifnet.if_obytes, 
+		      if_ifnet.if_imcasts, if_ifnet.if_omcasts, 
+		      if_ifnet.if_ierrors, if_ifnet.if_oerrors, 
+		      if_ifnet.if_collisions, if_ifnet.if_iqdrops);
+	}
+    }
+
+    return 0;
+}
+

+ 106 - 0
mon/mon/io.c

@@ -0,0 +1,106 @@
+/* $Id: io.c,v 1.4 2002/08/09 08:21:31 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * Get current disk transfer statistics from kernel and return them in mon_buf as
+ * 
+ * total nr of transfers : total seeks : total bytes transferred
+ *
+ * This module uses the kvm interface to read kernel values directly. It needs
+ * a valid kvm handle and will only work if this has been obtained by someone
+ * belonging to group kmem. Note that these priviledges can be dropped right
+ * after the handle has been obtained. (sgid kmem on executable) 
+ *
+ * Re-entrant code: globals are used to speedup calculations for all disks.
+ * 
+ * TODO: di_name is too large
+ */
+#include <sys/disk.h>
+
+#include <limits.h>
+#include <string.h>
+
+#include "error.h"
+#include "mon.h"
+
+/* Globals for this module start with io_ */
+struct disk io_disk;
+size_t io_size_disk;
+struct disklist_head io_dihead;
+size_t io_size_dihead;
+char io_name[_POSIX2_LINE_MAX];
+/* Prepare io module for first use */
+void 
+init_io(char *s) 
+{
+    io_size_disk = sizeof io_disk;
+    io_size_dihead = sizeof io_dihead;
+
+    info("started module io(%s)", s);
+}
+/* Get new io statistics */
+int 
+get_io(char *mon_buf, int maxlen, char *disk) 
+{
+    u_long diskptr;
+
+    diskptr = mon_nl[MON_DL].n_value;
+    if (diskptr == 0)
+	fatal("%s:%d: dl = symbol not defined", __FILE__, __LINE__);
+
+    /* obtain first disk structure in kernel memory */
+    if (kread(mon_nl[MON_DL].n_value, (char *) &io_dihead, io_size_dihead)) {
+	warning("io(%s) failed", disk);
+	return 0;
+    }
+
+    diskptr = (u_long) io_dihead.tqh_first;
+
+    while (diskptr) {
+	kread(diskptr, (char *) &io_disk, io_size_disk);
+	diskptr = (u_long) io_disk.dk_link.tqe_next;
+
+	if (io_disk.dk_name != NULL) {
+	    kread((u_long)io_disk.dk_name, (char *) &io_name, _POSIX2_LINE_MAX);
+	    io_name[_POSIX2_LINE_MAX - 1] = '\0';
+
+	    if (disk != NULL && strncmp(io_name, disk, _POSIX2_LINE_MAX) == 0) {
+		return snpack(mon_buf, maxlen, disk, MT_IO,
+			      io_disk.dk_xfer, io_disk.dk_seek,
+			      io_disk.dk_bytes);
+	    }
+	}
+    }
+
+    return 0;
+}
+

+ 134 - 0
mon/mon/mem.c

@@ -0,0 +1,134 @@
+/* $Id: mem.c,v 1.11 2002/04/09 05:37:37 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * Get current memory statistics in bytes; reports them back in mon_buf as
+ *
+ * real active : real total : free : [swap used : swap total]
+ *
+ * This code is not re-entrant.
+ */
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/swap.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+
+#include "error.h"
+#include "mon.h"
+#include "xmalloc.h"
+
+#define pagetob(size) ((size) << me_pageshift)
+
+/* Globals for this module all start with me_ */
+static int me_pageshift;
+static long me_stats[4];
+static int me_vm_mib[] = {CTL_VM, VM_METER};
+static struct vmtotal me_vmtotal;
+static size_t me_vmsize;
+static int me_pagesize;
+static int me_nswap;
+struct swapent *me_swdev = NULL;
+/* Prepare mem module for first use */
+void
+init_mem(char *s) 
+{
+    me_pagesize = sysconf( _SC_PAGESIZE );
+    me_pageshift = 0;
+    while (me_pagesize > 1) {
+	me_pageshift++;
+	me_pagesize >>= 1;
+    }
+
+    /* get total -- systemwide main memory usage structure */
+    me_vmsize = sizeof(me_vmtotal);
+
+    /* determine number of swap entries */
+    me_nswap = swapctl(SWAP_NSWAP, 0, 0);
+  
+    if (me_swdev) 
+	xfree(me_swdev);
+
+    if (me_nswap != 0)
+      me_swdev = xmalloc(me_nswap * sizeof(*me_swdev));
+    else
+      me_swdev = NULL;
+
+    if (me_swdev == NULL && me_nswap != 0) 
+	me_nswap=0; 
+
+    info("started module mem(%s)", s);
+}
+/* Get memory statistics */
+int 
+get_mem(char *mon_buf, int maxlen, char *s)
+{
+    int i,rnswap;
+
+    if (sysctl(me_vm_mib, 2, &me_vmtotal, &me_vmsize, NULL, 0) < 0) {
+	warning("%s:%d: sysctl failed", __FILE__, __LINE__);
+	bzero(&me_vmtotal, sizeof(me_vmtotal));
+    }
+
+    /* convert memory stats to Kbytes */
+    me_stats[0] = pagetob(me_vmtotal.t_arm);
+    me_stats[1] = pagetob(me_vmtotal.t_rm);
+    me_stats[2] = pagetob(me_vmtotal.t_free);
+
+    rnswap = swapctl(SWAP_STATS, me_swdev, me_nswap);
+    if (rnswap == -1) { 
+	/* A swap device may have been added; increase and retry */
+	init_mem(NULL); 
+	rnswap = swapctl(SWAP_STATS, me_swdev, me_nswap);
+    }
+
+    me_stats[3] = me_stats[4] = 0;
+    if (rnswap == me_nswap) { /* Read swap succesfully */
+	/* Total things up */
+	for (i = 0; i < me_nswap; i++) {
+	    if (me_swdev[i].se_flags & SWF_ENABLE) {
+		me_stats[3] += (me_swdev[i].se_inuse * DEV_BSIZE);
+		me_stats[4] += (me_swdev[i].se_nblks * DEV_BSIZE);
+	    }
+	}
+    }
+
+    return snpack(mon_buf, maxlen, s, MT_MEM, 
+		  me_stats[0], me_stats[1], me_stats[2], 
+		  me_stats[3], me_stats[4]);
+}
+

+ 132 - 0
mon/mon/mon.8

@@ -0,0 +1,132 @@
+.\"  -*- nroff -*-
+.\"
+.\" Copyright (c) 2001-2002 Willem Dijkstra
+.\" All rights reserved.
+.\" 
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 
+.\"    - Redistributions of source code must retain the above copyright
+.\"      notice, this list of conditions and the following disclaimer.
+.\"    - Redistributions in binary form must reproduce the above
+.\"      copyright notice, this list of conditions and the following
+.\"      disclaimer in the documentation and/or other materials provided
+.\"      with the distribution.
+.\" 
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+.\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+.\" FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+.\" COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+.\" CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\" 
+.Dd April 5, 2002
+.Dt MON 8
+.Os
+.Sh NAME
+.Nm mon
+.Nd system monitor
+.Sh SYNOPSIS
+.Nm 
+.Op Fl vd
+.Pp
+.Sh DESCRIPTION
+.Nm
+is a lightweight system monitor that measures cpu, memory, interface and disk
+statistics every 5 seconds. This information is then spooled to
+.Xr monmux 8
+for further processing. 
+.Pp
+.Nm
+has been designed to inflict minimal performance and security impact on the system it monitors. 
+.Xr monmux 8
+has performance impact proportional to the amount of streams it needs to manage. Ideally 
+.Xr monmux 
+should live on a different system and collect data from several 
+.Nm
+instances in a LAN. 
+.Lp
+.Nm
+needs
+.Ar kmem
+priviledges in order to obtain it's data.
+.Lp
+The options:
+.Bl -tag -width Ds
+.It Fl a
+Show version information.
+.It Fl d
+Stop 
+.Nm
+from becoming a daemon and show debug information on stdout.
+.El
+.Sh CONFIGURATION
+.Nm
+obtains configuration data from 
+.Pa /etc/mon.conf .
+The configuration file contains monitor stanzas that define what resources should be monitored and to which 
+.Xr monmux 8
+the information should be streamed to.
+.Pp
+Multiple monitor statements to different muxes are allowed. Whitespace, newlines and text behind '#' are ignored. The format in BNF:
+.Pp
+.nf
+monitor-rule = "monitor" "{" resources "}" "stream" ["to"] host [ port ]
+resources    = resource ["(" argument ")"] [ ","|" " resources ]
+resource     = "cpu" | "mem" | "if" | "io"
+host         = ip4addr | hostname
+port         = [ "port" | ":" | "," ] portnumber
+argument     = number | interfacename | diskname
+.fi
+.Sh EXAMPLE
+Here is an example 
+.Ar mon.conf
+that monitors cpu, memory, interfaces xl0/de0/lo0/wi0, disks wd[0-3]/cd[0-1]
+and streams that information to localhost on port 2100.
+.Pp
+.nf
+monitor { cpu(0),  mem,     if(xl0), if(de0),
+          if(lo0), if(wi0), io(wd0), io(wd1), 
+          io(wd2), io(wd3), io(cd0), io(cd1), 
+          io(ccd0)} stream to 127.0.0.1 2100
+.fi
+.Sh SIGNALS
+.Bl -tag -width Ds
+.It SIGHUP
+Causes
+.Nm
+to read 
+.Pa /etc/mon.conf .
+.Nm
+will keep the old configuration if errors occured during parsing of the configuration file.
+.Sh FILES
+.Bl -tag -width Ds
+.It Pa /var/run/mon.pid
+Contains the program id of the
+.Nm
+daemon.
+.It Pa /etc/mon.conf
+.Nm
+system wide configuration file. 
+.El
+.Sh BUGS
+Every monitored resource mentioned 
+.Pa /etc/mon.conf 
+gets queried. Mentioning, for example, cpu(0) twice for different muxes will result in two distinct cpu(0) measurement actions. 
+.Pp
+.Nm 
+does not check whether resources mentioned in 
+.Pa /etc/mon.conf
+exist. 
+.Sh AUTHOR
+Willem Dijkstra <wpd@xs4all.nl>
+.Sh SEE ALSO
+.Xr monmux 8 
+ 

+ 257 - 0
mon/mon/mon.c

@@ -0,0 +1,257 @@
+/* $Id: mon.c,v 1.19 2002/08/11 19:52:16 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <sys/param.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "data.h"
+#include "error.h"
+#include "mon.h"
+#include "monnet.h"
+#include "net.h"
+#include "readconf.h"
+#include "xmalloc.h"
+
+__BEGIN_DECLS
+void alarmhandler(int);
+void exithandler(int);
+void huphandler(int);
+__END_DECLS
+
+int flag_hup = 0;
+
+#ifdef MON_KVM
+kvm_t *kvmd;
+struct nlist mon_nl[] = {
+    {"_ifnet"},      /* MON_IFNET = 0  (mon.h)*/
+    {"_disklist"},   /* MON_DL    = 1  (mon.h)*/
+    {""},
+};
+/* Read kernel memory */
+int 
+kread(u_long addr, char *buf, int size)
+{
+    if (kvm_read(kvmd, addr, buf, size) != size) {
+	warning("kvm_read:%s", kvm_geterr(kvmd));
+	return 1;
+    }
+
+    return (0);
+}
+#endif /* MON_KVM */
+
+/* map stream types to inits and getters */
+struct funcmap streamfunc[] = {
+    {MT_IO,  init_io,  get_io},
+    {MT_CPU, init_cpu, get_cpu},
+    {MT_MEM, init_mem, get_mem},
+    {MT_IF,  init_if,  get_if},
+    {MT_EOT, NULL, NULL}
+};
+
+/* Alarmhandler that gets called every MON_INTERVAL */
+void 
+alarmhandler(int s) {
+    /* EMPTY */
+}
+void
+exithandler(int s) {
+    info("received signal %d - quitting", s);
+    exit(1);
+}
+void
+huphandler(int s) {
+    info("hup received");
+    flag_hup = 1;
+}
+/* 
+ * Mon is a system measurement utility. 
+ *
+ * The main goals mon hopes to accomplish is:
+ * - to take fine grained measurements of system parameters 
+ * - with minimal performance impact 
+ * - in a secure way.
+ * 
+ * Measuring system parameters (e.g. interfaces) sometimes means traversing
+ * lists in kernel memory. Because of this the measurement of data has been
+ * decoupled from the processing and storage of data. Storing the measured
+ * information that mon provides is done by a second program, called monmux.
+ * 
+ * Mon can keep track of cpu, memory, disk and network interface
+ * interactions. Mon was built specifically for OpenBSD.
+ */
+int 
+main(int argc, char *argv[])
+{
+    struct muxlist mul, newmul;
+    struct itimerval alarminterval;
+    struct stream *stream;
+    struct mux *mux;
+    char mon_buf[_POSIX2_LINE_MAX];
+    FILE *f;
+#ifdef MON_KVM
+    char *nlistf = NULL, *memf = NULL;
+#endif
+    int ch;
+
+    SLIST_INIT(&mul);
+
+    /* reset flags */
+    flag_debug = 0;
+    flag_daemon = 0;
+
+    while ((ch = getopt(argc, argv, "dv")) != -1) {
+	switch (ch) {
+	case 'd':
+	    flag_debug = 1;
+	    break;
+	case 'v':
+	    info("mon version %s", MON_VERSION);
+	default:
+	    info("usage: %s [-d] [-v]", __progname);
+	    exit(1);
+	}
+    }
+
+#ifdef MON_KVM
+    /* Populate our kernel name list */
+    if ((kvmd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, mon_buf)) == NULL)
+	fatal("kvm_open: %s", mon_buf);
+    
+    if (kvm_nlist(kvmd, mon_nl) < 0 || mon_nl[0].n_type == 0) {
+	if (nlistf)
+	    fatal("%s: no namelist", nlistf);
+	else
+	    fatal("no namelist");
+    }
+#endif /* MON_KVM */
+
+    setegid(getgid());
+    setgid(getgid());
+
+    if (flag_debug != 1) {
+	if (daemon(0,0) != 0)
+	    fatal("daemonize failed");
+	
+	flag_daemon = 1;
+
+	/* record pid */
+	f = fopen(MON_PID_FILE, "w");
+	if (f) {
+	    fprintf(f, "%u\n", (u_int) getpid());
+	    fclose(f);
+	}
+    } 
+
+    info("mon version %s", MON_VERSION);
+
+    if (!read_config_file(&mul, MON_CONFIG_FILE))
+	fatal("configuration contained errors; quitting");
+
+    if (flag_debug == 1)
+	info("program id=%d", (u_int) getpid());
+
+    /* Setup signal handlers */
+    signal(SIGALRM, alarmhandler);
+    signal(SIGHUP, huphandler);
+    signal(SIGINT, exithandler); 
+    signal(SIGQUIT, exithandler); 
+    signal(SIGTERM, exithandler); 
+
+    /* Prepare crc32 */
+    init_crc32();
+
+    /* init modules */
+    SLIST_FOREACH(mux, &mul, muxes) {
+	connect2mux(mux);
+	SLIST_FOREACH(stream, &mux->sl, streams) {
+	    (streamfunc[stream->type].init)(stream->args);
+	}
+    }
+
+    /* Setup alarm */
+    timerclear(&alarminterval.it_interval);
+    timerclear(&alarminterval.it_value);
+    alarminterval.it_interval.tv_sec=
+	alarminterval.it_value.tv_sec=MON_INTERVAL;
+
+    if (setitimer(ITIMER_REAL, &alarminterval, NULL) != 0) {
+	fatal("alarm setup failed -- %s", strerror(errno));
+    }
+
+    for (;;) {  /* FOREVER */
+	sleep(MON_INTERVAL*2);    /* alarm will always interrupt sleep */
+
+	if (flag_hup == 1) {
+	    flag_hup = 0;
+
+	    SLIST_INIT(&newmul);
+
+	    if (!read_config_file(&newmul, MON_CONFIG_FILE)) {
+		info("new configuration contains errors; keeping old configuration");
+		free_muxlist(&newmul);
+	    } else {
+		free_muxlist(&mul);
+		mul = newmul;
+		info("read configuration file succesfully");
+
+		/* init modules */
+		SLIST_FOREACH(mux, &mul, muxes) {
+		    connect2mux(mux);
+		    SLIST_FOREACH(stream, &mux->sl, streams) {
+			(streamfunc[stream->type].init)(stream->args);
+		    }
+		}
+	    }
+	}
+
+	SLIST_FOREACH(mux, &mul, muxes) {
+	    prepare_packet(mux);
+	    
+	    SLIST_FOREACH(stream, &mux->sl, streams)
+		stream_in_packet(stream, mux);
+
+	    finish_packet(mux);
+
+	    send_packet(mux);
+	}
+    }
+    /* NOTREACHED */
+}

+ 17 - 0
mon/mon/mon.conf

@@ -0,0 +1,17 @@
+#
+# $Id: mon.conf,v 1.7 2002/07/25 14:23:04 dijkstra Exp $
+#
+# Initial mon.conf demonstrates how I configure mon.
+
+# monitor { <cpu(arg)|mem|if(arg)|io(arg)> } stream [to] <host>:<port>
+#
+# cpu(x) - gather stats for cpu nr. x
+# mem    - gather memory stats
+# if(x)  - gather io stats for interface x
+# io(x)  - gather io stats for disk x.
+
+monitor { cpu(0),  mem,     if(xl0), if(de0),
+	  if(lo0), if(wi0), io(wd0), io(wd1), 
+          io(wd2), io(wd3), io(cd0), io(ccd0)}  
+		stream to 10.0.0.1 2100
+

+ 89 - 0
mon/mon/mon.h

@@ -0,0 +1,89 @@
+/* $Id: mon.h,v 1.14 2002/04/04 20:48:56 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _MON_MON_H
+#define _MON_MON_H
+
+#include <sys/queue.h>
+
+#include <kvm.h>
+#include <nlist.h>
+
+#include "lex.h"
+#include "data.h"
+
+#define MON_CONFIG_FILE "/etc/mon.conf"
+#define MON_PID_FILE    "/var/run/mon.pid"
+#define MON_VERSION     "2.0"
+#define MON_INTERVAL 5                           /* measurement interval */
+#define MON_WARN_SENDERR 50                      /* warn every x errors */
+
+/* kvm interface */
+#ifdef MON_KVM
+extern kvm_t *kvmd;
+extern struct nlist mon_nl[];
+#define MON_IFNET 0
+#define MON_DL    1
+#endif /* MON_KVM */
+
+struct funcmap {
+    int type;
+    void (*init)(char *);
+    int (*get)(char*, int, char *);
+};
+
+extern struct funcmap streamfunc[];
+
+/* prototypes */
+__BEGIN_DECLS
+#ifdef MON_KVM
+extern int kread(u_long, char *, int);
+#endif 
+
+/* cpu.c */
+extern void init_cpu(char *);
+extern int  get_cpu(char *, int, char *);
+
+/* mem.c */
+extern void init_mem(char *);
+extern int  get_mem(char *, int, char *);
+
+/* if.c */
+extern void init_if(char *);
+extern int  get_if(char *, int, char *);
+
+/* io.c */
+extern void init_io(char *);
+extern int  get_io(char *, int, char *);
+__END_DECLS
+
+#endif /*_MON_MON_H*/

+ 121 - 0
mon/mon/monnet.c

@@ -0,0 +1,121 @@
+/* $Id: monnet.c,v 1.8 2002/08/11 20:00:57 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+
+#include "error.h"
+#include "data.h"
+#include "mon.h"
+
+/* Fill a mux structure with inet details */
+void 
+connect2mux(struct mux *mux)
+{
+    struct sockaddr_in sockaddr;
+
+    if ((mux->monsocket = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+	fatal("could not obtain socket: %.200s", strerror(errno));
+
+    sockaddr.sin_family = AF_INET;
+    sockaddr.sin_port = 0;
+    sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+    bzero(&sockaddr.sin_zero, 8);
+
+    if (bind(mux->monsocket, (struct sockaddr *) &sockaddr, 
+	     sizeof(struct sockaddr)) == -1)
+	fatal("could not bind socket: %.200s", strerror(errno));
+
+    mux->sockaddr.sin_family = AF_INET;
+    mux->sockaddr.sin_port = htons(mux->port);
+    mux->sockaddr.sin_addr.s_addr = htonl(mux->ip);
+    bzero(&mux->sockaddr.sin_zero, 8);
+
+    info("sending packets to udp:%s:%d", 
+	 mux->name, mux->port);
+}
+/* Send data stored in the mux structure to a mux */
+void 
+send_packet(struct mux *mux)
+{   
+    if (sendto(mux->monsocket, (void *)&mux->packet, 
+	       mux->offset + sizeof(mux->packet.header), 0,
+	       (struct sockaddr *)&mux->sockaddr, sizeof(mux->sockaddr)) 
+	!= (mux->offset + sizeof(mux->packet.header))) {
+	mux->senderr++;
+    }
+
+    if (mux->senderr >= MON_WARN_SENDERR)
+	warning("%d updates to mux(%u.%u.%u.%u) lost due to send errors",
+		mux->senderr, 
+		(mux->ip >> 24), (mux->ip >> 16) & 0xff, 
+		(mux->ip >> 8) & 0xff, mux->ip & 0xff), mux->senderr = 0;
+}
+/* Prepare a packet for data */
+void 
+prepare_packet(struct mux *mux) 
+{
+    time_t t = time(NULL);
+
+    bzero(&mux->packet, sizeof(mux->packet));
+    mux->offset = 0;
+
+    mux->packet.header.mon_version = MON_PACKET_VER;
+    mux->packet.header.timestamp = htonq((u_int64_t) t);
+}
+/* Put a stream into the packet for a mux */
+void 
+stream_in_packet(struct stream *stream, struct mux *mux) 
+{
+    mux->offset += 
+	(streamfunc[stream->type].get)      /* call getter of stream */
+	(&mux->packet.data[mux->offset],    /* packet buffer */
+	 _POSIX2_LINE_MAX - mux->offset,    /* maxlen */
+	 stream->args);
+}
+/* Ready a packet for transmission, set length and crc */
+void finish_packet(struct mux *mux) 
+{
+    u_int32_t crc;
+
+    mux->packet.header.length = htons(mux->offset);
+    
+    mux->packet.header.crc = 0;
+    crc = crc32(&mux->packet, mux->offset + sizeof(mux->packet.header));
+    mux->packet.header.crc = htonl(crc);
+}

+ 46 - 0
mon/mon/monnet.h

@@ -0,0 +1,46 @@
+/* $Id: monnet.h,v 1.3 2002/03/31 14:27:48 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _MON_MONNET_H
+#define _MON_MONNET_H
+
+#include "data.h"
+
+/* prototypes */
+__BEGIN_DECLS
+void connect2mux(struct mux *);
+void send_packet(struct mux *);
+void prepare_packet(struct mux *);
+void stream_in_packet(struct stream *, struct mux *);
+void finish_packet(struct mux *);
+__END_DECLS
+#endif /* _MON_MONNET_H */

+ 219 - 0
mon/mon/readconf.c

@@ -0,0 +1,219 @@
+/* $Id: readconf.c,v 1.8 2002/08/11 19:53:15 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <sys/queue.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "data.h"
+#include "error.h"
+#include "lex.h"
+#include "mon.h"
+#include "net.h"
+#include "readconf.h"
+#include "xmalloc.h"
+
+__BEGIN_DECLS
+int read_host_port(struct muxlist *, struct mux *, struct lex *);
+int read_mon_args(struct mux *, struct lex *);
+int read_monitor(struct muxlist *, struct lex *);
+__END_DECLS
+
+/* <hostname> (port|:|,| ) <number> */
+int
+read_host_port(struct muxlist *mul, struct mux *mux, struct lex *l)
+{
+    lex_nexttoken(l);
+    if (!lookup(l->token)) {
+	warning("%s:%d: could not resolve '%s'",
+		l->filename, l->cline, l->token);
+	return 0;
+    }
+
+    if (rename_mux(mul, mux, lookup_address) == NULL) {
+	warning("%s:%d: monitored data for host '%s' has already been specified",
+		l->filename, l->cline, lookup_address);
+	return 0;
+    }
+	
+    mux->ip = lookup_ip;
+
+    /* check for port statement */
+    if (!lex_nexttoken(l))
+	return 1;
+
+    if (l->op == LXT_PORT || l->op == LXT_COLON || l->op == LXT_COMMA)
+	lex_nexttoken(l);
+    else {
+	if (l->type != LXY_NUMBER) {
+	    lex_ungettoken(l);
+	    mux->port = MONMUX_PORT;
+	    return 1;
+	}
+    }
+
+    if (l->type != LXY_NUMBER) {
+	parse_error(l, "<number>");
+	return 0;
+    }
+
+    mux->port = l->value;
+    return 1;
+}
+/* parse "<cpu(arg)|mem|if(arg)|io(arg)>", end condition == "}" */
+int 
+read_mon_args(struct mux *mux, struct lex *l) 
+{
+    char sn[_POSIX2_LINE_MAX];
+    char sa[_POSIX2_LINE_MAX];
+    int  st;
+
+    EXPECT(l, LXT_BEGIN)
+    while (lex_nexttoken(l) && l->op != LXT_END) {
+	switch (l->op) {
+	case LXT_CPU:
+	case LXT_IF:
+	case LXT_IO:
+	case LXT_MEM:
+	    st = token2type(l->op);
+	    strncpy(&sn[0], l->token, _POSIX2_LINE_MAX);
+
+	    /* parse arg */
+	    lex_nexttoken(l);
+	    if (l->op == LXT_OPEN) {
+		lex_nexttoken(l);
+		if (l->op == LXT_CLOSE){
+		    parse_error(l, "<stream argument>");
+		    return 0;
+		}
+		strncpy(&sa[0], l->token, _POSIX2_LINE_MAX);
+		lex_nexttoken(l);
+		if (l->op != LXT_CLOSE){
+		    parse_error(l, ")");
+		    return 0;
+		}
+	    } else {
+		lex_ungettoken(l);
+		sa[0]='\0';
+	    }
+
+	    if ((add_mux_stream(mux, st, sa)) == NULL) {
+		warning("%s:%d: stream %s(%s) redefined",
+			l->filename, l->cline, sn, sa);
+		return 0;
+	    }
+	    
+	    break; /* LXT_CPU/IF/IO/MEM */
+	case LXT_COMMA:
+	    break;
+	default:
+	    parse_error(l, "cpu|mem|if|io|}");
+	    return 0;
+	    break;
+	}
+    }
+    
+    return 1;
+}
+
+/* parse monitor <args> stream [to] <host>:<port> */
+int
+read_monitor(struct muxlist *mul, struct lex *l)
+{
+    struct mux *mux;
+
+    mux = add_mux(mul, "<unnamed host>");
+
+    /* parse cpu|mem|if|io */
+    if (!read_mon_args(mux, l))
+	return 0;
+
+    /* parse stream to */
+    EXPECT(l, LXT_STREAM);
+    lex_nexttoken(l);
+    if (l->op != LXT_TO) 
+	lex_ungettoken(l);
+
+    /* parse host */
+    if (!read_host_port(mul, mux, l))
+	return 0;
+    
+    return 1;
+}
+
+/* Read mon.conf */
+int
+read_config_file(struct muxlist *muxlist, 
+		 const char *filename)
+{
+    struct lex *l;
+    struct mux *mux;
+
+    SLIST_INIT(muxlist);
+
+    if ((l = open_lex(filename)) == NULL)
+	return 0;
+    
+    info("reading configfile '%s'", filename);
+
+    while (lex_nexttoken(l)) {
+    /* expecting keyword now */
+	switch (l->op) {
+	case LXT_MONITOR:
+	    if (!read_monitor(muxlist, l)) 
+		return 0;
+	    break;
+	default:
+	    parse_error(l, "monitor" );
+	    return 0;
+	    break;
+	}
+    }
+
+    /* sanity checks */
+    SLIST_FOREACH(mux, muxlist, muxes) {
+	if (SLIST_EMPTY(&mux->sl)) {
+	    warning("%s: no monitors selected for mux '%s'",
+		    l->filename, mux->name);
+	    return 0;
+	}
+    }
+
+    close_lex(l);
+    return 1;
+}

+ 53 - 0
mon/mon/readconf.h

@@ -0,0 +1,53 @@
+/* $Id: readconf.h,v 1.3 2002/04/01 20:15:59 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _MON_READCONF_H
+#define _MON_READCONF_H
+
+#include <sys/cdefs.h>
+
+#include "data.h"
+#include "lex.h"
+
+/* Monitor subsystem structure */
+struct monm {
+    int type;
+    void  (*init)(char *);
+    char* (*get)(char *);
+};
+
+__BEGIN_DECLS
+int read_config_file(struct muxlist *, const char *);
+__END_DECLS
+#endif /*_MON_READCONF_H*/
+
+

+ 74 - 0
mon/mon2web/class_cpu.inc

@@ -0,0 +1,74 @@
+<?php
+require_once("class_graph.inc");
+
+class CPU_Graph extends Graph {
+    function CPU_Graph() {
+	// get all the sensible defaults from our parent class
+	$this->init();
+    }
+    function execute($url) {
+	$this->url2options($url);
+	// and add a few private things
+	$this->graphsettings["rawcmdline"] .=" -v '% ".$this->get("name")."' -u 100 --rigid ";
+
+	$this->shortcut = array_merge($this->shortcut, array(
+	    "cpu" => array(
+		"brief" => array(
+		    "dsdefinition" => array(
+			"DEF:A=".$this->get("rrdfile").":user:AVERAGE",
+			"DEF:B=".$this->get("rrdfile").":nice:AVERAGE",
+			"DEF:C=".$this->get("rrdfile").":system:AVERAGE",
+			"DEF:D=".$this->get("rrdfile").":interrupt:AVERAGE",
+			"DEF:E=".$this->get("rrdfile").":idle:AVERAGE"),
+		    "graphdefinition" => array(
+			"AREA:A#00FF00:'user'",
+			"GPRINT:A:AVERAGE:'%.0lf\g'",
+			"STACK:B#00FFFF:'nice'",
+			"GPRINT:B:AVERAGE:'%.0lf\g'",
+			"STACK:C#DDA0DD:'system'",
+			"GPRINT:C:AVERAGE:'%.0lf\g'", 
+			"STACK:D#9932CC:'interrupt'", 
+			"GPRINT:D:AVERAGE:'%.0lf\g'",
+			"STACK:E#F5FFFA:'idle'", 
+			"GPRINT:E:AVERAGE:'%.0lf\g'")),
+		"detailed" => array(
+		    "dsdefinition" => array(
+			"DEF:A=".$this->get("rrdfile").":user:AVERAGE",
+			"DEF:B=".$this->get("rrdfile").":nice:AVERAGE",
+			"DEF:C=".$this->get("rrdfile").":system:AVERAGE",
+			"DEF:D=".$this->get("rrdfile").":interrupt:AVERAGE",
+			"DEF:E=".$this->get("rrdfile").":idle:AVERAGE"),
+		    "graphdefinition" => array(
+			'COMMENT:"                 min       avg       max       last\n"',
+			"AREA:A#00FF00:'user     '",
+			'GPRINT:A:MIN:" %6.2lf %%"',
+			'GPRINT:A:AVERAGE:"%6.2lf %%"',
+			'GPRINT:A:MAX:"%6.2lf %%"',
+			'GPRINT:A:LAST:"%6.2lf %%\n"',
+			"STACK:B#00FFFF:'nice     '",
+			'GPRINT:B:MIN:" %6.2lf %%"',
+			'GPRINT:B:AVERAGE:"%6.2lf %%"',
+			'GPRINT:B:MAX:"%6.2lf %%"',
+			'GPRINT:B:LAST:"%6.2lf %%\n"',
+			"STACK:C#DDA0DD:'system   '",
+			'GPRINT:C:MIN:" %6.2lf %%"',
+			'GPRINT:C:AVERAGE:"%6.2lf %%"',
+			'GPRINT:C:MAX:"%6.2lf %%"',
+			'GPRINT:C:LAST:"%6.2lf %%\n"',
+			"STACK:D#9932CC:'interrupt'", 
+			'GPRINT:D:MIN:" %6.2lf %%"',
+			'GPRINT:D:AVERAGE:"%6.2lf %%"',
+			'GPRINT:D:MAX:"%6.2lf %%"',
+			'GPRINT:D:LAST:"%6.2lf %%\n"',
+			"STACK:E#F5FFFA:'idle     '",
+			'GPRINT:E:MIN:" %6.2lf %%"',
+			'GPRINT:E:AVERAGE:"%6.2lf %%"',
+			'GPRINT:E:MAX:"%6.2lf %%"',
+			'GPRINT:E:LAST:"%6.2lf %%\n"')))));
+	$this->graphdefaults = array_merge($this->graphdefaults, 
+					   $this->shortcut["cpu"]["brief"]);
+	$this->url2options($url);
+
+    }
+}
+?>

+ 234 - 0
mon/mon2web/class_graph.inc

@@ -0,0 +1,234 @@
+<?php
+// $Id: class_graph.inc,v 1.5 2002/08/16 12:50:16 dijkstra Exp $
+//
+// Base class for all graph classes. 
+
+// 
+class Graph {
+    var $debug;
+    var $graphdefaults;
+    var $graphsettings;
+    var $options;
+    var $shortcut;    
+
+    function Graph() {
+	$this->init();
+    }
+
+    function enable_debug() {
+	$this->debug = 1;
+    }
+    function debug($string) {
+	if ($this->debug) {
+	    print "<hr><br>$string<br><hr>";
+	}
+    }
+
+    function dump_vars() {
+	print "<br><hr><br><b>graphsettings</b>:<br><pre>";
+	print_r($this->graphsettings);
+	print "</pre><br><hr><br><b>options</b>:<br><pre>";
+	print_r($this->options);
+	print "</pre><br><hr><br>";
+    }
+    // fill meta parameters
+    // this function will most likely not be overloaded by child classes
+    function init() {
+	$this->debug = 0;
+
+	$this->graphdefaults = array( 
+	    "width"     => array("max" => 2048, "min" => 32,           "default" => 300),
+	    "height"    => array("max" => 2048, "min" => 32,           "default" => 225),
+	    "start"     => array("max" => 429467296,    "min" => -4294967296,  "default" => -86400),	
+	    "end"       => array("max" => 429467296,    "min" => -4294967296,  "default" => -1),
+	    "imagetype" => "PNG",
+	    "rrdtool"   => "/usr/local/bin/rrdtool graph -",
+	    "rrdfile"   => "",
+	    "name"      => "unnamed graph");
+	
+	$this->options = array(
+	    "width"     => "-w",
+	    "height"    => "-h",
+	    "imagetype" => "-a",
+	    "start"     => "-s", 
+	    "end"       => "-e" );
+
+	$this->shortcut = array(
+	    // date shortcuts
+	    "timespan"  => array(
+		"today"     => array(
+		    "start" => -86400 ),
+		"lastweek"  => array(
+		    "start" => -604800 ),
+		"lastmonth" => array( 
+		    "start" => -2419200 )),
+	    // size shortcuts
+	    "size"      => array(
+		"tiny"      => array(
+		    "width"  => 100,
+		    "height" => 75 ),
+		"small"     => array(
+		    "width"  => 200,
+		    "height" => 125 ),
+		"normal"    => array(
+		    "width"  => 300,
+		    "height" => 225 ),
+		"big"      => array(
+		    "width" => 600,
+		    "height" => 450 ),
+		"huge"      => array(
+		    "width" => 1024,
+		    "height" => 640 )));
+	$this->graphsettings = array(
+	    "graphdefinition" => array(),
+	    "dsdefinition" => array(),
+	    "rawcmdline" => "");
+    }
+
+    // parseoptions will not be overloaded by child classes
+    function parseoptions (&$params) {
+	$myparams = array();
+	// look for shortcuts
+	if (!empty($params) && is_array($params)) {
+	    reset($params);
+	    while (list($key, $value) = each($params)) {
+		if (isset($this->shortcut[$key][$value])) { 
+		    $ar = $this->shortcut[$key][$value];
+		    while (list($itemkey,$itemvalue) = each($ar)) {
+			if (is_array($this->shortcut[$key][$value][$itemkey])) {
+			    $myparams[$itemkey] = array_merge(array(),&$this->shortcut[$key][$value][$itemkey]);
+			} else if (is_scalar($itemvalue)) {
+			    $myparams[$itemkey] = $itemvalue;
+			}
+		    }
+		} else {
+		    $myparams[$key] = $value;
+		}
+	    }
+	}
+
+	// copy parameters, filling in defaults and constraining to acceptable
+	// values where necessary
+	reset($this->graphdefaults);
+	reset($myparams);
+	while (list($key, $value) = each($this->graphdefaults)) {
+	    if (isset($myparams[$key])) {
+		if (is_array($myparams[$key])) {
+		    $value = $myparams[$key];
+		} else {
+		    $value = escapeshellcmd($myparams[$key]);
+		}
+	    } else {
+		if (isset($this->graphsettings[$key])) {
+		    // is this set already, is current value .. 
+		    if ((is_array($this->graphsettings[$key]) && 
+			 count($this->graphsettings[$key]) != 0) // an non empty array?
+			|| (is_scalar($this->graphsettings[$key]))) // or a scalar
+		    {
+			// yes: keep current set value
+			$value = $this->graphsettings[$key];
+		    }
+		    // no: implicit; set to default
+		} else if (is_array($value) && isset($value["default"])) {
+		    // - check if it is an min/max/default array
+		    $value = $value["default"];
+		}
+	    }
+
+	    // normalize parameters
+	    if (is_array($this->graphdefaults[$key])
+		&& isset($this->graphdefaults[$key]["max"]) 
+		&& $value > $this->graphdefaults[$key]["max"]) {
+		$value = $this->graphdefaults[$key]["max"];
+	    }
+	    if (is_array($this->graphdefaults[$key]) 
+		&& isset($this->graphdefaults[$key]["min"]) 
+		&& $value < $this->graphdefaults[$key]["min"]) {
+		$value = $this->graphdefaults[$key]["min"];
+	    }
+	    
+	    $this->graphsettings[$key] = $value;
+	}
+    }
+    
+    // will not be overloaded by child classes
+    function cmdline() {
+
+	$cmdline = $this->graphsettings["rrdtool"];
+
+	// get options
+	reset($this->options);
+	while (list($key, $value) = each($this->options)) {
+	    if (isset($this->graphsettings[$key])) {
+		$cmdline .= ' '.$value.' '.$this->graphsettings[$key];
+	    }
+	}
+	
+	$cmdline .= $this->graphsettings["rawcmdline"];
+	while (list($key, $value) = each($this->graphsettings["dsdefinition"])) {
+	    $cmdline .= ' '.$value;
+	}
+
+	while (list($key, $value) = each($this->graphsettings["graphdefinition"])) {
+	    $cmdline .= ' '.$value;
+	}
+
+	return $cmdline;
+    }
+
+    function sendimage() {
+	$cmd = $this->cmdline();
+	$this->debug($cmd);
+	header("content-type: image/png");
+	passthru($cmd);
+    }
+
+    function url2options($url) {
+	$options = array();
+	$url = urldecode($url);
+	$args = explode('&', $url);
+	$i = 0;
+	while ($i < count($args)) {
+	    $operand = split('=', $args[$i]);
+	    if (!empty($operand) 
+		&& is_array($operand) 
+		&& count($operand) >= 2) {
+		$key = htmlspecialchars(urldecode($operand[0]));
+		if ($key == "opcode") {
+		    $this->graphsettings = 
+			unserialize(
+			    gzuncompress(
+				base64_decode(
+				    urldecode($operand[1]))));
+		} else {
+		    $value = htmlspecialchars(urldecode($operand[1]));
+		    $options[$key] = $value;
+		}
+	    }
+	    $i++;
+	}
+	$this->parseoptions($options);
+    }
+    function settings2url() {
+	$settings = urlencode(
+	    base64_encode(
+		gzcompress(
+		    serialize($this->graphsettings),9)));
+	return "opcode=".$settings;
+    }
+
+    function add($setting, $valuearray) {
+	$this->graphsettings[$setting] = 
+	    array_merge($this->graphsettings[$setting], $valuearray);
+    }
+
+    function set($setting, $value) {
+	$this->graphsettings[$setting] = $value;
+    }
+
+    function get($setting) {
+	return $this->graphsettings[$setting];
+    }
+}
+
+?>

+ 211 - 0
mon/mon2web/class_if.inc

@@ -0,0 +1,211 @@
+<?php
+require_once("class_graph.inc");
+
+class IF_Graph extends Graph {
+    function IF_Graph() {
+	// get all the sensible defaults from our parent class
+	$this->init();
+    }
+
+    function execute($url) {	
+	$this->url2options($url);
+	// Four major display modes:
+	// if=brief|bits|packets|all
+	$this->shortcut = array_merge($this->shortcut, array(
+	    "if" => array(
+		"brief" => array(
+		    "dsdefinition" => array(
+			"DEF:A=".$this->get("rrdfile").":ibytes:AVERAGE",
+			"DEF:B=".$this->get("rrdfile").":obytes:AVERAGE",
+			"CDEF:inb=A,8,*",
+			"CDEF:outb=B,8,*",
+			"CDEF:ioutb=0,outb,-"),
+		    "graphdefinition" => array(
+			"HRULE:0#000000",
+			"AREA:inb#00FF00:'in'",
+			"GPRINT:A:AVERAGE:'%6.2lf %sbps\g'",
+			"AREA:ioutb#00FFFF:'out'" ,
+			"GPRINT:outb:AVERAGE:'%6.2lf %sbps'")),
+		"bits" => array(
+		    "dsdefinition" => array(
+			'DEF:in='.$this->get("rrdfile").':ibytes:AVERAGE',
+			'DEF:out='.$this->get("rrdfile").':obytes:AVERAGE',
+			'DEF:inp='.$this->get("rrdfile").':ipackets:AVERAGE',
+			'DEF:outp='.$this->get("rrdfile").':opackets:AVERAGE',
+			'DEF:coll='.$this->get("rrdfile").':collisions:AVERAGE',
+			// bits per second
+			'CDEF:inb=in,8,*',
+			'CDEF:outb=out,8,*',
+			// outbound bits per second negated
+			'CDEF:noutb=outb,-1,*',
+			// 2% extra at each bound (+x/-x) to ensure that we see the packetloss lines
+			'CDEF:pmax=inb,100,/,102,*',
+			'CDEF:nmax=noutb,100,/,102,*',
+			// percentage of collisions
+			'CDEF:totp=inp,outp,+',
+			'CDEF:per=coll,totp,/,100,*',
+			'CDEF:p0=per,0,EQ,INF,0,IF',
+			'CDEF:p10=per,10,LE,INF,0,IF,per,0,GT,INF,0,IF,MIN',
+			'CDEF:p20=per,20,LE,INF,0,IF,per,10,GT,INF,0,IF,MIN',
+			'CDEF:p30=per,30,LE,INF,0,IF,per,20,GT,INF,0,IF,MIN',
+			'CDEF:p40=per,40,LE,INF,0,IF,per,30,GT,INF,0,IF,MIN',
+			'CDEF:p50=per,50,LE,INF,0,IF,per,40,GT,INF,0,IF,MIN',
+			'CDEF:p60=per,60,LE,INF,0,IF,per,50,GT,INF,0,IF,MIN',
+			'CDEF:p70=per,70,LE,INF,0,IF,per,60,GT,INF,0,IF,MIN',
+			'CDEF:p80=per,80,LE,INF,0,IF,per,70,GT,INF,0,IF,MIN',
+			'CDEF:p90=per,80,LE,INF,0,IF,per,80,GT,INF,0,IF,MIN',
+			'CDEF:p100=per,100,LE,INF,0,IF,per,90,GT,INF,0,IF,MIN',
+			// negative percentages
+			'CDEF:n0=p0,-1,*',
+			'CDEF:n10=p10,-1,*',
+			'CDEF:n20=p20,-1,*',
+			'CDEF:n30=p30,-1,*',
+			'CDEF:n40=p40,-1,*',
+			'CDEF:n50=p50,-1,*',
+			'CDEF:n60=p60,-1,*',
+			'CDEF:n70=p70,-1,*',
+			'CDEF:n80=p80,-1,*',
+			'CDEF:n90=p90,-1,*',
+			'CDEF:n100=p100,-1,*'),
+		    "graphdefinition" => array(
+			'LINE1:pmax',
+			'LINE1:nmax',
+			'COMMENT:"               min          avg          max          last\n"',
+			'AREA:inb#00FF00:"in     "',
+			'GPRINT:inb:MIN:" %6.2lf %sbps"',
+			'GPRINT:inb:AVERAGE:"%6.2lf %sbps"',
+			'GPRINT:inb:MAX:"%6.2lf %sbps"',
+			'GPRINT:inb:LAST:"%6.2lf %sbps\n"',
+			'STACK:p0#FAFFFA', 
+			'STACK:p10#FFFFE6',
+			'STACK:p20#FFD900',
+			'STACK:p30#FD6724',
+			'STACK:p40#E61800',
+			'STACK:p50#AB2934',
+			'STACK:p60#B2888B',
+			'STACK:p70#CC91BA',
+			'STACK:p80#6A2990',
+			'STACK:p90#0571B0',
+			'STACK:p100#000000',
+			'AREA:noutb#00FFFF:"out     "',
+			'GPRINT:outb:MIN:"%6.2lf %sbps"',
+			'GPRINT:outb:AVERAGE:"%6.2lf %sbps"',
+			'GPRINT:outb:MAX:"%6.2lf %sbps"',
+			'GPRINT:outb:LAST:"%6.2lf %sbps\n"',
+			'COMMENT:packetloss',
+			'GPRINT:per:MIN:"%6.2lf%%    "',
+			'GPRINT:per:AVERAGE:"%6.2lf%%    "',
+			'GPRINT:per:MAX:"%6.2lf%%    "',
+			'GPRINT:per:LAST:"%6.2lf%%\n"',
+			'STACK:n0#FAFFFA:" 0%  "',
+			'STACK:n10#FFFFE6:" <10%"',
+			'STACK:n20#FFD900:" <20%"',
+			'STACK:n30#FD6724:" <30%"',
+			'STACK:n40#E61800:" <40%"',
+			'STACK:n50#AB2934:" <50%\n"',
+			'STACK:n60#B2888B:" <60%"',
+			'STACK:n70#CC91BA:" <70%"',
+			'STACK:n80#6A2990:" <80%"',
+			'STACK:n90#0571B0:" <90%"',
+			'STACK:n100#000000:" <100%"')),
+		"packets" => array(
+		    "dsdefinition" => array(
+			"DEF:A=".$this->get("rrdfile").":ipackets:AVERAGE",
+			"DEF:B=".$this->get("rrdfile").":opackets:AVERAGE",
+			"DEF:C=".$this->get("rrdfile").":imcasts:AVERAGE",
+			"DEF:D=".$this->get("rrdfile").":omcasts:AVERAGE",
+			"DEF:E=".$this->get("rrdfile").":ierrors:AVERAGE",
+			"DEF:F=".$this->get("rrdfile").":oerrors:AVERAGE",
+			"DEF:G=".$this->get("rrdfile").":collisions:AVERAGE",
+			"DEF:H=".$this->get("rrdfile").":drops:AVERAGE"),
+		    "graphdefinition" => array(
+			"AREA:A#00FF00:'ipackets'",
+			"GPRINT:A:AVERAGE:'%.0lf\g'",
+			"LINE1:B#00FFFF:'opackets'",
+			"GPRINT:B:AVERAGE:'%.0lf\g'",
+			"LINE1:C#0000FF:'imcasts'",
+			"GPRINT:C:AVERAGE:'%.0lf\g'",
+			"LINE1:D#FF00FF:'omcasts'",
+			"GPRINT:D:AVERAGE:'%.0lf\g'",
+			"LINE1:E#FF0000:'ierrors'",
+			"GPRINT:E:AVERAGE:'%.0lf\g'",
+			"LINE1:F#8B0000:'oerrors'",
+			"GPRINT:F:AVERAGE:'%.0lf\g'",
+			"LINE1:G#888888:'collisions'",
+			"GPRINT:G:AVERAGE:'%.0lf\g'",
+			"LINE1:H#000000:'drops'",
+			"GPRINT:H:AVERAGE:'%.0lf\g'")),
+		"all" => array(
+		    "dsdefinition" => array(
+			"DEF:in=".$this->get("rrdfile").":ibytes:AVERAGE",
+			"DEF:out=".$this->get("rrdfile").":obytes:AVERAGE",
+			"DEF:inp=".$this->get("rrdfile").":ipackets:AVERAGE",
+			"DEF:outp=".$this->get("rrdfile").":opackets:AVERAGE",
+			"DEF:inm=".$this->get("rrdfile").":imcasts:AVERAGE",
+			"DEF:outm=".$this->get("rrdfile").":omcasts:AVERAGE",
+			"DEF:ierr=".$this->get("rrdfile").":ierrors:AVERAGE",
+			"DEF:oerr=".$this->get("rrdfile").":oerrors:AVERAGE",
+			"DEF:coll=".$this->get("rrdfile").":collisions:AVERAGE",
+			"DEF:drop=".$this->get("rrdfile").":drops:AVERAGE"),
+		    "graphdefinition" => array(
+			"HRULE:0#000000",
+			'COMMENT:"           min          avg          max          last\n"',
+			"AREA:in#00FF00:'in '",
+			"GPRINT:in:MIN:' %6.2lf %sbps'",
+			"GPRINT:in:AVERAGE:'%6.2lf %sbps'",
+			"GPRINT:in:MAX:'%6.2lf %sbps'",
+			'GPRINT:in:LAST:"%6.2lf %sbps\n"',
+			"LINE1:out",
+			"LINE1:out#00FFFF:'out '" ,
+			"GPRINT:out:MIN:'%6.2lf %sbps'",
+			"GPRINT:out:AVERAGE:'%6.2lf %sbps'",
+			"GPRINT:out:MAX:'%6.2lf %sbps'",
+			'GPRINT:out:LAST:"%6.2lf %sbps\n"',
+			"LINE1:inp#222222:'inp '" ,
+			"GPRINT:inp:MIN:'%6.2lf %sbps'",
+			"GPRINT:inp:AVERAGE:'%6.2lf %sbps'",
+			"GPRINT:inp:MAX:'%6.2lf %sbps'",
+			'GPRINT:inp:LAST:"%6.2lf %sbps\n"',
+			"LINE1:outp#444444:'outp'" ,
+			"GPRINT:outp:MIN:'%6.2lf %sbps'",
+			"GPRINT:outp:AVERAGE:'%6.2lf %sbps'",
+			"GPRINT:outp:MAX:'%6.2lf %sbps'",
+			'GPRINT:outp:LAST:"%6.2lf %sbps\n"',
+			"LINE1:inm#666666:'inm '" ,
+			"GPRINT:inm:MIN:'%6.2lf %sbps'",
+			"GPRINT:inm:AVERAGE:'%6.2lf %sbps'",
+			"GPRINT:inm:MAX:'%6.2lf %sbps'",
+			'GPRINT:inm:LAST:"%6.2lf %sbps\n"',
+			"LINE1:outm#888888:'outm'" ,
+			"GPRINT:outm:MIN:'%6.2lf %sbps'",
+			"GPRINT:outm:AVERAGE:'%6.2lf %sbps'",
+			"GPRINT:outm:MAX:'%6.2lf %sbps'",
+			'GPRINT:outm:LAST:"%6.2lf %sbps\n"',
+			"LINE1:ierr#aaaaaa:'ierr'" ,
+			"GPRINT:ierr:MIN:'%6.2lf %sbps'",
+			"GPRINT:ierr:AVERAGE:'%6.2lf %sbps'",
+			"GPRINT:ierr:MAX:'%6.2lf %sbps'",
+			'GPRINT:ierr:LAST:"%6.2lf %sbps\n"',
+			"LINE1:oerr#cccccc:'oerr'" ,
+			"GPRINT:oerr:MIN:'%6.2lf %sbps'",
+			"GPRINT:oerr:AVERAGE:'%6.2lf %sbps'",
+			"GPRINT:oerr:MAX:'%6.2lf %sbps'",
+			'GPRINT:oerr:LAST:"%6.2lf %sbps\n"',
+			"LINE1:coll#dddddd:'coll'" ,
+			"GPRINT:coll:MIN:'%6.2lf %sbps'",
+			"GPRINT:coll:AVERAGE:'%6.2lf %sbps'",
+			"GPRINT:coll:MAX:'%6.2lf %sbps'",
+			'GPRINT:coll:LAST:"%6.2lf %sbps\n"',
+			"LINE1:drop#ffffff:'drop'" ,
+			"GPRINT:drop:MIN:'%6.2lf %sbps'",
+			"GPRINT:drop:AVERAGE:'%6.2lf %sbps'",
+			"GPRINT:drop:MAX:'%6.2lf %sbps'",
+			'GPRINT:drop:LAST:"%6.2lf %sbps\n"')))));
+
+	$this->graphdefaults = array_merge($this->graphdefaults,
+					   $this->shortcut["if"]["brief"]);
+	$this->graphsettings["rawcmdline"] .=" -v '".$this->get("name")."'";
+	$this->url2options($url);
+    }
+}
+?>

+ 55 - 0
mon/mon2web/class_io.inc

@@ -0,0 +1,55 @@
+<?php
+require_once("class_graph.inc");
+
+class IO_Graph extends Graph {
+    function IO_Graph() {
+	// get all the sensible defaults from our parent class
+	$this->init(); 
+    }
+    function execute($url) {
+	$this->url2options($url);
+
+	// and add a few private things
+	$this->graphsettings["rawcmdline"] .=" -v '".$this->get("name")."'";
+	$this->shortcut = array_merge($this->shortcut, array(
+	    "io" => array(
+		"brief" => array(
+		    "dsdefinition" => array(
+			"DEF:A=".$this->get("rrdfile").":bytes:AVERAGE "),
+		    "graphdefinition" => array(
+			'COMMENT:"            min          avg          max          last\n"',
+			"AREA:A#00FF00:'bytes'",
+			"GPRINT:A:MIN:'%6.2lf %sBps'",
+			"GPRINT:A:AVERAGE:'%6.2lf %sBps'",
+			"GPRINT:A:MAX:'%6.2lf %sBps'",
+			"GPRINT:A:LAST:'%6.2lf %sBps'")),
+		"all" => array(
+		    "dsdefinition" => array(
+			"DEF:A=".$this->get("rrdfile").":transfers:AVERAGE ",
+			"DEF:B=".$this->get("rrdfile").":seeks:AVERAGE ",
+			"DEF:C=".$this->get("rrdfile").":bytes:AVERAGE "),
+		    "graphdefinition" => array(
+			'COMMENT:"               min          avg          max          last\n"',
+			"AREA:C#00FF00:'bytes    '",
+			"GPRINT:C:MIN:'%4.2lf %sBps'",
+			"GPRINT:C:AVERAGE:'%4.2lf %sBps'",
+			"GPRINT:C:MAX:'%4.2lf %sBps'",
+			'GPRINT:C:LAST:"%4.2lf %sBps\n"',
+			"LINE1:B#0000FF:'seeks    '",
+			"GPRINT:B:MIN:'%4.2lf      '",
+			"GPRINT:B:AVERAGE:'%4.2lf      '",
+			"GPRINT:B:MAX:'%4.2lf      '",
+			'GPRINT:B:LAST:"%4.2lf\n"',
+			"LINE1:A#FF0000:'transfers'",
+			"GPRINT:A:MIN:'%4.2lf      '",
+			"GPRINT:A:AVERAGE:'%4.2lf      '",
+			"GPRINT:A:MAX:'%4.2lf      '",
+			"GPRINT:A:LAST:'%4.2lf'")))));
+
+	$this->graphdefaults = array_merge($this->graphdefaults,
+					   $this->shortcut["io"]["brief"]);
+
+	$this->url2options($url);
+    }
+}
+?>

+ 75 - 0
mon/mon2web/class_mem.inc

@@ -0,0 +1,75 @@
+<?php
+require_once("class_graph.inc");
+
+class MEM_Graph extends Graph {
+    function MEM_Graph() {
+	// get all the sensible defaults from our parent class
+	$this->init();
+    }
+
+    function execute($url) {
+	$this->url2options($url);
+	// and add a few private things
+	$this->graphsettings["rawcmdline"] .=" -v '% ".$this->get("name")."' -b 1024 ";
+	
+	$this->shortcut = array_merge($this->shortcut, array(
+	    "mem" => array(
+		"brief" => array(
+		    "dsdefinition" => array(
+			"DEF:A=".$this->get("rrdfile").":real_active:AVERAGE",
+			"DEF:B=".$this->get("rrdfile").":real_total:AVERAGE",
+			"DEF:C=".$this->get("rrdfile").":free:AVERAGE",
+			"DEF:D=".$this->get("rrdfile").":swap_used:AVERAGE",
+			"DEF:E=".$this->get("rrdfile").":swap_total:AVERAGE"),
+		    "graphdefinition" => array(
+			"AREA:B#008B8B:real",
+			"GPRINT:B:AVERAGE:'%.0lf%s\g'",
+			"STACK:C#3CB371:free",
+			"GPRINT:C:AVERAGE:'%.0lf%s\g'",
+			"LINE1:A#00FFFF:active",
+			"GPRINT:A:AVERAGE:'%.0lf%s\g'",
+			"LINE1:D#888C00:swap_used",
+			"GPRINT:D:AVERAGE:'%.0lf%s\g'",
+			"LINE2:E#FF8C00:swap_total",
+			"GPRINT:E:AVERAGE:'%.0lf%s\g'")),
+		"detailed" => array(
+		    "dsdefinition" => array(
+			"DEF:A=".$this->get("rrdfile").":real_active:AVERAGE",
+			"DEF:B=".$this->get("rrdfile").":real_total:AVERAGE",
+			"DEF:C=".$this->get("rrdfile").":free:AVERAGE",
+			"DEF:D=".$this->get("rrdfile").":swap_used:AVERAGE",
+			"DEF:E=".$this->get("rrdfile").":swap_total:AVERAGE"),
+		    "graphdefinition" => array(
+			'COMMENT:"                  min        avg        max        last\n"',
+			'AREA:B#008B8B:"real      "',
+			'GPRINT:B:MIN:" %6.2lf %sb"',
+			'GPRINT:B:AVERAGE:"%6.2lf %sb"',
+			'GPRINT:B:MAX:"%6.2lf %sb"',
+			'GPRINT:B:LAST:"%6.2lf %sb\n"',
+			'STACK:C#3CB371:"free      "',
+			'GPRINT:C:MIN:" %6.2lf %sb"',
+			'GPRINT:C:AVERAGE:"%6.2lf %sb"',
+			'GPRINT:C:MAX:"%6.2lf %sb"',
+			'GPRINT:C:LAST:"%6.2lf %sb\n"',
+			'LINE1:A#00FFFF:"active    "',
+			'GPRINT:A:MIN:" %6.2lf %sb"',
+			'GPRINT:A:AVERAGE:"%6.2lf %sb"',
+			'GPRINT:A:MAX:"%6.2lf %sb"',
+			'GPRINT:A:LAST:"%6.2lf %sb\n"',
+			'LINE1:D#888C00:"swap used "',
+			'GPRINT:D:MIN:" %6.2lf %sb"',
+			'GPRINT:D:AVERAGE:"%6.2lf %sb"',
+			'GPRINT:D:MAX:"%6.2lf %sb"',
+			'GPRINT:D:LAST:"%6.2lf %sb\n"',
+			'LINE2:E#FF8C00:"swap total"',
+			'GPRINT:E:MIN:" %6.2lf %sb"',
+			'GPRINT:E:AVERAGE:"%6.2lf %sb"',
+			'GPRINT:E:MAX:"%6.2lf %sb"',
+			'GPRINT:E:LAST:"%6.2lf %sb\n"')))));
+
+	$this->graphdefaults = array_merge($this->graphdefaults, 
+					   $this->shortcut["mem"]["brief"]);
+	$this->url2options($url);
+    }
+}
+?>

+ 7 - 0
mon/mon2web/datasources.inc

@@ -0,0 +1,7 @@
+<?php
+// $Id: datasources.inc,v 1.8 2002/08/16 12:51:36 dijkstra Exp $
+//
+// Contains definitions where the data files are to be found
+$mon2web["tree"]='/export/store_3/mon';
+
+?>

+ 7 - 0
mon/mon2web/graph_cpu.php

@@ -0,0 +1,7 @@
+<?php
+require_once("datasources.inc");
+require_once("class_cpu.inc");
+$g = new CPU_Graph();
+$g->execute($GLOBALS["QUERY_STRING"]);;
+$g->sendimage();
+?>

+ 7 - 0
mon/mon2web/graph_if.php

@@ -0,0 +1,7 @@
+<?php
+require_once("datasources.inc");
+require_once("class_if.inc");
+$g = new IF_Graph();
+$g->execute($GLOBALS["QUERY_STRING"]);
+$g->sendimage();
+?>

+ 7 - 0
mon/mon2web/graph_io.php

@@ -0,0 +1,7 @@
+<?php
+require_once("datasources.inc");
+require_once("class_io.inc");
+$g = new IO_Graph();
+$g->execute($GLOBALS["QUERY_STRING"]);
+$g->sendimage();
+?>

+ 7 - 0
mon/mon2web/graph_mem.php

@@ -0,0 +1,7 @@
+<?php
+require_once("datasources.inc");
+require_once("class_mem.inc");
+$g = new MEM_Graph();
+$g->execute($GLOBALS["QUERY_STRING"]);
+$g->sendimage();
+?>

+ 219 - 0
mon/mon2web/index.php

@@ -0,0 +1,219 @@
+<?php
+require_once('datasources.inc');
+require_once('class_cpu.inc');
+require_once('class_if.inc');
+require_once('class_io.inc');
+require_once('class_mem.inc');
+
+if (!empty($GLOBALS["QUERY_STRING"])) {
+    $args = $GLOBALS["QUERY_STRING"];
+    // rework args to contain ony last set 'command' 
+    $options = array();
+    $allargs = explode('&', $args);
+    $i = 0;
+    while ($i < count($allargs)) {
+	$operand = split('=', $allargs[$i]);
+	if (count($operand) == 2) {
+	    $options[$operand[0]]=$operand[1];
+	}
+	$i++;
+    }
+    // decode done, now recode
+    $i = 0;
+    $args = "";
+    reset($options);
+    while (list($key,$val) = each($options)) {
+	if ($args) {
+	    $args .= '&';
+	}
+	$args .= $key.'='.$val;
+    }
+} else {
+    $args = "";
+}
+function make_graph_url($type, $new_args) {
+    global $args;
+    $new_args = urlencode($new_args);
+    $url = "graph_".$type.".php?";
+    $allargs="";
+    if ($args) {
+	$allargs.=$args.'&';
+    }
+    $allargs .= $new_args;
+
+    switch ($type) {
+    case 'cpu':
+	$g = new CPU_Graph();
+	break;
+    case 'if':
+	$g = new IF_Graph();
+	break;
+    case 'io':
+	$g = new IO_Graph();
+	break;
+    case 'mem':
+	$g = new MEM_Graph();
+	break;
+    }
+    $g->execute($allargs);
+    return ($url . $g->settings2url());
+}
+function print_index_url($new_args) {
+    global $args;
+    $url = 'index.php?';
+    if ($args) {
+	$url.=$args.'&';
+    }
+    $url .= sprintf("%s", $new_args);
+
+    print $url;
+}
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <title>mon statistics</title>
+ <link rel="stylesheet" href="mon.css" type="text/css" />
+</head>
+<body>
+ <table border="0" cellspacing="0" cellpadding="0" align="center">
+  <tr>
+   <td valign="top">
+    <table border="0" cellpadding="0" cellspacing="0">
+     <tr>
+      <td width="5%"/></td>
+      <td colspan="5" bgcolor="#555599"><img src="spacer.png" width="1" height="1" /></td>
+      <td width="5%"/></td>
+     </tr>
+     <tr>
+      <!-- sidebar -->
+      <td width="5%"/></td>
+      <td width="1" bgcolor="#555599"><img src="spacer.png" width="1" height="1" /></td>
+      <td bgcolor="#FFFFFF" valign="top" width="20%">
+       <div class="left">
+        <img src="mon.png" width="149" border="0" alt="[mon]" />
+       </div> 
+
+       <div class="left">
+        <div class="section-head">Graph Options</div>
+        <div class="left-links">
+	 Timespan: 
+	 <a href="<? print_index_url("start=-14515200") ?>">last 6 months</a>, 
+	 <a href="<? print_index_url("start=-2419200") ?>">last month</a>,
+	 <a href="<? print_index_url("start=-691200") ?>">last 8 days</a>,
+	 <a href="<? print_index_url("start=-86400") ?>">last 24 hours</a>,
+	 <a href="<? print_index_url("start=-3600") ?>">last hour</a><br />
+	 Size: 
+	 <a href="<? print_index_url("size=tiny") ?>">tiny</a>, 
+	 <a href="<? print_index_url("size=small") ?>">small</a>, 
+	 <a href="<? print_index_url("size=normal") ?>">normal</a>, 
+	 <a href="<? print_index_url("size=big") ?>">big</a>, 
+	 <a href="<? print_index_url("size=huge") ?>">huge</a><br />
+	</div>
+	<div class="section-head">Type options</div>
+	<div class="left-links">
+	 Cpu:
+	 <a href="<? print_index_url("cpu=brief")?> ">brief</a>,
+	 <a href="<? print_index_url("cpu=detailed")?> ">detailed</a><br />
+	 If:
+	 <a href="<? print_index_url("if=brief")?> ">brief</a>,
+	 <a href="<? print_index_url("if=bits")?> ">bits</a>,
+	 <a href="<? print_index_url("if=packets")?> ">packets</a>,
+	 <a href="<? print_index_url("if=all")?> ">all</a><br />
+	 Io:
+	 <a href="<? print_index_url("io=brief")?> ">brief</a>,
+	 <a href="<? print_index_url("io=all")?> ">all</a><br />
+	 Mem:
+	 <a href="<? print_index_url("mem=brief")?> ">brief</a>,
+	 <a href="<? print_index_url("mem=detailed")?> ">detailed</a><br />
+        </div>
+       </div>
+      </td>
+
+     <!-- main window -->
+      <td width="1" bgcolor="#555599"><img src="spacer.png" width="1" height="1" /></td>
+      <td bgcolor="#FFFFFF" valign="top" width="95%">
+       <div align="center" class="blue">
+        <table bgcolor="#6699CC" border="0" cellpadding="0" cellspacing="0">
+         <tr>
+<?
+	 $root_dir = dir($mon2web["tree"]);
+         while ($machine = $root_dir->read()) {
+	     if ($machine != '.' && 
+		 $machine != '..' && 
+		 is_dir($mon2web["tree"]."/".$machine)) {
+		 print '
+          <td><a class="blue" href="';
+		 print_index_url("#" . $machine);
+		 print '">'. $machine . '</a>&nbsp;&nbsp;&nbsp;</td>
+';
+	     }
+	 }
+?>
+	 </tr>
+        </table>
+       </div>
+
+       <div class="items">
+	
+<?php
+// traverse through the monrrd tree
+  $root_dir = dir($mon2web["tree"]);
+  while ($machine = $root_dir->read()) {
+      if ($machine != '.' && 
+	  $machine != '..' && 
+	  is_dir($mon2web["tree"]."/".$machine)) {
+	  print '
+        <!-- item start -->
+        <div class="header">
+	 <a class="header" name="'. $machine . '">'. $machine. '</a><br />
+	</div>
+';
+	  $machine_dir = dir($mon2web["tree"]."/".$machine);
+	  while ($filename = $machine_dir->read()) {
+	      $file = $mon2web["tree"]."/".$machine."/".$filename;
+	      if (is_file($file) && 
+		  preg_match("/^(cpu|if|io|mem)(_([a-z]+))?([0-9]?).rrd$/", 
+			     $filename, $match)) {
+		  $rrdtype = $match[1];
+		  $rrdwhat = $match[3];
+		  $rrdwhich = $match[4];
+		  
+		  $cmd = make_graph_url($rrdtype,
+					"rrdfile=".$file.
+					"&name=".$rrdtype.$rrdwhat.$rrdwhich);
+		  print '<img src="' . $cmd . '">'."\n";
+	      }
+	  }
+	  print '
+        <div class="item-footer"></div>
+';
+
+      }
+  }
+
+?>
+       <div class="copyright">Copyright &copy; 2002 Willem Dijkstra</div>
+      </div> <!-- end items div -->
+
+      </td> <!-- end main window -->
+
+      <!-- horizontal endline -->
+      <td width="1" bgcolor="#555599"><img src="spacer.png" width="1" height="1" /></td>
+     </tr>
+
+      <!-- vertical endline -->
+     <tr> 
+      <td width="5%"/></td>
+      <td colspan="5" bgcolor="#555599"><img src="spacer.png" width="1" height="1" /></td>
+      <td width="5%"/></td>
+     </tr>
+
+    </table>
+   </td>
+   <td width="5%"/></td>
+  </tr>
+ </table>
+</body>
+</html>

+ 245 - 0
mon/mon2web/mon.css

@@ -0,0 +1,245 @@
+body {
+	background-color:#EDEDED;
+	margin: 0px 0px 0px 0px;
+	padding: 0px 0px 0px 0px;
+	color:#333;
+	font-family:verdana, arial, helvetica;
+	font-size:10px;
+	line-height:16px;
+}
+
+a { 
+	font-family:verdana, arial, helvetica; 
+	font-size:11px; 
+	color: #666; 
+	text-decoration: underline; 
+	line-height:19px; 
+	background-color:#FFF;
+} 
+a:link { 
+	color: #666; 
+	text-decoration: underline; 
+	background-color:#FFF;
+} 
+a:visited { 
+	color: #666; 
+	text-decoration: underline; 
+	background-color:#FFF;
+} 
+a:active { 
+	color: #99CC66; 
+	background-color:#FFF;
+} 
+a:hover { 
+	color: #6699CC; 
+	background-color:#FFF;
+} 
+
+a.blue { 
+	color: #FFF; 
+	font-family:verdana, arial, helvetica; 
+	font-weight:normal; 
+	font-size:11px; 
+	text-decoration: underline; 
+	padding-left:15px; 
+	padding-right:15px; 
+	background-color:#6699CC;
+} 
+a.blue:visited { 
+	color: #FFF; 
+	text-decoration: underline; 
+	background-color:#6699CC;
+} 
+a.blue:active { 
+	color: #DDDDDD; 
+	text-decoration: underline; 
+	background-color:#6699CC;
+} 
+a.blue:hover { 
+	color: #FFF; 
+	text-decoration:none; 
+	background-color:#4578AB;
+} 
+
+a.source { 
+	font-family:Courier, MS Courier New, Prestige, Everson Mono; 
+	font-size:12px; 
+	color: #000; 
+	text-decoration: underline; 
+	background-color:#EEEEEE; 
+	padding:1px;
+} 
+a.source:link { 
+	color: #000; 
+	text-decoration: underline; 
+	background-color:#EEEEEE;
+} 
+a.source:visited { 
+	color: #000; 
+	text-decoration: underline; 
+	background-color:#EEEEEE;
+} 
+a.source:active	{ 
+	color: #FFFFFF; 
+	background-color:#888888;
+} 
+a.source:hover { 
+	color: #FFF; text-decoration:none; 
+	background-color:#AAAAAA;
+} 
+
+a.header { 
+	font-family:verdana, arial, helvetica; font-size:11px; 
+	color: #666; 
+	text-decoration: none; 
+	line-height:19px; 
+	background-color:#FFF;
+} 
+a.header:link { 
+	color: #666; 
+	text-decoration: none; 
+	background-color:#FFF;
+} 
+a.header:visited { 
+	color: #666; 
+	text-decoration: none; 
+	background-color:#FFF;
+} 
+a.header:active { 
+	color: #99CC66; 
+	background-color:#FFF;
+} 
+a.header:hover { 
+	color: #6699CC; 
+	background-color:#FFF;
+} 
+
+.links {
+	background-color:#FFF;
+	width:200px;
+	font-family:geneva, verdana, trebuchet MS, trebuchet, verdana, arial, sans-serif;
+	font-size:9px;
+	line-height:17px;
+	color:#000;
+	padding-bottom:15px;
+	border-bottom:1px solid #ccc;
+	margin-bottom:15px;	
+}
+	
+	
+.left {
+	background-color:#FFF;
+	font-family:geneva, verdana, trebuchet MS, trebuchet, verdana, arial, sans-serif;
+	font-size:9px;
+	line-height:17px;
+	color:#000;
+	margin-bottom:15px;
+	padding-left:7px;
+}	
+	
+.blue {
+	background-color:#6699CC;
+	font-family:verdana, trebuchet MS, trebuchet, verdana, arial, sans-serif;
+	font-size:9px;
+	line-height:17px;
+	color:#000;
+	padding-top:3px;	
+	padding-bottom:3px;	
+	height:20px
+	text-align:center;
+}	
+	
+.source {
+	background-color:#EEEEEE;
+	font-family:Courier, MS Courier New, Prestige, Everson Mono;
+	font-size:12px;
+	line-height:13px;
+	color:#000;
+	padding-left:10px;
+	padding-top:5px;
+	border-bottom:1px solid #999;
+	padding-bottom:15px;	
+}
+	
+.page-title {
+	background-color:#CCCC66;
+	padding-left:10px;
+	padding-top:5px;
+	border-bottom:1px solid #999;
+	padding-right:10px;	
+	text-align:right;
+	font-family:verdana, arial, helvetica;
+	font-size:18px;
+	color:#FFF;
+	font-weight:bold;
+	padding-bottom:5px;	
+}
+
+
+.items {
+	background-color:#FFF;
+	font-family:verdana, trebuchet MS, trebuchet, verdana, arial, sans-serif;
+	font-size:11px;
+	line-height:17px;
+	color:#333;
+	padding-left:10px;
+	padding-right:10px;
+	padding-top:5px;
+}
+
+
+.header {
+	background-color:#FFF;
+	font-family:verdana, trebuchet MS, trebuchet, verdana, arial, sans-serif;
+	font-size:10px;
+	text-transform:uppercase;
+	font-weight:bold;
+	line-height:12px;
+	color:#666;
+	padding-left:2px;
+	margin-top:10px;		
+}		
+	
+p {
+	color:#333;
+	font-family: verdana, arial, helvetica;
+	font-size:11px;
+	line-height:17px;
+}
+
+.item-footer {
+	background-color:#FFF;
+	font-family:geneva, verdana, arial, sans-serif;
+	font-size:9px;
+	line-height:12px;
+	color:#666;
+	margin-top:10px;
+	text-align:right;
+	margin-bottom:20px;
+	padding-bottom:10px;
+	border-bottom:1px solid #999;		
+}
+	
+.section-head {
+	background-color:#FFF;
+	font-family:verdana, trebuchet MS, trebuchet, verdana, arial, sans-serif;
+	font-size:11px;
+	line-height:17px;
+	font-weight:bold;
+	color:#333;
+	padding-top:15px;
+	margin-bottom:0px;		
+}	
+		
+.left-links {
+	padding-left:20px;		
+}	
+
+.copyright {
+	font-family: geneva, arial, verdana, helvetica, sans-serif;
+	font-size: 9px;
+	margin-top:5px;
+	text-align:center;
+	color:#999;
+}		
+	

二進制
mon/mon2web/mon.png


二進制
mon/mon2web/spacer.png


+ 30 - 0
mon/monmux/Makefile

@@ -0,0 +1,30 @@
+# $Id: Makefile,v 1.13 2002/07/25 14:25:31 dijkstra Exp $
+.include "../Makefile.inc"
+
+SRCS=	monmux.c readconf.c muxnet.c share.c
+OBJS+=	${SRCS:R:S/$/.o/g}
+LIBS=  -L../lib -L/usr/local/lib -lrrd -lmon
+CFLAGS+=-I../lib -I/usr/local/include
+
+all: monmux monmux.cat8
+
+monmux: ${OBJS}
+	${CC} -o $@ ${OBJS} ${LIBS}
+.ifndef DEBUG
+	${STRIP} $@
+.endif
+
+clean:
+	rm -f monmux monmux.cat8 ${OBJS}
+
+install: monmux monmux.cat8 c_monrrds.sh monmux.conf
+	${INSTALL} -c -m 555 -g bin   -o root monmux      ${PREFIX}/${BINDIR}
+	${INSTALL} -c -m 444 -g wheel -o root monmux.cat8 ${PREFIX}/${MANDIR}/cat8/monmux.0
+	${INSTALL} -d -m 555 -g bin   -o root ${PREFIX}/${SHRDIR}
+	${INSTALL} -c -m 544 -g bin   -o root c_monrrds.sh  ${PREFIX}/${SHRDIR}
+	${INSTALL} -c -m 444 -g bin   -o root monmux.conf   ${PREFIX}/${SHRDIR}
+
+cleanipc:
+	ipcs | egrep "^m" | awk '{print $$2}' | xargs -n1 ipcrm -m &
+	ipcs | egrep "^s" | awk '{print $$2}' | xargs -n1 ipcrm -s 
+

+ 194 - 0
mon/monmux/c_monrrds.sh

@@ -0,0 +1,194 @@
+#!/bin/sh
+# $Id: c_monrrds.sh,v 1.8 2002/07/25 19:10:03 dijkstra Exp $
+
+#
+# Copyright (c) 2001-2002 Willem Dijkstra
+# All rights reserved.
+# 
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 
+#    - Redistributions of source code must retain the above copyright
+#      notice, this list of conditions and the following disclaimer.
+#    - Redistributions in binary form must reproduce the above
+#      copyright notice, this list of conditions and the following
+#      disclaimer in the documentation and/or other materials provided
+#      with the distribution.
+# 
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+# mon datafiles "make" file. Valid arguments:
+#       all      Makes all files for active interfaces and disks
+#       mem      Make memory file
+#       cpu?     Make cpu file
+
+# --- user configuration starts here
+INTERVAL=`grep MON_INTERVAL ../mon/mon.h 2>/dev/null | cut -f3 -d\ `
+INTERVAL=${INTERVAL:-5}
+# RRA setup:
+# - 2   days of  5 second  samples = 34560 x 5 second samples
+# - 14  days of 30 minutes samples = 672 x 360 x 5 second samples 
+# - 50  days of  2 hour    samples = 600 x 1440 x 5 second samples
+# - 600 days of  1 day     samples = 600 x 17280 x 5 second samples
+RRA_SETUP=" RRA:AVERAGE:0.5:1:34560 
+	    RRA:AVERAGE:0.5:360:672 
+	    RRA:AVERAGE:0.5:1440:600 
+	    RRA:AVERAGE:0.5:17280:600 
+	    RRA:MAX:0.5:1:34560 
+	    RRA:MAX:0.5:360:672 
+	    RRA:MAX:0.5:1440:600 
+	    RRA:MAX:0.5:17280:600 
+	    RRA:MIN:0.5:1:34560 
+	    RRA:MIN:0.5:360:672 
+	    RRA:MIN:0.5:1440:600 
+	    RRA:MIN:0.5:17280:600"
+# --- user configuration ends here
+
+# All interfaces and disks
+INTERFACES="an|awi|bge|bridge|cnw|dc|de|ec|ef|eg|el|enc|ep|ex|faith|fea|fpa|fxp|gif|gre|ie|lc|le|le|lge|lmc|lo|ne|ne|nge|ray|rl|ppp|sf|sis|sk|skc|sl|sm|sppp|ste|stge|strip|ti|tl|tr|tun|tx|txp|vlan|vr|wb|we|wi|wx|xe|xl"
+DISKS="sd|cd|ch|rd|raid|ss|uk|vnc|wd"
+
+# addsuffix adds a suffix to each entry of a list (item|item|...)
+addsuffix() {
+    list=$1'|'
+    suffix=$2
+    while [ `echo $list | grep '|'` ]; do
+	newlist=$newlist'|'`echo $list | cut -f1 -d\|`$suffix
+	list=`echo $list | cut -f2- -d\|`
+    done
+    echo $newlist | cut -b2-
+}
+
+this=$0
+if [ X"$1$2$3$4$5$6$7$8$9" == "X" ]; then
+    sh $this help
+    exit 1;
+fi
+
+DISKS=`addsuffix $DISKS [0-9]`
+INTERFACES=`addsuffix $INTERFACES [0-9]`
+
+for i
+do
+# add if_*.rrd if it is an interface
+if [ `echo $i | egrep -e "^($INTERFACES)$"` ]; then i=if_$i.rrd; fi
+# add io_*.rrd if it is a disk
+if [ `echo $i | egrep -e "^($DISKS)$"` ]; then i=io_$i.rrd; fi
+# add .rrd if it is a cpu or mem
+if [ `echo $i | egrep -e "^(cpu[0-9]|mem)$"` ]; then i=$i.rrd; fi
+
+if [ -f $i ]; then
+    echo "$i exists - ignoring"
+    i="done"
+fi
+
+case $i in
+
+all)
+    echo "Creating rrd files for {cpu0|mem|disks|interfaces}"
+    sh $this cpu0 mem
+    sh $this interfaces
+    sh $this disks
+    ;;
+
+if|interfaces)
+    # obtain all network cards
+    sh $this `ifconfig -a| egrep -e "^($INTERFACES):" | cut -f1 -d\:  | sort -u`
+    ;;
+
+io|disks)
+    # obtain all disks
+    sh $this `df | grep dev | sed 's/^\/dev\/\(.*\)[a-z] .*$/\1/' | sort -u`
+    ;;
+
+cpu[0-9].rrd)
+    # Build cpu file
+    rrdtool create $i --step=$INTERVAL \
+	DS:user:GAUGE:5:0:100 \
+	DS:nice:GAUGE:5:0:100 \
+	DS:system:GAUGE:5:0:100 \
+	DS:interrupt:GAUGE:5:0:100 \
+	DS:idle:GAUGE:5:0:100 \
+	$RRA_SETUP
+    echo "$i created"
+    ;;
+
+mem.rrd)
+    # Build memory file
+    rrdtool create $i --step=$INTERVAL \
+	DS:real_active:GAUGE:5:0:U \
+	DS:real_total:GAUGE:5:0:U \
+	DS:free:GAUGE:5:0:U \
+	DS:swap_used:GAUGE:5:0:U \
+	DS:swap_total:GAUGE:5:0:U \
+	$RRA_SETUP
+    echo "$i created"
+    ;;
+
+if_*.rrd)
+    # Build interface files
+    rrdtool create $i --step=$INTERVAL \
+	DS:ipackets:COUNTER:5:U:U DS:opackets:COUNTER:5:U:U \
+	DS:ibytes:COUNTER:5:U:U DS:obytes:COUNTER:5:U:U \
+	DS:imcasts:COUNTER:5:U:U DS:omcasts:COUNTER:5:U:U \
+	DS:ierrors:COUNTER:5:U:U DS:oerrors:COUNTER:5:U:U \
+	DS:collisions:COUNTER:5:U:U DS:drops:COUNTER:5:U:U \
+	$RRA_SETUP
+    echo "$i created"
+    ;;
+
+io_*.rrd)
+    # Build disk files
+    rrdtool create $i --step=$INTERVAL \
+	DS:transfers:COUNTER:5:U:U \
+	DS:seeks:COUNTER:5:U:U \
+	DS:bytes:COUNTER:5:U:U \
+	$RRA_SETUP
+    echo "$i created"
+    ;;
+
+"done")
+    # ignore
+    ;;
+*)
+    # Default match
+    cat <<EOF
+Usage: $0 all
+       $0 cpu0|mem|<if>|<io>
+
+Where:
+if=	`echo $INTERFACES|
+    awk 'BEGIN  {FS="|"}
+		{for (i=1; i<=NF; i++) { 
+		    printf("%s|",$i); 
+		    if ((i%6)==0) {
+			printf("%s","\n	")
+		    }
+		} 
+		print " ";}'`
+io=	`echo $DISKS|
+	awk 'BEGIN  {FS="|"}
+		{for (i=1; i<=NF; i++) { 
+		    printf("%s|",$i); 
+		    if ((i%6)==0) {
+			printf("%s","\n	")
+		    }
+		} 
+		print " ";}'`
+
+EOF
+    ;;
+esac
+done

+ 246 - 0
mon/monmux/monmux.8

@@ -0,0 +1,246 @@
+.\"  -*- nroff -*-
+.\"
+.\" Copyright (c) 2001-2002 Willem Dijkstra
+.\" All rights reserved.
+.\" 
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 
+.\"    - Redistributions of source code must retain the above copyright
+.\"      notice, this list of conditions and the following disclaimer.
+.\"    - Redistributions in binary form must reproduce the above
+.\"      copyright notice, this list of conditions and the following
+.\"      disclaimer in the documentation and/or other materials provided
+.\"      with the distribution.
+.\" 
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+.\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+.\" FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+.\" COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+.\" CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\" 
+.Dd April 5, 2002
+.Dt MONMUX 8
+.Os
+.Sh NAME
+.Nm monmux
+.Nd mon stream multiplexer
+.Sh SYNOPSIS
+.Nm 
+.Op Fl vd
+.Pp
+.Sh DESCRIPTION
+.Xr mon 8
+is a lightweight system monitor that measures cpu, memory, interface and disk
+statistics every 5 seconds. This information is then spooled to
+.Nm
+for further processing. 
+.Pp
+.Xr mon 8
+has been designed to inflict minimal performance and security impact on the
+system it monitors.
+.Nm
+has performance impact proportional to the amount of streams it needs to
+manage. Ideally 
+.Nm 
+should live on a different system and collect data from several
+.Xr mon 8
+instances in a LAN.
+.Nm 
+stores the incoming streams in .rrd files and distributes the information to
+connected listeners. Listeners can connect to
+.Nm
+on a tcp port and receive incoming 
+.Xr mon 8
+transmissions decoded into ascii.
+.Lp
+.Nm
+needs no specific priviledges besides being able to open it's ports and the rrd files. It should be run as
+.Ar "nobody" .
+.Lp
+The options:
+.Bl -tag -width Ds
+.It Fl a
+Show version information.
+.It Fl d
+Stop 
+.Nm
+from becoming a daemon and show debug information on stdout.
+.It Fl f Ar filename
+Read configuration from 
+.Ar filename
+instead of 
+.Pa /etc/monmux.conf .
+.Sh CONFIGURATION
+.Nm
+obtains configuration data from 
+.Pa /etc/monmux.conf .
+The configuration file contains one mux stanza that defines on what interface and port
+.Nm 
+should listen to for incoming monitored data. There is a source section for
+every host that is to be monitored. The source section defines what data to
+accept and where to write that data to. Whitespace, newlines and text behind '#' are ignored. The format in BNF:
+.Pp
+.nf
+stmt         = mux-stmt | source-stmt
+mux-stmt     = "mux" host [ port ]
+host         = ip4addr | hostname
+port         = [ "port" | ":" | "," ] portnumber
+source-stmt  = "source" host "{" accept-stmts write-stmts "}"
+accept-stmts = accept-stmt [accept-stmts]
+accept-stmt  = "accept" "{" resources "}"
+resources    = resource ["(" argument ")"] [ ","|" " resources ]
+resource     = "cpu" | "mem" | "if" | "io"
+argument     = number | interfacename | diskname
+write-stmts  = write-stmt [write-stmts]
+write-stmt   = "write" resource "in" filename
+.fi
+.Pp
+Note that 
+.Va port 
+in the 
+.Va mux-stmt
+specifies the port-number for both the udp port (incoming 
+.Xr mon 8
+traffic) and the tcp port for incoming listeners.
+
+.Sh EXAMPLE
+Here is an example 
+.Ar monmux.conf
+that listens to udp port 2100 on lo0, and accepts cpu, memory, interfaces
+xl0/de0/lo0/wi0, disks wd[0-3]/cd[0-1] streams from a
+.Xr mon 8
+on localhost. 
+.Nm
+will also listen on tcp port 2100 for incoming listeners.
+.Pp
+.nf
+mux 127.0.0.1:2100
+source 127.0.0.1 {
+    accept { cpu(0), mem, 
+             if(xl0), if(de0),
+             if(lo0), if(wi0), 
+             io(wd0), io(wd1), io(wd2), 
+             io(wd3), io(cd0), io(cd1) }
+
+    write cpu(0)   in "/export/mon/localhost/cpu0.rrd"
+    write mem      in "/export/mon/localhost/mem.rrd"
+
+    write if(xl0)  in "/export/mon/localhost/if_xl0.rrd"
+    write if(de0)  in "/export/mon/localhost/if_de0.rrd"
+    write if(lo0)  in "/export/mon/localhost/if_lo0.rrd"
+    write if(wi0)  in "/export/mon/localhost/if_wi0.rrd"
+
+    write io(wd0)  in "/export/mon/localhost/io_wd0.rrd"
+    write io(wd1)  in "/export/mon/localhost/io_wd1.rrd"
+    write io(wd2)  in "/export/mon/localhost/io_wd2.rrd"
+    write io(wd3)  in "/export/mon/localhost/io_wd3.rrd"
+
+    write io(cd0)  in "/export/mon/localhost/io_cd0.rrd"
+    write io(cd1)  in "/export/mon/localhost/io_cd1.rrd"
+}
+.fi
+.Sh LISTENERS
+.Nm
+offers received 
+.Xr mon 8 
+data to other programs via tcp. An example of a listener session:
+.Pp
+.nf
+hoard:~/project/mon$ telnet 127.0.0.1 2100
+Trying 127.0.0.1...
+Connected to 127.0.0.1.
+Escape character is '^]'.
+
+127.0.0.1;io:cd1:1027452867:262:308:536576;io:cd0:1027452867
+:0:0:0;io:wd3:1027452867:249140:249140:11797277696;io:wd2:10
+27452867:4074001:4074001:64923271168;io:wd1:1027452867:43183
+9:431839:11880622080;io:wd0:1027452867:10890717:10890717:125
+382360064;if:lo0:1027452867:86952111:86952111:1164079422:116
+4079422:0:0:0:0:0:0;if:de0:1027452867:117562055:90333984:405
+8603563:1844391820:2588654:1:0:0:4013757:0;if:xl0:1027452867
+:149845272:187785769:2494862812:739916810:43291:1:3:0:318440
+7:0;mem::1027452867:26566656:68579328:323141632:3571712:1047
+67488;cpu:0:1027452867:0.00:0.00:0.00:0.00:99.00;
+^]
+
+telnet> close
+Connection closed.
+.fi
+.Lp
+The format is 
+.Va mon-version
+:
+.Va mon-host-ip
+:
+.Va io|if|cpu|mem
+:
+.Va stream-argument
+:
+.Va timestamp
+:
+.Va data
+.Lp
+Data formats:
+.Bl -tag -width Ds
+.It cpu
+Time spent in (user, nice, system, interrupt, idle). Total time is 100, data is
+offered with precision 2.
+.It mem
+Memory in (real active, real total, free, swap used, swap total). All values
+are in bytes rounded of to page boundaries. Values are 32 bit unsigned integers
+internally.
+.It if
+Interface counters (packets in, packets out, bytes in, bytes out, multicasts
+in, multicasts out, errors in, errors out, collisions, drops). Values are 32
+bit unsigned integers internally.
+.It io
+Io/disk counters (total transfers, total seeks, total bytes). Values are 64 bit
+unsigned integers internally.
+.El
+.Sh SIGNALS
+.Bl -tag -width Ds
+.It SIGHUP
+Causes
+.Nm
+to read 
+.Pa /etc/monmux.conf 
+or the file specified by the 
+.Fl f 
+flag.
+.Nm
+will keep the old configuration if errors occured during parsing of the
+configuration file.
+.El
+.Sh FILES
+.Bl -tag -width Ds
+.It Pa /var/run/monmux.pid
+Contains the program id of the
+.Nm
+daemon.
+.It Pa /etc/monmux.conf
+.Nm
+system wide configuration file. 
+.El
+.Sh BUGS
+.Nm 
+writes incoming data to rrd files "in process". An rrdupdate on a somewhat
+stale rrdfile -- with the last data from quite some time in the past -- is a
+very expensive operation. This can cause
+.Nm 
+to lockup while rrdupdate is updating the rrd file.
+.Nm
+will be unresponsive during this process. 
+.Sh AUTHOR
+Willem Dijkstra <wpd@xs4all.nl>
+.Sh SEE ALSO
+.Xr mon 8 
+ 

+ 310 - 0
mon/monmux/monmux.c

@@ -0,0 +1,310 @@
+/* $Id: monmux.c,v 1.20 2002/08/26 14:53:01 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ 
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <machine/endian.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <rrd.h>
+
+#include "data.h"
+#include "error.h"
+#include "limits.h"
+#include "monmux.h"
+#include "muxnet.h"
+#include "net.h"
+#include "readconf.h"
+#include "share.h"
+#include "xmalloc.h"
+
+__BEGIN_DECLS
+void exithandler();
+void huphandler(int);
+void signalhandler(int);
+__END_DECLS
+
+int flag_hup = 0;
+fd_set fdset;
+int maxfd;
+
+void
+exithandler(int s) {
+    info("received signal %d - quitting", s);
+    exit(EX_TEMPFAIL);
+}
+void
+huphandler(int s) {
+    info("sighup (%d) received", s);
+    flag_hup = 1;
+}
+/* 
+ * Monmux is the receiver of mon performance measurements.
+ *
+ * The main goals mon hopes to accomplish is:
+ * - to take fine grained measurements of system parameters 
+ * - with minimal performance impact 
+ * - in a secure way.
+ * 
+ * Measuring system parameters (e.g. interfaces) sometimes means traversing
+ * lists in kernel memory. Because of this the measurement of data has been
+ * decoupled from the processing and storage of data. Storing the measured
+ * information that mon provides is done by a second program, called monmux.
+ * 
+ * Mon can keep track of cpu, memory, disk and network interface
+ * interactions. Mon was built specifically for OpenBSD.
+ */
+int 
+main(int argc, char *argv[])
+{
+    struct monpacket packet;
+    struct packedstream ps;
+    char *cfgfile;
+    char *cfgpath;
+    char *stringbuf;
+    char *stringptr;
+    int maxstringlen;
+    struct muxlist mul, newmul;
+    struct sourcelist sol, newsol;
+    char *arg_ra[4];
+    struct stream *stream;
+    struct source *source;
+    struct mux *mux;
+    FILE *f;
+    int ch;
+    int offset;
+    time_t timestamp;
+    int churnbuflen;
+
+    SLIST_INIT(&mul);
+    SLIST_INIT(&sol);
+	
+    /* reset flags */
+    flag_debug = 0;
+    flag_daemon = 0;
+    
+    cfgfile = MONMUX_CONFIG_FILE;
+    while ((ch = getopt(argc, argv, "dvf:")) != -1) {
+	switch (ch) {
+	case 'd':
+	    flag_debug = 1;
+	    break;
+	case 'f':
+	    if (optarg && optarg[1] != '/') {
+		/* cfg path needs to be absolute, we will be a daemon soon */
+		if ((cfgpath = getwd(NULL)) == NULL)
+		    fatal("could not get working directory");
+		
+		maxstringlen = strlen(cfgpath) + strlen(optarg) + 1;
+		cfgfile = xmalloc(maxstringlen);
+		strncpy(cfgfile, cfgpath, maxstringlen);
+		stringptr = cfgfile + strlen(cfgpath);
+		stringptr[0] = '/';
+		stringptr++;
+		strncpy(stringptr, optarg, maxstringlen - (cfgfile - stringptr));
+		cfgfile[maxstringlen] = '\0';
+
+		free(cfgpath);
+	    } else 
+		cfgfile = xstrdup(optarg);
+
+	    break;
+	case 'v':
+	    info("monmux version %s", MONMUX_VERSION);
+	default:
+	    info("usage: %s [-d] [-v] [-f cfgfile]", __progname);
+	    exit(EX_USAGE);
+	}
+    }
+
+    if (flag_debug != 1) {
+	if (daemon(0,0) != 0)
+	    fatal("daemonize failed");
+	
+	flag_daemon = 1;
+
+	/* record pid */
+	f = fopen(MONMUX_PID_FILE, "w");
+	if (f) {
+	    fprintf(f, "%u\n", (u_int) getpid());
+	    fclose(f);
+	}
+    } 
+    
+    info("monmux version %s", MONMUX_VERSION);
+
+    /* parse configuration file */
+    if (!read_config_file(&mul, &sol, cfgfile))
+	fatal("configuration contained errors; quitting");
+
+
+    if (flag_debug == 1)
+	info("program id=%d", (u_int) getpid());
+
+    mux = SLIST_FIRST(&mul);
+
+    churnbuflen = calculate_churnbuffer(&sol);
+    debug("size of churnbuffer = %d", churnbuflen);
+    initshare(churnbuflen);
+
+    /* catch signals */
+    signal(SIGHUP, huphandler);
+    signal(SIGINT, exithandler); 
+    signal(SIGQUIT, exithandler); 
+    signal(SIGTERM, exithandler);
+    signal(SIGTERM, exithandler); 
+
+    /* Prepare crc32 */
+    init_crc32();
+
+    getmonsocket(mux);
+    getclientsocket(mux);
+
+    for (;;) {
+	waitfortraffic(mux, &sol, &source, &packet);
+
+	if (flag_hup == 1) {
+	    flag_hup = 0;
+
+	    SLIST_INIT(&newmul);
+	    SLIST_INIT(&newsol);
+
+	    if (!read_config_file(&newmul, &newsol, cfgfile)) {
+		info("new configuration contains errors; keeping old configuration");
+		free_muxlist(&newmul);
+		free_sourcelist(&newsol);
+	    } else {
+		info("read configuration file succesfully");
+		free_muxlist(&mul);
+		free_sourcelist(&sol);
+		mul = newmul;
+		sol = newsol;
+		mux = SLIST_FIRST(&mul);
+		getmonsocket(mux);
+		getclientsocket(mux);
+	    }
+	    break; /* wait for next alarm */
+	} 
+
+	/* Put information from packet into stringbuf (shared region). Note
+	 * that the stringbuf is used twice: 1) to update the rrdfile and 2) to
+	 * collect all the data from a single packet that needs to shared to
+	 * the clients. This is the reason for the hasseling with stringptr.
+	 */
+	
+	offset = 0;
+	maxstringlen = shared_getmaxlen();
+	/* put time:ip: into shared region */
+	master_forbidread();
+	timestamp = (time_t) packet.header.timestamp;
+	stringbuf = (char *)shared_getmem();
+	snprintf(stringbuf, maxstringlen, "%u.%u.%u.%u;",
+		 IPAS4BYTES(source->ip));
+	
+	/* hide this string region from rrd update */
+	maxstringlen -= strlen(stringbuf);
+	stringptr = stringbuf + strlen(stringbuf);
+	
+	while (offset < packet.header.length) {
+	    offset += sunpack(packet.data + offset, &ps);
+	    
+	    /* find stream in source */
+	    stream = find_source_stream(source, ps.type, ps.args);
+	    
+	    if (stream != NULL) {
+		/* put type in and hide from rrd */
+		snprintf(stringptr, maxstringlen, "%s:", type2str(ps.type));
+		maxstringlen -= strlen(stringptr);
+		stringptr += strlen(stringptr);
+		/* put arguments in and hide from rrd */
+		snprintf(stringptr, maxstringlen, "%s:", 
+			 ((ps.args == NULL) ? "0" : ps.args));
+		maxstringlen -= strlen(stringptr);
+		stringptr += strlen(stringptr);
+		/* put timestamp in and show to rrd */
+		snprintf(stringptr, maxstringlen, "%u", timestamp);
+		arg_ra[3] = stringptr;
+		maxstringlen -= strlen(stringptr);
+		stringptr += strlen(stringptr);
+		
+		/* put measurements in */
+		ps2strn(&ps, stringptr, maxstringlen, PS2STR_RRD);
+		
+		if (stream->file != NULL) {
+		    /* save if file specified */
+		    arg_ra[0] = "rrdupdate";
+		    arg_ra[1] = "--";
+		    arg_ra[2] = stream->file;
+		    
+		    /* This call will cost a lot (monmux will become
+		     * unresponsive and eat up massive amounts of cpu) if
+		     * the rrdfile is out of sync. While I could update the
+		     * rrd in a separate process, I choose not to at this
+		     * time.  
+		     */
+		    rrd_update(4, arg_ra);
+		    
+		    if (rrd_test_error()) {
+			warning("rrd_update:%s", rrd_get_error());
+			warning("%s %s %s %s", arg_ra[0], arg_ra[1], 
+				arg_ra[2], arg_ra[3]);
+			rrd_clear_error();                                                            
+		    } else {
+			if (flag_debug == 1) 
+			    debug("%s %s %s %s", arg_ra[0], arg_ra[1], 
+				  arg_ra[2], arg_ra[3]);
+		    }
+		}
+		maxstringlen -= strlen(stringptr);
+		stringptr += strlen(stringptr);
+		snprintf(stringptr, maxstringlen, ";");
+		maxstringlen -= strlen(stringptr);
+		stringptr += strlen(stringptr);
+	    }
+	}
+	/* packet = parsed and in ascii in shared region -> copy to clients */
+	snprintf(stringptr, maxstringlen, "\n");
+	stringptr += strlen(stringptr);
+	shared_setlen((stringptr - stringbuf));
+	debug("Churnbuffer used: %d", (stringptr - stringbuf));
+	master_permitread();
+    }
+    /* NOT REACHED */
+    return (EX_SOFTWARE);
+}

+ 62 - 0
mon/monmux/monmux.conf

@@ -0,0 +1,62 @@
+#
+# $Id: monmux.conf,v 1.16 2002/08/26 14:53:01 dijkstra Exp $
+#
+# Initial monmux.conf demonstrates how I configure monmux.
+
+#
+# hub <hubname> defines the destination of all monitored data described in this
+# configuration file.
+#
+# note that monmux listens for incoming mon traffic on udp 2100 and incoming
+# client connections on tcp 2100
+#
+
+mux 10.0.0.1:2100
+
+#
+# source <hostname> defines a source of monitored data. 
+#
+source 10.0.0.1 {
+
+	# All accepted formats have to be defined explicitly. 
+	#
+	# cpu(x) - gather stats for cpu nr. x
+	# mem    - gather memory stats
+	# if(x)  - gather io stats for interface x
+	# io(x)  - gather io stats for disk x.
+
+	accept { cpu(0), mem, if(xl0), if(de0),
+	         if(lo0), io(wd0), io(wd1), 
+                 io(wd2), io(wd3), io(cd0)}
+	
+	# Define where data should be put
+	#
+	# Accepted sources that are not written out will be 
+	# discarded.
+
+	write cpu(0)   in "/export/mon/www/cpu0.rrd"
+	write mem      in "/export/mon/www/mem.rrd"
+
+	write if(xl0)  in "/export/mon/www/if_xl0.rrd"
+	write if(de0)  in "/export/mon/www/if_de0.rrd"
+	write if(lo0)  in "/export/mon/www/if_lo0.rrd"
+	
+	write io(wd0)  in "/export/mon/www/io_wd0.rrd"
+	write io(wd1)  in "/export/mon/www/io_wd1.rrd"
+	write io(wd2)  in "/export/mon/www/io_wd2.rrd"
+	write io(wd3)  in "/export/mon/www/io_wd3.rrd"
+
+	write io(cd0)  in "/export/mon/www/io_cd0.rrd"
+}
+source 10.0.0.2 {
+	accept { cpu(0), mem, if(sis0), if(sis1),
+		 if(lo0), if(wi0), io(wd0) }
+
+	write cpu(0)   in "/export/mon/4512/cpu0.rrd"
+	write mem      in "/export/mon/4512/mem.rrd"
+	write if(sis0) in "/export/mon/4512/if_sis0.rrd"
+	write if(sis1) in "/export/mon/4512/if_sis1.rrd"
+	write if(lo0)  in "/export/mon/4512/if_lo0.rrd"
+	write if(wi0)  in "/export/mon/4512/if_wi0.rrd"
+	write io(wd0)  in "/export/mon/4512/io_wd0.rrd"
+}

+ 48 - 0
mon/monmux/monmux.h

@@ -0,0 +1,48 @@
+/* $Id: monmux.h,v 1.10 2002/08/09 08:23:39 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _MONMUX_MONMUX_H
+#define _MONMUX_MONMUX_H
+
+#define MONMUX_CONFIG_FILE "/etc/monmux.conf"
+#define MONMUX_PID_FILE "/var/run/monmux.pid"
+#define MONMUX_VERSION "2.0"
+
+/* Amount of connections allowed in listen backlog 
+ * - note that more than 128 makes no sense in OpenBSD
+ */
+#define MONMUX_TCPBACKLOG 5
+
+/* Number of retries allowed in recvfrom */
+#define MONMUX_MAXREADTRIES 5
+
+#endif /*_MONMUX_MONMUX_H*/

+ 235 - 0
mon/monmux/muxnet.c

@@ -0,0 +1,235 @@
+/* $Id: muxnet.c,v 1.8 2002/08/11 20:00:41 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "data.h"
+#include "error.h"
+#include "monmux.h"
+#include "muxnet.h"
+#include "net.h"
+#include "xmalloc.h"
+#include "share.h"
+
+/* Obtain a udp socket for incoming mon traffic */
+int 
+getmonsocket(struct mux *mux) 
+{
+    struct sockaddr_in sockaddr;
+    int sock;
+    
+    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 
+	fatal("could not obtain socket: %.200s", strerror(errno));
+
+    sockaddr.sin_family = AF_INET;
+    sockaddr.sin_port = htons(mux->port);
+    sockaddr.sin_addr.s_addr = htonl(mux->ip);
+    bzero(&sockaddr.sin_zero, sizeof(sockaddr.sin_zero));
+
+    if (bind(sock, (struct sockaddr *) &sockaddr, 
+	     sizeof(struct sockaddr)) == -1)
+	fatal("could not bind socket: %.200s", strerror(errno));
+
+    mux->monsocket = sock;
+
+    info("listening for incoming mon traffic on udp:%s:%d", 
+	 mux->name, mux->port);
+
+    return sock;
+}
+/* Obtain a listen socket for new clients */
+int
+getclientsocket(struct mux *mux)
+{
+  struct sockaddr_in sockaddr;
+  int sock;
+  
+  if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
+	fatal("could not obtain socket: %.200s", strerror(errno));
+
+    sockaddr.sin_family = AF_INET;
+    sockaddr.sin_port = htons(mux->port);
+    sockaddr.sin_addr.s_addr = htonl(mux->ip);
+    bzero(&sockaddr.sin_zero, sizeof(sockaddr.sin_zero));
+
+    if (bind(sock, (struct sockaddr *) &sockaddr, 
+	     sizeof(struct sockaddr)) == -1)
+	fatal("could not bind socket: %.200s", strerror(errno));
+
+    if (listen(sock, MONMUX_TCPBACKLOG) == -1)
+      fatal("could not listen to socket: %.200s", strerror(errno));
+
+    fcntl(sock, O_NONBLOCK);
+    mux->clientsocket = sock;
+
+    info("listening for incoming connections on tcp:%s:%d", 
+	 mux->name, mux->port);
+
+    return sock;
+}
+/*
+ * Wait for traffic (mon reports from a source in sourclist | clients trying to connect
+ * Returns the <source> and <packet>
+ * Silently forks off clienthandlers
+ */
+void
+waitfortraffic(struct mux *mux, struct sourcelist *sourcelist, 
+	       struct source **source, struct monpacket *packet) 
+{
+    fd_set readset;
+    int socksactive;
+    int maxsock;
+
+    maxsock = ((mux->clientsocket > mux->monsocket) ?
+	       mux->clientsocket :
+	       mux->monsocket);
+    maxsock++;
+
+    for (;;) { /* FOREVER - until a valid mon packet is received */
+	FD_ZERO(&readset);
+	FD_SET(mux->clientsocket, &readset);
+	FD_SET(mux->monsocket, &readset);
+	
+	socksactive = select(maxsock, &readset, NULL, NULL, NULL);
+	
+	if (socksactive != -1) {
+	    if (FD_ISSET(mux->clientsocket, &readset)) {
+		spawn_client(mux->clientsocket);
+	    }
+
+	    if (FD_ISSET(mux->monsocket, &readset)) {
+		if (recvmonpacket(mux, sourcelist, source, packet))
+		    return;
+	    }
+	} else {
+	    if (errno == EINTR)
+		return;  /* signal received while waiting, bail out */
+	}
+    }
+}
+/* Receive a mon packet for mux. Checks if the source is allowed and returns the source found.
+ * return 0 if no valid packet found 
+ */
+int
+recvmonpacket(struct mux *mux, struct sourcelist *sourcelist, 
+	      struct source **source, struct monpacket *packet) 
+
+{
+    struct sockaddr_in sind;
+    u_int32_t sourceaddr;
+    int size;
+    unsigned int received;
+    socklen_t sl;
+    int tries;
+    u_int32_t crc;
+
+    received = 0;
+    tries = 0;
+    do {
+	sl = sizeof(sind);
+
+	size = recvfrom(mux->monsocket, (void *)packet + received, 
+			sizeof(struct monpacket) - received, 
+			0, (struct sockaddr *)&sind, &sl);
+	if (size > 0)
+	  received += size;
+
+	tries++;
+    } while ((size == -1) && 
+	     (errno == EAGAIN || errno == EINTR) && 
+	     (tries < MONMUX_MAXREADTRIES) &&
+	     (received < sizeof(struct monpacket)));
+
+    if ((size == -1) && 
+	errno) 
+      warning("recvfrom failed: %s", strerror(errno));
+
+    sourceaddr = ntohl((u_int32_t)sind.sin_addr.s_addr);
+    *source = find_source_ip(sourcelist, sourceaddr);
+	    
+    if (*source == NULL) {
+	debug("ignored data from %u.%u.%u.%u",
+	      IPAS4BYTES(sourceaddr));
+	return 0;
+    } else {
+	/* check crc */
+	crc = ntohl(packet->header.crc);
+	packet->header.crc = 0;
+	crc ^= crc32(packet, received);
+	if ( crc != 0 ) {
+	    warning("ignored packet with bad crc from %u.%u.%u.%u",
+		    IPAS4BYTES(sourceaddr));
+	    return 0;
+	}
+	/* check packet version */
+	if (packet->header.mon_version != MON_PACKET_VER) {
+	    warning("ignored packet with illegal type %d", 
+		    packet->header.mon_version);
+	    return 0;
+	} else {
+	    /* rewrite structs to host order */
+	    packet->header.length = ntohs(packet->header.length);
+	    packet->header.crc = ntohs(packet->header.crc);
+	    packet->header.timestamp = ntohq(packet->header.timestamp);
+		
+	    if (flag_debug) 
+		debug("good data received from %u.%u.%u.%u",
+		      IPAS4BYTES(sourceaddr));
+	    return 1;  /* good packet received */
+	} 
+    }
+}
+int 
+acceptconnection(int sock)
+{
+    struct sockaddr_in sind;
+    socklen_t len;
+    u_int32_t clientaddr;
+    int clientsock;
+
+    if ((clientsock = accept(sock, (struct sockaddr *)&sind, &len)) < 0)
+	fatal("Failed to accept an incoming connection. (.200%s)",
+	      strerror(errno));
+
+    clientaddr = ntohl((u_int32_t)sind.sin_addr.s_addr);
+    info("Accepted incoming client connection from %u.%u.%u.%u",
+	 IPAS4BYTES(clientaddr));
+
+    return clientsock;
+}   

+ 53 - 0
mon/monmux/muxnet.h

@@ -0,0 +1,53 @@
+/* $Id: muxnet.h,v 1.3 2002/06/21 12:24:21 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _MONMUX_MUXNET_H
+#define _MONMUX_MUXNET_H
+
+#include "data.h"
+
+#define IPAS4BYTES(x) \
+        ((x) >> 24), ((x) >> 16) & 0xff, ((x) >> 8) & 0xff, (x) & 0xff
+
+/* prototypes */
+__BEGIN_DECLS
+int  acceptconnection(int);
+int  getmonsocket(struct mux *);
+int  getclientsocket(struct mux *);
+void waitfortraffic(struct mux *, struct sourcelist *, 
+		    struct source **, struct monpacket *);
+int  recvmonpacket(struct mux *, struct sourcelist *, 
+		   struct source **, struct monpacket *);
+int  check_crc_packet(struct monpacket *);
+__END_DECLS
+#endif /*_MONMUX_MUXNET_H*/
+

+ 316 - 0
mon/monmux/readconf.c

@@ -0,0 +1,316 @@
+/* $Id: readconf.c,v 1.11 2002/08/11 19:54:48 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <sys/queue.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "xmalloc.h"
+#include "lex.h"
+#include "error.h"
+#include "net.h"
+#include "data.h"
+
+__BEGIN_DECLS
+int read_mux(struct muxlist *mul, struct lex *);
+int read_source(struct sourcelist *sol, struct lex *);
+__END_DECLS
+
+/* mux <host> (port|:|,| ) <number> */
+int 
+read_mux(struct muxlist *mul, struct lex *l)
+{
+    struct mux *m;
+
+    if (! SLIST_EMPTY(mul)) {
+	warning("%s:%d: only one mux statement allowed", 
+		l->filename, l->cline);
+	return 0;
+    }
+    
+    lex_nexttoken(l);
+    if (!lookup(l->token)) {
+	warning("%s:%d: could not resolve '%s'",
+		l->filename, l->cline, l->token );
+	return 0;
+    }
+
+    m = add_mux(mul, lookup_address);
+    m->ip = lookup_ip;
+
+    /* check for port statement */
+    lex_nexttoken(l);
+    if (l->op == LXT_PORT || l->op == LXT_COLON || l->op == LXT_COMMA)
+	lex_nexttoken(l);
+    else {
+	if (l->type != LXY_NUMBER) {
+	    lex_ungettoken(l);
+	    m->port = MONMUX_PORT;
+	    return 1;
+	}
+    }
+
+    if (l->type != LXY_NUMBER) {
+	parse_error(l, "<number>");
+	return 0;
+    }
+
+    m->port = l->value;
+
+    return 1;
+}
+/* source <host> { accept ... | write ... } */
+int 
+read_source(struct sourcelist *sol, struct lex *l)
+{
+    struct source *source;
+    struct stream *stream;
+    char sn[_POSIX2_LINE_MAX];
+    char sa[_POSIX2_LINE_MAX];
+    int  st;
+
+    /* get hostname */
+    lex_nexttoken(l);
+    if (!lookup(l->token)) {
+	warning("%s:%d: could not resolve '%s'",
+		l->filename, l->cline, l->token );
+	return 0; 
+    }
+
+    source = add_source(sol, lookup_address);
+    source->ip = lookup_ip;
+
+    EXPECT(l, LXT_BEGIN);
+    while (lex_nexttoken(l)) {
+	switch (l->op) {
+	    /* accept { cpu(x), ... } */
+	case LXT_ACCEPT:
+	    EXPECT(l, LXT_BEGIN);
+	    while (lex_nexttoken(l) && l->op != LXT_END) {
+		switch (l->op) {
+		case LXT_CPU:
+		case LXT_IF:
+		case LXT_IO:
+		case LXT_MEM:
+		    st = token2type(l->op);
+		    strncpy(&sn[0], l->token, _POSIX2_LINE_MAX);
+
+		    /* parse arg */
+		    lex_nexttoken(l);
+		    if (l->op == LXT_OPEN) {
+			lex_nexttoken(l);
+			if (l->op == LXT_CLOSE) {
+			    parse_error(l, "<stream argument>");
+			    return 0;
+			}
+
+			strncpy(&sa[0], l->token, _POSIX2_LINE_MAX);
+			lex_nexttoken(l);
+
+			if (l->op != LXT_CLOSE) {
+			    parse_error(l, ")");
+			    return 0;
+			}
+		    } else {
+			lex_ungettoken(l);
+			sa[0]='\0';
+		    }
+
+		    if ((stream = add_source_stream(source, st, sa)) == NULL) {
+			warning("%s:%d: stream %s(%s) redefined", 
+				l->filename, l->cline, sn, sa);
+			return 0;
+		    }
+
+		    break; /* LXT_CPU/LXT_IF/LXT_IO/LXT_MEM */
+		case LXT_COMMA:
+		    break;
+		default:
+		    parse_error(l, "{cpu|mem|if|io}");
+		    return 0;
+
+		    break;
+		}
+	    }
+	    break; /* LXT_ACCEPT */
+	    /* write cpu(0) in "filename" */
+	case LXT_WRITE:
+	    lex_nexttoken(l);
+	    switch (l->op) {
+	    case LXT_CPU:
+	    case LXT_IF:
+	    case LXT_IO:
+	    case LXT_MEM:
+		st = token2type(l->op);
+		strncpy(&sn[0], l->token, _POSIX2_LINE_MAX);
+		
+		/* parse arg */
+		lex_nexttoken(l);
+		if (l->op == LXT_OPEN) {
+		    lex_nexttoken(l);
+		    if (l->op == LXT_CLOSE) {
+			parse_error(l, "<stream argument>");
+			return 0;
+		    }
+
+		    strncpy(&sa[0], l->token, _POSIX2_LINE_MAX);
+		    lex_nexttoken(l);
+		    if (l->op != LXT_CLOSE) {
+			parse_error(l, ")");
+			return 0;
+		    }
+		} else {
+		    lex_ungettoken(l);
+		    sa[0]='\0';
+		}
+		
+		EXPECT(l, LXT_IN);
+
+		lex_nexttoken(l);
+		
+		if ((stream = find_source_stream(source, st, sa)) == NULL) {
+		    if (strlen(sa)) {
+			warning("%s:%d: stream %s(%s) is not accepted for %s", 
+				l->filename, l->cline, sn, sa, source->name);
+			return 0;
+		    } else {
+			warning("%s:%d: stream %s is not accepted for %s", 
+				l->filename, l->cline, sn, source->name);
+			return 0;
+		    }
+		} else {
+		    int fd;
+		    /* try filename */
+		    if ((fd = open(l->token, O_RDWR | O_NONBLOCK, 0)) == -1) {
+			warning("%s:%d: file '%s' cannot be opened", 
+				l->filename, l->cline, l->token);
+			return 0;
+		    } else {
+			close(fd);
+			stream->file = xstrdup(l->token);
+		    }
+		}
+		break; /* LXT_CPU/LXT_IF/LXT_IO/LXT_MEM */
+	    default:
+		parse_error(l, "{cpu|mem|if|io}");
+		return 0;
+		break;
+	    }
+	    break; /* LXT_WRITE */
+	case LXT_END:
+	    return 1;
+	default:
+	    parse_error(l, "accept|write");
+	    return 0;
+	}
+    }
+
+    warning("%s:%d: missing close brace on source statement", 
+	    l->filename, l->cline);
+
+    return 0;
+}
+/* Read monmux.conf */
+int  
+read_config_file(struct muxlist *mul, 
+		 struct sourcelist *sol, 
+		 const char *filename)
+{
+    struct lex *l;
+    struct source *source;
+    struct stream *stream;
+
+    SLIST_INIT(mul);
+    SLIST_INIT(sol);
+
+    if ((l = open_lex(filename)) == NULL)
+	return 0;
+    
+    info("reading configfile '%s'", filename);
+
+    while (lex_nexttoken(l)) {
+    /* expecting keyword now */
+	switch (l->op) {
+	case LXT_MUX:
+	    if (!read_mux(mul, l))
+		return 0;
+	    break;
+	case LXT_SOURCE:
+	    if (!read_source(sol, l))
+		return 0;
+	    break;
+	default:
+	    parse_error(l, "mux|source" );
+	    return 0;
+	    break;
+	}
+    }
+    
+    /* sanity checks */
+    if (SLIST_EMPTY(mul)) {
+	warning("%s: no mux statement seen",
+		l->filename);
+	return 0;
+    }
+
+    if (SLIST_EMPTY(sol)) {
+	warning("%s: no source section seen", 
+		l->filename);
+	return 0;
+    } else {
+	SLIST_FOREACH(source, sol, sources) {
+	    if (SLIST_EMPTY(&source->sl)) {
+		warning("%s: no streams accepted for source '%s'", 
+			l->filename, source->name);
+		return 0;
+	    } else {
+		SLIST_FOREACH(stream, &source->sl, streams) {
+		    if (stream->file == NULL) {
+			warning("%s: no filename specified for stream '%s(%s)' in source '%s'",
+				l->filename, type2str(stream->type), stream->args, source->name);
+			return 0;
+		    }
+		}
+	    }
+	}
+    }
+    
+    close_lex(l);
+    
+    return 1;
+}

+ 45 - 0
mon/monmux/readconf.h

@@ -0,0 +1,45 @@
+/* $Id: readconf.h,v 1.4 2002/04/01 20:16:04 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (copyrighting a file like this does seem rather pointless...)
+ */
+
+#ifndef _MONMUX_READCONF_H
+#define _MONMUX_READCONF_H
+
+#include "data.h"
+
+__BEGIN_DECLS
+int read_config_file(struct muxlist *, struct sourcelist *, const char *);
+__END_DECLS
+
+#endif /*_MONMUX_READCONF_H*/
+
+

+ 395 - 0
mon/monmux/share.c

@@ -0,0 +1,395 @@
+/* $Id: share.c,v 1.6 2002/07/11 15:27:33 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/* TODO:
+ * Dynamically allocate buffer size 
+ * Check wether one buffer suffices, do some performance tests
+ */
+
+/* Share contains all routines needed for the ipc between monmuxes */
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "data.h"
+#include "error.h"
+#include "muxnet.h"
+#include "share.h"
+
+/* Shared operation: 
+ *
+ * The moment the master monmux receives a new packet:
+ *
+ * master calls master_forbidread:
+ * - master checks the 'SEM_READ' semaphore to be equal to the number of
+ *   children active. If so, all children are up to date. If not, a child is
+ *   lagging and will die soon. Reap_clients().
+ * - master adds any pending new clients
+ * - master resets all semaphores, preventing clients to start reading
+ *
+ * master calls master_permitread:
+ * - increment sequence number in the shared region
+ * - increment 'SEM_WAIT' with the number of registered clients
+ *
+ * clients call client_waitread:
+ * - block on semaphore 'SEM_WAIT'
+ * - set client sequence number to shared region sequence number
+ *
+ * clients do something to the data in the shared region
+ *
+ * clients call client_doneread:
+ * - If the recorded sequence number deviates from the one in the shared region
+ *   the client is lagging behind. The client will kill itself.
+ * - The client increments 'SEM_READ'.
+ *
+ */
+
+__BEGIN_DECLS
+void check_master();
+void check_sem();
+void client_doneread();
+void client_loop();
+void client_waitread();
+void exitmaster();
+void master_resetsem();
+void reap_clients();
+int  recvfd(int);
+int  sendfd(int, int);
+__END_DECLS
+
+#define SEM_WAIT     0              /* wait semaphore */
+#define SEM_READ     1              /* have read semaphore */
+#define SEM_TOTAL    2
+
+int   realclients;               /* number of clients active */
+int   newclients;
+int   master;                    /* is current process master or child */
+int   clientsock;                /* connected client */
+
+enum  ipcstat { SIPC_FREE, SIPC_KEYED, SIPC_ATTACHED };
+key_t shmid;
+struct sharedregion *shm;
+enum ipcstat shmstat;
+key_t semid;
+enum ipcstat semstat;
+int   seqnr;
+/* Get start of data in shared region */
+long *
+shared_getmem()
+{
+    return &shm->data;
+}
+/* Get max length of data stored in shared region */
+long
+shared_getmaxlen()
+{
+    return shm->reglen - sizeof(struct sharedregion);
+}
+/* Set length of data stored in shared region */
+void
+shared_setlen(long length)
+{
+    if (length > (shm->reglen - (long)sizeof(struct sharedregion)))
+	fatal("%s:%d: Internal error:"
+	      "set_length of shared region called with value larger than actual size",
+	      __FILE__, __LINE__);
+    shm->ctlen = length;
+}
+/* Get length of data stored in shared region */
+long
+shared_getlen()
+{
+    return shm->ctlen;
+}
+/* Check whether semaphore is available */
+void
+check_sem()
+{
+    if (semstat != SIPC_KEYED)
+	fatal("%s:%d: Internal error: Semaphore not available",
+	      __FILE__, __LINE__);
+}
+
+/* Check whether process is the master process */
+void
+check_master()
+{
+    if (master == 0)
+	fatal("%s:%d: Internal error: Child process tried to access master routines",
+	      __FILE__, __LINE__);
+}
+
+/* Reset semaphores before each distribution cycle */
+void 
+master_resetsem()
+{
+    check_sem();
+    check_master();
+
+    if ((semctl(semid, SEM_WAIT, SETVAL, 0) != 0) ||
+	(semctl(semid, SEM_READ, SETVAL, 0) != 0))
+	fatal("%s:%d: Internal error: Cannot reset semaphores",
+	      __FILE__, __LINE__);
+}
+/* Prepare for writing to shm */
+void
+master_forbidread()
+{
+    int clientsread;
+
+    check_sem();
+    check_master();
+    
+    /* prepare for a new read */
+    if ((clientsread = semctl(semid, SEM_READ, GETVAL, 0)) < 0)
+	fatal("%s:%d: Internal error: Cannot read semaphore",
+	      __FILE__, __LINE__);
+
+    if (clientsread != realclients)
+	reap_clients();
+    
+    /* add new clients */
+    realclients += newclients;
+    newclients = 0;    
+    master_resetsem();
+}
+/* Signal 'permit read' to all clients */
+void
+master_permitread()
+{
+    shm->seqnr++;
+
+    if (semctl(semid, SEM_WAIT, SETVAL, realclients) != 0)
+	fatal("%s:%d: Internal error: Cannot reset semaphores",
+	      __FILE__, __LINE__);
+}
+
+/* Make clients wait until master signals */
+void
+client_waitread()
+{
+    struct sembuf sops;
+
+    check_sem();
+
+    sops.sem_num = SEM_WAIT;
+    sops.sem_op  = -1;
+    sops.sem_flg = 0;
+
+    if (semop(semid, &sops, 1) != 0 )
+	fatal("%s:%d: Internal error: Cannot obtain semaphore (%.200s)",
+	      __FILE__, __LINE__, strerror(errno));
+
+    seqnr = shm->seqnr;
+}
+
+/* Client signal 'done reading' to master */
+void
+client_doneread()
+{
+    struct sembuf sops;
+
+    check_sem();
+
+    if (seqnr != shm->seqnr)
+	fatal("%s:%d: Internal error: Client lagging behind (%d, %d)",
+	      __FILE__, __LINE__, seqnr, shm->seqnr);
+
+    sops.sem_num = SEM_READ;
+    sops.sem_op  = 1;
+    sops.sem_flg = IPC_NOWAIT;
+
+    if (semop(semid, &sops, 1) != 0 )
+	fatal("%s:%d: Internal error: Cannot release semaphore (%.200s)",
+	      __FILE__, __LINE__, strerror(errno));
+}
+
+/* Prepare sharing structures for use */
+void 
+initshare(int bufsize)
+{
+    newclients = 0;
+    realclients = 0;
+    master = 1;
+    
+    /* need some extra space for housekeeping */
+    bufsize += sizeof(struct sharedregion);
+
+    /* allocate shared memory region for control information */
+    shmstat = semstat = SIPC_FREE;
+
+    atexit(exitmaster);
+
+    if ((shmid = shmget(IPC_PRIVATE, bufsize, SHM_R | SHM_W)) < 0)
+	fatal("Could not get a shared memory identifier");
+    
+    shmstat = SIPC_KEYED;
+
+    if ((int)(shm = (struct sharedregion *)shmat(shmid, 0, 0)) < 0)
+	fatal("Could not attach shared memory");
+
+    shmstat = SIPC_ATTACHED;
+    bzero(shm, bufsize);
+    shm->reglen = bufsize;
+    
+    /* allocate semaphores */
+    if ((semid = semget(IPC_PRIVATE, SEM_TOTAL, SEM_A | SEM_R)) < 0)
+	fatal("Could not get a semaphore");
+    
+    semstat = SIPC_KEYED;
+
+    master_resetsem();
+}
+
+/* Spawn off a new client */
+void
+spawn_client(int sock) 
+{
+    pid_t client_pid;
+    check_master();
+
+    clientsock = acceptconnection(sock);
+    
+    if ((client_pid = fork())) {
+	/* server */
+	newclients++;
+	close(clientsock);
+    } else {
+	/* client */
+	master = 0;
+	client_loop();
+    }
+}
+
+/* Reap exit/stopped clients */
+void
+reap_clients()
+{
+    pid_t clientpid;
+    int   status;
+    
+    status = 0;
+
+    /* Reap all children that died */
+    while (((int)(clientpid = wait4(-1, &status, WNOHANG, NULL)) > 0) && realclients >= 0) {
+
+      /* wait4 is supposed to return 0 if there is no status to report, but
+	 it also reports -1 on OpenBSD 2.9 */
+
+	if (WIFEXITED(status))
+	    info("client process %d exited", clientpid, status);
+	if (WIFSIGNALED(status))
+	    info("client process %d killed with signal %d", clientpid, WTERMSIG(status));
+	if (WIFSTOPPED(status))
+	    info("client process %d stopped with signal %d", clientpid, WSTOPSIG(status));
+
+	realclients--;
+    }
+}
+
+/* Remove shared memory and semaphores at exit */
+void 
+exitmaster() 
+{
+    if (master == 0)
+	return;
+	
+    switch (shmstat) {
+    case SIPC_ATTACHED:
+	if (shmdt(shm))
+	    warning("%s:%d: Internal error: control region could not be detached",
+		    __FILE__, __LINE__);
+
+	/* no break */
+    case SIPC_KEYED:
+	if (shmctl(shmid, IPC_RMID, NULL))
+	    warning("%s:%d: Internal error: could remove control region %d",
+		    __FILE__, __LINE__, shmid);
+	/* no break */
+    case SIPC_FREE:
+	break;
+
+    default:
+	warning("%s:%d: Internal error: control region is in an unknown state",
+		__FILE__, __LINE__);
+    }
+
+    switch (semstat) {
+    case SIPC_KEYED:
+	if (semctl(semid, 0, IPC_RMID, 0) != 0) 
+	    warning("%s:%d: Internal error: could not remove semaphore %d",
+		    __FILE__, __LINE__, semid);
+	/* no break */
+    case SIPC_FREE:
+	break;
+
+    default:
+	warning("%s:%d: Internal error: semaphore is in an unknown state",
+		__FILE__, __LINE__);
+    }
+}
+void 
+client_loop()
+{
+    int total;
+    int sent;
+    int written;
+
+    for (;;) { /* FOREVER */
+	
+	client_waitread();
+	
+	total = shared_getlen();
+	sent = 0;
+
+	while (sent < total) {
+	  
+	  if ((written = write(clientsock, (char *)(shared_getmem() + sent), total - sent)) == -1) {
+	    info("Write error. Client will quit.");
+	    exit(1);
+	  }
+	  
+	  sent += written;
+	  
+	  debug("client written %d bytes of %d total", sent, total);
+	}
+	
+	client_doneread();
+    }
+}

+ 67 - 0
mon/monmux/share.h

@@ -0,0 +1,67 @@
+/* $Id: share.h,v 1.3 2002/06/21 15:53:32 dijkstra Exp $ */
+
+/*
+ * Copyright (c) 2001-2002 Willem Dijkstra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/* TODO:
+ * Dynamically allocate buffer size 
+ * Check wether one buffer suffices, do some performance tests
+ */
+
+#ifndef _MONMUX_SHARE_H
+#define _MONMUX_SHARE_H
+
+#include "data.h"
+
+/* Share contains all routines needed for the ipc between monmuxes */
+#define SEM_WAIT     0              /* wait semaphore */
+#define SEM_READ     1              /* have read semaphore */
+#define SEM_TOTAL    2
+
+struct sharedregion {
+    long seqnr;
+    long reglen;                   /* size of buffer */
+    long ctlen;                    /* amount of content in buffer, assert(< size) */
+    long data;
+};
+
+/* prototypes */
+__BEGIN_DECLS
+void  master_forbidread();
+void  master_permitread();
+long  shared_getlen();
+long  shared_getmaxlen();
+long *shared_getmem();
+void  initshare();
+void  shared_setlen(long);
+void  spawn_client(int);
+__END_DECLS
+
+#endif /*_MONMUX_SHARE_H*/

+ 26 - 0
mon/ports/mon/Makefile

@@ -0,0 +1,26 @@
+# $OpenBSD: $
+
+COMMENT=		"active monitoring tool"
+
+DISTNAME=		mon-2.0
+PKGNAME=		mon-2.0
+CATEGORIES=		net sysutils
+NEED_VERSION=		1.502
+
+HOMEPAGE=		http://www.xs4all.nl/~wpd/mon/
+
+MAINTAINER=		Willem Dijkstra <wpd@xs4all.nl>
+
+PERMIT_PACKAGE_CDROM=	Yes
+PERMIT_PACKAGE_FTP=	Yes
+PERMIT_DISTFILES_CDROM=	Yes
+PERMIT_DISTFILES_FTP=	Yes
+
+MASTER_SITES=		http://www.xs4all.nl/~wpd/mon/
+
+LIB_DEPENDS=		rrd.0.0::net/rrdtool
+
+#DESTDIR=		/usr/local
+WRKDIST=		${WRKDIR}/mon
+
+.include <bsd.port.mk>

+ 3 - 0
mon/ports/mon/pkg/DESCR

@@ -0,0 +1,3 @@
+mon is a lightweight system monitor that measures cpu, memory, interface and disk st
+atistics every 5 seconds. This information is then spooled over "the network" to monmux for further processing.
+

+ 7 - 0
mon/ports/mon/pkg/MESSAGE

@@ -0,0 +1,7 @@
++---------------
+| Example configurations for both mon and monmux have been installed in
+| ${PREFIX}/share/mon.
+|
+| RRD files can be obtained by running
+| ${PREFIX}/share/mon/c_monrrds.sh
++---------------

+ 9 - 0
mon/ports/mon/pkg/PLIST

@@ -0,0 +1,9 @@
+@comment $OpenBSD$
+libexec/mon
+libexec/monmux
+man/cat8/mon.0
+man/cat8/monmux.0
+share/mon/c_monrrds.sh
+share/mon/mon.conf
+share/mon/monmux.conf
+@dirrm share/mon