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, *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 if ((ptr[0] == 'd') && (ptr[1] == 'f') && 346 (ptr[3] == 'X') && (ptr[4] == 'X') && 347 (ptr[5] == 'X')) { 348 ptr[3] = '0' + (id / 100) % 10; 349 ptr[4] = '0' + (id / 10) % 10; 350 ptr[5] = '0' + id % 10; 351 352 if (strncmp(&ptr[6], host, strlen(host)) != 0) { 353 free(datacpy); 354 return (-1); 355 } 356 } else { 357 free(datacpy); 358 return (-1); 359 } 360 } 361 362 } 363 free(datacpy); 364 365 if (!host_present) { 366 return (-1); 367 } 368 369 return (1); 370 } 371 372 static int 373 send_lpd_message(int fd, char *fmt, ...) 374 { 375 char buf[BUFSIZ]; 376 size_t size; 377 va_list ap; 378 379 va_start(ap, fmt); 380 size = vsnprintf(buf, sizeof (buf), fmt, ap); 381 va_end(ap); 382 if (size == 0) 383 size = 1; 384 385 syslog(LOG_DEBUG, "lpd_messsage(%d, %s)", fd, buf); 386 387 if (write(fd, buf, size) != size) { 388 errno = EIO; 389 return (-1); 390 } 391 392 if ((read(fd, buf, 1) != 1) || (buf[0] != 0)) 393 return (-1); 394 395 return (0); 396 } 397 398 static int 399 send_data_file(int sock, char *dfname, char *name) 400 { 401 size_t len; 402 off_t off = 0; 403 struct stat st; 404 char buf[32]; 405 int fd = -1; 406 407 if (strcmp(name, "standard input") != 0) { 408 if ((fd = open(name, O_RDONLY)) < 0) 409 return (-1); 410 411 if (fstat(fd, &st) < 0) 412 return (-1); 413 } else 414 st.st_size = MAXINT; /* should be 0 */ 415 416 /* request data file transfer, read ack/nack */ 417 errno = ENOSPC; 418 if (send_lpd_message(sock, "\003%d %s\n", st.st_size, dfname) < 0) 419 return (-1); 420 421 if (fd != -1) { 422 /* write the data */ 423 if (sendfile(sock, fd, &off, st.st_size) != st.st_size) 424 return (-1); 425 close(fd); 426 427 /* request ack/nack after the data transfer */ 428 errno = EIO; 429 if (send_lpd_message(sock, "") < 0) 430 return (-1); 431 } 432 433 return (0); 434 } 435 436 static int 437 send_control_file(int sock, char *data, int id) 438 { 439 int len; 440 char buf[BUFSIZ]; 441 char *ptr, *iter = NULL; 442 char *datacpy = NULL; 443 char *host = NULL; 444 445 len = strlen(data); 446 447 if ((datacpy = strdup(data)) == NULL) 448 return (-1); 449 450 syslog(LOG_DEBUG, "cfA: %s\n", datacpy); 451 452 for (ptr = strtok_r(datacpy, "\n", &iter); ptr != NULL; 453 ptr = strtok_r(NULL, "\n", &iter)) { 454 455 if (ptr[0] != 'H') 456 continue; 457 458 ptr++; 459 host = ptr; 460 syslog(LOG_DEBUG, "hostname: %s\n", host); 461 } 462 463 free(datacpy); 464 465 /* request data file transfer, read ack/nack */ 466 errno = ENOSPC; 467 if (send_lpd_message(sock, "\002%d cfA%.3d%s\n", len, id, host) < 0) 468 return (-1); 469 470 /* write the data */ 471 if (write(sock, data, len) != len) 472 return (-1); 473 474 /* request ack/nack after the data transfer */ 475 errno = EIO; 476 if (send_lpd_message(sock, "") < 0) 477 return (-1); 478 479 return (0); 480 } 481 482 483 static int 484 submit_job(int sock, char *printer, int job_id, char *path) 485 { 486 struct stat st; 487 int current = 0; 488 off_t off = 0; 489 char *metadata = NULL; 490 char *ptr, *iter = NULL; 491 int fd, err; 492 int sent_files = 0; 493 char buf[BUFSIZ]; 494 size_t len; 495 496 /* open the control file */ 497 if ((fd = open(path, O_RDONLY)) < 0) { 498 syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): open(): %m", 499 sock, printer, job_id, path); 500 return (-1); 501 } 502 503 /* get the size of the control file */ 504 if (fstat(fd, &st) < 0) { 505 syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): fstat(): %m", 506 sock, printer, job_id, path); 507 close(fd); 508 return (-1); 509 } 510 511 /* allocate memory for the control file */ 512 if ((metadata = calloc(1, st.st_size + 1)) == NULL) { 513 syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): calloc(): %m", 514 sock, printer, job_id, path); 515 close(fd); 516 return (-1); 517 } 518 519 /* read in the control file */ 520 if (read(fd, metadata, st.st_size) != st.st_size) { 521 syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): read(): %m", 522 sock, printer, job_id, path); 523 free(metadata); 524 close(fd); 525 return (-1); 526 } 527 528 /* massage the control file */ 529 if (massage_control_data(metadata, job_id) < 0) { 530 /* bad control data, dump the job */ 531 syslog(LOG_ALERT, 532 "bad control file, possible subversion attempt"); 533 free(metadata); 534 errno = EINVAL; 535 close(fd); 536 return (-1); 537 } 538 539 /* request to transfer the job */ 540 if (send_lpd_message(sock, "\002%s\n", printer) < 0) { 541 /* no such (or disabled) queue, got to love rfc-1179 */ 542 errno = ENOENT; 543 return (-1); 544 } 545 546 /* send the control data */ 547 if (send_control_file(sock, metadata, job_id) < 0) { 548 err = errno; 549 write(sock, "\001\n", 2); /* abort */ 550 errno = err; 551 return (-1); 552 } 553 554 /* walk the control file sending the data files */ 555 for (ptr = strtok_r(metadata, "\n", &iter); ptr != NULL; 556 ptr = strtok_r(NULL, "\n", &iter)) { 557 char *name = NULL; 558 559 if (ptr[0] != 'U') 560 continue; 561 562 name = strtok_r(NULL, "\n", &iter); 563 if (name[0] != 'N') 564 continue; 565 566 ptr++; 567 name++; 568 569 if (send_data_file(sock, ptr, name) < 0) { 570 err = errno; 571 write(sock, "\001\n", 2); /* abort */ 572 errno = err; 573 return (-1); 574 } 575 if (strcmp(name, "standard input") != 0) 576 sent_files++; 577 } 578 579 /* write back the job-id */ 580 err = errno; 581 if ((fd = open(path, O_WRONLY)) >= 0) { 582 ftruncate(fd, 0); 583 write(fd, &job_id, sizeof (job_id)); 584 close(fd); 585 } 586 errno = err; 587 588 if (sent_files != 0) { 589 err = errno; 590 close(sock); 591 errno = err; 592 } 593 594 return (0); 595 } 596 static int 597 query(int fd, char *printer, int ac, char **av) 598 { 599 char buf[BUFSIZ]; 600 int rc, len; 601 602 /* build the request */ 603 snprintf(buf, sizeof (buf), "\04%s", printer); 604 add_args(ac, av, buf, sizeof (buf)); 605 strlcat(buf, "\n", sizeof (buf)); 606 len = strlen(buf); 607 608 if (((rc = write(fd, buf, len)) >= 0) && (rc != len)) { 609 errno = EMSGSIZE; 610 rc = -1; 611 } else 612 rc = 0; 613 614 return (rc); 615 } 616 617 static int 618 cancel(int fd, char *printer, int ac, char **av) 619 { 620 char buf[BUFSIZ]; 621 int rc, len; 622 623 /* build the request */ 624 snprintf(buf, sizeof (buf), "\05%s %s", printer, get_user_name()); 625 add_args(ac, av, buf, sizeof (buf)); 626 strlcat(buf, "\n", sizeof (buf)); 627 len = strlen(buf); 628 629 if (((rc = write(fd, buf, len)) >= 0) && (rc != len)) { 630 errno = EMSGSIZE; 631 rc = -1; 632 } else 633 rc = 0; 634 635 return (rc); 636 } 637 638 static void 639 usage(char *program) 640 { 641 char *name; 642 643 setreuid(getuid(), getuid()); 644 645 if ((name = strrchr(program, '/')) == NULL) 646 name = program; 647 else 648 name++; 649 650 fprintf(stderr, "usage:\t%s -H host [-t timeout] -s queue control ]\n", 651 name); 652 fprintf(stderr, "\t%s -H host [-t timeout] -c queue [user|job ...]\n", 653 name); 654 fprintf(stderr, "\t%s -H host [-t timeout] -q queue [user|job ...]\n", 655 name); 656 exit(EINVAL); 657 } 658 659 /* 660 * The main program temporarily loses privilege while searching the command 661 * line arguments. It then allocates any resources it need privilege for 662 * job-id, reserved port. Once it has the resources it needs, it perminently 663 * drops all elevated privilege. It ghen connects to the remote print service 664 * based on destination hostname. Doing it this way reduces the potenential 665 * opportunity for a breakout with elevated privilege, breakout with an 666 * unconnected reserved port, and exploitation of the remote print service 667 * by a calling program. 668 */ 669 int 670 main(int ac, char *av[]) 671 { 672 enum { OP_NONE, OP_SUBMIT, OP_QUERY, OP_CANCEL } operation = OP_NONE; 673 int fd, c, timeout = 0, exit_code = 0; 674 char *host = NULL, *queue = NULL; 675 uid_t uid = getuid(); 676 #ifdef PRIV_ALLSETS 677 priv_set_t *saveset = NULL; 678 #endif 679 680 openlog("lpd-port", LOG_PID, LOG_LPR); 681 682 #ifdef PRIV_ALLSETS 683 684 /* lose as much as we can perminently and temporarily drop the rest. */ 685 686 if ((saveset = priv_str_to_set("PRIV_NET_PRIVADDR," 687 "PRIV_FILE_DAC_READ,PRIV_FILE_DAC_WRITE,", 688 ",", (const char **)NULL)) == NULL) { 689 syslog(LOG_ERR, 690 "lpd_port: priv_str_to_set saveset failed: %m\n"); 691 return (-1); 692 } 693 694 if ((setppriv(PRIV_SET, PRIV_PERMITTED, saveset)) < 0) { 695 syslog(LOG_ERR, "lpd_port:setppriv:priv_set failed: %m"); 696 return (-1); 697 } 698 699 /* 700 * These privileges permanently dropped in next_job_id() and 701 * reserved_port() 702 */ 703 704 if ((setppriv(PRIV_OFF, PRIV_EFFECTIVE, saveset)) < 0) { 705 syslog(LOG_ERR, "lpd_port:setppriv:priv_off failed: %m"); 706 return (-1); 707 } 708 709 priv_freeset(saveset); 710 711 syslog(LOG_DEBUG, "using privs"); 712 #else 713 714 syslog(LOG_DEBUG, "no privs"); 715 seteuid(uid); 716 #endif 717 718 while ((c = getopt(ac, av, "H:t:c:q:s:")) != EOF) { 719 switch (c) { 720 case 'H': 721 host = optarg; 722 break; 723 case 't': 724 timeout = atoi(optarg); 725 break; 726 case 'c': 727 if (operation != OP_NONE) 728 usage(av[0]); 729 operation = OP_CANCEL; 730 queue = optarg; 731 break; 732 case 'q': 733 if (operation != OP_NONE) 734 usage(av[0]); 735 operation = OP_QUERY; 736 queue = optarg; 737 break; 738 case 's': 739 if (operation != OP_NONE) 740 usage(av[0]); 741 operation = OP_SUBMIT; 742 queue = optarg; 743 break; 744 default: 745 usage(av[0]); 746 /* does not return */ 747 } 748 } 749 750 if ((host == NULL) || (queue == NULL) || (timeout < 0) || 751 (operation == OP_NONE)) 752 usage(av[0]); 753 754 if (operation == OP_SUBMIT) /* get a job-id if we need it */ 755 if ((c = next_job_id()) < 0) { 756 syslog(LOG_ERR, "lpd_port:main:next_job_id fails"); 757 return (-1); 758 } 759 760 if ((fd = reserved_port()) < 0) { 761 syslog(LOG_ERR, "reserved_port() failed %m"); 762 return (errno); 763 } 764 765 /* 766 * we no longer want or need any elevated privilege, lose it all 767 * permanently. 768 */ 769 770 setreuid(uid, uid); 771 772 /* connect to the print service */ 773 if ((fd = sock_connect(fd, host, timeout)) < 0) 774 return (errno); 775 776 /* perform the requested operation */ 777 switch (operation) { 778 case OP_SUBMIT: /* transfer the job, close the fd */ 779 if (submit_job(fd, queue, c, av[optind]) < 0) 780 exit_code = errno; 781 break; 782 case OP_QUERY: /* send the query string, return the fd */ 783 if (query(fd, queue, ac - optind, &av[optind]) < 0) 784 exit_code = errno; 785 break; 786 case OP_CANCEL: /* send the cancel string, return the fd */ 787 if (cancel(fd, queue, ac - optind, &av[optind]) < 0) 788 exit_code = errno; 789 break; 790 default: /* This should never happen */ 791 exit_code = EINVAL; 792 } 793 794 795 /* if the operation succeeded, send the fd to our parent */ 796 if ((exit_code == 0) && (sendfd(1, fd) < 0)) { 797 char buf[BUFSIZ]; 798 799 exit_code = errno; 800 801 /* sendfd() failed, dump the socket data for the heck of it */ 802 while ((c = read(fd, buf, sizeof (buf))) > 0) 803 write(1, buf, c); 804 } 805 806 syslog(LOG_DEBUG, "exit code: %d", exit_code); 807 return (exit_code); 808 } 809