[net.sources] load control

muller@sdcc3.UUCP (Keith Muller) (02/11/85)

This is a shar file of the second part of eight shar files for the load control
system as described in net.unix-wizards.
NOTE: Part 1 must be unpacked BEFORE any other part.
	Keith Muller
	ucbvax!sdcsvax!muller

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by sdcc3!muller on Sat Feb  9 13:44:50 PST 1985
# Contents:  client/Makefile client/main.c scripts/addldd scripts/makedirs
#	scripts/qaddldd scripts/rmldd scripts/saddldd
 
echo x - client/Makefile
sed 's/^@//' > "client/Makefile" <<'@//E*O*F client/Makefile//'
#
# Makefile for batch client
#

CFLAGS= -O

HDR=	../h/common.h ../h/client.h

SRC=	main.c

DEST1=	/bin
TARG1=	binclient
TARG1Q=	qbinclient
DEST2=	/usr/bin
TARG2=	usrbinclient
TARG2Q=	qusrbinclient
DEST3=	/usr/local
TARG3=	usrlocclient
TARG3Q=	qusrlocclient
DEST4=	/usr/ucb
TARG4=	usrucbclient
TARG4Q=	qusrucbclient
DEST5=	/usr/games
TARG5=	gamesclient
TARG5Q=	qgamesclient
DEST6=	/usr/new
TARG6=	usrnewclient
TARG6Q=	qusrnewclient
#
#the spec macros have the name of the program
#SPEC1=	test
#DESTSPEC1= /tmp

all:	$(TARG1) $(TARG1Q) $(TARG2) $(TARG2Q) $(TARG3) $(TARG3Q) $(TARG4) \
	$(TARG4Q) $(TARG5) $(TARG5Q) $(TARG6) $(TARG6Q)

clean:
	rm -f core *client 

lint:
	lint -abchx  main.c

install: $(DEST1)/.client $(DEST1)/.qclient \
	 $(DEST2)/.client $(DEST2)/.qclient \
	 $(DEST3)/.client $(DEST3)/.qclient \
	 $(DEST4)/.client $(DEST4)/.qclient \
	 $(DEST5)/.client $(DEST5)/.qclient
#	 $(DEST5)/.client $(DEST5)/.qclient \
#	 $(DEST6)/.client $(DEST6)/.qclient 
####################################################################
#	 Have the two commented lines replace the last line of the
#	 dependency list if your machine has /usr/new.
#
#        The following line is a sample for $(SPEC1) which would
#	 have to be added into the install dependency for each
#	 SPEC defined.
#        $(DESTSPEC1)/.$(SPEC1)client
####################################################################

####################################################################
# $(SPEC1) is a sample of a special client for controlling
# a single binary (like pi)
# NOARGV should be the full path name of where the real binary is
# is store (usually a .code directory)
####################################################################

$(SPEC1)client: main.c $(HDR)
	cc $(CFLAGS) -DNOARGV=\"$(DESTSPEC1)/.code/$(SPEC1)\" main.c -o $(SPEC1)client

$(DESTSPEC1)/.$(SPEC1)client: $(SPEC1)client
	install -c -m 4711 -o root $(SPEC1)client $(DESTSPEC1)/.$(SPEC1)client

####################################################################

$(TARG1): main.c $(HDR)
	cc $(CFLAGS) -DCODEPATH=\"$(DEST1)/.code/\" main.c -o $(TARG1)

$(TARG1Q): main.c $(HDR)
	cc $(CFLAGS) -DCODEPATH=\"$(DEST1)/.code/\" -DQUIET main.c -o $(TARG1Q)

$(TARG2): main.c $(HDR)
	cc $(CFLAGS) -DCODEPATH=\"$(DEST2)/.code/\" main.c -o $(TARG2)

$(TARG2Q): main.c $(HDR)
	cc $(CFLAGS) -DCODEPATH=\"$(DEST2)/.code/\" -DQUIET main.c -o $(TARG2Q)

$(TARG3): main.c $(HDR)
	cc $(CFLAGS) -DCODEPATH=\"$(DEST3)/.code/\" main.c -o $(TARG3)

$(TARG3Q): main.c $(HDR)
	cc $(CFLAGS) -DCODEPATH=\"$(DEST3)/.code/\" -DQUIET main.c -o $(TARG3Q)

$(TARG4): main.c $(HDR)
	cc $(CFLAGS) -DCODEPATH=\"$(DEST4)/.code/\" main.c -o $(TARG4)

$(TARG4Q): main.c $(HDR)
	cc $(CFLAGS) -DCODEPATH=\"$(DEST4)/.code/\" -DQUIET main.c -o $(TARG4Q)

$(TARG5): main.c $(HDR)
	cc $(CFLAGS) -DCODEPATH=\"$(DEST5)/.code/\" main.c -o $(TARG5)

$(TARG5Q): main.c $(HDR)
	cc $(CFLAGS) -DCODEPATH=\"$(DEST5)/.code/\" -DQUIET main.c -o $(TARG5Q)

$(TARG6): main.c $(HDR)
	cc $(CFLAGS) -DCODEPATH=\"$(DEST6)/.code/\" main.c -o $(TARG6)

$(TARG6Q): main.c $(HDR)
	cc $(CFLAGS) -DCODEPATH=\"$(DEST6)/.code/\" -DQUIET main.c -o $(TARG6Q)

$(DEST1)/.client: $(TARG1)
	install -c -m 4711 -o root $(TARG1) $(DEST1)/.client

$(DEST1)/.qclient: $(TARG1Q)
	install -c -m 4711 -o root $(TARG1Q) $(DEST1)/.qclient

$(DEST2)/.client: $(TARG2)
	install -c -m 4711 -o root $(TARG2) $(DEST2)/.client

$(DEST2)/.qclient: $(TARG2Q)
	install -c -m 4711 -o root $(TARG2Q) $(DEST2)/.qclient

$(DEST3)/.client: $(TARG3)
	install -c -m 4711 -o root $(TARG3) $(DEST3)/.client

$(DEST3)/.qclient: $(TARG3Q)
	install -c -m 4711 -o root $(TARG3Q) $(DEST3)/.qclient

$(DEST4)/.client: $(TARG4)
	install -c -m 4711 -o root $(TARG4) $(DEST4)/.client

$(DEST4)/.qclient: $(TARG4Q)
	install -c -m 4711 -o root $(TARG4Q) $(DEST4)/.qclient

$(DEST5)/.client: $(TARG5)
	install -c -m 4711 -o root $(TARG5) $(DEST5)/.client

$(DEST5)/.qclient: $(TARG5Q)
	install -c -m 4711 -o root $(TARG5Q) $(DEST5)/.qclient

$(DEST6)/.client: $(TARG6)
	install -c -m 4711 -o root $(TARG6) $(DEST6)/.client

$(DEST6)/.qclient: $(TARG6Q)
	install -c -m 4711 -o root $(TARG6Q) $(DEST6)/.qclient

####################################################################
# tmpclient is used for testing the ldc system with a /tmp/.code
# directory. This is not normally used.
####################################################################

tmpclient: main.c $(HDR)
	cc $(CFLAGS) -DCODEPATH=\"/tmp/.code/\" main.c -o tmpclient
	install -c -m 4711 -o root tmpclient /tmp/.client
@//E*O*F client/Makefile//
chmod u=r,g=r,o=r client/Makefile
 
echo x - client/main.c
sed 's/^@//' > "client/main.c" <<'@//E*O*F client/main.c//'

/*--------------------------------------------------------------------------
 * main.c - client
 *
 * Front end program that communicates with the ldd server. This front
 * end replaces the program to be controlled. The controlled binary is
 * hidden in a directory that is only accessable through group privledges.
 * Only one client executable is needed for each protected binary directory.
 * The real name of the program to be executed is extracted from argv[0]
 * unless NOARGV is defined. When defined, NOARGV has the name of the program
 * to be exec'ed wired in. The NOARGV option is necessary for programs like
 * pi and px which use argv[0] to pass data to them (YUCK!!!) when they are
 * called from pix. Usually all the front ends can just be links (hard or soft)
 * to the same code file.
 * The QUIET option allows suppression of the client status messages (this
 * is good for nroff). The ONETIME option exempts all child processes from
 * being queued once the parent process has passed through load control
 * once. (Good for queueing individual passes of a compiler, make, etc).
 * If for any reason the server is dead or not responding, this program will
 * simply exec the proper code file. This allows the load control system to
 * be quickly disabled by killing off the ldd server program. 
 * The front end checks every QUEUETIME seconds to see if the server is
 * still running and has this process queued up. If this poll fails the
 * control program is exec'ed. This protects against the system locking up due
 * to server death. The system WILL NOT be overloaded from a rash of executing
 * jobs as each job will expire relative to the time it was queued (which will
 * be spread out over time).
 *
 * Author : Keith Muller
 *          University of California, San Diego
 *	    Academic Computer Center C - 010
 *	    La Jolla Ca 92093
 *	    ucbvax!sdcsvax!sdcc3!muller
 *	    (619) 452-6090
 *---------------------------------------------------------------------------
 */

/* $Log$ */

#include "../h/common.h"
#include "../h/client.h"
#include <sys/uio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/time.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>

int queued = 0;				 /* 1 if queued */
int msgsock = -1;			 /* desciptor of socket */
int len = 0;				 /* used to store address len */
char *ptr;				 /* ptr for pulling apart argv[0] */
char clientpath[255];			 /* buffer for socket name */
char binary[255];			 /* buffer for real binary's path */
struct request job;			 /* datagram for the server */
struct sockaddr_un name;		 /* socket address of server */
struct timeval polltime = {WAITTIME, 0}; /* waitime to check server */
extern int onint();			 /* interrupt handler */

/*-----------------------------------------------------------------------
 * main
 *
 *-----------------------------------------------------------------------
 */
