/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2012 The FreeBSD Foundation * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iscsictl.h" struct conf * conf_new(void) { struct conf *conf; conf = calloc(1, sizeof(*conf)); if (conf == NULL) xo_err(1, "calloc"); TAILQ_INIT(&conf->conf_targets); return (conf); } struct target * target_find(struct conf *conf, const char *nickname) { struct target *targ; TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { if (targ->t_nickname != NULL && strcasecmp(targ->t_nickname, nickname) == 0) return (targ); } return (NULL); } struct target * target_new(struct conf *conf) { struct target *targ; targ = calloc(1, sizeof(*targ)); if (targ == NULL) xo_err(1, "calloc"); targ->t_conf = conf; targ->t_dscp = -1; targ->t_pcp = -1; targ->t_pingtimeout = -1; targ->t_logintimeout = -1; TAILQ_INSERT_TAIL(&conf->conf_targets, targ, t_next); return (targ); } void target_delete(struct target *targ) { TAILQ_REMOVE(&targ->t_conf->conf_targets, targ, t_next); free(targ); } static char * default_initiator_name(void) { char *name; size_t namelen; int error; namelen = _POSIX_HOST_NAME_MAX + strlen(DEFAULT_IQN); name = calloc(1, namelen + 1); if (name == NULL) xo_err(1, "calloc"); strcpy(name, DEFAULT_IQN); error = gethostname(name + strlen(DEFAULT_IQN), namelen - strlen(DEFAULT_IQN)); if (error != 0) xo_err(1, "gethostname"); return (name); } static bool valid_hex(const char ch) { switch (ch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'a': case 'A': case 'b': case 'B': case 'c': case 'C': case 'd': case 'D': case 'e': case 'E': case 'f': case 'F': return (true); default: return (false); } } int parse_enable(const char *enable) { if (enable == NULL) return (ENABLE_UNSPECIFIED); if (strcasecmp(enable, "on") == 0 || strcasecmp(enable, "yes") == 0) return (ENABLE_ON); if (strcasecmp(enable, "off") == 0 || strcasecmp(enable, "no") == 0) return (ENABLE_OFF); return (ENABLE_UNSPECIFIED); } bool valid_iscsi_name(const char *name) { int i; if (strlen(name) >= MAX_NAME_LEN) { xo_warnx("overlong name for \"%s\"; max length allowed " "by iSCSI specification is %d characters", name, MAX_NAME_LEN); return (false); } /* * In the cases below, we don't return an error, just in case the admin * was right, and we're wrong. */ if (strncasecmp(name, "iqn.", strlen("iqn.")) == 0) { for (i = strlen("iqn."); name[i] != '\0'; i++) { /* * XXX: We should verify UTF-8 normalisation, as defined * by 3.2.6.2: iSCSI Name Encoding. */ if (isalnum(name[i])) continue; if (name[i] == '-' || name[i] == '.' || name[i] == ':') continue; xo_warnx("invalid character \"%c\" in iSCSI name " "\"%s\"; allowed characters are letters, digits, " "'-', '.', and ':'", name[i], name); break; } /* * XXX: Check more stuff: valid date and a valid reversed domain. */ } else if (strncasecmp(name, "eui.", strlen("eui.")) == 0) { if (strlen(name) != strlen("eui.") + 16) xo_warnx("invalid iSCSI name \"%s\"; the \"eui.\" " "should be followed by exactly 16 hexadecimal " "digits", name); for (i = strlen("eui."); name[i] != '\0'; i++) { if (!valid_hex(name[i])) { xo_warnx("invalid character \"%c\" in iSCSI " "name \"%s\"; allowed characters are 1-9 " "and A-F", name[i], name); break; } } } else if (strncasecmp(name, "naa.", strlen("naa.")) == 0) { if (strlen(name) > strlen("naa.") + 32) xo_warnx("invalid iSCSI name \"%s\"; the \"naa.\" " "should be followed by at most 32 hexadecimal " "digits", name); for (i = strlen("naa."); name[i] != '\0'; i++) { if (!valid_hex(name[i])) { xo_warnx("invalid character \"%c\" in ISCSI " "name \"%s\"; allowed characters are 1-9 " "and A-F", name[i], name); break; } } } else { xo_warnx("invalid iSCSI name \"%s\"; should start with " "either \".iqn\", \"eui.\", or \"naa.\"", name); } return (true); } void conf_verify(struct conf *conf) { struct target *targ; TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { assert(targ->t_nickname != NULL); if (targ->t_session_type == SESSION_TYPE_UNSPECIFIED) targ->t_session_type = SESSION_TYPE_NORMAL; if (targ->t_session_type == SESSION_TYPE_NORMAL && targ->t_name == NULL) xo_errx(1, "missing TargetName for target \"%s\"", targ->t_nickname); if (targ->t_session_type == SESSION_TYPE_DISCOVERY && targ->t_name != NULL) xo_errx(1, "cannot specify TargetName for discovery " "sessions for target \"%s\"", targ->t_nickname); if (targ->t_name != NULL) { if (valid_iscsi_name(targ->t_name) == false) xo_errx(1, "invalid target name \"%s\"", targ->t_name); } if (targ->t_protocol == PROTOCOL_UNSPECIFIED) targ->t_protocol = PROTOCOL_ISCSI; if (targ->t_address == NULL) xo_errx(1, "missing TargetAddress for target \"%s\"", targ->t_nickname); if (targ->t_initiator_name == NULL) targ->t_initiator_name = default_initiator_name(); if (valid_iscsi_name(targ->t_initiator_name) == false) xo_errx(1, "invalid initiator name \"%s\"", targ->t_initiator_name); if (targ->t_header_digest == DIGEST_UNSPECIFIED) targ->t_header_digest = DIGEST_NONE; if (targ->t_data_digest == DIGEST_UNSPECIFIED) targ->t_data_digest = DIGEST_NONE; if (targ->t_auth_method == AUTH_METHOD_UNSPECIFIED) { if (targ->t_user != NULL || targ->t_secret != NULL || targ->t_mutual_user != NULL || targ->t_mutual_secret != NULL) targ->t_auth_method = AUTH_METHOD_CHAP; else targ->t_auth_method = AUTH_METHOD_NONE; } if (targ->t_auth_method == AUTH_METHOD_CHAP) { if (targ->t_user == NULL) { xo_errx(1, "missing chapIName for target \"%s\"", targ->t_nickname); } if (targ->t_secret == NULL) xo_errx(1, "missing chapSecret for target \"%s\"", targ->t_nickname); if (targ->t_mutual_user != NULL || targ->t_mutual_secret != NULL) { if (targ->t_mutual_user == NULL) xo_errx(1, "missing tgtChapName for " "target \"%s\"", targ->t_nickname); if (targ->t_mutual_secret == NULL) xo_errx(1, "missing tgtChapSecret for " "target \"%s\"", targ->t_nickname); } } } } static void conf_from_target(struct iscsi_session_conf *conf, const struct target *targ) { memset(conf, 0, sizeof(*conf)); /* * XXX: Check bounds and return error instead of silently truncating. */ if (targ->t_initiator_name != NULL) strlcpy(conf->isc_initiator, targ->t_initiator_name, sizeof(conf->isc_initiator)); if (targ->t_initiator_address != NULL) strlcpy(conf->isc_initiator_addr, targ->t_initiator_address, sizeof(conf->isc_initiator_addr)); if (targ->t_initiator_alias != NULL) strlcpy(conf->isc_initiator_alias, targ->t_initiator_alias, sizeof(conf->isc_initiator_alias)); if (targ->t_name != NULL) strlcpy(conf->isc_target, targ->t_name, sizeof(conf->isc_target)); if (targ->t_address != NULL) strlcpy(conf->isc_target_addr, targ->t_address, sizeof(conf->isc_target_addr)); if (targ->t_user != NULL) strlcpy(conf->isc_user, targ->t_user, sizeof(conf->isc_user)); if (targ->t_secret != NULL) strlcpy(conf->isc_secret, targ->t_secret, sizeof(conf->isc_secret)); if (targ->t_mutual_user != NULL) strlcpy(conf->isc_mutual_user, targ->t_mutual_user, sizeof(conf->isc_mutual_user)); if (targ->t_mutual_secret != NULL) strlcpy(conf->isc_mutual_secret, targ->t_mutual_secret, sizeof(conf->isc_mutual_secret)); if (targ->t_session_type == SESSION_TYPE_DISCOVERY) conf->isc_discovery = 1; if (targ->t_enable != ENABLE_OFF) conf->isc_enable = 1; if (targ->t_protocol == PROTOCOL_ISER) conf->isc_iser = 1; if (targ->t_offload != NULL) strlcpy(conf->isc_offload, targ->t_offload, sizeof(conf->isc_offload)); if (targ->t_header_digest == DIGEST_CRC32C) conf->isc_header_digest = ISCSI_DIGEST_CRC32C; else conf->isc_header_digest = ISCSI_DIGEST_NONE; if (targ->t_data_digest == DIGEST_CRC32C) conf->isc_data_digest = ISCSI_DIGEST_CRC32C; else conf->isc_data_digest = ISCSI_DIGEST_NONE; conf->isc_dscp = targ->t_dscp; conf->isc_pcp = targ->t_pcp; conf->isc_ping_timeout = targ->t_pingtimeout; conf->isc_login_timeout = targ->t_logintimeout; } static int kernel_add(int iscsi_fd, const struct target *targ) { struct iscsi_session_add isa; int error; memset(&isa, 0, sizeof(isa)); conf_from_target(&isa.isa_conf, targ); error = ioctl(iscsi_fd, ISCSISADD, &isa); if (error != 0) xo_warn("ISCSISADD"); return (error); } static int kernel_modify(int iscsi_fd, unsigned int session_id, const struct target *targ) { struct iscsi_session_modify ism; int error; memset(&ism, 0, sizeof(ism)); ism.ism_session_id = session_id; conf_from_target(&ism.ism_conf, targ); error = ioctl(iscsi_fd, ISCSISMODIFY, &ism); if (error != 0) xo_warn("ISCSISMODIFY"); return (error); } static void kernel_modify_some(int iscsi_fd, unsigned int session_id, const char *target, const char *target_addr, const char *user, const char *secret, int enable) { struct iscsi_session_state *states = NULL; struct iscsi_session_state *state; struct iscsi_session_conf *conf; struct iscsi_session_list isl; struct iscsi_session_modify ism; unsigned int i, nentries = 1; int error; for (;;) { states = realloc(states, nentries * sizeof(struct iscsi_session_state)); if (states == NULL) xo_err(1, "realloc"); memset(&isl, 0, sizeof(isl)); isl.isl_nentries = nentries; isl.isl_pstates = states; error = ioctl(iscsi_fd, ISCSISLIST, &isl); if (error != 0 && errno == EMSGSIZE) { nentries *= 4; continue; } break; } if (error != 0) xo_errx(1, "ISCSISLIST"); for (i = 0; i < isl.isl_nentries; i++) { state = &states[i]; if (state->iss_id == session_id) break; } if (i == isl.isl_nentries) xo_errx(1, "session-id %u not found", session_id); conf = &state->iss_conf; if (target != NULL) strlcpy(conf->isc_target, target, sizeof(conf->isc_target)); if (target_addr != NULL) strlcpy(conf->isc_target_addr, target_addr, sizeof(conf->isc_target_addr)); if (user != NULL) strlcpy(conf->isc_user, user, sizeof(conf->isc_user)); if (secret != NULL) strlcpy(conf->isc_secret, secret, sizeof(conf->isc_secret)); if (enable == ENABLE_ON) conf->isc_enable = 1; else if (enable == ENABLE_OFF) conf->isc_enable = 0; memset(&ism, 0, sizeof(ism)); ism.ism_session_id = session_id; memcpy(&ism.ism_conf, conf, sizeof(ism.ism_conf)); error = ioctl(iscsi_fd, ISCSISMODIFY, &ism); if (error != 0) xo_warn("ISCSISMODIFY"); } static int kernel_remove(int iscsi_fd, const struct target *targ) { struct iscsi_session_remove isr; int error; memset(&isr, 0, sizeof(isr)); conf_from_target(&isr.isr_conf, targ); error = ioctl(iscsi_fd, ISCSISREMOVE, &isr); if (error != 0) xo_warn("ISCSISREMOVE"); return (error); } /* * XXX: Add filtering. */ static int kernel_list(int iscsi_fd, const struct target *targ __unused, int verbose) { struct iscsi_session_state *states = NULL; const struct iscsi_session_state *state; const struct iscsi_session_conf *conf; struct iscsi_session_list isl; unsigned int i, nentries = 1; int error; for (;;) { states = realloc(states, nentries * sizeof(struct iscsi_session_state)); if (states == NULL) xo_err(1, "realloc"); memset(&isl, 0, sizeof(isl)); isl.isl_nentries = nentries; isl.isl_pstates = states; error = ioctl(iscsi_fd, ISCSISLIST, &isl); if (error != 0 && errno == EMSGSIZE) { nentries *= 4; continue; } break; } if (error != 0) { xo_warn("ISCSISLIST"); return (error); } if (verbose != 0) { xo_open_list("session"); for (i = 0; i < isl.isl_nentries; i++) { state = &states[i]; conf = &state->iss_conf; xo_open_instance("session"); /* * Display-only modifier as this information * is also present within the 'session' container */ xo_emit("{L:/%-26s}{V:sessionId/%u}\n", "Session ID:", state->iss_id); xo_open_container("initiator"); xo_emit("{L:/%-26s}{V:name/%s}\n", "Initiator name:", conf->isc_initiator); xo_emit("{L:/%-26s}{V:portal/%s}\n", "Initiator portal:", conf->isc_initiator_addr); xo_emit("{L:/%-26s}{V:alias/%s}\n", "Initiator alias:", conf->isc_initiator_alias); xo_close_container("initiator"); xo_open_container("target"); xo_emit("{L:/%-26s}{V:name/%s}\n", "Target name:", conf->isc_target); xo_emit("{L:/%-26s}{V:portal/%s}\n", "Target portal:", conf->isc_target_addr); xo_emit("{L:/%-26s}{V:alias/%s}\n", "Target alias:", state->iss_target_alias); if (conf->isc_dscp != -1) xo_emit("{L:/%-26s}{V:dscp/0x%02x}\n", "Target DSCP:", conf->isc_dscp); if (conf->isc_pcp != -1) xo_emit("{L:/%-26s}{V:pcp/0x%02x}\n", "Target PCP:", conf->isc_pcp); if (conf->isc_ping_timeout != -1) xo_emit("{L:/%-26s}{V:PingTimeout/%d}\n", "Target PingTimeout:", conf->isc_ping_timeout); if (conf->isc_login_timeout != -1) xo_emit("{L:/%-26s}{V:LoginTimeout/%d}\n", "Target LoginTimeout:", conf->isc_login_timeout); xo_close_container("target"); xo_open_container("auth"); xo_emit("{L:/%-26s}{V:user/%s}\n", "User:", conf->isc_user); xo_emit("{L:/%-26s}{V:secret/%s}\n", "Secret:", conf->isc_secret); xo_emit("{L:/%-26s}{V:mutualUser/%s}\n", "Mutual user:", conf->isc_mutual_user); xo_emit("{L:/%-26s}{V:mutualSecret/%s}\n", "Mutual secret:", conf->isc_mutual_secret); xo_close_container("auth"); xo_emit("{L:/%-26s}{V:type/%s}\n", "Session type:", conf->isc_discovery ? "Discovery" : "Normal"); xo_emit("{L:/%-26s}{V:enable/%s}\n", "Enable:", conf->isc_enable ? "Yes" : "No"); xo_emit("{L:/%-26s}{V:state/%s}\n", "Session state:", state->iss_connected ? "Connected" : "Disconnected"); xo_emit("{L:/%-26s}{V:failureReason/%s}\n", "Failure reason:", state->iss_reason); xo_emit("{L:/%-26s}{V:headerDigest/%s}\n", "Header digest:", state->iss_header_digest == ISCSI_DIGEST_CRC32C ? "CRC32C" : "None"); xo_emit("{L:/%-26s}{V:dataDigest/%s}\n", "Data digest:", state->iss_data_digest == ISCSI_DIGEST_CRC32C ? "CRC32C" : "None"); xo_emit("{L:/%-26s}{V:recvDataSegmentLen/%d}\n", "MaxRecvDataSegmentLength:", state->iss_max_recv_data_segment_length); xo_emit("{L:/%-26s}{V:sendDataSegmentLen/%d}\n", "MaxSendDataSegmentLength:", state->iss_max_send_data_segment_length); xo_emit("{L:/%-26s}{V:maxBurstLen/%d}\n", "MaxBurstLen:", state->iss_max_burst_length); xo_emit("{L:/%-26s}{V:firstBurstLen/%d}\n", "FirstBurstLen:", state->iss_first_burst_length); xo_emit("{L:/%-26s}{V:immediateData/%s}\n", "ImmediateData:", state->iss_immediate_data ? "Yes" : "No"); xo_emit("{L:/%-26s}{V:iSER/%s}\n", "iSER (RDMA):", conf->isc_iser ? "Yes" : "No"); xo_emit("{L:/%-26s}{V:offloadDriver/%s}\n", "Offload driver:", state->iss_offload); xo_emit("{L:/%-26s}", "Device nodes:"); print_periphs(state->iss_id); xo_emit("\n\n"); xo_close_instance("session"); } xo_close_list("session"); } else { xo_emit("{T:/%-36s} {T:/%-16s} {T:/%s}\n", "Target name", "Target portal", "State"); if (isl.isl_nentries != 0) xo_open_list("session"); for (i = 0; i < isl.isl_nentries; i++) { state = &states[i]; conf = &state->iss_conf; xo_open_instance("session"); xo_emit("{V:name/%-36s/%s} {V:portal/%-16s/%s} ", conf->isc_target, conf->isc_target_addr); if (state->iss_reason[0] != '\0' && conf->isc_enable != 0) { xo_emit("{V:state/%s}\n", state->iss_reason); } else { if (conf->isc_discovery) { xo_emit("{V:state}\n", "Discovery"); } else if (conf->isc_enable == 0) { xo_emit("{V:state}\n", "Disabled"); } else if (state->iss_connected) { xo_emit("{V:state}: ", "Connected"); print_periphs(state->iss_id); xo_emit("\n"); } else { xo_emit("{V:state}\n", "Disconnected"); } } xo_close_instance("session"); } if (isl.isl_nentries != 0) xo_close_list("session"); } return (0); } static int kernel_wait(int iscsi_fd, int timeout) { struct iscsi_session_state *states = NULL; const struct iscsi_session_state *state; struct iscsi_session_list isl; unsigned int i, nentries = 1; bool all_connected; int error; for (;;) { for (;;) { states = realloc(states, nentries * sizeof(struct iscsi_session_state)); if (states == NULL) xo_err(1, "realloc"); memset(&isl, 0, sizeof(isl)); isl.isl_nentries = nentries; isl.isl_pstates = states; error = ioctl(iscsi_fd, ISCSISLIST, &isl); if (error != 0 && errno == EMSGSIZE) { nentries *= 4; continue; } break; } if (error != 0) { xo_warn("ISCSISLIST"); return (error); } all_connected = true; for (i = 0; i < isl.isl_nentries; i++) { state = &states[i]; if (!state->iss_connected) { all_connected = false; break; } } if (all_connected) return (0); sleep(1); if (timeout > 0) { timeout--; if (timeout == 0) return (1); } } } static void usage(void) { fprintf(stderr, "usage: iscsictl -A -p portal -t target " "[-u user -s secret] [-w timeout] [-e on | off]\n"); fprintf(stderr, " iscsictl -A -d discovery-host " "[-u user -s secret] [-e on | off]\n"); fprintf(stderr, " iscsictl -A -a [-c path]\n"); fprintf(stderr, " iscsictl -A -n nickname [-c path]\n"); fprintf(stderr, " iscsictl -M -i session-id [-p portal] " "[-t target] [-u user] [-s secret] [-e on | off]\n"); fprintf(stderr, " iscsictl -M -i session-id -n nickname " "[-c path]\n"); fprintf(stderr, " iscsictl -R [-p portal] [-t target]\n"); fprintf(stderr, " iscsictl -R -a\n"); fprintf(stderr, " iscsictl -R -n nickname [-c path]\n"); fprintf(stderr, " iscsictl -L [-v] [-w timeout]\n"); exit(1); } int main(int argc, char **argv) { int Aflag = 0, Mflag = 0, Rflag = 0, Lflag = 0, aflag = 0, rflag = 0, vflag = 0; const char *conf_path = DEFAULT_CONFIG_PATH; char *nickname = NULL, *discovery_host = NULL, *portal = NULL, *target = NULL, *user = NULL, *secret = NULL; int timeout = -1, enable = ENABLE_UNSPECIFIED; long long session_id = -1; char *end; int ch, error, iscsi_fd, retval, saved_errno; int failed = 0; struct conf *conf; struct target *targ; argc = xo_parse_args(argc, argv); if (argc < 0) exit(1); xo_open_container("iscsictl"); while ((ch = getopt(argc, argv, "AMRLac:d:e:i:n:p:rt:u:s:vw:")) != -1) { switch (ch) { case 'A': Aflag = 1; break; case 'M': Mflag = 1; break; case 'R': Rflag = 1; break; case 'L': Lflag = 1; break; case 'a': aflag = 1; break; case 'c': conf_path = optarg; break; case 'd': discovery_host = optarg; break; case 'e': enable = parse_enable(optarg); if (enable == ENABLE_UNSPECIFIED) { xo_errx(1, "invalid argument to -e, " "must be either \"on\" or \"off\""); } break; case 'i': session_id = strtol(optarg, &end, 10); if ((size_t)(end - optarg) != strlen(optarg)) xo_errx(1, "trailing characters after session-id"); if (session_id < 0) xo_errx(1, "session-id cannot be negative"); if (session_id > UINT_MAX) xo_errx(1, "session-id cannot be greater than %u", UINT_MAX); break; case 'n': nickname = optarg; break; case 'p': portal = optarg; break; case 'r': rflag = 1; break; case 't': target = optarg; break; case 'u': user = optarg; break; case 's': secret = optarg; break; case 'v': vflag = 1; break; case 'w': timeout = strtol(optarg, &end, 10); if ((size_t)(end - optarg) != strlen(optarg)) xo_errx(1, "trailing characters after timeout"); if (timeout < 0) xo_errx(1, "timeout cannot be negative"); break; case '?': default: usage(); } } argc -= optind; if (argc != 0) usage(); if (Aflag + Mflag + Rflag + Lflag == 0) Lflag = 1; if (Aflag + Mflag + Rflag + Lflag > 1) xo_errx(1, "at most one of -A, -M, -R, or -L may be specified"); /* * Note that we ignore unnecessary/inapplicable "-c" flag; so that * people can do something like "alias ISCSICTL="iscsictl -c path" * in shell scripts. */ if (Aflag != 0) { if (aflag != 0) { if (enable != ENABLE_UNSPECIFIED) xo_errx(1, "-a and -e are mutually exclusive"); if (portal != NULL) xo_errx(1, "-a and -p are mutually exclusive"); if (target != NULL) xo_errx(1, "-a and -t are mutually exclusive"); if (user != NULL) xo_errx(1, "-a and -u are mutually exclusive"); if (secret != NULL) xo_errx(1, "-a and -s are mutually exclusive"); if (nickname != NULL) xo_errx(1, "-a and -n are mutually exclusive"); if (discovery_host != NULL) xo_errx(1, "-a and -d are mutually exclusive"); if (rflag != 0) xo_errx(1, "-a and -r are mutually exclusive"); } else if (nickname != NULL) { if (enable != ENABLE_UNSPECIFIED) xo_errx(1, "-n and -e are mutually exclusive"); if (portal != NULL) xo_errx(1, "-n and -p are mutually exclusive"); if (target != NULL) xo_errx(1, "-n and -t are mutually exclusive"); if (user != NULL) xo_errx(1, "-n and -u are mutually exclusive"); if (secret != NULL) xo_errx(1, "-n and -s are mutually exclusive"); if (discovery_host != NULL) xo_errx(1, "-n and -d are mutually exclusive"); if (rflag != 0) xo_errx(1, "-n and -r are mutually exclusive"); } else if (discovery_host != NULL) { if (portal != NULL) xo_errx(1, "-d and -p are mutually exclusive"); if (target != NULL) xo_errx(1, "-d and -t are mutually exclusive"); } else { if (target == NULL && portal == NULL) xo_errx(1, "must specify -a, -n or -t/-p"); if (target != NULL && portal == NULL) xo_errx(1, "-t must always be used with -p"); if (portal != NULL && target == NULL) xo_errx(1, "-p must always be used with -t"); } if (user != NULL && secret == NULL) xo_errx(1, "-u must always be used with -s"); if (secret != NULL && user == NULL) xo_errx(1, "-s must always be used with -u"); if (session_id != -1) xo_errx(1, "-i cannot be used with -A"); if (vflag != 0) xo_errx(1, "-v cannot be used with -A"); } else if (Mflag != 0) { if (session_id == -1) xo_errx(1, "-M requires -i"); if (nickname != NULL) { if (enable != ENABLE_UNSPECIFIED) xo_errx(1, "-n and -e are mutually exclusive"); if (portal != NULL) xo_errx(1, "-n and -p are mutually exclusive"); if (target != NULL) xo_errx(1, "-n and -t are mutually exclusive"); if (user != NULL) xo_errx(1, "-n and -u are mutually exclusive"); if (secret != NULL) xo_errx(1, "-n and -s are mutually exclusive"); } if (aflag != 0) xo_errx(1, "-a cannot be used with -M"); if (discovery_host != NULL) xo_errx(1, "-d cannot be used with -M"); if (rflag != 0) xo_errx(1, "-r cannot be used with -M"); if (vflag != 0) xo_errx(1, "-v cannot be used with -M"); if (timeout != -1) xo_errx(1, "-w cannot be used with -M"); } else if (Rflag != 0) { if (aflag != 0) { if (portal != NULL) xo_errx(1, "-a and -p are mutually exclusive"); if (target != NULL) xo_errx(1, "-a and -t are mutually exclusive"); if (nickname != NULL) xo_errx(1, "-a and -n are mutually exclusive"); } else if (nickname != NULL) { if (portal != NULL) xo_errx(1, "-n and -p are mutually exclusive"); if (target != NULL) xo_errx(1, "-n and -t are mutually exclusive"); } else if (target == NULL && portal == NULL) { xo_errx(1, "must specify either -a, -n, -t, or -p"); } if (discovery_host != NULL) xo_errx(1, "-d cannot be used with -R"); if (enable != ENABLE_UNSPECIFIED) xo_errx(1, "-e cannot be used with -R"); if (session_id != -1) xo_errx(1, "-i cannot be used with -R"); if (rflag != 0) xo_errx(1, "-r cannot be used with -R"); if (user != NULL) xo_errx(1, "-u cannot be used with -R"); if (secret != NULL) xo_errx(1, "-s cannot be used with -R"); if (vflag != 0) xo_errx(1, "-v cannot be used with -R"); if (timeout != -1) xo_errx(1, "-w cannot be used with -R"); } else { assert(Lflag != 0); if (discovery_host != NULL) xo_errx(1, "-d cannot be used with -L"); if (session_id != -1) xo_errx(1, "-i cannot be used with -L"); if (nickname != NULL) xo_errx(1, "-n cannot be used with -L"); if (portal != NULL) xo_errx(1, "-p cannot be used with -L"); if (rflag != 0) xo_errx(1, "-r cannot be used with -L"); if (target != NULL) xo_errx(1, "-t cannot be used with -L"); if (user != NULL) xo_errx(1, "-u cannot be used with -L"); if (secret != NULL) xo_errx(1, "-s cannot be used with -L"); } iscsi_fd = open(ISCSI_PATH, O_RDWR); if (iscsi_fd < 0 && errno == ENOENT) { saved_errno = errno; retval = kldload("iscsi"); if (retval != -1) iscsi_fd = open(ISCSI_PATH, O_RDWR); else errno = saved_errno; } if (iscsi_fd < 0) xo_err(1, "failed to open %s", ISCSI_PATH); if (Aflag != 0 && aflag != 0) { conf = conf_new_from_file(conf_path); TAILQ_FOREACH(targ, &conf->conf_targets, t_next) failed += kernel_add(iscsi_fd, targ); } else if (nickname != NULL) { conf = conf_new_from_file(conf_path); targ = target_find(conf, nickname); if (targ == NULL) xo_errx(1, "target %s not found in %s", nickname, conf_path); if (Aflag != 0) failed += kernel_add(iscsi_fd, targ); else if (Mflag != 0) failed += kernel_modify(iscsi_fd, session_id, targ); else if (Rflag != 0) failed += kernel_remove(iscsi_fd, targ); else failed += kernel_list(iscsi_fd, targ, vflag); } else if (Mflag != 0) { kernel_modify_some(iscsi_fd, session_id, target, portal, user, secret, enable); } else { if (Aflag != 0 && target != NULL) { if (valid_iscsi_name(target) == false) xo_errx(1, "invalid target name \"%s\"", target); } conf = conf_new(); targ = target_new(conf); targ->t_initiator_name = default_initiator_name(); targ->t_header_digest = DIGEST_NONE; targ->t_data_digest = DIGEST_NONE; targ->t_name = target; if (discovery_host != NULL) { targ->t_session_type = SESSION_TYPE_DISCOVERY; targ->t_address = discovery_host; } else { targ->t_session_type = SESSION_TYPE_NORMAL; targ->t_address = portal; } targ->t_enable = enable; if (rflag != 0) targ->t_protocol = PROTOCOL_ISER; targ->t_user = user; targ->t_secret = secret; if (Aflag != 0) failed += kernel_add(iscsi_fd, targ); else if (Rflag != 0) failed += kernel_remove(iscsi_fd, targ); else failed += kernel_list(iscsi_fd, targ, vflag); } if (timeout != -1) failed += kernel_wait(iscsi_fd, timeout); error = close(iscsi_fd); if (error != 0) xo_err(1, "close"); xo_close_container("iscsictl"); xo_finish(); if (failed != 0) return (1); return (0); }