[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#808041: s390-zfcp: Install Debian on FC-attached SCSI devices on s390 (patches)



Control: tags -1 + patch

Below you can find the patches for the FCP configuration utility.
The patch set consists of five patches:

1. A README file describing how to use the FCP configuration utility.
2. Generic interfaces and functions to manage CCW devices on s390.
3. Functions to manage FCP devices.
4. The FCP configuration utility.
5. Debian packaging files to build the Debian Installer udeb.

Initial tests to configure FCP devices ran successful on z/VM and LPAR
using FCP devices with and without N_Port ID virtualization (NPIV).

Review and feedback of the patches is very welcome!  As a beginner in
Debian packaging, please review the Debian packaging files.

Thank you in advance!

Kind regards,
  Hendrik
From b88cc8f54a3badfa1585a7d3d87d0bade3067211 Mon Sep 17 00:00:00 2001
From: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
Date: Tue, 15 Dec 2015 11:06:21 +0100
Subject: [PATCH 1/5] s390-zfcp: README

Provide a README file with explanations and tipps for using the
FCP device configuration Debian Installer module.

Signed-off-by: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
---
 README | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 118 insertions(+)
 create mode 100644 README

diff --git a/README b/README
new file mode 100644
index 0000000..7537276
--- /dev/null
+++ b/README
@@ -0,0 +1,118 @@
+= FCP device management for the Debian Installer =
+
+***********************************************************************
+Debian Installer module to configure FCP devices to install Debian
+Linux instances on FC-attached SCSI devices on Linux on z Systems.
+***********************************************************************
+
+Using the FCP device configuration module
+-----------------------------------------
+You can use the FCP device configuration module in two different ways:
+
+1. Configuring FCP devices interactively
+2. Configuring FCP devices through preseeding
+
+Configuring FCP devices interactively
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+When the FCP device configuration module starts, it scans the
+CCW bus for FCP devices.  If it detects FCP devices, a panel
+appears to let the user select a particular FCP device for
+configuration.
+
+After the user selected a particular FCP device for configuration,
+the module enables the FCP device first.
+
+After the FCP device is active, the module checks if the FCP devices
+uses N_Port ID virtualization.  If the FCP device uses N_Port ID
+virtualization and automatic LUN scanning is switched on (the default
+setting), no further user configuration steps are required.
+The FCP device becomes configured.
+
+If the FCP does not use N_Port ID virtualization or automatic LUN
+scanning is switched off, the user is requested to specify LUNs.
+To add LUNs, the user must specify the target port (WWPN) and the
+logical unit number (LUN) as pair, WWPN:LUN.  The user can add
+numerous LUNs and, if necessary, can also remove them.  The specified
+LUNs will be attached to the system and the FCP device configuration
+module writes the respective configuration file.
+
+Configuring FCP devices through preseeding
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Use the *s390-zfcp/zfcp* variable to specify one or more FCP devices
+to be configured.  With this variable, you specify a comma-separated
+list of entries.  An entry can be either the bus-ID of an FCP device
+or a combination of the bus-ID of an FCP device followed by the WWPN
+and LUN, each delimited by a colon.
+
+For example:
+
+	0.0.1234,0.0.5678:0x2005000e11159c32:0x12345678000000
+
+The bus-ID for an FCP device is sufficient if
+
+1. the FCP device uses N_Port ID virtualization and
+2. automatic LUN scanning is switched on (default)
+
+You have to specify triplets consisting of the bus-ID of the FCP device,
+WWPN, and LUN in any other case.  Note that the FCP device configuration
+module fails if an entry specified with the *s390-zfcp/zfcp* variable is
+not valid.  An error message which describes the error is written to the
+syslog.
+
+
+Additional installation considerations
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+After you completed and configured your FC-attached SCSI devices,
+consider to set up multipath.  To prevent single path failures,
+install your Linux instance on those multipath devices.
+
+Note that it is sufficient to install on multipath devices even
+if there is only a single path available.  Later, you can extend
+your multipath setup and configure additional paths.  Note that
+configuring multipath later, on already installed Linux instances,
+is typically complex.
+
+
+Controlling the FCP device configuration behavior
+-------------------------------------------------
+You can control different aspects of the FCP device configuration
+through kernel and module parameters.  The FCP device configuration
+module does not change any of the kernel and module parameters
+described below.
+
+scsi_mod.scan
+~~~~~~~~~~~~~
+The *scsi_mod.scan* module parameter controls the behavior of the SCSI
+module when processing new target ports and LUN attachments.  Typical
+values are *sync* for synchronous and *async* for asynchronous processing.
+For FCP devices, *sync* is the preferred setting.  Note that Debian kernel
+might have a different value assigned.  For asynchronous processing, the
+FCP device configuration module must wait until a LUN becomes visible.
+This might cause timeouts, in particular, when preseeding is used.  If you
+experience timeouts, specify *sync* for *scsi_mod.scan*.
+
+cio_ignore
+~~~~~~~~~~
+Use the *cio_ignore* kernel parameter to limit the number of FCP devices
+that are visible to the FCP device configuration module.  The FCP device
+configuration module does not check nor remove FCP from the cio_ignore
+blacklist.  Note that Debian does not include support for managing
+cio_ignore blacklists.  So you can use this kernel parameter in general
+to limit the number of CCW devices visible to the Debian Installer and
+the Linux instance to be installed.
+
+zfcp.allow_lun_scan
+~~~~~~~~~~~~~~~~~~~
+Use the *zfcp.allow_lun_scan* module parameter to control the automatic
+LUN scanning for FCP devices with N_Port ID virtualization.   The FCP
+device configuration module checks this kernel parameter at startup.
+Automatic LUN scanning is enabled by default and there is no particular
+reason to turn it off.
+
+
+References
+----------
+- Device Drivers, Features, and Commands (SC33-8411-28)
+  http://public.dhe.ibm.com/software/dw/linux390/docu/l4n2dd28.pdf
+- How to use FC-attached SCSI devices with Linux on z Systems (SC33-8413-08)
+  http://public.dhe.ibm.com/software/dw/linux390/docu/l4n0sg08.pdf
-- 
2.6.2

From 6c28f62e7e0888fb10a22f1e6e3c544d236db24a Mon Sep 17 00:00:00 2001
From: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
Date: Tue, 15 Dec 2015 11:09:10 +0100
Subject: [PATCH 2/5] s390-dev: Add functions to manage CCW devices on s390

Add a set of functions and interfaces to manage CCW devices on s390.
The functions are used to get and set device attributes and perform
the respective sysfs operations.

These interfaces are isolated from the core FCP configuration module
because the functions are generic and could be re-used by other
modules as well.

Signed-off-by: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
---
 s390-dev-misc.c | 166 +++++++++++++++++++++++++++++++++++++++++++
 s390-dev-path.c | 191 +++++++++++++++++++++++++++++++++++++++++++++++++
 s390-dev.c      | 215 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 s390-dev.h      | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 764 insertions(+)
 create mode 100644 s390-dev-misc.c
 create mode 100644 s390-dev-path.c
 create mode 100644 s390-dev.c
 create mode 100644 s390-dev.h

diff --git a/s390-dev-misc.c b/s390-dev-misc.c
new file mode 100644
index 0000000..a1ff68f
--- /dev/null
+++ b/s390-dev-misc.c
@@ -0,0 +1,166 @@
+/*
+ * Miscellaneous helper functions
+ *
+ * Copyright IBM Corp. 2015
+ *
+ * Author(s): Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
+ *	      Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "s390-dev.h"
+
+#define READ_CHUNK_SIZE		4096
+
+/* Read text from @fd and return resulting NULL-terminated text buffer.
+ * If @chomp is non-zero, remove trailing newline character. Return %NULL
+ * on error or when unprintable characters are read.
+ */
+static char *read_fd(FILE *fd, int chomp)
+{
+	char *buffer = NULL;
+	size_t done, i;
+
+	done = 0;
+	while (!feof(fd)) {
+		buffer = realloc(buffer, done + READ_CHUNK_SIZE + 1);
+		if (buffer == NULL)
+			return NULL;
+		done += fread(&buffer[done], 1, READ_CHUNK_SIZE, fd);
+		if (ferror(fd)) {
+			free(buffer);
+			return NULL;
+		}
+	}
+
+	/* Check if this is a text file at all (required to filter out
+	 * binary sysfs attributes). */
+	for (i = 0; i < done; i++) {
+		if (!isgraph(buffer[i]) && !isspace(buffer[i])) {
+			free(buffer);
+			return NULL;
+		}
+	}
+
+	/* Remove trailing new-line character if requested. */
+	if (chomp && done > 0 && buffer[done - 1] == '\n')
+		done--;
+
+	if (buffer) {
+		/* NULL-terminate. */
+		buffer[done++] = 0;
+	}
+
+	return realloc(buffer, done);
+}
+
+/* Read file as text and return NULL-terminated contents. Remove trailing
+ * newline if CHOMP is specified.
+ */
+char *misc_read_text_file(const char *path, int chomp)
+{
+	char *buffer = NULL;
+	FILE *fd;
+
+	fd = fopen(path, "r");
+	if (fd == NULL)
+		goto out;
+
+	buffer = read_fd(fd, chomp);
+	fclose(fd);
+out:
+	return buffer;
+}
+
+/* Write text to file. */
+static int write_text(const char *path, const char *text)
+{
+	size_t len = strlen(text);
+	FILE *fd;
+	int err;
+
+	fd = fopen(path, "w");
+	if (fd == NULL)
+		goto err;
+	if (fwrite(text, 1, len, fd) != len)
+		goto err;
+	if (fclose(fd)) {
+		fd = NULL;
+		goto err;
+	}
+
+	return 0;
+err:
+	if (fd != NULL) {
+		err = errno;
+		fclose(fd);
+		errno = err;
+	}
+
+	return -1;
+}
+
+result_t misc_write_text_file(const char *path, const char *text)
+{
+	if (write_text(path, text))
+		return RESULT_RUNTIME_ERROR;
+
+	return RESULT_OK;
+}
+
+#define READLINE_SIZE	4096
+
+/* Return a newly allocated string containing the contents of the symbolic link
+ * at the specified path or NULL on error. */
+char *misc_readlink(const char *path)
+{
+	char *name;
+	ssize_t len;
+
+	name = malloc(READLINE_SIZE);
+	len = readlink(path, name, READLINE_SIZE - 1);
+	if (len < 0) {
+		free(name);
+		return NULL;
+	}
+	name[len++] = 0;
+
+	return realloc(name, len);
+}
+
+/* Check if a directory exists. */
+int dir_exists(const char *path)
+{
+	struct stat s;
+
+	if (stat(path, &s) != 0)
+		return 0;
+	return S_ISDIR(s.st_mode);
+}
+
+char *strtrim(char *s)
+{
+	size_t len;
+	char *trail;
+
+	len = strlen(s);
+	if (!len)
+		return s;
+
+	trail = s + len - 1;
+	while (trail >= s && isblank(*trail))
+		trail--;
+	trail[1] = '\0';
+
+	while (*s && isblank(*s))
+		s++;
+
+	return s;
+}
diff --git a/s390-dev-path.c b/s390-dev-path.c
new file mode 100644
index 0000000..9bfd171
--- /dev/null
+++ b/s390-dev-path.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright IBM Corp. 2015
+ *
+ * Author(s): Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
+ *	      Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "s390-dev.h"
+
+
+/* Return a path that is created by resolving the specified format string
+ * @fmt and applying any base prefixes. */
+char *path_get(const char *fmt, ...)
+{
+	va_list args;
+	char *path;
+
+	/* Get original path. */
+	va_start(args, fmt);
+	if (vasprintf(&path, fmt, args) == -1)
+		return NULL;
+	va_end(args);
+
+	return path;
+}
+
+/* Return sysfs path to module directory. */
+char *path_get_sys_module(const char *mod)
+{
+	return path_get("/sys/module/%s", mod);
+}
+
+/* Return sysfs path to module parameter file. */
+char *path_get_sys_module_param(const char *mod, const char *name)
+{
+	if (name)
+		return path_get("/sys/module/%s/parameters/%s", mod, name);
+
+	return path_get("/sys/module/%s/parameters", mod);
+}
+
+/* Return sysfs path to /sys/dev/block/major:minor directory. */
+char *path_get_sys_dev_block(unsigned int major, unsigned int minor)
+{
+	return path_get("/sys/dev/block/%d:%d", major, minor);
+}
+
+/* Return sysfs path to class directory. */
+char *path_get_sys_class(const char *class, const char *name)
+{
+	if (name)
+		return path_get("/sys/class/%s/%s", class, name);
+
+	return path_get("/sys/class/%s", class);
+}
+
+/* Return sysfs path to CCW device. */
+char *path_get_ccw_device(const char *drv, const char *id)
+{
+	if (drv)
+		return path_get("%s/drivers/%s/%s", PATH_CCW_BUS, drv, id);
+
+	return path_get("%s/devices/%s", PATH_CCW_BUS, id);
+}
+
+/* Return sysfs path to the specified attribute of a CCW device. */
+char *path_get_ccw_device_attr(const char *drv, const char *id,
+			       const char *attr) {
+	if (drv)
+		return path_get("%s/drivers/%s/%s/%s",
+				PATH_CCW_BUS, drv, id, attr);
+
+	return path_get("%s/devices/%s/%s", PATH_CCW_BUS, id, attr);
+}
+
+/* Return sysfs path to directory containing all CCW devices. */
+char *path_get_ccw_devices(const char *drv)
+{
+	if (drv)
+		return path_get("%s/drivers/%s/", PATH_CCW_BUS, drv);
+
+	return path_get("%s/devices/", PATH_CCW_BUS);
+}
+
+/* Return sysfs path to CCWGROUP device. */
+char *path_get_ccwgroup_device(const char *drv, const char *id)
+{
+	if (drv)
+		return path_get("%s/drivers/%s/%s", PATH_CCWGROUP_BUS, drv, id);
+
+	return path_get("%s/devices/%s", PATH_CCWGROUP_BUS, id);
+}
+
+/* Return sysfs path to directory containing all CCWGROUP devices. */
+char *path_get_ccwgroup_devices(const char *drv)
+{
+	if (drv)
+		return path_get("%s/drivers/%s/", PATH_CCWGROUP_BUS, drv);
+
+	return path_get("%s/devices/", PATH_CCWGROUP_BUS);
+}
+
+/* Call a function for each entry in a directory:
+ * exit_code_t callback(const char *abs_path, const char *rel_path, void *data)
+ * Aborts when callback returns any value other than EXIT_OK.
+ */
+result_t path_for_each(const char *path,
+			  result_t (*callback)(const char *, const char *,
+						  void *), void *data)
+{
+	DIR *dir;
+	struct dirent de, *dep;
+	char *p;
+	int err;
+	result_t rc = RESULT_OK;
+
+	dir = opendir(path);
+	if (dir == NULL)
+		return RESULT_RUNTIME_ERROR;
+
+	do {
+		err = readdir_r(dir, &de, &dep);
+		if (err) {
+			rc = RESULT_RUNTIME_ERROR;
+		} else if (dep) {
+			if (strcmp(de.d_name, ".") == 0 ||
+			    strcmp(de.d_name, "..") == 0)
+				continue;
+			p = path_get("%s/%s", path, de.d_name);
+			rc = callback(p, de.d_name, data);
+			free(p);
+		}
+	} while (dep && rc == RESULT_OK);
+
+	closedir(dir);
+
+	return rc;
+}
+
+/* Return sysfs path to device or devices directory. */
+char *path_get_sys_bus_dev(const char *bus, const char *id)
+{
+	if (!id)
+		return path_get("/sys/bus/%s/devices", bus);
+
+	return path_get("/sys/bus/%s/devices/%s", bus, id);
+}
+
+/* Return sysfs path to scsi drivers directory. */
+char *path_get_sys_bus_drv(const char *bus, const char *drv)
+{
+	if (!drv)
+		return path_get("/sys/bus/%s/drivers", bus);
+
+	return path_get("/sys/bus/%s/drivers/%s", bus, drv);
+}
+
+/* Return sysfs path to zFCP LUN directory. */
+char *path_get_zfcp_lun_dev(const struct zfcp_lun_devid *id)
+{
+	return path_get("%s/drivers/%s/%x.%x.%04x/0x%016" PRIx64
+			"/0x%016" PRIx64, PATH_CCW_BUS,
+			ZFCP_CCWDRV_NAME, id->fcp_dev.cssid,
+			id->fcp_dev.ssid, id->fcp_dev.devno, id->wwpn, id->lun);
+}
+
+/* Return sysfs path to zFCP target port directory. */
+char *path_get_zfcp_port_dev(const struct zfcp_lun_devid *id)
+{
+	return path_get("%s/drivers/%s/%x.%x.%04x/0x%016" PRIx64,
+			PATH_CCW_BUS, ZFCP_CCWDRV_NAME,
+			id->fcp_dev.cssid, id->fcp_dev.ssid,
+			id->fcp_dev.devno, id->wwpn);
+}
+
+/* Return sysfs path to SCSI device directory. */
+char *path_get_scsi_hctl_dev(const char *hctl)
+{
+	return path_get("/sys/bus/scsi/devices/%s", hctl);
+}
diff --git a/s390-dev.c b/s390-dev.c
new file mode 100644
index 0000000..56d1c5f
--- /dev/null
+++ b/s390-dev.c
@@ -0,0 +1,215 @@
+/*
+ * Helper functions to manage s390 devices
+ *
+ * Copyright IBM Corp. 2015
+ * Author(s):	Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
+ *		Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include "s390-dev.h"
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+/**
+ * ccw_parse_devid - Parse a string into a ccw_devid
+ *
+ * @devid_ptr: Target ccw_devid or NULL
+ * @id: String to parse
+ *
+ * Return %RESULT_OK on success, a corresponding result code otherwise.
+ */
+result_t ccw_parse_devid(struct ccw_devid *devid_ptr, const char *id)
+{
+	unsigned int cssid, ssid, devno;
+	char d;
+	result_t result = RESULT_INVALID_ID;
+
+	if (sscanf(id, "%8x %c", &devno, &d) == 1) {
+		if (strcasecmp(id, "0x") == 0) {
+			result = RESULT_INVALID_HEXNUM;
+			goto out;
+		}
+		cssid = 0;
+		ssid = 0;
+	} else if (sscanf(id, "%8x.%8x.%8x %c", &cssid, &ssid, &devno, &d) != 3) {
+		result = RESULT_INVALID_DEVID_FMT;
+		goto out;
+	}
+
+	if (cssid > CSSID_MAX) {
+		result = RESULT_INVALID_CSSID_RANGE;
+		goto out;
+	}
+	if (ssid > SSID_MAX) {
+		result = RESULT_INVALID_SSID_RANGE;
+		goto out;
+	}
+	if (devno > DEVNO_MAX) {
+		result = RESULT_INVALID_DEVNO_RANGE;
+		goto out;
+	}
+
+	result = RESULT_OK;
+	if (devid_ptr) {
+		devid_ptr->cssid = cssid;
+		devid_ptr->ssid = ssid;
+		devid_ptr->devno = devno;
+	}
+out:
+	return result;
+}
+
+result_t ccw_devid_to_str(char **id, const struct ccw_devid *devid)
+{
+	int rc;
+
+	rc = asprintf(id, "%x.%x.%04x", devid->cssid, devid->ssid, devid->devno);
+
+	return rc == -1 ? RESULT_OUT_OF_MEMORY : RESULT_OK;
+}
+
+/*
+ * Parse zfcp luns
+ *
+ * Format: <wwpn>:<lun>
+ * wwpn: Target port World Wide Port Name: aaaaaaaaaaaaaaaa or
+ *	 0xaaaaaaaaaaaaaaaa
+ * lun: FCP LUN: aaaaaaaaaaaaaaaa or 0xaaaaaaaaaaaaaaaa
+ *
+ * @lun_ptr:  Target zfcp_lun or NULL
+ * @id:	      String to parse
+ *
+ * Return %RESULT_OK on success, a corresponding result code otherwise.
+ */
+result_t zfcp_lun_parse_devid(struct zfcp_lun_devid *devid_ptr,
+			      const char *id)
+{
+	result_t result = EXIT_INVALID_ID;
+	char *copy, *curr, *delim;
+	struct ccw_devid fcp_dev;
+	uint64_t wwpn, lun;
+	char dummy;
+
+	copy = strdup(id);
+	if (copy == NULL)
+		return RESULT_OUT_OF_MEMORY;
+	curr = copy;
+
+	/* Parse FCP device ID. */
+	delim = strchr(curr, ':');
+	if (!delim) {
+		result = RESULT_INVALID_LUN_FMT;
+		goto out;
+	}
+	*delim = 0;
+
+	if (ccw_parse_devid(&fcp_dev, copy) != RESULT_OK)
+		goto out;
+
+	curr = delim + 1;
+
+	/* Parse WWPN. */
+	delim = strchr(curr, ':');
+	if (!delim) {
+		result = RESULT_INVALID_LUN_FMT;
+		goto out;
+	}
+	*delim = 0;
+	if (strncasecmp(curr, "0x", 2) == 0)
+		curr += 2;
+	if (strlen(curr) != 16) {
+		result = RESULT_INVALID_WWPN_LEN;
+		goto out;
+	}
+	if (sscanf(curr, "%16" SCNx64 " %c", &wwpn, &dummy) != 1) {
+		result = RESULT_INVALID_WWPN;
+		goto out;
+	}
+	curr = delim + 1;
+
+	/* Parse LUN. */
+	if (strncasecmp(curr, "0x", 2) == 0)
+		curr += 2;
+	if (strlen(curr) != 16) {
+		result = RESULT_INVALID_LUN_LEN;
+		goto out;
+	}
+	if (sscanf(curr, "%16" SCNx64 " %c", &lun, &dummy) != 1) {
+		result = RESULT_INVALID_LUN;
+		goto out;
+	}
+
+	if (devid_ptr) {
+		devid_ptr->fcp_dev = fcp_dev;
+		devid_ptr->wwpn = wwpn;
+		devid_ptr->lun = lun;
+	}
+	result = RESULT_OK;
+
+out:
+	free(copy);
+
+	return result;
+}
+
+result_t zfcp_lun_to_str(char **id, const struct zfcp_lun_devid *lun)
+{
+	if (asprintf(id, "0x%016" PRIx64 ":0x%016" PRIx64, lun->wwpn, lun->lun) == -1)
+		return RESULT_OUT_OF_MEMORY;
+	return RESULT_OK;
+}
+
+/* Return a newly allocated string representing the specified device ID. */
+result_t zfcp_lun_devid_to_str(char **id, const struct zfcp_lun_devid *devid)
+{
+	int rc;
+
+	rc = asprintf(id, "%x.%x.%04x:0x%016" PRIx64 ":0x%016" PRIx64,
+		      devid->fcp_dev.cssid, devid->fcp_dev.ssid,
+		      devid->fcp_dev.devno, devid->wwpn, devid->lun);
+	return rc == -1 ? RESULT_OUT_OF_MEMORY : RESULT_OK;
+}
+
+
+int scsi_hctl_parse_devid(struct scsi_hctl_devid *id, const char *str)
+{
+	unsigned int host, channel, target;
+	uint64_t lun;
+	char dummy;
+
+	if (sscanf(str, "%u:%u:%u:%" SCNu64 " %c", &host, &channel, &target,
+		   &lun, &dummy) != 4)
+		return 1;
+
+	if (id) {
+		id->host = host;
+		id->channel = channel;
+		id->target = target;
+		id->lun = lun;
+	}
+
+	return 0;
+}
+
+/* Swap @len bytes in @addr according to @pos. */
+void byte_swap(uint8_t *addr, unsigned int *pos, unsigned num)
+{
+	unsigned int i;
+	uint8_t s;
+
+	for (i = 0; i < num; i++) {
+		s = addr[i];
+		addr[i] = addr[pos[i]];
+		addr[pos[i]] = s;
+	}
+}
+
+static unsigned int lun_swap[] = { 6, 7, 4, 5 };
+
+uint64_t scsi_lun_to_fcp_lun(uint64_t lun)
+{
+	byte_swap((uint8_t *) &lun, lun_swap, ARRAY_SIZE(lun_swap));
+
+	return lun;
+}
diff --git a/s390-dev.h b/s390-dev.h
new file mode 100644
index 0000000..fa001ee
--- /dev/null
+++ b/s390-dev.h
@@ -0,0 +1,192 @@
+/*
+ * Interface for s390 device helper functions.
+ *
+ * Copyright IBM Corp. 2015
+ * Author(s):	Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
+ *		Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ */
+#ifndef _S390_DEV_H
+#define _S390_DEV_H
+
+#include <inttypes.h>
+#include <string.h>
+
+#define PATH_CCW_BUS		"/sys/bus/ccw"
+#define PATH_CCWGROUP_BUS	"/sys/bus/ccwgroup"
+#define	PATH_PROC		"/proc"
+
+#define	ZFCP_MOD_NAME		"zfcp"
+#define ZFCP_CCWDRV_NAME	"zfcp"
+#define ZFCP_LUN_NAME		"zfcp-lun"
+#define SCSI_ATTR_PREFIX	"scsi_dev"
+
+#define CSSID_MAX	0x00ff
+#define SSID_MAX	0x000f
+#define DEVNO_MAX	0xffff
+
+/**
+ * ccw_devid - CCW device ID
+ *
+ * @cssid: Channel Subsystem ID
+ * @ssid:  Subchannel Set ID
+ * @devno: Device number
+ */
+struct ccw_devid {
+	unsigned int cssid:8;
+	unsigned int ssid:8;
+	unsigned int devno:16;
+} __attribute__((packed));
+
+struct zfcp_lun_devid {
+	struct ccw_devid fcp_dev;
+	uint64_t wwpn;
+	uint64_t lun;
+} __attribute__ ((packed));
+
+struct scsi_hctl_devid {
+	unsigned int host;
+	unsigned int channel;
+	unsigned int target;
+	uint64_t lun;
+};
+
+/* Program exit codes. */
+typedef enum {
+	EXIT_OK			= 0,  /* Program finished successfully */
+
+	/* Usage related */
+	EXIT_INVALID_ID		= 10, /* Invalid device ID specified */
+	EXIT_INCOMPLETE_ID	= 11, /* Incomplete device ID specified */
+
+	/* Run-time related */
+	EXIT_RUNTIME_ERROR	= 15, /* A run-time error occurred */
+	EXIT_OUT_OF_MEMORY	= 22, /* Not enough available memory */
+
+	/* zfcp-related */
+	EXIT_ZFCP_FCP_NOT_FOUND  = 23, /* FCP device not found */
+	EXIT_ZFCP_WWPN_NOT_FOUND = 25, /* WWPN not found */
+	EXIT_ZFCP_INVALID_LUN	 = 26, /* Invalid LUN specified */
+	EXIT_ZFCP_SCSI_NOT_FOUND = 27, /* SCSI device not found */
+
+} exit_code_t;
+
+
+/*
+ * Function result code.
+ *
+ * A result code consists of a reason code and a desired program
+ * exit code.
+ */
+#define DEFINE_RESULT(exit_code, reason)	(((exit_code) << 16) | reason)
+typedef enum {
+	RESULT_OK	  = DEFINE_RESULT(EXIT_OK, 0),
+
+	/* Usage related */
+	RESULT_INVALID_ID     = DEFINE_RESULT(EXIT_INVALID_ID, 0),
+	RESULT_INVALID_HEXNUM = DEFINE_RESULT(EXIT_INVALID_ID, 2),
+
+	/* CCW device ID related */
+	RESULT_INVALID_DEVID_FMT   = DEFINE_RESULT(EXIT_INVALID_ID, 20),
+	RESULT_INVALID_CSSID_RANGE = DEFINE_RESULT(EXIT_INVALID_ID, 21),
+	RESULT_INVALID_SSID_RANGE  = DEFINE_RESULT(EXIT_INVALID_ID, 22),
+	RESULT_INVALID_DEVNO_RANGE = DEFINE_RESULT(EXIT_INVALID_ID, 23),
+
+	/* FCP related */
+	RESULT_INVALID_LUN_FMT	   = DEFINE_RESULT(EXIT_INVALID_ID, 24),
+	RESULT_INVALID_WWPN_LEN	   = DEFINE_RESULT(EXIT_INVALID_ID, 25),
+	RESULT_INVALID_WWPN	   = DEFINE_RESULT(EXIT_INVALID_ID, 26),
+	RESULT_INVALID_LUN_LEN	   = DEFINE_RESULT(EXIT_INVALID_ID, 27),
+	RESULT_INVALID_LUN	   = DEFINE_RESULT(EXIT_INVALID_ID, 28),
+	RESULT_ZFCP_FCP_NOT_FOUND  = DEFINE_RESULT(EXIT_ZFCP_FCP_NOT_FOUND, 29),
+	RESULT_ZFCP_WWPN_NOT_FOUND = DEFINE_RESULT(EXIT_ZFCP_WWPN_NOT_FOUND, 30),
+	RESULT_ZFCP_INVALID_LUN	   = DEFINE_RESULT(EXIT_ZFCP_INVALID_LUN, 31),
+	RESULT_ZFCP_SCSI_NOT_FOUND = DEFINE_RESULT(EXIT_ZFCP_SCSI_NOT_FOUND, 32),
+
+	/* Run-time related */
+	RESULT_RUNTIME_ERROR	   = DEFINE_RESULT(EXIT_RUNTIME_ERROR, 0),
+	RESULT_OUT_OF_MEMORY	   = DEFINE_RESULT(EXIT_OUT_OF_MEMORY, 0),
+
+} result_t;
+
+static inline int reason_code(result_t result)
+{
+	return result & 0xffff;
+}
+
+static inline exit_code_t exit_code(result_t result)
+{
+	return result >> 16;
+}
+
+char *path_get(const char *, ...);
+char *path_get_sys_module(const char *);
+char *path_get_sys_module_param(const char *, const char *);
+char *path_get_sys_class(const char *, const char *);
+char *path_get_ccw_device(const char *, const char *);
+char *path_get_ccw_device_attr(const char *, const char *, const char *);
+char *path_get_ccw_devices(const char *);
+char *path_get_sys_bus_dev(const char *, const char *);
+char *path_get_sys_bus_drv(const char *, const char *);
+char *path_get_zfcp_lun_dev(const struct zfcp_lun_devid *);
+char *path_get_zfcp_port_dev(const struct zfcp_lun_devid *);
+char *path_get_scsi_hctl_dev(const char *);
+
+result_t path_for_each(const char *,
+			  result_t (*callback)(const char *, const char *,
+						  void *), void *);
+/*
+ * Iterate through a directory opened with opendir().
+ *
+ * @dir:    the DIR pointer, returned from opendir()
+ * @de_rp:  pointer to a caller-allocated struct dirent
+ *	    to ensure thread-safety
+ * @dep:    struct dirent pointer used as iterator
+ */
+#define dir_for_each_entry_safe(_dir, _de_rp, _dep, _err)	\
+	 for (_err = readdir_r(_dir, _de_rp, &_dep);		\
+	      !_err && _dep != NULL;				\
+	      _err = readdir_r(_dir, _de_rp, &_dep))
+
+char *misc_read_text_file(const char *path, int chomp);
+result_t misc_write_text_file(const char *path, const char *text);
+char *misc_readlink(const char *path);
+int dir_exists(const char *path);
+char *strtrim(char *s);
+
+result_t ccw_parse_devid(struct ccw_devid *devid_ptr, const char *id);
+result_t ccw_devid_to_str(char **id, const struct ccw_devid *devid);
+result_t zfcp_lun_parse_devid(struct zfcp_lun_devid *devid_ptr, const char *id);
+result_t zfcp_lun_to_str(char **id, const struct zfcp_lun_devid *lun);
+result_t zfcp_lun_devid_to_str(char **id, const struct zfcp_lun_devid *devid);
+int scsi_hctl_parse_devid(struct scsi_hctl_devid *id, const char *str);
+void byte_swap(uint8_t *addr, unsigned int *pos, unsigned num);
+uint64_t scsi_lun_to_fcp_lun(uint64_t lun);
+
+
+static inline int ccw_cmp_devids(const struct ccw_devid *a, const struct ccw_devid *b)
+{
+	return memcmp(a, b, sizeof(*a));
+}
+
+static inline int zfcp_lun_cmp_devids(const struct zfcp_lun_devid *a,
+				      const struct zfcp_lun_devid *b)
+{
+	return memcmp(a, b, sizeof(*a));
+}
+
+/* Helper functions */
+static inline int starts_with(const char *str, const char *s)
+{
+	size_t len;
+
+	len = strlen(s);
+	return !strncmp(str, s, len);
+}
+
+/* /a/b/ -> /b/ */
+static inline char *skip_comp(const char *s)
+{
+	return s != NULL ? strchr(s + 1, '/') : NULL;
+}
+
+#endif /* _S390_DEV_H */
-- 
2.6.2

From 1a8f65828759d8581fc92bd628a7f5e4793f2b82 Mon Sep 17 00:00:00 2001
From: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
Date: Tue, 15 Dec 2015 11:12:50 +0100
Subject: [PATCH 3/5] s390-zfcp: Add FCP device specific functions

Add FCP device helper functions to check for N_Port ID virtualization
and automatic LUN scanning.  Also provide interfaces detect, add, and
remove logical unit numbers (LUNs).

Signed-off-by: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
---
 s390-dev.h  |   9 ++
 s390-zfcp.c | 339 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 348 insertions(+)
 create mode 100644 s390-zfcp.c

diff --git a/s390-dev.h b/s390-dev.h
index fa001ee..6f68367 100644
--- a/s390-dev.h
+++ b/s390-dev.h
@@ -189,4 +189,13 @@ static inline char *skip_comp(const char *s)
 	return s != NULL ? strchr(s + 1, '/') : NULL;
 }
 
