[comp.sources.unix] v24i088: Simple load-balancing program

rsalz@bbn.com (Rich Salz) (06/06/91)

Submitted-by: Dikran Kassabian <deke@ee.rochester.edu>
Posting-number: Volume 24, Issue 88
Archive-name: lb


LB is a load-balancing program.  It can check the current load on various
machines on a LAN, and find the best candidate machine to accept a new job
to it's run queue.  Additionally, it can send the job to that machine
using rsh(1).

LB works best in homogeneous environments, where each machine has similar
or identical file system layout.  Here at the University of Rochester
Department of Electrical Engineering, we have nearly 70 SUN computers,
which share home-directories and local binaries.  When a user sitting at a
small workstation wants to run a compute intensive job, LB helps to find a
machine on the network that has a low load, knowing that that machine will
also have his/her home directory and required program.

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# Contents:  INSTALL Makefile OTHER_SERVERS README config.h lb.1 lb.5
#   rsdclnt.c srvclnt.c st_sendrecv.c stats.h
# Wrapped by rsalz@litchi.bbn.com on Thu Jun  6 12:43:40 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo If this archive is complete, you will see the following message:
echo '          "shar: End of archive."'
if test -f 'INSTALL' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'INSTALL'\"
else
  echo shar: Extracting \"'INSTALL'\" \(1988 characters\)
  sed "s/^X//" >'INSTALL' <<'END_OF_FILE'
X
X		LB - The Load Balancer (version 1.2)
X			by Dikran Kassabian
X
X		      University Of Rochester
X		Department of Electrical Engineering
X			March 3, 1991
X
XINSTALLATION NOTES:
X
X
X1. CHOOSING A SERVER
X====================
XThe lb load balancer uses statistics gathered via connection with
Xservers on each of the candidate machines.  Two servers are supported,
Xthe SUN rstatd, and Dave Curry's statsrv.  The installer must see to
Xit that all candidate machines support the selected server program.
XIn an all SUN environment, rstatd is the better choice as it seems to
Xbe somewhat faster.  In other environments, it may be necessary to use
Xthe statsrv server, which is public domain.
X
X2. BUILDING THE PROGRAM
X=======================
XOnce a server is selected, the Makefile must be edited to reflect the 
Xchoice.  If rstatd is to be used, edit the Makefile definition for SERVER
Xto be 'SERVER=rstatd'.  If the statsrv server is to be used, make it
X'SERVER=statsrv'.  Check the defines in config.h, and choose the location
Xof the configuration file.  Remember that every machine that wants to
Xuse lb needs to be able to see that configuration file.  You may choose
Xto use multiple copies, or a single copy that all machines can see.  Once
Xthe Makefile and config.h are setup to your satisfaction, type 'make'.
XThe program 'lb' will be built.
X
X3. WRITING THE CONFIGURATION FILE
X=================================
XThe configuration file contains information on every machine on which lb
Xmay start jobs. See the man page lb(5) for details on its setup.
X
X
X4. INSTALLING THE FILES
X=======================
XInstall the program lb in a location within the path of the users requiring
Xit.  Good choices would include /usr/local/bin or /usr/new.  Then install
Xthe configuration file wherever the config.h file claimed it would be.  Again,
Xremember that every machine needs to have read access to this file.  Finally,
Xinstall the man pages lb(1) and lb(5) as appropriate.
X
XThat's all!
X
X
XDeke Kassabian, 3/8/91
END_OF_FILE
  if test 1988 -ne `wc -c <'INSTALL'`; then
    echo shar: \"'INSTALL'\" unpacked with wrong size!
  fi
  # end of 'INSTALL'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
  echo shar: Extracting \"'Makefile'\" \(1069 characters\)
  sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X#
