diff -Nru s390-tools-2.25.0/ap_tools/Makefile s390-tools-2.26.0/ap_tools/Makefile --- s390-tools-2.25.0/ap_tools/Makefile 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/ap_tools/Makefile 2023-02-14 14:05:17.000000000 +0100 @@ -26,9 +26,9 @@ install: all @if [ ! -d $(DESTDIR)$(MDEVCTL_CALLOUTS) ]; then \ mkdir -p $(DESTDIR)$(MDEVCTL_CALLOUTS); \ - chown $(OWNER).$(GROUP) $(DESTDIR)$(MDEVCTL_DIR); \ - chown $(OWNER).$(GROUP) $(DESTDIR)$(MDEVCTL_SCRIPTS); \ - chown $(OWNER).$(GROUP) $(DESTDIR)$(MDEVCTL_CALLOUTS); \ + chown $(OWNER):$(GROUP) $(DESTDIR)$(MDEVCTL_DIR); \ + chown $(OWNER):$(GROUP) $(DESTDIR)$(MDEVCTL_SCRIPTS); \ + chown $(OWNER):$(GROUP) $(DESTDIR)$(MDEVCTL_CALLOUTS); \ chmod 755 $(DESTDIR)$(MDEVCTL_DIR); \ chmod 755 $(DESTDIR)$(MDEVCTL_SCRIPTS); \ chmod 755 $(DESTDIR)$(MDEVCTL_CALLOUTS); \ @@ -37,9 +37,9 @@ $(DESTDIR)$(MDEVCTL_CALLOUTS) @if [ ! -d $(DESTDIR)$(MDEVCTL_DEP_CALLOUTS) ]; then \ mkdir -p $(DESTDIR)$(MDEVCTL_DEP_CALLOUTS); \ - chown $(OWNER).$(GROUP) $(DESTDIR)$(MDEVCTL_DEP_DIR); \ - chown $(OWNER).$(GROUP) $(DESTDIR)$(MDEVCTL_DEP_SCRIPTS); \ - chown $(OWNER).$(GROUP) $(DESTDIR)$(MDEVCTL_DEP_CALLOUTS); \ + chown $(OWNER):$(GROUP) $(DESTDIR)$(MDEVCTL_DEP_DIR); \ + chown $(OWNER):$(GROUP) $(DESTDIR)$(MDEVCTL_DEP_SCRIPTS); \ + chown $(OWNER):$(GROUP) $(DESTDIR)$(MDEVCTL_DEP_CALLOUTS); \ chmod 755 $(DESTDIR)$(MDEVCTL_DEP_DIR); \ chmod 755 $(DESTDIR)$(MDEVCTL_DEP_SCRIPTS); \ chmod 755 $(DESTDIR)$(MDEVCTL_DEP_CALLOUTS); \ diff -Nru s390-tools-2.25.0/AUTHORS.md s390-tools-2.26.0/AUTHORS.md --- s390-tools-2.25.0/AUTHORS.md 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/AUTHORS.md 2023-02-14 14:05:17.000000000 +0100 @@ -90,6 +90,7 @@ - Mikhail Zaslonko - Nikita Dubrovskii - Niklas Schnelle +- Nikolay Gueorguiev - Peter Oberparleiter - Peter Tiedemann - Philipp Kern diff -Nru s390-tools-2.25.0/CHANGELOG.md s390-tools-2.26.0/CHANGELOG.md --- s390-tools-2.25.0/CHANGELOG.md 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/CHANGELOG.md 2023-02-14 14:05:17.000000000 +0100 @@ -1,6 +1,25 @@ Release history for s390-tools (MIT version) -------------------------------------------- +* __v2.26.0 (2023-02-14)__ + + For Linux kernel version: 6.2 + + Remove tools / libraries: + - Remove vmconvert and libvmdump in favor of vmdump file support in zdump + + Changes of existing tools: + - ipl_tools: Add support for list-directed IPL from ECKD DASD + - lszcrypt: Display hardware filtering support capability + - vmur: Remove option -c for dump file conversion (See zdump changes) + - zdev: Add zfcp ber_stop parameter handling + - zdump: Add vmdump dfi for vmdump format to elf format + - zkey: Support EP11 host library version + + Bug Fixes: + - zipl: Move dump parmline processing and verification + - zipl/genprotimg: Various build improvements + * __v2.25.0 (2022-12-08)__ For Linux kernel version: 6.1 diff -Nru s390-tools-2.25.0/common.mak s390-tools-2.26.0/common.mak --- s390-tools-2.25.0/common.mak 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/common.mak 2023-02-14 14:05:17.000000000 +0100 @@ -5,7 +5,7 @@ # The variable "DISTRELEASE" should be overwritten in rpm spec files with: # "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}" VERSION = 2 -RELEASE = 25 +RELEASE = 26 PATCHLEVEL = 0 DISTRELEASE = build-$(shell date +%Y%m%d) S390_TOOLS_RELEASE = $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE) @@ -146,6 +146,20 @@ fi # +# Test for linker option +# +# $1: Linker option +# +# Returns the linker option if available and nothing otherwise +# +define test_linker_flag +$(shell printf "int main(void) {return 0;}\n" | \ + ( $(CC) "-Wl,$1" -o /dev/null -x c - ) >/dev/null 2>&1 && printf -- '-Wl,%s' "$1") +endef + +NO_WARN_RWX_SEGMENTS_LDFLAGS := $(call test_linker_flag,"--no-warn-rwx-segments") + +# # Support alternate install root # # INSTALLDIR: Finally install s390-tools to INSTALLDIR. This can be used @@ -261,7 +275,7 @@ NO_PIE_LDFLAGS := endif -# Overwrite implicite makefile rules for having nice compile output +# Overwrite implicit makefile rules for having nice compile output %.o: %.c ifeq ("${C}","1") $(CHECKTOOL) $(ALL_CPPFLAGS) $(ALL_CFLAGS) -c $< -o $@ @@ -364,10 +378,6 @@ $(MAKE) -C $(rootdir)/libzds/ libzds.a .PHONY: $(rootdir)/libzds -$(rootdir)/libvmdump/libvmdump.a: $(rootdir)/libvmdump - $(MAKE) -C $(rootdir)/libvmdump/ libvmdump.a -.PHONY: $(rootdir)/libvmdump - $(rootdir)/libvmcp/libvmcp.a: $(rootdir)/libvmcp $(MAKE) -C $(rootdir)/libvmcp/ libvmcp.a .PHONY: $(rootdir)/libvmcp @@ -396,8 +406,8 @@ $(MAKE) -C $(rootdir)/libpv libpv.a .PHONY: $(rootdir)/libpv -$(rootdir)/zipl/boot/data.o: - $(MAKE) -C $(rootdir)/zipl/boot/ data.o +$(rootdir)/zipl/boot/.loaders: + $(MAKE) -C $(rootdir)/zipl/boot/ .loaders install_dirs: for dir in $(INSTDIRS); do \ @@ -422,4 +432,3 @@ clean: clean_echo clean_gcov clean_dep endif - diff -Nru s390-tools-2.25.0/debian/changelog s390-tools-2.26.0/debian/changelog --- s390-tools-2.25.0/debian/changelog 2023-01-26 12:22:05.000000000 +0100 +++ s390-tools-2.26.0/debian/changelog 2023-02-14 15:00:53.000000000 +0100 @@ -1,3 +1,12 @@ +s390-tools (2.26.0-0ubuntu1) lunar; urgency=medium + + * New upstream release, requested in LP: #2003284 + but also solves: LP: #1983221, LP: #1982339, LP: #2003281, LP: #2003286 + LP: #2003393, LP: #2003633, LP: #2003672, LP: #2003676, LP: #2003680 + * Update 'Standards-Version' field in d/control to latest version 4.6.1.0. + + -- Frank Heimes Tue, 14 Feb 2023 15:00:53 +0100 + s390-tools (2.25.0-0ubuntu1) lunar; urgency=medium * New upstream release, requested in LP: #2006451 diff -Nru s390-tools-2.25.0/debian/control s390-tools-2.26.0/debian/control --- s390-tools-2.25.0/debian/control 2023-01-26 12:22:05.000000000 +0100 +++ s390-tools-2.26.0/debian/control 2023-02-14 15:00:53.000000000 +0100 @@ -5,7 +5,7 @@ XSBC-Original-Maintainer: Debian S/390 Team Uploaders: Bastian Blank Build-Depends: debhelper-compat (= 13), libz-dev, quilt, gcc-multilib, libfuse3-dev, libncurses-dev, libpfm4-dev, libssl-dev, libcurl4-openssl-dev, libcryptsetup-dev, libjson-c-dev, libsnmp-dev, libglib2.0-dev, libxml2-dev, bsdextrautils, liblockfile-dev, dh-exec -Standards-Version: 3.9.7 +Standards-Version: 4.6.1.0 Homepage: https://github.com/ibm-s390-linux/s390-tools Package: s390-tools diff -Nru s390-tools-2.25.0/genprotimg/boot/head.S s390-tools-2.26.0/genprotimg/boot/head.S --- s390-tools-2.25.0/genprotimg/boot/head.S 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/genprotimg/boot/head.S 2023-02-14 14:05:17.000000000 +0100 @@ -29,3 +29,8 @@ brasl %r14, initialize .Lstack: .long STACK_ADDRESS + STACK_SIZE - STACK_FRAME_OVERHEAD .previous + +/* The code doesn't require an executable stack */ +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff -Nru s390-tools-2.25.0/genprotimg/boot/Makefile s390-tools-2.26.0/genprotimg/boot/Makefile --- s390-tools-2.25.0/genprotimg/boot/Makefile 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/genprotimg/boot/Makefile 2023-02-14 14:05:17.000000000 +0100 @@ -69,27 +69,15 @@ stage3b_reloc.o: stage3b.bin -stage3a.elf: head.o stage3a_init.o stage3a.o stage3a.lds $(ZIPL_OBJS) -stage3b.elf: head.o stage3b.o stage3b.lds $(ZIPL_OBJS) -stage3b_reloc.elf: +stage3a.elf: head.o stage3a_init.o $(ZIPL_OBJS) +stage3b.elf: head.o $(ZIPL_OBJS) -%.elf: %.o - case $* in \ - stage3a) SFLAGS="-Wl,-T,stage3a.lds";; \ - stage3b) SFLAGS="-Wl,-T,stage3b.lds";; \ - stage3b_reloc) SFLAGS="-Wl,-estage3b_reloc_start,-Ttext,0";; \ - esac; \ - $(LINK) $$SFLAGS $(NO_PIE_LDFLAGS) -m64 -static -nostdlib $(filter %.o, $^) -o $@ +%.elf: %.lds %.o + $(LINK) $(NO_PIE_LDFLAGS) $(NO_WARN_RWX_SEGMENTS_LDFLAGS) -Wl,-T,$< -Wl,--build-id=none -m64 -static -nostdlib $(filter %.o, $^) -o $@ @chmod a-x $@ -%.bin: %.elf - $(OBJCOPY) -O binary \ - --only-section=.text* \ - --only-section=.ex_table* \ - --only-section=.fixup* \ - --only-section=.data* \ - --only-section=.rodata* \ - $< $@ +%.bin: %.elf + $(OBJCOPY) -O binary $< $@ @chmod a-x $@ clean: diff -Nru s390-tools-2.25.0/genprotimg/boot/stage3a_init.S s390-tools-2.26.0/genprotimg/boot/stage3a_init.S --- s390-tools-2.25.0/genprotimg/boot/stage3a_init.S 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/genprotimg/boot/stage3a_init.S 2023-02-14 14:05:17.000000000 +0100 @@ -26,3 +26,8 @@ br %r1 .Lstage3a_entry: .long STAGE3A_ENTRY .previous + +/* The code doesn't require an executable stack */ +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff -Nru s390-tools-2.25.0/genprotimg/boot/stage3a.lds.S s390-tools-2.26.0/genprotimg/boot/stage3a.lds.S --- s390-tools-2.25.0/genprotimg/boot/stage3a.lds.S 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/genprotimg/boot/stage3a.lds.S 2023-02-14 14:05:17.000000000 +0100 @@ -23,8 +23,6 @@ SECTIONS { - . = 0x0; - . = HEAP_ADDRESS; __heap_start = .; .heap : { @@ -46,7 +44,7 @@ . = STAGE3A_INIT_ENTRY; __text_init_start = .; .text : { - stage3a_init.o(.text.init) + *(.text.init) __text_init_stop = ABSOLUTE(.); /* Text size of text_init must be smaller than 'PARMAREA - IMAGE_ENTRY', * otherwise the text data could be overwritten by the original zipl stage3 @@ -56,8 +54,8 @@ . = 0x1000; ASSERT(ABSOLUTE(.) == STAGE3A_ENTRY, "Text section doesn't conform to the described memory layout"); - head.o(.text.start) - *(.text) + *(.text.start) + *(.text .text.*) } .ex_table ALIGN(16) : { @@ -74,7 +72,7 @@ .rodata ALIGN(16) : { *(.rodata) - *(.rodata.*) + *(.rodata*) } .data ALIGN(16) : { @@ -90,14 +88,10 @@ ASSERT(ABSOLUTE(.) < 0x13000, "Data section doesn't conform to the described memory layout"); } - /* List this explicitly as otherwise .note.gnu.build-id will be - * put at 0x0 */ - .notes : { - *(.note.*) - } - /* Sections to be discarded */ /DISCARD/ : { *(.eh_frame) + *(.interp) + *(.note.GNU-stack) } } diff -Nru s390-tools-2.25.0/genprotimg/boot/stage3b.lds.S s390-tools-2.26.0/genprotimg/boot/stage3b.lds.S --- s390-tools-2.25.0/genprotimg/boot/stage3b.lds.S 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/genprotimg/boot/stage3b.lds.S 2023-02-14 14:05:17.000000000 +0100 @@ -21,8 +21,6 @@ SECTIONS { - . = 0x0; - . = HEAP_ADDRESS; __heap_start = .; .heap : { @@ -34,8 +32,8 @@ . = STAGE3B_ENTRY; .text : { - head.o(.text.start) - *(.text) + *(.text.start) + *(.text .text.*) } .ex_table ALIGN(16) : { @@ -52,7 +50,7 @@ .rodata ALIGN(16) : { *(.rodata) - *(.rodata.*) + *(.rodata*) } .data ALIGN(16) : { @@ -74,14 +72,12 @@ } __stack_end = .; - /* List this explicitly as otherwise .note.gnu.build-id will be - * put at 0x0 */ - .notes : { - *(.note.*) - } + ASSERT(. <= IMAGE_ENTRY, "stage3b size must be smaller than 0x10000 bytes") /* Sections to be discarded */ /DISCARD/ : { *(.eh_frame) + *(.interp) + *(.note.GNU-stack) } } diff -Nru s390-tools-2.25.0/genprotimg/boot/stage3b_reloc.lds.S s390-tools-2.26.0/genprotimg/boot/stage3b_reloc.lds.S --- s390-tools-2.25.0/genprotimg/boot/stage3b_reloc.lds.S 1970-01-01 01:00:00.000000000 +0100 +++ s390-tools-2.26.0/genprotimg/boot/stage3b_reloc.lds.S 2023-02-14 14:05:17.000000000 +0100 @@ -0,0 +1,19 @@ +OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390") +OUTPUT_ARCH(s390:64-bit) + +ENTRY(_start) + +SECTIONS +{ + .text : { + *(.text.start) + *(.text .text.*) + } + + /* Sections to be discarded */ + /DISCARD/ : { + *(.eh_frame) + *(.interp) + *(.note.GNU-stack) + } +} diff -Nru s390-tools-2.25.0/genprotimg/boot/stage3b_reloc.S s390-tools-2.26.0/genprotimg/boot/stage3b_reloc.S --- s390-tools-2.25.0/genprotimg/boot/stage3b_reloc.S 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/genprotimg/boot/stage3b_reloc.S 2023-02-14 14:05:17.000000000 +0100 @@ -22,8 +22,8 @@ .org 0x0 .section .text.start -.globl stage3b_reloc_start -stage3b_reloc_start: +.globl _start +_start: /* Might be called after a diag308 so better set * architecture and addressing mode */ @@ -53,3 +53,8 @@ .incbin "stage3b.bin" stage3b_end: .previous + +/* The code doesn't require an executable stack */ +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff -Nru s390-tools-2.25.0/.gitignore s390-tools-2.26.0/.gitignore --- s390-tools-2.25.0/.gitignore 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/.gitignore 2023-02-14 14:05:17.000000000 +0100 @@ -78,7 +78,6 @@ tape390/tape390_crypt tape390/tape390_display tunedasd/src/tunedasd -vmconvert/vmconvert vmcp/vmcp vmur/vmur zconf/chp/chchp @@ -110,6 +109,7 @@ ziomon/ziorep_utilization zipl/boot/*.bin zipl/boot/*.exec +zipl/boot/.loaders zipl/boot/data.h zipl/src/chreipl_helper.device-mapper zdev/src/zdev_id diff -Nru s390-tools-2.25.0/hmcdrvfs/Makefile s390-tools-2.26.0/hmcdrvfs/Makefile --- s390-tools-2.25.0/hmcdrvfs/Makefile 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/hmcdrvfs/Makefile 2023-02-14 14:05:17.000000000 +0100 @@ -52,7 +52,7 @@ cat $$i | \ sed -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' \ >$(DESTDIR)$(USRSBINDIR)/$$i; \ - chown $(OWNER).$(GROUP) $(DESTDIR)$(USRSBINDIR)/$$i; \ + chown $(OWNER):$(GROUP) $(DESTDIR)$(USRSBINDIR)/$$i; \ chmod 755 $(DESTDIR)$(USRSBINDIR)/$$i; \ done diff -Nru s390-tools-2.25.0/hsci/Makefile s390-tools-2.26.0/hsci/Makefile --- s390-tools-2.25.0/hsci/Makefile 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/hsci/Makefile 2023-02-14 14:05:17.000000000 +0100 @@ -5,7 +5,7 @@ install: hsci $(SED) -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' \ < hsci >$(DESTDIR)$(BINDIR)/hsci; \ - chown $(OWNER).$(GROUP) $(DESTDIR)$(BINDIR)/hsci; \ + chown $(OWNER):$(GROUP) $(DESTDIR)$(BINDIR)/hsci; \ chmod 755 $(DESTDIR)$(BINDIR)/hsci; \ $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 hsci.8 \ diff -Nru s390-tools-2.25.0/include/boot/loaders_layout.h s390-tools-2.26.0/include/boot/loaders_layout.h --- s390-tools-2.25.0/include/boot/loaders_layout.h 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/include/boot/loaders_layout.h 2023-02-14 14:05:17.000000000 +0100 @@ -14,12 +14,18 @@ #include "lib/zt_common.h" #include "linux_layout.h" +#define STAGE0_LOAD_ADDRESS _AC(0x0, UL) + +#define STAGE1_LOAD_ADDRESS _AC(0x18, UL) +#define STAGE1B_LOAD_ADDRESS _AC(0xe000, UL) + #define STAGE2_DESC _AC(0x78, UL) #define STAGE2_ENTRY _AC(0x2018, UL) #define STAGE2_HEAP_ADDRESS _AC(0x6000, UL) #define STAGE2_HEAP_SIZE _AC(0x3000, UL) #define STAGE2_STACK_ADDRESS _AC(0xe400, UL) #define STAGE2_STACK_SIZE _AC(0x1c00, UL) +#define STAGE2_MAX_SIZE _AC(0x3000, UL) #define STAGE3_ENTRY _AC(0xa000, UL) diff -Nru s390-tools-2.25.0/include/libpv/glib-helper.h s390-tools-2.26.0/include/libpv/glib-helper.h --- s390-tools-2.25.0/include/libpv/glib-helper.h 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/include/libpv/glib-helper.h 2023-02-14 14:05:17.000000000 +0100 @@ -18,6 +18,12 @@ #define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_56 #endif +#define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_56 + +#ifdef __G_LIB_H__ +#error "glib.h must be included via libpv/glib-helper.h" +#endif + #include #include #include diff -Nru s390-tools-2.25.0/ipl_tools/cmd_chreipl.c s390-tools-2.26.0/ipl_tools/cmd_chreipl.c --- s390-tools-2.25.0/ipl_tools/cmd_chreipl.c 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/ipl_tools/cmd_chreipl.c 2023-02-14 14:05:17.000000000 +0100 @@ -23,12 +23,15 @@ #define BOOTPARMS_CCW_MAX 64 #define BOOTPARMS_FCP_MAX 3452 +#define OPT_BRCHR 0x80 + enum target_type { TT_CCW, TT_FCP, TT_NSS, TT_NODE, TT_NVME, + TT_ECKD, }; enum reipl_type { @@ -36,12 +39,14 @@ REIPL_CCW, REIPL_NSS, REIPL_NVME, + REIPL_ECKD, }; static const char *const usage_chreipl = "Usage: %s [TARGET] [ARGS] [OPTIONS]\n" "\n" " chreipl [ccw] [-d] [OPTIONS]\n" +" chreipl [eckd] [-d] [OPTIONS]\n" " chreipl [fcp] [-d] [-w] [-l] [OPTIONS]\n" " chreipl nvme [-i] [-s] [OPTIONS]\n" " chreipl [node] [OPTIONS]\n" @@ -50,6 +55,7 @@ "\n" "The following re-IPL targets are supported:\n" " ccw IPL from CCW device\n" +" eckd IPL from ECKD device\n" " fcp IPL from FCP device\n" " nvme IPL from NVME device\n" " nss IPL from NSS\n" @@ -66,6 +72,13 @@ " -L, --loadparm Loadparm specification\n" " -c, --clear 0|1 Control if memory is cleared on re-IPL\n" "\n" +"Options for eckd target:\n" +" -d, --device Device number of the ECKD IPL device\n" +" -b, --bootprog Bootprog specification\n" +" --brchr Boot record location in Cylinder,Head,Record format\n" +" -L, --loadparm Loadparm specification\n" +" -c, --clear 0|1 Control if memory is cleared on re-IPL\n" +"\n" "Options for fcp target:\n" " -d, --device Device number of the adapter of the FCP IPL device\n" " -l --lun Logical unit number of the FCP IPL device\n" @@ -114,6 +127,8 @@ int target_type_auto_mode; enum reipl_type reipl_type; /* CCW, FCP, NVME, NSS */ int reipl_clear; + char *brchr; + int brchr_set; } l; static void __noreturn print_usage_chreipl_exit(void) @@ -329,6 +344,19 @@ set_device(nargv[0]); } +static void parse_eckd_args(char *nargv[], int nargc) +{ + /* + * we might be called like this: + * chreipl eckd 4711 + */ + if (l.busid_set) + ERR_EXIT("Use either options or positional parameters"); + if (nargc > 1) + ERR_EXIT("Too many arguments specified for \"eckd\" re-IPL type"); + set_device(nargv[0]); +} + static void parse_nss_args(char *nargv[], int nargc) { /* @@ -369,6 +397,8 @@ l.reipl_type = REIPL_FCP; else if (strncmp(dev_name, "nvme", strlen("nvme")) == 0) l.reipl_type = REIPL_NVME; + else if (strncmp(dev_name, "eckd", strlen("eckd")) == 0) + l.reipl_type = REIPL_ECKD; else return -1; @@ -493,12 +523,15 @@ case TT_NODE: parse_node_args(nargv, nargc); break; + case TT_ECKD: + parse_eckd_args(nargv, nargc); + break; } } static void check_fcp_opts(void) { - if (l.nss_name_set) + if (l.nss_name_set || l.brchr_set) ERR_EXIT("Invalid option for \"fcp\" target specified"); if (!(l.busid_set && l.wwpn_set && l.lun_set)) ERR_EXIT("The \"fcp\" target requires device, WWPN, " @@ -507,7 +540,8 @@ static void check_nvme_opts(void) { - if (l.nss_name_set || l.wwpn_set || l.lun_set || l.busid_set) + if (l.nss_name_set || l.wwpn_set || l.lun_set || l.busid_set || + l.brchr_set) ERR_EXIT("Invalid option for \"nvme\" target specified"); if (!(l.fid_set && l.nsid_set)) ERR_EXIT("The \"nvme\" target requires FID, and optional NSID"); @@ -515,16 +549,25 @@ static void check_ccw_opts(void) { - if (l.bootprog_set || l.lun_set || l.wwpn_set || l.nss_name_set) + if (l.bootprog_set || l.lun_set || l.wwpn_set || l.nss_name_set || + l.brchr_set) ERR_EXIT("Invalid option for \"ccw\" target specified"); if (!l.busid_set) ERR_EXIT("The \"ccw\" target requires device"); } +static void check_eckd_opts(void) +{ + if (l.nss_name_set || l.wwpn_set || l.lun_set || l.fid_set) + ERR_EXIT("Invalid option for \"eckd\" target specified"); + if (!(l.busid_set)) + ERR_EXIT("The \"eckd\" target requires device"); +} + static void check_nss_opts(void) { if (l.bootprog_set || l.loadparm_set || l.busid_set || l.wwpn_set || - l.lun_set) + l.lun_set || l.brchr_set) ERR_EXIT("Invalid option for \"nss\" target specified"); if (!l.nss_name_set) ERR_EXIT("The \"nss\" target requires NSS name"); @@ -563,6 +606,12 @@ ERR_EXIT("re-IPL clear argument must be either 1 or 0"); } +static void set_brchr(const char *arg) +{ + l.brchr = strdup(arg); + l.brchr_set = 1; +} + static void parse_chreipl_options(int argc, char *argv[]) { int opt, idx; @@ -580,6 +629,7 @@ { "force", no_argument, NULL, 'f' }, { "version", no_argument, NULL, 'v' }, { "clear", required_argument, NULL, 'c' }, + { "brchr", required_argument, NULL, OPT_BRCHR }, { NULL, 0, NULL, 0 } }; static const char optstr[] = "hd:vw:l:fL:b:n:p:c:i:s:"; @@ -592,6 +642,8 @@ set_target_type(TT_FCP, 0); else if (strcmp(argv[1], "ccw") == 0) set_target_type(TT_CCW, 0); + else if (strcmp(argv[1], "eckd") == 0) + set_target_type(TT_ECKD, 0); else if (strcmp(argv[1], "nss") == 0) set_target_type(TT_NSS, 0); else if (strcmp(argv[1], "nvme") == 0) @@ -640,6 +692,9 @@ case 'c': set_reipl_clear(optarg); break; + case OPT_BRCHR: + set_brchr(optarg); + break; case 'v': print_version_exit(); default: @@ -737,6 +792,34 @@ print_ccw(0); } +static void chreipl_eckd(void) +{ + check_eckd_opts(); + + if (!ccw_is_device(l.busid) && !l.force_set) { + if (is_ignored(l.busid)) + ERR_EXIT("Device is on cio_ignore list, try \"cio_ignore -r %s\"?", + l.busid); + ERR_EXIT("Could not find DASD ECKD device \"%s\"", l.busid); + } + + if (l.reipl_clear >= 0) { + check_exists("reipl/eckd/clear", "ECKD re-IPL clear attribute"); + write_str(l.reipl_clear ? "1" : "0", "reipl/eckd/clear"); + } + + if (!l.brchr_set) + l.brchr = "auto"; + write_str(l.brchr, "reipl/eckd/br_chr"); + if (!l.bootprog_set) + sprintf(l.bootprog, "0"); + write_str(l.bootprog, "reipl/eckd/bootprog"); + write_str(l.busid, "reipl/eckd/device"); + write_str_optional(l.loadparm, "reipl/eckd/loadparm", l.loadparm_set, "loadparm"); + write_str("eckd", "reipl/reipl_type"); + print_eckd(0, "eckd"); +} + static void chreipl_fcp(void) { check_fcp_opts(); @@ -850,6 +933,11 @@ l.busid_set = 1; chreipl_ccw(); break; + case REIPL_ECKD: + ccw_busid_get(l.dev, l.busid); + l.busid_set = 1; + chreipl_eckd(); + break; case REIPL_FCP: fcp_wwpn_get(l.dev, l.wwpn); l.wwpn_set = 1; @@ -878,6 +966,9 @@ case TT_CCW: chreipl_ccw(); break; + case TT_ECKD: + chreipl_eckd(); + break; case TT_FCP: chreipl_fcp(); break; diff -Nru s390-tools-2.25.0/ipl_tools/cmd_lsreipl.c s390-tools-2.26.0/ipl_tools/cmd_lsreipl.c --- s390-tools-2.25.0/ipl_tools/cmd_lsreipl.c 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/ipl_tools/cmd_lsreipl.c 2023-02-14 14:05:17.000000000 +0100 @@ -141,6 +141,30 @@ print_fw_str("clear: %s\n", dir, "clear"); } +void print_eckd(int show_ipl, const char *name) +{ + char *path_loadparm = show_ipl ? "/sys/firmware/ipl/loadparm" : + "/sys/firmware/reipl/eckd/loadparm"; + char *dir = show_ipl ? "ipl" : "reipl/eckd"; + char loadparm[9], loadparm_path[PATH_MAX]; + + printf("%-12s %s\n", get_ipl_banner(show_ipl), name); + + print_fw_str("Device: %s\n", dir, "device"); + print_fw_str("bootprog: %s\n", dir, "bootprog"); + print_fw_str("br_chr: %s\n", dir, "br_chr"); + print_fw_str("Bootparm: \"%s\"\n", dir, "scp_data"); + if (access(path_loadparm, R_OK) == 0) { + sprintf(loadparm_path, "%s/%s", dir, "loadparm"); + read_fw_str(loadparm, loadparm_path, sizeof(loadparm)); + if (strcmp(loadparm, " ") == 0) + loadparm[0] = 0; + printf("Loadparm: \"%s\"\n", loadparm); + } + if (!show_ipl) + print_fw_str("clear: %s\n", dir, "clear"); +} + static void parse_lsreipl_options(int argc, char *argv[]) { int opt, idx; @@ -193,6 +217,9 @@ print_nvme(l.ipl_set, 1); else if (strcmp(reipl_type_str, "ccw") == 0) print_ccw(l.ipl_set); + else if (strcmp(reipl_type_str, "eckd") == 0 || + strcmp(reipl_type_str, "eckd_dump") == 0) + print_eckd(l.ipl_set, reipl_type_str); else if (strcmp(reipl_type_str, "nss") == 0) print_nss(l.ipl_set); else diff -Nru s390-tools-2.25.0/ipl_tools/ipl_tools.h s390-tools-2.26.0/ipl_tools/ipl_tools.h --- s390-tools-2.25.0/ipl_tools/ipl_tools.h 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/ipl_tools/ipl_tools.h 2023-02-14 14:05:17.000000000 +0100 @@ -45,6 +45,7 @@ extern void print_fcp(int show_ipl, int dump); extern void print_nvme(int show_ipl, int dump); extern void print_nss(int show_ipl); +extern void print_eckd(int show_ipl, const char *name); /* * Helper diff -Nru s390-tools-2.25.0/ipl_tools/man/chreipl.8 s390-tools-2.26.0/ipl_tools/man/chreipl.8 --- s390-tools-2.25.0/ipl_tools/man/chreipl.8 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/ipl_tools/man/chreipl.8 2023-02-14 14:05:17.000000000 +0100 @@ -36,6 +36,9 @@ .RB "- " ccw : Specify a DASD CCW device for reboot .TP +.RB "- " eckd : +Specify a DASD ECKD device for reboot +.TP .RB "- " fcp : Specify a FCP device for reboot .TP @@ -175,6 +178,63 @@ variables in the kernel command line: \fB# chreipl ccw -d 0.0.7e78 -L 1S2\fP + +.SH eckd +Use the eckd re-IPL target for DASD devices that are accessed by the hardware +using channel command word (CCW) channels. In contrast to the ccw re-IPL target, +the eckd target uses a list-directed bootloader that supports secure boot. +.TP +.BR "\-d" " or " "\-\-device" +Specifies the device bus-ID of the DASD. If the bus-ID starts with "0.0." this +prefix can be omitted and the four digit short notation can be used (e.g. 5000 is +the same as 0.0.5000). + +.TP +.BR "\-b" " or " "\-\-bootprog" +Specifies an entry in the ECKD boot configuration by defining the IPL boot +program selector. If omitted, '0' is used. + +.TP +.BR "\-\-brchr" +Specifies the block on the DASD where the boot record is located. If omitted, +0 is used. The specification comprises comma-separated values for cylinder, head, +and record: "--brchr C,H,R". Alternatively, specifying "0" or "auto" makes the +bootloader determine the boot record location from the volume label. + +.TP +.BR "\-L" " or " "\-\-loadparm" +The loadparm for the eckd re-IPL target is not used to control the ECKD boot +configuration that is defined by the +.BR zipl (8) +boot menu. Instead it can be used to control higher level boot loaders +like GRUB. For more details refer to distribution specific documentation. + +.TP +.BR "\-c" " or " "\-\-clear" +Controls whether memory is cleared on re-IPL. Possible values are 0 to +disable and 1 to enable memory clearing on re-IPL. +Memory clearing is supported if the "clear" attribute is present in +/sys/firmware/reipl/eckd/. + +.PP +\fBExamples:\fP +.br + +1. Next time reboot from the ECKD device with the bus-ID 0.0.7e78 and boot +program selector 0: + +\fB# chreipl eckd 0.0.7e78\fP + +2. Next time reboot from the ECKD device with the bus-ID 0.0.7e78 and boot +program selector 2: + +\fB# chreipl eckd -d 0.0.7e78 -b 2\fP + +3. Next time reboot from the ECKD device with the bus-ID 0.0.7e78 and tell +the bootloader that the bootrecord is located in Cylinder 3, Head 0, Record 1: + +\fB# chreipl eckd -d 0.0.7e78 --brchr 3,0,1\fP + .SH fcp Use the fcp re-IPL target for SCSI disks that are accessed by the hardware using Fibre Channel Protocol (FCP) channels. diff -Nru s390-tools-2.25.0/ip_watcher/Makefile s390-tools-2.26.0/ip_watcher/Makefile --- s390-tools-2.25.0/ip_watcher/Makefile 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/ip_watcher/Makefile 2023-02-14 14:05:17.000000000 +0100 @@ -12,7 +12,7 @@ install: ip_watcher.pl xcec-bridge start_hsnc.sh $(SED) -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' \ < start_hsnc.sh >$(DESTDIR)$(USRSBINDIR)/start_hsnc.sh; \ - chown $(OWNER).$(GROUP) $(DESTDIR)$(USRSBINDIR)/start_hsnc.sh; \ + chown $(OWNER):$(GROUP) $(DESTDIR)$(USRSBINDIR)/start_hsnc.sh; \ chmod 755 $(DESTDIR)$(USRSBINDIR)/start_hsnc.sh; \ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 ip_watcher.pl \ $(DESTDIR)$(USRSBINDIR) diff -Nru s390-tools-2.25.0/libseckey/sk_provider.c s390-tools-2.26.0/libseckey/sk_provider.c --- s390-tools-2.25.0/libseckey/sk_provider.c 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/libseckey/sk_provider.c 2023-02-14 14:05:17.000000000 +0100 @@ -552,6 +552,33 @@ return provctx->cached_parms[index]; } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +static bool sk_prov_check_uint_param(const OSSL_PARAM params[], + const char *param_name, + const struct sk_prov_key *key, + int key_type, + unsigned int expected_value) +{ + const OSSL_PARAM *p; + unsigned int value; + + if (key == NULL) + return true; + if (key->type != key_type) + return true; + + p = OSSL_PARAM_locate_const(params, param_name); + if (p == NULL) + return true; + + if (OSSL_PARAM_get_uint(p, &value) != 1) + return true; + + return value == expected_value; +} +#pragma GCC diagnostic pop + static struct sk_prov_op_ctx *sk_prov_op_newctx(struct sk_prov_ctx *provctx, const char *propq, int type) @@ -850,6 +877,20 @@ for (p = params; p != NULL && p->key != NULL; p++) sk_debug_op_ctx(ctx, "param: %s", p->key); +#ifdef OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION + /* OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION is for RSA decrypt only */ + if (!sk_prov_check_uint_param(params, + OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION, + ctx->key, EVP_PKEY_RSA, 0) || + !sk_prov_check_uint_param(params, + OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION, + ctx->key, EVP_PKEY_RSA_PSS, 0)) { + put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_PARAM, + "Implicit rejection is not supported"); + return 0; + } +#endif + default_set_params_fn = (OSSL_FUNC_asym_cipher_set_ctx_params_fn *) sk_prov_get_default_asym_func(ctx->provctx, ctx->type, @@ -1125,6 +1166,14 @@ struct sk_prov_op_ctx *ctx = vctx; struct sk_prov_key *key = vkey; const OSSL_PARAM *p; +#ifdef OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION + unsigned int implicit_rejection = 0; + OSSL_PARAM set_params[] = { + OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION, + &implicit_rejection), + OSSL_PARAM_END + }; +#endif if (ctx == NULL || key == NULL) return 0; @@ -1133,6 +1182,20 @@ for (p = params; p != NULL && p->key != NULL; p++) sk_debug_op_ctx(ctx, "param: %s", p->key); +#ifdef OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION + /* OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION is for RSA decrypt only */ + if (!sk_prov_check_uint_param(params, + OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION, + key, EVP_PKEY_RSA, 0) || + !sk_prov_check_uint_param(params, + OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION, + key, EVP_PKEY_RSA_PSS, 0)) { + put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_PARAM, + "Implicit rejection is not supported"); + return 0; + } +#endif + default_decrypt_init_fn = (OSSL_FUNC_asym_cipher_decrypt_init_fn *) sk_prov_get_default_asym_func(ctx->provctx, ctx->type, @@ -1155,6 +1218,26 @@ return 0; } +#ifdef OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION + if ((key->type == EVP_PKEY_RSA || key->type == EVP_PKEY_RSA_PSS) && + key->secure_key != NULL) { + /* + * By default, implicit rejection is enabled for the default + * provider. We currently do not support implicit rejection with + * secure keys, so disable implicit rejection in the default + * provider operation context to report its status correctly + * with get-params. + */ + implicit_rejection = 0; + if (!sk_prov_asym_op_set_ctx_params(ctx, set_params)) { + sk_debug_op_ctx(ctx, + "ERROR: sk_prov_asym_op_set_ctx_params " + "failed"); + return 0; + } + } +#endif + return 1; } @@ -1361,6 +1444,16 @@ for (p = params; p != NULL && p->key != NULL; p++) sk_debug_op_ctx(ctx, "param: %s", p->key); +#ifdef OSSL_SIGNATURE_PARAM_NONCE_TYPE + /* OSSL_SIGNATURE_PARAM_NONCE_TYPE is used for EC sign ops only */ + if (!sk_prov_check_uint_param(params, OSSL_SIGNATURE_PARAM_NONCE_TYPE, + ctx->key, EVP_PKEY_EC, 0)) { + put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_PARAM, + "Deterministic signature is not supported"); + return 0; + } +#endif + default_set_params_fn = (OSSL_FUNC_signature_set_ctx_params_fn *) sk_prov_get_default_sign_func(ctx->provctx, ctx->type, OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS); @@ -1701,6 +1794,11 @@ salt_len = max_saltlen; else if (strcmp(saltlen, OSSL_PKEY_RSA_PSS_SALT_LEN_AUTO) == 0) salt_len = max_saltlen; +#ifdef OSSL_PKEY_RSA_PSS_SALT_LEN_AUTO_DIGEST_MAX + else if (strcmp(saltlen, + OSSL_PKEY_RSA_PSS_SALT_LEN_AUTO_DIGEST_MAX) == 0) + salt_len = MIN(max_saltlen, EVP_MD_size(mgf_md)); +#endif else salt_len = atoi(saltlen); @@ -1729,6 +1827,16 @@ for (p = params; p != NULL && p->key != NULL; p++) sk_debug_op_ctx(ctx, "param: %s", p->key); +#ifdef OSSL_SIGNATURE_PARAM_NONCE_TYPE + /* OSSL_SIGNATURE_PARAM_NONCE_TYPE is used for EC sign ops only */ + if (!sk_prov_check_uint_param(params, OSSL_SIGNATURE_PARAM_NONCE_TYPE, + key, EVP_PKEY_EC, 0)) { + put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_PARAM, + "Deterministic signature is not supported"); + return 0; + } +#endif + default_sign_init_fn = (OSSL_FUNC_signature_sign_init_fn *) sk_prov_get_default_sign_func(ctx->provctx, ctx->type, @@ -1955,6 +2063,16 @@ for (p = params; p != NULL && p->key != NULL; p++) sk_debug_op_ctx(ctx, "param: %s", p->key); +#ifdef OSSL_SIGNATURE_PARAM_NONCE_TYPE + /* OSSL_SIGNATURE_PARAM_NONCE_TYPE is used for EC sign ops only */ + if (!sk_prov_check_uint_param(params, OSSL_SIGNATURE_PARAM_NONCE_TYPE, + key, EVP_PKEY_EC, 0)) { + put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_PARAM, + "Deterministic signature is not supported"); + return 0; + } +#endif + default_digest_sign_init_fn = (OSSL_FUNC_signature_digest_sign_init_fn *) sk_prov_get_default_sign_func(ctx->provctx, diff -Nru s390-tools-2.25.0/libvmdump/dump.cpp s390-tools-2.26.0/libvmdump/dump.cpp --- s390-tools-2.25.0/libvmdump/dump.cpp 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/libvmdump/dump.cpp 1970-01-01 01:00:00.000000000 +0100 @@ -1,65 +0,0 @@ -/* - * vmdump - z/VM dump conversion library - * - * Dump base class - * - * Copyright IBM Corp. 2004, 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 "dump.h" - -int debug = 0; - -void s390TodToTimeval(uint64_t todval, struct timeval *xtime) -{ - /* adjust todclock to 1970 */ - todval -= 0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096); - - todval >>= 12; - xtime->tv_sec = todval / 1000000; - xtime->tv_usec = todval % 1000000; -} - -Dump::Dump(const char *filename, const char *mode) -{ - fh = fopen(filename, mode); - if(!fh){ - throw(DumpErrnoException("Open dump file failed!")); - } -} - -Dump::~Dump(void) -{ - if(fh){ - fclose(fh); - } -} - -void ProgressBar::initProgress(void) -{ - progressPercentage = -1; -} - -void ProgressBar::displayProgress(uint64_t value, uint64_t maxValue) -{ - char progress_bar[51]; - int j; - - 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] = '-'; - progress_bar[50] = 0; - fprintf(stderr, "%6lld of %6lld |%s| %3d%% \r", - (long long) value, (long long) maxValue, - progress_bar, progressPercentage); - } -} diff -Nru s390-tools-2.25.0/libvmdump/dump.h s390-tools-2.26.0/libvmdump/dump.h --- s390-tools-2.25.0/libvmdump/dump.h 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/libvmdump/dump.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,95 +0,0 @@ -/* - * vmdump - z/VM dump conversion library - * - * Dump base class - * - * Copyright IBM Corp. 2004, 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 DUMP_H -#define DUMP_H - -#include -#include -#include -#include -#include - -extern int debug; - -class DumpException -{ -public: - DumpException(void) { - msg[0] = 0; - errorCode = 0; - } - DumpException(const char *m) { - sprintf(msg, "%s", m); - errorCode = 0; - } - - const char *what(void) const { return msg; } - int code(void) const { return errorCode; } -protected: - char msg[2048]; - int errorCode; -}; - -class DumpErrnoException : public DumpException -{ -public: - DumpErrnoException(const char *m) { - sprintf(msg, "%s (%s)", m, strerror(errno)); - errorCode = errno; - } -}; - -class Dump -{ -public: - Dump(const char *filename, const char *mode); - Dump(void) : fh(0) {} - virtual ~Dump(void); - typedef enum {DT_VM32, DT_VM64, DT_VM64_BIG, DT_LKCD32, DT_LKCD64, - DT_UNKNOWN} DumpType; - - virtual void readMem(char *buf, int size) = 0; - virtual int seekMem(uint64_t offset) = 0; - virtual uint64_t getMemSize(void) const = 0; - virtual struct timeval getDumpTime(void) const = 0; -protected: - FILE *fh; -}; - -class ProgressBar -{ -public: - ProgressBar(void) { progressPercentage = -1; } - void initProgress(void); - void displayProgress(uint64_t value, uint64_t maxValue); -private: - int progressPercentage; -}; - -void s390TodToTimeval(uint64_t todval, struct timeval *xtime); -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 */ diff -Nru s390-tools-2.25.0/libvmdump/lkcd_dump.cpp s390-tools-2.26.0/libvmdump/lkcd_dump.cpp --- s390-tools-2.25.0/libvmdump/lkcd_dump.cpp 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/libvmdump/lkcd_dump.cpp 1970-01-01 01:00:00.000000000 +0100 @@ -1,261 +0,0 @@ -/* - * vmdump - z/VM dump conversion library - * - * LKCD dump classes: LKCDDump, LKCDDump32, LKCDDump64 - * - * Copyright IBM Corp. 2004, 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 "lkcd_dump.h" - -LKCDDump::LKCDDump(Dump* dump, const char* arch) { - referenceDump = dump; - dumpHeader.magic_number = DUMP_MAGIC_NUMBER; - dumpHeader.version = DUMP_VERSION_NUMBER; - dumpHeader.header_size = sizeof(struct _lkcd_dump_header); - dumpHeader.time.tv_sec = dump->getDumpTime().tv_sec; - dumpHeader.time.tv_usec = dump->getDumpTime().tv_usec; - strcpy(dumpHeader.utsname_machine, arch); - dumpHeader.memory_size = dump->getMemSize(); - dumpHeader.memory_start = 0; - dumpHeader.memory_end = dump->getMemSize(); - dumpHeader.num_dump_pages = dump->getMemSize()/0x1000; - dumpHeader.page_size = 0x1000; - dumpHeader.dump_compress = DUMP_COMPRESS_GZIP; - dumpHeader.dump_level = DUMP_LEVEL_ALL; - - dumpHeaderAsm.magic_number = DUMP_ASM_MAGIC_NUMBER; - dumpHeaderAsm.version = 1; - dumpHeaderAsm.header_size = sizeof(dumpHeaderAsm); -} - -int LKCDDump::compressGZIP(const char *old, uint32_t old_size, char *n, - uint32_t new_size) -{ - unsigned long len = old_size; - int rc; - - rc = compress((Bytef*)n, &len, (const Bytef*)old, new_size); - switch(rc) { - case Z_OK: - rc = len; - break; - case Z_BUF_ERROR: - /* In this case the compressed output is bigger than */ - /* the uncompressed */ - rc = GZIP_NOT_COMPRESSED; - break; - case Z_MEM_ERROR: - throw(DumpException("gzip call failed: out of memory")); - case Z_DATA_ERROR: - throw(DumpException("gzip call failed: input data " \ - "corrupted!")); - default: - throw(DumpException("gzip call failed: unknown error")); - } - return rc; -} - -void LKCDDump::writeDump(const char* fileName) -{ - char dump_header_buf[DUMP_HEADER_SIZE] = {}; - char dump_page_buf[DUMP_BUFFER_SIZE]; - char dpcpage[DUMP_PAGE_SIZE]; - uint32_t dp_size,dp_flags; - ProgressBar progressBar; - char buf[DUMP_PAGE_SIZE]; - struct _dump_page dp; - uint64_t mem_loc = 0; - ssize_t buf_loc = 0; - int size, fd; - - 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 */ - - memcpy(dump_header_buf, &dumpHeader, sizeof(dumpHeader)); - memcpy(&dump_header_buf[sizeof(dumpHeader)], &dumpHeaderAsm, - sizeof(dumpHeaderAsm)); - if (write(fd, dump_header_buf, sizeof(dump_header_buf)) != - sizeof(dump_header_buf)) { - throw(DumpErrnoException("write failed")); - } - - /* write memory */ - - referenceDump->seekMem(0); - - while (mem_loc < dumpHeader.memory_size) { - referenceDump->readMem(buf, DUMP_PAGE_SIZE); - copyRegsToPage(mem_loc,buf); - - memset(dpcpage, 0, DUMP_PAGE_SIZE); - /* - * Get the new compressed page size - */ - size = compressGZIP((char *)buf, DUMP_PAGE_SIZE, - (char *)dpcpage, DUMP_PAGE_SIZE); - - /* - * If compression failed or compressed was ineffective, - * we write an uncompressed page - */ - if (size == GZIP_NOT_COMPRESSED) { - dp_flags = DUMP_DH_RAW; - dp_size = DUMP_PAGE_SIZE; - } else { - dp_flags = DUMP_DH_COMPRESSED; - dp_size = size; - } - dp.address = mem_loc; - dp.size = dp_size; - dp.flags = dp_flags; - memcpy((void *)(dump_page_buf + buf_loc), - (const void *)&dp, sizeof(dp)); - buf_loc += sizeof(dp); - /* - * Copy the page of memory - */ - if (dp_flags & DUMP_DH_COMPRESSED) { - /* Copy the compressed page */ - memcpy((void *)(dump_page_buf + buf_loc), - (const void *)dpcpage, dp_size); - } else { - /* Copy directly from memory */ - memcpy((void *)(dump_page_buf + buf_loc), - (const void *)buf, dp_size); - } - buf_loc += dp_size; - if(write(fd, dump_page_buf, buf_loc) != buf_loc){ - throw(DumpErrnoException("write failed")); - } - buf_loc = 0; - mem_loc += DUMP_PAGE_SIZE; - progressBar.displayProgress(mem_loc/(1024*1024), - dumpHeader.memory_size/(1024*1024)); - } - - /* - * Write end marker - */ - dp.address = 0x0; - dp.size = 0x0; - dp.flags = DUMP_DH_END; - if(write(fd, &dp, sizeof(dp)) != sizeof(dp)){ - throw(DumpErrnoException("write failed")); - } - fprintf(stderr, "\n"); - if (fd != STDOUT_FILENO) - close(fd); -} - -struct timeval LKCDDump::getDumpTime(void) const -{ - struct timeval rc; - - rc.tv_sec = dumpHeader.time.tv_sec; - rc.tv_usec = dumpHeader.time.tv_usec; - return rc; -} - -LKCDDump32::LKCDDump32(Dump* dump, const RegisterContent32& r) - : LKCDDump(dump,"s390") -{ - unsigned int i; - - dumpHeaderAsm.real_cpu_cnt = (uint32_t) r.getNumCpus(); - for (i = 0; i < dumpHeaderAsm.real_cpu_cnt; i++) { - if (!r.regSets[i].prefix) - continue; - dumpHeaderAsm.lc_vec[i] = r.regSets[i].prefix; - dumpHeaderAsm.cpu_cnt++; - } - registerContent = r; -} - -void LKCDDump32::copyRegsToPage(uint64_t offset, char *buf) -{ - int cpu; - - for(cpu = 0; cpu < registerContent.getNumCpus(); cpu++){ - if(offset == registerContent.regSets[cpu].prefix){ - memcpy(buf+0xd8,®isterContent.regSets[cpu].cpuTimer, - sizeof(registerContent.regSets[cpu].cpuTimer)); - memcpy(buf+0xe0,®isterContent.regSets[cpu].clkCmp, - sizeof(registerContent.regSets[cpu].clkCmp)); - memcpy(buf+0x100,®isterContent.regSets[cpu].psw, - sizeof(registerContent.regSets[cpu].psw)); - memcpy(buf+0x108,®isterContent.regSets[cpu].prefix, - sizeof(registerContent.regSets[cpu].prefix)); - memcpy(buf+0x120,®isterContent.regSets[cpu].acrs, - sizeof(registerContent.regSets[cpu].acrs)); - memcpy(buf+0x160,®isterContent.regSets[cpu].fprs, - sizeof(registerContent.regSets[cpu].fprs)); - memcpy(buf+0x180,®isterContent.regSets[cpu].gprs, - sizeof(registerContent.regSets[cpu].gprs)); - memcpy(buf+0x1c0,®isterContent.regSets[cpu].crs, - sizeof(registerContent.regSets[cpu].crs)); - } - } -} - -LKCDDump64::LKCDDump64(Dump* dump, const RegisterContent64& r) - : LKCDDump(dump,"s390x") -{ - unsigned int i; - - dumpHeaderAsm.real_cpu_cnt = (uint32_t) r.getNumCpus(); - for (i = 0; i < dumpHeaderAsm.real_cpu_cnt; i++) { - if (!r.regSets[i].prefix) - continue; - dumpHeaderAsm.lc_vec[i] = r.regSets[i].prefix; - dumpHeaderAsm.cpu_cnt++; - } - registerContent = r; -} - -void LKCDDump64::copyRegsToPage(uint64_t offset, char *buf) -{ - int cpu; - - for(cpu = 0; cpu < registerContent.getNumCpus(); cpu++){ - if(offset == (registerContent.regSets[cpu].prefix + 0x1000)){ - memcpy(buf+0x328,®isterContent.regSets[cpu].cpuTimer, - sizeof(registerContent.regSets[cpu].cpuTimer)); - memcpy(buf+0x330,®isterContent.regSets[cpu].clkCmp, - sizeof(registerContent.regSets[cpu].clkCmp)); - memcpy(buf+0x300,®isterContent.regSets[cpu].psw, - sizeof(registerContent.regSets[cpu].psw)); - memcpy(buf+0x318,®isterContent.regSets[cpu].prefix, - sizeof(registerContent.regSets[cpu].prefix)); - memcpy(buf+0x340,®isterContent.regSets[cpu].acrs, - sizeof(registerContent.regSets[cpu].acrs)); - memcpy(buf+0x200,®isterContent.regSets[cpu].fprs, - sizeof(registerContent.regSets[cpu].fprs)); - memcpy(buf+0x280,®isterContent.regSets[cpu].gprs, - sizeof(registerContent.regSets[cpu].gprs)); - memcpy(buf+0x380,®isterContent.regSets[cpu].crs, - sizeof(registerContent.regSets[cpu].crs)); - memcpy(buf+0x31c,®isterContent.regSets[cpu].fpCr, - sizeof(registerContent.regSets[cpu].fpCr)); - } - } -} diff -Nru s390-tools-2.25.0/libvmdump/lkcd_dump.h s390-tools-2.26.0/libvmdump/lkcd_dump.h --- s390-tools-2.25.0/libvmdump/lkcd_dump.h 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/libvmdump/lkcd_dump.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,149 +0,0 @@ -/* - * vmdump - z/VM dump conversion library - * - * LKCD dump classes: LKCDDump, LKCDDump32, LKCDDump64 - * - * Copyright IBM Corp. 2004, 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 LKCD_DUMP_H -#define LKCD_DUMP_H - -#include "lib/zt_common.h" - -#include "dump.h" -#include "register_content.h" - -#define UTS_LEN 65 -#define DUMP_BUFFER_SIZE 0x2000 /* Size of dump buffer */ - -/* Standard header definitions */ -#define DUMP_HEADER_SIZE 0x10000 -#define DUMP_MAGIC_NUMBER 0xa8190173618f23edULL /* Dump magic number */ -#define DUMP_ASM_MAGIC_NUMBER 0x733339302d64756dULL /* asm magic number */ -#define DUMP_VERSION_NUMBER 0x8 /* Dump version number */ -#define DUMP_PANIC_LEN 0x100 /* Dump panic string length */ - -/* Dump levels - type specific stuff added later */ -#define DUMP_LEVEL_ALL 0x10 /* Dump all memory RAM and firmware */ - -/* Dump compression options */ -#define DUMP_COMPRESS_GZIP 0x2 /* Use GZIP compression */ - -/* Dump header flags */ -#define DUMP_DH_RAW 0x1 /* Raw page (no compression) */ -#define DUMP_DH_COMPRESSED 0x2 /* Page is compressed */ -#define DUMP_DH_END 0x4 /* End marker on a full dump */ - -/* Dump page defines */ -#define DUMP_PAGE_SHIFT 12ULL -#define DUMP_PAGE_SIZE (1ULL << DUMP_PAGE_SHIFT) - -#define GZIP_NOT_COMPRESSED -1 - -class LKCDDump : public Dump -{ -public: - LKCDDump(Dump*, const char *); - virtual ~LKCDDump(void) {} - inline virtual void readMem(char *UNUSED(buf), int UNUSED(size)) - { - throw(DumpException("LKCDDump::readMem() not implemented!")); - } - inline int seekMem(uint64_t UNUSED(offset)) - { - throw(DumpException("LKCDDump::seekMem() not implemented!")); - } - inline virtual uint64_t getMemSize() const - { - return dumpHeader.memory_size; - } - virtual struct timeval getDumpTime(void) const; - virtual void writeDump(const char *fileName); - virtual void copyRegsToPage(uint64_t offset, char *buf) = 0; -protected: - struct _lkcd_dump_header { - uint64_t magic_number; /* dump magic number,unique to verify */ - /* dump */ - uint32_t version; /* version number of this dump */ - uint32_t header_size; /* size of this header */ - uint32_t dump_level; /* level of this dump */ - uint32_t page_size; /* page size (e.g. 4K, 8K, 16K, etc.) */ - uint64_t memory_size; /* size of entire physical memory */ - uint64_t memory_start; /* start of physical memory */ - uint64_t memory_end; /* end of physical memory */ - /* the number of dump pages in this dump specifically */ - uint32_t num_dump_pages; - char panic_string[DUMP_PANIC_LEN]; - - /* timeval depends on machine, two long values */ - struct {uint64_t tv_sec; - uint64_t tv_usec; - } time; /* the time of the system crash */ - - /* the NEW utsname (uname) information -- in character form */ - /* we do this so we don't have to include utsname.h */ - /* plus it helps us be more architecture independent */ - char utsname_sysname[UTS_LEN]; - char utsname_nodename[UTS_LEN]; - char utsname_release[UTS_LEN]; - char utsname_version[UTS_LEN]; - char utsname_machine[UTS_LEN]; - char utsname_domainname[UTS_LEN]; - - uint64_t current_task; - uint32_t dump_compress; /* compression type used in this dump */ - uint32_t dump_flags; /* any additional flags */ - uint32_t dump_device; /* any additional flags */ - uint64_t s390_asm_magic; - uint16_t cpu_cnt; - uint32_t lowcore_ptr[512]; - } __packed; - - struct _lkcd_dump_header_asm { - uint64_t magic_number; - uint32_t version; - uint32_t header_size; - uint16_t cpu_cnt; - uint16_t real_cpu_cnt; - uint32_t lc_vec[512]; - } __packed; - - struct _dump_page { - uint64_t address; /* the address of this dump page */ - uint32_t size; /* the size of this dump page */ - uint32_t flags; /* flags (DUMP_COMPRESSED, DUMP_RAW */ - /* or DUMP_END) */ - } __packed; - - struct _lkcd_dump_header dumpHeader; - struct _lkcd_dump_header_asm dumpHeaderAsm; - -private: - int compressGZIP(const char *old, uint32_t old_size, char *n, - uint32_t new_size); - Dump *referenceDump; -}; - -class LKCDDump32 : public LKCDDump -{ -public: - LKCDDump32(Dump *dump, const RegisterContent32 &rc); - virtual void copyRegsToPage(uint64_t offset, char *buf); -private: - RegisterContent32 registerContent; -}; - -class LKCDDump64 : public LKCDDump -{ -public: - LKCDDump64(Dump *dump, const RegisterContent64 &rc); - virtual void copyRegsToPage(uint64_t offset, char *buf); -private: - RegisterContent64 registerContent; -}; - -#endif /* LKCD_DUMP_H */ diff -Nru s390-tools-2.25.0/libvmdump/Makefile s390-tools-2.26.0/libvmdump/Makefile --- s390-tools-2.25.0/libvmdump/Makefile 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/libvmdump/Makefile 1970-01-01 01:00:00.000000000 +0100 @@ -1,15 +0,0 @@ -include ../common.mak - -lib = libvmdump.a - -all: $(lib) - -objects = register_content.o dump.o lkcd_dump.o register_content.o \ - vmdump_convert.o vm_dump.o - -$(lib): $(objects) - -install: all - -clean: - rm -f *.o $(lib) diff -Nru s390-tools-2.25.0/libvmdump/register_content.cpp s390-tools-2.26.0/libvmdump/register_content.cpp --- s390-tools-2.25.0/libvmdump/register_content.cpp 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/libvmdump/register_content.cpp 1970-01-01 01:00:00.000000000 +0100 @@ -1,76 +0,0 @@ -/* - * vmdump - z/VM dump conversion library - * - * Register content classes - * - * Copyright IBM Corp. 2004, 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 "register_content.h" -#include "dump.h" - -RegisterContent32::RegisterContent32(void) - : regSets(), nrCpus(0) -{ -} - -RegisterContent32::RegisterContent32(const RegisterContent32& r) -{ - nrCpus = r.nrCpus; - memcpy(®Sets,&r.regSets,sizeof(regSets)); -} - -void RegisterContent32::addRegisterSet(const RegisterSet32& rs) -{ - if(nrCpus < MAX_CPUS){ - regSets[nrCpus++] = rs; - } else { - throw(DumpException("RegisterContent32::addRegisterSet - " \ - "No more register sets available")); - } -} - -RegisterSet32 RegisterContent32::getRegisterSet(int cpu){ - if(cpu <= nrCpus){ - return regSets[cpu]; - } else { - throw(DumpException("RegisterContent32::getRegisterSet - " \ - "No register set for cpu")); - } -} - -RegisterContent64::RegisterContent64(void) - : regSets(), nrCpus(0) -{ -} - -RegisterContent64::RegisterContent64(const RegisterContent64& r) -{ - nrCpus = r.nrCpus; - memcpy(®Sets,&r.regSets,sizeof(regSets)); -} - -void RegisterContent64::addRegisterSet(const RegisterSet64& rs) -{ - if(nrCpus < MAX_CPUS){ - regSets[nrCpus++] = rs; - } else { - throw(DumpException("RegisterContent64::addRegisterSet - " \ - "No more register sets available")); - } -} - -RegisterSet64 RegisterContent64::getRegisterSet(int cpu) -{ - if(cpu <= nrCpus){ - return regSets[cpu]; - } else { - throw(DumpException("RegisterContent64::getRegisterSet - " \ - "No register set for cpu")); - } -} diff -Nru s390-tools-2.25.0/libvmdump/register_content.h s390-tools-2.26.0/libvmdump/register_content.h --- s390-tools-2.25.0/libvmdump/register_content.h 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/libvmdump/register_content.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,75 +0,0 @@ -/* - * vmdump - z/VM dump conversion library - * - * Register content classes - * - * Copyright IBM Corp. 2004, 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 REGISTER_CONTENT_H -#define REGISTER_CONTENT_H - -#include - -#define MAX_CPUS 512 - -class RegisterSet64 -{ -public: - uint64_t gprs[16]; - uint64_t crs[16]; - uint32_t acrs[16]; - uint64_t fprs[16]; - uint32_t fpCr; - uint64_t psw[2]; - uint32_t prefix; - uint64_t cpuTimer; - uint64_t clkCmp; -}; - -class RegisterSet32 -{ -public: - uint32_t gprs[16]; - uint32_t crs[16]; - uint32_t acrs[16]; - uint64_t fprs[4]; - uint32_t psw[2]; - uint32_t prefix; - uint64_t cpuTimer; - uint64_t clkCmp; -}; - -class RegisterContent64{ -public: - RegisterContent64(void); - RegisterContent64(const RegisterContent64&); - RegisterSet64 getRegisterSet(int cpu); - void addRegisterSet(const RegisterSet64&); - inline int getNumCpus(void) const { return nrCpus; } - RegisterContent64& operator=(const RegisterContent64&) = default; - - RegisterSet64 regSets[MAX_CPUS]; -private: - int nrCpus; - -}; - -class RegisterContent32{ -public: - RegisterContent32(void); - RegisterContent32(const RegisterContent32&); - RegisterSet32 getRegisterSet(int cpu); - void addRegisterSet(const RegisterSet32&); - inline int getNumCpus(void) const { return nrCpus; } - RegisterContent32& operator=(const RegisterContent32&) = default; - - RegisterSet32 regSets[MAX_CPUS]; -private: - int nrCpus; -}; - -#endif /* REGISTER_CONTENT_H */ diff -Nru s390-tools-2.25.0/libvmdump/vmdump_convert.cpp s390-tools-2.26.0/libvmdump/vmdump_convert.cpp --- s390-tools-2.25.0/libvmdump/vmdump_convert.cpp 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/libvmdump/vmdump_convert.cpp 1970-01-01 01:00:00.000000000 +0100 @@ -1,72 +0,0 @@ -/* - * vmdump - z/VM dump conversion library - * - * Dump convert function: Converts VMDUMP to LKCD dump - * - * Copyright IBM Corp. 2004, 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 "lkcd_dump.h" -#include "vm_dump.h" - -int vmdump_convert(const char* inputFileName, const char* outputFileName, - const char* progName) -{ - /* Do the conversion */ - try { - switch(VMDump::getDumpType(inputFileName)){ - case Dump::DT_VM64_BIG: - { - LKCDDump64* lkcddump; - VMDump64Big* vmdump; - - vmdump = new VMDump64Big(inputFileName); - vmdump->printInfo(); - lkcddump = new LKCDDump64(vmdump, - vmdump->getRegisterContent()); - lkcddump->writeDump(outputFileName); - delete vmdump; - delete lkcddump; - break; - } - case Dump::DT_VM64: - { - LKCDDump64* lkcddump; - VMDump64* vmdump; - - vmdump = new VMDump64(inputFileName); - vmdump->printInfo(); - lkcddump = new LKCDDump64(vmdump, - vmdump->getRegisterContent()); - lkcddump->writeDump(outputFileName); - delete vmdump; - delete lkcddump; - break; - } - case Dump::DT_VM32: - { - LKCDDump32* lkcddump; - VMDump32* vmdump; - - 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; -} diff -Nru s390-tools-2.25.0/libvmdump/vm_dump.cpp s390-tools-2.26.0/libvmdump/vm_dump.cpp --- s390-tools-2.25.0/libvmdump/vm_dump.cpp 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/libvmdump/vm_dump.cpp 1970-01-01 01:00:00.000000000 +0100 @@ -1,624 +0,0 @@ -/* - * vmdump - z/VM dump conversion library - * - * Register content classes: - * VMDump, VMDumpClassic, VMDump64, VMDump64Big, VMDump32 - * - * Copyright IBM Corp. 2004, 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 "vm_dump.h" - -Dump::DumpType VMDump::getDumpType(const char* inputFileName) -{ - uint8_t fmbk_id[8] = {0xc8, 0xc3, 0xd7, 0xc4, 0xc6, 0xd4, 0xc2, 0xd2}; - struct _fir_basic fir; - struct _fmbk fmbk; - struct _adsr adsr; - char msg[200]; - FILE* fh; - - fh = fopen(inputFileName,"r"); - if (!fh) { - sprintf(msg,"Could not open '%s'",inputFileName); - throw DumpErrnoException(msg); - } - - /* Record 1: adsr */ - dump_read(&adsr, sizeof(adsr), 1, fh); - - /* Record 2: fmbk */ - dump_seek(fh, 0x1000, SEEK_SET); - if (fread(&fmbk, sizeof(fmbk), 1, fh) != 1) { - 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); - } - } - - /* Check if this is a vmdump */ - if (memcmp(fmbk.id, fmbk_id, 8) != 0 || - adsr.dump_type != 0xe5d4c4e4d4d74040ULL) { - fclose(fh); - sprintf(msg, "Input file '%s' is not a vmdump", inputFileName); - throw DumpException(msg); - } - - /* Record 3-7: fir */ - dump_seek(fh, (fmbk.rec_nr_fir - 1) * 0x1000, SEEK_SET); - if(fread(&fir, sizeof(fir), 1, fh) != 1) { - 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); - } - } - fclose(fh); - if (fir.fir_format == 0) { - return DT_VM32; - } else if (fir.fir_format == 0x02) {/*XXX && (fir.dump_format == 0x1))*/ - return DT_VM64_BIG; - } else if (fir.fir_format == 0x82) { - return DT_VM64; - } else { - return DT_UNKNOWN; - } -} - -VMDump::VMDump(const char *fileName) : Dump(fileName, "rb") -{ - uint8_t fmbk_id[8] = {0xc8, 0xc3, 0xd7, 0xc4, 0xc6, 0xd4, 0xc2, 0xd2}; - - ebcdicAsciiConv = iconv_open("ISO-8859-1", "EBCDIC-US"); - - /* Record 1: adsrRecord */ - - dump_seek(fh,0,SEEK_SET); - dump_read(&adsrRecord,sizeof(adsrRecord),1,fh); - - if(debug) { - char buf_asc[1024]; - char buf[1024]; - int i; - - fprintf(stderr, "off=%d\n", adsrRecord.sec5_offset); - dump_seek(fh, adsrRecord.sec5_offset, SEEK_SET); - dump_read(buf, adsrRecord.sec5_len, 1, fh); - ebcAsc(buf, buf_asc, adsrRecord.sec5_len); - for (i=0; i < adsrRecord.sec5_len; i++) { - if ((buf_asc[i]==0) || iscntrl(buf_asc[i])) - buf_asc[i]=' '; - } - buf_asc[adsrRecord.sec5_len] = 0; - printf("symptom string1: %s\n",buf_asc); - } - - /* Record 2: fmbk */ - - 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) { - throw DumpException("Input file is not a vmdump"); - } - - /* Record 3-7: fir records read by subclasses */ - - /* Record 8: albk */ - - dump_seek(fh,(fmbkRecord.rec_nr_access-1)*0x1000 ,SEEK_SET); - dump_read(&albkRecord,sizeof(albkRecord),1,fh); -} - -struct timeval VMDump::getDumpTime(void) const -{ - struct timeval rc; - - s390TodToTimeval(adsrRecord.tod,&rc); - return rc; -} - -void VMDump::printDebug(void) -{ - struct timeval time; - char fmbk_id[8]; - char albk_id[8]; - - s390TodToTimeval(adsrRecord.tod, &time); - - /* adsr */ - - printf("time : %s\n", ctime(&time.tv_sec)); - printf("stat1 : %x\n", adsrRecord.record_status_flag1); - printf("stat2 : %x\n", adsrRecord.record_status_flag2); - printf("sec 2 len: %i\n", adsrRecord.sec2_len); - printf("sec 2.1 len: %i/%i\n", adsrRecord.sec2_1_len, - adsrRecord.sec2_1_offset); - printf("sec 3 len: %i/%i\n", adsrRecord.sec3_len, - adsrRecord.sec3_offset); - printf("sec 4 len: %i/%i\n", adsrRecord.sec4_len, - adsrRecord.sec4_offset); - printf("sec 5 len: %i/%i\n", adsrRecord.sec5_len, - adsrRecord.sec5_offset); - printf("sec 6 len: %i/%i\n", adsrRecord.sec6_len, - adsrRecord.sec6_offset); - - /* fmbk */ - - ebcAsc(fmbkRecord.id, fmbk_id, sizeof(fmbkRecord.id)); - fmbk_id[7] = 0; - printf("id : %s\n", fmbk_id); - printf("fir rec nr: %i\n", fmbkRecord.rec_nr_fir); - printf("vec rec nr: %i\n", fmbkRecord.rec_nr_vector); - printf("access rec nr: %i\n", fmbkRecord.rec_nr_access); - - - /* albk */ - - ebcAsc(albkRecord.id, albk_id, sizeof(albkRecord.id)); - albk_id[7]=0; - printf("ALBK id : %s\n",albk_id); -} - -void VMDump::printInfo(void) -{ - struct timeval time; - - s390TodToTimeval(adsrRecord.tod,&time); - fprintf(stderr, " date........: %s",ctime(&time.tv_sec)); -} - -int VMDump::seekMem(uint64_t offset) -{ - if (offset != 0) { - return -1; - } - pageOffset = 0; - return 0; -} - -void VMDump::readMem(char* buf, int size) -{ - int i; - - if (pageOffset == 0) - dump_seek(fh, memoryStartRecord, SEEK_SET); - - if (size % 0x1000 != 0) { - throw(DumpException("internal error: VMDump::readMem() " \ - "can only handle sizes which are multiples of page size")); - } - - for(i = 0; i < size; i += 0x1000) { - if(testPage(pageOffset)) { - dump_read(buf + i, 0x1000, 1, fh); - } else { - memset(buf + i, 0, 0x1000); - } - pageOffset += 1; - } -} - -VMDump::~VMDump(void) -{ -} - -/*****************************************************************************/ -/* VMDumpClassic: traditional 32/64 bit vmdump (before z/VM 5.2) */ -/*****************************************************************************/ - -VMDumpClassic::VMDumpClassic(const char *fileName) : VMDump(fileName) -{ - int storageKeyPages,bitMapPages; - - pageOffset = 0; - - /* Record 9: asibk */ - - dump_seek(fh,fmbkRecord.rec_nr_access * 0x1000,SEEK_SET); - dump_read(&asibkRecord,sizeof(asibkRecord),1,fh); - - /* Record 10: bitmaps */ - - dump_seek(fh,(fmbkRecord.rec_nr_access + 1)* 0x1000 ,SEEK_SET); - bitmap = new char[asibkRecord.storage_size_2GB / (0x1000 * 8)]; - dump_read(bitmap,asibkRecord.storage_size_2GB / (0x1000*8),1,fh); - - bitMapPages=asibkRecord.storage_size_2GB / (0x1000 * 8); - if (bitMapPages % 0x1000 != 0) - bitMapPages = bitMapPages/0x1000 + 1; - else - bitMapPages = bitMapPages/0x1000; - - storageKeyPages=asibkRecord.storage_size_2GB / 0x1000; - if(storageKeyPages % 0x1000 != 0) { - storageKeyPages = storageKeyPages/0x1000 + 1; - } else { - storageKeyPages = storageKeyPages/0x1000; - } - - /* Skip storage keys */ - - memoryStartRecord = (fmbkRecord.rec_nr_access + 1) *0x1000 /* 0x9000 */ - + (bitMapPages + storageKeyPages)*0x1000; - if(debug) { - printf("Mem Offset: %llx\n", (long long) memoryStartRecord); - } -} - -void VMDumpClassic::printInfo(void) -{ - VMDump::printInfo(); - fprintf(stderr, " storage.....: %i MB\n", - asibkRecord.storage_size_2GB/(1024*1024)); -} - -VMDumpClassic::~VMDumpClassic(void) -{ - delete bitmap; -} - - -/*****************************************************************************/ -/* VMDump32: 32 bit vmdump */ -/*****************************************************************************/ - -VMDump32::VMDump32(const char* filename) : VMDumpClassic(filename) -{ - int i; - - if(!fh) { - return; - } - - 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 */ - dump_read(&fir32OtherRecords[i],sizeof(fir32OtherRecords[i]),1, - fh); - } - if(debug) - printDebug(); -} - -RegisterContent32 VMDump32::getRegisterContent(void) -{ - RegisterContent32 rc; - RegisterSet32 rs; - int cpu; - - /* First CPU */ - - memcpy(&rs.gprs, &fir32Record.gprs, sizeof(rs.gprs)); - memcpy(&rs.crs, &fir32Record.crs, sizeof(rs.crs)); - memcpy(&rs.acrs, &fir32Record.acrs, sizeof(rs.acrs)); - memcpy(&rs.psw, &fir32Record.psw, sizeof(rs.psw)); - memcpy(&rs.prefix, &fir32Record.prefix, sizeof(rs.prefix)); - memcpy(&rs.fprs, &fir32Record.fprs, sizeof(rs.fprs)); - memcpy(&rs.cpuTimer, &fir32Record.cpu_timer, sizeof(rs.cpuTimer)); - memcpy(&rs.clkCmp, &fir32Record.clock_cmp, sizeof(rs.clkCmp)); - - rc.addRegisterSet(rs); - - /* Other online cpus */ - - for(cpu = 0; cpu < fir32Record.online_cpus; cpu++) { - memcpy(&rs.gprs, &fir32OtherRecords[cpu].gprs, sizeof(rs.gprs)); - memcpy(&rs.crs, &fir32OtherRecords[cpu].crs, sizeof(rs.crs)); - memcpy(&rs.acrs, &fir32OtherRecords[cpu].acrs, sizeof(rs.acrs)); - /* No psw for ESA vmdumps */ - rs.psw[0] = 0xdeadbeef; - rs.psw[1] = 0xdeadbeef; - memcpy(&rs.prefix, &fir32OtherRecords[cpu].prefix, - sizeof(rs.prefix)); - memcpy(&rs.fprs, &fir32OtherRecords[cpu].fprs, sizeof(rs.fprs)); - memcpy(&rs.cpuTimer, &fir32OtherRecords[cpu].cpu_timer, - sizeof(rs.cpuTimer)); - memcpy(&rs.clkCmp, &fir32OtherRecords[cpu].clock_cmp, - sizeof(rs.clkCmp)); - rc.addRegisterSet(rs); - } - return rc; -} - -void VMDump32::printDebug(void) -{ - int i; - - VMDump::printDebug(); - printf("prefix: %x\n", fir32Record.prefix); - printf("cpus: %x\n", fir32Record.online_cpus); - printf("psw: %08x %08x\n", fir32Record.psw[0], fir32Record.psw[1]); - - for (i=0; i < fir32Record.online_cpus; i++) { - /* fir other */ - printf("prefix (%i): %x\n", i, fir32OtherRecords[i].prefix); - } -} - -void -VMDump32::printInfo(void) -{ - fprintf(stderr, "vmdump information:\n"); - fprintf(stderr, " architecture: 32 bit\n"); - VMDumpClassic::printInfo(); - fprintf(stderr, " cpus........: %x\n", fir32Record.online_cpus + 1); -} - - -VMDump32::~VMDump32(void) -{ - delete fir32OtherRecords; -} - - -/*****************************************************************************/ -/* VMDump64: 64 bit vmdump for old vmdump format (z/VM < 5.2) */ -/*****************************************************************************/ - -VMDump64::VMDump64(const char* filename) : VMDumpClassic(filename) -{ - int i; - - if(!fh) { - return; - } - - 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 */ - dump_read(&fir64OtherRecords[i], sizeof(fir64OtherRecords[i]), - 1, fh); - } - if(debug) - printDebug(); -} - -void VMDump64::printDebug(void) -{ - int i; - - VMDump::printDebug(); - printf("prefix: %x\n", fir64Record.prefix); - printf("cpus: %x\n", fir64Record.online_cpus); - printf("psw: %016llx %016llx\n", (long long)fir64Record.psw[0], - (long long)fir64Record.psw[1]); - - for (i=0; i < fir64Record.online_cpus; i++) { - /* fir other */ - printf("prefix (%i): %x\n", i, fir64OtherRecords[i].prefix); - } -} - -void VMDump64::printInfo(void) -{ - fprintf(stderr, "vmdump information:\n"); - fprintf(stderr, " architecture: 64 bit\n"); - VMDumpClassic::printInfo(); - fprintf(stderr, " cpus........: %x\n",fir64Record.online_cpus + 1); -} - - -RegisterContent64 VMDump64::getRegisterContent(void) -{ - RegisterContent64 rc; - RegisterSet64 rs; - int cpu; - - /* First CPU */ - - memcpy(&rs.gprs, &fir64Record.gprs, sizeof(rs.gprs)); - memcpy(&rs.crs, &fir64Record.crs, sizeof(rs.crs)); - memcpy(&rs.acrs, &fir64Record.acrs, sizeof(rs.acrs)); - memcpy(&rs.psw, &fir64Record.psw, sizeof(rs.psw)); - memcpy(&rs.prefix, &fir64Record.prefix, sizeof(rs.prefix)); - memcpy(&rs.fprs, &fir64Record.fprs, sizeof(rs.fprs)); - memcpy(&rs.cpuTimer, &fir64Record.cpu_timer, sizeof(rs.cpuTimer)); - memcpy(&rs.clkCmp, &fir64Record.clock_cmp, sizeof(rs.clkCmp)); - memcpy(&rs.fpCr, &fir64Record.fp_cntrl_reg, sizeof(rs.fpCr)); - - rc.addRegisterSet(rs); - - /* other online cpus */ - - for (cpu = 0; cpu < fir64Record.online_cpus; cpu++) { - memcpy(&rs.gprs, &fir64OtherRecords[cpu].gprs, sizeof(rs.gprs)); - memcpy(&rs.crs, &fir64OtherRecords[cpu].crs, sizeof(rs.crs)); - memcpy(&rs.acrs, &fir64OtherRecords[cpu].acrs, sizeof(rs.acrs)); - memcpy(&rs.psw, &fir64OtherRecords[cpu].psw, sizeof(rs.psw)); - memcpy(&rs.prefix, &fir64OtherRecords[cpu].prefix, - sizeof(rs.prefix)); - memcpy(&rs.fprs, &fir64OtherRecords[cpu].fprs, sizeof(rs.fprs)); - memcpy(&rs.cpuTimer, &fir64OtherRecords[cpu].cpu_timer, - sizeof(rs.cpuTimer)); - memcpy(&rs.clkCmp, &fir64OtherRecords[cpu].clock_cmp, - sizeof(rs.clkCmp)); - memcpy(&rs.fpCr, &fir64OtherRecords[cpu].fp_cntrl_reg, - sizeof(rs.fpCr)); - rc.addRegisterSet(rs); - } - return rc; -} - -VMDump64::~VMDump64(void) -{ - delete fir64OtherRecords; -} - -/*****************************************************************************/ -/* VMDump64Big: 64 bit vmdump with new big storage dump format */ -/*****************************************************************************/ - -VMDump64Big::VMDump64Big(const char* filename) : VMDump(filename) -{ - uint64_t pageNum, nrDumpedPages; - int i, j; - - if(!fh) { - return; - } - - /* Record 9: asibk */ - - 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 */ - - pageNum = 0; - nrDumpedPages = asibkRecordNew.storage_size_def_store / 0x1000; - memoryStartRecord = (fmbkRecord.rec_nr_access + 1) * 0x1000; - bitmap = new char[asibkRecordNew.storage_size_def_store/(0x1000 * 8)]; - if(!bitmap) { - throw(DumpErrnoException("out of memory")); - } - memset(bitmap,0,asibkRecordNew.storage_size_def_store/(0x1000 * 8)); - - dump_seek(fh,(fmbkRecord.rec_nr_access + 1)* 0x1000 ,SEEK_SET); - - do { - char bmIndexPage[0x1000]; - - dump_read(bmIndexPage, sizeof(bmIndexPage), 1, fh); - memoryStartRecord += 0x1000; - for (i=0; i < 0x1000; i++) { - if(testBitmapPage(bmIndexPage, i)) { - char bmPage[0x1000]; - - dump_read(bmPage,sizeof(bmPage),1,fh); - memoryStartRecord += 0x1000; - for(j = 0; j < 0x1000; j++) { - if(testBitmapKeyPage(bmPage, j)) { - setPageBit(pageNum); - } - pageNum++; - if(pageNum == nrDumpedPages) { - goto all_bitmaps_read; - } - } - } else { - pageNum += 0x1000; /* Empty pages */ - } - } - } while (pageNum < nrDumpedPages); - -all_bitmaps_read: - - if(debug) - printf("Mem Offset: %llx\n", (long long)memoryStartRecord); - - 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 */ - dump_read(&fir64OtherRecords[i], sizeof(fir64OtherRecords[i]), - 1, fh); - } - if(debug) - printDebug(); -} - -void VMDump64Big::printDebug(void) -{ - int i; - - VMDump::printDebug(); - printf("prefix: %x\n", fir64Record.prefix); - printf("cpus: %x\n", fir64Record.online_cpus); - printf("psw: %016llx %016llx\n", (long long)fir64Record.psw[0], - (long long)fir64Record.psw[1]); - - for (i=0; i < fir64Record.online_cpus; i++) { - /* fir other */ - printf("prefix (%i): %x\n", i, fir64OtherRecords[i].prefix); - } -} - -void VMDump64Big::printInfo(void) -{ - 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(); - fprintf(stderr, " cpus........: %x\n", fir64Record.online_cpus + 1); -} - - -RegisterContent64 VMDump64Big::getRegisterContent(void) -{ - RegisterContent64 rc; - RegisterSet64 rs; - int cpu; - - /* First CPU */ - - memcpy(&rs.gprs, &fir64Record.gprs, sizeof(rs.gprs)); - memcpy(&rs.crs, &fir64Record.crs, sizeof(rs.crs)); - memcpy(&rs.acrs, &fir64Record.acrs, sizeof(rs.acrs)); - memcpy(&rs.psw, &fir64Record.psw, sizeof(rs.psw)); - memcpy(&rs.prefix, &fir64Record.prefix, sizeof(rs.prefix)); - memcpy(&rs.fprs, &fir64Record.fprs, sizeof(rs.fprs)); - memcpy(&rs.cpuTimer, &fir64Record.cpu_timer, sizeof(rs.cpuTimer)); - memcpy(&rs.clkCmp, &fir64Record.clock_cmp, sizeof(rs.clkCmp)); - memcpy(&rs.fpCr, &fir64Record.fp_cntrl_reg, sizeof(rs.fpCr)); - - rc.addRegisterSet(rs); - - /* other online cpus */ - - for(cpu = 0; cpu < fir64Record.online_cpus; cpu++) { - memcpy(&rs.gprs, &fir64OtherRecords[cpu].gprs, sizeof(rs.gprs)); - memcpy(&rs.crs, &fir64OtherRecords[cpu].crs, sizeof(rs.crs)); - memcpy(&rs.acrs, &fir64OtherRecords[cpu].acrs, sizeof(rs.acrs)); - memcpy(&rs.psw, &fir64OtherRecords[cpu].psw, sizeof(rs.psw)); - memcpy(&rs.prefix, &fir64OtherRecords[cpu].prefix, - sizeof(rs.prefix)); - memcpy(&rs.fprs, &fir64OtherRecords[cpu].fprs, sizeof(rs.fprs)); - memcpy(&rs.cpuTimer, &fir64OtherRecords[cpu].cpu_timer, - sizeof(rs.cpuTimer)); - memcpy(&rs.clkCmp, &fir64OtherRecords[cpu].clock_cmp, - sizeof(rs.clkCmp)); - memcpy(&rs.fpCr, &fir64OtherRecords[cpu].fp_cntrl_reg, - sizeof(rs.fpCr)); - rc.addRegisterSet(rs); - } - return rc; -} - -VMDump64Big::~VMDump64Big(void) -{ - delete bitmap; - delete fir64OtherRecords; -} diff -Nru s390-tools-2.25.0/libvmdump/vm_dump.h s390-tools-2.26.0/libvmdump/vm_dump.h --- s390-tools-2.25.0/libvmdump/vm_dump.h 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/libvmdump/vm_dump.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,484 +0,0 @@ -/* - * vmdump - z/VM dump conversion library - * - * Register content classes: - * VMDump, VMDumpClassic, VMDump64, VMDump64Big, VMDump32 - * - * Copyright IBM Corp. 2004, 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. - */ - -/*===========================* - * The format of an vmdump: * - *===========================* - *---------------------------* - * Symptom Record * - * (ADSR COPY) Record 1 * - *---------------------------* - * Dump File Map Record * - * (HCPDFMBK COPY) Record 2 * - *---------------------------* - * Dump File Info Record * - * (HCPDFIR COPY) Records 3-7* - *---------------------------* - * Vector Registers * - * (optional) * - *---------------------------* - * Access Lists (HCPDALBK) * - *---------------------------* - * Address Space A * - * Information and Map * - * Record (HCPASIBK) * - *---------------------------* - * Address Space A * - * Bit Maps * - *---------------------------* - * Address Space A * - * Key Maps * - *---------------------------* - * Address Space A * - * Guest Storage * - *---------------------------* - * Additional Address Spaces * - * ASIBK * - * Bit Maps * - * Key Maps * - * Guest Storage * - * *NOTE* These probably * - * aren't in a Linux guest. * - *---------------------------* -*/ - -#ifndef VMDUMP_H -#define VMDUMP_H - -#include -#include - -#include "lib/zt_common.h" - -#include "dump.h" -#include "register_content.h" - -class VMDump : public Dump -{ -public: - VMDump(const char *filename); - virtual ~VMDump(void); - virtual void readMem(char *buf, int size); - virtual int seekMem(uint64_t offset); - virtual struct timeval getDumpTime(void) const; - - void printDebug(void); - void printInfo(void); - - static DumpType getDumpType(const char *); - inline int testPage(uint64_t bit) const - { - return (bitmap[bit/8] & (1 << (7-(bit % 8)))); - } - inline void setPageBit(uint64_t bit) const - { - bitmap[bit/8] |= (1 << (7-(bit % 8))); - } -protected: - /* Types */ - struct _adsr { - /* Section 1*/ - uint16_t sr; - uint32_t cpu_model; - char cpu_serial[6]; - uint32_t time_zone_conversion_factor; - uint64_t tod; - char time_stamp_str[4]; - char date_str[6]; - char node_name[8]; - char product_id[4]; - char feature_level[8]; - uint8_t record_status_flag1; - uint8_t record_status_flag2; - uint64_t dump_type; - - /* Section 2*/ - char arch_level[2]; - uint16_t sec2_len; - uint16_t sec2_1_len; - uint16_t sec2_1_offset; - uint16_t sec3_len; - uint16_t sec3_offset; - uint16_t sec4_len; - uint16_t sec4_offset; - uint16_t sec5_len; - uint16_t sec5_offset; - uint16_t sec6_len; - uint16_t sec6_offset; - } __packed; - - struct _fmbk { - char id[8]; - uint32_t rec_nr_fir; - uint32_t rec_nr_vector; - uint32_t rec_nr_access; - uint32_t num_acc_recs; - uint32_t num_addr_spaces; - uint32_t rec_nr_asibk; - } __packed; - - struct _fir_basic { - char filler1[15]; - uint8_t dump_format;/* 0x1 for big storage dump, 0x2 for cp */ - /* hard abend, 0x3 for cp soft abend */ - char filler2[171]; - uint8_t fir_format; /* 0x2 for big esame, 0x82 for esame, */ - /* 0x00 for esa */ - } __packed; - - struct _albk { - char id[8]; - } __packed; - - /* Methods */ - inline void ebcAsc(char *in, char *out, size_t size) const - { - size_t size_out = size; - size_t size_in = size; - size_t rc; - - rc = iconv(ebcdicAsciiConv, &in, &size_in, &out, &size_out); - if (rc == (size_t) -1) - throw(DumpException("Code page translation EBCDIC-ASCII failed")); - } - - /* Members */ - struct _adsr adsrRecord; - struct _fmbk fmbkRecord; - struct _albk albkRecord; - uint64_t memoryStartRecord; - char *bitmap; - uint64_t pageOffset; -private: - iconv_t ebcdicAsciiConv; -}; - -class VMDumpClassic : public VMDump -{ -public: - VMDumpClassic(const char *filename); - virtual ~VMDumpClassic(void); - void printInfo(void); - inline virtual uint64_t getMemSize(void) const - { - return (uint64_t)asibkRecord.storage_size_2GB; - } -protected: - /* Types */ - struct _asibk { - char id[8]; - char as_token[8]; - char spaceid[33]; - char reserved1[3]; - uint32_t storage_size_2GB; - uint32_t dcss_bitmap_first_rec; - uint32_t byte_past_highest_defined_byte; - uint64_t format_of_as; - char dump_id[100]; - uint32_t nr_of_recs_of_first_bit_map; - } __packed; - - - /* Members */ - struct _asibk asibkRecord; - -}; - -#define MAX_BKEY_PAGES 2 - -class VMDump64Big : public VMDump -{ -public: - VMDump64Big(const char *filename); - virtual ~VMDump64Big(void); - RegisterContent64 getRegisterContent(void); - - void printDebug(void); - void printInfo(void); - - inline virtual uint64_t getMemSize(void) const - { - return (uint64_t)asibkRecordNew.storage_size_def_store; - } -private: - inline int testBitmapPage(char *page, uint64_t bit) const - { - return (page[bit/8] & (1 << (7-(bit % 8)))); - } - - inline int testBitmapKeyPage(char *page, uint64_t bit) const - { - return (page[bit] & 0x01); - } - - /* types */ - struct _asibk_64_new { - char id[8]; - char as_token[8]; - char spaceid[33]; - char reserved1[2]; - uint8_t asibk_format; - char filler1[12]; - uint64_t storage_size_with_dcss; - uint64_t storage_size_def_store; - char filler2[136]; - uint64_t online_storage_table[8]; /* for "def store config" */ - uint64_t fence1; - uint64_t requested_range_table[8]; - uint64_t fence2; - uint32_t record_number_of_first_bit_map; /* XXX */ - } __packed; - - struct _fir_64 { - char id[8]; - uint64_t reserved1; - uint64_t gprs[16]; - uint32_t prefix; - char reserved2[5]; - uint64_t tod; - char reserved3[8]; - uint64_t cpu_timer; - char reserved4[7]; - uint8_t flag; - uint8_t type; - uint8_t complete; - uint8_t fir_format; /* 0x82 for esame - 0x00 for esa */ - uint8_t cont_flags; - uint8_t crypto_domain_index_reg; - uint8_t virt_cpu_info; - uint8_t arch_mode_id; - uint64_t psw[2]; - uint64_t crs[16]; - uint64_t fprs[16]; - uint8_t reserved5; - uint64_t clock_cmp; - char reserved6[3]; - uint32_t tod_programmable_reg; - uint32_t reserved_for_dvf[20]; - uint32_t acrs[16]; - uint32_t storage_size_2GB; - uint32_t reserved7; - uint32_t hcpsys_addr; - uint32_t reserved8; - uint64_t storage_size; - uint32_t snap_area_map_blk; - uint32_t reserved9; - char loc_mem[256]; - uint16_t online_cpus; - uint16_t cpu_addr; - uint16_t section_size_vector; - uint16_t reserved10; - char asit_primary[8]; - char space_id_primary[33]; - char reserved12[3]; - uint16_t crypto_domain_index_mask; - uint16_t reserved11; - uint32_t fp_cntrl_reg; - uint32_t reserved13; - uint64_t reserved14[16]; - } __packed; - - struct _fir_other_64 { - uint16_t cpu_addr; - uint16_t vector_sec_size; - uint8_t crypto_index_reg; - uint8_t virt_cpu_info; - uint16_t crypto_index_mask; - uint32_t reserved1[2]; - uint64_t fprs[16]; - uint64_t gprs[16]; - uint64_t psw[2]; - uint32_t reserved2[2]; - uint32_t prefix; - uint32_t fp_cntrl_reg; - uint32_t reserved3; - uint32_t tod; - uint64_t cpu_timer; - uint64_t clock_cmp; - uint32_t reserved4[2]; - uint32_t acrs[16]; - uint64_t crs[16]; - uint64_t mc_interrupt_code; - uint32_t reserved5; - uint32_t external_damage_code; - uint64_t mc_failing_storage_addr; - } __packed; - - /* Members */ - struct _asibk_64_new asibkRecordNew; - struct _fir_64 fir64Record; - struct _fir_other_64 *fir64OtherRecords; -}; - -class VMDump64 : public VMDumpClassic -{ -public: - VMDump64(const char *filename); - virtual ~VMDump64(void); - RegisterContent64 getRegisterContent(void); - - void printDebug(void); - void printInfo(void); -private: - /* Types */ - struct _fir_64 { - char id[8]; - uint64_t reserved1; - uint64_t gprs[16]; - uint32_t prefix; - char reserved2[5]; - uint64_t tod; - char reserved3[8]; - uint64_t cpu_timer; - char reserved4[7]; - uint8_t flag; - uint8_t type; - uint8_t complete; - uint8_t fir_format; /* 0x82 for esame - 0x00 for esa */ - uint8_t cont_flags; - uint8_t crypto_domain_index_reg; - uint8_t virt_cpu_info; - uint8_t arch_mode_id; - uint64_t psw[2]; - uint64_t crs[16]; - uint64_t fprs[16]; - uint8_t reserved5; - uint64_t clock_cmp; - char reserved6[3]; - uint32_t tod_programmable_reg; - uint32_t reserved_for_dvf[20]; - uint32_t acrs[16]; - uint32_t storage_size_2GB; - uint32_t reserved7; - uint32_t hcpsys_addr; - uint32_t reserved8; - uint64_t storage_size; - uint32_t snap_area_map_blk; - uint32_t reserved9; - char loc_mem[256]; - uint16_t online_cpus; - uint16_t cpu_addr; - uint16_t section_size_vector; - uint16_t reserved10; - char asit_primary[8]; - char space_id_primary[33]; - char reserved12[3]; - uint16_t crypto_domain_index_mask; - uint16_t reserved11; - uint32_t fp_cntrl_reg; - uint32_t reserved13; - uint64_t reserved14[16]; - } __packed; - - struct _fir_other_64 { - uint16_t cpu_addr; - uint16_t vector_sec_size; - uint8_t crypto_index_reg; - uint8_t virt_cpu_info; - uint16_t crypto_index_mask; - uint32_t reserved1[2]; - uint64_t fprs[16]; - uint64_t gprs[16]; - uint64_t psw[2]; - uint32_t reserved2[2]; - uint32_t prefix; - uint32_t fp_cntrl_reg; - uint32_t reserved3; - uint32_t tod; - uint64_t cpu_timer; - uint64_t clock_cmp; - uint32_t reserved4[2]; - uint32_t acrs[16]; - uint64_t crs[16]; - uint64_t mc_interrupt_code; - uint32_t reserved5; - uint32_t external_damage_code; - uint64_t mc_failing_storage_addr; - } __packed; - - /* Members */ - struct _fir_64 fir64Record; - struct _fir_other_64 *fir64OtherRecords; -}; - -class VMDump32 : public VMDumpClassic -{ -public: - VMDump32(const char *filename); - virtual ~VMDump32(void); - RegisterContent32 getRegisterContent(void); - void printDebug(void); - void printInfo(void); -private: - /* Types */ - struct _fir_32 { - uint32_t gprs[16]; - uint32_t crs[16]; - uint64_t fprs[4]; - uint64_t tod; - uint64_t cpu_timer; - uint64_t clock_cmp; - uint8_t flag; - uint8_t type; - uint8_t complete; - uint8_t fir_format; - uint32_t storage_size_2GB; - char loc_mem[256]; - uint32_t prefix; - uint16_t online_cpus; - uint8_t cont_flags; - uint8_t crypto_domain_index_reg; - uint8_t virt_cpu_info; - uint8_t arch_mode_id; - uint16_t crypto_domain_index_mask; - uint32_t reserved1; - uint32_t snap_area_map_blk; - uint64_t reserved2; - uint32_t hcpsys_addr; - uint32_t reserved3[20]; - uint32_t psw[2]; - uint16_t cpu_addr; - uint16_t section_size_vector; - uint32_t acrs[16]; - char asit_primary[8]; - char space_id_primary[33]; - char reserved4[131]; - } __packed; - - struct _fir_other_32 { - uint16_t cpu_addr; - uint16_t vector_sec_size; - uint32_t prefix; - uint8_t crypto_index_reg; - uint8_t virt_cpu_info; - uint16_t crypto_index_mask; - uint32_t reserved1; - uint64_t cpu_timer; - uint64_t clock_cmp; - uint64_t mc_interrupt_code; - uint64_t reserved2; - uint32_t mc_failing_storage_addr; - uint32_t machine_dependent_region_code; - uint32_t lixed_logout_area[4]; - char reserved3[16]; - uint32_t acrs[16]; - uint64_t fprs[4]; - uint32_t gprs[16]; - uint32_t crs[16]; - } __packed; - - /* Members */ - struct _fir_32 fir32Record; - struct _fir_other_32 *fir32OtherRecords; -}; - -#endif /* VMDUMP_H */ diff -Nru s390-tools-2.25.0/Makefile s390-tools-2.26.0/Makefile --- s390-tools-2.25.0/Makefile 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/Makefile 2023-02-14 14:05:17.000000000 +0100 @@ -9,11 +9,11 @@ # TOOLS: Tools that can have a dependency to base libraries or libraries # BASELIB_DIRS = libutil libseckey -LIB_DIRS = libvtoc libzds libdasd libvmdump libccw libvmcp libekmfweb \ +LIB_DIRS = libvtoc libzds libdasd libccw libvmcp libekmfweb \ libkmipclient libcpumf libap libpv TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \ tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \ - vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \ + 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 \ genprotimg lsstp hsci hsavmcore chreipl-fcp-mpath ap_tools pvattest diff -Nru s390-tools-2.25.0/netboot/Makefile s390-tools-2.26.0/netboot/Makefile --- s390-tools-2.25.0/netboot/Makefile 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/netboot/Makefile 2023-02-14 14:05:17.000000000 +0100 @@ -15,13 +15,13 @@ install-scripts: $(SCRIPTS) @if [ ! -d $(DESTDIR)$(NETBOOT_SAMPLEDIR) ]; then \ mkdir -p $(DESTDIR)$(NETBOOT_SAMPLEDIR); \ - chown $(OWNER).$(GROUP) $(DESTDIR)$(NETBOOT_SAMPLEDIR); \ + chown $(OWNER):$(GROUP) $(DESTDIR)$(NETBOOT_SAMPLEDIR); \ chmod 755 $(DESTDIR)$(NETBOOT_SAMPLEDIR); \ fi; \ for i in $^; do \ $(SED) -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' \ < $$i >$(DESTDIR)$(NETBOOT_SAMPLEDIR)/$$i; \ - chown $(OWNER).$(GROUP) $(DESTDIR)$(NETBOOT_SAMPLEDIR)/$$i; \ + chown $(OWNER):$(GROUP) $(DESTDIR)$(NETBOOT_SAMPLEDIR)/$$i; \ chmod 755 $(DESTDIR)$(NETBOOT_SAMPLEDIR)/$$i; \ done diff -Nru s390-tools-2.25.0/pvattest/src/common.h s390-tools-2.26.0/pvattest/src/common.h --- s390-tools-2.25.0/pvattest/src/common.h 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/pvattest/src/common.h 2023-02-14 14:05:17.000000000 +0100 @@ -11,10 +11,10 @@ /* Must be included before any other header */ #include "config.h" -#include #include #include "libpv/glib-helper.h" +#include #include "libpv/macros.h" #include "lib/zt_common.h" diff -Nru s390-tools-2.25.0/qethconf/Makefile s390-tools-2.26.0/qethconf/Makefile --- s390-tools-2.25.0/qethconf/Makefile 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/qethconf/Makefile 2023-02-14 14:05:17.000000000 +0100 @@ -5,7 +5,7 @@ install: qethconf $(SED) -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' \ < qethconf >$(DESTDIR)$(BINDIR)/qethconf; \ - chown $(OWNER).$(GROUP) $(DESTDIR)$(BINDIR)/qethconf; \ + chown $(OWNER):$(GROUP) $(DESTDIR)$(BINDIR)/qethconf; \ chmod 755 $(DESTDIR)$(BINDIR)/qethconf; \ $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 qethconf.8 \ diff -Nru s390-tools-2.25.0/README.md s390-tools-2.26.0/README.md --- s390-tools-2.25.0/README.md 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/README.md 2023-02-14 14:05:17.000000000 +0100 @@ -98,11 +98,6 @@ * tunedasd: Adjust tunable parameters on DASD devices. - * vmconvert: - Convert system dumps created by the z/VM VMDUMP command into dumps with - LKCD format. These LKCD dumps can then be analyzed with the dump analysis - tool lcrash. - * vmcp: Send commands from Linux as a z/VM guest to the z/VM control program (CP). Call vmcp with the CP command as an argument. The response of z/VM is diff -Nru s390-tools-2.25.0/scripts/dbginfo.sh s390-tools-2.26.0/scripts/dbginfo.sh --- s390-tools-2.25.0/scripts/dbginfo.sh 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/scripts/dbginfo.sh 2023-02-14 14:05:17.000000000 +0100 @@ -2,7 +2,7 @@ # # dbginfo.sh - Tool to collect runtime, configuration, and trace information # -# Copyright IBM Corp. 2002, 2022 +# Copyright IBM Corp. 2002, 2023 # # s390-tools is free software; you can redistribute it and/or modify # it under the terms of the MIT license. See LICENSE for details. @@ -71,7 +71,7 @@ print_version() { cat < -#include -#include -#include -#include -#include -#include - -#include "lib/vmdump.h" -#include "lib/zt_common.h" - -static struct option longopts[] = { - {"file",required_argument,0,'f'}, - {"help",no_argument,0,'h'}, - {"version",no_argument,0,'v'}, - {"output",required_argument,0,'o'}, - {0,0,0,0} -}; - -#define OPTSTRING "f:o:vh" -extern char *optarg; - -/* Version info */ -static const char version_text[] = "vmconvert: vmdump converter tool version "\ - RELEASE_STRING; - -/* Copyright notice */ -static const char copyright_notice[] = "Copyright IBM Corp. 2004, 2017"; - -/* Usage information */ -static const char usage_text[] = \ -"Usage: vmconvert -f VMDUMPFILE [-o OUTPUTFILE]\n" \ -" vmconvert VMDUMPFILE [OUTPUTFILE]\n" \ -"\n" \ -"Convert a vmdump into a lkcd (linux kernel crash dumps) dump.\n" \ -"\n" \ -"-h, --help Print this help, then exit.\n" \ -"-v, --version Print version information, then exit.\n" \ -"-f, --file VMDUMPFILE The vmdump file VMDUMPFILE, which should be\n"\ -" converted.\n" \ -"-o, --output OUTPUTFILE The converted lkcd dump file OUTPUTFILE.\n"\ -" The default file name is 'dump.lkcd'.\n"; - -/* Globals */ -char inputFileName[1024]; -char outputFileName[1024] = "dump.lkcd"; - -void -parseOpts(int argc, char* argv[]) -{ - int inputFileSet = 0; - int outputFileSet = 0; - int c, longIndex; - while((c = getopt_long(argc, argv, OPTSTRING, longopts, - &longIndex)) != -1) { - switch (c) { - case 'f': - strcpy(inputFileName, optarg); - inputFileSet = 1; - break; - case 'o': - strcpy(outputFileName, optarg); - outputFileSet = 1; - break; - case 'h': - printf("%s", usage_text); - exit(0); - case 'v': - printf("%s\n", version_text); - printf("%s\n", copyright_notice); - exit(0); - default: - fprintf(stderr, "Try 'vmconvert --help' for" - " more information.\n"); - exit(1); - } - } - /* check for positional parameters */ - if (optind < argc) { - int count = 0; - while (optind < argc){ - if(!inputFileSet && count==0){ - strcpy(inputFileName, argv[optind]); - inputFileSet = 1; - } else if(!outputFileSet && count==1){ - strcpy(outputFileName, argv[optind]); - outputFileSet = 1; - } else if(count == 2){ - printf("%s", usage_text); - exit(0); - } - count++; - optind++; - } - } - - if(!inputFileSet){ - printf("%s: input file required - use '-f' option!\n",argv[0]); - exit(1); - } -} - -int -main(int argc, char* argv[]) -{ - struct stat s; - int rc; - - parseOpts(argc,argv); - - /* Check if output file already exists */ - if(stat(outputFileName,&s) == 0){ - char answer[100]; - printf("%s: overwrite file '%s'? ",argv[0],outputFileName); - if(scanf("%s",answer) != 1) - exit(1); - if((strcmp(answer,"y") != 0) && (strcmp(answer,"yes") != 0)) - exit(0); - } - rc = vmdump_convert(inputFileName, outputFileName, argv[0]); - if (!rc) - printf("'%s' has been written successfully.\n", outputFileName); - return rc; -} diff -Nru s390-tools-2.25.0/vmur/Makefile s390-tools-2.26.0/vmur/Makefile --- s390-tools-2.25.0/vmur/Makefile 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/vmur/Makefile 2023-02-14 14:05:17.000000000 +0100 @@ -5,13 +5,12 @@ all: vmur -libs = $(rootdir)/libvmdump/libvmdump.a \ - $(rootdir)/libvmcp/libvmcp.a $(rootdir)/libutil/libutil.a +libs = $(rootdir)/libvmcp/libvmcp.a $(rootdir)/libutil/libutil.a objects = vmur.o vmur: $(objects) $(libs) - $(LINKXX) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ + $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ install: all $(INSTALL) -d -m 755 $(DESTDIR)$(USRSBINDIR) $(DESTDIR)$(MANDIR)/man8 diff -Nru s390-tools-2.25.0/vmur/vmur.8 s390-tools-2.26.0/vmur/vmur.8 --- s390-tools-2.25.0/vmur/vmur.8 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/vmur/vmur.8 2023-02-14 14:05:17.000000000 +0100 @@ -50,7 +50,7 @@ The \*v program requires the vmcp kernel module as prerequisite. For the receive, punch, and print commands the vmur kernel module is also required, and the corresponding z/VM virtual unit record devices -(in most cases 000c as reader, 000d as punch, and 000e as printer) +(in most cases 000c as reader, 000d as punch, and 000e as printer) must be set online. See .BR chccwdev (8) @@ -77,7 +77,7 @@ .IP "" 0 Synopsis: .IP "" 2 -receive [-fH] [-d dev_node] [-C class] [-t | -b sep.pad | -c] +receive [-fH] [-d dev_node] [-C class] [-t | -b sep.pad] spoolid [-O | outfile] .PP @@ -124,7 +124,7 @@ .IP "" 0 \fB-t or --text\fR .IP "" 2 -Specifies that a text file requiring EBCDIC-to-ASCII conversion is to be +Specifies that a text file requiring EBCDIC-to-ASCII conversion is to be received. Character sets IBM037 and ISO-8859-1 are used for the conversion. An ASCII line feed character (0x0a) is inserted for each 80-byte input record read from the z/VM reader. Trailing EBCDIC blanks (0x40) in the @@ -150,8 +150,14 @@ .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. +Dump file conversion has been removed from +.BR vmur (8) +and -c is no longer a valid option. +An error message is displayed and the program terminates. +Use +.BR zgetdump (8) +to convert VMDUMP files to a format +appropriate for further analysis with crash. .SP .IP "" 0 \fB-O or --stdout\fR @@ -299,7 +305,7 @@ If a line exceeds the record length, an error is printed. .br .BR iconv (1) -output can be piped to punch or print, for example: +output can be piped to punch or print, for example: .IP "" 2 # iconv xyz -f ISO-8859-1 -t EBCDIC-US | vmur pun -b 0x25,0x40 -N abc .SP @@ -403,7 +409,7 @@ \fBspoolid\fR .IP "" 2 Identifies the z/VM spool file to be listed. -If omitted, all spool files on the specified queue are listed +If omitted, all spool files on the specified queue are listed .PD .IP "" 0 .SP @@ -530,7 +536,7 @@ .in +0.25in .nf # vmcp sp cons clo \(rs* rdr -RDR FILE 0398 SENT FROM LINUX025 CON WAS 0398 RECS 1872 +RDR FILE 0398 SENT FROM LINUX025 CON WAS 0398 RECS 1872 .fi .in -0.25in .ft @@ -605,6 +611,7 @@ .SH "SEE ALSO" .BR chccwdev (8), .BR vmcp (8), -.BR iconv (1) +.BR iconv (1), +.BR zgetdump (8) .I "Linux on System z - Device Drivers, Features, and Commands" diff -Nru s390-tools-2.25.0/vmur/vmur.c s390-tools-2.26.0/vmur/vmur.c --- s390-tools-2.25.0/vmur/vmur.c 1970-01-01 01:00:00.000000000 +0100 +++ s390-tools-2.26.0/vmur/vmur.c 2023-02-14 14:05:17.000000000 +0100 @@ -0,0 +1,2329 @@ +/* + * vmur - Work with z/VM spool file queues (reader, punch, printer) + * + * Copyright IBM Corp. 2007, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lib/vmdump.h" +#include "lib/zt_common.h" +#include "lib/util_libc.h" +#include "lib/vmcp.h" + +#include "vmur.h" + +#define CP_PREFIX_LEN 11 + +/* 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, 2017"; + +/* + * 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; + int lock_fd; + /* ur device spool state */ + char spool_restore_cmd[MAXCMDLEN]; + int spool_restore_needed; + char spool_class; + int spool_class_specified; + char spool_dest[9]; + int spool_dest_specified; + char spool_form[9]; + int spool_form_specified; + char spool_dist[9]; + int spool_dist_specified; +} 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" +"-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" +"-C, --class Specify the spool class to match a reader file.\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" +"-C, --class Spool class to be assigned to the created spool file.\n" +" --form Form to be assigned to the created spool file.\n" +" --dest Destination to be assigned to the created spool file.\n" +" --dist Distribution code for the resulting spool file.\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("%s", 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); +} + +/* + * 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; + } + } +} + +/* + * Handle CP error message and exit + */ +static void cperr_exit(char *cpcmd, int cprc, char *buf) +{ + if (strlen(buf) <= CP_PREFIX_LEN) + ERR_EXIT("CP command '%s' failed with rc=%i\n", cpcmd, cprc); + + strip_cperr(buf); + ERR_EXIT("%s\n", buf); +} + +static void _cpcmd(char *cpcmd, char **resp, int *rc, int retry, int upper) +{ + struct vmcp_parm cp; + char cmd[MAXCMDLEN]; + int ret; + + strcpy(cmd, cpcmd); + cp.cpcmd = cmd; + cp.do_upper = upper; + cp.buffer_size = VMCP_DEFAULT_BUFSZ; + +retry: + ret = vmcp(&cp); + + switch (ret) { + case VMCP_ERR_OPEN: + ERR_EXIT("Could not issue CP command: \"%s\"\n" + "Ensure that vmcp kernel module is loaded!\n", cmd); + + case VMCP_ERR_SETBUF: + goto fail; + + case VMCP_ERR_WRITE: + goto fail; + + case VMCP_ERR_GETCODE: + goto fail; + + case VMCP_ERR_GETSIZE: + goto fail; + + case VMCP_ERR_READ: + goto fail; + + case VMCP_ERR_TOOSMALL: + if (retry) { + cp.buffer_size = cp.response_size; + free(cp.response); + goto retry; + } + ERR_EXIT("Not enough buffer space (%u/%u) for CP " + "command '%s'.\nSorry, please issue command " + " on your 3270 console!\n", cp.response_size, + cp.buffer_size, cmd); + } + + if (rc == NULL) { + if (cp.cprc != 0) { + /* caller wants us to handle the error */ + cperr_exit(cmd, cp.cprc, cp.response); + } + } else { + *rc = cp.cprc; + } + + if (resp) + *resp = cp.response; + else + free(cp.response); + 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 */ + 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 is not online\n"); +found: + if (sscanf(bus_id, "0.0.%x", &devno) != 1) + ERR_EXIT("Could not extract device number 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"); +} + +/* + * Copy printable characters + */ +static void strncpy_graph(char *dest, const char *src, size_t len) +{ + size_t n; + + for (n = 0; n < len; n++) { + if (!isgraph(src[n])) + break; + dest[n] = src[n]; + } + dest[n] = '\0'; +} + +/* + * Create CP command to restore spooling options + */ +static void save_spool_options(struct vmur *info) +{ + char cmd[MAXCMDLEN], *resp; + char cl, value[9], *tmp; + int n; + + /* Retrieve spooling options for ur device */ + sprintf(cmd, "QUERY VIRTUAL %X", info->devno); + cpcmd(cmd, &resp, NULL, 0); + + /* Prepare CP spool restore command */ + n = sprintf(info->spool_restore_cmd, "SPOOL %X", info->devno); + + /* Save the CLASS value if required */ + if (info->spool_class_specified) { + cl = resp[13]; + n += sprintf(info->spool_restore_cmd + n, " CL %c", cl); + } + + /* Save FORM value if required */ + if (info->spool_form_specified) { + tmp = strstr(resp, "FORM "); + if (tmp == NULL) + ERR_EXIT("Could not retrieve value for the FORM " + "spooling option\n"); + strncpy_graph(value, tmp + 5, 8); + n += sprintf(info->spool_restore_cmd + n, " FORM %s", value); + } + + /* Save DEST value if required */ + if (info->spool_dest_specified) { + tmp = strstr(resp, "DEST "); + if (tmp == NULL) + ERR_EXIT("Could not retrieve value for the DEST " + "spooling option\n"); + strncpy_graph(value, tmp + 5, 8); + n += sprintf(info->spool_restore_cmd + n, " DEST %s", value); + } + + /* Save DIST value if required */ + if (info->spool_dist_specified) { + tmp = strstr(resp, "DIST "); + if (tmp == NULL) + ERR_EXIT("Could not retrieve value for the DIST " + "spooling option\n"); + strncpy_graph(value, tmp + 5, 8); + n += sprintf(info->spool_restore_cmd + n, " DIST %s", value); + } + + free(resp); + ++info->spool_restore_needed; +} + +/* + * Restore saved spooling options for a ur device + */ +static void restore_spool_options(struct vmur *info) +{ + if (!info->spool_restore_needed) + return; + + cpcmd(info->spool_restore_cmd, NULL, NULL, 0); + --info->spool_restore_needed; +} + +/* + * Returns non-zero if spool options have to be saved, changed, and restored, + * otherwise zero is returned. + */ +static int require_spool_setup(struct vmur *info) +{ + return !!(info->spool_class_specified || + info->spool_form_specified || + info->spool_dest_specified || + info->spool_dist_specified); +} + +/* + * Setup spooling options for a ur device + */ +static void setup_spool_options(struct vmur *info) +{ + char cmd[MAXCMDLEN]; + int n, rc; + + /* + * Check if spool options must be changed. If so, save the current + * spool option values and restore them at program exit. + */ + if (!require_spool_setup(info)) + return; + + /* Save spool options */ + save_spool_options(info); + + /* Change spool options */ + n = sprintf(cmd, "SPOOL %X", info->devno); + if (info->spool_class_specified) + n += sprintf(cmd + n, " CLASS %c", info->spool_class); + if (info->spool_form_specified) + n += sprintf(cmd + n, " FORM %s", info->spool_form); + if (info->spool_dest_specified) + n += sprintf(cmd + n, " DEST %s", info->spool_dest); + if (info->spool_dist_specified) + n += sprintf(cmd + n, " DIST %s", info->spool_dist); + + cpcmd(cmd, NULL, &rc, 0); + if (rc) + ERR_EXIT("Could not set spooling options (rc=%i)\n", rc); +} + +/* + * Setup and check ur device + */ +static void setup_ur_device(struct vmur *info) +{ + unsigned long driver_major; + dev_t node_dev; + int rc; + + /* + * Few vmur commands do not require a particular device node and, + * therefore, no device setup is required. + */ + if (!strlen(info->devnode)) + return; + + 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"); + info->lock_fd = -1; +} + +/* + * 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; +} + +/* + * Set block mode and the separator and padding byte + */ +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"); +} + +/* + * Set spool class value + */ +static void set_spool_class(struct vmur *info, const char *val, int is_rdr) +{ + char cl = val[0]; + + if (strlen(val) > 1 || (!isalnum(cl) && cl != '*')) + ERR_EXIT("Class must be one of A through Z, 0 through 9, " + "or an asterisk (*)\n"); + if (cl == '*' && !is_rdr) + ERR_EXIT("The asterisk (*) class is only valid for readers\n"); + info->spool_class = toupper(cl); + ++info->spool_class_specified; +} + +/* + * 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'}, + { "class", required_argument, NULL, 'C'}, + { 0, 0, 0, 0 } + }; + static const char option_string[] = "vhtOfHcd:b:C:"; + + 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': + fprintf(stderr, "%s: Option '-c' is no longer valid. " + "Use zgetdump for dump file conversion.\n", + prog_name); + std_usage_exit(); + break; + case 'C': + set_spool_class(info, optarg, 1); + 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->spool_class_specified, 1, "class"); + + if (info->stdout_specified && info->file_name_specified) + ERR_EXIT("File name not allowed, when --stdout specified!\n"); + if (info->blocked_specified + info->text_specified) + ERR_EXIT("Conflicting options: -b and -t are mutually " + "exclusive.\n"); + if (!info->spool_class_specified) + set_spool_class(info, "*", 1); +} + +/* + * 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'}, + { "class", required_argument, NULL, 'C'}, + { "dest", required_argument, NULL, 'D'}, + { "form", required_argument, NULL, 'F'}, + { "dist", required_argument, NULL, 'I'}, + { 0, 0, 0, 0 } + }; + static const char option_string[] = "vhtrfu:n:d:b:N:C:"; + + 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; + case 'C': + set_spool_class(info, optarg, 0); + break; + case 'D': + ++info->spool_dest_specified; + if (invalid_operand(optarg)) + ERR_EXIT("Invalid destination: %s\n", optarg); + else + strcpy(info->spool_dest, optarg); + break; + case 'F': + ++info->spool_form_specified; + if (invalid_operand(optarg)) + ERR_EXIT("Invalid form: %s\n", optarg); + else + strcpy(info->spool_form, optarg); + break; + case 'I': + ++info->spool_dist_specified; + if (invalid_operand(optarg)) + ERR_EXIT("Invalid distribution code: %s\n", optarg); + else + strcpy(info->spool_dist, 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"); + CHECK_SPEC_MAX(info->spool_class_specified, 1, "class"); + CHECK_SPEC_MAX(info->spool_form_specified, 1, "form"); + CHECK_SPEC_MAX(info->spool_dest_specified, 1, "dest"); + CHECK_SPEC_MAX(info->spool_dist_specified, 1, "dist"); + + 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'}, + { "class", required_argument, NULL, 'C'}, + { "dest", required_argument, NULL, 'D'}, + { "form", required_argument, NULL, 'F'}, + { 0, 0, 0, 0 } + }; + static const char option_string[] = "fvhq:C:"; + + 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; + case 'C': + set_spool_class(info, optarg, 0); + break; + case 'D': + ++info->spool_dest_specified; + if (invalid_operand(optarg)) + ERR_EXIT("Invalid destination: %s\n", optarg); + else + strcpy(info->spool_dest, optarg); + break; + case 'F': + ++info->spool_form_specified; + if (invalid_operand(optarg)) + ERR_EXIT("Invalid form: %s\n", optarg); + else + strcpy(info->spool_form, optarg); + break; + default: + std_usage_exit(); + } + } + CHECK_SPEC_MAX(info->force_specified, 1, "force"); + CHECK_SPEC_MAX(info->queue_specified, 1, "queue"); + CHECK_SPEC_MAX(info->spool_class_specified, 1, "class"); + CHECK_SPEC_MAX(info->spool_form_specified, 1, "form"); + CHECK_SPEC_MAX(info->spool_dest_specified, 1, "dest"); + 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(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(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 . + * If ORDER fails, the CP command response is as follows: + * NO FILES ORDERED + * HCPxxxnnnt + * If ORDER returns successfully, CP command CHANGE RDR 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); +} + +/* + * 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 file name 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); + util_strstrip(name); + memcpy(type, &buf[140], 8); + util_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) +{ + char failed_action[10] = {}; + + info->lock_fd = open(LOCK_FILE, O_RDONLY | O_CREAT, S_IRUSR); + if (info->lock_fd == -1) { + ERR("WARNING: Unable to open lock file %s, continuing " + "without any serialization.\n", LOCK_FILE); + return; + } + if (flock(info->lock_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); + } +} + +/* + * Release lock and close lock file + */ +static void release_lock(struct vmur *info) +{ + flock(info->lock_fd, LOCK_UN); + close(info->lock_fd); + info->lock_fd = -1; +} + +/* + * 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 text mode: 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; +} + +/* + * Clean up and restore spool options + */ +static void cleanup_atexit_fn(void) +{ + restore_spool_options(&vmur_info); + release_lock(&vmur_info); +} + +/* + * Close VM virtual reader in case of a signal e.g. CTRL-C +*/ +static void ur_receive_sig_handler(int UNUSED(sig), siginfo_t *UNUSED(sip), + void *UNUSED(p)) +{ + close_reader(&vmur_info, "HOLD"); + cleanup_atexit_fn(); + 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 the specified " + "spool file class\n"); + 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 spool file 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 */ + 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); + 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); + 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); + } else if (cprc) { + ERR_EXIT("CP command failed with rc=%i\n%s\n", cprc, response); + } + free(response); + 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 UNUSED(sig), siginfo_t *UNUSED(sip), + void *UNUSED(p)) +{ + close_ur_device_purge(&vmur_info); + cleanup_atexit_fn(); + 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; + + /* 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; + + fprintf(stderr, "%s: purge selected %s file(s)? ", prog_name, + info->queue); + /* + * Release the vmur session lock while waiting for user input. It is + * safe to release the lock here, because the ur_purge operation does + * not modify spool options. + */ + release_lock(info); + str = fgets(buf, sizeof(buf), stdin); + acquire_lock(info); + 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]; + int n, m; + + ur_purge_question(info); + + /* Prepare the CP PURGE command */ + n = m = sprintf(cmd, "PURGE * %s", info->queue); + + /* Add selection criteria to match spool files */ + if (info->spoolid_specified) + n += sprintf(cmd + n, " %s", info->spoolid); + if (info->spool_class_specified) + n += sprintf(cmd + n, " CLASS %c", info->spool_class); + if (info->spool_form_specified) + n += sprintf(cmd + n, " FORM %s", info->spool_form); + if (info->spool_dest_specified) + n += sprintf(cmd + n, " DEST %s", info->spool_dest); + + /* Purge all files if no spoolid or any of the class, form, or dest + * option is specified. + */ + if (n == m) + n += sprintf(cmd + n, " ALL"); + + 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); + 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); + + /* Register cleanup function */ + if (atexit(cleanup_atexit_fn)) + ERR_EXIT("Could not set up vmur session cleanup\n"); + + /* Acquire a lock to serialize concurrent vmur invocations */ + acquire_lock(&vmur_info); + + /* Retrieve ur device number */ + setup_ur_device(&vmur_info); + + switch (vmur_info.action) { + case RECEIVE: + /* Setup spool options */ + setup_spool_options(&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: + /* Setup spool options */ + setup_spool_options(&vmur_info); + if (vmur_info.text_specified) + setup_iconv(&vmur_info, ASCII_CODE_PAGE, + EBCDIC_CODE_PAGE); + 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; +} diff -Nru s390-tools-2.25.0/vmur/vmur.cpp s390-tools-2.26.0/vmur/vmur.cpp --- s390-tools-2.25.0/vmur/vmur.cpp 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/vmur/vmur.cpp 1970-01-01 01:00:00.000000000 +0100 @@ -1,2351 +0,0 @@ -/* - * vmur - Work with z/VM spool file queues (reader, punch, printer) - * - * Copyright IBM Corp. 2007, 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "lib/vmdump.h" -#include "lib/zt_common.h" -#include "lib/util_libc.h" -#include "lib/vmcp.h" - -#include "vmur.h" - -#define CP_PREFIX_LEN 11 - -/* 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, 2017"; - -/* - * 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; - int lock_fd; - /* ur device spool state */ - char spool_restore_cmd[MAXCMDLEN]; - int spool_restore_needed; - char spool_class; - int spool_class_specified; - char spool_dest[9]; - int spool_dest_specified; - char spool_form[9]; - int spool_form_specified; - char spool_dist[9]; - int spool_dist_specified; -} 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" -"-C, --class Specify the spool class to match a reader file.\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" -"-C, --class Spool class to be assigned to the created spool file.\n" -" --form Form to be assigned to the created spool file.\n" -" --dest Destination to be assigned to the created spool file.\n" -" --dist Distribution code for the resulting spool file.\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("%s", 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); -} - -/* - * 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; - } - } -} - -/* - * Handle CP error message and exit - */ -static void cperr_exit(char *cpcmd, int cprc, char *buf) -{ - if (strlen(buf) <= CP_PREFIX_LEN) - ERR_EXIT("CP command '%s' failed with rc=%i\n", cpcmd, cprc); - - strip_cperr(buf); - ERR_EXIT("%s\n", buf); -} - -static void _cpcmd(char *cpcmd, char **resp, int *rc, int retry, int upper) -{ - struct vmcp_parm cp; - char cmd[MAXCMDLEN]; - int ret; - - strcpy(cmd, cpcmd); - cp.cpcmd = cmd; - cp.do_upper = upper; - cp.buffer_size = VMCP_DEFAULT_BUFSZ; - -retry: - ret = vmcp(&cp); - - switch (ret) { - case VMCP_ERR_OPEN: - ERR_EXIT("Could not issue CP command: \"%s\"\n" - "Ensure that vmcp kernel module is loaded!\n", cmd); - - case VMCP_ERR_SETBUF: - goto fail; - - case VMCP_ERR_WRITE: - goto fail; - - case VMCP_ERR_GETCODE: - goto fail; - - case VMCP_ERR_GETSIZE: - goto fail; - - case VMCP_ERR_READ: - goto fail; - - case VMCP_ERR_TOOSMALL: - if (retry) { - cp.buffer_size = cp.response_size; - free(cp.response); - goto retry; - } - ERR_EXIT("Not enough buffer space (%u/%u) for CP " - "command '%s'.\nSorry, please issue command " - " on your 3270 console!\n", cp.response_size, - cp.buffer_size, cmd); - } - - if (rc == NULL) { - if (cp.cprc != 0) { - /* caller wants us to handle the error */ - cperr_exit(cmd, cp.cprc, cp.response); - } - } else { - *rc = cp.cprc; - } - - if (resp) - *resp = cp.response; - else - free(cp.response); - 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 */ - 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 is not online\n"); -found: - if (sscanf(bus_id, "0.0.%x", &devno) != 1) - ERR_EXIT("Could not extract device number 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"); -} - -/* - * Copy printable characters - */ -static void strncpy_graph(char *dest, const char *src, size_t len) -{ - size_t n; - - for (n = 0; n < len; n++) { - if (!isgraph(src[n])) - break; - dest[n] = src[n]; - } - dest[n] = '\0'; -} - -/* - * Create CP command to restore spooling options - */ -static void save_spool_options(struct vmur *info) -{ - char cmd[MAXCMDLEN], *resp; - char cl, value[9], *tmp; - int n; - - /* Retrieve spooling options for ur device */ - sprintf(cmd, "QUERY VIRTUAL %X", info->devno); - cpcmd(cmd, &resp, NULL, 0); - - /* Prepare CP spool restore command */ - n = sprintf(info->spool_restore_cmd, "SPOOL %X", info->devno); - - /* Save the CLASS value if required */ - if (info->spool_class_specified) { - cl = resp[13]; - n += sprintf(info->spool_restore_cmd + n, " CL %c", cl); - } - - /* Save FORM value if required */ - if (info->spool_form_specified) { - tmp = strstr(resp, "FORM "); - if (tmp == NULL) - ERR_EXIT("Could not retrieve value for the FORM " - "spooling option\n"); - strncpy_graph(value, tmp + 5, 8); - n += sprintf(info->spool_restore_cmd + n, " FORM %s", value); - } - - /* Save DEST value if required */ - if (info->spool_dest_specified) { - tmp = strstr(resp, "DEST "); - if (tmp == NULL) - ERR_EXIT("Could not retrieve value for the DEST " - "spooling option\n"); - strncpy_graph(value, tmp + 5, 8); - n += sprintf(info->spool_restore_cmd + n, " DEST %s", value); - } - - /* Save DIST value if required */ - if (info->spool_dist_specified) { - tmp = strstr(resp, "DIST "); - if (tmp == NULL) - ERR_EXIT("Could not retrieve value for the DIST " - "spooling option\n"); - strncpy_graph(value, tmp + 5, 8); - n += sprintf(info->spool_restore_cmd + n, " DIST %s", value); - } - - free(resp); - ++info->spool_restore_needed; -} - -/* - * Restore saved spooling options for a ur device - */ -static void restore_spool_options(struct vmur *info) -{ - if (!info->spool_restore_needed) - return; - - cpcmd(info->spool_restore_cmd, NULL, NULL, 0); - --info->spool_restore_needed; -} - -/* - * Returns non-zero if spool options have to be saved, changed, and restored, - * otherwise zero is returned. - */ -static int require_spool_setup(struct vmur *info) -{ - return !!(info->spool_class_specified || - info->spool_form_specified || - info->spool_dest_specified || - info->spool_dist_specified); -} - -/* - * Setup spooling options for a ur device - */ -static void setup_spool_options(struct vmur *info) -{ - char cmd[MAXCMDLEN]; - int n, rc; - - /* - * Check if spool options must be changed. If so, save the current - * spool option values and restore them at program exit. - */ - if (!require_spool_setup(info)) - return; - - /* Save spool options */ - save_spool_options(info); - - /* Change spool options */ - n = sprintf(cmd, "SPOOL %X", info->devno); - if (info->spool_class_specified) - n += sprintf(cmd + n, " CLASS %c", info->spool_class); - if (info->spool_form_specified) - n += sprintf(cmd + n, " FORM %s", info->spool_form); - if (info->spool_dest_specified) - n += sprintf(cmd + n, " DEST %s", info->spool_dest); - if (info->spool_dist_specified) - n += sprintf(cmd + n, " DIST %s", info->spool_dist); - - cpcmd(cmd, NULL, &rc, 0); - if (rc) - ERR_EXIT("Could not set spooling options (rc=%i)\n", rc); -} - -/* - * Setup and check ur device - */ -static void setup_ur_device(struct vmur *info) -{ - unsigned long driver_major; - dev_t node_dev; - int rc; - - /* - * Few vmur commands do not require a particular device node and, - * therefore, no device setup is required. - */ - if (!strlen(info->devnode)) - return; - - 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"); - info->lock_fd = -1; -} - -/* - * 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; -} - -/* - * Set block mode and the separator and padding byte - */ -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"); -} - -/* - * Set spool class value - */ -static void set_spool_class(struct vmur *info, const char *val, int is_rdr) -{ - char cl = val[0]; - - if (strlen(val) > 1 || (!isalnum(cl) && cl != '*')) - ERR_EXIT("Class must be one of A through Z, 0 through 9, " - "or an asterisk (*)\n"); - if (cl == '*' && !is_rdr) - ERR_EXIT("The asterisk (*) class is only valid for readers\n"); - info->spool_class = toupper(cl); - ++info->spool_class_specified; -} - -/* - * 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'}, - { "class", required_argument, NULL, 'C'}, - { 0, 0, 0, 0 } - }; - static const char option_string[] = "vhtOfHcd:b:C:"; - - 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; - case 'C': - set_spool_class(info, optarg, 1); - 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"); - CHECK_SPEC_MAX(info->spool_class_specified, 1, "class"); - - if (info->stdout_specified && info->file_name_specified) - ERR_EXIT("File name not allowed, when --stdout specified!\n"); - if (info->blocked_specified + info->text_specified + - info->convert_specified > 1) - ERR_EXIT("Conflicting options: -b, -t and -c are mutually " - "exclusive.\n"); - if (!info->spool_class_specified) - set_spool_class(info, "*", 1); -} - -/* - * 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'}, - { "class", required_argument, NULL, 'C'}, - { "dest", required_argument, NULL, 'D'}, - { "form", required_argument, NULL, 'F'}, - { "dist", required_argument, NULL, 'I'}, - { 0, 0, 0, 0 } - }; - static const char option_string[] = "vhtrfu:n:d:b:N:C:"; - - 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; - case 'C': - set_spool_class(info, optarg, 0); - break; - case 'D': - ++info->spool_dest_specified; - if (invalid_operand(optarg)) - ERR_EXIT("Invalid destination: %s\n", optarg); - else - strcpy(info->spool_dest, optarg); - break; - case 'F': - ++info->spool_form_specified; - if (invalid_operand(optarg)) - ERR_EXIT("Invalid form: %s\n", optarg); - else - strcpy(info->spool_form, optarg); - break; - case 'I': - ++info->spool_dist_specified; - if (invalid_operand(optarg)) - ERR_EXIT("Invalid distribution code: %s\n", optarg); - else - strcpy(info->spool_dist, 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"); - CHECK_SPEC_MAX(info->spool_class_specified, 1, "class"); - CHECK_SPEC_MAX(info->spool_form_specified, 1, "form"); - CHECK_SPEC_MAX(info->spool_dest_specified, 1, "dest"); - CHECK_SPEC_MAX(info->spool_dist_specified, 1, "dist"); - - 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'}, - { "class", required_argument, NULL, 'C'}, - { "dest", required_argument, NULL, 'D'}, - { "form", required_argument, NULL, 'F'}, - { 0, 0, 0, 0 } - }; - static const char option_string[] = "fvhq:C:"; - - 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; - case 'C': - set_spool_class(info, optarg, 0); - break; - case 'D': - ++info->spool_dest_specified; - if (invalid_operand(optarg)) - ERR_EXIT("Invalid destination: %s\n", optarg); - else - strcpy(info->spool_dest, optarg); - break; - case 'F': - ++info->spool_form_specified; - if (invalid_operand(optarg)) - ERR_EXIT("Invalid form: %s\n", optarg); - else - strcpy(info->spool_form, optarg); - break; - default: - std_usage_exit(); - } - } - CHECK_SPEC_MAX(info->force_specified, 1, "force"); - CHECK_SPEC_MAX(info->queue_specified, 1, "queue"); - CHECK_SPEC_MAX(info->spool_class_specified, 1, "class"); - CHECK_SPEC_MAX(info->spool_form_specified, 1, "form"); - CHECK_SPEC_MAX(info->spool_dest_specified, 1, "dest"); - 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(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(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 . - * If ORDER fails, the CP command response is as follows: - * NO FILES ORDERED - * HCPxxxnnnt - * If ORDER returns successfully, CP command CHANGE RDR 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); -} - -/* - * 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 file name 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); - util_strstrip(name); - memcpy(type, &buf[140], 8); - util_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) -{ - char failed_action[10] = {}; - - info->lock_fd = open(LOCK_FILE, O_RDONLY | O_CREAT, S_IRUSR); - if (info->lock_fd == -1) { - ERR("WARNING: Unable to open lock file %s, continuing " - "without any serialization.\n", LOCK_FILE); - return; - } - if (flock(info->lock_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); - } -} - -/* - * Release lock and close lock file - */ -static void release_lock(struct vmur *info) -{ - flock(info->lock_fd, LOCK_UN); - close(info->lock_fd); - info->lock_fd = -1; -} - -/* - * 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 text mode: 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; -} - -/* - * Clean up and restore spool options - */ -static void cleanup_atexit_fn(void) -{ - restore_spool_options(&vmur_info); - release_lock(&vmur_info); -} - -/* - * Close VM virtual reader in case of a signal e.g. CTRL-C -*/ -static void ur_receive_sig_handler(int UNUSED(sig), siginfo_t *UNUSED(sip), - void *UNUSED(p)) -{ - close_reader(&vmur_info, "HOLD"); - cleanup_atexit_fn(); - 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 the specified " - "spool file class\n"); - 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 spool file 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 = vmdump_convert(info->devnode, NULL, - prog_name); - else - rc = vmdump_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); - 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); - } else if (cprc) { - ERR_EXIT("CP command failed with rc=%i\n%s\n", cprc, response); - } - free(response); - 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 UNUSED(sig), siginfo_t *UNUSED(sip), - void *UNUSED(p)) -{ - close_ur_device_purge(&vmur_info); - cleanup_atexit_fn(); - 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; - - /* 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; - - fprintf(stderr, "%s: purge selected %s file(s)? ", prog_name, - info->queue); - /* - * Release the vmur session lock while waiting for user input. It is - * safe to release the lock here, because the ur_purge operation does - * not modify spool options. - */ - release_lock(info); - str = fgets(buf, sizeof(buf), stdin); - acquire_lock(info); - 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]; - int n, m; - - ur_purge_question(info); - - /* Prepare the CP PURGE command */ - n = m = sprintf(cmd, "PURGE * %s", info->queue); - - /* Add selection criteria to match spool files */ - if (info->spoolid_specified) - n += sprintf(cmd + n, " %s", info->spoolid); - if (info->spool_class_specified) - n += sprintf(cmd + n, " CLASS %c", info->spool_class); - if (info->spool_form_specified) - n += sprintf(cmd + n, " FORM %s", info->spool_form); - if (info->spool_dest_specified) - n += sprintf(cmd + n, " DEST %s", info->spool_dest); - - /* Purge all files if no spoolid or any of the class, form, or dest - * option is specified. - */ - if (n == m) - n += sprintf(cmd + n, " ALL"); - - 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); - 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); - - /* Register cleanup function */ - if (atexit(cleanup_atexit_fn)) - ERR_EXIT("Could not set up vmur session cleanup\n"); - - /* Acquire a lock to serialize concurrent vmur invocations */ - acquire_lock(&vmur_info); - - /* Retrieve ur device number */ - setup_ur_device(&vmur_info); - - switch (vmur_info.action) { - case RECEIVE: - /* Setup spool options */ - setup_spool_options(&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: - /* Setup spool options */ - setup_spool_options(&vmur_info); - if (vmur_info.text_specified) - setup_iconv(&vmur_info, ASCII_CODE_PAGE, - EBCDIC_CODE_PAGE); - 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; -} diff -Nru s390-tools-2.25.0/zconf/Makefile s390-tools-2.26.0/zconf/Makefile --- s390-tools-2.25.0/zconf/Makefile 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/zconf/Makefile 2023-02-14 14:05:17.000000000 +0100 @@ -25,7 +25,7 @@ cat $$i | \ sed -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' \ >$(DESTDIR)$(BINDIR)/$$i; \ - chown $(OWNER).$(GROUP) $(DESTDIR)$(BINDIR)/$$i; \ + chown $(OWNER):$(GROUP) $(DESTDIR)$(BINDIR)/$$i; \ chmod 755 $(DESTDIR)$(BINDIR)/$$i; \ done @@ -34,15 +34,15 @@ cat $$i | \ sed -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' \ >$(DESTDIR)$(USRSBINDIR)/$$i; \ - chown $(OWNER).$(GROUP) $(DESTDIR)$(USRSBINDIR)/$$i; \ + chown $(OWNER):$(GROUP) $(DESTDIR)$(USRSBINDIR)/$$i; \ chmod 755 $(DESTDIR)$(USRSBINDIR)/$$i; \ done install-manpages: $(MANPAGES) @if [ ! -d $(DESTDIR)$(MANDIR) ]; then \ mkdir -p $(DESTDIR)$(MANDIR)/man8; \ - chown $(OWNER).$(GROUP) $(DESTDIR)$(MANDIR); \ - chown $(OWNER).$(GROUP) $(DESTDIR)$(MANDIR)/man8; \ + chown $(OWNER):$(GROUP) $(DESTDIR)$(MANDIR); \ + chown $(OWNER):$(GROUP) $(DESTDIR)$(MANDIR)/man8; \ chmod 755 $(DESTDIR)$(MANDIR); \ chmod 755 $(DESTDIR)$(MANDIR)/man8; \ fi; \ diff -Nru s390-tools-2.25.0/zconf/zcrypt/lszcrypt.8 s390-tools-2.26.0/zconf/zcrypt/lszcrypt.8 --- s390-tools-2.25.0/zconf/zcrypt/lszcrypt.8 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/zconf/zcrypt/lszcrypt.8 2023-02-14 14:05:17.000000000 +0100 @@ -218,6 +218,8 @@ .br N - APXA available (ability to address more than 16 crypto cards and domains). .br +H - Hardware support for stateless filtering available. +.br F - Full function support (opposed to restricted function support, see below). .br R - Restricted function support. The F and R flag both reflect if a diff -Nru s390-tools-2.25.0/zconf/zcrypt/lszcrypt.c s390-tools-2.26.0/zconf/zcrypt/lszcrypt.c --- s390-tools-2.25.0/zconf/zcrypt/lszcrypt.c 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/zconf/zcrypt/lszcrypt.c 2023-02-14 14:05:17.000000000 +0100 @@ -45,15 +45,17 @@ #define CAP_RNG "Long RNG" #define CAP_EP11 "EP11 Secure Key" #define CAP_APMMS "AP bus max message size limit %ld Kb" +#define CAP_HSL "Hardware support for stateless filtering" /* - * Card types + * Card types and other feature masks */ #define MASK_APSC 0x80000000 #define MASK_RSA4K 0x60000000 #define MASK_COPRO 0x10000000 #define MASK_ACCEL 0x08000000 #define MASK_EP11 0x04000000 +#define MASK_HSL 0x01000000 /* * Classification @@ -66,20 +68,21 @@ /* * facility bits */ -#define MAX_FAC_BITS 9 +#define MAX_FAC_BITS 10 static struct fac_bits_s { int mask; char c; } fac_bits[MAX_FAC_BITS] = { - { 0x80000000, 'S' }, - { 0x40000000, 'M' }, - { 0x20000000, 'C' }, - { 0x10000000, 'D' }, - { 0x08000000, 'A' }, - { 0x04000000, 'X' }, - { 0x02000000, 'N' }, - { 0x00800000, 'F' }, - { 0x00400000, 'R' }, + { 0x80000000, 'S' }, /* bit 0 */ + { 0x40000000, 'M' }, /* bit 1 */ + { 0x20000000, 'C' }, /* bit 2 */ + { 0x10000000, 'D' }, /* bit 3, cca mode */ + { 0x08000000, 'A' }, /* bit 4, accel mode */ + { 0x04000000, 'X' }, /* bit 5, ep11 mode */ + { 0x02000000, 'N' }, /* bit 6, apxa */ + { 0x01000000, 'H' }, /* bit 7, stateless filtering by hardware */ + { 0x00800000, 'F' }, /* bit 8, full function set */ + { 0x00400000, 'R' }, /* bit 9, restricted function set */ }; /* @@ -444,6 +447,8 @@ printf("%s (%s)\n", CAP_CCA, cbuf); else printf("%s\n", CAP_CCA); + if (func_val & MASK_HSL) + printf("%s\n", CAP_HSL); printf("%s\n", CAP_RNG); } else if (func_val & MASK_EP11) { printf("%s\n", CAP_EP11); diff -Nru s390-tools-2.25.0/zdev/src/zfcp.c s390-tools-2.26.0/zdev/src/zfcp.c --- s390-tools-2.25.0/zdev/src/zfcp.c 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/zdev/src/zfcp.c 2023-02-14 14:05:17.000000000 +0100 @@ -144,6 +144,19 @@ .accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 4294967295)), }; +static struct attrib zfcp_tattr_ber_stop = { + .name = "ber_stop", + .title = "Stop FCP devices on bit-error threshold", + .desc = + "Control the automatic shutdown of FCP devices for FCP channels\n" + "that report a bit-error count in excess of its threshold:\n" + " 0: Stop on bit-error threshold is disabled\n" + " 1: Stop on bit-error threshold is enabled\n", + .nounload = 1, + .defval = "1", + .accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)), +}; + /* * ZFCP methods. */ @@ -171,6 +184,7 @@ util_list_iterate(&list->list, s) { if (s->attrib == &zfcp_tattr_allow_lun_scan || + s->attrib == &zfcp_tattr_ber_stop || s->attrib == &zfcp_tattr_dif || s->attrib == &zfcp_tattr_dix || s->attrib == &zfcp_tattr_datarouter || @@ -313,6 +327,7 @@ &zfcp_tattr_no_auto_port_rescan, &zfcp_tattr_port_scan_ratelimit, &zfcp_tattr_port_scan_backoff, + &zfcp_tattr_ber_stop, ), .unknown_type_attribs = 1, diff -Nru s390-tools-2.25.0/zdev/src/zfcp_lun.c s390-tools-2.26.0/zdev/src/zfcp_lun.c --- s390-tools-2.25.0/zdev/src/zfcp_lun.c 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/zdev/src/zfcp_lun.c 2023-02-14 14:05:17.000000000 +0100 @@ -346,6 +346,7 @@ "of a SCSI device. Note that storage servers typically limit the\n" "total number of outstanding SCSI commands of all SCSI devices\n" "accessed from all attached hosts.\n", + .defval = "32", .accept = ACCEPT_ARRAY(ACCEPT_NUM_GE(1)), }; diff -Nru s390-tools-2.25.0/zdump/dfi.c s390-tools-2.26.0/zdump/dfi.c --- s390-tools-2.25.0/zdump/dfi.c 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/zdump/dfi.c 2023-02-14 14:05:17.000000000 +0100 @@ -3,7 +3,7 @@ * * Generic input dump format functions (DFI - Dump Format Input) * - * Copyright IBM Corp. 2001, 2018 + * Copyright IBM Corp. 2001, 2023 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -34,6 +34,7 @@ &dfi_s390_ext, &dfi_s390, &dfi_lkcd, + &dfi_vmdump, &dfi_pv_elf, &dfi_elf, &dfi_kdump, diff -Nru s390-tools-2.25.0/zdump/dfi.h s390-tools-2.26.0/zdump/dfi.h --- s390-tools-2.25.0/zdump/dfi.h 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/zdump/dfi.h 2023-02-14 14:05:17.000000000 +0100 @@ -3,7 +3,7 @@ * * Generic input dump format functions (DFI - Dump Format Input) * - * Copyright IBM Corp. 2001, 2018 + * Copyright IBM Corp. 2001, 2023 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -255,5 +255,6 @@ extern struct dfi dfi_kdump_flat; extern struct dfi dfi_devmem; extern struct dfi dfi_ngdump; +extern struct dfi dfi_vmdump; #endif /* DFI_H */ diff -Nru s390-tools-2.25.0/zdump/dfi_pv_elf.c s390-tools-2.26.0/zdump/dfi_pv_elf.c --- s390-tools-2.25.0/zdump/dfi_pv_elf.c 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/zdump/dfi_pv_elf.c 2023-02-14 14:05:17.000000000 +0100 @@ -21,7 +21,6 @@ #include #include -#include #include #include diff -Nru s390-tools-2.25.0/zdump/dfi_vmdump.c s390-tools-2.26.0/zdump/dfi_vmdump.c --- s390-tools-2.25.0/zdump/dfi_vmdump.c 1970-01-01 01:00:00.000000000 +0100 +++ s390-tools-2.26.0/zdump/dfi_vmdump.c 2023-02-14 14:05:17.000000000 +0100 @@ -0,0 +1,468 @@ +/* + * zgetdump - Tool for copying and converting IBM zSystem dumps + * + * VMDUMP input format - Convert a vmdump 64big format to internal format. + * + * Copyright IBM Corp. 2023 + * + * 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 +#include + +#include "zgetdump.h" +#include "df_vmdump.h" +#include "dfi.h" +#include "dfi_mem_chunk.h" +#include "lib/util_log.h" +#include "lib/util_libc.h" + +static struct { + struct vmd_adsr adsr; /* Dump file symptom record */ + struct vmd_fmbk fmbk; /* Dump file map record */ + struct vmd_fir_basic fir_basic; /* Dump file info record */ + struct vmd_albk albk; /* Dump access list record */ + + struct vmd_asibk_64_new asibk_new; + struct vmd_fir_64 fir; + struct vmd_fir_other_64 *fir_other; + + u64 memory_start_record; + u8 *bitmap; +} l; + +/* Convert EBCDIC to ASCII. Used for very few selective fields. */ +static void ebc_2_asc(u8 *in, u8 *out, const size_t size) +{ + iconv_t etoa = iconv_open("ISO-8859-1", "EBCDIC-US"); + size_t size_out = size; + size_t size_in = size; + size_t rc; + + rc = iconv(etoa, (char **)&in, &size_in, (char **)&out, &size_out); + if (rc == (size_t)-1) + errx(EXIT_FAILURE, "Code page translation EBCDIC-ASCII failed"); + iconv_close(etoa); +} + +static void vmdump_tod_to_timeval(u64 todval, struct timeval *xtime) +{ + /* adjust todclock to 1970 */ + todval -= 0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096); + + todval >>= 12; + xtime->tv_sec = todval / 1000000; + xtime->tv_usec = todval % 1000000; +} + +static void vmdump64big_debug(void) +{ + u8 fmbk_id[sizeof(FMBK_MAGIC)]; + u8 albk_id[sizeof(ALBK_MAGIC)]; + struct timeval time; + + util_log_print(UTIL_LOG_DEBUG, "Memory Offset: %#lx\n", l.memory_start_record); + util_log_print(UTIL_LOG_DEBUG, "Dumped Pages: %d\n", + l.asibk_new.storage_size_def_store / PAGE_SIZE); + + /* adsr */ + vmdump_tod_to_timeval(l.adsr.tod, &time); + util_log_print(UTIL_LOG_DEBUG, "Time: %s", ctime(&time.tv_sec)); + util_log_print(UTIL_LOG_DEBUG, "Statusflag 1: %#x\n", l.adsr.record_status_flag1); + util_log_print(UTIL_LOG_DEBUG, "Statusflag 2: %#x\n", l.adsr.record_status_flag2); + util_log_print(UTIL_LOG_DEBUG, "Section 2 len: %i\n", l.adsr.sec2_len); + util_log_print(UTIL_LOG_DEBUG, "Section 2.1 len: %i/%i\n", l.adsr.sec2_1_len, + l.adsr.sec2_1_offset); + util_log_print(UTIL_LOG_DEBUG, "Section 3 len: %i/%i\n", l.adsr.sec3_len, + l.adsr.sec3_offset); + util_log_print(UTIL_LOG_DEBUG, "Section 4 len: %i/%i\n", l.adsr.sec4_len, + l.adsr.sec4_offset); + util_log_print(UTIL_LOG_DEBUG, "Section 5 len: %i/%i\n", l.adsr.sec5_len, + l.adsr.sec5_offset); + util_log_print(UTIL_LOG_DEBUG, "Section 6 len: %i/%i\n", l.adsr.sec6_len, + l.adsr.sec6_offset); + + /* fmbk */ + ebc_2_asc(l.fmbk.id, fmbk_id, sizeof(l.fmbk.id)); + fmbk_id[sizeof(fmbk_id) - 1] = 0; + util_log_print(UTIL_LOG_DEBUG, "Fmbk id: %8.8s\n", fmbk_id); + util_log_print(UTIL_LOG_DEBUG, "Fir rec nr: %i\n", l.fmbk.rec_nr_fir); + util_log_print(UTIL_LOG_DEBUG, "Vec rec nr: %i\n", l.fmbk.rec_nr_vector); + util_log_print(UTIL_LOG_DEBUG, "Access rec nr: %i\n", l.fmbk.rec_nr_access); + + /* albk */ + ebc_2_asc(l.albk.id, albk_id, sizeof(l.albk.id)); + albk_id[sizeof(albk_id) - 1] = 0; + util_log_print(UTIL_LOG_DEBUG, "Albk id: %8.8s\n", albk_id); + + /* fir */ + util_log_print(UTIL_LOG_DEBUG, "Cpus: %d\n", l.fir.online_cpus + 1); + util_log_print(UTIL_LOG_DEBUG, "PSW: %#016lx %#016lx\n", l.fir.psw[0], l.fir.psw[1]); + util_log_print(UTIL_LOG_DEBUG, "Prefix (CPU 0): %#010x\n", l.fir.prefix); + for (unsigned int i = 0; i < l.fir.online_cpus; i++) + util_log_print(UTIL_LOG_DEBUG, "Prefix (CPU %i): %#010x\n", i + 1, + l.fir_other[i].prefix); +} + +static bool test_bitmap_key_page(u8 *page, const u64 bit) +{ + return page[bit] & 0x01; +} + +static bool test_page_bit(u8 *bitmap, const u64 bit) +{ + return bitmap[bit / 8] & (1 << (7 - (bit % 8))); +} + +static void set_page_bit(u8 *bitmap, const u64 bit) +{ + bitmap[bit / 8] |= (1 << (7 - (bit % 8))); +} + +/* + * Read VMDUMP headers, page index bit maps and page bit maps to + * construct a list of non-zero pages with memory locations. Zeroed pages + * are not bitmapped and are detected by memory location without bitmap + * entry. + */ +static void vmdump64big_init(void) +{ + u64 page_num = 0, nr_dumped_pages; + size_t bitmap_sz; + unsigned int i; + + /* Record 1: adsr */ + zg_seek(g.fh, 0, ZG_CHECK); + zg_read(g.fh, &l.adsr, sizeof(l.adsr), ZG_CHECK); + + if (g.opts.verbose) { + u8 buf_asc[1024], buf[1024]; + + zg_seek(g.fh, l.adsr.sec5_offset, ZG_CHECK); + zg_read(g.fh, buf, l.adsr.sec5_len, ZG_CHECK); + ebc_2_asc(buf, buf_asc, l.adsr.sec5_len); + for (i = 0; i < l.adsr.sec5_len; i++) { + if (buf_asc[i] == 0 || iscntrl(buf_asc[i])) + buf_asc[i] = ' '; + } + buf_asc[l.adsr.sec5_len] = 0; + util_log_print(UTIL_LOG_DEBUG, "Symptom string: %s\n", buf_asc); + } + + /* Record 2: fmbk */ + zg_seek(g.fh, PAGE_SIZE, ZG_CHECK); + zg_read(g.fh, &l.fmbk, sizeof(l.fmbk), ZG_CHECK); + + /* Record 3-7: fir records */ + zg_seek(g.fh, (l.fmbk.rec_nr_fir - 1) * PAGE_SIZE, ZG_CHECK); + zg_read(g.fh, &l.fir, sizeof(l.fir), ZG_CHECK); + + bitmap_sz = sizeof(struct vmd_fir_other_64) * l.fir.online_cpus; + l.fir_other = util_zalloc(bitmap_sz); + for (i = 0; i < l.fir.online_cpus; i++) + zg_read(g.fh, &l.fir_other[i], sizeof(l.fir_other[0]), ZG_CHECK); + + /* Record 8: albk */ + zg_seek(g.fh, (l.fmbk.rec_nr_access - 1) * PAGE_SIZE, SEEK_SET); + zg_read(g.fh, &l.albk, sizeof(l.albk), SEEK_SET); + + /* Record 9: asibk */ + zg_seek(g.fh, l.fmbk.rec_nr_access * PAGE_SIZE, ZG_CHECK); + zg_read(g.fh, &l.asibk_new, sizeof(l.asibk_new), ZG_CHECK); + + l.memory_start_record = (l.fmbk.rec_nr_access + 1) * PAGE_SIZE; + + /* + * Record 10: bitmaps: + * Read all bitmap pages and setup bitmap array + */ + nr_dumped_pages = l.asibk_new.storage_size_def_store / PAGE_SIZE; + bitmap_sz = l.asibk_new.storage_size_def_store / (PAGE_SIZE * 8); + if (!bitmap_sz) + ERR_EXIT("Dump file inconsistent, no bitmap detected"); + l.bitmap = util_zalloc(bitmap_sz); + zg_seek(g.fh, (l.fmbk.rec_nr_access + 1) * PAGE_SIZE, ZG_CHECK); + + do { + u8 bm_index_page[PAGE_SIZE]; + + zg_read(g.fh, bm_index_page, sizeof(bm_index_page), ZG_CHECK); + l.memory_start_record += PAGE_SIZE; + for (i = 0; i < PAGE_SIZE; i++) { + if (test_page_bit(bm_index_page, i)) { + u8 bm_page[PAGE_SIZE]; + + zg_read(g.fh, bm_page, sizeof(bm_page), ZG_CHECK); + l.memory_start_record += PAGE_SIZE; + for (unsigned int j = 0; j < PAGE_SIZE; j++) { + if (page_num / 8 >= bitmap_sz) + ERR_EXIT("Dump file inconsistent," + " corrupted bitmap detected"); + if (test_bitmap_key_page(bm_page, j)) + set_page_bit(l.bitmap, page_num); + page_num++; + if (page_num == nr_dumped_pages) + goto out; + } + } else { + page_num += PAGE_SIZE; /* Empty page */ + } + } + } while (page_num < nr_dumped_pages); + +out: + vmdump64big_debug(); +} + +static void display_register(const struct dfi_cpu *cpu) +{ + unsigned int i; + + util_log_print(UTIL_LOG_TRACE, "CPU %d\n", cpu->cpu_id); + for (i = 0; i < ARRAY_SIZE(cpu->gprs); i += 2) + util_log_print(UTIL_LOG_TRACE, "gpr%02d: %016lx\t gpr%02d: %016lx\n", i, + cpu->gprs[i], i + 1, cpu->gprs[i + 1]); + for (i = 0; i < ARRAY_SIZE(cpu->ctrs); i += 2) + util_log_print(UTIL_LOG_TRACE, "ctr%02d: %016lx\t ctr%02d: %016lx\n", i, + cpu->ctrs[i], i + 1, cpu->ctrs[i + 1]); + for (i = 0; i < ARRAY_SIZE(cpu->acrs); i += 2) + util_log_print(UTIL_LOG_TRACE, "acr%02d: %016lx\t acr%02d: %016lx\n", i, + cpu->acrs[i], i + 1, cpu->acrs[i + 1]); + for (i = 0; i < ARRAY_SIZE(cpu->fprs); i += 2) + util_log_print(UTIL_LOG_TRACE, "fpr%02d: %016lx\t fpr%02d: %016lx\n", i, + cpu->fprs[i], i + 1, cpu->fprs[i + 1]); +} + +/* + * Read out register setting for each CPU. + */ +static void vmdump_read_register(int cpu_nr) +{ + struct dfi_cpu *cpu; + + if (cpu_nr >= l.fir.online_cpus + 1) + return; + + cpu = dfi_cpu_alloc(); + cpu->cpu_id = dfi_cpu_cnt(); + if (!cpu_nr) { /* First CPU in fir */ + memcpy(cpu->gprs, l.fir.gprs, sizeof(cpu->gprs)); + memcpy(cpu->ctrs, &l.fir.crs, sizeof(cpu->ctrs)); + memcpy(cpu->acrs, &l.fir.acrs, sizeof(cpu->acrs)); + memcpy(cpu->fprs, &l.fir.fprs, sizeof(cpu->fprs)); + memcpy(cpu->psw, &l.fir.psw, sizeof(cpu->psw)); + memcpy(&cpu->prefix, &l.fir.prefix, sizeof(cpu->prefix)); + memcpy(&cpu->timer, &l.fir.cpu_timer, sizeof(cpu->timer)); + memcpy(&cpu->todcmp, &l.fir.clock_cmp, sizeof(cpu->todcmp)); + memcpy(&cpu->fpc, &l.fir.fp_cntrl_reg, sizeof(cpu->fpc)); + } else { + cpu_nr -= 1; /* Other CPUs start at offset 0 in fir_other */ + memcpy(cpu->gprs, l.fir_other[cpu_nr].gprs, sizeof(cpu->gprs)); + memcpy(cpu->ctrs, &l.fir_other[cpu_nr].crs, sizeof(cpu->ctrs)); + memcpy(cpu->acrs, &l.fir_other[cpu_nr].acrs, sizeof(cpu->acrs)); + memcpy(cpu->fprs, &l.fir_other[cpu_nr].fprs, sizeof(cpu->fprs)); + memcpy(cpu->psw, &l.fir_other[cpu_nr].psw, sizeof(cpu->psw)); + memcpy(&cpu->prefix, &l.fir_other[cpu_nr].prefix, sizeof(cpu->prefix)); + memcpy(&cpu->timer, &l.fir_other[cpu_nr].cpu_timer, sizeof(cpu->timer)); + memcpy(&cpu->todcmp, &l.fir_other[cpu_nr].clock_cmp, sizeof(cpu->todcmp)); + memcpy(&cpu->fpc, &l.fir_other[cpu_nr].fp_cntrl_reg, sizeof(cpu->fpc)); + } + display_register(cpu); + dfi_cpu_add(cpu); +} + +/* + * Initialize z/VM VMDUMP file DFI. Check if the input file is vmdump file + * with format 64big. Other formats are not supported. + */ +static int read_vmdump_hdr(void) +{ + if (zg_type(g.fh) != ZG_TYPE_FILE) + return -EBADF; + if (zg_size(g.fh) < DF_VMDUMP_HDR_SIZE) + return -ENODEV; + zg_read(g.fh, &l.adsr, sizeof(l.adsr), ZG_CHECK); + if (memcmp(l.adsr.sr, ADSR_MAGIC, sizeof(l.adsr.sr))) + return -EBADF; /* Not an ADSR record */ + if (memcmp(l.adsr.dump_type, VMDUMP_MAGIC, sizeof(l.adsr.dump_type))) + return -EBADF; /* Not a vmdump */ + zg_seek(g.fh, PAGE_SIZE, ZG_CHECK); + zg_read(g.fh, &l.fmbk, sizeof(l.fmbk), ZG_CHECK); + if (memcmp(l.fmbk.id, FMBK_MAGIC, sizeof(l.fmbk.id))) + return -EBADF; /* Not a FMBK record */ + + /* Record 3-7: fir */ + zg_seek(g.fh, (l.fmbk.rec_nr_fir - 1) * PAGE_SIZE, ZG_CHECK); + zg_read(g.fh, &l.fir, sizeof(l.fir), ZG_CHECK); + switch (l.fir.fir_format) { + case 0x0: + case 0x82: + util_log_print(UTIL_LOG_DEBUG, "Vmdump %d bit format not supported anymore\n", + l.fir.fir_format ? 64 : 32); + return -EBADF; + case 0x2: + util_log_print(UTIL_LOG_DEBUG, "%s: Vmdump 64big format\n", g.opts.device); + break; + default: + util_log_print(UTIL_LOG_DEBUG, "Vmdump unknown format\n"); + return -EBADF; + } + + return 0; +} + +/* + * Read vmdump page. Need to check if page was dumped or is located in a + * hole. Holes are ranges of pages full of zeroes. + * The vmdump file format has a 64KB header followed by some bitmaps for + * valid non-zero pages and the 4KB memory pages itself. Only non-zero pages + * are stored. The memory pages start at vmdump file location stored in + * member named memory_start_record. + * + * Bit map organization: + * Page 0: bitmap byte 0 bit 0 (most signification bit) + * Page 1: bitmap byte 0 bit 1 (seconds most signification bit) + * ... + * Page 15: bitmap byte 1 bit 7 (least signification bit) + * + * If a bit for a page is set, this page is stored in the vmdump file. + * The location in the file depends on the number of previous non-zero pages. + * If bitmap byte zero has value 1 and bitmap byte one has value 5, three + * bits are set and page 15 is located at file offset: + * memory_start_record + 2 * PAGE_SIZE. + * + * Calculate the number of bits set to determine the file location of a + * given page in the vmdump file. + */ +static unsigned int count_bits(u8 x) +{ + unsigned int bits_set = 0; + + while (x) { + bits_set += x & 1; + x >>= 1; + } + return bits_set; +} + +/* + * Return byte offset into vmdump file for a given page number. + */ +static u64 bitmap_2_fileoffset(const u64 pg_num) +{ + unsigned int bytes_to_check = pg_num / 8; + unsigned int bits_to_check = pg_num % 8; + unsigned int bits_set = 0; + + /* Count bits set in first to second last byte */ + for (unsigned int i = 0; i < bytes_to_check; i++) + bits_set += count_bits(l.bitmap[i]); + + /* Count bits set in last byte */ + bits_set += count_bits(l.bitmap[bytes_to_check] >> (8 - bits_to_check)); + return bits_set * PAGE_SIZE; +} + +static void read_page(const u64 pg_num, void *buf) +{ + if (test_page_bit(l.bitmap, pg_num)) { + u64 file_off = bitmap_2_fileoffset(pg_num); + + zg_seek(g.fh, l.memory_start_record + file_off, ZG_CHECK); + zg_read(g.fh, buf, PAGE_SIZE, ZG_CHECK); + } else { + memset(buf, 0, PAGE_SIZE); + } +} + +/* + * VMDUMP mem chunk read callback + */ +static void dfi_vmdump_mem_chunk_read_fn(struct dfi_mem_chunk *mem_chunk, u64 off, void *buf, + u64 cnt) +{ + u64 copied = 0, size, pg_nr, addr = off + mem_chunk->start; + char pg_buf[PAGE_SIZE]; + unsigned int pg_off; + + while (copied != cnt) { + pg_nr = (addr + copied) / PAGE_SIZE; + pg_off = (addr + copied) % PAGE_SIZE; + size = MIN(cnt - copied, PAGE_SIZE - pg_off); + read_page(pg_nr, pg_buf); + memcpy(buf + copied, &pg_buf[pg_off], size); + copied += size; + } +} + +static void cpu_init(void) +{ + dfi_cpu_info_init(DFI_CPU_CONTENT_ALL); + for (unsigned int i = 0; i <= l.fir.online_cpus; i++) + vmdump_read_register(i); +} + +/* + * Walk bitmap and find consecutive bitstring consisting of '1' or '0'. + * Return its length. + */ +static u64 find_bitrange(u8 *bitmap, const u64 bit, const u64 max, const bool isset) +{ + u64 pos; + + for (pos = bit + 1; pos < max && test_page_bit(bitmap, pos) == isset; pos++) + ; + return pos - bit; +} + +static void mem_init(void) +{ + u64 nr_dumped_pages = l.asibk_new.storage_size_def_store / PAGE_SIZE; + u64 pos, more; + + for (pos = 0; pos < nr_dumped_pages;) { + bool isset = test_page_bit(l.bitmap, pos); + + more = find_bitrange(l.bitmap, pos, nr_dumped_pages, isset); + dfi_mem_chunk_add(pos * PAGE_SIZE, more * PAGE_SIZE, NULL, + isset ? dfi_vmdump_mem_chunk_read_fn : dfi_mem_chunk_read_zero, + NULL); + pos += more; + } +} + +static int dfi_vmdump_init(void) +{ + if (read_vmdump_hdr()) + return -ENODEV; + + vmdump64big_init(); + dfi_attr_version_set(l.fir.fir_format); + dfi_arch_set(DFI_ARCH_64); + dfi_attr_real_cpu_cnt_set(l.fir.online_cpus + 1); + mem_init(); + cpu_init(); + return 0; +} + +/* + * z/VM VMDUMP DFI operations + */ +struct dfi dfi_vmdump = { + .name = "vmdump", + .init = dfi_vmdump_init, + .feat_bits = DFI_FEAT_SEEK | DFI_FEAT_COPY, +}; diff -Nru s390-tools-2.25.0/zdump/df_vmdump.h s390-tools-2.26.0/zdump/df_vmdump.h --- s390-tools-2.25.0/zdump/df_vmdump.h 1970-01-01 01:00:00.000000000 +0100 +++ s390-tools-2.26.0/zdump/df_vmdump.h 2023-02-14 14:05:17.000000000 +0100 @@ -0,0 +1,208 @@ +/* + * zgetdump - Tool for copying and converting System z dumps + * + * VMDUMP format definitions + * + * Copyright IBM Corp. 2023 + * + * 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 DF_VMDUMP_H +#define DF_VMDUMP_H + +/* + * The format of an vmdump + * + * Symptom Record: (ADSR COPY) Record 1 + * Dump File Map Record: (HCPDFMBK COPY) Record 2 + * Dump File Info Record: (HCPDFIR COPY) Records 3-7 + * Vector Registers: (optional) + * Access Lists (HCPDALBK): + * Address Space A: Information and Map Record (HCPASIBK) + * Address Space A: Bit Maps + * Address Space A: Key Maps + * Address Space A: Guest Storage + * Additional Address Spaces (not in linux): + * - ASIBK + * - Bit Maps + * - Key Maps + * - Guest Storage + */ + +/* + * Defines for VMDUMP magic numbers. + */ +/* ID 0xe2d9 --> 'S' 'R' */ +const u8 ADSR_MAGIC[2] = { 0xe2, 0xd9 }; +/* ID e5d4c4e4d4d74040 --> 'V' 'M' 'D' 'U' 'M' 'P' ' ' ' ' */ +const u8 VMDUMP_MAGIC[8] = { 0xe5, 0xd4, 0xc4, 0xe4, 0xd4, 0xd7, 0x40, 0x40 }; +/* ID c8c3d7c4c6d4c2d3 --> 'H' 'C' 'P' 'D' 'F' 'M' 'B' 'K' */ +const u8 FMBK_MAGIC[8] = { 0xc8, 0xc3, 0xd7, 0xc4, 0xc6, 0xd4, 0xc2, 0xd2 }; +/* ID c8c3d7c4c6d4c2d3 --> 'H' 'C' 'P' 'D' 'A' 'L' 'B' 'K' */ +const u8 ALBK_MAGIC[8] = { 0xc8, 0xc3, 0xd7, 0xc4, 0xc1, 0xd3, 0xc2, 0xd2 }; + +/* + * Layout of dump symptom record . The ADSR is always the first record in + * a dump file. Its size varies between 200 bytes and 4000 bytes + */ +struct vmd_adsr { + /* Section 1*/ + u8 sr[sizeof(ADSR_MAGIC)]; + u32 cpu_model; + u8 cpu_serial[6]; + u32 time_zone_conversion_factor; + u64 tod; + u8 time_stamp_str[4]; + u8 date_str[6]; + u8 node_name[8]; + u8 product_id[4]; + u8 feature_level[8]; + u8 record_status_flag1; + u8 record_status_flag2; + u8 dump_type[sizeof(VMDUMP_MAGIC)]; + + /* Section 2*/ + u8 arch_level[2]; + u16 sec2_len; + u16 sec2_1_len; + u16 sec2_1_offset; + u16 sec3_len; + u16 sec3_offset; + u16 sec4_len; + u16 sec4_offset; + u16 sec5_len; + u16 sec5_offset; + u16 sec6_len; + u16 sec6_offset; +} __packed; + +/* + * Layout of Dump file map record. The DFMBK is always the second record in + * a dump file. Its size is 4 KB + */ +struct vmd_fmbk { + u8 id[sizeof(FMBK_MAGIC)]; + u32 rec_nr_fir; + u32 rec_nr_vector; + u32 rec_nr_access; + u32 num_acc_recs; + u32 num_addr_spaces; + u32 rec_nr_asibk; +} __packed; + +struct vmd_fir_basic { /* Dump file (basic) information record */ + u8 filler1[15]; + u8 dump_format; /* 0x1 --> big storage dump + * 0x2 --> cp hard abend + * 0x3 --> cp soft abend + */ + u8 filler2[171]; + u8 fir_format; /* 0x02 --> big esame (only one supported) + * 0x82 --> esame + * 0x00 --> esa + */ +} __packed; + +struct vmd_albk { + u8 id[sizeof(ALBK_MAGIC)]; +} __packed; + +struct vmd_asibk_64_new { + u8 id[8]; + u8 as_token[8]; + u8 spaceid[33]; + u8 reserved1[2]; + u8 asibk_format; + u8 filler1[12]; + u64 storage_size_with_dcss; + u64 storage_size_def_store; + u8 filler2[136]; + u64 online_storage_table[8]; /* For "def store config" */ + u64 fence1; + u64 requested_range_table[8]; + u64 fence2; + u32 record_number_of_first_bit_map; +} __packed; + +struct vmd_fir_64 { + u8 id[8]; + u64 reserved1; + u64 gprs[16]; + u32 prefix; + u8 reserved2[5]; + u64 tod; + u8 reserved3[8]; + u64 cpu_timer; + u8 reserved4[7]; + u8 flag; + u8 type; + u8 complete; + u8 fir_format; /* 0x82 for esame - 0x00 for esa */ + u8 cont_flags; + u8 crypto_domain_index_reg; + u8 virt_cpu_info; + u8 arch_mode_id; + u64 psw[2]; + u64 crs[16]; + u64 fprs[16]; + u8 reserved5; + u64 clock_cmp; + u8 reserved6[3]; + u32 tod_programmable_reg; + u32 reserved_for_dvf[20]; + u32 acrs[16]; + u32 storage_size_2GB; + u32 reserved7; + u32 hcpsys_addr; + u32 reserved8; + u64 storage_size; + u32 snap_area_map_blk; + u32 reserved9; + u8 loc_mem[256]; + u16 online_cpus; + u16 cpu_addr; + u16 section_size_vector; + u16 reserved10; + u8 asit_primary[8]; + u8 space_id_primary[33]; + u8 reserved12[3]; + u16 crypto_domain_index_mask; + u16 reserved11; + u32 fp_cntrl_reg; + u32 reserved13; + u64 reserved14[16]; +} __packed; + +struct vmd_fir_other_64 { + u16 cpu_addr; + u16 vector_sec_size; + u8 crypto_index_reg; + u8 virt_cpu_info; + u16 crypto_index_mask; + u32 reserved1[2]; + u64 fprs[16]; + u64 gprs[16]; + u64 psw[2]; + u32 reserved2[2]; + u32 prefix; + u32 fp_cntrl_reg; + u32 reserved3; + u32 tod; + u64 cpu_timer; + u64 clock_cmp; + u32 reserved4[2]; + u32 acrs[16]; + u64 crs[16]; + u64 mc_interrupt_code; + u32 reserved5; + u32 external_damage_code; + u64 mc_failing_storage_addr; +} __packed; + +#define DF_VMDUMP_HDR_SIZE \ + (sizeof(struct vmd_adsr) + sizeof(struct vmd_fmbk) + sizeof(struct vmd_fir_basic) + \ + sizeof(struct vmd_albk) + sizeof(struct vmd_asibk_64_new) + sizeof(struct vmd_fir_64)) + +#endif diff -Nru s390-tools-2.25.0/zdump/Makefile s390-tools-2.26.0/zdump/Makefile --- s390-tools-2.25.0/zdump/Makefile 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/zdump/Makefile 2023-02-14 14:05:17.000000000 +0100 @@ -76,7 +76,7 @@ dt.o dt_s390sv.o dt_s390sv_ext.o \ dt_s390mv.o dt_s390mv_ext.o \ dt_scsi.o output.o \ - ngdump.o dt_ngdump.o dfi_ngdump.o \ + ngdump.o dt_ngdump.o dfi_ngdump.o dfi_vmdump.o \ pv_utils.o ifneq ($(shell sh -c 'command -v pkg-config'),) @@ -119,7 +119,9 @@ all: $(BUILD_TARGETS) -zgetdump: .check_dep_zgetdump $(OBJECTS) $(libs) +$(OBJECTS): .check_dep_zgetdump + +zgetdump: $(OBJECTS) $(libs) skip-zgetdump: echo " SKIP zgetdump due to unresolved dependencies" diff -Nru s390-tools-2.25.0/zdump/pv_utils.c s390-tools-2.26.0/zdump/pv_utils.c --- s390-tools-2.25.0/zdump/pv_utils.c 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/zdump/pv_utils.c 2023-02-14 14:05:17.000000000 +0100 @@ -674,7 +674,7 @@ size_t mapped_size; pv_tweak_component_t *tweak_components; size_t num_tweaks; - gatomicrefcount ref_count; + int ref_count; }; storage_state_mmap_t *storage_state_mmap_new(const int fd, const size_t file_size, const u64 offset, @@ -733,14 +733,14 @@ ret->tweak_components = (pv_tweak_component_t *)(ptr + in_page_offset); ret->num_tweaks = tweak_components_cnt; ret->mapped_size = mmapped_size; - g_atomic_ref_count_init(&ret->ref_count); + ret->ref_count = 1; return g_steal_pointer(&ret); } storage_state_mmap_t *storage_state_mmap_ref(storage_state_mmap_t *storage_state) { g_assert(storage_state); - g_atomic_ref_count_inc(&storage_state->ref_count); + g_atomic_int_inc(&storage_state->ref_count); return storage_state; } @@ -748,7 +748,7 @@ { if (!storage_state) return; - if (storage_state->ref_count && !g_atomic_ref_count_dec(&storage_state->ref_count)) + if (storage_state->ref_count && !g_atomic_int_dec_and_test(&storage_state->ref_count)) return; if (storage_state->first_page_ptr) { int rc = munmap(storage_state->first_page_ptr, storage_state->mapped_size); diff -Nru s390-tools-2.25.0/zdump/zgetdump.8 s390-tools-2.26.0/zdump/zgetdump.8 --- s390-tools-2.25.0/zdump/zgetdump.8 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/zdump/zgetdump.8 2023-02-14 14:05:17.000000000 +0100 @@ -1,10 +1,10 @@ -.\" Copyright 2022 IBM Corp. +.\" Copyright 2023 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 ZGETDUMP 8 "October 2022" "s390-tools" +.TH ZGETDUMP 8 "January 2023" "s390-tools" .SH NAME -zgetdump \- Tool for copying and converting System z dumps +zgetdump \- Tool for copying and converting IBM zSystems dumps .SH SYNOPSIS \fBzgetdump\fR DUMP [-s SYS] [-f FMT] [-k KEY] > DUMP_FILE @@ -107,6 +107,8 @@ .IP " -" 12 Regular dump file (e.g. /dumps/dump.0) .IP " -" 12 +vmdump file (e. g. VMDUMP.FILE) +.IP " -" 12 DASD partition device node (e.g. /dev/dasdc1) .IP " -" 12 DASD device node for multi-volume dump (e.g. /dev/dasdc) @@ -186,16 +188,21 @@ Protected virtualization vmcore dump in Executable and Linkable Format .TP .BR "s390" -This dump format is System z specific and is used for DASD and tape dumps. +This dump format is IBM zSystems specific and is used for DASD and tape dumps. .TP The following dump formats are supported for the source dump only: .TP .BR "s390_ext" -This dump format is System z specific and is used for DASD dumps only. +This dump format is IBM zSystems specific and is used for DASD dumps only. +.TP +.BR "vmdump" +Dumps with this format are created by the z/VM vmdump command +and stored in the reader device. +Use the "vmur" tool to extract such dumps from the reader +device and save them on disk. .TP .BR "lkcd" -This dump format is used by the Linux Kernel Crash Dumps (LKCD) project -and also on System z for the "vmconvert" dump tool. +This dump format is used by the Linux Kernel Crash Dumps (LKCD) project. .TP .BR "devmem" On live systems the /dev/mem or /dev/crash device nodes can be used as source @@ -505,5 +512,5 @@ for example via the "/dev/disk/by-path/" device nodes. .SH SEE ALSO -.BR zipl (8), crash (8), makedumpfile (8), dumpconf (8), vmconvert (1), vmur (8) +.BR zipl (8), crash (8), makedumpfile (8), dumpconf (8), vmur (8) .BR fdisk (8), parted (8) diff -Nru s390-tools-2.25.0/zipl/boot/entry.S s390-tools-2.26.0/zipl/boot/entry.S --- s390-tools-2.25.0/zipl/boot/entry.S 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/zipl/boot/entry.S 2023-02-14 14:05:17.000000000 +0100 @@ -18,3 +18,8 @@ basr %r14,%r14 lmg %r0,%r15,__LC_SAVE_AREA_SYNC lpswe __LC_PGM_OLD_PSW(%r0) + +/* The code doesn't require an executable stack */ +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff -Nru s390-tools-2.25.0/zipl/boot/head.S s390-tools-2.26.0/zipl/boot/head.S --- s390-tools-2.25.0/zipl/boot/head.S 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/zipl/boot/head.S 2023-02-14 14:05:17.000000000 +0100 @@ -28,3 +28,8 @@ brasl %r14,initialize .Lstack: .long 0x10000-160 .previous + +/* The code doesn't require an executable stack */ +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff -Nru s390-tools-2.25.0/zipl/boot/Makefile s390-tools-2.26.0/zipl/boot/Makefile --- s390-tools-2.25.0/zipl/boot/Makefile 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/zipl/boot/Makefile 2023-02-14 14:05:17.000000000 +0100 @@ -19,7 +19,10 @@ tape0.bin \ eckd2dump_sv.bin tape2dump.bin fba2dump.bin eckd2dump_mv.bin -all: data.o data.h tape0.bin stage3.bin +all: .loaders tape0.bin stage3.bin + +.loaders: $(FILES) + touch .loaders # Prevent make from using some default rules... %: %.S @@ -41,6 +44,15 @@ %.lds: %.lds.S $(CPP) -Wp,-MD,.$@.d,-MT,$@ $(INCLUDE_PARMS) -P -C -o $@ $< +fba0.exec eckd0_ldl.exec eckd0_cdl.exec tape0.exec: \ + stage0.lds + +eckd1.exec: \ + stage1.lds + +eckd1b.exec fba1b.exec: \ + stage1b.lds + eckd2dump_sv.exec: \ head.o stage2dump.o cio.o eckd2dump.o eckd2dump_sv.o \ libc.o ebcdic.o sclp.o entry.o stage2.lds @@ -61,64 +73,14 @@ sclp_stage3.o kdump.o entry.o stage3.lds %.exec: %.o - STAGE=$$( \ - echo $@ | awk ' \ - match($$0,/[0-9]+b*/){ \ - print substr($$0,RSTART,RLENGTH) \ - }' \ - ); \ - case $$STAGE in \ - 0) SFLAGS="-Wl,-Ttext,0";; \ - 1) SFLAGS="-Wl,-Ttext,0x18";; \ - 1b) SFLAGS="-Wl,-Ttext,0xE000";; \ - 2) SFLAGS="-Wl,-T,stage2.lds";; \ - 3) SFLAGS="-Wl,-T,stage3.lds";; \ - esac; \ - $(LINK) $$SFLAGS $(NO_PIE_LDFLAGS) -m64 -static -nostdlib $(filter %.o, $^) -o $@ + $(LINK) -Wl,-T,$(filter %.lds,$^) $(NO_PIE_LDFLAGS) $(NO_WARN_RWX_SEGMENTS_LDFLAGS) -Wl,--build-id=none -m64 -static -nostdlib $(filter %.o, $^) -o $@ %.bin: %.exec - $(OBJCOPY) -O binary \ - --only-section=.stage2.head \ - --only-section=.text.dummy \ - --only-section=.text.start \ - --only-section=.text \ - --only-section=.ex_table \ - --only-section=.data \ - --only-section=.rodata.str1.2 \ - --only-section=.rodata.cst8 \ - --only-section=.rodata \ - --only-section=.stage2dump.tail \ - --only-section=.eckd2dump_mv.tail \ - --only-section=.fixup \ - $< $@ - -stage3.bin: stage3.exec - $(OBJCOPY) -O binary \ - --only-section=.stage2.head \ - --only-section=.text.dummy \ - --only-section=.text.start \ - --only-section=.text \ - --only-section=.ex_table \ - --only-section=.data \ - --only-section=.rodata.str1.2 \ - --only-section=.rodata.cst8 \ - --only-section=.rodata \ - --only-section=.stage2dump.tail \ - --only-section=.eckd2dump_mv.tail \ - --only-section=.fixup \ - --only-section=.sb.trailer \ - $< $@ - -data.o: $(FILES) - $(LINK) $(NO_PIE_LDFLAGS) -static -nostdlib -Wl,--relocatable -Wl,--format,binary -o data.o $(FILES) - -data.h: data.o - rm -f data.h - $(NM) data.o | while read ADDR TYPE SYMBOL ; do \ - echo "extern char $$SYMBOL;" >>data.h; done + $(OBJCOPY) -O binary $< $@ + clean: - rm -f -- *.o *.exec *.bin $(FILES) data.o data.h tape0.bin *.xxx *.yyy \ - stage3.bin *.lds .*.lds.d + rm -f -- *.o *.exec *.bin $(FILES) tape0.bin *.xxx *.yyy \ + stage3.bin *.lds .*.lds.d .loaders .PHONY: all clean diff -Nru s390-tools-2.25.0/zipl/boot/stage0.lds.S s390-tools-2.26.0/zipl/boot/stage0.lds.S --- s390-tools-2.25.0/zipl/boot/stage0.lds.S 1970-01-01 01:00:00.000000000 +0100 +++ s390-tools-2.26.0/zipl/boot/stage0.lds.S 2023-02-14 14:05:17.000000000 +0100 @@ -0,0 +1,18 @@ +#include "boot/loaders_layout.h" + +ENTRY(_start) + +SECTIONS +{ + . = STAGE0_LOAD_ADDRESS; + .text : { + *(.text .text.*) + } + + /* Sections to be discarded */ + /DISCARD/ : { + *(.eh_frame) + *(.interp) + *(.note.GNU-stack) + } +} diff -Nru s390-tools-2.25.0/zipl/boot/stage1b.lds.S s390-tools-2.26.0/zipl/boot/stage1b.lds.S --- s390-tools-2.25.0/zipl/boot/stage1b.lds.S 1970-01-01 01:00:00.000000000 +0100 +++ s390-tools-2.26.0/zipl/boot/stage1b.lds.S 2023-02-14 14:05:17.000000000 +0100 @@ -0,0 +1,18 @@ +#include "boot/loaders_layout.h" + +ENTRY(_start) + +SECTIONS +{ + . = STAGE1B_LOAD_ADDRESS; + .text : { + *(.text .text.*) + } + + /* Sections to be discarded */ + /DISCARD/ : { + *(.eh_frame) + *(.interp) + *(.note.GNU-stack) + } +} diff -Nru s390-tools-2.25.0/zipl/boot/stage1.lds.S s390-tools-2.26.0/zipl/boot/stage1.lds.S --- s390-tools-2.25.0/zipl/boot/stage1.lds.S 1970-01-01 01:00:00.000000000 +0100 +++ s390-tools-2.26.0/zipl/boot/stage1.lds.S 2023-02-14 14:05:17.000000000 +0100 @@ -0,0 +1,18 @@ +#include "boot/loaders_layout.h" + +ENTRY(_start) + +SECTIONS +{ + . = STAGE1_LOAD_ADDRESS; + .text : { + *(.text .text.*) + } + + /* Sections to be discarded */ + /DISCARD/ : { + *(.eh_frame) + *(.interp) + *(.note.GNU-stack) + } +} diff -Nru s390-tools-2.25.0/zipl/boot/stage2.lds.S s390-tools-2.26.0/zipl/boot/stage2.lds.S --- s390-tools-2.25.0/zipl/boot/stage2.lds.S 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/zipl/boot/stage2.lds.S 2023-02-14 14:05:17.000000000 +0100 @@ -88,6 +88,10 @@ } __stack_end = .; - .eh_frame : { *(.eh_frame) } - .note.gnu.build-id : { *(.note.gnu.build-id) } + /* Sections to be discarded */ + /DISCARD/ : { + *(.eh_frame) + *(.interp) + *(.note.GNU-stack) + } } diff -Nru s390-tools-2.25.0/zipl/boot/stage3.lds.S s390-tools-2.26.0/zipl/boot/stage3.lds.S --- s390-tools-2.25.0/zipl/boot/stage3.lds.S 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/zipl/boot/stage3.lds.S 2023-02-14 14:05:17.000000000 +0100 @@ -39,7 +39,6 @@ __ex_table_start = .; .ex_table : { *(.ex_table) } __ex_table_stop = .; - .eh_frame : { *(.eh_frame) } __bss_start = .; .bss : { *(.bss) } @@ -70,9 +69,10 @@ } __stack_end = .; - /* List this explicitly as otherwise .note.gnu.build-id will be - * put at 0x0 */ - .notes : { - *(.note.*) + /* Sections to be discarded */ + /DISCARD/ : { + *(.eh_frame) + *(.interp) + *(.note.GNU-stack) } } diff -Nru s390-tools-2.25.0/zipl/include/install.h s390-tools-2.26.0/zipl/include/install.h --- s390-tools-2.25.0/zipl/include/install.h 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/zipl/include/install.h 2023-02-14 14:05:17.000000000 +0100 @@ -74,6 +74,9 @@ int fd; char *device; char *filename; + char *dump_mount_point; + unsigned int dump_tmp_dir_created:1; + unsigned int dump_mounted:1; struct disk_info *info; disk_blockptr_t scsi_dump_sb_blockptr; }; diff -Nru s390-tools-2.25.0/zipl/include/zipl.h s390-tools-2.26.0/zipl/include/zipl.h --- s390-tools-2.25.0/zipl/include/zipl.h 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/zipl/include/zipl.h 2023-02-14 14:05:17.000000000 +0100 @@ -25,6 +25,7 @@ #define BOOTMAP_FILENAME "bootmap" #define BOOTMAP_TEMPLATE_FILENAME "bootmap_temp.XXXXXX" +#define DUMP_TEMP_MOUNT_POINT_NAME "zipl-dump-mount-point-XXXXXX" #define DEFAULTBOOT_SECTION "defaultboot" diff -Nru s390-tools-2.25.0/zipl/src/boot.c s390-tools-2.26.0/zipl/src/boot.c --- s390-tools-2.25.0/zipl/src/boot.c 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/zipl/src/boot.c 2023-02-14 14:05:17.000000000 +0100 @@ -17,27 +17,61 @@ #include #include #include "lib/util_libc.h" +#include "boot/loaders_layout.h" #include "stage3.h" -#include "../boot/data.h" #include "boot.h" #include "bootmap.h" #include "error.h" #include "misc.h" -#define DATA_SIZE(x) ((size_t) (&_binary_##x##_bin_end - &_binary_##x##_bin_start)) -#define DATA_ADDR(x) (&_binary_##x##_bin_start) +/* Import a binary file */ +/* clang-format off */ +#define DATA_NAME(SYM, SUFFIX) _binary_##SYM##_bin##SUFFIX +#define DATA_SIZE(SYM) ((size_t)(&DATA_NAME(SYM, _end) - &DATA_NAME(SYM, _start))) +#define DATA_ADDR(SYM) (&DATA_NAME(SYM, _start)) +#define BIN_FILE_PATH(FILE_NAME) STRINGIFY(BUILD_PATH) "/" STRINGIFY(FILE_NAME) ".bin" +#define IMPORT_DATA(SYM) \ + extern const uint8_t DATA_NAME(SYM, _start); \ + extern const uint8_t DATA_NAME(SYM, _end); \ + asm(".section \".rodata\", \"a\", @progbits\n" \ + ".balign 4\n" \ + ".global " STRINGIFY(DATA_NAME(SYM, _start)) "\n" \ + STRINGIFY(DATA_NAME(SYM, _start)) ":\n" \ + ".incbin \"" BIN_FILE_PATH(SYM) "\"\n" \ + ".global " STRINGIFY(DATA_NAME(SYM, _end)) "\n" \ + STRINGIFY(DATA_NAME(SYM, _end)) ":\n" \ + ".balign 4\n" \ + ".previous\n") +/* clang-format on */ + +/* Stage 0 Loader */ +IMPORT_DATA(eckd0_cdl); +IMPORT_DATA(eckd0_ldl); +IMPORT_DATA(fba0); +IMPORT_DATA(tape0); +/* Stage 1 Loader */ +IMPORT_DATA(eckd1); +/* Stage 1b Loader */ +IMPORT_DATA(eckd1b); +IMPORT_DATA(fba1b); +/* Stage 2 Loader */ +IMPORT_DATA(eckd2); +IMPORT_DATA(fba2); +/* Stage 2 Dump Loader */ +IMPORT_DATA(eckd2dump_mv); +IMPORT_DATA(eckd2dump_sv); +IMPORT_DATA(fba2dump); +IMPORT_DATA(tape2dump); -#define STAGE2_MAX_SIZE 0x3000 -#define STAGE1B_LOAD_ADDR 0xe000 #define CCW_FLAG_CC 0x40 #define CCW_FLAG_SLI 0x20 #define FBA_BLK_SIZE 512 static struct boot_ccw0 tic_to_stage1b = { .cmd = 0x08, /* tic */ - .address_lo = STAGE1B_LOAD_ADDR, + .address_lo = STAGE1B_LOAD_ADDRESS, }; /* Check sizes of internal objects. Return 0 if everything is correct, @@ -133,8 +167,7 @@ for (i = 0; i < stage1b_count; i++) { stage0->locdata[i].blocknr = (uint32_t) stage1b_list[i].linear.block; - stage0->locread[i].read.address_lo = - STAGE1B_LOAD_ADDR + i * FBA_BLK_SIZE; + stage0->locread[i].read.address_lo = STAGE1B_LOAD_ADDRESS + i * FBA_BLK_SIZE; } /* Terminate CCW chain: Tic to stage 1b */ memcpy(&stage0->locread[i], &tic_to_stage1b, sizeof(tic_to_stage1b)); @@ -178,7 +211,7 @@ ((stage1b_list[i].chs.cyl >> 12) & 0xfff0); stage1->seek[i].sec = stage1b_list[i].chs.sec; stage1->ssrt[i].read.address_lo = - STAGE1B_LOAD_ADDR + i * stage1b_list[i].chs.size; + STAGE1B_LOAD_ADDRESS + i * stage1b_list[i].chs.size; stage1->ssrt[i].read.flags = CCW_FLAG_CC | CCW_FLAG_SLI; } /* Terminate CCW chain: Tic to stage 1b */ diff -Nru s390-tools-2.25.0/zipl/src/bootmap.c s390-tools-2.26.0/zipl/src/bootmap.c --- s390-tools-2.25.0/zipl/src/bootmap.c 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/zipl/src/bootmap.c 2023-02-14 14:05:17.000000000 +0100 @@ -9,6 +9,7 @@ * it under the terms of the MIT license. See LICENSE for details. */ +#include #include #include #include @@ -952,30 +953,10 @@ #define DUMP_PARAM_MAX_LEN 896 -static char * -create_dump_parmline(const char* parmline, const char* root_dev, - uint64_t mem, int max_cpus) -{ - char* result; - - result = misc_malloc(DUMP_PARAM_MAX_LEN); - if (!result) - return NULL; - snprintf(result, DUMP_PARAM_MAX_LEN, "%s%sroot=%s dump_mem=%lld " - "possible_cpus=%d cgroup_disable=memory ", - parmline ? parmline : "", parmline ? " " : "", root_dev, - (unsigned long long) mem, max_cpus); - result[DUMP_PARAM_MAX_LEN - 1] = 0; - return result; -} - - static int -get_dump_parmline(char *partition, char *parameters, - struct disk_info *target_info, - struct job_target_data *target, char **result) +check_dump_device_late(char *partition, struct disk_info *target_info, + struct job_target_data *target) { - char* buffer; struct disk_info* info; int rc; @@ -998,15 +979,7 @@ disk_free_info(info); return -1; } - if (is_ngdump_enabled(partition, target)) - buffer = misc_strdup(parameters); - else - buffer = create_dump_parmline(parameters, "/dev/ram0", - info->partnum, 1); disk_free_info(info); - if (buffer == NULL) - return -1; - *result = buffer; return 0; } @@ -1024,10 +997,10 @@ ipl.common = dump->common; /* Get file system dump parmline */ - rc = get_dump_parmline(dump->device, dump->common.parmline, - bis->info, target, &ipl.common.parmline); + rc = check_dump_device_late(dump->device, bis->info, target); if (rc) return rc; + ipl.common.parmline = dump->common.parmline; ipl.common.parm_addr = dump->common.parm_addr; return add_ipl_program(bis, NULL, false, NULL, &ipl, program, verbose, 1, type, target, SECURE_BOOT_DISABLED, @@ -1711,7 +1684,6 @@ struct install_set *bis, int program_table_id) { - char mount_point[] = "/tmp/zipl-dump-mount-point-XXXXXX"; struct disk_info *info; int rc; @@ -1723,12 +1695,23 @@ return -1; if (check_dump_device(job, info, bis->device)) return -1; + + bis->dump_mount_point = misc_make_path("/tmp", + DUMP_TEMP_MOUNT_POINT_NAME); + if (!bis->dump_mount_point) { + error_reason(strerror(errno)); + error_text("Could not make path for '%s'", + DUMP_TEMP_MOUNT_POINT_NAME); + return -1; + } /* Create a mount point directory */ - if (mkdtemp(mount_point) == NULL) { + if (mkdtemp(bis->dump_mount_point) == NULL) { error_reason(strerror(errno)); - error_text("Could not create mount point '%s'", mount_point); + error_text("Could not create mount point '%s'", + bis->dump_mount_point); return -1; } + bis->dump_tmp_dir_created = 1; if (!dry_run) { char *cmd = NULL; util_asprintf(&cmd, "mkfs.%s -qF %s >/dev/null", @@ -1742,33 +1725,27 @@ error_reason(strerror(errno)); error_text("Could not format partition '%s':", job->data.dump.device); - goto out_rmdir; + return -1; } } /* * Mount partition where bootmap file and also a dump file will * be stored. */ - if (mount(job->data.dump.device, mount_point, NGDUMP_FSTYPE, 0, NULL)) { + if (mount(job->data.dump.device, bis->dump_mount_point, + NGDUMP_FSTYPE, 0, NULL)) { error_reason(strerror(errno)); error_text("Could not mount partition '%s':", job->data.dump.device); - goto out_rmdir; + return -1; } - if (bootmap_create_file(job, mount_point, bis, program_table_id)) - goto out_umount; - if (ngdump_create_meta(mount_point)) - goto out_umount; - /* Cleanup */ - umount(mount_point); - rmdir(mount_point); + bis->dump_mounted = 1; + if (bootmap_create_file(job, bis->dump_mount_point, + bis, program_table_id)) + return -1; + if (ngdump_create_meta(bis->dump_mount_point)) + return -1; return 0; - -out_umount: - umount(mount_point); -out_rmdir: - rmdir(mount_point); - return -1; } @@ -1835,6 +1812,9 @@ return rc; } +/** + * Release all resources accumulated along the installation process + */ void free_bootloader(struct install_set *bis) { int i, j; @@ -1853,4 +1833,13 @@ free(bis->filename); misc_free_temp_dev(bis->device); disk_free_info(bis->info); + if (bis->dump_mount_point) { + if (bis->dump_mounted && umount(bis->dump_mount_point)) + warn("Could not umount dump device at %s", + bis->dump_mount_point); + if (bis->dump_tmp_dir_created && rmdir(bis->dump_mount_point)) + warn("Could not remove directory %s", + bis->dump_mount_point); + free(bis->dump_mount_point); + } } diff -Nru s390-tools-2.25.0/zipl/src/job.c s390-tools-2.26.0/zipl/src/job.c --- s390-tools-2.25.0/zipl/src/job.c 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/zipl/src/job.c 2023-02-14 14:05:17.000000000 +0100 @@ -30,6 +30,8 @@ #include "zipl.h" #include "envblk.h" +#define DEFAULT_DUMP_PARMLINE "root=/dev/ram0 possible_cpus=1 cgroup_disable=memory" + const char *blsdir; /* Command line options */ @@ -891,6 +893,7 @@ dump->common.ramdisk_addr = UNSPECIFIED_ADDRESS; } + dump->common.parmline = misc_strdup(DEFAULT_DUMP_PARMLINE); dump->common.parm_addr = UNSPECIFIED_ADDRESS; return finalize_common_address_data(&dump->common, name); } diff -Nru s390-tools-2.25.0/zipl/src/Makefile s390-tools-2.26.0/zipl/src/Makefile --- s390-tools-2.25.0/zipl/src/Makefile 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/zipl/src/Makefile 2023-02-14 14:05:17.000000000 +0100 @@ -4,14 +4,14 @@ ALL_CPPFLAGS += -I../include -I../boot \ -DZFCPDUMP_IMAGE="STRINGIFY($(ZFCPDUMP_DIR)/$(ZFCPDUMP_IMAGE))" \ -DZFCPDUMP_INITRD="STRINGIFY($(ZFCPDUMP_DIR)/$(ZFCPDUMP_INITRD))" \ - -D_FILE_OFFSET_BITS=64 $(NO_PIE_CFLAGS) + -D_FILE_OFFSET_BITS=64 $(NO_PIE_CFLAGS) -DBUILD_PATH="../boot" ALL_LDFLAGS += -Wl,-z,noexecstack $(NO_PIE_LDFLAGS) libs = $(rootdir)/libutil/libutil.a \ $(rootdir)/libvtoc/libvtoc.a \ objects = misc.o error.o scan.o job.o boot.o bootmap.o fs-map.o disk.o \ - bootmap_header.o envblk.o install.o zipl.o $(rootdir)/zipl/boot/data.o + bootmap_header.o envblk.o install.o zipl.o zipl_helpers = $(basename $(wildcard zipl_helper.*.c)) chreipl_helpers = $(subst zipl_,chreipl_, $(zipl_helpers)) @@ -19,6 +19,7 @@ all: zipl zipl-editenv $(chreipl_helpers) $(zipl_stage3) +boot.o: ../boot/.loaders zipl: $(objects) $(libs) zipl_helper.device-mapper: $(rootdir)/libdasd/libdasd.a \ @@ -47,13 +48,8 @@ # Additional manual dependencies -.boot.o.d boot.o: ../boot/data.h - -../boot/data.h: - $(MAKE) -C ../boot data.h - -../boot/data.o: - $(MAKE) -C ../boot data.o +../boot/.loaders: + $(MAKE) -C ../boot .loaders ../boot/stage3.bin: $(MAKE) -C ../boot stage3.bin diff -Nru s390-tools-2.25.0/zkey/ep11.c s390-tools-2.26.0/zkey/ep11.c --- s390-tools-2.25.0/zkey/ep11.c 2022-12-08 13:40:15.000000000 +0100 +++ s390-tools-2.26.0/zkey/ep11.c 2023-02-14 14:05:17.000000000 +0100 @@ -35,7 +35,7 @@ * Definitions for the EP11 library */ #define EP11_LIBRARY_NAME "libep11.so" -#define EP11_LIBRARY_VERSION 3 +#define EP11_LIBRARY_VERSION 4 #define EP11_WEB_PAGE "http://www.ibm.com/security/cryptocards" /**