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