1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 */ 27 28 /* $Id: lpd-port.c 155 2006-04-26 02:34:54Z ktou $ */ 29 30 #include <config-site.h> 31 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <unistd.h> 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <fcntl.h> 38 #include <stdarg.h> 39 #include <string.h> 40 #include <signal.h> 41 #include <sys/socket.h> 42 #include <netinet/in.h> 43 #include <arpa/inet.h> 44 #include <netdb.h> 45 #include <errno.h> 46 #include <syslog.h> 47 #include <values.h> 48 #include <stropts.h> /* for sendfd */ 49 #include <sys/uio.h> /* for sendmsg stuff */ 50 #include <pwd.h> 51 #include <sys/sendfile.h> 52 #include <ctype.h> 53 #ifdef HAVE_PRIV_H 54 #include <priv.h> 55 #endif 56 57 #ifndef JOB_ID_FILE 58 #define JOB_ID_FILE "/var/run/rfc-1179.seq" 59 #endif /* JOB_ID_FILE */ 60 61 static int 62 sendfd(int sockfd, int fd) 63 { 64 syslog(LOG_DEBUG, "sendfd(%d, %d)", sockfd, fd); 65 66 #if defined(sun) && defined(unix) && defined(I_SENDFD) 67 return (ioctl(sockfd, I_SENDFD, fd)); 68 #else 69 struct iovec iov[1]; 70 struct msghdr msg; 71 #ifdef CMSG_DATA 72 struct cmsghdr cmp[1]; 73 char buf[2]; /* send/recv 2 byte protocol */ 74 75 iov[0].iov_base = buf; 76 iov[0].iov_len = 2; 77 78 cmp[0].cmsg_level = SOL_SOCKET; 79 cmp[0].cmsg_type = SCM_RIGHTS; 80 cmp[0].cmsg_len = sizeof (struct cmsghdr) + sizeof (int); 81 * (int *)CMSG_DATA(cmp) = fd; 82 83 buf[1] = 0; 84 buf[0] = 0; 85 msg.msg_control = cmp; 86 msg.msg_controllen = sizeof (struct cmsghdr) + sizeof (int); 87 #else 88 iov[0].iov_base = NULL; 89 iov[0].iov_len = 0; 90 msg.msg_accrights = (caddr_t)&fd; 91 msg.msg_accrights = sizeof (fd); 92 #endif 93 msg.msg_iov = iov; 94 msg.msg_iovlen = 1; 95 msg.msg_name = NULL; 96 msg.msg_namelen = 0; 97 98 return (sendmsg(sockfd, &msg, 0)); 99 #endif 100 } 101 102 static void 103 null(int i) 104 { 105 } 106 107 static int 108 sock_connect(int sock, char *host, int timeout) 109 { 110 struct hostent *hp; 111 struct servent *sp; 112 #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF) 113 struct sockaddr_in6 sin; 114 #else 115 struct sockaddr_in sin; 116 #endif 117 static void (*old_handler)(); 118 int err, error_num; 119 unsigned timo = 1; 120 121 /* 122 * Get the host address and port number to connect to. 123 */ 124 if (host == NULL) { 125 return (-1); 126 } 127 128 /* linux style NULL usage */ 129 (void) memset((char *)&sin, (int)NULL, sizeof (sin)); 130 131 #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF) 132 if ((hp = getipnodebyname(host, AF_INET6, AI_DEFAULT, 133 &error_num)) == NULL) { 134 errno = ENOENT; 135 return (-1); 136 } 137 (void) memcpy((caddr_t)&sin.sin6_addr, hp->h_addr, hp->h_length); 138 sin.sin6_family = hp->h_addrtype; 139 #else 140 if ((hp = gethostbyname(host)) == NULL) { 141 errno = ENOENT; 142 return (-1); 143 } 144 145 (void) memcpy((caddr_t)&sin.sin_addr, hp->h_addr, hp->h_length); 146 sin.sin_family = hp->h_addrtype; 147 #endif 148 149 if ((sp = getservbyname("printer", "tcp")) == NULL) { 150 errno = ENOENT; 151 return (-1); 152 } 153 154 #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF) 155 sin.sin6_port = sp->s_port; 156 #else 157 sin.sin_port = sp->s_port; 158 #endif 159 160 retry: 161 old_handler = signal(SIGALRM, null); 162 (void) alarm(timeout); 163 164 if (connect(sock, (struct sockaddr *)&sin, sizeof (sin)) < 0) { 165 (void) alarm(0); 166 (void) signal(SIGALRM, old_handler); 167 168 if (errno == ECONNREFUSED && timo <= 16) { 169 (void) sleep(timo); 170 timo *= 2; 171 goto retry; 172 } 173 174 return (-1); 175 } 176 177 (void) alarm(0); 178 (void) signal(SIGALRM, old_handler); 179 return (sock); 180 } 181 182 static int 183 next_job_id() 184 { 185 int fd, result = getpid() % 1000; 186 187 /* gain back enough privilege to open the id file */ 188 #ifdef PRIV_ALLSETS 189 if ((priv_set(PRIV_ON, PRIV_EFFECTIVE, 190 PRIV_FILE_DAC_READ, PRIV_FILE_DAC_WRITE, NULL)) < 0) { 191 syslog(LOG_ERR, "lpd_port:next_job_id:priv_set fails: : %m"); 192 return (-1); 193 } 194 #else 195 seteuid(0); 196 #endif 197 198 /* open the sequence file */ 199 if (((fd = open(JOB_ID_FILE, O_RDWR)) < 0) && (errno == ENOENT)) 200 fd = open(JOB_ID_FILE, O_CREAT|O_EXCL|O_RDWR, 0644); 201 202 syslog(LOG_DEBUG, "sequence file fd: %d", fd); 203 204 /* drop our privilege again */ 205 #ifdef PRIV_ALLSETS 206 /* drop file access privilege */ 207 priv_set(PRIV_OFF, PRIV_PERMITTED, 208 PRIV_FILE_DAC_READ, PRIV_FILE_DAC_WRITE, NULL); 209 #else 210 seteuid(getuid()); 211 #endif 212 213 if (fd >= 0) { 214 /* wait for a lock on the file */ 215 if (lockf(fd, F_LOCK, 0) == 0) { 216 char buf[8]; 217 int next; 218 219 /* get the current id */ 220 (void) memset(buf, 0, sizeof (buf)); 221 if (read(fd, buf, sizeof (buf)) > 0) 222 result = atoi(buf); 223 224 next = ((result < 999) ? (result + 1) : 0); 225 226 /* store the next id in the file */ 227 snprintf(buf, sizeof (buf), "%.3d", next); 228 if ((lseek(fd, 0, SEEK_SET) == 0) && 229 (ftruncate(fd, 0) == 0)) 230 write(fd, buf, strlen(buf)); 231 } 232 close(fd); 233 } 234 syslog(LOG_DEBUG, "next_job_id() is %d", result); 235 236 return (result); 237 } 238 239 static int 240 reserved_port() 241 { 242 int result = -1; 243 int port; 244 245 /* gain back enough privilege to open a reserved port */ 246 #ifdef PRIV_ALLSETS 247 if ((priv_set( 248 PRIV_ON, PRIV_EFFECTIVE, PRIV_NET_PRIVADDR, NULL)) != 0) { 249 syslog(LOG_ERR, "priv_set fails for net_privaddr %m"); 250 return (-1); 251 } 252 #else 253 seteuid(0); 254 #endif 255 256 #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF) 257 port = 0; /* set to 0, rresvport_af() will find us one. */ 258 result = rresvport_af(&port, AF_INET6); 259 #else 260 port = IPPORT_RESERVED - 1; 261 while (((result = rresvport(&port)) < 0) && (port >= 0)) 262 port--; 263 #endif 264 265 /* drop our privilege again */ 266 #ifdef PRIV_ALLSETS 267 priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_NET_PRIVADDR, NULL); 268 #else 269 seteuid(getuid()); 270 #endif 271 272 return (result); 273 } 274 275 static char * 276 get_user_name() 277 { 278 static struct passwd *p = NULL; 279 280 if ((p = getpwuid(getuid())) != NULL) 281 return (p->pw_name); 282 else 283 return ("unknown"); 284 } 285 286 static void 287 add_args(int ac, char **av, char *buf, size_t len) 288 { 289 while (ac--) { 290 strlcat(buf, " ", len); 291 strlcat(buf, *(av++), len); 292 } 293 } 294 295 static int 296 massage_control_data(char *data, int id) 297 { 298 char *line, *iter = NULL; 299 char *ptr, *mod_ptr, *datacpy; 300 char host[BUFSIZ]; 301 int host_present = 0; 302 303 if (gethostname(host, sizeof (host)) != 0) 304 return (-1); 305 306 if ((datacpy = strdup(data)) == NULL) { 307 return (-1); 308 } 309 310 for (ptr = strtok_r(datacpy, "\n", &iter); ptr != NULL; 311 ptr = strtok_r(NULL, "\n", &iter)) { 312 313 if (ptr[0] == 'H') { 314 if (strncmp(++ptr, host, strlen(host)) != 0) { 315 free(datacpy); 316 return (-1); 317 } 318 host_present = 1; 319 } else if ((ptr[0] == 'P') || (ptr[0] == 'L')) { 320 /* check the user name */ 321 uid_t uid = getuid(); 322 struct passwd *pw; 323 int len; 324 325 if (uid == 0) { /* let root do what they want */ 326 continue; 327 } 328 if ((pw = getpwuid(uid)) == NULL) { 329 free(datacpy); 330 return (-1); /* failed */ 331 } 332 len = strlen(pw->pw_name); 333 if ((strncmp(++ptr, pw->pw_name, len) != 0)) { 334 free(datacpy); 335 return (-1); /* failed */ 336 } 337 } else if ((islower(ptr[0]) != 0) || (ptr[0] == 'U')) { 338 /* check/fix df?XXXhostname */ 339 ptr++; 340 341 if (strlen(ptr) < 6) { 342 free(datacpy); 343 return (-1); 344 } 345 346 /* 347 * As ptr is a copy of the string (df?XXX...) the code 348 * needs to work on the original, hence the need for 349 * mod_ptr. No need to check for a NULL mod_ptr 350 * because the required string must already exist as 351 * ptr is a copy of the original data. 352 */ 353 354 mod_ptr = strstr(data, ptr); 355 356 if ((mod_ptr[0] == 'd') && (mod_ptr[1] == 'f') && 357 (mod_ptr[3] == 'X') && (mod_ptr[4] == 'X') && 358 (mod_ptr[5] == 'X')) { 359 mod_ptr[3] = '0' + (id / 100) % 10; 360 mod_ptr[4] = '0' + (id / 10) % 10; 361 mod_ptr[5] = '0' + id % 10; 362 363 if (strncmp(&mod_ptr[6], host, strlen(host)) 364 != 0) { 365 free(datacpy); 366 return (-1); 367 } 368 } else { 369 free(datacpy); 370 return (-1); 371 } 372 } 373 374 } 375 free(datacpy); 376 377 if (!host_present) { 378 return (-1); 379 } 380 381 return (1); 382 } 383 384 static int 385 send_lpd_message(int fd, char *fmt, ...) 386 { 387 char buf[BUFSIZ]; 388 size_t size; 389 va_list ap; 390 391 va_start(ap, fmt); 392 size = vsnprintf(buf, sizeof (buf), fmt, ap); 393 va_end(ap); 394 if (size == 0) 395 size = 1; 396 397 syslog(LOG_DEBUG, "lpd_messsage(%d, %s)", fd, buf); 398 399 if (write(fd, buf, size) != size) { 400 errno = EIO; 401 return (-1); 402 } 403 404 if ((read(fd, buf, 1) != 1) || (buf[0] != 0)) 405 return (-1); 406 407 return (0); 408 } 409 410 static int 411 send_data_file(int sock, char *dfname, char *name) 412 { 413 size_t len; 414 off_t off = 0; 415 struct stat st; 416 char buf[32]; 417 int fd = -1; 418 419 if (strcmp(name, "standard input") != 0) { 420 if ((fd = open(name, O_RDONLY)) < 0) 421 return (-1); 422 423 if (fstat(fd, &st) < 0) 424 return (-1); 425 } else 426 st.st_size = MAXINT; /* should be 0 */ 427 428 /* request data file transfer, read ack/nack */ 429 errno = ENOSPC; 430 if (send_lpd_message(sock, "\003%d %s\n", st.st_size, dfname) < 0) 431 return (-1); 432 433 if (fd != -1) { 434 /* write the data */ 435 if (sendfile(sock, fd, &off, st.st_size) != st.st_size) 436 return (-1); 437 close(fd); 438 439 /* request ack/nack after the data transfer */ 440 errno = EIO; 441 if (send_lpd_message(sock, "") < 0) 442 return (-1); 443 } 444 445 return (0); 446 } 447 448 static int 449 send_control_file(int sock, char *data, int id) 450 { 451 int len; 452 char buf[BUFSIZ]; 453 char *ptr, *iter = NULL; 454 char *datacpy = NULL; 455 char *host = NULL; 456 457 len = strlen(data); 458 459 if ((datacpy = strdup(data)) == NULL) 460 return (-1); 461 462 syslog(LOG_DEBUG, "cfA: %s\n", datacpy); 463 464 for (ptr = strtok_r(datacpy, "\n", &iter); ptr != NULL; 465 ptr = strtok_r(NULL, "\n", &iter)) { 466 467 if (ptr[0] != 'H') 468 continue; 469 470 ptr++; 471 host = ptr; 472 syslog(LOG_DEBUG, "hostname: %s\n", host); 473 } 474 475 free(datacpy); 476 477 /* request data file transfer, read ack/nack */ 478 errno = ENOSPC; 479 if (send_lpd_message(sock, "\002%d cfA%.3d%s\n", len, id, host) < 0) 480 return (-1); 481 482 /* write the data */ 483 if (write(sock, data, len) != len) 484 return (-1); 485 486 /* request ack/nack after the data transfer */ 487 errno = EIO; 488 if (send_lpd_message(sock, "") < 0) 489 return (-1); 490 491 return (0); 492 } 493 494 495 static int 496 submit_job(int sock, char *printer, int job_id, char *path) 497 { 498 struct stat st; 499 int current = 0; 500 off_t off = 0; 501 char *metadata = NULL; 502 char *ptr, *iter = NULL; 503 int fd, err; 504 int sent_files = 0; 505 char buf[BUFSIZ]; 506 size_t len; 507 508 /* open the control file */ 509 if ((fd = open(path, O_RDONLY)) < 0) { 510 syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): open(): %m", 511 sock, printer, job_id, path); 512 return (-1); 513 } 514 515 /* get the size of the control file */ 516 if (fstat(fd, &st) < 0) { 517 syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): fstat(): %m", 518 sock, printer, job_id, path); 519 close(fd); 520 return (-1); 521 } 522 523 /* allocate memory for the control file */ 524 if ((metadata = calloc(1, st.st_size + 1)) == NULL) { 525 syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): calloc(): %m", 526 sock, printer, job_id, path); 527 close(fd); 528 return (-1); 529 } 530 531 /* read in the control file */ 532 if (read(fd, metadata, st.st_size) != st.st_size) { 533 syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): read(): %m", 534 sock, printer, job_id, path); 535 free(metadata); 536 close(fd); 537 return (-1); 538 } 539 540 /* massage the control file */ 541 if (massage_control_data(metadata, job_id) < 0) { 542 /* bad control data, dump the job */ 543 syslog(LOG_ALERT, 544 "bad control file, possible subversion attempt"); 545 free(metadata); 546 errno = EINVAL; 547 close(fd); 548 return (-1); 549 } 550 551 /* request to transfer the job */ 552 if (send_lpd_message(sock, "\002%s\n", printer) < 0) { 553 /* no such (or disabled) queue, got to love rfc-1179 */ 554 errno = ENOENT; 555 return (-1); 556 } 557 558 /* send the control data */ 559 if (send_control_file(sock, metadata, job_id) < 0) { 560 err = errno; 561 write(sock, "\001\n", 2); /* abort */ 562 errno = err; 563 return (-1); 564 } 565 566 /* walk the control file sending the data files */ 567 for (ptr = strtok_r(metadata, "\n", &iter); ptr != NULL; 568 ptr = strtok_r(NULL, "\n", &iter)) { 569 char *name = NULL; 570 571 if (ptr[0] != 'U') 572 continue; 573 574 name = strtok_r(NULL, "\n", &iter); 575 if (name[0] != 'N') 576 continue; 577 578 ptr++; 579 name++; 580 581 if (send_data_file(sock, ptr, name) < 0) { 582 err = errno; 583 write(sock, "\001\n", 2); /* abort */ 584 errno = err; 585 return (-1); 586 } 587 if (strcmp(name, "standard input") != 0) 588 sent_files++; 589 } 590 591 /* write back the job-id */ 592 err = errno; 593 if ((fd = open(path, O_WRONLY)) >= 0) { 594 ftruncate(fd, 0); 595 write(fd, &job_id, sizeof (job_id)); 596 close(fd); 597 } 598 errno = err; 599 600 if (sent_files != 0) { 601 err = errno; 602 close(sock); 603 errno = err; 604 } 605 606 return (0); 607 } 608 static int 609 query(int fd, char *printer, int ac, char **av) 610 { 611 char buf[BUFSIZ]; 612 int rc, len; 613 614 /* build the request */ 615 snprintf(buf, sizeof (buf), "\04%s", printer); 616 add_args(ac, av, buf, sizeof (buf)); 617 strlcat(buf, "\n", sizeof (buf)); 618 len = strlen(buf); 619 620 if (((rc = write(fd, buf, len)) >= 0) && (rc != len)) { 621 errno = EMSGSIZE; 622 rc = -1; 623 } else 624 rc = 0; 625 626 return (rc); 627 } 628 629 static int 630 cancel(int fd, char *printer, int ac, char **av) 631 { 632 char buf[BUFSIZ]; 633 int rc, len; 634 635 /* build the request */ 636 snprintf(buf, sizeof (buf), "\05%s %s", printer, get_user_name()); 637 add_args(ac, av, buf, sizeof (buf)); 638 strlcat(buf, "\n", sizeof (buf)); 639 len = strlen(buf); 640 641 if (((rc = write(fd, buf, len)) >= 0) && (rc != len)) { 642 errno = EMSGSIZE; 643 rc = -1; 644 } else 645 rc = 0; 646 647 return (rc); 648 } 649 650 static void 651 usage(char *program) 652 { 653 char *name; 654 655 setreuid(getuid(), getuid()); 656 657 if ((name = strrchr(program, '/')) == NULL) 658 name = program; 659 else 660 name++; 661 662 fprintf(stderr, "usage:\t%s -H host [-t timeout] -s queue control ]\n", 663 name); 664 fprintf(stderr, "\t%s -H host [-t timeout] -c queue [user|job ...]\n", 665 name); 666 fprintf(stderr, "\t%s -H host [-t timeout] -q queue [user|job ...]\n", 667 name); 668 exit(EINVAL); 669 } 670 671 /* 672 * The main program temporarily loses privilege while searching the command 673 * line arguments. It then allocates any resources it need privilege for 674 * job-id, reserved port. Once it has the resources it needs, it perminently 675 * drops all elevated privilege. It ghen connects to the remote print service 676 * based on destination hostname. Doing it this way reduces the potenential 677 * opportunity for a breakout with elevated privilege, breakout with an 678 * unconnected reserved port, and exploitation of the remote print service 679 * by a calling program. 680 */ 681 int 682 main(int ac, char *av[]) 683 { 684 enum { OP_NONE, OP_SUBMIT, OP_QUERY, OP_CANCEL } operation = OP_NONE; 685 int fd, c, timeout = 0, exit_code = 0; 686 char *host = NULL, *queue = NULL; 687 uid_t uid = getuid(); 688 #ifdef PRIV_ALLSETS 689 priv_set_t *saveset = NULL; 690 #endif 691 692 openlog("lpd-port", LOG_PID, LOG_LPR); 693 694 #ifdef PRIV_ALLSETS 695 696 /* lose as much as we can perminently and temporarily drop the rest. */ 697 698 if ((saveset = priv_str_to_set("PRIV_NET_PRIVADDR," 699 "PRIV_FILE_DAC_READ,PRIV_FILE_DAC_WRITE,", 700 ",", (const char **)NULL)) == NULL) { 701 syslog(LOG_ERR, 702 "lpd_port: priv_str_to_set saveset failed: %m\n"); 703 return (-1); 704 } 705 706 if ((setppriv(PRIV_SET, PRIV_PERMITTED, saveset)) < 0) { 707 syslog(LOG_ERR, "lpd_port:setppriv:priv_set failed: %m"); 708 return (-1); 709 } 710 711 /* 712 * These privileges permanently dropped in next_job_id() and 713 * reserved_port() 714 */ 715 716 if ((setppriv(PRIV_OFF, PRIV_EFFECTIVE, saveset)) < 0) { 717 syslog(LOG_ERR, "lpd_port:setppriv:priv_off failed: %m"); 718 return (-1); 719 } 720 721 priv_freeset(saveset); 722 723 syslog(LOG_DEBUG, "using privs"); 724 #else 725 726 syslog(LOG_DEBUG, "no privs"); 727 seteuid(uid); 728 #endif 729 730 while ((c = getopt(ac, av, "H:t:c:q:s:")) != EOF) { 731 switch (c) { 732 case 'H': 733 host = optarg; 734 break; 735 case 't': 736 timeout = atoi(optarg); 737 break; 738 case 'c': 739 if (operation != OP_NONE) 740 usage(av[0]); 741 operation = OP_CANCEL; 742 queue = optarg; 743 break; 744 case 'q': 745 if (operation != OP_NONE) 746 usage(av[0]); 747 operation = OP_QUERY; 748 queue = optarg; 749 break; 750 case 's': 751 if (operation != OP_NONE) 752 usage(av[0]); 753 operation = OP_SUBMIT; 754 queue = optarg; 755 break; 756 default: 757 usage(av[0]); 758 /* does not return */ 759 } 760 } 761 762 if ((host == NULL) || (queue == NULL) || (timeout < 0) || 763 (operation == OP_NONE)) 764 usage(av[0]); 765 766 if (operation == OP_SUBMIT) /* get a job-id if we need it */ 767 if ((c = next_job_id()) < 0) { 768 syslog(LOG_ERR, "lpd_port:main:next_job_id fails"); 769 return (-1); 770 } 771 772 if ((fd = reserved_port()) < 0) { 773 syslog(LOG_ERR, "reserved_port() failed %m"); 774 return (errno); 775 } 776 777 /* 778 * we no longer want or need any elevated privilege, lose it all 779 * permanently. 780 */ 781 782 setreuid(uid, uid); 783 784 /* connect to the print service */ 785 if ((fd = sock_connect(fd, host, timeout)) < 0) 786 return (errno); 787 788 /* perform the requested operation */ 789 switch (operation) { 790 case OP_SUBMIT: /* transfer the job, close the fd */ 791 if (submit_job(fd, queue, c, av[optind]) < 0) 792 exit_code = errno; 793 break; 794 case OP_QUERY: /* send the query string, return the fd */ 795 if (query(fd, queue, ac - optind, &av[optind]) < 0) 796 exit_code = errno; 797 break; 798 case OP_CANCEL: /* send the cancel string, return the fd */ 799 if (cancel(fd, queue, ac - optind, &av[optind]) < 0) 800 exit_code = errno; 801 break; 802 default: /* This should never happen */ 803 exit_code = EINVAL; 804 } 805 806 807 /* if the operation succeeded, send the fd to our parent */ 808 if ((exit_code == 0) && (sendfd(1, fd) < 0)) { 809 char buf[BUFSIZ]; 810 811 exit_code = errno; 812 813 /* sendfd() failed, dump the socket data for the heck of it */ 814 while ((c = read(fd, buf, sizeof (buf))) > 0) 815 write(1, buf, c); 816 } 817 818 syslog(LOG_DEBUG, "exit code: %d", exit_code); 819 return (exit_code); 820 } 821