Subject: v13i031: Remote statistics server Newsgroups: comp.sources.unix Sender: sources Approved: rsalz@uunet.UU.NET Submitted-by: Dave Curry 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 # 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@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.3 87/12/08 14:39:43 davy Exp $ X# X# Makefile for stats and statsrv. X# X# David A. Curry X# Purdue University X# Engineering Computer Network X# davy@intrepid.ecn.purdue.edu X# October, 1987 X# X# $Log: Makefile,v $ 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 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 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" 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" 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 \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.1 87/10/17 21:01:03 davy Exp Locker: davy $ X * X * stats.h - definitions for statistics server X * X * David A. Curry X * Purdue University X * Engineering Computer Network X * davy@intrepid.ecn.purdue.edu X * October, 1987 X * X * $Log: stats.h,v $ 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@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 X#include X#include X#include X#include 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.2 87/10/29 14:24:02 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@intrepid.ecn.purdue.edu X * October, 1987 X * X * $Log: getcpustats.c,v $ 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 X#include X#include X#include X#include X#include 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.2 87/10/29 14:24:54 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@intrepid.ecn.purdue.edu X * October, 1987 X * X * $Log: getloadstats.c,v $ 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 X#include X#include X#include X#include 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#ifdef sun 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#ifdef sun X sprintf(buf, "%.2f\n", (double) avenrun[0] / FSCALE); X#else 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 - 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@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 X#include X#include 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@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 X#include X#include 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@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 X#include X#include X#include X#include X#include 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@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 X#include X#include X#include X#include X#include 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@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 X#include X#include X#include X#include 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@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 X#include X#include X#include X#include 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.2 87/12/08 14:39:23 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@intrepid.ecn.purdue.edu X * October, 1987 X * X * $Log: statsrv.c,v $ 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 X#include X#include X#include X#include X#include X#include 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 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 { "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 packets proto \ X 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