+/* zfcp-related helper functions */
+int zfcp_check_allow_lun_scan(int *enabled);
+result_t zfcp_dev_check_npiv(const char *ccwid, int *enabled);
+result_t zfcp_lun_add(const struct zfcp_lun_devid *lun);
+result_t scsi_device_remove(const char *hctl);
+result_t zfcp_lun_remove(const struct zfcp_lun_devid *lun);
+int zfcp_for_each_scsi_dev(int (*cb)(const char *, const char *, const char *,
+			   const char*, void *data), void *data);
+
 #endif /* _S390_DEV_H */
diff --git a/s390-zfcp.c b/s390-zfcp.c
new file mode 100644
index 0000000..a10b8d8
--- /dev/null
+++ b/s390-zfcp.c
@@ -0,0 +1,339 @@
+/*
+ * zfcp helper functions for the s390-zfcp Debian-Installation
+ * configuration module.
+ *
+ *
+ * Copyright IBM Corp. 2015
+ * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/types.h>
+
+#include <debian-installer.h>
+
+#include "s390-dev.h"
+
+int zfcp_check_allow_lun_scan(int *enabled)
+{
+	int rc;
+	char *path, *value;
+
+	rc = -1;
+	path = path_get_sys_module_param(ZFCP_MOD_NAME, "allow_lun_scan");
+	if (path == NULL)
+		goto out;
+
+	value = misc_read_text_file(path, 1);
+	if (value == NULL)
+		goto out_free_path;
+
+	rc = 0;
+	*enabled = !strncmp(value, "Y", 1);
+
+	free(value);
+out_free_path:
+	free(path);
+out:
+	return rc;
+}
+
+static char *get_port_type_path(const char *id)
+{
+	int err;
+	DIR *dir;
+	char *devpath, *path;
+	struct dirent de, *dep;
+
+	devpath = path_get_ccw_device(ZFCP_CCWDRV_NAME, id);
+	dir = opendir(devpath);
+	if (dir == NULL)
+		goto out;
+
+	path = NULL;
+	dir_for_each_entry_safe(dir, &de, dep, err) {
+		if (!starts_with(de.d_name, "host"))
+			continue;
+		asprintf(&path, "%s/%s/fc_host/%s/port_type",
+			 devpath, de.d_name, de.d_name);
+		break;
+	}
+
+	closedir(dir);
+out:
+	free(devpath);
+
+	return path;
+}
+
+result_t zfcp_dev_check_npiv(const char *ccwid, int *enabled)
+{
+	result_t rc;
+	char *path, *type;
+
+	rc = RESULT_RUNTIME_ERROR;
+	path = get_port_type_path(ccwid);
+	if (path == NULL)
+		goto out;
+
+	type = misc_read_text_file(path, 1);
+	if (type == NULL)
+		goto out;
+
+	/* Check FCP port type. */
+	*enabled = !strcmp(type, "NPIV VPORT");
+
+	rc = RESULT_OK;
+	free(type);
+out:
+	free(path);
+
+	return rc;
+}
+
+result_t zfcp_lun_op(const struct zfcp_lun_devid *devid, const char *op)
+{
+	int rc;
+	result_t result;
+	char *portpath, *path = NULL, *lun = NULL;
+
+	result = RESULT_OUT_OF_MEMORY;
+
+	portpath = path_get_zfcp_port_dev(devid);
+	if (portpath == NULL)
+		goto out;
+
+	if (!dir_exists(portpath)) {
+		result = RESULT_ZFCP_WWPN_NOT_FOUND;
+		goto out;
+	}
+
+	rc = asprintf(&path, "%s/%s", portpath, op);
+	if (rc == -1)
+		goto out;
+	di_debug("ZFCP_LUN_OP: path=%s\n", path);
+
+	rc = asprintf(&lun, "0x%016" PRIx64, devid->lun);
+	if (rc == -1)
+		goto out;
+
+	if (misc_write_text_file(path, lun) != RESULT_OK) {
+		result = RESULT_ZFCP_INVALID_LUN;
+		goto out;
+	}
+	di_debug("ZFCP_LUN_OP: modified LUN: %s\n", lun);
+
+	result = RESULT_OK;
+out:
+	free(lun);
+	free(path);
+	free(portpath);
+
+	return result;
+}
+
+result_t zfcp_lun_add(const struct zfcp_lun_devid *devid)
+{
+	result_t rc;
+	char *lunpath;
+
+	lunpath = path_get_zfcp_lun_dev(devid);
+	if (lunpath == NULL)
+		return RESULT_OUT_OF_MEMORY;
+
+	if (dir_exists(lunpath)) {
+		rc = RESULT_OK;
+		goto out;
+	}
+
+	rc = zfcp_lun_op(devid, "unit_add");
+out:
+	free(lunpath);
+	return rc;
+}
+
+result_t scsi_device_remove(const char *hctl)
+{
+	int rc;
+	result_t result;
+	char *path, *delete = NULL;
+
+	path = path_get_sys_bus_dev("scsi", hctl);
+	if (path == NULL)
+		return RESULT_OUT_OF_MEMORY;
+
+	if (!dir_exists(path)) {
+		result = RESULT_ZFCP_SCSI_NOT_FOUND;
+		goto out;
+	}
+
+	rc = asprintf(&delete, "%s/delete", path);
+	if (rc == -1) {
+		result = RESULT_OUT_OF_MEMORY;
+		goto out;
+	}
+
+	result = misc_write_text_file(delete, "\n");
+out:
+	free(delete);
+	free(path);
+
+	return result;
+}
+
+result_t zfcp_lun_remove(const struct zfcp_lun_devid *devid)
+{
+	result_t rc;
+	char *lunpath;
+
+	lunpath = path_get_zfcp_lun_dev(devid);
+	if (lunpath == NULL)
+		return RESULT_OUT_OF_MEMORY;
+
+	if (!dir_exists(lunpath)) {
+		rc = RESULT_ZFCP_INVALID_LUN;
+		goto out;
+	}
+
+	rc = zfcp_lun_op(devid, "unit_remove");
+out:
+	free(lunpath);
+	return rc;
+}
+
+/* Retrieve CCW device ID of HBA for specified SCSI device path. */
+static char *devpath_to_hba_id(const char *path)
+{
+	char *copy, *start, *end, *hba_id = NULL;
+
+	copy = strdup(path);
+	if (copy == NULL)
+		return NULL;
+
+	/* copy=/devices/css0/0.0.001c/0.0.1940/host0/... */
+	end = strstr(copy, "/host");
+	if (end == NULL)
+		goto out;
+	*end = 0;
+	/* copy=/devices/css0/0.0.001c/0.0.1940 */
+	start = strrchr(copy, '/');
+	if (start == NULL)
+		goto out;
+	start++;
+	hba_id = strdup(start);
+out:
+	free(copy);
+
+	return hba_id;
+}
+
+/* Retrieve WWPN from specified SCSI device path. */
+static char *devpath_to_wwpn(const char *devpath)
+{
+	char *copy, *rport, *end, *path = NULL, *wwpn = NULL;
+
+	/* devpath=/devices/css0/0.0.001c/0.0.1940/host0/rport-0:0-16/
+	 *          target0:0:16/0:0:16:1085030433/ */
+	copy = strdup(devpath);
+	rport = strstr(copy, "/rport-");
+	end = skip_comp(rport);
+	if (end == NULL)
+		goto out;
+	*end = 0;
+
+	/* devpath=/devices/css0/0.0.001c/0.0.1940/host0/rport-0:0-16
+	 * rport=rport-0:0-16 */
+	path = path_get("/sys%s/fc_remote_ports%s/port_name", copy, rport);
+	if (path != NULL)
+		wwpn = misc_read_text_file(path, 1);
+out:
+	free(path);
+	free(copy);
+
+	return wwpn;
+}
+
+static char *lun_to_str(uint64_t lun)
+{
+	int rc;
+	char *fcplun = NULL;
+
+	rc = asprintf(&fcplun, "0x%16" PRIx64, lun);
+	if (rc == -1)
+		return NULL;
+	return fcplun;
+}
+
+static char *scsi_htcl_to_zfcp_lun_devpath(const char *hctl)
+{
+	char *buspath, *link = NULL;
+
+	buspath = path_get_sys_bus_dev("scsi", hctl);
+	if (buspath == NULL)
+		return NULL;
+	link = misc_readlink(buspath);
+	free(buspath);
+
+	return link;
+}
+
+int zfcp_for_each_scsi_dev(int (*cb)(const char *, const char *, const char *,
+				     const char*, void *data), void *data)
+{
+	int rc, err;
+	DIR *devices;
+	struct dirent scsi, *dep;
+	struct scsi_hctl_devid hctl;
+	char *path, *devpath, *hba_id, *wwpn, *lun;
+
+	path = path_get_sys_bus_dev("scsi", NULL);
+	if (path == NULL)
+		return 0;
+	rc = 0;
+	devices = opendir(path);
+	if (devices == NULL) {
+		di_info("Could not open directory: %s: %s\n", path,
+			strerror(errno));
+		free(path);
+		/* There are likely no SCSI devices */
+		return 0;
+	}
+
+	/*
+	 * Scan all devices on the SCSI bus to spot SCSI devices managed by
+	 * the zfcp driver.
+	 */
+	dir_for_each_entry_safe(devices, &scsi, dep, err) {
+		if (starts_with(scsi.d_name, "host") ||
+		    starts_with(scsi.d_name, "target"))
+			continue;
+		if (scsi_hctl_parse_devid(&hctl, scsi.d_name))
+			continue;
+
+		devpath = scsi_htcl_to_zfcp_lun_devpath(scsi.d_name);
+		if (devpath == NULL)
+			continue;
+		path = strstr(devpath, "/devices/");
+		if (path == NULL) {
+			free(devpath);
+			continue;
+		}
+		hba_id = devpath_to_hba_id(path);
+		wwpn = devpath_to_wwpn(path);
+		lun = lun_to_str(scsi_lun_to_fcp_lun(hctl.lun));
+		if (hba_id != NULL && wwpn != NULL && lun != NULL)
+			rc = cb(scsi.d_name, hba_id, wwpn, lun, data);
+		free(lun);
+		free(wwpn);
+		free(hba_id);
+		free(devpath);
+		if (rc)
+			break;
+	}
+	closedir(devices);
+
+	return rc;
+}
-- 
2.6.2