X# Makefile for "lb", the load-balancer
X# ^Deke Kassabian 6/3/88,  mods and additions 3/7/89
X#
XSERVER=rstatd
X#
X#CFLAGS=  -g -f68881
XCFLAGS=   -O 
XLDFLAGS=  -s
X#PRINT=    ptroff -man
XPRINT=	  troff -t -man 
X
XSRCS=	  rsdclnt.c srvclnt.c st_sendrecv.c stats.h config.h
XMANS=	  lb.1 lb.5
XOBJS_SRV= srvclnt.o st_sendrecv.o
XOBJS_RSD= rsdclnt.o 
XLIBS=     -lrpcsvc
X
Xlb: lb.$(SERVER)
X	@echo "lb has been built for $(SERVER) server."
X
Xlb.rstatd: $(OBJS_RSD)
X	$(CC) $(CFLAGS) $(LDFLAGS) -o lb $(OBJS_RSD) $(LIBS)
X
Xlb.statsrv: $(OBJS_SRV)
X	$(CC) $(CFLAGS) $(LDFLAGS) -o lb $(OBJS_SRV)
X
Xtags:
X	ctags *.c > tags
X
Xclean:
X	rm -f core *.o
X
Xspotless:
X	rm -f core lb *.o
X
Xlove:
X	@echo 'not war?'
X
Xjoke: 
X	@echo 'What do you want for nothing?'
X
Xshar: 
X	@shar Makefile README INSTALL OTHER_SERVERS $(MANS) $(SRCS) >lb.shar
X	@chmod a+r lb.shar
X	@echo "SHAR file lb.shar created."
X
Xhardcopy:
X	lpr README INSTALL OTHER_SERVERS $(SRCS)
X	$(PRINT) $(MANS) | lpr -t
X
X# Dependencies
Xlbmain.o: 	lbmain.c stats.h config.h
Xst_sendrecv.o:	st_sendrecv.c stats.h config.h
Xrsdclnt.o:	rsdclnt.c config.h
END_OF_FILE
  if test 1069 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
  fi
  # end of 'Makefile'
fi
if test -f 'OTHER_SERVERS' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'OTHER_SERVERS'\"
else
  echo shar: Extracting \"'OTHER_SERVERS'\" \(1224 characters\)
  sed "s/^X//" >'OTHER_SERVERS' <<'END_OF_FILE'
X
XWHAT?  No rstatd?  No statsrv?  Fear not.
X------------------------------------------
X
XLB is simple enough to modify for use with other servers.  This package
Xincludes client programs to talk to rstatd or statsrv, and these clients
Xcan be used as templates for others.
X
Xstatsrv is available from comp.sources.unix archive servers.
X
X-------------------------------------------------------------------------
XFrom the SunOS rstatd manual page:
X
X  rstatd is a  server  which  returns  performance  statistics obtained  
X  from the kernel.  The rstatd daemon is normally invoked by inetd(8C).
X
X-------------------------------------------------------------------------
XThe opening paragraph from Dave Curry's statsrv README file:
X
X  This is a remote statistics server as described in RFC996, "Statistics
X  Server", D. L. Mills, February 1987.  With it, you can collect things
X  like load averages, number of users, uptime, and network statistics
X  from remote machines easily.  (David Curry, davy@riacs.edu)
X-------------------------------------------------------------------------
X
X      ^Deke Kassabian,   deke@ee.rochester.edu   or   ur-valhalla!deke
X   Univ of Rochester, Dept of EE, Rochester, NY 14627     (+1 716-275-3106)
END_OF_FILE
  if test 1224 -ne `wc -c <'OTHER_SERVERS'`; then
    echo shar: \"'OTHER_SERVERS'\" unpacked with wrong size!
  fi
  # end of 'OTHER_SERVERS'
fi
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
  echo shar: Extracting \"'README'\" \(2930 characters\)
  sed "s/^X//" >'README' <<'END_OF_FILE'
X		LB - The Load Balancer (version 1.2)
X			by Dikran Kassabian
X
X		      University Of Rochester
X		Department of Electrical Engineering
X			March 3, 1991
X_______________________________________________________________________
X
XWhat is LB?
X
X	LB is a load-balancing program.  It can check the current
X	load on various machines on a LAN, and find the best candidate
X	machine to accept a new job to it's run queue.  Additionally,
X	it can send the job to that machine using rsh(1).
X
X	LB works best in homogeneous environments, where each machine
X	has similar or identical file system layout.  Here at the
X	University of Rochester Department of Electrical Engineering,
X	we have nearly 70 SUN computers, which share home-directories
X	and local binaries.  When a user sitting at a small workstation
X	wants to run a compute intensive job, LB helps to find a machine
X	on the network that has a low load, knowing that that machine
X	will also have his/her home directory and required program.
X
XHow to install LB:
X
X	A separate file, called INSTALL, is included with detailed
X	installation notes, but briefly...
X
X	To install lb, unpack the shar file (hmm, I guess you've already
X	done that :-), decide which statistics server you want to use
X	(rstatd or statsrv) and make the two small adjustments to the
X	Makefile, then type "make".  The program 'lb' will be produced.
X	Then move 'lb' to an appropriate directory (/usr/local/bin),
X	edit and install the configuration file, and you're ready to 
X	go.  The relevant man pages are included, and can be installed 
X	in your local man page directory.
X
XHow to use LB:
X
X	To use lb, just type 'lb <command-line>' at your prompt, where
X	<command line> is some shell command you might normally type
X	at your prompt.  LB will read a configuration file that tells
X	it about the candidate machines, select one, and then use rsh(1)
X	to run <command-line> on the specified machine.
X
X	See the man page for lb(1) for more information.
X
XSpecial thanks to Dave Curry (davy@riacs.edu), whose statsrv server can
Xbe used as an option to the rpc.rstatd server.  He was nice enough to let
Xme include some of his code with this package.  The files st_sendrecv.c
Xand stats.h are directly from his statsrv distribution.
X
XThis version of lb has been tested in only a few environments.  It works on 
Xour Sun-3 and Sun-4 computers under SunOS 4.1.  It also works on our 68030
XNeXT Cubes under 1.0a OS.  Previous versions ran on our Vax 11/750 under 
XMt.Xinu BSD (but that machine has since been retired).  Beyond that I don't 
Xknow.  If you port it to other operating systems and hardware, send me the 
Xdiffs so I can include them in the next release (if there is one).
X
XIf you have comments/fixes/bug-reports/flames, send 'em to me at
Xdeke@ee.rochester.edu.  I'll make every effort to reply, and to address
Xyour concerns, but can't always guarantee that I can be quick about it.
X
XGood luck with lb!  I hope you find it useful.
END_OF_FILE
  if test 2930 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
  fi
  # end of 'README'
