1/*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2005-2010 Daniel Braniss <danny@cs.huji.ac.il> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 */ 29 30/* 31 | $Id: fsm.c,v 2.8 2007/05/19 16:34:21 danny Exp danny $ 32 */ 33 34#include <sys/cdefs.h> 35__FBSDID("$FreeBSD$"); 36 37#include <sys/param.h> 38#include <sys/types.h> 39#include <sys/socket.h> 40#include <sys/sysctl.h> 41 42#include <netinet/in.h> 43#include <netinet/tcp.h> 44#include <arpa/inet.h> 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 <dev/iscsi_initiator/iscsi.h> 59#include "iscontrol.h" 60 61typedef 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 */ 74static trans_t 75tcpConnect(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 150 if(connect(soc, res->ai_addr, res->ai_addrlen) == 0) 151 break; 152 sv_errno = errno; 153 close(soc); 154 soc = -1; 155 } 156 freeaddrinfo(res0); 157 if(soc != -1) { 158 sess->soc = soc; 159 160#if 0 161 struct timeval timeout; 162 163 val = 1; 164 if(setsockopt(sess->soc, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) 165 fprintf(stderr, "Cannot set socket KEEPALIVE option err=%d %s\n", 166 errno, strerror(errno)); 167 168 if(setsockopt(sess->soc, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) < 0) 169 fprintf(stderr, "Cannot set socket NO delay option err=%d %s\n", 170 errno, strerror(errno)); 171 172 timeout.tv_sec = 10; 173 timeout.tv_usec = 0; 174 if((setsockopt(sess->soc, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0) 175 || (setsockopt(sess->soc, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0)) { 176 fprintf(stderr, "Cannot set socket timeout to %ld err=%d %s\n", 177 timeout.tv_sec, errno, strerror(errno)); 178 } 179#endif 180#ifdef CURIOUS 181 { 182 int len = sizeof(val); 183 if(getsockopt(sess->soc, SOL_SOCKET, SO_SNDBUF, &val, &len) == 0) 184 fprintf(stderr, "was: SO_SNDBUF=%dK\n", val/1024); 185 } 186#endif 187 if(sess->op->sockbufsize) { 188 val = sess->op->sockbufsize * 1024; 189 if((setsockopt(sess->soc, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)) < 0) 190 || (setsockopt(sess->soc, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)) < 0)) { 191 fprintf(stderr, "Cannot set socket sndbuf & rcvbuf to %d err=%d %s\n", 192 val, errno, strerror(errno)); 193 return 0; 194 } 195 } 196 sess->flags |= SESS_CONNECTED; 197 return T1; 198 } 199 200 fprintf(stderr, "errno=%d\n", sv_errno); 201 perror("connect"); 202 switch(sv_errno) { 203 case ECONNREFUSED: 204 case EHOSTUNREACH: 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_initiator.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 int 347doCAM(isess_t *sess) 348{ 349 char pathstr[1024]; 350 union ccb *ccb; 351 int i, n; 352 353 if(ioctl(sess->fd, ISCSIGETCAM, &sess->cam) != 0) { 354 syslog(LOG_WARNING, "ISCSIGETCAM failed: %d", errno); 355 return 0; 356 } 357 debug(1, "nluns=%d", sess->cam.target_nluns); 358 /* 359 | for now will do this for each lun ... 360 */ 361 for(n = i = 0; i < sess->cam.target_nluns; i++) { 362 debug(2, "CAM path_id=%d target_id=%d", 363 sess->cam.path_id, sess->cam.target_id); 364 365 sess->camdev = cam_open_btl(sess->cam.path_id, sess->cam.target_id, 366 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 CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->crs); 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 if(cam_send_ccb(sess->camdev, ccb) < 0) 382 debug(2, "%s", cam_errbuf); 383 else 384 if((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 385 syslog(LOG_WARNING, "XPT_REL_SIMQ CCB failed"); 386 // cam_error_print(sess->camdev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); 387 } 388 else { 389 n++; 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 return n; 396} 397 398static trans_t 399supervise(isess_t *sess) 400{ 401 int sig, val; 402 403 debug_called(3); 404 405 if(strcmp(sess->op->sessionType, "Discovery") == 0) { 406 sess->flags |= SESS_DISCONNECT; 407 return T9; 408 } 409 410 if(vflag) 411 printf("ready to go scsi\n"); 412 413 if(setOptions(sess, SESS_FULLFEATURE) != 0) 414 return 0; // failure 415 416 if((sess->flags & SESS_FULLFEATURE) == 0) { 417 if(daemon(0, 1) != 0) { 418 perror("daemon"); 419 exit(1); 420 } 421 if(sess->op->pidfile != NULL) { 422 FILE *pidf; 423 424 pidf = fopen(sess->op->pidfile, "w"); 425 if(pidf != NULL) { 426 fprintf(pidf, "%d\n", getpid()); 427 fclose(pidf); 428 } 429 } 430 openlog("iscontrol", LOG_CONS|LOG_PERROR|LOG_PID|LOG_NDELAY, LOG_KERN); 431 syslog(LOG_INFO, "running"); 432 433 currsess = sess; 434 if(ioctl(sess->fd, ISCSISTART)) { 435 perror("ISCSISTART"); 436 return -1; 437 } 438 if(doCAM(sess) == 0) { 439 syslog(LOG_WARNING, "no device found"); 440 ioctl(sess->fd, ISCSISTOP); 441 return T15; 442 } 443 444 } 445 else { 446 if(ioctl(sess->fd, ISCSIRESTART)) { 447 perror("ISCSIRESTART"); 448 return -1; 449 } 450 } 451 452 signal(SIGINT, trap); 453 signal(SIGHUP, trap); 454 signal(SIGTERM, trap); 455 456 sig = SIGUSR1; 457 signal(sig, trap); 458 if(ioctl(sess->fd, ISCSISIGNAL, &sig)) { 459 perror("ISCSISIGNAL"); 460 return -1; 461 } 462 sess->flags |= SESS_FULLFEATURE; 463 464 sess->flags &= ~(SESS_REDIRECT | SESS_RECONNECT); 465 if(vflag) 466 printf("iscontrol: supervise starting main loop\n"); 467 /* 468 | the main loop - actually do nothing 469 | all the work is done inside the kernel 470 */ 471 while((sess->flags & (SESS_REDIRECT|SESS_RECONNECT|SESS_DISCONNECT)) == 0) { 472 // do something? 473 // like sending a nop_out? 474 sleep(60); 475 } 476 printf("iscontrol: supervise going down\n"); 477 syslog(LOG_INFO, "sess flags=%x", sess->flags); 478 479 sig = 0; 480 if(ioctl(sess->fd, ISCSISIGNAL, &sig)) { 481 perror("ISCSISIGNAL"); 482 } 483 484 if(sess->flags & SESS_DISCONNECT) { 485 sess->flags &= ~SESS_FULLFEATURE; 486 return T9; 487 } 488 else { 489 val = 0; 490 if(ioctl(sess->fd, ISCSISTOP, &val)) { 491 perror("ISCSISTOP"); 492 } 493 sess->flags |= SESS_INITIALLOGIN1; 494 } 495 return T8; 496} 497 498static int 499handledDiscoveryResp(isess_t *sess, pdu_t *pp) 500{ 501 u_char *ptr; 502 int len, n; 503 504 debug_called(3); 505 506 len = pp->ds_len; 507 ptr = pp->ds_addr; 508 while(len > 0) { 509 if(*ptr != 0) 510 printf("%s\n", ptr); 511 n = strlen((char *)ptr) + 1; 512 len -= n; 513 ptr += n; 514 } 515 return 0; 516} 517 518static int 519doDiscovery(isess_t *sess) 520{ 521 pdu_t spp; 522 text_req_t *tp = (text_req_t *)&spp.ipdu.bhs; 523 524 debug_called(3); 525 526 bzero(&spp, sizeof(pdu_t)); 527 tp->cmd = ISCSI_TEXT_CMD /*| 0x40 */; // because of a bug in openiscsi-target 528 tp->F = 1; 529 tp->ttt = 0xffffffff; 530 addText(&spp, "SendTargets=All"); 531 return sendPDU(sess, &spp, handledDiscoveryResp); 532} 533 534static trans_t 535doLogin(isess_t *sess) 536{ 537 isc_opt_t *op = sess->op; 538 int status, count; 539 540 debug_called(3); 541 542 if(op->chapSecret == NULL && op->tgtChapSecret == NULL) 543 /* 544 | don't need any security negotiation 545 | or in other words: we don't have any secrets to exchange 546 */ 547 sess->csg = LON_PHASE; 548 else 549 sess->csg = SN_PHASE; 550 551 if(sess->tsih) { 552 sess->tsih = 0; // XXX: no 'reconnect' yet 553 sess->flags &= ~SESS_NEGODONE; // XXX: KLUDGE 554 } 555 count = 10; // should be more than enough 556 do { 557 debug(3, "count=%d csg=%d", count, sess->csg); 558 status = loginPhase(sess); 559 if(count-- == 0) 560 // just in case we get into a loop 561 status = -1; 562 } while(status == 0 && (sess->csg != FF_PHASE)); 563 564 sess->flags &= ~SESS_INITIALLOGIN; 565 debug(3, "status=%d", status); 566 567 switch(status) { 568 case 0: // all is ok ... 569 sess->flags |= SESS_LOGGEDIN; 570 if(strcmp(sess->op->sessionType, "Discovery") == 0) 571 doDiscovery(sess); 572 return T5; 573 574 case 1: // redirect - temporary/permanent 575 /* 576 | start from scratch? 577 */ 578 sess->flags &= ~SESS_NEGODONE; 579 sess->flags |= (SESS_REDIRECT | SESS_INITIALLOGIN1); 580 syslog(LOG_DEBUG, "target sent REDIRECT"); 581 return T7; 582 583 case 2: // initiator terminal error 584 return 0; 585 case 3: // target terminal error -- could retry ... 586 sleep(5); 587 return T7; // lets try 588 default: 589 return 0; 590 } 591} 592 593static int 594handleLogoutResp(isess_t *sess, pdu_t *pp) 595{ 596 if(sess->flags & SESS_DISCONNECT) { 597 int val = 0; 598 if(ioctl(sess->fd, ISCSISTOP, &val)) { 599 perror("ISCSISTOP"); 600 } 601 return 0; 602 } 603 return T13; 604} 605 606static trans_t 607startLogout(isess_t *sess) 608{ 609 pdu_t spp; 610 logout_req_t *p = (logout_req_t *)&spp.ipdu.bhs; 611 612 bzero(&spp, sizeof(pdu_t)); 613 p->cmd = ISCSI_LOGOUT_CMD| 0x40; 614 p->reason = BIT(7) | 0; 615 p->CID = htons(1); 616 617 return sendPDU(sess, &spp, handleLogoutResp); 618} 619 620static trans_t 621inLogout(isess_t *sess) 622{ 623 if(sess->flags & SESS_RECONNECT) 624 return T18; 625 return 0; 626} 627 628typedef enum { 629 S1, S2, /*S3,*/ S4, S5, S6, S7, S8 630} state_t; 631 632/** 633 S1: FREE 634 S2: XPT_WAIT 635 S4: IN_LOGIN 636 S5: LOGGED_IN 637 S6: IN_LOGOUT 638 S7: LOGOUT_REQUESTED 639 S8: CLEANUP_WAIT 640 641 -------<-------------+ 642 +--------->/ S1 \<----+ | 643 T13| +->\ /<-+ \ | 644 | / ---+--- \ \ | 645 | / | T2 \ | | 646 | T8 | |T1 | | | 647 | | | / |T7 | 648 | | | / | | 649 | | | / | | 650 | | V / / | 651 | | ------- / / | 652 | | / S2 \ / | 653 | | \ / / | 654 | | ---+--- / | 655 | | |T4 / | 656 | | V / | T18 657 | | ------- / | 658 | | / S4 \ | 659 | | \ / | 660 | | ---+--- | T15 661 | | |T5 +--------+---------+ 662 | | | /T16+-----+------+ | 663 | | | / -+-----+--+ | | 664 | | | / / S7 \ |T12| | 665 | | | / +->\ /<-+ V V 666 | | | / / -+----- ------- 667 | | | / /T11 |T10 / S8 \ 668 | | V / / V +----+ \ / 669 | | ---+-+- ----+-- | ------- 670 | | / S5 \T9 / S6 \<+ ^ 671 | +-----\ /--->\ / T14 | 672 | ------- --+----+------+T17 673 +---------------------------+ 674*/ 675 676int 677fsm(isc_opt_t *op) 678{ 679 state_t state; 680 isess_t *sess; 681 682 if((sess = calloc(1, sizeof(isess_t))) == NULL) { 683 // boy, is this a bad start ... 684 fprintf(stderr, "no memory!\n"); 685 return -1; 686 } 687 688 state = S1; 689 sess->op = op; 690 sess->fd = -1; 691 sess->soc = -1; 692 sess->target.address = strdup(op->targetAddress); 693 sess->target.port = op->port; 694 sess->target.pgt = op->targetPortalGroupTag; 695 696 sess->flags = SESS_INITIALLOGIN | SESS_INITIALLOGIN1; 697 698 do { 699 switch(state) { 700 701 case S1: 702 switch(tcpConnect(sess)) { 703 case T1: state = S2; break; 704 default: state = S8; break; 705 } 706 break; 707 708 case S2: 709 switch(startSession(sess)) { 710 case T2: state = S1; break; 711 case T4: state = S4; break; 712 default: state = S8; break; 713 } 714 break; 715 716 case S4: 717 switch(doLogin(sess)) { 718 case T7: state = S1; break; 719 case T5: state = S5; break; 720 default: state = S8; break; 721 } 722 break; 723 724 case S5: 725 switch(supervise(sess)) { 726 case T8: state = S1; break; 727 case T9: state = S6; break; 728 case T11: state = S7; break; 729 case T15: state = S8; break; 730 default: state = S8; break; 731 } 732 break; 733 734 case S6: 735 switch(startLogout(sess)) { 736 case T13: state = S1; break; 737 case T14: state = S6; break; 738 case T16: state = S8; break; 739 default: state = S8; break; 740 } 741 break; 742 743 case S7: 744 switch(inLogout(sess)) { 745 case T18: state = S1; break; 746 case T10: state = S6; break; 747 case T12: state = S7; break; 748 case T16: state = S8; break; 749 default: state = S8; break; 750 } 751 break; 752 753 case S8: 754 // maybe do some clean up? 755 syslog(LOG_INFO, "terminated"); 756 return 0; 757 } 758 } while(1); 759} 760