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