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