usbdump.c revision 221604
1/*-
2 * Copyright (c) 2010 Weongyo Jeong <weongyo@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer,
10 *    without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13 *    redistribution must be conditioned upon including a substantially
14 *    similar Disclaimer requirement for further binary redistribution.
15 *
16 * NO WARRANTY
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 * THE POSSIBILITY OF SUCH DAMAGES.
28 *
29 * $FreeBSD: head/usr.sbin/usbdump/usbdump.c 221604 2011-05-07 16:28:23Z hselasky $
30 */
31
32#include <sys/param.h>
33#include <sys/endian.h>
34#include <sys/ioctl.h>
35#include <sys/socket.h>
36#include <sys/stat.h>
37#include <sys/utsname.h>
38#include <net/if.h>
39#include <net/bpf.h>
40#include <dev/usb/usb.h>
41#include <dev/usb/usb_pf.h>
42#include <dev/usb/usbdi.h>
43#include <errno.h>
44#include <fcntl.h>
45#include <limits.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#include <time.h>
50#include <unistd.h>
51#include <sysexits.h>
52#include <err.h>
53
54struct usbcap {
55	int		fd;		/* fd for /dev/usbpf */
56	uint32_t	bufsize;
57	uint8_t		*buffer;
58
59	/* for -w option */
60	int		wfd;
61	/* for -r option */
62	int		rfd;
63};
64
65struct usbcap_filehdr {
66	uint32_t	magic;
67#define	USBCAP_FILEHDR_MAGIC	0x9a90000e
68	uint8_t   	major;
69	uint8_t		minor;
70	uint8_t		reserved[26];
71} __packed;
72
73static int doexit = 0;
74static int pkt_captured = 0;
75static int verbose = 0;
76static const char *i_arg = "usbus0";
77static const char *r_arg = NULL;
78static const char *w_arg = NULL;
79static const char *errstr_table[USB_ERR_MAX] = {
80	[USB_ERR_NORMAL_COMPLETION]	= "0",
81	[USB_ERR_PENDING_REQUESTS]	= "PENDING_REQUESTS",
82	[USB_ERR_NOT_STARTED]		= "NOT_STARTED",
83	[USB_ERR_INVAL]			= "INVAL",
84	[USB_ERR_NOMEM]			= "NOMEM",
85	[USB_ERR_CANCELLED]		= "CANCELLED",
86	[USB_ERR_BAD_ADDRESS]		= "BAD_ADDRESS",
87	[USB_ERR_BAD_BUFSIZE]		= "BAD_BUFSIZE",
88	[USB_ERR_BAD_FLAG]		= "BAD_FLAG",
89	[USB_ERR_NO_CALLBACK]		= "NO_CALLBACK",
90	[USB_ERR_IN_USE]		= "IN_USE",
91	[USB_ERR_NO_ADDR]		= "NO_ADDR",
92	[USB_ERR_NO_PIPE]		= "NO_PIPE",
93	[USB_ERR_ZERO_NFRAMES]		= "ZERO_NFRAMES",
94	[USB_ERR_ZERO_MAXP]		= "ZERO_MAXP",
95	[USB_ERR_SET_ADDR_FAILED]	= "SET_ADDR_FAILED",
96	[USB_ERR_NO_POWER]		= "NO_POWER",
97	[USB_ERR_TOO_DEEP]		= "TOO_DEEP",
98	[USB_ERR_IOERROR]		= "IOERROR",
99	[USB_ERR_NOT_CONFIGURED]	= "NOT_CONFIGURED",
100	[USB_ERR_TIMEOUT]		= "TIMEOUT",
101	[USB_ERR_SHORT_XFER]		= "SHORT_XFER",
102	[USB_ERR_STALLED]		= "STALLED",
103	[USB_ERR_INTERRUPTED]		= "INTERRUPTED",
104	[USB_ERR_DMA_LOAD_FAILED]	= "DMA_LOAD_FAILED",
105	[USB_ERR_BAD_CONTEXT]		= "BAD_CONTEXT",
106	[USB_ERR_NO_ROOT_HUB]		= "NO_ROOT_HUB",
107	[USB_ERR_NO_INTR_THREAD]	= "NO_INTR_THREAD",
108	[USB_ERR_NOT_LOCKED]		= "NOT_LOCKED",
109};
110
111static const char *xfertype_table[4] = {
112	[UE_CONTROL]			= "CTRL",
113	[UE_ISOCHRONOUS]		= "ISOC",
114	[UE_BULK]			= "BULK",
115	[UE_INTERRUPT]			= "INTR"
116};
117
118static const char *speed_table[USB_SPEED_MAX] = {
119	[USB_SPEED_FULL] = "FULL",
120	[USB_SPEED_HIGH] = "HIGH",
121	[USB_SPEED_LOW] = "LOW",
122	[USB_SPEED_VARIABLE] = "VARI",
123	[USB_SPEED_SUPER] = "SUPER",
124};
125
126static void
127handle_sigint(int sig)
128{
129
130	(void)sig;
131	doexit = 1;
132}
133
134#define	FLAGS(x, name)	\
135	(((x) & USBPF_FLAG_##name) ? #name "|" : "")
136
137#define	STATUS(x, name) \
138	(((x) & USBPF_STATUS_##name) ? #name "|" : "")
139
140static const char *
141usb_errstr(uint32_t error)
142{
143	if (error >= USB_ERR_MAX || errstr_table[error] == NULL)
144		return ("UNKNOWN");
145	else
146		return (errstr_table[error]);
147}
148
149static const char *
150usb_speedstr(uint8_t speed)
151{
152	if (speed >= USB_SPEED_MAX  || speed_table[speed] == NULL)
153		return ("UNKNOWN");
154	else
155		return (speed_table[speed]);
156}
157
158static void
159print_flags(uint32_t flags)
160{
161	printf(" flags %#x <%s%s%s%s%s%s%s%s%s0>\n",
162	    flags,
163	    FLAGS(flags, FORCE_SHORT_XFER),
164	    FLAGS(flags, SHORT_XFER_OK),
165	    FLAGS(flags, SHORT_FRAMES_OK),
166	    FLAGS(flags, PIPE_BOF),
167	    FLAGS(flags, PROXY_BUFFER),
168	    FLAGS(flags, EXT_BUFFER),
169	    FLAGS(flags, MANUAL_STATUS),
170	    FLAGS(flags, NO_PIPE_OK),
171	    FLAGS(flags, STALL_PIPE));
172}
173
174static void
175print_status(uint32_t status)
176{
177	printf(" status %#x <%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s0>\n",
178	    status,
179	    STATUS(status, OPEN),
180	    STATUS(status, TRANSFERRING),
181	    STATUS(status, DID_DMA_DELAY),
182	    STATUS(status, DID_CLOSE),
183	    STATUS(status, DRAINING),
184	    STATUS(status, STARTED),
185	    STATUS(status, BW_RECLAIMED),
186	    STATUS(status, CONTROL_XFR),
187	    STATUS(status, CONTROL_HDR),
188	    STATUS(status, CONTROL_ACT),
189	    STATUS(status, CONTROL_STALL),
190	    STATUS(status, SHORT_FRAMES_OK),
191	    STATUS(status, SHORT_XFER_OK),
192	    STATUS(status, BDMA_ENABLE),
193	    STATUS(status, BDMA_NO_POST_SYNC),
194	    STATUS(status, BDMA_SETUP),
195	    STATUS(status, ISOCHRONOUS_XFR),
196	    STATUS(status, CURR_DMA_SET),
197	    STATUS(status, CAN_CANCEL_IMMED),
198	    STATUS(status, DOING_CALLBACK));
199}
200
201/*
202 * Dump a byte into hex format.
203 */
204static void
205hexbyte(char *buf, uint8_t temp)
206{
207	uint8_t lo;
208	uint8_t hi;
209
210	lo = temp & 0xF;
211	hi = temp >> 4;
212
213	if (hi < 10)
214		buf[0] = '0' + hi;
215	else
216		buf[0] = 'A' + hi - 10;
217
218	if (lo < 10)
219		buf[1] = '0' + lo;
220	else
221		buf[1] = 'A' + lo - 10;
222}
223
224/*
225 * Display a region in traditional hexdump format.
226 */
227static void
228hexdump(const uint8_t *region, uint32_t len)
229{
230	const uint8_t *line;
231	char linebuf[128];
232	int i;
233	int x;
234	int c;
235
236	for (line = region; line < (region + len); line += 16) {
237
238		i = 0;
239
240		linebuf[i] = ' ';
241		hexbyte(linebuf + i + 1, ((line - region) >> 8) & 0xFF);
242		hexbyte(linebuf + i + 3, (line - region) & 0xFF);
243		linebuf[i + 5] = ' ';
244		linebuf[i + 6] = ' ';
245		i += 7;
246
247		for (x = 0; x < 16; x++) {
248		  if ((line + x) < (region + len)) {
249			hexbyte(linebuf + i,
250			    *(const u_int8_t *)(line + x));
251		  } else {
252			  linebuf[i] = '-';
253			  linebuf[i + 1] = '-';
254			}
255			linebuf[i + 2] = ' ';
256			if (x == 7) {
257			  linebuf[i + 3] = ' ';
258			  i += 4;
259			} else {
260			  i += 3;
261			}
262		}
263		linebuf[i] = ' ';
264		linebuf[i + 1] = '|';
265		i += 2;
266		for (x = 0; x < 16; x++) {
267			if ((line + x) < (region + len)) {
268				c = *(const u_int8_t *)(line + x);
269				/* !isprint(c) */
270				if ((c < ' ') || (c > '~'))
271					c = '.';
272				linebuf[i] = c;
273			} else {
274				linebuf[i] = ' ';
275			}
276			i++;
277		}
278		linebuf[i] = '|';
279		linebuf[i + 1] = 0;
280		i += 2;
281		puts(linebuf);
282	}
283}
284
285static void
286print_apacket(const struct bpf_xhdr *hdr, const uint8_t *ptr, int ptr_len)
287{
288	struct tm *tm;
289	struct usbpf_pkthdr up_temp;
290	struct usbpf_pkthdr *up;
291	struct timeval tv;
292	size_t len;
293	uint32_t x;
294	char buf[64];
295
296	ptr += USBPF_HDR_LEN;
297	ptr_len -= USBPF_HDR_LEN;
298	if (ptr_len < 0)
299		return;
300
301	/* make sure we don't change the source buffer */
302	memcpy(&up_temp, ptr - USBPF_HDR_LEN, sizeof(up_temp));
303	up = &up_temp;
304
305	/*
306	 * A packet from the kernel is based on little endian byte
307	 * order.
308	 */
309	up->up_totlen = le32toh(up->up_totlen);
310	up->up_busunit = le32toh(up->up_busunit);
311	up->up_address = le32toh(up->up_address);
312	up->up_flags = le32toh(up->up_flags);
313	up->up_status = le32toh(up->up_status);
314	up->up_error = le32toh(up->up_error);
315	up->up_interval = le32toh(up->up_interval);
316	up->up_frames = le32toh(up->up_frames);
317	up->up_packet_size = le32toh(up->up_packet_size);
318	up->up_packet_count = le32toh(up->up_packet_count);
319	up->up_endpoint = le32toh(up->up_endpoint);
320
321	tv.tv_sec = hdr->bh_tstamp.bt_sec;
322	tv.tv_usec = hdr->bh_tstamp.bt_frac;
323	tm = localtime(&tv.tv_sec);
324
325	len = strftime(buf, sizeof(buf), "%H:%M:%S", tm);
326
327	printf("%.*s.%06ld usbus%d.%d %s-%s-EP=%08x,SPD=%s,NFR=%d,SLEN=%d,IVAL=%d%s%s\n",
328	    (int)len, buf, tv.tv_usec,
329	    (int)up->up_busunit, (int)up->up_address,
330	    (up->up_type == USBPF_XFERTAP_SUBMIT) ? "SUBM" : "DONE",
331	    xfertype_table[up->up_xfertype],
332	    (unsigned int)up->up_endpoint,
333	    usb_speedstr(up->up_speed),
334	    (int)up->up_frames,
335	    (int)(up->up_totlen - USBPF_HDR_LEN -
336	    (USBPF_FRAME_HDR_LEN * up->up_frames)),
337	    (int)up->up_interval,
338	    (up->up_type == USBPF_XFERTAP_DONE) ? ",ERR=" : "",
339	    (up->up_type == USBPF_XFERTAP_DONE) ?
340	    usb_errstr(up->up_error) : "");
341
342	if (verbose >= 1) {
343		for (x = 0; x != up->up_frames; x++) {
344			const struct usbpf_framehdr *uf;
345			uint32_t framelen;
346			uint32_t flags;
347
348			uf = (const struct usbpf_framehdr *)ptr;
349			ptr += USBPF_FRAME_HDR_LEN;
350			ptr_len -= USBPF_FRAME_HDR_LEN;
351			if (ptr_len < 0)
352				return;
353
354			framelen = le32toh(uf->length);
355			flags = le32toh(uf->flags);
356
357			printf(" frame[%u] %s %d bytes\n",
358			    (unsigned int)x,
359			    (flags & USBPF_FRAMEFLAG_READ) ? "READ" : "WRITE",
360			    (int)framelen);
361
362			if (flags & USBPF_FRAMEFLAG_DATA_FOLLOWS) {
363
364				int tot_frame_len;
365
366				tot_frame_len = USBPF_FRAME_ALIGN(framelen);
367
368				ptr_len -= tot_frame_len;
369
370				if (tot_frame_len < 0 ||
371				    (int)framelen < 0 || (int)ptr_len < 0)
372					break;
373
374				hexdump(ptr, framelen);
375
376				ptr += tot_frame_len;
377			}
378		}
379	}
380	if (verbose >= 2)
381		print_flags(up->up_flags);
382	if (verbose >= 3)
383		print_status(up->up_status);
384}
385
386static void
387print_packets(uint8_t *data, const int datalen)
388{
389	const struct bpf_xhdr *hdr;
390	uint8_t *ptr;
391	uint8_t *next;
392
393	for (ptr = data; ptr < (data + datalen); ptr = next) {
394		hdr = (const struct bpf_xhdr *)ptr;
395		next = ptr + BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen);
396
397		if (w_arg == NULL) {
398			print_apacket(hdr, ptr +
399			    hdr->bh_hdrlen, hdr->bh_caplen);
400		}
401		pkt_captured++;
402	}
403}
404
405static void
406write_packets(struct usbcap *p, const uint8_t *data, const int datalen)
407{
408	int len = htole32(datalen);
409	int ret;
410
411	ret = write(p->wfd, &len, sizeof(int));
412	if (ret != sizeof(int)) {
413		err(EXIT_FAILURE, "Could not write length "
414		    "field of USB data payload");
415	}
416	ret = write(p->wfd, data, datalen);
417	if (ret != datalen) {
418		err(EXIT_FAILURE, "Could not write "
419		    "complete USB data payload");
420	}
421}
422
423static void
424read_file(struct usbcap *p)
425{
426	int datalen;
427	int ret;
428	uint8_t *data;
429
430	while ((ret = read(p->rfd, &datalen, sizeof(int))) == sizeof(int)) {
431		datalen = le32toh(datalen);
432		data = malloc(datalen);
433		if (data == NULL)
434			errx(EX_SOFTWARE, "Out of memory.");
435		ret = read(p->rfd, data, datalen);
436		if (ret != datalen) {
437			err(EXIT_FAILURE, "Could not read complete "
438			    "USB data payload");
439		}
440		print_packets(data, datalen);
441		free(data);
442	}
443}
444
445static void
446do_loop(struct usbcap *p)
447{
448	int cc;
449
450	while (doexit == 0) {
451		cc = read(p->fd, (uint8_t *)p->buffer, p->bufsize);
452		if (cc < 0) {
453			switch (errno) {
454			case EINTR:
455				break;
456			default:
457				fprintf(stderr, "read: %s\n", strerror(errno));
458				return;
459			}
460			continue;
461		}
462		if (cc == 0)
463			continue;
464		if (w_arg != NULL)
465			write_packets(p, p->buffer, cc);
466		print_packets(p->buffer, cc);
467	}
468}
469
470static void
471init_rfile(struct usbcap *p)
472{
473	struct usbcap_filehdr uf;
474	int ret;
475
476	p->rfd = open(r_arg, O_RDONLY);
477	if (p->rfd < 0) {
478		err(EXIT_FAILURE, "Could not open "
479		    "'%s' for read", r_arg);
480	}
481	ret = read(p->rfd, &uf, sizeof(uf));
482	if (ret != sizeof(uf)) {
483		err(EXIT_FAILURE, "Could not read USB capture "
484		    "file header");
485	}
486	if (le32toh(uf.magic) != USBCAP_FILEHDR_MAGIC) {
487		errx(EX_SOFTWARE, "Invalid magic field(0x%08x) "
488		    "in USB capture file header.",
489		    (unsigned int)le32toh(uf.magic));
490	}
491	if (uf.major != 0) {
492		errx(EX_SOFTWARE, "Invalid major version(%d) "
493		    "field in USB capture file header.", (int)uf.major);
494	}
495	if (uf.minor != 2) {
496		errx(EX_SOFTWARE, "Invalid minor version(%d) "
497		    "field in USB capture file header.", (int)uf.minor);
498	}
499}
500
501static void
502init_wfile(struct usbcap *p)
503{
504	struct usbcap_filehdr uf;
505	int ret;
506
507	p->wfd = open(w_arg, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR);
508	if (p->wfd < 0) {
509		err(EXIT_FAILURE, "Could not open "
510		    "'%s' for write", r_arg);
511	}
512	memset(&uf, 0, sizeof(uf));
513	uf.magic = htole32(USBCAP_FILEHDR_MAGIC);
514	uf.major = 0;
515	uf.minor = 2;
516	ret = write(p->wfd, (const void *)&uf, sizeof(uf));
517	if (ret != sizeof(uf)) {
518		err(EXIT_FAILURE, "Could not write "
519		    "USB capture header");
520	}
521}
522
523static void
524usage(void)
525{
526
527#define FMT "    %-14s %s\n"
528	fprintf(stderr, "usage: usbdump [options]\n");
529	fprintf(stderr, FMT, "-i <usbusX>", "Listen on USB bus interface");
530	fprintf(stderr, FMT, "-r <file>", "Read the raw packets from file");
531	fprintf(stderr, FMT, "-s <snaplen>", "Snapshot bytes from each packet");
532	fprintf(stderr, FMT, "-v", "Increase the verbose level");
533	fprintf(stderr, FMT, "-w <file>", "Write the raw packets to file");
534#undef FMT
535	exit(EX_USAGE);
536}
537
538int
539main(int argc, char *argv[])
540{
541	struct timeval tv;
542	struct bpf_insn total_insn;
543	struct bpf_program total_prog;
544	struct bpf_stat us;
545	struct bpf_version bv;
546	struct usbcap uc, *p = &uc;
547	struct ifreq ifr;
548	long snapshot = 192;
549	uint32_t v;
550	int fd, o;
551	const char *optstring;
552
553	memset(&uc, 0, sizeof(struct usbcap));
554
555	optstring = "i:r:s:vw:";
556	while ((o = getopt(argc, argv, optstring)) != -1) {
557		switch (o) {
558		case 'i':
559			i_arg = optarg;
560			break;
561		case 'r':
562			r_arg = optarg;
563			init_rfile(p);
564			break;
565		case 's':
566			snapshot = strtol(optarg, NULL, 10);
567			errno = 0;
568			if (snapshot == 0 && errno == EINVAL)
569				usage();
570			/* snapeshot == 0 is special */
571			if (snapshot == 0)
572				snapshot = -1;
573			break;
574		case 'v':
575			verbose++;
576			break;
577		case 'w':
578			w_arg = optarg;
579			init_wfile(p);
580			break;
581		default:
582			usage();
583			/* NOTREACHED */
584		}
585	}
586
587	if (r_arg != NULL) {
588		read_file(p);
589		exit(EXIT_SUCCESS);
590	}
591
592	p->fd = fd = open("/dev/bpf", O_RDONLY);
593	if (p->fd < 0)
594		err(EXIT_FAILURE, "Could not open BPF device");
595
596	if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0)
597		err(EXIT_FAILURE, "BIOCVERSION ioctl failed");
598
599	if (bv.bv_major != BPF_MAJOR_VERSION ||
600	    bv.bv_minor < BPF_MINOR_VERSION)
601		errx(EXIT_FAILURE, "Kernel BPF filter out of date");
602
603	/* USB transfers can be greater than 64KByte */
604	v = 1U << 16;
605
606	/* clear ifr structure */
607	memset(&ifr, 0, sizeof(ifr));
608
609	for ( ; v >= USBPF_HDR_LEN; v >>= 1) {
610		(void)ioctl(fd, BIOCSBLEN, (caddr_t)&v);
611		(void)strncpy(ifr.ifr_name, i_arg, sizeof(ifr.ifr_name));
612		if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0)
613			break;
614	}
615	if (v == 0)
616		errx(EXIT_FAILURE, "No buffer size worked.");
617
618	if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0)
619		err(EXIT_FAILURE, "BIOCGBLEN ioctl failed");
620
621	p->bufsize = v;
622	p->buffer = (uint8_t *)malloc(p->bufsize);
623	if (p->buffer == NULL)
624		errx(EX_SOFTWARE, "Out of memory.");
625
626	/* XXX no read filter rules yet so at this moment accept everything */
627	total_insn.code = (u_short)(BPF_RET | BPF_K);
628	total_insn.jt = 0;
629	total_insn.jf = 0;
630	total_insn.k = snapshot;
631
632	total_prog.bf_len = 1;
633	total_prog.bf_insns = &total_insn;
634	if (ioctl(p->fd, BIOCSETF, (caddr_t)&total_prog) < 0)
635		err(EXIT_FAILURE, "BIOCSETF ioctl failed");
636
637	/* 1 second read timeout */
638	tv.tv_sec = 1;
639	tv.tv_usec = 0;
640	if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&tv) < 0)
641		err(EXIT_FAILURE, "BIOCSRTIMEOUT ioctl failed");
642
643	(void)signal(SIGINT, handle_sigint);
644
645	do_loop(p);
646
647	if (ioctl(fd, BIOCGSTATS, (caddr_t)&us) < 0)
648		err(EXIT_FAILURE, "BIOCGSTATS ioctl failed");
649
650	/* XXX what's difference between pkt_captured and us.us_recv? */
651	printf("\n");
652	printf("%d packets captured\n", pkt_captured);
653	printf("%d packets received by filter\n", us.bs_recv);
654	printf("%d packets dropped by kernel\n", us.bs_drop);
655
656	if (p->fd > 0)
657		close(p->fd);
658	if (p->rfd > 0)
659		close(p->rfd);
660	if (p->wfd > 0)
661		close(p->wfd);
662
663	return (EXIT_SUCCESS);
664}
665