main(argc, argv, envp)
int argc;
char **argv, **envp;
{
	register int i;			/* general counter */
	int msgmask;			/* mask for select */
	int readfds;			/* mask for desc to select on */
	int numfds;			/* number of desc select ret */
	int egid;			/* effective group id */
	int rgid;			/* real group id */
	int uid;			/* real user id */
	int pollcount;			/* number of polls to server */
	int descsize;			/* size of desc table */
	int sigmask;			/* signal mask before block */
	char msg;			/* answer from server */
	struct timeval now;		/* time (secs) value */
	struct timezone zone;		/* timezone value (unused) */
	int fromlen = 0;
#ifndef QUIET
	int announce;			/* limits "queued" messages */
	char *eptr;
	extern char *getenv();
	extern int strcmp();
#endif QUIET
	extern char *strcpy();
	extern char *strncpy();
	extern char *strcat();
	extern int getpid();
	extern int getegid();
	extern int getgid();
	extern int getuid();
	extern int sigblock();
	extern int sigsetmask();
	extern int errno;
	extern char *sprintf();
	extern char *rindex();

	/*
	 * the client front end runs ONLY setuid to root. so get real user
	 * and both effective and real gids.
	 */
	egid = getegid();
	rgid = getgid();
	uid = getuid();

	/*
	 * set the users real and effective uid (no limits on root). also set
	 * the group id to LDDGID so a socket can be bound in the spool
	 * directory and a datagram can be sent to the server. (the spool
	 * directory MUST BE in group LDDGID and mode 0730 only!
	 * NO OTHER PRIVLEDGES AT ALL!!!!!)
	 */
	(void)setregid(rgid, LDDGID);
	(void)setreuid(uid, uid);

	/*
	 * If NOARGV is defined, then this is a special client which
	 * will only exec a SINGLE program. This is to get around things
	 * like pi which can use argv[0] to pass data. Otherwise we must
	 * find the base name of the requested program. Since argv[0]
	 * can be a long ugly path name, ugly looking code is needed
	 * to strip off the path.
	 */
#ifdef NOARGV
	/*
	 * NOARGV is set in the makefile to have the FULL path of where the
	 * real binary lives: for example /usr/bin/.code/yuck
	 */
	(void)strcpy(binary, NOARGV);
	if ((ptr = rindex(binary, '/')) == (char *)0)
		ptr = binary;
	else
		ptr++;
#else
	/*
	 * must pull the path out of the argv[0]
	 */
	if ((ptr = rindex(argv[0], '/')) == (char *)0)
		ptr = argv[0];
	else
		ptr++;
	(void)sprintf(binary, "%s%s", CODEPATH, ptr);
#endif
	/*
	 * If ONETIME is defined, then all child processes of this job are
	 * EXEMPT from being queued. This is useful for things like pi which
	 * can be called both by a user and from pix.
	 * This works because if the effective gid of the process
	 * is the group LDDGID this process must be a decendent of a process
	 * that has already passed through the load control system. This
	 * mechanism will work only if this program is setuid and IS NOT
	 * setgid.
	 *
	 * root is always exempt!
	 *
	 * NOTE: ptr will be used later to build up the command line buffer
	 * in the datagram request packet sent to the server.
	 */

#ifdef ONETIME
	if ((egid == LDDGID) || (uid == 0))
		run(argv, envp);
#else
	if (uid == 0)
		run(argv, envp);
#endif ONETIME

	/*
	 * create the socket and the datagram. if anything fails
	 * just run. cannot afford to have this process HANG!
	 */
	msgsock = socket(AF_UNIX, SOCK_DGRAM, 0);
	if (msgsock < 0)
		run(argv, envp);

	/*
	 * bind the handler to clean up
	 */
	(void)signal(SIGINT, onint);
	(void)signal(SIGHUP, onint);
	(void)signal(SIGQUIT, onint);
	(void)signal(SIGTERM, onint);

	/*
	 * make the datagram up
	 */
	job.pid = (u_long)getpid();
	(void)sprintf(clientpath,"%s/%s%u",SPOOLDIR,CLIENTPRE,job.pid);
	(void)strcpy(name.sun_path, clientpath);
	name.sun_family = AF_UNIX;
	len = strlen(name.sun_path) + 1 + sizeof(name.sun_family);

	/*
	 * block off interrupt and control z until we get datagram
	 * sent
	 */
	sigmask = sigblock((1<<(SIGINT-1)) | (1<<(SIGTSTP-1)) | (1<<(SIGHUP-1)) |
			   (1<<(SIGQUIT-1)) | (1<<(SIGTERM-1)));

	/*
	 * bind the socket, if it fails just run
	 */
	(void)unlink(name.sun_path);

	if (bind(msgsock, &name, len) < 0){
		(void)sigsetmask(sigmask);
		run(argv, envp);
	}

	/*
	 * build up the command line that will be displayed in the
	 * when the user interrogates the queue. This helps in identifying
	 * which job is which.
	 */
	(void)strncpy(job.com, ptr, COMLEN - 1);
	i = 1;
	len = strlen(job.com) + 1;
	while((i < argc) && ((len = len + strlen(argv[i]) + 1) <= COMLEN)){
		(void)strcat(job.com, " ");
		(void)strcat(job.com, argv[i]);
		i++;
	}

	/*
	 * put the path name of the servers datagram in the sockaddr struct
	 */
	(void)strcpy(name.sun_path, MSGPATH);
	len = strlen(name.sun_path) + 1 + sizeof(name.sun_family);

	/*
	 * time stamp the datagram, and place my pid in it (identfies the name
	 * of this clients bound socket
	 */
	if (gettimeofday(&now, &zone) < 0)
		run(argv, envp);
	job.time = now.tv_sec;
	job.type = QCMD;
	job.uid = (u_long)uid;

	/*
	 * send the request to the server
	 */
	if (sendto(msgsock, &job, sizeof(struct request), 0, &name, len) < 0){
		(void)sigsetmask(sigmask);
		run(argv, envp);
	}

	descsize = getdtablesize();
	msgmask = 1 << msgsock;
	pollcount = 0;

	/*
	 * unblock the signals
	 */
	 (void)sigsetmask(sigmask);

#ifndef QUIET
	/*
	 * set announce to 0 to get at least one status message
	 * if user has ENVNAME = quiet in his environment, no messages!
	 */
	if (((eptr=getenv(ENVNAME))==(char *)0)||(strcmp(eptr,"quiet")!=0))
		announce = 0;
	else
		announce = -1;
#endif QUIET

	/*
	 * wait for the word from the server!
	 */
	for(;;){
		readfds = msgmask;
		numfds = select(descsize,&readfds,(int *)0,(int *)0,&polltime);

		/*
		 * if there is a screwup here just run
		 */
		if (((numfds<0)&&(errno!=EINTR))||((numfds<=0)&&(pollcount>MAXPOLLS))){
#ifndef QUIET
			if (announce == 1)
				fprintf(stderr,"running\n");
#endif QUIET
			run(argv, envp);
		}
		
		/*
		 * we waitied polltime seconds and no word from the server
		 * so send the datagram again in case the system lost it
		 * OR else we got a garbage answer!
		 */
		if ((numfds<=0)||(recvfrom(msgsock,&msg,sizeof(msg),0,(struct sockaddr *)0,&fromlen)<=0)){
			pollcount++;
			/*
			 * oh server where are you?
			 */
			if (sendto(msgsock,&job,sizeof(struct request),0,&name,len)<0){
#ifndef QUIET
				if (announce == 1)
					fprintf(stderr,"running\n");
#endif QUIET
				run(argv, envp);
			}else{
				/*
				 * the datagram was sent so switch to WAITTIME
				 * for an answer. WAITTIME is much shorter
				 * than QUEUETIME as we want to be queued.
				 */
				polltime.tv_sec = WAITTIME;
				continue;
			}
		}

		/*
		 * we got the word see what to do
		 */
		switch(msg){
			case RUNCMD:
				/*
				 * we can run
				 */
#ifndef QUIET
			 	if (announce == 1)
					fprintf(stderr,"running\n");
#endif QUIET
				run(argv, envp);
				break;
			case STOPCMD:
				/*
				 * bye bye
				 */
#ifndef QUIET
			 	if (announce == 1)
					fprintf(stderr,"stopped\n");
#endif QUIET
				(void)close(msgsock);
				(void)unlink(clientpath);
				exit(1);
			case QCMD:
				/*
				 * we have been queued 
				 * switch to QUEUETIME so to check later
				 * that the server is still around (so we
				 * don't wait forever!
				 * Switch to POLLCMD so that the server knows
				 * we have been in the queue at least once.
				 */
				polltime.tv_sec = QUEUETIME;
				queued = 1;
				pollcount = 0;
				job.type = POLLCMD;
#ifndef QUIET
				/*
				 * tell user he is being queued
				 */
				if (announce == 0){
					fprintf(stderr,"Queued, waiting to run....");
					announce = 1;
				}
#endif QUIET
				break;
			case FULLQUEUE:
				/*
				 * The queue is full, this job cannot be
				 * accepted. This prevents the system
				 * from running out of slots in the
				 * process table.
				 */
			 	fprintf(stderr,"Cannot run, the system is overloaded. Try again later.\n");
				(void)close(msgsock);
				(void)unlink(clientpath);
				exit(1);
			case POLLCMD:
				/*
				 * server wants the data again
				 * The only way we can get this is from
				 * a new server during startup.
				 * So reset the datagram to a QCMD.
				 * (fall through to default below),
				 */
				job.type = QCMD;
			default:
				/*
				 * or got garbage
				 */
				if (sendto(msgsock,&job,sizeof(struct request),0,&name,len)<0){
#ifndef QUIET
			 		if (announce == 1)
						fprintf(stderr,"running\n");
#endif QUIET
					run(argv, envp);
				}

				/*
				 * switch back to WAITTIME to be a pest until
				 * we get queued
				 */
				polltime.tv_sec = WAITTIME;
				queued = 0;
				pollcount = 0;
				break;
		}
	}
}

/*-----------------------------------------------------------------------------
 * onint
 *
 * what to do when the user wants out
 *-----------------------------------------------------------------------------
 */
onint()
{

	/*
	 * if we are already queued say goodbye to the server
	 */
	if (queued == 1){
		/*
		 * Send a message to the server we are quitting so the server
		 * can remove us from the queue.
		 */
		job.type = PJOBCMD;
		job.time = job.pid;
		(void)sendto(msgsock,&job,sizeof(struct request),0,&name,len);
	}
	(void)close(msgsock);
	(void)unlink(clientpath);
	exit(0);
}

/*-----------------------------------------------------------------------------
 * run
 *
 * routine that execs the real program after getting the ok 
 *-----------------------------------------------------------------------------
 */

run(argv, envp)
char **argv, **envp;
{
	extern int msgsock;
	extern char binary[];
	extern char clientpath[];

	/*
	 * shut down the socket and remove it from the spool
	 */
	if (msgsock != -1)
		(void)close(msgsock);
	(void)unlink(clientpath);

	/*
	 * all is set try to run!
	 * this works because the directory where the REAL code file is
	 * is mode 0730 and the group MUST BE LDDGID!
	 * and we are now running with an effective gid of LDDGID
	 */
	(void)execve(binary, argv, envp);

	/*
	 * from now on something screwed up! print the error and
	 * hope the user reports it!
	 */
	perror(binary);
	exit(0);
}
@//E*O*F client/main.c//
chmod u=r,g=r,o=r client/main.c
 
