xref: /openbsd/usr.sbin/lpd/engine_lpr.c (revision 9d855d3d)
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