diff options
author | trasz <trasz@FreeBSD.org> | 2013-09-14 15:29:06 +0000 |
---|---|---|
committer | trasz <trasz@FreeBSD.org> | 2013-09-14 15:29:06 +0000 |
commit | a992abf0413f4cc428d4368e1d82d65f5b3d6397 (patch) | |
tree | d04af1389a0e20c7613b9dccaf4f3176084e40cc /usr.bin/iscsictl | |
parent | 889b9d0e0bc82fd4927dd02d64c973c36fa661a3 (diff) | |
download | FreeBSD-src-a992abf0413f4cc428d4368e1d82d65f5b3d6397.zip FreeBSD-src-a992abf0413f4cc428d4368e1d82d65f5b3d6397.tar.gz |
Bring in the new iSCSI target and initiator.
Reviewed by: ken (parts)
Approved by: re (delphij)
Sponsored by: FreeBSD Foundation
Diffstat (limited to 'usr.bin/iscsictl')
-rw-r--r-- | usr.bin/iscsictl/Makefile | 19 | ||||
-rw-r--r-- | usr.bin/iscsictl/iscsictl.8 | 153 | ||||
-rw-r--r-- | usr.bin/iscsictl/iscsictl.c | 732 | ||||
-rw-r--r-- | usr.bin/iscsictl/iscsictl.h | 116 | ||||
-rw-r--r-- | usr.bin/iscsictl/parse.y | 333 | ||||
-rw-r--r-- | usr.bin/iscsictl/periphs.c | 186 | ||||
-rw-r--r-- | usr.bin/iscsictl/token.l | 93 |
7 files changed, 1632 insertions, 0 deletions
diff --git a/usr.bin/iscsictl/Makefile b/usr.bin/iscsictl/Makefile new file mode 100644 index 0000000..9331ca5 --- /dev/null +++ b/usr.bin/iscsictl/Makefile @@ -0,0 +1,19 @@ +# $FreeBSD$ + +PROG= iscsictl +SRCS= iscsictl.c periphs.c parse.y token.l y.tab.h +CFLAGS+= -I${.CURDIR} +CFLAGS+= -I${.CURDIR}/../../sys/dev/iscsi +MAN= iscsictl.8 + +DPADD= ${LIBCAM} ${LIBUTIL} +LDADD= -lcam -lfl -lutil + +YFLAGS+= -v +LFLAGS+= -i +CLEANFILES= y.tab.c y.tab.h y.output + +WARNS= 6 +NO_WMISSING_VARIABLE_DECLARATIONS= + +.include <bsd.prog.mk> diff --git a/usr.bin/iscsictl/iscsictl.8 b/usr.bin/iscsictl/iscsictl.8 new file mode 100644 index 0000000..06edca8 --- /dev/null +++ b/usr.bin/iscsictl/iscsictl.8 @@ -0,0 +1,153 @@ +.\" Copyright (c) 2012 The FreeBSD Foundation +.\" All rights reserved. +.\" +.\" 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 AUTHORS 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 AUTHORS 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. +.\" +.\" $FreeBSD$ +.\" +.Dd September 20, 2012 +.Dt ISCSICTL 8 +.Os +.Sh NAME +.Nm iscsictl +.Nd iSCSI initiator management utility +.Sh SYNOPSIS +.Nm +.Fl A +.Fl h Ar host Fl t Ar target Op Fl u Ar user Fl s Ar secret +.Nm +.Fl A +.Fl d Ar discovery-host Op Fl u Ar user Fl s Ar secret +.Nm +.Fl A +.Fl a Op Fl c Ar path +.Nm +.Fl A +.Fl n Ar nickname Op Fl c Ar path +.Nm +.Fl R +.Op Fl h Ar host +.Op Fl t Ar target +.Nm +.Fl R +.Fl a +.Nm +.Fl R +.Fl n Ar nickname Op Fl c Ar path +.Nm +.Fl L +.Op Fl v +.Sh DESCRIPTION +The +.Nm +utility is used to configure the iSCSI initiator. +.Pp +The following options are available: +.Bl -tag -width ".Fl A" +.It Fl A +Add session. +.It Fl R +Remove session. +.It Fl L +List sessions. +.It Fl a +When adding, add all sessions defined in the configuration file. +When removing, remove all currently established sessions. +.It Fl c +Path to the configuration file. +The default is +.Pa /etc/iscsi.conf . +.It Fl d +Target host name or address used for SendTargets discovery. +When used, it will add a temporary discovery session. +After discovery is done, sessions will be added for each discovered target, +and the temporary discovery sesion will be removed. +.It Fl h +Target host name or address for statically defined targets. +.It Fl n +The "nickname" of session defined in the configuration file. +.It Fl t +Target name. +.It Fl v +Verbose mode. +.El +.Pp +Since connecting to the target is performed in background, non-zero +exit status does not mean that the session was successfully established. +Use +.Nm Fl L +to check the connection status. +The initiator notifies +.Xr devd 8 +when session gets connected or disconnected. +.Pp +Note that in order to the iSCSI initiator to be able to connect to a target, +the +.Xr iscsid 8 +daemon must be running. +.Pp +Also note that +.Fx +currently supports two different initiators: the old one, +.Xr iscsi_initiator 4 , +with its control utility +.Xr iscontrol 8 , +and the new one, +.Xr iscsi 4 , +with +.Xr iscsictl 8 +and +.Xr iscsid 8 . +The only thing the two have in common is the configuration file, +.Xr iscsi.conf 5 . +.Sh FILES +.Bl -tag -width ".Pa /etc/iscsi.conf" -compact +.It Pa /etc/iscsi.conf +iSCSI initiator configuration file. +.El +.Sh EXIT STATUS +The +.Nm +utility exits 0 on success, and >0 if an error occurs. +.Sh EXAMPLES +Attach to target qn.2012-06.com.example:target0, served by 192.168.1.1: +.Dl Nm Fl A Fl t Ar qn.2012-06.com.example:target0 Fl h Ar 192.168.1.1 +.Pp +Disconnect all iSCSI sessions: +.Dl Nm Fl Ra +.Sh SEE ALSO +.Xr iscsid 8 , +.Xr iscsi.conf 5 +.Sh HISTORY +The +.Nm +command appeared in +.Fx 10.0 . +.Sh AUTHORS +The +.Nm +was developed by +.An Edward Tomasz Napierala Aq trasz@FreeBSD.org +under sponsorship from the FreeBSD Foundation. diff --git a/usr.bin/iscsictl/iscsictl.c b/usr.bin/iscsictl/iscsictl.c new file mode 100644 index 0000000..ff37c12 --- /dev/null +++ b/usr.bin/iscsictl/iscsictl.c @@ -0,0 +1,732 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * 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. + * + * $FreeBSD$ + */ + +#include <sys/ioctl.h> +#include <assert.h> +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <iscsi_ioctl.h> +#include "iscsictl.h" + +struct conf * +conf_new(void) +{ + struct conf *conf; + + conf = calloc(1, sizeof(*conf)); + if (conf == NULL) + 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) + err(1, "calloc"); + targ->t_conf = conf; + 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) + err(1, "calloc"); + strcpy(name, DEFAULT_IQN); + error = gethostname(name + strlen(DEFAULT_IQN), + namelen - strlen(DEFAULT_IQN)); + if (error != 0) + 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); + } +} + +bool +valid_iscsi_name(const char *name) +{ + int i; + + if (strlen(name) >= MAX_NAME_LEN) { + 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; + 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) + 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])) { + 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) + 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])) { + warnx("invalid character \"%c\" in ISCSI " + "name \"%s\"; allowed characters are 1-9 " + "and A-F", name[i], name); + break; + } + } + } else { + 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) + errx(1, "missing TargetName for target \"%s\"", + targ->t_nickname); + if (targ->t_session_type == SESSION_TYPE_DISCOVERY && + targ->t_name != NULL) + 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) + errx(1, "invalid target name \"%s\"", + targ->t_name); + } + if (targ->t_protocol == PROTOCOL_UNSPECIFIED) + targ->t_protocol = PROTOCOL_ISCSI; +#ifndef ICL_KERNEL_PROXY + if (targ->t_protocol == PROTOCOL_ISER) + errx(1, "iSER support requires ICL_KERNEL_PROXY; " + "see iscsi(4) for details"); +#endif + if (targ->t_address == NULL) + 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) + 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) { + errx(1, "missing chapIName for target \"%s\"", + targ->t_nickname); + } + if (targ->t_secret == NULL) + 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) + errx(1, "missing tgtChapName for " + "target \"%s\"", targ->t_nickname); + if (targ->t_mutual_secret == NULL) + 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_protocol == PROTOCOL_ISER) + conf->isc_iser = 1; + 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; +} + +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) + warn("ISCSISADD"); + return (error); +} + +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) + 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; + bool show_periphs; + + for (;;) { + states = realloc(states, + nentries * sizeof(struct iscsi_session_state)); + if (states == NULL) + 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) { + warn("ISCSISLIST"); + return (error); + } + + if (verbose != 0) { + for (i = 0; i < isl.isl_nentries; i++) { + state = &states[i]; + conf = &state->iss_conf; + + printf("Session ID: %d\n", state->iss_id); + printf("Initiator name: %s\n", conf->isc_initiator); + printf("Initiator addr: %s\n", + conf->isc_initiator_addr); + printf("Initiator alias: %s\n", + conf->isc_initiator_alias); + printf("Target name: %s\n", conf->isc_target); + printf("Target addr: %s\n", + conf->isc_target_addr); + printf("Target alias: %s\n", + state->iss_target_alias); + printf("User: %s\n", conf->isc_user); + printf("Secret: %s\n", conf->isc_secret); + printf("Mutual user: %s\n", + conf->isc_mutual_user); + printf("Mutual secret: %s\n", + conf->isc_mutual_secret); + printf("Session type: %s\n", + conf->isc_discovery ? "Discovery" : "Normal"); + printf("Session state: %s\n", + state->iss_connected ? + "Connected" : "Disconnected"); + printf("Failure reason: %s\n", state->iss_reason); + printf("Header digest: %s\n", + state->iss_header_digest == ISCSI_DIGEST_CRC32C ? + "CRC32C" : "None"); + printf("Data digest: %s\n", + state->iss_data_digest == ISCSI_DIGEST_CRC32C ? + "CRC32C" : "None"); + printf("DataSegmentLen: %d\n", + state->iss_max_data_segment_length); + printf("ImmediateData: %s\n", + state->iss_immediate_data ? "Yes" : "No"); + printf("iSER (RDMA): %s\n", + conf->isc_iser ? "Yes" : "No"); + printf("Device nodes: "); + print_periphs(state->iss_id); + printf("\n\n"); + } + } else { + printf("%-36s %-16s %s\n", + "Target name", "Target addr", "State"); + for (i = 0; i < isl.isl_nentries; i++) { + state = &states[i]; + conf = &state->iss_conf; + show_periphs = false; + + printf("%-36s %-16s ", + conf->isc_target, conf->isc_target_addr); + + if (state->iss_reason[0] != '\0') { + printf("%s\n", state->iss_reason); + } else { + if (conf->isc_discovery) { + printf("Discovery\n"); + } else if (state->iss_connected) { + printf("Connected: "); + print_periphs(state->iss_id); + printf("\n"); + } else { + printf("Disconnected\n"); + } + } + } + } + + return (0); +} + +static void +usage(void) +{ + + fprintf(stderr, "usage: iscsictl -A -h host -t target " + "[-u user -s secret]\n"); + fprintf(stderr, " iscsictl -A -d discovery-host " + "[-u user -s secret]\n"); + fprintf(stderr, " iscsictl -A -a [-c path]\n"); + fprintf(stderr, " iscsictl -A -n nickname [-c path]\n"); + fprintf(stderr, " iscsictl -R [-h host] [-t target]\n"); + fprintf(stderr, " iscsictl -R -a\n"); + fprintf(stderr, " iscsictl -R -n nickname [-c path]\n"); + fprintf(stderr, " iscsictl -L [-v]\n"); + exit(1); +} + +char * +checked_strdup(const char *s) +{ + char *c; + + c = strdup(s); + if (c == NULL) + err(1, "strdup"); + return (c); +} + +int +main(int argc, char **argv) +{ + int Aflag = 0, Rflag = 0, Lflag = 0, aflag = 0, vflag = 0; + const char *conf_path = DEFAULT_CONFIG_PATH; + char *nickname = NULL, *discovery_host = NULL, *host = NULL, + *target = NULL, *user = NULL, *secret = NULL; + int ch, error, iscsi_fd; + int failed = 0; + struct conf *conf; + struct target *targ; + + while ((ch = getopt(argc, argv, "ARLac:d:n:h:t:u:s:v")) != -1) { + switch (ch) { + case 'A': + Aflag = 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 'n': + nickname = optarg; + break; + case 'h': + host = optarg; + break; + case 't': + target = optarg; + break; + case 'u': + user = optarg; + break; + case 's': + secret = optarg; + break; + case 'v': + vflag = 1; + break; + case '?': + default: + usage(); + } + } + argc -= optind; + if (argc != 0) + usage(); + + if (Aflag + Rflag + Lflag == 0) + Lflag = 1; + if (Aflag + Rflag + Lflag > 1) + errx(1, "at most one of -A, -R, or -L may be specified"); + + /* + * Note that we ignore unneccessary/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 (host != NULL) + errx(1, "-a and -h and mutually exclusive"); + if (target != NULL) + errx(1, "-a and -t and mutually exclusive"); + if (user != NULL) + errx(1, "-a and -u and mutually exclusive"); + if (secret != NULL) + errx(1, "-a and -s and mutually exclusive"); + if (nickname != NULL) + errx(1, "-a and -n and mutually exclusive"); + if (discovery_host != NULL) + errx(1, "-a and -d and mutually exclusive"); + } else if (nickname != NULL) { + if (host != NULL) + errx(1, "-n and -h and mutually exclusive"); + if (target != NULL) + errx(1, "-n and -t and mutually exclusive"); + if (user != NULL) + errx(1, "-n and -u and mutually exclusive"); + if (secret != NULL) + errx(1, "-n and -s and mutually exclusive"); + if (discovery_host != NULL) + errx(1, "-n and -d and mutually exclusive"); + } else if (discovery_host != NULL) { + if (host != NULL) + errx(1, "-d and -h and mutually exclusive"); + if (target != NULL) + errx(1, "-d and -t and mutually exclusive"); + } else { + if (target == NULL && host == NULL) + errx(1, "must specify -a, -n or -t/-h"); + + if (target != NULL && host == NULL) + errx(1, "-t must always be used with -h"); + if (host != NULL && target == NULL) + errx(1, "-h must always be used with -t"); + } + + if (user != NULL && secret == NULL) + errx(1, "-u must always be used with -s"); + if (secret != NULL && user == NULL) + errx(1, "-s must always be used with -u"); + + if (vflag != 0) + errx(1, "-v cannot be used with -A"); + + } else if (Rflag != 0) { + if (user != NULL) + errx(1, "-R and -u are mutually exclusive"); + if (secret != NULL) + errx(1, "-R and -s are mutually exclusive"); + if (discovery_host != NULL) + errx(1, "-R and -d are mutually exclusive"); + + if (aflag != 0) { + if (host != NULL) + errx(1, "-a and -h and mutually exclusive"); + if (target != NULL) + errx(1, "-a and -t and mutually exclusive"); + if (nickname != NULL) + errx(1, "-a and -n and mutually exclusive"); + } else if (nickname != NULL) { + if (host != NULL) + errx(1, "-n and -h and mutually exclusive"); + if (target != NULL) + errx(1, "-n and -t and mutually exclusive"); + } else if (host != NULL) { + if (target != NULL) + errx(1, "-h and -t and mutually exclusive"); + } else if (target != NULL) { + if (host != NULL) + errx(1, "-t and -h and mutually exclusive"); + } else + errx(1, "must specify either-a, -n, -t, or -h"); + + if (vflag != 0) + errx(1, "-v cannot be used with -R"); + + } else { + assert(Lflag != 0); + + if (host != NULL) + errx(1, "-L and -h and mutually exclusive"); + if (target != NULL) + errx(1, "-L and -t and mutually exclusive"); + if (user != NULL) + errx(1, "-L and -u and mutually exclusive"); + if (secret != NULL) + errx(1, "-L and -s and mutually exclusive"); + if (nickname != NULL) + errx(1, "-L and -n and mutually exclusive"); + if (discovery_host != NULL) + errx(1, "-L and -d and mutually exclusive"); + } + + iscsi_fd = open(ISCSI_PATH, O_RDWR); + if (iscsi_fd < 0) + 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) + errx(1, "target %s not found in the configuration file", + nickname); + + 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); + } else { + if (Aflag != 0 && target != NULL) { + if (valid_iscsi_name(target) == false) + 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 = host; + } + 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); + } + + error = close(iscsi_fd); + if (error != 0) + err(1, "close"); + + if (failed > 0) + return (1); + return (0); +} diff --git a/usr.bin/iscsictl/iscsictl.h b/usr.bin/iscsictl/iscsictl.h new file mode 100644 index 0000000..e8d4768 --- /dev/null +++ b/usr.bin/iscsictl/iscsictl.h @@ -0,0 +1,116 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * 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. + * + * $FreeBSD$ + */ + +#ifndef ISCSICTL_H +#define ISCSICTL_H + +#include <sys/queue.h> +#include <stdbool.h> +#include <libutil.h> + +#define DEFAULT_CONFIG_PATH "/etc/iscsi.conf" +#define DEFAULT_IQN "iqn.1994-09.org.freebsd:" + +#define MAX_NAME_LEN 223 +#define MAX_DATA_SEGMENT_LENGTH 65536 + +#define AUTH_METHOD_UNSPECIFIED 0 +#define AUTH_METHOD_NONE 1 +#define AUTH_METHOD_CHAP 2 + +#define DIGEST_UNSPECIFIED 0 +#define DIGEST_NONE 1 +#define DIGEST_CRC32C 2 + +#define SESSION_TYPE_UNSPECIFIED 0 +#define SESSION_TYPE_NORMAL 1 +#define SESSION_TYPE_DISCOVERY 2 + +#define PROTOCOL_UNSPECIFIED 0 +#define PROTOCOL_ISCSI 1 +#define PROTOCOL_ISER 2 + +struct target { + TAILQ_ENTRY(target) t_next; + struct conf *t_conf; + char *t_nickname; + char *t_name; + char *t_address; + char *t_initiator_name; + char *t_initiator_address; + char *t_initiator_alias; + int t_header_digest; + int t_data_digest; + int t_auth_method; + int t_session_type; + int t_protocol; + char *t_user; + char *t_secret; + char *t_mutual_user; + char *t_mutual_secret; +}; + +struct conf { + TAILQ_HEAD(, target) conf_targets; +}; + +#define CONN_SESSION_TYPE_NONE 0 +#define CONN_SESSION_TYPE_DISCOVERY 1 +#define CONN_SESSION_TYPE_NORMAL 2 + +struct connection { + struct target *conn_target; + int conn_socket; + int conn_session_type; + uint32_t conn_cmdsn; + uint32_t conn_statsn; + size_t conn_max_data_segment_length; + size_t conn_max_burst_length; + size_t conn_max_outstanding_r2t; + int conn_header_digest; + int conn_data_digest; +}; + +struct conf *conf_new(void); +struct conf *conf_new_from_file(const char *path); +void conf_delete(struct conf *conf); +void conf_verify(struct conf *conf); + +struct target *target_new(struct conf *conf); +struct target *target_find(struct conf *conf, const char *nickname); +void target_delete(struct target *ic); + +void print_periphs(int session_id); + +char *checked_strdup(const char *); +bool valid_iscsi_name(const char *name); + +#endif /* !ISCSICTL_H */ diff --git a/usr.bin/iscsictl/parse.y b/usr.bin/iscsictl/parse.y new file mode 100644 index 0000000..b44fc3b --- /dev/null +++ b/usr.bin/iscsictl/parse.y @@ -0,0 +1,333 @@ +%{ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * 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. + * + * $FreeBSD$ + */ + +#include <sys/queue.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <assert.h> +#include <err.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "iscsictl.h" + +extern FILE *yyin; +extern char *yytext; +extern int lineno; + +static struct conf *conf; +static struct target *target; + +extern void yyerror(const char *); +extern int yylex(void); +extern void yyrestart(FILE *); + +%} + +%token AUTH_METHOD HEADER_DIGEST DATA_DIGEST TARGET_NAME TARGET_ADDRESS +%token INITIATOR_NAME INITIATOR_ADDRESS INITIATOR_ALIAS USER SECRET +%token MUTUAL_USER MUTUAL_SECRET SESSION_TYPE PROTOCOL IGNORED +%token EQUALS OPENING_BRACKET CLOSING_BRACKET + +%union +{ + char *str; +} + +%token <str> STR + +%% + +statements: + | + statements target_statement + ; + +target_statement: STR OPENING_BRACKET target_entries CLOSING_BRACKET + { + if (target_find(conf, $1) != NULL) + errx(1, "duplicated target %s", $1); + target->t_nickname = $1; + target = target_new(conf); + } + ; + +target_entries: + | + target_entries target_entry + ; + +target_entry: + target_name_statement + | + target_address_statement + | + initiator_name_statement + | + initiator_address_statement + | + initiator_alias_statement + | + user_statement + | + secret_statement + | + mutual_user_statement + | + mutual_secret_statement + | + auth_method_statement + | + header_digest_statement + | + data_digest_statement + | + session_type_statement + | + protocol_statement + | + ignored_statement + ; + +target_name_statement: TARGET_NAME EQUALS STR + { + if (target->t_name != NULL) + errx(1, "duplicated TargetName at line %d", lineno + 1); + target->t_name = $3; + } + ; + +target_address_statement: TARGET_ADDRESS EQUALS STR + { + if (target->t_address != NULL) + errx(1, "duplicated TargetAddress at line %d", lineno + 1); + target->t_address = $3; + } + ; + +initiator_name_statement: INITIATOR_NAME EQUALS STR + { + if (target->t_initiator_name != NULL) + errx(1, "duplicated InitiatorName at line %d", lineno + 1); + target->t_initiator_name = $3; + } + ; + +initiator_address_statement: INITIATOR_ADDRESS EQUALS STR + { + if (target->t_initiator_address != NULL) + errx(1, "duplicated InitiatorAddress at line %d", lineno + 1); + target->t_initiator_address = $3; + } + ; + +initiator_alias_statement: INITIATOR_ALIAS EQUALS STR + { + if (target->t_initiator_alias != NULL) + errx(1, "duplicated InitiatorAlias at line %d", lineno + 1); + target->t_initiator_alias = $3; + } + ; + +user_statement: USER EQUALS STR + { + if (target->t_user != NULL) + errx(1, "duplicated chapIName at line %d", lineno + 1); + target->t_user = $3; + } + ; + +secret_statement: SECRET EQUALS STR + { + if (target->t_secret != NULL) + errx(1, "duplicated chapSecret at line %d", lineno + 1); + target->t_secret = $3; + } + ; + +mutual_user_statement: MUTUAL_USER EQUALS STR + { + if (target->t_mutual_user != NULL) + errx(1, "duplicated tgtChapName at line %d", lineno + 1); + target->t_mutual_user = $3; + } + ; + +mutual_secret_statement:MUTUAL_SECRET EQUALS STR + { + if (target->t_mutual_secret != NULL) + errx(1, "duplicated tgtChapSecret at line %d", lineno + 1); + target->t_mutual_secret = $3; + } + ; + +auth_method_statement: AUTH_METHOD EQUALS STR + { + if (target->t_auth_method != AUTH_METHOD_UNSPECIFIED) + errx(1, "duplicated AuthMethod at line %d", lineno + 1); + if (strcasecmp($3, "none") == 0) + target->t_auth_method = AUTH_METHOD_NONE; + else if (strcasecmp($3, "chap") == 0) + target->t_auth_method = AUTH_METHOD_CHAP; + else + errx(1, "invalid AuthMethod at line %d; " + "must be either \"none\" or \"CHAP\"", lineno + 1); + } + ; + +header_digest_statement: HEADER_DIGEST EQUALS STR + { + if (target->t_header_digest != DIGEST_UNSPECIFIED) + errx(1, "duplicated HeaderDigest at line %d", lineno + 1); + if (strcasecmp($3, "none") == 0) + target->t_header_digest = DIGEST_NONE; + else if (strcasecmp($3, "CRC32C") == 0) + target->t_header_digest = DIGEST_CRC32C; + else + errx(1, "invalid HeaderDigest at line %d; " + "must be either \"none\" or \"CRC32C\"", lineno + 1); + } + ; + +data_digest_statement: DATA_DIGEST EQUALS STR + { + if (target->t_data_digest != DIGEST_UNSPECIFIED) + errx(1, "duplicated DataDigest at line %d", lineno + 1); + if (strcasecmp($3, "none") == 0) + target->t_data_digest = DIGEST_NONE; + else if (strcasecmp($3, "CRC32C") == 0) + target->t_data_digest = DIGEST_CRC32C; + else + errx(1, "invalid DataDigest at line %d; " + "must be either \"none\" or \"CRC32C\"", lineno + 1); + } + ; + +session_type_statement: SESSION_TYPE EQUALS STR + { + if (target->t_session_type != SESSION_TYPE_UNSPECIFIED) + errx(1, "duplicated SessionType at line %d", lineno + 1); + if (strcasecmp($3, "normal") == 0) + target->t_session_type = SESSION_TYPE_NORMAL; + else if (strcasecmp($3, "discovery") == 0) + target->t_session_type = SESSION_TYPE_DISCOVERY; + else + errx(1, "invalid SessionType at line %d; " + "must be either \"normal\" or \"discovery\"", lineno + 1); + } + ; + +protocol_statement: PROTOCOL EQUALS STR + { + if (target->t_protocol != PROTOCOL_UNSPECIFIED) + errx(1, "duplicated protocol at line %d", lineno + 1); + if (strcasecmp($3, "iscsi") == 0) + target->t_protocol = PROTOCOL_ISCSI; + else if (strcasecmp($3, "iser") == 0) + target->t_protocol = PROTOCOL_ISER; + else + errx(1, "invalid protocol at line %d; " + "must be either \"iscsi\" or \"iser\"", lineno + 1); + } + ; + +ignored_statement: IGNORED EQUALS STR + { + warnx("obsolete statement ignored at line %d", lineno + 1); + } + ; + +%% + +void +yyerror(const char *str) +{ + + errx(1, "error in configuration file at line %d near '%s': %s", + lineno + 1, yytext, str); +} + +static void +check_perms(const char *path) +{ + struct stat sb; + int error; + + error = stat(path, &sb); + if (error != 0) { + warn("stat"); + return; + } + if (sb.st_mode & S_IWOTH) { + warnx("%s is world-writable", path); + } else if (sb.st_mode & S_IROTH) { + warnx("%s is world-readable", path); + } else if (sb.st_mode & S_IXOTH) { + /* + * Ok, this one doesn't matter, but still do it, + * just for consistency. + */ + warnx("%s is world-executable", path); + } + + /* + * XXX: Should we also check for owner != 0? + */ +} + +struct conf * +conf_new_from_file(const char *path) +{ + int error; + + conf = conf_new(); + target = target_new(conf); + + yyin = fopen(path, "r"); + if (yyin == NULL) + err(1, "unable to open configuration file %s", path); + check_perms(path); + lineno = 0; + yyrestart(yyin); + error = yyparse(); + assert(error == 0); + fclose(yyin); + + assert(target->t_nickname == NULL); + target_delete(target); + + conf_verify(conf); + + return (conf); +} diff --git a/usr.bin/iscsictl/periphs.c b/usr.bin/iscsictl/periphs.c new file mode 100644 index 0000000..78b237f --- /dev/null +++ b/usr.bin/iscsictl/periphs.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 1997-2007 Kenneth D. Merry + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/ioctl.h> +#include <sys/stdint.h> +#include <sys/types.h> +#include <sys/endian.h> +#include <sys/sbuf.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <inttypes.h> +#include <limits.h> +#include <fcntl.h> +#include <err.h> + +#include <cam/cam.h> +#include <cam/cam_debug.h> +#include <cam/cam_ccb.h> +#include <cam/scsi/scsi_all.h> +#include <cam/scsi/scsi_da.h> +#include <cam/scsi/scsi_pass.h> +#include <cam/scsi/scsi_message.h> +#include <cam/scsi/smp_all.h> +#include <cam/ata/ata_all.h> +#include <camlib.h> + +#include "iscsictl.h" + +void +print_periphs(int session_id) +{ + union ccb ccb; + int bufsize, fd; + unsigned int i; + int skip_bus, skip_device; + + if ((fd = open(XPT_DEVICE, O_RDWR)) == -1) { + warn("couldn't open %s", XPT_DEVICE); + return; + } + + /* + * First, iterate over the whole list to find the bus. + */ + + bzero(&ccb, sizeof(union ccb)); + + ccb.ccb_h.path_id = CAM_XPT_PATH_ID; + ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; + ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; + + ccb.ccb_h.func_code = XPT_DEV_MATCH; + bufsize = sizeof(struct dev_match_result) * 100; + ccb.cdm.match_buf_len = bufsize; + ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize); + if (ccb.cdm.matches == NULL) { + warnx("can't malloc memory for matches"); + close(fd); + return; + } + ccb.cdm.num_matches = 0; + + /* + * We fetch all nodes, since we display most of them in the default + * case, and all in the verbose case. + */ + ccb.cdm.num_patterns = 0; + ccb.cdm.pattern_buf_len = 0; + + /* + * We do the ioctl multiple times if necessary, in case there are + * more than 100 nodes in the EDT. + */ + do { + if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) { + warn("error sending CAMIOCOMMAND ioctl"); + break; + } + + if ((ccb.ccb_h.status != CAM_REQ_CMP) + || ((ccb.cdm.status != CAM_DEV_MATCH_LAST) + && (ccb.cdm.status != CAM_DEV_MATCH_MORE))) { + warnx("got CAM error %#x, CDM error %d\n", + ccb.ccb_h.status, ccb.cdm.status); + break; + } + + skip_bus = 1; + skip_device = 1; + + for (i = 0; i < ccb.cdm.num_matches; i++) { + switch (ccb.cdm.matches[i].type) { + case DEV_MATCH_BUS: { + struct bus_match_result *bus_result; + + bus_result = &ccb.cdm.matches[i].result.bus_result; + + skip_bus = 1; + + if (strcmp(bus_result->dev_name, "iscsi") != 0) { + //printf("not iscsi\n"); + continue; + } + + if ((int)bus_result->unit_number != session_id) { + //printf("wrong unit, %d != %d\n", bus_result->unit_number, session_id); + continue; + } + + skip_bus = 0; + } + case DEV_MATCH_DEVICE: { + skip_device = 1; + + if (skip_bus != 0) + continue; + + skip_device = 0; + + break; + } + case DEV_MATCH_PERIPH: { + struct periph_match_result *periph_result; + + periph_result = + &ccb.cdm.matches[i].result.periph_result; + + if (skip_device != 0) + continue; + + if (strcmp(periph_result->periph_name, "pass") == 0) + continue; + + fprintf(stdout, "%s%d ", + periph_result->periph_name, + periph_result->unit_number); + + break; + } + default: + fprintf(stdout, "unknown match type\n"); + break; + } + } + + } while ((ccb.ccb_h.status == CAM_REQ_CMP) + && (ccb.cdm.status == CAM_DEV_MATCH_MORE)); + + close(fd); +} + diff --git a/usr.bin/iscsictl/token.l b/usr.bin/iscsictl/token.l new file mode 100644 index 0000000..f6c03ae --- /dev/null +++ b/usr.bin/iscsictl/token.l @@ -0,0 +1,93 @@ +%{ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * 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. + * + * $FreeBSD$ + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> + +#include "iscsictl.h" +#include "y.tab.h" + +int lineno; + +#define YY_DECL int yylex(void) +extern int yylex(void); + +%} + +%option noinput +%option nounput + +%% +HeaderDigest { return HEADER_DIGEST; } +DataDigest { return DATA_DIGEST; } +TargetName { return TARGET_NAME; } +TargetAddress { return TARGET_ADDRESS; } +InitiatorName { return INITIATOR_NAME; } +InitiatorAddress { return INITIATOR_ADDRESS; } +InitiatorAlias { return INITIATOR_ALIAS; } +chapIName { return USER; } +chapSecret { return SECRET; } +tgtChapName { return MUTUAL_USER; } +tgtChapSecret { return MUTUAL_SECRET; } +AuthMethod { return AUTH_METHOD; } +SessionType { return SESSION_TYPE; } +protocol { return PROTOCOL; } +port { return IGNORED; } +MaxConnections { return IGNORED; } +TargetAlias { return IGNORED; } +TargetPortalGroupTag { return IGNORED; } +InitialR2T { return IGNORED; } +ImmediateData { return IGNORED; } +MaxRecvDataSegmentLength { return IGNORED; } +MaxBurstLength { return IGNORED; } +FirstBurstLength { return IGNORED; } +DefaultTime2Wait { return IGNORED; } +DefaultTime2Retain { return IGNORED; } +MaxOutstandingR2T { return IGNORED; } +DataPDUInOrder { return IGNORED; } +DataSequenceInOrder { return IGNORED; } +ErrorRecoveryLevel { return IGNORED; } +tags { return IGNORED; } +maxluns { return IGNORED; } +sockbufsize { return IGNORED; } +chapDigest { return IGNORED; } +\"[^"]+\" { yylval.str = strndup(yytext + 1, + strlen(yytext) - 2); return STR; } +[a-zA-Z0-9\.\-_/\:\[\]]+ { yylval.str = strdup(yytext); return STR; } +\{ { return OPENING_BRACKET; } +\} { return CLOSING_BRACKET; } += { return EQUALS; } +#.*$ /* ignore comments */; +\n { lineno++; } +[ \t]+ /* ignore whitespace */; +%% |