iscsictl.c revision 262843
1/*-
2 * Copyright (c) 2012 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Edward Tomasz Napierala under sponsorship
6 * from the FreeBSD Foundation.
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 AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $FreeBSD: stable/10/usr.bin/iscsictl/iscsictl.c 262843 2014-03-06 11:10:57Z trasz $
30 */
31
32#include <sys/ioctl.h>
33#include <sys/param.h>
34#include <sys/linker.h>
35#include <assert.h>
36#include <ctype.h>
37#include <err.h>
38#include <errno.h>
39#include <fcntl.h>
40#include <limits.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <unistd.h>
45
46#include <iscsi_ioctl.h>
47#include "iscsictl.h"
48
49struct conf *
50conf_new(void)
51{
52	struct conf *conf;
53
54	conf = calloc(1, sizeof(*conf));
55	if (conf == NULL)
56		err(1, "calloc");
57
58	TAILQ_INIT(&conf->conf_targets);
59
60	return (conf);
61}
62
63struct target *
64target_find(struct conf *conf, const char *nickname)
65{
66	struct target *targ;
67
68	TAILQ_FOREACH(targ, &conf->conf_targets, t_next) {
69		if (targ->t_nickname != NULL &&
70		    strcasecmp(targ->t_nickname, nickname) == 0)
71			return (targ);
72	}
73
74	return (NULL);
75}
76
77struct target *
78target_new(struct conf *conf)
79{
80	struct target *targ;
81
82	targ = calloc(1, sizeof(*targ));
83	if (targ == NULL)
84		err(1, "calloc");
85	targ->t_conf = conf;
86	TAILQ_INSERT_TAIL(&conf->conf_targets, targ, t_next);
87
88	return (targ);
89}
90
91void
92target_delete(struct target *targ)
93{
94
95	TAILQ_REMOVE(&targ->t_conf->conf_targets, targ, t_next);
96	free(targ);
97}
98
99
100static char *
101default_initiator_name(void)
102{
103	char *name;
104	size_t namelen;
105	int error;
106
107	namelen = _POSIX_HOST_NAME_MAX + strlen(DEFAULT_IQN);
108
109	name = calloc(1, namelen + 1);
110	if (name == NULL)
111		err(1, "calloc");
112	strcpy(name, DEFAULT_IQN);
113	error = gethostname(name + strlen(DEFAULT_IQN),
114	    namelen - strlen(DEFAULT_IQN));
115	if (error != 0)
116		err(1, "gethostname");
117
118	return (name);
119}
120
121static bool
122valid_hex(const char ch)
123{
124	switch (ch) {
125	case '0':
126	case '1':
127	case '2':
128	case '3':
129	case '4':
130	case '5':
131	case '6':
132	case '7':
133	case '8':
134	case '9':
135	case 'a':
136	case 'A':
137	case 'b':
138	case 'B':
139	case 'c':
140	case 'C':
141	case 'd':
142	case 'D':
143	case 'e':
144	case 'E':
145	case 'f':
146	case 'F':
147		return (true);
148	default:
149		return (false);
150	}
151}
152
153bool
154valid_iscsi_name(const char *name)
155{
156	int i;
157
158	if (strlen(name) >= MAX_NAME_LEN) {
159		warnx("overlong name for \"%s\"; max length allowed "
160		    "by iSCSI specification is %d characters",
161		    name, MAX_NAME_LEN);
162		return (false);
163	}
164
165	/*
166	 * In the cases below, we don't return an error, just in case the admin
167	 * was right, and we're wrong.
168	 */
169	if (strncasecmp(name, "iqn.", strlen("iqn.")) == 0) {
170		for (i = strlen("iqn."); name[i] != '\0'; i++) {
171			/*
172			 * XXX: We should verify UTF-8 normalisation, as defined
173			 * 	by 3.2.6.2: iSCSI Name Encoding.
174			 */
175			if (isalnum(name[i]))
176				continue;
177			if (name[i] == '-' || name[i] == '.' || name[i] == ':')
178				continue;
179			warnx("invalid character \"%c\" in iSCSI name "
180			    "\"%s\"; allowed characters are letters, digits, "
181			    "'-', '.', and ':'", name[i], name);
182			break;
183		}
184		/*
185		 * XXX: Check more stuff: valid date and a valid reversed domain.
186		 */
187	} else if (strncasecmp(name, "eui.", strlen("eui.")) == 0) {
188		if (strlen(name) != strlen("eui.") + 16)
189			warnx("invalid iSCSI name \"%s\"; the \"eui.\" "
190			    "should be followed by exactly 16 hexadecimal "
191			    "digits", name);
192		for (i = strlen("eui."); name[i] != '\0'; i++) {
193			if (!valid_hex(name[i])) {
194				warnx("invalid character \"%c\" in iSCSI "
195				    "name \"%s\"; allowed characters are 1-9 "
196				    "and A-F", name[i], name);
197				break;
198			}
199		}
200	} else if (strncasecmp(name, "naa.", strlen("naa.")) == 0) {
201		if (strlen(name) > strlen("naa.") + 32)
202			warnx("invalid iSCSI name \"%s\"; the \"naa.\" "
203			    "should be followed by at most 32 hexadecimal "
204			    "digits", name);
205		for (i = strlen("naa."); name[i] != '\0'; i++) {
206			if (!valid_hex(name[i])) {
207				warnx("invalid character \"%c\" in ISCSI "
208				    "name \"%s\"; allowed characters are 1-9 "
209				    "and A-F", name[i], name);
210				break;
211			}
212		}
213	} else {
214		warnx("invalid iSCSI name \"%s\"; should start with "
215		    "either \".iqn\", \"eui.\", or \"naa.\"",
216		    name);
217	}
218	return (true);
219}
220
221void
222conf_verify(struct conf *conf)
223{
224	struct target *targ;
225
226	TAILQ_FOREACH(targ, &conf->conf_targets, t_next) {
227		assert(targ->t_nickname != NULL);
228		if (targ->t_session_type == SESSION_TYPE_UNSPECIFIED)
229			targ->t_session_type = SESSION_TYPE_NORMAL;
230		if (targ->t_session_type == SESSION_TYPE_NORMAL &&
231		    targ->t_name == NULL)
232			errx(1, "missing TargetName for target \"%s\"",
233			    targ->t_nickname);
234		if (targ->t_session_type == SESSION_TYPE_DISCOVERY &&
235		    targ->t_name != NULL)
236			errx(1, "cannot specify TargetName for discovery "
237			    "sessions for target \"%s\"", targ->t_nickname);
238		if (targ->t_name != NULL) {
239			if (valid_iscsi_name(targ->t_name) == false)
240				errx(1, "invalid target name \"%s\"",
241				    targ->t_name);
242		}
243		if (targ->t_protocol == PROTOCOL_UNSPECIFIED)
244			targ->t_protocol = PROTOCOL_ISCSI;
245		if (targ->t_address == NULL)
246			errx(1, "missing TargetAddress for target \"%s\"",
247			    targ->t_nickname);
248		if (targ->t_initiator_name == NULL)
249			targ->t_initiator_name = default_initiator_name();
250		if (valid_iscsi_name(targ->t_initiator_name) == false)
251			errx(1, "invalid initiator name \"%s\"",
252			    targ->t_initiator_name);
253		if (targ->t_header_digest == DIGEST_UNSPECIFIED)
254			targ->t_header_digest = DIGEST_NONE;
255		if (targ->t_data_digest == DIGEST_UNSPECIFIED)
256			targ->t_data_digest = DIGEST_NONE;
257		if (targ->t_auth_method == AUTH_METHOD_UNSPECIFIED) {
258			if (targ->t_user != NULL || targ->t_secret != NULL ||
259			    targ->t_mutual_user != NULL ||
260			    targ->t_mutual_secret != NULL)
261				targ->t_auth_method =
262				    AUTH_METHOD_CHAP;
263			else
264				targ->t_auth_method =
265				    AUTH_METHOD_NONE;
266		}
267		if (targ->t_auth_method == AUTH_METHOD_CHAP) {
268			if (targ->t_user == NULL) {
269				errx(1, "missing chapIName for target \"%s\"",
270				    targ->t_nickname);
271			}
272			if (targ->t_secret == NULL)
273				errx(1, "missing chapSecret for target \"%s\"",
274				    targ->t_nickname);
275			if (targ->t_mutual_user != NULL ||
276			    targ->t_mutual_secret != NULL) {
277				if (targ->t_mutual_user == NULL)
278					errx(1, "missing tgtChapName for "
279					    "target \"%s\"", targ->t_nickname);
280				if (targ->t_mutual_secret == NULL)
281					errx(1, "missing tgtChapSecret for "
282					    "target \"%s\"", targ->t_nickname);
283			}
284		}
285	}
286}
287
288static void
289conf_from_target(struct iscsi_session_conf *conf,
290    const struct target *targ)
291{
292	memset(conf, 0, sizeof(*conf));
293
294	/*
295	 * XXX: Check bounds and return error instead of silently truncating.
296	 */
297	if (targ->t_initiator_name != NULL)
298		strlcpy(conf->isc_initiator, targ->t_initiator_name,
299		    sizeof(conf->isc_initiator));
300	if (targ->t_initiator_address != NULL)
301		strlcpy(conf->isc_initiator_addr, targ->t_initiator_address,
302		    sizeof(conf->isc_initiator_addr));
303	if (targ->t_initiator_alias != NULL)
304		strlcpy(conf->isc_initiator_alias, targ->t_initiator_alias,
305		    sizeof(conf->isc_initiator_alias));
306	if (targ->t_name != NULL)
307		strlcpy(conf->isc_target, targ->t_name,
308		    sizeof(conf->isc_target));
309	if (targ->t_address != NULL)
310		strlcpy(conf->isc_target_addr, targ->t_address,
311		    sizeof(conf->isc_target_addr));
312	if (targ->t_user != NULL)
313		strlcpy(conf->isc_user, targ->t_user,
314		    sizeof(conf->isc_user));
315	if (targ->t_secret != NULL)
316		strlcpy(conf->isc_secret, targ->t_secret,
317		    sizeof(conf->isc_secret));
318	if (targ->t_mutual_user != NULL)
319		strlcpy(conf->isc_mutual_user, targ->t_mutual_user,
320		    sizeof(conf->isc_mutual_user));
321	if (targ->t_mutual_secret != NULL)
322		strlcpy(conf->isc_mutual_secret, targ->t_mutual_secret,
323		    sizeof(conf->isc_mutual_secret));
324	if (targ->t_session_type == SESSION_TYPE_DISCOVERY)
325		conf->isc_discovery = 1;
326	if (targ->t_protocol == PROTOCOL_ISER)
327		conf->isc_iser = 1;
328	if (targ->t_header_digest == DIGEST_CRC32C)
329		conf->isc_header_digest = ISCSI_DIGEST_CRC32C;
330	else
331		conf->isc_header_digest = ISCSI_DIGEST_NONE;
332	if (targ->t_data_digest == DIGEST_CRC32C)
333		conf->isc_data_digest = ISCSI_DIGEST_CRC32C;
334	else
335		conf->isc_data_digest = ISCSI_DIGEST_NONE;
336}
337
338static int
339kernel_add(int iscsi_fd, const struct target *targ)
340{
341	struct iscsi_session_add isa;
342	int error;
343
344	memset(&isa, 0, sizeof(isa));
345	conf_from_target(&isa.isa_conf, targ);
346	error = ioctl(iscsi_fd, ISCSISADD, &isa);
347	if (error != 0)
348		warn("ISCSISADD");
349	return (error);
350}
351
352static int
353kernel_remove(int iscsi_fd, const struct target *targ)
354{
355	struct iscsi_session_remove isr;
356	int error;
357
358	memset(&isr, 0, sizeof(isr));
359	conf_from_target(&isr.isr_conf, targ);
360	error = ioctl(iscsi_fd, ISCSISREMOVE, &isr);
361	if (error != 0)
362		warn("ISCSISREMOVE");
363	return (error);
364}
365
366/*
367 * XXX: Add filtering.
368 */
369static int
370kernel_list(int iscsi_fd, const struct target *targ __unused,
371    int verbose)
372{
373	struct iscsi_session_state *states = NULL;
374	const struct iscsi_session_state *state;
375	const struct iscsi_session_conf *conf;
376	struct iscsi_session_list isl;
377	unsigned int i, nentries = 1;
378	int error;
379
380	for (;;) {
381		states = realloc(states,
382		    nentries * sizeof(struct iscsi_session_state));
383		if (states == NULL)
384			err(1, "realloc");
385
386		memset(&isl, 0, sizeof(isl));
387		isl.isl_nentries = nentries;
388		isl.isl_pstates = states;
389
390		error = ioctl(iscsi_fd, ISCSISLIST, &isl);
391		if (error != 0 && errno == EMSGSIZE) {
392			nentries *= 4;
393			continue;
394		}
395		break;
396	}
397	if (error != 0) {
398		warn("ISCSISLIST");
399		return (error);
400	}
401
402	if (verbose != 0) {
403		for (i = 0; i < isl.isl_nentries; i++) {
404			state = &states[i];
405			conf = &state->iss_conf;
406
407			printf("Session ID:       %d\n", state->iss_id);
408			printf("Initiator name:   %s\n", conf->isc_initiator);
409			printf("Initiator portal: %s\n",
410			    conf->isc_initiator_addr);
411			printf("Initiator alias:  %s\n",
412			    conf->isc_initiator_alias);
413			printf("Target name:      %s\n", conf->isc_target);
414			printf("Target portal:    %s\n",
415			    conf->isc_target_addr);
416			printf("Target alias:     %s\n",
417			    state->iss_target_alias);
418			printf("User:             %s\n", conf->isc_user);
419			printf("Secret:           %s\n", conf->isc_secret);
420			printf("Mutual user:      %s\n",
421			    conf->isc_mutual_user);
422			printf("Mutual secret:    %s\n",
423			    conf->isc_mutual_secret);
424			printf("Session type:     %s\n",
425			    conf->isc_discovery ? "Discovery" : "Normal");
426			printf("Session state:    %s\n",
427			    state->iss_connected ?
428			    "Connected" : "Disconnected");
429			printf("Failure reason:   %s\n", state->iss_reason);
430			printf("Header digest:    %s\n",
431			    state->iss_header_digest == ISCSI_DIGEST_CRC32C ?
432			    "CRC32C" : "None");
433			printf("Data digest:      %s\n",
434			    state->iss_data_digest == ISCSI_DIGEST_CRC32C ?
435			    "CRC32C" : "None");
436			printf("DataSegmentLen:   %d\n",
437			    state->iss_max_data_segment_length);
438			printf("ImmediateData:    %s\n",
439			    state->iss_immediate_data ? "Yes" : "No");
440			printf("iSER (RDMA):      %s\n",
441			    conf->isc_iser ? "Yes" : "No");
442			printf("Device nodes:     ");
443			print_periphs(state->iss_id);
444			printf("\n\n");
445		}
446	} else {
447		printf("%-36s %-16s %s\n",
448		    "Target name", "Target portal", "State");
449		for (i = 0; i < isl.isl_nentries; i++) {
450			state = &states[i];
451			conf = &state->iss_conf;
452
453			printf("%-36s %-16s ",
454			    conf->isc_target, conf->isc_target_addr);
455
456			if (state->iss_reason[0] != '\0') {
457				printf("%s\n", state->iss_reason);
458			} else {
459				if (conf->isc_discovery) {
460					printf("Discovery\n");
461				} else if (state->iss_connected) {
462					printf("Connected: ");
463					print_periphs(state->iss_id);
464					printf("\n");
465				} else {
466					printf("Disconnected\n");
467				}
468			}
469		}
470	}
471
472	return (0);
473}
474
475static void
476usage(void)
477{
478
479	fprintf(stderr, "usage: iscsictl -A -p portal -t target "
480	    "[-u user -s secret]\n");
481	fprintf(stderr, "       iscsictl -A -d discovery-host "
482	    "[-u user -s secret]\n");
483	fprintf(stderr, "       iscsictl -A -a [-c path]\n");
484	fprintf(stderr, "       iscsictl -A -n nickname [-c path]\n");
485	fprintf(stderr, "       iscsictl -R [-p portal] [-t target]\n");
486	fprintf(stderr, "       iscsictl -R -a\n");
487	fprintf(stderr, "       iscsictl -R -n nickname [-c path]\n");
488	fprintf(stderr, "       iscsictl -L [-v]\n");
489	exit(1);
490}
491
492char *
493checked_strdup(const char *s)
494{
495	char *c;
496
497	c = strdup(s);
498	if (c == NULL)
499		err(1, "strdup");
500	return (c);
501}
502
503int
504main(int argc, char **argv)
505{
506	int Aflag = 0, Rflag = 0, Lflag = 0, aflag = 0, vflag = 0;
507	const char *conf_path = DEFAULT_CONFIG_PATH;
508	char *nickname = NULL, *discovery_host = NULL, *host = NULL,
509	     *target = NULL, *user = NULL, *secret = NULL;
510	int ch, error, iscsi_fd, retval, saved_errno;
511	int failed = 0;
512	struct conf *conf;
513	struct target *targ;
514
515	while ((ch = getopt(argc, argv, "ARLac:d:n:p:t:u:s:v")) != -1) {
516		switch (ch) {
517		case 'A':
518			Aflag = 1;
519			break;
520		case 'R':
521			Rflag = 1;
522			break;
523		case 'L':
524			Lflag = 1;
525			break;
526		case 'a':
527			aflag = 1;
528			break;
529		case 'c':
530			conf_path = optarg;
531			break;
532		case 'd':
533			discovery_host = optarg;
534			break;
535		case 'n':
536			nickname = optarg;
537			break;
538		case 'p':
539			host = optarg;
540			break;
541		case 't':
542			target = optarg;
543			break;
544		case 'u':
545			user = optarg;
546			break;
547		case 's':
548			secret = optarg;
549			break;
550		case 'v':
551			vflag = 1;
552			break;
553		case '?':
554		default:
555			usage();
556		}
557	}
558	argc -= optind;
559	if (argc != 0)
560		usage();
561
562	if (Aflag + Rflag + Lflag == 0)
563		Lflag = 1;
564	if (Aflag + Rflag + Lflag > 1)
565		errx(1, "at most one of -A, -R, or -L may be specified");
566
567	/*
568	 * Note that we ignore unneccessary/inapplicable "-c" flag; so that
569	 * people can do something like "alias ISCSICTL="iscsictl -c path"
570	 * in shell scripts.
571	 */
572	if (Aflag != 0) {
573		if (aflag != 0) {
574			if (host != NULL)
575				errx(1, "-a and -p and mutually exclusive");
576			if (target != NULL)
577				errx(1, "-a and -t and mutually exclusive");
578			if (user != NULL)
579				errx(1, "-a and -u and mutually exclusive");
580			if (secret != NULL)
581				errx(1, "-a and -s and mutually exclusive");
582			if (nickname != NULL)
583				errx(1, "-a and -n and mutually exclusive");
584			if (discovery_host != NULL)
585				errx(1, "-a and -d and mutually exclusive");
586		} else if (nickname != NULL) {
587			if (host != NULL)
588				errx(1, "-n and -p and mutually exclusive");
589			if (target != NULL)
590				errx(1, "-n and -t and mutually exclusive");
591			if (user != NULL)
592				errx(1, "-n and -u and mutually exclusive");
593			if (secret != NULL)
594				errx(1, "-n and -s and mutually exclusive");
595			if (discovery_host != NULL)
596				errx(1, "-n and -d and mutually exclusive");
597		} else if (discovery_host != NULL) {
598			if (host != NULL)
599				errx(1, "-d and -p and mutually exclusive");
600			if (target != NULL)
601				errx(1, "-d and -t and mutually exclusive");
602		} else {
603			if (target == NULL && host == NULL)
604				errx(1, "must specify -a, -n or -t/-p");
605
606			if (target != NULL && host == NULL)
607				errx(1, "-t must always be used with -p");
608			if (host != NULL && target == NULL)
609				errx(1, "-p must always be used with -t");
610		}
611
612		if (user != NULL && secret == NULL)
613			errx(1, "-u must always be used with -s");
614		if (secret != NULL && user == NULL)
615			errx(1, "-s must always be used with -u");
616
617		if (vflag != 0)
618			errx(1, "-v cannot be used with -A");
619
620	} else if (Rflag != 0) {
621		if (user != NULL)
622			errx(1, "-R and -u are mutually exclusive");
623		if (secret != NULL)
624			errx(1, "-R and -s are mutually exclusive");
625		if (discovery_host != NULL)
626			errx(1, "-R and -d are mutually exclusive");
627
628		if (aflag != 0) {
629			if (host != NULL)
630				errx(1, "-a and -p and mutually exclusive");
631			if (target != NULL)
632				errx(1, "-a and -t and mutually exclusive");
633			if (nickname != NULL)
634				errx(1, "-a and -n and mutually exclusive");
635		} else if (nickname != NULL) {
636			if (host != NULL)
637				errx(1, "-n and -p and mutually exclusive");
638			if (target != NULL)
639				errx(1, "-n and -t and mutually exclusive");
640		} else if (host != NULL) {
641			if (target != NULL)
642				errx(1, "-p and -t and mutually exclusive");
643		} else if (target != NULL) {
644			if (host != NULL)
645				errx(1, "-t and -p and mutually exclusive");
646		} else
647			errx(1, "must specify either -a, -n, -t, or -p");
648
649		if (vflag != 0)
650			errx(1, "-v cannot be used with -R");
651
652	} else {
653		assert(Lflag != 0);
654
655		if (host != NULL)
656			errx(1, "-L and -p and mutually exclusive");
657		if (target != NULL)
658			errx(1, "-L and -t and mutually exclusive");
659		if (user != NULL)
660			errx(1, "-L and -u and mutually exclusive");
661		if (secret != NULL)
662			errx(1, "-L and -s and mutually exclusive");
663		if (nickname != NULL)
664			errx(1, "-L and -n and mutually exclusive");
665		if (discovery_host != NULL)
666			errx(1, "-L and -d and mutually exclusive");
667	}
668
669	iscsi_fd = open(ISCSI_PATH, O_RDWR);
670	if (iscsi_fd < 0 && errno == ENOENT) {
671		saved_errno = errno;
672		retval = kldload("iscsi");
673		if (retval != -1)
674			iscsi_fd = open(ISCSI_PATH, O_RDWR);
675		else
676			errno = saved_errno;
677	}
678	if (iscsi_fd < 0)
679		err(1, "failed to open %s", ISCSI_PATH);
680
681	if (Aflag != 0 && aflag != 0) {
682		conf = conf_new_from_file(conf_path);
683
684		TAILQ_FOREACH(targ, &conf->conf_targets, t_next)
685			failed += kernel_add(iscsi_fd, targ);
686	} else if (nickname != NULL) {
687		conf = conf_new_from_file(conf_path);
688		targ = target_find(conf, nickname);
689		if (targ == NULL)
690			errx(1, "target %s not found in the configuration file",
691			    nickname);
692
693		if (Aflag != 0)
694			failed += kernel_add(iscsi_fd, targ);
695		else if (Rflag != 0)
696			failed += kernel_remove(iscsi_fd, targ);
697		else
698			failed += kernel_list(iscsi_fd, targ, vflag);
699	} else {
700		if (Aflag != 0 && target != NULL) {
701			if (valid_iscsi_name(target) == false)
702				errx(1, "invalid target name \"%s\"", target);
703		}
704		conf = conf_new();
705		targ = target_new(conf);
706		targ->t_initiator_name = default_initiator_name();
707		targ->t_header_digest = DIGEST_NONE;
708		targ->t_data_digest = DIGEST_NONE;
709		targ->t_name = target;
710		if (discovery_host != NULL) {
711			targ->t_session_type = SESSION_TYPE_DISCOVERY;
712			targ->t_address = discovery_host;
713		} else {
714			targ->t_session_type = SESSION_TYPE_NORMAL;
715			targ->t_address = host;
716		}
717		targ->t_user = user;
718		targ->t_secret = secret;
719
720		if (Aflag != 0)
721			failed += kernel_add(iscsi_fd, targ);
722		else if (Rflag != 0)
723			failed += kernel_remove(iscsi_fd, targ);
724		else
725			failed += kernel_list(iscsi_fd, targ, vflag);
726	}
727
728	error = close(iscsi_fd);
729	if (error != 0)
730		err(1, "close");
731
732	if (failed > 0)
733		return (1);
734	return (0);
735}
736