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
59typedef 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 */
72static trans_t
73tcpConnect(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
221int
222setOptions(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
267static trans_t
268startSession(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
319isess_t *currsess;
320
321static void
322trap(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
343static int
344doCAM(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	  bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_relsim) - sizeof(struct ccb_hdr));
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
395static trans_t
396supervise(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
495static int
496handledDiscoveryResp(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
515static int
516doDiscovery(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
531static trans_t
532doLogin(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
590static int
591handleLogoutResp(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
603static trans_t
604startLogout(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
617static trans_t
618inLogout(isess_t *sess)
619{
620     if(sess->flags & SESS_RECONNECT)
621	  return T18;
622     return 0;
623}
624
625typedef enum {
626     S1, S2, /*S3,*/ S4, S5, S6, S7, S8
627} state_t;
628
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
673int
674fsm(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}
757