iscsictl.c revision 269969
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 269969 2014-08-14 12:33:24Z 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_modify(int iscsi_fd, unsigned int session_id, const struct target *targ)
354{
355	struct iscsi_session_modify ism;
356	int error;
357
358	memset(&ism, 0, sizeof(ism));
359	ism.ism_session_id = session_id;
360	conf_from_target(&ism.ism_conf, targ);
361	error = ioctl(iscsi_fd, ISCSISMODIFY, &ism);
362	if (error != 0)
363		warn("ISCSISMODIFY");
364	return (error);
365}
366
367static void
368kernel_modify_some(int iscsi_fd, unsigned int session_id, const char *target,
369  const char *target_addr, const char *user, const char *secret)
370{
371	struct iscsi_session_state *states = NULL;
372	struct iscsi_session_state *state;
373	struct iscsi_session_conf *conf;
374	struct iscsi_session_list isl;
375	struct iscsi_session_modify ism;
376	unsigned int i, nentries = 1;
377	int error;
378
379	for (;;) {
380		states = realloc(states,
381		    nentries * sizeof(struct iscsi_session_state));
382		if (states == NULL)
383			err(1, "realloc");
384
385		memset(&isl, 0, sizeof(isl));
386		isl.isl_nentries = nentries;
387		isl.isl_pstates = states;
388
389		error = ioctl(iscsi_fd, ISCSISLIST, &isl);
390		if (error != 0 && errno == EMSGSIZE) {
391			nentries *= 4;
392			continue;
393		}
394		break;
395	}
396	if (error != 0)
397		errx(1, "ISCSISLIST");
398
399	for (i = 0; i < isl.isl_nentries; i++) {
400		state = &states[i];
401
402		if (state->iss_id == session_id)
403			break;
404	}
405	if (i == isl.isl_nentries)
406		errx(1, "session-id %u not found", session_id);
407
408	conf = &state->iss_conf;
409
410	if (target != NULL)
411		strlcpy(conf->isc_target, target, sizeof(conf->isc_target));
412	if (target_addr != NULL)
413		strlcpy(conf->isc_target_addr, target_addr,
414		    sizeof(conf->isc_target_addr));
415	if (user != NULL)
416		strlcpy(conf->isc_user, user, sizeof(conf->isc_user));
417	if (secret != NULL)
418		strlcpy(conf->isc_secret, secret, sizeof(conf->isc_secret));
419
420	memset(&ism, 0, sizeof(ism));
421	ism.ism_session_id = session_id;
422	memcpy(&ism.ism_conf, conf, sizeof(ism.ism_conf));
423	error = ioctl(iscsi_fd, ISCSISMODIFY, &ism);
424	if (error != 0)
425		warn("ISCSISMODIFY");
426}
427
428static int
429kernel_remove(int iscsi_fd, const struct target *targ)
430{
431	struct iscsi_session_remove isr;
432	int error;
433
434	memset(&isr, 0, sizeof(isr));
435	conf_from_target(&isr.isr_conf, targ);
436	error = ioctl(iscsi_fd, ISCSISREMOVE, &isr);
437	if (error != 0)
438		warn("ISCSISREMOVE");
439	return (error);
440}
441
442/*
443 * XXX: Add filtering.
444 */
445static int
446kernel_list(int iscsi_fd, const struct target *targ __unused,
447    int verbose)
448{
449	struct iscsi_session_state *states = NULL;
450	const struct iscsi_session_state *state;
451	const struct iscsi_session_conf *conf;
452	struct iscsi_session_list isl;
453	unsigned int i, nentries = 1;
454	int error;
455
456	for (;;) {
457		states = realloc(states,
458		    nentries * sizeof(struct iscsi_session_state));
459		if (states == NULL)
460			err(1, "realloc");
461
462		memset(&isl, 0, sizeof(isl));
463		isl.isl_nentries = nentries;
464		isl.isl_pstates = states;
465
466		error = ioctl(iscsi_fd, ISCSISLIST, &isl);
467		if (error != 0 && errno == EMSGSIZE) {
468			nentries *= 4;
469			continue;
470		}
471		break;
472	}
473	if (error != 0) {
474		warn("ISCSISLIST");
475		return (error);
476	}
477
478	if (verbose != 0) {
479		for (i = 0; i < isl.isl_nentries; i++) {
480			state = &states[i];
481			conf = &state->iss_conf;
482
483			printf("Session ID:       %u\n", state->iss_id);
484			printf("Initiator name:   %s\n", conf->isc_initiator);
485			printf("Initiator portal: %s\n",
486			    conf->isc_initiator_addr);
487			printf("Initiator alias:  %s\n",
488			    conf->isc_initiator_alias);
489			printf("Target name:      %s\n", conf->isc_target);
490			printf("Target portal:    %s\n",
491			    conf->isc_target_addr);
492			printf("Target alias:     %s\n",
493			    state->iss_target_alias);
494			printf("User:             %s\n", conf->isc_user);
495			printf("Secret:           %s\n", conf->isc_secret);
496			printf("Mutual user:      %s\n",
497			    conf->isc_mutual_user);
498			printf("Mutual secret:    %s\n",
499			    conf->isc_mutual_secret);
500			printf("Session type:     %s\n",
501			    conf->isc_discovery ? "Discovery" : "Normal");
502			printf("Session state:    %s\n",
503			    state->iss_connected ?
504			    "Connected" : "Disconnected");
505			printf("Failure reason:   %s\n", state->iss_reason);
506			printf("Header digest:    %s\n",
507			    state->iss_header_digest == ISCSI_DIGEST_CRC32C ?
508			    "CRC32C" : "None");
509			printf("Data digest:      %s\n",
510			    state->iss_data_digest == ISCSI_DIGEST_CRC32C ?
511			    "CRC32C" : "None");
512			printf("DataSegmentLen:   %d\n",
513			    state->iss_max_data_segment_length);
514			printf("ImmediateData:    %s\n",
515			    state->iss_immediate_data ? "Yes" : "No");
516			printf("iSER (RDMA):      %s\n",
517			    conf->isc_iser ? "Yes" : "No");
518			printf("Device nodes:     ");
519			print_periphs(state->iss_id);
520			printf("\n\n");
521		}
522	} else {
523		printf("%-36s %-16s %s\n",
524		    "Target name", "Target portal", "State");
525		for (i = 0; i < isl.isl_nentries; i++) {
526			state = &states[i];
527			conf = &state->iss_conf;
528
529			printf("%-36s %-16s ",
530			    conf->isc_target, conf->isc_target_addr);
531
532			if (state->iss_reason[0] != '\0') {
533				printf("%s\n", state->iss_reason);
534			} else {
535				if (conf->isc_discovery) {
536					printf("Discovery\n");
537				} else if (state->iss_connected) {
538					printf("Connected: ");
539					print_periphs(state->iss_id);
540					printf("\n");
541				} else {
542					printf("Disconnected\n");
543				}
544			}
545		}
546	}
547
548	return (0);
549}
550
551static void
552usage(void)
553{
554
555	fprintf(stderr, "usage: iscsictl -A -p portal -t target "
556	    "[-u user -s secret]\n");
557	fprintf(stderr, "       iscsictl -A -d discovery-host "
558	    "[-u user -s secret]\n");
559	fprintf(stderr, "       iscsictl -A -a [-c path]\n");
560	fprintf(stderr, "       iscsictl -A -n nickname [-c path]\n");
561	fprintf(stderr, "       iscsictl -M -i session-id [-p portal] "
562	    "[-t target] [-u user] [-s secret]\n");
563	fprintf(stderr, "       iscsictl -M -i session-id -n nickname "
564	    "[-c path]\n");
565	fprintf(stderr, "       iscsictl -R [-p portal] [-t target]\n");
566	fprintf(stderr, "       iscsictl -R -a\n");
567	fprintf(stderr, "       iscsictl -R -n nickname [-c path]\n");
568	fprintf(stderr, "       iscsictl -L [-v]\n");
569	exit(1);
570}
571
572char *
573checked_strdup(const char *s)
574{
575	char *c;
576
577	c = strdup(s);
578	if (c == NULL)
579		err(1, "strdup");
580	return (c);
581}
582
583int
584main(int argc, char **argv)
585{
586	int Aflag = 0, Mflag = 0, Rflag = 0, Lflag = 0, aflag = 0, vflag = 0;
587	const char *conf_path = DEFAULT_CONFIG_PATH;
588	char *nickname = NULL, *discovery_host = NULL, *portal = NULL,
589	     *target = NULL, *user = NULL, *secret = NULL;
590	long long session_id = -1;
591	char *end;
592	int ch, error, iscsi_fd, retval, saved_errno;
593	int failed = 0;
594	struct conf *conf;
595	struct target *targ;
596
597	while ((ch = getopt(argc, argv, "AMRLac:d:i:n:p:t:u:s:v")) != -1) {
598		switch (ch) {
599		case 'A':
600			Aflag = 1;
601			break;
602		case 'M':
603			Mflag = 1;
604			break;
605		case 'R':
606			Rflag = 1;
607			break;
608		case 'L':
609			Lflag = 1;
610			break;
611		case 'a':
612			aflag = 1;
613			break;
614		case 'c':
615			conf_path = optarg;
616			break;
617		case 'd':
618			discovery_host = optarg;
619			break;
620		case 'i':
621			session_id = strtol(optarg, &end, 10);
622			if ((size_t)(end - optarg) != strlen(optarg))
623				errx(1, "trailing characters after session-id");
624			if (session_id < 0)
625				errx(1, "session-id cannot be negative");
626			if (session_id > UINT_MAX)
627				errx(1, "session-id cannot be greater than %u",
628				    UINT_MAX);
629			break;
630		case 'n':
631			nickname = optarg;
632			break;
633		case 'p':
634			portal = optarg;
635			break;
636		case 't':
637			target = optarg;
638			break;
639		case 'u':
640			user = optarg;
641			break;
642		case 's':
643			secret = optarg;
644			break;
645		case 'v':
646			vflag = 1;
647			break;
648		case '?':
649		default:
650			usage();
651		}
652	}
653	argc -= optind;
654	if (argc != 0)
655		usage();
656
657	if (Aflag + Mflag + Rflag + Lflag == 0)
658		Lflag = 1;
659	if (Aflag + Mflag + Rflag + Lflag > 1)
660		errx(1, "at most one of -A, -M, -R, or -L may be specified");
661
662	/*
663	 * Note that we ignore unneccessary/inapplicable "-c" flag; so that
664	 * people can do something like "alias ISCSICTL="iscsictl -c path"
665	 * in shell scripts.
666	 */
667	if (Aflag != 0) {
668		if (aflag != 0) {
669			if (portal != NULL)
670				errx(1, "-a and -p and mutually exclusive");
671			if (target != NULL)
672				errx(1, "-a and -t and mutually exclusive");
673			if (user != NULL)
674				errx(1, "-a and -u and mutually exclusive");
675			if (secret != NULL)
676				errx(1, "-a and -s and mutually exclusive");
677			if (nickname != NULL)
678				errx(1, "-a and -n and mutually exclusive");
679			if (discovery_host != NULL)
680				errx(1, "-a and -d and mutually exclusive");
681		} else if (nickname != NULL) {
682			if (portal != NULL)
683				errx(1, "-n and -p and mutually exclusive");
684			if (target != NULL)
685				errx(1, "-n and -t and mutually exclusive");
686			if (user != NULL)
687				errx(1, "-n and -u and mutually exclusive");
688			if (secret != NULL)
689				errx(1, "-n and -s and mutually exclusive");
690			if (discovery_host != NULL)
691				errx(1, "-n and -d and mutually exclusive");
692		} else if (discovery_host != NULL) {
693			if (portal != NULL)
694				errx(1, "-d and -p and mutually exclusive");
695			if (target != NULL)
696				errx(1, "-d and -t and mutually exclusive");
697		} else {
698			if (target == NULL && portal == NULL)
699				errx(1, "must specify -a, -n or -t/-p");
700
701			if (target != NULL && portal == NULL)
702				errx(1, "-t must always be used with -p");
703			if (portal != NULL && target == NULL)
704				errx(1, "-p must always be used with -t");
705		}
706
707		if (user != NULL && secret == NULL)
708			errx(1, "-u must always be used with -s");
709		if (secret != NULL && user == NULL)
710			errx(1, "-s must always be used with -u");
711
712		if (session_id != -1)
713			errx(1, "-i cannot be used with -A");
714		if (vflag != 0)
715			errx(1, "-v cannot be used with -A");
716
717	} else if (Mflag != 0) {
718		if (session_id == -1)
719			errx(1, "-M requires -i");
720
721		if (discovery_host != NULL)
722			errx(1, "-M and -d are mutually exclusive");
723		if (aflag != 0)
724			errx(1, "-M and -a are mutually exclusive");
725		if (nickname != NULL) {
726			if (portal != NULL)
727				errx(1, "-n and -p and mutually exclusive");
728			if (target != NULL)
729				errx(1, "-n and -t and mutually exclusive");
730			if (user != NULL)
731				errx(1, "-n and -u and mutually exclusive");
732			if (secret != NULL)
733				errx(1, "-n and -s and mutually exclusive");
734		}
735
736		if (vflag != 0)
737			errx(1, "-v cannot be used with -M");
738
739	} else if (Rflag != 0) {
740		if (user != NULL)
741			errx(1, "-R and -u are mutually exclusive");
742		if (secret != NULL)
743			errx(1, "-R and -s are mutually exclusive");
744		if (discovery_host != NULL)
745			errx(1, "-R and -d are mutually exclusive");
746
747		if (aflag != 0) {
748			if (portal != NULL)
749				errx(1, "-a and -p and mutually exclusive");
750			if (target != NULL)
751				errx(1, "-a and -t and mutually exclusive");
752			if (nickname != NULL)
753				errx(1, "-a and -n and mutually exclusive");
754		} else if (nickname != NULL) {
755			if (portal != NULL)
756				errx(1, "-n and -p and mutually exclusive");
757			if (target != NULL)
758				errx(1, "-n and -t and mutually exclusive");
759		} else if (portal != NULL) {
760			if (target != NULL)
761				errx(1, "-p and -t and mutually exclusive");
762		} else if (target != NULL) {
763			if (portal != NULL)
764				errx(1, "-t and -p and mutually exclusive");
765		} else
766			errx(1, "must specify either -a, -n, -t, or -p");
767
768		if (session_id != -1)
769			errx(1, "-i cannot be used with -R");
770		if (vflag != 0)
771			errx(1, "-v cannot be used with -R");
772
773	} else {
774		assert(Lflag != 0);
775
776		if (portal != NULL)
777			errx(1, "-L and -p and mutually exclusive");
778		if (target != NULL)
779			errx(1, "-L and -t and mutually exclusive");
780		if (user != NULL)
781			errx(1, "-L and -u and mutually exclusive");
782		if (secret != NULL)
783			errx(1, "-L and -s and mutually exclusive");
784		if (nickname != NULL)
785			errx(1, "-L and -n and mutually exclusive");
786		if (discovery_host != NULL)
787			errx(1, "-L and -d and mutually exclusive");
788
789		if (session_id != -1)
790			errx(1, "-i cannot be used with -L");
791	}
792
793	iscsi_fd = open(ISCSI_PATH, O_RDWR);
794	if (iscsi_fd < 0 && errno == ENOENT) {
795		saved_errno = errno;
796		retval = kldload("iscsi");
797		if (retval != -1)
798			iscsi_fd = open(ISCSI_PATH, O_RDWR);
799		else
800			errno = saved_errno;
801	}
802	if (iscsi_fd < 0)
803		err(1, "failed to open %s", ISCSI_PATH);
804
805	if (Aflag != 0 && aflag != 0) {
806		conf = conf_new_from_file(conf_path);
807
808		TAILQ_FOREACH(targ, &conf->conf_targets, t_next)
809			failed += kernel_add(iscsi_fd, targ);
810	} else if (nickname != NULL) {
811		conf = conf_new_from_file(conf_path);
812		targ = target_find(conf, nickname);
813		if (targ == NULL)
814			errx(1, "target %s not found in %s",
815			    nickname, conf_path);
816
817		if (Aflag != 0)
818			failed += kernel_add(iscsi_fd, targ);
819		else if (Mflag != 0)
820			failed += kernel_modify(iscsi_fd, session_id, targ);
821		else if (Rflag != 0)
822			failed += kernel_remove(iscsi_fd, targ);
823		else
824			failed += kernel_list(iscsi_fd, targ, vflag);
825	} else if (Mflag != 0) {
826		kernel_modify_some(iscsi_fd, session_id, target, portal,
827		    user, secret);
828	} else {
829		if (Aflag != 0 && target != NULL) {
830			if (valid_iscsi_name(target) == false)
831				errx(1, "invalid target name \"%s\"", target);
832		}
833		conf = conf_new();
834		targ = target_new(conf);
835		targ->t_initiator_name = default_initiator_name();
836		targ->t_header_digest = DIGEST_NONE;
837		targ->t_data_digest = DIGEST_NONE;
838		targ->t_name = target;
839		if (discovery_host != NULL) {
840			targ->t_session_type = SESSION_TYPE_DISCOVERY;
841			targ->t_address = discovery_host;
842		} else {
843			targ->t_session_type = SESSION_TYPE_NORMAL;
844			targ->t_address = portal;
845		}
846		targ->t_user = user;
847		targ->t_secret = secret;
848
849		if (Aflag != 0)
850			failed += kernel_add(iscsi_fd, targ);
851		else if (Rflag != 0)
852			failed += kernel_remove(iscsi_fd, targ);
853		else
854			failed += kernel_list(iscsi_fd, targ, vflag);
855	}
856
857	error = close(iscsi_fd);
858	if (error != 0)
859		err(1, "close");
860
861	if (failed > 0)
862		return (1);
863	return (0);
864}
865