1 /* $OpenBSD: engine_lpr.c,v 1.2 2019/04/04 19:25:45 eric 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
21 #include <ctype.h>
22 #include <errno.h>
23 #include <limits.h>
24 #include <netgroup.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28
29 #include "lpd.h"
30 #include "lp.h"
31
32 #include "log.h"
33 #include "proc.h"
34
35 struct lpr_recvfile {
36 TAILQ_ENTRY(lpr_recvfile) entry;
37 char *dfname;
38 };
39
40 struct lpr_recvjob {
41 TAILQ_ENTRY(lpr_recvjob) entry;
42 struct lp_printer lp;
43 uint32_t connid;
44 char *hostfrom;
45 char *cfname;
46 int dfcount;
47 size_t dfsize;
48 TAILQ_HEAD(, lpr_recvfile) df;
49 };
50
51 static void lpr_allowedhost(uint32_t, const struct sockaddr *);
52 static void lpr_allowedhost_res(uint32_t, const char *, const char *);
53 static int lpr_mkstemp(void);
54 static void lpr_displayq(uint32_t, const char *, int, struct lp_jobfilter *);
55 static void lpr_displayq_res(uint32_t, int, const char *, const char *);
56 static void lpr_rmjob(uint32_t, const char *, const char *,
57 struct lp_jobfilter *);
58 static void lpr_rmjob_res(uint32_t, int, const char *, const char *);
59 static void lpr_recvjob(uint32_t, const char*, const char *);
60 static void lpr_recvjob_res(uint32_t, int);
61 static void lpr_recvjob_cf(uint32_t, size_t, const char *);
62 static void lpr_recvjob_df(uint32_t, size_t, const char *);
63 static void lpr_recvjob_clear(uint32_t);
64 static void lpr_recvjob_commit(uint32_t);
65 static void lpr_recvjob_rollback(uint32_t);
66 static void lpr_recvjob_free(struct lpr_recvjob *);
67 static int matchaddr(const char *, const struct sockaddr *, int *);
68 static int cmpsockaddr(const struct sockaddr *, const struct sockaddr *);
69
70 static TAILQ_HEAD(, lpr_recvjob) recvjobs = TAILQ_HEAD_INITIALIZER(recvjobs);
71
72 void
lpr_dispatch_frontend(struct imsgproc * proc,struct imsg * imsg)73 lpr_dispatch_frontend(struct imsgproc *proc, struct imsg *imsg)
74 {
75 struct sockaddr_storage ss;
76 struct lp_jobfilter jf;
77 struct lp_printer lp;
78 const char *hostfrom, *prn, *filename, *agent;
79 uint32_t connid;
80 size_t size;
81 int lng, i;
82
83 connid = imsg->hdr.peerid;
84
85 switch (imsg->hdr.type) {
86 case IMSG_LPR_ALLOWEDHOST:
87 m_get_sockaddr(proc, (struct sockaddr *)&ss);
88 m_end(proc);
89 lpr_allowedhost(connid, (struct sockaddr *)&ss);
90 break;
91
92 case IMSG_LPR_DISPLAYQ:
93 memset(&jf, 0, sizeof(jf));
94 m_get_int(proc, &lng);
95 m_get_string(proc, &jf.hostfrom);
96 m_get_string(proc, &prn);
97 m_get_int(proc, &jf.njob);
98 for (i = 0; i < jf.njob; i++)
99 m_get_int(proc, &jf.jobs[i]);
100 m_get_int(proc, &jf.nuser);
101 for (i = 0; i < jf.nuser; i++)
102 m_get_string(proc, &jf.users[i]);
103 m_end(proc);
104 lpr_displayq(connid, prn, lng, &jf);
105 break;
106
107 case IMSG_LPR_PRINTJOB:
108 m_get_string(proc, &prn);
109 m_end(proc);
110 /* Make sure the printer exists. */
111 if (lp_getprinter(&lp, prn) == -1)
112 break;
113 lpr_printjob(lp.lp_name);
114 lp_clearprinter(&lp);
115 break;
116
117 case IMSG_LPR_RECVJOB:
118 m_get_string(proc, &hostfrom);
119 m_get_string(proc, &prn);
120 m_end(proc);
121 lpr_recvjob(connid, hostfrom, prn);
122 break;
123
124 case IMSG_LPR_RECVJOB_CLEAR:
125 m_end(proc);
126 lpr_recvjob_clear(connid);
127 break;
128
129 case IMSG_LPR_RECVJOB_CF:
130 m_get_size(proc, &size);
131 m_get_string(proc, &filename);
132 m_end(proc);
133 lpr_recvjob_cf(connid, size, filename);
134 break;
135
136 case IMSG_LPR_RECVJOB_DF:
137 m_get_size(proc, &size);
138 m_get_string(proc, &filename);
139 m_end(proc);
140 lpr_recvjob_df(connid, size, filename);
141 break;
142
143 case IMSG_LPR_RECVJOB_COMMIT:
144 m_end(proc);
145 lpr_recvjob_commit(connid);
146 break;
147
148 case IMSG_LPR_RECVJOB_ROLLBACK:
149 m_end(proc);
150 lpr_recvjob_rollback(connid);
151 break;
152
153 case IMSG_LPR_RMJOB:
154 memset(&jf, 0, sizeof(jf));
155 m_get_string(proc, &jf.hostfrom);
156 m_get_string(proc, &prn);
157 m_get_string(proc, &agent);
158 m_get_int(proc, &jf.njob);
159 for (i = 0; i < jf.njob; i++)
160 m_get_int(proc, &jf.jobs[i]);
161 m_get_int(proc, &jf.nuser);
162 for (i = 0; i < jf.nuser; i++)
163 m_get_string(proc, &jf.users[i]);
164 m_end(proc);
165 lpr_rmjob(connid, prn, agent, &jf);
166 break;
167
168 default:
169 fatalx("%s: unexpected imsg %s", __func__,
170 log_fmt_imsgtype(imsg->hdr.type));
171 }
172 }
173
174 void
lpr_shutdown()175 lpr_shutdown()
176 {
177 struct lpr_recvjob *j;
178
179 /* Cleanup incoming jobs. */
180 while ((j = TAILQ_FIRST(&recvjobs))) {
181 lpr_recvjob_clear(j->connid);
182 lpr_recvjob_free(j);
183 }
184 }
185
186 void
lpr_printjob(const char * prn)187 lpr_printjob(const char *prn)
188 {
189 m_create(p_priv, IMSG_LPR_PRINTJOB, 0, 0, -1);
190 m_add_string(p_priv, prn);
191 m_close(p_priv);
192 }
193
194 static void
lpr_allowedhost(uint32_t connid,const struct sockaddr * sa)195 lpr_allowedhost(uint32_t connid, const struct sockaddr *sa)
196 {
197 FILE *fp;
198 size_t linesz = 0;
199 ssize_t linelen;
200 char host[NI_MAXHOST], addr[NI_MAXHOST], serv[NI_MAXSERV];
201 char dom[NI_MAXHOST], *lp, *ep, *line = NULL;
202 int e, rev = 0, ok = 0;
203
204 /* Always accept local connections. */
205 if (sa->sa_family == AF_UNIX) {
206 lpr_allowedhost_res(connid, lpd_hostname, NULL);
207 return;
208 }
209
210 host[0] = '\0';
211
212 /* Print address. */
213 if ((e = getnameinfo(sa, sa->sa_len, addr, sizeof(addr), serv,
214 sizeof(serv), NI_NUMERICHOST))) {
215 log_warnx("%s: could not print addr: %s", __func__,
216 gai_strerror(e));
217 lpr_allowedhost_res(connid, host, "Malformed address");
218 return;
219 }
220
221 /* Get the hostname for the address. */
222 if ((e = getnameinfo(sa, sa->sa_len, host, sizeof(host), NULL, 0,
223 NI_NAMEREQD))) {
224 if (e != EAI_NONAME)
225 log_warnx("%s: could not resolve %s: %s", __func__,
226 addr, gai_strerror(e));
227 lpr_allowedhost_res(connid, host,
228 "No hostname found for your address");
229 return;
230 }
231
232 /* Check for a valid DNS roundtrip. */
233 if (!matchaddr(host, sa, &e)) {
234 if (e)
235 log_warnx("%s: getaddrinfo: %s: %s", __func__,
236 host, gai_strerror(e));
237 lpr_allowedhost_res(connid, host, e ?
238 "Cannot resolve your hostname" :
239 "Your hostname and your address do not match");
240 return;
241 }
242
243 /* Scan the hosts.lpd file. */
244 if ((fp = fopen(_PATH_HOSTSLPD, "r")) == NULL) {
245 log_warn("%s: %s", __func__, _PATH_HOSTSLPD);
246 lpr_allowedhost_res(connid, host,
247 "Cannot access " _PATH_HOSTSLPD);
248 return;
249 }
250
251 dom[0] = '\0';
252 while ((linelen = getline(&line, &linesz, fp)) != -1) {
253 /* Drop comment and strip line. */
254 for (lp = line; *lp; lp++)
255 if (!isspace((unsigned char)*lp))
256 break;
257 if (*lp == '#' || *lp == '\0')
258 continue;
259 for (ep = lp + 1; *ep; ep++)
260 if (isspace((unsigned char)*ep) || *ep == '#') {
261 *ep = '\0';
262 break;
263 }
264
265 rev = 0;
266 switch (lp[0]) {
267 case '-':
268 case '+':
269 switch (lp[1]) {
270 case '\0':
271 ok = 1;
272 break;
273 case '@':
274 if (dom[0] == '\0')
275 getdomainname(dom, sizeof(dom));
276 ok = innetgr(lp + 2, host, NULL, dom);
277 break;
278 default:
279 ok = matchaddr(lp + 1, sa, NULL);
280 break;
281 }
282 if (lp[0] == '-')
283 ok = -ok;
284 break;
285 default:
286 ok = matchaddr(lp, sa, NULL);
287 break;
288 }
289 if (ok)
290 break;
291 }
292
293 free(line);
294 fclose(fp);
295
296 lpr_allowedhost_res(connid, host,
297 (ok > 0) ? NULL : "Access denied");
298 }
299
300 static void
lpr_allowedhost_res(uint32_t connid,const char * hostname,const char * reject)301 lpr_allowedhost_res(uint32_t connid, const char *hostname, const char *reject)
302 {
303 m_create(p_frontend, IMSG_LPR_ALLOWEDHOST, connid, 0, -1);
304 m_add_string(p_frontend, hostname);
305 m_add_string(p_frontend, reject);
306 m_close(p_frontend);
307 }
308
309 static int
matchaddr(const char * host,const struct sockaddr * sa,int * gaierrno)310 matchaddr(const char *host, const struct sockaddr *sa, int *gaierrno)
311 {
312 struct addrinfo hints, *res, *r;
313 int e, ok = 0;
314
315 memset(&hints, 0, sizeof(hints));
316 hints.ai_family = sa->sa_family;
317 hints.ai_socktype = SOCK_DGRAM; /*dummy*/
318 if ((e = getaddrinfo(host, NULL, &hints, &res))) {
319 if (gaierrno)
320 *gaierrno = e;
321 return 0;
322 }
323 if (gaierrno)
324 *gaierrno = 0;
325
326 for (r = res; r; r = r->ai_next)
327 if (cmpsockaddr(sa, r->ai_addr) == 0) {
328 ok = 1;
329 break;
330 }
331 freeaddrinfo(res);
332
333 return ok;
334 }
335
336 static int
cmpsockaddr(const struct sockaddr * a,const struct sockaddr * b)337 cmpsockaddr(const struct sockaddr *a, const struct sockaddr *b)
338 {
339 const void *aa, *ab;
340 size_t l;
341
342 if (a->sa_family != b->sa_family)
343 return (a->sa_family < b->sa_family) ? -1 : 1;
344
345 switch (a->sa_family) {
346 case AF_UNIX:
347 return 0;
348
349 case AF_INET:
350 aa = &(((const struct sockaddr_in*)a)->sin_addr);
351 ab = &(((const struct sockaddr_in*)b)->sin_addr);
352 l = sizeof(((const struct sockaddr_in*)a)->sin_addr);
353 return memcmp(aa, ab, l);
354
355 case AF_INET6:
356 aa = &(((const struct sockaddr_in6*)a)->sin6_addr);
357 ab = &(((const struct sockaddr_in6*)b)->sin6_addr);
358 l = sizeof(((const struct sockaddr_in*)a)->sin_addr);
359 return memcmp(aa, ab, l);
360
361 }
362
363 return 0;
364 }
365
366 static int
lpr_mkstemp(void)367 lpr_mkstemp(void)
368 {
369 char path[PATH_MAX];
370 int fd;
371
372 if (strlcpy(path, _PATH_TMP "lpd.XXXXXXXXXX", sizeof(path)) >=
373 sizeof(path)) {
374 log_warnx("%s: path too long", __func__);
375 return -1;
376 }
377 if ((fd = mkstemp(path)) == -1) {
378 log_warn("%s: mkstemp", __func__);
379 return -1;
380 }
381 (void)unlink(path);
382 return fd;
383
384 }
385
386 static void
lpr_displayq(uint32_t connid,const char * prn,int lng,struct lp_jobfilter * jf)387 lpr_displayq(uint32_t connid, const char *prn, int lng, struct lp_jobfilter *jf)
388 {
389 struct lp_printer lp;
390 char cmd[LPR_MAXCMDLEN], buf[16];
391 int fd, i;
392
393 if (lp_getprinter(&lp, prn) == -1) {
394 lpr_displayq_res(connid, -1, NULL, NULL);
395 return;
396 }
397
398 fd = lpr_mkstemp();
399 if (fd != -1) {
400 /* Write formatted queue content into the temporary file. */
401 lp_displayq(fd, &lp, lng, jf);
402 if (lseek(fd, 0, SEEK_SET) == -1)
403 log_warn("%s: lseek", __func__);
404 }
405
406 /* Send the result to frontend. */
407 if (lp.lp_type == PRN_LPR) {
408 snprintf(cmd, sizeof(cmd), "%c%s", lng?'\4':'\3', LP_RP(&lp));
409 for (i = 0; i < jf->nuser; i++) {
410 strlcat(cmd, " ", sizeof(cmd));
411 strlcat(cmd, jf->users[i], sizeof(cmd));
412 }
413 for (i = 0; i < jf->njob; i++) {
414 snprintf(buf, sizeof(buf), " %d", jf->jobs[i]);
415 strlcat(cmd, buf, sizeof(cmd));
416 }
417 lpr_displayq_res(connid, fd, lp.lp_host, cmd);
418 }
419 else
420 lpr_displayq_res(connid, fd, NULL, NULL);
421
422 lp_clearprinter(&lp);
423 }
424
425 static void
lpr_displayq_res(uint32_t connid,int fd,const char * host,const char * cmd)426 lpr_displayq_res(uint32_t connid, int fd, const char *host, const char *cmd)
427 {
428 m_create(p_frontend, IMSG_LPR_DISPLAYQ, connid, 0, fd);
429 m_add_string(p_frontend, host);
430 m_add_string(p_frontend, cmd);
431 m_close(p_frontend);
432 }
433
434 static void
lpr_rmjob(uint32_t connid,const char * prn,const char * agent,struct lp_jobfilter * jf)435 lpr_rmjob(uint32_t connid, const char *prn, const char *agent,
436 struct lp_jobfilter *jf)
437 {
438 struct lp_printer lp;
439 char cmd[LPR_MAXCMDLEN], buf[16];
440 int fd, i, restart = 0;
441
442 if (lp_getprinter(&lp, prn) == -1) {
443 lpr_rmjob_res(connid, -1, NULL, NULL);
444 return;
445 }
446
447 fd = lpr_mkstemp();
448 if (fd != -1) {
449 /* Write result to the temporary file. */
450 restart = lp_rmjob(fd, &lp, agent, jf);
451 if (lseek(fd, 0, SEEK_SET) == -1)
452 log_warn("%s: lseek", __func__);
453 }
454
455 /* Send the result to frontend. */
456 if (lp.lp_type == PRN_LPR) {
457 snprintf(cmd, sizeof(cmd), "\5%s %s", LP_RP(&lp), agent);
458 for (i = 0; i < jf->nuser; i++) {
459 strlcat(cmd, " ", sizeof(cmd));
460 strlcat(cmd, jf->users[i], sizeof(cmd));
461 }
462 for (i = 0; i < jf->njob; i++) {
463 snprintf(buf, sizeof(buf), " %d", jf->jobs[i]);
464 strlcat(cmd, buf, sizeof(cmd));
465 }
466 lpr_rmjob_res(connid, fd, lp.lp_host, cmd);
467 }
468 else
469 lpr_rmjob_res(connid, fd, NULL, NULL);
470
471 /* If the printer process was stopped, tell parent to re-spawn one. */
472 if (restart)
473 lpr_printjob(lp.lp_name);
474
475 lp_clearprinter(&lp);
476 }
477
478 static void
lpr_rmjob_res(uint32_t connid,int fd,const char * host,const char * cmd)479 lpr_rmjob_res(uint32_t connid, int fd, const char *host, const char *cmd)
480 {
481 m_create(p_frontend, IMSG_LPR_RMJOB, connid, 0, fd);
482 m_add_string(p_frontend, host);
483 m_add_string(p_frontend, cmd);
484 m_close(p_frontend);
485 }
486
487 static void
lpr_recvjob(uint32_t connid,const char * hostfrom,const char * prn)488 lpr_recvjob(uint32_t connid, const char *hostfrom, const char *prn)
489 {
490 struct lpr_recvjob *j;
491 int qstate;
492
493 if ((j = calloc(1, sizeof(*j))) == NULL) {
494 log_warn("%s: calloc", __func__);
495 goto fail;
496 }
497 if (lp_getprinter(&j->lp, prn) == -1)
498 goto fail;
499
500 /* Make sure queueing is not disabled. */
501 if (lp_getqueuestate(&j->lp, 0, &qstate) == -1) {
502 log_warnx("cannot get queue state");
503 goto fail;
504 }
505 if (qstate & LPQ_QUEUE_OFF)
506 goto fail;
507
508 if ((j->hostfrom = strdup(hostfrom)) == NULL) {
509 log_warn("%s: strdup", __func__);
510 goto fail;
511 }
512
513 j->connid = connid;
514 TAILQ_INIT(&j->df);
515 TAILQ_INSERT_TAIL(&recvjobs, j, entry);
516
517 lpr_recvjob_res(connid, LPR_ACK);
518 return;
519
520 fail:
521 if (j) {
522 lp_clearprinter(&j->lp);
523 free(j->hostfrom);
524 }
525 free(j);
526 lpr_recvjob_res(connid, LPR_NACK);
527 }
528
529 static void
lpr_recvjob_res(uint32_t connid,int ack)530 lpr_recvjob_res(uint32_t connid, int ack)
531 {
532 m_create(p_frontend, IMSG_LPR_RECVJOB, connid, 0, -1);
533 m_add_int(p_frontend, ack);
534 m_close(p_frontend);
535 }
536
537 static void
lpr_recvjob_cf(uint32_t connid,size_t size,const char * filename)538 lpr_recvjob_cf(uint32_t connid, size_t size, const char *filename)
539 {
540 struct lpr_recvjob *j;
541 char fname[PATH_MAX];
542 int fd;
543
544 fd = -1;
545 TAILQ_FOREACH(j, &recvjobs, entry)
546 if (j->connid == connid)
547 break;
548 if (j == NULL) {
549 log_warnx("invalid job id");
550 goto done;
551 }
552
553 if (j->cfname) {
554 log_warnx("duplicate control file");
555 goto done;
556 }
557
558 if (!lp_validfilename(filename, 1)) {
559 log_warnx("invalid control file name %s", filename);
560 goto done;
561 }
562
563 /* Rewrite file to make sure the hostname is correct. */
564 (void)strlcpy(fname, filename, 7);
565 if (strlcat(fname, j->hostfrom, sizeof(fname)) >= sizeof(fname)) {
566 log_warnx("filename too long");
567 goto done;
568 }
569
570 if ((j->cfname = strdup(fname)) == NULL) {
571 log_warn("%s: stdrup", __func__);
572 goto done;
573 }
574
575 fd = lp_create(&j->lp, 1, size, j->cfname);
576 if (fd == -1) {
577 if (errno == EFBIG || errno == ENOSPC)
578 log_warn("rejected control file");
579 else
580 log_warnx("cannot create control file");
581 free(j->cfname);
582 j->cfname = NULL;
583 }
584
585 done:
586 m_create(p_frontend, IMSG_LPR_RECVJOB_CF, connid, 0, fd);
587 m_add_int(p_frontend, (fd == -1) ? LPR_NACK : LPR_ACK);
588 m_add_size(p_frontend, size);
589 m_close(p_frontend);
590 }
591
592 static void
lpr_recvjob_df(uint32_t connid,size_t size,const char * filename)593 lpr_recvjob_df(uint32_t connid, size_t size, const char *filename)
594 {
595 struct lpr_recvfile *f;
596 struct lpr_recvjob *j;
597 int fd;
598
599 fd = -1;
600 TAILQ_FOREACH(j, &recvjobs, entry)
601 if (j->connid == connid)
602 break;
603 if (j == NULL) {
604 log_warnx("invalid job id");
605 goto done;
606 }
607
608 if (!lp_validfilename(filename, 0)) {
609 log_warnx("invalid data file name %s", filename);
610 goto done;
611 }
612
613 if ((f = calloc(1, sizeof(*f))) == NULL) {
614 log_warn("%s: calloc", __func__);
615 goto done;
616 }
617
618 if ((f->dfname = strdup(filename)) == NULL) {
619 log_warn("%s: strdup", __func__);
620 free(f);
621 goto done;
622 }
623
624 fd = lp_create(&j->lp, 0, size, f->dfname);
625 if (fd == -1) {
626 if (errno == EFBIG || errno == ENOSPC)
627 log_warn("rejected data file");
628 else
629 log_warnx("cannot create data file");
630 free(f->dfname);
631 free(f);
632 goto done;
633 }
634
635 j->dfcount += 1;
636 j->dfsize += size;
637 TAILQ_INSERT_TAIL(&j->df, f, entry);
638
639 done:
640 m_create(p_frontend, IMSG_LPR_RECVJOB_DF, connid, 0, fd);
641 m_add_int(p_frontend, (fd == -1) ? LPR_NACK : LPR_ACK);
642 m_add_size(p_frontend, size);
643 m_close(p_frontend);
644 }
645
646 static void
lpr_recvjob_clear(uint32_t connid)647 lpr_recvjob_clear(uint32_t connid)
648 {
649 struct lpr_recvfile *f;
650 struct lpr_recvjob *j;
651
652 TAILQ_FOREACH(j, &recvjobs, entry)
653 if (j->connid == connid)
654 break;
655 if (j == NULL) {
656 log_warnx("invalid job id");
657 return;
658 }
659
660 if (j->cfname) {
661 j->cfname[0] = 'c';
662 if (lp_unlink(&j->lp, j->cfname) == -1)
663 log_warn("cannot unlink %s", j->cfname);
664 j->cfname[0] = 't';
665 if (lp_unlink(&j->lp, j->cfname) == 1)
666 log_warn("cannot unlink %s", j->cfname);
667 free(j->cfname);
668 j->cfname = NULL;
669 }
670
671 while ((f = TAILQ_FIRST(&j->df))) {
672 TAILQ_REMOVE(&j->df, f, entry);
673 if (lp_unlink(&j->lp, f->dfname) == -1)
674 log_warn("cannot unlink %s", f->dfname);
675 free(f->dfname);
676 free(f);
677 }
678 }
679
680 static void
lpr_recvjob_commit(uint32_t connid)681 lpr_recvjob_commit(uint32_t connid)
682 {
683 struct lpr_recvjob *j;
684 int ack;
685
686 ack = LPR_NACK;
687 TAILQ_FOREACH(j, &recvjobs, entry)
688 if (j->connid == connid)
689 break;
690 if (j == NULL) {
691 log_warnx("invalid job id");
692 return;
693 }
694
695 if (!j->cfname) {
696 log_warnx("no control file received from %s", j->hostfrom);
697 lpr_recvjob_clear(connid);
698 lpr_recvjob_free(j);
699 return;
700 }
701
702 if ((lp_commit(&j->lp, j->cfname) == -1)) {
703 log_warn("cannot commit %s", j->cfname);
704 lpr_recvjob_clear(connid);
705 lpr_recvjob_free(j);
706 return;
707 }
708
709 log_info("received job %s printer=%s host=%s files=%d size=%zu",
710 j->cfname, j->lp.lp_name, j->hostfrom, j->dfcount, j->dfsize);
711
712 /* Start the printer. */
713 lpr_printjob(j->lp.lp_name);
714 lpr_recvjob_free(j);
715 }
716
717 static void
lpr_recvjob_rollback(uint32_t connid)718 lpr_recvjob_rollback(uint32_t connid)
719 {
720 struct lpr_recvjob *j;
721
722 lpr_recvjob_clear(connid);
723
724 TAILQ_FOREACH(j, &recvjobs, entry)
725 if (j->connid == connid)
726 break;
727 if (j == NULL) {
728 log_warnx("invalid job id");
729 return;
730 }
731 lpr_recvjob_free(j);
732 }
733
734 static void
lpr_recvjob_free(struct lpr_recvjob * j)735 lpr_recvjob_free(struct lpr_recvjob *j)
736 {
737 struct lpr_recvfile *f;
738
739 TAILQ_REMOVE(&recvjobs, j, entry);
740 lp_clearprinter(&j->lp);
741 free(j->hostfrom);
742 free(j->cfname);
743 while ((f = TAILQ_FIRST(&j->df))) {
744 TAILQ_REMOVE(&j->df, f, entry);
745 free(f->dfname);
746 free(f);
747 }
748 }
749