v13i031: Remote statistics server

Rich Salz rsalz at bbn.com
Wed Feb 10 10:20:18 AEST 1988


Submitted-by: Dave Curry <davy at intrepid.ecn.purdue.edu>
Posting-number: Volume 13, Issue 31
Archive-name: rstat

This is a remote statistics server for BSD.  With it you can write
survey programs to collect statistics on things like load averages,
number of users, uptime, and general network statistics from remote
machines.  It implements the server described by Dave Mills in RFC996,
"Statistics Server", February 1987.

It has been tested on a Vax-11/780 running 4.3BSD, a Gould PN9080
running UTX/32 2.0 (4.3BSD), a Sun 3/180 and a Sun 3/50 running Sun
3.3 (4.2BSD), and a CCI 6/32 running 4.3BSD.  It should work equally
well under 4.2BSD.  (Note: on real new 4.3BSD, like on the CCI 6/32,
change "sys/dk.h" to "sys/dkstat.h" in getcpustats.c - I wish Berkeley
would make up their minds!)

# This is a shell archive.  Save this into a file, edit it
# and delete all lines above this comment.  Then give this
# file to sh by executing the command "sh file".  The files
# will be extracted into the current directory owned by
# you with default permissions.
#
# The files contained herein are:
#          README       Makefile        stats.1      statsrv.8
#         stats.h  dg_sendrecv.c  getcpustats.c getloadstats.c
#     getmsgbuf.c  getnetstats.cgettablestats.c gettimestats.c
#  getuserstats.c  st_sendrecv.c        stats.c      statsrv.c
#          tester            rfc
#
echo 'x - README'
sed 's/^X//' <<'________This_Is_The_END________' >>README
XThis is a remote statistics server as described in RFC996, "Statistics
XServer", D. L. Mills, February 1987.  With it, you can collect things
Xlike load averages, number of users, uptime, and network statistics
Xfrom remote machines easily.
X
XIt has been tested on a Vax-11/780 running 4.3BSD, a Gould PN9080
Xrunning UTX/32 2.0 (4.3BSD), a Sun 3/180 and a Sun 3/50 running Sun
X3.3 (4.2BSD), a CCI 6/32 running 4.3BSD, and a Sequent running Dynix.
XIt should work equally well under 4.2BSD.  (Note: on real new 4.3BSD,
Xlike on the CCI 6/32, change "sys/dk.h" to "sys/dkstat.h" in
Xgetcpustats.c - I wish Berkeley would make up their minds!)
X
XOn Suns, you will want to edit "Makefile" and add "-fswitch" to CFLAGS,
Xand change the install for "statsrv" to put it in "/usr/etc/in.statsrv".
XAfter you have made and installed the programs, edit "/etc/inetd.conf"
Xand add the lines:
X
Xstatsrv	stream	tcp	nowait	root	/etc/statsrv	statsrv
Xstatsrv	dgram	udp	wait	root	/etc/statsrv	statsrv
X
XThen kill -1 inetd.  On Suns, instead of "/etc/inetd.conf", edit
X"/etc/servers" and add the lines:
X
Xstatsrv	tcp	/usr/etc/in.statsrv
Xstatsrv	udp	/usr/etc/in.statsrv
X
XThen kill inetd and restart it.
X
XIf you don't keep up with the NIC, you may need to add the following
Xlines to "/etc/services":
X
Xstatsrv		133/tcp		# statistics server
Xstatsrv		133/udp
X
XGood luck.
X
XDave Curry
XPurdue University
XEngineering Computer Network
Xdavy at intrepid.ecn.purdue.edu
X{ihnp4, rutgers, iuvax}!pur-ee!davy
________This_Is_The_END________
echo 'x - Makefile'
sed 's/^X//' <<'________This_Is_The_END________' >>Makefile
X#
X# $Header: /ecn1/src/ecn/statsrv/RCS/Makefile,v 1.4 88/01/02 12:45:37 davy Exp $
X#
X# Makefile for stats and statsrv.
X#
X# David A. Curry
X# Purdue University
X# Engineering Computer Network
X# davy at intrepid.ecn.purdue.edu
X# October, 1987
X#
X# $Log:	Makefile,v $
X# Revision 1.4  88/01/02  12:45:37  davy
X# Added getmsgbuf.c.
X# 
X# Revision 1.3  87/12/08  14:39:43  davy
X# Added lines for gettablestats.c.
X# 
X# Revision 1.2  87/10/29  14:23:50  davy
X# Updated dependencies.
X# 
X# Revision 1.1  87/10/17  21:00:57  davy
X# Initial revision
X# 
X#
XCFLAGS=	-O
XDESTDIR=
X
XSTATS=	stats.o dg_sendrecv.o st_sendrecv.o
XSTATSRV=statsrv.o dg_sendrecv.o st_sendrecv.o getcpustats.o getloadstats.o \
X	getmsgbuf.o getnetstats.o gettablestats.o gettimestats.o getuserstats.o
X
Xall: stats statsrv
X
Xstats: $(STATS)
X	$(CC) $(CFLAGS) -o stats $(STATS)
X
Xstatsrv: $(STATSRV)
X	$(CC) $(CFLAGS) -o statsrv $(STATSRV)
X
Xinstall: all
X	install -c -s stats $(DESTDIR)/usr/ecn/stats
X	install -c -s statsrv $(DESTDIR)/etc/statsrv
X
Xtags:
X	@ctags *.c > tags
X
Xclean:
X	rm -f a.out core *.o
X
Xzap: clean
X	rm -f stats statsrv
X
Xstats.o: stats.c stats.h
Xgetmsgbuf.o: getmsgbuf.c stats.h
Xdg_sendrecv.o: dg_sendrecv.c stats.h
Xst_sendrecv.o: st_sendrecv.c stats.h
Xgetcpustats.o: getcpustats.c stats.h
Xgetnetstats.o: getnetstats.c
Xgetloadstats.o: getloadstats.c stats.h
Xgettimestats.o: gettimestats.c stats.h
Xgetuserstats.o: getuserstats.c stats.h
Xgettablestats.o: gettablestats.c
________This_Is_The_END________
echo 'x - stats.1'
sed 's/^X//' <<'________This_Is_The_END________' >>stats.1
X.TH STATS 1 "17 October 1987" Purdue-ECN
X.SH NAME
Xstats \- gather remote statistics
X.SH SYNOPSIS
X.B stats
X[
X.B \-d
X]
X.I hostname
X.I statname
X[
X.IR statname ...
X]
X.SH DESCRIPTION
X.PP
X.B Stats
Xis used to gather statistics from remote hosts by connecting to a
Xstatistics server on internet port 133 (see
X.BR statsrv (8)).
XStatistics names are based on the remote host's command language,
Xtry using the ``help'' statistic to get a list.
X.PP
XBy default,
X.B stats
Xuses a stream (\s-1TCP\s0) connection;
Xif the
X.B \-d
Xflag is given as the first argument,
Xdatagrams (\s-1UDP\s0) will be used instead.
XNot all statistics servers support stream connections.
X.SH SEE ALSO
X.BR statsrv (8)
X.br
X\s-1RFC996\s0,
X.IR "Statistics Server" ,
XD. L. Mills,
XFebruary 1987.
X.SH AUTHOR
XDavid A. Curry,
XPurdue University Engineering Computer Network
________This_Is_The_END________
echo 'x - statsrv.8'
sed 's/^X//' <<'________This_Is_The_END________' >>statsrv.8
X.TH STATSRV 8 "8 December 1987" Purdue-ECN
X.SH NAME
Xstatsrv \- statistics server
X.SH SYNOPSIS
X.B statsrv
X.SH DESCRIPTION
X.PP
X.B Statsrv
Xis invoked by
X.BR inetd (8)
Xwhen connections or datagrams arrive on internet port 133.
XIt understands both stream (\s-1TCP\s0) and datagram (\s-1UDP\s0)
Xconnections.
X.PP
X.B Statsrv
Xreads null-terminated strings requesting statistics,
Xand returns null-terminated strings suitable for printing on a terminal
Xor line printer.
XThe special statistic name ``help'' may be sent to request a list of
Xknown commands.
XAs of this writing,
Xthe known commands are:
X.IP \fBactusers\fP \w'boottimeXX'u
XThe number of active users (those users idle less than one hour)
Xis returned.
X.IP \fBboottime\fP \w'boottimeXX'u
XThe time the system was last booted is returned.
X.IP \fBcpu\fP \w'boottimeXX'u
XThe cpu utilization,
Xbroken down into percentages of user,
Xnice,
Xsystem,
Xand idle time,
Xis returned.
X.IP \fBdate\fP \w'boottimeXX'u
XThe current date is returned.
X.IP \fBhelp\fP \w'boottimeXX'u
XA list of known commands,
Xseparated by newlines,
Xis returned.
X.IP \fBloadav\fP \w'boottimeXX'u
XThe one-minute load average,
Xan average of the number of runnable jobs,
Xis returned.
X.IP \fBmbufs\fP \w'boottimeXX'u
XA summary of network buffer (mbufs) utilization is returned.
XThis is the output from
X.BR "netstat \-m" .
X.IP \fBmsgbuf\fP \w'boottimeXX'u
XThe contents of the system message buffer,
Xwhich holds the most recent messages printed on the console,
Xit returned.
XThis statistic is almost always too long to fit into a datagram.
X.IP \fBmsgtail\fP \w'boottimeXX'u
XThe last fifteen lines of the system message buffer are returned.
XThis statistic,
Xunlike
X.B msgbuf ,
Xwill usually fit in a datagram.
X.IP \fBpackets\fP \w'boottimeXX'u
XA summary of input and output packets on the network interfaces is
Xreturned.
XThis is the output from
X.BR "netstat \-i" .
X.IP \fBproto\fP \w'boottimeXX'u
XA summary of various network protocol information is returned.
XThis is the output from
X.BR "netstat -s" .
XUsually,
Xthis statistic is too long to fit into a datagram.
X.IP \fBtables\fP \w'boottimeXX'u
XA summary of the utilization of various internal system tables.
XThis is the output from
X.BR "pstat -T" .
X.IP \fBtime\fP \w'boottimeXX'u
XThe current time is returned.
X.IP \fBuptime\fP \w'boottimeXX'u
XThe amount of time the system has been up is returned.
X.IP \fBusers\fP \w'boottimeXX'u
XThe number of users logged in is returned.
X.IP \fBwho\fP \w'boottimeXX'u
XA list of all logged in users.
XThis list is contained on a single line,
Xwith login names separated from each other by a space character.
XOccasionally the output from this statistic will be too long to fit
Xinto a datagram.
X.SH SEE ALSO
X.BR netstat (1),
X.BR stats (1)
X.br
X\s-1RFC996\s0,
X.IR "Statistics Server" ,
XD. L. Mills,
XFebruary 1987
X.SH AUTHOR
XDavid A. Curry,
XPurdue University Engineering Computer Network
________This_Is_The_END________
echo 'x - stats.h'
sed 's/^X//' <<'________This_Is_The_END________' >>stats.h
X/*
X * $Header: /ecn1/src/ecn/statsrv/RCS/stats.h,v 1.2 87/12/17 14:54:27 davy Exp $
X *
X * stats.h - definitions for statistics server
X *
X * David A. Curry
X * Purdue University
X * Engineering Computer Network
X * davy at intrepid.ecn.purdue.edu
X * October, 1987
X *
X * $Log:	stats.h,v $
X * Revision 1.2  87/12/17  14:54:27  davy
X * Added defines for VMUNIX and KMEM.
X * 
X * Revision 1.1  87/10/17  21:01:03  davy
X * Initial revision
X * 
X */
X
X#define SERVNAME	"statsrv"	/* name of our service		*/
X#define MAXDGRAM	576		/* maximum size of a datagram	*/
X
X#define KMEM		"/dev/kmem"	/* path to kernel memory	*/
X
X#if vax || sun || gould || tahoe
X#define VMUNIX		"/vmunix"	/* path to kernel		*/
X#endif
X#if sequent
X#define VMUNIX		"/dynix"
X#endif
X
X/*
X * For 4.2BSD syslogs.
X */
X#ifndef LOG_DAEMON
X#define LOG_DAEMON	(3<<3)
X#endif
X
Xextern int st_send(), st_recv();	/* stream send/recv functions	*/
Xextern int dg_send(), dg_recv();	/* datagram send/recv functions	*/
________This_Is_The_END________
echo 'x - dg_sendrecv.c'
sed 's/^X//' <<'________This_Is_The_END________' >>dg_sendrecv.c
X#ifndef lint
Xstatic char *RCSid = "$Header: /ecn1/src/ecn/statsrv/RCS/dg_sendrecv.c,v 1.1 87/10/17 21:01:12 davy Exp $";
X#endif
X/*
X * dg_sendrecv.c - datagram send/recv functions
X *
X * David A. Curry
X * Purdue University
X * Engineering Computer Network
X * davy at intrepid.ecn.purdue.edu
X * October, 1987
X *
X * $Log:	dg_sendrecv.c,v $
X * Revision 1.1  87/10/17  21:01:12  davy
X * Initial revision
X * 
X */
X#include <sys/param.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <syslog.h>
X#include <stdio.h>
X
X#include "stats.h"
X
Xextern char	*pname;			/* program name			*/
Xextern short	server;			/* 1 if server, 0 if client	*/
Xextern struct 	sockaddr_in sin;	/* address of remote host	*/
X
X/*
X * dg_send - send buf as a datagram to the address in sin~r on socket s.
X */
Xdg_send(s, buf)
Xchar *buf;
Xint s;
X{
X	register int cnt;
X
X	/*
X	 * We want the length including the null.
X	 */
X	cnt = strlen(buf) + 1;
X
X	/*
X	 * According to RFC996, can be no larger than MAXDGRAM.
X	 */
X	if (cnt > MAXDGRAM) {
X		if (server) {
X			strcpy(buf, "output length too long for datagram.\n");
X			cnt = strlen(buf) + 1;
X		}
X		else {
X			fprintf(stderr, "%s: string too long for datagram.\n", pname);
X			exit(1);
X		}
X	}
X
X	/*
X	 * Send the datagram.
X	 */
X	if (sendto(s, buf, cnt, 0, &sin, sizeof(struct sockaddr_in)) < 0) {
X		if (server)
X			syslog(LOG_ERR, "sendto: %m");
X		else
X			error("sendto");
X		exit(1);
X	}
X}
X
X/*
X * dg_recv - receive a datagram of maximum size cnt into buf from the address
X *	     in sin on socket s.
X */
Xdg_recv(s, buf, cnt)
Xint s, cnt;
Xchar *buf;
X{
X	int len;
X
X	len = sizeof(struct sockaddr_in);
X
X	/*
X	 * Receive the datagram.
X	 */
X	if (recvfrom(s, buf, cnt, 0, &sin, &len) < 0) {
X		if (server)
X			syslog(LOG_ERR, "recvfrom: %m");
X		else
X			error("recvfrom");
X		exit(1);
X	}
X}
________This_Is_The_END________
echo 'x - getcpustats.c'
sed 's/^X//' <<'________This_Is_The_END________' >>getcpustats.c
X#ifndef lint
Xstatic char *RCSid = "$Header: /ecn1/src/ecn/statsrv/RCS/getcpustats.c,v 1.4 88/01/02 14:09:57 davy Exp $";
X#endif
X/*
X * getcpustats - get cpu usage statistics
X *
X * David A. Curry
X * Purdue University
X * Engineering Computer Network
X * davy at intrepid.ecn.purdue.edu
X * October, 1987
X *
X * $Log:	getcpustats.c,v $
X * Revision 1.4  88/01/02  14:09:57  davy
X * Changes to support Sequents.
X * 
X * Revision 1.3  88/01/02  13:41:56  davy
X * Changed dk.h to dkstat.h for the CCI, which has newer 4.3BSD.
X * 
X * Revision 1.2  87/10/29  14:24:02  davy
X * Modified to use VMUNIX and KMEM instead of hard-coded paths.
X * 
X * Revision 1.1  87/10/17  21:01:18  davy
X * Initial revision
X * 
X */
X#include <sys/param.h>
X#include <sys/file.h>
X#if sequent
X#include <sys/time.h>
X#include <sys/vmmeter.h>
X#endif
X#if tahoe
X#include <sys/dkstat.h>
X#else
X#include <sys/dk.h>
X#endif
X#include <syslog.h>
X#include <nlist.h>
X#include <stdio.h>
X#include "stats.h"
X
Xstatic struct nlist nl[] = {
X#define X_CPTIME	0
X	{	"_cp_time"	},
X	{	0		}
X};
X
Xextern	int (*fn_recv)(), (*fn_send)();	/* send/recv functions		*/
X
X/*
X * getcpustats - get cpu usage statistics
X */
Xgetcpustats(name)
Xchar *name;
X{
X	int kmem;
X	double f;
X	register int i;
X	double percent();
X	char buf[BUFSIZ];
X	int user, nice, sys, idle;
X	long times1[CPUSTATES], times2[CPUSTATES];
X
X	/*
X	 * Open kernel memory.
X	 */
X	if ((kmem = open(KMEM, O_RDONLY)) < 0) {
X		syslog(LOG_ERR, "open: %s: %m", KMEM);
X		exit(1);
X	}
X
X	/*
X	 * Read kernel namelist.
X	 */
X	if ((nlist(VMUNIX, nl) < 0) || (nl[0].n_type == 0)) {
X		syslog(LOG_ERR, "%s: no namelist", VMUNIX);
X		exit(1);
X	}
X
X	/*
X	 * Read the first set of usage stats.
X	 */
X	lseek(kmem, (long) nl[X_CPTIME].n_value, L_SET);
X	read(kmem, (char *) times1, sizeof(times1));
X
X	/*
X	 * Give it a time interval.
X	 */
X	sleep(1);
X
X	/*
X	 * Read the second set of usage stats.
X	 */
X	lseek(kmem, (long) nl[X_CPTIME].n_value, L_SET);
X	read(kmem, (char *) times2, sizeof(times2));
X
X	/*
X	 * Calculate change.
X	 */
X	for (i=0; i < CPUSTATES; i++)
X		times2[i] -= times1[i];
X
X	/*
X	 * Calculate times.
X	 */
X	for (i=0; i < CPUSTATES; i++) {
X		f = percent(i, times2);
X
X		/*
X		 * Save results.  This is risky, and assumes the order
X		 * is as given.  Check sys/dk.h if you think it's wrong
X		 * on your system.
X		 */
X		switch (i) {
X		case 0:
X			user = (int) f;
X			break;
X		case 1:
X			nice = (int) f;
X			break;
X		case 2:
X			sys = (int) f;
X			break;
X		case 3:
X			idle = (int) f;
X			break;
X		}
X	}
X
X	sprintf(buf, "user %d%% nice %d%% sys %d%% idle %d%%\n", user, nice, sys, idle);
X	(*fn_send)(0, buf);
X	close(kmem);
X}
X
X/*
X * percent - figure what percentage of time is used by row.
X */
Xstatic double percent(row, times)
Xlong *times;
Xint row;
X{
X	double t;
X	register int i;
X
X	t = 0.0;
X	for (i=0; i < CPUSTATES; i++)
X		t += times[i];
X
X	if (t == 0.0)
X		t = 1.0;
X
X	return(times[row] * 100.0 / t);
X}
________This_Is_The_END________
echo 'x - getloadstats.c'
sed 's/^X//' <<'________This_Is_The_END________' >>getloadstats.c
X#ifndef lint
Xstatic char *RCSid = "$Header: /ecn1/src/ecn/statsrv/RCS/getloadstats.c,v 1.3 88/01/02 14:09:10 davy Exp $";
X#endif
X/*
X * getloadstats - get load average statistics
X *
X * David A. Curry
X * Purdue University
X * Engineering Computer Network
X * davy at intrepid.ecn.purdue.edu
X * October, 1987
X *
X * $Log:	getloadstats.c,v $
X * Revision 1.3  88/01/02  14:09:10  davy
X * Changes for Sequent support.
X * 
X * Revision 1.2  87/10/29  14:24:54  davy
X * Modified to use VMUNIX and KMEM instead of hard-coded paths.
X * 
X * Revision 1.1  87/10/17  21:01:23  davy
X * Initial revision
X * 
X */
X#include <sys/param.h>
X#include <sys/file.h>
X#include <syslog.h>
X#include <nlist.h>
X#include <stdio.h>
X#include "stats.h"
X
Xstatic struct nlist nl[] = {
X#define X_AVENRUN	0
X	{	"_avenrun"	},
X	{	0		}
X};
X
Xextern	int (*fn_recv)(), (*fn_send)();	/* send/recv functions		*/
X
X/*
X * getloadstats - get load average statistics
X */
Xgetloadstats(name)
Xchar *name;
X{
X	int kmem;
X	char buf[BUFSIZ];
X#if sun || sequent
X	long avenrun[3];
X#else
X	double avenrun[3];
X#endif
X
X	/*
X	 * Open kernel memory.
X	 */
X	if ((kmem = open(KMEM, O_RDONLY)) < 0) {
X		syslog(LOG_ERR, "open: %s: %m", KMEM);
X		exit(1);
X	}
X
X	/*
X	 * Read kernel namelist.
X	 */
X	if ((nlist(VMUNIX, nl) < 0) || (nl[0].n_type == 0)) {
X		syslog(LOG_ERR, "%s: no namelist", VMUNIX);
X		exit(1);
X	}
X
X	/*
X	 * Read the load averages.
X	 */
X	lseek(kmem, (long) nl[X_AVENRUN].n_value, L_SET);
X	read(kmem, (char *) avenrun, sizeof(avenrun));
X
X	/*
X	 * Return the one-minute load average.
X	 */
X#if sun
X	sprintf(buf, "%.2f\n", (double) avenrun[0] / FSCALE);
X#endif
X#if sequent
X	sprintf(buf, "%.2f\n", (double) avenrun[0] / 1000.0);
X#endif
X#if !(sun || sequent)
X	sprintf(buf, "%.2f\n", avenrun[0]);
X#endif
X
X	(*fn_send)(0, buf);
X	close(kmem);
X}
________This_Is_The_END________
echo 'x - getmsgbuf.c'
sed 's/^X//' <<'________This_Is_The_END________' >>getmsgbuf.c
X#ifndef lint
Xstatic char *RCSid = "$Header: /ecn1/src/ecn/statsrv/RCS/getmsgbuf.c,v 1.3 88/01/02 15:20:52 davy Exp $";
X#endif
X/*
X * getmsgbuf - get the message buffer from the kernel
X *
X * David A. Curry
X * Purdue University
X * Engineering Computer Network
X * davy at intrepid.ecn.purdue.edu
X * January, 1988
X *
X * $Log:	getmsgbuf.c,v $
X * Revision 1.3  88/01/02  15:20:52  davy
X * Modified to work on Sequents.
X * 
X * Revision 1.2  88/01/02  13:31:23  davy
X * Fixes for stupid vendors like Gould who can't leave Berkeley well
X * enough alone.
X * 
X * Revision 1.1  88/01/02  12:45:57  davy
X * Initial revision
X * 
X */
X#include <sys/param.h>
X#if sequent
X#include <sys/ioctl.h>
X#include <sec/sec.h>
X#else
X#include <sys/msgbuf.h>
X#endif
X#include <sys/file.h>
X#include <syslog.h>
X#include <nlist.h>
X#include <stdio.h>
X#include "stats.h"
X
Xstatic struct nlist nl[] = {
X#define X_MSGBUF	0
X#if vax || sun || tahoe
X	{	"_msgbuf"	},
X#endif
X#if GOULD_PN
X	{	"_cmsgbuf"	},
X#endif
X#if GOULD_NP1
X	{	"_cmsgbufloc"	},
X#endif
X	{	0		}
X};
X
Xextern int (*fn_recv)(), (*fn_send)(); /* send/recv functions		*/
X
X/*
X * getmsgbuf - get the message buffer from the kernel
X */
Xgetmsgbuf(name)
Xchar *name;
X{
X	int kmem;
X	int sawnl, ignore;
X	register char *s, *t;
X#if sequent
X	int len;
X	char *malloc();
X	struct sec_mem sm;
X	char *buf, *tmpbuf, *bufstart;
X#else
X	struct msgbuf msgbuf;
X	char buf[MSG_BSIZE+1];
X#endif
X#if GOULD_NP1
X	struct msgbuf *cmsgbufloc;
X#endif
X
X#if sequent
X	/*
X	 * Sequent doesn't use a message buffer; instead they
X	 * read stuff out of the SCSI console device.  Yuck.
X	 */
X	if ((kmem = open("/dev/smemco", O_RDONLY)) < 0) {
X		syslog(LOG_ERR, "open: /dev/smemco: %m");
X		exit(1);
X	}
X
X	/*
X	 * Find out the size of stuff.
X	 */
X	if (ioctl(kmem, SMIOGETLOG, &sm) < 0) {
X		syslog(LOG_ERR, "ioctl: SMIOGETLOG: %m");
X		exit(1);
X	}
X
X	/*
X	 * Get some memory.
X	 */
X	if ((tmpbuf = malloc(sm.mm_size)) == NULL) {
X		syslog(LOG_ERR, "cannot allocate %d bytes of memory.", sm.mm_size);
X		exit(1);
X	}
X
X	if ((buf = malloc(sm.mm_size+2)) == NULL) {
X		syslog(LOG_ERR, "cannot allocate %d bytes of memory.", sm.mm_size);
X		exit(1);
X	}
X
X	/*
X	 * Read the console information.
X	 */
X	lseek(kmem, (long) sm.mm_buffer, L_SET);
X	read(kmem, (char *) tmpbuf, sm.mm_size);
X
X	bufstart = &tmpbuf[(int) sm.mm_nextchar - (int) sm.mm_buffer];
X	s = bufstart;
X	t = buf;
X
X	sawnl = 1;
X	ignore = 0;
X
X	/*
X	 * Copy the info.  It's a circular buffer, hence all
X	 * this stuff.
X	 */
X	do {
X		if ((sawnl == 1) && (*s == '<'))
X			ignore = 1;
X		if ((*s != NULL) && ((*s & 0200) == 0) && (ignore == 0))
X			*t++ = *s;
X		if ((ignore == 1) && (*s == '>'))
X			ignore = 0;
X
X		sawnl = (*s++ == '\n');
X
X		if (s >= &tmpbuf[sm.mm_size])
X			s = tmpbuf;
X	} while (s != bufstart);
X
X	*t = NULL;
X
X#else sequent
X
X	/*
X	 * Open kmem.
X	 */
X	if ((kmem = open(KMEM, O_RDONLY)) < 0) {
X		syslog(LOG_ERR, "open: %s: %m", KMEM);
X		exit(1);
X	}
X
X	/* 
X	 * Read kernel namelist.
X	 */
X	if ((nlist(VMUNIX, nl) < 0) || (nl[0].n_type == 0)) {
X		syslog(LOG_ERR, "%s: no namelist", VMUNIX);
X		exit(1);
X	}
X
X	lseek(kmem, (long) nl[X_MSGBUF].n_value, L_SET);
X
X#if GOULD_NP1
X	/*
X	 * For some reason, Gould has made _cmsgbufloc a pointer
X	 * to msgbuf.  Yeeesh.
X	 */
X	read(kmem, (char *) &cmsgbufloc, sizeof(cmsgbufloc));
X	lseek(kmem, (long) cmsgbufloc, L_SET);
X#endif GOULD_NP1
X
X	read(kmem, (char *) &msgbuf, sizeof(msgbuf));
X
X	if (msgbuf.msg_magic != MSG_MAGIC) {
X		syslog(LOG_ERR, "msgbuf magic number mismatch.");
X		exit(1);
X	}
X
X	if ((msgbuf.msg_bufx < 0) || (msgbuf.msg_bufx >= MSG_BSIZE))
X		msgbuf.msg_bufx = 0;
X
X	s = &msgbuf.msg_bufc[msgbuf.msg_bufx];
X	t = buf;
X
X	sawnl = 1;
X	ignore = 0;
X
X	/*
X	 * Copy the message buffer.  It's a circular buffer,
X	 * hence all this.
X	 */
X	do {
X		if ((sawnl == 1) && (*s == '<'))
X			ignore = 1;
X		if ((*s != NULL) && ((*s & 0200) == 0) && (ignore == 0))
X			*t++ = *s;
X		if ((ignore == 1) && (*s == '>'))
X			ignore = 0;
X
X		sawnl = (*s++ == '\n');
X
X		if (s >= &msgbuf.msg_bufc[MSG_BSIZE])
X			s = msgbuf.msg_bufc;
X	} while (s != &msgbuf.msg_bufc[msgbuf.msg_bufx]);
X
X	*t = NULL;
X#endif sequent
X
X	/*
X	 * If they asked for "msgtail" then only give them
X	 * the last fifteen lines of the message buffer.
X	 * Otherwise, give them the whole thing.
X	 */
X	if (!strcmp(name, "msgtail")) {
X		for (sawnl = 0; (t >= buf) && (sawnl < 16); t--) {
X			if (*t == '\n')
X				sawnl++;
X		}
X
X		t += 2;
X	}
X	else {
X#if sequent
X		/*
X		 * Sequents save HUGE amounts of stuff!
X		 */
X		len = strlen(buf);
X
X		if (len > 4096) {
X			t = &buf[len - 4096];
X			while ((*t != NULL) && (*t != '\n'))
X				t++;
X			t++;
X		}
X		else
X#endif
X		t = buf;
X	}
X
X	(*fn_send)(0, t);
X	close(kmem);
X
X#if sequent
X	free(tmpbuf);
X	free(buf);
X#endif
X}
________This_Is_The_END________
echo 'x - getnetstats.c'
sed 's/^X//' <<'________This_Is_The_END________' >>getnetstats.c
X#ifndef lint
Xstatic char *RCSid = "$Header: /ecn1/src/ecn/statsrv/RCS/getnetstats.c,v 1.1 87/10/17 21:01:29 davy Exp $";
X#endif
X/*
X * getnetstats - get network statistics
X *
X * David A. Curry
X * Purdue University
X * Engineering Computer Network
X * davy at intrepid.ecn.purdue.edu
X * October, 1987
X *
X * $Log:	getnetstats.c,v $
X * Revision 1.1  87/10/17  21:01:29  davy
X * Initial revision
X * 
X */
X#include <sys/param.h>
X#include <syslog.h>
X#include <stdio.h>
X
X#define NETSTAT		"/usr/ucb/netstat"
X
Xextern	int (*fn_recv)(), (*fn_send)();	/* send/recv functions		*/
X
X/*
X * getnetstats - get network statistic
X */
Xgetnetstats(name)
Xchar *name;
X{
X	int pid;
X	int pf[2];
X	char *flag;
X	register int cnt;
X	char buf[10 * BUFSIZ];
X	register char *s, *ebuf;
X
X	/*
X	 * Different stats get different flags.
X	 */
X	if (!strcmp(name, "mbufs"))
X		flag = "-m";
X	else if (!strcmp(name, "packets"))
X		flag = "-i";
X	else if (!strcmp(name, "proto"))
X		flag = "-s";
X
X	if (pipe(pf) < 0) {
X		syslog(LOG_ERR, "pipe: %m");
X		exit(1);
X	}
X
X	/*
X	 * Start a child.
X	 */
X	if ((pid = vfork()) < 0) {
X		syslog(LOG_ERR, "fork: %m");
X		exit(1);
X	}
X
X	/*
X	 * Run the command.
X	 */
X	if (pid == 0) {
X		close(pf[0]);
X
X		dup2(pf[1], 1);
X		close(pf[1]);
X
X		execl(NETSTAT, "netstat", flag, 0);
X		syslog(LOG_ERR, "exec: %m");
X		exit(1);
X	}
X
X	close(pf[1]);
X
X	/*
X	 * Read from the pipe.
X	 */
X	s = buf;
X	ebuf = &buf[sizeof(buf) - 1];
X	while ((cnt = read(pf[0], s, (int) (ebuf - s))) > 0) {
X		s += cnt;
X
X		if (s > ebuf) {
X			syslog(LOG_ERR, "output from %s %s too big", NETSTAT, flag);
X			s = ebuf;
X			break;
X		}
X	}
X
X	*s = '\0';
X	(*fn_send)(0, buf);
X
X	close(pf[0]);
X
X	/*
X	 * Pick up the child.
X	 */
X	while (wait((int *) 0) >= 0)
X		;
X}
________This_Is_The_END________
echo 'x - gettablestats.c'
sed 's/^X//' <<'________This_Is_The_END________' >>gettablestats.c
X#ifndef lint
Xstatic char *RCSid = "$Header: /ecn1/src/ecn/statsrv/RCS/gettablestats.c,v 1.1 87/12/08 14:40:03 davy Exp $";
X#endif
X/*
X * gettablestats - get internal table statistics
X *
X * David A. Curry
X * Purdue University
X * Engineering Computer Network
X * davy at intrepid.ecn.purdue.edu
X * December, 1987
X *
X * $Log:	gettablestats.c,v $
X * Revision 1.1  87/12/08  14:40:03  davy
X * Initial revision
X * 
X */
X#include <sys/param.h>
X#include <syslog.h>
X#include <stdio.h>
X
X#define PSTAT		"/etc/pstat"
X
Xextern	int (*fn_recv)(), (*fn_send)();	/* send/recv functions		*/
X
X/*
X * gettablestats - get internal table statistics
X */
Xgettablestats(name)
Xchar *name;
X{
X	int pid;
X	int pf[2];
X	char *flag;
X	register int cnt;
X	char buf[10 * BUFSIZ];
X	register char *s, *ebuf;
X
X	/*
X	 * Different stats get different flags.
X	 */
X	if (!strcmp(name, "tables"))
X		flag = "-T";
X
X	if (pipe(pf) < 0) {
X		syslog(LOG_ERR, "pipe: %m");
X		exit(1);
X	}
X
X	/*
X	 * Start a child.
X	 */
X	if ((pid = vfork()) < 0) {
X		syslog(LOG_ERR, "fork: %m");
X		exit(1);
X	}
X
X	/*
X	 * Run the command.
X	 */
X	if (pid == 0) {
X		close(pf[0]);
X
X		dup2(pf[1], 1);
X		close(pf[1]);
X
X		execl(PSTAT, "pstat", flag, 0);
X		syslog(LOG_ERR, "exec: %m");
X		exit(1);
X	}
X
X	close(pf[1]);
X
X	/*
X	 * Read from the pipe.
X	 */
X	s = buf;
X	ebuf = &buf[sizeof(buf) - 1];
X	while ((cnt = read(pf[0], s, (int) (ebuf - s))) > 0) {
X		s += cnt;
X
X		if (s > ebuf) {
X			syslog(LOG_ERR, "output from %s %s too big", PSTAT, flag);
X			s = ebuf;
X			break;
X		}
X	}
X
X	*s = '\0';
X	(*fn_send)(0, buf);
X
X	close(pf[0]);
X
X	/*
X	 * Pick up the child.
X	 */
X	while (wait((int *) 0) >= 0)
X		;
X}
________This_Is_The_END________
echo 'x - gettimestats.c'
sed 's/^X//' <<'________This_Is_The_END________' >>gettimestats.c
X#ifndef lint
Xstatic char *RCSid = "$Header: /ecn1/src/ecn/statsrv/RCS/gettimestats.c,v 1.3 87/10/29 15:03:57 davy Exp $";
X#endif
X/*
X * gettimestats - get time-related statistics
X *
X * David A. Curry
X * Purdue University
X * Engineering Computer Network
X * davy at intrepid.ecn.purdue.edu
X * October, 1987
X *
X * $Log:	gettimestats.c,v $
X * Revision 1.3  87/10/29  15:03:57  davy
X * Fixed bug with timezone printing.
X * 
X * Revision 1.2  87/10/29  14:25:03  davy
X * Modified to use VMUNIX and KMEM instead of hard-coded paths.
X * 
X * Revision 1.1  87/10/17  21:01:34  davy
X * Initial revision
X * 
X */
X#include <sys/param.h>
X#include <sys/file.h>
X#include <sys/time.h>
X#include <syslog.h>
X#include <nlist.h>
X#include <stdio.h>
X#include "stats.h"
X
Xstatic struct nlist nl[] = {
X#define X_BOOTTIME	0
X	{	"_boottime"	},
X	{	0		}
X};
X
Xextern	int (*fn_recv)(), (*fn_send)();	/* send/recv functions		*/
X
X/*
X * gettimestats - get time-related statistics
X */
Xgettimestats(name)
Xchar *name;
X{
X	int kmem;
X	struct timezone tz;
X	struct timeval curtime, boottime;
X
X	/*
X	 * Get current date and time.
X	 */
X	gettimeofday(&curtime, &tz);
X
X	/*
X	 * If we're doing "date" or "time", we just print
X	 * out the current date and time.
X	 */
X	if (!strcmp(name, "date") || !strcmp(name, "time")) {
X		prdate(&curtime, &tz);
X		return;
X	}
X
X	/*
X	 * Open kernel memory.
X	 */
X	if ((kmem = open(KMEM, O_RDONLY)) < 0) {
X		syslog(LOG_ERR, "open: %s: %m", KMEM);
X		exit(1);
X	}
X
X	/*
X	 * Read kernel namelist.
X	 */
X	if ((nlist(VMUNIX, nl) < 0) || (nl[0].n_type == 0)) {
X		syslog(LOG_ERR, "%s: no namelist", VMUNIX);
X		exit(1);
X	}
X
X	/*
X	 * Read the boot time.
X	 */
X	lseek(kmem, (long) nl[X_BOOTTIME].n_value, L_SET);
X	read(kmem, (char *) &boottime, sizeof(boottime));
X
X	/*
X	 * For "boottime" we just print the boot time, for
X	 * "uptime" we print the time up.
X	 */
X	if (!strcmp(name, "boottime")) {
X		prdate(&boottime, &tz);
X	}
X	else {
X		curtime.tv_sec -= boottime.tv_sec;
X		prtime(&curtime);
X	}
X
X	close(kmem);
X}
X
X/*
X * prdate - print date and time
X */
Xstatic prdate(tv, tz)
Xstruct timezone *tz;
Xstruct timeval *tv;
X{
X	char buf[64];
X	struct tm *tm;
X	struct tm *localtime();
X	register char *ap, *tzn;
X	char *asctime(), *timezone();
X
X	tm = localtime(&tv->tv_sec);
X
X	ap = asctime(tm);
X	tzn = timezone(tz->tz_minuteswest, tm->tm_isdst);
X
X	/*
X	 * Date and time.
X	 */
X	strncpy(buf, ap, 20);
X	buf[20] = '\0';
X
X	/*
X	 * Timezone.
X	 */
X	if (tzn)
X		strcat(buf, tzn);
X
X	/*
X	 * Year.
X	 */
X	strcat(buf, ap+19);
X
X	(*fn_send)(0, buf);
X}
X
X/*
X * prtime - print time as days, hours, minutes, seconds.
X */
Xstatic prtime(tv)
Xstruct timeval *tv;
X{
X	char tmp[32], buf[128];
X
X	*buf = '\0';
X
X	if (tv->tv_sec >= 86400) {
X		sprintf(tmp, "%d days ", tv->tv_sec / 86400);
X		tv->tv_sec %= 86400;
X		strcat(buf, tmp);
X	}
X
X	if (tv->tv_sec >= 3600) {
X		sprintf(tmp, "%d hours ", tv->tv_sec / 3600);
X		tv->tv_sec %= 3600;
X		strcat(buf, tmp);
X	}
X
X	if (tv->tv_sec >= 60) {
X		sprintf(tmp, "%d minutes ", tv->tv_sec / 60);
X		tv->tv_sec %= 60;
X		strcat(buf, tmp);
X	}
X
X	if (tv->tv_sec) {
X		sprintf(tmp, "%d seconds", tv->tv_sec);
X		strcat(buf, tmp);
X	}
X
X	strcat(buf, "\n");
X	(*fn_send)(0, buf);
X}
________This_Is_The_END________
echo 'x - getuserstats.c'
sed 's/^X//' <<'________This_Is_The_END________' >>getuserstats.c
X#ifndef lint
Xstatic char *RCSid = "$Header: /ecn1/src/ecn/statsrv/RCS/getuserstats.c,v 1.3 87/12/08 14:39:06 davy Exp $";
X#endif
X/*
X * getuserstats - get user statistics
X *
X * David A. Curry
X * Purdue University
X * Engineering Computer Network
X * davy at intrepid.ecn.purdue.edu
X * October, 1987
X *
X * $Log:	getuserstats.c,v $
X * Revision 1.3  87/12/08  14:39:06  davy
X * Added code for the "who" statistic.
X * 
X * Revision 1.2  87/10/29  14:24:36  davy
X * Modified to use UTMP instead of hard-coded path.
X * 
X * Revision 1.1  87/10/17  21:01:40  davy
X * Initial revision
X * 
X */
X#include <sys/param.h>
X#include <sys/stat.h>
X#include <sys/time.h>
X#include <syslog.h>
X#include <stdio.h>
X#include <utmp.h>
X
X#define WHO		0
X#define USERS		1
X#define ACTUSERS	2
X
X#define UTMP		"/etc/utmp"
X#define USERBUFSIZE	2048		/* 256 8-char logins		*/
X
Xextern	int (*fn_recv)(), (*fn_send)();	/* send/recv functions		*/
X
X/*
X * getuserstats - get user statistics
X */
Xgetuserstats(name)
Xchar *name;
X{
X	FILE *fp;
X	char buf[32];
X	struct utmp ut;
X	struct stat st;
X	struct timeval tv;
X	register int cnt, mode;
X	char userbuf[USERBUFSIZE];
X
X	/*
X	 * "actusers" means we only want active users.
X	 * "who" means we want user names.
X	 */
X	if (!strcmp(name, "actusers"))
X		mode = ACTUSERS;
X	else if (!strcmp(name, "users"))
X		mode = USERS;
X	else
X		mode = WHO;
X
X	if ((fp = fopen(UTMP, "r")) == NULL) {
X		syslog(LOG_ERR, "cannot open %s", UTMP);
X		exit(1);
X	}
X
X	*userbuf = NULL;
X	gettimeofday(&tv, (struct timeval *) 0);
X
X	/*
X	 * For each user...
X	 */
X	cnt = 0;
X	while (fread(&ut, sizeof(struct utmp), 1, fp) == 1) {
X		/*
X		 * Not logged in.
X		 */
X		if (ut.ut_name[0] == NULL)
X			continue;
X
X		/*
X		 * For active users, see how long he's been idle.
X		 */
X		if (mode == ACTUSERS) {
X			sprintf(buf, "/dev/%.8s", ut.ut_line);
X
X			if (stat(buf, &st) < 0)
X				continue;
X
X			/*
X			 * If idle less than 1 hour, he's active.
X			 */
X			if ((tv.tv_sec - st.st_atime) < 3600)
X				cnt++;
X		}
X		else {
X			/*
X			 * Copy the login name if needed.
X			 */
X			if (mode == WHO) {
X				sprintf(buf, "%.8s ", ut.ut_name);
X				strcat(userbuf, buf);
X			}
X
X			cnt++;
X		}
X	}
X
X	/*
X	 * Send the appropriate buffer.
X	 */
X	if (mode == WHO) {
X		strcat(userbuf, "\n");
X		(*fn_send)(0, userbuf);
X	}
X	else {
X		sprintf(buf, "%d\n", cnt);
X		(*fn_send)(0, buf);
X	}
X
X	fclose(fp);
X}
________This_Is_The_END________
echo 'x - st_sendrecv.c'
sed 's/^X//' <<'________This_Is_The_END________' >>st_sendrecv.c
X#ifndef lint
Xstatic char *RCSid = "$Header: /ecn1/src/ecn/statsrv/RCS/st_sendrecv.c,v 1.2 87/10/19 08:37:02 davy Exp $";
X#endif
X/*
X * st_sendrecv.c - stream send/recv functions
X *
X * David A. Curry
X * Purdue University
X * Engineering Computer Network
X * davy at intrepid.ecn.purdue.edu
X * October, 1987
X *
X * $Log:	st_sendrecv.c,v $
X * Revision 1.2  87/10/19  08:37:02  davy
X * Fixed to catch end of file on socket.
X * 
X * Revision 1.1  87/10/17  21:01:46  davy
X * Initial revision
X * 
X */
X#include <sys/param.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <syslog.h>
X#include <stdio.h>
X
X#include "stats.h"
X
Xextern char	*pname;
Xextern short	server;
X
X/*
X * st_send - send buf on the socket s.
X */
Xst_send(s, buf)
Xchar *buf;
Xint s;
X{
X	register int cnt;
X
X	/*
X	 * We want the length including the null.
X	 */
X	cnt = strlen(buf) + 1;
X
X	/*
X	 * Send the buffer.
X	 */
X	if (send(s, buf, cnt, 0) < 0) {
X		if (server)
X			syslog(LOG_ERR, "send: %m");
X		else
X			error("send");
X		exit(1);
X	}
X}
X
X/*
X * st_recv - receive data of maximum length cnt into the buffer buf on
X *	     socket s.  err describes what we're expecting for the error
X *	     message.
X */
Xst_recv(s, buf, cnt, err)
Xchar *buf, *err;
Xint s, cnt;
X{
X	char c;
X	register int n;
X
X	/*
X	 * Receive a character at a time up to a null.
X	 */
X	do {
X		if ((n = recv(s, &c, sizeof(char), 0)) < 0) {
X			if (server)
X				syslog(LOG_ERR, "recv: %m");
X			else
X				error("recv");
X			exit(1);
X		}
X
X		/*
X		 * End of file.
X		 */
X		if (n == 0)
X			exit(0);
X
X		if (--cnt < 0) {
X			if (server)
X				syslog(LOG_ERR, "%s too long", err);
X			else
X				fprintf(stderr, "%s: %s too long.\n", pname, err);
X			exit(1);
X		}
X
X		*buf++ = c;
X	} while (c != '\0');
X}
________This_Is_The_END________
echo 'x - stats.c'
sed 's/^X//' <<'________This_Is_The_END________' >>stats.c
X#ifndef lint
Xstatic char *RCSid = "$Header: /ecn1/src/ecn/statsrv/RCS/stats.c,v 1.1 87/10/17 21:01:52 davy Exp $";
X#endif
X/*
X * stats - retrieve statistics from the statistics server
X *
X * Usage:	stats [-d] host statname [statname...]
X *
X * The STATSRV protocol is described in RFC996, "Statistics Server",
X * D.L. Mills, February 1987.
X *
X * David A. Curry
X * Purdue University
X * Engineering Computer Network
X * davy at intrepid.ecn.purdue.edu
X * October, 1987
X *
X * $Log:	stats.c,v $
X * Revision 1.1  87/10/17  21:01:52  davy
X * Initial revision
X * 
X */
X#include <sys/param.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <netdb.h>
X#include <stdio.h>
X
X#include "stats.h"
X
Xchar	*pname;				/* program name			*/
Xshort	server = 0;			/* indicates we aren't server	*/
Xstruct	sockaddr_in sin;		/* address of remote host	*/
Xint	(*fn_send)(), (*fn_recv)();	/* send/recv functions		*/
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	int socktype;
X	register int s;
X	char buf[10240];
X	struct hostent *hp;
X	struct servent *sp;
X	char *host, *servtype;
X	struct hostent *gethostbyname();
X	struct servent *getservbyname();
X
X	pname = *argv;
X
X	/*
X	 * Set up to use TCP stream sockets.
X	 */
X	servtype = "tcp";
X	fn_recv = st_recv;
X	fn_send = st_send;
X	socktype = SOCK_STREAM;
X
X	/*
X	 * Check arguments.
X	 */
Xargchk:	if (argc < 3) {
X		fprintf(stderr, "Usage: %s [-d] host statname [statname...]\n", pname);
X		fprintf(stderr, "    use the \"help\" statistic for more info.\n");
X		exit(1);
X	}
X
X	/*
X	 * -d indicates use datagrams; set up to use
X	 * datagram sockets and then go recheck the
X	 * argument list.
X	 */
X	if (!strcmp(*++argv, "-d")) {
X		socktype = SOCK_DGRAM;
X		fn_recv = dg_recv;
X		fn_send = dg_send;
X		servtype = "udp";
X		argc--;
X
X		goto argchk;
X	}
X
X	/*
X	 * Host is first argument.
X	 */
X	argc--;
X	host = *argv;
X
X	/*
X	 * Get a socket.
X	 */
X	if ((s = socket(AF_INET, socktype, 0)) < 0) {
X		error("socket");
X		exit(1);
X	}
X
X	/*
X	 * Look up the host's address.
X	 */
X	if ((hp = gethostbyname(host)) == NULL) {
X		fprintf(stderr, "%s: %s: host unknown.\n", pname, host);
X		exit(1);
X	}
X
X	/*
X	 * Look up the port the server lives on.
X	 */
X	if ((sp = getservbyname(SERVNAME, servtype)) == NULL) {
X		fprintf(stderr, "%s: %s/%s: service unknown.\n", pname, SERVNAME, servtype);
X		exit(1);
X	}
X
X	/*
X	 * Build the server's address.
X	 */
X	sin.sin_port = sp->s_port;
X	sin.sin_family = hp->h_addrtype;
X	bcopy(hp->h_addr, &sin.sin_addr, sizeof(sin.sin_addr));
X
X	/*
X	 * If we're using a stream socket, connect to
X	 * the remote host.
X	 */
X	if (socktype == SOCK_STREAM) {
X		if (connect(s, &sin, sizeof(struct sockaddr_in)) < 0) {
X			error("connect");
X			exit(1);
X		}
X	}
X
X	/*
X	 * For each statistic requested...
X	 */
X	while (--argc) {
X		/*
X		 * Send the name of the statistic to the
X		 * server.
X		 */
X		(*fn_send)(s, *++argv);
X
X		/*
X		 * Wait for a response.
X		 */
X		(*fn_recv)(s, buf, sizeof(buf), "response");
X
X		/*
X		 * Print what we got.
X		 */
X		fputs(buf, stdout);
X	}
X
X	exit(0);
X}
X
X/*
X * error - print program name and error message.
X */
Xerror(s)
X{
X	fprintf(stderr, "%s: ", pname);
X	perror(s);
X}
________This_Is_The_END________
echo 'x - statsrv.c'
sed 's/^X//' <<'________This_Is_The_END________' >>statsrv.c
X#ifndef lint
Xstatic char *RCSid = "$Header: /ecn1/src/ecn/statsrv/RCS/statsrv.c,v 1.3 88/01/02 12:46:07 davy Exp $";
X#endif
X/*
X * statsrv - statistics server
X *
X * Statsrv is invoked via inetd(8).  Place the following lines in your
X * inetd.conf file (statsrv is port 133 for both udp and tcp):
X *
X * statsrv	stream	tcp	nowait	root	/etc/statsrv	statsrv
X * statsrv	dgram	udp	wait	root	/etc/statsrv	statsrv
X *
X * The STATSRV protocol is described in RFC996, "Statistics Server",
X * D.L. Mills, February 1987.
X *
X * David A. Curry
X * Purdue University
X * Engineering Computer Network
X * davy at intrepid.ecn.purdue.edu
X * October, 1987
X *
X * $Log:	statsrv.c,v $
X * Revision 1.3  88/01/02  12:46:07  davy
X * Added "msgbuf" and "msgtail" statistics.
X * 
X * Revision 1.2  87/12/08  14:39:23  davy
X * Added lines for the "tables" and "who" commands.
X * 
X * Revision 1.1  87/10/17  21:01:57  davy
X * Initial revision
X * 
X */
X#include <sys/param.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <syslog.h>
X#include <errno.h>
X#include <ctype.h>
X#include <stdio.h>
X
X#include "stats.h"
X
X/*
X * One of these for each statistic we understand.
X */
Xstruct statistic {
X	char	*st_name;
X	int	(*st_func)();
X};
X
X/*
X * Functions which do the statistics.
X */
Xextern int liststats();
Xextern int getmsgbuf();
Xextern int getcpustats();
Xextern int getnetstats();
Xextern int getloadstats();
Xextern int getuserstats();
Xextern int gettimestats();
Xextern int gettablestats();
X
X/*
X * Statistics we understand.
X */
Xstatic struct statistic stats[] = {
X	{	"actusers",	getuserstats	},
X	{	"boottime",	gettimestats	},
X	{	"cpu",		getcpustats	},
X	{	"date",		gettimestats	},
X	{	"help",		liststats	},
X	{	"loadav",	getloadstats	},
X	{	"mbufs",	getnetstats	},
X	{	"msgbuf",	getmsgbuf	},
X	{	"msgtail",	getmsgbuf	},
X	{	"packets",	getnetstats	},
X	{	"proto",	getnetstats	},
X	{	"tables",	gettablestats	},
X	{	"time",		gettimestats	},
X	{	"uptime",	gettimestats	},
X	{	"users",	getuserstats	},
X	{	"who",		getuserstats	},
X	{	0,		0		}
X};
X	
Xchar	*pname;				/* program name			*/
Xshort	server = 1;			/* indicates we're the server	*/
Xstruct	sockaddr_in sin;		/* address of remote host	*/
Xint	(*fn_recv)(), (*fn_send)();	/* send/recv functions		*/
X
Xextern	int errno;
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	char *rindex();
X	char buf[BUFSIZ];
X	register char *s;
X	int len, socktype;
X	register struct statistic *st;
X
X	/*
X	 * Get program name.
X	 */
X	if ((pname = rindex(*argv, '/')) == NULL)
X		pname = *argv;
X	else
X		pname++;
X
X	/*
X	 * Set up syslog.
X	 */
X	openlog(pname, LOG_PID, LOG_DAEMON);
X
X	/*
X	 * Set up to run on stream sockets.
X	 */
X	fn_recv = st_recv;
X	fn_send = st_send;
X	socktype = SOCK_STREAM;
X
X	/*
X	 * Figure out if we're on a stream or datagram socket.  If
X	 * we are on a datagram socket, we have no peer, and we
X	 * will get an ENOTCONN error.
X	 */
X	len = sizeof(struct sockaddr_in);
X	if (getpeername(0, &sin, &len) < 0) {
X		/*
X		 * Datagram socket.
X		 */
X		if (errno == ENOTCONN) {
X			socktype = SOCK_DGRAM;
X			fn_recv = dg_recv;
X			fn_send = dg_send;
X		}
X		else {
X			syslog(LOG_ERR, "getpeername: %m");
X			exit(1);
X		}
X	}
X
X	/*
X	 * Forever...
X	 */
Xtop:	for (;;) {
X		/*
X		 * Get a statistic request from the remote host.
X		 */
X		(*fn_recv)(0, buf, sizeof(buf), "statname");
X
X		/*
X		 * Convert to lower case.
X		 */
X		for (s = buf; *s; s++) {
X			if (isupper(*s))
X				*s = tolower(*s);
X		}
X
X		/*
X		 * Look up the statistic and run the
X		 * function.
X		 */
X		for (st = stats; st->st_name; st++) {
X			if (!strcmp(st->st_name, buf)) {
X				(*(st->st_func))(buf);
X
X				/*
X				 * Only one statistic per datagram.
X				 */
X				if (socktype == SOCK_DGRAM)
X					exit(0);
X
X				goto top;
X			}
X		}
X
X		/*
X		 * Unknown statistic.
X		 */
X		(*fn_send)(0, "unknown command - use \"help\" for list.\n");
X		exit(1);
X	}
X}
X
X/*
X * liststats - list statistics we know about.
X */
Xliststats(name)
Xchar *name;
X{
X	char buf[BUFSIZ];
X	register struct statistic *st;
X
X	*buf = '\0';
X
X	for (st = stats; st->st_name; st++) {
X		strcat(buf, st->st_name);
X		strcat(buf, "\n");
X	}
X
X	(*fn_send)(0, buf);
X}
X
X/*
X * error - print program name and error message.
X */
Xerror(s)
X{
X	fprintf(stderr, "%s: ", pname);
X	perror(s);
X}
________This_Is_The_END________
echo 'x - tester'
sed 's/^X//' <<'________This_Is_The_END________' >>tester
X#!/bin/csh -f
X
Xset hostname = `hostname`
X
Xforeach i (actusers boottime cpu date help loadav mbufs msgbuf msgtail \
X	   packets proto tables time uptime users who)
X	echo "--------- $i (stream) ---------"
X	stats $hostname $i
X	echo "--------- $i (datagram) ---------"
X	stats -d $hostname $i
Xend
X
________This_Is_The_END________
echo 'x - rfc'
sed 's/^X//' <<'________This_Is_The_END________' >>rfc
X
XNetwork Working Group                                         D.L. Mills
XRequest for Comments: 996                         University of Delaware
X                                                           February 1987
X
X
X                           Statistics Server
X
XSTATUS OF THIS MEMO
X
X   This RFC specifies a standard for the ARPA Internet community. Hosts
X   and gateways on the DARPA Internet that choose to implement a remote
X   statistics monitoring facility may use this protocol to send
X   statistics data upon request to a monitoring center or debugging
X   host.  Distribution of this memo is unlimited.
X
XDISCUSSION
X
X   Many host and gateway implementations include a facility which
X   records traffic statistics, such as packet counters, error counters
X   and significant event counters for debugging and performance
X   evluation.  Simple data-access and formatting programs can be used to
X   display these statistics along with the status of connections, etc.
X   Several operating systems, including the various Unix systems and
X   Fuzzball systems, already provide extensive facilities to capture and
X   display these data for local users and/or operators.
X
X   In many instances it is highly useful to observe statistics data on
X   remote hosts and gateways from a monitoring center or debugging host.
X   Indeed, several protocols have been implemented and used expressly
X   for this purpose [1-6]. In many cases the data can be retrieved using
X   conventional services such as remote login or even file transfer.
X   However, use of these heavyweight mechanisms is awkward and intrusive
X   if conducted on a regular, frequent basis and may involve substantial
X   intrusion in the operating system if retrofitted to existing systems.
X
X   The Statistics Server (STATSRV) protocol is intended as a lightweight
X   mechanism similar in spirit to NETSTAT [7] and complementary to it.
X   STATSRV is designed to capture statistics data with minimal intrusion
X   on existing systems or networks. It is intended for use with existing
X   hosts and gateways primarily for casual monitoring and debugging
X   purposes. It is not intended as a full-function monitoring protocol
X   [1,5,6] providing detailed, standardized reports suitable for machine
X   analysis, for example, but could be useful in exploratory development
X   leading to enduring systems of this type.
X
X   The STATSRV model is based on the native host command language used
X   for statistics monitoring and display. The client sends a null-
X   terminated ASCII command to the server, which then responds with a
X   null-terminated ASCII response suitable for a printer or CRT display.
X   Although in principle STATSRV could be used over TCP, it is less
X   intrusive and more efficient to use it over UDP. In the case of UDP,
X
X
X
XD. L. Mills                                                     [Page 1]
X
XRFC 996                                                    February 1987
X
X
X   commands and responses must fit into a single 576-octet IP datagram.
X   In both UDP and TCP the assigned port number is 133 (decimal).
X
X   As is conventional in other lightweight services of this type
X   (NETSTAT, FINGER, etc.), there is no provision for access control or
X   authentication in STATSRV. If necessary, each command could include a
X   password or other mechanism to discourage casual abuse.
X
XEXAMPLE
X
X   The Fuzzball system includes many local commands to display internal
X   data structures, including one that produces the following billboard
X   for each network device, in this case "dm0" on host "udel2.udel.edu":
X
X        Process type: 000027  options: 040000
X        Subnet: DMV  status: 376  hello: 15  timeout: 2000
X        Foreign address: [192.5.39.87]  max size: 576
X        Input packets      3645    Output packets  3690
X          bad format       0       ICMP msgs       0
X          bad checksum     0       Input errors    0
X          returned         0       Output errors   0
X          dropped          2       No buffer       0
X          HELLO msgs       2286    Preempted       0
X
X   The same billboard is returned as a null-terminated ASCII string in a
X   UDP datagram by sending the null-terminated ASCII command "dm0" in a
X   UDP datagram to the host. Similar billboards can be produced for most
X   processes in the system. Unix programs and shell scripts have been
X   built which send commands like these to selected hosts on a periodic
X   basis in order to construct a simple, ad-hoc monitoring facility.
X
XREFERENCES
X
X   [1]  Flood Page, D.,"Gateway Monitoring Protocol", DARPA Network
X        Working Group Report IEN-131, Bolt Beranek and Newman, February
X        1980.
X
X   [2]  Flood Page, D., "The CMCC Terminal Process", DARPA Network
X        Working Group Report IEN-132, Bolt Beranek and Newman, February
X        1980.
X
X   [3]  Flood Page, D., "CMCC Performance Measurement Message Formats",
X        DARPA Network Working Group Report IEN-157, Bolt Beranek and
X        Newman, September 1980.
X
X   [4]  Jones, R.G., " A Proposal for Simple Measurement Support for
X        Users", DARPA Network Working Group Report IEN-161, University
X        College London, November 1980.
X
X
X
X
X
X
XD. L. Mills                                                     [Page 2]
X
XRFC 996                                                    February 1987
X
X
X   [5]  Littauer, B.M., A.J. Huang and R.M. Hinden," A Host Monitoring
X        Protocol", DARPA Network Working Group Report IEN-197, Bolt
X        Beranek and Newman, September 1981.
X
X   [6]  Hinden, R.M.," A Host Monitoring Protocol", DARPA Network
X        Working Group Report RFC-869, BBN Communications Corporation,
X        December 1983.
X
X   [7]  Reynolds, J.K., and J. Postel, "Assigned Numbers", DARPA Network
X        Working Group Report RFC-990, USC Information Sciences
X        Institute, November 1986.
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
XD. L. Mills                                                     [Page 3]
X
________This_Is_The_END________
exit

-- 
For comp.sources.unix stuff, mail to sources at uunet.uu.net.



More information about the Comp.sources.unix mailing list