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