[comp.sources.unix] v15i009: Wrapper for System V lp, bug work-around

rsalz@uunet.uu.net (Rich Salz) (05/26/88)

Submitted-by: utgpu!utfyzx!harrison (David Harrison)
Posting-number: Volume 15, Issue 9
Archive-name: lp-onionskin

Here is a small program of use only for systems with the System V 
semaphore facility.  It works around a bug in the lockfile mechanism 
of lp(1) that causes the spooler to periodically crash if it gets 
2 or more jobs at almost the same time.  The README file discusses 
further.  To do the job right would nuke the lockfiles in lp(1)
in favour of the System V ipcs facility, but that requires source
(which I have) and time (which I don't have), so this is just
an onionskin around lp(1).
    David Harrison, Dept. of Physics, Univ. of Toronto
    {ihnp4,utzoo}!utgpu!utfyzx!harrison

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	Makefile
#	README
#	lp.c
#	lock.c
export PATH; PATH=/bin:$PATH
echo shar: extracting "'Makefile'" '(435 characters)'
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
sed 's/^X//' << \SHAR_EOF > 'Makefile'
X#	@(#)Makefile	1.1 U of T Physics 2/24/88
X
XSRC = lp.c lock.c
XOBJS = lp.o lock.o
XBIN = /usr/bin
XLP = lp -s
XLINTFLAGS = -DLINT
X
X# for symbolic debugger
X# CFLAGS = -g
X# CDBOBJ = /usr/lib/end.o
X
Xlptry:	$(OBJS)
X	cc -o lptry $(OBJS) $(CDBOBJ)
X
Xinstall:	lptry
X	strip lptry
X	mv $(BIN)/lp $(BIN)/real_lp
X	cpset lptry $(BIN) 0755
X
Xlint:	
X	lint $(LINTFLAGS) $(SRC)
X
Xclean:
X	/bin/rm -f *.o core
X
Xprint:	Makefile $(SRC)
X	pr $? | $(LP)
X	touch print
SHAR_EOF
if test 435 -ne "`wc -c < 'Makefile'`"
then
	echo shar: error transmitting "'Makefile'" '(should have been 435 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'README'" '(1519 characters)'
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
sed 's/^X//' << \SHAR_EOF > 'README'
XThe standard lp(1) spooler in System V uses a variety of
Xlockfiles in processing a job.  This leaves a window of
Xvulnerability if two jobs are received nearly simultaneously:
X
X	lp job 1 checks to see if there is a lockfile,
X		finds there isn't.
X	lp job 2 checks to see if there is a lockfile,
X		finds there isn't
X	lp job 1 establishes the lockfile
X	lp job 2 tries to establish the lockfile and fails.
X
XWhen this happens, the spooler croaks.  Often the symptom is
Xthat jobs start piling up for printing but none ever are,
Xsometimes it is more gory than that.  The fix is sometimes 
Xto disable(1) the printer and then enable(1) it.  Sometimes 
Xthe only fix is to cancel(1) the pending jobs.
X
XThis small program is another fix.  It moves /usr/bin/lp to
X/usr/bin/real_lp, and subsitutes this program as /usr/bin/lp.
XThus, you must be root to install the program.
X
XEssentially all this program does is throw up a semaphore,
Xexecs real_lp, and lowers the semaphore.
X
XWe have been running this for two months without any failures
Xor problems on an HP9000 Series 500 running Release 5.11 of
XHP-UX (System V.2).  Because our application involves up to
X200 one-page prints per day from 20 scattered users, prior to 
Xits installation our spooler was hanging every couple of days 
Xor so average with peaks of 2 or 3 hangs a day.
X
XYou may want to check ipcs(1) and ipcrm(1) if you are unfamiliar
Xwith the System V semaphore facility.
X--
X    David Harrison, Dept. of Physics, Univ. of Toronto
X    {ihnp4,utzoo}!utgpu!utfyzx!harrison
SHAR_EOF
if test 1519 -ne "`wc -c < 'README'`"
then
	echo shar: error transmitting "'README'" '(should have been 1519 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'lp.c'" '(2324 characters)'
if test -f 'lp.c'
then
	echo shar: will not over-write existing file "'lp.c'"
else
sed 's/^X//' << \SHAR_EOF > 'lp.c'
X#ifndef LINT
Xstatic char SccsId[] = "@(#)lp.c	1.2 U of T Physics 12/16/87";
X#endif
X
X/*
X * This is an onionskin around the lp spooler.
X * The problem is that the standard lp(1) uses lockfiles, with
X * the well-known window of vulnerability between testing for
X * the existence of the file and trying to create it.
X * Thus, we have moved the standard lp command to "real_lp",
X * and this program becomes "lp".
X * It uses a semaphore to lock and unlock jobs sent to lp.
X *		David Harrison - Dec/87
X */
X
X#include <stdio.h>
X#include <signal.h>
X
Xchar *real_lp_cmd = "real_lp";
X
Xchar *progname;
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X	void _exit(), perror();
X	char *emalloc();
X	char *strcpy(), *strcat();
X	char **exec_argv;
X	int pid, w, status;
X	int (*istat)(), (*qstat)();
X	int i;
X
X	progname = argv[0];
X
X	/*
X	 * put arguments from argv into exec_argv, terminating
X	 * with a null.
X	 *
X	 * First malloc the array size needed ( with one for the NULL
X	 * and the end).
X	 */
X	exec_argv = 
X	    (char **) emalloc( (unsigned) ((argc + 1) * sizeof (char *) ));
X	/*
X	 * Put real_lp_cmd in argv[0]
X	 */
X	exec_argv[0] = emalloc( (unsigned) (strlen(real_lp_cmd) + 1) );
X	(void) strcpy(exec_argv[0], real_lp_cmd);
X	/*
X	 * Now the other argements
X	 */
X	if( argc > 1) {
X		for(i = 1; i < argc; i++) {
X		    exec_argv[i] = emalloc( (unsigned) (strlen(argv[i]) + 1 ));
X		    (void) strcpy(exec_argv[i], argv[i]);
X		}
X	}
X	/*
X	 * Terminate with a NULL
X	 */
X	exec_argv[argc] = NULL;
X
X	lockproc();
X
X	switch( (pid = fork()) ) {
X
X		case -1:
X			unlockproc();
X			Sys_Error("Can't fork\n");
X			break;
X
X		case 0:
X			execvp( real_lp_cmd , exec_argv );
X			unlockproc();
X			fprintf(stderr,"%s: can't exec %s\n", 
X				progname, real_lp_cmd );
X			perror("exec");
X			_exit(127);
X
X		default:
X			break;
X			
X	}
X
X	/*
X	 * Trap signals and wait for child to finish.
X	 */
X	istat = signal(SIGINT, SIG_IGN);
X	qstat = signal(SIGQUIT, SIG_IGN);
X	while( (w = wait(&status)) != pid && w != -1)
X		;
X	if( w == -1)
X		status = -1;
X	signal(SIGINT, istat);
X	signal(SIGQUIT, qstat);
X
X	unlockproc();
X			
X	return status;
X
X}
X
Xchar *
Xemalloc(n)
Xunsigned n;
X{
X	char *p, *malloc();
X
X	p = malloc(n);
X	if ( p == 0)
X		Sys_Error("Out of memory");
X	return p;
X}
X
XSys_Error(s)
Xchar *s;
X{
X	void exit(), perror();
X
X	fprintf(stderr,"%s: %s\n",progname, s);
X	perror("terminating");
X	exit(1);
X
X}
SHAR_EOF
if test 2324 -ne "`wc -c < 'lp.c'`"
then
	echo shar: error transmitting "'lp.c'" '(should have been 2324 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'lock.c'" '(2776 characters)'
