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.