added status file to spooldir and other improvements.
[unix-history] / usr / src / usr.sbin / lpr / lpr / lpr.c
CommitLineData
51d1035a
BJ
1/*
2 *
3 * 1.1 mike 5/77 - all files copied to spooler space
4 * (see note below)
5 *
6 * 1.2 dlm 26 Sep 1977
7 * fix for allowing variable indents (-i flag)
8 *
9 * 1.3 dlm/njl 12 Dec 1977
10 * fix for open pipe when spawning progess (affects lpd)
11 *
12 * 1.4 dlm 24 Feb 1978
13 * test for data files; add -C option for classification
14 * and change format of $L card; reversed 1.1 on copies
15 *
16 * 1.5 dlm 27 Mar 1978
17 * add job name and -h option
18 *
19 * 2.0 was 21 Dec 1978
20 * -h option changed to -J
21 * added -H option to cause header page to be
22 * printed, default is no header page
23 * added -p option to cause files to be printed
24 * using pr
25 * default for classification changed
26 *
27 * 2.1 was 14 Mar 1979
28 * multiple printer logic changed,
29 * printer number sent to daemon.
30 *
31 * 3.0 sjl 6 May 1980
32 * Mods for Version 7, plus portability
33 * (in preparation for UNIX/24V)
34 *
35 * 3.1 sjl Oct 28 1980
36 * Mods for protected spooling area, see note below
37 *
38 * 3.2 sjl Mar 13 1981
39 * Add N card for file names to help out sq
40 *
41 * 3.3 sjl Mar 27 1981 from decvax!shannon
42 * Mods and cleanup for 4bsd.
43 * Send mail option added.
44 *
45 * 4.0 sjl Mar 28 1981
46 * Support multiple printers and daemons through termcap-like
47 * data base
48 *
49 * 4.1 sjl Apr 28 1981
50 * Check for printer being down (mode 0 on device)
51 *
52 * 4.2 was May 1 1981
53 * Clean up handling of printcap database, add more defaults
54 */
55
56char lpr_id[] = "~|^`lpr.c:\t4.2\t1 May 1981\n";
57
c249bc99 58/* lpr.c 4.7 83/01/07 */
51d1035a
BJ
59/*
60 * lpr -- off line print
61 * also known as print
fdaeefb7
BJ
62 *
63 * Allows multiple printers and printers on remote machines by
64 * using information from a printer data base.
51d1035a
BJ
65 */
66
67#include <sys/types.h>
68#include <sys/stat.h>
fdaeefb7 69#include <sys/file.h>
51d1035a
BJ
70#include <signal.h>
71#include <pwd.h>
72#include <stdio.h>
73#include <ctype.h>
74#include "lp.local.h"
75
fdaeefb7
BJ
76char *tfname; /* tmp copy of cf before linking */
77char *cfname; /* daemon control files, linked from tf's */
78char *dfname; /* data files */
51d1035a
BJ
79
80int nact; /* number of jobs to act on */
fdaeefb7 81int tfd; /* control file descriptor */
51d1035a 82int mailflg; /* send mail */
fdaeefb7 83int qflag; /* q job, but don't exec daemon */
51d1035a 84int prflag; /* ``pr'' files */
fdaeefb7 85int rflag; /* remove files upon completion */
e95c0734 86int lflag; /* link flag */
51d1035a
BJ
87char *person; /* user name */
88int inchar; /* location to increment char in file names */
89int ncopies = 1; /* # of copies to make */
90int iflag; /* indentation wanted */
91int indent; /* amount to indent */
fdaeefb7
BJ
92char *DN; /* path name to daemon program */
93char *LP; /* line printer device name */
94char *RM; /* remote machine name if no local printer */
95char *SD; /* spool directory */
51d1035a 96int MX; /* maximum size in blocks of a print file */
fdaeefb7 97int hdr = 1; /* print header or not (default is yes) */
51d1035a 98int user; /* user id */
51d1035a 99char *title; /* pr'ing title */
fdaeefb7
BJ
100char host[32]; /* host name */
101char *class = host; /* class title on header page */
51d1035a
BJ
102char *jobname; /* job name on header page */
103char *name; /* program name */
51d1035a
BJ
104
105char *pgetstr();
51d1035a
BJ
106char *malloc();
107char *getenv();
108char *rindex();
109
fdaeefb7 110/*ARGSUSED*/
51d1035a 111main(argc, argv)
6f038d7b
RC
112 int argc;
113 char *argv[];
51d1035a
BJ
114{
115 register char *arg;
fdaeefb7
BJ
116 int i, f, out();
117 char *printer = NULL;
118 struct stat stb;
51d1035a
BJ
119
120 /*
121 * Strategy to maintain protected spooling area:
122 * 1. Spooling area is writable only by daemon and spooling group
123 * 2. lpr runs setuid root and setgrp spooling group; it uses
124 * root to access any file it wants (verifying things before
125 * with an access call) and group id to know how it should
126 * set up ownership of files in spooling area.
fdaeefb7 127 * 3. Files in spooling area are owned by daemon and spooling
51d1035a 128 * group, with mode 660.
fdaeefb7 129 * 4. lpd runs setuid root and setgrp spooling group to
51d1035a 130 * access files and printer. Users can't get to anything
fdaeefb7 131 * w/o help of lpq and lprm programs.
51d1035a 132 */
6f038d7b 133 if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
51d1035a 134 signal(SIGHUP, out);
6f038d7b 135 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
51d1035a 136 signal(SIGINT, out);
6f038d7b 137 if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
51d1035a 138 signal(SIGQUIT, out);
6f038d7b 139 if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
51d1035a 140 signal(SIGTERM, out);
fdaeefb7
BJ
141
142 gethostname(host, sizeof (host));
143 user = getuid();
51d1035a
BJ
144 name = argv[0];
145
fdaeefb7
BJ
146 while (argc > 1 && (arg = argv[1])[0] == '-') {
147 argc--;
148 argv++;
51d1035a
BJ
149 switch (arg[1]) {
150
fdaeefb7
BJ
151 case 'P': /* specifiy printer name */
152 printer = &arg[2];
51d1035a
BJ
153 break;
154
155 case 'C': /* classification spec */
156 hdr++;
157 if (arg[2])
158 class = &arg[2];
fdaeefb7 159 else if (argc > 0) {
51d1035a 160 argc--;
fdaeefb7 161 class = *++argv;
51d1035a
BJ
162 }
163 break;
164
fdaeefb7
BJ
165 case 'J': /* job name */
166 hdr++;
167 if (arg[2])
51d1035a 168 jobname = &arg[2];
fdaeefb7 169 else if (argc > 0) {
51d1035a 170 argc--;
fdaeefb7 171 jobname = *++argv;
51d1035a
BJ
172 }
173 break;
174
fdaeefb7
BJ
175 case 'T': /* pr's title line */
176 if (arg[2])
177 title = &arg[2];
178 else if (argc > 0) {
179 argc--;
180 title = *++argv;
181 }
51d1035a
BJ
182 break;
183
184 case 'p': /* use pr to print files */
185 prflag++;
186 break;
187
fdaeefb7
BJ
188 case 'r': /* remove file when done */
189 rflag++;
51d1035a
BJ
190 break;
191
fdaeefb7
BJ
192 case 'm': /* send mail when done */
193 mailflg++;
51d1035a
BJ
194 break;
195
fdaeefb7 196 case 'h': /* toggle want of header page */
51d1035a
BJ
197 hdr = !hdr;
198 break;
199
e95c0734
BJ
200 case 'l': /* try to link files */
201 lflag++;
fdaeefb7
BJ
202 break;
203
204 case 'q': /* just q job */
205 qflag++;
206 break;
207
208 case 'i': /* indent output */
209 iflag++;
210 indent = arg[2] ? atoi(&arg[2]) : 8;
211 break;
212
51d1035a
BJ
213 default: /* n copies ? */
214 if (isdigit(arg[1]))
215 ncopies = atoi(&arg[1]);
216 }
51d1035a 217 }
fdaeefb7
BJ
218 if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
219 printer = DEFLP;
51d1035a 220 if (!chkprinter(printer)) {
fdaeefb7 221 printf("%s: unknown printer\n", name, printer);
51d1035a
BJ
222 exit(2);
223 }
fdaeefb7
BJ
224 mktemps();
225 tfd = nfile(tfname);
226 if (jobname == NULL) {
6f038d7b 227 if (argc == 1)
fdaeefb7 228 jobname = &cfname[inchar-2];
51d1035a
BJ
229 else
230 jobname = argv[1];
231 }
232 ident();
233
6f038d7b 234 if (argc == 1)
51d1035a 235 copy(0, " ");
6f038d7b 236 else while (--argc) {
c249bc99 237 if ((i = test(arg = *++argv)) < 0)
fdaeefb7
BJ
238 continue; /* file unreasonable */
239
c249bc99 240 if (i && lflag && linked(arg)) {
fdaeefb7
BJ
241 if (prflag)
242 card('T', title ? title : arg);
243 for (i = 0;i < ncopies; i++)
244 card(prflag ? 'R' : 'F', &dfname[inchar-2]);
245 card('U', &dfname[inchar-2]);
246 card('N', arg);
247 dfname[inchar]++;
51d1035a 248 nact++;
c249bc99 249 } else {
fdaeefb7
BJ
250 if ((f = open(arg, 0)) < 0) {
251 printf("%s: cannot open %s\n", name, arg);
252 continue;
253 }
254 copy(f, arg);
255 (void) close(f);
51d1035a 256 }
fdaeefb7
BJ
257 if (rflag) {
258 register char *cp;
259
260 if ((cp = rindex(arg, '/')) == NULL)
261 f = access(".", 2);
262 else {
263 *cp = '\0';
264 f = access(arg, 2);
265 *cp = '/';
266 }
267 if (f || unlink(arg))
268 printf("%s: cannot remove %s\n", name, arg);
51d1035a 269 }
51d1035a
BJ
270 }
271
6f038d7b 272 if (nact) {
51d1035a 273 tfname[inchar]--;
fdaeefb7
BJ
274 if (link(tfname, cfname) < 0) {
275 printf("%s: cannot rename %s\n", name, cfname);
51d1035a
BJ
276 tfname[inchar]++;
277 out();
278 }
279 unlink(tfname);
fdaeefb7 280 if (qflag) /* just q things up */
51d1035a 281 exit(0);
fdaeefb7
BJ
282 if (*LP && stat(LP, &stb) >= 0 && (stb.st_mode & 0777) == 0) {
283 printf("jobs queued, but line printer is down.\n");
51d1035a
BJ
284 exit(0);
285 }
fdaeefb7
BJ
286 execl(DN, arg = rindex(DN, "/") ? arg+1 : DN, printer, 0);
287 printf("jobs queued, but cannot start daemon.\n");
288 exit(0);
51d1035a
BJ
289 }
290 out();
fdaeefb7 291 /*NOTREACHED*/
51d1035a
BJ
292}
293
fdaeefb7
BJ
294/*
295 * Create the file n and copy from file descriptor f.
296 */
51d1035a 297copy(f, n)
6f038d7b
RC
298 int f;
299 char n[];
51d1035a 300{
fdaeefb7 301 register int fd, i, nr, nc;
51d1035a
BJ
302 char buf[BUFSIZ];
303
fdaeefb7
BJ
304 if (prflag)
305 card('T', title ? title : n);
306 for (i = 0; i < ncopies; i++)
307 card(prflag ? 'R' : 'F', &dfname[inchar-2]);
308 card('U', &dfname[inchar-2]);
309 card('N', n);
310 fd = nfile(dfname);
51d1035a 311 nr = nc = 0;
6f038d7b 312 while ((i = read(f, buf, BUFSIZ)) > 0) {
fdaeefb7 313 if (write(fd, buf, i) != i) {
51d1035a
BJ
314 printf("%s: %s: temp file write error\n", name, n);
315 break;
316 }
317 nc += i;
6f038d7b 318 if (nc >= BUFSIZ) {
51d1035a 319 nc -= BUFSIZ;
6f038d7b 320 if (nr++ > MX) {
51d1035a
BJ
321 printf("%s: %s: copy file is too large\n", name, n);
322 break;
323 }
324 }
325 }
fdaeefb7 326 (void) close(fd);
51d1035a
BJ
327 nact++;
328}
329
fdaeefb7
BJ
330/*
331 * Try and link the file to dfname. Return true if successful.
332 */
333linked(file)
334 register char *file;
335{
336 register char *cp;
337 char buf[BUFSIZ];
338
339 if (link(file, dfname) == 0)
340 return(1);
341 if (*file != '/') {
342 if (getwd(buf) == NULL)
343 return(0);
344 while (file[0] == '.') {
345 switch (file[1]) {
346 case '/':
347 file += 2;
348 continue;
349 case '.':
350 if (file[2] == '/') {
351 if ((cp = rindex(buf, '/')) != NULL)
352 *cp = '\0';
353 file += 3;
354 continue;
355 }
356 }
357 break;
358 }
359 strcat(buf, "/");
360 strcat(buf, file);
361 file = buf;
362 }
363 return(symlink(file, dfname) == 0);
364}
365
366/*
367 * Put a line into the control file.
368 */
51d1035a 369card(c, p2)
6f038d7b 370 register char c, *p2;
51d1035a
BJ
371{
372 char buf[BUFSIZ];
373 register char *p1 = buf;
fdaeefb7 374 register int len = 2;
51d1035a
BJ
375
376 *p1++ = c;
6f038d7b 377 while ((c = *p2++) != '\0') {
51d1035a 378 *p1++ = c;
fdaeefb7 379 len++;
51d1035a
BJ
380 }
381 *p1++ = '\n';
fdaeefb7 382 write(tfd, buf, len);
51d1035a
BJ
383}
384
fdaeefb7
BJ
385/*
386 * Get the identity of the person doing the lpr and save it in the
387 * control file.
388 */
51d1035a
BJ
389ident()
390{
391 extern char *getlogin();
392 extern struct passwd *getpwuid();
393 struct passwd *pw;
394 extern char *itoa();
395
396 if ((person = getlogin()) == NULL) {
397 if ((pw = getpwuid(user)) == NULL)
398 person = "Unknown User";
399 else
400 person = pw->pw_name;
401 }
402
fdaeefb7
BJ
403 card('H', host);
404 card('P', person);
51d1035a 405 if (hdr) {
fdaeefb7
BJ
406 card('J', jobname);
407 card('C', class);
51d1035a
BJ
408 card('L', person);
409 }
410 if (iflag)
411 card('I', itoa(indent));
412 if (mailflg)
413 card('M', person);
414}
415
fdaeefb7
BJ
416/*
417 * Create a new file in the spool directory.
418 */
419
51d1035a 420nfile(n)
6f038d7b 421 char *n;
51d1035a
BJ
422{
423 register f;
fdaeefb7 424 int oldumask = umask(0); /* should block signals */
51d1035a 425
6f038d7b
RC
426 f = creat(n, FILMOD);
427 (void) umask(oldumask);
428 if (f < 0) {
51d1035a
BJ
429 printf("%s: cannot create %s\n", name, n);
430 out();
431 }
fdaeefb7 432 if (chown(n, user, getegid()) < 0) {
51d1035a
BJ
433 unlink(n);
434 printf("%s: cannot chown %s\n", name, n);
435 out();
436 }
437 n[inchar]++;
fdaeefb7 438 return(f);
51d1035a
BJ
439}
440
fdaeefb7
BJ
441/*
442 * Cleanup after interrupts and errors.
443 */
51d1035a
BJ
444out()
445{
446 register i;
447
448 signal(SIGHUP, SIG_IGN);
449 signal(SIGINT, SIG_IGN);
450 signal(SIGQUIT, SIG_IGN);
451 signal(SIGTERM, SIG_IGN);
452 i = inchar;
453 if (tfname)
6f038d7b 454 while (tfname[i] != 'A') {
51d1035a
BJ
455 tfname[i]--;
456 unlink(tfname);
457 }
458 if (cfname)
6f038d7b 459 while (cfname[i] != 'A') {
51d1035a
BJ
460 cfname[i]--;
461 unlink(cfname);
462 }
51d1035a 463 if (dfname)
6f038d7b 464 while (dfname[i] != 'A') {
51d1035a
BJ
465 dfname[i]--;
466 unlink(dfname);
467 }
468 exit();
469}
470
fdaeefb7
BJ
471/*
472 * Test to see if this is a printable file.
c249bc99 473 * Return -1 if it is not, 1 if it's publically readable, else 0
fdaeefb7 474 */
51d1035a 475test(file)
6f038d7b 476 char *file;
51d1035a 477{
fdaeefb7
BJ
478 struct exec execb;
479 struct stat statb;
51d1035a
BJ
480 int fd;
481
482 if (access(file, 4) < 0) {
483 printf("%s: cannot access %s\n", name, file);
fdaeefb7 484 return(-1);
51d1035a 485 }
fdaeefb7 486 if (stat(file, &statb) < 0) {
51d1035a 487 printf("%s: cannot stat %s\n", name, file);
fdaeefb7 488 return(-1);
51d1035a 489 }
fdaeefb7 490 if ((statb.st_mode & S_IFMT) == S_IFDIR) {
51d1035a 491 printf("%s: %s is a directory\n", name, file);
fdaeefb7 492 return(-1);
51d1035a 493 }
6f038d7b 494 if ((fd = open(file, 0)) < 0) {
51d1035a 495 printf("%s: cannot open %s\n", name, file);
fdaeefb7 496 return(-1);
51d1035a 497 }
fdaeefb7
BJ
498 if (read(fd, &execb, sizeof(execb)) == sizeof(execb))
499 switch(execb.a_magic) {
51d1035a
BJ
500 case A_MAGIC1:
501 case A_MAGIC2:
502 case A_MAGIC3:
503#ifdef A_MAGIC4
504 case A_MAGIC4:
505#endif
506 printf("%s: %s is an executable program", name, file);
507 goto error1;
508
509 case ARMAG:
510 printf("%s: %s is an archive file", name, file);
511 goto error1;
512 }
fdaeefb7
BJ
513 (void) close(fd);
514 if (rflag) { /* check to make sure user can delete this file */
515 register char *cp = rindex(file, '/');
516
517 if (cp == NULL)
518 file = ".";
519 else
520 *cp = '\0';
521 if (access(file, 2) < 0) {
522 *cp = '/';
523 printf("%s: cannot remove %s\n", name, file);
524 return(-1);
525 }
526 *cp = '/';
527 }
c249bc99
RC
528 if (statb.st_mode & 04)
529 return(1);
fdaeefb7 530 return(0);
51d1035a 531
51d1035a
BJ
532error1:
533 printf(" and is unprintable\n");
fdaeefb7
BJ
534 (void) close(fd);
535 return(-1);
51d1035a
BJ
536}
537
538/*
539 * itoa - integer to string conversion
540 */
541char *
542itoa(i)
6f038d7b 543 register int i;
51d1035a
BJ
544{
545 static char b[10] = "########";
546 register char *p;
547
548 p = &b[8];
549 do
550 *p-- = i%10 + '0';
551 while (i /= 10);
fdaeefb7 552 return(++p);
51d1035a
BJ
553}
554
555/*
556 * Perform lookup for printer name or abbreviation --
51d1035a
BJ
557 */
558chkprinter(s)
6f038d7b 559 register char *s;
51d1035a
BJ
560{
561 static char buf[BUFSIZ/2];
562 char b[BUFSIZ];
563 int stat;
564 char *bp = buf;
565
566 if ((stat = pgetent(b, s)) < 0) {
fdaeefb7 567 printf("%s: can't open printer description file\n", name);
51d1035a
BJ
568 exit(3);
569 } else if (stat == 0)
fdaeefb7 570 return(0);
51d1035a
BJ
571 if ((DN = pgetstr("dn", &bp)) == NULL)
572 DN = DEFDAEMON;
573 if ((LP = pgetstr("lp", &bp)) == NULL)
574 LP = DEFDEVLP;
fdaeefb7
BJ
575 if ((SD = pgetstr("sd", &bp)) == NULL)
576 SD = DEFSPOOL;
51d1035a
BJ
577 if ((MX = pgetnum("mx")) < 0)
578 MX = DEFMX;
fdaeefb7
BJ
579 RM = pgetstr("rm", &bp);
580 return(1);
581}
582
583/*
584 * Make the temp files.
585 */
586mktemps()
587{
c249bc99 588 register int c, len;
fdaeefb7
BJ
589 int n;
590 char buf[BUFSIZ], *mktemp();
c249bc99 591 FILE *fp;
fdaeefb7
BJ
592
593 (void) sprintf(buf, "%s/.seq", SD);
c249bc99
RC
594 if ((fp = fopen(buf, "r+")) == NULL) {
595 printf("%s: cannot create %s\n", name, buf);
596 exit(1);
597 } else {
598 setbuf(fp, buf);
599 if (flock(fileno(fp), FEXLOCK)) {
fdaeefb7
BJ
600 printf("%s: cannot lock %s\n", name, buf);
601 exit(1);
602 }
c249bc99
RC
603 n = 0;
604 while ((c = getc(fp)) >= '0' && c <= '9')
605 n = n * 10 + (c - '0');
fdaeefb7
BJ
606 }
607 len = strlen(SD) + strlen(host) + 8;
608 tfname = mktemp("tf", n, len);
609 cfname = mktemp("cf", n, len);
610 dfname = mktemp("df", n, len);
611 inchar = strlen(SD) + 3;
612 n = (n + 1) % 1000;
c249bc99
RC
613 (void) fseek(fp, 0L, 0);
614 fprintf(fp, "%d\n", n);
615 (void) fclose(fp);
51d1035a
BJ
616}
617
618/*
fdaeefb7 619 * Make a temp file name.
51d1035a
BJ
620 */
621char *
fdaeefb7
BJ
622mktemp(id, num, len)
623 char *id;
624 int num, len;
51d1035a
BJ
625{
626 register char *s;
627
fdaeefb7
BJ
628 if ((s = malloc(len)) == NULL) {
629 printf("%s: out of memory\n", name);
51d1035a
BJ
630 exit(1);
631 }
fdaeefb7
BJ
632 (void) sprintf(s, "%s/%sA%03d%s", SD, id, num, host);
633 return(s);
51d1035a 634}