v16i026: Public lineprinter spooler package, Part13/16

Rich Salz rsalz at bbn.com
Thu Sep 15 22:16:25 AEST 1988


Submitted-by: papowell at julius.cs.umn.edu
Posting-number: Volume 16, Issue 26
Archive-name: plp/part13

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 13 (of 16)."
# Contents:  src/print_support.c src/recvfiles.c
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'src/print_support.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/print_support.c'\"
else
echo shar: Extracting \"'src/print_support.c'\" \(17575 characters\)
sed "s/^X//" >'src/print_support.c' <<'END_OF_FILE'
X/***************************************************************************
X * U. Minnesota LPD Software * Copyright 1987, 1988, Patrick Powell
X ***************************************************************************
X * MODULE: Print_support.c
X * handle the actual output to the Printer
X ***************************************************************************
X * Revision History: Created Wed Jan 13 16:21:39 CST 1988
X * $Log:	print_support.c,v $
X * Revision 3.1  88/06/18  09:35:18  papowell
X * Version 3.0- Distributed Sat Jun 18 1988
X * 
X * Revision 2.4  88/05/27  08:28:01  papowell
X * Added a SIGCONT to start up the output filter.
X * 
X * Revision 2.3  88/05/19  10:34:19  papowell
X * Fixed open() calls to have a 0 parameter, ie: open(f, perms, 0), where needed
X * 
X * Revision 2.2  88/05/14  10:21:16  papowell
X * Modified -X flag handling
X * 
X * Revision 2.1  88/05/09  10:09:44  papowell
X * PLP: Released Version
X * 
X * Revision 1.4  88/03/25  15:01:04  papowell
X * Debugged Version:
X * 1. Added the PLP control file first transfer
X * 2. Checks for MX during file transfers
X * 3. Found and fixed a mysterious bug involving the SYSLOG facilities;
X * 	apparently they open files and then assume that they will stay
X * 	open.
X * 4. Made sure that stdin, stdout, stderr was available at all times.
X * 
X * Revision 1.3  88/03/11  19:29:04  papowell
X * Minor Changes, Updates
X * 
X * Revision 1.2  88/03/05  15:01:11  papowell
X * Minor Corrections,  Lint Problems
X * 
X * Revision 1.1  88/03/01  11:09:01  papowell
X * Initial revision
X * 
X ***************************************************************************
X * void Print_open(): opens the Printer
X * static int Print_of_fd(): makes an 'of' filter if needed
X * void Print_close(): closes the Printer
X * int Print_ready(): combines Print_open() and Print_of_fd()
X * static int of_stop(): stops the 'of' filter
X * int Print_string( str ): prints a string through 'of' or to Printer
X * int Print_copy( fd ): copies a file through 'of' or to Printer
X * int Print_filter( file, cmd ): makes a filter and copies file
X * int Print_banner(): prints a banner through 'of' or to Printer
X * Note: the above procedures which return values return JSUCC on success,
X *   and JFAIL or JABORT on failure
X ***************************************************************************/
X
X#ifndef lint
static char id_str1[] =
X	"$Header: print_support.c,v 3.1 88/06/18 09:35:18 papowell Exp $ PLP Copyright 1988 Patrick Powell";
X#endif lint
X
X#include "lp.h"
X
X/***************************************************************************
X * Print_open()
X * 	Open the Printer, and set the Print_fd variables
X * 	If the RW printcap flag is set, output is opened RW, otherwise
X * 	opened writeonly in append mode.
X * 
X * 	If the Printer is a tty (i.e.- isatty returns non-zero),
X * 	then the baud rate is set.  The baud rate table is stolen from
X * 	the original LPD code, which stole it from STTY, etc.
X * Side Effect:
X * 	sets the Print_fd variable to a non-zero value
X *  terminates if Printer is unable to be opened
X ****************************************************************************/
struct bauds {
X	char *string;
X	int	baud;
X	int	speed;
X} bauds[] = {
X	"110", 110,	B110,
X	"134", 134,	B134,
X	"150", 150,	B150,
X	"300", 300,	B300,
X	"600", 600,	B600,
X	"1200", 1200,	B1200,
X	"1800", 1800,	B1800,
X	"2400", 2400,	B2400,
X	"4800", 4800,	B4800,
X	"9600", 9600,	B9600,
X	"19200", 19200,	B19200,
X	"38400", 38400,	B38400,
X	(char *)0, 0,	0
X};
X
static int of_pid;		/* OF process pid */
static int of_fd;		/* pipe to OF process */
static int of_running;	/* of filter running */
static char filter_stop[] = "\031\001";	/* what to send filter */
X
Print_open()
X{
X	int err;
X
X	/*
X	 * check to see if it is open
X	 */
X	if( Print_fd ){
X		return;
X	}
X	if( LP == 0 || *LP == 0 ){
X		fatal( XLOG_INFO, "Missing LP value for local Printer");
X	}
X	setstatus("waiting for %s to become ready since %s (offline?)",
X		Printer, Time_str());
X	Print_fd = open(LP, RW ? ( O_RDWR|O_APPEND ) : ( O_WRONLY|O_APPEND ), 0);
X	if( Print_fd < 0 ){
X		err = errno;
X		setstatus("Print_open: '%s' open(%s) failed, %s",Printer,LP,
X			Errormsg(err));
X		errno = err;
X		logerr_die( XLOG_INFO, "Print_open: cannot open %s", LP);
X	} else if( Print_fd == 0 ){
X		fatal( XLOG_INFO, "Print_open: cannot happen- Print_fd == 0");
X	}
X	/*
X	 * if it is a tty, set the baud rates and control information
X	 */
X	if (isatty(Print_fd)){
X		Do_stty(Print_fd);
X	}
X	if(Debug>3)log(XLOG_DEBUG,"Print_open: %s is fd %d", LP, Print_fd );
X}
X
X/*
X * Start up an output filter, if needed.
X */
Print_of_fd()
X{
X	int p[2];
X	char *cmd;
X
X	if( OF == 0 || *OF == 0 || of_pid ){
X		return;
X	}
X	/*
X	 * set OF command line
X	 */
X	cmd = Setup_filter('o',OF);
X	if( cmd == 0 || *cmd == 0 ){
X		fatal(XLOG_INFO, "bad OF entry");
X	}
X	if(Debug>2)log( XLOG_DEBUG, "starting OF, '%s'", cmd);
X	/*
X	 * create pipe and fork
X	 */
X	if( pipe(p) < 0 ){
X		logerr_die( XLOG_NOTICE, "Print_of_of: pipe failed" );
X	}
X	if( p[0] < 3 || p[1] < 3 ){
X		fatal( XLOG_INFO, "Print_of_fd: IMPOSSIBLE- pipe fd %d %d", p[0], p[1]);
X	}
X	if ((of_pid = fork()) == 0) {	/* child */
X		if( dup2(p[0], 0) < 0		/* pipe is std in */
X		 || dup2(Print_fd, 1) < 0 ){		/* Printer is std out */
X			logerr_die( XLOG_NOTICE, "Print_of_fd: dup2 failed" );
X		}
X		if(Debug>3)log( XLOG_DEBUG, "Print_of_fd: forked %d OF: %s",
X			getpid(), cmd );
X		if( geteuid() == 0 && setreuid( Daemon_uid, Daemon_uid ) < 0 ){
X			logerr_die( XLOG_NOTICE, "Print_of_fd: setreuid failed" );
X		}
X		mexecv( cmd );
X		logerr_die( XLOG_NOTICE,"Print_of_fd: execv failed %s", cmd);
X	} else if( of_pid < 0 ){
X		logerr_die( XLOG_NOTICE, "Print_of_fd: fork failed" );
X	}
X	(void)close(p[0]);		/* close input side */
X	of_fd = p[1];
X	of_running = of_pid;
X	if(Debug>3)log(XLOG_DEBUG,"started OF process %d, fd %d", of_pid, of_fd);
X}
X
X/***************************************************************************
X * Print_close()
X * 1. Close the pipes and outputs.
X * 2. Signal the of process that it is to do something.
X *	Note that there is a Reapchild() call that will collect the
X *	children;  this will make sure that there is a minimal number of
X *	outstanding processes active.
X ***************************************************************************/
X
void
Print_close()
X{
X	if( of_fd ){
X		(void)close(of_fd);
X		of_fd = 0;
X	}
X	if( Print_fd ){
X		(void)close(Print_fd);	/* close Printer */
X		Print_fd = 0;
X	}
X	/*
X	 * when you kill the printer, make sure that you do it neatly
X	 */
X	if( of_pid ){
X		(void)kill(of_pid, SIGCONT);
X	}
X	of_pid = 0;
X}
X
X/***************************************************************************
X * Print_ready()
X * 1. open the Printer if neccessary
X * 2. start up the output filter
X * 3. if the filter has been stopped, start it
X * Return: JSUCC if started, JFAIL if not
X ***************************************************************************/
Print_ready()
X{
X	/*
X	 * open the Printer
X	 */
X	Print_open();
X	Print_of_fd();
X	if( of_pid && of_running == 0 ){
X		if (kill(of_pid, SIGCONT) < 0) {
X			logerr( XLOG_INFO,"cannot restart output filter");
X			Print_close();
X			return( JFAIL );
X		}
X		of_running = of_pid;
X	}
X	return( JSUCC );
X}
X
X/***************************************************************************
X * of_stop()
X * stop the output filter if neccessary
X * 1. open and create the filter
X * 2. flush output
X * 3. kill(SIGSTOP) it
X * Return: JSUCC if stopped, JFAIL if not
X ***************************************************************************/
X
int
of_stop()
X{
X	union wait statb;
X	int pid;
X
X	Print_open();
X	Print_of_fd();
X	/*
X	 * stop the OF filter
X	 */
X	if( of_pid && of_running ){
X		if(Debug>3)log( XLOG_DEBUG, "stopping output filter" );
X		if( write( of_fd, filter_stop, strlen(filter_stop))
X			!= strlen(filter_stop) ){
X			logerr( XLOG_NOTICE,"of_stop: cannot write to OF");
X			Print_close();
X			return( JFAIL );
X		}
X		/*
X		 * wait until OF blocks
X		 */
X		if(Debug>3)log( XLOG_DEBUG, "of_stop: waiting for OF %d", of_pid );
X		while ((pid = wait3(&statb,WUNTRACED,(struct rusage *)0)) != -1
X			&& pid != of_pid) ;
X		if( pid < 0 || statb.w_stopval != WSTOPPED ){
X			logerr( XLOG_INFO, "of_stop: OF %d died (%s)", of_pid,
X				Decode_status( &statb ) );
X			Print_close();
X			return( JFAIL );
X		}
X		if(Debug>3)log( XLOG_DEBUG, "of_stop: output filter stopped" );
X		of_running = 0;
X	}
X	return( JSUCC );
X}
X
X/***************************************************************************
X * Print_string( char *str )
X * print a string through a filter 
X * 1. Enable the line Printer
X * 2. get the filter or device fd;
X * 3. put out the string
X * 4. if unsuccessful, close the Printer
X * Return: JSUCC if successful, JFAIL otherwise
X ***************************************************************************/
int
Print_string( str )
X	char *str;
X{
X	int f;
X	int l;
X
X	if( Print_ready() != JSUCC ){
X		return( JFAIL );
X	}
X	if( of_fd ){
X		f = of_fd;
X	} else {
X		f = Print_fd;
X	}
X	l = strlen( str );
X	if( write( f, str, l ) != l ){
X		logerr( XLOG_INFO, "Print_string: write error");
X		Print_close();
X		return( JFAIL );
X	}
X	return( JSUCC );
X}
X
X
X/***************************************************************************
X * Print_copy( int fd )
X * copy a file through a filter
X * 1. ready the Printer
X * 2. copy the file to the appropriate output device
X * 3. if an error, close the Printer
X ***************************************************************************/
int
Print_copy( fd )
X	int fd;
X{
X	int f;
X	long cnt;
X	char buf[BUFSIZ];
X	int in;			/* bytes read */
X	int out;		/* bytes written out */
X
X	cnt = 0;
X	if( Print_ready() != JSUCC ){
X		return( JFAIL );
X	}
X	if( of_fd ){
X		f = of_fd;
X	} else {
X		f = Print_fd;
X	}
X	while( (in = read( fd, buf, sizeof(buf) )) > 0 ){
X		out = write( f, buf, in );
X		if( in != out ){
X			logerr( XLOG_INFO, "Print_copy: write error");
X			Print_close();
X			return( JFAIL );
X		}
X		cnt = cnt + out;
X	}
X	/*
X	 * completed the reading
X	 */
X	if( in < 0 ){
X		logerr( XLOG_INFO, "Print_copy: read error");
X		Print_close();
X		return( JFAIL );
X	}
X	if(Debug>3)log(XLOG_DEBUG,"Print_copy: printed %d bytes", cnt);
X	return( JSUCC );
X}
X
X/***************************************************************************
X *Print_filter( int fd, char *cmd )
X *  spawn a subprocess to do the printing
X * 1. stop the Printer
X * 2. fork a process
X * 3. wait for process to complete
X * 4. restart the process
X * Return: JSUCC if successful, JFAIL otherwise
X ***************************************************************************/
int
Print_filter( file, cmd )
X	int file;
X	char *cmd;
X{
X	int succ;			/* success code */
X	int filter_pid;		/* filter process */
X	int pid;			/* daughter pid */
X	union wait status;	/* daughter status */
X	int err;			/* saved value of errno */
X	
X	/*
X	 * stop the Printer
X	 */
X	succ = of_stop();
X	if( succ != JSUCC ){
X		return( succ );
X	}
X	/*
X	 * fork a process,  and connect file to fd 0, Printer to fd 1.
X	 */
X	if ((filter_pid = fork()) == 0) {	/* daughter */
X		/*
X		 * dup input file to standard in and Printer to stdout
X		 */
X		if( dup2(file, 0)<0 || dup2(Print_fd, 1)<0 ){
X			logerr_die(XLOG_NOTICE,"Print_filter: dup2 failed filter %s",cmd);
X		}
X		mexecv(cmd);
X		logerr_die( XLOG_NOTICE,"Print_filter: cannot execv %s", cmd);
X	} else if( filter_pid < 0 ){
X		logerr( XLOG_NOTICE, "Print_filter: fork failed" );
X		Print_close();
X		return( JFAIL );
X	}
X	/*
X	 *	Wait for filter to complete 
X	 */
X	if(Debug>2)log(XLOG_DEBUG,"Print_filter: waiting for pid %d",filter_pid);
X	while ((pid = wait(&status)) > 0 && pid != filter_pid){
X		if(Debug>3)log( XLOG_DEBUG, "Print_filter:caught %d (%s)",
X			pid, Decode_status(&status) );
X	}
X	err = errno;
X	if(Debug>2)log( XLOG_DEBUG, "Print_filter: filter %d finished (%s)",
X		pid,Decode_status(&status));
X	errno = err;
X	/*
X	 * Check to see how filter terminated
X	 */
X	if( pid < 0 || !WIFEXITED(status)
X		|| (unsigned)status.w_retcode > 1 ){
X		/*
X		 * died for bad reasons, don't run this again
X		 */
X		log(XLOG_INFO,"Print_filter:Filter '%s' Malfunction (%s)",
X			cmd, Decode_status(&status));
X		Print_close();
X		return( JABORT );
X	} else if (status.w_retcode != 0){
X		/*
X		 * try again
X		 */
X		log( XLOG_INFO, "Print_filter:Filter '%s' Retry wanted", cmd );
X		Print_close();
X		return(JFAIL);
X	}
X	return(JSUCC);
X}
X
X/*
X * Print_banner()
X * 1. get the Printer ready
X * 2. call the banner() routine with the correct parameter
X */
Print_banner()
X{
X	int f;
X
X	if(Debug>3)log(XLOG_DEBUG,"Print_banner: printing banner");
X	if( Print_ready() != JSUCC ){
X		return( JFAIL );
X	}
X	if( of_fd ){
X		f = of_fd;
X	} else {
X		f = Print_fd;
X	}
X	if( banner(f) != JSUCC ){
X		if(Debug>3)log(XLOG_DEBUG,"Print_banner: banner failed");
X		Print_close();
X		return( JFAIL );
X	}
X	return( JSUCC );
X}
X
X/*
X * Fri Feb 26 08:44:53 CST 1988 Patrick Powell
X * set terminal modes
X * This code was based on a public domain version of public domain version
X * of stty. I suppose that I could have created if from scratermctrlh,
X * but I have seen the same table appearing
X * in many "public domain" display terminal modes programs.
X */
X
struct tchars termctrl;
struct ltchars linectrl;
struct sgttyb mode;
struct
X{
X	char	*string;
X	int	set;
X	int	reset;
X	int	lset;
X	int	lreset;
X} modes[] = {
X	"bs0",		BS0, BS1, 0, 0,
X	"bs1",		BS1, BS1, 0, 0,
X	"cbreak",	CBREAK, 0, 0, 0,
X	"-cbreak",	0, CBREAK, 0, 0,
X	"cooked",	0, RAW, 0, 0,
X	"cr0",		CR0, CR3, 0, 0,
X	"cr1",		CR1, CR3, 0, 0,
X	"cr2",		CR2, CR3, 0, 0,
X	"cr3",		CR3, CR3, 0, 0,
X	"decctlq",	0, 0, LDECCTQ, 0,
X	"-decctlq",	0, 0, 0, LDECCTQ,
X	"echo",		ECHO, 0, 0, 0,
X	"-echo",	0, ECHO, 0, 0,
X	"even",		EVENP, 0, 0, 0,
X	"-even",	0, EVENP, 0, 0,
X	"ff0",		FF0, FF1, 0, 0,
X	"ff1",		FF1, FF1, 0, 0,
X	"lcase",	LCASE, 0, 0, 0,
X	"-lcase",	0, LCASE, 0, 0,
X	"litout",	0, 0, LLITOUT, 0,
X	"-litout",	0, 0, 0, LLITOUT,
X	"nl",		0, CRMOD, 0, 0,
X	"-nl",		CRMOD, 0, 0, 0,
X	"nl0",		NL0, NL3, 0, 0,
X	"nl1",		NL1, NL3, 0, 0,
X	"nl2",		NL2, NL3, 0, 0,
X	"nl3",		NL3, NL3, 0, 0,
X	"noflsh",	0, 0, LNOFLSH, 0,
X	"-noflsh",	0, 0, 0, LNOFLSH,
X	"nohang",	0, 0, LNOHANG, 0,
X	"-nohang",	0, 0, 0, LNOHANG,
X	"odd",		ODDP, 0, 0, 0,
X	"-odd",		0, ODDP, 0, 0,
X	"raw",		RAW, 0, 0, 0,
X	"-raw",		0, RAW, 0, 0,
X	"tab0",		TAB0, XTABS, 0, 0,
X	"tab1",		TAB1, XTABS, 0, 0,
X	"tab2",		TAB2, XTABS, 0, 0,
X	"tabs",		0, XTABS, 0, 0,
X	"-tabs",	XTABS, 0, 0, 0,
X	"tandem",	TANDEM, 0, 0, 0,
X	"-tandem",	0, TANDEM, 0, 0,
X	"tilde",	0, 0, LTILDE, 0,
X	"-tilde",	0, 0, 0, LTILDE,
X	"tn300",	CR1, ALLDELAY, 0, 0,
X	"tty33",	CR1, ALLDELAY, 0, 0,
X	"tty37",	FF1+CR2+TAB1+NL1, ALLDELAY, 0, 0,
X	"vt05",		NL2, ALLDELAY, 0, 0,
X	0,
X};
X
X
struct	special {
X	char	*name;
X	char	*cp;
X	char	def;
X} special[] = {
X	"stop",		&termctrl.t_stopc,		CSTOP,
X	"start",	&termctrl.t_startc,		CSTART,
X	0
X};
X
Do_stty( fd )
X	int fd;
X{
X	int i;
X	int	localmode;
X	int	linedisc;
X	char buf[BUFSIZ], *bp, *ep, *arg;
X
X	if( ioctl(fd, TIOCGETP, &mode) < 0
X	 || ioctl(fd, TIOCGETC, &termctrl) < 0
X	 || ioctl(fd, TIOCLGET, &localmode) < 0
X	 || ioctl(fd, TIOCGLTC, &linectrl) < 0 ){
X		logerr_die( XLOG_INFO,"cannot get tty parameters");
X	}
X	if(Debug>3)log(XLOG_DEBUG,"stty: before mode 0x%x, lmode 0x%x, speed 0x%x",
X			mode.sg_flags, localmode, mode.sg_ispeed );
X	if( BR ){
X	  for(i=0; bauds[i].baud && BR != bauds[i].baud; i++);
X	  if( i == 0){
X		  fatal(XLOG_INFO,"illegal baud rate %d", BR);
X	  }
X	  mode.sg_ispeed = mode.sg_ospeed = bauds[i].speed;
X	}
X	mode.sg_flags &= ~FC;
X	mode.sg_flags |= FS;
X	localmode &= ~XC;
X	localmode |= XS;
X 
X
X	if( TY && *TY ){
X		(void)strcpy(buf, TY);
X		ep = buf;
X	} else {
X		ep = 0;
X	}
X	while( ep && *ep ){
X		for( ; *ep && isspace(*ep) ; ++ ep );
X		for( arg = ep; *ep && !isspace(*ep) ; ++ ep );
X		if( *ep ){
X			*ep = 0;
X			++ep;
X		}
X		for(i=0; modes[i].string && strcmp(modes[i].string,arg); i++);
X		if(modes[i].string) {
X			if(Debug>4)log(XLOG_DEBUG,
X				"stty: modes %s, mc 0x%x ms 0x%x lc 0x%x ls 0x%x",
X				modes[i].string, modes[i].reset, modes[i].set,
X				modes[i].lreset, modes[i].lset );
X			mode.sg_flags &= ~modes[i].reset;
X			mode.sg_flags |= modes[i].set;
X			localmode &= ~modes[i].lreset;
X			localmode |= modes[i].lset;
X			continue;
X		}
X		for (i = 0; special[i].name && strcmp(special[i].name,arg); i++);
X		if( special[i].name ){
X			for( ; *ep && isspace(*ep) ; ++ ep );
X			for( bp = ep; *ep && !isspace(*ep) ; ++ ep );
X			if( *ep ){
X				*ep = 0;
X				++ep;
X			}
X			if( *bp == 0 ){
X				fatal( XLOG_INFO, "stty: missing parameter for %s", arg );
X			}
X			if (bp[0] == '^'){
X				if( bp[1] == '?' ){
X					*special[i].cp = 0177;
X				} else {
X					*special[i].cp = 037 & bp[1];
X				}
X			} else {
X				*special[i].cp = bp[0];
X			}
X			if(Debug>4)log(XLOG_DEBUG,"stty: special %s %s", arg, bp );
X			continue;
X		}
X		for(i=0; bauds[i].string && strcmp(bauds[i].string,arg); i++);
X		if(bauds[i].string) {
X			if(Debug>4)log(XLOG_DEBUG,"stty: speed %s", arg );
X			mode.sg_ispeed = mode.sg_ospeed = bauds[i].speed;
X			continue;
X		}
X		if (!strcmp("new", arg)){
X			if(Debug>4)log(XLOG_DEBUG,"stty: ldisc %s", arg );
X			linedisc = NTTYDISC;
X			if (ioctl(fd, TIOCSETD, &linedisc)<0)
X				logerr_die(XLOG_INFO,"stty: TIOCSETD ioctl failed");
X			continue;
X		}
X		if (!strcmp("old",arg)){
X			if(Debug>4)log(XLOG_DEBUG,"stty: ldisc %s", arg );
X			linedisc = 0;
X			if (ioctl(fd, TIOCSETD, &linedisc)<0)
X				logerr_die(XLOG_INFO,"stty: TIOCSETD ioctl failed");
X			continue;
X		}
X		fatal(XLOG_INFO,"unknown mode: %s\n", arg);
X	}
X	if(Debug>3)log(XLOG_DEBUG,"stty: after mode 0x%x, lmode 0x%x, speed 0x%x",
X			mode.sg_flags, localmode, mode.sg_ispeed );
X	if( ioctl(fd, TIOCSETN, &mode) < 0
X		|| ioctl(fd, TIOCSETC, &termctrl) < 0
X		|| ioctl(fd, TIOCSLTC, &linectrl) < 0
X		|| ioctl(fd, TIOCLSET, &localmode) < 0 ){
X		logerr_die( XLOG_NOTICE,"cannot set tty parameters");
X	}
X}
END_OF_FILE
if test 17575 -ne `wc -c <'src/print_support.c'`; then
    echo shar: \"'src/print_support.c'\" unpacked with wrong size!