if test -f 'lock.c'
then
	echo shar: will not over-write existing file "'lock.c'"
else
sed 's/^X//' << \SHAR_EOF > 'lock.c'
X#ifndef LINT
Xstatic char SccsId[] = "@(#)lock.c	1.1 U of T Physics 12/15/87";
X#endif
X
X/*
X * Standard lp creates lockfiles to suspend a second request.
X * This leaves a small window of vulnerability if 2 users
X * are using the program at one time.
X *
X * This file contains:
X *	lockproc() - which locks so other user can use
X *			the program.
X *	unlockproc() - which unlocks for other users.
X *
X * It uses the semaphore facility and is highly system dependent.
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/ipc.h>
X#include <sys/sem.h>
X#include <errno.h>
X
X/*
X * The following are the way to get a key out of ftok()
X */
X#define PATH 	"/usr/bin/real_lp"
X#define ID	'a'
X
X#define UNLOCK	(1)
X#define LOCK	(-1)	/* impossible, so will lock */
X
X#define MODE	(0666)  /* rw by the world */
X
X/*
X * If the owner removes the facility while a 2nd process is 
X * waiting to lock it, the second process will receive an
X * error from semop(2).  Thus, we try TRIES times to lock
X * the process.
X */
X#define TRIES	5	
X
X#define YES	1
X#define NO	0
X
Xstatic int sid;		/* semaphore id number */
Xstatic short creator;	/* == YES if this process created */
X
Xextern char *progname;
X
Xlockproc()
X{
X	int sem_flg, numbad;
X	key_t key, ftok();
X	struct sembuf sb;
X
X	numbad = 0;
X
Xretry:
X
X	if((key = ftok(PATH,ID)) == -1) 
X		Lock_Error("Cannot get ipc key");
X
X
X	errno = 0;
X	creator = NO;
X
X	if(numbad >= TRIES) {
X		if(creator == YES)
X			semctl(sid, IPC_RMID, 0);
X		Lock_Error("Lock error");
X	}
X
X	sem_flg = MODE | IPC_CREAT | IPC_EXCL;
X
X	sid = semget(key, 1, sem_flg);
X	if(sid == -1 && errno == EEXIST) {
X		/*
X		 * In use by another user.
X		 */
X		sem_flg = MODE;
X		(void) fflush(stdout);
X		sid = semget(key, 1, sem_flg);
X		/* it will get here, but then block */
X	} else {
X		creator = YES;
X		if(semctl(sid, 0, SETVAL, UNLOCK) == -1) {
X			semctl(sid, 0, IPC_RMID, 0);
X			Lock_Error("Cannot create semaphore");
X		}
X	}
X
X	sb.sem_num = 0;		/* 1st semaphore */
X	sb.sem_op = LOCK;	/* we are locking the semaphore */
X	sb.sem_flg = SEM_UNDO;	/* auto reverse */
X
X	if (semop(sid, &sb, 1) == -1) {
X		if( errno == EINTR || errno == EIDRM) {
X			numbad++;
X			goto retry;
X		} else {
X			Lock_Error("Cannot semop()");
X		}
X	}
X}
X
Xunlockproc()
X{
X	struct sembuf sb;
X
X	if(creator == YES) {
X		if(semctl(sid, IPC_RMID, 0) != 0) {
X			Lock_Error("Cannot remove lock");
X		}
X	} else {
X		sb.sem_num = 0;
X		sb.sem_op = UNLOCK;
X		sb.sem_flg = SEM_UNDO;
X
X		if(semop(sid, &sb, 1) == -1)
X			Lock_Error("Cannot unlock");
X	}
X}
X
XLock_Error(s)
Xchar *s;
X{
X	void exit();
X	extern char *progname;
X
X	fprintf(stderr,"%s: please notify your system administrator\n",
X		progname);
X	fprintf(stderr,"that you received the following error message:\n");
X	fprintf(stderr," ***** %s *****\n", s);
X	fprintf(stderr,"Your print job has aborted.\n");
X	exit(1);
X}
SHAR_EOF
if test 2776 -ne "`wc -c < 'lock.c'`"
then
	echo shar: error transmitting "'lock.c'" '(should have been 2776 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.