summaryrefslogtreecommitdiffstats
path: root/usr.bin
diff options
context:
space:
mode:
authortrasz <trasz@FreeBSD.org>2013-09-14 15:29:06 +0000
committertrasz <trasz@FreeBSD.org>2013-09-14 15:29:06 +0000
commita992abf0413f4cc428d4368e1d82d65f5b3d6397 (patch)
treed04af1389a0e20c7613b9dccaf4f3176084e40cc /usr.bin
parent889b9d0e0bc82fd4927dd02d64c973c36fa661a3 (diff)
downloadFreeBSD-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')
-rw-r--r--usr.bin/Makefile1
-rw-r--r--usr.bin/iscsictl/Makefile19
-rw-r--r--usr.bin/iscsictl/iscsictl.8153
-rw-r--r--usr.bin/iscsictl/iscsictl.c732
-rw-r--r--usr.bin/iscsictl/iscsictl.h116
-rw-r--r--usr.bin/iscsictl/parse.y333
-rw-r--r--usr.bin/iscsictl/periphs.c186
-rw-r--r--usr.bin/iscsictl/token.l93
8 files changed, 1633 insertions, 0 deletions
diff --git a/usr.bin/Makefile b/usr.bin/Makefile
index 09fb97c..8f4d920 100644
--- a/usr.bin/Makefile
+++ b/usr.bin/Makefile
@@ -68,6 +68,7 @@ SUBDIR= alias \
id \
ipcrm \
ipcs \
+ iscsictl \
join \
jot \
${_kdump} \
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 */;
+%%
OpenPOWER on IntegriCloud