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