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