From e7bcefc51212d2072f74db5e5bd5d7023d6871ab Mon Sep 17 00:00:00 2001
From: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
Date: Tue, 15 Dec 2015 11:15:38 +0100
Subject: [PATCH 4/5] zfcp-config: Add utility to configure FCP devices on s390

This is the core Debian Installer utility to configure FCP devices
and prepare to install Debian on FC-attched SCSI devices.

The zfcp-config utility provides an interactive user interface,
as well as, a preseeding interface for unattended FCP configuration
and installation.

Signed-off-by: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
---
 Makefile      |   35 ++
 zfcp-config.c | 1188 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1223 insertions(+)
 create mode 100644 Makefile
 create mode 100644 zfcp-config.c

diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..727cf86
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,35 @@
+#
+# Makefile to build the s390 FCP device configuration panels for the
+# Debian Installer
+#
+
+CC := gcc
+CFLAGS := -Wall -W -pipe -Os -I/usr/include/sysfs -D_GNU_SOURCE
+LDFLAGS := -ldebconfclient -ldebian-installer
+
+DEB_BUILD_OPTIONS=debug
+
+ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS)))
+CFLAGS += -g -ggdb
+STRIP = /bin/true
+else
+CFLAGS += -fomit-frame-pointer
+STRIPTOOL=strip
+STRIP = $(STRIPTOOL) --remove-section=.note --remove-section=.comment
+endif
+
+S390_DEV_LIB = s390-dev.o s390-dev-misc.o s390-dev-path.o
+ZFCP_CONFIG = zfcp-config
+
+all: $(ZFCP_CONFIG)
+
+$(S390_DEV_LIB): s390-dev.h
+
+zfcp-config: zfcp-config.o $(S390_DEV_LIB) s390-zfcp.o
+	$(CC) -o $@ $^ $(LDFLAGS)
+	$(STRIP) $@
+
+clean:
+	rm -f $(ZFCP_CONFIG) *.o
+
+.PHONY: clean
diff --git a/zfcp-config.c b/zfcp-config.c
new file mode 100644
index 0000000..bed4a32
--- /dev/null
+++ b/zfcp-config.c
@@ -0,0 +1,1188 @@
+/*
+ * Debian Installer Utility to configure FC-attched SCSI devices
+ * for Linux on System z and Linux on z Systems (s390).
+ *
+ *
+ * Copyright IBM Corp. 2015
+ * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ */
+#include <errno.h>
+#include <dirent.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <cdebconf/debconfclient.h>
+#include <debian-installer.h>
+
+#include "s390-dev.h"
+
+#ifndef __unused
+#	define  __unused __attribute__ ((unused))
+#endif
+
+/* Directory where the zfcp configuration files are written */
+#define SYSCONFIG_DIR "/etc/sysconfig/hardware/"
+#define CONFIG_PREFIX SYSCONFIG_DIR "config-ccw-"
+
+/* External commands */
+#define UDEVADM "udevadm"
+
+/* Debconf related definitions */
+#define TEMPLATE_PREFIX	"s390-zfcp/"
+#define DEBCONF_CAPS	"backup"
+#define PRESEED_DELIM	","
+#define SCSI_ASYNC_TRIES	15
+#define SCSI_ASYNC_TIMEOUT	250000	  /* useconds */
+#define MAX_HOST_LIST_ITEMS	100
+#define MAX_HOST_LIST_SIZE	(MAX_HOST_LIST_ITEMS * (9 + 20 +1))
+#define MAX_LUN_LIST_SIZE	2048
+
+/* Definition of a zFCP host adapter */
+struct zfcp_host {
+	struct ccw_devid devid;	      /* CCW device ID */
+	char	name[PATH_MAX];	      /* Device and display name */
+	struct di_tree *luns;	      /* List of WWPN:LUN pairs */
+	int	online;		      /* Adapter is enabled */
+	int	npiv;		      /* Adapter uses N_PORT virtualization */
+	int	write_luns;	      /* Write automatically added LUNs */
+	int	configured;	      /* Configuration file written */
+};
+
+/* Definition of state and state transitions */
+enum state {
+	BACKUP,			/* Backup state */
+	DETECT_ZFCP_HOSTS,	/* Detect available zfcp host adapter */
+	PRESEED,		/* Read and parse preseeding data */
+	SELECT_ZFCP_HOST,	/* Select adapter for configuration */
+	REMOVE_CONFIG,		/* Remove zfcp configuration file */
+	ENABLE,			/* Activate an zfcp host adapter */
+	ADD_ZFCP_LUNS,		/* Attach SCSI disks */
+	WRITE,			/* Write zfcp configuration files */
+	ERROR,			/* Indicate an error condition */
+	FINISH,			/* Completed zfcp configuration */
+};
+
+enum state_wanted
+{
+	WANT_BACKUP,
+	WANT_NEXT,
+	WANT_FINISH,
+	WANT_ERROR
+};
+
+/* Exit codes */
+#define EXIT_BACKUP 10
+
+/* List of zfcp host adapters available for configuration */
+static di_tree *zfcp_hosts;
+
+/* Indicator that the zfcp device driver performs automatic LUN scanning */
+static int zfcp_allow_lun_scan;
+
+/* Indicator that the module runs unattended through preseeding */
+static int preseeded;
+
+/* List of preseeded zfcp lun identifiers */
+static di_tree *preseed_data;
+
+
+static int di_compare_ccw_devids(const void *key1, const void *key2)
+{
+	return ccw_cmp_devids(key1, key2);
+}
+
+static int di_compare_zfcp_lun_devids(const void *key1, const void *key2)
+{
+	return zfcp_lun_cmp_devids(key1, key2);
+}
+
+static void di_free_zfcp_host(void *host)
+{
+	struct zfcp_host *zfcp_host = host;
+
+	if (zfcp_host->luns != NULL)
+		di_tree_destroy(zfcp_host->luns);
+	di_free(zfcp_host);
+}
+
+static int add_zfcp_host(struct di_tree *tree, const char *id)
+{
+	int online;
+	char *path, *value;
+	char config[PATH_MAX];
+	struct zfcp_host *zfcp_host;
+
+	path = path_get_ccw_device_attr(ZFCP_CCWDRV_NAME, id, "online");
+	if (path == NULL)
+		return -1;
+
+	if (access(path, F_OK)) {
+		free(path);
+		/* Ignore this host adapter */
+		return 0;
+	}
+
+	value = misc_read_text_file(path, 1);
+	online = strtol(value, NULL, 10);
+	free(value);
+	free(path);
+
+	zfcp_host = di_malloc0(sizeof(*zfcp_host));
+	if (zfcp_host == NULL)
+		return -1;
+
+	strncpy(zfcp_host->name, id, sizeof(zfcp_host->name));
+	if (ccw_parse_devid(&zfcp_host->devid, zfcp_host->name) != RESULT_OK) {
+		di_free(zfcp_host);
+		return -1;
+	}
+
+	if (online > 0) {
+		zfcp_host->online = 1;
+		zfcp_dev_check_npiv(zfcp_host->name, &zfcp_host->npiv);
+	}
+
+	snprintf(config, sizeof(config), CONFIG_PREFIX "%s", zfcp_host->name);
+	zfcp_host->configured = !access(config, F_OK);
+
+	zfcp_host->luns = NULL;
+	di_tree_insert(tree, &zfcp_host->devid, zfcp_host);
+	di_debug("DETECT: Added FCP device: %s: online=%d npiv=%d\n",
+		 zfcp_host->name, zfcp_host->online, zfcp_host->npiv);
+
+	return 0;
+}
+
+static enum state_wanted detect_zfcp_hosts(struct debconfclient *client)
+{
+	int err;
+	char *path;
+	DIR *devices;
+	enum state_wanted rc;
+	struct dirent dev, *dep;
+
+	/*
+	 * Detect and collect FCP adapters.  FCP adapters might not be
+	 * available, because
+	 *
+	 *  a) FCP adapters are not configured or attached to the Linux
+	 *     instance, or
+	 *  b) the CCW device IDs for the FCP adapters are black-listed
+	 *     through cio_ignore.
+	 *
+	 * CIO_IGNORE:
+	 *
+	 * Handling cio_ignore is currently not supported by this module.
+	 * To support it, users have to provide the CCW device IDs
+	 * manually.  The module has then to renove the device IDs from
+	 * the black-list.  This can be automatically handled by using
+	 * preseed data or by introducing a [WANT_]UNIGNORE_ZFCP_HOSTS
+	 * state to ask the user for FCP devices.
+	 *
+	 * For now, the module reports that there are no FCP devices
+	 * if the zfcp device driver module is not loaded or if it is
+	 * loaded but no FCP devices were found.
+	 */
+	path = path_get_ccw_devices(ZFCP_CCWDRV_NAME);
+	if (path == NULL) {
+		di_error("Could not allocate memory\n");
+		return WANT_ERROR;
+	}
+
+	rc = WANT_ERROR;
+	devices = opendir(path);
+	if (devices == NULL) {
+		if (errno == ENOENT) {
+			di_debug("DETECT: The zfcp device driver is not loaded\n");
+			debconf_input(client, "low", TEMPLATE_PREFIX "no_zfcp_hosts");
+			debconf_capb(client);
+			debconf_go(client);
+			rc = WANT_FINISH;
+		}
+		di_warning("Could not open directory: %s: %s\n", path,
+			   strerror(errno));
+		goto out;
+	}
+
+	dir_for_each_entry_safe(devices, &dev, dep, err) {
+		if (ccw_parse_devid(NULL, dev.d_name) != RESULT_OK)
+			continue;
+		if (add_zfcp_host(zfcp_hosts, dev.d_name))
+			goto out;
+	}
+	closedir(devices);
+
+	zfcp_check_allow_lun_scan(&zfcp_allow_lun_scan);
+	if (zfcp_allow_lun_scan)
+		di_debug("DETECT: Automatic LUN scanning is enabled\n");
+
+	rc = WANT_NEXT;
+	if (!di_tree_size(zfcp_hosts)) {
+		di_debug("DETECT: No FCP devices are available\n");
+		debconf_input(client, "low", TEMPLATE_PREFIX "no_zfcp_hosts");
+		debconf_capb(client);
+		debconf_go(client);
+		rc = WANT_FINISH;
+	}
+out:
+	free(path);
+	return rc;
+}
+
+static result_t preseed_zfcp_lun_id(struct zfcp_lun_devid **new, const char *spec)
+{
+	result_t rc;
+	struct zfcp_lun_devid *devid;
+
+	devid = di_malloc0(sizeof(*devid));
+	if (devid == NULL)
+		return -1;
+
+	if (strchr(spec, ':') == NULL)
+		/* Only a FCP CCW device ID specified */
+		rc = ccw_parse_devid(&devid->fcp_dev, spec);
+	else
+		rc = zfcp_lun_parse_devid(devid, spec);
+
+	if (rc == RESULT_OK)
+		*new = devid;
+	else
+		di_free(devid);
+
+	return rc;
+}
+
+static enum state_wanted preseed_zfcp(struct debconfclient *c)
+{
+	int *seen;
+	enum state_wanted rc;
+	char *ent, *spec = NULL, *token_state;
+	struct zfcp_lun_devid *devid;
+
+	debconf_get(c, TEMPLATE_PREFIX "zfcp");
+	if (c->value == NULL || !strlen(c->value)) {
+		di_debug("PRESEED: No preseed data available\n");
+		return WANT_NEXT;
+	}
+	di_info("PRESEED: '%s'", c->value);
+
+	rc = WANT_ERROR;
+	spec = strdup(c->value);
+	if (spec == NULL) {
+		di_error("Could not allocate memory for preseed data\n");
+		goto out;
+	}
+
+	preseed_data = di_tree_new_full(di_compare_zfcp_lun_devids, di_free, di_free);
+	if (preseed_data == NULL) {
+		di_error("Could not allocate memory for preseed data tree\n");
+		goto out;
+	}
+
+	ent = strtok_r(spec, PRESEED_DELIM, &token_state);
+	while (ent != NULL) {
+		ent = strtrim(ent);
+		di_debug("PRESEED DATA ENTRY: '%s'\n", ent);
+		if (preseed_zfcp_lun_id(&devid, ent) != RESULT_OK)
+			goto out_free_tree;
+		seen = di_malloc0(sizeof(*seen));
+		if (seen == NULL)
+			goto out_free_tree;
+		di_tree_insert(preseed_data, devid, seen);
+		ent = strtok_r(NULL, PRESEED_DELIM, &token_state);
+	}
+
+	rc = WANT_NEXT;
+	preseeded = di_tree_size(preseed_data);
+
+out_free_tree:
+	if (!preseeded)
+		di_tree_destroy(preseed_data);
+out:
+	free(spec);
+
+	return rc;
+}
+
+/*
+ * Request input from debconf for the specified template.  The function
+ * returns the return code of the debconf_go() function and sets the
+ * **result pointer to the result.
+ */
+static cmdstatus_t debconf_result(struct debconfclient *client,
+				  const char *priority, const char *template,
+				  char **result)
+{
+	cmdstatus_t ret;
+
+	debconf_input(client, priority, template);
+	ret = debconf_go(client);
+	debconf_get(client, template);
+	*result = client->value;
+
+	return ret;
+}
+
+static cmdstatus_t debconf_show_error(struct debconfclient *client,
+				      const char *template)
+{
+	cmdstatus_t ret;
+
+	debconf_input(client, "critical", template);
+	debconf_capb(client);
+	ret = debconf_go(client);
+	debconf_capb(client, DEBCONF_CAPS);
+
+	return ret;
+}
+
+static void build_zfcp_host_list(void *key __unused, void *value, void *data)
+{
+	struct zfcp_host *host = value;
+	char *buf = data;
+
+	if (buf[0])
+		strncat(buf, ",", MAX_HOST_LIST_SIZE);
+	strncat(buf, host->name, MAX_HOST_LIST_SIZE);
+
+	if (host->configured)
+		strncat(buf, " (configured)", MAX_HOST_LIST_SIZE);
+	else if (host->online)
+		strncat(buf, " (not configured)", MAX_HOST_LIST_SIZE);
+}
+
+static void preseed_select_one(void *key, void *value, void *data)
+{
+	char *fcpdev;
+	int *seen = value;
+	struct zfcp_host *host;
+	struct zfcp_lun_devid *devid = key;
+	struct zfcp_host **selected = data;
+
+	/* There is already a zfcp host adapter selected */
+	if (*selected != NULL)
+		return;
+
+	/* The current entry has been already processed. */
+	if (*seen)
+		return;
+
+	/* Try to find the next unconfigured zfcp host adapter */
+	host = di_tree_lookup(zfcp_hosts, &devid->fcp_dev);
+	if (host == NULL) {
+		/*
+		 * CIO_IGNORE:
+		 * Remove the FCP device from the blacklist and retry.
+		 */
+		ccw_devid_to_str(&fcpdev, &devid->fcp_dev);
+		di_error("Preseeded FCP device was not detected: %s\n", fcpdev);
+		free(fcpdev);
+		return;
+	}
+
+	if (host->configured)
+		return;
+
+	*selected = host;
+	*seen = 1;
+}
+
+static enum state_wanted select_preseeded(struct zfcp_host **zfcp_host)
+{
+	if (!di_tree_size(preseed_data)) {
+		di_error("No preseeded FCP device data available\n");
+		return WANT_ERROR;
+	}
+
+	/*
+	 * Iterate through the list of preseed data and select each
+	 * zfcp host adapter which is not configured yet.
+	 */
+	*zfcp_host = NULL;
+	di_tree_foreach(preseed_data, preseed_select_one, zfcp_host);
+	if (*zfcp_host == NULL)
+		return WANT_FINISH;
+
+	return WANT_NEXT;
+}
+
+static enum state_wanted select_zfcp_host(struct debconfclient *client,
+					  struct zfcp_host **zfcp_host)
+{
+	cmdstatus_t rc;
+	char hostlist[MAX_HOST_LIST_SIZE], *val, *delim;
+	struct ccw_devid devid;
+
+	memset(&hostlist, 0, sizeof(hostlist));
+	di_tree_foreach(zfcp_hosts, build_zfcp_host_list, hostlist);
+
+	debconf_subst(client, TEMPLATE_PREFIX "select_zfcp_host", "choices", hostlist);
+	rc = debconf_result(client, "critical", TEMPLATE_PREFIX "select_zfcp_host", &val);
+	if (rc == CMD_GOBACK)
+		return WANT_BACKUP;
+	if (!strcmp("Finish", val))
+		return WANT_FINISH;
+
+	/* Remove the trailing "configured" information */
+	delim = strchr(val, ' ');
+	if (delim != NULL)
+		*delim = '\0';
+
+	ccw_parse_devid(&devid, val);
+	*zfcp_host = di_tree_lookup(zfcp_hosts, &devid);
+	if (*zfcp_host == NULL) {
+		di_debug("Could not find FCP device for devid: %s\n", val);
+		return WANT_ERROR;
+	}
+	di_debug("SELECT: Using FCP device %s\n", val);
+
+	return WANT_NEXT;
+}
+
+static enum state_wanted remove_config_file(struct debconfclient *client,
+					    struct zfcp_host *zfcp_host)
+{
+	char *val;
+	cmdstatus_t rc;
+	char path[PATH_MAX];
+
+	/* Ignore FCP devices that are not configured */
+	if (!zfcp_host->configured)
+		return WANT_NEXT;
+
+	/* Reset the question to its default value */
+	debconf_reset(client, TEMPLATE_PREFIX "remove_zfcp_config");
+
+	/* Confirm removal of the FCP device configuration file */
+	rc = debconf_result(client, "critical",
+			    TEMPLATE_PREFIX "remove_zfcp_config", &val);
+	if (rc == CMD_GOBACK)
+		return WANT_BACKUP;
+
+	if (!strcmp(val, "false"))
+		return WANT_NEXT;
+
+	/* Remove FCP device configuration file */
+	snprintf(path, sizeof(path), CONFIG_PREFIX "%s", zfcp_host->name);
+	if (unlink(path)) {
+		di_warning("Could not remove %s: %s\n", path, strerror(errno));
+		return WANT_ERROR;
+	}
+	zfcp_host->configured = 0;
+
+	return WANT_BACKUP;
+}
+
+static void udev_settle(void)
+{
+	if (di_exec_shell_log(UDEVADM " settle") == -1)
+		di_warning("Could not run " UDEVADM " settle: %s", strerror(errno));
+}
+
+/* XXX
+ *
+ * Remove this function if the zfcp race condition is corrected.  For now,
+ * delay the installation for some time to wait until the SCSI devices
+ * becomes populated.
+ */
+static void debconf_udev_progress(struct debconfclient *client, useconds_t usec,
+			     unsigned tries)
+{
+	unsigned count;
+
+	debconf_capb(client);
+	debconf_progress_start(client, 0, tries, TEMPLATE_PREFIX "udev_progress");
+
+	for (count = 0; count < tries; count++) {
+		usleep(usec);
+		debconf_progress_step(client, 1);
+	}
+	udev_settle();
+
+	debconf_progress_stop(client);
+	debconf_capb(client, DEBCONF_CAPS);
+}
+
+static enum state_wanted enable_zfcp_host(struct zfcp_host *zfcp_host)
+{
+	char *attr;
+	enum state_wanted rc;
+
+	if (zfcp_host->online)
+		return WANT_NEXT;
+
+	rc = WANT_ERROR;
+	attr = path_get_ccw_device_attr(NULL, zfcp_host->name, "online");
+	if (attr == NULL)
+		goto out;
+
+	if (misc_write_text_file(attr, "1") != RESULT_OK)
+		goto out_free;
+	zfcp_host->online = 1;
+
+	if (zfcp_dev_check_npiv(zfcp_host->name, &zfcp_host->npiv))
+		goto out;
+
+	di_debug("ENABLE: Activated FCP device %s (npiv=%d)\n",
+		 zfcp_host->name, zfcp_host->npiv);
+
+	rc = WANT_NEXT;
+out_free:
+	free(attr);
+out:
+	return rc;
+}
+
+struct lun_tree {
+	struct zfcp_host *hba;
+	struct di_tree *luns;
+};
+
+static int lun_tree_insert(const char *hctl, const char *hba_id,
+			   const char *wwpn, const char *lun, void *data)
+{
+	struct zfcp_lun_devid *zfcplun;
+	uint64_t wwpn_num, lun_num;
+	struct lun_tree *lt = data;
+	char *scsidev;
+
+	/*
+	 * Compare the host bus adapter IDs by using their stringified
+	 * device name.  An improved version could use the parsed CCW
+	 * bus ID instead.  Note that this filters the LUNs for a
+	 * particular zfcp host adapter.
+	 */
+	if (strcmp(lt->hba->name, hba_id))
+		return 0;
+
+	/*
+	 * Scan the WWPN and LUN value.  Note that both starts with 0x
+	 * resulting in the offset by 2.  Ignore this LUN if it cannot
+	 * be parsed successfully.
+	 */
+	if (sscanf(wwpn + 2, "%16" SCNx64, &wwpn_num) != 1)
+		return 0;
+	if (sscanf(lun + 2, "%16" SCNx64, &lun_num) != 1)
+		return 0;
+
+	zfcplun = di_malloc0(sizeof(*zfcplun));
+	if (zfcplun == NULL) {
+		di_error("Could not allocate memory\n");
+		return 1;
+	}
+	zfcplun->fcp_dev = lt->hba->devid;
+	zfcplun->wwpn = wwpn_num;
+	zfcplun->lun = lun_num;
+	scsidev = strdup(hctl);
+	if (scsidev == NULL) {
+		di_free(zfcplun);
+		di_error("Could not allocate memory\n");
+		return 1;
+	}
+
+	if (di_tree_lookup(lt->luns, zfcplun) != NULL) {
+		di_free(zfcplun);
+		free(scsidev);
+		return 0;
+	}
+	di_tree_insert(lt->luns, zfcplun, scsidev);
+	di_debug("POPULATE LUN TREE: %s:%s:%s [%s]\n", hba_id, wwpn, lun, hctl);
+
+	return 0;
+}
+
+static di_tree *lun_tree(struct lun_tree *lt)
+{
+	return lt->luns;
+}
+
+static void lun_tree_destroy(struct lun_tree *lt)
+{
+	if (lt->luns != NULL)
+		di_tree_destroy(lt->luns);
+	lt->luns = NULL;
+}
+
+static int lun_tree_populate(struct lun_tree *lt)
+{
+	lun_tree_destroy(lt);
+
+	lt->luns = di_tree_new_full(di_compare_zfcp_lun_devids, di_free, free);
+	if (lt->luns == NULL) {
+		di_error("Could not allocate memory for the LUN tree\n");
+		return -1;
+	}
+	udev_settle();
+
+	return zfcp_for_each_scsi_dev(lun_tree_insert, lt);
+}
+
+/* Returns a zfcp LUN ID consisting of FCP device, WWPN, and LUN part. */
+static result_t zfcp_parse_wwpn_lun(struct zfcp_lun_devid *devid,
+				    struct zfcp_host *hba, const char *lun_str)
+{
+	char *devid_str;
+	result_t result;
+
+	if (asprintf(&devid_str, "%s:%s", hba->name, lun_str) == -1)
+		return RESULT_OUT_OF_MEMORY;
+	result = zfcp_lun_parse_devid(devid, devid_str);
+	free(devid_str);
+
+	return result;
+}
+
+static result_t __add_lun(struct zfcp_lun_devid *lun, struct lun_tree *lt)
+{
+	int i;
+	result_t result;
+
+	/* Check if the specified LUN is already in the list */
+	if (di_tree_lookup(lun_tree(lt), lun) != NULL)
+		return RESULT_OK;
+
+	/* Enable the LUN */
+	result = zfcp_lun_add(lun);
+	if (result != RESULT_OK)
+		goto out;
+
+	/*
+	 * Repopulate the LUN tree and confirm that a SCSI device was created
+	 * for the specified LUN.  The updated LUN tree contains a LUN node
+	 * for it.
+	 *
+	 * If scsi_mod.scan is "async" is set, try several times until the
+	 * SCSI asynchronous processing completes.  To prevent any issues
+	 * here, set scsi_mod.scan=sync on the kernel command line.
+	 */
+	for (i = 0; i < SCSI_ASYNC_TRIES; i++) {
+		lun_tree_populate(lt);
+		if (di_tree_lookup(lun_tree(lt), lun) != NULL)
+			goto out;
+		usleep(SCSI_ASYNC_TIMEOUT);
+	}
+
+	/* Number of tries exceeded, remove LUN and fail */
+	result = RESULT_ZFCP_SCSI_NOT_FOUND;
+	zfcp_lun_remove(lun);
+	di_info("Number of retries to find a SCSI device exceeded\n");
+	di_info("Consider specifying the kernel parameter: scsi_mod.scan=sync\n");
+out:
+	return result;
+}
+
+static int add_lun(struct debconfclient *client, struct zfcp_host *zfcp_host,
+		   struct lun_tree *lt)
+{
+	struct zfcp_lun_devid *lun;
+	cmdstatus_t rc;
+	result_t result;
+	char *value;
+
+	rc = debconf_result(client, "critical", TEMPLATE_PREFIX "get_lun", &value);
+	if (rc == CMD_GOBACK)
+		return 0;
+
+	lun = di_malloc0(sizeof(*lun));
+	if (lun == NULL)
+		return -1;
+	result = zfcp_parse_wwpn_lun(lun, zfcp_host, value);
+	if (result != RESULT_OK) {
+		di_free(lun);
+		switch (result) {
+		case RESULT_INVALID_LUN_FMT:
+			debconf_show_error(client, TEMPLATE_PREFIX "invalid_lun_fmt");
+			goto out;
+		case RESULT_INVALID_WWPN_LEN:
+		case RESULT_INVALID_WWPN:
+		case RESULT_INVALID_LUN_LEN:
+		case RESULT_INVALID_LUN:
+			debconf_show_error(client, TEMPLATE_PREFIX "invalid_wwpn_or_lun");
+			goto out;
+		default:
+			return -1;
+		}
+	}
+
+	/* Enable the LUN */
+	result = __add_lun(lun, lt);
+	switch (result) {
+	case RESULT_OK:
+		di_debug("ADD_LUN: %s\n", value);
+		break;
+	case RESULT_ZFCP_WWPN_NOT_FOUND:
+		debconf_show_error(client, TEMPLATE_PREFIX "port_not_found");
+		goto out;
+	case RESULT_ZFCP_INVALID_LUN:
+		debconf_show_error(client, TEMPLATE_PREFIX "lun_not_added");
+		goto out;
+	case RESULT_ZFCP_SCSI_NOT_FOUND:
+		debconf_show_error(client, TEMPLATE_PREFIX "lun_not_added");
+		goto out;
+	default:
+		di_free(lun);
+		return -1;
+	}
+
+	debconf_reset(client, TEMPLATE_PREFIX "get_lun");
+	di_free(lun);
+out:
+	return 0;
+}
+
+static int remove_lun(struct debconfclient *client, const char *lunstr,
+		      struct zfcp_host *zfcp_host, struct lun_tree *lt)
+{
+	int rc;
+	char *value, *hctl;
+	result_t result;
+	struct zfcp_lun_devid *lun;
+
+	lun = di_malloc0(sizeof(*lun));
+	if (lun == NULL)
+		return -1;
+	result = zfcp_parse_wwpn_lun(lun, zfcp_host, lunstr);
+	if (result != RESULT_OK) {
+		di_free(lun);
+		di_error("REMOVE_LUN: Failed to parse LUN: %s\n", lunstr);
+		return -1;
+	}
+
+	rc = 0;
+	debconf_subst(client, TEMPLATE_PREFIX "remove_lun", "LUN", lunstr);
+	rc = debconf_result(client, "critical", TEMPLATE_PREFIX "remove_lun", &value);
+	if (rc == CMD_GOBACK)
+		goto out;
+
+	if (!strcmp(value, "false"))
+		goto out;
+
+	/* Lookup the LUN and retrieve the associated SCSI device */
+	hctl = di_tree_lookup(lun_tree(lt), lun);
+	if (hctl == NULL) {
+		di_warning("REMOVE_LUN: Could not find LUN %s\n", value);
+		goto out;
+	}
+
+	/*
+	 * Remove the SCSI device and, then, remove the LUN from the zfcp
+	 * host adapter.
+	 */
+	scsi_device_remove(hctl);
+	result = zfcp_lun_remove(lun);
+	switch (result) {
+	case RESULT_OK:
+		break;
+	case RESULT_ZFCP_FCP_NOT_FOUND:
+		di_warning("REMOVE_LUN: Could not find FCP device: %s\n",
+			   zfcp_host->name);
+	default:/* fall-through */
+		rc = -1;
+		debconf_show_error(client, TEMPLATE_PREFIX "lun_not_removed");
+		goto out;
+	}
+
+	/*
+	 * Repopulate the LUN tree and confirm that the SCSI device was
+	 * removed.
+	 */
+	lun_tree_populate(lt);
+	if (di_tree_lookup(lun_tree(lt), lun) != NULL) {
+		di_warning("REMOVE_LUN: LUN still present %s\n", lunstr);
+		debconf_show_error(client, TEMPLATE_PREFIX "lun_not_removed");
+	}
+out:
+	di_free(lun);
+	return rc;
+}
+
+static void build_lun_list(void *key, void *value __unused, void *data)
+{
+	struct zfcp_lun_devid *lun = key;
+	char *list = data;
+	size_t len;
+
+	if (list[0])
+		strncat(list, ",", MAX_LUN_LIST_SIZE);
+	len = strnlen(list, MAX_LUN_LIST_SIZE);
+	if (len == MAX_LUN_LIST_SIZE)
+		return;
+	snprintf(list + len, MAX_LUN_LIST_SIZE - len,
+		 "0x%016" PRIx64 ":0x%016" PRIx64, lun->wwpn, lun->lun);
+}
+
+static enum state_wanted add_zfcp_luns_user(struct debconfclient *client,
+					    struct lun_tree *lt)
+{
+	int finished;
+	cmdstatus_t rc;
+	char *lunlist, *action;
+	enum state_wanted wanted;
+	struct zfcp_host *zfcp_host = lt->hba;
+
+	lunlist = di_new0(char, MAX_LUN_LIST_SIZE);
+	if (lunlist == NULL)
+		return WANT_ERROR;
+
+	wanted = WANT_NEXT;
+	finished = 0;
+	do {
+		memset(lunlist, 0, MAX_LUN_LIST_SIZE);
+		di_tree_foreach(lun_tree(lt), build_lun_list, lunlist);
+		debconf_subst(client, TEMPLATE_PREFIX "add_zfcp_luns", "LUNS", lunlist);
+		rc = debconf_result(client, "critical", TEMPLATE_PREFIX "add_zfcp_luns", &action);
+		if (rc == CMD_GOBACK) {
+			/*
+			 * The current configuration is kept as-is.  Added and
+			 * enabled LUNs are not removed.  The users can simply
+			 * go back and can then continue where they have
+			 * stopped.
+			 */
+			wanted = WANT_BACKUP;
+			goto out;
+		}
+
+		if (!strcmp(action, "Add LUN"))
+			if (add_lun(client, zfcp_host, lt) < 0) {
+				wanted = WANT_ERROR;
+				goto out;
+			}
+
+		if (starts_with(action, "0x"))
+			if (remove_lun(client, action, zfcp_host, lt) < 0) {
+				wanted = WANT_ERROR;
+				goto out;
+			}
+
+		if (!strcmp(action, "Finish"))
+			finished = 1;
+	} while (!finished);
+out:
+	di_free(lunlist);
+	return wanted;
+}
+
+struct preseed_cb {
+	struct lun_tree *lt;
+	int err;
+};
+
+static void preseed_add_luns(void *key, void *value, void *cookie)
+{
+	char *ent;
+	result_t rc;
+	int *seen = value;
+	struct preseed_cb *data = cookie;
+	struct zfcp_lun_devid *devid = key;
+
+	/* Skip the preseed LUN entry if an error condition is pending */
+	if (data->err)
+		return;
+
+	/*
+	 * The preseed data tree contains LUNs for different zfcp host
+	 * bus adapters.  Only those that match the specified host bus
+	 * adapter in the lun tree are important here.
+	 */
+	if (ccw_cmp_devids(&devid->fcp_dev, &data->lt->hba->devid))
+		return;
+
+	zfcp_lun_devid_to_str(&ent, devid);
+
+	rc = __add_lun(devid, data->lt);
+	switch (rc) {
+	case RESULT_OK:
+		di_info("PRESEED Successfully added LUN: %s\n", ent);
+		break;
+	case RESULT_ZFCP_WWPN_NOT_FOUND:
+		data->err = 1;
+		di_error("Could not find target port: %s\n", ent);
+		break;
+	case RESULT_ZFCP_INVALID_LUN:
+		data->err = 1;
+		di_error("Could not add LUN, %s is not valid\n", ent);
+		break;
+	case RESULT_ZFCP_SCSI_NOT_FOUND:
+		data->err = 1;
+		di_error("Could not find SCSI device for %s\n", ent);
+		break;
+	default:
+		data->err = 1;
+		di_error("Could not add LUN %s: %d/%d\n", ent,
+			 exit_code(rc), reason_code(rc));
+		break;
+	}
+
+	free(ent);
+	*seen = 1;	    /* Mark the preseed data entry as processed */
+}
+
+static enum state_wanted add_zfcp_luns(struct debconfclient *client,
+				       struct zfcp_host *zfcp_host)
+{
+	enum state_wanted wanted;
+	struct preseed_cb data;
+	struct lun_tree lt = {
+		.hba = zfcp_host,
+		.luns = NULL,
+	};
+
+	/*
+	 * Create a list of active LUNs for the specified zfcp host
+	 * adapter.  This step is important for adapters using NPIV
+	 * with automatic LUN scanning to include the LUNs in the
+	 * device configuration.
+	 *
+	 * The list of active LUNs is presented to users and they
+	 * can add or remove LUNs.  The list is then recreated and,
+	 * if the user completes the configuration, the tree is added
+	 * and written to the configuration file.
+	 */
+	lun_tree_populate(&lt);
+
+	/*
+	 * If automatic LUN scanning is allowed and the specified zfcp
+	 * host adapter uses N_PORT ID virtualization, LUNs become
+	 * automatically added.  Adding the SCSI devices takes some
+	 * time to settle.
+	 */
+	if (zfcp_allow_lun_scan && zfcp_host->npiv) {
+		if (zfcp_host->luns == NULL) {
+			debconf_udev_progress(client, SCSI_ASYNC_TIMEOUT, 5);
+			lun_tree_populate(&lt);
+			zfcp_host->luns = lun_tree(&lt);
+		}
+		return WANT_NEXT;
+	}
+
+	/*
+	 * Add LUNs to the zfcp host bus adapter that is specified in
+	 * lun tree data structure.
+	 */
+	if (preseeded) {
+		data.lt = &lt;
+		data.err = 0;
+		di_tree_foreach(preseed_data, preseed_add_luns, &data);
+		wanted = data.err ? WANT_ERROR : WANT_NEXT;
+	} else {
+		wanted = add_zfcp_luns_user(client, &lt);
+	}
+
+	/*
+	 * Replace the current LUN list with the updated LUN list before
+	 * advacning to the next state.
+	 *
+	 * If the updated LUN list is empty, do not configure the FCP
+	 * device and go back to (re-)select a FCP device for configuration.
+	 * Note that an empty LUN list is considered an error condition if
+	 * any LUNs were preseeded.
+	 */
+	if (wanted == WANT_NEXT) {
+		if (zfcp_host->luns != NULL)
+			di_tree_destroy(zfcp_host->luns);
+		zfcp_host->luns = lun_tree(&lt);
+		zfcp_host->write_luns = 1;
+		if (!di_tree_size(zfcp_host->luns))
+			wanted = preseeded ? WANT_ERROR : WANT_BACKUP;
+	}
+
+	/* Free a temporary LUN list */
+	if (zfcp_host->luns == NULL)
+		lun_tree_destroy(&lt);
+
+	return wanted;
+}
+
+static void write_lun_config(void *key, void *value __unused, void *data)
+{
+	struct zfcp_lun_devid *lun = key;
+	FILE *config = data;
+
+	fprintf(config, "0x%016" PRIx64 ":0x%016" PRIx64 "\n",
+		lun->wwpn, lun->lun);
+}
+
+static enum state_wanted write_config_file(struct zfcp_host *zfcp_host)
+{
+	result_t rc;
+	FILE *config;
+	char buf[PATH_MAX], *id;
+
+	/* Build configuration file path using the adapter CCW device ID */
+	rc = ccw_devid_to_str(&id, &zfcp_host->devid);
+	if (rc != RESULT_OK)
+		return WANT_ERROR;
+	snprintf(buf, sizeof(buf), CONFIG_PREFIX "%s", id);
+	free(id);
+
+	/* Create configuration file */
+	config = fopen(buf, "w");
+	if (config == NULL) {
+		di_error("Could not create configuration file: %s: %s\n",
+			 buf, strerror(errno));
+		return WANT_ERROR;
+	}
+
+	di_debug("WRITE CONFIG: %s\n", buf);
+
+	/*
+	 * Write the start of the zfcp device array.  For NPIV-enabled FCP
+	 * devices, the zfcp device array might be empty depending on
+	 * whether the user requested to write the LUN configuration.
+	 *
+	 * For non-NPIV FCP devices, the list contains the WWPN:LUN pairs
+	 * which the user has configured.
+	 */
+	fprintf(config, "ZFCP_DEVICES=(\n");
+	if (zfcp_host->write_luns)
+		di_tree_foreach(zfcp_host->luns, write_lun_config, config);
+	fprintf(config, ")\n");
+	fflush(config);
+	fclose(config);
+
+	zfcp_host->configured = 1;
+
+	return WANT_NEXT;
+}
+
+
+
+int main(void)
+{
+	int rc;
+	enum state state;
+	enum state_wanted wanted;
+	static struct debconfclient *client;
+	struct zfcp_host *selected_zfcp_host;
+
+	/*
+	 * Initializes the debian-installer library and
+	 * create a debconf client handle.
+	 */
+	di_system_init("s390-zfcp");
+	client = debconfclient_new();
+	debconf_capb(client, DEBCONF_CAPS);
+
+	/*
+	 * Initialize the zfcp host adapeter list.
+	 */
+	zfcp_hosts = di_tree_new_full(di_compare_ccw_devids, NULL, di_free_zfcp_host);
+
+	rc = 0;
+	selected_zfcp_host = NULL;
+	state = DETECT_ZFCP_HOSTS;
+	while (1) {
+		wanted = WANT_ERROR;
+		switch (state) {
+		case DETECT_ZFCP_HOSTS:
+			wanted = detect_zfcp_hosts(client);
+			break;
+		case PRESEED:
+			wanted = preseed_zfcp(client);
+			break;
+		case SELECT_ZFCP_HOST:
+			if (preseeded)
+				wanted = select_preseeded(&selected_zfcp_host);
+			else
+				wanted = select_zfcp_host(client, &selected_zfcp_host);
+			break;
+		case REMOVE_CONFIG:
+			wanted = remove_config_file(client, selected_zfcp_host);
+			break;
+		case ENABLE:
+			wanted = enable_zfcp_host(selected_zfcp_host);
+			break;
+		case ADD_ZFCP_LUNS:
+			wanted = add_zfcp_luns(client, selected_zfcp_host);
+			break;
+		case WRITE:
+			wanted = write_config_file(selected_zfcp_host);
+			break;
+		case BACKUP:
+			rc = EXIT_BACKUP;
+			goto out;
+		case ERROR:
+			rc = EXIT_FAILURE;
+			goto out;
+		case FINISH:
+			if (zfcp_hosts)
+				di_tree_destroy(zfcp_hosts);
+			rc = EXIT_SUCCESS;
+			goto out;
+		}
+
+		switch (wanted) {
+		case WANT_NEXT:
+			switch (state) {
+			case DETECT_ZFCP_HOSTS:
+				/*
+				 * CIO_IGNORE:
+				 *
+				 * To support cio_ignore, use preceeding or
+				 * ask the user for FCP devices to be removed
+				 * from the blacklist.
+				 */
+				state = PRESEED;
+				break;
+			case PRESEED:
+				state = SELECT_ZFCP_HOST;
+				break;
+			case SELECT_ZFCP_HOST:
+				state = REMOVE_CONFIG;
+				break;
+			case REMOVE_CONFIG:
+				state = ENABLE;
+				break;
+			case ENABLE:
+				state = ADD_ZFCP_LUNS;
+				break;
+			case ADD_ZFCP_LUNS:
+				state = WRITE;
+				break;
+			case WRITE:
+				state = SELECT_ZFCP_HOST;
+				break;
+			default:
+				/* Program is not in a valid state */
+				state = WANT_ERROR;
+				break;
+			}
+			break;
+		case WANT_FINISH:
+			state = FINISH;
+			break;
+		case WANT_BACKUP:
+			/*
+			 * If the user press the "Back" button, the debconf go
+			 * returns CMD_GOBACK (== 30) and the WANT_BACKUP
+			 * state should be set.  The WANT_BACKUP state should
+			 * then go a step backward by setting the actual state
+			 * variable to a previous state.
+			 */
+			switch (state) {
+			case SELECT_ZFCP_HOST:
+				state = BACKUP;
+				break;
+			case REMOVE_CONFIG:
+				state = SELECT_ZFCP_HOST;
+				break;
+			case ADD_ZFCP_LUNS:
+				state = SELECT_ZFCP_HOST;
+				break;
+			default:
+				state = ERROR;
+				break;
+			}
+			break;
+		case WANT_ERROR:
+			state = ERROR;
+			break;
+		};
+	}
+out:
+	debconfclient_delete(client);
+	return rc;
+}
-- 
2.6.2

