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