load control updates

Keith Muller muller at sdcc3.UUCP
Fri Aug 16 06:20:09 AEST 1985


Here are the updates to the load control system of Jan 24, 1985. These
changes only add a couple of security measures and allow load control to
run on more recent versions of 4.2BSD. These changes are not really
required for 4.2BSD systems.
This shar file should be unpacked in the directory where the ldc sources
exsist (as it expects the exsistence of the directories client, server and
control).
NOTE: make sure to ether remove or change the names of the following
files BEFORE unpacking: client/main.c server/setup.c control/ipc.c

	Keith Muller
	University of California
	muller at nprdc
	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 Thu Aug 15 12:48:30 PDT 1985
# Contents:  client/main.c server/main.c control/ipc.c
 
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>

/*
 * The following is for 4.3 and 4.2 system compatability
 */
#ifndef sigmask
#define sigmask(m)	(1 << ((m)-1))
#endif

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 masksig;			/* 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);

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

	/*
	 * bind the handler to clean up, if the signal was being
	 * ignored (invoked from a bourne shell with a &), continue
	 * to ignore the signal
	 */
	if (signal(SIGINT, onint) == SIG_IGN)
		(void)signal(SIGINT, SIG_IGN);
	if (signal(SIGHUP, onint) == SIG_IGN)
		(void)signal(SIGHUP, SIG_IGN);
	if (signal(SIGQUIT, onint) == SIG_IGN)
		(void)signal(SIGQUIT, SIG_IGN);
	if (signal(SIGTERM, onint) == SIG_IGN)
		(void)signal(SIGTERM, SIG_IGN);

	/*
	 * 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);

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

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

	/*
	 * chmod the socket. keeps all the users honest.
	 */
	(void)chmod(name.sun_path, 0700);

	/*
	 * 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(masksig);
		run(argv, envp);
	}

	msgmask = 1 << msgsock;
	descsize = msgsock + 1;
	pollcount = 0;

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

#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=rw,g=r,o=r client/main.c
 
echo x - server/main.c
sed 's/^@//' > "server/main.c" <<'@//E*O*F server/main.c//'

/*-------------------------------------------------------------------------
 * main.c - server
 *
 * The server takes requests from client processes and the control
 * program, and performs various operations. The servers major task is
 * to attempt to maintain the systems load average close to a set limit
 * loadlevel. Client processes are kept in a queue and are waiting for a
 * command from the server (to run or abort). The server reads /dev/kmem
 * every ALRMTIME seconds checking to see if the load level has dropped
 * below the required loadlevel. If the queue is empty the timer is turned
 * off. While the timer is off, the server will only read /dev/kmem at the
 * receipt of a request to run from a client program.
 *
 * The server was designed to be as fault tolerant as possible and maintains
 * an errorfile of detectable errors. The server can safely be aborted and
 * restarted without deadlocking the clients. The server when restarted
 * will rebuild the queue of waiting processes to the state that exsisted
 * before the prvious server exited. The entire system was designed to allow
 * execution of user programs (even those under load control) even if the
 * server is not functioning properly! (user jobs will ALWAYS run, the system
 * will never hang).
 *
 * The effectiveness of the system depends on what fraction of the programs
 * that are causing the system overload are maintained under this system.
 * Processes can only remain in queue a maximium of "mqtime" seconds 
 * REGARDLESS of the loadlevel setting. This was done in case the programs 
 * that are keeping the systems loadlevel above the threshold are not
 * controlled by the server! So eventually all jobs will run.
 *
 * The control program allows users to remove their jobs from the queue and
 * allows root to adjust the operating parameters of the server while the
 * server is running.
 * 
 * All the programs and routines are commented and warnings about certain
 * sections of code are given when the code might be vague.
 * 
 * This system has ONLY BEEN RUN ON 4.2 UNIX (sun, vax and pyramid) and uses
 * datagrams in the AF_UNIX domain. (which seems to be extremely reliable).
 *
 * 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/server.h"
#include <sys/time.h>
#include <sys/file.h>
#include <stdio.h>
#include <errno.h>

/*--------------------------------------------------------------------------
 * main
 *
 *--------------------------------------------------------------------------
 */