echo x - scripts/addldd
sed 's/^@//' > "scripts/addldd" <<'@//E*O*F scripts/addldd//'
#! /bin/csh -f
#
# script to add file1 through filen located in directory to the processes
# controlled by the ldd system.
#
# THIS IS FOR COMMANDS THAT SHOULD HAVE STATUS ANNOUNCEMENTS

umask 022
if ($#argv < 2) then
	echo "usage: addldd directory file1 file2 .. filen"
else
	echo "cd $1"
	cd $1
	shift
	while($#argv)
		if (-e .client == 0) then
			echo "there is no .client front end. Do a make install."
			break
		else if (-e .code/$1) then
			echo "$1 is already load controlled"
		else if (-e $1) then
			echo "putting $1 under load control"
			echo "mv $1 .code/$1"
			/bin/mv $1 .code/$1
			echo "ln -s .client $1"
			/bin/ln -s .client $1
		else
			echo "$1 does not exsist"
		endif
		shift
	end
endif
@//E*O*F scripts/addldd//
chmod u=rx,g=rx,o=rx scripts/addldd
 
echo x - scripts/makedirs
sed 's/^@//' > "scripts/makedirs" <<'@//E*O*F scripts/makedirs//'
#! /bin/csh -f
# make all the directories needed by the load control system
# NOTE: lddgrp must be defined as the same group id as in h/client.h
#
echo "making the directories for load control"

foreach i (/bin /usr/bin /usr/local /usr/ucb /usr/new /usr/games)
	if (-e $i && -e $i/.code == 0) then
		echo "mkdir $i/.code"
		/bin/mkdir $i/.code
	endif
	if (-e $i/.code) then
		echo "chown root $i/.code"
		/etc/chown root $i/.code
		echo "chgrp lddgrp $i/.code"
		/bin/chgrp lddgrp $i/.code
		echo "chmod 0710 $i/.code"
		/bin/chmod 0710 $i/.code
	endif
end
#
if (-e /usr/spool/ldd == 0) then
	echo "mkdir /usr/spool/ldd"
	/bin/mkdir /usr/spool/ldd
endif
if (-e /usr/spool/ldd/sr == 0) then
	echo "mkdir /usr/spool/ldd/sr"
	/bin/mkdir /usr/spool/ldd/sr
endif
if (-e /usr/spool/ldd/cl == 0) then
	echo "mkdir /usr/spool/ldd/cl"
	/bin/mkdir /usr/spool/ldd/cl
endif
#
# spool/ldd/cl MUST BE GROUP writeable
# all others should NOT be GROUP writeable
#
echo "chown root /usr/spool/ldd"
/etc/chown root /usr/spool/ldd

echo "chgrp lddgrp /usr/spool/ldd"
/bin/chgrp lddgrp /usr/spool/ldd

echo "chmod 0710 /usr/spool/ldd"
/bin/chmod 0710 /usr/spool/ldd

echo "chown root /usr/spool/ldd/cl"
/etc/chown root /usr/spool/ldd/cl

echo "chgrp lddgrp /usr/spool/ldd/cl"
/bin/chgrp lddgrp /usr/spool/ldd/cl

echo "chmod 0730 /usr/spool/ldd/cl"
/bin/chmod 0730 /usr/spool/ldd/cl

echo "chown root /usr/spool/ldd/sr"
/etc/chown root /usr/spool/ldd/sr

echo "chgrp lddgrp /usr/spool/ldd/sr"
/bin/chgrp lddgrp /usr/spool/ldd/sr

echo "chmod 0710 /usr/spool/ldd/sr"
/bin/chmod 0710 /usr/spool/ldd/sr
@//E*O*F scripts/makedirs//
chmod u=rx,g=rx,o=rx scripts/makedirs
 
echo x - scripts/qaddldd
sed 's/^@//' > "scripts/qaddldd" <<'@//E*O*F scripts/qaddldd//'
#! /bin/csh -f
#
# script to add file1 through filen located in directory to the processes
# controlled by the ldd system.
#
# THIS IS FOR COMMANDS THAT *DO* *NOT* HAVE STATUS ANNOUNCEMENTS

umask 022
if ($#argv < 2) then
	echo "usage: qaddldd directory file1 file2 .. filen"
else
	echo "cd $1"
	cd $1
	shift
	while($#argv)
		if (-e .qclient == 0) then
			echo "there is no .qclient front end. Do a make install."
			break
		else if (-e .code/$1) then
			echo "$1 is already load controlled"
		else if (-e $1) then
			echo "putting $1 under load control (quiet mode)"
			echo "mv $1 .code/$1"
			/bin/mv $1 .code/$1
			echo "ln -s .qclient $1"
			/bin/ln -s .qclient $1
		else
			echo "$1 does not exsist"
		endif
		shift
	end
endif
@//E*O*F scripts/qaddldd//
chmod u=rx,g=rx,o=rx scripts/qaddldd
 
echo x - scripts/rmldd
sed 's/^@//' > "scripts/rmldd" <<'@//E*O*F scripts/rmldd//'
#! /bin/csh -f
#
# script to remove process file1 through filen in directory from the
# load control system. 

umask 022
if ($#argv < 2) then
	echo "usage: rmldd directory file1 file2 .. filen"
else
	echo "cd $1"
	cd $1
	shift
	while($#argv)
		if (-e .code/$1) then
			echo "removing $1 from load control"
			echo "rm $1"
			/bin/rm $1
			echo "mv .code/$1 $1"
			/bin/mv .code/$1 $1
		else
			echo "$1 is not load controlled"
		endif
		shift
	end
endif
@//E*O*F scripts/rmldd//
chmod u=rx,g=rx,o=rx scripts/rmldd
 
echo x - scripts/saddldd
sed 's/^@//' > "scripts/saddldd" <<'@//E*O*F scripts/saddldd//'
#! /bin/csh -f
#
# script to add file file1 as a SPECIAL client in directory directory.
#
# THIS IS FOR COMMANDS THAT REQUIRE SPECIAL PRIVATE CLIENTS

umask 022
if ($#argv != 2) then
	echo "usage: saddldd directory file"
else
	echo "cd $1"
	cd $1
	shift
	if (-e .$1client == 0) then
		echo "there is no .$1client front end. Do a make install."
		break
	else if (-e .code/$1) then
		echo "$1 is already load controlled"
	else if (-e $1) then
		echo "putting $1 under load control (special client)"
		echo "mv $1 .code/$1"
		/bin/mv $1 .code/$1
		echo "ln -s .$1client $1"
		/bin/ln -s .$1client $1
	else
		echo "$1 does not exsist"
	endif
endif
@//E*O*F scripts/saddldd//
chmod u=rx,g=rx,o=rx scripts/saddldd
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
     154     491    4545 Makefile
     471    1929   12916 main.c
      32     123     711 addldd
      63     202    1580 makedirs
      32     126     733 qaddldd
      25      76     454 rmldd
      28     113     644 saddldd
     805    3060   21583 total
!!!
wc  client/Makefile client/main.c scripts/addldd scripts/makedirs scripts/qaddldd scripts/rmldd scripts/saddldd | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0

muller@sdcc3.UUCP (Keith Muller) (02/12/85)

This is part 3 of the load control system. Part 1 must be unpacked before
any other part.
	Keith Muller
	ucbvax!sdcsvax!muller


# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by sdcc3!muller on Sat Feb  9 13:48:06 PST 1985
# Contents:  h/client.h h/common.h h/control.h h/server.h
 
echo x - h/client.h
sed 's/^@//' > "h/client.h" <<'@//E*O*F h/client.h//'

/*-----------------------------------------------------------------------
 * client.h
 *
 * definitions used by the client process.
 *-----------------------------------------------------------------------
 */

/* $Log$ */

#define	MAXPOLLS	5	/* max polls to server from a client if the */
				/* server doesnt respond after, just run */
#define	WAITTIME	60	/* time (secs) wait for response between each */
				/* poll of the server */
#define	QUEUETIME	600	/* time (secs) wait before waking up to see if*/
				/* if the server is still running */

#define ENVNAME	"LDDOPTS"	/* name to set in environment, read by client */

#define ONETIME			/* When defined once a program passed through */
				/* the ldd system and inherits the effective */
				/* group id of ldd, all child processes will */
				/* be exempt from load control. Can be used */
				/* to encourage the use of make. Note that if */
				/* make is load controlled a user can bypass */
				/* load control by having make create a sh. */
				/* A more uesful way to use this is to load */
				/* control all passes of a compiler and only */
				/* first passes will be queued, all other */
				/* passes will be exempt. See client/main.c */
@//E*O*F h/client.h//
chmod u=r,g=r,o=r h/client.h
 
echo x - h/common.h
sed 's/^@//' > "h/common.h" <<'@//E*O*F h/common.h//'
/*------------------------------------------------------------------------
 * common.h
 *
 * Common definations for all the programs releted to the load control
 * system that MUST be EXACTLY the same.
 *------------------------------------------------------------------------
 */

/* $Log$ */

#include <sys/types.h>

/*
 * The basic datagram package sent TO the server.
 */

#define	COMLEN	15	/* buffer for user command */

struct request {
	u_long	pid;		/* pid of client process (socket name) */
	u_long	uid;		/* Usually the uid of the client, not always */
	u_long	time;		/* Usually the submit time of the client */
	char	com[COMLEN];	/* Part of the command line the user typed */
	char	type;		/* The command identifier (a single char) */
};

/*
 * The commands that can be sent to/from the server.
 */

#define	CHTIMER		'N'	/* change the server interval timer */
#define	LOADLIMCMD	'L'	/* change the server load average threshold */
#define	LISTCMD		'I'	/* ask server for a list of queued jobs */
#define	MOVECMD		'M'	/* ask server to move a job in the queue */
#define	MQTIMECMD	'T'	/* change the server limit on max queue time */
#define	POLLCMD		'P'	/* to/from server that request be resent */
#define	QCMD		'Q'	/* to/from server stating job is queued */
#define	PALLCMD		'F'	/* ask server to remove ALL queued jobs */
#define	PUSRCMD		'U'	/* ask server to remove all a users jobs */
#define	PJOBCMD		'R'	/* ask server to remove a specific job */
#define	RUSRCMD		'W'	/* ask server to run all a users jobs */
#define	RJOBCMD		'X'	/* ask server to run a specific job */
#define	RUNCMD		'G'	/* message to continue execution */
#define	STOPCMD		'H'	/* message to stop or command invalid */
#define	STATUSCMD	'S'	/* ask server to list parameters */
#define	ABORTCMD	'A'	/* ask server to terminate */
#define	FULLQUEUE	'D'	/* from server that queue is full */
#define	QUEUESIZE	'O'	/* ask server to change queue full point */

/*
 * path names of files used in the system
 */

#define	CLIENTPRE	"cl"                        /* client socket prefix */
#define	CNTRLPRE	"cn"                        /* control socket prefix */
#define	LDDGID		25		     	    /* gid used to protect */
						    /* REAL binaries this must*/
						    /* be the gid of the */
						    /* protected directories */
/*
 * Defined paths. If these are altered the onetime script makedirs must
 * also be altered.
 *
 * The load control system uses two subdirectories to store
 * sockets and status files. server directories should be owner (root)
 * write only and execute all others only (mode 710). The client directory
 * must be group writeable but not readable (mode 730). This is done for
 * security reasons and should not be altered.
 */
#define	SPOOLDIR	"/usr/spool/ldd/cl"         /* client sockets dir */
#define SERVERDIR	"/usr/spool/ldd/sr"	    /* servers directory  */

/*
 * The following MUST be in the SERVERDIR directory!!!!
 */
#define	CNTRLPATH	"/usr/spool/ldd/sr/cnsock"  /* serv cntrl socket */
#define	MSGPATH		"/usr/spool/ldd/sr/msgsock" /* serv msg socket */
#define	LISTFILE	"/usr/spool/ldd/sr/list"    /* jobs in queue file */
#define	STATUSFILE	"/usr/spool/ldd/sr/status"  /* status of server */
#define	ERRORPATH	"/usr/spool/ldd/sr/errors"  /* saved errors */
#define	LOCK		"/usr/spool/ldd/sr/lock"    /* lockfile */
@//E*O*F h/common.h//
chmod u=r,g=r,o=r h/common.h
 
echo x - h/control.h
sed 's/^@//' > "h/control.h" <<'@//E*O*F h/control.h//'

/*------------------------------------------------------------------------
 * control.h
 *
 * definations used by the control program
 *------------------------------------------------------------------------
 */

/* $Log$ */

/*
 * structure of control program command table
 */

struct	cmd {
	char	*c_name;	/* command name */
	char	*c_help;	/* help message	*/
	int	(*c_handler)();	/* routine to do the work */
	int	c_priv;		/* command privledge */
};

#define	RESTRICT	1	/* a 1 means restricted command */
#define	NORESTRICT	0	/* a 0 mean no restrictions */

/*
 * structure of the hash table used by the list commands to keep a
 * local listing of recently referenced users in the passwd file.
 * (used for speedup).
 */
#define PNAMSIZ		12	/* size of list name bucket to store name */
#define HASHMOD		151	/* size of list hash table, MUST BE prime */

struct hashbuck{
	int buckuid;
	char buckname[PNAMSIZ+1];
};

/*
 * warning message limits for ldc commands to help prevent the admin
 * from setting server settings that might cause problems.
 */
#define LOWCYCLE	5	/* If cycle time is less complain */
#define HIGHCYCLE	600	/* If cycle time is greater complain */
#define LOWMAX		90	/* If max queue time is less complain */
#define HIGHMAX		43200	/* If max queue time is greater complain */
#define LOWSIZE		10	/* If max queue size is less complain */
#define HIGHSIZE	200	/* If max queue size is greater complain */
#define LOWLOAD		1.0	/* If load limit is less complain */
#define HIGHLOAD	35.0	/* If load limit is greater complain */

/*
 * other defines
 */
#define	WAITTIME	120	/* time (secs) before packet resend */
@//E*O*F h/control.h//
chmod u=r,g=r,o=r h/control.h
 
echo x - h/server.h
sed 's/^@//' > "h/server.h" <<'@//E*O*F h/server.h//'

/*------------------------------------------------------------------------
 * server.h
 *
 * definations used by the server process.
 *------------------------------------------------------------------------
 */

/* $Log$ */

/*
 * structure of the double linked queue of waiting processes.
 */

struct qnode {
	u_long	pid;			/* pid of waiting client */
	u_long	uid;			/* uid of waiting client */
	u_long	time;			/* time job submitted in queue */
	struct qnode *fow;		/* foward link to newer job */
	struct qnode *back;		/* back link to older job */
	char	com[COMLEN+1];		/* command line (truncated) */
};
#define QNIL	(struct qnode *) 0	/* use for nil pointer */

/*
 * General definations.
 */

#define	MAXLOAD		10.0	/* default load level to queue at */
				/* 10 for pyramid 90x, 8.5 for 780, 7 for */
				/* 750. note you can override this on
				/* ldd's command line */
#define	ALRMTIME	60	/* default cycle time to check load */
				/* 60 is good for vaxes. pyramids being */
				/* so much faster should be 20. Note you */
				/* can override this default on ldd's */
				/* command line in seconds */
#define	MAXQTIME	14400	/* default max queue time for a process  */
				/* waiting to run in seconds */
#define	PRIO		-4	/* server runs at this nice level this has */
				/* little impact on the system, but makes */
				/* sure packets get handled properly under */
				/* heavy load. This should be -4 or smaller */
				/* (-5, -6, ...) */
#define	WAITTIME	60	/* wait for client during startup scan */
				/* of the spool directory in seconds */
#define	MAXPOLLS	2	/* how many times to send a poll request */
				/* during startup polling */
#define	MAXINQUEUE	150	/* max number of jobs that can be waiting to */
				/* run */
@//E*O*F h/server.h//
chmod u=r,g=r,o=r h/server.h
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
      30     200    1207 client.h
      81     528    3321 common.h
      54     270    1630 control.h
      50     298    1737 server.h
     215    1296    7895 total
!!!
wc  h/client.h h/common.h h/control.h h/server.h | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0

muller@sdcc3.UUCP (Keith Muller) (02/12/85)

This is part 4 of the load control system. Part 1 must be unpacked before
any other part.
	Keith Muller
	ucbvax!sdcsvax!muller


# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by sdcc3!muller on Sat Feb  9 13:51:28 PST 1985
# Contents:  control/Makefile control/delete.c control/globals.c control/ipc.c
#	control/ldccmds.c
 
echo x - control/Makefile
sed 's/^@//' > "control/Makefile" <<'@//E*O*F control/Makefile//'

#
# Makefile for ldc control programs : ldc, ldrm, ldq
#

CFLAGS= -O

BGID=	lddgrp

DEST=	/etc

DEST2=  /usr/local

HDR=	../h/common.h ../h/control.h

LDCSRC=	ldcmain.c ldccmds.c list.c delete.c ldccmdtab.c ipc.c globals.c 

LDRMSRC= ldrmmain.c delete.c ipc.c globals.c

LDQSRC=	ldqmain.c list.c ipc.c globals.c

LDCOBJ=	ldcmain.o ldccmds.o list.o delete.o ldccmdtab.o ipc.o globals.o

LDRMOBJ=ldrmmain.o delete.o ipc.o globals.o

LDQOBJ=	ldqmain.o list.o ipc.o globals.o

all:	ldc ldrm ldq

ldc:  $(LDCOBJ)
	cc -o ldc $(LDCOBJ)

ldrm: $(LDRMOBJ)
	cc -o ldrm $(LDRMOBJ)

ldq:  $(LDQOBJ)
	cc -o ldq $(LDQOBJ)

$(LDCOBJ):  $(HDR)

$(LDRMOBJ): $(HDR)

$(LDQOBJ):  $(HDR)

install: $(DEST)/ldc $(DEST2)/ldq $(DEST2)/ldrm

$(DEST)/ldc: ldc
	install -c -m 4711 -o root -g $(BGID) ldc $(DEST)

$(DEST2)/ldrm: ldrm
	install -c -m 4711 -o root -g $(BGID) ldrm $(DEST2)

$(DEST2)/ldq: ldq
	install -c -m 4711 -o root -g $(BGID) ldq $(DEST2)

clean:
	rm -f *.o core ldc ldrm ldq

lint:
	@echo "*************************lint for ldc*************************"
	lint -abchx $(LDCSRC)
	@echo "*************************lint for ldrm************************"
	lint -abchx $(LDRMSRC)
	@echo "*************************lint for ldq*************************"
	lint -abchx $(LDQSRC)
@//E*O*F control/Makefile//
chmod u=r,g=r,o=r control/Makefile
 
echo x - control/delete.c
sed 's/^@//' > "control/delete.c" <<'@//E*O*F control/delete.c//'

/*-----------------------------------------------------------------------
 * delete.c - control program
 *
 * this file contains the delete command which is common to both ldc
 * and ldrm
 *-----------------------------------------------------------------------
 */

/* $Log$ */

#include "../h/common.h"
#include <sys/time.h>
#include <stdio.h>
#include <pwd.h>

/*-----------------------------------------------------------------------
 * delete
 *
 * tell the server to remove all a users jobs or a specific job from its
 * queue. a user can remove only his jobs, root can remove any job.
 *-----------------------------------------------------------------------
 */
delete(argc, argv)
int argc;
char **argv;
{
	register int i;
	struct passwd *pwd;
	long temp;
	extern int uid;
	extern struct request job;
	extern struct passwd *getpwnam();
	extern long atol();
	extern int endpwent();
	extern int strncmp();

	if (argc < 2){
		printf("Usage: delete [pid(s)] [-u user(s)]\n");
		return;
	}

 	if (strncmp(argv[1], "-u", 2) == 0){
		/*
	 	 * remove all jobs by user (-u option)
	 	 */
		if (argc < 3){
			printf("No user name\n");
			printf("Usage: delete [pid(s)] [-u user(s)]\n");
			return;
		}
		job.type = PUSRCMD;
		for (i = 2; i < argc; i++){
			/*
			 * loop through each arg and remove that users jobs
			 */
			if ((pwd = getpwnam(argv[i])) == (struct passwd *)NULL){
				/*
				 * couldn't find the user, go to next one
				 */
				printf("No such user: %s\n", argv[i]);
				continue;
			}

			/*
			 * the server DOES NOT check to see if this command
			 * can be performed by this user.
			 */
			 if ((uid != 0) && (uid != pwd->pw_uid)){
				printf("You are not: %s\n", argv[i]);
				continue;
			}

			/*
			 * remove the specified users jobs
			 */
			job.uid = (u_long)pwd->pw_uid;
			printf("user %s: ", argv[i]);
			if (sendcntrl() == 0)
				printf("removed from the queue.\n");
		}
		(void)endpwent();
	}else{
		/*
		 * remove a job specified by its pid. Only the server
		 * can determine if the pid is a valid pid for a queued job.
		 * The server will check to see if this user can remove that
		 * pid from the queue (root can remove any job, a user can only
		 * remove his jobs).
		 */
		job.type = PJOBCMD;
		job.uid = uid;

		for (i = 1; i < argc; i++){
			/*
		 	 * loop through each arg and try to remove it (each arg 
		 	 * is a pid).
		 	 */
			if ((temp = atol(argv[i])) <= 0){
				/*
				 * bad pid, try next
				 */
				printf("Bad pid: %ld\n", temp);
				continue;
			}
			job.time = (u_long)temp;
			printf("pid %ld: ", temp);
			if (sendcntrl() == 0)
				printf("removed\n");
		}
	}
}
@//E*O*F control/delete.c//
chmod u=r,g=r,o=r control/delete.c
 
echo x - control/globals.c
sed 's/^@//' > "control/globals.c" <<'@//E*O*F control/globals.c//'

/*-------------------------------------------------------------------------
 * globals.c - control program, used in ldc, ldq, and ldrm
 *
 *-------------------------------------------------------------------------
 */

/* $Log$ */

#include "../h/common.h"
#include "../h/control.h"
#include <sys/uio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/time.h>
#include <stdio.h>

int	uid;					/* uid of the user */
char	path[256];				/* path to bound socket */
int	sock = -1;				/* socket desriptor */
int	descsize;				/* number of desc table */
int 	sockmask;				/* mask to select socket */
int 	stdinmask;				/* mask to select stdin */
int	len;					/* length of address */
FILE	*out = NULL;				/* file desc for status file */
struct sockaddr_un name;			/* socket address */
struct request job;				/* datagram to server */
struct timeval polltime = {WAITTIME, 0};	/* time so select not hangs */
@//E*O*F control/globals.c//
chmod u=r,g=r,o=r control/globals.c
 
echo x - control/ipc.c
sed 's/^@//' > "control/ipc.c" <<'@//E*O*F control/ipc.c//'

/*-------------------------------------------------------------------------
 * ipc.c - control
 *
 * all the routines used to communicate with the server
 *-------------------------------------------------------------------------
 */

/* $Log$ */

#include "../h/common.h"
#include "../h/control.h"
#include <sys/uio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/time.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>

/*-------------------------------------------------------------------------
 * setup
 *
 * create all the goodies needed to run the control programs
 *-------------------------------------------------------------------------
 */
setup()
{

	extern struct sockaddr_un name;
	extern int uid;
	extern int len;
	extern int sock;
	extern int sockmask;
	extern int stdinmask;
	extern int descsize;
	extern char *strcpy();
	extern int onint();
	extern int quit();
	extern struct request job;
	extern char path[];
	extern int getpid();
	extern int getuid();
	extern int getgid();
	extern int errno;
	extern char *sprintf();

	/*
	 * set the global uid as it will be used throughout the control
	 * program
	 */
	uid = getuid();

	/*
	 * setup the socket to send and recieve messages
	 */
	name.sun_family = AF_UNIX;
	sock = socket(AF_UNIX, SOCK_DGRAM, 0);
	if (sock < 0){
		perror("cannot create socket:");
		exit(1);
	}

	/*
	 * bind the interrupt handler
	 */
	(void)signal(SIGINT, onint);
	(void)signal(SIGHUP, quit);
	(void)signal(SIGQUIT, quit);
	(void)signal(SIGTERM, quit);

	/*
	 * bind the socket
	 */
	job.pid = (u_long)getpid();
	(void)sprintf(path, "%s/%s%u",SPOOLDIR, CNTRLPRE, job.pid);
	(void)strcpy(name.sun_path, path);
	name.sun_family = AF_UNIX;
	len = strlen(name.sun_path) + 1 + sizeof(name.sun_family);
	(void)unlink(name.sun_path);
	if (bind(sock, &name, len) < 0){
		perror("cannot bind socket");
		exit(1);
	}

	/*
	 * set the users real and effective uid back to the user (we are
	 * running setuid root). All cntrl sockets must be owned by root.
	 * set gid to lddgrp so socket can be removed later.
	 */
	(void)setregid(getgid(), LDDGID);
	(void)setreuid(uid, uid);

	/*
	 * setup the name structure so all messages will be sent
	 * to the server
	 */
	(void)strcpy(name.sun_path, CNTRLPATH);
	len = strlen(name.sun_path) + 1 + sizeof(name.sun_family);

	/*
	 * descsize for the select call
	 */
	descsize = getdtablesize();

	/*
	 * mask to select the socket for messages or stdin input
	 */
	sockmask = 1 << sock;
	stdinmask = 1 << fileno(stdin);
}

/*-------------------------------------------------------------------------
 * sendcntrl
 *
 * send the job datagram as set up by the specific command to the server
 * and then wait for a response. Print diagnostics if the command was not
 * executed by the server or the server does not respond.
 *-------------------------------------------------------------------------
 */
sendcntrl()
{
	int readfds;
	int numfds;
	char msg;
	int fromlen = 0;
	extern struct timeval polltime;
	extern int len;
	extern int errno;
	extern int descsize;
	extern int sockmask;
	extern int sock;
	extern struct request job;
	extern struct sockaddr_un name;
	extern int quit();

	if (sendto(sock,&job,sizeof(struct request),0,&name,len) < 0){
		perror("message to server failed");
		return(-1);
	}

	readfds = sockmask;

	/*
	 * wait for the ok from the server
	 */
	numfds = select(descsize,&readfds,(int *)0,(int *)0,&polltime);
	if ((numfds < 0) && (errno != EINTR)){
		perror("select failed");
		return(-1);
	}

	if (numfds <= 0){
		printf("Server did not respond after %ld seconds\n",WAITTIME);
		return(-1);
	}

	if (recvfrom(sock,&msg,sizeof(msg),0,(struct sockaddr *)0,&fromlen)<=0){
		perror("message from server failed");
		return(-1);
	}
	switch(msg){
		case RUNCMD:
			return(0);
		case POLLCMD:
			printf("new server started, command lost\n");
			return(-1);
		case STOPCMD:
			printf("command failed\n");
			return(-1);
		default:
			printf("unknown message from server\n");
			return(-1);
	}
}
@//E*O*F control/ipc.c//
chmod u=r,g=r,o=r control/ipc.c
 
echo x - control/ldccmds.c
sed 's/^@//' > "control/ldccmds.c" <<'@//E*O*F control/ldccmds.c//'

/*-----------------------------------------------------------------------
 * commands.c - control program
 *
 * this file contains the commands that the ldc program can execute
 *-----------------------------------------------------------------------
 */

/* $Log$ */

#include "../h/common.h"
#include "../h/control.h"
#include <sys/time.h>
#include <stdio.h>
#include <pwd.h>


/*---------------------------------------------------------------------------
 * abortserver
 *
 * tell the server to give it up
 * NOTE: for saftey reasons you must type both words: abort server 
 *       abort alone will NOT work (for people who cannot type!)
 *---------------------------------------------------------------------------
 */
abortserv(argc, argv)
int argc;
char **argv;
{
	extern struct request job;

	if (argc != 2){
		printf("Usage: abort server\n");
		return;
	}

	/*
	 * second arg better be a prefix of the word server, else a typo!
	 */
	if (strncmp(argv[1], "server", strlen(argv[1])) != 0){
		printf("Usage: abort server\n");
		return;
	}

	/*
	 * bye bye old server
	 */
	job.type = ABORTCMD;
	(void)sendcntrl();
}

/*-------------------------------------------------------------------------
 * errprint
 *
 * print out the contents of the error logging file.
 *-------------------------------------------------------------------------
 */
errprint(argc, argv)
int argc;
char **argv;
{
	char linebuf[256];
	extern char *fgets();
	extern FILE *out;
	extern int fclose();
	FILE *fopen();

	if (argc != 1){
		printf("Usage: errprint\n");
		return;
	}

	if ((out = fopen(ERRORPATH, "r")) == NULL){
		printf("cannot open error file\n");
		return;
	}

	/*
	 * print out the contents of the error file exactly
	 */
	while (fgets(linebuf, sizeof(linebuf), out) != (char *)0)
		fputs(linebuf, stdout);

	(void)fclose(out);
	out = NULL;
}

/*-------------------------------------------------------------------------
 * loadlimt
 *
 * tell the server to change the point at which it queues jobs
 *-------------------------------------------------------------------------
 */
loadlimit(argc, argv)
int argc;
char **argv;
{
	double load;
	extern struct request job;
	extern double atof();

	if (argc != 2){
		printf("Usage: loadlimit load\n");
		return;
	}

	job.type = LOADLIMCMD;
	if ((load = atof(argv[1])) <= 0){
		printf("Bad value for loadlimit: %.2lf\n",load);
		return;
	}
	if (load < LOWLOAD)
		printf("WARNING: %.2lf load might cause excessive queue times\n",load);
	else if (load > HIGHLOAD)
		printf("WARNING: %.2lf load might never queue jobs\n",load);
	
	/*
	 * the value 256 is used to scale up the floating point load
	 * average so the value fits in a long. The 256 value was picked
	 * as that is the same value sun uses in the kernel to scale the
	 * load averages
	 */
	job.time  = (u_long)(256.0 * load);
	(void)sendcntrl();
}

/*-------------------------------------------------------------------------
 * move
 *
 * move a pid around in the queue
 *-------------------------------------------------------------------------
 */
move(argc, argv)
int argc;
char **argv;
{
	long temp;
	extern struct request job;
	extern long atol();

	if (argc != 3){
		printf("Usage: move pid rank\n");
		return;
	}
	job.type = MOVECMD;
	if ((temp = atol(argv[1])) <= 0){
		printf("Bad pid: %ld\n",temp);
		return;
	}
	job.uid = (u_long)temp;
	if ((temp = atol(argv[2])) <= 0){
		printf("Bad rank: %ld\n",temp);
		return;
	}
	job.time = (u_long)temp;
	(void)sendcntrl();
}

/*-------------------------------------------------------------------------
 * purge
 *
 * tell the server to blow away all the jobs in its queue.
 * NOTE: for saftey you must type purge all!
 *-------------------------------------------------------------------------
 */
purge(argc, argv)
int argc;
char **argv;
{
	extern struct request job;

	if (argc != 2){
		printf("Usage: purge all\n");
		return;
	}

	/*
	 * next arg must be a prefix of the word: all
	 */
	if (strncmp(argv[1], "all", strlen(argv[1])) != 0){
		printf("Usage: purge all\n");
		return;
	}

	/*
	 * clean up the queue server
	 */
	job.type = PALLCMD;
	(void)sendcntrl();
}

/*-------------------------------------------------------------------------
 * run
 *
 * tell the server to run listed job[s] regardless of the load
 *-------------------------------------------------------------------------
 */
run(argc, argv)
int argc;
char **argv;
{
	register int i;
	long temp;
	struct passwd *pwd;
	extern int uid;
	extern struct request job;
	extern long atol();
	extern int strncmp();
	extern int endpwent();
	extern struct passwd *getpwnam();

	if (argc < 2){
		printf("Usage: run [pid(s)] [-u user(s)]\n");
		return;
	}

	if (strncmp(argv[1], "-u", 2) == 0){
		/*
		 * run all jobs owned by user (-u option)
		 */
		if (argc < 3){
			printf("No user name\n");
			printf("Usage: run [pid(s)] [-u user(s)]\n");
			return;
		}
		job.type = RUSRCMD;
		for (i = 2; i < argc; i++){
			/*
			 * loop through each arg and run all that users jobs
			 */
		 	if ((pwd = getpwnam(argv[i])) == (struct passwd *)NULL){
				/*
				 * couldn't find the user go to next one
				 */
				printf("No such user: %s\n", argv[i]);
				continue;
			}

			/*
			 * run all this users jobs
			 */
			job.uid = (u_long)pwd->pw_uid;
			printf("user %s: ", argv[i]);
			if (sendcntrl() == 0)
				printf("running.\n");
		}
		(void)endpwent();
	}else{
		job.type = RJOBCMD;
		job.uid = uid;
		for (i = 1; i < argc; i++){
			if ((temp = atol(argv[i])) <= 0){
				printf("Bad pid: %ld\n",temp);
				continue;
			}
			job.time = (u_long)temp;
			printf("%ld: ", temp);
			if (sendcntrl() == 0)
				printf("running\n");
		}
	}
}

/*-------------------------------------------------------------------------
 * status
 *
 * ask the server to update the status file. The status file contains the
 * current settings of those paramters the control program can adjust.
 *-------------------------------------------------------------------------
 */
status(argc, argv)
int argc;
char **argv;
{
	int qcount;
	int full;
	int timerstop;
	u_long timesecs;
	u_long mqtime;
	int errorcount;
#ifdef sun
	long loadlevel;
#else
	double loadlevel;
#endif
	extern int fclose();
	FILE *fopen();
	extern FILE *out;
	extern struct request job;

	if (argc != 1){
		printf("Usage: status\n");
		return;
	}

	job.type = STATUSCMD;

	/*
	 * only print if ok
	 */
	if (sendcntrl() < 0)
		return;
	
	if ((out = fopen(STATUSFILE, "r")) == NULL){
		printf("Cannot open status file\n");
		return;
	}

	/*
	 * the value 7 below is the argument count returned by fscanf.
	 * should be the same as the number of variables read.
	 */
#ifdef sun
	if (fscanf(out,"%d %d %d %ld %ld %d %ld",&qcount,&full,&timerstop,
#else
	if (fscanf(out,"%d %d %d %ld %ld %d %lf",&qcount,&full,&timerstop,
#endif
			      &timesecs,&mqtime,&errorcount,&loadlevel) != 7){
		printf("Status file has a data count error.\n");
		(void)fclose(out);
		out = NULL;
		return;
	}
	(void)fclose(out);
	out = NULL;

	if ((timerstop == 1) && (qcount != 0)){
		printf("WARNING: the timer is stopped (nonempty queue)\n");
		printf("THIS SHOULD NEVER HAPPEN!\n");
		printf("Try the timerset command to restart\n\n");
	}

	printf("number of jobs in queue:\t%d ", qcount);
	if (qcount >= full)
		printf("\t\t*** WARNING: FULL QUEUE ***\n");
	else
		printf("\n");
	printf("current maximium queue size:\t%d ", full);
	if (full < LOWSIZE)
		printf("\t\t*** WARNING: SET TOO LOW ***\n");
	else if  (full > HIGHSIZE)
		printf("\t\t*** WARNING: SET TOO HIGH ***\n");
	else
		printf("\n");
#ifdef sun
	printf("jobs queue at loads above:\t%3.2lf ",((double)loadlevel)/256.0);
	if (loadlevel < (LOWLOAD * 256))
		printf("\t\t*** WARNING: SET TOO LOW ***\n");
	else if (loadlevel > (HIGHLOAD * 256))
		printf("\t\t*** WARNING: SET TOO HIGH ***\n");
	else
		printf("\n");
#else
	printf("jobs queue at loads above:\t%3.2lf ", loadlevel);
	if (loadlevel < LOWLOAD)
		printf("\t\t*** WARNING: SET TOO LOW ***\n");
	else if (loadlevel > HIGHLOAD)
		printf("\t\t*** WARNING: SET TOO HIGH ***\n");
	else
		printf("\n");
#endif
	printf("jobs are queued no more than:\t%u (secs) ", mqtime);
	if (mqtime <= timesecs)
		printf("\t*** WARNING: SET TOO LOW ***\n");
	else if (mqtime > HIGHMAX)
		printf("\t*** WARNING: SET TOO HIGH ***\n");
	else
		printf("\n");
	printf("load average is checked every:\t%u (secs) ",timesecs);
	if (timesecs > (mqtime/2))
		printf("\t*** WARNING: SET TOO HIGH ***\n");
	else if (timesecs < LOWCYCLE)
		printf("\t*** WARNING: SET TOO LOW ***\n");
	else
		printf("\n");
	printf("the number of server errors:\t%d\n", errorcount);
}

/*-------------------------------------------------------------------------
 * sizeset
 *
 * change the limit on the number of jobs allowed to be queued waiting for
 * the systems load average to drop.
 *-------------------------------------------------------------------------
 */
sizeset(argc, argv)
int argc;
char **argv;
{
	long temp;
	extern struct request job;
	extern long atol();

	if (argc != 2){
		printf("Usage: sizeset size\n");
		return;
	}
	job.type = QUEUESIZE;
	if ((temp = atol(argv[1])) <= 0){
		printf("Bad value for size of queue: %ld\n", temp);
		return;
	}
	if (temp < LOWSIZE)
		printf("WARNING: %ld is a very small queue size limit.\n",temp);
	else if (temp > HIGHSIZE)
		printf("WARNING: %ld is a very large queue size limit.\n",temp);
	job.time = (u_long)temp;
	(void)sendcntrl();
}

/*-------------------------------------------------------------------------
 * timerset
 *
 * change the number of second the server waits before checking the load
 * average to see if queued jobs can run
 *-------------------------------------------------------------------------
 */
timerset(argc, argv)
int argc;
char **argv;
{
	long temp;
	extern struct request job;
	extern long atol();

	if (argc != 2){
		printf("Usage: timerset seconds\n");
		return;
	}
	job.type = CHTIMER;
	if ((temp = atol(argv[1])) <= 0){
		printf("Bad value for cycle time: %ld\n", temp);
		return;
	}
	if (temp < LOWCYCLE)
		printf("WARNING: %ld is a very small cycle time\n", temp);
	else if (temp > HIGHCYCLE)
		printf("WARNING: %ld is a very large cycle time\n", temp);
	job.time = (u_long)temp;
	(void)sendcntrl();
}

/*-------------------------------------------------------------------------
 * waitset
 *
 * change the maximium time a job can remain queued. After that time the
 * job will run regardless of the current load. This is needed in case the
 * loadlimit is set too low, or not enough of the programs that are causing
 * the high load are NOT controlled by the server. This keeps jobs from
 * being queued up for excessive times.
 *-------------------------------------------------------------------------
 */
waitset(argc, argv)
int argc;
char **argv;
{
	long temp;
	extern struct request job;
	extern long atol();

	if (argc != 2){
		printf("Usage: waitset seconds\n");
		return;
	}
	job.type = MQTIMECMD;
	if ((temp = atol(argv[1])) <= 0){
		printf("Bad value for waitset: %ld\n",temp);
		return;
	}
	job.time = (u_long)temp;
	if (job.time < LOWMAX)
		printf("WARNING: %u is a very short time limit for the max queue time\n",job.time);
	else if (job.time > HIGHMAX)
		printf("WARNING: %u is a very large time limit for the max queue time\n",job.time);
	(void)sendcntrl();
}
@//E*O*F control/ldccmds.c//
chmod u=r,g=r,o=r control/ldccmds.c
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
      65     156    1262 Makefile
     112     382    2627 delete.c
      28     126     907 globals.c
     175     492    4008 ipc.c
     476    1455   11267 ldccmds.c
     856    2611   20071 total
!!!
wc  control/Makefile control/delete.c control/globals.c control/ipc.c control/ldccmds.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0

muller@sdcc3.UUCP (Keith Muller) (02/12/85)

This is part 5 of the load control system. Part 1 must be unpacked before any
other part.
	Keith Muller
	ucbvax!sdcsvax!muller



# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by sdcc3!muller on Sat Feb  9 13:54:09 PST 1985
# Contents:  control/ldccmdtab.c control/ldcmain.c control/ldqmain.c
#	control/ldrmmain.c control/list.c
 
echo x - control/ldccmdtab.c
sed 's/^@//' > "control/ldccmdtab.c" <<'@//E*O*F control/ldccmdtab.c//'

/*---------------------------------------------------------------------------
 * commandtab.c - control
 *
 * tables of commands that the load program will call
 *
 *---------------------------------------------------------------------------
 */

/* $Log$ */

#include "../h/control.h"

char	aborhelp[] =	"terminate the server (disable load control)";
char	delhelp[] =	"remove specified jobs (by pid or user) from the queue";
char	errhelp[] =	"print the contents of the error logging file";
char	helphelp[] =	"get help on any command";
char	listhelp[] =	"list all the jobs queued for a specified user";
char	loadhelp[] =	"change the load level above which jobs are queued";
char	longhelp[] =	"list all queued jobs";
char	movehelp[] =	"move a processes position in the queue";
char	purghelp[] =	"remove ALL the jobs from the queue (the jobs terminate)";
char	quithelp[] =	"exit the control program";
char	runhelp[] =	"run specified jobs (by pid or user) from the queue";
char	sizehelp[] =	"change the maximium number of jobs that can be queued";
char	statushelp[] =	"print the current settings of changeable parameters";
char	timerhelp[] =	"change the number of seconds between load average checks";
char	waithelp[] =	"change the maximium time (seconds) a job can be queued";

/*
 * import the functions
 */
extern int abortserv(), delete(), errprint(), help(), list(), loadlimit(), longlist();
extern int move(), purge(), quit(), run(), sizeset(), status(), timerset(), waitset();

/*
 * the command table
 * the privledge column restrict certain commands to root (root only
 * as other changes could cause the server to be aborted with no
 * way to restart!
 */

struct cmd cmdtab[] = {

/*	   command	helpmessage	routine		privledge */

	{ "abort",	aborhelp,	abortserv,	RESTRICT },
	{ "delete",	delhelp,	delete,		NORESTRICT },
	{ "errors",	errhelp,	errprint,	NORESTRICT },
	{ "help",	helphelp,	help,		NORESTRICT },
	{ "list",	listhelp,	list,		NORESTRICT },
	{ "loadlimit",	loadhelp,	loadlimit,	RESTRICT },
	{ "longlist",	longhelp,	longlist,	NORESTRICT },
	{ "move",	movehelp,	move,		RESTRICT },
	{ "purge",	purghelp,	purge,		RESTRICT },
	{ "quit",	quithelp,	quit,		NORESTRICT },
	{ "run",	runhelp,	run,		RESTRICT },
	{ "sizeset",	sizehelp,	sizeset,	RESTRICT },
	{ "status",	statushelp,	status,		NORESTRICT },
	{ "timerset",	timerhelp,	timerset,	RESTRICT },
	{ "waitset",	waithelp,	waitset,	RESTRICT },
	{ "?",		helphelp,	help,		NORESTRICT },
	{ 0 },
};

int	NCMDS = sizeof (cmdtab) / sizeof (cmdtab[0]);
@//E*O*F control/ldccmdtab.c//
chmod u=r,g=r,o=r control/ldccmdtab.c
 
echo x - control/ldcmain.c
sed 's/^@//' > "control/ldcmain.c" <<'@//E*O*F control/ldcmain.c//'

/*-------------------------------------------------------------------------
 * main.c - control/ldc
 *
 * the ldc program is used to send commands to the server that can 
 * alter the servers execution or display the servers current state
 *
 * Author: Keith Muller
 *	   University Of California, San Diego
 *	   Academic Computer Center C - 010
 *	   La Jolla, Ca 92093
 *	   ucbvax!sdcsvax!sdcc3!muller
 *	   (619) 452-6090
 *-------------------------------------------------------------------------
 */

/* $Log$ */

#include "../h/common.h"
#include "../h/control.h"
#include <sys/uio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/time.h>
#include <stdio.h>
#include <setjmp.h>
#include <errno.h>

int fromatty;		/* set to 1 if interactive, 0 otherwise */
char cmdline[256];	/* buffer for the command */
char *margv[64];	/* args in interactive mode */
int margc;		/* count of args */
jmp_buf toplevel;	/* jmp_buf used if a command is aborted premature */

/*-------------------------------------------------------------------------
 * main
 *
 *-------------------------------------------------------------------------
 */
main(argc, argv)
int argc;
char *argv[];
{
	register struct cmd *c;
	extern int uid;
	extern int isatty();
	extern char *gets();
	extern struct cmd *getcmd();

	/*
	 * set up the necessary overhead 
	 */
	setup();

	/*
	 * could be a single line command
	 */
	if (--argc > 0) {
		if ((c = getcmd(*++argv)) == 0)
			quit();
		if ((c->c_priv == RESTRICT) && (uid != 0)){
			printf("?Privileged command\n");
			quit();
		}
		/*
		 * run the entry from the command table
		 */
		(*c->c_handler)(argc, argv);
		/*
		 * clean up
		 */
		quit();
	}

	/*
	 * see if the program is attached to a tty
	 */
	fromatty = isatty(fileno(stdin));

	/*
	 * on an interrupt return to this point!
	 */
	(void)setjmp(toplevel);

	for (;;) {
		if (fromatty){
			printf("\nldc> ");
			(void)fflush(stdout);
		}

		/*
		 * wait for io either on the tty or a status message
		 * from the server is read.
		 */
		if (iowait() == 0)
			continue;

		if (gets(cmdline) == NULL)
			break;
		if (cmdline[0] == '\0')
			continue;
		/*
		 * transform into a argv argc array
		 */
		makeargv();

		/*
		 * valid command?
		 */
		if ((c = getcmd(margv[0])) == 0)
			continue;

		if ((c->c_priv == RESTRICT) && (uid != 0)) {
			printf("?Privileged command\n");
			continue;
		}

		/*
		 * run the command from the command table
		 */
		(*c->c_handler)(margc, margv);
	}
	quit();
}

/*-------------------------------------------------------------------------
 * onint
 *
 * when an interrupt occurs jump here!
 *-------------------------------------------------------------------------
 */
onint()
{
	extern FILE *out;

	/*
	 * if fromtty == 0 standard input is not a tty, so exit
	 */
	if (fromatty == 0)
		quit();
	
	/*
	 * if list or status command was interrupted, close off the
	 * open file
	 */
	if (out != NULL)
		(void)fclose(out);

	/*
	 * interactive so continue
	 */
	putchar('\n');
	longjmp(toplevel, 1);
}

/*-------------------------------------------------------------------------
 * quit
 *
 *-------------------------------------------------------------------------
 */
quit()
{
	extern int sock;
	extern char path[];

	(void)close(sock);
	(void)unlink(path);
	exit(0);
}

/*-------------------------------------------------------------------------
 * getcmd
 *
 * find closest fit for the command typed in
 *-------------------------------------------------------------------------
 */
struct cmd *
getcmd(comname)
char *comname;
{
	register char *p;
	register char *q;
	register struct cmd *c;
	register struct cmd *found;
	int nmatches;
	int longest;
	extern struct cmd cmdtab[];

	longest = 0;
	nmatches = 0;
	found = (struct cmd *)0;

	for (c = cmdtab; (p = c->c_name) != (char *)0; c++) {
		for (q = comname; *q == *p++; q++){
			/*
			 * exact match?
			 */
			if (*q == '\0')
				return(c);
		}

		/*
		 * name is a prefix
		 */
		if (*q == '\0') {
			if (q - comname > longest) {
				longest = q - comname;
				nmatches = 1;
				found = c;
			} else if ((q - comname) == longest)
				nmatches++;
		}
	}

	/*
	 * More than one match found
	 */
	if (nmatches > 1){
		printf("?Ambiguous command\n");
		return((struct cmd *)0);
	}

	/*
	 * No match found in table
	 */
	if (found == (struct cmd *)0)
		printf("?Invalid command\n");
	return(found);
}

/*-------------------------------------------------------------------------
 * makeargv
 *
 * Slice a string up into argc/argv.
 *-------------------------------------------------------------------------
 */
makeargv()
{
	register char *cp;
	register char **argp = margv;

	margc = 0;
	for (cp = cmdline; *cp != '\0'; ) {
		/*
		 * skip over the blanks
		 */
		while (*cp == ' ')
			cp++;

		if (*cp == '\0')
			break;

		*argp++ = cp;
		margc = margc + 1;
		while ((*cp != '\0') && (*cp != ' '))
			cp++;

		if (*cp == '\0')
			break;

		*cp++ = '\0';
	}
	*argp++ = (char *)0;
}

/*-------------------------------------------------------------------------
 * Help command.
 *
 *-------------------------------------------------------------------------
 */

help(argc, argv)
int argc;
char *argv[];
{
	register struct cmd *c;
	register int i;
	register int j;
	register int w;
	char *arg;
	int columns;
	int width = 0;
	int lines;
	int len;
	extern int NCMDS;

	if (argc == 1) {
		printf("Commands may be abbreviated.  Commands are:\n");

		for (c = cmdtab; c < &(cmdtab[NCMDS-1]); c++) {
			len = strlen(c->c_name);
			if (len > width)
				width = len;
		}

		width = (width + 8) &~ 7;
		columns = 80 / width;
		if (columns == 0)
			columns = 1;
		lines = (NCMDS + columns - 1) / columns;

		for (i = 0; i < lines; i++) {
			for (j = 0; j < columns; j++) {
				c = cmdtab + j * lines + i;
				printf("%s", c->c_name);
				if (c + lines >= &(cmdtab[NCMDS-1])) {
					printf("\n");
					break;
				}
				w = strlen(c->c_name);
				while (w < width) {
					w = (w + 8) &~ 7;
					putchar('\t');
				}
			}
		}
		return;
	}
	while (--argc > 0) {
		arg = *++argv;
		if ((c = getcmd(arg)) == (struct cmd *)0)
			continue;
		printf("%-*s\t%s\n", 10, c->c_name, c->c_help);
	}
}

/*-------------------------------------------------------------------------
 * iowait
 *
 * wait for io on either stdin or a status message from server.
 *-------------------------------------------------------------------------
 */
iowait()
{
	int readfds;
	int numfds;
	char msg;
	int fromlen = 0;
	extern int errno;
	extern int descsize;
	extern int sockmask;
	extern int stdinmask;
	extern int sock;

	for(;;){
		readfds = sockmask | stdinmask;
		numfds = select(descsize,&readfds,(int *)0,(int *)0,(struct timeval *)0);
		if ((numfds < 0) && (errno != EINTR)){
			perror("select failed");
			quit();
		}

		if (numfds <= 0)
			continue;

		if (readfds & stdinmask)
			return(1);

		if (recvfrom(sock,&msg,sizeof(msg),0,(struct sockaddr *)0,&fromlen)<=0){
			perror("Failed message from server");
			quit();
		}
		switch(msg){
			case POLLCMD:
				printf("A new server just started\n");
				break;
			default:
				printf("Unexpected message from server\n");
				break;
		}
		return(0);
	}
}
@//E*O*F control/ldcmain.c//
chmod u=r,g=r,o=r control/ldcmain.c
 
echo x - control/ldqmain.c
sed 's/^@//' > "control/ldqmain.c" <<'@//E*O*F control/ldqmain.c//'

/*-------------------------------------------------------------------------
 * ldqmain.c - control/ldq
 *
 * the ldq program is used to print the contents of the load queue.
 * The same function is also part of the ldc command.
 *
 * Author: Keith Muller
 *	   University Of California, San Diego
 *	   Academic Computer Center C - 010
 *	   La Jolla, Ca 92093
 *	   ucbvax!sdcsvax!sdcc3!muller
 *	   (619) 452-6090
 *-------------------------------------------------------------------------
 */

/* $Log$ */

#include "../h/control.h"
#include <stdio.h>

/*-------------------------------------------------------------------------
 * main
 *
 *-------------------------------------------------------------------------
 */
main(argc, argv)
int argc;
char *argv[];
{
	if (argc > 2){
		printf("usage: ldq [-a] [user]\n");
		exit(1);
	}

	/*
	 * set up the necessary overhead 
	 */
	setup();

	/*
	 * call the correct list version (same as in ldc).
	 */
	if ((argc == 2) && (strcmp(argv[1],"-a") == 0)){
		/*
		 * decrement the argc count to get rid of the -a flag.
		 */
		argc = 1;
		longlist(argc,argv);
	}else{
		/*
		 * must be a short list call
		 */
		list(argc,argv);
	}

	/*
	 * clean up the sockets
	 */
	quit();
}

/*-------------------------------------------------------------------------
 * onint
 *
 * when an interrupt occurs jump here!
 *-------------------------------------------------------------------------
 */
onint()
{
	printf("\n....interrupted\n");
	quit();
}

/*-------------------------------------------------------------------------
 * quit
 *
 *-------------------------------------------------------------------------
 */
quit()
{
	extern int sock;
	extern char path[];

	(void)close(sock);
	(void)unlink(path);
	exit(0);
}
@//E*O*F control/ldqmain.c//
chmod u=r,g=r,o=r control/ldqmain.c
 
echo x - control/ldrmmain.c
sed 's/^@//' > "control/ldrmmain.c" <<'@//E*O*F control/ldrmmain.c//'

/*-------------------------------------------------------------------------
 * ldrmmain.c - control/ldrm
 *
 * the ldrm program is used to delete jobs from the load queue.
 * The same function is also part of the ldc command.
 *
 * Author: Keith Muller
 *	   University Of California, San Diego
 *	   Academic Computer Center C - 010
 *	   La Jolla, Ca 92093
 *	   ucbvax!sdcsvax!sdcc3!muller
 *	   (619) 452-6090
 *-------------------------------------------------------------------------
 */

/* $Log$ */

#include "../h/control.h"
#include <stdio.h>

/*-------------------------------------------------------------------------
 * main
 *
 *-------------------------------------------------------------------------
 */
main(argc, argv)
int argc;
char *argv[];
{
	if (argc < 2){
		printf("useage: ldrm [pids] [-u users]\n");
		exit(1);
	}
 	if (strncmp(argv[1], "-u", 2) == 0){
		/*
	 	 * remove all jobs by user (-u option)
	 	 */
		if (argc < 3){
			printf("No user name.\n");
			printf("Usage: ldrm [pids] [-u users]\n");
			return;
		}
	}

	/*
	 * set up the necessary overhead 
	 */
	setup();

	/*
	 * call the delete routine
	 */
	delete(argc, argv);

	/*
	 * clean up the sockets
	 */
	quit();
}

/*-------------------------------------------------------------------------
 * onint
 *
 * when an interrupt occurs jump here!
 *-------------------------------------------------------------------------
 */
onint()
{
	printf("\n....interrupted\n");
	quit();
}

/*-------------------------------------------------------------------------
 * quit
 *
 *-------------------------------------------------------------------------
 */
quit()
{
	extern int sock;
	extern char path[];

	(void)close(sock);
	(void)unlink(path);
	exit(0);
}
@//E*O*F control/ldrmmain.c//
chmod u=r,g=r,o=r control/ldrmmain.c
 
echo x - control/list.c
sed 's/^@//' > "control/list.c" <<'@//E*O*F control/list.c//'

/*-----------------------------------------------------------------------
 * list.c - control program
 *
 * this file contains the commands: longlist and list which are common
 * to both ldc and ldq.
 *-----------------------------------------------------------------------
 */

/* $Log$ */

#include "../h/common.h"
#include "../h/control.h"
#include <sys/time.h>
#include <stdio.h>
#include <pwd.h>

struct hashbuck names[HASHMOD];

/*----------------------------------------------------------------------------
 * getuname
 *
 * keep a local hash table of recently requested uids. Speeds up the list
 * commands when repeatively invoked by ldc. This is done because looking
 * up passwd entries are expensive. So expensive that it SHOULD NEVER be
 * done by the server. The server NEVER SHOULD look up passwd entries. This
 * routine helps unhashed (generic 4.2) passwd files greatly. It is even
 * worthwhile on newer dbm passwd file.
 *-----------------------------------------------------------------------------
 */
static char *
getuname(huid)
register int huid;
{
	register struct hashbuck *hashptr;
	register struct passwd *pwd;
	extern struct passwd *getpwuid();
	extern char *strncpy();

	/*
	 * hash and find the address of the bucket
	 */
	hashptr = &(names[huid % HASHMOD]);

	/*
	 * if this is the user return the name
	 */
	if ((hashptr->buckuid == huid) && (hashptr->buckname[0] != '\0'))
		return(hashptr->buckname);

	/*
	 * not the same user (or empty) so update this bucket. 
	 * collisions should be rare if the hashtable is of reasonable
	 * size reletive to the size of the passwd file.
	 */
	if ((pwd = getpwuid(huid)) != (struct passwd *)NULL){
		hashptr->buckuid = huid;
		(void)strncpy(hashptr->buckname, pwd->pw_name, PNAMSIZ);
		return(hashptr->buckname);
	}

	/*
	 * bad uid, skip it
	 */
	return((char *)0);
}

/*----------------------------------------------------------------------------
 * longlist
 *
 * tell the server to update the list of queued jobs in the list file. When
 * the server says the list is updated, show ALL the queued jobs to the user
 *-----------------------------------------------------------------------------
 */
longlist(argc, argv)
int argc;
char **argv;
{
	int juid;
	u_long jpid;
	u_long jtime;
	int pos;
	int qcount;
	char *jname;
	char jcom[COMLEN];
	struct timezone zone;
	struct timeval now;
	extern FILE *out;
	extern int gettimeofday();
	extern int fclose();
	FILE *fopen();
	extern struct request job;

	if (argc != 1){
		printf("Usage: longlist\n");
		return;
	}

	job.type = LISTCMD;

	/*
	 * only try to print the list if the server says all is ok!
	 */
	if (sendcntrl() < 0)
		return;
	
	if ((out = fopen(LISTFILE, "r")) == NULL){
		printf("cannot open listfile file\n");
		return;
	}

	/*
	 * first entry in the queue is the qcount. if it is zero just say so
	 */
	if (fscanf(out,"%d",&qcount) != 1){
		printf("No queue info available\n");
		(void)fclose(out);
		out = NULL;
		return;
	}

	if (qcount < 1){
		printf("Queue empty\n");
		(void)fclose(out);
		out = NULL;
		return;
	}

	/*
	 * at least one job is in the queue.
	 */
	printf(" rank\t  pid\t  user\t  time(sec)     commands\n");
	printf("-------------------------------------------------------\n");
	pos = 1;
	(void)gettimeofday(&now, &zone);
	while(fscanf(out,"%d %ld %ld %[^\n]",&juid,&jpid,&jtime,jcom) == 4){
		printf(" %3d\t",pos++);
		printf(" %5u\t",jpid);
		if ((jname = getuname(juid)) != (char *)0)
			printf(" %-8.8s",jname);
		else
			printf(" %-8.8d",juid);
		printf("   %5ld",now.tv_sec - (long)jtime);
		printf("      %s\n",jcom);
	}

	/*
	 * if eof is not set, file has an error
	 */
	if (feof(out) == 0)
		printf("Error in list file\n");
	(void)fclose(out);
	out = NULL;
}

/*----------------------------------------------------------------------------
 * list
 *
 * tell the server to update the list of queued jobs in the list file. When
 * the server says the list is updated, show ONLY this users jobs.
 *-----------------------------------------------------------------------------
 */
list(argc, argv)
int argc;
char **argv;
{
	int juid;
	int cmpuid;
	u_long jpid;
	u_long jtime;
	int pos;
	int qcount;
	char *jname;
	char jcom[COMLEN];
	int found;
	struct passwd *pwd;
	struct timezone zone;
	struct timeval now;
	extern int uid;
	extern FILE *out;
	extern struct passwd *getpwnam();
	extern int gettimeofday();
	extern int fclose();
	FILE *fopen();
	extern struct request job;

	if (argc > 2){
		printf("Usage: list [user]\n");
		return;
	}

	if (argc == 2){
		/*
	 	 * if a user is specified, look him up in the passwd file
	  	 */
		if ((pwd = getpwnam(argv[1])) == (struct passwd *)NULL){
			printf("No such user: %s\n", argv[1]);
			(void)endpwent();
			return;
		}
		cmpuid = pwd->pw_uid;
		jname = pwd->pw_name;
		(void)endpwent();
	}else{
		/*
		 * no user specified, use the current users uid
		 */
		cmpuid = uid;
		if ((jname = getuname(uid)) == (char *)0){
			printf("Cannot find you in the passwd file\n");
			return;
		}
	}

	job.type = LISTCMD;

	/*
	 * only try to print the list if the server says all is ok!
	 */
	if (sendcntrl() < 0)
		return;
	
	if ((out = fopen(LISTFILE, "r")) == NULL){
		printf("cannot open listfile file\n");
		return;
	}

	/*
	 * first entry in the queue is the qcount. if it is zero just say so
	 */
	if (fscanf(out,"%d",&qcount) != 1){
		printf("No queue info available\n");
		(void)fclose(out);
		out = NULL;
		return;
	}

	if (qcount < 1){
		printf("Queue empty\n");
		(void)fclose(out);
		out = NULL;
		return;
	}

	/*
	 * at least one job is in the queue. Look for a job owned by the
	 * specific user.
	 */
	found = 0;
	pos = 0;
	while(fscanf(out,"%d %ld %ld %[^\n]",&juid,&jpid,&jtime,jcom) == 4){
		pos++;
		if (cmpuid == juid){
			/*
			 * found a job owned by the user
			 */
			found = 1;
			break;
		}
	}
	if (found == 0){
		/*
		 * no jobs found, either an error or no jobs for that user
		 * is in the queue.
		 */
		if (feof(out) == 0){
			printf("Error in list file\n");
		}else{
			printf("No jobs in queue for: %s\n", jname);
		}
		(void)fclose(out);
		out = NULL;
		return;
	}
	(void)gettimeofday(&now, &zone);
	printf(" rank\t  pid\t  user\t  time(sec)     commands\n");
	printf("-------------------------------------------------------\n");
	printf(" %3d\t %5u\t %-8.8s",pos++, jpid, jname);
	printf("   %5ld",now.tv_sec - (long)jtime);
	printf("      %s\n",jcom);
	while(fscanf(out,"%d %ld %ld %[^\n]",&juid,&jpid,&jtime,jcom) == 4){
		/*
		 * only print this users jobs
		 */
		if (cmpuid != juid){
			pos++;
			continue;
		}
		printf(" %3d\t %5u\t %-8.8s",pos++, jpid, jname);
		printf("   %5ld",now.tv_sec - (long)jtime);
		printf("      %s\n",jcom);
	}
	if (feof(out) == 0)
		printf("Error in list file\n");
	(void)fclose(out);
	out = NULL;
}
@//E*O*F control/list.c//
chmod u=r,g=r,o=r control/list.c
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
      66     365    2506 ldccmdtab.c
     373     968    7150 ldcmain.c
      88     195    1754 ldqmain.c
      87     185    1736 ldrmmain.c
     297     910    6776 list.c
     911    2623   19922 total
!!!
wc  control/ldccmdtab.c control/ldcmain.c control/ldqmain.c control/ldrmmain.c control/list.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0