From: avalon@coombs.anu.edu.au (Darren Reed)
Newsgroups: alt.irc
Subject: IRC Traffic Stats stuff.
Date: 17 Dec 92 16:05:45 GMT
Organization: Australian National University
NNTP-Posting-Host: 150.203.76.2


I've reworked the stats program a bit and included a couple of perl
scripts which can be used to easily generate a set of commands for
gnuplot.  If you dont have gnuplot then this wont be of much use,
even less use if your system isn't a member of the perl club.

unshar, "make all" and run "runplot".  The stats collector backgrounds
into a 'daemon' mode.  use "genplot" to generate the plot file for
gnuplot.  A few data files are also created.  Any bugs, send me email.
Nothing much else has changed.  The graph should turn out to be bytes vs
time and messages vs time.  If you know a bit about gnuplot, you can
get a postscript file of the graph too.  This would be best run by those
who are concerned about traffic and/or would like some hard data about
how much bandwidth IRC is using.

avalon
----cut here--------cut here--------cut here--------cut here--------cut here----
#!/bin/sh
# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	stat/Makefile
#	stat/README
#	stat/flowmon.c
#	stat/genplot
#	stat/runplot
#
echo x - stat/Makefile
sed 's/^X//' >stat/Makefile << 'END-of-stat/Makefile'
X# Makefile (C)1992 Darren Reed
X# Makefile for realtime server stats clients
X#
XLIBS=-lcurses -ltermcap
XCFLAGS=-O
XPROGS=flowmon
X
Xall: $(PROGS)
X
Xcmdmon: cmdmon.c
X	$(CC) $@.c $(CFLAGS) -o $@ $(LIBS)
X
Xflowmon: flowmon.c
X	$(CC) $@.c $(CFLAGS) -o $@ $(LIBS)
X
Xclean:
X	/bin/rm -f core *.o $(PROGS)
END-of-stat/Makefile
echo x - stat/README
sed 's/^X//' >stat/README << 'END-of-stat/README'
X
XSimple...
X
XEdit them to set the nicks, user info and server and then get them compiled.
X
Xthen run using
X
Xcmdmon [ server [ port [ updatedelay ] ] ]
Xflowmon [ server [ port [ updatedelay ] ] ]
X
XBrief.
X------
X
Xcmdmon
X
X   Shows the changing use of the most used commands on the server in sorted
Xorder of most used to least used.
X
Xflowmon
X
X   Shows the flow of data into a server and out its links.  Can be very
Xuseful.  Try flowmon -h for command line options.
X
XBoth use curses to maintain the display and should show as many entries
Xas possible.
X
XThere are probably bugs somewhere too.
END-of-stat/README
echo x - stat/flowmon.c
sed 's/^X//' >stat/flowmon.c << 'END-of-stat/flowmon.c'
X/*
X * flownmon.c (C)1992 Darren Reed.
X * monitors data flow over irc server connections.
X * maybe of use to someone.
X */
X#ifndef lint
Xstatic	char	sccsid[] = "@(#)flowmon.c 2.2 12/18/92 (C)1992 Darren Reed";
X#endif
X
X#include <sys/types.h>
X#include <sys/time.h>
X#include <netinet/in.h>
X#include <sys/socket.h>
X#include <stdio.h>
X#include <fcntl.h>
X#include <curses.h>
X#include <netdb.h>
X/*
X * change this.
X */
Xchar	signon[] ="NICK %s\n\rUSER %s xx xx :Flow monitor\n\rMODE %s +i\r\n";
X/*
X * leave these
X */
Xchar	pong[] = "PONG ping\r\n";
Xchar	statsm[] ="STATS l *\r\n";
Xchar	sends[100], *msg;
Xchar	operm[] = "OPER ---- -----\r\n";
X
X#define	SERVERIP	"150.203.76.2"
X#define	SERVERPORT	6667
X#define	UPDATEDELAY	60
X
Xchar	buff[8192], servn[60], nick[20];
Xchar	name[60];
Xint	port;
Xlong	sq, sm, sb, rm, rb;
Xint	logfd[4];
X
Xstruct stats {
X	int	sndq, sndm, sndb, rcvm, rcvb;
X};
X
Xtypedef struct	Message {
X	char	cnam[60];
X	struct	stats	now;
X 	struct	stats	old;
X	long	update;
X} MType;
X
Xstruct Message msgtab[256];
X
Xstatic	int	entries = sizeof(msgtab)/sizeof(MType);
X
Xvoid	doio();
X
Xmain(argc, argv)
Xint	argc;
Xchar	*argv[];
X{
X	FILE	*f;
X	int	fd, mlen, daemon = 0;
X	int	log = 0, set = 0, samples = 0;
X	char	*remote = NULL, *server, *progname = *argv;
X	char	*nick = NULL, *user = NULL;
X	long	waittime;
X	struct	hostent *hp = NULL;
X
X	bzero(msgtab, sizeof(msgtab));
X
X	argv++;
X	argc--;
X	server =  SERVERIP;
X	port = SERVERPORT;
X	waittime = UPDATEDELAY;
X
X	while (*argv && **argv == '-')
X	    {
X		register char *s = *argv;
X
X		argc--;
X		for (s++; *s; s++)
X			switch(*s)
X			{
X			case 'd' :
X				daemon = 1;
X				break;
X			case 'l' :
X				log = 1;
X				break;
X			case 'n' :
X				if (*(argv+1))
X					nick = *++argv;
X				break;
X			case 'p' :
X				if (*(argv+1))
X					port = atoi(*++argv);
X				break;
X			case 'r' :
X				if (*(argv+1))
X					remote = *++argv;
X				break;
X			case 's' :
X				if (*(argv+1))
X					server = *++argv;
X				break;
X			case 'u' :
X				if (*(argv+1))
X					user = *++argv;
X				break;
X			case 'w' :
X				if (*(argv+1))
X					waittime = atoi(*++argv);
X				break;
X			case 'z' :
X				if (*(argv+1))
X					samples = atoi(*++argv);
X				break;
X			case '?' :
X				printf("Usage: %s [-dl] [-n nick] [-p port] \
X[-r server] [-s server] [-u username] [-w seconds]\n");
X				exit(1);
X			default :
X				break;
X			}
X		argv++;
X	    }
X	/*
X	 * ip# of server to connect to
X	 */
X
X	if (remote)
X	    {
X		sprintf(sends, "STATS l %s\r\n", remote);
X		if (strcasecmp(remote, server))
X			mlen = strlen(msg = sends);
X		else
X			mlen = strlen(msg = statsm);
X	    }
X	else
X	    {
X		remote = server;
X		mlen = strlen(msg = statsm);
X	    }
X	if (!nick)
X		nick = "flower";
X	if (!user)
X		user = "power";
X
X	sprintf(buff, signon, nick, user, nick);
X	if (log)
X		openlogs(remote);
X
X	fd = connectto(server);
X
X	printf("Connected.\n");
X	write(fd, buff, strlen(buff));
X	write(fd, msg, mlen);
X	bzero(buff, sizeof(buff));
X	if (waittime < 10)
X		waittime = 10;
X
X	if (!daemon)
X	    {
X		initscr();
X		clear();
X		refresh();
X	    }
X	else
X	    {
X		if (fork())
X			exit(0);
X		setpgrp(getpid(),0);
X	    }
X
X	while (1) 
X		doio(fd, msg, mlen, remote, log, waittime, daemon, samples);
X}
X
Xint	connectto(server)
Xchar	*server;
X{
X	struct	hostent	*hp;
X	struct	sockaddr_in	sock;
X	int	fd;
X
X	bzero(&sock, sizeof(sock));
X	if (*server)
X	    {
X		hp = gethostbyname(server);
X		if (hp)
X			bcopy(hp->h_addr_list[0], (char *)&sock.sin_addr,
X				sizeof(struct in_addr));
X		else
X		    {
X			printf("couldnt lookup %s\n",server);
X			exit(0);
X		    }
X	    }
X	else
X		sock.sin_addr.s_addr = inet_addr(server);
X	printf("Connecting to %s\n", inet_ntoa(sock.sin_addr));
X	sock.sin_port = htons(port);
X	sock.sin_family = AF_INET;
X
X	if ((fd = socket(AF_INET, SOCK_STREAM, 0))<0) {
X		perror("socket");
X		exit(-1);
X	}
X
X	if (connect(fd, (char *)&sock, sizeof(sock))==-1) {
X		perror("connect");
X		exit(-1);
X	}
X	return fd;
X}
X
Xvoid	doio(fd, cmd, cmdlen, remserver, dolog, waittime, daemon, samples)
Xchar	*cmd, *remserver;
Xint	cmdlen, dolog, daemon, samples;
Xlong	waittime;
X{
X	static	long	last = 0, lastm = 0, scanned = 0;
X	static	char	*head = buff, *tail = buff;
X	static	int	done = 0;
X	int	n, len, fields, numb;
X	char	*s = NULL, *t = NULL;
X	fd_set	rd;
X	struct	timeval wait;
X
X	if (!last)
X		last = time(NULL);
X	if (!lastm)
X		lastm = time(NULL);
X	if (!scanned)
X		scanned = time(NULL);
X
X	wait.tv_sec = 1;
X	wait.tv_usec = 0;
X	FD_ZERO(&rd);
X	FD_SET(fd, &rd);
X	n = select(FD_SETSIZE, &rd, NULL, NULL, &wait);
X	if (FD_ISSET(fd, &rd))
X	    {
X		len = sizeof(buff) - (tail - buff);
X		if ((len = read(fd, tail, len)) <= 0)
X		    {
X			s_error(s);
X			exit(-1);
X		    }
X		tail += len;
X		*tail = '\0';
X		/*
X		** check buffer for presence of full replies (terminated by
X		** \n's).
X		*/
X		while (s = (char *)index(head, '\n'))
X		    {
X			*s++ = '\0';
X			len -= (s - head);
X			head = s;
X			if (len <= 0)
X				*buff = '\0';
X			if (t = (char *)index(head, ' '))
X				if (!isdigit(*++t))
X					continue;
X			fields = sscanf(head, ":%s %d %s", servn, &numb, nick);
X			if ((fields != 3) || (numb < 211) || (numb > 219))
X				continue;
X			if (numb == 219)
X			    {
X				if (!daemon)
X					showstats(remserver, scanned);
X				done++;
X				if (samples && (done == samples))
X				    {
X					if (dolog)
X						closelogs();
X					exit(0);
X				    }
X			    }
X			else if (numb == 211)
X			    {
X				fields = sscanf(head,
X						":%s %d %s %s %d %d %d %d %d",
X						servn, &numb, nick, name,
X						&sq, &sm, &sb, &rm, &rb);
X				if (fields == 9)
X					dostats(remserver, dolog, scanned);
X			    }
X		    }
X		if (len > 0 && head != buff)
X		    {
X			movemem(head, buff, len);
X			head = buff;
X			tail = head + len;
X			*(tail+1) = '\0';
X		    }
X		else
X		    {
X			head = tail = buff;
X			*buff = '\0';
X		    }
X	    }
X
X	if ((time(NULL) - last) > 30)
X	    {
X		write(fd, pong, strlen(pong));
X		last = time(NULL);
X	    }
X
X	if ((time(NULL) - lastm) > waittime)
X	    {
X		write(fd, cmd, cmdlen);
X		scanned = lastm = time(NULL);
X	    }
X}
X
Xs_error(fd)
Xint	fd;
X{
X	perror("read");
X}
X
Xdostats(remserver, dolog, scantime)
Xchar	*remserver;
Xint	dolog;
Xlong	scantime;
X{
X	register int i;
X	register MType *mp;
X
X	/*
X	** update old table entry
X	*/
X	mp = msgtab;
X	for (i = 256; i; i--, mp++)
X		if (!strcasecmp(mp->cnam, name))
X		    {
X			bcopy(&mp->now, &mp->old,
X				sizeof(struct stats));
X			mp->now.sndq = sq;
X			mp->now.sndb = sb;
X			mp->now.sndm = sm;
X			mp->now.rcvb = rb;
X			mp->now.rcvm = rm;
X			mp->update = scantime;
X			if (dolog && !strcasecmp(name, remserver))
X			    {
X					char	num[16];
X	
X					sprintf(num, "%d\n",
X						sb - mp->old.sndb);
X					write(logfd[0], num, strlen(num));
X					sprintf(num, "%d\n",
X						sm - mp->old.sndm);
X					write(logfd[1], num, strlen(num));
X					sprintf(num, "%d\n",
X						rb - mp->old.rcvb);
X					write(logfd[2], num, strlen(num));
X					sprintf(num, "%d\n",
X						rm - mp->old.rcvm);
X					write(logfd[3], num, strlen(num));
X			    }
X			return;
X		    }
X
X	/*
X	** add a new one in
X	*/
X	mp = msgtab;
X	for (i = 256; i; i--, mp++)
X		if (!*mp->cnam)
X		    {
X			strcpy(mp->cnam, name);
X			mp->now.sndq = sq;
X			mp->now.sndb = sb;
X			mp->now.sndm = sm;
X			mp->now.rcvb = rb;
X			mp->now.rcvm = rm;
X			mp->update = scantime;
X			break;
X		    }
X}
X
X/*
X** remove un updated entries from the table
X*/
Xremoveold(scantime)
Xlong	scantime;
X{
X	register	int i;
X	register MType *mp;
X
X	mp = msgtab;
X	for (i = 256; i; i--,mp++)
X		if (mp->update && (mp->update < scantime))
X		    {
X			mp->now.rcvb = 0;
X			mp->now.sndb = 0;
X			*mp->cnam = '\0';
X		    }
X}
X
X/*
X** sort table on bytes received
X*/
Xsortm(p1, p2)
Xregister MType *p1, *p2;
X{
X	return (p2->now.rcvb - p1->now.rcvb);
X}
X
Xshowstats(remserver, scantime)
Xchar	*remserver;
Xlong	scantime;
X{
X	register MType *mp;
X	int	i, show;
X	long	now = time(0);
X	char	*s;
X
X	if ((show = entries) > LINES - 3)
X		show = LINES - 3;
X
X	removeold(scantime);
X	qsort(msgtab, sizeof(msgtab)/sizeof(MType), sizeof(MType), sortm);
X	move(0,0);
X	refresh();
X	mvcur(LINES-1,COLS-1,0,0);
X	refresh();
X	printw("%s %d %s", remserver, port, ctime(&now));
X	printw("Connection                SendQ  SendM     SendB      \
X RecvM     RecvB\n");
X	printw("------------------------- ------ --------- -----------\
X --------- -----------\n");
X
X	mp = msgtab;
X	for (i = COLS - 3; i; i--, mp++)
X	    {
X		if (!*mp->cnam)
X			clrtoeol();
X		else
X		    {
X			printw("%-25.25s %6d %9d %11d ", mp->cnam,
X				mp->now.sndq, mp->now.sndm - mp->old.sndm,
X				mp->now.sndb - mp->old.sndb);
X			printw("%9d %11d\n", mp->now.rcvm - mp->old.rcvm,
X				mp->now.rcvb - mp->old.rcvb);
X		    }
X	    }
X	refresh();
X	mvcur(LINES-1,COLS-1,0,0);
X	refresh();
X}
X
X
Xmovemem(s,t,l)
Xregister char *s, *t;
Xregister int l;
X{
X	for (; l >= 0; l--, s++, t++) *t = *s;
X}
X
Xint	stlen(s)
Xregister char *s;
X{
X	register int i = 0;
X
X	for (; *s; s++, i++)
X		;
X	return i;
X}
X
X/*
X** open 1 log file for each report: bytes in and out, messages in and out
X*/
Xopenlogs(server)
Xchar	*server;
X{
X	char	name[50], *s, logname[70];
X
X	if (s = (char *)index(server, '.'))
X	    {
X		*s = '\0';
X		strncpy(name, server, sizeof(name)-1);
X		name[sizeof(name)-1] = '\0';
X		*s = '.';
X	    }
X	else
X	    {
X		strncpy(name, server, sizeof(name)-1);
X		name[sizeof(name)-1] = '\0';
X	    }
X	sprintf(logname, "%s.b-out", name);
X	truncate(logname, 0);
X	if ((logfd[0] = open(logname, O_WRONLY|O_CREAT,0600)) == -1)
X		logopen_error();
X	sprintf(logname, "%s.m-out", name);
X	truncate(logname, 0);
X	if((logfd[1] = open(logname, O_WRONLY|O_CREAT,0600)) == -1)
X		logopen_error();
X	sprintf(logname, "%s.b-in", name);
X	truncate(logname, 0);
X	if((logfd[2] = open(logname, O_WRONLY|O_CREAT,0600)) == -1)
X		logopen_error();
X	sprintf(logname, "%s.m-in", name);
X	truncate(logname, 0);
X	if((logfd[3] = open(logname, O_WRONLY|O_CREAT,0600)) == -1)
X		logopen_error();
X}
X
Xlogopen_error()
X{
X	perror("open");
X	exit(-1);
X}
X
Xcloselogs()
X{
X	int	i;
X
X	for (i = 0; i < 4; i++)
X		close(logfd[i]);
X}
END-of-stat/flowmon.c
echo x - stat/genplot
sed 's/^X//' >stat/genplot << 'END-of-stat/genplot'
X#!/usr/bin/perl
X
X$sccsid = "@(#)genplot	1.1 12/17/92 (C)1992 Darren Reed";
X
Xprint "Generate plot for which server ? ";
X$server=<STDIN>;
Xchop($server);
Xif ($server =~ /[^\.]*\./) {
X	$server =~ s/([^\.]*).*/$1/;
X}
X
X
Xopen(PLT,">$server.plt") || die $!;
Xopen(WC,"wc -l $server.b-in|") || die $!;
X$txt = <WC>;
X@s = split(' ',$txt);
X$lines = @s[0];
Xclose(WC);
Xprint "$lines samples.\n";
X
Xprint PLT "set samples $lines\n";
Xprint PLT "set autoscale y; set grid; set border\n";
Xprint PLT "set xrange[0:$lines]\n";
Xprint PLT "set xlabel \"Time\"\n";
Xprint PLT "plot [0:$lines] '$server.b-in' w lines, '$server.b-out' w lines\n";
Xprint PLT "pause -1\n";
Xprint PLT "plot [0:$lines] '$server.m-in' w lines, '$server.m-out' w lines\n";
Xprint PLT "pause -1\n";
Xclose(PLT);
Xprint "File for gnuplot: $server.plt\n";
Xprint "Datafiles: $server.b-in $server.b-out $server.m-in $server.m-out\n";
X
END-of-stat/genplot
echo x - stat/runplot
sed 's/^X//' >stat/runplot << 'END-of-stat/runplot'
X#!/usr/bin/perl
X$sccsid = "@(#)runplot	1.2 12/18/92 (C)1992 Darren Reed";
X
Xsub ReadName {
X	$_ = <>;
X	chop;
X	if (length < 1) {
X		exit(0);
X	}
X	return $_;
X}
X
X	
Xprint "\nIRC Traffic Monitor\n-------------------\n";
Xprint "Server to connect monitor to > ";
X$server = &ReadName();
Xprint "Server to gather stats on > ";
X$remserver = &ReadName();
Xprint "Nick for monitor to use > ";
X$nick = &ReadName();
Xprint "Number of samples to collect > ";
X$samples = &ReadName();
Xprint "Interval between samples (eg 1s or 1, 2m, 1h) > ";
X$_ = &ReadName();
X
Xif (/[1-9]*m$/) {
X	chop;
X	$interval = $_ * 60;
X} elsif (/[1-9]*h$/) {
X	chop;
X	$interval = $_ * 3600;
X} elsif (/[1-9]*$/) {
X	$interval = $_;
X} elsif ($intv =~ /[1-9]*s$/) {
X	chop;
X	$interval = $_;
X} else {
X	print "Unkown quantity \"$_\"\n";
X	exit(-1);
X}
X
Xprint "Server       : $server\nTarget Server: $remserver\n";
Xprint "Nick         : $nick\nNo. of samples : $samples\n";
Xprint "Interval     : $interval\n";
Xprint "\nOk ? (y/N) ";
X
X$_ = &ReadName();
X
Xif (/^y|^Y/) {
X	system("flowmon -dlsrnwz $server $remserver $nick $interval $samples");
X	print "Monitoring started.\n";
X	print "Come back later and run \"genplot\" to generate gnuplot file\n";
X}
END-of-stat/runplot
exit

Newsgroups: alt.irc