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