diff --git a/Makefile.am b/Makefile.am
index 4d861ea..61cfaba 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -9,11 +9,11 @@ LDADD = lib/libiscsi.la
XSLTPROC = /usr/bin/xsltproc
# Manpages
-man1_MANS = doc/iscsi-inq.1 doc/iscsi-ls.1
+man1_MANS = doc/iscsi-inq.1 doc/iscsi-ls.1 doc/iscsi-swp.1
EXTRA_DIST = autogen.sh COPYING LICENCE-GPL-2.txt LICENCE-LGPL-2.1.txt \
packaging/RPM/libiscsi.spec.in packaging/RPM/makerpms.sh \
- doc/iscsi-inq.1.xml doc/iscsi-ls.1.xml
+ doc/iscsi-inq.1.xml doc/iscsi-ls.1.xml doc/iscsi-swp.1.xml
# Simplify conditions below by declaring variables as empty
@@ -50,9 +50,11 @@ lib_libiscsi_la_LDFLAGS = \
# libiscsi utilities
-bin_PROGRAMS += bin/iscsi-inq bin/iscsi-ls bin/iscsi-readcapacity16
+bin_PROGRAMS += bin/iscsi-inq bin/iscsi-ls bin/iscsi-readcapacity16 \
+ bin/iscsi-swp
bin_iscsi_inq_SOURCES = src/iscsi-inq.c
bin_iscsi_ls_SOURCES = src/iscsi-ls.c
+bin_iscsi_swp_SOURCES = src/iscsi-swp.c
bin_iscsi_readcapacity16_SOURCES = src/iscsi-readcapacity16.c
# Other examples
@@ -387,3 +389,7 @@ doc/iscsi-ls.1: doc/iscsi-ls.1.xml
doc/iscsi-inq.1: doc/iscsi-inq.1.xml
-test -z "$(XSLTPROC)" || $(XSLTPROC) -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $<
+
+doc/iscsi-swp.1: doc/iscsi-swp.1.xml
+ -test -z "$(XSLTPROC)" || $(XSLTPROC) -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $<
+
diff --git a/doc/iscsi-swp.1.xml b/doc/iscsi-swp.1.xml
new file mode 100644
index 0000000..ec1c683
--- /dev/null
+++ b/doc/iscsi-swp.1.xml
@@ -0,0 +1,146 @@
+
+
+
+
+
+ iscsi-swp
+ 1
+ iscsi-swp
+ iscsi-swp: get/set software write protect
+
+
+
+
+ iscsi-swp
+ Utility to get/set software write protect on an iSCSI LUN
+
+
+
+
+ iscsi-swp [ OPTIONS ] <ISCSI-PORTAL>
+
+
+
+ iscsi-ls
+ -i --initiator-name=<IQN>
+ -s --swp {on|off}
+ -d --debug=<INTEGER>
+ -? --help
+ --usage
+
+
+
+
+ DESCRIPTION
+
+ iscsi-swp is a utility to get or set the software write protect on an iSCSI LUN.
+
+
+
+ ISCSI PORTAL URL FORMAT
+
+ iSCSI portal format is 'iscsi://[<username>[%<password>]@]<host>[:<port>]'
+
+
+
+ Username and password are only required if the target requires CHAP
+ authentication. Optionally you can specify the username and password via
+ the environment variables LIBISCSI_CHAP_USERNAME and
+ LIBISCSI_CHAP_PASSWORD.
+
+
+
+ Host can be specified either as a hostname, an IPv4 address or an
+ IPv6 address.
+
+ Examples:
+
+ iscsi://192.0.2.1
+ iscsi://[2001:DB8::1]:3261
+ iscsi://ronnie%password@iscsi.example.com
+
+
+
+
+ Port is the TCP port on the target to connect to. Default is 3260.
+
+
+
+
+ OPTIONS
+
+
+
+ -i --initiator-name=<IQN>
+
+
+ This specifies the initiator-name that iscsi-ls will use when
+ logging in to the target.
+
+
+ The default name is
+ 'iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-ls' but you can use
+ this argument to override this. This is mainly needed for cases
+ where the target is configured with access-control to only
+ allow discovery logins from known initiator-names.
+
+
+
+
+ -s --swp {on|off}
+
+
+ By default iscsi-swp will only print the current setting of
+ the software write protect bit. By using this argument
+ iscsi-swp will also try to set/clear the flag on the target LUN.
+
+
+iscsi-swp iscsi://127.0.0.1/iqn.ronnie.test/1
+SWP:0
+
+iscsi-swp iscsi://127.0.0.1/iqn.ronnie.test/1 --swp on
+SWP:0
+Turning SWP ON
+
+iscsi-swp iscsi://127.0.0.1/iqn.ronnie.test/1 --swp off
+SWP:0
+Turning SWP OFF
+
+
+
+
+ -d --debug=<INTEGER>
+
+
+ Debug level.
+
+
+
+
+ -? --help
+
+
+ Display basic help text.
+
+
+
+
+ --usage
+
+
+ Display basic usage text.
+
+
+
+
+
+
+
+ SEE ALSO
+
+ iscsi-inq(1), iscsi-ls(1)
+
+
+
+
+
diff --git a/src/iscsi-swp.c b/src/iscsi-swp.c
new file mode 100644
index 0000000..e118c82
--- /dev/null
+++ b/src/iscsi-swp.c
@@ -0,0 +1,251 @@
+/*
+ Copyright (C) 2013 by Ronnie Sahlberg
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see .
+*/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_POLL_H
+#include
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include "iscsi.h"
+#include "scsi-lowlevel.h"
+
+#ifndef discard_const
+#define discard_const(ptr) ((void *)((intptr_t)(ptr)))
+#endif
+
+const char *initiator = "iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-swp";
+
+
+void print_usage(void)
+{
+ fprintf(stderr, "Usage: iscsi-swp [-?] [-?|--help] [--usage] [-i|--initiator-name=iqn-name]\n"
+ "\t\t[-s|--swp {on|off}] \n");
+}
+
+void print_help(void)
+{
+ fprintf(stderr, "Usage: iscsi-swp [OPTION...] \n");
+ fprintf(stderr, " -i, --initiator-name=iqn-name Initiatorname to use\n");
+ fprintf(stderr, " -s, --swp={on|off} Turn software write protect on/off\n");
+ fprintf(stderr, " -d, --debug=integer debug level (0=disabled)\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Help options:\n");
+ fprintf(stderr, " -?, --help Show this help message\n");
+ fprintf(stderr, " --usage Display brief usage message\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "iSCSI URL format : %s\n", ISCSI_URL_SYNTAX);
+ fprintf(stderr, "\n");
+ fprintf(stderr, " is either of:\n");
+ fprintf(stderr, " \"hostname\" iscsi.example\n");
+ fprintf(stderr, " \"ipv4-address\" 10.1.1.27\n");
+ fprintf(stderr, " \"ipv6-address\" [fce0::1]\n");
+}
+
+int main(int argc, char *argv[])
+{
+ struct iscsi_context *iscsi;
+ const char *url = NULL;
+ struct iscsi_url *iscsi_url = NULL;
+ int show_help = 0, show_usage = 0, debug = 0;
+ int c;
+ int ret = 0;
+ int swp = 0;
+ struct scsi_task *sense_task = NULL;
+ struct scsi_task *select_task = NULL;
+ struct scsi_mode_sense *ms;
+ struct scsi_mode_page *mp;
+
+ static struct option long_options[] = {
+ {"help", no_argument, NULL, 'h'},
+ {"usage", no_argument, NULL, 'u'},
+ {"debug", no_argument, NULL, 'd'},
+ {"initiator_name", required_argument, NULL, 'i'},
+ {"swp", required_argument, NULL, 's'},
+ {0, 0, 0, 0}
+ };
+ int option_index;
+
+ while ((c = getopt_long(argc, argv, "h?udi:s:", long_options,
+ &option_index)) != -1) {
+ switch (c) {
+ case 'h':
+ case '?':
+ show_help = 1;
+ break;
+ case 'u':
+ show_usage = 1;
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'i':
+ initiator = optarg;
+ break;
+ case 's':
+ if (!strcmp(optarg, "on") || !strcmp(optarg, "ON")) {
+ swp = 1;
+ }
+ if (!strcmp(optarg, "off") || !strcmp(optarg, "OFF")) {
+ swp = 2;
+ }
+ break;
+ default:
+ fprintf(stderr, "Unrecognized option '%c'\n\n", c);
+ print_help();
+ exit(0);
+ }
+ }
+
+ if (show_help != 0) {
+ print_help();
+ exit(0);
+ }
+
+ if (show_usage != 0) {
+ print_usage();
+ exit(0);
+ }
+
+ iscsi = iscsi_create_context(initiator);
+ if (iscsi == NULL) {
+ fprintf(stderr, "Failed to create context\n");
+ exit(10);
+ }
+
+ if (debug > 0) {
+ iscsi_set_log_level(iscsi, debug);
+ iscsi_set_log_fn(iscsi, iscsi_log_to_stderr);
+ }
+
+ url = strdup(argv[optind]);
+ if (url == NULL) {
+ fprintf(stderr, "You must specify the URL\n");
+ print_usage();
+ ret = 10;
+ goto finished;
+ }
+ iscsi_url = iscsi_parse_full_url(iscsi, url);
+
+ free(discard_const(url));
+
+ if (iscsi_url == NULL) {
+ fprintf(stderr, "Failed to parse URL: %s\n",
+ iscsi_get_error(iscsi));
+ ret = 10;
+ goto finished;
+ }
+
+ iscsi_set_targetname(iscsi, iscsi_url->target);
+ iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL);
+ iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
+
+ if (iscsi_url->user != NULL) {
+ if (iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user, iscsi_url->passwd) != 0) {
+ fprintf(stderr, "Failed to set initiator username and password\n");
+ ret = 10;
+ goto finished;
+ }
+ }
+
+ if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) {
+ fprintf(stderr, "Login Failed. %s\n", iscsi_get_error(iscsi));
+ ret = 10;
+ goto finished;
+ }
+
+
+ sense_task = iscsi_modesense6_sync(iscsi, iscsi_url->lun,
+ 0, SCSI_MODESENSE_PC_CURRENT,
+ SCSI_MODESENSE_PAGECODE_CONTROL,
+ 0, 255);
+ if (sense_task == NULL) {
+ printf("Failed to send MODE_SENSE6 command: %s\n",
+ iscsi_get_error(iscsi));
+ ret = 10;
+ goto finished;
+ }
+ if (sense_task->status != SCSI_STATUS_GOOD) {
+ printf("MODE_SENSE6 failed: %s\n",
+ iscsi_get_error(iscsi));
+ ret = 10;
+ goto finished;
+ }
+ ms = scsi_datain_unmarshall(sense_task);
+ if (ms == NULL) {
+ printf("failed to unmarshall mode sense datain blob\n");
+ ret = 10;
+ goto finished;
+ }
+ mp = scsi_modesense_get_page(ms, SCSI_MODESENSE_PAGECODE_CONTROL, 0);
+ if (mp == NULL) {
+ printf("failed to read control mode page\n");
+ ret = 10;
+ goto finished;
+ }
+
+ printf("SWP:%d\n", mp->control.swp);
+
+ switch (swp) {
+ case 1:
+ mp->control.swp = 1;
+ break;
+ case 2:
+ mp->control.swp = 0;
+ break;
+ default:
+ goto finished;
+ }
+
+ printf("Turning SWP %s\n", (swp == 1) ? "ON" : "OFF");
+ select_task = iscsi_modeselect6_sync(iscsi, iscsi_url->lun,
+ 1, 0, mp);
+ if (select_task == NULL) {
+ printf("Failed to send MODE_SELECT6 command: %s\n",
+ iscsi_get_error(iscsi));
+ ret = 10;
+ goto finished;
+ }
+ if (select_task->status != SCSI_STATUS_GOOD) {
+ printf("MODE_SELECT6 failed: %s\n",
+ iscsi_get_error(iscsi));
+ ret = 10;
+ goto finished;
+ }
+
+
+finished:
+ if (sense_task != NULL) {
+ scsi_free_scsi_task(sense_task);
+ }
+ if (select_task != NULL) {
+ scsi_free_scsi_task(select_task);
+ }
+ if (iscsi_url != NULL) {
+ iscsi_destroy_url(iscsi_url);
+ }
+ iscsi_logout_sync(iscsi);
+ iscsi_destroy_context(iscsi);
+ return ret;
+}
+