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