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