xref: /netbsd-src/external/bsd/iscsi/dist/src/initiator/iscsi-initiator.c (revision abb0f93cd77b67f080613360c65701f85e5f5cfe)
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
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
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 	*maxlba = ISCSI_NTOHL(*((uint32_t *) (data)));
167 	*blocklen = ISCSI_NTOHL(*((uint32_t *) (data + 4)));
168 	if (*maxlba == 0) {
169 		iscsi_err(__FILE__, __LINE__, "Device returned Maximum LBA of zero\n");
170 		return -1;
171 	}
172 	if (*blocklen % 2) {
173 		iscsi_err(__FILE__, __LINE__, "Device returned strange block len: %u\n", *blocklen);
174 		return -1;
175 	}
176 	return 0;
177 }
178 
179 /* send inquiry command to the target, to get it to identify itself */
180 static int
181 inquiry(uint64_t target, uint32_t lun, uint8_t type, uint8_t inquire, uint8_t *data)
182 {
183 	iscsi_scsi_cmd_args_t	args;
184 	initiator_cmd_t		cmd;
185 	uint8_t			cdb[16];
186 
187 	(void) memset(cdb, 0x0, sizeof(cdb));
188 	cdb[0] = INQUIRY;
189 	cdb[1] = type | (lun << 5);
190 	cdb[2] = inquire;
191 	cdb[4] = 256 - 1;
192 
193 	(void) memset(&args, 0x0, sizeof(args));
194 	args.input = 1;
195 	args.trans_len = 256;
196 	args.cdb = cdb;
197 	args.lun = lun;
198 	args.recv_data = data;
199 	(void) memset(&cmd, 0x0, sizeof(cmd));
200 	cmd.isid = target;
201 	cmd.type = ISCSI_SCSI_CMD;
202 	cmd.ptr = &args;
203 
204 	if (initiator_command(&cmd) != 0) {
205 		iscsi_err(__FILE__, __LINE__, "initiator_command() failed\n");
206 		return -1;
207 	}
208 	if (args.status) {
209 		iscsi_err(__FILE__, __LINE__, "INQUIRY failed (status %#x)\n", args.status);
210 		return -1;
211 	}
212 
213 	return 0;
214 }
215 
216 /* read or write a single block of information */
217 static int
218 blockop(uint64_t target, uint32_t lun, uint32_t lba, uint32_t len,
219 	      uint32_t blocklen, uint8_t *data, int writing)
220 {
221 	iscsi_scsi_cmd_args_t	args;
222 	initiator_cmd_t		cmd;
223 	uint16_t   		readlen;
224 	uint8_t   		cdb[16];
225 
226 	/* Build CDB */
227 	(void) memset(cdb, 0, 16);
228 	cdb[0] = (writing) ? WRITE_10 : READ_10;
229 	cdb[1] = lun << 5;
230 	readlen = (uint16_t) len;
231 	lba2cdb(cdb, &lba, &readlen);
232 
233 	/* Build SCSI command */
234 	(void) memset(&args, 0x0, sizeof(args));
235 	if (writing) {
236 		args.send_data = data;
237 		args.output = 1;
238 	} else {
239 		args.recv_data = data;
240 		args.input = 1;
241 	}
242 	args.lun = lun;
243 	args.trans_len = len*blocklen;
244 	args.length = len*blocklen;
245 	args.cdb = cdb;
246 	(void) memset(&cmd, 0, sizeof(initiator_cmd_t));
247 	cmd.isid = target;
248 	cmd.type = ISCSI_SCSI_CMD;
249 	cmd.ptr = &args;
250 	/* Execute iSCSI command */
251 	if (initiator_command(&cmd) != 0) {
252 		iscsi_err(__FILE__, __LINE__, "initiator_command() failed\n");
253 		return -1;
254 	}
255 
256 	if (args.status) {
257 		iscsi_err(__FILE__, __LINE__, "scsi_command() failed (status %#x)\n", args.status);
258 		return -1;
259 	}
260 	return 0;
261 }
262 
263 /* perform a scatter/gather block operation */
264 static int
265 sgblockop(uint64_t target, uint32_t lun, uint32_t lba, uint32_t len,
266 	      uint32_t blocklen, uint8_t *data, int sglen, int writing)
267 {
268 	iscsi_scsi_cmd_args_t	args;
269 	initiator_cmd_t		cmd;
270 	uint16_t		readlen;
271 	uint8_t			cdb[16];
272 
273 	/* Build CDB */
274 
275 	(void) memset(cdb, 0, 16);
276 	cdb[0] = (writing) ? WRITE_10 : READ_10;
277 	cdb[1] = lun << 5;
278 	readlen = (uint16_t) len;
279 	lba2cdb(cdb, &lba, &readlen);
280 
281 	/* Build iSCSI command */
282 	(void) memset(&args, 0x0, sizeof(args));
283 	args.lun = lun;
284 	args.output = (writing) ? 1 : 0;
285 	args.input = (writing) ? 0 : 1;
286 	args.trans_len = len * blocklen;
287 	args.length = len * blocklen;
288 	args.send_data = (writing) ? data : NULL;
289 	args.send_sg_len = (writing) ? sglen : 0;
290 	args.recv_data = (writing) ? NULL : data;
291 	args.recv_sg_len = (writing) ? 0 : sglen;
292 	args.cdb = cdb;
293 	memset(&cmd, 0, sizeof(initiator_cmd_t));
294 	cmd.isid = target;
295 	cmd.ptr = &args;
296 	cmd.type = ISCSI_SCSI_CMD;
297 
298 	/* Execute iSCSI command */
299 
300 	if (initiator_command(&cmd) != 0) {
301 		iscsi_err(__FILE__, __LINE__, "initiator_command() failed\n");
302 		return -1;
303 	}
304 	if (args.status) {
305 		iscsi_err(__FILE__, __LINE__, "scsi_command() failed (status %#x)\n", args.status);
306 		return -1;
307 	}
308 	return 0;
309 }
310 
311 /* read info from the target - method depends on size of data being read */
312 static int
313 targetop(uint32_t t, uint64_t offset, uint32_t length, uint32_t request, char *buf, int writing)
314 {
315 	struct iovec 	*iov;
316 	uint32_t	 ioc;
317 	uint32_t	 i;
318 	int		 req_len;
319 
320 	if (request > SGsize) {
321 		/* split up request into blocksize chunks */
322 		ioc = request / SGsize;
323 		if ((ioc * SGsize) < request)
324 			ioc++;
325 		if ((iov = iscsi_malloc(ioc * sizeof(*iov))) == NULL) {
326 			iscsi_err(__FILE__, __LINE__, "out of memory\n");
327 			return -1;
328 		}
329 
330 		for (i = 0 ; i < ioc ; i++) {
331 			iov[i].iov_base = &buf[i * SGsize];
332 			if (i == (ioc - 1)) { /* last one */
333 				iov[i].iov_len = request - (i * SGsize);
334 			} else {
335 				iov[i].iov_len = SGsize;
336 			}
337 		}
338 
339 		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) {
340 			iscsi_free(iov);
341 			iscsi_err(__FILE__, __LINE__, "read_10() failed\n");
342 			return -1;
343 		}
344 		iscsi_free(iov);
345 	} else {
346 		req_len = length / tv.v[t].blocksize;
347 		if ((req_len * tv.v[t].blocksize) < length)
348 			req_len++;
349 		if (blockop(tv.v[t].target, tv.v[t].lun, offset / tv.v[t].blocksize,
350 				req_len, tv.v[t].blocksize, (uint8_t *) buf, writing) != 0) {
351 			iscsi_err(__FILE__, __LINE__, "read_10() failed\n");
352 			return -1;
353 		}
354 	}
355 	return 0;
356 }
357 
358 
359 /****************************************************************************/
360 
361 /* perform the stat operation */
362 /* if this is the root, then just synthesise the data */
363 /* otherwise, retrieve the data, and be sure to fill in the size */
364 static int
365 iscsifs_getattr(const char *path, struct stat *st)
366 {
367 	virt_dirent_t	*ep;
368 	sti_t		*p;
369 
370 	if (strcmp(path, "/") == 0) {
371 		(void) memset(st, 0x0, sizeof(*st));
372 		st->st_mode = S_IFDIR | 0755;
373 		st->st_nlink = 2;
374 		return 0;
375 	}
376 	if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) {
377 		return -ENOENT;
378 	}
379 	switch(ep->type) {
380 	case 'b':
381 		(void) memcpy(st, &iscsi.file, sizeof(*st));
382 		st->st_mode = (S_IFBLK | 0644);
383 		break;
384 	case 'c':
385 		(void) memcpy(st, &iscsi.file, sizeof(*st));
386 		st->st_mode = (S_IFCHR | 0644);
387 		break;
388 	case 'd':
389 		(void) memcpy(st, &iscsi.dir, sizeof(*st));
390 		break;
391 	case 'f':
392 		(void) memcpy(st, &iscsi.file, sizeof(*st));
393 		p = (sti_t *) ep->tgt;
394 		st->st_size = p->st.st_size;
395 		break;
396 	case 'l':
397 		(void) memcpy(st, &iscsi.lnk, sizeof(*st));
398 		st->st_size = ep->tgtlen;
399 		break;
400 	default:
401 		warn("unknown directory type `%c'", ep->type);
402 		return -ENOENT;
403 	}
404 	st->st_ino = ep->ino;
405 	return 0;
406 }
407 
408 /* readdir operation */
409 static int
410 iscsifs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
411 	      off_t offset, struct fuse_file_info * fi)
412 {
413 	virt_dirent_t	*dp;
414 	VIRTDIR		*dirp;
415 
416 	if ((dirp = openvirtdir(&iscsi, path)) == NULL) {
417 		return 0;
418 	}
419 	filler(buf, ".", NULL, 0);
420 	filler(buf, "..", NULL, 0);
421 	while ((dp = readvirtdir(dirp)) != NULL) {
422 		filler(buf, dp->d_name, NULL, 0);
423 	}
424 	closevirtdir(dirp);
425 	return 0;
426 }
427 
428 /* open the file in the file system */
429 static int
430 iscsifs_open(const char *path, struct fuse_file_info *fi)
431 {
432 	virt_dirent_t	*ep;
433 	const char	*slash;
434 
435 	if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) {
436 		return -ENOENT;
437 	}
438 	/* check path is the correct one */
439 	if ((slash = strrchr(path, '/')) == NULL) {
440 		slash = path;
441 	} else {
442 		slash += 1;
443 	}
444 	if (strcmp(slash, "storage") != 0) {
445 		return -ENOENT;
446 	}
447 	return 0;
448 }
449 
450 /* read the storage from the iSCSI target */
451 static int
452 iscsifs_read(const char *path, char *buf, size_t size, off_t offset,
453 	   struct fuse_file_info * fi)
454 {
455 	virt_dirent_t	*ep;
456 	uint64_t target;
457 	sti_t *p;
458 
459 	if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) {
460 		return -ENOENT;
461 	}
462 
463 	p = (sti_t *)ep->tgt;
464 	target = p->target;
465 
466 	if (targetop(target, offset, size, size, buf, 0) < 0) {
467 		return -EPERM;
468 	}
469 	return size;
470 }
471 
472 /* write the file's contents to the file system */
473 static int
474 iscsifs_write(const char *path, const char *buf, size_t size, off_t offset,
475 	   struct fuse_file_info * fi)
476 {
477 	virt_dirent_t	*ep;
478         uint64_t	 target;
479         sti_t		*p;
480 
481 	if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) {
482 		return -ENOENT;
483 	}
484 
485 	p = (sti_t *)ep->tgt;
486 	target = p->target;
487 
488 	if (targetop(target, offset, size, size, __UNCONST(buf), 1) < 0) {
489 		return -EPERM;
490 	}
491 	return size;
492 }
493 
494 /* fill in the statvfs struct */
495 static int
496 iscsifs_statfs(const char *path, struct statvfs *st)
497 {
498 	(void) memset(st, 0x0, sizeof(*st));
499 	return 0;
500 }
501 
502 /* read the symbolic link */
503 static int
504 iscsifs_readlink(const char *path, char *buf, size_t size)
505 {
506 	virt_dirent_t	*ep;
507 
508 	if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) {
509 		return -ENOENT;
510 	}
511 	if (ep->tgt == NULL) {
512 		return -ENOENT;
513 	}
514 	(void) strlcpy(buf, ep->tgt, size);
515 	return 0;
516 }
517 
518 /* operations struct */
519 static struct fuse_operations iscsiops = {
520 	.getattr = iscsifs_getattr,
521 	.readlink = iscsifs_readlink,
522 	.readdir = iscsifs_readdir,
523 	.open = iscsifs_open,
524 	.read = iscsifs_read,
525 	.write = iscsifs_write,
526 	.statfs = iscsifs_statfs
527 };
528 
529 int
530 main(int argc, char **argv)
531 {
532 	iscsi_initiator_t	ini;
533 	initiator_target_t	tinfo;
534 	unsigned		u;
535 	uint32_t		lbac;
536 	uint32_t		blocksize;
537 	uint8_t			data[256];
538 	sti_t			sti;
539 	char			hostname[1024];
540 	char			name[1024];
541 	char			*colon;
542 	char		       *host;
543 	char		       *user;
544 	char			buf[32];
545 	char			devtype;
546 	int			discover;
547 	int			cc;
548 	int			i;
549 
550 	(void) memset(&tinfo, 0x0, sizeof(tinfo));
551 	iscsi_initiator_set_defaults(&ini);
552 	user = NULL;
553 	(void) gethostname(host = hostname, sizeof(hostname));
554 	discover = 0;
555 	(void) stat("/etc/hosts", &sti.st);
556 	devtype = 'f';
557 	iscsi_initiator_setvar(&ini, "address family", "4");
558 	while ((i = getopt(argc, argv, "46a:bcd:Dfh:p:t:u:v:V")) != -1) {
559 		switch(i) {
560 		case '4':
561 		case '6':
562 			buf[0] = i;
563 			buf[1] = 0x0;
564 			iscsi_initiator_setvar(&ini, "address family", buf);
565 			break;
566 		case 'a':
567 			iscsi_initiator_setvar(&ini, "auth type", optarg);
568 			break;
569 		case 'b':
570 			devtype = 'b';
571 			break;
572 		case 'c':
573 			devtype = 'c';
574 			break;
575 		case 'd':
576 			iscsi_initiator_setvar(&ini, "digest type", optarg);
577 			break;
578 		case 'D':
579 			discover = 1;
580 			break;
581 		case 'f':
582 			devtype = 'f';
583 			break;
584 		case 'h':
585 			iscsi_initiator_setvar(&ini, "target hostname", optarg);
586 			break;
587 		case 'p':
588 			iscsi_initiator_setvar(&ini, "target port", optarg);
589 			break;
590 		case 't':
591 			iscsi_initiator_setvar(&ini, "target instance", optarg);
592 			break;
593 		case 'u':
594 			iscsi_initiator_setvar(&ini, "user", optarg);
595 			break;
596 		case 'V':
597 			(void) printf("\"%s\" %s\nPlease send all bug reports "
598 					"to %s\n",
599 					PACKAGE_NAME,
600 					PACKAGE_VERSION,
601 					PACKAGE_BUGREPORT);
602 			exit(EXIT_SUCCESS);
603 			/* NOTREACHED */
604 		case 'v':
605 			verbose += 1;
606 			if (strcmp(optarg, "net") == 0) {
607 				iscsi_initiator_setvar(&ini, "debug", "net");
608 			} else if (strcmp(optarg, "iscsi") == 0) {
609 				iscsi_initiator_setvar(&ini, "debug", "iscsi");
610 			} else if (strcmp(optarg, "scsi") == 0) {
611 				iscsi_initiator_setvar(&ini, "debug", "scsi");
612 			} else if (strcmp(optarg, "all") == 0) {
613 				iscsi_initiator_setvar(&ini, "debug", "all");
614 			}
615 			break;
616 		default:
617 			(void) fprintf(stderr, "%s: unknown option `%c'",
618 					*argv, i);
619 		}
620 	}
621 	if (iscsi_initiator_getvar(&ini, "user") == NULL) {
622 		iscsi_err(__FILE__, __LINE__, "user must be specified with -u\n");
623 		exit(EXIT_FAILURE);
624 	}
625 
626 	if (iscsi_initiator_start(&ini) == -1) {
627 		iscsi_err(__FILE__, __LINE__, "initiator_init() failed\n");
628 		exit(EXIT_FAILURE);
629 	}
630 
631         if (iscsi_initiator_discover(host, 0, 0) < 0) {
632                 printf("initiator_discover() in discover failed\n");
633 		exit(EXIT_FAILURE);
634 	}
635 
636         if (iscsi_initiator_get_targets(0,&all_targets) == -1) {
637  		iscsi_err(__FILE__, __LINE__,
638 			"initiator_get_targets() failed\n");
639                	exit(EXIT_FAILURE);
640 	}
641 
642 
643         if (discover) {
644 		printf("Targets available from host %s:\n",host);
645 		for (u = 0; u < all_targets.c ; u += 2) {
646 			printf("%s at %s\n", all_targets.v[u],
647 				all_targets.v[u + 1]);
648 		}
649 
650                 exit(EXIT_SUCCESS);
651         }
652 
653 	if (all_targets.c/2 > CONFIG_INITIATOR_NUM_TARGETS) {
654 		(void) fprintf(stderr,
655 			"CONFIG_INITIATOR_NUM_TARGETS in initiator.h "
656 			"is too small.  %d targets available, "
657 			"only %d configurable.\n",
658 			all_targets.c/2, CONFIG_INITIATOR_NUM_TARGETS);
659 		(void) fprintf(stderr,
660 			"Truncating number of targets to %d.\n",
661 			CONFIG_INITIATOR_NUM_TARGETS);
662 		all_targets.c = CONFIG_INITIATOR_NUM_TARGETS;
663 	}
664 
665 	sti.st.st_ino = 0x15c51;
666 
667 #if defined(__NetBSD__) && defined(USE_LIBKMOD)
668 	/* check that the puffs module is loaded on NetBSD */
669 	if (kmodstat("puffs", NULL) == 0 && !kmodload("puffs")) {
670 		(void) fprintf(stderr, "initiator: can't load puffs module\n");
671 	}
672 #endif
673 
674 	for (u = 0 ; u < all_targets.c / 2 ; u++) {
675 		ALLOC(targetinfo_t, tv.v, tv.size, tv.c, 10, 10, "iscsifs",
676 				exit(EXIT_FAILURE));
677 
678 		initiator_set_target_name(u, all_targets.v[u * 2]);
679 
680 		if (iscsi_initiator_discover(host, u, 0) < 0) {
681 			printf("iscsi_initiator_discover() failed\n");
682 			break;
683 		}
684 
685 		get_target_info(u, &tinfo);
686 		if ((colon = strrchr(tinfo.TargetName, ':')) == NULL) {
687 			colon = tinfo.TargetName;
688 		} else {
689 			colon += 1;
690 		}
691 
692 		/* stuff size into st.st_size */
693 		(void) read_capacity(u, 0, &lbac, &blocksize);
694 		sti.st.st_size = ((uint64_t)lbac + 1) * blocksize;
695 		sti.target = u;
696 
697 		tv.v[tv.c].host = strdup(tinfo.name);
698 		tv.v[tv.c].ip = strdup(tinfo.ip);
699 		tv.v[tv.c].targetname = strdup(tinfo.TargetName);
700 		tv.v[tv.c].stargetname = strdup(colon);
701 		tv.v[tv.c].target = u;
702 		tv.v[tv.c].lun = 0;
703 		tv.v[tv.c].lbac = lbac;
704 		tv.v[tv.c].blocksize = blocksize;
705 
706 		/* get iSCSI target information */
707 		(void) memset(data, 0x0, sizeof(data));
708 		inquiry(u, 0, 0, 0, data);
709 		tv.v[tv.c].devicetype = (data[0] & 0x1f);
710 		(void) memcpy(tv.v[tv.c].vendor, &data[8], VendorLen);
711 		(void) memcpy(tv.v[tv.c].product, &data[8 + VendorLen],
712 				ProductLen);
713 		(void) memcpy(tv.v[tv.c].version,
714 				&data[8 + VendorLen + ProductLen], VersionLen);
715 		(void) memset(data, 0x0, sizeof(data));
716 		inquiry(u, 0, INQUIRY_EVPD_BIT, INQUIRY_UNIT_SERIAL_NUMBER_VPD,
717 				data);
718 		tv.v[tv.c].serial = strdup((char *)&data[4]);
719 
720 		/* create the tree using virtdir routines */
721 		cc = snprintf(name, sizeof(name), "/%s/%s", host, colon);
722 		virtdir_add(&iscsi, name, cc, 'd', name, cc);
723 		cc = snprintf(name, sizeof(name), "/%s/%s/storage", host,
724 				colon);
725 		virtdir_add(&iscsi, name, cc, devtype, (void *)&sti,
726 				sizeof(sti));
727 		cc = snprintf(name, sizeof(name), "/%s/%s/hostname", host,
728 				colon);
729 		virtdir_add(&iscsi, name, cc, 'l', tinfo.name,
730 				strlen(tinfo.name));
731 		cc = snprintf(name, sizeof(name), "/%s/%s/ip", host, colon);
732 		virtdir_add(&iscsi, name, cc, 'l', tinfo.ip, strlen(tinfo.ip));
733 		cc = snprintf(name, sizeof(name), "/%s/%s/targetname", host,
734 				colon);
735 		virtdir_add(&iscsi, name, cc, 'l', tinfo.TargetName,
736 				strlen(tinfo.TargetName));
737 		cc = snprintf(name, sizeof(name), "/%s/%s/vendor", host, colon);
738 		virtdir_add(&iscsi, name, cc, 'l', tv.v[tv.c].vendor,
739 				strlen(tv.v[tv.c].vendor));
740 		cc = snprintf(name, sizeof(name), "/%s/%s/product", host,
741 				colon);
742 		virtdir_add(&iscsi, name, cc, 'l', tv.v[tv.c].product,
743 				strlen(tv.v[tv.c].product));
744 		cc = snprintf(name, sizeof(name), "/%s/%s/version", host,
745 				colon);
746 		virtdir_add(&iscsi, name, cc, 'l', tv.v[tv.c].version,
747 				strlen(tv.v[tv.c].version));
748 		if (tv.v[tv.c].serial[0] && tv.v[tv.c].serial[0] != ' ') {
749 			cc = snprintf(name, sizeof(name), "/%s/%s/serial",
750 				host, colon);
751 			virtdir_add(&iscsi, name, cc, 'l', tv.v[tv.c].serial,
752 				strlen(tv.v[tv.c].serial));
753 		}
754 		tv.c += 1;
755 	}
756 	return fuse_main(argc - optind, argv + optind, &iscsiops, NULL);
757 }
758