2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2012 The FreeBSD Foundation
7 * This software was developed by Edward Tomasz Napierala under sponsorship
8 * from the FreeBSD Foundation.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
36 #include <sys/ioctl.h>
37 #include <sys/param.h>
38 #include <sys/linker.h>
50 #include <iscsi_ioctl.h>
58 conf = calloc(1, sizeof(*conf));
62 TAILQ_INIT(&conf->conf_targets);
68 target_find(struct conf *conf, const char *nickname)
72 TAILQ_FOREACH(targ, &conf->conf_targets, t_next) {
73 if (targ->t_nickname != NULL &&
74 strcasecmp(targ->t_nickname, nickname) == 0)
82 target_new(struct conf *conf)
86 targ = calloc(1, sizeof(*targ));
90 TAILQ_INSERT_TAIL(&conf->conf_targets, targ, t_next);
96 target_delete(struct target *targ)
99 TAILQ_REMOVE(&targ->t_conf->conf_targets, targ, t_next);
104 default_initiator_name(void)
110 namelen = _POSIX_HOST_NAME_MAX + strlen(DEFAULT_IQN);
112 name = calloc(1, namelen + 1);
115 strcpy(name, DEFAULT_IQN);
116 error = gethostname(name + strlen(DEFAULT_IQN),
117 namelen - strlen(DEFAULT_IQN));
119 xo_err(1, "gethostname");
125 valid_hex(const char ch)
157 parse_enable(const char *enable)
160 return (ENABLE_UNSPECIFIED);
162 if (strcasecmp(enable, "on") == 0 ||
163 strcasecmp(enable, "yes") == 0)
166 if (strcasecmp(enable, "off") == 0 ||
167 strcasecmp(enable, "no") == 0)
170 return (ENABLE_UNSPECIFIED);
174 valid_iscsi_name(const char *name)
178 if (strlen(name) >= MAX_NAME_LEN) {
179 xo_warnx("overlong name for \"%s\"; max length allowed "
180 "by iSCSI specification is %d characters",
186 * In the cases below, we don't return an error, just in case the admin
187 * was right, and we're wrong.
189 if (strncasecmp(name, "iqn.", strlen("iqn.")) == 0) {
190 for (i = strlen("iqn."); name[i] != '\0'; i++) {
192 * XXX: We should verify UTF-8 normalisation, as defined
193 * by 3.2.6.2: iSCSI Name Encoding.
195 if (isalnum(name[i]))
197 if (name[i] == '-' || name[i] == '.' || name[i] == ':')
199 xo_warnx("invalid character \"%c\" in iSCSI name "
200 "\"%s\"; allowed characters are letters, digits, "
201 "'-', '.', and ':'", name[i], name);
205 * XXX: Check more stuff: valid date and a valid reversed domain.
207 } else if (strncasecmp(name, "eui.", strlen("eui.")) == 0) {
208 if (strlen(name) != strlen("eui.") + 16)
209 xo_warnx("invalid iSCSI name \"%s\"; the \"eui.\" "
210 "should be followed by exactly 16 hexadecimal "
212 for (i = strlen("eui."); name[i] != '\0'; i++) {
213 if (!valid_hex(name[i])) {
214 xo_warnx("invalid character \"%c\" in iSCSI "
215 "name \"%s\"; allowed characters are 1-9 "
216 "and A-F", name[i], name);
220 } else if (strncasecmp(name, "naa.", strlen("naa.")) == 0) {
221 if (strlen(name) > strlen("naa.") + 32)
222 xo_warnx("invalid iSCSI name \"%s\"; the \"naa.\" "
223 "should be followed by at most 32 hexadecimal "
225 for (i = strlen("naa."); name[i] != '\0'; i++) {
226 if (!valid_hex(name[i])) {
227 xo_warnx("invalid character \"%c\" in ISCSI "
228 "name \"%s\"; allowed characters are 1-9 "
229 "and A-F", name[i], name);
234 xo_warnx("invalid iSCSI name \"%s\"; should start with "
235 "either \".iqn\", \"eui.\", or \"naa.\"",
242 conf_verify(struct conf *conf)
246 TAILQ_FOREACH(targ, &conf->conf_targets, t_next) {
247 assert(targ->t_nickname != NULL);
248 if (targ->t_session_type == SESSION_TYPE_UNSPECIFIED)
249 targ->t_session_type = SESSION_TYPE_NORMAL;
250 if (targ->t_session_type == SESSION_TYPE_NORMAL &&
251 targ->t_name == NULL)
252 xo_errx(1, "missing TargetName for target \"%s\"",
254 if (targ->t_session_type == SESSION_TYPE_DISCOVERY &&
255 targ->t_name != NULL)
256 xo_errx(1, "cannot specify TargetName for discovery "
257 "sessions for target \"%s\"", targ->t_nickname);
258 if (targ->t_name != NULL) {
259 if (valid_iscsi_name(targ->t_name) == false)
260 xo_errx(1, "invalid target name \"%s\"",
263 if (targ->t_protocol == PROTOCOL_UNSPECIFIED)
264 targ->t_protocol = PROTOCOL_ISCSI;
265 if (targ->t_address == NULL)
266 xo_errx(1, "missing TargetAddress for target \"%s\"",
268 if (targ->t_initiator_name == NULL)
269 targ->t_initiator_name = default_initiator_name();
270 if (valid_iscsi_name(targ->t_initiator_name) == false)
271 xo_errx(1, "invalid initiator name \"%s\"",
272 targ->t_initiator_name);
273 if (targ->t_header_digest == DIGEST_UNSPECIFIED)
274 targ->t_header_digest = DIGEST_NONE;
275 if (targ->t_data_digest == DIGEST_UNSPECIFIED)
276 targ->t_data_digest = DIGEST_NONE;
277 if (targ->t_auth_method == AUTH_METHOD_UNSPECIFIED) {
278 if (targ->t_user != NULL || targ->t_secret != NULL ||
279 targ->t_mutual_user != NULL ||
280 targ->t_mutual_secret != NULL)
281 targ->t_auth_method =
284 targ->t_auth_method =
287 if (targ->t_auth_method == AUTH_METHOD_CHAP) {
288 if (targ->t_user == NULL) {
289 xo_errx(1, "missing chapIName for target \"%s\"",
292 if (targ->t_secret == NULL)
293 xo_errx(1, "missing chapSecret for target \"%s\"",
295 if (targ->t_mutual_user != NULL ||
296 targ->t_mutual_secret != NULL) {
297 if (targ->t_mutual_user == NULL)
298 xo_errx(1, "missing tgtChapName for "
299 "target \"%s\"", targ->t_nickname);
300 if (targ->t_mutual_secret == NULL)
301 xo_errx(1, "missing tgtChapSecret for "
302 "target \"%s\"", targ->t_nickname);
309 conf_from_target(struct iscsi_session_conf *conf,
310 const struct target *targ)
312 memset(conf, 0, sizeof(*conf));
315 * XXX: Check bounds and return error instead of silently truncating.
317 if (targ->t_initiator_name != NULL)
318 strlcpy(conf->isc_initiator, targ->t_initiator_name,
319 sizeof(conf->isc_initiator));
320 if (targ->t_initiator_address != NULL)
321 strlcpy(conf->isc_initiator_addr, targ->t_initiator_address,
322 sizeof(conf->isc_initiator_addr));
323 if (targ->t_initiator_alias != NULL)
324 strlcpy(conf->isc_initiator_alias, targ->t_initiator_alias,
325 sizeof(conf->isc_initiator_alias));
326 if (targ->t_name != NULL)
327 strlcpy(conf->isc_target, targ->t_name,
328 sizeof(conf->isc_target));
329 if (targ->t_address != NULL)
330 strlcpy(conf->isc_target_addr, targ->t_address,
331 sizeof(conf->isc_target_addr));
332 if (targ->t_user != NULL)
333 strlcpy(conf->isc_user, targ->t_user,
334 sizeof(conf->isc_user));
335 if (targ->t_secret != NULL)
336 strlcpy(conf->isc_secret, targ->t_secret,
337 sizeof(conf->isc_secret));
338 if (targ->t_mutual_user != NULL)
339 strlcpy(conf->isc_mutual_user, targ->t_mutual_user,
340 sizeof(conf->isc_mutual_user));
341 if (targ->t_mutual_secret != NULL)
342 strlcpy(conf->isc_mutual_secret, targ->t_mutual_secret,
343 sizeof(conf->isc_mutual_secret));
344 if (targ->t_session_type == SESSION_TYPE_DISCOVERY)
345 conf->isc_discovery = 1;
346 if (targ->t_enable != ENABLE_OFF)
347 conf->isc_enable = 1;
348 if (targ->t_protocol == PROTOCOL_ISER)
350 if (targ->t_offload != NULL)
351 strlcpy(conf->isc_offload, targ->t_offload,
352 sizeof(conf->isc_offload));
353 if (targ->t_header_digest == DIGEST_CRC32C)
354 conf->isc_header_digest = ISCSI_DIGEST_CRC32C;
356 conf->isc_header_digest = ISCSI_DIGEST_NONE;
357 if (targ->t_data_digest == DIGEST_CRC32C)
358 conf->isc_data_digest = ISCSI_DIGEST_CRC32C;
360 conf->isc_data_digest = ISCSI_DIGEST_NONE;
364 kernel_add(int iscsi_fd, const struct target *targ)
366 struct iscsi_session_add isa;
369 memset(&isa, 0, sizeof(isa));
370 conf_from_target(&isa.isa_conf, targ);
371 error = ioctl(iscsi_fd, ISCSISADD, &isa);
373 xo_warn("ISCSISADD");
378 kernel_modify(int iscsi_fd, unsigned int session_id, const struct target *targ)
380 struct iscsi_session_modify ism;
383 memset(&ism, 0, sizeof(ism));
384 ism.ism_session_id = session_id;
385 conf_from_target(&ism.ism_conf, targ);
386 error = ioctl(iscsi_fd, ISCSISMODIFY, &ism);
388 xo_warn("ISCSISMODIFY");
393 kernel_modify_some(int iscsi_fd, unsigned int session_id, const char *target,
394 const char *target_addr, const char *user, const char *secret, int enable)
396 struct iscsi_session_state *states = NULL;
397 struct iscsi_session_state *state;
398 struct iscsi_session_conf *conf;
399 struct iscsi_session_list isl;
400 struct iscsi_session_modify ism;
401 unsigned int i, nentries = 1;
405 states = realloc(states,
406 nentries * sizeof(struct iscsi_session_state));
408 xo_err(1, "realloc");
410 memset(&isl, 0, sizeof(isl));
411 isl.isl_nentries = nentries;
412 isl.isl_pstates = states;
414 error = ioctl(iscsi_fd, ISCSISLIST, &isl);
415 if (error != 0 && errno == EMSGSIZE) {
422 xo_errx(1, "ISCSISLIST");
424 for (i = 0; i < isl.isl_nentries; i++) {
427 if (state->iss_id == session_id)
430 if (i == isl.isl_nentries)
431 xo_errx(1, "session-id %u not found", session_id);
433 conf = &state->iss_conf;
436 strlcpy(conf->isc_target, target, sizeof(conf->isc_target));
437 if (target_addr != NULL)
438 strlcpy(conf->isc_target_addr, target_addr,
439 sizeof(conf->isc_target_addr));
441 strlcpy(conf->isc_user, user, sizeof(conf->isc_user));
443 strlcpy(conf->isc_secret, secret, sizeof(conf->isc_secret));
444 if (enable == ENABLE_ON)
445 conf->isc_enable = 1;
446 else if (enable == ENABLE_OFF)
447 conf->isc_enable = 0;
449 memset(&ism, 0, sizeof(ism));
450 ism.ism_session_id = session_id;
451 memcpy(&ism.ism_conf, conf, sizeof(ism.ism_conf));
452 error = ioctl(iscsi_fd, ISCSISMODIFY, &ism);
454 xo_warn("ISCSISMODIFY");
458 kernel_remove(int iscsi_fd, const struct target *targ)
460 struct iscsi_session_remove isr;
463 memset(&isr, 0, sizeof(isr));
464 conf_from_target(&isr.isr_conf, targ);
465 error = ioctl(iscsi_fd, ISCSISREMOVE, &isr);
467 xo_warn("ISCSISREMOVE");
472 * XXX: Add filtering.
475 kernel_list(int iscsi_fd, const struct target *targ __unused,
478 struct iscsi_session_state *states = NULL;
479 const struct iscsi_session_state *state;
480 const struct iscsi_session_conf *conf;
481 struct iscsi_session_list isl;
482 unsigned int i, nentries = 1;
486 states = realloc(states,
487 nentries * sizeof(struct iscsi_session_state));
489 xo_err(1, "realloc");
491 memset(&isl, 0, sizeof(isl));
492 isl.isl_nentries = nentries;
493 isl.isl_pstates = states;
495 error = ioctl(iscsi_fd, ISCSISLIST, &isl);
496 if (error != 0 && errno == EMSGSIZE) {
503 xo_warn("ISCSISLIST");
508 xo_open_list("session");
509 for (i = 0; i < isl.isl_nentries; i++) {
511 conf = &state->iss_conf;
513 xo_open_instance("session");
516 * Display-only modifier as this information
517 * is also present within the 'session' container
519 xo_emit("{L:/%-18s}{V:sessionId/%u}\n",
520 "Session ID:", state->iss_id);
522 xo_open_container("initiator");
523 xo_emit("{L:/%-18s}{V:name/%s}\n",
524 "Initiator name:", conf->isc_initiator);
525 xo_emit("{L:/%-18s}{V:portal/%s}\n",
526 "Initiator portal:", conf->isc_initiator_addr);
527 xo_emit("{L:/%-18s}{V:alias/%s}\n",
528 "Initiator alias:", conf->isc_initiator_alias);
529 xo_close_container("initiator");
531 xo_open_container("target");
532 xo_emit("{L:/%-18s}{V:name/%s}\n",
533 "Target name:", conf->isc_target);
534 xo_emit("{L:/%-18s}{V:portal/%s}\n",
535 "Target portal:", conf->isc_target_addr);
536 xo_emit("{L:/%-18s}{V:alias/%s}\n",
537 "Target alias:", state->iss_target_alias);
538 xo_close_container("target");
540 xo_open_container("auth");
541 xo_emit("{L:/%-18s}{V:user/%s}\n",
542 "User:", conf->isc_user);
543 xo_emit("{L:/%-18s}{V:secret/%s}\n",
544 "Secret:", conf->isc_secret);
545 xo_emit("{L:/%-18s}{V:mutualUser/%s}\n",
546 "Mutual user:", conf->isc_mutual_user);
547 xo_emit("{L:/%-18s}{V:mutualSecret/%s}\n",
548 "Mutual secret:", conf->isc_mutual_secret);
549 xo_close_container("auth");
551 xo_emit("{L:/%-18s}{V:type/%s}\n",
553 conf->isc_discovery ? "Discovery" : "Normal");
554 xo_emit("{L:/%-18s}{V:enable/%s}\n",
556 conf->isc_enable ? "Yes" : "No");
557 xo_emit("{L:/%-18s}{V:state/%s}\n",
559 state->iss_connected ? "Connected" : "Disconnected");
560 xo_emit("{L:/%-18s}{V:failureReason/%s}\n",
561 "Failure reason:", state->iss_reason);
562 xo_emit("{L:/%-18s}{V:headerDigest/%s}\n",
564 state->iss_header_digest == ISCSI_DIGEST_CRC32C ?
566 xo_emit("{L:/%-18s}{V:dataDigest/%s}\n",
568 state->iss_data_digest == ISCSI_DIGEST_CRC32C ?
570 xo_emit("{L:/%-18s}{V:dataSegmentLen/%d}\n",
571 "DataSegmentLen:", state->iss_max_data_segment_length);
572 xo_emit("{L:/%-18s}{V:maxBurstLen/%d}\n",
573 "MaxBurstLen:", state->iss_max_burst_length);
574 xo_emit("{L:/%-18s}{V:firstBurstLen/%d}\n",
575 "FirstBurstLen:", state->iss_first_burst_length);
576 xo_emit("{L:/%-18s}{V:immediateData/%s}\n",
577 "ImmediateData:", state->iss_immediate_data ? "Yes" : "No");
578 xo_emit("{L:/%-18s}{V:iSER/%s}\n",
579 "iSER (RDMA):", conf->isc_iser ? "Yes" : "No");
580 xo_emit("{L:/%-18s}{V:offloadDriver/%s}\n",
581 "Offload driver:", state->iss_offload);
582 xo_emit("{L:/%-18s}",
584 print_periphs(state->iss_id);
586 xo_close_instance("session");
588 xo_close_list("session");
590 xo_emit("{T:/%-36s} {T:/%-16s} {T:/%s}\n",
591 "Target name", "Target portal", "State");
593 if (isl.isl_nentries != 0)
594 xo_open_list("session");
595 for (i = 0; i < isl.isl_nentries; i++) {
598 conf = &state->iss_conf;
600 xo_open_instance("session");
601 xo_emit("{V:name/%-36s/%s} {V:portal/%-16s/%s} ",
602 conf->isc_target, conf->isc_target_addr);
604 if (state->iss_reason[0] != '\0' &&
605 conf->isc_enable != 0) {
606 xo_emit("{V:state/%s}\n", state->iss_reason);
608 if (conf->isc_discovery) {
609 xo_emit("{V:state}\n", "Discovery");
610 } else if (conf->isc_enable == 0) {
611 xo_emit("{V:state}\n", "Disabled");
612 } else if (state->iss_connected) {
613 xo_emit("{V:state}: ", "Connected");
614 print_periphs(state->iss_id);
617 xo_emit("{V:state}\n", "Disconnected");
620 xo_close_instance("session");
622 if (isl.isl_nentries != 0)
623 xo_close_list("session");
630 kernel_wait(int iscsi_fd, int timeout)
632 struct iscsi_session_state *states = NULL;
633 const struct iscsi_session_state *state;
634 struct iscsi_session_list isl;
635 unsigned int i, nentries = 1;
641 states = realloc(states,
642 nentries * sizeof(struct iscsi_session_state));
644 xo_err(1, "realloc");
646 memset(&isl, 0, sizeof(isl));
647 isl.isl_nentries = nentries;
648 isl.isl_pstates = states;
650 error = ioctl(iscsi_fd, ISCSISLIST, &isl);
651 if (error != 0 && errno == EMSGSIZE) {
658 xo_warn("ISCSISLIST");
662 all_connected = true;
663 for (i = 0; i < isl.isl_nentries; i++) {
666 if (!state->iss_connected) {
667 all_connected = false;
689 fprintf(stderr, "usage: iscsictl -A -p portal -t target "
690 "[-u user -s secret] [-w timeout] [-e on | off]\n");
691 fprintf(stderr, " iscsictl -A -d discovery-host "
692 "[-u user -s secret] [-e on | off]\n");
693 fprintf(stderr, " iscsictl -A -a [-c path]\n");
694 fprintf(stderr, " iscsictl -A -n nickname [-c path]\n");
695 fprintf(stderr, " iscsictl -M -i session-id [-p portal] "
696 "[-t target] [-u user] [-s secret] [-e on | off]\n");
697 fprintf(stderr, " iscsictl -M -i session-id -n nickname "
699 fprintf(stderr, " iscsictl -R [-p portal] [-t target]\n");
700 fprintf(stderr, " iscsictl -R -a\n");
701 fprintf(stderr, " iscsictl -R -n nickname [-c path]\n");
702 fprintf(stderr, " iscsictl -L [-v] [-w timeout]\n");
707 main(int argc, char **argv)
709 int Aflag = 0, Mflag = 0, Rflag = 0, Lflag = 0, aflag = 0,
710 rflag = 0, vflag = 0;
711 const char *conf_path = DEFAULT_CONFIG_PATH;
712 char *nickname = NULL, *discovery_host = NULL, *portal = NULL,
713 *target = NULL, *user = NULL, *secret = NULL;
714 int timeout = -1, enable = ENABLE_UNSPECIFIED;
715 long long session_id = -1;
717 int ch, error, iscsi_fd, retval, saved_errno;
722 argc = xo_parse_args(argc, argv);
723 xo_open_container("iscsictl");
725 while ((ch = getopt(argc, argv, "AMRLac:d:e:i:n:p:rt:u:s:vw:")) != -1) {
746 discovery_host = optarg;
749 enable = parse_enable(optarg);
750 if (enable == ENABLE_UNSPECIFIED) {
751 xo_errx(1, "invalid argument to -e, "
752 "must be either \"on\" or \"off\"");
756 session_id = strtol(optarg, &end, 10);
757 if ((size_t)(end - optarg) != strlen(optarg))
758 xo_errx(1, "trailing characters after session-id");
760 xo_errx(1, "session-id cannot be negative");
761 if (session_id > UINT_MAX)
762 xo_errx(1, "session-id cannot be greater than %u",
787 timeout = strtol(optarg, &end, 10);
788 if ((size_t)(end - optarg) != strlen(optarg))
789 xo_errx(1, "trailing characters after timeout");
791 xo_errx(1, "timeout cannot be negative");
802 if (Aflag + Mflag + Rflag + Lflag == 0)
804 if (Aflag + Mflag + Rflag + Lflag > 1)
805 xo_errx(1, "at most one of -A, -M, -R, or -L may be specified");
808 * Note that we ignore unnecessary/inapplicable "-c" flag; so that
809 * people can do something like "alias ISCSICTL="iscsictl -c path"
814 if (enable != ENABLE_UNSPECIFIED)
815 xo_errx(1, "-a and -e and mutually exclusive");
817 xo_errx(1, "-a and -p and mutually exclusive");
819 xo_errx(1, "-a and -t and mutually exclusive");
821 xo_errx(1, "-a and -u and mutually exclusive");
823 xo_errx(1, "-a and -s and mutually exclusive");
824 if (nickname != NULL)
825 xo_errx(1, "-a and -n and mutually exclusive");
826 if (discovery_host != NULL)
827 xo_errx(1, "-a and -d and mutually exclusive");
829 xo_errx(1, "-a and -r and mutually exclusive");
830 } else if (nickname != NULL) {
831 if (enable != ENABLE_UNSPECIFIED)
832 xo_errx(1, "-n and -e and mutually exclusive");
834 xo_errx(1, "-n and -p and mutually exclusive");
836 xo_errx(1, "-n and -t and mutually exclusive");
838 xo_errx(1, "-n and -u and mutually exclusive");
840 xo_errx(1, "-n and -s and mutually exclusive");
841 if (discovery_host != NULL)
842 xo_errx(1, "-n and -d and mutually exclusive");
844 xo_errx(1, "-n and -r and mutually exclusive");
845 } else if (discovery_host != NULL) {
847 xo_errx(1, "-d and -p and mutually exclusive");
849 xo_errx(1, "-d and -t and mutually exclusive");
851 if (target == NULL && portal == NULL)
852 xo_errx(1, "must specify -a, -n or -t/-p");
854 if (target != NULL && portal == NULL)
855 xo_errx(1, "-t must always be used with -p");
856 if (portal != NULL && target == NULL)
857 xo_errx(1, "-p must always be used with -t");
860 if (user != NULL && secret == NULL)
861 xo_errx(1, "-u must always be used with -s");
862 if (secret != NULL && user == NULL)
863 xo_errx(1, "-s must always be used with -u");
865 if (session_id != -1)
866 xo_errx(1, "-i cannot be used with -A");
868 xo_errx(1, "-v cannot be used with -A");
870 } else if (Mflag != 0) {
871 if (session_id == -1)
872 xo_errx(1, "-M requires -i");
874 if (nickname != NULL) {
875 if (enable != ENABLE_UNSPECIFIED)
876 xo_errx(1, "-n and -e and mutually exclusive");
878 xo_errx(1, "-n and -p and mutually exclusive");
880 xo_errx(1, "-n and -t and mutually exclusive");
882 xo_errx(1, "-n and -u and mutually exclusive");
884 xo_errx(1, "-n and -s and mutually exclusive");
888 xo_errx(1, "-a cannot be used with -M");
889 if (discovery_host != NULL)
890 xo_errx(1, "-d cannot be used with -M");
892 xo_errx(1, "-r cannot be used with -M");
894 xo_errx(1, "-v cannot be used with -M");
896 xo_errx(1, "-w cannot be used with -M");
898 } else if (Rflag != 0) {
901 xo_errx(1, "-a and -p and mutually exclusive");
903 xo_errx(1, "-a and -t and mutually exclusive");
904 if (nickname != NULL)
905 xo_errx(1, "-a and -n and mutually exclusive");
906 } else if (nickname != NULL) {
908 xo_errx(1, "-n and -p and mutually exclusive");
910 xo_errx(1, "-n and -t and mutually exclusive");
911 } else if (target == NULL && portal == NULL) {
912 xo_errx(1, "must specify either -a, -n, -t, or -p");
915 if (discovery_host != NULL)
916 xo_errx(1, "-d cannot be used with -R");
917 if (enable != ENABLE_UNSPECIFIED)
918 xo_errx(1, "-e cannot be used with -R");
919 if (session_id != -1)
920 xo_errx(1, "-i cannot be used with -R");
922 xo_errx(1, "-r cannot be used with -R");
924 xo_errx(1, "-u cannot be used with -R");
926 xo_errx(1, "-s cannot be used with -R");
928 xo_errx(1, "-v cannot be used with -R");
930 xo_errx(1, "-w cannot be used with -R");
935 if (discovery_host != NULL)
936 xo_errx(1, "-d cannot be used with -L");
937 if (session_id != -1)
938 xo_errx(1, "-i cannot be used with -L");
939 if (nickname != NULL)
940 xo_errx(1, "-n cannot be used with -L");
942 xo_errx(1, "-p cannot be used with -L");
944 xo_errx(1, "-r cannot be used with -L");
946 xo_errx(1, "-t cannot be used with -L");
948 xo_errx(1, "-u cannot be used with -L");
950 xo_errx(1, "-s cannot be used with -L");
953 iscsi_fd = open(ISCSI_PATH, O_RDWR);
954 if (iscsi_fd < 0 && errno == ENOENT) {
956 retval = kldload("iscsi");
958 iscsi_fd = open(ISCSI_PATH, O_RDWR);
963 xo_err(1, "failed to open %s", ISCSI_PATH);
965 if (Aflag != 0 && aflag != 0) {
966 conf = conf_new_from_file(conf_path);
968 TAILQ_FOREACH(targ, &conf->conf_targets, t_next)
969 failed += kernel_add(iscsi_fd, targ);
970 } else if (nickname != NULL) {
971 conf = conf_new_from_file(conf_path);
972 targ = target_find(conf, nickname);
974 xo_errx(1, "target %s not found in %s",
975 nickname, conf_path);
978 failed += kernel_add(iscsi_fd, targ);
980 failed += kernel_modify(iscsi_fd, session_id, targ);
982 failed += kernel_remove(iscsi_fd, targ);
984 failed += kernel_list(iscsi_fd, targ, vflag);
985 } else if (Mflag != 0) {
986 kernel_modify_some(iscsi_fd, session_id, target, portal,
987 user, secret, enable);
989 if (Aflag != 0 && target != NULL) {
990 if (valid_iscsi_name(target) == false)
991 xo_errx(1, "invalid target name \"%s\"", target);
994 targ = target_new(conf);
995 targ->t_initiator_name = default_initiator_name();
996 targ->t_header_digest = DIGEST_NONE;
997 targ->t_data_digest = DIGEST_NONE;
998 targ->t_name = target;
999 if (discovery_host != NULL) {
1000 targ->t_session_type = SESSION_TYPE_DISCOVERY;
1001 targ->t_address = discovery_host;
1003 targ->t_session_type = SESSION_TYPE_NORMAL;
1004 targ->t_address = portal;
1006 targ->t_enable = enable;
1008 targ->t_protocol = PROTOCOL_ISER;
1009 targ->t_user = user;
1010 targ->t_secret = secret;
1013 failed += kernel_add(iscsi_fd, targ);
1014 else if (Rflag != 0)
1015 failed += kernel_remove(iscsi_fd, targ);
1017 failed += kernel_list(iscsi_fd, targ, vflag);
1021 failed += kernel_wait(iscsi_fd, timeout);
1023 error = close(iscsi_fd);
1027 xo_close_container("iscsictl");