xref: /original-bsd/usr.sbin/lpr/lpr/lpr.c (revision 860e07fc)
1 /*
2  * Copyright (c) 1983 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 char copyright[] =
36 "@(#) Copyright (c) 1983, 1989 Regents of the University of California.\n\
37  All rights reserved.\n";
38 #endif /* not lint */
39 
40 #ifndef lint
41 static char sccsid[] = "@(#)lpr.c	5.13 (Berkeley) 8/31/92";
42 #endif /* not lint */
43 
44 /*
45  *      lpr -- off line print
46  *
47  * Allows multiple printers and printers on remote machines by
48  * using information from a printer data base.
49  */
50 
51 #include <sys/param.h>
52 #include <sys/stat.h>
53 
54 #include <dirent.h>
55 #include <fcntl.h>
56 #include <a.out.h>
57 #include <signal.h>
58 #include <syslog.h>
59 #include <pwd.h>
60 #include <grp.h>
61 #include <unistd.h>
62 #include <stdlib.h>
63 #include <stdio.h>
64 #include <ctype.h>
65 #include <string.h>
66 #include "lp.h"
67 #include "lp.local.h"
68 #include "pathnames.h"
69 
70 static char	*cfname;	/* daemon control files, linked from tf's */
71 static char	*class = host;	/* class title on header page */
72 static char	*dfname;		/* data files */
73 static char	*fonts[4];	/* troff font names */
74 static char	 format = 'f';	/* format char for printing files */
75 static int	 hdr = 1;	/* print header or not (default is yes) */
76 static int	 iflag;		/* indentation wanted */
77 static int	 inchar;	/* location to increment char in file names */
78 static int	 indent;	/* amount to indent */
79 static char	*jobname;	/* job name on header page */
80 static int	 mailflg;	/* send mail */
81 static int	 nact;		/* number of jobs to act on */
82 static int	 ncopies = 1;	/* # of copies to make */
83 static char	*person;	/* user name */
84 static int	 qflag;		/* q job, but don't exec daemon */
85 static int	 rflag;		/* remove files upon completion */
86 static int	 sflag;		/* symbolic link flag */
87 static int	 tfd;		/* control file descriptor */
88 static char	*tfname;	/* tmp copy of cf before linking */
89 static char	*title;		/* pr'ing title */
90 static int	 userid;	/* user id */
91 static char	*width;		/* width for versatec printing */
92 
93 static struct stat statb;
94 
95 static void	 card __P((int, char *));
96 static void	 chkprinter __P((char *));
97 static void	 cleanup __P((int));
98 static void	 copy __P((int, char []));
99 static void	 fatal2 __P((const char *, ...));
100 static char	*itoa __P((int));
101 static char	*linked __P((char *));
102 static char	*lmktemp __P((char *, int, int));
103 static void	 mktemps __P((void));
104 static int	 nfile __P((char *));
105 static int	 test __P((char *));
106 
107 void
108 main(argc, argv)
109 	int argc;
110 	char *argv[];
111 {
112 	struct passwd *pw;
113 	struct group *gptr;
114 	extern char *itoa();
115 	register char *arg, *cp;
116 	char buf[BUFSIZ];
117 	int i, f;
118 	struct stat stb;
119 
120 	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
121 		signal(SIGHUP, cleanup);
122 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
123 		signal(SIGINT, cleanup);
124 	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
125 		signal(SIGQUIT, cleanup);
126 	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
127 		signal(SIGTERM, cleanup);
128 
129 	name = argv[0];
130 	gethostname(host, sizeof (host));
131 	openlog("lpd", 0, LOG_LPR);
132 
133 	while (argc > 1 && argv[1][0] == '-') {
134 		argc--;
135 		arg = *++argv;
136 		switch (arg[1]) {
137 
138 		case 'P':		/* specifiy printer name */
139 			if (arg[2])
140 				printer = &arg[2];
141 			else if (argc > 1) {
142 				argc--;
143 				printer = *++argv;
144 			}
145 			break;
146 
147 		case 'C':		/* classification spec */
148 			hdr++;
149 			if (arg[2])
150 				class = &arg[2];
151 			else if (argc > 1) {
152 				argc--;
153 				class = *++argv;
154 			}
155 			break;
156 
157 		case 'U':		/* user name */
158 			hdr++;
159 			if (arg[2])
160 				person = &arg[2];
161 			else if (argc > 1) {
162 				argc--;
163 				person = *++argv;
164 			}
165 			break;
166 
167 		case 'J':		/* job name */
168 			hdr++;
169 			if (arg[2])
170 				jobname = &arg[2];
171 			else if (argc > 1) {
172 				argc--;
173 				jobname = *++argv;
174 			}
175 			break;
176 
177 		case 'T':		/* pr's title line */
178 			if (arg[2])
179 				title = &arg[2];
180 			else if (argc > 1) {
181 				argc--;
182 				title = *++argv;
183 			}
184 			break;
185 
186 		case 'l':		/* literal output */
187 		case 'p':		/* print using ``pr'' */
188 		case 't':		/* print troff output (cat files) */
189 		case 'n':		/* print ditroff output */
190 		case 'd':		/* print tex output (dvi files) */
191 		case 'g':		/* print graph(1G) output */
192 		case 'c':		/* print cifplot output */
193 		case 'v':		/* print vplot output */
194 			format = arg[1];
195 			break;
196 
197 		case 'f':		/* print fortran output */
198 			format = 'r';
199 			break;
200 
201 		case '4':		/* troff fonts */
202 		case '3':
203 		case '2':
204 		case '1':
205 			if (argc > 1) {
206 				argc--;
207 				fonts[arg[1] - '1'] = *++argv;
208 			}
209 			break;
210 
211 		case 'w':		/* versatec page width */
212 			width = arg+2;
213 			break;
214 
215 		case 'r':		/* remove file when done */
216 			rflag++;
217 			break;
218 
219 		case 'm':		/* send mail when done */
220 			mailflg++;
221 			break;
222 
223 		case 'h':		/* toggle want of header page */
224 			hdr = !hdr;
225 			break;
226 
227 		case 's':		/* try to link files */
228 			sflag++;
229 			break;
230 
231 		case 'q':		/* just q job */
232 			qflag++;
233 			break;
234 
235 		case 'i':		/* indent output */
236 			iflag++;
237 			indent = arg[2] ? atoi(&arg[2]) : 8;
238 			break;
239 
240 		case '#':		/* n copies */
241 			if (isdigit(arg[2])) {
242 				i = atoi(&arg[2]);
243 				if (i > 0)
244 					ncopies = i;
245 			}
246 		}
247 	}
248 	if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
249 		printer = DEFLP;
250 	chkprinter(printer);
251 	if (SC && ncopies > 1)
252 		fatal2("multiple copies are not allowed");
253 	if (MC > 0 && ncopies > MC)
254 		fatal2("only %d copies are allowed", MC);
255 	/*
256 	 * Get the identity of the person doing the lpr using the same
257 	 * algorithm as lprm.
258 	 */
259 	userid = getuid();
260 	if (userid != DU || person == 0) {
261 		if ((pw = getpwuid(userid)) == NULL)
262 			fatal2("Who are you?");
263 		person = pw->pw_name;
264 	}
265 	/*
266 	 * Check for restricted group access.
267 	 */
268 	if (RG != NULL && userid != DU) {
269 		if ((gptr = getgrnam(RG)) == NULL)
270 			fatal2("Restricted group specified incorrectly");
271 		if (gptr->gr_gid != getgid()) {
272 			while (*gptr->gr_mem != NULL) {
273 				if ((strcmp(person, *gptr->gr_mem)) == 0)
274 					break;
275 				gptr->gr_mem++;
276 			}
277 			if (*gptr->gr_mem == NULL)
278 				fatal2("Not a member of the restricted group");
279 		}
280 	}
281 	/*
282 	 * Check to make sure queuing is enabled if userid is not root.
283 	 */
284 	(void) sprintf(buf, "%s/%s", SD, LO);
285 	if (userid && stat(buf, &stb) == 0 && (stb.st_mode & 010))
286 		fatal2("Printer queue is disabled");
287 	/*
288 	 * Initialize the control file.
289 	 */
290 	mktemps();
291 	tfd = nfile(tfname);
292 	(void) fchown(tfd, DU, -1);	/* owned by daemon for protection */
293 	card('H', host);
294 	card('P', person);
295 	if (hdr) {
296 		if (jobname == NULL) {
297 			if (argc == 1)
298 				jobname = "stdin";
299 			else
300 				jobname = (arg = rindex(argv[1], '/')) ? arg+1 : argv[1];
301 		}
302 		card('J', jobname);
303 		card('C', class);
304 		card('L', person);
305 	}
306 	if (iflag)
307 		card('I', itoa(indent));
308 	if (mailflg)
309 		card('M', person);
310 	if (format == 't' || format == 'n' || format == 'd')
311 		for (i = 0; i < 4; i++)
312 			if (fonts[i] != NULL)
313 				card('1'+i, fonts[i]);
314 	if (width != NULL)
315 		card('W', width);
316 
317 	/*
318 	 * Read the files and spool them.
319 	 */
320 	if (argc == 1)
321 		copy(0, " ");
322 	else while (--argc) {
323 		if ((f = test(arg = *++argv)) < 0)
324 			continue;	/* file unreasonable */
325 
326 		if (sflag && (cp = linked(arg)) != NULL) {
327 			(void) sprintf(buf, "%d %d", statb.st_dev, statb.st_ino);
328 			card('S', buf);
329 			if (format == 'p')
330 				card('T', title ? title : arg);
331 			for (i = 0; i < ncopies; i++)
332 				card(format, &dfname[inchar-2]);
333 			card('U', &dfname[inchar-2]);
334 			if (f)
335 				card('U', cp);
336 			card('N', arg);
337 			dfname[inchar]++;
338 			nact++;
339 			continue;
340 		}
341 		if (sflag)
342 			printf("%s: %s: not linked, copying instead\n", name, arg);
343 		if ((i = open(arg, O_RDONLY)) < 0) {
344 			printf("%s: cannot open %s\n", name, arg);
345 			continue;
346 		}
347 		copy(i, arg);
348 		(void) close(i);
349 		if (f && unlink(arg) < 0)
350 			printf("%s: %s: not removed\n", name, arg);
351 	}
352 
353 	if (nact) {
354 		(void) close(tfd);
355 		tfname[inchar]--;
356 		/*
357 		 * Touch the control file to fix position in the queue.
358 		 */
359 		if ((tfd = open(tfname, O_RDWR)) >= 0) {
360 			char c;
361 
362 			if (read(tfd, &c, 1) == 1 &&
363 			    lseek(tfd, (off_t)0, 0) == 0 &&
364 			    write(tfd, &c, 1) != 1) {
365 				printf("%s: cannot touch %s\n", name, tfname);
366 				tfname[inchar]++;
367 				cleanup(0);
368 			}
369 			(void) close(tfd);
370 		}
371 		if (link(tfname, cfname) < 0) {
372 			printf("%s: cannot rename %s\n", name, cfname);
373 			tfname[inchar]++;
374 			cleanup(0);
375 		}
376 		unlink(tfname);
377 		if (qflag)		/* just q things up */
378 			exit(0);
379 		if (!startdaemon(printer))
380 			printf("jobs queued, but cannot start daemon.\n");
381 		exit(0);
382 	}
383 	cleanup(0);
384 	/* NOTREACHED */
385 }
386 
387 /*
388  * Create the file n and copy from file descriptor f.
389  */
390 static void
391 copy(f, n)
392 	int f;
393 	char n[];
394 {
395 	register int fd, i, nr, nc;
396 	char buf[BUFSIZ];
397 
398 	if (format == 'p')
399 		card('T', title ? title : n);
400 	for (i = 0; i < ncopies; i++)
401 		card(format, &dfname[inchar-2]);
402 	card('U', &dfname[inchar-2]);
403 	card('N', n);
404 	fd = nfile(dfname);
405 	nr = nc = 0;
406 	while ((i = read(f, buf, BUFSIZ)) > 0) {
407 		if (write(fd, buf, i) != i) {
408 			printf("%s: %s: temp file write error\n", name, n);
409 			break;
410 		}
411 		nc += i;
412 		if (nc >= BUFSIZ) {
413 			nc -= BUFSIZ;
414 			nr++;
415 			if (MX > 0 && nr > MX) {
416 				printf("%s: %s: copy file is too large\n", name, n);
417 				break;
418 			}
419 		}
420 	}
421 	(void) close(fd);
422 	if (nc==0 && nr==0)
423 		printf("%s: %s: empty input file\n", name, f ? n : "stdin");
424 	else
425 		nact++;
426 }
427 
428 /*
429  * Try and link the file to dfname. Return a pointer to the full
430  * path name if successful.
431  */
432 static char *
433 linked(file)
434 	register char *file;
435 {
436 	register char *cp;
437 	static char buf[BUFSIZ];
438 
439 	if (*file != '/') {
440 		if (getwd(buf) == NULL)
441 			return(NULL);
442 		while (file[0] == '.') {
443 			switch (file[1]) {
444 			case '/':
445 				file += 2;
446 				continue;
447 			case '.':
448 				if (file[2] == '/') {
449 					if ((cp = rindex(buf, '/')) != NULL)
450 						*cp = '\0';
451 					file += 3;
452 					continue;
453 				}
454 			}
455 			break;
456 		}
457 		strcat(buf, "/");
458 		strcat(buf, file);
459 		file = buf;
460 	}
461 	return(symlink(file, dfname) ? NULL : file);
462 }
463 
464 /*
465  * Put a line into the control file.
466  */
467 static void
468 card(c, p2)
469 	register int c;
470 	register char *p2;
471 {
472 	char buf[BUFSIZ];
473 	register char *p1 = buf;
474 	register int len = 2;
475 
476 	*p1++ = c;
477 	while ((c = *p2++) != '\0') {
478 		*p1++ = (c == '\n') ? ' ' : c;
479 		len++;
480 	}
481 	*p1++ = '\n';
482 	write(tfd, buf, len);
483 }
484 
485 /*
486  * Create a new file in the spool directory.
487  */
488 static int
489 nfile(n)
490 	char *n;
491 {
492 	register int f;
493 	int oldumask = umask(0);		/* should block signals */
494 
495 	f = creat(n, FILMOD);
496 	(void) umask(oldumask);
497 	if (f < 0) {
498 		printf("%s: cannot create %s\n", name, n);
499 		cleanup(0);
500 	}
501 	if (fchown(f, userid, -1) < 0) {
502 		printf("%s: cannot chown %s\n", name, n);
503 		cleanup(0);
504 	}
505 	if (++n[inchar] > 'z') {
506 		if (++n[inchar-2] == 't') {
507 			printf("too many files - break up the job\n");
508 			cleanup(0);
509 		}
510 		n[inchar] = 'A';
511 	} else if (n[inchar] == '[')
512 		n[inchar] = 'a';
513 	return(f);
514 }
515 
516 /*
517  * Cleanup after interrupts and errors.
518  */
519 static void
520 cleanup(signo)
521 	int signo;
522 {
523 	register i;
524 
525 	signal(SIGHUP, SIG_IGN);
526 	signal(SIGINT, SIG_IGN);
527 	signal(SIGQUIT, SIG_IGN);
528 	signal(SIGTERM, SIG_IGN);
529 	i = inchar;
530 	if (tfname)
531 		do
532 			unlink(tfname);
533 		while (tfname[i]-- != 'A');
534 	if (cfname)
535 		do
536 			unlink(cfname);
537 		while (cfname[i]-- != 'A');
538 	if (dfname)
539 		do {
540 			do
541 				unlink(dfname);
542 			while (dfname[i]-- != 'A');
543 			dfname[i] = 'z';
544 		} while (dfname[i-2]-- != 'd');
545 	exit(1);
546 }
547 
548 /*
549  * Test to see if this is a printable file.
550  * Return -1 if it is not, 0 if its printable, and 1 if
551  * we should remove it after printing.
552  */
553 static int
554 test(file)
555 	char *file;
556 {
557 	struct exec execb;
558 	register int fd;
559 	register char *cp;
560 
561 	if (access(file, 4) < 0) {
562 		printf("%s: cannot access %s\n", name, file);
563 		return(-1);
564 	}
565 	if (stat(file, &statb) < 0) {
566 		printf("%s: cannot stat %s\n", name, file);
567 		return(-1);
568 	}
569 	if ((statb.st_mode & S_IFMT) == S_IFDIR) {
570 		printf("%s: %s is a directory\n", name, file);
571 		return(-1);
572 	}
573 	if (statb.st_size == 0) {
574 		printf("%s: %s is an empty file\n", name, file);
575 		return(-1);
576  	}
577 	if ((fd = open(file, O_RDONLY)) < 0) {
578 		printf("%s: cannot open %s\n", name, file);
579 		return(-1);
580 	}
581 	if (read(fd, &execb, sizeof(execb)) == sizeof(execb) &&
582 	    !N_BADMAG(execb)) {
583 			printf("%s: %s is an executable program", name, file);
584 			goto error1;
585 		}
586 	(void) close(fd);
587 	if (rflag) {
588 		if ((cp = rindex(file, '/')) == NULL) {
589 			if (access(".", 2) == 0)
590 				return(1);
591 		} else {
592 			if (cp == file) {
593 				fd = access("/", 2);
594 			} else {
595 				*cp = '\0';
596 				fd = access(file, 2);
597 				*cp = '/';
598 			}
599 			if (fd == 0)
600 				return(1);
601 		}
602 		printf("%s: %s: is not removable by you\n", name, file);
603 	}
604 	return(0);
605 
606 error1:
607 	printf(" and is unprintable\n");
608 	(void) close(fd);
609 	return(-1);
610 }
611 
612 /*
613  * itoa - integer to string conversion
614  */
615 static char *
616 itoa(i)
617 	register int i;
618 {
619 	static char b[10] = "########";
620 	register char *p;
621 
622 	p = &b[8];
623 	do
624 		*p-- = i%10 + '0';
625 	while (i /= 10);
626 	return(++p);
627 }
628 
629 /*
630  * Perform lookup for printer name or abbreviation --
631  */
632 static void
633 chkprinter(s)
634 	char *s;
635 {
636 	int status;
637 
638 	if ((status = cgetent(&bp, printcapdb, s)) == -2)
639 		fatal2("cannot open printer description file");
640 	else if (status == -1)
641 		fatal2("%s: unknown printer", s);
642 	if (cgetstr(bp, "sd", &SD) == -1)
643 		SD = _PATH_DEFSPOOL;
644 	if (cgetstr(bp, "lo", &LO) == -1)
645 		LO = DEFLOCK;
646 	cgetstr(bp, "rg", &RG);
647 	if (cgetnum(bp, "mx", &MX) < 0)
648 		MX = DEFMX;
649 	if (cgetnum(bp,"mc", &MC) < 0)
650 		MC = DEFMAXCOPIES;
651 	if (cgetnum(bp, "du", &DU) < 0)
652 		DU = DEFUID;
653 	SC = (cgetcap(bp, "sc", ':') != NULL);
654 }
655 
656 /*
657  * Make the temp files.
658  */
659 static void
660 mktemps()
661 {
662 	register int len, fd, n;
663 	register char *cp;
664 	char buf[BUFSIZ];
665 	char *lmktemp();
666 
667 	(void) sprintf(buf, "%s/.seq", SD);
668 	if ((fd = open(buf, O_RDWR|O_CREAT, 0661)) < 0) {
669 		printf("%s: cannot create %s\n", name, buf);
670 		exit(1);
671 	}
672 	if (flock(fd, LOCK_EX)) {
673 		printf("%s: cannot lock %s\n", name, buf);
674 		exit(1);
675 	}
676 	n = 0;
677 	if ((len = read(fd, buf, sizeof(buf))) > 0) {
678 		for (cp = buf; len--; ) {
679 			if (*cp < '0' || *cp > '9')
680 				break;
681 			n = n * 10 + (*cp++ - '0');
682 		}
683 	}
684 	len = strlen(SD) + strlen(host) + 8;
685 	tfname = lmktemp("tf", n, len);
686 	cfname = lmktemp("cf", n, len);
687 	dfname = lmktemp("df", n, len);
688 	inchar = strlen(SD) + 3;
689 	n = (n + 1) % 1000;
690 	(void) lseek(fd, (off_t)0, 0);
691 	sprintf(buf, "%03d\n", n);
692 	(void) write(fd, buf, strlen(buf));
693 	(void) close(fd);	/* unlocks as well */
694 }
695 
696 /*
697  * Make a temp file name.
698  */
699 static char *
700 lmktemp(id, num, len)
701 	char	*id;
702 	int	num, len;
703 {
704 	register char *s;
705 
706 	if ((s = malloc(len)) == NULL)
707 		fatal2("out of memory");
708 	(void) sprintf(s, "%s/%sA%03d%s", SD, id, num, host);
709 	return(s);
710 }
711 
712 #if __STDC__
713 #include <stdarg.h>
714 #else
715 #include <varargs.h>
716 #endif
717 
718 static void
719 #if __STDC__
720 fatal2(const char *msg, ...)
721 #else
722 fatal2(msg, va_alist)
723 	char *msg;
724         va_dcl
725 #endif
726 {
727 	va_list ap;
728 #if __STDC__
729 	va_start(ap, msg);
730 #else
731 	va_start(ap);
732 #endif
733 	printf("%s: ", name);
734 	vprintf(msg, ap);
735 	putchar('\n');
736 	va_end(ap);
737 	exit(1);
738 }
739