[mod.sources] v07i037: An alternative to the BSD ruptime command

sources-request@mirror.UUCP (10/24/86)

Submitted by: alasdair@ux.cs.man.ac.uk
Mod.sources: Volume 7, Issue 37
Archive-name: hostup

[  This program can be very expensive to run.  -r$  ]

Inspired by the Tektronix TCP/IP 'hostup' command, I've done an
equivalent thing for 4.[23] UNIX.  Tried and tested on a 4.3 Beta
Vax8600, and SUN3 version 3.0.  It's primitive stuff, so feel free to
enhance it to suit your own needs!
Alasdair
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	hostup.1
#	Makefile
#	hostup.c
#	hostup.h
#	hostupd.c
# This archive created: Sat Sep  6 16:01:51 1986
export PATH; PATH=/bin:$PATH
echo shar: extracting "'hostup.1'" '(1386 characters)'
if test -f 'hostup.1'
then
	echo shar: will not over-write existing file "'hostup.1'"
else
cat << \SHAR_EOF > 'hostup.1'
.TH HOSTUP l "6 September 1986"
.SH NAME
hostup, hostupd \- network status report
.SH SYNOPSIS
.B hostup
.SH DESCRIPTION
.I hostup
prints a list of network node names, with an indication of their
reachability.  It relies on a copy of
.I hostupd
running:
.I hostupd
polls machines in the /etc/hosts file and maintains
a binary file of its results.
.PP
.I hostupd
polls each machine by attempting to
.IR connect (2)
to an unused TCP port, and logging the type of error returned.  This
mechanism is a useful alternative to
.IR ruptime (1),
as it works with non-BSD machines, as well as with machines not
running the ruptime daemon (such as diskless
.B SUN
workstations).
.SH "SEE ALSO"
ruptime(1)
.br
connect(2)
.SH BUGS
The polling process is
.B slow
(taking perhaps 75 minutes to complete one poll of the internal
network at Manchester at weekends, when most machines are switched off)
as it depends upon the
.IR connect (2)
timeout for each machine that is down.
.PP
.I hostupd
currently sleeps for 5 minutes every time round the loop of machines:
this is not strictly necessary if enough machines are down!
.PP
For machines connected to the Internet, polling every machine in
/etc/hosts is probably a mistake.
.PP
The order of polling and the format of displaying the hosts is
optimised for my network and my convenience.
.SH AUTHOR
Alasdair Rawsthorne (arawsthorne@uk.ac.man.cs.ux).

SHAR_EOF
if test 1386 -ne "`wc -c < 'hostup.1'`"
then
	echo shar: error transmitting "'hostup.1'" '(should have been 1386 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'Makefile'" '(119 characters)'
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
hostup:	hostup.c hostup.h
	cc -O -o hostup hostup.c -ltermcap

hostupd:	hostupd.c hostup.h
	cc -O -o hostupd hostupd.c
SHAR_EOF
if test 119 -ne "`wc -c < 'Makefile'`"
then
	echo shar: error transmitting "'Makefile'" '(should have been 119 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'hostup.c'" '(2480 characters)'
if test -f 'hostup.c'
then
	echo shar: will not over-write existing file "'hostup.c'"
else
cat << \SHAR_EOF > 'hostup.c'
#include <stdio.h>

#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>

#include "hostup.h"

#define FIELDWIDTH 11

FILE *f;			/* input file */
struct timeval  first, last;	/* GMT of polls */
struct timezone timezone;	/* my offset */
char * timeof();
int currentpos = 0, width = 80;	/* for formatting screen */
char tbuf[1024];

u_long lastnetwork = 0;

struct perhost thishost;

main()
{
  long seconds;
  int cols;
  u_long network;
  if (tgetent(tbuf, getenv("TERM")) == 1) {
    if ((cols = tgetnum("co")) > 0) {
      width = cols;
    }
  }
  if ((f = fopen(FILENAME, "r")) == NULL) {
    perror(FILENAME);
    exit(1);
  }
  gettimeofday(&first, &timezone);
  last.tv_sec = 0;
  while (fread(&thishost, sizeof(thishost), 1, f) == 1) {
    network = ntohl(thishost.ph_to.sin_addr.s_addr);
    if (IN_CLASSA(network)) {
      network &= IN_CLASSA_NET;
    } else if (IN_CLASSB(network)) {
      network &= IN_CLASSB_NET;
    } else {
      network &= IN_CLASSC_NET;
    }
    if (network != lastnetwork) {
      lastnetwork = network;
      donl();
    }
    printstat(thishost.ph_name, thishost.ph_result);
    seconds = thishost.ph_time.tv_sec;
    if (first.tv_sec > seconds) {
      first.tv_sec = seconds;
    }
    if (last.tv_sec < seconds) {
      last.tv_sec = seconds;
    }
  }
  donl();
  printf("Polled from %s", timeof(&first));
  printf(" to %s; KEY: ", timeof(&last));
  printstat("Up", ECONNREFUSED);
  printstat("Down", ETIMEDOUT);
  printstat("UnReachable", ENETUNREACH);
  donl();
  exit(0);
}

