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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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