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