fi
# end of 'src/print_support.c'
fi
if test -f 'src/recvfiles.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/recvfiles.c'\"
else
echo shar: Extracting \"'src/recvfiles.c'\" \(18372 characters\)
sed "s/^X//" >'src/recvfiles.c' <<'END_OF_FILE'
X/***************************************************************************
X * U. Minnesota LPD Software * Copyright 1987, 1988, Patrick Powell
X ***************************************************************************
X * MODULE: recvfiles.c
X * Receive files from a remote site
X ***************************************************************************
X * Revision History: Created Sat Jan 16 07:10:12 CST 1988
X * $Log:	recvfiles.c,v $
X * Revision 3.1  88/06/18  09:35:25  papowell
X * Version 3.0- Distributed Sat Jun 18 1988
X * 
X * Revision 2.4  88/05/21  10:28:14  papowell
X * Minor editing
X * 
X * Revision 2.3  88/05/14  10:18:01  papowell
X * Use long format for job file names;
X * Added 'fd', no forward flag;
X * Control file has to have hostname and origination agree.
X * 
X * Revision 2.2  88/05/11  09:52:55  papowell
X * Remote printer file transfer error fixed.
X * 
X * Revision 2.1  88/05/09  10:09:55  papowell
X * PLP: Released Version
X * 
X * Revision 1.5  88/04/28  09:52:40  papowell
X * added casts to shut up lint
X * 
X * Revision 1.4  88/04/26  15:53:39  papowell
X * Fixed up a horribly silly bug in the add_files and File_name
X * routines;  sigh.  Would you believe an L (l) and a one (1) got mixed
X * up?
X * 
X * Revision 1.3  88/03/25  15:01:17  papowell
X * Debugged Version:
X * 1. Added the PLP control file first transfer
X * 2. Checks for MX during file transfers
X * 3. Found and fixed a mysterious bug involving the SYSLOG facilities;
X * 	apparently they open files and then assume that they will stay
X * 	open.
X * 4. Made sure that stdin, stdout, stderr was available at all times.
X * 
X * Revision 1.2  88/03/05  15:01:07  papowell
X * Minor Corrections,  Lint Problems
X * 
X * Revision 1.1  88/03/01  11:09:06  papowell
X * Initial revision
X * 
X ***************************************************************************/
X#ifndef lint
static char id_str1[] =
X	"$Header: recvfiles.c,v 3.1 88/06/18 09:35:25 papowell Exp $ PLP Copyright 1988 Patrick Powell";
X#endif lint
X
X#include "lp.h"
X
X/***************************************************************************
X * This module implements the file transfer protocol at the receiving
X * site. For each job:
X * 1.  The data files are transferred.
X * 2.  The control files are transferred.
X *     If the transfer is unsuccessful,  all job files are removed.
X * 
X * Individual files are transferred using the following protocol.
X * 
X * 1.  The sending site sends a line with the size and name of the file,
X * 	and a flag indicating if it is a data or control file.
X * 2.  The receiving site will acknowledge reception.
X * 3.  The remote site will send the file.
X * 4.  The receiving site will acknowledge reception.
X * 
X * The transfer protocol is implemented by a simple FSM:
X * INIT:  no files transferred
X * DATA:  data file transferred
X * CONTROL: control file transferred
X * 
X * A list of all files associated with the job is maintained,
X * and if any errors result,  the list of files will be deleted.
X * 
X ***************************************************************************/
X
X/***************************************************************************
X * recvfiles()
X *
X * Gets a job from the remote end.
X * 
X * The old Berkeley protocol was to send data files and then the control
X * file.  The PLP protocol will send the control file first, then the data
X * files, followed by the name of the control file.
X * 
X * If there is an error during transfer, all the jobs associated with the
X * file are removed.
X * 
X * In order to be compatible with the Berkeley LPD, old Berkeley format is
X * also supported.
X ***************************************************************************/
recvfiles()
X{
X	int i;					/* ACME Integers */
X	int succ;				/* success flag */
X	int flag;				/* file kind */
X	long size;				/* file size */
X	char fname[CFNAMELEN+1];	/* file fname */
X	char cfname[CFNAMELEN+1];	/* file fname */
X	char tfname[CFNAMELEN+1];	/* file fname */
X	int fd;					/* file descriptor */
X	FILE *cfp;				/* FILE descriptor */
X	int state;				/* state: IDLE, BDATA, BCNTRL, PCNTRL, PDATA */
X	long jobsize;			/* job size */
X#define IDLE	0	/* nothing doing */
X#define BDATA	1	/* transferring Berkeley data file */
X#define BCNTRL	2	/* transferring Berkeley control file */
X#define PCNTRL	3	/* transferring PLP control file */
X#define PDATA	4	/* transferring PLP data file */
X#define PLAST	5	/* last file has been transferred */
X
X	/*
X	 * get the printcap entry
X	 */
X	if( Get_pc_entry(Printer, Status_pc_vars, Status_pc_len ) == 0){
X		log( XLOG_INFO, "revcfiles: trying to start non-existent Printer" );
X	}
X	if( SD == 0 || *SD == 0 ){
X		/*
X		 * no spooling directory, not a Printer
X		 */
X		if(Debug>0)log(XLOG_DEBUG, "revcfiles: not a Printer");
X		return;
X	}
X	/* chdir to spool directory */
X	if (chdir(SD) < 0) {
X		logerr_die( XLOG_NOTICE,"revcfiles: cannot chdir to %s", SD);
X	}
X	if(Debug>3)log(XLOG_DEBUG,"recvfiles: setting up log");
X	Setuplog( LF, 1 );
X	if(Debug>3)log(XLOG_DEBUG,"recvfiles: sending confirm");
X	if( send_ack(0) != JSUCC ){
X		if(Debug>3)log(XLOG_DEBUG,"recvfiles: confirm failed");
X		goto error;
X	}
X
X	/*
X	 * set up the file transfers
X	 */
X	state = IDLE;
X	while( (succ = get_file_xfer(&flag, &size, fname )) == JSUCC && flag ){
X		if(Debug>3)log(XLOG_DEBUG,"recvfiles: state %d, flag %d, file %s",
X			state,flag,fname);
X		switch( state ){
X		case IDLE:
X			cfp = NULL;
X			Rec_cnt = 0;
X			(void)strcpy( cfname, fname );
X			jobsize = 0;
X			for( i = 0; i < 26; ++i ){
X				CFparm[i][0] = 0;
X			}
X			switch( flag ){
X			case CNAME:
X				state = PCNTRL; break;
X			case DFILE:
X				state = BDATA;	break;
X			default:
X				goto protocol;
X			}
X			break;
X		case BDATA:
X			switch( flag ){
X			case DFILE:
X				break;
X			case CFILE:
X				state = BCNTRL;
X				break;
X			default:
X				goto protocol;
X			}
X			break;
X		case PCNTRL:
X			switch( flag ){
X			case DFILE:
X				state = PDATA;	break;
X			default:
X				goto protocol;
X			}
X			break;
X		case PDATA:
X			switch( flag ){
X			case DFILE:
X				break;
X			case CEND:
X				state = PLAST;
X				break;
X			default:
X				goto protocol;
X			}
X			break;
X		default:
X			goto protocol;
X		}
X		/*
X		 * trying to send a file with a different sequence number?
X		 */
X		if( Job_match( cfname, fname ) == 0 ){
X			log( XLOG_INFO, "recvfiles: file with bad format %s", fname );
X			succ = JFAIL;
X			goto error;
X		}
X		switch( state ){
X		case PDATA:
X			/*
X			 * add names to list
X			 */
X			if( Find_name( fname ) < 0 ){
X				log( XLOG_INFO,
X					"recvfiles: data file not in job '%s'", fname );
X				succ = JFAIL;
X				goto error;
X			}
X			break;
X		case BCNTRL:
X		case PCNTRL:
X			(void)strcpy(tfname, fname );
X			fname[0] = 't';
X			break;
X		}
X		/*
X		 * add the file name to the temporary list
X		 */
X		if( (i = add_recfiles( fname )) < 0 ){
X			log( XLOG_INFO, "recvfiles: too many files (%s)", fname );
X			succ = JFAIL;
X			goto error;
X		}
X		/*
X		 * check to see that file size does not exceed the total
X		 */
X		Parms[i].num = 1;
X		Parms[i].size = ((size+1023)/1024);
X		jobsize = jobsize + Parms[i].size;
X		if( MX && jobsize > MX ){
X			log( XLOG_INFO, "recvfiles: file (%s) exceeds MX",fname, MX);
X			succ = JFAIL;
X			goto error;
X		}
X		/*
X		 * check to see if we have opened the file already
X		 */
X		if( state != PLAST ){
X			/*
X			 * we lock the file
X			 */
X			fd = Exlockcf( fname );
X			if( fd < 0 ){
X				/*
X				 * The file is locked.  This can only happen if some other
X				 * process  got hold of the file.  We either have several
X				 * processes transferring files or horrible collision course.
X				 * DONT remove already transferred files.
X				 */
X				logerr(XLOG_NOTICE,"recvfiles: %s IMPOSSIBLE collision %s",
X					fname,From);
X				exit(1);
X			}
X			succ = get_file( size, fd, fname );
X			if( succ != JSUCC ){
X				goto error;
X			}
X		}
X		/*
X		 * open a FILE for the control file, and check the entries in it.
X		 * If we have just got the file in PLP, just scan for entries.
X		 * Otherwise check to see that all are present.
X		 */
X		switch( state ){
X		case BCNTRL:
X		case PCNTRL:
X			if( (cfp = fdopen( fd, "r" ) ) < 0 ){
X				logerr_die( XLOG_INFO,"recvfiles: fdopen failed" );
X			}
X			if( Validate_cf( state == PCNTRL, cfp, fname ) == 0 ){
X				goto error;
X			}
X			break;
X		case PLAST:
X			if( Validate_cf( 0, cfp, fname ) == 0 ){
X				goto error;
X			}
X			break;
X		}
X		switch( state ){
X		case PLAST:
X		case BCNTRL:
X			Rename_cf( fname );
X			Rec_cnt = 0;
X			(void)fclose(cfp);
X			state = IDLE;
X			break;
X		case PCNTRL:
X			break;
X		default:
X			(void)close( fd );
X			break;
X		}
X		/*
X		 * We can confirm transfer to the other end
X		 */
X		if( send_ack( 0 ) != JSUCC ){
X			goto error;
X		}
X	}
X	if( succ == JSUCC && flag == 0 && state == IDLE ){
X		/*
X		 * finished! start up Printer
X		 */
X		if(Debug>3)log(XLOG_DEBUG,"recvfiles: done, starting Printer");
X		Link_close();
X		Startprinter();
X		return;
X	}
X	/*
X	 * Error conditions
X	 * 1. remove files
X	 * 2. reply with error status to remote end
X	 */
protocol:
X	log(XLOG_INFO,
X	"recvfiles: protocol violation, state %d, flag %d, file %s",
X		state, flag, fname );
error:
X	rm_recfiles();
X	(void)send_ack(1);
X	Startprinter();
X	return;
X}
X
X/***************************************************************************
X * add_recfiles(char * Name)
X * add a file Name to the received files
X ***************************************************************************/
X
add_recfiles( filename )
X	char *filename;
X{
X	int i;
X
X	if(Debug>4)log(XLOG_DEBUG,"add_recfiles: %s", filename );
X	/*
X	 * add a control file name to the parameter list entries
X	 * if there is nothing in the list, clobber the entry.
X	 */
X	if( Rec_cnt ==  0 ){
X		Parmcount = 0;
X	}
X	if( (i = Add_name( filename )) < 0 ){
X		return( -1 );
X	}
X	Rec_cnt = Parmcount;
X	return( i );
X}
X
X/***************************************************************************
X * rm_recfiles()
X * remove the files that are in the receive list;
X ***************************************************************************/
X
rm_recfiles()
X{
X	int i;		/* ACME Integer, Inc. */
X
X	if(Debug>4)log(XLOG_DEBUG,"rm_recfiles: removing files" );
X	for( i = 0; i < Rec_cnt; ++i ){
X		if(Debug>4)log(XLOG_DEBUG,"rm_recfiles: %s", Parms[i].filename );
X		(void)unlink_daemon( Parms[i].filename );
X	}
X	Rec_cnt = 0;
X}
X
X/***************************************************************************
X * get_file_xfer(int *flag; long *size; char *name )
X * 1. read a line from the far end
X * 2. the line has the format <flag><size> <name>\n
X *      "%c%d %s\n" format
X * 3. extract the information from the line
X * 4. acknowledge the information
X ***************************************************************************/
get_file_xfer( flag, size, name )
X	int *flag;
X	long *size;
X	char *name;
X{
X	char buf[BUFSIZ];		/* buffer for reading */
X	int i;					/* ACME Integers, Inc. */
X	char *cp;				/* ACME Pointers, Inc. */
X
X	/*
X	 * read a line from the remote end
X	 * use Bomb-proof Read (bpread), which reads up to the first \n
X	 */
X	if(Debug>3)log(XLOG_DEBUG,"get_file_xfer: starting", buf );
X	if( (i = bpread(1, buf, sizeof(buf)) ) < 0 ){
X		logerr(XLOG_INFO, "get_file_xfer: read error from remote");
X		*flag = 0;
X		goto error;
X	} else if( i == 0 ){
X		if(Debug>3)log(XLOG_DEBUG,"get_file_xfer: end of input");
X		*flag = 0;
X		return( JSUCC );
X	}
X	/*
X	 * UGLY UGLY UGLY:
X	 * what I wanted to do is:
X	 *    if( sscanf( buf, "%c%d %s", flag, size, name ) != 3 )...
X	 * but the guilty-of-incestuous-rape sscanf is not portable across
X	 * several implementations.
X	 */
X	if(Debug>3)log(XLOG_DEBUG,"get_file_xfer: %d'%s'", buf[0], &buf[1]);
X	/*
X	 * pull off the flag information
X	 */
X	i = buf[0];
X	if( i != DFILE && i != CFILE && i != CNAME && i != CEND){
X		log(XLOG_INFO, "get_file_xfer: bad first char (%d)", i);
X		goto error;
X	}
X	*flag = i;
X	/*
X	 * now pull off the size information
X	 */
X	*size = 0;
X	for( cp = &buf[1]; (i = *cp) && isdigit( i ); ++cp ){
X		*size = 10*(*size) + (i - '0');
X	}
X	if( *cp != ' '){
X		log(XLOG_INFO, "get_file_xfer: no space separator (%d)", *cp);
X		goto error;
X	}
X	while( *cp == ' ' ) ++cp;
X	if( Job_match( cp, cp ) == 0 ){
X		log(XLOG_INFO, "get_file_xfer: bad job name '%s'", cp);
X		goto error;
X	}
X	/*
X	 * must be a data or control file only
X	 */
X	(void)strcpy( name, cp );
X	/*
X	 * send a confirmation
X	 */
X	if( send_ack( 0 ) != JSUCC ){
X		goto error;
X	}
X	if(Debug>3)log(XLOG_DEBUG,"get_file_xfer: flag %d, size %d, name '%s'",
X		*flag, *size, name );
X	return( JSUCC );
X	/*
X	 * error, give up
X	 */
error:
X	Link_close();
X	return( JFAIL );
X}
X
X/***************************************************************************
X * get_file(long size; int fd; char *name)
X * 1. copy size bytes from fd 1 to fd
X * 2. the next byte read should be 0; if not, then you have error
X ***************************************************************************/
get_file( size, fd, name )
X	long size;
X	int fd;
X	char *name;
X{
X	char buf[BUFSIZ];		/* buffer for reading */
X	long cnt;				/* number of bytes */
X	int i;					/* ACME Integers, Inc. */
X
X	/*
X	 * we simply copy the file from the remote end
X	 */
X	if(Debug>3)log(XLOG_DEBUG,"get_file: get %d (%s) from %s",size,name,From);
X	cnt = size;
X	while( cnt > 0 ){
X		if( cnt > sizeof(buf) ){
X			i = sizeof(buf);
X		} else {
X			i = cnt;
X		}
X		i = read(1, buf, i);
X		if( i < 0 ){
X			logerr(XLOG_INFO, "get_file: read from %s failed", From );
X			goto error;
X		}
X		if( i == 0 ){
X			log(XLOG_INFO,"get_file: read 0 bytes from %s, assume dead",From);
X			goto error;
X		}
X		if( write( fd, buf, i ) != i ){
X			logerr(XLOG_INFO, "write to %s failed", name );
X			goto error;
X		}
X		cnt = cnt - i;
X	}
X	if(Debug>3)log(XLOG_DEBUG,"get_file: success %d (%s) from %s",
X		size,name,From);
X	i = read(1, buf, 1);
X	if( i < 0 ){
X		logerr(XLOG_INFO, "get_file: end from %s failed", From );
X		goto error;
X	}
X	if( i == 0 ){
X		log(XLOG_INFO, "get_file: end, 0 bytes from %s, assume dead", From );
X		goto error;
X	}
X	if( buf[0] != 0 ){
X		log(XLOG_INFO, "get_file: bad end confirm, %d from %s",buf[0],From);
X		goto error;
X	}
X	if(Debug>3)log(XLOG_DEBUG,"get_file: read %s from %s", name, From );
X	return( JSUCC );
error:
X	Link_close();
X	return( JFAIL );
X}
X
X/***************************************************************************
X * send_ack( int c )
X * acknowledge by sending back single char c
X ***************************************************************************/
send_ack( c )
X	int c;
X{
X	char buf[1];
X
X	buf[0] = c;
X	if( write( 1, buf, 1 ) != 1 ){
X		logerr(XLOG_INFO,"send_ack: %d to %s failed", c, From );
X		return( JFAIL );
X	}
X	if(Debug>3)log(XLOG_DEBUG,"send_ack: %d to %s succeeded", c, From );
X	return( JSUCC );
X}
X
X/***************************************************************************
X * Validate_cf( scan, fp, fname )
X * Check to ensure that the files contained in this control file
X * were actually sent as part of the control file.
X * 1. fseek to the start of the file
X * 2. read the file, looking data file entries
X * 3. Check that the names of the data files are consistent with the
X *    name of the control file
X * 4. Check that the file was sent
X ***************************************************************************/
Validate_cf( scan, fp, fname )
X	int scan;
X	FILE *fp;
X	char *fname;
X{
X	char buf[BUFSIZ];	/* Buffer, Inc. */
X	int c, l;			/* AMCE Aluminum Siding and Integers, Inc. */
X	char *bp;			/* Buffer */
X	long size;			/* total job size */
X	int perms;			/* permissions */
X	int name_len;		/* length of hostname to check */
X
X	if(Debug>6){
X		(void)fprintf( stderr,"Validate_cf: files %d", Parmcount );
X		for( c = 0; c < Parmcount; ++c ){
X			(void)fprintf(stderr,", %d '%s'(%d)",c,
X				Parms[c].filename,Parms[c].num);
X		}
X		(void)fprintf( stderr, "\n" );
X	}
X	size = 0;
X	if( fseek( fp, 0L, 0 ) < 0 ){
X		logerr_die( XLOG_INFO, "Validate_cf: fseek failed '%s'", fname );
X	}
X	while( fgets( buf, sizeof(buf), fp ) ){
X		l = strlen( buf );
X		if( l > MAXPARMLEN ){
X			log( XLOG_INFO, "Validate_cf: '%s' line too long '%s'",fname,buf);
X			return( 0 );
X		}
X		if( buf[l-1] != '\n' ){
X			log( XLOG_INFO, "Validate_cf: '%s', missing \\n", fname );
X			return( 0 );
X		}
X		buf[l-1] = 0;
X		c = *buf;
X		bp = buf+1;
X		if( !isascii(c) || !isalnum(c)){
X			log( XLOG_INFO, "Validate_cf: file %s, bad line'%s'",fname,bp);
X			return( 0 );
X		}
X		/*
X		 * Check to see that data file information is OK
X		 */
X		if( isupper(c) && c != 'L' && c != 'U' ){
X			(void)strcpy( CFparm[c-'A'], bp );
X		}
X		if( islower(c) || c == 'U' ){
X			/*
X			 * check to see that the file is in the list
X			 */
X			if( Job_match( fname, bp ) == 0 ){
X				log( XLOG_INFO,"Validate_cf: file %s, bad data file '%s'",
X					fname,bp);
X				return( 0 );
X			}
X			/*
X			 * if we are scanning, just enter name in list, otherwise
X			 * check to see that file is in the Parms[] list and that the
X			 * total job size is within limits
X			 */
X			if( scan ){
X				if( Add_name( bp ) < 0 ){
X					log( XLOG_INFO,"Validate_cf: too many files in job %s (%s)",
X						fname,bp);
X					return( 0 );
X				}
X			} else if( (l = Find_name( bp )) < 0 ){
X				log(XLOG_INFO,
X					"Validate_cf: file %s, data file '%s' not in list",
X					fname,bp);
X				return( 0 );
X			} else if( Parms[l].num == 0 ){
X				log( XLOG_INFO,
X					"Validate_cf: file %s, data file '%s' not transferred",
X					fname,bp);
X				return( 0 );
X			} else if( islower( c ) ){
X				size = size + Parms[l].size;
X				if( MX && size > MX ){
X					log( XLOG_INFO,"Validate_cf: job %s too large", fname);
X					return( 0 );
X				}
X			}
X		}
X	}
X	/*
X	 * check to see if the remote submitter has valid authorizations
X	 */
X	if( LOGNAME[0] == 0 ){
X		log( XLOG_INFO,"Validate_cf: job %s missing username", fname);
X		return( 0 );
X	}
X	/*
X	 * check for long or short name
X	 */
X	if( LH ){
X		name_len = strlen( From );
X	} else {
X		name_len = strlen( &fname[STARTFR] );
X	}
X	if( strncmp( FROMHOST, &fname[STARTFR], name_len ) ){
X		log( XLOG_INFO,"Validate_cf: bad filename '%s' FROMHOST '%s'",
X			fname, FROMHOST);
X		return( 0 );
X	}
X	/*
X	 * check to see if you will accept forwarded files
X	 * if FD is set, then no forwarded files allowed
X	 */
X	if( FD && strcmp( FROMHOST, From ) ){
X		log( XLOG_INFO,"Validate_cf: forwarded job '%s' FROMHOST '%s'",
X			fname, FROMHOST);
X		return( 0 );
X	}
X	perms = 'R';
X	if((Permfile && *Permfile &&
X			!Checkperm(Permfile,FROMHOST,LOGNAME,First_name,&perms,(int *)0,0))
X       ||(XU && *XU &&
X			!Checkperm(XU,FROMHOST,LOGNAME,First_name,&perms,(int *)0,0 ) )){
X		log(XLOG_INFO, "Validate_cf: %s@%s cannot use '%s'",
X			LOGNAME, FROMHOST, First_name );
X			return( 0 );
X	}
X	return( 1 );
X}
END_OF_FILE
if test 18372 -ne `wc -c <'src/recvfiles.c'`; then
    echo shar: \"'src/recvfiles.c'\" unpacked with wrong size!
fi
# end of 'src/recvfiles.c'
fi
echo shar: End of archive 13 \(of 16\).
cp /dev/null ark13isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 16 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

-- 
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.



More information about the Comp.sources.unix mailing list