main(argc, argv)
int argc;
char **argv;
{
	register int msgmask;
	register int cntrlmask;
	int numfds;
	int readfds;
	int readmask;
	extern int msgsock;
	extern int cntrlsock;
	extern int descsize;
	extern int errno;

	/*
	 * check the command line args
	 */
	doargs(argc, argv);

	/*
	 * setup the server
	 */
	setup();

	/*
	 * create all the sockets
	 */
	crsock();

	/*
	 * scan the spool for waiting clients and send them a POLLCMD
	 */
	scanspool();

	/*
	 * create the bit mask used by select to determine which descriptors
	 * are checked for available input ( datagrams).
	 */
	msgmask = 1 << msgsock;
	cntrlmask = 1 << cntrlsock;
	readmask = msgmask | cntrlmask;

	/*
	 * do this forever
	 */
	for(;;){
		readfds = readmask;

		/*
		 * wait for a datagram to arrive
		 */
		numfds = select(descsize,&readfds,(int *)0,(int *)0,(struct timeval *)0);
		if ((numfds < 0) && (errno != EINTR)){
			errlog("select error");
			cleanup();
		}

		/*
		 * if the interval timer interrupted us, go back to the select
		 */
		if (numfds <= 0)
			continue;
		/*
		 * WARNING! note that BOTH SOCKETS are always checked 
		 * when the select indicates at least one datagram is waiting.
		 * This was done to prevent a situation where one socket
		 * "locks" out the other if it is subject to high traffic!
		 */

		/*
		 * first check to see if there is a control message
		 */
		if (readfds & cntrlmask)
			cntrldis();

		/*
		 * now see if there is a queue message
		 */
		if (readfds & msgmask)
			msgdis();
	}

}


/*--------------------------------------------------------------------------
 * onalrm
 *
 * handler for the SIGALRM sent by the interval timer. This routine checks
 * the queue to see if there is any jobs that can be run. The two conditions
 * for running a job is that the load on the machine is below loadlimit or
 * the oldest job in the queue has exceed the maximium queue time and should
 * be run regardless of the load.
 *--------------------------------------------------------------------------
 */
onalrm()
{
	register int count;
	struct timezone zone;
	struct timeval now;
	struct itimerval oldalrm;
	extern struct itimerval stopalrm;
	extern struct qnode *qhead;
	extern u_long mqtime;
	extern int qcount;
	extern int timerstop;
	extern int newstatus;

	/*
	 * if the load average is below the limit run as many jobs as
	 * possable to bring the load up to the loadlimit.
	 * this could cause an overshoot of the loadlimit, but in most
	 * cases this overshoot will be small. This prevents excessive
	 * waiting of jobs due to momentary load peaks.
	 */
	if ((count = getrun()) != 0){
		while ((count > 0) && (qcount > 0)){
			/*
			 * only decrement count if there was really
			 * a waiting client (the client could be dead)
			 */
			if (outmsg(qhead->pid, RUNCMD) == 0)
				count--;
			rmqueue(qhead);
		}
	}else if (qcount > 0){
		/*
		 * load is too high to run a job, check if oldest can be run
		 */
		if (gettimeofday(&now, &zone) < 0){
			errlog("onalrm cannot get time");
			return;
		}
		while ((qcount>0)&&(((u_long)now.tv_sec - qhead->time)>mqtime)){
			/*
			 * determined oldest job can run. if job is
			 * dead try next one
			 */
			if (outmsg(qhead->pid, RUNCMD) == 0){
				rmqueue(qhead);
				break;
			}else
				rmqueue(qhead);
		}
	}

	/*
	 * if the queue is not empty or the interval timer is stopped
	 * then return
	 */
	if ((qcount != 0) || (timerstop == 1))
		return;

	/*
	 * otherwise stop the timer
	 */
	if (setitimer(ITIMER_REAL,&stopalrm, &oldalrm) < 0)
		errlog("stop timer error");
	else{
		timerstop = 1;
		newstatus = 1;
	}
}


