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 #include <sys/ioctl.h> 41 #include <netdb.h> 42 #include <stdlib.h> 43 #include <unistd.h> 44 #include <stdio.h> 45 #include <string.h> 46 #include <errno.h> 47 #include <fcntl.h> 48 #include <time.h> 49 #include <syslog.h> 50 #include <stdarg.h> 51 #include <camlib.h> 52 53 #include "iscsi.h" 54 #include "iscontrol.h" 55 56 typedef enum { 57 T1 = 1, 58 T2, /*T3,*/ T4, T5, /*T6,*/ T7, T8, T9, 59 T10, T11, T12, T13, T14, T15, T16, T18 60 } trans_t; 61 62 /* 63 | now supports IPV6 64 | thanks to: 65 | Hajimu UMEMOTO @ Internet Mutual Aid Society Yokohama, Japan 66 | ume@mahoroba.org ume@{,jp.}FreeBSD.org 67 | http://www.imasy.org/~ume/ 68 */ 69 static trans_t 70 tcpConnect(isess_t *sess) 71 { 72 isc_opt_t *op = sess->op; 73 int val, sv_errno, soc; 74 struct addrinfo *res, *res0, hints; 75 char pbuf[10]; 76 77 debug_called(3); 78 if(sess->flags & (SESS_RECONNECT|SESS_REDIRECT)) { 79 syslog(LOG_INFO, "%s", (sess->flags & SESS_RECONNECT) 80 ? "Reconnect": "Redirected"); 81 82 debug(1, "%s", (sess->flags & SESS_RECONNECT) ? "Reconnect": "Redirected"); 83 shutdown(sess->soc, SHUT_RDWR); 84 //close(sess->soc); 85 sess->soc = -1; 86 87 sess->flags &= ~SESS_CONNECTED; 88 if(sess->flags & SESS_REDIRECT) { 89 sess->redirect_cnt++; 90 sess->flags |= SESS_RECONNECT; 91 } else 92 sleep(2); // XXX: actually should be ? 93 #ifdef notyet 94 { 95 time_t sec; 96 // make sure we are not in a loop 97 // XXX: this code has to be tested 98 sec = time(0) - sess->reconnect_time; 99 if(sec > (5*60)) { 100 // if we've been connected for more that 5 minutes 101 // then just reconnect 102 sess->reconnect_time = sec; 103 sess->reconnect_cnt1 = 0; 104 } 105 else { 106 // 107 sess->reconnect_cnt1++; 108 if((sec / sess->reconnect_cnt1) < 2) { 109 // if less that 2 seconds from the last reconnect 110 // we are most probably looping 111 syslog(LOG_CRIT, "too many reconnects %d", sess->reconnect_cnt1); 112 return 0; 113 } 114 } 115 } 116 #endif 117 sess->reconnect_cnt++; 118 } 119 120 snprintf(pbuf, sizeof(pbuf), "%d", op->port); 121 memset(&hints, 0, sizeof(hints)); 122 hints.ai_family = PF_UNSPEC; 123 hints.ai_socktype = SOCK_STREAM; 124 debug(1, "targetAddress=%s port=%d", op->targetAddress, op->port); 125 if((val = getaddrinfo(op->targetAddress, pbuf, &hints, &res0)) != 0) { 126 fprintf(stderr, "getaddrinfo(%s): %s\n", op->targetAddress, gai_strerror(val)); 127 return 0; 128 } 129 sess->flags &= ~SESS_CONNECTED; 130 sv_errno = 0; 131 soc = -1; 132 for(res = res0; res; res = res->ai_next) { 133 soc = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 134 if (soc == -1) 135 continue; 136 137 // from Patrick.Guelat@imp.ch: 138 // iscontrol can be called without waiting for the socket entry to time out 139 val = 1; 140 if(setsockopt(soc, SOL_SOCKET, SO_REUSEADDR, &val, (socklen_t)sizeof(val)) < 0) { 141 fprintf(stderr, "Cannot set socket SO_REUSEADDR %d: %s\n\n", 142 errno, strerror(errno)); 143 } 144 if(connect(soc, res->ai_addr, res->ai_addrlen) == 0) 145 break; 146 147 sv_errno = errno; 148 close(soc); 149 soc = -1; 150 } 151 freeaddrinfo(res0); 152 153 if(soc != -1) { 154 sess->soc = soc; 155 156 /* Default to TCP_NODELAY to improve transfers */ 157 if(setsockopt(sess->soc, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) < 0) 158 fprintf(stderr, "Cannot set socket NO delay option err=%d %s\n", 159 errno, strerror(errno)); 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 224 int 225 setOptions(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 270 static trans_t 271 startSession(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 sleep(1); /* XXX temporary */ 300 asprintf(&dev, "%s%d", iscsidev, n); 301 nfd = open(dev, O_RDWR); 302 if(nfd < 0) { 303 perror(dev); 304 free(dev); 305 return 0; 306 } 307 free(dev); 308 close(fd); 309 sess->fd = nfd; 310 311 if(setOptions(sess, 0) != 0) 312 return -1; 313 } 314 315 if(ioctl(sess->fd, ISCSISETSOC, &sess->soc)) { 316 perror("ISCSISETSOC"); 317 return 0; 318 } 319 320 return T4; 321 } 322 323 isess_t *currsess; 324 325 static void 326 trap(int sig) 327 { 328 syslog(LOG_NOTICE, "trapped signal %d", sig); 329 fprintf(stderr, "trapped signal %d\n", sig); 330 331 switch(sig) { 332 case SIGHUP: 333 currsess->flags |= SESS_DISCONNECT; 334 break; 335 336 case SIGUSR1: 337 currsess->flags |= SESS_RECONNECT; 338 break; 339 340 case SIGINT: 341 case SIGTERM: 342 default: 343 return; // ignore 344 } 345 } 346 347 static void 348 doCAM(isess_t *sess) 349 { 350 char pathstr[1024]; 351 union ccb *ccb; 352 int i; 353 354 if(ioctl(sess->fd, ISCSIGETCAM, &sess->cam) != 0) { 355 syslog(LOG_WARNING, "ISCSIGETCAM failed: %d", errno); 356 return; 357 } 358 debug(2, "nluns=%d", sess->cam.target_nluns); 359 /* 360 | for now will do this for each lun ... 361 */ 362 for(i = 0; i < sess->cam.target_nluns; i++) { 363 debug(2, "CAM path_id=%d target_id=%d target_lun=%d", 364 sess->cam.path_id, sess->cam.target_id, sess->cam.target_lun[i]); 365 366 sess->camdev = cam_open_btl(sess->cam.path_id, sess->cam.target_id, 367 sess->cam.target_lun[i], O_RDWR, NULL); 368 if(sess->camdev == NULL) { 369 syslog(LOG_WARNING, "%s", cam_errbuf); 370 debug(3, "%s", cam_errbuf); 371 continue; 372 } 373 374 cam_path_string(sess->camdev, pathstr, sizeof(pathstr)); 375 debug(2, "pathstr=%s", pathstr); 376 377 ccb = cam_getccb(sess->camdev); 378 bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_relsim) - sizeof(struct ccb_hdr)); 379 ccb->ccb_h.func_code = XPT_REL_SIMQ; 380 ccb->crs.release_flags = RELSIM_ADJUST_OPENINGS; 381 ccb->crs.openings = sess->op->tags; 382 383 if(cam_send_ccb(sess->camdev, ccb) < 0) 384 syslog(LOG_WARNING, "%s", cam_errbuf); 385 else 386 if((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 387 syslog(LOG_WARNING, "XPT_REL_SIMQ CCB failed"); 388 // cam_error_print(sess->camdev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); 389 } 390 else 391 syslog(LOG_INFO, "%s tagged openings now %d\n", pathstr, ccb->crs.openings); 392 393 cam_freeccb(ccb); 394 cam_close_device(sess->camdev); 395 } 396 } 397 398 static trans_t 399 supervise(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 422 openlog("iscontrol", LOG_CONS|LOG_PERROR|LOG_PID|LOG_NDELAY, LOG_KERN); 423 syslog(LOG_INFO, "running"); 424 425 currsess = sess; 426 if(ioctl(sess->fd, ISCSISTART)) { 427 perror("ISCSISTART"); 428 return -1; 429 } 430 doCAM(sess); 431 432 } 433 else { 434 if(ioctl(sess->fd, ISCSIRESTART)) { 435 perror("ISCSIRESTART"); 436 return -1; 437 } 438 } 439 440 signal(SIGINT, trap); 441 signal(SIGHUP, trap); 442 signal(SIGTERM, trap); 443 444 sig = SIGUSR1; 445 signal(sig, trap); 446 if(ioctl(sess->fd, ISCSISIGNAL, &sig)) { 447 perror("ISCSISIGNAL"); 448 return -1; 449 } 450 sess->flags |= SESS_FULLFEATURE; 451 452 sess->flags &= ~(SESS_REDIRECT | SESS_RECONNECT); 453 printf("iscontrol: supervise starting main loop\n"); 454 /* 455 | the main loop - actually do nothing 456 | all the work is done inside the kernel 457 */ 458 while((sess->flags & (SESS_REDIRECT|SESS_RECONNECT|SESS_DISCONNECT)) == 0) { 459 // do something? 460 // like sending a nop_out? 461 sleep(60); 462 } 463 printf("iscontrol: supervise going down\n"); 464 syslog(LOG_INFO, "sess flags=%x", sess->flags); 465 466 sig = 0; 467 if(ioctl(sess->fd, ISCSISIGNAL, &sig)) { 468 perror("ISCSISIGNAL"); 469 } 470 471 if(sess->flags & SESS_DISCONNECT) { 472 val = 0; 473 if(ioctl(sess->fd, ISCSISTOP, &val)) { 474 perror("ISCSISTOP"); 475 } 476 sess->flags &= ~SESS_FULLFEATURE; 477 return T9; 478 } 479 else { 480 sess->flags |= SESS_INITIALLOGIN1; 481 } 482 return T8; 483 } 484 485 static int 486 handledDiscoveryResp(isess_t *sess, pdu_t *pp) 487 { 488 u_char *ptr; 489 int len, n; 490 491 debug_called(3); 492 493 len = pp->ds_len; 494 ptr = pp->ds; 495 while(len > 0) { 496 if(*ptr != 0) 497 printf("%s\n", ptr); 498 n = strlen((char *)ptr) + 1; 499 len -= n; 500 ptr += n; 501 } 502 return 0; 503 } 504 505 static int 506 doDiscovery(isess_t *sess) 507 { 508 pdu_t spp; 509 text_req_t *tp = (text_req_t *)&spp.ipdu.bhs; 510 511 debug_called(3); 512 513 bzero(&spp, sizeof(pdu_t)); 514 tp->cmd = ISCSI_TEXT_CMD /*| 0x40 */; // because of a bug in openiscsi-target 515 tp->F = 1; 516 tp->ttt = 0xffffffff; 517 addText(&spp, "SendTargets=All"); 518 return sendPDU(sess, &spp, handledDiscoveryResp); 519 } 520 521 static trans_t 522 doLogin(isess_t *sess) 523 { 524 isc_opt_t *op = sess->op; 525 int status, count; 526 527 debug_called(3); 528 529 if(op->chapSecret == NULL && op->tgtChapSecret == NULL) 530 /* 531 | don't need any security negotiation 532 | or in other words: we don't have any secrets to exchange 533 */ 534 sess->csg = LON_PHASE; 535 else 536 sess->csg = SN_PHASE; 537 538 if(sess->tsih) { 539 sess->tsih = 0; // XXX: no 'reconnect' yet 540 sess->flags &= ~SESS_NEGODONE; // XXX: KLUDGE 541 } 542 count = 10; // should be more than enough 543 do { 544 debug(3, "count=%d csg=%d", count, sess->csg); 545 status = loginPhase(sess); 546 if(count-- == 0) 547 // just in case we get into a loop 548 status = -1; 549 } while(status == 0 && (sess->csg != FF_PHASE)); 550 551 sess->flags &= ~SESS_INITIALLOGIN; 552 debug(3, "status=%d", status); 553 554 switch(status) { 555 case 0: // all is ok ... 556 sess->flags |= SESS_LOGGEDIN; 557 if(strcmp(sess->op->sessionType, "Discovery") == 0) 558 doDiscovery(sess); 559 return T5; 560 561 case 1: // redirect - temporary/permanent 562 /* 563 | start from scratch? 564 */ 565 sess->flags &= ~SESS_NEGODONE; 566 sess->flags |= (SESS_REDIRECT | SESS_INITIALLOGIN1); 567 syslog(LOG_DEBUG, "target sent REDIRECT"); 568 return T7; 569 570 case 2: // initiator terminal error 571 return 0; 572 case 3: // target terminal error -- could retry ... 573 sleep(5); 574 return T7; // lets try 575 default: 576 return 0; 577 } 578 } 579 580 static int 581 handleLogoutResp(isess_t *sess, pdu_t *pp) 582 { 583 if(sess->flags & SESS_DISCONNECT) 584 return 0; 585 return T13; 586 } 587 588 static trans_t 589 startLogout(isess_t *sess) 590 { 591 pdu_t spp; 592 logout_req_t *p = (logout_req_t *)&spp.ipdu.bhs; 593 594 bzero(&spp, sizeof(pdu_t)); 595 p->cmd = ISCSI_LOGOUT_CMD| 0x40; 596 p->reason = BIT(7) | 0; 597 p->CID = htons(1); 598 599 return sendPDU(sess, &spp, handleLogoutResp); 600 } 601 602 static trans_t 603 inLogout(isess_t *sess) 604 { 605 if(sess->flags & SESS_RECONNECT) 606 return T18; 607 return 0; 608 } 609 610 typedef enum { 611 S1=1, S2, S3, S4, S5, S6, S7, S8 612 } state_t; 613 614 #if 0 615 S1: FREE 616 S2: XPT_WAIT 617 S4: IN_LOGIN 618 S5: LOGGED_IN 619 S6: IN_LOGOUT 620 S7: LOGOUT_REQUESTED 621 S8: CLEANUP_WAIT 622 623 -------<-------------+ 624 +--------->/ S1 \<----+ | 625 T13| +->\ /<-+ \ | 626 | / ---+--- \ \ | 627 | / | T2 \ | | 628 | T8 | |T1 | | | 629 | | | / |T7 | 630 | | | / | | 631 | | | / | | 632 | | V / / | 633 | | ------- / / | 634 | | / S2 \ / | 635 | | \ / / | 636 | | ---+--- / | 637 | | |T4 / | 638 | | V / | T18 639 | | ------- / | 640 | | / S4 \ | 641 | | \ / | 642 | | ---+--- | T15 643 | | |T5 +--------+---------+ 644 | | | /T16+-----+------+ | 645 | | | / -+-----+--+ | | 646 | | | / / S7 \ |T12| | 647 | | | / +->\ /<-+ V V 648 | | | / / -+----- ------- 649 | | | / /T11 |T10 / S8 \ 650 | | V / / V +----+ \ / 651 | | ---+-+- ----+-- | ------- 652 | | / S5 \T9 / S6 \<+ ^ 653 | +-----\ /--->\ / T14 | 654 | ------- --+----+------+T17 655 +---------------------------+ 656 #endif 657 658 int 659 fsm(isc_opt_t *op) 660 { 661 state_t state; 662 isess_t *sess; 663 664 if((sess = calloc(1, sizeof(isess_t))) == NULL) { 665 // boy, is this a bad start ... 666 fprintf(stderr, "no memory!\n"); 667 return -1; 668 } 669 670 state = S1; 671 sess->op = op; 672 sess->fd = -1; 673 sess->soc = -1; 674 sess->target.address = strdup(op->targetAddress); 675 sess->target.port = op->port; 676 sess->target.pgt = op->targetPortalGroupTag; 677 678 sess->flags = SESS_INITIALLOGIN | SESS_INITIALLOGIN1; 679 680 do { 681 switch(state) { 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 default: 739 syslog(LOG_INFO, "unknown state %d", state); 740 return 0; 741 } 742 } while(1); 743 } 744