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