]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/iscontrol/fsm.c
MFV r326007: less v529.
[FreeBSD/FreeBSD.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 EHOSTUNREACH:
203      case ENETUNREACH:
204      case ETIMEDOUT:
205           if((sess->flags & SESS_REDIRECT) == 0) {
206                if(strcmp(op->targetAddress, sess->target.address) != 0) {
207                     syslog(LOG_INFO, "reconnecting to original target address");
208                     free(op->targetAddress);
209                     op->targetAddress           = sess->target.address;
210                     op->port                    = sess->target.port;
211                     op->targetPortalGroupTag    = sess->target.pgt;
212                     return T1;
213                }
214           }
215           sleep(5); // for now ...
216           return T1;
217      default:
218           return 0; // terminal error
219      }
220 }
221
222 int
223 setOptions(isess_t *sess, int flag)
224 {
225      isc_opt_t  oop;
226      char       *sep;
227
228      debug_called(3);
229
230      bzero(&oop, sizeof(isc_opt_t));
231
232      if((flag & SESS_FULLFEATURE) == 0) {
233           oop.initiatorName     = sess->op->initiatorName;
234           oop.targetAddress     = sess->op->targetAddress;
235           if(sess->op->targetName != 0)
236                oop.targetName = sess->op->targetName;
237           
238           oop.maxRecvDataSegmentLength = sess->op->maxRecvDataSegmentLength;
239           oop.maxXmitDataSegmentLength = sess->op->maxXmitDataSegmentLength; // XXX:
240           oop.maxBurstLength = sess->op->maxBurstLength;
241           oop.maxluns = sess->op->maxluns;
242      }
243      else {
244           /*
245            | turn on digestion only after login
246            */
247           if(sess->op->headerDigest != NULL) {
248                sep = strchr(sess->op->headerDigest, ',');
249                if(sep == NULL)
250                     oop.headerDigest = sess->op->headerDigest;
251                debug(1, "oop.headerDigest=%s", oop.headerDigest);
252           }
253           if(sess->op->dataDigest != NULL) {
254                sep = strchr(sess->op->dataDigest, ',');
255                if(sep == NULL)
256                     oop.dataDigest = sess->op->dataDigest;
257                debug(1, "oop.dataDigest=%s", oop.dataDigest);
258           }
259      }
260
261      if(ioctl(sess->fd, ISCSISETOPT, &oop)) {
262           perror("ISCSISETOPT");
263           return -1;
264      }
265      return 0;
266 }
267
268 static trans_t
269 startSession(isess_t *sess)
270 {
271      
272      int        n, fd, nfd;
273      char       *dev;
274
275      debug_called(3);
276
277      if((sess->flags & SESS_CONNECTED) == 0) {
278           return T2;
279      }
280      if(sess->fd == -1) {
281           fd = open(iscsidev, O_RDWR);
282           if(fd < 0) {
283                perror(iscsidev);
284                return 0;
285           }
286           {
287                // XXX: this has to go
288                size_t   n;
289                n = sizeof(sess->isid);
290                if(sysctlbyname("net.iscsi_initiator.isid", (void *)sess->isid, (size_t *)&n, 0, 0) != 0)
291                     perror("sysctlbyname");
292           }
293           if(ioctl(fd, ISCSISETSES, &n)) {
294                perror("ISCSISETSES");
295                return 0;
296           }
297           asprintf(&dev, "%s%d", iscsidev, n);
298           nfd = open(dev, O_RDWR);
299           if(nfd < 0) {
300                perror(dev);
301                free(dev);
302                return 0;
303           }
304           free(dev);
305           close(fd);
306           sess->fd = nfd;
307
308           if(setOptions(sess, 0) != 0)
309                return -1;
310      }
311
312      if(ioctl(sess->fd, ISCSISETSOC, &sess->soc)) {
313           perror("ISCSISETSOC");
314           return 0;
315      }
316
317      return T4;
318 }
319
320 isess_t *currsess;
321
322 static void
323 trap(int sig)
324 {
325      syslog(LOG_NOTICE, "trapped signal %d", sig);
326      fprintf(stderr, "trapped signal %d\n", sig);
327
328      switch(sig) {
329      case SIGHUP:
330           currsess->flags |= SESS_DISCONNECT;
331           break;
332
333      case SIGUSR1:
334           currsess->flags |= SESS_RECONNECT;
335           break;
336
337      case SIGINT: 
338      case SIGTERM:
339      default:
340           return; // ignore
341      }
342 }
343
344 static int
345 doCAM(isess_t *sess)
346 {
347      char       pathstr[1024];
348      union ccb  *ccb;
349      int        i, n;
350
351      if(ioctl(sess->fd, ISCSIGETCAM, &sess->cam) != 0) {
352           syslog(LOG_WARNING, "ISCSIGETCAM failed: %d", errno);
353           return 0;
354      }
355      debug(1, "nluns=%d", sess->cam.target_nluns);
356      /*
357       | for now will do this for each lun ...
358       */
359      for(n = i = 0; i < sess->cam.target_nluns; i++) {
360           debug(2, "CAM path_id=%d target_id=%d",
361                 sess->cam.path_id, sess->cam.target_id);
362
363           sess->camdev = cam_open_btl(sess->cam.path_id, sess->cam.target_id,
364                                       i, O_RDWR, NULL);
365           if(sess->camdev == NULL) {
366                //syslog(LOG_WARNING, "%s", cam_errbuf);
367                debug(3, "%s", cam_errbuf);
368                continue;
369           }
370
371           cam_path_string(sess->camdev, pathstr, sizeof(pathstr));
372           debug(2, "pathstr=%s", pathstr);
373
374           ccb = cam_getccb(sess->camdev);
375           CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->crs);
376           ccb->ccb_h.func_code = XPT_REL_SIMQ;
377           ccb->crs.release_flags = RELSIM_ADJUST_OPENINGS;
378           ccb->crs.openings = sess->op->tags;
379           if(cam_send_ccb(sess->camdev, ccb) < 0)
380                debug(2, "%s", cam_errbuf);
381           else
382           if((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
383                syslog(LOG_WARNING, "XPT_REL_SIMQ CCB failed");
384                // cam_error_print(sess->camdev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr);
385           }
386           else {
387                n++;
388                syslog(LOG_INFO, "%s tagged openings now %d\n", pathstr, ccb->crs.openings);
389           }
390           cam_freeccb(ccb);
391           cam_close_device(sess->camdev);
392      }
393      return n;
394 }
395
396 static trans_t
397 supervise(isess_t *sess)
398 {
399      int        sig, val;
400
401      debug_called(3);
402
403      if(strcmp(sess->op->sessionType, "Discovery") == 0) {
404           sess->flags |= SESS_DISCONNECT;
405           return T9;
406      }
407
408      if(vflag)
409           printf("ready to go scsi\n");
410
411      if(setOptions(sess, SESS_FULLFEATURE) != 0)
412           return 0; // failure
413
414      if((sess->flags & SESS_FULLFEATURE) == 0) {
415           if(daemon(0, 1) != 0) {
416                perror("daemon");
417                exit(1);
418           }
419           if(sess->op->pidfile != NULL) {
420                FILE *pidf;
421
422                pidf = fopen(sess->op->pidfile, "w");
423                if(pidf != NULL) { 
424                     fprintf(pidf, "%d\n", getpid());
425                     fclose(pidf);
426                }
427           }
428           openlog("iscontrol", LOG_CONS|LOG_PERROR|LOG_PID|LOG_NDELAY, LOG_KERN);
429           syslog(LOG_INFO, "running");
430
431           currsess = sess;
432           if(ioctl(sess->fd, ISCSISTART)) {
433                perror("ISCSISTART");
434                return -1;
435           }
436           if(doCAM(sess) == 0) {
437                syslog(LOG_WARNING, "no device found");
438                ioctl(sess->fd, ISCSISTOP);
439                return T15;
440           }
441
442      }
443      else {
444           if(ioctl(sess->fd, ISCSIRESTART)) {
445                perror("ISCSIRESTART");
446                return -1;
447           }
448      }
449           
450      signal(SIGINT, trap);
451      signal(SIGHUP, trap);
452      signal(SIGTERM, trap);
453
454      sig = SIGUSR1;
455      signal(sig, trap);
456      if(ioctl(sess->fd, ISCSISIGNAL, &sig)) {
457           perror("ISCSISIGNAL");
458           return -1;
459      }
460      sess->flags |= SESS_FULLFEATURE;
461
462      sess->flags &= ~(SESS_REDIRECT | SESS_RECONNECT);
463      if(vflag)
464           printf("iscontrol: supervise starting main loop\n");
465      /*
466       | the main loop - actually do nothing
467       | all the work is done inside the kernel
468       */
469      while((sess->flags & (SESS_REDIRECT|SESS_RECONNECT|SESS_DISCONNECT)) == 0) {
470           // do something?
471           // like sending a nop_out?
472           sleep(60);
473      }
474      printf("iscontrol: supervise going down\n");
475      syslog(LOG_INFO, "sess flags=%x", sess->flags);
476
477      sig = 0;
478      if(ioctl(sess->fd, ISCSISIGNAL, &sig)) {
479           perror("ISCSISIGNAL");
480      }
481
482      if(sess->flags & SESS_DISCONNECT) {
483           sess->flags &= ~SESS_FULLFEATURE;
484           return T9;
485      } 
486      else {
487           val = 0;
488           if(ioctl(sess->fd, ISCSISTOP, &val)) {
489                perror("ISCSISTOP");
490           }
491           sess->flags |= SESS_INITIALLOGIN1;
492      }
493      return T8;
494 }
495
496 static int
497 handledDiscoveryResp(isess_t *sess, pdu_t *pp)
498 {
499      u_char     *ptr;
500      int        len, n;
501
502      debug_called(3);
503
504      len = pp->ds_len;
505      ptr = pp->ds_addr;
506      while(len > 0) {
507           if(*ptr != 0)
508                printf("%s\n", ptr);
509           n = strlen((char *)ptr) + 1;
510           len -= n;
511           ptr += n;
512      }
513      return 0;
514 }
515
516 static int
517 doDiscovery(isess_t *sess)
518 {
519      pdu_t      spp;
520      text_req_t *tp = (text_req_t *)&spp.ipdu.bhs;
521
522      debug_called(3);
523
524      bzero(&spp, sizeof(pdu_t));
525      tp->cmd = ISCSI_TEXT_CMD /*| 0x40 */; // because of a bug in openiscsi-target
526      tp->F = 1;
527      tp->ttt = 0xffffffff;
528      addText(&spp, "SendTargets=All");
529      return sendPDU(sess, &spp, handledDiscoveryResp);
530 }
531
532 static trans_t
533 doLogin(isess_t *sess)
534 {
535      isc_opt_t  *op = sess->op;
536      int        status, count;
537
538      debug_called(3);
539
540      if(op->chapSecret == NULL && op->tgtChapSecret == NULL)
541           /*
542            | don't need any security negotiation
543            | or in other words: we don't have any secrets to exchange
544            */
545           sess->csg = LON_PHASE;
546      else
547           sess->csg = SN_PHASE;
548
549      if(sess->tsih) {
550           sess->tsih = 0;       // XXX: no 'reconnect' yet
551           sess->flags &= ~SESS_NEGODONE; // XXX: KLUDGE
552      }
553      count = 10; // should be more than enough
554      do {
555           debug(3, "count=%d csg=%d", count, sess->csg);
556           status = loginPhase(sess);
557           if(count-- == 0)
558                // just in case we get into a loop
559                status = -1;
560      } while(status == 0 && (sess->csg != FF_PHASE));
561
562      sess->flags &= ~SESS_INITIALLOGIN;
563      debug(3, "status=%d", status);
564
565      switch(status) {
566      case 0: // all is ok ...
567           sess->flags |= SESS_LOGGEDIN;
568           if(strcmp(sess->op->sessionType, "Discovery") == 0)
569                doDiscovery(sess);
570           return T5;
571
572      case 1:    // redirect - temporary/permanent
573           /*
574            | start from scratch?
575            */
576           sess->flags &= ~SESS_NEGODONE;
577           sess->flags |= (SESS_REDIRECT | SESS_INITIALLOGIN1);
578           syslog(LOG_DEBUG, "target sent REDIRECT");
579           return T7;
580
581      case 2: // initiator terminal error
582           return 0;
583      case 3: // target terminal error -- could retry ...
584           sleep(5);
585           return T7; // lets try
586      default:
587           return 0;
588      }
589 }
590
591 static int
592 handleLogoutResp(isess_t *sess, pdu_t *pp)
593 {
594      if(sess->flags & SESS_DISCONNECT) {
595           int val = 0;
596           if(ioctl(sess->fd, ISCSISTOP, &val)) {
597                perror("ISCSISTOP");
598           }
599           return 0;
600      }
601      return T13;
602 }
603
604 static trans_t
605 startLogout(isess_t *sess)
606 {
607      pdu_t      spp;
608      logout_req_t *p = (logout_req_t *)&spp.ipdu.bhs;
609
610      bzero(&spp, sizeof(pdu_t));
611      p->cmd = ISCSI_LOGOUT_CMD| 0x40;
612      p->reason = BIT(7) | 0;
613      p->CID = htons(1);
614
615      return sendPDU(sess, &spp, handleLogoutResp);
616 }
617
618 static trans_t
619 inLogout(isess_t *sess)
620 {
621      if(sess->flags & SESS_RECONNECT)
622           return T18;
623      return 0;
624 }
625
626 typedef enum {
627      S1, S2, /*S3,*/ S4, S5, S6, S7, S8
628 } state_t;
629 \f
630 /**
631       S1: FREE
632       S2: XPT_WAIT
633       S4: IN_LOGIN
634       S5: LOGGED_IN
635       S6: IN_LOGOUT
636       S7: LOGOUT_REQUESTED
637       S8: CLEANUP_WAIT
638
639                      -------<-------------+
640          +--------->/ S1    \<----+       |
641       T13|       +->\       /<-+   \      |
642          |      /    ---+---    \   \     |
643          |     /        |     T2 \   |    |
644          |  T8 |        |T1       |  |    |
645          |     |        |        /   |T7  |
646          |     |        |       /    |    |
647          |     |        |      /     |    |
648          |     |        V     /     /     |
649          |     |     ------- /     /      |
650          |     |    / S2    \     /       |
651          |     |    \       /    /        |
652          |     |     ---+---    /         |
653          |     |        |T4    /          |
654          |     |        V     /           | T18
655          |     |     ------- /            |
656          |     |    / S4    \             |
657          |     |    \       /             |
658          |     |     ---+---              |         T15
659          |     |        |T5      +--------+---------+
660          |     |        |       /T16+-----+------+  |
661          |     |        |      /   -+-----+--+   |  |
662          |     |        |     /   /  S7   \  |T12|  |
663          |     |        |    / +->\       /<-+   V  V
664          |     |        |   / /    -+-----       -------
665          |     |        |  / /T11   |T10        /  S8   \
666          |     |        V / /       V  +----+   \       /
667          |     |      ---+-+-      ----+--  |    -------
668          |     |     / S5    \T9  / S6    \<+    ^
669          |     +-----\       /--->\       / T14  |
670          |            -------      --+----+------+T17
671          +---------------------------+
672 */
673
674 int
675 fsm(isc_opt_t *op)
676 {
677      state_t    state;
678      isess_t    *sess;
679
680      if((sess = calloc(1, sizeof(isess_t))) == NULL) {
681           // boy, is this a bad start ...
682           fprintf(stderr, "no memory!\n");
683           return -1;
684      }
685
686      state = S1;
687      sess->op = op;
688      sess->fd = -1;
689      sess->soc = -1;
690      sess->target.address = strdup(op->targetAddress);
691      sess->target.port = op->port;
692      sess->target.pgt = op->targetPortalGroupTag;
693
694      sess->flags = SESS_INITIALLOGIN | SESS_INITIALLOGIN1;
695
696      do {
697           switch(state) {
698
699           case S1:
700                switch(tcpConnect(sess)) {
701                case T1: state = S2; break;
702                default: state = S8; break;
703                }
704                break;
705
706           case S2:
707                switch(startSession(sess)) {
708                case T2: state = S1; break;
709                case T4: state = S4; break;
710                default: state = S8; break;
711                }
712                break;
713
714           case S4:
715                switch(doLogin(sess)) {
716                case T7:  state = S1; break;
717                case T5:  state = S5; break;
718                default: state = S8; break;
719                }
720                break;
721
722           case S5:
723                switch(supervise(sess)) {
724                case T8:  state = S1; break;
725                case T9:  state = S6; break;
726                case T11: state = S7; break;
727                case T15: state = S8; break;
728                default: state = S8; break;
729                }
730                break;
731
732           case S6:
733                switch(startLogout(sess)) {
734                case T13: state = S1; break;
735                case T14: state = S6; break;
736                case T16: state = S8; break;
737                default: state = S8; break;
738                }
739                break;
740           
741           case S7: 
742                switch(inLogout(sess)) {
743                case T18: state = S1; break;
744                case T10: state = S6; break;
745                case T12: state = S7; break;
746                case T16: state = S8; break;
747                default: state = S8; break;
748                }
749                break;
750
751           case S8:
752                // maybe do some clean up?
753                syslog(LOG_INFO, "terminated");
754                return 0;
755           }
756      } while(1);
757 }