* Return -1 if a non-recoverable error occured, 1 if a recoverable error and
* Return -1 if a non-recoverable error occurred, 1 if a recoverable error and
static char      *scnline __P((int, char *, int));static int        sendfile __P((int, char *));static int        sendit __P((char *));static void       sendmail __P((char *, int));static void       setty __P((void));voidprintjob(){	struct stat stb;	struct queue *q, **qp;	struct queue **queue;	int i, nitems;	off_t pidoff;	int errcnt, count = 0;	init();					/* set up capabilities */	(void) write(1, "", 1);			/* ack that daemon is started */	(void) close(2);			/* set up log file */	if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) {		syslog(LOG_ERR, "%s: %m", LF);		(void) open(_PATH_DEVNULL, O_WRONLY);	}	setgid(getegid());	pid = getpid();				/* for use with lprm */	setpgrp(0, pid);	signal(SIGHUP, abortpr);	signal(SIGINT, abortpr);	signal(SIGQUIT, abortpr);	signal(SIGTERM, abortpr);	(void) mktemp(tempfile);	/*	 * uses short form file names	 */	if (chdir(SD) < 0) {		syslog(LOG_ERR, "%s: %m", SD);		exit(1);	}	if (stat(LO, &stb) == 0 && (stb.st_mode & 0100))		exit(0);		/* printing disabled */	lfd = open(LO, O_WRONLY|O_CREAT, 0644);	if (lfd < 0) {		syslog(LOG_ERR, "%s: %s: %m", printer, LO);		exit(1);	}	if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {		if (errno == EWOULDBLOCK)	/* active deamon present */			exit(0);		syslog(LOG_ERR, "%s: %s: %m", printer, LO);		exit(1);	}	ftruncate(lfd, 0);	/*	 * write process id for others to know	 */	sprintf(line, "%d\n", pid);	pidoff = i = strlen(line);	if (write(lfd, line, i) != i) {		syslog(LOG_ERR, "%s: %s: %m", printer, LO);		exit(1);	}	/*	 * search the spool directory for work and sort by queue order.	 */	if ((nitems = getq(&queue)) < 0) {		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);		exit(1);	}	if (nitems == 0)		/* no work to do */		exit(0);	if (stb.st_mode & 01) {		/* reset queue flag */		if (fchmod(lfd, stb.st_mode & 0776) < 0)			syslog(LOG_ERR, "%s: %s: %m", printer, LO);	}	openpr();			/* open printer or remote */again:	/*	 * we found something to do now do it --	 *    write the name of the current control file into the lock file	 *    so the spool queue program can tell what we're working on	 */	for (qp = queue; nitems--; free((char *) q)) {		q = *qp++;		if (stat(q->q_name, &stb) < 0)			continue;		errcnt = 0;	restart:		(void) lseek(lfd, pidoff, 0);		(void) snprintf(line, sizeof line, "%s\n", q->q_name);		i = strlen(line);		if (write(lfd, line, i) != i)			syslog(LOG_ERR, "%s: %s: %m", printer, LO);		if (!remote)			i = printit(q->q_name);		else			i = sendit(q->q_name);		/*		 * Check to see if we are supposed to stop printing or		 * if we are to rebuild the queue.		 */		if (fstat(lfd, &stb) == 0) {			/* stop printing before starting next job? */			if (stb.st_mode & 0100)				goto done;			/* rebuild queue (after lpc topq) */			if (stb.st_mode & 01) {				for (free((char *) q); nitems--; free((char *) q))					q = *qp++;				if (fchmod(lfd, stb.st_mode & 0776) < 0)					syslog(LOG_WARNING, "%s: %s: %m",						printer, LO);				break;			}		}		if (i == OK)		/* file ok and printed */			count++;		else if (i == REPRINT && ++errcnt < 5) {			/* try reprinting the job */			syslog(LOG_INFO, "restarting %s", printer);			if (ofilter > 0) {				kill(ofilter, SIGCONT);	/* to be sure */				(void) close(ofd);				while ((i = wait(NULL)) > 0 && i != ofilter)					;				ofilter = 0;			}			(void) close(pfd);	/* close printer */			if (ftruncate(lfd, pidoff) < 0)				syslog(LOG_WARNING, "%s: %s: %m", printer, LO);			openpr();		/* try to reopen printer */			goto restart;		} else {			syslog(LOG_WARNING, "%s: job could not be %s (%s)", printer,				remote ? "sent to remote host" : "printed", q->q_name);			if (i == REPRINT) {				/* ensure we don't attempt this job again */				(void) unlink(q->q_name);				q->q_name[0] = 'd';				(void) unlink(q->q_name);				if (logname[0])					sendmail(logname, FATALERR);			}		}	}	free((char *) queue);	/*	 * search the spool directory for more work.	 */	if ((nitems = getq(&queue)) < 0) {		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);		exit(1);	}	if (nitems == 0) {		/* no more work to do */	done:		if (count > 0) {	/* Files actually printed */			if (!SF && !tof)				(void) write(ofd, FF, strlen(FF));			if (TR != NULL)		/* output trailer */				(void) write(ofd, TR, strlen(TR));		}		(void) close(ofd);		(void) wait(NULL);		(void) unlink(tempfile);		exit(0);	}	goto again;}char	fonts[4][50];	/* fonts for troff */char ifonts[4][40] = {	_PATH_VFONTR,	_PATH_VFONTI,	_PATH_VFONTB,	_PATH_VFONTS,};/* * The remaining part is the reading of the control file (cf) * and performing the various actions. */static intprintit(file)	char *file;{	int i;	char *cp;	int bombed = OK;	/*	 * open control file; ignore if no longer there.	 */	if ((cfp = fopen(file, "r")) == NULL) {		syslog(LOG_INFO, "%s: %s: %m", printer, file);		return(OK);	}	/*	 * Reset troff fonts.	 */	for (i = 0; i < 4; i++)		strcpy(fonts[i], ifonts[i]);	sprintf(&width[2], "%ld", PW);	strcpy(indent+2, "0");	/*	 *      read the control file for work to do	 *	 *      file format -- first character in the line is a command	 *      rest of the line is the argument.	 *      valid commands are:	 *	 *		S -- "stat info" for symbolic link protection	 *		J -- "job name" on banner page	 *		C -- "class name" on banner page	 *              L -- "literal" user's name to print on banner	 *		T -- "title" for pr	 *		H -- "host name" of machine where lpr was done	 *              P -- "person" user's login name	 *              I -- "indent" amount to indent output	 *		R -- laser dpi "resolution"	 *              f -- "file name" name of text file to print	 *		l -- "file name" text file with control chars	 *		p -- "file name" text file to print with pr(1)	 *		t -- "file name" troff(1) file to print	 *		n -- "file name" ditroff(1) file to print	 *		d -- "file name" dvi file to print	 *		g -- "file name" plot(1G) file to print	 *		v -- "file name" plain raster file to print	 *		c -- "file name" cifplot file to print	 *		1 -- "R font file" for troff	 *		2 -- "I font file" for troff	 *		3 -- "B font file" for troff	 *		4 -- "S font file" for troff	 *		N -- "name" of file (used by lpq)	 *              U -- "unlink" name of file to remove	 *                    (after we print it. (Pass 2 only)).	 *		M -- "mail" to user when done printing	 *	 *      getline reads a line and expands tabs to blanks	 */	/* pass 1 */	while (getline(cfp))		switch (line[0]) {		case 'H':			strlcpy(fromhost, line+1, sizeof(fromhost));			if (class[0] == '\0') {				strlcpy(class, line+1, sizeof(class));			}			continue;		case 'P':			strlcpy(logname, line+1, sizeof(logname));			if (RS) {			/* restricted */				if (getpwnam(logname) == NULL) {					bombed = NOACCT;					sendmail(line+1, bombed);					goto pass2;				}			}			continue;		case 'S':			cp = line+1;			i = 0;			while (*cp >= '0' && *cp <= '9')				i = i * 10 + (*cp++ - '0');			fdev = i;			cp++;			i = 0;			while (*cp >= '0' && *cp <= '9')				i = i * 10 + (*cp++ - '0');			fino = i;			continue;		case 'J':			if (line[1] != '\0') {				strlcpy(jobname, line+1, sizeof(jobname));			} else				strcpy(jobname, " ");			continue;		case 'C':			if (line[1] != '\0')				strlcpy(class, line+1, sizeof(class));			else if (class[0] == '\0')				gethostname(class, sizeof(class));			continue;		case 'T':	/* header title for pr */			strlcpy(title, line+1, sizeof(title));			continue;		case 'L':	/* identification line */			if (!SH && !HL)				banner(line+1, jobname);			continue;		case '1':	/* troff fonts */		case '2':		case '3':		case '4':			if (line[1] != '\0') {				strlcpy(fonts[line[0]-'1'], line+1,				    50);			}			continue;		case 'W':	/* page width */			strlcpy(width+2, line+1, sizeof(width)-2);			continue;		case 'I':	/* indent amount */			strlcpy(indent+2, line+1, sizeof(indent)-2);			continue;		default:	/* some file to print */			switch (i = print(line[0], line+1)) {			case ERROR:				if (bombed == OK)					bombed = FATALERR;				break;			case REPRINT:				(void) fclose(cfp);				return(REPRINT);			case FILTERERR:			case ACCESS:				bombed = i;				sendmail(logname, bombed);			}			title[0] = '\0';			continue;		case 'N':		case 'U':		case 'M':		case 'R':			continue;		}	/* pass 2 */pass2:	fseek(cfp, 0L, 0);	while (getline(cfp))		switch (line[0]) {		case 'L':	/* identification line */			if (!SH && HL)				banner(line+1, jobname);			continue;		case 'M':			if (bombed < NOACCT)	/* already sent if >= NOACCT */				sendmail(line+1, bombed);			continue;		case 'U':			if (strchr(line+1, '/'))				continue;			(void) unlink(line+1);		}	/*	 * clean-up in case another control file exists	 */	(void) fclose(cfp);	(void) unlink(file);	return(bombed == OK ? OK : ERROR);}/* * Print a file. * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}. * Return -1 if a non-recoverable error occured, * 2 if the filter detected some errors (but printed the job anyway), * 1 if we should try to reprint this job and * 0 if all is well. * Note: all filters take stdin as the file, stdout as the printer, * stderr as the log file, and must not ignore SIGINT. */static intprint(format, file)	int format;	char *file;{	int n;	char *prog;	int fi, fo;	FILE *fp;	char *av[15], buf[BUFSIZ];	int pid, p[2], stopped = 0, nofile;	union wait status;	struct stat stb;	if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0)		return(ERROR);	/*	 * Check to see if data file is a symbolic link. If so, it should	 * still point to the same file or someone is trying to print	 * something he shouldn't.	 */	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&	    (stb.st_dev != fdev || stb.st_ino != fino))		return(ACCESS);	if (!SF && !tof) {		/* start on a fresh page */		(void) write(ofd, FF, strlen(FF));		tof = 1;	}	if (IF == NULL && (format == 'f' || format == 'l')) {		tof = 0;		while ((n = read(fi, buf, BUFSIZ)) > 0)			if (write(ofd, buf, n) != n) {				(void) close(fi);				return(REPRINT);			}		(void) close(fi);		return(OK);	}	switch (format) {	case 'p':	/* print file using 'pr' */		if (IF == NULL) {	/* use output filter */			prog = _PATH_PR;			av[0] = "pr";			av[1] = width;			av[2] = length;			av[3] = "-h";			av[4] = *title ? title : " ";			av[5] = 0;			fo = ofd;			goto start;		}		pipe(p);		if ((prchild = dofork(DORETURN)) == 0) {	/* child */			dup2(fi, 0);		/* file is stdin */			dup2(p[1], 1);		/* pipe is stdout */			closelog();			for (n = 3, nofile = sysconf(_SC_OPEN_MAX); n < nofile; n++)				(void) close(n);			execl(_PATH_PR, "pr", width, length,			    "-h", *title ? title : " ", (char *)NULL);			syslog(LOG_ERR, "cannot execl %s", _PATH_PR);			exit(2);		}		(void) close(p[1]);		/* close output side */		(void) close(fi);		if (prchild < 0) {			prchild = 0;			(void) close(p[0]);			return(ERROR);		}		fi = p[0];			/* use pipe for input */	case 'f':	/* print plain text file */		prog = IF;		av[1] = width;		av[2] = length;		av[3] = indent;		n = 4;		break;	case 'l':	/* like 'f' but pass control characters */		prog = IF;		av[1] = "-c";		av[2] = width;		av[3] = length;		av[4] = indent;		n = 5;		break;	case 'r':	/* print a fortran text file */		prog = RF;		av[1] = width;		av[2] = length;		n = 3;		break;	case 't':	/* print troff output */	case 'n':	/* print ditroff output */	case 'd':	/* print tex output */		(void) unlink(".railmag");		if ((fo = creat(".railmag", FILMOD)) < 0) {			syslog(LOG_ERR, "%s: cannot create .railmag", printer);			(void) unlink(".railmag");		} else {			for (n = 0; n < 4; n++) {				if (fonts[n][0] != '/')					(void) write(fo, _PATH_VFONT,					    sizeof(_PATH_VFONT) - 1);				(void) write(fo, fonts[n], strlen(fonts[n]));				(void) write(fo, "\n", 1);			}			(void) close(fo);		}		prog = (format == 't') ? TF : (format == 'n') ? NF : DF;		av[1] = pxwidth;		av[2] = pxlength;		n = 3;		break;	case 'c':	/* print cifplot output */		prog = CF;		av[1] = pxwidth;		av[2] = pxlength;		n = 3;		break;	case 'g':	/* print plot(1G) output */		prog = GF;		av[1] = pxwidth;		av[2] = pxlength;		n = 3;		break;	case 'v':	/* print raster output */		prog = VF;		av[1] = pxwidth;		av[2] = pxlength;		n = 3;		break;	default:		(void) close(fi);		syslog(LOG_ERR, "%s: illegal format character '%c'",			printer, format);		return(ERROR);	}	if (prog == NULL) {		(void) close(fi);		syslog(LOG_ERR,		   "%s: no filter found in printcap for format character '%c'",		   printer, format);		return(ERROR);	}	if ((av[0] = strrchr(prog, '/')) != NULL)		av[0]++;	else		av[0] = prog;	av[n++] = "-n";	av[n++] = logname;	av[n++] = "-h";	av[n++] = fromhost;	av[n++] = AF;	av[n] = 0;	fo = pfd;	if (ofilter > 0) {		/* stop output filter */		write(ofd, "\031\1", 2);		while ((pid = waitpid((pid_t)-1, (int *)&status, WUNTRACED)) > 0		    && pid != ofilter)			;		if (status.w_stopval != WSTOPPED) {			(void) close(fi);			syslog(LOG_WARNING,			    "%s: output filter died (retcode=%d termsig=%d)",			    printer, status.w_retcode, status.w_termsig);			return(REPRINT);		}		stopped++;	}start:	if ((child = dofork(DORETURN)) == 0) {	/* child */		dup2(fi, 0);		dup2(fo, 1);		n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);		if (n >= 0)			dup2(n, 2);		closelog();		for (n = 3, nofile = sysconf(_SC_OPEN_MAX); n < nofile; n++)			(void) close(n);		execv(prog, av);		syslog(LOG_ERR, "cannot execv %s", prog);		exit(2);	}	(void) close(fi);	if (child < 0)		status.w_retcode = 100;	else		while ((pid = wait((int *)&status)) > 0 && pid != child)			;	child = 0;	prchild = 0;	if (stopped) {		/* restart output filter */		if (kill(ofilter, SIGCONT) < 0) {			syslog(LOG_ERR, "cannot restart output filter");			exit(1);		}	}	tof = 0;	/* Copy filter output to "lf" logfile */	if ((fp = fopen(tempfile, "r"))) {		while (fgets(buf, sizeof(buf), fp))			fputs(buf, stderr);		fclose(fp);	}	if (!WIFEXITED(status)) {		syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)",			printer, format, status.w_termsig);		return(ERROR);	}	switch (status.w_retcode) {	case 0:		tof = 1;		return(OK);	case 1:		return(REPRINT);	case 2:		return(ERROR);	default:		syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)",			printer, format, status.w_retcode);		return(FILTERERR);	}}/* * Send the daemon control file (cf) and any data files. * Return -1 if a non-recoverable error occured, 1 if a recoverable error and * 0 if all is well. */static intsendit(file)	char *file;{	int i, err = OK;	char *cp, last[BUFSIZ];	/*	 * open control file	 */	if ((cfp = fopen(file, "r")) == NULL)		return(OK);	/*	 *      read the control file for work to do	 *	 *      file format -- first character in the line is a command	 *      rest of the line is the argument.	 *      commands of interest are:	 *	 *            a-z -- "file name" name of file to print	 *              U -- "unlink" name of file to remove	 *                    (after we print it. (Pass 2 only)).	 */	/*	 * pass 1	 */	while (getline(cfp)) {	again:		if (line[0] == 'S') {			cp = line+1;			i = 0;			while (*cp >= '0' && *cp <= '9')				i = i * 10 + (*cp++ - '0');			fdev = i;			cp++;			i = 0;			while (*cp >= '0' && *cp <= '9')				i = i * 10 + (*cp++ - '0');			fino = i;			continue;		}		if (line[0] >= 'a' && line[0] <= 'z') {			strcpy(last, line);			while ((i = getline(cfp)))				if (strcmp(last, line))					break;			switch (sendfile('\3', last+1)) {			case OK:				if (i)					goto again;				break;			case REPRINT:				(void) fclose(cfp);				return(REPRINT);			case ACCESS:				sendmail(logname, ACCESS);			case ERROR:				err = ERROR;			}			break;		}	}	if (err == OK && sendfile('\2', file) > 0) {		(void) fclose(cfp);		return(REPRINT);	}	/*	 * pass 2	 */	fseek(cfp, 0L, 0);	while (getline(cfp))		if (line[0] == 'U' && !strchr(line+1, '/'))			(void) unlink(line+1);	/*	 * clean-up in case another control file exists	 */	(void) fclose(cfp);	(void) unlink(file);	return(err);}/* * Send a data file to the remote machine and spool it. * Return positive if we should try resending. */static intsendfile(type, file)	int type;	char *file;{	int f, i, amt;	struct stat stb;	char buf[BUFSIZ];	int sizerr, resp;	if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)		return(ERROR);	/*	 * Check to see if data file is a symbolic link. If so, it should	 * still point to the same file or someone is trying to print something	 * he shouldn't.	 */	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&	    (stb.st_dev != fdev || stb.st_ino != fino))		return(ACCESS);	if (snprintf(buf, sizeof buf, "%c%qd %s\n", type,	    stb.st_size, file) > sizeof buf-1)		return (ACCESS);		/* XXX hack */	amt = strlen(buf);	for (i = 0;  ; i++) {		if (write(pfd, buf, amt) != amt ||		    (resp = response()) < 0 || resp == '\1') {			(void) close(f);			return(REPRINT);		} else if (resp == '\0')			break;		if (i == 0)			pstatus("no space on remote; waiting for queue to drain");		if (i == 10)			syslog(LOG_ALERT, "%s: can't send to %s; queue full",				printer, RM);		sleep(5 * 60);	}	if (i)		pstatus("sending to %s", RM);	sizerr = 0;	for (i = 0; i < stb.st_size; i += BUFSIZ) {		amt = BUFSIZ;		if (i + amt > stb.st_size)			amt = stb.st_size - i;		if (sizerr == 0 && read(f, buf, amt) != amt)			sizerr = 1;		if (write(pfd, buf, amt) != amt) {			(void) close(f);			return(REPRINT);		}	}	(void) close(f);	if (sizerr) {		syslog(LOG_INFO, "%s: %s: changed size", printer, file);		/* tell recvjob to ignore this file */		(void) write(pfd, "\1", 1);		return(ERROR);	}	if (write(pfd, "", 1) != 1 || response())		return(REPRINT);	return(OK);}/* * Check to make sure there have been no errors and that both programs * are in sync with eachother. * Return non-zero if the connection was lost. */static charresponse(){	char resp;	if (read(pfd, &resp, 1) != 1) {		syslog(LOG_INFO, "%s: lost connection", printer);		return(-1);	}	return(resp);}/* * Banner printing stuff */static voidbanner(name1, name2)	char *name1, *name2;{	time_t tvec;	time(&tvec);	if (!SF && !tof)		(void) write(ofd, FF, strlen(FF));	if (SB) {	/* short banner only */		if (class[0]) {			(void) write(ofd, class, strlen(class));			(void) write(ofd, ":", 1);		}		(void) write(ofd, name1, strlen(name1));		(void) write(ofd, "  Job: ", 7);		(void) write(ofd, name2, strlen(name2));		(void) write(ofd, "  Date: ", 8);		(void) write(ofd, ctime(&tvec), 24);		(void) write(ofd, "\n", 1);	} else {	/* normal banner */		(void) write(ofd, "\n\n\n", 3);		scan_out(ofd, name1, '\0');		(void) write(ofd, "\n\n", 2);		scan_out(ofd, name2, '\0');		if (class[0]) {			(void) write(ofd,"\n\n\n",3);			scan_out(ofd, class, '\0');		}		(void) write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);		(void) write(ofd, name2, strlen(name2));		(void) write(ofd, "\n\t\t\t\t\tDate: ", 12);		(void) write(ofd, ctime(&tvec), 24);		(void) write(ofd, "\n", 1);	}	if (!SF)		(void) write(ofd, FF, strlen(FF));	tof = 1;}static char *scnline(key, p, c)	int key;	char *p;	int c;{	int scnwidth;	for (scnwidth = WIDTH; --scnwidth;) {		key <<= 1;		*p++ = key & 0200 ? c : BACKGND;	}	return (p);}