From 925b343a65b6c000e14cdeef7e32dc206232cc19 Mon Sep 17 00:00:00 2001 From: Dan Horák Date: Dec 22 2009 11:00:08 +0000 Subject: - fixed return value in cpi initscript (#541389) - updated zfcpconf.sh script from dracut - added device-mapper support into zipl (#546280) - added missing check and print NSS name in case an NSS has been IPLed (#546297) - added device_cio_free script and its symlinks - added qualified return codes and further error handling in znetconf (#548487) --- diff --git a/0015-s390tools-1.8.2-zipl-dm.patch b/0015-s390tools-1.8.2-zipl-dm.patch new file mode 100644 index 0000000..306006d --- /dev/null +++ b/0015-s390tools-1.8.2-zipl-dm.patch @@ -0,0 +1,2716 @@ +From e089f907d7ba7f18479eaff61852171842a219e2 Mon Sep 17 00:00:00 2001 +From: =?utf-8?q?Dan=20Hor=C3=A1k?= +Date: Thu, 10 Dec 2009 18:27:58 +0100 +Subject: [PATCH 15/16] s390tools-1.8.2-zipl-dm + +device mapper support for zipl +--- + zipl/include/disk.h | 25 ++- + zipl/include/install.h | 9 +- + zipl/include/job.h | 17 +- + zipl/include/scan.h | 19 +- + zipl/man/zipl.8 | 88 +++++- + zipl/man/zipl.conf.5 | 71 ++++- + zipl/src/Makefile | 1 + + zipl/src/bootmap.c | 70 ++--- + zipl/src/disk.c | 242 ++++++++++--- + zipl/src/install.c | 11 +- + zipl/src/job.c | 362 +++++++++++++++++- + zipl/src/scan.c | 262 ++++++++++++- + zipl/src/zipl.c | 13 +- + zipl/src/zipl_helper.device-mapper | 716 ++++++++++++++++++++++++++++++++++++ + 14 files changed, 1759 insertions(+), 147 deletions(-) + create mode 100644 zipl/src/zipl_helper.device-mapper + +diff --git a/zipl/include/disk.h b/zipl/include/disk.h +index c5179b7..4b39698 100644 +--- a/zipl/include/disk.h ++++ b/zipl/include/disk.h +@@ -2,7 +2,7 @@ + * s390-tools/zipl/include/disk.h + * Functions to handle disk layout specific operations. + * +- * Copyright IBM Corp. 2001, 2006. ++ * Copyright IBM Corp. 2001, 2009. + * + * Author(s): Carsten Otte + * Peter Oberparleiter +@@ -59,6 +59,19 @@ struct hd_geometry { + unsigned long start; + }; + ++/* Disk information source */ ++typedef enum { ++ source_auto, ++ source_user, ++ source_script ++} source_t; ++ ++/* targetbase definition */ ++typedef enum { ++ defined_as_device, ++ defined_as_name ++} definition_t; ++ + /* Disk information type */ + struct disk_info { + disk_type_t type; +@@ -72,11 +85,17 @@ struct disk_info { + struct hd_geometry geo; + char* name; + char* drv_name; ++ source_t source; ++ definition_t targetbase; + }; + ++struct job_target_data; + +-int disk_get_info(const char* device, struct disk_info** info); +-int disk_get_info_from_file(const char* filename, struct disk_info** info); ++int disk_get_info(const char* device, struct job_target_data* target, ++ struct disk_info** info); ++int disk_get_info_from_file(const char* filename, ++ struct job_target_data* target, ++ struct disk_info** info); + void disk_free_info(struct disk_info* info); + char* disk_get_type_name(disk_type_t type); + int disk_is_large_volume(struct disk_info* info); +diff --git a/zipl/include/install.h b/zipl/include/install.h +index ba31bff..5504deb 100644 +--- a/zipl/include/install.h ++++ b/zipl/include/install.h +@@ -2,7 +2,7 @@ + * s390-tools/zipl/include/install.h + * Functions handling the installation of the boot loader code onto disk. + * +- * Copyright IBM Corp. 2001, 2006. ++ * Copyright IBM Corp. 2001, 2009. + * + * Author(s): Carsten Otte + * Peter Oberparleiter +@@ -24,8 +24,9 @@ int install_tapeloader(const char* device, const char* image, + const char* parmline, const char* ramdisk, + address_t image_addr, address_t parm_addr, + address_t initrd_addr); +-int install_dump(const char* device, uint64_t mem); +-int install_mvdump(char* const device[], int device_count, uint64_t mem, +- uint8_t force); ++int install_dump(const char* device, struct job_target_data* target, ++ uint64_t mem); ++int install_mvdump(char* const device[], struct job_target_data* target, ++ int device_count, uint64_t mem, uint8_t force); + + #endif /* INSTALL_H */ +diff --git a/zipl/include/job.h b/zipl/include/job.h +index 824ffc4..cf881db 100644 +--- a/zipl/include/job.h ++++ b/zipl/include/job.h +@@ -3,7 +3,7 @@ + * Functions and data structures representing the actual 'job' that the + * user wants us to execute. + * +- * Copyright IBM Corp. 2001, 2006. ++ * Copyright IBM Corp. 2001, 2009. + * + * Author(s): Carsten Otte + * Peter Oberparleiter +@@ -13,6 +13,7 @@ + #define JOB_H + + #include "zipl.h" ++#include "disk.h" + + + enum job_id { +@@ -27,6 +28,17 @@ enum job_id { + job_mvdump = 9, + }; + ++struct job_target_data { ++ char* bootmap_dir; ++ char* targetbase; ++ disk_type_t targettype; ++ int targetcylinders; ++ int targetheads; ++ int targetsectors; ++ int targetblocksize; ++ blocknum_t targetoffset; ++}; ++ + struct job_ipl_data { + char* image; + char* parmline; +@@ -94,7 +106,7 @@ struct job_menu_data { + + struct job_data { + enum job_id id; +- char* bootmap_dir; ++ struct job_target_data target; + char* name; + union { + struct job_ipl_data ipl; +@@ -115,5 +127,6 @@ struct job_data { + + int job_get(int argc, char* argv[], struct job_data** data); + void job_free(struct job_data* job); ++int type_from_target(char *target, disk_type_t *type); + + #endif /* not JOB_H */ +diff --git a/zipl/include/scan.h b/zipl/include/scan.h +index b1c0e3a..ed5714e 100644 +--- a/zipl/include/scan.h ++++ b/zipl/include/scan.h +@@ -2,7 +2,7 @@ + * s390-tools/zipl/include/scan.h + * Scanner for zipl.conf configuration files + * +- * Copyright IBM Corp. 2001, 2006. ++ * Copyright IBM Corp. 2001, 2009. + * + * Author(s): Carsten Otte + * Peter Oberparleiter +@@ -15,7 +15,7 @@ + + + #define SCAN_SECTION_NUM 7 +-#define SCAN_KEYWORD_NUM 14 ++#define SCAN_KEYWORD_NUM 19 + + enum scan_id { + scan_id_empty = 0, +@@ -40,6 +40,11 @@ enum scan_keyword_id { + scan_keyword_defaultmenu = 11, + scan_keyword_tape = 12, + scan_keyword_mvdump = 13, ++ scan_keyword_targetbase = 14, ++ scan_keyword_targettype = 15, ++ scan_keyword_targetgeometry = 16, ++ scan_keyword_targetblocksize = 17, ++ scan_keyword_targetoffset = 18, + }; + + enum scan_section_type { +@@ -53,6 +58,14 @@ enum scan_section_type { + section_mvdump = 6, + }; + ++enum scan_target_type { ++ target_type_invalid = -1, ++ target_type_scsi = 0, ++ target_type_fba = 1, ++ target_type_ldl = 2, ++ target_type_cdl = 3, ++}; ++ + enum scan_key_state { + req, /* Keyword is required */ + opt, /* Keyword is optional */ +@@ -100,6 +113,8 @@ int scan_find_section(struct scan_token* scan, char* name, enum scan_id type, + int offset); + int scan_check_section_data(char* keyword[], int* line, char* name, + int section_line, enum scan_section_type* type); ++int scan_check_target_data(char* keyword[], int* line); + enum scan_section_type scan_get_section_type(char* keyword[]); ++enum scan_target_type scan_get_target_type(char *type); + + #endif /* not SCAN_H */ +diff --git a/zipl/man/zipl.8 b/zipl/man/zipl.8 +index 6df6026..e291445 100644 +--- a/zipl/man/zipl.8 ++++ b/zipl/man/zipl.8 +@@ -1,4 +1,4 @@ +-.TH ZIPL 8 "Apr 2006" "s390-tools" ++.TH ZIPL 8 "Nov 2009" "s390-tools" + .SH NAME + zipl \- boot loader for IBM S/390 and zSeries architectures + +@@ -58,6 +58,45 @@ See the + .BR zipl.conf (5) + man page for details on how to use the boot menu. + ++.B Logical devices ++ ++zipl can be used to prepare logical devices (e.g. a linear device mapper target) ++for booting when the following requirements are met by the logical device setup: ++.IP " -" ++all boot relevant files (i.e. kernel, ramdisk and parameter files) must be ++located on a logical device which is mapped to a single physical disk of a type ++supported by zipl (i.e. DASD or SCSI disk) ++.IP " -" ++adjacent data blocks on the logical device must correspond to adjacent blocks on ++the physical device ++.IP " -" ++access to the first blocks (starting at block 0) of the physical device must be ++given ++.PP ++Examples for logical device setups that are supported are linear and mirror ++mapping. ++ ++When working with logical devices, zipl requires that the user provides more ++information about the target device: ++.IP " -" ++device characteristics of the underlying physical device: disk type and format ++(e.g. ECKD CDL or FCP SCSI), disk geometry in case of ECKD DASDs and block size ++.IP " -" ++target device offset, i.e. the number of blocks between the physical device ++start and the start of the logical device containing the filesystem with all ++boot relevant files ++.IP " -" ++a device node which provides access to the first blocks of the device ++.PP ++If the user does not provide this information explicitly by parameters ++zipl automatically runs a driver specific helper script to obtain these data, ++e.g. zipl_helper.device-mapper. ++ ++Note that zipl uses /proc/devices to determine the driver name for a given ++device. If the driver name cannot be determined the preparation of a logical ++device for boot might fail. ++This can be the case in a chroot environment when /proc is not mounted ++explicitly. + + .SH OPTIONS + .TP +@@ -85,6 +124,53 @@ It is not possible to specify both this parameter and the name of a menu + or configuration section on the command line at the same time. + + .TP ++.BR "\-\-targetbase=" ++Install the actual boot loader on the device node specified by BASE DEVICE. ++ ++This option is required when working with logical devices (see section ++"Logical devices" above). ++ ++.TP ++.BR "\-\-targettype=" ++Assume that the physical device is of the specified type. Valid values are: ++.IP " -" 12 ++CDL: DASD disk with ECKD/compatible disk layout ++.IP " -" 12 ++LDL: DASD disk with ECDK/linux disk layout ++.IP " -" 12 ++FBA: FBA disk DASD ++.IP " -" 12 ++SCSI: SCSI disk ++.PP ++.IP " " 8 ++This option is required when working with logical devices (see section ++"Logical devices" above). ++ ++.TP ++.BR "\-\-targetgeometry=" ++Assume that the physical device has the specified number of cylinders, heads and ++sectors. ++ ++This option is required when working with logical devices which are located on ++DASD ECKD disks (see section "Logical devices" above). ++ ++.TP ++.BR "\-\-targetblocksize=" ++Assume that blocks on the physical device are SIZE bytes long. ++ ++This option is required when working with logical devices (see section ++"Logical devices" above). ++ ++.TP ++.BR "\-\-targetoffset=" ++Assume that the logical device containing the directory specified by the ++--target option is located on the physical device starting at the block ++specified by OFFSET. ++ ++This option is required when working with logical devices (see section ++"Logical devices" above). ++ ++.TP + .BR "\-T " " or " "\-\-tape=" + Install bootloader on the specified . Use this option instead + of the 'target' option to prepare a tape device for IPL. +diff --git a/zipl/man/zipl.conf.5 b/zipl/man/zipl.conf.5 +index 6be623e..9d0f60d 100644 +--- a/zipl/man/zipl.conf.5 ++++ b/zipl/man/zipl.conf.5 +@@ -1,4 +1,4 @@ +-.TH ZIPL.CONF 5 "Apr 2006" "s390-tools" ++.TH ZIPL.CONF 5 "Nov 2009" "s390-tools" + + .SH NAME + zipl.conf \- zipl configuration file +@@ -490,6 +490,75 @@ The device on which the target directory is located will be used as 'target + device', i.e. it will be prepared for booting (initial program load). + .PP + ++.B targetbase ++= ++.I base\-device ++(configuration and menu) ++.IP ++.B Configuration and menu section: ++.br ++Specify the device which will be prepared for booting. ++ ++This parameter is required when working with logical devices (see zipl(8)). ++.PP ++ ++.B targettype ++= ++.I type ++(configuration and menu) ++.IP ++.B Configuration and menu section: ++.br ++Specify the device type for the physical device. ++.IP " - " 12 ++CDL: DASD disk with ECKD/compatible disk layout ++.IP " - " 12 ++LDL: DASD disk with ECDK/linux disk layout ++.IP " - " 12 ++FBA: FBA disk DASD ++.IP " - " 12 ++SCSI disk ++.PP ++.IP " " 8 ++This parameter is required when working with logical devices (see zipl(8)). ++.PP ++ ++.B targetgeometry ++= ++.I cylinders,heads,sectors ++(configuration and menu) ++.IP ++.B Configuration and menu section: ++.br ++Specify the number of cylinders, heads and sectors for the physical device. ++ ++This parameter is required when working with logical devices (see zipl(8)). ++.PP ++ ++.B targetblocksize ++= ++.I size ++(configuration and menu) ++.IP ++.B Configuration and menu section: ++.br ++Specify the number of bytes per block for the physical device. ++ ++This parameter is required when working with logical devices (see zipl(8)). ++.PP ++ ++.B targetoffset ++= ++.I offset ++(configuration and menu) ++.IP ++.B Configuration and menu section: ++.br ++Specify the starting block number of the logical device on the physical device. ++ ++This parameter is required when working with logical devices (see zipl(8)). ++.PP ++ + .B timeout + = + .I menu-timeout +diff --git a/zipl/src/Makefile b/zipl/src/Makefile +index 234464b..f95bb55 100644 +--- a/zipl/src/Makefile ++++ b/zipl/src/Makefile +@@ -17,6 +17,7 @@ zipl: $(objects) + install: all + $(INSTALL) -d -m 755 $(BINDIR) + $(INSTALL) -c zipl $(BINDIR) ++ $(INSTALL) -m 755 $(wildcard zipl_helper.*) $(TOOLS_LIBDIR) + + clean: + rm -f *.o zipl +diff --git a/zipl/src/bootmap.c b/zipl/src/bootmap.c +index 566e59d..526aa48 100644 +--- a/zipl/src/bootmap.c ++++ b/zipl/src/bootmap.c +@@ -2,7 +2,7 @@ + * s390-tools/zipl/src/bootmap.c + * Functions to build the bootmap file. + * +- * Copyright IBM Corp. 2001, 2006. ++ * Copyright IBM Corp. 2001, 2009. + * + * Author(s): Carsten Otte + * Peter Oberparleiter +@@ -298,7 +298,8 @@ struct component_loc { + static int + add_component_file(int fd, const char* filename, address_t load_address, + off_t offset, void* component, int add_files, +- struct disk_info* info, struct component_loc *location) ++ struct disk_info* info, struct job_target_data* target, ++ struct component_loc *location) + { + struct disk_info* file_info; + struct component_loc loc; +@@ -336,7 +337,7 @@ add_component_file(int fd, const char* filename, address_t load_address, + } + } else { + /* Make sure file is on correct device */ +- rc = disk_get_info_from_file(filename, &file_info); ++ rc = disk_get_info_from_file(filename, target, &file_info); + if (rc) + return -1; + if (file_info->device != info->device) { +@@ -472,7 +473,7 @@ check_component_overlap(const char *name[], struct component_loc *loc, int num) + static int + add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, + int verbose, int add_files, component_header_type type, +- struct disk_info* info) ++ struct disk_info* info, struct job_target_data* target) + { + struct stat stats; + void* table; +@@ -500,7 +501,7 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, + } + rc = add_component_file(fd, ipl->image, ipl->image_addr, + KERNEL_HEADER_SIZE, VOID_ADD(table, offset), +- add_files, info, &comp_loc[0]); ++ add_files, info, target, &comp_loc[0]); + if (rc) { + error_text("Could not add image file '%s'", ipl->image); + free(table); +@@ -542,7 +543,7 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, + rc = add_component_file(fd, ipl->ramdisk, + ipl->ramdisk_addr, 0, + VOID_ADD(table, offset), +- add_files, info, &comp_loc[2]); ++ add_files, info, target, &comp_loc[2]); + if (rc) { + error_text("Could not add ramdisk '%s'", + ipl->ramdisk); +@@ -594,7 +595,8 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, + static int + add_segment_program(int fd, struct job_segment_data* segment, + disk_blockptr_t* program, int verbose, int add_files, +- component_header_type type, struct disk_info* info) ++ component_header_type type, struct disk_info* info, ++ struct job_target_data* target) + { + const char *comp_name[1] = {"segment file"}; + struct component_loc comp_loc[1]; +@@ -618,7 +620,7 @@ add_segment_program(int fd, struct job_segment_data* segment, + } + rc = add_component_file(fd, segment->segment, segment->segment_addr, 0, + VOID_ADD(table, offset), add_files, info, +- &comp_loc[0]); ++ target, &comp_loc[0]); + if (rc) { + error_text("Could not add segment file '%s'", + segment->segment); +@@ -661,14 +663,15 @@ create_dump_fs_parmline(const char* parmline, const char* root_dev, + + static int + get_dump_fs_parmline(char* partition, char* parameters, uint64_t mem, +- char** result, struct disk_info* target_info) ++ struct disk_info* target_info, ++ struct job_target_data* target, char** result) + { + char* buffer; + struct disk_info* info; + int rc; + + /* Get information about partition */ +- rc = disk_get_info(partition, &info); ++ rc = disk_get_info(partition, target, &info); + if (rc) { + error_text("Could not get information for dump partition '%s'", + partition); +@@ -700,7 +703,7 @@ static int + add_dump_fs_program(int fd, struct job_dump_fs_data* dump_fs, + disk_blockptr_t* program, int verbose, + int add_files, component_header_type type, +- struct disk_info* info) ++ struct disk_info* info, struct job_target_data* target) + { + struct job_ipl_data ipl; + int rc; +@@ -725,12 +728,12 @@ add_dump_fs_program(int fd, struct job_dump_fs_data* dump_fs, + + /* Get file system dump parmline */ + rc = get_dump_fs_parmline(dump_fs->partition, dump_fs->parmline, +- dump_fs->mem, &ipl.parmline, info); ++ dump_fs->mem, info, target, &ipl.parmline); + if (rc) + return rc; + ipl.parm_addr = DEFAULT_PARMFILE_ADDRESS; + return add_ipl_program(fd, &ipl, program, verbose, 1, +- type, info); ++ type, info, target); + } + + +@@ -763,7 +766,7 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer, + rc = add_ipl_program(fd, &job->data.ipl, &table[0], + verbose || job->command_line, + job->add_files, component_header_ipl, +- info); ++ info, &job->target); + break; + case job_segment: + if (job->command_line) +@@ -774,7 +777,7 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer, + rc = add_segment_program(fd, &job->data.segment, &table[0], + verbose || job->command_line, + job->add_files, component_header_ipl, +- info); ++ info, &job->target); + break; + case job_dump_fs: + if (job->command_line) +@@ -785,7 +788,7 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer, + rc = add_dump_fs_program(fd, &job->data.dump_fs, &table[0], + verbose || job->command_line, + job->add_files, component_header_dump, +- info); ++ info, &job->target); + break; + case job_menu: + printf("Building menu '%s'\n", job->name); +@@ -804,7 +807,7 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer, + &table[job->data.menu.entry[i].pos], + verbose || job->command_line, + job->add_files, component_header_ipl, +- info); ++ info, &job->target); + break; + case job_dump_fs: + printf("Adding #%d: fs-dump section '%s'%s\n", +@@ -818,7 +821,7 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer, + &table[job->data.menu.entry[i].pos], + verbose || job->command_line, + job->add_files, component_header_dump, +- info); ++ info, &job->target); + break; + case job_print_usage: + case job_print_version: +@@ -888,9 +891,9 @@ bootmap_create(struct job_data* job, disk_blockptr_t* program_table, + size_t stage2_size; + int fd; + int rc; +- + /* Get full path of bootmap file */ +- filename = misc_make_path(job->bootmap_dir, BOOTMAP_TEMPLATE_FILENAME); ++ filename = misc_make_path(job->target.bootmap_dir, ++ BOOTMAP_TEMPLATE_FILENAME); + if (filename == NULL) + return -1; + /* Create temporary bootmap file */ +@@ -904,32 +907,14 @@ bootmap_create(struct job_data* job, disk_blockptr_t* program_table, + /* Retrieve target device information. Note that we have to + * call disk_get_info_from_file() to also get the file system + * block size. */ +- rc = disk_get_info_from_file(filename, &info); ++ rc = disk_get_info_from_file(filename, &job->target, &info); + if (rc) { + close(fd); + free(filename); + return -1; + } + /* Check for supported disk and driver types */ +- switch (info->type) { +- case disk_type_scsi: +- case disk_type_fba: +- break; +- case disk_type_eckd_classic: +- case disk_type_eckd_compatible: +- /* Check for valid CHS geometry data. */ +- if ((info->geo.cylinders == 0) || (info->geo.heads == 0) || +- (info->geo.sectors == 0)) { +- error_reason("Invalid disk geometry (CHS=%d/%d/%d)", +- info->geo.cylinders, info->geo.heads, +- info->geo.sectors); +- disk_free_info(info); +- close(fd); +- free(filename); +- return -1; +- } +- break; +- case disk_type_diag: ++ if ((info->source == source_auto) && (info->type == disk_type_diag)) { + error_reason("Unsupported disk type (%s)", + disk_get_type_name(info->type)); + disk_free_info(info); +@@ -959,7 +944,7 @@ bootmap_create(struct job_data* job, disk_blockptr_t* program_table, + return rc; + } + } +- printf("Building bootmap in '%s'%s\n", job->bootmap_dir, ++ printf("Building bootmap in '%s'%s\n", job->target.bootmap_dir, + job->add_files ? " (files will be added to bootmap file)" : + ""); + /* Write bootmap header */ +@@ -1046,7 +1031,8 @@ bootmap_create(struct job_data* job, disk_blockptr_t* program_table, + "file %s!\n", filename); + } else { + /* Rename to final bootmap name */ +- mapname = misc_make_path(job->bootmap_dir, BOOTMAP_FILENAME); ++ mapname = misc_make_path(job->target.bootmap_dir, ++ BOOTMAP_FILENAME); + if (mapname == NULL) { + misc_free_temp_dev(device); + disk_free_info(info); +diff --git a/zipl/src/disk.c b/zipl/src/disk.c +index 08f0c64..9392968 100644 +--- a/zipl/src/disk.c ++++ b/zipl/src/disk.c +@@ -2,13 +2,14 @@ + * s390-tools/zipl/src/disk.c + * Functions to handle disk layout specific operations. + * +- * Copyright IBM Corp. 2001, 2006. ++ * Copyright IBM Corp. 2001, 2009. + * + * Author(s): Carsten Otte + * Peter Oberparleiter + */ + + #include "disk.h" ++#include "job.h" + + #include + #include +@@ -84,16 +85,34 @@ disk_determine_dasd_type(struct disk_info *data, + return 0; + } + ++/* Return non-zero for ECKD type. */ + int +-disk_get_info(const char* device, struct disk_info** info) ++disk_is_eckd(disk_type_t type) ++{ ++ return (type == disk_type_eckd_classic || ++ type == disk_type_eckd_compatible); ++} ++ ++int ++disk_get_info(const char* device, struct job_target_data* target, ++ struct disk_info** info) + { + struct stat stats; ++ struct stat script_stats; + struct proc_part_entry part_entry; + struct proc_dev_entry dev_entry; + struct dasd_information dasd_info; + struct disk_info *data; + int fd; + long devsize; ++ FILE *fh; ++ char *script_pre = "/lib/s390-tools/zipl_helper."; ++ char script_file[80]; ++ char ppn_cmd[80]; ++ char buffer[80]; ++ char value[40]; ++ int majnum, minnum; ++ int checkparm; + + /* Get file information */ + if (stat(device, &stats)) { +@@ -113,37 +132,140 @@ disk_get_info(const char* device, struct disk_info** info) + return -1; + } + memset((void *) data, 0, sizeof(struct disk_info)); +- /* Get disk geometry. Note: geo.start contains a sector number +- * offset measured in physical blocks, not sectors (512 bytes) */ +- if (ioctl(fd, HDIO_GETGEO, &data->geo)) { +- error_reason("Could not get disk geometry"); +- free(data); +- close(fd); +- return -1; +- } + /* Try to get device driver name */ + if (proc_dev_get_entry(stats.st_rdev, 1, &dev_entry) == 0) { + data->drv_name = misc_strdup(dev_entry.name); + proc_dev_free_entry(&dev_entry); ++ } else { ++ fprintf(stderr, "Warning: Could not determine driver name for " ++ "major %d from /proc/devices\n", major(stats.st_rdev)); ++ fprintf(stderr, "Warning: Preparing a logical device for boot " ++ "might fail\n"); ++ } ++ data->source = source_user; ++ /* Check if targetbase script is available */ ++ strcpy(script_file, script_pre); ++ if (data->drv_name) { ++ strcat(script_file, data->drv_name); ++ } ++ if ((target->targetbase == NULL) && ++ (!stat(script_file, &script_stats))) { ++ data->source = source_script; ++ /* Run targetbase script */ ++ strcpy(ppn_cmd, script_file); ++ strcat(ppn_cmd, " "); ++ strcat(ppn_cmd, target->bootmap_dir); ++ printf("Run %s\n", ppn_cmd); ++ fh = popen(ppn_cmd, "r"); ++ if (fh == NULL) { ++ error_reason("Failed to run popen(%s,\"r\",)"); ++ goto out_close; ++ } ++ checkparm = 0; ++ while (fgets(buffer, 80, fh) != NULL) { ++ if (sscanf(buffer, "targetbase=%s", value) == 1) { ++ target->targetbase = misc_strdup(value); ++ checkparm++; ++ } ++ if (sscanf(buffer, "targettype=%s", value) == 1) { ++ type_from_target(value, &target->targettype); ++ checkparm++; ++ } ++ if (sscanf(buffer, "targetgeometry=%s", value) == 1) { ++ target->targetcylinders = ++ atoi(strtok(value, ",")); ++ target->targetheads = atoi(strtok(NULL, ",")); ++ target->targetsectors = atoi(strtok(NULL, ",")); ++ checkparm++; ++ } ++ if (sscanf(buffer, "targetblocksize=%s", value) == 1) { ++ target->targetblocksize = atoi(value); ++ checkparm++; ++ } ++ if (sscanf(buffer, "targetoffset=%s", value) == 1) { ++ target->targetoffset = atol(value); ++ checkparm++; ++ } ++ } ++ switch (pclose(fh)) { ++ case 0 : ++ /* success */ ++ break; ++ case -1 : ++ error_reason("Failed to run pclose"); ++ goto out_close; ++ default : ++ error_reason("Script could not determine target " ++ "parameters"); ++ goto out_close; ++ } ++ if ((!disk_is_eckd(target->targettype) && checkparm < 4) || ++ (disk_is_eckd(target->targettype) && checkparm != 5)) { ++ error_reason("Target parameters missing from script"); ++ goto out_close; ++ } + } +- /* Get DASD information */ +- if (ioctl(fd, BIODASDINFO, &dasd_info)) ++ ++ /* Get disk geometry. Note: geo.start contains a sector number ++ * offset measured in physical blocks, not sectors (512 bytes) */ ++ if (target->targetbase != NULL) { ++ data->geo.heads = target->targetheads; ++ data->geo.sectors = target->targetsectors; ++ data->geo.cylinders = target->targetcylinders; ++ data->geo.start = target->targetoffset; ++ } else { ++ data->source = source_auto; ++ if (ioctl(fd, HDIO_GETGEO, &data->geo)) { ++ error_reason("Could not get disk geometry"); ++ goto out_close; ++ } ++ } ++ if ((data->source == source_user) || (data->source == source_script)) { + data->devno = -1; +- else +- data->devno = dasd_info.devno; +- /* Get physical block size */ +- if (ioctl(fd, BLKSSZGET, &data->phy_block_size)) { +- error_reason("Could not get blocksize"); +- free(data); +- close(fd); +- return -1; ++ data->phy_block_size = target->targetblocksize; ++ } else { ++ /* Get DASD information */ ++ if (ioctl(fd, BIODASDINFO, &dasd_info)) ++ data->devno = -1; ++ else ++ data->devno = dasd_info.devno; ++ /* Get physical block size */ ++ if (ioctl(fd, BLKSSZGET, &data->phy_block_size)) { ++ error_reason("Could not get blocksize"); ++ goto out_close; ++ } + } + /* Get size of device in sectors (512 byte) */ + if (ioctl(fd, BLKGETSIZE, &devsize)) { + error_reason("Could not get device size"); +- close(fd); +- free(data); +- return -1; ++ goto out_close; ++ } ++ if ((data->source == source_user) || (data->source == source_script)) { ++ data->type = target->targettype; ++ data->partnum = 0; ++ /* Get file information */ ++ if (sscanf(target->targetbase, "%d:%d", &majnum, &minnum) ++ == 2) { ++ data->device = makedev(majnum, minnum); ++ data->targetbase = defined_as_device; ++ } ++ else { ++ if (stat(target->targetbase, &stats)) { ++ error_reason(strerror(errno)); ++ error_text("Could not get information for " ++ "file '%s'", target->targetbase); ++ goto out_close; ++ } ++ if (!S_ISBLK(stats.st_mode)) { ++ error_reason("Target base device '%s' is not " ++ "a block device", ++ target->targetbase); ++ goto out_close; ++ } ++ data->device = stats.st_rdev; ++ data->targetbase = defined_as_name; ++ } ++ goto type_determined; + } + /* Determine disk type */ + if (!data->drv_name) { +@@ -194,6 +316,15 @@ disk_get_info(const char* device, struct disk_info** info) + goto out_close; + } + ++type_determined: ++ /* Check for valid CHS geometry data. */ ++ if (disk_is_eckd(data->type) && (data->geo.cylinders == 0 || ++ data->geo.heads == 0 || data->geo.sectors == 0)) { ++ error_reason("Invalid disk geometry (CHS=%d/%d/%d)", ++ data->geo.cylinders, data->geo.heads, ++ data->geo.sectors); ++ goto out_close; ++ } + /* Convert device size to size in physical blocks */ + data->phy_blocks = devsize / (data->phy_block_size / 512); + if (data->partnum != 0) +@@ -221,7 +352,8 @@ out_close: + + + int +-disk_get_info_from_file(const char* filename, struct disk_info** info) ++disk_get_info_from_file(const char* filename, struct job_target_data* target, ++ struct disk_info** info) + { + struct stat stats; + char* device; +@@ -251,7 +383,7 @@ disk_get_info_from_file(const char* filename, struct disk_info** info) + if (rc) + return -1; + /* Get device info */ +- rc = disk_get_info(device, info); ++ rc = disk_get_info(device, target, info); + if (rc == 0) + (*info)->fs_block_size = blocksize; + /* Clean up */ +@@ -523,8 +655,14 @@ disk_is_large_volume(struct disk_info* info) + void + disk_print_info(struct disk_info* info) + { ++ char footnote[4] = ""; ++ if ((info->source == source_user) || (info->source == source_script)) ++ strcpy(footnote, " *)"); ++ + printf(" Device..........................: "); + disk_print_devt(info->device); ++ if (info->targetbase == defined_as_device) ++ printf("%s", footnote); + printf("\n"); + if (info->partnum != 0) { + printf(" Partition.......................: "); +@@ -532,45 +670,55 @@ disk_print_info(struct disk_info* info) + printf("\n"); + } + if (info->name) { +- printf(" Device name.....................: %s\n", ++ printf(" Device name.....................: %s", + info->name); ++ if (info->targetbase == defined_as_name) ++ printf("%s", footnote); ++ printf("\n"); + } + if (info->drv_name) { + printf(" Device driver name..............: %s\n", + info->drv_name); + } +- if ((info->type == disk_type_fba) || +- (info->type == disk_type_diag) || +- (info->type == disk_type_eckd_classic) || +- (info->type == disk_type_eckd_compatible)) { ++ if (((info->type == disk_type_fba) || ++ (info->type == disk_type_diag) || ++ (info->type == disk_type_eckd_classic) || ++ (info->type == disk_type_eckd_compatible)) && ++ (info->source == source_auto)) { + printf(" DASD device number..............: %04x\n", + info->devno); + } + printf(" Type............................: disk %s\n", + (info->partnum != 0) ? "partition" : "device"); +- printf(" Disk layout.....................: %s\n", +- disk_get_type_name(info->type)); +- printf(" Geometry - heads................: %d\n", +- info->geo.heads); +- printf(" Geometry - sectors..............: %d\n", +- info->geo.sectors); +- if (disk_is_large_volume(info)) { +- /* ECKD large volume. There is not enough information +- * available in INFO to calculate disk cylinder size. */ +- printf(" Geometry - cylinders............: > 65534\n"); +- } else { +- printf(" Geometry - cylinders............: %d\n", +- info->geo.cylinders); ++ printf(" Disk layout.....................: %s%s\n", ++ disk_get_type_name(info->type), footnote); ++ if (disk_is_eckd(info->type)) { ++ printf(" Geometry - heads................: %d%s\n", ++ info->geo.heads, footnote); ++ printf(" Geometry - sectors..............: %d%s\n", ++ info->geo.sectors, footnote); ++ if (disk_is_large_volume(info)) { ++ /* ECKD large volume. There is not enough information ++ * available in INFO to calculate disk cylinder size. */ ++ printf(" Geometry - cylinders............: > 65534\n"); ++ } else { ++ printf(" Geometry - cylinders............: %d%s\n", ++ info->geo.cylinders, footnote); ++ } + } +- printf(" Geometry - start................: %ld\n", +- info->geo.start); ++ printf(" Geometry - start................: %ld%s\n", ++ info->geo.start, footnote); + if (info->fs_block_size >= 0) + printf(" File system block size..........: %d\n", + info->fs_block_size); +- printf(" Physical block size.............: %d\n", +- info->phy_block_size); ++ printf(" Physical block size.............: %d%s\n", ++ info->phy_block_size, footnote); + printf(" Device size in physical blocks..: %ld\n", + (long) info->phy_blocks); ++ if (info->source == source_user) ++ printf(" *) Data provided by user.\n"); ++ if (info->source == source_script) ++ printf(" *) Data provided by script.\n"); + } + + +diff --git a/zipl/src/install.c b/zipl/src/install.c +index 55b0dd3..ec84821 100644 +--- a/zipl/src/install.c ++++ b/zipl/src/install.c +@@ -2,7 +2,7 @@ + * s390-tools/zipl/src/install.c + * Functions handling the installation of the boot loader code onto disk. + * +- * Copyright IBM Corp. 2001, 2006. ++ * Copyright IBM Corp. 2001, 2009. + * + * Author(s): Carsten Otte + * Peter Oberparleiter +@@ -944,7 +944,7 @@ install_dump_tape(int fd, uint64_t mem) + + + int +-install_dump(const char* device, uint64_t mem) ++install_dump(const char* device, struct job_target_data* target, uint64_t mem) + { + struct disk_info* info; + char* tempdev; +@@ -985,7 +985,7 @@ install_dump(const char* device, uint64_t mem) + } + close(fd); + /* This is a disk device */ +- rc = disk_get_info(device, &info); ++ rc = disk_get_info(device, target, &info); + if (rc) { + error_text("Could not get information for dump target " + "'%s'", device); +@@ -1063,7 +1063,8 @@ install_dump(const char* device, uint64_t mem) + + + int +-install_mvdump(char* const device[], int count, uint64_t mem, uint8_t force) ++install_mvdump(char* const device[], struct job_target_data* target, int count, ++ uint64_t mem, uint8_t force) + { + struct disk_info* info[MAX_DUMP_VOLUMES] = {0}; + struct mvdump_parm_table parm; +@@ -1095,7 +1096,7 @@ install_mvdump(char* const device[], int count, uint64_t mem, uint8_t force) + } + close(fd); + /* This is a disk device */ +- rc = disk_get_info(device[i], &info[i]); ++ rc = disk_get_info(device[i], target, &info[i]); + if (rc) { + error_text("Could not get information for dump target " + "'%s'", device[i]); +diff --git a/zipl/src/job.c b/zipl/src/job.c +index 89c8c23..a65e8c1 100644 +--- a/zipl/src/job.c ++++ b/zipl/src/job.c +@@ -3,7 +3,7 @@ + * Functions and data structures representing the actual 'job' that the + * user wants us to execute. + * +- * Copyright IBM Corp. 2001, 2006. ++ * Copyright IBM Corp. 2001, 2009. + * + * Author(s): Carsten Otte + * Peter Oberparleiter +@@ -29,6 +29,11 @@ + static struct option options[] = { + { "config", required_argument, NULL, 'c'}, + { "target", required_argument, NULL, 't'}, ++ { "targetbase", required_argument, NULL, 0xaa}, ++ { "targettype", required_argument, NULL, 0xab}, ++ { "targetgeometry", required_argument, NULL, 0xac}, ++ { "targetblocksize", required_argument, NULL, 0xad}, ++ { "targetoffset", required_argument, NULL, 0xae}, + { "image", required_argument, NULL, 'i'}, + { "ramdisk", required_argument, NULL, 'r'}, + { "parmfile", required_argument, NULL, 'p'}, +@@ -71,6 +76,12 @@ struct command_line { + + /* Global variable for default boot target. Ugly but necessary... */ + static char *default_target; ++static char *default_targetbase; ++static char *default_targettype; ++static char *default_targetgeometry; ++static char *temp_targetgeometry; ++static char *default_targetblocksize; ++static char *default_targetoffset; + + static int + store_option(struct command_line* cmdline, enum scan_keyword_id keyword, +@@ -171,6 +182,32 @@ get_command_line(int argc, char* argv[], struct command_line* line) + rc = store_option(&cmdline, scan_keyword_target, + optarg); + break; ++ case 0xaa: ++ is_keyword = 1; ++ rc = store_option(&cmdline, scan_keyword_targetbase, ++ optarg); ++ break; ++ case 0xab: ++ is_keyword = 1; ++ rc = store_option(&cmdline, scan_keyword_targettype, ++ optarg); ++ break; ++ case 0xac: ++ is_keyword = 1; ++ rc = store_option(&cmdline, scan_keyword_targetgeometry, ++ optarg); ++ break; ++ case 0xad: ++ is_keyword = 1; ++ rc = store_option(&cmdline, ++ scan_keyword_targetblocksize, ++ optarg); ++ break; ++ case 0xae: ++ is_keyword = 1; ++ rc = store_option(&cmdline, scan_keyword_targetoffset, ++ optarg); ++ break; + case 'T': + is_keyword = 1; + cmdline.automenu = 0; +@@ -288,6 +325,9 @@ get_command_line(int argc, char* argv[], struct command_line* line) + } + return rc; + } ++ rc = scan_check_target_data(cmdline.data, NULL); ++ if (rc) ++ return rc; + } + *line = cmdline; + return 0; +@@ -295,6 +335,13 @@ get_command_line(int argc, char* argv[], struct command_line* line) + + + static void ++free_target_data(struct job_target_data* data) ++{ ++ if (data->targetbase != NULL) ++ free(data->targetbase); ++} ++ ++static void + free_ipl_data(struct job_ipl_data* data) + { + if (data->image != NULL) +@@ -388,8 +435,9 @@ free_mvdump_data(struct job_mvdump_data* data) + void + job_free(struct job_data* job) + { +- if (job->bootmap_dir != NULL) +- free(job->bootmap_dir); ++ if (job->target.bootmap_dir != NULL) ++ free(job->target.bootmap_dir); ++ free_target_data(&job->target); + if (job->name != NULL) + free(job->name); + switch (job->id) { +@@ -667,16 +715,16 @@ check_job_data(struct job_data* job) + int rc; + + /* Check for missing information */ +- if (job->bootmap_dir != NULL) { +- rc = misc_check_writable_directory(job->bootmap_dir); ++ if (job->target.bootmap_dir != NULL) { ++ rc = misc_check_writable_directory(job->target.bootmap_dir); + if (rc) { + if (job->name == NULL) { + error_text("Target directory '%s'", +- job->bootmap_dir); ++ job->target.bootmap_dir); + } else { + error_text("Target directory '%s' in section " + "'%s'", +- job->bootmap_dir, job->name); ++ job->target.bootmap_dir, job->name); + } + return rc; + } +@@ -854,6 +902,28 @@ get_parmline(char* filename, char* line, char** parmline, address_t* address, + + #define MEGABYTE_MASK (1024LL * 1024LL - 1LL) + ++int ++type_from_target(char *target, disk_type_t *type) ++{ ++ switch (scan_get_target_type(target)) { ++ case target_type_scsi: ++ *type = disk_type_scsi; ++ return 0; ++ case target_type_fba: ++ *type = disk_type_fba; ++ return 0; ++ case target_type_ldl: ++ *type = disk_type_eckd_classic; ++ return 0; ++ case target_type_cdl: ++ *type = disk_type_eckd_compatible; ++ return 0; ++ default: ++ return -1; ++ } ++} ++ ++ + static int + get_job_from_section_data(char* data[], struct job_data* job, char* section) + { +@@ -869,11 +939,76 @@ get_job_from_section_data(char* data[], struct job_data* job, char* section) + error_text("Unable to find default section in your config file."); + break; + } +- job->bootmap_dir = misc_strdup(default_target); ++ job->target.bootmap_dir = misc_strdup(default_target); + } else +- job->bootmap_dir = misc_strdup(data[(int) scan_keyword_target]); +- if (job->bootmap_dir == NULL) ++ job->target.bootmap_dir = misc_strdup(data[(int) scan_keyword_target]); ++ if (job->target.bootmap_dir == NULL) + return -1; ++ /* Fill in target */ ++ if (data[(int) scan_keyword_targetbase] != NULL) { ++ job->target.targetbase = ++ misc_strdup(data[(int) ++ scan_keyword_targetbase]); ++ if (job->target.targetbase == NULL) ++ return -1; ++ } else { ++ if ((data[(int) scan_keyword_target] == NULL) && ++ (default_targetbase != NULL)) { ++ job->target.targetbase = ++ misc_strdup(default_targetbase); ++ if (job->target.targetbase == NULL) ++ return -1; ++ } ++ } ++ if (data[(int) scan_keyword_targettype] != NULL) { ++ if (type_from_target( ++ data[(int) scan_keyword_targettype], ++ &job->target.targettype)) ++ return -1; ++ } else { ++ if ((data[(int) scan_keyword_target] == NULL) && ++ (default_targettype != NULL)) ++ type_from_target(default_targettype, ++ &job->target.targettype); ++ } ++ if (data[(int) scan_keyword_targetgeometry] != NULL) { ++ job->target.targetcylinders = ++ atoi(strtok(data[(int) ++ scan_keyword_targetgeometry], ",")); ++ job->target.targetheads = atoi(strtok(NULL, ",")); ++ job->target.targetsectors = atoi(strtok(NULL, ",")); ++ } else { ++ if ((data[(int) scan_keyword_target] == NULL) && ++ (default_targetgeometry != NULL)) { ++ temp_targetgeometry = ++ misc_strdup(default_targetgeometry); ++ if (temp_targetgeometry == NULL) ++ return -1; ++ job->target.targetcylinders = ++ atoi(strtok(temp_targetgeometry, ",")); ++ job->target.targetheads = atoi(strtok(NULL, ",")); ++ job->target.targetsectors = atoi(strtok(NULL, ",")); ++ free(temp_targetgeometry); ++ } ++ } ++ if (data[(int) scan_keyword_targetblocksize] != NULL) ++ job->target.targetblocksize = ++ atoi(data[(int) scan_keyword_targetblocksize]); ++ else { ++ if ((data[(int) scan_keyword_target] == NULL) && ++ (default_targetblocksize != NULL)) ++ job->target.targetblocksize = ++ atoi(default_targetblocksize); ++ } ++ if (data[(int) scan_keyword_targetoffset] != NULL) ++ job->target.targetoffset = ++ atol(data[(int) scan_keyword_targetoffset]); ++ else { ++ if ((data[(int) scan_keyword_target] == NULL) && ++ (default_targetoffset != NULL)) ++ job->target.targetoffset = ++ atol(default_targetoffset); ++ } + /* Fill in name and address of image file */ + job->data.ipl.image = misc_strdup( + data[(int) scan_keyword_image]); +@@ -942,8 +1077,9 @@ get_job_from_section_data(char* data[], struct job_data* job, char* section) + /* SEGMENT LOAD job */ + job->id = job_segment; + /* Fill in name of bootmap directory */ +- job->bootmap_dir = misc_strdup(data[(int) scan_keyword_target]); +- if (job->bootmap_dir == NULL) ++ job->target.bootmap_dir = ++ misc_strdup(data[(int) scan_keyword_target]); ++ if (job->target.bootmap_dir == NULL) + return -1; + /* Fill in segment filename */ + job->data.segment.segment = +@@ -979,8 +1115,9 @@ get_job_from_section_data(char* data[], struct job_data* job, char* section) + /* DUMP TO FILESYSTEM job */ + job->id = job_dump_fs; + /* Fill in name of bootmap directory */ +- job->bootmap_dir = misc_strdup(data[(int) scan_keyword_target]); +- if (job->bootmap_dir == NULL) ++ job->target.bootmap_dir = ++ misc_strdup(data[(int) scan_keyword_target]); ++ if (job->target.bootmap_dir == NULL) + return -1; + /* Fill in partition name */ + job->data.dump_fs.partition = +@@ -1085,11 +1222,43 @@ get_menu_job(struct scan_token* scan, char* menu, struct job_data* job) + atol(scan[i].content.keyword.value); + break; + case scan_keyword_target: +- job->bootmap_dir = misc_strdup( ++ job->target.bootmap_dir = misc_strdup( + scan[i].content.keyword.value); +- if (job->bootmap_dir == NULL) ++ if (job->target.bootmap_dir == NULL) + return -1; + break; ++ case scan_keyword_targetbase: ++ job->target.targetbase = misc_strdup( ++ scan[i].content.keyword.value); ++ if (job->target.targetbase == NULL) ++ return -1; ++ break; ++ case scan_keyword_targettype: ++ if (type_from_target( ++ scan[i].content.keyword.value, ++ &job->target.targettype)) ++ return -1; ++ break; ++ case scan_keyword_targetgeometry: ++ job->target.targetcylinders = ++ atoi(strtok( ++ scan[i].content.keyword.value, ++ ",")); ++ job->target.targetheads = ++ atoi(strtok(NULL, ",")); ++ job->target.targetsectors = ++ atoi(strtok(NULL, ",")); ++ break; ++ case scan_keyword_targetblocksize: ++ job->target.targetblocksize = ++ atoi( ++ scan[i].content.keyword.value); ++ break; ++ case scan_keyword_targetoffset: ++ job->target.targetoffset = ++ atol( ++ scan[i].content.keyword.value); ++ break; + default: + /* Should not happen */ + break; +@@ -1142,7 +1311,8 @@ get_menu_job(struct scan_token* scan, char* menu, struct job_data* job) + return -1; + memset((void *) temp_job, 0, sizeof(struct job_data)); + if (data[(int) scan_keyword_target] == NULL) +- data[(int) scan_keyword_target] = misc_strdup(job->bootmap_dir); ++ data[(int) scan_keyword_target] = ++ misc_strdup(job->target.bootmap_dir); + rc = get_job_from_section_data(data, temp_job, + job->data.menu.entry[current].name); + if (rc) { +@@ -1254,6 +1424,56 @@ get_section_job(struct scan_token* scan, char* section, struct job_data* job, + scan[i].content.keyword.keyword == scan_keyword_target && + !strcmp(DEFAULTBOOT_SECTION, name)) + default_target = misc_strdup(scan[i].content.keyword.value); ++ if (scan[i].id == scan_id_keyword_assignment && ++ scan[i].content.keyword.keyword == ++ scan_keyword_targetbase && ++ scan[i].content.keyword.value != NULL && ++ !strcmp(DEFAULTBOOT_SECTION, name)) { ++ default_targetbase = ++ misc_strdup(scan[i].content.keyword.value); ++ if (default_targetbase == NULL) ++ return -1; ++ } ++ if (scan[i].id == scan_id_keyword_assignment && ++ scan[i].content.keyword.keyword == ++ scan_keyword_targettype && ++ scan[i].content.keyword.value != NULL && ++ !strcmp(DEFAULTBOOT_SECTION, name)) { ++ default_targettype = ++ misc_strdup(scan[i].content.keyword.value); ++ if (default_targettype == NULL) ++ return -1; ++ } ++ if (scan[i].id == scan_id_keyword_assignment && ++ scan[i].content.keyword.keyword == ++ scan_keyword_targetgeometry && ++ scan[i].content.keyword.value != NULL && ++ !strcmp(DEFAULTBOOT_SECTION, name)) { ++ default_targetgeometry = ++ misc_strdup(scan[i].content.keyword.value); ++ if (default_targetgeometry == NULL) ++ return -1; ++ } ++ if (scan[i].id == scan_id_keyword_assignment && ++ scan[i].content.keyword.keyword == ++ scan_keyword_targetblocksize && ++ scan[i].content.keyword.value != NULL && ++ !strcmp(DEFAULTBOOT_SECTION, name)) { ++ default_targetblocksize = ++ misc_strdup(scan[i].content.keyword.value); ++ if (default_targetblocksize == NULL) ++ return -1; ++ } ++ if (scan[i].id == scan_id_keyword_assignment && ++ scan[i].content.keyword.keyword == ++ scan_keyword_targetoffset && ++ scan[i].content.keyword.value != NULL && ++ !strcmp(DEFAULTBOOT_SECTION, name)) { ++ default_targetoffset = ++ misc_strdup(scan[i].content.keyword.value); ++ if (default_targetoffset == NULL) ++ return -1; ++ } + } + } + if (strcmp(section, DEFAULTBOOT_SECTION) == 0) { +@@ -1335,16 +1555,25 @@ create_fake_menu(struct scan_token *scan) + int i, j, pos, numsec, size, defaultpos; + char *name; + char *target; ++ char *targetbase; ++ char *targettype; ++ char *targetgeometry; ++ char *targetblocksize; ++ char *targetoffset; + char *timeout; + char *seclist[1024]; + char *defaultsection; + char buf[1024]; + struct scan_token *tmp; +- + /* Count # of sections */ + numsec = 0; + name = NULL; + target = NULL; ++ targetbase = NULL; ++ targettype = NULL; ++ targetgeometry = NULL; ++ targetblocksize = NULL; ++ targetoffset = NULL; + timeout = NULL; + for (i = 0; (int) scan[i].id != 0; i++) { + if (scan[i].id == scan_id_section_heading) { +@@ -1364,6 +1593,36 @@ create_fake_menu(struct scan_token *scan) + target = scan[i].content.keyword.value; + + if (scan[i].id == scan_id_keyword_assignment && ++ scan[i].content.keyword.keyword == ++ scan_keyword_targetbase && ++ !strcmp(DEFAULTBOOT_SECTION, name)) ++ targetbase = scan[i].content.keyword.value; ++ ++ if (scan[i].id == scan_id_keyword_assignment && ++ scan[i].content.keyword.keyword == ++ scan_keyword_targettype && ++ !strcmp(DEFAULTBOOT_SECTION, name)) ++ targettype = scan[i].content.keyword.value; ++ ++ if (scan[i].id == scan_id_keyword_assignment && ++ scan[i].content.keyword.keyword == ++ scan_keyword_targetgeometry && ++ !strcmp(DEFAULTBOOT_SECTION, name)) ++ targetgeometry = scan[i].content.keyword.value; ++ ++ if (scan[i].id == scan_id_keyword_assignment && ++ scan[i].content.keyword.keyword == ++ scan_keyword_targetblocksize && ++ !strcmp(DEFAULTBOOT_SECTION, name)) ++ targetblocksize = scan[i].content.keyword.value; ++ ++ if (scan[i].id == scan_id_keyword_assignment && ++ scan[i].content.keyword.keyword == ++ scan_keyword_targetoffset && ++ !strcmp(DEFAULTBOOT_SECTION, name)) ++ targetoffset = scan[i].content.keyword.value; ++ ++ if (scan[i].id == scan_id_keyword_assignment && + scan[i].content.keyword.keyword == scan_keyword_timeout) + timeout = scan[i].content.keyword.value; + } +@@ -1380,8 +1639,33 @@ create_fake_menu(struct scan_token *scan) + } + + default_target = misc_strdup(target); ++ if (targetbase != NULL) { ++ default_targetbase = misc_strdup(targetbase); ++ if (default_targetbase == NULL) ++ return NULL; ++ } ++ if (targettype != NULL) { ++ default_targettype = misc_strdup(targettype); ++ if (default_targettype == NULL) ++ return NULL; ++ } ++ if (targetgeometry != NULL) { ++ default_targetgeometry = misc_strdup(targetgeometry); ++ if (default_targetgeometry == NULL) ++ return NULL; ++ } ++ if (targetblocksize != NULL) { ++ default_targetblocksize = misc_strdup(targetblocksize); ++ if (default_targetblocksize == NULL) ++ return NULL; ++ } ++ if (targetoffset != NULL) { ++ default_targetoffset = misc_strdup(targetoffset); ++ if (default_targetoffset == NULL) ++ return NULL; ++ } + +- size = i+6+numsec; ++ size = i+11+numsec; + tmp = (struct scan_token *) misc_malloc(size * sizeof(struct scan_token)); + if (tmp == NULL) { + error_text("Couldn't allocate memory for menu entries"); +@@ -1408,6 +1692,46 @@ create_fake_menu(struct scan_token *scan) + scan[i].line = i; + scan[i].content.keyword.keyword = scan_keyword_target; + scan[i++].content.keyword.value = misc_strdup(target); ++ if ( targetbase) { ++ scan[i].id = scan_id_keyword_assignment; ++ scan[i].line = i; ++ scan[i].content.keyword.keyword = scan_keyword_targetbase; ++ scan[i++].content.keyword.value = misc_strdup(targetbase); ++ if (scan[i - 1].content.keyword.value == NULL) ++ return NULL; ++ } ++ if ( targettype) { ++ scan[i].id = scan_id_keyword_assignment; ++ scan[i].line = i; ++ scan[i].content.keyword.keyword = scan_keyword_targettype; ++ scan[i++].content.keyword.value = misc_strdup(targettype); ++ if (scan[i - 1].content.keyword.value == NULL) ++ return NULL; ++ } ++ if ( targetgeometry) { ++ scan[i].id = scan_id_keyword_assignment; ++ scan[i].line = i; ++ scan[i].content.keyword.keyword = scan_keyword_targetgeometry; ++ scan[i++].content.keyword.value = misc_strdup(targetgeometry); ++ if (scan[i - 1].content.keyword.value == NULL) ++ return NULL; ++ } ++ if ( targetblocksize) { ++ scan[i].id = scan_id_keyword_assignment; ++ scan[i].line = i; ++ scan[i].content.keyword.keyword = scan_keyword_targetblocksize; ++ scan[i++].content.keyword.value = misc_strdup(targetblocksize); ++ if (scan[i - 1].content.keyword.value == NULL) ++ return NULL; ++ } ++ if ( targetoffset) { ++ scan[i].id = scan_id_keyword_assignment; ++ scan[i].line = i; ++ scan[i].content.keyword.keyword = scan_keyword_targetoffset; ++ scan[i++].content.keyword.value = misc_strdup(targetoffset); ++ if (scan[i - 1].content.keyword.value == NULL) ++ return NULL; ++ } + scan[i].id = scan_id_keyword_assignment; + scan[i].line = i; + scan[i].content.keyword.keyword = scan_keyword_default; +diff --git a/zipl/src/scan.c b/zipl/src/scan.c +index caca3cf..16da9b3 100644 +--- a/zipl/src/scan.c ++++ b/zipl/src/scan.c +@@ -2,7 +2,7 @@ + * s390-tools/zipl/src/scan.c + * Scanner for zipl.conf configuration files + * +- * Copyright IBM Corp. 2001, 2006. ++ * Copyright IBM Corp. 2001, 2009. + * + * Author(s): Carsten Otte + * Peter Oberparleiter +@@ -31,21 +31,33 @@ enum scan_key_state scan_key_table[SCAN_SECTION_NUM][SCAN_KEYWORD_NUM] = { + /* defa dump dump imag para parm ramd segm targ prom time defa tape mv + * ult to tofs e mete file isk ent et pt out ultm dump + * rs enu ++ * ++ * targ targ targ targ targ ++ * etba etty etge etbl etof ++ * se pe omet ocks fset ++ * ry ize + */ + /* defaultboot */ +- {opt, inv, inv, inv, inv, inv, inv, inv, req, inv, opt, opt, inv, inv}, ++ {opt, inv, inv, inv, inv, inv, inv, inv, req, inv, opt, opt, inv, inv, ++ opt, opt, opt, opt, opt}, + /* ipl */ +- {inv, inv, inv, req, opt, opt, opt, inv, opt, inv, inv, inv, inv, inv}, ++ {inv, inv, inv, req, opt, opt, opt, inv, opt, inv, inv, inv, inv, inv, ++ opt, opt, opt, opt, opt}, + /* segment load */ +- {inv, inv, inv, inv, inv, inv, inv, req, req, inv, inv, inv, inv, inv}, ++ {inv, inv, inv, inv, inv, inv, inv, req, req, inv, inv, inv, inv, inv, ++ inv, inv, inv, inv, inv}, + /* part dump */ +- {inv, req, inv, inv, inv, inv, inv, inv, opt, inv, inv, inv, inv, inv}, ++ {inv, req, inv, inv, inv, inv, inv, inv, opt, inv, inv, inv, inv, inv, ++ inv, inv, inv, inv, inv}, + /* fs dump */ +- {inv, inv, req, inv, opt, opt, inv, inv, req, inv, inv, inv, inv, inv}, ++ {inv, inv, req, inv, opt, opt, inv, inv, req, inv, inv, inv, inv, inv, ++ inv, inv, inv, inv, inv}, + /* ipl tape */ +- {inv, inv, inv, req, opt, opt, opt, inv, inv, inv, inv, inv, req, inv}, ++ {inv, inv, inv, req, opt, opt, opt, inv, inv, inv, inv, inv, req, inv, ++ inv, inv, inv, inv, inv}, + /* multi volume dump */ +- {inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req} ++ {inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, ++ inv, inv, inv, inv, inv} + }; + + /* Mapping of keyword IDs to strings */ +@@ -63,13 +75,17 @@ static const struct { + { "parmfile", scan_keyword_parmfile }, + { "ramdisk", scan_keyword_ramdisk }, + { "segment", scan_keyword_segment }, ++ { "targetbase", scan_keyword_targetbase}, ++ { "targettype", scan_keyword_targettype}, ++ { "targetgeometry", scan_keyword_targetgeometry}, ++ { "targetblocksize", scan_keyword_targetblocksize}, ++ { "targetoffset", scan_keyword_targetoffset}, + { "target", scan_keyword_target}, + { "prompt", scan_keyword_prompt}, + { "timeout", scan_keyword_timeout}, + { "tape", scan_keyword_tape} + }; + +- + /* Retrieve name of keyword identified by ID. */ + char * + scan_keyword_name(enum scan_keyword_id id) +@@ -608,6 +624,19 @@ scan_get_section_type(char* keyword[]) + return section_invalid; + } + ++enum scan_target_type ++scan_get_target_type(char *type) ++{ ++ if (strcasecmp(type, "SCSI") == 0) ++ return target_type_scsi; ++ else if (strcasecmp(type, "FBA") == 0) ++ return target_type_fba; ++ else if (strcasecmp(type, "LDL") == 0) ++ return target_type_ldl; ++ else if (strcasecmp(type, "CDL") == 0) ++ return target_type_cdl; ++ return target_type_invalid; ++} + + #define MAX(a,b) ((a)>(b)?(a):(b)) + +@@ -643,9 +672,13 @@ scan_check_section_data(char* keyword[], int* line, char* name, + } else if (keyword[(int) scan_keyword_mvdump]) { + *type = section_mvdump; + main_keyword = scan_keyword_name(scan_keyword_mvdump); +- } else +- /* Incomplete section data */ ++ } else { ++ error_reason("Line %d: section '%s' must contain " ++ "either one of keywords 'image', " ++ "'segment', 'dumpto', 'dumptofs', " ++ "'mvdump' or 'tape'", section_line, name); + return -1; ++ } + } + /* Check keywords */ + for (i=0; i < SCAN_KEYWORD_NUM; i++) { +@@ -734,6 +767,174 @@ scan_check_section_data(char* keyword[], int* line, char* name, + } + + ++static int ++check_blocksize(int size) ++{ ++ switch (size) { ++ case 512: ++ case 1024: ++ case 2048: ++ case 4096: ++ return 0; ++ } ++ return -1; ++} ++ ++ ++int ++scan_check_target_data(char* keyword[], int* line) ++{ ++ int cylinders, heads, sectors; ++ char dummy; ++ int number; ++ enum scan_keyword_id errid; ++ ++ if ((keyword[(int) scan_keyword_targetbase] != 0) && ++ (keyword[(int) scan_keyword_target] == 0)) { ++ if (line != NULL) ++ error_reason("Line %d: keyword 'target' required " ++ "when specifying 'targetbase'", ++ line[(int) scan_keyword_targetbase]); ++ else ++ error_reason("Option 'target' required when " ++ "specifying 'targetbase'"); ++ return -1; ++ } ++ if (keyword[(int) scan_keyword_targetbase] == 0) { ++ if (keyword[(int) scan_keyword_targettype] != 0) ++ errid = scan_keyword_targettype; ++ else if ((keyword[(int) scan_keyword_targetgeometry] != 0)) ++ errid = scan_keyword_targetgeometry; ++ else if ((keyword[(int) scan_keyword_targetblocksize] != 0)) ++ errid = scan_keyword_targetblocksize; ++ else if ((keyword[(int) scan_keyword_targetoffset] != 0)) ++ errid = scan_keyword_targetoffset; ++ else ++ return 0; ++ if (line != NULL) ++ error_reason("Line %d: keyword 'targetbase' required " ++ "when specifying '%s'", ++ line[(int) errid], scan_keyword_name(errid)); ++ else ++ error_reason("Option 'targetbase' required when " ++ "specifying '%s'", ++ scan_keyword_name(errid)); ++ return -1; ++ } ++ if (keyword[(int) scan_keyword_targettype] == 0) { ++ if (line != NULL) ++ error_reason("Line %d: keyword 'targettype' " ++ "required when specifying 'targetbase'", ++ line[(int) scan_keyword_targetbase]); ++ else ++ error_reason("Option 'targettype' required " ++ "when specifying 'targetbase'"); ++ return -1; ++ } ++ switch (scan_get_target_type(keyword[(int) scan_keyword_targettype])) { ++ case target_type_cdl: ++ case target_type_ldl: ++ if ((keyword[(int) scan_keyword_targetgeometry] != 0)) ++ break; ++ if (line != NULL) ++ error_reason("Line %d: keyword 'targetgeometry' " ++ "required when specifying 'targettype' %s", ++ line[(int) scan_keyword_targettype], ++ keyword[(int) scan_keyword_targettype]); ++ else ++ error_reason("Option 'targetgeometry' required when " ++ "specifying 'targettype' %s", ++ keyword[(int) scan_keyword_targettype]); ++ return -1; ++ case target_type_scsi: ++ case target_type_fba: ++ if ((keyword[(int) scan_keyword_targetgeometry] == 0)) ++ break; ++ if (line != NULL) ++ error_reason("Line %d: keyword " ++ "'targetgeometry' not allowed for " ++ "'targettype' %s", ++ line[(int) scan_keyword_targetgeometry], ++ keyword[(int) scan_keyword_targettype]); ++ else ++ error_reason("Keyword 'targetgeometry' not " ++ "allowed for 'targettype' %s", ++ keyword[(int) scan_keyword_targettype]); ++ return -1; ++ case target_type_invalid: ++ if (line != NULL) ++ error_reason("Line %d: Unrecognized 'targettype' value " ++ "'%s'", ++ line[(int) scan_keyword_targettype], ++ keyword[(int) scan_keyword_targettype]); ++ else ++ error_reason("Unrecognized 'targettype' value '%s'", ++ keyword[(int) scan_keyword_targettype]); ++ return -1; ++ } ++ if (keyword[(int) scan_keyword_targetgeometry] != 0) { ++ if ((sscanf(keyword[(int) scan_keyword_targetgeometry], ++ "%d,%d,%d %c", &cylinders, &heads, §ors, &dummy) ++ != 3) || (cylinders <= 0) || (heads <= 0) || ++ (sectors <= 0)) { ++ if (line != NULL) ++ error_reason("Line %d: Invalid target geometry " ++ "'%s'", line[ ++ (int) scan_keyword_targetgeometry], ++ keyword[ ++ (int) scan_keyword_targetgeometry]); ++ else ++ error_reason("Invalid target geometry '%s'", ++ keyword[ ++ (int) scan_keyword_targetgeometry]); ++ return -1; ++ } ++ } ++ if (keyword[(int) scan_keyword_targetblocksize] == 0) { ++ if (line != NULL) ++ error_reason("Line %d: Keyword 'targetblocksize' " ++ "required when specifying 'targetbase'", ++ line[(int) scan_keyword_targetbase]); ++ else ++ error_reason("Option 'targetblocksize' required when " ++ "specifying 'targetbase'"); ++ return -1; ++ } ++ if ((sscanf(keyword[(int) scan_keyword_targetblocksize], "%d %c", ++ &number, &dummy) != 1) || check_blocksize(number)) { ++ if (line != NULL) ++ error_reason("Line %d: Invalid target blocksize '%s'", ++ line[(int) scan_keyword_targetblocksize], ++ keyword[(int) scan_keyword_targetblocksize]); ++ else ++ error_reason("Invalid target blocksize '%s'", ++ keyword[(int) scan_keyword_targetblocksize]); ++ return -1; ++ } ++ if (keyword[(int) scan_keyword_targetoffset] == 0) { ++ if (line != NULL) ++ error_reason("Line %d: Keyword 'targetoffset' " ++ "required when specifying 'targetbase'", ++ line[(int) scan_keyword_targetbase]); ++ else ++ error_reason("Option 'targetoffset' required when " ++ "specifying 'targetbase'"); ++ return -1; ++ } ++ if (sscanf(keyword[(int) scan_keyword_targetoffset], "%d %c", ++ &number, &dummy) != 1) { ++ if (line != NULL) ++ error_reason("Line %d: Invalid target offset '%s'", ++ line[(int) scan_keyword_targetoffset], ++ keyword[(int) scan_keyword_targetoffset]); ++ else ++ error_reason("Invalid target offset '%s'", ++ keyword[(int) scan_keyword_targetoffset]); ++ return -1; ++ } ++ return 0; ++} ++ + /* Check section at INDEX for compliance with config file rules. Upon success, + * return zero and advance INDEX to point to the end of the section. Return + * non-zero otherwise. */ +@@ -764,6 +965,7 @@ check_section(struct scan_token* scan, int* index) + else + type = section_invalid; + memset(keyword, 0, sizeof(keyword)); ++ memset(keyword_line, 0, sizeof(keyword_line)); + line = scan[i].line; + /* Account for keywords */ + for (i++; (int) scan[i].id != 0; i++) { +@@ -802,13 +1004,10 @@ check_section(struct scan_token* scan, int* index) + } + } + rc = scan_check_section_data(keyword, keyword_line, name, line, &type); +- /* Check for missing keyword */ +- if (type == section_invalid) { +- error_reason("Line %d: section '%s' must contain either one " +- "of keywords 'image', 'segment', 'dumpto', " +- "'dumptofs', 'mvdump' or 'tape'", line, name); +- return -1; +- } ++ if (rc) ++ return rc; ++ /* Check target data */ ++ rc = scan_check_target_data(keyword, keyword_line); + if (rc) + return rc; + /* Advance index to end of section */ +@@ -840,6 +1039,8 @@ find_num_assignment(struct scan_token* scan, int num, int offset) + static int + check_menu(struct scan_token* scan, int* index) + { ++ char* keyword[SCAN_KEYWORD_NUM]; ++ int keyword_line[SCAN_KEYWORD_NUM]; + enum scan_keyword_id key_id; + char* name; + char* str; +@@ -852,6 +1053,7 @@ check_menu(struct scan_token* scan, int* index) + int is_default; + int is_prompt; + int is_timeout; ++ int rc; + + i = *index; + name = scan[i].content.menu.name; +@@ -862,6 +1064,8 @@ check_menu(struct scan_token* scan, int* index) + scan[line].line, name); + return -1; + } ++ memset(keyword, 0, sizeof(keyword)); ++ memset(keyword_line, 0, sizeof(keyword_line)); + line = scan[i].line; + is_num = 0; + is_target = 0; +@@ -886,6 +1090,24 @@ check_menu(struct scan_token* scan, int* index) + } + is_target = 1; + break; ++ case scan_keyword_targetbase: ++ case scan_keyword_targettype: ++ case scan_keyword_targetgeometry: ++ case scan_keyword_targetblocksize: ++ case scan_keyword_targetoffset: ++ key_id = scan[i].content.keyword.keyword; ++ keyword_line[key_id] = scan[i].line; ++ /* Rule 5 */ ++ if (keyword[(int) key_id] != NULL) { ++ error_reason("Line %d: keyword '%s' " ++ "already specified", ++ scan[i].line, ++ scan_keyword_name(key_id)); ++ return -1; ++ } ++ keyword[(int) key_id] = ++ scan[i].content.keyword.value; ++ break; + case scan_keyword_default: + if (is_default) { + error_reason("Line %d: keyword '%s' " +@@ -1044,6 +1266,10 @@ check_menu(struct scan_token* scan, int* index) + name); + return -1; + } ++ /* Check target data */ ++ rc = scan_check_target_data(keyword, keyword_line); ++ if (rc) ++ return rc; + /* Advance index to end of menu section */ + *index = i - 1; + return 0; +diff --git a/zipl/src/zipl.c b/zipl/src/zipl.c +index 4d9fd36..3a4c18c 100644 +--- a/zipl/src/zipl.c ++++ b/zipl/src/zipl.c +@@ -2,7 +2,7 @@ + * s390-tools/zipl/src/zipl.c + * zSeries Initial Program Loader tool. + * +- * Copyright IBM Corp. 2001, 2006. ++ * Copyright IBM Corp. 2001, 2009. + * + * Author(s): Carsten Otte + * Peter Oberparleiter +@@ -41,7 +41,7 @@ int dry_run = 1; + static const char tool_name[] = "zipl: zSeries Initial Program Loader"; + + /* Copyright notice */ +-static const char copyright_notice[] = "Copyright IBM Corp. 2001, 2008"; ++static const char copyright_notice[] = "Copyright IBM Corp. 2001, 2009"; + + /* Usage information */ + static const char* usage_text[] = { +@@ -55,6 +55,11 @@ static const char* usage_text[] = { + "-c, --config CONFIGFILE Read configuration from CONFIGFILE", + "-t, --target TARGETDIR Write bootmap file to TARGETDIR and install", + " bootloader on device containing TARGETDIR", ++" --targetbase BASEDEVICE Install bootloader on BASEDEVICE", ++" --targettype TYPE Use device type: CDL, LDL, FBA, SCSI", ++" --targetgeometry C,H,S Use disk geometry: cylinders,heads,sectors", ++" --targetblocksize SIZE Use number of bytes per block", ++" --targetoffset OFFSET Use offset between logical and physical disk", + "-i, --image IMAGEFILE[,ADDR] Install Linux kernel image from IMAGEFILE", + "-r, --ramdisk RAMDISK[,ADDR] Install initial ramdisk from file RAMDISK", + "-p, --parmfile PARMFILE[,ADDR] Use kernel parmline stored in PARMFILE", +@@ -190,10 +195,12 @@ main(int argc, char* argv[]) + break; + case job_dump_partition: + /* Retrieve target device information */ +- rc = install_dump(job->data.dump.device, job->data.dump.mem); ++ rc = install_dump(job->data.dump.device, &job->target, ++ job->data.dump.mem); + break; + case job_mvdump: + rc = install_mvdump(job->data.mvdump.device, ++ &job->target, + job->data.mvdump.device_count, + job->data.mvdump.mem, + job->data.mvdump.force); +diff --git a/zipl/src/zipl_helper.device-mapper b/zipl/src/zipl_helper.device-mapper +new file mode 100644 +index 0000000..669f3e3 +--- /dev/null ++++ b/zipl/src/zipl_helper.device-mapper +@@ -0,0 +1,716 @@ ++#!/usr/bin/perl -w ++# ++# zipl_helper.device-mapper: print zipl parameters for a device-mapper device ++# ++# Copyright IBM Corp. 2009 ++# ++# Author(s): Peter Oberparleiter ++# ++# Usage: zipl_helper.device-mapper ++# ++# This tool attempts to obtain zipl parameters for a target directory located ++# on a device-mapper device. It assumes that the device-mapper table for this ++# device conforms to the following rules: ++# - directory is located on a device consisting of a single device-mapper ++# target ++# - only linear, mirror and multipath targets are supported ++# - supported physical device types are DASD and SCSI devices ++# - all of the device which contains the directory must be located on a single ++# physical device (which may be mirrorred or accessed through a multipath ++# target) ++# - any mirror in the device-mapper setup must include block 0 of the ++# physical device ++# ++ ++use strict; ++use File::Basename; ++ ++# Required tools ++our $dmsetup = "dmsetup"; ++our $mknod = "mknod"; ++our $dasdview = "dasdview"; ++our $blockdev = "blockdev"; ++ ++# Constants ++our $SECTOR_SIZE = 512; ++our $DASD_PARTN_MASK = 0x03; ++our $SCSI_PARTN_MASK = 0x0f; ++ ++# Internal constants ++our $DEV_TYPE_CDL = 0; ++our $DEV_TYPE_LDL = 1; ++our $DEV_TYPE_FBA = 2; ++our $DEV_TYPE_SCSI = 3; ++ ++our $TARGET_START = 0; ++our $TARGET_LENGTH = 1; ++our $TARGET_TYPE = 2; ++our $TARGET_DATA = 3; ++ ++our $TARGET_TYPE_LINEAR = 0; ++our $TARGET_TYPE_MIRROR = 1; ++our $TARGET_TYPE_MULTIPATH = 2; ++ ++our $LINEAR_MAJOR = 0; ++our $LINEAR_MINOR = 1; ++our $LINEAR_START_SECTOR = 2; ++ ++our $MIRROR_MAJOR = 0; ++our $MIRROR_MINOR = 1; ++our $MIRROR_START_SECTOR = 2; ++ ++our $MULTIPATH_MAJOR = 0; ++our $MULTIPATH_MINOR = 1; ++ ++sub get_physical_device($); ++sub get_major_minor($); ++sub get_table($$); ++sub get_linear_data($$); ++sub get_mirror_data($$); ++sub get_multipath_data($$); ++sub filter_table($$$); ++sub get_target_start($); ++sub get_target_major_minor($); ++sub create_temp_device_node($$$); ++sub get_blocksize($); ++sub get_dasd_info($); ++sub get_partition_start($); ++sub is_dasd($); ++sub get_partition_base($$$); ++sub get_device_characteristics($$); ++sub get_type_name($); ++sub check_for_mirror($@); ++sub get_target_base($$$$@); ++sub get_device_name($$); ++ ++my $phy_geometry; # Disk geometry of physical device ++my $phy_blocksize; # Blocksize of physical device ++my $phy_offset; # Offset in 512-byte sectors between start of physical ++ # device and start of filesystem ++my $phy_type; # Type of physical device ++my $phy_bootsectors; # Size of boot record in 512-byte sectors ++my $phy_partstart; # Partition offset of physical device ++my $phy_major; # Major device number of physical device ++my $phy_minor; # Minor device number of physical device ++my @target_list; # List of dm-targets between filesystem and physical ++ # device. ++my $base_major; # Major device number of base device. ++my $base_minor; # Minor device number of base device ++my $directory; # Command line parameter ++my $toolname; # Name of tool ++ ++# Start ++$toolname = basename($0); ++$directory = $ARGV[0]; ++if (!defined($directory)) { ++ die("Usage: $toolname \n"); ++} ++ ++# Determine physical (non-dm) device on which directory is located ++($phy_major, $phy_minor, $phy_offset, @target_list) = ++ get_physical_device($directory); ++# Determine type and characteristics of physical device ++($phy_type, $phy_blocksize, $phy_geometry, $phy_bootsectors, $phy_partstart) = ++ get_device_characteristics($phy_major, $phy_minor); ++ ++# Handle partitions ++if ($phy_partstart > 0) { ++ # Only the partition of the physical device is mapped so only the ++ # physical device can provide access to the boot record. ++ ($base_major, $base_minor) = ++ get_partition_base($phy_type, $phy_major, $phy_minor); ++ # Check for mirror ++ check_for_mirror(scalar(@target_list) - 1, @target_list); ++ # Adjust filesystem offset ++ $phy_offset += $phy_partstart * ($phy_blocksize / $SECTOR_SIZE); ++ $phy_partstart = 0; ++ # Update device geometry ++ (undef, undef, $phy_geometry, undef, undef) = ++ get_device_characteristics($base_major, $base_minor); ++} else { ++ # All of the device is mapped, so the base device is the top most ++ # dm device which provides access to boot sectors ++ ($base_major, $base_minor) = ++ get_target_base($phy_major, $phy_minor, 0, $phy_bootsectors, ++ @target_list); ++} ++ ++# Check for valid offset of file system ++if (($phy_offset % ($phy_blocksize / $SECTOR_SIZE)) != 0) { ++ die("Error: File system not aligned on physical block size\n"); ++} ++ ++# Print resulting information ++print("targetbase=$base_major:$base_minor\n"); ++print("targettype=".get_type_name($phy_type)."\n"); ++if (defined($phy_geometry)) { ++ print("targetgeometry=$phy_geometry\n"); ++} ++print("targetblocksize=$phy_blocksize\n"); ++print("targetoffset=".($phy_offset / ($phy_blocksize / $SECTOR_SIZE))."\n"); ++ ++exit(0); ++ ++# get_physical_device(directory) ++# Returns (phy_major, phy_minor, phy_offset, @target_list). ++# target_list: [target_data1, target_data2, ..., target_datan] ++# target_data: [major, minor, target] ++sub get_physical_device($) ++{ ++ my ($directory) = @_; ++ my $major; ++ my $minor; ++ my $table; ++ my $target; ++ my $start; ++ my $length; ++ my @target_list; ++ ++ # Get information about device containing filesystem ++ ($major, $minor) = get_major_minor($directory); ++ $table = get_table($major, $minor); ++ if (scalar(@$table) == 0) { ++ die("Error: Could not retrieve device-mapper information for ". ++ "device '".get_device_name($major, $minor)."'\n"); ++ } ++ # Filesystem must be on a single dm target ++ if (scalar(@$table) != 1) { ++ die("Error: Unsupported setup: Directory '$directory' is ". ++ "located on a multi-target device-mapper device\n"); ++ } ++ ++ $target = $table->[0]; ++ push(@target_list, [$major, $minor, $target]); ++ $start = $target->[$TARGET_START]; ++ $length = $target->[$TARGET_LENGTH]; ++ while (1) { ++ # Convert fs_start to offset on parent dm device ++ $start += get_target_start($target); ++ ($major, $minor) = get_target_major_minor($target); ++ $table = get_table($major, $minor); ++ if (scalar(@$table) == 0) { ++ # Found non-dm device ++ return ($major, $minor, $start, @target_list); ++ } ++ # Get target in parent table which contains filesystem ++ $table = filter_table($table, $start, $length); ++ if (scalar(@$table) != 1) { ++ die("Error: Unsupported setup: Could not map ". ++ "directory '$directory' to a single physical ". ++ "device\n"); ++ } ++ $target = $table->[0]; ++ push(@target_list, [$major, $minor, $target]); ++ # Convert fs_start to offset on parent target ++ $start -= $target->[$TARGET_START]; ++ } ++} ++ ++# get_major_minor(filename) ++# Returns: (device major, device minor) of the device containing the ++# specified file. ++sub get_major_minor($) ++{ ++ my ($filename) = @_; ++ my @stat; ++ my $dev; ++ my $major; ++ my $minor; ++ ++ @stat = stat($filename); ++ if (!@stat) { ++ die("Error: Could not stat '$filename'\n"); ++ } ++ $dev = $stat[0]; ++ $major = ($dev & 0xfff00) >> 8; ++ $minor = ($dev & 0xff) | (($dev >> 12) & 0xfff00); ++ ++ return ($major, $minor); ++} ++ ++# get_table(major, minor) ++# Returns: [target1, target2, ..., targetn] ++# target: [start, length, type, data] ++# data: linear_data|mirror_data|multipath_data ++sub get_table($$) ++{ ++ my ($major, $minor) = @_; ++ my @table; ++ my $dev_name = get_device_name($major, $minor); ++ local *HANDLE; ++ ++ open(HANDLE, "$dmsetup table -j $major -m $minor 2>/dev/null|") or ++ return undef; ++ while () { ++ if (!(/^(\d+)\s+(\d+)\s+(\S+)\s+(\S.*)$/)) { ++ die("Error: Unrecognized device-mapper table format ". ++ "for device '$dev_name'\n"); ++ } ++ my ($start, $length, $target_type, $args) = ($1, $2, $3, $4); ++ my $data; ++ my $type; ++ ++ if ($target_type eq "linear") { ++ $type = $TARGET_TYPE_LINEAR; ++ $data = get_linear_data($dev_name, $args); ++ } elsif ($target_type eq "mirror") { ++ $type = $TARGET_TYPE_MIRROR; ++ $data = get_mirror_data($dev_name, $args); ++ } elsif ($target_type eq "multipath") { ++ $type = $TARGET_TYPE_MULTIPATH; ++ $data = get_multipath_data($dev_name, $args); ++ } else { ++ die("Error: Unsupported setup: Unsupported ". ++ "device-mapper target type '$target_type' for ". ++ "device '$dev_name'\n"); ++ } ++ push(@table, [$start, $length, $type, $data]); ++ } ++ close(HANDLE); ++ return \@table; ++} ++ ++# get_linear_data(dev_name, args) ++# Returns: [major, minor, start_sector] ++sub get_linear_data($$) ++{ ++ my ($dev_name, $args) = @_; ++ ++ if (!($args =~ /^(\d+):(\d+)\s+(\d+)$/)) { ++ die("Error: Unrecognized device-mapper table format for ". ++ "device '$dev_name'\n"); ++ } ++ return [$1, $2, $3]; ++} ++ ++# get_mirror_data(dev_name, args) ++# Returns [[major1, minor1, start_sector1], [major2, minor2, start_sector2], ..] ++sub get_mirror_data($$) ++{ ++ my ($dev_name, $args) = @_; ++ my @argv = split(/\s+/, $args); ++ my @data; ++ my $offset; ++ ++ # Remove log_type + #logargs + logargs + #devs ++ splice(@argv, 0, $argv[1] + 3); ++ if (!@argv) { ++ goto out_error; ++ } ++ while (@argv) { ++ if (!($argv[0] =~ /^(\d+):(\d+)$/)) { ++ goto out_error; ++ } ++ push(@data, [$1, $2, $argv[1]]); ++ if (!defined($offset)) { ++ $offset = $argv[1]; ++ } elsif ($argv[1] != $offset) { ++ die("Error: Unsupported setup: Mirror target on ". ++ "device '$dev_name' contains entries with varying ". ++ "sector offsets\n"); ++ } ++ splice(@argv, 0, 2); ++ } ++ if (!scalar(@data)) { ++ goto out_error; ++ } ++ return \@data; ++ ++out_error: ++ die("Error: Unrecognized device-mapper table format for device ". ++ "'$dev_name'\n"); ++} ++ ++# get_multipath_data(dev_name, args) ++# Returns [[major1, minor1], [major2, minor2], ..] ++sub get_multipath_data($$) ++{ ++ my ($dev_name, $args) = @_; ++ my @argv = split(/\s+/, $args); ++ my @data; ++ ++ # Remove #features + features ++ splice(@argv, 0, $argv[0] + 1); ++ if (!@argv) { ++ goto out_error; ++ } ++ # Remove #handlerargs + handlerargs ++ splice(@argv, 0, $argv[0] + 1); ++ if (!@argv) { ++ goto out_error; ++ } ++ # Remove #pathgroups + pathgroup ++ splice(@argv, 0, 2); ++ while (@argv) { ++ # Remove pathselector + #selectorargs + selectorargs ++ splice(@argv, 0, 2 + $argv[1]); ++ if (!@argv) { ++ goto out_error; ++ } ++ my $num_paths = $argv[0]; ++ my $num_path_args = $argv[1]; ++ # Remove #paths + #pathargs ++ splice(@argv, 0, 2); ++ while ($num_paths-- > 0) { ++ if (!@argv) { ++ goto out_error; ++ } ++ if (!($argv[0] =~ /(\d+):(\d+)/)) { ++ goto out_error; ++ } ++ push(@data, [$1, $2]); ++ # Remove device + deviceargs ++ splice(@argv, 0, 1 + $num_path_args); ++ } ++ } ++ if (!@data) { ++ goto out_error; ++ } ++ return \@data; ++ ++out_error: ++ die("Error: Unrecognized device-mapper table format for device ". ++ "'$dev_name'\n"); ++} ++ ++# filter_table(table, start, length) ++# Returns table containing only targets between start and start + length - 1. ++sub filter_table($$$) ++{ ++ my ($table, $start, $length) = @_; ++ my $end = $start + $length - 1; ++ my @result; ++ my $target; ++ ++ foreach $target (@$table) { ++ my $target_start = $target->[$TARGET_START]; ++ my $target_end = $target_start + $target->[$TARGET_LENGTH] - 1; ++ ++ if (!(($target_end < $start) || ($target_start > $end))) { ++ push(@result, $target); ++ } ++ } ++ return \@result; ++} ++ ++# get_target_start(target) ++# Returns the start sector of target. ++sub get_target_start($) ++{ ++ my ($target) = @_; ++ my $type = $target->[$TARGET_TYPE]; ++ my $data = $target->[$TARGET_DATA]; ++ ++ if ($type == $TARGET_TYPE_LINEAR) { ++ return $data->[$LINEAR_START_SECTOR]; ++ } elsif ($type == $TARGET_TYPE_MIRROR) { ++ my $mirror_data = $data->[0]; ++ return $mirror_data->[$MIRROR_START_SECTOR]; ++ } else { ++ return 0; ++ } ++} ++ ++# get_target_major_minor(target) ++# Returns (major, minor) of target of target. ++sub get_target_major_minor($) ++{ ++ my ($target) = @_; ++ my $type = $target->[$TARGET_TYPE]; ++ my $data = $target->[$TARGET_DATA]; ++ my $major; ++ my $minor; ++ ++ if ($type == $TARGET_TYPE_LINEAR) { ++ $major = $data->[$LINEAR_MAJOR]; ++ $minor = $data->[$LINEAR_MINOR]; ++ } elsif ($type == $TARGET_TYPE_MIRROR) { ++ # Use data of first device in list ++ my $mirror_data = $data->[0]; ++ $major = $mirror_data->[$MIRROR_MAJOR]; ++ $minor = $mirror_data->[$MIRROR_MINOR]; ++ } elsif ($type == $TARGET_TYPE_MULTIPATH) { ++ # Use data of first device in list ++ my $multipath_data = $data->[0]; ++ $major = $multipath_data->[$MULTIPATH_MAJOR]; ++ $minor = $multipath_data->[$MULTIPATH_MINOR]; ++ } ++ return ($major, $minor); ++} ++ ++# create_temp_device_node(type, major, minor) ++# Returns the name of a temporary device node. ++sub create_temp_device_node($$$) ++{ ++ my ($type, $major, $minor) = @_; ++ my $path = "/dev"; ++ my $name; ++ my $num; ++ ++ for ($num = 0; $num < 100; $num++) { ++ $name = sprintf("$path/zipl-dm-temp-%02d", $num); ++ if (-e $name) { ++ next; ++ } ++ if (system("$mknod $name $type $major $minor --mode 0600 ". ++ "2>/dev/null")) { ++ next; ++ } ++ return $name; ++ } ++ die("Error: Could not create temporary device node in '$path'\n"); ++} ++ ++# get_blocksize(device) ++# # Return blocksize in bytes for device. ++sub get_blocksize($) ++{ ++ my ($dev) = @_; ++ my $blocksize; ++ local *HANDLE; ++ ++ open(HANDLE, "$blockdev --getss $dev 2>/dev/null|") or ++ return undef; ++ $blocksize = ; ++ chomp($blocksize); ++ close(HANDLE); ++ ++ return $blocksize; ++} ++ ++# get_dasd_info(device) ++# Returns (type, cylinders, heads, sectors) ++sub get_dasd_info($) ++{ ++ my ($dev) = @_; ++ my $disk_type; ++ my $format; ++ my $cyl; ++ my $heads; ++ my $sectors; ++ my $type; ++ local *HANDLE; ++ ++ open(HANDLE, "$dasdview -x -f $dev 2>/dev/null|") or ++ # dasdview returned with an error ++ return undef; ++ while () { ++ if (/^number of cylinders.*\s(\d+)\s*$/) { ++ $cyl = $1; ++ } elsif (/^tracks per cylinder.*\s(\d+)\s*$/) { ++ $heads = $1; ++ } elsif (/^blocks per track.*\s(\d+)\s*$/) { ++ $sectors = $1; ++ } elsif (/^type\s+:\s+(\S+)\s*$/) { ++ $disk_type = $1; ++ } elsif (/^format.*\s+dec\s(\d+)\s/) { ++ $format = $1; ++ } ++ } ++ close(HANDLE); ++ if (!defined($cyl) || !defined($heads) || !defined($sectors) || ++ !defined($disk_type) || !defined($format)) { ++ # Unrecognized dadsview output format ++ return undef; ++ } ++ if ($disk_type eq "FBA") { ++ $type = $DEV_TYPE_FBA; ++ } elsif ($disk_type eq "ECKD") { ++ if ($format == 1) { ++ $type = $DEV_TYPE_LDL; ++ } elsif ($format == 2) { ++ $type = $DEV_TYPE_CDL; ++ } ++ } ++ ++ return ($type, $cyl, $heads, $sectors); ++} ++ ++# get_partition_start(device) ++# Return the partition offset of device. ++sub get_partition_start($) ++{ ++ my ($dev) = @_; ++ my $line; ++ my $offset; ++ local *HANDLE; ++ ++ open(HANDLE, "$blockdev --report $dev 2>/dev/null|") or ++ return undef; ++ $line = ; ++ if ($line =~ /RO\s+RA\s+SSZ\s+BSZ\s+StartSec\s+Size\s+Device/) { ++ $line = ; ++ if ($line =~ /^\S+\s+\d+\s+\d+\s+\d+\s+(\d+)/) { ++ $offset = $1; ++ } ++ } ++ close(HANDLE); ++ return $offset; ++} ++ ++# is_dasd(type) ++# Return whether disk with type is a DASD. ++sub is_dasd($) ++{ ++ my ($type) = @_; ++ ++ return ($type == $DEV_TYPE_CDL) || ($type == $DEV_TYPE_LDL) || ++ ($type == $DEV_TYPE_FBA); ++} ++ ++# get_partition_base(type, major, minor) ++# Return (major, minor) of the base device on which the partition is located. ++sub get_partition_base($$$) ++{ ++ my ($type, $major, $minor) = @_; ++ ++ if (is_dasd($type)) { ++ return ($major, $minor & ~$DASD_PARTN_MASK); ++ } else { ++ return ($major, $minor & ~$SCSI_PARTN_MASK); ++ } ++} ++ ++# get_device_characteristics(major, minor) ++# Returns (type, blocksize, geometry, bootsectors, partstart) for device. ++sub get_device_characteristics($$) ++{ ++ my ($major, $minor) = @_; ++ my $dev; ++ my $blocksize; ++ my $type; ++ my $cyl; ++ my $heads; ++ my $sectors; ++ my $geometry; ++ my $bootsectors; ++ my $partstart; ++ ++ $dev = create_temp_device_node("b", $major, $minor); ++ $blocksize = get_blocksize($dev); ++ if (!defined($blocksize)) { ++ unlink($dev); ++ die("Error: Could not get block size for ". ++ get_device_name($major, $minor)."\n"); ++ } ++ ($type, $cyl, $heads, $sectors) = get_dasd_info($dev); ++ if (defined($type)) { ++ $geometry = "$cyl,$heads,$sectors"; ++ if ($type == $DEV_TYPE_CDL) { ++ # First track contains IPL records ++ $bootsectors = $blocksize * $sectors / $SECTOR_SIZE; ++ } elsif ($type == $DEV_TYPE_LDL) { ++ # First two blocks contain IPL records ++ $bootsectors = $blocksize * 2 / $SECTOR_SIZE; ++ } elsif ($type == $DEV_TYPE_FBA) { ++ # First block contains IPL records ++ $bootsectors = $blocksize / $SECTOR_SIZE; ++ } ++ } else { ++ # Assume SCSI if get_dasd_info failed ++ $type = $DEV_TYPE_SCSI; ++ # First block contains IPL records ++ $bootsectors = $blocksize / $SECTOR_SIZE; ++ } ++ $partstart = get_partition_start($dev); ++ unlink($dev); ++ if (!defined($partstart)) { ++ die("Error: Could not determine partition start for ". ++ get_device_name($major, $minor)."\n"); ++ } ++ return ($type, $blocksize, $geometry, $bootsectors, $partstart); ++} ++ ++# get_type_name(type) ++# Return textual representation of device type. ++sub get_type_name($) ++{ ++ my ($type) = @_; ++ ++ if ($type == $DEV_TYPE_CDL) { ++ return "CDL"; ++ } elsif ($type == $DEV_TYPE_LDL) { ++ return "LDL"; ++ } elsif ($type == $DEV_TYPE_FBA) { ++ return "FBA"; ++ } elsif ($type == $DEV_TYPE_SCSI) { ++ return "SCSI"; ++ } ++ return undef; ++} ++ ++ ++# check_for_mirror(index, target_list) ++# Die if there is a mirror target between index and 0. ++sub check_for_mirror($@) ++{ ++ my ($i, @target_list) = @_; ++ ++ for (;$i >= 0; $i--) { ++ my $entry = $target_list[$i]; ++ my ($major, $minor, $target) = @$entry; ++ ++ if ($target->[$TARGET_TYPE] == $TARGET_TYPE_MIRROR) { ++ # IPL records are not mirrored. ++ die("Error: Unsupported setup: Block 0 is not ". ++ "mirrored in device '". ++ get_device_name($major, $minor)."'\n"); ++ } ++ } ++} ++ ++# get_target_base(bottom_major, bottom_minor, start, length, target_list) ++# Return (major, minor) for the top most target in the target list that maps ++# the region on (bottom_major, bottom_minor) defined by start and length at ++# offset 0. ++sub get_target_base($$$$@) ++{ ++ my ($bot_major, $bot_minor, $start, $length, @target_list) = @_; ++ my $entry; ++ my $top_major; ++ my $top_minor; ++ my $i; ++ ++ # Pre-initialize with bottom major-minor ++ $top_major = $bot_major; ++ $top_minor = $bot_minor; ++ # Process all entries starting with the last one ++ for ($i = scalar(@target_list) - 1; $i >= 0; $i--) { ++ my $entry = $target_list[$i]; ++ my ($major, $minor, $target) = @$entry; ++ ++ if (($target->[$TARGET_START] != 0) || ++ (get_target_start($target) != 0) || ++ ($target->[$TARGET_LENGTH] < $length)) { ++ last; ++ } ++ $top_major = $major; ++ $top_minor = $minor; ++ } ++ # Check for mirrorring between base device and fs device. ++ check_for_mirror($i, @target_list); ++ return ($top_major, $top_minor); ++} ++ ++# get_device_name(major, minor) ++# Return the name of the device specified by major and minor. ++sub get_device_name($$) ++{ ++ my ($major, $minor) = @_; ++ my $name; ++ local *HANDLE; ++ ++ $name = "$major:$minor"; ++ open(HANDLE, ") { ++ if (/^\s*(\d+)\s+(\d+)\s+\d+\s+(\S+)\s*$/) { ++ if (($major == $1) && ($minor == $2)) { ++ $name = $3; ++ last; ++ } ++ } ++ } ++ close(HANDLE); ++out: ++ return $name; ++} +-- +1.6.3.3 + diff --git a/0016-s390tools-1.8.2-lsreipl-nss.patch b/0016-s390tools-1.8.2-lsreipl-nss.patch new file mode 100644 index 0000000..e2b3413 --- /dev/null +++ b/0016-s390tools-1.8.2-lsreipl-nss.patch @@ -0,0 +1,376 @@ +From 62fb535a68f1df693869e4361150259b42c6f211 Mon Sep 17 00:00:00 2001 +From: =?utf-8?q?Dan=20Hor=C3=A1k?= +Date: Thu, 10 Dec 2009 18:30:52 +0100 +Subject: [PATCH 16/16] s390tools-1.8.2-lsreipl-nss + +--- + ipl_tools/ccw.c | 42 ++++++++++++++++--------------------- + ipl_tools/chreipl.h | 9 +++++-- + ipl_tools/fcp.c | 13 ++++++----- + ipl_tools/ipl.c | 57 +++++++++++++++++++++++++++++++------------------- + ipl_tools/main.c | 25 +++++++++++++++++----- + ipl_tools/system.c | 3 +- + 6 files changed, 87 insertions(+), 62 deletions(-) + +diff --git a/ipl_tools/ccw.c b/ipl_tools/ccw.c +index 7959831..eef4550 100644 +--- a/ipl_tools/ccw.c ++++ b/ipl_tools/ccw.c +@@ -52,22 +52,19 @@ int isccwdev(const char *devno) + } + + +-int get_ccw_devno_old_sysfs(char *device, char *devno) ++int get_ccw_devno_old_sysfs(char *device) + { + FILE *filp; +- int len, errorpath, rc; ++ int errorpath; + char path1[4096]; + char buf[4096]; +- char *match, *s1, *s2; ++ char *match = NULL, *s1, *s2; + + errorpath = 1; +- rc = 0; + sprintf(path1, "/sys/block/%s/uevent", device); + filp = fopen(path1, "r"); +- if (!filp) { +- rc = -1; +- return rc; +- } ++ if (!filp) ++ return -1; + /* + * the uevent file contains an entry like this: + * PHYSDEVPATH=/devices/css0/0.0.206a/0.0.7e78 +@@ -77,16 +74,16 @@ int get_ccw_devno_old_sysfs(char *device, char *devno) + if (match != NULL) + break; + } ++ fclose(filp); ++ if (!match) ++ return -1; + s1 = strchr(buf, '/'); + s2 = strrchr(buf, '/'); +- len = s2-s1; +- strncpy(devno, s2 + 1, sizeof(devno)); +- devno[len] = '\0'; +- fclose(filp); ++ strncpy(devno, s2 + 1, sizeof(devno) - 1); + return 0; + } + +-int get_ccw_devno_new_sysfs(char *device, char *devno) ++int get_ccw_devno_new_sysfs(char *device) + { + int len, errorpath, rc; + char path2[4096]; +@@ -119,8 +116,7 @@ int get_ccw_devno_new_sysfs(char *device, char *devno) + return rc; + } + } +- strncpy(devno, s2 + 1, sizeof(devno)); +- devno[len] = '\0'; ++ strncpy(devno, s2 + 1, sizeof(devno) - 1); + return 0; + } + +@@ -134,16 +130,14 @@ int get_ccw_devno_new_sysfs(char *device, char *devno) + * + * This does not work when booting from tape + */ +-int get_ccw_devno(char *device, char *devno) ++int get_ccw_devno(char *device) + { +- if (get_ccw_devno_old_sysfs(device, devno) != 0) { +- if (get_ccw_devno_new_sysfs(device, devno) != 0) { +- fprintf(stderr, "%s: Failed to lookup the device number\n", +- name); +- return -1; +- } +- } +- return 0; ++ if (get_ccw_devno_old_sysfs(device) == 0) ++ return 0; ++ if (get_ccw_devno_new_sysfs(device) == 0) ++ return 0; ++ fprintf(stderr, "%s: Failed to lookup the device number\n", name); ++ return -1; + } + + int get_ccw_dev(char *partition, char *device) +diff --git a/ipl_tools/chreipl.h b/ipl_tools/chreipl.h +index 19a83eb..37807a7 100644 +--- a/ipl_tools/chreipl.h ++++ b/ipl_tools/chreipl.h +@@ -34,6 +34,9 @@ extern char saction[8]; + extern char name[256]; + extern int action; + ++#define IPL_TYPE_LEN_MAX 100 ++#define NSS_NAME_LEN_MAX 8 ++ + #define ACT_CCW 1 + #define ACT_FCP 2 + #define ACT_NODE 3 +@@ -63,10 +66,10 @@ int is_valid_case(char *c); + int is_valid_action(char *action); + void parse_shutdown_options(int argc, char **argv); + void strlow(char *s); +-int get_ccw_devno(char *device, char *devno); +-int get_reipl_type(void); ++int get_ccw_devno(char *device); ++int get_reipl_type(char *reipltype); + void parse_lsreipl_options(int argc, char **argv); +-int get_ipl_type(); ++int get_ipl_type(char *reipltype); + int get_ipl_loadparam(void); + void print_ipl_settings(void); + int get_sa(char *action, char *file); +diff --git a/ipl_tools/fcp.c b/ipl_tools/fcp.c +index 86fa95b..7a8b4c5 100644 +--- a/ipl_tools/fcp.c ++++ b/ipl_tools/fcp.c +@@ -33,12 +33,11 @@ + + /* + * return the current reipl type from /sys/firmware/reipl/reipl_type +- * 0 = fcp, 1 = ccw, -1, error ++ * 0 = fcp, 1 = ccw, 2 = nss, -1 = unknown + */ +-int get_reipl_type(void) ++int get_reipl_type(char *reipltype) + { + FILE *filp; +- char reipltype[4]; + char path[4096]; + int rc; + +@@ -49,7 +48,7 @@ int get_reipl_type(void) + fprintf(stderr, "%s: Can not open /sys/firmware/" + "reipl/reipl_type: ", name); + fprintf(stderr, "%s\n", strerror(errno)); +- return -1; ++ exit(1); + } + rc = fscanf(filp, "%s", reipltype); + fclose(filp); +@@ -57,17 +56,19 @@ int get_reipl_type(void) + fprintf(stderr, "%s: Failed to read " + "/sys/firmware/reipl/reipl_type:", name); + fprintf(stderr, "%s\n", strerror(errno)); +- return -1; ++ exit(1); + } + if (strncmp(reipltype, "fcp", strlen("fcp")) == 0) + return T_FCP; + else if (strncmp(reipltype, "ccw", strlen("ccw")) == 0) + return T_CCW; +- /* TODO: add NSS support */ ++ else if (strncmp(reipltype, "nss", strlen("nss")) == 0) ++ return T_NSS; + } else { + fprintf(stderr, "%s: Can not open /sys/firmware/reipl/" + "reipl_type:", name); + fprintf(stderr, " %s\n", strerror(errno)); ++ exit(1); + } + return -1; + } +diff --git a/ipl_tools/ipl.c b/ipl_tools/ipl.c +index 2bf817a..8cca700 100644 +--- a/ipl_tools/ipl.c ++++ b/ipl_tools/ipl.c +@@ -33,12 +33,11 @@ + + /* + * return the ipl type based on /sys/firmware/ipl/ipl_type +- * returns 0 in case of fcp and 1 in case of ccw, -1 otherwise ++ * returns 0 in case of fcp and 1 in case of ccw, 2 for nss and -1 otherwise + */ +-int get_ipl_type() ++int get_ipl_type(char *reipltype) + { + FILE *filp; +- char reipltype[4]; + char path[4096]; + int rc; + +@@ -49,7 +48,7 @@ int get_ipl_type() + fprintf(stderr, "%s: Can not open /sys/firmware/ipl/" + "ipl_type: ", name); + fprintf(stderr, "%s\n", strerror(errno)); +- return -1; ++ exit(1); + } + rc = fscanf(filp, "%s", reipltype); + fclose(filp); +@@ -57,12 +56,14 @@ int get_ipl_type() + fprintf(stderr, "%s: Failed to read " + "/sys/firmware/ipl/ipl_type: ", name); + fprintf(stderr, "%s\n", strerror(errno)); +- return -1; ++ exit(1); + } + if (strncmp(reipltype, "fcp", strlen("fcp")) == 0) + return T_FCP; + else if (strncmp(reipltype, "ccw", strlen("ccw")) == 0) + return T_CCW; ++ else if (strncmp(reipltype, "nss", strlen("nss")) == 0) ++ return T_NSS; + } else { + fprintf(stderr, "%s: Can not open /sys/firmware/ipl/" + "ipl_type:", name); +@@ -111,30 +112,38 @@ int get_ipl_loadparam(void) + void print_ipl_settings(void) + { + int rc, type; +- char bootprog[1024], lba[1024]; ++ char bootprog[1024], lba[1024], nss_name[NSS_NAME_LEN_MAX + 1]; ++ char reipltype[IPL_TYPE_LEN_MAX + 1]; + +- type = get_ipl_type(); +- /* +- * TODO: add nss support +- */ +- if (type == 1) ++ type = get_ipl_type(reipltype); ++ switch (type) { ++ case T_NSS: ++ printf("IPL type: nss\n"); ++ rc = strrd(nss_name, "/sys/firmware/ipl/name"); ++ if (rc != 0) ++ exit(1); ++ printf("Name: %s\n", nss_name); ++ break; ++ case T_CCW: + printf("IPL type: ccw\n"); +- if (type == 0) +- printf("IPL type: fcp\n"); +- rc = strrd(devno, "/sys/firmware/ipl/device"); +- if (rc != 0) +- exit(1) /* the error msg comes from get_ipl_device */; +- if (strlen(devno) > 0) +- printf("Device: %s\n", devno); +- if (type == 1) { ++ rc = strrd(devno, "/sys/firmware/ipl/device"); ++ if (rc != 0) ++ exit(1); ++ if (strlen(devno) > 0) ++ printf("Device: %s\n", devno); + rc = get_ipl_loadparam(); + if (rc != -1) + printf("Loadparm: %d\n", rc); + else + printf("Loadparm: \n"); +- } +- if (type == 0) { +- /* these settings are only available for fcp */ ++ break; ++ case T_FCP: ++ printf("IPL type: fcp\n"); ++ rc = strrd(devno, "/sys/firmware/ipl/device"); ++ if (rc != 0) ++ exit(1); ++ if (strlen(devno) > 0) ++ printf("Device: %s\n", devno); + rc = strrd(wwpn, "/sys/firmware/reipl/fcp/wwpn"); + if (rc != -1 && strlen(wwpn) > 0) + printf("WWPN: %s\n", wwpn); +@@ -147,6 +156,10 @@ void print_ipl_settings(void) + rc = strrd(lba, "/sys/firmware/ipl/br_lba"); + if (rc != -1 && strlen(lba) > 0) + printf("br_lba: %s\n", lba); ++ break; ++ default: ++ printf("IPL type: %s (unknown)\n", reipltype); ++ break; + } + exit(0); + } +diff --git a/ipl_tools/main.c b/ipl_tools/main.c +index 2eaa043..bcebabe 100644 +--- a/ipl_tools/main.c ++++ b/ipl_tools/main.c +@@ -57,13 +57,22 @@ int action; /* either CCW, FCP or NODE */ + int lsreipl(int argc, char *argv[]) + { + int rc; +- char bootprog[1024], lba[1024], val[9]; ++ char bootprog[1024], lba[1024], val[9], reipltype[IPL_TYPE_LEN_MAX + 1]; ++ char nss_name[NSS_NAME_LEN_MAX + 1]; + + /* parse the command line options in getop.c */ + parse_lsreipl_options(argc, argv); + +- rc = get_reipl_type(); +- if (rc == 0) { ++ rc = get_reipl_type(reipltype); ++ switch (rc) { ++ case T_NSS: ++ printf("Re-IPL type: nss\n"); ++ rc = strrd(nss_name, "/sys/firmware/reipl/nss/name"); ++ if (rc != 0) ++ exit(1); ++ printf("Name: %s\n", nss_name); ++ break; ++ case T_FCP: + printf("Re-IPL type: fcp\n"); + rc = strrd(wwpn, "/sys/firmware/reipl/fcp/wwpn"); + if (rc != 0) +@@ -90,8 +99,8 @@ int lsreipl(int argc, char *argv[]) + printf("bootprog: %s\n", bootprog); + if (strlen(lba) > 0) + printf("br_lba: %s\n", lba); +- } +- if (rc == 1) { ++ break; ++ case T_CCW: + printf("Re-IPL type: ccw\n"); + rc = strrd(devno, "/sys/firmware/reipl/ccw/device"); + if (rc != 0) +@@ -106,6 +115,10 @@ int lsreipl(int argc, char *argv[]) + printf("Loadparm: %s\n", val); + else + printf("Loadparm: \n"); ++ break; ++ default: ++ printf("Re-IPL type: %s (unknown)\n", reipltype); ++ break; + } + return 0; + } +@@ -134,7 +147,7 @@ int reipl(int argc, char *argv[]) + "partition: %s\n", name, partition); + exit(1); + } +- rc = get_ccw_devno(device, devno); ++ rc = get_ccw_devno(device); + if (rc != 0) { + fprintf(stderr, "%s: Unable to lookup device" + " number for device %s\n", name, +diff --git a/ipl_tools/system.c b/ipl_tools/system.c +index fd5b76b..ca6c5af 100644 +--- a/ipl_tools/system.c ++++ b/ipl_tools/system.c +@@ -174,7 +174,7 @@ int strrd(char *string, char *file) + fprintf(stderr, "%s\n", strerror(errno)); + exit(1); + } +- rc = fread(string, 4096, 1, filp); ++ rc = fread(string, 1, 4096, filp); + fclose(filp); + /* + * special handling is required for +@@ -193,6 +193,7 @@ int strrd(char *string, char *file) + fprintf(stderr, "%s\n", strerror(errno)); + return -1; + } else { ++ string[rc] = 0; + if (string[strlen(string) - 1] == '\n') + string[strlen(string) - 1] = 0; + return 0; +-- +1.6.3.3 + diff --git a/0017-qualified-return-codes-and-further-error-handling-in.patch b/0017-qualified-return-codes-and-further-error-handling-in.patch new file mode 100644 index 0000000..bc325c0 --- /dev/null +++ b/0017-qualified-return-codes-and-further-error-handling-in.patch @@ -0,0 +1,1215 @@ +From 4725dd1a67185ab4296674fcd3b2cf17f2b1cf21 Mon Sep 17 00:00:00 2001 +From: =?utf-8?q?Dan=20Hor=C3=A1k?= +Date: Thu, 17 Dec 2009 17:08:22 +0100 +Subject: [PATCH] qualified return codes and further error handling in znetconf + +Description: znetconf: qualified return codes and further error handling +Symptom: always returned 1 in case errors +Problem: wrapper tools not able to generate qualified error msgs +Solution: return codes +--- + zconf/znetconf | 324 ++++++++++++++++++++++++++++++++---------------------- + zconf/znetconf.8 | 219 +++++++++++++++++++++++++++++++------ + 2 files changed, 375 insertions(+), 168 deletions(-) + +diff --git a/zconf/znetconf b/zconf/znetconf +index 4edf6fb..873124a 100755 +--- a/zconf/znetconf ++++ b/zconf/znetconf +@@ -2,14 +2,58 @@ + # + # znetconf - list and configure network devices + # +-# Tool for the automatic and semi-automatic configuration of network devices ++# Tool for the automatic and semi-automatic configuration of network devices + # from ccw devices, including network device option handling and network + # device removal. + # +-# Copyright IBM Corp. 2009 ++# Copyright IBM Corp. 2009 + # Author(s): Einar Lueck + # Steffen Maier + # ++# 9 Could not group devices ++readonly RC_COULD_NOT_GROUP_DEVICES=9 ++# 10 Could not set device online ++readonly RC_COULD_NOT_SET_DEV_ONLINE=10 ++# 11 Could not set device offline ++readonly RC_COULD_NOT_SET_DEV_OFFLINE=11 ++# 12 Invalid attribute value pair ++readonly RC_INVALID_ATTRIBUTE_VALUE_PAIR=12 ++# 13 Missing component (broken installation) ++readonly RC_BROKEN_INSTALLATION=13 ++# 14 Missing device ID ++readonly RC_MISSING_DEVICE_ID=14 ++# 15 Invalid device ID format ++readonly RC_INVALID_DEVICE_ID_FORMAT=15 ++# 16 Driver name expected ++readonly RC_DRIVER_NAME_EXPECTED=16 ++# 17 Unknown driver ++readonly RC_UNKNOWN_DRIVER=17 ++# 18 Missing attribute value pair for -o|--option ++readonly RC_MISSING_ATTRIBUTE_VALUE_PAIR=18 ++# 19 Invalid argument ++readonly RC_INVALID_ARGUMENT=19 ++# 20 Too much arguments ++readonly RC_TOO_MUCH_ARGUMENTS=20 ++# 21 No configuration found for device ID ++readonly RC_NO_CONFIG_FOUND_FOR_DEV=21 ++# 22 Device is not configured ++readonly RC_DEVICE_NOT_CONFIGURED=22 ++# 23 Could not ungroup device ++readonly RC_COULD_NOT_UNGROUP_DEVICE=23 ++# 24 At least one option could not be configured ++readonly RC_OPTION_NOT_CONFIGURED=24 ++# 25 Missing value for attribute ++readonly RC_MISSING_VALUE=25 ++# 26 Device does not exist ++readonly RC_DEVICE_DOES_NOT_EXIST=26 ++# 27 Device already in use ++readonly RC_DEVICE_ALREADY_IN_USE=27 ++# 28 Net device did not come online ++readonly RC_NET_DEVICE_NOT_ONLINE=28 ++# 29 Some devices could not be added or failed ++readonly RC_SOME_DEVICES_FAILED=29 ++# 99 internal error, this should never happen ++readonly RC_INTERNAL_ERROR=99 + + #============================================================================== + # constants +@@ -29,6 +73,7 @@ else + fi + SYSFSDIR=$(cat /proc/mounts|awk '$3=="sysfs"{print $2; exit}') + CCWGROUPBUS_DIR=$SYSFSDIR/bus/ccwgroup ++CCWDEV_DIR=$SYSFSDIR/bus/ccw/devices + CCWGROUPBUS_DEVICEDIR=$CCWGROUPBUS_DIR/devices + CCWGROUPBUS_DRIVERDIR=$CCWGROUPBUS_DIR/drivers + DEVNOSEP="," +@@ -290,15 +335,14 @@ function lookup_layer() + #============================================================================== + + # +-# group_device ++# group_device + # $1: ccwdevid[;ccwdevid][;ccwdevid] + # $2: ccwgroupdevid + # $3: driver +-# returns ++# returns + # 0 success +-# 1 group file not found +-# 2 grouping failed +-# ++# RC_COULD_NOT_GROUP_DEVICES ++# + function group_device() + { + local DEVICES=$1 +@@ -307,13 +351,13 @@ function group_device() + local GROUPFILE=$CCWGROUPBUS_DRIVERDIR/$DRIVER/group + local CTCMGROUPFILE=$CCWGROUPBUS_DRIVERDIR/ctcm/group + local CTCGROUPFILE=$CCWGROUPBUS_DRIVERDIR/ctc/group +- ++ + # check if group file exists +- if [ ! -e $GROUPFILE ] ++ if [ ! -e $GROUPFILE ] + then + # try to load driver + if ! modprobe $DRIVER &> /dev/null +- then ++ then + if [ $DRIVER = "ctc" ] + then + # check if ctcm driver exists +@@ -329,7 +373,7 @@ function group_device() + GROUPFILE=$CTCGROUPFILE + fi + fi +- fi ++ fi + fi + if [ -e $GROUPFILE ] + then +@@ -340,13 +384,13 @@ function group_device() + *) + print_error "Could not group devices" \ + "$DEVICES" +- return 2 ++ return $RC_COULD_NOT_GROUP_DEVICES + ;; + esac + else + print_error "$GROUPFILE does not exist" +- +- return 1 ++ ++ return $RC_COULD_NOT_GROUP_DEVICES + fi + return 0 + } +@@ -354,14 +398,13 @@ function group_device() + #============================================================================== + + # +-# ungroup_device ++# ungroup_device + # $1: ccwgroupdevid + # $2: network device name (just for display purposes) +-# returns ++# returns + # 0 success +-# 1 group file not found +-# 2 ungrouping failed +-# ++# RC_COULD_NOT_UNGROUP_DEVICE ++# + function ungroup_device() + { + local TARGET_DEVID="$1" +@@ -378,12 +421,12 @@ function ungroup_device() + ;; + *) + print_error "Failed to ungroup $TARGET_DEVID" +- return 2 ++ return $RC_COULD_NOT_UNGROUP_DEVICE + ;; + esac + else + print_error "$DEVICE_UNGROUPFILE does not exist." +- return 1 ++ return $RC_COULD_NOT_UNGROUP_DEVICE + fi + return 0 + } +@@ -393,12 +436,12 @@ function ungroup_device() + # + # try_read_netdevname + # $1: ccwgroupdevno +-# returns ++# returns + # 0 success + # 1 failed. giving up after retries + # stdout + # in case of success: device name +-# ++# + function try_read_netdevname() + { + local CCWGROUPDEVNO="$1" +@@ -406,7 +449,7 @@ function try_read_netdevname() + local NET_SUBDIR="$CCWGROUPBUS_DEVICEDIR/$CCWGROUPDEVNO/net/" + local IF_NAME="" + local rc=1 +- ++ + # check if interface file containing the name exists + if [ -e "$IF_NAME_FILE" ] + then +@@ -417,7 +460,7 @@ function try_read_netdevname() + IF_NAME=$(ls $NET_SUBDIR) + else + # if the file does not exist +- local LINKNAME=$(find $CCWGROUPBUS_DEVICEDIR/$CCWGROUPDEVNO/ -type l -name net*) ++ local LINKNAME=$(find $CCWGROUPBUS_DEVICEDIR/$CCWGROUPDEVNO/ -type l -name net*) + if [[ ! -z $LINKNAME ]] + then + IF_NAME=$(readlink $LINKNAME) +@@ -434,10 +477,10 @@ function try_read_netdevname() + # + # wait_for_net_device + # $1: ccwgroupdevno +-# returns ++# returns + # 0 success + # 1 failed. giving up after retries +-# ++# + function wait_for_net_device() + { + local CCWGROUPDEVNO="$1" +@@ -447,7 +490,7 @@ function wait_for_net_device() + local IF_NAME="" + local CMD_FINDNETLINK="find $CCWGROUPBUS_DEVICEDIR/$CCWGROUPDEVNO/ -type l -name net*" + local LINKNAME="" +- ++ + # polling loop to wait for net device to become available + if [ -e $UDEVSETTLE ] + then +@@ -460,7 +503,7 @@ function wait_for_net_device() + fi + while [[ -z "$IF_NAME" ]] && [[ retries -lt MAX_RETRIES ]] + do +- sleep 1 ++ sleep 1 + retries=$retries+1 + IF_NAME=$(try_read_netdevname $CCWGROUPDEVNO) + done +@@ -468,8 +511,8 @@ function wait_for_net_device() + then + echo "Successfully configured device $CCWGROUPDEVNO ($IF_NAME)" + else +- print_error "Failed to make $CCWGROUPDEVNO online." +- return 1 ++ print_error "Failed to make $CCWGROUPDEVNO online." ++ return 1 + fi + } + +@@ -479,11 +522,11 @@ function wait_for_net_device() + # switch_device + # $1: ccwgroupdevno + # $2: 1|0 (online|offline) +-# returns ++# returns + # 0 success + # 1 command to make device online/offline returned error + # 2 online file does not exist +-# ++# + function switch_device() + { + local CCWGROUPDEVNO="$1" +@@ -491,7 +534,7 @@ function switch_device() + local DEVICE_DIRECTORY="$CCWGROUPBUS_DEVICEDIR/$CCWGROUPDEVNO" + local ONLINE_FILE="$DEVICE_DIRECTORY/online" + local STATESTR="online" +- ++ + if [ $SWITCHVALUE -eq 0 ] + then + STATESTR="offline" +@@ -513,7 +556,7 @@ function switch_device() + print_error "$ONLINE_FILE does not exist." + return 2 + fi +- ++ + return 0 + } + +@@ -524,12 +567,12 @@ function switch_device() + # $1: ccwgroupdevid + # $2: option_name + # $3: option_value +-# returns ++# returns + # 0 success + # 1 option not allowed to be set (e.g. online) + # 2 unknown option + # 3 configuration failed +-# ++# + function configure_ccwgroupdev_option() + { + local CCWGROUPDEVID="$1" +@@ -539,10 +582,10 @@ function configure_ccwgroupdev_option() + # filter some attributes away + if [ "$OPTION_NAME" == "online" ] + then +- print_error "Ignoring option $OPTION_NAME=$OPTION_VALUE" ++ print_error "Ignoring option $OPTION_NAME=$OPTION_VALUE" + return 1 + fi +- ++ + # check if attribute exists + local ATTRFILE="$CCWGROUPBUS_DEVICEDIR/$CCWGROUPDEVID/$OPTION_NAME" + if [ -f $ATTRFILE ] +@@ -574,13 +617,13 @@ function configure_ccwgroupdev_option() + # $4: opt_count + # $5..$(5+opt_count): opt_name_array + # $5+opt_count..5+2*opt_count: opt_value_array +-# returns ++# returns + # 0 success +-# 1 grouping failed +-# 2 failed to make device online +-# 3 net device did not become available +-# 4 device online but some option could not be set +-# ++# RC_COULD_NOT_GROUP_DEVICES ++# RC_COULD_NOT_SET_DEV_ONLINE ++# RC_NET_DEVICE_NOT_ONLINE ++# RC_OPTION_NOT_CONFIGURED ++# + function add_net_device() + { + local DEVICES="$1" +@@ -591,10 +634,9 @@ function add_net_device() + local OPT_NAME_ARRAY=("${RAW[@]:4:$OPT_COUNT}") + local OPT_VALUE_ARRAY=("${RAW[@]:$[4 + $OPT_COUNT]:$OPT_COUNT}") + +- group_device $DEVICES $CCWGROUPDEVID $DRIVER +- if [ $? -ne 0 ] ++ if ! group_device $DEVICES $CCWGROUPDEVID $DRIVER + then +- return 1 ++ return $? + fi + + local i=0 +@@ -602,9 +644,8 @@ function add_net_device() + local HAS_LAYER2_OPTION=0 + while [ $i -lt $OPT_COUNT ] + do +- configure_ccwgroupdev_option $CCWGROUPDEVID \ ++ if ! configure_ccwgroupdev_option $CCWGROUPDEVID \ + ${OPT_NAME_ARRAY[$i]} ${OPT_VALUE_ARRAY[$i]} +- if [ $? -ne 0 ] + then + SOMEOPTION_FAILED=1 + fi +@@ -628,22 +669,20 @@ function add_net_device() + "layer2" "$LAYER2" + fi + fi +- +- switch_device $CCWGROUPDEVNO 1 +- if [ $? -ne 0 ] ++ ++ if ! switch_device $CCWGROUPDEVNO 1 + then +- return 2 ++ return $RC_COULD_NOT_SET_DEV_ONLINE + fi + +- wait_for_net_device $CCWGROUPDEVNO +- if [ $? -ne 0 ] ++ if ! wait_for_net_device $CCWGROUPDEVNO + then +- return 3 ++ return $RC_NET_DEVICE_NOT_ONLINE + fi + + if [ $SOMEOPTION_FAILED -ne 0 ] + then +- return 4 ++ return $RC_OPTION_NOT_CONFIGURED + fi + + return 0 +@@ -655,26 +694,24 @@ function add_net_device() + # remove_net_device + # $1: ccwgroupdevid + # $2: network interface name (just for display purposes) +-# returns ++# returns + # 0 success +-# 1 making device offline failed +-# 2 ungrouping device failed +-# ++# RC_COULD_NOT_SET_DEV_OFFLINE ++# RC_COULD_NOT_UNGROUP_DEVICE ++# + function remove_net_device() + { + local DEVICE_TO_REMOVE="$1" + local DEVNAME="$2" + +- switch_device $DEVICE_TO_REMOVE 0 +- if [ $? -ne 0 ] ++ if ! switch_device $DEVICE_TO_REMOVE 0 + then +- return 1 ++ return $RC_COULD_NOT_SET_DEV_OFFLINE + fi + +- ungroup_device $DEVICE_TO_REMOVE $DEVNAME +- if [ $? -ne 0 ] ++ if ! ungroup_device $DEVICE_TO_REMOVE $DEVNAME + then +- return 2 ++ return $RC_COULD_NOT_UNGROUP_DEVICE + fi + return 0 + } +@@ -684,9 +721,9 @@ function remove_net_device() + # + # list_configured + # $1 supress_header +-# returns ++# returns + # 0 success +-# ++# + function list_configured() + { + supress_header=0 +@@ -723,11 +760,11 @@ function list_configured() + + # read all links and parse driver, device name and ccw device + # bus-ids +- ls -l $d/ | grep '^l' | +- { ++ ls -l $d/ | grep '^l' | ++ { + +- while read line +- do ++ while read line ++ do + local LINKNAME=${line// ->*/""} + LINKNAME=${LINKNAME##* } + if [ "$LINKNAME" = "driver" ] +@@ -742,14 +779,14 @@ function list_configured() + then + DEVNAME=${line##* -> */} + fi +- done ++ done + + local CUTYPE="" + if [ -e $d/cdev0/cutype ] + then + read CUTYPE < $d/cdev0/cutype + fi +- ++ + read ONLINE < $d/online + if [ $ONLINE -eq 1 ] + then +@@ -795,15 +832,15 @@ function print_list_unconf_header() + + # + # list_unconfigured +-# returns ++# returns + # 0 success +-# ++# + function list_unconfigured() + { + local PRINTED_HEADLINES=0 + local LIST_FORMATSTR="%-26.26s %-7.7s %-14.14s %5.5s %-4.4s \n" + print_scanning_4_nwdevices +- ++ + $LSZNET_CALL | + { + while read no cutype chp devtype devdrv devname chlist cardtype +@@ -824,29 +861,29 @@ function list_unconfigured() + # + # store_option + # $1: attribute=value +-# returns ++# returns + # 0 success + # 1 attribute starts with a - / or is invalid + # 2 missing value +-# ++# + function store_option() + { + local OPTIONSTRING="$1" + + # ensure that there is no option intepreted as an +- # attribute value pair ++ # attribute value pair + [[ "$OPTIONSTRING" =~ ^- ]] + case $? in + 0) + print_error "$OPTIONSTRING is not a valid attribute" \ +- "value pair" ++ "value pair" + exit 1 + ;; + 1) + # option considered ok + ;; + 2) +- print_error "Internal error" ++ print_error "Internal error" + exit 1 + esac + +@@ -874,17 +911,17 @@ function store_option() + # + # is_complete_ccwdevbusid + # $1: possibly correct ccw device bus id +-# returns ++# returns + # 0 if the given string is a correctly formatted ccw device bus id + # 1 else +-# ++# + function is_complete_ccwdevbusid() + { + local DEV="$1" + [[ "$DEV" =~ $FORMAT_FULLDEVNO ]] +- case $? in ++ case $? in + 0) +- return 0 ++ return 0 + ;; + 1) + return 1 +@@ -902,41 +939,41 @@ function is_complete_ccwdevbusid() + # + # is_partial_ccwdevbusid + # $1: possibly correct partial ccw device bus id +-# returns ++# returns + # 0 if the given string is a correctly formatted partial ccw device + # bus id + # 1 else +-# ++# + function is_partial_ccwdevbusid() + { + local DEV="$1" + [[ "$DEV" =~ $FORMAT_PARTDEVNO ]] + case $? in + 0) +- return 0 ++ return 0 + ;; + 1) +- return 1 ++ return 1 + ;; + 2) + print_error "Internal error" + exit 1 + ;; + esac +-} ++} + + #============================================================================== + + # + # is_ccwdevbusid_list + # $1: possibly correct list of ccw device bus ids +-# returns ++# returns + # 0 if the given string is a correctly formatted list of ccw device + # bus ids + # 1 else +-# ++# + function is_ccwdevbusid_list() +-{ ++{ + local DEVLIST="$1" + [[ "$DEVLIST" =~ $FORMAT_DEVLIST ]] + case $? in +@@ -959,13 +996,13 @@ function is_ccwdevbusid_list() + # + # is_shortccwdevbusid_list + # $1: possibly correct list of short ccw device bus ids +-# returns ++# returns + # 0 if the given string is a correctly formatted list of short ccw device + # bus ids + # 1 else +-# ++# + function is_shortccwdevbusid_list() +-{ ++{ + local DEVLIST="$1" + [[ "$DEVLIST" =~ $FORMAT_SHORTDEVLIST ]] + case $? in +@@ -987,14 +1024,14 @@ function is_shortccwdevbusid_list() + # + # is_supported_driver + # $1: possibly supported driver +-# returns +-# 0 if the given string denotes a supported driver ++# returns ++# 0 if the given string denotes a supported driver + # 1 else +-# ++# + function is_supported_driver() + { + local DRIVER="$1" +- [[ "$DRIVER" =~ "^(qeth|lcs|ctc|ctcm)$" ]] ++ [[ "$DRIVER" =~ "^(qeth|lcs|ctc|ctcm)$" ]] + case $? in + 0) + return 0 +@@ -1081,7 +1118,7 @@ DEVICE_TO_REMOVE="" + DO_REMOVEALL=0 + NONINTERACTIVE=0 + EXCEPT="" +-if [ $# -gt 0 ] ++if [ $# -gt 0 ] + then + while [ $# -gt 0 ] + do +@@ -1092,7 +1129,7 @@ then + ;; + -v|--version) + print_version +- exit 0 ++ exit 0 + ;; + -A|--add-all) + DO_ADDALL=1 +@@ -1103,15 +1140,15 @@ then + if [ $# -lt 1 ] + then + print_error "Device ID expected" +- exit 1 ++ exit $RC_MISSING_DEVICE_ID + fi + +- # get parameter expected to be a +- # ccw device bus id ++ # get parameter expected to be a ++ # ccw device bus id + DEVICE_TO_ADD="$1" + shift + +- # check syntax of ccw device bus id ++ # check syntax of ccw device bus id + if is_complete_ccwdevbusid "$DEVICE_TO_ADD" || + is_ccwdevbusid_list "$DEVICE_TO_ADD" + then +@@ -1127,9 +1164,9 @@ then + DO_ADD=1 + else + print_error "Invalid device ID format" \ +- "$DEVICE_TO_ADD" +- +- exit 1 ++ "$DEVICE_TO_ADD" ++ ++ exit $RC_INVALID_DEVICE_ID_FORMAT + fi + + ;; +@@ -1142,18 +1179,18 @@ then + if [ $# -lt 1 ] + then + print_error "Driver name expected" +- exit 1 ++ exit $RC_DRIVER_NAME_EXPECTED + fi + + # ensure driver is supported + DRIVER="$1" + shift + if [ "$DRIVER" != "" ] && \ +- ! is_supported_driver "$DRIVER" ++ ! is_supported_driver "$DRIVER" + then + # unknown driver + print_error "Unknown driver $DRIVER" +- exit 1 ++ exit $RC_UNKNOWN_DRIVER + fi + ;; + -o|--option) +@@ -1164,7 +1201,7 @@ then + print_error "Missing attrtibute" \ + "value pair for -o|--" \ + "option" +- exit 1 ++ exit $RC_MISSING_ATTRIBUTE_VALUE_PAIR + fi + + # get option string +@@ -1174,26 +1211,26 @@ then + # store option + if ! store_option $OPTIONSTRING + then +- exit $? ++ exit $RC_INVALID_ATTRIBUTE_VALUE_PAIR + fi + ;; + -r|--remove) + DO_REMOVE=1 + shift +- ++ + # ensure there is a further parameter + if [ $# -lt 1 ] + then + print_error "Expecting "\ + "of device to be removed" +- exit 1 ++ exit $RC_MISSING_ATTRIBUTE_VALUE_PAIR + fi + + # get device to be removed + DEVICE_TO_REMOVE="$1" + shift +- +- # validate it is a ++ ++ # validate it is a + # ccw dev bus id (short or long) + if is_partial_ccwdevbusid "$DEVICE_TO_REMOVE" + then +@@ -1203,7 +1240,7 @@ then + then + print_error "Invalid device ID format" \ + "$DEVICE_TO_REMOVE" +- exit 1 ++ exit $RC_INVALID_DEVICE_ID_FORMAT + fi + ;; + -R|--remove-all) +@@ -1219,7 +1256,7 @@ then + then + print_error " missing"\ + "for -e|--except" +- exit 1 ++ exit $RC_MISSING_DEVICE_ID + fi + + # get device to be removed +@@ -1236,7 +1273,7 @@ then + then + print_error "Invalid device ID format" \ + "$EXCEPT_DEVNO" +- exit 1 ++ exit $RC_INVALID_DEVICE_ID_FORMAT + fi + + # create a filter statement +@@ -1258,7 +1295,7 @@ then + *) + echo "$CMD: Invalid option $1" + echo "Try '$CMD --help' for more information." +- exit 1 ++ exit $RC_INVALID_ARGUMENT + ;; + esac + done +@@ -1271,7 +1308,7 @@ if [ $(($DO_ADD + $DO_ADDALL + $DO_LIST_UNCONFIGURED + $DO_LIST_CONFIGURED + \ + then + print_error "Too much arguments" + echo "Try '$CMD --help' for more information." +- exit 1 ++ exit $RC_TOO_MUCH_ARGUMENTS + fi + + # react to parsed options +@@ -1290,12 +1327,31 @@ then + then + print_error "No configuration found for device ID" \ + "$DEVICE_TO_ADD" +- exit 1 ++ exit $RC_NO_CONFIG_FOUND_FOR_DEV + fi + else +- # define set of device numbers to be used ++ # define set of device numbers to be used + DEVICES="$DEVICE_TO_ADD" +- ++ ++ # ensure none of the CCW devices is already in use ++ CCW_DEVS=${DEVICES//,/ } ++ for CCW_DEVNO in $CCW_DEVS ++ do ++ CCW_DEV_ONLINEFILE="$CCWDEV_DIR/$CCW_DEVNO/online" ++ if [ ! -e $CCW_DEV_ONLINEFILE ] ++ then ++ print_error "Device $CCW_DEVNO does not exist" ++ exit $RC_DEVICE_DOES_NOT_EXIST ++ fi ++ ++ read CCW_DEV_ONLINE < $CCW_DEV_ONLINEFILE ++ if [ "$CCW_DEV_ONLINE" == "1" ] ++ then ++ print_error "$CCW_DEVNO is already in use" ++ exit $RC_DEVICE_ALREADY_IN_USE ++ fi ++ done ++ + # try to find a driver for the given set of device numbers + CANDIDATE=$($LSZNET_CALL| + awk "\$7~/${DEVICE_TO_ADD//,// && \$7~/}/ {print \$5}") +@@ -1305,7 +1361,7 @@ then + # compute the expected group device number + CCWGROUPDEVNO=${DEVICES%%,*} + +- # check whether an appropriate driver was determined automatically or ++ # check whether an appropriate driver was determined automatically or + # not, if not, one has to be given by the user via -d + if [ "$DRIVER" == "" ] + then +@@ -1345,7 +1401,7 @@ then + $ATTRIBUTE_COUNT "${ATTRIBUTE_NAME[@]}" \ + "${ATTRIBUTE_VALUE[@]}" + then +- ADDALL_RC=1 ++ ADDALL_RC=$RC_SOME_DEVICES_FAILED + fi + done + exit "$ADDALL_RC" +@@ -1363,9 +1419,9 @@ then + if [[ -z "$CCWGROUPDEVID_TO_REMOVE" ]] + then + print_error "$DEVICE_TO_REMOVE is not a configured device" +- exit 1 ++ exit $RC_DEVICE_NOT_CONFIGURED + fi +- ++ + echo "Remove network device $CCWGROUPDEVID_TO_REMOVE" \ + "($CCWDEVBUSIDS_TO_REMOVE)?" + echo "Warning: this may affect network connectivity!" +@@ -1403,7 +1459,7 @@ then + fi + if [ $DO_LIST_UNCONFIGURED -eq 1 ] + then +- list_unconfigured ++ list_unconfigured + exit 0 + fi + if [ $DO_LIST_CONFIGURED -eq 1 ] +diff --git a/zconf/znetconf.8 b/zconf/znetconf.8 +index 3af8565..a6da42c 100644 +--- a/zconf/znetconf.8 ++++ b/zconf/znetconf.8 +@@ -1,6 +1,6 @@ + .TH ZNETCONF 8 "Mar 2009" "s390-tools" + +-.SH NAME ++.SH NAME + znetconf \- list and configure network devices for System z network adapters + + .SH SYNOPSIS +@@ -8,10 +8,10 @@ znetconf \- list and configure network devices for System z network adapters + .B [-h|--help] [-v|--version] + .br + +-.br +-.B znetconf -u | -c + .br +-.B znetconf -a [,...]{2} [-o =]+ [-d ] ++.B znetconf -u | -c ++.br ++.B znetconf -a [,...]{2} [-o =]+ [-d ] + .br + .B znetconf -A [-o =]+ [-d ] [-e ]+ + .br +@@ -27,12 +27,12 @@ Based on these lists, it automatically adds or removes network devices. + For automatic configuration, znetconf builds a channel command word + (CCW) group device from sensed CCW devices, configures any specified + option through the sensed network device driver and sets the new +-network device online. ++network device online. + .P + During automatic removal, znetconf sets the device offline and removes it. + Be aware that removing all network devices leads to the +-complete loss of network connectivity. So a terminal session (e.g. 3270) +-might be required to restore. ++complete loss of network connectivity. So a terminal session (e.g. 3270) ++might be required to restore. + + .SH OPTIONS + .TP 8 +@@ -43,30 +43,30 @@ Print help text. + .BR -v | --version + Print the version of the s390-tools package and the znetconf command. + +-.TP +-.BR -u | --unconfigured ++.TP ++.BR -u | --unconfigured + List potential network devices that are not yet configured. + For each device, the following data is provided: + .RS + .TP 4 +-* +-Device IDs (device bus bus-IDs) of the CCW devices constituting the network ++* ++Device IDs (device bus bus-IDs) of the CCW devices constituting the network + device + .TP +-* ++* + Type of control unit (e.g. 1731/01) + .TP +-* ++* + Network card type (e.g. OSA (QDIO)) + .TP + * +-Channel path identifier (CHPID) ++Channel path identifier (CHPID) + .TP + * + Device driver (qeth, lcs, ctc, ctcm) + .RE +-.TP +-.BR -c | --configured ++.TP ++.BR -c | --configured + List configured network devices. For each device, the following data is + provided: + .RS +@@ -104,7 +104,7 @@ device_bus_id can be any of the device + IDs listed as part of the potential network device list (argument + .BR -u ")." + For example, if znetconf +-.BR -u ++.BR -u + lists 0.0.f503,0.0.f504,0.0.f505 for a potential network device, device_bus_id + may be 0.0.f503 or 0.0.f504 or 0.0.f505. + If a device bus-ID begins with 0.0., you can abbreviate it to the final 4 +@@ -115,11 +115,11 @@ If attribute value pairs are given with + .BR -o ", " + these pairs are configured for the created network device. The + device is then set online regardless of whether the given attribute value pairs +-were applied successfully. ++were applied successfully. + .br + Finally, the corresponding network interface name (e.g. eth1) is displayed. + .br +-If more then one device_bus_id is given, the given set of devices is configured as a network device. znetconf tries to sense the required device driver ++If more then one device_bus_id is given, the given set of devices is configured as a network device. znetconf tries to sense the required device driver + automatically. If the device driver cannot be sensed, you must specify it with + -d. + .BR -d "." +@@ -145,7 +145,7 @@ znetconf continues with the next remaining potential network device. + + .TP + .BR -r | --remove " [-n | --non-interactive]" +-Remove the network device identified by device_bus_id. device_bus_id is one of ++Remove the network device identified by device_bus_id. device_bus_id is one of + the device IDs of the network device. They are listed as part of znetconf + .BR -c "." + znetconf sets the device offline and removes it. If +@@ -164,21 +164,105 @@ with + .TP + \fB\fR + Specify a device option. The option must match a sysfs attribute for the device +-to be configured. ++to be configured. For a detailed description of the semantics of sysfs ++attributes please refer to the Device Drivers, Features, and Commands book for ++Linux on System z. The attributes are: ++ ++.RS ++.B qeth ++.br ++broadcast_mode ++.br ++buffer_count ++.br ++canonical_macaddr ++.br ++checksumming ++.br ++fake_broadcast ++.br ++ipa_takeover/add4 ++.br ++ipa_takeover/add6 ++.br ++ipa_takeover/del4 ++.br ++ipa_takeover/del6 ++.br ++ipa_takeover/enable ++.br ++ipa_takeover/invert4 ++.br ++ipa_takeover/invert6 ++.br ++isolation ++.br ++large_send ++.br ++layer2 ++.br ++performance_stats ++.br ++portname ++.br ++portno ++.br ++priority_queueing ++.br ++route4 ++.br ++route6 ++.br ++rxip/add4 ++.br ++rxip/add6 ++.br ++rxip/del4 ++.br ++rxip/del6 ++.br ++vipa/add4 ++.br ++vipa/add6 ++.br ++vipa/del4 ++.br ++vipa/del ++.RE ++ ++.RS ++.B ctc(m) ++.br ++buffer ++.br ++loglevel ++.br ++protocol ++.br ++stats ++.RE ++ ++.RS ++.B lcs ++.br ++portno ++.br ++lancmd_timeout ++.RE + + .TP +-\fB\fR ++\fB\fR + Specify the device bus-ID of a CCW device. Device bus-IDs have the form + ([A-Fa-f0-9].[A-Fa-f0-9].)[A-Fa-f0-9]{4}. + +-If a device bus-ID begins with 0.0., you can abbreviate it to the final 4 ++If a device bus-ID begins with 0.0., you can abbreviate it to the final 4 + hexadecimal digits. + + For example, you can abbreviate 0.0.f503 to f503. + + .TP + \fB\fR +-Specify the device driver for the device. Valid values are qeth, lcs, ctc, or ++Specify the device driver for the device. Valid values are qeth, lcs, ctc, or + ctcm. + + .SH EXAMPLES +@@ -242,9 +326,9 @@ Shows the list of potential network devices. Example output: + Device IDs Type Card Type CHPID Drv. + .br + -------------------------------------------------------- +-0.0.f500,0.0.f501,0.0.f502 1731/01 OSA (QDIO) 00 qeth ++0.0.f500,0.0.f501,0.0.f502 1731/01 OSA (QDIO) 00 qeth + .br +-0.0.f503,0.0.f504,0.0.f505 1731/01 OSA (QDIO) 01 qeth ++0.0.f503,0.0.f504,0.0.f505 1731/01 OSA (QDIO) 01 qeth + .RE + .P + \fBznetconf -a 0.0.f503\fR +@@ -258,13 +342,13 @@ lists the new network device. + .P + \fBznetconf -a f503\fR + .RS +-This command is equivalent to \fBznetconf -a 0.0.f503\fR. ++This command is equivalent to \fBznetconf -a 0.0.f503\fR. + .RE + .P + \fBznetconf -a f503 -o layer2=0 -o portname=myname\fR + .RS +-Adds the potential network device +-with 0.0.f503 as one of its device bus-IDs ++Adds the potential network device ++with 0.0.f503 as one of its device bus-IDs + and configures the options layer2 with value 0 and + portname with myname. + .RE +@@ -288,9 +372,9 @@ Device IDs Type Card Type CHPID Drv. Name State + + \fBznetconf -r 0.0.f503\fR + .RS +-Removes the network device with 0.0.f503 as one of its device bus-IDs. ++Removes the network device with 0.0.f503 as one of its device bus-IDs. + You can only remove configured devices as listed by znetconf +-.BR -c "." ++.BR -c "." + After successfully running this command the corresponding device appears in the + list of potential network devices as listed by znetconf + .BR -u "." +@@ -303,9 +387,76 @@ This command is equivalent to \fBznetconf -r 0.0.f503\fR. + .P + + .SH DIAGNOSTICS +-If znetconf runs successfully, the exit status is 0. In case of errors, 1 is +-returned. +- ++If znetconf runs successfully, the exit status is 0. In case of errors, the following codes are returned: ++.TP ++.BR 0 ++success ++.TP ++.BR 9 ++could not group devices ++.TP ++.BR 10 ++could not set device online ++.TP ++.BR 11 ++could not set device offline ++.TP ++.BR 12 ++invalid attribute value pair ++.TP ++.BR 13 ++missing component (broken installation) ++.TP ++.BR 14 ++missing device ID ++.TP ++.BR 15 ++invalid device ID format ++.TP ++.BR 16 ++driver name expected ++.TP ++.BR 17 ++unknown driver ++.TP ++.BR 18 ++missing attribute value pair for -o|--option ++.TP ++.BR 19 ++invalid argument ++.TP ++.BR 20 ++too much arguments ++.TP ++.BR 21 ++no configuration found for device ID ++.TP ++.BR 22 ++device is not configured ++.TP ++.BR 23 ++could not ungroup device ++.TP ++.BR 24 ++at least one option could not be configured ++.TP ++.BR 25 ++missing value for attribute ++.TP ++.BR 26 ++device does not exist ++.TP ++.BR 27 ++device already in use ++.TP ++.BR 28 ++net device did not come online ++.TP ++.BR 29 ++some devices could not be added or failed ++.TP ++.BR 99 ++internal znetconf bug + .SH AUTHOR + .nf + This man-page was written by Einar Lueck . +-- +1.6.3.3 + diff --git a/device_cio_free b/device_cio_free new file mode 100644 index 0000000..599f5ae --- /dev/null +++ b/device_cio_free @@ -0,0 +1,203 @@ +#!/bin/sh +# +# Copyright 2009 Red Hat, Inc. +# License: GPLv2 +# Author: Dan Horák +# +# unblock devices listed in various config files and wait until they are ready +# +# it uses dasd and zfcp config file +# config file syntax: +# deviceno options +# or +# deviceno WWPN FCPLUN +# +# also processes the system ccw config file and network interface configurations +# + +DASDCONFIG=/etc/dasd.conf +ZFCPCONFIG=/etc/zfcp.conf +ZNETCONFIG=/etc/ccw.conf +BLACKLIST=/proc/cio_ignore +VERBOSE= +PATH=/bin:/usr/bin:/sbin:/usr/sbin +ALL_DEVICES= + +usage() +{ + echo "$0 [-h|--help] [-V|--verbose]" + exit 1 +} + +# accepts single device, comma-separated lists and dash separated ranges and their combinations +free_device() +{ + local DEV + + [ -z "$1" ] && return + + DEV=$(echo $1 | tr "A-Z" "a-z") + + [ $VERBOSE ] && echo "Freeing device(s) $DEV" + if [ echo "free $DEV" > $BLACKLIST 2> /dev/null ]; then + echo "Error: can't free device(s) $DEV" + else + if [ -z $ALL_DEVICES ]; then + ALL_DEVICES=$DEV + else + ALL_DEVICES="$ALL_DEVICES,$DEV" + fi + fi +} + +# wait until a device appears on the ccw bus +wait_on_device() +{ + local DEVICE_ONLINE DEV + + [ -z "$1" ] && return + + DEV=$1 + DEVICE_ONLINE=/sys/bus/ccw/devices/$DEV/online + + [ $VERBOSE ] && echo "Waiting on device $DEV" + [ -f "$DEVICE_ONLINE" ] && return + + for t in 1 2 3 4 5 + do + sleep $t + [ -f "$DEVICE_ONLINE" ] && return + done + echo "Error: device $DEV still not ready" +} + +# check how we were called +case $(basename "$0") in + "dasd_cio_free") + CONFIG=$DASDCONFIG + MODE=dasd + ;; + "zfcp_cio_free") + CONFIG=$ZFCPCONFIG + MODE=zfcp + ;; + "znet_cio_free") + CONFIG=$ZNETCONFIG + MODE=znet + ;; + *) + echo "Error: unknown alias '$CMD'." + echo "Supported aliases are dasd_cio_free, zfcp_cio_free and znet_cio_free." + exit 1 + ;; +esac + +# process command line options +if [ $# -gt 0 ]; then + case $1 in + -V|--verbose) + VERBOSE=yes + shift + ;; + -h|--help) + usage + ;; + *) + echo "Error: unknown option $1" + usage + ;; + esac +fi + +if [ ! -f $BLACKLIST ]; then + echo "Error: $BLACKLIST kernel interface doesn't exist" + exit 2 +fi + +if [ $MODE = "dasd" -o $MODE = "zfcp" ]; then + # process the config file + if [ -f "$CONFIG" ]; then + while read line; do + case $line in + \#*) ;; + *) + [ -z "$line" ] && continue + set $line + free_device $1 + ;; + esac + done < $CONFIG + fi +fi + +if [ $MODE = "dasd" ]; then + # process the device list defined as option for the dasd module + DEVICES=$(modprobe --showconfig | grep "options[[:space:]]\+dasd_mod" | \ + sed -e 's/.*[[:space:]]dasd=\([^[:space:]]*\).*/\1/' -e 's/([^)]*)//g' \ + -e 's/nopav\|nofcx\|autodetect\|probeonly//g' -e 's/,,/,/g' -e 's/^,//' -e 's/,$//') + + free_device $DEVICES +fi + +if [ $MODE = "znet" ]; then + # process the config file + if [ -f "$CONFIG" ]; then + while read line; do + case $line in + \#*) ;; + *) + [ -z "$line" ] && continue + # grep 2 or 3 channels from beginning of each line + DEVICES=$(echo $line | egrep -i -o "^([0-9]\.[0-9]\.[a-f0-9]+,){1,2}([0-9]\.[0-9]\.[a-f0-9]+)") + free_device $DEVICES + ;; + esac + done < $CONFIG + fi + # process channels from network interface configurations + for line in $(egrep -i -h "^[[:space:]]*SUBCHANNELS=['\"]?([0-9]\.[0-9]\.[a-f0-9]+,){1,2}([0-9]\.[0-9]\.[a-f0-9]+)['\"]?([[:space:]]+#|[[:space:]]*$)" /etc/sysconfig/network-scripts/ifcfg-* 2> /dev/null) + do + eval "$line" + free_device $SUBCHANNELS + done +fi + +# wait until recently unblocked devices are ready +# at this point we know the content of ALL_DEVICES is syntacticly correct +IFS="," +set $ALL_DEVICES +while [ "$1" ] +do + DEV="$1" + IFS="." + + # get the lower bound for range or get the single device + LOWER=${DEV%%-*} + read -a L <<< "$LOWER" + if [ ${#L[@]} -eq 1 ]; then + L[2]=${L[0]} + L[0]=0 + L[1]=0 + fi + + # get the upper bound for range or get the single device + UPPER=${DEV##*-} + read -a U <<< "$UPPER" + if [ ${#U[@]} -eq 1 ]; then + U[2]=${U[0]} + U[0]=0 + U[1]=0 + fi + + # iterate thru all devices + for (( i=0x${L[0]}; i<=0x${U[0]}; i++ )); do + for (( j=0x${L[1]}; j<=0x${U[1]}; j++ )); do + for (( k=0x${L[2]}; k<=0x${U[2]}; k++ )); do + wait_on_device "$(printf %x.%x.%04x $i $j $k)" + done + done + done + + # go to the next device + shift +done diff --git a/s390utils.spec b/s390utils.spec index 6d27f19..462d7ce 100644 --- a/s390utils.spec +++ b/s390utils.spec @@ -8,7 +8,7 @@ Name: s390utils Summary: Utilities and daemons for IBM System/z Group: System Environment/Base Version: 1.8.2 -Release: 5%{?dist} +Release: 6%{?dist} Epoch: 2 License: GPLv2 and GPLv2+ and CPL Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) @@ -31,6 +31,7 @@ Source11: cpi.sysconfig # files for DASD initialization Source12: dasd.udev Source13: dasdconf.sh +Source14: device_cio_free Patch1: 0001-s390-tools-1.5.3-zipl-zfcpdump-2.patch Patch2: 0002-s390-tools-1.8.1-zipl-automenu.patch @@ -46,6 +47,9 @@ Patch11: 0011-update-readahead-value-for-better-performance.patch Patch12: 0012-fix-multipath-device-detection-in-ziomon.patch Patch13: 0013-zipl-handle-status-during-ipl.patch Patch14: 0014-dasdview-fdasd-fix-floating-point-error-for-unformat.patch +Patch15: 0015-s390tools-1.8.2-zipl-dm.patch +Patch16: 0016-s390tools-1.8.2-lsreipl-nss.patch +Patch17: 0017-qualified-return-codes-and-further-error-handling-in.patch Patch100: cmsfs-1.1.8-warnings.patch Patch101: cmsfs-1.1.8-kernel26.patch @@ -117,6 +121,15 @@ be used together with the zSeries (s390) Linux kernel and device drivers. # Fix floating point error for unformatted devices in fdasd and dasdview (#537144) %patch14 -p1 -b .dasd-zero-division +# Add device-mapper support into zipl (#546280) +%patch15 -p1 -b .zipl-dm + +# Add missing check and print NSS name in case an NSS has been IPLed (#546297) +%patch16 -p1 -b .lsreipl-nss + +# Add qualified return codes and further error handling in znetconf (#548487) +%patch17 -p1 -b .znetconf-returncodes + # # cmsfs # @@ -190,6 +203,9 @@ rm -rf ${RPM_BUILD_ROOT} mkdir -p $RPM_BUILD_ROOT{%{_lib},%{_libdir},/sbin,/bin,/boot,%{_mandir}/man1,%{_mandir}/man8,%{_sbindir},%{_bindir},%{_sysconfdir}/{profile.d,udev/rules.d,sysconfig},%{_initddir}} +# workaround an issue in the zipl-device-mapper patch +rm -f zipl/src/zipl_helper.device-mapper.* + make install \ INSTROOT=$RPM_BUILD_ROOT \ MANDIR=$RPM_BUILD_ROOT%{_mandir} \ @@ -252,6 +268,14 @@ install -p -m 644 include/vtoc.h $RPM_BUILD_ROOT%{_includedir}/%{name} install -p -m 644 %{SOURCE11} ${RPM_BUILD_ROOT}%{_sysconfdir}/sysconfig/cpi install -p -m 755 %{SOURCE10} ${RPM_BUILD_ROOT}%{_initddir}/cpi +# device_cio_free +install -p -m 755 %{SOURCE14} ${RPM_BUILD_ROOT}/sbin +pushd ${RPM_BUILD_ROOT}/sbin +for lnk in dasd zfcp znet; do + ln -sf device_cio_free ${lnk}_cio_free +done +popd + %clean rm -rf ${RPM_BUILD_ROOT} @@ -422,9 +446,11 @@ fi %doc README %doc LICENSE /sbin/zipl +/sbin/dasd_cio_free /sbin/dasdfmt /sbin/dasdinfo /sbin/dasdview +/sbin/device_cio_free /sbin/fdasd /sbin/chccwdev /sbin/chchp @@ -446,7 +472,9 @@ fi /sbin/tape390_crypt /sbin/tunedasd /sbin/vmcp +/sbin/zfcp_cio_free /sbin/zgetdump +/sbin/znet_cio_free /sbin/znetconf /sbin/dbginfo.sh %{_sbindir}/lsreipl @@ -798,6 +826,14 @@ User-space development files for the s390/s390x architecture. %changelog +* Tue Dec 22 2009 Dan Horák 2:1.8.2-6 +- fixed return value in cpi initscript (#541389) +- updated zfcpconf.sh script from dracut +- added device-mapper support into zipl (#546280) +- added missing check and print NSS name in case an NSS has been IPLed (#546297) +- added device_cio_free script and its symlinks +- added qualified return codes and further error handling in znetconf (#548487) + * Fri Nov 13 2009 Dan Horák 2:1.8.2-5 - added multiple fixes from IBM (#533955, #537142, #537144)