xref: /netbsd-src/external/bsd/iscsi/dist/src/initiator/iscsi-initiator.c (revision 45b261d12f0e594311dd24457a980324e85b4b3a)
1 /*-
2  * Copyright (c) 2007,2009 The NetBSD Foundation, Inc.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to The NetBSD Foundation
6  * by Alistair Crooks (agc@netbsd.org)
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 #include <sys/types.h>
30 
31 #define FUSE_USE_VERSION	26
32 
33 #include <err.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <fuse.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 
42 #include "scsi_cmd_codes.h"
43 #include "iscsi.h"
44 #include "initiator.h"
45 #include "tests.h"
46 
47 #include "virtdir.h"
48 
49 #if defined(__NetBSD__) && defined(USE_LIBKMOD)
50 #include "libkmod.h"
51 #endif
52 
53 #include "defs.h"
54 
55 static int		 verbose; /* how chatty are we? */
56 
57 static virtdir_t	 iscsi;
58 
59 enum {
60 	VendorLen = 8,
61 	ProductLen = 16,
62 	VersionLen = 4,
63 
64 	SGsize = 131072
65 };
66 
67 
68 /* this struct keeps information on the target */
69 typedef struct targetinfo_t {
70 	 char			*host;		/* resolvable host name */
71 	 char			*ip;		/* textual IP address */
72 	 char			*targetname;	/* name of iSCSI target prog */
73 	 char			*stargetname;   /* short name of the target */
74 	 uint64_t		 target;	/* target number */
75 	 uint32_t		 lun;		/* LUN number */
76 	 uint32_t		 lbac;		/* number of LBAs */
77 	 uint32_t		 blocksize;	/* size of device blocks */
78 	 uint32_t		 devicetype;	/* SCSI device type */
79 	 char			 vendor[VendorLen + 1];
80 	 					/* device vendor information */
81 	 char			 product[ProductLen + 1];
82 	 					/* device product information */
83 	 char			 version[VersionLen + 1];
84 	 					/* device version information */
85 	 char			*serial;	/* unit serial number */
86 } targetinfo_t;
87 
88 DEFINE_ARRAY(targetv_t, targetinfo_t);
89 
90 static targetv_t	tv;	/* target vector of targetinfo_t structs */
91 
92 /* iqns and target addresses are returned as pairs in this dynamic array */
93 static strv_t		all_targets;
94 
95 /* Small Target Info... */
96 typedef struct sti_t {
97 	struct stat             st;		/* normal stat info */
98 	uint64_t                target;		/* cached target number, so we don't have an expensive pathname-based lookup */
99 } sti_t;
100 
101 #ifndef __UNCONST
102 #define __UNCONST(x)	(x)
103 #endif
104 
105 static void
lba2cdb(uint8_t * cdb,uint32_t * lba,uint16_t * len)106 lba2cdb(uint8_t *cdb, uint32_t *lba, uint16_t *len)
107 {
108 	/* Some platforms (like strongarm) aligns on */
109 	/* word boundaries.  So HTONL and NTOHL won't */
110 	/* work here. */
111 	int	little_endian = 1;
112 
113 	if (*(char *) (void *) &little_endian) {
114 		/* little endian */
115 		cdb[2] = ((uint8_t *) (void *)lba)[3];
116 		cdb[3] = ((uint8_t *) (void *)lba)[2];
117 		cdb[4] = ((uint8_t *) (void *)lba)[1];
118 		cdb[5] = ((uint8_t *) (void *)lba)[0];
119 		cdb[7] = ((uint8_t *) (void *)len)[1];
120 		cdb[8] = ((uint8_t *) (void *)len)[0];
121 	} else {
122 		/* big endian */
123 		cdb[2] = ((uint8_t *) (void *)lba)[2];
124 		cdb[3] = ((uint8_t *) (void *)lba)[3];
125 		cdb[4] = ((uint8_t *) (void *)lba)[0];
126 		cdb[5] = ((uint8_t *) (void *)lba)[1];
127 		cdb[7] = ((uint8_t *) (void *)len)[0];
128 		cdb[8] = ((uint8_t *) (void *)len)[1];
129 	}
130 }
131 
132 /* read the capacity (maximum LBA and blocksize) from the target */
133 int
read_capacity(uint64_t target,uint32_t lun,uint32_t * maxlba,uint32_t * blocklen)134 read_capacity(uint64_t target, uint32_t lun, uint32_t *maxlba, uint32_t *blocklen)
135 {
136 	iscsi_scsi_cmd_args_t	args;
137 	initiator_cmd_t		cmd;
138 	uint8_t			data[8];
139 	uint8_t			cdb[16];
140 
141 	(void) memset(cdb, 0x0, sizeof(cdb));
142 	cdb[0] = READ_CAPACITY;
143 	cdb[1] = lun << 5;
144 
145 	(void) memset(&args, 0x0, sizeof(args));
146 	args.recv_data = data;
147 	args.input = 1;
148 	args.lun = lun;
149 	args.trans_len = 8;
150 	args.cdb = cdb;
151 
152 	(void) memset(&cmd, 0, sizeof(initiator_cmd_t));
153 
154 	cmd.isid = target;
155 	cmd.type = ISCSI_SCSI_CMD;
156 	cmd.ptr = &args;
157 
158 	if (initiator_command(&cmd) != 0) {
159 		iscsi_err(__FILE__, __LINE__, "initiator_command() failed\n");
160 		return -1;
161 	}
162 	if (args.status) {
163 		iscsi_err(__FILE__, __LINE__, "READ_CAPACITY failed (status %#x)\n", args.status);
164 		return -1;
165 	}
166 	memcpy(maxlba, data, sizeof(*maxlba));
167 	*maxlba = ISCSI_NTOHL(*maxlba);
168 	if (*maxlba == 0) {
169 		iscsi_err(__FILE__, __LINE__, "Device returned Maximum LBA of zero\n");
170 		return -1;
171 	}
172 	memcpy(blocklen, data + 4, sizeof(*blocklen));
173 	*blocklen = ISCSI_NTOHL(*blocklen);
174 	if (*blocklen % 2) {
175 		iscsi_err(__FILE__, __LINE__, "Device returned strange block len: %u\n", *blocklen);
176 		return -1;
177 	}
178 	return 0;
179 }
180 
181 /* send inquiry command to the target, to get it to identify itself */
182 static int
inquiry(uint64_t target,uint32_t lun,uint8_t type,uint8_t inquire,uint8_t * data)183 inquiry(uint64_t target, uint32_t lun, uint8_t type, uint8_t inquire, uint8_t *data)
184 {
185 	iscsi_scsi_cmd_args_t	args;
186 	initiator_cmd_t		cmd;
187 	uint8_t			cdb[16];
188 
189 	(void) memset(cdb, 0x0, sizeof(cdb));
190 	cdb[0] = INQUIRY;
191 	cdb[1] = type | (lun << 5);
192 	cdb[2] = inquire;
193 	cdb[4] = 256 - 1;
194 
195 	(void) memset(&args, 0x0, sizeof(args));
196 	args.input = 1;
197 	args.trans_len = 256;
198 	args.cdb = cdb;
199 	args.lun = lun;
200 	args.recv_data = data;
201 	(void) memset(&cmd, 0x0, sizeof(cmd));
202 	cmd.isid = target;
203 	cmd.type = ISCSI_SCSI_CMD;
204 	cmd.ptr = &args;
205 
206 	if (initiator_command(&cmd) != 0) {
207 		iscsi_err(__FILE__, __LINE__, "initiator_command() failed\n");
208 		return -1;
209 	}
210 	if (args.status) {
211 		iscsi_err(__FILE__, __LINE__, "INQUIRY failed (status %#x)\n", args.status);
212 		return -1;
213 	}
214 
215 	return 0;
216 }
217 
218 /* read or write a single block of information */
219 static int
blockop(uint64_t target,uint32_t lun,uint32_t lba,uint32_t len,uint32_t blocklen,uint8_t * data,int writing)220 blockop(uint64_t target, uint32_t lun, uint32_t lba, uint32_t len,
221 	      uint32_t blocklen, uint8_t *data, int writing)
222 {
223 	iscsi_scsi_cmd_args_t	args;
224 	initiator_cmd_t		cmd;
225 	uint16_t   		readlen;
226 	uint8_t   		cdb[16];
227 
228 	/* Build CDB */
229 	(void) memset(cdb, 0, 16);
230 	cdb[0] = (writing) ? WRITE_10 : READ_10;
231 	cdb[1] = lun << 5;
232 	readlen = (uint16_t) len;
233 	lba2cdb(cdb, &lba, &readlen);
234 
235 	/* Build SCSI command */
236 	(void) memset(&args, 0x0, sizeof(args));
237 	if (writing) {
238 		args.send_data = data;
239 		args.output = 1;
240 	} else {
241 		args.recv_data = data;
242 		args.input = 1;
243 	}
244 	args.lun = lun;
245 	args.trans_len = len*blocklen;
246 	args.length = len*blocklen;
247 	args.cdb = cdb;
248 	(void) memset(&cmd, 0, sizeof(initiator_cmd_t));
249 	cmd.isid = target;
250 	cmd.type = ISCSI_SCSI_CMD;
251 	cmd.ptr = &args;
252 	/* Execute iSCSI command */
253 	if (initiator_command(&cmd) != 0) {
254 		iscsi_err(__FILE__, __LINE__, "initiator_command() failed\n");
255 		return -1;
256 	}
257 
258 	if (args.status) {
259 		iscsi_err(__FILE__, __LINE__, "scsi_command() failed (status %#x)\n", args.status);
260 		return -1;
261 	}
262 	return 0;
263 }
264 
265 /* perform a scatter/gather block operation */
266 static int
sgblockop(uint64_t target,uint32_t lun,uint32_t lba,uint32_t len,uint32_t blocklen,uint8_t * data,int sglen,int writing)267 sgblockop(uint64_t target, uint32_t lun, uint32_t lba, uint32_t len,
268 	      uint32_t blocklen, uint8_t *data, int sglen, int writing)
269 {
270 	iscsi_scsi_cmd_args_t	args;
271 	initiator_cmd_t		cmd;
272 	uint16_t		readlen;
273 	uint8_t			cdb[16];
274 
275 	/* Build CDB */
276 
277 	(void) memset(cdb, 0, 16);
278 	cdb[0] = (writing) ? WRITE_10 : READ_10;
279 	cdb[1] = lun << 5;
280 	readlen = (uint16_t) len;
281 	lba2cdb(cdb, &lba, &readlen);
282 
283 	/* Build iSCSI command */
284 	(void) memset(&args, 0x0, sizeof(args));
285 	args.lun = lun;
286 	args.output = (writing) ? 1 : 0;
287 	args.input = (writing) ? 0 : 1;
288 	args.trans_len = len * blocklen;
289 	args.length = len * blocklen;
290 	args.send_data = (writing) ? data : NULL;
291 	args.send_sg_len = (writing) ? sglen : 0;
292 	args.recv_data = (writing) ? NULL : data;
293 	args.recv_sg_len = (writing) ? 0 : sglen;
294 	args.cdb = cdb;
295 	memset(&cmd, 0, sizeof(initiator_cmd_t));
296 	cmd.isid = target;
297 	cmd.ptr = &args;
298 	cmd.type = ISCSI_SCSI_CMD;
299 
300 	/* Execute iSCSI command */
301 
302 	if (initiator_command(&cmd) != 0) {
303 		iscsi_err(__FILE__, __LINE__, "initiator_command() failed\n");
304 		return -1;
305 	}
306 	if (args.status) {
307 		iscsi_err(__FILE__, __LINE__, "scsi_command() failed (status %#x)\n", args.status);
308 		return -1;
309 	}
310 	return 0;
311 }
312 
313 /* read info from the target - method depends on size of data being read */
314 static int
targetop(uint32_t t,uint64_t offset,uint32_t length,uint32_t request,char * buf,int writing)315 targetop(uint32_t t, uint64_t offset, uint32_t length, uint32_t request, char *buf, int writing)
316 {
317 	struct iovec 	*iov;
318 	uint32_t	 ioc;
319 	uint32_t	 i;
320 	int		 req_len;
321 
322 	if (request > SGsize) {
323 		/* split up request into blocksize chunks */
324 		ioc = request / SGsize;
325 		if ((ioc * SGsize) < request)
326 			ioc++;
327 		if ((iov = iscsi_malloc(ioc * sizeof(*iov))) == NULL) {
328 			iscsi_err(__FILE__, __LINE__, "out of memory\n");
329 			return -1;
330 		}
331 
332 		for (i = 0 ; i < ioc ; i++) {
333 			iov[i].iov_base = &buf[i * SGsize];
334 			if (i == (ioc - 1)) { /* last one */
335 				iov[i].iov_len = request - (i * SGsize);
336 			} else {
337 				iov[i].iov_len = SGsize;
338 			}
339 		}
340 
341 		if (sgblockop(tv.v[t].target, tv.v[t].lun, offset / tv.v[t].blocksize, (length / tv.v[t].blocksize), tv.v[t].blocksize, (uint8_t *) iov, ioc, writing) != 0) {
342 			iscsi_free(iov);
343 			iscsi_err(__FILE__, __LINE__, "read_10() failed\n");
344 			return -1;
345 		}
346 		iscsi_free(iov);
347 	} else {
348 		req_len = length / tv.v[t].blocksize;
349 		if ((req_len * tv.v[t].blocksize) < length)
350 			req_len++;
351 		if (blockop(tv.v[t].target, tv.v[t].lun, offset / tv.v[t].blocksize,
352 				req_len, tv.v[t].blocksize, (uint8_t *) buf, writing) != 0) {
353 			iscsi_err(__FILE__, __LINE__, "read_10() failed\n");
354 			return -1;
355 		}
356 	}
357 	return 0;
358 }
359 
360 
361 /****************************************************************************/
362 
363 /* perform the stat operation */
364 /* if this is the root, then just synthesise the data */
365 /* otherwise, retrieve the data, and be sure to fill in the size */
366 static int
iscsifs_getattr(const char * path,struct stat * st)367 iscsifs_getattr(const char *path, struct stat *st)
368 {
369 	virt_dirent_t	*ep;
370 	sti_t		*p;
371 
372 	if (strcmp(path, "/") == 0) {
373 		(void) memset(st, 0x0, sizeof(*st));
374 		st->st_mode = S_IFDIR | 0755;
375 		st->st_nlink = 2;
376 		return 0;
377 	}
378 	if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) {
379 		return -ENOENT;
380 	}
381 	switch(ep->type) {
382 	case 'b':
383 		(void) memcpy(st, &iscsi.file, sizeof(*st));
384 		st->st_mode = (S_IFBLK | 0644);
385 		break;
386 	case 'c':
387 		(void) memcpy(st, &iscsi.file, sizeof(*st));
388 		st->st_mode = (S_IFCHR | 0644);
389 		break;
390 	case 'd':
391 		(void) memcpy(st, &iscsi.dir, sizeof(*st));
392 		break;
393 	case 'f':
394 		(void) memcpy(st, &iscsi.file, sizeof(*st));
395 		p = (sti_t *) ep->tgt;
396 		st->st_size = p->st.st_size;
397 		break;
398 	case 'l':
399 		(void) memcpy(st, &iscsi.lnk, sizeof(*st));
400 		st->st_size = ep->tgtlen;
401 		break;
402 	default:
403 		warn("unknown directory type `%c'", ep->type);
404 		return -ENOENT;
405 	}
406 	st->st_ino = ep->ino;
407 	return 0;
408 }
409 
410 /* readdir operation */
411 static int
iscsifs_readdir(const char * path,void * buf,fuse_fill_dir_t filler,off_t offset,struct fuse_file_info * fi)412 iscsifs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
413 	      off_t offset, struct fuse_file_info * fi)
414 {
415 	virt_dirent_t	*dp;
416 	VIRTDIR		*dirp;
417 
418 	if ((dirp = openvirtdir(&iscsi, path)) == NULL) {
419 		return 0;
420 	}
421 	filler(buf, ".", NULL, 0);
422 	filler(buf, "..", NULL, 0);
423 	while ((dp = readvirtdir(dirp)) != NULL) {
424 		filler(buf, dp->d_name, NULL, 0);
425 	}
426 	closevirtdir(dirp);
427 	return 0;
428 }
429 
430 /* open the file in the file system */
431 static int
iscsifs_open(const char * path,struct fuse_file_info * fi)432 iscsifs_open(const char *path, struct fuse_file_info *fi)
433 {
434 	virt_dirent_t	*ep;
435 	const char	*slash;
436 
437 	if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) {
438 		return -ENOENT;
439 	}
440 	/* check path is the correct one */
441 	if ((slash = strrchr(path, '/')) == NULL) {
442 		slash = path;
443 	} else {
444 		slash += 1;
445 	}
446 	if (strcmp(slash, "storage") != 0) {
447 		return -ENOENT;
448 	}
449 	return 0;
450 }
451 
452 /* read the storage from the iSCSI target */
453 static int
iscsifs_read(const char * path,char * buf,size_t size,off_t offset,struct fuse_file_info * fi)454 iscsifs_read(const char *path, char *buf, size_t size, off_t offset,
455 	   struct fuse_file_info * fi)
456 {
457 	virt_dirent_t	*ep;
458 	uint64_t target;
459 	sti_t *p;
460 
461 	if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) {
462 		return -ENOENT;
463 	}
464 
465 	p = (sti_t *)ep->tgt;
466 	target = p->target;
467 
468 	if (targetop(target, offset, size, size, buf, 0) < 0) {
469 		return -EPERM;
470 	}
471 	return size;
472 }
473 
474 /* write the file's contents to the file system */
475 static int
iscsifs_write(const char * path,const char * buf,size_t size,off_t offset,struct fuse_file_info * fi)476 iscsifs_write(const char *path, const char *buf, size_t size, off_t offset,
477 	   struct fuse_file_info * fi)
478 {
479 	virt_dirent_t	*ep;
480         uint64_t	 target;
481         sti_t		*p;
482 
483 	if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) {
484 		return -ENOENT;
485 	}
486 
487 	p = (sti_t *)ep->tgt;
488 	target = p->target;
489 
490 	if (targetop(target, offset, size, size, __UNCONST(buf), 1) < 0) {
491 		return -EPERM;
492 	}
493 	return size;
494 }
495 
496 /* fill in the statvfs struct */
497 static int
iscsifs_statfs(const char * path,struct statvfs * st)498 iscsifs_statfs(const char *path, struct statvfs *st)
499 {
500 	(void) memset(st, 0x0, sizeof(*st));
501 	return 0;
502 }
503 
504 /* read the symbolic link */
505 static int
iscsifs_readlink(const char * path,char * buf,size_t size)506 iscsifs_readlink(const char *path, char *buf, size_t size)
507 {
508 	virt_dirent_t	*ep;
509 
510 	if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) {
511 		return -ENOENT;
512 	}
513 	if (ep->tgt == NULL) {
514 		return -ENOENT;
515 	}
516 	(void) strlcpy(buf, ep->tgt, size);
517 	return 0;
518 }
519 
520 /* operations struct */
521 static struct fuse_operations iscsiops = {
522 	.getattr = iscsifs_getattr,
523 	.readlink = iscsifs_readlink,
524 	.readdir = iscsifs_readdir,
525 	.open = iscsifs_open,
526 	.read = iscsifs_read,
527 	.write = iscsifs_write,
528 	.statfs = iscsifs_statfs
529 };
530 
531 int
main(int argc,char ** argv)532 main(int argc, char **argv)
533 {
534 	iscsi_initiator_t	ini;
535 	initiator_target_t	tinfo;
536 	unsigned		u;
537 	uint32_t		lbac;
538 	uint32_t		blocksize;
539 	uint8_t			data[256];
540 	sti_t			sti;
541 	char			hostname[1024];
542 	char			name[1024];
543 	char			*colon;
544 	char		       *host;
545 	char			buf[32];
546 	char			devtype;
547 	int			discover;
548 	int			cc;
549 	int			i;
550 	uint32_t		max_targets;
551 
552 	(void) memset(&tinfo, 0x0, sizeof(tinfo));
553 	iscsi_initiator_set_defaults(&ini);
554 	(void) gethostname(host = hostname, sizeof(hostname));
555 	discover = 0;
556 	(void) stat("/etc/hosts", &sti.st);
557 	devtype = 'f';
558 	iscsi_initiator_setvar(&ini, "address family", "4");
559 	max_targets = iscsi_initiator_get_max_targets();
560 
561 	while ((i = getopt(argc, argv, "46a:bcd:Dfh:p:t:u:v:V")) != -1) {
562 		switch(i) {
563 		case '4':
564 		case '6':
565 			buf[0] = i;
566 			buf[1] = 0x0;
567 			iscsi_initiator_setvar(&ini, "address family", buf);
568 			break;
569 		case 'a':
570 			iscsi_initiator_setvar(&ini, "auth type", optarg);
571 			break;
572 		case 'b':
573 			devtype = 'b';
574 			break;
575 		case 'c':
576 			devtype = 'c';
577 			break;
578 		case 'd':
579 			iscsi_initiator_setvar(&ini, "digest type", optarg);
580 			break;
581 		case 'D':
582 			discover = 1;
583 			break;
584 		case 'f':
585 			devtype = 'f';
586 			break;
587 		case 'h':
588 			iscsi_initiator_setvar(&ini, "target hostname", optarg);
589 			break;
590 		case 'p':
591 			iscsi_initiator_setvar(&ini, "target port", optarg);
592 			break;
593 		case 't':
594 			iscsi_initiator_setvar(&ini, "target instance", optarg);
595 			break;
596 		case 'u':
597 			iscsi_initiator_setvar(&ini, "user", optarg);
598 			break;
599 		case 'V':
600 			(void) printf("\"%s\" %s\nPlease send all bug reports "
601 					"to %s\n",
602 					PACKAGE_NAME,
603 					PACKAGE_VERSION,
604 					PACKAGE_BUGREPORT);
605 			exit(EXIT_SUCCESS);
606 			/* NOTREACHED */
607 		case 'v':
608 			verbose += 1;
609 			if (strcmp(optarg, "net") == 0) {
610 				iscsi_initiator_setvar(&ini, "debug", "net");
611 			} else if (strcmp(optarg, "iscsi") == 0) {
612 				iscsi_initiator_setvar(&ini, "debug", "iscsi");
613 			} else if (strcmp(optarg, "scsi") == 0) {
614 				iscsi_initiator_setvar(&ini, "debug", "scsi");
615 			} else if (strcmp(optarg, "all") == 0) {
616 				iscsi_initiator_setvar(&ini, "debug", "all");
617 			}
618 			break;
619 		default:
620 			(void) fprintf(stderr, "%s: unknown option `%c'",
621 					*argv, i);
622 		}
623 	}
624 	if (!strcmp(iscsi_initiator_getvar(&ini, "auth type"), "chap") &&
625 	    iscsi_initiator_getvar(&ini, "user") == NULL) {
626 		iscsi_err(__FILE__, __LINE__, "user must be specified with "
627 		    "-u if using CHAP authentication\n");
628 		exit(EXIT_FAILURE);
629 	}
630 
631 	if (strcmp(iscsi_initiator_getvar(&ini, "auth type"), "none") &&
632 	    iscsi_initiator_getvar(&ini, "user") != NULL) {
633 		/*
634 		 * For backwards compatibility, default to using CHAP
635 		 * if username given
636 		 */
637 		iscsi_initiator_setvar(&ini, "auth type", "chap");
638 	}
639 
640 	if (iscsi_initiator_start(&ini) == -1) {
641 		iscsi_err(__FILE__, __LINE__, "initiator_init() failed\n");
642 		exit(EXIT_FAILURE);
643 	}
644 
645         if (iscsi_initiator_discover(host, 0, 0) < 0) {
646                 printf("initiator_discover() in discover failed\n");
647 		exit(EXIT_FAILURE);
648 	}
649 
650         if (iscsi_initiator_get_targets(0,&all_targets) == -1) {
651  		iscsi_err(__FILE__, __LINE__,
652 			"initiator_get_targets() failed\n");
653                	exit(EXIT_FAILURE);
654 	}
655 
656 
657         if (discover) {
658 		printf("Targets available from host %s:\n",host);
659 		for (u = 0; u < all_targets.c ; u += 2) {
660 			printf("%s at %s\n", all_targets.v[u],
661 				all_targets.v[u + 1]);
662 		}
663 
664                 exit(EXIT_SUCCESS);
665         }
666 
667 	if (all_targets.c/2 > max_targets) {
668 		(void) fprintf(stderr,
669 			"CONFIG_INITIATOR_NUM_TARGETS in initiator.h "
670 			"is too small.  %d targets available, "
671 			"only %d configurable.\n",
672 			all_targets.c/2, max_targets);
673 		(void) fprintf(stderr,
674 			"To increase this value, libiscsi will have be "
675 			"recompiled.\n");
676 		(void) fprintf(stderr,
677 			"Truncating number of targets to %d.\n",
678 			max_targets);
679 		all_targets.c = 2 * max_targets;
680 	}
681 
682 	sti.st.st_ino = 0x15c51;
683 
684 #if defined(__NetBSD__) && defined(USE_LIBKMOD)
685 	/* check that the puffs module is loaded on NetBSD */
686 	if (kmodstat("puffs", NULL) == 0 && !kmodload("puffs")) {
687 		(void) fprintf(stderr, "initiator: can't load puffs module\n");
688 	}
689 #endif
690 
691 	for (u = 0 ; u < all_targets.c / 2 ; u++) {
692 		ALLOC(targetinfo_t, tv.v, tv.size, tv.c, 10, 10, "iscsifs",
693 				exit(EXIT_FAILURE));
694 
695 		initiator_set_target_name(u, all_targets.v[u * 2]);
696 
697 		if (iscsi_initiator_discover(host, u, 0) < 0) {
698 			printf("iscsi_initiator_discover() failed\n");
699 			break;
700 		}
701 
702 		get_target_info(u, &tinfo);
703 		if ((colon = strrchr(tinfo.TargetName, ':')) == NULL) {
704 			colon = tinfo.TargetName;
705 		} else {
706 			colon += 1;
707 		}
708 
709 		/* stuff size into st.st_size */
710 		{
711 			int retry = 5;
712 			while (retry > 0) {
713 				if (read_capacity(u, 0, &lbac, &blocksize) == 0)
714 					break;
715 				retry--;
716 				iscsi_warn(__FILE__, __LINE__,
717 				    "read_capacity failed - retrying %d\n", retry);
718 				sleep(1);
719 			}
720 			if (retry == 0) {
721 				iscsi_err(__FILE__, __LINE__, "read_capacity failed - giving up\n");
722 				break;
723 			}
724 		}
725 		sti.st.st_size = (off_t)(((uint64_t)lbac + 1) * blocksize);
726 		sti.target = u;
727 
728 		tv.v[tv.c].host = strdup(tinfo.name);
729 		tv.v[tv.c].ip = strdup(tinfo.ip);
730 		tv.v[tv.c].targetname = strdup(tinfo.TargetName);
731 		tv.v[tv.c].stargetname = strdup(colon);
732 		tv.v[tv.c].target = u;
733 		tv.v[tv.c].lun = 0;
734 		tv.v[tv.c].lbac = lbac;
735 		tv.v[tv.c].blocksize = blocksize;
736 
737 		/* get iSCSI target information */
738 		(void) memset(data, 0x0, sizeof(data));
739 		inquiry(u, 0, 0, 0, data);
740 		tv.v[tv.c].devicetype = (data[0] & 0x1f);
741 		(void) memcpy(tv.v[tv.c].vendor, &data[8], VendorLen);
742 		(void) memcpy(tv.v[tv.c].product, &data[8 + VendorLen],
743 				ProductLen);
744 		(void) memcpy(tv.v[tv.c].version,
745 				&data[8 + VendorLen + ProductLen], VersionLen);
746 		(void) memset(data, 0x0, sizeof(data));
747 		inquiry(u, 0, INQUIRY_EVPD_BIT, INQUIRY_UNIT_SERIAL_NUMBER_VPD,
748 				data);
749 		tv.v[tv.c].serial = strdup((char *)&data[4]);
750 
751 		/* create the tree using virtdir routines */
752 		cc = snprintf(name, sizeof(name), "/%s", colon);
753 		virtdir_add(&iscsi, name, cc, 'd', name, cc);
754 		cc = snprintf(name, sizeof(name), "/%s/storage", colon);
755 		virtdir_add(&iscsi, name, cc, devtype, (void *)&sti,
756 				sizeof(sti));
757 		cc = snprintf(name, sizeof(name), "/%s/hostname", colon);
758 		virtdir_add(&iscsi, name, cc, 'l', tinfo.name,
759 				strlen(tinfo.name));
760 		cc = snprintf(name, sizeof(name), "/%s/ip", colon);
761 		virtdir_add(&iscsi, name, cc, 'l', tinfo.ip, strlen(tinfo.ip));
762 		cc = snprintf(name, sizeof(name), "/%s/targetname", colon);
763 		virtdir_add(&iscsi, name, cc, 'l', tinfo.TargetName,
764 				strlen(tinfo.TargetName));
765 		cc = snprintf(name, sizeof(name), "/%s/vendor", colon);
766 		virtdir_add(&iscsi, name, cc, 'l', tv.v[tv.c].vendor,
767 				strlen(tv.v[tv.c].vendor));
768 		cc = snprintf(name, sizeof(name), "/%s/product", colon);
769 		virtdir_add(&iscsi, name, cc, 'l', tv.v[tv.c].product,
770 				strlen(tv.v[tv.c].product));
771 		cc = snprintf(name, sizeof(name), "/%s/version", colon);
772 		virtdir_add(&iscsi, name, cc, 'l', tv.v[tv.c].version,
773 				strlen(tv.v[tv.c].version));
774 		if (tv.v[tv.c].serial[0] && tv.v[tv.c].serial[0] != ' ') {
775 			cc = snprintf(name, sizeof(name), "/%s/serial",
776 				colon);
777 			virtdir_add(&iscsi, name, cc, 'l', tv.v[tv.c].serial,
778 				strlen(tv.v[tv.c].serial));
779 		}
780 		tv.c += 1;
781 	}
782 	return fuse_main(argc - optind, argv + optind, &iscsiops, NULL);
783 }
784