fsm.c revision 211095
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: head/sbin/iscontrol/fsm.c 211095 2010-08-09 12:36:36Z des $");
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
62typedef 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 */
75static trans_t
76tcpConnect(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
224int
225setOptions(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
270static trans_t
271startSession(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
322isess_t *currsess;
323
324static void
325trap(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
346static int
347doCAM(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 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				      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
398static trans_t
399supervise(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
498static int
499handledDiscoveryResp(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
518static int
519doDiscovery(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
534static trans_t
535doLogin(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
593static int
594handleLogoutResp(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
606static trans_t
607startLogout(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
620static trans_t
621inLogout(isess_t *sess)
622{
623     if(sess->flags & SESS_RECONNECT)
624	  return T18;
625     return 0;
626}
627
628typedef enum {
629     S1, S2, /*S3,*/ S4, S5, S6, S7, S8
630} state_t;
631
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
676int
677fsm(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}
760