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