char *
timeof(tp)
     struct timeval *tp;
{
  char *rp;
  struct tm *tm;
  tm = localtime(&tp->tv_sec);
  rp = asctime(tm);
  rp[19] = '\0';		/* truncate before timezone */
  return(rp + 11);		/* return just the time */
}

printstat(name, errno)
     char *name;
{
  int up = 0;
  char extra = ' ';
  switch(errno) {
  case ECONNREFUSED:
    up = 1;
    break;
  case 0:
    up = 1;
    extra = '!';
    break;
  case ETIMEDOUT:
    break;
  case ENETUNREACH:
    extra = '-';
    break;
  default:
    extra = '?';
    break;
  }
  if (up) {
    printf(" %s%c ", name, extra);
  } else {
    printf("(%s)%c", name, extra);
  }
  if (currentpos > (width - (2 * FIELDWIDTH))) {
    donl();
  } else {
    int i;
    currentpos += FIELDWIDTH;
    for (i = strlen(name)+3; i < FIELDWIDTH; i++) {
      putchar(' ');
    }
  }
}

donl()
{
  if (currentpos) {
    putchar('\n');
  }
  currentpos = 0;
}
SHAR_EOF
if test 2480 -ne "`wc -c < 'hostup.c'`"
then
	echo shar: error transmitting "'hostup.c'" '(should have been 2480 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'hostup.h'" '(186 characters)'
if test -f 'hostup.h'
then
	echo shar: will not over-write existing file "'hostup.h'"
else
cat << \SHAR_EOF > 'hostup.h'
#define MAXHOSTCHARS 12
#define FILENAME "/usr/adm/host up"

struct perhost {
  char			ph_name[MAXHOSTCHARS];
  struct sockaddr_in	ph_to;
  struct timeval	ph_time;
  int			ph_result;
};
SHAR_EOF
if test 186 -ne "`wc -c < 'hostup.h'`"
then
	echo shar: error transmitting "'hostup.h'" '(should have been 186 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'hostupd.c'" '(2349 characters)'
if test -f 'hostupd.c'
then
	echo shar: will not over-write existing file "'hostupd.c'"
else
cat << \SHAR_EOF > 'hostupd.c'
#include <stdio.h>

#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>

#include <netdb.h>

#define HISPORT 919		/* 'cos that's what tektronix use */
#define MAXHOSTS 200
#define SLEEPTIME 300		/* 5 mins wait at end of loop */

#include "hostup.h"

extern int errno;

struct hostent *gethostent(), *host;
int inetcomp();
int s;				/* socket number */
FILE *f;			/* output file */
struct timezone timezone;	/* somewhere to ignore it */

struct perhost thishost[MAXHOSTS];
int nohosts = 0;

main()
{
  if ((f = fopen(FILENAME, "w")) == NULL) {
    perror(FILENAME);
    exit(1);
  }
  readhosts();			/* find a table of hosts to poll */
#ifndef DEBUG
  if (fork())
    exit(0);
  { int s;
    for (s = 0; s < 10; s++)
      if (s != fileno(f))
	(void) close(s);
    (void) open("/", 0);
    (void) dup2(0, 1);
    (void) dup2(0, 2);
    s = open("/dev/tty", 2);
    if (s >= 0) {
      ioctl(s, TIOCNOTTY, 0);
      (void) close(s);
    }
  }
#endif
  while(1) {
    fseek(f, 0L, 0);		/* keep writing from start */
    pollhosts();
    sleep(SLEEPTIME);
  }
}

readhosts()
{
  while ((host = gethostent()) != NULL) {
    strncpy(thishost[nohosts].ph_name, host->h_name, MAXHOSTCHARS);
    thishost[nohosts].ph_name[MAXHOSTCHARS-1] = '\0';
    bzero((char *)&thishost[nohosts].ph_to, sizeof(thishost[nohosts].ph_to));
    bcopy(host->h_addr, (char *)&thishost[nohosts].ph_to.sin_addr, host->h_length);
    thishost[nohosts].ph_to.sin_family = host->h_addrtype;
    thishost[nohosts].ph_to.sin_port = HISPORT;
    nohosts++;
    if (nohosts == MAXHOSTS)
      break;
  }
  endhostent();
  qsort(thishost, nohosts, sizeof(thishost[0]), inetcomp);
}

inetcomp(a, b)
     struct perhost *a, *b;
{
  return(ntohl(a->ph_to.sin_addr.s_addr) - ntohl(b->ph_to.sin_addr.s_addr));
}

pollhosts()
{
  int i;
  for (i = 0; i < nohosts; i++) {
    s = socket(AF_INET, SOCK_STREAM, 0);
    if (s < 0) {
#ifdef DEBUG
      perror("socket:");
#endif      
      exit(1);
    }
    if (connect(s, (char *)&thishost[i].ph_to,
		sizeof(thishost[i].ph_to)) < 0) {
      thishost[i].ph_result = errno;
    } else {
      thishost[i].ph_result = 0;
    }
    gettimeofday(&thishost[i].ph_time, &timezone);
    fwrite(&thishost[i], sizeof(thishost[i]), 1, f);
    fflush(f);
    (void) close(s);			/* ! */
  }
}
SHAR_EOF
if test 2349 -ne "`wc -c < 'hostupd.c'`"
then
	echo shar: error transmitting "'hostupd.c'" '(should have been 2349 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0