Style(9) cleanup.
[dragonfly.git] / usr.sbin / lpr / lpr / lpr.c
CommitLineData
984263bc
MD
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 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
1de703da
MD
38 *
39 * @(#) Copyright (c) 1983, 1989, 1993 The Regents of the University of California. All rights reserved.
40 * @(#)from: lpr.c 8.4 (Berkeley) 4/28/95
41 * $FreeBSD: src/usr.sbin/lpr/lpr/lpr.c,v 1.32.2.11 2002/04/28 23:40:23 gad Exp $
be6b9a2e 42 * $DragonFly: src/usr.sbin/lpr/lpr/lpr.c,v 1.3 2004/03/22 22:32:51 cpressey Exp $
984263bc
MD
43 */
44
984263bc
MD
45/*
46 * lpr -- off line print
47 *
48 * Allows multiple printers and printers on remote machines by
49 * using information from a printer data base.
50 */
51
52#include <sys/param.h>
53#include <sys/stat.h>
54
55#include <netinet/in.h> /* N_BADMAG uses ntohl() */
56
57#include <dirent.h>
58#include <fcntl.h>
59#include <a.out.h>
60#include <err.h>
61#include <locale.h>
62#include <signal.h>
63#include <syslog.h>
64#include <pwd.h>
65#include <grp.h>
66#include <unistd.h>
67#include <stdlib.h>
68#include <stdio.h>
69#include <ctype.h>
70#include <string.h>
71#include "lp.h"
72#include "lp.local.h"
73#include "pathnames.h"
74
75static char *cfname; /* daemon control files, linked from tf's */
76static char *class = local_host; /* class title on header page */
77static char *dfname; /* data files */
78static char *fonts[4]; /* troff font names */
79static char format = 'f'; /* format char for printing files */
80static int hdr = 1; /* print header or not (default is yes) */
81static int iflag; /* indentation wanted */
82static int inchar; /* location to increment char in file names */
83static int indent; /* amount to indent */
84static const char *jobname; /* job name on header page */
85static int mailflg; /* send mail */
86static int nact; /* number of jobs to act on */
87static int ncopies = 1; /* # of copies to make */
88static char *lpr_username; /* person sending the print job(s) */
89static int qflag; /* q job, but don't exec daemon */
90static int rflag; /* remove files upon completion */
91static int sflag; /* symbolic link flag */
92static int tfd; /* control file descriptor */
93static char *tfname; /* tmp copy of cf before linking */
94static char *title; /* pr'ing title */
95static char *locale; /* pr'ing locale */
96static int userid; /* user id */
97static char *Uflag; /* user name specified with -U flag */
98static char *width; /* width for versatec printing */
99static char *Zflag; /* extra filter options for LPRng servers */
100
101static struct stat statb;
102
103static void card(int _c, const char *_p2);
104static int checkwriteperm(const char *_file, const char *_directory);
105static void chkprinter(const char *_ptrname, struct printer *_pp);
106static void cleanup(int _signo);
107static void copy(const struct printer *_pp, int _f, const char _n[]);
108static char *itoa(int _i);
109static const char *linked(const char *_file);
110int main(int _argc, char *_argv[]);
111static char *lmktemp(const struct printer *_pp, const char *_id,
112 int _num, int len);
113static void mktemps(const struct printer *_pp);
114static int nfile(char *_n);
115static int test(const char *_file);
116static void usage(void);
117
118uid_t uid, euid;
119
120int
121main(int argc, char *argv[])
122{
123 struct passwd *pw;
124 struct group *gptr;
125 const char *arg, *cp, *printer;
126 char *p;
127 char buf[BUFSIZ];
128 int c, i, f, errs;
129 int ret, didlink;
130 struct stat stb;
131 struct stat statb1, statb2;
132 struct printer myprinter, *pp = &myprinter;
133
134 printer = NULL;
135 euid = geteuid();
136 uid = getuid();
137 seteuid(uid);
138 if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
139 signal(SIGHUP, cleanup);
140 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
141 signal(SIGINT, cleanup);
142 if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
143 signal(SIGQUIT, cleanup);
144 if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
145 signal(SIGTERM, cleanup);
146
147 progname = argv[0];
148 gethostname(local_host, sizeof(local_host));
149 openlog("lpd", 0, LOG_LPR);
150
151 errs = 0;
152 while ((c = getopt(argc, argv,
153 ":#:1:2:3:4:C:J:L:P:T:U:Z:cdfghi:lnmprstvw:"))
154 != -1)
155 switch (c) {
156 case '#': /* n copies */
157 i = strtol(optarg, &p, 10);
158 if (*p)
159 errx(1, "Bad argument to -#, number expected");
160 if (i > 0)
161 ncopies = i;
162 break;
163
164 case '1': /* troff fonts */
165 case '2':
166 case '3':
167 case '4':
168 fonts[optopt - '1'] = optarg;
169 break;
170
171 case 'C': /* classification spec */
172 hdr++;
173 class = optarg;
174 break;
175
176 case 'J': /* job name */
177 hdr++;
178 jobname = optarg;
179 break;
180
181 case 'P': /* specifiy printer name */
182 printer = optarg;
183 break;
184
185 case 'L': /* pr's locale */
186 locale = optarg;
187 break;
188
189 case 'T': /* pr's title line */
190 title = optarg;
191 break;
192
193 case 'U': /* user name */
194 hdr++;
195 Uflag = optarg;
196 break;
197
198 case 'Z':
199 Zflag = optarg;
200 break;
201
202 case 'c': /* print cifplot output */
203 case 'd': /* print tex output (dvi files) */
204 case 'g': /* print graph(1G) output */
205 case 'l': /* literal output */
206 case 'n': /* print ditroff output */
207 case 't': /* print troff output (cat files) */
208 case 'p': /* print using ``pr'' */
209 case 'v': /* print vplot output */
210 format = optopt;
211 break;
212
213 case 'f': /* print fortran output */
214 format = 'r';
215 break;
216
217 case 'h': /* nulifiy header page */
218 hdr = 0;
219 break;
220
221 case 'i': /* indent output */
222 iflag++;
223 indent = strtol(optarg, &p, 10);
224 if (*p)
225 errx(1, "Bad argument to -i, number expected");
226 break;
227
228 case 'm': /* send mail when done */
229 mailflg++;
230 break;
231
232 case 'q': /* just queue job */
233 qflag++;
234 break;
235
236 case 'r': /* remove file when done */
237 rflag++;
238 break;
239
240 case 's': /* try to link files */
241 sflag++;
242 break;
243
244 case 'w': /* versatec page width */
245 width = optarg;
246 break;
247
248 case ':': /* catch "missing argument" error */
249 if (optopt == 'i') {
250 iflag++; /* -i without args is valid */
251 indent = 8;
252 } else
253 errs++;
254 break;
255
256 default:
257 errs++;
258 }
259 argc -= optind;
260 argv += optind;
261 if (errs)
262 usage();
263 if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
264 printer = DEFLP;
265 chkprinter(printer, pp);
266 if (pp->no_copies && ncopies > 1)
267 errx(1, "multiple copies are not allowed");
268 if (pp->max_copies > 0 && ncopies > pp->max_copies)
269 errx(1, "only %ld copies are allowed", pp->max_copies);
270 /*
271 * Get the identity of the person doing the lpr using the same
272 * algorithm as lprm. Actually, not quite -- lprm will override
273 * the login name with "root" if the user is running as root;
274 * the daemon actually checks for the string "root" in its
275 * permission checking. Sigh.
276 */
277 userid = getuid();
278 if (Uflag) {
279 if (userid != 0 && userid != pp->daemon_user)
280 errx(1, "only privileged users may use the `-U' flag");
281 lpr_username = Uflag; /* -U person doing 'lpr' */
282 } else {
283 lpr_username = getlogin(); /* person doing 'lpr' */
284 if (userid != pp->daemon_user || lpr_username == 0) {
285 if ((pw = getpwuid(userid)) == NULL)
286 errx(1, "Who are you?");
287 lpr_username = pw->pw_name;
288 }
289 }
290
291 /*
292 * Check for restricted group access.
293 */
294 if (pp->restrict_grp != NULL && userid != pp->daemon_user) {
295 if ((gptr = getgrnam(pp->restrict_grp)) == NULL)
296 errx(1, "Restricted group specified incorrectly");
297 if (gptr->gr_gid != getgid()) {
298 while (*gptr->gr_mem != NULL) {
299 if ((strcmp(lpr_username, *gptr->gr_mem)) == 0)
300 break;
301 gptr->gr_mem++;
302 }
303 if (*gptr->gr_mem == NULL)
304 errx(1, "Not a member of the restricted group");
305 }
306 }
307 /*
308 * Check to make sure queuing is enabled if userid is not root.
309 */
310 lock_file_name(pp, buf, sizeof buf);
311 if (userid && stat(buf, &stb) == 0 && (stb.st_mode & LFM_QUEUE_DIS))
312 errx(1, "Printer queue is disabled");
313 /*
314 * Initialize the control file.
315 */
316 mktemps(pp);
317 tfd = nfile(tfname);
318 seteuid(euid);
319 (void) fchown(tfd, pp->daemon_user, -1);
320 /* owned by daemon for protection */
321 seteuid(uid);
322 card('H', local_host);
323 card('P', lpr_username);
324 card('C', class);
325 if (hdr && !pp->no_header) {
326 if (jobname == NULL) {
327 if (argc == 0)
328 jobname = "stdin";
329 else
330 jobname = ((arg = strrchr(argv[0], '/'))
331 ? arg + 1 : argv[0]);
332 }
333 card('J', jobname);
334 card('L', lpr_username);
335 }
336 if (format != 'p' && Zflag != 0)
337 card('Z', Zflag);
338 if (iflag)
339 card('I', itoa(indent));
340 if (mailflg)
341 card('M', lpr_username);
342 if (format == 't' || format == 'n' || format == 'd')
343 for (i = 0; i < 4; i++)
344 if (fonts[i] != NULL)
345 card('1'+i, fonts[i]);
346 if (width != NULL)
347 card('W', width);
348 /*
349 * XXX
350 * Our use of `Z' here is incompatible with LPRng's
351 * use. We assume that the only use of our existing
352 * `Z' card is as shown for `p' format (pr) files.
353 */
354 if (format == 'p') {
355 char *s;
356
357 if (locale)
358 card('Z', locale);
359 else if ((s = setlocale(LC_TIME, "")) != NULL)
360 card('Z', s);
361 }
362
363 /*
364 * Read the files and spool them.
365 */
366 if (argc == 0)
367 copy(pp, 0, " ");
368 else while (argc--) {
369 if (argv[0][0] == '-' && argv[0][1] == '\0') {
370 /* use stdin */
371 copy(pp, 0, " ");
372 argv++;
373 continue;
374 }
375 if ((f = test(arg = *argv++)) < 0)
376 continue; /* file unreasonable */
377
378 if (sflag && (cp = linked(arg)) != NULL) {
379 (void) snprintf(buf, sizeof(buf), "%d %d", statb.st_dev,
380 statb.st_ino);
381 card('S', buf);
382 if (format == 'p')
383 card('T', title ? title : arg);
384 for (i = 0; i < ncopies; i++)
385 card(format, &dfname[inchar-2]);
386 card('U', &dfname[inchar-2]);
387 if (f)
388 card('U', cp);
389 card('N', arg);
390 dfname[inchar]++;
391 nact++;
392 continue;
393 }
394 if (sflag)
395 printf("%s: %s: not linked, copying instead\n",
396 progname, arg);
397
398 if (f) {
399 /*
400 * The user wants the file removed after it is copied
401 * to the spool area, so see if the file can be moved
402 * instead of copy/unlink'ed. This is much faster and
403 * uses less spool space than copying the file. This
404 * can be very significant when running services like
405 * samba, pcnfs, CAP, et al.
406 */
407 seteuid(euid);
408 didlink = 0;
409 /*
410 * There are several things to check to avoid any
411 * security issues. Some of these are redundant
412 * under BSD's, but are necessary when lpr is built
413 * under some other OS's (which I do do...)
414 */
415 if (lstat(arg, &statb1) < 0)
416 goto nohardlink;
417 if (S_ISLNK(statb1.st_mode))
418 goto nohardlink;
419 if (link(arg, dfname) != 0)
420 goto nohardlink;
421 didlink = 1;
422 /*
423 * Make sure the user hasn't tried to trick us via
424 * any race conditions
425 */
426 if (lstat(dfname, &statb2) < 0)
427 goto nohardlink;
428 if (statb1.st_dev != statb2.st_dev)
429 goto nohardlink;
430 if (statb1.st_ino != statb2.st_ino)
431 goto nohardlink;
432 /*
433 * Skip if the file already had multiple hard links,
434 * because changing the owner and access-bits would
435 * change ALL versions of the file
436 */
437 if (statb2.st_nlink > 2)
438 goto nohardlink;
439 /*
440 * If we can access and remove the original file
441 * without special setuid-ness then this method is
442 * safe. Otherwise, abandon the move and fall back
443 * to the (usual) copy method.
444 */
445 seteuid(uid);
446 ret = access(dfname, R_OK);
447 if (ret == 0)
448 ret = unlink(arg);
449 seteuid(euid);
450 if (ret != 0)
451 goto nohardlink;
452 /*
453 * Unlink of user file was successful. Change the
454 * owner and permissions, add entries to the control
455 * file, and skip the file copying step.
456 */
457 chown(dfname, pp->daemon_user, getegid());
458 chmod(dfname, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
459 seteuid(uid);
460 if (format == 'p')
461 card('T', title ? title : arg);
462 for (i = 0; i < ncopies; i++)
463 card(format, &dfname[inchar-2]);
464 card('U', &dfname[inchar-2]);
465 card('N', arg);
466 nact++;
467 continue;
468 nohardlink:
469 if (didlink)
470 unlink(dfname);
471 seteuid(uid); /* restore old uid */
472 } /* end: if (f) */
473
474 if ((i = open(arg, O_RDONLY)) < 0) {
475 printf("%s: cannot open %s\n", progname, arg);
476 } else {
477 copy(pp, i, arg);
478 (void) close(i);
479 if (f && unlink(arg) < 0)
480 printf("%s: %s: not removed\n", progname, arg);
481 }
482 }
483
484 if (nact) {
485 (void) close(tfd);
486 tfname[inchar]--;
487 /*
488 * Touch the control file to fix position in the queue.
489 */
490 seteuid(euid);
491 if ((tfd = open(tfname, O_RDWR)) >= 0) {
492 char touch_c;
493
494 if (read(tfd, &touch_c, 1) == 1 &&
495 lseek(tfd, (off_t)0, 0) == 0 &&
496 write(tfd, &touch_c, 1) != 1) {
497 printf("%s: cannot touch %s\n", progname,
498 tfname);
499 tfname[inchar]++;
500 cleanup(0);
501 }
502 (void) close(tfd);
503 }
504 if (link(tfname, cfname) < 0) {
505 printf("%s: cannot rename %s\n", progname, cfname);
506 tfname[inchar]++;
507 cleanup(0);
508 }
509 unlink(tfname);
510 seteuid(uid);
511 if (qflag) /* just q things up */
512 exit(0);
513 if (!startdaemon(pp))
514 printf("jobs queued, but cannot start daemon.\n");
515 exit(0);
516 }
517 cleanup(0);
518 return (1);
519 /* NOTREACHED */
520}
521
522/*
523 * Create the file n and copy from file descriptor f.
524 */
525static void
526copy(const struct printer *pp, int f, const char n[])
527{
be6b9a2e 528 int fd, i, nr, nc;
984263bc
MD
529 char buf[BUFSIZ];
530
531 if (format == 'p')
532 card('T', title ? title : n);
533 for (i = 0; i < ncopies; i++)
534 card(format, &dfname[inchar-2]);
535 card('U', &dfname[inchar-2]);
536 card('N', n);
537 fd = nfile(dfname);
538 nr = nc = 0;
539 while ((i = read(f, buf, BUFSIZ)) > 0) {
540 if (write(fd, buf, i) != i) {
541 printf("%s: %s: temp file write error\n", progname, n);
542 break;
543 }
544 nc += i;
545 if (nc >= BUFSIZ) {
546 nc -= BUFSIZ;
547 nr++;
548 if (pp->max_blocks > 0 && nr > pp->max_blocks) {
549 printf("%s: %s: copy file is too large\n",
550 progname, n);
551 break;
552 }
553 }
554 }
555 (void) close(fd);
556 if (nc==0 && nr==0)
557 printf("%s: %s: empty input file\n", progname,
558 f ? n : "stdin");
559 else
560 nact++;
561}
562
563/*
564 * Try and link the file to dfname. Return a pointer to the full
565 * path name if successful.
566 */
567static const char *
568linked(const char *file)
569{
be6b9a2e 570 char *cp;
984263bc 571 static char buf[MAXPATHLEN];
be6b9a2e 572 int ret;
984263bc
MD
573
574 if (*file != '/') {
575 if (getcwd(buf, sizeof(buf)) == NULL)
576 return(NULL);
577 while (file[0] == '.') {
578 switch (file[1]) {
579 case '/':
580 file += 2;
581 continue;
582 case '.':
583 if (file[2] == '/') {
584 if ((cp = strrchr(buf, '/')) != NULL)
585 *cp = '\0';
586 file += 3;
587 continue;
588 }
589 }
590 break;
591 }
592 strncat(buf, "/", sizeof(buf) - strlen(buf) - 1);
593 strncat(buf, file, sizeof(buf) - strlen(buf) - 1);
594 file = buf;
595 }
596 seteuid(euid);
597 ret = symlink(file, dfname);
598 seteuid(uid);
599 return(ret ? NULL : file);
600}
601
602/*
603 * Put a line into the control file.
604 */
605static void
606card(int c, const char *p2)
607{
608 char buf[BUFSIZ];
be6b9a2e 609 char *p1 = buf;
984263bc
MD
610 size_t len = 2;
611
612 *p1++ = c;
613 while ((c = *p2++) != '\0' && len < sizeof(buf)) {
614 *p1++ = (c == '\n') ? ' ' : c;
615 len++;
616 }
617 *p1++ = '\n';
618 write(tfd, buf, len);
619}
620
621/*
622 * Create a new file in the spool directory.
623 */
624static int
625nfile(char *n)
626{
be6b9a2e 627 int f;
984263bc
MD
628 int oldumask = umask(0); /* should block signals */
629
630 seteuid(euid);
631 f = open(n, O_WRONLY | O_EXCL | O_CREAT, FILMOD);
632 (void) umask(oldumask);
633 if (f < 0) {
634 printf("%s: cannot create %s\n", progname, n);
635 cleanup(0);
636 }
637 if (fchown(f, userid, -1) < 0) {
638 printf("%s: cannot chown %s\n", progname, n);
639 cleanup(0); /* cleanup does exit */
640 }
641 seteuid(uid);
642 if (++n[inchar] > 'z') {
643 if (++n[inchar-2] == 't') {
644 printf("too many files - break up the job\n");
645 cleanup(0);
646 }
647 n[inchar] = 'A';
648 } else if (n[inchar] == '[')
649 n[inchar] = 'a';
650 return(f);
651}
652
653/*
654 * Cleanup after interrupts and errors.
655 */
656static void
657cleanup(int signo __unused)
658{
be6b9a2e 659 int i;
984263bc
MD
660
661 signal(SIGHUP, SIG_IGN);
662 signal(SIGINT, SIG_IGN);
663 signal(SIGQUIT, SIG_IGN);
664 signal(SIGTERM, SIG_IGN);
665 i = inchar;
666 seteuid(euid);
667 if (tfname)
668 do
669 unlink(tfname);
670 while (tfname[i]-- != 'A');
671 if (cfname)
672 do
673 unlink(cfname);
674 while (cfname[i]-- != 'A');
675 if (dfname)
676 do {
677 do
678 unlink(dfname);
679 while (dfname[i]-- != 'A');
680 dfname[i] = 'z';
681 } while (dfname[i-2]-- != 'd');
682 exit(1);
683}
684
685/*
686 * Test to see if this is a printable file.
687 * Return -1 if it is not, 0 if its printable, and 1 if
688 * we should remove it after printing.
689 */
690static int
691test(const char *file)
692{
693 struct exec execb;
694 size_t dlen;
695 int fd;
696 char *cp, *dirpath;
697
698 if (access(file, 4) < 0) {
699 printf("%s: cannot access %s\n", progname, file);
700 return(-1);
701 }
702 if (stat(file, &statb) < 0) {
703 printf("%s: cannot stat %s\n", progname, file);
704 return(-1);
705 }
706 if ((statb.st_mode & S_IFMT) == S_IFDIR) {
707 printf("%s: %s is a directory\n", progname, file);
708 return(-1);
709 }
710 if (statb.st_size == 0) {
711 printf("%s: %s is an empty file\n", progname, file);
712 return(-1);
713 }
714 if ((fd = open(file, O_RDONLY)) < 0) {
715 printf("%s: cannot open %s\n", progname, file);
716 return(-1);
717 }
718 /*
719 * XXX Shall we add a similar test for ELF?
720 */
721 if (read(fd, &execb, sizeof(execb)) == sizeof(execb) &&
722 !N_BADMAG(execb)) {
723 printf("%s: %s is an executable program", progname, file);
724 goto error1;
725 }
726 (void) close(fd);
727 if (rflag) {
728 /*
729 * aside: note that 'cp' is technically a 'const char *'
730 * (because it points into 'file'), even though strrchr
731 * returns a value of type 'char *'.
732 */
733 if ((cp = strrchr(file, '/')) == NULL) {
734 if (checkwriteperm(file,".") == 0)
735 return(1);
736 } else {
737 if (cp == file) {
738 fd = checkwriteperm(file,"/");
739 } else {
740 /* strlcpy will change the '/' to '\0' */
741 dlen = cp - file + 1;
742 dirpath = malloc(dlen);
743 strlcpy(dirpath, file, dlen);
744 fd = checkwriteperm(file, dirpath);
745 free(dirpath);
746 }
747 if (fd == 0)
748 return(1);
749 }
750 printf("%s: %s: is not removable by you\n", progname, file);
751 }
752 return(0);
753
754error1:
755 printf(" and is unprintable\n");
756 (void) close(fd);
757 return(-1);
758}
759
760static int
761checkwriteperm(const char *file, const char *directory)
762{
763 struct stat stats;
764 if (access(directory, W_OK) == 0) {
765 stat(directory, &stats);
766 if (stats.st_mode & S_ISVTX) {
767 stat(file, &stats);
768 if(stats.st_uid == userid) {
769 return(0);
770 }
771 } else return(0);
772 }
773 return(-1);
774}
775
776/*
777 * itoa - integer to string conversion
778 */
779static char *
780itoa(int i)
781{
782 static char b[10] = "########";
be6b9a2e 783 char *p;
984263bc
MD
784
785 p = &b[8];
786 do
787 *p-- = i%10 + '0';
788 while (i /= 10);
789 return(++p);
790}
791
792/*
793 * Perform lookup for printer name or abbreviation --
794 */
795static void
796chkprinter(const char *ptrname, struct printer *pp)
797{
798 int status;
799
800 init_printer(pp);
801 status = getprintcap(ptrname, pp);
802 switch(status) {
803 case PCAPERR_OSERR:
804 case PCAPERR_TCLOOP:
805 errx(1, "%s: %s", ptrname, pcaperr(status));
806 case PCAPERR_NOTFOUND:
807 errx(1, "%s: unknown printer", ptrname);
808 case PCAPERR_TCOPEN:
809 warnx("%s: unresolved tc= reference(s)", ptrname);
810 }
811}
812
813/*
814 * Tell the user what we wanna get.
815 */
816static void
817usage(void)
818{
819 fprintf(stderr, "%s\n",
820"usage: lpr [-Pprinter] [-#num] [-C class] [-J job] [-T title] [-U user]\n"
821 "\t[-Z daemon-options] [-i[numcols]] [-i[numcols]] [-1234 font]\n"
822 "\t[-L locale] [-wnum] [-cdfghlnmprstv] [name ...]");
823 exit(1);
824}
825
826
827/*
828 * Make the temp files.
829 */
830static void
831mktemps(const struct printer *pp)
832{
be6b9a2e
CP
833 int len, fd, n;
834 char *cp;
984263bc
MD
835 char buf[BUFSIZ];
836
837 (void) snprintf(buf, sizeof(buf), "%s/.seq", pp->spool_dir);
838 seteuid(euid);
839 if ((fd = open(buf, O_RDWR|O_CREAT, 0661)) < 0) {
840 printf("%s: cannot create %s\n", progname, buf);
841 exit(1);
842 }
843 if (flock(fd, LOCK_EX)) {
844 printf("%s: cannot lock %s\n", progname, buf);
845 exit(1);
846 }
847 seteuid(uid);
848 n = 0;
849 if ((len = read(fd, buf, sizeof(buf))) > 0) {
850 for (cp = buf; len--; ) {
851 if (*cp < '0' || *cp > '9')
852 break;
853 n = n * 10 + (*cp++ - '0');
854 }
855 }
856 len = strlen(pp->spool_dir) + strlen(local_host) + 8;
857 tfname = lmktemp(pp, "tf", n, len);
858 cfname = lmktemp(pp, "cf", n, len);
859 dfname = lmktemp(pp, "df", n, len);
860 inchar = strlen(pp->spool_dir) + 3;
861 n = (n + 1) % 1000;
862 (void) lseek(fd, (off_t)0, 0);
863 snprintf(buf, sizeof(buf), "%03d\n", n);
864 (void) write(fd, buf, strlen(buf));
865 (void) close(fd); /* unlocks as well */
866}
867
868/*
869 * Make a temp file name.
870 */
871static char *
872lmktemp(const struct printer *pp, const char *id, int num, int len)
873{
be6b9a2e 874 char *s;
984263bc
MD
875
876 if ((s = malloc(len)) == NULL)
877 errx(1, "out of memory");
878 (void) snprintf(s, len, "%s/%sA%03d%s", pp->spool_dir, id, num,
879 local_host);
880 return(s);
881}