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