fsm.c revision 185289
1/*- 2 * Copyright (c) 2005-2008 Daniel Braniss <danny@cs.huji.ac.il> 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 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 */ 27 28/* 29 | $Id: fsm.c,v 2.8 2007/05/19 16:34:21 danny Exp danny $ 30 */ 31 32#include <sys/cdefs.h> 33__FBSDID("$FreeBSD: head/sbin/iscontrol/fsm.c 185289 2008-11-25 07:17:11Z scottl $"); 34 35#include <sys/param.h> 36#include <sys/types.h> 37#include <sys/socket.h> 38#include <sys/sysctl.h> 39 40#include <netinet/in.h> 41#include <netinet/tcp.h> 42#include <arpa/inet.h> 43#if __FreeBSD_version < 500000 44#include <sys/time.h> 45#endif 46#include <sys/ioctl.h> 47#include <netdb.h> 48#include <stdlib.h> 49#include <unistd.h> 50#include <stdio.h> 51#include <string.h> 52#include <errno.h> 53#include <fcntl.h> 54#include <time.h> 55#include <syslog.h> 56#include <stdarg.h> 57#include <camlib.h> 58 59#include "iscsi.h" 60#include "iscontrol.h" 61 62typedef enum { 63 T1 = 1, 64 T2, /*T3,*/ T4, T5, /*T6,*/ T7, T8, T9, 65 T10, T11, T12, T13, T14, T15, T16, T18 66} trans_t; 67 68/* 69 | now supports IPV6 70 | thanks to: 71 | Hajimu UMEMOTO @ Internet Mutual Aid Society Yokohama, Japan 72 | ume@mahoroba.org ume@{,jp.}FreeBSD.org 73 | http://www.imasy.org/~ume/ 74 */ 75static trans_t 76tcpConnect(isess_t *sess) 77{ 78 isc_opt_t *op = sess->op; 79 int val, sv_errno, soc; 80 struct addrinfo *res, *res0, hints; 81 char pbuf[10]; 82 83 debug_called(3); 84 if(sess->flags & (SESS_RECONNECT|SESS_REDIRECT)) { 85 syslog(LOG_INFO, "%s", (sess->flags & SESS_RECONNECT) 86 ? "Reconnect": "Redirected"); 87 88 debug(1, "%s", (sess->flags & SESS_RECONNECT) ? "Reconnect": "Redirected"); 89 shutdown(sess->soc, SHUT_RDWR); 90 //close(sess->soc); 91 sess->soc = -1; 92 93 sess->flags &= ~SESS_CONNECTED; 94 if(sess->flags & SESS_REDIRECT) { 95 sess->redirect_cnt++; 96 sess->flags |= SESS_RECONNECT; 97 } else 98 sleep(2); // XXX: actually should be ? 99#ifdef notyet 100 { 101 time_t sec; 102 // make sure we are not in a loop 103 // XXX: this code has to be tested 104 sec = time(0) - sess->reconnect_time; 105 if(sec > (5*60)) { 106 // if we've been connected for more that 5 minutes 107 // then just reconnect 108 sess->reconnect_time = sec; 109 sess->reconnect_cnt1 = 0; 110 } 111 else { 112 // 113 sess->reconnect_cnt1++; 114 if((sec / sess->reconnect_cnt1) < 2) { 115 // if less that 2 seconds from the last reconnect 116 // we are most probably looping 117 syslog(LOG_CRIT, "too many reconnects %d", sess->reconnect_cnt1); 118 return 0; 119 } 120 } 121 } 122#endif 123 sess->reconnect_cnt++; 124 } 125 126 snprintf(pbuf, sizeof(pbuf), "%d", op->port); 127 memset(&hints, 0, sizeof(hints)); 128 hints.ai_family = PF_UNSPEC; 129 hints.ai_socktype = SOCK_STREAM; 130 debug(1, "targetAddress=%s port=%d", op->targetAddress, op->port); 131 if((val = getaddrinfo(op->targetAddress, pbuf, &hints, &res0)) != 0) { 132 fprintf(stderr, "getaddrinfo(%s): %s\n", op->targetAddress, gai_strerror(val)); 133 return 0; 134 } 135 sess->flags &= ~SESS_CONNECTED; 136 sv_errno = 0; 137 soc = -1; 138 for(res = res0; res; res = res->ai_next) { 139 soc = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 140 if (soc == -1) 141 continue; 142 143 // from Patrick.Guelat@imp.ch: 144 // iscontrol can be called without waiting for the socket entry to time out 145 val = 1; 146 if(setsockopt(soc, SOL_SOCKET, SO_REUSEADDR, &val, (socklen_t)sizeof(val)) < 0) { 147 fprintf(stderr, "Cannot set socket SO_REUSEADDR %d: %s\n\n", 148 errno, strerror(errno)); 149 } 150 151 if(connect(soc, res->ai_addr, res->ai_addrlen) == 0) 152 break; 153 sv_errno = errno; 154 close(soc); 155 soc = -1; 156 } 157 freeaddrinfo(res0); 158 if(soc != -1) { 159 sess->soc = soc; 160 161#if 0 162 struct timeval timeout; 163 164 val = 1; 165 if(setsockopt(sess->soc, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) 166 fprintf(stderr, "Cannot set socket KEEPALIVE option err=%d %s\n", 167 errno, strerror(errno)); 168 169 if(setsockopt(sess->soc, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) < 0) 170 fprintf(stderr, "Cannot set socket NO delay option err=%d %s\n", 171 errno, strerror(errno)); 172 173 timeout.tv_sec = 10; 174 timeout.tv_usec = 0; 175 if((setsockopt(sess->soc, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0) 176 || (setsockopt(sess->soc, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0)) { 177 fprintf(stderr, "Cannot set socket timeout to %ld err=%d %s\n", 178 timeout.tv_sec, errno, strerror(errno)); 179 } 180#endif 181#ifdef CURIOUS 182 { 183 int len = sizeof(val); 184 if(getsockopt(sess->soc, SOL_SOCKET, SO_SNDBUF, &val, &len) == 0) 185 fprintf(stderr, "was: SO_SNDBUF=%dK\n", val/1024); 186 } 187#endif 188 if(sess->op->sockbufsize) { 189 val = sess->op->sockbufsize * 1024; 190 if((setsockopt(sess->soc, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)) < 0) 191 || (setsockopt(sess->soc, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)) < 0)) { 192 fprintf(stderr, "Cannot set socket sndbuf & rcvbuf to %d err=%d %s\n", 193 val, errno, strerror(errno)); 194 return 0; 195 } 196 } 197 sess->flags |= SESS_CONNECTED; 198 return T1; 199 } 200 201 fprintf(stderr, "errno=%d\n", sv_errno); 202 perror("connect"); 203 switch(sv_errno) { 204 case ECONNREFUSED: 205 case ENETUNREACH: 206 case ETIMEDOUT: 207 if((sess->flags & SESS_REDIRECT) == 0) { 208 if(strcmp(op->targetAddress, sess->target.address) != 0) { 209 syslog(LOG_INFO, "reconnecting to original target address"); 210 free(op->targetAddress); 211 op->targetAddress = sess->target.address; 212 op->port = sess->target.port; 213 op->targetPortalGroupTag = sess->target.pgt; 214 return T1; 215 } 216 } 217 sleep(5); // for now ... 218 return T1; 219 default: 220 return 0; // terminal error 221 } 222} 223 224int 225setOptions(isess_t *sess, int flag) 226{ 227 isc_opt_t oop; 228 char *sep; 229 230 debug_called(3); 231 232 bzero(&oop, sizeof(isc_opt_t)); 233 234 if((flag & SESS_FULLFEATURE) == 0) { 235 oop.initiatorName = sess->op->initiatorName; 236 oop.targetAddress = sess->op->targetAddress; 237 if(sess->op->targetName != 0) 238 oop.targetName = sess->op->targetName; 239 240 oop.maxRecvDataSegmentLength = sess->op->maxRecvDataSegmentLength; 241 oop.maxXmitDataSegmentLength = sess->op->maxXmitDataSegmentLength; // XXX: 242 oop.maxBurstLength = sess->op->maxBurstLength; 243 oop.maxluns = sess->op->maxluns; 244 } 245 else { 246 /* 247 | turn on digestion only after login 248 */ 249 if(sess->op->headerDigest != NULL) { 250 sep = strchr(sess->op->headerDigest, ','); 251 if(sep == NULL) 252 oop.headerDigest = sess->op->headerDigest; 253 debug(1, "oop.headerDigest=%s", oop.headerDigest); 254 } 255 if(sess->op->dataDigest != NULL) { 256 sep = strchr(sess->op->dataDigest, ','); 257 if(sep == NULL) 258 oop.dataDigest = sess->op->dataDigest; 259 debug(1, "oop.dataDigest=%s", oop.dataDigest); 260 } 261 } 262 263 if(ioctl(sess->fd, ISCSISETOPT, &oop)) { 264 perror("ISCSISETOPT"); 265 return -1; 266 } 267 return 0; 268} 269 270static trans_t 271startSession(isess_t *sess) 272{ 273 274 int n, fd, nfd; 275 char *dev; 276 277 debug_called(3); 278 279 if((sess->flags & SESS_CONNECTED) == 0) { 280 return T2; 281 } 282 if(sess->fd == -1) { 283 fd = open(iscsidev, O_RDWR); 284 if(fd < 0) { 285 perror(iscsidev); 286 return 0; 287 } 288 { 289 // XXX: this has to go 290 size_t n; 291 n = sizeof(sess->isid); 292 if(sysctlbyname("net.iscsi.isid", (void *)sess->isid, (size_t *)&n, 0, 0) != 0) 293 perror("sysctlbyname"); 294 } 295 if(ioctl(fd, ISCSISETSES, &n)) { 296 perror("ISCSISETSES"); 297 return 0; 298 } 299 asprintf(&dev, "%s%d", iscsidev, n); 300 nfd = open(dev, O_RDWR); 301 if(nfd < 0) { 302 perror(dev); 303 free(dev); 304 return 0; 305 } 306 free(dev); 307 close(fd); 308 sess->fd = nfd; 309 310 if(setOptions(sess, 0) != 0) 311 return -1; 312 } 313 314 if(ioctl(sess->fd, ISCSISETSOC, &sess->soc)) { 315 perror("ISCSISETSOC"); 316 return 0; 317 } 318 319 return T4; 320} 321 322isess_t *currsess; 323 324static void 325trap(int sig) 326{ 327 syslog(LOG_NOTICE, "trapped signal %d", sig); 328 fprintf(stderr, "trapped signal %d\n", sig); 329 330 switch(sig) { 331 case SIGHUP: 332 currsess->flags |= SESS_DISCONNECT; 333 break; 334 335 case SIGUSR1: 336 currsess->flags |= SESS_RECONNECT; 337 break; 338 339 case SIGINT: 340 case SIGTERM: 341 default: 342 return; // ignore 343 } 344} 345 346static void 347doCAM(isess_t *sess) 348{ 349 char pathstr[1024]; 350 union ccb *ccb; 351 int i; 352 353 if(ioctl(sess->fd, ISCSIGETCAM, &sess->cam) != 0) { 354 syslog(LOG_WARNING, "ISCSIGETCAM failed: %d", errno); 355 return; 356 } 357 debug(2, "nluns=%d", sess->cam.target_nluns); 358 /* 359 | for now will do this for each lun ... 360 */ 361 for(i = 0; i < sess->cam.target_nluns; i++) { 362 debug(2, "CAM path_id=%d target_id=%d target_lun=%d", 363 sess->cam.path_id, sess->cam.target_id, sess->cam.target_lun[i]); 364 365 sess->camdev = cam_open_btl(sess->cam.path_id, sess->cam.target_id, 366 sess->cam.target_lun[i], O_RDWR, NULL); 367 if(sess->camdev == NULL) { 368 syslog(LOG_WARNING, "%s", cam_errbuf); 369 debug(3, "%s", cam_errbuf); 370 continue; 371 } 372 373 cam_path_string(sess->camdev, pathstr, sizeof(pathstr)); 374 debug(2, "pathstr=%s", pathstr); 375 376 ccb = cam_getccb(sess->camdev); 377 bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_relsim) - sizeof(struct ccb_hdr)); 378 ccb->ccb_h.func_code = XPT_REL_SIMQ; 379 ccb->crs.release_flags = RELSIM_ADJUST_OPENINGS; 380 ccb->crs.openings = sess->op->tags; 381 382 if(cam_send_ccb(sess->camdev, ccb) < 0) 383 syslog(LOG_WARNING, "%s", cam_errbuf); 384 else 385 if((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 386 syslog(LOG_WARNING, "XPT_REL_SIMQ CCB failed"); 387 // cam_error_print(sess->camdev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); 388 } 389 else 390 syslog(LOG_INFO, "%s tagged openings now %d\n", pathstr, ccb->crs.openings); 391 392 cam_freeccb(ccb); 393 cam_close_device(sess->camdev); 394 } 395} 396 397static trans_t 398supervise(isess_t *sess) 399{ 400 int sig, val; 401 402 debug_called(3); 403 404 if(strcmp(sess->op->sessionType, "Discovery") == 0) { 405 sess->flags |= SESS_DISCONNECT; 406 return T9; 407 } 408 409 if(vflag) 410 printf("ready to go scsi\n"); 411 412 if(setOptions(sess, SESS_FULLFEATURE) != 0) 413 return 0; // failure 414 415 if((sess->flags & SESS_FULLFEATURE) == 0) { 416 if(daemon(0, 1) != 0) { 417 perror("daemon"); 418 exit(1); 419 } 420 421 openlog("iscontrol", LOG_CONS|LOG_PERROR|LOG_PID|LOG_NDELAY, LOG_KERN); 422 syslog(LOG_INFO, "running"); 423 424 currsess = sess; 425 if(ioctl(sess->fd, ISCSISTART)) { 426 perror("ISCSISTART"); 427 return -1; 428 } 429 doCAM(sess); 430 431 } 432 else { 433 if(ioctl(sess->fd, ISCSIRESTART)) { 434 perror("ISCSIRESTART"); 435 return -1; 436 } 437 } 438 439 signal(SIGINT, trap); 440 signal(SIGHUP, trap); 441 signal(SIGTERM, trap); 442 443 sig = SIGUSR1; 444 signal(sig, trap); 445 if(ioctl(sess->fd, ISCSISIGNAL, &sig)) { 446 perror("ISCSISIGNAL"); 447 return -1; 448 } 449 sess->flags |= SESS_FULLFEATURE; 450 451 sess->flags &= ~(SESS_REDIRECT | SESS_RECONNECT); 452 printf("iscontrol: supervise starting main loop\n"); 453 /* 454 | the main loop - actually do nothing 455 | all the work is done inside the kernel 456 */ 457 while((sess->flags & (SESS_REDIRECT|SESS_RECONNECT|SESS_DISCONNECT)) == 0) { 458 // do something? 459 // like sending a nop_out? 460 sleep(60); 461 } 462 printf("iscontrol: supervise going down\n"); 463 syslog(LOG_INFO, "sess flags=%x", sess->flags); 464 465 sig = 0; 466 if(ioctl(sess->fd, ISCSISIGNAL, &sig)) { 467 perror("ISCSISIGNAL"); 468 } 469 470 if(sess->flags & SESS_DISCONNECT) { 471 val = 0; 472 if(ioctl(sess->fd, ISCSISTOP, &val)) { 473 perror("ISCSISTOP"); 474 } 475 sess->flags &= ~SESS_FULLFEATURE; 476 return T9; 477 } 478 else { 479 sess->flags |= SESS_INITIALLOGIN1; 480 } 481 return T8; 482} 483 484static int 485handledDiscoveryResp(isess_t *sess, pdu_t *pp) 486{ 487 u_char *ptr; 488 int len, n; 489 490 debug_called(3); 491 492 len = pp->ds_len; 493 ptr = pp->ds; 494 while(len > 0) { 495 if(*ptr != 0) 496 printf("%s\n", ptr); 497 n = strlen((char *)ptr) + 1; 498 len -= n; 499 ptr += n; 500 } 501 return 0; 502} 503 504static int 505doDiscovery(isess_t *sess) 506{ 507 pdu_t spp; 508 text_req_t *tp = (text_req_t *)&spp.ipdu.bhs; 509 510 debug_called(3); 511 512 bzero(&spp, sizeof(pdu_t)); 513 tp->cmd = ISCSI_TEXT_CMD /*| 0x40 */; // because of a bug in openiscsi-target 514 tp->F = 1; 515 tp->ttt = 0xffffffff; 516 addText(&spp, "SendTargets=All"); 517 return sendPDU(sess, &spp, handledDiscoveryResp); 518} 519 520static trans_t 521doLogin(isess_t *sess) 522{ 523 isc_opt_t *op = sess->op; 524 int status, count; 525 526 debug_called(3); 527 528 if(op->chapSecret == NULL && op->tgtChapSecret == NULL) 529 /* 530 | don't need any security negotiation 531 | or in other words: we don't have any secrets to exchange 532 */ 533 sess->csg = LON_PHASE; 534 else 535 sess->csg = SN_PHASE; 536 537 if(sess->tsih) { 538 sess->tsih = 0; // XXX: no 'reconnect' yet 539 sess->flags &= ~SESS_NEGODONE; // XXX: KLUDGE 540 } 541 count = 10; // should be more than enough 542 do { 543 debug(3, "count=%d csg=%d", count, sess->csg); 544 status = loginPhase(sess); 545 if(count-- == 0) 546 // just in case we get into a loop 547 status = -1; 548 } while(status == 0 && (sess->csg != FF_PHASE)); 549 550 sess->flags &= ~SESS_INITIALLOGIN; 551 debug(3, "status=%d", status); 552 553 switch(status) { 554 case 0: // all is ok ... 555 sess->flags |= SESS_LOGGEDIN; 556 if(strcmp(sess->op->sessionType, "Discovery") == 0) 557 doDiscovery(sess); 558 return T5; 559 560 case 1: // redirect - temporary/permanent 561 /* 562 | start from scratch? 563 */ 564 sess->flags &= ~SESS_NEGODONE; 565 sess->flags |= (SESS_REDIRECT | SESS_INITIALLOGIN1); 566 syslog(LOG_DEBUG, "target sent REDIRECT"); 567 return T7; 568 569 case 2: // initiator terminal error 570 return 0; 571 case 3: // target terminal error -- could retry ... 572 sleep(5); 573 return T7; // lets try 574 default: 575 return 0; 576 } 577} 578 579static int 580handleLogoutResp(isess_t *sess, pdu_t *pp) 581{ 582 if(sess->flags & SESS_DISCONNECT) 583 return 0; 584 return T13; 585} 586 587static trans_t 588startLogout(isess_t *sess) 589{ 590 pdu_t spp; 591 logout_req_t *p = (logout_req_t *)&spp.ipdu.bhs; 592 593 bzero(&spp, sizeof(pdu_t)); 594 p->cmd = ISCSI_LOGOUT_CMD| 0x40; 595 p->reason = BIT(7) | 0; 596 p->CID = htons(1); 597 598 return sendPDU(sess, &spp, handleLogoutResp); 599} 600 601static trans_t 602inLogout(isess_t *sess) 603{ 604 if(sess->flags & SESS_RECONNECT) 605 return T18; 606 return 0; 607} 608 609typedef enum { 610 S1, S2, /*S3,*/ S4, S5, S6, S7, S8 611} state_t; 612 613#if 0 614 S1: FREE 615 S2: XPT_WAIT 616 S4: IN_LOGIN 617 S5: LOGGED_IN 618 S6: IN_LOGOUT 619 S7: LOGOUT_REQUESTED 620 S8: CLEANUP_WAIT 621 622 -------<-------------+ 623 +--------->/ S1 \<----+ | 624 T13| +->\ /<-+ \ | 625 | / ---+--- \ \ | 626 | / | T2 \ | | 627 | T8 | |T1 | | | 628 | | | / |T7 | 629 | | | / | | 630 | | | / | | 631 | | V / / | 632 | | ------- / / | 633 | | / S2 \ / | 634 | | \ / / | 635 | | ---+--- / | 636 | | |T4 / | 637 | | V / | T18 638 | | ------- / | 639 | | / S4 \ | 640 | | \ / | 641 | | ---+--- | T15 642 | | |T5 +--------+---------+ 643 | | | /T16+-----+------+ | 644 | | | / -+-----+--+ | | 645 | | | / / S7 \ |T12| | 646 | | | / +->\ /<-+ V V 647 | | | / / -+----- ------- 648 | | | / /T11 |T10 / S8 \ 649 | | V / / V +----+ \ / 650 | | ---+-+- ----+-- | ------- 651 | | / S5 \T9 / S6 \<+ ^ 652 | +-----\ /--->\ / T14 | 653 | ------- --+----+------+T17 654 +---------------------------+ 655#endif 656 657int 658fsm(isc_opt_t *op) 659{ 660 state_t state; 661 isess_t *sess; 662 663 if((sess = calloc(1, sizeof(isess_t))) == NULL) { 664 // boy, is this a bad start ... 665 fprintf(stderr, "no memory!\n"); 666 return -1; 667 } 668 669 state = S1; 670 sess->op = op; 671 sess->fd = -1; 672 sess->soc = -1; 673 sess->target.address = strdup(op->targetAddress); 674 sess->target.port = op->port; 675 sess->target.pgt = op->targetPortalGroupTag; 676 677 sess->flags = SESS_INITIALLOGIN | SESS_INITIALLOGIN1; 678 679 do { 680 switch(state) { 681 682 case S1: 683 switch(tcpConnect(sess)) { 684 case T1: state = S2; break; 685 default: state = S8; break; 686 } 687 break; 688 689 case S2: 690 switch(startSession(sess)) { 691 case T2: state = S1; break; 692 case T4: state = S4; break; 693 default: state = S8; break; 694 } 695 break; 696 697 case S4: 698 switch(doLogin(sess)) { 699 case T7: state = S1; break; 700 case T5: state = S5; break; 701 default: state = S8; break; 702 } 703 break; 704 705 case S5: 706 switch(supervise(sess)) { 707 case T8: state = S1; break; 708 case T9: state = S6; break; 709 case T11: state = S7; break; 710 case T15: state = S8; break; 711 default: state = S8; break; 712 } 713 break; 714 715 case S6: 716 switch(startLogout(sess)) { 717 case T13: state = S1; break; 718 case T14: state = S6; break; 719 case T16: state = S8; break; 720 default: state = S8; break; 721 } 722 break; 723 724 case S7: 725 switch(inLogout(sess)) { 726 case T18: state = S1; break; 727 case T10: state = S6; break; 728 case T12: state = S7; break; 729 case T16: state = S8; break; 730 default: state = S8; break; 731 } 732 break; 733 734 case S8: 735 // maybe do some clean up? 736 syslog(LOG_INFO, "terminated"); 737 return 0; 738 } 739 } while(1); 740} 741