File:  [ELWIX - Embedded LightWeight unIX -] / tftpd / src / srv.c
Revision 1.2.2.6: download - view: text, annotated - select for diffs - revision graph
Thu Feb 20 15:39:15 2014 UTC (10 years, 4 months ago) by misho
Branches: tftp0_3
Diff to: branchpoint 1.2: preferred, unified
bug fixes

    1: #include "global.h"
    2: #include "exec.h"
    3: #include "srv.h"
    4: 
    5: 
    6: static void *
    7: timeoutSession(sched_task_t *task)
    8: {
    9: 	ETRACE();
   10: 
   11: 	/* drop session */
   12: 	if (cli.fd > 2)
   13: 		close(cli.fd);
   14: 	memset(&cli, 0, sizeof cli);
   15: 
   16: 	taskExit(task, NULL);
   17: }
   18: 
   19: static void *
   20: txPkt(sched_task_t *task)
   21: {
   22: 	int wlen;
   23: 	rpack_t *pkt = TASK_DATA(task);
   24: 	struct tftp_hdr *tftp = (struct tftp_hdr*) RPACK_BUF(pkt);
   25: 
   26: 	ETRACE();
   27: 
   28: 	wlen = sendto(TASK_FD(task), RPACK_BUF(pkt), TASK_DATLEN(task), 
   29: 			0, &cli.addr.sa, cli.addr.sa.sa_len);
   30: 	if (wlen == -1)
   31: 		ESYSERR(0);
   32: 	else if (wlen != TASK_DATLEN(task)) {
   33: 		EERROR(EIO, "Sended %d bytes != packet %d bytes", 
   34: 				wlen, TASK_DATLEN(task));
   35: 		schedEvent(TASK_ROOT(task), execProg, "error", 0, NULL, TFTP_OPC_ERROR);
   36: 		schedCancelby(TASK_ROOT(task), taskTIMER, CRITERIA_CALL, 
   37: 				timeoutSession, NULL);
   38: 		schedEvent(TASK_ROOT(task), timeoutSession, NULL, 0, NULL, 0);
   39: 	} else
   40: 		EVERBOSE(2, "Sended %d bytes", wlen);
   41: 	/* on error or argument, drop session */
   42: 	if (TASK_ARG(task) == (void*) -1 || ntohs(tftp->tftp_opc) == TFTP_OPC_ERROR) {
   43: 		schedEvent(TASK_ROOT(task), execProg, "error", 0, NULL, TFTP_OPC_ERROR);
   44: 		schedCancelby(TASK_ROOT(task), taskTIMER, CRITERIA_CALL, 
   45: 				timeoutSession, NULL);
   46: 		schedEvent(TASK_ROOT(task), timeoutSession, NULL, 0, NULL, 0);
   47: 	}
   48: 
   49: 	taskExit(task, NULL);
   50: }
   51: 
   52: static void *
   53: txData(sched_task_t *task)
   54: {
   55: 	rpack_t *pkt = TASK_DATA(task);
   56: 	u_short code = 0, n = htons(TFTP_OPC_DATA);
   57: 	int len;
   58: 
   59: 	ETRACE();
   60: 
   61: 	RPACK_REWIND(pkt);
   62: 	rpack_uint16(pkt, &n, 0);
   63: 	n = htons(cli.seq);
   64: 	rpack_uint16(pkt, &n, 0);
   65: 
   66: 	/* max file size check */
   67: 	if (cli.tsiz && cli.tsiz < cli.seq * cli.siz) {
   68: 		len = cli.tsiz - (cli.seq - 1) * cli.siz;
   69: 		cli.close = 42;	/* last sended packet, should be close! */
   70: 	} else
   71: 		len = cli.siz;
   72: 
   73: 	len = pread(cli.fd, RPACK_NEXT(pkt), len, (cli.seq - 1) * cli.siz);
   74: 	if (len == -1) {
   75: 		ESYSERR(0);
   76: 		code = htole16(3);
   77: 		goto end;
   78: 	} else {
   79: 		rpack_rnext(pkt, len);
   80: 		EVERBOSE(3, "Read from file %s %d bytes", cli.file, len);
   81: 	}
   82: 
   83: 	if (cli.siz - len)
   84: 		cli.close = 42;	/* last sended packet, should be close! */
   85: 
   86: 	schedEvent(TASK_ROOT(task), txPkt, NULL, TASK_FD(task), 
   87: 			TASK_DATA(task), RPACK_OFF(pkt));
   88: 	taskExit(task, NULL);
   89: end:
   90: 	RPACK_REWIND(pkt);
   91: 	n = htons(TFTP_OPC_ERROR);
   92: 	rpack_uint16(pkt, &n, 0);
   93: 	rpack_uint16(pkt, &code, 0);
   94: 	rpack_rdata(pkt, errs[le16toh(code)].err_msg, strlen(errs[le16toh(code)].err_msg) + 1);
   95: 
   96: 	schedEvent(TASK_ROOT(task), txPkt, (void*) -1, TASK_FD(task), 
   97: 			TASK_DATA(task), RPACK_OFF(pkt));
   98: 	taskExit(task, NULL);
   99: }
  100: 
  101: static void *
  102: txAck(sched_task_t *task)
  103: {
  104: 	rpack_t *pkt = TASK_DATA(task);
  105: 	u_short n = htons(TFTP_OPC_ACK);
  106: 
  107: 	ETRACE();
  108: 
  109: 	RPACK_REWIND(pkt);
  110: 	rpack_uint16(pkt, &n, 0);
  111: 	n = htons(cli.seq);
  112: 	rpack_uint16(pkt, &n, 0);
  113: 
  114: 	schedEvent(TASK_ROOT(task), txPkt, NULL, TASK_FD(task), 
  115: 			TASK_DATA(task), RPACK_OFF(pkt));
  116: 
  117: 	if (cli.close) {
  118: 		schedEvent(TASK_ROOT(task), execProg, "complete", 0, NULL, TFTP_OPC_WRQ);
  119: 		schedCancelby(TASK_ROOT(task), taskTIMER, CRITERIA_CALL, timeoutSession, NULL);
  120: 		schedEvent(TASK_ROOT(task), timeoutSession, NULL, 0, NULL, 0);
  121: 		EVERBOSE(2, "Finish WRQ request");
  122: 	}
  123: 	taskExit(task, NULL);
  124: }
  125: 
  126: static int
  127: getOpts(rpack_t * __restrict pkt, int rlen)
  128: {
  129: 	char *opt, *val;
  130: 	int len;
  131: 
  132: 	do {
  133: 		/* option */
  134: 		len = str_getString(RPACK_NEXT(pkt), RPACK_REMAIN(pkt), NULL);
  135: 		if (len == -1)
  136: 			return -1;
  137: 		else
  138: 			rlen -= len;
  139: 		opt = (char*) rpack_rnext(pkt, len);
  140: 		if (!opt)
  141: 			return -1;
  142: 		/* value */
  143: 		len = str_getString(RPACK_NEXT(pkt), RPACK_REMAIN(pkt), NULL);
  144: 		if (len == -1)
  145: 			return -1;
  146: 		else
  147: 			rlen -= len;
  148: 		val = (char*) rpack_rnext(pkt, len);
  149: 		if (!val)
  150: 			return -1;
  151: 
  152: 		if (!strcasecmp(opt, TFTP_OPT_BLKSIZE)) {
  153: 			len = strtol(val, NULL, 10);
  154: 			if (len > TFTP_LOAD_MAX) {
  155: 				cli.siz = len;
  156: 				if (rpack_resize(pkt, cli.siz + 4))
  157: 					cli.siz = TFTP_PKT_MAX;
  158: 			}
  159: 		} else if (!strcasecmp(opt, TFTP_OPT_TSIZE))
  160: 			cli.tsiz = strtoll(val, NULL, 10);
  161: 		else if (!strcasecmp(opt, TFTP_OPT_TIMEOUT))
  162: 			cli.tout = strtol(val, NULL, 10);
  163: 		else if (!strcasecmp(opt, TFTP_OPT_ROLLOVER))
  164: 			cli.roll = strtol(val, NULL, 10);
  165: 		else
  166: 			return -1;
  167: 	} while (rlen > 0);
  168: 
  169: 	EVERBOSE(4, "blksize=%u tsize=%llu timeout=%u rollover=%u", 
  170: 			cli.siz, cli.tsiz, cli.tout, cli.roll);
  171: 	return 0;
  172: }
  173: 
  174: static void *
  175: txOack(sched_task_t *task)
  176: {
  177: 	rpack_t *pkt = TASK_DATA(task);
  178: 	u_short n = htons(TFTP_OPC_OACK);
  179: 	struct stat sb;
  180: 	char szStr[STRSIZ];
  181: 
  182: 	ETRACE();
  183: 
  184: 	RPACK_REWIND(pkt);
  185: 	rpack_uint16(pkt, &n, 0);
  186: 
  187: 	/* if opcode is RRQ and tsize is 0 then we must return file size to client */
  188: 	if (cli.opc == TFTP_OPC_RRQ && !cli.tsiz && stat(cli.file, &sb) != -1)
  189: 		cli.tsiz = sb.st_size;
  190: 
  191: 	if (cli.siz > TFTP_LOAD_MAX) {
  192: 		memset(szStr, 0, sizeof szStr);
  193: 		snprintf(szStr, sizeof szStr, "%u", cli.siz);
  194: 		rpack_rdata(pkt, TFTP_OPT_BLKSIZE, strlen(TFTP_OPT_BLKSIZE) + 1);
  195: 		rpack_rdata(pkt, szStr, strlen(szStr) + 1);
  196: 	}
  197: 	if (cli.tsiz) {
  198: 		memset(szStr, 0, sizeof szStr);
  199: 		snprintf(szStr, sizeof szStr, "%llu", cli.tsiz);
  200: 		rpack_rdata(pkt, TFTP_OPT_TSIZE, strlen(TFTP_OPT_TSIZE) + 1);
  201: 		rpack_rdata(pkt, szStr, strlen(szStr) + 1);
  202: 	}
  203: 	if (cli.tout) {
  204: 		memset(szStr, 0, sizeof szStr);
  205: 		snprintf(szStr, sizeof szStr, "%u", cli.tout);
  206: 		rpack_rdata(pkt, TFTP_OPT_TIMEOUT, strlen(TFTP_OPT_TIMEOUT) + 1);
  207: 		rpack_rdata(pkt, szStr, strlen(szStr) + 1);
  208: 	}
  209: 	if (cli.roll) {
  210: 		memset(szStr, 0, sizeof szStr);
  211: 		snprintf(szStr, sizeof szStr, "%u", cli.roll);
  212: 		rpack_rdata(pkt, TFTP_OPT_ROLLOVER, strlen(TFTP_OPT_ROLLOVER) + 1);
  213: 		rpack_rdata(pkt, szStr, strlen(szStr) + 1);
  214: 	}
  215: 
  216: 	EVERBOSE(4, "blksize=%u tsize=%llu timeout=%u rollover=%u", 
  217: 			cli.siz, cli.tsiz, cli.tout, cli.roll);
  218: 	schedEvent(TASK_ROOT(task), txPkt, NULL, TASK_FD(task), 
  219: 			TASK_DATA(task), RPACK_OFF(pkt));
  220: 	taskExit(task, NULL);
  221: }
  222: 
  223: static void *
  224: RQ(sched_task_t *task)
  225: {
  226: 	rpack_t *pkt = TASK_DATA(task);
  227: 	struct tftp_hdr *tftp = (struct tftp_hdr*) RPACK_BUF(pkt);
  228: 	int len, rlen = TASK_DATLEN(task) - 2;
  229: 	char *str;
  230: 	u_short code = 0;
  231: 
  232: 	ETRACE();
  233: 
  234: 	cli.siz = TFTP_LOAD_MAX;
  235: 	cli.opc = ntohs(rpack_uint16(pkt, NULL, 0));
  236: 	len = str_getString(tftp->tftp_data, rlen, &str);
  237: 	if (len == -1)
  238: 		goto end;
  239: 	else {
  240: 		rlen -= len;
  241: 		rpack_rnext(pkt, len);
  242: 		strlcpy(cli.file, (char*) tftp->tftp_data, sizeof cli.file);
  243: 	}
  244: 	len = str_getString((const u_char*) str, rlen, NULL);
  245: 	if (len == -1)
  246: 		goto end;
  247: 	else {
  248: 		rlen -= len;
  249: 		rpack_rnext(pkt, len);
  250: 		if (!strcasecmp(str, TFTP_MODE_ASCII))
  251: 			strlcpy(cli.mode, TFTP_MODE_ASCII, sizeof cli.mode);
  252: 		else if (!strcasecmp(str, TFTP_MODE_OCTET))
  253: 			strlcpy(cli.mode, TFTP_MODE_OCTET, sizeof cli.mode);
  254: 		else if (!strcasecmp(str, TFTP_MODE_MAIL)) {
  255: 			strlcpy(cli.mode, TFTP_MODE_MAIL, sizeof cli.mode);
  256: 			code = htole16(4);
  257: 			goto end;
  258: 		} else {
  259: 			code = htole16(1);
  260: 			goto end;
  261: 		}
  262: 	}
  263: 
  264: 	switch (cli.opc) {
  265: 		case TFTP_OPC_RRQ:
  266: 			code = O_RDONLY;
  267: 			EVERBOSE(2, "RRQ:: file=%s mode=%s\n", cli.file, cli.mode);
  268: 			break;
  269: 		case TFTP_OPC_WRQ:
  270: 			code = O_WRONLY | O_CREAT;
  271: 			str = (char*) cfg_getAttribute(&cfg, "tftpd", "override");
  272: 			if (!str || tolower(*str) != 'y')
  273: 				code |= O_EXCL;
  274: 			EVERBOSE(2, "WRQ:: file=%s mode=%s\n", cli.file, cli.mode);
  275: 			break;
  276: 	}
  277: 
  278: 	cli.fd = open(cli.file, code, 0644);
  279: 	if (cli.fd == -1) {
  280: 		if (errno == EACCES)
  281: 			code = htole16(2);
  282: 		else if (errno == ENFILE)
  283: 			code = htole16(3);
  284: 		else if (errno == EEXIST)
  285: 			code = htole16(6);
  286: 		else
  287: 			code = htole16(0);
  288: 		ESYSERR(0);
  289: 		goto end;
  290: 	} else
  291: 		cli.seq = 0;
  292: 
  293: 	schedEvent(TASK_ROOT(task), execProg, "request", 0, NULL, cli.opc);
  294: 
  295: 	if (!RPACK_ISEND(pkt) && !getOpts(pkt, rlen))
  296: 		schedEvent(TASK_ROOT(task), txOack, NULL, TASK_FD(task), 
  297: 				TASK_DATA(task), 0);
  298: 	else if (cli.opc == TFTP_OPC_WRQ) {
  299: 		/* ack */
  300: 		tftp->tftp_opc = htons(TFTP_OPC_ACK);
  301: 		RPACK_REWIND(pkt);
  302: 		rpack_uint16(pkt, NULL, 0);
  303: 		rpack_uint16(pkt, &cli.seq, 0);
  304: 
  305: 		schedEvent(TASK_ROOT(task), txPkt, NULL, TASK_FD(task), 
  306: 				TASK_DATA(task), RPACK_OFF(pkt));
  307: 	} else
  308: 		schedEvent(TASK_ROOT(task), txData, NULL, TASK_FD(task), 
  309: 				TASK_DATA(task), 0);
  310: 
  311: 	cli.seq = 1;	/* 1st ack */
  312: 	taskExit(task, NULL);
  313: end:
  314: 	tftp->tftp_opc = htons(TFTP_OPC_ERROR);
  315: 	RPACK_REWIND(pkt);
  316: 	rpack_uint16(pkt, NULL, 0);
  317: 	rpack_uint16(pkt, &code, 0);
  318: 	rpack_rdata(pkt, errs[le16toh(code)].err_msg, strlen(errs[le16toh(code)].err_msg) + 1);
  319: 
  320: 	schedEvent(TASK_ROOT(task), txPkt, (void*) -1, TASK_FD(task), 
  321: 			TASK_DATA(task), RPACK_OFF(pkt));
  322: 	taskExit(task, NULL);
  323: }
  324: 
  325: static void *
  326: ACK(sched_task_t *task)
  327: {
  328: 	rpack_t *pkt = TASK_DATA(task);
  329: 	struct tftp_hdr *tftp = (struct tftp_hdr*) RPACK_BUF(pkt);
  330: 	u_short code;
  331: 
  332: 	ETRACE();
  333: 
  334: 	RPACK_REWIND(pkt);
  335: 	code = rpack_uint16(pkt, NULL, 0);
  336: 	if (ntohs(code) != TFTP_OPC_ACK) {
  337: 		code = htole16(5);
  338: 		goto end;
  339: 	}
  340: 
  341: 	code = rpack_uint16(pkt, NULL, 0);
  342: 	if (ntohs(code) > cli.seq || (ntohs(code) < (cli.seq - 1))) {
  343: 		code = htole16(5);
  344: 		goto end;
  345: 	} else if (ntohs(code) == cli.seq)
  346: 		cli.seq++;
  347: 
  348: 	EVERBOSE(3, "ACK:: seq=%hu; my new seq=%hu;", ntohs(code), cli.seq);
  349: 
  350: 	if (!cli.close)
  351: 		schedEvent(TASK_ROOT(task), txData, NULL, TASK_FD(task), 
  352: 				TASK_DATA(task), 0);
  353: 	else {
  354: 		schedEvent(TASK_ROOT(task), execProg, "complete", 0, NULL, TFTP_OPC_RRQ);
  355: 		schedCancelby(TASK_ROOT(task), taskTIMER, CRITERIA_CALL, timeoutSession, NULL);
  356: 		schedEvent(TASK_ROOT(task), timeoutSession, NULL, 0, NULL, 0);
  357: 		EVERBOSE(2, "Finish RRQ request");
  358: 	}
  359: 	taskExit(task, NULL);
  360: end:
  361: 	tftp->tftp_opc = htons(TFTP_OPC_ERROR);
  362: 	RPACK_REWIND(pkt);
  363: 	rpack_uint16(pkt, NULL, 0);
  364: 	rpack_uint16(pkt, &code, 0);
  365: 	rpack_rdata(pkt, errs[le16toh(code)].err_msg, strlen(errs[le16toh(code)].err_msg) + 1);
  366: 
  367: 	schedEvent(TASK_ROOT(task), txPkt, (void*) -1, TASK_FD(task), 
  368: 			TASK_DATA(task), RPACK_OFF(pkt));
  369: 	taskExit(task, NULL);
  370: }
  371: 
  372: static void *
  373: DATA(sched_task_t *task)
  374: {
  375: 	rpack_t *pkt = TASK_DATA(task);
  376: 	struct tftp_hdr *tftp = (struct tftp_hdr*) RPACK_BUF(pkt);
  377: 	u_short code;
  378: 	int len;
  379: 
  380: 	ETRACE();
  381: 
  382: 	RPACK_REWIND(pkt);
  383: 	code = rpack_uint16(pkt, NULL, 0);
  384: 	if (ntohs(code) != TFTP_OPC_DATA) {
  385: 		code = htole16(5);
  386: 		goto end;
  387: 	}
  388: 
  389: 	code = rpack_uint16(pkt, NULL, 0);
  390: 	if (ntohs(code) < cli.seq || ntohs(code) > cli.seq + 1) {
  391: 		code = htole16(5);
  392: 		goto end;
  393: 	} else
  394: 		cli.seq = ntohs(code);
  395: 
  396: 	/* max file size check */
  397: 	len = TASK_DATLEN(task) - RPACK_OFF(pkt);
  398: 	if (cli.tsiz && cli.tsiz < cli.seq * cli.siz)
  399: 		len = MIN(len, cli.tsiz - (cli.seq - 1) * cli.siz);
  400: 	if (len < cli.siz)
  401: 		cli.close = 42;	/* last received packet, should be close! */
  402: 
  403: 	EVERBOSE(3, "DATA:: seq=%hu; len=%d", cli.seq, len);
  404: 
  405: 	len = pwrite(cli.fd, RPACK_NEXT(pkt), len, (cli.seq - 1) * cli.siz);
  406: 	if (len == -1) {
  407: 		ESYSERR(0);
  408: 		code = htole16(3);
  409: 		goto end;
  410: 	} else {
  411: 		rpack_rnext(pkt, len);
  412: 		EVERBOSE(3, "Written to file %s %d bytes", cli.file, len);
  413: 	}
  414: 
  415: 	schedEvent(TASK_ROOT(task), txAck, NULL, TASK_FD(task), TASK_DATA(task), 0);
  416: 	taskExit(task, NULL);
  417: end:
  418: 	tftp->tftp_opc = htons(TFTP_OPC_ERROR);
  419: 	RPACK_REWIND(pkt);
  420: 	rpack_uint16(pkt, NULL, 0);
  421: 	rpack_uint16(pkt, &code, 0);
  422: 	rpack_rdata(pkt, errs[le16toh(code)].err_msg, strlen(errs[le16toh(code)].err_msg) + 1);
  423: 
  424: 	schedEvent(TASK_ROOT(task), txPkt, (void*) -1, TASK_FD(task), 
  425: 			TASK_DATA(task), RPACK_OFF(pkt));
  426: 	taskExit(task, NULL);
  427: }
  428: 
  429: void *
  430: rxPkt(sched_task_t *task)
  431: {
  432: 	sockaddr_t sa;
  433: 	socklen_t salen = sizeof sa;
  434: 	int rlen;
  435: 	rpack_t *pkt = TASK_DATA(task);
  436: 	struct tftp_hdr *tftp = (struct tftp_hdr*) RPACK_BUF(pkt);
  437: 	u_short code;
  438: 
  439: 	ETRACE();
  440: 
  441: 	memset(RPACK_BUF(pkt), 0, RPACK_LEN(pkt));
  442: 	rlen = recvfrom(TASK_FD(task), RPACK_BUF(pkt), RPACK_LEN(pkt), 0, &sa.sa, &salen);
  443: 	if (rlen == -1) {
  444: 		ESYSERR(0);
  445: 		goto end;
  446: 	} else if (!cli.addr.sa.sa_len) {
  447: 		cli.addr = sa;
  448: 		switch (ntohs(tftp->tftp_opc)) {
  449: 			case TFTP_OPC_RRQ:
  450: 			case TFTP_OPC_WRQ:
  451: 				schedEvent(TASK_ROOT(task), RQ, NULL, TASK_FD(task), 
  452: 						TASK_DATA(task), rlen);
  453: 				break;
  454: 			case TFTP_OPC_ERROR:
  455: 			default:
  456: 				RPACK_REWIND(pkt);
  457: 				code = htons(TFTP_OPC_ERROR);
  458: 				rpack_uint16(pkt, &code, 0);
  459: 				code = htole16(4);
  460: 				rpack_uint16(pkt, &code, 0);
  461: 				rpack_rdata(pkt, errs[4].err_msg, strlen(errs[4].err_msg) + 1);
  462: 
  463: 				schedEvent(TASK_ROOT(task), txPkt, (void*) -1, TASK_FD(task), 
  464: 						TASK_DATA(task), RPACK_OFF(pkt));
  465: 				goto end;
  466: 		}
  467: 	} else if (memcmp(&cli.addr, &sa, salen)) {
  468: 		EERROR(LOG_WARNING, "Packet dropped!!!\n"
  469: 				"Get frame from different address for this session");
  470: 		goto end;
  471: 	} else
  472: 		switch (ntohs(tftp->tftp_opc)) {
  473: 			case TFTP_OPC_ACK:
  474: 				schedEvent(TASK_ROOT(task), ACK, NULL, TASK_FD(task), 
  475: 						TASK_DATA(task), rlen);
  476: 				break;
  477: 			case TFTP_OPC_DATA:
  478: 				schedEvent(TASK_ROOT(task), DATA, NULL, TASK_FD(task), 
  479: 						TASK_DATA(task), rlen);
  480: 				break;
  481: 			case TFTP_OPC_OACK:
  482: 			case TFTP_OPC_ERROR:
  483: 			default:
  484: 				RPACK_REWIND(pkt);
  485: 				code = htons(TFTP_OPC_ERROR);
  486: 				rpack_uint16(pkt, &code, 0);
  487: 				code = htole16(4);
  488: 				rpack_uint16(pkt, &code, 0);
  489: 				rpack_rdata(pkt, errs[4].err_msg, strlen(errs[4].err_msg) + 1);
  490: 
  491: 				schedEvent(TASK_ROOT(task), txPkt, (void*) -1, TASK_FD(task), 
  492: 						TASK_DATA(task), RPACK_OFF(pkt));
  493: 				goto end;
  494: 		}
  495: 
  496: 	schedCancelby(TASK_ROOT(task), taskTIMER, CRITERIA_CALL, timeoutSession, NULL);
  497: 	schedTimer(TASK_ROOT(task), timeoutSession, NULL, timeout, NULL, 0);
  498: end:
  499: 	schedReadSelf(task);
  500: 	taskExit(task, NULL);
  501: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>