This patch contains all new IBM features for s390-tools-1.8.0 SLES11 SP1.
It applies to Novell s390-tools-1.8.0-43.3.2 plus the following add-on
patch: s390-tools-sles11sp1-dasdfmt-norecord_r0.patch

Summary:     iucvterm: IUCV terminal applications
Description: The IUCV terminal application consists of iucvconn, iucvtty,
             and ts-shell. In addition, this feature patch provides the
             documentation for these tools.
             The iucvconn program establishes a terminal connection to
             another Linux instance. The communication path between both
             virtual machines is based on z/VM IUCV. Because IUCV is
             independent from TCP/IP, Linux instances with no external
             network can be accessed.
             iucvconn can establish connections to either an iucvtty
             instance or a z/VM IUCV HVC terminal device.
             The iucvtty program allows remote logins over IUCV.
             ts-shell is a login shell for terminal servers using
             iucvconn to access the terminals over z/VM IUCV.

Summary:     zipl, dasdview, dasdfmt, fdasd: Add large volume support
Description: Add support for the new disk sizes and the extended
             label information (cdl and ldl).

Summary:     zipl dump tools: Add large volume support
Description: DASD dump tools are enabled to support devices with more
             than 65520 cylinders.

Summary:     dasdview: DS8000 disk encryption support
Description: Add support for encrypted disks on DS8000
             Add option -c or --characteristic to dasdview to.
             show if a disk is encrypted or not..

Summary:     lsluns: Add disk encryption display feature.
Description: Add support for displaying the new disk encryption feature.

Summary:     Add ziomon reporting tools
Description: Tool set for zfcp performance report creation.

Summary:     dumpconf: New shutdown action "dump_reipl" for dumpconf.
Description: The dump tools are enabled to trigger re-IPL after the dump
             process has completed.

Summary:     vmur: Add --convert option
Description: Convert VMDUMP spool file into a format appropriate for further
             analysis with crash or lcrash, while receiving it.

Summary:     zconf: Add new network configuration tool znetconf
Description: znetconf lists and configures network devices for System z
             network adapters.
---
 Makefile                                        |    2 
 README                                          |   19 
 common.mak                                      |   12 
 dasdfmt/dasdfmt.c                               |  134 +
 dasdfmt/dasdfmt.h                               |  107 -
 dasdview/dasdview.8                             |   18 
 dasdview/dasdview.c                             |  681 ++++---
 dasdview/dasdview.h                             |   29 
 etc/init.d/dumpconf                             |  172 +
 etc/sysconfig/dumpconf                          |   19 
 fdasd/fdasd.c                                   |  481 +++--
 fdasd/fdasd.h                                   |   26 
 include/vtoc.h                                  |  112 -
 iucvterm/Makefile                               |   28 
 iucvterm/bin/Makefile                           |   42 
 iucvterm/bin/chiucvallow.in                     |  273 +++
 iucvterm/bin/ts-shell.in                        |  669 +++++++
 iucvterm/doc/Makefile                           |   27 
 iucvterm/doc/chiucvallow.8                      |  145 +
 iucvterm/doc/hvc_iucv.9                         |  340 +++
 iucvterm/doc/iucvconn.1                         |  223 ++
 iucvterm/doc/iucvtty.1                          |  205 ++
 iucvterm/doc/ts-shell.1                         |  574 ++++++
 iucvterm/doc/ts-shell/README.ts-shell           |  126 +
 iucvterm/doc/ts-shell/authorization-sample.conf |   24 
 iucvterm/doc/ts-shell/iucvconn_on_login         |   35 
 iucvterm/doc/ts-shell/sample-system.list        |   11 
 iucvterm/etc/Makefile                           |   30 
 iucvterm/etc/ts-audit-systems.conf              |    3 
 iucvterm/etc/ts-authorization.conf              |    6 
 iucvterm/etc/ts-shell.conf.in                   |   27 
 iucvterm/etc/unrestricted.conf                  |    2 
 iucvterm/include/af_iucv.h                      |   29 
 iucvterm/include/iucvterm/config.h              |   31 
 iucvterm/include/iucvterm/functions.h           |   74 
 iucvterm/include/iucvterm/gettext.h             |   74 
 iucvterm/include/iucvterm/proto.h               |  108 +
 iucvterm/po/Makefile                            |   57 
 iucvterm/po/POTFILES.in                         |    4 
 iucvterm/src/Makefile                           |   33 
 iucvterm/src/auditlog.c                         |  207 ++
 iucvterm/src/functions.c                        |  519 +++++
 iucvterm/src/getopt.c                           |  226 ++
 iucvterm/src/iucvconn.c                         |  349 ++++
 iucvterm/src/iucvtty.c                          |  292 +++
 iucvterm/test/Makefile                          |   40 
 iucvterm/test/test.h                            |   24 
 iucvterm/test/test_afiucv.c                     |   30 
 iucvterm/test/test_allow.c                      |   77 
 iucvterm/test/test_auditlog.c                   |   76 
 iucvterm/test/test_common.c                     |   40 
 iucvterm/test/test_functions.c                  |  243 ++
 libvtoc/vtoc.c                                  |  215 +-
 man/Makefile                                    |   19 
 man/af_iucv.7                                   |  402 ++++
 man/dumpconf.8                                  |   56 
 vmconvert/Makefile                              |    5 
 vmconvert/convert.cpp                           |   71 
 vmconvert/dump.cpp                              |   26 
 vmconvert/dump.h                                |   17 
 vmconvert/lkcd_dump.cpp                         |   47 
 vmconvert/main.cpp                              |   69 
 vmconvert/register_content.cpp                  |    4 
 vmconvert/vm_dump.cpp                           |  139 -
 vmur/Makefile                                   |   19 
 vmur/vmur.8                                     |    8 
 vmur/vmur.c                                     | 2054 -----------------------
 vmur/vmur.cpp                                   | 2092 ++++++++++++++++++++++++
 vmur/vmur.h                                     |   14 
 zconf/Makefile                                  |    7 
 zconf/lsluns                                    |  137 +
 zconf/lsluns.8                                  |   12 
 zconf/lsznet.raw                                |  363 ++++
 zconf/znetconf                                  | 1418 ++++++++++++++++
 zconf/znetconf.8                                |  313 +++
 zconf/znetcontrolunits                          |   53 
 zfcpdump_v2/zfcpdump.c                          |    8 
 zfcpdump_v2/zfcpdump.h                          |    2 
 ziomon/Makefile                                 |   39 
 ziomon/blkiomon.h                               |   45 
 ziomon/stats.h                                  |   41 
 ziomon/ziomon                                   |  124 +
 ziomon/ziomon.8                                 |   12 
 ziomon/ziomon_dacc.c                            |  528 ++++--
 ziomon/ziomon_dacc.h                            |  108 +
 ziomon/ziomon_fcpconf                           |   12 
 ziomon/ziomon_fcpconf.8                         |   13 
 ziomon/ziomon_mgr.8                             |   13 
 ziomon/ziomon_mgr.c                             |  329 +--
 ziomon/ziomon_msg_tools.c                       |  137 +
 ziomon/ziomon_msg_tools.h                       |   29 
 ziomon/ziomon_tools.c                           |   56 
 ziomon/ziomon_tools.h                           |   34 
 ziomon/ziomon_util.8                            |   12 
 ziomon/ziomon_util.c                            |  564 +++---
 ziomon/ziomon_util.h                            |   69 
 ziomon/ziomon_zfcpdd.8                          |   18 
 ziomon/ziomon_zfcpdd.c                          |  180 +-
 ziomon/ziomon_zfcpdd.h                          |   22 
 ziomon/ziorep_cfgreader.cpp                     |  889 ++++++++++
 ziomon/ziorep_cfgreader.hpp                     |  239 ++
 ziomon/ziorep_collapser.cpp                     |  570 ++++++
 ziomon/ziorep_collapser.hpp                     |  185 ++
 ziomon/ziorep_config                            |  492 +++++
 ziomon/ziorep_config.8                          |  118 +
 ziomon/ziorep_filters.cpp                       |  516 +++++
 ziomon/ziorep_filters.hpp                       |  214 ++
 ziomon/ziorep_framer.cpp                        |  288 +++
 ziomon/ziorep_framer.hpp                        |   79 
 ziomon/ziorep_frameset.cpp                      |  607 ++++++
 ziomon/ziorep_frameset.hpp                      |  228 ++
 ziomon/ziorep_printers.cpp                      | 1254 ++++++++++++++
 ziomon/ziorep_printers.hpp                      |  220 ++
 ziomon/ziorep_traffic.8                         |  216 ++
 ziomon/ziorep_traffic.cpp                       |  547 ++++++
 ziomon/ziorep_utilization.8                     |  137 +
 ziomon/ziorep_utilization.cpp                   |  372 ++++
 ziomon/ziorep_utils.cpp                         |  555 ++++++
 ziomon/ziorep_utils.hpp                         |   85 
 zipl/boot/Makefile                              |   31 
 zipl/boot/dumpcommon.S                          |   86 
 zipl/boot/eckd2dump.S                           |  241 --
 zipl/boot/eckd2mvdump.S                         |   42 
 zipl/boot/tapedump.S                            |   95 -
 zipl/include/disk.h                             |    1 
 zipl/man/zipl.conf.5                            |    3 
 zipl/src/Makefile                               |    7 
 zipl/src/boot.c                                 |  107 -
 zipl/src/bootmap.c                              |    3 
 zipl/src/disk.c                                 |   24 
 zipl/src/install.c                              |   98 -
 zipl/src/job.c                                  |    6 
 zipl/src/zipl.c                                 |    4 
 133 files changed, 21389 insertions(+), 4260 deletions(-)

Index: s390-tools-sles11/Makefile
===================================================================
--- s390-tools-sles11.orig/Makefile
+++ s390-tools-sles11/Makefile
@@ -3,7 +3,7 @@ ARCH := $(shell uname -m | sed -e s/i.86
 # Include commond definitions
 include common.mak
 
-SUB_DIRS  = libvtoc zipl zdump fdasd dasdfmt dasdview tunedasd tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools ziomon
+SUB_DIRS  = libvtoc zipl zdump fdasd dasdfmt dasdview tunedasd tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools ziomon iucvterm
 
 ifeq ($(strip $(SYSFS)),true)
 	SUB_DIRS+=libu2s
Index: s390-tools-sles11/README
===================================================================
--- s390-tools-sles11.orig/README
+++ s390-tools-sles11/README
@@ -108,6 +108,8 @@ s390-tools (1.8.0)
      - lsluns:   List available SCSI LUNs depending on adapter or port.
      - lszcrypt: Show Information about zcrypt devices and configuration.
      - chzcrypt: Modify zcrypt configuration.
+     - znetconf: List and configure network devices for System z network
+                 adapters.
 
    * dumpconf:
      Allows to configure the dump device used for system dump in case a kernel
@@ -139,11 +141,26 @@ s390-tools (1.8.0)
                 reboot or panic.
 
    * ziomon tools:
-     Tool set to collect data for zfcp performance analysis.
+     Tool set to collect data for zfcp performance analysis and generate
+     reports.
+
+   * iucvterm:
+     z/VM IUCV terminal applications.
+
+     A set of applications to provide terminal access over the z/VM Inter-User
+     Communication Vehicle (IUCV). The terminal access does not require an
+     active IP network connection between two Linux guest operating systems.
+
+     - iucvconn:  Start terminal connection over z/VM IUCV.
+     - iucvtty:   Allow remote logins over z/VM IUCV.
+     - ts-shell:  Terminal server shell to authorize and control IUCV terminal
+                  connections for individual Linux users.
+
 
 For more information refer to the following publications:
    * "Device Drivers, Features, and Commands" chapter "Useful Linux commands"
    * "Using the dump tools"
+   * "How to Set up a Terminal Server Environment"
 
 Dependencies:
 =============
Index: s390-tools-sles11/common.mak
===================================================================
--- s390-tools-sles11.orig/common.mak
+++ s390-tools-sles11/common.mak
@@ -2,10 +2,13 @@ ifndef $(COMMON_INCLUDED)
 COMMON_INCLUDED = true
 
 # Global definitions
+# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
+# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
 VERSION            = 1
 RELEASE            = 8
 PATCHLEVEL         = 0
-S390_TOOLS_RELEASE = $(VERSION).$(RELEASE).$(PATCHLEVEL)
+DISTRELEASE        = build-$(shell date +%Y%m%d)
+S390_TOOLS_RELEASE = $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
 SYSFS              = true
 export S390_TOOLS_RELEASE SYSFS 
 
@@ -22,7 +25,8 @@ STRIP           = $(CROSS_COMPILE)strip
 OBJCOPY         = $(CROSS_COMPILE)objcopy
 OBJDUMP         = $(CROSS_COMPILE)objdump
 INSTALL         = install # FIXME: We need s390-install (strip)
-CFLAGS		= $(OPT_FLAGS) -Wall -O3 -DS390_TOOLS_RELEASE=$(S390_TOOLS_RELEASE)
+CFLAGS          = $(OPT_FLAGS) -Wall -O3 -DS390_TOOLS_RELEASE=$(S390_TOOLS_RELEASE)
+CXXFLAGS        = $(OPT_FLAGS) -Wall -O3 -DS390_TOOLS_RELEASE=$(S390_TOOLS_RELEASE)
 export AS LD CC CPP AR NM STRIP OBJCOPY OBJDUMP INSTALL CFLAGS
 
 # Support alternate install root
@@ -31,8 +35,10 @@ USRSBINDIR      = $(INSTROOT)/usr/sbin
 USRBINDIR       = $(INSTROOT)/usr/bin
 BINDIR          = $(INSTROOT)/sbin
 LIBDIR          = $(INSTROOT)/lib
+SYSCONFDIR      = $(INSTROOT)/etc
 MANDIR          = $(INSTROOT)/usr/share/man
-INSTDIRS        = $(USRSBINDIR) $(USRBINDIR) $(BINDIR) $(LIBDIR) $(MANDIR)
+TOOLS_LIBDIR    = $(INSTROOT)/lib/s390-tools
+INSTDIRS        = $(USRSBINDIR) $(USRBINDIR) $(BINDIR) $(LIBDIR) $(MANDIR) $(TOOLS_LIBDIR)
 OWNER           = $(shell id -un)
 GROUP		= $(shell id -gn)
 export INSTROOT BINDIR LIBDIR MANDIR OWNER GROUP
Index: s390-tools-sles11/dasdfmt/dasdfmt.c
===================================================================
--- s390-tools-sles11.orig/dasdfmt/dasdfmt.c
+++ s390-tools-sles11/dasdfmt/dasdfmt.c
@@ -23,6 +23,12 @@ static const char tool_name[] = "dasdfmt
 /* Copyright notice */
 static const char copyright_notice[] = "Copyright IBM Corp. 1999, 2006";
 
+static int            filedes        = 0;
+static int            disk_disabled  = 0;
+static format_data_t  format_params;
+char                 *prog_name;
+volatile sig_atomic_t program_interrupt_in_progress = 0;
+
 /*
  * Print version information.
  */
@@ -380,13 +386,13 @@ static format_data_t ask_user_for_blksiz
  * print all information needed to format the device
  */
 static void dasdfmt_print_info(dasdfmt_info_t *info, volume_label_t *vlabel,
-			       struct hd_geometry *drive_geo, format_data_t *p)
+			       unsigned int cylinders, unsigned int heads,
+			       format_data_t *p)
 {
 	char volser[6], vollbl[4];
 
 	printf("Drive Geometry: %d Cylinders * %d Heads =  %d Tracks\n",
-	       drive_geo->cylinders, drive_geo->heads,
-	       (drive_geo->cylinders * drive_geo->heads));
+	       cylinders, heads, (cylinders * heads));
 
 	printf("\nI am going to format the device ");
 	printf("%s in the following way:\n", info->devname);
@@ -453,7 +459,8 @@ static int dasdfmt_get_volser(char * dev
 /*
  * do all the labeling (volume label and initial VTOC)
  */
-static void dasdfmt_write_labels(dasdfmt_info_t *info, volume_label_t *vlabel)
+static void dasdfmt_write_labels(dasdfmt_info_t *info, volume_label_t *vlabel,
+				 unsigned int cylinders, unsigned int heads)
 {
 	unsigned int        label_position;
 	dasd_information_t  dasd_info;
@@ -472,7 +479,12 @@ static void dasdfmt_write_labels(dasdfmt
 	if (ioctl(filedes, BLKSSZGET, &blksize) != 0)
 		ERRMSG_EXIT(EXIT_FAILURE, "%s: (write labels) IOCTL BLKSSZGET "
 			    "failed (%s).\n", prog_name, strerror(errno));
-	
+
+	/*
+         * Don't rely on the cylinders returned by HDIO_GETGEO, they might be
+         * to small. geo is only used to get the number of sectors, which may
+	 * vary depending on the format.
+         */
 	if (ioctl(filedes, HDIO_GETGEO, &geo) != 0)
 		ERRMSG_EXIT(EXIT_FAILURE, "%s: (write labels) IOCTL HDIO_GET"
 			    "GEO failed (%s).\n", prog_name, strerror(errno));
@@ -504,38 +516,48 @@ static void dasdfmt_write_labels(dasdfmt
 			    "failed, only wrote %d bytes.\n", prog_name, rc);
 
 	/* write VTOC */
-	vtoc_init_format4_label(&f4, USABLE_PARTITIONS, geo.cylinders,
-		geo.heads, geo.sectors, blksize, dasd_info.dev_type);
+	vtoc_init_format4_label(&f4, USABLE_PARTITIONS, geo.cylinders, cylinders,
+				heads, geo.sectors, blksize, dasd_info.dev_type);
 
 	vtoc_init_format5_label(&f5);
 	vtoc_init_format7_label(&f7);
 	vtoc_set_freespace(&f4, &f5, &f7, '+', 0, FIRST_USABLE_TRK,
-			   (geo.cylinders * geo.heads - 1),
-			   geo.cylinders, geo.heads);
+			   (cylinders * heads - 1), cylinders, heads);
 
 	label_position = dasd_info.label_block * blksize;
-			
+
 	if (info->verbosity > 0) printf("Writing label...\n");
 
 	rc = lseek(filedes, label_position, SEEK_SET);
 	if (rc != label_position)
 		ERRMSG_EXIT(EXIT_FAILURE, "%s: lseek command to %i failed "
-			    "(%s).\n", prog_name, label_position, 
+			    "(%s).\n", prog_name, label_position,
 			    strerror(errno));
 
+	/*
+	 * Note: cdl volume labels do not contain the 'formatted_blocks' part
+	 * and ldl labels do not contain the key field
+	 */
 	if (info->cdl_format)
-		rc = write(filedes, vlabel, sizeof(volume_label_t));
-	else
-		rc = write(filedes, vlabel->vollbl,(sizeof(volume_label_t)-4));
+		rc = write(filedes, vlabel, (sizeof(*vlabel)
+					     - sizeof(vlabel->formatted_blocks)));
+	else {
+		vlabel->ldl_version = 0xf2; /* EBCDIC '2' */
+		vlabel->formatted_blocks = cylinders * heads * geo.sectors;
+		rc = write(filedes, &vlabel->vollbl, (sizeof(*vlabel)
+						     - sizeof(vlabel->volkey)));
+	}
 
-	if (((rc != sizeof(volume_label_t)) && info->cdl_format) || 
-	    ((rc != (sizeof(volume_label_t)-4)) && (!info->cdl_format)))
+	if (((rc != sizeof(*vlabel) - sizeof(vlabel->formatted_blocks)) &&
+	     info->cdl_format) ||
+	    ((rc != (sizeof(*vlabel)- sizeof(vlabel->volkey))) &&
+	     (!info->cdl_format)))
 		ERRMSG_EXIT(EXIT_FAILURE, "%s: Error writing volume label "
 			    "(%d).\n", prog_name, rc);
 
 	if (info->verbosity > 0) printf("Writing VTOC... ");
 
-	label_position = (VTOC_START_CC * geo.heads + VTOC_START_HH) * 
+	label_position = (VTOC_START_CC * heads + VTOC_START_HH) *
 		geo.sectors * blksize;
 
 	rc = lseek(filedes, label_position, SEEK_SET);
@@ -563,7 +585,7 @@ static void dasdfmt_write_labels(dasdfmt
 		ERRMSG_EXIT(EXIT_FAILURE, "%s: Error writing FMT5 label "
 			    "(%d).\n", prog_name, rc);
 
-	if ((geo.cylinders * geo.heads) > BIG_DISK_SIZE) {
+	if ((cylinders * heads) > BIG_DISK_SIZE) {
 		label_position += blksize;
 
 		rc = lseek(filedes, label_position, SEEK_SET);
@@ -588,7 +610,7 @@ static void dasdfmt_write_labels(dasdfmt
 /*
  * formats the disk cylinderwise
  */
-static void dasdfmt_format(dasdfmt_info_t *info, struct hd_geometry *drive_geo,
+static void dasdfmt_format(dasdfmt_info_t *info, unsigned int cylinders, unsigned int heads,
 			   format_data_t *format_params)
 {
 	format_data_t format_step;
@@ -618,7 +640,7 @@ static void dasdfmt_format(dasdfmt_info_
 		p2 =  0;
 
 		format_step.start_unit = k;
-		format_step.stop_unit  = k + drive_geo->heads - 1;
+		format_step.stop_unit  = k + heads - 1;
 
 		if (cyl == 1)
 			format_step.start_unit += 1;
@@ -629,13 +651,13 @@ static void dasdfmt_format(dasdfmt_info_
 				    prog_name, strerror(errno));
 
 		if (info->print_progressbar) {
-			printf("cyl %5d of %5d |", cyl, drive_geo->cylinders);
+			printf("cyl %5d of %5d |", cyl, cylinders);
 			p2 = p1;
-			p1 = cyl*100/drive_geo->cylinders;
+			p1 = cyl*100/cylinders;
 			if (p1 != p2)
 			{
 				/* percent value has changed */
-				tmp = cyl*50/drive_geo->cylinders;
+				tmp = cyl*50/cylinders;
 				for (j=1; j<=tmp; j++)
 					printf("#");
 				for (j=tmp+1; j<=50; j++) 
@@ -652,12 +674,12 @@ static void dasdfmt_format(dasdfmt_info_
 				fflush(stdout);
 			}
 
-		if (k % drive_geo->heads == 0) {
-			k += drive_geo->heads;
+		if (k % heads == 0) {
+			k += heads;
 			cyl++;
 		}
 		else 
-			k += format_params->stop_unit % drive_geo->heads;
+			k += format_params->stop_unit % heads;
 
 		if (k > format_params->stop_unit) 
 			break;
@@ -672,7 +694,8 @@ static void dasdfmt_format(dasdfmt_info_
  *
  */
 static void dasdfmt_prepare_and_format (dasdfmt_info_t *info,
-					struct hd_geometry *drive_geo,
+					unsigned int cylinders,
+					unsigned int heads,
 					format_data_t *p)
 {
 	format_data_t temp = {
@@ -701,7 +724,7 @@ static void dasdfmt_prepare_and_format (
 	/* except track 0 from standard formatting procss */
 	p->start_unit = 1;
 
-	dasdfmt_format(info, drive_geo, p);
+	dasdfmt_format(info, cylinders, heads, p);
 
 	if (info->verbosity > 0) printf("formatting tracks complete...\n");
 
@@ -730,22 +753,48 @@ static void dasdfmt_prepare_and_format (
 static void do_format_dasd(dasdfmt_info_t *info, format_data_t *p, 
 			   volume_label_t *vlabel)
 {
-	struct hd_geometry drive_geo;
 	char               inp_buffer[5];
-
-
+	dasd_information_t  dasd_info;
+	struct dasd_eckd_characteristics *characteristics;
+	unsigned int cylinders, heads;
 
 	if (info->verbosity > 0) printf("Retrieving disk geometry...\n");
 
-	if (ioctl(filedes, HDIO_GETGEO, &drive_geo) != 0)
-		ERRMSG_EXIT(EXIT_FAILURE, "%s: the ioctl call to get geometry "
-			    "returned with the following error message:\n%s\n",
+	if (ioctl(filedes, BIODASDINFO, &dasd_info) != 0)
+		ERRMSG_EXIT(EXIT_FAILURE, "%s: (retrieving disk information) "
+			    "IOCTL BIODASDINFO failed (%s).\n",
 			    prog_name, strerror(errno));
 
+	characteristics =
+		(struct dasd_eckd_characteristics *) &dasd_info.characteristics;
+        if (characteristics->no_cyl == LV_COMPAT_CYL &&
+	    characteristics->long_no_cyl)
+		cylinders = characteristics->long_no_cyl;
+	else
+		cylinders = characteristics->no_cyl;
+	heads = characteristics->trk_per_cyl;
+
 	p->start_unit = 0;
-	p->stop_unit  = (drive_geo.cylinders * drive_geo.heads) - 1;
-			
-        if (!info->writenolabel) {
+	p->stop_unit  = (cylinders * heads) - 1;
+
+        if (info->writenolabel) {
+		if (cylinders > LV_COMPAT_CYL && !info->withoutprompt) {
+			printf("\n--->> ATTENTION! <<---\n");
+			printf("You specified to write no labels to a"
+			       " volume with more then %u cylinders.\n"
+			       "Cylinders above this limit will not be"
+			       " accessable as a linux partition!\n"
+			       "Type \"yes\" to continue, no will leave"
+			       " the disk untouched: ", LV_COMPAT_CYL);
+			fgets(inp_buffer, sizeof(inp_buffer), stdin);
+			if (strcasecmp(inp_buffer,"yes") &&
+			    strcasecmp(inp_buffer,"yes\n")) {
+				printf("Omitting ioctl call (disk will "
+					"NOT be formatted).\n");
+				return;
+			}
+		}
+	} else {
         	if (!info->labelspec && !info->keep_volser) {
 			char buf[7];
 
@@ -759,13 +808,12 @@ static void do_format_dasd(dasdfmt_info_
 			vtoc_volume_label_set_label(vlabel, "VOL1");
 			vtoc_volume_label_set_key(vlabel, "VOL1");
                         vtoc_set_cchhb(&vlabel->vtoc, 0x0000, 0x0001, 0x01);
-                } 
-		else 
+                } else
 			vtoc_volume_label_set_label(vlabel, "LNX1");
        	}
 
 	if ((info->verbosity > 0) || (!info->withoutprompt)) 
-		dasdfmt_print_info(info, vlabel, &drive_geo, p);
+		dasdfmt_print_info(info, vlabel, cylinders, heads, p);
 
 	if (!info->testmode) {
 		if (!info->withoutprompt) {
@@ -786,12 +834,12 @@ static void do_format_dasd(dasdfmt_info_
 			printf("Formatting the device. This may take a "
 			       "while (get yourself a coffee).\n");
 		
-		dasdfmt_prepare_and_format(info, &drive_geo, p);
+		dasdfmt_prepare_and_format(info, cylinders, heads, p);
 
 		if(!info->yast_mode) printf("Finished formatting the device.\n");
 
-		if (!info->writenolabel) 
-			dasdfmt_write_labels(info, vlabel);
+		if (!info->writenolabel)
+			dasdfmt_write_labels(info, vlabel, cylinders, heads);
 
 		if(!info->yast_mode) printf("Rereading the partition table... ");
 		if (reread_partition_table()) {
Index: s390-tools-sles11/dasdfmt/dasdfmt.h
===================================================================
--- s390-tools-sles11.orig/dasdfmt/dasdfmt.h
+++ s390-tools-sles11/dasdfmt/dasdfmt.h
@@ -31,10 +31,10 @@
 
 #define DASD_IOCTL_LETTER 'D'
 
-/* 
+/*
  * struct dasd_information_t
  * represents any data about the device, which is visible to userspace.
- *  including foramt and featueres.
+ *  including format and features.
  */
 typedef struct dasd_information_t {
         unsigned int devno;           /* S/390 devno                        */
@@ -58,15 +58,91 @@ typedef struct dasd_information_t {
         char configuration_data[256]; /* from read_configuration_data       */
 } dasd_information_t;
 
+
+struct dasd_eckd_characteristics {
+	unsigned short cu_type;
+	struct {
+		unsigned char support:2;
+		unsigned char async:1;
+		unsigned char reserved:1;
+		unsigned char cache_info:1;
+		unsigned char model:3;
+	} __attribute__ ((packed)) cu_model;
+	unsigned short dev_type;
+	unsigned char dev_model;
+	struct {
+		unsigned char mult_burst:1;
+		unsigned char RT_in_LR:1;
+		unsigned char reserved1:1;
+		unsigned char RD_IN_LR:1;
+		unsigned char reserved2:4;
+		unsigned char reserved3:8;
+		unsigned char defect_wr:1;
+		unsigned char XRC_supported:1;
+		unsigned char reserved4:1;
+		unsigned char striping:1;
+		unsigned char reserved5:4;
+		unsigned char cfw:1;
+		unsigned char reserved6:2;
+		unsigned char cache:1;
+		unsigned char dual_copy:1;
+		unsigned char dfw:1;
+		unsigned char reset_alleg:1;
+		unsigned char sense_down:1;
+	} __attribute__ ((packed)) facilities;
+	unsigned char dev_class;
+	unsigned char unit_type;
+	unsigned short no_cyl;
+	unsigned short trk_per_cyl;
+	unsigned char sec_per_trk;
+	unsigned char byte_per_track[3];
+	unsigned short home_bytes;
+	unsigned char formula;
+	union {
+		struct {
+			unsigned char f1;
+			unsigned short f2;
+			unsigned short f3;
+		} __attribute__ ((packed)) f_0x01;
+		struct {
+			unsigned char f1;
+			unsigned char f2;
+			unsigned char f3;
+			unsigned char f4;
+			unsigned char f5;
+		} __attribute__ ((packed)) f_0x02;
+	} __attribute__ ((packed)) factors;
+	unsigned short first_alt_trk;
+	unsigned short no_alt_trk;
+	unsigned short first_dia_trk;
+	unsigned short no_dia_trk;
+	unsigned short first_sup_trk;
+	unsigned short no_sup_trk;
+	unsigned char MDR_ID;
+	unsigned char OBR_ID;
+	unsigned char director;
+	unsigned char rd_trk_set;
+	unsigned short max_rec_zero;
+	unsigned char reserved1;
+	unsigned char RWANY_in_LR;
+	unsigned char factor6;
+	unsigned char factor7;
+	unsigned char factor8;
+	unsigned char reserved2[3];
+	unsigned char reserved3[6];
+	unsigned int long_no_cyl;
+} __attribute__ ((packed));
+
+
 /* 
  * struct format_data_t
  * represents all data necessary to format a dasd
  */
 typedef struct format_data_t {
-	int start_unit; /* from track */
-	int stop_unit;  /* to track */
-	int blksize;    /* sectorsize */
-        int intensity;  
+	unsigned int start_unit; /* from track */
+	unsigned int stop_unit;  /* to track */
+	unsigned int blksize;    /* sectorsize */
+        unsigned int intensity;
 } format_data_t;
 
 /*
@@ -95,7 +171,7 @@ typedef struct format_data_t {
 #define BIODASDFMT     _IOW(DASD_IOCTL_LETTER,1,format_data_t) 
 
 /****************************************************************************
- * SECTION: Further IOCTL Definitions  (see fs.h)                           *
+ * SECTION: Further IOCTL Definitions  (see fs.h and hdreq.h )              *
  ****************************************************************************/
 /* re-read partition table */
 #define BLKRRPART  _IO(0x12,95)	
@@ -104,17 +180,6 @@ typedef struct format_data_t {
 /* get read-only status (0 = read_write) */
 #define BLKROGET   _IO(0x12,94)
 
-/****************************************************************************
- * SECTION: Definition from hdreq.h                                         *
- ****************************************************************************/
-
-struct hd_geometry {
-      unsigned char heads;
-      unsigned char sectors;
-      unsigned short cylinders;
-      unsigned long start;
-};
-
 /* get device geometry */
 #define HDIO_GETGEO		0x0301	
 
@@ -172,12 +237,6 @@ static struct option dasdfmt_getopt_long
         {0, 0, 0, 0}
 };
 
-static int            filedes        = 0;
-static int            disk_disabled  = 0;
-static format_data_t  format_params;
-char                 *prog_name;
-volatile sig_atomic_t program_interrupt_in_progress = 0;
-
 typedef struct bootstrap1 {
         u_int32_t key;
         u_int32_t data[6];
Index: s390-tools-sles11/dasdview/dasdview.8
===================================================================
--- s390-tools-sles11.orig/dasdview/dasdview.8
+++ s390-tools-sles11/dasdview/dasdview.8
@@ -7,9 +7,9 @@ to the console.
 .br
          [-b \fIbegin\fR] [-s \fIsize\fR] [-1|-2]
 .br
-         [-i] [-x] [-j]
+         [-i] [-x] [-j] [-c]
 .br
-         [-l] [-t {\fIinfo\fR|\fIf1\fR|\fIf4\fR|\fIf5\fR|\fIf7\fR}]
+         [-l] [-t {\fIinfo\fR|\fIf1\fR|\fIf4\fR|\fIf5\fR|\fIf7\fR|\fIf8\fR|\fIf9\fR}]
 .br
          {-n \fIdevno\fR|-f \fInode\fR} \fIdevice\fR
 .SH DESCRIPTION
@@ -133,6 +133,10 @@ Print volume serial number (volume ident
 Print the volume label.
 
 .TP
+\fB-c\fR or \fB--characteristic\fR
+Print some information about the device e.g. if it is encrypted.
+
+.TP
 \fB-t\fR \fIspec\fR or \fB--vtoc=\fR\fIspec\fR
 Print the VTOC (table of content) or single VTOC entries to the console.
 \fIspec\fR can be one of the following strings:
@@ -164,6 +168,16 @@ Print the content of the format 5 DSCB.
 Print the content of the format 7 DSCB.
 .br
 
+\fIf8\fR:
+.br
+Print the content of all format 8 DSCBs.
+.br
+
+\fIf9\fR:
+.br
+Print the content of all format 9 DSCBs.
+.br
+
 \fIall\fR: 
 .br
 Print the content of all DSCBs. 
Index: s390-tools-sles11/dasdview/dasdview.c
===================================================================
--- s390-tools-sles11.orig/dasdview/dasdview.c
+++ s390-tools-sles11/dasdview/dasdview.c
@@ -74,7 +74,7 @@ dasdview_usage(void)
 	printf("\nprints DASD information:\n\n");
 	printf("dasdview [-b begin] [-s size] [-1|-2] \n"
 	       "         [-i] [-x] [-j]\n"
-	       "         [-l] [-t {info|f1|f4|f5|f7|all}] \n"
+	       "         [-l] [-t {info|f1|f4|f5|f7|f8|f9|all}] \n"
 	       "         [-h] [-v] \n"
 	       "         {-n devno|-f node}\n"
 	       "\nwhere:\n"
@@ -91,6 +91,7 @@ dasdview_usage(void)
 	       "-j: prints the volume serial number (volume identifier)\n"
 	       "-l: prints information about the volume label\n"
 	       "-t: prints the table of content (VTOC)\n"
+	       "-c: prints the characteristics of a device\n"
 	       "-h: prints this usage text\n"
 	       "-v: prints the version number\n"
 	       "-n: specifies the device by a device number (needs devfs)\n"
@@ -168,6 +169,7 @@ static void
 dasdview_get_info(dasdview_info_t *info)
 {
 	int fd;
+	u_int64_t device_size;
 
 	fd = open(info->device, O_RDONLY);
 	if (fd == -1)
@@ -199,6 +201,16 @@ dasdview_get_info(dasdview_info_t *info)
 		exit(-1);
 	}
 
+	if (ioctl(fd, BLKGETSIZE64, &device_size) != 0) {
+	        close(fd);
+		zt_error_print("dasdview: ioctl error\n" \
+			"Could not retrieve device size information.\n");
+		exit(-1);
+	}
+
+	info->hw_cylinders = ((device_size / info->blksize)
+			      / info->geo.sectors) / info->geo.heads;
+
 	/* get disk information */
 	if (ioctl(fd, BIODASDINFO2, &info->dasd_info) == 0) {
 		info->dasd_info_version = 2;
@@ -308,8 +320,8 @@ dasdview_print_general_info(dasdview_inf
 	printf("\n--- DASD geometry ----------------------------" \
 	       "---------------------------------\n");
 	printf("number of cylinders    : hex %x  \tdec %d\n",
-	       info->geo.cylinders,
-	       info->geo.cylinders);
+	       info->hw_cylinders,
+	       info->hw_cylinders);
 	printf("tracks per cylinder    : hex %x  \tdec %d\n",
 	       info->geo.heads,
 	       info->geo.heads);
@@ -490,7 +502,8 @@ dasdview_print_vlabel(dasdview_info_t *i
 	else
 		printf("\n                                 " \
 		       "(cyl %d, trk %d, blk %d)\n\n",
-		       vlabel.vtoc.cc, vlabel.vtoc.hh, vlabel.vtoc.b);
+		       vtoc_get_cyl_from_cchhb(&vlabel.vtoc),
+		       vtoc_get_head_from_cchhb(&vlabel.vtoc), vlabel.vtoc.b);
 
 	bzero(s5, 6); bzero(t5, 6); strncpy((char *)s5, vlabel.res1, 5);
 	printf("reserved                : ascii  '%5s'\n", s5);
@@ -539,13 +552,13 @@ dasdview_print_vlabel(dasdview_info_t *i
 		if ((i+1)%8 == 0) printf(" ");
 	}
 
-	bzero(s29, 30); strncpy(s29, vlabel.res3, 29);
-	printf("\n\nreserved                : ascii  '%29s'\n", s29);
+	bzero(s29, 30); strncpy(s29, vlabel.res3, 28);
+	printf("\n\nreserved                : ascii  '%28s'\n", s29);
 	bzero(t29, 30);
-	vtoc_ebcdic_dec(s29, t29, 29);
-	printf("                          ebcdic '%29s'\n", t29);
+	vtoc_ebcdic_dec(s29, t29, 28);
+	printf("                          ebcdic '%28s'\n", t29);
 	printf("                          hex    ");
-	for (i=0; i<29; i++)
+	for (i=0; i<28; i++)
 	{
 		printf("%02x", s29[i]);
 		if ((i+1)%4 == 0) printf(" ");
@@ -553,6 +566,18 @@ dasdview_print_vlabel(dasdview_info_t *i
 		if ((i+1)%16 == 0) printf("\n                " \
 					  "                 ");
 	}
+
+	bzero(s4, 5); bzero(t4, 5); s4[0] = vlabel.ldl_version;
+	printf("\n\nldl_version             : ascii  '%1s'\n", s4);
+	vtoc_ebcdic_dec((char *)s4, (char *)t4, 1);
+	printf("                        : ebcdic '%1s'\n", t4);
+	printf("                        : hex    %02x", s4[0]);
+
+	printf("\n\nformatted_blocks        : dec %llu",
+	       vlabel.formatted_blocks);
+	printf("\n                        : hex %016llx",
+	       vlabel.formatted_blocks);
+
 	printf("\n");
 }
 
@@ -586,7 +611,8 @@ dasdview_read_vtoc(dasdview_info_t *info
 {
         volume_label_t vlabel;
 	format1_label_t tmp;
-	unsigned long pos, vtocblk, maxblk;
+	unsigned long maxblk, pos;
+	u_int64_t vtocblk;
 	int i;
 
 	pos = info->dasd_info.label_block * info->blksize;
@@ -606,10 +632,18 @@ dasdview_read_vtoc(dasdview_info_t *info
 		exit(-1);
 	}
 
-	vtocblk = vlabel.vtoc.cc * info->geo.heads +
-		vlabel.vtoc.hh * info->geo.sectors +
-		vlabel.vtoc.b;
-
+	vtocblk = (u_int64_t) vtoc_get_cyl_from_cchhb(&vlabel.vtoc) *
+		info->geo.heads * info->geo.sectors +
+                vtoc_get_head_from_cchhb(&vlabel.vtoc) * info->geo.sectors +
+                vlabel.vtoc.b;
+
+	/*
+	 * geo.cylinders is the minimum of hw_cylinders and LV_COMPAT_CYL
+	 * Actually the vtoc should be located in in the first 65k-1 tracks
+         * so this check could be even more restrictive, but it doesn't
+         * hurt the way it is. Linux cdl format restricts the vtoc to
+         * the first two tracks anyway.
+	 */
 	maxblk = info->geo.cylinders * info->geo.heads * info->geo.sectors;
 
 	if ((vtocblk <= 0) || (vtocblk > maxblk))
@@ -640,28 +674,37 @@ dasdview_read_vtoc(dasdview_info_t *info
 	        pos += info->blksize;
 	        vtoc_read_label(info->device, pos, &tmp, NULL, NULL, NULL);
 
-		switch (tmp.DS1FMTID)
-		{
-		  case 0xf1:
-		          memcpy(&info->f1[info->f1c], &tmp,
-				 sizeof(format1_label_t));
-		          info->f1c++;
-		          break;
-		  case 0xf4:
-		          info->f4c++;
-		          break;
-		  case 0xf5:
-		          memcpy(&info->f5, &tmp, sizeof(format1_label_t));
-		          info->f5c++;
-		          break;
-		  case 0xf7:
-		          memcpy(&info->f7, &tmp, sizeof(format1_label_t));
-		          info->f7c++;
-		          break;
-		  case 0x00:
-		          break;
-		  default:
-			  printf("Unknown label in VTOC detected (id=%x)\n",
+		switch (tmp.DS1FMTID) {
+		case 0xf1:
+			memcpy(&info->f1[info->f1c], &tmp,
+			       sizeof(format1_label_t));
+			info->f1c++;
+			break;
+		case 0xf4:
+			info->f4c++;
+			break;
+		case 0xf5:
+			memcpy(&info->f5, &tmp, sizeof(format1_label_t));
+			info->f5c++;
+			break;
+		case 0xf7:
+			memcpy(&info->f7, &tmp, sizeof(format1_label_t));
+			info->f7c++;
+			break;
+		case 0xf8:
+			memcpy(&info->f8[info->f8c], &tmp,
+			       sizeof(format1_label_t));
+			info->f8c++;
+			break;
+		case 0xf9:
+			memcpy(&info->f9[info->f9c], &tmp,
+			       sizeof(format1_label_t));
+			info->f9c++;
+			break;
+		case 0x00:
+			break;
+		default:
+			printf("Unknown label in VTOC detected (id=%x)\n",
 				 tmp.DS1FMTID);
 		}
 	}
@@ -688,13 +731,49 @@ dasdview_read_vtoc(dasdview_info_t *info
 	}
 }
 
+static void dasdview_print_format1_8_short_info(format1_label_t *f1,
+						struct hd_geometry *geo)
+{
+	char s6[7], s13[14], s44[45];
+	unsigned long track_low , track_up;
 
-static void
-dasdview_print_vtoc_info(dasdview_info_t *info)
+	bzero(s44, 45);
+	strncpy(s44, f1->DS1DSNAM, 44);
+	vtoc_ebcdic_dec(s44, s44, 44);
+	bzero(s6, 7);
+	strncpy(s6, (char *)f1->DS1DSSN, 6);
+	vtoc_ebcdic_dec(s6, s6, 6);
+	bzero(s13, 14);
+	strncpy(s13, (char *)f1->DS1SYSCD, 13);
+	vtoc_ebcdic_dec(s13, s13, 13);
+
+	track_low = cchh2trk(&f1->DS1EXT1.llimit, geo);
+	track_up = cchh2trk(&f1->DS1EXT1.ulimit, geo);
+
+	printf(" | %44s |          trk |          trk |\n",
+	       s44);
+	printf(" | data set serial number :"	     \
+	       " '%6s'            |"					\
+	       " %12ld | %12ld |\n", s6, track_low, track_up);
+	printf(" | system code            :"   \
+	       " '%13s'     |"						\
+	       "      cyl/trk |      cyl/trk |\n", s13);
+	printf(" | creation date          :"	       \
+	       "  year %4d, day %3d |"				\
+	       " %8d/%3d | %8d/%3d |\n",
+	       f1->DS1CREDT.year + 1900,
+	       f1->DS1CREDT.day,
+	       vtoc_get_cyl_from_cchh(&f1->DS1EXT1.llimit),
+	       vtoc_get_head_from_cchh(&f1->DS1EXT1.llimit),
+	       vtoc_get_cyl_from_cchh(&f1->DS1EXT1.ulimit),
+	       vtoc_get_head_from_cchh(&f1->DS1EXT1.ulimit));
+	printf(" +-----------------------------------------"		\
+	       "-----+--------------+--------------+\n");
+}
+
+static void dasdview_print_vtoc_info(dasdview_info_t *info)
 {
         int i;
-	char s6[7], s13[14], s44[45];
-	unsigned long l1 ,l2;
 
 	printf("--- VTOC info --------------------------------" \
 	       "---------------------------------\n");
@@ -703,9 +782,10 @@ dasdview_print_vtoc_info(dasdview_info_t
 	printf("  %d format 4 label(s)\n", info->f4c);
 	printf("  %d format 5 label(s)\n", info->f5c);
 	printf("  %d format 7 label(s)\n", info->f7c);
+	printf("  %d format 8 label(s)\n", info->f8c);
+	printf("  %d format 9 label(s)\n", info->f9c);
 
-	if (info->f1c < 1)
-	{
+	if ((info->f1c < 1) && (info->f8c < 1))	{
 	        printf("There are no partitions defined.\n");
 	} else {
 	        printf("Other S/390 and zSeries operating systems would see " \
@@ -718,59 +798,192 @@ dasdview_print_vtoc_info(dasdview_info_t
 		       "--------------+--------------+\n");
 
 		for (i=0; i<info->f1c; i++)
-		{
-		        bzero(s44, 45);
-			strncpy(s44, info->f1[i].DS1DSNAM, 44);
-			vtoc_ebcdic_dec(s44, s44, 44);
-		        bzero(s6, 7);
-			strncpy(s6, (char *)info->f1[i].DS1DSSN, 6);
-			vtoc_ebcdic_dec(s6, s6, 6);
-		        bzero(s13, 14);
-			strncpy(s13, (char *)info->f1[i].DS1SYSCD, 13);
-			vtoc_ebcdic_dec(s13, s13, 13);
-
-			l1 = (unsigned long) info->f1[i].DS1EXT1.llimit.cc *
-			     (unsigned long) info->geo.heads +
-			     (unsigned long) info->f1[i].DS1EXT1.llimit.hh;
-			l2 = (unsigned long) info->f1[i].DS1EXT1.ulimit.cc *
-			     (unsigned long) info->geo.heads +
-			     (unsigned long) info->f1[i].DS1EXT1.ulimit.hh;
-
-			printf(" | %44s |          trk |          trk |\n",
-			       s44);
-			printf(" | data set serial number :" \
-			       " '%6s'            |" \
-			       " %12ld | %12ld |\n", s6, l1, l2);
-			printf(" | system code            :" \
-			       " '%13s'     |" \
-			       "      cyl/trk |      cyl/trk |\n", s13);
-			printf(" | creation date          :" \
-			       "  year %4d, day %3d |" \
-			       " %8d/%3d | %8d/%3d |\n",
-			       info->f1[i].DS1CREDT.year + 1900,
-			       info->f1[i].DS1CREDT.day,
-			       info->f1[i].DS1EXT1.llimit.cc,
-			       info->f1[i].DS1EXT1.llimit.hh,
-			       info->f1[i].DS1EXT1.ulimit.cc,
-			       info->f1[i].DS1EXT1.ulimit.hh);
-			printf(" +-----------------------------------------" \
-			       "-----+--------------+--------------+\n");
-		}
+			dasdview_print_format1_8_short_info(&info->f1[i],
+							    &info->geo);
+		for (i=0; i<info->f8c; i++)
+			dasdview_print_format1_8_short_info(&info->f8[i],
+							    &info->geo);
 	}
-
 }
 
+/*
+ * Note: the explicit cylinder/head conversion for large volume
+ * adresses should not be necessary for entries that point to
+ * vtoc labels, as those must be located in the first 65K-1 tracks,
+ * but we do it anyway to be on the safe side.
+ */
 
-static void
-dasdview_print_vtoc_f1(dasdview_info_t *info)
+static void dasdview_print_format1_8_full(format1_label_t *f1)
 {
-        int i, j;
 	char s6[7], s13[14], s44[45];
+	int i;
 
 	bzero(s6, 7);
 	bzero(s13, 14);
 	bzero(s44, 45);
 
+	strncpy(s44, f1->DS1DSNAM, 44);
+	printf("DS1DSNAM    : ascii  '%44s'\n", s44);
+	vtoc_ebcdic_dec(s44, s44, 44);
+	printf("              ebcdic '%44s'\n", s44);
+	printf("DS1FMTID    : dec %d, hex %02x\n",
+	       f1->DS1FMTID, f1->DS1FMTID);
+	printf("DS1DSSN     : hex    ");
+	for (i=0; i<6; i++) printf("%02x", f1->DS1DSSN[i]);
+	strncpy(s6, (char *)f1->DS1DSSN, 6);
+	printf("\n              ascii  '%6s'\n", s6);
+	vtoc_ebcdic_dec(s6, s6, 6);
+	printf("              ebcdic '%6s'\n", s6);
+	printf("DS1VOLSQ    : dec %d, hex %04x\n",
+	       f1->DS1VOLSQ, f1->DS1VOLSQ);
+	printf("DS1CREDT    : hex %02x%04x "	\
+	       "(year %d, day %d)\n",
+	       f1->DS1CREDT.year, f1->DS1CREDT.day,
+	       f1->DS1CREDT.year + 1900,
+	       f1->DS1CREDT.day);
+	printf("DS1EXPDT    : hex %02x%04x "	\
+	       "(year %d, day %d)\n",
+	       f1->DS1EXPDT.year, f1->DS1EXPDT.day,
+	       f1->DS1EXPDT.year + 1900,
+	       f1->DS1EXPDT.day);
+	printf("DS1NOEPV    : dec %d, hex %02x\n",
+	       f1->DS1NOEPV, f1->DS1NOEPV);
+	printf("DS1NOBDB    : dec %d, hex %02x\n",
+	       f1->DS1NOBDB, f1->DS1NOBDB);
+	printf("DS1FLAG1    : dec %d, hex %02x\n",
+	       f1->DS1FLAG1, f1->DS1FLAG1);
+	printf("DS1SYSCD    : hex    ");
+	for (i=0; i<13; i++) printf("%02x", f1->DS1SYSCD[i]);
+	strncpy(s13, (char *)f1->DS1SYSCD, 13);
+	printf("\n              ascii  '%13s'\n", s13);
+	vtoc_ebcdic_dec(s13, s13, 13);
+	printf("              ebcdic '%13s'\n", s13);
+	printf("DS1REFD     : hex %02x%04x "	\
+	       "(year %d, day %d)\n",
+	       f1->DS1REFD.year, f1->DS1REFD.day,
+	       f1->DS1REFD.year + 1900,
+	       f1->DS1REFD.day);
+	printf("DS1SMSFG    : dec %d, hex %02x\n",
+	       f1->DS1SMSFG, f1->DS1SMSFG);
+	printf("DS1SCXTF    : dec %d, hex %02x\n",
+	       f1->DS1SCXTF, f1->DS1SCXTF);
+	printf("DS1SCXTV    : dec %d, hex %04x\n",
+	       f1->DS1SCXTV, f1->DS1SCXTV);
+	printf("DS1DSRG1    : dec %d, hex %02x\n",
+	       f1->DS1DSRG1, f1->DS1DSRG1);
+	printf("DS1DSRG2    : dec %d, hex %02x\n",
+	       f1->DS1DSRG2, f1->DS1DSRG2);
+	printf("DS1RECFM    : dec %d, hex %02x\n",
+	       f1->DS1RECFM, f1->DS1RECFM);
+	printf("DS1OPTCD    : dec %d, hex %02x\n",
+	       f1->DS1OPTCD, f1->DS1OPTCD);
+	printf("DS1BLKL     : dec %d, hex %04x\n",
+	       f1->DS1BLKL, f1->DS1BLKL);
+	printf("DS1LRECL    : dec %d, hex %04x\n",
+	       f1->DS1LRECL, f1->DS1LRECL);
+	printf("DS1KEYL     : dec %d, hex %02x\n",
+	       f1->DS1KEYL, f1->DS1KEYL);
+	printf("DS1RKP      : dec %d, hex %04x\n",
+	       f1->DS1RKP, f1->DS1RKP);
+	printf("DS1DSIND    : dec %d, hex %02x\n",
+	       f1->DS1DSIND, f1->DS1DSIND);
+	printf("DS1SCAL1    : dec %d, hex %02x\n",
+	       f1->DS1SCAL1, f1->DS1SCAL1);
+	printf("DS1SCAL3    : hex ");
+	for (i=0; i<3; i++) printf("%02x", f1->DS1SCAL3[i]);
+	printf("\nDS1LSTAR    : hex %04x%02x "	\
+	       "(trk %d, blk %d)\n",
+	       f1->DS1LSTAR.tt, f1->DS1LSTAR.r,
+	       f1->DS1LSTAR.tt, f1->DS1LSTAR.r);
+	printf("DS1TRBAL    : dec %d, hex %04x\n",
+	       f1->DS1TRBAL, f1->DS1TRBAL);
+	printf("reserved    : dec %d, hex %04x\n",
+	       f1->res1, f1->res1);
+	printf("DS1EXT1     : hex %02x%02x%04x%04x%04x%04x\n",
+	       f1->DS1EXT1.typeind,
+	       f1->DS1EXT1.seqno,
+	       f1->DS1EXT1.llimit.cc,
+	       f1->DS1EXT1.llimit.hh,
+	       f1->DS1EXT1.ulimit.cc,
+	       f1->DS1EXT1.ulimit.hh);
+	printf("              typeind    : dec %d, hex %02x\n",
+	       f1->DS1EXT1.typeind,
+	       f1->DS1EXT1.typeind);
+	printf("              seqno      : dec %d, hex %02x\n",
+	       f1->DS1EXT1.seqno, f1->DS1EXT1.seqno);
+	printf("              llimit     : hex %04x%04x "	\
+	       "(cyl %d, trk %d)\n",
+	       f1->DS1EXT1.llimit.cc,
+	       f1->DS1EXT1.llimit.hh,
+	       vtoc_get_cyl_from_cchh(&f1->DS1EXT1.llimit),
+	       vtoc_get_head_from_cchh(&f1->DS1EXT1.llimit));
+	printf("              ulimit     : hex %04x%04x "	\
+	       "(cyl %d, trk %d)\n",
+	       f1->DS1EXT1.ulimit.cc,
+	       f1->DS1EXT1.ulimit.hh,
+	       vtoc_get_cyl_from_cchh(&f1->DS1EXT1.ulimit),
+	       vtoc_get_head_from_cchh(&f1->DS1EXT1.ulimit));
+	printf("DS1EXT2     : hex %02x%02x%04x%04x%04x%04x\n",
+	       f1->DS1EXT2.typeind,
+	       f1->DS1EXT2.seqno,
+	       f1->DS1EXT2.llimit.cc,
+	       f1->DS1EXT2.llimit.hh,
+	       f1->DS1EXT2.ulimit.cc,
+	       f1->DS1EXT2.ulimit.hh);
+	printf("              typeind    : dec %d, hex %02x\n",
+	       f1->DS1EXT2.typeind,
+	       f1->DS1EXT2.typeind);
+	printf("              seqno      : dec %d, hex %02x\n",
+	       f1->DS1EXT2.seqno, f1->DS1EXT2.seqno);
+	printf("              llimit     : hex %04x%04x "	\
+	       "(cyl %d, trk %d)\n",
+	       f1->DS1EXT2.llimit.cc,
+	       f1->DS1EXT2.llimit.hh,
+	       vtoc_get_cyl_from_cchh(&f1->DS1EXT2.llimit),
+	       vtoc_get_head_from_cchh(&f1->DS1EXT2.llimit));
+	printf("              ulimit     : hex %04x%04x "	\
+	       "(cyl %d, trk %d)\n",
+	       f1->DS1EXT2.ulimit.cc,
+	       f1->DS1EXT2.ulimit.hh,
+	       vtoc_get_cyl_from_cchh(&f1->DS1EXT2.ulimit),
+	       vtoc_get_head_from_cchh(&f1->DS1EXT2.ulimit));
+	printf("DS1EXT3     : hex %02x%02x%04x%04x%04x%04x\n",
+	       f1->DS1EXT3.typeind,
+	       f1->DS1EXT3.seqno,
+	       f1->DS1EXT3.llimit.cc,
+	       f1->DS1EXT3.llimit.hh,
+	       f1->DS1EXT3.ulimit.cc,
+	       f1->DS1EXT3.ulimit.hh);
+	printf("              typeind    : dec %d, hex %02x\n",
+	       f1->DS1EXT3.typeind,
+	       f1->DS1EXT3.typeind);
+	printf("              seqno      : dec %d, hex %02x\n",
+	       f1->DS1EXT3.seqno, f1->DS1EXT3.seqno);
+	printf("              llimit     : hex %04x%04x "	\
+	       "(cyl %d, trk %d)\n",
+	       f1->DS1EXT3.llimit.cc,
+	       f1->DS1EXT3.llimit.hh,
+	       vtoc_get_cyl_from_cchh(&f1->DS1EXT3.llimit),
+	       vtoc_get_head_from_cchh(&f1->DS1EXT3.llimit));
+	printf("              ulimit     : hex %04x%04x "	\
+	       "(cyl %d, trk %d)\n",
+	       f1->DS1EXT3.ulimit.cc,
+	       f1->DS1EXT3.ulimit.hh,
+	       vtoc_get_cyl_from_cchh(&f1->DS1EXT3.ulimit),
+	       vtoc_get_head_from_cchh(&f1->DS1EXT3.ulimit));
+	printf("DS1PTRDS    : %04x%04x%02x "		\
+	       "(cyl %d, trk %d, blk %d)\n",
+	       f1->DS1PTRDS.cc, f1->DS1PTRDS.hh,
+	       f1->DS1PTRDS.b,
+	       vtoc_get_cyl_from_cchhb(&f1->DS1PTRDS),
+	       vtoc_get_head_from_cchhb(&f1->DS1PTRDS),
+	       f1->DS1PTRDS.b);
+}
+
+static void dasdview_print_vtoc_f1(dasdview_info_t *info)
+{
+        int j;
+
 	printf("--- VTOC format 1 labels ----------------------" \
 	       "---------------------------------\n");
 
@@ -780,163 +993,28 @@ dasdview_print_vtoc_f1(dasdview_info_t *
 		return;
 	}
 
-	for (j=0; j<info->f1c; j++)
+	for (j=0; j<info->f1c; j++) {
+		printf("\n--- format 1 DSCB number %d ---\n", j+1);
+		dasdview_print_format1_8_full(&info->f1[j]);
+	}
+}
+
+static void dasdview_print_vtoc_f8(dasdview_info_t *info)
+{
+        int j;
+
+	printf("--- VTOC format 8 labels ----------------------" \
+	       "---------------------------------\n");
+
+	if (info->f8c < 1)
 	{
-	        printf("\n--- format 1 DSCB number %d ---\n", j+1);
-		strncpy(s44, info->f1[j].DS1DSNAM, 44);
-		printf("DS1DSNAM    : ascii  '%44s'\n", s44);
-		vtoc_ebcdic_dec(s44, s44, 44);
-		printf("              ebcdic '%44s'\n", s44);
-		printf("DS1FMTID    : dec %d, hex %02x\n",
-		       info->f1[j].DS1FMTID, info->f1[j].DS1FMTID);
-		printf("DS1DSSN     : hex    ");
-		for (i=0; i<6; i++) printf("%02x", info->f1[j].DS1DSSN[i]);
-		strncpy(s6, (char *)info->f1[j].DS1DSSN, 6);
-		printf("\n              ascii  '%6s'\n", s6);
-		vtoc_ebcdic_dec(s6, s6, 6);
-		printf("              ebcdic '%6s'\n", s6);
-		printf("DS1VOLSQ    : dec %d, hex %04x\n",
-		       info->f1[j].DS1VOLSQ, info->f1[j].DS1VOLSQ);
-		printf("DS1CREDT    : hex %02x%04x " \
-		       "(year %d, day %d)\n",
-		       info->f1[j].DS1CREDT.year, info->f1[j].DS1CREDT.day,
-		       info->f1[j].DS1CREDT.year + 1900,
-		       info->f1[j].DS1CREDT.day);
-		printf("DS1EXPDT    : hex %02x%04x " \
-		       "(year %d, day %d)\n",
-		       info->f1[j].DS1EXPDT.year, info->f1[j].DS1EXPDT.day,
-		       info->f1[j].DS1EXPDT.year + 1900,
-		       info->f1[j].DS1EXPDT.day);
-		printf("DS1NOEPV    : dec %d, hex %02x\n",
-		       info->f1[j].DS1NOEPV, info->f1[j].DS1NOEPV);
-		printf("DS1NOBDB    : dec %d, hex %02x\n",
-		       info->f1[j].DS1NOBDB, info->f1[j].DS1NOBDB);
-		printf("DS1FLAG1    : dec %d, hex %02x\n",
-		       info->f1[j].DS1FLAG1, info->f1[j].DS1FLAG1);
-		printf("DS1SYSCD    : hex    ");
-		for (i=0; i<13; i++) printf("%02x", info->f1[j].DS1SYSCD[i]);
-		strncpy(s13, (char *)info->f1[j].DS1SYSCD, 13);
-		printf("\n              ascii  '%13s'\n", s13);
-		vtoc_ebcdic_dec(s13, s13, 13);
-		printf("              ebcdic '%13s'\n", s13);
-		printf("DS1REFD     : hex %02x%04x " \
-		       "(year %d, day %d)\n",
-		       info->f1[j].DS1REFD.year, info->f1[j].DS1REFD.day,
-		       info->f1[j].DS1REFD.year + 1900,
-		       info->f1[j].DS1REFD.day);
-		printf("DS1SMSFG    : dec %d, hex %02x\n",
-		       info->f1[j].DS1SMSFG, info->f1[j].DS1SMSFG);
-		printf("DS1SCXTF    : dec %d, hex %02x\n",
-		       info->f1[j].DS1SCXTF, info->f1[j].DS1SCXTF);
-		printf("DS1SCXTV    : dec %d, hex %04x\n",
-		       info->f1[j].DS1SCXTV, info->f1[j].DS1SCXTV);
-		printf("DS1DSRG1    : dec %d, hex %02x\n",
-		       info->f1[j].DS1DSRG1, info->f1[j].DS1DSRG1);
-		printf("DS1DSRG2    : dec %d, hex %02x\n",
-		       info->f1[j].DS1DSRG2, info->f1[j].DS1DSRG2);
-		printf("DS1RECFM    : dec %d, hex %02x\n",
-		       info->f1[j].DS1RECFM, info->f1[j].DS1RECFM);
-		printf("DS1OPTCD    : dec %d, hex %02x\n",
-		       info->f1[j].DS1OPTCD, info->f1[j].DS1OPTCD);
-		printf("DS1BLKL     : dec %d, hex %04x\n",
-		       info->f1[j].DS1BLKL, info->f1[j].DS1BLKL);
-		printf("DS1LRECL    : dec %d, hex %04x\n",
-		       info->f1[j].DS1LRECL, info->f1[j].DS1LRECL);
-		printf("DS1KEYL     : dec %d, hex %02x\n",
-		       info->f1[j].DS1KEYL, info->f1[j].DS1KEYL);
-		printf("DS1RKP      : dec %d, hex %04x\n",
-		       info->f1[j].DS1RKP, info->f1[j].DS1RKP);
-		printf("DS1DSIND    : dec %d, hex %02x\n",
-		       info->f1[j].DS1DSIND, info->f1[j].DS1DSIND);
-		printf("DS1SCAL1    : dec %d, hex %02x\n",
-		       info->f1[j].DS1SCAL1, info->f1[j].DS1SCAL1);
-		printf("DS1SCAL3    : hex ");
-		for (i=0; i<3; i++) printf("%02x", info->f1[j].DS1SCAL3[i]);
-		printf("\nDS1LSTAR    : hex %04x%02x " \
-		       "(trk %d, blk %d)\n",
-		       info->f1[j].DS1LSTAR.tt, info->f1[j].DS1LSTAR.r,
-		       info->f1[j].DS1LSTAR.tt, info->f1[j].DS1LSTAR.r);
-		printf("DS1TRBAL    : dec %d, hex %04x\n",
-		       info->f1[j].DS1TRBAL, info->f1[j].DS1TRBAL);
-		printf("reserved    : dec %d, hex %04x\n",
-		       info->f1[j].res1, info->f1[j].res1);
-		printf("DS1EXT1     : hex %02x%02x%04x%04x%04x%04x\n",
-		       info->f1[j].DS1EXT1.typeind,
-		       info->f1[j].DS1EXT1.seqno,
-		       info->f1[j].DS1EXT1.llimit.cc,
-		       info->f1[j].DS1EXT1.llimit.hh,
-		       info->f1[j].DS1EXT1.ulimit.cc,
-		       info->f1[j].DS1EXT1.ulimit.hh);
-		printf("              typeind    : dec %d, hex %02x\n",
-		       info->f1[j].DS1EXT1.typeind,
-		       info->f1[j].DS1EXT1.typeind);
-		printf("              seqno      : dec %d, hex %02x\n",
-		       info->f1[j].DS1EXT1.seqno, info->f1[j].DS1EXT1.seqno);
-		printf("              llimit     : hex %04x%04x " \
-		       "(cyl %d, trk %d)\n",
-		       info->f1[j].DS1EXT1.llimit.cc,
-		       info->f1[j].DS1EXT1.llimit.hh,
-		       info->f1[j].DS1EXT1.llimit.cc,
-		       info->f1[j].DS1EXT1.llimit.hh);
-		printf("              ulimit     : hex %04x%04x " \
-		       "(cyl %d, trk %d)\n",
-		       info->f1[j].DS1EXT1.ulimit.cc,
-		       info->f1[j].DS1EXT1.ulimit.hh,
-		       info->f1[j].DS1EXT1.ulimit.cc,
-		       info->f1[j].DS1EXT1.ulimit.hh);
-		printf("DS1EXT2     : hex %02x%02x%04x%04x%04x%04x\n",
-		       info->f1[j].DS1EXT2.typeind,
-		       info->f1[j].DS1EXT2.seqno,
-		       info->f1[j].DS1EXT2.llimit.cc,
-		       info->f1[j].DS1EXT2.llimit.hh,
-		       info->f1[j].DS1EXT2.ulimit.cc,
-		       info->f1[j].DS1EXT2.ulimit.hh);
-		printf("              typeind    : dec %d, hex %02x\n",
-		       info->f1[j].DS1EXT2.typeind,
-		       info->f1[j].DS1EXT2.typeind);
-		printf("              seqno      : dec %d, hex %02x\n",
-		       info->f1[j].DS1EXT2.seqno, info->f1[j].DS1EXT2.seqno);
-		printf("              llimit     : hex %04x%04x " \
-		       "(cyl %d, trk %d)\n",
-		       info->f1[j].DS1EXT2.llimit.cc,
-		       info->f1[j].DS1EXT2.llimit.hh,
-		       info->f1[j].DS1EXT2.llimit.cc,
-		       info->f1[j].DS1EXT2.llimit.hh);
-		printf("              ulimit     : hex %04x%04x " \
-		       "(cyl %d, trk %d)\n",
-		       info->f1[j].DS1EXT2.ulimit.cc,
-		       info->f1[j].DS1EXT2.ulimit.hh,
-		       info->f1[j].DS1EXT2.ulimit.cc,
-		       info->f1[j].DS1EXT2.ulimit.hh);
-		printf("DS1EXT3     : hex %02x%02x%04x%04x%04x%04x\n",
-		       info->f1[j].DS1EXT3.typeind,
-		       info->f1[j].DS1EXT3.seqno,
-		       info->f1[j].DS1EXT3.llimit.cc,
-		       info->f1[j].DS1EXT3.llimit.hh,
-		       info->f1[j].DS1EXT3.ulimit.cc,
-		       info->f1[j].DS1EXT3.ulimit.hh);
-		printf("              typeind    : dec %d, hex %02x\n",
-		       info->f1[j].DS1EXT3.typeind,
-		       info->f1[j].DS1EXT3.typeind);
-		printf("              seqno      : dec %d, hex %02x\n",
-		       info->f1[j].DS1EXT3.seqno, info->f1[j].DS1EXT3.seqno);
-		printf("              llimit     : hex %04x%04x " \
-		       "(cyl %d, trk %d)\n",
-		       info->f1[j].DS1EXT3.llimit.cc,
-		       info->f1[j].DS1EXT3.llimit.hh,
-		       info->f1[j].DS1EXT3.llimit.cc,
-		       info->f1[j].DS1EXT3.llimit.hh);
-		printf("              ulimit     : hex %04x%04x " \
-		       "(cyl %d, trk %d)\n",
-		       info->f1[j].DS1EXT3.ulimit.cc,
-		       info->f1[j].DS1EXT3.ulimit.hh,
-		       info->f1[j].DS1EXT3.ulimit.cc,
-		       info->f1[j].DS1EXT3.ulimit.hh);
-		printf("DS1PTRDS    : %04x%04x%02x " \
-		       "(cyl %d, trk %d, blk %d)\n",
-		       info->f1[j].DS1PTRDS.cc, info->f1[j].DS1PTRDS.hh,
-		       info->f1[j].DS1PTRDS.b,  info->f1[j].DS1PTRDS.cc,
-		       info->f1[j].DS1PTRDS.hh, info->f1[j].DS1PTRDS.b);
+	        printf("This VTOC doesn't contain a format 8 label.\n");
+		return;
+	}
+
+	for (j=0; j<info->f8c; j++) {
+		printf("\n--- format 8 DSCB number %d ---\n", j+1);
+		dasdview_print_format1_8_full(&info->f8[j]);
 	}
 }
 
@@ -961,13 +1039,16 @@ dasdview_print_vtoc_f4(dasdview_info_t *
 	printf("DS4HPCHR    : %04x%04x%02x " \
 	       "(cyl %d, trk %d, blk %d)\n",
 	       info->f4.DS4HPCHR.cc, info->f4.DS4HPCHR.hh,
-	       info->f4.DS4HPCHR.b,  info->f4.DS4HPCHR.cc,
-	       info->f4.DS4HPCHR.hh, info->f4.DS4HPCHR.b);
+	       info->f4.DS4HPCHR.b,
+	       vtoc_get_cyl_from_cchhb(&info->f4.DS4HPCHR),
+	       vtoc_get_head_from_cchhb(&info->f4.DS4HPCHR),
+	       info->f4.DS4HPCHR.b);
 	printf("DS4DSREC    : dec %d, hex %04x\n",
 	       info->f4.DS4DSREC, info->f4.DS4DSREC);
 	printf("DS4HCCHH    : %04x%04x (cyl %d, trk %d)\n",
 	       info->f4.DS4HCCHH.cc, info->f4.DS4HCCHH.hh,
-	       info->f4.DS4HCCHH.cc, info->f4.DS4HCCHH.hh);
+	       vtoc_get_cyl_from_cchh(&info->f4.DS4HCCHH),
+	       vtoc_get_head_from_cchh(&info->f4.DS4HCCHH));
 	printf("DS4NOATK    : dec %d, hex %04x\n",
 	       info->f4.DS4NOATK, info->f4.DS4NOATK);
 	printf("DS4VTOCI    : dec %d, hex %02x\n",
@@ -1018,10 +1099,12 @@ dasdview_print_vtoc_f4(dasdview_info_t *
 	       info->f4.DS4VTOCE.seqno, info->f4.DS4VTOCE.seqno);
 	printf("              llimit     : hex %04x%04x (cyl %d, trk %d)\n",
 	       info->f4.DS4VTOCE.llimit.cc, info->f4.DS4VTOCE.llimit.hh,
-	       info->f4.DS4VTOCE.llimit.cc, info->f4.DS4VTOCE.llimit.hh);
+	       vtoc_get_cyl_from_cchh(&info->f4.DS4VTOCE.llimit),
+	       vtoc_get_head_from_cchh(&info->f4.DS4VTOCE.llimit));
 	printf("              ulimit     : hex %04x%04x (cyl %d, trk %d)\n",
 	       info->f4.DS4VTOCE.ulimit.cc, info->f4.DS4VTOCE.ulimit.hh,
-	       info->f4.DS4VTOCE.ulimit.cc, info->f4.DS4VTOCE.ulimit.hh);
+	       vtoc_get_cyl_from_cchh(&info->f4.DS4VTOCE.ulimit),
+	       vtoc_get_head_from_cchh(&info->f4.DS4VTOCE.ulimit));
 	printf("res2        : hex ");
 	for (i=0; i<10; i++) printf("%02x", info->f4.res2[i]);
 	printf("\nDS4EFLVL    : dec %d, hex %02x\n",
@@ -1029,11 +1112,18 @@ dasdview_print_vtoc_f4(dasdview_info_t *
 	printf("DS4EFPTR    : hex %04x%04x%02x " \
 	       "(cyl %d, trk %d, blk %d)\n",
 	       info->f4.DS4EFPTR.cc, info->f4.DS4EFPTR.hh,
-	       info->f4.DS4EFPTR.b,  info->f4.DS4EFPTR.cc,
-	       info->f4.DS4EFPTR.hh, info->f4.DS4EFPTR.b);
-	printf("res3        : hex ");
-	for (i=0; i<9; i++) printf("%02x", info->f4.res3[i]);
-	printf("\n");
+	       info->f4.DS4EFPTR.b,
+	       vtoc_get_cyl_from_cchhb(&info->f4.DS4EFPTR),
+	       vtoc_get_head_from_cchhb(&info->f4.DS4EFPTR),
+	       info->f4.DS4EFPTR.b);
+	printf("res3        : hex %02x\n", info->f4.res3);
+	printf("DS4DCYL     : dec %d, hex %08x\n",
+	       info->f4.DS4DCYL, info->f4.DS4DCYL);
+	printf("res4        : hex ");
+	for (i=0; i<2; i++) printf("%02x", info->f4.res4[i]);
+	printf("\nDS4DEVF2    : dec %d, hex %02x\n",
+	       info->f4.DS4DEVF2, info->f4.DS4DEVF2);
+	printf("res5        : hex %02x\n", info->f4.res5);
 }
 
 static void
@@ -1083,8 +1173,10 @@ dasdview_print_vtoc_f5(dasdview_info_t *
 	       "        DS5PTRDS    : %04x%04x%02x " \
 	       "(cyl %d, trk %d, blk %d)\n",
 	       info->f5.DS5PTRDS.cc, info->f5.DS5PTRDS.hh,
-	       info->f5.DS5PTRDS.b,  info->f5.DS5PTRDS.cc,
-	       info->f5.DS5PTRDS.hh, info->f5.DS5PTRDS.b);
+	       info->f5.DS5PTRDS.b,
+	       vtoc_get_cyl_from_cchhb(&info->f5.DS5PTRDS),
+	       vtoc_get_head_from_cchhb(&info->f5.DS5PTRDS),
+	       info->f5.DS5PTRDS.b);
 }
 
 static void
@@ -1128,10 +1220,59 @@ dasdview_print_vtoc_f7(dasdview_info_t *
 	       "        DS7PTRDS    : %04x%04x%02x " \
 	       "(cyl %d, trk %d, blk %d)\n",
 	       info->f7.DS7PTRDS.cc, info->f7.DS7PTRDS.hh,
-	       info->f7.DS7PTRDS.b, info->f7.DS7PTRDS.cc,
-	       info->f7.DS7PTRDS.hh, info->f7.DS7PTRDS.b);
+	       info->f7.DS7PTRDS.b,
+	       vtoc_get_cyl_from_cchhb(&info->f7.DS7PTRDS),
+	       vtoc_get_head_from_cchhb(&info->f7.DS7PTRDS),
+	       info->f7.DS7PTRDS.b);
 }
 
+static void
+dasdview_print_vtoc_f9(dasdview_info_t *info)
+{
+        int i,j;
+
+	printf("\n--- VTOC format 9 label ----------------------" \
+	       "---------------------------------\n");
+
+	if (info->f9c < 1)
+	{
+	        printf("This VTOC doesn't contain a format 9 label.\n");
+		return;
+	}
+
+	for (j=0; j<info->f9c; j++) {
+		printf("\n--- format 9 DSCB number %d ---\n", j+1);
+		printf("DS9KEYID    : dec %d, hex %02x\n",
+		       info->f9[j].DS9KEYID, info->f9[j].DS9KEYID);
+		printf("DS9SUBTY    : dec %d, hex %02x\n",
+		       info->f9[j].DS9SUBTY, info->f9[j].DS9SUBTY);
+		printf("DS9NUMF9    : dec %d, hex %02x\n",
+		       info->f9[j].DS9NUMF9, info->f9[j].DS9NUMF9);
+
+		printf("res1        : hex ");
+		for (i=0; i < sizeof(info->f9[j].res1); i++) {
+			if ( (i > 0) && (i % 16 == 0) )
+				printf("\n                  ");
+			printf("%02x", info->f9[j].res1[i]);
+			if ( (i+9) % 16 == 0 )
+				printf(" ");
+		}
+		printf("\n");
+
+		printf("DS9FMTID    : dec %d, hex %02x\n",
+		       info->f9[j].DS9FMTID, info->f9[j].DS9FMTID);
+
+		printf("res2        : hex ");
+		for (i=0; i < sizeof(info->f9[j].res2); i++) {
+			if ( (i > 0) && (i % 16 == 0) )
+				printf("\n                  ");
+			printf("%02x", info->f9[j].res2[i]);
+			if ( (i+9) % 16 == 0 )
+				printf(" ");
+		}
+		printf("\n");
+	}
+}
 
 static int
 dasdview_print_format1(unsigned int size, unsigned char *dumpstr)
@@ -1340,6 +1481,15 @@ dasdview_view(dasdview_info_t *info)
 	return 0;
 }
 
+static void
+dasdview_print_characteristic(dasdview_info_t *info)
+{
+		 dasd_information2_t dasd_info;
+		 dasd_info = info->dasd_info;
+		 printf("encrypted disk         : %s\n",
+			(dasd_info.characteristics[46]&0x80) ? "yes" : "no");
+}
+
 
 int main(int argc, char * argv[]) {
 
@@ -1418,6 +1568,10 @@ int main(int argc, char * argv[]) {
 		                info.vtoc_f5 = 1;
 		        else if (strncmp(optarg,"f7",2)==0)
 		                info.vtoc_f7 = 1;
+		        else if (strncmp(optarg,"f8",2)==0)
+		                info.vtoc_f8 = 1;
+		        else if (strncmp(optarg,"f9",2)==0)
+		                info.vtoc_f9 = 1;
 		        else if (strncmp(optarg,"all",3)==0)
 		                info.vtoc_all = 1;
 		        else
@@ -1430,6 +1584,10 @@ int main(int argc, char * argv[]) {
 		        info.vtoc = 1;
 			info.action_specified = 1;
 			break;
+		case 'c':
+			info.action_specified = 1;
+			info.characteristic_specified = 1;
+			break;
 		case 'l':
 			info.action_specified = 1;
 			info.vlabel_info = 1;
@@ -1496,7 +1654,7 @@ int main(int argc, char * argv[]) {
 	else
 		info.begin = DEFAULT_BEGIN;
 
-	max = (unsigned long long) info.geo.cylinders *
+	max = (unsigned long long) info.hw_cylinders *
 		(unsigned long long) info.geo.heads *
 		(unsigned long long) info.geo.sectors *
 		(unsigned long long) info.blksize;
@@ -1585,10 +1743,23 @@ int main(int argc, char * argv[]) {
 	        dasdview_print_vtoc_f1(&info);
 	}
 
+	if (info.vtoc_f8 || info.vtoc_all)
+	{
+	        dasdview_print_vtoc_f8(&info);
+	}
+
+	if (info.vtoc_f9 || info.vtoc_all)
+	{
+	        dasdview_print_vtoc_f9(&info);
+	}
+
 	if (!info.action_specified)
 	{
 		printf("No action specified.\n");
 	}
 
+	if (info.characteristic_specified)
+		dasdview_print_characteristic(&info);
+
 	return 0;
 }
Index: s390-tools-sles11/dasdview/dasdview.h
===================================================================
--- s390-tools-sles11.orig/dasdview/dasdview.h
+++ s390-tools-sles11/dasdview/dasdview.h
@@ -97,21 +97,11 @@ typedef struct dasd_information2_t {
 #define BIODASDINFO2   _IOR(DASD_IOCTL_LETTER,3,dasd_information2_t)
 
 /********************************************************************************
- * SECTION: Further IOCTL Definitions  (see fs.h)
+ * SECTION: Further IOCTL Definitions  (see fs.h and hdreq.h)
  *******************************************************************************/
 /* get block device sector size */
 #define BLKSSZGET  _IO(0x12,104)
-
-/********************************************************************************
- * SECTION: Definition from hdreq.h
- *******************************************************************************/
-
-struct hd_geometry {
-      unsigned char heads;
-      unsigned char sectors;
-      unsigned short cylinders;
-      unsigned long start;
-};
+#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */
 
 /* get device geometry */
 #define HDIO_GETGEO		0x0301
@@ -126,7 +116,7 @@ struct hd_geometry {
 #define DASDVIEW_ERROR "dasdview:"
 #define DEFAULT_BEGIN 0
 #define DEFAULT_SIZE 128
-#define NO_F1_LABELS 8
+#define NO_PART_LABELS 8 /* for partition related labels (f1,f8 and f9) */
 #define SEEK_STEP 4194304LL
 #define DUMP_STRING_SIZE 1024LL
 
@@ -158,6 +148,7 @@ typedef struct dasdview_info
 	int blksize;
 	int devno;
 	struct hd_geometry geo;
+	u_int32_t hw_cylinders;
 
 	unsigned long long begin;
 	unsigned long long size;
@@ -169,6 +160,7 @@ typedef struct dasdview_info
 	int node_specified;
 	int begin_specified;
 	int size_specified;
+	int characteristic_specified;
 	int device_id;
 	int general_info;
 	int extended_info;
@@ -179,21 +171,27 @@ typedef struct dasdview_info
 	int vtoc_f4;
 	int vtoc_f5;
 	int vtoc_f7;
+	int vtoc_f8;
+	int vtoc_f9;
 	int vtoc_all;
 	int vlabel_info;
 
-	format1_label_t f1[NO_F1_LABELS];
+	format1_label_t f1[NO_PART_LABELS];
 	format4_label_t f4;
 	format5_label_t f5;
 	format7_label_t f7;
+	format1_label_t f8[NO_PART_LABELS];
+	format9_label_t f9[NO_PART_LABELS];
 	int f1c;
 	int f4c;
 	int f5c;
 	int f7c;
+	int f8c;
+	int f9c;
 } dasdview_info_t;
 
 
-#define dasdview_getopt_string "t:n:f:b:s:vhixjl12"
+#define dasdview_getopt_string "t:n:f:b:s:vhixjl12c"
 
 /* struct options for getopt */
 static struct option dasdview_getopt_long_options[]=
@@ -209,6 +207,7 @@ static struct option dasdview_getopt_lon
 	{ "volser",      0, 0, 'j'},
 	{ "vtoc",        1, 0, 't'},
 	{ "label",       0, 0, 'l'},
+	{ "characteristic", 0, 0, 'c'},
 	{0, 0, 0, 0}
 };
 
Index: s390-tools-sles11/etc/init.d/dumpconf
===================================================================
--- s390-tools-sles11.orig/etc/init.d/dumpconf
+++ s390-tools-sles11/etc/init.d/dumpconf
@@ -13,6 +13,7 @@
 # chkconfig: 12345 01 99
 
 DUMP_CONFIG_FILE=/etc/sysconfig/dumpconf
+ERRMSG="Check $DUMP_CONFIG_FILE!"
 
 RETVAL=0
 
@@ -74,8 +75,8 @@ EOF
 printversion()
 {
     cat <<EOF
-dumpconf: zSeries dump configuration script version 1.1.
-Copyright IBM Corp. 2006, 2008.
+dumpconf: zSeries dump configuration script version 1.1
+Copyright IBM Corp. 2006, 2009
 EOF
 }
 
@@ -117,18 +118,77 @@ verify_ccw_dump_device()
 	fi
 }
 
-setup_ccw_device()
-{
-	echo $DEVICE > $1/ccw/device || RETVAL=1
+#------------------------------------------------------------------------------
+# Helper function to check a device string.
+#------------------------------------------------------------------------------
+function CheckDeviceString() {
+	local X
+
+	X=$(
+		echo "$1" |
+		awk --posix -F. '
+			function PrintBusID(css, grp, devno) {
+				while(length(devno) < 4)
+					devno = "0" devno
+				print css "." grp "." devno
+			}
+			NF == 1 && $1 ~ /^[0-9a-fA-F]{1,4}$/ {
+				PrintBusID("0","0", $1)
+				next
+			}
+			NF != 3 || $1 !~ /^[0-9a-fA-F]{1,2}$/ {
+				next
+			}
+			$2 !~ /^[0-9a-fA-F]{1,2}$/ {
+				next
+			}
+			$3 !~ /^[0-9a-fA-F]{1,4}$/ {
+				next
+			}
+			{
+				PrintBusID($1, $2, $3)
+			}
+		'
+		)
+
+	if [ "$X" != "" ]; then
+		echo $X
+		return 0
+	fi
 }
 
-setup_fcp_device()
+setup_device()
 {
-	echo $DEVICE > $1/fcp/device || RETVAL=1
-	echo $WWPN > $1/fcp/wwpn || RETVAL=1
-	echo $LUN > $1/fcp/lun || RETVAL=1
-	echo $BOOTPROG > $1/fcp/bootprog || RETVAL=1
-	echo $BR_LBA > $1/fcp/br_lba || RETVAL=1
+	DEV="$(CheckDeviceString $DEVICE)"
+	if [ "$DEV" != "" ]; then
+		echo $DEV > $1/$2/device
+	else
+		RETVAL=1
+		echo "ERROR: Invalid DEVICE '$DEVICE'." $ERRMSG >&2
+		return
+	fi
+	if [ $2 == "fcp" ]; then
+		echo $WWPN > $1/fcp/wwpn 2>/dev/null || RETVAL=1
+		if [ $RETVAL -eq 1 ]; then
+			echo "ERROR: Invalid WWPN '$WWPN'." $ERRMSG >&2
+			return
+		fi
+		echo $LUN > $1/fcp/lun 2>/dev/null || RETVAL=1
+		if [ $RETVAL -eq 1 ]; then
+			echo "ERROR: Invalid LUN '$LUN'." $ERRMSG >&2
+			return
+		fi
+		echo $BOOTPROG > $1/fcp/bootprog 2>/dev/null || RETVAL=1
+		if [ $RETVAL -eq 1 ]; then
+			echo "ERROR: Invalid BOOTPROG '$BOOTPROG'." $ERRMSG >&2
+			return
+		fi
+		echo $BR_LBA > $1/fcp/br_lba 2>/dev/null || RETVAL=1
+		if [ $RETVAL -eq 1 ]; then
+			echo "ERROR: Invalid BR_LBA '$BR_LBA'." $ERRMSG >&2
+			return
+		fi
+	fi
 }
 
 setup_nss_device()
@@ -143,23 +203,20 @@ setup_reipl()
 		return
 	fi
 
-	if [ "$REIPL_TYPE" == "ccw" ]; then
-		setup_ccw_device $REIPL_CONFIG_DIR
-	elif [ "$REIPL_TYPE" == "fcp" ]; then
-		setup_fcp_device $REIPL_CONFIG_DIR
+	if [ "$REIPL_TYPE" == "ccw" ] || [ "$REIPL_TYPE" == "fcp" ]; then
+		setup_device $REIPL_CONFIG_DIR $REIPL_TYPE
 	elif [ "$REIPL_TYPE" == "nss" ]; then
 		setup_nss_device $REIPL_CONFIG_DIR
 	else
-		echo "ERROR: Unknown reipl type '$REIPL_TYPE'" >&2
+		echo "ERROR: Unknown reipl type '$REIPL_TYPE'." $ERRMSG >&2
 		RETVAL=1
+		return
 	fi
 
 	echo $REIPL_TYPE > $REIPL_CONFIG_DIR/reipl_type || RETVAL=1
 
 	if [ $RETVAL -eq 1 ]; then
-		echo "ERROR: Setup of $REIPL_TYPE reipl device failed." >&2
-		echo none > $REIPL_CONFIG_DIR/reipl_type
-		return $RETVAL
+		return
 	fi
 
 	echo "$REIPL_TYPE reipl device configured."
@@ -167,28 +224,22 @@ setup_reipl()
 
 setup_dump()
 {
-	if [ "$DUMP_TYPE" == "ccw" ]; then
-		setup_ccw_device $DUMP_CONFIG_DIR
-	elif [ "$DUMP_TYPE" == "fcp" ]; then
-		setup_fcp_device $DUMP_CONFIG_DIR
+	if [ "$DUMP_TYPE" == "ccw" ] || [ "$DUMP_TYPE" == "fcp" ]; then
+		setup_device $DUMP_CONFIG_DIR $DUMP_TYPE
 	elif [ "$DUMP_TYPE" != "none" ]; then
-		echo "ERROR: Unknown dump type '$DUMP_TYPE'" >&2
+		echo "ERROR: Unknown dump type '$DUMP_TYPE'." $ERRMSG >&2
 		RETVAL=1
+		return
 	fi
 
 	echo $DUMP_TYPE > $DUMP_CONFIG_DIR/dump_type || RETVAL=1
 
 	if [ $RETVAL -eq 1 ]; then
-		echo "ERROR: Setup of $DUMP_TYPE dump device failed." >&2
 		echo none > $DUMP_CONFIG_DIR/dump_type
-		return $RETVAL
+		return
 	fi
 
-	if [ "$CONF_DUMP_TYPE" == "none" ]; then
-		echo "No dump device configured. "
-	else
-		echo "dump on panic configured: Using $DUMP_TYPE device."
-	fi
+	echo "$ON_PANIC on panic configured: Using $DUMP_TYPE dump device."
 }
 
 setup_on_panic_vmcmd()
@@ -207,7 +258,7 @@ setup_on_panic_vmcmd()
 		echo "ERROR: No vmcmd support. Are you running on LPAR?" >&2
 		RETVAL=1
 	elif [ "$VMCMD" == "" ]; then
-		echo "ERROR: No vmcmd for panic specified!" >&2
+		echo "ERROR: No VMCMD_x keyword specified." $ERRMSG >&2
 		RETVAL=1
 	else
 		echo -en "$VMCMD" | cat > $VMCMD_CONFIG_DIR/on_panic || RETVAL=1
@@ -221,23 +272,28 @@ setup_on_panic_vmcmd()
 
 print_fcp_device()
 {
-	echo "device..: $(cat $1/fcp/device)" || RETVAL=1
-	echo "wwpn....: $(cat $1/fcp/wwpn)" || RETVAL=1
-	echo "lun.....: $(cat $1/fcp/lun)" || RETVAL=1
-	echo "bootprog: $(cat $1/fcp/bootprog)" || RETVAL=1
-	echo "br_lba..: $(cat $1/fcp/br_lba)" || RETVAL=1
+	DEVICE=$(cat $1/fcp/device) || RETVAL=1
+	echo "device..: $DEVICE"
+	WWPN=$(cat $1/fcp/wwpn) || RETVAL=1
+	echo "wwpn....: $WWPN"
+	LUN=$(cat $1/fcp/lun) || RETVAL=1
+	echo "lun.....: $LUN"
+	BOOTPROG=$(cat $1/fcp/bootprog) || RETVAL=1
+	echo "bootprog: $BOOTPROG"
+	BR_LBA=$(cat $1/fcp/br_lba) || RETVAL=1
+	echo "br_lba..: $BR_LBA"
 }
 
 print_ccw_device()
 {
-	DEVICE=$(cat $1/ccw/device)
-	echo "device..: $DEVICE" || RETVAL=1
+	DEVICE=$(cat $1/ccw/device) || RETVAL=1
+	echo "device..: $DEVICE"
 }
 
 print_nss_name()
 {
-	NAME=$(cat $1/nss/device)
-	echo "device..: $NAME" || RETVAL=1
+	NAME=$(cat $1/nss/device) || RETVAL=1
+	echo "device..: $NAME"
 }
 
 status_dump()
@@ -246,14 +302,14 @@ status_dump()
 	if [ "$CONF_DUMP_TYPE" == "none" ]; then
 		echo "type....: no dump device configured"
 	elif [ "$CONF_DUMP_TYPE" == "ccw" ]; then
-		echo "type....: $(cat $DUMP_CONFIG_DIR/dump_type)" || RETVAL=1
+		echo "type....: ccw"
 		print_ccw_device $DUMP_CONFIG_DIR
 		verify_ccw_dump_device $(cat $DUMP_CONFIG_DIR/ccw/device)
 	elif [ "$CONF_DUMP_TYPE" == "fcp" ]; then
-		echo "type....: $(cat $DUMP_CONFIG_DIR/dump_type)" || RETVAL=1
+		echo "type....: fcp"
 		print_fcp_device $DUMP_CONFIG_DIR
 	else
-		echo "ERROR: Unknown dump device type '$TYPE'!" >&2
+		echo "ERROR: Unknown dump device type '$CONF_DUMP_TYPE'!" >&2
 		echo "       Please check if you have the latest dumpconf package!" >&2
 	fi
 }
@@ -261,7 +317,7 @@ status_dump()
 status_reipl()
 {
 	REIPL_TYPE=$(cat $REIPL_CONFIG_DIR/reipl_type) || RETVAL=1
-	echo "type....: $(cat $REIPL_CONFIG_DIR/reipl_type)" || RETVAL=1
+	echo "type....: $REIPL_TYPE"
 	if [ "$REIPL_TYPE" == "ccw" ]; then
 		print_ccw_device $REIPL_CONFIG_DIR
 	elif [ "$REIPL_TYPE" == "fcp" ]; then
@@ -274,11 +330,20 @@ status_reipl()
 	fi
 }
 
+status_dump_reipl()
+{
+	echo -e "\ndump:"
+	status_dump
+	echo -e "\nreipl:"
+	status_reipl
+}
+
+
 status_vmcmd()
 {
 	VMCMD=$(cat $VMCMD_CONFIG_DIR/on_panic) || RETVAL=1
 	if [ "$VMCMD" == "" ]; then
-		echo "WARNING: No VM command specied!"
+		echo "WARNING: No VM command specified!"
 	else
 		echo "---------------"
 		echo "$VMCMD"
@@ -293,26 +358,27 @@ start()
 
 	if [ "$ON_PANIC" == "reipl" ]; then
 		setup_reipl
-	elif [ "$ON_PANIC" == "dump" ]; then
+	elif [ "$ON_PANIC" == "dump" ] || [ "$ON_PANIC" == "dump_reipl" ]; then
 		setup_dump
 	elif [ "$ON_PANIC" == "vmcmd" ]; then
 		setup_on_panic_vmcmd
 	elif [ "$ON_PANIC" == "stop" ]; then
 		echo "stop on panic configured."
 	else
-		echo "ERROR: Unknown 'on panic' type '$ON_PANIC'" >&2
+		echo "ERROR: Unknown 'on panic' type '$ON_PANIC'." $ERRMSG >&2
 		RETVAL=1
 	fi
+	if [ $RETVAL -eq 1 ]; then
+		return $RETVAL
+	fi
 
-	echo $ON_PANIC > $ON_PANIC_CONFIG_FILE || RETVAL=1
+	echo $ON_PANIC > $ON_PANIC_CONFIG_FILE 2> /dev/null || RETVAL=1
 
 	# check for errors
 
 	if [ $RETVAL -eq 1 ]; then
-		ERRMSG="\"on panic action\" configuration failed!"
 		echo stop > $ON_PANIC_CONFIG_FILE
-		echo "ERROR: $ERRMSG Check $DUMP_CONFIG_FILE!" >&2
-		return $RETVAL
+		echo "ERROR: $ON_PANIC not supported by hardware!" >&2
 	fi
 
 	return $RETVAL
@@ -340,6 +406,8 @@ status()
 		status_reipl
 	elif [ "$ON_PANIC" == "dump" ]; then
 		status_dump
+	elif [ "$ON_PANIC" == "dump_reipl" ]; then
+		status_dump_reipl
 	elif [ "$ON_PANIC" != "stop" ]; then
 		echo "ERROR: Unknown on_panic type '$ON_PANIC'" >&2
 	fi
Index: s390-tools-sles11/etc/sysconfig/dumpconf
===================================================================
--- s390-tools-sles11.orig/etc/sysconfig/dumpconf
+++ s390-tools-sles11/etc/sysconfig/dumpconf
@@ -5,16 +5,19 @@
 #
 # The following actions are supported:
 #
-# * stop:  Stop Linux (default)
-# * dump:  Dump Linux
-# * vmcmd: Issue z/VM CP commands
-# * reipl: Reboot Linux using setting under /sys/firmware/reipl
+# * stop:       Stop Linux (default)
+# * dump:       Dump Linux
+# * vmcmd:      Issue z/VM CP commands
+# * reipl:      Re-IPL Linux using setting under /sys/firmware/reipl
+# * dump_reipl: First dump Linux, then re-IPL Linux using setting under
+#               /sys/firmware/reipl
 #
 
 #
-# Dump on ccw device (DASD)
+# Dump on ccw device (DASD) and re-IPL after dump is complete.
+# The re-IPL device, as specified under "/sys/firmware/reipl", is used.
 #
-# ON_PANIC=dump
+# ON_PANIC=dump_reipl
 # DUMP_TYPE=ccw
 # DEVICE=0.0.4e13
 
@@ -43,7 +46,7 @@
 # ON_PANIC=stop
 
 #
-# Reipl on panic
-# The reipl device, as specified under "/sys/firmware/reipl", is used.
+# Re-IPL on panic
+# The re-IPL device, as specified under "/sys/firmware/reipl", is used.
 #
 # ON_PANIC=reipl
Index: s390-tools-sles11/fdasd/fdasd.c
===================================================================
--- s390-tools-sles11.orig/fdasd/fdasd.c
+++ s390-tools-sles11/fdasd/fdasd.c
@@ -12,6 +12,11 @@
 #include "vtoc.h"
 #include "fdasd.h"
 
+/* global variables */
+struct hd_geometry geo;
+char line_buffer[LINE_LENGTH];
+char *line_ptr = line_buffer;
+
 /* Full tool name */
 static const char tool_name[] = "fdasd: zSeries DASD partitioning program";
 
@@ -53,6 +58,45 @@ setpos (fdasd_anchor_t *anc, int dsn, in
 	anc->partno[dsn] = pos;
 }
 
+static u_int32_t get_usable_cylinders(fdasd_anchor_t *anc)
+{
+	u_int32_t cyl;
+
+	/* large volume */
+	if (anc->f4->DS4DEVCT.DS4DSCYL == LV_COMPAT_CYL &&
+	    anc->f4->DS4DCYL > anc->f4->DS4DEVCT.DS4DSCYL)
+		return anc->f4->DS4DCYL;
+	/* normal volume */
+	if (anc->f4->DS4DEVCT.DS4DEVFG & ALTERNATE_CYLINDERS_USED)
+		cyl = anc->f4->DS4DEVCT.DS4DSCYL -
+			(u_int16_t) anc->f4->DS4DEVAC;
+	else
+		cyl = anc->f4->DS4DEVCT.DS4DSCYL;
+	return cyl;
+}
+
+static void get_addr_of_highest_f1_f8_label(fdasd_anchor_t *anc, cchhb_t *addr)
+{
+
+	u_int8_t record;
+	/* We have to count the follwing labels:
+	 * one format 4
+	 * one format 5
+	 * format 7 only if we have moren then BIG_DISK_SIZE tracks
+	 * one for each format 1 or format 8 label == one for each partition
+	 * one for each format 9 label before the last format 8
+	 * We assume that all partitions use format 8 labels when
+	 *  anc->formatted_cylinders > LV_COMPAT_CYL
+	 * Note: Record zero is special, so block 0 on our disk is record 1!
+	 */
+
+	record = anc->used_partitions + 2;
+	if (anc->big_disk)
+		record++;
+	if (anc->formatted_cylinders > LV_COMPAT_CYL)
+		record += anc->used_partitions - 1;
+        vtoc_set_cchhb(addr, VTOC_START_CC, VTOC_START_HH, record);
+}
 
 /* 
  * Check for valid volume serial characters (max. 6) - remove invalid.
@@ -255,18 +299,6 @@ yes_no(char *question_str) 
 
 
 /*
- * converts cyl-cyl-head-head-blk to blk
- */
-static unsigned long
-cchhb2blk (cchhb_t *p) 
-{
-        return (unsigned long) (p->cc * geo.heads * geo.sectors + 
-                                p->hh * geo.sectors +
-                                p->b);
-}
-
-
-/*
  *
  */
 static char *
@@ -370,9 +402,17 @@ fdasd_initialize_anchor (fdasd_anchor_t 
 		fdasd_error(anc, malloc_failed,
 			    "FMT7 DSCB memory allocation failed.");
 
+	/* template for all format 9 labels */
+	anc->f9 = malloc(sizeof(format9_label_t));
+	if (anc->f9 == NULL)
+		fdasd_error(anc, malloc_failed,
+			    "FMT9 DSCB memory allocation failed.");
+
 	bzero(anc->f4, sizeof(format4_label_t));
 	bzero(anc->f5, sizeof(format5_label_t));
 	bzero(anc->f7, sizeof(format7_label_t));
+	bzero(anc->f9, sizeof(format9_label_t));
+	vtoc_init_format9_label(anc->f9);
 
 	vlabel = malloc(sizeof(volume_label_t));
 	if (vlabel == NULL) 
@@ -415,6 +455,8 @@ fdasd_initialize_anchor (fdasd_anchor_t 
 		
 		prev_part_info = part_info;
 	}
+	anc->hw_cylinders = 0;
+	anc->formatted_cylinders = 0;
 }
 
 
@@ -554,7 +596,8 @@ fdasd_parse_conffile(fdasd_anchor_t *anc
 
 		token = strtok(NULL, ",");
 		if (strstr(token, "LAST") != NULL)
-			anc->confdata[i].stop = geo.cylinders * geo.heads - 1;
+			anc->confdata[i].stop = anc->formatted_cylinders
+				* geo.heads - 1;
 		else
 			anc->confdata[i].stop = strtol(token, &stopstring, 10);
 		c1 = c2 + 1;
@@ -585,7 +628,7 @@ fdasd_check_conffile_input (fdasd_anchor
 		if ((start == 0) || (stop == 0)) break;
 
 		first_trk = FIRST_USABLE_TRK;
-		last_trk = geo.cylinders * geo.heads - 1;
+		last_trk = anc->formatted_cylinders * geo.heads - 1;
 
 		if ((start < first_trk) || (start > last_trk)) {
 			snprintf(err_str, ERROR_STRING_SIZE,
@@ -857,7 +900,7 @@ fdasd_list_partition_table (fdasd_anchor
 		       "  tracks per cylinder ..: %d\n"
 		       "  blocks per track .....: %d\n"
 		       "  bytes per block ......: %d\n",
-		       options.device, geo.cylinders, geo.heads, 
+		       options.device, anc->formatted_cylinders, geo.heads,
 		       geo.sectors, anc->blksize);
 
 		vtoc_volume_label_get_label(anc->vlabel, str);
@@ -1010,46 +1053,47 @@ static void
 fdasd_write_vtoc_labels (fdasd_anchor_t *anc) 
 {
         partition_info_t *part_info;
-	unsigned long blk;
+	unsigned long blk, maxblk;
 	char dsno[6], volser[VOLSER_LENGTH + 1], s2[45], *c1, *c2, *ch;
 	int i=0, k=0;
+	cchhb_t f9addr;
+	format1_label_t emptyf1;
 
 	if (!anc->silent) printf("writing VTOC...\n");
 	if (anc->verbose) printf("DSCBs: ");
 
-	blk = (cchhb2blk(&anc->vlabel->vtoc) - 1) * anc->blksize;
+	blk = (cchhb2blk(&anc->vlabel->vtoc, &geo) - 1) * anc->blksize;
 	if (blk <= 0) 
 		fdasd_error(anc, vlabel_corrupted, "");
+	maxblk = blk + anc->blksize * 9; /* f4+f5+f7+3*f8+3*f9 */
 
 	/* write FMT4 DSCB */
-	vtoc_write_label(options.device, blk, NULL, anc->f4, NULL, NULL);
+	vtoc_write_label(options.device, blk, NULL, anc->f4, NULL, NULL, NULL);
 	if (anc->verbose) printf("f4 ");
+	blk += anc->blksize;
 
 	/* write FMT5 DSCB */
-	blk += anc->blksize;
-	vtoc_write_label(options.device, blk, NULL, NULL, anc->f5, NULL);
+	vtoc_write_label(options.device, blk, NULL, NULL, anc->f5, NULL, NULL);
 	if (anc->verbose) printf("f5 ");
+	blk += anc->blksize;
 
 	/* write FMT7 DSCB */
 	if (anc->big_disk) {
-		blk += anc->blksize;
 		vtoc_write_label(options.device, blk,  NULL, NULL,
-				 NULL,anc->f7);
+				 NULL,anc->f7, NULL);
 		if (anc->verbose) printf("f7 ");
+		blk += anc->blksize;
 	}
 
-	/* loop over all FMT1 DSCBs */
+	/* loop over all partitions (format 1 or format 8 DCB) */
 	for (part_info = anc->first; part_info != NULL;
 	     part_info = part_info->next) {
-		blk += anc->blksize; 
-		i++;
 
 		if (part_info->used != 0x01) {
-			vtoc_write_label(options.device, blk, 
-					 part_info->f1, NULL, NULL, NULL);
 			continue;
 		}
 
+		i++;
 		strncpy((char *)part_info->f1->DS1DSSN,	anc->vlabel->volid,
 			VOLSER_LENGTH);
 
@@ -1102,11 +1146,41 @@ fdasd_write_vtoc_labels (fdasd_anchor_t 
 				strncpy(c1, ".NATIVE", 7);
 		}
 		vtoc_ebcdic_enc(ch, ch, 44);
-		if (anc->verbose) printf("f1 ");
+		if (anc->verbose) printf("%2x ", part_info->f1->DS1FMTID);
 
-		vtoc_write_label(options.device, blk, part_info->f1, NULL,
-				 NULL, NULL);
+		if (part_info->f1->DS1FMTID == 0xf8 ) {
+			/* Now as we know where which label will be written, we
+			 * can add the address of the format 9 label to the
+			 * format 8 label. The f9 record will be written to the
+			 * block after the current blk. Remember: records are of
+			 * by one, so we have to add 2 and not just one.
+			 */
+			vtoc_set_cchhb(&f9addr, VTOC_START_CC, VTOC_START_HH,
+				       ((blk / anc->blksize) % geo.sectors) + 2);
+			vtoc_update_format8_label(&f9addr, part_info->f1);
+			vtoc_write_label(options.device, blk, part_info->f1,
+					 NULL, NULL, NULL, NULL);
+			blk += anc->blksize;
+			vtoc_write_label(options.device, blk,  NULL, NULL,
+					 NULL, NULL, anc->f9);
+			if (anc->verbose) printf("f9 ");
+			blk += anc->blksize;
+		} else {
+			vtoc_write_label(options.device, blk, part_info->f1,
+					 NULL, NULL, NULL, NULL);
+			blk += anc->blksize;
+		}
 	}
+
+	/* write empty labels to the rest of the blocks */
+	bzero(&emptyf1, sizeof(emptyf1));
+	while (blk < maxblk) {
+		vtoc_write_label(options.device, blk, &emptyf1, NULL,
+				 NULL, NULL, NULL);
+		if (anc->verbose) printf("empty ");
+		blk += anc->blksize;
+	}
+
 	if (anc->verbose) printf("\n");
 }
 
@@ -1134,34 +1208,22 @@ fdasd_write_labels (fdasd_anchor_t *anc)
 /*
  * re-creates the VTOC and deletes all partitions
  */
-static void
-fdasd_recreate_vtoc(fdasd_anchor_t *anc)
+static void fdasd_recreate_vtoc_unconditional(fdasd_anchor_t *anc)
 {
 	partition_info_t *part_info = anc->first;
-	char str[INPUT_BUF_SIZE];
 	int i;
 
-	if (!anc->silent) {
-		snprintf(str, INPUT_BUF_SIZE, 
-			 "WARNING: All partitions on device '%s' will be "
-			 "deleted!\nDo you want to continue?",
-			 options.device);
-
-		if (yes_no(str) != 0)
-			return;
-
-		printf("creating new VTOC... ");
-	}
-
 	vtoc_init_format4_label(anc->f4, USABLE_PARTITIONS,
-				geo.cylinders, geo.heads, geo.sectors,
+				geo.cylinders, anc->formatted_cylinders,
+				geo.heads, geo.sectors,
 				anc->blksize, anc->dev_type);
 
 	vtoc_init_format5_label(anc->f5);
 	vtoc_init_format7_label(anc->f7);
 	vtoc_set_freespace(anc->f4,anc->f5, anc->f7, '+', anc->verbose,
-			   FIRST_USABLE_TRK, geo.cylinders * geo.heads - 1,
-			   geo.cylinders, geo.heads);
+			   FIRST_USABLE_TRK,
+			   anc->formatted_cylinders * geo.heads - 1,
+			   anc->formatted_cylinders, geo.heads);
 
 	while (part_info != NULL) {
 
@@ -1179,12 +1241,34 @@ fdasd_recreate_vtoc(fdasd_anchor_t *anc)
 	}
 
 	anc->used_partitions = 0;
-	anc->fspace_trk = geo.cylinders * geo.heads - FIRST_USABLE_TRK;
+	anc->fspace_trk = anc->formatted_cylinders * geo.heads
+		- FIRST_USABLE_TRK;
 
 	for (i=0; i<USABLE_PARTITIONS; i++)
 		setpos(anc, i, -1);
 
 	anc->vtoc_changed++;
+}
+
+/*
+ * asks user for confirmation before recreating the vtoc
+ */
+static void fdasd_recreate_vtoc(fdasd_anchor_t *anc)
+{
+	char str[INPUT_BUF_SIZE];
+
+	if (!anc->silent) {
+		snprintf(str, INPUT_BUF_SIZE,
+			 "WARNING: All partitions on device '%s' will be "
+			 "deleted!\nDo you want to continue?",
+			 options.device);
+
+		if (yes_no(str) != 0)
+			return;
+
+		printf("creating new VTOC... ");
+	}
+	fdasd_recreate_vtoc_unconditional(anc);
 	if (!anc->silent) printf("ok\n");
 }
 
@@ -1216,7 +1300,8 @@ fdasd_reuse_vtoc(fdasd_anchor_t *anc)
 	if (!anc->silent) printf("re-creating VTOC... ");
 
 	vtoc_init_format4_label(&f4, USABLE_PARTITIONS,
-				geo.cylinders, geo.heads, geo.sectors,
+				geo.cylinders, anc->formatted_cylinders,
+				geo.heads, geo.sectors,
 				anc->blksize, anc->dev_type);
 
 	/* reuse some FMT4 values */
@@ -1231,7 +1316,7 @@ fdasd_reuse_vtoc(fdasd_anchor_t *anc)
 		vtoc_set_freespace(&f4, &f5, &f7, '+', anc->verbose,
 				   FIRST_USABLE_TRK, 
 				   FIRST_USABLE_TRK + anc->fspace_trk - 1,
-				   geo.cylinders, geo.heads);
+				   anc->formatted_cylinders, geo.heads);
 
 	while (part_info != NULL) {
 		if (part_info->used != 0x01) {
@@ -1239,8 +1324,13 @@ fdasd_reuse_vtoc(fdasd_anchor_t *anc)
 			continue;
 		}
 
-		vtoc_init_format1_label(anc->vlabel->volid, anc->blksize, 
-					&part_info->f1->DS1EXT1, &f1);
+		if (anc->formatted_cylinders > LV_COMPAT_CYL) {
+		    vtoc_init_format8_label(anc->vlabel->volid, anc->blksize,
+					    &part_info->f1->DS1EXT1, &f1);
+		} else
+		    vtoc_init_format1_label(anc->vlabel->volid, anc->blksize,
+					    &part_info->f1->DS1EXT1, &f1);
+
 
 		strncpy(f1.DS1DSNAM, part_info->f1->DS1DSNAM, 44);
 		strncpy((char *)f1.DS1DSSN, (char *)part_info->f1->DS1DSSN, 6);
@@ -1253,7 +1343,7 @@ fdasd_reuse_vtoc(fdasd_anchor_t *anc)
 					   part_info->end_trk + 1, 
 					   part_info->end_trk +
 					   part_info->fspace_trk,
-					   geo.cylinders, geo.heads);
+					   anc->formatted_cylinders, geo.heads);
 
 		part_info = part_info->next;
 	}
@@ -1392,8 +1482,7 @@ fdasd_init_volume_label (fdasd_anchor_t 
 			read_line();
 			fdasd_check_volser(line_ptr, anc->devno);
 			vtoc_volume_label_set_volser(vlabel, line_ptr);
-		} 
-		else {
+		} else {
 			fdasd_check_volser(options.volser, anc->devno);
 			vtoc_volume_label_set_volser(vlabel, options.volser);
 		}
@@ -1415,14 +1504,14 @@ static void
 fdasd_update_partition_info (fdasd_anchor_t *anc) 
 {
         partition_info_t *prev_part_info = NULL, *part_info = anc->first;
-	unsigned int head = geo.heads;
-	unsigned long max = geo.cylinders * head - 1;
+	unsigned long max = anc->formatted_cylinders * geo.heads - 1;
         int i;
 
 	anc->used_partitions = geo.sectors - 2 - anc->f4->DS4DSREC;
 
         for (i=1; i<=USABLE_PARTITIONS; i++) {
-	        if (part_info->f1->DS1FMTID != 0xf1) {
+	        if (part_info->f1->DS1FMTID != 0xf1 &&
+		    part_info->f1->DS1FMTID != 0xf8) {
 		        if (i == 1)
 				/* there is no partition at all */
 				anc->fspace_trk = max - FIRST_USABLE_TRK + 1;
@@ -1435,10 +1524,11 @@ fdasd_update_partition_info (fdasd_ancho
 		
 		/* this is a valid format 1 label */
 		part_info->used = 0x01;
-		part_info->start_trk = part_info->f1->DS1EXT1.llimit.cc * 
-			head + part_info->f1->DS1EXT1.llimit.hh;
-		part_info->end_trk = part_info->f1->DS1EXT1.ulimit.cc * head +
-			part_info->f1->DS1EXT1.ulimit.hh;
+		part_info->start_trk = cchh2trk(&part_info->f1->DS1EXT1.llimit,
+						&geo);
+		part_info->end_trk = cchh2trk(&part_info->f1->DS1EXT1.ulimit,
+					      &geo);
+
 		part_info->len_trk = part_info->end_trk -
 			part_info->start_trk + 1;
 		
@@ -1494,15 +1584,29 @@ static void
 fdasd_process_invalid_vtoc(fdasd_anchor_t *anc)
 {
 	printf(" invalid\ncreating new VTOC...\n");
+	if (anc->hw_cylinders > LV_COMPAT_CYL) {
+		printf("Warning: Device has more then %u cylinders!\n",
+			       LV_COMPAT_CYL);
+		if (yes_no("Are you sure it was completely"
+			   " formatted with dasdfmt?") == 1) {
+			if (!anc->silent) printf("exiting...\n");
+			fdasd_exit(anc, 0);
+		}
+	}
+	anc->formatted_cylinders = anc->hw_cylinders;
+	anc->fspace_trk = anc->formatted_cylinders * geo.heads
+		- FIRST_USABLE_TRK;
 	vtoc_init_format4_label(anc->f4, USABLE_PARTITIONS,
-				geo.cylinders, geo.heads, geo.sectors,
+				geo.cylinders, anc->formatted_cylinders,
+				geo.heads, geo.sectors,
 				anc->blksize, anc->dev_type);
 
 	vtoc_init_format5_label(anc->f5);
 	vtoc_init_format7_label(anc->f7);
 	vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '+', anc->verbose,
-			   FIRST_USABLE_TRK, geo.cylinders * geo.heads - 1,
-			   geo.cylinders, geo.heads);
+			   FIRST_USABLE_TRK,
+			   anc->formatted_cylinders * geo.heads - 1,
+			   anc->formatted_cylinders, geo.heads);
 
 	anc->vtoc_changed++;
 }
@@ -1521,8 +1625,21 @@ fdasd_process_valid_vtoc(fdasd_anchor_t 
 	char part_no_str[5], *part_pos;
 
 	if (!anc->silent) printf(" ok\n");
+
+	if (anc->f4->DS4DEVCT.DS4DSCYL == LV_COMPAT_CYL &&
+	    anc->f4->DS4DCYL > anc->f4->DS4DEVCT.DS4DSCYL)
+		anc->formatted_cylinders = anc->f4->DS4DCYL;
+	else
+		anc->formatted_cylinders = anc->f4->DS4DEVCT.DS4DSCYL;
+	anc->fspace_trk = anc->formatted_cylinders * geo.heads
+		- FIRST_USABLE_TRK;
 	blk += anc->blksize;
 
+	if (anc->formatted_cylinders < anc->hw_cylinders)
+		printf("WARNING: This device is not fully formatted! "
+		       "Only %u of %u cylinders are available.\n",
+		       anc->formatted_cylinders, anc->hw_cylinders);
+
 	if (anc->verbose) printf("VTOC DSCBs          : ");
 
 	for (i = 1; i <= geo.sectors; i++) {
@@ -1532,8 +1649,11 @@ fdasd_process_valid_vtoc(fdasd_anchor_t 
 
 		switch (f1_label.DS1FMTID) {
 		case 0xf1:
-			if (anc->verbose) printf("f1 ");
-			if(part_info == NULL) break;
+		case 0xf8:
+			if (anc->verbose)
+				printf("%s ",
+				       f1_label.DS1FMTID == 0xf1 ? "f1" : "f8");
+			if (part_info == NULL) break;
 			memcpy(part_info->f1, &f1_label, f1_size);
 
 			part_no = -1;
@@ -1572,6 +1692,12 @@ fdasd_process_valid_vtoc(fdasd_anchor_t 
 				memcpy(anc->f7, &f1_label, f1_size);
 			f7_counter++;
 			break;
+		case 0xf9:
+			/* each format 8 lable has an associated format 9 lable,
+                         * but they are of no further use to us.
+			 */
+			if (anc->verbose) printf("f9 ");
+			break;
 		default: 
 			if (f1_label.DS1FMTID > 0)
 				printf("'%d' is not supported!\n", 
@@ -1614,30 +1740,9 @@ fdasd_valid_vtoc_pointer(fdasd_anchor_t 
 		}
 		fdasd_process_invalid_vtoc(anc);
 	}
-	else 
+	else
 		fdasd_process_valid_vtoc(anc, blk);
 
-	/* change volume serial? */
-	if (options.volser && !anc->print_table) {
-		char volser[VOLSER_LENGTH + 1];
-
-		vtoc_volume_label_get_volser(anc->vlabel, volser);
-		volser[VOLSER_LENGTH] = 0x00;
-
-		fdasd_check_volser(options.volser, anc->devno);
-
-		snprintf(str, INPUT_BUF_SIZE,
-			"Overwrite existing volume serial '%-6s'\n"
-			"with new volume serial '%-6s'?", 
-			volser, options.volser);
-
-		if (yes_no(str) != 0)
-			return 0;
-		
-		vtoc_volume_label_set_volser(anc->vlabel, options.volser);
-		anc->vlabel_changed++;
-		anc->vtoc_changed++;
-	}
 	return 0;
 }
 
@@ -1654,16 +1759,30 @@ fdasd_invalid_vtoc_pointer(fdasd_anchor_
 		fdasd_exit(anc, 0);
 	}
 
-	vtoc_init_format4_label(anc->f4, USABLE_PARTITIONS, 
-				geo.cylinders, geo.heads, geo.sectors, 
+	if (anc->hw_cylinders > LV_COMPAT_CYL) {
+		printf("Warning: Device has more then %u cylinders!\n",
+			       LV_COMPAT_CYL);
+		if (yes_no("Are you sure it was completely"
+			   " formatted with dasdfmt?") == 1) {
+			if (!anc->silent) printf("exiting...\n");
+			fdasd_exit(anc, 0);
+		}
+	}
+	anc->formatted_cylinders = anc->hw_cylinders;
+	anc->fspace_trk = anc->formatted_cylinders * geo.heads
+		- FIRST_USABLE_TRK;
+	vtoc_init_format4_label(anc->f4, USABLE_PARTITIONS,
+				geo.cylinders, anc->formatted_cylinders,
+				geo.heads, geo.sectors,
 				anc->blksize, anc->dev_type);
 
 	vtoc_init_format5_label(anc->f5);
 	vtoc_init_format7_label(anc->f7);
 
 	vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '+', anc->verbose,
-			   FIRST_USABLE_TRK, geo.cylinders * geo.heads - 1,
-			   geo.cylinders, geo.heads);
+			   FIRST_USABLE_TRK,
+			   anc->formatted_cylinders * geo.heads - 1,
+			   anc->formatted_cylinders, geo.heads);
 
 	vtoc_set_cchhb(&anc->vlabel->vtoc, VTOC_START_CC, VTOC_START_HH, 0x01);
 
@@ -1679,7 +1798,7 @@ static int
 fdasd_check_volume (fdasd_anchor_t *anc) 
 {
 	volume_label_t *vlabel = anc->vlabel;
-        unsigned long blk = -1;
+        long long blk = -1;
 	char str[LINE_LENGTH];
 	char inp_buf[INPUT_BUF_SIZE];
 
@@ -1693,7 +1812,7 @@ fdasd_check_volume (fdasd_anchor_t *anc)
 		if (!anc->silent)
 			printf(" VOL1\n");
 
-		blk = (cchhb2blk(&vlabel->vtoc) - 1) * anc->blksize;
+		blk = (cchhb2blk(&vlabel->vtoc, &geo) - 1) * anc->blksize;
 		if (blk > 0) {
 			int rc;
 			rc = fdasd_valid_vtoc_pointer(anc, blk);
@@ -1708,8 +1827,7 @@ fdasd_check_volume (fdasd_anchor_t *anc)
 			}
 			fdasd_invalid_vtoc_pointer(anc);
 		}
-	}
-	else {
+	} else {
 	        /* didn't find VOL1 volume label */
 
 		if (anc->print_table) {
@@ -1734,19 +1852,33 @@ fdasd_check_volume (fdasd_anchor_t *anc)
 		               "partitioning\nexiting...\n");
 			fdasd_exit(anc, -1);
                 }
+
+		if (anc->hw_cylinders > LV_COMPAT_CYL) {
+			printf("Warning: Device has more then %u cylinders!\n",
+			       LV_COMPAT_CYL);
+			if (yes_no("Are you sure it was completely"
+				   " formatted with dasdfmt?") == 1) {
+				if (!anc->silent) printf("exiting...\n");
+				fdasd_exit(anc, 0);
+			}
+		}
+		anc->formatted_cylinders = anc->hw_cylinders;
+		anc->fspace_trk = anc->formatted_cylinders * geo.heads - FIRST_USABLE_TRK;
+
 		fdasd_init_volume_label(anc);
 
 		vtoc_init_format4_label(anc->f4, USABLE_PARTITIONS,
-					geo.cylinders, geo.heads, geo.sectors,
+					geo.cylinders, anc->formatted_cylinders,
+					geo.heads, geo.sectors,
 					anc->blksize, anc->dev_type);
 
 		vtoc_init_format5_label(anc->f5);
 		vtoc_init_format7_label(anc->f7);
 
-		vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '+', 
-				   anc->verbose, FIRST_USABLE_TRK, 
-				   geo.cylinders * geo.heads - 1,
-				   geo.cylinders, geo.heads);
+		vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '+',
+				   anc->verbose, FIRST_USABLE_TRK,
+				   anc->formatted_cylinders * geo.heads - 1,
+				   anc->formatted_cylinders, geo.heads);
 
 		anc->vtoc_changed++;
 	}
@@ -1823,6 +1955,7 @@ fdasd_get_geometry (fdasd_anchor_t *anc)
         int fd, blksize = 0;
 	dasd_information_t dasd_info;
 	char err_str[ERROR_STRING_SIZE];
+	u_int64_t device_size;
 
 	if ((fd = open(options.device,O_RDONLY)) < 0) {
 		snprintf(err_str, ERROR_STRING_SIZE,
@@ -1843,6 +1976,14 @@ fdasd_get_geometry (fdasd_anchor_t *anc)
 			    "Could not retrieve blocksize information.");
 	}
 
+	if (ioctl(fd, BLKGETSIZE64, &device_size) != 0) {
+	        close(fd);
+	        fdasd_error(anc, unable_to_ioctl,
+			    "Could not retrieve device size information.");
+	}
+
+	anc->hw_cylinders = ((device_size / blksize) / geo.sectors) / geo.heads;
+
 	/* get disk type */
 	if (ioctl(fd, BIODASDINFO, &dasd_info) != 0) {
 	        close(fd);
@@ -1891,7 +2032,6 @@ fdasd_get_geometry (fdasd_anchor_t *anc)
 	anc->blksize    = blksize;
 	anc->label_pos  = dasd_info.label_block * blksize;
 	anc->devno      = dasd_info.devno;
-	anc->fspace_trk = geo.cylinders * geo.heads - FIRST_USABLE_TRK;
 }
 
 
@@ -2025,20 +2165,16 @@ fdasd_get_partition_data (fdasd_anchor_t
 			  partition_info_t *part_info) 
 {
 	unsigned long start, stop, limit;
-	unsigned int cc, hh;
+	u_int32_t cc, cyl;
+	u_int16_t hh, head;
 	cchh_t llimit,ulimit;
 	partition_info_t *part_tmp;
         char mesg[48];
 	u_int8_t b1, b2;
-	u_int16_t cyl, head;
 
 	start = FIRST_USABLE_TRK;
-	if (anc->f4->DS4DEVCT.DS4DEVFG & ALTERNATE_CYLINDERS_USED)
-		cyl = anc->f4->DS4DEVCT.DS4DSCYL - 
-			(u_int16_t) anc->f4->DS4DEVAC;
-	else
-		cyl = anc->f4->DS4DEVCT.DS4DSCYL;
 
+ 	cyl = get_usable_cylinders(anc);
 	head = anc->f4->DS4DEVCT.DS4DSTRK;
 	limit = (head * cyl - 1);
 
@@ -2254,7 +2390,6 @@ fdasd_add_partition (fdasd_anchor_t *anc
 	partition_info_t *part_info;
 	extent_t ext;
 	unsigned long start, stop;
-	int i;
 
 	if ((part_info = fdasd_get_empty_f1_label(anc)) == NULL) {
 	        printf("No more free partitions left,\n"
@@ -2264,22 +2399,25 @@ fdasd_add_partition (fdasd_anchor_t *anc
 
 	if (fdasd_get_partition_data(anc, &ext, part_info) != 0)
 	        return;
-	vtoc_init_format1_label(anc->vlabel->volid, anc->blksize, &ext,
-				part_info->f1);
+
+	if (anc->formatted_cylinders > LV_COMPAT_CYL) {
+		vtoc_init_format8_label(anc->vlabel->volid, anc->blksize, &ext,
+					part_info->f1);
+	} else
+		vtoc_init_format1_label(anc->vlabel->volid, anc->blksize, &ext,
+					 part_info->f1);
+
 	fdasd_enqueue_new_partition(anc);
 	anc->used_partitions += 1;
 
-	i = anc->used_partitions + 2;
-	if (anc->big_disk) i++;
-
-        vtoc_set_cchhb(&hf1, VTOC_START_CC, VTOC_START_HH, i);
+	get_addr_of_highest_f1_f8_label(anc, &hf1);
 	vtoc_update_format4_label(anc->f4, &hf1, anc->f4->DS4DSREC - 1);
 
-	start = ext.llimit.cc * geo.heads + ext.llimit.hh;
-	stop  = ext.ulimit.cc * geo.heads + ext.ulimit.hh;
+	start = cchh2trk(&ext.llimit, &geo);
+	stop = cchh2trk(&ext.ulimit, &geo);
 
 	vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '-', anc->verbose,
-			   start, stop, geo.cylinders, geo.heads);
+			   start, stop, anc->formatted_cylinders, geo.heads);
 
 	anc->vtoc_changed++;
 }
@@ -2316,26 +2454,20 @@ fdasd_remove_partition (fdasd_anchor_t *
 	setpos(anc, part_id-1, -1);
 	for (i=1; i<part_id; i++) part_info=part_info->next;
 
-	start = part_info->f1->DS1EXT1.llimit.cc * geo.heads + 
-		part_info->f1->DS1EXT1.llimit.hh;
-	stop  = part_info->f1->DS1EXT1.ulimit.cc * geo.heads + 
-		part_info->f1->DS1EXT1.ulimit.hh;
+	start = cchh2trk(&part_info->f1->DS1EXT1.llimit, &geo);
+	stop  = cchh2trk(&part_info->f1->DS1EXT1.ulimit, &geo);
 
 	fdasd_dequeue_old_partition (anc, part_info, part_id-1);
 	anc->used_partitions -= 1;
 
-	if (anc->used_partitions != 0) {
-		i = anc->used_partitions + 2;
-		if (anc->big_disk) i++;
-
-        	vtoc_set_cchhb(&hf1, VTOC_START_CC, VTOC_START_HH, i);
-	}
+	if (anc->used_partitions != 0)
+		get_addr_of_highest_f1_f8_label(anc, &hf1);
 	else
 		bzero(&hf1, sizeof(struct cchhb));
 
 	vtoc_update_format4_label(anc->f4, &hf1, anc->f4->DS4DSREC + 1);
 	vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '+', anc->verbose,
-			   start, stop, geo.cylinders, geo.heads);
+			   start, stop, anc->formatted_cylinders, geo.heads);
 
 	anc->vtoc_changed++;
 }
@@ -2356,8 +2488,8 @@ fdasd_auto_partition(fdasd_anchor_t *anc
 	cchhb_t hf1;
 	extent_t ext;
 	char volser[VOLSER_LENGTH + 1];
-	u_int16_t cyl, head;
-	int i;
+	u_int32_t cyl;
+	u_int16_t head;
 	char old_volser[VOLSER_LENGTH + 1];
 
 	if (!anc->silent)
@@ -2390,18 +2522,14 @@ fdasd_auto_partition(fdasd_anchor_t *anc
 
 	if (anc->verbose) printf("initializing labels...\n");
 	vtoc_init_format4_label(anc->f4, USABLE_PARTITIONS,
-				geo.cylinders, geo.heads, geo.sectors,
+				geo.cylinders, anc->formatted_cylinders,
+				geo.heads, geo.sectors,
 				anc->blksize, anc->dev_type);
 
 	vtoc_init_format5_label(anc->f5);
 	vtoc_init_format7_label(anc->f7);
 
-	if (anc->f4->DS4DEVCT.DS4DEVFG & ALTERNATE_CYLINDERS_USED)
-		cyl = anc->f4->DS4DEVCT.DS4DSCYL -
-			(u_int16_t) anc->f4->DS4DEVAC;
-	else
-		cyl = anc->f4->DS4DEVCT.DS4DSCYL;
-
+	cyl = get_usable_cylinders(anc);
 	head = anc->f4->DS4DEVCT.DS4DSTRK;
 
 	part_info->used       = 0x01;
@@ -2415,22 +2543,23 @@ fdasd_auto_partition(fdasd_anchor_t *anc
 
 	vtoc_set_extent(&ext, 0x01, 0x00, &llimit, &ulimit);
 
-	vtoc_init_format1_label(anc->vlabel->volid, anc->blksize, &ext,
-				part_info->f1);
-	i = 3;
-	if (anc->big_disk) i++;
-
-        vtoc_set_cchhb(&hf1, VTOC_START_CC, VTOC_START_HH, i);
-	vtoc_update_format4_label(anc->f4, &hf1, anc->f4->DS4DSREC - 1);
-
+	if (anc->formatted_cylinders > LV_COMPAT_CYL) {
+		vtoc_init_format8_label(anc->vlabel->volid, anc->blksize, &ext,
+					part_info->f1);
+	} else
+		vtoc_init_format1_label(anc->vlabel->volid, anc->blksize, &ext,
+					part_info->f1);
         anc->fspace_trk      = 0;
 	anc->used_partitions = 1;
 
+	get_addr_of_highest_f1_f8_label(anc, &hf1);
+	vtoc_update_format4_label(anc->f4, &hf1, anc->f4->DS4DSREC - 1);
+
 	anc->vlabel_changed++;
 	anc->vtoc_changed++;
 
 	fdasd_write_labels(anc);
-	fdasd_exit(anc, 0);		    
+	fdasd_exit(anc, 0);
 }
 
 
@@ -2443,7 +2572,6 @@ fdasd_auto_partition_conffile(fdasd_anch
 	volume_label_t *vlabel = anc->vlabel;
 	partition_info_t *part_info = anc->first;
 	char volser[VOLSER_LENGTH + 1];
-	int i;
 	cchh_t llimit,ulimit;
 	unsigned long start, stop;
 	extent_t ext;
@@ -2477,7 +2605,8 @@ fdasd_auto_partition_conffile(fdasd_anch
 
 	if (anc->verbose) printf("initializing labels...\n");
 	vtoc_init_format4_label(anc->f4, USABLE_PARTITIONS,
-				geo.cylinders, geo.heads, geo.sectors,
+				geo.cylinders, anc->formatted_cylinders,
+				geo.heads, geo.sectors,
 				anc->blksize, anc->dev_type);
 
 	vtoc_init_format5_label(anc->f5);
@@ -2486,10 +2615,10 @@ fdasd_auto_partition_conffile(fdasd_anch
 	if (anc->fspace_trk != 0) {
 		start = FIRST_USABLE_TRK;
 		stop  = start + anc->fspace_trk - 1;
-				
-		vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '+', 
+
+		vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '+',
 				   anc->verbose, start, stop,
-				   geo.cylinders, geo.heads);
+				   anc->formatted_cylinders, geo.heads);
 	}
 
 	do {
@@ -2497,35 +2626,35 @@ fdasd_auto_partition_conffile(fdasd_anch
 			continue;
 
 		vtoc_set_cchh(&llimit,
-			      part_info->start_trk / geo.heads, 
+			      part_info->start_trk / geo.heads,
 			      part_info->start_trk % geo.heads);
-		vtoc_set_cchh(&ulimit, 
-			      part_info->end_trk / geo.heads, 
+		vtoc_set_cchh(&ulimit,
+			      part_info->end_trk / geo.heads,
 			      part_info->end_trk % geo.heads);
 
-		vtoc_set_extent(&ext, (llimit.hh == 0 ? 0x81 : 0x01), 
+		vtoc_set_extent(&ext, (vtoc_get_head_from_cchh(&llimit) == 0
+				       ? 0x81 : 0x01),
 				0x00, &llimit, &ulimit);
 
-		vtoc_init_format1_label(vlabel->volid,
-					anc->blksize, &ext, part_info->f1);
-
+		if (anc->formatted_cylinders > LV_COMPAT_CYL) {
+			vtoc_init_format8_label(vlabel->volid, anc->blksize,
+						&ext, part_info->f1);
+		} else
+			vtoc_init_format1_label(vlabel->volid, anc->blksize,
+						&ext, part_info->f1);
 		anc->used_partitions += 1;
 
-		i = anc->used_partitions + 2;
-		if (anc->big_disk) i++;
-
-		vtoc_set_cchhb(&hf1, VTOC_START_CC, VTOC_START_HH, i);
-
+		get_addr_of_highest_f1_f8_label(anc, &hf1);
 		vtoc_update_format4_label(anc->f4, &hf1,anc->f4->DS4DSREC - 1);
 
 		/* update free space labels */
 		if (part_info->fspace_trk != 0) {
 			start = part_info->end_trk + 1;
 			stop  = start + part_info->fspace_trk -1;
-				
+
 			vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '+', 
 					   anc->verbose, start, stop,
-					   geo.cylinders, geo.heads);
+					   anc->formatted_cylinders, geo.heads);
 		}
 	} while ((part_info = part_info->next) != NULL);
 
@@ -2577,20 +2706,24 @@ main(int argc, char *argv[]) 
 	fdasd_get_geometry(&anchor);
 	fdasd_check_disk_access(&anchor);
 
-	if ((geo.cylinders * geo.heads) > BIG_DISK_SIZE) anchor.big_disk++;
+	/* check dasd for labels and vtoc */
+	rc = fdasd_check_volume(&anchor);
+
+	if ((anchor.formatted_cylinders * geo.heads) > BIG_DISK_SIZE)
+		anchor.big_disk++;
 
-	if (anchor.auto_partition)
+	if (anchor.auto_partition) {
+		fdasd_recreate_vtoc_unconditional(&anchor);
 		fdasd_auto_partition(&anchor);
+	}
 
 	if (options.conffile) {
+		fdasd_recreate_vtoc_unconditional(&anchor);
 		fdasd_parse_conffile(&anchor, &options);
 		fdasd_check_conffile_input(&anchor, &options);
 		fdasd_auto_partition_conffile(&anchor);
 	}
 
-	/* check dasd for labels and vtoc */
-	rc = fdasd_check_volume(&anchor);
-
 	if (anchor.print_table) {
 		if (rc == 0)
 			fdasd_list_partition_table(&anchor);
Index: s390-tools-sles11/fdasd/fdasd.h
===================================================================
--- s390-tools-sles11.orig/fdasd/fdasd.h
+++ s390-tools-sles11/fdasd/fdasd.h
@@ -48,25 +48,15 @@ typedef struct dasd_information_t {
 
 
 /*****************************************************************************
- * SECTION: Further IOCTL Definitions  (see fs.h)                            *
+ * SECTION: Further IOCTL Definitions  (see fs.h and hdreq.h)                *
  *****************************************************************************/
 #define BLKROGET   _IO(0x12,94) /* get read-only status (0 = read_write) */
 #define BLKRRPART  _IO(0x12,95) /* re-read partition table */
 #define BLKSSZGET  _IO(0x12,104)/* get block device sector size */
-
-/*****************************************************************************
- * SECTION: Definition from hdreq.h                                          *
- *****************************************************************************/
-
-struct hd_geometry {
-      unsigned char heads;
-      unsigned char sectors;
-      unsigned short cylinders;
-      unsigned long start;
-};
+#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* device size in bytes (u64 *arg)*/
 
 /* get device geometry */
-#define HDIO_GETGEO		0x0301	
+#define HDIO_GETGEO		0x0301
 
 /*****************************************************************************
  * SECTION: FDASD internal types                                             *
@@ -119,7 +109,7 @@ typedef struct partition_info {
         unsigned long      end_trk;
         unsigned long      len_trk;
         unsigned long      fspace_trk;
-        format1_label_t  * f1; 
+        format1_label_t  * f1;
         struct partition_info *next;
         struct partition_info *prev;
 } partition_info_t;
@@ -153,10 +143,13 @@ typedef struct fdasd_anchor {
         format4_label_t  *f4;
         format5_label_t  *f5;
 	format7_label_t  *f7;
+	format9_label_t  *f9; /* template for all f9 labels */
         partition_info_t *first;
         partition_info_t *last;
         volume_label_t   *vlabel;
 	config_data_t confdata[USABLE_PARTITIONS];
+	u_int32_t hw_cylinders;
+	u_int32_t formatted_cylinders;
 } fdasd_anchor_t;
 
 enum offset {lower, upper};
@@ -179,11 +172,6 @@ enum fdasd_failure {
 	volser_not_found
 };
 
-/* global variables */
-struct hd_geometry geo;
-char line_buffer[LINE_LENGTH];
-char *line_ptr = line_buffer;
-
 #define ERROR_STRING_SIZE 1024
 #define INPUT_BUF_SIZE 1024
 
Index: s390-tools-sles11/include/vtoc.h
===================================================================
--- s390-tools-sles11.orig/include/vtoc.h
+++ s390-tools-sles11/include/vtoc.h
@@ -42,9 +42,17 @@
 
 #define VOLSER_LENGTH 6
 #define BIG_DISK_SIZE 0x10000
+#define LV_COMPAT_CYL 0xFFFE
 
 #define VTOC_ERROR "VTOC error:"
 
+/* definition from hdreq.h */
+struct hd_geometry {
+      unsigned char heads;
+      unsigned char sectors;
+      unsigned short cylinders;
+      unsigned long start;
+};
 
 typedef struct ttr 
 {
@@ -71,8 +79,19 @@ typedef struct labeldate 
         u_int16_t day;
 } __attribute__ ((packed)) labeldate_t;
 
-
-typedef struct volume_label 
+/*
+ * The following structure is a merger of the cdl and ldl volume label.
+ * On an ldl disk there is no key information, so when reading an
+ * ldl label from disk, the data should be copied at the address of vollbl.
+ * On the other side, the field ldl_version is reserved in a cdl record
+ * and the field formatted_cyl exists only for ldl labels. So when
+ * reading a cdl label from disk, the formatted_cyl field will contain
+ * arbitrary data.
+ * This layout may be a bit awkward, but the advantage of having the
+ * same label type for both disk layout types is bigger than the effort
+ * for taking a bit of extra care at the fringes.
+ */
+typedef struct volume_label
 {
         char volkey[4];         /* volume key = volume label                 */
 	char vollbl[4];	        /* volume label                              */
@@ -86,10 +105,11 @@ typedef struct volume_label 
 	char labperci[4];       /* no of labels per CI (FBA), blanks for CKD */
 	char res2[4];	        /* reserved                                  */
 	char lvtoc[14];	        /* owner code for LVTOC                      */
-	char res3[29];	        /* reserved                                  */
+	char res3[28];	        /* reserved                                  */
+ 	char ldl_version;	/* version number, valid for ldl format      */
+ 	unsigned long long formatted_blocks; /* valid when ldl_version >= f2 */
 } __attribute__ ((packed)) volume_label_t;
 
-
 typedef struct extent 
 {
         u_int8_t  typeind;          /* extent type indicator                     */
@@ -113,8 +133,11 @@ typedef struct dev_const 
         u_int8_t  DS4DEVDB;           /* number of directory blocks per track    */
 } __attribute__ ((packed)) dev_const_t;
 
-
-typedef struct format1_label 
+/*
+ * format 1 and format 8 label have the same layout so we use the following
+ * structure for both.
+ */
+typedef struct format1_label
 {
 	char  DS1DSNAM[44];       /* data set name                           */
 	u_int8_t  DS1FMTID;       /* format identifier                       */
@@ -123,27 +146,27 @@ typedef struct format1_label 
 	labeldate_t DS1CREDT;     /* creation date: ydd                      */
 	labeldate_t DS1EXPDT;     /* expiration date                         */
 	u_int8_t  DS1NOEPV;           /* number of extents on volume             */
-        u_int8_t  DS1NOBDB;           /* no. of bytes used in last direction blk */
+	u_int8_t  DS1NOBDB;           /* no. of bytes used in last direction blk */
 	u_int8_t  DS1FLAG1;           /* flag 1                                  */
 	unsigned char  DS1SYSCD[13];  /* system code                         */
 	labeldate_t DS1REFD;      /* date last referenced                    */
-        u_int8_t  DS1SMSFG;           /* system managed storage indicators       */
-        u_int8_t  DS1SCXTF;           /* sec. space extension flag byte          */
-        u_int16_t DS1SCXTV;           /* secondary space extension value         */
-        u_int8_t  DS1DSRG1;           /* data set organisation byte 1            */
-        u_int8_t  DS1DSRG2;           /* data set organisation byte 2            */
-  	u_int8_t  DS1RECFM;           /* record format                           */
+	u_int8_t  DS1SMSFG;           /* system managed storage indicators       */
+	u_int8_t  DS1SCXTF;           /* sec. space extension flag byte          */
+	u_int16_t DS1SCXTV;           /* secondary space extension value         */
+	u_int8_t  DS1DSRG1;           /* data set organisation byte 1            */
+	u_int8_t  DS1DSRG2;           /* data set organisation byte 2            */
+	u_int8_t  DS1RECFM;           /* record format                           */
 	u_int8_t  DS1OPTCD;           /* option code                             */
 	u_int16_t DS1BLKL;            /* block length                            */
 	u_int16_t DS1LRECL;           /* record length                           */
 	u_int8_t  DS1KEYL;            /* key length                              */
 	u_int16_t DS1RKP;             /* relative key position                   */
 	u_int8_t  DS1DSIND;           /* data set indicators                     */
-        u_int8_t  DS1SCAL1;           /* secondary allocation flag byte          */
-  	char DS1SCAL3[3];         /* secondary allocation quantity           */
+	u_int8_t  DS1SCAL1;           /* secondary allocation flag byte          */
+	char DS1SCAL3[3];         /* secondary allocation quantity           */
 	ttr_t DS1LSTAR;           /* last used track and block on track      */
 	u_int16_t DS1TRBAL;           /* space remaining on last used track      */
-        u_int16_t res1;               /* reserved                                */
+	u_int16_t res1;               /* reserved                                */
 	extent_t DS1EXT1;         /* first extent description                */
 	extent_t DS1EXT2;         /* second extent description               */
 	extent_t DS1EXT3;         /* third extent description                */
@@ -175,9 +198,13 @@ typedef struct format4_label 
         char DS4F6PTR[5];         /* pointer to first format 6 DSCB          */
         extent_t DS4VTOCE;        /* VTOC extent description                 */
         char res2[10];            /* reserved                                */
-        u_int8_t DS4EFLVL;            /* extended free-space management level    */
+        u_int8_t DS4EFLVL;        /* extended free-space management level    */
         cchhb_t DS4EFPTR;         /* pointer to extended free-space info     */
-        char res3[9];             /* reserved                                */
+	char res3;		  /* reserved */
+	u_int32_t DS4DCYL;	  /* number of logical cyls */
+	char res4[2];		  /* reserved */
+        u_int8_t DS4DEVF2;        /* device flags */
+	char res5;		  /* reserved */
 } __attribute__ ((packed)) format4_label_t;
 
 
@@ -218,6 +245,16 @@ typedef struct format7_label 
 } __attribute__ ((packed)) format7_label_t;
 
 
+typedef struct format9_label
+{
+	u_int8_t  DS9KEYID;       /* key code for format 9 labels (0x09) */
+	u_int8_t  DS9SUBTY;       /* subtype (0x01) */
+	u_int8_t  DS9NUMF9;       /* number of F9 datasets  */
+	u_int8_t  res1[41];       /* reserved  */
+	u_int8_t  DS9FMTID;       /* format identifier  */
+	u_int8_t  res2[95];       /* reserved */
+} __attribute__ ((packed)) format9_label_t;
+
 char * vtoc_ebcdic_enc (char *source, char *target, int l);
 char * vtoc_ebcdic_dec (char *source, char *target, int l);
 void vtoc_set_extent (
@@ -228,13 +265,21 @@ void vtoc_set_extent (
         cchh_t * upper);
 void vtoc_set_cchh (
         cchh_t * addr,
-	u_int16_t cc,
+	u_int32_t cc,
 	u_int16_t hh);
+u_int32_t vtoc_get_cyl_from_cchh(cchh_t *addr);
+u_int16_t vtoc_get_head_from_cchh(cchh_t *addr);
 void vtoc_set_cchhb (
         cchhb_t * addr,
-        u_int16_t cc,
+        u_int32_t cc,
         u_int16_t hh,
         u_int8_t b);
+u_int32_t vtoc_get_cyl_from_cchhb(cchhb_t *addr);
+u_int16_t vtoc_get_head_from_cchhb(cchhb_t *addr);
+u_int64_t cchhb2blk(cchhb_t *p, struct hd_geometry *geo);
+u_int64_t cchh2blk (cchh_t *p, struct hd_geometry *geo);
+u_int32_t cchh2trk (cchh_t *p, struct hd_geometry *geo);
+
 void vtoc_set_date (
         labeldate_t * d,
         u_int8_t year,
@@ -287,7 +332,8 @@ void vtoc_write_label (
         format1_label_t *f1,
 	format4_label_t *f4,
 	format5_label_t *f5,
-	format7_label_t *f7);
+	format7_label_t *f7,
+	format9_label_t *f9);
 
 
 void vtoc_init_format1_label (
@@ -296,11 +342,11 @@ void vtoc_init_format1_label (
         extent_t *part_extent,
         format1_label_t *f1);
 
-
 void vtoc_init_format4_label (
         format4_label_t *f4lbl,
 	unsigned int usable_partitions,
-	unsigned int cylinders,
+	unsigned int compat_cylinders,
+	unsigned int real_cylinders,
 	unsigned int tracks,
 	unsigned int blocks,
 	unsigned int blksize,
@@ -311,7 +357,6 @@ void vtoc_update_format4_label (
 	cchhb_t *highest_f1,
 	u_int16_t unused_update);
 
-
 void vtoc_init_format5_label (
 	format5_label_t *f5);
 
@@ -323,7 +368,7 @@ void vtoc_update_format5_label_add (
 	u_int16_t a, 
 	u_int16_t b, 
 	u_int8_t c);
- 
+
 void vtoc_update_format5_label_del (
 	format5_label_t *f5,
 	int verbose,
@@ -333,7 +378,6 @@ void vtoc_update_format5_label_del (
 	u_int16_t b, 
 	u_int8_t c);
 
-
 void vtoc_init_format7_label (
 	format7_label_t *f7);
 
@@ -349,6 +393,18 @@ void vtoc_update_format7_label_del (
 	u_int32_t a, 
 	u_int32_t b);
 
+void vtoc_init_format8_label (
+        char *volid,
+        unsigned int blksize,
+        extent_t *part_extent,
+        format1_label_t *f1);
+
+void vtoc_update_format8_label (
+	cchhb_t *associated_f9,
+	format1_label_t *f8);
+
+void vtoc_init_format9_label (
+	format9_label_t *f9);
 
 void vtoc_set_freespace(
 	format4_label_t *f4,
@@ -358,8 +414,8 @@ void vtoc_set_freespace(
 	int verbose,
 	u_int32_t start,
 	u_int32_t stop,
-	int cyl,
-	int trk);
+	u_int32_t cyl,
+	u_int32_t trk);
 
 
 #endif /* VTOC_H */
Index: s390-tools-sles11/iucvterm/Makefile
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/Makefile
@@ -0,0 +1,28 @@
+#! /usr/bin/make -f
+
+include ../common.mak
+
+
+GETTEXT_TEXTDOMAIN = iucvterm
+export GETTEXT_TEXTDOMAIN
+
+
+SUBDIRS = src po doc bin etc test
+RECURSIVE_TARGETS = all-recursive install-recursive clean-recursive \
+                    check-recursive
+
+
+all: all-recursive
+check: check-recursive
+install: all install-recursive
+clean: clean-recursive
+
+
+$(RECURSIVE_TARGETS):
+	@target=`echo $@ |sed s/-recursive//`; \
+	for d in $(SUBDIRS); do \
+		(cd $$d && $(MAKE) $$target) \
+	done
+
+
+.PHONY: install clean
Index: s390-tools-sles11/iucvterm/bin/Makefile
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/bin/Makefile
@@ -0,0 +1,42 @@
+#! /usr/bin/make -f
+
+include ../../common.mak
+
+
+BIN_SCRIPTS = ts-shell
+SBIN_SCRIPTS = chiucvallow
+
+SCRIPTS = $(BIN_SCRIPTS) $(SBIN_SCRIPTS)
+
+
+all:
+check:
+install: $(SCRIPTS)
+	for prg in $(BIN_SCRIPTS); do \
+	  $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $$prg $(USRBINDIR) ; \
+	done
+	for prg in $(SBIN_SCRIPTS); do \
+	  $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $$prg $(USRSBINDIR) ; \
+	done
+	$(INSTALL) -g $(GROUP) -o $(OWNER) -d $(INSTROOT)/var/log/ts-shell
+	ln -f -s chiucvallow $(USRSBINDIR)/lsiucvallow
+
+clean:
+	-rm -f *.o $(SCRIPTS)
+
+
+%: %.in
+	real_usrbin=`echo $(USRBINDIR) |sed 's#^$(INSTROOT)##'` ; \
+	my_sysconf=`echo $(SYSCONFDIR)/iucvterm |sed 's#^$(INSTROOT)##'` ; \
+	cat $< \
+	  |sed -e "s#@sysconf_path@#$$my_sysconf#g" \
+	       -e "s#@iucvconn_path@#$$real_usrbin#g" \
+	       -e 's#@S390_TOOLS_RELEASE@#$(S390_TOOLS_RELEASE)#g' \
+	> $@
+
+perl: ts-shell
+	perl -c $<
+
+scripts: $(SCRIPTS)
+
+.PHONY: install clean perl
Index: s390-tools-sles11/iucvterm/bin/chiucvallow.in
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/bin/chiucvallow.in
@@ -0,0 +1,273 @@
+#!/bin/sh
+#
+# z/VM IUCV HVC device driver -- Edit z/VM user ID filter
+#
+#
+# Copyright IBM Corp. 2009.
+# Author: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+#
+PRG=`basename $0`
+
+# sysfs file that contains the z/VM user ID filter for the
+# z/VM IUCV HVC device driver
+sysfs_hvc_iucv_allow="/sys/module/kernel/parameters/hvc_iucv_allow"
+# older kernel versions do not have the "kernel" directory
+test -f $sysfs_hvc_iucv_allow || \
+	sysfs_hvc_iucv_allow="/sys/module/hvc_iucv/parameters/hvc_iucv_allow"
+
+
+show_help(){
+	cat <<"EoHelp"
+Usage:
+  chiucvallow [-h|--help] [-v|--version]
+
+  chiucvallow -l|--list
+  chiucvallow -c|--clear
+  chiucvallow -e|--edit  [<filter file>]
+  chiucvallow -s|--set    <filter file>
+  chiucvallow -V|--verify <filter file>
+
+
+Options:
+  -h|--help                     Print help information, then exit.
+  -v|--version                  Print version information, then exit.
+
+  -l|--list                     List current z/VM user ID filter.
+  -c|--clear                    Clear the z/VM user ID filter.
+  -e|--edit  [<filter file>]    Edit the z/VM user ID filter.
+  -s|--set    <filter file>     Set the z/VM user ID filter from a filter file.
+  -V|--verify <filter file>     Verify the z/VM user ID filter file.
+
+  The list, edit, set and clear options require root authority.
+EoHelp
+}
+
+show_version(){
+	cat <<EoVersion
+chiucvallow: List and change the z/VM user ID filter, version @S390_TOOLS_RELEASE@
+Copyright IBM Corp. 2009
+EoVersion
+}
+
+error(){
+	echo "$PRG: $1" >&2
+	exit ${2:-1}
+}
+
+hvciucv_available(){
+	test -r "$sysfs_hvc_iucv_allow" \
+		&& cat "$sysfs_hvc_iucv_allow" >/dev/null 2>&1
+	if test $? -gt 0; then
+		cat >&2 <<EoM
+$PRG: Failed to read z/VM user ID filter: $sysfs_hvc_iucv_allow
+Ensure that your Linux kernel supports the z/VM IUCV HVC device driver.
+EoM
+		exit 1
+	fi
+	return 0
+}
+
+
+#
+# List z/VM user ID filter information
+list_filter(){
+	cat $sysfs_hvc_iucv_allow |tr ',' '\n'
+}
+
+#
+# Verify z/VM user ID filter file
+verify_filter(){
+	local filename=$1
+	local failed=0
+	local count=0
+	local fsize=0
+
+	test -r "$filename" \
+		|| error "Failed to read z/VM user ID filter: $filename"
+
+	# check size... sysfs uses PAGE_SIZE and the filter should be below that
+	fsize=`grep -vE '^(\s*$|#)' $filename 2>/dev/null |wc -c`
+	if test "$fsize" -gt 4095; then
+		printf "$PRG: The z/VM user ID filter exceeds the maximum size (%d of %d bytes)\n" \
+			$fsize 4095 >&2
+		return -1
+	fi
+
+	while read userid; do
+		# skip empty lines and lines starting with '#'
+		echo "$userid" |grep -q -E '^(\s*$|#)' && continue
+
+		printf "Verify z/VM user ID: %-8s : " "$userid"
+		if echo -n "$userid" |grep -q -E -i '^[_$[:alnum:]]{1,8}$'; then
+			printf "OK\n"
+		else
+			printf "FAILED\n"
+			failed=$((failed + 1))
+		fi
+
+		count=$((count + 1))
+	done <$filename
+
+	test $count -gt 500 && \
+		error "The z/VM user ID filter exceeds the maximum of 500 user IDs"
+
+	printf "\n$PRG: Verification summary: verified=%d failed=%d size=%d bytes\n" \
+		$count $failed $fsize
+	return $failed
+}
+
+#
+# Edit the z/VM user ID filter
+edit_filter(){
+	local fromfile="$1"
+	local context=$2
+	local tmpfile=`mktemp /tmp/hvc_iucv_allow.XXXXXX`
+	local md5file=`mktemp /tmp/hvc_iucv_allow.md5.XXXXXX`
+
+	if test -w $tmpfile && test -w $md5file; then :; else
+		error "Creating temporary files failed"
+	fi
+
+	# save list in temp file
+	if test -r "$fromfile"; then cat $fromfile; else list_filter; fi > $tmpfile
+
+	# check whether to open editor
+	if test "x$context" != xnoeditor; then
+		md5sum $tmpfile > $md5file	# save checksum to track changes
+		${EDITOR:-vi} $tmpfile		# open editor
+		if md5sum --status -c $md5file; then
+			cat <<EoM
+$PRG: The filter is not updated because it has not been changed
+EoM
+			rm -f $tmpfile $md5file
+			exit 0
+		fi
+		rm -f $md5file
+	fi
+
+	# verify temp file content and, if successful, write back
+	if verify_filter $tmpfile >/dev/null; then :; else
+		cat >&2 <<EoM
+$PRG: Verifying the z/VM user ID filter failed.
+A backup copy has been saved as '$tmpfile'.
+Consider verifying the file using:
+    $0 --verify $tmpfile
+Correct the problem by editing the filter:
+    $0 --edit $tmpfile
+EoM
+	exit 2
+	fi
+
+	# Post-process and write z/VM user ID filter to sysfs file
+	#	1. skip empty and comment lines,
+	#	2. convert lower to upper case,
+	#	3. sort and remove duplicates,
+	#	4. replace '\n' with ',' and
+	#	5. and remove the last ','.
+	grep -Ev '^(\s*$|#)' $tmpfile \
+		|tr '[:lower:]' '[:upper:]' \
+		|sort -u \
+		|tr '\n' ',' \
+		|sed -e 's#,$##' > $sysfs_hvc_iucv_allow
+
+	if test x$? != x0; then
+		cat >&2 <<EoM
+$PRG: Writing the filter failed. Check for previous error messages.
+A backup copy has been saved as '$tmpfile'.
+Consider re-editing the file using \`$0 --edit $tmpfile'.
+EoM
+		exit 3
+	else
+		test "x$context" != xnoeditor && \
+			printf "$PRG: The filter has been updated successfully\n"
+	fi
+
+	# finally remove temporary files
+	rm -f $tmpfile $md5file 2>/dev/null
+	exit 0
+}
+
+#
+# Clear z/VM user ID filter
+clear_filter(){
+	echo > $sysfs_hvc_iucv_allow
+}
+
+#
+# Check whether we run as root, otherwise complain and exit
+for_root_only(){
+	local euid=`id -u 2>/dev/null`
+
+	test "x$euid" == x0 && return
+	error "You need root authority to use option '$1'"
+}
+
+lock_operation(){
+	( flock -nx 21 \
+		|| error "The filter is currently being changed. Try again later."
+		$@
+	) 21>/var/lock/hvc_iucv_allow
+}
+
+
+# Common options
+case $1 in
+	-h|--help)
+		show_help
+		exit 0
+	;;
+	-v|--version)
+		show_version
+		exit 0
+	;;
+esac
+
+# check the name under which we have been called
+case `basename $0` in
+	lsiucvallow) exec chiucvallow --list ;;
+esac
+
+# chiucvallow program options
+case $1 in
+	-l|--list)
+		for_root_only "$1"
+		hvciucv_available
+		list_filter
+	;;
+	-e|--edit)
+		for_root_only "$1"
+		hvciucv_available
+		lock_operation edit_filter "$2"
+	;;
+	-c|--clear)
+		for_root_only "$1"
+		hvciucv_available
+		lock_operation clear_filter
+	;;
+	-s|--set)
+		for_root_only "$1"
+		test -n "$2" \
+			|| error "This option requires a file as argument"
+		test -r "$2" \
+			|| error "The specified file must be readable"
+		hvciucv_available
+		lock_operation edit_filter "$2" noeditor
+	;;
+	-V|--verify)
+		test -n "$2" \
+			|| error "This option requires a file as argument"
+		verify_filter "$2"
+		exit $?
+	;;
+	'')
+		echo "$PRG: One or more arguments are missing" >&2
+		echo "Try '$0 --help' for more information." >&2
+		exit 201
+	;;
+	*)
+		echo "$PRG: Invalid option -- '$1'" >&2
+		echo "Try '$0 --help' for more information." >&2
+		exit 201
+	;;
+esac
+exit 0
Index: s390-tools-sles11/iucvterm/bin/ts-shell.in
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/bin/ts-shell.in
@@ -0,0 +1,669 @@
+#! /usr/bin/perl -W
+#
+# ts-shell - Simple terminal server shell to access systems over IUCV
+#
+# This script can be used as login shell for users to restrict
+# IUCV-based terminal access to other systems.
+#
+# Copyright IBM Corp. 2008, 2009.
+# Author: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+#
+use strict;
+use warnings;
+use File::Basename;
+use Getopt::Long qw(:config no_ignore_case);
+use Term::ReadLine;
+use POSIX;
+
+
+$ENV{'PERL_RL'} = " o=0";   # use best avail. readline
+$ENV{'PATH'} = "/bin:/sbin:/usr/bin:/usr/sbin";
+$ENV{'LESSSECURE'} = 1;     # let less run in "secure" mode
+$ENV{'PAGER'} = $ENV{'PAGER'} || "/usr/bin/less";
+my $ts_shell = fileparse($0, qr/\.[^.]+/);
+$SIG{__WARN__} = sub { print STDERR "$ts_shell: $_[0]"; };
+$SIG{__DIE__}  = sub { print STDERR "$ts_shell: $_[0]"; exit 255; };
+
+# Terminal server configuration settings
+my %config = (
+	# general options
+	'conffile'	=>	"@sysconf_path@/ts-shell.conf",
+	'authfile'	=>	"@sysconf_path@/ts-authorization.conf",
+	'sysfile'	=>	"@sysconf_path@/ts-systems.conf",
+	'auditfile'	=>	"@sysconf_path@/ts-logsys.conf",
+	'auditdir'	=>	"/var/log/ts-shell",
+	'iucvconn'	=>	"@iucvconn_path@/iucvconn",
+	'prompt'	=>	getpwuid($>) . '@'."$ts_shell> ",
+	# runtime options
+	'rl'		=>	undef,		# terminal readline (rl)
+	'user'		=>	getpwuid($>),	# user name
+	'groups'	=>	[],		# user groups (list ref)
+	'service'	=>	"lnxhvc0",	# default service
+	'auth_func'	=>	sub { return -1; },	# no authorization
+	'regex'		=>	[],		# re to match systems (list ref)
+	'systems'	=>	[],		# system list (list reference)
+	'services'      =>      [qw/lnxhvc0/],	# services list (completion)
+	'termsys'	=>	{},		# global ts sys list (hash ref)
+	'auditsys'	=>	{},		# audit ts sys list (hash ref)
+);
+
+
+sub main();
+sub help();
+sub usage();
+sub version();
+sub intro();
+sub auth_global($$);
+sub auth_regex($$);
+sub auth_list($$);
+sub get_auditlog_file($$$);
+sub cmd_connect($\%);
+sub cmd_service($\%);
+sub cmd_list(\%);
+sub rl_cmd_completion($$$);
+sub list_regex_match($);
+sub updateConfiguration(\%$);
+sub loadAuthorization(\%);
+sub readFile($$);
+sub log_debug($);
+sub log_error($);
+sub log_info($);
+sub pager($);
+
+
+# main() - Terminal server program
+#
+# The program loads the system authorizations for the effective user id,
+# and then initializes the term readline environment and starts the shell.
+#
+sub main()
+{
+	unless (GetOptions("v|version" => sub { version(); exit 0; },
+	 		   "h|help"    => sub { usage();   exit 0; })) {
+		log_error "Enter '$ts_shell --help' for more information";
+		exit 1;
+	}
+
+	unless (-t STDIN) {
+		log_error "The $ts_shell requires a terminal to run on";
+		exit 1;
+	}
+
+	unless ($config{user}) {
+		log_error "Resolving the name of user ID $> failed";
+		exit 2;
+	}
+
+	# update ts-shell configuration
+	unless (readFile($config{conffile},
+			sub { updateConfiguration(%config, $_); })) {
+		log_error "Reading the ts-shell configuration file " .
+			  "$config{conffile} failed: $!";
+		exit 3;
+	}
+
+	# load list of systems the ts-shell is allowed to use; and
+	# hash the systems names in uppercase
+	unless (readFile($config{sysfile},
+			 sub { $config{termsys}->{uc $_} = 1; })) {
+		log_error "Reading $config{sysfile} failed: $!";
+		exit 4;
+	}
+
+	# load list of systems for for that auditing should be enabled
+	# hash the system names in uppercase
+	unless (readFile($config{auditfile},
+			 sub { $config{auditsys}->{uc $_} = 1; })) {
+		log_error "Reading $config{auditsys} failed: $!";
+		exit 5;
+	}
+
+	# retrieve user group membership and load authorizations
+	my %uniq_groups = ();
+	# $) returns egid twice, so we hash group names to avoid dups
+	foreach(map {$_ = getgrgid($_)} split / /, $)) {
+		$uniq_groups{$_} = 1;
+	}
+	push @{$config{groups}}, keys %uniq_groups;
+
+	# finally, load user specific authorization data from file
+	unless (loadAuthorization(%config)) {
+		log_error "Reading $config{authfile} failed: $!";
+		exit 5;
+	}
+
+	# set up terminal readline
+	$config{rl} = new Term::ReadLine 'Terminal Server Shell';
+	$config{rl}->Attribs->{'completion_function'} = \&rl_cmd_completion;
+	select ($config{rl}->OUT || \*STDOUT);
+
+	# setup signal handler to ignore SIGINT
+	my $sigint = POSIX::SigAction->new(sub { return 1; },
+					   POSIX::SigSet->new(),
+					   &POSIX::SA_NODEFER);
+	POSIX::sigaction(&POSIX::SIGINT, $sigint);
+
+	log_debug "User: $config{user} / Groups: $) [@{$config{groups}}]";
+	log_debug("Using Term::ReadLine backend: " . $config{rl}->ReadLine);
+
+	intro();
+	SHELL: while (defined($_ = $config{rl}->readline($config{prompt}))) {
+		chomp;
+		s/^\s+|\s+$//;
+		next if /^$/;
+		SWITCH: {
+			/^(?:q|quit|exit)/  and last SHELL;
+			/^help/		    and help(), last SWITCH;
+			/^version/	    and version(), last SWITCH;
+			/^(?:list|ls)/	    and cmd_list(%config), last SWITCH;
+			/^terminal\s*(\w*)/ and cmd_service($1, %config), last SWITCH;
+
+			if (/^connect\s*(\w*$|\w+\s+\w+)/) {
+				cmd_connect($1, %config);
+				last SWITCH;
+			}
+
+			log_error "$_ is not a known command";
+		}
+		$config{rl}->addhistory($_)
+			if (/\S/ && !$config{rl}->Features->{autohistory});
+	}
+	exit 0;
+}
+
+# updateConfiguration() - Load ts-shell configuration from ts-shell.conf
+#
+# %cfg:		Hash reference to terminal server configuration
+# $line:	Configuration line (key = value pair)
+#
+# The routine updates the ts-shell configuration based on configuration lines
+# from the ts-shell.conf file.
+#
+sub updateConfiguration(\%$)
+{
+	my ($cfg, $line) = @_;
+	my ($option, $value) = split /\s*=\s*/, $line;
+
+	$cfg->{sysfile}   = $value if $option =~ /^ts-systems$/;
+	$cfg->{authfile}  = $value if $option =~ /^ts-authorization$/;
+	$cfg->{auditdir}  = $value if $option =~ /^transcript-directory$/;
+	$cfg->{auditfile} = $value if $option =~ /^transcript-systems$/;
+}
+
+# loadAuthorization() - Load system authorizations from file
+#
+# $cfg:		Hash reference to terminal server configuration
+#
+# The routine parses the authorization data for the effective user
+# and stores the information in the configuration hash. Further, it
+# sets the auth_func reference to either auth_list() or auth_regex()
+# to abstract authorization checks.
+#
+sub loadAuthorization(\%)
+{
+	my $cfg = shift();
+
+	return 0 unless open(AUTH, "<$cfg->{authfile}");
+
+	AUTH_ENT: while (<AUTH>) {
+		chomp;
+		next if /^#/;		# ignore comments
+		next if /^\s*$/;	# skip empty lines
+		s/^\s+|\s+$//g;		# trim
+
+		my $authorized = 0;
+		my ($key, $val) = split /\s*=\s*/;
+
+		# read authorization configuration for user and its groups
+		if ($key =~ /^$cfg->{user}$/) {
+			$authorized = 1;
+			log_debug "Found user: $key";
+
+		} elsif ($key =~ /^@(\w+)$/) {
+			my $group = $1;
+			$authorized = 1 if grep {/^${group}$/} @{$cfg->{groups}};
+			log_debug "Found group: $key" if $authorized;
+		}
+
+		# skip line if there was no auth data for current user
+		next unless $authorized;
+
+		# build authorization policy and set auth_func
+		if ($val =~ /^list:\s*(.+)$/) {
+			if (@{$cfg->{regex}}) {
+				log_error "Authorization by list is ignored because "
+					  ."$cfg->{user} uses regular expressions "
+					  ."($cfg->{authfile}:$.)";
+				next AUTH_ENT;
+			}
+
+			$cfg->{auth_func} = \&auth_list;
+			push @{$cfg->{systems}}, split /\s*[;,]\s*/, $1;
+
+		} elsif ($val =~ /^file:(.+)$/) {
+			if (@{$cfg->{regex}}) {
+				log_error "Authorization by list is ignored because "
+					. "$cfg->{user} uses regular expressions "
+					. "($cfg->{authfile}:$.)";
+				next AUTH_ENT;
+			}
+
+			$cfg->{auth_func} = \&auth_list;
+			unless(readFile($1, sub { push @{$cfg->{systems}}, $_; })) {
+				log_error "Reading $1 failed: $! "
+					. "($cfg->{authfile}:$.)";
+			}
+		} elsif ($val =~ /^regex:(.+)$/) {
+			if (@{$cfg->{systems}}) {
+				log_error "Authorization by regular expression "
+					. "is ignored because $cfg->{user} "
+					. "uses lists ($cfg->{authfile}:$.)";
+				next AUTH_ENT;
+			}
+
+			# check regex syntax and complain if it is not correct
+			my $re = eval "qr/$1/io";
+			if ($@) {
+				log_error "An authorization entry is not a valid"
+					. " regular expression "
+					. "($cfg->{authfile}:$.):\n$@";
+				next AUTH_ENT;
+			}
+
+			$cfg->{auth_func} = \&auth_regex;
+			push @{$cfg->{regex}}, $re;
+
+		} else {
+			log_error "An authorization entry is not valid "
+				. "($cfg->{authfile}:$.)";
+		}
+		log_debug "'$key' => '$val'";
+	}
+	close(AUTH);
+
+	return 1;
+}
+
+# readFile() - Helper routine to read data from file
+#
+# The routine reads the file content, line by line, skips
+# comments and empty lines. It calls a routine for each line
+# containing data. The routine can access the data either
+# using the first parameter or using the context variable $_.
+#
+# Error handling is up to the caller; the routine exits with
+# zero if the file could not be opened. open() should set $!.
+#
+# $file:	File name
+# $sub:		Reference to sub routine
+#
+sub readFile($$)
+{
+	my ($file, $sub) = @_;
+
+	return 0 unless open(CONF, "<$file");
+	while (<CONF>) {
+		chomp;
+		next if /^#/;		# ignore comments
+		next if /^\s*$/;	# skip empty lines
+		s/^\s+|\s+$//g;		# trim
+		&{$sub}($_);		# execute sub in current context
+	}
+	close(CONF);
+	return 1;
+}
+
+# auth_global() - Authorization check using global system list
+#
+# $guest:	Scalar reference to the guest name string
+# $cfg:         Hash reference to terminal server configuration
+#
+sub auth_global($$)
+{
+	my ($guest, $cfg) = @_;
+
+	return -1 unless $cfg->{termsys};
+
+	# unstricted
+	return  1 if exists $cfg->{termsys}->{'[*ALL*]'};
+	# if restricted, check if system has been hashed (in uppercase)
+	return  1 if exists $cfg->{termsys}->{uc $$guest};
+
+	# guest has not been defined, so deny authorization request
+	return -2;
+}
+
+# auth_regex() - Authorization check using a regex
+#
+# $guest:	Scalar reference to the guest name string
+# $cfg:		Hash reference to terminal server configuration
+#
+sub auth_regex($$)
+{
+	my ($guest, $cfg) = @_;
+
+	return -1 unless @{$cfg->{regex}};
+	foreach my $re (@{$cfg->{regex}}) {
+		return 1 if $$guest =~ /$re/; # $re has been compiled as //io
+	}
+	return  0;
+}
+
+# auth_list() - Authorization check using a list of systems
+#
+# $guest:	Scalar reference to the guest name string
+# $cfg:		Hash reference to terminal server configuration
+#
+sub auth_list($$)
+{
+	my ($guest, $cfg) = @_;
+
+	return -1 unless @{$cfg->{systems}};
+	return 1 if grep(/^\s*\Q$$guest\E\s*$/i, @{$cfg->{systems}});
+	return 0;
+}
+
+# get_auditlog_file() - Set file path for session transcript
+#
+# Return false if an error has occured; otherwise true.
+#
+# $filepath:	Scalar reference to file path variable
+# $guest:	Name of the virtual guest machine
+# $cfg:		Hash reference to terminal server configuration
+sub get_auditlog_file($$$)
+{
+	my ($filepath, $guest, $cfg) = @_;
+
+	unless (exists $cfg->{auditsys}->{'[*ALL*]'} ||
+	     	exists $cfg->{auditsys}->{uc $guest}) {
+		$$filepath = "";
+		return 1;		# no transcript, return success
+	}
+
+	# check and create log directory for user
+	unless (-d "$cfg->{auditdir}/$cfg->{user}") {
+		unless (mkdir "$cfg->{auditdir}/$cfg->{user}") {
+			log_error "Creating the session transcript directory "
+				. "$cfg->{auditdir}/$cfg->{user} failed: $!";
+			$$filepath = "";
+			return 0;	# return error
+		}
+	}
+
+	$$filepath  = "$cfg->{auditdir}/$cfg->{user}/" . uc $guest . "_";
+	$$filepath .= strftime '%F-%H%M%S', localtime;
+
+	log_debug "Session transcript file: $$filepath";
+
+	return 1;			# return success
+}
+
+# cmd_connect() - Perform auth check; and on success, connect to system
+#
+# $guest:	Name of the virtual guest machine
+# $cfg:		Hash reference to terminal server configuration
+#
+sub cmd_connect($\%)
+{
+	my ($params, $cfg) = @_;
+	my $service        = $cfg->{service};
+
+	unless (length($params)) {
+		log_error "The z/VM guest name is missing";
+		return;
+	}
+
+	my ($guest, $srv) = split /\s+/, $params;
+	$service = $srv if $srv && $srv =~ /\w{1,8}/;
+
+	# check authorization:
+	# The auth_func contract is to return a code, that must be one of:
+	#	-2:	ts-shell is not permitted to connect to the guest
+	#	-1:	The user does not have any authorization policy
+	#	 0:	User does not have authorization to connect to guest
+	#	 1:	User is permitted to connect to guest (success)
+	# Other return codes are not allowed!
+	#
+	# call auth_func to check authorization
+	my  $rc = &{$cfg->{auth_func}}(\$guest, $cfg);
+
+	# if the user is authorized, finally check if the system is
+	# (globally) restricted to be used by the terminal server
+	$rc = auth_global(\$guest, $cfg) if $rc > 0;
+
+	if ($rc == -2) {
+		log_error "$ts_shell is not configured to connect to $guest";
+		return;
+	}
+	if ($rc == -1) {
+		log_error "You are not authorized to connect to "
+			. "any z/VM virtual machines";
+		return;
+	}
+	if ($rc == 0) {
+		log_error "You are not authorized to connect to $guest";
+		return;
+	}
+
+	# construct iucvconn command line
+	my @iucvconn = ("$cfg->{iucvconn}");
+
+	# check if the terminal session to $guest requires session logging
+	my $session_path = "";
+	# return if get_auditlog_file() failed
+	return unless get_auditlog_file(\$session_path, $guest, $cfg);
+	push @iucvconn, "-s", $session_path if $session_path;
+
+	# add $guest name and terminal identifier (service)
+	push @iucvconn, $guest, $service;
+
+	log_info "Connecting to $guest (terminal identifier: $service)...";
+	if (-x $cfg->{iucvconn}) {
+		system @iucvconn;
+		log_info "Connection ended";
+	} else {
+		log_error "Running $cfg->{iucvconn} failed: $!";
+	}
+}
+
+# cmd_service() - Show / set service/terminal name to identify remote terminal
+#
+# $new:		New terminal name; can be empty
+# %cfg:		Hash reference to terminal server configuration
+#
+sub cmd_service($\%)
+{
+	my ($new, $cfg) = @_;
+
+	unless ($new) {
+		print "$cfg->{service}\n";
+		return;
+	}
+
+	if ($new =~ /\w{1,8}/) {
+		$cfg->{service} = $new;
+		# push new service for cmd completion
+		unless (grep /$new/, @{$cfg->{services}}) {
+			push @{$cfg->{services}}, $new;
+		}
+	} else {
+		log_error("Terminal identifier $new is not valid");
+		log_error("Terminal identifiers consist of up to " .
+			  "eight alphanumerical characters");
+	}
+}
+
+# cmd_list() - List system authorization for effective user
+#
+# $cfg:		Hash reference to terminal server configuration
+#
+sub cmd_list(\%)
+{
+	my $cfg = shift();
+
+	if (@{$cfg->{regex}}) {
+		pager sub {
+			print "Regular expressions for your authorization:\n";
+			print "$_\n" foreach (@{$cfg->{regex}});
+			my $matches = list_regex_match($cfg);
+			if (@$matches) {
+				print "\nYou are authorized to connect to ".
+				      "these z/VM guest virtual machines:\n";
+				print "$_\n" foreach (@$matches);
+			}
+		};
+	} elsif (@{$cfg->{systems}}) {
+		pager sub {
+			foreach (@{$cfg->{systems}}) {
+				printf "$_\n" if auth_global(\$_, $cfg) > 0;
+			}
+		};
+	} else {
+		log_error "You are not authorized to connect to " .
+			  "any z/VM virtual machines";
+	}
+}
+
+sub log_debug($)
+{
+	print STDERR "[DEBUG] " . shift() . "\n" if $ENV{'TS_SHELL_DEBUG'};
+}
+
+sub log_error($)
+{
+	print STDERR "$ts_shell: " . shift() . "\n";
+}
+
+sub log_info($)
+{
+	print "$ts_shell: " . shift() . "\n";
+}
+
+sub pager($)
+{
+	my $eval = shift();
+
+	unless (open(PAGER, "|$ENV{'PAGER'}")) {
+		eval &$eval;
+		return;
+	}
+	my $old_out = select PAGER;
+	eval &$eval;
+	select $old_out;
+	close (PAGER);
+}
+
+sub intro()
+{
+  print <<EoIntro;
+Welcome to the Terminal Server shell.
+Type 'help' to get a list of available commands.
+
+
+EoIntro
+}
+
+sub version()
+{
+	print <<EoVersion
+Terminal Server Shell, version @S390_TOOLS_RELEASE@
+Copyright IBM Corp. 2008, 2009
+
+EoVersion
+}
+
+sub help()
+{
+	print STDERR <<EoHelp;
+Terminal Server shell help
+
+Available commands:
+	list			List authorizations.
+	connect <vm_guest>	Connect to specified z/VM guest virtual machine.
+	terminal [<identifier>]	Display or set the terminal identifier.
+	q | quit | exit		Exit the current shell session.
+	help			Display help information.
+	version			Display version information.
+
+EoHelp
+}
+
+sub usage()
+{
+	printf <<EoUsage;
+Usage: ts-shell [-h|--help] [-v|--version]
+
+Options:
+	-h, --help	Print help information, then exit.
+	-v, --version	Print version information, then exit.
+EoUsage
+}
+
+
+# list_regex_match() - Helper routine to get a list of systems that match regex
+#
+# $cfg:		Hash reference to terminal server configuration
+# $listref:	List reference to store result
+sub list_regex_match($)
+{
+	my $cfg      = shift();
+	my %uniq_sys = ();
+	my @result   = ();
+
+	return \@result if exists $cfg->{termsys}->{'[*ALL*]'};
+
+	# get a list of systems if ts shell runs unrestricted
+	foreach my $re (@{$cfg->{regex}}) {
+		foreach (grep (/$re/, keys %{$cfg->{termsys}})) {
+			$uniq_sys{$_} = 1;
+		}
+	}
+	push @result, (keys %uniq_sys);
+	return \@result;
+}
+
+# rl_cmd_completion() - Term::ReadLine completion for ts-shell commands
+#
+# $text:	Text to complete
+# $line:	Line buffer
+# $start:	offset
+#
+sub rl_cmd_completion($$$)
+{
+	my ($text, $line, $start) = @_;
+	my @cmds = qw/help version list connect terminal quit exit/;
+
+	# complete commands
+	return grep (/^$text/, @cmds) unless $start;
+
+	# complete terminal names for service and optionally for connect command
+	if ($line =~ /^(?:terminal|connect\s+\w+)\s+(?:\w+)?$/) {
+		return grep (/^$text/, @{$config{services}});
+	}
+
+	# complete systems for connect command
+	if ($line =~ /^connect\s+(\w+)?$/) {
+		my $systems = [];	# list ref for completion
+
+		# complete for simple lists
+		if (@{$config{systems}}) {
+			foreach my $sys (@{$config{systems}}) {
+				if (auth_global(\$sys, \%config) > 0) {
+					push @$systems, $sys;
+				}
+			}
+		# complete if ts shell uses regex's
+		} elsif (@{$config{regex}}) {
+			$systems = list_regex_match(\%config);
+		}
+		return grep {/^$text/} @$systems;
+	}
+	return ();
+}
+
+# start the terminal server shell
+&main();
+
+__DATA__
+__END__
+# vim: set ai noet ts=8 sw=8 tw=80:
Index: s390-tools-sles11/iucvterm/doc/Makefile
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/doc/Makefile
@@ -0,0 +1,27 @@
+#! /usr/bin/make -f
+
+include ../../common.mak
+
+MANS = iucvconn.1 iucvtty.1 ts-shell.1 hvc_iucv.9 chiucvallow.8
+
+all:
+
+check:
+
+install: install-man
+
+install-man: $(MANS)
+	for man in $(MANS); do \
+	  msection=`echo $$man |sed 's/.*\.\([1-9]\)$$/man\1/'` ; \
+	  $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 -D $$man $(MANDIR)/$$msection/$$man ; \
+	done
+
+clean:
+
+pdf: $(MANS)
+	for man in $(MANS); do \
+	  man -t ./$$man |ps2pdf -sPAPERSIZE=a4 - $${man}.pdf ; \
+	done
+
+
+.PHONY: install-man install clean pdf
Index: s390-tools-sles11/iucvterm/doc/chiucvallow.8
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/doc/chiucvallow.8
@@ -0,0 +1,145 @@
+.\" chiucvallow.8
+.\"
+.\"
+.\" Copyright IBM Corp. 2009
+.\" Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+.\" ----------------------------------------------------------------------
+.TH chiucvallow "8" "March 2009" "s390-tools" "z/VM IUCV HVC device driver"
+.LO 8
+.
+.
+.ds f \fBchiucvallow\fP
+
+.
+.
+.SH NAME
+lsiucvallow, chiucvallow \- List, change and verify z/VM user ID filter settings
+.
+.
+.
+.SH SYNOPSIS
+.B lsiucvallow
+.br
+.B \*f
+.BR \-l | \-\-list
+
+.B \*f
+.BR \-e | \-\-edit
+.RI " [" filter ]
+.br
+.B \*f
+.BR \-s | \-\-set
+.I filter
+.br
+.B \*f
+.BR \-c | \-\-clear
+
+.B \*f
+.BR \-V | \-\-verify
+.I filter
+.
+.
+.
+.SH DESCRIPTION
+The \*f program lists, verifies, and changes the z/VM user ID filter of the
+z/VM IUCV hypervisor console (HVC) device driver.
+
+The z/VM user ID filter lists the z/VM guest virtual machines (user IDs) that
+are permitted to establish IUCV communication paths with the z/VM IUCV HVC
+device driver.
+
+The filter list is a comma-separated list of z/VM user IDs as specified by the
+\fBhvc_iucv_allow\fP kernel parameter. With \*f you can change the filter list
+at runtime.
+
+\*f works with the currently active filter or with filter files that list each
+z/VM user ID on a separate line. Filter files might also contain comment lines
+starting with '#'.
+.br
+You can edit and verify existing filter files and replace the current
+z/VM user ID filter.
+.
+.
+.
+.SH OPTIONS
+.TP
+.BR \-h ", " \-\-help
+Display a short help text, then exit.
+.
+.TP
+.BR \-v ", " \-\-version
+Display the version of \*f, then exit.
+.
+.TP
+.BR \-l ", " \-\-list
+Display the z/VM user IDs contained in the current filter.
+
+Using this parameter requires root authority.
+.
+.TP
+.BR \-e ", " \-\-edit " [" \fIfilter\fP ]
+Edit the current z/VM user ID filter.
+
+\fIfilter\fP specifies a filter file listing z/VM user IDs.
+If \fIfilter\fP is not specified, the entries of the current z/VM user ID
+filter are used.
+
+The filter is opened temporarily in an editor program.  When the editor is
+closed, the changed filter is verified and, if verification succeeds, the
+current filter is replaced.  Otherwise a backup copy is saved for correcting
+errors.
+
+Using this parameter requires root authority.
+.
+.TP
+.BR \-c ", " \-\-clear
+Clear the current z/VM user ID filter by deleting all user ID entries.
+
+Using this parameter requires root authority.
+.
+.TP
+.BR \-s ", " \-\-set " " "\fIfilter\fP"
+Replace the current filter with the filter entries specified by \fIfilter\fP.
+The current filter can be replaced only after the new filter has been
+successfully verified.
+
+Using this parameter requires root authority.
+.
+.TP
+.BR \-V ", " \-\-verify " " "\fIfilter\fP"
+Verify the z/VM user ID filter specified by \fIfilter\fP.
+
+The verification process checks the following rules:
+.RS 10
+.IP "\(bu" 2
+z/VM user IDs consist of up to eight alphanumeric characters or underscores (_)
+.IP "\(bu" 2
+The total number of z/VM user IDs in the filter must not exceed 500
+.IP "\(bu" 2
+The filter size in bytes must not exceed 4095 bytes
+.RE
+.
+.
+.
+.SH "FILES"
+.TP
+.B /sys/module/kernel/parameters/hvc_iucv_allow
+sysfs attribute for accessing the \fBhvc_iucv_allow\fP kernel parameter.
+Use \*f to modify and access this sysfs attribute.
+.
+.
+.
+.SH "ENVIRONMENT"
+.TP
+.B EDITOR
+Specifies an editor program that is used for editing z/VM user ID filters.
+If \fBEDITOR\fP is not set or empty,
+.BR vi (1)
+is used.
+.
+.
+.
+.SH "SEE ALSO"
+.BR hvc_iucv (9)
+
+.I "Linux on System z - Device Drivers, Features, and Commands"
Index: s390-tools-sles11/iucvterm/doc/hvc_iucv.9
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/doc/hvc_iucv.9
@@ -0,0 +1,340 @@
+.\" hvc_iucv.9
+.\"
+.\" z/VM IUCV hypervisor console (HVC) device driver
+.\"
+.\"
+.\" Copyright IBM Corp. 2008, 2009.
+.\" Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+.\" -------------------------------------------------------------------------
+.TH "hvc_iucv" "9" "March 2009" "s390-tools" "z/VM IUCV HVC device driver"
+.LO 8
+.
+.
+.
+.SH NAME
+hvc_iucv \- Introduction to the z/VM IUCV hypervisor console (HVC) device driver
+.
+.
+.
+.SH SYNOPSIS
+.BR hvc_iucv "=" \fInumber\fP
+
+.BR hvc_iucv_allow "=" "\fIcomma-separated list of z/VM user IDs"
+.
+.
+.
+.SH DESCRIPTION
+The Hypervisor Console (HVC) device driver is a generic TTY device driver
+providing terminal devices with pluggable transport device drivers.
+
+The z/VM IUCV hypervisor console (HVC) device driver is a transport plug-in
+that uses z/VM IUCV communication to establish terminal connections and to
+transfer terminal data.
+
+The kernel parameter \fBhvc_iucv\fP controls the number of HVC terminals managed
+by the z/VM IUCV HVC device driver.  \fInumber\fP is an integer in the range
+0 to 8.  If \fInumber\fP is zero, the z/VM IUCV HVC device driver is
+switched off; otherwise up to \fInumber\fP z/VM IUCV HVC terminal devices are
+created.
+.\"(The maximum of 8 is a hypervisor console layer constant and might change
+.\"in future kernel versions.)
+
+The \fBhvc_iucv_allow\fP kernel parameter optionally specifies a comma separated
+list of z/VM user IDs. If the kernel parameter has been specified, the z/VM IUCV
+HVC device driver accepts IUCV connections from listed z/VM user IDs only.
+
+The first z/VM IUCV HVC terminal device is registered automatically for use as
+the Linux console.
+
+The
+.BR iucvconn (1)
+program establishes connections to z/VM IUCV HVC terminal devices. If a terminal
+is disconnected, output written by Linux is not displayed or saved.
+Therefore, a newly opened connection to a terminal is always blank. For most
+applications, like login or shell prompts, it is sufficient to press "Return" to
+obtain a new prompt.
+
+.
+.
+.
+.SH USAGE
+.SS "Connecting to z/VM IUCV HVC terminal devices"
+Connections to z/VM IUCV HVC terminal devices are established with the IUCV
+terminal application
+.BR iucvconn (1).
+.PP
+To connect to the first z/VM IUCV HVC terminal device on the z/VM guest virtual
+machine "LNX1234", run:
+.PP
+.ft CW
+.in +0.25in
+.nf
+iucvconn LNX1234 lnxhvc0
+.fi
+.in -0.25in
+.ft
+.PP
+The \fBlnxhvc\fP\fIn\fP identifier refers to the IUCV name of z/VM IUCV
+HVC terminal devices; where \fIn\fP corresponds to the terminal device number.
+.PP
+For z/VM IUCV HVC terminal devices,
+.BR iucvconn (1)
+does not set the terminal environment. See section "Setting up the terminal
+environment" below for further instructions.
+.PP
+The terminal connection is disconnected if the terminal session ends by logging
+off (for example, by typing exit).
+Alternatively,
+.BR iucvconn (1)
+provides an escape function for disconnecting from the terminal at any time.
+See also the manual page of
+.BR iucvconn (1).
+.
+.TP
+.B Note:
+If the first z/VM HVC terminal device is configured as the preferred Linux
+console, a logoff might be followed by a new login prompt.
+End the terminal session by using the disconnect escape function. Reconnecting
+always resumes the existing terminal session. Explicitly logging off before
+using the disconnect escape function assures that users must log in again.
+.
+.
+.SS "Using the magic sysrequest function on the first z/VM IUCV HVC terminal"
+The first z/VM IUCV HVC terminal, which can be used as Linux console, supports
+the "magic sysrequest" function if the Linux kernel has been built with
+\f(CWCONFIG_MAGIC_SYSRQ\fP.
+.PP
+To invoke the "magic sysrequest" function, press "Ctrl\^+\^o" followed by a second
+character that designates the debugging or emergency function.
+.PP
+The most important magic sysrequest functions are:
+.RS 4
+.IP "0 .. 9" 8
+Set the Linux console log level and control which kernel message are written to
+Linux console devices.
+.
+.IP "b" 8
+Re-IPL immediately (without syncing or unmounting file systems).
+The re-IPL configuration is used for the re-IPL of the Linux system.
+.
+.IP "s" 8
+Emergency sync all file file systems.
+.
+.IP "u" 8
+Emergency remount all mounted file systems read-only.
+.
+.IP "t" 8
+Show task info.
+.
+.IP "m" 8
+Show memory.
+.
+.IP "e" 8
+Terminate all tasks except
+.BR init (8).
+.
+.IP "i" 8
+Kill all tasks except
+.BR init (8).
+.
+.RE
+.PP
+See \fBsysrq.txt\fP of the Linux kernel documentation for a more complete
+list of functions.
+.PP
+This feature can be switched on or off during runtime by echoing "1" (on) or
+"0" (off) to \fB/proc/sys/kernel/sysrq\fP.
+.
+.
+.
+.SH CONFIGURATION
+.SS "Using the z/VM IUCV HVC terminal device as Linux console"
+The first z/VM IUCV HVC terminal device can display kernel messages
+and it can also be used as the preferred Linux console (i.e. become
+\fB/dev/console\fP).
+The preferred console is used as the initial input and output device,
+beginning at the stage of the boot process when the
+.BR init (8)
+program is called. Messages issued by programs that run at this stage are
+only displayed on the preferred console.
+
+.TP
+.B Note:
+Console messages are not displayed if the z/VM IUCV HVC terminal
+is not connected.
+If console messages flood the terminal, the z/VM HVC terminal device driver
+might discard console messages if they come in too fast.
+.PP
+To use the z/VM IUCV hypervisor console as the preferred console, append the
+\fBconsole=hvc0\fP parameter to the kernel command line.
+
+To display console messages only and use another device as preferred console,
+append \fBconsole=hvc0\fP, followed by an additional \fBconsole=\fP
+parameter that explicitly defines the preferred console,
+e.g. \fBconsole=ttyS0\fP.
+
+.TP
+.B Note:
+The last \fBconsole=\fP parameter designates the device for use as the
+preferred console. For Linux on System z, the default preferred console device
+is \fBttyS0\fP.
+.
+.
+.SS "Restricting terminal connections using a z/VM user ID filter"
+The z/VM IUCV HVC device driver can filter incoming connection requests based
+on the user ID of the originating z/VM guest virtual machine.
+
+The z/VM user ID filter is specified as a comma separated list of user IDs for
+the \fBhvc_iucv_allow\fP kernel parameter. The kernel parameter can be set
+initially on the kernel command line or through a sysfs attribute at runtime.
+
+For changing the parameter value at runtime, see the
+.BR chiucvallow (8)
+command.
+
+If the z/VM user ID filter is changed at runtime, the new filter applies only
+to new incoming connection requests. Existing terminal connections remain active
+until they get closed.
+Any re-connection attempt is subject to the new z/VM user ID filter.
+
+.TP
+.B Note:
+If z/VM user ID filtering is active, connection attempts from the z/VM
+guest virtual machine to itself are also subject to filtering. If the local
+z/VM user ID is not listed, local connection requests are refused.
+.
+.
+.SS "Configuring HVC terminals for user logins"
+Typically, \fBgetty\fP programs initialize terminals and prepare terminals for
+user logins.
+
+For each HVC terminal device, a \fBgetty\fP program must be started.
+To configure and start \fBgetty\fP on a HVC terminal, open
+.BR /etc/inittab (5),
+and add a new entry similar to this one:
+.ft CW
+.in +0.25in
+.nf
+
+h0:2345:respawn:/sbin/mingetty hvc0
+
+.fi
+.in -0.25in
+.ft
+Add new entries for each HVC terminal device that is designated for user logins.
+
+See
+.BR inittab (5)
+about the format of \fBinittab\fP entries; see
+.BR getty (8),
+or
+.BR mingetty (8)
+for options that are related to terminal setup and user login.
+.
+.
+.SS "Setting up HVC terminals for root logins"
+To allow root logins on HVC terminals, ensure that the respective terminal
+device names are listed in
+.BR /etc/securetty (5)\fR.\fP
+HVC terminal device names start with \fBhvc\fP followed by an integer in
+the range 0 to 7.
+.
+.
+.SS "Setting up the terminal environment"
+The terminal environment is changed by assigning the terminal name to the
+\fBTERM\fP environment variable. This can be done manually, or by a
+getty program.
+
+Complete the following steps for setting up the terminal environment for a z/VM
+IUCV HVC terminal:
+.RS 4
+.IP "1." 4
+To display the current terminal name, issue the command:
+
+.ft CW
+.in +0.25in
+.nf
+user@host:~$ echo $TERM
+xterm
+.fi
+.in -0.25in
+.ft
+
+.IP "2." 4
+To connect to a z/VM IUCV HVC terminal, run
+.BR iucvconn (1)
+and log in as usual:
+
+.ft CW
+.in +0.25in
+.nf
+user@host:~$ iucvconn MYLNX01 lnxhvc0
+     ...
+user@MYLNX01:~$
+.fi
+.in -0.25in
+.ft
+
+.IP "3." 4
+To assign the terminal name from step 1. to the terminal environment variable,
+issue the following command:
+
+.ft CW
+.in +0.25in
+.nf
+user@MYLNX01:~$ export TERM=xterm
+.fi
+.in -0.25in
+.ft
+.RE
+
+If unsure which terminal name to use, assign "\f(CWlinux\fP" to the terminal
+environment variable.
+
+.PP
+For getty programs, a sample terminal environment configuration might look like:
+
+.ft CW
+.in +0.25in
+.nf
+h0:2345:respawn:/sbin/agetty -L 9600 hvc0 linux
+.fi
+.in -0.25in
+.ft
+
+The terminal name typically follows the terminal device name. In the example,
+.BR agetty (8)
+sets the terminal environment variable to "linux" before
+.BR login (1)
+is started.
+
+Some getty programs do not support the terminal name parameter, see
+the manual page of the respective getty program for more information.
+.
+.
+.
+.SH "DIAGNOSTICS"
+The z/VM IUCV hypervisor console device driver uses the kernel message
+infrastructure for its messages. The message prefix is \fBhvc_iucv\fP.
+.
+.
+.
+.SH "SEE ALSO"
+.BR iucvconn (1),
+.BR chiucvallow (8),
+.BR af_iucv (7),
+.BR bootparam (7),
+.BR inittab (5),
+.BR getty (8),
+.BR agetty (8),
+.BR mingetty (8),
+.BR login (1),
+.BR securetty (5)
+
+See
+.BR kernel-parameters.txt " and"
+.BR sysrq.txt
+of the Linux kernel documentation for more information about kernel boot
+parameters and the magic sysrequest function.
+
+.I "Linux on System z - Device Drivers, Features, and Commands"
Index: s390-tools-sles11/iucvterm/doc/iucvconn.1
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/doc/iucvconn.1
@@ -0,0 +1,223 @@
+.\" iucvconn.1
+.\"
+.\"
+.\" Copyright IBM Corp. 2008, 2009.
+.\" Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+.\" ----------------------------------------------------------------------
+.TH iucvconn "1" "March 2009" "s390-tools" "IUCV terminal applications"
+.LO 8
+.
+.ds i \fBiucvconn\fP
+.
+.
+.
+.SH NAME
+iucvconn \- start terminal connection over z/VM IUCV
+.
+.
+.
+.SH SYNOPSIS
+.B iucvconn
+.RB [ \-h | \-\-help ]
+.br
+.B iucvconn
+.RB [ \-v | \-\-version ]
+.br
+.B iucvconn
+.RB [ \-e | \-\-escape-char
+.IR escape_char ]
+.RB [ \-s | \-\-sessionlog
+.IR log_file ]
+.I vm_guest
+.I terminal_id
+.
+.
+.
+.SH DESCRIPTION
+The \*i application establishes a terminal connection to another
+Linux instance. The Linux instance where \*i runs and the target Linux instance
+must be z/VM guest operating systems of the same z/VM instance.
+The communication path between both virtual machines is based on the z/VM
+Inter-User Communication Vehicle (IUCV).
+Because z/VM IUCV is independent from TCP/IP, Linux instances with no external
+network can be accessed.
+.PP
+\*i accesses the terminal specified by \fIterminal_id\fP on the z/VM guest
+virtual machine \fIvm_guest\fP.
+The \fIterminal_id\fP is similar to a port number in TCP/IP communications.
+.
+\fIterminal_id\fP is case-sensitive and consists of up to 8 alphanumeric
+characters. \fIterminal_id\fP must either identify a running \fBiucvtty\fP
+instance, or a terminal device provided by the z/VM IUCV hypervisor console
+(HVC) device driver.
+.
+.
+.
+.SH OPTIONS
+.TP
+.BR \-h ", " \-\-help
+Display a short help text, then exit.
+.
+.TP
+.BR \-v ", " \-\-version
+Display the version number of \*i, then exit.
+.
+.TP
+.BR \-e ", " \-\-escape-char\~\fIescape_char\fP
+Set an escape character for this terminal session. You need an escape character
+to access special \*i functions. The default escape character is underscore (_).
+If \fIescape_char\fP is set to "none", escaping is not possible.
+The escape character can be the closing bracket (]), the caret (^), the
+underscore (_), or any alphabetical character except C, D, Q, S and Z.
+The \fIescape_char\fP character is not case-sensitive.
+
+To invoke a \*i function, press Ctrl\^+\^\fIescape_char\fP, followed
+by the function character. For example, press "Ctrl\^+\^_ d" to disconnect the
+terminal session and exit \*i.
+
+For details, see section "ESCAPE CHARACTERS" below.
+.
+.TP
+.BR \-s ", " \-\-sessionlog\~\fIlogfile\fP
+Create a transcript of the terminal session and write session data to three
+different files.
+
+\fIlog_file\fP contains the raw terminal data stream. The
+.I log_file\fB.timing\fP
+file contains timing data that can be used for replaying the raw terminal data
+stream using realistic output delays.
+Additional terminal session information is logged to
+.IR log_file\fB.info\fP "."
+
+If any of these files already exist, \*i exits with an error. To proceed
+either delete the files or choose another file name for \fIlog_file\fP.
+.
+.
+.
+.SH "ESCAPE CHARACTERS"
+\*i supports functions through the use of an escape character.
+The default escape character (\fIescape_char\fP) is underscore (_).
+
+To enter the "escape mode" of \*i, press the Control and the \fIescape_char\fP
+key together ("Ctrl\^+\^_") In the "escape mode", \*i expects a single character
+designating the special function.
+
+The special functions are (assuming \fIescape_char\fP is set to underscore (_)):
+.RS 4
+.IP "\fBCtrl\^+\^_\fP" 8
+Send the escape character to the connected terminal.
+.IP "\fB.\fP or \fBd\fP" 8
+Disconnect and exit \*i.
+.IP "\fBr\fP" 8
+Force resizing of the connected terminal.
+.RE
+.PP
+Any other single character is ignored and is not send to the terminal. If
+multiple characters are entered together, \*i leaves the "escape mode" and sends
+the characters to the connected terminal.
+.
+.\" FIXME: change "connected terminal" to terminal
+.
+.
+.SH "EXAMPLES"
+To access the "lnxterm" terminal on the Linux instance in
+z/VM guest virtual machine LNXSYS01:
+.PP
+.ft CW
+.in +0.25in
+.nf
+iucvconn LNXSYS01 lnxterm
+.fi
+.in -0.25in
+.ft
+.PP
+To access the first z/VM IUCV HVC terminal on the Linux instance in
+z/VM guest virtual machine LNXSYS02:
+.PP
+.ft CW
+.in +0.25in
+.nf
+iucvconn LNXSYS02 lnxhvc0
+.fi
+.in -0.25in
+.ft
+.PP
+To create a transcript of the terminal session to the Linux instance in
+z/VM guest virtual machine LNXSYS99:
+.PP
+.ft CW
+.in +0.25in
+.nf
+iucvconn -s ~/transcripts/lnxsys99 LNXSYS99 lnxhvc0
+.fi
+.in -0.25in
+.ft
+.
+.
+.
+.SH ENVIRONMENT
+.TP
+.B TERM
+The terminal environment variable contains the name of the terminal
+(e.g. linux, xterm, ...) and also specifies the terminal capabilities.
+
+\*i transfers the value of the \fBTERM\fP environment variable at connection
+startup to the target system. The \fBiucvtty\fP application sets the
+\fBTERM\fP environment variable on the target system before the login program
+is started.
+Setting the \fBTERM\fP environment variable ensures that the terminal
+capabilities are the same on both sides of the connection.
+
+The z/VM IUCV hypervisor console (HVC) device driver ignores the content of
+the \fBTERM\fP environment variable transferred by \*i. See the
+.BR hvc_iucv (9)
+manual page for instructions to configure the terminal environment.
+.
+.
+.
+.SH DIAGNOSTICS
+If the Linux kernel does not include kernel support for the AF_IUCV network
+addressing family, \*i exits and displays the message
+.I 'AF_IUCV address family is not available: Address family not supported by
+.IR protocol' "."
+.
+.
+.
+.SH SECURITY
+.SS Linux
+The \*i program does not require superuser privileges and can be used by
+regular users.  Each connection attempt and failure is logged to the
+.BR authpriv
+syslog facility.
+.\".PP
+.\"Depending on the login program started by the \fBiucvtty\fP program,
+.\"the user must authenticate to the remote system.
+.PP
+Terminal session transcripts facilitate logging and auditing of terminal
+activity on remote systems or recording and playing back terminal sessions,
+for example for education and presentations.
+.
+.
+.SS z/VM guest virtual machine
+The z/VM guest virtual machine must have authorization to establish IUCV
+communication paths to other z/VM guest virtual machines.
+
+See the
+.BR af_iucv (7)
+manual page for details about IUCV authorizations.
+.
+.
+.
+.SH NOTES
+The Linux instance where \*i runs and the target Linux instances
+must be z/VM guest operating systems of the same z/VM instance.
+.
+.
+.
+.SH "SEE ALSO"
+.BR iucvtty (1),
+.BR hvc_iucv (9),
+.BR af_iucv (7),
+.BR scriptreplay (1)
+
+.I "Linux on System z - Device Drivers, Features, and Commands"
Index: s390-tools-sles11/iucvterm/doc/iucvtty.1
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/doc/iucvtty.1
@@ -0,0 +1,205 @@
+.\" iucvtty.1
+.\"
+.\"
+.\" Copyright IBM Corp. 2008, 2009.
+.\" Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+.\" ----------------------------------------------------------------------
+.TH iucvtty "1" "March 2009" "s390-tools" "IUCV terminal applications"
+.LO 8
+.
+.ds t \fBiucvtty\fP
+.ds i \fBiucvconn\fP
+.
+.
+.
+.SH NAME
+iucvtty \- allow remote logins over z/VM IUCV
+.
+.
+.
+.SH SYNOPSIS
+.B iucvtty
+.RB [ \-h | \-\-help ]
+.br
+.B iucvtty
+.RB [ \-v | \-\-version ]
+.br
+.B iucvtty
+.RB [ \-a | \-\-allow-from
+.IR regex ]
+.IR terminal_id
+.RB [\-\-
+.IR login_program " [" login_options ]]
+.
+.
+.
+.SH DESCRIPTION
+The \*t application provides full-screen terminal access to a Linux instance
+running as a z/VM guest operating system.
+The \*i application is used to connect to a running \*t instance.
+
+The terminal connection is based on the z/VM Inter-User Communication Vehicle
+(IUCV). z/VM IUCV establishes a communication path between two z/VM guest
+virtual machines on the same z/VM instance.
+Because z/VM IUCV is independent from TCP/IP, Linux instances with no external
+network can be accessed.
+
+\fIterminal_id\fP identifies the z/VM IUCV connection and is similar to a port
+number in TCP/IP communications.  \fIterminal_id\fP is case-sensitive and
+consists of up to 8 alphanumeric characters.
+It must be specified as a parameter in connection requests against
+a \*t instance.
+
+When a connection is established, \*t starts a login program.
+When the login program ends, \*t also exits. Consider an
+.BR inittab (5)
+entry
+.\"or an upstart job file
+to ensure that \*t is restarted and ready for the next terminal session.
+.
+.
+.
+.SH OPTIONS
+.TP
+.BR \-\^h ", " \-\^\-help
+Display a short help text and exit.
+.
+.TP
+.BR \-\^v ", " \-\^\-version
+Display the version number of \*t, then exit.
+.
+.TP
+.BR \-\^a ", " \-\^\-allow-from " " \fIregex\fP
+Limit permissions for incoming connections to z/VM user IDs that match
+the regular expression \fIregex\fP. The connection is refused if the ID
+does not match. If this parameter is omitted, connections are permitted
+from any z/VM user ID.
+.
+.TP
+.I login_program
+\fIlogin_program\fP specifies the absolute path to the login program to be
+started when a connection is established.
+The default is
+.BR /bin/login (1).
+.br
+The \fIlogin_program\fP must be separated by two dashes (--).
+.
+.TP
+.I login_options
+Depending on the particular login program used, \fIlogin_options\fP specifies
+additional options.
+.
+.
+.
+.SH EXAMPLES
+To allow remote logins using the terminal identifier "lnxterm":
+.PP
+.ft CW
+.in +0.25in
+.nf
+iucvtty lnxterm
+.fi
+.in -0.25in
+.ft
+
+To only allow users from LNXSYS01 to connect to terminal "lnxterm":
+.PP
+.ft CW
+.in +0.25in
+.nf
+iucvtty -a LNXSYS01 lnxterm
+.fi
+.in -0.25in
+.ft
+
+To only allow users from LNXSYS10 through LNXSYS19 to connect to terminal
+"lnxterm":
+.PP
+.ft CW
+.in +0.25in
+.nf
+iucvtty -a "LNXSYS1[0-9]" lnxterm
+.fi
+.in -0.25in
+.ft
+
+To use \fB/sbin/sulogin\fP instead of \fB/bin/login\fP for terminal "suterm":
+.PP
+.ft CW
+.in +0.25in
+.nf
+iucvtty suterm -- /sbin/sulogin
+.fi
+.in -0.25in
+.ft
+
+An entry in \fB/etc/inittab\fP to facilitate user logins on terminal "lnxterm"
+with \fB/bin/login\fP could be:
+.PP
+.ft CW
+.in +0.25in
+.nf
+t1:2345:respawn:/usr/bin/iucvtty lnxterm
+.fi
+.in -0.25in
+.ft
+
+An entry in \fB/etc/inittab\fP to facilitate user logins on terminal "suterm"
+with \fB/sbin/sulogin\fP in single user mode could be:
+.PP
+.ft CW
+.in +0.25in
+.nf
+s1:S:respawn:/usr/bin/iucvtty suterm -- /sbin/sulogin
+.fi
+.in -0.25in
+.ft
+.
+.
+.
+.SH DIAGNOSTICS
+If the Linux kernel does not include kernel support for the AF_IUCV network
+addressing family, \*t exits and displays the message
+.I 'AF_IUCV address family is not available: Address family not supported by
+.IR protocol' "."
+.
+.
+.
+.SH SECURITY
+.SS Linux
+The \*t program can be used by regular users.
+Depending on the particular login program, \*t must be started with superuser
+privileges for user authentication and authorization. For instance,
+\fB/bin/login\fP requires superuser privileges and, thus, regular users
+must use a different login program.
+
+Each connection attempt is logged to the \fBauthpriv\fP syslog facility.
+
+\*t uses pseudo-terminal (pts) devices to communicate with the
+login program. For security reasons, some login programs, like
+\fB/bin/login\fP, do not permit root logins on pseudo-terminal devices (see also
+.BR /etc/securetty "(5))."
+
+To permit root logins, consider using HVC terminal devices that are provided by
+the z/VM IUCV hypervisor console (HVC) device driver.
+.\"Enabling root logins on pseudo-terminal devices can compromise system security.
+.\"To avoid this potential security exposure, consider using HVC terminal devices
+.\"that are provided by the z/VM IUCV hypervisor console (HVC) device driver.
+.
+.
+.
+.SS z/VM guest virtual machine
+See the
+.BR af_iucv (7)
+manual page for details about IUCV authorizations.
+.
+.
+.
+.SH "SEE ALSO"
+.BR iucvconn (1),
+.BR login (1),
+.BR pts(4),
+.BR regex (7),
+.BR securetty (5),
+.BR af_iucv (7),
+.BR hvc_iucv (9)
Index: s390-tools-sles11/iucvterm/doc/ts-shell.1
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/doc/ts-shell.1
@@ -0,0 +1,574 @@
+.\" ts-shell.1
+.\"
+.\"
+.\" Copyright IBM Corp. 2008, 2009.
+.\" Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+.\" ----------------------------------------------------------------------
+.TH "ts-shell" "1" "March 2009" "s390-tools" "Terminal Server over IUCV"
+.LO 8
+.
+.
+.
+.SH "NAME"
+ts\-shell \- Login shell for terminal servers over z/VM IUCV
+.
+.
+.
+.SH "SYNOPSIS"
+.B ts\-shell
+.RB [ \-h | \-\-help ]
+.br
+.B ts\-shell
+.RB [ \-v | \-\-version ]
+.
+.
+.
+.SH "DESCRIPTION"
+\fBts-shell\fP is a login shell for terminal server environments using the IUCV
+terminal applications.
+\fBts-shell\fP authorizes Linux users based on user names and group memberships
+for accessing terminals.  Linux users can list the authorizations and access
+terminals.  If a user is authorized to access a terminal, \fBts-shell\fP
+establishes the terminal connection using the
+.BR iucvconn (1)
+program.
+
+Apart from \fBiucvconn\fP the IUCV terminal applications include \fBiucvtty\fP.
+.BR iucvtty (1)
+provides full-screen terminal access to a Linux instance
+running as a z/VM guest operating system.
+
+.BR iucvconn (1)
+can also establish terminal connections to z/VM IUCV hypervisor console (HVC)
+device drivers. The Hypervisor Console (HVC) is a generic TTY device driver for
+the Linux kernel providing terminals. One of the terminals can be used as the
+Linux console.
+
+The Linux instances where \fBts-shell\fP and \fBiucvconn\fP run and the target
+Linux instance must be z/VM guest operating systems of the same z/VM instance.
+Because z/VM IUCV is independent from TCP/IP, you can access Linux instances
+with no external network connection.
+.
+.
+.
+.
+.SH "OPTIONS"
+.TP
+.BR \-\^h ", " \-\^\-help
+Display a short help text, then exit.
+.
+.TP
+.BR \-\^v ", " \-\^\-version
+Display the version information, then exit.
+.
+.
+.
+.SH "USAGE"
+.SS "Terminal server shell commands"
+The terminal server shell provides the following commands:
+.PP
+.TP 4
+.B list
+The \fBlist\fP command lists z/VM guest virtual machines to which the
+Linux user is authorized to connect.
+
+The output of the \fBlist\fP command depends on the configured authorization
+method which can be "list" or "regex". The available authorization
+methods are explained in section "Configure terminal authorization for Linux
+users".
+
+The output for "list" authorization is a list of z/VM guest virtual machines,
+for example:
+.ft CW
+.in +0.25i
+.nf
+
+user@ts-shell> list
+guest1
+guest2
+guest3
+guest5
+
+.fi
+.in -0.25i
+.ft
+The output for "regex" authorization is a list of one or more
+regular expressions, for example:
+.ft CW
+.in +0.25i
+.nf
+
+user@ts-shell> list
+Regular expressions for your authorization:
+(?i-xsm:lnx\\w{5})
+(?i-xsm:^palim$)
+
+.fi
+.in -0.25i
+.ft
+If \fBts-shell\fP is configured to connect to particular z/VM guest virtual
+machines only, the output for "regex" authorization is followed by a list of
+the user IDs that match at least one of the regular expressions:
+.ft CW
+.in +0.25i
+.nf
+
+user@ts-shell> list
+Regular expressions for your authorization:
+(?i-xsm:lnx\\w{5})
+(?i-xsm:^palim$)
+
+You are authorized to connect to these z/VM guest virtual machines:
+LNXSYS42
+LNXSYS01
+
+.fi
+.in -0.25i
+.ft
+
+.
+.TP 4
+.B connect \fIvm_guest\fP \fR[\fP\fIterminal_id\fP\fR]\fP
+\fBconnect\fP establishes a terminal connection to a particular z/VM guest
+virtual machine specified as \fIvm_guest\fP.
+\fIvm_guest\fP consists of up to eight alphanumeric characters.
+
+An optional terminal identifier can be specified with \fIterminal_id\fP.
+If not specified, the default terminal identifier is used.
+To change the default terminal identifier, use the \fBterminal\fP command.
+
+In the following example, a user opens a terminal connection to the Linux
+instance in z/VM guest virtual machine LNXSYS01:
+.ft CW
+.in +0.25i
+.nf
+
+user@ts-shell> connect LNXSYS01
+ts-shell: Connecting to LNXSYS01 (terminal identifier: lnxterm)...
+  ...
+ts-shell: Connection ended
+
+.fi
+.in -0.25i
+.ft
+.
+.TP 4
+.B terminal \fR[\fP\fIidentifier\fP\fR]\fP
+The \fBterminal\fP command displays or sets the default terminal identifier
+that is used by subsequent \fBconnect\fP commands.
+\fIidentifier\fP is case-sensitive and consists of up to eight alphanumeric
+characters.
+
+If \fBterminal\fP is called with the \fIidentifier\fP being specified,
+\fIidentifier\fP is set as the new default terminal identifier.
+If \fIidentifier\fP is not specified, the current default terminal identifier
+is displayed:
+
+.ft CW
+.in +0.25i
+.nf
+user@ts-shell> terminal
+lnxterm
+.fi
+.in -0.25i
+.ft
+.
+.TP 4
+.BR quit ", " exit
+Exit the terminal server shell session.
+.
+.TP 4
+.B help
+Display the help about terminal server shell commands.
+.
+.TP 4
+.B version
+Display the \fBts-shell\fP version.
+.
+.
+.
+.SH "CONFIGURATION"
+To set up a Linux system as a terminal server and to use \fBts-shell\fP for
+Linux users, complete the following configuration steps:
+.IP "1." 4
+Authorize the terminal server z/VM guest virtual machine for IUCV.
+.IP "2." 4
+Create a terminal server shell configuration file.
+.IP "3." 4
+List z/VM guest virtual machines providing terminal access over IUCV.
+.IP "4." 4
+Configure terminal session transcripts.
+.IP "5." 4
+Configure terminal authorizations for Linux users.
+.IP "6." 4
+Install \fBts-shell\fP as the login shell for Linux users.
+.
+.
+.SS "Authorize the terminal server z/VM guest virtual machine for IUCV"
+The z/VM guest virtual machine on which the terminal server shell runs needs
+particular authorization to establish IUCV communication paths to other z/VM
+guest virtual machines.
+
+A typical \fBIUCV\fP authorization statement in the z/VM directory entry of the
+terminal server z/VM guest virtual machine might be:
+.PP
+.ft CW
+.in +0.25in
+.nf
+IUCV ANY
+OPTION MAXCONN 256
+.fi
+.in -0.25in
+.ft
+.PP
+The example allows the terminal server shell to establish IUCV communication
+paths with any z/VM guest virtual machine.
+The number of IUCV connections is limited to 256.
+
+See the
+.BR af_iucv (7)
+manual page for further details.
+.
+.
+.SS "Create a terminal server shell configuration file"
+When \fBts-shell\fP starts, it reads its configuration from the
+\fB/etc/iucvterm/ts-shell.conf\fP configuration file. The file contains
+configuration options that specify further configuration files with lists
+of z/VM guest virtual machines and terminal authorization definitions.
+.PP
+Supported configuration options (with default settings) are:
+.RS 4
+.TP
+.BR ts-systems " = " \fI/etc/iucvterm/ts-systems.conf\fP
+The \fBts-systems\fP configuration option specifies a file that
+lists z/VM guest virtual machines. \fBts-shell\fP permits connections
+to these z/VM guest virtual machines only.
+
+See also section
+"List z/VM guest virtual machines providing terminal access over IUCV".
+.
+.TP
+.BR ts-authorization " = " \fI/etc/iucvterm/ts-authorization.conf\fP
+The \fBts-authorization\fP option specifies a file containing
+the terminal authorization definitions for Linux users.
+
+See section "Configure terminal authorization for Linux users" about the file
+format.
+.
+.TP
+.BR transcript-systems " = " \fI/etc/iucvterm/ts-audit-systems.conf\fP
+The \fBtranscript-systems\fP option specifies a file that lists
+z/VM guest virtual machines for which terminal sessions are logged.
+
+See section "Configure terminal session transcripts" for details.
+.
+.TP
+.BR transcript-directory " = " \fI/var/log/ts-shell\fP
+The \fBtranscript-directory\fP option specifies a directory where the terminal
+session transcripts are saved.
+
+See section "Configure terminal session transcripts" for details.
+.
+.RE
+.
+.
+.SS "List z/VM guest virtual machines providing terminal access over IUCV"
+\fBts-shell\fP establishes terminal connections only if a Linux user has been
+authorized. In some cases, the administrator might want to explicitly restrict
+connections to particular z/VM guest virtual machines independent of the user.
+
+The \fBts-systems\fP configuration option specifies a file that lists z/VM
+guest virtual machines to which \fBts-shell\fP is permitted to connect.
+The file lists each z/VM guest virtual machine on a separate line.
+If a line contains "[*ALL*]", \fBts-shell\fP is permitted to connect to any
+z/VM guest virtual machine.
+.
+.TP
+.B Note:
+The \fBts-systems\fP options applies to the \fBts-shell\fP program only.
+If necessary, further restrictions can be configured for the z/VM guest
+virtual machine itself using the \fBIUCV\fP z/VM directory statement.
+See the section about IUCV authorizations in the
+.BR af_iucv (7)
+manual page.
+.
+.
+.SS "Create lists of z/VM guest virtual machines"
+A convenient method for creating lists of z/VM guest virtual machines is to use
+the information from the z/VM user directory, which contains all the names of
+the z/VM guest virtual machines that are defined on a z/VM operating system
+instance.
+
+For example, to create a list of all z/VM guest virtual machines with names that
+start with "LINUX" and are followed by digits, use:
+.ft CW
+.in +0.25in
+.nf
+
+vmur receive -H -t 1234 -O |grep -E "^USER LINUX[0-9]+" |cut -d" " -f2
+
+.fi
+.in -0.25in
+.ft
+Spool ID 1234 refers to the z/VM user directory file in the z/VM virtual
+reader device.
+.br
+The output of the command can be saved in a file. The file can then be
+specified for the
+.BR ts-systems " or " transcript-systems
+configuration options in the \fBts-shell.conf\fP file.
+In addition, use these files to configure list authorizations.
+.
+.
+.SS "Configure terminal session transcripts"
+\fBts-shell\fP can create transcripts of terminal sessions to z/VM guest virtual
+machines.
+
+The \fBts-audit-systems.conf\fP configuration file lists z/VM guest virtual
+machines for which terminal sessions are logged. If the file
+contains "[*ALL*]", each terminal session is logged.
+To create a list of z/VM guest virtual machines, see section
+"Create lists of z/VM guest virtual machines".
+
+For saving the terminal session transcripts, \fBts-shell\fP requires a directory
+that is specified by the \fBtranscript-directory\fP option in the
+\fBts-shell.conf\fP configuration file.
+.TP
+.B Note:
+The terminal session transcript directory must be writable by all
+\fBts-shell\fP users. The system administrator might use a "ts-shell" group
+containing all \fBts-shell\fP users as members. The directory can be made
+writable for the "ts-shell" group only.
+.PP
+\fBts-shell\fP uses a combination of the Linux user name, z/VM guest virtual
+machine and a time stamp for creating new terminal session transcript files.
+
+The format is as follows:
+.br
+.RS 4
+.RI "/var/log/ts-shell/" user_name "/" VMGUEST "_" YY "-" MM "-" DD "-" HHMMSS
+.RE
+.PP
+Terminal session transcripts consist of three different files: the raw
+terminal data stream, timing data information and connection information.
+See
+.BR iucvconn (1)
+for more details about terminal session transcripts.
+.
+.
+.SS "Configure terminal authorizations for Linux users"
+\fBts-shell\fP performs authorization checks for Linux users before connecting
+to z/VM guest virtual machines.  The authorization configuration grants Linux
+users or groups to establish terminal connections only to particular z/VM guest
+virtual machines.  These authorization definitions are stored in the
+\fBts-authorization.conf\fP configuration file.
+
+This configuration file consists of authorization mappings where mappings can
+be created for Linux users or groups.
+For the specification of z/VM guest virtual machines, a list or regular
+expression is used.
+.br
+A Linux user is referenced by the user name; a Linux group is referenced by the
+group name and prefixed with "@".
+
+Here is an example of a Linux user and group authorization:
+.PP
+.ft CW
+.in +0.25in
+.nf
+alice  =  list:guest01,guest02
+@users =  list:guest03,guest04
+.fi
+.in -0.25in
+.ft
+.PP
+To create lists of z/VM guest virtual machines, use the following prefixes:
+.RS 4
+.IP "\fIlist:\fP" 8
+followed by a comma-separated list of names.
+.
+.IP "\fIfile:\fP" 8
+followed by a file path.  The file lists z/VM guest virtual machines, each
+name on a separate line.
+.RE
+.
+.PP
+The following example shows the usage of the \fIfile:\fP prefix:
+.PP
+.ft CW
+.in +0.25in
+.nf
+@testgrp = file:/etc/iucvterm/auth/test-systems.list
+@prodgrp = file:/etc/iucvterm/auth/production-systems.list
+.fi
+.in -0.25in
+.ft
+.PP
+See section "Create lists of z/VM guest virtual machines" above
+about creating lists of z/VM guest virtual machines with names that
+match a specific pattern.
+.
+.PP
+Instead of listing each z/VM guest virtual machine individually, regular
+expressions can be used to match names of z/VM guest virtual machines.
+If naming schemes exist for z/VM guest virtual machines, using regular
+expressions might be more efficient and allow for future additions.
+.br
+The \fIregex:\fP prefix starts the definition of a regular expression to match
+the names of z/VM guest virtual machines. The regular expression must be a
+Perl-compatible or an extended regular expression (ERE) as documented in POSIX.
+Basic regular expressions (BRE) cannot be used. See
+.BR regex (7)
+for POSIX extended regular expressions; and the Perl reference manual
+.BR perlre
+about regular expression in Perl.
+
+To authorize user bob for all z/VM guest virtual machines with names that
+start with "lnx" and are followed with at least three but not more than five
+alphanumeric characters, use:
+.PP
+.ft CW
+.in +0.25in
+.nf
+bob = regex:lnx\\w{3,5}
+.fi
+.in -0.25in
+.ft
+.PP
+.
+If a naming scheme exists for z/VM guest virtual machines belonging to the
+test or production environment: authorize all users in the "testgrp" group for
+all systems in the test environment; and respectively, authorize all users in
+the "prodgrp" group for all systems in the production environment:
+.PP
+.ft CW
+.in +0.25in
+.nf
+@testgrp = regex:test\\w+
+@prodgrp = regex:prod\\w+
+.fi
+.in -0.25in
+.ft
+.
+.PP
+You can have multiple authorizations for the same user, either directly through
+user authorizations or indirectly through authorizations for groups that the
+user is a member of.  Be aware that \fBts-shell\fP accepts only one type of
+authorization, list or regex, for a particular user. The first type of
+authorization that is found for a user sets the authorization type for this
+user. Further authorizations of the same type are accumulated.  Authorizations
+of the other type are ignored.
+
+Example:
+.PP
+.ft CW
+.in +0.25in
+.nf
+@users = list:guest01,guest03,guest05
+alice = list:guest02,guest04
+eve = regex:guest0[7890]
+.fi
+.in -0.25in
+.ft
+.PP
+If both alice and eve are members of group users, alice is authorized for
+guest01, guest02, guest03, guest04, and guest05. For eve, the regular expression
+is ignored and the authorizations are for guest01, guest03, guest05 as defined
+for the group.
+.
+.
+.
+.SS "Install ts-shell as login shell for Linux users"
+To use the \fBts-shell\fP as the login shell for Linux users, follow these steps:
+.IP "1." 4
+Add the path of the \fBts-shell\fP program to the \fI/etc/shells\fP file that
+contains the list of valid login shells:
+.PP
+.ft CW
+.in +0.25in
+.nf
+echo $(which ts-shell) >> /etc/shells
+.fi
+.in -0.25in
+.ft
+.PP
+.
+.IP "2." 4
+Change the login shell of a particular Linux user using the
+.BR chsh (1)
+program:
+.PP
+.ft CW
+.in +0.25in
+.nf
+chsh -s $(which ts-shell) alice
+.fi
+.in -0.25in
+.ft
+.
+.
+.
+.SH "FILES"
+.TP
+.B /etc/iucvterm/ts-shell.conf
+General terminal server shell configuration file.
+.
+.TP
+.BR /etc/iucvterm/ts-systems.conf ", " /etc/iucvterm/unrestricted.conf
+The \fBts-systems.conf\fP file lists z/VM guest virtual machines to which
+connections are permitted.
+\fBunrestricted.conf\fP contains "[*ALL*]" to permit
+.BR ts-shell (1)
+to connect to any z/VM guest virtual machine.
+
+The \fBts-systems\fP configuration option in the \fBts-shell.conf\fP file
+might specify one of these files.
+.
+.TP
+.B /etc/iucvterm/ts-authorization.conf
+The \fBts-authorization.conf\fP file grants Linux users or groups to establish
+terminal connections only to particular z/VM guest virtual machines.
+.
+.TP
+.B /etc/iucvterm/ts-audit-systems.conf
+The \fBts-audit-systems.conf\fP file lists z/VM guest virtual machines for which
+terminal sessions are logged.
+.
+.TP
+.B /var/log/ts-shell
+Directory for saving terminal session transcripts.
+.
+.
+.
+.SH "ENVIRONMENT"
+.TP
+.B PAGER
+The \fBPAGER\fP environment variable designates a program used as pager for the
+\fBlist\fP command of the terminal server shell.
+If \fBPAGER\fP is not set or empty,
+.BR less (1)
+is used.
+.
+.TP
+.B LESSSECURE
+\fBts-shell\fP sets this variable to run
+.BR less (1)
+in "secure" mode. See the SECURITY section in the
+.BR less (1)
+man page.
+.
+.
+.
+.SH "SEE ALSO"
+.BR iucvconn (1),
+.BR iucvtty (1),
+.BR af_iucv (7),
+.BR less (1),
+.BR chsh (1),
+.BR shells (5),
+.BR regex (7),
+.BR perlre
+
+.I "Linux on System z - Device Drivers, Features, and Commands"
+.br
+.I "z/VM CP Planning and Administration"
Index: s390-tools-sles11/iucvterm/doc/ts-shell/README.ts-shell
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/doc/ts-shell/README.ts-shell
@@ -0,0 +1,126 @@
+Terminal server setup
+=====================
+
+This README file provides additional information for configuring
+a terminal server.
+
+
+
+Installation overview
+---------------------
+The s390-tools package installs the ts-shell(1) program along with
+a set of configuration files in `/etc/iucvterm`.
+
+The ts-shell program is pre-configured to connect to any other z/VM
+guest virtual machines (if the z/VM guest virtual machine has the
+respective IUCV authorizations).
+The administrator can change the ts-shell configuration to restrict
+connections to particular z/VM guest virtual machines only.
+
+There are no user authorizations by default. The administrator must
+edit `/etc/iucvterm/ts-authorization.conf` to assign authorizations.
+See the manual page for ts-shell(1) and the `authorization-sample.conf`
+file in the documentation directory for configuration examples.
+
+In addition, the documentation directory also contains the
+iucvconn_on_login sample program that is an alternative to ts-shell.
+
+
+
+
+Setup considerations for the terminal server shell (ts-shell)
+-------------------------------------------------------------
+Adding new ts-shell users
+~~~~~~~~~~~~~~~~~~~~~~~~~
+The ts-shell installation creates a system group ts-shell.
+If you intend to use ts-shell as a login shell for users, ensure that
+these users are all members of ts-shell.  To add existing users to
+group ts-shell, use +usermod -G ts-shell 'username'+.
+
+The ts-shell configuration files and `/var/log/ts-shell` are
+readable only by members of the *ts-shell* group.
+
+
+Enabling terminal session transcripts
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ts-shell(1) can be configured to create transcripts of terminal sessions
+to particular z/VM guest virtual machines. The transcripts are written
+to log files in the `/var/log/ts-shell` directory.
+
+NOTE:	The `/var/log/ts-shell` directory permission has the
+	set-group-ID bit set. Sub-directories that are created by
+	different users will inherit the group ownership of the
+	`/var/log/ts-shell` directory.
+
+See the ts-shell(1) manual page for more information about terminal
+session transcripts.
+
+
+Setting up command completion (optional)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*ts-shell* supports completion for commands and, if possible, for
+z/VM guest virtual machines. The completion is triggered using the
+'Tab' key. Command completion is an optional feature.
+
+To enable command completion, install one of the following Perl
+ReadLine libraries:
+
+- Term::ReadLine::Gnu
+- Term::ReadLine::Perl
+
+Check your Linux distribution for packages that provide these Perl
+ReadLine interfaces.
+
+
+
+
+Setup a terminal server using the iucvconn_on_login program
+-----------------------------------------------------------
+*iucvconn_on_login* is a sample script that uses the iucvconn(1)
+program to establish an IUCV terminal connection to a Linux
+instance that runs as z/VM guest operating system.
+
+It can be used as an alternative to set up a terminal server.
+The idea of iucvconn_on_login is to establish a terminal connection
+at login time.
+The z/VM guest virtual machine to which iucvconn_on_login will
+connect is the name of the Linux user that logs in.
+
+For example, if you log in as Linux user "lxguest1", iucvconn_on_login
+is started and establishes a terminal connection to the z/VM guest
+virtual machine lxguest1.
+
+
+Adding new users for z/VM guest virtual machines
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+To set up a terminal server using the iucvconn_on_login program,
+create a new Linux user for each z/VM guest virtual machine using
+the z/VM user ID as name. Set the login shell of those users to
+*iucvconn_on_login*.
+
+NOTE:	User names are case-sensitive in Linux.	To ensure consistency,
+	always use either lower or upper case characters for your
+	user names.
+
+To create a new user for z/VM guest virtual machine LXGUEST1
+----
+useradd -s /usr/bin/iucvconn_on_login lxguest1
+----
+
+If you log in as user 'lxguest1' on the terminal server (for example
+using ssh(1)), *iucvconn_on_login* immediately establishes the terminal
+connection to the z/VM virtual guest virtual machine 'lxguest1'.
+
+To access the default terminal ('lnxhvc0') on the Linux instance in
+z/VM guest virtual machine LXGUEST1
+----
+ssh lxguest1@terminal.server
+----
+To access the terminal 'lnxterm' on the Linux instance in
+z/VM guest virtual machine LXGUEST1
+----
+ssh -t lxguest1@terminal.server lnxterm
+----
+For ssh(1), you must use the "-t" parameter if you want to specify an
+alternate terminal identifier. The "-t" parameter ensures that ssh
+starts iucvconn_on_login on a terminal.
Index: s390-tools-sles11/iucvterm/doc/ts-shell/authorization-sample.conf
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/doc/ts-shell/authorization-sample.conf
@@ -0,0 +1,24 @@
+# Terminal server authorization configuration
+#
+# See ts-shell(1) manual page for file format.
+
+
+## Examples -- System lists
+
+# alice is authorized to connect to guest1 .. guest5
+alice = list:guest1,guest2,guest3,guest4,guest5
+
+## bob is authorized to connect to systems listed in the file "test-systems.list"
+bob = file:/etc/iucvterm/systems/test-systems.list
+
+# authorize all members of the Linux 'admins' group to connect to systems
+# listed in the file "admin-systems.list"
+@admins = file:/etc/iucvterm/groups/admin-systems.list
+
+
+## Examples -- Regular expressions
+
+# eve has authorization to connect to systems with
+#   a) names starting with 'guest', followed by exactly 2 digits
+#   b) names starting with 'lnx', followed by 5 alphanumeric characters
+eve = regex:(?:guest\d{2}|lnx\w{5})
Index: s390-tools-sles11/iucvterm/doc/ts-shell/iucvconn_on_login
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/doc/ts-shell/iucvconn_on_login
@@ -0,0 +1,35 @@
+#! /bin/sh
+#
+# iucvconn_on_login - start terminal connection at login
+#
+# Shell script to connect to a Linux guest operation system
+# using the iucvconn(1) program.
+# The z/VM guest virtual machine to which iucvconn_on_login will connect
+# is the name of the Linux user that logs in.
+#
+# Copyright IBM Corp. 2008, 2009.
+# Author: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+#
+prog_name=`basename $0`
+guest_name=${USER:-`whoami 2>/dev/null`}
+terminal=lnxhvc0
+iucvconn=`which iucvconn 2>/dev/null`
+
+__error() {
+	printf "$prog_name: $@\n" >&2
+	exit 1
+}
+
+# check if we have been called with -c <name> to specify an alternate
+# terminal identifier. This can be used by ssh: "ssh -t guest@ts my_term"
+case "$1" in
+  -c) test -n "$2" && terminal=$2 ;;
+esac
+
+test -t 1 || __error "The $prog_name program requires a terminal to run on"
+test "x$guest_name" = x && \
+	__error "Failed to determine the target z/VM guest virtual machine"
+test -x "$iucvconn" || __error "Failed to run the 'iucvconn' program"
+
+printf "$prog_name: Connecting to $guest_name (terminal ID: $terminal)\n\n"
+exec $iucvconn $guest_name $terminal
Index: s390-tools-sles11/iucvterm/doc/ts-shell/sample-system.list
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/doc/ts-shell/sample-system.list
@@ -0,0 +1,11 @@
+# Terminal server shell -- system list
+#
+# This file lists z/VM guest virtual machines, each name on a separate line.
+# Empty lines or lines starting with '#' are ignored.
+# See ts-shell(1) for more information.
+#
+#------|<---------------------------------------------------
+LNXSYSA
+LNXSYSB
+LNXSYS01
+LNXSYS42
Index: s390-tools-sles11/iucvterm/etc/Makefile
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/etc/Makefile
@@ -0,0 +1,30 @@
+#! /usr/bin/make -f
+
+include ../../common.mak
+
+
+IUCVTERM_DIR = $(SYSCONFDIR)/iucvterm
+CONFIG_FILES = ts-shell.conf unrestricted.conf \
+               ts-authorization.conf ts-audit-systems.conf
+
+
+all:
+check:
+install: $(CONFIG_FILES)
+	$(INSTALL) -g $(GROUP) -o $(OWNER) -d $(IUCVTERM_DIR) ; \
+	for cnf in $(CONFIG_FILES); do \
+	  $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 $$cnf $(IUCVTERM_DIR) ; \
+	done
+
+clean:
+	-rm -f *.o ts-shell.conf
+
+
+%: %.in
+	my_sysconf=`echo $(IUCVTERM_DIR) |sed 's#^$(INSTROOT)##'` ; \
+	cat $< \
+	  |sed -e "s#@sysconf_path@#$$my_sysconf#g" \
+	       -e 's#@S390_TOOLS_RELEASE@#$(S390_TOOLS_RELEASE)#g' \
+	> $@
+
+.PHONY: install clean perl
Index: s390-tools-sles11/iucvterm/etc/ts-audit-systems.conf
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/etc/ts-audit-systems.conf
@@ -0,0 +1,3 @@
+# List of z/VM guest virtual machines
+# for which terminal session are logged
+#
Index: s390-tools-sles11/iucvterm/etc/ts-authorization.conf
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/etc/ts-authorization.conf
@@ -0,0 +1,6 @@
+# Terminal server authorization configuration
+#
+# See ts-shell(1) manual page for file format syntax.
+#
+# See also authorization-sample.conf in the documentation directory.
+#
Index: s390-tools-sles11/iucvterm/etc/ts-shell.conf.in
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/etc/ts-shell.conf.in
@@ -0,0 +1,27 @@
+# Terminal server shell (ts-shell) configuration file
+#
+# See ts-shell(1) manual page for file format syntax.
+
+
+# System and authorization settings
+# ---------------------------------
+
+# ts-system
+#	file path to the ts-systems.conf configuration file
+ts-systems = @sysconf_path@/unrestricted.conf
+
+# ts-authorization
+#	file path to the ts-authorization.conf configuration file
+ts-authorization = @sysconf_path@/ts-authorization.conf
+
+
+# Session transcript settings
+# ---------------------------
+
+# transcript-systems
+#	file path to a list of systems for which session transcript are created
+transcript-systems = @sysconf_path@/ts-audit-systems.conf
+
+# transcript-directory
+#	directory path for saving transcripts
+transcript-directory = /var/log/ts-shell
Index: s390-tools-sles11/iucvterm/etc/unrestricted.conf
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/etc/unrestricted.conf
@@ -0,0 +1,2 @@
+# Allow connections to all z/VM guest virtual machines on this z/VM instance
+[*ALL*]
Index: s390-tools-sles11/iucvterm/include/af_iucv.h
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/include/af_iucv.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2006 IBM Corporation
+ * IUCV protocol stack for Linux on zSeries
+ * Version 1.0
+ * Author(s): Jennifer Hunt <jenhunt@us.ibm.com>
+ *
+ */
+
+#ifndef __AFIUCV_H
+#define __AFIUCV_H
+
+#include <sys/socket.h>
+
+#ifndef AF_IUCV
+#	define AF_IUCV		32
+#	define PF_IUCV		AF_IUCV
+#endif
+
+/* IUCV socket address */
+struct sockaddr_iucv {
+	sa_family_t	siucv_family;
+	unsigned short	siucv_port;		/* Reserved */
+	unsigned int	siucv_addr;		/* Reserved */
+	char		siucv_nodeid[8];	/* Reserved */
+	char		siucv_user_id[8];	/* Guest User Id */
+	char		siucv_name[8];		/* Application Name */
+};
+
+#endif
Index: s390-tools-sles11/iucvterm/include/iucvterm/config.h
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/include/iucvterm/config.h
@@ -0,0 +1,31 @@
+/*
+ * iucvtty / iucvconn - IUCV Terminal Applications
+ *
+ * Configuration structure and command line processing function
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ */
+#ifndef __IUCVTTY_CONFIG_H_
+#define __IUCVTTY_CONFIG_H_
+
+enum iucvterm_prg {
+	PROG_IUCV_TTY  = 0,
+	PROG_IUCV_CONN = 1,
+};
+
+struct iucvtty_cfg {
+	char		client_check;	/* Flag to check for allowed clients.*/
+	char		client_re[129];	/* Regexp to match incoming clients. */
+	char		host[9];	/* IUCV target host name. */
+	char		service[9];	/* IUCV service name. */
+	char		**cmd_parms;	/* ptr to commandline parms. */
+	char		*sessionlog;	/* ptr to session log file path. */
+	unsigned char	esc_char;	/* Escape character. */
+};
+
+
+extern void parse_options(enum iucvterm_prg, struct iucvtty_cfg *,
+			  int, char **);
+
+#endif /* __IUCVTTY_CONFIG_H_ */
Index: s390-tools-sles11/iucvterm/include/iucvterm/functions.h
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/include/iucvterm/functions.h
@@ -0,0 +1,74 @@
+/*
+ * iucvtty / iucvconn - IUCV Terminal Applications
+ *
+ * Definition of common functions
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ */
+#ifndef __FUNCTIONS_H_
+#define __FUNCTIONS_H_
+
+#include <unistd.h>
+
+#include "af_iucv.h"
+#include "inttypes.h"
+#include "iucvterm/config.h"
+#include "iucvterm/proto.h"
+
+
+#define min(a,b)		((a) < (b) ? (a) : (b))
+#define max(a,b)		((a) > (b) ? (a) : (b))
+
+/* Message buffer: message header + 4096 bytes of data */
+#define MSG_BUFFER_SIZE		(MSG_DATA_OFFSET + (4096))
+
+/* Error macros */
+#define print_error(s)		program_error(PRG_COMPONENT, (s))
+#define iucvtty_error(c)	iucv_msg_error(PRG_COMPONENT, (c))
+
+/* Error codes */
+/* Unable to fork new process */
+#define ERR_FORK			105
+/* Cannot execute login program  */
+#define ERR_CANNOT_EXEC_LOGIN		106
+/* Cannot set up terminal as a login terminal */
+#define ERR_SETUP_LOGIN_TTY		107
+/* Client (vm guest) is not authorized */
+#define ERR_NOT_AUTHORIZED		110
+
+
+extern int iucvtty_handle_req(int);
+
+extern int iucvtty_socket(struct sockaddr_iucv *,
+			  const char *, const char *);
+
+extern int iucvtty_tx_termenv(int, char *);
+extern int iucvtty_rx_termenv(int, void *, size_t);
+extern int iucvtty_tx_winsize(int, int);
+extern int iucvtty_tx_data(int, int, struct iucvtty_msg *, size_t);
+extern int iucvtty_tx_error(int, uint32_t);
+extern int iucvtty_copy_data(int, struct iucvtty_msg *);
+extern int iucvtty_read_data(int, struct iucvtty_msg *, size_t);
+
+extern int iucvtty_read_msg(int, struct iucvtty_msg *, size_t, size_t *);
+extern int iucvtty_write_msg(int, struct iucvtty_msg *);
+extern void iucvtty_skip_msg_residual(int, size_t *);
+
+extern ssize_t __write(int, const void*, size_t);
+
+extern int strmatch(const char *, const char *);
+extern int is_regex_valid(const char *);
+extern int is_client_allowed(const char *, const struct iucvtty_cfg *);
+extern void userid_cpy(char [8], const char [8]);
+
+extern void iucv_msg_error(const char *, uint32_t);
+extern void program_error(const char *, const char *);
+
+/* Audit/session log */
+extern int open_session_log(const char *);
+extern ssize_t write_session_log(const void*, size_t);
+extern void write_session_info(const char *, ...);
+extern void close_session_log(void);
+
+#endif /* __FUNCTIONS_H_ */
Index: s390-tools-sles11/iucvterm/include/iucvterm/gettext.h
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/include/iucvterm/gettext.h
@@ -0,0 +1,74 @@
+/*
+ * iucvtty / iucvconn - IUCV Terminal Applications
+ *
+ * National language support (NLS) functions
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ */
+#ifndef __IUCVTTY_GETTEXT_H_
+#define __IUCVTTY_GETTEXT_H_
+
+#ifdef USE_NLS
+#	include <locale.h>
+#	include <libintl.h>
+#endif
+
+
+/* Gettext constants (should be supplied from Makefile) */
+#ifndef GETTEXT_TEXTDOMAIN
+#	define GETTEXT_TEXTDOMAIN	"iucvterm"
+#endif
+#ifndef GETTEXT_NLS_PATH
+#	define GETTEXT_NLS_PATH		"/usr/share/locale"
+#endif
+
+
+/* Gettext macros */
+#ifdef USE_NLS
+#	define _(translatable)		gettext(translatable)
+#else
+#	define _(translatable)		(translatable)
+#endif
+
+#define N_(translatable)		(translatable)
+
+
+
+/**
+ * gettext_setup_locale() - Init gettext text domain using a specific locale
+ * @locale:	Locale to be set.
+ *
+ * The function sets the program locale for LC_MESSAGES and then initializes
+ * gettext.
+ *
+ * The @locale parameter is directly passed to the setlocale() function.
+ * If @locale is "", LC_MESSAGES is set according to the environment variable.
+ */
+static inline int gettext_init_locale(const char *locale)
+{
+#ifdef USE_NLS
+	if (setlocale(LC_MESSAGES, locale) == NULL)
+		return -1;
+	if (bindtextdomain(GETTEXT_TEXTDOMAIN, GETTEXT_NLS_PATH) == NULL)
+		return -1;
+	if (textdomain(GETTEXT_TEXTDOMAIN) == NULL)
+		return -1;
+#endif
+	return 0;
+}
+
+/**
+ * gettext_setup() - Initialize gettext text domain
+ *
+ * Calls gettext_setup_locale() with "" as locale parameter value.
+ */
+static inline int gettext_init(void)
+{
+#ifdef USE_NLS
+	return gettext_init_locale("");
+#else
+	return 0;
+#endif
+}
+#endif /* __IUCVTTY_GETTEXT_H_ */
Index: s390-tools-sles11/iucvterm/include/iucvterm/proto.h
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/include/iucvterm/proto.h
@@ -0,0 +1,108 @@
+/*
+ * iucvtty / iucvconn - IUCV Terminal Applications
+ *
+ * Structure and function definitions for the IUCV terminal message protocol
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ */
+#ifndef __IUCVTTY_PROTO_H_
+#define __IUCVTTY_PROTO_H_
+
+#include <stdint.h>
+#include <string.h>
+
+
+/* Standard macros      */
+#ifndef max
+#	define max(a, b)       ((a) > (b) ? (a) : (b))
+#endif
+#ifndef min
+#	define min(a, b)       ((a) < (b) ? (a) : (b))
+#endif
+#ifndef offsetof
+#	define offsetof(type, member) ((size_t) &((type *)0)->member)
+#endif
+
+/* Packet version magic */
+#define MSG_VERSION		0x02
+/* Message types */
+#define MSG_TYPE_ERROR		0x01	/* Error message. */
+#define MSG_TYPE_TERMENV	0x02	/* Terminal environment variable. */
+#define MSG_TYPE_TERMIOS	0x04	/* Terminal IO struct update. */
+#define MSG_TYPE_WINSIZE	0x08	/* Terminal window size update. */
+#define MSG_TYPE_DATA		0x10	/* Terminal data. */
+
+
+struct iucvtty_msg {
+	uint8_t		version;	/* Message version */
+	uint8_t		type;		/* Message type */
+	uint16_t	datalen;	/* Number of bytes in payload */
+	uint8_t		data[];		/* Payload buffer */
+} __attribute__((packed));
+#define MSG_DATA_OFFSET		(offsetof(struct iucvtty_msg, data))
+#define msg_size(m)		(MSG_DATA_OFFSET + (m)->datalen)
+
+/**
+ * msg_cpy_from() - Copy data to message
+ * @msg:	IUCV terminal message
+ * @src:	Pointer to source data buffer
+ * @len:	Length of source data buffer
+ *
+ * Copies @len bytes from @src to the message data buffer. The caller must
+ * ensure not to overwrite the message data buffer.
+ * Finally, the message datalen is set to @len
+ */
+static inline void msg_cpy_from(struct iucvtty_msg *msg, const void *src,
+				  size_t len)
+{
+	memcpy(msg->data, src, len);
+	msg->datalen = len;
+}
+
+/**
+ * msg_cpy_to() - Copy data from a message
+ * @msg:	IUCV terminal message
+ * @dst:	Destination buffer
+ * @len:	Destination buffer length.
+ *
+ * Copies up to min(@len, message datalen) number of bytes from the IUCV
+ * terminal message to the destination buffer.
+ */
+static inline void msg_cpy_to(const struct iucvtty_msg *msg, void *dst,
+				size_t len)
+{
+	memcpy(dst, msg->data, min(msg->datalen, len));
+}
+
+/**
+ * msg_alloc() - Allocates a new iucv terminal message.
+ * @type:	Message type
+ * @size:	Message data size
+ *
+ * The function allocates a new iucv terminal message with the given data size
+ * @size. (The total message size is @size plus MSG_DATA_OFFSET.)
+ * The new message is initialized with the specific @type.
+ */
+static inline struct iucvtty_msg *msg_alloc(uint8_t type, uint16_t size)
+{
+	struct iucvtty_msg *m;
+
+	m = malloc(size + MSG_DATA_OFFSET);
+	if (m != NULL) {
+		m->version = MSG_VERSION;
+		m->type    = type;
+		m->datalen = size;
+	}
+	return m;
+}
+
+/**
+ * msg_free() - free an allocated iucv tty message
+ */
+static inline void msg_free(struct iucvtty_msg *m)
+{
+	free(m);
+}
+
+#endif /* __IUCVTTY_PROTO_H_ */
Index: s390-tools-sles11/iucvterm/po/Makefile
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/po/Makefile
@@ -0,0 +1,57 @@
+#! /usr/bin/make -f
+#
+# This Makefile creates a message.po template for translation input.
+#
+# To create a localized message.po file from the template, run
+# 	msginit -i progname.pot -l de_DE # --no-translator
+#
+# To finally create the binary message.mo file, run
+# 	msgfmt de.po -o /usr/share/locale/de/LC_MESSAGES/progname.mo
+#
+
+#include ../../common.mak
+
+
+ifndef GETTEXT_TEXTDOMAIN
+GETTEXT_TEXTDOMAIN = iucvterm
+endif
+
+XGETTEXT = xgettext
+XGETTEXT_ARGS = --keyword=print_error
+POTFILES = POTFILES.in
+
+
+all: $(GETTEXT_TEXTDOMAIN).pot
+
+check:
+
+install:
+
+clean:
+	-rm -f $(GETTEXT_TEXTDOMAIN).pot
+
+$(GETTEXT_TEXTDOMAIN).pot:
+	@$(XGETTEXT) --default-domain=$(GETTEXT_TEXTDOMAIN) --directory='..' \
+		--add-comments --keyword=_ --keyword=N_ \
+		--flag=g_strdup_printf:1:c-format \
+		--flag=g_string_printf:2:c-format \
+		--flag=g_string_append_printf:2:c-format \
+		--flag=g_error_new:3:c-format \
+		--flag=g_set_error:4:c-format \
+		--flag=g_markup_printf_escaped:1:c-format \
+		--flag=g_log:3:c-format \
+		--flag=g_print:1:c-format \
+		--flag=g_printerr:1:c-format \
+		--flag=g_printf:1:c-format \
+		--flag=g_fprintf:2:c-format \
+		--flag=g_sprintf:2:c-format \
+		--flag=g_snprintf:3:c-format \
+		--flag=g_scanner_error:2:c-format \
+		--flag=g_scanner_warn:2:c-format \
+		--files-from=$(POTFILES) $(XGETTEXT_ARGS) \
+	&& test ! -f $(GETTEXT_TEXTDOMAIN).po \
+		|| ( rm -f $(GETTEXT_TEXTDOMAIN).pot \
+			&& mv $(GETTEXT_TEXTDOMAIN).po $(GETTEXT_TEXTDOMAIN).pot )
+
+
+.PHONY: install clean $(GETTEXT_TEXTDOMAIN).pot
Index: s390-tools-sles11/iucvterm/po/POTFILES.in
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/po/POTFILES.in
@@ -0,0 +1,4 @@
+src/iucvconn.c
+src/functions.c
+src/iucvtty.c
+src/getopt.c
Index: s390-tools-sles11/iucvterm/src/Makefile
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/src/Makefile
@@ -0,0 +1,33 @@
+#! /usr/bin/make -f
+
+include ../../common.mak
+
+ifndef GETTEXT_TEXTDOMAIN
+GETTEXT_TEXTDOMAIN = iucvterm
+endif
+
+CPPFLAGS += -I../include -I../../include
+CPPFLAGS += -DUSE_NLS -DGETTEXT_TEXTDOMAIN=\"$(GETTEXT_TEXTDOMAIN)\"
+#CPPFLAGS += -D__DEBUG__
+
+PROGRAMS = iucvconn iucvtty
+
+all: $(PROGRAMS)
+check:
+install: strip
+	for prg in $(PROGRAMS); do \
+	  $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $$prg $(USRBINDIR) ; \
+	done
+
+strip: $(PROGRAMS)
+	$(STRIP) $^
+
+clean:
+	-rm -f *.o $(PROGRAMS)
+
+iucvconn: iucvconn.o getopt.o auditlog.o functions.o
+iucvtty: LDLIBS = -lutil
+iucvtty: iucvtty.o getopt.o auditlog.o functions.o
+
+
+.PHONY: install clean
Index: s390-tools-sles11/iucvterm/src/auditlog.c
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/src/auditlog.c
@@ -0,0 +1,207 @@
+/*
+ * iucvtty / iucvconn - IUCV Terminal Applications
+ *
+ * Functions for session logging/auditing.
+ * The session log and timing data files adhere to the format
+ * described in script(1).
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "iucvterm/functions.h"
+
+#define OPEN_FILEMODE		(O_WRONLY | O_CREAT | O_EXCL)
+#define OPEN_FILEMASK		(S_IRUSR  | S_IWUSR | S_IRGRP)
+
+static int script_fd   = -1;		/* fd of typescript file  */
+static int timing_fd   = -1;		/* fd of timing data file */
+static FILE *info_file = NULL;		/* FILE of info file      */
+static struct timeval last_tv;		/* tv to calculate timing */
+
+
+/**
+ * print_on_time() - Append formatted time string to a message.
+ * @prefix:	message
+ *
+ * Returns a new buffer starting with @prefix and appends
+ * a formatted string representation of the current time.
+ * Returns NULL if memory allocation has failed.
+ *
+ * The caller must free the returned buffer after use.
+ */
+static char *print_on_time(const char *prefix)
+{
+	char *buf = malloc(64 + strlen(prefix) + 1);
+	time_t t  = time(NULL);
+
+	if (buf != NULL) {
+		if (t == (time_t) -1)
+			sprintf(buf, "%s\n", prefix);
+		else {
+			sprintf(buf, "%s on ", prefix);
+			ctime_r(&t, buf + strlen(prefix) + 4);
+		}
+	}
+	return buf;
+}
+
+/**
+ * write_session_info() - Write data to the session info file
+ * @format:	Format string, shall not be NULL
+ *
+ * Writes informational messages to the session info file.
+ * The info message is prefixed with a timestamp as returned by the
+ * time(2) syscall.
+ */
+void write_session_info(const char *format, ...)
+{
+	va_list ap;
+
+	if (info_file == NULL)
+		return;
+
+	fprintf(info_file, "%lu ", time(NULL));
+	va_start(ap, format);
+	vfprintf(info_file, format, ap);
+	va_end(ap);
+	if (strrchr(format, '\n') == NULL)
+		fprintf(info_file, "\n");
+}
+
+/**
+ * write_session_log() - Write session data to the session log file
+ * @buf:	Pointer to a buffer with data to log
+ * @len:	Copy up to @len bytes from @buf
+ *
+ * The routines writes up to @len bytes of data from buffer @buf to
+ * the session transcript; write appropriate timing data to the timing
+ * file.
+ */
+ssize_t write_session_log(const void* buf, size_t len)
+{
+	ssize_t rc;
+	int count;
+	char data[64] = "";
+	struct timeval curr_tv;
+	long time_diff;
+
+	/* immediately return if there is no fd to write to */
+	if (script_fd == -1)
+		return -1;
+
+	rc = __write(script_fd, buf, len);
+	if (rc < 0)
+		return rc;
+
+	/* calculate delay and write timing info */
+	if (gettimeofday(&curr_tv, NULL))
+		time_diff = 1000000;	/* one second (in usecs) */
+	else {
+		time_diff = (curr_tv.tv_sec  - last_tv.tv_sec) * 1000000 +
+			     curr_tv.tv_usec - last_tv.tv_usec;
+		last_tv   = curr_tv;	/* reset last timeval */
+	}
+
+	count = sprintf(data, "%.6f %zu\n",
+			(double) time_diff / (double) 1000000, len);
+	rc = __write(timing_fd, data, (count < 0) ? 0 : count );
+	if (rc < 0)
+		return rc;
+
+	return 0;
+}
+
+/**
+ * close_session_log() - Close session logging
+ *
+ * The routine writes a trailer to the session log file and
+ * closes the session log, timing and info file descriptor.
+ */
+void close_session_log(void)
+{
+	char *trailer;
+
+	if (script_fd > 0) {
+		trailer = print_on_time("Script done");
+		if (trailer != NULL) {
+			__write(script_fd, trailer, strlen(trailer));
+			write_session_info(trailer);
+			free(trailer);
+		}
+		close(script_fd);
+	}
+	if (timing_fd > 0)
+		close(timing_fd);
+	if (info_file != NULL)
+		fclose(info_file);
+}
+
+/**
+ * open_session_log() - Open session logging
+ * @filepath:	File path to the session transcript
+ *
+ * Opens the session, timing and info log file.
+ * If the session specified by @filepath already exists; or one of the
+ * files cannot be opened successfully, return an error.
+ */
+int open_session_log(const char *filepath)
+{
+	char *buf;
+	int   old_errno;
+	int   info_fd;
+
+	buf = calloc(11 + strlen(filepath), sizeof(char));
+	if (buf == NULL)
+		goto out_no_mem;
+
+	script_fd = open(filepath, OPEN_FILEMODE, OPEN_FILEMASK);
+	if (script_fd == -1)
+		goto out_error_open;
+
+	sprintf(buf, "%s.timing", filepath);
+	timing_fd = open(buf, OPEN_FILEMODE, OPEN_FILEMASK);
+	if (timing_fd == -1)
+		goto out_error_open;
+
+	sprintf(buf, "%s.info", filepath);
+	info_fd = open(buf, OPEN_FILEMODE, OPEN_FILEMASK);
+	if (info_fd == -1)
+		goto out_error_open;
+	info_file = fdopen(info_fd, "w");
+	if (info_file == NULL)
+		goto out_error_open;
+
+	if(gettimeofday(&last_tv, NULL))
+		goto out_error_open;
+
+	free(buf);
+
+	buf = print_on_time("Script started");
+	if (buf != NULL) {
+		__write(script_fd, buf, strlen(buf));
+		write_session_info(buf);
+		free(buf);
+	}
+
+	return 0;
+
+out_error_open:
+	old_errno = errno;	/* preserve errno */
+	free(buf);
+	close_session_log();
+	errno = old_errno;	/* restore errno from failed open call */
+out_no_mem:
+	return -1;
+}
Index: s390-tools-sles11/iucvterm/src/functions.c
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/src/functions.c
@@ -0,0 +1,519 @@
+/*
+ * iucvtty / iucvconn - IUCV Terminal Applications
+ *
+ * Common functions
+ *
+ * Copyright IBM Corp. 2008, 2009
+ * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <regex.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "af_iucv.h"
+#include "iucvterm/config.h"
+#include "iucvterm/gettext.h"
+#include "iucvterm/functions.h"
+#include "iucvterm/proto.h"
+
+
+/* Global program component for iucv terminal tools */
+#define PRG_COMPONENT		"iucvterm"
+
+
+/**
+ * __write() - Write data
+ * @fd:		File descriptor
+ * @buf:	Pointer to data buffer
+ * @len:	Buffer length
+ *
+ * Write @len number of bytes from the buffer @buf to the file
+ * descriptor @fd. The routines handles EINTR and partially writes.
+ * Returns the error code from the underlying write(2) syscall.
+ */
+ssize_t __write(int fd, const void *buf, size_t len)
+{
+	ssize_t rc;
+	size_t  written = 0;
+
+	while (written < len) {
+		rc = write(fd, buf + written, len - written);
+		if (rc == -1 && errno == EINTR)
+			continue;
+		if (rc <= 0)
+			return rc;
+		written += rc;
+	}
+	return written;
+}
+
+#ifdef __DEBUG__
+static void __dump_msg(int fd, const struct iucvtty_msg *m, char dir)
+{
+	write_session_info("%c: (fd=%d) MSG: ver=%02x type=%02x datalen=%u\n",
+			   dir, fd, m->version, m->type, (uint16_t) m->datalen);
+}
+#endif
+
+
+/**
+ * iucvtty_socket() - Creates and return an IUCV socket
+ * @sai:	AF_IUCV socket address structure
+ * @host:	z/VM guest name
+ * @service:	Terminal name passed as additional data to @host after connect
+ *
+ * This function sets up the struct sockaddr_iucv with the specified
+ * VM guest virtual machine and terminal information.
+ * Finally, it returns an AF_IUCV socket.
+ */
+int iucvtty_socket(struct sockaddr_iucv *sai,
+		   const char *host, const char *service)
+{
+	char temp[9];
+
+	memset(sai, 0, sizeof(struct sockaddr_iucv));
+	sai->siucv_family = AF_IUCV;
+
+	if (host != NULL) {
+		snprintf(temp, 9, "%-8s", host);
+		memcpy(sai->siucv_user_id, temp, 8);
+	} else
+		memset(sai->siucv_user_id, ' ', 8);
+
+	if (service != NULL) {
+		snprintf(temp, 9, "%-8s", service);
+		memcpy(sai->siucv_name, temp, 8);
+	} else
+		memset(sai->siucv_user_id, ' ', 8);
+
+	return socket(PF_IUCV, SOCK_STREAM, 0);
+}
+
+/**
+ * iucvtty_tx_termenv() - Send terminal environment variable
+ * @dest:	File descriptor to output data
+ * @dflt:	TERM environment string ('\0' terminated)
+ *
+ * Copy terminal environment variable to destination @dest.
+ */
+int iucvtty_tx_termenv(int dest, char *dflt)
+{
+	struct iucvtty_msg *msg;
+	char *term = getenv("TERM");
+	size_t len;
+	int rc;
+
+	if (term == NULL && dflt != NULL)
+		term = dflt;
+
+	len = 0;
+	if (term != NULL)
+		len = 1 + strlen(term);
+
+	/* Note: The server console tool waits for terminal environment
+	 *       information: the message is sent even if it is empty */
+	msg = msg_alloc(MSG_TYPE_TERMENV, len);
+	if (msg == NULL)
+		return -1;
+	msg_cpy_from(msg, term, len);
+	rc = iucvtty_write_msg(dest, msg);
+	msg_free(msg);
+
+	return rc;
+}
+
+/**
+ * iucvtty_rx_termenv() - Receive terminal environment variable
+ * @fd:		File descriptor to read data from
+ * @buf:	Buffer to store the terminal environment variable
+ * @len:	Size of buffer @buf
+ */
+int iucvtty_rx_termenv(int fd, void *buf, size_t len)
+{
+	int rc;
+	size_t skip;
+	struct iucvtty_msg *msg = msg_alloc(MSG_TYPE_TERMENV, len);
+
+	if (msg == NULL)
+		return -1;
+	skip = 0;
+	rc = iucvtty_read_msg(fd, msg, msg_size(msg), &skip);
+	iucvtty_skip_msg_residual(fd, &skip);
+	if (!rc) {
+		if (msg->datalen == 0)
+			memset(buf, 0, min(1, len));
+		else
+			msg_cpy_to(msg, buf, len);
+	}
+	msg_free(msg);
+	return rc;
+}
+
+/**
+ * iucvtty_tx_data() - Send terminal data
+ * @from:	File descriptor to read data from
+ * @msg:	Pointer to iucv tty message buffer
+ * @len:	Size of message buffer
+ *
+ * This routine reads data from file descriptor @from and stores them in
+ * a data array of the specified iucv tty message @msg. It reads up to
+ * @len - MSG_DATA_OFFSET bytes from fd @from.
+ */
+int iucvtty_read_data(int from, struct iucvtty_msg *msg, size_t len)
+{
+	ssize_t r;
+
+	r = read(from, msg->data, len - MSG_DATA_OFFSET);
+	if (r == -1 && errno == EINTR)	/* REVIEW: loop if EINTR ? */
+		r = read(from, msg->data, len - MSG_DATA_OFFSET);
+	if (r <= 0)
+		return -1;
+	msg->version = MSG_VERSION;
+	msg->type    = MSG_TYPE_DATA;
+	msg->datalen = (uint16_t) r;
+
+	return 0;
+}
+
+/**
+ * iucvtty_tx_data() - Send terminal data
+ * @dest:	File descriptor to send data to
+ * @from:	File descriptor to read data from
+ * @msg:	Pointer to iucv tty message buffer
+ * @len:	Size of message buffer
+ *
+ * This routine reads data from file descriptor @from and stores them in
+ * a data array of the specified iucv tty message @msg. It reads up to
+ * @len - MSG_DATA_OFFSET bytes from fd @from.
+ * Finally, the iucv tty message written to file descriptor @dest.
+ */
+int iucvtty_tx_data(int dest, int from, struct iucvtty_msg *msg, size_t len)
+{
+	if (iucvtty_read_data(from, msg, len))
+		return -1;
+	if (iucvtty_write_msg(dest, msg))
+		return -1;
+	return 0;
+}
+
+/**
+ * iucvtty_tx_winsize() - Send terminal window size information.
+ * @dest:	Destination
+ * @from:	Terminal file descriptor to request winsize
+ *
+ * Sends the terminal window size from terminal file descriptor
+ * @from to the destination @dest.
+ * If the window size is not retrieved, the routine will not fail.
+ * The routine fails if there is a problem sending the window size
+ * to @dest. The return codes are specified by iucvtty_write_msg().
+ */
+int iucvtty_tx_winsize(int dest, int from)
+{
+	int rc;
+	struct iucvtty_msg *msg = msg_alloc(MSG_TYPE_WINSIZE,
+					      sizeof(struct winsize));
+	if (msg == NULL)
+		return -1;
+	rc = 0;
+	if (ioctl(from, TIOCGWINSZ, msg->data) > -1)
+		rc = iucvtty_write_msg(dest, msg);
+	msg_free(msg);
+
+	return rc;
+}
+
+/**
+ * iucvtty_tx_error() - Send an error code
+ * @dest:	Destination
+ * @errCode:	Error code
+ */
+int iucvtty_tx_error(int dest, uint32_t errCode)
+{
+	struct iucvtty_msg *msg;
+	int rc;
+
+	msg = msg_alloc(MSG_TYPE_ERROR, sizeof(errCode));
+	if (msg == NULL)
+		return -1;
+	msg_cpy_from(msg, &errCode, sizeof(errCode));
+	rc = iucvtty_write_msg(dest, msg);
+	msg_free(msg);
+	return rc;
+}
+
+/**
+ * iucvtty_copy_data() - Copy IUCV message data
+ * @dest:	Destination to copy data to
+ * @msg:	IUCV message
+ */
+int iucvtty_copy_data(int dest, struct iucvtty_msg *msg)
+{
+	if (__write(dest, msg->data, msg->datalen) <= 0)
+		return -1;
+	return 0;
+}
+
+/**
+ * iucvtty_skip_msg_residual() - Skip (receive & forget) count number of bytes
+ * @fd:		File descriptor
+ * @residual:	Residual of an iucv tty message received by iucvtty_read_msg()
+ *
+ * See iucvtty_read_msg() for an explanation when to use this routine.
+ * Note: The @residual parameter shall not be NULL.
+ */
+void iucvtty_skip_msg_residual(int fd, size_t *residual)
+{
+	char b;
+	size_t  i;
+
+	if (*residual <= 0)
+		return;
+	for (i = 0; i < *residual; i++)
+		if (read(fd, &b, 1) <= 0)
+			break;
+	*residual = 0;
+}
+
+/**
+ * iucvtty_read_msg() - Read/Receive an IUCV message
+ * @fd:		File descriptor to read from
+ * @msg:	Pointer to IUCV message buffer
+ * @len:	IUCV message data len
+ * @residual:	Status to be used by next call
+ *
+ * The function reads up to @len bytes from file descriptor @fd.
+ * If the received message is larger than @len bytes, the @residual value
+ * is set to the number of bytes remaining.
+ * The function shall then be re-called to create a new message and receive
+ * the next chunk of size @residual; or the remaining characters must be
+ * skipped using the iucvtty_skip_msg() routine.
+ * Note: The @len parameter shall be greater than MSG_DATA_OFFSET.
+ *       The @residual parameter shall not be NULL.
+ */
+int iucvtty_read_msg(int fd, struct iucvtty_msg *msg,
+		     size_t len, size_t *residual)
+{
+	int rc;
+	ssize_t r;		/* number of bytes read from fd */
+
+	if (*residual)
+		len = min(len - MSG_DATA_OFFSET, *residual);
+
+	while (1) {
+		if (*residual) {
+			r = read(fd, msg->data, len);
+			if (r > 0)
+				msg->datalen = r;
+		} else
+			r = read(fd, msg, len);
+
+		if (r == -1 && errno == EINTR)
+			continue;
+		if (r <= 0) {
+			rc = -1;
+			goto out_read_error;
+		}
+
+		break;	/* exit loop for a successful read */
+	}
+#ifdef __DEBUG__
+	if (!*residual)
+		__dump_msg(fd, msg, 'R');
+#endif
+
+	/* (re)calculate next chunk */
+	if (*residual)
+		*residual -= msg->datalen;
+	else
+		if (msg->datalen > (r - MSG_DATA_OFFSET)) {
+			/* calculate pending msg data and update datalen */
+			*residual = msg->datalen - (r - MSG_DATA_OFFSET);
+			msg->datalen = r - MSG_DATA_OFFSET;
+		}
+
+	/* check for a sane message */
+	if (msg->version != MSG_VERSION) {
+		fprintf(stderr, _("%s: %s\n"),
+			PRG_COMPONENT, _("The version of the received data "
+					 "message is not supported\n"));
+		rc = -2;
+		goto out_read_error;
+	}
+
+	rc = 0;
+out_read_error:
+	return rc;
+}
+
+/**
+ * iucvtty_write_msg() - Write/Send IUCV message
+ * @fd:		File descriptor
+ * @msg:	Pointer to IUCV message
+ */
+int iucvtty_write_msg(int fd, struct iucvtty_msg *msg)
+{
+	msg->version = MSG_VERSION;
+
+	if (__write(fd, msg, msg_size(msg)) <= 0)
+		return -1;
+
+#ifdef __DEBUG__
+	__dump_msg(fd, msg, 'S');
+#endif
+	return 0;
+}
+
+
+/**
+ * iucv_msg_error() - Reports an IUCV message error
+ * @comp:	Program component
+ * @errnum:	IUCV message error code
+ */
+void iucv_msg_error(const char *comp, uint32_t errnum)
+{
+	const char *translated;
+
+	switch (errnum) {
+	case ERR_FORK:
+		translated = _("Creating a new process to run the "
+				"login program failed");
+		break;
+	case ERR_CANNOT_EXEC_LOGIN:
+		translated = _("Running the login program failed");
+		break;
+	case ERR_SETUP_LOGIN_TTY:
+		translated = _("Setting up a terminal for user login failed");
+		break;
+	case ERR_NOT_AUTHORIZED:
+		translated = _("The z/VM guest virtual machine is not "
+				"permitted to connect");
+		break;
+	default:
+		translated = _("The specified error code is not known");
+		break;
+	}
+	fprintf(stderr, "%s: %s (%s=%" PRIu32 ")\r\n",
+		comp, translated, _("error code"), errnum);
+}
+
+/**
+ * program_error() - Report an program/syscall error.
+ * @comp:	Program component name
+ * @d:		Error message, subject to gettext translation
+ */
+void program_error(const char *comp, const char *d)
+{
+	fprintf(stderr, _("%s: %s: %s\n"), comp, _(d), strerror(errno));
+}
+
+
+/**
+ * __regerror - Report an error from a previous regex api call
+ * @error:	Error code
+ * @re:		Reference to the used regular expression
+ */
+static inline void __regerror(int error, const regex_t *re)
+{
+	char errbuf[81];
+
+	regerror(error, re, errbuf, 81);
+	fprintf(stderr, _("The regular expression has an error: %s\n"), errbuf);
+	return;
+}
+
+/**
+ * is_regex_valid() - Check if the specified regex is syntactically correct.
+ * @re:		String representation of the regular expression
+ *
+ * Returns zero on success, otherwise -1.
+ */
+int is_regex_valid(const char *re)
+{
+	regex_t regex;
+	int rc;
+
+	if (re == NULL)
+		return -1;
+	rc = regcomp(&regex, re, REG_EXTENDED | REG_ICASE | REG_NOSUB);
+	if (rc) {
+		__regerror(rc, &regex);
+		rc = -1;
+	}
+	regfree(&regex);
+
+	return rc;
+}
+
+/**
+ * strmatch() - Match a string using a regular expression
+ * @str:	String to match
+ * @re:		Regular expression
+ *
+ * Returns zero on success, -1 on error or if @str is NULL; and
+ * 1 if the regular expression did not match the string.
+ */
+int strmatch(const char *str, const char *re)
+{
+	regex_t regex;
+	regmatch_t pmatch[1];
+	size_t nmatch = 0;
+	int rc;
+
+	if (re == NULL)
+		return -1;
+
+	rc = regcomp(&regex, re, REG_EXTENDED | REG_ICASE | REG_NOSUB);
+	if (rc) {
+		__regerror(rc, &regex);
+		regfree(&regex);
+		return -1;
+	}
+
+	rc = regexec(&regex, str, nmatch, pmatch, 0);
+	if (rc == REG_NOMATCH)
+		rc = 1;
+
+	regfree(&regex);
+	return rc;
+}
+
+/**
+ * is_client_allowed() - Check if the client is allowed to connect.
+ * @client:	Client name
+ * @cfg:	Pointer to the IUCV terminal configuration structure
+ *
+ * The return code is identical to strmatch(). The function returns always
+ * zero if client_checking is disabled by the configuration.
+ */
+int is_client_allowed(const char *client, const struct iucvtty_cfg *cfg)
+{
+	if (!cfg->client_check)
+		return 0;
+
+	return strmatch(client, cfg->client_re);
+}
+
+/**
+ * userid_cpy() - Copy z/VM user ID and skip trailing spaces.
+ * @dest:	Destination buffer
+ * @userid:	z/VM user ID
+ */
+void userid_cpy(char dest[9], const char userid[8])
+{
+	ssize_t pos;
+
+	/* find pos of last character (pos 0..7) or -1 if user ID is empty */
+	for (pos = 7; pos >= 0; pos--)
+		if (userid[pos] != ' ')
+			break;
+
+	if (pos >= 0)
+		memcpy(dest, userid, pos + 1);
+	dest[pos + 1] = '\0';
+}
Index: s390-tools-sles11/iucvterm/src/getopt.c
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/src/getopt.c
@@ -0,0 +1,226 @@
+/*
+ * iucvtty / iucvconn - IUCV Terminal Applications
+ *
+ * Processing of command line arguments
+ *
+ * Copyright IBM Corp. 2008, 2009
+ * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ */
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "iucvterm/config.h"
+#include "iucvterm/gettext.h"
+#include "iucvterm/functions.h"
+
+#include "zt_common.h"
+
+
+static const char iucvtty_usage[] = N_(
+"Usage: %s [-h|--help] [-v|--version]\n"
+"       %s [-a <regex>] <terminal id>  [-- <login program> [<args>]]\n\n"
+"Options:\n"
+"  -h, --help           Print this help, then exit.\n"
+"  -v, --version        Print version information, then exit.\n"
+"  -a, --allow-from     Permit connections from particular z/VM guests only.\n"
+"                       A z/VM guest is permitted if regex matches its name.\n"
+);
+
+static const char iucvconn_usage[] = N_(
+"Usage: %s [-h|--help] [-v|--version]\n"
+"       %s [-e esc] [-s <file>] <vm guest> <terminal id>\n\n"
+"Options:\n"
+"  -h, --help           Print this help, then exit.\n"
+"  -v, --version        Print version information, then exit.\n"
+"  -s, --sessionlog     Write terminal session to file.\n"
+"  -e, --escape-char    Escape character (can be one of: A-Y, ], ^ or _)\n"
+"                       Characters C, D, Q, S, Z and [ are not allowed.\n"
+);
+
+static const struct option iucvterm_long_opts[] = {
+	{ "help",	  no_argument, NULL, 'h' },
+	{ "version",	  no_argument, NULL, 'v' },
+	{ "allow-from",	  required_argument, NULL, 'a' },
+	{ "sessionlog",   required_argument, NULL, 's' },
+	{ "escape-char",  required_argument, NULL, 'e' },
+	{  NULL,	  no_argument, NULL,  0  }
+};
+
+struct tool_info {
+	char		name[10];
+	char		optstring[10];
+	const char	*usage;
+	unsigned char	reqNonOpts;
+};
+
+/* program specific command line settings */
+static const struct tool_info iucv_tool[2] = {
+	{	.name = "iucvtty",
+		.optstring = "-hva:",
+		.usage = iucvtty_usage,
+		.reqNonOpts = 1,
+	},
+	{	.name = "iucvconn",
+		.optstring = "-hvs:e:",
+		.usage = iucvconn_usage,
+		.reqNonOpts = 2,
+	}
+};
+
+
+static void usage_exit(const struct tool_info *prg, int is_error,
+			const char *msg)
+{
+	if (msg != NULL)
+		fprintf(stderr, _("%s: %s\n"), prg->name, msg);
+	fprintf(stderr, _(prg->usage), prg->name, prg->name);
+	exit(is_error ? 1 : 0);	/* rc=1 .. invalid args */
+}
+
+static void version_exit(const struct tool_info *prg)
+{
+	printf(_("%s: IUCV Terminal Applications, version %s\n"),
+		prg->name, RELEASE_STRING);
+	printf(_("Copyright IBM Corp. 2008, 2009\n"));
+	exit(0);
+}
+
+static void cpy_or_exit(char *dest, const char *src, size_t size,
+			const struct tool_info *prg, const char *param)
+{
+	if (strlen(src) >= size){
+		fprintf(stderr,
+			_("%s: %s exceeds the maximum of %zu characters\n"),
+			prg->name, param, size - 1);
+		exit(1);
+	}
+	strncpy(dest, src, size);
+	dest[size - 1] = 0;
+}
+
+static void set_esc_or_exit(const struct tool_info *prg,
+	                    const char val, unsigned char *esc)
+{
+	unsigned char upval = toupper(val);
+
+	/* range of valid escape keys: A-Z [ \ ] ^ _ */
+	if (upval < 'A' || upval > '_')
+		usage_exit(prg, 1, _("The specified character is not a "
+				     "valid escape character"));
+
+	switch (upval) {
+	case 'C':	/* interrupt (ISIG) */
+	case 'D':	/* EoF / EoT */
+	case 'Q':	/* XON */
+	case 'S':	/* XOFF */
+	case 'Z':	/* suspend (shell) */
+	case '[':	/* ESC */
+		usage_exit(prg, 1, _("The specified character is not a "
+				     "valid escape character"));
+	default:
+		*esc = upval ^ 0100;	/* see ascii(7) */
+		break;
+	}
+}
+
+
+void parse_options(enum iucvterm_prg prg, struct iucvtty_cfg *config,
+		   int argc, char **argv)
+{
+	int c;
+	int index;
+	int nonOpts = 0;
+
+	config->cmd_parms = NULL;
+	config->client_check = 0;
+	config->sessionlog = NULL;
+	config->esc_char = '_' ^ 0100;		/* Ctrl-_ (0x1f) */
+
+	while (1) {
+		index = -1;
+		c = getopt_long(argc, argv, iucv_tool[prg].optstring,
+				iucvterm_long_opts, &index);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 1:
+			if (nonOpts >= iucv_tool[prg].reqNonOpts) {
+				usage_exit(&iucv_tool[prg], 1, NULL);
+				break;
+			}
+			switch (nonOpts) {
+			case 0:
+				if (prg == PROG_IUCV_CONN)
+					cpy_or_exit(config->host, optarg,
+						    sizeof(config->host),
+						    &iucv_tool[prg],
+						    _("<vm guest>"));
+				else
+					cpy_or_exit(config->service, optarg,
+						    sizeof(config->service),
+						    &iucv_tool[prg],
+						    _("<terminal id>"));
+				break;
+			case 1:
+				cpy_or_exit(config->service, optarg,
+					    sizeof(config->service),
+					    &iucv_tool[prg],
+					    _("<terminal id>"));
+				break;
+			default:
+				usage_exit(&iucv_tool[prg], 1, NULL);
+				break;
+			}
+			++nonOpts;
+			break;
+		case 'a':/* max 80 */
+			cpy_or_exit(config->client_re, optarg,
+				    sizeof(config->client_re),
+				    &iucv_tool[prg], _("<regex>"));
+			if (is_regex_valid(config->client_re))
+				exit(1);
+			config->client_check  = 1;
+			break;
+		case 'e':
+			switch (strlen(optarg)) {
+			case 1:
+				set_esc_or_exit(&iucv_tool[prg], optarg[0],
+					        &config->esc_char);
+				break;
+			case 4:
+				if (memcmp(optarg, "none", 4) == 0) {
+					config->esc_char = 0;
+					break;
+				}
+				/* fall through */
+			default:
+				usage_exit(&iucv_tool[prg], 1,
+					   _("The escape character must be a "
+					     "single character or 'none'"));
+			}
+			break;
+		case 's':
+			config->sessionlog = optarg;
+			break;
+		case 'h':
+			usage_exit(&iucv_tool[prg], 0, NULL);
+		case 'v':
+			version_exit(&iucv_tool[prg]);
+		case '?':
+			printf(_("Try '%s --help' for more information.\n"),
+				iucv_tool[prg].name);
+			exit(1);
+			break;
+		}
+	}
+
+	if (optind < argc)	/* save additional parameters */
+		config->cmd_parms = argv + optind;
+
+	if (nonOpts < iucv_tool[prg].reqNonOpts)	/* not enough args */
+		usage_exit(&iucv_tool[prg], 1,
+			_("The command does not have enough arguments"));
+}
Index: s390-tools-sles11/iucvterm/src/iucvconn.c
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/src/iucvconn.c
@@ -0,0 +1,349 @@
+/*
+ * iucvconn - Application that establishes a terminal connection over IUCV
+ *
+ * Core application
+ *
+ * Copyright IBM Corp. 2008, 2009
+ * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ */
+#include <errno.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <syslog.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "iucvterm/gettext.h"
+#include "iucvterm/config.h"
+#include "iucvterm/functions.h"
+
+
+#define SYSLOG_IDENT		"iucvconn"
+#define PRG_COMPONENT		SYSLOG_IDENT
+#define DEFAULT_TERM		"linux"
+
+#define AUDIT(f, ...)		do { \
+					syslog(LOG_INFO, (f), __VA_ARGS__); \
+					write_session_info((f), __VA_ARGS__); \
+				} while (0);
+
+/* escape mode actions */
+enum esc_action_t {
+	DISCONNECT,		/* Disconnect from terminal   */
+	RESIZE,			/* Force terminal resizing    */
+	SEND,			/* Send data (default action) */
+	IGNORE,			/* Ignore escape character   */
+};
+
+
+static volatile sig_atomic_t resize_tty;
+static struct termios ios_orig;		/* store original termio settings */
+
+
+/**
+ * sig_handler() - Signal handler
+ * @sig:	Signal number
+ */
+static void sig_handler(int sig)
+{
+	switch (sig) {
+	case SIGWINCH:
+		resize_tty = sig;
+		break;
+
+	case SIGTERM:
+		tcsetattr(STDIN_FILENO, TCSANOW, &ios_orig);
+		close_session_log();
+		_exit(0);
+		break;
+	}
+}
+
+/**
+ * get_msg_char() - Returns single character from message
+ * @msg:	The IUCV terminal message
+ *
+ * Returns the (single) character of an IUCV terminal message
+ * if @msg is of type MSG_TYPE_DATA and contains a single character
+ * (datalen == 1). Otherwise the routine returns zero.
+ */
+static unsigned char get_msg_char(const struct iucvtty_msg *msg)
+{
+	if (msg->type != MSG_TYPE_DATA || msg->datalen != 1)
+		return 0;
+
+	return msg->data[0];
+}
+
+/**
+ * is_esc_char() - Check message for escape character
+ * @msg:	The IUCV terminal message
+ * @esc:	The escape character
+ *
+ * Returns 1 if @msg contains the escape charater @esc only; otherwise the
+ * function returns 0.
+ */
+static int is_esc_char(const struct iucvtty_msg *msg, unsigned char esc)
+{
+	if (!esc)
+		return 0;
+
+	return (get_msg_char(msg) == esc) ? 1 : 0;
+}
+
+/**
+ * get_action() - Returns the action from an escaped character
+ * @msg:	The IUCV terminal message
+ * @esc:	The escape character
+ *
+ * Returns the appropriate action of the escaped charater.
+ * The action is derived from the single character stored in the IUCV terminal
+ * message @msg. If the escape character is recognized, SEND is returned for
+ * sending the "escaped" escape character to the terminal.
+ *
+ * If it contains multiple characters, the default SEND action is used to
+ * indicate that the escape mode is done and to force sending the complete data
+ * characters (e.g. entered by copy & paste).
+ *
+ * NOTE: This routine must be called in "escape mode".
+ */
+static enum esc_action_t get_action(const struct iucvtty_msg *msg,
+				    unsigned char esc)
+{
+	if (is_esc_char(msg, esc))
+		return SEND;
+
+	switch (get_msg_char(msg)) {
+	case   0:
+		return SEND;
+	case '.':
+	case 'd':
+		return DISCONNECT;
+	case 'r':
+		return RESIZE;
+	default:
+		return IGNORE;
+	}
+}
+
+/**
+ * iucvtty_worker() - Handle server connection
+ * @terminal:	IUCV TTY server file descriptor
+ */
+static int iucvtty_worker(int terminal, const struct iucvtty_cfg *cfg)
+{
+	struct iucvtty_msg *msg;
+	fd_set set;
+	size_t chunk;
+	int in_esc_mode;
+	enum esc_action_t action;
+
+	/* setup buffers */
+	msg = malloc(MSG_BUFFER_SIZE);
+	if (msg == NULL) {
+		print_error("Allocating memory for the data buffer failed");
+		return -1;
+	}
+
+	/* multiplex i/o between login program and socket */
+	chunk = 0;
+	in_esc_mode = 0;	/* escape mode state */
+	action = SEND;
+	while (1) {
+		if (resize_tty) {
+			iucvtty_tx_winsize(terminal, STDIN_FILENO);
+			resize_tty = 0;	/* clear signal flag */
+		}
+
+		FD_ZERO(&set);
+		FD_SET(terminal, &set);
+		FD_SET(STDIN_FILENO, &set);
+
+		if (select(max(STDIN_FILENO, terminal) + 1, &set,
+				 NULL, NULL, NULL) == -1) {
+			if (errno == EINTR)
+				continue;
+			break;
+		}
+
+		if (FD_ISSET(terminal, &set)) {
+			if (iucvtty_read_msg(terminal, msg,
+					     MSG_BUFFER_SIZE, &chunk))
+				break;
+
+			switch (msg->type) {
+			case MSG_TYPE_DATA:
+				iucvtty_copy_data(STDOUT_FILENO, msg);
+				write_session_log(msg->data, msg->datalen);
+				break;
+			case MSG_TYPE_ERROR:
+				iucvtty_error(*(uint32_t *) msg->data);
+				break;
+			}
+		}
+
+		if (FD_ISSET(STDIN_FILENO, &set)) {
+			if (iucvtty_read_data(STDIN_FILENO, msg,
+					      MSG_BUFFER_SIZE))
+				break;
+
+			if (in_esc_mode) {
+				in_esc_mode = 0;	/* reset */
+				action = get_action(msg, cfg->esc_char);
+			} else {
+				if (is_esc_char(msg, cfg->esc_char)) {
+					in_esc_mode = 1;
+					action = IGNORE;
+				} else
+					action = SEND;
+			}
+
+			/* handle escape mode */
+			switch (action) {
+			case SEND:	/* non-escape mode (default) */
+				if (iucvtty_write_msg(terminal, msg))
+					goto out_worker_loop;
+				break;
+
+			case DISCONNECT:/* disconnect  */
+				goto out_worker_loop;
+
+			case RESIZE: 	/* force terminal resize */
+				iucvtty_tx_winsize(terminal, STDIN_FILENO);
+				break;
+
+			case IGNORE:
+				break;
+			}
+		}
+	}
+
+out_worker_loop:
+	free(msg);
+	return 0;
+}
+
+/**
+ * main() - IUCV CONN program startup
+ */
+int main(int argc, char *argv[])
+{
+	int			rc;
+	int 			server;
+	struct sockaddr_iucv	addr;
+	struct termios		ios;
+	struct sigaction	sigact;
+	struct passwd		*passwd;
+	struct iucvtty_cfg	conf;
+
+
+	/* gettext initialization */
+	gettext_init();
+
+	/* parse command line options */
+	parse_options(PROG_IUCV_CONN, &conf, argc, argv);
+
+	/* open session audit log */
+	if (conf.sessionlog != NULL)
+		if (open_session_log(conf.sessionlog)) {
+			print_error("Creating the terminal session "
+				    "log files failed");
+			return 1;
+		}
+
+	/* open socket and connect to server */
+	server = iucvtty_socket(&addr, conf.host, conf.service);
+	if (server == -1) {
+		print_error((errno == EAFNOSUPPORT)
+			    ? N_("The AF_IUCV address family is not available")
+			    : N_("Creating the AF_IUCV socket failed"));
+		return 1;
+	}
+
+	/* syslog */
+	openlog(SYSLOG_IDENT, LOG_PID, LOG_AUTHPRIV);
+
+	/* get user information for syslog */
+	passwd = getpwuid(geteuid());
+
+	if (connect(server, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
+		switch (errno) {
+		case EAGAIN:
+			print_error("The new connection would exceed the "
+				    "maximum number of IUCV connections");
+			break;
+		case ENETUNREACH:
+			print_error("The target z/VM guest virtual machine "
+				    "is not logged on");
+			break;
+		case EACCES:
+			print_error("The IUCV authorizations do not permit "
+				    "connecting to the target z/VM guest");
+			break;
+		default:
+			print_error("Connecting to the z/VM guest virtual "
+				    "machine failed");
+			break;
+		}
+		AUDIT("Connection to %s/%s failed for user %s (uid=%i)",
+			conf.host, conf.service,
+			(passwd != NULL) ? passwd->pw_name : "n/a", geteuid());
+		rc = 2;
+		goto return_on_error;
+	}
+	AUDIT("Established connection to %s/%s for user %s (uid=%i)",
+		conf.host, conf.service,
+		(passwd != NULL) ? passwd->pw_name : "n/a", geteuid());
+
+	/* send client params */
+	iucvtty_tx_termenv(server, DEFAULT_TERM);
+	iucvtty_tx_winsize(server, STDIN_FILENO);
+
+	/* register signal handler */
+	sigemptyset(&sigact.sa_mask);
+	sigact.sa_flags = SA_RESTART;
+	sigact.sa_handler = sig_handler;
+	sigaction(SIGWINCH, &sigact, NULL);
+	sigaction(SIGTERM,  &sigact, NULL);
+
+	/* modify terminal settings */
+	if (tcgetattr(STDIN_FILENO, &ios_orig)) {
+		print_error("Getting the terminal I/O settings failed");
+		rc = 3;
+		goto return_on_error;
+	}
+	memcpy(&ios, &ios_orig, sizeof(ios));
+
+	/* put terminal into raw mode */
+	cfmakeraw(&ios);
+	/* NOTE: If the tty driver (ldisc) runs in TTY_DRIVER_REAL_RAW,
+	 *       we need to do the input character processing here;
+	 *       that means to translate CR into CR + NL (ICRNL).
+	 * Define TTY_REAL_RAW in for that case. */
+#ifdef TTY_REAL_RAW
+	ios.c_iflag |= ICRNL;			/* | IGNPAR | IGNBRK; */
+#endif
+	tcflush(STDIN_FILENO, TCIOFLUSH);
+	if (tcsetattr(STDIN_FILENO, TCSANOW, &ios)) {
+		print_error("Modifying the terminal I/O settings failed");
+		rc = 4;
+		goto return_on_error;
+	}
+
+	iucvtty_worker(server, &conf);
+
+	tcsetattr(STDIN_FILENO, TCSANOW, &ios_orig);
+
+	rc = 0;
+return_on_error:
+	close(server);
+	closelog();
+	close_session_log();
+
+	return rc;
+}
Index: s390-tools-sles11/iucvterm/src/iucvtty.c
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/src/iucvtty.c
@@ -0,0 +1,292 @@
+/*
+ * iucvtty - Application that provides a full-screen terminal for iucvconn
+ *
+ * Core application
+ *
+ * Copyright IBM Corp. 2008, 2009
+ * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <pty.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <utmp.h>
+
+#include "iucvterm/config.h"
+#include "iucvterm/gettext.h"
+#include "iucvterm/functions.h"
+#include "af_iucv.h"
+
+
+#define SYSLOG_IDENT		"iucvtty"
+#define PRG_COMPONENT           SYSLOG_IDENT
+#define TERM_BUFSIZE		256
+#define TERM_DEFAULT		"linux"
+
+
+static volatile sig_atomic_t sig_shutdown;
+
+
+/**
+ * sig_handler() - Signal handler
+ * @sig:	Signal number.
+ */
+static void sig_handler(int sig)
+{
+	sig_shutdown = sig;
+}
+
+/**
+ * exec_login_prog() - execute a login program
+ * @cmd:	Path to the (login) program executable
+ * @host:	Originator host that is passed to /bin/login
+ */
+static int exec_login_prog(char *cmd[], const char *host)
+{
+	int rc;
+
+	if (cmd != NULL)
+		rc = execv(cmd[0], cmd);
+	else
+		/* for root only: write hostname to [uw]tmp if set */
+		if (geteuid() == 0 && host != NULL)
+			rc = execl("/bin/login", "/bin/login", "-h", host,
+					(char *) NULL);
+		else
+			rc = execl("/bin/login", "/bin/login", (char *) NULL);
+	return rc;
+}
+
+/**
+ * iucvtty_worker() - Handle an incoming client connection
+ * @client:	Client file descriptor
+ * @master:	PTY master file descriptor
+ * @slave:	PTY slave file descriptor
+ * @host:	Originator z/VM guest virtual machine
+ * @cfg:	IUCV TTY configuration structure.
+ */
+static int iucvtty_worker(int client, int master, int slave,
+			  const char *host, const struct iucvtty_cfg *cfg)
+{
+	int rc;
+	struct iucvtty_msg *msg;
+	pid_t child;
+	fd_set set;
+	size_t chunk;
+	char term_env[TERM_BUFSIZE];
+
+
+	/* flush pending terminal data */
+	tcflush(master, TCIOFLUSH);
+
+	/* read terminal parameters from client */
+	if (iucvtty_rx_termenv(client, term_env, TERM_BUFSIZE))
+		sprintf(term_env, TERM_DEFAULT);
+
+	/* start login program */
+	child = fork();
+	if (child == -1) {
+		print_error("Creating a new process to run the "
+			    "login program failed");
+		iucvtty_tx_error(client, ERR_FORK);
+		return 1;	/* return from worker */
+	}
+	if (child == 0) {	/* child process */
+		closelog();	/* close syslog */
+
+		/* setup terminal */
+		if (login_tty(slave)) {
+			print_error("Setting up a terminal for user login failed");
+			iucvtty_tx_error(client, ERR_SETUP_LOGIN_TTY);
+			exit(2);
+		}
+		setenv("TERM", term_env, 1);
+		if (exec_login_prog(cfg->cmd_parms, host)) {
+			print_error("Running the login program failed");
+			iucvtty_tx_error(client, ERR_CANNOT_EXEC_LOGIN);
+		}
+		exit(3);	/* we only reach here if exec has failed */
+	}
+
+	/* setup buffers */
+	msg = malloc(MSG_BUFFER_SIZE);
+	if (msg == NULL) {
+		print_error("Allocating memory for the data buffer failed");
+		rc = 2;
+		goto out_kill_login;
+	}
+
+	/* multiplex i/o between login program and socket. */
+	rc = 0;
+	chunk = 0;
+	while (!sig_shutdown) {
+		FD_ZERO(&set);
+		FD_SET(client, &set);
+		FD_SET(master, &set);
+
+		if (select(max(master, client) + 1, &set,
+			   NULL, NULL, NULL) == -1) {
+			if (errno == EINTR)
+				continue;
+			break;
+		}
+
+		if (FD_ISSET(client, &set)) {
+			if (iucvtty_read_msg(client, msg,
+					     MSG_BUFFER_SIZE, &chunk))
+				break;
+			switch (msg->type) {
+			case MSG_TYPE_DATA:
+				iucvtty_copy_data(master, msg);
+				break;
+			case MSG_TYPE_WINSIZE:
+				if (msg->datalen != sizeof(struct winsize))
+					break;
+				if (ioctl(master, TIOCSWINSZ,
+					  (struct winsize *) msg->data))
+					print_error("Resizing the terminal "
+						    "window failed");
+				break;
+			case MSG_TYPE_TERMIOS:	/* ignored */
+				break;
+			case MSG_TYPE_ERROR:
+				iucvtty_error(*(uint32_t *) msg->data);
+				break;
+			}
+		}
+
+		if (FD_ISSET(master, &set))
+			if (iucvtty_tx_data(client, master,
+					    msg, MSG_BUFFER_SIZE))
+				break;
+	}
+	free(msg);
+
+out_kill_login:
+	/* ensure the chld is terminated before calling waitpid:
+	 * - in case a sigterm has been received,
+	 * - or a sigchld from other than the chld
+	 */
+	kill(child, SIGKILL);	/* cause a sigchld */
+	waitpid(child, NULL, 0);
+
+	return rc;
+}
+
+/**
+ * main() - IUCV TTY program startup
+ */
+int main(int argc, char *argv[])
+{
+	struct iucvtty_cfg	conf;		/* program configuration */
+	struct sockaddr_iucv	saddr, caddr;	/* IUCV socket address info */
+	char 			client_host[9];	/* client guest name */
+	int 			server, client;	/* socket file descriptors */
+	int 			master, slave;	/* pre-allocated PTY fds */
+	struct sigaction	sigact;		/* signal handler */
+	int 			rc;
+	socklen_t		len;
+
+
+	/* gettext initialization */
+	gettext_init();
+
+	/* parse command line arguments */
+	parse_options(PROG_IUCV_TTY, &conf, argc, argv);
+
+	/* create server socket... */
+	server = iucvtty_socket(&saddr, NULL, conf.service);
+	if (server == -1) {
+		print_error((errno == EAFNOSUPPORT)
+			    ? N_("The AF_IUCV address family is not available")
+			    : N_("Creating the AF_IUCV socket failed"));
+		return 1;
+	}
+	if (bind(server, (struct sockaddr *) &saddr, sizeof(saddr)) == -1) {
+		print_error("Binding the AF_IUCV socket failed");
+		close(server);
+		return 1;
+	}
+	if (listen(server, 1) == -1) {
+		print_error("Listening for incoming connections failed");
+		close(server);
+		return 1;
+	}
+
+	/* pre-allocate PTY master/slave file descriptors */
+	if (openpty(&master, &slave, NULL, NULL, NULL)) {
+		print_error("Opening a new PTY master/slave device pair failed");
+		close(server);
+		return 1;
+	}
+
+	/* set close-on-exec for file descriptors */
+	fcntl(master, F_SETFD, FD_CLOEXEC);
+	fcntl(server, F_SETFD, FD_CLOEXEC);
+
+	/* syslog */
+	openlog(SYSLOG_IDENT, LOG_PID, LOG_AUTHPRIV);
+	syslog(LOG_INFO, "Listening on terminal ID: %s, using pts device: %s",
+		conf.service, ttyname(slave));
+
+	rc = 0;
+	len = sizeof(struct sockaddr_iucv);
+	/* accept a new client connection */
+	client = accept(server, (struct sockaddr *) &caddr, &len);
+	if (client == -1) {
+		print_error("An incoming connection could not be accepted");
+		rc = 2;
+		goto exit_on_error;
+	}
+
+	/* check if client is allowed to connect */
+	userid_cpy(client_host, caddr.siucv_user_id);
+	if (is_client_allowed(client_host, &conf)) {
+		iucvtty_tx_error(client, ERR_NOT_AUTHORIZED);
+		syslog(LOG_WARNING, "Rejected client connection from %s; "
+				    "Client is not allowed to connect.",
+				    client_host);
+		rc = 3;
+
+	} else { /* client is allowed to connect */
+		syslog(LOG_INFO, "Accepted client connection from %s",
+			client_host);
+		/* set close-on-exec for client socket */
+		fcntl(client, F_SETFD, FD_CLOEXEC);
+		/* close server socket */
+		close(server);
+
+		/* setup signal handler to notify shutdown signal */
+		sigemptyset(&sigact.sa_mask);
+		sigact.sa_flags = SA_RESTART;
+		sigact.sa_handler = sig_handler;
+		if (sigaction(SIGCHLD, &sigact, NULL)
+			|| sigaction(SIGTERM, &sigact, NULL)
+			|| sigaction(SIGPIPE, &sigact, NULL)) {
+			print_error("Registering a signal handler failed");
+			rc = 4;
+			goto exit_on_error;
+		}
+
+		/* handle client terminal connection */
+		rc = iucvtty_worker(client, master, slave, client_host, &conf);
+	}
+
+	close(client);
+
+exit_on_error:
+	close(slave);
+	close(master);
+	closelog();
+
+	return rc;
+}
Index: s390-tools-sles11/iucvterm/test/Makefile
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/test/Makefile
@@ -0,0 +1,40 @@
+#! /usr/bin/make -f
+
+include ../../common.mak
+
+
+CPPFLAGS += -I../include
+CFLAGS   += -g
+
+
+PROGRAMS = test_afiucv
+TEST_PROGRAMS = test_functions test_allow
+TEST_PROGRAMS_NORUN = test_auditlog
+
+
+test_afiucv: test_afiucv.o
+test_functions: test_functions.o test_common.o ../src/functions.o
+test_allow: test_allow.o test_common.o ../src/functions.o
+test_auditlog: test_auditlog.o test_common.o ../src/auditlog.o ../src/functions.o
+
+
+all: $(PROGRAMS)
+check: $(TEST_PROGRAMS) $(TEST_PROGRAMS_NORUN)
+	@for prg in $(TEST_PROGRAMS); do \
+		failed=0 ;\
+		echo ; echo "=== RUN : $$prg ===" ;\
+		./$$prg || failed=$$? ;\
+		if test x$$failed = x0; then \
+			echo "=== PASS: $$prg ===" ;\
+		else \
+			echo "=== FAIL: $$prg (rc=$$failed) ===" ;\
+		fi ;\
+	done
+
+install:
+
+clean:
+	-rm -f *.o $(PROGRAMS) $(TEST_PROGRAMS) $(TEST_PROGRAMS_NORUN)
+
+
+.PHONY: install clean
Index: s390-tools-sles11/iucvterm/test/test.h
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/test/test.h
@@ -0,0 +1,24 @@
+/*
+ * test_common - Test program for the IUCV Terminal Applications
+ *
+ * Definition of common functions for test programs
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ */
+#ifndef __TEST_H_
+#define __TEST_H_
+
+
+#include <assert.h>
+#include <stdlib.h>
+#include "iucvterm/proto.h"
+
+
+#define __fail()	assert(0);
+
+
+extern int __socketpair(int sv[2]);
+extern int __msgcmp(const struct iucvtty_msg *, const struct iucvtty_msg *);
+
+#endif /* __TEST_H_ */
Index: s390-tools-sles11/iucvterm/test/test_afiucv.c
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/test/test_afiucv.c
@@ -0,0 +1,30 @@
+/*
+ * test_afiucv - Test program for the IUCV Terminal Applications
+ *
+ * Test program to check if the AF_IUCV family is supported by
+ * the running Linux kernel
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include "af_iucv.h"
+
+
+int main(void)
+{
+	int sk;
+
+	sk = socket(AF_IUCV, SOCK_STREAM, 0);
+	if (sk == -1) {
+		if (errno == EAFNOSUPPORT)
+			perror("AF_IUCV address family not supported");
+		return -1;
+	} else
+		close(sk);
+
+	return 0;
+}
Index: s390-tools-sles11/iucvterm/test/test_allow.c
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/test/test_allow.c
@@ -0,0 +1,77 @@
+/*
+ * test_allow - Test program for the IUCV Terminal Applications
+ *
+ * Test program to check common functions that use the regex api
+ *
+ * Copyright IBM Corp. 2008, 2009
+ * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include "test.h"
+
+#include "iucvterm/config.h"
+#include "iucvterm/functions.h"
+
+
+static int test_userid_cpy(char padded[9])
+{
+	char uid[8];
+	char id_str[9];
+	size_t len;
+
+	memcpy(uid, padded, 8);
+	userid_cpy(id_str, uid);
+
+	len = strlen(id_str);
+	assert(0 == memcmp(padded, id_str, len));
+
+	if (strchr(id_str, ' ') != NULL)
+		__fail();
+
+	return len;
+}
+
+int main(void)
+{
+	char re_ok[]    = "^T63[[:digit:]]{1,5}$";
+	char re_wrong[] = "^t63[[:alpha:]+$";
+	char re_short[] = "^lnx[[:digit:]]{3}$";
+	char id_short[9];
+	char user_id[8];
+
+	/* redirect stderr to /dev/null to avoid regex error output */
+	freopen("/dev/null", "w", stderr);
+
+	assert(-1 == is_regex_valid(re_wrong));
+	assert(-1 == is_regex_valid(NULL));
+	assert(0  == is_regex_valid(re_ok));
+	assert(0  == is_regex_valid(re_short));
+
+	/* simple */
+	assert(0 == strmatch("T6345050", "T6345050"));
+	/* using re */
+	assert(0 == strmatch("T6345050", re_ok));
+	assert(0 == strmatch("t6345050", re_ok)); /* icase */
+	assert(-1 == strmatch("T6345050", NULL));
+	assert(-1 == strmatch("T6345050", re_wrong));
+	assert(1 == strmatch("T634505A", re_ok));
+
+	/* test userid_cpy() function */
+	snprintf(id_short, 9, "%-8s", "");
+	assert(0 == test_userid_cpy(id_short));
+	snprintf(id_short, 9, "1");
+	assert(1 == test_userid_cpy(id_short));
+	snprintf(id_short, 9, "12345678");
+	assert(8 == test_userid_cpy(id_short));
+	snprintf(id_short, 9, "ABCD");
+	assert(4 == test_userid_cpy(id_short));
+
+	/* check for user IDs < 8 characters */
+	snprintf(id_short, 9, "%-8s", "LNX001");
+	memcpy(user_id, id_short, 8);
+	userid_cpy(id_short, user_id);
+	assert(0 == strmatch(id_short, re_short));
+
+	return 0;
+}
Index: s390-tools-sles11/iucvterm/test/test_auditlog.c
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/test/test_auditlog.c
@@ -0,0 +1,76 @@
+/*
+ * test_auditlog - Test program for the IUCV Terminal Applications
+ *
+ * Test program for the session logging functions used by iucvconn(1).
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/select.h>
+#include <unistd.h>
+#include "iucvterm/functions.h"
+#include "test.h"
+
+#define BUF_SIZE 1024
+
+
+static void do_cleanup(const char *filename)
+{
+	char tmp[64];
+
+	if (access(filename, W_OK) == 0)
+		unlink(filename);
+
+	snprintf(tmp, 64, "%s.timing", filename);
+	if (access(tmp, W_OK) == 0)
+		unlink(tmp);
+
+	snprintf(tmp, 64, "%s.info", filename);
+	if (access(tmp, W_OK) == 0)
+		unlink(tmp);
+}
+
+int main(int argc, char* argv[]){
+
+	char *filepath = NULL;
+	char *buf;
+	char  tmpfile[64];
+	int   interactive;
+	ssize_t rc;
+
+
+	if (argc < 2) {
+		fprintf(stderr, "Usage: %s <filepath>\n", argv[0]);
+		sprintf(tmpfile, "/tmp/test_auditlog.a1b2c3.%u", getpid());
+		fprintf(stderr, "now using file %s\n", tmpfile);
+		filepath = tmpfile;
+		/* cleanup stale file */
+		if (access(tmpfile, W_OK) == 0)
+			do_cleanup(tmpfile);
+		interactive = 0;
+	} else {
+		interactive = 1;
+		filepath = argv[1];
+	}
+	buf = malloc(BUF_SIZE);
+
+	rc = open_session_log(filepath);
+	assert(rc == 0);
+
+	write_session_info("The name of the log file is: %s\n", filepath);
+
+	sprintf(buf, "This is an entry\n");
+	rc = write_session_log(buf, strlen(buf));
+	assert(rc == 0);
+
+	close_session_log();
+
+	free(buf);
+	if (!interactive)
+		do_cleanup(tmpfile);
+	return 0;
+}
Index: s390-tools-sles11/iucvterm/test/test_common.c
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/test/test_common.c
@@ -0,0 +1,40 @@
+/*
+ * test_common library - Test program lib for the IUCV Terminal Applications
+ *
+ * Common functions for test programs
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ */
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "iucvterm/proto.h"
+
+
+int __socketpair(int sv[2])
+{
+	int rc;
+
+	rc = socketpair(AF_LOCAL, SOCK_STREAM, 0, sv);
+	if (rc)
+		perror("Could not create socketpair");
+
+	assert(rc == 0);
+	return rc;
+}
+
+int __msgcmp(const struct iucvtty_msg *m1, const struct iucvtty_msg *m2)
+{
+	if (m1->type != m2->type)
+		return 1;
+	if (m1->datalen != m2->datalen)
+		return 2;
+	if (0 != memcmp(m1->data, m2->data, m1->datalen))
+		return 3;
+	return 0;
+}
Index: s390-tools-sles11/iucvterm/test/test_functions.c
===================================================================
--- /dev/null
+++ s390-tools-sles11/iucvterm/test/test_functions.c
@@ -0,0 +1,243 @@
+/*
+ * test_functions - Test program for the IUCV Terminal Applications
+ *
+ * Test program for the common functions used by the
+ * IUCV Terminal Applications
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <termio.h>
+#include <unistd.h>
+#include "iucvterm/proto.h"
+#include "iucvterm/functions.h"
+#include "test.h"
+
+
+#define BUF_SIZE	256
+
+
+static int __testReadWriteMessage(int[2]);
+static int __testReadWriteMessage_chunks(int[2]);
+static int __testReadWriteMessage_nochunks(int[2]);
+static int __testTransmitData(int[2]);
+static int __testTermEnvVar(int[2]);
+static int __testWinsize(int[2]);
+static int __testCopyData(int[2]);
+
+
+int main(void)
+{
+	int sockv[2];
+
+	__socketpair(sockv);
+
+	assert(!__testReadWriteMessage(sockv));
+	assert(!__testReadWriteMessage_chunks(sockv));
+	assert(!__testReadWriteMessage_nochunks(sockv));
+	assert(!__testTermEnvVar(sockv));
+	assert(!__testWinsize(sockv));
+	assert(!__testCopyData(sockv));
+	assert(!__testTransmitData(sockv));
+
+	close(sockv[0]);
+	close(sockv[1]);
+
+	return 0;
+}
+
+
+static int __testReadWriteMessage_chunks(int sv[2])
+{
+	struct iucvtty_msg *msg[2];
+	char data[] = "Hallo\0test1\0test2\0test3\0test4\0test5\0test6\0test7\0test8";
+	size_t chunk;
+	int i;
+
+	/* setup msg to be sent */
+	msg[0] = msg_alloc(MSG_TYPE_DATA, BUF_SIZE);
+	msg[1] = msg_alloc(MSG_TYPE_DATA, BUF_SIZE);
+
+	/* send msg */
+	msg_cpy_from(msg[0], data, sizeof(data));
+	if (iucvtty_write_msg(sv[0], msg[0]))
+		return 1;
+
+	/* read msg */
+	chunk = 0;
+	for (i = 0; i < 9; i++) {
+		if (iucvtty_read_msg(sv[1], msg[1], 6 + MSG_DATA_OFFSET, &chunk))
+			return 2;
+		assert(msg[1]->datalen == 6);
+		assert(0 == memcmp(msg[1]->data, data + (i * 6), msg[1]->datalen));
+		/*
+		printf("chunk=%i datalen=%u data='%s'\n",
+			chunk, msg[1]->datalen, msg[1]->data);
+		*/
+	}
+	assert(chunk == 0);
+
+	msg_free(msg[0]);
+	msg_free(msg[1]);
+
+	return 0;
+}
+
+static int __testReadWriteMessage_nochunks(int sv[2])
+{
+	struct iucvtty_msg *msg[2];
+	char data[] = "Hallo\0test1\0test2\0test3\0test4\0test5\0test6\0test7\0test8";
+	int i;
+	size_t residual = 0;
+
+	/* setup msg to be sent */
+	msg[0] = msg_alloc(MSG_TYPE_DATA, BUF_SIZE);
+	msg[1] = msg_alloc(MSG_TYPE_DATA, BUF_SIZE);
+
+	/* send and read msg .. with chunks disabled.
+	 * The iucvtty_read_msg() shall ignore any data more than the given
+	 * length. */
+	i = 7;
+	while (i--) {
+		msg_cpy_from(msg[0], data, sizeof(data));
+		if (iucvtty_write_msg(sv[0], msg[0]))
+			return 1;
+
+		/* read msg */
+		if (iucvtty_read_msg(sv[1], msg[1], 6 + MSG_DATA_OFFSET, &residual))
+			return 2;
+		assert(0 == memcmp(msg[1]->data, data, msg[1]->datalen));
+		iucvtty_skip_msg_residual(sv[1], &residual);
+		assert(0 == residual);
+		/*printf("datalen=%u data='%s'\n",
+			msg[1]->datalen, msg[1]->data);*/
+	}
+
+	msg_free(msg[0]);
+	msg_free(msg[1]);
+
+	return 0;
+}
+
+static int __testReadWriteMessage(int sv[2])
+{
+	struct iucvtty_msg *msg[2];
+	char data[10] = "Hallo";
+	size_t chunk = 0;
+
+	/* setup msg to be sent */
+	msg[0] = msg_alloc(MSG_TYPE_DATA, BUF_SIZE);
+	msg[1] = msg_alloc(MSG_TYPE_DATA, BUF_SIZE);
+
+	/* send msg */
+	msg_cpy_from(msg[0], data, sizeof(data));
+	if (iucvtty_write_msg(sv[0], msg[0]))
+		return 1;
+
+	/* read msg */
+	if (iucvtty_read_msg(sv[1], msg[1], BUF_SIZE, &chunk))
+		return 2;
+	iucvtty_skip_msg_residual(sv[1], &chunk);
+
+	/* compare msg */
+	if (__msgcmp(msg[0], msg[1]))
+		return 3;
+
+	msg_free(msg[0]);
+	msg_free(msg[1]);
+
+	return 0;
+}
+
+static int __testTransmitData(int sv[2])
+{
+	struct iucvtty_msg *msg;
+	char data[] = "Vah __testTransmitData hai.";
+	size_t chunk = 0;
+
+
+	/* I. write something to sv[0]
+	 * II. tx_data: read from sv[1], write to sv[0]
+	 * III. read iucvtty_msg from sv[1]
+	 */
+	assert(sizeof(data) == write(sv[0], data, sizeof(data)));
+	msg = msg_alloc(MSG_TYPE_DATA, BUF_SIZE);
+	assert(0 == iucvtty_tx_data(sv[0], sv[1], msg, BUF_SIZE));
+	assert(0 == iucvtty_read_msg(sv[1], msg, BUF_SIZE, &chunk));
+	assert(MSG_TYPE_DATA == msg->type);
+	assert(sizeof(data) == msg->datalen);
+	assert(0 == memcmp(&data, msg->data, msg->datalen));
+
+	msg_free(msg);
+	return 0;
+}
+
+static int __testTermEnvVar(int sv[2])
+{
+#define __BUF_SIZE 81
+	char term[__BUF_SIZE] = "my_default_term";
+	char buf[__BUF_SIZE];
+	char *orig_term = getenv("TERM");
+
+	/* I. */
+	if (orig_term != NULL) {
+		assert(0 == iucvtty_tx_termenv(sv[0], NULL));
+		assert(0 == iucvtty_rx_termenv(sv[1], buf, __BUF_SIZE));
+		assert(0 == strncmp(orig_term, buf,
+				    min(__BUF_SIZE, strlen(orig_term))));
+	}
+
+	unsetenv("TERM");	/* clear term */
+
+	/* II. */
+	assert(0 == iucvtty_tx_termenv(sv[0], term));
+	assert(0 == iucvtty_rx_termenv(sv[1], buf, __BUF_SIZE));
+	assert(0 == strncmp(buf, term, __BUF_SIZE));
+
+	/* III. */
+	assert(0 == iucvtty_tx_termenv(sv[0], NULL));
+	assert(0 == iucvtty_rx_termenv(sv[1], buf, __BUF_SIZE));
+	assert(0 == strncmp("", buf, 1));
+
+	setenv("TERM", orig_term, 1);	/* restore original term */
+	return 0;
+#undef __BUF_SIZE
+}
+
+static int __testWinsize(int sv[2])
+{
+	struct iucvtty_msg *msg;
+	struct winsize winsz;
+	size_t chunk;
+
+	msg = msg_alloc(MSG_TYPE_DATA, BUF_SIZE);
+	assert(0 == ioctl(STDIN_FILENO, TIOCGWINSZ, &winsz));
+	assert(0 == iucvtty_tx_winsize(sv[0], STDIN_FILENO));
+	assert(0 == iucvtty_read_msg(sv[1], msg, BUF_SIZE, &chunk));
+	assert(MSG_TYPE_WINSIZE == msg->type);
+	assert(sizeof(struct winsize) == msg->datalen);
+	assert(0 == memcmp(&winsz, msg->data, msg->datalen));
+
+	msg_free(msg);
+	return 0;
+}
+
+static int __testCopyData(int sv[2])
+{
+	struct iucvtty_msg *msg;
+	char data[] = "palim palim";
+	char buf[BUF_SIZE];
+
+	msg = msg_alloc(MSG_TYPE_DATA, BUF_SIZE);
+	msg_cpy_from(msg, data, sizeof(data));
+	assert(0 == iucvtty_copy_data(sv[0], msg));
+	assert(sizeof(data) == read(sv[1], &buf, BUF_SIZE));
+	assert(0 == memcmp(data, buf, sizeof(data)));
+
+	msg_free(msg);
+	return 0;
+}
Index: s390-tools-sles11/libvtoc/vtoc.c
===================================================================
--- s390-tools-sles11.orig/libvtoc/vtoc.c
+++ s390-tools-sles11/libvtoc/vtoc.c
@@ -218,19 +218,37 @@ void vtoc_set_extent (extent_t *ext, u_i
 /*
  *
  */
-void vtoc_set_cchh (cchh_t *addr, u_int16_t cc, u_int16_t hh) 
+void vtoc_set_cchh (cchh_t *addr, u_int32_t cc, u_int16_t hh)
 {
-        addr->cc = cc;
-	addr->hh = hh;
+	addr->cc = (u_int16_t) cc;
+	addr->hh = cc >> 16;
+	addr->hh <<= 4;
+	addr->hh |= hh;
 }
 
+u_int32_t vtoc_get_cyl_from_cchh(cchh_t *addr)
+{
+        u_int32_t cyl;
+
+        /*decode cylinder for large volumes */
+	cyl = addr->hh & 0xFFF0;
+	cyl <<= 12;
+	cyl |= addr->cc;
+	return cyl;
+}
+
+u_int16_t vtoc_get_head_from_cchh(cchh_t *addr)
+{
+	/* decode heads for large volumes */
+	return addr->hh & 0x000F;
+}
 
 /*
  *
  */
-static void vtoc_set_ttr (ttr_t *addr, u_int16_t tt, u_int8_t r) 
+static void vtoc_set_ttr (ttr_t *addr, u_int16_t tt, u_int8_t r)
 {
-        addr->tt = tt;
+	addr->tt = tt;
 	addr->r  = r;
 }
 
@@ -238,13 +256,58 @@ static void vtoc_set_ttr (ttr_t *addr, u
 /*
  *
  */
-void vtoc_set_cchhb (cchhb_t *addr, u_int16_t cc, u_int16_t hh, u_int8_t b) 
+void vtoc_set_cchhb (cchhb_t *addr, u_int32_t cc, u_int16_t hh, u_int8_t b)
+{
+	addr->cc = (u_int16_t) cc;
+	addr->hh = cc >> 16;
+	addr->hh <<= 4;
+	addr->hh |= hh;
+	addr->b  = b;
+}
+
+u_int32_t vtoc_get_cyl_from_cchhb(cchhb_t *addr)
 {
-        addr->cc = cc;
-        addr->hh = hh;
-        addr->b  = b;
+        u_int32_t cyl;
+
+        /* decode cylinder for large volumes */
+        cyl = addr->hh & 0xFFF0;
+        cyl <<= 12;
+        cyl |= addr->cc;
+	return cyl;
 }
 
+u_int16_t vtoc_get_head_from_cchhb(cchhb_t *addr)
+{
+	/* decode heads for large volumes */
+	return addr->hh & 0x000F;
+}
+
+/*
+ * some functions to convert cyl-cyl-head-head addresses to
+ * block or track numbers
+ * Note: Record zero is special, so first block on a track is
+ * in record 1!
+ */
+u_int64_t cchhb2blk(cchhb_t *p, struct hd_geometry *geo)
+{
+        return (u_int64_t) vtoc_get_cyl_from_cchhb(p) *
+		geo->heads * geo->sectors +
+		vtoc_get_head_from_cchhb(p) * geo->sectors +
+		p->b;
+}
+
+u_int64_t cchh2blk (cchh_t *p, struct hd_geometry *geo) {
+
+        return (u_int64_t) vtoc_get_cyl_from_cchh(p) *
+		geo->heads * geo->sectors +
+                vtoc_get_head_from_cchh(p) * geo->sectors;
+}
+
+u_int32_t cchh2trk (cchh_t *p, struct hd_geometry *geo) {
+
+	return  vtoc_get_cyl_from_cchh(p) * geo->heads +
+		vtoc_get_head_from_cchh(p);
+}
 
 /*
  *
@@ -481,7 +544,8 @@ vtoc_write_label (char *device,
 		  format1_label_t *f1, 
 		  format4_label_t *f4,
 		  format5_label_t *f5,
-		  format7_label_t *f7)
+		  format7_label_t *f7,
+		  format9_label_t *f9)
 {
 	int f,t;
 
@@ -542,6 +606,17 @@ vtoc_write_label (char *device,
 		}
 	}
 
+	if (f9 != NULL)
+	{
+		t = sizeof(format9_label_t);
+	        if (write(f, f9, t) != t)
+		{
+		        close(f);
+			vtoc_error(unable_to_write, device,
+				   "Could not write VTOC FMT9 DSCB.");
+		}
+	}
+
         close(f);
 }
 
@@ -553,7 +628,8 @@ void 
 vtoc_init_format4_label (
 	format4_label_t    *f4,
 	unsigned int usable_partitions,
-	unsigned int cylinders,
+	unsigned int compat_cylinders,
+	unsigned int real_cylinders,
 	unsigned int tracks,
 	unsigned int blocks,
 	unsigned int blksize,
@@ -578,7 +654,7 @@ vtoc_init_format4_label (
         f4->DS4DEVAC = 0x00;
 
         /* -- begin f4->DS4DEVCT -- */
-        f4->DS4DEVCT.DS4DSCYL = cylinders;
+        f4->DS4DEVCT.DS4DSCYL = compat_cylinders;
         f4->DS4DEVCT.DS4DSTRK = tracks;
 
 	switch (dev_type)
@@ -619,7 +695,11 @@ vtoc_init_format4_label (
 	bzero(f4->res2, sizeof(f4->res2));
 	f4->DS4EFLVL = 0x00;
 	bzero(&f4->DS4EFPTR, sizeof(f4->DS4EFPTR));
-	bzero(f4->res3, sizeof(f4->res3));
+	bzero(&f4->res3, sizeof(f4->res3));
+	f4->DS4DCYL = real_cylinders;
+	bzero(f4->res4, sizeof(f4->res4));
+	f4->DS4DEVF2 = 0x40; /* allow format 8 and 9 labels */
+	bzero(&f4->res5, sizeof(f4->res5));
 }
 
 
@@ -652,16 +732,16 @@ vtoc_init_format7_label (format7_label_t
 
 
 /*
- * initializes a format1 label
+ * helper function that initializes a most parts of a
+ * format1 or format 8 label, all but the field DS1FMTID
  */
-void 
-vtoc_init_format1_label (
+static void vtoc_init_format_1_8_label (
 	char *volid,
         unsigned int blksize,
 	extent_t *part_extent,
-	format1_label_t *f1) 
+	format1_label_t *f1)
 {
-        struct tm * creatime;
+	struct tm * creatime;
 	time_t t;
 	char str[80];
 
@@ -673,53 +753,87 @@ vtoc_init_format1_label (
 	sprintf(str, "PART    .NEW                                ");
 	vtoc_ebcdic_enc(str, str, 44);
 	strncpy(f1->DS1DSNAM, str, 44);
-	f1->DS1FMTID = 0xf1;
 	strncpy((char *) f1->DS1DSSN, "      ", 6);
 	f1->DS1VOLSQ = 0x0001;
 
 	vtoc_set_date(&f1->DS1CREDT,
-                      (u_int8_t) creatime->tm_year,
+		      (u_int8_t) creatime->tm_year,
 		      (u_int16_t) creatime->tm_yday);
 	/* expires never - 99 365 */
-        vtoc_set_date(&f1->DS1EXPDT,
+	vtoc_set_date(&f1->DS1EXPDT,
 	              0x63,
-                      0x016D);
+		      0x016D);
 	f1->DS1NOEPV = 0x01;
 	f1->DS1NOBDB = 0x00;
-        f1->DS1FLAG1 = 0x00;
+	f1->DS1FLAG1 = 0x00;
 	vtoc_ebcdic_enc("IBM LINUX    ", str, 13);
 	strncpy((char *)f1->DS1SYSCD, str, 13);
-        vtoc_set_date(&f1->DS1REFD,
+	vtoc_set_date(&f1->DS1REFD,
 		      (u_int8_t) creatime->tm_year,
-                      (u_int16_t) creatime->tm_yday);
-        f1->DS1SMSFG = 0x00;
-        f1->DS1SCXTF = 0x00;
-        f1->DS1SCXTV = 0x0000;
-        f1->DS1DSRG1 = 0x00;
-        f1->DS1DSRG2 = 0x00;
-        f1->DS1RECFM = 0x88;
-        f1->DS1OPTCD = 0x00;
-        f1->DS1BLKL  = blksize;
-        f1->DS1LRECL = blksize;
-        f1->DS1KEYL  = 0x00;
-        f1->DS1RKP   = 0x0000;
-        f1->DS1DSIND = 0x80; /* last volume for this dataset */
-        f1->DS1SCAL1 = 0x80;
-        bzero(&f1->DS1SCAL3, sizeof(f1->DS1SCAL3));
+		      (u_int16_t) creatime->tm_yday);
+	f1->DS1SMSFG = 0x00;
+	f1->DS1SCXTF = 0x00;
+	f1->DS1SCXTV = 0x0000;
+	f1->DS1DSRG1 = 0x00;
+	f1->DS1DSRG2 = 0x00;
+	f1->DS1RECFM = 0x88;
+	f1->DS1OPTCD = 0x00;
+	f1->DS1BLKL  = blksize;
+	f1->DS1LRECL = blksize;
+	f1->DS1KEYL  = 0x00;
+	f1->DS1RKP   = 0x0000;
+	f1->DS1DSIND = 0x80; /* last volume for this dataset */
+	f1->DS1SCAL1 = 0x80;
+	bzero(&f1->DS1SCAL3, sizeof(f1->DS1SCAL3));
 	vtoc_set_ttr(&f1->DS1LSTAR,
-	             0x0000,
-	             0x00);
+		     0x0000,
+		     0x00);
 	f1->DS1TRBAL = 0x00;
 	bzero(&f1->res1, sizeof(f1->res1));
-        memcpy(&f1->DS1EXT1,
-               part_extent,
-               sizeof(extent_t));
-        bzero(&f1->DS1EXT2, sizeof(extent_t));
-        bzero(&f1->DS1EXT3, sizeof(extent_t));
-        vtoc_set_cchhb(&f1->DS1PTRDS,
-                       0x0000, 0x0000, 0x00);
+	memcpy(&f1->DS1EXT1,
+	       part_extent,
+	       sizeof(extent_t));
+	bzero(&f1->DS1EXT2, sizeof(extent_t));
+	bzero(&f1->DS1EXT3, sizeof(extent_t));
+	vtoc_set_cchhb(&f1->DS1PTRDS,
+		       0x0000, 0x0000, 0x00);
+}
+
+void vtoc_init_format1_label (
+	char *volid,
+        unsigned int blksize,
+	extent_t *part_extent,
+	format1_label_t *f1)
+{
+	vtoc_init_format_1_8_label(volid, blksize, part_extent, f1);
+	f1->DS1FMTID = 0xf1;
 }
 
+void vtoc_init_format8_label (
+	char *volid,
+        unsigned int blksize,
+	extent_t *part_extent,
+	format1_label_t *f8)
+{
+	vtoc_init_format_1_8_label(volid, blksize, part_extent, f8);
+	f8->DS1FMTID = 0xf8;
+}
+
+void vtoc_update_format8_label (
+	cchhb_t *associated_f9,
+	format1_label_t *f8)
+{
+	memcpy(&f8->DS1PTRDS, associated_f9, sizeof(*associated_f9));
+}
+
+void vtoc_init_format9_label (
+	format9_label_t *f9)
+{
+	f9->DS9KEYID = 0x09;
+	f9->DS9SUBTY = 0x01;
+	f9->DS9NUMF9 = 1;
+	f9->DS9FMTID = 0xf9;
+}
 
 /*
  * do some updates to the VTOC format4 label
@@ -1193,7 +1307,7 @@ void vtoc_update_format7_label_del (form
  */
 void vtoc_set_freespace(format4_label_t *f4, format5_label_t *f5,
 		   format7_label_t *f7, char ch, int verbose,
-		   u_int32_t start, u_int32_t stop, int cyl, int trk)
+		   u_int32_t start, u_int32_t stop, u_int32_t cyl, u_int32_t trk)
 {
 	if ((cyl * trk) > BIG_DISK_SIZE) {
 		if (ch == '+') {
@@ -1211,8 +1325,7 @@ void vtoc_set_freespace(format4_label_t 
 		f4->DS4VTOCI = 0xa0;
 		f4->DS4EFLVL = 0x07;
 		vtoc_set_cchhb(&f4->DS4EFPTR, 0x0000, 0x0001, 0x03);
-	}
-	else {
+	} else {
 		u_int16_t x,y;
 		u_int8_t  z;
 
Index: s390-tools-sles11/man/Makefile
===================================================================
--- s390-tools-sles11.orig/man/Makefile
+++ s390-tools-sles11/man/Makefile
@@ -1,13 +1,20 @@
 include ../common.mak
 
+MANS = dumpconf.8 prandom.4 af_iucv.7
+
 all:
 
 clean:
 
-install:
-	$(INSTALL) -d -m 755 $(MANDIR)/man8
-	$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 dumpconf.8 $(MANDIR)/man8
-	$(INSTALL) -d -m 755 $(MANDIR)/man4
-	$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 prandom.4 $(MANDIR)/man4
+install: $(MANS)
+	for man in $(MANS); do \
+	  msection=`echo $$man |sed 's/.*\.\([1-8]\)$$/man\1/'` ; \
+	  $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 -D $$man $(MANDIR)/$$msection/$$man ; \
+	done
+
+pdf: $(MANS)
+	for man in $(MANS); do \
+	  man -t ./$$man |ps2pdf -sPAPERSIZE=a4 - $${man}.pdf ; \
+	done
 
-.PHONY: all install clean
+.PHONY: all install clean pdf
Index: s390-tools-sles11/man/af_iucv.7
===================================================================
--- /dev/null
+++ s390-tools-sles11/man/af_iucv.7
@@ -0,0 +1,402 @@
+.\" af_iucv.7
+.\"
+.\"
+.\" Copyright IBM Corp. 2008, 2009.
+.\" Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+.\" ----------------------------------------------------------------------
+.TH AF_IUCV 7 "September 2009"  "s390-tools" "Linux Programmer's Manual"
+.SH NAME
+iucv, AF_IUCV \- Sockets for z/VM IUCV communication
+.
+.
+.
+.SH SYNOPSIS
+.B #include <sys/socket.h>
+
+.IB iucv_stream_socket " = socket(AF_IUCV, SOCK_STREAM, 0);"
+.br
+.IB iucv_packet_socket " = socket(AF_IUCV, SOCK_SEQPACKET, 0);"
+.
+.
+.
+.SH DESCRIPTION
+The Inter-User Communication Vehicle (IUCV) is a z/VM communication facility
+that enables a program running in one z/VM guest virtual machine to communicate
+with another z/VM guest virtual machine, or with the control program (CP), or
+even with itself.
+
+The AF_IUCV address family provides communication and addressing in the IUCV
+domain.  In the IUCV domain, address spaces or virtual machines can use the
+socket interface to communicate with other virtual machines or address spaces
+within the same z/VM guest operating system.
+
+AF_IUCV connects socket applications running on different Linux guest operating
+systems, or it connects a Linux application to another socket application
+running in another z/VM guest operating system (like z/VM CMS).
+
+The AF_IUCV address family supports stream-oriented sockets
+(\f(CWSOCK_STREAM\fP) and connection-oriented datagram sockets
+(\f(CWSOCK_SEQPACKET\fP). Stream-oriented sockets fragment data over several
+native IUCV messages, whereas sockets of type SOCK_SEQPACKET map a particular
+socket write or read operation to a single native IUCV message.
+
+.SS Features
+The AF_IUCV address family provides:
+.PP
+.IP "\(bu" 2
+Multiple outgoing socket connections from a Linux guest operating system.
+.IP "\(bu" 2
+Multiple incoming socket connections to a Linux guest operating system.
+.IP "\(bu" 2
+Socket communication with applications utilizing CMS AF_IUCV support.
+.
+.
+.
+.
+.SH "ADDRESS FORMAT"
+.ft CW
+.in +0.25i
+.nf
+#define AF_IUCV    32
+
+struct sockaddr_iucv {
+    sa_family_t    siucv_family;     /* AF_IUCV */
+    unsigned short siucv_port;       /* reserved */
+    unsigned int   siucv_addr;       /* reserved */
+    char           siucv_nodeid[8];  /* reserved */
+    char           siucv_userid[8];  /* guest user id */
+    char           siucv_name[8];    /* application name */
+};
+
+.fi
+.in -0.25i
+.ft R
+
+.TP
+.B siucv_family
+is set to
+.BR AF_IUCV
+(= 32)
+.
+.TP
+.B siucv_port, siucv_addr, siucv_nodeid
+are reserved for future use. The
+.B siucv_port
+and
+.B siucv_addr
+fields must be zero. The
+.B siucv_nodeid
+field must be set to exactly eight blank characters.
+.
+.TP
+.B siucv_userid
+is set to the z/VM user ID of the Linux guest virtual machine running the
+application that owns the address. The field must be eight characters long,
+padded with blanks on the right.
+
+For
+.BR bind "(2), " siucv_userid
+must contain blanks only to allow AF_IUCV to set the z/VM user ID of the Linux
+guest operating system.
+.
+.TP
+.B siucv_name
+is set to the application name by which the socket is known. Servers advertise
+application names and clients use these application names to connect to servers.
+This field must be eight characters long, padded with blanks on the right.
+.
+.
+.
+.SH "SOCKET OPTIONS"
+Socket options can be set with
+.BR setsockopt (2)
+and read with
+.BR getsockopt (2)
+by specifying \f(CWSOL_IUCV\fP as the socket level.
+
+.TP
+.B SO_IPRMDATA_MSG
+Enables the application to send up to seven bytes of socket data in the
+parameter list of an IUCV message. Use this option to increase performance
+when transferring small amounts of data.
+
+To send data in the parameter list, specify a non-zero integer value.
+
+.RS
+.TP
+.B Note:
+Use this option with care, older AF_IUCV versions do not support receiving
+socket data in the parameter list and shut down the socket on which
+a parameter list message has been received.
+.RE
+.
+.TP
+.B SO_MSGLIMIT
+Modifies the message limit for new IUCV communication paths. The message
+limit specifies the maximum number of outstanding messages that are allowed
+for established connections. This setting can be lowered by z/VM when an IUCV
+connection is established.
+
+The message limit is an integer value in range 1 to 65535.
+The default value is 65535.
+
+The message limit must be set before
+.BR connect "(2) or " listen (2)
+is called for sockets.
+.br
+For sockets that are already connected or listening for connections,
+the message limit cannot be changed.
+.br
+New sockets created by
+.BR accept (2)
+inherit the message limit that has been set for the listening socket.
+
+.BR getsockopt (2)
+returns the default message limit or the limit that has been set.
+For connected sockets, the current message limit is returned. The current
+message limit is assigned by z/VM for each connection and it depends
+on the IUCV MSGLIMIT statement specified for the z/VM guest virtual machine.
+The current message limit is the lower value of the socket option and the
+value specified for the IUCV MSGLIMIT statement.
+
+See the SECURITY section below for setting IUCV MSGLIMIT authorizations.
+.
+.
+.
+.SH "ANCILLARY DATA"
+Ancillary data is sent and received using the
+.BR sendmsg (2)
+and
+.BR recvmsg (2)\fR.\fP
+To send ancillary data, set the \fBcmsg_level\fP field of struct \fBcmsghdr\fP
+to \f(CWSOL_IUCV\fP and the \fBcmsg_type\fP field to the type.
+.br
+For more information see
+.BR cmsg (3).
+
+.TP
+.B SCM_IUCV_TRGCLS
+Send or receive IUCV target class information. The IUCV target class can be used
+to classify and identify an IUCV message at IUCV protocol level. If the target
+class is not specified as ancillary data, it is set to zero.
+
+The target class is a number of type \fBuint32_t\fP.
+.
+.
+.
+.
+.SH SECURITY
+This section provides an overview of the required IUCV statements for your z/VM
+guest virtual machine. For details and for general IUCV setup information for
+z/VM guests virtual machines see
+.I z/VM CP Programming Services
+and
+.I z/VM CP Planning and Administration\fR.\fP
+.
+.
+.SS "Granting IUCV authorizations"
+Use the
+.B IUCV
+directory control statement to grant the necessary authorizations.
+.
+.TP
+.B IUCV ALLOW
+allows any other z/VM virtual machine to establish a communication path with
+this z/VM virtual machine. With this statement, no further authorization is
+required in the z/VM virtual machine that initiates the communication.
+.
+.TP
+.B IUCV ANY
+allows this z/VM guest virtual machine to establish a communication path with
+any other z/VM guest virtual machine.
+.
+.TP
+.B IUCV \fIuser_ID\fP
+allows this z/VM guest virtual machine to establish a communication path to the
+z/VM guest virtual machine with the z/VM user ID \fIuser_ID\fP.
+.PP
+You can specify multiple IUCV statements. To any of these IUCV statements you
+can append the
+.B MSGLIMIT \fIlimit\fP
+parameter.
+\fIlimit\fP specifies the maximum number of outstanding messages that are
+allowed for each connection authorized by this statement.
+If no value is specified for \fBMSGLIMIT\fP, AF_IUCV requests 65535,
+which is the maximum supported by IUCV.
+.
+.
+.SS "Setting a connection limit"
+Use the \fBOPTION\fP statement to limit the number of concurrent connections.
+.TP
+.B OPTION MAXCONN \fImaxno\fP
+\fImaxno\fP specifies the maximum number of IUCV connections allowed for this
+virtual machine. The default is 64. The maximum is 65535.
+.
+.
+.SS "Example"
+These sample statements allow any z/VM guest virtual machine to connect to your
+z/VM guest virtual machine with a maximum of 10\^000 outstanding messages for each
+incoming connection. Your z/VM guest virtual machine is permitted to connect to
+all other z/VM guest virtual machines. The total number of connections for your
+z/VM guest virtual machine cannot exceed 100.
+.ft CW
+.in +0.25i
+.nf
+
+IUCV ALLOW MSGLIMIT 10000
+IUCV ANY
+OPTION MAXCONN 100
+
+.fi
+.in -0.25i
+.ft
+.
+.
+.
+.
+.SH ERRORS
+Several socket operations return error conditions that have a special meaning in
+the context of AF_IUCV. Those error conditions, and the respective descriptions
+are listed below.
+
+See the manual page of the respective socket operation for a complete list
+of errors.
+
+.TP
+.B ECONNREFUSED
+.BR connect (2)
+called but the target z/VM guest virtual machine is not listening on the
+application name.
+.
+.TP
+.B ENETUNREACH
+.BR connect (2)
+called but the target z/VM guest virtual machine is not logged on.
+Ensure that the z/VM guest virtual machine to which your application wants to
+connect is logged on.
+.
+.TP
+.B EAGAIN
+.BR connect (2)
+called but the maximum number of connections is exceeded for the calling or for
+the target z/VM guest virtual machine.
+This error can be temporary and the application may try again after some time.
+If the error occurs repeatedly, try to increase the maximum number of
+connections (for one or both z/VM guest virtual machines).
+See the SECURITY section about the required authorization statement.
+
+.B sendmsg (2)
+called but the maximum number of outstanding IUCV messages for the socket
+connection is reached; i.e. there are data available that has not yet been
+received by the communication partner.
+.br
+If necessary, change the IUCV message limit as explained in section
+"IUCV AUTHORIZATIONS".
+.
+.TP
+.B EACCES
+.BR connect (2)
+called but the calling z/VM guest virtual machine is missing IUCV authorization.
+See the SECURITY section about required IUCV authorizations.
+.
+.TP
+.B EINVAL
+.BR connect (2)
+or
+.BR bind (2)
+called but the AF_IUCV setting in the
+.I siucv_family
+field of the passed address is missing.
+
+.BR listen (2)
+called but the AF_IUCV socket has not yet been bound to an address.
+Always call
+.BR bind (2)
+before
+.BR listen (2).
+
+.BR setsockopt (2)
+called with option \fBSO_MSGLIMIT\fP for sockets that are already connected.
+.
+.TP
+.B ENOPROTOOPT
+.BR setsockopt (2),
+or
+.BR getsockopt (2)
+called but the socket level has not been set to \f(CWSOL_IUCV\fP, or the
+specified socket option is not supported.
+.
+.TP
+.B EOPNOTSUPP
+.BR sendmsg (2)
+or
+.BR recvmsg (2)
+might be called with the
+.I MSG_OOB
+flag set.
+AF_IUCV does not support sending or receiving \fIout-of-band\fP data on its
+sockets.
+
+For \f(CWSOCK_SEQPACKET\fP sockets,
+.BR sendmsg (2)
+called without the
+.I MSG_EOR
+flag set.
+AF_IUCV does not support segmentation, and thus, the "end-of-record"
+(\fIMSG_EOR\fP) flag must always be set.
+.
+.TP
+.B EPROTONOSUPPORT
+.BR socket (2)
+called with a protocol that is not supported. The socket protocol parameter
+must be either zero or \f(CWPF_IUCV\fP.
+.
+.TP
+.B EAFNOSUPPORT
+.BR socket (2)
+called with \f(CWAF_IUCV\fP but the AF_IUCV address family is not
+supported by the current Linux kernel.  Ensure that your Linux kernel has been
+compiled with support for the AF_IUCV address family.
+.
+.PP
+Other errors can be generated by the generic socket layer. See the respective
+manual pages for more information.
+.
+.
+.
+.SH "SEE ALSO"
+.BR connect (2),
+.BR recvmsg (2),
+.BR sendmsg (2),
+.BR socket (2),
+.BR setsockopt (2),
+.BR getsockopt (2),
+.BR cmsg (3),
+.BR socket (7)
+
+.I "Linux on System z - Device Drivers, Features, and Commands"
+.br
+.I "z/VM CP Planning and Administration"
+.br
+.I "z/VM CP Programming Services"
+.
+.
+.
+.SH "HISTORY"
+.TP
+.B AF_IUCV, version 1.0
+Initial version.
+.TP
+.B AF_IUCV, version 1.1
+.RS 4
+.IP "\(bu" 4
+Support for sending socket data in the parameter list of an IUCV message
+(\f(CWSO_IPRMDATA_MSG\fP).
+.IP "\(bu" 4
+Access the target class of an IUCV message as ancillary data using
+.BR sendmsg "(2) and " recvmsg (2).
+.IP "\(bu" 4
+Support for \f(CWSOCK_SEQPACKET\fP sockets to facilitate development of native
+IUCV applications that interact with AF_IUCV.
+.RE
+
Index: s390-tools-sles11/man/dumpconf.8
===================================================================
--- s390-tools-sles11.orig/man/dumpconf.8
+++ s390-tools-sles11/man/dumpconf.8
@@ -17,13 +17,18 @@ The following keywords can be used in th
 
 .TP
 \fB  - ON_PANIC:\fR
-Shutdown action in case of a kernel panic. Possible values are 'dump', 'reipl', 'stop' and 'vmcmd':
+Shutdown action in case of a kernel panic. Possible values are 'dump', 'reipl', 'dump_reipl', 'stop' and 'vmcmd':
 .br
 
 dump: trigger dump according to configuration in /etc/sysconfig/dumpconf.
 .br
 
-reipl: trigger reipl according to configuration under /sys/firmware/reipl.
+reipl: trigger re-IPL according to configuration under /sys/firmware/reipl.
+.br
+
+dump_reipl: first trigger dump according to configuration in
+/etc/sysconfig/dumpconf, then trigger re-IPL according to configuration under
+/sys/firmware/reipl.
 .br
 
 stop: stop Linux and enter disabled wait (default).
@@ -59,6 +64,47 @@ Boot record logical block address.
 \fB  - VMCMD_1, VMCMD_2 ... VMCMD_5:\fR
 Up to five CP commands, which are triggered in case of a kernel panic.
 
+.SH Reboot loop considerations
+
+If you select the shutdown actions "reipl" or "dump_reipl", in rare cases a
+"reboot loop" can occur, if the Linux kernel crashes everytime after the
+reboot. If you want to prevent that scenario, one of the following two
+approaches can be taken:
+
+1. Manual activation of dumpconf
+
+Ensure that the dumpconf service is not active by default:
+.br
+# chkconfig --del dumpconf
+
+Start dumpconf service manually:
+.br
+# service dumpconf start
+
+When your Linux system crashes, the system will be rebooted (after creating
+a dump in case of dump_reipl). Because the dumpconf script will then not be
+activated automatically, a second crash will stop the system.
+
+2. Automatic delayed activation of dumpconf
+
+Ensure that the dumpconf service is not active by default:
+.br
+# chkconfig --del dumpconf
+
+To enable delayed activation one of the following methods can be used:
+  a) Use a init script (e.g. /etc/rc.d/boot.local):
+     (sleep 10m; /sbin/service dumpconf start) &
+
+  b) Use a init script (e.g. /etc/rc.d/boot.local) together with "at":
+     echo 'echo /sbin/service dumpconf start' |at now+10min
+
+  c) Use the following crontab entry:
+     @reboot sleep 10m && /sbin/service dumpconf start
+
+In these examples, when your Linux system crashes within 10 minutes after
+the reboot, the dumpconf script is not active and a second crash will stop
+the system.
+
 .SH COMMANDS
 .TP
 \fBstart\fR
@@ -91,7 +137,7 @@ The following are examples for /etc/sysc
 .br
 #
 .br
-ON_PANIC=dump
+ON_PANIC=dump_reipl
 .br
 DUMP_TYPE=ccw
 .br
@@ -135,11 +181,11 @@ VMCMD_3="IPL 4711"
 
 #
 .br
-# Example config for reipl on panic
+# Example config for re-IPL on panic
 .br
 #
 .br
 ON_PANIC=reipl
 
 .SH SEE ALSO
-Linux on zSeries: Device Drivers, Features and Commands
+Linux on zSeries: Using the Dump Tools
Index: s390-tools-sles11/vmconvert/Makefile
===================================================================
--- s390-tools-sles11.orig/vmconvert/Makefile
+++ s390-tools-sles11/vmconvert/Makefile
@@ -1,8 +1,8 @@
 # Common definitions
 include ../common.mak
 
-CXXFLAGS      = $(CFLAGS) -D_FILE_OFFSET_BITS=64 -I../include
-OBJS          = main.o lkcd_dump.o vm_dump.o register_content.o dump.o
+CXXFLAGS      += -D_FILE_OFFSET_BITS=64 -I../include
+OBJS          = main.o lkcd_dump.o vm_dump.o register_content.o dump.o convert.o
 
 all: vmconvert
 
@@ -10,6 +10,7 @@ lkcd_dump.o: lkcd_dump.cpp lkcd_dump.h
 vm_dump.o: vm_dump.cpp vm_dump.h
 dump.o: dump.cpp dump.h
 register_content.o: register_content.cpp register_content.h
+convert.o: convert.cpp vm_dump.h lkcd_dump.h
 
 vmconvert: $(OBJS)
 	$(CXX) $(CXXFLAGS) -o vmconvert $(OBJS) -lz
Index: s390-tools-sles11/vmconvert/convert.cpp
===================================================================
--- /dev/null
+++ s390-tools-sles11/vmconvert/convert.cpp
@@ -0,0 +1,71 @@
+/*
+ * convert.cpp
+ *  dump convert program required by both vmconvert and vmur
+ *
+ *  Copyright IBM Corp. 2004, 2008.
+ *
+ *  Author(s): Michael Holzheu
+ */
+
+#include "vm_dump.h"
+#include "lkcd_dump.h"
+
+int
+vm_convert(const char* inputFileName, const char* outputFileName,
+	   const char* progName)
+{
+/* Do the conversion */
+	try {
+		switch(VMDump::getDumpType(inputFileName)){
+			case Dump::DT_VM64_BIG:
+			{
+				VMDump64Big* vmdump;
+				LKCDDump64* lkcddump;
+
+				vmdump = new VMDump64Big(inputFileName);
+				vmdump->printInfo();
+				lkcddump = new LKCDDump64(vmdump,
+						vmdump->getRegisterContent());
+				lkcddump->writeDump(outputFileName);
+				delete vmdump;
+				delete lkcddump;
+				break;
+			}
+			case Dump::DT_VM64:
+			{
+				VMDump64* vmdump;
+				LKCDDump64* lkcddump;
+
+				vmdump = new VMDump64(inputFileName);
+				vmdump->printInfo();
+				lkcddump = new LKCDDump64(vmdump,
+						vmdump->getRegisterContent());
+				lkcddump->writeDump(outputFileName);
+				delete vmdump;
+				delete lkcddump;
+				break;
+			}
+			case Dump::DT_VM32:
+			{
+				VMDump32* vmdump;
+				LKCDDump32* lkcddump;
+
+				vmdump = new VMDump32(inputFileName);
+				vmdump->printInfo();
+				lkcddump = new LKCDDump32(vmdump,
+						vmdump->getRegisterContent());
+				lkcddump->writeDump(outputFileName);
+				delete vmdump;
+				delete lkcddump;
+				break;
+			}
+			default:
+				throw DumpException("This is not a vmdump");
+		}
+	} catch (DumpException ex) {
+		printf("%s: %s\n", progName, ex.what());
+		fflush(stdout);
+		return 1;
+	}
+	return 0;
+}
Index: s390-tools-sles11/vmconvert/dump.cpp
===================================================================
--- s390-tools-sles11.orig/vmconvert/dump.cpp
+++ s390-tools-sles11/vmconvert/dump.cpp
@@ -47,17 +47,19 @@ void 
 ProgressBar::displayProgress(uint64_t value, uint64_t maxValue)
 {
 	int j;
-	printf("%6lld of %6lld |", (long long)value, (long long)maxValue);
-	if(progressPercentage != (int)(value*100/maxValue))
-	{
-		/* percent value has changed */
-		progressPercentage = (value*100/maxValue);
-		for (j=1; j<=progressPercentage/2; j++)
-			printf("#");
-		for (j=progressPercentage/2+1; j<=50; j++)
-			printf("-");
-		printf("| %3d%%  ", progressPercentage);
+	char progress_bar[50];
+
+	if (progressPercentage == (int) (value * 100 / maxValue))
+		fprintf(stderr, "%6lld of %6lld |\r",
+			(long long) value, (long long) maxValue);
+	else {  /* percent value has changed */
+		progressPercentage = (value * 100 / maxValue);
+		for (j = 0; j < progressPercentage / 2; j++)
+			progress_bar[j] = '#';
+		for (j = progressPercentage / 2; j < 50; j++)
+			progress_bar[j] = '-';
+		fprintf(stderr, "%6lld of %6lld |%s| %3d%%  \r",
+			(long long) value, (long long) maxValue,
+			progress_bar, progressPercentage);
 	}
-	printf("\r");
-	fflush(stdout);
 }
Index: s390-tools-sles11/vmconvert/dump.h
===================================================================
--- s390-tools-sles11.orig/vmconvert/dump.h
+++ s390-tools-sles11/vmconvert/dump.h
@@ -74,7 +74,22 @@ private:
 	int progressPercentage;
 };
 
-void s390TodToTimeval(uint64_t todval, struct timeval *xtime);
+extern void s390TodToTimeval(uint64_t todval, struct timeval *xtime);
+extern int vm_convert(const char* inputFileName, const char* outputFileName,
+		      const char* progName);
+
+static inline void dump_read(void *ptr, size_t size, size_t nmemb,
+			     FILE *stream)
+{
+	if (fread(ptr, size, nmemb, stream) != nmemb)
+		throw(DumpErrnoException("fread failed"));
+}
+
+static inline void dump_seek(FILE *stream, long offset, int whence)
+{
+	if (fseek(stream, offset, whence) == -1)
+		throw(DumpErrnoException("fseek failed"));
+}
 
 #endif /* DUMP_H */
 
Index: s390-tools-sles11/vmconvert/lkcd_dump.cpp
===================================================================
--- s390-tools-sles11.orig/vmconvert/lkcd_dump.cpp
+++ s390-tools-sles11/vmconvert/lkcd_dump.cpp
@@ -12,6 +12,8 @@
 
 #include <zlib.h>
 #include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
 #include "lkcd_dump.h"
 
 LKCDDump::LKCDDump(Dump* dump, const char* arch){
@@ -65,39 +67,37 @@ LKCDDump::writeDump(const char* fileName
 	char dump_page_buf[DUMP_BUFFER_SIZE];
 	char dpcpage[DUMP_PAGE_SIZE];
 	char buf[DUMP_PAGE_SIZE];
+	char dump_header_buf[DUMP_HEADER_SIZE] = {};
 	uint32_t dp_size,dp_flags;
 	uint64_t mem_loc = 0;
-	uint64_t buf_loc = 0;
+	ssize_t buf_loc = 0;
 	struct _dump_page dp;
-	int size;
-	FILE* fh = NULL;
+	int size, fd;
 
-	fh = fopen(fileName,"w+");
-	if(!fh){
-		char msg[1024];
-		sprintf(msg, "Open of dump '%s' failed",fileName);
-		throw(DumpErrnoException(msg));
+	if (fileName == NULL)
+		fd = STDOUT_FILENO;
+	else {
+		fd = open(fileName, O_WRONLY | O_CREAT | O_TRUNC,
+			  S_IRUSR | S_IWUSR);
+		if (fd == -1) {
+			char msg[1024];
+			sprintf(msg, "Open of dump '%s' failed.", fileName);
+			throw(DumpErrnoException(msg));
+		}
 	}
 
 	/* write dump header */
 
-	if(fseek(fh, 0L, SEEK_SET) < 0) {
-		throw(DumpErrnoException("LKCDDump::writeDump "\
-					"- seek failed"));
-	}
-	if (fwrite((char *)&dumpHeader, sizeof(dumpHeader), 1,fh) != 1) {
+	memcpy(dump_header_buf, &dumpHeader, sizeof(dumpHeader));
+	if (write(fd, dump_header_buf, sizeof(dump_header_buf)) !=
+	    sizeof(dump_header_buf)) {
 		throw(DumpErrnoException("write failed"));
 	}
-	if(fseek(fh, DUMP_HEADER_SIZE, SEEK_SET) < 0) {
-		throw(DumpErrnoException("LKCDDump::writeDump "\
-					"- seek failed"));
-	}
 
 	/* write memory */
 
 	referenceDump->seekMem(0);
 
-	mem_loc = 0;
 	while (mem_loc < dumpHeader.memory_size) {
 		referenceDump->readMem(buf, DUMP_PAGE_SIZE);
 		copyRegsToPage(mem_loc,buf);
@@ -139,7 +139,7 @@ LKCDDump::writeDump(const char* fileName
 					(const void *)buf, dp_size);
 		}
 		buf_loc += dp_size;
-		if(fwrite(dump_page_buf, buf_loc, 1,fh) != 1){
+		if(write(fd, dump_page_buf, buf_loc) != buf_loc){
 			throw(DumpErrnoException("write failed"));
 		}
 		buf_loc = 0;
@@ -154,13 +154,12 @@ LKCDDump::writeDump(const char* fileName
 	dp.address = 0x0;
 	dp.size    = DUMP_DH_END;
 	dp.flags   = 0x0;
-	if(fwrite(dump_page_buf, sizeof(dp),1,fh) != 1){
+	if(write(fd, dump_page_buf, sizeof(dp)) != sizeof(dp)){
 		throw(DumpErrnoException("write failed"));
 	}
-	printf("\n\n");
-	fflush(stdout);
-	if(fh)
-		fclose(fh);
+	fprintf(stderr, "\n");
+	if (fd != STDOUT_FILENO)
+		close(fd);
 }
 
 struct timeval 
Index: s390-tools-sles11/vmconvert/main.cpp
===================================================================
--- s390-tools-sles11.orig/vmconvert/main.cpp
+++ s390-tools-sles11/vmconvert/main.cpp
@@ -7,14 +7,12 @@
  *  Author(s): Michael Holzheu
  */
 
-#include <stdio.h>
 #include <getopt.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #include "vm_dump.h"
-#include "lkcd_dump.h"
 #include "zt_common.h"
 
 static struct option longopts[] = {
@@ -30,10 +28,10 @@ extern char *optarg;
 
 /* Version info */
 static const char version_text[] = "vmconvert: vmdump converter tool version "\
-                                   RELEASE_STRING".";
+                                   RELEASE_STRING;
 
 /* Copyright notice */
-static const char copyright_notice[] = "Copyright IBM Corp. 2004, 2006.";
+static const char copyright_notice[] = "Copyright IBM Corp. 2004, 2006";
 
 /* Usage information */
 static const char usage_text[] = \
@@ -112,7 +110,8 @@ int
 main(int argc, char* argv[])
 {
 	struct stat s;
-	
+	int rc;
+
 	parseOpts(argc,argv);
 
 	/* Check if output file already exists */
@@ -123,60 +122,8 @@ main(int argc, char* argv[])
 		if((strcmp(answer,"y") != 0) && (strcmp(answer,"yes") != 0))
 			exit(0);
 	}
-
-	/* Do the conversion */
-	try {
-		switch(VMDump::getDumpType(inputFileName)){
-			case Dump::DT_VM64_BIG:
-			{
-				VMDump64Big* vmdump;
-				LKCDDump64* lkcddump;
-				 
-				vmdump = new VMDump64Big(inputFileName);
-				vmdump->printInfo();
-				lkcddump = new LKCDDump64(vmdump, 
-						vmdump->getRegisterContent());
-				lkcddump->writeDump(outputFileName);
-				delete vmdump;
-				delete lkcddump;
-				break;
-			}
-			case Dump::DT_VM64:
-			{
-				VMDump64* vmdump;
-				LKCDDump64* lkcddump;
-				
-				vmdump = new VMDump64(inputFileName);
-				vmdump->printInfo();
-				lkcddump = new LKCDDump64(vmdump, 
-						vmdump->getRegisterContent());
-				lkcddump->writeDump(outputFileName);
-				delete vmdump;
-				delete lkcddump;
-				break;
-			}
-			case Dump::DT_VM32:
-			{
-				VMDump32* vmdump;
-				LKCDDump32* lkcddump;
-				 
-				vmdump = new VMDump32(inputFileName);
-				vmdump->printInfo();
-				lkcddump = new LKCDDump32(vmdump, 
-						vmdump->getRegisterContent());
-				lkcddump->writeDump(outputFileName);
-				delete vmdump;
-				delete lkcddump;
-				break;
-			}
-			default:
-				throw DumpException("This is not a vmdump");
-		}
-	} catch (DumpException ex) {
-		printf("%s: %s\n",argv[0],ex.what());
-		fflush(stdout);
-		return 1;
-	}
-	printf("'%s' has been written successfully.\n",outputFileName);
-	return 0;
+	rc = vm_convert(inputFileName, outputFileName, argv[0]);
+	if (!rc)
+		printf("'%s' has been written successfully.\n", outputFileName);
+	return rc;
 }
Index: s390-tools-sles11/vmconvert/register_content.cpp
===================================================================
--- s390-tools-sles11.orig/vmconvert/register_content.cpp
+++ s390-tools-sles11/vmconvert/register_content.cpp
@@ -12,7 +12,7 @@
 #include "dump.h"
 
 RegisterContent32::RegisterContent32(void)
-	: nrCpus(0)
+	: regSets(), nrCpus(0)
 {
 }
 
@@ -44,7 +44,7 @@ RegisterContent32::getRegisterSet(int cp
 }
 
 RegisterContent64::RegisterContent64(void)
-	: nrCpus(0)
+	: regSets(), nrCpus(0)
 {
 }
 
Index: s390-tools-sles11/vmconvert/vm_dump.cpp
===================================================================
--- s390-tools-sles11.orig/vmconvert/vm_dump.cpp
+++ s390-tools-sles11/vmconvert/vm_dump.cpp
@@ -32,19 +32,17 @@ VMDump::getDumpType(const char* inputFil
 	}
 
         /* Record 2: fmbk */
-	if(fseek(fh,0x1000,SEEK_SET) != 0) {
-		fclose(fh);
-		throw DumpErrnoException("Could not seek in vmdump");
-	}
+	dump_seek(fh,0x1000,SEEK_SET);
 	if(fread(&fmbk,sizeof(fmbk),1,fh) != 1) {
-		fclose(fh);
 		if(ferror(fh)) {
 			sprintf(msg,"Could not read header of vmdump '%s'",
 					inputFileName);
+			fclose(fh);
 			throw DumpErrnoException(msg);
 		} else{
 			sprintf(msg,"Input file '%s' is not a vmdump",
 					inputFileName);
+			fclose(fh);
 			throw DumpException(msg);
 		}
 	}
@@ -57,21 +55,18 @@ VMDump::getDumpType(const char* inputFil
 	}
 
 	/* Record 3-7: fir */
-	if(fseek(fh,(fmbk.rec_nr_fir-1)* 0x1000,SEEK_SET) != 0) {
-		fclose(fh);
-		sprintf(msg,"Could not seek in vmdump '%s'",inputFileName);
-		throw DumpErrnoException(msg);
-	}
+	dump_seek(fh,(fmbk.rec_nr_fir-1)* 0x1000,SEEK_SET);
 	if(fread(&fir,sizeof(fir),1,fh) != 1) {
-		fclose(fh);
 		if(ferror(fh)) {
 			sprintf(msg,"Could not read header of vmdump '%s'",
 					inputFileName);
+			fclose(fh);
 			throw DumpErrnoException(msg);
 		}
 		else{
 			sprintf(msg,"Could not read header of vmdump '%s'",
 					inputFileName);
+			fclose(fh);
 			throw DumpException(msg);
 		}
 	}
@@ -95,23 +90,15 @@ VMDump::VMDump(const char *fileName) : D
 
 	/* Record 1: adsrRecord */
 
-	if(fseek(fh,0,SEEK_SET) != 0) {
-		throw(DumpErrnoException("fseek failed"));
-	}
-	if(fread(&adsrRecord,sizeof(adsrRecord),1,fh) != 1) {
-		throw(DumpErrnoException("read failed"));
-	}
+	dump_seek(fh,0,SEEK_SET);
+	dump_read(&adsrRecord,sizeof(adsrRecord),1,fh);
 
 	if(debug) {
 		char buf[1024];
 		int i;
 	
-		if(fseek(fh,adsrRecord.sec3_offset,SEEK_SET) != 0) {
-			throw(DumpErrnoException("fseek failed"));
-		}
-		if(fread(buf,adsrRecord.sec3_len,1,fh) != 1) {
-			throw(DumpErrnoException("read failed"));
-		}
+		dump_seek(fh,adsrRecord.sec3_offset,SEEK_SET);
+		dump_read(buf, adsrRecord.sec3_len,1,fh);
 		ebcAsc(buf,adsrRecord.sec3_len);
 		for(i=0; i < adsrRecord.sec3_len; i++) {
 			if((buf[i]==0) || iscntrl(buf[i]))
@@ -123,12 +110,8 @@ VMDump::VMDump(const char *fileName) : D
 
 	/* Record 2: fmbk */
 
-	if(fseek(fh,0x1000,SEEK_SET) != 0) {
-		throw(DumpErrnoException("fseek failed"));
-	}
-	if(fread(&fmbkRecord,sizeof(fmbkRecord),1,fh) != 1) {
-		throw(DumpErrnoException("read failed"));
-	}
+	dump_seek(fh,0x1000,SEEK_SET);
+	dump_read(&fmbkRecord,sizeof(fmbkRecord),1,fh);
 
 	/* Check if this is a vmdump */
 	if(memcmp(fmbkRecord.id, fmbk_id, 8) != 0) {
@@ -139,13 +122,8 @@ VMDump::VMDump(const char *fileName) : D
 
 	/* Record 8: albk */
 
-	if(fseek(fh,(fmbkRecord.rec_nr_access-1)*0x1000 ,SEEK_SET) != 0) {
-		throw(DumpErrnoException("fseek failed"));
-	}
-	if(fread(&albkRecord,sizeof(albkRecord),1,fh) != 1) {
-		throw(DumpErrnoException("read failed"));
-	}
-
+	dump_seek(fh,(fmbkRecord.rec_nr_access-1)*0x1000 ,SEEK_SET);
+	dump_read(&albkRecord,sizeof(albkRecord),1,fh);
 }
 
 
@@ -216,7 +194,7 @@ VMDump::printInfo(void)
 	struct timeval time;
 	s390TodToTimeval(adsrRecord.tod,&time);
 
-	printf("  date........: %s",ctime(&time.tv_sec));
+	fprintf(stderr, "  date........: %s",ctime(&time.tv_sec));
 }
 
 int
@@ -235,7 +213,7 @@ VMDump::readMem(char* buf, int size)
 	int i;
 
 	if(pageOffset == 0)
-		fseek(fh, memoryStartRecord, SEEK_SET);
+		dump_seek(fh, memoryStartRecord, SEEK_SET);
 
 	if(size % 0x1000 != 0) {
 		throw(DumpException("internal error: VMDump::readMem() "\
@@ -244,8 +222,7 @@ VMDump::readMem(char* buf, int size)
 
 	for(i = 0; i < size; i += 0x1000) {
 		if(testPage(pageOffset)) {
-			if(fread(buf + i, 0x1000,1,fh) != 1)
-				throw(DumpErrnoException("read failed"));
+			dump_read(buf + i, 0x1000,1,fh);
 		} else {
 			memset(buf + i, 0, 0x1000);
 		}
@@ -269,23 +246,14 @@ VMDumpClassic::VMDumpClassic(const char 
 
 	/* Record 9: asibk */
 
-	if(fseek(fh,fmbkRecord.rec_nr_access * 0x1000,SEEK_SET) != 0) {
-		throw(DumpErrnoException("fseek failed"));
-	}
-	if(fread(&asibkRecord,sizeof(asibkRecord),1,fh) != 1) {
-		throw(DumpErrnoException("read failed"));
-	}
+	dump_seek(fh,fmbkRecord.rec_nr_access * 0x1000,SEEK_SET);
+	dump_read(&asibkRecord,sizeof(asibkRecord),1,fh);
 
 	/* Record 10: bitmaps */
 
-	if(fseek(fh,(fmbkRecord.rec_nr_access + 1)* 0x1000 ,SEEK_SET) != 0) { 
-		/* asibkRecord.nr_of_recs_of_first_bit_map XXX ?? */
-		throw(DumpErrnoException("fseek failed"));
-	}
+	dump_seek(fh,(fmbkRecord.rec_nr_access + 1)* 0x1000 ,SEEK_SET);
         bitmap = new char[asibkRecord.storage_size_2GB / (0x1000 * 8)];
-        if(fread(bitmap,asibkRecord.storage_size_2GB / (0x1000*8),1,fh) != 1) {
-		throw(DumpErrnoException("read failed"));
-        }
+	dump_read(bitmap,asibkRecord.storage_size_2GB / (0x1000*8),1,fh);
 
         bitMapPages=asibkRecord.storage_size_2GB / (0x1000 * 8);
         if(bitMapPages % 0x1000 != 0)
@@ -313,7 +281,7 @@ void
 VMDumpClassic::printInfo(void)
 {
 	VMDump::printInfo();
-	printf("  storage.....: %i MB\n",
+	fprintf(stderr, "  storage.....: %i MB\n",
 			asibkRecord.storage_size_2GB/(1024*1024));
 }
 
@@ -335,13 +303,14 @@ VMDump32::VMDump32(const char* filename)
 		return;
 	}
 
-	fseek(fh,(fmbkRecord.rec_nr_fir-1)* 0x1000 ,SEEK_SET);
-	fread(&fir32Record,sizeof(fir32Record),1,fh);
+	dump_seek(fh,(fmbkRecord.rec_nr_fir-1)* 0x1000 ,SEEK_SET);
+	dump_read(&fir32Record,sizeof(fir32Record),1,fh);
 
 	fir32OtherRecords = new _fir_other_32[fir32Record.online_cpus];
 	for(i=0; i < fir32Record.online_cpus; i++) {
 		/* fir other */
-		fread(&fir32OtherRecords[i],sizeof(fir32OtherRecords[i]),1,fh);
+		dump_read(&fir32OtherRecords[i],sizeof(fir32OtherRecords[i]),1,
+			  fh);
 	}
 	if(debug)
 		printDebug();
@@ -407,10 +376,10 @@ VMDump32::printDebug(void)
 void
 VMDump32::printInfo(void)
 {
-	printf("vmdump information:\n");
-	printf("  architecture: 32 bit\n");
+	fprintf(stderr, "vmdump information:\n");
+	fprintf(stderr, "  architecture: 32 bit\n");
 	VMDumpClassic::printInfo();
-	printf("  cpus........: %x\n",fir32Record.online_cpus + 1);
+	fprintf(stderr, "  cpus........: %x\n",fir32Record.online_cpus + 1);
 }
 
 
@@ -432,13 +401,14 @@ VMDump64::VMDump64(const char* filename)
 		return;
 	}
 
-	fseek(fh,(fmbkRecord.rec_nr_fir-1)* 0x1000 ,SEEK_SET);
-	fread(&fir64Record,sizeof(fir64Record),1,fh);
+	dump_seek(fh,(fmbkRecord.rec_nr_fir-1)* 0x1000 ,SEEK_SET);
+	dump_read(&fir64Record,sizeof(fir64Record),1,fh);
 
 	fir64OtherRecords = new _fir_other_64[fir64Record.online_cpus];
 	for(i=0; i < fir64Record.online_cpus; i++) {
 		/* fir other */
-		fread(&fir64OtherRecords[i],sizeof(fir64OtherRecords[i]),1,fh);
+		dump_read(&fir64OtherRecords[i],sizeof(fir64OtherRecords[i]),1,
+			  fh);
 	}
 	if(debug)
 		printDebug();
@@ -463,10 +433,10 @@ VMDump64::printDebug(void)
 void
 VMDump64::printInfo(void)
 {
-	printf("vmdump information:\n");
-	printf("  architecture: 64 bit\n");
+	fprintf(stderr, "vmdump information:\n");
+	fprintf(stderr, "  architecture: 64 bit\n");
 	VMDumpClassic::printInfo();
-	printf("  cpus........: %x\n",fir64Record.online_cpus + 1);
+	fprintf(stderr, "  cpus........: %x\n",fir64Record.online_cpus + 1);
 }
 
 
@@ -532,12 +502,8 @@ VMDump64Big::VMDump64Big(const char* fil
 		
 	/* Record 9: asibk */
 
-	if(fseek(fh,fmbkRecord.rec_nr_access * 0x1000,SEEK_SET) != 0) {
-		throw(DumpErrnoException("fseek failed"));
-	}
-	if(fread(&asibkRecordNew,sizeof(asibkRecordNew),1,fh) != 1) {
-		throw(DumpErrnoException("read failed"));
-	}
+	dump_seek(fh,fmbkRecord.rec_nr_access * 0x1000,SEEK_SET);
+	dump_read(&asibkRecordNew,sizeof(asibkRecordNew),1,fh);
 
 	/* Record 10: bitmaps: */
 	/* Read all bitmap pages and setup bitmap array */
@@ -551,24 +517,16 @@ VMDump64Big::VMDump64Big(const char* fil
 	}
 	memset(bitmap,0,asibkRecordNew.storage_size_def_store/(0x1000 * 8));
 
-	if(fseek(fh,(fmbkRecord.rec_nr_access + 1)* 0x1000 ,SEEK_SET) != 0) { 
-		/* asibkRecord.nr_of_recs_of_first_bit_map XXX ?? */
-		throw(DumpErrnoException("fseek failed"));
-	}
+	dump_seek(fh,(fmbkRecord.rec_nr_access + 1)* 0x1000 ,SEEK_SET);
 
 	do {
 		char bmIndexPage[0x1000];
-		if(fread(bmIndexPage,sizeof(bmIndexPage),1,fh) != 1) {
-			throw(DumpErrnoException("read failed"));
-		}
+		dump_read(bmIndexPage,sizeof(bmIndexPage),1,fh);
 		memoryStartRecord += 0x1000;
 		for(i=0; i < 0x1000; i++) {
 			if(testBitmapPage(bmIndexPage,i)) {
 				char bmPage[0x1000];
-				if(fread(bmPage,sizeof(bmPage),1,fh) != 1) {
-					throw(DumpErrnoException("read "\
-								"failed"));
-				}
+				dump_read(bmPage,sizeof(bmPage),1,fh);
 				memoryStartRecord += 0x1000;
 				for(j = 0; j < 0x1000; j++) {
 					if(testBitmapKeyPage(bmPage, j)) {
@@ -590,13 +548,14 @@ all_bitmaps_read:
 	if(debug)
 		printf("Mem Offset: %llx\n",(long long)memoryStartRecord);
 
-	fseek(fh,(fmbkRecord.rec_nr_fir-1)* 0x1000 ,SEEK_SET);
-	fread(&fir64Record,sizeof(fir64Record),1,fh);
+	dump_seek(fh,(fmbkRecord.rec_nr_fir-1)* 0x1000 ,SEEK_SET);
+	dump_read(&fir64Record,sizeof(fir64Record),1,fh);
 
 	fir64OtherRecords = new _fir_other_64[fir64Record.online_cpus];
 	for(i=0; i < fir64Record.online_cpus; i++) {
 		/* fir other */
-		fread(&fir64OtherRecords[i],sizeof(fir64OtherRecords[i]),1,fh);
+		dump_read(&fir64OtherRecords[i],sizeof(fir64OtherRecords[i]),1,
+			  fh);
 	}
 	if(debug)
 		printDebug();
@@ -621,12 +580,12 @@ VMDump64Big::printDebug(void)
 void
 VMDump64Big::printInfo(void)
 {
-	printf("vmdump information:\n");
-	printf("  architecture: 64 bit (big)\n");
-	printf("  storage.....: %lli MB\n",
+	fprintf(stderr, "vmdump information:\n");
+	fprintf(stderr, "  architecture: 64 bit (big)\n");
+	fprintf(stderr, "  storage.....: %lli MB\n",
 		(long long)asibkRecordNew.storage_size_def_store / (1024*1024));
 	VMDump::printInfo();
-	printf("  cpus........: %x\n",fir64Record.online_cpus + 1);
+	fprintf(stderr, "  cpus........: %x\n",fir64Record.online_cpus + 1);
 }
 
 
Index: s390-tools-sles11/vmur/Makefile
===================================================================
--- s390-tools-sles11.orig/vmur/Makefile
+++ s390-tools-sles11/vmur/Makefile
@@ -1,12 +1,25 @@
 # Common definitions
 include ../common.mak
 
-CFLAGS        += -D_FILE_OFFSET_BITS=64 -I../include -L.
+CXXFLAGS      += -c -D_FILE_OFFSET_BITS=64 -I../include -I../vmconvert
+VMCONVERT_SRC	= ../vmconvert/convert.cpp ../vmconvert/lkcd_dump.cpp \
+		  ../vmconvert/vm_dump.cpp ../vmconvert/register_content.cpp \
+		  ../vmconvert/dump.cpp ../vmconvert/dump.h \
+		  ../vmconvert/lkcd_dump.h ../vmconvert/register_content.h \
+		  ../vmconvert/vm_dump.h
+VMCONVERT_OBJS	= ../vmconvert/convert.o ../vmconvert/lkcd_dump.o \
+		  ../vmconvert/vm_dump.o ../vmconvert/register_content.o \
+		  ../vmconvert/dump.o
+
+OBJS          = vmur.o $(VMCONVERT_OBJS)
 
 all: vmur
 
-vmur: vmur.c vmur.h
-	$(CC) $(CFLAGS) -o $@ vmur.c
+vmur.o: vmur.cpp vmur.h $(VMCONVERT_SRC)
+	$(CXX) $(CXXFLAGS) vmur.cpp
+
+vmur: $(OBJS)
+	$(CXX) -o vmur $(OBJS) -lz
 
 install: all
 	$(INSTALL) -d -m 755 $(BINDIR) $(MANDIR)/man8
Index: s390-tools-sles11/vmur/vmur.8
===================================================================
--- s390-tools-sles11.orig/vmur/vmur.8
+++ s390-tools-sles11/vmur/vmur.8
@@ -56,7 +56,7 @@ respective command context.
 .IP "" 0
 Synopsis:
 .IP "" 2
-receive [-fH] [-d dev_node] [-t | -b sep.pad]
+receive [-fH] [-d dev_node] [-t | -b sep.pad | -c]
 spoolid
 [-O | outfile]
 .PP
@@ -118,6 +118,12 @@ character sets, for example:
 # vmur re 7 -b 0x25,0x40 -O | iconv -f EBCDIC-US -t ISO-8859-1 > file
 .SP
 .IP "" 0
+\fB-c or --convert\fR
+.IP "" 2
+Specifies to convert the VMDUMP spool file into a
+format appropriate for further analysis with crash or lcrash.
+.SP
+.IP "" 0
 \fB-O or --stdout\fR
 .IP "" 2
 Specifies that the reader file's contents are written to
Index: s390-tools-sles11/vmur/vmur.c
===================================================================
--- s390-tools-sles11.orig/vmur/vmur.c
+++ /dev/null
@@ -1,2054 +0,0 @@
-/*************************************************************************
- *
- * File...........: s390-tools/vmur/vmur.c
- * Author(s)......: Frank Munzert <munzert@de.ibm.com>
- *                  Michael Holzheu <holzheu@de.ibm.com>
- *
- * Copyright IBM Corp. 2007
- *
- *************************************************************************/
-
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <libgen.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <libgen.h>
-#include <signal.h>
-#include <iconv.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/file.h>
-#include <ctype.h>
-#include <linux/types.h>
-#include "zt_common.h"
-#include "vmur.h"
-
-/* Program name */
-static char *prog_name;
-
-/* Short description */
-static const char tool_name[] =
-	"vmur: Control virtual reader, punch and printer";
-
-/* Copyright notice */
-static const char copyright_notice[] = "Copyright IBM Corp. 2007";
-
-/* Structure for all global settings */
-struct vmur vmur_info;
-
-/*
- * Structure vmur is used to store the command line parameters and
- * other information needed at different places
- */
-struct vmur {
-	char  spoolid[5];
-	int   spoolid_specified;
-	char  spoolfile_name[9];
-	int   spoolfile_name_specified;
-	char  spoolfile_type[9];
-	int   spoolfile_type_specified;
-	char  devnode[PATH_MAX];
-	int   devnode_specified;
-	char  node[9];
-	int   node_specified;
-	char  file_name[PATH_MAX];
-	int   file_name_specified;
-	char  queue[4];
-	int   queue_specified;
-	char  user[9];
-	int   user_specified;
-	int   blocked_separator;
-	int   blocked_padding;
-	int   blocked_specified;
-	int   rdr_specified;
-	int   text_specified;
-	int   force_specified;
-	int   stdout_specified;
-	int   hold_specified;
-	enum  ur_action action;
-	int   devno;
-	int   ur_reclen;
-	int   file_reclen;
-	enum spoolfile_fmt spoolfile_fmt;
-	struct sigaction sigact;
-	iconv_t iconv;
-};
-
-/*
- * Print version information.
- */
-static void print_version (void)
-{
-	printf("%s version %s.\n", tool_name, RELEASE_STRING);
-	printf("%s.\n", copyright_notice);
-}
-
-/*
- * Convert string to to_upper
- */
-static char *to_upper(char *str)
-{
-	char *ptr = str;
-
-	while (*ptr) {
-		*ptr = toupper(*ptr);
-		ptr++;
-	}
-	return str;
-}
-
-/*
- * Convert string to valid CP spool file name
- */
-static char *to_valid_cpname(char *str)
-{
-	if (strlen(str) > 8)
-		str[8] = 0;
-
-	while (*str) {
-		if (!isprint(*str) || isspace(*str))
-			*str = '_';
-		str++;
-	}
-	return str;
-}
-
-/*
- * Convert string to valid Linux file name
- */
-static char *to_valid_linux_name(char *str)
-{
-	while (*str) {
-		if (*str == '/')
-			*str = '_';
-		str++;
-	}
-	return str;
-}
-
-/*
- * Print out usage text
- */
-
-static char HELP_TEXT[] =
-"Usage: vmur receive [OPTIONS] [SPOOLID] [FILE]\n"
-"       vmur punch   [OPTIONS] [FILE]\n"
-"       vmur print   [OPTIONS] [FILE]\n"
-"       vmur purge   [OPTIONS] [SPOOLID]\n"
-"       vmur order   [OPTIONS] [SPOOLID]\n"
-"       vmur list    [OPTIONS] [SPOOLID]\n"
-"\n"
-"Control virtual reader, punch and printer. Available commands are:\n"
-"  * REceive: Receive spool files from reader queue\n"
-"  * PUNch:   Punch a file to punch queue\n"
-"  * PRint:   Print a file to printer queue\n"
-"  * PURge:   Purge spool files\n"
-"  * ORder:   Order spool file\n"
-"  * LIst:    List spool files\n"
-"\n"
-"General options:\n"
-"\n"
-"-h, --help               Print this help, then exit.\n"
-"-v, --version            Print version information, then exit.\n"
-"\n"
-"Options of 'receive' command:\n"
-"\n"
-"-d, --device             Device node of the VM virtual reader.\n"
-"                         If omitted, /dev/vmrdr-0.0.000c is assumed.\n"
-"-t, --text               Indicates text data causing EBCDIC to ASCII\n"
-"                         conversion.\n"
-"-b, --blocked            Use blocked mode.\n"
-"-O, --stdout             Write spool file to stdout.\n"
-"-f, --force              Overwrite files without prompt.\n"
-"-H, --hold               Hold spool file in reader after receive.\n"
-"\n"
-"Options for 'punch' and 'print' command:\n"
-"\n"
-"-d, --device             Device node of the VM virtual punch or printer.\n"
-"                         If omitted, /dev/vmpun-0.0.000d or\n"
-"                         /dev/vmprt-0.0.000e is assumed, respectively.\n"
-"-t, --text               Indicates text data causing ASCII to EBCDIC\n"
-"                         conversion.\n"
-"-b, --blocked            Use blocked mode.\n"
-"-r, --rdr                Indicates to transfer file from punch/printer\n"
-"                         to reader.\n"
-"-u, --user               Transfer file to user's reader.\n"
-"                         If omitted: Your guest machine's reader.\n"
-"-n, --node               Remote node to send the file to.\n"
-"                         If omitted: Your local VM node.\n"
-"-N, --name               Name of new spool file.\n"
-"-f, --force              Convert file name to valid spool file name\n"
-"                         automatically without prompt.\n"
-"\n"
-"Options for 'purge' command:\n"
-"\n"
-"-f, --force              Purge without prompt.\n"
-"\n"
-"Options for 'order', 'list', and 'purge' commands:\n"
-"\n"
-"-q, --queue              Target queue for command. Possible queues are:\n"
-"                         'rdr' (default), 'pun' and 'prt'.\n";
-
-static void usage(void)
-{
-	printf(HELP_TEXT);
-}
-
-/*
- * Signal handler
- */
-static void set_signal_handler(struct vmur *info,
-			       void (*handler) (int, siginfo_t *, void *))
-{
-	info->sigact.sa_flags = (SA_NODEFER | SA_SIGINFO | SA_RESETHAND);
-	info->sigact.sa_sigaction = handler;
-
-	if (sigemptyset(&info->sigact.sa_mask) < 0)
-		goto fail;
-	if (sigaction(SIGINT, &info->sigact, NULL) < 0)
-		goto fail;
-	if (sigaction(SIGTERM, &info->sigact, NULL) < 0)
-		goto fail;
-
-	return;
-fail:
-	ERR_EXIT("Could not initialize signal handler (errno = %i)\n", errno);
-}
-
-/*
- * Read at most COUNT bytes from FD into memory at location BUF.
- * Return number of bytes read on success, -1 on error.
- */
-static ssize_t read_buf(int fd, char *buf, size_t count)
-{
-	ssize_t rc, len;
-
-	for (len = 0; len < count; len += rc) {
-		rc = read(fd, &buf[len], count - len);
-		if (rc == -1)
-			return -1;
-		if (rc == 0)
-			break;
-	}
-	return len;
-}
-
-/*
- * Strip leading CP header from error message
- */
-static void strip_cperr(char *buf)
-{
-	int i;
-
-	for (i = 0; i < strlen(buf) - CP_PREFIX_LEN; i++) {
-		if (strncmp(buf + i, "HCP", 3) == 0) {
-			int offs = i + CP_PREFIX_LEN;
-			memmove(buf, buf + offs, strlen(buf) - offs);
-			/* terminate string and remove newline */
-			buf[strlen(buf) - offs - 1] = 0;
-			return;
-		}
-	}
-}
-
-int _cpcmd(char *cpcmd, char **resp, int *rc, int retry, int upper)
-{
-	int fd, len, cprc, bufsize = VMCP_BUFSIZE;
-	char *buf;
-	char cmd[MAXCMDLEN];
-
-	strcpy(cmd, cpcmd);
-	if (upper)
-		to_upper(cmd);
-
-	fd = open(VMCP_DEVICE_NODE, O_RDWR);
-	if (fd == -1)
-		ERR_EXIT("Could not issue cp command: \"%s\"\n"
-			 "Ensure that vmcp kernel module is loaded!\n", cmd);
-
-	do {
-		if (ioctl(fd, VMCP_SETBUF, &bufsize) == -1)
-			goto fail;
-
-		if (write(fd, cmd, strlen(cmd)) == -1)
-			goto fail;
-
-		if (ioctl(fd, VMCP_GETCODE, &cprc) == -1)
-			goto fail;
-
-		if (ioctl(fd, VMCP_GETSIZE, &len) == -1)
-			goto fail;
-
-		if (len <= bufsize)
-			break;
-		else if (retry)
-			bufsize = len;
-		else
-			ERR_EXIT("Not enough buffer space (%i/%i) for CP "
-				 "command '%s'.\nSorry, please issue command "
-				 "on your 3270 console!\n", len, bufsize, cmd);
-	} while (1);
-
-	buf = malloc(len + 1);
-	if (!buf)
-		ERR_EXIT("Out of memory for CP command '%s'\n", cmd);
-
-	memset(buf, 0, len + 1);
-	if (read_buf(fd, buf, len) == -1)
-		goto fail;
-
-	if (rc == NULL) {
-		if (cprc != 0) {
-			/* caller wants us to handle the error */
-			strip_cperr(buf);
-			ERR_EXIT("%s\n", buf);
-		}
-	} else {
-		*rc = cprc;
-	}
-
-	if (resp)
-		*resp = buf;
-	else
-		free(buf);
-	close(fd);
-	return strlen(buf);
-
-fail:
-	ERR_EXIT("CP command '%s' failed.\n", cmd);
-}
-
-/*
- * Issue   CP command:
- * @cpcmd: CP command to be issued.
- * @resp:  CP command response on success, an error message on error.
- * @rc:    CP return code.
- * @retry: retry = 1 -> CP command can be retried.
- * This function converts the command string to uppercase.
- */
-int cpcmd(char *cpcmd, char **resp, int *rc, int retry)
-{
-	return _cpcmd(cpcmd, resp, rc, retry, 1);
-}
-
-/*
- * Issue   CP command:
- * @cpcmd: CP command to be issued.
- * @resp:  CP command response on success, an error message on error.
- * @rc:    CP return code.
- * @retry: retry = 1 -> CP command can be retried.
- * This function does not convert the command string to uppercase.
- */
-int cpcmd_cs(char *cpcmd, char **resp, int *rc, int retry)
-{
-	return _cpcmd(cpcmd, resp, rc, retry, 0);
-}
-
-/*
- * Extract minor from sysfs file
- */
-static int get_minor(char *path)
-{
-	FILE *fd;
-	char buf[20];
-	int major, minor;
-
-	fd = fopen(path, "r");
-	if (!fd)
-		ERR_EXIT("Could not open %s (err = %i)\n", path, errno);
-	fread(buf, sizeof(buf), 1, fd);
-	if (ferror(fd))
-		ERR_EXIT("Could not read %s (err = %i)\n", path, errno);
-	fclose(fd);
-
-	if (sscanf(buf, "%i:%i", &major, &minor) != 2)
-		ERR_EXIT("Malformed content of %s: %s\n", path, buf);
-
-	return minor;
-}
-
-/*
- * Find device number of ur device
- */
-static int get_ur_devno(int minor)
-{
-	struct dirent *direntp;
-	char dev_file[PATH_MAX];
-	DIR *fd;
-	char bus_id[9] = {};
-	int devno;
-
-	errno = 0;
-	fd = opendir(SYSFS_CLASS_DIR);
-	if (!fd)
-		ERR_EXIT("Could not open %s (err = %i)\n", SYSFS_CLASS_DIR,
-			 errno);
-	while ((direntp = readdir(fd))) {
-		if (strcmp(direntp->d_name, ".") == 0)
-			continue;
-		if (strcmp(direntp->d_name, "..") == 0)
-			continue;
-		sprintf(dev_file, "%s/%s/dev", SYSFS_CLASS_DIR,
-			direntp->d_name);
-		if (get_minor(dev_file) == minor) {
-			/* extract device id from <vmxxx-0.0.yyyy> */
-			memcpy(bus_id, &direntp->d_name[6], 8);
-			goto found;
-		}
-	};
-	if (errno != 0)
-		ERR_EXIT("Could not read %s (err = %i)\n",
-			    SYSFS_CLASS_DIR, errno);
-	else
-		ERR_EXIT("Device not online\n");
-found:
-	if (sscanf(bus_id, "0.0.%x", &devno) != 1)
-		ERR_EXIT("Could not extract devno from %s\n", direntp->d_name);
-	closedir(fd);
-	return devno;
-}
-
-/*
- * Extract major/minor from device node
- */
-static dev_t get_node_dev(char *devnode)
-{
-	struct stat stat_info;
-
-	if (stat(devnode, &stat_info)) {
-		ERR("Unable to get status for '%s': %s\n", devnode,
-		    strerror(errno));
-		ERR_EXIT("Please check if device is online!\n");
-	}
-
-	return stat_info.st_rdev;
-}
-
-/*
- * Extract major of vmur driver
- */
-static int get_driver_major(void)
-{
-	FILE *fh;
-	char string[PROC_DEVICES_FILE_WIDTH];
-	char last_string[PROC_DEVICES_FILE_WIDTH];
-
-	fh = fopen(PROC_DEVICES_FILE, "r");
-	if (!fh) {
-		ERR("WARNING: Cannot check for vmur in file %s.\n%s\n",
-		    PROC_DEVICES_FILE, strerror(errno));
-		return -1; /* check not possible, just continue */
-	}
-	while (fscanf(fh, "%s", string) != EOF) {
-		if (strcmp(string, "vmur") == 0) {
-			fclose(fh);
-			return atoi(last_string);
-		} else {
-			strcpy(last_string, string);
-		}
-	}
-	fclose(fh);
-	ERR_EXIT("Unit record device driver not loaded.\n");
-}
-
-/*
- * Setup and check ur device
- */
-static void setup_ur_device(struct vmur *info)
-{
-	int driver_major;
-	dev_t node_dev;
-
-	driver_major = get_driver_major();
-	node_dev = get_node_dev(info->devnode);
-	if ((driver_major != -1) && (driver_major != major(node_dev)))
-		ERR_EXIT("'%s' is not a unit record device.\n",
-			 info->devnode);
-	info->devno = get_ur_devno(minor(node_dev));
-}
-
-/*
- * initialize the vmur info structure
- */
-static void init_info(struct vmur *info)
-{
-	memset(info, 0, sizeof(struct vmur));
-	strcpy(info->queue, "rdr");
-}
-
-/*
- * set positional spoolid parameter
- */
-static void set_spoolid(struct vmur *info, char **argv, int argc, int optind,
-			int mandatory)
-{
-	char *str;
-
-	if (argc <= optind) {
-		if (mandatory)
-			ERR_EXIT("No spool id specified.\n");
-		else
-			return;
-	}
-
-	if ((argc > optind + 1) && (info->action != RECEIVE))
-		ERR_EXIT("More than one spool id specified.\n");
-
-	str = argv[optind];
-
-	if (strlen(str) > 4)
-		goto invalid;
-
-	while (*str) {
-		if (!isdigit(*str))
-			goto invalid;
-		str++;
-	}
-
-	strcpy(info->spoolid, argv[optind]);
-	++info->spoolid_specified;
-	return;
-
-invalid:
-	ERR_EXIT("Spoolid must be a decimal number in range 0-9999\n");
-}
-
-/*
- * set positional file parameter
- */
-static void set_file(struct vmur *info, char **argv, int argc, int optind)
-{
-	if (argc <= optind)
-		return;
-
-	if (argc > optind + 1)
-		ERR_EXIT("More than one file specified.\n");
-
-	strncpy(info->file_name, argv[optind], sizeof(info->file_name) - 1);
-	++info->file_name_specified;
-}
-
-/*
- * Set queue: rdr, pun or prt
- */
-static void set_queue(struct vmur *info, char *queue)
-{
-	if (strcmp(queue, "rdr") == 0)
-		strcpy(info->queue, "rdr");
-	else if (strcmp(queue, "pun") == 0)
-		strcpy(info->queue, "pun");
-	else if (strcmp(queue, "prt") == 0)
-		strcpy(info->queue, "prt");
-	else
-		ERR_EXIT("Invalid queue: %s\n", queue);
-	return;
-}
-
-static void set_blocked(struct vmur *info, char *blocked)
-{
-	if (sscanf(blocked, "0x%x,0x%x", &info->blocked_separator,
-		   &info->blocked_padding) != 2)
-		goto fail;
-	if (info->blocked_separator > 255)
-		goto fail;
-	if (info->blocked_padding > 255)
-		goto fail;
-	++info->blocked_specified;
-	return;
-fail:
-	ERR_EXIT("Invalid blocked parameter. It must have the format "
-		 "'0xSS,0xPP'.\n");
-}
-
-/*
- * Parse the command line: General options
- */
-static void check_std_opts(int opt)
-{
-	switch (opt) {
-	case 'v':
-		print_version();
-		exit(0);
-	case 'h':
-		usage();
-		exit(0);
-	}
-}
-
-static void std_usage_exit(void)
-{
-	fprintf(stderr, "Try '%s --help' for more information.\n", prog_name);
-	exit(1);
-}
-
-/*
- * Parse the command line: Receive command
- */
-static void parse_opts_receive(struct vmur *info, int argc, char *argv[])
-{
-	int opt, index;
-	static struct option long_options[] = {
-		{ "version",     no_argument,       NULL, 'v'},
-		{ "help",        no_argument,       NULL, 'h'},
-		{ "text",        no_argument,       NULL, 't'},
-		{ "stdout",      no_argument,       NULL, 'O'},
-		{ "force",       no_argument,       NULL, 'f'},
-		{ "hold",        no_argument,       NULL, 'H'},
-		{ "device",      required_argument, NULL, 'd'},
-		{ "blocked",     required_argument, NULL, 'b'},
-		{ 0,             0,                 0,    0  }
-	};
-	static const char option_string[] = "vhtOfHd:b:";
-
-	strcpy(info->devnode, VMRDR_DEVICE_NODE);
-	while (1) {
-		opt = getopt_long(argc, argv, option_string,
-				  long_options, &index);
-		if (opt == -1)
-			break;
-		check_std_opts(opt);
-		switch (opt) {
-		case 't':
-			++info->text_specified;
-			break;
-		case 'd':
-			++info->devnode_specified;
-			strncpy(info->devnode, optarg,
-				sizeof(info->devnode) - 1);
-			break;
-		case 'O':
-			++info->stdout_specified;
-			break;
-		case 'b':
-			set_blocked(info, optarg);
-			break;
-		case 'f':
-			++info->force_specified;
-			break;
-		case 'H':
-			++info->hold_specified;
-			break;
-		default:
-			std_usage_exit();
-		}
-	}
-
-	set_spoolid(info, argv, argc, optind + 1, 1);
-	set_file(info, argv, argc, optind + 2);
-
-	CHECK_SPEC_MAX(info->text_specified, 1, "text");
-	CHECK_SPEC_MAX(info->devnode_specified, 1, "devnode");
-	CHECK_SPEC_MAX(info->file_name_specified, 1, "file");
-	CHECK_SPEC_MAX(info->blocked_specified, 1, "blocked");
-
-	if (info->stdout_specified && info->file_name_specified)
-		ERR_EXIT("File name not allowed, when --stdout specifed!\n");
-	if (info->blocked_specified && info->text_specified)
-		ERR_EXIT("Conflicting options: -b together with -t "
-			 "specified\n");
-}
-
-/*
- * Validate VM userID
- */
-static int invalid_userid(char *operand)
-{
-	if (strlen(operand) > 8)
-		return 1;
-	while (*operand) {
-		if (!isalnum(*operand) &&
-		    (strchr("@#$_-", *operand) == NULL))
-			return 1;
-		operand++;
-	}
-	return 0;
-}
-
-/*
- * Validate CP command operand such as nodeID or name/type
- */
-static int invalid_operand(char *operand)
-{
-	if (strlen(operand) > 8)
-		return 1;
-	while (*operand) {
-		if (!isprint(*operand) || isspace(*operand))
-			return 1;
-		operand++;
-	}
-	return 0;
-}
-
-/*
- * Check whether user is in the CP directory
- */
-static int check_local_user(char *user)
-{
-	char cmd[MAXCMDLEN];
-	int cprc;
-
-	strcpy(cmd, "LINK ");
-	strcat(cmd, user);
-	cpcmd(cmd, NULL, &cprc, 0);
-	if ((cprc == 53)	/* user not in CP directory */
-	    || (cprc == 20))	/* Userid missing or invalid */
-		return 1;
-	else
-		return 0;
-}
-
-/*
- * Set spool file name/type in vmur structure and validate name/type
- */
-static void set_spoolfile_name(struct vmur *info, char *name)
-{
-	char spoolfile_name[PATH_MAX], spoolfile_type[PATH_MAX] = {};
-	int i, flag = 0;
-
-	strcpy(spoolfile_name, name);
-
-	if (strlen(spoolfile_name) == 0)
-		ERR_EXIT("Empty spool file name is invalid\n");
-
-	/* check for period delimiting name from type */
-	if (spoolfile_name[strlen(spoolfile_name) - 1] == '.')
-		flag = 1; /* name/type string ends with period */
-	for (i = strlen(spoolfile_name) - 1; i > 0; i--) {
-		if (spoolfile_name[i] == '.') {
-			if (flag && (spoolfile_name[i-1] == '.'))
-				continue;
-			else
-				break;
-		}
-	}
-	if ((i > 0) && (i < strlen(spoolfile_name) - 1)) {
-		strcpy(spoolfile_type, spoolfile_name + i + 1);
-		spoolfile_name[i] = 0;
-		++info->spoolfile_type_specified;
-	}
-
-	if (info->force_specified) {
-		/* adjust spool file name, in order to have a valid one */
-		to_valid_cpname(spoolfile_name);
-		if (info->spoolfile_type_specified)
-			to_valid_cpname(spoolfile_type);
-		goto out;
-	}
-	if (invalid_operand(spoolfile_name) || invalid_operand(spoolfile_type))
-		goto invalid;
-out:
-	strcpy(info->spoolfile_name, spoolfile_name),
-	++info->spoolfile_name_specified;
-	if (info->spoolfile_type_specified)
-		strcpy(info->spoolfile_type, spoolfile_type);
-	return;
-
-invalid:
-	ERR_EXIT("Malformed spool file name: %s\n"
-		 "Specify --force, if the name should be converted "
-		 "automatically.\n", name);
-}
-
-/*
- * Parse the command line: punch command
- */
-static void parse_opts_punch_print(struct vmur *info, int argc, char *argv[])
-{
-	int opt, index;
-	char *spoolfile_name = NULL;
-	static struct option long_options[] = {
-		{ "version",     no_argument,       NULL, 'v'},
-		{ "help",        no_argument,       NULL, 'h'},
-		{ "text",        no_argument,       NULL, 't'},
-		{ "rdr",         no_argument,       NULL, 'r'},
-		{ "force",       no_argument,       NULL, 'f'},
-		{ "user",        required_argument, NULL, 'u'},
-		{ "node",        required_argument, NULL, 'n'},
-		{ "device",      required_argument, NULL, 's'},
-		{ "blocked",     required_argument, NULL, 'b'},
-		{ "name",        required_argument, NULL, 'N'},
-		{ 0,             0,                 0,    0  }
-	};
-	static const char option_string[] = "vhtrfu:n:d:b:N:";
-
-	if (info->action == PUNCH) {
-		strcpy(info->devnode, VMPUN_DEVICE_NODE);
-		info->ur_reclen = VMPUN_RECLEN;
-	} else {
-		strcpy(info->devnode, VMPRT_DEVICE_NODE);
-		info->ur_reclen = VMPRT_RECLEN;
-	}
-
-	while (1) {
-		opt = getopt_long(argc, argv, option_string,
-				  long_options, &index);
-		if (opt == -1)
-			break;
-		check_std_opts(opt);
-		switch (opt) {
-		case 'd':
-			++info->devnode_specified;
-			strcpy(info->devnode, optarg);
-			break;
-		case 'N':
-			++info->spoolfile_name_specified;
-			spoolfile_name = optarg;
-			break;
-		case 'r':
-			++info->rdr_specified;
-			break;
-		case 'f':
-			++info->force_specified;
-			break;
-		case 't':
-			++info->text_specified;
-			break;
-		case 'u':
-			++info->user_specified;
-			if (invalid_userid(optarg))
-				ERR_EXIT("Invalid userid: %s\n", optarg);
-			else
-				strcpy(info->user, optarg);
-			break;
-		case 'b':
-			set_blocked(info, optarg);
-			break;
-		case 'n':
-			++info->node_specified;
-			if (invalid_operand(optarg))
-				ERR_EXIT("Invalid node specified.\n");
-			else
-				strcpy(info->node, optarg);
-			break;
-		default:
-			std_usage_exit();
-		}
-	}
-
-	CHECK_SPEC_MAX(info->rdr_specified, 1, "rdr");
-	CHECK_SPEC_MAX(info->force_specified, 1, "force");
-	CHECK_SPEC_MAX(info->user_specified, 1, "user");
-	CHECK_SPEC_MAX(info->file_name_specified, 1, "file");
-	CHECK_SPEC_MAX(info->spoolfile_name_specified, 1, "name");
-	CHECK_SPEC_MAX(info->user_specified, 1, "user");
-	CHECK_SPEC_MAX(info->node_specified, 1, "node");
-	CHECK_SPEC_MAX(info->blocked_specified, 1, "blocked");
-
-	if (info->user_specified && !info->rdr_specified)
-		ERR_EXIT("--user without --rdr specified\n");
-	if (info->node_specified && !info->user_specified)
-		ERR_EXIT("--node without --user specified\n");
-	if ((info->user_specified && !info->node_specified)
-	    && check_local_user(info->user))
-		ERR_EXIT("Invalid userid: %s\n", info->user);
-	if (info->node_specified && check_local_user(RSCS_USERID))
-		ERR_EXIT("Invalid RSCS userid: %s\n", info->node);
-	if (info->blocked_specified && info->text_specified)
-		ERR_EXIT("Conflicting options: -b together with -t "
-			 "specified\n");
-
-	set_file(info, argv, argc, optind + 1);
-
-	if (info->spoolfile_name_specified)
-		set_spoolfile_name(info, spoolfile_name);
-	else if (info->file_name_specified)
-		set_spoolfile_name(info, basename(info->file_name));
-	else
-		ERR_EXIT("No name for spool file specified!\n");
-}
-
-/*
- * Parse the command line: Purge command
- */
-static void parse_opts_purge(struct vmur *info, int argc, char *argv[])
-{
-	int opt, index;
-	static struct option long_options[] = {
-		{ "version",     no_argument,       NULL, 'v'},
-		{ "help",        no_argument,       NULL, 'h'},
-		{ "force",       no_argument,       NULL, 'f'},
-		{ "queue",       no_argument,       NULL, 'q'},
-		{ 0,             0,                 0,    0  }
-	};
-	static const char option_string[] = "fvhq:";
-
-	while (1) {
-		opt = getopt_long(argc, argv, option_string,
-				  long_options, &index);
-		if (opt == -1)
-			break;
-		check_std_opts(opt);
-		switch (opt) {
-		case 'f':
-			++info->force_specified;
-			break;
-		case 'q':
-			++info->queue_specified;
-			set_queue(info, optarg);
-			break;
-		default:
-			std_usage_exit();
-		}
-	}
-	CHECK_SPEC_MAX(info->force_specified, 1, "force");
-	CHECK_SPEC_MAX(info->queue_specified, 1, "queue");
-	set_spoolid(info, argv, argc, optind + 1, 0);
-}
-
-/*
- * Parse the command line: Order command
- */
-static void parse_opts_order(struct vmur *info, int argc, char *argv[])
-{
-	int opt, index;
-	static struct option long_options[] = {
-		{ "version",     no_argument,       NULL, 'v'},
-		{ "help",        no_argument,       NULL, 'h'},
-		{ "queue",       required_argument, NULL, 'q'},
-		{ 0,             0,                 0,    0  }
-	};
-	static const char option_string[] = "vhq:";
-
-	while (1) {
-		opt = getopt_long(argc, argv, option_string,
-				  long_options, &index);
-		if (opt == -1)
-			break;
-		check_std_opts(opt);
-		switch (opt) {
-		case 'q':
-			++info->queue_specified;
-			set_queue(info, optarg);
-			break;
-		default:
-			std_usage_exit();
-		}
-	}
-	CHECK_SPEC_MAX(info->queue_specified, 1, "queue");
-	set_spoolid(info, argv, argc, optind + 1, 1);
-}
-
-/*
- * Parse the command line: List command
- */
-static void parse_opts_list(struct vmur *info, int argc, char *argv[])
-{
-	int opt, index;
-	static struct option long_options[] = {
-		{ "version",     no_argument,       NULL, 'v'},
-		{ "help",        no_argument,       NULL, 'h'},
-		{ "queue",       required_argument, NULL, 'q'},
-		{ 0,             0,                 0,    0  }
-	};
-	static const char option_string[] = "vhq:";
-
-	while (1) {
-		opt = getopt_long(argc, argv, option_string,
-				  long_options, &index);
-		if (opt == -1)
-			break;
-		check_std_opts(opt);
-		switch (opt) {
-		case 'q':
-			++info->queue_specified;
-			set_queue(info, optarg);
-			break;
-		default:
-			std_usage_exit();
-		}
-	}
-	CHECK_SPEC_MAX(info->queue_specified, 1, "queue");
-	set_spoolid(info, argv, argc, optind + 1, 0);
-}
-
-/*
- * Parse the command line: Default options
- */
-static void parse_opts_default(struct vmur *info, int argc, char *argv[])
-{
-	int opt, index;
-	static struct option long_options[] = {
-		{ "version",     no_argument,       NULL, 'v'},
-		{ "help",        no_argument,       NULL, 'h'},
-		{ 0,             0,                 0,    0  }
-	};
-	static const char option_string[] = "vh";
-
-	while (1) {
-		opt = getopt_long(argc, argv, option_string,
-				  long_options, &index);
-		if (opt == -1)
-			break;
-		check_std_opts(opt);
-		std_usage_exit();
-	}
-}
-
-/*
- * Parse action strings
- */
-static int is_action(enum ur_action action, char *str)
-{
-	char action_str[80] = {};
-
-	if (strlen(str) < ur_action_prefix_len[action])
-		return 0;
-	if (strlen(str) > strlen(ur_action_str[action]))
-		return 0;
-
-	strncpy(action_str, ur_action_str[action], strlen(str));
-
-	if (strcasecmp(str, action_str) != 0)
-		return 0;
-
-	return 1;
-}
-
-/*
- * Get action string and set action field in info structure
- */
-static int set_action(struct vmur *info, char *str)
-{
-	int action;
-
-	for (action = 0; action < LAST; action++) {
-		if (is_action((enum ur_action) action, str)) {
-			info->action = (enum ur_action) action;
-			return 0;
-		}
-	}
-	return -EINVAL;
-}
-
-/*
- * The toplevel parameter parsing function
- */
-static void parse_opts(struct vmur *info, int argc, char *argv[])
-{
-	if (argc == 1)
-		ERR_EXIT("Missing command\n");
-
-	if (set_action(info, argv[1])) {
-		parse_opts_default(info, argc, argv);
-		ERR("Unknown command '%s'\n", argv[1]);
-		std_usage_exit();
-	}
-
-	switch (info->action) {
-	case RECEIVE:
-		return parse_opts_receive(info, argc, argv);
-	case PUNCH:
-		return parse_opts_punch_print(info, argc, argv);
-	case PRINT:
-		return parse_opts_punch_print(info, argc, argv);
-	case PURGE:
-		return parse_opts_purge(info, argc, argv);
-	case ORDER:
-		return parse_opts_order(info, argc, argv);
-	case LIST:
-		return parse_opts_list(info, argc, argv);
-	default:
-		ERR_EXIT("Internal error. Unknown action: %i\n", info->action);
-	}
-}
-
-/*
- * Check if spool file has hold state "NONE"
- */
-static void check_hold_state(char *spoolid)
-{
-	char cmd[MAXCMDLEN];
-	char *response;
-
-	sprintf(cmd, "QUERY READER * ALL %s", spoolid);
-	cpcmd(cmd, &response, 0, 0);
-	response[114] = 0;
-	if (strcmp(&response[110], "NONE") != 0)
-		ERR_EXIT("Could not receive spool file %s: hold state = %s\n",
-			 spoolid, &response[110]);
-	free(response);
-}
-
-/*
- * Issue CP command ORDER RDR <spoolid>.
- * If ORDER fails, the CP command response is as follows:
- *      NO FILES ORDERED
- * HCPxxxnnnt <message text>
- * If ORDER returns successfully, CP command CHANGE RDR <spoolid> NOHOLD
- * is issued.
- */
-static void order_change_reader_file(struct vmur *info)
-{
-	char cmd[MAXCMDLEN];
-
-	sprintf(cmd, "ORDER * READER %s", info->spoolid);
-	cpcmd(cmd, NULL, NULL, 0);
-	sprintf(cmd, "CHANGE * READER %s NOHOLD", info->spoolid);
-	cpcmd(cmd, NULL, NULL, 0);
-
-	check_hold_state(info->spoolid);
-}
-
-/*
- * Issue CP command CLOSE RDR
- */
-static void close_reader(struct vmur *info, char *hold)
-{
-	char cmd[MAXCMDLEN];
-
-	sprintf(cmd, "CLOSE %X %s", info->devno, hold);
-	cpcmd(cmd, NULL, NULL, 0);
-}
-
-/*
- * strip leading and trailing blanks
- */
-static char *strstrip(char *s)
-{
-	size_t size;
-	char *end;
-
-	size = strlen(s);
-
-	if (!size)
-		return s;
-
-	end = s + size - 1;
-	while (end >= s && isspace(*end))
-		end--;
-	*(end + 1) = '\0';
-
-	while (*s && isspace(*s))
-		s++;
-
-	return s;
-}
-
-/*
- *  Check whether reader can handle class of reader spool file
- */
-static int check_class(struct vmur *info)
-{
-	char cmd[MAXCMDLEN];
-	char device_class, file_class;
-	char *buf;
-
-	sprintf(cmd, "QUERY VIRTUAL %X", info->devno);
-	cpcmd(cmd, &buf, NULL, 0);
-	device_class = buf[13];
-	free(buf);
-	if (device_class == '*') /* reader device can handle any class */
-		return 0;
-
-	sprintf(cmd, "QUERY RDR * %s ALL", info->spoolid);
-	cpcmd(cmd, &buf, NULL, 0);
-	file_class = buf[91];
-	free(buf);
-	if (file_class != device_class)
-		return 1;
-	return 0;
-}
-
-/*
- * Get filename for spoolid from reader
- */
-static int get_filename_from_reader(struct vmur *info)
-{
-	char cmd[MAXCMDLEN];
-	char name[9] = {0};
-	char type[9] = {0};
-	char *buf;
-
-	sprintf(cmd, "QUERY RDR * %s ALL", info->spoolid);
-	cpcmd(cmd, &buf, NULL, 0);
-	memcpy(name, &buf[130], 8);
-	strstrip(name);
-	memcpy(type, &buf[140], 8);
-	strstrip(type);
-	free(buf);
-	if (strlen(name) == 0) {
-		ERR("Please specify name to receive "
-		    "unnamed spool file: %s\n", info->spoolid);
-		return 1;
-	}
-
-	to_valid_linux_name(name);
-	to_valid_linux_name(type);
-
-	if (strlen(type) == 0)
-		strcpy(info->file_name, name);
-	else
-		sprintf(info->file_name, "%s.%s", name, type);
-	info->file_name_specified = 1;
-	return 0;
-}
-
-/*
- * Serialize access to unit record devices and their related spool file queues:
- * open lock file and apply lock
- */
-static void acquire_lock(struct vmur *info)
-{
-	int fd;
-	char failed_action[10] = {};
-
-	fd = open(LOCK_FILE, O_RDONLY | O_CREAT, S_IRUSR);
-	if (fd == -1) {
-		ERR("WARNING: Unable to open lock file %s, continuing "
-		    "without any serialization.\n", LOCK_FILE);
-		return;
-	}
-	if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
-		switch (info->action) {
-		case RECEIVE:
-			strcpy(failed_action, "received");
-			break;
-		case PUNCH:
-			strcpy(failed_action, "punched");
-			break;
-		case PRINT:
-			strcpy(failed_action, "printed");
-			break;
-		case PURGE:
-			strcpy(failed_action, "purged");
-			break;
-		case ORDER:
-			strcpy(failed_action, "ordered");
-			break;
-		default:
-			strcpy(failed_action, "processed");
-		}
-		ERR("A concurrent instance of vmur is already active."
-		    " No file %s.\n", failed_action);
-		exit(EBUSY);
-	}
-}
-
-/*
- * Check if file already exists. If yes, ask if it should be overwritten.
- */
-static void check_overwrite(struct vmur *info)
-{
-	char buf[5] = {};
-	struct stat stat_info;
-
-	if (info->force_specified)
-		return;
-
-	if (stat(info->file_name, &stat_info))
-		return;
-
-	if (S_ISDIR(stat_info.st_mode)) {
-		ERR("Cannot overwrite directory '%s'\n", info->file_name);
-		exit(1);
-	}
-
-	fprintf(stderr, "%s: Overwrite '%s'? ", prog_name, info->file_name);
-	fgets(buf, sizeof(buf), stdin);
-	if (strcasecmp(buf, "y\n") == 0)
-		return;
-	if (strcasecmp(buf, "yes\n") == 0)
-		return;
-	close_reader(info, "HOLD");
-	exit(0);
-}
-
-/*
- * Get format of spool file: Check VMDUMP and NETDATA flags,
- *                           set record length of file
- */
-enum spoolfile_fmt get_spoolfile_fmt(struct vmur *info,
-				     struct splink_page *buf)
-{
-	struct splink_record *rec;
-	char netdata_id[5] = {0xc9, 0xd5, 0xd4, 0xd9, 0xf0}; /* EBCDIC: INMR0 */
-	int i;
-
-	rec = (struct splink_record *) &buf->data;
-
-	if (buf->magic != 0)
-		return TYPE_VMDUMP;
-
-	info->file_reclen = buf->rec_len;
-
-	for (i = 0; i < buf->data_recs; i++) {
-		if (rec->ccw.flag & CCW_IMMED_FLAG) {
-			rec = ((void *) rec) + sizeof(rec->ccw);
-			continue; /* skip immediate CCWs */
-		}
-		if ((rec->data.flag & IS_CONTROL_RECORD) &&
-		    (memcmp(rec->data.magic, netdata_id, 5) == 0))
-			return TYPE_NETDATA;
-		rec = ((void *) rec) + rec->record_len;
-	}
-
-	return TYPE_NORMAL;
-}
-
-/*
- * Convert record for texmode: Do EBCDIC->ASCII translation
- */
-static int convert_text(struct vmur *info, struct splink_record *rec,
-			char **out_ptr)
-{
-	size_t in_count = rec->ccw.data_len;
-	size_t out_count = rec->ccw.data_len;
-	char *data_ptr = (char *) &rec->data;
-	int rc;
-
-	if ((rec->ccw.data_len == 1) && (data_ptr[0] == 0x40))
-		goto out; /* one blank -> just a newline */
-
-	rc = iconv(info->iconv, &data_ptr, &in_count, out_ptr, &out_count);
-	if ((rc == -1) || (in_count != 0)) {
-		ERR("Code page translation EBCDIC-ASCII failed\n");
-		return -1;
-	}
-
-out:
-	**out_ptr = ASCII_LF;
-	*out_ptr += 1;
-	return 0;
-}
-
-/*
- * Convert record for binary mode: Fill up missing 0x40 bytes
- */
-static int convert_binary(struct vmur *info, struct splink_record *rec,
-			  char **out_ptr)
-{
-	int residual;
-
-	memcpy(*out_ptr, &rec->data, rec->ccw.data_len);
-	*out_ptr += rec->ccw.data_len;
-
-	/* Since CP removed trailing EBCDIC blanks, we have */
-	/* to insert them again */
-
-	residual = info->file_reclen - rec->ccw.data_len;
-	memset(*out_ptr, 0x40, residual);
-	*out_ptr += residual;
-	return 0;
-}
-
-/*
- * Convert record for blocked mode: remove padding bytes and add separator
- */
-static int convert_blocked(struct vmur *info, struct splink_record *rec,
-			   char **out_ptr)
-{
-	int residual, i;
-
-	memcpy(*out_ptr, &rec->data, rec->ccw.data_len);
-	*out_ptr += rec->ccw.data_len;
-
-	/* Since CP removed trailing EBCDIC blanks, we have */
-	/* to insert them again */
-
-	residual = info->file_reclen - rec->ccw.data_len;
-	memset(*out_ptr, 0x40, residual);
-	*out_ptr += residual;
-
-	/* Now remove trailing padding bytes */
-
-	for (i = 0; i < info->file_reclen; i++) {
-		if (*(*out_ptr - 1) != info->blocked_padding)
-			break;
-		*out_ptr -= 1;
-	}
-
-	/* ... and insert separator */
-
-	**out_ptr = info->blocked_separator;
-	*out_ptr += 1;
-	return 0;
-}
-
-/*
- * Extract data from VM spool file data blocks (SPLINK).
- */
-ssize_t convert_sfdata(struct vmur *info, struct splink_page *in, char *out)
-{
-	struct splink_record *rec;
-	char *out_ptr = out;
-	int i, rc = 0;
-
-	rec = (struct splink_record *) &in->data;
-
-	for (i = 0; i < in->data_recs; i++) {
-		if (rec->ccw.opcode == NOP) {
-			rec = ((void *) rec) + rec->record_len;
-			continue; /* skip NOP CCWs */
-		} else if (rec->ccw.flag & CCW_IMMED_FLAG) {
-			rec = ((void *) rec) + sizeof(rec->ccw);
-			continue; /* skip immediate CCWs */
-		}
-		if (info->text_specified) {
-			rc = convert_text(info, rec, &out_ptr);
-			if (rc)
-				return rc;
-		} else if (info->blocked_specified) {
-			rc = convert_blocked(info, rec, &out_ptr);
-			if (rc)
-				return rc;
-		} else {
-			rc = convert_binary(info, rec, &out_ptr);
-			if (rc)
-				return rc;
-		}
-		rec = ((void *) rec) + rec->record_len;
-	}
-	return out_ptr - out;
-}
-
-/*
- * Write normal spool file data.
- */
-int write_normal(struct vmur *info, struct splink_page *sfdata, int count,
-		 int fho)
-{
-	char *outbuf;
-	int len, i;
-
-	for (i = 0; i < count; i++) {
-		struct splink_page *data;
-
-		data = &sfdata[i];
-		outbuf = malloc((info->file_reclen + 1) * data->data_recs);
-		if (!outbuf) {
-			ERR("Out of memory\n");
-			return -ENOMEM;
-		}
-		len = convert_sfdata(info, data, outbuf);
-		if (len < 0) {
-			ERR("Data conversion failed\n");
-			return -EINVAL;
-		}
-		if (write(fho, outbuf, len) == -1) {
-			ERR("Write to file %s failed: %s\n", info->file_name,
-			    strerror(errno));
-			return -errno;
-		}
-		free(outbuf);
-	}
-
-	return 0;
-}
-
-/*
- * Write vmdump data.
- */
-int write_vmdump(struct vmur *info, struct splink_page *sfdata, int count,
-		 int fho)
-{
-	if (write(fho, sfdata, count * sizeof(sfdata[0])) == -1) {
-		ERR("Write to file %s failed: %s\n", info->file_name,
-		    strerror(errno));
-		return -errno;
-	}
-
-	return 0;
-}
-
-/*
- * Close VM virtual reader in case of a signal e.g. CTRL-C
-*/
-static void ur_receive_sig_handler(int sig, siginfo_t *sip, void *p)
-{
-	close_reader(&vmur_info, "HOLD");
-	ERR_EXIT("Operation terminated, spool file received incompletely.\n");
-}
-
-/*
- * Receive reader file.
- */
-static void ur_receive(struct vmur *info)
-{
-	struct splink_page sfdata[READ_BLOCKS];
-	enum spoolfile_fmt type;
-	int fhi, fho, count;
-
-	if (check_class(info))
-		ERR_EXIT("Reader device class does not match spool file "
-			 "class.\nIssue CP command 'SPOOL %X CLASS *' to "
-			 "enable %X for any spool file class.\n",
-			 info->devno, info->devno);
-	acquire_lock(info);
-	close_reader(info, "HOLD");
-	order_change_reader_file(info);
-
-	fhi = open(info->devnode, O_RDONLY | O_NONBLOCK);
-	if (fhi == -1)
-		ERR_EXIT("Could not open device %s\n%s\n", info->devnode,
-			 strerror(errno));
-
-	set_signal_handler(info, ur_receive_sig_handler);
-
-	if (info->stdout_specified) {
-		fho = STDOUT_FILENO;
-	} else {
-		if (!info->file_name_specified &&
-		    get_filename_from_reader(info))
-			goto fail;
-		check_overwrite(info);
-		fho = open(info->file_name, O_WRONLY | O_CREAT | O_TRUNC,
-			   S_IRUSR | S_IWUSR);
-		if (fho == -1) {
-			ERR("Could not open file %s\n%s\n", info->file_name,
-			    strerror(errno));
-			goto fail;
-		}
-	}
-
-	/* Read first block and check spoolfile format */
-
-	count = read(fhi, &sfdata, READ_BLOCKS * sizeof(sfdata[0]));
-	if (count == -1) {
-		ERR("Could not read from device %s\n%s\n", info->devnode,
-		    strerror(errno));
-		goto fail;
-	}
-
-	type = get_spoolfile_fmt(info, &sfdata[0]);
-	if (type == TYPE_VMDUMP)
-		ERR("INFO: Reader file %s has VMDUMP format.\n", info->spoolid);
-	if (type == TYPE_NETDATA)
-		ERR("INFO: Reader file %s has NETDATA format.\n",
-		    info->spoolid);
-
-	if (type != TYPE_VMDUMP) {
-		int spoolid = atoi(info->spoolid);
-
-		if (spoolid != sfdata->spoolid) {
-			check_hold_state(info->spoolid);
-			ERR_EXIT("Could not receive spool file %s. Spoolid "
-				 "mismatch (%i)\n", info->spoolid,
-				 sfdata->spoolid);
-		}
-	}
-
-	/* read spool file data, convert it, and write it to output file or
-	 * stdout */
-	while (count != 0) {
-		int rc, blocks;
-
-		blocks = count / sizeof(sfdata[0]);
-		if (type == TYPE_VMDUMP)
-			rc = write_vmdump(info, &sfdata[0], blocks, fho);
-		else
-			rc = write_normal(info, &sfdata[0], blocks, fho);
-
-		if (rc)
-			goto fail;
-
-		/* read next records */
-		count = read(fhi, &sfdata, READ_BLOCKS * sizeof(sfdata[0]));
-		if (count == -1) {
-			ERR("Could not read from device %s\n%s\n",
-			    info->devnode, strerror(errno));
-			goto fail;
-		}
-	}
-	if (fho != STDOUT_FILENO)
-		close(fho);
-	close(fhi);
-	if (info->hold_specified)
-		close_reader(info, "HOLD");
-	else
-		close_reader(info, "NOHOLD NOKEEP");
-	return;
-
-fail:
-	close_reader(info, "HOLD");
-	exit(1);
-}
-
-/*
- * Issue CP command CLOSE PUNCH
- */
-static void close_ur_device(struct vmur *info)
-{
-	char cmd[MAXCMDLEN], spoolid[5] = {}, *response;
-	int cprc;
-
-	if (info->node_specified) {
-		sprintf(cmd, "SPOOL %X NOCONT", info->devno);
-		cpcmd(cmd, NULL, NULL, 0);
-	}
-	if (info->rdr_specified) {
-		sprintf(cmd, "CLOSE %X TO ", info->devno);
-		if (info->node_specified)
-			strcat(cmd, RSCS_USERID);
-		else if (info->user_specified)
-			strcat(cmd, to_upper(info->user));
-		else
-			strcat(cmd, "*");
-		strcat(cmd, " RDR");
-	} else {
-		sprintf(cmd, "CLOSE %X", info->devno);
-	}
-	strcat(cmd, " NAME ");
-	strcat(cmd, info->spoolfile_name);
-	if (info->spoolfile_type_specified) {
-		strcat(cmd, " ");
-		strcat(cmd, info->spoolfile_type);
-	}
-	cpcmd_cs(cmd, &response, &cprc, 0);
-	memcpy(spoolid, &response[9], 4);
-	free(response);
-	if (cprc == 439) {
-		if (info->action == PUNCH)
-			sprintf(cmd, "PURGE * PUN %s", spoolid);
-		else
-			sprintf(cmd, "PURGE * PRT %s", spoolid);
-		cpcmd(cmd, NULL, NULL, 0);
-		ERR_EXIT("User %s spool fileid limit exceeded.\n"
-			 , info->user);
-	}
-	if (info->rdr_specified) {
-		if (info->node_specified)
-			printf("Reader file with spoolid %s created and "
-			       "transferred to %s.\n", spoolid, RSCS_USERID);
-		else if (info->user_specified)
-			printf("Reader file with spoolid %s created and "
-			       "transferred to %s.\n", spoolid, info->user);
-		else
-			printf("Reader file with spoolid %s created.\n",
-			       spoolid);
-	} else if (info->action == PUNCH)
-		printf("Punch file with spoolid %s created.\n", spoolid);
-	else
-		printf("Printer file with spoolid %s created.\n", spoolid);
-}
-
-/*
- * Issue CP command CLOSE PUNCH
- */
-static void close_ur_device_simple(struct vmur *info)
-{
-	char cmd[MAXCMDLEN];
-
-	sprintf(cmd, "CLOSE %X", info->devno);
-	cpcmd(cmd, NULL, NULL, 0);
-}
-
-/*
- * Issue CP command CLOSE PUNCH PURGE
- */
-static void close_ur_device_purge(struct vmur *info)
-{
-	char cmd[MAXCMDLEN];
-
-	if (info->node_specified) {
-		sprintf(cmd, "SPOOL %X NOCONT", info->devno);
-		cpcmd(cmd, NULL, NULL, 0);
-	}
-	sprintf(cmd, "CLOSE %X PURGE", info->devno);
-	cpcmd(cmd, NULL, NULL, 0);
-}
-
-/*
- * Issue "CP QUERY VIRTUAL devno" to retrieve punch device information
- * Exit, if devno has CONT status
- */
-static int is_punch_cont(struct vmur *info)
-{
-	char *buf;
-	char cmd[MAXCMDLEN];
-	int rc = 0;
-
-	sprintf(cmd, "QUERY VIRTUAL %X", info->devno);
-	cpcmd(cmd, &buf, NULL, 0);
-	if (!strncmp(buf + 15, "  CONT", 6))
-		rc = 1;
-	free(buf);
-	return rc;
-}
-
-/*
- * Provide tag information for RSCS
- */
-static void rscs_punch_setup(struct vmur *info)
-{
-	char cmd[MAXCMDLEN];
-
-	sprintf(cmd, "SPOOL %X CONT", info->devno);
-	cpcmd(cmd, NULL, NULL, 0);
-	sprintf(cmd, "TAG DEV %X %s %s", info->devno, info->node, info->user);
-	cpcmd(cmd, NULL, NULL, 0);
-	return;
-}
-
-/*
- * Purge punch file in case of a signal e.g. CTRL-C
- */
-static void ur_write_sig_handler(int sig, siginfo_t *sip, void *p)
-{
-	close_ur_device_purge(&vmur_info);
-	ERR_EXIT("Operation terminated, no spool file created.\n");
-}
-
-/*
- * Read on line from fd not including newline
- */
-static int read_line(int fd, char *buf, int len, int lf)
-{
-	int offs = 0;
-
-	memset(buf, 0, len);
-	do {
-		int rc;
-
-		rc = read(fd, buf + offs, 1);
-		if (rc < 0)
-			return -EIO;
-		if (rc == 0)
-			return -ENODATA;
-		if (*(buf + offs) == lf)
-			goto found;
-		offs++;
-	} while (offs < len);
-
-	return -EINVAL;
-
-found:
-	*(buf + offs) = 0;
-	return offs;
-}
-
-/*
- * Read text file for punch/print
- */
-static int read_text_file(struct vmur *info, int fd, char *out_buf, size_t len)
-{
-	int rc, pos = 0;
-	static int line = 1;
-	char sep, pad;
-	char *buf;
-
-	sep = '\n';
-	pad = ' ';
-
-	buf = malloc(info->ur_reclen + 1);
-	if (!buf)
-		return -ENOMEM;
-
-	do {
-		int line_len;
-		size_t rec_len, out_len;
-		char *in_ptr, *out_ptr;
-
-		line_len = read_line(fd, buf, info->ur_reclen  + 1, sep);
-		if (line_len == -ENODATA) {
-			break;
-		} else if (line_len == -EINVAL) {
-			ERR("Input line %i too long. Unit record length"
-			    " must not exceed %i\n", line, info->ur_reclen);
-			goto fail;
-		} else if (line_len < 0) {
-			ERR("Read failed: %s", strerror(errno));
-			goto fail;
-		}
-		line++;
-		memset(buf + line_len, pad, info->ur_reclen - line_len);
-		rec_len = out_len = info->ur_reclen;
-		in_ptr = buf;
-		out_ptr = &out_buf[pos];
-		rc = iconv(info->iconv, &in_ptr, &rec_len, &out_ptr, &out_len);
-		if ((rc == -1) || (out_len != 0)) {
-			ERR("Code page conversion failed at line %i\n", line);
-			goto fail;
-		}
-		pos += info->ur_reclen;
-	} while (pos < len);
-	free(buf);
-	return pos;
-fail:
-	free(buf);
-	return -1;
-}
-
-/*
- * Read blocked file for punch/print
- */
-static int read_blocked_file(struct vmur *info, int fd, char *out_buf,
-			     size_t len)
-{
-	int line_len, pos = 0;
-	static int line = 1;
-	char sep, pad;
-	char *buf;
-
-	sep = info->blocked_separator;
-	pad = info->blocked_padding;
-
-	buf = malloc(info->ur_reclen + 1);
-	if (!buf)
-		return -ENOMEM;
-
-	do {
-		line_len = read_line(fd, buf, info->ur_reclen  + 1, sep);
-		if (line_len == -ENODATA) {
-			break;
-		} else if (line_len == -EINVAL) {
-			ERR("Input line %i too long. Unit record length"
-			    " must not exceed %i\n", line, info->ur_reclen);
-			goto fail;
-		} else if (len < 0) {
-			ERR("Read failed: %s", strerror(errno));
-			goto fail;
-		}
-		line++;
-		memset(buf + line_len, pad, info->ur_reclen - line_len);
-		memcpy(&out_buf[pos], buf, info->ur_reclen);
-		pos += info->ur_reclen;
-	} while (pos < len);
-	free(buf);
-	return pos;
-fail:
-	free(buf);
-	return -1;
-}
-
-/*
- * Read file for punch/print
- */
-static int read_input_file(struct vmur *info, int fd, char *out_buf, size_t len)
-{
-	int rc;
-
-	if (info->text_specified) {
-		rc = read_text_file(info, fd, out_buf, len);
-	} else if (info->blocked_specified) {
-		rc = read_blocked_file(info, fd, out_buf, len);
-	} else {
-		rc = read(fd, out_buf, len);
-		if (rc == -1) {
-			ERR("Could not read file %s\n%s\n", info->file_name,
-			    strerror(errno));
-			return -EIO;
-		}
-	}
-	return rc;
-}
-
-/*
- * Write function for punch and printer
- */
-static void ur_write(struct vmur *info)
-{
-	int fhi, fho, residual, anything_written = 0;
-	char *sfdata;
-	ssize_t count;
-
-	acquire_lock(info);
-
-	/* close punch preventively */
-	close_ur_device_simple(info);
-
-	/* Check punch. If punch is spooled CONT, exit */
-	if (is_punch_cont(info))
-		ERR_EXIT("Virtual punch device %X is spooled CONT.\n",
-			 info->devno);
-
-	sfdata = malloc(info->ur_reclen * VMUR_REC_COUNT);
-	if (!sfdata)
-		ERR_EXIT("Could allocate memory for buffer (%i)\n",
-			    info->ur_reclen);
-
-	/* Open Linux file */
-	if (info->file_name_specified) {
-		fhi = open(info->file_name, O_RDONLY);
-		if (fhi == -1)
-			ERR_EXIT("Could not open file %s\n%s\n",
-				 info->file_name, strerror(errno));
-	} else {
-		fhi = STDIN_FILENO;
-	}
-
-	if (info->node_specified)
-		rscs_punch_setup(info);
-
-	/* Open UR device */
-	fho = open(info->devnode, O_WRONLY | O_NONBLOCK);
-	if (fho == -1) {
-		ERR("Could not open device %s\n%s\n", info->devnode,
-		    strerror(errno));
-		goto fail;
-	}
-
-	set_signal_handler(info, ur_write_sig_handler);
-
-	/* read linux file data, and write it to VM punch device */
-	do {
-		count = read_input_file(info, fhi, sfdata,
-					info->ur_reclen * VMUR_REC_COUNT);
-		if (count < 0)
-			goto fail;
-		else if (count == 0)
-			break; /* EOF */
-
-		residual = (info->ur_reclen - (count % info->ur_reclen))
-			% info->ur_reclen;
-		memset(sfdata + count, 0, residual);
-		if (write(fho, sfdata, count + residual) == -1) {
-			ERR("Could not write on device %s (%s)\n",
-			    info->devnode, strerror(errno));
-			if (errno == EIO)
-				ERR("Spool file limit exceeded or spool space "
-				    "full?\n");
-			goto fail;
-		} else
-			anything_written = 1;
-	} while (1);
-
-	close(fho);
-	if (anything_written)
-		close_ur_device(info);
-	else
-		ERR_EXIT("No spool file created - probably empty input.\n");
-	free(sfdata);
-	if (fhi != STDIN_FILENO)
-		close(fhi);
-	return;
-fail:
-	close_ur_device_purge(info);
-	exit(1);
-}
-
-/*
- * Ask if file should be purged, if -f is not specified
- */
-static void ur_purge_question(struct vmur *info)
-{
-	char buf[5] = {};
-	if (info->force_specified)
-		return;
-
-	if (info->spoolid_specified)
-		fprintf(stderr, "%s: purge %s %s? ", prog_name, info->queue,
-		       info->spoolid);
-	else
-		fprintf(stderr, "%s: purge all %s files? ", prog_name,
-			info->queue);
-	fgets(buf, sizeof(buf), stdin);
-	if (strcasecmp(buf, "y\n") == 0)
-		return;
-	if (strcasecmp(buf, "yes\n") == 0)
-		return;
-	exit(0);
-}
-
-/*
- * Purge spool file
- */
-static int ur_purge(struct vmur *info)
-{
-	char *buf, cmd[MAXCMDLEN];
-
-	ur_purge_question(info);
-	if (info->spoolid_specified)
-		sprintf(cmd, "PURGE * %s %s", info->queue, info->spoolid);
-	else
-		sprintf(cmd, "PURGE * %s ALL", info->queue);
-	acquire_lock(info);
-	cpcmd(cmd, &buf, NULL, 0);
-	ERR("%s", buf);
-	free(buf);
-	return 0;
-}
-
-/*
- * Order spool file to top of the queue
- */
-static int ur_order(struct vmur *info)
-{
-	char cmd[MAXCMDLEN];
-
-	sprintf(cmd, "ORDER * %s %s", info->queue, info->spoolid);
-	acquire_lock(info);
-	cpcmd(cmd, NULL, NULL, 0);
-	return 0;
-}
-
-/*
- * List spool files
- */
-static int ur_list(struct vmur *info)
-{
-	char *buf;
-	char cmd[MAXCMDLEN];
-
-	if (info->spoolid_specified)
-		sprintf(cmd, "QUERY %s * %s ALL", info->queue, info->spoolid);
-	else
-		sprintf(cmd, "QUERY %s * ALL", info->queue);
-
-	cpcmd(cmd, &buf, NULL, 1);
-	printf("%s", buf);
-	free(buf);
-	return 0;
-}
-
-/*
- * Initialize iconv: "from" -> "to"
- */
-static void setup_iconv(struct vmur *info, char *from, char *to)
-{
-	info->iconv = iconv_open(to, from);
-	if (info->iconv == ((iconv_t) -1))
-		ERR_EXIT("Could not initialize conversion table %s->%s.\n",
-			 from, to);
-}
-
-int main(int argc, char **argv)
-{
-	/* set name of program */
-	prog_name = basename(argv[0]);
-
-	/* set default values */
-	init_info(&vmur_info);
-
-	/* parse command line options and check syntax */
-	parse_opts(&vmur_info, argc, argv);
-
-	switch (vmur_info.action) {
-	case RECEIVE:
-		setup_ur_device(&vmur_info);
-		if (vmur_info.text_specified)
-			setup_iconv(&vmur_info, EBCDIC_CODE_PAGE,
-				    ASCII_CODE_PAGE);
-		ur_receive(&vmur_info);
-		break;
-	case PUNCH:
-	case PRINT:
-		if (vmur_info.text_specified)
-			setup_iconv(&vmur_info, ASCII_CODE_PAGE,
-				    EBCDIC_CODE_PAGE);
-		setup_ur_device(&vmur_info);
-		ur_write(&vmur_info);
-		break;
-	case PURGE:
-		ur_purge(&vmur_info);
-		break;
-	case ORDER:
-		ur_order(&vmur_info);
-		break;
-	case LIST:
-		ur_list(&vmur_info);
-		break;
-	default:
-		ERR("Internal error: unknown action '%i'\n", vmur_info.action);
-		return -EINVAL;
-	}
-	return 0;
-}
Index: s390-tools-sles11/vmur/vmur.cpp
===================================================================
--- /dev/null
+++ s390-tools-sles11/vmur/vmur.cpp
@@ -0,0 +1,2092 @@
+/*************************************************************************
+ *
+ * File...........: s390-tools/vmur/vmur.cpp
+ * Author(s)......: Frank Munzert <munzert@de.ibm.com>
+ *                  Michael Holzheu <holzheu@de.ibm.com>
+ *
+ * Copyright IBM Corp. 2007
+ *
+ *************************************************************************/
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <signal.h>
+#include <iconv.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <ctype.h>
+#include <linux/types.h>
+#include "zt_common.h"
+#include "vmur.h"
+#include "dump.h"
+
+/* Program name */
+static char *prog_name;
+
+/* Short description */
+static const char tool_name[] =
+	"vmur: Control virtual reader, punch and printer";
+
+/* Copyright notice */
+static const char copyright_notice[] = "Copyright IBM Corp. 2007";
+
+/*
+ * Structure vmur is used to store the command line parameters and
+ * other information needed at different places
+ */
+struct vmur {
+	char  spoolid[5];
+	int   spoolid_specified;
+	char  spoolfile_name[9];
+	int   spoolfile_name_specified;
+	char  spoolfile_type[9];
+	int   spoolfile_type_specified;
+	char  devnode[PATH_MAX];
+	int   devnode_specified;
+	char  node[9];
+	int   node_specified;
+	char  file_name[PATH_MAX];
+	int   file_name_specified;
+	char  queue[4];
+	int   queue_specified;
+	char  user[9];
+	int   user_specified;
+	int   blocked_separator;
+	int   blocked_padding;
+	int   blocked_specified;
+	int   rdr_specified;
+	int   text_specified;
+	int   force_specified;
+	int   stdout_specified;
+	int   hold_specified;
+	int   convert_specified;
+	enum  ur_action action;
+	int   devno;
+	int   ur_reclen;
+	int   file_reclen;
+	enum spoolfile_fmt spoolfile_fmt;
+	struct sigaction sigact;
+	iconv_t iconv;
+} vmur_info;
+
+/*
+ * Print version information.
+ */
+static void print_version (void)
+{
+	printf("%s version %s\n", tool_name, RELEASE_STRING);
+	printf("%s\n", copyright_notice);
+}
+
+/*
+ * Convert string to to_upper
+ */
+static char *to_upper(char *str)
+{
+	char *ptr = str;
+
+	while (*ptr) {
+		*ptr = toupper(*ptr);
+		ptr++;
+	}
+	return str;
+}
+
+/*
+ * Convert string to valid CP spool file name
+ */
+static char *to_valid_cpname(char *str)
+{
+	if (strlen(str) > 8)
+		str[8] = 0;
+
+	while (*str) {
+		if (!isprint(*str) || isspace(*str))
+			*str = '_';
+		str++;
+	}
+	return str;
+}
+
+/*
+ * Convert string to valid Linux file name
+ */
+static char *to_valid_linux_name(char *str)
+{
+	while (*str) {
+		if (*str == '/')
+			*str = '_';
+		str++;
+	}
+	return str;
+}
+
+/*
+ * Print out usage text
+ */
+
+static char HELP_TEXT[] =
+"Usage: vmur receive [OPTIONS] [SPOOLID] [FILE]\n"
+"       vmur punch   [OPTIONS] [FILE]\n"
+"       vmur print   [OPTIONS] [FILE]\n"
+"       vmur purge   [OPTIONS] [SPOOLID]\n"
+"       vmur order   [OPTIONS] [SPOOLID]\n"
+"       vmur list    [OPTIONS] [SPOOLID]\n"
+"\n"
+"Control virtual reader, punch and printer. Available commands are:\n"
+"  * REceive: Receive spool files from reader queue\n"
+"  * PUNch:   Punch a file to punch queue\n"
+"  * PRint:   Print a file to printer queue\n"
+"  * PURge:   Purge spool files\n"
+"  * ORder:   Order spool file\n"
+"  * LIst:    List spool files\n"
+"\n"
+"General options:\n"
+"\n"
+"-h, --help               Print this help, then exit.\n"
+"-v, --version            Print version information, then exit.\n"
+"\n"
+"Options of 'receive' command:\n"
+"\n"
+"-d, --device             Device node of the VM virtual reader.\n"
+"                         If omitted, /dev/vmrdr-0.0.000c is assumed.\n"
+"-t, --text               Indicates text data causing EBCDIC to ASCII\n"
+"                         conversion.\n"
+"-b, --blocked            Use blocked mode.\n"
+"-c, --convert            Specifies to convert VMDUMP file into a format\n"
+"                         appropriate for further analysis with (l)crash.\n"
+"-O, --stdout             Write spool file to stdout.\n"
+"-f, --force              Overwrite files without prompt.\n"
+"-H, --hold               Hold spool file in reader after receive.\n"
+"\n"
+"Options for 'punch' and 'print' command:\n"
+"\n"
+"-d, --device             Device node of the VM virtual punch or printer.\n"
+"                         If omitted, /dev/vmpun-0.0.000d or\n"
+"                         /dev/vmprt-0.0.000e is assumed, respectively.\n"
+"-t, --text               Indicates text data causing ASCII to EBCDIC\n"
+"                         conversion.\n"
+"-b, --blocked            Use blocked mode.\n"
+"-r, --rdr                Indicates to transfer file from punch/printer\n"
+"                         to reader.\n"
+"-u, --user               Transfer file to user's reader.\n"
+"                         If omitted: Your guest machine's reader.\n"
+"-n, --node               Remote node to send the file to.\n"
+"                         If omitted: Your local VM node.\n"
+"-N, --name               Name of new spool file.\n"
+"-f, --force              Convert file name to valid spool file name\n"
+"                         automatically without prompt.\n"
+"\n"
+"Options for 'purge' command:\n"
+"\n"
+"-f, --force              Purge without prompt.\n"
+"\n"
+"Options for 'order', 'list', and 'purge' commands:\n"
+"\n"
+"-q, --queue              Target queue for command. Possible queues are:\n"
+"                         'rdr' (default), 'pun' and 'prt'.\n";
+
+static void usage(void)
+{
+	printf(HELP_TEXT);
+}
+
+/*
+ * Signal handler
+ */
+static void set_signal_handler(struct vmur *info,
+			       void (*handler) (int, siginfo_t *, void *))
+{
+	info->sigact.sa_flags = (SA_NODEFER | SA_SIGINFO | SA_RESETHAND);
+	info->sigact.sa_sigaction = handler;
+
+	if (sigemptyset(&info->sigact.sa_mask) < 0)
+		goto fail;
+	if (sigaction(SIGINT, &info->sigact, NULL) < 0)
+		goto fail;
+	if (sigaction(SIGTERM, &info->sigact, NULL) < 0)
+		goto fail;
+
+	return;
+fail:
+	ERR_EXIT("Could not initialize signal handler (errno = %i)\n", errno);
+}
+
+/*
+ * Read at most COUNT bytes from FD into memory at location BUF.
+ * Return number of bytes read on success, -1 on error.
+ */
+static ssize_t read_buf(int fd, char *buf, ssize_t count)
+{
+	ssize_t rc, len;
+
+	for (len = 0; len < count; len += rc) {
+		rc = read(fd, &buf[len], count - len);
+		if (rc == -1)
+			return -1;
+		if (rc == 0)
+			break;
+	}
+	return len;
+}
+
+/*
+ * Strip leading CP header from error message
+ */
+static void strip_cperr(char *buf)
+{
+	unsigned int i;
+
+	for (i = 0; i < strlen(buf) - CP_PREFIX_LEN; i++) {
+		if (strncmp(buf + i, "HCP", 3) == 0) {
+			int offs = i + CP_PREFIX_LEN;
+			memmove(buf, buf + offs, strlen(buf) - offs);
+			/* terminate string and remove newline */
+			buf[strlen(buf) - offs - 1] = 0;
+			return;
+		}
+	}
+}
+
+static void _cpcmd(char *cpcmd, char **resp, int *rc, int retry, int upper)
+{
+	int fd, len, cprc, bufsize = VMCP_BUFSIZE;
+	char *buf;
+	char cmd[MAXCMDLEN];
+
+	strcpy(cmd, cpcmd);
+	if (upper)
+		to_upper(cmd);
+
+	fd = open(VMCP_DEVICE_NODE, O_RDWR);
+	if (fd == -1)
+		ERR_EXIT("Could not issue cp command: \"%s\"\n"
+			 "Ensure that vmcp kernel module is loaded!\n", cmd);
+
+	do {
+		if (ioctl(fd, VMCP_SETBUF, &bufsize) == -1)
+			goto fail;
+
+		if (write(fd, cmd, strlen(cmd)) == -1)
+			goto fail;
+
+		if (ioctl(fd, VMCP_GETCODE, &cprc) == -1)
+			goto fail;
+
+		if (ioctl(fd, VMCP_GETSIZE, &len) == -1)
+			goto fail;
+
+		if (len <= bufsize)
+			break;
+		else if (retry)
+			bufsize = len;
+		else
+			ERR_EXIT("Not enough buffer space (%i/%i) for CP "
+				 "command '%s'.\nSorry, please issue command "
+				 "on your 3270 console!\n", len, bufsize, cmd);
+	} while (1);
+
+	buf = (char *) malloc(len + 1);
+	if (!buf)
+		ERR_EXIT("Out of memory for CP command '%s'\n", cmd);
+
+	memset(buf, 0, len + 1);
+	if (read_buf(fd, buf, len) == -1)
+		goto fail;
+
+	if (rc == NULL) {
+		if (cprc != 0) {
+			/* caller wants us to handle the error */
+			strip_cperr(buf);
+			ERR_EXIT("%s\n", buf);
+		}
+	} else {
+		*rc = cprc;
+	}
+
+	if (resp)
+		*resp = buf;
+	else
+		free(buf);
+	close(fd);
+	return;
+
+fail:
+	ERR_EXIT("CP command '%s' failed.\n", cmd);
+}
+
+/*
+ * Issue   CP command:
+ * @cpcmd: CP command to be issued.
+ * @resp:  CP command response on success, an error message on error.
+ * @rc:    CP return code.
+ * @retry: retry = 1 -> CP command can be retried.
+ * This function converts the command string to uppercase.
+ */
+static void cpcmd(char *cpcmd, char **resp, int *rc, int retry)
+{
+	_cpcmd(cpcmd, resp, rc, retry, 1);
+}
+
+/*
+ * Issue   CP command:
+ * @cpcmd: CP command to be issued.
+ * @resp:  CP command response on success, an error message on error.
+ * @rc:    CP return code.
+ * @retry: retry = 1 -> CP command can be retried.
+ * This function does not convert the command string to uppercase.
+ */
+static void cpcmd_cs(char *cpcmd, char **resp, int *rc, int retry)
+{
+	_cpcmd(cpcmd, resp, rc, retry, 0);
+}
+
+/*
+ * Extract minor from sysfs file
+ */
+static int get_minor(char *path)
+{
+	FILE *fd;
+	char buf[20];
+	int major, minor, rc;
+
+	fd = fopen(path, "r");
+	if (!fd)
+		ERR_EXIT("Could not open %s (err = %i)\n", path, errno);
+	rc = fread(buf, sizeof(buf), 1, fd);
+	if (rc == -1)
+		ERR_EXIT("Could not read %s (err = %i)\n", path, errno);
+	fclose(fd);
+
+	if (sscanf(buf, "%i:%i", &major, &minor) != 2)
+		ERR_EXIT("Malformed content of %s: %s\n", path, buf);
+
+	return minor;
+}
+
+/*
+ * Find device number of ur device
+ */
+static int get_ur_devno(int minor)
+{
+	struct dirent *direntp;
+	char dev_file[PATH_MAX];
+	DIR *fd;
+	char bus_id[9] = {};
+	int devno;
+
+	errno = 0;
+	fd = opendir(SYSFS_CLASS_DIR);
+	if (!fd)
+		ERR_EXIT("Could not open %s (err = %i)\n", SYSFS_CLASS_DIR,
+			 errno);
+	while ((direntp = readdir(fd))) {
+		if (strcmp(direntp->d_name, ".") == 0)
+			continue;
+		if (strcmp(direntp->d_name, "..") == 0)
+			continue;
+		sprintf(dev_file, "%s/%s/dev", SYSFS_CLASS_DIR,
+			direntp->d_name);
+		if (get_minor(dev_file) == minor) {
+			/* extract device id from <vmxxx-0.0.yyyy> */
+			memcpy(bus_id, &direntp->d_name[6], 8);
+			goto found;
+		}
+	};
+	if (errno != 0)
+		ERR_EXIT("Could not read %s (err = %i)\n",
+			    SYSFS_CLASS_DIR, errno);
+	else
+		ERR_EXIT("Device not online\n");
+found:
+	if (sscanf(bus_id, "0.0.%x", &devno) != 1)
+		ERR_EXIT("Could not extract devno from %s\n", direntp->d_name);
+	closedir(fd);
+	return devno;
+}
+
+/*
+ * Extract major/minor from device node
+ */
+static dev_t get_node_dev(char *devnode)
+{
+	struct stat stat_info;
+
+	if (stat(devnode, &stat_info)) {
+		ERR("Unable to get status for '%s': %s\n", devnode,
+		    strerror(errno));
+		ERR_EXIT("Please check if device is online!\n");
+	}
+
+	return stat_info.st_rdev;
+}
+
+/*
+ * Extract major of vmur driver
+ */
+static int get_driver_major(unsigned long *major)
+{
+	FILE *fh;
+	char string[PROC_DEVICES_FILE_WIDTH];
+	char last_string[PROC_DEVICES_FILE_WIDTH];
+
+	fh = fopen(PROC_DEVICES_FILE, "r");
+	if (!fh) {
+		ERR("WARNING: Cannot check for vmur in file %s.\n%s\n",
+		    PROC_DEVICES_FILE, strerror(errno));
+		return -1; /* check not possible, just continue */
+	}
+	while (fscanf(fh, "%s", string) != EOF) {
+		if (strcmp(string, "vmur") == 0) {
+			fclose(fh);
+			*major = atoi(last_string);
+			return 0;
+		} else {
+			strcpy(last_string, string);
+		}
+	}
+	fclose(fh);
+	ERR_EXIT("Unit record device driver not loaded.\n");
+}
+
+/*
+ * Setup and check ur device
+ */
+static void setup_ur_device(struct vmur *info)
+{
+	unsigned long driver_major;
+	dev_t node_dev;
+	int rc;
+
+	node_dev = get_node_dev(info->devnode);
+	rc = get_driver_major(&driver_major);
+	if ((rc == 0) && (driver_major != major(node_dev)))
+		ERR_EXIT("'%s' is not a unit record device.\n",
+			 info->devnode);
+	info->devno = get_ur_devno(minor(node_dev));
+}
+
+/*
+ * initialize the vmur info structure
+ */
+static void init_info(struct vmur *info)
+{
+	memset(info, 0, sizeof(struct vmur));
+	strcpy(info->queue, "rdr");
+}
+
+/*
+ * set positional spoolid parameter
+ */
+static void set_spoolid(struct vmur *info, char **argv, int argc, int optind,
+			int mandatory)
+{
+	char *str;
+
+	if (argc <= optind) {
+		if (mandatory)
+			ERR_EXIT("No spool id specified.\n");
+		else
+			return;
+	}
+
+	if ((argc > optind + 1) && (info->action != RECEIVE))
+		ERR_EXIT("More than one spool id specified.\n");
+
+	str = argv[optind];
+
+	if (strlen(str) > 4)
+		goto invalid;
+
+	while (*str) {
+		if (!isdigit(*str))
+			goto invalid;
+		str++;
+	}
+
+	strcpy(info->spoolid, argv[optind]);
+	++info->spoolid_specified;
+	return;
+
+invalid:
+	ERR_EXIT("Spoolid must be a decimal number in range 0-9999\n");
+}
+
+/*
+ * set positional file parameter
+ */
+static void set_file(struct vmur *info, char **argv, int argc, int optind)
+{
+	if (argc <= optind)
+		return;
+
+	if (argc > optind + 1)
+		ERR_EXIT("More than one file specified.\n");
+
+	strncpy(info->file_name, argv[optind], sizeof(info->file_name) - 1);
+	++info->file_name_specified;
+}
+
+/*
+ * Set queue: rdr, pun or prt
+ */
+static void set_queue(struct vmur *info, char *queue)
+{
+	if (strcmp(queue, "rdr") == 0)
+		strcpy(info->queue, "rdr");
+	else if (strcmp(queue, "pun") == 0)
+		strcpy(info->queue, "pun");
+	else if (strcmp(queue, "prt") == 0)
+		strcpy(info->queue, "prt");
+	else
+		ERR_EXIT("Invalid queue: %s\n", queue);
+	return;
+}
+
+static void set_blocked(struct vmur *info, char *blocked)
+{
+	if (sscanf(blocked, "0x%x,0x%x", &info->blocked_separator,
+		   &info->blocked_padding) != 2)
+		goto fail;
+	if (info->blocked_separator > 255)
+		goto fail;
+	if (info->blocked_padding > 255)
+		goto fail;
+	++info->blocked_specified;
+	return;
+fail:
+	ERR_EXIT("Invalid blocked parameter. It must have the format "
+		 "'0xSS,0xPP'.\n");
+}
+
+/*
+ * Parse the command line: General options
+ */
+static void check_std_opts(int opt)
+{
+	switch (opt) {
+	case 'v':
+		print_version();
+		exit(0);
+	case 'h':
+		usage();
+		exit(0);
+	}
+}
+
+static void std_usage_exit(void)
+{
+	fprintf(stderr, "Try '%s --help' for more information.\n", prog_name);
+	exit(1);
+}
+
+/*
+ * Parse the command line: Receive command
+ */
+static void parse_opts_receive(struct vmur *info, int argc, char *argv[])
+{
+	int opt, index;
+	static struct option long_options[] = {
+		{ "version",     no_argument,       NULL, 'v'},
+		{ "help",        no_argument,       NULL, 'h'},
+		{ "text",        no_argument,       NULL, 't'},
+		{ "stdout",      no_argument,       NULL, 'O'},
+		{ "force",       no_argument,       NULL, 'f'},
+		{ "hold",        no_argument,       NULL, 'H'},
+		{ "convert",     no_argument,       NULL, 'c'},
+		{ "device",      required_argument, NULL, 'd'},
+		{ "blocked",     required_argument, NULL, 'b'},
+		{ 0,             0,                 0,    0  }
+	};
+	static const char option_string[] = "vhtOfHcd:b:";
+
+	strcpy(info->devnode, VMRDR_DEVICE_NODE);
+	while (1) {
+		opt = getopt_long(argc, argv, option_string,
+				  long_options, &index);
+		if (opt == -1)
+			break;
+		check_std_opts(opt);
+		switch (opt) {
+		case 't':
+			++info->text_specified;
+			break;
+		case 'd':
+			++info->devnode_specified;
+			strncpy(info->devnode, optarg,
+				sizeof(info->devnode) - 1);
+			break;
+		case 'O':
+			++info->stdout_specified;
+			break;
+		case 'b':
+			set_blocked(info, optarg);
+			break;
+		case 'f':
+			++info->force_specified;
+			break;
+		case 'H':
+			++info->hold_specified;
+			break;
+		case 'c':
+			++info->convert_specified;
+			break;
+		default:
+			std_usage_exit();
+		}
+	}
+
+	set_spoolid(info, argv, argc, optind + 1, 1);
+	set_file(info, argv, argc, optind + 2);
+
+	CHECK_SPEC_MAX(info->text_specified, 1, "text");
+	CHECK_SPEC_MAX(info->devnode_specified, 1, "devnode");
+	CHECK_SPEC_MAX(info->file_name_specified, 1, "file");
+	CHECK_SPEC_MAX(info->blocked_specified, 1, "blocked");
+	CHECK_SPEC_MAX(info->force_specified, 1, "force");
+	CHECK_SPEC_MAX(info->hold_specified, 1, "hold");
+	CHECK_SPEC_MAX(info->stdout_specified, 1, "stdout");
+	CHECK_SPEC_MAX(info->convert_specified, 1, "convert");
+
+	if (info->stdout_specified && info->file_name_specified)
+		ERR_EXIT("File name not allowed, when --stdout specifed!\n");
+	if (info->blocked_specified + info->text_specified +
+	    info->convert_specified > 1)
+		ERR_EXIT("Conflicting options: -b, -t and -c are mutually "
+			 "exclusive.\n");
+}
+
+/*
+ * Validate VM userID
+ */
+static int invalid_userid(char *operand)
+{
+	if (strlen(operand) > 8)
+		return 1;
+	while (*operand) {
+		if (!isalnum(*operand) &&
+		    (strchr("@#$_-", *operand) == NULL))
+			return 1;
+		operand++;
+	}
+	return 0;
+}
+
+/*
+ * Validate CP command operand such as nodeID or name/type
+ */
+static int invalid_operand(char *operand)
+{
+	if (strlen(operand) > 8)
+		return 1;
+	while (*operand) {
+		if (!isprint(*operand) || isspace(*operand))
+			return 1;
+		operand++;
+	}
+	return 0;
+}
+
+/*
+ * Check whether user is in the CP directory
+ */
+static int check_local_user(const char *user)
+{
+	char cmd[MAXCMDLEN];
+	int cprc;
+
+	strcpy(cmd, "LINK ");
+	strcat(cmd, user);
+	cpcmd(cmd, NULL, &cprc, 0);
+	if ((cprc == 53)	/* user not in CP directory */
+	    || (cprc == 20))	/* Userid missing or invalid */
+		return 1;
+	else
+		return 0;
+}
+
+/*
+ * Set spool file name/type in vmur structure and validate name/type
+ */
+static void set_spoolfile_name(struct vmur *info, char *name)
+{
+	char spoolfile_name[PATH_MAX], spoolfile_type[PATH_MAX] = {};
+	int i, flag = 0;
+
+	strcpy(spoolfile_name, name);
+
+	if (strlen(spoolfile_name) == 0)
+		ERR_EXIT("Empty spool file name is invalid\n");
+
+	/* check for period delimiting name from type */
+	if (spoolfile_name[strlen(spoolfile_name) - 1] == '.')
+		flag = 1; /* name/type string ends with period */
+	for (i = strlen(spoolfile_name) - 1; i > 0; i--) {
+		if (spoolfile_name[i] == '.') {
+			if (flag && (spoolfile_name[i-1] == '.'))
+				continue;
+			else
+				break;
+		}
+	}
+	if ((i > 0) && (i < (int) strlen(spoolfile_name) - 1)) {
+		strcpy(spoolfile_type, spoolfile_name + i + 1);
+		spoolfile_name[i] = 0;
+		++info->spoolfile_type_specified;
+	}
+
+	if (info->force_specified) {
+		/* adjust spool file name, in order to have a valid one */
+		to_valid_cpname(spoolfile_name);
+		if (info->spoolfile_type_specified)
+			to_valid_cpname(spoolfile_type);
+		goto out;
+	}
+	if (invalid_operand(spoolfile_name) || invalid_operand(spoolfile_type))
+		goto invalid;
+out:
+	strcpy(info->spoolfile_name, spoolfile_name),
+	++info->spoolfile_name_specified;
+	if (info->spoolfile_type_specified)
+		strcpy(info->spoolfile_type, spoolfile_type);
+	return;
+
+invalid:
+	ERR_EXIT("Malformed spool file name: %s\n"
+		 "Specify --force, if the name should be converted "
+		 "automatically.\n", name);
+}
+
+/*
+ * Parse the command line: punch command
+ */
+static void parse_opts_punch_print(struct vmur *info, int argc, char *argv[])
+{
+	int opt, index;
+	char *spoolfile_name = NULL;
+	static struct option long_options[] = {
+		{ "version",     no_argument,       NULL, 'v'},
+		{ "help",        no_argument,       NULL, 'h'},
+		{ "text",        no_argument,       NULL, 't'},
+		{ "rdr",         no_argument,       NULL, 'r'},
+		{ "force",       no_argument,       NULL, 'f'},
+		{ "user",        required_argument, NULL, 'u'},
+		{ "node",        required_argument, NULL, 'n'},
+		{ "device",      required_argument, NULL, 's'},
+		{ "blocked",     required_argument, NULL, 'b'},
+		{ "name",        required_argument, NULL, 'N'},
+		{ 0,             0,                 0,    0  }
+	};
+	static const char option_string[] = "vhtrfu:n:d:b:N:";
+
+	if (info->action == PUNCH) {
+		strcpy(info->devnode, VMPUN_DEVICE_NODE);
+		info->ur_reclen = VMPUN_RECLEN;
+	} else {
+		strcpy(info->devnode, VMPRT_DEVICE_NODE);
+		info->ur_reclen = VMPRT_RECLEN;
+	}
+
+	while (1) {
+		opt = getopt_long(argc, argv, option_string,
+				  long_options, &index);
+		if (opt == -1)
+			break;
+		check_std_opts(opt);
+		switch (opt) {
+		case 'd':
+			++info->devnode_specified;
+			strcpy(info->devnode, optarg);
+			break;
+		case 'N':
+			++info->spoolfile_name_specified;
+			spoolfile_name = optarg;
+			break;
+		case 'r':
+			++info->rdr_specified;
+			break;
+		case 'f':
+			++info->force_specified;
+			break;
+		case 't':
+			++info->text_specified;
+			break;
+		case 'u':
+			++info->user_specified;
+			if (invalid_userid(optarg))
+				ERR_EXIT("Invalid userid: %s\n", optarg);
+			else
+				strcpy(info->user, optarg);
+			break;
+		case 'b':
+			set_blocked(info, optarg);
+			break;
+		case 'n':
+			++info->node_specified;
+			if (invalid_operand(optarg))
+				ERR_EXIT("Invalid node specified.\n");
+			else
+				strcpy(info->node, optarg);
+			break;
+		default:
+			std_usage_exit();
+		}
+	}
+
+	CHECK_SPEC_MAX(info->rdr_specified, 1, "rdr");
+	CHECK_SPEC_MAX(info->force_specified, 1, "force");
+	CHECK_SPEC_MAX(info->user_specified, 1, "user");
+	CHECK_SPEC_MAX(info->file_name_specified, 1, "file");
+	CHECK_SPEC_MAX(info->spoolfile_name_specified, 1, "name");
+	CHECK_SPEC_MAX(info->user_specified, 1, "user");
+	CHECK_SPEC_MAX(info->node_specified, 1, "node");
+	CHECK_SPEC_MAX(info->blocked_specified, 1, "blocked");
+
+	if (info->user_specified && !info->rdr_specified)
+		ERR_EXIT("--user without --rdr specified\n");
+	if (info->node_specified && !info->user_specified)
+		ERR_EXIT("--node without --user specified\n");
+	if ((info->user_specified && !info->node_specified)
+	    && check_local_user(info->user))
+		ERR_EXIT("Invalid userid: %s\n", info->user);
+	if (info->node_specified && check_local_user(RSCS_USERID))
+		ERR_EXIT("Invalid RSCS userid: %s\n", info->node);
+	if (info->blocked_specified && info->text_specified)
+		ERR_EXIT("Conflicting options: -b together with -t "
+			 "specified\n");
+
+	set_file(info, argv, argc, optind + 1);
+
+	if (info->spoolfile_name_specified)
+		set_spoolfile_name(info, spoolfile_name);
+	else if (info->file_name_specified)
+		set_spoolfile_name(info, basename(info->file_name));
+	else
+		ERR_EXIT("No name for spool file specified!\n");
+}
+
+/*
+ * Parse the command line: Purge command
+ */
+static void parse_opts_purge(struct vmur *info, int argc, char *argv[])
+{
+	int opt, index;
+	static struct option long_options[] = {
+		{ "version",     no_argument,       NULL, 'v'},
+		{ "help",        no_argument,       NULL, 'h'},
+		{ "force",       no_argument,       NULL, 'f'},
+		{ "queue",       no_argument,       NULL, 'q'},
+		{ 0,             0,                 0,    0  }
+	};
+	static const char option_string[] = "fvhq:";
+
+	while (1) {
+		opt = getopt_long(argc, argv, option_string,
+				  long_options, &index);
+		if (opt == -1)
+			break;
+		check_std_opts(opt);
+		switch (opt) {
+		case 'f':
+			++info->force_specified;
+			break;
+		case 'q':
+			++info->queue_specified;
+			set_queue(info, optarg);
+			break;
+		default:
+			std_usage_exit();
+		}
+	}
+	CHECK_SPEC_MAX(info->force_specified, 1, "force");
+	CHECK_SPEC_MAX(info->queue_specified, 1, "queue");
+	set_spoolid(info, argv, argc, optind + 1, 0);
+}
+
+/*
+ * Parse the command line: Order command
+ */
+static void parse_opts_order(struct vmur *info, int argc, char *argv[])
+{
+	int opt, index;
+	static struct option long_options[] = {
+		{ "version",     no_argument,       NULL, 'v'},
+		{ "help",        no_argument,       NULL, 'h'},
+		{ "queue",       required_argument, NULL, 'q'},
+		{ 0,             0,                 0,    0  }
+	};
+	static const char option_string[] = "vhq:";
+
+	while (1) {
+		opt = getopt_long(argc, argv, option_string,
+				  long_options, &index);
+		if (opt == -1)
+			break;
+		check_std_opts(opt);
+		switch (opt) {
+		case 'q':
+			++info->queue_specified;
+			set_queue(info, optarg);
+			break;
+		default:
+			std_usage_exit();
+		}
+	}
+	CHECK_SPEC_MAX(info->queue_specified, 1, "queue");
+	set_spoolid(info, argv, argc, optind + 1, 1);
+}
+
+/*
+ * Parse the command line: List command
+ */
+static void parse_opts_list(struct vmur *info, int argc, char *argv[])
+{
+	int opt, index;
+	static struct option long_options[] = {
+		{ "version",     no_argument,       NULL, 'v'},
+		{ "help",        no_argument,       NULL, 'h'},
+		{ "queue",       required_argument, NULL, 'q'},
+		{ 0,             0,                 0,    0  }
+	};
+	static const char option_string[] = "vhq:";
+
+	while (1) {
+		opt = getopt_long(argc, argv, option_string,
+				  long_options, &index);
+		if (opt == -1)
+			break;
+		check_std_opts(opt);
+		switch (opt) {
+		case 'q':
+			++info->queue_specified;
+			set_queue(info, optarg);
+			break;
+		default:
+			std_usage_exit();
+		}
+	}
+	CHECK_SPEC_MAX(info->queue_specified, 1, "queue");
+	set_spoolid(info, argv, argc, optind + 1, 0);
+}
+
+/*
+ * Parse the command line: Default options
+ */
+static void parse_opts_default(struct vmur *info, int argc, char *argv[])
+{
+	int opt, index;
+	static struct option long_options[] = {
+		{ "version",     no_argument,       NULL, 'v'},
+		{ "help",        no_argument,       NULL, 'h'},
+		{ 0,             0,                 0,    0  }
+	};
+	static const char option_string[] = "vh";
+
+	while (1) {
+		opt = getopt_long(argc, argv, option_string,
+				  long_options, &index);
+		if (opt == -1)
+			break;
+		check_std_opts(opt);
+		std_usage_exit();
+	}
+}
+
+/*
+ * Parse action strings
+ */
+static int is_action(enum ur_action action, char *str)
+{
+	char action_str[80] = {};
+
+	if (strlen(str) < ur_action_prefix_len[action])
+		return 0;
+	if (strlen(str) > strlen(ur_action_str[action]))
+		return 0;
+
+	strncpy(action_str, ur_action_str[action], strlen(str));
+
+	if (strcasecmp(str, action_str) != 0)
+		return 0;
+
+	return 1;
+}
+
+/*
+ * Get action string and set action field in info structure
+ */
+static int set_action(struct vmur *info, char *str)
+{
+	int action;
+
+	for (action = 0; action < LAST; action++) {
+		if (is_action((enum ur_action) action, str)) {
+			info->action = (enum ur_action) action;
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+/*
+ * The toplevel parameter parsing function
+ */
+static void parse_opts(struct vmur *info, int argc, char *argv[])
+{
+	if (argc == 1)
+		ERR_EXIT("Missing command\n");
+
+	if (set_action(info, argv[1])) {
+		parse_opts_default(info, argc, argv);
+		ERR("Unknown command '%s'\n", argv[1]);
+		std_usage_exit();
+	}
+
+	switch (info->action) {
+	case RECEIVE:
+		return parse_opts_receive(info, argc, argv);
+	case PUNCH:
+		return parse_opts_punch_print(info, argc, argv);
+	case PRINT:
+		return parse_opts_punch_print(info, argc, argv);
+	case PURGE:
+		return parse_opts_purge(info, argc, argv);
+	case ORDER:
+		return parse_opts_order(info, argc, argv);
+	case LIST:
+		return parse_opts_list(info, argc, argv);
+	default:
+		ERR_EXIT("Internal error. Unknown action: %i\n", info->action);
+	}
+}
+
+/*
+ * Check if spool file has hold state "NONE"
+ */
+static void check_hold_state(char *spoolid)
+{
+	char cmd[MAXCMDLEN];
+	char *response;
+
+	sprintf(cmd, "QUERY READER * %s ALL SHORTDATE", spoolid);
+	cpcmd(cmd, &response, 0, 0);
+	response[114] = 0;
+	if (strcmp(&response[110], "NONE") != 0)
+		ERR_EXIT("Could not receive spool file %s: hold state = %s\n",
+			 spoolid, &response[110]);
+	free(response);
+}
+
+/*
+ * Issue CP command ORDER RDR <spoolid>.
+ * If ORDER fails, the CP command response is as follows:
+ *      NO FILES ORDERED
+ * HCPxxxnnnt <message text>
+ * If ORDER returns successfully, CP command CHANGE RDR <spoolid> NOHOLD
+ * is issued.
+ */
+static void order_change_reader_file(struct vmur *info)
+{
+	char cmd[MAXCMDLEN];
+
+	sprintf(cmd, "ORDER * READER %s", info->spoolid);
+	cpcmd(cmd, NULL, NULL, 0);
+	sprintf(cmd, "CHANGE * READER %s NOHOLD", info->spoolid);
+	cpcmd(cmd, NULL, NULL, 0);
+
+	check_hold_state(info->spoolid);
+}
+
+/*
+ * Issue CP command CLOSE RDR
+ */
+static void close_reader(struct vmur *info, const char *hold)
+{
+	char cmd[MAXCMDLEN];
+
+	sprintf(cmd, "CLOSE %X %s", info->devno, hold);
+	cpcmd(cmd, NULL, NULL, 0);
+}
+
+/*
+ * strip leading and trailing blanks
+ */
+static char *strstrip(char *s)
+{
+	size_t size;
+	char *end;
+
+	size = strlen(s);
+
+	if (!size)
+		return s;
+
+	end = s + size - 1;
+	while (end >= s && isspace(*end))
+		end--;
+	*(end + 1) = '\0';
+
+	while (*s && isspace(*s))
+		s++;
+
+	return s;
+}
+
+/*
+ *  Check whether reader can handle class of reader spool file
+ */
+static int check_class(struct vmur *info)
+{
+	char cmd[MAXCMDLEN];
+	char device_class, file_class;
+	char *buf;
+
+	sprintf(cmd, "QUERY VIRTUAL %X", info->devno);
+	cpcmd(cmd, &buf, NULL, 0);
+	device_class = buf[13];
+	free(buf);
+	if (device_class == '*') /* reader device can handle any class */
+		return 0;
+
+	sprintf(cmd, "QUERY RDR * %s ALL SHORTDATE", info->spoolid);
+	cpcmd(cmd, &buf, NULL, 0);
+	file_class = buf[91];
+	free(buf);
+	if (file_class != device_class)
+		return 1;
+	return 0;
+}
+
+/*
+ * Get filename for spoolid from reader
+ */
+static int get_filename_from_reader(struct vmur *info)
+{
+	char cmd[MAXCMDLEN];
+	char name[9] = {0};
+	char type[9] = {0};
+	char *buf;
+
+	sprintf(cmd, "QUERY RDR * %s ALL SHORTDATE", info->spoolid);
+	cpcmd(cmd, &buf, NULL, 0);
+	memcpy(name, &buf[130], 8);
+	strstrip(name);
+	memcpy(type, &buf[140], 8);
+	strstrip(type);
+	free(buf);
+	if (strlen(name) == 0) {
+		ERR("Please specify name to receive "
+		    "unnamed spool file: %s\n", info->spoolid);
+		return 1;
+	}
+
+	to_valid_linux_name(name);
+	to_valid_linux_name(type);
+
+	if (strlen(type) == 0)
+		strcpy(info->file_name, name);
+	else
+		sprintf(info->file_name, "%s.%s", name, type);
+	info->file_name_specified = 1;
+	return 0;
+}
+
+/*
+ * Serialize access to unit record devices and their related spool file queues:
+ * open lock file and apply lock
+ */
+static void acquire_lock(struct vmur *info)
+{
+	int fd;
+	char failed_action[10] = {};
+
+	fd = open(LOCK_FILE, O_RDONLY | O_CREAT, S_IRUSR);
+	if (fd == -1) {
+		ERR("WARNING: Unable to open lock file %s, continuing "
+		    "without any serialization.\n", LOCK_FILE);
+		return;
+	}
+	if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
+		switch (info->action) {
+		case RECEIVE:
+			strcpy(failed_action, "received");
+			break;
+		case PUNCH:
+			strcpy(failed_action, "punched");
+			break;
+		case PRINT:
+			strcpy(failed_action, "printed");
+			break;
+		case PURGE:
+			strcpy(failed_action, "purged");
+			break;
+		case ORDER:
+			strcpy(failed_action, "ordered");
+			break;
+		default:
+			strcpy(failed_action, "processed");
+		}
+		ERR("A concurrent instance of vmur is already active."
+		    " No file %s.\n", failed_action);
+		exit(EBUSY);
+	}
+}
+
+/*
+ * Check if file already exists. If yes, ask if it should be overwritten.
+ */
+static void check_overwrite(struct vmur *info)
+{
+	char buf[5] = {};
+	char *str;
+	struct stat stat_info;
+
+	if (info->force_specified)
+		return;
+
+	if (stat(info->file_name, &stat_info))
+		return;
+
+	if (S_ISDIR(stat_info.st_mode)) {
+		ERR("Cannot overwrite directory '%s'\n", info->file_name);
+		exit(1);
+	}
+
+	fprintf(stderr, "%s: Overwrite '%s'? ", prog_name, info->file_name);
+	str = fgets(buf, sizeof(buf), stdin);
+	if (!str)
+		exit(1);
+	if (strcasecmp(buf, "y\n") == 0)
+		return;
+	if (strcasecmp(buf, "yes\n") == 0)
+		return;
+	close_reader(info, "HOLD");
+	exit(0);
+}
+
+/*
+ * Get format of spool file: Check VMDUMP and NETDATA flags,
+ *                           set record length of file
+ */
+enum spoolfile_fmt get_spoolfile_fmt(struct vmur *info,
+				     struct splink_page *buf)
+{
+	struct splink_record *rec;
+	char netdata_id[5] = {0xc9, 0xd5, 0xd4, 0xd9, 0xf0}; /* EBCDIC: INMR0 */
+	unsigned int i;
+
+	rec = (struct splink_record *) &buf->data;
+
+	if (buf->magic != 0)
+		return TYPE_VMDUMP;
+
+	info->file_reclen = buf->rec_len;
+
+	for (i = 0; i < buf->data_recs; i++) {
+		if (rec->ccw.flag & CCW_IMMED_FLAG) {
+			rec = (struct splink_record *) ((char *) rec +
+							sizeof(rec->ccw));
+			continue; /* skip immediate CCWs */
+		}
+		if ((rec->data.flag & IS_CONTROL_RECORD) &&
+		    (memcmp(rec->data.magic, netdata_id, 5) == 0))
+			return TYPE_NETDATA;
+		rec = (struct splink_record *) ((char *) rec + rec->record_len);
+	}
+
+	return TYPE_NORMAL;
+}
+
+/*
+ * Convert record for texmode: Do EBCDIC->ASCII translation
+ */
+static int convert_text(struct vmur *info, struct splink_record *rec,
+			char **out_ptr)
+{
+	size_t in_count = rec->ccw.data_len;
+	size_t out_count = rec->ccw.data_len;
+	char *data_ptr = (char *) &rec->data;
+	int rc;
+
+	if ((rec->ccw.data_len == 1) && (data_ptr[0] == 0x40))
+		goto out; /* one blank -> just a newline */
+
+	rc = iconv(info->iconv, &data_ptr, &in_count, out_ptr, &out_count);
+	if ((rc == -1) || (in_count != 0)) {
+		ERR("Code page translation EBCDIC-ASCII failed\n");
+		return -1;
+	}
+
+out:
+	**out_ptr = ASCII_LF;
+	*out_ptr += 1;
+	return 0;
+}
+
+/*
+ * Convert record for binary mode: Fill up missing 0x40 bytes
+ */
+static void convert_binary(struct vmur *info, struct splink_record *rec,
+			  char **out_ptr)
+{
+	int residual;
+
+	memcpy(*out_ptr, &rec->data, rec->ccw.data_len);
+	*out_ptr += rec->ccw.data_len;
+
+	/* Since CP removed trailing EBCDIC blanks, we have */
+	/* to insert them again */
+
+	residual = info->file_reclen - rec->ccw.data_len;
+	memset(*out_ptr, 0x40, residual);
+	*out_ptr += residual;
+}
+
+/*
+ * Convert record for blocked mode: remove padding bytes and add separator
+ */
+static void convert_blocked(struct vmur *info, struct splink_record *rec,
+			   char **out_ptr)
+{
+	int residual, i;
+
+	memcpy(*out_ptr, &rec->data, rec->ccw.data_len);
+	*out_ptr += rec->ccw.data_len;
+
+	/* Since CP removed trailing EBCDIC blanks, we have */
+	/* to insert them again */
+
+	residual = info->file_reclen - rec->ccw.data_len;
+	memset(*out_ptr, 0x40, residual);
+	*out_ptr += residual;
+
+	/* Now remove trailing padding bytes */
+
+	for (i = 0; i < info->file_reclen; i++) {
+		if (*(*out_ptr - 1) != info->blocked_padding)
+			break;
+		*out_ptr -= 1;
+	}
+
+	/* ... and insert separator */
+
+	**out_ptr = info->blocked_separator;
+	*out_ptr += 1;
+}
+
+/*
+ * Extract data from VM spool file data blocks (SPLINK).
+ */
+ssize_t convert_sfdata(struct vmur *info, struct splink_page *in, char *out)
+{
+	struct splink_record *rec;
+	char *out_ptr = out;
+	unsigned int i, rc = 0;
+
+	rec = (struct splink_record *) &in->data;
+
+	for (i = 0; i < in->data_recs; i++) {
+		if (rec->ccw.opcode == NOP) {
+			rec = (struct splink_record *) ((char *) rec +
+				rec->record_len);
+			continue; /* skip NOP CCWs */
+		} else if (rec->ccw.flag & CCW_IMMED_FLAG) {
+			rec = (struct splink_record *) ((char *) rec +
+				sizeof(rec->ccw));
+			continue; /* skip immediate CCWs */
+		}
+		if (info->text_specified) {
+			rc = convert_text(info, rec, &out_ptr);
+			if (rc)
+				return rc;
+		} else if (info->blocked_specified) {
+			convert_blocked(info, rec, &out_ptr);
+		} else
+			convert_binary(info, rec, &out_ptr);
+		rec = (struct splink_record *) ((char *) rec + rec->record_len);
+	}
+	return out_ptr - out;
+}
+
+/*
+ * Write normal spool file data.
+ */
+int write_normal(struct vmur *info, struct splink_page *sfdata, int count,
+		 int fho)
+{
+	char *outbuf;
+	int len, i;
+
+	for (i = 0; i < count; i++) {
+		struct splink_page *data;
+
+		data = &sfdata[i];
+		outbuf = (char *) malloc((info->file_reclen + 1) *
+					 data->data_recs);
+		if (!outbuf) {
+			ERR("Out of memory\n");
+			return -ENOMEM;
+		}
+		len = convert_sfdata(info, data, outbuf);
+		if (len < 0) {
+			ERR("Data conversion failed\n");
+			return -EINVAL;
+		}
+		if (write(fho, outbuf, len) == -1) {
+			ERR("Write to file %s failed: %s\n", info->file_name,
+			    strerror(errno));
+			return -errno;
+		}
+		free(outbuf);
+	}
+
+	return 0;
+}
+
+/*
+ * Write vmdump data.
+ */
+int write_vmdump(struct vmur *info, struct splink_page *sfdata, int count,
+		 int fho)
+{
+	if (write(fho, sfdata, count * sizeof(sfdata[0])) == -1) {
+		ERR("Write to file %s failed: %s\n", info->file_name,
+		    strerror(errno));
+		return -errno;
+	}
+
+	return 0;
+}
+
+/*
+ * Close VM virtual reader in case of a signal e.g. CTRL-C
+*/
+static void ur_receive_sig_handler(int sig, siginfo_t *sip, void *p)
+{
+	close_reader(&vmur_info, "HOLD");
+	ERR_EXIT("Operation terminated, spool file received incompletely.\n");
+}
+
+/*
+ * Receive reader file.
+ */
+static void ur_receive(struct vmur *info)
+{
+	struct splink_page sfdata[READ_BLOCKS];
+	enum spoolfile_fmt type;
+	int fhi, fho = STDOUT_FILENO, count;
+	int rc;
+
+	if (check_class(info))
+		ERR_EXIT("Reader device class does not match spool file "
+			 "class.\nIssue CP command 'SPOOL %X CLASS *' to "
+			 "enable %X for any spool file class.\n",
+			 info->devno, info->devno);
+	acquire_lock(info);
+	close_reader(info, "HOLD");
+	order_change_reader_file(info);
+
+	fhi = open(info->devnode, O_RDONLY | O_NONBLOCK);
+	if (fhi == -1)
+		ERR_EXIT("Could not open device %s\n%s\n", info->devnode,
+			 strerror(errno));
+
+	set_signal_handler(info, ur_receive_sig_handler);
+
+	if (!info->stdout_specified) {
+		if (!info->file_name_specified &&
+		    get_filename_from_reader(info))
+			goto fail;
+		check_overwrite(info);
+	}
+
+	/* Read first block and check spoolfile format */
+
+	count = read(fhi, &sfdata, READ_BLOCKS * sizeof(sfdata[0]));
+	if (count == -1) {
+		ERR("Could not read from device %s\n%s\n", info->devnode,
+		    strerror(errno));
+		goto fail;
+	}
+
+	type = get_spoolfile_fmt(info, &sfdata[0]);
+	if (info->convert_specified) {
+		if (type != TYPE_VMDUMP) {
+			ERR("Reader file %s does not have VMDUMP format, "
+			    "conversion not possible.\n", info->spoolid);
+			goto fail;
+		} else {
+			close(fhi);
+			if (info->stdout_specified)
+				rc = vm_convert(info->devnode, NULL, prog_name);
+			else
+				rc = vm_convert(info->devnode, info->file_name,
+						prog_name);
+			if (rc)
+				goto fail;
+			else
+				goto vm_convert_done;
+		}
+	}
+	if (type == TYPE_VMDUMP)
+		ERR("INFO: Reader file %s has VMDUMP format.\n", info->spoolid);
+	if (type == TYPE_NETDATA)
+		ERR("INFO: Reader file %s has NETDATA format.\n",
+		    info->spoolid);
+
+	if (type != TYPE_VMDUMP) {
+		int spoolid = atoi(info->spoolid);
+
+		if (spoolid != sfdata->spoolid) {
+			check_hold_state(info->spoolid);
+			ERR_EXIT("Could not receive spool file %s. Spoolid "
+				 "mismatch (%i)\n", info->spoolid,
+				 sfdata->spoolid);
+		}
+	}
+
+	/* read spool file data, convert it, and write it to output file or
+	 * stdout */
+	if (!info->stdout_specified) {
+		fho = open(info->file_name, O_WRONLY | O_CREAT | O_TRUNC,
+			   S_IRUSR | S_IWUSR);
+		if (fho == -1) {
+			ERR("Could not open file %s\n%s\n", info->file_name,
+			    strerror(errno));
+			goto fail;
+		}
+	}
+	while (count != 0) {
+		int blocks;
+
+		blocks = count / sizeof(sfdata[0]);
+		if (type == TYPE_VMDUMP)
+			rc = write_vmdump(info, &sfdata[0], blocks, fho);
+		else
+			rc = write_normal(info, &sfdata[0], blocks, fho);
+
+		if (rc)
+			goto fail;
+
+		/* read next records */
+		count = read(fhi, &sfdata, READ_BLOCKS * sizeof(sfdata[0]));
+		if (count == -1) {
+			ERR("Could not read from device %s\n%s\n",
+			    info->devnode, strerror(errno));
+			goto fail;
+		}
+	}
+	if (fho != STDOUT_FILENO)
+		close(fho);
+	close(fhi);
+vm_convert_done:
+	if (info->hold_specified)
+		close_reader(info, "HOLD");
+	else
+		close_reader(info, "NOHOLD NOKEEP");
+	return;
+
+fail:
+	close_reader(info, "HOLD");
+	exit(1);
+}
+
+/*
+ * Issue CP command CLOSE PUNCH
+ */
+static void close_ur_device(struct vmur *info)
+{
+	char cmd[MAXCMDLEN], spoolid[5] = {}, *response;
+	int cprc;
+
+	if (info->node_specified) {
+		sprintf(cmd, "SPOOL %X NOCONT", info->devno);
+		cpcmd(cmd, NULL, NULL, 0);
+	}
+	if (info->rdr_specified) {
+		sprintf(cmd, "CLOSE %X TO ", info->devno);
+		if (info->node_specified)
+			strcat(cmd, RSCS_USERID);
+		else if (info->user_specified)
+			strcat(cmd, to_upper(info->user));
+		else
+			strcat(cmd, "*");
+		strcat(cmd, " RDR");
+	} else {
+		sprintf(cmd, "CLOSE %X", info->devno);
+	}
+	strcat(cmd, " NAME ");
+	strcat(cmd, info->spoolfile_name);
+	if (info->spoolfile_type_specified) {
+		strcat(cmd, " ");
+		strcat(cmd, info->spoolfile_type);
+	}
+	cpcmd_cs(cmd, &response, &cprc, 0);
+	memcpy(spoolid, &response[9], 4);
+	free(response);
+	if (cprc == 439) {
+		if (info->action == PUNCH)
+			sprintf(cmd, "PURGE * PUN %s", spoolid);
+		else
+			sprintf(cmd, "PURGE * PRT %s", spoolid);
+		cpcmd(cmd, NULL, NULL, 0);
+		ERR_EXIT("User %s spool fileid limit exceeded.\n"
+			 , info->user);
+	}
+	if (info->rdr_specified) {
+		if (info->node_specified)
+			printf("Reader file with spoolid %s created and "
+			       "transferred to %s.\n", spoolid, RSCS_USERID);
+		else if (info->user_specified)
+			printf("Reader file with spoolid %s created and "
+			       "transferred to %s.\n", spoolid, info->user);
+		else
+			printf("Reader file with spoolid %s created.\n",
+			       spoolid);
+	} else if (info->action == PUNCH)
+		printf("Punch file with spoolid %s created.\n", spoolid);
+	else
+		printf("Printer file with spoolid %s created.\n", spoolid);
+}
+
+/*
+ * Issue CP command CLOSE PUNCH
+ */
+static void close_ur_device_simple(struct vmur *info)
+{
+	char cmd[MAXCMDLEN];
+
+	sprintf(cmd, "CLOSE %X", info->devno);
+	cpcmd(cmd, NULL, NULL, 0);
+}
+
+/*
+ * Issue CP command CLOSE PUNCH PURGE
+ */
+static void close_ur_device_purge(struct vmur *info)
+{
+	char cmd[MAXCMDLEN];
+
+	if (info->node_specified) {
+		sprintf(cmd, "SPOOL %X NOCONT", info->devno);
+		cpcmd(cmd, NULL, NULL, 0);
+	}
+	sprintf(cmd, "CLOSE %X PURGE", info->devno);
+	cpcmd(cmd, NULL, NULL, 0);
+}
+
+/*
+ * Issue "CP QUERY VIRTUAL devno" to retrieve punch device information
+ * Exit, if devno has CONT status
+ */
+static int is_punch_cont(struct vmur *info)
+{
+	char *buf;
+	char cmd[MAXCMDLEN];
+	int rc = 0;
+
+	sprintf(cmd, "QUERY VIRTUAL %X", info->devno);
+	cpcmd(cmd, &buf, NULL, 0);
+	if (!strncmp(buf + 15, "  CONT", 6))
+		rc = 1;
+	free(buf);
+	return rc;
+}
+
+/*
+ * Provide tag information for RSCS
+ */
+static void rscs_punch_setup(struct vmur *info)
+{
+	char cmd[MAXCMDLEN];
+
+	sprintf(cmd, "SPOOL %X CONT", info->devno);
+	cpcmd(cmd, NULL, NULL, 0);
+	sprintf(cmd, "TAG DEV %X %s %s", info->devno, info->node, info->user);
+	cpcmd(cmd, NULL, NULL, 0);
+	return;
+}
+
+/*
+ * Purge punch file in case of a signal e.g. CTRL-C
+ */
+static void ur_write_sig_handler(int sig, siginfo_t *sip, void *p)
+{
+	close_ur_device_purge(&vmur_info);
+	ERR_EXIT("Operation terminated, no spool file created.\n");
+}
+
+/*
+ * Read on line from fd not including newline
+ */
+static int read_line(int fd, char *buf, int len, int lf)
+{
+	int offs = 0;
+
+	memset(buf, 0, len);
+	do {
+		int rc;
+
+		rc = read(fd, buf + offs, 1);
+		if (rc < 0)
+			return -EIO;
+		if (rc == 0)
+			return -ENODATA;
+		if (*(buf + offs) == lf)
+			goto found;
+		offs++;
+	} while (offs < len);
+
+	return -EINVAL;
+
+found:
+	*(buf + offs) = 0;
+	return offs;
+}
+
+/*
+ * Read text file for punch/print
+ */
+static int read_text_file(struct vmur *info, int fd, char *out_buf, size_t len)
+{
+	unsigned int pos = 0;
+	static int line = 1;
+	char sep, pad;
+	char *buf;
+	int rc;
+
+	sep = '\n';
+	pad = ' ';
+
+	buf = (char *) malloc(info->ur_reclen + 1);
+	if (!buf)
+		return -ENOMEM;
+
+	do {
+		int line_len;
+		size_t rec_len, out_len;
+		char *in_ptr, *out_ptr;
+
+		line_len = read_line(fd, buf, info->ur_reclen  + 1, sep);
+		if (line_len == -ENODATA) {
+			break;
+		} else if (line_len == -EINVAL) {
+			ERR("Input line %i too long. Unit record length"
+			    " must not exceed %i\n", line, info->ur_reclen);
+			goto fail;
+		} else if (line_len < 0) {
+			ERR("Read failed: %s", strerror(errno));
+			goto fail;
+		}
+		line++;
+		memset(buf + line_len, pad, info->ur_reclen - line_len);
+		rec_len = out_len = info->ur_reclen;
+		in_ptr = buf;
+		out_ptr = &out_buf[pos];
+		rc = iconv(info->iconv, &in_ptr, &rec_len, &out_ptr, &out_len);
+		if ((rc == -1) || (out_len != 0)) {
+			ERR("Code page conversion failed at line %i\n", line);
+			goto fail;
+		}
+		pos += info->ur_reclen;
+	} while (pos < len);
+	free(buf);
+	return pos;
+fail:
+	free(buf);
+	return -1;
+}
+
+/*
+ * Read blocked file for punch/print
+ */
+static int read_blocked_file(struct vmur *info, int fd, char *out_buf,
+			     size_t len)
+{
+	unsigned int pos = 0;
+	static int line = 1;
+	char sep, pad;
+	int line_len;
+	char *buf;
+
+	sep = info->blocked_separator;
+	pad = info->blocked_padding;
+
+	buf = (char *) malloc(info->ur_reclen + 1);
+	if (!buf)
+		return -ENOMEM;
+
+	do {
+		line_len = read_line(fd, buf, info->ur_reclen  + 1, sep);
+		if (line_len == -ENODATA) {
+			break;
+		} else if (line_len == -EINVAL) {
+			ERR("Input line %i too long. Unit record length"
+			    " must not exceed %i\n", line, info->ur_reclen);
+			goto fail;
+		} else if (line_len < 0) {
+			ERR("Read failed: %s", strerror(errno));
+			goto fail;
+		}
+		line++;
+		memset(buf + line_len, pad, info->ur_reclen - line_len);
+		memcpy(&out_buf[pos], buf, info->ur_reclen);
+		pos += info->ur_reclen;
+	} while (pos < len);
+	free(buf);
+	return pos;
+fail:
+	free(buf);
+	return -1;
+}
+
+/*
+ * Read file for punch/print
+ */
+static int read_input_file(struct vmur *info, int fd, char *out_buf, size_t len)
+{
+	int rc;
+
+	if (info->text_specified) {
+		rc = read_text_file(info, fd, out_buf, len);
+	} else if (info->blocked_specified) {
+		rc = read_blocked_file(info, fd, out_buf, len);
+	} else {
+		rc = read(fd, out_buf, len);
+		if (rc == -1) {
+			ERR("Could not read file %s\n%s\n", info->file_name,
+			    strerror(errno));
+			return -EIO;
+		}
+	}
+	return rc;
+}
+
+/*
+ * Write function for punch and printer
+ */
+static void ur_write(struct vmur *info)
+{
+	int fhi, fho, residual, anything_written = 0;
+	char *sfdata;
+	ssize_t count;
+
+	acquire_lock(info);
+
+	/* close punch preventively */
+	close_ur_device_simple(info);
+
+	/* Check punch. If punch is spooled CONT, exit */
+	if (is_punch_cont(info))
+		ERR_EXIT("Virtual punch device %X is spooled CONT.\n",
+			 info->devno);
+
+	sfdata = (char *) malloc(info->ur_reclen * VMUR_REC_COUNT);
+	if (!sfdata)
+		ERR_EXIT("Could allocate memory for buffer (%i)\n",
+			    info->ur_reclen);
+
+	/* Open Linux file */
+	if (info->file_name_specified) {
+		fhi = open(info->file_name, O_RDONLY);
+		if (fhi == -1)
+			ERR_EXIT("Could not open file %s\n%s\n",
+				 info->file_name, strerror(errno));
+	} else {
+		fhi = STDIN_FILENO;
+	}
+
+	if (info->node_specified)
+		rscs_punch_setup(info);
+
+	/* Open UR device */
+	fho = open(info->devnode, O_WRONLY | O_NONBLOCK);
+	if (fho == -1) {
+		ERR("Could not open device %s\n%s\n", info->devnode,
+		    strerror(errno));
+		goto fail;
+	}
+
+	set_signal_handler(info, ur_write_sig_handler);
+
+	/* read linux file data, and write it to VM punch device */
+	do {
+		count = read_input_file(info, fhi, sfdata,
+					info->ur_reclen * VMUR_REC_COUNT);
+		if (count < 0)
+			goto fail;
+		else if (count == 0)
+			break; /* EOF */
+
+		residual = (info->ur_reclen - (count % info->ur_reclen))
+			% info->ur_reclen;
+		memset(sfdata + count, 0, residual);
+		if (write(fho, sfdata, count + residual) == -1) {
+			ERR("Could not write on device %s (%s)\n",
+			    info->devnode, strerror(errno));
+			if (errno == EIO)
+				ERR("Spool file limit exceeded or spool space "
+				    "full?\n");
+			goto fail;
+		} else
+			anything_written = 1;
+	} while (1);
+
+	close(fho);
+	if (anything_written)
+		close_ur_device(info);
+	else
+		ERR_EXIT("No spool file created - probably empty input.\n");
+	free(sfdata);
+	if (fhi != STDIN_FILENO)
+		close(fhi);
+	return;
+fail:
+	close_ur_device_purge(info);
+	exit(1);
+}
+
+/*
+ * Ask if file should be purged, if -f is not specified
+ */
+static void ur_purge_question(struct vmur *info)
+{
+	char buf[5] = {};
+	char *str;
+
+	if (info->force_specified)
+		return;
+
+	if (info->spoolid_specified)
+		fprintf(stderr, "%s: purge %s %s? ", prog_name, info->queue,
+		       info->spoolid);
+	else
+		fprintf(stderr, "%s: purge all %s files? ", prog_name,
+			info->queue);
+	str = fgets(buf, sizeof(buf), stdin);
+	if (!str)
+		exit(1);
+	if (strcasecmp(buf, "y\n") == 0)
+		return;
+	if (strcasecmp(buf, "yes\n") == 0)
+		return;
+	exit(0);
+}
+
+/*
+ * Purge spool file
+ */
+static int ur_purge(struct vmur *info)
+{
+	char *buf, cmd[MAXCMDLEN];
+
+	ur_purge_question(info);
+	if (info->spoolid_specified)
+		sprintf(cmd, "PURGE * %s %s", info->queue, info->spoolid);
+	else
+		sprintf(cmd, "PURGE * %s ALL", info->queue);
+	acquire_lock(info);
+	cpcmd(cmd, &buf, NULL, 0);
+	ERR("%s", buf);
+	free(buf);
+	return 0;
+}
+
+/*
+ * Order spool file to top of the queue
+ */
+static int ur_order(struct vmur *info)
+{
+	char cmd[MAXCMDLEN];
+
+	sprintf(cmd, "ORDER * %s %s", info->queue, info->spoolid);
+	acquire_lock(info);
+	cpcmd(cmd, NULL, NULL, 0);
+	return 0;
+}
+
+/*
+ * List spool files
+ */
+static int ur_list(struct vmur *info)
+{
+	char *buf;
+	char cmd[MAXCMDLEN];
+
+	if (info->spoolid_specified)
+		sprintf(cmd, "QUERY %s * %s ALL", info->queue, info->spoolid);
+	else
+		sprintf(cmd, "QUERY %s * ALL", info->queue);
+
+	cpcmd(cmd, &buf, NULL, 1);
+	printf("%s", buf);
+	free(buf);
+	return 0;
+}
+
+/*
+ * Initialize iconv: "from" -> "to"
+ */
+static void setup_iconv(struct vmur *info, const char *from, const char *to)
+{
+	info->iconv = iconv_open(to, from);
+	if (info->iconv == ((iconv_t) -1))
+		ERR_EXIT("Could not initialize conversion table %s->%s.\n",
+			 from, to);
+}
+
+int main(int argc, char **argv)
+{
+	/* set name of program */
+	prog_name = basename(argv[0]);
+
+	/* set default values */
+	init_info(&vmur_info);
+
+	/* parse command line options and check syntax */
+	parse_opts(&vmur_info, argc, argv);
+
+	switch (vmur_info.action) {
+	case RECEIVE:
+		setup_ur_device(&vmur_info);
+		if (vmur_info.text_specified)
+			setup_iconv(&vmur_info, EBCDIC_CODE_PAGE,
+				    ASCII_CODE_PAGE);
+		ur_receive(&vmur_info);
+		break;
+	case PUNCH:
+	case PRINT:
+		if (vmur_info.text_specified)
+			setup_iconv(&vmur_info, ASCII_CODE_PAGE,
+				    EBCDIC_CODE_PAGE);
+		setup_ur_device(&vmur_info);
+		ur_write(&vmur_info);
+		break;
+	case PURGE:
+		ur_purge(&vmur_info);
+		break;
+	case ORDER:
+		ur_order(&vmur_info);
+		break;
+	case LIST:
+		ur_list(&vmur_info);
+		break;
+	default:
+		ERR("Internal error: unknown action '%i'\n", vmur_info.action);
+		return -EINVAL;
+	}
+	return 0;
+}
Index: s390-tools-sles11/vmur/vmur.h
===================================================================
--- s390-tools-sles11.orig/vmur/vmur.h
+++ s390-tools-sles11/vmur/vmur.h
@@ -96,13 +96,13 @@ const char *ur_action_str[] = {
 	"list",
 };
 
-int ur_action_prefix_len[] = {
-	[RECEIVE] = 2,
-	[PUNCH]   = 3,
-	[PRINT]   = 2,
-	[PURGE]   = 3,
-	[ORDER]   = 2,
-	[LIST]    = 2,
+unsigned int ur_action_prefix_len[] = {
+	2, /* RECEIVE */
+	3, /* PUNCH */
+	2, /* PRINT */
+	3, /* PURGE */
+	2, /* ORDER */
+	2, /* LIST */
 };
 
 struct ccw {
Index: s390-tools-sles11/zconf/Makefile
===================================================================
--- s390-tools-sles11.orig/zconf/Makefile
+++ s390-tools-sles11/zconf/Makefile
@@ -4,15 +4,18 @@
 include ../common.mak
 
 SCRIPTS	= lsdasd lstape lscss chccwdev lsqeth lszfcp lschp chchp lszcrypt \
-	  chzcrypt lsluns
+	  chzcrypt lsluns znetconf
 MANPAGES= lsdasd.8 lstape.8 lscss.8 chccwdev.8 lsqeth.8 lszfcp.8 lschp.8 \
-	  chchp.8 lszcrypt.8 chzcrypt.8 lsluns.8
+	  chchp.8 lszcrypt.8 chzcrypt.8 lsluns.8 znetconf.8
 
 all:
 
 clean:
 
 install:	install-scripts install-manpages
+	$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 lsznet.raw $(TOOLS_LIBDIR)
+	$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 znetcontrolunits \
+		$(TOOLS_LIBDIR)
 
 install-scripts:	$(SCRIPTS)
 	@for i in $^; do \
Index: s390-tools-sles11/zconf/lsluns
===================================================================
--- s390-tools-sles11.orig/zconf/lsluns
+++ s390-tools-sles11/zconf/lsluns
@@ -1,10 +1,10 @@
 #!/usr/bin/perl
 
 ###############################################################################
-# Script to list all available LUNs. 
+# Script to list all available LUNs.
 #
 # Copyright IBM Corp. 2008.
-# 
+#
 # Author(s): Martin Petermann <martin@linux.vnet.ibm.com>
 #            Swen Schillig    <swen@vnet.ibm.com>
 #
@@ -22,6 +22,8 @@ my @port = ();
 my $active = "";
 
 my $wlun = "0xc101000000000000";
+my $lun0 = "0x0000000000000000";
+
 
 sub list_luns
 {
@@ -29,19 +31,17 @@ sub list_luns
     my $drv_dir = "/sys/bus/ccw/drivers/zfcp";
     my $sg_dir = "/sys/class/scsi_generic";
     my $man_att;
-    my $lun;
-    my $sg_dev;
 
     foreach my $a (sort keys %res_hash) {
         print "Scanning for LUNs on adapter $a\n";
         foreach my $p (@{$res_hash{$a}}) {
             next if (! -e $drv_dir."/$a/$p");
-            my $stat = `cat $drv_dir/$a/$p/status`;
-            chomp($stat);
-            if (!(hex($stat) & 0x10000000)) {
+            my $status = `cat $drv_dir/$a/$p/access_denied`;
+            $status .= `cat $drv_dir/$a/$p/failed`;
+            $status .= `cat $drv_dir/$a/$p/in_recovery`;
+            if ($status =~ /1/) {
                 print "\tat port $p:\n";
-                print "\t\tPort not online. ",
-                "Cannot scan for LUNs.\n";
+                print "\t\tPort not online. Cannot scan for LUNs.\n";
                 next;
             }
             if (!defined($lun_hash{$a}{$p})) {
@@ -49,33 +49,45 @@ sub list_luns
                 select(undef, undef, undef, 0.1);
                 %lun_hash = get_lun_hash();
                 if (!defined($lun_hash{$a}{$p})) {
-                    `echo $wlun >> $drv_dir/$a/$p/unit_remove`;
-                    print"\tat port $p:\n";
-                    print "\t\tCannot attach WLUN.\n";
-                    next;
+                    `echo $wlun >> $drv_dir/$a/$p/unit_remove 2>/dev/null`;
+                    `echo $lun0 >> $drv_dir/$a/$p/unit_add 2>/dev/null`;
+                    select(undef, undef, undef, 0.1);
+		    %lun_hash = get_lun_hash();
+                    if (!defined($lun_hash{$a}{$p})) {
+                    	`echo $lun0 >> $drv_dir/$a/$p/unit_remove 2>/dev/null`;
+                    	print"\tat port $p:\n";
+                    	print "\t\tCannot attach WLUN / LUN0 for scanning.\n";
+                    	next;
+		    }
                 }
                 $man_att = 1;
             }
-            $lun = ${[keys %{$lun_hash{$a}{$p}}]}[0];
-            $sg_dev = $lun_hash{$a}{$p}{$lun};
-            select(undef, undef, undef, 0.1) while (! -e "/dev/$sg_dev");
-            my @output = `sg_luns /dev/$sg_dev 2>/dev/null`;
-            my $error = $?;
-            if ($man_att) {
-                `echo 1 >> $sg_dir/$sg_dev/device/delete 2>/dev/null`;
-                select(undef, undef, undef, 0.1);
-                `echo $lun >> $drv_dir/$a/$p/unit_remove`;
-                $man_att = 0;
-            }
-            print "\tat port $p:\n";
-            if (!$error && @output) {
-                splice(@output,0,2);
-                map { s/\s*(\w{16})\s*/\t\t0x$1\n/ } @output;
-                print @output;
-            }
-            if ($error) {
-                print "\t\a\tUnable to send the REPORT_LUNS SCSI command to WLUN.\n";
-            }
+
+	    my $retries = 0;
+            foreach my $lun (@{[keys %{$lun_hash{$a}{$p}}]}) {
+            	my $sg_dev = $lun_hash{$a}{$p}{$lun};
+            	select(undef, undef, undef, 0.1) while (! -e "/dev/$sg_dev");
+            	my @output = `sg_luns /dev/$sg_dev 2>/dev/null`;
+            	my $error = $?;
+            	if ($man_att) {
+                	`echo 1 >> $sg_dir/$sg_dev/device/delete 2>/dev/null`;
+                	select(undef, undef, undef, 0.1);
+                	`echo $lun >> $drv_dir/$a/$p/unit_remove 2>/dev/null`;
+                	$man_att = 0;
+            	}
+            	print "\tat port $p:\n" if (!$retries);
+            	if (!$error && @output) {
+                	splice(@output, 0, 2);
+                	map { s/\s*(\w{16})\s*/\t\t0x$1\n/ } @output;
+                	print @output;
+			last;
+            	}
+            	if ($error) {
+                	print "\t\tUnable to send the REPORT_LUNS command to LUN.\n";
+            	}
+		$retries++;
+		last if ($retries > 3);
+	    }
         }
     }
 }
@@ -92,9 +104,9 @@ sub get_lun_hash
         $l =~ s/(0x\w{16})*\n/$1/;
         $p =~ s/(0x\w{16})*\n/$1/;
         chomp($a);
-        
+
         $lun_hash{$a}{$p}{$l} = ${[split('/', $device)]}[-1];
-    }	
+    }
     return %lun_hash;
 }
 
@@ -108,6 +120,11 @@ Usage: $PROGRAM_NAME [<options>]
     The display can be limited by specifying an adapter or a port.
 
 Options:
+    -a, --active
+    Shows all activated LUNs.
+    In addition LUN encryption information is provided.
+    e.g. "lsluns -a"
+
     -c, --ccw
     Shows LUNs for a specific ccw device.
     e.g. "lsluns -c 0.0.3922"
@@ -126,8 +143,8 @@ EOD
 }
 
 sub lsluns_version {
-    print "$PROGRAM_NAME: version %S390_TOOLS_VERSION%.\n";
-    print "Copyright IBM Corp. 2008.\n";
+    print "$PROGRAM_NAME: version %S390_TOOLS_VERSION%\n";
+    print "Copyright IBM Corp. 2008\n";
 }
 
 sub lsluns_invalid_usage {
@@ -159,7 +176,7 @@ reload:
             "Removing from resource list.\n";
         }
     }
-    
+
     push @t_arr, map { @{$res_hash{$_}} } keys %res_hash;
     foreach my $p (@$p_ref_list) {
         if ("@t_arr" !~ /$p/) {
@@ -167,7 +184,7 @@ reload:
             "Removing from resource list.\n";
         }
     }
-    
+
     if (!%res_hash) {
         print "\nNo valid parameters left, ",
         "using all available resources in system.\n\n";
@@ -179,13 +196,46 @@ reload:
     return %res_hash;
 }
 
+sub show_attached_lun_info
+{
+        my %lun_hash = get_lun_hash();
+        my @txt = ("Disk", "Tape", "Printer", "Proc", "WRO",
+                   "CD/DVD", "Scanner", "OMD", "Changer","Comm","n/a",
+                   "n/a","RAID", "Encl");
+
+        foreach my $a (sort keys %lun_hash) {
+                next if ("@adapter" !~ /$a/);
+                print "adapter = $a\n";
+                foreach my $p (sort keys %{$lun_hash{$a}}) {
+                        next if ("@port" !~ /$p/);
+                        print "\tport = $p\n";
+                        foreach my $l (sort keys %{$lun_hash{$a}{$p}}) {
+                                my $sg_dev = "/dev/".$lun_hash{$a}{$p}{$l};
+                                my $inq = `sg_inq -r $sg_dev`;
+                                (my $vend = substr($inq, 0x8, 0x8)) =~ s/\s*//g;
+                                (my $mod = substr($inq, 0x10, 0x10)) =~ s/\s*//g;
+                                my $type = ord(substr($inq, 0x0, 0x1));
+                                my $enc = ($mod =~ /2107/) ?
+                                        ord(substr($inq, 0xa2, 0x1)) : 0;
+                                $l .= "(X)" if ($enc & 0x80);
+                                $txt[$type] = $type if (!defined($txt[$type]));
+                                print("\t\tlun = $l\t$sg_dev\t$txt[$type]",
+                                      "\t$vend:$mod\n");
+                        }
+                }
+        }
+}
+
+
+
 ########### main ################
 
 $PROGRAM_NAME = basename($PROGRAM_NAME);
 
 Getopt::Long::Configure(qw(bundling));
-GetOptions('c|ccw=s' => \@adapter, 
+GetOptions('c|ccw=s' => \@adapter,
 	   'p|port=s'    => \@port,
+	   'a|active'    => \$active,
 	   'v|version'   => sub { lsluns_version(); exit 0; },
 	   'h|help'      => sub { lsluns_usage(); exit 0; },
            ) or do {
@@ -206,5 +256,10 @@ push @port, map { @{$res_hash{$_}} } key
 die "$PROGRAM_NAME: Unable to execute due to missing sg3_utils package. ".
     "Processing stopped.\n" if system("sg_luns -V > /dev/null 2>&1");
 
-list_luns();
+if ($active) {
+        show_attached_lun_info();
+} else {
+        list_luns();
+}
+
 
Index: s390-tools-sles11/zconf/lsluns.8
===================================================================
--- s390-tools-sles11.orig/zconf/lsluns.8
+++ s390-tools-sles11/zconf/lsluns.8
@@ -33,6 +33,11 @@ Shows LUNs for a specific adapter.
 .TP
 .B -p, --port
 Shows LUNs for a specific port.
+.TP
+.B -a, --active
+Shows all activated LUNs. In addition information is provided
+whether the LUN is encrypted or not. This is indicated with a bracketed X
+right after the LUN number.
 
 .SH EXAMPLES
 .PP
@@ -46,3 +51,10 @@ Shows all LUNs found on adapter 0.0.3922
 Shows all LUNs for port 0x5005123456789000.
 .IP "lsluns -c 0.0.3922 -p 0x5005123456789000"
 Shows all LUNs for adapter 0.0.3922 and port 0x5005123456789000.
+.IP "lsluns -a "
+adapter = 0.0.3c02
+        port = 0x500507630300c562
+                lun = 0x401040a200000000(X)     /dev/sg0        Disk    IBM:2107900
+                lun = 0x401040a300000000        /dev/sg1        Disk    IBM:2107900
+
+Shows all active LUNs including the information whether the device is encrypted or not.
Index: s390-tools-sles11/zconf/lsznet.raw
===================================================================
--- /dev/null
+++ s390-tools-sles11/zconf/lsznet.raw
@@ -0,0 +1,363 @@
+#! /bin/bash
+#
+# lsznet.raw - List sensible network device hardware setups for Linux on s390(x)
+#
+# This script is not intended to be used as standalone tool, but should be
+# used from other tools like a library function. E.g. znetconf is one exploiter
+# of this script.
+#
+# Copyright IBM Corp. 2008, 2009
+# Author(s):  Steffen Maier <maier@de.ibm.com>
+#
+
+readonly SYSFS=/sys
+# DEBUG=0 turns off debugging. >=1 means increasing debugging.
+readonly DEBUG=0
+
+# nothing to be changed below here
+
+export TEXTDOMAIN=lsznet.raw
+
+readonly CMD=${0##*/}
+
+function error() {
+    echo $"$CMD: ERROR: $*" 1>&2
+    exit 1;
+}
+
+# currently requires bash version 3.0 or later
+# (this seems reasonable since bash-3.0 has been shipped with
+#  RHEL 4.0, 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7;
+#  bash-3.1 with RHEL 5.0, 5.1; and bash-3.2 with RHEL 5.2)
+if [ ${BASH_VERSINFO[0]} -lt 3 -o \
+    ${BASH_VERSINFO[1]} -lt 0 ]; then
+    error $"only works with BASH version 3.0 or later (current version used is ${BASH_VERSION})"
+fi
+
+# the script was designed for Linux kernel 2.6 and might work with newer ones
+kernel_version=$(uname -r)
+IFS=.
+read krn_ver krn_patch krn_foo <<< "$kernel_version"
+unset IFS
+if [ $krn_ver -lt 2 -o $krn_patch -lt 6 ]; then
+    error $"only works for kernel versions 2.6 or probably later"
+fi
+
+# The following combinations of control unit type and model were taken from the
+# Linux network device drivers for s390 on 2008-06-09 from Linux 2.6.25.4.
+# The list (among other things) should be adapted, if any of those device
+# drivers start supporting different CU types/models.
+# (Alternatively, the list could be generated by reading the modaliases
+#  directly out of the device driver modules:
+#  modinfo qeth/cu3088 | fgrep 'alias:'
+#  However, this would still require a list of device driver modules.)
+readonly -a CU=(
+    1731/01
+    1731/05
+    3088/08
+    3088/1f
+    3088/1e
+    3088/01
+    3088/60
+    3088/61
+)
+
+# $CU_CARDTYPE array is the only one which may contain entries with spaces
+readonly -a CU_CARDTYPE=(
+    "OSA (QDIO)"
+    "HiperSocket"
+    "CTC adapter"
+    "escon channel"
+    "ficon channel"
+    "LCS p390"
+    "LCS OSA"
+    "LCS CLAW"
+)
+
+readonly -a CU_DEVDRV=(
+    qeth
+    qeth
+    ctc
+    ctc
+    ctc
+    lcs
+    lcs
+    lcs
+)
+
+readonly -a CU_DEVNAME=(
+    eth
+    hsi
+    ctc
+    ctc
+    ctc
+    eth
+    eth
+    eth
+)
+
+readonly -a CU_GROUPCHANNELS=(
+    3
+    3
+    2
+    2
+    2
+    2
+    2
+    2
+)
+readonly -a CHPIDTYPES=(
+    [10]=OSE
+    [11]=OSD
+    [24]=IQD
+)
+
+readonly PREFIXFORMAT=[[:xdigit:]]*
+readonly SSIDFORMAT=[0-3]
+readonly BUSIDFORMAT=[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]
+readonly IDFORMAT=$PREFIXFORMAT.$SSIDFORMAT.$BUSIDFORMAT
+readonly SUBCHANNEL_TYPE_IO=0
+
+function debug() {
+    level=$1
+    shift
+    [ $DEBUG -ge $level ] && echo "$*" 1>&2
+}
+
+# Searches for a match of argument 1 on the array $CU and sets $cu_idx
+# to the matched array index on success.
+# Returns 0 on success, 1 on failure.
+function search_cu() {
+    local scu=$1
+    local i
+    for ((i=0; i < ${#CU[@]}; i++)); do
+        if [ $scu == ${CU[i]} ]; then
+            cu_idx=$i
+            return 0
+        fi
+    done
+    return 1
+}
+
+# Returns symbolic name of CHPID type in $chpidtype_symbolic,
+# if an entry in the array $CHPIDTYPES has been found at index of argument 1.
+# Returns "?" otherwise.
+# Always succeeds and returns 0.
+function search_chpt() {
+    local chpidtype_number=$1
+    chpidtype_symbolic=${CHPIDTYPES[$chpidtype_number]}
+    if [ "$chpidtype_symbolic" == "" ]; then
+        chpidtype_symbolic="?"
+    fi
+    return 0
+}
+
+# build_list:
+#
+# Prints list on standard output consisting of all subchannels and
+# ccwdevices whose control unit type/model match supported network
+# device types on s390.  Each matching entry is accompanied with
+# (almost all) corresponding attributes.
+#
+function build_list() {
+    # use /sys/devices/css*/ for startpath
+    readonly STARTPATH=$SYSFS/devices
+    # change to base directory so path globbing length with find is minimal
+    cd $STARTPATH
+    # fail out gracefully, if there is not expected sysfs environment
+    # (could even fail out near the top, if $(uname -m) != s390x)
+    csses=css$PREFIXFORMAT
+    for d in $csses; do
+        [ -d $d ] || exit
+    done
+    find $csses -name "$IDFORMAT" |
+    while read dir; do
+        debug 6 " examining sysfs directory $dir"
+        # must not use $...FORMAT (file globs) here since this is a regex:
+# old code:        [[ "$dir" =~ ^css\([[:xdigit:]]+\)/\([[:xdigit:]]+\\.[0-3]\\.[[:xdigit:]]{4}\)/\([[:xdigit:]]+\\.[0-3]\\.[[:xdigit:]]{4}\)$ ]]
+	EXPR="^css([[:xdigit:]]+)/([[:xdigit:]]+.[0-3].[[:xdigit:]]{4})/([[:xdigit:]]+.[0-3].[[:xdigit:]]{4})$"
+	[[ "$dir" =~ $EXPR ]]
+        case $? in
+            0)
+                # string matched the pattern
+                debug 6 " ${BASH_REMATCH[@]}"
+                prefix=${BASH_REMATCH[1]}
+                subch=${BASH_REMATCH[2]}
+                devbusid=${BASH_REMATCH[3]}
+                subch_p=css$prefix/$subch
+                dev_p=$subch_p/$devbusid
+                debug 6 " $subch_p $dev_p"
+                ;;
+            1)
+                # string did not match the pattern
+                continue
+                ;;
+            2)
+                error $"syntax error in regex of match operator =~, code needs to be fixed"
+                ;;
+            *)
+                error $"unexpected return code of regex match operator =~, code needs to be fixed"
+                ;;
+        esac
+        debug 5 " sysfs directory matched regex $dir"
+        # skip non-I/O-subchannels, i.e. chsc and message subchannels
+	if [ -f $subch_p/type ]; then
+	    read type < $subch_p/type
+            if [ $type != $SUBCHANNEL_TYPE_IO ]; then
+                debug 3 " skip non-I/O subchannel"
+                continue
+            fi
+	fi
+        # get subchannel information...
+        # ATTENTION: hex values from sysfs are WITHOUT leading 0x prefix!
+        read chpid_list < $subch_p/chpids
+        read -a chpids <<< "$chpid_list"
+        if [ ${#chpids[@]} -ne 8 ]; then
+            error $"sysfs reported ${#chpids[@]} CHPIDs instead of expected 8"
+        fi
+        read pim pam pom foo < $subch_p/pimpampom
+        pimchpidZ=""
+        local chp
+        for ((chp=0; chp < 8; chp++)); do
+            mask=$((0x80 >> chp))
+            if (( 0x$pim & $mask )); then
+                pimchpidZ=${pimchpidZ}${chpids[chp]}
+            else
+                pimchpidZ=${pimchpidZ}"ZZ"
+            fi
+        done
+        # get device information...
+        read cutype < $dev_p/cutype
+        read active < $dev_p/online
+        # skip already active subchannels and those that are already in a
+        # ccwgroup and thus not available any more:
+        [ $active == "1" ] && continue
+        [ -h $dev_p/group_device ] && continue
+        # get chpid information...
+        pimchpids=${pimchpidZ//ZZ/}
+        [ $pimchpids == "" ] && continue
+        # Taking the first 2 hex digits as CHPID relies somewhat on the fact
+        # that network adaptors don't use multipathing and only have one CHP.
+        # Anyway it's OK since we're only interested in CHPID type and I guess
+        # this should be equal for all possible multipaths to the same device.
+        chpid=${pimchpids:0:2}
+        chpid_p=css$prefix/chp$prefix.$chpid
+        read chptype < $chpid_p/type
+        # filter and output...
+        if search_cu $cutype; then
+            if [ "${CU_DEVDRV[$cu_idx]}" == "ctc" ]; then
+                # assume CTC are mostly virtual and ignore chpid from sysfs
+                chpidtype_symbolic="-"
+            else
+                search_chpt $chptype
+            fi
+            echo $pimchpids $devbusid $STARTPATH/$dev_p $cutype $chpidtype_symbolic $chptype ${CU_DEVDRV[$cu_idx]} ${CU_DEVNAME[$cu_idx]} ${CU_GROUPCHANNELS[$cu_idx]} $cu_idx ${CU_CARDTYPE[$cu_idx]}
+        else
+            debug 5 " skip non-network device $devbusid CU $cutype"
+        fi
+    done
+}
+
+# search_groups:
+#
+# Prints enumeration list on standard output consisting of possible
+# hardware configurations (ccwgroups) for network devices on s390.
+# Each configuration suggestion includes corresponding attributes
+# that are of potential interest for the user and fit in a fixed column
+# table on an 80 column screen.
+#
+# PRECONDITION: Standard input has to be stably sorted by device bus IDs and
+# then by CHPIDs, i.e. grouped by CHPIDs.
+#
+function search_groups() {
+    local w_prefix w_ssid w_devno
+    local d_prefix d_ssid d_devno
+    local prefix ssid devno x
+    local chp devbusid dev_p cutype chpidtypename chptype devdrv devname groupchs cu_idx cardtype
+    # remembered last state variables for possible ccwgroup:
+    local r_prefix="Z"
+    local r_ssid="Z"
+    local r_devno="ZZZZ"
+    local r_chp="ZZ"
+    local r_cutype="ZZZZ/ZZ"
+    local count=0
+    local item=1
+    local skipped=0
+    # currently unused are: dev_p,chptype, cu_idx.
+    while read chp devbusid dev_p cutype chpidtypename chptype devdrv devname groupchs cu_idx cardtype; do
+        debug 1 " # $chp $devbusid $dev_p $cutype $chpidtypename $chptype $devdrv $devname $groupchs $cu_idx $cardtype"
+        IFS=.
+        read prefix ssid devno x <<< "$devbusid"
+        unset IFS
+        if [ $r_chp != $chp \
+            -o $r_prefix != $prefix \
+            -o $r_ssid != $ssid \
+            -o $r_cutype != $cutype ]; then
+            # restart with new read channel info and remember it
+            r_prefix=$prefix
+            r_ssid=$ssid
+            r_devno=$devno
+            r_chp=$chp
+            r_cutype=$cutype
+            count=1
+            debug 2 " INFO: restart on different CHPID or prefix or CUtype/model"
+            continue
+        fi
+        count=$((count + 1))
+        if [ $count -eq 2 ]; then
+            # about to check if write channel is one above read channel
+            if [ $((0x$devno)) -ne $((0x$r_devno + 1)) ]; then
+                # start with new read channel info
+                r_prefix=$prefix
+                r_ssid=$ssid
+                r_devno=$devno
+                r_chp=$chp
+                r_cutype=$cutype
+                count=1
+                skipped=$((skipped + 1))
+                # unimplemented possible packed channel usage option:
+                # remember unused channels for later use as data channel
+                debug 2 " INFO: restart on unmatching read channel"
+                continue
+            fi
+            w_prefix=$prefix
+            w_ssid=$ssid
+            w_devno=$devno
+        elif [ $count -eq 3 ]; then
+            # remember data channel info
+            d_prefix=$prefix
+            d_ssid=$ssid
+            d_devno=$devno
+        fi
+        debug 2 " INFO: groupchs=$groupchs count=$count"
+        if [ $count -ne $groupchs ]; then
+            debug 2 " INFO: skip"
+            continue
+        fi
+        # found possible ccwgroup
+        case $count in
+            2)
+                chlist=$r_prefix.$r_ssid.$r_devno,$w_prefix.$w_ssid.$w_devno
+                ;;
+            3)
+                chlist=$r_prefix.$r_ssid.$r_devno,$w_prefix.$w_ssid.$w_devno,$d_prefix.$d_ssid.$d_devno
+                ;;
+            *)
+                error $"unknown number of channels for group, code needs to be fixed"
+                ;;
+        esac
+        echo $item $cutype $chp $chpidtypename $devdrv $devname $chlist "$cardtype"
+        item=$((item + 1))
+        # restart after successful detection
+        r_prefix="Z"
+        count=0
+    done
+    debug 1 " STATISTIC: skipped $skipped devnos because of unmatching read channel"
+}
+
+build_list |
+# stable sort by device bus IDs and then by CHPIDs => grouped by CHPIDs
+# (sorting only works since keys are fixed no. of digits with leading zeros!)
+sort -s -k 1,1 -k 2,2 |
+#cat ; exit # move at desired line and uncomment to see intermediate output
+search_groups
Index: s390-tools-sles11/zconf/znetconf
===================================================================
--- /dev/null
+++ s390-tools-sles11/zconf/znetconf
@@ -0,0 +1,1418 @@
+#!/bin/bash
+#
+# znetconf - list and configure network devices
+#
+# Tool for the automatic and semi-automatic configuration of network devices
+# from ccw devices, including network device option handling and network
+# device removal.
+#
+# Copyright IBM Corp. 2009
+# Author(s): Einar Lueck <elelueck@de.ibm.com>
+#            Steffen Maier <maier@de.ibm.com>
+#
+
+#==============================================================================
+# constants
+#==============================================================================
+
+CMD=$(basename $0)
+LSZNET=/lib/s390-tools/lsznet.raw
+LSZNET_ARGS=-a
+LSZNET_CALL="$LSZNET $LSZNET_ARGS"
+UDEVSETTLE=/sbin/udevadm
+if [ ! -e $UDEVSETTLE ]
+then
+	UDEVSETTLE=/sbin/udevsettle
+	UDEVSETTLE_CALL="$UDEVSETTLE --timeout=10"
+else
+	UDEVSETTLE_CALL="$UDEVSETTLE settle --timeout=10"
+fi
+SYSFSDIR=$(cat /proc/mounts|awk '$3=="sysfs"{print $2; exit}')
+CCWGROUPBUS_DIR=$SYSFSDIR/bus/ccwgroup
+CCWGROUPBUS_DEVICEDIR=$CCWGROUPBUS_DIR/devices
+CCWGROUPBUS_DRIVERDIR=$CCWGROUPBUS_DIR/drivers
+DEVNOSEP=","
+readonly ERRCODE_NO_LAYER_DETECTED=255
+
+FORMAT_FULLDEVNO=^[[:xdigit:]]\\.[[:xdigit:]]\\.[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]$
+FORMAT_PARTDEVNO=^[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]$
+FORMAT_DEVLIST=^[[:xdigit:]]\\.[[:xdigit:]]\\.[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]],[[:xdigit:]]\\.[[:xdigit:]]\\.[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]\(,[[:xdigit:]]\\.[[:xdigit:]]\\.[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]\)?$
+FORMAT_SHORTDEVLIST=^[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]],[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]\(,[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]\)?$
+
+#==============================================================================
+# global variables
+#==============================================================================
+
+ATTRIBUTE_COUNT=0
+ATTRIBUTE_NAME=()
+ATTRIBUTE_VALUE=()
+IS_VM_ENV=0
+
+#==============================================================================
+# functions
+#==============================================================================
+
+function print_error()
+{
+	local ERRMSG="$@"
+	echo "$CMD: Error: $ERRMSG" >&2
+}
+
+#==============================================================================
+
+function print_warning()
+{
+	local ERRMSG="$@"
+	echo "$CMD: Warning: $ERRMSG" >&2
+}
+
+#==============================================================================
+
+function check_vm_env()
+{
+	if modprobe vmcp 2> /dev/null ; then
+		IS_VM_ENV=1
+	else
+		IS_VM_ENV=0
+	fi
+}
+
+#==============================================================================
+
+function print_usage()
+{
+	cat <<-EOD
+Usage: $CMD COMMANDS [OPTIONS]
+
+  znetconf -a <device_bus_id>
+  znetconf -A [-o ATTR=VALUE]+ [-d DRIVER] [-e <device_bus_id>]+
+  znetconf -r <device_bus_id> | -R [-e <device_bus_id>]+
+  znetconf -u | -c
+
+List and configure network devices.
+
+COMMANDS:
+  -u, --unconfigured		List posssible network devices
+  -c, --configured		List configured network devices
+  -a, --add <device_bus_id>[,..]Configure network device with the device bus-IDs
+  -A, --add-all			Configure all possible network devices
+  -r, --remove <device_bus_id>	Remove network device with given <device_bus_id>
+  -R, --remove-all		Remove all network devices
+  -h, --help			Print this help, then exit
+  -v, --version			Print version infromation, then exit
+
+OPTIONS:
+  -o, --option ATTR=VALUE	Configure device with specified option
+  -d, --driver DRIVER		Configure device with specified driver
+  -n, --non-interactive		Answer all confirmation questions with 'yes'
+  -e, --except			Do not add or remove the specified device
+	EOD
+}
+
+#==============================================================================
+
+function print_version()
+{
+	echo "$CMD: version %S390_TOOLS_VERSION%"
+	echo "Copyright IBM Corp. 2009"
+}
+
+#==============================================================================
+
+function print_short_usage()
+{
+	echo "znetconf: Need one of the options -u, -c, -a, -A, -r or -R"
+	echo "Use 'znetconf --help' to get usage information"
+}
+
+#==============================================================================
+
+function print_scanning_4_nwdevices()
+{
+	printf "%s\n" "Scanning for network devices..."
+}
+
+#==============================================================================
+
+function lookup_vswitch_layer()
+{
+	local SWITCHNAME="$1"
+	local LAYER2=$ERRCODE_NO_LAYER_DETECTED
+
+	LAYER2=$(vmcp "q vswitch $SWITCHNAME" 2> /dev/null |
+	{
+		local FIELD1
+		local FIELD2
+		local FIELD3
+		local REST
+		local LAYER2=$ERRCODE_NO_LAYER_DETECTED
+		local LINENO=1
+		while read FIELD1 FIELD2 FIELD3 REST
+		do
+			if [ $LINENO -eq 2 ]
+			then
+				if [ "$FIELD3" == "ETHERNET" ]
+				then
+					LAYER2=1
+				elif [ "$FIELD3" == "NONROUTER" ] ||
+					[ "$FIELD3" == "PRIROUTER" ]
+				then
+					LAYER2=0
+				fi
+			fi
+			let "LINENO++"
+		done
+		echo $LAYER2
+	})
+
+	return $LAYER2
+}
+
+#==============================================================================
+
+function lookup_lan_layer()
+{
+	local NAME="$1"
+	local OWNER="$2"
+	if [ "$NAME" != "*" ]
+	then
+		local LAYER2=$(vmcp "q lan $NAME owner $OWNER" 2> /dev/null |
+		{
+			local FIELD1
+			local FIELD2
+			local FIELD3
+			local REST
+			local LAYER2=$ERRCODE_NO_LAYER_DETECTED
+			local LINENO=1
+			while read FIELD1 FIELD2 FIELD3 REST
+			do
+				if [ $LINENO -eq 2 ]
+				then
+					if [ "$FIELD3" == "ETHERNET" ]
+					then
+						LAYER2=1
+					elif [ "$FIELD3" == "IP" ]
+					then
+						LAYER2=0
+					fi
+				fi
+				let "LINENO++"
+			done
+			echo $LAYER2
+		})
+		return $LAYER2
+	fi
+	return $ERRCODE_NO_LAYER_DETECTED
+}
+
+#==============================================================================
+
+function lookup_type_and_lan_or_vswitch_name()
+{
+	local DEVNO="$1"
+	vmcp "q nic $DEVNO" 2> /dev/null |
+	{
+		local FIELD1
+		local FIELD2
+		local FIELD3
+		local FIELD4
+		local REST
+		local TYPE="ERROR"
+		local TARGET="ERROR"
+		local NAME="ERROR"
+		local OWNER="ERROR"
+		local LINENO=1
+		while read FIELD1 FIELD2 FIELD3 FIELD4 FIELD5 REST
+		do
+			if [ $LINENO -eq 1 ]
+			then
+				TYPE="$FIELD4"
+			elif [ $LINENO -eq 2 ]
+			then
+				if [ "$FIELD3" == "LAN:" ]
+				then
+					TARGET="LAN"
+					OWNER="$FIELD4"
+					NAME="$FIELD5"
+				elif [ "$FIELD3" == "VSWITCH:" ]
+				then
+					TARGET="VSWITCH"
+					OWNER="$FIELD4"
+					NAME="$FIELD5"
+				fi
+			fi
+			let "LINENO++"
+		done
+		echo "$TYPE $TARGET $OWNER $NAME"
+	}
+}
+
+#==============================================================================
+
+function lookup_layer()
+{
+	local DEVNO=$1
+	local LAYER2=$ERRCODE_NO_LAYER_DETECTED
+	local QRESULT
+	local VSWITCH
+	local LAN
+
+	# query VM for the nic
+	QRESULT=$(lookup_type_and_lan_or_vswitch_name "$DEVNO")
+
+	# derive type of nic
+	local TYPE=${QRESULT%% *}
+	local TARGET_OWNER=${QRESULT#* }
+	TARGET_OWNER=${TARGET_OWNER% *}
+	local TARGET=${TARGET_OWNER% *}
+	local OWNER=${TARGET_OWNER#* }
+	local NAME=${QRESULT##* }
+	if [ "$TYPE" == "HIPERS" ]
+	then
+		# HIPERSOCKET in z/VM always indicates layer 3
+		LAYER2=0
+	elif [ "$TYPE" == "QDIO" ]
+	then
+		if  [ "$TARGET" == "VSWITCH" ]
+		then
+			lookup_vswitch_layer "$NAME"
+			LAYER2=$?
+		elif [ "$TARGET" == "LAN" ]
+		then
+			lookup_lan_layer "$NAME" "$OWNER"
+			return $?
+		fi
+	fi
+
+	return $LAYER2
+}
+
+#==============================================================================
+
+#
+# group_device
+# 	$1: ccwdevid[;ccwdevid][;ccwdevid]
+#	$2: ccwgroupdevid
+#	$3: driver
+# returns
+#	0 success
+#	1 group file not found
+#	2 grouping failed
+#
+function group_device()
+{
+	local DEVICES=$1
+	local TARGET_DEVID=$2
+	local DRIVER=$3
+	local GROUPFILE=$CCWGROUPBUS_DRIVERDIR/$DRIVER/group
+	local CTCMGROUPFILE=$CCWGROUPBUS_DRIVERDIR/ctcm/group
+	local CTCGROUPFILE=$CCWGROUPBUS_DRIVERDIR/ctc/group
+
+	# check if group file exists
+	if [ ! -e $GROUPFILE ]
+	then
+		# try to load driver
+		if ! modprobe $DRIVER &> /dev/null
+		then
+			if [ $DRIVER = "ctc" ]
+			then
+				# check if ctcm driver exists
+				if modprobe ctcm &> /dev/null
+				then
+					GROUPFILE=$CTCMGROUPFILE
+				fi
+			elif [ $DRIVER = "ctcm" ]
+			then
+				# check if ctc driver exists
+				if modprobe ctc &> /dev/null
+				then
+					GROUPFILE=$CTCGROUPFILE
+				fi
+			fi
+		fi
+	fi
+	if [ -e $GROUPFILE ]
+	then
+		echo "$DEVICES" >> $GROUPFILE 2> /dev/null
+		case $? in
+			0)
+				;;
+			*)
+				print_error "Could not group devices" \
+					"$DEVICES"
+				return 2
+				;;
+		esac
+	else
+		print_error "$GROUPFILE does not exist"
+
+		return 1
+	fi
+	return 0
+}
+
+#==============================================================================
+
+#
+# ungroup_device
+#	$1: ccwgroupdevid
+#	$2: network device name (just for display purposes)
+# returns
+#	0 success
+#	1 group file not found
+#	2 ungrouping failed
+#
+function ungroup_device()
+{
+	local TARGET_DEVID="$1"
+	local DEVNAME="$2"
+	local DEVICE_DIRECTORY="$CCWGROUPBUS_DEVICEDIR/$TARGET_DEVID"
+	local DEVICE_UNGROUPFILE="$DEVICE_DIRECTORY/ungroup"
+	if [ -e $DEVICE_UNGROUPFILE ]
+	then
+		echo "1" >> $DEVICE_UNGROUPFILE 2> /dev/null
+		case $? in
+			0)
+				echo "Successfully removed device" \
+					"$TARGET_DEVID ($DEVNAME)"
+				;;
+			*)
+				print_error "Failed to ungroup $TARGET_DEVID"
+				return 2
+				;;
+		esac
+	else
+		print_error "$DEVICE_UNGROUPFILE does not exist."
+		return 1
+	fi
+	return 0
+}
+
+#==============================================================================
+
+#
+# try_read_netdevname
+# 	$1: ccwgroupdevno
+# returns
+#	0 success
+#	1 failed. giving up after retries
+# stdout
+#	in case of success: device name
+#
+function try_read_netdevname()
+{
+	local CCWGROUPDEVNO="$1"
+	local IF_NAME_FILE="$CCWGROUPBUS_DEVICEDIR/$CCWGROUPDEVNO/if_name"
+	local NET_SUBDIR="$CCWGROUPBUS_DEVICEDIR/$CCWGROUPDEVNO/net/"
+	local IF_NAME=""
+	local rc=1
+
+	# check if interface file containing the name exists
+	if [ -e "$IF_NAME_FILE" ]
+	then
+		read IF_NAME < $IF_NAME_FILE
+		rc=0
+	elif [ -d "$NET_SUBDIR" ]
+	then
+		IF_NAME=$(ls $NET_SUBDIR)
+	else
+		# if the file does not exist
+		local LINKNAME=$(find $CCWGROUPBUS_DEVICEDIR/$CCWGROUPDEVNO/ -type l -name net*)
+		if [[ ! -z $LINKNAME ]]
+		then
+			IF_NAME=$(readlink $LINKNAME)
+			IF_NAME=${IF_NAME##*/}
+			rc=0
+		fi
+	fi
+	echo $IF_NAME
+	return $rc
+}
+
+#==============================================================================
+
+#
+# wait_for_net_device
+# 	$1: ccwgroupdevno
+# returns
+#	0 success
+#	1 failed. giving up after retries
+#
+function wait_for_net_device()
+{
+	local CCWGROUPDEVNO="$1"
+	local retries=0
+	local MAX_RETRIES=10
+	local IF_NAME_FILE="$CCWGROUPBUS_DEVICEDIR/$CCWGROUPDEVNO/if_name"
+	local IF_NAME=""
+	local CMD_FINDNETLINK="find $CCWGROUPBUS_DEVICEDIR/$CCWGROUPDEVNO/ -type l -name net*"
+	local LINKNAME=""
+
+	# polling loop to wait for net device to become available
+	if [ -e $UDEVSETTLE ]
+	then
+		$UDEVSETTLE_CALL
+	fi
+	IF_NAME=$(try_read_netdevname $CCWGROUPDEVNO)
+	if [[ -z "$IF_NAME" ]]
+	then
+		echo "Now waiting for device $CCWGROUPDEVNO to get available..."
+	fi
+	while [[ -z "$IF_NAME" ]] && [[ retries -lt MAX_RETRIES ]]
+	do
+		sleep 1
+		retries=$retries+1
+		IF_NAME=$(try_read_netdevname $CCWGROUPDEVNO)
+	done
+	if [ ! -z "$IF_NAME" ]
+	then
+		echo "Successfully configured device $CCWGROUPDEVNO ($IF_NAME)"
+	else
+		print_error "Failed to make $CCWGROUPDEVNO online."
+		return 1
+	fi
+}
+
+#==============================================================================
+
+#
+# switch_device
+# 	$1: ccwgroupdevno
+#	$2: 1|0 (online|offline)
+# returns
+#	0 success
+#	1 command to make device online/offline returned error
+#	2 online file does not exist
+#
+function switch_device()
+{
+	local CCWGROUPDEVNO="$1"
+	local SWITCHVALUE="$2"
+	local DEVICE_DIRECTORY="$CCWGROUPBUS_DEVICEDIR/$CCWGROUPDEVNO"
+	local ONLINE_FILE="$DEVICE_DIRECTORY/online"
+	local STATESTR="online"
+
+	if [ $SWITCHVALUE -eq 0 ]
+	then
+		STATESTR="offline"
+	fi
+
+	if [ -f $ONLINE_FILE ]
+	then
+		echo "$SWITCHVALUE" >> $ONLINE_FILE
+		case $? in
+			0)
+				;;
+			*)
+				print_error "Failed to make $CCWGROUPDEVNO" \
+					"$STATESTR"
+				return 1
+				;;
+		esac
+	else
+		print_error "$ONLINE_FILE does not exist."
+		return 2
+	fi
+
+	return 0
+}
+
+#==============================================================================
+
+#
+# configure_ccwgroupdev_option
+#	$1: ccwgroupdevid
+#	$2: option_name
+#	$3: option_value
+# returns
+#	0 success
+# 	1 option not allowed to be set (e.g. online)
+#	2 unknown option
+#	3 configuration failed
+#
+function configure_ccwgroupdev_option()
+{
+	local CCWGROUPDEVID="$1"
+	local OPTION_NAME="$2"
+	local OPTION_VALUE="$3"
+
+	# filter some attributes away
+	if [ "$OPTION_NAME" == "online" ]
+	then
+		print_error "Ignoring option $OPTION_NAME=$OPTION_VALUE"
+		return 1
+	fi
+
+	# check if attribute exists
+	local ATTRFILE="$CCWGROUPBUS_DEVICEDIR/$CCWGROUPDEVID/$OPTION_NAME"
+	if [ -f $ATTRFILE ]
+	then
+		echo $OPTION_VALUE >> $ATTRFILE 2> /dev/null
+		case $? in
+			0)
+				;;
+			*)
+				print_error "Failed to configure" \
+					"$OPTION_NAME=$OPTION_VALUE"
+				return 3
+				;;
+		esac
+	else
+		print_error "$ATTRFILE does not exist"
+		return 2
+	fi
+	return 0
+}
+
+#==============================================================================
+
+#
+# add_net_device
+# 	$1: ccwdevid[;ccwdevid][;ccwdevid]
+#	$2: ccwgroupdevid
+#	$3: driver
+#	$4: opt_count
+#	$5..$(5+opt_count): opt_name_array
+#	$5+opt_count..5+2*opt_count: opt_value_array
+# returns
+#	0 success
+#	1 grouping failed
+#	2 failed to make device online
+#	3 net device did not become available
+#	4 device online but some option could not be set
+#
+function add_net_device()
+{
+	local DEVICES="$1"
+	local CCWGROUPDEVID="$2"
+	local DRIVER="$3"
+	local OPT_COUNT=$4
+	local RAW=( "$@" )
+	local OPT_NAME_ARRAY=("${RAW[@]:4:$OPT_COUNT}")
+	local OPT_VALUE_ARRAY=("${RAW[@]:$[4 + $OPT_COUNT]:$OPT_COUNT}")
+
+	group_device $DEVICES $CCWGROUPDEVID $DRIVER
+	if [ $? -ne 0 ]
+	then
+		return 1
+	fi
+
+	local i=0
+	local SOMEOPTION_FAILED=0
+	local HAS_LAYER2_OPTION=0
+	while [ $i -lt $OPT_COUNT ]
+	do
+		configure_ccwgroupdev_option $CCWGROUPDEVID \
+			 ${OPT_NAME_ARRAY[$i]} ${OPT_VALUE_ARRAY[$i]}
+		if [ $? -ne 0 ]
+		then
+			SOMEOPTION_FAILED=1
+		fi
+		if [ "${OPT_NAME_ARRAY[$i]}" == "layer2" ]
+		then
+			HAS_LAYER2_OPTION=1
+		fi
+		let "i++"
+	done
+
+	# in case of qeth and no given layer2 option try to determine layer
+	# automatically
+	if [ $IS_VM_ENV -eq 1 ] && [ $HAS_LAYER2_OPTION -eq 0 ] &&
+		[ "$DRIVER" == "qeth" ]
+	then
+		lookup_layer ${CCWGROUPDEVID##*.}
+		local LAYER2=$?
+		if [ $LAYER2 -eq 0 ] || [ $LAYER2 -eq 1 ]
+		then
+			configure_ccwgroupdev_option $CCWGROUPDEVID \
+				"layer2" "$LAYER2"
+		fi
+	fi
+
+	switch_device $CCWGROUPDEVNO 1
+	if [ $? -ne 0 ]
+	then
+		return 2
+	fi
+
+	wait_for_net_device $CCWGROUPDEVNO
+	if [ $? -ne 0 ]
+	then
+		return 3
+	fi
+
+	if [ $SOMEOPTION_FAILED -ne 0 ]
+	then
+		return 4
+	fi
+
+	return 0
+}
+
+#==============================================================================
+
+#
+# remove_net_device
+#	$1: ccwgroupdevid
+#	$2: network interface name (just for display purposes)
+# returns
+#	0 success
+#	1 making device offline failed
+#	2 ungrouping device failed
+#
+function remove_net_device()
+{
+	local DEVICE_TO_REMOVE="$1"
+	local DEVNAME="$2"
+
+	switch_device $DEVICE_TO_REMOVE 0
+	if [ $? -ne 0 ]
+	then
+		return 1
+	fi
+
+	ungroup_device $DEVICE_TO_REMOVE $DEVNAME
+	if [ $? -ne 0 ]
+	then
+		return 2
+	fi
+	return 0
+}
+
+#==============================================================================
+
+#
+# list_configured
+#	$1 supress_header
+# returns
+#	0 success
+#
+function list_configured()
+{
+	supress_header=0
+	if [ $# -ge 1 ]
+	then
+		supress_header=$1
+	fi
+
+	local LIST_FORMAT_STRING="%-26.26s %-7.7s %-14.14s %5.5s %-4.4s %-11.11s %-7.7s\n"
+	if [ $supress_header -eq 0 ]
+	then
+		printf "$LIST_FORMAT_STRING" "Device IDs" "Type" \
+			"Card Type" "CHPID" "Drv." "Name" "State"
+		echo "--------------------------------------------------------------------------------"
+	fi
+
+	NETWORK_DEVICES=$(find $CCWGROUPBUS_DEVICEDIR -type l)
+	for d in $NETWORK_DEVICES
+	do
+		# determine dev no
+		local DEVNO=${d##*/*/}
+		DRIVER=""
+		local DEVNAME=""
+
+		# determine interface name, if there (only for qeth and if
+		# device is online)
+		if [ -f $d/if_name ]
+		then
+			read DEVNAME < $d/if_name
+		elif [ -d $d/net ]
+		then
+			DEVNAME=$(ls $d/net/)
+		fi
+
+		# read all links and parse driver, device name and ccw device
+		# bus-ids
+		ls -l $d/ | grep '^l' |
+		{
+
+			while read line
+			do
+				local LINKNAME=${line// ->*/""}
+				LINKNAME=${LINKNAME##* }
+				if [ "$LINKNAME" = "driver" ]
+				then
+					DRIVER=${line##* -> */}
+				elif [[ "$LINKNAME" =~ "cdev" ]]
+				then
+					CDEVNOS="$CDEVNOS$DEVNOSEP${line//*..\/*\//}"
+				# the following is necessary if the driver is
+				# not qeth
+				elif [[ -z "$DEVNAME" ]] && [[ "$LINKNAME" =~ "net" ]]
+				then
+					DEVNAME=${line##* -> */}
+				fi
+			done
+
+			local CUTYPE=""
+			if [ -e $d/cdev0/cutype ]
+			then
+				read CUTYPE < $d/cdev0/cutype
+			fi
+
+			read ONLINE < $d/online
+			if [ $ONLINE -eq 1 ]
+			then
+				ONLINE_STATE="online"
+			else
+				ONLINE_STATE="offline"
+			fi
+			local TYPEFILE="$d/card_type"
+			if [ ! -f $TYPEFILE ]
+			then
+				TYPEFILE="$d/type"
+			fi
+			read CARDTYPE < $TYPEFILE
+			local CHPID=""
+			if [ -e $d/chpid ]
+			then
+				read CHPID < $d/chpid
+			fi
+			printf "$LIST_FORMAT_STRING" "${CDEVNOS#$DEVNOSEP}" \
+				"$CUTYPE" "$CARDTYPE" \
+				"$CHPID" "$DRIVER" "$DEVNAME" "$ONLINE_STATE"
+		}
+	done
+	return 0
+}
+
+#==============================================================================
+
+#
+# print_list_unconf_header
+# 	$1: format string
+# returns
+# 	0
+function print_list_unconf_header()
+{
+	local LIST_FORMATSTR="$1"
+	printf "$LIST_FORMATSTR" "Device IDs" "Type" "Card Type" "CHPID" "Drv."
+	echo "------------------------------------------------------------"
+	return 0
+}
+
+#==============================================================================
+
+#
+# list_unconfigured
+# returns
+#	0 success
+#
+function list_unconfigured()
+{
+	local PRINTED_HEADLINES=0
+	local LIST_FORMATSTR="%-26.26s %-7.7s %-14.14s %5.5s %-4.4s \n"
+	print_scanning_4_nwdevices
+
+	$LSZNET_CALL |
+	{
+		while read no cutype chp devtype devdrv devname chlist cardtype
+		do
+			if [ $PRINTED_HEADLINES -eq 0 ]
+			then
+				print_list_unconf_header "$LIST_FORMATSTR"
+				PRINTED_HEADLINES=1
+			fi
+			printf "$LIST_FORMATSTR" "$chlist" "$cutype" \
+				"$cardtype" "$chp" "$devdrv"
+		done
+	}
+}
+
+#==============================================================================
+
+#
+# store_option
+#	$1: attribute=value
+# returns
+#	0 success
+#	1 attribute starts with a - / or is invalid
+#	2 missing value
+#
+function store_option()
+{
+	local OPTIONSTRING="$1"
+
+	# ensure that there is no option intepreted as an
+	# attribute value pair
+	[[ "$OPTIONSTRING" =~ ^- ]]
+	case $? in
+		0)
+			print_error "$OPTIONSTRING is not a valid attribute" \
+				"value pair"
+			exit 1
+			;;
+		1)
+			# option considered ok
+			;;
+		2)
+			print_error "Internal error"
+			exit 1
+	esac
+
+	# do the parsing
+	local NAME=$(echo $OPTIONSTRING|cut -d= -f1)
+	local VALUE=$(echo $OPTIONSTRING|cut -d= -f2)
+	if [ -z "$NAME" ]
+	then
+		print_error "Attribute name missing for -o|--option"
+		return 1
+	fi
+	if [ -z "$VALUE" ]
+	then
+		print_error "Missing value for attribute $NAME"
+		return 2
+	fi
+	ATTRIBUTE_COUNT=$[ $ATTRIBUTE_COUNT + 1 ]
+	ATTRIBUTE_NAME[$ATTRIBUTE_COUNT]="$NAME"
+	ATTRIBUTE_VALUE[$ATTRIBUTE_COUNT]="$VALUE"
+	return 0
+}
+
+#==============================================================================
+
+#
+# is_complete_ccwdevbusid
+#	$1: possibly correct ccw device bus id
+# returns
+#	0 if the given string is a correctly formatted ccw device bus id
+#	1 else
+#
+function is_complete_ccwdevbusid()
+{
+	local DEV="$1"
+	[[ "$DEV" =~ $FORMAT_FULLDEVNO ]]
+	case $? in
+		0)
+			return 0
+			;;
+		1)
+			return 1
+			;;
+		2)
+			print_error "Internal error"
+			exit 1
+			;;
+	esac
+	return 0
+}
+
+#==============================================================================
+
+#
+# is_partial_ccwdevbusid
+#	$1: possibly correct partial ccw device bus id
+# returns
+#	0 if the given string is a correctly formatted partial ccw device
+#	  bus id
+#	1 else
+#
+function is_partial_ccwdevbusid()
+{
+	local DEV="$1"
+	[[ "$DEV" =~ $FORMAT_PARTDEVNO ]]
+	case $? in
+		0)
+			return 0
+			;;
+		1)
+			return 1
+			;;
+		2)
+			print_error "Internal error"
+			exit 1
+			;;
+	esac
+}
+
+#==============================================================================
+
+#
+# is_ccwdevbusid_list
+#	$1: possibly correct list of ccw device bus ids
+# returns
+#	0 if the given string is a correctly formatted list of ccw device
+# 	  bus ids
+#	1 else
+#
+function is_ccwdevbusid_list()
+{
+	local DEVLIST="$1"
+	[[ "$DEVLIST" =~ $FORMAT_DEVLIST ]]
+	case $? in
+		0)
+			return 0
+			;;
+		1)
+			return 1
+			;;
+		2)
+			print_error "Internal error"
+			exit 1
+			;;
+	esac
+}
+
+
+#==============================================================================
+
+#
+# is_shortccwdevbusid_list
+#	$1: possibly correct list of short ccw device bus ids
+# returns
+#	0 if the given string is a correctly formatted list of short ccw device
+# 	  bus ids
+#	1 else
+#
+function is_shortccwdevbusid_list()
+{
+	local DEVLIST="$1"
+	[[ "$DEVLIST" =~ $FORMAT_SHORTDEVLIST ]]
+	case $? in
+		0)
+			return 0
+			;;
+		1)
+			return 1
+			;;
+		2)
+			print_error "Internal error"
+			exit 1
+			;;
+	esac
+}
+
+#==============================================================================
+
+#
+# is_supported_driver
+#	$1: possibly supported driver
+# returns
+#	0 if the given string denotes a supported driver
+#	1 else
+#
+function is_supported_driver()
+{
+	local DRIVER="$1"
+	[[ "$DRIVER" =~ "^(qeth|lcs|ctc|ctcm)$" ]]
+	case $? in
+		0)
+			return 0
+			;;
+		1)
+			return 1
+			;;
+		2)
+			print_error "Internal error"
+			exit 1
+			;;
+	esac
+}
+
+#==============================================================================
+
+#
+# ask_for_remove
+#	$1: interaction not allowed (0 == interaction allowed)
+# returns
+#	if interaction is allowed and the given question was answered with y|Y
+#	or no interaction is allowed
+# exits
+# 	with error code 0 in any other case
+#
+function ask_for_remove()
+{
+	local INTERACTION_NOT_ALLOWED=$1
+
+	if [ "$INTERACTION_NOT_ALLOWED" -eq 0 ]
+	then
+		printf "%s" "Do you want to continue (y/n)?"
+		read REMOVE_ANSWER
+		if [[ "$REMOVE_ANSWER" != "y" ]] &&
+			[[ "$REMOVE_ANSWER" != "Y" ]]
+		then
+			exit 0
+		fi
+	fi
+	return 0
+}
+
+#==============================================================================
+
+#
+# extract_interface_name
+# 	$1: list_configured line of a network device
+# returns
+#	0
+# environment
+#	REPLY: interface name
+function extract_interface_name()
+{
+	local CFGLINE="$1"
+	local IF_NAME=$(expr substr "$CFGLINE" 62 11)
+	REPLY=${IF_NAME%% *}
+	return 0
+}
+
+#==============================================================================
+# main
+#==============================================================================
+
+# check consistency of installtion
+if [ ! -e "$LSZNET" ]
+then
+	print_error "Could not find tool at $LSZNET"
+	exit 1
+fi
+
+# determine environment
+check_vm_env
+
+# parse command line
+DO_LIST_CONFIGURED=0
+DO_LIST_UNCONFIGURED=0
+DO_ADDALL=0
+DO_ADD=0
+DEVICE_TO_ADD=""
+DO_APPEND_OPTIONS=0
+DO_LIST=0
+DO_REMOVE=0
+DEVICE_TO_REMOVE=""
+DO_REMOVEALL=0
+NONINTERACTIVE=0
+EXCEPT=""
+if [ $# -gt 0 ]
+then
+	while [ $# -gt 0 ]
+	do
+		case $1 in
+			-h|--help)
+				print_usage
+				exit 0
+				;;
+			-v|--version)
+				print_version
+				exit 0
+				;;
+			-A|--add-all)
+				DO_ADDALL=1
+				shift
+				;;
+			-a|--add)
+				shift
+				if [ $# -lt 1 ]
+				then
+					print_error "Device ID expected"
+					exit 1
+				fi
+
+				# get parameter expected to be a
+				# ccw device bus id
+				DEVICE_TO_ADD="$1"
+				shift
+
+				# check syntax of ccw device bus id
+				if is_complete_ccwdevbusid "$DEVICE_TO_ADD" ||
+					is_ccwdevbusid_list "$DEVICE_TO_ADD"
+				then
+					DO_ADD=1
+				elif is_partial_ccwdevbusid "$DEVICE_TO_ADD"
+				then
+					DEVICE_TO_ADD="0.0.$DEVICE_TO_ADD"
+					DO_ADD=1
+				elif is_shortccwdevbusid_list "$DEVICE_TO_ADD"
+				then
+					DEVICE_TO_ADD="0.0.$DEVICE_TO_ADD"
+					DEVICE_TO_ADD="${DEVICE_TO_ADD//,/,0.0.}"
+					DO_ADD=1
+				else
+					print_error "Invalid device ID format" \
+						"$DEVICE_TO_ADD"
+
+					exit 1
+				fi
+
+				;;
+			-c|--configured)
+				DO_LIST_CONFIGURED=1
+				shift
+				;;
+			-d|--driver)
+				shift
+				if [ $# -lt 1 ]
+				then
+					print_error "Driver name expected"
+					exit 1
+				fi
+
+				# ensure driver is supported
+				DRIVER="$1"
+				shift
+				if [ "$DRIVER" != "" ] && \
+					! is_supported_driver "$DRIVER"
+				then
+					# unknown driver
+					print_error "Unknown driver $DRIVER"
+					exit 1
+				fi
+				;;
+			-o|--option)
+				DO_APPEND_OPTIONS=1
+				shift
+				if [ $# -lt 1 ]
+				then
+					print_error "Missing attrtibute" \
+						"value pair for -o|--" \
+						"option"
+					exit 1
+				fi
+
+				# get option string
+				OPTIONSTRING="$1"
+				shift
+
+				# store option
+				if ! store_option $OPTIONSTRING
+				then
+					exit $?
+				fi
+				;;
+			-r|--remove)
+				DO_REMOVE=1
+				shift
+
+				# ensure there is a further parameter
+				if [ $# -lt 1 ]
+				then
+					print_error "Expecting <device_bus_id>"\
+						"of device to be removed"
+					exit 1
+				fi
+
+				# get device to be removed
+				DEVICE_TO_REMOVE="$1"
+				shift
+
+				# validate it is a
+				# ccw dev bus id (short or long)
+				if is_partial_ccwdevbusid "$DEVICE_TO_REMOVE"
+				then
+					DEVICE_TO_REMOVE="0.0.$DEVICE_TO_REMOVE"
+				elif ! is_complete_ccwdevbusid \
+					"$DEVICE_TO_REMOVE"
+				then
+					print_error "Invalid device ID format" \
+						"$DEVICE_TO_REMOVE"
+					exit 1
+				fi
+				;;
+			-R|--remove-all)
+				DO_REMOVEALL=1
+				shift
+				;;
+			-e|--except)
+				DO_IGNORE_DEVNOS=1
+				shift
+
+				# ensure there is a further parameter
+				if [ $# -lt 1 ]
+				then
+					print_error "<device_bus_id> missing"\
+						"for -e|--except"
+					exit 1
+				fi
+
+				# get device to be removed
+				EXCEPT_DEVNO="$1"
+				shift
+
+				# validate it is a
+				# ccw dev bus id (short or long)
+				if is_partial_ccwdevbusid "$EXCEPT_DEVNO"
+				then
+					EXCEPT_DEVNO="0.0.$EXCEPT_DEVNO"
+				elif ! is_complete_ccwdevbusid \
+					"$EXCEPT_DEVNO"
+				then
+					print_error "Invalid device ID format" \
+						"$EXCEPT_DEVNO"
+					exit 1
+				fi
+
+				# create a filter statement
+				if [ "$EXCEPT" == "" ]
+				then
+					EXCEPT="$EXCEPT_DEVNO"
+				else
+					EXCEPT="$EXCEPT|$EXCEPT_DEVNO"
+				fi
+				;;
+			-u|--unconfigured)
+				DO_LIST_UNCONFIGURED=1
+				shift
+				;;
+			-n|--non-interactive)
+				NONINTERACTIVE=1
+				shift
+				;;
+			*)
+				echo "$CMD: Invalid option $1"
+				echo "Try '$CMD --help' for more information."
+				exit 1
+				;;
+		esac
+	done
+fi
+
+
+# check for too much arguments
+if [ $(($DO_ADD + $DO_ADDALL + $DO_LIST_UNCONFIGURED + $DO_LIST_CONFIGURED + \
+	 $DO_REMOVEALL + $DO_REMOVE)) -gt 1 ]
+then
+	print_error "Too much arguments"
+	echo "Try '$CMD --help' for more information."
+	exit 1
+fi
+
+# react to parsed options
+if [ $DO_ADD -eq 1 ]
+then
+	print_scanning_4_nwdevices
+
+	# scan for details like all CCW device bus IDs and the driver name
+	if ! is_ccwdevbusid_list "$DEVICE_TO_ADD"
+	then
+		# read the set of device numbers from the LSZNET
+		CANDIDATE=$($LSZNET_CALL|awk \
+			"\$7 ~ /$DEVICE_TO_ADD/ {print \$5,\$7}")
+		read PROPOSED_DRIVER DEVICES <<< "$CANDIDATE"
+		if [ "$DEVICES" == "" ]
+		then
+			print_error "No configuration found for device ID" \
+				"$DEVICE_TO_ADD"
+			exit 1
+		fi
+	else
+		# define set of device numbers to be used
+		DEVICES="$DEVICE_TO_ADD"
+
+		# try to find a driver for the given set of device numbers
+		CANDIDATE=$($LSZNET_CALL|
+			awk "\$7~/${DEVICE_TO_ADD//,// && \$7~/}/ {print \$5}")
+		read PROPOSED_DRIVER <<< "$CANDIDATE"
+	fi
+
+	# compute the expected group device number
+	CCWGROUPDEVNO=${DEVICES%%,*}
+
+	# check whether an appropriate driver was determined automatically or
+	# not, if not, one has to be given by the user via -d
+	if [ "$DRIVER" == "" ]
+	then
+		DRIVER="$PROPOSED_DRIVER"
+	fi
+	if [ "$DRIVER" == "" ]
+	then
+		print_warning "No driver found for $DEVICE_TO_ADD. Using qeth."
+		DRIVER="qeth"
+	fi
+	add_net_device $DEVICES $CCWGROUPDEVNO $DRIVER \
+		$ATTRIBUTE_COUNT "${ATTRIBUTE_NAME[@]}" "${ATTRIBUTE_VALUE[@]}"
+	exit $?
+fi
+if [ $DO_ADDALL -eq 1 ]
+then
+	print_scanning_4_nwdevices
+	ADDALL_RC=0
+	if [ "$EXCEPT" == "" ]
+	then
+		PATTERNSWITCH=""
+	else
+		PATTERNSWITCH="-v"
+	fi
+	PATTERN='([[:xdigit:]]|\.|,)*'
+	PATTERN=" $PATTERN$EXCEPT$PATTERN "
+	$LSZNET_CALL|grep $PATTERNSWITCH -E -e "$PATTERN"|awk '{print $5,$7}'| {
+		while read PROPOSED_DRIVER CCWDEVNOS
+		do
+			DRV="$DRIVER"
+			if [ "$DRV" == "" ]
+			then
+				DRV="$PROPOSED_DRIVER"
+			fi
+			CCWGROUPDEVNO=${CCWDEVNOS/,*/""}
+			if ! add_net_device $CCWDEVNOS $CCWGROUPDEVNO "$DRV" \
+				$ATTRIBUTE_COUNT "${ATTRIBUTE_NAME[@]}" \
+				"${ATTRIBUTE_VALUE[@]}"
+			then
+				ADDALL_RC=1
+			fi
+		done
+		exit "$ADDALL_RC"
+	}
+	exit $?
+fi
+if [ $DO_REMOVE -eq 1 ]
+then
+	# ensure that device exists
+	CONFIGURED_DEVICE_STRING=$(list_configured 1|
+		grep "$DEVICE_TO_REMOVE")
+	CCWDEVBUSIDS_TO_REMOVE=${CONFIGURED_DEVICE_STRING%% *}
+	CCWGROUPDEVID_TO_REMOVE=${CCWDEVBUSIDS_TO_REMOVE%%,*}
+
+	if [[ -z "$CCWGROUPDEVID_TO_REMOVE" ]]
+	then
+		print_error "$DEVICE_TO_REMOVE is not a configured device"
+		exit 1
+	fi
+
+	echo "Remove network device $CCWGROUPDEVID_TO_REMOVE" \
+	 	"($CCWDEVBUSIDS_TO_REMOVE)?"
+	echo "Warning: this may affect network connectivity!"
+	ask_for_remove "$NONINTERACTIVE" "$REMOVE_QUESTION"
+
+	# remove it
+	extract_interface_name "$CONFIGURED_DEVICE_STRING"
+	remove_net_device $CCWGROUPDEVID_TO_REMOVE $REPLY
+	exit $?
+fi
+if [ $DO_REMOVEALL -eq 1 ]
+then
+	if [ "$EXCEPT" == "" ]
+	then
+		echo "Remove all network devices"
+		PATTERNSWITCH=""
+	else
+		echo "Remove all network devices except ${EXCEPT//|/ and }"
+		PATTERNSWITCH="-v"
+	fi
+	echo "Warning: this will affect network connectivity!"
+	ask_for_remove "$NONINTERACTIVE"
+
+	PATTERN='([[:xdigit:]]|\.|,)*'
+	PATTERN="^$PATTERN$EXCEPT$PATTERN "
+	list_configured 1|grep $PATTERNSWITCH -E -e "$PATTERN"|{
+	while read -e d
+		do
+			CCWGROUPDEVID_TO_REMOVE=${d%%,*}
+			extract_interface_name "$d"
+			remove_net_device $CCWGROUPDEVID_TO_REMOVE $REPLY
+		done
+	}
+	exit 0
+fi
+if [ $DO_LIST_UNCONFIGURED -eq 1 ]
+then
+	list_unconfigured
+	exit 0
+fi
+if [ $DO_LIST_CONFIGURED -eq 1 ]
+then
+	list_configured
+	exit 0
+fi
+
+# fallthrough: nothing was done
+# print hint to user
+print_short_usage
+exit 0
Index: s390-tools-sles11/zconf/znetconf.8
===================================================================
--- /dev/null
+++ s390-tools-sles11/zconf/znetconf.8
@@ -0,0 +1,313 @@
+.TH ZNETCONF 8 "Mar 2009" "s390-tools"
+
+.SH NAME
+znetconf \- list and configure network devices for System z network adapters
+
+.SH SYNOPSIS
+.B znetconf
+.B [-h|--help] [-v|--version]
+.br
+
+.br
+.B znetconf -u | -c
+.br
+.B znetconf -a <device_bus_id>[,...]{2} [-o <ATTR>=<VALUE>]+ [-d <DRIVER>]
+.br
+.B znetconf -A [-o <ATTR>=<VALUE>]+ [-d <DRIVER>] [-e <device_bus_id>]+
+.br
+.B znetconf -r <device_bus_id> [-n] | -R [-n] [-e <device_bus_id>]+
+
+
+.SH DESCRIPTION
+The znetconf command lists and automatically configures network devices for
+System z network adapters. It senses and lists potential
+network devices that are not yet configured or already configured.
+Based on these lists, it automatically adds or removes network devices.
+.P
+For automatic configuration, znetconf builds a channel command word
+(CCW) group device from sensed CCW devices, configures any specified
+option through the sensed network device driver and sets the new
+network device online.
+.P
+During automatic removal, znetconf sets the device offline and removes it.
+Be aware that removing all network devices leads to the
+complete loss of network connectivity. So a terminal session (e.g. 3270)
+might be required to restore.
+
+.SH OPTIONS
+.TP 8
+.BR -h | --help
+Print help text.
+
+.TP 8
+.BR -v | --version
+Print the version of the s390-tools package and the znetconf command.
+
+.TP
+.BR -u | --unconfigured
+List potential network devices that are not yet configured.
+For each device, the following data is provided:
+.RS
+.TP 4
+*
+Device IDs (device bus bus-IDs) of the CCW devices constituting the network
+device
+.TP
+*
+Type of control unit (e.g. 1731/01)
+.TP
+*
+Network card type (e.g. OSA (QDIO))
+.TP
+*
+Channel path identifier (CHPID)
+.TP
+*
+Device driver (qeth, lcs, ctc, ctcm)
+.RE
+.TP
+.BR -c | --configured
+List configured network devices. For each device, the following data is
+provided:
+.RS
+.TP 4
+*
+Device IDs (device bus-IDs) of the CCW devices constituting the network device
+.TP
+*
+Control unit type (e.g. 1731/01)
+.TP
+*
+Card type (e.g. GuestLAN QDIO)
+.TP
+*
+Channel path identifier (CHPID)
+.TP
+*
+Driver (qeth, lcs, ctc, ctcm)
+.TP
+*
+Network interface name (if available)
+.TP
+*
+State (online vs. offline)
+.RE
+
+.TP
+.BR -a | --add " <device_bus_id>[,<device_bus_id>)][,<device_bus_id>]
+[-o | --option <ATTR>=<VALUE>]+ [-d | --driver <DRIVER>]
+.br
+
+.br
+Add the potential network device identified by device_bus_id.
+device_bus_id can be any of the device
+IDs listed as part of the potential network device list (argument
+.BR -u ")."
+For example, if znetconf
+.BR -u
+lists 0.0.f503,0.0.f504,0.0.f505 for a potential network device, device_bus_id
+may be 0.0.f503 or 0.0.f504 or 0.0.f505.
+If a device bus-ID begins with 0.0., you can abbreviate it to the final 4
+hexadecimal digits. For example, you can abbreviate 0.0.f503 to f503.
+.br
+.br
+If attribute value pairs are given with
+.BR -o ", "
+these pairs are configured for the created network device. The
+device is then set online regardless of whether the given attribute value pairs
+were applied successfully.
+.br
+Finally, the corresponding network interface name (e.g. eth1) is displayed.
+.br
+If more then one device_bus_id is given, the given set of devices is configured as a network device. znetconf tries to sense the required device driver
+automatically. If the device driver cannot be sensed, you must specify it with
+-d.
+.BR -d "."
+With
+.BR -d
+znetconf does NOT check the validity of the combination of device bus-IDs.
+
+.TP
+.BR -A | --addall " [-o | --option <ATTR>=<VALUE>]+ [-d | --driver DRIVER]"
+[-e | --except <device_bus_id>]+
+.br
+
+.br
+Add all potential network devices. If one or more device_bus_id are specified
+with
+.BR -e ", "
+the corresponding network devices are not added.
+Attribute value pairs given with
+.BR -o
+are configured for the network devices before they are set
+online. If the configuration of one potential network device fails,
+znetconf continues with the next remaining potential network device.
+
+.TP
+.BR -r | --remove " <device_bus_id> [-n | --non-interactive]"
+Remove the network device identified by device_bus_id. device_bus_id is one of
+the device IDs of the network device. They are listed as part of znetconf
+.BR -c "."
+znetconf sets the device offline and removes it. If
+.BR -n
+is given, all confirmation questions are answered with 'yes'.
+
+.TP
+.BR -R|--removall " [-n | --non-interactive] [-e | --except <device_bus_id>]+"
+Remove all network devices. If
+.BR -n
+is given, all confirmation questions are answered with 'yes'. To exclude
+certain devices from the removal, their device bus IDs have to be given
+with
+.BR -e ". "
+
+.TP
+\fB<ATTR>\fR
+Specify a device option. The option must match a sysfs attribute for the device
+to be configured.
+
+.TP
+\fB<device_bus_id>\fR
+Specify the device bus-ID of a CCW device. Device bus-IDs have the form
+([A-Fa-f0-9].[A-Fa-f0-9].)[A-Fa-f0-9]{4}.
+
+If a device bus-ID begins with 0.0., you can abbreviate it to the final 4
+hexadecimal digits.
+
+For example, you can abbreviate 0.0.f503 to f503.
+
+.TP
+\fB<DRIVER>\fR
+Specify the device driver for the device. Valid values are qeth, lcs, ctc, or
+ctcm.
+
+.SH EXAMPLES
+\fBznetconf -A\fR
+.RS
+Configures all potential network devices. To display a list of all potential
+network devices enter znetconf
+.BR -u "."
+After running znetconf
+.BR -A
+enter znetconf
+.BR -c
+to see which devices have been configured successfully.
+You can also enter znetconf
+.BR -u
+to display devices that have not been configured successfully.
+Successfully configured devices are no longer listed with znetconf
+.BR -u "."
+.RE
+.P
+\fBznetconf -A -e f500\fR
+.RS
+Configures all potential network devices except the one with the device bus ID
+0.0.f500. To display a list of all potential network devices enter znetconf
+.BR -u "."
+After running \fBznetconf -A -e f500\fR
+enter znetconf
+.BR -c
+to see which devices have been configured successfully.
+You can also enter znetconf
+.BR -u
+to display devices that have not been configured successfully.
+Successfully configured devices are no longer listed with znetconf
+.BR -u "."
+.RE
+.P
+\fBznetconf -R\fR
+.RS
+Removes all configured network devices.
+After successfully running this command, all devices listed by znetconf -c
+become potential devices listed by
+.BR -u "."
+.RE
+.P
+\fBznetconf -R -e f501\fR
+.RS
+Removes all configured network devices except the one having the device bus ID
+0.0.f501.
+After successfully running this command, all devices listed by znetconf -c
+except the one having the device bus ID 0.0.f501 become potential devices
+listed by
+.BR -u "."
+.RE
+.P
+\fBznetconf -u\fR
+.RS
+Shows the list of potential network devices. Example output:
+.br
+
+.br
+Device IDs                 Type    Card Type  CHPID Drv.
+.br
+--------------------------------------------------------
+0.0.f500,0.0.f501,0.0.f502 1731/01 OSA (QDIO) 00    qeth
+.br
+0.0.f503,0.0.f504,0.0.f505 1731/01 OSA (QDIO) 01    qeth
+.RE
+.P
+\fBznetconf -a 0.0.f503\fR
+.RS
+Adds the potential network device
+with 0.0.f503 as one of its device bus-IDs.
+After successfully running this command, znetconf
+.BR -c
+lists the new network device.
+.RE
+.P
+\fBznetconf -a f503\fR
+.RS
+This command is equivalent to \fBznetconf -a 0.0.f503\fR.
+.RE
+.P
+\fBznetconf -a f503 -o layer2=0 -o portname=myname\fR
+.RS
+Adds the potential network device
+with 0.0.f503 as one of its device bus-IDs
+and configures the options layer2 with value 0 and
+portname with myname.
+.RE
+.P
+\fBznetconf -c\fR
+.br
+Shows a list of configured network devices. Example output:
+.br
+
+.br
+.br
+Device IDs                 Type    Card Type     CHPID Drv. Name State
+.br
+-----------------------------------------------------------------------
+.br
+0.0.f503,0.0.f504,0.0.f505 1731/01 GuestLAN QDIO    01 qeth eth1 online
+.br
+0.0.f5f0,0.0.f5f1,0.0.f5f2 1731/01 OSD_1000         76 qeth eth0 online
+.br
+.P
+
+\fBznetconf -r 0.0.f503\fR
+.RS
+Removes the network device with 0.0.f503 as one of its device bus-IDs.
+You can only remove configured devices as listed by znetconf
+.BR -c "."
+After successfully running this command the corresponding device appears in the
+list of potential network devices as listed by znetconf
+.BR -u "."
+.RE
+.P
+\fBznetconf -r f503\fR
+.RS
+This command is equivalent to \fBznetconf -r 0.0.f503\fR.
+.RE
+.P
+
+.SH DIAGNOSTICS
+If znetconf runs successfully, the exit status is 0. In case of errors, 1 is
+returned.
+
+.SH AUTHOR
+.nf
+This man-page was written by Einar Lueck <elelueck@de.ibm.com>.
+.fi
+
Index: s390-tools-sles11/zconf/znetcontrolunits
===================================================================
--- /dev/null
+++ s390-tools-sles11/zconf/znetcontrolunits
@@ -0,0 +1,53 @@
+#!/bin/bash
+#
+# znetcontrolunits - define some common control unit mappings
+#
+# This definitions are not intended to be used as standalone tool, but should be
+# used from other tools like a library. E.g. znetconf is one exploiter
+# of them.
+#
+# Copyright (C) IBM Corp. 2008, 2009
+# Author: Steffen Maier <maier@de.ibm.com>
+#
+
+# The arrays (among other things) should be adapted, if any of those device
+# drivers start supporting different CU types/models.
+
+readonly -a CU=(
+    1731/01
+    1731/05
+    3088/08
+    3088/1f
+    3088/1e
+    3088/01
+    3088/60
+    3088/61
+    1731/06
+)
+
+readonly -a CU_DEVDRV=(
+    qeth
+    qeth
+    ctcm
+    ctcm
+    ctcm
+    lcs
+    lcs
+    lcs
+    qeth
+)
+
+# Searches for a match of argument 1 on the array $CU and sets $cu_idx
+# to the matched array index on success.
+# Returns 0 on success, 1 on failure.
+function search_cu() {
+    local scu=$1
+    local i
+    for ((i=0; i < ${#CU[@]}; i++)); do
+        if [ "$scu" == "${CU[i]}" ]; then
+            cu_idx=$i
+            return 0
+        fi
+    done
+    return 1
+}
Index: s390-tools-sles11/zfcpdump_v2/zfcpdump.c
===================================================================
--- s390-tools-sles11.orig/zfcpdump_v2/zfcpdump.c
+++ s390-tools-sles11/zfcpdump_v2/zfcpdump.c
@@ -339,7 +339,15 @@ static int umount_dump_device(void)
  */
 static void terminate(void)
 {
+	int fd;
+
 	sleep(WAIT_TIME_END); /* give the messages time to be displayed */
+	fd = open(DEV_ZCORE_REIPL, O_WRONLY, 0);
+	if (fd == -1)
+		goto no_reipl;
+	write(fd, REIPL, 1);
+	close(fd);
+no_reipl:
 	reboot(LINUX_REBOOT_CMD_POWER_OFF);
 }
 
Index: s390-tools-sles11/zfcpdump_v2/zfcpdump.h
===================================================================
--- s390-tools-sles11.orig/zfcpdump_v2/zfcpdump.h
+++ s390-tools-sles11/zfcpdump_v2/zfcpdump.h
@@ -77,6 +77,8 @@ struct globals {
 #define PROC_MISC	"/proc/misc"
 #define DEV_ZCORE	"/sys/kernel/debug/zcore/mem"
 #define DEV_ZCORE_MAP	"/sys/kernel/debug/zcore/memmap"
+#define DEV_ZCORE_REIPL	"/sys/kernel/debug/zcore/reipl"
+#define REIPL		"1"
 #define DEV_SCSI	"/dev/sda"
 #define DUMP_DIR	"/mnt"
 
Index: s390-tools-sles11/ziomon/Makefile
===================================================================
--- s390-tools-sles11.orig/ziomon/Makefile
+++ s390-tools-sles11/ziomon/Makefile
@@ -3,21 +3,36 @@ include ../common.mak
 
 #CFLAGS+=-Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -g -I../include
 CFLAGS+=-Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -DNDEBUG -O3 -I../include
+#CFLAGS+=-Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -g -O3 -I../include
+#CXXFLAGS+=-Wall -Wundef -Wno-trigraphs -g -I../include
+CXXFLAGS+=-Wall -Wundef -Wno-trigraphs -DNDEBUG -O3 -I../include
+#CXXFLAGS+=-Wall -Wundef -Wno-trigraphs -g -O3 -I../include
 
-all: ziomon_util ziomon_mgr ziomon_zfcpdd
+TARGETS = ziomon_util ziomon_mgr ziomon_zfcpdd ziorep_utilization ziorep_traffic
+
+all: $(TARGETS)
 
 ziomon_mgr: ziomon_dacc.o ziomon_util.o ziomon_mgr.c ziomon_tools.o ziomon_zfcpdd.o ziomon_msg_tools.o
-	$(CC) -DWITH_MAIN $(CFLAGS) ziomon_mgr.c ziomon_dacc.o ziomon_tools.o ziomon_util.o ziomon_zfcpdd.o ziomon_msg_tools.o -o $@
+	$(CC) -DWITH_MAIN $(CFLAGS) -lm $^ -o $@
 
 ziomon_util: ziomon_util.c ziomon_util.h ziomon_tools.o
-	$(CC) -DWITH_MAIN $(CFLAGS) ziomon_tools.o $< -o $@
+	$(CC) -DWITH_MAIN $(CFLAGS) ziomon_tools.o -lm $< -o $@
 
 ziomon_zfcpdd: ziomon_zfcpdd.c ziomon_zfcpdd.h ziomon_tools.o
-	$(CC) -DWITH_MAIN $(CFLAGS) ziomon_tools.o -lrt $< -o $@
+	$(CC) -DWITH_MAIN $(CFLAGS) ziomon_tools.o -lm -lrt $< -o $@
+
+ziorep_traffic: ziorep_traffic.cpp ziorep_framer.o ziorep_frameset.o ziorep_printers.o ziomon_dacc.o ziomon_util.o ziomon_msg_tools.o ziomon_tools.o ziomon_zfcpdd.o ziorep_cfgreader.o ziorep_collapser.o ziorep_utils.o ziorep_filters.o
+	$(CXX) $(CXXFLAGS) $^ -o $@
+
+ziorep_utilization: ziorep_utilization.cpp ziorep_framer.o ziorep_frameset.o ziorep_printers.o ziomon_dacc.o ziomon_util.o ziomon_msg_tools.o ziomon_tools.o ziomon_zfcpdd.o ziorep_cfgreader.o ziorep_collapser.o ziorep_utils.o ziorep_filters.o
+	$(CXX) $(CXXFLAGS) $^ -o $@
 
 %.o: %.c
 	$(CC) $(CFLAGS) -c $<
 
+%.o: %.cpp
+	$(CXX) $(CXXFLAGS) -c $<
+
 install: all
 	cat ziomon  | sed -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' > $(BINDIR)/ziomon;
 	chown $(OWNER).$(GROUP) $(BINDIR)/ziomon;
@@ -25,6 +40,9 @@ install: all
 	cat ziomon_fcpconf  | sed -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' > $(BINDIR)/ziomon_fcpconf;
 	chown $(OWNER).$(GROUP) $(BINDIR)/ziomon_fcpconf;
 	chmod 755 $(BINDIR)/ziomon_fcpconf;
+	cat ziorep_config  | sed -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' > $(BINDIR)/ziorep_config;
+	chown $(OWNER).$(GROUP) $(BINDIR)/ziorep_config;
+	chmod 755 $(BINDIR)/ziorep_config;
 	$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 ziomon.8 $(MANDIR)/man8
 	$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 ziomon_fcpconf.8 $(MANDIR)/man8
 	$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 ziomon_util $(BINDIR)
@@ -33,6 +51,11 @@ install: all
 	$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 ziomon_mgr.8 $(MANDIR)/man8
 	$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 ziomon_zfcpdd $(BINDIR)
 	$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 ziomon_zfcpdd.8 $(MANDIR)/man8
+	$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 ziorep_config.8 $(MANDIR)/man8
+	$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 ziorep_utilization $(BINDIR)
+	$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 ziorep_utilization.8 $(MANDIR)/man8
+	$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 ziorep_traffic $(BINDIR)
+	$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 ziorep_traffic.8 $(MANDIR)/man8
 
 uninstall:
 	rm $(BINDIR)/ziomon
@@ -40,11 +63,17 @@ uninstall:
 	rm $(BINDIR)/ziomon_mgr
 	rm $(BINDIR)/ziomon_zfcpdd
 	rm $(BINDIR)/ziomon_fcpconf
+	rm $(BINDIR)/ziorep_config
+	rm $(BINDIR)/ziorep_utilization
+	rm $(BINDIR)/ziorep_traffic
 	rm $(MANDIR)/man8/ziomon.8*
 	rm $(MANDIR)/man8/ziomon_util.8*
 	rm $(MANDIR)/man8/ziomon_mgr.8*
 	rm $(MANDIR)/man8/ziomon_zfcpdd.8*
 	rm $(MANDIR)/man8/ziomon_fcpconf.8*
+	rm $(MANDIR)/man8/ziorep_config.8*
+	rm $(MANDIR)/man8/ziorep_utilization.8*
+	rm $(MANDIR)/man8/ziorep_traffic.8*
 
 clean:
-	-rm -f ziomon_util ziomon_mgr ziomon_dacc.o ziomon_dacc.o check_integrity ziomon_util.o ziomon_zfcpdd ziomon_zfcpdd.o ziomon_tools.o ziomon_msg_tools.o
+	-rm -f *.o $(TARGETS)
Index: s390-tools-sles11/ziomon/blkiomon.h
===================================================================
--- s390-tools-sles11.orig/ziomon/blkiomon.h
+++ s390-tools-sles11/ziomon/blkiomon.h
@@ -19,29 +19,26 @@
 #define BLKIOMON_D2C_BUCKETS 25
 struct blkiomon_stat {
 	__u64 time;
+	/* Histogram of request sizes in Bytes.
+	   Step-size is 1024, starting at 0. */
 	__u32 size_hist[BLKIOMON_SIZE_BUCKETS];
+	/* Histogram of dispatch to completion request times in u-secs.
+	   Step-size is 8, starting at 0. */
 	__u32 d2c_hist[BLKIOMON_D2C_BUCKETS];
-	struct minmax size_r;
-	struct minmax size_w;
-	struct minmax d2c_r;
-	struct minmax d2c_w;
-	struct minmax thrput_r;
-	struct minmax thrput_w;
-	__u64 bidir;
-	__u32 device;
+	struct minmax size_r;	/* stats of read request sizes in Bytes */
+	struct minmax size_w;	/* stats of write request sizes in Bytes */
+	struct minmax d2c_r;	/* stats of read request durations in u-secs */
+	struct minmax d2c_w;	/* stats of write request durations in u-secs */
+	struct minmax thrput_r;	/* stats of read throughput in Kbytes per micro-sec */
+	struct minmax thrput_w;	/* stats of write throughput in Kbytes per micro-sec */
+	__u64 bidir;	/* number of bi-directional requests, is set exclusive
+			(ie. not implicitly adding 1 to rd and wrt as well) */
+	__u32 device;	/* device identifier */
 } __attribute__ ((packed));
 
-static struct histlog2 size_hist = {
-	.first = 0,
-	.delta = 1024,
-	.num = BLKIOMON_SIZE_BUCKETS
-};
-
-static struct histlog2 d2c_hist = {
-	.first = 0,
-	.delta = 8,
-	.num = BLKIOMON_D2C_BUCKETS
-};
+static struct histlog2 size_hist = {0, 1024, BLKIOMON_SIZE_BUCKETS};
+
+static struct histlog2 d2c_hist = {0, 8, BLKIOMON_D2C_BUCKETS};
 
 static inline void blkiomon_stat_init(struct blkiomon_stat *bstat)
 {
@@ -78,8 +75,10 @@ static inline void blkiomon_conv_from_BE
 }
 
 static inline void blkiomon_stat_merge(struct blkiomon_stat *dst,
-				       struct blkiomon_stat *src)
+				       const struct blkiomon_stat *src)
 {
+	if (src->device != dst->device)
+		dst->device = 0xffffffff;
 	if (src->time > dst->time)
 		dst->time = src->time;
 	histlog2_merge(&size_hist, dst->size_hist, src->size_hist);
@@ -98,12 +97,12 @@ static inline void blkiomon_stat_merge(s
 #define MAJOR(dev)	((unsigned int) ((dev) >> MINORBITS))
 #define MINOR(dev)	((unsigned int) ((dev) & MINORMASK))
 
-static inline void blkiomon_stat_print(struct blkiomon_stat *p)
+static inline void blkiomon_stat_print(const struct blkiomon_stat *p)
 {
 	time_t t = p->time;
 
-	printf("\ntime: %s", ctime(&t));
-	printf("device: %d,%d\n", MAJOR(p->device), MINOR(p->device));
+	printf("time: %s", ctime(&t));
+	printf("device: %d:%d\n", MAJOR(p->device), MINOR(p->device));
 	minmax_print("sizes read (bytes)", &p->size_r);
 	minmax_print("sizes write (bytes)", &p->size_w);
 	minmax_print("d2c read (usec)", &p->d2c_r);
Index: s390-tools-sles11/ziomon/stats.h
===================================================================
--- s390-tools-sles11.orig/ziomon/stats.h
+++ s390-tools-sles11/ziomon/stats.h
@@ -12,7 +12,9 @@
 #define STATS_H
 
 #include <linux/types.h>
-#include "endian.h"
+#include <endian.h>
+#include <stdio.h>
+#include <math.h>
 
 struct minmax {
 	__u64 min;
@@ -42,7 +44,7 @@ static inline void minmax_account(struct
 	mm->num++;
 }
 
-static inline void minmax_merge(struct minmax *dst, struct minmax *src)
+static inline void minmax_merge(struct minmax *dst, const struct minmax *src)
 {
 	dst->sum += src->sum;
 	dst->sos += src->sos;
@@ -62,25 +64,33 @@ static inline void minmax_swap(struct mi
 	swap_64(mm->num);
 }
 
-static inline double minmax_avg(struct minmax *mm)
+static inline double minmax_avg(const struct minmax *mm)
 {
 	return (mm->sum / (double)mm->num);
 }
 
-static inline double minmax_var(struct minmax *mm)
+static inline double minmax_var(const struct minmax *mm)
 {
 	double num = (double)mm->num;
 
 	return ((mm->sos - ((mm->sum * mm->sum) / num)) / num);
 }
 
-static inline int minmax_print(const char *s, struct minmax *mm)
+static inline double minmax_std_dev(const struct minmax *mm)
 {
-	return printf("%s: num %Ld, min %Ld, max %Ld, sum %Ld, squ %Ld, "
-		       "avg %.1f, var %.1f\n", s, (unsigned long long)mm->num,
+	return sqrt(minmax_var(mm));
+}
+
+static inline int minmax_print(const char *s, const struct minmax *mm)
+{
+	if (mm->num)
+		return printf("%s: num %Lu, min %Lu, max %Lu, sum %Lu, squ %Lu, "
+		       "avg %.1lf, std dev %.1lf\n", s, (unsigned long long)mm->num,
 		       (unsigned long long)mm->min, (unsigned long long)mm->max,
 		       (unsigned long long)mm->sum, (unsigned long long)mm->sos,
-		       minmax_avg(mm), minmax_var(mm));
+		       minmax_avg(mm), minmax_std_dev(mm));
+	else
+		return printf("%s: (none)\n", s);
 }
 
 struct histlog2 {
@@ -89,7 +99,7 @@ struct histlog2 {
 	int num;
 };
 
-static inline __u64 histlog2_upper_limit(int index, struct histlog2 *h)
+static inline __u64 histlog2_upper_limit(int index, const struct histlog2 *h)
 {
 	return h->first + (index ? h->delta << (index - 1) : 0);
 }
@@ -98,7 +108,7 @@ static inline int histlog2_index(__u64 v
 {
 	int i;
 
-	for (i = 0; i < (h->num - 1) && val > histlog2_upper_limit(i, h); i++);
+	for (i = 0; i < h->num && val > histlog2_upper_limit(i, h); i++);
 	return i;
 }
 
@@ -109,24 +119,25 @@ static inline void histlog2_account(__u3
 	bucket[index]++;
 }
 
-static inline void histlog2_merge(struct histlog2 *h, __u32 *dst, __u32 *src)
+static inline void histlog2_merge(struct histlog2 *h, __u32 *dst, const __u32 *src)
 {
 	int i;
 
-	for (i = 0; i < h->num - 1; i++)
+	for (i = 0; i < h->num; i++) {
 		dst[i] += src[i];
+	}
 }
 
 static inline void histlog2_swap(__u32 a[], struct histlog2 *h)
 {
 	int i;
 
-	for (i = 0; i < h->num - 1; i++)
+	for (i = 0; i < h->num; i++)
 		swap_32(a[i]);
 }
 
-static inline void histlog2_print(const char *s, __u32 a[],
-				  struct histlog2 *h)
+static inline void histlog2_print(const char *s, const __u32 a[],
+				  const struct histlog2 *h)
 {
 	int i;
 
Index: s390-tools-sles11/ziomon/ziomon
===================================================================
--- s390-tools-sles11.orig/ziomon/ziomon
+++ s390-tools-sles11/ziomon/ziomon
@@ -10,6 +10,7 @@
 # Author(s): Stefan Raspl <raspl@linux.vnet.ibm.com>
 #
 
+WRP_TOOLNAME="ziomon"
 WRP_MSG_Q_PATH="/tmp/.zfcpstats$RANDOM";
 WRP_MSG_Q_ID=1;
 WRP_MSG_Q_UTIL_ID=1;
@@ -31,7 +32,7 @@ WRP_DEVICES=();
 WRP_LUNS=();
 WRP_LOGFILE="";
 # limit of actual data in percent that need space on disk
-WRP_SIZE_THRESHOLD="25";
+WRP_SIZE_THRESHOLD="10";
 WRP_FORCE=0;
 
 function debug() {
@@ -73,7 +74,7 @@ function print_usage() {
 function check_for_int() {
    [ $1 -gt 0 ] >/dev/null 2>&1;
    if [ $? -ne 0 ]; then
-      echo "Error: $1 is not a valid argument to $2 - must be positive integer";
+      echo "$WRP_TOOLNAME: $1 is not a valid argument to $2 - must be positive integer";
       exit 3;
    fi
 }
@@ -107,7 +108,7 @@ function parse_size() {
       G) (( WRP_SIZE=1000*$size ));;
       M) WRP_SIZE=$size;;
       T) (( WRP_SIZE=1000000*$size ));;
-      *) echo "Error: Unknown size suffix $unit";
+      *) echo "$WRP_TOOLNAME: Unknown size suffix $unit";
          return 1;;
    esac;
 
@@ -152,7 +153,7 @@ function parse_params() {
                 print_version;
                 exit 1;;
             -*)
-                echo "ziomon: Invalid option -- $1";
+                echo "$WRP_TOOLNAME: Invalid option -- $1";
                 echo "Try 'ziomon --help' for more information.";
                 exit 1;;
             *)
@@ -162,22 +163,22 @@ function parse_params() {
         shift;
    done
    if [ ${#WRP_DEVICES[@]} -eq 0 ]; then
-      echo "Error: No devices specified!";
+      echo "$WRP_TOOLNAME: No devices specified";
       error=1;
    fi
    if [ "$WRP_LOGFILE" == "" ]; then
-      echo "Error: No logfile specified";
+      echo "$WRP_TOOLNAME: No logfile specified";
       error=1;
    fi
    if [ $error -eq 0 ] && [ `dirname $WRP_LOGFILE` == "" ]; then
       WRP_LOGFILE=$WRP_LOGFILE;
    fi
    if [ $error -eq 0 ] && [ ! -d `dirname $WRP_LOGFILE` ]; then
-      echo "Error: Directory `dirname $WRP_LOGFILE` does not exist.";
+      echo "$WRP_TOOLNAME: Directory `dirname $WRP_LOGFILE` does not exist";
       error=1;
    fi
    if [ "$WRP_DURATION" == "" ]; then
-      echo "Error: No duration specified - see option '-d'";
+      echo "$WRP_TOOLNAME: No duration specified - see option '-d'";
       error=1;
    fi
    [ $error -eq 0 ] && check_for_int $WRP_DURATION -d;
@@ -190,12 +191,12 @@ function parse_params() {
 
    tmp=`expr $WRP_INTERVAL % 2`;
    if [ $error -eq 0 ] && [ $tmp -ne 0 ]; then
-      echo "Error: The interval length must be an even number.";
+      echo "$WRP_TOOLNAME: The interval length must be an even number";
       error=1;
    fi
    tmp=`expr $WRP_DURATION % $WRP_INTERVAL`;
    if [ $error -eq 0 ] && [ $tmp -ne 0 ]; then
-      echo "Error: The duration must be a multiple of the interval length";
+      echo "$WRP_TOOLNAME: The duration must be a multiple of the interval length";
       error=1;
    fi
    if [ $error -ne 0 ]; then
@@ -213,11 +214,11 @@ function parse_params() {
 
 
 function check_for_process() {
-   if [ ! -d /proc/$1 ]; then
+   if [ $# -eq 0 ] || [ ! -d /proc/$1 ]; then
       echo "failed";
-      shutdown;
       return 1;
    fi
+
    return 0;
 }
 
@@ -233,6 +234,7 @@ function start_trace() {
    local hosts_param;
    local luns_param;
    local i;
+   local len;
 
    if [ $WRP_DEBUG -ne 0 ]; then
       verbose="-V";
@@ -275,7 +277,7 @@ function start_trace() {
    blkiomon_command="blkiomon --interval=$WRP_INTERVAL -Q  $WRP_MSG_Q_PATH -q $WRP_MSG_Q_ID -m $WRP_MSG_Q_BLKIOMON_ID $verbose_blk -d -";
    zfcpdd_command="ziomon_zfcpdd -Q  $WRP_MSG_Q_PATH -q $WRP_MSG_Q_ID -m $WRP_MSG_Q_ZIOMON_ZFCPDD_ID -i $WRP_INTERVAL";
    debug "starting blktrace: $blktrace_command | $blkiomon_command | $zfcpdd_command";
-   $blktrace_command | $blkiomon_command | $zfcpdd_command > $WRP_MSG_Q_PATH/blktrace.log &
+   $blktrace_command 2>$WRP_MSG_Q_PATH/blktrace.err | $blkiomon_command | $zfcpdd_command > $WRP_MSG_Q_PATH/blktrace.log &
    i=0;
    # might take a moment to start all processes in the pipe if system under load
    while [ $i -lt 60 ]; do
@@ -288,27 +290,38 @@ function start_trace() {
       sleep 1;
       (( i++ ));
    done
-
    check_for_process $WRP_ZIOMON_MGR_PID;
-   [ $? -ne 0 ] && echo "Error: Failed to determine ziomon_mgr pid" && return 1;
+   [ $? -ne 0 ] && echo "$WRP_TOOLNAME: Failed to determine ziomon_mgr pid" && return 1;
    check_for_process $WRP_ZIOMON_UTIL_PID;
-   [ $? -ne 0 ] && echo "Error: Failed to determine ziomon_util pid" && return 1;
+   [ $? -ne 0 ] && echo "$WRP_TOOLNAME: Failed to determine ziomon_util pid" && return 1;
    check_for_process $WRP_BLKTRACE_PID;
-   [ "$WRP_BLKTRACE_PID" == "" ] && echo "Error: Failed to determine blktrace pid" && return 1;
+   [ $? -ne 0 ] && echo "$WRP_TOOLNAME: Failed to determine blktrace pid" && return 1;
    check_for_process $WRP_BLKIOMON_PID;
-   [ "$WRP_BLKIOMON_PID" == "" ] && echo "Error: Failed to determine blkiomon pid" && return 1;
+   [ $? -ne 0 ] && echo "$WRP_TOOLNAME: Failed to determine blkiomon pid" && return 1;
    check_for_process $WRP_ZIOMON_ZFCPDD_PID;
-   [ "$WRP_ZIOMON_ZFCPDD_PID" == "" ] && echo "Error: Failed to determine ziomon_zfcpdd pid" && return 1;
+   [ $? -ne 0 ] && echo "$WRP_TOOLNAME: Failed to determine ziomon_zfcpdd pid" && return 1;
 
    echo "done";
    echo -n "Collecting data...";
 
-   sleep $WRP_DURATION;
+   # pay extra attention to blktrace
+   for (( i=0; i<$WRP_DURATION; ++i )); do
+      len=`cat $WRP_MSG_Q_PATH/blktrace.err | wc -l`;
+      if [ $len -ne 0 ]; then
+         cat $WRP_MSG_Q_PATH/blktrace.err;
+         echo "Error: blktrace has errors, aborting";
+         return;
+      fi
+      sleep 1;
+   done
+
    echo "done";
 }
 
 
 function shutdown() {
+   local failed=0;
+
    echo "Shutting down";
    # one more second to write final result
    sleep 2;
@@ -332,6 +345,13 @@ function shutdown() {
          kill -s SIGTERM $WRP_ZIOMON_MGR_PID;
       fi
    fi
+   # kill running traces on devices
+   for dev in ${WRP_DEVICES[@]}; do
+      [ -d /sys/kernel/debug/block/${dev##*/} ] && debug "Trace on $dev exists, attempt to kill..." && blktrace -k $dev >/dev/null 2>&1;
+      [ -d /sys/kernel/debug/block/${dev##*/} ] && echo "$WRP_TOOLNAME: Failed to stop trace on $dev" && (( failed++ ));
+   done
+   [ $failed -ne 0 ] && echo "$WRP_TOOLNAME: blktrace has leftovers, manual cleanup required!";
+
    if [ -e $WRP_MSG_Q_PATH ]; then
       if [ $WRP_DEBUG -gt 1 ]; then
          debug "Logfiles available at $WRP_MSG_Q_PATH";
@@ -349,11 +369,28 @@ function emergency_shutdown() {
 }
 
 
+function check_cpuplugd {
+   # check if cpuplugd is running
+   # If so, the whole per-cpu mechanism of blktrace gets corrupted, which
+   # results in the infamous 'bad trace magic' message
+   if [ -e /var/run/cpuplugd.pid ]; then
+      echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
+      echo "ziomon: Warning: cpuplugd is running which can corrupt the traces.";
+      echo "        It is recommended to stop cpuplugd for the duration of the";
+      echo "        trace using 'service cpuplugd stop'.";
+      echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
+   fi
+}
+
+
 function setup() {
    while [ -e $WRP_MSG_Q_PATH ]; do
       WRP_MSG_Q_PATH="$WRP_MSG_Q_PATH$RANDOM";
    done
    mkdir $WRP_MSG_Q_PATH;
+   # check if debugfs is mounted (required for blktrace)
+   cat /proc/mounts | awk '{ print $3 }' | grep debugfs >/dev/null;
+   [ $? -ne 0 ] && echo "Error: No debugfs found. Mount debugfs and try again!" && exit 1;
 }
 
 
@@ -399,14 +436,14 @@ function check_for_multipath_devices() {
                devices_basenames[$j]="";
                clean_devices;
                (( i+=3 ));
-               while [ "${mp_arr[$i]:0:1}" == "_" ]; do
+               while [[ ! "${mp_arr[$i]:0:1}" =~ "[0-9a-zA-Z]" ]] && [ $i -lt ${#mp_arr[@]} ]; do
                   checked_devs[${#checked_devs[@]}]=`echo ${mp_arr[$i]} | awk '{print "/dev/"$3}'`;
                   ddebug "   add ${checked_devs[${#checked_devs[@]}-1]}";
-                  line=${mp_arr[$i]#_*};
+                  line=${mp_arr[$i]#* };
                   line=${line%%:*};
                   line=`echo $line | sed 's/ //g'`;
                   WRP_HOST_ADAPTERS[${#WRP_HOST_ADAPTERS[@]}]="host$line";
-                  WRP_LUNS[${#WRP_LUNS[@]}]=`echo ${mp_arr[$i]#_*} | awk '{print $1}'`;
+                  WRP_LUNS[${#WRP_LUNS[@]}]=`echo ${mp_arr[$i]#* } | awk '{print $1}'`;
                   (( i++ ));
                done;
                (( --i ));
@@ -464,7 +501,7 @@ function determine_host_adapters() {
    local num_s_devs;
    local s_dev_ratio;
 
-   echo -n "check devices...";
+   echo -n "Check devices...";
 
    # Estimate fraction of /dev/s* devices - if >50%, start with check for regular devices
    num_s_devs=`echo ${WRP_DEVICES[@]} | sed "s/ /\n/g" | grep /dev/s | wc -l`;
@@ -482,7 +519,7 @@ function determine_host_adapters() {
    for (( i=0; i<${#WRP_DEVICES[@]}; ++i )); do
       if [ "${WRP_DEVICES[$i]}" != "" ]; then
          if [ $found -eq 0 ]; then
-           echo "Error: The following devices do not seem to exist:";
+           echo "$WRP_TOOLNAME: The following devices do not seem to exist:";
            found=1;
          fi
          echo "   ${WRP_DEVICES[$i]}";
@@ -503,7 +540,7 @@ function determine_host_adapters() {
    debug "WRP_HOST_ADAPTERS: ${WRP_HOST_ADAPTERS[@]}";
 
    if [ ${#WRP_DEVICES[@]} -ne ${#WRP_LUNS[@]} ]; then
-      echo "Error: Number of LUNs does not match number of devices: ${#WRP_DEVICES[@]} devices and ${#WRP_LUNS[@]} LUNs.";
+      echo "$WRP_TOOLNAME: Number of LUNs does not match number of devices: ${#WRP_DEVICES[@]} devices and ${#WRP_LUNS[@]} LUNs";
       exit 2;
    fi
 }
@@ -522,13 +559,13 @@ function check_free_space_mileage() {
       (( history_percent = $limit * 100 / $estimated_size ));
       if [ $WRP_FORCE -eq 0 ] && [ $history_percent -lt $WRP_SIZE_THRESHOLD ]; then
          if [ "$WRP_SIZE" != "" ]; then
-            echo "Error: User-specified limit for log data too low.";
+            echo "$WRP_TOOLNAME: User-specified limit for log data too low";
             echo "Estimated maximum size of log data: $estimated_size MBytes.";
             echo "User-specified limit: $limit MBytes.";
             echo "This would equal $history_percent percent of the total data, while at least $WRP_SIZE_THRESHOLD percent is required.";
             echo "Either lower the duration, reduce the interval length, raise the limit or override using option '-f'."
          else
-            echo "Error: Not enough free disk space available for log data.";
+            echo "Error: Not enough free disk space available for log data";
             echo "Estimated maximum size of log data: $estimated_size MBytes.";
             echo "Free disk space available: $limit MBytes.";
             echo "This would equal $history_percent percent of the total data, while at least $WRP_SIZE_THRESHOLD percent is required.";
@@ -553,6 +590,28 @@ function check_free_space_mileage() {
 }
 
 
+function check_blktrace() {
+   which blktrace >/dev/null 2>&1;
+   if [ $? -ne 0 ]; then
+      echo "$WRP_TOOLNAME: Could not find blktrace. Please make sure that the blktrace package is installed and matches the level in the documentation.";
+      exit 1;
+   fi
+}
+
+
+function check_for_existing_output() {
+    # avoid config mismatch. Accidental deletion doesn't harm - can be recreated from .cfg anyway
+    if [ -e "$WRP_LOGFILE.config" ]; then
+        debug "$WRP_LOGFILE.config exists, removing";
+        rm -rf $WRP_LOGFILE.config;
+    fi
+    if [ -e "$WRP_LOGFILE.agg" ]; then
+        debug "$WRP_LOGFILE.agg exists, removing";
+        rm -rf $WRP_LOGFILE.agg;
+    fi
+}
+
+
 function check_size_requirements() {
    local util_base_sz;
    local util_variable_sz;
@@ -586,14 +645,13 @@ function check_size_requirements() {
    (( estimated_size=$total_num_records * $size_per_record ));
    debug "    estimated size: $estimated_size Bytes";
    (( estimated_size=$estimated_size / 1000000 ));
-   debug "    estimated size: $estimated_size MBytes";
    (( free_space = `df -k $logpath | tail -n 1 | awk '{print $4}'` / 1024 ));
    debug "    free space on '$logpath': $free_space MBytes";
    if [ "$WRP_SIZE" == "" ]; then
       echo "NOTE: No size limit specified, run without a limit.";
    else
       if [ $WRP_FORCE -eq 0 ] && [ $WRP_SIZE -gt $free_space ]; then
-         echo "Error: The user-specified limit of $WRP_SIZE MBytes for the log data exceeds the actual free space of $free_space MBytes.";
+         echo "$WRP_TOOLNAME: The user-specified limit of $WRP_SIZE MBytes for the log data exceeds the actual free space of $free_space MBytes.";
          echo "Either lower the limit, get more free space or use option '-f' to override.";
          return -1;
       fi
@@ -611,10 +669,16 @@ function check_size_requirements() {
 
 trap emergency_shutdown SIGHUP SIGTERM SIGINT SIGQUIT;
 
+check_blktrace;
+
 setup;
 
 parse_params $@;
 
+check_cpuplugd;
+
+check_for_existing_output;
+
 determine_host_adapters;
 
 check_size_requirements;
Index: s390-tools-sles11/ziomon/ziomon.8
===================================================================
--- s390-tools-sles11.orig/ziomon/ziomon.8
+++ s390-tools-sles11/ziomon/ziomon.8
@@ -65,18 +65,6 @@ The output files should base their names
 
 ziomon -i 20 -d 5 -l 50M -o trace_data /dev/mapper/36005076303ffc104000000000000212b /dev/sda /dev/sdg /dev/sdp
 
-.SH AUTHORS
-ziomon and this man page were written by Stefan Raspl.
-
-
-.SH COPYRIGHT
-Copyright \(co 2008 IBM Corp.
-.br
-This is free software.  You may redistribute copies of it under the terms of
-the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.
-There is NO WARRANTY, to the extent permitted by law.
-
-
 .SH "SEE ALSO"
 .BR ziomon_mgr (8),
 .BR ziomon_util (8),
Index: s390-tools-sles11/ziomon/ziomon_dacc.c
===================================================================
--- s390-tools-sles11.orig/ziomon/ziomon_dacc.c
+++ s390-tools-sles11/ziomon/ziomon_dacc.c
@@ -10,27 +10,25 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <time.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
 
 #include "ziomon_dacc.h"
 #include "ziomon_util.h"
 #include "ziomon_msg_tools.h"
 
 
-#define ZIOMON_DACC_NAME	"ziomon_dacc"
-
 #define ZIOMON_DACC_GARBAGE_MSG	-1
 
+extern const char *toolname;
 extern int verbose;
-#define vverbose_msg(x...)	do {\
-					if (verbose >= 2)\
-						printf("\t===     " x);\
-				} while (0)
 
 /*
  * Structure of binary file:
  *                                                                          |
  * +-----+---+---+-----+---+---+---------+---+---+---------+-      -+-----+ |
- * | hdr | l | t | msg | l | t |   msg   | l | t |   msg   |        | msg | |
+ * | hdr | l | t | dat | l | t |   dat   | l | t |   dat   |        | dat | |
  * +-----+---+---+-----+---+---+---------+---+---+---------+- .... -+-----+ |
  *                                                                          |
  *                                                                        limit
@@ -40,17 +38,27 @@ extern int verbose;
  * around, overwriting old messages. Hence it is utterly important that we keep
  * the lengths 'l' intact, since we will have random garbage after one of the
  * messages once we wrapped around for the first time.
- * The length is specified as __s32 and specifies the length of the variable
+ * 't' denotes the type of the message, 'dat' is the (variable length) data.
+ * The length is specified as __u32 and specifies the length of the variable
  * part of the message entry, beginning after the message type.
  * 'length' will always specify the length of the variable part of the message
  *
  * Aggregated statistics are put into a separate file.
  * Messages inside the aggregated file are written in fixed order (so we do not
  * need struct file_header to figure out what is where).
- * Note that we write a single garbage messages for any message that might have
+ * Note that we write a single garbage message for any message that might have
  * not been used yet!
  */
 
+
+/* indicates whether we already wrapped or not */
+static int wrapped = -1;
+
+#ifndef NDEBUG
+static int open_count = 0;
+#endif
+
+
 /**
  * Calculate size of the message. In contrast to the message's
  * length attribute, this also includes the header */
@@ -59,11 +67,12 @@ static int get_total_msg_size(struct mes
 }
 
 
+/**
+ * Position at first _physical_ message, not necessarily the first
+ * logical message */
 static int position_at_first_msg(FILE * fp)
 {
-	fseek(fp, sizeof(struct file_header), SEEK_SET);
-
-	return 0;
+	return fseek(fp, sizeof(struct file_header) - sizeof(__u64), SEEK_SET);
 }
 
 
@@ -79,6 +88,7 @@ static void swap_header(struct file_head
 	swap_32(f_hdr->msgid_zfcpdd);
 	swap_64(f_hdr->end_time);
 	swap_64(f_hdr->first_msg_offset);
+	swap_64(f_hdr->begin_time);
 }
 
 
@@ -95,9 +105,10 @@ static int write_f_header(FILE *fp, stru
 
 	rewind(fp);
 	swap_header(f_hdr);
-	if (fwrite(f_hdr, sizeof(struct file_header), 1, fp) != 1) {
-		fprintf(stderr, ZIOMON_DACC_NAME ": Failed to write"
-			" header\n");
+	if (fwrite(f_hdr, sizeof(struct file_header)
+		   - sizeof(__u64), 1, fp) != 1) {
+		fprintf(stderr, "%s: Failed to write"
+			" header\n", toolname);
 		rc = -1;
 	}
 	swap_header(f_hdr);
@@ -121,8 +132,8 @@ static int write_garbage_message(FILE *f
 
 	swap_msg_header(&msg);
 	if (!fwrite(&msg, 8, 1, fp)) {
-		fprintf(stderr, ZIOMON_DACC_NAME ": Error writing"
-			" garbage\n");
+		fprintf(stderr, "%s: Error writing"
+			" garbage\n", toolname);
 		return -1;
 	}
 
@@ -130,27 +141,42 @@ static int write_garbage_message(FILE *f
 }
 
 
-static int read_message(FILE *fp, struct message *msg)
+static int read_message_header(FILE *fp, __u32 *length, __u32 *type)
 {
+#ifndef NDEBUG
 	long pos = ftell(fp);
+#endif
 
-	if (fread(&msg->length, 4, 1, fp) != 1) {
+	if (fread(length, 4, 1, fp) != 1) {
 		if (feof(fp))
 			return 1;	/* end of file reached */
 		else {
-			fprintf(stderr, ZIOMON_DACC_NAME ": Error reading"
-				" message length\n");
+			fprintf(stderr, "%s: Error reading"
+				" message length\n", toolname);
 			return -1;
 		}
 	}
-	if (fread(&msg->type, 4, 1, fp) != 1) {
-		fprintf(stderr, ZIOMON_DACC_NAME ": Error reading message"
-			" type\n");
+	if (fread(type, 4, 1, fp) != 1) {
+		fprintf(stderr, "%s: Error reading message"
+			" type\n", toolname);
 		return -1;
 	}
-	swap_msg_header(msg);
-	vverbose_msg("read %smsg at pos=%ld, total size=%d\n",
-		     (msg->type == ZIOMON_DACC_GARBAGE_MSG ? "garbage " : ""), pos, get_total_msg_size(msg));
+	swap_32(*type);
+	swap_32(*length);
+
+	vverbose_msg("read %smsg at pos=%ld, data size=%d\n",
+		     (*type == ZIOMON_DACC_GARBAGE_MSG ? "garbage " : ""), pos, *length);
+
+	return 0;
+}
+
+
+static int read_message(FILE *fp, struct message *msg)
+{
+	int rc;
+
+	if ( (rc = read_message_header(fp, &msg->length, &msg->type)) )
+		return rc;
 
 	if (msg->type == ZIOMON_DACC_GARBAGE_MSG) {
 		fseek(fp, msg->length, SEEK_CUR);
@@ -159,8 +185,8 @@ static int read_message(FILE *fp, struct
 	else {
 		msg->data = malloc(msg->length);
 		if (fread(msg->data, msg->length, 1, fp) != 1) {
-			fprintf(stderr, ZIOMON_DACC_NAME ": Error reading"
-				" message content\n");
+			fprintf(stderr, "%s: Error reading %u Bytes message"
+				" content\n", toolname, msg->length);
 			return -1;
 		}
 	}
@@ -169,6 +195,34 @@ static int read_message(FILE *fp, struct
 }
 
 
+static int read_message_preview(FILE *fp, struct message_preview *msg)
+{
+	int rc;
+
+	msg->pos = ftell(fp);
+	if ( (rc = read_message_header(fp, &msg->length, &msg->type)) )
+		return rc;
+
+	/* Eventually read timestamp and fforward to next msg */
+	if (msg->type != ZIOMON_DACC_GARBAGE_MSG) {
+		/* per convention, the first 8 bytes of the actual message
+		 * is the timestamp. */
+		assert(msg->length >= 8);
+		if (fread(&msg->timestamp, 8, 1, fp) != 1) {
+			fprintf(stderr, "%s: Error reading"
+				" message timestamp\n", toolname);
+			return -1;
+		}
+		swap_64(msg->timestamp);
+		fseek(fp, msg->length - 8, SEEK_CUR);
+	}
+	else
+		fseek(fp, msg->length, SEEK_CUR);
+
+	return 0;
+}
+
+
 /**
  * Add msg to the bunch of aggregated messages.
  */
@@ -283,7 +337,7 @@ static __s32 position_in_file(FILE *fp, 
 	}
 	if (cum_size - get_total_msg_size(msg) > 0
 	    && cum_size - get_total_msg_size(msg) < 8) {
-		/* garbage message required, but even minimum size 
+		/* garbage message required, but even minimum size
 		   would overwrite start of next msg - aggregate one more! */
 		if (get_next_msg_size(fp, &next_msg_length)) {
 			/* corner case: we are at EOF, but our message
@@ -313,21 +367,21 @@ static int write_message(FILE *fp, struc
 		     get_total_msg_size(msg));
 	swap_msg_header(msg);
 	if (!fwrite(&msg->length, 4, 1, fp)) {
-		fprintf(stderr, ZIOMON_DACC_NAME ": Writing of length"
-			" failed\n");
+		fprintf(stderr, "%s: Writing of length"
+			" failed\n", toolname);
 		swap_msg_header(msg);
 		return -1;
 	}
 	if (!fwrite(&msg->type, 4, 1, fp)) {
-		fprintf(stderr, ZIOMON_DACC_NAME ": Writing of type failed\n");
+		fprintf(stderr, "%s: Writing of type failed\n", toolname);
 		swap_msg_header(msg);
 		return -2;
 	}
 	swap_msg_header(msg);
 
 	if (!fwrite(msg->data, msg->length, 1, fp)) {
-		fprintf(stderr, ZIOMON_DACC_NAME ": Writing of message data"
-			" failed\n");
+		fprintf(stderr, "%s: Writing of message data"
+			" failed\n", toolname);
 		return -3;
 	}
 
@@ -375,72 +429,92 @@ int init_file(FILE *fp, struct file_head
 	f_hdr->version = DATA_MGR_V2;
 	f_hdr->first_msg_offset = 0;
 	f_hdr->end_time = 0;
+	f_hdr->begin_time = 0;
 
 	if (write_f_header(fp, f_hdr))
 		return -1;
-/*	if (position_at_first_msg(fp))
-		return -1;*/
 
 	return 0;
 }
 
-int get_header(FILE *fp, struct file_header *hdr)
+
+static int get_header(FILE *fp, struct file_header *hdr)
 {
 	rewind(fp);
-	if (fread(hdr, sizeof(struct file_header), 1, fp) != 1) {
-		fprintf(stderr, ZIOMON_DACC_NAME ": Could not read header\n");
+	if (fread(hdr, sizeof(struct file_header)
+		  - sizeof(__u64), 1, fp) != 1) {
+		fprintf(stderr, "%s: Could not read header\n", toolname);
 		return -1;
 	}
 	swap_header(hdr);
 	if (hdr->magic != DATA_MGR_MAGIC) {
-		fprintf(stderr, ZIOMON_DACC_NAME ": No valid header\n");
+		fprintf(stderr, "%s: Unregocgnized data in .log file.\n",
+			toolname);
 		return -2;
 	}
 	if (hdr->version != DATA_MGR_V2) {
-		fprintf(stderr, ZIOMON_DACC_NAME ": Wrong version\n");
+		fprintf(stderr, "%s: Wrong version: .log data is in version %u"
+			" format, while this tool only supports version %u."
+			" Get the matching tool version and try again.\n",
+			toolname, hdr->version, DATA_MGR_V2);
 		return -2;
 	}
+	hdr->begin_time = 0;
 
 	return 0;
 }
 
 
-int get_next_msg(FILE *fp, struct message *msg, struct file_header *f_hdr)
+int open_log_file(FILE **fp, const char *filename, struct file_header *fhdr)
 {
-	int rc;
-	static long pos = 0;
-	static int wrapped = 0;
-
-	if (pos == 0) {
-		if (f_hdr->first_msg_offset)
-			pos = f_hdr->first_msg_offset;
-		else {
-			pos = sizeof(struct file_header);
-			wrapped++;	/* no need to wrap */
-		}
-		fseek(fp, pos, SEEK_SET);
-	}
+	int rc = 0;
+	char *fname = NULL;
+	struct message_preview msg_prev;
 
-	do {
-		rc = read_message(fp, msg);
-		if (rc > 0 && !wrapped) {
-			position_at_first_msg(fp);
-			rc = read_message(fp, msg);
-			wrapped++;
-		}
-	} while (!rc && msg->type == ZIOMON_DACC_GARBAGE_MSG);
+	fname = (char*)malloc(strlen(filename) + strlen(DACC_FILE_EXT_LOG) + 1);
+	sprintf(fname, "%s%s", filename, DACC_FILE_EXT_LOG);
+	*fp = fopen(fname, "r");
+	if (!*fp) {
+		fprintf(stderr, "%s: Could not open %s"
+			" - file not accessible?", toolname, fname);
+		rc = 1;
+		goto out;
+	}
+	if (get_header(*fp, fhdr)) {
+		rc = -2;
+		goto out;
+	}
+	if (get_next_msg_preview(*fp, &msg_prev, fhdr)) {
+		rc = -3;
+		goto out;
+	}
+	rewind_to(*fp, &msg_prev);
+	fhdr->begin_time = msg_prev.timestamp;
+
+out:
+	free(fname);
+	if (rc < 0)
+		fclose(*fp);
 
 	return rc;
 }
 
 
-void discard_msg(struct message *msg)
+void close_log_file(FILE *fp)
 {
-	if (msg) {
-		free(msg->data);
-		msg->data = NULL;
+	wrapped = -1;
+	if (fp)
+		fclose(fp);
+}
 
-	}
+
+void close_data_files(FILE *fp)
+{
+#ifndef NDEBUG
+	open_count--;
+	assert(open_count == 0);
+#endif
+	close_log_file(fp);
 }
 
 
@@ -448,14 +522,24 @@ static void swap_agg_header(struct aggr_
 {
 	swap_32(hdr->magic);
 	swap_32(hdr->version);
-	swap_64(hdr->timestamp);
+	swap_64(hdr->begin_time);
 	swap_64(hdr->num_zfcpdd);
 	swap_64(hdr->num_blkiomon);
 	swap_64(hdr->end_time);
 }
 
 
-int read_aggr_file(FILE *fp, struct aggr_data *data)
+static void conv_agg_header_from_BE(struct aggr_data *hdr) {
+	swap_agg_header(hdr);
+}
+
+
+static void conv_agg_header_to_BE(struct aggr_data *hdr) {
+	swap_agg_header(hdr);
+}
+
+
+static int read_aggr_file(FILE *fp, struct aggr_data *data)
 {
 	int i;
 	int rc;
@@ -463,15 +547,28 @@ int read_aggr_file(FILE *fp, struct aggr
 
 	rewind(fp);
 	if (fread(data, DACC_AGGR_FILE_HDR_LEN, 1, fp) != 1) {
-		fprintf(stderr, ZIOMON_DACC_NAME ": Error reading aggregation"
-			" content\n");
+		fprintf(stderr, "%s: Error reading aggregation"
+			" content\n", toolname);
+		return -1;
+	}
+	conv_agg_header_from_BE(data);
+	if (data->magic != DATA_MGR_MAGIC_AGGR) {
+		fprintf(stderr, "%s: Unregocgnized data in .agg file.\n",
+			toolname);
+		return -1;
+	}
+	if (data->version != DATA_MGR_V2) {
+		fprintf(stderr, "%s: Wrong version: .agg data is in version %u"
+			" format, while this tool only supports version %u."
+			" Get the matching tool version and try again.\n",
+			toolname, data->version, DATA_MGR_V2);
 		return -1;
 	}
-	swap_agg_header(data);
 
 	data->util_aggr = NULL;
 	if ( (rc = read_message(fp, &msg)) < 0)
 		return -2;
+
 	if (msg.type != ZIOMON_DACC_GARBAGE_MSG) {
 		data->util_aggr = malloc(sizeof(struct message));
 		*(data->util_aggr) = msg;
@@ -521,17 +618,153 @@ int read_aggr_file(FILE *fp, struct aggr
 }
 
 
+int open_agg_file(FILE **fp, const char *filename, struct aggr_data *agg)
+{
+	int rc = 0;
+	char *fname = NULL;
+
+	fname = (char*)malloc(strlen(filename) + strlen(DACC_FILE_EXT_AGG) + 1);
+	sprintf(fname, "%s%s", filename, DACC_FILE_EXT_AGG);
+
+	/* test the water */
+	if (access(fname, F_OK) != 0) {
+		rc = 1;
+		goto out;
+	}
+	*fp = fopen(fname, "r");
+	if (!*fp) {
+		fprintf(stderr, "%s: Could not open %s"
+			" - file not accessible?", toolname, fname);
+		rc = 1;
+		goto out;
+	}
+	if (read_aggr_file(*fp, agg)) {
+		fclose(*fp);
+		rc = -1;
+		goto out;
+	}
+
+out:
+	free(fname);
+
+	return rc;
+}
+
+
+void close_agg_file(FILE *fp)
+{
+	if (fp)
+		fclose(fp);
+}
+
+
+static int seek_initial_file_pos(FILE *fp, struct file_header *f_hdr)
+{
+	int rc = 0;
+	long pos;
+
+	if (f_hdr->first_msg_offset)
+		pos = f_hdr->first_msg_offset;
+	else {
+		pos = sizeof(struct file_header) - sizeof(__u64);
+		rc = 1;	/* no need to wrap */
+	}
+	fseek(fp, pos, SEEK_SET);
+
+	return rc;
+}
+
+
+int get_next_msg(FILE *fp, struct message *msg, struct file_header *f_hdr)
+{
+	int rc;
+
+	if (wrapped < 0)
+		wrapped = seek_initial_file_pos(fp, f_hdr);
+
+	do {
+		if (f_hdr->first_msg_offset != 0 && wrapped
+		    && ftell(fp) >= f_hdr->first_msg_offset)
+			return 1;	/* final msg read */
+
+		rc = read_message(fp, msg);
+		if (rc > 0 && !wrapped) {
+			position_at_first_msg(fp);
+			rc = read_message(fp, msg);
+			wrapped++;
+		}
+	} while (!rc && msg->type == ZIOMON_DACC_GARBAGE_MSG);
+
+	return rc;
+}
+
+
+int get_next_msg_preview(FILE *fp, struct message_preview *msg,
+			 struct file_header *f_hdr)
+{
+	int rc;
+
+	if (wrapped < 0)
+		wrapped = seek_initial_file_pos(fp, f_hdr);
+
+	do {
+		if (f_hdr->first_msg_offset != 0 && wrapped
+		    && ftell(fp) >= f_hdr->first_msg_offset)
+			return 1;	/* final msg read */
+
+		rc = read_message_preview(fp, msg);
+		if (rc > 0 && !wrapped) {
+			position_at_first_msg(fp);
+			rc = read_message_preview(fp, msg);
+			wrapped++;
+		}
+	} while (!rc && msg->type == ZIOMON_DACC_GARBAGE_MSG);
+
+	return rc;
+}
+
+
+void rewind_to(FILE *fp, struct message_preview *msg)
+{
+	assert(msg->pos > 0);
+	fseek(fp, msg->pos, SEEK_SET);
+}
+
+
+int get_complete_msg(FILE *fp, struct message_preview *msg_prev,
+		     struct message *msg)
+{
+	long pos = ftell(fp);
+	int rc;
+
+	fseek(fp, msg_prev->pos, SEEK_SET);
+	rc = read_message(fp, msg);
+	fseek(fp, pos, SEEK_SET);
+
+	return rc;
+}
+
+
+void discard_msg(struct message *msg)
+{
+	if (msg) {
+		free(msg->data);
+		msg->data = NULL;
+
+	}
+}
+
+
 int write_aggr_file(FILE *fp, struct aggr_data *data)
 {
 	int i;
 
-	swap_agg_header(data);
+	conv_agg_header_to_BE(data);
 	rewind(fp);
-	if (fwrite(data, DACC_AGGR_FILE_HDR_LEN, 1, fp) != 1) {
-		swap_agg_header(data);
+	i = fwrite(data, DACC_AGGR_FILE_HDR_LEN, 1, fp);
+	conv_agg_header_from_BE(data);
+	if (i != 1)
 		return -1;
-	}
-	swap_agg_header(data);
 
 	if (data->util_aggr) {
 		if (write_message(fp, data->util_aggr))
@@ -576,7 +809,7 @@ void init_aggr_data_struct(struct aggr_d
 	data->num_zfcpdd = 0;
 	data->num_blkiomon = 0;
 	data->end_time = 0;
-	data->timestamp = 0;
+	data->begin_time = 0;
 	data->util_aggr = NULL;
 	data->ioerr_aggr = NULL;
 	data->blkio_aggr = NULL;
@@ -588,20 +821,127 @@ void discard_aggr_data_struct(struct agg
 {
 	unsigned int i;
 
-	discard_msg(data->util_aggr);
-	discard_msg(data->ioerr_aggr);
-	for (i=0; i<data->num_blkiomon; ++i) {
-		discard_msg(data->blkio_aggr[i]);
-		free(data->blkio_aggr[i]);
-	}
-	for (i=0; i<data->num_zfcpdd; ++i) {
-		discard_msg(data->zfcpdd_aggr[i]);
-		free(data->zfcpdd_aggr[i]);
-	}
-	free(data->util_aggr);
-	free(data->ioerr_aggr);
-	free(data->blkio_aggr);
-	free(data->zfcpdd_aggr);
+	if (data) {
+		discard_msg(data->util_aggr);
+		discard_msg(data->ioerr_aggr);
+		for (i=0; i<data->num_blkiomon; ++i) {
+			discard_msg(data->blkio_aggr[i]);
+			free(data->blkio_aggr[i]);
+		}
+		for (i=0; i<data->num_zfcpdd; ++i) {
+			discard_msg(data->zfcpdd_aggr[i]);
+			free(data->zfcpdd_aggr[i]);
+		}
+		free(data->util_aggr);
+		free(data->ioerr_aggr);
+		free(data->blkio_aggr);
+		free(data->zfcpdd_aggr);
+	}
+}
+
+
+int open_data_files(FILE **fp, const char *filename, struct file_header *f_hdr,
+	      struct aggr_data **agg)
+{
+	struct message_preview msg_prev;
+	struct message msg;
+	int rc = 0;
+	int i;
+	__u64 end_of_agg;
+	time_t t;
+
+#ifndef NDEBUG
+	assert(open_count == 0);
+	open_count++;
+#endif
+
+	verbose_msg("open data\n");
+
+	/*
+	 * Open .agg file if exists
+	 */
+
+	*agg = (struct aggr_data*)malloc(sizeof(struct aggr_data));
+	if ( (rc = open_agg_file(fp, filename, *agg)) < 0 ) {
+		free(*agg);
+		return -1;
+	}
+	if (rc == 0) {
+		verbose_msg("  found .agg file\n");
+		close_agg_file(*fp);
+	}
+	else {
+		verbose_msg("  no .agg file found\n");
+		free(*agg);
+		*agg = NULL;
+	}
+
+	/*
+	 * Open .log file
+	 */
+	if ( (rc = open_log_file(fp, filename, f_hdr)) )
+		return -1;
+
+	/*
+	 * Eventually add messages from final frame of .agg file and adjust
+	 * respective boundaries
+	 */
+
+	if (*agg) {
+		conv_aggr_data_msg_data_from_BE(*agg);
+
+		/* We use the first message that we have as the basis to
+		   calculate when the final timeframe of the .agg data
+		   would have ended. Note that we always add all messages
+		   that are interval/2 after that timestamp! */
+		end_of_agg = ((*agg)->end_time - (*agg)->begin_time -
+			f_hdr->interval_length / 2) % f_hdr->interval_length;
+		if (end_of_agg)
+			end_of_agg = (*agg)->end_time
+				+ (f_hdr->interval_length - end_of_agg);
+
+		i = 0;
+		while ( (rc = get_next_msg_preview(*fp, &msg_prev, f_hdr)) == 0
+			 && msg_prev.timestamp <= end_of_agg) {
+			if (get_complete_msg(*fp, &msg_prev, &msg))
+			    return -1;
+			rc = add_to_agg(*agg, &msg, f_hdr);
+			discard_msg(&msg);
+			if (rc)
+				return -1;
+			++i;
+		}
+		if (rc < 0) {
+			fprintf(stderr, "%s: Could not read"
+				" any messages in %s%s\n", toolname, filename,
+				DACC_FILE_EXT_LOG);
+			return -1;
+		}
+		// condition of the check impossible to fail, but still...
+		if (msg_prev.timestamp > end_of_agg)
+			rewind_to(*fp, &msg_prev);
+
+		// finally, adjust boundaries
+		(*agg)->end_time = end_of_agg - f_hdr->interval_length / 2;
+		f_hdr->begin_time = (*agg)->end_time + f_hdr->interval_length;
+		if (verbose > 0) {
+			t = (*agg)->end_time;
+			verbose_msg("  adjust agg end time to  : %s",
+				    ctime(&t));
+			t = (*agg)->begin_time;
+			verbose_msg("  adjust log begin time to: %s",
+				    ctime(&t));
+		}
+
+		conv_aggr_data_msg_data_to_BE(*agg);
+		verbose_msg("  added %d messages to aggregated structure\n",
+			    i);
+	}
+
+	verbose_msg("open data finished\n");
+
+	return 0;
 }
 
 
+
Index: s390-tools-sles11/ziomon/ziomon_dacc.h
===================================================================
--- s390-tools-sles11.orig/ziomon/ziomon_dacc.h
+++ s390-tools-sles11/ziomon/ziomon_dacc.h
@@ -40,6 +40,19 @@ struct message {
 	void   *data;
 } __attribute__ ((packed));
 
+/**
+ * Preview version of a message. Has timestamp included,
+ * but not the actual content.
+ */
+struct message_preview {
+	__u32	length;	/* length of the message in Bytes,
+			   excluding the 'length' and 'type' attributes.
+			   Or, in other word, length of 'data' */
+	__u32	type;
+	__u64   timestamp;
+	long	pos;	/* position in file where msg starts */
+};
+
 #define DACC_FILE_EXT_LOG	".log"
 struct file_header {
 	__u32	magic;
@@ -63,6 +76,16 @@ struct file_header {
 	__u32	msgid_ioerr;
 	__u32	msgid_blkiomon;
 	__u32	msgid_zfcpdd;
+
+	/* NOT WRITTEN TO DISK!!! */
+	/* Since we might move messages from .log to .agg in a round-robin
+	 * fashion, it is not trivial to get the timestamp of the first message
+	 * in .log - each time we write a new message, we would have to read
+	 * the next one and rewind again.
+	 * Hence we figure this one out only when we open a finished .log file,
+	 * but do not actually write it to disk.
+	*/
+	__u64	begin_time;
 } __attribute__ ((packed));
 
 
@@ -71,7 +94,7 @@ struct file_header {
 struct aggr_data {
 	__u32	magic;
 	__u32	version;
-	__u64	timestamp;	/* start time of aggregated data */
+	__u64	begin_time;	/* start time of aggregated data */
 	__u64	end_time;	/* timestamp of latest message */
 	__u64	num_zfcpdd;	/* number of zfcpdd messages */
 	__u64	num_blkiomon;	/* number of blkiomon messages */
@@ -89,6 +112,42 @@ struct aggr_data {
  */
 int init_file(FILE *fp, struct file_header *f_hdr);
 
+
+/**
+ * Open an existing .log file and read its header.
+ * Returns <0 in case of error, >0 if file doesn't exist.
+ * 'filename' is assumed to NOT carry the .log extension.
+ * ONLY USE IF YOU KNOW WHAT YOU DO, i.e. in case you want to access
+ * the as-is data. Anyone else should rather use open_data().
+ * NOTE: Use close_log_file() when finished! */
+int open_log_file(FILE **fp, const char *filename, struct file_header *fhdr);
+
+
+/**
+ * Must be called to close fp and reset internals */
+void close_log_file(FILE *fp);
+
+/**
+ * Open the data files. This function will not only open the .log and .agg
+ * files, but also
+ * - read and aggregate the first few messages from .log in case they are
+ *   missing in the 'agg' data to complete the respective frame.
+ * - adjust the boundaries of both, .log and .agg so that .agg has the
+ *   complete content of the last frame it scratched upon and the next message
+ *   read from .log will be the first message of the first frame not in .agg
+ * Returns <0 in case of error, >0 if file doesn't exist.
+ * 'filename' is assumed to NOT carry the .log or .agg extension.
+ * Note that, consistent with all other API calls, the messages inside 'agg'
+ * are still in BE format!
+ * NOTE: Use close_data_files() when finished! */
+int open_data_files(FILE **fp, const char *filename, struct file_header *fhdr,
+	      struct aggr_data **agg);
+
+
+/**
+ * Must be called to close fp and reset internals */
+void close_data_files(FILE *fp);
+
 /**
  * Put a message into the file. Will automatically wrap around.
  * If existing messages have to be deleted to add the new message,
@@ -102,18 +161,35 @@ int add_msg(FILE *fp, struct message *ms
 	    struct message ***del_msgs, int *num_del_msgs);
 
 /**
- * Retrieve the header information and go to first message.
+ * Retrieve the next message from the file. Note that the returned message has
+ * to be discarded!
+ * Returns 0 if successful, >0 if end of file reached, <0 in case of error.
  * fp is assumed to have been opened.
  */
-int get_header(FILE *fp, struct file_header *hdr);
+int get_next_msg(FILE *fp, struct message *msg, struct file_header *f_hdr);
 
 /**
- * Retrieve the next message from the file. Note that the returned message has
- * to be discarded!
+ * Retrieve preview of next message from the file.
+ * Note that fp will be forwarded to the next message!
+ * If you want the actual message, use get_complete_msg().
+ * If you want to stay at the message, use rewind_to().
  * Returns 0 if successful, >0 if end of file reached, <0 in case of error.
  * fp is assumed to have been opened.
  */
-int get_next_msg(FILE *fp, struct message *msg, struct file_header *f_hdr);
+int get_next_msg_preview(FILE *fp, struct message_preview *msg,
+			 struct file_header *f_hdr);
+
+/**
+ * Rewinds to the start of the provided message preview.
+ * Handy in case you could not process the message preview on the
+ * first time and want to make sure that you read it again next time. */
+void rewind_to(FILE *fp, struct message_preview *msg);
+
+/**
+ * Get complete message for a preview. Rewinds back to where it was at.
+ */
+int get_complete_msg(FILE *fp, struct message_preview *msg_prev,
+		     struct message *msg);
 
 /**
  * Frees the alloc'd portion of a message.
@@ -126,15 +202,22 @@ void discard_msg(struct message *msg);
 void init_aggr_data_struct(struct aggr_data *data);
 
 /**
- * Frees the alloc'd portion of the struct.
- */
-void discard_aggr_data_struct(struct aggr_data *data);
+ * Open an existing .agg file and read its header.
+ * Returns <0 in case of error, >0 if file doesn't exist.
+ * 'filename' is assumed to NOT carry the .log extension.
+ * ONLY USE IF YOU KNOW WHAT YOU DO, i.e. in case you want to access
+ * the as-is data. Anyone else should rather use open_data().
+ * NOTE: Use close_agg_file() when finished! */
+int open_agg_file(FILE **fp, const char *filename, struct aggr_data *agg);
 
 /**
- * Read content of a file holding aggregated data.
- * fp is assumed to have been opened.
+ * Must be called to close fp and reset internals */
+void close_agg_file(FILE *fp);
+
+/**
+ * Frees the alloc'd portion of the struct.
  */
-int read_aggr_file(FILE *fp, struct aggr_data *data);
+void discard_aggr_data_struct(struct aggr_data *data);
 
 /**
  * Write aggregated data to file.
@@ -144,3 +227,4 @@ int write_aggr_file(FILE *fp, struct agg
 
 
 #endif
+
Index: s390-tools-sles11/ziomon/ziomon_fcpconf
===================================================================
--- s390-tools-sles11.orig/ziomon/ziomon_fcpconf
+++ s390-tools-sles11/ziomon/ziomon_fcpconf
@@ -17,8 +17,8 @@ use File::Spec::Functions;
 use Getopt::Long;
 
 sub version {
-	print "$PROGRAM_NAME: version %S390_TOOLS_VERSION%.\n";
-	print "Copyright IBM Corp. 2008.\n";
+	print "$PROGRAM_NAME: version %S390_TOOLS_VERSION%\n";
+	print "Copyright IBM Corp. 2008\n";
 }
 
 sub usage
@@ -32,7 +32,7 @@ Options:
 
         -h, --help
 		print this help text and exit.
-	
+
 	-v, --version
 		print version information and exit.
 
@@ -84,8 +84,8 @@ system("mkdir -p $temp_dir/sys/devices/v
 system("mkdir -p $temp_dir/sys/class");
 system("mkdir -p $temp_dir/dev/mapper");
 system("rsync -a --inplace /sys/block/dm* /sys/block/sd* $temp_dir/sys/block 2>/dev/null");
-system("rsync -a --inplace --exclude=chpd* /sys/devices/css0 $temp_dir/sys/devices 2>/dev/null"); 
-system("rsync -a --inplace /sys/devices/virtual/block/dm* $temp_dir/sys/devices/virtual/block 2>/dev/null"); 
-system("rsync -a --inplace /sys/class/fc_host /sys/class/scsi_device /sys/class/scsi_tape /sys/class/fc_remote_ports $temp_dir/sys/class 2>/dev/null");
+system("rsync -a --inplace --exclude=chpd* /sys/devices/css0 $temp_dir/sys/devices 2>/dev/null");
+system("rsync -a --inplace /sys/devices/virtual/block/dm* $temp_dir/sys/devices/virtual/block 2>/dev/null");
+system("rsync -a --inplace /sys/class/fc_host /sys/class/scsi_device /sys/class/scsi_tape /sys/class/scsi_generic /sys/class/fc_remote_ports $temp_dir/sys/class 2>/dev/null");
 store_mapper_devices($temp_dir,"/dev/mapper");
 system("tar -czf $out_file.cfg -C $temp_dir sys dev 2>/dev/null");
Index: s390-tools-sles11/ziomon/ziomon_fcpconf.8
===================================================================
--- s390-tools-sles11.orig/ziomon/ziomon_fcpconf.8
+++ s390-tools-sles11/ziomon/ziomon_fcpconf.8
@@ -24,19 +24,6 @@ Print version information, then exit.
 .BR "\-o" " or " "\-\-output"
 Specify the outfile basename. The suffix .cfg will be appended. Defaults to 'config'.
 
-
-.SH AUTHORS
-This man page was written by Stefan Raspl.
-
-
-.SH COPYRIGHT
-Copyright \(co 2008 IBM Corp.
-.br
-This is free software.  You may redistribute copies of it under the terms of
-the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.
-There is NO WARRANTY, to the extent permitted by law.
-
-
 .SH "SEE ALSO"
 .BR ziomon (8)
 .BR ziomon_mgr (8),
Index: s390-tools-sles11/ziomon/ziomon_mgr.8
===================================================================
--- s390-tools-sles11.orig/ziomon/ziomon_mgr.8
+++ s390-tools-sles11/ziomon/ziomon_mgr.8
@@ -87,7 +87,6 @@ Must be an integer >0.
 Id of the messages that ziomon_zfcpdd will send.
 Must be an integer >0.
 
-
 .SH EXAMPLES
 Start a message queue named /tmp/ziomon.23943 with id 95.
 Utilization messages from ziomon_util will carry the id 17, ioerr messages 18,
@@ -97,18 +96,6 @@ be 20 seconds. The output files should b
 
 ziomon_mgr -i 20 -Q /tmp/ziomon.23943 -q 95 -u 17 -r 18 -b 47 -z 59 -o trace_data
 
-.SH AUTHORS
-ziomon_mgr and this man page were written by Stefan Raspl.
-
-
-.SH COPYRIGHT
-Copyright \(co 2008 IBM Corp.
-.br
-This is free software.  You may redistribute copies of it under the terms of
-the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.
-There is NO WARRANTY, to the extent permitted by law.
-
-
 .SH "SEE ALSO"
 .BR ziomon (8),
 .BR ziomon_util (8),
Index: s390-tools-sles11/ziomon/ziomon_mgr.c
===================================================================
--- s390-tools-sles11.orig/ziomon/ziomon_mgr.c
+++ s390-tools-sles11/ziomon/ziomon_mgr.c
@@ -34,9 +34,7 @@
 
 
 
-#define ZIOMON_MGR_NAME		"ziomon_mgr"
-
-
+const char *toolname = "ziomon_mgr";
 int verbose=0;
 static int keep_running = 1;
 
@@ -89,9 +87,9 @@ static void deinit_opts(struct options *
 	if (opts->msg_q >= 0) {
 		verbose_msg("shutting down message queue\n");
 		if (msgctl(opts->msg_q, IPC_RMID, 0) == -1)
-			fprintf(stderr, ZIOMON_MGR_NAME ": Error encountered"
+			fprintf(stderr, "%s: Error encountered"
 				" while shutting down message queue: %s\n",
-				strerror(errno));
+				toolname, strerror(errno));
 	}
 	if (opts->outfile)
 		fclose(opts->outfile);
@@ -105,7 +103,7 @@ static void deinit_opts(struct options *
 
 
 static const char help_text[] =
-  "Usage: " ZIOMON_MGR_NAME " [-h] [-v] [-V] [-e] [-f] [-l <size>]"
+  "Usage: ziomon_mgr [-h] [-v] [-V] [-e] [-f] [-l <size>]"
   " -o <filename> -i <length>\n"
   "                  -Q <msgq-path> -q <msgq-id> -u <util-id> -r <ioerr-id>\n"
   "                  -b <blkiomon-id> -z <ziomon_zfcpdd-id>\n"
@@ -129,17 +127,17 @@ static const char help_text[] =
   "-o, --output            Specify the name of the output file(s).\n"
   "-l, --size-limit        Maximum size of data collected in MB.\n";
 
-static void print_help(const char *name)
+static void print_help(void)
 {
         fprintf(stdout, "%s", help_text);
 }
 
 
-static void print_version(const char *name)
+static void print_version(void)
 {
-        fprintf(stdout, "%s: ziomon manager, version %s.\n"
-               "Copyright IBM Corp. 2008.\n",
-               name, RELEASE_STRING);
+        fprintf(stdout, "%s: ziomon manager, version %s\n"
+               "Copyright IBM Corp. 2008\n",
+               toolname, RELEASE_STRING);
 }
 
 
@@ -151,11 +149,11 @@ static int setup_msg_q(struct options *o
 	util_q = ftok(opts->msg_q_path, opts->msg_q_id);
 
 	if (util_q == -1) {
-		fprintf(stderr, ZIOMON_MGR_NAME ": Error: Cannot create"
+		fprintf(stderr, "%s: Cannot create"
 			" message queue with path %s and id %d: %s\n",
-			opts->msg_q_path, opts->msg_q_id, strerror(errno));
+			toolname, opts->msg_q_path, opts->msg_q_id, strerror(errno));
 		return -1;
-	}	
+	}
 
 	verbose_msg("message queue key is %d\n", util_q);
 
@@ -164,11 +162,11 @@ static int setup_msg_q(struct options *o
 		flags |= IPC_EXCL;
 	opts->msg_q = msgget(util_q, flags);
 	if (opts->msg_q < 0) {
-		fprintf(stderr, ZIOMON_MGR_NAME ": Could not open message queue"
-			": %s\n", strerror(errno));
+		fprintf(stderr, "%s: Could not open message queue"
+			": %s\n", toolname, strerror(errno));
 		if (!opts->force)
-			fprintf(stderr, ZIOMON_MGR_NAME ": Retry using the"
-							" 'force' option\n");
+			fprintf(stderr, "%s: Retry using the 'force'"
+				" option\n", toolname);
 		return -1;
 	}
 	verbose_msg("message queue id is %d\n", opts->msg_q);
@@ -177,61 +175,7 @@ static int setup_msg_q(struct options *o
 }
 
 
-static void aggregate_blkiomon(struct message *msg, struct options *opts)
-{
-	int i;
-	struct message **tmp;
-	struct blkiomon_stat *stat = msg->data;
-
-	for (i=0; i<opts->agg_data.num_blkiomon; ++i)
-		if ((((struct blkiomon_stat*)(opts->agg_data.blkio_aggr[i]->data))->device)
-			== stat->device)
-			break;
-	if (i >= opts->agg_data.num_blkiomon) {
-		/* messages for this device not aggregated yet
-		   - add a new entry */
-		tmp = opts->agg_data.blkio_aggr;
-		++(opts->agg_data.num_blkiomon);
-		opts->agg_data.blkio_aggr =
-			malloc(opts->agg_data.num_blkiomon * sizeof(struct message*));
-		for (i = 0; i < opts->agg_data.num_blkiomon - 1; ++i)
-			opts->agg_data.blkio_aggr[i] = tmp[i];
-		copy_msg(msg, &opts->agg_data.blkio_aggr[i]);
-		free(tmp);
-	}
-	else
-		blkiomon_stat_merge(opts->agg_data.blkio_aggr[i]->data, msg->data);
-}
-
-
-static void aggregate_zfcpdd(struct message *msg, struct options *opts)
-{
-	int i;
-	struct message **tmp;
-	struct zfcpdd_dstat *stat = msg->data;
-
-	for (i=0; i<opts->agg_data.num_zfcpdd; ++i)
-		if ((((struct zfcpdd_dstat*)(opts->agg_data.zfcpdd_aggr[i]->data))->device)
-			== stat->device)
-			break;
-	if (i >= opts->agg_data.num_zfcpdd) {
-		/* messages for this device not aggregated yet
-		   - add a new entry */
-		tmp = opts->agg_data.zfcpdd_aggr;
-		++(opts->agg_data.num_zfcpdd);
-		opts->agg_data.zfcpdd_aggr =
-			malloc(opts->agg_data.num_zfcpdd * sizeof(struct message*));
-		for (i = 0; i < opts->agg_data.num_zfcpdd - 1; ++i)
-			opts->agg_data.zfcpdd_aggr[i] = tmp[i];
-		copy_msg(msg, &opts->agg_data.zfcpdd_aggr[i]);
-		free(tmp);
-	}
-	else
-		aggregate_dstat(msg->data, opts->agg_data.zfcpdd_aggr[i]->data);
-}
-
-
-static int add_to_aggregated(struct message **msg, int num_msgs,
+static int add_to_aggregated(struct message **msgs, int num_msgs,
 			     struct options *opts)
 {
 	int i;
@@ -239,72 +183,59 @@ static int add_to_aggregated(struct mess
 	if (!opts->outfile_agg) {
 		opts->outfile_agg = fopen(opts->outfile_name_agg, "w+");
 		if (!opts->outfile_agg) {
-			fprintf(stderr, ZIOMON_MGR_NAME ": Could not open file"
-				" for aggregated data: %s\n", strerror(errno));
+			fprintf(stderr, "%s: Could not open file"
+				" for aggregated data: %s\n", toolname,
+				strerror(errno));
 			return -1;
 		}
 		init_aggr_data_struct(&opts->agg_data);
-		/* init with oldest message - we can assume that the first
-		   message is the oldest one. */
-		opts->agg_data.timestamp = *(__u64*)(msg[0]->data);
-		swap_64(opts->agg_data.timestamp);
 	}
 
 	/* aggregate data */
 	for (i = 0; i < num_msgs; ++i) {
-		conv_msg_data_from_BE(msg[i], &opts->f_hdr);
-		if (msg[i]->type == opts->msg_id_utilization) {
-			if (opts->agg_data.util_aggr)
-				aggregate_utilization_data(msg[i]->data,
-					    opts->agg_data.util_aggr->data);
-			else
-				copy_msg(msg[i], &opts->agg_data.util_aggr);
-		} else if (msg[i]->type == opts->msg_id_ioerr) {
-			if (opts->agg_data.ioerr_aggr)
-				aggregate_ioerr_data(msg[i]->data,
-					    opts->agg_data.ioerr_aggr->data);
-			else
-				copy_msg(msg[i], &opts->agg_data.ioerr_aggr);
-		} else if (msg[i]->type == opts->msg_id_blkiomon)
-			aggregate_blkiomon(msg[i], opts);
-		else if (msg[i]->type == opts->msg_id_zfcpdd)
-			aggregate_zfcpdd(msg[i], opts);
-		else {
-			fprintf(stderr, ZIOMON_MGR_NAME ": Unknow msg id: %d,"
-				" discarding\n", msg[i]->type);
-			continue;
-		}
-		opts->agg_data.end_time = *(__u64*)(msg[i]->data);
+		if (add_to_agg(&opts->agg_data, msgs[i], &opts->f_hdr))
+			return -1;
+		discard_msg(msgs[i]);
+		free(msgs[i]);
 	}
+	free(msgs);
 
 	/* write back to file */
-	conv_aggr_data_msg_data_to_BE(&opts->agg_data, &opts->f_hdr);
+	conv_aggr_data_msg_data_to_BE(&opts->agg_data);
 	i = write_aggr_file(opts->outfile_agg, &opts->agg_data);
-	conv_aggr_data_msg_data_from_BE(&opts->agg_data, &opts->f_hdr);
-	
+	conv_aggr_data_msg_data_from_BE(&opts->agg_data);
+
 	return i;
 }
 
+static int compare_msg_ids(const void *a, const void *b)
+{
+	return (*(long*)b - *(long*)a);
+}
 
 static int check_msg_ids(struct options *opts)
 {
-	if (opts->msg_id_utilization <= 0
-	    || opts->msg_id_ioerr <= 0
-	    || opts->msg_id_blkiomon <= 0
-	    || opts->msg_id_zfcpdd <= 0) {
-		fprintf(stderr, ZIOMON_MGR_NAME ": ids for messages must be"
-			" greater than 0\n");
-		return -1;
-	}
-	if (opts->msg_id_utilization == opts->msg_id_blkiomon
-	    || opts->msg_id_utilization == opts->msg_id_ioerr
-	    || opts->msg_id_utilization == opts->msg_id_zfcpdd
-	    || opts->msg_id_blkiomon == opts->msg_id_zfcpdd
-	    || opts->msg_id_blkiomon == opts->msg_id_ioerr
-	    || opts->msg_id_zfcpdd == opts->msg_id_ioerr) {
-		fprintf(stderr, ZIOMON_MGR_NAME ": ids for messages must be"
-			" different from another\n");
-		return -1;
+	int i;
+	long msg_ids[4];
+
+	msg_ids[0] = opts->msg_id_utilization;
+	msg_ids[1] = opts->msg_id_ioerr;
+	msg_ids[2] = opts->msg_id_blkiomon;
+	msg_ids[3] = opts->msg_id_zfcpdd;
+
+	qsort(msg_ids, 4, sizeof(long), compare_msg_ids);
+
+	for (i = 0; i < 4; ++i) {
+		if (msg_ids[i] <= 0) {
+			fprintf(stderr, "%s: ids for messages must be"
+				" greater than 0\n", toolname);
+			return -1;
+		}
+		if (i != 0 && msg_ids[i] == msg_ids[i-1]) {
+			fprintf(stderr, "%s: ids for messages must be"
+				" different from another\n", toolname);
+			return -1;
+		}
 	}
 
 	return 0;
@@ -314,8 +245,8 @@ static int check_msg_ids(struct options 
 void print_bin_struct_sizes(void)
 {
 	fprintf(stdout, "%ld %ld %ld %ld %ld %ld\n",
-		(unsigned long int)sizeof(struct overall_result),
-		(unsigned long int)sizeof(struct adapter_result),
+		(unsigned long int)sizeof(struct utilization_data),
+		(unsigned long int)sizeof(struct adapter_utilization),
 		(unsigned long int)sizeof(struct ioerr_data),
 		(unsigned long int)sizeof(struct ioerr_cnt),
 		(unsigned long int)sizeof(struct zfcpdd_dstat),
@@ -326,14 +257,14 @@ void print_bin_struct_sizes(void)
 static int convert_long_optarg(long *tgt, char *option_str)
 {
 	if (!optarg) {
-		fprintf(stderr, ZIOMON_MGR_NAME ": Error: Argument missing to"
-			" option %s\n", option_str);
+		fprintf(stderr, "%s: Error: Argument missing to"
+			" option %s\n", toolname, option_str);
 		return -1;
 	}
 	*tgt = strtol(optarg, NULL, 0);
 	if (errno) {
-		fprintf(stderr, ZIOMON_MGR_NAME ": Error converting %s to"
-			" long: %s\n", optarg, strerror(errno));
+		fprintf(stderr, "%s: Error converting %s to"
+			" long: %s\n", toolname, optarg, strerror(errno));
 		return -1;
 	}
 
@@ -365,7 +296,7 @@ static int parse_params(int argc, char *
 	};
 
 	if (argc <= 1) {
-		print_help(argv[0]);
+		print_help();
 		return 1;
 	}
 
@@ -377,23 +308,25 @@ static int parse_params(int argc, char *
 			break;
 		case 'Q':
 			if (!optarg) {
-				fprintf(stderr, ZIOMON_MGR_NAME ": Error:"
-					" Argument missing to option '-Q'\n");
+				fprintf(stderr, "%s: Error:"
+					" Argument missing to option '-Q'\n",
+					toolname);
 				return -1;
 			}
 			opts->msg_q_path = optarg;
 			break;
 		case 'q':
 			if (!optarg) {
-				fprintf(stderr, ZIOMON_MGR_NAME ": Error:"
-					" Argument missing to option '-q'\n");
+				fprintf(stderr, "%s: Error:"
+					" Argument missing to option '-q'\n",
+					toolname);
 				return -1;
 			}
 			opts->msg_q_id = atoi(optarg);
 			if (opts->msg_q_id < 0) {
-				fprintf(stderr, ZIOMON_MGR_NAME ": Error:"
+				fprintf(stderr, "%s: Error:"
 					" Parameter to option '-q' must be"
-					" greater than 0\n");
+					" greater than 0\n", toolname);
 				return -1;
 			}
 			break;
@@ -418,7 +351,7 @@ static int parse_params(int argc, char *
 				return -1;
 			break;
 		case 'h':
-			print_help(argv[0]);
+			print_help();
 			return 1;
 		case 'e':
 			print_bin_struct_sizes();
@@ -428,25 +361,35 @@ static int parse_params(int argc, char *
 			break;
 		case 'i':
 			if (!optarg) {
-				fprintf(stderr, ZIOMON_MGR_NAME ": Error:"
-					" Argument missing to option '-i'\n");
+				fprintf(stderr, "%s: Argument missing to"
+					" option '-i'\n", toolname);
 				return -1;
 			}
 			opts->interval_length = atoi(optarg);
 			if (opts->interval_length <= 0) {
-				fprintf(stderr, ZIOMON_MGR_NAME ": Interval"
-					" length must be >0\n");
+				fprintf(stderr, "%s: Interval"
+					" length must be >0\n", toolname);
 				return -1;
 			}
 			break;
 		case 'o':
 			if (!optarg) {
-				fprintf(stderr, ZIOMON_MGR_NAME "Error:"
-					" Argument missing to option '-o'\n");
+				fprintf(stderr, "%s: Argument missing to"
+					" option '-o'\n", toolname);
 				return -1;
 			}
+			if (opts->outfile_name) {
+				fprintf(stderr, "%s: Multiple instance of "
+					" option '-o'\n", toolname);
+				return -2;
+			}
 			opts->outfile_name = malloc(strlen(optarg)
 					+ strlen(DACC_FILE_EXT_LOG) + 1);
+			if (!opts->outfile_name) {
+				fprintf(stderr, "%s: Memory allocation "
+					"error\n", toolname);
+				return -3;
+			}
 			opts->outfile_name_agg = malloc(strlen(optarg)
 					+ strlen(DACC_FILE_EXT_AGG) + 1);
 			sprintf(opts->outfile_name, "%s" DACC_FILE_EXT_LOG,
@@ -456,72 +399,71 @@ static int parse_params(int argc, char *
 			break;
 		case 'l':
 			if (!optarg) {
-				fprintf(stderr, ZIOMON_MGR_NAME ": Error:"
-					" Argument missing to option '-o'\n");
+				fprintf(stderr, "%s: Argument missing to"
+					" option '-o'\n", toolname);
 				return -1;
 			}
 			opts->size_limit = strtol(optarg, NULL, 0);
 			if (errno) {
-				fprintf(stderr, ZIOMON_MGR_NAME ": Error"
-					" during conversion: %s\n",
-					strerror(errno));
+				fprintf(stderr, "%s: Error during conversion:"
+					" %s\n", toolname, strerror(errno));
 				return -1;
 			}
 			if (opts->size_limit < 1) {
-				fprintf(stderr, ZIOMON_MGR_NAME ": Error: Size"
-					" limit must be >=1.\n");
+				fprintf(stderr, "%s: Size limit must be >=1.\n",
+					toolname);
 				return -1;
 			}
 			opts->size_limit *= 1024*1024;
 			break;
 		case 'v':
-			print_version(argv[0]);
+			print_version();
 			return 1;
 		default:
-			fprintf(stderr, "Try '" ZIOMON_MGR_NAME " --help' for"
-				" more information.\n");
+			fprintf(stderr, "Try '%s --help' for"
+				" more information.\n", toolname);
 			return -1;
 		}
 	}
 
 	if (!opts->msg_q_path) {
-		fprintf(stderr, ZIOMON_MGR_NAME ": Error: No message queue"
-			" path specified\n");
+		fprintf(stderr, "%s: No message queue path specified\n",
+				toolname);
 		error++;
 	}
 	if (opts->msg_q_id < 0) {
-		fprintf(stderr, ZIOMON_MGR_NAME ": Error: No message queue"
-			" id specified\n");
+		fprintf(stderr, "%s: No message queue"
+			" id specified\n", toolname);
 		error++;
 	}
 	if (opts->msg_id_blkiomon == LONG_MIN) {
-		fprintf(stderr, ZIOMON_MGR_NAME ": Error: No id for blkiomon"
-			" messages specified\n");
+		fprintf(stderr, "%s: No id for blkiomon"
+			" messages specified\n", toolname);
 		error++;
 	}
 	if (opts->msg_id_utilization == LONG_MIN) {
-		fprintf(stderr, ZIOMON_MGR_NAME ": Error: No id for"
-			" utilizaton messages specified\n");
+		fprintf(stderr, "%s: No id for"
+			" utilizaton messages specified\n", toolname);
 		error++;
 	}
 	if (opts->msg_id_ioerr == LONG_MIN) {
-		fprintf(stderr, ZIOMON_MGR_NAME ": Error: No id for"
-			" ioerr messages specified\n");
+		fprintf(stderr, "%s: No id for"
+			" ioerr messages specified\n", toolname);
 		error++;
 	}
 	if (opts->msg_id_zfcpdd == LONG_MIN) {
-		fprintf(stderr, ZIOMON_MGR_NAME ": Error: No id for"
-			" ziomon_zfcpdd messages specified\n");
+		fprintf(stderr, "%s: No id for"
+			" ziomon_zfcpdd messages specified\n", toolname);
 		error++;
 	}
 	if (opts->interval_length < 0) {
-		fprintf(stderr, ZIOMON_MGR_NAME ": Error: No interval length"
-			" specified\n");
+		fprintf(stderr, "%s: No interval length"
+			" specified\n", toolname);
 		error++;
 	}
 	if (!opts->outfile_name) {
-		fprintf(stderr, ZIOMON_MGR_NAME ": Error: No filename for"
-			" output specified\n");
+		fprintf(stderr, "%s: No filename for"
+			" output specified\n", toolname);
 		error++;
 	}
 	if (error)
@@ -532,8 +474,8 @@ static int parse_params(int argc, char *
 
 	opts->outfile = fopen(opts->outfile_name, "w+");
 	if (!opts->outfile) {
-		fprintf(stderr, ZIOMON_MGR_NAME ": Could not open output"
-			" file: %s\n", strerror(errno));
+		fprintf(stderr, "%s: Could not open output"
+			" file: %s\n", toolname, strerror(errno));
 		return -1;
 	}
 
@@ -566,12 +508,20 @@ static void void_handler(int sig)
 }
 
 
+static void print_timestamp(struct tm *my_tm, const char *type, __u32 length,
+			    struct timeval *t)
+{
+	verbose_msg("%02d:%02d:%02d.%06ld: received %s"
+		    " message of %u bytes\n", my_tm->tm_hour,
+		    my_tm->tm_min, my_tm->tm_sec, t->tv_usec, type, length);
+}
+
+
 static int handle_msg(struct message *msg, struct options *opts)
 {
 	struct message **msgs;
 	int count;
 	int rc = 0;
-	int i;
 	struct timeval t;
 	struct tm *my_tm = NULL;
 
@@ -581,45 +531,32 @@ static int handle_msg(struct message *ms
 	}
 
 	if (msg->type == opts->msg_id_utilization)
-		verbose_msg("%02d:%02d:%02d.%06ld: received utilization"
-			    " message of %d bytes\n", my_tm->tm_hour,
-			    my_tm->tm_min, my_tm->tm_sec, t.tv_usec, msg->length);
+		print_timestamp(my_tm, "utilization", msg->length, &t);
 	else if (msg->type == opts->msg_id_ioerr)
-		verbose_msg("%02d:%02d:%02d.%06ld: received ioerr message of"
-			    " %d bytes\n", my_tm->tm_hour, my_tm->tm_min,
-			    my_tm->tm_sec, t.tv_usec, msg->length);
+		print_timestamp(my_tm, "ioerr", msg->length, &t);
 	else if (msg->type == opts->msg_id_blkiomon)
-		verbose_msg("%02d:%02d:%02d.%06ld: received blkiomon message of"
-			    " %d bytes\n", my_tm->tm_hour, my_tm->tm_min,
-			    my_tm->tm_sec, t.tv_usec, msg->length);
+		print_timestamp(my_tm, "blkiomon", msg->length, &t);
 	else if (msg->type == opts->msg_id_zfcpdd)
-		verbose_msg("%02d:%02d:%02d.%06ld: received ziomon_zfcpdd message of"
-			    " %d bytes\n", my_tm->tm_hour, my_tm->tm_min,
-			    my_tm->tm_sec, t.tv_usec, msg->length);
+		print_timestamp(my_tm, "zfcpdd", msg->length, &t);
 	else {
-		fprintf(stderr, ZIOMON_MGR_NAME ": Received message of "
+		fprintf(stderr, "%s: Received message of "
 				"unrecognized type %d, length %d bytes, "
-				"discarding\n", msg->type, msg->length);
+				"discarding\n", toolname, msg->type, msg->length);
 		return 1;
 	}
 
 	if (add_msg(opts->outfile, msg, &opts->f_hdr, &msgs, &count)) {
-		fprintf(stderr, ZIOMON_MGR_NAME ": Error while writing"
-			" message");
+		fprintf(stderr, "%s: Error while writing"
+			" message\n", toolname);
 		rc = -1;
 	} else
 		verbose_msg("message written\n");
 	if (count) {
 		if (add_to_aggregated(msgs, count, opts)) {
-			fprintf(stderr, ZIOMON_MGR_NAME ": Failed to aggregate"
-				" %d messages\n", count);
+			fprintf(stderr, "%s: Failed to aggregate"
+				" %d messages\n", toolname, count);
 			rc = -1;
 		}
-		for (i = 0; i < count; ++i) {
-			discard_msg(msgs[i]);
-			free(msgs[i]);
-		}
-		free(msgs);
 	}
 
 	return rc;
@@ -673,8 +610,8 @@ int main(int argc, char **argv)
 					    " increasing to %d\n", data_sz);
 				continue;
 			}
-			fprintf(stderr, ZIOMON_MGR_NAME ": Error receiving"
-				" message: %s\n", strerror(errno));
+			fprintf(stderr, "%s: Error receiving"
+				" message: %s\n", toolname, strerror(errno));
 			verbose_msg("msgrcv() returned error %d\n", tmperr);
 			break;
 		}
Index: s390-tools-sles11/ziomon/ziomon_msg_tools.c
===================================================================
--- s390-tools-sles11.orig/ziomon/ziomon_msg_tools.c
+++ s390-tools-sles11/ziomon/ziomon_msg_tools.c
@@ -9,6 +9,7 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <assert.h>
 
 #include "ziomon_msg_tools.h"
 #include "ziomon_util.h"
@@ -16,10 +17,11 @@
 #include "blkiomon.h"
 
 
-#define ZIOMON_MSG_TOOLS	"ziomon_msg_tools"
+extern const char *toolname;
 
 
-void conv_msg_data_to_BE(struct message *msg, struct file_header *hdr)
+void conv_msg_data_to_BE(struct message *msg,
+			 const struct file_header *hdr)
 {
 	if (msg->type == hdr->msgid_utilization)
 		conv_overall_result_to_BE(msg->data);
@@ -30,11 +32,11 @@ void conv_msg_data_to_BE(struct message 
 	else if (msg->type == hdr->msgid_zfcpdd)
 		conv_dstat_to_BE(msg->data);
 	else
-		fprintf(stderr, ZIOMON_MSG_TOOLS
-			": Internal Error: Unknown message encountered\n");
+		fprintf(stderr, "%s: Unknown message encountered\n", toolname);
 }
 
-void conv_msg_data_from_BE(struct message *msg, struct file_header *hdr)
+void conv_msg_data_from_BE(struct message *msg,
+			   const struct file_header *hdr)
 {
 	if (msg->type == hdr->msgid_utilization)
 		conv_overall_result_from_BE(msg->data);
@@ -45,36 +47,35 @@ void conv_msg_data_from_BE(struct messag
 	else if (msg->type == hdr->msgid_zfcpdd)
 		conv_dstat_from_BE(msg->data);
 	else
-		fprintf(stderr, ZIOMON_MSG_TOOLS
-			": Internal Error: Unknown message encountered\n");
+		fprintf(stderr,"%s: Unknown message encountered\n", toolname);
 }
 
-void conv_aggr_data_msg_data_to_BE(struct aggr_data *hdr, struct file_header *f_hdr)
+void conv_aggr_data_msg_data_to_BE(struct aggr_data *hdr)
 {
 	int i;
 
 	if (hdr->util_aggr)
-		conv_msg_data_to_BE(hdr->util_aggr, f_hdr);
+		conv_overall_result_to_BE(hdr->util_aggr->data);
 	if (hdr->ioerr_aggr)
-		conv_msg_data_to_BE(hdr->ioerr_aggr, f_hdr);
+		conv_ioerr_data_to_BE(hdr->ioerr_aggr->data);
 	for (i=0; i<hdr->num_blkiomon; ++i)
-		conv_msg_data_to_BE(hdr->blkio_aggr[i], f_hdr);
+		blkiomon_conv_to_BE(hdr->blkio_aggr[i]->data);
 	for (i=0; i<hdr->num_zfcpdd; ++i)
-		conv_msg_data_to_BE(hdr->zfcpdd_aggr[i], f_hdr);
+		conv_dstat_to_BE(hdr->zfcpdd_aggr[i]->data);
 }
 
-void conv_aggr_data_msg_data_from_BE(struct aggr_data *hdr, struct file_header *f_hdr)
+void conv_aggr_data_msg_data_from_BE(struct aggr_data *hdr)
 {
 	int i;
 
 	if (hdr->util_aggr)
-		conv_msg_data_from_BE(hdr->util_aggr, f_hdr);
+		conv_overall_result_from_BE(hdr->util_aggr->data);
 	if (hdr->ioerr_aggr)
-		conv_msg_data_from_BE(hdr->ioerr_aggr, f_hdr);
+		conv_ioerr_data_from_BE(hdr->ioerr_aggr->data);
 	for (i=0; i<hdr->num_blkiomon; ++i)
-		conv_msg_data_from_BE(hdr->blkio_aggr[i], f_hdr);
+		blkiomon_conv_to_BE(hdr->blkio_aggr[i]->data);
 	for (i=0; i<hdr->num_zfcpdd; ++i)
-		conv_msg_data_from_BE(hdr->zfcpdd_aggr[i], f_hdr);
+		conv_dstat_from_BE(hdr->zfcpdd_aggr[i]->data);
 }
 
 void copy_msg(struct message *src, struct message **tgt)
@@ -86,4 +87,106 @@ void copy_msg(struct message *src, struc
 	memcpy((*tgt)->data, src->data, src->length);
 }
 
+time_t get_timestamp_from_BE_msg(const struct message *msg)
+{
+	time_t timestamp = get_timestamp_from_msg(msg);
+	swap_64(timestamp);
+
+	return timestamp;
+}
+
+time_t get_timestamp_from_msg(const struct message *msg)
+{
+	return *(__u64*)(msg->data);
+}
+
+static void aggregate_blkiomon(struct aggr_data *agg_data, struct message *msg)
+{
+	int i;
+	struct message **tmp;
+	struct blkiomon_stat *stat = msg->data;
+
+	for (i = 0; i < agg_data->num_blkiomon; ++i)
+		if ((((struct blkiomon_stat*)(agg_data->blkio_aggr[i]->data))->device)
+			== stat->device)
+			break;
+	if (i >= agg_data->num_blkiomon) {
+		/* messages for this device not aggregated yet
+		   - add a new entry */
+		tmp = agg_data->blkio_aggr;
+		++(agg_data->num_blkiomon);
+		agg_data->blkio_aggr =
+			malloc(agg_data->num_blkiomon * sizeof(struct message*));
+		for (i = 0; i < agg_data->num_blkiomon - 1; ++i)
+			agg_data->blkio_aggr[i] = tmp[i];
+		copy_msg(msg, &agg_data->blkio_aggr[i]);
+		free(tmp);
+	}
+	else
+		blkiomon_stat_merge(agg_data->blkio_aggr[i]->data, msg->data);
+}
+
+
+static void aggregate_zfcpdd(struct aggr_data *agg_data, struct message *msg)
+{
+	int i;
+	struct message **tmp;
+	struct zfcpdd_dstat *stat = msg->data;
+
+	for (i = 0; i < agg_data->num_zfcpdd; ++i)
+		if ((((struct zfcpdd_dstat*)(agg_data->zfcpdd_aggr[i]->data))->device)
+			== stat->device)
+			break;
+	if (i >= agg_data->num_zfcpdd) {
+		/* messages for this device not aggregated yet
+		   - add a new entry */
+		tmp = agg_data->zfcpdd_aggr;
+		++(agg_data->num_zfcpdd);
+		agg_data->zfcpdd_aggr =
+			malloc(agg_data->num_zfcpdd * sizeof(struct message*));
+		for (i = 0; i < agg_data->num_zfcpdd - 1; ++i)
+			agg_data->zfcpdd_aggr[i] = tmp[i];
+		copy_msg(msg, &agg_data->zfcpdd_aggr[i]);
+		free(tmp);
+	}
+	else
+		aggregate_dstat(msg->data, agg_data->zfcpdd_aggr[i]->data);
+}
+
+
+int add_to_agg(struct aggr_data *agg_data, struct message *msg,
+	       const struct file_header *f_hdr)
+{
+	assert(agg_data->magic == DATA_MGR_MAGIC_AGGR);
+
+	conv_msg_data_from_BE(msg, f_hdr);
+
+	if (msg->type == f_hdr->msgid_utilization) {
+		if (agg_data->util_aggr)
+			aggregate_utilization_data(msg->data,
+				    agg_data->util_aggr->data);
+		else
+			copy_msg(msg, &agg_data->util_aggr);
+	} else if (msg->type == f_hdr->msgid_ioerr) {
+		if (agg_data->ioerr_aggr)
+			aggregate_ioerr_data(msg->data,
+				    agg_data->ioerr_aggr->data);
+		else
+			copy_msg(msg, &agg_data->ioerr_aggr);
+	} else if (msg->type == f_hdr->msgid_blkiomon)
+		aggregate_blkiomon(agg_data, msg);
+	else if (msg->type == f_hdr->msgid_zfcpdd)
+		aggregate_zfcpdd(agg_data, msg);
+	else {
+		fprintf(stderr, "%s: Unknow msg id: %d,"
+			" discarding\n", toolname, msg->type);
+		return -1;
+	}
+	agg_data->end_time = get_timestamp_from_msg(msg);
+	if (agg_data->begin_time == 0)
+		agg_data->begin_time = agg_data->end_time;
+
+	return 0;
+}
+
 
Index: s390-tools-sles11/ziomon/ziomon_msg_tools.h
===================================================================
--- s390-tools-sles11.orig/ziomon/ziomon_msg_tools.h
+++ s390-tools-sles11/ziomon/ziomon_msg_tools.h
@@ -13,17 +13,34 @@
 #include "ziomon_dacc.h"
 
 
-void conv_msg_data_to_BE(struct message *msg, struct file_header *hdr);
+void conv_msg_data_to_BE(struct message *msg, const struct file_header *hdr);
 
-void conv_msg_data_from_BE(struct message *msg, struct file_header *hdr);
+void conv_msg_data_from_BE(struct message *msg, const struct file_header *hdr);
 
-/* TODO: Get rid of f_hdr arg */
-void conv_aggr_data_msg_data_to_BE(struct aggr_data *hdr, struct file_header *f_hdr);
+void conv_aggr_data_msg_data_to_BE(struct aggr_data *hdr);
 
-/* TODO: Get rid of f_hdr arg */
-void conv_aggr_data_msg_data_from_BE(struct aggr_data *hdr, struct file_header *f_hdr);
+void conv_aggr_data_msg_data_from_BE(struct aggr_data *hdr);
 
 void copy_msg(struct message *src, struct message **tgt);
 
+/**
+ * Retrieve the timestamp from a message in BE format.
+ * The timestamp is returned in regular format */
+time_t get_timestamp_from_BE_msg(const struct message *msg);
+
+/**
+ * Retrieve the timestamp from a message in regular format.
+ * The timestamp is returned in regular format */
+time_t get_timestamp_from_msg(const struct message *msg);
+
+/**
+ * Add 'msg' to the 'hdr' struct.
+ * The aggregated header is assumed to be in regular format,
+ * while the message should be in BE format.
+ * The message will be left converted to BE for performance
+ * reasons */
+int add_to_agg(struct aggr_data *hdr, struct message *msg,
+	       const struct file_header *f_hdr);
+
 #endif
 
Index: s390-tools-sles11/ziomon/ziomon_tools.c
===================================================================
--- s390-tools-sles11.orig/ziomon/ziomon_tools.c
+++ s390-tools-sles11/ziomon/ziomon_tools.c
@@ -7,19 +7,16 @@
  * Author(s): Stefan Raspl <raspl@linux.vnet.ibm.com>
  */
 
-#include "ziomon_tools.h"
 #include <stdio.h>
 #include <stdint.h>
 #include <assert.h>
+#include <math.h>
 
+#include "ziomon_tools.h"
 
 extern int verbose;
-#define vverbose_msg(x...)	do {\
-					if (verbose >= 2)\
-						printf("\t===     " x);\
-				} while (0)
 
-void swap_stat_variance(struct stat_variance *var) {
+void swap_abbrev_stat(struct abbrev_stat *var) {
 	swap_64(var->min);
 	swap_64(var->max);
 	swap_64(var->sum);
@@ -28,29 +25,43 @@ void swap_stat_variance(struct stat_vari
 
 double calc_avg(__u64 sum, __u64 count)
 {
-	assert(count >= 0);
+	assert(count > 0);
 	return (sum / (double)count);
 }
 
 double calc_variance(__u64 sum, __u64 sos, __u64 count)
 {
-	assert(count >= 0);
+	assert(count > 0);
 	return ((sos - ((sum * sum) / (double)count)) / (double)count);
 }
 
-void print_stat_variance(struct stat_variance *stats, __u64 count)
+double calc_std_dev(__u64 sum, __u64 sos, __u64 count)
+{
+	return sqrt(calc_variance(sum, sos, count));
+}
+
+void transform_abbrev_stat(struct abbrev_stat *stat,
+				       __u64 old_count,
+				       double new_count)
+{
+	stat->min = 0;
+	stat->sum = (__u64)(calc_avg(stat->sum, old_count) * new_count);
+	stat->sos = (__u64)(calc_avg(stat->sos, old_count) * new_count);
+}
+
+void print_abbrev_stat(struct abbrev_stat *stats, __u64 count)
 {
-	printf("\t\tmin      : %lld\n", (long long)stats->min);
-	printf("\t\tmax      : %lld\n", (long long)stats->max);
-	vverbose_msg("\t\tsum      : %lld\n", (long long)stats->sum);
-	vverbose_msg("\t\tsos      : %lld\n", (long long)stats->sos);
-	printf("\t\tavg      : %.1f\n", calc_avg(stats->sum, count));
-	printf("\t\tvariance : %.1f\n", calc_variance(stats->sum, stats->sos,
+	printf("\t\tmin      : %Lu\n", (long long)stats->min);
+	printf("\t\tmax      : %Lu\n", (long long)stats->max);
+	vverbose_msg("\t\tsum      : %Lu\n", (long long)stats->sum);
+	vverbose_msg("\t\tsos      : %Lu\n", (long long)stats->sos);
+	printf("\t\tavg      : %.1lf\n", calc_avg(stats->sum, count));
+	printf("\t\tstd dev  : %.1lf\n", calc_std_dev(stats->sum, stats->sos,
 						      count));
 }
 
-void aggregate_stat_variance(struct stat_variance *src,
-				    struct stat_variance *tgt)
+void aggregate_abbrev_stat(const struct abbrev_stat *src,
+				    struct abbrev_stat *tgt)
 {
 	if (src->max > tgt->max)
 		tgt->max = src->max;
@@ -60,19 +71,18 @@ void aggregate_stat_variance(struct stat
 	tgt->sos += src->sos;
 }
 
-void update_stat_variance(struct stat_variance *stat, __u64 val)
+void update_abbrev_stat(struct abbrev_stat *stat, __u64 val)
 {
-	assert(val >= 0);
 	if (val < stat->min)
 		stat->min = val;
 	if (val > stat->max)
 		stat->max = val;
 	stat->sum += val;
-	stat->sos += val * val;
+	stat->sos += (val * val);
 }
 
-void copy_stat_variance(struct stat_variance *tgt,
-			       struct stat_variance *src)
+void copy_abbrev_stat(struct abbrev_stat *tgt,
+			const struct abbrev_stat *src)
 {
 	tgt->min = src->min;
 	tgt->max = src->max;
@@ -80,7 +90,7 @@ void copy_stat_variance(struct stat_vari
 	tgt->sos = src->sos;
 }
 
-void init_stat_variance(struct stat_variance *data)
+void init_abbrev_stat(struct abbrev_stat *data)
 {
 	data->min = UINT64_MAX;
 	data->max = 0;
Index: s390-tools-sles11/ziomon/ziomon_tools.h
===================================================================
--- s390-tools-sles11.orig/ziomon/ziomon_tools.h
+++ s390-tools-sles11/ziomon/ziomon_tools.h
@@ -30,38 +30,52 @@
 						printf("\t=== " x);\
 				} while (0)
 
+#ifdef NDEBUG
+#define vverbose_msg(x...)
+#else
 #define vverbose_msg(x...)	do {\
 					if (verbose >= 2)\
 						printf("\t===     " x);\
 				} while (0)
+#endif
 
-
-struct stat_variance {
+/**
+ * abbreviated stat (without a count).
+ * Used in various places where multiple instances of stats have
+ * to be collected, sharing a single count variable to save some space */
+struct abbrev_stat {
 	__u64 min;
 	__u64 max;
 	__u64 sum;
 	__u64 sos;	/* sum of squares */
 } __attribute__ ((packed));
 
-void swap_stat_variance(struct stat_variance *var);
+void swap_abbrev_stat(struct abbrev_stat *var);
 
-void print_stat_variance(struct stat_variance *stats, __u64 count);
+void print_abbrev_stat(struct abbrev_stat *stats, __u64 count);
 
-void aggregate_stat_variance(struct stat_variance *src,
-				    struct stat_variance *tgt);
+void aggregate_abbrev_stat(const struct abbrev_stat *src,
+				    struct abbrev_stat *tgt);
 
-void update_stat_variance(struct stat_variance *stat, __u64 val);
+void update_abbrev_stat(struct abbrev_stat *stat, __u64 val);
 
-void copy_stat_variance(struct stat_variance *tgt,
-		        struct stat_variance *src);
+void copy_abbrev_stat(struct abbrev_stat *tgt,
+		      const struct abbrev_stat *src);
 
-void init_stat_variance(struct stat_variance *data);
+/**
+ * Rebase abbrev_stat structure from 'old_count' to 'new_count' number of
+ * sample. Since we assume that the additional samples are all 0, we also
+ * fix the respective 'min' value while at it. */
+void transform_abbrev_stat(struct abbrev_stat *stat, __u64 old_count,
+				 double new_count);
 
+void init_abbrev_stat(struct abbrev_stat *data);
 
 double calc_avg(__u64 sum, __u64 count);
 
 double calc_variance(__u64 sum, __u64 sos, __u64 count);
 
+double calc_std_dev(__u64 sum, __u64 sos, __u64 count);
 
 #endif
 
Index: s390-tools-sles11/ziomon/ziomon_util.8
===================================================================
--- s390-tools-sles11.orig/ziomon/ziomon_util.8
+++ s390-tools-sles11/ziomon/ziomon_util.8
@@ -85,18 +85,6 @@ the id is 95 and the the messages should
 
 ziomon_util -s 4 -i 20 -d 300 -Q /tmp/ziomon.23943 -q 95 -m 17 -a 1 -l 0:0:1:2057 -L 18
 
-.SH AUTHORS
-ziomon_util and this man page were written by Stefan Raspl.
-
-
-.SH COPYRIGHT
-Copyright \(co 2008 IBM Corp.
-.br
-This is free software.  You may redistribute copies of it under the terms of
-the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.
-There is NO WARRANTY, to the extent permitted by law.
-
-
 .SH "SEE ALSO"
 .BR ziomon (8),
 .BR ziomon_mgr (8),
Index: s390-tools-sles11/ziomon/ziomon_util.c
===================================================================
--- s390-tools-sles11.orig/ziomon/ziomon_util.c
+++ s390-tools-sles11/ziomon/ziomon_util.c
@@ -24,18 +24,24 @@
 #include <sys/time.h>
 #include <getopt.h>
 #include <limits.h>
+#include <math.h>
 #include <assert.h>
 
 #include "ziomon_util.h"
 #include "zt_common.h"
 
 
-#define ZIOMON_UTIL_NAME	"ziomon_util"
+#ifdef WITH_MAIN
+const char *toolname = "ziomon_util";
+#else
+extern const char *toolname;
+#endif
+
 
 struct overall_result_wrp {
 	long			mtype;	/* must be long due to msg-passing
 					   interface */
-	struct overall_result   o_res;
+	struct utilization_data   o_res;
 };
 
 struct ioerr_wrp {
@@ -43,10 +49,10 @@ struct ioerr_wrp {
 	struct ioerr_data	data;
 };
 
-__u32 get_result_sz(struct overall_result *res)
+__u32 get_result_sz(struct utilization_data *res)
 {
-	return (sizeof(struct overall_result)
-		+ res->num_adapters * sizeof(struct adapter_result));
+	return (sizeof(struct utilization_data)
+		+ res->num_adapters * sizeof(struct adapter_utilization));
 }
 
 
@@ -57,154 +63,172 @@ __u32 get_ioerr_data_sz(struct ioerr_dat
 }
 
 
-void print_utilization_result(struct overall_result *res)
+void print_utilization_result(struct utilization_data *res)
 {
 	int i;
-	struct adapter_result	*a_res;
-	struct util_result	*u_res;
+	struct adapter_utilization	*a_res;
+	struct utilization_stats	*u_res;
+	time_t			 t;
 
-	printf("timestamp     : %s", ctime((time_t*)&res->timestamp));
+	t = res->timestamp;
+	printf("timestamp     : %s", ctime(&t));
 	printf("num adapters  : %d\n", res->num_adapters);
 	for (i = 0; i < res->num_adapters; ++i) {
-		a_res = &res->results[i];
-		u_res = &a_res->result;
+		a_res = &res->adapt_utils[i];
+		u_res = &a_res->stats;
 		if (i != 0)
 			printf("\n");
 		printf("utilization of host adapter %d\n", a_res->adapter_no);
 		printf("\tnum samples: %llu\n", (unsigned long long)u_res->count);
-		printf("\tqueue full incidents: %d\n", u_res->queue_full);
-		printf("\tadapter:\n");
-		print_stat_variance(&u_res->adapter, u_res->count);
-		printf("\tbus:\n");
-		print_stat_variance(&u_res->bus, u_res->count);
-		printf("\tprocessor:\n");
-		print_stat_variance(&u_res->cpu, u_res->count);
+		if (u_res->count > 0) {
+			printf("\tqueue full incidents: %d\n", u_res->queue_full);
+			printf("\taverage queue utilization: %.1lf slots\n",
+				u_res->queue_util_integral / (double)u_res->queue_util_interval);
+			printf("\tadapter:\n");
+			print_abbrev_stat(&u_res->adapter, u_res->count);
+			printf("\tbus:\n");
+			print_abbrev_stat(&u_res->bus, u_res->count);
+			printf("\tprocessor:\n");
+			print_abbrev_stat(&u_res->cpu, u_res->count);
+		}
 	}
 }
 
 
-static void swap_overall_result(struct overall_result *res)
+static void swap_overall_result(struct utilization_data *res)
 {
 	int i;
-	struct adapter_result *a_res;
-	struct util_result *u_res;
+	struct adapter_utilization *a_res;
+	struct utilization_stats *u_res;
 
 	swap_64(res->timestamp);
 
 	for (i = 0; i < res->num_adapters; ++i) {
-		a_res = &res->results[i];
+		a_res = &res->adapt_utils[i];
 		swap_32(a_res->adapter_no);
 		swap_16(a_res->valid);
-		u_res = &a_res->result;
+		u_res = &a_res->stats;
 		swap_64(u_res->count);
 		swap_32(u_res->queue_full);
 		swap_64(u_res->queue_util_integral);
 		swap_64(u_res->queue_util_interval);
-		swap_stat_variance(&u_res->adapter);
-		swap_stat_variance(&u_res->bus);
-		swap_stat_variance(&u_res->cpu);
+		swap_abbrev_stat(&u_res->adapter);
+		swap_abbrev_stat(&u_res->bus);
+		swap_abbrev_stat(&u_res->cpu);
 	}
 }
 
 
-void conv_overall_result_to_BE(struct overall_result *res)
+void conv_overall_result_to_BE(struct utilization_data *res)
 {
 	swap_overall_result(res);
 	swap_16(res->num_adapters);
 }
 
 
-void conv_overall_result_from_BE(struct overall_result *res)
+void conv_overall_result_from_BE(struct utilization_data *res)
 {
 	swap_16(res->num_adapters);
 	swap_overall_result(res);
 }
 
 
-static struct adapter_result *get_result_no(struct overall_result *res, int no)
+int compare_hctl_idents(const struct hctl_ident *src,
+			const struct hctl_ident *tgt)
 {
-	int i;
-
-	for (i = 0; i < res->num_adapters; ++i) {
-		if (res->results[i].adapter_no == no)
-			return &res->results[i];
+	if (src->host == tgt->host) {
+		if (src->channel == tgt->channel) {
+			if (src->target == tgt->target)
+				return (src->lun - tgt->lun);
+			else
+				return (src->target - tgt->target);
+		}
+		else
+			return (src->channel - tgt->channel);
 	}
-
-	return NULL;
+	else
+		return (src->host - tgt->host);
 }
 
-
-void aggregate_utilization_data(struct overall_result *src,
-				struct overall_result *tgt)
+void aggregate_utilization_data(const struct utilization_data *src,
+				struct utilization_data *tgt)
 {
-	struct adapter_result	*src_a_res, *tgt_a_res;
 	int i;
 
 	assert(tgt->timestamp = src->timestamp);
 	tgt->timestamp = src->timestamp;
 
 	if (src->num_adapters != tgt->num_adapters)
-		fprintf(stderr, ZIOMON_UTIL_NAME ": Warning: inconsistent"
-			" number of adapters: %d %d\n", src->num_adapters,
-			tgt->num_adapters);
-
-	for (i = 0; i < src->num_adapters; ++i) {
-		src_a_res = &src->results[i];
-		if (!src_a_res->valid)
-			continue;
-		tgt_a_res = get_result_no(tgt, src_a_res->adapter_no);
-		tgt_a_res->result.queue_full += src_a_res->result.queue_full;
-		tgt_a_res->result.queue_util_integral +=
-					src_a_res->result.queue_util_integral;
-		tgt_a_res->result.queue_util_interval +=
-					src_a_res->result.queue_util_interval;
-		tgt_a_res->result.count += src_a_res->result.count;
-		aggregate_stat_variance(&src_a_res->result.adapter,
-				&tgt_a_res->result.adapter);
-		aggregate_stat_variance(&src_a_res->result.bus,
-				&tgt_a_res->result.bus);
-		aggregate_stat_variance(&src_a_res->result.cpu,
-				&tgt_a_res->result.cpu);
-	}
+		fprintf(stderr, "%s: Warning: inconsistent"
+			" number of adapters: %d %d\n", toolname,
+			src->num_adapters, tgt->num_adapters);
+
+	for (i = 0; i < src->num_adapters; ++i)
+		aggregate_adapter_result(&src->adapt_utils[i], &tgt->adapt_utils[i]);
 
 	return;
 }
 
 
+void aggregate_adapter_result(const struct adapter_utilization *src,
+			      struct adapter_utilization *tgt)
+{
+	if (src->valid) {
+		/* in case we aggregate over different adapters,
+		   we clearly indicate that this is not valid anymore */
+		if (src->adapter_no != tgt->adapter_no)
+			tgt->adapter_no = -1;
+
+		tgt->stats.queue_full += src->stats.queue_full;
+		tgt->stats.queue_util_integral +=
+					src->stats.queue_util_integral;
+		tgt->stats.queue_util_interval +=
+					src->stats.queue_util_interval;
+		tgt->stats.count += src->stats.count;
+		aggregate_abbrev_stat(&src->stats.adapter,
+				&tgt->stats.adapter);
+		aggregate_abbrev_stat(&src->stats.bus,
+				&tgt->stats.bus);
+		aggregate_abbrev_stat(&src->stats.cpu,
+				&tgt->stats.cpu);
+	}
+}
+
+
 void print_ioerr_data(struct ioerr_data *data)
 {
 	int i;
 	struct ioerr_cnt *cnt;
+	time_t		  t;
 
-	fprintf(stdout, "timestamp : %s", ctime((time_t*)&data->timestamp));
+	t = data->timestamp;
+	fprintf(stdout, "timestamp : %s", ctime(&t));
 	fprintf(stdout, "num luns  : %lld\n", (long long)data->num_luns);
 	for (i = 0; i < data->num_luns; ++i) {
 		cnt = &data->ioerrors[i];
-		fprintf(stdout, "\t%d:%d:%d:%d: %lld I/O errors\n", cnt->host,
-			cnt->channel, cnt->target, cnt->lun,
+		fprintf(stdout, "\t%d:%d:%d:%d: %lld I/O errors\n",
+			cnt->identifier.host, cnt->identifier.channel,
+			cnt->identifier.target, cnt->identifier.lun,
 			(long long)cnt->num_ioerr);
 	}
 }
 
-
 static void swap_ioerr_data(struct ioerr_data *data)
 {
 	int i;
 	struct ioerr_cnt *cnt;
 
-
 	swap_64(data->timestamp);
 	for (i = 0; i < data->num_luns; ++i) {
 		cnt = &data->ioerrors[i];
-		swap_32(cnt->host);
-		swap_32(cnt->channel);
-		swap_32(cnt->target);
-		swap_32(cnt->lun);
+		swap_32(cnt->identifier.host);
+		swap_32(cnt->identifier.channel);
+		swap_32(cnt->identifier.target);
+		swap_32(cnt->identifier.lun);
 		swap_64(cnt->num_ioerr);
 	}
 }
 
-
 void conv_ioerr_data_to_BE(struct ioerr_data *data)
 {
 	swap_ioerr_data(data);
@@ -219,14 +243,25 @@ void conv_ioerr_data_from_BE(struct ioer
 }
 
 
-void aggregate_ioerr_data(struct ioerr_data *src, struct ioerr_data *tgt)
+void aggregate_ioerr_cnt(const struct ioerr_cnt *src, struct ioerr_cnt *tgt)
+{
+	/* in case we aggregate over different devices,
+	   we clearly indicate that this is not valid anymore */
+	if (compare_hctl_idents(&src->identifier, &tgt->identifier) != 0)
+		memset(&tgt->identifier, 0xff, sizeof(struct hctl_ident));
+
+	tgt->num_ioerr += src->num_ioerr;
+}
+
+
+void aggregate_ioerr_data(const struct ioerr_data *src, struct ioerr_data *tgt)
 {
 	int i;
 
 	assert(tgt->timestamp <= src->timestamp);
 	tgt->timestamp = src->timestamp;
 	for (i=0; i<src->num_luns; ++i)
-		tgt->ioerrors[i].num_ioerr += src->ioerrors[i].num_ioerr;
+		aggregate_ioerr_cnt(&src->ioerrors[i], &tgt->ioerrors[i]);
 }
 
 
@@ -249,9 +284,9 @@ struct util_data {
 	__u64			queue_util_prev; /* previous absolut value
 						    of queue_util_integral */
 	struct timeval		queue_util_timestamp;
-	struct stat_variance 	adapter;
-	struct stat_variance 	bus;
-	struct stat_variance 	cpu;
+	struct abbrev_stat 	adapter;
+	struct abbrev_stat 	bus;
+	struct abbrev_stat 	cpu;
 	long count;
 };
 
@@ -322,10 +357,10 @@ static void deinit_opts(struct options *
 		free(opts->host_path);
 		opts->host_path = NULL;
 	}
-	opts->num_hosts_a = 0;
-	opts->msg_q = -1;
 	for (i=0; i<opts->num_hosts_a; ++i)
 		free(opts->luns[i]);
+	opts->num_hosts_a = 0;
+	opts->msg_q = -1;
 	free(opts->luns);
 	free(opts->luns_prev);
 }
@@ -333,9 +368,9 @@ static void deinit_opts(struct options *
 
 static void init_util_data(struct util_data *data)
 {
-	init_stat_variance(&data->adapter);
-	init_stat_variance(&data->bus);
-	init_stat_variance(&data->cpu);
+	init_abbrev_stat(&data->adapter);
+	init_abbrev_stat(&data->bus);
+	init_abbrev_stat(&data->cpu);
 	data->count = 0;
 }
 
@@ -343,8 +378,9 @@ static void init_util_data(struct util_d
 static int init_ioerr_cnt(struct ioerr_cnt *cnt, char *str)
 {
 	cnt->num_ioerr = 0;
-	return (sscanf(str, "%d:%d:%d:%d", &cnt->host, &cnt->channel,
-		       &cnt->target, &cnt->lun) == 5);
+	return (sscanf(str, "%d:%d:%d:%d", &cnt->identifier.host,
+		       &cnt->identifier.channel, &cnt->identifier.target,
+		       &cnt->identifier.lun) == 5);
 }
 
 
@@ -361,22 +397,22 @@ static int init_host_opts(struct options
 
 	opts->host_nr = calloc(num_hosts, sizeof(long));
 	if (!opts->host_nr) {
-	     fprintf(stderr, ZIOMON_UTIL_NAME ": calloc opts->host_nr: %s\n",
-		     strerror(errno));
+	     fprintf(stderr, "%s: calloc opts->host_nr: %s\n",
+			toolname, strerror(errno));
 	     return -1;
 	}
 	opts->host_path = malloc(num_hosts * sizeof(char *));
 	if (!opts->host_path) {
-	     fprintf(stderr, ZIOMON_UTIL_NAME ": malloc opts->host_path: %s\n",
-		     strerror(errno));
+	     fprintf(stderr, "%s: malloc opts->host_path: %s\n",
+			toolname, strerror(errno));
 	     return -1;
 	}
 	for (i = 0; i < num_hosts; ++i) {
-		opts->host_path[i] = malloc(sizeof(char)*MAX_HOST_PATH_LEN);
+		opts->host_path[i] = malloc(sizeof(char) * MAX_HOST_PATH_LEN + 1);
 		if (!opts->host_path[i]) {
-		     fprintf(stderr, ZIOMON_UTIL_NAME ": malloc"
-			     " opts->host_path[]: %s\n", strerror(errno));
-		     return -1;
+			fprintf(stderr, "%s: malloc opts->host_path[]: %s\n",
+				toolname, strerror(errno));
+			return -1;
 		}
 	}
 
@@ -385,16 +421,16 @@ static int init_host_opts(struct options
 	free(opts->luns_prev);
 	opts->luns_prev = calloc(num_hosts, sizeof(__u32));
 	if (!opts->luns || !opts->luns_prev) {
-	     fprintf(stderr, ZIOMON_UTIL_NAME ": malloc opts->luns: %s\n",
-		     strerror(errno));
+	     fprintf(stderr, "%s: malloc opts->luns: %s\n",
+			toolname, strerror(errno));
 	     return -1;
 	}
 	for (i = 0; i < num_hosts; ++i) {
-		opts->luns[i] = malloc(sizeof(char)*MAX_HOST_PATH_LEN);
+		opts->luns[i] = malloc(sizeof(char) * MAX_LUN_PATH_LEN + 1);
 		if (!opts->host_path[i]) {
-		     fprintf(stderr, ZIOMON_UTIL_NAME ": malloc"
-			     " opts->host_path[]: %s\n", strerror(errno));
-		     return -1;
+			fprintf(stderr, "%s: malloc opts->luns[]: %s\n",
+				toolname, strerror(errno));
+			return -1;
 		}
 	}
 	opts->num_hosts_a = num_hosts;
@@ -421,8 +457,8 @@ static int read_attribute(char *path, ch
 		goto out;
 	}
 	rc = 0;
-out:
 	close(fd);
+out:
 	if (status)
 		*status = rc;
 
@@ -435,7 +471,7 @@ static int poll_utilization(struct adapt
 {
 	char line[LINE_LEN];
 	int cpu, bus, adapter;
-	int rc = 0;
+	int rc = 0, grc = 0;
 	struct adapter_data *adpt;
 	struct util_data    *u_data;
 	int i;
@@ -444,19 +480,21 @@ static int poll_utilization(struct adapt
 		adpt = &all_adapters->adapters[i];
 		u_data = &adpt->data;
 		/* read utilization attribute */
-		if (read_attribute(adpt->path, line, &adpt->status))
+		if (read_attribute(adpt->path, line, &adpt->status)) {
+			grc++;
 			continue;
+		}
 		rc = sscanf(line, "%d %d %d", &cpu, &bus, &adapter);
 		if (rc != 3) {
-			fprintf(stderr, ZIOMON_UTIL_NAME ": Warning:"
-				" Could not parse %s: %s\n", line,
-				strerror(errno));
+			fprintf(stderr, "%s: Warning:"
+				" Could not parse %s: %s\n", toolname,
+				line, strerror(errno));
 			adpt->status = 3;
 			continue;
 		}
-		update_stat_variance(&u_data->adapter, adapter);
-		update_stat_variance(&u_data->bus, bus);
-		update_stat_variance(&u_data->cpu, cpu);
+		update_abbrev_stat(&u_data->adapter, adapter);
+		update_abbrev_stat(&u_data->bus, bus);
+		update_abbrev_stat(&u_data->cpu, cpu);
 
 		verbose_msg("data read for adapter %d: adapter=%d, bus=%d,"
 			    " cpu=%d\n",
@@ -465,7 +503,23 @@ static int poll_utilization(struct adapt
 		u_data->count++;
 	}
 
-	return 0;
+	return grc;
+}
+
+
+static int calc_overflow(__u64 old_val, __u64 new_val)
+{
+	double ofl_val = 0;
+	int i = 0;
+
+	assert(old_val > new_val);
+
+	while (ofl_val < old_val) {
+		ofl_val = pow(2, i);
+		++i;
+	}
+
+	return (new_val + (int)(ofl_val - old_val));
 }
 
 
@@ -490,32 +544,37 @@ static int poll_queue_full(int init, str
 			continue;
 		rc = sscanf(line, "%d %Lu", &queue_full_tmp, &queue_util_tmp);
 		if (rc == 1) {
-			fprintf(stderr, ZIOMON_UTIL_NAME ": Only one value in"
+			fprintf(stderr, "%s: Only one value in"
 				" %s, your kernel level is probably too old.\n",
-				adpt->q_full_path);
+				toolname, adpt->q_full_path);
 			return -1;
 		}
 		if (rc != 2) {
-			fprintf(stderr, ZIOMON_UTIL_NAME ": Warning:"
-				" Could not parse %s: %s\n", line,
-				strerror(errno));
+			fprintf(stderr, "%s: Warning:"
+				" Could not parse %s: %s\n",
+				toolname, line,	strerror(errno));
 			adpt->status = 6;
 			continue;
 		}
 		gettimeofday(&cur_time, NULL);
 		if (!init) {
-			u_data->queue_full = queue_full_tmp
+			if (queue_full_tmp < u_data->queue_full_prev)
+				u_data->queue_full =
+					calc_overflow(u_data->queue_full_prev,
+						      queue_full_tmp);
+			else
+				u_data->queue_full = queue_full_tmp
 						- u_data->queue_full_prev;
-			u_data->queue_util_integral = queue_util_tmp
+			if (queue_util_tmp < u_data->queue_util_prev)
+				u_data->queue_util_integral =
+					calc_overflow(u_data->queue_util_prev,
+							queue_util_tmp);
+			else
+				u_data->queue_util_integral = queue_util_tmp
 						- u_data->queue_util_prev;
-			timersub(&cur_time, &u_data->queue_util_timestamp,
-						&tmp);
+			timersub(&cur_time, &u_data->queue_util_timestamp, &tmp);
 			u_data->queue_util_interval = tmp.tv_sec * 1000000
 							+ tmp.tv_usec;
-			verbose_msg("data read for adapter %d: queue_full=%d,"
-				    " queue_util=%Lu\n", adpt->host_nr,
-				    u_data->queue_full, (long long unsigned int)
-				    u_data->queue_util_integral);
 		}
 		u_data->queue_full_prev = queue_full_tmp;
 		u_data->queue_util_prev = queue_util_tmp;
@@ -530,7 +589,7 @@ static int poll_ioerr_cnt(int init, stru
 			  struct options *opts)
 {
 	char line[LINE_LEN];
-	int rc = 0;
+	int rc = 0, grc = 0;
 	int i, tmp;
 
 	if (!init)
@@ -538,26 +597,33 @@ static int poll_ioerr_cnt(int init, stru
 	for (i=0; i<opts->num_luns; ++i) {
 		/* read ioerr_cnt attribute */
 		if (read_attribute(opts->luns[i], line, NULL)) {
-			fprintf(stderr, ZIOMON_UTIL_NAME ": Warning:"
-				" Could read %s\n", opts->luns[i]);
+			fprintf(stderr, "%s: Warning: Could not read %s\n",
+				toolname, opts->luns[i]);
+			grc++;
 			continue;
 		}
 		rc = sscanf(line, "%i", &tmp);
 		if (rc != 1) {
-			fprintf(stderr, ZIOMON_UTIL_NAME ": Warning:"
-				" Could not parse ioerr line %s: %s\n", line,
-				strerror(errno));
+			fprintf(stderr, "%s: Warning:"
+				" Could not parse ioerr line %s: %s\n",
+				toolname, line,	strerror(errno));
+			grc++;
 			continue;
 		}
 		if (!init) {
-			data->ioerrors[i].num_ioerr = tmp - opts->luns_prev[i];
+			if (tmp < opts->luns_prev[i])
+				data->ioerrors[i].num_ioerr = calc_overflow(
+						opts->luns_prev[i], tmp);
+			else
+				data->ioerrors[i].num_ioerr = tmp
+							- opts->luns_prev[i];
 			verbose_msg("data read for i/o err %s: ioerr_cnt=%d\n",
 			    opts->luns[i], data->ioerrors[i].num_ioerr);
 		}
 		opts->luns_prev[i] = tmp;
 	}
 
-	return 0;
+	return grc;
 }
 
 
@@ -573,7 +639,8 @@ static void reinit_adapters(struct adapt
 static int init_adapters(struct adapters *all_adapters, struct options *opts)
 {
 	struct adapter_data *adapter;
-	int i;
+	struct stat buf;
+	int i, rc = 0;
 
 	if (opts)
 		all_adapters->num_adapters = opts->num_hosts;
@@ -585,10 +652,20 @@ static int init_adapters(struct adapters
 			adapter->path = malloc(sizeof(char)*MAX_HOST_PATH_LEN);
 			sprintf(adapter->path, "%s/utilization",
 				opts->host_path[i]);
+			if (stat(adapter->path, &buf)) {
+				fprintf(stderr, "%s: Path does not exist: %s - correct kernel version?\n",
+					toolname, adapter->path);
+				rc++;
+			}
 			adapter->q_full_path =
 				malloc(sizeof(char)*MAX_HOST_PATH_LEN);
 			sprintf(adapter->q_full_path, "%s/queue_full",
 				opts->host_path[i]);
+			if (stat(adapter->path, &buf)) {
+				fprintf(stderr, "%s: Path does not exist: %s - correct kernel version?\n",
+					toolname, adapter->q_full_path);
+				rc++;
+			}
 			adapter->status = 0;
 		}
 		init_util_data(&all_adapters->adapters[i].data);
@@ -596,7 +673,7 @@ static int init_adapters(struct adapters
 	if (poll_queue_full(1, all_adapters, opts))
 		return -1;
 
-	return 0;
+	return rc;
 }
 
 
@@ -624,29 +701,28 @@ static int init_ioerr_wrp(struct ioerr_w
 	*wrp = malloc(sizeof(struct ioerr_wrp)
 		     + opts->num_luns*sizeof(struct ioerr_cnt));
 	if (!*wrp) {
-		fprintf(stderr, ZIOMON_UTIL_NAME ": Error: "
-			"Memory allocation\n");
+		fprintf(stderr, "%s: Memory allocation failed\n", toolname);
 		return -1;
 	}
 	(*wrp)->mtype = opts->msg_id_ioerr;
 	(*wrp)->data.num_luns = opts->num_luns;
 	for (i=0; i<opts->num_luns; ++i) {
 		if (init_ioerr_cnt(&(*wrp)->data.ioerrors[i], opts->luns[i])) {
-			fprintf(stderr, ZIOMON_UTIL_NAME ": Error: "
-				"Could not parse %s\n", opts->luns[i]);
+			fprintf(stderr, "%s: Could not parse %s\n",
+				toolname, opts->luns[i]);
 			return -2;
 		}
 		strcpy(tmp, opts->luns[i]);
 		sprintf(opts->luns[i], "/sys/bus/scsi/devices/%s/ioerr_cnt", tmp);
 		if (stat(opts->luns[i], &buf)) {
-			fprintf(stderr, ZIOMON_UTIL_NAME ": Error: "
-				"Could not open %s: %s\n", opts->luns[i], strerror(errno));
+			fprintf(stderr, "%s: Could not open %s: %s\n",
+				toolname, opts->luns[i], strerror(errno));
 			return -3;
 		}
 	}
 	if (poll_ioerr_cnt(1, NULL, opts)) {
-		fprintf(stderr, ZIOMON_UTIL_NAME ": Error: "
-			"Could not read initial values of ioerr attributes.\n");
+		fprintf(stderr, "%s: Could not read initial values of ioerr"
+			" attributes.\n", toolname);
 		return -1;
 	}
 
@@ -657,8 +733,7 @@ static int init_ioerr_wrp(struct ioerr_w
 static void init_result_wrp(struct overall_result_wrp **res, int num_adapters,
 			    struct options *opts)
 {
-	*res = malloc(sizeof(struct overall_result_wrp)
-		      + (num_adapters * sizeof(struct adapter_result)));
+	*res = malloc(sizeof(struct overall_result_wrp) + (num_adapters * sizeof(struct adapter_utilization)));
 	(*res)->o_res.num_adapters = num_adapters;
 	(*res)->mtype = opts->msg_id;
 }
@@ -671,11 +746,11 @@ static void deinit_result_wrp(struct ove
 }
 
 
-static void print_version(const char *name)
+static void print_version(void)
 {
-        fprintf(stdout, "%s: ziomon utilization monitor, version %s.\n"
-               "Copyright IBM Corp. 2008.\n",
-               name, RELEASE_STRING);
+        fprintf(stdout, "%s: ziomon utilization monitor, version %s\n"
+               "Copyright IBM Corp. 2008\n",
+               toolname, RELEASE_STRING);
 }
 
 
@@ -684,20 +759,20 @@ static int get_argument_long(char *progn
 	char *p;
 
 	if (!optarg) {
-		fprintf(stderr, ZIOMON_UTIL_NAME ": Error: Argument missing"
-			" to option %c\n", opt);
+		fprintf(stderr, "%s: Argument missing"
+			" to option %c\n", toolname, opt);
 		return -1;
 	}
 	*param = strtol(optarg, &p, 0);
 	if (errno) {
-		fprintf(stderr, ZIOMON_UTIL_NAME ": Error: Unrecognized"
-			" parameter to option %c: %s\n", opt,
+		fprintf(stderr, "%s: Unrecognized"
+			" parameter to option %c: %s\n", toolname, opt,
 			strerror(errno));
 		return -2;
 	}
 	if (*p != '\0') {
-		fprintf(stderr, ZIOMON_UTIL_NAME ": Error: Unrecognized"
-			" parameter to option %c\n", opt);
+		fprintf(stderr, "%s: Unrecognized"
+			" parameter to option %c\n", toolname, opt);
 		return -3;
 	}
 
@@ -717,22 +792,24 @@ static int check_host_param(long *host_n
 	char *tmp;
 
 	if (*host_nr > 9999) {
-		fprintf(stderr, ZIOMON_UTIL_NAME ": Error: Host number out"
-			" of range\n");
+		fprintf(stderr, "%s: Host number out"
+			" of range\n", toolname);
 		return -1;
 	}
 
 	sprintf(host_path, "/sys/class/scsi_host/host%ld", *host_nr);
 	if (stat(host_path, &buf)) {
-		fprintf(stderr, ZIOMON_UTIL_NAME ": Error: Cannot access %s:"
-			" %s\n", host_path, strerror(errno));
+		fprintf(stderr, "%s: Cannot access %s:"
+			" %s\n", toolname, host_path, strerror(errno));
 		return -1;
 	}
 	tmp = malloc(strlen(host_path) + strlen("/queue_full") + 1);
 	sprintf(tmp, "%s/queue_full", host_path);
 	if (stat(host_path, &buf)) {
-		fprintf(stderr, ZIOMON_UTIL_NAME ": Error: Cannot access %s:"
-			" %s\n", tmp, strerror(errno));
+		fprintf(stderr, "%s: Cannot access %s."
+			" Your installed kernel is probably too old. Please"
+			" check that your kernel matches the level in the"
+			" documentation.\n", toolname, tmp);
 		free(tmp);
 		return -1;
 	}
@@ -754,8 +831,7 @@ static int find_all_hosts(struct options
 	verbose_msg("no host adapter(s) specified, scanning...\n");
 	num_dirents = scandir(h_path, &namelist, hostdir_filter, alphasort);
 	if (num_dirents <= 0) {
-		fprintf(stderr, ZIOMON_UTIL_NAME ": Error: No host adapter(s)"
-			" found\n");
+		fprintf(stderr, "%s: No host adapter(s) found\n", toolname);
 		rc = -3;
 		goto out;
 	}
@@ -767,8 +843,8 @@ static int find_all_hosts(struct options
 	for (i = 0; i < num_dirents; ++i) {
 		k = sscanf(namelist[i]->d_name, "host%ld", &opts->host_nr[i]);
 		if (k != 1) {
-			fprintf(stderr, ZIOMON_UTIL_NAME ": Error: Internal"
-				" error while scanning host adapter no\n");
+			fprintf(stderr, "%s: Internal error while scanning"
+				" host adapter no\n", toolname);
 			rc = -4;
 		} else
 			check_host_param(&opts->host_nr[i],
@@ -796,19 +872,19 @@ static int setup_msg_q(struct options *o
 	int wait=0;
 
 	if (opts->msg_id <= 0) {
-		fprintf(stderr, ZIOMON_UTIL_NAME ": Invalid or missing msg"
-			" id for utilization messages\n");
+		fprintf(stderr, "%s: Invalid or missing msg"
+			" id for utilization messages\n", toolname);
 		return -1;
 	}
 	if (opts->msg_id_ioerr <= 0) {
-		fprintf(stderr, ZIOMON_UTIL_NAME ": Invalid or missing msg"
-			" id for I/O error messages\n");
+		fprintf(stderr, "%s: Invalid or missing msg"
+			" id for I/O error messages\n", toolname);
 		return -1;
 	}
 	if (opts->msg_id == opts->msg_id_ioerr) {
-		fprintf(stderr, ZIOMON_UTIL_NAME ": Error: Message IDs for"
+		fprintf(stderr, "%s: Message IDs for"
 			" I/O error count and utilization messages must be"
-			" different\n");
+			" different\n", toolname);
 		return -1;
 	}
 
@@ -820,12 +896,14 @@ static int setup_msg_q(struct options *o
 		opts->msg_q = msgget(util_q, S_IRWXU);
 		if (opts->msg_q >= 0) {
 			if (wait)
-				fprintf(stderr, "Message queue is up!\n");
+				fprintf(stderr, "%s: Message queue is up!\n",
+					toolname);
 			break;
 		}
 		if (!wait) {
 			wait = 1;
-			fprintf(stderr, "Warning: Message queue not up yet, waiting...\n");
+			fprintf(stderr, "%s: Warning: Message queue not"
+				" up yet, waiting...\n", toolname);
 		}
 		usleep(200000);
 	}
@@ -842,13 +920,13 @@ static int setup_msg_q(struct options *o
 }
 
 
-static void generate_result(struct overall_result *ures,
+static void generate_result(struct utilization_data *ures,
 			    struct adapters *all_adapters)
 {
 	struct adapter_data	*a_data;
 	struct util_data	*u_data;
-	struct adapter_result	*a_res;
-	struct util_result	*u_res;
+	struct adapter_utilization	*a_res;
+	struct utilization_stats	*u_res;
 	int i;
 
 	ures->num_adapters = all_adapters->num_adapters;
@@ -856,21 +934,19 @@ static void generate_result(struct overa
 
 	for (i = 0; i < all_adapters->num_adapters; ++i) {
 		a_data = &all_adapters->adapters[i];
-		a_res = &ures->results[i];
+		a_res = &ures->adapt_utils[i];
 		a_res->adapter_no = a_data->host_nr;
 		u_data = &a_data->data;
 		a_res->valid = u_data->count;
 		if (a_res->valid) {
-			u_res = &a_res->result;
+			u_res = &a_res->stats;
 			u_res->queue_full = u_data->queue_full;
-			u_res->queue_util_integral
-						= u_data->queue_util_integral;
-			u_res->queue_util_interval
-						= u_data->queue_util_interval;
+			u_res->queue_util_integral = u_data->queue_util_integral;
+			u_res->queue_util_interval = u_data->queue_util_interval;
 			u_res->count = u_data->count;
-			copy_stat_variance(&u_res->adapter, &u_data->adapter);
-			copy_stat_variance(&u_res->bus, &u_data->bus);
-			copy_stat_variance(&u_res->cpu, &u_data->cpu);
+			copy_abbrev_stat(&u_res->adapter, &u_data->adapter);
+			copy_abbrev_stat(&u_res->bus, &u_data->bus);
+			copy_abbrev_stat(&u_res->cpu, &u_data->cpu);
 		}
 	}
 }
@@ -878,12 +954,12 @@ static void generate_result(struct overa
 
 
 static const char help_text[] =
-    "Usage: " ZIOMON_UTIL_NAME " [-h] [-v] [-V] [-i n] [-s n] "
+    "Usage: ziomon_util [-h] [-v] [-V] [-i n] [-s n] "
             "[-Q <msgq_path> -q <msgq_id>\n"
     "                   -m <msg_id>] -d n -a <n> -l <lun>\n"
     "\n"
     "Start the monitor for the host adapter utilization.\n"
-    "Example: " ZIOMON_UTIL_NAME " -d 60 -i 4 -a 0\n"
+    "Example: ziomon_util -d 60 -i 4 -a 0\n"
     "\n"
     "-h, --help            Print usage information and exit.\n"
     "-v, --version         Print version information and exit.\n"
@@ -904,7 +980,7 @@ static const char help_text[] =
     "-L, --msg-id-ioerr    Specify the message id for I/O error count"
 			" messages.\n";
 
-static void print_help(const char *name)
+static void print_help(void)
 {
         fprintf(stdout, "%s", help_text);
 }
@@ -931,7 +1007,7 @@ static int parse_params(int argc, char *
 	};
 
 	if (argc <= 1) {
-		print_help(argv[0]);
+		print_help();
 		return 1;
 	}
 
@@ -954,8 +1030,8 @@ static int parse_params(int argc, char *
 			break;
 		case 'l':
 			if (!optarg) {
-				fprintf(stderr, ZIOMON_UTIL_NAME ": Error:"
-					 "Argument missing to option '-l'\n");
+				fprintf(stderr, "%s: Argument missing to"
+					" option '-l'\n", toolname);
 				return -1;
 			}
 			strcpy(opts->luns[opts->num_luns], optarg);
@@ -988,65 +1064,62 @@ static int parse_params(int argc, char *
 			break;
 		case 'Q':
 			if (!optarg) {
-				fprintf(stderr, ZIOMON_UTIL_NAME ": Error:"
-					" Argument missing to option '-Q'\n");
+				fprintf(stderr, "%s: Argument missing to"
+					" option '-Q'\n", toolname);
 				return -1;
 			}
 			opts->msg_q_path = optarg;
 			break;
 		case 'q':
 			if (!optarg) {
-				fprintf(stderr, ZIOMON_UTIL_NAME ": Error:"
-					 "Argument missing to option '-Q'\n");
+				fprintf(stderr, "%s: Argument missing to"
+					" option '-Q'\n", toolname);
 				return -1;
 			}
 			opts->msg_q_id = atoi(optarg);
 			if (opts->msg_q_id < 0) {
-				fprintf(stderr, ZIOMON_UTIL_NAME ": Error:"
-					" Parameter to option '-q' must be"
-					" greater than 0\n");
+				fprintf(stderr, "%s: Parameter to option '-q'"
+					" must be greater than 0\n", toolname);
 				return -1;
 			}
 			break;
 		case 'v':
-			print_version(argv[0]);
+			print_version();
 			return 1;
 		case 'h':
-			print_help(argv[0]);
+			print_help();
 			return 1;
 		default:
-			fprintf(stderr, ZIOMON_UTIL_NAME ": Try '"
-				ZIOMON_UTIL_NAME " --help' for more"
-				" information.\n");
+			fprintf(stderr, "%s: Try '%s --help' for more"
+				" information.\n", toolname, toolname);
 			return -1;
 		}
 	}
 
 	if (opts->duration < 0) {
-		fprintf(stderr, ZIOMON_UTIL_NAME ": Error: No duration"
-			" specified\n");
+		fprintf(stderr, "%s: No duration specified\n", toolname);
 		return -1;
 	}
 	if (opts->i_duration < 0)
 		opts->i_duration = opts->duration;
 	if (opts->i_duration < opts->s_duration) {
-		fprintf(stderr, ZIOMON_UTIL_NAME ": Error: Sample duration"
-			" must be at least the interval duration\n");
+		fprintf(stderr, "%s: Sample duration"
+			" must be at least the interval duration\n", toolname);
 		return -1;
 	}
 	if (opts->duration < opts->i_duration) {
-		fprintf(stderr, ZIOMON_UTIL_NAME ": Error: Overall duration"
-			" must be at least the interval duration\n");
+		fprintf(stderr, "%s: Overall duration"
+			" must be at least the interval duration\n", toolname);
 		return -1;
 	}
-	if (opts->i_duration % opts->s_duration) {
-		fprintf(stderr, ZIOMON_UTIL_NAME ": Error: Sample duration"
-			" must be a multiple of sample duration\n");
+	if (opts->s_duration && opts->i_duration % opts->s_duration) {
+		fprintf(stderr, "%s: Sample duration"
+			" must be a multiple of sample duration\n", toolname);
 		return -1;
 	}
-	if (opts->duration % opts->i_duration) {
-		fprintf(stderr, ZIOMON_UTIL_NAME ": Error: overall duration"
-			" must be a multiple of section duration\n");
+	if (opts->i_duration && opts->duration % opts->i_duration) {
+		fprintf(stderr, "%s: overall duration"
+			" must be a multiple of section duration\n", toolname);
 		return -1;
 	}
 	if (opts->num_hosts > 0) {
@@ -1061,16 +1134,15 @@ static int parse_params(int argc, char *
 		if (find_all_hosts(opts))
 			return -1;
 	}
-	if ((opts->msg_q_path && (opts->msg_q_id < 0
-				 || opts->msg_id == LONG_MIN))
-	    || (opts->msg_q_id >= 0 && (!opts->msg_q_path
-				 || opts->msg_id == LONG_MIN))
-	    || (opts->msg_id != LONG_MIN && (!opts->msg_q_path
-				 || opts->msg_q_id < 0))) {
-		fprintf(stderr, ZIOMON_UTIL_NAME ": Error: Make sure to"
-			" specify all required arguments for message"
-			" queue.\n");
-		return -1;
+	if (opts->msg_q_path || opts->msg_q_id >= 0
+		|| opts->msg_id != LONG_MIN) {
+		if (!opts->msg_q_path || opts->msg_q_id < 0
+			|| opts->msg_id == LONG_MIN) {
+			fprintf(stderr, "%s: Make sure to"
+				" specify all required arguments for message"
+				" queue.\n", toolname);
+			return -1;
+		}
 	}
 
 	verbose_msg("num adapters        : %d\n", opts->num_hosts);
@@ -1092,10 +1164,10 @@ static void send_message(int msg_q, void
 		   though we should... */
 		if (errno == EIDRM) {
 			keep_running = 0;
-			verbose_msg("msgqueue removed, shutting down...");
+			verbose_msg("msgqueue removed, shutting down...\n");
 		} else {
-			fprintf(stderr, ZIOMON_UTIL_NAME ": Failed to send"
-				" message: %s\n", strerror(errno));
+			fprintf(stderr, "%s: Failed to send"
+				" message: %s\n", toolname, strerror(errno));
 			verbose_msg("msgsnd() returned error code %d\n",
 				    errno);
 		}
@@ -1105,7 +1177,7 @@ static void send_message(int msg_q, void
 
 static int has_ioerrs(struct ioerr_data *ioerr)
 {
-	int i = 0;
+	int i;
 
 	for (i = 0; i < ioerr->num_luns; ++i) {
 		if (ioerr->ioerrors[i].num_ioerr != 0)
@@ -1116,33 +1188,33 @@ static int has_ioerrs(struct ioerr_data 
 }
 
 
-static int has_non_null_stats(struct stat_variance *var)
+static int has_non_null_stats(struct abbrev_stat *var)
 {
 	return !(var->max == 0 && var->min == 0);
 }
 
 
-static int has_adapter_traffic(struct overall_result *res)
+static int has_adapter_traffic(struct utilization_data *res)
 {
-	int i=0;
-	struct adapter_result *a_res;
+	int i;
+	struct adapter_utilization *a_res;
 
 	for (i=0; i<res->num_adapters; ++i) {
-		a_res = &res->results[i];
+		a_res = &res->adapt_utils[i];
 		if (!a_res->valid)
 			return 1;
-		if (a_res->result.queue_full != 0)
+		if (a_res->stats.queue_full != 0)
 			return 1;
-		/* this makes sure that we don't send messages when essentially
-		   no data is processed - reading the 'utilization' attribute
-		   causes always traffic, hence we would always send a message
-		   without this threshold! */
-		if (a_res->result.queue_util_integral
-			/ (double)a_res->result.queue_util_interval >= 0.05)
+		/* makes sure that we don't send messages when essentially no
+		   data is processed. Reading the 'utilization' sysfs attribute
+		   causes traffic, hence we would always send a message without
+		   this threshold! */
+		if (a_res->stats.queue_util_integral
+			/ (double)a_res->stats.queue_util_interval >= 0.05)
 			return 1;
-		if (has_non_null_stats(&a_res->result.adapter)
-		    || has_non_null_stats(&a_res->result.bus)
-		    || has_non_null_stats(&a_res->result.cpu))
+		if (has_non_null_stats(&a_res->stats.adapter)
+		    || has_non_null_stats(&a_res->stats.bus)
+		    || has_non_null_stats(&a_res->stats.cpu))
 			return 1;
 	}
 
@@ -1163,22 +1235,19 @@ static void print_to_msg_q(struct overal
 			print_utilization_result(&res_wrp->o_res);
 		verbose_msg("write utilization result to msg q %d (msg-type: %ld, msg-size: %d)\n",
 				opts->msg_q, res_wrp->mtype, (unsigned int)msg_size);
-	
+
 		conv_overall_result_to_BE(&res_wrp->o_res);
-	
+
 		send_message(opts->msg_q, res_wrp, msg_size);
 	}
 
-
 	if (has_ioerrs(&ioerr->data) || force) {
 		msg_size = get_ioerr_data_sz(&ioerr->data);
 		if (verbose)
 			print_ioerr_data(&ioerr->data);
 		verbose_msg("write ioerr result to msg q %d (msg-type: %ld, msg-size: %d)\n",
 				opts->msg_q, ioerr->mtype, (unsigned int)msg_size);
-	
 		conv_ioerr_data_to_BE(&ioerr->data);
-	
 		send_message(opts->msg_q, ioerr, msg_size);
 	}
 }
@@ -1278,10 +1347,7 @@ int main(int argc, char **argv)
 		do {
 			sample_end.tv_sec += opts.s_duration;
 			sleep_until(&sample_end);
-			if (poll_utilization(all_adapters, &opts)) {
-				rc = -2;
-				goto out;
-			}
+			poll_utilization(all_adapters, &opts);
 			if (timercmp(&sample_end, &interval_end, >=)) {
 				/* final sample in interval */
 				if (poll_queue_full(0, all_adapters, &opts)) {
Index: s390-tools-sles11/ziomon/ziomon_util.h
===================================================================
--- s390-tools-sles11.orig/ziomon/ziomon_util.h
+++ s390-tools-sles11/ziomon/ziomon_util.h
@@ -16,40 +16,51 @@
 #include "ziomon_tools.h"
 
 
-struct util_result {
+struct utilization_stats {
 	__u64			count;
-	struct stat_variance 	adapter;
-	struct stat_variance 	bus;
-	struct stat_variance 	cpu;
+	struct abbrev_stat 	adapter; /* adapter utilization in percent */
+	struct abbrev_stat 	bus;     /* bus utilization in percent */
+	struct abbrev_stat 	cpu;     /* cpu utilization in percent */
 	__u64			queue_util_integral; /* integral of the queue
 					utilization over microseconds */
 	__u64			queue_util_interval; /* interval length
 							in microseconds */
 	__u32			queue_full;	/* number of queue_full
-	      instances within the given timeframe */
+				   instances within the given timeframe */
 } __attribute__ ((packed));
 
-struct adapter_result {
-	struct util_result	result;
-	__u32			adapter_no;	/* device identifier */
-	__u16			valid;	/* ==0 in case the result is invalid */
+struct adapter_utilization {
+	struct utilization_stats	stats;
+	__u32				adapter_no;	/* device identifier */
+	__u16				valid;	/* ==0 in case the result is invalid */
 } __attribute__ ((packed));
 
-/* Note that this message will always be transmitted in first and final
+/** Note that this message will always be transmitted in first and final
  * interval. Inbetween, only messages with actual traffic will be transmitted.
  */
-struct overall_result {
-	__u64			timestamp;
-	__u16			num_adapters;
-	struct adapter_result	results[0];
+struct utilization_data {
+	__u64				timestamp;
+	__u16				num_adapters;
+	struct adapter_utilization	adapt_utils[0];
 } __attribute__ ((packed));
 
-struct ioerr_cnt {
+struct hctl_ident {
 	__u32			host;	/* device identifier (1) */
 	__u32			channel;/* device identifier (2) */
 	__u32			target;	/* device identifier (3) */
 	__u32			lun;	/* device identifier (4) */
-	__u32			num_ioerr;	
+} __attribute__ ((packed));
+
+/**
+ * Returns <0 if src is 'smaller' than tgt, >0 of 'larger' and 0 if identical.
+ */
+int compare_hctl_idents(const struct hctl_ident *src,
+			const struct hctl_ident *tgt);
+
+struct ioerr_cnt {
+	struct hctl_ident	identifier;
+	__u32			num_ioerr; /* number of io errors
+						within timeframe */
 } __attribute__ ((packed));
 
 /* Note that this message will always be transmitted in first and final
@@ -63,14 +74,21 @@ struct ioerr_data {
 } __attribute__ ((packed));
 
 
-void print_utilization_result(struct overall_result *res);
+void print_utilization_result(struct utilization_data *res);
+
+void conv_overall_result_to_BE(struct utilization_data *res);
 
-void conv_overall_result_to_BE(struct overall_result *res);
+void conv_overall_result_from_BE(struct utilization_data *res);
 
-void conv_overall_result_from_BE(struct overall_result *res);
+/**
+ * Aggregate a complete utilization result with all included structures */
+void aggregate_utilization_data(const struct utilization_data *src,
+				struct utilization_data *tgt);
 
-void aggregate_utilization_data(struct overall_result *src,
-				struct overall_result *tgt);
+/**
+ * Aggregate a result for a single adapter only */
+void aggregate_adapter_result(const struct adapter_utilization *src,
+			      struct adapter_utilization *tgt);
 
 void print_ioerr_data(struct ioerr_data *data);
 
@@ -80,9 +98,14 @@ void conv_ioerr_data_from_BE(struct ioer
 
 /**
  * src must be later data than target! We assume that both structs have
- * data for the same LUNs in the same sequence. 
+ * data for the same LUNs in the same sequence.
  */
-void aggregate_ioerr_data(struct ioerr_data *src, struct ioerr_data *tgt);
+void aggregate_ioerr_data(const struct ioerr_data *src, struct ioerr_data *tgt);
+
+/**
+ * Aggregate data for a single device only */
+void aggregate_ioerr_cnt(const struct ioerr_cnt *src, struct ioerr_cnt *tgt);
+
 
 #endif
 
Index: s390-tools-sles11/ziomon/ziomon_zfcpdd.8
===================================================================
--- s390-tools-sles11.orig/ziomon/ziomon_zfcpdd.8
+++ s390-tools-sles11/ziomon/ziomon_zfcpdd.8
@@ -56,26 +56,8 @@ Specify the message queue id.
 \fB-m\fR \fImsg_id\fR or \fB--msg-id\fR \fImsg_id\fR
 Specify the message id to use.
 
-
-.SH EXAMPLES
-
-
-.SH AUTHORS
-ziomon_zfcpdd was written by Sven Schuetz.
-This man page was written by Martin Peschke.
-
-
 .SH "REPORTING BUGS"
 Report bugs to <linux\-s390>
 
-
-.SH COPYRIGHT
-Copyright \(co 2008 IBM Corp.
-.br
-This is free software.  You may redistribute copies of it under the terms of
-the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.
-There is NO WARRANTY, to the extent permitted by law.
-
-
 .SH "SEE ALSO"
 blktrace (8)
Index: s390-tools-sles11/ziomon/ziomon_zfcpdd.c
===================================================================
--- s390-tools-sles11.orig/ziomon/ziomon_zfcpdd.c
+++ s390-tools-sles11/ziomon/ziomon_zfcpdd.c
@@ -26,21 +26,25 @@
 #include "blktrace.h"
 #include "ziomon_zfcpdd.h"
 #include "zt_common.h"
+#include "blkiomon.h"
 
 
 #ifdef WITH_MAIN
+const char *toolname = "ziomon_zfcpdd";
 int verbose=0;
 #endif
 
-static void swap_dstat(struct zfcpdd_dstat *stat) {
+
+static void swap_dstat(struct zfcpdd_dstat *stat)
+{
 	int i;
 
 	swap_64(stat->time);
 	swap_64(stat->count);
 	swap_32(stat->device);
-	swap_stat_variance(&stat->chan_lat);
-	swap_stat_variance(&stat->fabr_lat);
-	swap_stat_variance(&stat->inb);
+	swap_abbrev_stat(&stat->chan_lat);
+	swap_abbrev_stat(&stat->fabr_lat);
+	swap_abbrev_stat(&stat->inb);
 	swap_16(stat->outb_max);
 	for (i=0; i<BLKIOMON_CHAN_LAT_BUCKETS; ++i)
 		swap_32(stat->chan_lat_hist[i]);
@@ -48,11 +52,13 @@ static void swap_dstat(struct zfcpdd_dst
 		swap_32(stat->fabr_lat_hist[i]);
 }
 
-void conv_dstat_to_BE(struct zfcpdd_dstat *stat) {
+void conv_dstat_to_BE(struct zfcpdd_dstat *stat)
+{
 	swap_dstat(stat);
 }
 
-void conv_dstat_from_BE(struct zfcpdd_dstat *stat) {
+void conv_dstat_from_BE(struct zfcpdd_dstat *stat)
+{
 	swap_dstat(stat);
 }
 
@@ -61,6 +67,8 @@ void aggregate_dstat(struct zfcpdd_dstat
 {
 	int i;
 
+	if (src->device != tgt->device)
+		tgt->device = 0xffffffff;
 	if (src->time > tgt->time)
 		tgt->time = src->time;
 	tgt->count += src->count;
@@ -68,9 +76,9 @@ void aggregate_dstat(struct zfcpdd_dstat
 		tgt->chan_lat_hist[i] += src->chan_lat_hist[i];
 	for (i=0; i<BLKIOMON_FABR_LAT_BUCKETS; ++i)
 		tgt->fabr_lat_hist[i] += src->fabr_lat_hist[i];
-	aggregate_stat_variance(&src->chan_lat, &tgt->chan_lat);
-	aggregate_stat_variance(&src->fabr_lat, &tgt->fabr_lat);
-	aggregate_stat_variance(&src->inb, &tgt->inb);
+	aggregate_abbrev_stat(&src->chan_lat, &tgt->chan_lat);
+	aggregate_abbrev_stat(&src->fabr_lat, &tgt->fabr_lat);
+	aggregate_abbrev_stat(&src->inb, &tgt->inb);
 	if (src->outb_max > tgt->outb_max)
 		tgt->outb_max = src->outb_max;
 }
@@ -78,25 +86,28 @@ void aggregate_dstat(struct zfcpdd_dstat
 void zfcpdd_print_stats(struct zfcpdd_dstat *stat)
 {
 	int i;
+	time_t t = stat->time;
 
-	printf("timestamp     : %s", ctime((time_t*)&stat->time));
-	printf("device        : %u\n", stat->device);
-	printf("count         : %lu\n", stat->count);
-	printf("\tchannel latency:\n");
-	print_stat_variance(&stat->chan_lat, stat->count);
+	printf("timestamp     : %s", ctime(&t));
+	printf("device        : %d:%d\n", MAJOR(stat->device),
+					MINOR(stat->device));
+	printf("count         : %lu\n", (long unsigned int)stat->count);
+	printf("\tchannel latency (in nsecs):\n");
+	print_abbrev_stat(&stat->chan_lat, stat->count);
 	printf("\t\tbuckets:");
 	for (i=0; i<BLKIOMON_CHAN_LAT_BUCKETS; ++i)
 		printf(" %u", stat->chan_lat_hist[i]);
-	printf("\n\tfabric latency:\n");
-	print_stat_variance(&stat->fabr_lat, stat->count);
+	printf("\n\tfabric latency (in usecs):\n");
+	print_abbrev_stat(&stat->fabr_lat, stat->count);
 	printf("\t\tbuckets:");
 	for (i=0; i<BLKIOMON_FABR_LAT_BUCKETS; ++i)
 		printf(" %u", stat->fabr_lat_hist[i]);
-	printf("\n\tinbound latency:\n");
-	print_stat_variance(&stat->inb, stat->count);
-	printf("\toutbound q max   : %hu\n", stat->outb_max);
+	printf("\n\tinbound queue fill size:\n");
+	print_abbrev_stat(&stat->inb, stat->count);
+        printf("\toutbound q max   : %hu\n", stat->outb_max);
 }
 
+
 #ifdef WITH_MAIN
 struct output {
 	char *fn;
@@ -105,6 +116,12 @@ struct output {
 	int pipe;
 };
 
+struct hist_log2 {
+	int first;
+	int delta;
+	int num;
+};
+
 static struct hist_log2 clat = {
 	.first = 0,
 	.delta = 1000,
@@ -170,9 +187,9 @@ static struct dstat *zfcpdd_dstat_alloc(
 	else
 		dstat = malloc(sizeof(*dstat));
 	memset(dstat, 0, sizeof(*dstat));
-	init_stat_variance(&dstat->msg.stat.chan_lat);
-	init_stat_variance(&dstat->msg.stat.fabr_lat);
-	init_stat_variance(&dstat->msg.stat.inb);
+	init_abbrev_stat(&dstat->msg.stat.chan_lat);
+	init_abbrev_stat(&dstat->msg.stat.fabr_lat);
+	init_abbrev_stat(&dstat->msg.stat.inb);
 
 	return dstat;
 }
@@ -212,7 +229,7 @@ static int hist_index(__u64 val, struct 
 	return i;
 }
 
-static void zfcpdd_account_hist_log2(__u32 *bucket, __u32 val,
+static void zfcpdd_account_hist_log2(__u32 *bucket, __u64 val,
 					struct hist_log2 *h)
 {
 	int index = hist_index(val, h);
@@ -222,6 +239,7 @@ static void zfcpdd_account_hist_log2(__u
 static void zfcpdd_account_outb(struct zfcpdd_dstat *stat,
 					struct zfcp_blk_drv_data *dd)
 {
+	/* incoming data reports number of _free_ slots! */
 	dd->outb_usage = 128 - dd->outb_usage;
 	if (dd->outb_usage > stat->outb_max)
 		stat->outb_max = dd->outb_usage;
@@ -232,14 +250,14 @@ static int zfcpdd_account(struct blk_io_
 {
 	struct dstat *dstat;
 	struct zfcpdd_dstat *stat;
-	
+
 	pthread_mutex_lock(&dstat_mutex);
-	
+
 	dstat = zfcpdd_dstat_find(&dstat_hash[dstat_curr], bit);
 	if (!dstat) {
 		dstat = zfcpdd_dstat_alloc();
 		if (!dstat) {
-			perror("ziomon_zfcpdd: could not alloc statistic");
+			fprintf(stderr, "%s: could not alloc statistic: %s\n", toolname, strerror(errno));
 			pthread_mutex_unlock(&dstat_mutex);
 			return 1;
 		}
@@ -251,9 +269,9 @@ static int zfcpdd_account(struct blk_io_
 		dstat->msg.stat.device, dstat_curr, &dstat_hash[dstat_curr], dstat);
 
 	stat = &dstat->msg.stat;
-	update_stat_variance(&stat->chan_lat, dd->chan_lat);
-	update_stat_variance(&stat->fabr_lat, dd->fabr_lat / 1000);
-	update_stat_variance(&stat->inb, dd->inb_usage);
+	update_abbrev_stat(&stat->chan_lat, dd->chan_lat);
+	update_abbrev_stat(&stat->fabr_lat, dd->fabr_lat / 1000);
+	update_abbrev_stat(&stat->inb, dd->inb_usage);
 	zfcpdd_account_outb(stat, dd);
 	zfcpdd_account_hist_log2(stat->chan_lat_hist, dd->chan_lat,
 				    &clat);
@@ -267,11 +285,11 @@ static int zfcpdd_account(struct blk_io_
 
 static void dump_bit(struct blk_io_trace *bit, const char *descr)
 {
-	fprintf(stderr, "--- %s ---\n", descr);
+	fprintf(stderr, "--- %s: %s ---\n", toolname, descr);
 	fprintf(stderr, "magic    %16d\n", bit->magic);
 	fprintf(stderr, "sequence %16d\n", bit->sequence);
-	fprintf(stderr, "time     %16ld\n", bit->time);
-	fprintf(stderr, "sector   %16ld\n", bit->sector);
+	fprintf(stderr, "time     %16lu\n", (long unsigned int)bit->time);
+	fprintf(stderr, "sector   %16lu\n", (long unsigned int)bit->sector);
 	fprintf(stderr, "bytes    %16d\n", bit->bytes);
 	fprintf(stderr, "action   %16x\n", bit->action);
 	fprintf(stderr, "pid      %16d\n", bit->pid);
@@ -295,7 +313,7 @@ static int zfcpdd_output_binary(struct d
 	return 0;
 
 failed:
-	fprintf(stderr, "ziomon_zfcpdd: could not write to %s\n", binary.fn);
+	fprintf(stderr, "%s: could not write to %s\n", toolname, binary.fn);
 	fclose(binary.fp);
 	binary.fn = NULL;
 	return 1;
@@ -316,20 +334,20 @@ static void print_hist(FILE *fp, const c
 		(unsigned long)(hist_upper_limit(i - 1, h)), a[i]);
 }
 
-static void print_var(FILE *fp, const char *s, struct stat_variance *v)
+static void print_var(FILE *fp, const char *s, struct abbrev_stat *v)
 {
 	fprintf(fp, "%s: min %ld, max %ld, sum %ld, squ %ld\n", s,
 		(unsigned long)v->min, (unsigned long)v->max,
 		(unsigned long)v->sum, (unsigned long)v->sos);
 }
 
-static int zfcpdd_output_ascii(struct dstat *dstat)
+static void zfcpdd_output_ascii(struct dstat *dstat)
 {
 	struct zfcpdd_dstat *p = &dstat->msg.stat;
 	FILE *fp = ascii.fp;
 
 	if (!ascii.fn)
-		return 0;
+		return;
 
 	fprintf(fp, "device: %d\t", p->device);
 	fprintf(fp, "interval end: %ld\n", (unsigned long)p->time);
@@ -342,7 +360,7 @@ static int zfcpdd_output_ascii(struct ds
 		   p->chan_lat_hist, &clat);
 	print_hist(fp, "fabric latency histogram (in usec)",
 		   p->fabr_lat_hist, &flat);
-	return 0;
+	return;
 }
 
 static int zfcpdd_output_msg_q(struct dstat *dstat)
@@ -366,8 +384,7 @@ static int zfcpdd_output(struct dstat *d
 		dstat->msg.stat.device, dstat);
 
 	dstat->msg.stat.time = time(NULL);
-	if (zfcpdd_output_ascii(dstat))
-		return 1;
+	zfcpdd_output_ascii(dstat);
 	if (zfcpdd_output_binary(dstat))
 		return 1;
 	if (zfcpdd_output_msg_q(dstat))
@@ -399,8 +416,6 @@ static void zfcpdd_consume(struct dhash 
 	}
 }
 
-static char zfcpdd_version[] = "0.2";
-
 static pthread_t interval_thread;
 
 
@@ -444,7 +459,7 @@ static int zfcpdd_open_output(struct out
 	return 0;
 
 failed:
-	fprintf(stderr, "ziomon_zfcpdd: could not open %s\n", out->fn);
+	fprintf(stderr, "%s: could not open %s\n", toolname, out->fn);
 	out->fn = NULL;
 	free(out->buf);
 	return 1;
@@ -485,25 +500,26 @@ static int zfcpdd_do_fifo(void)
 	while (fread(&bit, sizeof(bit), 1, ifp) == 1 && main_run) {
 		if (ferror(ifp)) {
 			clearerr(ifp);
-			perror("ziomon_zfcpdd: could not read trace");
-			break;
-		}
-		if (!(bit.action & 0x40000000) ||
-		    bit.pdu_len != sizeof(dd)) {
-			clearerr(ifp);
-			dump_bit(&bit, "ziomon_zfcpdd: not a valid trace");
+			fprintf(stderr, "%s: could not read trace: %s\n", toolname, strerror(errno));
 			break;
 		}
 		if (fread(&dd, bit.pdu_len, 1, ifp) != 1) {
 			clearerr(ifp);
-			perror("ziomon_zfcpdd: could not read trace payload");
+			fprintf(stderr, "%s: could not read trace payload: %s\n", toolname, strerror(errno));
 			break;
 		}
-		if (zfcpdd_account(&bit, &dd))
-			break;
+		if (bit.action & 0x40000000) {
+			if (bit.pdu_len != sizeof(dd)) {
+				clearerr(ifp);
+				dump_bit(&bit, "not a valid trace");
+				break;
+			}
+			if (zfcpdd_account(&bit, &dd))
+				break;
+		}
 	}
 	if (main_run)
-		verbose_msg("pipe ended, exiting");
+		verbose_msg("pipe ended, exiting\n");
 
 	return 0;
 }
@@ -512,15 +528,15 @@ static void *zfcpdd_interval(void *data)
 {
 	struct timespec t;
 	int finished;
-	
+
 	clock_gettime(CLOCK_REALTIME, &t);
-	
+
 	while (run) {
 		t.tv_sec += interval;
 		if (clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &t, NULL)) {
 			if (!run)
 				break;
-			perror("ziomon_zfcpdd: interrupted sleep");
+			fprintf(stderr, "%s: interrupted sleep:%s\n", toolname, strerror(errno));
 			continue;
 		}
 
@@ -535,10 +551,10 @@ static void *zfcpdd_interval(void *data)
 	return data;
 }
 
-#define S_OPTS "a:b:i:Q:q:m:v:V"
+#define S_OPTS "a:b:i:Q:q:m:Vvh"
 
 static char usage_str[] = "[-v] [-V] [-h] [-b <file>] [-Q <msgq_path> -q <msgq_id>\n"
-	"                       -m <msg_id>] -i <interval>\n"
+	" -m <msg_id>] -i <interval>\n"
 	"\n"
 	"Collect device statistics from blktrace stream.\n"
 	"\n"
@@ -565,9 +581,9 @@ static struct option l_opts[] = {
 	{ NULL,              0,                 NULL,  0  }
 };
 
-static void zfcpdd_usage(char *prog)
+static void zfcpdd_usage(void)
 {
-	fprintf(stdout, "Usage: %s %s", prog, usage_str);
+	fprintf(stdout, "Usage: %s %s", toolname, usage_str);
 }
 
 int main(int argc, char *argv[])
@@ -577,8 +593,8 @@ int main(int argc, char *argv[])
 	register_signals();
 
 	if (argc <= 1) {
-		zfcpdd_usage(argv[0]);
-		return 1;	
+		zfcpdd_usage();
+		return 1;
 	}
 
 	while ((c = getopt_long(argc, argv, S_OPTS, l_opts, NULL)) != -1) {
@@ -605,40 +621,40 @@ int main(int argc, char *argv[])
 			verbose++;
 			break;
 		case 'h':
-			zfcpdd_usage(argv[0]);
+			zfcpdd_usage();
 			return 0;
 		case 'v':
-			printf("ziomon_zfcpdd: ziomon manager %s (%s).\n"
-				"Copyright IBM Corp. 2008.\n",
-				RELEASE_STRING, zfcpdd_version);
+			printf("%s: ziomon fcp monitor version %s\n"
+				"Copyright IBM Corp. 2008\n", toolname,
+				RELEASE_STRING);
 			return 0;
 		default:
-			fprintf(stderr, "Try 'ziomon_zfcpdd --help' for more"
-                                        " information.\n");
+			fprintf(stderr, "Try '%s --help' for more"
+                                        " information.\n", toolname);
 			return 1;
 		}
 	}
 
 	ifp = fdopen(STDIN_FILENO, "r");
 	if (!ifp) {
-		perror("ziomon_zfcpdd: could not open stdin for reading");
+		fprintf(stderr, "%s: could not open stdin for reading: %s\n", toolname, strerror(errno));
 		return 1;
 	}
 
-	if ((msg_q_name && (msg_q_id < 0 || msg_id == LONG_MIN))
-	    || (msg_q_id >= 0 && (!msg_q_name || msg_id == LONG_MIN))
-	    || (msg_id != LONG_MIN && (!msg_q_name || msg_q_id < 0))) {
-		fprintf(stderr, "ziomon_zfcpdd: error: make sure to specify "
-			"all required arguments for message queue.\n");
-		return -1;
+	if (msg_q_name || msg_q_id >= 0 || msg_id != LONG_MIN) {
+		if (!msg_q_name || msg_q_id < 0 || msg_id == LONG_MIN) {
+			fprintf(stderr, "%s: error: make sure to specify "
+				"all required arguments for message queue.\n", toolname);
+			return -1;
+		}
 	}
-	
+
 	if (!interval) {
-		fprintf(stderr, "ziomon_zfcpdd: error: interval required\n");
-		zfcpdd_usage(argv[0]);
+		fprintf(stderr, "%s: error: interval required\n", toolname);
+		zfcpdd_usage();
 		return 1;
 	}
-	
+
 	if (zfcpdd_open_output(&ascii))
 		return 1;
 	if (zfcpdd_open_output(&binary))
@@ -648,7 +664,7 @@ int main(int argc, char *argv[])
 
 	/* setup thread which saves data to disk after the specified interval */
 	if (pthread_create(&interval_thread, NULL, zfcpdd_interval, NULL)) {
-		perror("ziomon_zfcpdd: could not create thread");
+		fprintf(stderr, "%s: could not create thread: %s\n", toolname, strerror(errno));
 		return 1;
 	}
 
@@ -659,12 +675,12 @@ int main(int argc, char *argv[])
 	run = 0; /* thread control variable */
 	pthread_kill(interval_thread, SIGINT);
 	pthread_join(interval_thread, NULL);
-	
+
 	/* aquire mutes before closing the file to avoid data corruption */
 	pthread_mutex_lock(&dstat_mutex);
 	zfcpdd_close_output(&binary);
 	pthread_mutex_unlock(&dstat_mutex);
-	
+
 	return 0;
 }
 #endif
Index: s390-tools-sles11/ziomon/ziomon_zfcpdd.h
===================================================================
--- s390-tools-sles11.orig/ziomon/ziomon_zfcpdd.h
+++ s390-tools-sles11/ziomon/ziomon_zfcpdd.h
@@ -18,22 +18,22 @@
 
 struct zfcpdd_dstat {
 	__u64 time;
+	/* Channel latency histogram in n-secs.
+	   Step-size is 1000, starting at 0. */
 	__u32 chan_lat_hist[BLKIOMON_CHAN_LAT_BUCKETS];
+	/* fabric latency histogram in u-secs.
+	   Step-size is 8, starting at 0. */
 	__u32 fabr_lat_hist[BLKIOMON_FABR_LAT_BUCKETS];
-	struct stat_variance chan_lat;
-	struct stat_variance fabr_lat;
-	struct stat_variance inb;
-	__u64 count;
+	struct abbrev_stat chan_lat;	/* channel latency in nano-seconds
+					   NOTE: often rescaled to micro-seconds
+						 later on! */
+	struct abbrev_stat fabr_lat;	/* fabric latency in micro-seconds */
+	struct abbrev_stat inb;	/* inbound fill size */
+	__u64 count;	/* number of samples for abbrev_stats */
 	__u32 device;	/* device identifier */
-	__u16 outb_max;
+	__u16 outb_max;	/* max used slots in qdio outbound queue */
 } __attribute__ ((packed));
 
-struct hist_log2 {
-	int first;
-	int delta;
-	int num;
-};
-
 void zfcpdd_print_stats(struct zfcpdd_dstat *stat);
 
 void conv_dstat_to_BE(struct zfcpdd_dstat *stat);
Index: s390-tools-sles11/ziomon/ziorep_cfgreader.cpp
===================================================================
--- /dev/null
+++ s390-tools-sles11/ziomon/ziorep_cfgreader.cpp
@@ -0,0 +1,889 @@
+/*
+ * FCP report generators
+ *
+ * Utility classes to read and access FCP configuration information
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Stefan Raspl <raspl@linux.vnet.ibm.com>
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "ziorep_cfgreader.hpp"
+#include "ziorep_filters.hpp"
+#include "ziorep_utils.hpp"
+
+#define	ZIOREP_CFG_EXTENSION	".cfg"
+#define ZIOREP_CONFIG_PATH	"/sbin/ziorep_config"
+
+extern const char *toolname;
+extern int verbose;
+
+
+ConfigReader::ConfigReader(int *rc, const char *filename)
+: m_tmp_file(NULL), m_cfg_cached(false)
+{
+	FILE			*fp = NULL;
+	struct device_info	 new_elem;
+	char			*line = NULL;
+	size_t			 line_len;
+	int			 lrc;
+	int			 line_idx = 1;
+	long long unsigned int	 tmp_lun;
+	long long unsigned int	 tmp_wwpn;
+
+	*rc = 0;
+
+	if (check_config_file(filename)) {
+		*rc = -1;
+		return;
+	}
+
+	if (extract_config_data(filename)) {
+		*rc = -2;
+		return;
+	}
+
+	init_device_info(&new_elem);
+
+	verbose_msg("ConfigReader: reading from %s\n", m_tmp_file);
+
+	fp = fopen(m_tmp_file, "r");
+	if (!fp) {
+		fprintf(stderr, "%s: Could not open config file %s\n", toolname, m_tmp_file);
+		*rc = -1;
+		goto out_fp_not_opened;
+	}
+	while ( (lrc = getline(&line, &line_len, fp)) >= 0) {
+		// allocation could be improved...
+		init_device_info(&new_elem);
+		new_elem.device = (char*)malloc(lrc + 1);
+		new_elem.type = (char*)malloc(lrc + 1);
+		new_elem.multipath_device = (char*)malloc(lrc + 1);
+		lrc = sscanf(line, "%x %u:%u:%u:%u 0.0.%x:0.0.%x:%Lx:%Lx %s %u %u:%u %s %u %u:%u %s",
+		       &new_elem.chpid,
+		       &new_elem.hctl_identifier.host,
+		       &new_elem.hctl_identifier.channel,
+		       &new_elem.hctl_identifier.target,
+		       &new_elem.hctl_identifier.lun,
+		       &new_elem.subchannel, &new_elem.devno,
+		       &tmp_wwpn,
+		       &tmp_lun,
+		       new_elem.multipath_device, &new_elem.mp_mm,
+		       &new_elem.mp_major,
+		       &new_elem.mp_minor, new_elem.device,
+		       &new_elem.mm_internal,
+		       &new_elem.major, &new_elem.minor,
+		       new_elem.type);
+		free(line);
+		line = NULL;
+		if (lrc != 18) {
+			fprintf(stderr, "%s: Could not parse line %d"
+				" - configuration file broken?\n", toolname, line_idx);
+			*rc = -1;
+			goto out;
+		}
+		new_elem.wwpn = tmp_wwpn;
+		new_elem.lun = tmp_lun;
+
+		if (strcmp(new_elem.multipath_device, "n/a") == 0) {
+			free(new_elem.multipath_device);
+			new_elem.multipath_device = NULL;
+			new_elem.mp_major = 0;
+			new_elem.mp_minor = 0;
+		}
+		m_devices.push_back(new_elem);
+		++line_idx;
+	}
+	init_device_info(&new_elem);
+
+	if (filter_unused_devices(filename)) {
+		*rc = -2;
+		goto out;
+	}
+
+	verbose_msg("ConfigReader: done\n");
+
+out:
+	fclose(fp);
+out_fp_not_opened:
+	free_device_info(&new_elem);
+	free(line);
+
+	if (m_tmp_file) {
+		if (!m_cfg_cached || *rc)
+			remove(m_tmp_file);
+		free(m_tmp_file);
+		m_tmp_file = NULL;
+	}
+}
+
+
+ConfigReader::~ConfigReader()
+{
+	for (list<struct device_info>::iterator i = m_devices.begin();
+	      i != m_devices.end(); ++i)
+		free_device_info(&(*i));
+}
+
+
+int ConfigReader::filter_unused_devices(const char *filename)
+{
+	DeviceFilter dev_filt;
+        int j = 0;
+
+	if (get_all_devices(filename, dev_filt, *this))
+		return -1;
+
+	for (list<struct device_info>::iterator i = m_devices.begin();
+	      i != m_devices.end();) {
+		if (!dev_filt.is_eligible_mm((*i).mm_internal)) {
+			verbose_msg("  device %u:%u not in data, remove\n",
+				    (*i).major, (*i).minor);
+			free_device_info(&(*i));
+			i = m_devices.erase(i);
+			j++;
+		}
+		else
+			++i;
+	}
+	verbose_msg("removed %d of %lu devices\n", j,
+			(long unsigned int)(m_devices.size() + j));
+
+	return 0;
+}
+
+
+int ConfigReader::check_ziorep_config() const
+{
+	if (access(ZIOREP_CONFIG_PATH, F_OK | R_OK | X_OK)) {
+		fprintf(stderr, "%s: Cannot access " ZIOREP_CONFIG_PATH
+			". Please check your installation and try again.\n", toolname);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int ConfigReader::check_config_file(const char *fname) const
+{
+	char *tmp;
+	int rc = 0;
+
+	tmp = (char*)malloc(strlen(fname) +  strlen(ZIOREP_CFG_EXTENSION) + 1);
+	sprintf(tmp, "%s%s", fname, ZIOREP_CFG_EXTENSION);
+
+	if (access(tmp, F_OK | R_OK)) {
+		fprintf(stderr, "%s: Cannot access config file %s."
+			" Please make sure that you get the matching .cfg"
+			" file for your data.\n", toolname, tmp);
+		rc = -1;
+	}
+	free(tmp);
+
+	return rc;
+}
+
+#define ZIOREP_TMP_TEMPLATE	"/tmp/ziorepXXXXXX"
+#define ZIOREP_CONFIG_EXT	".config"
+
+
+int ConfigReader::extract_config_data(const char *fname)
+{
+	verbose_msg("Check config data...\n");
+	if (cached_config_exists(fname)) {
+		verbose_msg("Cached file found, reusing.\n");
+		m_cfg_cached = true;
+		return 0;
+	}
+
+	// Try to extract to .config first, which will be permanently cached
+	verbose_msg("No data cached, extract\n");
+	if (check_ziorep_config())
+		return -1;
+
+	fprintf(stdout, "Extracting config data...");
+	fflush(stdout);
+	if (extract_cached(fname)) {
+		verbose_msg("Could not create %s file, create tmp file\n",
+			    ZIOREP_CONFIG_EXT);
+		if (extract_tmp(fname)) {
+			fprintf(stderr, "%s: Could not extract"
+				" configuration data. Check the integrity of"
+				" %s%s with %s and retry.", toolname, fname,
+				ZIOREP_CFG_EXTENSION, ZIOREP_CONFIG_PATH);
+			return -1;
+		}
+	}
+
+	fprintf(stdout, "done\n");
+
+	verbose_msg("Check config data finished\n");
+
+	return 0;
+}
+
+
+int ConfigReader::extract_cached(const char *fname)
+{
+	assert(m_tmp_file == NULL);
+
+	m_tmp_file = (char*)malloc(strlen(fname) + strlen(ZIOREP_CONFIG_EXT)
+				   + 1);
+	sprintf(m_tmp_file, "%s%s", fname, ZIOREP_CONFIG_EXT);
+
+	if (extract(fname)) {
+		// we can't guarantee that something was not written,
+		// hence we remove the file anyway to avoid stumbling
+		// over a broken .config file next time
+		remove(m_tmp_file);
+		free(m_tmp_file);
+		m_tmp_file = NULL;
+		return -1;
+	}
+	m_cfg_cached = true;
+
+	return 0;
+}
+
+
+int ConfigReader::extract_tmp(const char *fname)
+{
+	int rc = 0;
+
+	assert(m_tmp_file == NULL);
+
+	m_tmp_file = (char*)malloc(strlen(ZIOREP_TMP_TEMPLATE) + 1);
+	strcpy(m_tmp_file, ZIOREP_TMP_TEMPLATE);
+
+	if (mkstemp(m_tmp_file) == -1) {
+		fprintf(stdout, "Error: Could not create temporary"
+			" filename: %s\n", strerror(errno));
+		rc = -1;
+		goto out;
+	}
+
+	rc = extract(fname);
+
+out:
+	if (rc) {
+		free(m_tmp_file);
+		m_tmp_file = NULL;
+	}
+
+	return rc;
+}
+
+
+int ConfigReader::extract(const char *fname)
+{
+	int rc = 0;
+	char *cmd = NULL;
+
+	// /sbin/ziorep_config -I -i <fname.cfg> > <file> 2>/dev/null
+	cmd = (char*)malloc(strlen(ZIOREP_CONFIG_PATH) + 7 + strlen(fname)
+		     + strlen(ZIOREP_CFG_EXTENSION) + 3 + strlen(m_tmp_file)
+		     + 12 + 1);
+
+	sprintf(cmd, "%s -I -i %s%s > %s 2>/dev/null", ZIOREP_CONFIG_PATH, fname,
+		ZIOREP_CFG_EXTENSION, m_tmp_file);
+
+	verbose_msg("Issue command: %s\n", cmd);
+
+	if (system(cmd)) {
+		rc = -1;
+		goto out;
+	}
+	verbose_msg("Data extracted to %s\n", m_tmp_file);
+
+out:
+	free(cmd);
+	return rc;
+}
+
+
+bool ConfigReader::cached_config_exists(const char *fname)
+{
+	int rc = true;
+
+	m_tmp_file = (char*)malloc(strlen(fname) + strlen(ZIOREP_CONFIG_EXT)
+				   + 1);
+	sprintf(m_tmp_file, "%s%s", fname, ZIOREP_CONFIG_EXT);
+
+	if (access(m_tmp_file, R_OK)) {
+		verbose_msg("No cached file found.\n");
+		free(m_tmp_file);
+		m_tmp_file = NULL;
+		rc = false;
+	}
+
+	return rc;
+}
+
+
+int ConfigReader::extract_adapter_info_sub(char **tgt, char *p, char delim)
+{
+	int offset;
+
+	offset = get_index_to(p, delim);
+	if (offset <=0)
+		return -1;
+	*tgt = (char*)malloc(offset + 1);
+	strncpy(*tgt, p, offset);
+	(*tgt)[offset] = '\0';
+
+	return 0;
+}
+
+
+void ConfigReader::init_device_info(struct device_info *info)
+{
+	info->multipath_device = NULL;
+	info->device = NULL;
+	info->type = NULL;
+}
+
+
+void ConfigReader::free_device_info(struct device_info *info)
+{
+	free(info->multipath_device);
+	info->multipath_device = NULL;
+	free(info->device);
+	info->device = NULL;
+	free(info->type);
+	info->type = NULL;
+}
+
+
+int ConfigReader::get_index_to(char *p, char c) const
+{
+	int i;
+
+	for (i = 0; *p != c && *p != '\0'; ++p, ++i);
+
+	return i;
+}
+
+
+void ConfigReader::host_id_not_found_error(__u32 h, int *rc) const
+{
+	*rc = -1;
+	fprintf(stderr, "%s: Could not find host index %u in configuration"
+		" file. Please check if you have the right .cfg file and try"
+		" again\n", toolname, h);
+}
+
+
+void ConfigReader::devno_not_found_error(__u32 devno, int *rc) const
+{
+	*rc = -1;
+	fprintf(stderr, "%s: Could not find bus id 0.0.%x in configuration"
+		" file. Please check if you have the right .cfg file and try"
+		" again\n", toolname, devno);
+}
+
+
+void ConfigReader::chpid_not_found_error(__u32 chpid, int *rc) const
+{
+	*rc = -1;
+	fprintf(stderr, "%s: Could not find chpid %x in configuration"
+		" file. Please check if you have the right .cfg file and try"
+		" again\n", toolname, chpid);
+}
+
+
+void ConfigReader::lun_not_found_error(__u64 lun, int *rc) const
+{
+	*rc = -1;
+	fprintf(stderr, "%s: Could not find LUN %016Lx in configuration"
+		" file. Please check if you have the right .cfg file and try"
+		" again\n", toolname, (long long unsigned int)lun);
+}
+
+
+void ConfigReader::ident_not_found_error(const struct hctl_ident *ident, int *rc) const
+{
+	*rc = -1;
+	fprintf(stderr, "%s: Could not find identifier [%d:%d:%d:%d] in"
+		" configuration file. Please check if you have the right .cfg"
+		" file and try again\n", toolname, ident->host, ident->channel,
+		ident->target, ident->lun);
+}
+
+
+void ConfigReader::mm_internal_not_found_error(__u32 mm, int *rc) const
+{
+	*rc = -1;
+	fprintf(stderr, "%s: Could not find device with internal mm %u in"
+		" configuration file. Please check if you have the right .cfg"
+		" file and try again\n", toolname, mm);
+}
+
+
+void ConfigReader::device_not_found_error(const char *dev, int *rc) const
+{
+	*rc = -1;
+	fprintf(stderr, "%s: Could not find device %s in"
+		" configuration file. Please check if you have the right .cfg"
+		" file and try again\n", toolname, dev);
+}
+
+
+void ConfigReader::mp_not_found_error(const char *mp, int *rc) const
+{
+	*rc = -1;
+	fprintf(stderr, "%s: Could not find multipath device %s in"
+		" configuration file. Please check if you have the right .cfg"
+		" file and try again\n", toolname, mp);
+}
+
+
+void ConfigReader::mp_mm_not_found_error(__u32 mp_mm, int *rc) const
+{
+	*rc = -1;
+	fprintf(stderr, "%s: Could not find multipath device %ud in"
+		" configuration file. Please check if you have the right .cfg"
+		" file and try again\n", toolname, mp_mm);
+}
+
+
+#define	search_for(att, crit, ret)	for (list<struct device_info>::const_iterator i = m_devices.begin(); \
+							i != m_devices.end(); ++i) { \
+						if ((*i).att == crit) \
+							return (*i).ret; \
+					}
+
+#define	search_for_by_dev(crit, ret)	for (list<struct device_info>::const_iterator i = m_devices.begin(); \
+							i != m_devices.end(); ++i) { \
+						if (compare_hctl_idents(&(*i).hctl_identifier, crit) == 0) \
+							return (*i).ret; \
+					}
+
+__u32 ConfigReader::get_chpid_by_host_id(__u32 host, int *rc) const
+{
+	search_for(hctl_identifier.host, host, chpid);
+
+	host_id_not_found_error(host, rc);
+
+	return 0;
+}
+
+
+__u32 ConfigReader::get_chpid_by_devno(__u32 d, int *rc) const
+{
+	search_for(devno, d, chpid);
+
+	devno_not_found_error(d, rc);
+
+	return 0;
+}
+
+
+__u32 ConfigReader::get_chpid_by_ident(const struct hctl_ident *ident, int *rc) const
+{
+	search_for_by_dev(ident, chpid);
+
+	ident_not_found_error(ident, rc);
+
+	return 0;
+}
+
+
+__u32 ConfigReader::get_chpid_by_mm_internal(__u32 mm, int *rc) const
+{
+	search_for(mm_internal, mm, chpid);
+
+	mm_internal_not_found_error(mm, rc);
+
+	return 0;
+}
+
+
+__u32 ConfigReader::get_host_id_by_chpid(__u32 chpid, int *rc) const
+{
+	search_for(chpid, chpid, hctl_identifier.host);
+
+	chpid_not_found_error(chpid, rc);
+
+	return 0;
+}
+
+
+__u32 ConfigReader::get_devno_by_host_id(__u32 host, int *rc) const
+{
+	search_for(hctl_identifier.host, host, devno);
+
+	host_id_not_found_error(host, rc);
+
+	return 0;
+}
+
+
+__u32 ConfigReader::get_devno_by_ident(const struct hctl_ident *ident,
+				       int *rc) const
+{
+	search_for_by_dev(ident, devno);
+
+	ident_not_found_error(ident, rc);
+
+	return 0;
+}
+
+
+__u32 ConfigReader::get_devno_by_mm_internal(__u32 mm, int *rc) const
+{
+	search_for(mm_internal, mm, devno);
+
+	mm_internal_not_found_error(mm, rc);
+
+	return 0;
+}
+
+
+__u32 ConfigReader::get_mp_mm_by_multipath(const char* mp, int *rc) const
+{
+	for (list<struct device_info>::const_iterator i = m_devices.begin();
+	    i != m_devices.end(); ++i) {
+		if ((*i).multipath_device
+		    && strcmp((*i).multipath_device + strlen("/dev/mapper/"),
+			      mp) == 0)
+			return (*i).mp_mm;
+	}
+
+	mp_not_found_error(mp, rc);
+
+	return 0;
+}
+
+
+const char* ConfigReader::get_multipath_by_mp_mm(__u32 mp_mm, int *rc) const
+{
+	search_for(mp_mm, mp_mm, multipath_device);
+
+	mp_mm_not_found_error(mp_mm, rc);
+
+	return NULL;
+}
+
+
+__u64 ConfigReader::get_wwpn_by_mm_internal(__u32 dev, int *rc) const
+{
+	search_for(mm_internal, dev, wwpn);
+
+	mm_internal_not_found_error(dev, rc);
+
+	return 0;
+}
+
+
+__u64 ConfigReader::get_wwpn_by_ident(const struct hctl_ident *ident, int *rc) const
+{
+	search_for_by_dev(ident, wwpn);
+
+	ident_not_found_error(ident, rc);
+
+	return 0;
+}
+
+
+__u32 ConfigReader::get_mp_mm_by_mm_internal(__u32 mm, int *rc) const
+{
+	search_for(mm_internal, mm, mp_mm);
+
+	mm_internal_not_found_error(mm, rc);
+
+	return 0;
+}
+
+
+__u32 ConfigReader::get_mp_mm_by_ident(const struct hctl_ident *ident, int *rc) const
+{
+	search_for_by_dev(ident, mp_mm);
+
+	ident_not_found_error(ident, rc);
+
+	return 0;
+}
+
+
+__u64 ConfigReader::get_lun_by_mm_internal(__u32 mm, int *rc) const
+{
+	search_for(mm_internal, mm, lun);
+
+	mm_internal_not_found_error(mm, rc);
+
+	return 0;
+}
+
+const char* ConfigReader::get_dev_by_mm_internal(__u32 mm, int *rc) const
+{
+	search_for(mm_internal, mm, device);
+
+	mm_internal_not_found_error(mm, rc);
+
+	return "<invalid device>";
+}
+
+__u32 ConfigReader::get_mm_by_ident(const struct hctl_ident *id, int *rc) const
+{
+	search_for_by_dev(id, mm_internal);
+
+	ident_not_found_error(id, rc);
+
+	return 0;
+}
+
+__u32 ConfigReader::get_mm_by_device(const char *dev, int *rc) const
+{
+	for (list<struct device_info>::const_iterator i = m_devices.begin();
+	    i != m_devices.end(); ++i) {
+		if (strcmp((*i).device + strlen("/dev/"), dev) == 0)
+			return (*i).mm_internal;
+	}
+
+	device_not_found_error(dev, rc);
+
+	return 0;
+}
+
+const struct hctl_ident* ConfigReader::get_ident_by_mm_internal(__u32 mm, int *rc) const
+{
+	for (list<struct device_info>::const_iterator i = m_devices.begin();
+		i != m_devices.end(); ++i) {
+		if ((*i).mm_internal == mm)
+			return &(*i).hctl_identifier;
+	}
+
+	mm_internal_not_found_error(mm, rc);
+
+	return NULL;
+}
+
+
+#define	get_uniq(tgt, a, type)		tgt.clear(); \
+	for (list<struct device_info>::const_iterator i = m_devices.begin(); \
+	      i != m_devices.end(); ++i) { \
+		list<type>::iterator j; \
+		for (j = tgt.begin(); j != tgt.end() && (*i).a != (*j); ++j); \
+		if (j == tgt.end()) \
+			tgt.push_back((*i).a); \
+	}
+
+
+void ConfigReader::get_unique_wwpns(list<__u64> &wwpns) const
+{
+	get_uniq(wwpns, wwpn, __u64);
+}
+
+
+void ConfigReader::get_unique_devnos(list<__u32> &devnos) const
+{
+	get_uniq(devnos, devno, __u32);
+}
+
+
+void ConfigReader::get_unique_chpids(list<__u32> &chpids) const
+{
+	get_uniq(chpids, chpid, __u32);
+}
+
+
+void ConfigReader::get_unique_host_ids(list<__u32> &host_ids) const
+{
+	get_uniq(host_ids, hctl_identifier.host, __u32);
+}
+
+
+void ConfigReader::get_unique_mp_mms(list<__u32> &mp_mms) const
+{
+	mp_mms.clear();
+	for (list<struct device_info>::const_iterator i = m_devices.begin();
+	      i != m_devices.end(); ++i) {
+		list<__u32>::iterator j;
+		for (j = mp_mms.begin();
+		      j != mp_mms.end() && (*i).mp_mm != (*j); ++j);
+		/* Watch out: Always check the multipath_device attribute
+		   to see whether the mp_mm is valid or not! */
+		if (j == mp_mms.end() && (*i).multipath_device != NULL)
+			mp_mms.push_back((*i).mp_mm);
+	}
+}
+
+
+void ConfigReader::get_unique_mms(list<__u32> &lst) const
+{
+	lst.clear();
+	for (list<struct device_info>::const_iterator i = m_devices.begin();
+	      i != m_devices.end(); ++i)
+		// devices are unique by definition!
+		lst.push_back((*i).mm_internal);
+}
+
+
+void ConfigReader::get_unique_devices(list<struct hctl_ident> &idents) const
+{
+	idents.clear();
+	for (list<struct device_info>::const_iterator i = m_devices.begin();
+	      i != m_devices.end(); ++i) {
+		// devices are unique by definition!
+		idents.push_back((*i).hctl_identifier);
+	}
+}
+
+
+void ConfigReader::get_devnos_by_chpid(list<__u32> &devnos, __u32 chpid) const
+{
+	devnos.clear();
+	for (list<struct device_info>::const_iterator i = m_devices.begin();
+	      i != m_devices.end(); ++i) {
+		if ((*i).chpid == chpid)
+			devnos.push_back((*i).devno);
+	}
+}
+
+
+void ConfigReader::get_devnos_by_host_id(list<__u32> &devnos, __u32 host_id) const
+{
+	devnos.clear();
+	for (list<struct device_info>::const_iterator i = m_devices.begin();
+	      i != m_devices.end(); ++i) {
+		if ((*i).hctl_identifier.host == host_id)
+			devnos.push_back((*i).devno);
+	}
+}
+
+
+#define get_mms_list(lst, crit, val)		lst.clear(); \
+	for (list<struct device_info>::const_iterator i = m_devices.begin(); \
+	      i != m_devices.end(); ++i) { \
+		if ((*i).crit == val) \
+			lst.push_back((*i).mm_internal); \
+	} \
+
+void ConfigReader::get_mms_by_chpid(list<__u32> &mms, __u32 chpid) const
+{
+	get_mms_list(mms, chpid, chpid);
+}
+
+void ConfigReader::get_mms_by_mp_mm(list<__u32> &mms, __u32 mp_mm) const
+{
+	get_mms_list(mms, mp_mm, mp_mm);
+}
+
+void ConfigReader::get_mms_by_wwpn(list<__u32> &mms, __u64 wwpn) const
+{
+	get_mms_list(mms, wwpn, wwpn);
+}
+
+void ConfigReader::get_mms_by_devno(list<__u32> &mms, __u32 d) const
+{
+	get_mms_list(mms, devno, d);
+}
+
+void ConfigReader::get_mms_by_lun(list<__u32> &mms, __u64 l) const
+{
+	get_mms_list(mms, lun, l);
+}
+
+#define verify_numeric(criterion, val)		for \
+		(list<struct device_info>::const_iterator \
+		i = m_devices.begin(); i != m_devices.end(); ++i) { \
+		if ((*i).criterion == val) \
+			return true; \
+	} \
+	return false;
+
+
+#define verify_char(criterion, val, rc)		for \
+		(list<struct device_info>::const_iterator \
+		i = m_devices.begin(); i != m_devices.end(); ++i) { \
+		if (val && (*i).criterion && strcmp((*i).criterion, val) == 0) \
+			rc = true; \
+	}
+
+
+bool ConfigReader::verify_chpid(__u32 c) const
+{
+	verify_numeric(chpid, c);
+}
+
+
+bool ConfigReader::verify_device(const char *dev) const
+{
+	bool rc = false;
+	char *tmp;
+
+	tmp = (char*)malloc(strlen(dev) + strlen("/dev/") + 1);
+	sprintf(tmp, "/dev/%s", dev);
+
+	verify_char(device, tmp, rc);
+
+	free(tmp);
+
+	return rc;
+}
+
+
+bool ConfigReader::verify_mp_device(const char *mp) const
+{
+	bool rc = false;
+	char *tmp;
+
+	tmp = (char*)malloc(strlen(mp) + strlen("/dev/mapper/") + 1);
+	sprintf(tmp, "/dev/mapper/%s", mp);
+
+	verify_char(multipath_device, tmp, rc);
+
+	free(tmp);
+
+	return rc;
+}
+
+
+bool ConfigReader::verify_wwpn(__u64 w) const
+{
+	verify_numeric(wwpn, w);
+}
+
+
+bool ConfigReader::verify_devno(__u32 d) const
+{
+	verify_numeric(devno, d);
+}
+
+
+bool ConfigReader::verify_lun(__u64 l) const
+{
+	verify_numeric(lun, l);
+}
+
+
+void ConfigReader::dump(FILE *fp) const
+{
+	fprintf(fp, "dumping cfg....\n");
+	for (list<struct device_info>::const_iterator i = m_devices.begin();
+	      i != m_devices.end(); ++i) {
+		fprintf(fp, "%x %u %u:%u:%u:%u 0.0.%04x:0.0.%x:%016Lx:%016Lx %s %u:%u %s %u:%u %s\n",
+		       (*i).chpid, (*i).mm_internal,
+		       (*i).hctl_identifier.host,
+		       (*i).hctl_identifier.channel,
+		       (*i).hctl_identifier.target,
+		       (*i).hctl_identifier.lun,
+		       (*i).subchannel, (*i).devno,
+		       (long long unsigned int)(*i).wwpn,
+		       (long long unsigned int)(*i).lun,
+		       ((*i).multipath_device ? (*i).multipath_device : "n/a"), (*i).mp_major,
+		       (*i).mp_minor, (*i).device,
+		       (*i).major, (*i).minor,
+		       (*i).type);
+	}
+}
+
+
+
+
Index: s390-tools-sles11/ziomon/ziorep_cfgreader.hpp
===================================================================
--- /dev/null
+++ s390-tools-sles11/ziomon/ziorep_cfgreader.hpp
@@ -0,0 +1,239 @@
+/*
+ * FCP report generators
+ *
+ * Utility classes to read and access FCP configuration information
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Stefan Raspl <raspl@linux.vnet.ibm.com>
+ */
+
+#ifndef ZIOMON_CFGREADER
+#define ZIOMON_CFGREADER
+
+#include <stdio.h>
+#include <list>
+
+#include <linux/types.h>
+
+extern "C" {
+	#include "ziomon_util.h"
+}
+
+
+
+using std::list;
+
+
+/**
+ * Parses a file holding the system-wide available devices. Since this
+ * is more than what is in the data, the devices are filtered, stripping
+ * it down to devices only actually used in the data.
+ */
+class ConfigReader {
+public:
+	ConfigReader(int *rc, const char *filename);
+	~ConfigReader();
+
+	/// rc is only set on error!
+	__u32 get_chpid_by_host_id(__u32 h, int *rc) const;
+	/// rc is only set on error!
+	__u32 get_chpid_by_devno(__u32 devno, int *rc) const;
+	/// rc is only set on error!
+	__u32 get_chpid_by_ident(const struct hctl_ident *ident, int *rc) const;
+	/// rc is only set on error!
+	__u32 get_chpid_by_mm_internal(__u32 mm, int *rc) const;
+
+	/// rc is only set on error!
+	__u32 get_host_id_by_chpid(__u32 chpid, int *rc) const;
+
+	/// rc is only set on error!
+	__u32 get_devno_by_host_id(__u32 h, int *rc) const;
+	/// rc is only set on error!
+	__u32 get_devno_by_ident(const struct hctl_ident *ident, int *rc) const;
+
+	/// rc is only set on error!
+	__u32 get_devno_by_mm_internal(__u32 mm, int *rc) const;
+
+	/// rc is only set on error!
+	__u32 get_mp_mm_by_multipath(const char* mp, int *rc) const;
+	/// rc is only set on error!
+	const char* get_multipath_by_mp_mm(__u32 mp_mm, int *rc) const;
+
+	/// rc is only set on error!
+	__u64 get_wwpn_by_ident(const struct hctl_ident *ident, int *rc) const;
+	/// rc is only set on error!
+	__u64 get_wwpn_by_mm_internal(__u32 mm, int *rc) const;
+
+	/// rc is only set on error!
+	__u32 get_mp_mm_by_ident(const struct hctl_ident *ident, int *rc) const;
+	/// rc is only set on error!
+	__u32 get_mp_mm_by_mm_internal(__u32 mm, int *rc) const;
+
+	/// rc is only set on error!
+	__u64 get_lun_by_mm_internal(__u32 mm_internal, int *rc) const;
+
+	/// rc is only set on error!
+	const char* get_dev_by_mm_internal(__u32 mm_internal, int *rc) const;
+
+	/// rc is only set on error!
+	__u32 get_mm_by_ident(const struct hctl_ident *id, int *rc) const;
+
+	/// rc is only set on error!
+	__u32 get_mm_by_device(const char *dev, int *rc) const;
+
+	/// rc is only set on error!
+	const struct hctl_ident* get_ident_by_mm_internal(__u32 mm, int *rc) const;
+
+	void get_unique_wwpns(list<__u64> &lst) const;
+
+	void get_unique_devnos(list<__u32> &lst) const;
+
+	void get_unique_chpids(list<__u32> &lst) const;
+
+	void get_unique_mp_mms(list<__u32> &lst) const;
+
+	void get_unique_mms(list<__u32> &mms) const;
+
+	void get_unique_devices(list<struct hctl_ident> &idents) const;
+
+	void get_unique_host_ids(list<__u32> &host_ids) const;
+
+	void get_devnos_by_chpid(list<__u32> &devnos, __u32 chpid) const;
+
+	void get_devnos_by_host_id(list<__u32> &devnos, __u32 host_id) const;
+
+	void get_mms_by_chpid(list<__u32> &mms, __u32 chpid) const;
+
+	void get_mms_by_mp_mm(list<__u32> &mms, __u32 mp_mm) const;
+
+	void get_mms_by_wwpn(list<__u32> &mms, __u64 wwpn) const;
+
+	void get_mms_by_devno(list<__u32> &mms, __u32 devno) const;
+
+	void get_mms_by_lun(list<__u32> &mms, __u64 lun) const;
+
+	/// returns 'true' in case it exists, 'false' otherwise
+	bool verify_chpid(__u32 chpid) const;
+
+	/// returns 'true' in case it exists, 'false' otherwise
+	bool verify_device(const char *) const;
+
+	/// returns 'true' in case it exists, 'false' otherwise
+	bool verify_mp_device(const char *) const;
+
+	/// returns 'true' in case it exists, 'false' otherwise
+	bool verify_wwpn(__u64 wwpn) const;
+
+	/// returns 'true' in case it exists, 'false' otherwise
+	bool verify_devno(__u32 devno) const;
+
+	/// returns 'true' in case it exists, 'false' otherwise
+	bool verify_lun(__u64 lun) const;
+
+	/// debugging
+	void dump(FILE *fp) const;
+
+private:
+	/** compare currently held devices with devices as found
+	 * in the actual data, and remove anything that is unused */
+	int filter_unused_devices(const char *filename);
+
+	int check_ziorep_config() const;
+
+	int check_config_file(const char *fname) const;
+
+	int extract_config_data(const char *fname);
+
+	int extract(const char *fname);
+
+	int extract_tmp(const char *fname);
+
+	int extract_cached(const char *fname);
+
+	bool cached_config_exists(const char *fname);
+
+	struct device_info {
+		// chpid, e.g. 43 (hex)
+		__u32	chpid;
+
+		// kernel-internal major/minor representation, e.g. 2096 (number)
+		__u32	mm_internal;
+
+		// fcp device identifier, e.g. 0:0:17:1075920929
+		struct hctl_ident	hctl_identifier;
+
+		//
+		// e.g. 0.0.0010:0.0.5b1d:0x50050763030b0104:0x4021402100000000
+		__u32	subchannel;
+		__u32	devno;
+		__u64	wwpn;
+		__u64	lun;
+
+		// multipath device, e.g. /dev/mapper/2376878489249234
+		char   *multipath_device;
+
+		// maj/min of multipath device, e.g. 0:0
+		__u32	mp_major;
+		__u32	mp_minor;
+
+		__u32	mp_mm;	// NOT IN INPUT-DATA!!!
+
+		// device node, e.g. /dev/sda
+		char   *device;
+
+		// major/minor, e.g. 8:48
+		__u32	major;
+		__u32	minor;
+
+		// device type, e.g. "Disk"
+		char   *type;
+	};
+	list<struct device_info>	m_devices;
+
+	/**
+	 * File holding the internal representation of the configuration
+	 * data. If m_cfg_cached is false, then it must be removed once
+	 * we are done. Otherwise we can leave as is for successive runs. */
+	char 			       *m_tmp_file;
+
+	/**
+	 * If this is set, then we didn't extract the configuration data to
+	 * /tmp but rather created an additional .config file which we leave
+	 * for successive runs. */
+	bool				m_cfg_cached;
+
+	/**
+	 * Returns the offset of the first appearance of 'c'
+	 * within p. */
+	int get_index_to(char *p, char c) const;
+
+	void init_device_info(struct device_info *info);
+
+	void free_device_info(struct device_info *info);
+
+	int extract_adapter_info(char *p, struct device_info *info);
+
+	int extract_adapter_info_sub(char **tgt, char *p, char delim);
+
+	void host_id_not_found_error(__u32 h, int *rc) const;
+
+	void devno_not_found_error(__u32 d, int *rc) const;
+
+	void chpid_not_found_error(__u32 chpid, int *rc) const;
+
+	void lun_not_found_error(__u64 lun, int *rc) const;
+
+	void ident_not_found_error(const struct hctl_ident *ident, int *rc) const;
+
+	void mm_internal_not_found_error(__u32 mm, int *rc) const;
+
+	void device_not_found_error(const char*, int *rc) const;
+
+	void mp_not_found_error(const char* mp, int *rc) const;
+
+	void mp_mm_not_found_error(__u32 mp_mm, int *rc) const;
+};
+
+
+#endif
+
Index: s390-tools-sles11/ziomon/ziorep_collapser.cpp
===================================================================
--- /dev/null
+++ s390-tools-sles11/ziomon/ziorep_collapser.cpp
@@ -0,0 +1,570 @@
+/*
+ * FCP report generators
+ *
+ * Utility classes to get indices for collapsing data by
+ * variable criteria
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Stefan Raspl <raspl@linux.vnet.ibm.com>
+ */
+
+#include <string.h>
+#include <assert.h>
+
+#include "ziorep_collapser.hpp"
+
+extern "C" {
+	#include "ziomon_tools.h"
+}
+
+extern const char *toolname;
+extern int verbose;
+
+
+Collapser::Collapser(Aggregator criterion)
+: m_criterion(criterion) {
+}
+
+
+Collapser::~Collapser()
+{
+}
+
+
+Aggregator Collapser::get_criterion() const
+{
+	return m_criterion;
+}
+
+
+void Collapser::add_to_index(struct ident_mapping *new_mapping) const
+{
+	list<struct ident_mapping>::iterator i;
+
+	for (i = m_idents.begin(); i != m_idents.end()
+	      && compare_hctl_idents(&new_mapping->ident, &(*i).ident) >= 0;
+	      ++i);
+	if (i == m_idents.end() || compare_hctl_idents(&new_mapping->ident, &(*i).ident) != 0)
+		m_idents.insert(i, *new_mapping);
+}
+
+
+void Collapser::add_to_index(struct device_mapping *new_mapping) const
+{
+	list<struct device_mapping>::iterator i;
+
+	for (i = m_devices.begin(); i != m_devices.end()
+	      && new_mapping->device >= (*i).device;
+	      ++i);
+	if (i == m_devices.end() || (*i).device != new_mapping->device)
+		m_devices.insert(i, *new_mapping);
+}
+
+
+void Collapser::add_to_index(struct host_id_mapping *new_mapping) const
+{
+	list<struct host_id_mapping>::iterator i;
+
+	for (i = m_host_ids.begin(); i != m_host_ids.end()
+	      && new_mapping->h >= (*i).h;
+	      ++i);
+	if (i == m_host_ids.end() || (*i).h != new_mapping->h)
+		m_host_ids.insert(i, *new_mapping);
+}
+
+
+int Collapser::lookup_index(struct hctl_ident *identifier) const
+{
+	int rc;
+
+	for (list<struct ident_mapping>::const_iterator i = m_idents.begin();
+	      i != m_idents.end() && (rc = compare_hctl_idents(identifier, &(*i).ident)) >= 0; ++i ) {
+		if (rc == 0)
+			return (*i).idx;
+	}
+
+	return -1;
+}
+
+
+int Collapser::lookup_index(__u32 device) const
+{
+	for (list<struct device_mapping>::const_iterator i = m_devices.begin();
+	      i != m_devices.end() && device >= (*i).device; ++i ) {
+		if ((*i).device == device)
+			return (*i).idx;
+	}
+
+	return -1;
+}
+
+
+int Collapser::lookup_index_by_host_id(__u32 h) const
+{
+	for (list<struct host_id_mapping>::const_iterator i = m_host_ids.begin();
+	      i != m_host_ids.end() && h >= (*i).h; ++i ) {
+		if ((*i).h == h)
+			return (*i).idx;
+	}
+
+	return -1;
+}
+
+
+NoopCollapser::NoopCollapser()
+: Collapser(none) {
+	m_criterion = none;
+}
+
+
+unsigned int NoopCollapser::get_index(struct hctl_ident *identifier) const
+{
+	int rc;
+
+	rc = lookup_index(identifier);
+	if (rc < 0) {
+		struct ident_mapping new_mapping;
+		new_mapping.ident = *identifier;
+		new_mapping.idx   = m_idents.size();
+		add_to_index(&new_mapping);
+		rc = new_mapping.idx;
+	}
+
+	return rc;
+}
+
+
+unsigned int NoopCollapser::get_index(__u32 device) const
+{
+	int rc;
+
+	rc = lookup_index(device);
+	if (rc < 0) {
+		struct device_mapping new_mapping;
+		new_mapping.device = device;
+		new_mapping.idx    = m_devices.size();
+		add_to_index(&new_mapping);
+		rc = new_mapping.idx;
+	}
+
+	return rc;
+}
+
+
+
+unsigned int NoopCollapser::get_index_by_host_id(__u32 h) const
+{
+	int rc;
+
+	rc = lookup_index_by_host_id(h);
+	if (rc < 0) {
+		struct host_id_mapping new_mapping;
+		new_mapping.h	= h;
+		new_mapping.idx	= m_host_ids.size();
+		add_to_index(&new_mapping);
+		rc = new_mapping.idx;
+	}
+
+	return rc;
+}
+
+
+TotalCollapser::TotalCollapser()
+: Collapser(all)
+{
+}
+
+
+unsigned int TotalCollapser::get_index(struct hctl_ident *identifier) const
+{
+	return 0;
+}
+
+
+unsigned int TotalCollapser::get_index(__u32 device) const
+{
+	return 0;
+}
+
+
+unsigned int TotalCollapser::get_index_by_host_id(__u32 h) const
+{
+	return 0;
+}
+
+
+AggregationCollapser::AggregationCollapser(ConfigReader &cfg,
+		     Aggregator &criterion, DeviceFilter &dev_filt, int *rc)
+: Collapser(criterion)
+{
+	*rc = 0;
+	/*
+	 * setup everything so we won't ever have a miss when looking up
+	 */
+	verbose_msg("AggregationCollapser initializing\n");
+
+	switch(m_criterion) {
+	case chpid:
+		setup_by_chpid(cfg, dev_filt);
+		break;
+	case devno:
+		setup_by_devno(cfg, dev_filt);
+		break;
+	case wwpn:
+		setup_by_wwpn(cfg, dev_filt);
+		break;
+	case multipath_device:
+		*rc = setup_by_multipath(cfg, dev_filt);
+		break;
+	default:
+		assert(false);
+	}
+
+	verbose_msg("AggregationCollapser type %d constructed, mapping by:\n", m_criterion);
+	verbose_msg("    %zu host ids\n", m_host_ids.size());
+	verbose_msg("    %zu hctl devices\n", m_idents.size());
+	verbose_msg("    %zu devices\n", m_devices.size());
+}
+
+
+int AggregationCollapser::get_index_u32(list<__u32> &lst, __u32 chpid)
+{
+	int idx = 0;
+
+	for (list<__u32>::const_iterator i = lst.begin();
+	      i != lst.end(); ++i, ++idx) {
+		if (chpid == *i)
+			return idx;
+	}
+
+	return -1;
+}
+
+
+int AggregationCollapser::get_index_u64(list<__u64> &lst, __u64 chpid)
+{
+	int idx = 0;
+
+	for (list<__u64>::const_iterator i = lst.begin();
+	      i != lst.end(); ++i, ++idx) {
+		if (chpid == *i)
+			return idx;
+	}
+
+	return -1;
+}
+
+
+void AggregationCollapser::setup_by_chpid(ConfigReader &cfg,
+					  DeviceFilter &dev_filt)
+{
+	list<__u32>			mms;
+	list<struct hctl_ident>		idents;
+	list<__u32>			host_ids;
+	struct host_id_mapping		host_id_mapping;
+	struct device_mapping		dev_mapping;
+	struct ident_mapping		ide_mapping;
+	__u32				chpid;
+	int				rc = 0;
+
+	// this is our master list for collapsing
+	dev_filt.get_eligible_chpids(cfg, m_reference_values_u32);
+
+	cfg.get_unique_mms(mms);
+	for (list<__u32>::const_iterator i = mms.begin();
+	      i != mms.end(); ++i) {
+		if (!dev_filt.is_eligible_mm(*i))
+			continue;
+		dev_mapping.device = *i;
+		dev_mapping.idx = -1;
+		chpid = cfg.get_chpid_by_mm_internal(*i, &rc);
+		assert(rc == 0);
+		dev_mapping.idx = get_index_u32(m_reference_values_u32, chpid);
+		assert(dev_mapping.idx >= 0);
+		add_to_index(&dev_mapping);
+		vverbose_msg("    map mm %d to chpid %x (index %d)\n", *i,
+			     chpid, dev_mapping.idx);
+	}
+
+	cfg.get_unique_host_ids(host_ids);
+	for (list<__u32>::const_iterator i = host_ids.begin();
+	      i != host_ids.end(); ++i) {
+		if (!dev_filt.is_eligible_host_id(*i))
+			continue;
+		host_id_mapping.h = *i;
+		host_id_mapping.idx = -1;
+		chpid = cfg.get_chpid_by_host_id(*i, &rc);
+		assert(rc == 0);
+		host_id_mapping.idx = get_index_u32(m_reference_values_u32,
+						    chpid);
+		assert(host_id_mapping.idx >= 0);
+		add_to_index(&host_id_mapping);
+		vverbose_msg("    map host id %d to chpid %x (index %d)\n", *i,
+			     chpid, host_id_mapping.idx);
+	}
+
+	cfg.get_unique_devices(idents);
+	for (list<struct hctl_ident>::iterator i = idents.begin();
+	      i != idents.end(); ++i) {
+		if (!dev_filt.is_eligible_ident(&(*i)))
+			continue;
+		ide_mapping.ident = *i;
+		ide_mapping.idx = -1;
+		chpid = cfg.get_chpid_by_ident(&(*i), &rc);
+		assert(rc == 0);
+		ide_mapping.idx = get_index_u32(m_reference_values_u32, chpid);
+		assert(ide_mapping.idx >= 0);
+		add_to_index(&ide_mapping);
+		vverbose_msg("    map device [%d:%d:%d:%d] to chpid %x (index %d)\n",
+			    (*i).host, (*i).channel, (*i).target, (*i).lun,
+			    chpid, ide_mapping.idx);
+	}
+}
+
+
+void AggregationCollapser::setup_by_devno(ConfigReader &cfg,
+					  DeviceFilter &dev_filt)
+{
+	list<__u32>			mms;
+	list<struct hctl_ident>		idents;
+	list<__u32>			host_ids;
+	struct host_id_mapping		host_id_mapping;
+	struct device_mapping		dev_mapping;
+	struct ident_mapping		ide_mapping;
+	__u32				devno;
+	int				rc = 0;
+
+	/* this is our master list for collapsing
+	*/
+	dev_filt.get_eligible_devnos(cfg, m_reference_values_u32);
+
+	cfg.get_unique_mms(mms);
+	for (list<__u32>::const_iterator i = mms.begin();
+	      i != mms.end(); ++i) {
+		if (!dev_filt.is_eligible_mm(*i))
+			continue;
+		dev_mapping.device = *i;
+		dev_mapping.idx = -1;
+		devno = cfg.get_devno_by_mm_internal(*i, &rc);
+		assert(rc == 0);
+		dev_mapping.idx = get_index_u32(m_reference_values_u32, devno);
+		assert(dev_mapping.idx >= 0);
+		add_to_index(&dev_mapping);
+		vverbose_msg("    map mm %d to bus id 0.0.%x (index %d)\n", *i,
+			     devno, dev_mapping.idx);
+	}
+
+	cfg.get_unique_host_ids(host_ids);
+	for (list<__u32>::const_iterator i = host_ids.begin();
+	      i != host_ids.end(); ++i) {
+		if (!dev_filt.is_eligible_host_id(*i))
+			continue;
+		host_id_mapping.h = *i;
+		host_id_mapping.idx = -1;
+		devno = cfg.get_devno_by_host_id(*i, &rc);
+		assert(rc == 0);
+		host_id_mapping.idx = get_index_u32(m_reference_values_u32,
+						    devno);
+		assert(host_id_mapping.idx >= 0);
+		add_to_index(&host_id_mapping);
+		vverbose_msg("    map host id %d to bus id 0.0.%x"
+			     " (index %d)\n", *i, devno, host_id_mapping.idx);
+	}
+
+	cfg.get_unique_devices(idents);
+	for (list<struct hctl_ident>::iterator i = idents.begin();
+	      i != idents.end(); ++i) {
+		if (!dev_filt.is_eligible_ident(&(*i)))
+			continue;
+		ide_mapping.ident = *i;
+		ide_mapping.idx = -1;
+		devno = cfg.get_devno_by_ident(&(*i), &rc);
+		assert(rc == 0);
+		ide_mapping.idx = get_index_u32(m_reference_values_u32, devno);
+		assert(ide_mapping.idx >= 0);
+		add_to_index(&ide_mapping);
+		vverbose_msg("    map device [%d:%d:%d:%d] to bus id 0.0.%x"
+			     " (index %d)\n",
+			    (*i).host, (*i).channel, (*i).target, (*i).lun,
+			    devno, ide_mapping.idx);
+	}
+}
+
+
+void AggregationCollapser::setup_by_wwpn(ConfigReader &cfg,
+					 DeviceFilter &dev_filt)
+{
+	list<__u32>			mms;
+	list<struct hctl_ident>		idents;
+	struct device_mapping		dev_mapping;
+	struct ident_mapping		ide_mapping;
+	__u64				wwpn;
+	int				rc = 0;
+
+	// this is our master list for collapsing
+	dev_filt.get_eligible_wwpns(cfg, m_reference_values_u64);
+
+	cfg.get_unique_mms(mms);
+	for (list<__u32>::const_iterator i = mms.begin();
+	      i != mms.end(); ++i) {
+		if (!dev_filt.is_eligible_mm(*i))
+			continue;
+		dev_mapping.device = *i;
+		dev_mapping.idx = -1;
+		wwpn = cfg.get_wwpn_by_mm_internal(*i, &rc);
+		assert(rc == 0);
+		dev_mapping.idx = get_index_u64(m_reference_values_u64, wwpn);
+		assert(dev_mapping.idx >= 0);
+		add_to_index(&dev_mapping);
+		vverbose_msg("    map mm %d to wwpn %016Lx (index %d)\n", *i,
+			     (long long unsigned int)wwpn, dev_mapping.idx);
+	}
+
+	cfg.get_unique_devices(idents);
+	for (list<struct hctl_ident>::iterator i = idents.begin();
+	      i != idents.end(); ++i) {
+		if (!dev_filt.is_eligible_ident(&(*i)))
+			continue;
+		ide_mapping.ident = *i;
+		ide_mapping.idx = -1;
+		wwpn = cfg.get_wwpn_by_ident(&(*i), &rc);
+		assert(rc == 0);
+		ide_mapping.idx = get_index_u64(m_reference_values_u64, wwpn);
+		assert(ide_mapping.idx >= 0);
+		add_to_index(&ide_mapping);
+		vverbose_msg("    map device [%d:%d:%d:%d] to wwpn %016Lx"
+			     " (index %d)\n",
+			    (*i).host, (*i).channel, (*i).target, (*i).lun,
+			    (long long unsigned int)wwpn, ide_mapping.idx);
+	}
+}
+
+
+int AggregationCollapser::setup_by_multipath(ConfigReader &cfg,
+					     DeviceFilter &dev_filt)
+{
+	list<__u32>			mms;
+	list<struct hctl_ident>		idents;
+	struct device_mapping		dev_mapping;
+	struct ident_mapping		ide_mapping;
+	__u32			        mp_mm;
+	int				rc = 0, grc = 0;
+
+	// this is our master list for collapsing
+	dev_filt.get_eligible_mp_mms(cfg, m_reference_values_u32);
+
+	if (m_reference_values_u32.size() == 0) {
+		fprintf(stderr, "%s: No multipath devices in configuration"
+			" found. Aggregation by multipath devices not"
+			" possible with this data.\n", toolname);
+		return -1;
+	}
+
+	cfg.get_unique_mms(mms);
+	for (list<__u32>::const_iterator i = mms.begin();
+	      i != mms.end(); ++i) {
+		if (!dev_filt.is_eligible_mm(*i))
+			continue;
+		dev_mapping.device = *i;
+		dev_mapping.idx = -1;
+		mp_mm = cfg.get_mp_mm_by_mm_internal(*i, &rc);
+		if (mp_mm == 0) {
+			fprintf(stderr, "%s: Device %s is not in a multipath "
+				"group. Please remove via command line options "
+				"and try again.\n", toolname, cfg.get_dev_by_mm_internal(*i, &rc));
+			grc = -1;
+			continue;
+		}
+		dev_mapping.idx = get_index_u32(m_reference_values_u32, mp_mm);
+		assert(dev_mapping.idx >= 0);
+		add_to_index(&dev_mapping);
+		vverbose_msg("    map mm %d to mp_mm %x (index %d)\n", *i,
+			     mp_mm, dev_mapping.idx);
+	}
+	if (grc)
+		return grc;
+
+	cfg.get_unique_devices(idents);
+	for (list<struct hctl_ident>::iterator i = idents.begin();
+	      i != idents.end(); ++i) {
+		if (!dev_filt.is_eligible_ident(&(*i)))
+			continue;
+		ide_mapping.ident = *i;
+		ide_mapping.idx = -1;
+		mp_mm = cfg.get_mp_mm_by_ident(&(*i), &rc);
+		assert(rc == 0);
+		ide_mapping.idx = get_index_u32(m_reference_values_u32, mp_mm);
+		assert(ide_mapping.idx >= 0);
+		add_to_index(&ide_mapping);
+		vverbose_msg("    map device [%d:%d:%d:%d] to mp_mm %x"
+			     " (index %d)\n",
+			     (*i).host, (*i).channel, (*i).target, (*i).lun,
+			     mp_mm, ide_mapping.idx);
+	}
+
+	return grc;
+}
+
+
+unsigned int AggregationCollapser::get_index(struct hctl_ident *identifier) const
+{
+	int rc = lookup_index(identifier);
+
+	assert(rc >= 0);
+
+	return rc;
+}
+
+
+unsigned int AggregationCollapser::get_index(__u32 device) const
+{
+	int rc = lookup_index(device);
+
+	assert(rc >= 0);
+
+	return rc;
+}
+
+
+unsigned int AggregationCollapser::get_index_by_host_id(__u32 h) const
+{
+	int rc;
+
+	// since a host_id can host multiple wwpns and multipath devices,
+	// no mapping is possible in these cases.
+	assert(m_criterion != wwpn);
+	assert(m_criterion != multipath_device);
+
+	rc = lookup_index_by_host_id(h);
+
+	assert(rc >= 0);
+
+	return rc;
+}
+
+
+const list<__u32>& AggregationCollapser::get_reference_chpids() const
+{
+	return m_reference_values_u32;
+}
+
+
+const list<__u64>& AggregationCollapser::get_reference_wwpns() const
+{
+	return m_reference_values_u64;
+}
+
+
+const list<__u32>& AggregationCollapser::get_reference_devnos() const
+{
+	return m_reference_values_u32;
+}
+
+
+const list<__u32>& AggregationCollapser::get_reference_mp_mms() const
+{
+	return m_reference_values_u32;
+}
+
+
+
Index: s390-tools-sles11/ziomon/ziorep_collapser.hpp
===================================================================
--- /dev/null
+++ s390-tools-sles11/ziomon/ziorep_collapser.hpp
@@ -0,0 +1,185 @@
+/*
+ * FCP report generators
+ *
+ * Utility classes to get indices for collapsing data by
+ * variable criteria
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Stefan Raspl <raspl@linux.vnet.ibm.com>
+ */
+
+#ifndef ZIOMON_COLLAPSER
+#define ZIOMON_COLLAPSER
+
+#include <list>
+
+#include <linux/types.h>
+
+#include "ziorep_cfgreader.hpp"
+#include "ziorep_filters.hpp"
+
+using std::list;
+
+
+enum Aggregator {
+	none,
+	chpid,
+	devno,
+	wwpn,
+	multipath_device,
+	all
+};
+
+
+/**
+ * A collapser is basically a mapper that maps a given device to an index.
+ * The interesting part is the AggregationCollapser, which maps devices that
+ * share a certain property to the same index.
+ * NOTE: There is no guarantee that all indices will be used! E.g. the first
+ * index received might be 0, but the next one could be 5. In that case, it is
+ * the caller's responsibility to make sure that indices 1 to 4 are handled
+ * appropriately.
+ */
+class Collapser {
+public:
+	Collapser(Aggregator criterion);
+	virtual ~Collapser();
+
+	/// get index by device identifier
+	virtual unsigned int get_index(struct hctl_ident *identifier) const = 0;
+
+	/// get index by major/minor
+	virtual unsigned int get_index(__u32 device) const = 0;
+
+	/// get index by host_id
+	virtual unsigned int get_index_by_host_id(__u32 h) const = 0;
+
+	Aggregator get_criterion() const;
+
+protected:
+	struct host_id_mapping {
+		__u32		h;
+		int		idx;
+	};
+	struct device_mapping {
+		__u32		device;
+		int		idx;
+	};
+	struct ident_mapping {
+		struct hctl_ident	ident;
+		int			idx;
+	};
+	/// Lookup list for matching a host id to an index, sorted ascending
+	mutable list<struct host_id_mapping>	m_host_ids;
+
+	/// Lookup list for matching a device to an index, sorted ascending
+	mutable list<struct device_mapping>	m_devices;
+
+	/// Lookup list for matching an identifier to an index, sorted ascending
+	mutable list<struct ident_mapping>	m_idents;
+
+	/// add entry, skips duplicates.
+	void add_to_index(struct ident_mapping *new_mapping) const;
+
+	/// add entry, skips duplicates.
+	void add_to_index(struct device_mapping *new_mapping) const;
+
+	/// add entry, skips duplicates.
+	void add_to_index(struct host_id_mapping *new_mapping) const;
+
+	/// search for device, returns <0 if not found
+	int lookup_index(struct hctl_ident *identifier) const;
+
+	/// search for device, returns <0 if not found
+	int lookup_index(__u32 device) const;
+
+	/// search for device, returns <0 if not found
+	int lookup_index_by_host_id(__u32 h) const;
+
+	Aggregator			m_criterion;
+};
+
+/**
+ * Collapser that doesn't do any collapsing (ooops) - it merely assigns
+ * an individual index to each device.
+ */
+class NoopCollapser : public Collapser {
+public:
+	NoopCollapser();
+
+	virtual unsigned int get_index(struct hctl_ident *identifier) const;
+
+	virtual unsigned int get_index(__u32 device) const;
+
+	virtual unsigned int get_index_by_host_id(__u32 h) const;
+};
+
+
+/**
+ * Collapser that collapses everything into a single element. That is,
+ * all devices will be mapped to the same index of 0.
+ */
+class TotalCollapser : public Collapser {
+public:
+	TotalCollapser();
+
+	virtual unsigned int get_index(struct hctl_ident *identifier) const;
+
+	virtual unsigned int get_index(__u32 device) const;
+
+	virtual unsigned int get_index_by_host_id(__u32 h) const;
+};
+
+
+/**
+ * Collapser that collapses by a given criterion. For instance, if the
+ * criterion used is wwpn, then all devices that have the same wwpn will
+ * be matched to the same index.
+ */
+class AggregationCollapser : public Collapser {
+public:
+	/**
+	 * 'filt' must hold all available devices */
+	AggregationCollapser(ConfigReader &cfg,
+			     Aggregator &criterion,
+			     DeviceFilter &dev_filt,
+			     int *rc);
+
+	virtual unsigned int get_index(struct hctl_ident *identifier) const;
+	virtual unsigned int get_index(__u32 device) const;
+	virtual unsigned int get_index_by_host_id(__u32 h) const;
+
+	/// Reference chpids as used for collapsing.
+	const list<__u32>& get_reference_chpids() const;
+
+	/// Reference wwpns as used for collapsing.
+	const list<__u64>& get_reference_wwpns() const;
+
+	/// Reference devnos as used for collapsing.
+	const list<__u32>& get_reference_devnos() const;
+
+	/// Reference multipathes as used for collapsing.
+	const list<__u32>& get_reference_mp_mms() const;
+
+private:
+	/** list of all unique __u32 values of the criterion we were
+	  * collapsing by. */
+	list<__u32>		m_reference_values_u32;
+	list<__u64>		m_reference_values_u64;
+
+	int get_index_u32(list<__u32> &lst, __u32 chpid);
+	int get_index_u64(list<__u64> &lst, __u64 chpid);
+
+	void setup_by_chpid(ConfigReader &cfg, DeviceFilter &dev_filt);
+	void setup_by_devno(ConfigReader &cfg, DeviceFilter &dev_filt);
+	void setup_by_wwpn(ConfigReader &cfg, DeviceFilter &dev_filt);
+	int setup_by_multipath(ConfigReader &cfg, DeviceFilter &dev_filt);
+};
+
+
+
+#endif
+
+
+
+
Index: s390-tools-sles11/ziomon/ziorep_config
===================================================================
--- /dev/null
+++ s390-tools-sles11/ziomon/ziorep_config
@@ -0,0 +1,492 @@
+#!/usr/bin/perl
+
+#
+# FCP report generators
+#
+# Script to list the FCP, SCSI and multipath configuration
+#
+# Copyright IBM Corp. 2008.
+# Author(s): Swen Schillig    <swen@vnet.ibm.com>
+#
+
+use strict;
+use warnings;
+use English;
+use Getopt::Long;
+use File::Spec::Functions;
+use File::Temp qw/ tempdir /;
+use Data::Dumper;
+
+use constant S_DIR1 => "/sys/class/fc_host";
+use constant S_DIR2 => "/sys/devices/css0"; #/<sub_ch>/<device>
+use constant S_DIR3 => "/sys/class/scsi_device";
+use constant S_DIR4 => "/sys/class/fc_remote_ports";
+
+our $base_dir = "/";
+our %sub_ch;
+our %devices;
+our %mapper_dev;
+our %rports;
+our $c_src;
+
+sub get_line
+{
+	return "n/a" unless open(FH, (catfile($c_src, @_))[0]);
+	my $line = readline(FH);
+	$line =~ s/\s*\Z// unless !$line;
+	close(FH);
+	return $line;
+}
+
+sub dir_content
+{
+	my @temp_dir;
+	opendir(DH, shift()) or return 0;
+	@temp_dir = readdir(DH);
+	closedir(DH);
+	return @temp_dir;
+}
+
+sub glob_last
+{
+	return (glob(catfile($c_src, @_)))[-1];
+}
+
+sub mm_to_32
+{
+	return "0" unless ($_[0] and $_[0] =~ /\d+:\d+/);
+	my @t_arr = split(':',shift);
+	return ($t_arr[0] << 20) + $t_arr[1];
+}
+
+sub get_sub_ch_data
+{
+	my @entries = grep { /^host\d+\Z/ }
+			dir_content(catdir($base_dir, S_DIR1));
+
+	foreach my $host (@entries) {
+		$c_src = catdir($base_dir, S_DIR1, $host);
+		my $sub_ch;
+		my $adapter;
+		if (-l $c_src) {
+			$sub_ch = (split("/", readlink($c_src)))[-5];
+			$adapter = (split("/", readlink($c_src)))[-4];
+		} else {
+			$sub_ch = (split("/", readlink($c_src."/device")))[-3];
+			$adapter = (split("/", readlink($c_src."/device")))[-2];
+		}
+		$sub_ch{$adapter}{sub_ch} = $sub_ch;
+		$sub_ch{$adapter}{host}  = $host;
+		$sub_ch{$adapter}{speed} = get_line("speed");
+		$sub_ch{$adapter}{name}  = get_line("port_name");
+		$sub_ch{$adapter}{pname} = get_line("permanent_port_name");
+		$sub_ch{$adapter}{type}  = get_line("port_type");
+		$c_src = catdir($base_dir, S_DIR2, $sub_ch, $adapter);
+		$sub_ch{$adapter}{lic}   = get_line("lic_version");
+		$sub_ch{$adapter}{gen}   = get_line("card_version");
+		$sub_ch{$adapter}{state} = get_line("online") == 1 ? "Online" :
+								     "Offline";
+		$c_src = catdir($base_dir, S_DIR2, $sub_ch);
+		$sub_ch{$adapter}{chpid} = substr(get_line("chpids"), 0, 2);
+	}
+}
+
+sub get_device_data
+{
+	my @entries = grep { /^\d+:\d+:\d+:\d+\Z/ }
+			dir_content(catdir($base_dir, S_DIR3));
+
+	foreach my $hctl (@entries) {
+		$c_src = catdir($base_dir, S_DIR3, $hctl, "device");
+		my $type =  get_line("type");
+		$devices{$hctl}{hba_id} = get_line("hba_id");
+		$devices{$hctl}{model}  = get_line("model");
+		$devices{$hctl}{vendor} = get_line("vendor");
+		$devices{$hctl}{lun}    = get_line("fcp_lun");
+		$devices{$hctl}{wwpn}   = get_line("wwpn");
+		$devices{$hctl}{sg} = (-d catdir($c_src, "scsi_generic")) ?
+					glob_last(catdir("scsi_generic/*")) :
+					glob_last("scsi_generic\:*");
+
+		$devices{$hctl}{sg}     =~ s/.*(sg\d+)\Z/$1/
+					unless(!$devices{$hctl}{sg});
+		$devices{$hctl}{type}   = "type_".$type; #preset only
+		$devices{$hctl}{mm}     = "0:0"; #preset only
+		$devices{$hctl}{sg_mm}  = "0:0"; #preset only
+		$devices{$hctl}{dev}    = undef; #preset only
+		if ($type == "0") {
+			$devices{$hctl}{type} = "Disk";
+			if (-d catdir($c_src, "block")) {
+				$devices{$hctl}{dev}  = glob_last("block/sd*");
+			} else {
+				$devices{$hctl}{dev}  = glob_last("block\:sd*");
+			}
+			$c_src = $devices{$hctl}{dev};
+			$devices{$hctl}{dev}  =~ s/.*(sd[a-z0-9]+)\Z/$1/
+						unless(!$devices{$hctl}{dev});
+			$devices{$hctl}{mm}   = get_line("dev");
+			$devices{$hctl}{sg_mm}   = get_line("generic", "dev");
+			$devices{$hctl}{mp_dev} = glob_last("holders", "dm*");
+			$devices{$hctl}{mp_dev} =~ s/.*\/(.+)\Z/$1/
+						unless (!$devices{$hctl}{mp_dev});
+			$c_src = catdir($base_dir, "sys", "block",
+					$devices{$hctl}{mp_dev});
+			$devices{$hctl}{mp_dev_mm} = get_line("dev");
+		} elsif ($type == "1") {
+			$devices{$hctl}{type} = "Tape";
+			$devices{$hctl}{mm} = get_line("tape", "dev");
+			$devices{$hctl}{sg_mm} = get_line("generic", "dev");
+			$devices{$hctl}{dev} = (-d catdir($c_src, "scsi_tape")) ?
+						(grep { /st\d+\Z/ }
+                                                glob(catdir($c_src, "scsi_tape/st*")))[0] :
+						(grep { /\:st\d+\Z/ }
+						glob(catdir($c_src, "scsi_tape:st*")))[0];
+			$devices{$hctl}{dev} =~ s/.*(st\d+)\Z/$1/
+						unless(!$devices{$hctl}{dev});
+		}
+	}
+}
+
+sub get_mapper_devices
+{
+	$c_src = catdir($base_dir, "dev", "mapper");
+	my @entries = grep { ! /^\./ } dir_content($c_src);
+
+	foreach my $dev (@entries) {
+		next if ("control" =~ /$dev/);
+		if ($base_dir !~ /^\/\Z/) {
+			$mapper_dev{get_line("$dev")} = $dev;
+			next;
+		}
+		my $tf = catfile($c_src, "$dev");
+		my $mm = `stat -c%t:%T $tf`;
+		chomp($mm);
+		$mm = join(":", map { hex($_) } split(":", $mm));
+		$mapper_dev{$mm} = $dev;
+	}
+}
+
+sub get_rport_data
+{
+	my @entries = grep { ! /^\./ } dir_content(catdir($base_dir, S_DIR4));
+
+	foreach my $rport (@entries) {
+		$c_src = catdir($base_dir, S_DIR4, "$rport");
+		$rports{$rport}{wwpn} = get_line("port_name");
+		$rports{$rport}{did} = get_line("port_id");
+		$rports{$rport}{sup_class} = get_line("supported_classes");
+		$rports{$rport}{scsi_target} = get_line("scsi_target_id");
+		$rports{$rport}{dl_tmo} = get_line("dev_loss_tmo");
+		$rports{$rport}{state} = get_line("port_state");
+		$rports{$rport}{hba_id} = (split('/', get_line("uevent")))[-1];
+	}
+}
+
+sub mapping_table
+{
+	foreach my $line (sort keys %devices) {
+		my @out_str;
+		my $mp_dev_mm = $devices{$line}{mp_dev_mm};
+
+		push @out_str, $sub_ch{$devices{$line}{hba_id}}{chpid};
+		push @out_str, $line;
+		push @out_str, "$sub_ch{$devices{$line}{hba_id}}{sub_ch}:".
+			       "$devices{$line}{hba_id}:$devices{$line}{wwpn}:".
+				$devices{$line}{lun};
+		if ($mp_dev_mm && defined $mapper_dev{$mp_dev_mm}) {
+			push @out_str, "/dev/mapper/$mapper_dev{$mp_dev_mm}";
+			push @out_str, mm_to_32($devices{$line}{mp_dev_mm});
+			push @out_str, "$devices{$line}{mp_dev_mm}";
+		} else {
+			push @out_str, "n/a";
+			push @out_str, "0";
+			push @out_str, "0:0";
+		}
+		if ($devices{$line}{type} eq "Tape") {
+			push @out_str, $devices{$line}{dev} ? "/dev/$devices{$line}{sg}" : "n/a" ;
+			push @out_str, mm_to_32($devices{$line}{sg_mm});
+			push @out_str, $devices{$line}{sg_mm};
+		} else {
+			push @out_str, $devices{$line}{dev} ? "/dev/$devices{$line}{dev}" : "n/a" ;
+			push @out_str, mm_to_32($devices{$line}{mm});
+			push @out_str, $devices{$line}{mm};
+		}
+		push @out_str, $devices{$line}{type};
+		print "@out_str\n";
+	}
+}
+
+sub adapter_report
+{
+	my @adapters = @_;
+
+	foreach my $a (sort keys %sub_ch) {
+		next if (@adapters && "@adapters" !~ /\b$a\b/);
+		my @out_str;
+		push @out_str, "Host:    $sub_ch{$a}{host}\n";
+		push @out_str, "CHPID:   $sub_ch{$a}{chpid}\n";
+		push @out_str, "Adapter: $a\n";
+		push @out_str, "Sub-Ch.: $sub_ch{$a}{sub_ch}\n";
+		push @out_str, "Name:    $sub_ch{$a}{name}\n";
+		push @out_str, "P-Name:  $sub_ch{$a}{pname}\n";
+		push @out_str, "Version: $sub_ch{$a}{gen}\n";
+		push @out_str, "LIC:     $sub_ch{$a}{lic}\n";
+		push @out_str, "Type:    $sub_ch{$a}{type}\n";
+		push @out_str, "Speed:   $sub_ch{$a}{speed}\n";
+		push @out_str, "State:   $sub_ch{$a}{state}\n";
+		print @out_str, "\n";
+	}
+}
+
+sub device_report
+{
+	my $adapters = shift;
+	my $ports = shift;
+	my $luns = shift;
+	my $s_devs = shift;
+	my $hosts = shift;
+
+	if ($_[0]) {
+		print "adapter  remote_port        LUN                SCSI  ",
+		      "gen_dev  scsi_dev MM  type model vendor H:C:T:L\n",
+		      "======================================================",
+		      "===============================================\n";
+	}
+	foreach my $hctl (sort keys %devices) {
+		next if (@$adapters && "@$adapters" !~ /\b$devices{$hctl}{hba_id}\b/);
+		next if (@$ports && "@$ports" !~ /\b$devices{$hctl}{wwpn}\b/);
+		next if (@$luns && "@$luns" !~ /\b$devices{$hctl}{lun}\b/);
+		next if (@$s_devs && "@$s_devs" !~ /\b$devices{$hctl}{dev}\b/);
+		next if (@$hosts && "@$hosts" !~ /\b$sub_ch{$devices{$hctl}{hba_id}}{host}\b/);
+		my @out_str;
+		push @out_str, $devices{$hctl}{hba_id};
+		push @out_str, $devices{$hctl}{wwpn};
+		push @out_str, $devices{$hctl}{lun};
+		push @out_str, $sub_ch{$devices{$hctl}{hba_id}}{host};
+		push @out_str, "/dev/".$devices{$hctl}{sg};
+		push @out_str, $devices{$hctl}{dev} ?
+					"/dev/".$devices{$hctl}{dev}: "NO_SCSI";
+		push @out_str, $devices{$hctl}{mm};
+		push @out_str, $devices{$hctl}{type};
+		push @out_str, $devices{$hctl}{model};
+		push @out_str, $devices{$hctl}{vendor};
+		push @out_str, $hctl;
+
+		print "@out_str\n";
+	}
+}
+
+sub mapper_report
+{
+	my $adapters = shift;
+	my $ports = shift;
+	my $s_devs = shift;
+	my $m_devs = shift;
+	my @out_str;
+
+	if (! (keys %mapper_dev)) {
+		print "No device mapper devices configured.\n";
+		return;
+	}
+	if ($_[0]) {
+		print "adapter  remote_port        scsi_dev multipath_device\n",
+		      "=====================================================\n";
+	}
+	foreach my $hctl (sort keys %devices) {
+		next if (! $devices{$hctl}{mp_dev_mm});
+		next if (@$adapters && "@$adapters" !~ /\b$devices{$hctl}{hba_id}\b/);
+		next if (@$ports && "@$ports" !~ /\b$devices{$hctl}{wwpn}\b/);
+		next if (@$s_devs && "@$s_devs" !~ /\b$devices{$hctl}{dev}\b/);
+		next if (@$m_devs && "@$m_devs" !~ /\b$mapper_dev{$devices{$hctl}{mp_dev_mm}}\b/);
+		my @line_str;
+		push @line_str, $devices{$hctl}{hba_id};
+		push @line_str, $devices{$hctl}{wwpn};
+		push @line_str, "/dev/".$devices{$hctl}{dev};
+		push @line_str, "/dev/mapper/".$mapper_dev{$devices{$hctl}{mp_dev_mm}};
+		push @out_str, "@line_str\n";
+	}
+	@out_str = sort {
+			my @a_fields = split('\s+', $a);
+			my @b_fields = split('\s+', $b);
+
+			$a_fields[3] cmp $b_fields[3]
+				||
+			$a_fields[1] cmp $b_fields[1]
+				||
+			$a_fields[0] cmp $b_fields[0]
+		} @out_str;
+	print @out_str;
+}
+
+sub usage
+{
+    print <<MSG
+Usage: $PROGRAM_NAME [-h] [-v] [-V] [-t <num>] [-i <source>] [-a <adapter>]
+                     [-p <port>] [-l <lun>] [-s <host>] [-d <dev>] [-m <dev>]
+                     [-A] [-D] [-M] [-I]
+
+$PROGRAM_NAME provides information of the multipath, SCSI and FCP configuration
+
+The default is to print the adapter configuration report.
+
+Options:
+
+        -h, --help
+            print this help text.
+
+        -v, --version
+            print version information.
+
+        -t, --topline
+            print a header for column description
+            the default is to print no header which is useful if the results
+            are imported by another application.
+            e.g. $PROGRAM_NAME -D -t
+
+        -i, --input
+            specify a directory or configuration file as source
+            instead of working on the current live-system.
+
+        -a, --adapter
+            limit the output to the list of adapters specified
+            e.g. $PROGRAM_NAME -a 0.0.3c07,0.0.3d07
+
+        -p, --port
+            limit the output to the list of remote-ports specified
+            e.g. $PROGRAM_NAME -D -p 0x5005123456789000,0x5005123456789001
+
+        -l, --lun
+            limit the output to the list of LUNs specified
+            e.g. $PROGRAM_NAME -D -l 0x401040a600000000 -l 0x401040a700000000
+
+        -s, --scsi
+            limit the output to the list SCSI hosts specified
+            e.g. $PROGRAM_NAME -D --scsi host0,host1 -s host5
+
+        -d, --device
+            limit the output to the list of SCSI devices specified
+            e.g. $PROGRAM_NAME -D --device sda,sdb -d sde
+
+        -m, --mdev
+            limit the output to the list of multipath devices specified
+            e.g. $PROGRAM_NAME -M -m 36005076303ffc56200000000000010a6
+
+        -A, --Adapter
+            print the adapter report, this is the default.
+
+        -D, --Device
+            print the device report
+
+        -M, --Map
+            print the multipath, mapper report
+
+        -I, --Internal
+            prints an assembly of data used as input for a variety of reports
+MSG
+}
+
+sub version {
+	print "$PROGRAM_NAME: version %S390_TOOLS_VERSION%\n";
+	print "Copyright IBM Corp. 2008\n";
+}
+
+sub unpack_config
+{
+	my $td = tempdir( CLEANUP => 1 );
+
+	print STDERR "Unpacking configuration ...";
+	system("tar xzf $base_dir -C $td 2>/dev/null");
+	print STDERR "Done.\n";
+
+	$base_dir = $td;
+}
+
+sub verify_and_init
+{
+	# only brief checks if there's something at all #
+	return -1 if (! (-e catdir($base_dir, S_DIR1) &&
+			 -e catdir($base_dir, S_DIR2) &&
+			 -e catdir($base_dir, S_DIR3) &&
+			 -e catdir($base_dir, S_DIR4)));
+
+	get_sub_ch_data($base_dir);
+	get_device_data($base_dir);
+	get_mapper_devices($base_dir);
+	#get_rport_data($base_dir);
+
+	return 0;
+}
+
+##################### main #######################
+my @adapter;
+my @port;
+my @lun;
+my @host;
+my @s_dev;
+my @m_dev;
+my $int_map_list;
+my $adapter_rep;
+my $device_rep;
+my $map_rep;
+my $header = 0;
+
+Getopt::Long::Configure(qw/bundling no_ignore_case/);
+GetOptions('i|input=s' => \$base_dir,
+           'a|adapter=s' => \@adapter,
+           't|topline' => \$header,
+           'h|help' => sub { usage(); exit 0; },
+           'v|version' => sub { version(); exit 0; },
+           'p|port=s' => \@port,
+           'l|lun=s' => \@lun,
+           's|scsi=s' => \@host,
+           'd|device=s' => \@s_dev,
+           'm|mdev=s' => \@m_dev,
+           'I|Internal' => \$int_map_list,
+           'A|Adapter' => \$adapter_rep,
+           'D|Device' => \$device_rep,
+           'M|Map' => \$map_rep,
+          ) or do {
+	print "Invalid usage !\n";
+	usage();
+	exit 1;
+};
+
+if (scalar(@ARGV)) {
+	print "$PROGRAM_NAME: extra operand '@ARGV'\n";
+	print "Try '$PROGRAM_NAME --help' for more information.\n";
+	exit 2;
+}
+
+if (-f $base_dir) {
+	unpack_config();
+}
+
+if (verify_and_init() != 0) {
+	print "No valid configuration found.\nTerminating\n\n";
+	exit 4;
+}
+
+@adapter = split(',', join(',', @adapter));
+@port = split(',', join(',', @port));
+@lun = split(',', join(',', @lun));
+@host = split(',', join(',', @host));
+@s_dev = split(',', join(',', @s_dev));
+@m_dev = split(',', join(',', @m_dev));
+
+if ($int_map_list) {
+	mapping_table();
+	exit 0;
+}
+
+if ($adapter_rep || !($device_rep || $map_rep)) {
+	adapter_report(@adapter);
+}
+
+if ($device_rep) {
+	device_report(\@adapter, \@port, \@lun, \@s_dev, \@host, $header);
+}
+
+if ($map_rep) {
+	mapper_report(\@adapter, \@port, \@s_dev, \@m_dev, $header);
+}
+
Index: s390-tools-sles11/ziomon/ziorep_config.8
===================================================================
--- /dev/null
+++ s390-tools-sles11/ziomon/ziorep_config.8
@@ -0,0 +1,118 @@
+.TH ZIOREP_CONFIG 8 "Oct 2008" "s390-tools"
+
+.SH NAME
+ziorep_config \- Configuration report for the ziomon framework.
+
+.SH SYNOPSIS
+.B ziorep_config
+[<options>]
+
+.SH DESCRIPTION
+.B ziorep_config
+The ziorep_config report is a multi purpose utility serving a variety of requirements.
+The purpose of this report is to visualize the SCSI-, FCP- and multipath-configuration of the entire system.
+Depending on the current point of interest this information is delivered from different angles,
+controlled by command-line switches and described in more detail in the example section.
+The report is not only usable on a pre-processed configuration file or configuration directory-tree
+but also on the live system. All parameters have to specified in their full extent,
+``short-versions'' (e.g. 3c07 as subchannel-ID) are not allowed.
+Hexadecimal values have to be specified with a leading ``0x'' and have to be lowercase always.
+In addition all WWPNs and LUNs have to be specified as a 16-digit hex-value, leading and trailing zeros
+are vital and have to be part of the number.
+
+.SH OPTIONS
+.TP
+.BR "\-h" " or " "\-\-help"
+Print help information, then exit.
+
+.TP
+.BR "\-v" " or " "\-\-version"
+Print version information, then exit.
+
+.TP
+.BR "\-t" " or " "\-\-topline"
+print a header for column description
+the default is to print no header which is useful if the results
+are imported by another application.
+.br
+e.g. ziorep_config -D -t
+
+.TP
+.BR "\-i" " or " "\-\-input" " <src-file | src-dir>"
+specify a directory or configuration file as source
+instead of working on the current live-system.
+.br
+e.g. ziorep_config -i config.cfg
+
+.TP
+.BR "\-a" " or " "\-\-adapter" " <BUS-ID>"
+limit the output to the list of adapters specified
+.br
+e.g. ziorep_config -a 0.0.3c07 -a 0.0.3d07
+
+.TP
+.BR "\-p" " or " "\-\-port" " <port-WWPN>"
+limit the output to the list of remote-ports specified
+.br
+e.g. ziorep_config -D -p 0x5005123456789000 -p 0x5005123456789001
+
+.TP
+.BR "\-l" " or " "\-\-lun" " <LUN>"
+limit the output to the list of LUNs specified
+.br
+e.g. ziorep_config -D -l 0x401040a600000000 -l 0x401040a700000000
+
+.TP
+.BR "\-s" " or " "\-\-scsi" " <shost>"
+limit the output to the list SCSI hosts specified
+.br
+e.g. ziorep_config -D --scsi host0 -s host5
+
+.TP
+.BR "\-d" " or " "\-\-device" " <sdev>"
+limit the output to the list of SCSI devices specified
+.br
+e.g. ziorep_config -D --device sda -d sde
+
+.TP
+.BR "\-m" " or " "\-\-mdev" " <mdev>"
+limit the output to the list of multipath devices specified
+.br
+e.g. ziorep_config -M -m 36005076303ffc56200000000000010a6
+
+.TP
+.BR "\-A" " or " "\-\-Adapter"
+print the adapter report, this is the default.
+
+.TP
+.BR "\-D" " or " "\-\-Device"
+print the device report
+
+.TP
+.BR "\-M" " or " "\-\-Map"
+print the multipath, mapper report
+
+.TP
+.BR "\-I" " or " "\-\-Internal"
+prints an assembly of data used as input for a variety of reports
+
+
+.SH EXAMPLES
+The following example shows how to trigger the ``device report'', limiting the output to
+the two adapters 0.0.3c07 and 0.0.3d07 with an enabled ``topline'' (table header).
+
+ziorep_config -D -t -a 0.0.3c07 -a 0.0.3d07
+
+The limiting parameters (-a, -p, -l, -d, -s, -m) can be used in two ways.
+First to provide the values as a comma separated list or by specifying the switch multiple times.
+A combination of both is valid as well.
+To trigger the "mapper report" and limiting the output to the SCSI devices sda, sde and sdf
+the command-line would look like this
+
+ziorep_config -M -d sda -d sde -d sdf
+
+.SH "SEE ALSO"
+.BR ziomon (8),
+.BR ziomon_util (8),
+.BR ziomon_zfcpdd (8)
+.BR ziomon_fcpconf (8)
Index: s390-tools-sles11/ziomon/ziorep_filters.cpp
===================================================================
--- /dev/null
+++ s390-tools-sles11/ziomon/ziorep_filters.cpp
@@ -0,0 +1,516 @@
+/*
+ * FCP report generators
+ *
+ * Classes implementing various filters
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Stefan Raspl <raspl@linux.vnet.ibm.com>
+ */
+
+#include <assert.h>
+#include <unistd.h>
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "ziorep_filters.hpp"
+
+extern "C" {
+#include "ziomon_msg_tools.h"
+}
+
+extern const char *toolname;
+extern int verbose;
+
+
+
+MsgFilter::MsgFilter()
+{
+}
+
+
+MsgFilter::~MsgFilter()
+{
+}
+
+
+MsgTypeFilter::MsgTypeFilter()
+{
+}
+
+
+MsgTypeFilter::~MsgTypeFilter()
+{
+}
+
+
+void MsgTypeFilter::add_type(__u32 type)
+{
+	m_types.insert(type);
+}
+
+
+bool MsgTypeFilter::is_eligible(const struct message_preview *msg) const
+{
+	return is_eligible(msg->type);
+}
+
+
+bool MsgTypeFilter::is_eligible(const struct message *msg) const
+{
+	return is_eligible(msg->type);
+}
+
+
+bool MsgTypeFilter::is_eligible(__u32 type) const
+{
+	return (m_types.find(type) != m_types.end());
+}
+
+
+MsgTimeFilter::MsgTimeFilter(__u64 begin, __u64 end) :
+m_begin(begin), m_end(end)
+{
+	assert(m_end >= m_begin);
+}
+
+
+MsgTimeFilter::~MsgTimeFilter()
+{
+}
+
+
+bool MsgTimeFilter::is_eligible(__u64 timestamp) const
+{
+	return (timestamp >= m_begin && timestamp <= m_end);
+}
+
+
+__u64 MsgTimeFilter::get_begin_time() const
+{
+	return m_begin;
+}
+
+
+__u64 MsgTimeFilter::get_end_time() const
+{
+	return m_end;
+}
+
+
+bool MsgTimeFilter::is_eligible(const struct message_preview *msg) const
+{
+	return is_eligible(msg->timestamp);
+}
+
+
+bool MsgTimeFilter::is_eligible(const struct message *msg) const
+{
+	return is_eligible(get_timestamp_from_msg(msg));
+}
+
+
+void DeviceFilter::add_device(__u32 device, const struct hctl_ident *id) {
+	add_device(device);
+	add_device(id);
+	add_host(id->host);
+}
+
+
+void DeviceFilter::add_device(__u32 device)
+{
+	list<__u32>::iterator i;
+
+	for (i = m_devices.begin(); i != m_devices.end() && device > *i; ++i);
+
+	if (m_devices.size() == 0 || *i != device)
+		m_devices.insert(i, device);
+}
+
+
+void DeviceFilter::add_device(const struct hctl_ident *id)
+{
+	list<struct hctl_ident>::iterator i;
+
+	for (i = m_idents.begin(); i != m_idents.end()
+	      && compare_hctl_idents(id, &(*i)) > 0; ++i);
+
+	if (m_idents.size() == 0 || i == m_idents.end()
+	    || compare_hctl_idents(&(*i), id) != 0)
+		m_idents.insert(i, *id);
+}
+
+
+void DeviceFilter::add_host(__u32 host)
+{
+	list<__u32>::iterator i;
+
+	for (i = m_host_ids.begin(); i != m_host_ids.end() && host > *i; ++i);
+
+	if (m_host_ids.size() == 0 || i == m_host_ids.end() || *i != host)
+		m_host_ids.insert(i, host);
+}
+
+
+const list<__u32>& DeviceFilter::get_host_id_list() const
+{
+	return m_host_ids;
+}
+
+
+const list<__u32>& DeviceFilter::get_mm_list() const
+{
+	return m_devices;
+}
+
+
+bool DeviceFilter::is_eligible(const struct message *msg,
+				  const struct file_header *hdr) const
+{
+	if (msg->type == hdr->msgid_blkiomon)
+		return is_eligible_mm(((struct blkiomon_stat *)msg->data)->device);
+
+	assert(msg->type == hdr->msgid_zfcpdd);
+
+	return is_eligible_mm(((struct zfcpdd_dstat *)msg->data)->device);
+}
+
+
+bool DeviceFilter::is_eligible(const struct adapter_utilization *res) const
+{
+	return is_eligible_host_id(res->adapter_no);
+}
+
+
+bool DeviceFilter::is_eligible(const struct ioerr_cnt *cnt) const
+{
+	return is_eligible_ident(&cnt->identifier);
+}
+
+
+bool DeviceFilter::is_eligible_mm(__u32 mm) const
+{
+	for (list<__u32>::const_iterator i = m_devices.begin();
+	      i != m_devices.end() && mm >= *i; ++i) {
+		if (mm == *i)
+			return true;
+	}
+
+	return false;
+}
+
+
+bool DeviceFilter::is_eligible_ident(const struct hctl_ident *ident) const
+{
+	int rc;
+
+	for (list<struct hctl_ident>::const_iterator i = m_idents.begin();
+	      i != m_idents.end(); ++i) {
+		rc = compare_hctl_idents(ident, &(*i));
+		if (rc == 0)
+			return true;
+		if (rc < 0)
+			break;
+	}
+
+	return false;
+}
+
+
+bool DeviceFilter::is_eligible_host_id(__u32 host) const
+{
+	for (list<__u32>::const_iterator i = m_host_ids.begin();
+	      i != m_host_ids.end() && host >= (*i); ++i) {
+		if (host == (*i))
+			return true;
+	}
+
+	return false;
+}
+
+
+void DeviceFilter::get_eligible_chpids(ConfigReader &cfg, list<__u32> &lst) const
+{
+	int rc = 0;
+
+	lst.clear();
+	for (list<__u32>::const_iterator i = m_host_ids.begin();
+	      i != m_host_ids.end(); ++i) {
+		lst.push_back(cfg.get_chpid_by_host_id(*i, &rc));
+		assert(rc == 0);
+	}
+}
+
+
+void DeviceFilter::get_eligible_devnos(ConfigReader &cfg, list<__u32> &lst) const
+{
+	if (m_host_ids.size() == 0)
+		cfg.get_unique_devnos(lst);
+	else {
+		lst.clear();
+		list<__u32> tmp;
+		for (list<__u32>::const_iterator i = m_host_ids.begin();
+		      i != m_host_ids.end(); ++i) {
+			cfg.get_devnos_by_host_id(tmp, *i);
+			lst.insert(lst.begin(), tmp.begin(), tmp.end());
+		}
+		lst.sort();
+		lst.unique();
+	}
+}
+
+void DeviceFilter::get_eligible_wwpns(ConfigReader &cfg, list<__u64> &lst) const
+{
+	if (m_devices.size() == 0)
+		cfg.get_unique_wwpns(lst);
+	else {
+		int rc = 0;
+
+		lst.clear();
+		for (list<__u32>::const_iterator i = m_devices.begin();
+		      i != m_devices.end(); ++i) {
+			lst.push_back(cfg.get_wwpn_by_mm_internal(*i, &rc));
+			assert(rc == 0);
+		}
+		lst.sort();
+		lst.unique();
+	}
+}
+
+void DeviceFilter::get_eligible_mp_mms(ConfigReader &cfg, list<__u32> &lst) const
+{
+	if (m_devices.size() == 0)
+		cfg.get_unique_mp_mms(lst);
+	else {
+		int rc = 0;
+		__u32 mp_mm;
+
+		lst.clear();
+		for (list<__u32>::const_iterator i = m_devices.begin();
+		      i != m_devices.end(); ++i) {
+			mp_mm = cfg.get_mp_mm_by_mm_internal(*i, &rc);
+			assert(rc == 0);
+			if (mp_mm)
+				lst.push_back(mp_mm);
+		}
+		lst.sort();
+		lst.unique();
+	}
+}
+
+
+void StagedDeviceFilter::stage_wwpn(__u64 w)
+{
+	list<__u64>::iterator i;
+
+	for (i = m_wwpns.begin(); i != m_wwpns.end() && w > *i; ++i);
+
+	if (m_wwpns.size() == 0 || i == m_wwpns.end() || w != *i)
+		m_wwpns.insert(i, w);
+}
+
+
+void StagedDeviceFilter::stage_mp_mm(__u32 mp_mm)
+{
+	list<__u32>::iterator i;
+
+	for (i = m_mp_mms.begin(); i != m_mp_mms.end() && mp_mm > *i; ++i);
+
+	if (m_mp_mms.size() == 0 || i == m_mp_mms.end() || mp_mm != *i)
+		m_mp_mms.insert(i, mp_mm);
+}
+
+
+void StagedDeviceFilter::stage_devno(__u32 d)
+{
+	list<__u32>::iterator i;
+
+	for (i = m_devnos.begin(); i != m_devnos.end() && d > *i; ++i);
+
+	if (m_devnos.size() == 0 || i == m_devnos.end() || d != *i)
+		m_devnos.insert(i, d);
+}
+
+
+void StagedDeviceFilter::stage_chpid(__u32 c)
+{
+	list<__u32>::iterator i;
+
+	for (i = m_chpids.begin(); i != m_chpids.end() && c > *i; ++i);
+
+	if (m_chpids.size() == 0 || i == m_chpids.end() || c != *i)
+		m_chpids.insert(i, c);
+}
+
+
+const list<__u64>& StagedDeviceFilter::get_filter_wwpns()
+{
+	return m_wwpns;
+}
+
+
+const list<__u32>& StagedDeviceFilter::get_filter_chpids()
+{
+	return m_chpids;
+}
+
+
+const list<__u32>& StagedDeviceFilter::get_filter_devnos()
+{
+	return m_devnos;
+}
+
+
+const list<__u32>& StagedDeviceFilter::get_filter_mp_mms()
+{
+	return m_mp_mms;
+}
+
+
+void StagedDeviceFilter::add_mm_with_host(ConfigReader &cfg, __u32 mm, int *rc)
+{
+	const struct hctl_ident *ident = cfg.get_ident_by_mm_internal(mm, rc);
+	assert(*rc == 0);
+
+	add_device(mm, ident);
+	add_host(ident->host);
+}
+
+
+void StagedDeviceFilter::add_mms_with_host(ConfigReader &cfg, list<__u32> &mms, int *rc)
+{
+	for (list<__u32>::const_iterator i = mms.begin();
+	      i != mms.end(); ++i)
+		add_mm_with_host(cfg, *i, rc);
+}
+
+
+bool StagedDeviceFilter::check_wwpn(__u32 mm, ConfigReader &cfg, int *rc)
+{
+	if (m_wwpns.size() == 0)
+		return true;
+
+	__u64	wwpn = cfg.get_wwpn_by_mm_internal(mm, rc);
+	for (list<__u64>::const_iterator i = m_wwpns.begin();
+	      i != m_wwpns.end(); ++i) {
+		if (*i == wwpn)
+			return true;
+	}
+
+	return false;
+}
+
+
+bool StagedDeviceFilter::check_mm(__u32 mm, ConfigReader &cfg, int *rc)
+{
+	if (m_devices.size() == 0)
+		return true;
+
+	for (list<__u32>::const_iterator i = m_devices.begin();
+	      i != m_devices.end(); ++i) {
+		if (*i == mm)
+			return true;
+	}
+
+	return false;
+}
+
+
+bool StagedDeviceFilter::check_devno(__u32 mm, ConfigReader &cfg, int *rc)
+{
+	if (m_devnos.size() == 0)
+		return true;
+
+	__u32	devno = cfg.get_devno_by_mm_internal(mm, rc);
+	for (list<__u32>::const_iterator i = m_devnos.begin();
+	      i != m_devnos.end(); ++i) {
+		if (*i == devno)
+			return true;
+	}
+
+	return false;
+}
+
+
+bool StagedDeviceFilter::check_chpid(__u32 mm, ConfigReader &cfg, int *rc)
+{
+	if (m_chpids.size() == 0)
+		return true;
+
+	__u32	chpid = cfg.get_chpid_by_mm_internal(mm, rc);
+	for (list<__u32>::const_iterator i = m_chpids.begin();
+	      i != m_chpids.end(); ++i) {
+		if (*i == chpid)
+			return true;
+	}
+
+	return false;
+}
+
+
+bool StagedDeviceFilter::check_mp_device(__u32 mm, ConfigReader &cfg, int *rc)
+{
+	if (m_mp_mms.size() == 0)
+		return true;
+
+	__u32 mp = cfg.get_mp_mm_by_mm_internal(mm, rc);
+	assert(mp);
+	for (list<__u32>::const_iterator i = m_mp_mms.begin();
+	      i != m_mp_mms.end(); ++i) {
+		if (*i == mp)
+			return true;
+	}
+
+	return false;
+}
+
+
+int StagedDeviceFilter::finish(ConfigReader &cfg, bool intersect)
+{
+	int rc = 0;
+	list<__u32> mm_lst;
+
+	if (intersect) {
+		bool found_something = false;
+		cfg.get_unique_mms(mm_lst);
+		for (list<__u32>::const_iterator i = mm_lst.begin();
+		      i != mm_lst.end(); ++i) {
+			if (   check_chpid(*i, cfg, &rc)
+			    && check_mp_device(*i, cfg, &rc)
+			    && check_devno(*i, cfg, &rc)
+			    && check_wwpn(*i, cfg, &rc)
+			    && check_mm(*i, cfg, &rc)) {
+				found_something = true;
+				add_mm_with_host(cfg, *i, &rc);
+			}
+		}
+		if (!rc && !found_something)
+			rc = 1;
+	}
+	else {
+		for (list<__u64>::const_iterator i = m_wwpns.begin();
+		      i != m_wwpns.end(); ++i) {
+			cfg.get_mms_by_wwpn(mm_lst, *i);
+			add_mms_with_host(cfg, mm_lst, &rc);
+		}
+		for (list<__u32>::const_iterator i = m_mp_mms.begin();
+		      i != m_mp_mms.end(); ++i) {
+			cfg.get_mms_by_mp_mm(mm_lst, *i);
+			add_mms_with_host(cfg, mm_lst, &rc);
+		}
+		for (list<__u32>::const_iterator i = m_devnos.begin();
+		      i != m_devnos.end(); ++i) {
+			cfg.get_mms_by_devno(mm_lst, *i);
+			add_mms_with_host(cfg, mm_lst, &rc);
+		}
+		for (list<__u32>::const_iterator i = m_chpids.begin();
+		      i != m_chpids.end(); ++i) {
+			cfg.get_mms_by_chpid(mm_lst, *i);
+			add_mms_with_host(cfg, mm_lst, &rc);
+		}
+	}
+
+	return rc;
+}
+
+
Index: s390-tools-sles11/ziomon/ziorep_filters.hpp
===================================================================
--- /dev/null
+++ s390-tools-sles11/ziomon/ziorep_filters.hpp
@@ -0,0 +1,214 @@
+/*
+ * FCP report generators
+ *
+ * Classes implementing various filters
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Stefan Raspl <raspl@linux.vnet.ibm.com>
+ */
+
+#ifndef ZIOREP_FILTERS
+#define ZIOREP_FILTERS
+
+#include <list>
+#include <set>
+
+
+#include "ziorep_cfgreader.hpp"
+
+
+using std::list;
+using std::set;
+
+extern "C" {
+#include "blkiomon.h"
+#include "ziomon_zfcpdd.h"
+}
+
+
+enum MsgTypes {
+	utilization,
+	ioerr,
+	blkiomon,
+	zfcpdd
+};
+
+
+class MsgFilter {
+protected:
+	virtual ~MsgFilter();
+
+	MsgFilter();
+
+public:
+	/** Returns 'true' if the message should be kept,
+	 * 'false' in case it should be filtered
+	 */
+	virtual bool is_eligible(const struct message_preview *msg) const = 0;
+
+	/** Returns 'true' if the message should be kept,
+	 * 'false' in case it should be filtered
+	 */
+	virtual bool is_eligible(const struct message *msg) const = 0;
+};
+
+
+class MsgTypeFilter : public MsgFilter {
+public:
+	virtual ~MsgTypeFilter();
+
+	MsgTypeFilter();
+
+	/// add type that should be kept
+	void add_type(__u32 type);
+
+	virtual bool is_eligible(const struct message_preview *msg) const;
+
+	virtual bool is_eligible(const struct message *msg) const;
+
+private:
+	bool is_eligible(__u32 type) const;
+
+	/// types to keep
+	set<__u32>	m_types;
+};
+
+
+class MsgTimeFilter : public MsgFilter {
+public:
+	virtual ~MsgTimeFilter();
+
+	/// add type that should be kept
+	MsgTimeFilter(__u64 begin, __u64 end);
+
+	virtual bool is_eligible(const struct message_preview *msg) const;
+
+	/**
+	 * NOTE: Message must be in native endianess! */
+	virtual bool is_eligible(const struct message *msg) const;
+
+	__u64	get_begin_time() const;
+	__u64	get_end_time() const;
+
+private:
+	bool is_eligible(__u64 timestamp) const;
+
+	__u64		m_begin;
+	__u64		m_end;
+};
+
+
+/**
+ * Class to filter messages by the device they refer to, where
+ * the device can be of any type.
+ */
+class DeviceFilter {
+public:
+	void add_device(__u32 device, const struct hctl_ident *id);
+
+	const list<__u32>& get_host_id_list() const;
+	const list<__u32>& get_mm_list() const;
+
+	/** Returns 'true' if the message should be kept,
+	 * 'false' in case it should be filtered
+	 */
+	bool is_eligible(const message *msg, const struct file_header *hdr) const;
+	bool is_eligible(const struct adapter_utilization *res) const;
+	bool is_eligible(const struct ioerr_cnt *cnt) const;
+
+	bool is_eligible_mm(__u32 mm) const;
+	bool is_eligible_ident(const struct hctl_ident *ident) const;
+	bool is_eligible_host_id(__u32 host) const;
+
+	/** Returns a list of all chpids that are eligible according to
+	 * the filter criteria. */
+	void get_eligible_chpids(ConfigReader &cfg, list<__u32> &lst) const;
+
+	/** Returns a list of all devnos that are eligible according to
+	 * the filter criteria. */
+	void get_eligible_devnos(ConfigReader &cfg, list<__u32> &lst) const;
+
+	/** Returns a list of all wwpns that are eligible according to
+	 * the filter criteria. */
+	void get_eligible_wwpns(ConfigReader &cfg, list<__u64> &lst) const;
+
+	/** Returns a list of all mp_mms that are eligible according to
+	 * the filter criteria. */
+	void get_eligible_mp_mms(ConfigReader &cfg, list<__u32> &lst) const;
+
+protected:
+	void add_host(__u32 host_id);
+	/// ascending list of devices to keep - in sync with m_idents
+	list<__u32>			m_devices;
+
+private:
+	void add_device(__u32 device);
+	void add_device(const struct hctl_ident *id);
+
+	/// ascending list of HBAs to keep
+	list<__u32>			m_host_ids;
+	/// ascending list of devices to keep - in sync with m_devices
+	list<struct hctl_ident>		m_idents;
+};
+
+/**
+ * Special kind of DeviceFilter: Add all criteria to it and finally
+ * decide whether devices should be filtered by (a) either criterion
+ * or (b) only the intersection of the criteria.
+ * Internally, we store all submitted data in separate structures. When
+ * the user hits 'finished', the data will be analyzed and transferred to
+ * the base class.
+ */
+class StagedDeviceFilter : public DeviceFilter {
+public:
+	void stage_wwpn(__u64 wwpn);
+	void stage_mp_mm(__u32 mp_mm);
+	void stage_devno(__u32 sub);
+	void stage_chpid(__u32 chpid);
+
+	/**
+	  * retrieve an ordered list of the wwpns as they were specified
+	  * for filtering. */
+	const list<__u64>& get_filter_wwpns();
+	/**
+	  * retrieve an ordered list of the chpids as they were specified
+	  * for filtering. */
+	const list<__u32>& get_filter_chpids();
+	/**
+	  * retrieve an ordered list of the devnos as they were specified
+	  * for filtering. */
+	const list<__u32>& get_filter_devnos();
+	/**
+	  * retrieve an ordered list of the mp_mms as they were specified
+	  * for filtering. */
+	const list<__u32>& get_filter_mp_mms();
+
+	/**
+	 * Call this method prior to actually using the class.
+	 * If 'intersect' is enabled, a positive return code indicates
+	 * that the intersection is empty. <0 means error, 0 in success.
+	 */
+	int finish(ConfigReader &cfg, bool intersect);
+
+private:
+	void add_mm_with_host(ConfigReader &cfg, __u32 mm, int *rc);
+	void add_mms_with_host(ConfigReader &cfg, list<__u32> &mms, int *rc);
+	/// check if 'mm's wwpn is eligible
+	bool check_wwpn(__u32 mm, ConfigReader &cfg, int *rc);
+	/// check if 'mm' is eligible
+	bool check_mm(__u32 mm, ConfigReader &cfg, int *rc);
+	/// check if 'mm's devo is eligible
+	bool check_devno(__u32 mm, ConfigReader &cfg, int *rc);
+	/// check if 'mm's chpid is eligible
+	bool check_chpid(__u32 mm, ConfigReader &cfg, int *rc);
+	/// check if 'mm's mp is eligible
+	bool check_mp_device(__u32 mm, ConfigReader &cfg, int *rc);
+
+	list<__u64>		m_wwpns;
+	list<__u32>		m_devnos;
+	list<__u32>		m_chpids;
+	list<__u32>		m_mp_mms;
+};
+
+#endif
+
Index: s390-tools-sles11/ziomon/ziorep_framer.cpp
===================================================================
--- /dev/null
+++ s390-tools-sles11/ziomon/ziorep_framer.cpp
@@ -0,0 +1,288 @@
+/*
+ * FCP report generators
+ *
+ * Class for reading messages into frames.
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Stefan Raspl <raspl@linux.vnet.ibm.com>
+ */
+
+#include <assert.h>
+#include <unistd.h>
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "ziorep_framer.hpp"
+#include "ziorep_utils.hpp"
+
+extern "C" {
+#include "ziomon_msg_tools.h"
+}
+
+extern const char *toolname;
+extern int verbose;
+
+
+Framer::Framer(__u64 begin, __u64 end, __u32 interval_length,
+	       list<MsgTypes> *filter_types, DeviceFilter *devFilter,
+	       const char *filename, int *rc)
+	: m_interval_length(interval_length), m_type_filter(NULL),
+	m_device_filter(devFilter), m_filename(filename), m_fp(NULL),
+	m_agg_read(false)
+{
+	m_begin = begin;
+	m_end = end;
+	assert(m_begin <= m_end);
+
+	// set up .log file on first time
+	if (open_data_files(&m_fp, m_filename, &m_fhdr, &m_agg_data)) {
+		*rc = -2;
+		return;
+	}
+	if (m_agg_data)
+		conv_aggr_data_msg_data_from_BE(m_agg_data);
+
+	time_t t = m_begin;
+	t = m_end;
+
+	if (filter_types) {
+		m_type_filter = new MsgTypeFilter;
+		for (list<MsgTypes>::const_iterator i = filter_types->begin();
+		      i != filter_types->end(); ++i) {
+			switch (*i) {
+			case utilization:
+				m_type_filter->add_type(m_fhdr.msgid_utilization);
+				break;
+			case ioerr:
+				m_type_filter->add_type(m_fhdr.msgid_ioerr);
+				break;
+			case blkiomon:
+				m_type_filter->add_type(m_fhdr.msgid_blkiomon);
+				break;
+			case zfcpdd:
+				m_type_filter->add_type(m_fhdr.msgid_zfcpdd);
+				break;
+			}
+		}
+	}
+
+	*rc = 0;
+}
+
+Framer::~Framer()
+{
+	close_data_files(m_fp);
+
+	if (m_type_filter)
+		delete m_type_filter;
+
+	if (m_agg_data) {
+		discard_aggr_data_struct(m_agg_data);
+		free(m_agg_data);
+	}
+}
+
+bool Framer::handle_agg_data(Frameset &frameset) const
+{
+	// Initial test - if we pass, we still have to check
+	// the messages individually later on!
+	if (m_begin > m_agg_data->end_time || m_end < m_agg_data->begin_time)
+		return false;
+
+	/* collate all messages */
+	list<struct message *> agg_msgs;
+	if (m_agg_data->util_aggr)
+		agg_msgs.insert(agg_msgs.end(), m_agg_data->util_aggr);
+	if (m_agg_data->ioerr_aggr)
+		agg_msgs.insert(agg_msgs.end(), m_agg_data->ioerr_aggr);
+	for (unsigned int i = 0; i<m_agg_data->num_blkiomon; ++i)
+		agg_msgs.insert(agg_msgs.end(), m_agg_data->blkio_aggr[i]);
+	for (unsigned int i = 0; i<m_agg_data->num_zfcpdd; ++i)
+		agg_msgs.insert(agg_msgs.end(), m_agg_data->zfcpdd_aggr[i]);
+
+	/* loop over collated msgs */
+	for (list<struct message*>::iterator i=agg_msgs.begin();
+	      i != agg_msgs.end(); ++i) {
+		// we do an all-or-nothing approach - anything else would be
+		// too confusing
+		/*
+		__u64 t = get_timestamp_from_msg(*i);
+		if (m_begin > t || m_end < t) {
+			vverbose_msg("message not in timeframe\n");
+			continue;
+		} */
+		if (m_type_filter && !m_type_filter->is_eligible(*i)) {
+			vverbose_msg("message type not eligible\n");
+			continue;
+		}
+		vverbose_msg("adding msg\n");
+		handle_msg(*i, frameset);
+	}
+	verbose_msg("    found eligible data in aggregated messages: %d\n",
+		    frameset.is_aggregated());
+
+	return (!frameset.is_empty());
+}
+
+void Framer::handle_msg(struct message *msg, Frameset &frameset) const
+{
+	if (msg->type == m_fhdr.msgid_utilization) {
+		struct utilization_data *res = (struct utilization_data*)msg->data;
+		struct adapter_utilization *a_res;
+		for (int i = 0; i < res->num_adapters; ++i) {
+			a_res = &res->adapt_utils[i];
+			if (m_device_filter && !m_device_filter->is_eligible(a_res)) {
+				vverbose_msg("message not for eligible device\n");
+				continue;
+			}
+			vverbose_msg("adding utilization msg\n");
+			frameset.add_data(a_res);
+		}
+	}
+	else if (msg->type == m_fhdr.msgid_ioerr) {
+		struct ioerr_data *data = (struct ioerr_data*)msg->data;
+		struct ioerr_cnt *cnt;
+		for (unsigned int i = 0; i < data->num_luns; ++i) {
+			cnt = &data->ioerrors[i];
+			if (m_device_filter && !m_device_filter->is_eligible(cnt)) {
+				vverbose_msg("message not for eligible device\n");
+				continue;
+			}
+			vverbose_msg("adding ioerr msg\n");
+			frameset.add_data(cnt);
+		}
+	}
+	else {
+		if (m_device_filter && !m_device_filter->is_eligible(msg, &m_fhdr)) {
+			vverbose_msg("message not for eligible device\n");
+			return;
+		}
+		if (msg->type == m_fhdr.msgid_blkiomon) {
+			vverbose_msg("adding blkiomon msg\n");
+			frameset.add_data((struct blkiomon_stat*)msg->data);
+		}
+		else {
+			assert(msg->type == m_fhdr.msgid_zfcpdd);
+			vverbose_msg("adding zfcpdd msg\n");
+			frameset.add_data((struct zfcpdd_dstat*)msg->data);
+		}
+	}
+}
+
+int Framer::get_next_frameset(Frameset &frameset, bool replace_missing)
+{
+	int rc = 0;
+	int msgs_read = 0;
+	__u64 shifted_begin;
+	__u64 shifted_end;
+	__u64 frame_begin = 0;
+
+	frameset.reinit();
+
+	if (m_begin > m_end)
+		return 1;
+
+	if (verbose >= 2) {
+#ifndef NDEBUG
+		time_t t = m_begin;
+#endif
+		vverbose_msg("retrieving next frameset for: %s", ctime(&t));
+	}
+
+	// did we check out .agg yet?
+	if (!m_agg_read) {
+		m_agg_read = true;
+		if (m_agg_data) {
+			verbose_msg("    found aggregated data, check if eligible\n");
+			handle_agg_data(frameset);
+			if (!frameset.is_empty()) {
+				if (m_interval_length != 0) {
+					verbose_msg(".agg data processed, wrap up frame\n");
+					frameset.set_aggregated(true);
+					frameset.set_timeframe(
+						m_agg_data->begin_time
+							- m_fhdr.interval_length / 2,
+						m_agg_data->end_time
+							+ m_fhdr.interval_length / 2,
+						m_agg_data->end_time);
+					// just bump it to the next frame
+					m_begin += m_fhdr.interval_length;
+					if (replace_missing)
+						frameset.replace_missing_datasets(m_fhdr.interval_length);
+
+					return 0;
+				}
+				verbose_msg(".agg data processed, add all of the rest to it\n");
+				frame_begin = m_agg_data->begin_time - m_fhdr.interval_length / 2;
+			}
+		}
+		else
+			verbose_msg("    no aggregated data found\n");
+	}
+
+	// down to real business
+	struct message		msg;
+	struct message_preview	msg_preview;
+
+	shifted_begin = m_begin - m_fhdr.interval_length / 2;
+	shifted_end = shifted_begin + m_interval_length;
+	if (m_interval_length == 0 || shifted_end > m_end)
+		shifted_end = m_end + m_fhdr.interval_length / 2;
+	MsgTimeFilter timeFilter(shifted_begin, shifted_end);
+
+	if (frame_begin == 0)
+		frame_begin = timeFilter.get_begin_time();
+
+	while( (rc = get_next_msg_preview(m_fp, &msg_preview, &m_fhdr)) == 0 ) {
+		vverbose_msg("checking out next msg\n");
+		++msgs_read;
+		if (msg_preview.timestamp > timeFilter.get_end_time()) {
+			vverbose_msg("timeframe exceeded\n");
+			rewind_to(m_fp, &msg_preview);
+			break;
+		}
+		// is this necessary at all?!?
+		if (!timeFilter.is_eligible(&msg_preview))
+			continue;
+		vverbose_msg("timestamp: OK\n");
+		if (m_type_filter && !m_type_filter->is_eligible(&msg_preview)) {
+			vverbose_msg("wrong type (%u)\n", msg_preview.type);
+			continue;
+		}
+		vverbose_msg("type     : OK\n");
+		if (get_complete_msg(m_fp, &msg_preview, &msg) < 0) {
+			fprintf(stderr, "%s: Error retrieving next message, aborting"
+				" - file corrupt?\n", toolname);
+			return -5;
+		}
+		conv_msg_data_from_BE(&msg, &m_fhdr);
+		handle_msg(&msg, frameset);
+		discard_msg(&msg);
+	}
+
+	if (rc < 0) {
+		fprintf(stderr, "%s: Error retrieving next message, aborting"
+			" - file corrupt?\n", toolname);
+		return -4;
+	}
+
+	/* if we read some messages, though not the right ones,
+	   we pass on an empty frame still. Will indicate EOF next time */
+	if (rc > 0 && msgs_read)
+		rc = 0;
+
+	if (rc == 0) {
+		frameset.set_timeframe(frame_begin, timeFilter.get_end_time(),
+                              timeFilter.get_end_time() - m_fhdr.interval_length / 2);
+		if (m_interval_length == 0)
+			m_begin = m_end + 1;	// we're done
+		else
+			m_begin += m_interval_length;
+		if (replace_missing)
+			frameset.replace_missing_datasets(m_fhdr.interval_length);
+	}
+
+	return rc;
+}
+
Index: s390-tools-sles11/ziomon/ziorep_framer.hpp
===================================================================
--- /dev/null
+++ s390-tools-sles11/ziomon/ziorep_framer.hpp
@@ -0,0 +1,79 @@
+/*
+ * FCP report generators
+ *
+ * Class for reading messages into frames.
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Stefan Raspl <raspl@linux.vnet.ibm.com>
+ */
+
+#ifndef ZIOREP_FRAMER
+#define ZIOREP_FRAMER
+
+#include <list>
+
+#include "ziorep_filters.hpp"
+#include "ziorep_frameset.hpp"
+
+
+using std::list;
+
+
+extern "C" {
+#include "ziomon_dacc.h"
+}
+
+
+class Framer {
+public:
+	/**
+	 * Note: Filter parameters transfer memory ownership to class.
+	 * 'filter_types' is an optional list of message types that should
+	 * be processed exclusively, anything else will be ignored. If not set,
+	 * all messages will be processed.
+	 */
+	Framer(__u64 begin, __u64 end, __u32 interval_length,
+	       list<MsgTypes> *filter_types, DeviceFilter *devFilter,
+	       const char *filename, int *rc);
+
+	~Framer();
+
+	/**
+	 * Retrieve the next set of messages.
+	 * Returns 0 in case of success, <0 in case of failure
+	 * and >0 in case end of data has been reached.
+	 * Set 'replace_missing' to fill in for non-present datasets.
+	 * E.g. if no utilization data was found in the interval (since there
+	 * was no traffic), a dataset with the expected number of samples
+	 * (but all 0s for the values) will be generated.
+	 */
+	int get_next_frameset(Frameset &frameset, bool replace_missing = false);
+
+private:
+	void handle_msg(struct message *msg, Frameset &frameset) const;
+	bool handle_agg_data(Frameset &frameset) const;
+
+	/* timestamps of samples to consider
+	 * These are exact timestamps, we shift them a bit to make sure that
+	 * we catch any late or early messages as well */
+	__u64		 	 m_begin;
+	__u64		 	 m_end;
+	/// user-specified interval length
+	__u32		 	 m_interval_length;
+
+	/* Criteria to identify the right messages */
+	MsgTypeFilter		*m_type_filter;
+	DeviceFilter		*m_device_filter;
+
+	// filename without extension
+	const char		*m_filename;
+	FILE			*m_fp;
+	struct file_header	 m_fhdr;
+	struct aggr_data	*m_agg_data;
+	/// indicates whether the .agg file was already read or not
+	bool			 m_agg_read;
+};
+
+
+#endif
+
Index: s390-tools-sles11/ziomon/ziorep_frameset.cpp
===================================================================
--- /dev/null
+++ s390-tools-sles11/ziomon/ziorep_frameset.cpp
@@ -0,0 +1,607 @@
+/*
+ * FCP report generators
+ *
+ * Class to hold information of a single frame.
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Stefan Raspl <raspl@linux.vnet.ibm.com>
+ */
+
+#include <assert.h>
+
+#include "ziorep_frameset.hpp"
+
+extern "C" {
+	#include "ziomon_tools.h"
+}
+
+extern const char *toolname;
+extern int verbose;
+
+
+Frameset::Frameset(const Collapser *col, bool normalize) :
+m_empty(true), m_aggregated(false),
+	m_collapser(col), m_normalize(normalize)
+{
+	reinit();
+
+	/* We set everything up for aggregation collapsers */
+	if (m_collapser->get_criterion() != none
+	    && m_collapser->get_criterion() != all) {
+		unsigned int len = 0;
+
+		AggregationCollapser *col = (AggregationCollapser*)m_collapser;
+		switch (m_collapser->get_criterion()) {
+		case chpid: len = col->get_reference_chpids().size();
+			break;
+		case devno: len = col->get_reference_devnos().size();
+			break;
+		case wwpn: len = col->get_reference_wwpns().size();
+			break;
+		case multipath_device: len = col->get_reference_mp_mms().size();
+			break;
+		default:
+			assert(false);
+		}
+		m_util_stats.resize(len);
+		m_ioerr_stats.resize(len);
+		m_blkiomon_stats.resize(len);
+		m_zfcpdd_stats.resize(len);
+		for (unsigned int i = 0; i < len; ++i) {
+			init_utilization_wrapper(&m_util_stats[i]);
+			m_ioerr_stats[i] = NULL;
+			m_blkiomon_stats[i] = NULL;
+			init_zfcpdd_wrapper(&m_zfcpdd_stats[i]);
+		}
+	}
+}
+
+Frameset::~Frameset()
+{
+	reinit();
+}
+
+void Frameset::reinit()
+{
+	for (vector<struct utilization_wrapper>::iterator i=m_util_stats.begin();
+	      i != m_util_stats.end(); i++) {
+		delete (*i).stat;
+		init_utilization_wrapper(&(*i));
+	}
+
+	for (vector<struct ioerr_cnt*>::iterator i=m_ioerr_stats.begin();
+	      i != m_ioerr_stats.end(); i++) {
+		delete *i;
+		*i = NULL;
+	}
+
+	for (vector<struct zfcpdd_wrapper>::iterator i=m_zfcpdd_stats.begin();
+	      i != m_zfcpdd_stats.end(); i++) {
+		delete (*i).stat;
+		init_zfcpdd_wrapper(&(*i));
+	}
+
+	for (vector<struct blkiomon_stat*>::iterator i=m_blkiomon_stats.begin();
+	      i != m_blkiomon_stats.end(); i++) {
+		delete *i;
+		*i = NULL;
+	}
+
+	if (m_collapser->get_criterion() == none
+		|| m_collapser->get_criterion() == all) {
+		m_util_stats.clear();
+		m_ioerr_stats.clear();
+		m_zfcpdd_stats.clear();
+		m_blkiomon_stats.clear();
+	}
+
+	m_aggregated = false;
+	m_start_time = 0;
+	m_end_time = 0;
+	m_empty = true;
+	m_timestamp = 0;
+}
+
+const Collapser* Frameset::get_collapser() const
+{
+	return m_collapser;
+}
+
+
+void Frameset::add_zero_frames(struct utilization_wrapper *wrp,
+			     int num_expected, int interval_length)
+{
+	// found a (probable) kernel bug that cause a violation of this
+	// assertion. There might be other reasons (exzessive steal times)
+	// that could cause a violation as well.
+	//assert(!wrp || wrp->counter <= num_expected);
+
+	if (wrp
+	    && wrp->counter > 0
+	    && wrp->counter < num_expected
+	    && wrp->stat->valid) {
+		vverbose_msg("Correcting util stat from %d to %d datasets\n",
+			     wrp->counter, num_expected);
+		transform_abbrev_stat(&wrp->stat->stats.adapter,
+				  wrp->stat->stats.count,
+				  wrp->counter);
+		transform_abbrev_stat(&wrp->stat->stats.bus,
+				  wrp->stat->stats.count,
+				  wrp->counter);
+		transform_abbrev_stat(&wrp->stat->stats.cpu,
+				  wrp->stat->stats.count,
+				  wrp->counter);
+		wrp->stat->stats.queue_util_interval += interval_length * 1000000
+				* (num_expected - wrp->counter);
+		wrp->stat->stats.count = num_expected;
+		wrp->counter = num_expected;
+	}
+}
+
+
+/**
+ * Only fix utilization and the qdio portion of util
+ * datasets - the rest doesn't matter */
+void Frameset::replace_missing_datasets(int interval_length)
+{
+	int num_expected_datasets;
+
+	assert(m_start_time != 0);
+	assert(m_end_time != 0);
+	assert(interval_length > 0);
+
+	num_expected_datasets = (m_end_time - m_start_time) /interval_length;
+	for (vector<struct utilization_wrapper>::iterator i = m_util_stats.begin();
+	      i != m_util_stats.end(); ++i)
+		add_zero_frames(&(*i), num_expected_datasets, interval_length);
+}
+
+void Frameset::set_timeframe(__u64 begin, __u64 end, __u64 timestamp)
+{
+	m_start_time = begin;
+	m_end_time = end;
+	m_timestamp = timestamp;
+}
+
+__u64 Frameset::get_begin_time() const
+{
+	return m_start_time;
+}
+
+__u64 Frameset::get_end_time() const
+{
+	return m_end_time;
+}
+
+__u64 Frameset::get_timestamp() const
+{
+	return m_timestamp;
+}
+
+__u64 Frameset::get_duration() const
+{
+	return (m_end_time - m_start_time);
+}
+
+
+void Frameset::normalize_stat(struct abbrev_stat *stat, __u64 count)
+{
+	stat->sum = (__u64)calc_avg(stat->sum, count);
+	stat->sos = (__u64)calc_avg(stat->sos, count);
+}
+
+
+void Frameset::normalize_util_stat(struct utilization_stats *stats)
+{
+	if (stats->count > 1) {
+		normalize_stat(&stats->adapter, stats->count);
+		normalize_stat(&stats->bus, stats->count);
+		normalize_stat(&stats->cpu, stats->count);
+		stats->count = 1;
+	}
+}
+
+
+void Frameset::add_data(struct adapter_utilization *res)
+{
+	unsigned int idx = m_collapser->get_index_by_host_id(res->adapter_no);
+
+	m_empty = false;
+
+	if (idx >= m_util_stats.size()) {
+		unsigned int old_size = m_util_stats.size();
+		m_util_stats.resize(idx + 1);
+		for (unsigned int i = old_size; i < idx + 1; ++i)
+			init_utilization_wrapper(&m_util_stats[i]);
+	}
+
+	if (m_normalize)
+		normalize_util_stat(&res->stats);
+
+	if (m_util_stats[idx].counter) {
+		aggregate_adapter_result(res, m_util_stats[idx].stat);
+		m_util_stats[idx].counter++;
+	}
+	else {
+		m_util_stats[idx].stat = new struct adapter_utilization;
+		*m_util_stats[idx].stat = *res;
+		m_util_stats[idx].counter = 1;
+	}
+}
+
+
+void Frameset::add_data(struct ioerr_cnt *cnt)
+{
+	unsigned int idx = m_collapser->get_index(&cnt->identifier);
+
+	m_empty = false;
+
+	if (idx >= m_ioerr_stats.size()) {
+		unsigned int old_size = m_ioerr_stats.size();
+		m_ioerr_stats.resize(idx + 1);
+		for (unsigned int i = old_size; i < idx + 1; ++i)
+			m_ioerr_stats[i] = NULL;
+	}
+
+	if (m_ioerr_stats[idx])
+		aggregate_ioerr_cnt(cnt, m_ioerr_stats[idx]);
+	else {
+		m_ioerr_stats[idx] = new struct ioerr_cnt;
+		*m_ioerr_stats[idx] = *cnt;
+	}
+}
+
+
+void Frameset::add_data(struct blkiomon_stat *stat)
+{
+	unsigned int idx = m_collapser->get_index(stat->device);
+
+	m_empty = false;
+
+	if (idx >= m_blkiomon_stats.size()) {
+		unsigned int old_size = m_blkiomon_stats.size();
+		m_blkiomon_stats.resize(idx + 1);
+		for (unsigned int i = old_size; i < idx + 1; ++i)
+			m_blkiomon_stats[i] = NULL;
+	}
+
+	if (m_blkiomon_stats[idx])
+		blkiomon_stat_merge(m_blkiomon_stats[idx], stat);
+	else {
+		m_blkiomon_stats[idx] = new struct blkiomon_stat;
+		*m_blkiomon_stats[idx] = *stat;
+	}
+}
+
+
+void Frameset::normalize_zfcpdd_stat(struct zfcpdd_dstat *stat)
+{
+	stat->chan_lat.min /= 1000;
+	stat->chan_lat.max /= 1000;
+	stat->chan_lat.sum /= 1000;
+	stat->chan_lat.sos /= (1000 * 1000);
+}
+
+
+void Frameset::add_data(struct zfcpdd_dstat *stat)
+{
+	unsigned int idx = m_collapser->get_index(stat->device);
+
+	m_empty = false;
+
+	normalize_zfcpdd_stat(stat);
+
+	if (idx >= m_zfcpdd_stats.size()) {
+		unsigned int old_size = m_zfcpdd_stats.size();
+		m_zfcpdd_stats.resize(idx + 1);
+		for (unsigned int i = old_size; i < idx + 1; ++i)
+			init_zfcpdd_wrapper(&m_zfcpdd_stats[i]);
+	}
+
+	if (m_zfcpdd_stats[idx].counter) {
+		aggregate_dstat(stat, m_zfcpdd_stats[idx].stat);
+		m_zfcpdd_stats[idx].counter++;
+	}
+	else {
+		m_zfcpdd_stats[idx].stat = new struct zfcpdd_dstat;
+		*m_zfcpdd_stats[idx].stat = *stat;
+		m_zfcpdd_stats[idx].counter = 1;
+	}
+}
+
+void Frameset::set_aggregated(bool aggr)
+{
+	m_aggregated = aggr;
+}
+
+bool Frameset::is_aggregated() const
+{
+	return m_aggregated;
+}
+
+bool Frameset::is_empty() const
+{
+	return m_empty;
+}
+
+const vector<struct ioerr_cnt*>& Frameset::get_ioerr_stats() const
+{
+	return m_ioerr_stats;
+}
+
+const struct zfcpdd_dstat* Frameset::get_first_zfcpdd_stat() const
+{
+	assert(m_zfcpdd_stats.size() <= 1);
+
+	if (m_zfcpdd_stats.size() > 0)
+		return m_zfcpdd_stats[0].stat;
+	else
+		return NULL;
+}
+
+const struct blkiomon_stat* Frameset::get_first_blkiomon_stat() const
+{
+	assert(m_blkiomon_stats.size() <= 1);
+
+	if (m_blkiomon_stats.size() > 0)
+                return m_blkiomon_stats[0];
+	else
+		return NULL;
+}
+
+void Frameset::init_utilization_wrapper(struct utilization_wrapper *wrp)
+{
+	wrp->counter = 0;
+	wrp->stat = NULL;
+}
+
+void Frameset::init_zfcpdd_wrapper(struct zfcpdd_wrapper *wrp)
+{
+	wrp->counter = 0;
+	wrp->stat = NULL;
+}
+
+int Frameset::get_by_chpid(__u32 chp) const
+{
+	assert(m_collapser->get_criterion() == chpid);
+
+	int idx = find_index(((AggregationCollapser*)m_collapser)->get_reference_chpids(), chp);
+	assert(idx >= 0);
+
+	return idx;
+}
+
+int Frameset::get_by_devno(__u32 d) const
+{
+	assert(m_collapser->get_criterion() == devno);
+
+	int idx = find_index(((AggregationCollapser*)m_collapser)->get_reference_devnos(), d);
+	assert(idx >= 0);
+
+	return idx;
+}
+
+int Frameset::get_by_mp_mm(__u32 mp_mm) const
+{
+	assert(m_collapser->get_criterion() == multipath_device);
+
+	int idx = find_index(((AggregationCollapser*)m_collapser)->get_reference_mp_mms(), mp_mm);
+	assert(idx >= 0);
+
+	return idx;
+}
+
+int Frameset::get_by_mm(__u32 mm) const
+{
+	assert(m_collapser->get_criterion() == none
+	       || m_collapser->get_criterion() == all);
+
+	int idx = m_collapser->get_index(mm);
+	assert(idx >= 0);
+
+	return idx;
+}
+
+int Frameset::get_by_wwpn(__u64 w) const
+{
+	assert(m_collapser->get_criterion() == wwpn);
+
+	int idx = find_index(((AggregationCollapser*)m_collapser)->get_reference_wwpns(), w);
+	assert(idx >= 0);
+
+	return idx;
+}
+
+const struct adapter_utilization* Frameset::get_utilization_stat_by_host_id(
+	__u32 h_id) const
+{
+	for (vector<struct utilization_wrapper>::const_iterator i = m_util_stats.begin();
+	      i != m_util_stats.end(); ++i) {
+		if ((*i).counter && (*i).stat->adapter_no == h_id)
+			return (*i).stat;
+	}
+
+	return NULL;
+}
+
+const struct adapter_utilization* Frameset::get_utilization_stat_by_chpid(
+	__u32 chpid) const
+{
+	int idx = get_by_chpid(chpid);
+
+	assert(idx < (int)m_util_stats.size());
+
+	if (idx >= (int)m_util_stats.size())
+		return NULL;
+
+	return m_util_stats[idx].stat;
+}
+
+const struct ioerr_cnt* Frameset::get_ioerr_stat_by_chpid(__u32 chpid) const
+{
+	int idx = get_by_chpid(chpid);
+
+	assert(idx < (int)m_ioerr_stats.size());
+
+	if (idx >= (int)m_ioerr_stats.size())
+		return NULL;
+
+	return m_ioerr_stats[idx];
+}
+
+const struct blkiomon_stat* Frameset::get_blkiomon_stat_by_chpid(__u32 chpid) const
+{
+	int idx = get_by_chpid(chpid);
+
+	if (idx >= (int)m_blkiomon_stats.size())
+		return NULL;
+
+	return m_blkiomon_stats[idx];
+}
+
+const struct blkiomon_stat* Frameset::get_blkiomon_stat_by_devno(__u32 devno) const
+{
+	int idx = get_by_devno(devno);
+
+	if (idx >= (int)m_blkiomon_stats.size())
+		return NULL;
+
+	return m_blkiomon_stats[idx];
+}
+
+const struct blkiomon_stat* Frameset::get_blkiomon_stat_by_wwpn(__u64 wwpn) const
+{
+	int idx = get_by_wwpn(wwpn);
+
+	if (idx >= (int)m_blkiomon_stats.size())
+		return NULL;
+
+	return m_blkiomon_stats[idx];
+}
+
+const struct blkiomon_stat* Frameset::get_blkiomon_stat_by_mp_mm(__u32 mp_mm) const
+{
+	int idx = get_by_mp_mm(mp_mm);
+
+	if (idx >= (int)m_blkiomon_stats.size())
+		return NULL;
+
+	return m_blkiomon_stats[idx];
+}
+
+const struct blkiomon_stat* Frameset::get_blkiomon_stat_by_mm(__u32 mm) const
+{
+	int idx = get_by_mm(mm);
+
+	if (idx >= (int)m_blkiomon_stats.size())
+		return NULL;
+
+	return m_blkiomon_stats[idx];
+}
+
+const struct zfcpdd_dstat* Frameset::get_zfcpdd_stat_by_chpid(__u32 chpid) const
+{
+	int idx = get_by_chpid(chpid);
+
+	if (idx >= (int)m_zfcpdd_stats.size())
+		return NULL;
+
+	return m_zfcpdd_stats[idx].stat;
+}
+
+const struct zfcpdd_dstat* Frameset::get_zfcpdd_stat_by_devno(__u32 devno) const
+{
+	int idx = get_by_devno(devno);
+
+	if (idx >= (int)m_zfcpdd_stats.size())
+		return NULL;
+
+	return m_zfcpdd_stats[idx].stat;
+}
+
+const struct zfcpdd_dstat* Frameset::get_zfcpdd_stat_by_wwpn(__u64 wwpn) const
+{
+	int idx = get_by_wwpn(wwpn);
+
+	if (idx >= (int)m_zfcpdd_stats.size())
+		return NULL;
+
+	return m_zfcpdd_stats[idx].stat;
+}
+
+const struct zfcpdd_dstat* Frameset::get_zfcpdd_stat_by_mp_mm(__u32 mp_mm) const
+{
+	int idx = get_by_mp_mm(mp_mm);
+
+	if (idx >= (int)m_zfcpdd_stats.size())
+		return NULL;
+
+	return m_zfcpdd_stats[idx].stat;
+}
+
+const struct zfcpdd_dstat* Frameset::get_zfcpdd_stat_by_mm(__u32 mm) const
+{
+	int idx = get_by_mm(mm);
+
+	if (idx >= (int)m_zfcpdd_stats.size())
+		return NULL;
+
+	return m_zfcpdd_stats[idx].stat;
+}
+
+const struct adapter_utilization* Frameset::get_utilization_stat_by_devno(
+	__u32 subchnl) const
+{
+	int idx = get_by_devno(subchnl);
+
+	assert(idx < (int)m_util_stats.size());
+
+	if (idx >= (int)m_util_stats.size())
+		return NULL;
+
+	return m_util_stats[idx].stat;
+}
+
+const struct ioerr_cnt* Frameset::get_ioerr_stat_by_devno(
+	__u32 devno) const
+{
+	int idx = get_by_devno(devno);
+
+	assert(idx < (int)m_ioerr_stats.size());
+
+	if (idx >= (int)m_ioerr_stats.size())
+		return NULL;
+
+	return m_ioerr_stats[idx];
+}
+
+int Frameset::find_index(const list<__u32> &lst, __u32 val) const
+{
+	unsigned int idx = 0;
+
+	for (list<__u32>::const_iterator i = lst.begin();
+	      i != lst.end(); ++i, ++idx) {
+		if (*i == val)
+			return idx;
+	}
+
+	return -1;
+}
+
+int Frameset::find_index(const list<__u64> &lst, __u64 val) const
+{
+	unsigned int idx = 0;
+
+	for (list<__u64>::const_iterator i = lst.begin();
+	      i != lst.end(); ++i, ++idx) {
+		if (*i == val)
+			return idx;
+	}
+
+	return -1;
+}
+
+
+
+
+
Index: s390-tools-sles11/ziomon/ziorep_frameset.hpp
===================================================================
--- /dev/null
+++ s390-tools-sles11/ziomon/ziorep_frameset.hpp
@@ -0,0 +1,228 @@
+/*
+ * FCP report generators
+ *
+ * Class to hold information of a single frame.
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Stefan Raspl <raspl@linux.vnet.ibm.com>
+ */
+
+#ifndef ZIOREP_FRAMESET
+#define ZIOREP_FRAMESET
+
+#include <list>
+#include <vector>
+
+#include "ziorep_collapser.hpp"
+
+extern "C" {
+	#include "ziomon_util.h"
+	#include "blkiomon.h"
+	#include "ziomon_zfcpdd.h"
+	#include "ziomon_dacc.h"
+}
+
+using std::list;
+using std::vector;
+
+class Frameset {
+
+public:
+	/**
+	 * If 'normalize' is set, incoming data with varying sample
+	 * sizes per interval (namely utilization messages and zfcpdd data)
+	 * will be normalized so that different sample sizes will not
+	 * result in different weighting when aggregating.
+	 */
+	Frameset(const Collapser *col, bool normalize = true);
+	~Frameset();
+
+	/**
+	 * Clear frame-related structures */
+	void reinit();
+
+	/// get pointer to collapser
+	const Collapser* get_collapser() const;
+
+	/**
+	 * Since we don't necessarily have datasets for all messages in each
+	 * frame, utilization and the qdio portion of zfcpdd messages
+	 * can be inaccurate when aggregated. This method makes up for it by
+	 * calculating how many datasets are expected and fixing the data so
+	 * that mean values (and minima!) correctly reflect the frames with
+	 * inactivity.
+	 * 'interval_length' is the length of the intervals in the source data,
+	 * NOT any user-specified interval!
+	 */
+	void replace_missing_datasets(int interval_length);
+
+	/**
+	 * Add structures to frame, automatically aggregates structures for
+	 * the same device if possible.
+	 */
+	void add_data(struct adapter_utilization *msg);
+	void add_data(struct ioerr_cnt *msg);
+	void add_data(struct blkiomon_stat *msg);
+	void add_data(struct zfcpdd_dstat *msg);
+
+	/**
+	 * Get start time of respective timeframe. This is the actually used
+	 * start time, which means that it is slightly earlier than the
+	 * expected start time so that messages that arrived slightly
+	 * earlier are included in this this rather than the previous frame. */
+	__u64 get_begin_time() const;
+
+	/**
+	* Get end time of respective timeframe. This is the actually used
+	* end time, which means that messages that arrived slightly
+	* later than the prediced end time might still have been included
+	* since they belong to this rather than to the next frame. */
+	__u64 get_end_time() const;
+
+	/* Get the timestamp of the frame. The timestamp
+	 * is the timestamp of the last message that was added to the frameset.
+	 */
+	__u64 get_timestamp() const;
+
+	/**
+	 * Get duration of respective timeframe */
+	__u64 get_duration() const;
+
+	/**
+	 * Set begin and end of the interval, as well as the predicted
+	 * timestamp of the last interval where all the messages should have
+	 * arrived */
+	void set_timeframe(__u64 begin, __u64 end, __u64 timestamp);
+
+	/** Retrieve ioerr result.
+	 * WARNING: Memory ownership remains in class - copy if necessary!
+	* FURTHER WARNING: Some of the pointers can be NULL!
+	 */
+	const vector<struct ioerr_cnt*>& get_ioerr_stats() const;
+
+	/** Retrieve zfcpdd result.
+	*  Can be NULL.
+	 */
+	const struct zfcpdd_dstat* get_first_zfcpdd_stat() const;
+
+	/** Retrieve blkiomon result.
+	 *  Can be NULL.
+	 */
+	const struct blkiomon_stat* get_first_blkiomon_stat() const;
+
+	const struct adapter_utilization* get_utilization_stat_by_host_id(__u32 h_id) const;
+
+	const struct adapter_utilization* get_utilization_stat_by_chpid(__u32 chpid) const;
+
+	const struct adapter_utilization* get_utilization_stat_by_devno(__u32 devno) const;
+
+	const struct ioerr_cnt* get_ioerr_stat_by_chpid(__u32 chpid) const;
+
+	const struct ioerr_cnt* get_ioerr_stat_by_devno(__u32 devno) const;
+
+	const struct blkiomon_stat* get_blkiomon_stat_by_chpid(__u32 chpid) const;
+
+	const struct blkiomon_stat* get_blkiomon_stat_by_devno(__u32 devno) const;
+
+	const struct blkiomon_stat* get_blkiomon_stat_by_wwpn(__u64 wwpn) const;
+
+	const struct blkiomon_stat* get_blkiomon_stat_by_mp_mm(__u32 mp_mm) const;
+
+	const struct blkiomon_stat* get_blkiomon_stat_by_mm(__u32 mm) const;
+
+	const struct zfcpdd_dstat* get_zfcpdd_stat_by_chpid(__u32 chpid) const;
+
+	const struct zfcpdd_dstat* get_zfcpdd_stat_by_devno(__u32 devno) const;
+
+	const struct zfcpdd_dstat* get_zfcpdd_stat_by_wwpn(__u64 wwpn) const;
+
+	const struct zfcpdd_dstat* get_zfcpdd_stat_by_mp_mm(__u32 mp_mm) const;
+
+	const struct zfcpdd_dstat* get_zfcpdd_stat_by_mm(__u32 mm) const;
+
+	void set_aggregated(bool aggr);
+
+	/** Is the frameset built from the .agg file? */
+	bool is_aggregated() const;
+
+	/** Query whether the frameset holds data or not */
+	bool is_empty() const;
+
+protected:
+	struct utilization_wrapper {
+		/// number aggregated datasets
+		int			 	 counter;
+		struct adapter_utilization	*stat;
+	};
+
+	struct zfcpdd_wrapper {
+		/// number aggregated datasets
+		int			 counter;
+		struct zfcpdd_dstat	*stat;
+	};
+
+private:
+	bool m_empty;
+
+	void normalize_util_stat(struct utilization_stats *stats);
+
+	void normalize_stat(struct abbrev_stat *stat, __u64 count);
+
+	/// rescale zfcpdd_dstat->channel_latency from ns to us
+	void normalize_zfcpdd_stat(struct zfcpdd_dstat *stat);
+
+	void init_utilization_wrapper(struct utilization_wrapper *wrp);
+
+	void init_zfcpdd_wrapper(struct zfcpdd_wrapper *wrp);
+
+	void add_zero_frames(struct utilization_wrapper *wrp,
+			     int num_expected, int interval_length);
+
+	int get_by_chpid(__u32 chpid) const;
+
+	int get_by_devno(__u32 devno) const;
+
+	int get_by_mp_mm(__u32 mp_mm) const;
+
+	int get_by_mm(__u32 mm) const;
+
+	int get_by_wwpn(__u64 wwpn) const;
+
+	/// returns index of 'val' as found in 'lst', <0 otherwise
+	int find_index(const list<__u32> &lst, __u32 val) const;
+
+	/// returns index of 'val' as found in 'lst', <0 otherwise
+	int find_index(const list<__u64> &lst, __u64 val) const;
+
+	/// zfcpdd statistics, ordered by host adapter no (ascending)
+	vector<struct utilization_wrapper>	m_util_stats;
+
+	/** ioerror stats, ordered by device identifiers
+	 * (hierarchical & ascending) */
+	vector<struct ioerr_cnt*>		m_ioerr_stats;
+
+	/// zfcpdd statistics, ordered by device (ascending)
+	vector<struct zfcpdd_wrapper>		m_zfcpdd_stats;
+	/// zfcpdd statistics, ordered by device (ascending)
+	vector<struct blkiomon_stat*>		m_blkiomon_stats;
+	/// begin of the frame
+	__u64					m_start_time;
+	/// end of the frame
+	__u64					m_end_time;
+	/// timestamp of latest message added to the set
+	__u64					m_timestamp;
+	/// indicates whether the frameset is from the .agg file
+	bool					m_aggregated;
+
+	/** provides translation from data objects to (vector) index
+	  * This member is kept throughout, even survives calls to reinit(). */
+	const Collapser			       *m_collapser;
+
+	/** normalize all incoming datasets so they are equally weighted
+	 * when aggregating over multiple frames */
+	bool					m_normalize;
+};
+
+
+#endif
+
Index: s390-tools-sles11/ziomon/ziorep_printers.cpp
===================================================================
--- /dev/null
+++ s390-tools-sles11/ziomon/ziorep_printers.cpp
@@ -0,0 +1,1254 @@
+/*
+ * FCP report generators
+ *
+ * Utility classes to print framsets
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Stefan Raspl <raspl@linux.vnet.ibm.com>
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <math.h>
+
+extern "C" {
+	#include "ziomon_tools.h"
+	#include "stats.h"
+}
+#include "ziorep_printers.hpp"
+#include "ziorep_utils.hpp"
+
+extern const char *toolname;
+extern int verbose;
+
+
+#define ZIOREP_PRINTERS_MAX_QUEUE_LEN		128.
+
+
+
+Printer::Printer(const ConfigReader *cfg, bool csv_mode)
+: m_cfg(cfg), m_csv(csv_mode), m_prev_day(-1)
+{
+	if (m_csv)
+		m_delim = ',';
+	else
+		m_delim = ' ';
+
+	memset(&m_util, 0, sizeof(struct adapter_utilization));
+	m_util.valid = 1;
+	memset(&m_util_invalid, 0, sizeof(struct adapter_utilization));
+	m_util_invalid.valid = 0;
+	memset(&m_ioerr, 0, sizeof(struct ioerr_cnt));
+	memset(&m_dstat, 0, sizeof(struct zfcpdd_dstat));
+	memset(&m_stat, 0, sizeof(struct blkiomon_stat));
+}
+
+
+bool Printer::print_csv() const
+{
+	return m_csv;
+}
+
+
+void Printer::print_timestamp(FILE *fp, const Frameset &frameset)
+{
+	time_t t = frameset.get_end_time();
+	struct tm *tm = localtime(&t);
+
+	if (m_csv) {
+		fprintf(fp, print_time_formatted(frameset.get_timestamp()));
+		print_delimiter(fp);
+		fprintf(fp, "%d", frameset.is_aggregated());
+		print_delimiter(fp);
+	}
+	else {
+		if (m_prev_day < 0 || m_prev_day != tm->tm_mday) {
+			fprintf(fp, "%s",
+				print_time_formatted(frameset.get_timestamp()));
+			m_prev_day = tm->tm_mday;
+		}
+		else
+			fprintf(fp, "%s",
+				print_time_formatted_short(frameset.get_timestamp()));
+
+		if (frameset.is_aggregated())
+			fprintf(fp, " Aggregated Frame\n");
+		else
+			fputc('\n', fp);
+	}
+}
+
+
+void Printer::determine_mag_factor(int leading_places, char *magnitude,
+				   double *factor) const
+{
+	if (leading_places < 7) {
+		*magnitude = 'K';
+		*factor = 1000;
+	}
+	else if (leading_places < 10) {
+		*magnitude = 'M';
+		*factor = 1000000;
+	}
+	else if (leading_places < 13) {
+		*magnitude = 'G';
+		*factor = 1000000000;
+	}
+	else {
+		// this is quite expensive, hence we avoid if possible
+		*factor = pow(1000, leading_places/3);
+		if (leading_places < 16)
+			*magnitude = 'T';
+		else if (leading_places < 19)
+			*magnitude = 'P';
+		else {
+			assert(false);	// would be surprising if
+			*magnitude = '?';
+		}
+	}
+}
+
+
+void Printer::determine_digs(int leading_places, int max_digs,
+			     int *num_full_digs, int *num_places) const
+{
+	*num_full_digs = leading_places % 3;
+	if (*num_full_digs == 0)
+		*num_full_digs = 3;
+	*num_places = max_digs - *num_full_digs - 2;
+	if (*num_places <= 0) {
+		*num_places = 0;
+		*num_full_digs = max_digs - 1;
+	}
+}
+
+
+void Printer::print_abbreviated(FILE *fp, double num, int leading_places,
+			       int max_digs) const
+{
+	int num_full_digs;
+	int num_places;
+	double factor;
+	int rc;
+	char magnitude;
+
+	determine_digs(leading_places, max_digs, &num_full_digs, &num_places);
+
+	determine_mag_factor(leading_places, &magnitude, &factor);
+
+	double tmp = num / factor;
+	if (tmp + 5 * pow(10, -1 - num_places) >= pow(10, num_full_digs)) {
+		if (num_places > 0)
+			// just strip down one decimal place,
+			// e.g. 9.96... with 1.1 format would result in
+			// 10.0 otherwise
+			num_places--;
+		else {
+			// indicate that we need one more leading place
+			// e.g. 999.872 with 3.0 format digits would result
+			// in 1.000K otherwise
+			leading_places++;
+			determine_digs(leading_places, max_digs, &num_full_digs, &num_places);
+			determine_mag_factor(leading_places, &magnitude, &factor);
+			tmp = num / factor;
+		}
+	}
+
+	rc = fprintf(fp, "%*.*lf%c", max_digs - 1, num_places, num / factor, magnitude);
+
+	assert(rc <= max_digs);
+}
+
+
+void Printer::print_abbrev_num(FILE *fp, __u64 num, int max_digs) const
+{
+	int rc = 0;
+	char tmp[128];
+
+	if (m_csv) {
+		fprintf(fp, "%lld", (long long int)num);
+		return;
+	}
+
+	/* print into a tmp buffer first - if buf is too small we might
+	   write out of bounds otherwise! */
+	rc = sprintf(tmp, "%Lu", (long long unsigned int)num);
+	if (rc < max_digs)
+		fprintf(fp, "%*Lu", max_digs, (long long unsigned int)num);
+	else
+		print_abbreviated(fp, num, rc, max_digs);
+}
+
+
+void Printer::print_abbrev_num(FILE *fp, __u32 num, int max_digs) const
+{
+	return print_abbrev_num(fp, (__u64)num, max_digs);
+}
+
+
+void Printer::print_abbrev_num(FILE *fp, double num, int max_digs,
+			       bool more_places) const
+{
+	int  rc = 0;
+
+	assert(max_digs > 2);
+
+	if (m_csv) {
+		fprintf(fp, "%lf", num);
+		return;
+	}
+
+	/* print into a tmp buffer first - if buf is too small we might
+	   write out of bounds otherwise! */
+	if (num == 0)
+		fprintf(fp, "%*.1lf", max_digs, 0.);
+	else if (num < 1 && more_places)
+		fprintf(fp, "%.*lf", max_digs - 2, num);
+	else if (num < pow(10, max_digs - 2))
+		fprintf(fp, "%*.1lf", max_digs, num);
+	else
+		print_abbreviated(fp, num, rc, max_digs);
+}
+
+
+/**
+ * Always print doubles using 6 characters: 5 for a 3.1 format and
+ * 1 for an optional character giving the magintude. */
+void Printer::print_abbrev_num(FILE *fp, double num) const
+{
+	int  rc = 0;
+	char tmp[128];
+
+	if (m_csv) {
+		fprintf(fp, "%.1lf", num);
+		return;
+	}
+
+	if (num == 0) {
+		fprintf(fp, "  0.0 ");
+		return;
+	}
+
+	/* print into a tmp buffer first - if buf is too small we might
+	   write out of bounds otherwise! */
+	rc = sprintf(tmp, "%lld", (long long int)num);
+	if (rc < 4)
+		fprintf(fp, "%5.1lf ", num);
+	else
+		print_abbreviated(fp, num, rc, 6);
+}
+
+
+const struct adapter_utilization* Printer::get_empty_utilization(__u32 host_id)
+{
+	m_util.adapter_no = host_id;
+
+	return &m_util;
+}
+
+const struct adapter_utilization* Printer::get_invalid_utilization(__u32 host_id)
+{
+	m_util_invalid.adapter_no = host_id;
+
+	return &m_util_invalid;
+}
+
+const struct ioerr_cnt* Printer::get_empty_ioerr(struct hctl_ident *identifier)
+{
+	if (identifier)
+		m_ioerr.identifier = *identifier;
+	else
+		memset(&m_ioerr.identifier, 0, sizeof(struct hctl_ident));
+
+	return &m_ioerr;
+}
+
+
+const struct zfcpdd_dstat* Printer::get_empty_zfcpdd_dstat(__u32 device)
+{
+	m_dstat.device = device;
+
+	return &m_dstat;
+}
+
+
+const struct blkiomon_stat* Printer::get_empty_blkiomon_stat(__u32 device)
+{
+	m_stat.device = device;
+
+	return &m_stat;
+}
+
+
+void Printer::print_delimiter(FILE *fp)
+{
+	fputc(m_delim, fp);
+}
+
+void Printer::print_invalid(FILE *fp, int width)
+{
+	if (m_csv)
+		fputc('-', fp);
+	else
+		fprintf(fp, "%*c", width, '-');
+}
+
+void PhysAdapterPrinter::print_phys_adpt(FILE *fp, __u32 host_id, int *rc)
+{
+	__u32 chpid = m_cfg->get_chpid_by_host_id(host_id, rc);
+
+	if (m_csv)
+		fprintf(fp, "%x", chpid);
+	else
+		fprintf(fp, "%3x", chpid);
+}
+
+
+void PhysAdapterPrinter::print_utilization(FILE *fp,
+					  const struct abbrev_stat *stat,
+					  __u64 count, bool valid)
+{
+	__u64 tmp64;
+
+	print_delimiter(fp);
+	if (valid) {
+		tmp64 = 0;
+		if (count)
+			tmp64 = stat->min;
+		print_abbrev_num(fp, tmp64, 3);
+	}
+	else
+		print_invalid(fp, 3);
+
+
+	print_delimiter(fp);
+	if (valid) {
+		tmp64 = 0;
+		if (count)
+			tmp64 = stat->max;
+		print_abbrev_num(fp, tmp64, 3);
+	}
+	else
+		print_invalid(fp, 3);
+
+	print_delimiter(fp);
+	if (valid) {
+		double tmp = 0;
+		if (count)
+			tmp = calc_avg(stat->sum, count);
+		print_abbrev_num(fp, tmp, 5);
+	}
+	else
+		print_invalid(fp, 5);
+}
+
+
+PhysAdapterPrinter::PhysAdapterPrinter(const ConfigReader *cfg,
+				       bool csv_mode)
+: Printer(cfg, csv_mode)
+{
+}
+
+
+void PhysAdapterPrinter::print_topline(FILE *fp, const Frameset &frameset)
+{
+	if (m_csv)
+		fprintf(fp, "timestamp,aggregated,CHPID,adapter min %%,"
+			"adapter max %%,adapter avg %%,bus min %%,bus max %%,"
+			"bus avg %%,cpu min %%,cpu max %%,cpu avg %%\n");
+	else {
+		fprintf(fp, "CHP|adapter in %%-|--bus in %%---|--cpu in %%---|\n");
+		fprintf(fp, " ID min max   avg min max   avg min max   avg\n");
+	}
+}
+
+
+int PhysAdapterPrinter::print_frame(FILE *fp,
+				    const Frameset &frameset,
+				    const DeviceFilter &dev_filt)
+{
+	int lrc = 0;
+	bool timestamp_printed = false;
+	const struct adapter_utilization *util;
+	list<__u32> host_ids = dev_filt.get_host_id_list();
+
+	assert(frameset.get_collapser()->get_criterion() == none);
+	for (list<__u32>::iterator i = host_ids.begin();
+	      i != host_ids.end(); ++i) {
+		if (!timestamp_printed) {
+			print_timestamp(fp, frameset);
+			if (!m_csv)
+				// print timestamp for every line in CSV mode
+				timestamp_printed = true;
+		}
+		print_phys_adpt(fp, *i, &lrc);
+		if (lrc)
+			return -1;
+		util = frameset.get_utilization_stat_by_host_id(*i);
+		if (!util)
+			util = get_empty_utilization(*i);
+		print_utilization(fp, &util->stats.adapter,
+				  util->stats.count,
+				  util->valid);
+		print_utilization(fp, &util->stats.bus,
+				  util->stats.count,
+				  util->valid);
+		print_utilization(fp, &util->stats.cpu,
+				  util->stats.count,
+				  util->valid);
+		fputc('\n', fp);
+	}
+
+	return 0;
+}
+
+
+VirtAdapterPrinter::VirtAdapterPrinter(const ConfigReader *cfg,
+				       bool csv_mode)
+: Printer(cfg, csv_mode)
+{
+}
+
+
+void VirtAdapterPrinter::print_virt_adpt(FILE *fp, __u32 devno,
+					int *rc)
+{
+	if (m_csv)
+		fprintf(fp, "%x,0.0.%04x,",
+			       m_cfg->get_chpid_by_devno(devno, rc),
+			       devno);
+	else
+		fprintf(fp, "%3x/0.0.%04x",
+			       m_cfg->get_chpid_by_devno(devno, rc),
+			       devno);
+}
+
+void VirtAdapterPrinter::print_queue_fill(FILE *fp,
+					  const struct zfcpdd_dstat *stat,
+					  const struct adapter_utilization *res)
+{
+	double tmp;
+
+	tmp = 0;
+	if (stat && stat->count)
+		tmp = (stat->outb_max * 100)/ZIOREP_PRINTERS_MAX_QUEUE_LEN;
+	print_delimiter(fp);
+	print_abbrev_num(fp, tmp, 5);
+
+	tmp = 0;
+	if (res && res->valid)
+		tmp = res->stats.queue_util_integral
+			/ (double)res->stats.queue_util_interval;
+	print_delimiter(fp);
+	print_abbrev_num(fp, tmp, 5);
+}
+
+
+void VirtAdapterPrinter::print_queue_full(FILE *fp,
+					 const struct adapter_utilization *res)
+{
+	__u32 val = 0;
+
+	if (res) {
+		if (res->valid)
+			val = res->stats.queue_full;
+		else {
+			print_invalid(fp, 4);
+			return;
+		}
+	}
+	print_delimiter(fp);
+	print_abbrev_num(fp, val, 4);
+}
+
+
+void VirtAdapterPrinter::print_failures(FILE *fp, const struct ioerr_cnt *cnt)
+{
+	print_delimiter(fp);
+	if (cnt)
+		print_abbrev_num(fp, cnt->num_ioerr, 4);
+	else
+		print_abbrev_num(fp, (__u32)0, 4);
+}
+
+void VirtAdapterPrinter::print_throughput(FILE *fp,
+					 const struct blkiomon_stat *stat)
+{
+	double tmp;
+
+	if (!stat || stat->d2c_r.num <= 0 || stat->size_r.num <= 0)
+		tmp = 0;
+	else
+		tmp = stat->size_r.sum/(double)stat->d2c_r.sum;
+	print_delimiter(fp);
+	print_abbrev_num(fp, tmp);
+
+	if (!stat || stat->d2c_w.num <= 0 || stat->size_w.num <= 0)
+		tmp = 0;
+	else
+		tmp = stat->size_w.sum/(double)stat->d2c_w.sum;
+	print_delimiter(fp);
+	print_abbrev_num(fp, tmp);
+}
+
+
+void VirtAdapterPrinter::print_num_requests(FILE *fp, const struct blkiomon_stat *stat)
+{
+	print_delimiter(fp);
+	if (stat)
+		print_abbrev_num(fp, stat->size_r.num, 4);
+	else
+		print_abbrev_num(fp, (__u64)0, 4);
+
+	print_delimiter(fp);
+	if (stat)
+		print_abbrev_num(fp, stat->size_w.num, 4);
+	else
+		print_abbrev_num(fp, (__u64)0, 4);
+}
+
+
+void VirtAdapterPrinter::print_topline(FILE *fp,
+				       const Frameset &frameset)
+{
+	if (m_csv)
+		fprintf(fp, "timestamp,aggregated,CHPID,Bus-ID,qdio utilization max %%,qdio utilization avg %%,queue full,fail erc,throughput read / MS/s,throughput write / MS/s,I/O requests read,I/O requqests write\n");
+	else {
+		fprintf(fp, "CHP Bus-ID  |qdio util.%%|queu|fail|-thp in MB/s-|I/O reqs-|\n");
+		fprintf(fp, " ID            max   avg full  erc     rd    wrt   rd  wrt\n");
+	}
+}
+
+
+int VirtAdapterPrinter::print_frame(FILE *fp,
+				    const Frameset &frameset,
+				    const DeviceFilter &dev_filt)
+{
+	int lrc = 0;
+	bool timestamp_printed = false;
+	const struct adapter_utilization 	*util;
+	const struct ioerr_cnt			*ioerr;
+	const struct blkiomon_stat		*blk_stat;
+	const struct zfcpdd_dstat		*zfcp_stat;
+
+	list<__u32> devnos;
+	devnos = ((StagedDeviceFilter*)&dev_filt)->get_filter_devnos();
+
+	assert(frameset.get_collapser()->get_criterion() == devno);
+
+	for (list<__u32>::iterator i = devnos.begin(); i != devnos.end(); ++i) {
+		if (!timestamp_printed) {
+			print_timestamp(fp, frameset);
+			if (!m_csv)
+				// print timestamp for every line in CSV mode
+				timestamp_printed = true;
+		}
+
+		util = frameset.get_utilization_stat_by_devno(*i);
+		ioerr = frameset.get_ioerr_stat_by_devno(*i);
+		blk_stat = frameset.get_blkiomon_stat_by_devno(*i);
+		zfcp_stat = frameset.get_zfcpdd_stat_by_devno(*i);
+		print_virt_adpt(fp, *i, &lrc);
+		print_queue_fill(fp, zfcp_stat, util);
+		print_queue_full(fp, util);
+		print_failures(fp, ioerr);
+		print_throughput(fp, blk_stat);
+		print_num_requests(fp, blk_stat);
+		if (lrc) {
+			fprintf(stderr, "%s: Did not find matching data in"
+				" .cfg file. Please check if it matches your"
+				" data and try again.\n", toolname);
+			return -1;
+		}
+		fputc('\n', fp);
+	}
+
+	return 0;
+}
+
+
+
+TrafficPrinter::TrafficPrinter(const ConfigReader *cfg, Collapser &col,
+			       bool csv_mode)
+: Printer(cfg, csv_mode), m_mp_whitespace(NULL), m_mp_topline_pref1(NULL),
+	m_mp_topline_pref2(NULL)
+{
+	m_agg_crit = col.get_criterion();
+
+	if (m_agg_crit == multipath_device) {
+		if (m_csv) {
+			m_mp_topline_pref1 = (char*)malloc(25);
+			sprintf(m_mp_topline_pref1, "Multipath Device");
+		}
+		else {
+			int rc = 0;
+			int max_len = 32;
+			int len;
+			int corrector = 0;
+			const list<__u32> lst =
+				((AggregationCollapser*)&col)->get_reference_mp_mms();
+			for (list<__u32>::const_iterator i = lst.begin();
+			      i != lst.end(); ++i) {
+				len = strlen(cfg->get_multipath_by_mp_mm(*i, &rc));
+				assert(rc == 0);
+				if (len > max_len)
+					max_len = len;
+			}
+			m_mp_whitespace = (char*)malloc(max_len + 1);
+			m_mp_topline_pref1 = (char*)malloc(max_len + 1);
+			m_mp_topline_pref2 = (char*)malloc(max_len + 1);
+			sprintf(m_mp_whitespace, "%*c", max_len - 1, ' ');
+			corrector = (max_len - 9) % 2;
+			len = (max_len - 9) / 2;
+			sprintf(m_mp_topline_pref1, "%*cMultipath%*c",
+				len, ' ', len + corrector, ' ');
+			corrector = 1 - corrector; // invert
+			len = (max_len - 6) / 2;
+			sprintf(m_mp_topline_pref2, "%*cdevice%*c",
+				len, ' ', len + corrector, ' ');
+		}
+	}
+}
+
+
+TrafficPrinter::~TrafficPrinter()
+{
+	free(m_mp_whitespace);
+	free(m_mp_topline_pref1);
+	free(m_mp_topline_pref2);
+}
+
+
+void TrafficPrinter::print_topline_prefix1(FILE *fp)
+{
+	const char *str = NULL;
+
+	switch (m_agg_crit) {
+	case none:
+		if (m_csv)
+			str = "WWPN,LUN";
+		else
+			str = "       WWPN                LUN       ";
+		break;
+	case chpid:
+		if (m_csv)
+			str = "CHPID";
+		else
+			str = "CHP";
+		break;
+	case devno:
+		if (m_csv)
+			str = "Bus-ID";
+		else
+			str = " Bus-ID ";
+		break;
+	case wwpn:
+		if (m_csv)
+			str = "WWPN";
+		else
+			str = "       WWPN       ";
+		break;
+	case multipath_device:
+		str = m_mp_topline_pref1;
+		break;
+	case all:
+		str = " * ";
+		break;
+	}
+
+	fprintf(fp, "%s", str);
+}
+
+void TrafficPrinter::print_topline_prefix2(FILE *fp)
+{
+	const char *str = NULL;
+
+	switch (m_agg_crit) {
+	case none:
+		str = "                                     ";
+		break;
+	case chpid:
+		str = " ID";
+		break;
+	case devno:
+		str = "        ";
+		break;
+	case wwpn:
+		str = "                  ";
+		break;
+	case multipath_device:
+		str = m_mp_topline_pref2;
+		break;
+	case all:
+		str = "   ";
+		break;
+	}
+
+	fprintf(fp, "%s", str);
+}
+
+void TrafficPrinter::print_topline_whitespace(FILE *fp)
+{
+	const char *str = NULL;
+
+	switch (m_agg_crit) {
+	case none:
+		str = "                                     ";
+		break;
+	case chpid:
+		str = "   ";
+		break;
+	case devno:
+		str = "        ";
+		break;
+	case wwpn:
+		str = "                  ";
+		break;
+	case multipath_device:
+		str = m_mp_whitespace;
+		break;
+	case all:
+		str = "   ";
+		break;
+	}
+
+	fprintf(fp, "%s", str);
+}
+
+void TrafficPrinter::get_device_list(list<__u32> &lst,
+				     const DeviceFilter &dev_filt)
+{
+	switch (m_agg_crit) {
+	case none:
+	case all:
+		lst = dev_filt.get_mm_list();
+		break;
+	case devno:
+		lst = ((StagedDeviceFilter*)&dev_filt)->get_filter_devnos();
+		break;
+	case multipath_device:
+		lst = ((StagedDeviceFilter*)&dev_filt)->get_filter_mp_mms();
+		break;
+	case chpid:
+		lst = ((StagedDeviceFilter*)&dev_filt)->get_filter_chpids();
+		break;
+	default:
+		assert(false);
+	}
+}
+
+
+void TrafficPrinter::get_device_list(list<__u64> &lst,
+				     const DeviceFilter &dev_filt)
+{
+	assert(m_agg_crit == wwpn);
+	lst = ((StagedDeviceFilter*)&dev_filt)->get_filter_wwpns();
+}
+
+
+void TrafficPrinter::print_device_wwpn(FILE *fp, __u64 wwpn)
+{
+	fprintf(fp, "0x%016Lx", (long long unsigned int)wwpn);
+}
+
+void TrafficPrinter::print_device_chpid(FILE *fp, __u32 chpid)
+{
+	if (m_csv)
+		fprintf(fp, "%x", chpid);
+	else
+		fprintf(fp, "%3x", chpid);
+}
+
+void TrafficPrinter::print_device_devno(FILE *fp, __u32 devno)
+{
+	fprintf(fp, "0.0.%04x", devno);
+}
+
+void TrafficPrinter::print_device_mp_mm(FILE *fp, __u32 mp_mm,
+				       const ConfigReader &cfg, int *rc)
+{
+	if (m_csv)
+		fprintf(fp, "%s", cfg.get_multipath_by_mp_mm(mp_mm, rc));
+	else
+		fprintf(fp, "%16s", cfg.get_multipath_by_mp_mm(mp_mm, rc));
+}
+
+void TrafficPrinter::print_device(FILE *fp, __u32 dev,
+				 const ConfigReader &cfg, int *rc)
+{
+	if (m_csv)
+		fprintf(fp, "0x%016Lx,0x%016Lx",
+			       (long long unsigned int)cfg.get_wwpn_by_mm_internal(dev, rc),
+			       (long long unsigned int)cfg.get_lun_by_mm_internal(dev, rc));
+	else
+		fprintf(fp, "0x%016Lx:0x%016Lx",
+			       (long long unsigned int)cfg.get_wwpn_by_mm_internal(dev, rc),
+			       (long long unsigned int)cfg.get_lun_by_mm_internal(dev, rc));
+}
+
+void TrafficPrinter::print_device_all(FILE *fp)
+{
+	if (m_csv)
+		fprintf(fp, "*");
+	else
+		fprintf(fp, " * ");
+}
+
+int TrafficPrinter::print_frame(FILE *fp, const Frameset &frameset,
+				       const DeviceFilter &dev_filt)
+{
+	int rc = 0;
+	bool timestamp_printed = false;
+	list<__u32> lst_32;
+	list<__u64> lst_64;
+	const struct blkiomon_stat	*blk_stat = NULL;
+	const struct zfcpdd_dstat	*zfcp_stat = NULL;
+	const AggregationCollapser *agg_col;
+
+	switch (m_agg_crit) {
+	case none:
+		get_device_list(lst_32, dev_filt);
+		break;
+	case chpid:
+		agg_col = (AggregationCollapser*)frameset.get_collapser();
+		lst_32 = agg_col->get_reference_chpids();
+		break;
+	case devno:
+		agg_col = (AggregationCollapser*)frameset.get_collapser();
+		lst_32 = agg_col->get_reference_devnos();
+		break;
+	case wwpn:
+		agg_col = (AggregationCollapser*)frameset.get_collapser();
+		lst_64 = agg_col->get_reference_wwpns();
+		break;
+	case multipath_device:
+		agg_col = (AggregationCollapser*)frameset.get_collapser();
+		lst_32 = agg_col->get_reference_mp_mms();
+		break;
+	case all:
+		break;
+	}
+
+	if (m_agg_crit == wwpn) {
+		for (list<__u64>::const_iterator i = lst_64.begin();
+		      i != lst_64.end(); ++i) {
+			if (!timestamp_printed) {
+				print_timestamp(fp, frameset);
+				if (!m_csv)
+					// print timestamp for every line in CSV mode
+					timestamp_printed = true;
+			}
+
+			blk_stat = frameset.get_blkiomon_stat_by_wwpn(*i);
+			zfcp_stat = frameset.get_zfcpdd_stat_by_wwpn(*i);
+			print_device_wwpn(fp, *i);
+			print_data_row(fp, blk_stat, zfcp_stat);
+		}
+	}
+	else if (m_agg_crit == all) {
+		print_timestamp(fp, frameset);
+
+		blk_stat = frameset.get_first_blkiomon_stat();
+		zfcp_stat = frameset.get_first_zfcpdd_stat();
+		print_device_all(fp);
+		print_data_row(fp, blk_stat, zfcp_stat);
+	}
+	else {
+		for (list<__u32>::const_iterator i = lst_32.begin();
+		      i != lst_32.end(); ++i) {
+			if (!timestamp_printed) {
+				print_timestamp(fp, frameset);
+				if (!m_csv)
+					// print timestamp for every line in CSV mode
+					timestamp_printed = true;
+			}
+
+			switch (m_agg_crit) {
+			case none:
+				blk_stat = frameset.get_blkiomon_stat_by_mm(*i);
+				zfcp_stat = frameset.get_zfcpdd_stat_by_mm(*i);
+				print_device(fp, *i, *m_cfg, &rc);
+				break;
+			case devno:
+				blk_stat = frameset.get_blkiomon_stat_by_devno(*i);
+				zfcp_stat = frameset.get_zfcpdd_stat_by_devno(*i);
+				print_device_devno(fp, *i);
+				break;
+			case multipath_device:
+				blk_stat = frameset.get_blkiomon_stat_by_mp_mm(*i);
+				zfcp_stat = frameset.get_zfcpdd_stat_by_mp_mm(*i);
+				print_device_mp_mm(fp, *i, *m_cfg, &rc);
+				break;
+			case chpid:
+				blk_stat = frameset.get_blkiomon_stat_by_chpid(*i);
+				zfcp_stat = frameset.get_zfcpdd_stat_by_chpid(*i);
+				print_device_chpid(fp, *i);
+				break;
+			default:
+				assert(false);
+			}
+			if (rc )
+				return -1;
+			print_data_row(fp, blk_stat, zfcp_stat);
+		}
+	}
+
+	return 0;
+}
+
+
+SummaryTrafficPrinter::SummaryTrafficPrinter(const ConfigReader *cfg,
+					     Collapser &col,
+					     bool csv_mode)
+: TrafficPrinter(cfg, col, csv_mode)
+{
+}
+
+
+void SummaryTrafficPrinter::print_topline(FILE *fp, const Frameset &frameset)
+{
+	if (m_csv) {
+		fprintf(fp, "timestamp,aggregated,");
+		print_topline_prefix1(fp);
+		fprintf(fp, ",I/O rate in MB/s min,I/O rate in MB/s max,throughput in MB/s avg,throughput var,#I/O requests total,#I/O requests rd,"
+			"#I/O requests wrt,#I/O requests bidi,#I/O subsystem latency in us min,#I/O subsystem latency in us max,"
+			"#I/O subsystem latency in us avg,#I/O subsystem latency var,channel latency in us min,channel latency in us max,"
+			"channel latency in us avg,channel latency var,fabric latency in us min,fabric latency in us max,fabric latency in us avg,"
+			"fabric latency var\n");
+	}
+	else {
+		print_topline_prefix1(fp);
+		fprintf(fp, "|I/O rt MB/s|thrp in MB/s-|----I/O requests----|-I/O subs. lat. in us--|--channel lat. in us---|---fabric lat. in us---|\n");
+		print_topline_prefix2(fp);
+		fprintf(fp, "   min   max    avg  stdev #reqs   rd  wrt bidi  min  max    avg  stdev  min  max    avg  stdev  min  max    avg  stdev\n");
+	}
+}
+
+
+void SummaryTrafficPrinter::print_throughput(FILE *fp, const struct blkiomon_stat *stat)
+{
+	struct minmax thrp_data, total_size, total_latency;
+	double tmp;
+
+	minmax_init(&thrp_data);
+	minmax_init(&total_size);
+	minmax_init(&total_latency);
+
+	if (stat) {
+		minmax_merge(&thrp_data, &stat->thrput_r);
+		minmax_merge(&thrp_data, &stat->thrput_w);
+		minmax_merge(&total_size, &stat->size_r);
+		minmax_merge(&total_size, &stat->size_w);
+		minmax_merge(&total_latency, &stat->d2c_r);
+		minmax_merge(&total_latency, &stat->d2c_w);
+	}
+
+	tmp = 0;
+	if (stat)
+		tmp = thrp_data.min / 1000.;
+	print_delimiter(fp);
+	if (tmp < 1)
+		print_abbrev_num(fp, tmp, 5, true);
+	else
+		print_abbrev_num(fp, tmp, 5);
+
+	tmp = 0;
+	if (stat)
+		tmp = ((double)(thrp_data.max)) / 1000.;
+	print_delimiter(fp);
+	if (tmp < 1)
+		print_abbrev_num(fp, tmp, 5, false);
+	else
+		print_abbrev_num(fp, tmp, 5);
+
+	tmp = 0;
+	if (stat && total_size.sum > 0)
+		tmp = calc_avg(total_size.sum, total_latency.sum);
+	print_delimiter(fp);
+	print_abbrev_num(fp, tmp);
+
+	tmp = 0;
+	if (stat && total_size.sum > 0)
+		tmp = calc_std_dev(total_size.sum, total_size.sos,
+					total_latency.sum);
+	print_delimiter(fp);
+	print_abbrev_num(fp, tmp);
+}
+
+void SummaryTrafficPrinter::print_request_stats(FILE *fp,
+					const struct blkiomon_stat *stat)
+{
+	__u64 val;
+
+	val = 0;
+	if (stat)
+		val = stat->bidir + stat->thrput_r.num + stat->thrput_w.num;
+	print_delimiter(fp);
+	print_abbrev_num(fp, val, 5);
+
+	val = 0;
+	if (stat)
+		val = stat->size_r.num;
+	print_delimiter(fp);
+	print_abbrev_num(fp, val, 4);
+
+	val = 0;
+	if (stat)
+		val = stat->size_w.num;
+	print_delimiter(fp);
+	print_abbrev_num(fp, val, 4);
+
+	val = 0;
+	if (stat)
+		val = stat->bidir;
+	print_delimiter(fp);
+	print_abbrev_num(fp, val, 4);
+}
+
+
+void SummaryTrafficPrinter::print_latency(FILE *fp,
+					 const struct minmax *data)
+{
+	__u64 tmp64;
+	double tmplf;
+
+	tmp64 = 0;
+	if (data && data->num > 0)
+		tmp64 = data->min;
+	print_delimiter(fp);
+	print_abbrev_num(fp, tmp64, 4);
+
+	tmp64 = 0;
+	if (data && data->num > 0)
+		tmp64 = data->max;
+	print_delimiter(fp);
+	print_abbrev_num(fp, tmp64, 4);
+
+	tmplf = 0;
+	if (data && data->num > 0)
+		tmplf = minmax_avg(data);
+	print_delimiter(fp);
+	print_abbrev_num(fp, tmplf);
+
+	tmplf = 0;
+	if (data && data->num > 0)
+		tmplf = minmax_std_dev(data);
+	print_delimiter(fp);
+	print_abbrev_num(fp, tmplf);
+}
+
+void SummaryTrafficPrinter::print_latency(FILE *fp,
+					 const struct abbrev_stat *data,
+					 __u64 count)
+{
+	__u64 tmp64;
+	double tmplf;
+
+	tmp64 = 0;
+	if (count > 0)
+		tmp64 = data->min;
+	print_delimiter(fp);
+	print_abbrev_num(fp, tmp64, 4);
+
+	tmp64 = 0;
+	if (count > 0)
+		tmp64 = data->max;
+	print_delimiter(fp);
+	print_abbrev_num(fp, tmp64, 4);
+
+	tmplf = 0;
+	if (count > 0)
+		tmplf = calc_avg(data->sum, count);
+	print_delimiter(fp);
+	print_abbrev_num(fp, tmplf);
+
+	tmplf = 0;
+	if (count > 0)
+		tmplf = calc_std_dev(data->sum, data->sos, count);
+	print_delimiter(fp);
+	print_abbrev_num(fp, tmplf);
+}
+
+void SummaryTrafficPrinter::print_io_subsystem_latency(FILE *fp,
+					const struct blkiomon_stat *stat)
+{
+	struct minmax data;
+
+	minmax_init(&data);
+	if (stat) {
+		minmax_merge(&data, &stat->d2c_r);
+		minmax_merge(&data, &stat->d2c_w);
+	}
+
+	print_latency(fp, &data);
+}
+
+void SummaryTrafficPrinter::print_channel_latency(FILE *fp,
+					const struct zfcpdd_dstat *stat)
+{
+	print_latency(fp, &stat->chan_lat, stat->count);
+}
+
+void SummaryTrafficPrinter::print_fabric_latency(FILE *fp, const struct zfcpdd_dstat *stat)
+{
+	print_latency(fp, &stat->fabr_lat, stat->count);
+}
+
+void SummaryTrafficPrinter::print_data_row(FILE *fp,
+					const struct blkiomon_stat *blk_stat,
+					const struct zfcpdd_dstat *zfcp_stat)
+{
+	if (!blk_stat)
+		blk_stat = get_empty_blkiomon_stat();
+	if (!zfcp_stat)
+		zfcp_stat = get_empty_zfcpdd_dstat();
+
+	print_throughput(fp, blk_stat);
+	print_request_stats(fp, blk_stat);
+	print_io_subsystem_latency(fp, blk_stat);
+	print_channel_latency(fp, zfcp_stat);
+	print_fabric_latency(fp, zfcp_stat);
+	fputc('\n', fp);
+}
+
+
+DetailedTrafficPrinter::DetailedTrafficPrinter(const ConfigReader *cfg,
+					       Collapser &col,
+					       bool csv_mode)
+: TrafficPrinter(cfg, col, csv_mode)
+{
+}
+
+
+void DetailedTrafficPrinter::print_topline(FILE *fp, const Frameset &frameset)
+{
+	if (m_csv) {
+		fprintf(fp, "timestamp,aggregated,");
+		print_topline_prefix1(fp);
+		fprintf(fp, ",I/O requests 0KB,I/O requests <1KB,I/O requests <2KB,I/O requests <4KB,I/O requests <8KB,"
+			"I/O requests <16KB,I/O requests <32KB,I/O requests <64KB,I/O requests <128KB,I/O requests <256KB,"
+			"I/O requests <512KB,I/O requests <1MB,I/O requests <2MB,I/O requests <4MB,I/O requests <8MB,"
+			"I/O requests >=8MB,I/O subsystem latency 0us,I/O subsystem latency <8us,I/O subsystem latency <16us,"
+			"I/O subsystem latency <32us,I/O subsystem latency <64us,I/O subsystem latency <128us,"
+			"I/O subsystem latency <256us,I/O subsystem latency <512us,I/O subsystem latency <1ms,"
+			"I/O subsystem latency <2ms,I/O subsystem latency <4ms,I/O subsystem latency <8ms,"
+			"I/O subsystem latency <16ms,I/O subsystem latency <32ms,I/O subsystem latency <64ms,"
+			"I/O subsystem latency <128ms,I/O subsystem latency <256ms,I/O subsystem latency <512ms,"
+			"I/O subsystem latency <1s,I/O subsystem latency <2s,I/O subsystem latency <4s,"
+			"I/O subsystem latency <8s,I/O subsystem latency <16s,I/O subsystem latency <32s,"
+			"I/O subsystem latency >=32s,channel latency 0us,"
+			"channel latency <1us,channel latency <2us,channel latency <4us,channel latency <8us,"
+			"channel latency <16us,channel latency <32us,channel latency <64us,channel latency <128us,"
+			"channel latency <256us,channel latency <512us,channel latency <1ms,channel latency <2ms,"
+			"channel latency <4us,channel latency <8ms,channel latency <16ms,channel latency <32ms,"
+			"channel latency <64ms,channel latency <128ms,channel latency >=128ms,fabric latency 0us,"
+			"fabric latency <8us,fabric latency <16us,fabric latency <32us,fabric latency <64us,"
+			"fabric latency <128us,fabric latency <256us,fabric latency <512us,fabric latency <1ms,"
+			"fabric latency <2ms,fabric latency <4ms,fabric latency <8ms,fabric latency <16ms,"
+			"fabric latency <32ms,fabric latency <64ms,fabric latency <128ms,fabric latency <256ms,"
+			"fabric latency <512ms,fabric latency <1s,fabric latency <2s,fabric latency <4s,"
+			"fabric latency <8s,fabric latency <16s,fabric latency <32s,fabric latency >=32s\n");
+	}
+	else {
+		print_topline_whitespace(fp);
+		fprintf(fp, "|------------------------I/O request sizes in KBytes----------------------------|\n");
+		print_topline_whitespace(fp);
+		fprintf(fp, "    0    1    2    4    8   16   32   64  128  256  512   1K   2K   4K   8K  >8K\n");
+		print_topline_whitespace(fp);
+		fprintf(fp, "|------------------------I/O subsystem latency in us-------------------------------------------------------------------------|\n");
+		print_topline_whitespace(fp);
+		fprintf(fp, "    0    8   16   32   64  128  256  512   1K   2K   4K   8K  16K  32K  64K 128K 256K 512K   1M   2M   4M   8M  16M  32M >32M\n");
+		print_topline_whitespace(fp);
+		fprintf(fp, "|------------------------channel latency in us------------------------------------------------------|\n");
+		print_topline_whitespace(fp);
+		fprintf(fp, "    0    1    2    4    8   16   32   64  128  256  512   1K   2K   4K   8K  16K  32K  64K 128K>128K\n");
+		print_topline_prefix1(fp);
+		fprintf(fp, "|------------------------fabric latency in us--------------------------------------------------------------------------------|\n");
+		print_topline_prefix2(fp);
+		fprintf(fp, "    0    8   16   32   64  128  256  512   1K   2K   4K   8K  16K  32K  64K 128K 256K 512K   1M   2M   4M   8M  16M  32M >32M\n");
+	}
+}
+
+
+void DetailedTrafficPrinter::print_data_row(FILE *fp,
+			   const struct blkiomon_stat *blk_stat,
+			   const struct zfcpdd_dstat *zfcp_stat)
+{
+	if (!blk_stat)
+		blk_stat = get_empty_blkiomon_stat();
+	if (!zfcp_stat)
+		zfcp_stat = get_empty_zfcpdd_dstat();
+
+	print_histogram_io_reqs(fp, blk_stat);
+	if (!m_csv) {
+		fputc('\n', fp);
+		print_topline_whitespace(fp);
+	}
+	print_histogram_io_subs_lat(fp, blk_stat);
+	if (!m_csv) {
+		fputc('\n', fp);
+		print_topline_whitespace(fp);
+	}
+	print_histogram_channel_lat(fp, zfcp_stat);
+	if (!m_csv) {
+		fputc('\n', fp);
+		print_topline_whitespace(fp);
+	}
+	print_histogram_fabric_lat(fp, zfcp_stat);
+	fputc('\n', fp);
+}
+
+
+void DetailedTrafficPrinter::print_histogram_io_reqs(FILE *fp,
+			    const struct blkiomon_stat *stat)
+{
+	for (unsigned int i = 0; i < BLKIOMON_SIZE_BUCKETS; ++i) {
+		print_delimiter(fp);
+		if (stat)
+			print_abbrev_num(fp, stat->size_hist[i], 4);
+		else
+			print_abbrev_num(fp, (__u32)0, 4);
+	}
+}
+
+
+void DetailedTrafficPrinter::print_histogram_io_subs_lat(FILE *fp,
+				const struct blkiomon_stat *stat)
+{
+	for (unsigned int i = 0; i < BLKIOMON_D2C_BUCKETS; ++i) {
+		print_delimiter(fp);
+		if (stat)
+			print_abbrev_num(fp, stat->d2c_hist[i], 4);
+		else
+			print_abbrev_num(fp, (__u32)0, 4);
+	}
+}
+
+
+void DetailedTrafficPrinter::print_histogram_channel_lat(FILE *fp,
+				const struct zfcpdd_dstat *stat)
+{
+	for (unsigned int i = 0; i < BLKIOMON_CHAN_LAT_BUCKETS; ++i) {
+		print_delimiter(fp);
+		if (stat)
+			print_abbrev_num(fp, stat->chan_lat_hist[i], 4);
+		else
+			print_abbrev_num(fp, (__u32)0, 4);
+	}
+}
+
+
+void DetailedTrafficPrinter::print_histogram_fabric_lat(FILE *fp,
+			       const struct zfcpdd_dstat *stat)
+{
+	for (unsigned int i = 0; i < BLKIOMON_FABR_LAT_BUCKETS; ++i) {
+		print_delimiter(fp);
+		if (stat)
+			print_abbrev_num(fp, stat->fabr_lat_hist[i], 4);
+		else
+			print_abbrev_num(fp, (__u32)0, 4);
+	}
+}
+
+
+
Index: s390-tools-sles11/ziomon/ziorep_printers.hpp
===================================================================
--- /dev/null
+++ s390-tools-sles11/ziomon/ziorep_printers.hpp
@@ -0,0 +1,220 @@
+/*
+ * FCP report generators
+ *
+ * Utility classes to print framsets
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Stefan Raspl <raspl@linux.vnet.ibm.com>
+ */
+
+#ifndef ZIOREP_PRINTERS
+#define ZIOREP_PRINTERS
+
+#include "ziorep_frameset.hpp"
+#include "ziorep_framer.hpp"
+#include "ziorep_cfgreader.hpp"
+
+
+/**
+ * Base class for all printer classes
+ */
+class Printer {
+public:
+	Printer(const ConfigReader *cfg, bool csv_mode);
+	virtual ~Printer() {};
+
+	/**
+	 * Print topline to fp.
+	 * Returns number of characters printed or <0 in case of error */
+	virtual void print_topline(FILE *fp, const Frameset &frameset) = 0;
+
+	/**
+	 * Print frameset to fp.
+	 * Returns number of characters printed or <0 in case of error */
+	virtual int print_frame(FILE *fp, const Frameset &frameset,
+				const DeviceFilter &dev_filt) = 0;
+
+	/// Whether the output should be done in CSV format or not
+	bool print_csv() const;
+
+protected:
+	/**
+	 * Print timestamp before the actual frame is printed.
+	 * Returns number of characters printed or <0 in case of error */
+	void print_timestamp(FILE *fp, const Frameset &frameset);
+
+	const ConfigReader	*m_cfg;
+	/**
+	 * Print a given number 'num' in 'num_digs' characters with
+	 * as many decimal places as possible.
+	 * NOTE: max_digits must be 4 or 5!
+	 * E.g. print_abbrev_num(13400, 5) returns '13.4K'.
+	 */
+	void print_abbrev_num(FILE *fp, __u64 num, int max_digs) const;
+	void print_abbrev_num(FILE *fp, __u32 num, int max_digs) const;
+
+	/**
+	 * Print floats always using 6 characters */
+	void print_abbrev_num(FILE *fp, double num) const;
+	void print_abbrev_num(FILE *fp, double num, int max_digs,
+			      bool more_places = false) const;
+
+	const struct adapter_utilization* get_empty_utilization(__u32 host_id);
+	const struct adapter_utilization* get_invalid_utilization(__u32 host_id);
+	const struct ioerr_cnt* get_empty_ioerr(struct hctl_ident *identifier);
+	const struct zfcpdd_dstat* get_empty_zfcpdd_dstat(__u32 device = 0);
+	const struct blkiomon_stat* get_empty_blkiomon_stat(__u32 device = 0);
+
+	/** Print the delimiter appropriate for the output. That's usually
+	 * a space for regular output and ',' for CSV. */
+	inline void print_delimiter(FILE *fp);
+
+	/// Print a character indicating that the respective value is not valid
+	inline void print_invalid(FILE *fp, int width);
+
+	/// set if result should be printed is csv
+	bool				m_csv;
+	char				m_delim;
+
+private:
+	void print_abbreviated(FILE *fp, double num, int leading_places,
+			      int max_digs) const;
+	void determine_mag_factor(int leading_places, char *magnitude,
+				double *factor) const;
+	void determine_digs(int leading_places, int max_digs,
+			     int *num_full_digs, int *num_places) const;
+
+	struct adapter_utilization	m_util;
+	struct adapter_utilization	m_util_invalid;
+	struct ioerr_cnt 		m_ioerr;
+	struct zfcpdd_dstat		m_dstat;
+	struct blkiomon_stat		m_stat;
+
+	/// day of month of last day that was printed
+	int				m_prev_day;
+};
+
+
+class PhysAdapterPrinter : public Printer {
+public:
+	PhysAdapterPrinter(const ConfigReader *cfg, bool csv_mode);
+
+	virtual void print_topline(FILE *fp, const Frameset &frameset);
+	virtual int print_frame(FILE *fp, const Frameset &frameset,
+				const DeviceFilter &dev_filt);
+
+private:
+	void print_phys_adpt(FILE *fp, __u32 host_id,
+			     int *rc);
+	void print_utilization(FILE *fp, const struct abbrev_stat *stat,
+			       __u64 count, bool valid);
+
+};
+
+
+class VirtAdapterPrinter : public Printer {
+public:
+	VirtAdapterPrinter(const ConfigReader *cfg, bool csv_mode);
+
+	virtual void print_topline(FILE *fp, const Frameset &frameset);
+	virtual int print_frame(FILE *fp, const Frameset &frameset,
+				const DeviceFilter &dev_filt);
+
+private:
+	void print_virt_adpt(FILE *fp, __u32 subchnl, int *rc);
+	void print_queue_fill(FILE *fp, const struct zfcpdd_dstat *stat,
+			      const struct adapter_utilization *res);
+	void print_queue_full(FILE *fp, const struct adapter_utilization *res);
+	void print_failures(FILE *fp, const struct ioerr_cnt *cnt);
+	void print_throughput(FILE *fp, const struct blkiomon_stat *stat);
+	void print_num_requests(FILE *fp, const struct blkiomon_stat *stat);
+};
+
+
+class TrafficPrinter : public Printer {
+public:
+	virtual int print_frame(FILE *fp, const Frameset &frameset,
+				const DeviceFilter &dev_filt);
+
+protected:
+	TrafficPrinter(const ConfigReader *cfg, Collapser &col,
+			bool csv_mode);
+	virtual ~TrafficPrinter();
+
+	/**
+	 * Print the actual row, excluding the first column */
+	virtual void print_data_row(FILE *fp,
+			   const struct blkiomon_stat *blk_stat,
+			   const struct zfcpdd_dstat *zfcp_stat) = 0;
+
+	void print_topline_prefix1(FILE *fp);
+	void print_topline_prefix2(FILE *fp);
+	void print_topline_whitespace(FILE *fp);
+
+	void get_device_list(list<__u64> &lst, const DeviceFilter &dev_filt);
+
+	void get_device_list(list<__u32> &lst, const DeviceFilter &dev_filt);
+	void print_device_wwpn(FILE *fp, __u64 wwpn);
+	void print_device_chpid(FILE *fp, __u32 chpid);
+	void print_device_devno(FILE *fp, __u32 devno);
+	void print_device_mp_mm(FILE *fp, __u32 mp_mm,
+			       const ConfigReader &cfg,
+			       int *rc);
+	void print_device(FILE *fp, __u32 dev,
+			 const ConfigReader &cfg,
+			 int *rc);
+	void print_device_all(FILE *fp);
+
+	Aggregator		m_agg_crit;
+	/// Multipath devices have variable length
+	char		       *m_mp_whitespace;
+	char		       *m_mp_topline_pref1;
+	char		       *m_mp_topline_pref2;
+};
+
+
+class SummaryTrafficPrinter : public TrafficPrinter {
+public:
+	SummaryTrafficPrinter(const ConfigReader *cfg,
+			      Collapser &col, bool csv_mode);
+
+	virtual void print_topline(FILE *fp, const Frameset &frameset);
+
+private:
+	virtual void print_data_row(FILE *fp,
+			   const struct blkiomon_stat *blk_stat,
+			   const struct zfcpdd_dstat *zfcp_stat);
+	void print_throughput(FILE *fp, const struct blkiomon_stat *stat);
+	void print_request_stats(FILE *fp, const struct blkiomon_stat *stat);
+	void print_io_subsystem_latency(FILE *fp, const struct blkiomon_stat *stat);
+	void print_channel_latency(FILE *fp, const struct zfcpdd_dstat *stat);
+	void print_fabric_latency(FILE *fp, const struct zfcpdd_dstat *stat);
+	void print_latency(FILE *fp, const struct minmax *data);
+	void print_latency(FILE *fp, const struct abbrev_stat *data,
+			  __u64 count);
+};
+
+
+class DetailedTrafficPrinter : public TrafficPrinter {
+public:
+	DetailedTrafficPrinter(const ConfigReader *cfg, Collapser &col,
+			       bool csv_mode);
+
+	virtual void print_topline(FILE *fp, const Frameset &frameset);
+
+private:
+	virtual void print_data_row(FILE *fp,
+			   const struct blkiomon_stat *blk_stat,
+			   const struct zfcpdd_dstat *zfcp_stat);
+	void print_histogram_io_reqs(FILE *fp,
+				    const struct blkiomon_stat *stat);
+	void print_histogram_io_subs_lat(FILE *fp,
+					const struct blkiomon_stat *stat);
+	void print_histogram_channel_lat(FILE *fp,
+					const struct zfcpdd_dstat *stat);
+	void print_histogram_fabric_lat(FILE *fp,
+				       const struct zfcpdd_dstat *stat);
+};
+
+#endif
+
Index: s390-tools-sles11/ziomon/ziorep_traffic.8
===================================================================
--- /dev/null
+++ s390-tools-sles11/ziomon/ziorep_traffic.8
@@ -0,0 +1,216 @@
+.TH ZIOMON_MGR 8 "Jul 2008" "s390-tools"
+
+.SH NAME
+ziorep_traffic \- I/O traffic report for FCP adapters.
+
+.SH SYNOPSIS
+.B ziorep_traffic [-V] [-v] [-h] [-b <begin>] [-e <end>] [-i <time>] [-s] [-c <chpid>] [-u <id>] [-t <num>] [-p <port>] [-l <lun>] [-d <fdev> ] [-m <mdev> ] [-x] [-D] [-C a|u|p|m|A] <filename>
+
+
+
+.SH DESCRIPTION
+.B ziorep_traffic
+Prints a report from the specified data.
+
+.SH OPTIONS
+.TP
+.BR "\-h" " or " "\-\-help"
+Print help information, then exit.
+
+.TP
+.BR "\-v" " or " "\-\-version"
+Print version information, then exit.
+
+.TP
+.BR "\-V" " or " "\-\-verbose"
+Be verbose.
+
+.TP
+.BR "\-b" " or " "\-\-begin"
+Limit the timeframe to consider to data beginning with the specified date.
+.br
+Dates must be specified in the following format: YYYY-MM-DD HH:MM[:SS].
+.br
+E.g. 2008-03-21 09:08 is 9:08 on March 21, 2008.
+
+.TP
+.BR "\-e" " or " "\-\-end"
+Limit the timeframe to consider to data ending with the specified date.
+.br
+Dates must be specified in the following format: YYYY-MM-DD HH:MM[:SS].
+.br
+E.g. 2008-03-21 09:08 is 9:08 on March 21, 2008.
+
+.TP
+.BR "\-i" " or " "\-\-interval"
+Specify an aggregation interval. The interval is given in seconds, and must be a multiple
+of the interval as found in the source data.
+
+.TP
+.BR "\-s" " or " "\-\-summary"
+Print a summary of the data, then exit.
+
+.TP
+.BR "\-c" " or " "\-\-chpid"
+Consider the specified physical adapter. Adapters must be specified in hex.
+.br
+If multiple adapters should be specified, specify each one separately.
+.br
+E.g. '-c 7a'.
+
+.TP
+.BR "\-u" " or " "\-\-bus-id"
+Consider the specified bus-IDs. bus-IDs must be specified in hex.
+.br
+If multiple bus-IDs should be specified, specify each one separately.
+.br
+E.g. '-u 0.0.4c78'.
+
+.TP
+.BR "\-p" " or " "\-\-port"
+Consider the specified target port. Target ports must be specified in hex.
+.br
+If multiple ports should be specified, specify each one separately.
+.br
+E.g. '-p 0x500507630040710b'.
+
+.TP
+.BR "\-l" " or " "\-\-lun"
+Consider the specified lun. luns must be specified in hex.
+.br
+If multiple luns should be specified, specify each one separately.
+.br
+E.g. '-l 0x4021402d00000000'.
+
+.TP
+.BR "\-d" " or " "\-\-device"
+Consider the specified device.
+.br
+If multiple devices should be specified, specify each one separately.
+.br
+E.g. '-d sda'.
+
+.TP
+.BR "\-m" " or " "\-\-mdev"
+Consider the specified multipath device.
+.br
+If multiple devices should be specified, specify each one separately.
+.br
+E.g. '-m 36005076303ffc1040002120'.
+
+.TP
+.BR "\-x" " or " "\-\-export-csv"
+Write data to file(s) in CSV format. Output filenames will be based on the data filename.
+
+.TP
+.BR "\-t" " or " "\-\-topline"
+Repeat topline after specified number of frames.
+.br
+0 for no repeat (default).
+
+.TP
+.BR "\-D" " or " "\-\-detailed"
+Print histograms.
+
+.TP
+.BR "\-C" " or " "\-\-collapse"
+Collapse the data by the specified criterion:
+.br
+.BR "a"
+collapse by physical adapter.
+.br
+.BR "u"
+collapse by bus-ID.
+.br
+.BR "p"
+collapse by target port.
+.br
+.BR "m"
+collapse by multipath device.
+.br
+.BR "A"
+collapse all data into a single dataset.
+
+
+.SH OUTPUT
+Here is a list of the columns and their descriptions.
+Timestamps of the frames printed depict the ending of the respective timeframe.
+
+.TP
+.BR "I/O rt MB/s"
+I/O rate of the respective device in megabytes per second, considering individual requests and their total processing time, including channel latency, etc.
+.br
+.IR "min" " and " "max"
+give the minimum and maximum respectively.
+
+.TP
+.BR "thrp in MB/s"
+Throughput of the respective device in megabytes per second, considering times of activity only.
+.br
+.IR "avg" " and " "stdev"
+give the average utilization as well as its standard deviation respectively.
+
+.TP
+.BR "I/O requests"
+Statistics on the numbers and kinds of I/O requests seen.
+.br
+.IR "#reqs"
+is the total number of requests,
+.br
+.IR "rd"
+is the number of read requests,
+.br
+.IR "wrt"
+is the number of write requests and
+.br
+.IR "bidi"
+is the number of bi-directional requests.
+
+.TP
+.BR "I/O subs. lat. in us"
+Latency of the I/O subsystem in microseconds. This is the time needed to execute a request, from the I/O stack down to the hardware and back up again.
+.br
+.IR "min" ", " "max" ", " "avg" " and " "stdev"
+give the minimum, maximum and average latency as well as its standard deviation respectively.
+
+.TP
+.BR "channel lat. in us"
+Channel latency in microseconds. This is the time needed to execute a request on the FCP adapter.
+.br
+.IR "min" ", " "max" ", " "avg" " and " "stdev"
+give the minimum, maximum and average latency as well as its standard deviation respectively.
+
+.TP
+.BR "fabric lat. in us"
+Fabric latency in microseconds. This is the time needed for the requests within the fabric, from the FCP adapter to the target device and back again.
+.br
+.IR "min" ", " "max" ", " "avg" " and " "stdev"
+give the minimum, maximum and average latency as well as its standard deviation respectively.
+
+
+.SH EXAMPLES
+.B Example
+.br
+Print a traffic report for all devices.
+Only data after 8:57 April 5, 2008 should be considered.
+Data should be aggregated to 60 second intervals.
+
+ziorep_traffic -i 60 -b "2008-04-05 08:57" sample.log
+
+.B Example
+.br
+Print a detailed traffic report for all devices connected to target port 0x500507630313c562.
+Data should be aggregated over the whole data range.
+
+ziorep_traffic -i 0 -p 0x500507630313c562 sample.log
+
+.B Example
+.br
+Print a detailed traffic report for all devices that are either connected to target port 0x500507630313c562 or part of multipath device 36005076303ffc5620000000000001314.
+The result should be collapsed on the bus-ID level.
+
+ziorep_traffic -C u -p 0x500507630313c562 -m 36005076303ffc5620000000000001314 sample.log
+
+.SH "SEE ALSO"
+.BR ziorep_config (8),
+.BR ziorep_utilization (8)
Index: s390-tools-sles11/ziomon/ziorep_traffic.cpp
===================================================================
--- /dev/null
+++ s390-tools-sles11/ziomon/ziorep_traffic.cpp
@@ -0,0 +1,547 @@
+/*
+ * FCP report generators
+ *
+ * Traffic report program
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Stefan Raspl <raspl@linux.vnet.ibm.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <linux/types.h>
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
+#include <assert.h>
+#include <limits.h>
+
+#include <list>
+
+#include "zt_common.h"
+#include "ziorep_collapser.hpp"
+#include "ziorep_utils.hpp"
+
+
+using std::list;
+
+
+const char *toolname = "ziorep_traffic";
+int verbose=0;
+
+
+struct options {
+	__u64			begin;
+	__u64			end;
+	__u32			interval;
+	list<const char*>	devices;
+	list<const char*>	mp_devices;
+	__u64			topline;
+	char*			filename;
+	bool			print_summary;
+	bool			details;
+	Aggregator		col_crit;
+	list<__u32>		chpids;
+	list<__u32>		devnos;
+	list<__u64>		wwpns;
+	list<__u64>		luns;
+	bool			csv_export;
+};
+
+
+static void init_opts(struct options *opts)
+{
+	opts->begin		= 0;
+	opts->end		= UINT64_MAX;
+	opts->interval		= UINT32_MAX;
+	opts->topline		= 0;
+	opts->filename		= NULL;
+	opts->print_summary	= false;
+	opts->details		= false;
+	opts->col_crit		= none;
+	opts->csv_export	= false;
+}
+
+
+static const char help_text[] =
+    "Usage: ziorep_traffic [-V] [-v] [-h] [-b <begin>] [-e <end>]"
+    " [-i <time>] [-s]\n"
+    "                        [-c <chpid>] [-u <id>] [-t <num>] [-p <port>]\n"
+    "                        [-l <lun>] [-d <fdev> ] [-m <mdev>] [-x] [-D]\n"
+    "                        [-C a|u|p|m|A] <filename>\n\n"
+    "-h, --help              Print usage information and exit.\n"
+    "-v, --version           Print version information and exit.\n"
+    "-V, --verbose           Be verbose.\n"
+    "-b, --begin <begin>     Do not consider data earlier than 'begin'.\n"
+    "                        Defaults to begin of available data.\n"
+    "                        Format is YYYY-MM-DD HH:MM[:SS],\n"
+    "                        e.g. '-b \"2008-03-21 09:08\"\n"
+    "-e, --end <end>         Do not consider data later than 'end'.\n"
+    "                        Defaults to end of available data.\n"
+    "                        Format is YYYY-MM-DD HH:MM[:SS],\n"
+    "                        e.g. '-e \"2008-03-21 09:08:57\"\n"
+    "-i, --interval <time>   Set aggregation interval to 'time' in seconds.\n"
+    "                        Must be a multiple of the interval size of the source\n"
+    "                        data.\n"
+    "                        Set to 0 to aggregate over all data.\n"
+    "-s, --summary           Show a summary of the data.\n"
+    "-C, --collapse <val>    Collapse data for multiple instances of\n"
+    "                        a device into a single one. See man page for details.\n"
+    "-c, --chpid <chpid>     Select adapter by CHPID in hex, e.g. '-c 32'\n"
+    "-u, --bus-id <id>       Select by bus-ID, e.g. '-u 0.0.7f1d'\n"
+    "-p, --port <port>       Select by target port, e.g. '-p 0x500507630040710b'\n"
+    "-l, --lun <lun>         Select by lun, e.g. '-l 0x4021402200000000'\n"
+    "-d, --device <fdev>     Select by device, e.g. '-d sda'\n"
+    "-m, --mdev <mdev>       Select by multipath device,\n"
+    "                        e.g. '-m 36005076303ffc1040002120'\n"
+    "-D, --detailed          Print histograms instead of min/max/avg/stdev\n"
+    "-x, --export-csv        Export data to files in CSV format.\n"
+    "-t, --topline <num>     Repeat topline after every 'num' frames.\n"
+    "                        0 for no repeat (default).\n";
+
+
+static void print_help()
+{
+        printf("%s", help_text);
+}
+
+
+static void print_version()
+{
+        printf("%s: Traffic report generator version %s\n"
+               "Copyright IBM Corp. 2008\n", toolname, RELEASE_STRING);
+}
+
+
+static int parse_params(int argc, char **argv, struct options *opts)
+{
+	int c;
+	int index;
+	int rc;
+	__u32 tmp32;
+	long long unsigned int tmp64;
+	long tmpl;
+	char mychar;
+        static struct option long_options[] = {
+                { "version",         no_argument,       NULL, 'v'},
+		{ "help",            no_argument,       NULL, 'h'},
+		{ "verbose",         no_argument,       NULL, 'V'},
+		{ "begin",           required_argument, NULL, 'b'},
+                { "end",             required_argument, NULL, 'e'},
+		{ "interval",        required_argument, NULL, 'i'},
+		{ "summary",         no_argument,       NULL, 's'},
+		{ "collapse",        required_argument, NULL, 'C'},
+		{ "chpid",           required_argument, NULL, 'c'},
+		{ "bus-id",          required_argument, NULL, 'u'},
+		{ "port",            required_argument, NULL, 'p'},
+		{ "lun",             required_argument, NULL, 'l'},
+		{ "device",          required_argument, NULL, 'd'},
+		{ "mdev",            required_argument, NULL, 'm'},
+		{ "detailed",        required_argument, NULL, 'D'},
+		{ "export-csv",      no_argument,       NULL, 'x'},
+		{ "topline",         required_argument, NULL, 't'},
+                { 0,                 0,                 0,     0 }
+	};
+
+	if (argc < 2) {
+		print_help();
+		return 1;
+	}
+
+	assert(sizeof(long long int) == sizeof(__u64));
+	while ((c = getopt_long(argc, argv, "m:C:b:e:i:c:u:p:l:d:t:xDshvV",
+				long_options, &index)) != EOF) {
+		switch (c) {
+		case 'V':
+			verbose++;
+			break;
+		case 'h':
+			print_help();
+			return 1;
+		case 'v':
+			print_version();
+			return 1;
+		case 'b':
+			if (get_datetime_val(optarg, &opts->begin))
+				return -1;
+			break;
+		case 'e':
+			if (get_datetime_val(optarg, &opts->end))
+				return -1;
+			break;
+		case 'i':
+			if (sscanf(optarg, "%lu", &tmpl) != 1) {
+				fprintf(stderr, "%s:"
+					" Cannot parse %s as an integer value."
+					" Please correct and try again.\n", toolname,
+					optarg);
+				return -1;
+			}
+			if (tmpl < 0) {
+				fprintf(stderr, "%s:"
+					" Argument %s must be greater than or"
+					" equal to 0.", toolname, optarg);
+				return -1;
+			}
+			opts->interval = tmpl;
+			break;
+		case 's':
+			opts->print_summary = true;
+			break;
+		case 'c':
+			rc = sscanf(optarg, "%x%c", &tmp32, &mychar);
+			if (rc < 1) {
+				fprintf(stderr, "%s: Could"
+					" not read chpid %s\n", toolname, optarg);
+				return -1;
+			}
+			if (rc > 1) {
+				fprintf(stderr, "%s: %s is not a valid chpid\n", toolname, optarg);
+				return -1;
+			}
+			opts->chpids.push_back(tmp32);
+			break;
+		case 'p':
+			 rc = sscanf(optarg, "0x%Lx", &tmp64);
+			 if (rc != 1) {
+				 fprintf(stderr, "%s: Could"
+					 " not read port number %s\n", toolname, optarg);
+				 return -1;
+			 }
+			 opts->wwpns.push_back(tmp64);
+			 break;
+		case 'l':
+			rc = sscanf(optarg, "0x%Lx", &tmp64);
+			if (rc != 1) {
+				fprintf(stderr, "%s: Could"
+					" not read lun %s\n", toolname, optarg);
+				return -1;
+			}
+			opts->luns.push_back(tmp64);
+			break;
+		case 'u':
+			rc = sscanf(optarg, "0.0.%x", &tmp32);
+			if (rc != 1) {
+				fprintf(stderr, "%s: Could not read bus-ID"
+					" %s\n", toolname, optarg);
+				return -1;
+			}
+			opts->devnos.push_back(tmp32);
+			break;
+		case 'd':
+			opts->devices.push_back(optarg);
+			break;
+		case 'm':
+			opts->mp_devices.push_back(optarg);
+			break;
+		case 'D':
+			opts->details = true;
+			break;
+		case 't':
+			if (parse_topline_arg(optarg, &opts->topline))
+				return -1;
+			break;
+		case 'x':
+			opts->csv_export = true;
+			break;
+		case 'C':
+			rc = 0;
+			switch (*optarg) {
+			case 'a': opts->col_crit = chpid;
+				break;
+			case 'u': opts->col_crit = devno;
+				break;
+			case 'p': opts->col_crit = wwpn;
+				break;
+			case 'm': opts->col_crit = multipath_device;
+				break;
+			case 'A': opts->col_crit = all;
+				break;
+			default:
+				rc = -1;
+			}
+			if (rc || strlen(optarg) > 1) {
+				fprintf(stderr, "%s:"
+				    " Unrecognized switch '%s' to parameter"
+				    " '-C'. Please check the help for a list"
+				    " of valid switches, correct and try"
+				    " again.\n", toolname, optarg);
+				return -2;
+			}
+			break;
+		default:
+			fprintf(stderr, "%s: Try '%s --help' for"
+				" more information.\n", toolname, toolname);
+			return -1;
+		}
+	}
+	if (optind == argc - 1)
+		opts->filename = argv[optind];
+	if (optind < argc - 1) {
+		fprintf(stderr, "%s: Multiple filenames"
+			" specified. Specify only a single one at a time.\n", toolname);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int check_opts(struct options *opts, ConfigReader **cfg)
+{
+	int rc = 0;
+
+	// check filename
+	if (!opts->filename) {
+		fprintf(stderr, "%s: No filename specified.\n", toolname);
+		return -2;
+	}
+	else {
+		if (strncmp(opts->filename + strlen(opts->filename) - strlen(DACC_FILE_EXT_LOG),
+			    DACC_FILE_EXT_LOG, strlen(DACC_FILE_EXT_LOG)) == 0) {
+			verbose_msg("Filename carries " DACC_FILE_EXT_LOG " extension - stripping\n");
+			opts->filename[strlen(opts->filename) - strlen(DACC_FILE_EXT_LOG)] = '\0';
+		}
+		if (strncmp(opts->filename + strlen(opts->filename) - strlen(DACC_FILE_EXT_AGG),
+			    DACC_FILE_EXT_AGG, strlen(DACC_FILE_EXT_AGG)) == 0) {
+			verbose_msg("Filename carries " DACC_FILE_EXT_AGG " extension - stripping\n");
+			opts->filename[strlen(opts->filename) - strlen(DACC_FILE_EXT_AGG)] = '\0';
+		}
+		verbose_msg("Filename is %s\n", opts->filename);
+	}
+
+	// check config
+	*cfg = new ConfigReader(&rc, opts->filename);
+	if (rc)
+		return -1;
+
+	for (list<__u32>::const_iterator i = opts->chpids.begin();
+	      i != opts->chpids.end(); ++i) {
+		if (!(*cfg)->verify_chpid(*i)) {
+			fprintf(stderr, "Error: Could not find chpid %x in"
+				" configuration.\n", *i);
+			rc = -2;
+		}
+	}
+	for (list<__u32>::const_iterator i = opts->devnos.begin();
+	      i != opts->devnos.end(); ++i) {
+		if (!(*cfg)->verify_devno(*i)) {
+			fprintf(stderr, "Error: Could not find bus-ID 0.0.%04x in"
+				" configuration.\n", *i);
+			rc = -3;
+		}
+	}
+	for (list<__u64>::const_iterator i = opts->wwpns.begin();
+	      i != opts->wwpns.end(); ++i) {
+		if (!(*cfg)->verify_wwpn(*i)) {
+			fprintf(stderr, "Error: Could not find WWPN %016Lx in"
+				" configuration.\n", (long long unsigned int)*i);
+			rc = -4;
+		}
+	}
+	for (list<__u64>::const_iterator i = opts->luns.begin();
+	      i != opts->luns.end(); ++i) {
+		if (!(*cfg)->verify_lun(*i)) {
+			fprintf(stderr, "Error: Could not find LUN %016Lx in"
+				" configuration.\n", (long long unsigned int)*i);
+			rc = -5;
+		}
+	}
+	for (list<const char*>::iterator i = opts->devices.begin();
+	      i != opts->devices.end(); ++i) {
+		if (!(*cfg)->verify_device(*i)) {
+			fprintf(stderr, "Error: Could not find device %s in"
+				" configuration.\n", *i);
+			rc = -6;
+		}
+	}
+	for (list<const char*>::iterator i = opts->mp_devices.begin();
+	      i != opts->mp_devices.end(); ++i) {
+		if (!(*cfg)->verify_mp_device(*i)) {
+			fprintf(stderr, "Error: Could not find multipath"
+				" device %s in configuration.\n", *i);
+			rc = -7;
+		}
+	}
+	if (opts->csv_export && opts->topline > 0) {
+		fprintf(stderr, "%s: Warning: Both, topline"
+			" repeat and CSV export activated, deactivating"
+			" topline repeat.\n", toolname);
+		opts->topline = 0;
+	}
+
+	if (!opts->print_summary
+	    && adjust_timeframe(opts->filename, &opts->begin, &opts->end,
+			     &opts->interval))
+		rc = -8;
+
+	return rc;
+}
+
+
+static int configure_coarse_devices(struct options *opts, ConfigReader &cfg,
+				     StagedDeviceFilter &dev_flt)
+{
+	list<__u32> mms;
+	int rc = 0;
+
+	for (list<const char *>::const_iterator i = opts->devices.begin();
+	      i != opts->devices.end(); ++i) {
+		__u32 mm = cfg.get_mm_by_device(*i, &rc);
+		dev_flt.add_device(mm, cfg.get_ident_by_mm_internal(mm, &rc));
+	}
+	for (list<__u64>::const_iterator i = opts->luns.begin();
+	      i != opts->luns.end(); ++i) {
+		cfg.get_mms_by_lun(mms, *i);
+		for (list<__u32>::const_iterator j = mms.begin();
+		      j != mms.end(); j++)
+			dev_flt.add_device(*j, cfg.get_ident_by_mm_internal(*j, &rc));
+	}
+	for (list<const char *>::const_iterator i = opts->mp_devices.begin();
+	      i != opts->mp_devices.end(); ++i)
+		dev_flt.stage_mp_mm(cfg.get_mp_mm_by_multipath(*i, &rc));
+	if (rc)
+		return -1;
+	for (list<__u64>::const_iterator i = opts->wwpns.begin();
+	      i != opts->wwpns.end(); ++i)
+		dev_flt.stage_wwpn(*i);
+	for (list<__u32>::const_iterator i = opts->devnos.begin();
+	      i != opts->devnos.end(); ++i)
+		dev_flt.stage_devno(*i);
+	for (list<__u32>::const_iterator i = opts->chpids.begin();
+	      i != opts->chpids.end(); ++i)
+		dev_flt.stage_chpid(*i);
+
+	return dev_flt.finish(cfg, false);
+}
+
+
+static int configure_device_filter(struct options *opts, ConfigReader &cfg,
+				   DeviceFilter **dev_filt)
+{
+	int rc = 0;
+	list<__u32> mm_lst;
+	list<__u32>::const_iterator i_32, j_32;
+
+	verbose_msg("Configure device filter\n");
+	// verify and sort out devices
+	if (opts->devices.size() > 0 ||  opts->luns.size() > 0
+		 || opts->wwpns.size() > 0 || opts->mp_devices.size() > 0
+		 || opts->devnos.size() > 0 || opts->chpids.size() > 0) {
+		verbose_msg("    devices specified via coarse criteria"
+			    " - let's see...\n");
+		StagedDeviceFilter *stgd_flt = new StagedDeviceFilter;
+		*dev_filt = stgd_flt;
+		rc = configure_coarse_devices(opts, cfg, *stgd_flt);
+	}
+	else {
+		verbose_msg("    no devices specified - accept all\n");
+		*dev_filt = new DeviceFilter;
+		add_all_devices(cfg, **dev_filt);
+	}
+
+	verbose_msg("Device filter configured (total: %zd devices)\n",
+		    (*dev_filt)->get_mm_list().size());
+
+	return rc;
+}
+
+
+static int print_report(struct options *opts, ConfigReader &cfg)
+{
+	int rc = 0;
+	FILE *fp;
+	MsgTypeFilter msgtype_filter;
+	Printer *printer = NULL;
+	DeviceFilter *dev_filt;
+	Collapser *col = NULL;
+	list<MsgTypes> type_flt;
+
+	type_flt.push_back(zfcpdd);
+	type_flt.push_back(blkiomon);
+
+	if (configure_device_filter(opts, cfg, &dev_filt)) {
+		rc = -1;
+		goto out;
+	}
+
+	if (opts->col_crit == none)
+		col = new NoopCollapser();
+	else if (opts->col_crit == all)
+		col = new TotalCollapser();
+	else {
+		col = new AggregationCollapser(cfg,
+					       opts->col_crit,
+					       *dev_filt,
+					       &rc);
+		if (rc)
+			goto out;
+	}
+
+	if (opts->details) {
+		if (opts->csv_export) {
+			fp = open_csv_output_file(opts->filename,
+						  "_traffic_detailed.csv", &rc);
+			if (rc)
+				goto out;
+		}
+		else
+			fp = stdout;
+		printer = new DetailedTrafficPrinter(&cfg, *col,
+						     opts->csv_export);
+	}
+	else {
+		if (opts->csv_export) {
+			fp = open_csv_output_file(opts->filename,
+						  "_traffic.csv",
+						  &rc);
+			if (rc)
+				goto out;
+		}
+		else
+			fp = stdout;
+		printer = new SummaryTrafficPrinter(&cfg, *col,
+						    opts->csv_export);
+	}
+
+	if ( (rc = print_report(fp, opts->begin, opts->end,
+				opts->interval, opts->filename, opts->topline,
+				&type_flt, *dev_filt, *col, *printer)) < 0 )
+		rc = -3;
+
+	if (opts->csv_export)
+		fclose(fp);
+out:
+	delete dev_filt;
+	delete col;
+	delete printer;
+
+	return rc;
+}
+
+
+int main(int argc, char **argv)
+{
+	int rc;
+	struct options opts;
+	ConfigReader *cfg = NULL;
+
+	verbose = 0;
+
+	init_opts(&opts);
+	if ( (rc = parse_params(argc, argv, &opts)) )
+		goto out;
+	if ( (rc = check_opts(&opts, &cfg)) )
+		goto out;
+
+	if (opts.print_summary)
+		rc = print_summary_report(stdout, opts.filename, *cfg);
+	else
+		rc = print_report(&opts, *cfg);
+
+out:
+	delete cfg;
+
+	return rc;
+}
+
Index: s390-tools-sles11/ziomon/ziorep_utilization.8
===================================================================
--- /dev/null
+++ s390-tools-sles11/ziomon/ziorep_utilization.8
@@ -0,0 +1,137 @@
+.TH ZIOMON_MGR 8 "Jul 2008" "s390-tools"
+
+.SH NAME
+ziorep_utilization \- Utilization report for FCP adapters.
+
+.SH SYNOPSIS
+.B ziorep_utilization
+[-V] [-v] [-h] [-b <begin>] [-e <end>] [-i <time>] [-s] [-c <chpid>] [-x] [-t <num>] <filename>
+
+.SH DESCRIPTION
+.B ziorep_utilization
+Prints a report from the specified data.
+
+.SH OPTIONS
+.TP
+.BR "\-h" " or " "\-\-help"
+Print help information, then exit.
+
+.TP
+.BR "\-v" " or " "\-\-version"
+Print version information, then exit.
+
+.TP
+.BR "\-V" " or " "\-\-verbose"
+Be verbose.
+
+.TP
+.BR "\-b" " or " "\-\-begin"
+Limit the timeframe to consider to data beginning with the specified date.
+.br
+Dates must be specified in the following format: YYYY-MM-DD HH:MM[:SS].
+.br
+E.g. 2008-03-21 09:08 is 9:08 on March 21, 2008.
+
+.TP
+.BR "\-e" " or " "\-\-end"
+Limit the timeframe to consider to data ending with the specified date.
+.br
+Dates must be specified in the following format: YYYY-MM-DD HH:MM[:SS].
+.br
+E.g. 2008-03-21 09:08 is 9:08 on March 21, 2008.
+
+.TP
+.BR "\-i" " or " "\-\-interval"
+Specify an aggregation interval. The interval is given in seconds, and must be a multiple
+of the interval as found in the source data.
+
+.TP
+.BR "\-s" " or " "\-\-summary"
+Print a summary of the data, then exit.
+
+.TP
+.BR "\-c" " or " "\-\-chpid"
+Only consider the specified physical adapter. Adapters must be specified in hex.
+If multiple adapters should be specified, specify each one separately.
+
+.TP
+.BR "\-x" " or " "\-\-export-csv"
+Write data to file(s) in CSV format. Output filenames will be based on the data filename.
+
+.TP
+.BR "\-t" " or " "\-\-topline"
+Repeat topline after specified number of frames.
+0 for no repeat (default).
+
+.SH OUTPUT
+Here is a list of the columns and their descriptions.
+Timestamps of the frames printed depict the ending of the respective timeframe.
+
+.TP
+.BR "adapter in %"
+FCP adapter utilization in percent.
+.br
+.IR "min" ", " "max" " and " avg "
+gives the minimum, maximum and average utilization respectively.
+
+.TP
+.BR "bus in %"
+Bus utilization of the FCP adapter in percent.
+.br
+.IR "min" ", " "max" " and " avg "
+gives the minimum, maximum and average utilization respectively.
+
+.TP
+.BR "cpu in %"
+Utilization of the cpu on the FCP adapter in percent.
+.br
+.IR "min" ", " "max" " and " avg "
+gives the minimum, maximum and average utilization respectively.
+
+.TP
+.BR "qdio util.%"
+gives the qdio utilization in percent.
+.br
+.IR "max" " and " avg "
+gives the maximum and average utilization respectively. Note that the minimum value is not reported since it provides no additional information: You will see the number of queue full encounters rise drastically before the minimum becomes non-zero.
+
+.TP
+.BR "queue full"
+The number of instances where a request to the adapter could not be submitted
+due to no empty slots left in the outbound queue.
+
+.TP
+.BR "fail"
+The number of failures as reported by the FCP adapter.
+.br
+.IR "erc"
+is the number of error recovery conditions.
+
+.TP
+.BR "thp in MS/s"
+gives the average throughput over time (transmitted data volume / elapsed time), not over number of requests (sum over all request throughputs / number of requests)! This means that a long-running request with a significantly different throughput profile from the rest will have a bigger impact as a brief one with the same throughput profile would. This gives a better impression of the overall profile and especially makes requests with very low throughputs have a bigger impact, making it easier to detect anomalies.
+.br
+.IR "rd" " and " "wrt"
+give the values for read and write requests respectively.
+
+.TP
+.BR "I/O reqs"
+give the number of I/O requests processed in the respective interval.
+.br
+.IR "rd" " and " "wrt"
+give the values for read and write requests respectively.
+
+
+.SH EXAMPLES
+Print a utilization report using
+.IR sample.log
+, considering adapters 4e and 4f only.
+Only data between 8:57 April 5, 2008, and 17:09 June 21, 2008, should be considered.
+Data should be aggregated to 60 second intervals.
+
+ziorep_utilization -c 4e -c 4f -i 60 -b "2008-04-05 08:57" -e "2008-06-21 17:09"
+
+
+.SH "SEE ALSO"
+.BR ziorep_config (8),
+.BR ziorep_traffic (8)
Index: s390-tools-sles11/ziomon/ziorep_utilization.cpp
===================================================================
--- /dev/null
+++ s390-tools-sles11/ziomon/ziorep_utilization.cpp
@@ -0,0 +1,372 @@
+/*
+ * FCP report generators
+ *
+ * Utilization report program
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Stefan Raspl <raspl@linux.vnet.ibm.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <linux/types.h>
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
+#include <assert.h>
+#include <limits.h>
+
+#include <list>
+
+#include "zt_common.h"
+#include "ziorep_framer.hpp"
+#include "ziorep_printers.hpp"
+#include "ziorep_frameset.hpp"
+#include "ziorep_utils.hpp"
+#include "ziorep_collapser.hpp"
+
+extern "C" {
+#include "ziomon_msg_tools.h"
+}
+
+
+using std::list;
+
+
+const char *toolname = "ziorep_utilization";
+int verbose=0;
+
+
+struct options {
+	__u64		begin;
+	__u64		end;
+	__u32		interval;
+	list<__u32>	chpids;
+	__u64		topline;
+	char*		filename;
+	bool		print_summary;
+	bool		csv_export;
+};
+
+
+static void init_opts(struct options *opts)
+{
+	opts->begin		= 0;
+	opts->end		= UINT64_MAX;
+	opts->interval		= UINT32_MAX;
+	opts->topline		= 0;
+	opts->filename		= NULL;
+	opts->print_summary	= false;
+	opts->csv_export	= false;
+}
+
+
+static const char help_text[] =
+    "Usage: ziorep_utilization [-V] [-v] [-h] [-b <begin>] [-e <end>] [-i <time>]\n"
+    "                          [-x] [-s] [-c <chpid>] [-t <num>] <filename>\n\n"
+    "-h, --help              Print usage information and exit.\n"
+    "-v, --version           Print version information and exit.\n"
+    "-V, --verbose           Be verbose.\n"
+    "-b, --begin <begin>     Do not consider data earlier than 'begin'.\n"
+    "                        Defaults to begin of available data.\n"
+    "                        Format is YYYY-MM-DD HH:MM[:SS],\n"
+    "                        e.g. '-b \"2008-03-21 09:08\"\n"
+    "-e, --end <end>         Do not consider data later than 'end'.\n"
+    "                        Defaults to end of available data.\n"
+    "                        Format is YYYY-MM-DD HH:MM[:SS],\n"
+    "                        e.g. '-e \"2008-03-21 09:08:57\"\n"
+    "-i, --interval <time>   Set aggregation interval to 'time' in seconds.\n"
+    "                        Must be a multiple of the interval size of the source\n"
+    "                        data.\n"
+    "                        Set to 0 to aggregate over all data.\n"
+    "-s, --summary           Show a summary of the data.\n"
+    "-c, --chpid <chpid>     Select physical adapter in hex.\n"
+    "                        E.g. '-c 32a'\n"
+    "-x, --export-csv        Export data to files in CSV format.\n"
+    "-t, --topline <num>     Repeat topline after every 'num' frames.\n"
+    "                        0 for no repeat (default).\n";
+
+
+static void print_help()
+{
+        printf("%s", help_text);
+}
+
+
+static void print_version()
+{
+        printf("%s: Utilization report generator version %s\n"
+               "Copyright IBM Corp. 2008\n", toolname, RELEASE_STRING);
+}
+
+
+static int parse_params(int argc, char **argv, struct options *opts)
+{
+	int c;
+	int index;
+	int rc;
+	__u32 tmp;
+	long tmpl;
+        static struct option long_options[] = {
+                { "version",         no_argument,       NULL, 'v'},
+		{ "help",            no_argument,       NULL, 'h'},
+		{ "verbose",         no_argument,       NULL, 'V'},
+		{ "begin",           required_argument, NULL, 'b'},
+                { "end",             required_argument, NULL, 'e'},
+		{ "interval",        required_argument, NULL, 'i'},
+		{ "summary",         no_argument,       NULL, 's'},
+		{ "chpid",           required_argument, NULL, 'c'},
+		{ "export-csv",      no_argument,       NULL, 'x'},
+		{ "topline",         required_argument, NULL, 't'},
+                { 0,                 0,                 0,     0 }
+	};
+
+	if (argc < 2) {
+		print_help();
+		return 1;
+	}
+
+	assert(sizeof(long long int) == sizeof(__u64));
+	while ((c = getopt_long(argc, argv, "b:e:i:c:t:xshvV",
+				long_options, &index)) != EOF) {
+		switch (c) {
+		case 'V':
+			verbose++;
+			break;
+		case 'h':
+			print_help();
+			return 1;
+		case 'v':
+			print_version();
+			return 1;
+		case 'b':
+			if (get_datetime_val(optarg, &opts->begin))
+				return -1;
+			break;
+		case 'e':
+			if (get_datetime_val(optarg, &opts->end))
+				return -1;
+			break;
+		case 'i':
+			if (sscanf(optarg, "%lu", &tmpl) != 1) {
+				fprintf(stderr, "%s: Cannot parse %s as an integer value."
+					" Please correct and try again.\n", toolname,
+					optarg);
+				return -1;
+			}
+			if (tmpl < 0) {
+				fprintf(stderr, "%s: Argument %s must be greater than or"
+					" equal to 0.", toolname, optarg);
+				return -1;
+			}
+			opts->interval = tmpl;
+			break;
+		case 's':
+			opts->print_summary = true;
+			break;
+		case 'c':
+			rc = sscanf(optarg, "%x", &tmp);
+			if (rc != 1) {
+				fprintf(stderr, "Error: Could not read chpid"
+					" %s\n", optarg);
+				return -1;
+			}
+			opts->chpids.push_back(tmp);
+			break;
+		case 'x':
+			opts->csv_export = true;
+			break;
+		case 't':
+			if (parse_topline_arg(optarg, &opts->topline))
+				return -1;
+			break;
+		default:
+			fprintf(stderr, "%s: Try '%s --help' for"
+				" more information.\n", toolname, toolname);
+			return -1;
+		}
+	}
+	if (optind == argc - 1)
+		opts->filename = argv[optind];
+	if (optind < argc - 1) {
+		fprintf(stderr, "%s: Multiple filenames"
+			" specified. Specify only a single one at a time.\n", toolname);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int check_opts(struct options *opts, ConfigReader **cfg)
+{
+	int rc = 0;
+
+	// check filename
+	if (!opts->filename) {
+		fprintf(stderr, "%s: No filename"
+			" specified.\n", toolname);
+		return -2;
+	}
+	else {
+		if (strncmp(opts->filename + strlen(opts->filename)
+			    - strlen(DACC_FILE_EXT_LOG), DACC_FILE_EXT_LOG,
+			    strlen(DACC_FILE_EXT_LOG)) == 0) {
+			verbose_msg("Filename carries " DACC_FILE_EXT_LOG
+				    " extension - stripping\n");
+			opts->filename[strlen(opts->filename)
+					- strlen(DACC_FILE_EXT_LOG)] = '\0';
+		}
+		if (strncmp(opts->filename + strlen(opts->filename)
+			    - strlen(DACC_FILE_EXT_AGG), DACC_FILE_EXT_AGG,
+			    strlen(DACC_FILE_EXT_AGG)) == 0) {
+			verbose_msg("Filename carries " DACC_FILE_EXT_AGG
+				    " extension - stripping\n");
+			opts->filename[strlen(opts->filename)
+					- strlen(DACC_FILE_EXT_AGG)] = '\0';
+		}
+		verbose_msg("Filename is %s\n", opts->filename);
+	}
+
+	// check config
+	*cfg = new ConfigReader(&rc, opts->filename);
+	if (rc)
+		return -1;
+
+	// check optional chpids
+	for (list<__u32>::const_iterator i = opts->chpids.begin();
+	      i != opts->chpids.end(); ++i) {
+		if (!(*cfg)->verify_chpid(*i)) {
+			fprintf(stderr, "Error: Could not find chpid %x in"
+				" configuration.\n", *i);
+			rc = -2;
+		}
+	}
+
+	if (opts->csv_export && opts->topline > 0) {
+		fprintf(stderr, "%s: Warning: Both, topline"
+			" repeat and CSV export activated, deactivating"
+			" topline repeat.\n", toolname);
+		opts->topline = 0;
+	}
+
+	if (!opts->print_summary
+		&& adjust_timeframe(opts->filename, &opts->begin, &opts->end,
+			     &opts->interval))
+		rc = -3;
+
+	return rc;
+}
+
+
+static int print_reports(struct options *opts, ConfigReader &cfg)
+{
+	int rc = 0;
+	PhysAdapterPrinter physPrnt(&cfg, opts->csv_export);
+	VirtAdapterPrinter virtPrnt(&cfg, opts->csv_export);
+	Aggregator agg = devno;
+	StagedDeviceFilter dev_filt;
+	NoopCollapser noop_col;
+	list<__u32> devnos;
+	list<__u32> chpids;
+	list<MsgTypes> type_flt;
+	AggregationCollapser *col = NULL;
+	FILE *fp;
+
+	if (opts->chpids.size())
+		chpids = opts->chpids;
+	else
+		cfg.get_unique_chpids(chpids);
+
+	cfg.get_unique_mms(devnos);
+
+	// add all HBAs and all devices connected to them
+	for (list<__u32>::const_iterator i = chpids.begin();
+	      i != chpids.end(); ++i) {
+		cfg.get_devnos_by_chpid(devnos, *i);
+		for (list<__u32>::const_iterator j = devnos.begin();
+		      j != devnos.end(); ++j)
+			dev_filt.stage_devno(*j);
+	}
+	dev_filt.finish(cfg, false);
+
+	col = new AggregationCollapser(cfg, agg, dev_filt, &rc);
+
+	type_flt.push_back(utilization);
+
+	if (opts->csv_export) {
+		fp = open_csv_output_file(opts->filename,
+					  "_util_phys_adpt.csv", &rc);
+		if (!fp)
+			goto out;
+	}
+	else
+		fp = stdout;
+
+	if ( (rc = print_report(fp, opts->begin, opts->end,
+				opts->interval, opts->filename, opts->topline,
+				&type_flt, dev_filt, noop_col,
+				physPrnt)) < 0 ) {
+		rc = -3;
+		goto out1;
+	}
+
+	if (rc == 0)
+		fprintf(stderr, "%s: No eligible data found.\n", toolname);
+
+	if (opts->csv_export) {
+		fclose(fp);
+		fp = open_csv_output_file(opts->filename,
+					  "_util_virt_adpt.csv", &rc);
+		if (!fp)
+			goto out;
+	}
+	else {
+		fp = stdout;
+		fputc('\n', fp);
+	}
+
+	if (print_report(fp, opts->begin, opts->end, opts->interval,
+			 opts->filename, opts->topline, NULL, dev_filt,
+			 *col, virtPrnt)) {
+		rc = -4;
+		goto out1;
+	}
+
+out1:
+	if (opts->csv_export)
+		fclose(fp);
+out:
+	delete col;
+
+	return rc;
+}
+
+
+int main(int argc, char **argv)
+{
+	int rc;
+	struct options opts;
+	ConfigReader *cfg = NULL;
+
+	verbose = 0;
+
+	init_opts(&opts);
+	if ( (rc = parse_params(argc, argv, &opts)) )
+		goto out;
+	if ( (rc = check_opts(&opts, &cfg)) )
+		goto out;
+
+	if (opts.print_summary)
+		rc = print_summary_report(stdout, opts.filename, *cfg);
+	else
+		rc = print_reports(&opts, *cfg);
+
+out:
+	delete cfg;
+
+	return rc;
+}
+
+
+
Index: s390-tools-sles11/ziomon/ziorep_utils.cpp
===================================================================
--- /dev/null
+++ s390-tools-sles11/ziomon/ziorep_utils.cpp
@@ -0,0 +1,555 @@
+/*
+ * FCP report generators
+ *
+ * Utility functions
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Stefan Raspl <raspl@linux.vnet.ibm.com>
+ */
+
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "ziorep_utils.hpp"
+#include "ziorep_cfgreader.hpp"
+
+extern "C" {
+	#include "ziomon_msg_tools.h"
+}
+
+extern const char *toolname;
+extern int verbose;
+
+
+
+/**
+ * Read all essential data from the files,
+ * including the headers, timestamp of first message
+ * and a DeviceFilter for all available devices.
+ * Note that 'agg' is NULL if not available and must
+ * be free()'d otherwise. */
+static int get_initial_data(const char *filename, struct file_header *f_hdr,
+			    struct aggr_data **agg,
+			    DeviceFilter &dev_filt, ConfigReader &cfg)
+{
+	FILE *fp;
+	int rc = 0;
+	__u64 begin;
+
+	if (open_data_files(&fp, filename, f_hdr, agg))
+		return -1;
+	close_data_files(fp);
+
+	/*
+	 * Retrieve first real frame
+	 */
+	MsgTypeFilter msgtype_filter;
+	NoopCollapser	nop_col;
+	Frameset frameset(&nop_col);
+	list<MsgTypes> type_flt;
+	type_flt.push_back(ioerr);
+
+	// we retrieve the first interval only, and eventually the .agg data
+	// as well
+	if (*agg)
+		begin = (*agg)->end_time;
+	else
+		begin = f_hdr->begin_time;
+
+	Framer framer(begin,
+		      begin + f_hdr->interval_length,
+		      f_hdr->interval_length, &type_flt,
+		      (DeviceFilter*)NULL, filename, &rc);
+	vector <struct ioerr_cnt*> ioerrs;
+	do {
+		if ( framer.get_next_frameset(frameset) != 0 ) {
+			fprintf(stderr, "%s: Could not read"
+				" any frames in %s%s\n", toolname, filename,
+				DACC_FILE_EXT_LOG);
+			return -2;
+		}
+
+		/*
+		 * Construct a DeviceFilter with all devices.
+		 * NOTE: The very first ioerr msg might already have been moved to the .agg
+		 * file - hence we have to consider the .agg data as well!
+		 */
+		ioerrs = frameset.get_ioerr_stats();
+		rc = 0;
+		for (vector<struct ioerr_cnt*>::const_iterator i = ioerrs.begin();
+		      i != ioerrs.end(); ++i) {
+			vverbose_msg("    add device: hctl=[%d:%d:%d:%d], mm=%d\n",
+				    (*i)->identifier.host, (*i)->identifier.channel,
+				    (*i)->identifier.target, (*i)->identifier.lun,
+				    cfg.get_mm_by_ident(&(*i)->identifier, &rc));
+			dev_filt.add_device(cfg.get_mm_by_ident(&(*i)->identifier, &rc), &(*i)->identifier);
+			if (rc)
+				return -1;
+		}
+	} while ( frameset.is_aggregated() && !ioerrs.size());
+
+	if (dev_filt.get_host_id_list().size() == 0 || dev_filt.get_mm_list().size() == 0) {
+		fprintf(stderr, "%s: Could not retrieve initial data"
+			" - data files corrupted or broken, or the .agg file is missing.\n",
+			toolname);
+		return -1;
+	}
+
+	verbose_msg("retrieve initial data FINISHED\n");
+
+	return 0;
+}
+
+
+int add_all_devices(ConfigReader &cfg, DeviceFilter &dev_filt)
+{
+	int rc = 0;
+	list<__u32> lst;
+
+	cfg.get_unique_mms(lst);
+	for (list<__u32>::const_iterator i = lst.begin();
+	      i != lst.end(); ++i) {
+		dev_filt.add_device(*i, cfg.get_ident_by_mm_internal(*i, &rc));
+		assert(rc == 0);
+	}
+
+	return 0;
+}
+
+
+int get_all_devices(const char *filename, DeviceFilter &dev_filt,
+		    ConfigReader &cfg)
+{
+	struct file_header f_hdr;
+	struct aggr_data *agg;
+	int rc = 0;
+
+	rc = get_initial_data(filename, &f_hdr, &agg, dev_filt, cfg);
+	discard_aggr_data_struct(agg);
+	free(agg);
+
+	return rc;
+}
+
+
+#define BUF_SZ		256
+
+const char* print_time_formatted(__u64 timestamp)
+{
+	time_t t = timestamp;
+	static char buf[BUF_SZ];
+
+	strftime(buf, BUF_SZ - 1, "%Y-%m-%d %H:%M:%S", localtime(&t));
+
+	return buf;
+}
+
+
+const char* print_time_formatted_short(__u64 timestamp)
+{
+	time_t t = timestamp;
+	static char buf[BUF_SZ];
+
+	strftime(buf, BUF_SZ - 1, "%H:%M:%S", localtime(&t));
+
+	return buf;
+}
+
+
+// adjust lower timestamp to last frame boundary that was touched
+static __u64 round_lower_boundary(__u64 time, __u64 begin,
+			       __u32 interval_length)
+{
+	__u64 tmp = (time - begin)%interval_length;
+
+	if (tmp)
+		time += (interval_length - tmp);
+
+	return time;
+}
+
+
+// adjust upper timestamp to last frame boundary that was touched
+static __u64 round_upper_boundary(__u64 time, __u64 begin,
+				     __u32 interval_length)
+{
+	__u64 tmp = (time - begin)%interval_length;
+
+	if (tmp)
+		time -= tmp;
+
+	return time;
+}
+
+
+/**
+ * The times will be adjusted to respective exakt frame boundaries.
+ * The end time is only ever used to determine whether we are done - we stop
+ * in case the begin of the timeframe is later than the end date.
+ * Note that in case we scratch into the .agg data, we
+ * (a) take all of it
+ * (b) synonymously associate the .agg data with the timestamp of its
+ *     final frame. This is _utterly_ important, especially in context
+ *     with the interval: If it is user adjusted, and the date range touches
+ *     the .agg data, we adjust the begin to the .agg data's final frame's
+ *     timestamp, and make sure that when we process the agg data, the next
+ *     frame is 1 unit of the original interval length away. Only after we
+ *     have shifted into the 'regular' .log data can we apply any user-set
+ *     interval length.
+ * */
+int adjust_timeframe(const char *filename, __u64 *begin, __u64 *end,
+		     __u32 *interval)
+{
+	struct file_header f_hdr;
+	struct aggr_data *agg = NULL;
+	FILE *fp;
+	time_t t;
+	int rc = 0;
+
+	verbose_msg("adjust timeframe:\n");
+
+	// check begin and end time
+	if (*begin > *end) {
+		fprintf(stderr, "%s: Start time must be"
+			" prior to end time.\n", toolname);
+		rc = -1;
+		goto out;
+	}
+
+	if (open_data_files(&fp, filename, &f_hdr, &agg)) {
+		rc = -1;
+		goto out;
+	}
+	close_data_files(fp);
+
+	// check if begin scratches into .agg data
+	// if so, we use the _end_ time of the .agg frame
+	if (*begin == 0 && agg) {
+		verbose_msg("    begin time: take from .agg data\n");
+		*begin = agg->end_time;
+	}
+	else if (agg && *begin < agg->begin_time) {
+		fprintf(stderr, "%s: Warning: Begin of timeframe is before"
+			" earliest available data, which is %s.\n", toolname,
+			print_time_formatted(agg->begin_time));
+		*begin = agg->end_time;
+		verbose_msg("    begin time: adjust to begin from .agg data\n");
+	}
+	else if (agg && *begin <= agg->end_time) {
+		*begin = agg->end_time;
+		verbose_msg("    begin time: set to end of .agg data\n");
+	}
+	else if (*begin < f_hdr.begin_time) {
+		*begin = f_hdr.begin_time;
+		verbose_msg("    begin time: adjust to begin of .log data\n");
+	}
+	else if (*begin > f_hdr.end_time) {
+		fprintf(stderr, "%s: Begin of timeframe is past the"
+			" end of available data, which is %s.\n",
+			toolname, print_time_formatted(f_hdr.end_time));
+		rc = -1;
+		goto out;
+	}
+	else {
+		verbose_msg("    begin time: round to nearest boundary\n");
+		*begin = round_lower_boundary(*begin, f_hdr.begin_time, f_hdr.interval_length);
+	}
+	t = *begin;
+	verbose_msg("    begin time set to: %s", ctime(&t));
+
+	if (*end == UINT64_MAX) {
+		*end = f_hdr.end_time;
+		verbose_msg("    end time  : take from .log data\n");
+	}
+	else if (agg && *end < agg->begin_time) {
+		fprintf(stderr, "%s: End of timeframe is prior to earliest"
+			" available data, which is %s.\n", toolname,
+			print_time_formatted(agg->begin_time));
+		rc = -1;
+		goto out;
+	}
+	else if (agg && *end < agg->end_time) {
+		*end = agg->end_time;
+		verbose_msg("    end time  : take from .agg data\n");
+	}
+	else if (!agg && *end < f_hdr.begin_time) {
+		fprintf(stderr, "%s: End of timeframe is prior to earliest"
+			" available data, which is %s.\n", toolname,
+			print_time_formatted(f_hdr.begin_time));
+		rc = -1;
+		goto out;
+	}
+	else if (*end > f_hdr.end_time) {
+		fprintf(stderr, "%s: Warning: End of timeframe is after"
+			" latest available data, which is %s.\n", toolname,
+			print_time_formatted(f_hdr.end_time));
+		*end = f_hdr.end_time;
+		verbose_msg("    end time  : adjust to end of .log data\n");
+	}
+	else {
+		*end = round_upper_boundary(*end, f_hdr.begin_time,
+					       f_hdr.interval_length);
+		verbose_msg("    end time  : round to nearest boundary\n");
+	}
+	t = *end;
+	verbose_msg("    end time set to  : %s", ctime(&t));
+	assert((*end - *begin) % f_hdr.interval_length == 0);
+
+	if (*interval == UINT32_MAX) {
+		*interval = f_hdr.interval_length;
+		verbose_msg("using original interval length: %lus\n",
+			    (long unsigned int)*interval);
+	}
+	/* the exact frame boundaries don't include the length of the very
+	   first interval, so we have to add one more to our calculations */
+	if (*interval && (*end - *begin + f_hdr.interval_length) % *interval != 0) {
+		// cut off rest in case of user-set interval
+		*end -= (*end - *begin) % *interval + f_hdr.interval_length;
+		t = *end;
+		verbose_msg("    cut off at : %s", ctime(&t));
+	}
+
+	// check if the interval is correct
+	if (*interval % f_hdr.interval_length) {
+		fprintf(stderr, "%s: Data aggregation interval %lu"
+			" is incompatible with source data. Please use"
+			" a multiple of %lu and try again.\n", toolname,
+			(long unsigned int)*interval,
+			(long unsigned int)(f_hdr.interval_length));
+		rc = -1;
+		goto out;
+	}
+
+out:
+	if (agg) {
+		discard_aggr_data_struct(agg);
+		free(agg);
+	}
+
+	return rc;
+}
+
+
+int print_report(FILE *fp, __u64 begin, __u64 end, __u32 interval,
+				char *filename, __u64 topline,
+				list<MsgTypes> *filter_types,
+				DeviceFilter &dev_filter, Collapser &col,
+				Printer &printer)
+{
+	int frames_printed = 0;
+	bool first_time = true;
+	time_t t;
+	int rc = 0;
+	Frameset frameset(&col);
+	Framer framer(begin, end, interval,
+		      filter_types, &dev_filter,
+		      filename, &rc);
+
+	if (rc)
+		return -1;
+
+	if (topline && printer.print_csv()) {
+		fprintf(stderr, "%s: Warning: Cannot use '-t' with CSV mode,"
+			" ignoring\n", toolname);
+		topline = 0;
+	}
+
+	verbose_msg("print report for:\n");
+	t = (time_t)begin;
+	verbose_msg("    begin    : %s", ctime(&t));
+	t = (time_t)end;
+	verbose_msg("    end      : %s", (end == UINT64_MAX ? "-\n" : ctime(&t)));
+	verbose_msg("    interval : %lu\n", (long unsigned int)interval);
+	verbose_msg("    topline  : %llu\n", (long long unsigned int)topline);
+	verbose_msg("    csv mode : %d\n", printer.print_csv());
+
+	while ( (rc = framer.get_next_frameset(frameset, true)) == 0 ) {
+		vverbose_msg("printing frameset %d\n", frames_printed);
+		if (first_time || (topline && frames_printed % topline == 0)) {
+			first_time = false;
+			printer.print_topline(fp, frameset);
+		}
+		if (printer.print_frame(fp, frameset, dev_filter) < 0)
+			return -1;
+		++frames_printed;
+	}
+
+	if (rc > 0)
+		return frames_printed;
+
+	return rc;
+}
+
+
+int print_summary_report(FILE *fp, char *filename, ConfigReader &cfg)
+{
+	int rc = 0;
+	int lrc=0;
+	struct file_header f_hdr;
+	struct aggr_data *a_hdr;
+	DeviceFilter dev_filt;
+
+	if (get_initial_data(filename, &f_hdr, &a_hdr, dev_filt, cfg))
+		return -1;
+
+	rc += fprintf(fp, "Data Summary\n");
+	rc += fprintf(fp, "------------\n");
+
+	rc += fprintf(fp, "Aggregated range: ");
+	if (a_hdr) {
+		rc += fprintf(fp, "%s to ",
+			      print_time_formatted(a_hdr->begin_time - f_hdr.interval_length));
+		rc += fprintf(fp, "%s\n",
+			      print_time_formatted(a_hdr->end_time));
+	}
+	else
+		rc += fprintf(fp, "none\n");
+
+	discard_aggr_data_struct(a_hdr);
+	free(a_hdr);
+	a_hdr = NULL;
+
+	rc += fprintf(fp, "Detailed range:   %s to ",
+		      print_time_formatted(f_hdr.begin_time - f_hdr.interval_length));
+	rc += fprintf(fp, "%s\n", print_time_formatted(f_hdr.end_time));
+	rc += fprintf(fp, "Interval length:  %d seconds\n",
+		      f_hdr.interval_length);
+
+	list<__u32> disks = dev_filt.get_mm_list();
+	list<__u32> host_ids  = dev_filt.get_host_id_list();
+	int first = 1;
+	const char *frmt;
+	for (list<__u32>::const_iterator i = host_ids.begin();
+	      i != host_ids.end(); ++i) {
+		if (first) {
+			frmt = "HBA/CHPID:        0.0.%04x/%x\n";
+			first = 0;
+		}
+		else
+			frmt = "                  0.0.%04x/%x\n";
+		rc += fprintf(fp, frmt, cfg.get_devno_by_host_id(*i, &lrc),
+			      cfg.get_chpid_by_host_id(*i, &lrc));
+		if (lrc)
+			return -1;
+	}
+
+	first = 1;
+	for (list<__u32>::const_iterator i = disks.begin();
+	      i != disks.end(); ++i) {
+		if (first) {
+			frmt = "WWPN/LUN (dev):   0x%016Lx/0x%016Lx (%s)\n";
+			first = 0;
+		}
+		else
+			frmt = "                  0x%016Lx/0x%016Lx (%s)\n";
+		rc += fprintf(fp, frmt, cfg.get_wwpn_by_mm_internal(*i, &lrc),
+			      cfg.get_lun_by_mm_internal(*i, &lrc),
+			      cfg.get_dev_by_mm_internal(*i, &lrc));
+		if (lrc)
+			return -1;
+	}
+
+	return rc;
+}
+
+/* Calculates seconds since 1970 _without_ caring for daylight
+   savings time (comtrary to mktime() et al).
+   It does not care for leap years and the like, which is OK,
+   since we use it in a very narrow scenario: To calculate any
+   daylight savings time related shifts.
+   Hence: Dont't use if you're not sure what you are doing... */
+static __u64 secs_since_1970(const struct tm *t) {
+	__u64 res = 0;
+	res += t->tm_sec;
+	res += 60 * t->tm_min;
+	res += 3600 * t->tm_hour;
+	res += 86400 * t->tm_yday;
+	res += 86400 * 365 * t->tm_year;
+
+	return res;
+}
+
+
+int get_datetime_val(const char *str, __u64 *tgt)
+{
+	struct tm t, t_old;
+	char *ret;
+
+	// strptime only sets
+	memset(&t, 0, sizeof(struct tm));
+	ret = strptime(str, "%Y-%m-%d %H:%M", &t);
+	if (ret == NULL || *ret != '\0') {
+		ret = strptime(str, "%Y-%m-%d %H:%M:%S", &t);
+		if (ret == NULL || *ret != '\0') {
+			fprintf(stderr, "%s: Could not parse date %s."
+				" Please use format as specified in"
+				" man-page\n", toolname, str);
+			return -1;
+		}
+	}
+	t_old = t;
+	*tgt = mktime(&t);
+	// if daylight savings time applies, 't' has been adjusted,
+	// so we have to correct
+	if (t_old.tm_hour != t.tm_hour)
+		*tgt -= secs_since_1970(&t) - secs_since_1970(&t_old);
+	verbose_msg("datetime value from user after translation: %s", ctime((const time_t *)tgt));
+
+	return 0;
+}
+
+
+int parse_topline_arg(char *str, __u64 *arg)
+{
+	char *p;
+
+	*arg = strtoull(str, &p, 0);
+	if (*arg == ULLONG_MAX) {
+		fprintf(stderr, "%s: Cannot convert"
+			" %s, over/underflow occured. Try a smaller/larger"
+			" value.\n", toolname, str);
+		return -1;
+	}
+	if (*p != '\0') {
+		fprintf(stderr, "%s: Non-numeric"
+			" characters in argument '%s' to option '-t'. Make"
+			" sure to use only numeric characters.\n", toolname,
+			str);
+		return -1;
+	}
+
+	return 0;
+}
+
+FILE* open_csv_output_file(const char *filename, const char *extension,
+			   int *rc)
+{
+	char *tmp;
+	FILE *fp = NULL;
+
+	*rc = 0;
+
+	tmp = (char*)malloc(strlen(filename) + strlen(extension) + 1);
+
+	sprintf(tmp, "%s%s", filename, extension);
+	fp = fopen(tmp, "w");
+
+	if (!fp) {
+		fprintf(stdout, "%s: Could not open file %s. Make sure that you"
+			" have sufficient permissions and try again.\n", toolname, tmp);
+		*rc = -1;
+	}
+	else
+		fprintf(stdout, "Exporting data in CSV format to %s\n", tmp);
+
+	free(tmp);
+
+	return fp;
+}
+
+
+
+
Index: s390-tools-sles11/ziomon/ziorep_utils.hpp
===================================================================
--- /dev/null
+++ s390-tools-sles11/ziomon/ziorep_utils.hpp
@@ -0,0 +1,85 @@
+/*
+ * FCP report generators
+ *
+ * Utility functions
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Stefan Raspl <raspl@linux.vnet.ibm.com>
+ */
+
+#ifndef ZIOREP_UTILS_HPP
+#define ZIOREP_UTILS_HPP
+
+#include "ziorep_framer.hpp"
+#include "ziorep_cfgreader.hpp"
+#include "ziorep_printers.hpp"
+#include "ziorep_collapser.hpp"
+
+extern "C" {
+#include "ziomon_dacc.h"
+}
+
+
+#include <linux/types.h>
+
+/**
+ * Parse date provided in 'str' and store as seconds since 1970 in 'tgt'. */
+int get_datetime_val(const char *str, __u64 *tgt);
+
+
+/**
+ * Print the provided value (seconds since 1970) in our common style. */
+const char* print_time_formatted(__u64 timestamp);
+
+/**
+ * Print the provided value (seconds since 1970) in our common style. */
+const char* print_time_formatted_short(__u64 timestamp);
+
+/**
+ * Adjust the timeframe to appropriate interval boundaries */
+int adjust_timeframe(const char* filename, __u64 *begin, __u64 *end,
+		     __u32 *interval);
+
+
+/**
+ * Add all present devices to the device filter */
+int add_all_devices(ConfigReader &cfg, DeviceFilter &dev_filt);
+
+
+/**
+ * Utility function to retrieve a list of all devices that we have data for.
+ * Result is put into 'dev_filt'.
+ */
+int get_all_devices(const char *filename, DeviceFilter &dev_filt,
+		    ConfigReader &cfg);
+
+/**
+ * Run over frames and print each one.
+ * Returns <0 in case of error and number of frames printed otherwise.
+ */
+int print_report(FILE *fp, __u64 begin, __u64 end,
+				__u32 interval,
+				char *filename, __u64 topline,
+				list<MsgTypes> *filter_types,
+				DeviceFilter &dev_filter, Collapser &col,
+				Printer &printer);
+
+/**
+ * Print summary of available data.
+ * 'fp' is the file to write all output to, 'filename' the standard
+ * basename.
+ * Returns number of characters printed.
+ */
+int print_summary_report(FILE *fp, char *filename, ConfigReader &cfg);
+
+
+/**
+ * Minor help function to parse a string into a __u64 and check
+ * that it is >= 0 */
+int parse_topline_arg(char *str, __u64 *arg);
+
+FILE* open_csv_output_file(const char *filename, const char *extension,
+			   int *rc);
+
+#endif
+
Index: s390-tools-sles11/zipl/boot/Makefile
===================================================================
--- s390-tools-sles11.orig/zipl/boot/Makefile
+++ s390-tools-sles11/zipl/boot/Makefile
@@ -1,13 +1,16 @@
 # Common definitions
 include ../../common.mak
 
-CFLAGS  += -I../include -D__ASSEMBLY__ -DVERSION=$(VERSION) \
-	  -DRELEASE=$(RELEASE) -DPATCHLEVEL=$(PATCHLEVEL)
+S390_TOOLS_VERSION_EBCDIC := $(shell echo -n $(S390_TOOLS_RELEASE) | dd conv=ebcdic | hexdump -v -e '/1 "0x%02x,"' | sed 's/.$$//')
+
+CFLAGS  += -I../include -D__ASSEMBLY__ \
+		-DS390_TOOLS_VERSION_EBCDIC=$(S390_TOOLS_VERSION_EBCDIC)
 LDFLAGS = -O2
-HEADERS = fba0.h fba2.h eckd0.h eckd1a.h eckd1b.h eckd2.h eckd2dump.h \
-          tapedump.h stage3.h tape0.h fba2dump.h eckd2mvdump.h
+FILES	:= fba0.bin fba2.bin eckd0.bin eckd1a.bin eckd1b.bin eckd2.bin \
+	   eckd2dump.bin tapedump.bin stage3.bin tape0.bin fba2dump.bin \
+	   eckd2mvdump.bin
 
-all: data.c tape0.bin
+all: data.o data.h tape0.bin
 
 # Prevent make from using some default rules...
 %:	%.S
@@ -36,20 +39,18 @@ all: data.c tape0.bin
 	$(CC) $(LDFLAGS) -o $@ $$SFLAGS $<
 
 %.bin:	%.exec
-	$(OBJCOPY) -O binary --only-section .text $< $@
+	$(OBJCOPY) -O binary --only-section=.text $< $@
 
-%.h:	%.bin
-	@( echo "const char boot_data_"$(basename $<)"[] = {";\
-	hexdump -v -e '1/1 "0x%02x,"' $<;\
-	echo "};";\
-	echo ;\
-	) >$@
+data.o: $(FILES)
+	$(LD) -r -b binary -o data.o $(FILES)
 
-data.c: $(HEADERS)
-	cat $(HEADERS) >data.c
+data.h: data.o
+	rm -f data.h
+	$(NM) data.o | while read ADDR TYPE SYMBOL ; do \
+				echo "extern char $$SYMBOL;" >>data.h; done
 
 clean:
-	rm -f *.o *.exec *.bin $(HEADERS) data.c tape0.bin
+	rm -f *.o *.exec *.bin $(FILES) data.o data.h tape0.bin
 
 .PHONY: all clean
 
Index: s390-tools-sles11/zipl/boot/dumpcommon.S
===================================================================
--- s390-tools-sles11.orig/zipl/boot/dumpcommon.S
+++ s390-tools-sles11/zipl/boot/dumpcommon.S
@@ -46,9 +46,9 @@
 #define ENOTIME          0x00000202  /* zipl time stamps do not match */
 
 #define __LC_ARCH_MODE_ID 163        /* here is the arch flag in the lowcore */
-#define __LC_EXT_NEW_PSW_S390  0x058
-#define __LC_EXT_NEW_PSW_S390X 0x01b0
-#define __LC_EXT_INT_CODE_ADDR 0x86
+#define __LC_IPIB         0xe00      /* IPL Parameter Information Block */
+#define DIAG308_IPL       3          /* Subcode 3 - Perform Load Clear  */
+#define DIAG308_SET       5          /* Subcode 5 - Set IPL Parameters  */
 
 #define PARAM_START        0x3000  /* 8-byte time stamp plus 2-byte count  */
                                    /* plus 32 13-byte entries              */
@@ -302,15 +302,37 @@ _wait4de_64:
 .Lpsw:  .long  0x0
 
 ################################################################################
-# Panik routine. Loads a disabled wait psw
+# Panik routine. Loads a disabled wait psw or performs reipl if required.
 #   -r2 : panik code
 ################################################################################
 
 _panik_64:
         stmg   %r6,%r15,48(%r15)
         basr   %r13,0                    # base register
-0:      stg    %r2,1f-0b+8(%r13)         # store code in address part of psw
+0:      aghi   %r15,-200                 # create stack frame
+	stg    %r2,1f-0b+8(%r13)         # store code in address part of psw
 	bras   %r14,_print_exit_message
+
+	# perform reipl: check lowcore for the address of an IPL Information
+	# Block followed by a valid checksum (as defined in lowcore.h and set
+	# by ipl.c). In case of match use diag308 to IPL.
+
+	lg    %r2,__LC_IPIB(%r0)                 # IPL Information Block
+	ltr   %r2,%r2                            # re-ipl required?
+	bz    .Lnoreipl-0b(%r13)                 # no, skip diag308
+	llgf  %r3,0(%r2)                         # length of IPIB
+	sr    %r1,%r1                            # zero accumator
+.Lcksmloop:
+	cksm  %r1,%r2                            # compute IPIB checksum
+	bnz   .Lcksmloop-0b(%r13)                # repeat if not complete
+	cl    %r1,__LC_IPIB+8(%r0)               # compare with cksm in lowcore
+	bne   .Lnoreipl-0b(%r13)                 # no match, skip diag308
+	lg    %r2,__LC_IPIB(%r0)                 # reload IPL Information Block
+	lghi  %r4,DIAG308_SET                    # load subcode
+	diag  %r2,%r4,0x308                      # set IPL parameters
+	lghi  %r4,DIAG308_IPL                    # load subcode
+	diag  %r2,%r4,0x308                      # perform load clear
+.Lnoreipl:
 	lpswe  1f-0b(%r13)
         .align 8
 1:      .long  0x00020000,0x80000000,0x00000000,0x00000000
@@ -621,7 +643,7 @@ _create_ida_list_64:
 _init_print_progress_32:
 	stm    %r6,%r15,24(%r15)
 	basr   %r13,0                   # base register
-0:	s      %r15,.Lc96-0b(%r13)
+0:	ahi    %r15,-96
 
 	l      %r2,.Ldh_mem_size+4-0b(%r13)	
 	srl    %r2,20
@@ -641,7 +663,7 @@ _init_print_progress_32:
 _print_progress_32:
 	stm    %r6,%r15,24(%r15)
 	basr   %r13,0                   # base register
-0:	s      %r15,.Lc96-0b(%r13)
+0:	ahi    %r15,-96
 
 	# check, if we have to print something
 
@@ -691,7 +713,7 @@ _print_progress_32:
 _enable_device_32:
         stm    %r6,%r15,24(%r15)
         basr   %r13,0                   # base register
-0:      s      %r15,.Lc96-0b(%r13)
+0:      ahi    %r15,-96
         lr     %r1,%r2
         lhi    %r2,EENABLE_DEV          # set panik code early
         stsch  .Ltmp_data-0b(%r13)
@@ -715,7 +737,7 @@ _enable_device_32:
 _ssch_32:
         stm    %r6,%r15,24(%r15)
         basr   %r13,0                    # base register
-0:      s      %r15,.Lc96-0b(%r13)       # create stack frame
+0:      ahi    %r15,-96                  # create stack frame
 	lhi    %r8,10                    # set retry count for ssch
         lr     %r12,%r2                  # save subchannel id
         lr     %r11,%r3                  # save orb
@@ -753,7 +775,7 @@ _ssch_32:
 _wait4de_32:
         stm    %r6,%r15,24(%r15)
         basr   %r13,0                   # base register
-0:      s      %r15,.Lc96-0b(%r13)      # create stack frame
+0:      ahi    %r15,-96                 # create stack frame
         lr     %r1,%r2
         mvc    0x78(8),6f-0b(%r13)      # set i/o new psw
 1:      lpsw   5f-0b(%r13)
@@ -778,16 +800,37 @@ _wait4de_32:
 .Lpsw:  .long  0x0
 
 ################################################################################
-# Panik routine. Loads a disabled wait psw
+# Panik routine. Loads a disabled wait psw or performs reipl if required.
 #   -r2 : panik code
 ################################################################################
 
 _panik_32:
 	stm    %r6,%r15,24(%r15)
 	basr   %r13,0                   # base register
-0:	s      %r15,.Lc96-0b(%r13)
-
+0:	ahi    %r15,-96
+	st     %r2,1f-0b+4(%r13)        # store code in address part of psw
 	bras   %r14,_print_exit_message
+
+	# perform reipl: check lowcore for the address of an IPL Information
+	# Block followed by a valid checksum (as defined in lowcore.h and set
+	# by ipl.c). In case of match use diag308 to IPL.
+
+	l     %r2,__LC_IPIB(%r0)                 # IPL Information Block
+	ltr   %r2,%r2                            # re-ipl required?
+	bz    .Lnoreipl-0b(%r13)                 # no, skip diag308
+	l     %r3,0(%r2)                         # length of IPIB
+	sr    %r1,%r1                            # zero accumator
+.Lcksmloop:
+	cksm  %r1,%r2                            # compute IPIB checksum
+	bnz   .Lcksmloop-0b(%r13)                # repeat if not complete
+	cl    %r1,__LC_IPIB+4(%r0)               # compare with cksm in lowcore
+	bne   .Lnoreipl-0b(%r13)                 # no match, skip diag308
+	l     %r2,__LC_IPIB(%r0)                 # reload IPL Information Block
+	lhi   %r4,DIAG308_SET                    # load subcode
+	diag  %r2,%r4,0x308                      # set IPL parameters
+	lhi   %r4,DIAG308_IPL                    # load subcode
+	diag  %r2,%r4,0x308                      # perform load clear
+.Lnoreipl:
 	lpsw   1f-0b(%r13)
 	.align 8
 1:	.long  0x000a0000,0x00000000
@@ -815,7 +858,7 @@ _panik_32:
 _take_dump_32:
         stm   %r6,%r15,24(%r15)
         basr  %r13,0
-.Lbase: s     %r15,.Lc96-.Lbase(%r13)            # create stack frame
+.Lbase: ahi   %r15,-96                           # create stack frame
 
 	sckc  .Lclock_comparator-.Lbase(%r13)    # set clock comparator to future
 	spt   .Lcpu_timer-.Lbase(%r13)           # set cpu timer to future
@@ -878,7 +921,7 @@ _take_dump_32:
 _count_mem_32:
         stm    %r6,%r15,24(%r15)
         basr   %r13,0                         # base register
-0:      s      %r15,.Lc96-0b(%r13)            # create stack frame
+0:      ahi    %r15,-96                       # create stack frame
 
         bras  %r14,_sclp_read_info
         chi   %r2,0
@@ -953,7 +996,7 @@ _count_mem_32:
 _create_ida_list_32:
         stm    %r6,%r15,24(%r15)
         basr   %r13,0                         # base register
-0:      s      %r15,.Lc96-0b(%r13)            # create stack frame
+0:      ahi    %r15,-96                       # create stack frame
 
         # setup new pgc psw for finding invalid pages
 
@@ -1009,7 +1052,7 @@ _create_ida_list_32:
 _store_status_32:
         stm   %r6,%r15,24(%r15)
         basr  %r13,0                          # base register
-0:      s     %r15,.Lc96-0b(%r13)
+0:      ahi   %r15,-96
         la    %r7,0x0                         # base register for 0 page
 
         ######## move lowcore info (assume user has made store  ########
@@ -1054,7 +1097,7 @@ _store_status_32:
 _copy_lowcore_32:
         stm    %r6,%r15,24(%r15)
         basr   %r13,0                         # base register
-0:      s      %r15,.Lc96-0b(%r13)
+0:      ahi    %r15,-96
 
         l      %r3,0x108(%r0)                 # get prefix page from lowcore
 
@@ -1116,7 +1159,7 @@ _copy_lowcore_32:
 _print_exit_message:
 	stm    %r6,%r15,24(%r15)
 	basr   %r13,0                         # base register
-0:	s      %r15,.Lc96-0b(%r13)
+0:	ahi    %r15,-96
 
 	lr     %r10,%r2
 
@@ -1224,9 +1267,8 @@ _print_exit_message:
 
 .Lmsg_start:
 	.byte 0xa9, 0xc9, 0xd7, 0xd3, 0x40, 0xa5
-	.byte 0xf0 + VERSION, 0x4b
-	.byte 0xf0 + RELEASE, 0x4b
-	.byte 0xf0 + PATCHLEVEL, 0x40
+	.byte S390_TOOLS_VERSION_EBCDIC
+	.byte 0x40
 	.byte 0x84, 0xa4, 0x94, 0x97, 0x40, 0xa3, 0x96, 0x96
 	.byte 0x93, 0x40, 0x4d
 #if defined(__s390x__)
Index: s390-tools-sles11/zipl/boot/eckd2dump.S
===================================================================
--- s390-tools-sles11.orig/zipl/boot/eckd2dump.S
+++ s390-tools-sles11/zipl/boot/eckd2dump.S
@@ -93,8 +93,6 @@ _start: 
         llgf   %r14,.Lenable_device_64-0b(%r13)
         basr   %r14,%r14
         bas    %r14,_get_device_characteristics_64-0b(%r13)
-        llgf   %r5,.Ldev_end_rec-0b(%r13)   # endblock (first block = nr 1) 
-        st     %r5,.Ldev_bpt-0b(%r13)      
         llgf   %r5,.Ldev_blk_size-0b(%r13)  # get blocksize
         stcm   %r5,3,.Lwrccw+2-0b(%r13)     # store blocksize into wr template
         stcm   %r5,3,.Llodata+14-0b(%r13)   # store blocksize into lodata
@@ -109,34 +107,26 @@ _start: 
 # Get device characteristics from zipl parameters (at 0x218)
 # The following parameter format is expected 2x8 byte:
 #
-# Dump Start: CCHH RBBN
-# Dump End:   CCHH RBBX
-#
-# - CC: Start/End Cylinder Number
-# - HH: Start/End Head Number
-# - R : Start/End Record Number
-# - BB: Blocksize
-# - N : Number of Heads of DASD
-# - X : Unused
-#
-# Cylinder, Heads are counted starting with 0.
-# Records are counted starting with 1.
-# We assume that the End Record Number is at track boundary.
-# This allows us to determine the number of Blocks Per Track.
+# struct eckd_dump_param {
+#       uint32_t        start_blk;
+#       uint32_t        end_blk;
+#       uint16_t        blocksize;
+#       uint8_t.        num_heads;
+#       uint8_t.        bpt;
+#       char            reserved[4];
+# } __attribute((packed));
 ################################################################################
 
 _get_device_characteristics_64:
         stmg   %r6,%r15,48(%r15)
         basr   %r13,0                   # base register
 0:      aghi   %r15,-200                # create stack frame
-        mvc    .Ldev_start_cyl-0b+2(2,%r13),0x218(%r0)
-        mvc    .Ldev_start_hd-0b+2(2,%r13),0x21a(%r0)
-        mvc    .Ldev_start_rec-0b+3(1,%r13),0x21c(%r0)
-        mvc    .Ldev_blk_size-0b+2(2,%r13),0x21d(%r0)
-        mvc    .Ldev_nr_hds-0b+3(1,%r13),0x21f(%r0)
-        mvc    .Ldev_end_cyl-0b+2(2,%r13),0x220(%r0)
-        mvc    .Ldev_end_hd-0b+2(2,%r13),0x222(%r0)
-        mvc    .Ldev_end_rec-0b+3(1,%r13),0x224(%r0)
+
+        mvc    .Ldev_start_blk-0b(4,%r13),0x218(%r0)
+        mvc    .Ldev_end_blk-0b(4,%r13),0x21c(%r0)
+        mvc    .Ldev_blk_size-0b+2(2,%r13),0x220(%r0)
+        mvc    .Ldev_nr_hds-0b+3(1,%r13),0x222(%r0)
+        mvc    .Ldev_bpt-0b+3(1,%r13),0x223(%r0)
         lmg    %r6,%r15,248(%r15)
         br     %r14
 
@@ -154,65 +144,6 @@ _dump_mem_64:    
 
 	bras %r14,_init_print_progress_64
 
-        # calculate start and end block
-
-        lghi    %r6,0
-        llgf    %r7,.Ldev_start_cyl-0b(%r13)
-        lghi    %r8,0
-        llgf    %r9,.Ldev_start_hd-0b(%r13)
-        llgf    %r10,.Ldev_start_rec-0b(%r13)
-        llgf    %r11,.Ldev_nr_hds-0b(%r13)
-        llgf    %r12,.Ldev_bpt-0b(%r13)       # = blocks per track
-
-        # calculate start block
-        #######################
-
-        # cyl * nr_hds * blkptr
-
-        mr      %r6,%r11
-        mr      %r6,%r12
-
-        # hd * recs
-
-        mr      %r8,%r12
-
-        # start block = cyl * nr_hds * bptr + hd * bptr + rec
-
-        ar      %r7,%r9
-        ar      %r7,%r10
-
-        bctr    %r7,0                         # decrement, we start counting
-                                              # beginning with block 0
-
-        st      %r7,.Ldev_start_blk-0b(%r13)
-        
-        
-
-        # calculate end block
-        #######################
-
-        llgf    %r7,.Ldev_end_cyl-0b(%r13)
-        llgf    %r9,.Ldev_end_hd-0b(%r13)
-
-        # cyl * nr_hds * blkptr
-
-        mr      %r6,%r11
-        mr      %r6,%r12
-
-        # hd * recs
-
-        mr      %r8,%r12
-
-        # end block = cyl * nr_hds * bptr + hd * bptr + rec
-
-        ar      %r7,%r9
-        ar      %r7,%r12
-
-        bctr    %r7,0                        # decrement, we start counting
-                                             # beginning with block 0
-
-        st      %r7,.Ldev_end_blk-0b(%r13)
-
         # calculate bytes per write (blksize * blwr)
 
         llgf    %r11,.Ldev_blk_size-0b(%r13)
@@ -375,8 +306,14 @@ _writeblock_64:
         xr      %r2,%r2                       # clear bot                  
         dr      %r2,%r7                       # cy=trk / heads, hd=trk % heads
                                               # r3: cy, r2: hd
+
+        lgr     %r8,%r3                       # r8=upper 12 bits of cylinder
+        srl     %r8,16                        # are coded into heads:
+        sll     %r8,4                         # c0c1c2h0 (2 byte)
+
         sll     %r3,16                        # combine to CCHH in r3
         or      %r3,%r2
+        or      %r3,%r8                       # add upper 12 bits
         st      %r3,.Ldedata+8-0b(%r13)       # store cchh to dedata
         st      %r3,.Llodata+4-0b(%r13)       # store cchh to lodata
         st      %r3,.Llodata+8-0b(%r13)       # store cchh to lodata
@@ -391,8 +328,14 @@ _writeblock_64:
 
         xr      %r4,%r4                       # cy2=tr2/heads, hd2=hd2 % heads
         dr      %r4,%r7                       # r5: cy2, r4: hd2
-        stcm    %r5,3,.Ldedata+12-0b(%r13)    # store cy2,hd2 to dedata
-        stcm    %r4,3,.Ldedata+14-0b(%r13)    # store cy2,hd2 to dedata
+
+        lgr     %r8,%r5                       # r8=upper 12 bits of cylinder
+        srl     %r8,16                        # are coded into heads:
+        sll     %r8,4                         # c0c1c2h0 (2 byte)
+        or      %r4,%r8
+
+        stcm    %r5,3,.Ldedata+12-0b(%r13)    # store cy2 to dedata
+        stcm    %r4,3,.Ldedata+14-0b(%r13)    # store hd2 to dedata
         lgr     %r5,%r11                      # restore r5
 
         # CCWs are setup now, arent they?
@@ -500,8 +443,6 @@ _start: basr   %r13,0
         l      %r14,.Lenable_device_32-0b(%r13)
         basr   %r14,%r14
         bas    %r14,_get_device_characteristics_32-0b(%r13)
-        l      %r5,.Ldev_end_rec-0b(%r13)   # endblock (first block = nr 1) 
-        st     %r5,.Ldev_bpt-0b(%r13)      
         l      %r5,.Ldev_blk_size-0b(%r13)  # get blocksize
         stcm   %r5,3,.Lwrccw+2-0b(%r13)     # store blocksize into wr template
         stcm   %r5,3,.Llodata+14-0b(%r13)   # store blocksize into lodata
@@ -517,34 +458,25 @@ _start: basr   %r13,0
 # Get device characteristics from zipl parameters (at 0x218)
 # The following parameter format is expected 2x8 byte:
 #
-# Dump Start: CCHH RBBN
-# Dump End:   CCHH RBBX
-#
-# - CC: Start/End Cylinder Number
-# - HH: Start/End Head Number
-# - R : Start/End Record Number
-# - BB: Blocksize
-# - N : Number of Heads of DASD
-# - X : Unused
-#
-# Cylinder, Heads are counted starting with 0.
-# Records are counted starting with 1.
-# We assume that the End Record Number is at track boundary.
-# This allows us to determine the number of Blocks Per Track.
+# struct eckd_dump_param {
+#       uint32_t        start_blk;
+#       uint32_t        end_blk;
+#       uint16_t        blocksize;
+#       uint8_t.        num_heads;
+#       uint8_t.        bpt;
+#       char            reserved[4];
+# } __attribute((packed));
 ################################################################################
 
 _get_device_characteristics_32:
         stm    %r6,%r15,24(%r15)
         basr   %r13,0                   # base register
 0:      s      %r15,.Lc96-0b(%r13)      # create stack frame
-        mvc    .Ldev_start_cyl-0b+2(2,%r13),0x218(%r0)
-        mvc    .Ldev_start_hd-0b+2(2,%r13),0x21a(%r0)
-        mvc    .Ldev_start_rec-0b+3(1,%r13),0x21c(%r0)
-        mvc    .Ldev_blk_size-0b+2(2,%r13),0x21d(%r0)
-        mvc    .Ldev_nr_hds-0b+3(1,%r13),0x21f(%r0)
-        mvc    .Ldev_end_cyl-0b+2(2,%r13),0x220(%r0)
-        mvc    .Ldev_end_hd-0b+2(2,%r13),0x222(%r0)
-        mvc    .Ldev_end_rec-0b+3(1,%r13),0x224(%r0)
+        mvc    .Ldev_start_blk-0b(4,%r13),0x218(%r0)
+        mvc    .Ldev_end_blk-0b(4,%r13),0x21c(%r0)
+        mvc    .Ldev_blk_size-0b+2(2,%r13),0x220(%r0)
+        mvc    .Ldev_nr_hds-0b+3(1,%r13),0x222(%r0)
+        mvc    .Ldev_bpt-0b+3(1,%r13),0x223(%r0)
         lm     %r6,%r15,120(%r15)
         br     %r14
 
@@ -562,65 +494,6 @@ _dump_mem_32:    
 
 	bras %r14,_init_print_progress_32
 
-        # calculate start and end block
-
-        la      %r6,0
-        l       %r7,.Ldev_start_cyl-0b(%r13)
-        la      %r8,0
-        l       %r9,.Ldev_start_hd-0b(%r13)
-        l       %r10,.Ldev_start_rec-0b(%r13)
-        l       %r11,.Ldev_nr_hds-0b(%r13)
-        l       %r12,.Ldev_bpt-0b(%r13)       # = blocks per track
-
-        # calculate start block
-        #######################
-
-        # cyl * nr_hds * blkptr
-
-        mr      %r6,%r11
-        mr      %r6,%r12
-
-        # hd * recs
-
-        mr      %r8,%r12
-
-        # start block = cyl * nr_hds * bptr + hd * bptr + rec
-
-        ar      %r7,%r9
-        ar      %r7,%r10
-
-        bctr    %r7,0                         # decrement, we start counting
-                                              # beginning with block 0
-
-        st      %r7,.Ldev_start_blk-0b(%r13)
-        
-        
-
-        # calculate end block
-        #######################
-
-        l       %r7,.Ldev_end_cyl-0b(%r13)
-        l       %r9,.Ldev_end_hd-0b(%r13)
-
-        # cyl * nr_hds * blkptr
-
-        mr      %r6,%r11
-        mr      %r6,%r12
-
-        # hd * recs
-
-        mr      %r8,%r12
-
-        # end block = cyl * nr_hds * bptr + hd * bptr + rec
-
-        ar      %r7,%r9
-        ar      %r7,%r12
-
-        bctr    %r7,0                        # decrement, we start counting
-                                             # beginning with block 0
-
-        st      %r7,.Ldev_end_blk-0b(%r13)
-
         # calculate bytes per write (blksize * blwr)
 
         l       %r11,.Ldev_blk_size-0b(%r13)
@@ -777,8 +650,14 @@ _writeblock_32:
         xr      %r2,%r2                       # clear bot                  
         dr      %r2,%r7                       # cy=trk / heads, hd=trk % heads
                                               # r3: cy, r2: hd
+
+        lr      %r8,%r3                       # r8=upper 12 bits of cylinder
+        srl     %r8,16                        # are coded into heads:
+        sll     %r8,4                         # c0c1c2h0 (2 byte)
+
         sll     %r3,16                        # combine to CCHH in r3
         or      %r3,%r2
+        or      %r3,%r8                       # add upper 12 bits
         st      %r3,.Ldedata+8-0b(%r13)       # store cchh to dedata
         st      %r3,.Llodata+4-0b(%r13)       # store cchh to lodata
         st      %r3,.Llodata+8-0b(%r13)       # store cchh to lodata
@@ -793,8 +672,14 @@ _writeblock_32:
 
         xr      %r4,%r4                       # cy2=tr2/heads, hd2=hd2 % heads
         dr      %r4,%r7                       # r5: cy2, r4: hd2
-        stcm    %r5,3,.Ldedata+12-0b(%r13)    # store cy2,hd2 to dedata
-        stcm    %r4,3,.Ldedata+14-0b(%r13)    # store cy2,hd2 to dedata
+
+        lr      %r8,%r5                       # r8=upper 12 bits of cylinder
+        srl     %r8,16                        # are coded into heads:
+        sll     %r8,4                         # c0c1c2h0 (2 byte)
+        or      %r4,%r8
+
+        stcm    %r5,3,.Ldedata+12-0b(%r13)    # store cy2 to dedata
+        stcm    %r4,3,.Ldedata+14-0b(%r13)    # store hd2 to dedata
         lr      %r5,%r11                      # restore r5
 
         # CCWs are setup now, arent they?
@@ -863,30 +748,16 @@ _writeblock_32:
 
 # device characteristics
 .align 8
-.Ldev_start_cyl:
-        .long  0x00000000
-.Ldev_start_hd:
-        .long  0x00000000
-.Ldev_start_rec:
-        .long  0x00000000
 .Ldev_blk_size:
         .long  0x00000000
 .Ldev_nr_hds:
         .long  0x00000000
-.Ldev_end_cyl:
-        .long  0x00000000
-.Ldev_end_hd:
-        .long  0x00000000
-.Ldev_end_rec:
+.Ldev_bpt:
         .long  0x00000000
-
-
 .Ldev_start_blk:
         .long  0x00000000
 .Ldev_end_blk:
         .long  0x00000000
-.Ldev_bpt:
-        .long  0x00000000
 
 
 
Index: s390-tools-sles11/zipl/boot/eckd2mvdump.S
===================================================================
--- s390-tools-sles11.orig/zipl/boot/eckd2mvdump.S
+++ s390-tools-sles11/zipl/boot/eckd2mvdump.S
@@ -38,7 +38,7 @@ _start:
 	basr  %r13,0
 .Linit_base:
 	la    %r9,0
-	st    %r9,.Ldh_arch-.Linit_base(%r13) # init arch 
+	st    %r9,.Ldh_arch-.Linit_base(%r13) # init arch
 	l     %r15,1f-.Linit_base(%r13)       # load end of stack address
 	la    %r7,0
 	tm    __LC_ARCH_MODE_ID(%r9),0x01     # check arch mode
@@ -93,7 +93,7 @@ _start:
 #  -no parameters
 ################################################################################
 
-_dump_mem_64:    
+_dump_mem_64:
 	stmg   %r6,%r15,48(%r15)
 	basr   %r13,0                         # base register
 0:      aghi   %r15,-200                      # create stack frame
@@ -119,7 +119,7 @@ _dump_mem_64:    
 
 	# calculate number of blocks required to store header
 	llgf    %r4,.Lheader_size-0b(%r13)
-	srda    %r4,32                        # shift ==> 64 bit number        
+	srda    %r4,32                        # shift ==> 64 bit number
 	llgf    %r6,.Ldev_blk_size-0b(%r13)   # get blocksize
 	dr      %r4,%r6                       # nr of blocks for header =
                                               # HEADER_SIZE / BLOCKSIZE = r5
@@ -183,7 +183,7 @@ _dump_mem_64:    
 					      # boundary
 	mvc     512(8,%r3),.Ldh_magic_number-0b(%r13) # preserve signature
 	lgr     %r4,%r5
-	lgr     %r12,%r5                      # save nr of blocks        
+	lgr     %r12,%r5                      # save nr of blocks
 	bas     %r14,_ioblock_64-0b(%r13)     # write block to disk
 	ar      %r11,%r12                     # update block counter
 
@@ -372,7 +372,7 @@ _ioblock_64:
 	llgf    %r6,.Ldev_end_rec-0b(%r13)    # load blocks per track to r6
 	llgf    %r7,.Ldev_nr_hds-0b(%r13)     # load heads to r7
 	la      %r5,.Lwrloop-0b(%r13)
-	br      %r5 
+	br      %r5
 
 2:      # loop for number of block (nbl) time
 
@@ -387,7 +387,7 @@ _ioblock_64:
 	# build up next write CCW
 	l       %r9,0(%r3)                    # test page
 	lgr     %r9,%r3
-	b       .Lpage_ok-0b(%r13) 
+	b       .Lpage_ok-0b(%r13)
 .Lpage_invalid:
 	lghi    %r9,ZERO_PAGE_START
 .Lpage_ok:
@@ -396,27 +396,33 @@ _ioblock_64:
 	bct     %r4,2b-0b(%r13)               # decrement no of blks still to do
 	ni      1(%r12),0x3f                  # no command chaining for last ccw
 
-	# write CCWs are setup now               
+	# write CCWs are setup now
 	lgr     %r4,%r11                      # restore number of blocks
 	stcm    %r4,3,.Llodata+2-0b(%r13)     # store number of blocks to lodata
 
-	# compute end block 
-	agr     %r4,%r2                       # r4: ebl = blk + nbl 
+	# compute end block
+	agr     %r4,%r2                       # r4: ebl = blk + nbl
 	bctr    %r4,0                         # decrement r4 (last blk touched)
 
 	# compute start track and start block on track
-	srda    %r2,32                        # shift ==> 64 bit number 
+	srda    %r2,32                        # shift ==> 64 bit number
 	dr      %r2,%r6                       # trk = blk / bpt, bot = blk % bpt
 	                                      # r3: trk, r2: bot
 	la      %r2,1(%r2)                    # bot++ ( we start counting at 1 )
 	stcm    %r2,1,.Llodata+12-0b(%r13)    # store bot to lodata
 
 	# compute start cylinder and head
-	xr      %r2,%r2                       # clear bot                  
+	xr      %r2,%r2                       # clear bot
 	dr      %r2,%r7                       # cy=trk / heads, hd=trk % heads
 	                                      # r3: cy, r2: hd
+
+	lgr     %r8,%r3                       # r8=upper 12 bits of cylinder
+	srl     %r8,16                        # are coded into heads:
+	sll     %r8,4                         # c0c1c2h0 (2 byte)
+
 	sll     %r3,16                        # combine to CCHH in r3
 	or      %r3,%r2
+	or      %r3,%r8                       # add upper 12 bits
 	st      %r3,.Ldedata+8-0b(%r13)       # store cchh to dedata
 	st      %r3,.Llodata+4-0b(%r13)       # store cchh to lodata
 	st      %r3,.Llodata+8-0b(%r13)       # store cchh to lodata
@@ -429,8 +435,14 @@ _ioblock_64:
 	# compute end cylinder and head
 	xr      %r4,%r4                       # cy2=tr2/heads, hd2=hd2 % heads
 	dr      %r4,%r7                       # r5: cy2, r4: hd2
-	stcm    %r5,3,.Ldedata+12-0b(%r13)    # store cy2,hd2 to dedata
-	stcm    %r4,3,.Ldedata+14-0b(%r13)    # store cy2,hd2 to dedata
+
+	lgr     %r8,%r5                       # r8=upper 12 bits of cylinder
+	srl     %r8,16                        # are coded into heads:
+	sll     %r8,4                         # c0c1c2h0 (2 byte)
+	or      %r4,%r8
+
+	stcm    %r5,3,.Ldedata+12-0b(%r13)    # store cy2 to dedata
+	stcm    %r4,3,.Ldedata+14-0b(%r13)    # store hd2 to dedata
 	lgr     %r5,%r11                      # restore r5
 
 	# CCWs are setup now, arent they?
@@ -459,7 +471,7 @@ _ioblock_64:
 .Lpage_invalid_psw:
 	.long 0x00000001,0x80000000,0x00000000,0x00000000 + .Lpage_invalid
 .Lold_pgc_psw:
-	.long 0x0,0x0,0x0,0x0 
+	.long 0x0,0x0,0x0,0x0
 
 ################################################################################
 # expand Macros
@@ -478,7 +490,7 @@ _ioblock_64:
 	sclp_read_info
 
 # extern functions
- 
+
 .Lpanik_64:
 	.long      _panik_64
 .Lenable_device_64:
Index: s390-tools-sles11/zipl/boot/tapedump.S
===================================================================
--- s390-tools-sles11.orig/zipl/boot/tapedump.S
+++ s390-tools-sles11/zipl/boot/tapedump.S
@@ -44,8 +44,7 @@
 
 #define ETAPE_REWUNL_1  0x00000001
 #define ETAPE_REWUNL_2  0x00000002
-#define ETAPE_REWUNL_3  0x00000004
-#define ETAPE_WRITE     0x00000003
+#define ETAPE_WRITE     0x0003
 
 /* Tape types */
 
@@ -264,9 +263,8 @@ _writer_64:        
 
         la    %r7,.Lccwwrite-0b(%r13)
         st    %r7,8+.Lorbwrite-0b(%r13)
-        llgf  %r7,.Luse_idrc-0b(%r13)
-        lghi  %r9,1
-        clr   %r7,%r9
+        lh    %r7,.Luse_idrc-0b(%r13)
+        chi   %r7,1
         bne   .Lnocomp-0b(%r13)
         la    %r7,.Lccwwrite_compressed-0b(%r13)
         st    %r7,8+.Lorbwrite-0b(%r13)
@@ -308,8 +306,7 @@ _writer_64:        
         lghi  %r2,0
         ic    %r2,.Ltmp_data+3-0b(%r13)      # get ERA
         sll   %r2,24                           # move it to first byte
-        lghi  %r3,ETAPE_WRITE
-        ar    %r2,%r3
+        ahi   %r2,ETAPE_WRITE
 
         llgf  %r14,.Lpanik_64-0b(%r13)
         basr  %r14,%r14
@@ -407,13 +404,11 @@ _rewind_unload_64:
 
 	/* check for 3590 */
 
-	l     %r9,.Ltape_type-0b(%r13)
-	lghi  %r10,TAPE_3590
-	clr   %r9,%r10
+	lh    %r9,.Ltape_type-0b(%r13)
+	chi   %r9,TAPE_3590
 	bne   .Lnot3590-0b(%r13)
 
-	la    %r4,.Lirb-0b(%r13)
-	tm    8(%r4),0x2                       # unit check ?
+	tm    .Lirb+8-0b(%r13),0x2             # unit check?
 	bz    3f-0b(%r13)                      # no unit check: rewunl worked
 
         /* 3590: retry rewind unload */
@@ -438,15 +433,13 @@ _rewind_unload_64:
         lghi  %r5,1
         bas   %r14,_ssch_64-0b(%r13)
 
-        lghi  %r9,0
-        ic    %r9,.Ltmp_data+3-0b(%r13)      # load sense byte 3
-        lghi  %r10,0x2b                        # 3480: ERA: rewunl compl (2b)
-        clr   %r9,%r10
+	cli   .Ltmp_data+3-0b(%r13),0x2b     # check load sense byte 3
+	                                     # 3480: ERA: rewunl completed (2b)
         be    3f-0b(%r13)
 
-        lghi  %r10,0x51                        # 3490: ERA: EOV (51)
         lghi  %r2,ETAPE_REWUNL_1
-        clr   %r9,%r10
+	cli   .Ltmp_data+3-0b(%r13),0x51     # check load sense byte 3
+	                                     # 3490: ERA: EOV (51)
         bne   2f-0b(%r13)
 
         /* 3490: retry rewind unload */
@@ -465,10 +458,8 @@ _rewind_unload_64:
         lghi  %r5,1
         bas   %r14,_ssch_64-0b(%r13)
 
-        lghi  %r9,0
-        ic    %r9,.Ltmp_data+3-0b(%r13)  # load sense byte 3
-        lghi  %r10,0x52                    # 3490: ERA: EOV Complete (52)
-        clr   %r9,%r10
+	cli   .Ltmp_data+3-0b(%r13),0x52     # check load sense byte 3
+	                                     # 3490: ERA: EOV Complete (52)
         be    3f-0b(%r13)
 
         lghi  %r2,ETAPE_REWUNL_2
@@ -491,10 +482,7 @@ _rewind_unload_64:
         llgf  %r2,IPL_SC
         la    %r3,.Lirb-0b(%r13)
         bas   %r14,_wait4de_64-0b(%r13)
-	lghi  %r9,0
-	ic    %r9,.Lirb+8-0b(%r13)        # get device state
-	lghi  %r10,0x85
-	clr   %r9,%r10
+	cli   .Lirb+8-0b(%r13),0x85
 	bne   .Lwait_loop-0b(%r13)
 
 4:      lmg   %r6,%r15,248(%r15)
@@ -549,8 +537,8 @@ _get_device_characteristics_64:
 	lhi     %r6,TAPE_UNKNOWN
 	lhi     %r7,0                   # idrc off
 1:
-	st	%r6,.Ltape_type-0b(%r13)
-	st      %r7,.Luse_idrc-0b(%r13)
+	sth     %r6,.Ltape_type-0b(%r13)
+	sth     %r7,.Luse_idrc-0b(%r13)
 	lmg	%r6,%r15,248(%r15)
         br	%r14
 
@@ -680,7 +668,7 @@ _start: basr   %r13,0
 _dump_mem_32:
         stm    %r6,%r15,24(%r15)
         basr   %r13,0                         # base register
-0:      s      %r15,.Lc96-0b(%r13)            # create stack frame
+0:      ahi    %r15,-96                       # create stack frame
 
 #
 # first write a tape mark
@@ -760,7 +748,7 @@ _dump_mem_32:
 _init_tape_32:
         stm   %r6,%r15,24(%r15)
         basr  %r13,0                           # base register
-0:      s     %r15,.Lc96-0b(%r13)              # create stack frame
+0:      ahi   %r15,-96                         # create stack frame
 
         l     %r2,IPL_SC                       # subchannel id
         la    %r3,.Lorbmode_set-0b(%r13)
@@ -785,7 +773,7 @@ _init_tape_32:
 _next_vol_32:
         stm   %r6,%r15,24(%r15)
         basr  %r13,0                           # base register
-0:      s     %r15,.Lc96-0b(%r13)              # create stack frame
+0:      ahi   %r15,-96                         # create stack frame
 
         /* write end of volume marker */
         la    %r2,.Leov_marker-0b(%r13)
@@ -833,7 +821,7 @@ _next_vol_32:
 _writer_32:        
         stm   %r6,%r15,24(%r15)
         basr  %r13,0                           # base register
-0:      s     %r15,.Lc96-0b(%r13)              # create stack frame
+0:      ahi   %r15,-96                         # create stack frame
 
         lr    %r10,%r2                         # save start address
         lr    %r11,%r3                         # save length
@@ -845,9 +833,8 @@ _writer_32:        
 
 	la    %r7,.Lccwwrite-0b(%r13)
 	st    %r7,8+.Lorbwrite-0b(%r13)
-	l     %r7,.Luse_idrc-0b(%r13)
-	la    %r9,1
-	clr   %r7,%r9
+	lh    %r7,.Luse_idrc-0b(%r13)
+	chi   %r7,1
 	bne   .Lnocomp-0b(%r13)
 	la    %r7,.Lccwwrite_compressed-0b(%r13)
         st    %r7,8+.Lorbwrite-0b(%r13)
@@ -888,8 +875,7 @@ _writer_32:        
         la    %r2,0 
         ic    %r2,.Ltmp_data+3-0b(%r13)      # get ERA
         sll   %r2,24                           # move it to first byte 
-        la    %r3,ETAPE_WRITE
-        ar    %r2,%r3
+        ahi   %r2,ETAPE_WRITE
  
         l     %r14,.Lpanik_32-0b(%r13)
         basr  %r14,%r14
@@ -957,7 +943,7 @@ _writer_32:        
 _hex_to_ebcdic_32:
         stm  %r6,%r15,24(%r15)
         basr  %r13,0                           # base register
-0:      s     %r15,.Lc96-0b(%r13)              # create stack frame
+0:      ahi   %r15,-96                         # create stack frame
         cvd   %r2,.Lout_packed-0b(%r13)        # convert to packed decimal
         unpk  0(8,%r3),.Lout_packed+4-0b(4,%r13) 
         oi    7(%r3),0xf0                      # remove signed flag
@@ -974,7 +960,7 @@ _hex_to_ebcdic_32:
 _rewind_unload_32:
         stm  %r6,%r15,24(%r15)
         basr  %r13,0                           # base register
-0:      s     %r15,.Lc96-0b(%r13)              # create stack frame
+0:      ahi   %r15,-96                         # create stack frame
 
         /* 3480/3490: rewind unload */
 
@@ -992,15 +978,13 @@ _rewind_unload_32:
         la    %r5,1
         bas   %r14,_ssch_32-0b(%r13)
 
-        la    %r9,0
-        ic    %r9,.Ltmp_data+3-0b(%r13)      # load sense byte 3
-        la    %r10,0x2b                        # 3480: ERA: rewunl completed (2b)
-        clr   %r9,%r10
+	cli   .Ltmp_data+3-0b(%r13),0x2b     # check load sense byte 3
+	                                     # 3480: ERA: rewunl completed (2b)
         be    3f-0b(%r13)
  
-        la    %r10,0x51                        # 3490: ERA: EOV (51)  
         la    %r2,ETAPE_REWUNL_1
-        clr   %r9,%r10
+	cli   .Ltmp_data+3-0b(%r13),0x51     # check load sense byte 3
+	                                     # 3490: ERA: EOV (51)
         bne   2f-0b(%r13)
 
         /* 3490: retry rewind unload */
@@ -1019,10 +1003,8 @@ _rewind_unload_32:
         la    %r5,1
         bas   %r14,_ssch_32-0b(%r13)
 
-        la    %r9,0
-        ic    %r9,.Ltmp_data+3-0b(%r13)  # load sense byte 3
-        la    %r10,0x52                    # 3490: ERA: EOV Complete (52)
-        clr   %r9,%r10
+	cli   .Ltmp_data+3-0b(%r13),0x52     # check load sense byte 3
+	                                     # 3490: ERA: EOV Complete (52)
         be    3f-0b(%r13)
  
         la    %r2,ETAPE_REWUNL_2
@@ -1069,7 +1051,7 @@ _rewind_unload_32:
 _get_device_characteristics_32:
 	stm    %r6,%r15,24(%r15)
         basr   %r13,0                   # base register
-0:      s      %r15,.Lc96-0b(%r13)      # create stack frame
+0:      ahi    %r15,-96                 # create stack frame
         l      %r2,IPL_SC
         la     %r3,.Lorb_read_cc-0b(%r13)
         la     %r4,.Lirb-0b(%r13)
@@ -1096,8 +1078,8 @@ _get_device_characteristics_32:
         la      %r6,TAPE_UNKNOWN
 	la      %r7,0                   # idrc off
 1:
-        st      %r6,.Ltape_type-0b(%r13)
-	st      %r7,.Luse_idrc-0b(%r13)
+        sth     %r6,.Ltape_type-0b(%r13)
+	sth     %r7,.Luse_idrc-0b(%r13)
 	lm      %r6,%r15,120(%r15)
         br      %r14
  
@@ -1122,7 +1104,7 @@ _get_device_characteristics_32:
 _load_display_32:
         stm    %r6,%r15,24(%r15)
         basr   %r13,0                         # base register
-0:      s      %r15,.Lc96-0b(%r13)            # create stack frame
+0:      ahi    %r15,-96                       # create stack frame
         st    %r2,.Lccwload_disp+4-0b(%r13)   # store text field
         l     %r2,IPL_SC                      # subchannel id
         la    %r3,.Lorbload_disp-0b(%r13)     # address of orb
@@ -1147,7 +1129,7 @@ _load_display_32:
 _tapemark_32:
         stm   %r6,%r15,24(%r15)
         basr  %r13,0                           # base register
-0:      s     %r15,.Lc96-0b(%r13)              # create stack frame
+0:      ahi   %r15,-96                         # create stack frame
 
         l     %r2,IPL_SC                       # subchannel id
         la    %r3,.Lorbmark-0b(%r13)           # r3 = address of orb
@@ -1195,12 +1177,11 @@ _tapemark_32:
 # DATA
 ################################################################################
 
-.Ltape_type: .long 0 /* 3480/90 3590 etc. */
-.Luse_idrc:  .long 1 /* 1 -> use idrc compression */
+.Ltape_type: .word 0 /* 3480/90 3590 etc. */
+.Luse_idrc:  .word 1 /* 1 -> use idrc compression */
                      /* 0 -> do not use idrc (e.g. for 3480 not allowed) */
 
 .Lirb:  .long  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
-.Lc96:  .long  96 # for creating stackframes
 .org IPL_BS-8
 .Lmem_upper_limit:
         .long 0xffffffff,0xffffffff              # can be used for memsize=xxx
Index: s390-tools-sles11/zipl/include/disk.h
===================================================================
--- s390-tools-sles11.orig/zipl/include/disk.h
+++ s390-tools-sles11/zipl/include/disk.h
@@ -79,6 +79,7 @@ int disk_get_info(const char* device, st
 int disk_get_info_from_file(const char* filename, struct disk_info** info);
 void disk_free_info(struct disk_info* info);
 char* disk_get_type_name(disk_type_t type);
+int disk_is_large_volume(struct disk_info* info);
 int disk_cyl_from_blocknum(blocknum_t blocknum, struct disk_info* info);
 int disk_head_from_blocknum(blocknum_t blocknum, struct disk_info* info);
 int disk_sec_from_blocknum(blocknum_t blocknum, struct disk_info* info);
Index: s390-tools-sles11/zipl/man/zipl.conf.5
===================================================================
--- s390-tools-sles11.orig/zipl/man/zipl.conf.5
+++ s390-tools-sles11/zipl/man/zipl.conf.5
@@ -148,7 +148,8 @@ Please choose:
 The menu prompt expects the configuration number as input. Typing '0' will
 start the default configuration. Additional kernel parameters can be
 specified by supplying them after the configuration number, separated by a
-blank character. Example input:
+blank character. These kernel parameters will be converted to lower-case.
+Example input:
 
 .IP
 2 mem=128m maxcpus=1
Index: s390-tools-sles11/zipl/src/Makefile
===================================================================
--- s390-tools-sles11.orig/zipl/src/Makefile
+++ s390-tools-sles11/zipl/src/Makefile
@@ -11,8 +11,8 @@ includes = $(wildcard ../include/*.h)
 
 all: zipl
 
-zipl: ../boot/data.c $(objects)
-	$(CC) $(objects) -o zipl
+zipl: $(objects)
+	$(CC) -Wl,-z,noexecstack $(objects) ../boot/data.o -o zipl
 
 %.o: %.c $(includes) Makefile
 	$(CC) $(CFLAGS) -c -o $@ $<
@@ -29,5 +29,4 @@ clean:
 
 # Additional manual dependencies
 
-boot.o: ../boot/data.c
-
+boot.o: ../boot/data.o ../boot/data.h
Index: s390-tools-sles11/zipl/src/boot.c
===================================================================
--- s390-tools-sles11.orig/zipl/src/boot.c
+++ s390-tools-sles11/zipl/src/boot.c
@@ -19,8 +19,11 @@
 #include "misc.h"
 #include "zt_common.h"
 
-/* Include boot data */
-#include "../boot/data.c"
+/* Include declarations for boot_data symbols. */
+#include "../boot/data.h"
+
+#define DATA_SIZE(x)	((size_t) (&_binary_##x##_bin_size))
+#define DATA_ADDR(x)	(&_binary_##x##_bin_start)
 
 #define STAGE2_MAX_SIZE		4096
 
@@ -30,25 +33,25 @@
 int
 boot_check_data(void)
 {
-	if (sizeof(boot_data_fba0) !=
+	if (DATA_SIZE(fba0) !=
 	    sizeof(struct boot_fba_stage0)) {
 		error_reason("Size mismatch of stage 0 loader for disks with "
 			     "FBA layout");
 		return -1;
 	}
-	if (sizeof(boot_data_eckd0) !=
+	if (DATA_SIZE(eckd0) !=
 	    sizeof(struct boot_eckd_stage0)) {
 		error_reason("Size mismatch of stage 0 loader for disks with "
 			     "ECKD layout");
 		return -1;
 	}
-	if (sizeof(boot_data_eckd1a) !=
+	if (DATA_SIZE(eckd1a) !=
 	    sizeof(struct boot_eckd_classic_stage1)) {
 		error_reason("Size mismatch of stage 1 loader for disks with "
 			     "ECKD classic layout");
 		return -1;
 	}
-	if (sizeof(boot_data_eckd1b) !=
+	if (DATA_SIZE(eckd1b) !=
 	    sizeof(struct boot_eckd_compatible_stage1)) {
 		error_reason("Size mismatch of stage 1 loader for disks with "
 			     "ECKD compatible layout");
@@ -75,7 +78,7 @@ boot_get_stage3(void** buffer, size_t* b
 		return -1;
 	}
 	/* Get memory */
-	data = misc_malloc(sizeof(boot_data_stage3));
+	data = misc_malloc(DATA_SIZE(stage3));
 	if (data == NULL)
 		return -1;
 	/* Prepare params section */
@@ -85,10 +88,10 @@ boot_get_stage3(void** buffer, size_t* b
 	params.load_psw = (uint64_t) (image_addr | PSW_LOAD);
 	params.extra_parm = (uint64_t) extra_parm;
 	/* Initialize buffer */
-	memcpy(data, boot_data_stage3, sizeof(boot_data_stage3));
+	memcpy(data, DATA_ADDR(stage3), DATA_SIZE(stage3));
 	memcpy(data, &params, sizeof(struct boot_stage3_params));
 	*buffer = data;
-	*bytecount = sizeof(boot_data_stage3);
+	*bytecount = DATA_SIZE(stage3);
 	return 0;
 }
 
@@ -103,7 +106,7 @@ boot_init_fba_stage0(struct boot_fba_sta
 	blocknum_t i;
 
 	/* Initialize stage 0 data */
-	memcpy(stage0, boot_data_fba0, sizeof(struct boot_fba_stage0));
+	memcpy(stage0, DATA_ADDR(fba0), sizeof(struct boot_fba_stage0));
 	/* Fill in blocklist for stage 2 loader */
 	if (stage2_count > 8) {
 		error_reason("Not enough room for FBA stage 2 loader "
@@ -123,7 +126,7 @@ boot_init_fba_stage0(struct boot_fba_sta
 void
 boot_init_eckd_classic_stage0(struct boot_eckd_stage0* stage0)
 {
-	memcpy(stage0, boot_data_eckd0, sizeof(struct boot_eckd_stage0));
+	memcpy(stage0, DATA_ADDR(eckd0), sizeof(struct boot_eckd_stage0));
 	/* Fill in size of stage 1 loader */
 	stage0->read.count = sizeof(struct boot_eckd_classic_stage1);
 }
@@ -132,7 +135,7 @@ boot_init_eckd_classic_stage0(struct boo
 void
 boot_init_eckd_compatible_stage0(struct boot_eckd_stage0* stage0)
 {
-	memcpy(stage0, boot_data_eckd0, sizeof(struct boot_eckd_stage0));
+	memcpy(stage0, DATA_ADDR(eckd0), sizeof(struct boot_eckd_stage0));
 	/* Fill in size of stage 1 loader */
 	stage0->read.count = sizeof(struct boot_eckd_compatible_stage1);
 }
@@ -148,7 +151,7 @@ boot_init_eckd_classic_stage1(struct boo
 {
 	blocknum_t i;
 
-	memcpy(stage1, boot_data_eckd1a,
+	memcpy(stage1, DATA_ADDR(eckd1a),
 	       sizeof(struct boot_eckd_classic_stage1));
 	/* Fill in blocklist for stage 2 loader */
 	if (stage2_count > 8) {
@@ -159,7 +162,8 @@ boot_init_eckd_classic_stage1(struct boo
 	for (i=0; i < stage2_count; i++) {
 		stage1->ssrt[i].read.count 	= stage2_list[i].chs.size;
 		stage1->seek[i].cyl		= stage2_list[i].chs.cyl;
-		stage1->seek[i].head		= stage2_list[i].chs.head;
+		stage1->seek[i].head		= stage2_list[i].chs.head |
+			((stage2_list[i].chs.cyl >> 12) & 0xfff0);
 		stage1->seek[i].sec		= stage2_list[i].chs.sec;
 		stage1->ssrt[i].read.address_lo	= ZIPL_STAGE2_LOAD_ADDRESS +
 						  i * stage2_list[i].chs.size;
@@ -179,9 +183,9 @@ boot_init_eckd_classic_dump_stage1(struc
 	blocknum_t blocks;
 	blocknum_t i;
 
-	memcpy(stage1, boot_data_eckd1a,
+	memcpy(stage1, DATA_ADDR(eckd1a),
 	       sizeof(struct boot_eckd_classic_stage1));
-	blocks = (sizeof(boot_data_eckd2dump) +
+	blocks = (DATA_SIZE(eckd2dump) +
 			info->phy_block_size - 1) / info->phy_block_size;
 	if (blocks > 8) {
 		error_reason("Not enough room for ECKD stage 2 dump record "
@@ -190,13 +194,16 @@ boot_init_eckd_classic_dump_stage1(struc
 	}
 	/* Fill in block list of stage 2 dump record */
 	for (i=0; i < blocks; i++) {
+		int cyl;
+
 		stage1->ssrt[i].read.count = info->phy_block_size;
 		stage1->ssrt[i].read.address_lo = ZIPL_STAGE2_LOAD_ADDRESS +
 						  i * info->phy_block_size;
-		stage1->seek[i].cyl =
-			disk_cyl_from_blocknum(i + info->geo.start, info);
+		cyl = disk_cyl_from_blocknum(i + info->geo.start, info);
+		stage1->seek[i].cyl = cyl;
 		stage1->seek[i].head =
-			disk_head_from_blocknum(i + info->geo.start, info);
+			disk_head_from_blocknum(i + info->geo.start, info) |
+			((cyl >> 12) & 0xfff0);
 		stage1->seek[i].sec =
 			disk_sec_from_blocknum(i + info->geo.start, info);
 	}
@@ -213,7 +220,7 @@ boot_init_eckd_compatible_stage1(struct 
 	blocknum_t blocks;
 	blocknum_t i;
 
-	memcpy(stage1, boot_data_eckd1b,
+	memcpy(stage1, DATA_ADDR(eckd1b),
 	       sizeof(struct boot_eckd_compatible_stage1));
 	/* Fill in blocklist for stage 2 loader */
 	blocks = (STAGE2_MAX_SIZE + info->phy_block_size - 1) /
@@ -246,7 +253,7 @@ boot_get_tape_ipl(void** data, size_t* s
 			     "addressing mode)");
 		return -1;
 	}
-	buffer = misc_malloc(sizeof(boot_data_tape0));
+	buffer = misc_malloc(DATA_SIZE(tape0));
 	if (buffer == NULL)
 		return -1;
 	/* Prepare params section */
@@ -254,11 +261,11 @@ boot_get_tape_ipl(void** data, size_t* s
 	params.initrd_addr = (uint64_t) initrd_addr;
 	params.load_psw = (uint64_t) (image_addr | PSW_LOAD);
 	/* Initialize buffer */
-	memcpy(buffer, boot_data_tape0, sizeof(boot_data_tape0));
+	memcpy(buffer, DATA_ADDR(tape0), DATA_SIZE(tape0));
 	memcpy(VOID_ADD(buffer, BOOT_TAPE_IPL_PARAMS_OFFSET), &params,
 	       sizeof(struct boot_tape_ipl_params));
 	*data = buffer;
-	*size = sizeof(boot_data_tape0);
+	*size = DATA_SIZE(tape0);
 	return 0;
 }
 
@@ -272,13 +279,13 @@ boot_init_eckd_compatible_dump_stage1(
 	blocknum_t blocks;
 	blocknum_t i;
 
-	memcpy(stage1, boot_data_eckd1b,
+	memcpy(stage1, DATA_ADDR(eckd1b),
 	       sizeof(struct boot_eckd_compatible_stage1));
 	if (mvdump_switch)
-		blocks = (sizeof(boot_data_eckd2mvdump) +
+		blocks = (DATA_SIZE(eckd2mvdump) +
 			  info->phy_block_size - 1) / info->phy_block_size;
 	else
-		blocks = (sizeof(boot_data_eckd2dump) +
+		blocks = (DATA_SIZE(eckd2dump) +
 			  info->phy_block_size - 1) / info->phy_block_size;
 	if (blocks > 12) {
 		error_reason("Not enough room for ECKD stage 2 dump record "
@@ -471,9 +478,9 @@ boot_get_fba_stage2(void** data, size_t*
 	buffer = misc_malloc(STAGE2_MAX_SIZE);
 	if (buffer == NULL)
 		return -1;
-	memcpy(buffer, boot_data_fba2, sizeof(boot_data_fba2));
-	rc = store_stage2_menu(VOID_ADD(buffer, sizeof(boot_data_fba2)),
-			       STAGE2_MAX_SIZE - sizeof(boot_data_fba2),
+	memcpy(buffer, DATA_ADDR(fba2), DATA_SIZE(fba2));
+	rc = store_stage2_menu(VOID_ADD(buffer, DATA_SIZE(fba2)),
+			       STAGE2_MAX_SIZE - DATA_SIZE(fba2),
 			       job);
 	if (rc) {
 		free(buffer);
@@ -494,9 +501,9 @@ boot_get_eckd_stage2(void** data, size_t
 	buffer = misc_malloc(STAGE2_MAX_SIZE);
 	if (buffer == NULL)
 		return -1;
-	memcpy(buffer, boot_data_eckd2, sizeof(boot_data_eckd2));
-	rc = store_stage2_menu(VOID_ADD(buffer, sizeof(boot_data_eckd2)),
-			       STAGE2_MAX_SIZE - sizeof(boot_data_eckd2),
+	memcpy(buffer, DATA_ADDR(eckd2), DATA_SIZE(eckd2));
+	rc = store_stage2_menu(VOID_ADD(buffer, DATA_SIZE(eckd2)),
+			       STAGE2_MAX_SIZE - DATA_SIZE(eckd2),
 			       job);
 	if (rc) {
 		free(buffer);
@@ -513,15 +520,15 @@ boot_get_tape_dump(void** data, size_t* 
 {
 	void* buffer;
 
-	buffer = misc_malloc(sizeof(boot_data_tapedump));
+	buffer = misc_malloc(DATA_SIZE(tapedump));
 	if (buffer == NULL)
 		return -1;
-	memcpy(buffer, boot_data_tapedump, sizeof(boot_data_tapedump));
+	memcpy(buffer, DATA_ADDR(tapedump), DATA_SIZE(tapedump));
 	/* Write mem size to end of dump record */
-	memcpy(VOID_ADD(buffer, sizeof(boot_data_tapedump) - sizeof(mem)), &mem,
+	memcpy(VOID_ADD(buffer, DATA_SIZE(tapedump) - sizeof(mem)), &mem,
 	       sizeof(mem));
 	*data = buffer;
-	*size = sizeof(boot_data_tapedump);
+	*size = DATA_SIZE(tapedump);
 	return 0;
 }
 
@@ -531,15 +538,15 @@ boot_get_eckd_dump_stage2(void** data, s
 {
 	void* buffer;
 
-	buffer = misc_malloc(sizeof(boot_data_eckd2dump));
+	buffer = misc_malloc(DATA_SIZE(eckd2dump));
 	if (buffer == NULL)
 		return -1;
-	memcpy(buffer, boot_data_eckd2dump, sizeof(boot_data_eckd2dump));
+	memcpy(buffer, DATA_ADDR(eckd2dump), DATA_SIZE(eckd2dump));
 	/* Write mem size to end of dump record */
-	memcpy(VOID_ADD(buffer, sizeof(boot_data_eckd2dump) - sizeof(mem)),
+	memcpy(VOID_ADD(buffer, DATA_SIZE(eckd2dump) - sizeof(mem)),
 	       &mem, sizeof(mem));
 	*data = buffer;
-	*size = sizeof(boot_data_eckd2dump);
+	*size = DATA_SIZE(eckd2dump);
 	return 0;
 }
 
@@ -549,22 +556,22 @@ boot_get_eckd_mvdump_stage2(void** data,
 {
 	void* buffer;
 
-	buffer = misc_malloc(sizeof(boot_data_eckd2mvdump));
+	buffer = misc_malloc(DATA_SIZE(eckd2mvdump));
 	if (buffer == NULL)
 		return -1;
-	memcpy(buffer, boot_data_eckd2mvdump, sizeof(boot_data_eckd2mvdump));
+	memcpy(buffer, DATA_ADDR(eckd2mvdump), DATA_SIZE(eckd2mvdump));
 	/* Write mem size and force indicator (as specified by zipl -M)
 	 * to end of dump record, right before 512-byte parameter table */
-	memcpy(VOID_ADD(buffer, sizeof(boot_data_eckd2mvdump) - sizeof(mem) -
+	memcpy(VOID_ADD(buffer, DATA_SIZE(eckd2mvdump) - sizeof(mem) -
 			sizeof(struct mvdump_parm_table)), &mem, sizeof(mem));
-	memcpy(VOID_ADD(buffer, sizeof(boot_data_eckd2mvdump) - sizeof(mem) -
+	memcpy(VOID_ADD(buffer, DATA_SIZE(eckd2mvdump) - sizeof(mem) -
 			sizeof(force) - sizeof(struct mvdump_parm_table)),
 	       &force, sizeof(force));
-	memcpy(VOID_ADD(buffer, sizeof(boot_data_eckd2mvdump) -
+	memcpy(VOID_ADD(buffer, DATA_SIZE(eckd2mvdump) -
 			sizeof(struct mvdump_parm_table)), &parm,
 	       sizeof(struct mvdump_parm_table));
 	*data = buffer;
-	*size = sizeof(boot_data_eckd2mvdump);
+	*size = DATA_SIZE(eckd2mvdump);
 	return 0;
 }
 
@@ -574,14 +581,14 @@ boot_get_fba_dump_stage2(void** data, si
 {
 	void* buffer;
 
-	buffer = misc_malloc(sizeof(boot_data_fba2dump));
+	buffer = misc_malloc(DATA_SIZE(fba2dump));
 	if (buffer == NULL)
 		return -1;
-	memcpy(buffer, boot_data_fba2dump, sizeof(boot_data_fba2dump));
+	memcpy(buffer, DATA_ADDR(fba2dump), DATA_SIZE(fba2dump));
 	/* Write mem size to end of dump record */
-	memcpy(VOID_ADD(buffer, sizeof(boot_data_fba2dump) - sizeof(mem)),
+	memcpy(VOID_ADD(buffer, DATA_SIZE(fba2dump) - sizeof(mem)),
 	       &mem, sizeof(mem));
 	*data = buffer;
-	*size = sizeof(boot_data_fba2dump);
+	*size = DATA_SIZE(fba2dump);
 	return 0;
 }
Index: s390-tools-sles11/zipl/src/bootmap.c
===================================================================
--- s390-tools-sles11.orig/zipl/src/bootmap.c
+++ s390-tools-sles11/zipl/src/bootmap.c
@@ -104,7 +104,8 @@ bootmap_store_blockptr(void* buffer, dis
 		case disk_type_eckd_compatible:
 			eckd = (struct eckd_blockptr *) buffer;
 			eckd->cyl = ptr->chs.cyl;
-			eckd->head = ptr->chs.head;
+			eckd->head = ptr->chs.head |
+				     ((ptr->chs.cyl >> 12) & 0xfff0);
 			eckd->sec = ptr->chs.sec;
 			eckd->size = ptr->chs.size;
 			eckd->blockct = ptr->chs.blockct;
Index: s390-tools-sles11/zipl/src/disk.c
===================================================================
--- s390-tools-sles11.orig/zipl/src/disk.c
+++ s390-tools-sles11/zipl/src/disk.c
@@ -216,7 +216,7 @@ out_close:
 	close(fd);
 	free(data);
 	return -1;
-	
+
 }
 
 
@@ -509,6 +509,16 @@ disk_get_type_name(disk_type_t type)
 }
 
 
+/* Return non-zero for ECKD large volumes. */
+int
+disk_is_large_volume(struct disk_info* info)
+{
+	return (info->type == disk_type_eckd_classic ||
+	        info->type == disk_type_eckd_compatible) &&
+		info->geo.cylinders == 0xfffe;
+}
+
+
 /* Print textual representation of INFO contents. */
 void
 disk_print_info(struct disk_info* info)
@@ -544,8 +554,14 @@ disk_print_info(struct disk_info* info)
 	       info->geo.heads);
 	printf("  Geometry - sectors..............: %d\n",
 	       info->geo.sectors);
-	printf("  Geometry - cylinders............: %d\n",
-	       info->geo.cylinders);
+	if (disk_is_large_volume(info)) {
+		/* ECKD large volume. There is not enough information
+		 * available in INFO to calculate disk cylinder size. */
+		printf("  Geometry - cylinders............: > 65534\n");
+	} else {
+		printf("  Geometry - cylinders............: %d\n",
+		       info->geo.cylinders);
+	}
 	printf("  Geometry - start................: %ld\n",
 	       info->geo.start);
 	if (info->fs_block_size >= 0)
@@ -732,7 +748,7 @@ disk_get_blocklist_from_file(const char*
 }
 
 /* Check whether input device is in subchannel set 0.
- * Path to "dev" attribute containing the major/minor number depends on 
+ * Path to "dev" attribute containing the major/minor number depends on
  * whether option CONFIG_SYSFS_DEPRECATED is set or not */
 
 int disk_check_subchannel_set(int devno, dev_t device, char* dev_name)
Index: s390-tools-sles11/zipl/src/install.c
===================================================================
--- s390-tools-sles11.orig/zipl/src/install.c
+++ s390-tools-sles11/zipl/src/install.c
@@ -645,20 +645,46 @@ overwrite_partition_start(int fd, struct
 	return 0;
 }
 
+
+static int check_partition_bounds(struct disk_info* info)
+{
+	unsigned long long end_blk = info->geo.start + info->phy_blocks - 1;
+
+	if (end_blk > UINT32_MAX) {
+		error_reason("partition end exceeds bounds (offset "
+			     "%lld MB, max %lld MB)",
+			(end_blk * info->phy_block_size) >> 20,
+			(((unsigned long long) UINT32_MAX) *
+				info->phy_block_size) >> 20);
+		return -1;
+	}
+	return 0;
+}
+
 struct eckd_dump_param {
-	uint16_t	cyl;
-	uint16_t	head;
-	uint8_t		sec;
+	uint32_t	start_blk;
+	uint32_t	end_blk;
 	uint16_t	blocksize;
 	uint8_t		num_heads;
+	uint8_t		bpt;
+	char		reserved[4];
 } __attribute((packed));
 
+static void eckd_dump_store_param(struct eckd_dump_param* param,
+				  struct disk_info* info)
+{
+	param->start_blk = info->geo.start;
+	param->end_blk = info->geo.start + info->phy_blocks - 1;
+	param->num_heads = info->geo.heads;
+	param->blocksize = info->phy_block_size;
+	param->bpt = disk_sec_from_blocknum(param->end_blk, info);
+}
+
 static int
 install_dump_eckd_classic(int fd, struct disk_info* info, uint64_t mem)
 {
 	struct boot_eckd_stage0 stage0;
 	struct boot_eckd_classic_stage1 stage1;
-	struct eckd_dump_param* param;
 	void* buffer;
 	size_t size;
 	int rc;
@@ -676,22 +702,8 @@ install_dump_eckd_classic(int fd, struct
 	rc = boot_init_eckd_classic_dump_stage1(&stage1, info);
 	if (rc)
 		return rc;
-	/* Fill in start of dump partition */
-	param = (struct eckd_dump_param *) &stage1.param1;
-	param->cyl = disk_cyl_from_blocknum(info->geo.start, info);
-	param->head = disk_head_from_blocknum(info->geo.start, info);
-	param->sec = disk_sec_from_blocknum(info->geo.start, info);
-	param->blocksize = info->phy_block_size;
-	param->num_heads = info->geo.heads;
-	/* Fill in end of dump partition. Note that on ECKD devices with
-	 * classic disk layout, there is exactly one partition so that the
-	 * end of the device equals the end of the partition. */
-	param = (struct eckd_dump_param *) &stage1.param2;
-	param->cyl = info->geo.cylinders - 1;
-	param->head = info->geo.heads - 1;
-	param->sec = info->geo.sectors;
-	param->blocksize = info->phy_block_size;
-	param->num_heads = 0; /* Unused */
+	/* Fill in dump partition parameter */
+	eckd_dump_store_param((struct eckd_dump_param *) &stage1.param1, info);
 	/* Install stage 1 */
 	if (lseek(fd, info->phy_block_size, SEEK_SET) !=
 	    info->phy_block_size) {
@@ -731,7 +743,6 @@ install_dump_eckd_compatible(int fd, str
 {
 	struct boot_eckd_stage0 stage0;
 	struct boot_eckd_compatible_stage1 stage1;
-	struct eckd_dump_param* param;
 	void* buffer;
 	size_t size;
 	int rc;
@@ -754,23 +765,8 @@ install_dump_eckd_compatible(int fd, str
 	rc = boot_init_eckd_compatible_dump_stage1(&stage1, info, 0);
 	if (rc)
 		return rc;
-	/* Fill in start of dump partition */
-	param = (struct eckd_dump_param *) &stage1.param1;
-	param->cyl = disk_cyl_from_blocknum(info->geo.start, info);
-	param->head = disk_head_from_blocknum(info->geo.start, info);
-	param->sec = disk_sec_from_blocknum(info->geo.start, info);
-	param->blocksize = info->phy_block_size;
-	param->num_heads = info->geo.heads;
-	/* Fill in end of dump partition. */
-	param = (struct eckd_dump_param *) &stage1.param2;
-	param->cyl = disk_cyl_from_blocknum(info->geo.start +
-					    info->phy_blocks - 1, info);
-	param->head = disk_head_from_blocknum(info->geo.start +
-					      info->phy_blocks - 1, info);
-	param->sec = disk_sec_from_blocknum(info->geo.start +
-					    info->phy_blocks - 1, info);
-	param->blocksize	= info->phy_block_size;
-	param->num_heads	= 0; /* Unused */
+	/* Fill in dump partition parameter */
+	eckd_dump_store_param((struct eckd_dump_param *) &stage1.param1, info);
 	/* Install stage 1 */
 	if (lseek(fd, info->phy_block_size + 4, SEEK_SET) !=
 	    info->phy_block_size + 4) {
@@ -993,7 +989,6 @@ install_dump(const char* device, uint64_
 	if (rc) {
 		error_text("Could not get information for dump target "
 			   "'%s'", device);
-		close(fd);
 		return rc;
 	}
 	if (info->partnum == 0) {
@@ -1022,6 +1017,12 @@ install_dump(const char* device, uint64_
 	switch (info->type) {
 	case disk_type_eckd_classic:
 	case disk_type_eckd_compatible:
+		if (check_partition_bounds(info)) {
+			error_text("Dump target '%s'", device);
+			rc = -1;
+			break;
+		}
+		/* Fall through. */
 	case disk_type_fba:
 		part_size = info->phy_block_size * info->phy_blocks;
 		printf("Dump target: partition '%s' with a size of "
@@ -1044,9 +1045,13 @@ install_dump(const char* device, uint64_
 			rc = install_dump_fba(fd, info, mem);
 		break;
 	case disk_type_scsi:
+		error_reason("%s: Unsupported disk type '%s' (try --dumptofs)",
+			     device, disk_get_type_name(info->type));
+		rc = -1;
+		break;
 	case disk_type_diag:
-		error_reason("Unsupported disk type '%s' of dump target '%s'",
-			     disk_get_type_name(info->type), device);
+		error_reason("%s: Unsupported disk type '%s'",
+			     device, disk_get_type_name(info->type));
 		rc = -1;
 		break;
 	}
@@ -1074,14 +1079,14 @@ install_mvdump(char* const device[], int
 		fd = open(device[i], O_RDWR);
 		if (fd == -1) {
 			error_reason(strerror(errno));
-			error_text("Could not open dump target '%s'", 
+			error_text("Could not open dump target '%s'",
 				   device[i]);
 			rc = -1;
 			goto out;
 		}
 		if (rewind_tape(fd) == 0) {
 			/* Rewind worked - this is a tape */
-			error_text("Dump target '%s' is a tape device", 
+			error_text("Dump target '%s' is a tape device",
 				   device[i]);
 			close(fd);
 			rc = -1;
@@ -1121,9 +1126,14 @@ install_mvdump(char* const device[], int
 					       info[i]->device, device[i]);
 		if (rc)
 			goto out;
+		if (check_partition_bounds(info[i])) {
+			error_text("Dump target '%s'", device[i]);
+			rc = -1;
+			goto out;
+		}
 		parm.param[i].start_blk = info[i]->geo.start;
 		parm.param[i].end_blk = info[i]->geo.start +
-			info[i]->phy_blocks -1;
+					info[i]->phy_blocks - 1;
 		parm.param[i].end_sec =
 			disk_sec_from_blocknum(info[i]->geo.start +
 					       info[i]->phy_blocks - 1,
Index: s390-tools-sles11/zipl/src/job.c
===================================================================
--- s390-tools-sles11.orig/zipl/src/job.c
+++ s390-tools-sles11/zipl/src/job.c
@@ -585,7 +585,7 @@ check_job_mvdump_data(struct job_mvdump_
 		return rc;
 	}
 	if (size == 0) {
-		error_text("Dump target list '%s' is empty.", 
+		error_text("Dump target list '%s' is empty.",
 			   dump->device_list);
 		free(buffer);
 		return -1;
@@ -596,7 +596,7 @@ check_job_mvdump_data(struct job_mvdump_
 		else if (j > 0) {
 			if (k == MAX_DUMP_VOLUMES) {
 				error_text("Dump target list '%s' contains "
-					   "more than %d entries.", 
+					   "more than %d entries.",
 					   dump->device_list, k);
 				free(buffer);
 				return -1;
@@ -620,7 +620,7 @@ check_job_mvdump_data(struct job_mvdump_
 		for (j = 0; j < i; j++) {
 			if (!strcmp(dump->device[i], dump->device[j])) {
 				error_text("Dump target list '%s' contains "
-					   "duplicate entry '%s'.", 
+					   "duplicate entry '%s'.",
 					   dump->device_list, dump->device[i]);
 				return -1;
 			}
Index: s390-tools-sles11/zipl/src/zipl.c
===================================================================
--- s390-tools-sles11.orig/zipl/src/zipl.c
+++ s390-tools-sles11/zipl/src/zipl.c
@@ -90,8 +90,8 @@ print_usage(void)
 static void
 print_version(void)
 {
-	printf("%s version %s.\n", tool_name, RELEASE_STRING);
-	printf("%s.\n", copyright_notice);
+	printf("%s version %s\n", tool_name, RELEASE_STRING);
+	printf("%s\n", copyright_notice);
 }
 
 
