xref: /original-bsd/usr.sbin/lpr/lpr/lpr.c (revision 444dc9d9)
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 are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1983, 1989 Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif /* not lint */
23 
24 #ifndef lint
25 static char sccsid[] = "@(#)lpr.c	5.5 (Berkeley) 12/21/88";
26 #endif /* not lint */
27 /*
28  *      lpr -- off line print
29  *
30  * Allows multiple printers and printers on remote machines by
31  * using information from a printer data base.
32  */
33 
34 #include <stdio.h>
35 #include <sys/types.h>
36 #include <sys/file.h>
37 #include <sys/stat.h>
38 #include <pwd.h>
39 #include <grp.h>
40 #include <signal.h>
41 #include <ctype.h>
42 #include <syslog.h>
43 #include "lp.local.h"
44 
45 char    *tfname;		/* tmp copy of cf before linking */
46 char    *cfname;		/* daemon control files, linked from tf's */
47 char    *dfname;		/* data files */
48 
49 int	nact;			/* number of jobs to act on */
50 int	tfd;			/* control file descriptor */
51 int     mailflg;		/* send mail */
52 int	qflag;			/* q job, but don't exec daemon */
53 char	format = 'f';		/* format char for printing files */
54 int	rflag;			/* remove files upon completion */
55 int	sflag;			/* symbolic link flag */
56 int	inchar;			/* location to increment char in file names */
57 int     ncopies = 1;		/* # of copies to make */
58 int	iflag;			/* indentation wanted */
59 int	indent;			/* amount to indent */
60 int	hdr = 1;		/* print header or not (default is yes) */
61 int     userid;			/* user id */
62 char	*person;		/* user name */
63 char	*title;			/* pr'ing title */
64 char	*fonts[4];		/* troff font names */
65 char	*width;			/* width for versatec printing */
66 char	host[32];		/* host name */
67 char	*class = host;		/* class title on header page */
68 char    *jobname;		/* job name on header page */
69 char	*name;			/* program name */
70 char	*printer;		/* printer name */
71 struct	stat statb;
72 
73 int	MX;			/* maximum number of blocks to copy */
74 int	MC;			/* maximum number of copies allowed */
75 int	DU;			/* daemon user-id */
76 char	*SD;			/* spool directory */
77 char	*LO;			/* lock file name */
78 char	*RG;			/* restrict group */
79 short	SC;			/* suppress multiple copies */
80 
81 char	*getenv();
82 char	*rindex();
83 char	*linked();
84 int	cleanup();
85 
86 /*ARGSUSED*/
87 main(argc, argv)
88 	int argc;
89 	char *argv[];
90 {
91 	extern struct passwd *getpwuid();
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 		fatal("multiple copies are not allowed");
233 	if (MC > 0 && ncopies > MC)
234 		fatal("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 			fatal("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 			fatal("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 				fatal("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 		fatal("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 && lseek(tfd, 0L, 0) == 0 &&
343 			    write(tfd, &c, 1) != 1) {
344 				printf("%s: cannot touch %s\n", name, tfname);
345 				tfname[inchar]++;
346 				cleanup();
347 			}
348 			(void) close(tfd);
349 		}
350 		if (link(tfname, cfname) < 0) {
351 			printf("%s: cannot rename %s\n", name, cfname);
352 			tfname[inchar]++;
353 			cleanup();
354 		}
355 		unlink(tfname);
356 		if (qflag)		/* just q things up */
357 			exit(0);
358 		if (!startdaemon(printer))
359 			printf("jobs queued, but cannot start daemon.\n");
360 		exit(0);
361 	}
362 	cleanup();
363 	/* NOTREACHED */
364 }
365 
366 /*
367  * Create the file n and copy from file descriptor f.
368  */
369 copy(f, n)
370 	int f;
371 	char n[];
372 {
373 	register int fd, i, nr, nc;
374 	char buf[BUFSIZ];
375 
376 	if (format == 'p')
377 		card('T', title ? title : n);
378 	for (i = 0; i < ncopies; i++)
379 		card(format, &dfname[inchar-2]);
380 	card('U', &dfname[inchar-2]);
381 	card('N', n);
382 	fd = nfile(dfname);
383 	nr = nc = 0;
384 	while ((i = read(f, buf, BUFSIZ)) > 0) {
385 		if (write(fd, buf, i) != i) {
386 			printf("%s: %s: temp file write error\n", name, n);
387 			break;
388 		}
389 		nc += i;
390 		if (nc >= BUFSIZ) {
391 			nc -= BUFSIZ;
392 			nr++;
393 			if (MX > 0 && nr > MX) {
394 				printf("%s: %s: copy file is too large\n", name, n);
395 				break;
396 			}
397 		}
398 	}
399 	(void) close(fd);
400 	if (nc==0 && nr==0)
401 		printf("%s: %s: empty input file\n", name, f ? n : "stdin");
402 	else
403 		nact++;
404 }
405 
406 /*
407  * Try and link the file to dfname. Return a pointer to the full
408  * path name if successful.
409  */
410 char *
411 linked(file)
412 	register char *file;
413 {
414 	register char *cp;
415 	static char buf[BUFSIZ];
416 
417 	if (*file != '/') {
418 		if (getwd(buf) == NULL)
419 			return(NULL);
420 		while (file[0] == '.') {
421 			switch (file[1]) {
422 			case '/':
423 				file += 2;
424 				continue;
425 			case '.':
426 				if (file[2] == '/') {
427 					if ((cp = rindex(buf, '/')) != NULL)
428 						*cp = '\0';
429 					file += 3;
430 					continue;
431 				}
432 			}
433 			break;
434 		}
435 		strcat(buf, "/");
436 		strcat(buf, file);
437 		file = buf;
438 	}
439 	return(symlink(file, dfname) ? NULL : file);
440 }
441 
442 /*
443  * Put a line into the control file.
444  */
445 card(c, p2)
446 	register char c, *p2;
447 {
448 	char buf[BUFSIZ];
449 	register char *p1 = buf;
450 	register int len = 2;
451 
452 	*p1++ = c;
453 	while ((c = *p2++) != '\0') {
454 		*p1++ = c;
455 		len++;
456 	}
457 	*p1++ = '\n';
458 	write(tfd, buf, len);
459 }
460 
461 /*
462  * Create a new file in the spool directory.
463  */
464 nfile(n)
465 	char *n;
466 {
467 	register 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();
475 	}
476 	if (fchown(f, userid, -1) < 0) {
477 		printf("%s: cannot chown %s\n", name, n);
478 		cleanup();
479 	}
480 	if (++n[inchar] > 'z') {
481 		if (++n[inchar-2] == 't') {
482 			printf("too many files - break up the job\n");
483 			cleanup();
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 cleanup()
495 {
496 	register i;
497 
498 	signal(SIGHUP, SIG_IGN);
499 	signal(SIGINT, SIG_IGN);
500 	signal(SIGQUIT, SIG_IGN);
501 	signal(SIGTERM, SIG_IGN);
502 	i = inchar;
503 	if (tfname)
504 		do
505 			unlink(tfname);
506 		while (tfname[i]-- != 'A');
507 	if (cfname)
508 		do
509 			unlink(cfname);
510 		while (cfname[i]-- != 'A');
511 	if (dfname)
512 		do {
513 			do
514 				unlink(dfname);
515 			while (dfname[i]-- != 'A');
516 			dfname[i] = 'z';
517 		} while (dfname[i-2]-- != 'd');
518 	exit(1);
519 }
520 
521 /*
522  * Test to see if this is a printable file.
523  * Return -1 if it is not, 0 if its printable, and 1 if
524  * we should remove it after printing.
525  */
526 test(file)
527 	char *file;
528 {
529 	struct exec execb;
530 	register int fd;
531 	register char *cp;
532 
533 	if (access(file, 4) < 0) {
534 		printf("%s: cannot access %s\n", name, file);
535 		return(-1);
536 	}
537 	if (stat(file, &statb) < 0) {
538 		printf("%s: cannot stat %s\n", name, file);
539 		return(-1);
540 	}
541 	if ((statb.st_mode & S_IFMT) == S_IFDIR) {
542 		printf("%s: %s is a directory\n", name, file);
543 		return(-1);
544 	}
545 	if (statb.st_size == 0) {
546 		printf("%s: %s is an empty file\n", name, file);
547 		return(-1);
548  	}
549 	if ((fd = open(file, O_RDONLY)) < 0) {
550 		printf("%s: cannot open %s\n", name, file);
551 		return(-1);
552 	}
553 	if (read(fd, &execb, sizeof(execb)) == sizeof(execb))
554 		switch(execb.a_magic) {
555 		case A_MAGIC1:
556 		case A_MAGIC2:
557 		case A_MAGIC3:
558 #ifdef A_MAGIC4
559 		case A_MAGIC4:
560 #endif
561 			printf("%s: %s is an executable program", name, file);
562 			goto error1;
563 
564 		case ARMAG:
565 			printf("%s: %s is an archive file", name, file);
566 			goto error1;
567 		}
568 	(void) close(fd);
569 	if (rflag) {
570 		if ((cp = rindex(file, '/')) == NULL) {
571 			if (access(".", 2) == 0)
572 				return(1);
573 		} else {
574 			*cp = '\0';
575 			fd = access(file, 2);
576 			*cp = '/';
577 			if (fd == 0)
578 				return(1);
579 		}
580 		printf("%s: %s: is not removable by you\n", name, file);
581 	}
582 	return(0);
583 
584 error1:
585 	printf(" and is unprintable\n");
586 	(void) close(fd);
587 	return(-1);
588 }
589 
590 /*
591  * itoa - integer to string conversion
592  */
593 char *
594 itoa(i)
595 	register int i;
596 {
597 	static char b[10] = "########";
598 	register char *p;
599 
600 	p = &b[8];
601 	do
602 		*p-- = i%10 + '0';
603 	while (i /= 10);
604 	return(++p);
605 }
606 
607 /*
608  * Perform lookup for printer name or abbreviation --
609  */
610 chkprinter(s)
611 	char *s;
612 {
613 	int status;
614 	char buf[BUFSIZ];
615 	static char pbuf[BUFSIZ/2];
616 	char *bp = pbuf;
617 	extern char *pgetstr();
618 
619 	if ((status = pgetent(buf, s)) < 0)
620 		fatal("cannot open printer description file");
621 	else if (status == 0)
622 		fatal("%s: unknown printer", s);
623 	if ((SD = pgetstr("sd", &bp)) == NULL)
624 		SD = DEFSPOOL;
625 	if ((LO = pgetstr("lo", &bp)) == NULL)
626 		LO = DEFLOCK;
627 	RG = pgetstr("rg", &bp);
628 	if ((MX = pgetnum("mx")) < 0)
629 		MX = DEFMX;
630 	if ((MC = pgetnum("mc")) < 0)
631 		MC = DEFMAXCOPIES;
632 	if ((DU = pgetnum("du")) < 0)
633 		DU = DEFUID;
634 	SC = pgetflag("sc");
635 }
636 
637 /*
638  * Make the temp files.
639  */
640 mktemps()
641 {
642 	register int c, len, fd, n;
643 	register char *cp;
644 	char buf[BUFSIZ];
645 	char *mktemp();
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 = mktemp("tf", n, len);
666 	cfname = mktemp("cf", n, len);
667 	dfname = mktemp("df", n, len);
668 	inchar = strlen(SD) + 3;
669 	n = (n + 1) % 1000;
670 	(void) lseek(fd, 0L, 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 char *
680 mktemp(id, num, len)
681 	char	*id;
682 	int	num, len;
683 {
684 	register char *s;
685 	extern char *malloc();
686 
687 	if ((s = malloc(len)) == NULL)
688 		fatal("out of memory");
689 	(void) sprintf(s, "%s/%sA%03d%s", SD, id, num, host);
690 	return(s);
691 }
692 
693 /*VARARGS1*/
694 fatal(msg, a1, a2, a3)
695 	char *msg;
696 {
697 	printf("%s: ", name);
698 	printf(msg, a1, a2, a3);
699 	putchar('\n');
700 	exit(1);
701 }
702