2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2012 The FreeBSD Foundation
6 * This software was developed by Edward Tomasz Napierala under sponsorship
7 * from the FreeBSD Foundation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
35 #include <sys/ioctl.h>
36 #include <sys/param.h>
37 #include <sys/linker.h>
49 #include <iscsi_ioctl.h>
57 conf = calloc(1, sizeof(*conf));
61 TAILQ_INIT(&conf->conf_targets);
67 target_find(struct conf *conf, const char *nickname)
71 TAILQ_FOREACH(targ, &conf->conf_targets, t_next) {
72 if (targ->t_nickname != NULL &&
73 strcasecmp(targ->t_nickname, nickname) == 0)
81 target_new(struct conf *conf)
85 targ = calloc(1, sizeof(*targ));
91 targ->t_pingtimeout = -1;
92 targ->t_logintimeout = -1;
93 TAILQ_INSERT_TAIL(&conf->conf_targets, targ, t_next);
99 target_delete(struct target *targ)
102 TAILQ_REMOVE(&targ->t_conf->conf_targets, targ, t_next);
107 default_initiator_name(void)
113 namelen = _POSIX_HOST_NAME_MAX + strlen(DEFAULT_IQN);
115 name = calloc(1, namelen + 1);
118 strcpy(name, DEFAULT_IQN);
119 error = gethostname(name + strlen(DEFAULT_IQN),
120 namelen - strlen(DEFAULT_IQN));
122 xo_err(1, "gethostname");
128 valid_hex(const char ch)
160 parse_enable(const char *enable)
163 return (ENABLE_UNSPECIFIED);
165 if (strcasecmp(enable, "on") == 0 ||
166 strcasecmp(enable, "yes") == 0)
169 if (strcasecmp(enable, "off") == 0 ||
170 strcasecmp(enable, "no") == 0)
173 return (ENABLE_UNSPECIFIED);
177 valid_iscsi_name(const char *name)
181 if (strlen(name) >= MAX_NAME_LEN) {
182 xo_warnx("overlong name for \"%s\"; max length allowed "
183 "by iSCSI specification is %d characters",
189 * In the cases below, we don't return an error, just in case the admin
190 * was right, and we're wrong.
192 if (strncasecmp(name, "iqn.", strlen("iqn.")) == 0) {
193 for (i = strlen("iqn."); name[i] != '\0'; i++) {
195 * XXX: We should verify UTF-8 normalisation, as defined
196 * by 3.2.6.2: iSCSI Name Encoding.
198 if (isalnum(name[i]))
200 if (name[i] == '-' || name[i] == '.' || name[i] == ':')
202 xo_warnx("invalid character \"%c\" in iSCSI name "
203 "\"%s\"; allowed characters are letters, digits, "
204 "'-', '.', and ':'", name[i], name);
208 * XXX: Check more stuff: valid date and a valid reversed domain.
210 } else if (strncasecmp(name, "eui.", strlen("eui.")) == 0) {
211 if (strlen(name) != strlen("eui.") + 16)
212 xo_warnx("invalid iSCSI name \"%s\"; the \"eui.\" "
213 "should be followed by exactly 16 hexadecimal "
215 for (i = strlen("eui."); name[i] != '\0'; i++) {
216 if (!valid_hex(name[i])) {
217 xo_warnx("invalid character \"%c\" in iSCSI "
218 "name \"%s\"; allowed characters are 1-9 "
219 "and A-F", name[i], name);
223 } else if (strncasecmp(name, "naa.", strlen("naa.")) == 0) {
224 if (strlen(name) > strlen("naa.") + 32)
225 xo_warnx("invalid iSCSI name \"%s\"; the \"naa.\" "
226 "should be followed by at most 32 hexadecimal "
228 for (i = strlen("naa."); name[i] != '\0'; i++) {
229 if (!valid_hex(name[i])) {
230 xo_warnx("invalid character \"%c\" in ISCSI "
231 "name \"%s\"; allowed characters are 1-9 "
232 "and A-F", name[i], name);
237 xo_warnx("invalid iSCSI name \"%s\"; should start with "
238 "either \".iqn\", \"eui.\", or \"naa.\"",
245 conf_verify(struct conf *conf)
249 TAILQ_FOREACH(targ, &conf->conf_targets, t_next) {
250 assert(targ->t_nickname != NULL);
251 if (targ->t_session_type == SESSION_TYPE_UNSPECIFIED)
252 targ->t_session_type = SESSION_TYPE_NORMAL;
253 if (targ->t_session_type == SESSION_TYPE_NORMAL &&
254 targ->t_name == NULL)
255 xo_errx(1, "missing TargetName for target \"%s\"",
257 if (targ->t_session_type == SESSION_TYPE_DISCOVERY &&
258 targ->t_name != NULL)
259 xo_errx(1, "cannot specify TargetName for discovery "
260 "sessions for target \"%s\"", targ->t_nickname);
261 if (targ->t_name != NULL) {
262 if (valid_iscsi_name(targ->t_name) == false)
263 xo_errx(1, "invalid target name \"%s\"",
266 if (targ->t_protocol == PROTOCOL_UNSPECIFIED)
267 targ->t_protocol = PROTOCOL_ISCSI;
268 if (targ->t_address == NULL)
269 xo_errx(1, "missing TargetAddress for target \"%s\"",
271 if (targ->t_initiator_name == NULL)
272 targ->t_initiator_name = default_initiator_name();
273 if (valid_iscsi_name(targ->t_initiator_name) == false)
274 xo_errx(1, "invalid initiator name \"%s\"",
275 targ->t_initiator_name);
276 if (targ->t_header_digest == DIGEST_UNSPECIFIED)
277 targ->t_header_digest = DIGEST_NONE;
278 if (targ->t_data_digest == DIGEST_UNSPECIFIED)
279 targ->t_data_digest = DIGEST_NONE;
280 if (targ->t_auth_method == AUTH_METHOD_UNSPECIFIED) {
281 if (targ->t_user != NULL || targ->t_secret != NULL ||
282 targ->t_mutual_user != NULL ||
283 targ->t_mutual_secret != NULL)
284 targ->t_auth_method =
287 targ->t_auth_method =
290 if (targ->t_auth_method == AUTH_METHOD_CHAP) {
291 if (targ->t_user == NULL) {
292 xo_errx(1, "missing chapIName for target \"%s\"",
295 if (targ->t_secret == NULL)
296 xo_errx(1, "missing chapSecret for target \"%s\"",
298 if (targ->t_mutual_user != NULL ||
299 targ->t_mutual_secret != NULL) {
300 if (targ->t_mutual_user == NULL)
301 xo_errx(1, "missing tgtChapName for "
302 "target \"%s\"", targ->t_nickname);
303 if (targ->t_mutual_secret == NULL)
304 xo_errx(1, "missing tgtChapSecret for "
305 "target \"%s\"", targ->t_nickname);
312 conf_from_target(struct iscsi_session_conf *conf,
313 const struct target *targ)
315 memset(conf, 0, sizeof(*conf));
318 * XXX: Check bounds and return error instead of silently truncating.
320 if (targ->t_initiator_name != NULL)
321 strlcpy(conf->isc_initiator, targ->t_initiator_name,
322 sizeof(conf->isc_initiator));
323 if (targ->t_initiator_address != NULL)
324 strlcpy(conf->isc_initiator_addr, targ->t_initiator_address,
325 sizeof(conf->isc_initiator_addr));
326 if (targ->t_initiator_alias != NULL)
327 strlcpy(conf->isc_initiator_alias, targ->t_initiator_alias,
328 sizeof(conf->isc_initiator_alias));
329 if (targ->t_name != NULL)
330 strlcpy(conf->isc_target, targ->t_name,
331 sizeof(conf->isc_target));
332 if (targ->t_address != NULL)
333 strlcpy(conf->isc_target_addr, targ->t_address,
334 sizeof(conf->isc_target_addr));
335 if (targ->t_user != NULL)
336 strlcpy(conf->isc_user, targ->t_user,
337 sizeof(conf->isc_user));
338 if (targ->t_secret != NULL)
339 strlcpy(conf->isc_secret, targ->t_secret,
340 sizeof(conf->isc_secret));
341 if (targ->t_mutual_user != NULL)
342 strlcpy(conf->isc_mutual_user, targ->t_mutual_user,
343 sizeof(conf->isc_mutual_user));
344 if (targ->t_mutual_secret != NULL)
345 strlcpy(conf->isc_mutual_secret, targ->t_mutual_secret,
346 sizeof(conf->isc_mutual_secret));
347 if (targ->t_session_type == SESSION_TYPE_DISCOVERY)
348 conf->isc_discovery = 1;
349 if (targ->t_enable != ENABLE_OFF)
350 conf->isc_enable = 1;
351 if (targ->t_protocol == PROTOCOL_ISER)
353 if (targ->t_offload != NULL)
354 strlcpy(conf->isc_offload, targ->t_offload,
355 sizeof(conf->isc_offload));
356 if (targ->t_header_digest == DIGEST_CRC32C)
357 conf->isc_header_digest = ISCSI_DIGEST_CRC32C;
359 conf->isc_header_digest = ISCSI_DIGEST_NONE;
360 if (targ->t_data_digest == DIGEST_CRC32C)
361 conf->isc_data_digest = ISCSI_DIGEST_CRC32C;
363 conf->isc_data_digest = ISCSI_DIGEST_NONE;
364 conf->isc_dscp = targ->t_dscp;
365 conf->isc_pcp = targ->t_pcp;
366 conf->isc_ping_timeout = targ->t_pingtimeout;
367 conf->isc_login_timeout = targ->t_logintimeout;
371 kernel_add(int iscsi_fd, const struct target *targ)
373 struct iscsi_session_add isa;
376 memset(&isa, 0, sizeof(isa));
377 conf_from_target(&isa.isa_conf, targ);
378 error = ioctl(iscsi_fd, ISCSISADD, &isa);
380 xo_warn("ISCSISADD");
385 kernel_modify(int iscsi_fd, unsigned int session_id, const struct target *targ)
387 struct iscsi_session_modify ism;
390 memset(&ism, 0, sizeof(ism));
391 ism.ism_session_id = session_id;
392 conf_from_target(&ism.ism_conf, targ);
393 error = ioctl(iscsi_fd, ISCSISMODIFY, &ism);
395 xo_warn("ISCSISMODIFY");
400 kernel_modify_some(int iscsi_fd, unsigned int session_id, const char *target,
401 const char *target_addr, const char *user, const char *secret, int enable)
403 struct iscsi_session_state *states = NULL;
404 struct iscsi_session_state *state;
405 struct iscsi_session_conf *conf;
406 struct iscsi_session_list isl;
407 struct iscsi_session_modify ism;
408 unsigned int i, nentries = 1;
412 states = realloc(states,
413 nentries * sizeof(struct iscsi_session_state));
415 xo_err(1, "realloc");
417 memset(&isl, 0, sizeof(isl));
418 isl.isl_nentries = nentries;
419 isl.isl_pstates = states;
421 error = ioctl(iscsi_fd, ISCSISLIST, &isl);
422 if (error != 0 && errno == EMSGSIZE) {
429 xo_errx(1, "ISCSISLIST");
431 for (i = 0; i < isl.isl_nentries; i++) {
434 if (state->iss_id == session_id)
437 if (i == isl.isl_nentries)
438 xo_errx(1, "session-id %u not found", session_id);
440 conf = &state->iss_conf;
443 strlcpy(conf->isc_target, target, sizeof(conf->isc_target));
444 if (target_addr != NULL)
445 strlcpy(conf->isc_target_addr, target_addr,
446 sizeof(conf->isc_target_addr));
448 strlcpy(conf->isc_user, user, sizeof(conf->isc_user));
450 strlcpy(conf->isc_secret, secret, sizeof(conf->isc_secret));
451 if (enable == ENABLE_ON)
452 conf->isc_enable = 1;
453 else if (enable == ENABLE_OFF)
454 conf->isc_enable = 0;
456 memset(&ism, 0, sizeof(ism));
457 ism.ism_session_id = session_id;
458 memcpy(&ism.ism_conf, conf, sizeof(ism.ism_conf));
459 error = ioctl(iscsi_fd, ISCSISMODIFY, &ism);
461 xo_warn("ISCSISMODIFY");
465 kernel_remove(int iscsi_fd, const struct target *targ)
467 struct iscsi_session_remove isr;
470 memset(&isr, 0, sizeof(isr));
471 conf_from_target(&isr.isr_conf, targ);
472 error = ioctl(iscsi_fd, ISCSISREMOVE, &isr);
474 xo_warn("ISCSISREMOVE");
479 * XXX: Add filtering.
482 kernel_list(int iscsi_fd, const struct target *targ __unused,
485 struct iscsi_session_state *states = NULL;
486 const struct iscsi_session_state *state;
487 const struct iscsi_session_conf *conf;
488 struct iscsi_session_list isl;
489 unsigned int i, nentries = 1;
493 states = realloc(states,
494 nentries * sizeof(struct iscsi_session_state));
496 xo_err(1, "realloc");
498 memset(&isl, 0, sizeof(isl));
499 isl.isl_nentries = nentries;
500 isl.isl_pstates = states;
502 error = ioctl(iscsi_fd, ISCSISLIST, &isl);
503 if (error != 0 && errno == EMSGSIZE) {
510 xo_warn("ISCSISLIST");
515 xo_open_list("session");
516 for (i = 0; i < isl.isl_nentries; i++) {
518 conf = &state->iss_conf;
520 xo_open_instance("session");
523 * Display-only modifier as this information
524 * is also present within the 'session' container
526 xo_emit("{L:/%-26s}{V:sessionId/%u}\n",
527 "Session ID:", state->iss_id);
529 xo_open_container("initiator");
530 xo_emit("{L:/%-26s}{V:name/%s}\n",
531 "Initiator name:", conf->isc_initiator);
532 xo_emit("{L:/%-26s}{V:portal/%s}\n",
533 "Initiator portal:", conf->isc_initiator_addr);
534 xo_emit("{L:/%-26s}{V:alias/%s}\n",
535 "Initiator alias:", conf->isc_initiator_alias);
536 xo_close_container("initiator");
538 xo_open_container("target");
539 xo_emit("{L:/%-26s}{V:name/%s}\n",
540 "Target name:", conf->isc_target);
541 xo_emit("{L:/%-26s}{V:portal/%s}\n",
542 "Target portal:", conf->isc_target_addr);
543 xo_emit("{L:/%-26s}{V:alias/%s}\n",
544 "Target alias:", state->iss_target_alias);
545 if (conf->isc_dscp != -1)
546 xo_emit("{L:/%-26s}{V:dscp/0x%02x}\n",
547 "Target DSCP:", conf->isc_dscp);
548 if (conf->isc_pcp != -1)
549 xo_emit("{L:/%-26s}{V:pcp/0x%02x}\n",
550 "Target PCP:", conf->isc_pcp);
551 if (conf->isc_ping_timeout != -1)
552 xo_emit("{L:/%-26s}{V:PingTimeout/%d}\n",
553 "Target PingTimeout:",
554 conf->isc_ping_timeout);
555 if (conf->isc_login_timeout != -1)
556 xo_emit("{L:/%-26s}{V:LoginTimeout/%d}\n",
557 "Target LoginTimeout:",
558 conf->isc_login_timeout);
559 xo_close_container("target");
561 xo_open_container("auth");
562 xo_emit("{L:/%-26s}{V:user/%s}\n",
563 "User:", conf->isc_user);
564 xo_emit("{L:/%-26s}{V:secret/%s}\n",
565 "Secret:", conf->isc_secret);
566 xo_emit("{L:/%-26s}{V:mutualUser/%s}\n",
567 "Mutual user:", conf->isc_mutual_user);
568 xo_emit("{L:/%-26s}{V:mutualSecret/%s}\n",
569 "Mutual secret:", conf->isc_mutual_secret);
570 xo_close_container("auth");
572 xo_emit("{L:/%-26s}{V:type/%s}\n",
574 conf->isc_discovery ? "Discovery" : "Normal");
575 xo_emit("{L:/%-26s}{V:enable/%s}\n",
577 conf->isc_enable ? "Yes" : "No");
578 xo_emit("{L:/%-26s}{V:state/%s}\n",
580 state->iss_connected ? "Connected" : "Disconnected");
581 xo_emit("{L:/%-26s}{V:failureReason/%s}\n",
582 "Failure reason:", state->iss_reason);
583 xo_emit("{L:/%-26s}{V:headerDigest/%s}\n",
585 state->iss_header_digest == ISCSI_DIGEST_CRC32C ?
587 xo_emit("{L:/%-26s}{V:dataDigest/%s}\n",
589 state->iss_data_digest == ISCSI_DIGEST_CRC32C ?
591 xo_emit("{L:/%-26s}{V:recvDataSegmentLen/%d}\n",
592 "MaxRecvDataSegmentLength:",
593 state->iss_max_recv_data_segment_length);
594 xo_emit("{L:/%-26s}{V:sendDataSegmentLen/%d}\n",
595 "MaxSendDataSegmentLength:",
596 state->iss_max_send_data_segment_length);
597 xo_emit("{L:/%-26s}{V:maxBurstLen/%d}\n",
598 "MaxBurstLen:", state->iss_max_burst_length);
599 xo_emit("{L:/%-26s}{V:firstBurstLen/%d}\n",
600 "FirstBurstLen:", state->iss_first_burst_length);
601 xo_emit("{L:/%-26s}{V:immediateData/%s}\n",
602 "ImmediateData:", state->iss_immediate_data ? "Yes" : "No");
603 xo_emit("{L:/%-26s}{V:iSER/%s}\n",
604 "iSER (RDMA):", conf->isc_iser ? "Yes" : "No");
605 xo_emit("{L:/%-26s}{V:offloadDriver/%s}\n",
606 "Offload driver:", state->iss_offload);
607 xo_emit("{L:/%-26s}",
609 print_periphs(state->iss_id);
611 xo_close_instance("session");
613 xo_close_list("session");
615 xo_emit("{T:/%-36s} {T:/%-16s} {T:/%s}\n",
616 "Target name", "Target portal", "State");
618 if (isl.isl_nentries != 0)
619 xo_open_list("session");
620 for (i = 0; i < isl.isl_nentries; i++) {
623 conf = &state->iss_conf;
625 xo_open_instance("session");
626 xo_emit("{V:name/%-36s/%s} {V:portal/%-16s/%s} ",
627 conf->isc_target, conf->isc_target_addr);
629 if (state->iss_reason[0] != '\0' &&
630 conf->isc_enable != 0) {
631 xo_emit("{V:state/%s}\n", state->iss_reason);
633 if (conf->isc_discovery) {
634 xo_emit("{V:state}\n", "Discovery");
635 } else if (conf->isc_enable == 0) {
636 xo_emit("{V:state}\n", "Disabled");
637 } else if (state->iss_connected) {
638 xo_emit("{V:state}: ", "Connected");
639 print_periphs(state->iss_id);
642 xo_emit("{V:state}\n", "Disconnected");
645 xo_close_instance("session");
647 if (isl.isl_nentries != 0)
648 xo_close_list("session");
655 kernel_wait(int iscsi_fd, int timeout)
657 struct iscsi_session_state *states = NULL;
658 const struct iscsi_session_state *state;
659 struct iscsi_session_list isl;
660 unsigned int i, nentries = 1;
666 states = realloc(states,
667 nentries * sizeof(struct iscsi_session_state));
669 xo_err(1, "realloc");
671 memset(&isl, 0, sizeof(isl));
672 isl.isl_nentries = nentries;
673 isl.isl_pstates = states;
675 error = ioctl(iscsi_fd, ISCSISLIST, &isl);
676 if (error != 0 && errno == EMSGSIZE) {
683 xo_warn("ISCSISLIST");
687 all_connected = true;
688 for (i = 0; i < isl.isl_nentries; i++) {
691 if (!state->iss_connected) {
692 all_connected = false;
714 fprintf(stderr, "usage: iscsictl -A -p portal -t target "
715 "[-u user -s secret] [-w timeout] [-e on | off]\n");
716 fprintf(stderr, " iscsictl -A -d discovery-host "
717 "[-u user -s secret] [-e on | off]\n");
718 fprintf(stderr, " iscsictl -A -a [-c path]\n");
719 fprintf(stderr, " iscsictl -A -n nickname [-c path]\n");
720 fprintf(stderr, " iscsictl -M -i session-id [-p portal] "
721 "[-t target] [-u user] [-s secret] [-e on | off]\n");
722 fprintf(stderr, " iscsictl -M -i session-id -n nickname "
724 fprintf(stderr, " iscsictl -R [-p portal] [-t target]\n");
725 fprintf(stderr, " iscsictl -R -a\n");
726 fprintf(stderr, " iscsictl -R -n nickname [-c path]\n");
727 fprintf(stderr, " iscsictl -L [-v] [-w timeout]\n");
732 main(int argc, char **argv)
734 int Aflag = 0, Mflag = 0, Rflag = 0, Lflag = 0, aflag = 0,
735 rflag = 0, vflag = 0;
736 const char *conf_path = DEFAULT_CONFIG_PATH;
737 char *nickname = NULL, *discovery_host = NULL, *portal = NULL,
738 *target = NULL, *user = NULL, *secret = NULL;
739 int timeout = -1, enable = ENABLE_UNSPECIFIED;
740 long long session_id = -1;
742 int ch, error, iscsi_fd, retval, saved_errno;
747 argc = xo_parse_args(argc, argv);
748 xo_open_container("iscsictl");
750 while ((ch = getopt(argc, argv, "AMRLac:d:e:i:n:p:rt:u:s:vw:")) != -1) {
771 discovery_host = optarg;
774 enable = parse_enable(optarg);
775 if (enable == ENABLE_UNSPECIFIED) {
776 xo_errx(1, "invalid argument to -e, "
777 "must be either \"on\" or \"off\"");
781 session_id = strtol(optarg, &end, 10);
782 if ((size_t)(end - optarg) != strlen(optarg))
783 xo_errx(1, "trailing characters after session-id");
785 xo_errx(1, "session-id cannot be negative");
786 if (session_id > UINT_MAX)
787 xo_errx(1, "session-id cannot be greater than %u",
812 timeout = strtol(optarg, &end, 10);
813 if ((size_t)(end - optarg) != strlen(optarg))
814 xo_errx(1, "trailing characters after timeout");
816 xo_errx(1, "timeout cannot be negative");
827 if (Aflag + Mflag + Rflag + Lflag == 0)
829 if (Aflag + Mflag + Rflag + Lflag > 1)
830 xo_errx(1, "at most one of -A, -M, -R, or -L may be specified");
833 * Note that we ignore unnecessary/inapplicable "-c" flag; so that
834 * people can do something like "alias ISCSICTL="iscsictl -c path"
839 if (enable != ENABLE_UNSPECIFIED)
840 xo_errx(1, "-a and -e are mutually exclusive");
842 xo_errx(1, "-a and -p are mutually exclusive");
844 xo_errx(1, "-a and -t are mutually exclusive");
846 xo_errx(1, "-a and -u are mutually exclusive");
848 xo_errx(1, "-a and -s are mutually exclusive");
849 if (nickname != NULL)
850 xo_errx(1, "-a and -n are mutually exclusive");
851 if (discovery_host != NULL)
852 xo_errx(1, "-a and -d are mutually exclusive");
854 xo_errx(1, "-a and -r are mutually exclusive");
855 } else if (nickname != NULL) {
856 if (enable != ENABLE_UNSPECIFIED)
857 xo_errx(1, "-n and -e are mutually exclusive");
859 xo_errx(1, "-n and -p are mutually exclusive");
861 xo_errx(1, "-n and -t are mutually exclusive");
863 xo_errx(1, "-n and -u are mutually exclusive");
865 xo_errx(1, "-n and -s are mutually exclusive");
866 if (discovery_host != NULL)
867 xo_errx(1, "-n and -d are mutually exclusive");
869 xo_errx(1, "-n and -r are mutually exclusive");
870 } else if (discovery_host != NULL) {
872 xo_errx(1, "-d and -p are mutually exclusive");
874 xo_errx(1, "-d and -t are mutually exclusive");
876 if (target == NULL && portal == NULL)
877 xo_errx(1, "must specify -a, -n or -t/-p");
879 if (target != NULL && portal == NULL)
880 xo_errx(1, "-t must always be used with -p");
881 if (portal != NULL && target == NULL)
882 xo_errx(1, "-p must always be used with -t");
885 if (user != NULL && secret == NULL)
886 xo_errx(1, "-u must always be used with -s");
887 if (secret != NULL && user == NULL)
888 xo_errx(1, "-s must always be used with -u");
890 if (session_id != -1)
891 xo_errx(1, "-i cannot be used with -A");
893 xo_errx(1, "-v cannot be used with -A");
895 } else if (Mflag != 0) {
896 if (session_id == -1)
897 xo_errx(1, "-M requires -i");
899 if (nickname != NULL) {
900 if (enable != ENABLE_UNSPECIFIED)
901 xo_errx(1, "-n and -e are mutually exclusive");
903 xo_errx(1, "-n and -p are mutually exclusive");
905 xo_errx(1, "-n and -t are mutually exclusive");
907 xo_errx(1, "-n and -u are mutually exclusive");
909 xo_errx(1, "-n and -s are mutually exclusive");
913 xo_errx(1, "-a cannot be used with -M");
914 if (discovery_host != NULL)
915 xo_errx(1, "-d cannot be used with -M");
917 xo_errx(1, "-r cannot be used with -M");
919 xo_errx(1, "-v cannot be used with -M");
921 xo_errx(1, "-w cannot be used with -M");
923 } else if (Rflag != 0) {
926 xo_errx(1, "-a and -p are mutually exclusive");
928 xo_errx(1, "-a and -t are mutually exclusive");
929 if (nickname != NULL)
930 xo_errx(1, "-a and -n are mutually exclusive");
931 } else if (nickname != NULL) {
933 xo_errx(1, "-n and -p are mutually exclusive");
935 xo_errx(1, "-n and -t are mutually exclusive");
936 } else if (target == NULL && portal == NULL) {
937 xo_errx(1, "must specify either -a, -n, -t, or -p");
940 if (discovery_host != NULL)
941 xo_errx(1, "-d cannot be used with -R");
942 if (enable != ENABLE_UNSPECIFIED)
943 xo_errx(1, "-e cannot be used with -R");
944 if (session_id != -1)
945 xo_errx(1, "-i cannot be used with -R");
947 xo_errx(1, "-r cannot be used with -R");
949 xo_errx(1, "-u cannot be used with -R");
951 xo_errx(1, "-s cannot be used with -R");
953 xo_errx(1, "-v cannot be used with -R");
955 xo_errx(1, "-w cannot be used with -R");
960 if (discovery_host != NULL)
961 xo_errx(1, "-d cannot be used with -L");
962 if (session_id != -1)
963 xo_errx(1, "-i cannot be used with -L");
964 if (nickname != NULL)
965 xo_errx(1, "-n cannot be used with -L");
967 xo_errx(1, "-p cannot be used with -L");
969 xo_errx(1, "-r cannot be used with -L");
971 xo_errx(1, "-t cannot be used with -L");
973 xo_errx(1, "-u cannot be used with -L");
975 xo_errx(1, "-s cannot be used with -L");
978 iscsi_fd = open(ISCSI_PATH, O_RDWR);
979 if (iscsi_fd < 0 && errno == ENOENT) {
981 retval = kldload("iscsi");
983 iscsi_fd = open(ISCSI_PATH, O_RDWR);
988 xo_err(1, "failed to open %s", ISCSI_PATH);
990 if (Aflag != 0 && aflag != 0) {
991 conf = conf_new_from_file(conf_path);
993 TAILQ_FOREACH(targ, &conf->conf_targets, t_next)
994 failed += kernel_add(iscsi_fd, targ);
995 } else if (nickname != NULL) {
996 conf = conf_new_from_file(conf_path);
997 targ = target_find(conf, nickname);
999 xo_errx(1, "target %s not found in %s",
1000 nickname, conf_path);
1003 failed += kernel_add(iscsi_fd, targ);
1004 else if (Mflag != 0)
1005 failed += kernel_modify(iscsi_fd, session_id, targ);
1006 else if (Rflag != 0)
1007 failed += kernel_remove(iscsi_fd, targ);
1009 failed += kernel_list(iscsi_fd, targ, vflag);
1010 } else if (Mflag != 0) {
1011 kernel_modify_some(iscsi_fd, session_id, target, portal,
1012 user, secret, enable);
1014 if (Aflag != 0 && target != NULL) {
1015 if (valid_iscsi_name(target) == false)
1016 xo_errx(1, "invalid target name \"%s\"", target);
1019 targ = target_new(conf);
1020 targ->t_initiator_name = default_initiator_name();
1021 targ->t_header_digest = DIGEST_NONE;
1022 targ->t_data_digest = DIGEST_NONE;
1023 targ->t_name = target;
1024 if (discovery_host != NULL) {
1025 targ->t_session_type = SESSION_TYPE_DISCOVERY;
1026 targ->t_address = discovery_host;
1028 targ->t_session_type = SESSION_TYPE_NORMAL;
1029 targ->t_address = portal;
1031 targ->t_enable = enable;
1033 targ->t_protocol = PROTOCOL_ISER;
1034 targ->t_user = user;
1035 targ->t_secret = secret;
1038 failed += kernel_add(iscsi_fd, targ);
1039 else if (Rflag != 0)
1040 failed += kernel_remove(iscsi_fd, targ);
1042 failed += kernel_list(iscsi_fd, targ, vflag);
1046 failed += kernel_wait(iscsi_fd, timeout);
1048 error = close(iscsi_fd);
1052 xo_close_container("iscsictl");