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