1171568Sscottl/*-
2211095Sdes * Copyright (c) 2005-2010 Daniel Braniss <danny@cs.huji.ac.il>
3171568Sscottl * All rights reserved.
4171568Sscottl *
5171568Sscottl * Redistribution and use in source and binary forms, with or without
6171568Sscottl * modification, are permitted provided that the following conditions
7171568Sscottl * are met:
8171568Sscottl * 1. Redistributions of source code must retain the above copyright
9171568Sscottl *    notice, this list of conditions and the following disclaimer.
10171568Sscottl * 2. Redistributions in binary form must reproduce the above copyright
11171568Sscottl *    notice, this list of conditions and the following disclaimer in the
12171568Sscottl *    documentation and/or other materials provided with the distribution.
13171568Sscottl *
14171568Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15171568Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16171568Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17171568Sscottl * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18171568Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19171568Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20171568Sscottl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21171568Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22171568Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23171568Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24171568Sscottl * SUCH DAMAGE.
25171568Sscottl *
26171568Sscottl */
27171568Sscottl
28171568Sscottl/*
29171568Sscottl | $Id: fsm.c,v 2.8 2007/05/19 16:34:21 danny Exp danny $
30171568Sscottl */
31171568Sscottl
32171568Sscottl#include <sys/cdefs.h>
33171568Sscottl__FBSDID("$FreeBSD$");
34171568Sscottl
35171568Sscottl#include <sys/param.h>
36171568Sscottl#include <sys/types.h>
37171568Sscottl#include <sys/socket.h>
38171568Sscottl#include <sys/sysctl.h>
39171568Sscottl
40171568Sscottl#include <netinet/in.h>
41171568Sscottl#include <netinet/tcp.h>
42171568Sscottl#include <arpa/inet.h>
43171568Sscottl#include <sys/ioctl.h>
44171568Sscottl#include <netdb.h>
45171568Sscottl#include <stdlib.h>
46171568Sscottl#include <unistd.h>
47171568Sscottl#include <stdio.h>
48171568Sscottl#include <string.h>
49171568Sscottl#include <errno.h>
50171568Sscottl#include <fcntl.h>
51171568Sscottl#include <time.h>
52171568Sscottl#include <syslog.h>
53171568Sscottl#include <stdarg.h>
54171568Sscottl#include <camlib.h>
55171568Sscottl
56211095Sdes#include <dev/iscsi/initiator/iscsi.h>
57171568Sscottl#include "iscontrol.h"
58171568Sscottl
59171568Sscottltypedef enum {
60171568Sscottl     T1 = 1,
61171568Sscottl     T2, /*T3,*/ T4, T5, /*T6,*/ T7, T8, T9,
62171568Sscottl     T10, T11, T12, T13, T14, T15, T16, T18
63171568Sscottl} trans_t;
64171568Sscottl
65185289Sscottl/*
66185289Sscottl | now supports IPV6
67185289Sscottl | thanks to:
68185289Sscottl |	Hajimu UMEMOTO @ Internet Mutual Aid Society Yokohama, Japan
69185289Sscottl |	ume@mahoroba.org  ume@{,jp.}FreeBSD.org
70185289Sscottl |	http://www.imasy.org/~ume/
71185289Sscottl */
72171568Sscottlstatic trans_t
73171568SscottltcpConnect(isess_t *sess)
74171568Sscottl{
75171568Sscottl     isc_opt_t *op = sess->op;
76185289Sscottl     int	val, sv_errno, soc;
77185289Sscottl     struct     addrinfo *res, *res0, hints;
78185289Sscottl     char	pbuf[10];
79171568Sscottl
80171568Sscottl     debug_called(3);
81171568Sscottl     if(sess->flags & (SESS_RECONNECT|SESS_REDIRECT)) {
82171568Sscottl	  syslog(LOG_INFO, "%s", (sess->flags & SESS_RECONNECT)
83171568Sscottl		 ? "Reconnect": "Redirected");
84171568Sscottl
85185289Sscottl	  debug(1, "%s", (sess->flags & SESS_RECONNECT) ? "Reconnect": "Redirected");
86171568Sscottl	  shutdown(sess->soc, SHUT_RDWR);
87171568Sscottl	  //close(sess->soc);
88171568Sscottl	  sess->soc = -1;
89171568Sscottl
90171568Sscottl	  sess->flags &= ~SESS_CONNECTED;
91171568Sscottl	  if(sess->flags & SESS_REDIRECT) {
92185289Sscottl	       sess->redirect_cnt++;
93171568Sscottl	       sess->flags |= SESS_RECONNECT;
94185289Sscottl	  } else
95185289Sscottl	       sleep(2); // XXX: actually should be ?
96185289Sscottl#ifdef notyet
97185289Sscottl	  {
98185289Sscottl	       time_t	sec;
99211095Sdes	       // make sure we are not in a loop
100211095Sdes	       // XXX: this code has to be tested
101211095Sdes	       sec = time(0) - sess->reconnect_time;
102211095Sdes	       if(sec > (5*60)) {
103211095Sdes		    // if we've been connected for more that 5 minutes
104211095Sdes		    // then just reconnect
105211095Sdes		    sess->reconnect_time = sec;
106211095Sdes		    sess->reconnect_cnt1 = 0;
107171568Sscottl	       }
108211095Sdes	       else {
109211095Sdes		    //
110211095Sdes		    sess->reconnect_cnt1++;
111211095Sdes		    if((sec / sess->reconnect_cnt1) < 2) {
112211095Sdes			 // if less that 2 seconds from the last reconnect
113211095Sdes			 // we are most probably looping
114211095Sdes			 syslog(LOG_CRIT, "too many reconnects %d", sess->reconnect_cnt1);
115211095Sdes			 return 0;
116211095Sdes		    }
117211095Sdes	       }
118171568Sscottl	  }
119185289Sscottl#endif
120171568Sscottl	  sess->reconnect_cnt++;
121171568Sscottl     }
122171568Sscottl
123185289Sscottl     snprintf(pbuf, sizeof(pbuf), "%d", op->port);
124171568Sscottl     memset(&hints, 0, sizeof(hints));
125185289Sscottl     hints.ai_family	= PF_UNSPEC;
126171568Sscottl     hints.ai_socktype	= SOCK_STREAM;
127185289Sscottl     debug(1, "targetAddress=%s port=%d", op->targetAddress, op->port);
128185289Sscottl     if((val = getaddrinfo(op->targetAddress, pbuf, &hints, &res0)) != 0) {
129171568Sscottl          fprintf(stderr, "getaddrinfo(%s): %s\n", op->targetAddress, gai_strerror(val));
130171568Sscottl          return 0;
131171568Sscottl     }
132185289Sscottl     sess->flags &= ~SESS_CONNECTED;
133185289Sscottl     sv_errno = 0;
134185289Sscottl     soc = -1;
135185289Sscottl     for(res = res0; res; res = res->ai_next) {
136185289Sscottl	  soc = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
137185289Sscottl	  if (soc == -1)
138185289Sscottl	       continue;
139171568Sscottl
140211095Sdes	  // from Patrick.Guelat@imp.ch:
141211095Sdes	  // iscontrol can be called without waiting for the socket entry to time out
142211095Sdes	  val = 1;
143185289Sscottl	  if(setsockopt(soc, SOL_SOCKET, SO_REUSEADDR, &val, (socklen_t)sizeof(val)) < 0) {
144211095Sdes	       fprintf(stderr, "Cannot set socket SO_REUSEADDR %d: %s\n\n",
145211095Sdes		       errno, strerror(errno));
146211095Sdes	  }
147171568Sscottl
148185289Sscottl	  if(connect(soc, res->ai_addr, res->ai_addrlen) == 0)
149185289Sscottl	       break;
150185289Sscottl	  sv_errno = errno;
151185289Sscottl	  close(soc);
152185289Sscottl	  soc = -1;
153185289Sscottl     }
154185289Sscottl     freeaddrinfo(res0);
155185289Sscottl     if(soc != -1) {
156185289Sscottl	  sess->soc = soc;
157171568Sscottl
158171568Sscottl#if 0
159171568Sscottl	  struct	timeval timeout;
160171568Sscottl
161171568Sscottl	  val = 1;
162171568Sscottl	  if(setsockopt(sess->soc, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0)
163171568Sscottl	       fprintf(stderr, "Cannot set socket KEEPALIVE option err=%d %s\n",
164171568Sscottl		       errno, strerror(errno));
165171568Sscottl
166171568Sscottl	  if(setsockopt(sess->soc, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) < 0)
167171568Sscottl	       fprintf(stderr, "Cannot set socket NO delay option err=%d %s\n",
168171568Sscottl		       errno, strerror(errno));
169171568Sscottl
170171568Sscottl	  timeout.tv_sec = 10;
171171568Sscottl	  timeout.tv_usec = 0;
172171568Sscottl	  if((setsockopt(sess->soc, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0)
173171568Sscottl	     || (setsockopt(sess->soc, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0)) {
174171568Sscottl	       fprintf(stderr, "Cannot set socket timeout to %ld err=%d %s\n",
175171568Sscottl		       timeout.tv_sec, errno, strerror(errno));
176171568Sscottl	  }
177171568Sscottl#endif
178171568Sscottl#ifdef CURIOUS
179171568Sscottl	  {
180171568Sscottl	       int len = sizeof(val);
181171568Sscottl	       if(getsockopt(sess->soc, SOL_SOCKET, SO_SNDBUF, &val, &len) == 0)
182171568Sscottl		    fprintf(stderr, "was: SO_SNDBUF=%dK\n", val/1024);
183171568Sscottl	  }
184171568Sscottl#endif
185171568Sscottl	  if(sess->op->sockbufsize) {
186171568Sscottl	       val = sess->op->sockbufsize * 1024;
187171568Sscottl	       if((setsockopt(sess->soc, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)) < 0)
188171568Sscottl		  || (setsockopt(sess->soc, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)) < 0)) {
189171568Sscottl		    fprintf(stderr, "Cannot set socket sndbuf & rcvbuf to %d err=%d %s\n",
190171568Sscottl			    val, errno, strerror(errno));
191171568Sscottl		    return 0;
192171568Sscottl	       }
193171568Sscottl	  }
194171568Sscottl	  sess->flags |= SESS_CONNECTED;
195171568Sscottl	  return T1;
196211095Sdes     }
197171568Sscottl
198171568Sscottl     fprintf(stderr, "errno=%d\n", sv_errno);
199171568Sscottl     perror("connect");
200171568Sscottl     switch(sv_errno) {
201171568Sscottl     case ECONNREFUSED:
202171568Sscottl     case ENETUNREACH:
203171568Sscottl     case ETIMEDOUT:
204185289Sscottl	  if((sess->flags & SESS_REDIRECT) == 0) {
205185289Sscottl	       if(strcmp(op->targetAddress, sess->target.address) != 0) {
206185289Sscottl		    syslog(LOG_INFO, "reconnecting to original target address");
207185289Sscottl		    free(op->targetAddress);
208185289Sscottl		    op->targetAddress           = sess->target.address;
209185289Sscottl		    op->port                    = sess->target.port;
210185289Sscottl		    op->targetPortalGroupTag    = sess->target.pgt;
211185289Sscottl		    return T1;
212185289Sscottl	       }
213185289Sscottl	  }
214171568Sscottl	  sleep(5); // for now ...
215171568Sscottl	  return T1;
216171568Sscottl     default:
217171568Sscottl	  return 0; // terminal error
218171568Sscottl     }
219171568Sscottl}
220171568Sscottl
221171568Sscottlint
222171568SscottlsetOptions(isess_t *sess, int flag)
223171568Sscottl{
224171568Sscottl     isc_opt_t	oop;
225171568Sscottl     char	*sep;
226171568Sscottl
227171568Sscottl     debug_called(3);
228171568Sscottl
229171568Sscottl     bzero(&oop, sizeof(isc_opt_t));
230171568Sscottl
231171568Sscottl     if((flag & SESS_FULLFEATURE) == 0) {
232171568Sscottl	  oop.initiatorName	= sess->op->initiatorName;
233171568Sscottl	  oop.targetAddress	= sess->op->targetAddress;
234171568Sscottl	  if(sess->op->targetName != 0)
235171568Sscottl	       oop.targetName = sess->op->targetName;
236171568Sscottl
237171568Sscottl	  oop.maxRecvDataSegmentLength = sess->op->maxRecvDataSegmentLength;
238171568Sscottl	  oop.maxXmitDataSegmentLength = sess->op->maxXmitDataSegmentLength; // XXX:
239171568Sscottl	  oop.maxBurstLength = sess->op->maxBurstLength;
240171568Sscottl	  oop.maxluns = sess->op->maxluns;
241171568Sscottl     }
242171568Sscottl     else {
243171568Sscottl	  /*
244171568Sscottl	   | turn on digestion only after login
245171568Sscottl	   */
246171568Sscottl	  if(sess->op->headerDigest != NULL) {
247171568Sscottl	       sep = strchr(sess->op->headerDigest, ',');
248171568Sscottl	       if(sep == NULL)
249171568Sscottl		    oop.headerDigest = sess->op->headerDigest;
250171568Sscottl	       debug(1, "oop.headerDigest=%s", oop.headerDigest);
251171568Sscottl	  }
252171568Sscottl	  if(sess->op->dataDigest != NULL) {
253171568Sscottl	       sep = strchr(sess->op->dataDigest, ',');
254171568Sscottl	       if(sep == NULL)
255171568Sscottl		    oop.dataDigest = sess->op->dataDigest;
256171568Sscottl	       debug(1, "oop.dataDigest=%s", oop.dataDigest);
257171568Sscottl	  }
258171568Sscottl     }
259171568Sscottl
260171568Sscottl     if(ioctl(sess->fd, ISCSISETOPT, &oop)) {
261171568Sscottl	  perror("ISCSISETOPT");
262171568Sscottl	  return -1;
263171568Sscottl     }
264171568Sscottl     return 0;
265171568Sscottl}
266171568Sscottl
267171568Sscottlstatic trans_t
268171568SscottlstartSession(isess_t *sess)
269171568Sscottl{
270171568Sscottl
271171568Sscottl     int	n, fd, nfd;
272171568Sscottl     char	*dev;
273171568Sscottl
274171568Sscottl     debug_called(3);
275171568Sscottl
276171568Sscottl     if((sess->flags & SESS_CONNECTED) == 0) {
277171568Sscottl	  return T2;
278171568Sscottl     }
279171568Sscottl     if(sess->fd == -1) {
280171568Sscottl	  fd = open(iscsidev, O_RDWR);
281171568Sscottl	  if(fd < 0) {
282171568Sscottl	       perror(iscsidev);
283171568Sscottl	       return 0;
284171568Sscottl	  }
285171568Sscottl	  {
286171568Sscottl	       // XXX: this has to go
287171568Sscottl	       size_t	n;
288171568Sscottl	       n = sizeof(sess->isid);
289211095Sdes	       if(sysctlbyname("net.iscsi_initiator.isid", (void *)sess->isid, (size_t *)&n, 0, 0) != 0)
290171568Sscottl		    perror("sysctlbyname");
291171568Sscottl	  }
292171568Sscottl	  if(ioctl(fd, ISCSISETSES, &n)) {
293171568Sscottl	       perror("ISCSISETSES");
294171568Sscottl	       return 0;
295171568Sscottl	  }
296171568Sscottl	  asprintf(&dev, "%s%d", iscsidev, n);
297171568Sscottl	  nfd = open(dev, O_RDWR);
298171568Sscottl	  if(nfd < 0) {
299171568Sscottl	       perror(dev);
300171568Sscottl	       free(dev);
301171568Sscottl	       return 0;
302171568Sscottl	  }
303171568Sscottl	  free(dev);
304171568Sscottl	  close(fd);
305171568Sscottl	  sess->fd = nfd;
306171568Sscottl
307171568Sscottl	  if(setOptions(sess, 0) != 0)
308171568Sscottl	       return -1;
309171568Sscottl     }
310171568Sscottl
311171568Sscottl     if(ioctl(sess->fd, ISCSISETSOC, &sess->soc)) {
312171568Sscottl	  perror("ISCSISETSOC");
313171568Sscottl	  return 0;
314171568Sscottl     }
315171568Sscottl
316171568Sscottl     return T4;
317171568Sscottl}
318171568Sscottl
319171568Sscottlisess_t *currsess;
320171568Sscottl
321171568Sscottlstatic void
322171568Sscottltrap(int sig)
323171568Sscottl{
324171568Sscottl     syslog(LOG_NOTICE, "trapped signal %d", sig);
325171568Sscottl     fprintf(stderr, "trapped signal %d\n", sig);
326171568Sscottl
327171568Sscottl     switch(sig) {
328171568Sscottl     case SIGHUP:
329171568Sscottl	  currsess->flags |= SESS_DISCONNECT;
330171568Sscottl	  break;
331171568Sscottl
332171568Sscottl     case SIGUSR1:
333171568Sscottl	  currsess->flags |= SESS_RECONNECT;
334171568Sscottl	  break;
335171568Sscottl
336171568Sscottl     case SIGINT:
337171568Sscottl     case SIGTERM:
338171568Sscottl     default:
339171568Sscottl	  return; // ignore
340171568Sscottl     }
341171568Sscottl}
342171568Sscottl
343211095Sdesstatic int
344171568SscottldoCAM(isess_t *sess)
345171568Sscottl{
346171568Sscottl     char	pathstr[1024];
347171568Sscottl     union ccb	*ccb;
348211095Sdes     int	i, n;
349171568Sscottl
350171568Sscottl     if(ioctl(sess->fd, ISCSIGETCAM, &sess->cam) != 0) {
351171568Sscottl	  syslog(LOG_WARNING, "ISCSIGETCAM failed: %d", errno);
352211095Sdes	  return 0;
353171568Sscottl     }
354211095Sdes     debug(1, "nluns=%d", sess->cam.target_nluns);
355171568Sscottl     /*
356171568Sscottl      | for now will do this for each lun ...
357171568Sscottl      */
358211095Sdes     for(n = i = 0; i < sess->cam.target_nluns; i++) {
359217859Scracauer	  debug(2, "CAM path_id=%d target_id=%d",
360217859Scracauer		sess->cam.path_id, sess->cam.target_id);
361171568Sscottl
362171568Sscottl	  sess->camdev = cam_open_btl(sess->cam.path_id, sess->cam.target_id,
363211095Sdes				      i, O_RDWR, NULL);
364171568Sscottl	  if(sess->camdev == NULL) {
365211095Sdes	       //syslog(LOG_WARNING, "%s", cam_errbuf);
366171568Sscottl	       debug(3, "%s", cam_errbuf);
367171568Sscottl	       continue;
368171568Sscottl	  }
369171568Sscottl
370171568Sscottl	  cam_path_string(sess->camdev, pathstr, sizeof(pathstr));
371171568Sscottl	  debug(2, "pathstr=%s", pathstr);
372171568Sscottl
373171568Sscottl	  ccb = cam_getccb(sess->camdev);
374171568Sscottl	  bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_relsim) - sizeof(struct ccb_hdr));
375171568Sscottl	  ccb->ccb_h.func_code = XPT_REL_SIMQ;
376171568Sscottl	  ccb->crs.release_flags = RELSIM_ADJUST_OPENINGS;
377171568Sscottl	  ccb->crs.openings = sess->op->tags;
378171568Sscottl	  if(cam_send_ccb(sess->camdev, ccb) < 0)
379211095Sdes	       debug(2, "%s", cam_errbuf);
380171568Sscottl	  else
381171568Sscottl	  if((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
382171568Sscottl	       syslog(LOG_WARNING, "XPT_REL_SIMQ CCB failed");
383171568Sscottl	       // cam_error_print(sess->camdev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr);
384171568Sscottl	  }
385211095Sdes	  else {
386211095Sdes	       n++;
387171568Sscottl	       syslog(LOG_INFO, "%s tagged openings now %d\n", pathstr, ccb->crs.openings);
388211095Sdes	  }
389171568Sscottl	  cam_freeccb(ccb);
390171568Sscottl	  cam_close_device(sess->camdev);
391171568Sscottl     }
392211095Sdes     return n;
393171568Sscottl}
394171568Sscottl
395171568Sscottlstatic trans_t
396171568Sscottlsupervise(isess_t *sess)
397171568Sscottl{
398171568Sscottl     int	sig, val;
399171568Sscottl
400171568Sscottl     debug_called(3);
401171568Sscottl
402171568Sscottl     if(strcmp(sess->op->sessionType, "Discovery") == 0) {
403171568Sscottl	  sess->flags |= SESS_DISCONNECT;
404171568Sscottl	  return T9;
405171568Sscottl     }
406171568Sscottl
407171568Sscottl     if(vflag)
408171568Sscottl	  printf("ready to go scsi\n");
409171568Sscottl
410171568Sscottl     if(setOptions(sess, SESS_FULLFEATURE) != 0)
411171568Sscottl	  return 0; // failure
412171568Sscottl
413171568Sscottl     if((sess->flags & SESS_FULLFEATURE) == 0) {
414171568Sscottl	  if(daemon(0, 1) != 0) {
415171568Sscottl	       perror("daemon");
416171568Sscottl	       exit(1);
417171568Sscottl	  }
418211095Sdes	  if(sess->op->pidfile != NULL) {
419211095Sdes	       FILE *pidf;
420171568Sscottl
421211095Sdes	       pidf = fopen(sess->op->pidfile, "w");
422211095Sdes	       if(pidf != NULL) {
423211095Sdes 		    fprintf(pidf, "%d\n", getpid());
424211095Sdes		    fclose(pidf);
425211095Sdes	       }
426211095Sdes	  }
427171568Sscottl	  openlog("iscontrol", LOG_CONS|LOG_PERROR|LOG_PID|LOG_NDELAY, LOG_KERN);
428171568Sscottl	  syslog(LOG_INFO, "running");
429171568Sscottl
430171568Sscottl	  currsess = sess;
431171568Sscottl	  if(ioctl(sess->fd, ISCSISTART)) {
432171568Sscottl	       perror("ISCSISTART");
433171568Sscottl	       return -1;
434171568Sscottl	  }
435211095Sdes	  if(doCAM(sess) == 0) {
436211095Sdes	       syslog(LOG_WARNING, "no device found");
437211095Sdes	       ioctl(sess->fd, ISCSISTOP);
438211095Sdes	       return T15;
439211095Sdes	  }
440171568Sscottl
441171568Sscottl     }
442171568Sscottl     else {
443171568Sscottl	  if(ioctl(sess->fd, ISCSIRESTART)) {
444171568Sscottl	       perror("ISCSIRESTART");
445171568Sscottl	       return -1;
446171568Sscottl	  }
447171568Sscottl     }
448171568Sscottl
449171568Sscottl     signal(SIGINT, trap);
450171568Sscottl     signal(SIGHUP, trap);
451171568Sscottl     signal(SIGTERM, trap);
452171568Sscottl
453171568Sscottl     sig = SIGUSR1;
454171568Sscottl     signal(sig, trap);
455171568Sscottl     if(ioctl(sess->fd, ISCSISIGNAL, &sig)) {
456171568Sscottl	  perror("ISCSISIGNAL");
457171568Sscottl	  return -1;
458171568Sscottl     }
459171568Sscottl     sess->flags |= SESS_FULLFEATURE;
460171568Sscottl
461171568Sscottl     sess->flags &= ~(SESS_REDIRECT | SESS_RECONNECT);
462211095Sdes     if(vflag)
463211095Sdes	  printf("iscontrol: supervise starting main loop\n");
464171568Sscottl     /*
465171568Sscottl      | the main loop - actually do nothing
466171568Sscottl      | all the work is done inside the kernel
467171568Sscottl      */
468171568Sscottl     while((sess->flags & (SESS_REDIRECT|SESS_RECONNECT|SESS_DISCONNECT)) == 0) {
469171568Sscottl	  // do something?
470171568Sscottl	  // like sending a nop_out?
471171568Sscottl	  sleep(60);
472171568Sscottl     }
473171568Sscottl     printf("iscontrol: supervise going down\n");
474171568Sscottl     syslog(LOG_INFO, "sess flags=%x", sess->flags);
475171568Sscottl
476171568Sscottl     sig = 0;
477171568Sscottl     if(ioctl(sess->fd, ISCSISIGNAL, &sig)) {
478171568Sscottl	  perror("ISCSISIGNAL");
479171568Sscottl     }
480171568Sscottl
481171568Sscottl     if(sess->flags & SESS_DISCONNECT) {
482211095Sdes	  sess->flags &= ~SESS_FULLFEATURE;
483211095Sdes	  return T9;
484211095Sdes     }
485211095Sdes     else {
486171568Sscottl	  val = 0;
487171568Sscottl	  if(ioctl(sess->fd, ISCSISTOP, &val)) {
488171568Sscottl	       perror("ISCSISTOP");
489171568Sscottl	  }
490171568Sscottl	  sess->flags |= SESS_INITIALLOGIN1;
491171568Sscottl     }
492171568Sscottl     return T8;
493171568Sscottl}
494171568Sscottl
495171568Sscottlstatic int
496171568SscottlhandledDiscoveryResp(isess_t *sess, pdu_t *pp)
497171568Sscottl{
498171568Sscottl     u_char	*ptr;
499171568Sscottl     int	len, n;
500171568Sscottl
501171568Sscottl     debug_called(3);
502171568Sscottl
503171568Sscottl     len = pp->ds_len;
504211095Sdes     ptr = pp->ds_addr;
505171568Sscottl     while(len > 0) {
506171568Sscottl	  if(*ptr != 0)
507171568Sscottl	       printf("%s\n", ptr);
508171568Sscottl	  n = strlen((char *)ptr) + 1;
509171568Sscottl	  len -= n;
510171568Sscottl	  ptr += n;
511171568Sscottl     }
512171568Sscottl     return 0;
513171568Sscottl}
514171568Sscottl
515171568Sscottlstatic int
516171568SscottldoDiscovery(isess_t *sess)
517171568Sscottl{
518171568Sscottl     pdu_t	spp;
519171568Sscottl     text_req_t	*tp = (text_req_t *)&spp.ipdu.bhs;
520171568Sscottl
521171568Sscottl     debug_called(3);
522171568Sscottl
523171568Sscottl     bzero(&spp, sizeof(pdu_t));
524171568Sscottl     tp->cmd = ISCSI_TEXT_CMD /*| 0x40 */; // because of a bug in openiscsi-target
525171568Sscottl     tp->F = 1;
526171568Sscottl     tp->ttt = 0xffffffff;
527171568Sscottl     addText(&spp, "SendTargets=All");
528171568Sscottl     return sendPDU(sess, &spp, handledDiscoveryResp);
529171568Sscottl}
530171568Sscottl
531171568Sscottlstatic trans_t
532171568SscottldoLogin(isess_t *sess)
533171568Sscottl{
534171568Sscottl     isc_opt_t	*op = sess->op;
535171568Sscottl     int	status, count;
536171568Sscottl
537171568Sscottl     debug_called(3);
538171568Sscottl
539171568Sscottl     if(op->chapSecret == NULL && op->tgtChapSecret == NULL)
540171568Sscottl	  /*
541171568Sscottl	   | don't need any security negotiation
542171568Sscottl	   | or in other words: we don't have any secrets to exchange
543171568Sscottl	   */
544171568Sscottl	  sess->csg = LON_PHASE;
545171568Sscottl     else
546171568Sscottl	  sess->csg = SN_PHASE;
547171568Sscottl
548171568Sscottl     if(sess->tsih) {
549171568Sscottl	  sess->tsih = 0;	// XXX: no 'reconnect' yet
550171568Sscottl	  sess->flags &= ~SESS_NEGODONE; // XXX: KLUDGE
551171568Sscottl     }
552171568Sscottl     count = 10; // should be more than enough
553171568Sscottl     do {
554171568Sscottl	  debug(3, "count=%d csg=%d", count, sess->csg);
555171568Sscottl	  status = loginPhase(sess);
556171568Sscottl	  if(count-- == 0)
557171568Sscottl	       // just in case we get into a loop
558171568Sscottl	       status = -1;
559171568Sscottl     } while(status == 0 && (sess->csg != FF_PHASE));
560171568Sscottl
561171568Sscottl     sess->flags &= ~SESS_INITIALLOGIN;
562171568Sscottl     debug(3, "status=%d", status);
563171568Sscottl
564171568Sscottl     switch(status) {
565171568Sscottl     case 0: // all is ok ...
566171568Sscottl	  sess->flags |= SESS_LOGGEDIN;
567171568Sscottl	  if(strcmp(sess->op->sessionType, "Discovery") == 0)
568171568Sscottl	       doDiscovery(sess);
569171568Sscottl	  return T5;
570171568Sscottl
571171568Sscottl     case 1:	// redirect - temporary/permanent
572171568Sscottl	  /*
573171568Sscottl	   | start from scratch?
574171568Sscottl	   */
575171568Sscottl	  sess->flags &= ~SESS_NEGODONE;
576171568Sscottl	  sess->flags |= (SESS_REDIRECT | SESS_INITIALLOGIN1);
577171568Sscottl	  syslog(LOG_DEBUG, "target sent REDIRECT");
578171568Sscottl	  return T7;
579171568Sscottl
580171568Sscottl     case 2: // initiator terminal error
581185289Sscottl	  return 0;
582171568Sscottl     case 3: // target terminal error -- could retry ...
583185289Sscottl	  sleep(5);
584185289Sscottl	  return T7; // lets try
585171568Sscottl     default:
586171568Sscottl	  return 0;
587171568Sscottl     }
588171568Sscottl}
589171568Sscottl
590171568Sscottlstatic int
591171568SscottlhandleLogoutResp(isess_t *sess, pdu_t *pp)
592171568Sscottl{
593211095Sdes     if(sess->flags & SESS_DISCONNECT) {
594211095Sdes	  int val = 0;
595211095Sdes	  if(ioctl(sess->fd, ISCSISTOP, &val)) {
596211095Sdes	       perror("ISCSISTOP");
597211095Sdes	  }
598171568Sscottl	  return 0;
599211095Sdes     }
600171568Sscottl     return T13;
601171568Sscottl}
602171568Sscottl
603171568Sscottlstatic trans_t
604171568SscottlstartLogout(isess_t *sess)
605171568Sscottl{
606171568Sscottl     pdu_t	spp;
607171568Sscottl     logout_req_t *p = (logout_req_t *)&spp.ipdu.bhs;
608171568Sscottl
609171568Sscottl     bzero(&spp, sizeof(pdu_t));
610171568Sscottl     p->cmd = ISCSI_LOGOUT_CMD| 0x40;
611171568Sscottl     p->reason = BIT(7) | 0;
612171568Sscottl     p->CID = htons(1);
613171568Sscottl
614171568Sscottl     return sendPDU(sess, &spp, handleLogoutResp);
615171568Sscottl}
616171568Sscottl
617171568Sscottlstatic trans_t
618171568SscottlinLogout(isess_t *sess)
619171568Sscottl{
620171568Sscottl     if(sess->flags & SESS_RECONNECT)
621171568Sscottl	  return T18;
622171568Sscottl     return 0;
623171568Sscottl}
624171568Sscottl
625171568Sscottltypedef enum {
626171568Sscottl     S1, S2, /*S3,*/ S4, S5, S6, S7, S8
627171568Sscottl} state_t;
628171568Sscottl
629211095Sdes/**
630171568Sscottl      S1: FREE
631171568Sscottl      S2: XPT_WAIT
632171568Sscottl      S4: IN_LOGIN
633171568Sscottl      S5: LOGGED_IN
634171568Sscottl      S6: IN_LOGOUT
635171568Sscottl      S7: LOGOUT_REQUESTED
636171568Sscottl      S8: CLEANUP_WAIT
637171568Sscottl
638171568Sscottl                     -------<-------------+
639171568Sscottl         +--------->/ S1    \<----+       |
640171568Sscottl      T13|       +->\       /<-+   \      |
641171568Sscottl         |      /    ---+---    \   \     |
642171568Sscottl         |     /        |     T2 \   |    |
643171568Sscottl         |  T8 |        |T1       |  |    |
644171568Sscottl         |     |        |        /   |T7  |
645171568Sscottl         |     |        |       /    |    |
646171568Sscottl         |     |        |      /     |    |
647171568Sscottl         |     |        V     /     /     |
648171568Sscottl         |     |     ------- /     /      |
649171568Sscottl         |     |    / S2    \     /       |
650171568Sscottl         |     |    \       /    /        |
651171568Sscottl         |     |     ---+---    /         |
652171568Sscottl         |     |        |T4    /          |
653171568Sscottl         |     |        V     /           | T18
654171568Sscottl         |     |     ------- /            |
655171568Sscottl         |     |    / S4    \             |
656171568Sscottl         |     |    \       /             |
657171568Sscottl         |     |     ---+---              |         T15
658171568Sscottl         |     |        |T5      +--------+---------+
659171568Sscottl         |     |        |       /T16+-----+------+  |
660171568Sscottl         |     |        |      /   -+-----+--+   |  |
661171568Sscottl         |     |        |     /   /  S7   \  |T12|  |
662171568Sscottl         |     |        |    / +->\       /<-+   V  V
663171568Sscottl         |     |        |   / /    -+-----       -------
664171568Sscottl         |     |        |  / /T11   |T10        /  S8   \
665171568Sscottl         |     |        V / /       V  +----+   \       /
666171568Sscottl         |     |      ---+-+-      ----+--  |    -------
667171568Sscottl         |     |     / S5    \T9  / S6    \<+    ^
668171568Sscottl         |     +-----\       /--->\       / T14  |
669171568Sscottl         |            -------      --+----+------+T17
670171568Sscottl         +---------------------------+
671211095Sdes*/
672171568Sscottl
673171568Sscottlint
674171568Sscottlfsm(isc_opt_t *op)
675171568Sscottl{
676171568Sscottl     state_t	state;
677171568Sscottl     isess_t	*sess;
678171568Sscottl
679171568Sscottl     if((sess = calloc(1, sizeof(isess_t))) == NULL) {
680171568Sscottl	  // boy, is this a bad start ...
681171568Sscottl	  fprintf(stderr, "no memory!\n");
682171568Sscottl	  return -1;
683171568Sscottl     }
684171568Sscottl
685171568Sscottl     state = S1;
686171568Sscottl     sess->op = op;
687171568Sscottl     sess->fd = -1;
688171568Sscottl     sess->soc = -1;
689185289Sscottl     sess->target.address = strdup(op->targetAddress);
690185289Sscottl     sess->target.port = op->port;
691185289Sscottl     sess->target.pgt = op->targetPortalGroupTag;
692171568Sscottl
693171568Sscottl     sess->flags = SESS_INITIALLOGIN | SESS_INITIALLOGIN1;
694171568Sscottl
695171568Sscottl     do {
696171568Sscottl	  switch(state) {
697171568Sscottl
698171568Sscottl	  case S1:
699171568Sscottl	       switch(tcpConnect(sess)) {
700171568Sscottl	       case T1: state = S2; break;
701171568Sscottl	       default: state = S8; break;
702171568Sscottl	       }
703171568Sscottl	       break;
704171568Sscottl
705171568Sscottl	  case S2:
706171568Sscottl	       switch(startSession(sess)) {
707171568Sscottl	       case T2: state = S1; break;
708171568Sscottl	       case T4: state = S4; break;
709171568Sscottl	       default: state = S8; break;
710171568Sscottl	       }
711171568Sscottl	       break;
712171568Sscottl
713171568Sscottl	  case S4:
714171568Sscottl	       switch(doLogin(sess)) {
715171568Sscottl	       case T7:  state = S1; break;
716171568Sscottl	       case T5:  state = S5; break;
717171568Sscottl	       default: state = S8; break;
718171568Sscottl	       }
719171568Sscottl	       break;
720171568Sscottl
721171568Sscottl	  case S5:
722171568Sscottl	       switch(supervise(sess)) {
723171568Sscottl	       case T8:  state = S1; break;
724171568Sscottl	       case T9:  state = S6; break;
725171568Sscottl	       case T11: state = S7; break;
726171568Sscottl	       case T15: state = S8; break;
727171568Sscottl	       default: state = S8; break;
728171568Sscottl	       }
729171568Sscottl	       break;
730171568Sscottl
731171568Sscottl	  case S6:
732171568Sscottl	       switch(startLogout(sess)) {
733171568Sscottl	       case T13: state = S1; break;
734171568Sscottl	       case T14: state = S6; break;
735171568Sscottl	       case T16: state = S8; break;
736171568Sscottl	       default: state = S8; break;
737171568Sscottl	       }
738171568Sscottl	       break;
739171568Sscottl
740171568Sscottl	  case S7:
741171568Sscottl	       switch(inLogout(sess)) {
742171568Sscottl	       case T18: state = S1; break;
743171568Sscottl	       case T10: state = S6; break;
744171568Sscottl	       case T12: state = S7; break;
745171568Sscottl	       case T16: state = S8; break;
746171568Sscottl	       default: state = S8; break;
747171568Sscottl	       }
748171568Sscottl	       break;
749171568Sscottl
750171568Sscottl	  case S8:
751171568Sscottl	       // maybe do some clean up?
752171568Sscottl	       syslog(LOG_INFO, "terminated");
753171568Sscottl	       return 0;
754171568Sscottl	  }
755171568Sscottl     } while(1);
756171568Sscottl}
757