This is dialmon.c in view mode; [Download] [Up]
/* ** Copyright (c) 1991 Bolt Beranek and Newman, Inc. ** All rights reserved. ** ** Redistribution and use in source and binary forms are permitted ** provided that: (1) source distributions retain this entire copyright ** notice and comment, and (2) distributions including binaries display ** the following acknowledgement: ``This product includes software ** developed by Bolt Beranek and Newman, Inc. and CREN/CSNET'' in the ** documentation or other materials provided with the distribution and in ** all advertising materials mentioning features or use of this software. ** Neither the name of Bolt Beranek and Newman nor CREN/CSNET may be used ** to endorse or promote products derived from this software without ** specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED ** WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include <stdio.h> #include <ctype.h> #include <curses.h> #include <signal.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/file.h> #include <sys/ioctl.h> #include <sys/time.h> #include <netinet/in.h> #include <net/if.h> #include <sys/dk.h> #include <netdb.h> #include <arpa/inet.h> #include <sys/param.h> #include <netinet/in_systm.h> #include <netinet/ip.h> #include <net/if_du.h> #include "dialmon.h" /* Shut up, lint */ #define Refresh (void)refresh #define Wrefresh (void)wrefresh #define Wclear (void)wclear #define Wclrtoeol (void)wclrtoeol #define Wprintw (void)wprintw #define Wmove (void)wmove /* Configuration data. */ #define MAXRECS 40 /* Max # of types of log records */ #define MAXPLOTS 49 /* # of plots per log record graph */ #define MAXWINDOWS 4 /* # of available windows */ #define HOSTNAMESIZE 25 /* Size of a host name */ #define MAXINTERVALS 1 /* intervals/average */ #define HOSTNAMELEN 128 /* Window numbers */ #define W_GLOBAL 0 /* Site-wide statistics */ #define W_LINE 1 /* Active line statistics */ #define W_TOTAL 2 /* Active line totals */ #define W_HELP 3 /* Help function */ #define NOTSET -1 /* When a yloc is not in use */ #define LINE_NOTCONF 'N' /* When a serial line is not configured */ #define MAXREQUEST 25 /* Maximum size of a request */ /* Fields in status structures, and statistics computed from them. */ #define R_CIPUP 0 #define R_CIPLN 1 #define R_COPUP 2 #define R_COPLN 3 #define R_CPSIP 4 #define R_CPRIP 5 #define R_CTPBUSY 6 #define R_CTPIDLE 7 #define R_CCHS 8 #define R_CCHR 9 #define R_gCHS 10 /* Global chars sent */ #define R_gCHR 11 /* Global chars received */ #define R_aCHS 12 /* Average chars sent */ #define R_aPKTS 13 /* Average pkts sent */ #define R_aCHPKTS 14 /* Average chars/pkt sent */ #define R_aCHR 15 /* Average chars received */ #define R_aPKTR 16 /* Average pkts received */ #define R_aCHPKTR 17 /* Average chars/pkt received */ #define R_aPCTESC 18 /* Average percent of esc chars sent */ #define R_STATE 19 /* State of the du_softc interface */ #define R_ESCR 20 /* number of esc chars received */ #define R_ESCS 21 /* number of esc chars sent */ #define R_IERROR 22 #define R_OERROR 23 #define R_gIERROR 24 /* Global */ #define R_gOERROR 25 /* Global */ /* A dial log record. */ typedef struct _DIALLOGREC { int yloc; /* Where to graph packet counts with the * corresponding attributes */ int Count; /* new packet count */ int Old; /* old count, so can update plots without * redrawing entire plot */ float fCount; /* New count packets as a float */ int Line; /* line number, 0 for global stats */ int Window; /* window to display this record in */ } DIALLOGREC; /* Averages */ typedef struct _AVERAGES { int chs; /* Characters sent */ int pkts; /* Packets sent */ int chr; /* Characters received */ int pktr; /* Packets received */ int sescs; /* Escape chars sent */ int rescs; /* Escape chars received */ int ierror; /* Input errors */ int oerror; /* Output errors */ int sec; /* Seconds between getting new data */ } AVERAGES; static AVERAGES Averages[MAX_NDU][MAXINTERVALS]; static char HostName[HOSTNAMELEN]; static char OtherHost[MAX_NDU][HOSTNAMESIZE]; static DIALSTATS StatsCur; static DIALSTATS StatsOld; static DIALSTATS StatsReset; static int fd; /* Descriptor connected to server */ static int CurrLine; /* Current serial line */ static int freset; /* Using reset totals? */ static int CurrWind; /* Current window */ static int maxyloc; /* largest y-local in graph window */ static int newdata; /* Index into Averages */ static int cursed; /* Curses set up? */ static WINDOW *Wgraph; static WINDOW *Whelp; static WINDOW *Wprompt; static WINDOW *Wtitle; static WINDOW *Wtypes; /* Pointers to log records which hold 5 sec data, logical line number this * log record applies to and the y-position where this data is printed. */ static DIALLOGREC *Records[MAXRECS]; /* State of du_softc interface. One message/bit. */ static char *DUStateMessages[] = { "ESCAPED", "OACTIVE", "", "", "LWAITING", "LACTIVE", "LDOWN", "FAILCALL", "MONITORON" }; /* Type descriptions. */ static char *TypeDescriptions[] = { "Pkts received from IP", "Pkts from remote site", "Pkts passed to IP", "Pkts sent to remote site", "Pkts passed to IP", "Pkts received from IP", "Status of line", "Tty busy", "Percent tty idle", "Characters sent", "Characters received", "Characters sent", "Characters received", "Avg chars sent/sec", "Avg pkts sent/sec", "Avg chars/pkt sent", "Avg chars received/sec", "Avg pkts received/sec", "Avg chars/pkt received", "Avg chars received/interrupt", "Avg chars escaped", "Escape chars received", "State of interface", "Escape chars sent", "Line Input Errors", "Line Output Errors", "Global Input Errors", "Global Output Errors" }; extern char *progname; extern char *dip_release(); extern int errno; extern int h_errno; extern int optind; extern char *ctime(); extern char *malloc(); extern char *optarg; extern char *strchr(); extern char *strcpy(); extern char *strerror(); extern char *strncpy(); extern time_t time(); extern unsigned int sleep(); extern void bcopy(); extern void exit(); /* ** Normal exit. */ static void ext() { (void)close(fd); if (cursed) endwin(); (void)printf("\n"); exit(0); } /* ** Print error and exit. */ static void fatal(text) char *text; { int oerrno; oerrno = errno; (void)close(fd); if (cursed) { Wmove(stdscr, COLS - 1, LINES - 1); Refresh(); endwin(); } (void)fprintf(stderr, "\n\n%s, %s\n", text, strerror(oerrno)); exit(1); } /* ** Compute the average stats for a line. */ static void computeavgs(i) int i; { int j; float *fp1; float *fp2; float time; AVERAGES *aptr; /* Get the pointer to the new data. */ aptr = &Averages[i][newdata]; aptr->chs = Records[R_CCHS][i].Count; aptr->pkts = Records[R_CPSIP][i].Count; aptr->chr = Records[R_CCHR][i].Count; aptr->pktr = Records[R_CPRIP][i].Count; aptr->rescs = Records[R_ESCR][i].Count; aptr->sescs = Records[R_ESCS][i].Count; aptr->sec = StatsCur.when - StatsOld.when; aptr->ierror = Records[R_IERROR][i].Count; aptr->oerror = Records[R_OERROR][i].Count; /* Total over MAXINTERVALS intervals. */ Records[R_aCHS][i].fCount = 0.0; Records[R_aPKTS][i].fCount = 0.0; Records[R_aCHR][i].fCount = 0.0; Records[R_aPKTR][i].fCount = 0.0; Records[R_aPCTESC][i].fCount = 0.0; for (time = 0.0, j = 0; j < MAXINTERVALS; j++) { aptr = &Averages[i][j]; Records[R_aCHS][i].fCount += aptr->chs; Records[R_aPKTS][i].fCount += aptr->pkts; Records[R_aCHR][i].fCount += aptr->chr; Records[R_aPKTR][i].fCount += aptr->pktr; Records[R_aPCTESC][i].fCount += aptr->rescs; time += aptr->sec; } /* Compute average chars sent/packet. */ fp1 = &Records[R_aCHS][i].fCount; fp2 = &Records[R_aPKTS][i].fCount; Records[R_aCHPKTS][i].fCount = *fp2 ? *fp1 / *fp2 : 0.0; if (time) *fp2 /= time; /* Factor in escape characters sent. */ fp2 = &Records[R_aPCTESC][i].fCount; *fp2 = *fp1 ? (*fp2 / *fp1 ) * 100. : 0.0; if (time) *fp1 /= time; /* Computer average chars received/packet. */ fp1 = &Records[R_aCHR][i].fCount; fp2 = &Records[R_aPKTR][i].fCount; Records[R_aCHPKTR][i].fCount = *fp2 ? *fp1 / *fp2 : 0.0; if (time) { *fp1 /= time; *fp2 /= time; } } /* ** Compute the differences between the old and new values of the stats ** structures and save the results in a log record. Since not all ** lines need have been configured on a host, set it to NOTCONF if ** necessary. The StatsReset variable holds all totals since the last ** reset command. */ static void computediffs() { LINESTATS *old; LINESTATS *cur; LINESTATS *res; int i; struct hostent *hp; unsigned long *paddr; /* global stats */ StatsReset.ipup += Records[R_CIPUP]->Count = StatsCur.ipup - StatsOld.ipup; StatsOld.ipup = StatsCur.ipup; StatsReset.ipln += Records[R_CIPLN]->Count = StatsCur.ipln - StatsOld.ipln; StatsOld.ipln = StatsCur.ipln; StatsReset.opup += Records[R_COPUP]->Count = StatsCur.opup - StatsOld.opup; StatsOld.opup = StatsCur.opup; StatsReset.opln += Records[R_COPLN]->Count = StatsCur.opln - StatsOld.opln; StatsOld.opln = StatsCur.opln; /* line stats */ old = StatsOld.ln; cur = StatsCur.ln; res = StatsReset.ln; for (i = 0; i < StatsCur.ndu; i++, old++, res++) { if (cur->ln == i) { /* line i is configured and new data was sent */ Records[R_STATE][i].Count = cur->flags; res->cchr += Records[R_CCHR][i].Count = cur->cchr - old->cchr; old->cchr = cur->cchr; /* sum up characters received for global stats */ Records[R_gCHR]->Count += Records[R_CCHR][i].Count; res->cchs += Records[R_CCHS][i].Count = cur->cchs - old->cchs; old->cchs = cur->cchs; /* sum up characters sent for global stats */ Records[R_gCHS]->Count += Records[R_CCHS][i].Count; res->cpsip += Records[R_CPSIP][i].Count = cur->cpsip - old->cpsip; old->cpsip = cur->cpsip; res->cprip += Records[R_CPRIP][i].Count = cur->cprip - old->cprip; old->cprip = cur->cprip; res->ierror += Records[R_IERROR][i].Count = cur->ierror - old->ierror; old->ierror = cur->ierror; Records[R_gIERROR]->Count += Records[R_IERROR][i].Count; res->oerror += Records[R_OERROR][i].Count = cur->oerror - old->oerror; old->oerror = cur->oerror; Records[R_gOERROR]->Count += Records[R_OERROR][i].Count; /* Handle wrap-around */ Records[R_CTPBUSY][i].fCount = cur->ctpbusy < old->ctpbusy ? (float)((unsigned int)cur->ctpbusy - (unsigned int)old->ctpbusy) : (float)(cur->ctpbusy - old->ctpbusy); res->ctpbusy += (int)Records[R_CTPBUSY][i].fCount; old->ctpbusy = cur->ctpbusy; /* Counter wrapped around. */ Records[R_CTPIDLE][i].fCount = cur->ctpidle < old->ctpidle ? (unsigned int)cur->ctpidle - (unsigned int)old->ctpidle : cur->ctpidle - old->ctpidle; res->ctpidle += (int)Records[R_CTPIDLE][i].fCount; old->ctpidle = cur->ctpidle; res->resc += Records[R_ESCR][i].Count = cur->resc - old->resc; old->resc = cur->resc; res->sesc += Records[R_ESCS][i].Count = cur->sesc - old->sesc; old->sesc = cur->sesc; /* Get the nost on the other end of the line, if different. */ cur->dest.s_addr = ntohl(cur->dest.s_addr); if (old->dest.s_addr != cur->dest.s_addr) { paddr = &cur->dest.s_addr; if (*paddr) { if (hp = gethostbyaddr(paddr, sizeof *paddr, AF_INET)) { (void)strncpy(OtherHost[i], hp->h_name, sizeof OtherHost[i] - 1); OtherHost[i][HOSTNAMESIZE - 1] = '\0'; } else (void)strcpy(OtherHost[i], inet_ntoa(*paddr)); } else (void)strcpy(OtherHost[i], "UNINITIALIZED"); res->dest.s_addr = old->dest.s_addr = cur->dest.s_addr; } res->ln = old->ln = cur->ln; cur->ln = LINE_NOTCONF; cur++; computeavgs(i); } else { /* line i not configured, no new data was sent */ res->ln = old->ln = LINE_NOTCONF; if ((CurrWind == W_LINE || CurrWind == W_TOTAL) && CurrLine == i) LineNotConf(i); } } /* finished storing this set of data, increment for the next set */ if (++newdata >= MAXINTERVALS) newdata = 0; /* save current time value */ StatsOld.when = StatsCur.when; } /* ** Convert fields in struct to host order */ FromNetworkOrder(sp) DIALSTATS *sp; { int i; LINESTATS *lsp; sp->when = ntohl((unsigned long)sp->when); for (i = 0; i < 3; i++) sp->avenrun[i] = ntohl(sp->avenrun[i]); for (i = 0; i < CPUSTATES; i++) sp->cputime[i] = ntohl(sp->cputime[i]); sp->ipup = ntohl(sp->ipup); sp->ipln = ntohl(sp->ipln); sp->opln = ntohl(sp->opln); sp->opup = ntohl(sp->opup); sp->ndu = ntohl(sp->ndu); for (lsp = sp->ln; lsp < &sp->ln[sp->ndu]; lsp++) { lsp->cchr = ntohl(lsp->cchr); lsp->cchs = ntohl(lsp->cchs); lsp->cpsip = ntohl(lsp->cpsip); lsp->cprip = ntohl(lsp->cprip); lsp->flags = ntohl(lsp->flags); lsp->ctpbusy = ntohl(lsp->ctpbusy); lsp->ctpidle = ntohl(lsp->ctpidle); lsp->sesc = ntohl(lsp->sesc); lsp->resc = ntohl(lsp->resc); lsp->dest.s_addr = ntohl(lsp->dest.s_addr); lsp->ierror = ntohl(lsp->ierror); lsp->oerror = ntohl(lsp->oerror); lsp->ln = ntohl(lsp->ln); } } /* ** Compute tty percents per line. */ static void computettypercent() { float ttysum; float percent; int i; DIALLOGREC *busyp; DIALLOGREC *idlep; busyp = Records[R_CTPBUSY]; idlep = Records[R_CTPIDLE]; for (i = 0; i < MAX_NDU; i++, busyp++, idlep++) { ttysum = busyp->fCount + idlep->fCount; if (ttysum == 0.0) continue; percent = (busyp->fCount / ttysum) * 100.0; busyp->fCount = percent; percent = (idlep->fCount / ttysum) * 100.0; idlep->fCount = percent; } } /* ** Read data from the socket. */ static void readsocket(fd) int fd; { char *timep; char *p; int n; int ntot; unsigned long size; /* Find out how much to read. */ if (read(fd, (caddr_t)&size, sizeof size) != sizeof size) fatal("Can't read size of stats"); size = ntohl(size); /* Read in all the data. */ for (ntot = 0, p = (char *)&StatsCur; ntot < size; ntot += n, p += n) if ((n = read(fd, p, size - ntot)) <= 0) fatal("Read from server failed"); FromNetworkOrder(&StatsCur); /* Update the time. */ timep = ctime(&StatsCur.when); if (p = strchr(timep, '\n')) *p = '\0'; Wmove(Wtitle, 1, 36); Wprintw(Wtitle, "%s", timep); /* Update the status fields. */ UpdateStatus(); Wrefresh(Wtitle); /* Compute differences, update the records. */ computediffs(); computettypercent(); /* Update the graphs for the displayed window */ if (CurrWind == W_GLOBAL || CurrWind == W_LINE) UpdateGraphs(); else if (CurrWind == W_TOTAL) UpdateTotals(); Wrefresh(Wprompt); } /* ** Process the request read from the terminal. */ static void processrequest(buff) char *buff; { char *p; int arg; int c; for (arg = NOTSET, p = buff; *p && isspace(*p); p++) ; for (c = *p; *++p && isspace(*p); ) ; switch (c) { case 'g': globalstats(); return; case 'q': ext(); /* NOTREACHED */ case 'l': if (isdigit(*p) && (arg = atoi(p)) < MAX_NDU) { linestats(arg); return; } case 't': if (*p == 'g') { totalstats(NOTSET); return; } if (isdigit(*p) && (arg = atoi(p)) < MAX_NDU) { totalstats(arg); return; } break; case 'r': switch (*p) { case 'n': reset(FALSE); return; case 'y': reset(TRUE); return; } break; } helpwindow(); } /* ** Read input from the user. */ readterminal() { static char buff[MAXREQUEST]; static int curpos; int count; int done; int i; char *p; /* Refresh the screen, read if there is any input. */ Wrefresh(Wprompt); if (ioctl(0, FIONREAD, (caddr_t)&count) >= 0 && count <= 0) return; p = &buff[curpos]; for (done = FALSE, i = 0; i < count && !done; i++) { switch (*p = wgetch(Wprompt)) { default: if (p < &buff[sizeof buff - 1]) p++; break; case '\n': case '\r': *p = '\0'; done = TRUE; break; case '\177': case '\b': if (p > buff) p--; break; case '\f': Wrefresh(curscr); break; } } /* Redraw the line. */ curpos = p - buff; Wclear(Wprompt); Wprintw(Wprompt, " ? "); for (p = buff, i = 0; i < curpos; i++, p++) Wprintw(Wprompt, "%c", *p); Wrefresh(Wprompt); /* If we got a full line, execute it. */ if (done) { curpos = 0; processrequest(buff); Wclear(Wprompt); Wprintw(Wprompt, " ? "); Wrefresh(Wprompt); } } /* ** Allocate storage for global log records */ static void AllocGStor(i, yloc) int i; int yloc; { DIALLOGREC *drp; if (yloc >= maxyloc) fatal("Screen too small (global stats)"); if ((drp = (DIALLOGREC *)malloc(sizeof *drp)) == NULL) fatal("Can't malloc storage for global log record"); drp->yloc = yloc; drp->Old = 0; drp->Count = 0; drp->fCount = 0.0; drp->Line = 0; drp->Window = W_GLOBAL; Records[i] = drp; } /* ** Allocate storage for line log records */ static void AllocLStor(i, yloc) int i; int yloc; { DIALLOGREC *drp; int j; if (yloc >= maxyloc) fatal("Screen too small (line stats)"); if ((drp = (DIALLOGREC *)malloc(MAX_NDU * sizeof *drp)) == NULL) fatal("Can't malloc storage for line log record"); for (Records[i] = drp, j = 0; j < MAX_NDU; j++, drp++) { drp->yloc = yloc; drp->Old = 0; drp->Count = 0; drp->fCount = 0.0; drp->Line = j; drp->Window = W_LINE; } } /* ** Initialize data structures. */ static void initdata() { int i; int j; LINESTATS *old; LINESTATS *cur; LINESTATS *res; struct timeval tv; AVERAGES *aptr; CurrWind = NOTSET; CurrLine = NOTSET; freset = FALSE; newdata = 0; maxyloc = LINES - 5; /* 4 lines at top, one for prompt at bottom */ StatsCur.when = gettimeofday(&tv, (struct timezone *)NULL) < 0 ? 0 : tv.tv_sec; StatsCur.avenrun[0] = 0; StatsCur.avenrun[1] = 0; StatsCur.avenrun[2] = 0; for (i = 0; i < CPUSTATES; i++) StatsCur.cputime[i] = 0; StatsReset.ipup = StatsCur.ipup = StatsOld.ipup = 0; StatsReset.ipln = StatsCur.ipln = StatsOld.ipln = 0; StatsReset.opln = StatsCur.opln = StatsOld.opln = 0; StatsReset.opup = StatsCur.opup = StatsOld.opup = 0; old = StatsOld.ln; cur = StatsCur.ln; res = StatsReset.ln; for (i = 0; i < StatsCur.ndu; i++, cur++, old++, res++) { /* Assume all lines configured, if we found out it's not, then * we will save the correct value in StatsOld. */ cur->ln = LINE_NOTCONF; res->ln = old->ln = i; res->dest.s_addr = cur->dest.s_addr = old->dest.s_addr = 0L; res->cchr = cur->cchr = old->cchr = 0; res->cchs = cur->cchs = old->cchs = 0; res->cpsip = cur->cpsip = old->cpsip = 0; res->cprip = cur->cprip = old->cprip = 0; res->ierror = cur->ierror = old->ierror = 0; res->oerror = cur->oerror = old->oerror = 0; res->flags = cur->flags = old->flags = 0; res->ctpbusy = cur->ctpbusy = old->ctpbusy = 0; res->ctpidle = cur->ctpidle = old->ctpidle = 0; res->resc = cur->resc = old->resc = 0; res->sesc = cur->sesc = old->sesc = 0; /* Set up the averages table. */ for (j = 0; j < MAXINTERVALS; j++) { aptr = &Averages[i][j]; aptr->chs = 0; aptr->pkts = 0; aptr->chr = 0; aptr->pktr = 0; aptr->rescs = 0; aptr->sescs = 0; aptr->sec = 0; } (void)strcpy(OtherHost[i], "UNINITIALIZED"); } /* Get storage for loc records, assign y locations. */ i = 1; AllocGStor(R_CIPUP, i++); AllocGStor(R_CIPLN, i++); AllocGStor(R_COPUP, i++); AllocGStor(R_COPLN, i++); AllocGStor(R_gIERROR, i++); AllocGStor(R_gOERROR, i++); AllocGStor(R_gCHS, i); AllocGStor(R_gCHR, i++); /* Local storage. Note that some share the same location. */ i = 1; AllocLStor(R_CPSIP, i++); AllocLStor(R_CPRIP, i++); AllocLStor(R_CTPBUSY, i); AllocLStor(R_CTPIDLE, NOTSET); AllocLStor(R_aPCTESC, i++); AllocLStor(R_ESCS, NOTSET); AllocLStor(R_ESCR, NOTSET); AllocLStor(R_CCHS, i); AllocLStor(R_CCHR, i++); AllocLStor(R_aCHPKTS, i); AllocLStor(R_aCHPKTR, i++); AllocLStor(R_aCHS, i); AllocLStor(R_aCHR, i++); AllocLStor(R_aPKTS, i); AllocLStor(R_aPKTR, i++); AllocLStor(R_IERROR, i++); AllocLStor(R_OERROR, i++); AllocLStor(R_STATE, i++); } /* ** Blank out a row of stars. */ removeplot(oldx, newx, yloc) int oldx; int newx; int yloc; { for (; oldx > newx; oldx--) mvwaddch(Wgraph, yloc, oldx - 1, ' '); } /* ** Add a row of stars. */ addplot(oldx, newx, yloc) int oldx; int newx; int yloc; { while (++oldx <= newx) mvwaddch(Wgraph, yloc, oldx - 1, '*'); } /* * globalstats -- set-up global statistics window for monitoring */ globalstats() { int line; /* window already global, don't change it */ if (CurrWind == W_GLOBAL) return; CurrWind = W_GLOBAL; Wclear(Whelp); Wclear(Wtypes); Wclear(Wgraph); Wrefresh(Whelp); /* print title and scale */ line = 0; Wmove(Wtypes, line, 0); Wprintw(Wtypes, "GLOBAL STATISTICS:"); Wmove(Wgraph, line++, 0); Wprintw(Wgraph, "1 10 20 30 40"); InitDisplay(R_CIPUP, 0); InitDisplay(R_CIPLN, 0); InitDisplay(R_COPUP, 0); InitDisplay(R_COPLN, 0); InitDisplay(R_gCHS, 0); InitDisplay(R_gCHR, 0); InitDisplay(R_gIERROR,0); InitDisplay(R_gOERROR,0); Wrefresh(Wtypes); Wrefresh(Wgraph); UpdateGraphs(); } /* ** Set-up line statistics window for monitoring. */ linestats(ln) int ln; { int line; if (CurrWind == W_LINE && CurrLine == ln) return; if (StatsOld.ln[ln].ln == LINE_NOTCONF) { LineNotConf(ln); return; } CurrWind = W_LINE; CurrLine = ln; Wclear(Whelp); Wclear(Wtypes); Wclear(Wgraph); Wrefresh(Whelp); line = 0; Wmove(Wtypes, line, 0); Wprintw(Wtypes, "LINE TO %.20s:", OtherHost[ln]); Wmove(Wgraph, line++, 0); Wprintw(Wgraph, "1 10 20 30 40"); InitDisplay(R_CPSIP, ln); InitDisplay(R_CPRIP, ln); InitDisplay(R_CTPBUSY, ln); InitDisplay(R_aPCTESC, ln); InitDisplay(R_CCHS, ln); InitDisplay(R_CCHR, ln); InitDisplay(R_aCHPKTS, ln); InitDisplay(R_aCHPKTR, ln); InitDisplay(R_aCHS, ln); InitDisplay(R_aCHR, ln); InitDisplay(R_aPKTS, ln); InitDisplay(R_aPKTR, ln); InitDisplay(R_STATE, ln); InitDisplay(R_IERROR, ln); InitDisplay(R_OERROR, ln); Wrefresh(Wtypes); Wrefresh(Wgraph); UpdateGraphs(); } /* ** Set-up total statistics window for monitoring. */ totalstats(ln) int ln; { int line; if (CurrWind == W_TOTAL && CurrLine == ln) return; if (ln != NOTSET && StatsOld.ln[ln].ln == LINE_NOTCONF) { LineNotConf(ln); return; } CurrWind = W_TOTAL; CurrLine = ln; Wclear(Whelp); Wclear(Wtypes); Wclear(Wgraph); Wrefresh(Whelp); /* Pint title and scale. */ line = 0; Wmove(Wtypes, line, 0); if (ln == NOTSET) Wprintw(Wtypes, "GLOBAL TOTALS:"); else Wprintw(Wtypes, "LINE TOTALS TO %.20s:", OtherHost[ln]); Wmove(Wgraph, line++, 0); Wprintw(Wgraph, " TOTAL COUNTS"); if (CurrLine == NOTSET) { /* Global records. */ InitDisplay(R_CIPUP, 0); InitDisplay(R_CIPLN, 0); InitDisplay(R_COPUP, 0); InitDisplay(R_COPLN, 0); InitDisplay(R_gCHS, 0); InitDisplay(R_gCHR, 0); InitDisplay(R_gIERROR,0); InitDisplay(R_gOERROR,0); } else { /* Line records. */ InitDisplay(R_CPSIP, ln); InitDisplay(R_CPRIP, ln); InitDisplay(R_CTPBUSY, ln); InitDisplay(R_aPCTESC, ln); InitDisplay(R_CCHS, ln); InitDisplay(R_CCHR, ln); InitDisplay(R_aCHPKTS, ln); InitDisplay(R_aCHPKTR, ln); InitDisplay(R_IERROR, ln); InitDisplay(R_OERROR, ln); } Wrefresh(Wtypes); Wrefresh(Wgraph); UpdateTotals(); } /* ** If flag is TRUE, reset totals to 0 and start accumulating, else use ** real totals as sent from monitored site. */ reset(flag) int flag; { int i; LINESTATS *res; freset = flag; if (freset) { StatsReset.ipup = 0; StatsReset.ipln = 0; StatsReset.opup = 0; StatsReset.opln = 0; for (res = StatsReset.ln, i = 0; i < StatsCur.ndu; i++, res++) { res->cchr = 0; res->cchs = 0; res->cpsip = 0; res->cprip = 0; res->ierror = 0; res->oerror = 0; res->ctpbusy = 0; res->ctpidle = 0; res->resc = 0; res->sesc = 0; } } if (CurrWind == W_TOTAL) UpdateTotals(); } /* ** Help function, displays the windows that are available. */ helpwindow() { int line; CurrWind = W_HELP; Wclear(Whelp); Wclear(Wtypes); Wclear(Wgraph); Wrefresh(Wtypes); Wrefresh(Wgraph); /* Print title. */ line = 0; Wmove(Whelp, line++, 0); Wprintw(Whelp, "COMMANDS:\n"); Wprintw(Whelp, " l#\t\tSerial line statistics for line #\n"); Wprintw(Whelp, " t# | tg\tTotal statistics for line # or g for global\n"); Wprintw(Whelp, " h or ?\tHelp\n"); Wprintw(Whelp, " ^L\t\tRedraw\n"); Wprintw(Whelp, " ry\t\tUse the reset totals\n"); Wprintw(Whelp, " rn\t\tUse totals since last reboot\n"); Wprintw(Whelp, " q\t\tQuit\n"); Wrefresh(Whelp); } /* ** Clear screen and print message if the line to be monitored is not ** configured. */ LineNotConf(ln) int ln; { Wclear(Whelp); Wclear(Wgraph); Wclear(Wtypes); Wrefresh(Whelp); Wmove(Wtypes, 0, 0); Wprintw(Wtypes, "SERIAL LINE %d NOT CONFIGURED", ln); CurrWind = W_HELP; Wrefresh(Wtypes); Wrefresh(Wgraph); } /* ** Initialize one type of log record's data structure for the line to be ** displayed and display the title. */ InitDisplay(t, l) int t; int l; { DIALLOGREC *drp; drp = &Records[t][l]; drp->Old = 0; switch (t) { default: Wmove(Wtypes, drp->yloc, 0); Wprintw(Wtypes, "%s", TypeDescriptions[t]); mvwaddch(Wtypes, drp->yloc, 29, ':'); break; case R_CTPBUSY: case R_CCHS: case R_gCHS: case R_aCHS: case R_aPKTS: case R_aCHPKTS: Wmove(Wtypes, drp->yloc, 0); Wprintw(Wtypes, "%s", TypeDescriptions[t]); break; case R_aPCTESC: case R_CCHR: case R_gCHR: case R_aCHR: case R_aPKTR: case R_aCHPKTR: Wmove(Wgraph, drp->yloc, 0); Wprintw(Wgraph, "%s", TypeDescriptions[t]); break; } } /* ** Update the system status lines. */ UpdateStatus() { float pct[CPUSTATES]; int i; int w; /* sum up the time spent in each cpu state and initialize pct */ for (w = 0, i = 0; i < CPUSTATES; i++) { pct[i] = 0.0; w += StatsCur.cputime[i]; } /* compute the percent of time spent in each cpu state */ if (w) for (i = 0; i < CPUSTATES; i++) pct[i] = ((float)StatsCur.cputime[i] / (float)w) * 100.0; Wmove(Wtitle, 2, 26); Wprintw(Wtitle, "%6.2f", pct[0]); Wmove(Wtitle, 2, 42); Wprintw(Wtitle, "%6.2f", pct[2]); Wmove(Wtitle, 2, 56); Wprintw(Wtitle, "%6.2f", pct[1]); Wmove(Wtitle, 2, 70); Wprintw(Wtitle, "%6.2f", pct[3]); Wmove(Wtitle, 3, 37); Wprintw(Wtitle, "%5.2f", ((float)StatsCur.avenrun[0]) / 100.); Wmove(Wtitle, 3, 44); Wprintw(Wtitle, "%5.2f", ((float)StatsCur.avenrun[1]) / 100.); Wmove(Wtitle, 3, 51); Wprintw(Wtitle, "%5.2f", ((float)StatsCur.avenrun[2]) / 100.); } /* ** Update the plots in the active window. Does not update the Old of ** any log record not being displayed or that prints a floating point ** number. */ UpdateGraphs() { DIALLOGREC *drp; if (CurrWind == W_GLOBAL) { /* Update all global plots */ UpdatePlot(Records[R_CIPUP]); UpdatePlot(Records[R_CIPLN]); UpdatePlot(Records[R_COPUP]); UpdatePlot(Records[R_COPLN]); drp = Records[R_gCHS]; PrintNum(Wtypes, (unsigned long)drp->Count, drp->yloc, 17); drp->Old = drp->Count; drp = Records[R_gCHR]; PrintNum(Wgraph, (unsigned long)drp->Count, drp->yloc, 21); drp->Old = drp->Count; drp = Records[R_gIERROR]; PrintNum(Wgraph, (unsigned long)drp->Count, drp->yloc, 0); drp->Old = drp->Count; drp = Records[R_gOERROR]; PrintNum(Wgraph, (unsigned long)drp->Count, drp->yloc, 0); drp->Old = drp->Count; } else if (CurrWind == W_LINE) { /* Update the displayed line's stats */ Wmove(Wtypes, 0, 8); Wclrtoeol(Wtypes); Wmove(Wtypes, 0, 8); Wprintw(Wtypes, "%-.20s", OtherHost[CurrLine]); mvwaddch(Wtypes, 0, 29, ':'); UpdatePlot(&Records[R_CPSIP][CurrLine]); UpdatePlot(&Records[R_CPRIP][CurrLine]); drp = &Records[R_CTPBUSY][CurrLine]; PrintPct(Wtypes, drp->fCount, drp->yloc, 19); drp = &Records[R_CCHS][CurrLine]; PrintNum(Wtypes, (unsigned long)drp->Count, drp->yloc, 17); drp->Old = drp->Count; drp = &Records[R_aCHPKTS][CurrLine]; PrintNumf(Wtypes, drp->fCount, drp->yloc, 19); drp = &Records[R_aCHS][CurrLine]; PrintNumf(Wtypes, drp->fCount, drp->yloc, 19); drp = &Records[R_aPKTS][CurrLine]; PrintNumf(Wtypes, drp->fCount, drp->yloc, 19); drp = &Records[R_aPCTESC][CurrLine]; PrintPct(Wgraph, drp->fCount, drp->yloc, 28); drp = &Records[R_CCHR][CurrLine]; PrintNum(Wgraph, (unsigned long)drp->Count, drp->yloc, 26); drp->Old = drp->Count; drp = &Records[R_aCHPKTR][CurrLine]; PrintNumf(Wgraph, drp->fCount, drp->yloc, 28); drp = &Records[R_aCHR][CurrLine]; PrintNumf(Wgraph, drp->fCount, drp->yloc, 28); drp = &Records[R_aPKTR][CurrLine]; PrintNumf(Wgraph, drp->fCount, drp->yloc, 28); drp = &Records[R_STATE][CurrLine]; PrintState(Wgraph, drp->Count, drp->yloc, 5); drp = &Records[R_IERROR][CurrLine]; PrintNum(Wgraph, (unsigned long)drp->Count, drp->yloc, 0); drp = &Records[R_OERROR][CurrLine]; PrintNum(Wgraph, (unsigned long)drp->Count, drp->yloc, 0); } Wrefresh(Wtypes); Wrefresh(Wgraph); } /* ** Update the total counts being displayed */ UpdateTotals() { int i; int yloc; LINESTATS *lsp; DIALSTATS *sp; float ttysum; unsigned long totchr; unsigned long totchs; unsigned long totierror; unsigned long totoerror; sp = freset ? &StatsReset : &StatsOld; if (CurrLine == NOTSET) { /* Global totals */ PrintNum(Wgraph, sp->ipup, Records[R_CIPUP]->yloc, 0); PrintNum(Wgraph, sp->ipln, Records[R_CIPLN]->yloc, 0); PrintNum(Wgraph, sp->opup, Records[R_COPUP]->yloc, 0); PrintNum(Wgraph, sp->opln, Records[R_COPLN]->yloc, 0); /* total chars received and sent and total silo overflows */ totchs = 0; totchr = 0; totierror = 0; totoerror = 0; for (i = 0; i < StatsCur.ndu; i++) { totchs += sp->ln[i].cchs; totchr += sp->ln[i].cchr; totierror += sp->ln[i].ierror; totoerror += sp->ln[i].oerror; } PrintNum(Wtypes, totchs, Records[R_gCHS]->yloc, 17); PrintNum(Wgraph, totchr, Records[R_gCHR]->yloc, 21); PrintNum(Wgraph, totierror, Records[R_gIERROR]->yloc, 17); PrintNum(Wgraph, totoerror, Records[R_gOERROR]->yloc, 17); } else { /* Displayed line totals */ Wmove(Wtypes, 0, 15); Wclrtoeol(Wtypes); Wmove(Wtypes, 0, 15); Wprintw(Wtypes, "%-.14s", OtherHost[CurrLine]); mvwaddch(Wtypes, 0, 29, ':'); lsp = &sp->ln[CurrLine]; PrintNum(Wgraph, lsp->cpsip, Records[R_CPSIP][CurrLine].yloc, 0); PrintNum(Wgraph, lsp->cprip, Records[R_CPRIP][CurrLine].yloc, 0); PrintNum(Wgraph, lsp->ierror, Records[R_IERROR][CurrLine].yloc, 0); PrintNum(Wgraph, lsp->oerror, Records[R_OERROR][CurrLine].yloc, 0); if (ttysum = (float)(lsp->ctpbusy + lsp->ctpidle)) PrintPct(Wtypes, ((float)lsp->ctpbusy / (float)ttysum) * 100.0, Records[R_CTPBUSY][CurrLine].yloc, 19); else PrintPct(Wtypes, 0.0, Records[R_CTPBUSY][CurrLine].yloc, 19); yloc = Records[R_aPCTESC][CurrLine].yloc; Wmove(Wgraph, yloc, 0); Wclrtoeol(Wgraph); Wmove(Wgraph, yloc, 0); if (lsp->cchs) Wprintw(Wgraph, "%10.5f%%s %10.5f%%r", ((float)lsp->sesc / (float)lsp->cchs) * 100.0, ((float)lsp->resc / (float)lsp->cchr) * 100.0); else Wprintw(Wgraph, "%10.5f%%", (float)lsp->cchs); PrintNum(Wtypes, lsp->cchs, Records[R_CCHS][CurrLine].yloc, 17); PrintNum(Wgraph, lsp->cchr, Records[R_CCHR][CurrLine].yloc, 26); } Wrefresh(Wtypes); Wrefresh(Wgraph); } /* ** Print the number in the specified window at the specified location. */ PrintNum(wn, num, yloc, xloc) WINDOW *wn; unsigned long num; int yloc; int xloc; { Wmove(wn, yloc, xloc); Wclrtoeol(wn); Wmove(wn, yloc, xloc); Wprintw(wn, "%10d", num); } /* ** Print the float in the specified window at the specified location. */ PrintNumf(wn, num, yloc, xloc) WINDOW *wn; float num; int yloc; int xloc; { Wmove(wn, yloc, xloc); Wclrtoeol(wn); Wmove(wn, yloc, xloc); Wprintw(wn, "%8.2f ", num); } /* ** Print the percent of time a tty is busy. */ PrintPct(wn, num, yloc, xloc) WINDOW *wn; float num; int yloc; int xloc; { Wmove(wn, yloc, xloc); Wclrtoeol(wn); Wmove(wn, yloc, xloc); Wprintw(wn, "%8.2f %%", num); } PrintState(wn, state, yloc, xloc) WINDOW *wn; int state; int yloc; int xloc; { int i; int j; Wmove(wn, yloc, xloc); Wclrtoeol(wn); Wmove(wn,yloc,xloc); for (i = 0, j = 1; i < 32 ; i++, j <<= 1) if (state & j) Wprintw(wn, "%s ", DUStateMessages[i]); } /* ** Update the plot for the log record passed in. */ UpdatePlot(drp) DIALLOGREC *drp; { if (drp->Count == drp->Old) return; if (drp->Count < 0) { Wmove(Wgraph, drp->yloc, 0); Wclrtoeol(Wgraph); drp->Count = 0; drp->Old = 0; } else if (drp->Count > MAXPLOTS && drp->Old > MAXPLOTS) { PrintNum(Wgraph, (unsigned long)drp->Count, drp->yloc, 0); drp->Old = drp->Count; } else if (drp->Count > MAXPLOTS && drp->Old <= MAXPLOTS) { PrintNum(Wgraph, (unsigned long)drp->Count, drp->yloc, 0); drp->Old = drp->Count; } else if (drp->Count <= MAXPLOTS && drp->Old > MAXPLOTS) { Wmove(Wgraph, drp->yloc, 0); Wclrtoeol(Wgraph); addplot(0, drp->Count, drp->yloc); drp->Old = drp->Count; } else if (drp->Count > drp->Old) { /* add to old graph */ addplot(drp->Old, drp->Count, drp->yloc); drp->Old = drp->Count; } else if (drp->Count < drp->Old) { /* remove from old graph */ removeplot(drp->Old, drp->Count, drp->yloc); drp->Old = drp->Count; } } /* ** Look up a hostname, fill in the socket. Return -1 on error. */ static int hosttosocket(p, psin) char *p; struct sockaddr_in *psin; { struct hostent *hp; unsigned long w; bzero((caddr_t)psin, sizeof *psin); if (isdigit(*p)) { if ((w = inet_addr(p)) == (unsigned long)-1) return -1; psin->sin_addr.s_addr = w; psin->sin_family = AF_INET; return 0; } if ((hp = gethostbyname(p)) == NULL) { (void)fprintf(stderr, "Can't get address of \"%s\": ", p); #ifndef HOST_NOT_FOUND (void)fprintf(stderr, "Host not found\n"); #else switch (h_errno) { default: (void)fprintf(stderr, "Unknown error: %d\n", h_errno); break; case HOST_NOT_FOUND: (void)fprintf(stderr, "Host not found\n"); break; case TRY_AGAIN: (void)fprintf(stderr, "Try again later\n"); break; case NO_RECOVERY: (void)fprintf(stderr, "No recovery possible\n"); break; case NO_ADDRESS: (void)fprintf(stderr, "No IP address\n"); break; } #endif /* NOT_NOT_FOUND */ return -1; } psin->sin_family = hp->h_addrtype; bcopy(hp->h_addr, (char *)&psin->sin_addr, hp->h_length); return 0; } /* ** Print usage message and exit. */ static void usage(p) char *p; { (void)fprintf(stderr, "Usage error: %s\n", p); (void)fprintf(stderr, "Usage: %s [-g] [-l line] [-t g|line] [site]\n", progname); exit(1); } main(argc, argv) int argc; char *argv[]; { char *p; struct servent *sp; struct sockaddr_in sin; int i; int fildes[2]; char *timep; char buff[90]; fd_set readers; int win; int arg; int localpipe; /* Set defaults. */ win = W_GLOBAL; arg = NOTSET; localpipe = 0; /* Get options. */ while ((i = getopt(argc, argv, "gl:pt:")) != EOF) switch (i) { default: usage("Bad flag"); /* NOTREACHED */ case 'g': /* global stats, no arg */ win = W_GLOBAL; arg = NOTSET; break; case 'l': /* line stats, next arg must be ln # */ if (!isdigit(*optarg) || (arg = atoi(optarg)) > MAX_NDU) usage("Bad number for -l"); win = W_LINE; break; case 'p': localpipe = 1; break; case 't': /* total stats, next arg must be */ if (*optarg == 'g') { win = W_TOTAL; arg = NOTSET; } else if (!isdigit(*optarg) || (arg = atoi(optarg)) >= MAX_NDU) usage("Bad number for -t"); win = W_TOTAL; break; } argc -= optind; argv += optind; if (!localpipe) { switch (argc) { default: usage("Wrong number of arguments"); /* NOTREACHED */ case 0: (void)gethostname(HostName, sizeof HostName); break; case 1: (void)strcpy(HostName, *argv); break; } if ((sp = getservbyname(DIALMON_SERVICE, "tcp")) == NULL) { (void)fprintf(stderr, "Warning: %s/tcp unknown service; using port %d\n", DIALMON_SERVICE, DIALMON_DEFAULT_PORT); (void)sleep(2); } } else { if (argc) usage("Wrong number of arguments"); (void)gethostname(HostName, sizeof HostName); } /* Initialize screen */ (void)initscr(); (void)noecho(); (void)nonl(); (void)crmode(); cursed++; if (LINES < 24 || COLS < 80) fatal("Screen too small"); (void)signal(SIGINT, ext); Wtitle = newwin(4, 0, 0, 0); /* 4 lines at top */ Wgraph = newwin(maxyloc, COLS - 30, 4, 30); /* 14 lines at center left*/ Wtypes = newwin(maxyloc, 30, 4, 0); /* 14 lines at center right */ Wprompt = newwin(1, 0, LINES - 1, 0); Whelp = newwin(maxyloc, 0, 5, 0); Wmove(Wtitle, 0, 14); Wprintw(Wtitle, "%s Monitor: ", dip_release()); Wmove(Wtitle, 1, 11); Wprintw(Wtitle, "Time of the last update: "); Wmove(Wtitle, 2, 5); Wprintw(Wtitle, "Cpu utilization USER: %%, SYSTEM: %%, "); Wprintw(Wtitle, "NICE: %%, IDLE: %%"); Wmove(Wtitle, 3, 23); Wprintw(Wtitle, "Load average: , , "); Wrefresh(Wtitle); /* This must be AFTER curses has been called, sigh. */ initdata(); switch (win) { default: helpwindow(); break; case W_GLOBAL: globalstats(); break; case W_LINE: linestats(arg); break; case W_TOTAL: totalstats(arg); break; } /* Connect to server */ if (localpipe) { if (pipe(fildes) < 0) fatal("Can't make pipe"); switch (fork()) { case -1: fatal("Can't fork"); /* NOTREACHED */ case 0: (void)close(fildes[0]); (void)close(0); (void)dup(fildes[1]); (void)close(fildes[1]); (void)execl(DIALMOND_PATH, "dialmond", (char *)NULL); fatal(DIALMOND_PATH); /* NOTREACHED */ } (void)close(fildes[1]); fd = fildes[0]; } else { if (hosttosocket(HostName, &sin) < 0) { (void)sprintf(buff, "Unknown host %s", HostName); fatal(buff); } if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) fatal("Socket failed"); sin.sin_port = sp ? sp->s_port : DIALMON_DEFAULT_PORT; if (connect(fd, (caddr_t)&sin, sizeof sin) < 0) { (void)sprintf(buff, "Can't connect to port %d at %s", ntohs(sin.sin_port), HostName); fatal(buff); } } Wmove(Wtitle, 0, 36); Wprintw(Wtitle, "connected to %s", HostName); /* Print the current time. */ timep = ctime(&StatsCur.when); if (p = strchr(timep, '\n')) *p = '\0'; Wmove(Wtitle, 1, 36); Wprintw(Wtitle, "%s", timep); UpdateStatus(); Wrefresh(Wtitle); /* Set up the prompt. */ Wclear(Wprompt); Wprintw(Wprompt, " ? "); Wrefresh(Wprompt); /* read and plot responses */ for ( ; ; ) { FD_ZERO(&readers); FD_SET(fd, &readers); FD_SET(0, &readers); if (select(fd + 1, &readers, (fd_set *)NULL, (fd_set *)NULL, (struct timeval *)NULL) > 0) { if (FD_ISSET(fd, &readers)) readsocket(fd); if (FD_ISSET(0, &readers)) readterminal(); } } }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.