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