From 09a4dc24c519a53a5057ab51b0c63e964b9c269a Mon Sep 17 00:00:00 2001
From: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
Date: Tue, 15 Dec 2015 11:24:11 +0100
Subject: [PATCH 5/5] debian: Add Debian packaging files

Add required files to build the Debian udeb for the Debian Installer.
Also add the post install script to invoke the FCP device configuration
utility.

Signed-off-by: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
---
 debian/changelog           |   5 +
 debian/compat              |   1 +
 debian/control             |  16 ++++
 debian/copyright           |   7 ++
 debian/install             |   1 +
 debian/po/POTFILES.in      |   1 +
 debian/po/templates.pot    | 222 +++++++++++++++++++++++++++++++++++++++++++++
 debian/postinst            |  14 +++
 debian/rules               |   3 +
 debian/s390-zfcp.templates | 102 +++++++++++++++++++++
 debian/source/format       |   1 +
 11 files changed, 373 insertions(+)
 create mode 100644 debian/changelog
 create mode 100644 debian/compat
 create mode 100644 debian/control
 create mode 100644 debian/copyright
 create mode 100644 debian/install
 create mode 100644 debian/po/POTFILES.in
 create mode 100644 debian/po/templates.pot
 create mode 100644 debian/postinst
 create mode 100755 debian/rules
 create mode 100644 debian/s390-zfcp.templates
 create mode 100644 debian/source/format

diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..34ea40d
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,5 @@
+s390-zfcp (1.0.0) UNRELEASED; urgency=medium
+
+  * Initial release
+
+ -- Hendrik Brueckner <brueckner@linux.vnet.ibm.com>  Tue, 15 Dec 2015 11:20:03 +0100
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..ec63514
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+9
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..ca12474
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,16 @@
+Source: s390-zfcp
+Section: debian-installer
+Priority: standard
+Standards-Version: 3.9.5
+Maintainer: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+Build-Depends: debhelper (>= 9), po-debconf (>= 0.5.0), libdebconfclient0-dev (>=0.47), libdebian-installer-dev
+
+Package: s390-zfcp
+Package-Type: udeb
+Architecture: s390x
+Depends: ${shlibs:Depends}, ${misc:Depends}, scsi-modules, s390-sysconfig-writer
+Provides: harddrive-detection
+XB-Installer-Menu-Item: 3700
+Description: Activate and configure FCP devices for installation
+ Configure FCP devices to install Debian on FC-attached SCSI devices
+ available on Linux on z Systems.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..8d7ef65
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,7 @@
+Copyright IBM Corp. 2015
+Author(s):  Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+	    Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
+
+This package is under the GNU General Public License, version 2, which
+can usually be found in /usr/share/common-licenses/GPL-2 on Debian
+systems.
diff --git a/debian/install b/debian/install
new file mode 100644
index 0000000..94024bf
--- /dev/null
+++ b/debian/install
@@ -0,0 +1 @@
+zfcp-config	sbin/
diff --git a/debian/po/POTFILES.in b/debian/po/POTFILES.in
new file mode 100644
index 0000000..9835263
--- /dev/null
+++ b/debian/po/POTFILES.in
@@ -0,0 +1 @@
+[type: gettext/rfc822deb] s390-zfcp.templates
diff --git a/debian/po/templates.pot b/debian/po/templates.pot
new file mode 100644
index 0000000..8e1f6a6
--- /dev/null
+++ b/debian/po/templates.pot
@@ -0,0 +1,222 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the s390-zfcp package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: s390-zfcp\n"
+"Report-Msgid-Bugs-To: s390-zfcp@packages.debian.org\n"
+"POT-Creation-Date: 2015-12-15 11:26+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. Type: text
+#. Description
+#: ../s390-zfcp.templates:2001
+msgid "Activate FCP devices for installation"
+msgstr ""
+
+#. Type: note
+#. Description
+#: ../s390-zfcp.templates:4001
+msgid "No FCP adapters available for installation"
+msgstr ""
+
+#. Type: note
+#. Description
+#: ../s390-zfcp.templates:4001
+msgid "Try to use DASDs for installation."
+msgstr ""
+
+#. Type: select
+#. Choices
+#: ../s390-zfcp.templates:5001
+msgid "${choices}"
+msgstr ""
+
+#. Type: select
+#. Choices
+#. Type: select
+#. Choices
+#: ../s390-zfcp.templates:5001 ../s390-zfcp.templates:7001
+msgid "Finish"
+msgstr ""
+
+#. Type: select
+#. Description
+#: ../s390-zfcp.templates:5002
+msgid "Available FCP devices:"
+msgstr ""
+
+#. Type: select
+#. Description
+#: ../s390-zfcp.templates:5002
+msgid ""
+"The following FCP devices are available for installation.  Select each FCP "
+"device you want to activate for accessing FC-attached SCSI devices.  "
+"Depending on your FCP device configuration, you will be asked further setup "
+"questions."
+msgstr ""
+
+#. Type: select
+#. Description
+#: ../s390-zfcp.templates:5002
+msgid ""
+"Select \"Finish\" when you have all FCP devices activated for your "
+"installation."
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../s390-zfcp.templates:6001
+msgid "Do you want to remove the FCP device configuration?"
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../s390-zfcp.templates:6001
+msgid ""
+"Confirm removing the FCP device configuration.  If you click \"Yes\", the "
+"FCP device configuration file is removed.  If you click \"No\", you can "
+"update the current FCP device configuration."
+msgstr ""
+
+#. Type: select
+#. Choices
+#: ../s390-zfcp.templates:7001
+msgid "${LUNS}"
+msgstr ""
+
+#. Type: select
+#. Choices
+#: ../s390-zfcp.templates:7001
+msgid "Add LUN"
+msgstr ""
+
+#. Type: select
+#. Description
+#. XXX add FCP device number in description ???
+#: ../s390-zfcp.templates:7002
+msgid "Available LUNs for installation:"
+msgstr ""
+
+#. Type: select
+#. Description
+#. XXX add FCP device number in description ???
+#: ../s390-zfcp.templates:7002
+msgid ""
+"The following list displays the LUNs that are available for installation. "
+"Click \"Add LUN\" to attach a SCSI device through its LUN.  When you have "
+"added the LUNs that you need for installation, click \"Finish\"."
+msgstr ""
+
+#. Type: string
+#. Description
+#: ../s390-zfcp.templates:8001
+msgid "WWPN and LUN for the SCSI device:"
+msgstr ""
+
+#. Type: string
+#. Description
+#: ../s390-zfcp.templates:8001
+msgid ""
+"Specify the worldwide port name (WWPN) of the target port and the logical "
+"unit number (LUN) to attach a particular SCSI disk.  The WWPN and LUN each "
+"consists of 16 hexadecimal digits.  Separate the WWPN and LUN with a colon "
+"(:). For example, 0x2005000e11159c32:0x1234567800000000."
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../s390-zfcp.templates:9001
+msgid "Do you want to remove the LUN ${LUN}?"
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../s390-zfcp.templates:9001
+msgid "Confirm removal of the specified LUN."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../s390-zfcp.templates:10001
+msgid "Could not recognize the specified LUN"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../s390-zfcp.templates:10001
+msgid ""
+"The specified LUN could not be recognized.  Specify a valid WWPN and LUN "
+"value which are separated by a colon (:)."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../s390-zfcp.templates:11001
+msgid "The specified WWPN or LUN is not valid"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../s390-zfcp.templates:11001
+msgid ""
+"Ensure that you specify the WWPN and LUN value as hexadecimal value, each "
+"consisting of up to 16 hexadecimal digits."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../s390-zfcp.templates:12001
+msgid "Could not found the specified target port"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../s390-zfcp.templates:12001
+msgid ""
+"The requested SCSI device could not be attached.  The target port that was "
+"specified with the WWPN could not be found.  Ensure you specified the "
+"correct WWPN value."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../s390-zfcp.templates:13001
+msgid "Could not add the specified LUN"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../s390-zfcp.templates:13001
+msgid ""
+"The requested SCSI device could not be attached. The specified LUN could not "
+"be added to the specified target port.  Ensure you specified the correct LUN "
+"and, respectively, WWPN."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../s390-zfcp.templates:14001
+msgid "Could not remove the specified LUN"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../s390-zfcp.templates:14001
+msgid "The requested SCSI device could not be removed."
+msgstr ""
+
+#. Type: text
+#. Description
+#: ../s390-zfcp.templates:15001
+msgid "Detecting SCSI devices..."
+msgstr ""
diff --git a/debian/postinst b/debian/postinst
new file mode 100644
index 0000000..29f1897
--- /dev/null
+++ b/debian/postinst
@@ -0,0 +1,14 @@
+#! /bin/sh
+set -e
+
+#
+# Run depmod to generate module dependencies and map file
+# to automatically detect and modules when SCSI devices
+# and partitions become active
+#
+test -x /sbin/depmod && depmod -a > /dev/null 2>&1 || true
+
+#
+# Start the FCP configuration utility
+#
+exec zfcp-config
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..78c7615
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,3 @@
+#! /usr/bin/make -f
+%:
+	dh $@
diff --git a/debian/s390-zfcp.templates b/debian/s390-zfcp.templates
new file mode 100644
index 0000000..1b4aae8
--- /dev/null
+++ b/debian/s390-zfcp.templates
@@ -0,0 +1,102 @@
+#
+# Templates for the s390-zfcp debian installer module
+#
+
+Template: debian-installer/s390-zfcp/title
+Type: text
+_Description: Activate FCP devices for installation
+
+
+Template: s390-zfcp/zfcp
+Type: string
+Description: for preseeding only
+ Enables one ore more SCSI devices for the installation.  Use the
+ comma (,) to separate multiple entries.  An entry consists of a
+ zfcp host adapter only or a zfcp host adapter followed by the WWPN
+ and LUN, separated by a colon (:).  For example,
+ 0.0.1234,0.0.5678:0x2005000e11159c32:0x1234567800000000
+
+
+Template: s390-zfcp/no_zfcp_hosts
+Type: note
+_Description: No FCP adapters available for installation
+ Try to use DASDs for installation.
+
+Template: s390-zfcp/select_zfcp_host
+Type: select
+__Choices: ${choices}, Finish
+_Description: Available FCP devices:
+ The following FCP devices are available for installation.  Select
+ each FCP device you want to activate for accessing FC-attached
+ SCSI devices.  Depending on your FCP device configuration, you
+ will be asked further setup questions.
+ .
+ Select "Finish" when you have all FCP devices activated for your
+ installation.
+
+Template: s390-zfcp/remove_zfcp_config
+Type: boolean
+Default: false
+_Description: Do you want to remove the FCP device configuration?
+ Confirm removing the FCP device configuration.  If you click "Yes",
+ the FCP device configuration file is removed.  If you click "No",
+ you can update the current FCP device configuration.
+
+
+Template: s390-zfcp/add_zfcp_luns
+Type: select
+__Choices: ${LUNS}, Add LUN, Finish
+# XXX add FCP device number in description ???
+_Description: Available LUNs for installation:
+ The following list displays the LUNs that are available for installation.
+ Click "Add LUN" to attach a SCSI device through its LUN.  When you have
+ added the LUNs that you need for installation, click "Finish".
+
+Template: s390-zfcp/get_lun
+Type: string
+_Description: WWPN and LUN for the SCSI device:
+ Specify the worldwide port name (WWPN) of the target port and the logical unit
+ number (LUN) to attach a particular SCSI disk.  The WWPN and LUN each consists
+ of 16 hexadecimal digits.  Separate the WWPN and LUN with a colon (:).
+ For example, 0x2005000e11159c32:0x1234567800000000.
+
+Template: s390-zfcp/remove_lun
+Type: boolean
+Default: false
+_Description: Do you want to remove the LUN ${LUN}?
+ Confirm removal of the specified LUN.
+
+Template: s390-zfcp/invalid_lun_fmt
+Type: error
+_Description: Could not recognize the specified LUN
+ The specified LUN could not be recognized.  Specify a valid WWPN and LUN
+ value which are separated by a colon (:).
+
+Template: s390-zfcp/invalid_wwpn_or_lun
+Type: error
+_Description: The specified WWPN or LUN is not valid
+ Ensure that you specify the WWPN and LUN value as hexadecimal value,
+ each consisting of up to 16 hexadecimal digits.
+
+Template: s390-zfcp/port_not_found
+Type: error
+_Description: Could not found the specified target port
+ The requested SCSI device could not be attached.  The target port that was
+ specified with the WWPN could not be found.  Ensure you specified the
+ correct WWPN value.
+
+Template: s390-zfcp/lun_not_added
+Type: error
+_Description: Could not add the specified LUN
+ The requested SCSI device could not be attached. The specified LUN could
+ not be added to the specified target port.  Ensure you specified the
+ correct LUN and, respectively, WWPN.
+
+Template: s390-zfcp/lun_not_removed
+Type: error
+_Description: Could not remove the specified LUN
+ The requested SCSI device could not be removed.
+
+Template: s390-zfcp/udev_progress
+Type: text
+_Description: Detecting SCSI devices...
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..89ae9db
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+3.0 (native)
-- 
2.6.2

Attachment: pgpHFaPRNhTuc.pgp
Description: PGP signature


Reply to: