[net.sources] New remote troff execution package

steve@tove.UUCP (Steve D. Miller) (09/09/85)

#   This is the new remote troff package; see net.unix-wizards, 
# the README, and/or the Makefile for more information.  If you
# have the older version of this program, I strongly suggest that
# you install this in its place (it's a whole lot better...)
# Spoken: Steve Miller 	ARPA:	steve@maryland	Phone: +1-301-454-4251
# CSNet:	steve@umcp-cs 	UUCP:	{seismo,allegra}!umcp-cs!steve
# USPS: Computer Science Dept., University of Maryland, College Park, MD 20742
: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ x$1 = x-a ]; then
	all=TRUE
fi
/bin/echo 'Extracting README'
sed 's/^X//' <<'//go.sysin dd *' >README
   The Remote Troff Execution Package is a set of programs put together
at the University of Maryland in an attempt to redistribute troffs
so that no one machine is loaded down with a large number of troff
jobs while other machines sit idle.  There are a total of five programs
included with this package.  They are:

	(1)  loadd -- a load daemon that responds to requests for
		the load on the machine upon which it is running,
		sending back the 1, 5, and 15 minute load averages
		in fixed-point format.  The load daemon also uses
		information in /etc/loadd.conf to send back an estimate
		of the cpu power of the machine on which it runs (on
		a relative scale); this number, too, is returned in
		fixed-point format.

	(2)  rtroff -- the remote troff program proper, rtroff handles
		choosing the least loaded host in the list in /etc/rtrhosts
		and the copying of files to a troff running on the
		chosen machine.  Rtroff handles elimination of things
		like .so's as it transfers the files around, and should
		behave just like a normal troff for all but the most
		perverse of cases (well, it won't handle .so's in
		macros properly, but what did you expect?)  It is	
		smart enough to exec the troff directly if the local
		load is the lowest one that it finds, and is apparently
		*very* fast; local benchmarks seem to indicate that
		there is a 30-to-1 decrease in the amount of cpu time
		used between a local and a remote troff.  Of course,
		some other machine still gets crippled with the
		troff (i.e. we're not creating cycles out of nothing
		here)...

	(3)  rcatdvi -- a remote implementation of the Imagen catdvi
		program; this program is to catdvi as rtroff is
		to troff in pretty much every respect.

	(4)  ltroff -- a shell script that we use locally to queue
		up our Imagen jobs, with options to send the Impress
		output to stdout and to force a local troff (in
		those perverse cases with which rtroff has problems).

	(5)  load -- a program that one can use from the shell to
		find out if the load is above a certain level; ltroff
		uses this program to determine if the local load is
		high enough to consider going somewhere else.

   This package includes a Makefile, man entries for everything
but rcatdvi (which is so much like catdvi that it doesn't really
need one), and the source for everything.  More detailed installation
instructions can be found in the Makefile.  This package has been
tested on Pyramids, Suns, and vaxen, and seems to be pretty solid.

   I would like to thank Anthony Tse of NRL for his contributions
to this package (his modifications to the original rtroff provided
the impetus for the improvements that have been made to this
program), Fred Blonder, who wrote the original "load", and Chris Torek,
whose ideas as to the *right* way to do things are no doubt responsible
for much of the speed and readability increases present in this version
of rtroff.

   I am interested in hearing about whatever bugs you find in the
code, and in hearing about your opinions in general; feel free to
drop me a line.

	-Steve

Spoken: Steve Miller 	ARPA:	steve@maryland	Phone: +1-301-454-4251
CSNet:	steve@umcp-cs 	UUCP:	{seismo,allegra}!umcp-cs!steve
USPS: Computer Science Dept., University of Maryland, College Park, MD 20742
//go.sysin dd *
if [ `wc -c < README` != 3281 ]; then
	made=FALSE
	/bin/echo 'error transmitting "README" --'
	/bin/echo 'length should be 3281, not' `wc -c < README`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	/bin/chmod 644 README
	/bin/echo -n '	'; /bin/ls -ld README
fi
/bin/echo 'Extracting Makefile'
sed 's/^X//' <<'//go.sysin dd *' >Makefile
# Makefile for remote troff stuff
# Steven D. Miller (steve@maryland.ARPA)
# 
# To make the remote troff execution package:
#
# (1) Install a "rtroff" account on your machines.  This account should
# 	live in its own group and have an asterisk in the password field
#	for security reasons.
#
# (2) Install the configuration files for rtroff and rcatdvi to use.  These
#	files are used when these two programs search for hosts to run
#	remote troffs on.  These files just look like a list of host-
#	names; one of ours is, for example,
#
#		gymble
#		gyre
#		tove
#		mimsy
#
#	Note that if a config file is on machine foo, foo should be
#	in that config file; if it is not, rtroff et al will *never*
#	run locally.  The config file for rtroff is /etc/rtrhosts,
#	and the one for rcatdvi is /etc/rcathosts; these might do
#	better living in /usr/local/lib or somewhere like that...
#
#	At the same time, the loadd configuration file (/etc/loadd.conf)
#	should be created.  Included is the one we use; look inside
#	it for details.
#
# (3) Add a line to /etc/rc.local so that the load daemon will be
#	started on the machines in /etc/rtrhosts at boot time.
#	Also, add the following line to your /etc/services file
#	on all those machines so that the daemon will run OK:
#	"load		701/udp				# load status daemon"
#	Note that the daemon uses syslog() to do its error logging;
#	I don't know how well syslog() works in 4.2, so you might
#	consider changing that (loadd is pretty much a hybrid 4.2/4.3
#	program, and seems to work OK on our 4.2 machines...)
#
# (4) We run MDQS as our printer spooler; if you are using the ltroff
#	shell script and are not running MDQS, you will need to change
#	the "qpr -q imagen-imp" stuff so that it spools to your imagen
#	in whatever way necessary.  Note also that we have some
#	security hacks in ltroff (users can't print on our imagens
#	unless they are listed in /etc/restrict/mdqs/ltroff_users)
#	that you may want to toss.  The other tuneable parameter in
#	the ltroff shell script is the "load" variable; this specifies
#	the load cutoff below which jobe will not be considered for
#	remote processing.  The "load" program is used to make
#	this decision; see its man entry for details.
#
# (5) Do a "make all" followed by a "make install" to compile everything
#	and put it into place.  Note that you should define BIN and ETC
#	to be something appropriate for your site.  There is conditional
#	code for Sun workstations in load and loadd; add a "-Dsun" to
#	the CFLAGS line below to use it.

CFLAGS = -O
LINTFLAGS = -hbux
SCRIPT = README Makefile getloads.c loadd.8l loadd.c ltroff ltroff.1 \
	rcatdvi.c rload.h rtroff.1l rtroff.c load.c load.1l loadd.conf

# BIN is the place you want these programs to live
BIN = /usr/local/bin

# ETC is the place you want the load daemon to live.
ETC = /etc

SRC = loadd.c rtroff.c rcatdvi.c getloads.c load.c

# MANS are the full pathnames for the installed man entries; if you want
# them to reside somewhere else, you'll have to change the names...
MANS  = /usr/man/man1/ltroff.1 /usr/man/man1/rtroff.1l \
	/usr/man/man1/load.1l /usr/man/man8/loadd.8l
MAN1DIR = /usr/man/man1
MAN8DIR = /usr/man/man8

PROGS = loadd rtroff rcatdvi ltroff load

# KMEM is a group id that can open /dev/kmem; the load program
# needs to be able to access /dev/kmem to do its stuff.
KMEM = bin


default: all

install: ${ETC}/loadd ${BIN}/rtroff ${BIN}/rcatdvi ${BIN}/ltroff \
	${BIN}/load man

${ETC}/loadd: loadd
	@echo "Don't forget to make the /etc/services entry for loadd"
	cp loadd ${ETC}/loadd
	chown root ${ETC}/loadd

${BIN}/rtroff: rtroff
	cp rtroff ${BIN}/rtroff
	chown root ${BIN}/rtroff
	chmod 4775 ${BIN}/rtroff
	-ln -s ${BIN}/rtroff ${BIN}/rnroff

${BIN}/rcatdvi: rcatdvi
	cp rcatdvi ${BIN}/rcatdvi
	chown root ${BIN}/rcatdvi
	chmod 4775 ${BIN}/rcatdvi

${BIN}/ltroff: ltroff
	cp ltroff ${BIN}/ltroff

${BIN}/load: load
	cp load ${BIN}/load
	chgrp ${KMEM} ${BIN}/load
	chmod g+s ${BIN}/load

loadd: loadd.o
	cc ${CFLAGS} -o loadd loadd.o

rtroff: rtroff.o getloads.o
	cc ${CFLAGS} -o rtroff rtroff.o getloads.o

rcatdvi: rcatdvi.o getloads.o
	cc ${CFLAGS} -o rcatdvi rcatdvi.o getloads.o

load: load.o
	cc ${CFLAGS} -o load load.o

getloads.o: rload.h

man: ${MANS}

${MAN1DIR}/ltroff.1: ltroff.1
	cp ltroff.1 ${MAN1DIR}

${MAN1DIR}/load.1l: load.1l
	cp load.1l ${MAN1DIR}

${MAN1DIR}/rtroff.1l: rtroff.1l
	cp rtroff.1l ${MAN1DIR}
	-ln -s ${MAN1DIR}/rtroff.1l ${MAN1DIR}/rnroff.1l

${MAN8DIR}/loadd.8l: loadd.8l
	cp loadd.8l ${MAN8DIR}

all: loadd rtroff rcatdvi load

clean:
	-rm *.o loadd rcatdvi rnroff rtroff load

shar: ${SCRIPT}
	makescript -t  -o rtroff.sh ${SCRIPT}

lint:
	lint ${LINTFLAGS} loadd.c
	lint ${LINTFLAGS} rtroff.c getloads.c
	lint ${LINTFLAGS} rcatdvi.c getloads.c
	lint ${LINTFLAGS} load.c

ci:
	ci ${VERSION} ${SCRIPT}

co:
	co ${VERSION} ${SCRIPT}
//go.sysin dd *
if [ `wc -c < Makefile` != 4852 ]; then
	made=FALSE
	/bin/echo 'error transmitting "Makefile" --'
	/bin/echo 'length should be 4852, not' `wc -c < Makefile`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	/bin/chmod 644 Makefile
	/bin/echo -n '	'; /bin/ls -ld Makefile
fi
/bin/echo 'Extracting getloads.c'
sed 's/^X//' <<'//go.sysin dd *' >getloads.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/time.h>
#include <errno.h>
#include <fcntl.h>
#include <sysexits.h>
#include <nlist.h>
#include "rload.h"
X/*
 * the fixify macro turns a float or a double into the fixed-point
 * notation we use for everything.
 */
#define fixify(a)	((long) ((a) * 256.0))
#define	unfixify(a)	((float) ((a) / 256.0))

#ifdef FD_SETSIZE
#define BSD4_3
#endif

struct	rl_node {
    struct rload r_load;
    struct in_addr r_host;
    struct rl_node *r_next;
} *rl_head = NULL;

char	*malloc();

struct	hostent *
gethost(hostfile)
    char	*hostfile;
{
    struct in_addr addr, getminav();
    struct hostent *localhost;

    getrloads(hostfile);
    addr = getminav(rl_head);
#ifdef DEBUG
    fprintf(stderr, "addr is %lx\n", ntohl(addr.s_addr));
#endif
    localhost = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
#ifdef DEBUG
    fprintf(stderr, "host with the least load is %s\n",localhost->h_name);
#endif
    return(localhost);
}

X/*
 * Open a UDP socket to the load daemons on all the machines in
 * the file named in hostfile, and insert their loads into the
 * load list so we can later find the address of the machine
 * with the minimum load.
 */

getrloads(hostfile)
    char	*hostfile;
{
    int skt, nready;
#ifdef BSD4_3
    struct fd_set readfds, readyrd;
#else
    int readfds, readyrd;
#endif
    FILE *hosts;
    char hostname[40];
    struct timeval timeout;
    struct sockaddr_in laddr, faddr;
    struct rl_node *r;
    struct servent *serv;

    serv = getservbyname("load", "udp");
    if (serv == NULL) {
	fprintf(stderr,"getloads: load/udp: unknown service\n");
	return;
    }
    skt = socket (AF_INET, SOCK_DGRAM, 0);
    if (skt < 0) {
	perror ("getloads: couldn't get socket");
	return;
    }

    laddr.sin_family = AF_INET;
    laddr.sin_port = 0;
    laddr.sin_addr.s_addr = 0;	/* for grins */
    if (bind (skt, &laddr, sizeof (laddr)) < 0) {
	perror ("getloads: bind");
	(void) close (skt);
	return;
    }

    /* 
     * Now that the socket is set up, we open and read the hostfile
     * to get the machines we can (maybe) go to.  For each machine,
     * we poll it for its load, then put the load in the list
     * (until we hit eof).
     */

    hosts = fopen (hostfile, "r");
    if (hosts == NULL) {
	perror ("getloads: fopen of hostfile");
	(void) close (skt);
	return;
    }
    while (fgets (hostname, 40, hosts) != NULL) {
	char   *pos, *index();
	struct hostent  *h_ent;

	pos = index (hostname, '\n');
	if (pos != NULL)
	    *pos = '\0';
	h_ent = gethostbyname (hostname);
	if (h_ent == NULL) {
	    fprintf (stderr, "getloads: host %s unknown\n", hostname);
	    continue;
	}

	faddr.sin_family = AF_INET;
	faddr.sin_port = serv->s_port;
	bcopy(h_ent->h_addr, (char *)&faddr.sin_addr.s_addr, h_ent->h_length);
	if (sendto (skt, "l", 1, 0, (struct sockaddr *)&faddr,
            sizeof(faddr)) != 1) {
	        perror ("getloads: sendto");
	        continue;
	}

        /* 
         * And now we get the response and put it in our
         * load list.
         */
#ifdef BSD4_3
	FD_ZERO(&readfds);
	FD_SET(skt, &readfds);	/* turn on proper bit in set readfds */
#else
	readfds = 1 << skt;
#endif
	timeout.tv_sec = 5;
	timeout.tv_usec = 0;	/* 5 second */
  	readyrd = readfds;
#ifdef BSD4_3
#define MKSET(n)	((struct fd_set *) (n))
	nready = select (32, &readyrd, MKSET(NULL), MKSET(NULL), &timeout);
#else
	nready = select (32, &readyrd, (int *)NULL, (int *)NULL, &timeout);
#endif
	if (nready < 0) {
	    perror ("getloads: select");
	    (void) close (skt);
	    return;
	}
#ifdef BSD4_3
	if (FD_ISSET(skt, &readyrd)) {
#else
	if (readyrd & readfds) {
#endif

	    struct sockaddr_in raddr;
	    int rsize = sizeof (raddr);
	    int	nbytes, i;

	    r = (struct rl_node *) malloc (sizeof (struct rl_node));
	    if (r == NULL) {
		fprintf(stderr, "getloads: malloc failed\n");
		(void) close(skt);
		return;
	    }
	    if ((nbytes = recvfrom (skt, (char *)(&r->r_load),
		sizeof(struct rload), 0, (struct sockaddr *)&raddr, &rsize))
		!= sizeof(struct rload)) {
		    perror("getloads: recvfrom");
		    fprintf(stderr, "getloads: out of data from %lx (%d/%d bytes)\n",
			ntohl(raddr.sin_addr.s_addr), nbytes,
			sizeof(r->r_load));
		    free((char *)r);
		    continue;
	    };
#ifdef DEBUG
	    fprintf(stderr, "got reply from host %lx\n",
		ntohl(raddr.sin_addr.s_addr));
#endif
	    for (i=0; i<=2; i++)
		r->r_load.avgs[i] = ntohl((u_long)r->r_load.avgs[i]);
	    r->r_load.cpuumph = ntohl((u_long)r->r_load.cpuumph);
	    r->r_host = raddr.sin_addr;
	    r->r_next = rl_head;
	    rl_head = r;
	}
    }
    (void) fclose(hosts);
    return;
}

#define	RELAVG(v)	((float) (((v)->r_load.avgs[0] * 1.0) / \
	(v)->r_load.cpuumph))


struct	in_addr
getminav(rlp)
    struct rl_node *rlp;
{

    struct rl_node *minp;

    if (rlp == NULL) {
	fprintf(stderr, "getloads: no loads?!?\n");
	dolocalexec();
    }
    /*
     * find the first machine in the list that doesn't
     * have a zero umph factor (assume that those who do
     * don't want us to go there).
     */
    while (rlp && rlp->r_load.cpuumph == 0)
	rlp = rlp->r_next;
    if (rlp == NULL)	/* nowhere to go */
	dolocalexec();
    minp = rlp;
    rlp = rlp->r_next;
    while (rlp != NULL) {
#ifdef DEBUG
	fprintf(stderr, "getmin: looking at host %lx\n",
	    ntohl(rlp->r_host.s_addr));
#endif
	if (rlp->r_load.cpuumph != 0 && RELAVG(rlp) < RELAVG(minp))
	    /* skip host if no cpu power */
		minp = rlp;
	rlp = rlp->r_next;
    }
    return(minp->r_host);
}

X/*
 * Return true iff h is a synonym for ourselves.
 */
islocalhost(h)
    register struct hostent *h;
{
    register struct hostent *thishost;
    register char **sp;
    char hname[512];
    int laddr, gethostid();	/* lint says it's int */

    if (h->h_length == 4) {
	laddr = gethostid();
#ifdef DEBUG
	fprintf(stderr, "laddr.s_addr = %lx\n", laddr);
#endif
	if (bcmp((char *)&laddr, h->h_addr, h->h_length) == 0)
	    return (1);
    }
    /*
     * gethostid doesn't always work, since no one need do
     * a sethostid to make the system run.
     */
    if (gethostname(hname, sizeof hname))/* don't know who I am?! */
	return (0);
#ifdef DEBUG
    fprintf(stderr, "comparing names: %s vs %s\n", hname, h->h_name);
#endif
    if (strcmp(hname, h->h_name) == 0)
	return (1);
    /*
     * Finally, try canonical version (look up our name in the
     * host table).
     */
    thishost = gethostbyname(hname);
    if (thishost == 0)
	return (0);
#ifdef DEBUG
    fprintf(stderr, "comparing name: %s\n", thishost->h_name);
#endif
    if (strcmp(thishost->h_name, h->h_name) == 0)
	return (1);
    for (sp = thishost->h_aliases; *sp; sp++) {
#ifdef DEBUG
	fprintf(stderr, "comparing alias: %s\n", *sp);
#endif
	if (strcmp(*sp, h->h_name) == 0)
	    return (1);
    }
    return (0);
}
//go.sysin dd *
if [ `wc -c < getloads.c` != 6886 ]; then
	made=FALSE
	/bin/echo 'error transmitting "getloads.c" --'
	/bin/echo 'length should be 6886, not' `wc -c < getloads.c`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	/bin/chmod 644 getloads.c
	/bin/echo -n '	'; /bin/ls -ld getloads.c
fi
/bin/echo 'Extracting loadd.8l'
sed 's/^X//' <<'//go.sysin dd *' >loadd.8l
X.TH LOAD 8L 11-Aug-1985
X.SH NAME
loadd \- remote load daemon
X.SH SYNOPSIS
X.B loadd
X.SH DESCRIPTION
X.I Loadd
listens for incoming UDP packets on a port defined in the file
X.I /etc/services.
When it receives a packet containing the single character "l",
it sends back another packet containing the 1, 5, and 15-minute
load averages in the form of long integers in fixed-point format.  
The high 24 bits of each integer is the integer part of the
load; the low 8 bits is the fractional part, measured in 256ths.
X.PP
The returned packet also includes another fixed-point value that
represents the relative power of that cpu; this value is stored in
the file /etc/loadd.conf in a line of the form "localpower <float>".
Any line in /etc/loadd.conf beginning with a pound sign in the first
position is treated as a comment.  If the information in this file is
changed; the daemon can be told to reread the file by sending it
a HUP signal.
X.SH "SEE ALSO"
rtroff(1)
X.SH DIAGNOSTICS
Reported through syslog(3); if you don't have a working syslog, you
probably want to change the code.
X.SH AUTHOR 
Steve Miller
X.SH BUGS
//go.sysin dd *
if [ `wc -c < loadd.8l` != 1109 ]; then
	made=FALSE
	/bin/echo 'error transmitting "loadd.8l" --'
	/bin/echo 'length should be 1109, not' `wc -c < loadd.8l`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	/bin/chmod 644 loadd.8l
	/bin/echo -n '	'; /bin/ls -ld loadd.8l
fi
/bin/echo 'Extracting loadd.c'
sed 's/^X//' <<'//go.sysin dd *' >loadd.c
X/*
 * loadd -- accept UDP load status requests, and return fixed-point
 * indication of current load averages.
 *
 * Steven D. Miller (steve@maryland.ARPA)
 *
 */

#include <nlist.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <syslog.h>
#include "rload.h"
#define fixify(a)	((long) ((a) * 256.0))

int	kmem;
long	umph = 1;	/*
			 * current cpu power factor; 1/256 if unknown
			 */

main()
{
    struct  rload r_load;
    struct  sockaddr_in laddr, faddr;
    struct  servent *serv;
    int	skt, fsize = sizeof(faddr);
    int getumph();
    char    c;

#ifndef DEBUG
    if (fork())
	exit (0);
    { int s;
      for (s = 0; s < 10; s++)
	  (void) close(s);
      (void) open("/", 0);
      (void) dup2(0, 1);
      (void) dup2(0, 2);
      s = open("/dev/tty", 2);
      if (s >= 0) {
	  (void) ioctl(s, TIOCNOTTY, (char *)0);
	  (void) close(s);
      }
    }
#endif
    (void) signal(SIGHUP, getumph);	/* reconfigure on HUP */
    serv = getservbyname("load", "udp");
    if (serv == NULL) {
#ifdef DEBUG
	fprintf(stderr,"loadd: load/udp: unknown service\n");
#else
	syslog(LOG_ERR,"loadd: load/udp: unknown service\n");
#endif
	exit (1);
    }
    skt = socket(AF_INET, SOCK_DGRAM, 0);
    if (skt < 0) {
#ifdef DEBUG
	perror("loadd: socket");
#else
	syslog(LOG_ERR, "loadd: couldn't get socket (%m)");
#endif
	exit (1);
    }
    laddr.sin_family = AF_INET;
    laddr.sin_port = serv->s_port;
    laddr.sin_addr.s_addr = 0;	/* for grins */
    if (bind (skt, &laddr, sizeof (laddr)) < 0) {
#ifdef DEBUG
	perror("loadd: bind");
#else
	syslog(LOG_ERR, "loadd: bind (%m)");
#endif
	(void) close (skt);
	exit (1);
    }
    getumph();
    kmem = open("/dev/kmem", 0);
    if (kmem < 0) {
#ifdef DEBUG
	perror("loadd: open /dev/kmem");
#else
	syslog(LOG_ERR, "loadd: open /dev/kmem (%m)");
#endif
	(void) close (skt);
	exit (1);
    }
    for (;;) {
	if (recvfrom(skt, &c, 1, 0, (struct sockaddr *)&faddr, &fsize) != 1) {
#ifdef DEBUG
	    perror("loadd: recvfrom");
#else
	    syslog(LOG_WARNING, "loadd: recvfrom: %m");
#endif
	    continue;
	}
#ifdef DEBUG
	fprintf(stderr,"loadd: got request from %lx\n", 
	   ntohl(faddr.sin_addr.s_addr));
#endif
	loadav(&r_load);
	if (sendto(skt, (char *)&r_load, 4*sizeof(long), 0,
	    (struct sockaddr *)&faddr, sizeof(faddr)) != 4*sizeof(long)) {
#ifdef DEBUG
	    perror("loadd: sendto");
#else
	    syslog(LOG_WARNING, "loadd: sendto: %m");
#endif
	    continue;
	}
    }
}

struct nlist avenrun[] = {
    {"_avenrun"},
    0
};

loadav(avg)
    struct rload *avg;
{
#ifndef sun
    double kavg[3];
#endif
    static beenhere;
    long lseek();

    if (!beenhere) {
	beenhere++;
	nlist("/vmunix", avenrun);
    }
    if (avenrun[0].n_type == 0) {
#ifdef DEBUG
	fprintf(stderr, "loadd: can't find _avenrun\n");
#else
	syslog(LOG_WARNING, "loadd: can't find _avenrun");
#endif
	avg->avgs[0] = avg->avgs[1] = avg->avgs[2] = 0xfffe;
	return;
    }

    (void) lseek(kmem, (long) avenrun[0].n_value, 0);
	      
#ifndef sun
    if (read(kmem, (char *)kavg, 3*sizeof (double)) != 3*sizeof(double)) {
#ifdef DEBUG
	perror("loadd: bad read");
#else
	syslog(LOG_WARNING, "loadd: bad read (%m)");
#endif
	avg->avgs[0] = avg->avgs[1] = avg->avgs[2] = 0xfffe;
    }
    /*
     * If we are using floats in the kernel, let's convert
     * them to fixed-point numbers to send across the net.
     */
    avg->avgs[0] = htonl(fixify(kavg[0]));
    avg->avgs[1] = htonl(fixify(kavg[1]));
    avg->avgs[2] = htonl(fixify(kavg[2]));
#else
    /* note sun byte order == net byte order - no conversion needed */
    if (read(kmem, (char *)avg->avgs, 3*sizeof(long)) != 3*sizeof(long)) {
#ifdef DEBUG
	perror("loadd: bad read");
#else
	syslog(LOG_WARNING, "loadd: bad read (%m)");
#endif
	avg->avgs[0] = avg->avgs[1] = avg->avgs[2] = 0xfffe;
    }
#endif
    avg->cpuumph = htonl(umph);
    return;
	
}

getumph()
{
    FILE *umphfile;
    char buf[512];
    float floatumph;

    umphfile = fopen("/etc/loadd.conf", "r");
    if (umphfile == NULL) {
#ifdef DEBUG
	perror("loadd: open /etc/loadd.conf");
#else
	syslog(LOG_ERR, "loadd: open /etc/loadd.conf (%m)");
#endif
	/* note that we leave the umph at its old value, if any */
    }
    else {
	while(fgets(buf, sizeof(buf), umphfile) != NULL) {
	    if (buf[0] == '#') /* comment line */
		continue;
	    if (sscanf(buf, "localpower %f", &floatumph) < 1) {
#ifdef DEBUG
	        fprintf(stderr, "loadd: /etc/loadd.conf: bad format\n");
#else
	        syslog(LOG_ERR, "loadd: /etc/loadd.conf: bad format");
#endif
		(void) fclose(umphfile);
	        return;	/* leave umph at old value */
	    }
	umph = fixify(floatumph);
	(void) fclose(umphfile);
	}
    }
}
//go.sysin dd *
if [ `wc -c < loadd.c` != 4759 ]; then
	made=FALSE
	/bin/echo 'error transmitting "loadd.c" --'
	/bin/echo 'length should be 4759, not' `wc -c < loadd.c`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	/bin/chmod 644 loadd.c
	/bin/echo -n '	'; /bin/ls -ld loadd.c
