fsm.c revision 185289
1171568Sscottl/*-
2185289Sscottl * Copyright (c) 2005-2008 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: head/sbin/iscontrol/fsm.c 185289 2008-11-25 07:17:11Z scottl $");
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#if __FreeBSD_version < 500000
44171568Sscottl#include <sys/time.h>
45171568Sscottl#endif
46171568Sscottl#include <sys/ioctl.h>
47171568Sscottl#include <netdb.h>
48171568Sscottl#include <stdlib.h>
49171568Sscottl#include <unistd.h>
50171568Sscottl#include <stdio.h>
51171568Sscottl#include <string.h>
52171568Sscottl#include <errno.h>
53171568Sscottl#include <fcntl.h>
54171568Sscottl#include <time.h>
55171568Sscottl#include <syslog.h>
56171568Sscottl#include <stdarg.h>
57171568Sscottl#include <camlib.h>
58171568Sscottl
59171568Sscottl#include "iscsi.h"
60171568Sscottl#include "iscontrol.h"
61171568Sscottl
62171568Sscottltypedef enum {
63171568Sscottl     T1 = 1,
64171568Sscottl     T2, /*T3,*/ T4, T5, /*T6,*/ T7, T8, T9,
65171568Sscottl     T10, T11, T12, T13, T14, T15, T16, T18
66171568Sscottl} trans_t;
67171568Sscottl
68185289Sscottl/*
69185289Sscottl | now supports IPV6
70185289Sscottl | thanks to:
71185289Sscottl |	Hajimu UMEMOTO @ Internet Mutual Aid Society Yokohama, Japan
72185289Sscottl |	ume@mahoroba.org  ume@{,jp.}FreeBSD.org
73185289Sscottl |	http://www.imasy.org/~ume/
74185289Sscottl */
75171568Sscottlstatic trans_t
76171568SscottltcpConnect(isess_t *sess)
77171568Sscottl{
78171568Sscottl     isc_opt_t *op = sess->op;
79185289Sscottl     int	val, sv_errno, soc;
80185289Sscottl     struct     addrinfo *res, *res0, hints;
81185289Sscottl     char	pbuf[10];
82171568Sscottl
83171568Sscottl     debug_called(3);
84171568Sscottl     if(sess->flags & (SESS_RECONNECT|SESS_REDIRECT)) {
85171568Sscottl	  syslog(LOG_INFO, "%s", (sess->flags & SESS_RECONNECT)
86171568Sscottl		 ? "Reconnect": "Redirected");
87171568Sscottl
88185289Sscottl	  debug(1, "%s", (sess->flags & SESS_RECONNECT) ? "Reconnect": "Redirected");
89171568Sscottl	  shutdown(sess->soc, SHUT_RDWR);
90171568Sscottl	  //close(sess->soc);
91171568Sscottl	  sess->soc = -1;
92171568Sscottl
93171568Sscottl	  sess->flags &= ~SESS_CONNECTED;
94171568Sscottl	  if(sess->flags & SESS_REDIRECT) {
95185289Sscottl	       sess->redirect_cnt++;
96171568Sscottl	       sess->flags |= SESS_RECONNECT;
97185289Sscottl	  } else
98185289Sscottl	       sleep(2); // XXX: actually should be ?
99185289Sscottl#ifdef notyet
100185289Sscottl	  {
101185289Sscottl	       time_t	sec;
102171568Sscottl	  // make sure we are not in a loop
103171568Sscottl	  // XXX: this code has to be tested
104171568Sscottl	  sec = time(0) - sess->reconnect_time;
105171568Sscottl	  if(sec > (5*60)) {
106171568Sscottl	       // if we've been connected for more that 5 minutes
107171568Sscottl	       // then just reconnect
108171568Sscottl	       sess->reconnect_time = sec;
109171568Sscottl	       sess->reconnect_cnt1 = 0;
110171568Sscottl	  }
111171568Sscottl	  else {
112171568Sscottl	       //
113171568Sscottl	       sess->reconnect_cnt1++;
114171568Sscottl	       if((sec / sess->reconnect_cnt1) < 2) {
115171568Sscottl		    // if less that 2 seconds from the last reconnect
116171568Sscottl		    // we are most probably looping
117171568Sscottl		    syslog(LOG_CRIT, "too many reconnects %d", sess->reconnect_cnt1);
118171568Sscottl		    return 0;
119171568Sscottl	       }
120171568Sscottl	  }
121185289Sscottl     }
122185289Sscottl#endif
123171568Sscottl	  sess->reconnect_cnt++;
124171568Sscottl     }
125171568Sscottl
126185289Sscottl     snprintf(pbuf, sizeof(pbuf), "%d", op->port);
127171568Sscottl     memset(&hints, 0, sizeof(hints));
128185289Sscottl     hints.ai_family	= PF_UNSPEC;
129171568Sscottl     hints.ai_socktype	= SOCK_STREAM;
130185289Sscottl     debug(1, "targetAddress=%s port=%d", op->targetAddress, op->port);
131185289Sscottl     if((val = getaddrinfo(op->targetAddress, pbuf, &hints, &res0)) != 0) {
132171568Sscottl          fprintf(stderr, "getaddrinfo(%s): %s\n", op->targetAddress, gai_strerror(val));
133171568Sscottl          return 0;
134171568Sscottl     }
135185289Sscottl     sess->flags &= ~SESS_CONNECTED;
136185289Sscottl     sv_errno = 0;
137185289Sscottl     soc = -1;
138185289Sscottl     for(res = res0; res; res = res->ai_next) {
139185289Sscottl	  soc = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
140185289Sscottl	  if (soc == -1)
141185289Sscottl	       continue;
142171568Sscottl
143171568Sscottl     // from Patrick.Guelat@imp.ch:
144171568Sscottl     // iscontrol can be called without waiting for the socket entry to time out
145171568Sscottl     val = 1;
146185289Sscottl	  if(setsockopt(soc, SOL_SOCKET, SO_REUSEADDR, &val, (socklen_t)sizeof(val)) < 0) {
147171568Sscottl	  fprintf(stderr, "Cannot set socket SO_REUSEADDR %d: %s\n\n",
148171568Sscottl		  errno, strerror(errno));
149171568Sscottl     }
150171568Sscottl
151185289Sscottl	  if(connect(soc, res->ai_addr, res->ai_addrlen) == 0)
152185289Sscottl	       break;
153185289Sscottl	  sv_errno = errno;
154185289Sscottl	  close(soc);
155185289Sscottl	  soc = -1;
156185289Sscottl     }
157185289Sscottl     freeaddrinfo(res0);
158185289Sscottl     if(soc != -1) {
159185289Sscottl	  sess->soc = soc;
160171568Sscottl
161171568Sscottl#if 0
162171568Sscottl	  struct	timeval timeout;
163171568Sscottl
164171568Sscottl	  val = 1;
165171568Sscottl	  if(setsockopt(sess->soc, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0)
166171568Sscottl	       fprintf(stderr, "Cannot set socket KEEPALIVE option err=%d %s\n",
167171568Sscottl		       errno, strerror(errno));
168171568Sscottl
169171568Sscottl	  if(setsockopt(sess->soc, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) < 0)
170171568Sscottl	       fprintf(stderr, "Cannot set socket NO delay option err=%d %s\n",
171171568Sscottl		       errno, strerror(errno));
172171568Sscottl
173171568Sscottl	  timeout.tv_sec = 10;
174171568Sscottl	  timeout.tv_usec = 0;
175171568Sscottl	  if((setsockopt(sess->soc, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0)
176171568Sscottl	     || (setsockopt(sess->soc, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0)) {
177171568Sscottl	       fprintf(stderr, "Cannot set socket timeout to %ld err=%d %s\n",
178171568Sscottl		       timeout.tv_sec, errno, strerror(errno));
179171568Sscottl	  }
180171568Sscottl#endif
181171568Sscottl#ifdef CURIOUS
182171568Sscottl	  {
183171568Sscottl	       int len = sizeof(val);
184171568Sscottl	       if(getsockopt(sess->soc, SOL_SOCKET, SO_SNDBUF, &val, &len) == 0)
185171568Sscottl		    fprintf(stderr, "was: SO_SNDBUF=%dK\n", val/1024);
186171568Sscottl	  }
187171568Sscottl#endif
188171568Sscottl	  if(sess->op->sockbufsize) {
189171568Sscottl	       val = sess->op->sockbufsize * 1024;
190171568Sscottl	       if((setsockopt(sess->soc, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)) < 0)
191171568Sscottl		  || (setsockopt(sess->soc, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)) < 0)) {
192171568Sscottl		    fprintf(stderr, "Cannot set socket sndbuf & rcvbuf to %d err=%d %s\n",
193171568Sscottl			    val, errno, strerror(errno));
194171568Sscottl		    return 0;
195171568Sscottl	       }
196171568Sscottl	  }
197171568Sscottl	  sess->flags |= SESS_CONNECTED;
198171568Sscottl	  return T1;
199185289Sscottl     }
200171568Sscottl
201171568Sscottl     fprintf(stderr, "errno=%d\n", sv_errno);
202171568Sscottl     perror("connect");
203171568Sscottl     switch(sv_errno) {
204171568Sscottl     case ECONNREFUSED:
205171568Sscottl     case ENETUNREACH:
206171568Sscottl     case ETIMEDOUT:
207185289Sscottl	  if((sess->flags & SESS_REDIRECT) == 0) {
208185289Sscottl	       if(strcmp(op->targetAddress, sess->target.address) != 0) {
209185289Sscottl		    syslog(LOG_INFO, "reconnecting to original target address");
210185289Sscottl		    free(op->targetAddress);
211185289Sscottl		    op->targetAddress           = sess->target.address;
212185289Sscottl		    op->port                    = sess->target.port;
213185289Sscottl		    op->targetPortalGroupTag    = sess->target.pgt;
214185289Sscottl		    return T1;
215185289Sscottl	       }
216185289Sscottl	  }
217171568Sscottl	  sleep(5); // for now ...
218171568Sscottl	  return T1;
219171568Sscottl     default:
220171568Sscottl	  return 0; // terminal error
221171568Sscottl     }
222171568Sscottl}
223171568Sscottl
224171568Sscottlint
225171568SscottlsetOptions(isess_t *sess, int flag)
226171568Sscottl{
227171568Sscottl     isc_opt_t	oop;
228171568Sscottl     char	*sep;
229171568Sscottl
230171568Sscottl     debug_called(3);
231171568Sscottl
232171568Sscottl     bzero(&oop, sizeof(isc_opt_t));
233171568Sscottl
234171568Sscottl     if((flag & SESS_FULLFEATURE) == 0) {
235171568Sscottl	  oop.initiatorName	= sess->op->initiatorName;
236171568Sscottl	  oop.targetAddress	= sess->op->targetAddress;
237171568Sscottl	  if(sess->op->targetName != 0)
238171568Sscottl	       oop.targetName = sess->op->targetName;
239171568Sscottl
240171568Sscottl	  oop.maxRecvDataSegmentLength = sess->op->maxRecvDataSegmentLength;
241171568Sscottl	  oop.maxXmitDataSegmentLength = sess->op->maxXmitDataSegmentLength; // XXX:
242171568Sscottl	  oop.maxBurstLength = sess->op->maxBurstLength;
243171568Sscottl	  oop.maxluns = sess->op->maxluns;
244171568Sscottl     }
245171568Sscottl     else {
246171568Sscottl	  /*
247171568Sscottl	   | turn on digestion only after login
248171568Sscottl	   */
249171568Sscottl	  if(sess->op->headerDigest != NULL) {
250171568Sscottl	       sep = strchr(sess->op->headerDigest, ',');
251171568Sscottl	       if(sep == NULL)
252171568Sscottl		    oop.headerDigest = sess->op->headerDigest;
253171568Sscottl	       debug(1, "oop.headerDigest=%s", oop.headerDigest);
254171568Sscottl	  }
255171568Sscottl	  if(sess->op->dataDigest != NULL) {
256171568Sscottl	       sep = strchr(sess->op->dataDigest, ',');
257171568Sscottl	       if(sep == NULL)
258171568Sscottl		    oop.dataDigest = sess->op->dataDigest;
259171568Sscottl	       debug(1, "oop.dataDigest=%s", oop.dataDigest);
260171568Sscottl	  }
261171568Sscottl     }
262171568Sscottl
263171568Sscottl     if(ioctl(sess->fd, ISCSISETOPT, &oop)) {
264171568Sscottl	  perror("ISCSISETOPT");
265171568Sscottl	  return -1;
266171568Sscottl     }
267171568Sscottl     return 0;
268171568Sscottl}
269171568Sscottl
270171568Sscottlstatic trans_t
271171568SscottlstartSession(isess_t *sess)
272171568Sscottl{
273171568Sscottl
274171568Sscottl     int	n, fd, nfd;
275171568Sscottl     char	*dev;
276171568Sscottl
277171568Sscottl     debug_called(3);
278171568Sscottl
279171568Sscottl     if((sess->flags & SESS_CONNECTED) == 0) {
280171568Sscottl	  return T2;
281171568Sscottl     }
282171568Sscottl     if(sess->fd == -1) {
283171568Sscottl	  fd = open(iscsidev, O_RDWR);
284171568Sscottl	  if(fd < 0) {
285171568Sscottl	       perror(iscsidev);
286171568Sscottl	       return 0;
287171568Sscottl	  }
288171568Sscottl	  {
289171568Sscottl	       // XXX: this has to go
290171568Sscottl	       size_t	n;
291171568Sscottl	       n = sizeof(sess->isid);
292171568Sscottl	       if(sysctlbyname("net.iscsi.isid", (void *)sess->isid, (size_t *)&n, 0, 0) != 0)
293171568Sscottl		    perror("sysctlbyname");
294171568Sscottl	  }
295171568Sscottl	  if(ioctl(fd, ISCSISETSES, &n)) {
296171568Sscottl	       perror("ISCSISETSES");
297171568Sscottl	       return 0;
298171568Sscottl	  }
299171568Sscottl	  asprintf(&dev, "%s%d", iscsidev, n);
300171568Sscottl	  nfd = open(dev, O_RDWR);
301171568Sscottl	  if(nfd < 0) {
302171568Sscottl	       perror(dev);
303171568Sscottl	       free(dev);
304171568Sscottl	       return 0;
305171568Sscottl	  }
306171568Sscottl	  free(dev);
307171568Sscottl	  close(fd);
308171568Sscottl	  sess->fd = nfd;
309171568Sscottl
310171568Sscottl	  if(setOptions(sess, 0) != 0)
311171568Sscottl	       return -1;
312171568Sscottl     }
313171568Sscottl
314171568Sscottl     if(ioctl(sess->fd, ISCSISETSOC, &sess->soc)) {
315171568Sscottl	  perror("ISCSISETSOC");
316171568Sscottl	  return 0;
317171568Sscottl     }
318171568Sscottl
319171568Sscottl     return T4;
320171568Sscottl}
321171568Sscottl
322171568Sscottlisess_t *currsess;
323171568Sscottl
324171568Sscottlstatic void
325171568Sscottltrap(int sig)
326171568Sscottl{
327171568Sscottl     syslog(LOG_NOTICE, "trapped signal %d", sig);
328171568Sscottl     fprintf(stderr, "trapped signal %d\n", sig);
329171568Sscottl
330171568Sscottl     switch(sig) {
331171568Sscottl     case SIGHUP:
332171568Sscottl	  currsess->flags |= SESS_DISCONNECT;
333171568Sscottl	  break;
334171568Sscottl
335171568Sscottl     case SIGUSR1:
336171568Sscottl	  currsess->flags |= SESS_RECONNECT;
337171568Sscottl	  break;
338171568Sscottl
339171568Sscottl     case SIGINT:
340171568Sscottl     case SIGTERM:
341171568Sscottl     default:
342171568Sscottl	  return; // ignore
343171568Sscottl     }
344171568Sscottl}
345171568Sscottl
346171568Sscottlstatic void
347171568SscottldoCAM(isess_t *sess)
348171568Sscottl{
349171568Sscottl     char	pathstr[1024];
350171568Sscottl     union ccb	*ccb;
351171568Sscottl     int	i;
352171568Sscottl
353171568Sscottl     if(ioctl(sess->fd, ISCSIGETCAM, &sess->cam) != 0) {
354171568Sscottl	  syslog(LOG_WARNING, "ISCSIGETCAM failed: %d", errno);
355171568Sscottl	  return;
356171568Sscottl     }
357171568Sscottl     debug(2, "nluns=%d", sess->cam.target_nluns);
358171568Sscottl     /*
359171568Sscottl      | for now will do this for each lun ...
360171568Sscottl      */
361171568Sscottl     for(i = 0; i < sess->cam.target_nluns; i++) {
362171568Sscottl	  debug(2, "CAM path_id=%d target_id=%d target_lun=%d",
363171568Sscottl		sess->cam.path_id, sess->cam.target_id, sess->cam.target_lun[i]);
364171568Sscottl
365171568Sscottl	  sess->camdev = cam_open_btl(sess->cam.path_id, sess->cam.target_id,
366171568Sscottl				      sess->cam.target_lun[i], O_RDWR, NULL);
367171568Sscottl	  if(sess->camdev == NULL) {
368171568Sscottl	       syslog(LOG_WARNING, "%s", cam_errbuf);
369171568Sscottl	       debug(3, "%s", cam_errbuf);
370171568Sscottl	       continue;
371171568Sscottl	  }
372171568Sscottl
373171568Sscottl	  cam_path_string(sess->camdev, pathstr, sizeof(pathstr));
374171568Sscottl	  debug(2, "pathstr=%s", pathstr);
375171568Sscottl
376171568Sscottl	  ccb = cam_getccb(sess->camdev);
377171568Sscottl	  bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_relsim) - sizeof(struct ccb_hdr));
378171568Sscottl	  ccb->ccb_h.func_code = XPT_REL_SIMQ;
379171568Sscottl	  ccb->crs.release_flags = RELSIM_ADJUST_OPENINGS;
380171568Sscottl	  ccb->crs.openings = sess->op->tags;
381171568Sscottl
382171568Sscottl	  if(cam_send_ccb(sess->camdev, ccb) < 0)
383171568Sscottl	       syslog(LOG_WARNING, "%s", cam_errbuf);
384171568Sscottl	  else
385171568Sscottl	  if((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
386171568Sscottl	       syslog(LOG_WARNING, "XPT_REL_SIMQ CCB failed");
387171568Sscottl	       // cam_error_print(sess->camdev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr);
388171568Sscottl	  }
389171568Sscottl	  else
390171568Sscottl	       syslog(LOG_INFO, "%s tagged openings now %d\n", pathstr, ccb->crs.openings);
391171568Sscottl
392171568Sscottl	  cam_freeccb(ccb);
393171568Sscottl	  cam_close_device(sess->camdev);
394171568Sscottl     }
395171568Sscottl}
396171568Sscottl
397171568Sscottlstatic trans_t
398171568Sscottlsupervise(isess_t *sess)
399171568Sscottl{
400171568Sscottl     int	sig, val;
401171568Sscottl
402171568Sscottl     debug_called(3);
403171568Sscottl
404171568Sscottl     if(strcmp(sess->op->sessionType, "Discovery") == 0) {
405171568Sscottl	  sess->flags |= SESS_DISCONNECT;
406171568Sscottl	  return T9;
407171568Sscottl     }
408171568Sscottl
409171568Sscottl     if(vflag)
410171568Sscottl	  printf("ready to go scsi\n");
411171568Sscottl
412171568Sscottl     if(setOptions(sess, SESS_FULLFEATURE) != 0)
413171568Sscottl	  return 0; // failure
414171568Sscottl
415171568Sscottl     if((sess->flags & SESS_FULLFEATURE) == 0) {
416171568Sscottl	  if(daemon(0, 1) != 0) {
417171568Sscottl	       perror("daemon");
418171568Sscottl	       exit(1);
419171568Sscottl	  }
420171568Sscottl
421171568Sscottl	  openlog("iscontrol", LOG_CONS|LOG_PERROR|LOG_PID|LOG_NDELAY, LOG_KERN);
422171568Sscottl	  syslog(LOG_INFO, "running");
423171568Sscottl
424171568Sscottl	  currsess = sess;
425171568Sscottl	  if(ioctl(sess->fd, ISCSISTART)) {
426171568Sscottl	       perror("ISCSISTART");
427171568Sscottl	       return -1;
428171568Sscottl	  }
429171568Sscottl	  doCAM(sess);
430171568Sscottl
431171568Sscottl     }
432171568Sscottl     else {
433171568Sscottl	  if(ioctl(sess->fd, ISCSIRESTART)) {
434171568Sscottl	       perror("ISCSIRESTART");
435171568Sscottl	       return -1;
436171568Sscottl	  }
437171568Sscottl     }
438171568Sscottl
439171568Sscottl     signal(SIGINT, trap);
440171568Sscottl     signal(SIGHUP, trap);
441171568Sscottl     signal(SIGTERM, trap);
442171568Sscottl
443171568Sscottl     sig = SIGUSR1;
444171568Sscottl     signal(sig, trap);
445171568Sscottl     if(ioctl(sess->fd, ISCSISIGNAL, &sig)) {
446171568Sscottl	  perror("ISCSISIGNAL");
447171568Sscottl	  return -1;
448171568Sscottl     }
449171568Sscottl     sess->flags |= SESS_FULLFEATURE;
450171568Sscottl
451171568Sscottl     sess->flags &= ~(SESS_REDIRECT | SESS_RECONNECT);
452171568Sscottl     printf("iscontrol: supervise starting main loop\n");
453171568Sscottl     /*
454171568Sscottl      | the main loop - actually do nothing
455171568Sscottl      | all the work is done inside the kernel
456171568Sscottl      */
457171568Sscottl     while((sess->flags & (SESS_REDIRECT|SESS_RECONNECT|SESS_DISCONNECT)) == 0) {
458171568Sscottl	  // do something?
459171568Sscottl	  // like sending a nop_out?
460171568Sscottl	  sleep(60);
461171568Sscottl     }
462171568Sscottl     printf("iscontrol: supervise going down\n");
463171568Sscottl     syslog(LOG_INFO, "sess flags=%x", sess->flags);
464171568Sscottl
465171568Sscottl     sig = 0;
466171568Sscottl     if(ioctl(sess->fd, ISCSISIGNAL, &sig)) {
467171568Sscottl	  perror("ISCSISIGNAL");
468171568Sscottl     }
469171568Sscottl
470171568Sscottl     if(sess->flags & SESS_DISCONNECT) {
471171568Sscottl	  val = 0;
472171568Sscottl	  if(ioctl(sess->fd, ISCSISTOP, &val)) {
473171568Sscottl	       perror("ISCSISTOP");
474171568Sscottl	  }
475171568Sscottl	  sess->flags &= ~SESS_FULLFEATURE;
476171568Sscottl	  return T9;
477171568Sscottl     }
478171568Sscottl     else {
479171568Sscottl	  sess->flags |= SESS_INITIALLOGIN1;
480171568Sscottl     }
481171568Sscottl     return T8;
482171568Sscottl}
483171568Sscottl
484171568Sscottlstatic int
485171568SscottlhandledDiscoveryResp(isess_t *sess, pdu_t *pp)
486171568Sscottl{
487171568Sscottl     u_char	*ptr;
488171568Sscottl     int	len, n;
489171568Sscottl
490171568Sscottl     debug_called(3);
491171568Sscottl
492171568Sscottl     len = pp->ds_len;
493171568Sscottl     ptr = pp->ds;
494171568Sscottl     while(len > 0) {
495171568Sscottl	  if(*ptr != 0)
496171568Sscottl	       printf("%s\n", ptr);
497171568Sscottl	  n = strlen((char *)ptr) + 1;
498171568Sscottl	  len -= n;
499171568Sscottl	  ptr += n;
500171568Sscottl     }
501171568Sscottl     return 0;
502171568Sscottl}
503171568Sscottl
504171568Sscottlstatic int
505171568SscottldoDiscovery(isess_t *sess)
506171568Sscottl{
507171568Sscottl     pdu_t	spp;
508171568Sscottl     text_req_t	*tp = (text_req_t *)&spp.ipdu.bhs;
509171568Sscottl
510171568Sscottl     debug_called(3);
511171568Sscottl
512171568Sscottl     bzero(&spp, sizeof(pdu_t));
513171568Sscottl     tp->cmd = ISCSI_TEXT_CMD /*| 0x40 */; // because of a bug in openiscsi-target
514171568Sscottl     tp->F = 1;
515171568Sscottl     tp->ttt = 0xffffffff;
516171568Sscottl     addText(&spp, "SendTargets=All");
517171568Sscottl     return sendPDU(sess, &spp, handledDiscoveryResp);
518171568Sscottl}
519171568Sscottl
520171568Sscottlstatic trans_t
521171568SscottldoLogin(isess_t *sess)
522171568Sscottl{
523171568Sscottl     isc_opt_t	*op = sess->op;
524171568Sscottl     int	status, count;
525171568Sscottl
526171568Sscottl     debug_called(3);
527171568Sscottl
528171568Sscottl     if(op->chapSecret == NULL && op->tgtChapSecret == NULL)
529171568Sscottl	  /*
530171568Sscottl	   | don't need any security negotiation
531171568Sscottl	   | or in other words: we don't have any secrets to exchange
532171568Sscottl	   */
533171568Sscottl	  sess->csg = LON_PHASE;
534171568Sscottl     else
535171568Sscottl	  sess->csg = SN_PHASE;
536171568Sscottl
537171568Sscottl     if(sess->tsih) {
538171568Sscottl	  sess->tsih = 0;	// XXX: no 'reconnect' yet
539171568Sscottl	  sess->flags &= ~SESS_NEGODONE; // XXX: KLUDGE
540171568Sscottl     }
541171568Sscottl     count = 10; // should be more than enough
542171568Sscottl     do {
543171568Sscottl	  debug(3, "count=%d csg=%d", count, sess->csg);
544171568Sscottl	  status = loginPhase(sess);
545171568Sscottl	  if(count-- == 0)
546171568Sscottl	       // just in case we get into a loop
547171568Sscottl	       status = -1;
548171568Sscottl     } while(status == 0 && (sess->csg != FF_PHASE));
549171568Sscottl
550171568Sscottl     sess->flags &= ~SESS_INITIALLOGIN;
551171568Sscottl     debug(3, "status=%d", status);
552171568Sscottl
553171568Sscottl     switch(status) {
554171568Sscottl     case 0: // all is ok ...
555171568Sscottl	  sess->flags |= SESS_LOGGEDIN;
556171568Sscottl	  if(strcmp(sess->op->sessionType, "Discovery") == 0)
557171568Sscottl	       doDiscovery(sess);
558171568Sscottl	  return T5;
559171568Sscottl
560171568Sscottl     case 1:	// redirect - temporary/permanent
561171568Sscottl	  /*
562171568Sscottl	   | start from scratch?
563171568Sscottl	   */
564171568Sscottl	  sess->flags &= ~SESS_NEGODONE;
565171568Sscottl	  sess->flags |= (SESS_REDIRECT | SESS_INITIALLOGIN1);
566171568Sscottl	  syslog(LOG_DEBUG, "target sent REDIRECT");
567171568Sscottl	  return T7;
568171568Sscottl
569171568Sscottl     case 2: // initiator terminal error
570185289Sscottl	  return 0;
571171568Sscottl     case 3: // target terminal error -- could retry ...
572185289Sscottl	  sleep(5);
573185289Sscottl	  return T7; // lets try
574171568Sscottl     default:
575171568Sscottl	  return 0;
576171568Sscottl     }
577171568Sscottl}
578171568Sscottl
579171568Sscottlstatic int
580171568SscottlhandleLogoutResp(isess_t *sess, pdu_t *pp)
581171568Sscottl{
582171568Sscottl     if(sess->flags & SESS_DISCONNECT)
583171568Sscottl	  return 0;
584171568Sscottl     return T13;
585171568Sscottl}
586171568Sscottl
587171568Sscottlstatic trans_t
588171568SscottlstartLogout(isess_t *sess)
589171568Sscottl{
590171568Sscottl     pdu_t	spp;
591171568Sscottl     logout_req_t *p = (logout_req_t *)&spp.ipdu.bhs;
592171568Sscottl
593171568Sscottl     bzero(&spp, sizeof(pdu_t));
594171568Sscottl     p->cmd = ISCSI_LOGOUT_CMD| 0x40;
595171568Sscottl     p->reason = BIT(7) | 0;
596171568Sscottl     p->CID = htons(1);
597171568Sscottl
598171568Sscottl     return sendPDU(sess, &spp, handleLogoutResp);
599171568Sscottl}
600171568Sscottl
601171568Sscottlstatic trans_t
602171568SscottlinLogout(isess_t *sess)
603171568Sscottl{
604171568Sscottl     if(sess->flags & SESS_RECONNECT)
605171568Sscottl	  return T18;
606171568Sscottl     return 0;
607171568Sscottl}
608171568Sscottl
609171568Sscottltypedef enum {
610171568Sscottl     S1, S2, /*S3,*/ S4, S5, S6, S7, S8
611171568Sscottl} state_t;
612171568Sscottl
613171568Sscottl#if 0
614171568Sscottl      S1: FREE
615171568Sscottl      S2: XPT_WAIT
616171568Sscottl      S4: IN_LOGIN
617171568Sscottl      S5: LOGGED_IN
618171568Sscottl      S6: IN_LOGOUT
619171568Sscottl      S7: LOGOUT_REQUESTED
620171568Sscottl      S8: CLEANUP_WAIT
621171568Sscottl
622171568Sscottl                     -------<-------------+
623171568Sscottl         +--------->/ S1    \<----+       |
624171568Sscottl      T13|       +->\       /<-+   \      |
625171568Sscottl         |      /    ---+---    \   \     |
626171568Sscottl         |     /        |     T2 \   |    |
627171568Sscottl         |  T8 |        |T1       |  |    |
628171568Sscottl         |     |        |        /   |T7  |
629171568Sscottl         |     |        |       /    |    |
630171568Sscottl         |     |        |      /     |    |
631171568Sscottl         |     |        V     /     /     |
632171568Sscottl         |     |     ------- /     /      |
633171568Sscottl         |     |    / S2    \     /       |
634171568Sscottl         |     |    \       /    /        |
635171568Sscottl         |     |     ---+---    /         |
636171568Sscottl         |     |        |T4    /          |
637171568Sscottl         |     |        V     /           | T18
638171568Sscottl         |     |     ------- /            |
639171568Sscottl         |     |    / S4    \             |
640171568Sscottl         |     |    \       /             |
641171568Sscottl         |     |     ---+---              |         T15
642171568Sscottl         |     |        |T5      +--------+---------+
643171568Sscottl         |     |        |       /T16+-----+------+  |
644171568Sscottl         |     |        |      /   -+-----+--+   |  |
645171568Sscottl         |     |        |     /   /  S7   \  |T12|  |
646171568Sscottl         |     |        |    / +->\       /<-+   V  V
647171568Sscottl         |     |        |   / /    -+-----       -------
648171568Sscottl         |     |        |  / /T11   |T10        /  S8   \
649171568Sscottl         |     |        V / /       V  +----+   \       /
650171568Sscottl         |     |      ---+-+-      ----+--  |    -------
651171568Sscottl         |     |     / S5    \T9  / S6    \<+    ^
652171568Sscottl         |     +-----\       /--->\       / T14  |
653171568Sscottl         |            -------      --+----+------+T17
654171568Sscottl         +---------------------------+
655171568Sscottl#endif
656171568Sscottl
657171568Sscottlint
658171568Sscottlfsm(isc_opt_t *op)
659171568Sscottl{
660171568Sscottl     state_t	state;
661171568Sscottl     isess_t	*sess;
662171568Sscottl
663171568Sscottl     if((sess = calloc(1, sizeof(isess_t))) == NULL) {
664171568Sscottl	  // boy, is this a bad start ...
665171568Sscottl	  fprintf(stderr, "no memory!\n");
666171568Sscottl	  return -1;
667171568Sscottl     }
668171568Sscottl
669171568Sscottl     state = S1;
670171568Sscottl     sess->op = op;
671171568Sscottl     sess->fd = -1;
672171568Sscottl     sess->soc = -1;
673185289Sscottl     sess->target.address = strdup(op->targetAddress);
674185289Sscottl     sess->target.port = op->port;
675185289Sscottl     sess->target.pgt = op->targetPortalGroupTag;
676171568Sscottl
677171568Sscottl     sess->flags = SESS_INITIALLOGIN | SESS_INITIALLOGIN1;
678171568Sscottl
679171568Sscottl     do {
680171568Sscottl	  switch(state) {
681171568Sscottl
682171568Sscottl	  case S1:
683171568Sscottl	       switch(tcpConnect(sess)) {
684171568Sscottl	       case T1: state = S2; break;
685171568Sscottl	       default: state = S8; break;
686171568Sscottl	       }
687171568Sscottl	       break;
688171568Sscottl
689171568Sscottl	  case S2:
690171568Sscottl	       switch(startSession(sess)) {
691171568Sscottl	       case T2: state = S1; break;
692171568Sscottl	       case T4: state = S4; break;
693171568Sscottl	       default: state = S8; break;
694171568Sscottl	       }
695171568Sscottl	       break;
696171568Sscottl
697171568Sscottl	  case S4:
698171568Sscottl	       switch(doLogin(sess)) {
699171568Sscottl	       case T7:  state = S1; break;
700171568Sscottl	       case T5:  state = S5; break;
701171568Sscottl	       default: state = S8; break;
702171568Sscottl	       }
703171568Sscottl	       break;
704171568Sscottl
705171568Sscottl	  case S5:
706171568Sscottl	       switch(supervise(sess)) {
707171568Sscottl	       case T8:  state = S1; break;
708171568Sscottl	       case T9:  state = S6; break;
709171568Sscottl	       case T11: state = S7; break;
710171568Sscottl	       case T15: state = S8; break;
711171568Sscottl	       default: state = S8; break;
712171568Sscottl	       }
713171568Sscottl	       break;
714171568Sscottl
715171568Sscottl	  case S6:
716171568Sscottl	       switch(startLogout(sess)) {
717171568Sscottl	       case T13: state = S1; break;
718171568Sscottl	       case T14: state = S6; break;
719171568Sscottl	       case T16: state = S8; break;
720171568Sscottl	       default: state = S8; break;
721171568Sscottl	       }
722171568Sscottl	       break;
723171568Sscottl
724171568Sscottl	  case S7:
725171568Sscottl	       switch(inLogout(sess)) {
726171568Sscottl	       case T18: state = S1; break;
727171568Sscottl	       case T10: state = S6; break;
728171568Sscottl	       case T12: state = S7; break;
729171568Sscottl	       case T16: state = S8; break;
730171568Sscottl	       default: state = S8; break;
731171568Sscottl	       }
732171568Sscottl	       break;
733171568Sscottl
734171568Sscottl	  case S8:
735171568Sscottl	       // maybe do some clean up?
736171568Sscottl	       syslog(LOG_INFO, "terminated");
737171568Sscottl	       return 0;
738171568Sscottl	  }
739171568Sscottl     } while(1);
740171568Sscottl}
741