From 89134a8f40d328a2c7cfc70f0470ae1c1354b036 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Hor=C3=A1k?= Date: Tue, 2 Jun 2026 13:25:08 +0200 Subject: [PATCH 1/7] Revert "zipl/man: Only mention 3490 tape devices" This reverts commit 075f7f71868be2b21fa6f6c49355db3df53c3926. --- zipl/man/zipl.8.in | 2 +- zipl/man/zipl.conf.5.in | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/zipl/man/zipl.8.in b/zipl/man/zipl.8.in index e7256ea5..1226d481 100644 --- a/zipl/man/zipl.8.in +++ b/zipl/man/zipl.8.in @@ -258,7 +258,7 @@ or configuration section on the command line at the same time. Install a system dump record on the device identified by DEVNODE. Supported devices are DASD ECKD or FBA disk partitions, device mapper multipath partitions of FCP attached SCSI disks, -partitions of NVMe disks and IBM 3490 tape devices. +partitions of NVMe disks and IBM 3480/3490/3590/3592 tape devices. For CCW-type DASD dump, zlib compression is used to compress the dump data before writing it to the DASD ECKD partition. Zlib compression diff --git a/zipl/man/zipl.conf.5.in b/zipl/man/zipl.conf.5.in index 2c25fac8..8d8a0461 100644 --- a/zipl/man/zipl.conf.5.in +++ b/zipl/man/zipl.conf.5.in @@ -372,7 +372,7 @@ indicates the menu name. .B Configuration section: .br Specify a DASD partition, device mapper multipath partition device node of a -SCSI disk, a NVMe partition or an IBM 3490 tape device on which to +SCSI disk, a NVMe partition or an IBM 3480/3490/3590 tape device on which to install a system dump record. Once a device prepared in such a way is booted, the current system status is written in a raw format to that device and can later be retrieved using the @@ -596,7 +596,7 @@ file. .IP .B Configuration section: .br -Specify a IBM 3490 tape device on which to install a boot record. +Specify a IBM 3480/3490/3590 tape device on which to install a boot record. This option cannot be used together with .BR 'target' ", " -- 2.54.0 From e48f23e21c46b4577c1c23063fbe5ba2cf369144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Hor=C3=A1k?= Date: Tue, 2 Jun 2026 13:25:38 +0200 Subject: [PATCH 2/7] Revert "zipl/tape2dump: Remove check for data compaction support" This reverts commit 5af1e8cc693ef9be4a8e7efb23b9fa9a4b2e0d6d. --- zipl/boot/tape2dump.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/zipl/boot/tape2dump.c b/zipl/boot/tape2dump.c index 3cb69f45..41136160 100644 --- a/zipl/boot/tape2dump.c +++ b/zipl/boot/tape2dump.c @@ -25,6 +25,7 @@ #define WRITE_CMD 0x01 /* Write block */ #define SENSE_OVERRUN 0x04 /* Sense */ #define WRITETAPEMARK 0x1f /* Write Tape Mark */ +#define READ_DEV_CHAR 0x64 /* Read device characteristics */ #define MODE_SET_DB 0xdb /* Mode set */ /* @@ -104,12 +105,27 @@ static void start_ccw(uint8_t code, uint8_t flags, uint16_t count, void *cda) } /* - * Enable data compaction + * Read tape device characteristics to find out if IDRC compression can be used */ static void setup_idrc_compression(void) { - ccw_init(&ccw_program.compress, MODE_SET_DB, CCW_FLAG_CC, 0x1, &mode_set_byte); - orb.cpa = __pa32(&ccw_program.compress); + uint16_t type; + + start_ccw(READ_DEV_CHAR, 0x0, 0x40, ccw_data); + + memcpy(&type, &ccw_data[3], sizeof(type)); + switch (type) { + case 0x3490: + case 0x3590: // XXX 3592 + ccw_init(&ccw_program.compress, MODE_SET_DB, CCW_FLAG_CC, 0x1, + &mode_set_byte); + orb.cpa = __pa32(&ccw_program.compress); + break; + default: + case 0x3480: + orb.cpa = __pa32(&ccw_program.write); + break; + } } /* -- 2.54.0 From 78202d1ec90774c252c04c4d4247edc020eb3c18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Hor=C3=A1k?= Date: Tue, 2 Jun 2026 13:26:09 +0200 Subject: [PATCH 3/7] Revert "zipl/tape2dump: Remove load display command" This reverts commit 145c21a9ca01b65aac527c8c91f455dccca27581. --- zipl/boot/tape2dump.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/zipl/boot/tape2dump.c b/zipl/boot/tape2dump.c index 41136160..6ad795a4 100644 --- a/zipl/boot/tape2dump.c +++ b/zipl/boot/tape2dump.c @@ -26,6 +26,7 @@ #define SENSE_OVERRUN 0x04 /* Sense */ #define WRITETAPEMARK 0x1f /* Write Tape Mark */ #define READ_DEV_CHAR 0x64 /* Read device characteristics */ +#define LOAD_DISPLAY 0x9f /* Load tape display */ #define MODE_SET_DB 0xdb /* Mode set */ /* @@ -128,6 +129,17 @@ static void setup_idrc_compression(void) } } +/* + * Print message on tape display + */ +static void ccw_load_display(const char *msg) +{ + char _msg[24] = {0x20}; + + strcpy(&_msg[1], msg); + start_ccw(LOAD_DISPLAY, CCW_FLAG_SLI, 0x11, (void *) _msg); +} + /* * Write sense data to buffer */ @@ -170,6 +182,19 @@ static void ccw_write_block(unsigned long addr, unsigned long size, panic(EMEM, "Device too small"); } +/* + * Every 16 MB we update the tape display + */ +static void progress_print_disp(unsigned long addr) +{ + char msg[24]; + + if (addr % (1024 * 1024 * 16) != 0) + return; + snprintf(msg, sizeof(msg), "%08u", addr >> 20); + ccw_load_display(msg); +} + /* * Dump all memory to tape */ @@ -187,9 +212,11 @@ void dt_dump_mem(void) ccw_write_block(addr, BLK_SIZE, page); total_dump_size += BLK_SIZE; progress_print(addr + BLK_SIZE); + progress_print_disp(addr + BLK_SIZE); } df_s390_em_page_init(page); ccw_write_block(page, sizeof(struct df_s390_em), 0); free_page(page); ccw_write_tapemark(); + ccw_load_display("DUMP*END"); } -- 2.54.0 From 724581ae5ccc9623174dd998a00a0797f2d7f993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Hor=C3=A1k?= Date: Tue, 2 Jun 2026 13:26:23 +0200 Subject: [PATCH 4/7] Revert "lstape: Remove type filter support" This reverts commit 1bd8ee4b79885bdf2afd389c914c030490319317. --- zconf/lstape | 19 +++++++++++++++++++ zconf/lstape.8 | 10 ++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/zconf/lstape b/zconf/lstape index 943f3256..b37bd9cd 100755 --- a/zconf/lstape +++ b/zconf/lstape @@ -28,6 +28,10 @@ function PrintUsage() { : Print this help text. : --ccw-only|--scsi-only : Limit output to CCW or SCSI devices only. + : -t|--type + : Limit output of CCW tape devices to the given + : devices. The list consists of device types + : separated by ','. : --online|--offline : Show only devices that are either online or : offline (only one option is allowed). This @@ -59,6 +63,7 @@ function PrintVersion() } FLSEP="" +DEVLIST="3490" SHORTID=false SHOWCCW=true VERBOSE=false @@ -72,6 +77,14 @@ while [ $# -ne 0 ]; do PrintUsage exit 0 ;; + -t|--type) + if [ $# -lt 2 ]; then + RequireArgument $1 1 + exit 1 + fi + shift + DEVLIST="$1" + ;; --online) if $FILTEROFFLINE; then echo -n "Option --online and --offline " >&2 @@ -135,6 +148,10 @@ function SysfsCreateListCCW() { medium_state_str[1] = "LOADED " medium_state_str[2] = "UNLOADED" + split("'$DEVLIST'", A, ",") + for(i = 1; i in A; i++) { + devlist[A[i]] = 1 + } shortid = "'$SHORTID'"=="true" ? 1 : 0 filteronline = "'$FILTERONLINE'"=="true" ? 1 : 0 filteroffline = "'$FILTEROFFLINE'"=="true" ? 1 : 0 @@ -151,6 +168,8 @@ function SysfsCreateListCCW() { devtype = Read(devdir "/devtype") split(devtype, A, "/") + if (!(A[1] in devlist)) + next online = Read(devdir "/online") if (filteronline && !online) diff --git a/zconf/lstape.8 b/zconf/lstape.8 index 6262c64d..019b6e58 100644 --- a/zconf/lstape.8 +++ b/zconf/lstape.8 @@ -64,6 +64,12 @@ of SCSI tape devices. Limit output to either online or offline devices. This filter has no effect on the output of SCSI devices. +.TP +.BR -t | --type " \fI\fR" +Limit output to given device types, for example 3490 +(currently only applies to channel-attached +tape devices). + .TP .I Limits the output to information about the specified tape device or @@ -125,9 +131,9 @@ The serial number. List all tape devices that are available .RE -\fBlstape --ccw-only --online\fR +\fBlstape --ccw-only -t 3490 --online\fR .RS -Show all channel-attached tape devices that are online. +Show all 3490 CCW devices that are online. .RE \fBlstape --scsi-only --verbose\fR -- 2.54.0 From 064e46bf7d7a827086c522d427b3988317f616cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Hor=C3=A1k?= Date: Tue, 2 Jun 2026 13:26:41 +0200 Subject: [PATCH 5/7] Revert "lstape: Remove 3480 and 3590 tape support" This reverts commit 4f0dfae97ea61b0518cb3d61977712f31f1d8206. --- zconf/lstape | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/zconf/lstape b/zconf/lstape index b37bd9cd..9496686b 100755 --- a/zconf/lstape +++ b/zconf/lstape @@ -31,7 +31,7 @@ function PrintUsage() { : -t|--type : Limit output of CCW tape devices to the given : devices. The list consists of device types - : separated by ','. + : separated by ','. (e.g. -t 3480,3490) : --online|--offline : Show only devices that are either online or : offline (only one option is allowed). This @@ -63,7 +63,7 @@ function PrintVersion() } FLSEP="" -DEVLIST="3490" +DEVLIST="3480,3490,3590" SHORTID=false SHOWCCW=true VERBOSE=false @@ -142,6 +142,8 @@ function SysfsCreateListCCW() { ( find $1/bus/ccw/drivers/tape_34xx -type l \ -printf '%f\n' 2>/dev/null + find $1/bus/ccw/drivers/tape_3590 -type l \ + -printf '%f\n' 2>/dev/null ) | awk -v format="$CCWFORMAT" ' BEGIN{ medium_state_str[0] = "UNKNOWN " -- 2.54.0 From 85b6f905206603c20496b91a8035494d6610ca7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Hor=C3=A1k?= Date: Tue, 2 Jun 2026 13:26:59 +0200 Subject: [PATCH 6/7] Revert "lstape: Remove trailing whitespace" This reverts commit 3d6bb988c24d1723ca4b631e5ff9489d3e419ed7. --- zconf/lstape | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zconf/lstape b/zconf/lstape index 9496686b..e2410e1b 100755 --- a/zconf/lstape +++ b/zconf/lstape @@ -220,7 +220,7 @@ function SysfsCreateListCCW() { (online==0) ? "N/A" : \ medium_state_str[medium_state]) } - ' | sort + ' | sort } # handle SCSI device not necessarily zfcp-attached, e.g. virtio-scsi-ccw -- 2.54.0 From b0e9602ade53403fd6e941e4b1d9ff5f76e9fcaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Hor=C3=A1k?= Date: Tue, 2 Jun 2026 13:27:23 +0200 Subject: [PATCH 7/7] Revert "tape390: Remove tape390_display and tape390_crypt" This reverts commit 8f83002e379eb84b91f09a303c97d8338adc40e0. --- .gitignore | 2 + Makefile | 2 +- README.md | 7 + tape390/Makefile | 25 ++ tape390/tape390_common.c | 86 ++++++ tape390/tape390_common.h | 22 ++ tape390/tape390_crypt.8 | 108 +++++++ tape390/tape390_crypt.c | 581 ++++++++++++++++++++++++++++++++++++++ tape390/tape390_display.8 | 150 ++++++++++ tape390/tape390_display.c | 287 +++++++++++++++++++ 10 files changed, 1269 insertions(+), 1 deletion(-) create mode 100644 tape390/Makefile create mode 100644 tape390/tape390_common.c create mode 100644 tape390/tape390_common.h create mode 100644 tape390/tape390_crypt.8 create mode 100644 tape390/tape390_crypt.c create mode 100644 tape390/tape390_display.8 create mode 100644 tape390/tape390_display.c diff --git a/.gitignore b/.gitignore index 36a2c851..e76abc9a 100644 --- a/.gitignore +++ b/.gitignore @@ -111,6 +111,8 @@ qethqoat/qethqoat systemd/cpacfstatsd.service systemd/iucvtty-login@.service systemd/ttyrun-getty@.service +tape390/tape390_crypt +tape390/tape390_display tunedasd/src/tunedasd tunedasd/src/_tunedasd tunedasd/src/tunedasd.bash diff --git a/Makefile b/Makefile index 4dbce8cc..329aebfa 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ BASELIB_DIRS = libutil libseckey LIB_DIRS = libvtoc libzds libdasd libccw libvmcp libekmfweb \ libkmipclient libcpumf libap libpv libzpci TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \ - osasnmpd qetharp ip_watcher qethconf scripts zconf \ + tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \ vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \ ziomon iucvterm hyptop cmsfs-fuse qethqoat zfcpdump zdsfs cpumf \ systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc zpcictl \ diff --git a/README.md b/README.md index 91d61c5a..8298e38c 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,13 @@ Package contents * qetharp: Read and flush the ARP cache on OSA Express network cards. + * tape390_display: + Display information on the message display facility of a s390 tape + device. + + * tape390_crypt: + Control and query crypto settings for 3592 tape devices. + * osasnmpd: NET-SNMP subagent implementing MIBs provided by OSA-Express features Fast Ethernet, Gigabit Ethernet, 10 Gigabit Ethernet. diff --git a/tape390/Makefile b/tape390/Makefile new file mode 100644 index 00000000..63b5937f --- /dev/null +++ b/tape390/Makefile @@ -0,0 +1,25 @@ +include ../common.mak + +ALL_CPPFLAGS += -D_FILE_OFFSET_BITS=64 + +all: tape390_display tape390_crypt + +tape390_display: tape390_display.o tape390_common.o + +tape390_crypt: tape390_crypt.o tape390_common.o + +install: all + $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8 + $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 tape390_display \ + $(DESTDIR)$(BINDIR) + $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 tape390_display.8 \ + $(DESTDIR)$(MANDIR)/man8 + $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 tape390_crypt \ + $(DESTDIR)$(BINDIR) + $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 tape390_crypt.8 \ + $(DESTDIR)$(MANDIR)/man8 + +clean: + rm -f *.o *~ tape390_display tape390_crypt core + +.PHONY: all install clean diff --git a/tape390/tape390_common.c b/tape390/tape390_common.c new file mode 100644 index 00000000..821e8485 --- /dev/null +++ b/tape390/tape390_common.c @@ -0,0 +1,86 @@ +/* + * tape_390 - Common functions + * + * Copyright IBM Corp. 2006, 2017 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include +#include +#include +#include +#include +#include + +#include "tape390_common.h" + +#define PROC_DEVICES_FILE "/proc/devices" +#define PROC_DEVICES_FILE_WIDTH 100 + +char *prog_name; /* Name of tool */ + +/* + * Set name of tool + */ +void set_prog_name(char *name) +{ + prog_name = name; +} + +/* + * Check whether specified device node is tape device + */ +int is_not_tape(char *device) +{ + FILE* fh; + char line[PROC_DEVICES_FILE_WIDTH]; + char last_line[PROC_DEVICES_FILE_WIDTH]; + int found = 0; + struct stat stat_struct; + + if (stat(device, &stat_struct)) { + ERRMSG("%s: Unable to get device status for " + "'%s'. \n", prog_name, device); + perror(""); + return 1; + } + fh = fopen(PROC_DEVICES_FILE,"r"); + if (!fh) { + ERRMSG("%s: WARNING: Cannot check for tape in file " + PROC_DEVICES_FILE ".\n", prog_name); + perror(""); + return(0); /* check not possible, just continue */ + } + while (!found && (fscanf(fh, "%s", line) != EOF)) { + if (strcmp(line, "tape") == 0) + found = 1; + else + strcpy(last_line, line); + } + fclose(fh); + if (found && (major(stat_struct.st_rdev) == + (unsigned int) atoi(last_line))) + return (0); + else { + ERRMSG("%s: '%s' is not a tape device. \n", prog_name, device); + return 1; + } +} + +/* + * Open the tape device + */ +int open_tape(char *device) +{ + int fd; + fd = open(device,O_RDONLY); + if (fd < 0) { + ERRMSG("%s: Cannot open device %s.\n", + prog_name,device); + perror(""); + exit(EXIT_MISUSE); + } + return fd; +} diff --git a/tape390/tape390_common.h b/tape390/tape390_common.h new file mode 100644 index 00000000..d18b88cc --- /dev/null +++ b/tape390/tape390_common.h @@ -0,0 +1,22 @@ +/* + * tape_390 - Common functions + * + * Copyright IBM Corp. 2006, 2017 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef _TAPE390_COMMON_H +#define _TAPE390_COMMON_H + +#define ERRMSG(x...) {fflush(stdout);fprintf(stderr,x);} +#define ERRMSG_EXIT(ec,x...) do {fflush(stdout);fprintf(stderr,x);exit(ec);} while(0) +#define EXIT_MISUSE 1 + +extern int is_not_tape(char *); +extern int open_tape(char *); +extern void set_prog_name(char *); +extern char *prog_name; + +#endif diff --git a/tape390/tape390_crypt.8 b/tape390/tape390_crypt.8 new file mode 100644 index 00000000..b3f0c455 --- /dev/null +++ b/tape390/tape390_crypt.8 @@ -0,0 +1,108 @@ +.\" Copyright 2017 IBM Corp. +.\" s390-tools is free software; you can redistribute it and/or modify +.\" it under the terms of the MIT license. See LICENSE for details. +.\" +.TH TAPE390_CRYPT 8 "Apr 2006" "s390-tools" + +.SH NAME +tape390_crypt \- encryption support for zSeries tape devices. + +.SH SYNOPSIS +.B tape390_crypt +[OPTION] [DEVICE] + +.SH DESCRIPTION +.B tape390_crypt +exploits encryption features available in zSeries tape devices. +It can be used to enable or disable tape encryption and to set +KEK (Key Encrypting Key) labels. + +.SH OPTIONS +.TP +.BR "\-h" " or " "\-\-help" +Print help information, then exit. + +.TP +.BR "\-v" " or " "\-\-version" +Print version information, then exit. + +.TP +.BR "\-q" " or " "\-\-query" +Print current encryption status of the specified tape device and of the +loaded medium. +If encryption is on and the medium is encrypted , +additional information on the encryption keys is displayed. + +.TP +.BR "\-e " { "on" | "off" } " " or " \-\-encryption=" { "on" | "off" } +sets tape encryption on or off. + +.TP +.BR "\-k " [ "label" | "hash" ] " " or " \-\-key=" [ "label" | "hash" ] +sets tape encryption keys. +.br + specifies the KEK +(Key Encrypting Key), which can be maximal 64 characters long. +.br +The store type (either label or hash) specifies how the KEK in is to be stored on the tape medium. Since labels are +human readable strings and hence more user friendly than hashes, +the default store type is label. +.br +The \-k option can only be specified, if the tape medium is at load point. +.br +While processing the \-k option, the tape medium is initialized and all +data eventually contained on the tape medium is lost. +To avoid inadvertent data loss a prompt message is issued asking the user +whether he or she really wants to proceed. +.br +The \-k option can be specified maximal twice, because on the tape medium +maximal two EEDKs (External Encrypted Data Keys) can be stored. +If specified once, two identical EEDKs are stored. +.br + is a character separating the KEK in from the store type +(either label or hash). This +delimiter can be specified with the \-d option as explained below. + +.TP +.BR "\-d " " or " "\-\-delimiter= +specifies the character which separates the KEK +in from the store type (either label or hash). +The default delimiter is : (colon). +The \-d option can only be specified together with the \-k option. + +.TP +.BR "\-f " " or " "\-\-force +specifies that no prompt message is to be issued before writing the KEK +information and initializing the tape medium. +The \-f option can only be specified together with the \-k option. + +.TP +.BR device +specifies the device node of the tape device. + +.SH EXAMPLES +1. Scenario: +.br +mount non-encrypted tape and write data with the +.B "default" +KEKs: +.br +tape390_crypt \-e on /dev/ntibm0 +.br +tar cfz /dev/ntibm0 /data + +2. Scenario: +.br +mount non-encrypted tape and write data with +.B "specific" +KEKs: +.br +tape390_crypt \-k my_first_key \-k my_second_key:hash /dev/ntibm0 +.br +tar cfz /dev/ntibm0 /data + +3. Scenario: +.br +mount tape and display current encryption status: +.br +tape390_crypt \-q /dev/ntibm0 diff --git a/tape390/tape390_crypt.c b/tape390/tape390_crypt.c new file mode 100644 index 00000000..03bd4522 --- /dev/null +++ b/tape390/tape390_crypt.c @@ -0,0 +1,581 @@ +/* + * tape390_crypt: Provide encryption for tape devices + * + * Copyright 2006, 2017 IBM Corp. + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lib/zt_common.h" +#include "tape390_common.h" + +#define KEKL_LENGTH 65 /* 64 bytes plus trailing \0 */ +#define TOT_KEKL_LENGTH KEKL_LENGTH+1+5 /* kekl plus colon plus LABEL or HASH */ +#define ENCRYPTION_ON 1 +#define ENCRYPTION_OFF 0 +#define CRYPT_SET_FAILED_MSG "%s: Tape drive does not support encryption.\n" +#define CRYPT_QUERY_FAILED_MSG CRYPT_SET_FAILED_MSG +#define KEKL_QUERY_FAILED_MSG "%s: Query of encryption keys failed.\n" +#define KEKL_SET_FAILED_MSG "%s: Unable to set or modify encryption keys.\n" +#define PROC_DEVICES_FILE "/proc/devices" +#define PROC_DEVICES_FILE_WIDTH 100 +#define MAX_BLOCKSIZE 65535 /* max blocksize supported by escon/ficon tapes */ + +#ifndef ENOKEY +#define ENOKEY 126 +#endif +#ifndef EKEYREJECTED +#define EKEYREJECTED 129 +#endif + +#define CHECK_SPEC_MAX(i,j,numstr,str) \ + {if (i>j) ERRMSG_EXIT(EXIT_MISUSE,"%s: " str " " \ + "can only be specified " numstr ".\n",prog_name);} + +#define PRINT_TYPE(byte) \ + {if (byte==TAPE390_KEKL_TYPE_LABEL) printf("label\n");\ + else if (byte == TAPE390_KEKL_TYPE_HASH) printf("hash\n");\ + else printf("Unsupported type\n");} + +/* + * Structure tape390_crypt is used to store the command line parameters + */ +struct tape390_crypt { + int e_switch; + char delimiter; + char devname[PATH_MAX]; + char kekl_input[2][TOT_KEKL_LENGTH]; + int encryption_specified; + int query_specified; + int key_specified; + int delimiter_specified; + int force_specified; +} __attribute__ ((packed)); + +/* + * The following ioctl's belong to linux-2.5/include/asm-s390/tape390.h + * + * The TAPE390_CRYPT_SET ioctl is used to switch on/off encryption. + * The "encryption_capable" field is ignored for this ioctl! + */ +#define TAPE390_CRYPT_SET _IOW('d', 2, struct tape390_crypt_info) + +/* + * The TAPE390_CRYPT_QUERY ioctl is used to query the encryption state. + */ +#define TAPE390_CRYPT_QUERY _IOR('d', 3, struct tape390_crypt_info) + +struct tape390_crypt_info { + char capability; + char status; + char medium_status; +} __attribute__ ((packed)); + +/* Macros for "capable" field */ +#define TAPE390_CRYPT_SUPPORTED_MASK 0x01 +#define TAPE390_CRYPT_SUPPORTED(x) \ + ((x.capability & TAPE390_CRYPT_SUPPORTED_MASK)) + +/* Macros for "status" field */ +#define TAPE390_CRYPT_ON_MASK 0x01 +#define TAPE390_CRYPT_ON(x) (((x.status) & TAPE390_CRYPT_ON_MASK)) + +/* Macros for "medium status" field */ +#define TAPE390_MEDIUM_LOADED_MASK 0x01 +#define TAPE390_MEDIUM_ENCRYPTED_MASK 0x02 +#define TAPE390_MEDIUM_ENCRYPTED(x) \ + (((x.medium_status) & TAPE390_MEDIUM_ENCRYPTED_MASK)) +#define TAPE390_MEDIUM_LOADED(x) \ + (((x.medium_status) & TAPE390_MEDIUM_LOADED_MASK)) + +/* + * The TAPE390_KEKL_SET ioctl is used to set Key Encrypting Key labels. + */ +#define TAPE390_KEKL_SET _IOW('d', 4, struct tape390_kekl_pair) + +/* + * The TAPE390_KEKL_QUERY ioctl is used to query Key Encrypting Key labels. + */ +#define TAPE390_KEKL_QUERY _IOR('d', 5, struct tape390_kekl_pair) + +struct tape390_kekl { + unsigned char type; + unsigned char type_on_tape; + char label[65]; +} __attribute__ ((packed)); + +struct tape390_kekl_pair { + struct tape390_kekl kekl[2]; +} __attribute__ ((packed)); + +/* Values for "kekl1/2_type" and "kekl1/2_type_on_tape" fields */ +#define TAPE390_KEKL_TYPE_NONE 0 +#define TAPE390_KEKL_TYPE_LABEL 1 +#define TAPE390_KEKL_TYPE_HASH 2 + +/* Full tool name */ +static const char tool_name[] = "tape390_crpyt: zSeries tape encryption program"; + +/* Copyright notice */ +static const char copyright_notice[] = "Copyright IBM Corp. 2006, 2017"; + +/* + * Print version information. + */ +static void print_version (void) +{ + printf ("%s version %s\n", tool_name, RELEASE_STRING); + printf ("%s\n", copyright_notice); +} + +/* + * prints out the usage text + */ +static void tape390_crypt_usage (void) +{ + printf ("Usage: tape390_crypt [OPTIONS] [DEVICE]\n" + "\n" + "Provide encryption for " + "tape devices.\n" + "DEVICE is the node of the tape device (e.g. '/dev/ntibm0')\n" + "\n" + "-h, --help Print this help, then exit\n" + "-v, --version Print version information, " + "then exit\n" + "-q, --query Query information on encryption " + "status and keys\n" + "-k, --key value:type Set encryption key(s)\n" + "-d, --delimiter char Specify character separating " + "key value from key type\n" + "-f, --force Do not prompt user\n" + "-e, --encryption on|off Switch encryption on or off\n"); +} + +/* + * initialize the tape390_crypt info structure + */ +static void init_info(struct tape390_crypt *info) +{ + info->delimiter = ':'; + info->encryption_specified = 0; + info->query_specified = 0; + info->key_specified = 0; + info->delimiter_specified = 0; + info->force_specified = 0; +} + +/* + * parse the commandline options + */ +static void tape390_crypt_parse_opts(struct tape390_crypt *info, int argc, + char* argv[]) +{ + int opt, index, kekl_too_long=0, deli_too_long=0, e_switch_invalid=0; + int i; + static struct option crypt_long_options[] = { + { "version", no_argument, NULL, 'v'}, + { "encryption", required_argument, NULL, 'e'}, + { "query", no_argument, NULL, 'q'}, + { "key", required_argument, NULL, 'k'}, + { "help", no_argument, NULL, 'h'}, + { "delimiter", required_argument, NULL, 'd'}, + { "force", no_argument, NULL, 'f'}, + { NULL, 0, NULL, 0 } + }; + /* Command line option abbreviations */ + static const char crypt_option_string[] = "ve:qk:hd:f"; + + while (1) { + opt = getopt_long(argc, argv, crypt_option_string, + crypt_long_options, &index); + if (opt == -1) + break; + switch (opt) { + case 'v': + print_version(); + exit(0); + case 'h': + tape390_crypt_usage(); + exit(0); + case 'q': + ++info->query_specified; + break; + case 'k': + ++info->key_specified; + if(strlen(optarg) >= TOT_KEKL_LENGTH) + kekl_too_long = 1; + else if (info->key_specified == 1) + for (i = 0; i < 2; i++) + strcpy(info->kekl_input[i], optarg); + else + strcpy(info->kekl_input[1], optarg); + break; + case 'e': + ++info->encryption_specified; + if (strcmp(optarg,"off") == 0) + info->e_switch = ENCRYPTION_OFF; + else if (strcmp(optarg,"on") == 0) + info->e_switch = ENCRYPTION_ON; + else + e_switch_invalid = 1; + break; + case 'd': + ++info->delimiter_specified; + if(strlen(optarg) > 1) + deli_too_long = 1; + else + info->delimiter = optarg[0]; + break; + case 'f': + ++info->force_specified; + break; + default: + fprintf(stderr, "Try '%s --help' for more" + " information.\n",prog_name); + exit(1); + } + } + CHECK_SPEC_MAX(info->query_specified, 1, "once", "query"); + CHECK_SPEC_MAX(info->encryption_specified, 1, "once", "encryption"); + CHECK_SPEC_MAX(info->delimiter_specified, 1, "once", "delimiter"); + CHECK_SPEC_MAX(info->force_specified, 1, "once", "force"); + CHECK_SPEC_MAX(info->key_specified,2, "twice", "key"); + if (kekl_too_long) + ERRMSG_EXIT(EXIT_MISUSE, + "%s: Specified key too long.\n",prog_name); + if (deli_too_long) + ERRMSG_EXIT(EXIT_MISUSE, + "%s: Delimiter must be one character.\n",prog_name); + if (e_switch_invalid) + ERRMSG_EXIT(EXIT_MISUSE, + "%s: ERROR: encryption can be set 'on' or 'off'.\n", + prog_name); + if (((info->query_specified) && + (info->key_specified + info->encryption_specified > 0)) || + ((info->encryption_specified) && + (info->key_specified + info->query_specified > 0)) || + ((info->force_specified) && + (info->encryption_specified)) || + ((info->delimiter_specified) && + (info->encryption_specified + info->query_specified > 0))) + ERRMSG_EXIT(EXIT_MISUSE, + "%s: Conflicting options specified.\n",prog_name); + if (info->query_specified + info->encryption_specified + + info->key_specified == 0) + ERRMSG_EXIT(EXIT_MISUSE, + "%s: ERROR: Missing options.\n",prog_name); + /* save device */ + if (optind >= argc) + ERRMSG_EXIT(EXIT_MISUSE, + "%s: No device specified.\n",prog_name); + if (optind + 1 < argc) + ERRMSG_EXIT(EXIT_MISUSE, + "%s: More than one device specified.\n",prog_name); + strcpy(info->devname, argv[optind]); +} + +/* + * check and parse key operand, must be value:type + * where value is an arbitrary string, maximal 64 char long + * : is a 1-char delimiter specified with the -d option (default :) + * type is the type identifier, which can be either 'hash' or 'label' + * default is 'label' + */ +static void fill_kekl(char *s, char delimiter, + struct tape390_kekl *mykekl) +{ + int i; + char typestring[TOT_KEKL_LENGTH] = {0}; + for (i = strlen(s); i >= 0; i--) + if (*(s+i) == delimiter) + break; + if (i == -1) /* delimiter not found, no type specified */ + mykekl->type_on_tape = TAPE390_KEKL_TYPE_LABEL; /* default type*/ + else if (i == 0) + ERRMSG_EXIT(EXIT_MISUSE, + "%s: ERROR: delimiter '%c' found as first " + "character.\n",prog_name,delimiter); + else if (i == (int) strlen(s) - 1) + ERRMSG_EXIT(EXIT_MISUSE, + "%s: ERROR: Delimiter '%c' found as last " + "character.\n",prog_name,delimiter); + else { + strcpy(typestring,s+i+1); + *(s+i) = '\0'; + if (strcmp(typestring,"hash") == 0) + mykekl->type_on_tape = TAPE390_KEKL_TYPE_HASH; + else if (strcmp(typestring,"label") == 0) + mykekl->type_on_tape = TAPE390_KEKL_TYPE_LABEL; + else + ERRMSG_EXIT(EXIT_MISUSE, + "%s: Invalid key type '%s'," + " must be either 'label' or 'hash'.\n", + prog_name,typestring); + } + if (strlen(s) >= KEKL_LENGTH) + ERRMSG_EXIT(EXIT_MISUSE, + "%s: Specified key value too long, has %d chars.\n", + prog_name,(int)strlen(s)); + else { + strcpy(mykekl->label, s); + mykekl->type = TAPE390_KEKL_TYPE_LABEL; + } +} + +/* + * Write two tapemarks on tape to initialize tape after kekl set + */ + +static int init_tape(int fd) +{ + struct mtop mtop; + int rc; + + mtop.mt_op = MTWEOF; + mtop.mt_count = 2; + rc = ioctl(fd, MTIOCTOP, &mtop); + if (rc) + return rc; + mtop.mt_op = MTREW; + return ioctl(fd, MTIOCTOP, &mtop); +} + +/* + * Tell the current block on tape + */ + +static int tell_tape(int fd) +{ + struct mtop mtop; + + mtop.mt_op = MTTELL; + mtop.mt_count = 1; + return ioctl(fd, MTIOCTOP, &mtop); +} + + +/* + * Do read IO to tape in order to enforce update of KEKL information. + * Leave tape position unchanged. + */ + +static int read_tape(int fd) +{ + struct mtop mtop; + int block_number, n_read; + char buffer[MAX_BLOCKSIZE]; + + /* remember current block on tape */ + block_number = tell_tape(fd); + n_read = read(fd, buffer, sizeof(buffer)); + if (n_read == -1) { + /* tape read failed, ignore if caused by end-of-volume */ + if (errno == ENOSPC) + return 0; + else + return 1; + } + if (block_number != tell_tape(fd)) { + /* re-position tape */ + mtop.mt_op = MTSEEK; + mtop.mt_count = block_number; + if (ioctl(fd, MTIOCTOP, &mtop) != 0) + return 1; + } + return 0; +} + +/* + * set Key Encrypting Key labels + */ +static int set_encryption_keys(int fd, struct tape390_crypt *info) +{ + struct tape390_kekl_pair kekls; + char delim; + char *s, inp_buffer[5]; + int i, rc; + + delim = info->delimiter; + for (i = 0; i < 2; i++) { + s = info->kekl_input[i]; + fill_kekl(s, delim, &kekls.kekl[i]); + } + if (info->force_specified == 0) { + printf("--->> ATTENTION! <<---\n"); + printf("All data on tape %s will be lost.\nType " + "\"yes\" to continue: ", info->devname); + if (fgets(inp_buffer, sizeof(inp_buffer), stdin) == NULL) { + fprintf(stderr, "Reading from terminal failed.\n"); + exit(1); + } + if (strcasecmp(inp_buffer,"yes\n")) { + printf("Tape and its encryption keys " + "remain unchanged.\n"); + exit(0); + } + } + rc = ioctl(fd, TAPE390_KEKL_SET, &kekls); + if (rc) + return rc; + return init_tape(fd); +} + +/* + * Switch on/off encryption + */ +static int switch_encryption_on_or_off(int fd,struct tape390_crypt *info) +{ + struct tape390_crypt_info myinfo; + + memset(&myinfo, 0, sizeof(myinfo)); + if (info->e_switch == ENCRYPTION_ON) + myinfo.status |= TAPE390_CRYPT_ON_MASK; + return ioctl(fd, TAPE390_CRYPT_SET, &myinfo); +} + +/* + * Query information on encryption and keys + */ +static int query_encryption(int fd,struct tape390_crypt *info) +{ + struct tape390_crypt_info myinfo; + struct tape390_kekl_pair kekls; + int i, ret; + + ret = ioctl(fd, TAPE390_CRYPT_QUERY, &myinfo); + if (ret) + return -1; + if (!TAPE390_MEDIUM_LOADED(myinfo)) { + printf("Medium not loaded\n"); + return 0; + } + printf("ENCRYPTION: "); + if (TAPE390_CRYPT_ON(myinfo)) + printf("ON\n"); + else + printf("OFF\n"); + printf("MEDIUM: "); + if (TAPE390_MEDIUM_ENCRYPTED(myinfo)) + printf("ENCRYPTED\n"); + else { + printf("NOT ENCRYPTED\n"); + return 0; + } + /* + * do a dummy read in order to force the control unit to get + * the key information from the EKM + */ + ret = read_tape(fd); + if (ret && (info->force_specified == 0)) + return -2; + ret = ioctl(fd, TAPE390_KEKL_QUERY, &kekls); + if (ret) + return -2; + printf("KEKLs on TAPE:\n"); + if (kekls.kekl[0].type == TAPE390_KEKL_TYPE_NONE) { + ERRMSG("Unable to retrieve key information.\n"); + return 0; + } + for (i = 0; i < 2; i++) { + printf(" KEY%i:\n",i+1); + if (kekls.kekl[i].type == TAPE390_KEKL_TYPE_NONE) + printf("None\n"); + else { + printf(" value: %s\n",kekls.kekl[i].label); + printf(" type: "); + PRINT_TYPE(kekls.kekl[i].type); + printf(" ontape: "); + PRINT_TYPE(kekls.kekl[i].type_on_tape); + } + } + return 0; +} + +int main(int argc, char *argv[]) +{ + struct tape390_crypt info; + char error_msg[80]; + int rc=0, fd=-1; + + /* set name of program */ + set_prog_name(basename(argv[0])); + + /* set default values */ + init_info(&info); + + /* parse command line options and check syntax */ + tape390_crypt_parse_opts(&info, argc, argv); + + /* check whether specified device node is tape */ + if (is_not_tape(info.devname)) + exit(EXIT_MISUSE); + + /* open device */ + fd = open_tape(info.devname); + + /* process -k or -e or -q operand */ + if (info.key_specified >= 1) { + rc = set_encryption_keys(fd, &info); + if (rc == 0) + printf("SUCCESS: key information set.\n"); + strcpy(error_msg, KEKL_SET_FAILED_MSG); + } else if (info.encryption_specified) { + rc = switch_encryption_on_or_off(fd, &info); + strcpy(error_msg, CRYPT_SET_FAILED_MSG); + } else if (info.query_specified) { + rc = query_encryption(fd, &info); + if (rc == -1) + strcpy(error_msg, CRYPT_QUERY_FAILED_MSG); + else if (rc == -2) + strcpy(error_msg, KEKL_QUERY_FAILED_MSG); + } + if (rc) { + ERRMSG(error_msg, prog_name); + switch (errno) { + case ENOKEY: + ERRMSG("EKM error.\n"); + break; + case ENOTCONN: + ERRMSG("Could not connect to EKM.\n"); + break; + case EKEYREJECTED: + ERRMSG("Key not available on EKM.\n"); + break; + case EUNATCH: + ERRMSG("Encryption must be set ON prior to " + "setting or querying encryption " + "keys.\n"); + break; + case EBADSLT: + ERRMSG("Tape must be at load point in order " + "to set encryption keys.\n"); + break; + case ENOSYS: + ERRMSG("Tape device does not support " + "encryption.\n"); + break; + case EINVAL: + ERRMSG("Kernel does not support tape " + "encryption.\n"); + break; + default: + perror(""); + } + } + if (fd != -1) + close(fd); + return(rc); +} diff --git a/tape390/tape390_display.8 b/tape390/tape390_display.8 new file mode 100644 index 00000000..0c51c5a0 --- /dev/null +++ b/tape390/tape390_display.8 @@ -0,0 +1,150 @@ +.\" Copyright 2017 IBM Corp. +.\" s390-tools is free software; you can redistribute it and/or modify +.\" it under the terms of the MIT license. See LICENSE for details. +.\" +.TH TAPE390_DISPLAY 8 "Apr 2006" "s390-tools" + +.SH NAME +tape390_display \- display messages on the display unit of a zSeries tape +device. + +.SH SYNOPSIS +.TP 16 +.B tape390_display +.RB [ \-b | \-\-blink ] +.br +.RB [ \-h | \-\-help ] +.br +.RB [ \-l | \-\-load ] +.br +.RB [ \-t | \-\-type ] +.br +.RB [ \-q | \-\-quiet ] +.br +.RB [ \-v | \-\-version ] +.br +.IR message1 +.RB [ +.I message2 +.RB ] +.I device + +.SH DESCRIPTION +\fBtape390_display\fR displays one or two 8-byte messages on the display +unit of a zSeries tape device. Characters to be displayed include capital +letters A-Z, numerics 0-9 and special characters. +However some special characters may not display on all device types. The +characters that should display on model 3490 are: +.br + '@$#,./()*&+-=%|:_<>?;' and the character. +.br +Spaces and shell-sensitive characters have to be quoted. + +.SH OPTIONS +.TP 8 +\fB\-h\fR or \fB\-\-help\fR +Print help text. + +.TP +\fB\-b\fR or \fB\-\-blink\fR +Display \fImessage1\fR repeatedly for 2 seconds at half-second intervals. +This option is ignored if two messages are specified on command line. + +.TP +\fB\-l\fR or \fB\-\-load\fR +If this option is given the display request will cause a load request to be +sent to the automatic tape loader (if installed). The next indexed tape will +then be loaded if the loader is in 'system' mode. Otherwise the request is +ignored. + +.TP +\fB\-q\fR or \fB\-\-quiet\fR +This option causes the command to run in quiet mode which will suppress +all warning messages. + +.TP +\fB\-t\fR \fItype\fR or \fB\-\-type\fR \fItype\fR +Set the type of the message to be displayed. This influences how long +the message(s) stay on the display. Available types are: + +.RS +.TP 16 +.B standard +The message(s) stay displayed until the drive executes the next tape +movement command. This is the default. +.TP +.B load +Displays the message(s) until a tape is loaded. If there already is +a tape loaded to the device, nothing is displayed at all. +.TP +.B unload +This will display the message(s) until currently loaded tape is unloaded. +Again, if the drive \fIis\fR currently unloaded there is no output at all. +.TP +.B noop +For testing purposes only, this will access the drive but won't do anything +at all (no operation). This doesn't seem to work with 3590 tape devices. +.TP +.B reload +This display type \fBrequires\fR two messages. The first message is +displayed as long as there is a tape loaded up to the time the unload +operation is completed (or, if there isn't a tape loaded, it will be +ignored). The second message is displayed as soon as a tape gets loaded +into the drive. +.RE + +.TP +\fB\-v\fR or \fB\-\-version\fR +Print the S/390 tools package version, the utility version and exit. + +.TP 8 +\fBmessage1\fR [ \fBmessage2\fR ] +The message(s) to be displayed. They stay displayed until either the +ending condition for the message type takes place or the display is +updated with another message. +.br +If two messages are specified, they are alternated on the display. +Each message is then displayed repeatedly for 2 seconds +at half-second intervals. + +.TP +\fBdevice\fR +The tape device on which the message(s) will be displayed, e.g.: +.sp + \fB/dev/ntibm\fR\fI0\fR +.br + +.SH EXAMPLES +\fBtape390_display \-\-blink \-\-type unload "TESTING" /dev/ntibm\fR\fI0\fR +.RS +Let the message TESTING blink on the display of the tape unit until the +tape is removed (or another message is given). If there is no tape in the +drive, nothing happens. +.RE +\fBtape390_display "BACKUP" "COMPLETE" /dev/ntibm\fR\fI0\fR +.RS +This will display the messages BACKUP and COMPLETE each for 2 seconds +until the tape is moved, ejected or (if there isn't a tape in the +device) inserted. +.RE +\fBtape390_display \-\-type reload "UNLOAD" "LOAD" /dev/ntibm\fR\fI0\fR +.RS +The UNLOAD message will be displayed if there is a tape inserted and as +long as the tape isn't removed. After that the LOAD message is displayed +until a tape is inserted. If there is no tape inserted when the command +.RE +\fBtape390_display \-\-load \-\-type load "LOADING" /dev/ntibm\fR\fI0\fR +.RS +Causes the device to display the message LOADING until a tape is inserted +and to instruct the automatic stacker to load the next tape (if the mode +of operation is \fIsystem\fR). If there already is a tape loaded, nothing +will happen at all. +.RE + +.SH AUTHOR +.nf +This man-page was written by Despina Papadopoulou +.nf +Updates by Stefan Bader +.fi + diff --git a/tape390/tape390_display.c b/tape390/tape390_display.c new file mode 100644 index 00000000..a77b6dba --- /dev/null +++ b/tape390/tape390_display.c @@ -0,0 +1,287 @@ +/* + * tape390_display - Display messages on the display unit of a tape drive + * + * Copyright IBM Corp. 2002, 2017 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lib/zt_common.h" +#include "tape390_common.h" + +#define TAPE390_DISPLAY _IOW('d', 1, struct display_struct) + +/* + * The TAPE390_DISPLAY belongs to linux-2.5/include/asm-s390/tape390.h + * + * The TAPE390_DISPLAY ioctl calls the Load Display command + * which transfers 17 bytes of data from the channel to the subsystem: + * - 1 format control byte, and + * - two 8-byte messages + * + * Format control byte: + * 0-2: New Message Overlay + * 3: Alternate Messages + * 4: Blink Message + * 5: Display Low/High Message + * 6: Reserved + * 7: Automatic Load Request + * + */ +#define TAPE_MSGTYPE_STANDARD 0 +#define TAPE_MSGTYPE_UNLOAD 1 +#define TAPE_MSGTYPE_LOAD 2 +#define TAPE_MSGTYPE_NOOP 3 +#define TAPE_MSGTYPE_UNLOAD_LOAD 7 + +typedef struct display_struct { + struct { + unsigned char type :3; + unsigned char alternate :1; + unsigned char blink :1; + unsigned char use_second :1; + unsigned char reserved :1; + unsigned char load_request :1; + } cntrl; + char message1[8]; + char message2[8]; +} __attribute__ ((packed)) display_struct; + +/* definitions */ +static char *help_text = + "tape390_display: display one or two 8-byte messages on the display" + "unit of a tape drive\n" + "\n" + "Usage: tape390_display \"\" [\"\"] " + "" + "\n\n" + "where are:\n" + "\t-b | --blink\n" + "\t\twill cause a single message to blink every 2 seconds\n" + "\t\t(this option has only effect with a single message)\n" + "\t-h | --help\n" + "\t\tprint this text\n" + "\t-v | --version\n" + "\t\toutput version information and exit\n" + "\t-l | --load\n" + "\t\twill try to load the next tape if the loader is in system mode\n" + "\t-q | --quiet\n" + "\t\twill suppress all warning messages\n" + "\t-t | --type \"standard|load|unload|noop|reload\"\n" + "\t\tcontrols how the message is displayed:\n" + "\t\t\tstandard = until the next tape movement (default)\n" + "\t\t\tload = until a tape is loaded\n" + "\t\t\tunload = until a tape is unloaded\n" + "\t\t\tnoop = not at all (test purposes)\n" + "\t\t\treload = message1 until tape is unloaded and message2\n" + "\t\t\t when the next tape is loaded\n" + "\n" + "Note: Characters to be displayed include capital letters (A-Z), " + "numerics (0-9)\n" + "and special characters within single quotes. However not all " + "characters\n" + "might be displayed on all tape devices. Special characters that " + "are supported\n" + "for 3490 are '@$#,./()*&+-=%|:_<>?;'.\n"; + +static int quiet; + +/* end of definitions */ + +static int typename2int(char *name) +{ + char *typenames[] = { + "standard", + "unload", + "load", + "noop", + NULL, + NULL, + NULL, + "reload" + }; + int i; + + for(i = 0; i < 8; i++) { + if(typenames[i] == NULL) + continue; + if(strcmp(typenames[i], name) == 0) { + return i; + } + } + + fprintf(stderr, "Invalid message type <%s>\n", name); + return -1; +} + +static void strchkcpy(char *tgt, const char *src) +{ + static char *nowarn = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + "@$#,./'()*&+-=%:_<>?; "; + char c; + char warned; + + warned = 0; + for(c=0; c<8 && *(src+c); c++) { + *(tgt+c) = toupper(*(src+c)); + if (index(nowarn, *(tgt+c)) == NULL && !quiet && !warned) { + fprintf( + stderr, + "WARNING: Some special characters may not " + "display on all device types.\n" + ); + warned = 1; + } + } +} + +int main(int argc, char *argv[]) { + + char *options = "hblqt:v"; + struct option long_options[] = { + { "help", no_argument, NULL, 'h' }, + { "blink", no_argument, NULL, 'b' }, + { "load", no_argument, NULL, 'l' }, + { "quiet", no_argument, NULL, 'q' }, + { "type", required_argument, NULL, 't' }, + { "version", no_argument, NULL, 'v' }, + { NULL, 0, NULL, 0 } + }; + char *pathname; + int c; + int fd, ret = 0; + struct display_struct ds; + + /* set name of program */ + set_prog_name(basename(argv[0])); + + memset(&ds, 0, sizeof(ds)); + ds.cntrl.type = TAPE_MSGTYPE_STANDARD; + + while( + (c = getopt_long(argc, argv, options, long_options, NULL)) + != EOF + ) { + switch(c) { + case 'h': + fprintf(stdout, "%s", help_text); + exit(0); + case 'b': + ds.cntrl.blink = 1; + break; + case 'l': + ds.cntrl.load_request = 1; + break; + case 'q': + quiet = 1; + break; + case 't': { + int type = typename2int(optarg); + + if(type < 0) + exit(1); + + ds.cntrl.type = type; + break; + } + case 'v': { + printf("tape390_display: zSeries tape " + "display control program version %s\n", + RELEASE_STRING); + printf("Copyright IBM Corp. 2002, 2017\n"); + exit(0); + } + default: + fprintf(stderr, "Try '%s --help' for more" + " information.\n",prog_name); + exit(1); + } + } + + if(optind + 2 < argc ) { + if( + ds.cntrl.type == TAPE_MSGTYPE_LOAD && + ds.cntrl.load_request + ) { + if(!quiet) { + fprintf( + stderr, + "WARNING: " + "A message with the " + "--load option will only " + "display the first" + "\nmessage.\n" + ); + } + } else if( + ds.cntrl.type == TAPE_MSGTYPE_UNLOAD_LOAD && + ds.cntrl.load_request + ) { + if(!quiet) { + fprintf( + stderr, + "WARNING: " + "A message with the " + "--load option will only " + "display the second" + "\nmessage.\n" + ); + } + } else { + ds.cntrl.alternate = 1; + + if(ds.cntrl.blink) { + ds.cntrl.blink = 0; + if(!quiet) { + fprintf( + stderr, + "Alternate messages " + "override blinking\n" + ); + } + } + } + strchkcpy(ds.message1, argv[optind]); + strchkcpy(ds.message2, argv[optind+1]); + pathname = argv[optind+2]; + } else if(optind + 1 < argc) { + if(ds.cntrl.type == TAPE_MSGTYPE_UNLOAD_LOAD) { + fprintf(stderr, "Reload message type requires " + "two messages.\n"); + exit(1); + } else if(ds.cntrl.type == TAPE_MSGTYPE_LOAD) { + strchkcpy(ds.message2, argv[optind]); + } + strchkcpy(ds.message1, argv[optind]); + pathname = argv[optind+1]; + } else { + fprintf(stderr, "%s", help_text); + exit(1); + } + + /* check whether specified device node is tape */ + if (is_not_tape(pathname)) + exit(EXIT_MISUSE); + + /* open device */ + fd = open_tape(pathname); + + ret = ioctl(fd, TAPE390_DISPLAY, &ds); + if (ret != 0) + fprintf(stderr, "TAPE390_DISPLAY failed\n"); + + close(fd); + + return ret; +} + -- 2.54.0