1 /* $OpenBSD: printer.c,v 1.4 2022/12/28 21:30:17 jmc Exp $ */
2
3 /*
4 * Copyright (c) 2017 Eric Faurot <eric@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <sys/tree.h>
22 #include <sys/wait.h>
23
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <limits.h>
27 #include <pwd.h>
28 #include <signal.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <syslog.h>
33 #include <unistd.h>
34 #include <vis.h>
35
36 #include "lpd.h"
37 #include "lp.h"
38 #include "log.h"
39
40 #define RETRY_MAX 5
41
42 #define JOB_OK 0
43 #define JOB_AGAIN 1
44 #define JOB_IGNORE 2
45 #define JOB_ERROR 3
46
47 enum {
48 OK = 0,
49 ERR_TRANSIENT, /* transient error */
50 ERR_ACCOUNT, /* account required on the local machine */
51 ERR_ACCESS, /* cannot read file */
52 ERR_INODE, /* inode changed */
53 ERR_NOIMPL, /* unimplemented feature */
54 ERR_REJECTED, /* remote server rejected a job */
55 ERR_ERROR, /* filter report an error */
56 ERR_FILTER, /* filter return invalid status */
57 };
58
59 struct job {
60 char *class;
61 char *host;
62 char *literal;
63 char *mail;
64 char *name;
65 char *person;
66 char *statinfo;
67 char *title;
68 int indent;
69 int pagewidth;
70 };
71
72 struct prnstate {
73 int pfd; /* printer fd */
74 int ofilter; /* use output filter when printing */
75 int ofd; /* output filter fd */
76 pid_t opid; /* output filter process */
77 int tof; /* true if at top of form */
78 int count; /* number of printed files */
79 char efile[64]; /* filename for filter stderr */
80 };
81
82 static void sighandler(int);
83 static char *xstrdup(const char *);
84
85 static int openfile(const char *, const char *, struct stat *, FILE **);
86 static int printjob(const char *, int);
87 static void printbanner(struct job *);
88 static int printfile(struct job *, int, const char *, const char *);
89 static int sendjob(const char *, int);
90 static int sendcmd(const char *, ...);
91 static int sendfile(int, const char *, const char *);
92 static int recvack(void);
93 static void mailreport(struct job *, int);
94
95 static void prn_open(void);
96 static int prn_connect(void);
97 static void prn_close(void);
98 static int prn_fstart(void);
99 static void prn_fsuspend(void);
100 static void prn_fresume(void);
101 static void prn_fclose(void);
102 static int prn_formfeed(void);
103 static int prn_write(const char *, size_t);
104 static int prn_writefile(FILE *);
105 static int prn_puts(const char *);
106 static ssize_t prn_read(char *, size_t);
107
108 static struct lp_printer *lp;
109 static struct prnstate *prn;
110
111 void
printer(int debug,int verbose,const char * name)112 printer(int debug, int verbose, const char *name)
113 {
114 struct sigaction sa;
115 struct passwd *pw;
116 struct lp_queue q;
117 int fd, jobidx, qstate, r, reload, retry;
118 char buf[64], curr[1024];
119
120 /* Early initialisation. */
121 log_init(debug, LOG_LPR);
122 log_setverbose(verbose);
123 snprintf(buf, sizeof(buf), "printer:%s", name);
124 log_procinit(buf);
125 setproctitle("%s", buf);
126
127 if ((lpd_hostname = malloc(HOST_NAME_MAX+1)) == NULL)
128 fatal("%s: malloc", __func__);
129 gethostname(lpd_hostname, HOST_NAME_MAX+1);
130
131 /* Detach from lpd session if not in debug mode. */
132 if (!debug)
133 if (setsid() == -1)
134 fatal("%s: setsid", __func__);
135
136 /* Read printer config. */
137 if ((lp = calloc(1, sizeof(*lp))) == NULL)
138 fatal("%s: calloc", __func__);
139 if (lp_getprinter(lp, name) == -1)
140 exit(1);
141
142 /*
143 * Redirect stderr if not in debug mode.
144 * This must be done before dropping privileges.
145 */
146 if (!debug) {
147 fd = open(LP_LF(lp), O_WRONLY|O_APPEND);
148 if (fd == -1)
149 fatal("%s: open: %s", __func__, LP_LF(lp));
150 if (fd != STDERR_FILENO) {
151 if (dup2(fd, STDERR_FILENO) == -1)
152 fatalx("%s: dup2", __func__);
153 (void)close(fd);
154 }
155 }
156
157 /* Drop privileges. */
158 if ((pw = getpwnam(LPD_USER)) == NULL)
159 fatalx("unknown user " LPD_USER);
160
161 if (setgroups(1, &pw->pw_gid) ||
162 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
163 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
164 fatal("cannot drop privileges");
165
166 /* Initialize the printer state. */
167 if ((prn = calloc(1, sizeof(*prn))) == NULL)
168 fatal("%s: calloc", __func__);
169 prn->pfd = -1;
170 prn->ofd = -1;
171
172 /* Setup signals */
173 memset(&sa, 0, sizeof(sa));
174 sa.sa_handler = sighandler;
175 sa.sa_flags = SA_RESTART;
176 sigemptyset(&sa.sa_mask);
177 sigaddset(&sa.sa_mask, SIGINT); /* for kill() in sighandler */
178 sigaction(SIGHUP, &sa, NULL);
179 sigaction(SIGINT, &sa, NULL);
180 sigaction(SIGQUIT, &sa, NULL);
181 sigaction(SIGTERM, &sa, NULL);
182
183 /* Grab lock file. */
184 if (lp_lock(lp) == -1) {
185 if (errno == EWOULDBLOCK) {
186 log_debug("already locked");
187 exit(0);
188 }
189 fatalx("cannot open lock file");
190 }
191
192 /* Pledge. */
193 switch (lp->lp_type) {
194 case PRN_LOCAL:
195 pledge("stdio rpath wpath cpath flock getpw tty proc exec",
196 NULL);
197 break;
198
199 case PRN_NET:
200 pledge("stdio rpath wpath cpath inet flock dns getpw proc exec",
201 NULL);
202 break;
203
204 case PRN_LPR:
205 pledge("stdio rpath wpath cpath inet flock dns getpw", NULL);
206 break;
207 }
208
209 /* Start processing the queue. */
210 memset(&q, 0, sizeof(q));
211 jobidx = 0;
212 reload = 1;
213 retry = 0;
214 curr[0] = '\0';
215
216 for (;;) {
217
218 /* Check the queue state. */
219 if (lp_getqueuestate(lp, 1, &qstate) == -1)
220 fatalx("cannot get queue state");
221 if (qstate & LPQ_PRINTER_DOWN) {
222 log_debug("printing disabled");
223 break;
224 }
225 if (qstate & LPQ_QUEUE_UPDATED) {
226 log_debug("queue updated");
227 if (reload == 0)
228 lp_clearqueue(&q);
229 reload = 1;
230 }
231
232 /* Read the queue if needed. */
233 if (reload || q.count == 0) {
234 if (lp_readqueue(lp, &q) == -1)
235 fatalx("cannot read queue");
236 jobidx = 0;
237 reload = 0;
238 }
239
240 /* If the queue is empty, all done */
241 if (q.count <= jobidx) {
242 log_debug("queue empty");
243 break;
244 }
245
246 /* Open the printer if needed. */
247 if (prn->pfd == -1) {
248 prn_open();
249 /*
250 * Opening the printer might take some time.
251 * Re-read the queue in case its state has changed.
252 */
253 lp_clearqueue(&q);
254 reload = 1;
255 continue;
256 }
257
258 if (strcmp(curr, q.cfname[jobidx]))
259 retry = 0;
260 else
261 strlcpy(curr, q.cfname[jobidx], sizeof(curr));
262
263 lp_setcurrtask(lp, q.cfname[jobidx]);
264 if (lp->lp_type == PRN_LPR)
265 r = sendjob(q.cfname[jobidx], retry);
266 else
267 r = printjob(q.cfname[jobidx], retry);
268 lp_setcurrtask(lp, NULL);
269
270 switch (r) {
271 case JOB_OK:
272 log_info("job %s %s successfully", q.cfname[jobidx],
273 (lp->lp_type == PRN_LPR) ? "relayed" : "printed");
274 break;
275 case JOB_AGAIN:
276 retry++;
277 continue;
278 case JOB_IGNORE:
279 break;
280 case JOB_ERROR:
281 log_warnx("job %s could not be printed",
282 q.cfname[jobidx]);
283 break;
284 }
285 curr[0] = '\0';
286 jobidx++;
287 retry = 0;
288 }
289
290 if (prn->pfd != -1) {
291 if (prn->count) {
292 prn_formfeed();
293 if (lp->lp_tr)
294 prn_puts(lp->lp_tr);
295 }
296 prn_close();
297 }
298
299 exit(0);
300 }
301
302 static void
sighandler(int code)303 sighandler(int code)
304 {
305 log_info("got signal %d", code);
306
307 exit(0);
308 }
309
310 static char *
xstrdup(const char * s)311 xstrdup(const char *s)
312 {
313 char *r;
314
315 if ((r = strdup(s)) == NULL)
316 fatal("strdup");
317
318 return r;
319 }
320
321 /*
322 * Open control/data file, and check that the inode information is valid.
323 * On success, fill the "st" structure and set "fpp" and return 0 (OK).
324 * Return an error code on error.
325 */
326 static int
openfile(const char * fname,const char * inodeinfo,struct stat * st,FILE ** fpp)327 openfile(const char *fname, const char *inodeinfo, struct stat *st, FILE **fpp)
328 {
329 FILE *fp;
330 char buf[64];
331
332 if (inodeinfo) {
333 log_warnx("cannot open %s: symlink not implemented", fname);
334 return ERR_NOIMPL;
335 }
336 else {
337 if ((fp = lp_fopen(lp, fname)) == NULL) {
338 log_warn("cannot open %s", fname);
339 return ERR_ACCESS;
340 }
341 }
342
343 if (fstat(fileno(fp), st) == -1) {
344 log_warn("%s: fstat: %s", __func__, fname);
345 fclose(fp);
346 return ERR_ACCESS;
347 }
348
349 if (inodeinfo) {
350 snprintf(buf, sizeof(buf), "%d %llu", st->st_dev, st->st_ino);
351 if (strcmp(inodeinfo, buf)) {
352 log_warnx("inode changed for %s", fname);
353 fclose(fp);
354 return ERR_INODE;
355 }
356 }
357
358 *fpp = fp;
359
360 return OK;
361 }
362
363 /*
364 * Print the job described by the control file.
365 */
366 static int
printjob(const char * cfname,int retry)367 printjob(const char *cfname, int retry)
368 {
369 struct job job;
370 FILE *fp;
371 ssize_t len;
372 size_t linesz = 0;
373 char *line = NULL;
374 const char *errstr;
375 long long num;
376 int r, ret = JOB_OK;
377
378 log_debug("printing job %s...", cfname);
379
380 prn->efile[0] = '\0';
381 memset(&job, 0, sizeof(job));
382 job.pagewidth = lp->lp_pw;
383
384 if ((fp = lp_fopen(lp, cfname)) == NULL) {
385 if (errno == ENOENT) {
386 log_info("missing control file %s", cfname);
387 return JOB_IGNORE;
388 }
389 /* XXX no fatal? */
390 fatal("cannot open %s", cfname);
391 }
392
393 /* First pass: setup the job structure, print banner and print data. */
394 while ((len = getline(&line, &linesz, fp)) != -1) {
395 if (line[len-1] == '\n')
396 line[len-1] = '\0';
397
398 switch (line[0]) {
399 case 'C': /* Classification */
400 if (line[1]) {
401 free(job.class);
402 job.class = xstrdup(line + 1);
403 }
404 else if (job.class == NULL)
405 job.class = xstrdup(lpd_hostname);
406 break;
407
408 case 'H': /* Host name */
409 free(job.host);
410 job.host = xstrdup(line + 1);
411 if (job.class == NULL)
412 job.class = xstrdup(line + 1);
413 break;
414
415 case 'I': /* Indent */
416 errstr = NULL;
417 num = strtonum(line + 1, 0, INT_MAX, &errstr);
418 if (errstr == NULL)
419 job.indent = num;
420 else
421 log_warnx("strtonum: %s", errstr);
422 break;
423
424 case 'J': /* Job Name */
425 free(job.name);
426 if (line[1])
427 job.name = strdup(line + 1);
428 else
429 job.name = strdup(" ");
430 break;
431
432 case 'L': /* Literal */
433 free(job.literal);
434 job.literal = xstrdup(line + 1);
435 if (!lp->lp_sh && !lp->lp_hl)
436 printbanner(&job);
437 break;
438
439 case 'M': /* Send mail to the specified user */
440 free(job.mail);
441 job.mail = xstrdup(line + 1);
442 break;
443
444 case 'N': /* Filename */
445 break;
446
447 case 'P': /* Person */
448 free(job.person);
449 job.person = xstrdup(line + 1);
450 if (lp->lp_rs && getpwnam(job.person) == NULL) {
451 mailreport(&job, ERR_ACCOUNT);
452 ret = JOB_ERROR;
453 goto remove;
454 }
455 break;
456
457 case 'S': /* Stat info for symlink protection */
458 job.statinfo = xstrdup(line + 1);
459 break;
460
461 case 'T': /* Title for pr */
462 job.title = xstrdup(line + 1);
463 break;
464
465 case 'U': /* Unlink */
466 break;
467
468 case 'W': /* Width */
469 errstr = NULL;
470 num = strtonum(line + 1, 0, INT_MAX, &errstr);
471 if (errstr == NULL)
472 job.pagewidth = num;
473 else
474 log_warnx("strtonum: %s", errstr);
475 break;
476
477 case '1': /* troff fonts */
478 case '2':
479 case '3':
480 case '4':
481 /* XXX not implemented */
482 break;
483
484 default:
485 if (line[0] < 'a' || line[0] > 'z')
486 break;
487
488 r = printfile(&job, line[0], line+1, job.statinfo);
489 free(job.statinfo);
490 job.statinfo = NULL;
491 free(job.title);
492 job.title = NULL;
493 if (r) {
494 if (r == ERR_TRANSIENT && retry < RETRY_MAX) {
495 ret = JOB_AGAIN;
496 goto done;
497 }
498 mailreport(&job, r);
499 ret = JOB_ERROR;
500 goto remove;
501 }
502 }
503 }
504
505 remove:
506 if (lp_unlink(lp, cfname) == -1)
507 log_warn("cannot unlink %s", cfname);
508
509 /* Second pass: print trailing banner, mail report, and remove files. */
510 rewind(fp);
511 while ((len = getline(&line, &linesz, fp)) != -1) {
512 if (line[len-1] == '\n')
513 line[len-1] = '\0';
514
515 switch (line[0]) {
516 case 'L': /* Literal */
517 if (ret != JOB_OK)
518 break;
519 if (!lp->lp_sh && lp->lp_hl)
520 printbanner(&job);
521 break;
522
523 case 'M': /* Send mail to the specified user */
524 if (ret == JOB_OK)
525 mailreport(&job, ret);
526 break;
527
528 case 'U': /* Unlink */
529 if (lp_unlink(lp, line + 1) == -1)
530 log_warn("cannot unlink %s", line + 1);
531 break;
532 }
533 }
534
535 done:
536 if (prn->efile[0])
537 unlink(prn->efile);
538 (void)fclose(fp);
539 free(job.class);
540 free(job.host);
541 free(job.literal);
542 free(job.mail);
543 free(job.name);
544 free(job.person);
545 free(job.statinfo);
546 free(job.title);
547 return ret;
548 }
549
550 static void
printbanner(struct job * job)551 printbanner(struct job *job)
552 {
553 time_t t;
554
555 time(&t);
556
557 prn_formfeed();
558
559 if (lp->lp_sb) {
560 if (job->class) {
561 prn_puts(job->class);
562 prn_puts(":");
563 }
564 prn_puts(job->literal);
565 prn_puts(" Job: ");
566 prn_puts(job->name);
567 prn_puts(" Date: ");
568 prn_puts(ctime(&t));
569 prn_puts("\n");
570 } else {
571 prn_puts("\n\n\n");
572 lp_banner(prn->pfd, job->literal, lp->lp_pw);
573 prn_puts("\n\n");
574 lp_banner(prn->pfd, job->name, lp->lp_pw);
575 if (job->class) {
576 prn_puts("\n\n\n");
577 lp_banner(prn->pfd, job->class, lp->lp_pw);
578 }
579 prn_puts("\n\n\n\n\t\t\t\t\tJob: ");
580 prn_puts(job->name);
581 prn_puts("\n\t\t\t\t\tDate: ");
582 prn_puts(ctime(&t));
583 prn_puts("\n");
584 }
585
586 prn_formfeed();
587 }
588
589 static int
printfile(struct job * job,int fmt,const char * fname,const char * inodeinfo)590 printfile(struct job *job, int fmt, const char *fname, const char *inodeinfo)
591 {
592 pid_t pid;
593 struct stat st;
594 FILE *fp;
595 size_t n;
596 int ret, argc, efd, status;
597 char *argv[16], *prog, width[16], length[16], indent[16], tmp[512];
598
599 log_debug("printing file %s...", fname);
600
601 switch (fmt) {
602 case 'f': /* print file as-is */
603 case 'o': /* print postscript file */
604 case 'l': /* print file as-is but pass control chars */
605 break;
606
607 case 'p': /* print using pr(1) */
608 case 'r': /* print fortran text file */
609 case 't': /* print troff output */
610 case 'n': /* print ditroff output */
611 case 'd': /* print tex output */
612 case 'c': /* print cifplot output */
613 case 'g': /* print plot output */
614 case 'v': /* print raster output */
615 default:
616 log_warn("unrecognized output format '%c'", fmt);
617 return ERR_NOIMPL;
618 }
619
620 if ((ret = openfile(fname, inodeinfo, &st, &fp)) != OK)
621 return ret;
622
623 prn_formfeed();
624
625 /*
626 * No input filter, just write the raw file.
627 */
628 if (!lp->lp_if) {
629 if (prn_writefile(fp) == -1)
630 ret = ERR_TRANSIENT;
631 else
632 ret = OK;
633 (void)fclose(fp);
634 return ret;
635 }
636
637 /*
638 * Otherwise, run the input filter with proper plumbing.
639 */
640
641 /* Prepare filter arguments. */
642 snprintf(width, sizeof(width), "-w%d", job->pagewidth);
643 snprintf(length, sizeof(length), "-l%ld", lp->lp_pl);
644 snprintf(indent, sizeof(indent), "-i%d", job->indent);
645 prog = strrchr(lp->lp_if, '/');
646
647 argc = 0;
648 argv[argc++] = prog ? (prog + 1) : lp->lp_if;
649 if (fmt == 'l')
650 argv[argc++] = "-c";
651 argv[argc++] = width;
652 argv[argc++] = length;
653 argv[argc++] = indent;
654 argv[argc++] = "-n";
655 argv[argc++] = job->person;
656 if (job->name) {
657 argv[argc++] = "-j";
658 argv[argc++]= job->name;
659 }
660 argv[argc++] = "-h";
661 argv[argc++] = job->host;
662 argv[argc++] = lp->lp_af;
663 argv[argc++] = NULL;
664
665 /* Open the stderr file. */
666 strlcpy(prn->efile, "/tmp/prn.XXXXXXXX", sizeof(prn->efile));
667 if ((efd = mkstemp(prn->efile)) == -1) {
668 log_warn("%s: mkstemp", __func__);
669 (void)fclose(fp);
670 return ERR_TRANSIENT;
671 }
672
673 /* Disable output filter. */
674 prn_fsuspend();
675
676 /* Run input filter */
677 switch ((pid = fork())) {
678 case -1:
679 log_warn("%s: fork", __func__);
680 close(efd);
681 prn_fresume();
682 return ERR_TRANSIENT;
683
684 case 0:
685 if (dup2(fileno(fp), STDIN_FILENO) == -1)
686 fatal("%s:, dup2", __func__);
687 if (dup2(prn->pfd, STDOUT_FILENO) == -1)
688 fatal("%s:, dup2", __func__);
689 if (dup2(efd, STDERR_FILENO) == -1)
690 fatal("%s:, dup2", __func__);
691 if (closefrom(3) == -1)
692 fatal("%s:, closefrom", __func__);
693 execv(lp->lp_if, argv);
694 log_warn("%s:, execv", __func__);
695 exit(2);
696
697 default:
698 break;
699 }
700
701 log_debug("waiting for ifilter...");
702
703 /* Wait for input filter to finish. */
704 while (waitpid(pid, &status, 0) == -1)
705 log_warn("%s: waitpid", __func__);
706
707 log_debug("ifilter done, status %d", status);
708
709 /* Resume output filter */
710 prn_fresume();
711 prn->tof = 0;
712
713 /* Copy efd to stderr */
714 if (lseek(efd, 0, SEEK_SET) == -1)
715 log_warn("%s: lseek", __func__);
716 while ((n = read(efd, tmp, sizeof(tmp))) > 0)
717 (void)write(STDERR_FILENO, tmp, n);
718 close(efd);
719
720 if (!WIFEXITED(status)) {
721 log_warn("filter terminated (termsig=%d)", WTERMSIG(status));
722 return ERR_FILTER;
723 }
724
725 switch (WEXITSTATUS(status)) {
726 case 0:
727 prn->tof = 1;
728 return OK;
729
730 case 1:
731 return ERR_TRANSIENT;
732
733 case 2:
734 return ERR_ERROR;
735
736 default:
737 log_warn("filter exited (exitstatus=%d)", WEXITSTATUS(status));
738 return ERR_FILTER;
739 }
740 }
741
742 static int
sendjob(const char * cfname,int retry)743 sendjob(const char *cfname, int retry)
744 {
745 struct job job;
746 FILE *fp;
747 ssize_t len;
748 size_t linesz = 0;
749 char *line = NULL;
750 int ret = JOB_OK, r;
751
752 log_debug("sending job %s...", cfname);
753
754 memset(&job, 0, sizeof(job));
755
756 if ((fp = lp_fopen(lp, cfname)) == NULL) {
757 if (errno == ENOENT) {
758 log_info("missing control file %s", cfname);
759 return JOB_IGNORE;
760 }
761 /* XXX no fatal? */
762 fatal("cannot open %s", cfname);
763 }
764
765 /* First pass: setup the job structure, and forward data files. */
766 while ((len = getline(&line, &linesz, fp)) != -1) {
767 if (line[len-1] == '\n')
768 line[len-1] = '\0';
769
770 switch (line[0]) {
771 case 'P':
772 free(job.person);
773 job.person = xstrdup(line + 1);
774 break;
775
776 case 'S':
777 free(job.statinfo);
778 job.statinfo = xstrdup(line + 1);
779 break;
780
781 default:
782 if (line[0] < 'a' || line[0] > 'z')
783 break;
784
785 r = sendfile('\3', line+1, job.statinfo);
786 free(job.statinfo);
787 job.statinfo = NULL;
788 if (r) {
789 if (r == ERR_TRANSIENT && retry < RETRY_MAX) {
790 ret = JOB_AGAIN;
791 goto done;
792 }
793 mailreport(&job, r);
794 ret = JOB_ERROR;
795 goto remove;
796 }
797 }
798 }
799
800 /* Send the control file. */
801 if ((r = sendfile('\2', cfname, ""))) {
802 if (r == ERR_TRANSIENT && retry < RETRY_MAX) {
803 ret = JOB_AGAIN;
804 goto done;
805 }
806 mailreport(&job, r);
807 ret = JOB_ERROR;
808 }
809
810 remove:
811 if (lp_unlink(lp, cfname) == -1)
812 log_warn("cannot unlink %s", cfname);
813
814 /* Second pass: remove files. */
815 rewind(fp);
816 while ((len = getline(&line, &linesz, fp)) != -1) {
817 if (line[len-1] == '\n')
818 line[len-1] = '\0';
819
820 switch (line[0]) {
821 case 'U':
822 if (lp_unlink(lp, line + 1) == -1)
823 log_warn("cannot unlink %s", line + 1);
824 break;
825 }
826 }
827
828 done:
829 (void)fclose(fp);
830 free(line);
831 free(job.person);
832 free(job.statinfo);
833 return ret;
834 }
835
836 /*
837 * Send a LPR command to the remote lpd server and return the ack.
838 * Return 0 for ack, 1 or nack, -1 and set errno on error.
839 */
840 static int
sendcmd(const char * fmt,...)841 sendcmd(const char *fmt, ...)
842 {
843 va_list ap;
844 unsigned char line[1024];
845 int len;
846
847 va_start(ap, fmt);
848 len = vsnprintf(line, sizeof(line), fmt, ap);
849 va_end(ap);
850
851 if (len < 0) {
852 log_warn("%s: vsnprintf", __func__);
853 return -1;
854 }
855
856 if (prn_puts(line) == -1)
857 return -1;
858
859 return recvack();
860 }
861
862 static int
sendfile(int type,const char * fname,const char * inodeinfo)863 sendfile(int type, const char *fname, const char *inodeinfo)
864 {
865 struct stat st;
866 FILE *fp = NULL;
867 int ret;
868
869 log_debug("sending file %s...", fname);
870
871 if ((ret = openfile(fname, inodeinfo, &st, &fp)) != OK)
872 return ret;
873
874 ret = ERR_TRANSIENT;
875 if (sendcmd("%c%lld %s\n", type, (long long)st.st_size, fname)) {
876 if (errno == 0)
877 ret = ERR_REJECTED;
878 goto fail;
879 }
880
881 lp_setstatus(lp, "sending %s to %s", fname, lp->lp_rm);
882 if (prn_writefile(fp) == -1 || prn_write("\0", 1) == -1)
883 goto fail;
884 if (recvack()) {
885 if (errno == 0)
886 ret = ERR_REJECTED;
887 goto fail;
888 }
889
890 ret = OK;
891
892 fail:
893 (void)fclose(fp);
894
895 if (ret == ERR_REJECTED)
896 log_warnx("%s rejected by remote host", fname);
897
898 return ret;
899 }
900
901 /*
902 * Read a ack response from the server.
903 * Return 0 for ack, 1 or nack, -1 and set errno on error.
904 */
905 static int
recvack(void)906 recvack(void)
907 {
908 char visbuf[256 * 4 + 1];
909 unsigned char line[1024];
910 ssize_t n;
911
912 if ((n = prn_read(line, sizeof(line))) == -1)
913 return -1;
914
915 if (n == 1) {
916 errno = 0;
917 if (line[0])
918 log_warnx("%s: \\%d", lp->lp_host, line[0]);
919 return line[0] ? 1 : 0;
920 }
921
922 if (n > 256)
923 n = 256;
924 line[n] = '\0';
925 if (line[n-1] == '\n')
926 line[--n] = '\0';
927
928 strvisx(visbuf, line, n, VIS_NL | VIS_CSTYLE);
929 log_warnx("%s: %s", lp->lp_host, visbuf);
930
931 errno = 0;
932 return -1;
933 }
934
935 static void
mailreport(struct job * job,int result)936 mailreport(struct job *job, int result)
937 {
938 struct stat st;
939 FILE *fp = NULL, *efp;
940 const char *user;
941 char *cp;
942 int p[2], c;
943
944 if (job->mail)
945 user = job->mail;
946 else
947 user = job->person;
948 if (user == NULL) {
949 log_warnx("no user to send report to");
950 return;
951 }
952
953 if (pipe(p) == -1) {
954 log_warn("pipe");
955 return;
956 }
957
958 switch (fork()) {
959 case -1:
960 (void)close(p[0]);
961 (void)close(p[1]);
962 log_warn("fork");
963 return;
964
965 case 0:
966 if (dup2(p[0], 0) == -1)
967 fatal("%s: dup2", __func__);
968 (void)closefrom(3);
969 if ((cp = strrchr(_PATH_SENDMAIL, '/')))
970 cp++;
971 else
972 cp = _PATH_SENDMAIL;
973 execl(_PATH_SENDMAIL, cp, "-t", (char *)NULL);
974 fatal("%s: execl: %s", __func__, _PATH_SENDMAIL);
975
976 default:
977 (void)close(p[0]);
978 if ((fp = fdopen(p[1], "w")) == NULL) {
979 (void)close(p[1]);
980 log_warn("fdopen");
981 return;
982 }
983 }
984
985 fprintf(fp, "Auto-Submitted: auto-generated\n");
986 fprintf(fp, "To: %s@%s\n", user, job->host);
987 fprintf(fp, "Subject: %s printer job \"%s\"\n", lp->lp_name,
988 job->name ? job->name : "<unknown>");
989 fprintf(fp, "Reply-To: root@%s\n\n", lpd_hostname);
990 fprintf(fp, "Your printer job ");
991 if (job->name)
992 fprintf(fp, " (%s) ", job->name);
993
994 fprintf(fp, "\n");
995
996 switch (result) {
997 case OK:
998 fprintf(fp, "completed successfully");
999 break;
1000
1001 case ERR_ACCOUNT:
1002 fprintf(fp, "could not be printed without an account on %s",
1003 lpd_hostname);
1004 break;
1005
1006 case ERR_ACCESS:
1007 fprintf(fp, "could not be printed because the file could "
1008 " not be read");
1009 break;
1010
1011 case ERR_INODE:
1012 fprintf(fp, "was not printed because it was not linked to"
1013 " the original file");
1014 break;
1015
1016 case ERR_NOIMPL:
1017 fprintf(fp, "was not printed because some feature is missing");
1018 break;
1019
1020 case ERR_FILTER:
1021 efp = fopen(prn->efile, "r");
1022 if (efp && fstat(fileno(efp), &st) == 0 && st.st_size) {
1023 fprintf(fp,
1024 "had the following errors and may not have printed:\n");
1025 while ((c = getc(efp)) != EOF)
1026 putc(c, fp);
1027 }
1028 else
1029 fprintf(fp,
1030 "had some errors and may not have printed\n");
1031
1032 if (efp)
1033 fclose(efp);
1034 break;
1035
1036 default:
1037 printf("could not be printed");
1038 break;
1039 }
1040
1041 fprintf(fp, "\n");
1042 fclose(fp);
1043
1044 wait(NULL);
1045 }
1046
1047 static void
prn_open(void)1048 prn_open(void)
1049 {
1050 const char *status, *oldstatus;
1051 int i;
1052
1053 switch (lp->lp_type) {
1054 case PRN_LOCAL:
1055 lp_setstatus(lp, "opening %s", LP_LP(lp));
1056 break;
1057
1058 case PRN_NET:
1059 case PRN_LPR:
1060 lp_setstatus(lp, "connecting to %s:%s", lp->lp_host,
1061 lp->lp_port ? lp->lp_port : "printer");
1062 break;
1063 }
1064
1065 status = oldstatus = NULL;
1066 for (i = 0; prn->pfd == -1; i += (i < 6) ? 1 : 0) {
1067
1068 if (status != oldstatus) {
1069 lp_setstatus(lp, "%s", status);
1070 oldstatus = status;
1071 }
1072
1073 if (i)
1074 sleep(1 << i);
1075
1076 if ((prn->pfd = prn_connect()) == -1) {
1077 status = "waiting for printer to come up";
1078 continue;
1079 }
1080
1081 if (lp->lp_type == PRN_LPR) {
1082 /* Send a recvjob request. */
1083 if (sendcmd("\2%s\n", LP_RP(lp))) {
1084 if (errno == 0)
1085 log_warnx("remote queue is disabled");
1086 (void)close(prn->pfd);
1087 prn->pfd = -1;
1088 status = "waiting for queue to be enabled";
1089 }
1090 }
1091 }
1092
1093 switch (lp->lp_type) {
1094 case PRN_LOCAL:
1095 lp_setstatus(lp, "printing to %s", LP_LP(lp));
1096 break;
1097
1098 case PRN_NET:
1099 lp_setstatus(lp, "printing to %s:%s", lp->lp_host, lp->lp_port);
1100 break;
1101
1102 case PRN_LPR:
1103 lp_setstatus(lp, "sending to %s", lp->lp_host);
1104 break;
1105 }
1106
1107 prn->tof = lp->lp_fo ? 0 : 1;
1108 prn->count = 0;
1109
1110 prn_fstart();
1111 }
1112
1113 /*
1114 * Open the printer device, or connect to the remote host.
1115 * Return the printer file descriptor, or -1 on error.
1116 */
1117 static int
prn_connect(void)1118 prn_connect(void)
1119 {
1120 struct addrinfo hints, *res, *res0;
1121 int save_errno;
1122 int fd, e, mode;
1123 const char *cause = NULL, *host, *port;
1124
1125 if (lp->lp_type == PRN_LOCAL) {
1126 mode = lp->lp_rw ? O_RDWR : O_WRONLY;
1127 if ((fd = open(LP_LP(lp), mode)) == -1) {
1128 log_warn("failed to open %s", LP_LP(lp));
1129 return -1;
1130 }
1131
1132 if (isatty(fd)) {
1133 lp_stty(lp, fd);
1134 return -1;
1135 }
1136
1137 return fd;
1138 }
1139
1140 host = lp->lp_host;
1141 port = lp->lp_port ? lp->lp_port : "printer";
1142
1143 memset(&hints, 0, sizeof(hints));
1144 hints.ai_family = AF_UNSPEC;
1145 hints.ai_socktype = SOCK_STREAM;
1146 if ((e = getaddrinfo(host, port, &hints, &res0))) {
1147 log_warnx("%s:%s: %s", host, port, gai_strerror(e));
1148 return -1;
1149 }
1150
1151 fd = -1;
1152 for (res = res0; res && fd == -1; res = res->ai_next) {
1153 fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1154 if (fd == -1)
1155 cause = "socket";
1156 else if (connect(fd, res->ai_addr, res->ai_addrlen) == -1) {
1157 cause = "connect";
1158 save_errno = errno;
1159 (void)close(fd);
1160 errno = save_errno;
1161 fd = -1;
1162 }
1163 }
1164
1165 if (fd == -1)
1166 log_warn("%s", cause);
1167 else
1168 log_debug("connected to %s:%s", host, port);
1169
1170 freeaddrinfo(res0);
1171 return fd;
1172 }
1173
1174 static void
prn_close(void)1175 prn_close(void)
1176 {
1177 prn_fclose();
1178
1179 (void)close(prn->pfd);
1180 prn->pfd = -1;
1181 }
1182
1183 /*
1184 * Fork the output filter process if needed.
1185 */
1186 static int
prn_fstart(void)1187 prn_fstart(void)
1188 {
1189 char width[32], length[32], *cp;
1190 int fildes[2], i;
1191
1192 if (lp->lp_type == PRN_LPR || (!lp->lp_of))
1193 return 0;
1194
1195 pipe(fildes);
1196
1197 for (i = 0; i < 20; i++) {
1198 if (i)
1199 sleep(i);
1200 if ((prn->opid = fork()) != -1)
1201 break;
1202 log_warn("%s: fork", __func__);
1203 }
1204
1205 if (prn->opid == -1) {
1206 log_warnx("cannot fork output filter");
1207 return -1;
1208 }
1209
1210 if (prn->opid == 0) {
1211 /* child */
1212 dup2(fildes[0], 0);
1213 dup2(prn->pfd, 1);
1214 (void)closefrom(3);
1215 cp = strrchr(lp->lp_of, '/');
1216 if (cp)
1217 cp += 1;
1218 else
1219 cp = lp->lp_of;
1220 snprintf(width, sizeof(width), "-w%ld", lp->lp_pw);
1221 snprintf(length, sizeof(length), "-l%ld", lp->lp_pl);
1222 execl(lp->lp_of, cp, width, length, (char *)NULL);
1223 log_warn("%s: execl", __func__);
1224 exit(1);
1225 }
1226
1227 close(fildes[0]);
1228 prn->ofd = fildes[1];
1229 prn->ofilter = 1;
1230
1231 return 0;
1232 }
1233
1234 /*
1235 * Suspend the output filter process.
1236 */
1237 static void
prn_fsuspend(void)1238 prn_fsuspend(void)
1239 {
1240 pid_t pid;
1241 int status;
1242
1243 if (prn->opid == 0)
1244 return;
1245
1246 prn_puts("\031\1");
1247 while ((pid = waitpid(WAIT_ANY, &status, WUNTRACED)) && pid != prn->opid)
1248 ;
1249
1250 prn->ofilter = 0;
1251 if (!WIFSTOPPED(status)) {
1252 log_warn("output filter died (exitstatus=%d termsig=%d)",
1253 WEXITSTATUS(status), WTERMSIG(status));
1254 prn->opid = 0;
1255 prn_fclose();
1256 }
1257 }
1258
1259 /*
1260 * Resume the output filter process.
1261 */
1262 static void
prn_fresume(void)1263 prn_fresume(void)
1264 {
1265 if (prn->opid == 0)
1266 return;
1267
1268 if (kill(prn->opid, SIGCONT) == -1)
1269 fatal("cannot restart output filter");
1270 prn->ofilter = 1;
1271 }
1272
1273 /*
1274 * Close the output filter socket and wait for the process to terminate
1275 * if currently running.
1276 */
1277 static void
prn_fclose(void)1278 prn_fclose(void)
1279 {
1280 pid_t pid;
1281
1282 close(prn->ofd);
1283 prn->ofd = -1;
1284
1285 while (prn->opid) {
1286 pid = wait(NULL);
1287 if (pid == -1)
1288 log_warn("%s: wait", __func__);
1289 else if (pid == prn->opid)
1290 prn->opid = 0;
1291 }
1292 }
1293
1294 /*
1295 * Write a form-feed if the printer cap requires it, and if not currently
1296 * at top of form. Return 0 on success, or -1 on error and set errno.
1297 */
1298 static int
prn_formfeed(void)1299 prn_formfeed(void)
1300 {
1301 if (!lp->lp_sf && !prn->tof)
1302 if (prn_puts(LP_FF(lp)) == -1)
1303 return -1;
1304 prn->tof = 1;
1305 return 0;
1306 }
1307
1308 /*
1309 * Write data to the printer (or output filter process).
1310 * Return 0 on success, or -1 and set errno.
1311 */
1312 static int
prn_write(const char * buf,size_t len)1313 prn_write(const char *buf, size_t len)
1314 {
1315 ssize_t n;
1316 int fd;
1317
1318 fd = prn->ofilter ? prn->ofd : prn->pfd;
1319
1320 log_debug("prn_write(fd=%d len=%zu, of=%d pfd=%d ofd=%d)", fd, len,
1321 prn->ofilter, prn->pfd, prn->ofd);
1322
1323 if (fd == -1) {
1324 log_warnx("printer socket not opened");
1325 errno = EPIPE;
1326 return -1;
1327 }
1328
1329 while (len) {
1330 if ((n = write(fd, buf, len)) == -1) {
1331 if (errno == EINTR)
1332 continue;
1333 log_warn("%s: write", __func__);
1334 /* XXX close the printer */
1335 return -1;
1336 }
1337 len -= n;
1338 buf += n;
1339 prn->tof = 0;
1340 }
1341
1342 return 0;
1343 }
1344
1345 /*
1346 * Write a string to the printer (or output filter process).
1347 * Return 0 on success, or -1 and set errno.
1348 */
1349 static int
prn_puts(const char * buf)1350 prn_puts(const char *buf)
1351 {
1352 return prn_write(buf, strlen(buf));
1353 }
1354
1355 /*
1356 * Write the FILE content to the printer (or output filter process).
1357 * Return 0 on success, or -1 and set errno.
1358 */
1359 static int
prn_writefile(FILE * fp)1360 prn_writefile(FILE *fp)
1361 {
1362 char buf[BUFSIZ];
1363 size_t r;
1364
1365 while (!feof(fp)) {
1366 r = fread(buf, 1, sizeof(buf), fp);
1367 if (ferror(fp)) {
1368 log_warn("%s: fread", __func__);
1369 return -1;
1370 }
1371 if (r && (prn_write(buf, r) == -1))
1372 return -1;
1373 }
1374
1375 return 0;
1376 }
1377
1378 /*
1379 * Read data from the printer socket into the given buffer.
1380 * Return 0 on success, or -1 and set errno.
1381 */
1382 static ssize_t
prn_read(char * buf,size_t sz)1383 prn_read(char *buf, size_t sz)
1384 {
1385 ssize_t n;
1386
1387 for (;;) {
1388 if ((n = read(prn->pfd, buf, sz)) == 0) {
1389 errno = ECONNRESET;
1390 n = -1;
1391 }
1392 if (n == -1) {
1393 if (errno == EINTR)
1394 continue;
1395 /* XXX close printer? */
1396 log_warn("%s: read", __func__);
1397 return -1;
1398 }
1399 return n;
1400 }
1401 }
1402