xref: /original-bsd/usr.sbin/lpr/lpr/lpr.c (revision f0fd5f8a)
1 /*	lpr.c	4.4	82/12/02	*/
2 /*
3  *      lpr -- off line print
4  *              also known as print
5  */
6 
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <signal.h>
10 #include <pwd.h>
11 #include <stdio.h>
12 #include <ctype.h>
13 #include "lp.local.h"
14 
15 /*
16  * Multiple printer scheme using info from printer data base:
17  *
18  *	DN		who to invoke to print stuff
19  *	SA		directory used as spool queue
20  *
21  * daemon identifies what printer it should use (in the case of the
22  *  same code being shared) by inspecting its argv[1].
23  */
24 char    *tfname;		/* tmp copy of df before linking */
25 char    *cfname;		/* copy files */
26 char    *lfname;		/* linked files */
27 char    *dfname;		/* daemon files, linked from tf's */
28 
29 int	nact;			/* number of jobs to act on */
30 int	tff;			/* daemon file descriptor */
31 int     mailflg;		/* send mail */
32 int	jflag;			/* job name specified */
33 int	qflg;			/* q job, but don't exec daemon */
34 int	prflag;			/* ``pr'' files */
35 char	*person;		/* user name */
36 int	inchar;			/* location to increment char in file names */
37 int     ncopies = 1;		/* # of copies to make */
38 int	iflag;			/* indentation wanted */
39 int	indent;			/* amount to indent */
40 char	*daemname;		/* path name to daemon program */
41 char	*DN;			/* daemon name */
42 char	*SA;			/* spooling area */
43 char	*LP;			/* line printer device */
44 int     MX;			/* maximum size in blocks of a print file */
45 int	hdr = 1;		/* 1 =>'s default header */
46 int     user;			/* user id */
47 int	spgroup;		/* daemon's group for creating spool files */
48 char	*title;			/* pr'ing title */
49 char	classbuf[32];		/* class title on header page */
50 char	*class = classbuf;
51 char    *jobname;		/* job name on header page */
52 char	*name;			/* program name */
53 char	*printer;		/* printer name */
54 
55 char	*pgetstr();
56 char	*mktmp();
57 char	*malloc();
58 char	*getenv();
59 char	*rindex();
60 
61 /* ARGSUSED */
62 main(argc, argv)
63 	int argc;
64 	char *argv[];
65 {
66 	register char *arg;
67 	int i, c, f, flag, out();
68 	char *sp;
69 	struct stat sbuf;
70 
71 	/*
72 	 * Strategy to maintain protected spooling area:
73 	 *	1. Spooling area is writable only by daemon and spooling group
74 	 *	2. lpr runs setuid root and setgrp spooling group; it uses
75 	 *	   root to access any file it wants (verifying things before
76 	 *	   with an access call) and group id to know how it should
77 	 *	   set up ownership of files in spooling area.
78 	 *	3. Files in spooling area are owned by printer and spooling
79 	 *	   group, with mode 660.
80 	 *	4. lpd runs setuid daemon and setgrp spooling group to
81 	 *	   access files and printer.  Users can't get to anything
82 	 *	   w/o help of sq and dq programs.
83 	 */
84 	gethostname(classbuf, sizeof (classbuf));
85 	user = getuid();
86 	spgroup = getegid();
87 	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
88 		signal(SIGHUP, out);
89 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
90 		signal(SIGINT, out);
91 	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
92 		signal(SIGQUIT, out);
93 	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
94 		signal(SIGTERM, out);
95 	flag = 0;
96 	if ((printer = getenv("PRINTER")) == NULL)
97 		printer = DEFLP;
98 	name = argv[0];
99 
100 	while (argc>1 && (arg = argv[1])[0]=='-') {
101 		switch (arg[1]) {
102 
103 		case 'c':		/* force copy of files */
104 			flag = '+';
105 			break;
106 
107 		case 'C':		/* classification spec */
108 			hdr++;
109 			if (arg[2])
110 				class = &arg[2];
111 			else if (argc >= 2) {
112 				++argv;
113 				arg = argv[1];
114 				argc--;
115 				class = arg;
116 			}
117 			break;
118 
119 		case 'r':		/* remove file when done */
120 			flag = '-';
121 			break;
122 
123 		case 'm':		/* send mail when done */
124 			mailflg++;
125 			break;
126 
127 		case 'q':		/* just q job */
128 			qflg++;
129 			break;
130 
131 		case 'J':		/* job spec */
132 			jflag++, hdr++;
133 			if (arg[2]) {
134 				jobname = &arg[2];
135 				break;
136 			}
137 			if (argc>=2) {
138 				++argv;
139 				arg = argv[1];
140 				jobname = &arg[0];
141 				argc--;
142 			}
143 			break;
144 
145 		case 'i':		/* indent output */
146 			iflag++;
147 			indent = arg[2] ? atoi(&arg[2]) : 8;
148 			break;
149 
150 		case 'p':		/* use pr to print files */
151 			prflag++;
152 			break;
153 
154 		case 'h':		/* pr's title line */
155 			if (arg[2])
156 				title = &arg[2];
157 			else if (argc >= 2) {
158 				++argv;
159 				arg = argv[1];
160 				argc--;
161 				title = arg;
162 			}
163 			break;
164 
165 		case 'P':		/* specifiy printer name */
166 			printer = &arg[2];
167 			break;
168 
169 		case 'H':		/* toggle want of header page */
170 			hdr = !hdr;
171 			break;
172 
173 		default:		/* n copies ? */
174 			if (isdigit(arg[1]))
175 				ncopies = atoi(&arg[1]);
176 		}
177 		argc--;
178 		argv++;
179 	}
180 	if (!chkprinter(printer)) {
181 		fprintf(stderr, "%s: no entry for default printer %s\n", name,
182 			printer);
183 		exit(2);
184 	}
185 	i = getpid();
186 	f = strlen(SA)+11;
187 	tfname = mktmp("tf", i, f);
188 	cfname = mktmp("cf", i, f);
189 	lfname = mktmp("lf", i, f);
190 	dfname = mktmp("df", i, f);
191 	inchar = f-7;
192 	tff = nfile(tfname);
193 	if (jflag == 0) {
194 		if (argc == 1)
195 			jobname = &dfname[f-10];
196 		else
197 			jobname = argv[1];
198 	}
199 	ident();
200 
201 	if (argc == 1)
202 		copy(0, " ");
203 	else while (--argc) {
204 		if (test(arg = *++argv) == -1)	/* file reasonable */
205 			continue;
206 
207 		if (flag == '+')		/* force copy flag */
208 			goto cf;
209 		if (stat(arg, &sbuf) < 0) {
210 			printf("%s:", name);
211 			perror(arg);
212 			continue;
213 		}
214 		if ((sbuf.st_mode&04) == 0)
215 			goto cf;
216 		if (*arg == '/' && flag != '-') {
217 			for (i=0;i<ncopies;i++) {
218 				if (prflag) {
219 					if (title)
220 						card('H', title);
221 					card('R', arg);
222 				} else
223 					card('F', arg);
224 				card('N', arg);
225 			}
226 			nact++;
227 			continue;
228 		}
229 		if (link(arg, lfname) < 0)
230 			goto cf;
231 		for (i=0;i<ncopies;i++) {
232 			if (prflag) {
233 				card('H', title ? title : arg);
234 				card('R', lfname);
235 			} else
236 				card('F', lfname);
237 			card('N', arg);
238 		}
239 		card('U', lfname);
240 		lfname[inchar]++;
241 		nact++;
242 		goto df;
243 
244 	cf:
245 		if ((f = open(arg, 0)) < 0) {
246 			printf("%s: cannot open %s\n", name, arg);
247 			continue;
248 		}
249 		copy(f, arg);
250 		close(f);
251 
252 	df:
253 		if (flag == '-' && unlink(arg))
254 			printf("%s: cannot remove %s\n", name, arg);
255 	}
256 
257 	if (nact) {
258 		tfname[inchar]--;
259 		if (link(tfname, dfname) < 0) {
260 			printf("%s: cannot rename %s\n", name, dfname);
261 			tfname[inchar]++;
262 			out();
263 		}
264 		unlink(tfname);
265 		if (qflg)		/* just q things up */
266 			exit(0);
267 		if (stat(LP, &sbuf) >= 0 && (sbuf.st_mode&0777) == 0) {
268 			printf("job queued, but printer down\n");
269 			exit(0);
270 		}
271 		for (f = 0; f < NOFILE; close(f++))
272 			;
273 		open("/dev/tty", 0);
274 		open("/dev/tty", 1);
275 		dup2(1, 2);
276 		execl(DN, rindex(DN, '/') ? rindex(DN, '/')+1 : DN, printer, 0);
277 		dfname[inchar]++;
278 	}
279 	out();
280 }
281 
282 copy(f, n)
283 	int f;
284 	char n[];
285 {
286 	int ff, i, nr, nc;
287 	char buf[BUFSIZ];
288 
289 	for (i=0;i<ncopies;i++) {
290 		if (prflag) {
291 			card('H', title ? title : n);
292 			card('R', cfname);
293 		} else
294 			card('F', cfname);
295 		card('N', n);
296 	}
297 	card('U', cfname);
298 	ff = nfile(cfname);
299 	nr = nc = 0;
300 	while ((i = read(f, buf, BUFSIZ)) > 0) {
301 		if (write(ff, buf, i) != i) {
302 			printf("%s: %s: temp file write error\n", name, n);
303 			break;
304 		}
305 		nc += i;
306 		if (nc >= BUFSIZ) {
307 			nc -= BUFSIZ;
308 			if (nr++ > MX) {
309 				printf("%s: %s: copy file is too large\n", name, n);
310 				break;
311 			}
312 		}
313 	}
314 	close(ff);
315 	nact++;
316 }
317 
318 card(c, p2)
319 	register char c, *p2;
320 {
321 	char buf[BUFSIZ];
322 	register char *p1 = buf;
323 	int col = 0;
324 
325 	*p1++ = c;
326 	while ((c = *p2++) != '\0') {
327 		*p1++ = c;
328 		col++;
329 	}
330 	*p1++ = '\n';
331 	write(tff, buf, col+2);
332 }
333 
334 ident()
335 {
336 	extern char *getlogin();
337 	extern struct passwd *getpwuid();
338 	struct passwd *pw;
339 	extern char *itoa();
340 
341 	if ((person = getlogin()) == NULL) {
342 		if ((pw = getpwuid(user)) == NULL)
343 			person = "Unknown User";
344 		else
345 			person = pw->pw_name;
346 	}
347 
348 	if (hdr) {
349 		card('J',jobname);
350 		card('C',class);
351 		card('L', person);
352 	}
353 	if (iflag)
354 		card('I', itoa(indent));
355 	if (mailflg)
356 		card('M', person);
357 }
358 
359 nfile(n)
360 	char *n;
361 {
362 	register f;
363 	int oldumask = umask(022);		/* should block signals */
364 
365 	f = creat(n, FILMOD);
366 	(void) umask(oldumask);
367 	if (f < 0) {
368 		printf("%s: cannot create %s\n", name, n);
369 		out();
370 	}
371 	if (chown(n, user, spgroup) < 0) {
372 		unlink(n);
373 		printf("%s: cannot chown %s\n", name, n);
374 		out();
375 	}
376 	n[inchar]++;
377 	return (f);
378 }
379 
380 out()
381 {
382 	register i;
383 
384 	signal(SIGHUP, SIG_IGN);
385 	signal(SIGINT, SIG_IGN);
386 	signal(SIGQUIT, SIG_IGN);
387 	signal(SIGTERM, SIG_IGN);
388 	i = inchar;
389 	if (tfname)
390 		while (tfname[i] != 'A') {
391 			tfname[i]--;
392 			unlink(tfname);
393 		}
394 	if (cfname)
395 		while (cfname[i] != 'A') {
396 			cfname[i]--;
397 			unlink(cfname);
398 		}
399 	if (lfname)
400 		while (lfname[i] != 'A') {
401 			lfname[i]--;
402 			unlink(lfname);
403 		}
404 	if (dfname)
405 		while (dfname[i] != 'A') {
406 			dfname[i]--;
407 			unlink(dfname);
408 		}
409 	exit();
410 }
411 
412 test(file)
413 	char *file;
414 {
415 	struct exec buf;
416 	struct stat mbuf;
417 	int fd;
418 
419 	if (access(file, 4) < 0) {
420 		printf("%s: cannot access %s\n", name, file);
421 		return (-1);
422 	}
423 	if (stat(file, &mbuf) < 0) {
424 		printf("%s: cannot stat %s\n", name, file);
425 		return (-1);
426 	}
427 	if ((mbuf.st_mode&S_IFMT) == S_IFDIR) {
428 		printf("%s: %s is a directory\n", name, file);
429 		return (-1);
430 	}
431 
432 	if ((fd = open(file, 0)) < 0) {
433 		printf("%s: cannot open %s\n", name, file);
434 		return (-1);
435 	}
436 	if (read(fd, &buf, sizeof(buf)) == sizeof(buf))
437 		switch(buf.a_magic) {
438 		case A_MAGIC1:
439 		case A_MAGIC2:
440 		case A_MAGIC3:
441 #ifdef A_MAGIC4
442 		case A_MAGIC4:
443 #endif
444 			printf("%s: %s is an executable program", name, file);
445 			goto error1;
446 
447 		case ARMAG:
448 			printf("%s: %s is an archive file", name, file);
449 			goto error1;
450 		}
451 
452 	close(fd);
453 	return (0);
454 error1:
455 	printf(" and is unprintable\n");
456 	close(fd);
457 	return (-1);
458 }
459 
460 /*
461  * itoa - integer to string conversion
462  */
463 char *
464 itoa(i)
465 	register int i;
466 {
467 	static char b[10] = "########";
468 	register char *p;
469 
470 	p = &b[8];
471 	do
472 		*p-- = i%10 + '0';
473 	while (i /= 10);
474 	return (++p);
475 }
476 
477 /*
478  * Perform lookup for printer name or abbreviation --
479  *   return pointer to daemon structure
480  */
481 chkprinter(s)
482 	register char *s;
483 {
484 	static char buf[BUFSIZ/2];
485 	char b[BUFSIZ];
486 	int stat;
487 	char *bp = buf;
488 
489 	if ((stat = pgetent(b, s)) < 0) {
490 		fprintf(stderr, "%s: can't open printer description file\n", name);
491 		exit(3);
492 	} else if (stat == 0)
493 		return (NULL);
494 	if ((DN = pgetstr("dn", &bp)) == NULL)
495 		DN = DEFDAEMON;
496 	if ((LP = pgetstr("lp", &bp)) == NULL)
497 		LP = DEFDEVLP;
498 	if ((SA = pgetstr("sa", &bp)) == NULL)
499 		SA = DEFSPOOL;
500 	if ((MX = pgetnum("mx")) < 0)
501 		MX = DEFMX;
502 	return (1);
503 }
504 
505 /*
506  * Make a temp file
507  */
508 char *
509 mktmp(id, pid, n)
510 	char *id;
511 {
512 	register char *s;
513 
514 	if ((s = malloc(n)) == NULL) {
515 		fprintf(stderr, "%s: out of memory\n", name);
516 		exit(1);
517 	}
518 	sprintf(s, "%s/%sA%05d", SA, id, pid);
519 	return (s);
520 }
521