fi
/bin/echo 'Extracting ltroff'
sed 's/^X//' <<'//go.sysin dd *' >ltroff
#! /bin/csh -f
# troff to the IMAGEN imPrint printer
# /etc/renice 4 $$ >& /dev/null
set flags=(-rv1) noglob fonts=() cflags=() host=() rshhost=()
set macp=(/usr/lib/tmac/tmac.vcat)
set facp=(-F/usr/lib/imagen/font/ftXX )
set PATH=(. /usr/local/bin /usr/bin /usr/ucb )
set path=($PATH)
set local load=(1)
unset t w dv i doremote
top:
	if ($#argv > 0) then
		switch ($argv[1])

		case -sd:
			set dv
			shift argv
			goto top

		case -si:
			set i
			shift argv
			goto top

		case -sc:
		case -t:
			set t
			shift argv
			goto top

		case -w:
			set w
			shift argv
			goto top

		case -x:
			set macp=()
			shift argv
			goto top

		case -F:
			shift argv
			if ($#argv > 0) then
				set flags = ($flags -F$argv[1]/ftXX)
				set cflags = ($cflags -C$argv[1]/catab)
				set facp = ()
				set w
				shift argv
			endif
			goto top

		case -c*:
			set cflags = ($cflags $argv[1])
			shift argv
			goto top
		case -h:
			set host = ($argv[1])
			set doremote
			shift argv
			set host= ($host $argv[1])
			set rshhost= ($argv[1])
			shift argv
			goto top

		case -l:
			unset local
			shift argv
			goto top

		case -r:
			shift argv
			set load=($argv[1])
			shift argv
			goto top

		case -*:
			set flags = ($flags $argv[1])
			shift argv
			goto top

		endsw
	endif
if ($#argv == 0) then
	set argv=(-)
	set banner=stdin
else
	set banner="$argv"
endif
X/usr/local/bin/load -s $load
if ($status == 0 && $?local) then
	set troffcmd=(rtroff)
	set catcmd=(/usr/local/bin/rcatdvi $host )
else
	set troffcmd=(troff)
	set catcmd=(/usr/local/bin/catdvi)
	set host=()
endif
if ($?doremote) then
	set troffcmd=(rtroff)
	set catcmd=(rcatdvi $host )
endif
if ($?t) then
    /usr/bin/troff -x -t $flags $facp $macp $*
else
    /usr/bin/fgrep -s ,`/usr/ucb/whoami`, /etc/restrict/mdqs/ltroff_users
    if ($status) then
         echo Sorry, you\'re not authorized to send directly to the Imagen.
	else if ($?dv) then
	    $troffcmd $host -x -t $flags $facp $macp $* | $catcmd -b "$banner" $cflags
	else if ($?i) then
	    $troffcmd $host -x -t $flags $facp $macp $* | $catcmd -a -b "$banner" $cflags 
	else if ($?w) then
	    $troffcmd $host -x -t $flags $facp $macp $* | $catcmd -b "$banner" $cflags
	else 
	    $troffcmd $host -x -t $flags $facp $macp $* | $catcmd -a -b "$banner" $cflags | /usr/bin/qpr -q imagen-imp
#######else
#######    /usr/bin/troff -x -t $flags $facp $macp $* | /usr/ucb/lpr -Pimagen -t
	endif
endif
//go.sysin dd *
if [ `wc -c < ltroff` != 2413 ]; then
	made=FALSE
	/bin/echo 'error transmitting "ltroff" --'
	/bin/echo 'length should be 2413, not' `wc -c < ltroff`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	/bin/chmod 755 ltroff
	/bin/echo -n '	'; /bin/ls -ld ltroff
fi
/bin/echo 'Extracting ltroff.1'
sed 's/^X//' <<'//go.sysin dd *' >ltroff.1
X.TH LTROFF 1 "4 October 1984"
X.SH NAME
ltroff \- text formatting on Imagen printer
X.SH SYNOPSIS
X.B ltroff
[ macro option ] ...
[ option ] ...
[ file ] ...
X.SH DESCRIPTION
X.I Ltroff
formats text in the named
X.I files
for printing on an Imprint-10 laser printer.  If the local load is above
seven, then the processing needed is done remotely; otherwise, the
processing is done locally.
X.I Ltroff
accepts files that would be used as input for
X.I troff
transparently, as far as the user is concerned.  In addition to
standard
X.I troff
options, the options that are specific to
X.I ltroff
are:
X.TP
X.BR \-sd " or " \-w
Leave output on standard output in DVI form.
X.TP
X.BI \-si
Leave output on standard output in imPress form.
This output may be printed with "qpr -q imagen-imp".
X.TP
X.BR \-sc " or " \-t
Leave output on standard output in C/A/T form.
X.TP
X.B \-x
Turn off the use of the standard macro package /usr/lib/tmac/tmac.vcat.
X.TP
X.B \-h [hostname]
Do the processing on machine
X.I hostname, regardless of load.
X.TP
X.B \-l
Force processing to be done on local machine, regardless of load.
X.TP
X.B \-Ffontdir
Use the files in this directory as the font width tables for
X.I troff.
The default font spacing files are in /usr/lib/imagen/font/ftXX.
X.TP
X.BR \-c " arg"
Pass arg to
X.I catdvi.
X.SH BUGS
X.PP
The options to leave files in DVI form are not currently implemented
due to modifications made in catdvi and dviimp.  The output
produced is in imPress form.
X.SH FILES
X.ta \w'/usr/lib/tmac/tmac.vcat  'u
X.br
X/usr/lib/tmac/tmac.vcat		C/A/T macro file
X.SH "SEE ALSO"
X.br
troff(1), rtroff(1), catdvi(1), dviimp(1), qpr(1), istat(1)
//go.sysin dd *
if [ `wc -c < ltroff.1` != 1624 ]; then
	made=FALSE
	/bin/echo 'error transmitting "ltroff.1" --'
	/bin/echo 'length should be 1624, not' `wc -c < ltroff.1`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	/bin/chmod 644 ltroff.1
	/bin/echo -n '	'; /bin/ls -ld ltroff.1
fi
/bin/echo 'Extracting rcatdvi.c'
sed 's/^X//' <<'//go.sysin dd *' >rcatdvi.c
X/*
 *
 * rcatdvi -- send stdin to catdvi on remote
 * machine, get stdout and stderr back.  Invoked as
 * "rcatdvi [-h host] [catdvioptions], runs setuid to make
 * connection, then does a setreuid(getuid, getuid) to go back
 * to being the "real" user.
 *
 */


#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <pwd.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <signal.h>
#include <errno.h>

#define LINESIZ 133
#define BUFSIZE 1024

char	**args;
char	*strcpy(), *strcat();
FILE 	*infile, *outfile;
int	errfd, iofd, pid1, pid2;
extern	int errno;

main(argc,argv)
int	argc;
char	*argv[];
{
	char	cmdline[LINESIZ], *host = NULL;
	struct	servent	*serv, rserv;
	int	i, firstarg, uid;
	int	reapchild();

	(void) signal(SIGCHLD, reapchild);
	args = argv;
	if((serv=getservbyname("shell","tcp"))==NULL) {
		perror("rcatdvi:getservbyname");
		exit(-1);
	}
	rserv = *serv;	/* copy data so we don't overwrite later */
	if (strcmp("-h",argv[1])==0) {
		host=argv[2];
		firstarg=3;
	}
	else
		firstarg=1;
	(void) strcat(cmdline,"/usr/local/bin/catdvi");
	if (argc>(firstarg-1)) {
		for (i=firstarg;i<argc;i++) {
			(void) strcat(cmdline," ");
			(void) strcat(cmdline,argv[i]);
#ifdef DEBUG
	fprintf(stderr, "added ' %s' to cmd line (arg %d of %d)\n",
		argv[i], i, argc);
#endif
		}
	}
#ifdef DEBUG
	fprintf(stderr, "command line is '%s'\n", cmdline);
#endif
	if (host == NULL) {
		/*
		 * since the user didn't tell us where to go,
		 * let's find the host with the least load and
		 * go to it.
		 */
		struct	hostent *fhent, sfhent, *gethost();
		char	hname[512];

		fhent = (struct hostent *)gethost("/etc/rcathosts");
		if (fhent == NULL) {
			fprintf(stderr, "rcatdvi: remote hostent unknown\n");
			exit (1);
		}
		(void) strcpy(hname, fhent->h_name);
		sfhent = *fhent;
		sfhent.h_name = hname;
		if (islocalhost(&sfhent))
			dolocalexec();
		if((iofd=rcmd(&sfhent.h_name, rserv.s_port, "rtroff",
			"rtroff", cmdline, &errfd))<0) {
			perror("rcatdvi: rcmd");
			fprintf(stderr,"Host %s down - try again later\n",
				sfhent.h_name);
			exit (1);
		}
	} else if((iofd=rcmd(&host, rserv.s_port, "rtroff",
		"rtroff", cmdline, &errfd))<0) {
			perror("rcatdvi: rcmd");
			exit(-1);
		}
	uid = getuid();
	(void) setreuid(uid, uid);	/* become the real user */
	/*
	 * Now we fork off the processes to handle sending
	 * and receiving.
	 */
	if ((pid1 = fork()) < 0) {
		perror("rcatdvi: fork");
		exit(1);
	}
	if (pid1 == 0) {
		/*
		 * Then we are child #1 and will be sending input
		 * to the other side.
		 */
		if (( outfile = fdopen (iofd,"w")) == NULL) {
			perror("rcatdvi: can't fdopen socket for output");
			exit(1);
		}
		sendstdin(outfile);
		(void) fflush(outfile);
		(void) shutdown(iofd, 1);
		(void) fclose(outfile);
#ifdef DEBUG
	fprintf(stderr, "rcatdvi: send process finished normally\n");
#endif
		exit(0);
	}
	/*
	 * Parent.
	 */
	if ((pid2 = fork()) < 0) {
		perror("rcatdvi: fork");
		(void) kill(pid1, SIGTERM);
		exit(1);
	}
	if (pid2 == 0) {
		/*
		 * Then we are the second child and will
		 * be reading results from the other side.
		 */
		if (( infile = fdopen(iofd,"r")) == NULL) {
			perror("rcatdvi: can't fdopen socket for input");
			(void) kill(pid1, SIGTERM);
			exit(1);
		}
		getresults(infile);
		(void) fclose(infile);
#ifdef DEBUG
	fprintf(stderr, "rcatdvi: recv process finished normally\n");
#endif
		exit(0);
	}
	/*
	 * Parent again -- read errors and wait for kids to exit.
	 */
	geterrsandwait(errfd);
	(void) close(errfd);
	exit(0);
}

sendstdin(fl)
	register FILE *fl;
{
	register int nread;
	char	buf[BUFSIZE];

	while((nread = fread(buf, sizeof(char), sizeof(buf), stdin)) > 0)
		if (fwrite(buf, sizeof(char), nread, fl) != nread) {
			perror("rcatdvi: fwrite");
			(void) shutdown(iofd, 1); /* cause SIGPIPE sometime */
			exit(23);
		}
}

getresults(fl)
	register FILE *fl;
{
	register int nread;
	char	buf[BUFSIZE];

	while ((nread = fread(buf, sizeof(char), sizeof(buf), fl)) > 0)
		if (fwrite(buf, sizeof(char), nread, stdout) != nread) {
			perror("rcatdvi: fwrite to stdout");
			(void) kill(pid1, SIGTERM);
			exit(23);
		}
}

geterrsandwait(skt)
	register int skt;
{
	register int nread;
	char buf[BUFSIZE];
	union wait status;

	while ((nread = read(skt, buf, sizeof(buf))) != 0) {
		if (nread < 0)
			if (errno == EINTR) /* SIGCHLD got us */
				continue;
			else
				break;
		(void) write(2, buf, nread);
	}
	/*
	 * Now we wait for the kids to finish.
	 */
	while (wait(&status) >= 0)
		;
}

reapchild()
{
	struct rusage rusage;
	union wait status;

	while (wait3(&status, WNOHANG, &rusage) > 0)
#ifdef DEBUG
	fprintf(stderr, "rcatdvi: wait3: got one kid\n");
#else
		;
#endif
}

dolocalexec()
{
	int uid;

	uid = getuid();
	(void) setreuid(uid, uid);
	execv("/usr/local/bin/catdvi", args);
	perror("rcatdvi: exec of catdvi");
	exit (1);
}

//go.sysin dd *
if [ `wc -c < rcatdvi.c` != 4906 ]; then
	made=FALSE
	/bin/echo 'error transmitting "rcatdvi.c" --'
	/bin/echo 'length should be 4906, not' `wc -c < rcatdvi.c`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	/bin/chmod 644 rcatdvi.c
	/bin/echo -n '	'; /bin/ls -ld rcatdvi.c
fi
/bin/echo 'Extracting rload.h'
sed 's/^X//' <<'//go.sysin dd *' >rload.h
X/*
 * Structure of a remote load packet (on top of UDP).
 * We use longs with 24 bits of resolution before the
 * decimal point, and 8 bits of resolution after the
 * decimal point.  This way, too, I don't have to worry
 * about whether or not a double is in some funny byte
 * order.  We also return a long representing our
 * relative cpu power (again, in fixed-point format).
 */

struct rload {
	long	avgs[3];
	long	cpuumph;	/* relative power of cpu */
};
//go.sysin dd *
if [ `wc -c < rload.h` != 460 ]; then
	made=FALSE
	/bin/echo 'error transmitting "rload.h" --'
	/bin/echo 'length should be 460, not' `wc -c < rload.h`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	/bin/chmod 644 rload.h
	/bin/echo -n '	'; /bin/ls -ld rload.h
fi
/bin/echo 'Extracting rtroff.1l'
sed 's/^X//' <<'//go.sysin dd *' >rtroff.1l
X.TH RTROFF 1 "16 September 1984"
X.SH NAME
rtroff, rnroff \- remote text formatting and typesetting
X.SH SYNOPSIS
X.B rtroff
[ \-h hostname ]
[ option ] ...
[ file ] ...
X.PP
X.B rnroff
[ \-h hostname ]
[ option ] ...
[ file ] ...
X.SH DESCRIPTION
X.I Rtroff
formats text in the named
X.I files
for printing on a Graphic Systems C/A/T phototypesetter;
X.I rnroff
is used for typewriter-like devices.
Both of these commands act like
X.I troff(1)
and
X.I nroff(1)
,except that the text processing is done on a remote machine.
The \-h option may be used to specify the remote host for processing;
if no host is specified, the file /etc/rtrhosts is searched and a
connection is attempted to whichever machine in that file has the
lowest load at present.  If no connection can be made, the user is
advised to try again later..PP
Load determination is by way of the load status daemon.
X.I
loadd(8).
X.SH SEE ALSO
troff(1), loadd(8)
X.SH BUGS
The troff directives '.so', '.nx', and '.rd' will not be handled
correctly if part of a macro definition or conditional statement,
but will be handled correctly if used as a deferred (i.e. "'so")
directive.
X.PP
Use of the '.pi' directive may produce unpredictable results.
X.PP
Files that use the '\-man' macros may have unusual page titles; i.e.
files sent from a Vax to a Pyramid 90X will not say "Unix Programmers'
Manual" but will say "Pyramid OSX Users' Manual."
X.PP
It should be noted that if the local host
is specified with the \-h option, the troff or nroff will indeed take
place on the local machine, but will use the network as if it
was taking place on a remote host; i.e. all the files will be sent over 
the net and the results will be read off a socket.  This behavior may help to
track down network-related problems with this program, but may
be inconvenient at other times; I'm not sure if this is a bug or
a feature.
X.SH AUTHOR
Steven D. Miller
//go.sysin dd *
if [ `wc -c < rtroff.1l` != 1886 ]; then
	made=FALSE
	/bin/echo 'error transmitting "rtroff.1l" --'
	/bin/echo 'length should be 1886, not' `wc -c < rtroff.1l`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	/bin/chmod 644 rtroff.1l
	/bin/echo -n '	'; /bin/ls -ld rtroff.1l
fi
/bin/echo 'Extracting rtroff.c'
sed 's/^X//' <<'//go.sysin dd *' >rtroff.c
#define BUFSIZE 1024
#define LINESIZ 133
#define MAXARGS 256
#define FALSE 0
#define TRUE 1
#define	eprintf(s)	fprintf(stderr, s, args[0], sys_errlist[errno])

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>

int	skt, errskt;
FILE	*inskt, *outskt;
extern	int errno;
char	*hostname;
int	catdvi = FALSE;
int	troff = FALSE;
char	**args;
char	*strcpy(), *strcat();
int	pid1, pid2;
extern	char *sys_errlist[];

main(argc, argv)
	int argc;
	char *argv[];
{
	int	uid, needstdin = FALSE;
	char	tcmd[BUFSIZE];
	char 	*fargv[MAXARGS];
	int	reapchild();
	struct	servent	*serv, rserv;

	(void) signal(SIGCHLD, reapchild);

	args = argv;
	needstdin = parseargs(argc, argv, fargv, tcmd);
	if ((serv = getservbyname("shell", "tcp")) == NULL) {
		eprintf("%s: getservbyname: %s\n");
		exit(1);
	}
	rserv = *serv;	/* copy data to avoid loss on next getservent() */
	if (hostname != NULL) {
		if ((skt = rcmd(&hostname, rserv.s_port, "rtroff",
		    "rtroff", tcmd, &errskt)) < 0) {
			eprintf("%s: rcmd: %s\n");
			exit(1);
		}
	}
	else  {
		/*
		 * If the user has not specified a host name, then
		 * we use gethost() to get the host with the least
		 * load.  Ideally, we should traverse the whole
		 * list in order if the first machine doesn't respond,
		 * but it should be pretty safe not to bother (after
		 * all, it just responded to our load request a second
		 * ago...) 
		 */
		struct	hostent *fhent, sfhent, *gethost();
		char	hname[512];

		fhent = gethost("/etc/rtrhosts");
		if (fhent == NULL) {
			fprintf(stderr,"%s: remote host unknown\n", argv[0]);
			exit (1);
		}
		/*
		 * Now we look at the host entry for the ideal
		 * host, and compare it to the host entry for the
		 * current host.  If the addresses are the same,
		 * then we make life easy and execv() things off
		 * locally.
		 */
		(void) strcpy(hname, fhent->h_name); /* static storage */
		sfhent = *fhent;
		sfhent.h_name = hname;
		if (islocalhost(&sfhent))
			dolocalexec();
#ifdef DEBUG
		fprintf(stderr,"host is %s, port is %d\n", sfhent.h_name,
		    ntohs(rserv.s_port));
#endif
		if ((skt = rcmd(&sfhent.h_name, rserv.s_port, "rtroff",
		    "rtroff", tcmd, &errskt)) < 0) {
			eprintf("%s: rcmd: %s\n");
			fprintf(stderr, "Host %s down - try again later\n",
			    sfhent.h_name);
			exit(1);
		}
	}
	uid = getuid();
	(void) setreuid(uid, uid);	/* become the real user */
	/*
	 * Now, life gets interesting.  We have to fork off
	 * some processes to handle sending and receiving
	 * info off all these sockets.  The parent will read
	 * the stderr channel; one child will be responsible
	 * for reading data from the input files and sending
	 * it to the remote troff, and another will be responsible
	 * for reading the results of the troff off the other
	 * side and sending them to stdout.
	 */

	if ((pid1 = fork()) < 0) {
		eprintf("%s: fork: %s\n");
		exit(1);
	}
	if (pid1 == 0) {
		/*
		 * Then we are child #1 and will be sending
		 * input to the other side.
		 */
		if ((outskt = fdopen(skt, "w")) == NULL) {
			eprintf("%s: fdopen for write: %s\n");
			exit(9);
		}
		sendfiles(fargv);
		if (needstdin)
			sendonefile(stdin);
		(void) fflush(outskt);
		(void) shutdown(skt, 1); /* ain't gonna send data no more */
		(void) fclose(outskt);
#ifdef DEBUG
	fprintf(stderr, "rtroff: send process finished normally\n");
#endif
		exit(0);
	}
	/*
	 * Parent.
	 */
	if ((pid2 = fork()) < 0) {
		eprintf("%s: fork: %s\n");
		(void) kill(pid1, SIGTERM);
		exit(1);
	}
	if (pid2 == 0) {
		/*
		 * Then we are the second child and will be reading
		 * results from the other side.
		 */
		if ((inskt = fdopen(skt, "r")) == NULL) {
			eprintf("%s: fdopen for read: %s\n");
			(void) kill(pid1, SIGTERM);
			exit(9);
		}
		getresults(inskt);
		(void) fclose(inskt);
#ifdef DEBUG
	fprintf(stderr, "rtroff: recv process finished normally\n");
#endif
		exit(0);
	}
	/*
	 * Parent again -- read errors and wait for kids to exit.
	 */
	geterrsandwait(errskt);
	(void) close(errskt);
	exit(0);
}

X/*
 * Is string "small" contained somewhere within "big"?  If so, return
 * a pointer to the first occurrence.
 */
char *
sindex(big, small)
	char *big;
	register char *small;
{
	register char *cp;
	int len = strlen(small);

	for (cp = big; *cp; cp++)
		if (*cp == *small && strncmp(cp, small, len) == 0)
			return (cp);
	return (NULL);
}

dolocalexec()
{
	int uid;

	if (catdvi)	/* I don't want to set up all these pipes... */
		return;
#ifdef DEBUG
	fprintf(stderr, "Doing local exec\n");
#endif
	uid = getuid();
	(void) setreuid(uid, uid);
	if (troff)
		execv("/usr/bin/troff", args);
	else
		execv("/usr/bin/nroff", args);
	eprintf("%s: exec: %s\n");
	exit(1);
}

int
parseargs(argc, argv, fargv, tcmd)
	int argc;
	char *argv[];
	char *fargv[];
	char *tcmd;
{
	int cnt = 1;
	int tostdout = FALSE;
	int needstdin = FALSE;
	int fcnt = 0;
	char *index();

	if (sindex(argv[0], "troff") != NULL) {
		(void) strcpy(tcmd, "/usr/bin/troff ");
		troff++;
	}
	else
		(void) strcpy(tcmd, "/usr/bin/nroff ");
	hostname = NULL;
	cnt = 1;
	while (cnt < argc) {
		char *arg;

		if (*argv[cnt] == '-') {
			arg = argv[cnt];
			arg++;
			switch (*arg) {
			case '\0':
				needstdin = TRUE;
				break; /* don't copy this arg, since troff
					  doesn't handle options after '-'
					  properly.  It will be put on the
					  end of the argument list later. */
			case 's':
			case 'i':
				needstdin = TRUE;
				(void) strcat(tcmd, argv[cnt]);
				(void) strcat(tcmd, " ");
				break;
			case 't':
			case 'a':
				tostdout = TRUE;
				(void) strcat(tcmd, argv[cnt]);
				(void) strcat(tcmd, " ");
				break;
			case 'h':
				hostname = argv[cnt+1];
				cnt++;
				break;
			case 'c':
				catdvi++;
				break;
			default:
				(void) strcat(tcmd, argv[cnt]);
				(void) strcat(tcmd, " ");
			}
			cnt++;
		}
		else { /* must be a file name */
			fargv[fcnt++] = argv[cnt];
			cnt++;
		}
	}
	if (tostdout == FALSE && sindex(tcmd, "nroff") == 0) /* not nroff */
		(void) strcat(tcmd, "-t "); /* force to stdout */
	(void) strcat(tcmd, "-");  /* must act as if reading only from stdin */
	fargv[fcnt] = 0; /* terminate lists */
	if (catdvi && troff)	/* invoke catdvi and queue it */
		(void) strcat(tcmd, " | /usr/local/bin/catdvi -a -b stdin | qpr -q imagen-imp");

	return (needstdin);
}


sendfiles(fargv)
	char *fargv[];
{
	int cnt = 0;

	if (fargv[0] == 0) {
		sendonefile(stdin);
		return;
	}
	while (fargv[cnt] != 0) {
		FILE *ftosend;
		
		if ((ftosend = fopen(fargv[cnt], "r")) == NULL) {
			fprintf(stderr, "%s: could not open file %s: %s\n",
				args[0], fargv[cnt], sys_errlist[errno]);
			(void) shutdown(skt, 1); /* will eventually SIGPIPE */
			exit(-1);
		}
		else {
			sendonefile(ftosend);
			cnt++;
		}
	}
}

X/*
 * Send a single stdio file to the server, handling any .so-type
 * indirections.  N.B.: when sendonefile returns, fd has been
 * fclose()ed.
 */
sendonefile(fd)
	register FILE *fd;
{
	char buf[BUFSIZ];
#define TCMD(s) \
	((buf[0] == '.' || buf[0] == '\'') && strncmp(&buf[1], s, 2) == 0)

	while(fgets(buf, BUFSIZ, fd) != NULL) {
		if (TCMD("so"))	/* include .so file in stream */
			getso(buf);
		else if (TCMD("nx")) {	/* send .nx file */
			getso(buf);
			(void) fclose(fd);
			return;
		}
		else if (TCMD("rd"))	/* handle .rd instruction */
			getrd(buf);
		else
			fputs(buf, outskt);
	}
	(void) fclose(fd);
	return;
}

X/*
 * Extracts an nroff/troff style name from a .so, .nx, or .rd command.
 */
getfname(cmd, name)
	register char *cmd, *name;
{

	cmd += 3;		/* skip .so, .nx, whatever */
	while (*cmd == ' ' || *cmd == '\t')
		cmd++;
	while (*cmd && *cmd != ' ' && *cmd != '\t' && *cmd != '\n')
		*name++ = *cmd++;
	*name = '\0';
}

getso(buf)
	char *buf;
{
	char sobuf[LINESIZ];
	FILE *sofd;

	getfname(buf, sobuf);
	if ((sofd = fopen(sobuf, "r")) == NULL) {
		fprintf(stderr, "%s: cannot open %s: %s\n", args[0], sobuf,
		    sys_errlist[errno]);
		(void) shutdown(skt, 1); /* eventually cause SIGPIPE */
		exit(-1);
	}
	sendonefile(sofd);
}

getrd(buf)
	char *buf;
{
	char prompt[LINESIZ];
	register int ichr;
	char chr;

	getfname(buf, prompt);
	fprintf(stderr, "%s", prompt);
	while ((ichr = getchar()) != EOF) {
		if (ichr == '\n') {
			fprintf(stderr, "%s", prompt);
			ichr = getchar();
			if (ichr == '\n' || ichr == EOF)
				break;
			(void) write(skt, "\n", 1);
		}
		chr = ichr;
		(void) write(skt, &chr, 1);
	}
	/* should really pass the end of the line on here */
}

getresults(fd)
	register FILE *fd;
{
	register int nread;
	char buf[BUFSIZE];

	while ((nread = fread(buf, sizeof(char), sizeof(buf), fd)) > 0) {
#ifdef DEBUG
	fprintf(stderr, "getresults: read %d\n", nread);
#endif
		if (fwrite(buf, sizeof(char), nread, stdout) != nread) {
			eprintf("%s: write of results: %s\n");
			(void) kill(pid1,SIGTERM);
			exit (23);
		}
	}
}

geterrsandwait(eskt)
	register int eskt;
{
	register int nread;
	char buf[BUFSIZE];
	union wait status;

	while ((nread = read(eskt, buf, sizeof(buf))) != 0) {
		if (nread < 0)
			if (errno == EINTR) /* SIGCHLD got us */
				continue;
			else
				break;
		(void) write(2, buf, nread);
	}
	/*
	 * Now we wait for the kids to finish up.
	 */
	while (wait(&status) >=0)
#ifdef DEBUG
	fprintf(stderr, "rtroff: wait: got one kid\n");
#else
		;
#endif
}

reapchild()
{
	struct rusage rusage;
	union wait status;

	while (wait3(&status, WNOHANG, &rusage) > 0)
#ifdef DEBUG
	{
		fprintf(stderr, "rtroff: wait3: got one kid\n");
		fprintf(stderr, "resource usage:\n");
		fprintf(stderr, "  sys time: %d sec, %d usec\n",
		    rusage.ru_stime.tv_sec, rusage.ru_stime.tv_usec);
		fprintf(stderr, "  user time: %d sec, %d usec\n",
		    rusage.ru_utime.tv_sec, rusage.ru_utime.tv_usec);
	}
#else
		;
#endif
}
//go.sysin dd *
if [ `wc -c < rtroff.c` != 9831 ]; then
	made=FALSE
	/bin/echo 'error transmitting "rtroff.c" --'
	/bin/echo 'length should be 9831, not' `wc -c < rtroff.c`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	/bin/chmod 644 rtroff.c
	/bin/echo -n '	'; /bin/ls -ld rtroff.c
fi
/bin/echo 'Extracting load.c'
sed 's/^X//' <<'//go.sysin dd *' >load.c
#ifndef lint
static char *scssid = "@(#)load.c	(University of Maryland) Oct 7, 1981";
static char RCSid[] = "@(#)$Header: /usr/steve/rtroff/RCS/load.c,v 2.1 85/09/09 11:51:55 steve Exp $";
#endif lint

X/*
 * load.c - returns true if the system load is >= value given by argv[1]
 *
 * 	Cannibalized from rogue (main.c) by Fred Blonder, Oct 7, 1981
 *
 * 	Modified by Steve Miller to work on Suns.
 */

#include <sysexits.h>
#include <nlist.h>
#include <stdio.h>

#define fixify(a)	((long) ((a) * 256.0))

main(argc, argv)
    int argc;
    char *argv[];
{
    double  maxload = 0.0, atof();
#ifdef sun
    long    avec[3];
#else
    double  avec[3];
#endif
    char   *yes_msg = "Yes\n",
           *no_msg = "No\n";

    while (argc > 2 && argv[1][0] == '-') {
	switch (argv[1][1]) {
	    case 's': 
		yes_msg = no_msg = "";
		break;

	    case 'v': 
		yes_msg = "System load too high\n";
		no_msg = "System load not too high\n";
		break;

	    default: 
		printf ("Load: option ``%s'' not recognized, ignored.\n", argv[1]);
	}
	argc--;
	argv++;
    }

    loadav (avec);

    if (argc < 2) {
#ifdef sun
	printf ("%5.2f\n", avec[0] / 256.0);
#else
	printf ("%5.2f\n", avec[0]);
#endif
	exit (EX_OK);
    }

    if ((maxload = atof (argv[1])) <= 0.0) {
	printf ("Load: absurd load value: %f\n", maxload);
	exit (EX_DATAERR);
    }

#ifdef sun
    if (avec[0] >= fixify(maxload)) {
#else
    if (avec[0] >= maxload) {
#endif
	printf (yes_msg);
	exit (EX_OK);
    }
    else {
	printf (no_msg);
	exit (1);
    }
}

struct nlist avenrun[] = {
    {"_avenrun"},
    0
};

loadav(avg)
#ifdef sun
    register long *avg;
#else
    register double *avg;
#endif
{
    register int kmem;
    int nread;

    if ((kmem = open("/dev/kmem", 0)) < 0) {
	perror("Can't open /dev/kmem");
	exit(EX_OSFILE);
    }
    nlist("/vmunix", avenrun);
    if (avenrun[0].n_type == 0) {
	fprintf(stderr, "load: can't find _avenrun\n");
#ifdef sun
	avg[0] = avg[1] = avg[2] = 0;
#else
	avg[0] = avg[1] = avg[2] = 0.0;
#endif
	return;
    }

    (void) lseek(kmem, (long) avenrun[0].n_value, 0);
#ifdef sun
    nread = read(kmem, (char *)avg, 3 * sizeof (long));
#else
    nread = read(kmem, (char *)avg, 3 * sizeof (double));
#endif
    if (nread < 0) {
	perror("load: read from kmem");
	exit(EX_IOERR);
    }
}
//go.sysin dd *
if [ `wc -c < load.c` != 2285 ]; then
	made=FALSE
	/bin/echo 'error transmitting "load.c" --'
	/bin/echo 'length should be 2285, not' `wc -c < load.c`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	/bin/chmod 644 load.c
	/bin/echo -n '	'; /bin/ls -ld load.c
fi
/bin/echo 'Extracting load.1l'
sed 's/^X//' <<'//go.sysin dd *' >load.1l
X.TH LOAD 1L 4-Oct-1984
X.SH NAME
load \- determines if system load is greater than input value,
or prints current system load
X.SH SYNOPSIS
X.B load 
[\-sv]
X.B [ maxload ]
X.SH DESCRIPTION
X.I Load
returns true if the system load is greater
than or equal to the value given by 
X.I
maxload.
By default, the program returns yes if the system
load is too high and no if it is not.
If the -v option is specified, a more detailed message 
is displayed.
The -s option will display no message.  
X.sp
If
X.I load
is invoked with no argument, it prints the current 1-minute load average.
X.SH FILES
X.SH "SEE ALSO"
uptime(1), w(1), vmpic(1l)
X.SH DIAGNOSTICS
X.SH AUTHOR 
Fred Blonder
X.SH BUGS
//go.sysin dd *
if [ `wc -c < load.1l` != 675 ]; then
	made=FALSE
	/bin/echo 'error transmitting "load.1l" --'
	/bin/echo 'length should be 675, not' `wc -c < load.1l`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	/bin/chmod 644 load.1l
	/bin/echo -n '	'; /bin/ls -ld load.1l
fi
/bin/echo 'Extracting loadd.conf'
sed 's/^X//' <<'//go.sysin dd *' >loadd.conf
#  This file contains the relative cpu power of this machine, as used
# by loadd(8l).  It is suggested that a 785 or a Pyramid be rated at
# about 2, a 750 at about 1, and a Sun II at about 0.5.
localpower	1.0
//go.sysin dd *
if [ `wc -c < loadd.conf` != 210 ]; then
	made=FALSE
	/bin/echo 'error transmitting "loadd.conf" --'
	/bin/echo 'length should be 210, not' `wc -c < loadd.conf`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	/bin/chmod 644 loadd.conf
	/bin/echo -n '	'; /bin/ls -ld loadd.conf
fi