Home | History | Annotate | Line # | Download | only in perfused
msg.c revision 1.2
      1 /*  $NetBSD: msg.c,v 1.2 2010/08/27 09:58:17 manu Exp $ */
      2 
      3 /*-
      4  *  Copyright (c) 2010 Emmanuel Dreyfus. All rights reserved.
      5  *
      6  *  Redistribution and use in source and binary forms, with or without
      7  *  modification, are permitted provided that the following conditions
      8  *  are met:
      9  *  1. Redistributions of source code must retain the above copyright
     10  *     notice, this list of conditions and the following disclaimer.
     11  *  2. Redistributions in binary form must reproduce the above copyright
     12  *     notice, this list of conditions and the following disclaimer in the
     13  *     documentation and/or other materials provided with the distribution.
     14  *
     15  *  THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     16  *  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     17  *  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     18  *  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     19  *  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     20  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     21  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     22  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     23  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     24  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     25  *  POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include <stdio.h>
     29 #include <stdlib.h>
     30 #include <unistd.h>
     31 #include <err.h>
     32 #include <errno.h>
     33 #include <string.h>
     34 #include <sysexits.h>
     35 #include <syslog.h>
     36 #include <paths.h>
     37 #include <puffs.h>
     38 #include <sys/socket.h>
     39 #include <sys/un.h>
     40 #include <machine/vmparam.h>
     41 
     42 #include "../../lib/libperfuse/perfuse_if.h"
     43 #include "perfused.h"
     44 
     45 static int xchg_pb_inloop(struct puffs_usermount *a, struct puffs_framebuf *,
     46 	int, enum perfuse_xchg_pb_reply);
     47 static int xchg_pb_early(struct puffs_usermount *a, struct puffs_framebuf *,
     48 	int, enum perfuse_xchg_pb_reply);
     49 
     50 int
     51 perfuse_open_sock(void)
     52 {
     53 	int s;
     54 	struct sockaddr_un sun;
     55 	const struct sockaddr *sa;
     56 
     57 	(void)unlink(_PATH_FUSE);
     58 
     59 	if ((s = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1)
     60 		err(EX_OSERR, "socket failed");
     61 
     62 	sa = (const struct sockaddr *)(void *)&sun;
     63 	sun.sun_len = sizeof(sun);
     64 	sun.sun_family = AF_LOCAL;
     65 	(void)strcpy(sun.sun_path, _PATH_FUSE);
     66 
     67 	if (bind(s, sa, (socklen_t )sun.sun_len) == -1)
     68 		err(EX_OSERR, "cannot open \"%s\" socket", _PATH_FUSE);
     69 
     70 	if (listen(s, 1) == -1)
     71 		err(EX_OSERR, "listen failed");
     72 
     73 	return s;
     74 }
     75 
     76 
     77 void *
     78 perfuse_recv_early(fd, len)
     79 	int fd;
     80 	size_t len;
     81 {
     82 	char *buf;
     83 
     84 	if (len == 0)
     85 		return NULL;
     86 
     87 	if ((buf = malloc(len + 1)) == NULL)
     88 		err(EX_OSERR, "malloc(%d) failed", len);
     89 
     90 	if (read(fd, buf, len) != len) {
     91 		DWARN("short read");
     92 		return NULL;
     93 	}
     94 
     95 	buf[len] = '\0';
     96 	return buf;
     97 }
     98 
     99 
    100 perfuse_msg_t *
    101 perfuse_new_pb (pu, opc, opcode, payload_len, cred)
    102 	struct puffs_usermount *pu;
    103 	puffs_cookie_t opc;
    104 	int opcode;
    105 	size_t payload_len;
    106 	const struct puffs_cred *cred;
    107 {
    108 	struct puffs_framebuf *pb;
    109 	struct fuse_in_header *fih;
    110 	struct puffs_cc *pcc;
    111 	uint64_t nodeid;
    112 	void *data;
    113 	size_t len;
    114 
    115 	if ((pb = puffs_framebuf_make()) == NULL)
    116 		DERR(EX_OSERR, "puffs_framebuf_make failed");
    117 
    118 	len = payload_len + sizeof(*fih);
    119 	nodeid = (opc != 0) ? perfuse_get_ino(pu, opc) : PERFUSE_UNKNOWN_INO;
    120 
    121 	if (puffs_framebuf_reserve_space(pb, len) != 0)
    122 		DERR(EX_OSERR, "puffs_framebuf_reserve_space failed");
    123 
    124 	if (puffs_framebuf_getwindow(pb, 0, &data, &len) != 0)
    125 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
    126 	if (len != payload_len + sizeof(*fih))
    127 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short len");
    128 
    129 	(void)memset(data, 0, len);
    130 	fih = (struct fuse_in_header *)data;
    131 	fih->len = len;
    132 	fih->opcode = opcode;
    133 	fih->unique = perfuse_next_unique(pu);
    134 	fih->nodeid = nodeid;
    135 	fih->uid = (uid_t)-1;
    136 	fih->gid = (gid_t)-1;
    137 	fih->pid = 0;
    138 	if (cred != NULL) {
    139 		(void)puffs_cred_getuid(cred, &fih->uid);
    140 		(void)puffs_cred_getgid(cred, &fih->gid);
    141 	}
    142 	if ((pcc = puffs_cc_getcc(pu)) != NULL)
    143 		(void)puffs_cc_getcaller(pcc, (pid_t *)&fih->pid, NULL);
    144 
    145 	return (perfuse_msg_t *)(void *)pb;
    146 }
    147 
    148 /*
    149  * framebuf send/receive primitives based on pcc are
    150  * not available until puffs mainloop is entered.
    151  * This xchg_pb_inloop() variant allow early communication.
    152  */
    153 static int
    154 xchg_pb_early(pu, pb, fd, reply)
    155 	struct puffs_usermount *pu;
    156 	struct puffs_framebuf *pb;
    157 	int fd;
    158 	enum perfuse_xchg_pb_reply reply;
    159 {
    160 	int done;
    161 	int error;
    162 
    163 	done = 0;
    164 	while (done == 0) {
    165 		if ((error = perfuse_writeframe(pu, pb, fd, &done)) != 0)
    166 			return error;
    167 	}
    168 
    169 	if (reply == no_reply) {
    170 		puffs_framebuf_destroy(pb);
    171 		return 0;
    172 	} else {
    173 		puffs_framebuf_recycle(pb);
    174 	}
    175 
    176 	done = 0;
    177 	while (done == 0) {
    178 		if ((error = perfuse_readframe(pu, pb, fd, &done)) != 0)
    179 			return error;
    180 	}
    181 
    182 	return 0;
    183 }
    184 
    185 static int
    186 xchg_pb_inloop(pu, pb, fd, reply)
    187 	struct puffs_usermount *pu;
    188 	struct puffs_framebuf *pb;
    189 	int fd;
    190 	enum perfuse_xchg_pb_reply reply;
    191 {
    192 	struct puffs_cc *pcc;
    193 	int error;
    194 
    195 	if (reply == no_reply) {
    196 		error = puffs_framev_enqueue_justsend(pu, fd, pb, 0, 0);
    197 	} else {
    198 		pcc = puffs_cc_getcc(pu);
    199 		error = puffs_framev_enqueue_cc(pcc, fd, pb, 0);
    200 	}
    201 
    202 	return error;
    203 }
    204 
    205 int
    206 perfuse_xchg_pb(pu, pm, expected_len, reply)
    207 	struct puffs_usermount *pu;
    208 	perfuse_msg_t *pm;
    209 	size_t expected_len;
    210 	enum perfuse_xchg_pb_reply reply;
    211 {
    212 	struct puffs_framebuf *pb = (struct puffs_framebuf *)(void *)pm;
    213 	int fd;
    214 	int error;
    215 	struct fuse_out_header *foh;
    216 #ifdef PERFUSE_DEBUG
    217 	struct fuse_in_header *fih;
    218 	uint64_t nodeid;
    219 	int opcode;
    220 	uint64_t unique_in;
    221 	uint64_t unique_out;
    222 
    223 	fih = perfuse_get_inhdr(pm);
    224 	unique_in = fih->unique;
    225 	nodeid = fih->nodeid;
    226 	opcode = fih->opcode;
    227 
    228 	if (perfuse_diagflags & PDF_FUSE)
    229 		DPRINTF("> unique = %lld, nodeid = %lld, opcode = %s (%d)\n",
    230 			unique_in, nodeid, perfuse_opname(opcode), opcode);
    231 
    232 	if (perfuse_diagflags & PDF_DUMP)
    233 		perfuse_hexdump((char *)fih, fih->len);
    234 
    235 #endif /* PERFUSE_DEBUG */
    236 
    237 	fd = (int)perfuse_getspecific(pu);
    238 
    239 	if (perfuse_inloop(pu))
    240 		error = xchg_pb_inloop(pu, pb, fd, reply);
    241 	else
    242 		error = xchg_pb_early(pu, pb, fd, reply);
    243 
    244 	if (error)
    245 		DERR(EX_SOFTWARE, "xchg_pb failed");
    246 
    247 	if (reply == no_reply)
    248 		return 0;
    249 
    250 	foh = perfuse_get_outhdr((perfuse_msg_t *)(void *)pb);
    251 #ifdef PERFUSE_DEBUG
    252 	unique_out = foh->unique;
    253 
    254 	if (perfuse_diagflags & PDF_FUSE)
    255 		DPRINTF("< unique = %lld, nodeid = %lld, opcode = %s (%d), "
    256 			"error = %d\n", unique_out, nodeid,
    257 			perfuse_opname(opcode), opcode, error);
    258 
    259 	if (perfuse_diagflags & PDF_DUMP)
    260 		perfuse_hexdump((char *)foh, foh->len);
    261 
    262 	if (unique_in != unique_out) {
    263 		printf("%s: packet mismatch unique %lld vs %lld\n",
    264 		     __func__, unique_in, unique_out);
    265 		abort();
    266 		errx(EX_SOFTWARE, "%s: packet mismatch unique %lld vs %lld\n",
    267 		     __func__, unique_in, unique_out);
    268 	}
    269 #endif /* PERFUSE_DEBUG */
    270 
    271 	if ((expected_len != PERFUSE_UNSPEC_REPLY_LEN) &&
    272 	    (foh->len - sizeof(*foh) < expected_len) &&
    273 	    (foh->error == 0)) {
    274 		DERRX(EX_PROTOCOL,
    275 		     "Unexpected short reply: received %d bytes, expected %d",
    276 		     foh->len - sizeof(*foh), expected_len);
    277 	}
    278 
    279 	if ((expected_len != 0) &&
    280 	    (foh->len - sizeof(*foh) > expected_len))
    281 		DWARNX("Unexpected long reply");
    282 
    283 	/*
    284 	 * Negative Linux errno...
    285 	 */
    286 	foh->error = -foh->error;
    287 
    288 	return foh->error;
    289 }
    290 
    291 
    292 struct fuse_in_header *
    293 perfuse_get_inhdr(pm)
    294 	perfuse_msg_t *pm;
    295 {
    296 	struct puffs_framebuf *pb;
    297 	struct fuse_in_header *fih;
    298 	void *hdr;
    299 	size_t len;
    300 
    301 	pb = (struct puffs_framebuf *)(void *)pm;
    302 	len = sizeof(*fih);
    303 	if (puffs_framebuf_getwindow(pb, 0, &hdr, &len) != 0)
    304 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
    305 	if (len != sizeof(*fih))
    306 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header");
    307 
    308 	fih = (struct fuse_in_header *)hdr;
    309 
    310 	return fih;
    311 }
    312 
    313 struct fuse_out_header *
    314 perfuse_get_outhdr(pm)
    315 	perfuse_msg_t *pm;
    316 {
    317 	struct puffs_framebuf *pb;
    318 	struct fuse_out_header *foh;
    319 	void *hdr;
    320 	size_t len;
    321 
    322 	pb = (struct puffs_framebuf *)(void *)pm;
    323 	len = sizeof(*foh);
    324 	if (puffs_framebuf_getwindow(pb, 0, &hdr, &len) != 0)
    325 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
    326 	if (len != sizeof(*foh))
    327 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header");
    328 
    329 	foh = (struct fuse_out_header *)hdr;
    330 
    331 	return foh;
    332 }
    333 
    334 char *
    335 perfuse_get_inpayload(pm)
    336 	perfuse_msg_t *pm;
    337 {
    338 	struct puffs_framebuf *pb;
    339 	struct fuse_in_header *fih;
    340 	void *hdr;
    341 	void *payload;
    342 	size_t len;
    343 
    344 	pb = (struct puffs_framebuf *)(void *)pm;
    345 	len = sizeof(*fih);
    346 	if (puffs_framebuf_getwindow(pb, 0, &hdr, &len) != 0)
    347 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
    348 	if (len != sizeof(*fih))
    349 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header");
    350 
    351 	fih = (struct fuse_in_header *)hdr;
    352 
    353 	len = fih->len - sizeof(*fih);
    354 	if (puffs_framebuf_getwindow(pb, sizeof(*fih), &payload, &len) != 0)
    355 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
    356 	if (len != fih->len - sizeof(*fih))
    357 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header");
    358 
    359 	return (char *)payload;
    360 }
    361 
    362 char *
    363 perfuse_get_outpayload(pm)
    364 	perfuse_msg_t *pm;
    365 {
    366 	struct puffs_framebuf *pb;
    367 	struct fuse_out_header *foh;
    368 	void *hdr;
    369 	void *payload;
    370 	size_t len;
    371 
    372 	pb = (struct puffs_framebuf *)(void *)pm;
    373 	len = sizeof(*foh);
    374 	if (puffs_framebuf_getwindow(pb, 0, &hdr, &len) != 0)
    375 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
    376 	if (len != sizeof(*foh))
    377 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header");
    378 
    379 	foh = (struct fuse_out_header *)hdr;
    380 
    381 	len = foh->len - sizeof(*foh);
    382 	if (puffs_framebuf_getwindow(pb, sizeof(*foh), &payload, &len) != 0)
    383 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
    384 	if (len != foh->len - sizeof(*foh))
    385 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header");
    386 
    387 	return (char *)payload;
    388 }
    389 
    390 #define PUFFS_FRAMEBUF_GETWINDOW(pb, offset, data, len) 		     \
    391 	do {								     \
    392 		int pfg_error;						     \
    393 		size_t pfg_len = *(len);				     \
    394 									     \
    395 		pfg_error = puffs_framebuf_getwindow(pb, offset, data, len); \
    396 		if (pfg_error != 0)					     \
    397 			DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");\
    398 									     \
    399 		if (*(len) != pfg_len)					     \
    400 			DERRX(EX_SOFTWARE, "puffs_framebuf_getwindow size"); \
    401 	} while (0 /* CONSTCOND */);
    402 
    403 /* ARGSUSED0 */
    404 int
    405 perfuse_readframe(pu, pufbuf, fd, done)
    406 	struct puffs_usermount *pu;
    407 	struct puffs_framebuf *pufbuf;
    408 	int fd;
    409 	int *done;
    410 {
    411 	struct fuse_out_header foh;
    412 	size_t offset;
    413 	size_t remain;
    414 	ssize_t readen;
    415 	void *data;
    416 
    417 	offset = puffs_framebuf_telloff(pufbuf);
    418 
    419 	/*
    420 	 * Read the header
    421 	 */
    422 	while (offset < sizeof(foh)) {
    423 		remain = sizeof(foh) - offset;
    424 		PUFFS_FRAMEBUF_GETWINDOW(pufbuf, offset, &data, &remain);
    425 
    426 		switch (readen = recv(fd, data, remain, MSG_NOSIGNAL)) {
    427 		case 0:
    428 			DWARNX("%s: recv retunred 0", __func__);
    429 			return ECONNRESET;
    430 			/* NOTREACHED */
    431 			break;
    432 		case -1:
    433 			if (errno == EAGAIN)
    434 				return 0;
    435 			DWARN("%s: recv retunred -1", __func__);
    436 			return errno;
    437 			/* NOTREACHED */
    438 			break;
    439 		default:
    440 			break;
    441 		}
    442 
    443 		offset += readen;
    444 		if (puffs_framebuf_seekset(pufbuf, offset) == -1)
    445 			DERR(EX_OSERR, "puffs_framebuf_seekset failed");
    446 	}
    447 
    448 
    449 	/*
    450 	 * We have a header, get remaing length to read
    451 	 */
    452 	if (puffs_framebuf_getdata_atoff(pufbuf, 0, &foh, sizeof(foh)) != 0)
    453 		DERR(EX_SOFTWARE, "puffs_framebuf_getdata_atoff failed");
    454 ;
    455 #ifdef PERFUSE_DEBUG
    456 		if (foh.len > FUSE_BUFSIZE)
    457 			DERRX(EX_SOFTWARE, "%s: foh.len = %d (this is huge!)",
    458 			      __func__, foh.len);
    459 #endif
    460 
    461 	/*
    462 	 * If we have only readen the header so far,
    463 	 * this is time to reserve space.
    464 	 */
    465 	remain = foh.len - offset;
    466 	if (offset == sizeof(foh))
    467 		if (puffs_framebuf_reserve_space(pufbuf, remain) == -1)
    468 			DERR(EX_OSERR, "puffs_framebuf_reserve_space failed");
    469 
    470 
    471 	/*
    472 	 * And read the remaining data
    473 	 */
    474 	while (remain != 0) {
    475 		PUFFS_FRAMEBUF_GETWINDOW(pufbuf, offset, &data, &remain);
    476 
    477 		switch (readen = recv(fd, data, remain, MSG_NOSIGNAL)) {
    478 		case 0:
    479 			DWARNX("%s: recv retunred 0", __func__);
    480 			return ECONNRESET;
    481 			/* NOTREACHED */
    482 			break;
    483 		case -1:
    484 			if (errno == EAGAIN)
    485 				return 0;
    486 			DWARN("%s: recv retunred -1", __func__);
    487 			return errno;
    488 			/* NOTREACHED */
    489 			break;
    490 		default:
    491 			break;
    492 		}
    493 
    494 		offset += readen;
    495 		remain -= readen;
    496 
    497 		if (puffs_framebuf_seekset(pufbuf, offset) == -1)
    498 			DERR(EX_OSERR, "puffs_framebuf_seekset failed");
    499 	}
    500 
    501 	*done = 1;
    502 	return 0;
    503 }
    504 
    505 /* ARGSUSED0 */
    506 int
    507 perfuse_writeframe(pu, pufbuf, fd, done)
    508 	struct puffs_usermount *pu;
    509 	struct puffs_framebuf *pufbuf;
    510 	int fd;
    511 	int *done;
    512 {
    513 	size_t offset;
    514 	size_t len;
    515 	ssize_t written;
    516 	size_t remain;
    517 	void *data;
    518 
    519 	offset = puffs_framebuf_telloff(pufbuf);
    520 	len = puffs_framebuf_tellsize(pufbuf) - offset;
    521 	remain = len;
    522 
    523 	while (remain != 0) {
    524 		PUFFS_FRAMEBUF_GETWINDOW(pufbuf, offset, &data, &len);
    525 
    526 		switch (written = send(fd, data, remain, MSG_NOSIGNAL)) {
    527 		case 0:
    528 			DWARNX("%s: send retunred 0", __func__);
    529 			return ECONNRESET;
    530 			/* NOTREACHED */
    531 			break;
    532 		case -1:
    533 			if (errno == EAGAIN)
    534 				return 0;
    535 			DWARN("%s: send retunred -1", __func__);
    536 			return errno;
    537 			/* NOTREACHED */
    538 			break;
    539 		default:
    540 			break;
    541 		}
    542 
    543 		remain -= written;
    544 		offset += written;
    545 
    546 		if (puffs_framebuf_seekset(pufbuf, offset) == -1)
    547 			DERR(EX_OSERR, "puffs_framebuf_seekset failed");
    548 	}
    549 
    550 	*done = 1;
    551 	return 0;
    552 }
    553 
    554 /* ARGSUSED0 */
    555 int
    556 perfuse_cmpframe(pu, pb1, pb2, match)
    557 	struct puffs_usermount *pu;
    558 	struct puffs_framebuf *pb1;
    559 	struct puffs_framebuf *pb2;
    560 	int *match;
    561 {
    562 	struct fuse_in_header *fih;
    563 	struct fuse_out_header *foh;
    564 	uint64_t unique_in;
    565 	uint64_t unique_out;
    566 	size_t len;
    567 
    568 	len = sizeof(*fih);
    569 	PUFFS_FRAMEBUF_GETWINDOW(pb1, 0, (void **)&fih, &len);
    570 	unique_in = fih->unique;
    571 
    572 	len = sizeof(*foh);
    573 	PUFFS_FRAMEBUF_GETWINDOW(pb2, 0, (void **)&foh, &len);
    574 	unique_out = foh->unique;
    575 
    576 	return unique_in != unique_out;
    577 }
    578 
    579 /* ARGSUSED0 */
    580 void
    581 perfuse_gotframe(pu, pb)
    582 	struct puffs_usermount *pu;
    583 	struct puffs_framebuf *pb;
    584 {
    585 	struct fuse_out_header *foh;
    586 	size_t len;
    587 
    588 	len = sizeof(*foh);
    589 	PUFFS_FRAMEBUF_GETWINDOW(pb, 0, (void **)&foh, &len);
    590 
    591 	DWARNX("Unexpected frame: unique = %lld, error = %d",
    592 	       foh->unique, foh->error);
    593 #ifdef PERFUSE_DEBUG
    594 	perfuse_hexdump((char *)(void *)foh, foh->len);
    595 #endif
    596 
    597 	return;
    598 }
    599 
    600 void
    601 perfuse_fdnotify(pu, fd, what)
    602 	struct puffs_usermount *pu;
    603 	int fd;
    604 	int what;
    605 {
    606 	if (fd != (int)perfuse_getspecific(pu))
    607 		DERRX(EX_SOFTWARE, "%s: unexpected notification for fd = %d",
    608 		      __func__, fd);
    609 
    610 	if ((what != PUFFS_FBIO_READ) && (what != PUFFS_FBIO_WRITE))
    611 		DERRX(EX_SOFTWARE, "%s: unexpected notification what = 0x%x",
    612 		      __func__, what);
    613 
    614 	if (perfuse_unmount(pu) != 0)
    615 		DWARN("unmount() failed");
    616 
    617 	if (shutdown(fd, SHUT_RDWR) != 0)
    618 		DWARN("shutdown() failed");
    619 
    620 	if (perfuse_diagflags & PDF_MISC)
    621 		DPRINTF("Exit");
    622 
    623 	exit(0);
    624 
    625 	/* NOTREACHED */
    626 	return;
    627 }
    628