/*-------------------------------------------------------------------------
 * getrun
 *
 * determines how many jobs can be run after obtaining current 1 minute
 * load average. since the load obtained from kmeme is an average, this
 * should provide some hysteresis so the server doesn't thrash around
 *-------------------------------------------------------------------------
 */
getrun()
{
	extern int qcount;
	extern int kmem;
	extern long loadaddr;
#ifdef sun
	long load;
	long run;
	extern long loadlevel;
#else
	double load;
	double run;
	extern double loadlevel;
#endif sun
	extern long lseek();

	/*
	 * seek out into kmem (yuck!!!)
	 */
	if (lseek(kmem, loadaddr, L_SET) == -1){
		errlog("lseek error");
		cleanup();
	}

	/*
	 * read the load
	 */
	if (read(kmem, (char *)&load, sizeof(load)) < 0){
		errlog("kmem read error");
		cleanup();
	}

	/*
	 * calculate the number of jobs that can run
	 * (will always overshoot by the fraction)
	 */
	if ((run = loadlevel - load) > 0){
#ifdef sun
		/*
	 	 * sun encodes the load average in a long. It is the
	 	 * load average * 256
	 	 */
		return(1 + (int)(run >> 8));
#else
		return(1 + (int)run);
#endif
	}else
		return(0);
}


/*------------------------------------------------------------------------
 * errlog
 *
 * log the erros into a log. should be small number (hopefully zero!!)
 *------------------------------------------------------------------------
 */
errlog (mess)
char *mess;

{
	struct timeval now;
	struct timezone zone;
	extern char *ctime();
	extern int errorcount;
	extern int errno;
	extern int sys_nerr;
	extern char *sys_errlist[];
	extern FILE *errfile;

	/*
	 * increase the errorcount
	 */
	errorcount = errorcount + 1;

	/*
	 * if called with an arg, print it first
	 */
	if (mess != (char *)0)
		fprintf(errfile,"%s: ", mess);
	/*
	 * if a valid error print the human message
	 */
	if ((errno > 0) && (errno < sys_nerr))
		fprintf(errfile," %s ", sys_errlist[errno]);
	/*
	 * stamp the time of occurance
	 */
	if (gettimeofday(&now, &zone) < 0)
		fprintf(errfile,"errlog cannot get time of day\n");
	else
		fprintf(errfile,"%s", ctime(&(now.tv_sec)));
	(void)fflush(errfile);
}


/*-------------------------------------------------------------------------
 * cleanup
 *
 * the whole system fell apart. close down the sockets log the server
 * termination and exit.
 *-------------------------------------------------------------------------
 */
cleanup()
{
	extern int msgsock;
	extern int cntrlsock;
	extern int errno;
	extern FILE *errfile;

	(void)close(msgsock);
	(void)close(cntrlsock);
	(void)unlink(MSGPATH);
	(void)unlink(CNTRLPATH);
	errno = 0;
	errlog("Server aborting at");
	(void)fclose(errfile);
	exit(1);
}
@//E*O*F server/main.c//
chmod u=r,g=r,o=r server/main.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
	 */
	if (sock > fileno(stdin))
		descsize = sock + 1;
	else
		descsize = fileno(stdin) + 1;

	/*
	 * 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=rw,g=r,o=r control/ipc.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 <<\!!!
     489    2005   13410 main.c
     355    1341    9080 main.c
     178     504    4067 ipc.c
    1022    3850   26557 total
!!!
wc  client/main.c server/main.c control/ipc.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



More information about the Comp.sources.bugs mailing list