]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - sbin/iscontrol/fsm.c
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.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 #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 <dev/iscsi/initiator/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_initiator.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 int
347 doCAM(isess_t *sess)
348 {
349      char       pathstr[1024];
350      union ccb  *ccb;
351      int        i, n;
352
353      if(ioctl(sess->fd, ISCSIGETCAM, &sess->cam) != 0) {
354           syslog(LOG_WARNING, "ISCSIGETCAM failed: %d", errno);
355           return 0;
356      }
357      debug(1, "nluns=%d", sess->cam.target_nluns);
358      /*
359       | for now will do this for each lun ...
360       */
361      for(n = i = 0; i < sess->cam.target_nluns; i++) {
362           debug(2, "CAM path_id=%d target_id=%d",
363                 sess->cam.path_id, sess->cam.target_id);
364
365           sess->camdev = cam_open_btl(sess->cam.path_id, sess->cam.target_id,
366                                       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           if(cam_send_ccb(sess->camdev, ccb) < 0)
382                debug(2, "%s", cam_errbuf);
383           else
384           if((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
385                syslog(LOG_WARNING, "XPT_REL_SIMQ CCB failed");
386                // cam_error_print(sess->camdev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr);
387           }
388           else {
389                n++;
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      return n;
396 }
397
398 static trans_t
399 supervise(isess_t *sess)
400 {
401      int        sig, val;
402
403      debug_called(3);
404
405      if(strcmp(sess->op->sessionType, "Discovery") == 0) {
406           sess->flags |= SESS_DISCONNECT;
407           return T9;
408      }
409
410      if(vflag)
411           printf("ready to go scsi\n");
412
413      if(setOptions(sess, SESS_FULLFEATURE) != 0)
414           return 0; // failure
415
416      if((sess->flags & SESS_FULLFEATURE) == 0) {
417           if(daemon(0, 1) != 0) {
418                perror("daemon");
419                exit(1);
420           }
421           if(sess->op->pidfile != NULL) {
422                FILE *pidf;
423
424                pidf = fopen(sess->op->pidfile, "w");
425                if(pidf != NULL) { 
426                     fprintf(pidf, "%d\n", getpid());
427                     fclose(pidf);
428                }
429           }
430           openlog("iscontrol", LOG_CONS|LOG_PERROR|LOG_PID|LOG_NDELAY, LOG_KERN);
431           syslog(LOG_INFO, "running");
432
433           currsess = sess;
434           if(ioctl(sess->fd, ISCSISTART)) {
435                perror("ISCSISTART");
436                return -1;
437           }
438           if(doCAM(sess) == 0) {
439                syslog(LOG_WARNING, "no device found");
440                ioctl(sess->fd, ISCSISTOP);
441                return T15;
442           }
443
444      }
445      else {
446           if(ioctl(sess->fd, ISCSIRESTART)) {
447                perror("ISCSIRESTART");
448                return -1;
449           }
450      }
451           
452      signal(SIGINT, trap);
453      signal(SIGHUP, trap);
454      signal(SIGTERM, trap);
455
456      sig = SIGUSR1;
457      signal(sig, trap);
458      if(ioctl(sess->fd, ISCSISIGNAL, &sig)) {
459           perror("ISCSISIGNAL");
460           return -1;
461      }
462      sess->flags |= SESS_FULLFEATURE;
463
464      sess->flags &= ~(SESS_REDIRECT | SESS_RECONNECT);
465      if(vflag)
466           printf("iscontrol: supervise starting main loop\n");
467      /*
468       | the main loop - actually do nothing
469       | all the work is done inside the kernel
470       */
471      while((sess->flags & (SESS_REDIRECT|SESS_RECONNECT|SESS_DISCONNECT)) == 0) {
472           // do something?
473           // like sending a nop_out?
474           sleep(60);
475      }
476      printf("iscontrol: supervise going down\n");
477      syslog(LOG_INFO, "sess flags=%x", sess->flags);
478
479      sig = 0;
480      if(ioctl(sess->fd, ISCSISIGNAL, &sig)) {
481           perror("ISCSISIGNAL");
482      }
483
484      if(sess->flags & SESS_DISCONNECT) {
485           sess->flags &= ~SESS_FULLFEATURE;
486           return T9;
487      } 
488      else {
489           val = 0;
490           if(ioctl(sess->fd, ISCSISTOP, &val)) {
491                perror("ISCSISTOP");
492           }
493           sess->flags |= SESS_INITIALLOGIN1;
494      }
495      return T8;
496 }
497
498 static int
499 handledDiscoveryResp(isess_t *sess, pdu_t *pp)
500 {
501      u_char     *ptr;
502      int        len, n;
503
504      debug_called(3);
505
506      len = pp->ds_len;
507      ptr = pp->ds_addr;
508      while(len > 0) {
509           if(*ptr != 0)
510                printf("%s\n", ptr);
511           n = strlen((char *)ptr) + 1;
512           len -= n;
513           ptr += n;
514      }
515      return 0;
516 }
517
518 static int
519 doDiscovery(isess_t *sess)
520 {
521      pdu_t      spp;
522      text_req_t *tp = (text_req_t *)&spp.ipdu.bhs;
523
524      debug_called(3);
525
526      bzero(&spp, sizeof(pdu_t));
527      tp->cmd = ISCSI_TEXT_CMD /*| 0x40 */; // because of a bug in openiscsi-target
528      tp->F = 1;
529      tp->ttt = 0xffffffff;
530      addText(&spp, "SendTargets=All");
531      return sendPDU(sess, &spp, handledDiscoveryResp);
532 }
533
534 static trans_t
535 doLogin(isess_t *sess)
536 {
537      isc_opt_t  *op = sess->op;
538      int        status, count;
539
540      debug_called(3);
541
542      if(op->chapSecret == NULL && op->tgtChapSecret == NULL)
543           /*
544            | don't need any security negotiation
545            | or in other words: we don't have any secrets to exchange
546            */
547           sess->csg = LON_PHASE;
548      else
549           sess->csg = SN_PHASE;
550
551      if(sess->tsih) {
552           sess->tsih = 0;       // XXX: no 'reconnect' yet
553           sess->flags &= ~SESS_NEGODONE; // XXX: KLUDGE
554      }
555      count = 10; // should be more than enough
556      do {
557           debug(3, "count=%d csg=%d", count, sess->csg);
558           status = loginPhase(sess);
559           if(count-- == 0)
560                // just in case we get into a loop
561                status = -1;
562      } while(status == 0 && (sess->csg != FF_PHASE));
563
564      sess->flags &= ~SESS_INITIALLOGIN;
565      debug(3, "status=%d", status);
566
567      switch(status) {
568      case 0: // all is ok ...
569           sess->flags |= SESS_LOGGEDIN;
570           if(strcmp(sess->op->sessionType, "Discovery") == 0)
571                doDiscovery(sess);
572           return T5;
573
574      case 1:    // redirect - temporary/permanent
575           /*
576            | start from scratch?
577            */
578           sess->flags &= ~SESS_NEGODONE;
579           sess->flags |= (SESS_REDIRECT | SESS_INITIALLOGIN1);
580           syslog(LOG_DEBUG, "target sent REDIRECT");
581           return T7;
582
583      case 2: // initiator terminal error
584           return 0;
585      case 3: // target terminal error -- could retry ...
586           sleep(5);
587           return T7; // lets try
588      default:
589           return 0;
590      }
591 }
592
593 static int
594 handleLogoutResp(isess_t *sess, pdu_t *pp)
595 {
596      if(sess->flags & SESS_DISCONNECT) {
597           int val = 0;
598           if(ioctl(sess->fd, ISCSISTOP, &val)) {
599                perror("ISCSISTOP");
600           }
601           return 0;
602      }
603      return T13;
604 }
605
606 static trans_t
607 startLogout(isess_t *sess)
608 {
609      pdu_t      spp;
610      logout_req_t *p = (logout_req_t *)&spp.ipdu.bhs;
611
612      bzero(&spp, sizeof(pdu_t));
613      p->cmd = ISCSI_LOGOUT_CMD| 0x40;
614      p->reason = BIT(7) | 0;
615      p->CID = htons(1);
616
617      return sendPDU(sess, &spp, handleLogoutResp);
618 }
619
620 static trans_t
621 inLogout(isess_t *sess)
622 {
623      if(sess->flags & SESS_RECONNECT)
624           return T18;
625      return 0;
626 }
627
628 typedef enum {
629      S1, S2, /*S3,*/ S4, S5, S6, S7, S8
630 } state_t;
631 \f
632 /**
633       S1: FREE
634       S2: XPT_WAIT
635       S4: IN_LOGIN
636       S5: LOGGED_IN
637       S6: IN_LOGOUT
638       S7: LOGOUT_REQUESTED
639       S8: CLEANUP_WAIT
640
641                      -------<-------------+
642          +--------->/ S1    \<----+       |
643       T13|       +->\       /<-+   \      |
644          |      /    ---+---    \   \     |
645          |     /        |     T2 \   |    |
646          |  T8 |        |T1       |  |    |
647          |     |        |        /   |T7  |
648          |     |        |       /    |    |
649          |     |        |      /     |    |
650          |     |        V     /     /     |
651          |     |     ------- /     /      |
652          |     |    / S2    \     /       |
653          |     |    \       /    /        |
654          |     |     ---+---    /         |
655          |     |        |T4    /          |
656          |     |        V     /           | T18
657          |     |     ------- /            |
658          |     |    / S4    \             |
659          |     |    \       /             |
660          |     |     ---+---              |         T15
661          |     |        |T5      +--------+---------+
662          |     |        |       /T16+-----+------+  |
663          |     |        |      /   -+-----+--+   |  |
664          |     |        |     /   /  S7   \  |T12|  |
665          |     |        |    / +->\       /<-+   V  V
666          |     |        |   / /    -+-----       -------
667          |     |        |  / /T11   |T10        /  S8   \
668          |     |        V / /       V  +----+   \       /
669          |     |      ---+-+-      ----+--  |    -------
670          |     |     / S5    \T9  / S6    \<+    ^
671          |     +-----\       /--->\       / T14  |
672          |            -------      --+----+------+T17
673          +---------------------------+
674 */
675
676 int
677 fsm(isc_opt_t *op)
678 {
679      state_t    state;
680      isess_t    *sess;
681
682      if((sess = calloc(1, sizeof(isess_t))) == NULL) {
683           // boy, is this a bad start ...
684           fprintf(stderr, "no memory!\n");
685           return -1;
686      }
687
688      state = S1;
689      sess->op = op;
690      sess->fd = -1;
691      sess->soc = -1;
692      sess->target.address = strdup(op->targetAddress);
693      sess->target.port = op->port;
694      sess->target.pgt = op->targetPortalGroupTag;
695
696      sess->flags = SESS_INITIALLOGIN | SESS_INITIALLOGIN1;
697
698      do {
699           switch(state) {
700
701           case S1:
702                switch(tcpConnect(sess)) {
703                case T1: state = S2; break;
704                default: state = S8; break;
705                }
706                break;
707
708           case S2:
709                switch(startSession(sess)) {
710                case T2: state = S1; break;
711                case T4: state = S4; break;
712                default: state = S8; break;
713                }
714                break;
715
716           case S4:
717                switch(doLogin(sess)) {
718                case T7:  state = S1; break;
719                case T5:  state = S5; break;
720                default: state = S8; break;
721                }
722                break;
723
724           case S5:
725                switch(supervise(sess)) {
726                case T8:  state = S1; break;
727                case T9:  state = S6; break;
728                case T11: state = S7; break;
729                case T15: state = S8; break;
730                default: state = S8; break;
731                }
732                break;
733
734           case S6:
735                switch(startLogout(sess)) {
736                case T13: state = S1; break;
737                case T14: state = S6; break;
738                case T16: state = S8; break;
739                default: state = S8; break;
740                }
741                break;
742           
743           case S7: 
744                switch(inLogout(sess)) {
745                case T18: state = S1; break;
746                case T10: state = S6; break;
747                case T12: state = S7; break;
748                case T16: state = S8; break;
749                default: state = S8; break;
750                }
751                break;
752
753           case S8:
754                // maybe do some clean up?
755                syslog(LOG_INFO, "terminated");
756                return 0;
757           }
758      } while(1);
759 }