iscsictl.c revision 262839
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 262839 2014-03-06 11:03:34Z 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#ifndef ICL_KERNEL_PROXY
246		if (targ->t_protocol == PROTOCOL_ISER)
247			errx(1, "iSER support requires ICL_KERNEL_PROXY; "
248			    "see iscsi(4) for details");
249#endif
250		if (targ->t_address == NULL)
251			errx(1, "missing TargetAddress for target \"%s\"",
252			    targ->t_nickname);
253		if (targ->t_initiator_name == NULL)
254			targ->t_initiator_name = default_initiator_name();
255		if (valid_iscsi_name(targ->t_initiator_name) == false)
256			errx(1, "invalid initiator name \"%s\"",
257			    targ->t_initiator_name);
258		if (targ->t_header_digest == DIGEST_UNSPECIFIED)
259			targ->t_header_digest = DIGEST_NONE;
260		if (targ->t_data_digest == DIGEST_UNSPECIFIED)
261			targ->t_data_digest = DIGEST_NONE;
262		if (targ->t_auth_method == AUTH_METHOD_UNSPECIFIED) {
263			if (targ->t_user != NULL || targ->t_secret != NULL ||
264			    targ->t_mutual_user != NULL ||
265			    targ->t_mutual_secret != NULL)
266				targ->t_auth_method =
267				    AUTH_METHOD_CHAP;
268			else
269				targ->t_auth_method =
270				    AUTH_METHOD_NONE;
271		}
272		if (targ->t_auth_method == AUTH_METHOD_CHAP) {
273			if (targ->t_user == NULL) {
274				errx(1, "missing chapIName for target \"%s\"",
275				    targ->t_nickname);
276			}
277			if (targ->t_secret == NULL)
278				errx(1, "missing chapSecret for target \"%s\"",
279				    targ->t_nickname);
280			if (targ->t_mutual_user != NULL ||
281			    targ->t_mutual_secret != NULL) {
282				if (targ->t_mutual_user == NULL)
283					errx(1, "missing tgtChapName for "
284					    "target \"%s\"", targ->t_nickname);
285				if (targ->t_mutual_secret == NULL)
286					errx(1, "missing tgtChapSecret for "
287					    "target \"%s\"", targ->t_nickname);
288			}
289		}
290	}
291}
292
293static void
294conf_from_target(struct iscsi_session_conf *conf,
295    const struct target *targ)
296{
297	memset(conf, 0, sizeof(*conf));
298
299	/*
300	 * XXX: Check bounds and return error instead of silently truncating.
301	 */
302	if (targ->t_initiator_name != NULL)
303		strlcpy(conf->isc_initiator, targ->t_initiator_name,
304		    sizeof(conf->isc_initiator));
305	if (targ->t_initiator_address != NULL)
306		strlcpy(conf->isc_initiator_addr, targ->t_initiator_address,
307		    sizeof(conf->isc_initiator_addr));
308	if (targ->t_initiator_alias != NULL)
309		strlcpy(conf->isc_initiator_alias, targ->t_initiator_alias,
310		    sizeof(conf->isc_initiator_alias));
311	if (targ->t_name != NULL)
312		strlcpy(conf->isc_target, targ->t_name,
313		    sizeof(conf->isc_target));
314	if (targ->t_address != NULL)
315		strlcpy(conf->isc_target_addr, targ->t_address,
316		    sizeof(conf->isc_target_addr));
317	if (targ->t_user != NULL)
318		strlcpy(conf->isc_user, targ->t_user,
319		    sizeof(conf->isc_user));
320	if (targ->t_secret != NULL)
321		strlcpy(conf->isc_secret, targ->t_secret,
322		    sizeof(conf->isc_secret));
323	if (targ->t_mutual_user != NULL)
324		strlcpy(conf->isc_mutual_user, targ->t_mutual_user,
325		    sizeof(conf->isc_mutual_user));
326	if (targ->t_mutual_secret != NULL)
327		strlcpy(conf->isc_mutual_secret, targ->t_mutual_secret,
328		    sizeof(conf->isc_mutual_secret));
329	if (targ->t_session_type == SESSION_TYPE_DISCOVERY)
330		conf->isc_discovery = 1;
331	if (targ->t_protocol == PROTOCOL_ISER)
332		conf->isc_iser = 1;
333	if (targ->t_header_digest == DIGEST_CRC32C)
334		conf->isc_header_digest = ISCSI_DIGEST_CRC32C;
335	else
336		conf->isc_header_digest = ISCSI_DIGEST_NONE;
337	if (targ->t_data_digest == DIGEST_CRC32C)
338		conf->isc_data_digest = ISCSI_DIGEST_CRC32C;
339	else
340		conf->isc_data_digest = ISCSI_DIGEST_NONE;
341}
342
343static int
344kernel_add(int iscsi_fd, const struct target *targ)
345{
346	struct iscsi_session_add isa;
347	int error;
348
349	memset(&isa, 0, sizeof(isa));
350	conf_from_target(&isa.isa_conf, targ);
351	error = ioctl(iscsi_fd, ISCSISADD, &isa);
352	if (error != 0)
353		warn("ISCSISADD");
354	return (error);
355}
356
357static int
358kernel_remove(int iscsi_fd, const struct target *targ)
359{
360	struct iscsi_session_remove isr;
361	int error;
362
363	memset(&isr, 0, sizeof(isr));
364	conf_from_target(&isr.isr_conf, targ);
365	error = ioctl(iscsi_fd, ISCSISREMOVE, &isr);
366	if (error != 0)
367		warn("ISCSISREMOVE");
368	return (error);
369}
370
371/*
372 * XXX: Add filtering.
373 */
374static int
375kernel_list(int iscsi_fd, const struct target *targ __unused,
376    int verbose)
377{
378	struct iscsi_session_state *states = NULL;
379	const struct iscsi_session_state *state;
380	const struct iscsi_session_conf *conf;
381	struct iscsi_session_list isl;
382	unsigned int i, nentries = 1;
383	int error;
384
385	for (;;) {
386		states = realloc(states,
387		    nentries * sizeof(struct iscsi_session_state));
388		if (states == NULL)
389			err(1, "realloc");
390
391		memset(&isl, 0, sizeof(isl));
392		isl.isl_nentries = nentries;
393		isl.isl_pstates = states;
394
395		error = ioctl(iscsi_fd, ISCSISLIST, &isl);
396		if (error != 0 && errno == EMSGSIZE) {
397			nentries *= 4;
398			continue;
399		}
400		break;
401	}
402	if (error != 0) {
403		warn("ISCSISLIST");
404		return (error);
405	}
406
407	if (verbose != 0) {
408		for (i = 0; i < isl.isl_nentries; i++) {
409			state = &states[i];
410			conf = &state->iss_conf;
411
412			printf("Session ID:       %d\n", state->iss_id);
413			printf("Initiator name:   %s\n", conf->isc_initiator);
414			printf("Initiator portal: %s\n",
415			    conf->isc_initiator_addr);
416			printf("Initiator alias:  %s\n",
417			    conf->isc_initiator_alias);
418			printf("Target name:      %s\n", conf->isc_target);
419			printf("Target portal:    %s\n",
420			    conf->isc_target_addr);
421			printf("Target alias:     %s\n",
422			    state->iss_target_alias);
423			printf("User:             %s\n", conf->isc_user);
424			printf("Secret:           %s\n", conf->isc_secret);
425			printf("Mutual user:      %s\n",
426			    conf->isc_mutual_user);
427			printf("Mutual secret:    %s\n",
428			    conf->isc_mutual_secret);
429			printf("Session type:     %s\n",
430			    conf->isc_discovery ? "Discovery" : "Normal");
431			printf("Session state:    %s\n",
432			    state->iss_connected ?
433			    "Connected" : "Disconnected");
434			printf("Failure reason:   %s\n", state->iss_reason);
435			printf("Header digest:    %s\n",
436			    state->iss_header_digest == ISCSI_DIGEST_CRC32C ?
437			    "CRC32C" : "None");
438			printf("Data digest:      %s\n",
439			    state->iss_data_digest == ISCSI_DIGEST_CRC32C ?
440			    "CRC32C" : "None");
441			printf("DataSegmentLen:   %d\n",
442			    state->iss_max_data_segment_length);
443			printf("ImmediateData:    %s\n",
444			    state->iss_immediate_data ? "Yes" : "No");
445			printf("iSER (RDMA):      %s\n",
446			    conf->isc_iser ? "Yes" : "No");
447			printf("Device nodes:     ");
448			print_periphs(state->iss_id);
449			printf("\n\n");
450		}
451	} else {
452		printf("%-36s %-16s %s\n",
453		    "Target name", "Target portal", "State");
454		for (i = 0; i < isl.isl_nentries; i++) {
455			state = &states[i];
456			conf = &state->iss_conf;
457
458			printf("%-36s %-16s ",
459			    conf->isc_target, conf->isc_target_addr);
460
461			if (state->iss_reason[0] != '\0') {
462				printf("%s\n", state->iss_reason);
463			} else {
464				if (conf->isc_discovery) {
465					printf("Discovery\n");
466				} else if (state->iss_connected) {
467					printf("Connected: ");
468					print_periphs(state->iss_id);
469					printf("\n");
470				} else {
471					printf("Disconnected\n");
472				}
473			}
474		}
475	}
476
477	return (0);
478}
479
480static void
481usage(void)
482{
483
484	fprintf(stderr, "usage: iscsictl -A -p portal -t target "
485	    "[-u user -s secret]\n");
486	fprintf(stderr, "       iscsictl -A -d discovery-host "
487	    "[-u user -s secret]\n");
488	fprintf(stderr, "       iscsictl -A -a [-c path]\n");
489	fprintf(stderr, "       iscsictl -A -n nickname [-c path]\n");
490	fprintf(stderr, "       iscsictl -R [-p portal] [-t target]\n");
491	fprintf(stderr, "       iscsictl -R -a\n");
492	fprintf(stderr, "       iscsictl -R -n nickname [-c path]\n");
493	fprintf(stderr, "       iscsictl -L [-v]\n");
494	exit(1);
495}
496
497char *
498checked_strdup(const char *s)
499{
500	char *c;
501
502	c = strdup(s);
503	if (c == NULL)
504		err(1, "strdup");
505	return (c);
506}
507
508int
509main(int argc, char **argv)
510{
511	int Aflag = 0, Rflag = 0, Lflag = 0, aflag = 0, vflag = 0;
512	const char *conf_path = DEFAULT_CONFIG_PATH;
513	char *nickname = NULL, *discovery_host = NULL, *host = NULL,
514	     *target = NULL, *user = NULL, *secret = NULL;
515	int ch, error, iscsi_fd, retval, saved_errno;
516	int failed = 0;
517	struct conf *conf;
518	struct target *targ;
519
520	while ((ch = getopt(argc, argv, "ARLac:d:n:p:t:u:s:v")) != -1) {
521		switch (ch) {
522		case 'A':
523			Aflag = 1;
524			break;
525		case 'R':
526			Rflag = 1;
527			break;
528		case 'L':
529			Lflag = 1;
530			break;
531		case 'a':
532			aflag = 1;
533			break;
534		case 'c':
535			conf_path = optarg;
536			break;
537		case 'd':
538			discovery_host = optarg;
539			break;
540		case 'n':
541			nickname = optarg;
542			break;
543		case 'p':
544			host = optarg;
545			break;
546		case 't':
547			target = optarg;
548			break;
549		case 'u':
550			user = optarg;
551			break;
552		case 's':
553			secret = optarg;
554			break;
555		case 'v':
556			vflag = 1;
557			break;
558		case '?':
559		default:
560			usage();
561		}
562	}
563	argc -= optind;
564	if (argc != 0)
565		usage();
566
567	if (Aflag + Rflag + Lflag == 0)
568		Lflag = 1;
569	if (Aflag + Rflag + Lflag > 1)
570		errx(1, "at most one of -A, -R, or -L may be specified");
571
572	/*
573	 * Note that we ignore unneccessary/inapplicable "-c" flag; so that
574	 * people can do something like "alias ISCSICTL="iscsictl -c path"
575	 * in shell scripts.
576	 */
577	if (Aflag != 0) {
578		if (aflag != 0) {
579			if (host != NULL)
580				errx(1, "-a and -p and mutually exclusive");
581			if (target != NULL)
582				errx(1, "-a and -t and mutually exclusive");
583			if (user != NULL)
584				errx(1, "-a and -u and mutually exclusive");
585			if (secret != NULL)
586				errx(1, "-a and -s and mutually exclusive");
587			if (nickname != NULL)
588				errx(1, "-a and -n and mutually exclusive");
589			if (discovery_host != NULL)
590				errx(1, "-a and -d and mutually exclusive");
591		} else if (nickname != NULL) {
592			if (host != NULL)
593				errx(1, "-n and -p and mutually exclusive");
594			if (target != NULL)
595				errx(1, "-n and -t and mutually exclusive");
596			if (user != NULL)
597				errx(1, "-n and -u and mutually exclusive");
598			if (secret != NULL)
599				errx(1, "-n and -s and mutually exclusive");
600			if (discovery_host != NULL)
601				errx(1, "-n and -d and mutually exclusive");
602		} else if (discovery_host != NULL) {
603			if (host != NULL)
604				errx(1, "-d and -p and mutually exclusive");
605			if (target != NULL)
606				errx(1, "-d and -t and mutually exclusive");
607		} else {
608			if (target == NULL && host == NULL)
609				errx(1, "must specify -a, -n or -t/-p");
610
611			if (target != NULL && host == NULL)
612				errx(1, "-t must always be used with -p");
613			if (host != NULL && target == NULL)
614				errx(1, "-p must always be used with -t");
615		}
616
617		if (user != NULL && secret == NULL)
618			errx(1, "-u must always be used with -s");
619		if (secret != NULL && user == NULL)
620			errx(1, "-s must always be used with -u");
621
622		if (vflag != 0)
623			errx(1, "-v cannot be used with -A");
624
625	} else if (Rflag != 0) {
626		if (user != NULL)
627			errx(1, "-R and -u are mutually exclusive");
628		if (secret != NULL)
629			errx(1, "-R and -s are mutually exclusive");
630		if (discovery_host != NULL)
631			errx(1, "-R and -d are mutually exclusive");
632
633		if (aflag != 0) {
634			if (host != NULL)
635				errx(1, "-a and -p and mutually exclusive");
636			if (target != NULL)
637				errx(1, "-a and -t and mutually exclusive");
638			if (nickname != NULL)
639				errx(1, "-a and -n and mutually exclusive");
640		} else if (nickname != NULL) {
641			if (host != NULL)
642				errx(1, "-n and -p and mutually exclusive");
643			if (target != NULL)
644				errx(1, "-n and -t and mutually exclusive");
645		} else if (host != NULL) {
646			if (target != NULL)
647				errx(1, "-p and -t and mutually exclusive");
648		} else if (target != NULL) {
649			if (host != NULL)
650				errx(1, "-t and -p and mutually exclusive");
651		} else
652			errx(1, "must specify either -a, -n, -t, or -p");
653
654		if (vflag != 0)
655			errx(1, "-v cannot be used with -R");
656
657	} else {
658		assert(Lflag != 0);
659
660		if (host != NULL)
661			errx(1, "-L and -p and mutually exclusive");
662		if (target != NULL)
663			errx(1, "-L and -t and mutually exclusive");
664		if (user != NULL)
665			errx(1, "-L and -u and mutually exclusive");
666		if (secret != NULL)
667			errx(1, "-L and -s and mutually exclusive");
668		if (nickname != NULL)
669			errx(1, "-L and -n and mutually exclusive");
670		if (discovery_host != NULL)
671			errx(1, "-L and -d and mutually exclusive");
672	}
673
674	iscsi_fd = open(ISCSI_PATH, O_RDWR);
675	if (iscsi_fd < 0 && errno == ENOENT) {
676		saved_errno = errno;
677		retval = kldload("iscsi");
678		if (retval != -1)
679			iscsi_fd = open(ISCSI_PATH, O_RDWR);
680		else
681			errno = saved_errno;
682	}
683	if (iscsi_fd < 0)
684		err(1, "failed to open %s", ISCSI_PATH);
685
686	if (Aflag != 0 && aflag != 0) {
687		conf = conf_new_from_file(conf_path);
688
689		TAILQ_FOREACH(targ, &conf->conf_targets, t_next)
690			failed += kernel_add(iscsi_fd, targ);
691	} else if (nickname != NULL) {
692		conf = conf_new_from_file(conf_path);
693		targ = target_find(conf, nickname);
694		if (targ == NULL)
695			errx(1, "target %s not found in the configuration file",
696			    nickname);
697
698		if (Aflag != 0)
699			failed += kernel_add(iscsi_fd, targ);
700		else if (Rflag != 0)
701			failed += kernel_remove(iscsi_fd, targ);
702		else
703			failed += kernel_list(iscsi_fd, targ, vflag);
704	} else {
705		if (Aflag != 0 && target != NULL) {
706			if (valid_iscsi_name(target) == false)
707				errx(1, "invalid target name \"%s\"", target);
708		}
709		conf = conf_new();
710		targ = target_new(conf);
711		targ->t_initiator_name = default_initiator_name();
712		targ->t_header_digest = DIGEST_NONE;
713		targ->t_data_digest = DIGEST_NONE;
714		targ->t_name = target;
715		if (discovery_host != NULL) {
716			targ->t_session_type = SESSION_TYPE_DISCOVERY;
717			targ->t_address = discovery_host;
718		} else {
719			targ->t_session_type = SESSION_TYPE_NORMAL;
720			targ->t_address = host;
721		}
722		targ->t_user = user;
723		targ->t_secret = secret;
724
725		if (Aflag != 0)
726			failed += kernel_add(iscsi_fd, targ);
727		else if (Rflag != 0)
728			failed += kernel_remove(iscsi_fd, targ);
729		else
730			failed += kernel_list(iscsi_fd, targ, vflag);
731	}
732
733	error = close(iscsi_fd);
734	if (error != 0)
735		err(1, "close");
736
737	if (failed > 0)
738		return (1);
739	return (0);
740}
741