fi
if test -f 'config.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'config.h'\"
else
  echo shar: Extracting \"'config.h'\" \(730 characters\)
  sed "s/^X//" >'config.h' <<'END_OF_FILE'
X/*							*/
X/* Configuration parameters for the load balancer, 'lb' */
X/* Copyright (c) 1988, 1989 University of Rochester     */
X/* Department of Electrical Engineering.                */
X/*							*/
X
X#define	CONFIGFILE	"/usr/share/lb.conf"
X#define	RSH		"/usr/ucb/rsh"
X
X#define loadavg(sp,n)  (((float) (sp)->avenrun[n]) / 256.0)
X#define MAXLINE		255		/* largest line in config file*/
X#define MAXSHBUF	10240		/* max size of shell buffer   */
X#define MAXHOST		64		/* longest allowable hostname */
X
X#if u3b || u3b5 || sun
X#define MAXFLOAT	((float)3.40282346638528860e+38)
X#endif
X
X#if pdp11 || vax
X#define MAXFLOAT	((float)1.701411733192644299e+38)
X#endif
X
X#ifndef MAXFLOAT
X#define MAXFLOAT	((float)1.701411733192644299e+38)
X#endif
END_OF_FILE
  if test 730 -ne `wc -c <'config.h'`; then
    echo shar: \"'config.h'\" unpacked with wrong size!
  fi
  # end of 'config.h'
fi
if test -f 'lb.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'lb.1'\"
else
  echo shar: Extracting \"'lb.1'\" \(2521 characters\)
  sed "s/^X//" >'lb.1' <<'END_OF_FILE'
X.tr ~ \ 
X.TH LB 1 "options
X.SH NAME
XLb \-  load balancer for homogeneous environment
X.SH SYNOPSIS
X.BR "lb [ -v | -q ] <command string>
X.PP
X.SH DESCRIPTION
X.I lb
Xfinds the best machine in the environment on which to run a batch type
Xcompute task.  This is done by comparing load averages
Xand computing power for various machines, and selecting the machine
Xbest able to accept the compute task.
X.I lb
Xis especially useful in distributing compute intensive jobs among
Xthe many computers in a homogeneous environment.
X.sp
X.SH OPTIONS
XWith no options,
X.I lb
Xprints a one line output identifying
Xthe selected machine and the command being submitted.
X.sp
X.nf
X	-v	verbose mode:  causes an excessive amount of
X		information about the determination of the best
X		machine selection to be printed.
X
X	-q	quiet mode:  causes even the usual one line
X		output identifying the selected machine to be
X		eliminated.  The job will be sent to the remote
X		machine, but you will never be told which machine
X		was selected!
X.fi
X.SH EXAMPLES
X.nf
X.I lb
X.fi
X	This gives the output of lb -v, but executes no command
X.sp
X.nf
X.I lb fortune
X.fi
X	This executes "fortune" on the least loaded machine.
X.sp
X.nf
X.I lb -v foobar
X.fi
X	This executes "foobar" on the least loaded machine, and gives
Xverbose messages.
X.sp
X.nf
X.I lb -q minimos infile
X.fi
X	This executes "minimos infile" on the least loaded machine,
Xand gives no messages.
X.sp
X.nf
X.I lb 'spice<infile>outfile'
X.fi
X	This is an important example.  Here, a program called "spice" is
Xbeing run with redirected input and output. Anytime redirection is 
Xdesired in using
X.I lb,
Xthe portion using redirection must be quoted to avoid interpretation at
Xthe originating shell.  The quoted string in this example allows for the
Xredirection to take place on the remote machine.
X.sp
X.nf
X.SH FILES
X.fi
X.I lb
Xalso requires the configuration file
X.I lb.conf
Xwhich lists machines to be
Xconsidered, and some basic information about them.  See the manual
Xpage lb(5) for more on the configuration file.
X.nf
X.SH CAVEATS
X.fi
X.sp
X.I lb
Xshould really use
X.I csh job control
Xso that the user may track the progress of the submitted job.  Maybe in
Xthe next version.
X.sp
X.nf
X.SH NOTES
XThis program is still under development.  Send 
Xdescriptions of problems and/or comments to deke@ee.rochester.edu
X(also ...!rochester!ur-valhalla!deke).
X.SH AUTHOR
X.nf
XDikran Kassabian,
XUniversity of Rochester Dept. of Electrical Engineering
XRochester, NY 14627 (716) 275-3106
X.SH "SEE ALSO"
Xrstatd(8), statsrv(8), w(1), uptime(1), lb(5)
END_OF_FILE
  if test 2521 -ne `wc -c <'lb.1'`; then
    echo shar: \"'lb.1'\" unpacked with wrong size!
  fi
  # end of 'lb.1'
fi
if test -f 'lb.5' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'lb.5'\"
else
  echo shar: Extracting \"'lb.5'\" \(2063 characters\)
  sed "s/^X//" >'lb.5' <<'END_OF_FILE'
X.tr ~ \ 
X.TH LB 5 "limit
X.SH NAME
XLb.conf \-  load balancer configuration file for lb(1)
X.fi
X.SH DESCRIPTION
XThis file contains information about local machines recognized by
X.IR lb (1).
XThe format consists of single line entries with three fields. The
Xfields are
X.I machine-name,
X.I power-factor,
Xand
X.I load-limit.
X.sp
X.I machine-name
Xis a legal or recognized hostname which is to be a candidate for jobs
Xinitiated through the use of lb(1).  This machine must support the
Xstatistics server protocol used by lb(1).
X.sp
X.I power-factor
Xis a floating point value, and has meaning only by comparison to
Xthe power-factors of other machines.  The machines with the larger
Xpower-factors will be preferred to those with smaller power-factors.
X.sp
X.I load-limit
Xspecifies the load-average beyond which jobs should not be sent
Xto that machine.
X.sp
X.SH SAMPLE FILE
X.nf
X# lb.conf
X#
X# The format of this file is <machine> <sfactor> <limit>
X.ta .8i
X# where	<machine> is a legal machine name
X#	<sfactor> is that machines selection factor
X#	<limit>   is the load limit for that machine
X#
X.ta 2.2i 3.2i
X#  MACHINE	SFACTOR	LIMIT
Xbrahms.fubar.com	200	3.0
Xmozart.fubar.com	150	2.5
Xchopin.fubar.com	120	2.5
Xbach.fubar.com	100	1.5
X.sp
X.fi
XLines beginning with 
X.B # 
Xare comments.
X.sp
XIn this example, brahms.fubar.com is preferred to mozart.fubar.com, which
Xis preferred to chopin.fubar.com, if their loads are exactly equal.  In
Xpractice, the load average (number of jobs in the run queue over the last
Xminute) is compared to the load-limit and then divided by the power-factor,
Xfor each machine listed.  If the load average exceeds the load-limit, that
Xmachine will not be considered.  When all machines listed have been considered,
Xthe one with the smallest value of (load-average/power-factor) will be
Xselected.
X.sp
X.SH "SEE ALSO"
Xlb(1), w(1), uptime(1)
X.SH NOTES
XThere are limits to the line-length and the hostname length, which are
Xset in the file config.h when lb(1) is built.  By default, these are
X255 and 64 respectively, but may be tweaked at the installers discretion.
END_OF_FILE
  if test 2063 -ne `wc -c <'lb.5'`; then
    echo shar: \"'lb.5'\" unpacked with wrong size!
  fi
  # end of 'lb.5'
fi
if test -f 'rsdclnt.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rsdclnt.c'\"
else
  echo shar: Extracting \"'rsdclnt.c'\" \(5598 characters\)
  sed "s/^X//" >'rsdclnt.c' <<'END_OF_FILE'
X/*
X * Copyright (c) 1989,1990,1991 University of Rochester
X * Department of Electrical Engineering
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that the above copyright notice and this paragraph are
X * duplicated in all such forms and that any documentation,
X * advertising materials, and other materials related to such
X * distribution and use acknowledge that the software was developed
X * at the University of Rochester.
X *
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X */
X
X/*
X * rsdclnt.c  for the 'lb' load balancer 
X *   -Deke Kassabian, University of Rochester EE Department
X *   ( deke@ee.rochester.edu )  (716) 275-3106
X *
X * $Revision: 1.2 $
X * $Author: deke $
X * $Date: 91/03/03 17:52:36 $
X * $Log:	rsdclnt.c,v $
X * Revision 1.2  91/03/03  17:52:36  deke
X * changed stats to lstats to avoid name clash: required by change in rstat.h
X *
X */
X
X#include <stdio.h>
X#include <rpc/rpc.h>
X#include <rpcsvc/rstat.h>
X#include "config.h"
X
Xmain(argc, argv, envp)
Xint argc;
Xchar *argv[];
Xchar *envp[];
X{
X
Xstruct statstime lstats;
XFILE *fd,*fopen();
Xchar *fgets();
Xint getstats();
X
Xint start, 			/* where in the argv the command begins */
X    ppgrp,			/* parent process group			*/
X    command,			/* whether there is a command 		*/
X    verbose_mode, 		/* all possible output			*/
X    quiet_mode;			/* no output				*/
X
Xchar *path=RSH;
X
Xchar *dir,*pname;
Xchar *cmd_exec[MAXSHBUF],	/* command to execute (local)		*/
X     *rcmd_exec[MAXSHBUF],	/* command to execute (remote)		*/
X     dbuf[MAXSHBUF],		/* working directory placeholder	*/
X     host[MAXHOST],		/* hostname placeholder			*/
X     best_host[MAXHOST],	/* hostname placeholder			*/
X     fentry[MAXLINE];		/* file buffer */
X
Xfloat my_sfactor,
X      best_sfactor,
X      factor, limit,		/* file variables			*/
X      load;
X
X	pname = *argv;			/* Name of this program  */
X	verbose_mode=0,quiet_mode=0;	/* Default is neither    */
X	best_sfactor=MAXFLOAT;	 	/* Practically infinity! */
X 	command=1;			/* There is a command	 */
X
X /* Check arguments. 
X  *
X  * if argc=1 then verbose=Y and command=NULL
X  * if argc>=2 check for flags:
X  *    -v=verbose (full output),  -q=quiet (don't output machine selection)
X  *    with no flags, only machine selection will be output
X  *    The above flags are mutually exclusive.  v overrides q
X  *
X  *    Additional arguments are taken to be a command.  No sanity checking
X  *    will be done.  When a machine is selected, the command will be sent
X  *    to that machine as a shell command.
X  *
X  */
X
X if (argc == 1) {
X	command=0;			/* no command to execute */
X	verbose_mode=1;			/* verbose mode		 */
X }
X else {
X     start=1;
X     if(!strcmp(argv[1],"-q")){
X      quiet_mode=1;			/* quiet mode - no output */
X      start=2;
X      if(argc == 2) exit(0);		/* No command, no output! */
X     } 
X     if(!strcmp(argv[1],"-v")){
X      verbose_mode=1;			/* verbose mode		 */
X      start=2;
X      if(argc == 2) command=0;		/* No command - verbose   */
X     } 
X }
X if(command){
X	for( ; start<argc ; start++){
X	    strcat(cmd_exec," "); strcat(cmd_exec,argv[start]);
X	}
X }
X
X
X if((fd=fopen(CONFIGFILE,"r"))==0){
X	 fprintf(stderr,"Error opening configuration file\n");
X	 exit(1);
X }
X
X	while((fgets(fentry,MAXLINE,fd))!=NULL){
X	   sscanf(fentry,"%s %f %f",host,&factor,&limit);
X	   if(fentry[0]=='#') continue; 	/*ignore comments*/
X		
X	   if(verbose_mode)
X	      printf("\nHOST=%s\tFACTOR=%.2f\tLIMIT=%.2f\n",host,factor,limit);
X	   
X	   if(getstats(host,&lstats) !=0){
X		load=MAXFLOAT;			/* skip this one */
X	   } 
X	   else {
X		load=loadavg(&lstats,0);	
X	   }
X
X	   if( load > limit){
X	      if(verbose_mode) printf("Host %s is over defined limit\n",host);
X	      continue;
X	      }
X
X	   my_sfactor=100*(load/factor);
X
X	   if(verbose_mode)
X		printf("Load avg=%.3f\tSelection factor=%.3f\n",
X			load,my_sfactor);
X
X	   if(best_sfactor>my_sfactor){
X		best_sfactor=my_sfactor;
X		strcpy(best_host,host);
X	   }
X	} /*endwhile */
X
X    if(verbose_mode)
X       printf("Selected host: <%s> factor=<%.3f>\n",best_host,best_sfactor);
X
X    if(command){
X	    sprintf(rcmd_exec,"(cd %s;%s)",getwd(dbuf),cmd_exec);
X	    if(!quiet_mode){ /* print if not quiet_mode */
X	       printf("\nrsh %s %s &\n",best_host,rcmd_exec); 
X	    }
X	    ppgrp = getpgrp(getppid());
X	    if (fork())
X		exit(0);
X	    if(setpgrp(getpid(), ppgrp)) 
X		perror("stepgrp");
X	    execle(path,path,best_host,rcmd_exec,(char *)0,envp); exit(0);
X	    perror("");
X    }
Xexit(0);
X}
X
Xgetstats(hostname,lstats)
Xchar *hostname;
Xstruct statstime *lstats;
X{
Xint c;
X    c=rstat(hostname,lstats);	/* rstat seems to return:	    */
X				/*	0  on success		    */
X				/*	13 on hostname lookup fail  */
X				/*	14 on connection timeout    */
X				/*	15 on "rstat not supported" */
X    /*  I chose to generate my own error messages.  perror() didn't */
X    /*	give error messages I liked.  Switch back to perror if you  */
X    /*	care to.						    */
X
X    switch(c){
X	case 0:
X		break;
X	case 13:
X		fprintf(stderr,"rstat error (%d): ",c);
X		fprintf(stderr,"host %s is unknown.\n",hostname);
X		break;
X	case 14:
X		fprintf(stderr,"rstat error (%d): ",c);
X		fprintf(stderr,"host %s is down or unreachable.\n",hostname);
X		break;
X	case 15:
X		fprintf(stderr,"rstat error (%d): ",c);
X		fprintf(stderr,"host %s does not support protocol.\n",hostname);
X		break;
X	default:
X		fprintf(stderr,"rstat error (%d): ",c);
X	        fprintf(stderr,"unknown error\n");
X		break;
X    }
X    return(c);
X}
END_OF_FILE
  if test 5598 -ne `wc -c <'rsdclnt.c'`; then
    echo shar: \"'rsdclnt.c'\" unpacked with wrong size!
  fi
  # end of 'rsdclnt.c'
