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