fi
if test -f 'srvclnt.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'srvclnt.c'\"
else
  echo shar: Extracting \"'srvclnt.c'\" \(5875 characters\)
  sed "s/^X//" >'srvclnt.c' <<'END_OF_FILE'
X/*
X * Copyright (c) 1989, University of Rochester
X * Department of Electrical Engineering
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that the above copyright notice and this paragraph are
X * duplicated in all such forms and that any documentation,
X * advertising materials, and other materials related to such
X * distribution and use acknowledge that the software was developed
X * at the University of Rochester.
X *
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X */
X
X/* srvclnt.c version 1.1  3/7/89
X * for the 'lb' load balancer - by Deke Kassabian
X *
X *   -Deke Kassabian, University of Rochester EE Department
X *   ( deke@ee.rochester.edu )  (716) 275-3106
X */
X
X#include <sys/param.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <netdb.h>
X#include <stdio.h>
X#include <string.h>
X#include "stats.h"
X#include "config.h"
X
Xchar *pname;
Xshort	server = 0;			/* indicates we aren't server	*/
Xstruct	sockaddr_in sin;		/* address of remote host	*/
X
Xmain(argc, argv, envp)
Xint argc;
Xchar **argv;
Xchar **envp;
X{
X	FILE *fd,*fopen();
X
Xint start, 			/* where in the argv the command begins */
X    ppgrp,			/* parent process group			*/
X    command,			/* whether there is a command 		*/
X    verbose_mode, 		/* all possible output			*/
X    quiet_mode;			/* no output				*/
X
Xchar *path=RSH;
X
Xchar *dir;
Xchar *cmd_exec[MAXSHBUF],	/* command to execute (local)		*/
X     *rcmd_exec[MAXSHBUF],	/* command to execute (remote)		*/
X     dbuf[MAXSHBUF],		/* working directory placeholder	*/
X     host[MAXHOST],		/* hostname placeholder			*/
X     best_host[MAXHOST],	/* hostname placeholder			*/
X     fentry[MAXLINE];		/* file entry             		*/
X
Xfloat my_sfactor,
X      best_sfactor,
X      load,
X      factor,limit;		/* file variables			*/
X
X	int socktype;
X	register int s;
X	char buf[10240];
X	struct hostent *hp;
X	struct servent *sp;
X	char *servtype;
X	struct hostent *gethostbyname();
X	struct servent *getservbyname();
X
X	pname = *argv;			/* Name of this program  */
X
X	verbose_mode=0,quiet_mode=0;	/* Default is neither    */
X	best_sfactor=MAXFLOAT;	 	/* Practically infinity! */
X 	command=1;			/* There is a command	 */
X
X /* Check arguments. 
X  *
X  * if argc=1 then verbose=Y and command=NULL
X  * if argc>=2 check for flags:
X  *    -v=verbose (full output),  -q=quiet (don't output machine selection)
X  *    with no flags, only machine selection will be output
X  *    The above flags are mutually exclusive.  v overrides q
X  *
X  *    Additional arguments are taken to be a command.  No sanity checking
X  *    will be done.  When a machine is selected, the command will be run
X  *    on that machine
X  *
X  */
X
X if (argc == 1) {
X	command=0;			/* no command to execute */
X	verbose_mode=1;			/* verbose mode		 */
X	}
X else {
X     start=1;
X     if(!strcmp(argv[1],"-q")){
X      quiet_mode=1;			/* quiet mode - no output */
X      start=2;
X      if(argc == 2) exit(0);		/* No command, no output! */
X     } 
X     if(!strcmp(argv[1],"-v")){
X      verbose_mode=1;			/* verbose mode		 */
X      start=2;
X      if(argc == 2) command=0;		/* No command - verbose   */
X     } 
X }
X
X  if(command){
X	for( ; start<argc; start++){
X	    strcat(cmd_exec," "); strcat(cmd_exec,argv[start]);
X	}
X  }
X
X /* Open the config file */
X if((fd=fopen(CONFIGFILE,"r"))==0){
X	 fprintf(stderr,"Error opening configuration file ");
X	 exit(1);
X }
X
X /* Set up to use TCP stream sockets.  */
X	servtype = "tcp";
X	socktype = SOCK_STREAM;
X
X /* Look up the port the server lives on.  */
X if ((sp = getservbyname(SERVNAME, servtype)) == NULL) {
X	fprintf(stderr, "%s: %s/%s: service unknown.\n", 
X		pname, SERVNAME, servtype);
X	exit(1);
X	}
X
X /* Read the config file */
X	strcpy(best_host,"localhost");
X	while((fgets(fentry,MAXLINE,fd))!=NULL){
X	   sscanf(fentry,"%s %f %f",host,&factor,&limit);
X	   if(fentry[0]=='#') continue; /*ignore comments*/
X		
X	   if(verbose_mode)
X	     printf("\nHOST=%s\tFACTOR=%.2f\tLIMIT=%.2f\n",host,factor,limit);
X	   
X
X	   /* Look up the host's address.  */
X	   if ((hp = gethostbyname(host)) == NULL) {
X		fprintf(stderr, "%s: %s: host unknown.\n", pname, host);
X		exit(1);
X	   }
X	
X	   /* Get a socket. */
X	   if ((s = socket(AF_INET, socktype, 0)) < 0) {
X			error("socket");
X			exit(1);
X	   }
X
X		/* Build the server's address.  */
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		/* connect to the remote host.  */
X		if (connect(s, &sin, sizeof(struct sockaddr_in)) < 0) {
X			/* if not quiet mode, print connect error */
X			error("connect");
X			/* can't reach this host, try the next */
X			continue;
X		}
X
X		/* make the request and grab the result */
X		st_send(s,"loadav");
X		st_recv(s,buf,sizeof(buf),"response");
X		sscanf(buf,"%f",&load);
X
X	        if( load > limit){
X	           if(verbose_mode)
X		      printf("Host %s is over defined limit\n",host);
X	           continue;
X	        }
X
X		my_sfactor=100*( load / factor );
X		if(verbose_mode){
X		  printf("Load avg=%.3f ",load);
X		  printf("Selection factor=%.2f\n",my_sfactor);
X		}
X		
X		if(best_sfactor>my_sfactor){
X			best_sfactor=my_sfactor;
X			strcpy(best_host,host);
X		}
X
X	} /*endwhile */
X
Xif(verbose_mode)
X   printf("Selected host: <%s> factor=<%.3f>\n",best_host,best_sfactor);
X
X    if(command){
X	    sprintf(rcmd_exec,"(cd %s;%s)",getwd(dbuf),cmd_exec);
X	    if(!quiet_mode){	/* print if not quiet_mode */
X	       printf("\nrsh %s %s &\n",best_host,rcmd_exec);
X	    }
X	    ppgrp = getpgrp(getppid());
X	    if (fork())
X		exit(0);
X	    if(setpgrp(getpid(), ppgrp)) 
X		perror("stepgrp");
X	    execle(path,path,best_host,rcmd_exec,(char *)0,envp); exit(0);
X	    perror("");
X     }
Xexit(0);
X}
X
Xerror(s)
X{
X	fprintf(stderr, "%s: ", pname); perror(s);
X}
END_OF_FILE
  if test 5875 -ne `wc -c <'srvclnt.c'`; then
    echo shar: \"'srvclnt.c'\" unpacked with wrong size!
  fi
  # end of 'srvclnt.c'
fi
if test -f 'st_sendrecv.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'st_sendrecv.c'\"
else
  echo shar: Extracting \"'st_sendrecv.c'\" \(1717 characters\)
  sed "s/^X//" >'st_sendrecv.c' <<'END_OF_FILE'
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@riacs.edu (previously 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 <sys/param.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <syslog.h>
X#include <stdio.h>
X
X#include "stats.h"
X
Xextern char	*pname;
Xextern short	server;
X
X/*
X * st_send - send buf on the socket s.
X */
Xst_send(s, buf)
Xchar *buf;
Xint s;
X{
X	register int cnt;
X
X	/*
X	 * We want the length including the null.
X	 */
X	cnt = strlen(buf) + 1;
X
X	/*
X	 * Send the buffer.
X	 */
X	if (send(s, buf, cnt, 0) < 0) {
X		if (server)
X			syslog(LOG_ERR, "send: %m");
X		else
X			error("send");
X		exit(1);
X	}
X}
X
X/*
X * st_recv - receive data of maximum length cnt into the buffer buf on
X *	     socket s.  err describes what we're expecting for the error
X *	     message.
X */
Xst_recv(s, buf, cnt, err)
Xchar *buf, *err;
Xint s, cnt;
X{
X	char c;
X	register int n;
X
X	/*
X	 * Receive a character at a time up to a null.
X	 */
X	do {
X		if ((n = recv(s, &c, sizeof(char), 0)) < 0) {
X			if (server)
X				syslog(LOG_ERR, "recv: %m");
X			else
X				error("recv");
X			exit(1);
X		}
X
X		/*
X		 * End of file.
X		 */
X		if (n == 0)
X			exit(0);
X
X		if (--cnt < 0) {
X			if (server)
X				syslog(LOG_ERR, "%s too long", err);
X			else
X				fprintf(stderr, "%s: %s too long.\n", pname, err);
X			exit(1);
X		}
X
X		*buf++ = c;
X	} while (c != '\0');
X}
END_OF_FILE
  if test 1717 -ne `wc -c <'st_sendrecv.c'`; then
    echo shar: \"'st_sendrecv.c'\" unpacked with wrong size!
  fi
  # end of 'st_sendrecv.c'
fi
if test -f 'stats.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'stats.h'\"
else
  echo shar: Extracting \"'stats.h'\" \(951 characters\)
  sed "s/^X//" >'stats.h' <<'END_OF_FILE'
X/*
X * $Header: /ecn1/src/ecn/statsrv/RCS/stats.h,v 1.2 87/12/17 14:54:27 davy Exp $
X *
X * stats.h - definitions for statistics server
X *
X * David A. Curry
X * Purdue University
X * Engineering Computer Network
X * davy@intrepid.ecn.purdue.edu
X * October, 1987
X *
X * $Log:	stats.h,v $
X * Revision 1.2  87/12/17  14:54:27  davy
X * Added defines for VMUNIX and KMEM.
X * 
X * Revision 1.1  87/10/17  21:01:03  davy
X * Initial revision
X * 
X */
X
X#define SERVNAME	"statsrv"	/* name of our service		*/
X#define MAXDGRAM	576		/* maximum size of a datagram	*/
X
X#define KMEM		"/dev/kmem"	/* path to kernel memory	*/
X
X#if vax || sun || gould || tahoe
X#define VMUNIX		"/vmunix"	/* path to kernel		*/
X#endif
X#if sequent
X#define VMUNIX		"/dynix"
X#endif
X
X/*
X * For 4.2BSD syslogs.
X */
X#ifndef LOG_DAEMON
X#define LOG_DAEMON	(3<<3)
X#endif
X
Xextern int st_send(), st_recv();	/* stream send/recv functions	*/
Xextern int dg_send(), dg_recv();	/* datagram send/recv functions	*/
END_OF_FILE
  if test 951 -ne `wc -c <'stats.h'`; then
    echo shar: \"'stats.h'\" unpacked with wrong size!
  fi
  # end of 'stats.h'
fi
echo shar: End of archive.
exit 0
exit 0 # Just in case...
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.