diff --git a/.gitignore b/.gitignore index 421d790..7b99947 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ cmsfs-1.1.8c.tar.gz lib-zfcp-hbaapi-2.0.tar.gz src_vipa-2.0.4.tar.gz /lib-zfcp-hbaapi-2.1.tar.gz +/s390-tools-1.14.0.tar.bz2 diff --git a/0001-s390-tools-1.5.3-zipl-zfcpdump-2.patch b/0001-s390-tools-1.5.3-zipl-zfcpdump-2.patch deleted file mode 100644 index c9d8a75..0000000 --- a/0001-s390-tools-1.5.3-zipl-zfcpdump-2.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 2b8336a93376953ba0ed84223264be55243ac7f8 Mon Sep 17 00:00:00 2001 -From: Dan Horak -Date: Sun, 20 Jul 2008 09:24:05 +0200 -Subject: [PATCH 1/9] s390-tools-1.5.3-zipl-zfcpdump-2 - ---- - common.mak | 4 ++-- - 1 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/common.mak b/common.mak -index 8b248f1..0a7916e 100644 ---- a/common.mak -+++ b/common.mak -@@ -63,8 +63,8 @@ GROUP = $(shell id -gn) - export INSTROOT BINDIR LIBDIR MANDIR OWNER GROUP - - # Special defines for zfcpdump --ZFCPDUMP_DIR = /usr/local/share/zfcpdump --ZFCPDUMP_IMAGE = zfcpdump.image -+ZFCPDUMP_DIR = /boot -+ZFCPDUMP_IMAGE = zfcpdump - ZFCPDUMP_RD = zfcpdump.rd - export ZFCPDUMP_DIR ZFCPDUMP_IMAGE ZFCPDUMP_RD - --- -1.6.3.3 - diff --git a/0002-s390-tools-1.8.1-zipl-automenu.patch b/0002-s390-tools-1.8.1-zipl-automenu.patch deleted file mode 100644 index 28ff804..0000000 --- a/0002-s390-tools-1.8.1-zipl-automenu.patch +++ /dev/null @@ -1,423 +0,0 @@ -From 2513c0f8096c681f962d77420dac731f7bb33e44 Mon Sep 17 00:00:00 2001 -From: =?utf-8?q?Dan=20Hor=C3=A1k?= -Date: Thu, 23 Apr 2009 11:45:36 +0200 -Subject: [PATCH 2/9] s390-tools-1.8.1-zipl-automenu - ---- - zipl/man/zipl.8 | 7 ++ - zipl/src/job.c | 194 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- - zipl/src/scan.c | 4 +- - zipl/src/zipl.c | 1 + - 4 files changed, 197 insertions(+), 9 deletions(-) - -diff --git a/zipl/man/zipl.8 b/zipl/man/zipl.8 -index 8a83c01..6ebf240 100644 ---- a/zipl/man/zipl.8 -+++ b/zipl/man/zipl.8 -@@ -249,6 +249,13 @@ whether they contain a dump signature or not. - This option can only be used together with - .BR \-\-mvdump . - -+.TP -+.BR "\-x" " or " "\-\-no-automenu" -+Disables the automatic creation of a multiboot menu. Specifying a menu with the -+"-m " option or a section disables this feature, too. This option was -+added for compatibility with previous versions of the multiboot version of -+zipl. -+ - .SH EXAMPLE - 1. Scenario: prepare disk for booting a Linux kernel image using the - following parameters: -diff --git a/zipl/src/job.c b/zipl/src/job.c -index c5c85d8..6a526e4 100644 ---- a/zipl/src/job.c -+++ b/zipl/src/job.c -@@ -43,6 +43,7 @@ static struct option options[] = { - { "version", no_argument, NULL, 'v'}, - { "verbose", no_argument, NULL, 'V'}, - { "add-files", no_argument, NULL, 'a'}, -+ { "no-automenu", no_argument, NULL, 'x'}, - { "tape", required_argument, NULL, 'T'}, - { "dry-run", no_argument, NULL, '0'}, - { "force", no_argument, NULL, 'f'}, -@@ -50,7 +51,7 @@ static struct option options[] = { - }; - - /* Command line option abbreviations */ --static const char option_string[] = "-c:t:i:r:p:P:d:D:M:s:m:hHnVvaT:f"; -+static const char option_string[] = "-c:t:i:r:p:P:d:D:M:s:m:hHnVvaxT:f"; - - struct command_line { - char* data[SCAN_KEYWORD_NUM]; -@@ -62,11 +63,14 @@ struct command_line { - int version; - int verbose; - int add_files; -+ int automenu; - int dry_run; - int force; - enum scan_section_type type; - }; - -+/* Global variable for default boot target. Ugly but necessary... */ -+static char *default_target; - - static int - store_option(struct command_line* cmdline, enum scan_keyword_id keyword, -@@ -92,6 +96,7 @@ get_command_line(int argc, char* argv[], struct command_line* line) - int i; - - memset((void *) &cmdline, 0, sizeof(struct command_line)); -+ cmdline.automenu = 1; - cmdline.type = section_invalid; - is_keyword = 0; - /* Process options */ -@@ -101,16 +106,22 @@ get_command_line(int argc, char* argv[], struct command_line* line) - switch (opt) { - case 'd': - is_keyword = 1; -+ cmdline.automenu = 0; -+ scan_key_table[1][8] = req; - rc = store_option(&cmdline, scan_keyword_dumpto, - optarg); - break; - case 'D': - is_keyword = 1; -+ cmdline.automenu = 0; -+ scan_key_table[1][8] = req; - rc = store_option(&cmdline, scan_keyword_dumptofs, - optarg); - break; - case 'M': - is_keyword = 1; -+ cmdline.automenu = 0; -+ scan_key_table[1][8] = req; - rc = store_option(&cmdline, scan_keyword_mvdump, - optarg); - #ifndef __s390x__ -@@ -121,35 +132,49 @@ get_command_line(int argc, char* argv[], struct command_line* line) - break; - case 'i': - is_keyword = 1; -+ cmdline.automenu = 0; -+ scan_key_table[1][8] = req; - rc = store_option(&cmdline, scan_keyword_image, - optarg); - break; - case 'P': -+ cmdline.automenu = 0; -+ scan_key_table[1][8] = req; - rc = store_option(&cmdline, scan_keyword_parameters, - optarg); - break; - case 'p': - is_keyword = 1; -+ cmdline.automenu = 0; -+ scan_key_table[1][8] = req; - rc = store_option(&cmdline, scan_keyword_parmfile, - optarg); - break; - case 'r': - is_keyword = 1; -+ cmdline.automenu = 0; -+ scan_key_table[1][8] = req; - rc = store_option(&cmdline, scan_keyword_ramdisk, - optarg); - break; - case 's': - is_keyword = 1; -+ cmdline.automenu = 0; -+ scan_key_table[1][8] = req; - rc = store_option(&cmdline, scan_keyword_segment, - optarg); - break; - case 't': - is_keyword = 1; -+ cmdline.automenu = 0; -+ scan_key_table[1][8] = req; - rc = store_option(&cmdline, scan_keyword_target, - optarg); - break; - case 'T': - is_keyword = 1; -+ cmdline.automenu = 0; -+ scan_key_table[1][8] = req; - rc = store_option(&cmdline, scan_keyword_tape, - optarg); - break; -@@ -190,6 +215,10 @@ get_command_line(int argc, char* argv[], struct command_line* line) - case 'f': - cmdline.force = 1; - break; -+ case 'x': -+ cmdline.automenu = 0; -+ scan_key_table[1][8] = req; -+ break; - case 1: - /* Non-option is interpreted as section name */ - if (cmdline.section != NULL) { -@@ -214,6 +243,9 @@ get_command_line(int argc, char* argv[], struct command_line* line) - if (cmdline.help || cmdline.version) { - /* Always accept --help and --version */ - } else if ((cmdline.menu != NULL) || (cmdline.section != NULL)) { -+ /* If either menu or section has been selected disable -+ automenu generation */ -+ cmdline.automenu = 0; - /* Config file mode */ - if ((cmdline.menu != NULL) && (cmdline.section != NULL)) { - error_reason("Option 'menu' cannot be used when " -@@ -832,7 +864,14 @@ get_job_from_section_data(char* data[], struct job_data* job, char* section) - /* IPL job */ - job->id = job_ipl; - /* Fill in name of bootmap directory */ -- job->bootmap_dir = misc_strdup(data[(int) scan_keyword_target]); -+ if (data[(int) scan_keyword_target] == NULL) { -+ if (default_target == NULL) { -+ error_text("Unable to find default section in your config file."); -+ break; -+ } -+ job->bootmap_dir = misc_strdup(default_target); -+ } else -+ job->bootmap_dir = misc_strdup(data[(int) scan_keyword_target]); - if (job->bootmap_dir == NULL) - return -1; - /* Fill in name and address of image file */ -@@ -1102,6 +1141,8 @@ get_menu_job(struct scan_token* scan, char* menu, struct job_data* job) - if (temp_job == NULL) - 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); - rc = get_job_from_section_data(data, temp_job, - job->data.menu.entry[current].name); - if (rc) { -@@ -1150,6 +1191,7 @@ get_default_section(struct scan_token* scan, char** section, int* is_menu) - i = scan_find_section(scan, DEFAULTBOOT_SECTION, - scan_id_section_heading, 0); - if (i<0) { -+ *section = NULL; - error_reason("No '" DEFAULTBOOT_SECTION "' section found and " - "no section specified on command line"); - return -1; -@@ -1169,6 +1211,7 @@ get_default_section(struct scan_token* scan, char** section, int* is_menu) - } - } - /* Should not happen */ -+ *section = NULL; - error_reason("No default section specified"); - return -1; - } -@@ -1184,19 +1227,35 @@ get_section_job(struct scan_token* scan, char* section, struct job_data* job, - { - char* data[SCAN_KEYWORD_NUM]; - char* buffer; -+ char* default_section; - int rc; - int i; - -+ rc = get_default_section(scan, &default_section, &i); -+ if (rc) -+ return rc; - if (section == NULL) { -- rc = get_default_section(scan, §ion, &i); -- if (rc) -- return rc; -+ section = default_section; - if (i) { - /* 'defaultmenu' was specified */ - rc = get_menu_job(scan, section, job); - return rc; - } - } -+ else -+ { -+ char* name = NULL; -+ -+ for (i = 0; (int) scan[i].id != 0; i++) { -+ if (scan[i].id == scan_id_section_heading) { -+ name = scan[i].content.section.name; -+ } -+ if (scan[i].id == scan_id_keyword_assignment && -+ scan[i].content.keyword.keyword == scan_keyword_target && -+ !strcmp(DEFAULTBOOT_SECTION, name)) -+ default_target = misc_strdup(scan[i].content.keyword.value); -+ } -+ } - if (strcmp(section, DEFAULTBOOT_SECTION) == 0) { - error_reason("Special section '" DEFAULTBOOT_SECTION "' cannot " - "be used as target section"); -@@ -1268,10 +1327,118 @@ get_section_job(struct scan_token* scan, char* section, struct job_data* job, - } - - -+/* Create a fake menu to simulate the old s390utils-1.1.7 multiboot -+ * behaviour. */ -+static struct scan_token * -+create_fake_menu(struct scan_token *scan) -+{ -+ int i, j, pos, numsec, size, defaultpos; -+ char *name; -+ char *target; -+ char *timeout; -+ char *seclist[1024]; -+ char *defaultsection; -+ char buf[1024]; -+ struct scan_token *tmp; -+ -+ /* Count # of sections */ -+ numsec = 0; -+ name = NULL; -+ target = NULL; -+ timeout = NULL; -+ for (i = 0; (int) scan[i].id != 0; i++) { -+ if (scan[i].id == scan_id_section_heading) { -+ name = scan[i].content.section.name; -+ if (strcmp(DEFAULTBOOT_SECTION, name)) -+ seclist[numsec++] = name; -+ } -+ if (scan[i].id == scan_id_keyword_assignment && -+ (scan[i].content.keyword.keyword == scan_keyword_dumpto || -+ scan[i].content.keyword.keyword == scan_keyword_dumptofs)) { -+ numsec--; -+ continue; -+ } -+ if (scan[i].id == scan_id_keyword_assignment && -+ scan[i].content.keyword.keyword == scan_keyword_target && -+ !strcmp(DEFAULTBOOT_SECTION, name)) -+ target = 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; -+ } -+ get_default_section(scan, &defaultsection, &j); -+ -+ if (defaultsection == NULL) { -+ error_text("Unable to find default section in your config file."); -+ return NULL; -+ } -+ -+ if (target == NULL) { -+ error_text("Keyword target is missing in default section."); -+ return NULL; -+ } -+ -+ default_target = misc_strdup(target); -+ -+ size = i+6+numsec; -+ tmp = (struct scan_token *) misc_malloc(size * sizeof(struct scan_token)); -+ if (tmp == NULL) { -+ error_text("Couldn't allocate memory for menu entries"); -+ return NULL; -+ } -+ -+ memset(tmp, 0, size * sizeof(struct scan_token)); -+ memcpy(tmp, scan, i * sizeof(struct scan_token)); -+ free(scan); -+ scan = tmp; -+ -+ defaultpos = 0; -+ for (j = 0; j < numsec; j++) { -+ if (!strcmp(defaultsection, seclist[j])) -+ defaultpos = j+1; -+ } -+ -+ snprintf(buf, 1024, "%d", defaultpos); -+ -+ scan[i].id = scan_id_menu_heading; -+ scan[i].line = i; -+ scan[i++].content.menu.name = misc_strdup("rh-automatic-menu"); -+ scan[i].id = scan_id_keyword_assignment; -+ scan[i].line = i; -+ scan[i].content.keyword.keyword = scan_keyword_target; -+ scan[i++].content.keyword.value = misc_strdup(target); -+ scan[i].id = scan_id_keyword_assignment; -+ scan[i].line = i; -+ scan[i].content.keyword.keyword = scan_keyword_default; -+ scan[i++].content.keyword.value = misc_strdup(buf); -+ scan[i].id = scan_id_keyword_assignment; -+ scan[i].line = i; -+ scan[i].content.keyword.keyword = scan_keyword_prompt; -+ scan[i++].content.keyword.value = misc_strdup("1"); -+ scan[i].id = scan_id_keyword_assignment; -+ scan[i].line = i; -+ scan[i].content.keyword.keyword = scan_keyword_timeout; -+ if (timeout) -+ scan[i++].content.keyword.value = misc_strdup(timeout); -+ else -+ scan[i++].content.keyword.value = misc_strdup("15"); -+ -+ pos = i; -+ for (i = 0; iautomenu) { -+ nscan = create_fake_menu(scan); -+ if (nscan == NULL) { -+ scan_free(scan); -+ return -1; -+ } -+ scan = nscan; -+ } -+ - /* Get job from config file data */ -- if (cmdline->menu != NULL) -+ if (cmdline->menu != NULL || cmdline->automenu) { -+ if (cmdline->automenu) -+ cmdline->menu = misc_strdup("rh-automatic-menu"); - rc = get_menu_job(scan, cmdline->menu, job); -+ } - else { - rc = get_section_job(scan, cmdline->section, job, - cmdline->data[(int) scan_keyword_parameters]); -diff --git a/zipl/src/scan.c b/zipl/src/scan.c -index 9948092..caca3cf 100644 ---- a/zipl/src/scan.c -+++ b/zipl/src/scan.c -@@ -33,9 +33,9 @@ enum scan_key_state scan_key_table[SCAN_SECTION_NUM][SCAN_KEYWORD_NUM] = { - * rs enu - */ - /* defaultboot */ -- {opt, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, opt, inv, inv}, -+ {opt, inv, inv, inv, inv, inv, inv, inv, req, inv, opt, opt, inv, inv}, - /* ipl */ -- {inv, inv, inv, req, opt, opt, opt, inv, req, inv, inv, inv, inv, inv}, -+ {inv, inv, inv, req, opt, opt, opt, inv, opt, inv, inv, inv, inv, inv}, - /* segment load */ - {inv, inv, inv, inv, inv, inv, inv, req, req, inv, inv, inv, inv, inv}, - /* part dump */ -diff --git a/zipl/src/zipl.c b/zipl/src/zipl.c -index 9cb764c..4d9fd36 100644 ---- a/zipl/src/zipl.c -+++ b/zipl/src/zipl.c -@@ -73,6 +73,7 @@ static const char* usage_text[] = { - "-n, --noninteractive Answer all confirmation questions with 'yes'", - "-V, --verbose Provide more verbose output", - "-a, --add-files Add all referenced files to bootmap file", -+"-x, --no-automenu Don't autogenerate multiboot menu", - " --dry-run Simulate run but don't modify IPL records" - }; - --- -1.6.3.3 - diff --git a/0003-s390-tools-1.8.1-fdasd-su.patch b/0003-s390-tools-1.8.1-fdasd-su.patch deleted file mode 100644 index ac49f37..0000000 --- a/0003-s390-tools-1.8.1-fdasd-su.patch +++ /dev/null @@ -1,33 +0,0 @@ -From f8acd63e169cdec39c47ac0d8c1b98e115cf24a1 Mon Sep 17 00:00:00 2001 -From: =?utf-8?q?Dan=20Hor=C3=A1k?= -Date: Thu, 23 Apr 2009 11:46:01 +0200 -Subject: [PATCH 3/9] s390-tools-1.8.1-fdasd-su - ---- - fdasd/fdasd.c | 10 ++++++---- - 1 files changed, 6 insertions(+), 4 deletions(-) - -diff --git a/fdasd/fdasd.c b/fdasd/fdasd.c -index aa16659..3a54968 100644 ---- a/fdasd/fdasd.c -+++ b/fdasd/fdasd.c -@@ -2009,10 +2009,12 @@ fdasd_get_geometry (fdasd_anchor_t *anc) - if (anc->verbose) printf("disk type check : ok\n"); - - if (dasd_info.FBA_layout != 0) { -- snprintf(err_str, ERROR_STRING_SIZE, -- "%s is not formatted with z/OS compatible " -- "disk layout!", options.device); -- fdasd_error(anc, wrong_disk_format, err_str); -+ if(!anc->silent) { -+ snprintf(err_str, ERROR_STRING_SIZE, -+ "%s is not formatted with z/OS compatible " -+ "disk layout!", options.device); -+ fdasd_error(anc, wrong_disk_format, err_str); -+ } - } - - if (anc->verbose) printf("disk layout check : ok\n"); --- -1.6.3.3 - diff --git a/0004-s390-tools-1.8.1-fdasd-raid-lvm.patch b/0004-s390-tools-1.8.1-fdasd-raid-lvm.patch deleted file mode 100644 index da27e0a..0000000 --- a/0004-s390-tools-1.8.1-fdasd-raid-lvm.patch +++ /dev/null @@ -1,153 +0,0 @@ -From 35298f5f9512835071b8c08e843731bbfbb15b7e Mon Sep 17 00:00:00 2001 -From: =?utf-8?q?Dan=20Hor=C3=A1k?= -Date: Thu, 23 Apr 2009 11:46:16 +0200 -Subject: [PATCH 4/9] s390-tools-1.8.1-fdasd-raid-lvm - ---- - fdasd/fdasd.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++---------- - 1 files changed, 53 insertions(+), 12 deletions(-) - -diff --git a/fdasd/fdasd.c b/fdasd/fdasd.c -index 3a54968..a526d7f 100644 ---- a/fdasd/fdasd.c -+++ b/fdasd/fdasd.c -@@ -258,10 +258,10 @@ fdasd_error(fdasd_anchor_t *anc, enum fdasd_failure why, char *str) - static int - read_line(void) - { -- bzero(line_buffer, LINE_LENGTH); - line_ptr = line_buffer; - if (!fgets(line_buffer, LINE_LENGTH, stdin)) - return 0; -+ line_buffer[LINE_LENGTH-1] = 0; - while (*line_ptr && !isgraph(*line_ptr)) - line_ptr++; - return *line_ptr; -@@ -312,6 +312,10 @@ fdasd_partition_type (char *str) - strcpy(str, "Linux native"); - else if (strncmp("SWAP ", str, 6) == 0) - strcpy(str, "Linux swap"); -+ else if (strncmp("RAID ", str, 6) == 0) -+ strcpy(str, "Linux Raid"); -+ else if (strncmp("LVM ", str, 6) == 0) -+ strcpy(str, "Linux LVM"); - else - strcpy(str, "unknown"); - -@@ -1122,14 +1126,24 @@ fdasd_write_vtoc_labels (fdasd_anchor_t *anc) - strncpy(c1, s2, 31); - } - else { -+ char str[20]; - char *tmp = strstr(ch, "SWAP"); -+ char *tmp1 = strstr(ch, "RAID"); - - /* create a new data set name */ - while (getpos(anc, k) > -1) - k++; - - setpos(anc, k, i-1); -- -+ -+ strncpy(s2, ch, 44); -+ s2[44]=0; -+ vtoc_ebcdic_dec(s2, s2, 44); -+ c2 = strstr(s2, "PART"); -+ if (c2 != NULL) strncpy(str, c2+=9, 6); -+ str[6] = '\0'; -+ fdasd_partition_type(str); -+ - strncpy(ch, "LINUX.V " - " ", 44); - -@@ -1145,10 +1159,21 @@ fdasd_write_vtoc_labels (fdasd_anchor_t *anc) - strncpy(c1, dsno, 4); - - c1 += 4; -- if (tmp) -- strncpy(c1, ".SWAP", 5); -- else -- strncpy(c1, ".NATIVE", 7); -+ if (tmp || tmp1) { -+ if (tmp) -+ strncpy(c1, ".SWAP", 5); -+ if (tmp1) -+ strncpy(c1, ".RAID", 5); -+ } else { -+ if (strcmp("unknown", str) == 0) { -+ strncpy(c1, ".NATIVE", 7); -+ } -+ else { -+ strncpy(c1, ".", 1); -+ strncpy(c1+1, c2, 6); -+ } -+ } -+ - } - vtoc_ebcdic_enc(ch, ch, 44); - if (anc->verbose) printf("%2x ", part_info->f1->DS1FMTID); -@@ -1433,9 +1458,10 @@ fdasd_change_part_type (fdasd_anchor_t *anc) - - printf("current partition type is: %s\n\n", fdasd_partition_type(str)); - printf(" 1 Linux native\n" \ -- " 2 Linux swap\n\n"); -+ " 2 Linux swap\n" \ -+ " 3 Linux raid\n\n"); - part_type = 0; -- while ((part_type < 1) || (part_type > 2)) { -+ while ((part_type < 1) || (part_type > 3)) { - while (!isdigit(part_type = - read_char("new partition type: "))); - part_type -= 48; -@@ -1448,6 +1474,9 @@ fdasd_change_part_type (fdasd_anchor_t *anc) - case 2: - strncpy(str, "SWAP ", 6); - break; -+ case 3: -+ strncpy(str, "RAID ", 6); -+ break; - default: - printf("'%d' is not supported!\n", part_type); - } -@@ -1625,7 +1654,7 @@ fdasd_process_invalid_vtoc(fdasd_anchor_t *anc) - static void - fdasd_process_valid_vtoc(fdasd_anchor_t *anc, unsigned long blk) - { -- int f1_counter = 0, f7_counter = 0, f5_counter = 0; -+ int f1_counter = 0, f7_counter = 0, f5_counter = 0, oldfmt = 0; - int i, part_no, f1_size = sizeof(format1_label_t); - partition_info_t *part_info = anc->first; - format1_label_t f1_label; -@@ -1677,14 +1706,26 @@ fdasd_process_valid_vtoc(fdasd_anchor_t *anc, unsigned long blk) - vtoc_ebcdic_enc(part_info->f1->DS1DSNAM, - part_info->f1->DS1DSNAM, 44); - -- if ((part_no < 0) || (part_no >= USABLE_PARTITIONS)) -+ /* this dasd has data set names 0000-0002 -+ but we use now 0001-0003 */ -+ if (part_no == -1) -+ oldfmt++; -+ -+ if (((oldfmt == 0) && (part_no < 0)) -+ || (part_no >= USABLE_PARTITIONS)) - printf("WARNING: partition number (%i) found " - "in data set name of an existing " - "partition\ndoes not match range of " - "possible partition numbers (1-%d)\n\n", - part_no + 1, USABLE_PARTITIONS); -- else -- setpos(anc, part_no, f1_counter); -+ else { -+ if (oldfmt) /* correct +1 */ { -+ setpos(anc, part_no+1, f1_counter); -+ printf("Correcting f1 header number!\n"); -+ } -+ else -+ setpos(anc, part_no, f1_counter); -+ } - - part_info = part_info->next; - f1_counter++; --- -1.6.3.3 - diff --git a/0005-don-t-create-automenu-when-default-menu-exists.patch b/0005-don-t-create-automenu-when-default-menu-exists.patch deleted file mode 100644 index 1cad83c..0000000 --- a/0005-don-t-create-automenu-when-default-menu-exists.patch +++ /dev/null @@ -1,59 +0,0 @@ -From 1efcfe8d7ad20f7a3f584628375da60682b4579c Mon Sep 17 00:00:00 2001 -From: =?utf-8?q?Dan=20Hor=C3=A1k?= -Date: Thu, 23 Apr 2009 15:28:06 +0200 -Subject: [PATCH 5/9] don't create automenu when default menu exists - ---- - zipl/src/job.c | 25 +++++++++++++++---------- - 1 files changed, 15 insertions(+), 10 deletions(-) - -diff --git a/zipl/src/job.c b/zipl/src/job.c -index 6a526e4..89c8c23 100644 ---- a/zipl/src/job.c -+++ b/zipl/src/job.c -@@ -1441,6 +1441,8 @@ get_job_from_config_file(struct command_line* cmdline, struct job_data* job) - struct scan_token* scan, *nscan; - char* filename; - char* source; -+ char* default_section; -+ int is_menu; - int rc; - - /* Read configuration file */ -@@ -1470,20 +1472,23 @@ get_job_from_config_file(struct command_line* cmdline, struct job_data* job) - scan_free(scan); - return rc; - } -- -- if (cmdline->automenu) { -- nscan = create_fake_menu(scan); -- if (nscan == NULL) { -- scan_free(scan); -- return -1; -- } -- scan = nscan; -- } -+ -+ /* disable automenu iff default menu exists */ -+ rc = get_default_section(scan, &default_section, &is_menu); -+ if (!rc && is_menu) -+ cmdline->automenu = 0; - - /* Get job from config file data */ - if (cmdline->menu != NULL || cmdline->automenu) { -- if (cmdline->automenu) -+ if (cmdline->automenu) { -+ nscan = create_fake_menu(scan); -+ if (nscan == NULL) { -+ scan_free(scan); -+ return -1; -+ } -+ scan = nscan; - cmdline->menu = misc_strdup("rh-automatic-menu"); -+ } - rc = get_menu_job(scan, cmdline->menu, job); - } - else { --- -1.6.3.3 - diff --git a/0006-s390-tools-1.8.1-zipl-kdump-man.patch b/0006-s390-tools-1.8.1-zipl-kdump-man.patch deleted file mode 100644 index cdbcf37..0000000 --- a/0006-s390-tools-1.8.1-zipl-kdump-man.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 894de5382653e4389dc8490f2ee4a2a9c59eb86d Mon Sep 17 00:00:00 2001 -From: =?utf-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 19 Jun 2009 10:01:30 +0200 -Subject: [PATCH 6/9] s390-tools-1.8.1-zipl-kdump-man - -Description: Add kdump kernel installation instruction to zipl man page. -Symptom: User wants to prepare SCSI disk for dump, but has not installed - the kdump kernel rpm. -Problem: The installation of the kdump kernel rpm is prereq for preparing - a SCSI dump disk for dump. -Solution: Document that in the zipl man page. ---- - zipl/man/zipl.8 | 2 ++ - 1 files changed, 2 insertions(+), 0 deletions(-) - -diff --git a/zipl/man/zipl.8 b/zipl/man/zipl.8 -index 6ebf240..6df6026 100644 ---- a/zipl/man/zipl.8 -+++ b/zipl/man/zipl.8 -@@ -176,6 +176,8 @@ will be incomplete. - 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. - -+.B Note that before using this option the "kernel-kdump" rpm has to be -+.B installed. - .TP - .BR "\-M " " or " "--mvdump=" - Install a multi-volume dump record on each device associated with one of the --- -1.6.3.3 - diff --git a/0007-s390-tools-1.8.1-lszfcp-perf.patch b/0007-s390-tools-1.8.1-lszfcp-perf.patch deleted file mode 100644 index 47bd955..0000000 --- a/0007-s390-tools-1.8.1-lszfcp-perf.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 80e1e88e108b774c7aa7c201326f1a92cf1b7e35 Mon Sep 17 00:00:00 2001 -From: =?utf-8?q?Dan=20Hor=C3=A1k?= -Date: Wed, 26 Aug 2009 14:21:14 +0200 -Subject: [PATCH 7/9] s390-tools-1.8.1-lszfcp-perf - ---- - zconf/lszfcp | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/zconf/lszfcp b/zconf/lszfcp -index d0bf84f..a872d05 100755 ---- a/zconf/lszfcp -+++ b/zconf/lszfcp -@@ -233,7 +233,7 @@ show_devices() - # Differentiate old and new sysfs layout - if $FC_CLASS; then - SCSI_DEVICE_LIST=`ls -d \ -- $SYSFS/devices/css0/*/*/host*/rport*/target*/*/ \ -+ $SYSFS/bus/ccw/drivers/zfcp/*/host*/rport*/target*/*/ \ - 2>/dev/null |grep -P '\d+:\d+:\d+:\d+'` - else - SCSI_DEVICE_LIST=`ls -d $SYSFS/devices/css0/*/*/host*/*/` --- -1.6.3.3 - diff --git a/0008-fix-string-overflow-in-vtoc_volume_label_init.patch b/0008-fix-string-overflow-in-vtoc_volume_label_init.patch deleted file mode 100644 index 790ebad..0000000 --- a/0008-fix-string-overflow-in-vtoc_volume_label_init.patch +++ /dev/null @@ -1,28 +0,0 @@ -From d0c06736586d8f85f2e7c64291f3f289c12a0caa Mon Sep 17 00:00:00 2001 -From: =?utf-8?q?Dan=20Hor=C3=A1k?= -Date: Thu, 24 Sep 2009 09:12:39 +0200 -Subject: [PATCH 8/9] fix string overflow in vtoc_volume_label_init - -Originaly it tries to copy a 84B string into 4B field and reset also -the other fields thru the overflow. This doesn't work with recent GCC -and the security-related compile flags that are used in Fedora. ---- - libvtoc/vtoc.c | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/libvtoc/vtoc.c b/libvtoc/vtoc.c -index 62a02a0..cebd5a4 100644 ---- a/libvtoc/vtoc.c -+++ b/libvtoc/vtoc.c -@@ -326,7 +326,7 @@ void vtoc_volume_label_init (volume_label_t *vlabel) - { - sprintf(buffer, "%84s", " "); - vtoc_ebcdic_enc(buffer, buffer, 84); -- strncpy(vlabel->volkey, buffer, 84); -+ memcpy(vlabel, buffer, 84); - } - - --- -1.6.3.3 - diff --git a/0009-change-default-load-address-for-ramdisk.patch b/0009-change-default-load-address-for-ramdisk.patch deleted file mode 100644 index f0b8d73..0000000 --- a/0009-change-default-load-address-for-ramdisk.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 1697d4f40be133c78cd09fb0af14af22e8c6219e Mon Sep 17 00:00:00 2001 -From: =?utf-8?q?Dan=20Hor=C3=A1k?= -Date: Sun, 4 Oct 2009 17:46:16 +0200 -Subject: [PATCH 9/9] change default load address for ramdisk - -The default load address for the initial ramdisk is changed from -0x800000 to 0x2000000 to allow larger kernels to be loaded. ---- - zipl/include/zipl.h | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/zipl/include/zipl.h b/zipl/include/zipl.h -index 1f70e48..d2d26dd 100644 ---- a/zipl/include/zipl.h -+++ b/zipl/include/zipl.h -@@ -22,7 +22,7 @@ - #define DEFAULT_PARMFILE_ADDRESS 0x1000LL - #define DEFAULT_STAGE3_ADDRESS 0xa000LL - #define DEFAULT_IMAGE_ADDRESS 0x10000LL --#define DEFAULT_RAMDISK_ADDRESS 0x800000LL -+#define DEFAULT_RAMDISK_ADDRESS 0x2000000LL - - #define PSW_ADDRESS_MASK 0x000000007fffffffLL - #define PSW_LOAD 0x0008000080000000LL --- -1.6.3.3 - diff --git a/0010-improve-mon_statd-init-script.patch b/0010-improve-mon_statd-init-script.patch deleted file mode 100644 index 0d91681..0000000 --- a/0010-improve-mon_statd-init-script.patch +++ /dev/null @@ -1,189 +0,0 @@ -From 7cd1f2254e27cae8e061d1b42fa6c78bc10a5f39 Mon Sep 17 00:00:00 2001 -From: =?utf-8?q?Dan=20Hor=C3=A1k?= -Date: Thu, 8 Oct 2009 10:29:23 +0200 -Subject: [PATCH] improve mon_statd init script - -- stop: do not print error messages if a daemon is not configured -- start: do not load module if no daemon is configured -- remove useless newlines ---- - etc/init.d/mon_statd | 113 ++++++++++++++++++++++++++++---------------------- - 1 files changed, 64 insertions(+), 49 deletions(-) - -diff --git a/etc/init.d/mon_statd b/etc/init.d/mon_statd -index 4d84b5b..60bcf00 100755 ---- a/etc/init.d/mon_statd -+++ b/etc/init.d/mon_statd -@@ -22,7 +22,6 @@ PROCD_PATH=/usr/sbin/$PROCD - CONFIG_FILE=/etc/sysconfig/$DAEMON - FSSTATD_PID_FILE=/var/run/$FSSTATD.pid - PROCD_PID_FILE=/var/run/$PROCD.pid --RETVAL=0 - - # source function library - . /lib/lsb/init-functions -@@ -32,68 +31,81 @@ if [ -f $CONFIG_FILE ]; then - . $CONFIG_FILE - fi - --start() -+load_kernel_module() - { - if [ ! -e /dev/monwriter ]; then - echo "Loading monwriter module..." - modprobe monwriter 2>&1 -- if [ "$?" -ne 0 ]; then -+ if [ $? -ne 0 ]; then -+ exit 1 -+ fi -+ udevsettle -+ if [ $? -ne 0 ]; then - exit 1 - fi -- while [ ! -e /dev/monwriter ]; do -- sleep 0 -- done - fi -+} - -- if [ ! -f $FSSTATD_PID_FILE -a "$FSSTAT" = "yes" ]; then -- echo -n $"Starting $FSSTATD:" -- $FSSTATD_PATH -i $FSSTAT_INTERVAL -- if [ $? == 0 ]; then -+start_daemon() -+{ -+ local daemon_name=$1 -+ local daemon_interval=$2 -+ local daemon_pid_file=$3 -+ local daemon_path=$4 -+ -+ if [ ! -f $daemon_pid_file ]; then -+ load_kernel_module -+ echo -n "Starting $daemon_name:" -+ $daemon_path -i $daemon_interval -+ if [ $? -eq 0 ]; then - touch /var/lock/subsys/mon_statd - log_success_msg - else - log_failure_msg - fi -- elif [ "$FSSTAT" = "yes" ]; then -- echo "$FSSTATD (pid $(cat $FSSTATD_PID_FILE)) is already running..." -+ else -+ echo "$daemon_name (pid $(cat $daemon_pid_file)) is already running..." -+ fi -+} -+ -+start() -+{ -+ if [ "$FSSTAT" = "yes" ]; then -+ start_daemon $FSSTATD $FSSTAT_INTERVAL $FSSTATD_PID_FILE \ -+ $FSSTATD_PATH - fi - -- if [ ! -f $PROCD_PID_FILE -a "$PROC" = "yes" ]; then -- echo -n $"Starting $PROCD:" -- $PROCD_PATH -i $PROC_INTERVAL -- if [ $? == 0 ]; then -- touch /var/lock/subsys/mon_statd -- log_success_msg -- else -- log_failure_msg -- fi -- elif [ "$PROC" = "yes" ]; then -- echo "$PROCD (pid $(cat $PROCD_PID_FILE)) is already running..." -+ if [ "$PROC" = "yes" ]; then -+ start_daemon $PROCD $PROC_INTERVAL $PROCD_PID_FILE \ -+ $PROCD_PATH - fi -- echo - } - --stop() -+stop_daemon() - { -- echo -n $"Stopping $FSSTATD:" -- if [ -f $FSSTATD_PID_FILE ]; then -- killproc $FSSTATD_PATH -TERM -+ local daemon_name=$1 -+ local daemon_pid_file=$2 -+ local daemon_path=$3 -+ -+ echo -n "Stopping $daemon_name:" -+ if [ -f $daemon_pid_file ]; then -+ killproc $daemon_path -TERM - log_success_msg -- rm -f $FSSTATD_PID_FILE -+ rm -f $daemon_pid_file - else - log_failure_msg - fi -+} - -- echo -n $"Stopping $PROCD:" -- if [ -f $PROCD_PID_FILE ]; then -- killproc $PROCD_PATH -TERM -- log_success_msg -- rm -f $PROCD_PID_FILE -- else -- log_failure_msg -+stop() -+{ -+ if [ "$FSSTAT" = "yes" ]; then -+ stop_daemon $FSSTATD $FSSTATD_PID_FILE $FSSTATD_PATH -+ fi -+ if [ "$PROC" = "yes" ]; then -+ stop_daemon $PROCD $PROCD_PID_FILE $PROCD_PATH - fi - rm -f /var/lock/subsys/mon_statd -- echo - } - - restart() { -@@ -101,20 +113,23 @@ restart() { - start - } - --status() -+status_daemon() - { -- if [ ! -f $FSSTATD_PID_FILE ]; then -- echo "$FSSTATD is not running." -- else -- echo "$FSSTATD (pid $(cat $FSSTATD_PID_FILE), interval: $FSSTAT_INTERVAL) is running." -- fi -+ local daemon_name=$1 -+ local daemon_pid_file=$2 -+ local daemon_interval=$3 - -- if [ ! -f $PROCD_PID_FILE ]; then -- echo "$PROCD is not running." -+ if [ ! -f $daemon_pid_file ]; then -+ echo "$daemon_name is not running." - else -- echo "$PROCD (pid $(cat $PROCD_PID_FILE), interval: $PROC_INTERVAL) is running." -+ echo "$daemon_name (pid $(cat $daemon_pid_file), interval: $daemon_interval) is running." - fi -- echo -+} -+ -+status() -+{ -+ status_daemon $FSSTATD $FSSTATD_PID_FILE $FSSTAT_INTERVAL -+ status_daemon $PROCD $PROCD_PID_FILE $PROC_INTERVAL - } - - # How are we called? -@@ -133,7 +148,7 @@ case "$1" in - ;; - *) - echo "Usage: $DAEMON {start|stop|status|restart|reload}" -- RETVAL=1 -+ exit 1 - esac - --exit $RETVAL -+exit 0 --- -1.6.3.3 - diff --git a/0011-update-readahead-value-for-better-performance.patch b/0011-update-readahead-value-for-better-performance.patch deleted file mode 100644 index 99ac905..0000000 --- a/0011-update-readahead-value-for-better-performance.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 5707bfdf0aac985e8e82c9a5004eb458d1d79801 Mon Sep 17 00:00:00 2001 -From: =?utf-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 30 Oct 2009 11:23:08 +0100 -Subject: [PATCH] update readahead value for better performance - -Description: dasd,zfcp: Add udev rule to set increased "default max readahead" -Symptom: Sequential read performance on disks is not as good as it could be. -Problem: The current "default max readahead" defined by the kernel is too - small for s390 (128 kb). -Solution: Add udev rule to set a better default value (512 kb). This will - increase sequential read performance up to 40%. ---- - etc/udev/rules.d/60-readahead.rules | 13 +++++++++++++ - 1 files changed, 13 insertions(+), 0 deletions(-) - create mode 100644 etc/udev/rules.d/60-readahead.rules - -diff --git a/etc/udev/rules.d/60-readahead.rules b/etc/udev/rules.d/60-readahead.rules -new file mode 100644 -index 0000000..3133c66 ---- /dev/null -+++ b/etc/udev/rules.d/60-readahead.rules -@@ -0,0 +1,13 @@ -+# -+# Rules to set an increased default max readahead size for s390 disk devices -+# This file should be installed in /etc/udev/rules.d -+# -+ -+SUBSYSTEM!="block", GOTO="ra_end" -+ -+ACTION!="add", GOTO="ra_end" -+# on device add set initial readahead to 512 (instead of in kernel 128) -+KERNEL=="sd*[!0-9]", ATTR{queue/read_ahead_kb}="512" -+KERNEL=="dasd*[!0-9]", ATTR{queue/read_ahead_kb}="512" -+ -+LABEL="ra_end" --- -1.6.3.3 - diff --git a/0012-fix-multipath-device-detection-in-ziomon.patch b/0012-fix-multipath-device-detection-in-ziomon.patch deleted file mode 100644 index c66b762..0000000 --- a/0012-fix-multipath-device-detection-in-ziomon.patch +++ /dev/null @@ -1,33 +0,0 @@ -From e9c5f5e15122b143f5f202e9782773321f8cf6b9 Mon Sep 17 00:00:00 2001 -From: =?utf-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 13 Nov 2009 10:46:57 +0100 -Subject: [PATCH 12/14] fix multipath device detection in ziomon - -Description: ziomon: Fix multipath device detection -Symptom: Running ziomon with a valid multipath device like - /dev/mapper/36005076303ffc56200000000000010cc - fails with "ziomon: The following devices do not seem - to exist:". -Problem: The output from multipath -l is not appended correctly to - the mp_arr array. -Solution: Properly initializing mp_arr to an empty array fixes this. ---- - ziomon/ziomon | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/ziomon/ziomon b/ziomon/ziomon -index 7449843..924c1dd 100755 ---- a/ziomon/ziomon -+++ b/ziomon/ziomon -@@ -471,7 +471,7 @@ function clean_devices() { - function check_for_multipath_devices() { - local i; - local j; -- local mp_arr; -+ local mp_arr=(); - local line; - local devices_basenames; - local tmp; --- -1.6.3.3 - diff --git a/0013-zipl-handle-status-during-ipl.patch b/0013-zipl-handle-status-during-ipl.patch deleted file mode 100644 index 60c0265..0000000 --- a/0013-zipl-handle-status-during-ipl.patch +++ /dev/null @@ -1,119 +0,0 @@ -From 4770ba2827a5c582c6376139f1ec2d10519779e2 Mon Sep 17 00:00:00 2001 -From: =?utf-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 13 Nov 2009 10:48:17 +0100 -Subject: [PATCH 13/14] zipl - handle status during ipl - -Description: zipl: handle status during ipl -Symptom: You encounter an error during IPL with "disabled wait" message. -Problem: During the IPL sequence the subchannel of the IPL device has - to be enabled. If there is a status pending or busy condition - on the subchannel the IPL code abborts the IPL and goes into - disabled wait. -Solution: To resolve the problem the IPL code accepts the status pending - or busy condition on the subchannel and does up to 256 retries. ---- - zipl/boot/common.S | 65 ++++++++++++++++++++++++++++++++++----------------- - 1 files changed, 43 insertions(+), 22 deletions(-) - -diff --git a/zipl/boot/common.S b/zipl/boot/common.S -index 620fed9..fa45e5a 100644 ---- a/zipl/boot/common.S -+++ b/zipl/boot/common.S -@@ -6,6 +6,7 @@ - # - - # some definitions from kernel land -+__LC_IRB = 0x0300 - __LC_IPLDEV = 0xC6C - __LC_PANIC_MAGIC= 0xE00 - -@@ -186,23 +187,33 @@ _load_blocklist: - _enable_device: - stm %r6,%r15,24(%r15) - basr %r13,0 # base register --0: s %r15,1f-0b(%r13) -+0: s %r15,4f-0b(%r13) - lr %r1,%r2 -- l %r2,4f-0b(%r13) # set panik code early -- stsch 2f-0b(%r13) -- oi 2f-0b+5(%r13),0x84 # enable ssch and multipath mode -- msch 2f-0b(%r13) -- bnz _panik-0b(%r13) # subchannel busy or in error ? -- lctl %c6,%c6,3f-0b(%r13) # enable all interrupt classes -+ l %r2,7f-0b(%r13) # set panik code early -+ stsch 5f-0b(%r13) -+ brc 1,3f # panic if not operational -+ oi 5f-0b+5(%r13),0x80 # enable subchannel -+ lhi %r6,256 # r6 retry counter -+1: # modify subchannel -+ msch 5f-0b(%r13) -+ brc 6,2f # status pending or busy -+ brc 1,3f # panic if not operational -+ lctl %c6,%c6,6f-0b(%r13) # enable all interrupt classes - sr %r2,%r2 -- ic %r2,10+2f-0b(%r13) # return lpum in r2 -+ ic %r2,10+5f-0b(%r13) # return lpum in r2 - lm %r6,%r15,120(%r15) - br %r14 --1: .long 96 -+2: # clear status and retry -+ tsch __LC_IRB -+ brc 1,3f # panic if not operational -+ brct %r6,1b # retry -+3: # panic -+ b _panik-0b(%r13) # panic -+4: .long 96 - .align 8 --2: .fill 64,1,0 --3: .long 0xff000000 # CR6 initial value --4: .long EENABLE_DEV -+5: .fill 64,1,0 -+6: .long 0xff000000 # CR6 initial value -+7: .long EENABLE_DEV - - # - # Disable I/O on the ipl device. -@@ -211,20 +222,30 @@ _enable_device: - _disable_device: - stm %r6,%r15,24(%r15) - basr %r13,0 # base register --0: s %r15,1f-0b(%r13) -+0: s %r15,4f-0b(%r13) - lr %r1,%r2 -- l %r2,3f-0b(%r13) # set panik code early -- lctl %c6,%c6,2f-0b(%r13) # disable all interrupt classes -- stsch 2f-0b(%r13) -- ni 2f-0b+5(%r13),0x7B # disable ssch and multipath mode -- msch 2f-0b(%r13) -- bnz _panik-0b(%r13) # subchannel busy or in error ? -+ l %r2,6f-0b(%r13) # set panik code early -+ lctl %c6,%c6,5f-0b(%r13) # disable all interrupt classes -+ stsch 5f-0b(%r13) -+ brc 1,3f # panic if not operational -+ ni 5f-0b+5(%r13),0x7F # disable subchannel -+ lhi %r6,256 # r6 retry counter -+1: # modify subchannel -+ msch 5f-0b(%r13) -+ brc 6,2f # status pending or busy -+ brc 1,3f # panic if not operational - lm %r6,%r15,120(%r15) - br %r14 --1: .long 96 -+2: # clear status and retry -+ tsch __LC_IRB -+ brc 1,3f # panic if not operational -+ brct %r6,1b -+3: # panic -+ b _panik-0b(%r13) # panic -+4: .long 96 - .align 8 --2: .long 0x00000000 # CR6 (all interrupts classes disabled) --3: .long EDISABLE_DEV -+5: .long 0x00000000 # CR6 (all interrupts classes disabled) -+6: .long EDISABLE_DEV - .endm - - .macro io_subroutines --- -1.6.3.3 - diff --git a/0014-dasdview-fdasd-fix-floating-point-error-for-unformat.patch b/0014-dasdview-fdasd-fix-floating-point-error-for-unformat.patch deleted file mode 100644 index 9c7b486..0000000 --- a/0014-dasdview-fdasd-fix-floating-point-error-for-unformat.patch +++ /dev/null @@ -1,285 +0,0 @@ -From 72168f2269dac94d25112e0ad548bd3fcdf907d9 Mon Sep 17 00:00:00 2001 -From: =?utf-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 13 Nov 2009 10:49:37 +0100 -Subject: [PATCH 14/14] dasdview, fdasd: fix floating point error for unformatted devices - -When executed on an unformatted device the tools dasdview and fdasd -will end with an floating point exception error. -The reason for the error lies in the fact that we cannot rely on the -HDIO_GETGEO ioctl to report a correct number of cylinders and so we -compute the number of cylinders from the device size. However, -for unformatted devices the device size is zero and thus our -computation ends with a floating point exception. -To solve this issue read the correct number of cylinders from -the DASD device characteristics, which can be found in the data -returned by the BIODASDINFO ioctl. ---- - dasdview/dasdview.c | 22 +++++++------- - dasdview/dasdview.h | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++ - fdasd/fdasd.c | 18 ++++++------ - fdasd/fdasd.h | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++ - 4 files changed, 168 insertions(+), 20 deletions(-) - -diff --git a/dasdview/dasdview.c b/dasdview/dasdview.c -index a74ae33..2909b16 100644 ---- a/dasdview/dasdview.c -+++ b/dasdview/dasdview.c -@@ -169,7 +169,7 @@ static void - dasdview_get_info(dasdview_info_t *info) - { - int fd; -- u_int64_t device_size; -+ struct dasd_eckd_characteristics *characteristics; - - fd = open(info->device, O_RDONLY); - if (fd == -1) -@@ -201,16 +201,6 @@ dasdview_get_info(dasdview_info_t *info) - exit(-1); - } - -- if (ioctl(fd, BLKGETSIZE64, &device_size) != 0) { -- close(fd); -- zt_error_print("dasdview: ioctl error\n" \ -- "Could not retrieve device size information.\n"); -- exit(-1); -- } -- -- info->hw_cylinders = ((device_size / info->blksize) -- / info->geo.sectors) / info->geo.heads; -- - /* get disk information */ - if (ioctl(fd, BIODASDINFO2, &info->dasd_info) == 0) { - info->dasd_info_version = 2; -@@ -223,6 +213,16 @@ dasdview_get_info(dasdview_info_t *info) - exit(-1); - } - } -+ -+ characteristics = (struct dasd_eckd_characteristics *) -+ &info->dasd_info.characteristics; -+ if (characteristics->no_cyl == LV_COMPAT_CYL && -+ characteristics->long_no_cyl) -+ info->hw_cylinders = characteristics->long_no_cyl; -+ else -+ info->hw_cylinders = characteristics->no_cyl; -+ -+ - close(fd); - } - -diff --git a/dasdview/dasdview.h b/dasdview/dasdview.h -index 39e1228..5388592 100644 ---- a/dasdview/dasdview.h -+++ b/dasdview/dasdview.h -@@ -72,6 +72,80 @@ typedef struct dasd_information2_t { - unsigned int reserved7; /* reserved for further use ,... */ - } dasd_information2_t; - -+struct dasd_eckd_characteristics { -+ unsigned short cu_type; -+ struct { -+ unsigned char support:2; -+ unsigned char async:1; -+ unsigned char reserved:1; -+ unsigned char cache_info:1; -+ unsigned char model:3; -+ } __attribute__ ((packed)) cu_model; -+ unsigned short dev_type; -+ unsigned char dev_model; -+ struct { -+ unsigned char mult_burst:1; -+ unsigned char RT_in_LR:1; -+ unsigned char reserved1:1; -+ unsigned char RD_IN_LR:1; -+ unsigned char reserved2:4; -+ unsigned char reserved3:8; -+ unsigned char defect_wr:1; -+ unsigned char XRC_supported:1; -+ unsigned char reserved4:1; -+ unsigned char striping:1; -+ unsigned char reserved5:4; -+ unsigned char cfw:1; -+ unsigned char reserved6:2; -+ unsigned char cache:1; -+ unsigned char dual_copy:1; -+ unsigned char dfw:1; -+ unsigned char reset_alleg:1; -+ unsigned char sense_down:1; -+ } __attribute__ ((packed)) facilities; -+ unsigned char dev_class; -+ unsigned char unit_type; -+ unsigned short no_cyl; -+ unsigned short trk_per_cyl; -+ unsigned char sec_per_trk; -+ unsigned char byte_per_track[3]; -+ unsigned short home_bytes; -+ unsigned char formula; -+ union { -+ struct { -+ unsigned char f1; -+ unsigned short f2; -+ unsigned short f3; -+ } __attribute__ ((packed)) f_0x01; -+ struct { -+ unsigned char f1; -+ unsigned char f2; -+ unsigned char f3; -+ unsigned char f4; -+ unsigned char f5; -+ } __attribute__ ((packed)) f_0x02; -+ } __attribute__ ((packed)) factors; -+ unsigned short first_alt_trk; -+ unsigned short no_alt_trk; -+ unsigned short first_dia_trk; -+ unsigned short no_dia_trk; -+ unsigned short first_sup_trk; -+ unsigned short no_sup_trk; -+ unsigned char MDR_ID; -+ unsigned char OBR_ID; -+ unsigned char director; -+ unsigned char rd_trk_set; -+ unsigned short max_rec_zero; -+ unsigned char reserved1; -+ unsigned char RWANY_in_LR; -+ unsigned char factor6; -+ unsigned char factor7; -+ unsigned char factor8; -+ unsigned char reserved2[3]; -+ unsigned char reserved3[6]; -+ unsigned int long_no_cyl; -+} __attribute__ ((packed)); -+ - /* - * values to be used for dasd_information2_t.format - * 0x00: NOT formatted -diff --git a/fdasd/fdasd.c b/fdasd/fdasd.c -index a526d7f..8f7f5aa 100644 ---- a/fdasd/fdasd.c -+++ b/fdasd/fdasd.c -@@ -2002,7 +2002,7 @@ fdasd_get_geometry (fdasd_anchor_t *anc) - int fd, blksize = 0; - dasd_information_t dasd_info; - char err_str[ERROR_STRING_SIZE]; -- u_int64_t device_size; -+ struct dasd_eckd_characteristics *characteristics; - - if ((fd = open(options.device,O_RDONLY)) < 0) { - snprintf(err_str, ERROR_STRING_SIZE, -@@ -2023,14 +2023,6 @@ fdasd_get_geometry (fdasd_anchor_t *anc) - "Could not retrieve blocksize information."); - } - -- if (ioctl(fd, BLKGETSIZE64, &device_size) != 0) { -- close(fd); -- fdasd_error(anc, unable_to_ioctl, -- "Could not retrieve device size information."); -- } -- -- anc->hw_cylinders = ((device_size / blksize) / geo.sectors) / geo.heads; -- - /* get disk type */ - if (ioctl(fd, BIODASDINFO, &dasd_info) != 0) { - close(fd); -@@ -2038,6 +2030,14 @@ fdasd_get_geometry (fdasd_anchor_t *anc) - "Could not retrieve disk information."); - } - -+ characteristics = -+ (struct dasd_eckd_characteristics *) &dasd_info.characteristics; -+ if (characteristics->no_cyl == LV_COMPAT_CYL && -+ characteristics->long_no_cyl) -+ anc->hw_cylinders = characteristics->long_no_cyl; -+ else -+ anc->hw_cylinders = characteristics->no_cyl; -+ - close(fd); - - if (strncmp(dasd_info.type, "ECKD", 4) != 0) { -diff --git a/fdasd/fdasd.h b/fdasd/fdasd.h -index 107e486..8a1bfd0 100644 ---- a/fdasd/fdasd.h -+++ b/fdasd/fdasd.h -@@ -43,6 +43,80 @@ typedef struct dasd_information_t { - char configuration_data[256]; /* from read_configuration_data */ - } dasd_information_t; - -+struct dasd_eckd_characteristics { -+ unsigned short cu_type; -+ struct { -+ unsigned char support:2; -+ unsigned char async:1; -+ unsigned char reserved:1; -+ unsigned char cache_info:1; -+ unsigned char model:3; -+ } __attribute__ ((packed)) cu_model; -+ unsigned short dev_type; -+ unsigned char dev_model; -+ struct { -+ unsigned char mult_burst:1; -+ unsigned char RT_in_LR:1; -+ unsigned char reserved1:1; -+ unsigned char RD_IN_LR:1; -+ unsigned char reserved2:4; -+ unsigned char reserved3:8; -+ unsigned char defect_wr:1; -+ unsigned char XRC_supported:1; -+ unsigned char reserved4:1; -+ unsigned char striping:1; -+ unsigned char reserved5:4; -+ unsigned char cfw:1; -+ unsigned char reserved6:2; -+ unsigned char cache:1; -+ unsigned char dual_copy:1; -+ unsigned char dfw:1; -+ unsigned char reset_alleg:1; -+ unsigned char sense_down:1; -+ } __attribute__ ((packed)) facilities; -+ unsigned char dev_class; -+ unsigned char unit_type; -+ unsigned short no_cyl; -+ unsigned short trk_per_cyl; -+ unsigned char sec_per_trk; -+ unsigned char byte_per_track[3]; -+ unsigned short home_bytes; -+ unsigned char formula; -+ union { -+ struct { -+ unsigned char f1; -+ unsigned short f2; -+ unsigned short f3; -+ } __attribute__ ((packed)) f_0x01; -+ struct { -+ unsigned char f1; -+ unsigned char f2; -+ unsigned char f3; -+ unsigned char f4; -+ unsigned char f5; -+ } __attribute__ ((packed)) f_0x02; -+ } __attribute__ ((packed)) factors; -+ unsigned short first_alt_trk; -+ unsigned short no_alt_trk; -+ unsigned short first_dia_trk; -+ unsigned short no_dia_trk; -+ unsigned short first_sup_trk; -+ unsigned short no_sup_trk; -+ unsigned char MDR_ID; -+ unsigned char OBR_ID; -+ unsigned char director; -+ unsigned char rd_trk_set; -+ unsigned short max_rec_zero; -+ unsigned char reserved1; -+ unsigned char RWANY_in_LR; -+ unsigned char factor6; -+ unsigned char factor7; -+ unsigned char factor8; -+ unsigned char reserved2[3]; -+ unsigned char reserved3[6]; -+ unsigned int long_no_cyl; -+} __attribute__ ((packed)); -+ - /* Get information on a dasd device (enhanced) */ - #define BIODASDINFO _IOR(DASD_IOCTL_LETTER,1,dasd_information_t) - --- -1.6.3.3 - diff --git a/0015-s390tools-1.8.2-zipl-dm.patch b/0015-s390tools-1.8.2-zipl-dm.patch deleted file mode 100644 index 306006d..0000000 --- a/0015-s390tools-1.8.2-zipl-dm.patch +++ /dev/null @@ -1,2716 +0,0 @@ -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 deleted file mode 100644 index e2b3413..0000000 --- a/0016-s390tools-1.8.2-lsreipl-nss.patch +++ /dev/null @@ -1,376 +0,0 @@ -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 deleted file mode 100644 index bc325c0..0000000 --- a/0017-qualified-return-codes-and-further-error-handling-in.patch +++ /dev/null @@ -1,1215 +0,0 @@ -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/0018-fix-uppercase-conversion-in-lscss.patch b/0018-fix-uppercase-conversion-in-lscss.patch deleted file mode 100644 index 9c5b945..0000000 --- a/0018-fix-uppercase-conversion-in-lscss.patch +++ /dev/null @@ -1,38 +0,0 @@ -From a8bcc744ec941c3b34c22c7a7729b52a390c7a08 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Wed, 13 Jan 2010 08:39:09 +0100 -Subject: [PATCH] fix uppercase conversion in lscss - -Description: lscss: fix uppercase conversion -Symptom: when invoking the -u option, lscss will fail with the message "tr: missing operand" -Problem: the bash option "nullglob" interferes with the tr operands -Solution: avoid expanding of those operands by placing quotes around them ---- - zconf/lscss | 4 ++-- - 1 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/zconf/lscss b/zconf/lscss -index efdb9ca..fc40fe6 100755 ---- a/zconf/lscss -+++ b/zconf/lscss -@@ -354,7 +354,7 @@ if [ $SCH_IO ] ;then - echo "----------------------------------------------------------------------" - fi - print_io | if [ $UPPERCASE ] ;then -- tr [:lower:] [:upper:] -+ tr "[:lower:]" "[:upper:]" - else - cat - - fi -@@ -368,7 +368,7 @@ if [ $SCH_CHSC ] ;then - echo "Device Subchan." - echo "-----------------" - print_chsc | if [ $UPPERCASE ] ;then -- tr [:lower:] [:upper:] -+ tr "[:lower:]" "[:upper:]" - else - cat - - fi --- -1.6.5.2 - diff --git a/0019-ziorep-fix-return-codes.patch b/0019-ziorep-fix-return-codes.patch deleted file mode 100644 index f2037a0..0000000 --- a/0019-ziorep-fix-return-codes.patch +++ /dev/null @@ -1,53 +0,0 @@ -From a8ffd7242da2adcc4ab5b0f23193cfe764dc376d Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Wed, 20 Jan 2010 08:42:52 +0100 -Subject: [PATCH 19/22] ziorep: fix return codes - -Description: ziomon: ziorep tools return 1 when using options -h and -v -Symptom: Using ziorep_traffic/utilization with these options give an -error. -Problem: 1 as been introduced as rc for parse_parms besides error codes, -but is not distinguished from them when parse_params is called. -Solution: After calling parse_parms check for rc 1 and substitute through 0. ---- - ziomon/ziorep_traffic.cpp | 5 ++++- - ziomon/ziorep_utilization.cpp | 5 ++++- - 2 files changed, 8 insertions(+), 2 deletions(-) - -diff --git a/ziomon/ziorep_traffic.cpp b/ziomon/ziorep_traffic.cpp -index 1461e55..20ee32f 100644 ---- a/ziomon/ziorep_traffic.cpp -+++ b/ziomon/ziorep_traffic.cpp -@@ -529,8 +529,11 @@ int main(int argc, char **argv) - verbose = 0; - - init_opts(&opts); -- if ( (rc = parse_params(argc, argv, &opts)) ) -+ if ( (rc = parse_params(argc, argv, &opts)) ) { -+ if (rc == 1) -+ rc = 0; - goto out; -+ } - if ( (rc = check_opts(&opts, &cfg)) ) - goto out; - -diff --git a/ziomon/ziorep_utilization.cpp b/ziomon/ziorep_utilization.cpp -index 3f57a47..ac4576f 100644 ---- a/ziomon/ziorep_utilization.cpp -+++ b/ziomon/ziorep_utilization.cpp -@@ -352,8 +352,11 @@ int main(int argc, char **argv) - verbose = 0; - - init_opts(&opts); -- if ( (rc = parse_params(argc, argv, &opts)) ) -+ if ( (rc = parse_params(argc, argv, &opts)) ) { -+ if (rc == 1) -+ rc = 0; - goto out; -+ } - if ( (rc = check_opts(&opts, &cfg)) ) - goto out; - --- -1.6.5.2 - diff --git a/0020-lstape-fix-return-code.patch b/0020-lstape-fix-return-code.patch deleted file mode 100644 index 77aed72..0000000 --- a/0020-lstape-fix-return-code.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 72ec5b5bd9a7fd23e2d1aaed455df8f2bda952ca Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Wed, 20 Jan 2010 08:44:43 +0100 -Subject: [PATCH 20/22] lstape: fix return code - -Description: lstape: help function returns 1, although it was successful -Symptom: Issuing "lstape -h" or "lstape --help" and then looking for the -error code on the console through e.g. "echo $?" shows 1. Which -looks like an error. -Problem: exit 1, where we chould have exit 0. -Solution: Return 0 after calling the help function with exit 0. ---- - zconf/lstape | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/zconf/lstape b/zconf/lstape -index efacfc9..5671315 100755 ---- a/zconf/lstape -+++ b/zconf/lstape -@@ -90,7 +90,7 @@ while [ $# -ne 0 ]; do - case $1 in - -h|--help) - PrintUsage -- exit 1 -+ exit 0 - ;; - -t|--type) - if [ $# -lt 2 ]; then --- -1.6.5.2 - diff --git a/0021-cpuplugd-fix-reading-the-size-of-proc-sys-vm-cmm_pag.patch b/0021-cpuplugd-fix-reading-the-size-of-proc-sys-vm-cmm_pag.patch deleted file mode 100644 index 9fb5604..0000000 --- a/0021-cpuplugd-fix-reading-the-size-of-proc-sys-vm-cmm_pag.patch +++ /dev/null @@ -1,32 +0,0 @@ -From d3c4eb2ced3a8e3c8f7a528f88721324028534e9 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Wed, 20 Jan 2010 08:46:14 +0100 -Subject: [PATCH 21/22] cpuplugd: fix reading the size of /proc/sys/vm/cmm_pages - -Description: cpuplugd: fix fscanf file handling in get_cmmpages_size -Symptom: cpuplugd was not able to correctly read the size of the -/proc/sys/vm/cmm_pages file. This prevented cpuplugd from using -the cmm feature correctly. -Problem: The code checked for the wrong return code (!= 0 instead of ==0) -Solution: Check for the right return code in the get_cmmpages_size error -handling function. ---- - cpuplugd/mem.c | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/cpuplugd/mem.c b/cpuplugd/mem.c -index a8f2a8e..8d5f05f 100644 ---- a/cpuplugd/mem.c -+++ b/cpuplugd/mem.c -@@ -230,7 +230,7 @@ int get_cmmpages_size() - return -1; - } - rc = fscanf(filp, "%d", &size); -- if (rc != 0) { -+ if (rc == 0) { - fprintf(stderr, "Can not read /proc/sys/vm/cmm_pages: %s\n", - strerror(errno)); - if (foreground == 0) --- -1.6.5.2 - diff --git a/0022-lsqeth-support-new-attributes.patch b/0022-lsqeth-support-new-attributes.patch deleted file mode 100644 index 6556adc..0000000 --- a/0022-lsqeth-support-new-attributes.patch +++ /dev/null @@ -1,74 +0,0 @@ -From c986172e782ae2afa41209e7c238315175d589e1 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Wed, 20 Jan 2010 08:48:52 +0100 -Subject: [PATCH 22/22] lsqeth: support new attributes - -Description: lsqeth: add recent qeth attributes -Symptom: no information about "isolation" and "sniffer" values -Problem: new sysfs attributes "isolation" and "sniffer" not mentioned in -lsqeth output. -Solution: add attributes "isolation" and "sniffer" to lsqeth. ---- - README | 2 ++ - zconf/lsqeth | 11 +++++++---- - 2 files changed, 9 insertions(+), 4 deletions(-) - -diff --git a/README b/README -index 30ba06f..7c75563 100644 ---- a/README -+++ b/README -@@ -192,6 +192,8 @@ Release History: - - - lscss: Also show devices on the defunct subchannel. - -+ - lsqeth: add attributes "isolation" and "sniffer" -+ - - all: Reworked s390-tools build process. - - Bug Fixes: -diff --git a/zconf/lsqeth b/zconf/lsqeth -index 9b155e9..b1c2809 100755 ---- a/zconf/lsqeth -+++ b/zconf/lsqeth -@@ -3,7 +3,7 @@ - # File...........: lsqeth - # Author(s)......: Steffen Thoss - # Peter Tiedemann --# Copyright IBM Corp. 2004, 2009 -+# Copyright IBM Corp. 2004, 2010 - # - # History of changes: - # -@@ -63,7 +63,9 @@ output_array=( if_name - buffer_count - add_hhlen - layer2 -- large_send ) -+ large_send -+ isolation -+ sniffer ) - # Array for sysfs values - sysfs_entries=( "sw checksumming" "hw checksumming" "no checksumming" - "always queue 0" "always queue 1" -@@ -87,7 +89,7 @@ parp_array=0 - function PrintVersion - { - echo "$script_name: version %S390_TOOLS_VERSION%" -- echo "Copyright IBM Corp. 2003, 2009" -+ echo "Copyright IBM Corp. 2003, 2010" - } - - -@@ -308,7 +310,8 @@ function __print_normal_format - function __layer2_print - { - del_layer2=( route4 route6 -- fake_ll fake_broadcast ) -+ fake_ll fake_broadcast -+ sniffer ) - - for l in ${del_layer2[@]} - do --- -1.6.5.2 - diff --git a/0023-znetconf-use-hex-index-for-chpidtype-table.patch b/0023-znetconf-use-hex-index-for-chpidtype-table.patch deleted file mode 100644 index 6fed2eb..0000000 --- a/0023-znetconf-use-hex-index-for-chpidtype-table.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 3ca0d01240ab14ab3217985bf8ed9c89111e36c9 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Wed, 3 Feb 2010 13:17:41 +0100 -Subject: [PATCH] znetconf: use hex index for chpidtype table - -Description: znetconf: index into chpidtype lookup table must be hex. -Symptom: Network subchannels with CHPID type containing non-decimal digits, - i.e. a-f, cause lsznet.raw errors like the following: - 1a: value too great for base (error token is "1a") -Problem: The value of the sysfs attribute "type" of a CHPID is hex, - but the lookup table index in the bash script must be decimal. -Solution: Interpret "type" as hex number when used as lookup table index. ---- - zconf/lsznet.raw | 10 +++++----- - 1 files changed, 5 insertions(+), 5 deletions(-) - -diff --git a/zconf/lsznet.raw b/zconf/lsznet.raw -index e5643ee..9821ba3 100755 ---- a/zconf/lsznet.raw -+++ b/zconf/lsznet.raw -@@ -68,10 +68,10 @@ readonly -a CU_GROUPCHANNELS=( - ) - - readonly -a CHPIDTYPES=( -- [10]=OSE -- [11]=OSD -- [15]=OSN -- [24]=IQD -+ [0x10]=OSE -+ [0x11]=OSD -+ [0x15]=OSN -+ [0x24]=IQD - ) - - # whitelist of network devices for TCP/IP stack, e.g. for Linux installers -@@ -116,7 +116,7 @@ function search_cu_tcpip() { - # Always succeeds and returns 0. - function search_chpt() { - local chpidtype_number=$1 -- chpidtype_symbolic=${CHPIDTYPES[$chpidtype_number]} -+ chpidtype_symbolic=${CHPIDTYPES[$((0x$chpidtype_number))]} - if [ "$chpidtype_symbolic" == "" ]; then - chpidtype_symbolic="?" - fi --- -1.6.6 - diff --git a/0024-zipl-handle-SSCH-status.patch b/0024-zipl-handle-SSCH-status.patch deleted file mode 100644 index 3b1b54f..0000000 --- a/0024-zipl-handle-SSCH-status.patch +++ /dev/null @@ -1,230 +0,0 @@ -From 9c94d906621e775f005fa34583671f08000f1723 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Thu, 4 Feb 2010 13:07:30 +0100 -Subject: [PATCH] zipl: handle SSCH status - -Description: zipl: handle status during IPL SSCH -Symptom: System enters disabled wait during IPL of DASD disk when an - unexpected status (e.g. triggered by a flashcopy operation) - is recognized on the IPL device. -Problem: Unexpected status is not handled correctly during the SSCH - portion of the IPL code. -Solution: Introduce a more robust SSCH result handling which performs - retries and clears status when encountering errors. ---- - zipl/boot/common.S | 163 +++++++++++++++++++++++++++++++++++----------------- - zipl/boot/eckd2.S | 1 - - zipl/boot/fba2.S | 1 - - 3 files changed, 110 insertions(+), 55 deletions(-) - -diff --git a/zipl/boot/common.S b/zipl/boot/common.S -index fa45e5a..108dbbf 100644 ---- a/zipl/boot/common.S -+++ b/zipl/boot/common.S -@@ -249,69 +249,126 @@ _disable_device: - .endm - - .macro io_subroutines -+ -+ -+__LC_IO_NEW_PSW = 0x78 -+__LC_SUBCHANNEL_ID = 0xb8 -+ -+# -+# Wait for I/O interrupt. -+# -+# Wait until status for the specified subchannel is available. -+# -+# %r2 : subchannel id -+# %r3 : address of irb -+# -+ -+_wait_for_int: -+ lr %r1,%r2 -+ basr %r4,0 # get base register -+0: -+ mvc __LC_IO_NEW_PSW(8),4f-0b(%r4) # set i/o new psw -+1: # wait -+ lpsw 3f-0b(%r4) -+2: # continue -+ tsch 0(%r3) # get status -+ brc 4,1b # if cc=1 goto wait -+ br %r14 # return -+ .align 8 -+3: -+ .long 0x020a0000,0x80000000+1b # enabled wait psw -+4: -+ .long 0x00080000,0x80000000+2b # io new psw -+ - # - # Start I/O --# %r2 : device subchannel id -+# -+# Attempt to start I/O defined by ORB on specified subchannel. Retry I/O -+# 256 times per path (recommended error recovery procedure for IFCCs) unless -+# a permanent path error condition is indicated. Try all paths twice to try to -+# work around "link flapping" (paths going down once each in the same order as -+# they are tried). Perform CLEAR SUBCHANNEL when switching paths to clear any -+# improper subchannel status. -+# -+# %r2 : subchannel id - # %r3 : address of orb - # %r4 : address of irb --# %r5 : retry count - # - _ssch: -- stm %r6,%r15,24(%r15) -- basr %r13,0 # base register --0: s %r15,6f-0b(%r13) # create stack frame -- lr %r12,%r2 # save subchannel id -- lr %r11,%r3 # save orb -- lr %r10,%r4 # save irb -- lr %r9,%r5 # save retry count -- ic %r0,.Llpm-0b(%r13) # copy lpm to orb lpm -- stc %r0,6(%r3) --1: lr %r1,%r12 -- ssch 0(%r11) # go -- bnz 4f-0b(%r13) # houston, we have a problem --2: lr %r2,%r12 # call _wait4de with subchannel id -- lr %r3,%r10 # and irb address as parameters -- bas %r14,_wait4de-0b(%r13) # wait until DE or error -- tm 9(%r10),0xff # test channel status -- bnz 4f-0b(%r13) -- tm 8(%r10),0xf3 # test device status -- bz 5f-0b(%r13) -- bct %r9,1b-0b(%r13) # something went wrong, retry. --4: l %r2,7f-0b(%r13) -- bas %r4,_panik-0b(%r13) # won't return --5: lm %r6,%r15,120(%r15) -- br %r14 --6: .long 96 --7: .long ESSCH -+ stm %r6,%r15,24(%r15) -+ basr %r13,0 # get base register -+0: -+ ahi %r15,-96 # create stack frame -+ lr %r6,%r2 # r6: sid -+ lr %r7,%r3 # r7: orb -+ lr %r8,%r4 # r8: irb -+ sr %r9,%r9 # r9: initial lpm -+ ic %r9,.Llpm-0b(%r13) -+ l %r10,.Lmask-0b(%r13) # r10: path mask -+1: # restart_all -+ lhi %r11,256 # r11: retry counter -+2: # restart -+ stc %r9,6(%r7) # store initial lpm in orb -+ ltr %r9,%r9 # if non-zero initial lpm -+ jne 3f # then use initial lpm -+ stc %r10,6(%r7) # else use current path mask -+3: # gotlpm -+ lr %r1,%r6 # get sid -+ ssch 0(%r7) # start subchannel -+ brc 1,7f # if cc==3 goto next_path -+ brc 7,6f # if cc!=0 goto retry -+4: # wait_for_int_loop -+ lr %r2,%r6 # get sid -+ lr %r3,%r8 # get irb -+ bras %r14,_wait_for_int # wait for interrupt -+ jnz 9f # if cc!=0 goto panic -+ tm 0(%r8),0x3 # test irb deferred cc -+ brc 1,7f # if cc==3 goto next_path -+ jz 5f # if cc==0 goto no_stctl_check -+ tm 3(%r8),0x10 # test irb status control -+ jnz 6f # if alert goto retry -+5: # no_stctl_check -+ tm 9(%r8),0xff # test irb subchannel status -+ jnz 6f # if status!=0 goto retry -+ tm 8(%r8),0xf3 # test irb unusual device status -+ jnz 6f # if status!=0 goto retry -+ tm 8(%r8),0x4 # test irb device end -+ jz 4b # if !device_end goto wait_for_int_loop -+ lm %r6,%r15,120(%r15) -+ br %r14 # return -+ -+6: # retry -+ lr %r1,%r6 # get sid -+ tsch 0(%r8) # clear status if necessary -+ brct %r11,2b # if --retries>0 goto restart -+7: # next_path -+ ltr %r9,%r9 # if initial lpm != 0 -+ jnz 8f # then goto noshift -+ srl %r10,1 # path_mask >>= 1 -+ ltr %r10,%r10 # if path_mask==0 -+ jz 9f # then goto panic -+8: # noshift -+ sr %r9,%r9 # clear initial lpm -+ lr %r1,%r6 # get sid -+ csch # clear subchannel -+ brc 7,9f # if cc!=0 goto panic -+ lr %r2,%r6 # get sid -+ lr %r3,%r8 # get irb -+ bras %r14,_wait_for_int # wait for interrupt -+ j 1b # goto restart_all -+9: # panic -+ l %r2,.Lerrno-0b(%r13) # get error code -+ bras %r14,_panik # panic -+ -+.Lerrno: -+ .long ESSCH -+.Lmask: -+ .long 0x8080 - .Llpm: -- .byte 0xff -+ .byte 0x00 - .align 2 - - # --# Wait for interrupt subroutine --# %r2 : device subchannel id --# %r3 : address of irb --# --_wait4de: -- lr %r1,%r2 -- basr %r4,0 --0: mvc 0x78(8),5f-0b(%r4) # set i/o new psw --1: lpsw 4f-0b(%r4) --2: c %r1,0xb8 # compare subchannel id -- bne 1b-0b(%r4) # unequal -> continue waiting -- tsch 0(%r3) -- tm 9(%r3),0xff # test channel status -- bnz 3f-0b(%r4) -- tm 8(%r3),0xf3 # got something unusual ? -- bnz 3f-0b(%r4) -- tm 8(%r3),0x04 # got device end ? -- bz 1b-0b(%r4) # still busy -> continue waiting --3: br %r14 -- .align 8 --4: .long 0x020a0000,0x80000000+1b --5: .long 0x00080000,0x80000000+2b # io new psw -- --# - # Panik routine. Loads a disabled wait psw - # %r2 : panik code - # -diff --git a/zipl/boot/eckd2.S b/zipl/boot/eckd2.S -index ba71db9..b59ab0e 100644 ---- a/zipl/boot/eckd2.S -+++ b/zipl/boot/eckd2.S -@@ -85,7 +85,6 @@ _load_direct: - la %r3,.Lorb-.Lbase(%r13) # pass address of orb - la %r4,.Lirb-.Lbase(%r13) # and pass address of irb - lr %r2,%r11 # pass subchannel id -- la %r5,5 # 5 retries - bas %r14,_ssch-.Lbase(%r13) # read records - .Lexit: - lr %r2,%r12 # return updated load address -diff --git a/zipl/boot/fba2.S b/zipl/boot/fba2.S -index 82b1447..90bb2cd 100644 ---- a/zipl/boot/fba2.S -+++ b/zipl/boot/fba2.S -@@ -98,7 +98,6 @@ _load_direct: - lr %r2,%r11 # pass subchannel id - la %r3,.Lorb-.Lbase(%r13) # pass address of orb - la %r4,.Lirb-.Lbase(%r13) # and pass address of irb -- la %r5,5 # 5 retries - bas %r14,_ssch-.Lbase(%r13) # read up to 128 blocks - b .Lmain-.Lbase(%r13) - .Lexit: lr %r2,%r12 # return updated load address --- -1.6.6 - diff --git a/0025-vmconvert-shows-garbage-in-progress-bar.patch b/0025-vmconvert-shows-garbage-in-progress-bar.patch deleted file mode 100644 index 7c13c59..0000000 --- a/0025-vmconvert-shows-garbage-in-progress-bar.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 04859d2270d1327873e1e8825bdd9d0eaf73f5cc Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Wed, 10 Mar 2010 11:52:18 +0100 -Subject: [PATCH] vmconvert shows garbage in progress bar - -Description: vmconvert: Progress bar shows garbage -Symptom: The progress bar shows control characters at end of line. -Problem: One variable responsible for the progress bar has not been -initialized. -Solution: Initialize variable. ---- - vmconvert/dump.cpp | 3 ++- - 1 files changed, 2 insertions(+), 1 deletions(-) - -diff --git a/vmconvert/dump.cpp b/vmconvert/dump.cpp -index f306b27..b4c97d6 100644 ---- a/vmconvert/dump.cpp -+++ b/vmconvert/dump.cpp -@@ -46,8 +46,8 @@ ProgressBar::initProgress(void) - void - ProgressBar::displayProgress(uint64_t value, uint64_t maxValue) - { -+ char progress_bar[51]; - int j; -- char progress_bar[50]; - - if (progressPercentage == (int) (value * 100 / maxValue)) - fprintf(stderr, "%6lld of %6lld |\r", -@@ -58,6 +58,7 @@ ProgressBar::displayProgress(uint64_t value, uint64_t maxValue) - progress_bar[j] = '#'; - for (j = progressPercentage / 2; j < 50; j++) - progress_bar[j] = '-'; -+ progress_bar[50] = 0; - fprintf(stderr, "%6lld of %6lld |%s| %3d%% \r", - (long long) value, (long long) maxValue, - progress_bar, progressPercentage); --- -1.6.6.1 - diff --git a/0026-zipl-zfcp-dump-partition-error.patch b/0026-zipl-zfcp-dump-partition-error.patch deleted file mode 100644 index 5864c85..0000000 --- a/0026-zipl-zfcp-dump-partition-error.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 18bc3ce1055c350aaa50696e53baad984f573697 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Thu, 11 Mar 2010 08:34:32 +0100 -Subject: [PATCH] zipl: zfcp dump partition error - -Description: zipl: zfcp dump partition error -Symptom: When configuring the zfcp dump partition in zipl.conf the -partition information may be wrong. -Problem: The info data structure is used after it was freed. -Solution: Free the info structure when it is not used any more. ---- - zipl/src/bootmap.c | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/zipl/src/bootmap.c b/zipl/src/bootmap.c -index 526aa48..043a562 100644 ---- a/zipl/src/bootmap.c -+++ b/zipl/src/bootmap.c -@@ -689,9 +689,9 @@ get_dump_fs_parmline(char* partition, char* parameters, uint64_t mem, - disk_free_info(info); - return -1; - } -- disk_free_info(info); - buffer = create_dump_fs_parmline(parameters, "/dev/ram0", info->partnum, - mem, 1); -+ disk_free_info(info); - if (buffer == NULL) - return -1; - *result = buffer; --- -1.6.6.1 - diff --git a/0027-zfcpdump-disable-memory-cgroups.patch b/0027-zfcpdump-disable-memory-cgroups.patch deleted file mode 100644 index 2a7db37..0000000 --- a/0027-zfcpdump-disable-memory-cgroups.patch +++ /dev/null @@ -1,34 +0,0 @@ -From cc672d10ef6ee3da060649fa595ed9d980e5e60e Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Mon, 22 Mar 2010 12:58:13 +0100 -Subject: [PATCH] zfcpdump - disable memory cgroups - -Description: zipl/zfcpdump: Use "cgroup_disable=memory" kernel parameter -Symptom: Out of memory kernel panic during SCSI dump. -Problem: The zfcpdump kernel has to run within a 32 MB limit. When - using "memory cgroups" about 2.6 MB are allocated, which leads - to memory problems for zfcpdump. -Solution: For zfcpdump we do not need "memory cgroups". Therefore this patch - adds the kernel parameter "cgroup_disable=memory" to save 2.5 MB - of memory. ---- - zipl/src/bootmap.c | 3 ++- - 1 files changed, 2 insertions(+), 1 deletions(-) - -diff --git a/zipl/src/bootmap.c b/zipl/src/bootmap.c -index 043a562..69f721e 100644 ---- a/zipl/src/bootmap.c -+++ b/zipl/src/bootmap.c -@@ -653,7 +653,8 @@ create_dump_fs_parmline(const char* parmline, const char* root_dev, - if (!result) - return NULL; - snprintf(result, DUMP_PARAM_MAX_LEN, "%s%sroot=%s dump_part=%d " -- "dump_mem=%lld maxcpus=%d", parmline ? parmline : "", -+ "dump_mem=%lld maxcpus=%d cgroup_disable=memory", -+ parmline ? parmline : "", - parmline ? " " : "", root_dev, part_num, - (unsigned long long) mem, max_cpus); - result[DUMP_PARAM_MAX_LEN - 1] = 0; --- -1.6.6.1 - diff --git a/0028-fix-df-usage-in-ziomon.patch b/0028-fix-df-usage-in-ziomon.patch deleted file mode 100644 index 24e7d4e..0000000 --- a/0028-fix-df-usage-in-ziomon.patch +++ /dev/null @@ -1,29 +0,0 @@ -From b0238ed360f65ecbf095d89c43c420ed036e4ff0 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Mon, 22 Mar 2010 15:21:55 +0100 -Subject: [PATCH] fix df usage in ziomon - -Description: ziomon: Fix 'df' command usage -Symptom: Long device names could not be parsed properly. -Problem: Long device names were split accross multiple lines. -Solution: Use df with the right options. ---- - ziomon/ziomon | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/ziomon/ziomon b/ziomon/ziomon -index 924c1dd..ea910fc 100755 ---- a/ziomon/ziomon -+++ b/ziomon/ziomon -@@ -709,7 +709,7 @@ function check_size_requirements() { - (( estimated_size=$total_num_records * $size_per_record )); - debug " estimated size: $estimated_size Bytes"; - (( estimated_size=$estimated_size / 1000000 )); -- (( free_space = `df -k $logpath | tail -n 1 | awk '{print $4}'` / 1024 )); -+ free_space=`df -mP $logpath | tail -n 1 | awk '{print $4}'`; - debug " free space on '$logpath': $free_space MBytes"; - if [ "$WRP_SIZE" == "" ]; then - echo "NOTE: No size limit specified, run without a limit."; --- -1.6.6.1 - diff --git a/0029-ziomon-remove-check-for-ziorep_config-availability.patch b/0029-ziomon-remove-check-for-ziorep_config-availability.patch deleted file mode 100644 index 2925582..0000000 --- a/0029-ziomon-remove-check-for-ziorep_config-availability.patch +++ /dev/null @@ -1,102 +0,0 @@ -From 2ec256f9dab30a57f60c83f7cd9b127c25b6cdd6 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Thu, 25 Mar 2010 14:11:31 +0100 -Subject: [PATCH] ziomon: remove check for ziorep_config availability - -Description: ziomon: Remove check for ziorep_config availability -Symptom: ziorep_config could not be found -Problem: The path for ziorep_config was hardcoded and checked - for to detect install errors. However, as distributions - install these files in varying directories, install issues - should be avoided by distributors to begin with, and - especially since the debug trace helps to detect these, - this check is not really necessary and obviously causing - more harm than it helps. -Solution: Config is read from PATH. ---- - ziomon/ziorep_cfgreader.cpp | 24 +++++------------------- - ziomon/ziorep_cfgreader.hpp | 2 -- - 2 files changed, 5 insertions(+), 21 deletions(-) - -diff --git a/ziomon/ziorep_cfgreader.cpp b/ziomon/ziorep_cfgreader.cpp -index c64c150..a8f424f 100644 ---- a/ziomon/ziorep_cfgreader.cpp -+++ b/ziomon/ziorep_cfgreader.cpp -@@ -18,7 +18,7 @@ - #include "ziorep_utils.hpp" - - #define ZIOREP_CFG_EXTENSION ".cfg" --#define ZIOREP_CONFIG_PATH "/sbin/ziorep_config" -+#define ZIOREP_CONFIG "ziorep_config" - - extern const char *toolname; - extern int verbose; -@@ -158,18 +158,6 @@ int ConfigReader::filter_unused_devices(const char *filename) - } - - --int ConfigReader::check_ziorep_config() const --{ -- if (access(ZIOREP_CONFIG_PATH, F_OK | R_OK | X_OK)) { -- fprintf(stderr, "%s: Cannot access " ZIOREP_CONFIG_PATH -- ". Please check your installation and try again.\n", toolname); -- return -1; -- } -- -- return 0; --} -- -- - int ConfigReader::check_config_file(const char *fname) const - { - char *tmp; -@@ -204,8 +192,6 @@ int ConfigReader::extract_config_data(const char *fname) - - // Try to extract to .config first, which will be permanently cached - verbose_msg("No data cached, extract\n"); -- if (check_ziorep_config()) -- return -1; - - fprintf(stdout, "Extracting config data..."); - fflush(stdout); -@@ -215,8 +201,8 @@ int ConfigReader::extract_config_data(const char *fname) - if (extract_tmp(fname)) { - fprintf(stderr, "%s: Could not extract" - " configuration data. Check the integrity of" -- " %s%s with %s and retry.", toolname, fname, -- ZIOREP_CFG_EXTENSION, ZIOREP_CONFIG_PATH); -+ " %s%s with %s and retry.\n", toolname, fname, -+ ZIOREP_CFG_EXTENSION, ZIOREP_CONFIG); - return -1; - } - } -@@ -286,11 +272,11 @@ int ConfigReader::extract(const char *fname) - char *cmd = NULL; - - // /sbin/ziorep_config -I -i > 2>/dev/null -- cmd = (char*)malloc(strlen(ZIOREP_CONFIG_PATH) + 7 + strlen(fname) -+ cmd = (char*)malloc(strlen(ZIOREP_CONFIG) + 7 + strlen(fname) - + strlen(ZIOREP_CFG_EXTENSION) + 3 + strlen(m_tmp_file) - + 12 + 1); - -- sprintf(cmd, "%s -I -i %s%s > %s 2>/dev/null", ZIOREP_CONFIG_PATH, fname, -+ sprintf(cmd, "%s -I -i %s%s > %s 2>/dev/null", ZIOREP_CONFIG, fname, - ZIOREP_CFG_EXTENSION, m_tmp_file); - - verbose_msg("Issue command: %s\n", cmd); -diff --git a/ziomon/ziorep_cfgreader.hpp b/ziomon/ziorep_cfgreader.hpp -index 94e612b..3053633 100644 ---- a/ziomon/ziorep_cfgreader.hpp -+++ b/ziomon/ziorep_cfgreader.hpp -@@ -138,8 +138,6 @@ private: - * in the actual data, and remove anything that is unused */ - int filter_unused_devices(const char *filename); - -- int check_ziorep_config() const; -- - int check_config_file(const char *fname) const; - - int extract_config_data(const char *fname); --- -1.6.6.1 - diff --git a/0030-ziomon-fix-multipathing.patch b/0030-ziomon-fix-multipathing.patch deleted file mode 100644 index f636f19..0000000 --- a/0030-ziomon-fix-multipathing.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 3aa5fa943cea0f01beb63c90a5b0255a797ac117 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Wed, 31 Mar 2010 10:19:50 +0200 -Subject: [PATCH] ziomon - fix multipathing - -Description: ziomon: Fix problem with multipath command output -Symptom: Number of LUNs does not mach number of devices: E.g. 12 devices - and 10 LUNs. -Problem: Some version of the multipath command use characters that can - break parsing of its output. -Solution: Use proper parsing of the multipath -l output. ---- - ziomon/ziomon | 16 ++++++++-------- - 1 files changed, 8 insertions(+), 8 deletions(-) - -diff --git a/ziomon/ziomon b/ziomon/ziomon -index ea910fc..b4c6e36 100755 ---- a/ziomon/ziomon -+++ b/ziomon/ziomon -@@ -499,15 +499,15 @@ function check_for_multipath_devices() { - WRP_DEVICES[$j]=""; - devices_basenames[$j]=""; - clean_devices; -- (( i+=3 )); -+ (( i+=2 )); - while [[ ! "${mp_arr[$i]:0:1}" =~ "[0-9a-zA-Z]" ]] && [ $i -lt ${#mp_arr[@]} ]; do -- checked_devs[${#checked_devs[@]}]=`echo ${mp_arr[$i]} | awk '{print "/dev/"$3}'`; -- ddebug " add ${checked_devs[${#checked_devs[@]}-1]}"; -- line=${mp_arr[$i]#* }; -- line=${line%%:*}; -- line=`echo $line | sed 's/ //g'`; -- WRP_HOST_ADAPTERS[${#WRP_HOST_ADAPTERS[@]}]="host$line"; -- WRP_LUNS[${#WRP_LUNS[@]}]=`echo ${mp_arr[$i]#* } | awk '{print $1}'`; -+ if [ `echo ${mp_arr[$i]} | grep -e "[0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}" | wc -l` -ne 0 ]; then -+ line="`echo ${mp_arr[$i]} | sed 's/.*\([0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}\)/\1/'`"; -+ checked_devs[${#checked_devs[@]}]=`echo $line | awk '{print "/dev/"$2}'`; -+ ddebug " add ${checked_devs[${#checked_devs[@]}-1]}"; -+ WRP_HOST_ADAPTERS[${#WRP_HOST_ADAPTERS[@]}]="host${line%%:*}"; -+ WRP_LUNS[${#WRP_LUNS[@]}]=`echo $line | awk '{print $1}'`; -+ fi - (( i++ )); - done; - (( --i )); --- -1.6.6.1 - diff --git a/0031-mismatch-between-man-and-h-in-chshut.patch b/0031-mismatch-between-man-and-h-in-chshut.patch deleted file mode 100644 index 8f8cddd..0000000 --- a/0031-mismatch-between-man-and-h-in-chshut.patch +++ /dev/null @@ -1,66 +0,0 @@ -From 37e697ae95b3c03f962ac7bb2dd591f00b6de87b Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Wed, 28 Apr 2010 10:24:41 +0200 -Subject: [PATCH] mismatch between man and -h in chshut - -Description: chshut: Mismatch between man and -h -Symptom: Option -h stills show "panic" as a valid state. -Problem: Although it should not be used it is possible to use the - command with a "panic" state. -Solution: Remove "panic" from the list of valid states. - Provide an explanatory message in case it is used anyhow. ---- - ipl_tools/getopt.c | 11 ----------- - ipl_tools/sa.c | 7 +++++-- - 2 files changed, 5 insertions(+), 13 deletions(-) - -diff --git a/ipl_tools/getopt.c b/ipl_tools/getopt.c -index abd0cc2..d729379 100644 ---- a/ipl_tools/getopt.c -+++ b/ipl_tools/getopt.c -@@ -60,7 +60,6 @@ const char *const usage_sa = - " halt System has been shut down (e.g. shutdown -h -H now)\n" - " poff System has been shut down for power off (e.g. shutdown -h -P now)\n" - " reboot System has been shut down for reboot (e.g. shutdown -r)\n" --" panic System has been shut down after a kernel panic\n" - " Note: Depending on the distribution, \"halt\" might be mapped to \"poff\".\n" - "\n" - "ACTION specifies the action to be performed:\n" -@@ -638,16 +637,6 @@ void parse_shutdown_options(int argc, char **argv) - "/sys/firmware/vmcmd/on_reboot"); - } - break; -- case 3: -- rc = strwrt(argv[2], -- "/sys/firmware/shutdown_actions/on_panic"); -- if (action == 4 && rc == 0) { -- /*rc = ustrwrt(argv[3], -- "/sys/firmware/vmcmd/on_reboot"); */ -- rc = ustrwrt(argv[3], -- "/sys/firmware/vmcmd/on_panic"); -- } -- break; - } - if (rc != 0) { - fprintf(stderr, "%s: Failed to change shutdown" -diff --git a/ipl_tools/sa.c b/ipl_tools/sa.c -index 0232a47..d6b72bb 100644 ---- a/ipl_tools/sa.c -+++ b/ipl_tools/sa.c -@@ -83,8 +83,11 @@ int is_valid_case(char *action) - rc = 1; - if (strncmp(action, "reboot", 6) >= 0 && length == 6) - rc = 2; -- if (strncmp(action, "panic", 5) >= 0 && length == 5) -- rc = 3; -+ if (strncmp(action, "panic", 5) >= 0 && length == 5) { -+ fprintf(stderr, "%s: Please use \"service dumpconf\" for " -+ "configuring the panic action\n", name); -+ exit(1); -+ } - return rc; - } - --- -1.6.6.1 - diff --git a/0032-lsdasd-update-man-page.patch b/0032-lsdasd-update-man-page.patch deleted file mode 100644 index 9dab991..0000000 --- a/0032-lsdasd-update-man-page.patch +++ /dev/null @@ -1,48 +0,0 @@ -From c3fc4200d7913b50fb8b501681a0f44e51833a81 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Wed, 28 Apr 2010 20:57:17 +0200 -Subject: [PATCH] lsdasd - update man page - -Description: lsdasd: add missing description of option -b to man page -Symptom: Option -b/--base is described in lsdasd --help, but not in - the respective man page. -Problem: Man page is missing the description. -Solution: Add description. ---- - zconf/lsdasd.8 | 6 +++++- - 1 files changed, 5 insertions(+), 1 deletions(-) - -diff --git a/zconf/lsdasd.8 b/zconf/lsdasd.8 -index f37c957..9c13644 100644 ---- a/zconf/lsdasd.8 -+++ b/zconf/lsdasd.8 -@@ -10,6 +10,7 @@ lsdasd \- list channel attached direct access storage devices (DASD). - .TP 8 - .B lsdasd - .RB [ -a ] -+.RB [ -b ] - .RB [ -s ] - .RB [ -v ] - .RB [ -l ] -@@ -19,7 +20,7 @@ lsdasd \- list channel attached direct access storage devices (DASD). - .RI [ " [" "] ...]]" - - .SH DESCRIPTION --The lsdasd command provides an overview of available DASD devices. -+The lsdasd command provides an overview of available DASD devices. - - .SH OPTIONS - .TP 8 -@@ -32,6 +33,9 @@ Supresses leading "0.0." for bus IDs. - .BR -a | --offline - Include all (offline) devices. - .TP -+.BR -b | --base -+Include only base devices. -+.TP - .BR -c | --compat - Old output of lsdasd for compatibility. - .TP --- -1.6.6.1 - diff --git a/0033-reinitialize-array-in-lsqeth.patch b/0033-reinitialize-array-in-lsqeth.patch deleted file mode 100644 index 331d7e9..0000000 --- a/0033-reinitialize-array-in-lsqeth.patch +++ /dev/null @@ -1,79 +0,0 @@ -From edd35870b833548853b4c739cee1a7c0bce1fa55 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Mon, 3 May 2010 10:14:08 +0200 -Subject: [PATCH] reinitialize array in lsqeth - -Description: lsqeth: add clearing of print array for every qeth device -Symptom: lsqeth without parameters displays information of all qeth - devices. For layer2 devices information from a previously - displayed layer3 device may show up. -Problem: A structure used for printing is not cleared after - information for the previous device is written. -Solution: Add clearing of print array before collecting display - information for a single qeth device.. ---- - README | 3 +++ - zconf/lsqeth | 18 ++++++++++++++++-- - 2 files changed, 19 insertions(+), 2 deletions(-) - -diff --git a/README b/README -index 7c75563..ffd5e54 100644 ---- a/README -+++ b/README -@@ -253,6 +253,9 @@ Release History: - Fix printing of vmcmd shutdown action: Also print CP commands that - have more than one word. - -+ - lsqeth: -+ * clear print array for every device displayed -+ - - all: - Fixed a lot of build warnings and minor bugs. - -diff --git a/zconf/lsqeth b/zconf/lsqeth -index b1c2809..19d49cd 100755 ---- a/zconf/lsqeth -+++ b/zconf/lsqeth -@@ -168,6 +168,19 @@ fi - } - - -+# -+# initialize format_array_print -+# -+function __init_format_array_print -+{ -+ local k=0 -+ -+ while [ $k -lt ${#output_array[@]} ]; -+ do -+ format_array_print[$k]='' -+ k=$((k+1)) -+ done -+} - - # - # search value in format_array -@@ -309,9 +322,9 @@ function __print_normal_format - # - function __layer2_print - { -- del_layer2=( route4 route6 -+ del_layer2=( route4 route6 large_send - fake_ll fake_broadcast -- sniffer ) -+ checksumming sniffer ) - - for l in ${del_layer2[@]} - do -@@ -358,6 +371,7 @@ fi - # - for k in ${device_list} - do -+ __init_format_array_print - if_name="`cat $device_dir/$k/if_name`" - if [ -z "$if_name" ]; then - if_name=0 --- -1.6.6.1 - diff --git a/0034-check-the-length-of-the-parameters-line.patch b/0034-check-the-length-of-the-parameters-line.patch deleted file mode 100644 index 86107bc..0000000 --- a/0034-check-the-length-of-the-parameters-line.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 78a9e00a3a9885298f09079c026bf5415c137cca Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Thu, 20 May 2010 16:19:17 +0200 -Subject: [PATCH] check the length of the parameters line - -The value of the parameters variable in zipl.conf that is passed as to the kernel -into /proc/cmdline is silently truncated to 896 bytes (it matches the value of -BOOT_PARM_LENGTH from boot/menu.S). Thus we add a check into zipl and end with -an error if the line is longer. ---- - zipl/include/zipl.h | 3 +++ - zipl/src/job.c | 8 ++++++++ - 2 files changed, 11 insertions(+), 0 deletions(-) - -diff --git a/zipl/include/zipl.h b/zipl/include/zipl.h -index d2d26dd..5e02f13 100644 ---- a/zipl/include/zipl.h -+++ b/zipl/include/zipl.h -@@ -40,6 +40,9 @@ - - #define MAX_DUMP_VOLUMES 32 - -+/* defined in boot/menu.S as MENU_PARM_LENGTH */ -+#define PARMLINE_LENGTH 896 -+ - /* Internal component load address type */ - typedef uint64_t address_t; - -diff --git a/zipl/src/job.c b/zipl/src/job.c -index a65e8c1..76ed5df 100644 ---- a/zipl/src/job.c -+++ b/zipl/src/job.c -@@ -894,6 +894,14 @@ get_parmline(char* filename, char* line, char** parmline, address_t* address, - return -1; - - } else result = NULL; -+ /* check the maximum possible length */ -+ if (result) { -+ len = strlen(result); -+ if (len > PARMLINE_LENGTH) { -+ error_text("The length of parameters line (%d bytes) exceeds the allowed maximum (%d bytes)", len, PARMLINE_LENGTH); -+ return -1; -+ } -+ } - *parmline = result; - *address = addr; - return 0; --- -1.6.6.1 - diff --git a/0035-ziorep-follow-symlink.patch b/0035-ziorep-follow-symlink.patch deleted file mode 100644 index dbe821a..0000000 --- a/0035-ziorep-follow-symlink.patch +++ /dev/null @@ -1,28 +0,0 @@ -From fabce87ca9d586e6da7ed0bd371f2102408c721c Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 4 Jun 2010 12:05:19 +0200 -Subject: [PATCH 35/36] ziorep - follow symlink - -Description: ziomon: fix the execution of stat to follow symlinks. -Symptom: The device mapper report is failing with a script error. -Solution: Add parameter to stat execution to follow symlinks. ---- - ziomon/ziorep_config | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/ziomon/ziorep_config b/ziomon/ziorep_config -index de60379..f2ad35c 100755 ---- a/ziomon/ziorep_config -+++ b/ziomon/ziorep_config -@@ -159,7 +159,7 @@ sub get_mapper_devices - next; - } - my $tf = catfile($c_src, "$dev"); -- my $mm = `stat -c%t:%T $tf`; -+ my $mm = `stat -L -c%t:%T $tf`; - chomp($mm); - $mm = join(":", map { hex($_) } split(":", $mm)); - $mapper_dev{$mm} = $dev; --- -1.6.6.1 - diff --git a/0036-ts-shell-do-not-restrict-group-names-to-be-alphanume.patch b/0036-ts-shell-do-not-restrict-group-names-to-be-alphanume.patch deleted file mode 100644 index 7a42d73..0000000 --- a/0036-ts-shell-do-not-restrict-group-names-to-be-alphanume.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 379f76e4084a0d1875e1f98b2d975600639f6574 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 4 Jun 2010 12:06:38 +0200 -Subject: [PATCH 36/36] ts-shell: do not restrict group names to be alphanumeric - -Description: ts-shell: do not restrict group names to be alphanumeric -Symptom: ts-shell does not add authorizations assigned to groups - which contain non-alphanumeric characters, for instance, - a hyphen (-) sign. -Problem: ts-shell uses a regular expression to parse group names. - This regular expression uses the alphanumeric character - class (\w) to match group names. Using group names like - "ts-user" are ignored, because hyphen signs are not - included in the alphanumeric character class. -Solution: Make the regular expression less restrictive through - permitting all characters that are not whitespaces (\S). ---- - iucvterm/bin/ts-shell.in | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/iucvterm/bin/ts-shell.in b/iucvterm/bin/ts-shell.in -index 654d0c6..5fdf66c 100755 ---- a/iucvterm/bin/ts-shell.in -+++ b/iucvterm/bin/ts-shell.in -@@ -219,7 +219,7 @@ sub loadAuthorization(\%) - $authorized = 1; - log_debug "Found user: $key"; - -- } elsif ($key =~ /^@(\w+)$/) { -+ } elsif ($key =~ /^@(\S+)$/) { - my $group = $1; - $authorized = 1 if grep {/^${group}$/} @{$cfg->{groups}}; - log_debug "Found group: $key" if $authorized; --- -1.6.6.1 - diff --git a/0037-znetconf-unknown-driver-for-qeth.patch b/0037-znetconf-unknown-driver-for-qeth.patch deleted file mode 100644 index 97f2b5a..0000000 --- a/0037-znetconf-unknown-driver-for-qeth.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 41d606899d4029f8ac9f3415ebd21142cb223d70 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Tue, 22 Jun 2010 13:25:32 +0200 -Subject: [PATCH 37/40] znetconf: unknown driver for qeth - -Description: znetconf: --drive|-d option returning "unknown driver" for qeth -Symptom: qeth classified as unknown if specified for -d -Problem: bash reg. expression interpretation changed in new bash versions -Solution: used backward and "forward" compatible expression ---- - zconf/znetconf | 3 ++- - 1 files changed, 2 insertions(+), 1 deletions(-) - -diff --git a/zconf/znetconf b/zconf/znetconf -index 873124a..e1b48b3 100755 ---- a/zconf/znetconf -+++ b/zconf/znetconf -@@ -1031,7 +1031,8 @@ function is_shortccwdevbusid_list() - function is_supported_driver() - { - local DRIVER="$1" -- [[ "$DRIVER" =~ "^(qeth|lcs|ctc|ctcm)$" ]] -+ local DRVEXPR='^(qeth|lcs|ctc|ctcm)$' -+ [[ "$DRIVER" =~ $DRVEXPR ]] - case $? in - 0) - return 0 --- -1.6.6.1 - diff --git a/0038-cpuplugd-fix-stack-overflow.patch b/0038-cpuplugd-fix-stack-overflow.patch deleted file mode 100644 index 58084eb..0000000 --- a/0038-cpuplugd-fix-stack-overflow.patch +++ /dev/null @@ -1,90 +0,0 @@ -From b0192c18160e5ea35889794895becab99890bca3 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Tue, 22 Jun 2010 13:27:27 +0200 -Subject: [PATCH 38/40] cpuplugd: fix stack overflow - -Description: cpuplugd: fix stack overwrite -Symptom: cpuplugd will terminate with "stack smashing" error on systems - with more than 30 CPUs. -Problem: NULL termination of a read buffer will write beyond the buffer - if a previous read() filled out the whole buffer. -Solution: Only read max. buffer size - 1 bytes. ---- - cpuplugd/daemon.c | 2 +- - cpuplugd/main.c | 1 + - cpuplugd/mem.c | 4 ++-- - cpuplugd/vmstat.c | 4 ++-- - 4 files changed, 6 insertions(+), 5 deletions(-) - -diff --git a/cpuplugd/daemon.c b/cpuplugd/daemon.c -index f1af263..391acba 100644 ---- a/cpuplugd/daemon.c -+++ b/cpuplugd/daemon.c -@@ -287,7 +287,7 @@ int check_lpar() - , strerror(errno)); - clean_up(); - } -- bytes_read = fread(buffer, 1, sizeof(buffer), filp); -+ bytes_read = fread(buffer, 1, sizeof(buffer) - 1, filp); - if (bytes_read == 0) { - fprintf(stderr, "Reading /proc/cpuinfo failed:"); - fprintf(stderr, "%s\n", strerror(errno)); -diff --git a/cpuplugd/main.c b/cpuplugd/main.c -index c280342..f8f9dfa 100644 ---- a/cpuplugd/main.c -+++ b/cpuplugd/main.c -@@ -347,6 +347,7 @@ int main(int argc, char *argv[]) - syslog(LOG_INFO, "Out of memory: Aborting.\n"); - clean_up(); - } -+ memset(vs, 0, sizeof(struct vmstat)); - /* - * If the thread routine requires multiple arguments, they must be - * passed bundled up in an array or a structure -diff --git a/cpuplugd/mem.c b/cpuplugd/mem.c -index 8d5f05f..13f902d 100644 ---- a/cpuplugd/mem.c -+++ b/cpuplugd/mem.c -@@ -70,7 +70,7 @@ int get_vmstats(struct vm_info *v) - rc = -1; - goto out; - } -- bytes_read = fread(buffer, 1, sizeof(buffer), filp); -+ bytes_read = fread(buffer, 1, sizeof(buffer) - 1, filp); - /* - * Bail if read failed or the buffer isn't big enough - */ -@@ -314,7 +314,7 @@ int get_free_memsize() - ":%s\n", strerror(errno)); - clean_up(); - } -- bytes_read = fread(buffer, 1, sizeof(buffer), filp); -+ bytes_read = fread(buffer, 1, sizeof(buffer) - 1, filp); - /* - * Bail if read failed or the buffer isn't big enough - */ -diff --git a/cpuplugd/vmstat.c b/cpuplugd/vmstat.c -index 8428cce..d5a0036 100644 ---- a/cpuplugd/vmstat.c -+++ b/cpuplugd/vmstat.c -@@ -76,7 +76,7 @@ void get_cpu_stats(struct cpustat *s) - syslog(LOG_ERR, "Can not open /proc/stat" - ":%s\n", strerror(errno)); - } else { -- bytes_read = fread(buffer, 1, sizeof(buffer), filp); -+ bytes_read = fread(buffer, 1, sizeof(buffer) - 1, filp); - fclose(filp); - /* - * Bail if read failed or the buffer isn't big enough -@@ -118,7 +118,7 @@ void get_cpu_stats(struct cpustat *s) - * softirq: servicing softirqs - * steal: the cpu time spent in involuntary wait - */ -- sscanf(match, "cpu %du %du %du %du %du %du %du %du", -+ sscanf(match, "cpu %d %d %d %d %d %d %d %d", - &s->cpu_user, - &s->cpu_nice, - &s->cpu_sys, --- -1.6.6.1 - diff --git a/0039-cpuplugd-fix-cmm-limits-checks.patch b/0039-cpuplugd-fix-cmm-limits-checks.patch deleted file mode 100644 index d620813..0000000 --- a/0039-cpuplugd-fix-cmm-limits-checks.patch +++ /dev/null @@ -1,106 +0,0 @@ -From 664177c0fe57f671ad0d1eeed18374d77233d4b4 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Tue, 22 Jun 2010 13:28:45 +0200 -Subject: [PATCH 39/40] cpuplugd: fix cmm limits checks - -Description: cpuplugd: fix cmm_min/max limit checks. -Symptom: The cmm_min and cmm_max limits are not enforced correctly - when starting the daemon without the -V option, or when - cmm_pages is set manually below or above the limits. -Problem: Part of the cmm_min/max checks is only done when -V option - is specified. Values above/below the max/min limits are also - not identified correctly. -Solution: Make checks independent from -V option and check for cmm_pages - values beyond the limit. ---- - cpuplugd/main.c | 54 +++++++++++++++--------------------------------------- - 1 files changed, 15 insertions(+), 39 deletions(-) - -diff --git a/cpuplugd/main.c b/cpuplugd/main.c -index f8f9dfa..3738a5f 100644 ---- a/cpuplugd/main.c -+++ b/cpuplugd/main.c -@@ -226,26 +226,6 @@ void eval_mem_rules(struct config *cfg) - } - /* Evaluate the memunplug rule. */ - if (cfg->memunplug && eval_term(cfg->memunplug, &symbols)) { -- /* -- * case where cmm has asynchronously increased -- * cmm_pages after cpuplugd reset it to cmm_max -- * at cpuplugd startup. -- * -- */ -- if (cmmpages_size > cfg->cmm_max) { -- if (debug) { -- if (foreground == 1) -- printf("Found cmm_pages above Limit. " -- "Resetting value to %d\n" -- , cfg->cmm_max); -- if (foreground == 0) -- syslog(LOG_INFO, "Found cmm_pages above" -- "Limit. Resetting value to %d\n" -- , cfg->cmm_max); -- } -- set_cmm_pages(cfg->cmm_max); -- return; -- } - /* check memory limit */ - if (cmmpages_size + cfg->cmm_inc > cfg->cmm_max) { - if (debug) { -@@ -256,18 +236,16 @@ void eval_mem_rules(struct config *cfg) - syslog(LOG_INFO, "maximum memory" - " limit is reached\n"); - } -- if (cmmpages_size < cfg->cmm_max) { -- /* if the next increment would exceed -- * the maximum we advance to the -- * maximum -- */ -+ /* if the next increment would exceed -+ * the maximum we advance to the -+ * maximum -+ */ -+ if (cmmpages_size != cfg->cmm_max) - set_cmm_pages(cfg->cmm_max); -- return; -- } -- } else { -- memunplug(cfg->cmm_inc); - return; - } -+ memunplug(cfg->cmm_inc); -+ return; - } - /* Evaluate the memplug rule. */ - if (cfg->memplug && eval_term(cfg->memplug, &symbols)) { -@@ -280,19 +258,17 @@ void eval_mem_rules(struct config *cfg) - if (foreground == 0) - syslog(LOG_INFO, "minimum memory" - " limit is reached\n"); -- if (cmmpages_size > cfg->cmm_min) { -- /* if the next increment would exceed -- * the minimum we advance to the -- * minimum -- */ -- set_cmm_pages(cfg->cmm_min); -- return; -- } - } -- } else { -- memplug(cfg->cmm_inc); -+ /* if the next increment would exceed -+ * the minimum we advance to the -+ * minimum -+ */ -+ if (cmmpages_size != cfg->cmm_min) -+ set_cmm_pages(cfg->cmm_min); - return; - } -+ memplug(cfg->cmm_inc); -+ return; - } - } - --- -1.6.6.1 - diff --git a/0040-cpuplugd-set-cpu_min-to-1-by-default.patch b/0040-cpuplugd-set-cpu_min-to-1-by-default.patch deleted file mode 100644 index 572f9e4..0000000 --- a/0040-cpuplugd-set-cpu_min-to-1-by-default.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 4a2b6aa7b2a873be78c27d2abe010cd7e73d9640 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Tue, 22 Jun 2010 13:33:36 +0200 -Subject: [PATCH 40/40] cpuplugd: set cpu_min to 1 by default - -Description: cpuplugd: set cpu_min to 1 by default. -Symptom: With a cpu_min default value of 2, the "cpu ping pong" effect - may still be visible with low system load, i.e. expensive cpu - signaling may occur if the workload is spread on 2 under-worked - cpus. -Problem: The default value for cpu_min is 2. -Solution: Set the default value of cpu_min to 1. ---- - etc/sysconfig/cpuplugd | 6 ++---- - 1 files changed, 2 insertions(+), 4 deletions(-) - -diff --git a/etc/sysconfig/cpuplugd b/etc/sysconfig/cpuplugd -index c807232..efafa20 100644 ---- a/etc/sysconfig/cpuplugd -+++ b/etc/sysconfig/cpuplugd -@@ -6,13 +6,11 @@ - # It does not contain shell environment variables. - - ## Type: integer --## Default: 2 -+## Default: 1 - # - # The minimum number of cpus. --# This means in this example, that every time at least two cpus --# will be available - # --CPU_MIN="2" -+CPU_MIN="1" - - ## Type: integer - ## Default: 2 --- -1.6.6.1 - diff --git a/0041-fix-dates-option-on-zfcpdbf.patch b/0041-fix-dates-option-on-zfcpdbf.patch deleted file mode 100644 index 1f39594..0000000 --- a/0041-fix-dates-option-on-zfcpdbf.patch +++ /dev/null @@ -1,40 +0,0 @@ -From e30dc240e7417b83957507f1da6b34c31f18afb5 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Tue, 29 Jun 2010 22:23:50 +0200 -Subject: [PATCH] fix --dates option on zfcpdbf - -Description: zfcpdbf: Fix --dates option -Symptom: Running zfcpdbf with the option --dates returns "Unknown - option: dates". -Problem: The code expects --date, not the documented --dates option. -Solution: Change zfcpdbf to expect --dates, as documented in the - man page and in the usage information. ---- - scripts/zfcpdbf | 4 ++-- - 1 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/scripts/zfcpdbf b/scripts/zfcpdbf -index 3693d71..0b75c43 100755 ---- a/scripts/zfcpdbf -+++ b/scripts/zfcpdbf -@@ -139,7 +139,7 @@ GetOptions( - 'V|verbose' => \$OPT_VERBOSE, - 'h|help' => \$OPT_HELP, - 'v|version' => \$OPT_VERSION, -- 'D|date=s' => \$OPT_DATE, -+ 'D|dates=s' => \$OPT_DATE, - 's|singleline' => \$OPT_SINGLELINE - ) or print_usage(); - -@@ -555,7 +555,7 @@ sub print_san { - - # - # Converts the Unix time to localtime an returns it --# depending on the --date option. -+# depending on the --dates option. - # - # \param UNIX Timestamp to convert - # \return Formated Localtime --- -1.6.6.1 - diff --git a/0042-lsluns-uninitialized-value-on-adapter-offline.patch b/0042-lsluns-uninitialized-value-on-adapter-offline.patch deleted file mode 100644 index 080912f..0000000 --- a/0042-lsluns-uninitialized-value-on-adapter-offline.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 76e25ac419c3f23c0cdbfcd2db64bf196d806994 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Tue, 13 Jul 2010 15:27:26 +0200 -Subject: [PATCH 42/43] lsluns: uninitialized value on adapter offline - -Description: lsluns: uninitialized value on adapter offline. -Symptom: An error message is presented stating that some values - are not initialized while an operation is due. -Problem: The program execution is not reflection the offline adapter - status. -Solution: Account for offlined adapter status and show an appropriate - message. ---- - zconf/lsluns | 6 +++++- - 1 files changed, 5 insertions(+), 1 deletions(-) - -diff --git a/zconf/lsluns b/zconf/lsluns -index 9227f64..769b846 100755 ---- a/zconf/lsluns -+++ b/zconf/lsluns -@@ -211,7 +211,11 @@ sub show_attached_lun_info - print "\tport = $p\n"; - foreach my $l (sort keys %{$lun_hash{$a}{$p}}) { - my $sg_dev = "/dev/".$lun_hash{$a}{$p}{$l}; -- my $inq = `sg_inq -r $sg_dev`; -+ my $inq = `sg_inq -r $sg_dev 2>/dev/null`; -+ if (!$inq) { -+ print("\t\tlun = $l [offline]\n"); -+ next; -+ } - (my $vend = substr($inq, 0x8, 0x8)) =~ s/\s*//g; - (my $mod = substr($inq, 0x10, 0x10)) =~ s/\s*//g; - my $type = ord(substr($inq, 0x0, 0x1)); --- -1.7.1.1 - diff --git a/0043-zfcpdbf-Fix-Use-of-uninitialized-value-and-output-is.patch b/0043-zfcpdbf-Fix-Use-of-uninitialized-value-and-output-is.patch deleted file mode 100644 index b88a4e0..0000000 --- a/0043-zfcpdbf-Fix-Use-of-uninitialized-value-and-output-is.patch +++ /dev/null @@ -1,78 +0,0 @@ -From e1f4564972a7d280badf24568d56c063b6ce0ca7 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Tue, 13 Jul 2010 15:29:42 +0200 -Subject: [PATCH 43/43] zfcpdbf: Fix "Use of uninitialized value" and output issues - -Description: zfcpdbf: Fix "Use of uninitialized value" and output issues -Symptom: zfcpdbf outputs the error "Use of uninitialized value". -Problem: zfcpdbf tried to output attributes for "status read" - commands that do not exist. It also tried to output the - attribute port_handle for ELS requests that does not exist. -Solution: Exit early for "status read" requests, there is no additional - FSF command data. Remove output of LS field for ELS requests. - It was wrong, and newer dbf does not have this redundant field. - For consistency, add devno and timestamp to output of "status - read". ---- - scripts/zfcpdbf | 20 +++++++++++++------- - 1 files changed, 13 insertions(+), 7 deletions(-) - -diff --git a/scripts/zfcpdbf b/scripts/zfcpdbf -index 0b75c43..f82044d 100755 ---- a/scripts/zfcpdbf -+++ b/scripts/zfcpdbf -@@ -375,11 +375,13 @@ sub print_hba { - } - elsif(defined($hba_hash{'tag2'}) && - $hba_hash{'tag2'} eq "dism") { -- print "status read request dissmissed"; -+ print "$adapter $hba_hash{'timestamp'} " . -+ "status read request dismissed"; - } - elsif(defined($hba_hash{'tag2'}) && - $hba_hash{'tag2'} eq "fail") { -- print "status read request failed"; -+ print "$adapter $hba_hash{'timestamp'} " . -+ "status read request failed"; - } - } - elsif(defined($hba_hash{'tag'}) && $hba_hash{'tag'} eq "resp") { -@@ -398,9 +400,14 @@ sub print_hba { - } - } - else { -- next; -+ return; - } -- -+ -+ if ($hba_hash{'tag'} eq 'stat') { -+ print "\n"; -+ return; -+ } -+ - if($OPT_VERBOSE) { - print "protocol status qualifier=" . - "'$hba_hash{'fsf_prot_status_qual'}'" . $endl . -@@ -410,7 +417,7 @@ sub print_hba { - "'$hba_hash{'fsf_req_status'}'" . $endl . - "SBAL=$hba_hash{'sbal_first'}/$hba_hash{ - 'sbal_last'}/$hba_hash{'sbal_response'} " . -- "(fist/last/response)" . $endl; -+ "(first/last/response)" . $endl; - } - - if($hba_hash{'fsf_command'} eq '0x00000002') { -@@ -432,8 +439,7 @@ sub print_hba { - " LUN handle=$hba_hash{'lun_handle'}"; - } - elsif($hba_hash{'fsf_command'} eq '0x0000000b' ) { -- print "D_ID=$hba_hash{'d_id'} LS " . -- "code=$hba_hash{'port_handle'}"; -+ print "D_ID=$hba_hash{'d_id'}"; - } - print"\n"; - } --- -1.7.1.1 - diff --git a/0044-xcec-bridge-fix-multicast-forwarding.patch b/0044-xcec-bridge-fix-multicast-forwarding.patch deleted file mode 100644 index 1a5a904..0000000 --- a/0044-xcec-bridge-fix-multicast-forwarding.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 30321208dbccbd634c5e91b594372d150df07a03 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Tue, 25 Jan 2011 10:38:42 +0100 -Subject: [PATCH 44/46] xcec-bridge: fix multicast forwarding - -Description: xcec-bridge: fix multicast forwarding -Symptom: Forwarding of multicast traffic does not work. -Problem: xcec-bridge was developed for the broken packet socket - support of the qeth layer 3 driver. The code assumes - there are no link level heades for incoming packets. -Solution: New qeth layer 3 driver has full packet socket support - so xcec-bridge has to account link level header. ---- - ip_watcher/xcec-bridge.c | 9 +++++---- - 1 files changed, 5 insertions(+), 4 deletions(-) - -diff --git a/ip_watcher/xcec-bridge.c b/ip_watcher/xcec-bridge.c -index d6dd112..7f551d2 100644 ---- a/ip_watcher/xcec-bridge.c -+++ b/ip_watcher/xcec-bridge.c -@@ -549,12 +549,13 @@ void process_packet(struct int_sock *i_s) - if (s_ll.sll_pkttype==PACKET_BROADCAST) { - s_in.sin_addr.s_addr=INADDR_BROADCAST; - } else { -- memcpy(&s_in.sin_addr,&buffer[16],4); -+ memcpy(&s_in.sin_addr, &buffer[16 + ETH_HLEN], 4); - } - -- retval=sendto(i_s_item->o_fd,buffer,buffer_len,0, -- (struct sockaddr *)&s_in, -- sizeof(struct sockaddr_in)); -+ retval=sendto(i_s_item->o_fd, buffer + ETH_HLEN, -+ buffer_len - ETH_HLEN, 0, -+ (struct sockaddr *)&s_in, -+ sizeof(struct sockaddr_in)); - if (retval==-1) { - if ( (errno==EMSGSIZE) && (!i_s_item->mtu_warning) ) { - syslog(LOG_WARNING,"MTU of %s too small " \ --- -1.7.3.4 - diff --git a/0045-ziomon-wrong-return-codes.patch b/0045-ziomon-wrong-return-codes.patch deleted file mode 100644 index 566480f..0000000 --- a/0045-ziomon-wrong-return-codes.patch +++ /dev/null @@ -1,53 +0,0 @@ -From 7ff057ee33e5e452c09308249b36fa5da051b238 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Tue, 25 Jan 2011 10:57:40 +0100 -Subject: [PATCH 45/46] ziomon: wrong return codes - -Description: ziomon: ziomon tools return 1 when using option -h, --help and -v, - when return code must be 0. ziomon return 0 when using no command line - parameter, return code must be 1 here. -Symptom: Confusing error code. Some automated test cases maybe fail. -Problem: 1 as been introduced as rc for parse_parms besides error codes, - but is not distinguished from them when parse_params is called. -Solution: In function parse_parms substitute "exit 1" by "exit 0" - in case the option -h, --help or -v was handled by the program. - Substitute "exit 0" by "exit 1" in case the case with no - commandline parametes was handled by the program. ---- - ziomon/ziomon | 6 +++--- - 1 files changed, 3 insertions(+), 3 deletions(-) - -diff --git a/ziomon/ziomon b/ziomon/ziomon -index b4c6e36..9af2f4e 100755 ---- a/ziomon/ziomon -+++ b/ziomon/ziomon -@@ -124,7 +124,7 @@ function parse_params() { - - if [ $# -eq 0 ]; then - print_usage; -- exit 0; -+ exit 1; - fi - - let i=0; -@@ -132,7 +132,7 @@ function parse_params() { - case $1 in - --help|-h) - print_usage; -- exit 1;; -+ exit 0;; - --verbose|-V) - (( WRP_DEBUG++ ));; - --duration|-d) -@@ -152,7 +152,7 @@ function parse_params() { - [ $? -ne 0 ] && ((error++));; - --version|-v) - print_version; -- exit 1;; -+ exit 0;; - -*) - echo "$WRP_TOOLNAME: Invalid option -- $1"; - echo "Try '$WRP_TOOLNAME --help' for more information."; --- -1.7.3.4 - diff --git a/0046-qethconf-process-devices-with-non-zero-subchannel.patch b/0046-qethconf-process-devices-with-non-zero-subchannel.patch deleted file mode 100644 index 7445e64..0000000 --- a/0046-qethconf-process-devices-with-non-zero-subchannel.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 037964c1dd79a637e66b51544b69243a2f4216ea Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Tue, 25 Jan 2011 11:00:09 +0100 -Subject: [PATCH 46/46] qethconf: process devices with non-zero subchannel - -Description: qethconf: process devices with subchannel set != 0 -Symptom: qethconf ipa list does not show devices with subchannel - set != 0. -Problem: The code matches only for subchannel set 0. -Solution: Extend code to match for other subchannel IDs. ---- - qethconf/qethconf | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/qethconf/qethconf b/qethconf/qethconf -index 10dc902..9d9de1e 100644 ---- a/qethconf/qethconf -+++ b/qethconf/qethconf -@@ -205,7 +205,7 @@ function __list_entries - fi - for j in ${cmd_type} - do -- device_list="`cat $sys_file/0.0.*/if_name`" -+ device_list="`cat $sys_file/*.*.*/if_name`" - for i in ${device_list} - do - __layer2_enabled "$i" "list" --- -1.7.3.4 - diff --git a/0047-wait-for-completion-of-any-pending-actions-affecting.patch b/0047-wait-for-completion-of-any-pending-actions-affecting.patch deleted file mode 100644 index 3ff225c..0000000 --- a/0047-wait-for-completion-of-any-pending-actions-affecting.patch +++ /dev/null @@ -1,102 +0,0 @@ -From 68c07ef0b9d9731c040880e0db3570f48a85f9b8 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 28 Jan 2011 13:06:00 +0100 -Subject: [PATCH 47/61] wait for completion of any pending actions affecting device - -Delay I/O operations until all pending requests against the common I/O layer -have been completed. The kernel now provides /proc/cio_settle file and a write -there will be blocked until all requests are completed. ---- - zconf/chccwdev | 5 +++++ - zconf/chchp | 5 +++++ - zconf/cio_ignore | 9 +++++++++ - 3 files changed, 19 insertions(+), 0 deletions(-) - -diff --git a/zconf/chccwdev b/zconf/chccwdev -index d2a697b..03985a1 100755 ---- a/zconf/chccwdev -+++ b/zconf/chccwdev -@@ -28,6 +28,7 @@ - #============================================================================== - CMD=$(basename $0) - MAX_RETRIES=5 -+CIO_SETTLE="/proc/cio_settle" - - if [ "$(cat /proc/filesystems|grep sysfs)" = "" ]; then - echo "ERROR: $CMD requires sysfs support!" >&2 -@@ -160,6 +161,10 @@ while [ $# -gt 0 ]; do - shift - done - -+if [ -w $CIO_SETTLE ] ; then -+ echo 1 > $CIO_SETTLE -+fi -+ - # - # Parse the BUSIDLIST and expand the ranges and short IDs. - # -diff --git a/zconf/chchp b/zconf/chchp -index 520ce3f..4a62579 100755 ---- a/zconf/chchp -+++ b/zconf/chchp -@@ -30,6 +30,7 @@ VERSION="%S390_TOOLS_VERSION%" - TOOLNAME=${0##*/} - MAX_CHPID_CSS=255 - MAX_CHPID_ID=255 -+CIO_SETTLE="/proc/cio_settle" - - # Print help text - function print_help() -@@ -408,3 +409,7 @@ for CHPID in $CHPID_LIST ; do - get_chpid_id TO_ID $CHPID_TO - loop_chpids $FROM_CSS $FROM_ID $TO_CSS $TO_ID perform_command - done -+ -+if [ -w $CIO_SETTLE ] ; then -+ echo 1 > $CIO_SETTLE -+fi -diff --git a/zconf/cio_ignore b/zconf/cio_ignore -index 71dccb1..476c481 100755 ---- a/zconf/cio_ignore -+++ b/zconf/cio_ignore -@@ -8,6 +8,8 @@ - - VERSION="%S390_TOOLS_VERSION%" - BLACKLIST="/proc/cio_ignore" -+CIO_SETTLE="/proc/cio_settle" -+WAIT_FOR_CIO=0 - SYSINFO="/proc/sysinfo" - CONSDRV="/sys/bus/ccw/drivers/3215" - MAXCSSID=0 -@@ -706,9 +708,11 @@ while [ $# -gt 0 ] ; do - -r|--remove) - shift - remove_device $1 -+ WAIT_FOR_CIO=1 - ;; - -R|--remove-all) - remove_all_devices -+ WAIT_FOR_CIO=1 - ;; - -l|--list) - list_blacklisted 1 -@@ -724,6 +728,7 @@ while [ $# -gt 0 ] ; do - ;; - -p|--purge) - purge -+ WAIT_FOR_CIO=1 - ;; - *) - warn "invalid option '$1'" -@@ -734,4 +739,8 @@ while [ $# -gt 0 ] ; do - shift - done - -+if [ \( -w $CIO_SETTLE \) -a $WAIT_FOR_CIO = 1 ] ; then -+ echo 1 > $CIO_SETTLE -+fi -+ - exit 0 --- -1.7.3.5 - diff --git a/0048-add-infrastructure-code-for-new-features.patch b/0048-add-infrastructure-code-for-new-features.patch deleted file mode 100644 index 3ac141d..0000000 --- a/0048-add-infrastructure-code-for-new-features.patch +++ /dev/null @@ -1,169 +0,0 @@ -From 1ce37d173f564b5070d1819d0f5cec4f015bcbdf Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 28 Jan 2011 13:14:48 +0100 -Subject: [PATCH 48/61] add infrastructure code for new features - -Summary: s390-tools: Add infrastructure code for new features -Description: Add some infrastructure code from s390-tools upstream - to be used by new features: - * Add linked list implementation - * Add typedefs for replacing __uxx and __sxx datatypes - * Define S390_TOOLS_LIBDIR and S390_TOOLS_SYSCONFDIR macros ---- - common.mak | 4 ++ - include/list.h | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++ - include/zt_common.h | 11 +++++ - 3 files changed, 119 insertions(+), 0 deletions(-) - create mode 100644 include/list.h - -diff --git a/common.mak b/common.mak -index 0a7916e..625cf6c 100644 ---- a/common.mak -+++ b/common.mak -@@ -42,8 +42,12 @@ else - WARNFLAGS = -W -Wall - endif - CFLAGS = $(WARNFLAGS) -O3 -DS390_TOOLS_RELEASE=$(S390_TOOLS_RELEASE) \ -+ -DS390_TOOLS_LIBDIR=$(TOOLS_LIBDIR) \ -+ -DS390_TOOLS_SYSCONFDIR=$(SYSCONFDIR) \ - $(OPT_FLAGS) - CXXFLAGS = $(WARNFLAGS) -O3 -DS390_TOOLS_RELEASE=$(S390_TOOLS_RELEASE) \ -+ -DS390_TOOLS_LIBDIR=$(TOOLS_LIBDIR) \ -+ -DS390_TOOLS_SYSCONFDIR=$(SYSCONFDIR) \ - $(OPT_FLAGS) - export AS LD CC CPP AR NM STRIP OBJCOPY OBJDUMP INSTALL CFLAGS - -diff --git a/include/list.h b/include/list.h -new file mode 100644 -index 0000000..b7344ed ---- /dev/null -+++ b/include/list.h -@@ -0,0 +1,104 @@ -+/* -+ * Linked list functions -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Carsten Otte (cotte@de.ibm.com) -+ * Michael Holzheu -+ */ -+ -+#ifndef LIST_H -+#define LIST_H -+ -+#include -+ -+struct list { -+ struct list *next, *prev; -+}; -+ -+#define EMPTY_LIST(list) { &(list), &(list) } -+ -+/* -+ * Add entry to begining of list -+ */ -+static inline void list_add(struct list *entry, struct list *head) -+{ -+ entry->next = head->next; -+ entry->next->prev = entry; -+ head->next = entry; -+ entry->prev = head; -+} -+ -+/* -+ * Add entry to end of list -+ */ -+static inline void list_add_end(struct list *entry, struct list *head) -+{ -+ entry->prev = head->prev; -+ entry->prev->next = entry; -+ head->prev = entry; -+ entry->next = head; -+} -+ -+/* -+ * Remove entry -+ */ -+static inline void list_del(struct list *entry) -+{ -+ entry->next->prev = entry->prev; -+ entry->prev->next = entry->next; -+ entry->next = entry; -+ entry->prev = entry; -+} -+ -+/* -+ * Check if list is empty -+ */ -+static inline int list_is_empty(struct list *head) -+{ -+ if ((head->next == head) && (head->prev == head)) -+ return 1; -+ else -+ return 0; -+} -+ -+/* -+ * Initialize list -+ */ -+static inline void list_init(struct list *head) -+{ -+ head->next = head; -+ head->prev = head; -+} -+ -+#define list_entry(ptr, type, member) ({ \ -+ const typeof(((type *) 0)->member) *__member_ptr = (ptr); \ -+ (type *)((char *)__member_ptr - offsetof(type, member) ); }) -+ -+#define list_entry_first(ptr, type, member) \ -+ list_entry((ptr)->next, type, member) -+ -+#define list_entry_next(ptr, type, member) \ -+ list_entry((ptr)->next, type, member) -+ -+#define list_entry_prev(ptr, type, member) \ -+ list_entry((ptr)->prev, type, member) -+ -+/* -+ * List iterators -+ */ -+#define list_get(entry, type, member) \ -+ ((type *)((char *)(entry)-(unsigned long)(&((type *)0)->member))) -+ -+#define list_iterate(i, head, member) \ -+ for (i = list_get((head)->next, typeof(*i), member); \ -+ &i->member != (head); \ -+ i = list_get(i->member.next, typeof(*i), member)) -+ -+#define list_iterate_safe(i, head, member, n) \ -+ for (i = list_get((head)->next, typeof(*i), member), \ -+ n = list_get(i->member.next, typeof(*i), member); \ -+ &i->member != (head); \ -+ i = n, \ -+ n = list_get(n->member.next, typeof(*n), member)) -+ -+#endif /* LIST_H */ -diff --git a/include/zt_common.h b/include/zt_common.h -index ba9a850..7950651 100644 ---- a/include/zt_common.h -+++ b/include/zt_common.h -@@ -21,5 +21,16 @@ - #endif - - #define RELEASE_STRING STRINGIFY (S390_TOOLS_RELEASE) -+#define TOOLS_LIBDIR STRINGIFY (S390_TOOLS_LIBDIR) -+#define TOOLS_SYSCONFDIR STRINGIFY (S390_TOOLS_SYSCONFDIR) -+ -+typedef unsigned long long u64; -+typedef signed long long s64; -+typedef unsigned int u32; -+typedef signed int s32; -+typedef unsigned short int u16; -+typedef signed short int s16; -+typedef unsigned char u8; -+typedef signed char s8; - - #endif /* ZT_COMMON_H */ --- -1.7.3.5 - diff --git a/0049-hyptop-Show-hypervisor-performance-data-on-System-z.patch b/0049-hyptop-Show-hypervisor-performance-data-on-System-z.patch deleted file mode 100644 index 1b6c28f..0000000 --- a/0049-hyptop-Show-hypervisor-performance-data-on-System-z.patch +++ /dev/null @@ -1,8264 +0,0 @@ -From 096c2b87270417456ce9d92046838795ccd32ad1 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 28 Jan 2011 13:16:36 +0100 -Subject: [PATCH 49/61] hyptop: Show hypervisor performance data on System z - -Summary: hyptop: Show hypervisor performance data on System z -Description: hyptop provides a dynamic real-time view of a System z hypervisor - environment. It works with the z/VM and LPAR hypervisor. Depending - on the available data it shows e.g. CPU and memory consumption of - active LPARs or z/VM guests. The tool provides a curses based - user interface similar to the popular Linux 'top' command. ---- - Makefile | 2 +- - hyptop/Makefile | 27 + - hyptop/dg_debugfs.c | 86 ++++ - hyptop/dg_debugfs.h | 20 + - hyptop/dg_debugfs_lpar.c | 361 ++++++++++++++ - hyptop/dg_debugfs_vm.c | 267 ++++++++++ - hyptop/helper.c | 383 ++++++++++++++ - hyptop/helper.h | 100 ++++ - hyptop/hyptop.8 | 213 ++++++++ - hyptop/hyptop.c | 352 +++++++++++++ - hyptop/hyptop.h | 220 +++++++++ - hyptop/nav_desc.c | 243 +++++++++ - hyptop/nav_desc.h | 55 ++ - hyptop/opts.c | 416 ++++++++++++++++ - hyptop/opts.h | 20 + - hyptop/sd.h | 479 ++++++++++++++++++ - hyptop/sd_core.c | 435 ++++++++++++++++ - hyptop/sd_cpu_items.c | 178 +++++++ - hyptop/sd_sys_items.c | 325 ++++++++++++ - hyptop/table.c | 1231 ++++++++++++++++++++++++++++++++++++++++++++++ - hyptop/table.h | 424 ++++++++++++++++ - hyptop/table_col_unit.c | 369 ++++++++++++++ - hyptop/tbox.c | 240 +++++++++ - hyptop/tbox.h | 52 ++ - hyptop/win_cpu_types.c | 248 ++++++++++ - hyptop/win_cpu_types.h | 26 + - hyptop/win_fields.c | 272 ++++++++++ - hyptop/win_fields.h | 31 ++ - hyptop/win_help.c | 122 +++++ - hyptop/win_help.h | 23 + - hyptop/win_sys.c | 387 +++++++++++++++ - hyptop/win_sys_list.c | 380 ++++++++++++++ - 32 files changed, 7986 insertions(+), 1 deletions(-) - create mode 100644 hyptop/Makefile - create mode 100644 hyptop/dg_debugfs.c - create mode 100644 hyptop/dg_debugfs.h - create mode 100644 hyptop/dg_debugfs_lpar.c - create mode 100644 hyptop/dg_debugfs_vm.c - create mode 100644 hyptop/helper.c - create mode 100644 hyptop/helper.h - create mode 100644 hyptop/hyptop.8 - create mode 100644 hyptop/hyptop.c - create mode 100644 hyptop/hyptop.h - create mode 100644 hyptop/nav_desc.c - create mode 100644 hyptop/nav_desc.h - create mode 100644 hyptop/opts.c - create mode 100644 hyptop/opts.h - create mode 100644 hyptop/sd.h - create mode 100644 hyptop/sd_core.c - create mode 100644 hyptop/sd_cpu_items.c - create mode 100644 hyptop/sd_sys_items.c - create mode 100644 hyptop/table.c - create mode 100644 hyptop/table.h - create mode 100644 hyptop/table_col_unit.c - create mode 100644 hyptop/tbox.c - create mode 100644 hyptop/tbox.h - create mode 100644 hyptop/win_cpu_types.c - create mode 100644 hyptop/win_cpu_types.h - create mode 100644 hyptop/win_fields.c - create mode 100644 hyptop/win_fields.h - create mode 100644 hyptop/win_help.c - create mode 100644 hyptop/win_help.h - create mode 100644 hyptop/win_sys.c - create mode 100644 hyptop/win_sys_list.c - -diff --git a/Makefile b/Makefile -index 4b798bc..89c5fc5 100644 ---- a/Makefile -+++ b/Makefile -@@ -7,7 +7,7 @@ LIB_DIRS = libvtoc libu2s - SUB_DIRS = $(LIB_DIRS) zipl zdump fdasd dasdfmt dasdview tunedasd \ - tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \ - vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \ -- ziomon iucvterm -+ ziomon iucvterm hyptop - - all: subdirs_make - -diff --git a/hyptop/Makefile b/hyptop/Makefile -new file mode 100644 -index 0000000..43d9e0f ---- /dev/null -+++ b/hyptop/Makefile -@@ -0,0 +1,27 @@ -+include ../common.mak -+ -+CPPFLAGS += -I../include -+ -+all: hyptop -+ -+LDLIBS += -lncurses -+ -+OBJECTS = hyptop.o opts.o helper.o \ -+ sd_core.o sd_sys_items.o sd_cpu_items.o \ -+ tbox.o table.o table_col_unit.o \ -+ dg_debugfs.o dg_debugfs_lpar.o dg_debugfs_vm.o \ -+ win_sys_list.o win_sys.o win_fields.o \ -+ win_cpu_types.o win_help.o nav_desc.o -+ -+$(OBJECTS): *.h Makefile -+ -+hyptop: $(OBJECTS) -+ -+install: all -+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 hyptop $(USRSBINDIR) -+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 hyptop.8 $(MANDIR)/man8 -+ -+clean: -+ rm -f *.o *~ hyptop core -+ -+.PHONY: all install clean -diff --git a/hyptop/dg_debugfs.c b/hyptop/dg_debugfs.c -new file mode 100644 -index 0000000..13a6342 ---- /dev/null -+++ b/hyptop/dg_debugfs.c -@@ -0,0 +1,86 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Common functions for debugfs data gatherer -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include "helper.h" -+#include "hyptop.h" -+#include "dg_debugfs.h" -+ -+static char *l_debugfs_dir; -+ -+static void l_check_rc(int rc, int exit_on_err) -+{ -+ if (!exit_on_err) -+ return; -+ if (rc == -EACCES) -+ ERR_EXIT("Permission denied, check \"%s/s390_hypfs/\"\n", -+ l_debugfs_dir); -+ if (rc != -ENOENT) -+ ERR_EXIT("Could not initialize data gatherer (%s)\n", -+ strerror(-rc)); -+} -+ -+static void l_check_rc_final(int rc, int exit_on_err) -+{ -+ if (!exit_on_err) -+ return; -+ l_check_rc(rc, exit_on_err); -+ ERR_EXIT("Could not initialize data gatherer (%s)\n", strerror(-rc)); -+} -+ -+/* -+ * Initialize debugfs data gatherer backend -+ */ -+int dg_debugfs_init(int exit_on_err) -+{ -+ int rc; -+ -+ l_debugfs_dir = ht_mount_point_get("debugfs"); -+ if (!l_debugfs_dir) { -+ if (!exit_on_err) -+ return -ENODEV; -+ ERR_EXIT("Debugfs is not mounted, try \"mount none -t debugfs " -+ "/sys/kernel/debug\"\n"); -+ } -+ rc = dg_debugfs_vm_init(); -+ if (rc == 0) -+ return 0; -+ else -+ l_check_rc(rc, exit_on_err); -+ rc = dg_debugfs_lpar_init(); -+ if (rc == 0) -+ return 0; -+ else -+ l_check_rc_final(rc, exit_on_err); -+ return rc; -+} -+ -+/* -+ * Open a debugfs file -+ */ -+int dg_debugfs_open(const char *file) -+{ -+ char path[PATH_MAX]; -+ int fh; -+ -+ path[0] = 0; -+ strcat(path, l_debugfs_dir); -+ strcat(path, "/s390_hypfs/"); -+ strcat(path, file); -+ fh = open(path, O_RDONLY); -+ if (fh == -1) -+ return -errno; -+ else -+ return fh; -+} -+ -diff --git a/hyptop/dg_debugfs.h b/hyptop/dg_debugfs.h -new file mode 100644 -index 0000000..f46956c ---- /dev/null -+++ b/hyptop/dg_debugfs.h -@@ -0,0 +1,20 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Common functions for debugfs data gatherer -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#ifndef DG_DEBUGFS_H -+#define DG_DEBUGFS_H -+ -+#define DBFS_WAIT_TIME_US 10000 -+ -+extern int dg_debugfs_init(int exit_on_err); -+extern int dg_debugfs_vm_init(void); -+extern int dg_debugfs_lpar_init(void); -+extern int dg_debugfs_open(const char *file); -+ -+#endif /* DG_DEBUGFS_H */ -diff --git a/hyptop/dg_debugfs_lpar.c b/hyptop/dg_debugfs_lpar.c -new file mode 100644 -index 0000000..4ae1b6d ---- /dev/null -+++ b/hyptop/dg_debugfs_lpar.c -@@ -0,0 +1,361 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Hyptop LPAR data gatherer that operates on debugfs -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include -+#include -+#include -+#include -+#include "hyptop.h" -+#include "sd.h" -+#include "helper.h" -+#include "dg_debugfs.h" -+ -+#define LPAR_NAME_LEN 8 -+#define TMP_SIZE 64 -+#define LPAR_PHYS_FLG 0x80 -+#define CPU_TYPE_LEN 16 -+#define DEBUGFS_FILE "diag_204" -+ -+static u64 l_update_time_us; -+static long l_204_buf_size; -+ -+/* -+ * Diag data structure definition -+ */ -+ -+struct l_x_info_blk_hdr { -+ u8 npar; -+ u8 flags; -+ u8 reserved1[6]; -+ u64 curtod1; -+ u64 curtod2; -+ u8 reserved[40]; -+} __attribute__ ((packed)); -+ -+struct l_x_sys_hdr { -+ u8 reserved1; -+ u8 cpus; -+ u8 rcpus; -+ u8 reserved2[5]; -+ char sys_name[LPAR_NAME_LEN]; -+ u8 reserved3[80]; -+} __attribute__ ((packed)); -+ -+static inline void l_sys_hdr__sys_name(struct l_x_sys_hdr *hdr, char *name) -+{ -+ memcpy(name, hdr->sys_name, LPAR_NAME_LEN); -+ ht_ebcdic_to_ascii(name, LPAR_NAME_LEN); -+ name[LPAR_NAME_LEN] = 0; -+ ht_strstrip(name); -+} -+ -+struct l_x_cpu_info { -+ u16 cpu_addr; -+ u8 reserved1[2]; -+ u8 ctidx; -+ u8 reserved2[3]; -+ u64 acc_time; -+ u64 lp_time; -+ u8 reserved3[6]; -+ u8 reserved4[2]; -+ u64 online_time; -+ u8 reserved5[56]; -+} __attribute__ ((packed)); -+ -+static void l_idx2name(int index, char *name) -+{ -+ switch (index) { -+ case 0: -+ strcpy(name, SD_CPU_TYPE_STR_CP); -+ break; -+ case 3: -+ strcpy(name, SD_CPU_TYPE_STR_IFL); -+ break; -+ default: -+ strcpy(name, SD_CPU_TYPE_STR_UN); -+ } -+} -+ -+struct l_x_phys_hdr { -+ u8 reserved1[1]; -+ u8 cpus; -+ u8 reserved2[94]; -+} __attribute__ ((packed)); -+ -+struct l_x_phys_cpu { -+ u16 cpu_addr; -+ u8 reserved1[2]; -+ u8 ctidx; -+ u8 reserved2[3]; -+ u64 mgm_time; -+ u8 reserved3[80]; -+} __attribute__ ((packed)); -+ -+/* -+ * Fill CPU with data -+ */ -+static void l_sd_cpu_fill(struct sd_cpu *cpu, struct l_x_cpu_info *cpu_info) -+{ -+ sd_cpu_cpu_time_us_set(cpu, cpu_info->lp_time); -+ sd_cpu_mgm_time_us_set(cpu, G0(cpu_info->acc_time - cpu_info->lp_time)); -+ sd_cpu_online_time_us_set(cpu, cpu_info->online_time); -+} -+ -+/* -+ * Fill system with data -+ */ -+static void *l_sd_sys_fill(struct sd_sys *lpar, struct l_x_sys_hdr *sys_hdr) -+{ -+ struct l_x_cpu_info *cpu_info; -+ int i; -+ -+ cpu_info = (struct l_x_cpu_info *) (sys_hdr + 1); -+ -+ for (i = 0; i < sys_hdr->rcpus; i++) { -+ char cpu_type[CPU_TYPE_LEN + 1]; -+ struct sd_cpu *cpu; -+ char cpu_id[10]; -+ -+ sprintf(cpu_id, "%i", cpu_info->cpu_addr); -+ -+ cpu = sd_cpu_get(lpar, cpu_id); -+ if (!cpu) { -+ l_idx2name(cpu_info->ctidx, cpu_type); -+ cpu = sd_cpu_new(lpar, cpu_id, cpu_type, 1); -+ } -+ -+ l_sd_cpu_fill(cpu, cpu_info); -+ -+ sd_cpu_commit(cpu); -+ cpu_info++; -+ } -+ return cpu_info; -+} -+ -+/* -+ * Fill one physical CPU with data -+ */ -+static void l_sd_cpu_phys_fill(struct sd_sys *sys, -+ struct l_x_phys_cpu *cpu_info) -+{ -+ char cpu_type[CPU_TYPE_LEN + 1]; -+ char cpu_id[TMP_SIZE]; -+ struct sd_cpu *cpu; -+ -+ snprintf(cpu_id, TMP_SIZE, "%i", cpu_info->cpu_addr); -+ cpu = sd_cpu_get(sys, cpu_id); -+ if (!cpu) { -+ l_idx2name(cpu_info->ctidx, cpu_type); -+ cpu = sd_cpu_new(sys, cpu_id, cpu_type, 1); -+ } -+ sd_cpu_mgm_time_us_set(cpu, cpu_info->mgm_time); -+ sd_cpu_real_type_set(cpu, cpu_type); -+ sd_cpu_commit(cpu); -+} -+ -+/* -+ * Fill all physical CPUs with data -+ */ -+static void l_sd_sys_root_cpu_phys_fill(struct sd_sys *sys, -+ struct l_x_phys_hdr *phys_hdr) -+{ -+ struct l_x_phys_cpu *cpu_info; -+ int i; -+ -+ cpu_info = (struct l_x_phys_cpu *) (phys_hdr + 1); -+ for (i = 0; i < phys_hdr->cpus; i++) { -+ l_sd_cpu_phys_fill(sys, cpu_info); -+ cpu_info++; -+ } -+} -+ -+/* -+ * Header for debugfs file "diag_204" -+ */ -+struct l_debugfs_d204_hdr { -+ u64 len; -+ u16 version; -+ u8 reserved[54]; -+} __attribute__ ((packed)); -+ -+struct l_debugfs_d204 { -+ struct l_debugfs_d204_hdr h; -+ char buf[]; -+} __attribute__ ((packed)); -+ -+/* -+ * Read debugfs file -+ */ -+static void l_read_debugfs(struct l_debugfs_d204_hdr **hdr, -+ struct l_x_info_blk_hdr **data) -+{ -+ long real_buf_size; -+ ssize_t rc; -+ void *buf; -+ int fh; -+ -+ do { -+ fh = dg_debugfs_open(DEBUGFS_FILE); -+ *hdr = buf = ht_alloc(l_204_buf_size); -+ rc = read(fh, buf, l_204_buf_size); -+ if (rc == -1) -+ ERR_EXIT_ERRNO("Reading hypervisor data failed"); -+ close(fh); -+ real_buf_size = (*hdr)->len + sizeof(struct l_debugfs_d204_hdr); -+ if (rc == real_buf_size) -+ break; -+ l_204_buf_size = real_buf_size; -+ ht_free(buf); -+ } while (1); -+ *data = buf + sizeof(struct l_debugfs_d204_hdr); -+} -+ -+/* -+ * Fill System Data -+ */ -+static void l_sd_sys_root_fill(struct sd_sys *sys) -+{ -+ struct l_x_info_blk_hdr *time_hdr; -+ struct l_debugfs_d204_hdr *hdr; -+ struct l_x_sys_hdr *sys_hdr; -+ static int first = 1; -+ struct sd_sys *lpar; -+ char lpar_id[10]; -+ int i; -+ -+ do { -+ l_read_debugfs(&hdr, &time_hdr); -+ if (l_update_time_us != ht_ext_tod_2_us(&time_hdr->curtod1)) { -+ l_update_time_us = ht_ext_tod_2_us(&time_hdr->curtod1); -+ break; -+ } -+ /* -+ * Got old snapshot from kernel. Wait some time until -+ * new snapshot is available. -+ */ -+ ht_free(hdr); -+ usleep(DBFS_WAIT_TIME_US); -+ } while (1); -+ sys_hdr = ((void *) time_hdr) + sizeof(struct l_x_info_blk_hdr); -+ for (i = 0; i < time_hdr->npar; i++) { -+ l_sys_hdr__sys_name(sys_hdr, lpar_id); -+ lpar = sd_sys_get(sys, lpar_id); -+ if (!lpar) -+ lpar = sd_sys_new(sys, lpar_id); -+ sys_hdr = l_sd_sys_fill(lpar, sys_hdr); -+ sd_sys_commit(lpar); -+ } -+ -+ if (first && (time_hdr->flags & LPAR_PHYS_FLG)) { -+ l_sd_sys_root_cpu_phys_fill(sys, (void *) sys_hdr); -+ first = 0; -+ } -+ ht_free(hdr); -+ sd_sys_commit(sys); -+} -+ -+/* -+ * Update system data -+ */ -+static void l_sd_update(void) -+{ -+ struct sd_sys *root = sd_sys_root_get(); -+ -+ sd_sys_update_start(root); -+ l_sd_sys_root_fill(root); -+ sd_sys_update_end(root, l_update_time_us); -+} -+ -+/* -+ * Supported system items -+ */ -+static struct sd_sys_item *l_sys_item_vec[] = { -+ &sd_sys_item_cpu_cnt, -+ &sd_sys_item_cpu_diff, -+ &sd_sys_item_mgm_diff, -+ &sd_sys_item_cpu, -+ &sd_sys_item_mgm, -+ &sd_sys_item_online, -+ NULL, -+}; -+ -+/* -+ * Default system items -+ */ -+static struct sd_sys_item *l_sys_item_enable_vec[] = { -+ &sd_sys_item_cpu_cnt, -+ &sd_sys_item_cpu_diff, -+ &sd_sys_item_mgm_diff, -+ &sd_sys_item_cpu, -+ &sd_sys_item_mgm, -+ &sd_sys_item_online, -+ NULL, -+}; -+ -+/* -+ * Supported CPU items -+ */ -+static struct sd_cpu_item *l_cpu_item_vec[] = { -+ &sd_cpu_item_type, -+ &sd_cpu_item_cpu_diff, -+ &sd_cpu_item_mgm_diff, -+ &sd_cpu_item_cpu, -+ &sd_cpu_item_mgm, -+ &sd_cpu_item_online, -+ NULL, -+}; -+ -+/* -+ * Default CPU items -+ */ -+static struct sd_cpu_item *l_cpu_item_enable_vec[] = { -+ &sd_cpu_item_type, -+ &sd_cpu_item_cpu_diff, -+ &sd_cpu_item_mgm_diff, -+ NULL, -+}; -+ -+/* -+ * Supported CPU types -+ */ -+static struct sd_cpu_type *l_cpu_type_vec[] = { -+ &sd_cpu_type_ifl, -+ &sd_cpu_type_cp, -+ &sd_cpu_type_un, -+ NULL, -+}; -+ -+/* -+ * Define data gatherer structure -+ */ -+static struct sd_dg l_sd_dg = { -+ .update_sys = l_sd_update, -+ .cpu_type_vec = l_cpu_type_vec, -+ .sys_item_vec = l_sys_item_vec, -+ .sys_item_enable_vec = l_sys_item_enable_vec, -+ .cpu_item_vec = l_cpu_item_vec, -+ .cpu_item_enable_vec = l_cpu_item_enable_vec, -+}; -+ -+/* -+ * Initialize LPAR debugfs data gatherer -+ */ -+int dg_debugfs_lpar_init(void) -+{ -+ int fh; -+ -+ l_204_buf_size = sizeof(struct l_debugfs_d204_hdr); -+ fh = dg_debugfs_open(DEBUGFS_FILE); -+ if (fh < 0) -+ return fh; -+ else -+ close(fh); -+ sd_dg_register(&l_sd_dg); -+ return 0; -+} -diff --git a/hyptop/dg_debugfs_vm.c b/hyptop/dg_debugfs_vm.c -new file mode 100644 -index 0000000..6aec28f ---- /dev/null -+++ b/hyptop/dg_debugfs_vm.c -@@ -0,0 +1,267 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Hyptop z/VM data gatherer that operates on debugfs -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include -+#include -+#include -+#include "hyptop.h" -+#include "sd.h" -+#include "helper.h" -+#include "dg_debugfs.h" -+ -+#define VM_CPU_TYPE "UN" -+#define VM_CPU_ID "ALL" -+#define NAME_LEN 8 -+#define DEBUGFS_FILE "diag_2fc" -+ -+static u64 l_update_time_us; -+static long l_2fc_buf_size; -+ -+/* -+ * Diag 2fc data structure definition -+ */ -+struct l_diag2fc_data { -+ u32 version; -+ u32 flags; -+ u64 used_cpu; -+ u64 el_time; -+ u64 mem_min_kb; -+ u64 mem_max_kb; -+ u64 mem_share_kb; -+ u64 mem_used_kb; -+ u32 pcpus; -+ u32 lcpus; -+ u32 vcpus; -+ u32 cpu_min; -+ u32 cpu_max; -+ u32 cpu_shares; -+ u32 cpu_use_samp; -+ u32 cpu_delay_samp; -+ u32 page_wait_samp; -+ u32 idle_samp; -+ u32 other_samp; -+ u32 total_samp; -+ char guest_name[NAME_LEN]; -+}; -+ -+/* -+ * Header for debugfs file "diag_2fc" -+ */ -+struct l_debugfs_d2fc_hdr { -+ u64 len; -+ u16 version; -+ char tod_ext[16]; -+ u64 count; -+ char reserved[30]; -+} __attribute__ ((packed)); -+ -+struct l_debugfs_d2fc { -+ struct l_debugfs_d2fc_hdr h; -+ char diag2fc_buf[]; -+} __attribute__ ((packed)); -+ -+/* -+ * Fill "guest" with data -+ */ -+static void l_sd_sys_fill(struct sd_sys *guest, struct l_diag2fc_data *data) -+{ -+ struct sd_cpu *cpu; -+ -+ cpu = sd_cpu_get(guest, VM_CPU_ID); -+ if (!cpu) -+ cpu = sd_cpu_new(guest, VM_CPU_ID, SD_CPU_TYPE_STR_UN, -+ data->vcpus); -+ -+ sd_cpu_cnt(cpu) = data->vcpus; -+ sd_cpu_cpu_time_us_set(cpu, data->used_cpu); -+ sd_cpu_online_time_us_set(cpu, data->el_time); -+ -+ sd_sys_weight_cur_set(guest, data->cpu_shares); -+ sd_sys_weight_min_set(guest, data->cpu_min); -+ sd_sys_weight_max_set(guest, data->cpu_max); -+ -+ sd_sys_mem_min_kib_set(guest, data->mem_min_kb); -+ sd_sys_mem_max_kib_set(guest, data->mem_max_kb); -+ sd_sys_mem_use_kib_set(guest, data->mem_used_kb); -+ -+ sd_sys_update_time_us_set(guest, l_update_time_us); -+ sd_sys_commit(guest); -+} -+ -+/* -+ * Read debugfs file -+ */ -+static void l_read_debugfs(struct l_debugfs_d2fc_hdr **hdr, -+ struct l_diag2fc_data **data) -+{ -+ long real_buf_size; -+ ssize_t rc; -+ void *buf; -+ int fh; -+ -+ do { -+ fh = dg_debugfs_open(DEBUGFS_FILE); -+ *hdr = buf = ht_alloc(l_2fc_buf_size); -+ rc = read(fh, buf, l_2fc_buf_size); -+ if (rc == -1) -+ ERR_EXIT_ERRNO("Reading hypervisor data failed"); -+ close(fh); -+ real_buf_size = (*hdr)->len + sizeof(struct l_debugfs_d2fc_hdr); -+ if (rc == real_buf_size) -+ break; -+ l_2fc_buf_size = real_buf_size; -+ ht_free(buf); -+ } while (1); -+ *data = buf + sizeof(struct l_debugfs_d2fc_hdr); -+} -+ -+/* -+ * Fill System Data -+ */ -+static void l_sd_sys_root_fill(struct sd_sys *sys) -+{ -+ struct l_diag2fc_data *d2fc_data; -+ struct l_debugfs_d2fc_hdr *hdr; -+ struct sd_cpu *cpu; -+ unsigned int i; -+ -+ do { -+ l_read_debugfs(&hdr, &d2fc_data); -+ if (l_update_time_us != ht_ext_tod_2_us(&hdr->tod_ext)) { -+ l_update_time_us = ht_ext_tod_2_us(&hdr->tod_ext); -+ break; -+ } -+ /* -+ * Got old snapshot from kernel. Wait some time until -+ * new snapshot is available. -+ */ -+ ht_free(hdr); -+ usleep(DBFS_WAIT_TIME_US); -+ } while (1); -+ -+ cpu = sd_cpu_get(sys, VM_CPU_ID); -+ if (!cpu) -+ cpu = sd_cpu_new(sys, VM_CPU_ID, SD_CPU_TYPE_STR_UN, -+ d2fc_data[0].lcpus); -+ -+ for (i = 0; i < hdr->count; i++) { -+ struct l_diag2fc_data *data = &d2fc_data[i]; -+ char guest_name[NAME_LEN + 1]; -+ struct sd_sys *guest; -+ -+ guest_name[NAME_LEN] = 0; -+ memcpy(guest_name, data->guest_name, NAME_LEN); -+ ht_ebcdic_to_ascii(guest_name, NAME_LEN); -+ ht_strstrip(guest_name); -+ -+ guest = sd_sys_get(sys, guest_name); -+ if (!guest) -+ guest = sd_sys_new(sys, guest_name); -+ l_sd_sys_fill(guest, data); -+ } -+ ht_free(hdr); -+ sd_sys_commit(sys); -+} -+ -+/* -+ * Update system data -+ */ -+static void l_sd_update(void) -+{ -+ struct sd_sys *root = sd_sys_root_get(); -+ -+ sd_sys_update_start(root); -+ l_sd_sys_root_fill(root); -+ sd_sys_update_end(root, l_update_time_us); -+} -+ -+/* -+ * Supported system items -+ */ -+static struct sd_sys_item *l_sys_item_vec[] = { -+ &sd_sys_item_cpu_cnt, -+ &sd_sys_item_cpu_diff, -+ &sd_sys_item_cpu, -+ &sd_sys_item_online, -+ &sd_sys_item_mem_use, -+ &sd_sys_item_mem_max, -+ &sd_sys_item_weight_min, -+ &sd_sys_item_weight_cur, -+ &sd_sys_item_weight_max, -+ NULL, -+}; -+ -+/* -+ * Default system items -+ */ -+static struct sd_sys_item *l_sys_item_enable_vec[] = { -+ &sd_sys_item_cpu_cnt, -+ &sd_sys_item_cpu_diff, -+ &sd_sys_item_cpu, -+ &sd_sys_item_online, -+ &sd_sys_item_mem_max, -+ &sd_sys_item_mem_use, -+ &sd_sys_item_weight_cur, -+ NULL, -+}; -+ -+/* -+ * Supported CPU items -+ */ -+static struct sd_cpu_item *l_cpu_item_vec[] = { -+ &sd_cpu_item_cpu_diff, -+ &sd_cpu_item_cpu, -+ &sd_cpu_item_online, -+ NULL, -+}; -+ -+/* -+ * Default CPU items -+ */ -+static struct sd_cpu_item *l_cpu_item_enable_vec[] = { -+ &sd_cpu_item_cpu_diff, -+ NULL, -+}; -+ -+/* -+ * Supported CPU types -+ */ -+static struct sd_cpu_type *l_cpu_type_vec[] = { -+ &sd_cpu_type_un, -+ NULL, -+}; -+ -+/* -+ * Define data gatherer structure -+ */ -+static struct sd_dg dg_debugfs_vm_dg = { -+ .update_sys = l_sd_update, -+ .cpu_type_vec = l_cpu_type_vec, -+ .sys_item_vec = l_sys_item_vec, -+ .sys_item_enable_vec = l_sys_item_enable_vec, -+ .cpu_item_vec = l_cpu_item_vec, -+ .cpu_item_enable_vec = l_cpu_item_enable_vec, -+}; -+ -+/* -+ * Initialize z/VM debugfs data gatherer -+ */ -+int dg_debugfs_vm_init(void) -+{ -+ int fh; -+ -+ fh = dg_debugfs_open(DEBUGFS_FILE); -+ if (fh < 0) -+ return fh; -+ else -+ close(fh); -+ l_2fc_buf_size = sizeof(struct l_debugfs_d2fc_hdr); -+ sd_dg_register(&dg_debugfs_vm_dg); -+ return 0; -+} -diff --git a/hyptop/helper.c b/hyptop/helper.c -new file mode 100644 -index 0000000..bcdea9f ---- /dev/null -+++ b/hyptop/helper.c -@@ -0,0 +1,383 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Helper functions -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ * Christian Borntraeger -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "helper.h" -+#include "hyptop.h" -+#include "sd.h" -+ -+/* -+ * Globals -+ */ -+static iconv_t l_iconv_ebcdic_ascii; -+static int l_underline_cnt; -+static int l_reverse_cnt; -+static int l_bold_cnt; -+ -+/* -+ * Print time of day -+ */ -+void ht_print_time(void) -+{ -+ char time_str[40]; -+ struct timeval tv; -+ struct tm *tm; -+ -+ gettimeofday(&tv, NULL); -+ tm = localtime(&tv.tv_sec); -+ strftime(time_str, sizeof(time_str), "%H:%M:%S", tm); -+ hyptop_printf("%s", time_str); -+} -+ -+/* -+ * Alloc uninitialized memory and exit on failure -+ */ -+void *ht_alloc(size_t size) -+{ -+ void *ptr; -+ -+ ptr = malloc(size); -+ if (!ptr) -+ ERR_EXIT("Out of memory (%zu Kb)", size / 1024); -+ return ptr; -+} -+ -+/* -+ * Alloc memory initialized with "0" and exit on failure -+ */ -+void *ht_zalloc(size_t size) -+{ -+ void *ptr; -+ -+ ptr = calloc(1, size); -+ if (!ptr) -+ ERR_EXIT("Out of memory (%zu Kb)", size / 1024); -+ return ptr; -+} -+ -+/* -+ * Realloc memory and exit on failure -+ */ -+void *ht_realloc(void *old_ptr, size_t size) -+{ -+ void *ptr; -+ -+ assert(size != 0); -+ if (old_ptr) -+ ptr = realloc(old_ptr, size); -+ else -+ ptr = calloc(1, size); -+ if (!ptr) -+ ERR_EXIT("Out of memory (%lu Kb)", (unsigned long) size / 1024); -+ return ptr; -+} -+ -+/* -+ * Convert EBCDIC string to ASCII -+ */ -+void ht_ebcdic_to_ascii(char *inout, size_t len) -+{ -+ iconv(l_iconv_ebcdic_ascii, &inout, &len, &inout, &len); -+} -+ -+/* -+ * Get mount point for file system tye "fs_type" -+ */ -+char *ht_mount_point_get(const char *fs_type) -+{ -+ struct mntent *mntbuf; -+ FILE *mounts; -+ -+ mounts = setmntent(_PATH_MOUNTED, "r"); -+ if (!mounts) -+ ERR_EXIT_ERRNO("Could not find \"%s\" mount point", fs_type); -+ while ((mntbuf = getmntent(mounts)) != NULL) { -+ if (strcmp(mntbuf->mnt_type, fs_type) == 0) -+ return ht_strdup(mntbuf->mnt_dir); -+ } -+ endmntent(mounts); -+ return NULL; -+} -+ -+/* -+ * Remove all trailing blanks and reture pointer to first non blank character -+ */ -+char *ht_strstrip(char *s) -+{ -+ size_t size; -+ char *end; -+ -+ size = strlen(s); -+ -+ if (!size) -+ return s; -+ -+ end = s + size - 1; -+ while (end >= s && isspace(*end)) -+ end--; -+ *(end + 1) = '\0'; -+ -+ while (*s && isspace(*s)) -+ s++; -+ -+ return s; -+} -+ -+/* -+ * Return copy of string -+ */ -+char *ht_strdup(const char *str) -+{ -+ char *rc; -+ -+ rc = ht_alloc(strlen(str) + 1); -+ strcpy(rc, str); -+ return rc; -+} -+ -+/* -+ * Print help icon in current line -+ */ -+void ht_print_help_icon(void) -+{ -+ hyptop_print_seek_back(6); -+ ht_underline_on(); -+ hyptop_printf("?"); -+ ht_underline_off(); -+ hyptop_printf("=help"); -+} -+ -+/* -+ * Print headline -+ */ -+void ht_print_head(const char *sys) -+{ -+ struct sd_cpu_type *cpu_type; -+ int i; -+ -+ ht_print_time(); -+ hyptop_printf(" "); -+ if (sys) { -+ ht_bold_on(); -+ hyptop_printf("%s", sys); -+ ht_bold_off(); -+ hyptop_printf(" "); -+ } -+ hyptop_printf("CPU-"); -+ ht_underline_on(); -+ hyptop_printf("T"); -+ ht_underline_off(); -+ hyptop_printf(": "); -+ -+ sd_cpu_type_iterate(cpu_type, i) { -+ if (!sd_cpu_type_selected(cpu_type)) -+ continue; -+ hyptop_printf("%s(%i) ", sd_cpu_type_id(cpu_type), -+ sd_cpu_type_cpu_cnt(cpu_type)); -+ } -+ ht_print_help_icon(); -+ hyptop_print_nl(); -+} -+ -+/* -+ * Curses attribute functions -+ */ -+static void ht_attr_on(int attr) -+{ -+ if (g.o.batch_mode_specified) -+ return; -+ attron(attr); -+} -+ -+static void ht_attr_off(int attr) -+{ -+ if (g.o.batch_mode_specified) -+ return; -+ attroff(attr); -+} -+ -+void ht_bold_on(void) -+{ -+ if (l_bold_cnt == 0) -+ ht_attr_on(A_BOLD); -+ l_bold_cnt++; -+} -+ -+void ht_bold_off(void) -+{ -+ -+ l_bold_cnt--; -+ if (l_bold_cnt == 0) -+ ht_attr_off(A_BOLD); -+} -+ -+void ht_underline_on(void) -+{ -+ if (l_underline_cnt == 0) -+ ht_attr_on(A_UNDERLINE); -+ l_underline_cnt++; -+} -+ -+void ht_underline_off(void) -+{ -+ l_underline_cnt--; -+ if (l_underline_cnt == 0) -+ ht_attr_off(A_UNDERLINE); -+} -+ -+void ht_reverse_on(void) -+{ -+ if (l_reverse_cnt == 0) -+ ht_attr_on(A_REVERSE); -+ l_reverse_cnt++; -+} -+ -+void ht_reverse_off(void) -+{ -+ l_reverse_cnt--; -+ if (l_reverse_cnt == 0) -+ ht_attr_off(A_REVERSE); -+} -+ -+/* -+ * Print scroll bar -+ */ -+void ht_print_scroll_bar(int row_cnt, int row_start, int rows_add_top, -+ int rows_add_bottom, int can_scroll_up, -+ int can_scroll_down, int with_border) -+{ -+ int row_cnt_displ, bar_len, start, i; -+ double scale1, scale2; -+ -+ row_cnt_displ = MIN(row_cnt, g.c.row_cnt - rows_add_top -+ - rows_add_bottom); -+ if (row_cnt_displ <= 0) -+ return; -+ /* scale1: Scaling factor virtual screen to physical screen */ -+ scale1 = ((double) row_cnt_displ) / ((double) row_cnt); -+ /* scale2: Scaling factor physical screen to scroll bar size */ -+ scale2 = ((double) row_cnt_displ - 2) / row_cnt_displ; -+ bar_len = MAX(((double) row_cnt_displ * scale1 * scale2 + 0.5), 1); -+ /* start: Start row in scroll bar */ -+ start = ((double) row_start) * scale1 * scale2 + 0.5; -+ -+ if (row_cnt_displ - 2 - start < bar_len) -+ start = row_cnt_displ - 2 - bar_len; -+ -+ ht_reverse_on(); -+ -+ if (with_border) { -+ ht_underline_on(); -+ hyptop_printf_pos(rows_add_top - 1, g.c.col_cnt - 1, " "); -+ ht_underline_off(); -+ hyptop_printf_pos(row_cnt_displ + rows_add_top, -+ g.c.col_cnt - 1, " "); -+ } -+ -+ ht_underline_on(); -+ if (can_scroll_up) { -+ ht_bold_on(); -+ hyptop_printf_pos(rows_add_top, g.c.col_cnt - 1, "^"); -+ ht_bold_off(); -+ } else { -+ hyptop_printf_pos(rows_add_top, g.c.col_cnt - 1, "^"); -+ } -+ ht_underline_off(); -+ -+ if (row_cnt_displ == 1) -+ goto out; -+ -+ ht_underline_on(); -+ if (can_scroll_down) { -+ ht_bold_on(); -+ hyptop_printf_pos(row_cnt_displ - 1 + rows_add_top, -+ g.c.col_cnt - 1, "v"); -+ ht_bold_off(); -+ } else { -+ hyptop_printf_pos(row_cnt_displ - 1 + rows_add_top, -+ g.c.col_cnt - 1, "v"); -+ } -+ ht_underline_off(); -+ -+ if (row_cnt_displ == 2) -+ goto out; -+ -+ for (i = 0; i < row_cnt_displ - 2; i++) -+ hyptop_printf_pos(i + rows_add_top + 1, g.c.col_cnt - 1, -+ " "); -+ ht_underline_on(); -+ hyptop_printf_pos(i + rows_add_top, g.c.col_cnt - 1, " "); -+ ht_underline_off(); -+ -+ ht_bold_on(); -+ for (i = 0; i < bar_len; i++) { -+ if (i + start == row_cnt_displ - 3) -+ ht_underline_on(); -+ hyptop_printf_pos(i + start + 1 + rows_add_top, -+ g.c.col_cnt - 1, "#"); -+ if (i + start == row_cnt_displ - 3) -+ ht_underline_off(); -+ } -+ ht_bold_off(); -+out: -+ ht_reverse_off(); -+} -+ -+/* -+ * Convert string to uppercase -+ */ -+void ht_str_to_upper(char *str) -+{ -+ while (*str) { -+ *str = toupper(*str); -+ str++; -+ } -+} -+ -+/* -+ * Convert ext TOD to microseconds -+ */ -+u64 ht_ext_tod_2_us(void *tod_ext) -+{ -+ char *tod_ptr = tod_ext; -+ u64 us, *tod1, *tod2; -+ -+ tod1 = (u64 *) tod_ptr; -+ tod2 = (u64 *) &tod_ptr[8]; -+ us = *tod1 << 8; -+ us |= *tod2 >> 58; -+ us = us >> 12; -+ -+ return us; -+} -+ -+/* -+ * Initialize helper module -+ */ -+void hyptop_helper_init(void) -+{ -+ l_iconv_ebcdic_ascii = iconv_open("ISO-8859-1", "EBCDIC-US"); -+ if (l_iconv_ebcdic_ascii == (iconv_t) -1) -+ ERR_EXIT("Could not initilize iconv\n"); -+} -diff --git a/hyptop/helper.h b/hyptop/helper.h -new file mode 100644 -index 0000000..61717f3 ---- /dev/null -+++ b/hyptop/helper.h -@@ -0,0 +1,100 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Helper functions -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ * Christian Borntraeger -+ */ -+ -+#ifndef HELPER_H -+#define HELPER_H -+ -+#include -+#include -+#include -+#include "zt_common.h" -+ -+/* -+ * min/max macros -+ */ -+#define MAX(x, y) ((x) > (y) ? (x) : (y)) -+#define MIN(x, y) ((x) < (y) ? (x) : (y)) -+#define G0(x) MAX(0, (s64) (x)) -+ -+/* -+ * Helper Prototypes -+ */ -+extern void hyptop_helper_init(void); -+extern char *ht_strstrip(char *str); -+extern char *ht_strdup(const char *str); -+extern void ht_print_head(const char *sys); -+extern void ht_print_help_icon(void); -+extern void ht_ebcdic_to_ascii(char *inout, size_t len); -+extern char *ht_mount_point_get(const char *fs_type); -+extern u64 ht_ext_tod_2_us(void *tod_ext); -+extern void ht_print_time(void); -+ -+/* -+ * Memory alloc functions -+ */ -+extern void *ht_zalloc(size_t size); -+extern void *ht_alloc(size_t size); -+extern void *ht_realloc(void *ptr, size_t size); -+static inline void ht_free(void *ptr) -+{ -+ free(ptr); -+} -+ -+/* -+ * Curses extensions -+ */ -+ -+#define KEY_RETURN 0012 -+#define KEY_ESCAPE 0033 -+ -+void ht_bold_on(void); -+void ht_bold_off(void); -+void ht_reverse_on(void); -+void ht_reverse_off(void); -+void ht_underline_on(void); -+void ht_underline_off(void); -+void ht_str_to_upper(char *str); -+ -+void ht_print_scroll_bar(int row_cnt, int row_start, int row_bar_start, -+ int row_bar_bottom, int can_scroll_up, -+ int can_scroll_down, int with_boder); -+ -+/* -+ * Error Macros -+ */ -+#define ERR_MSG(x...) \ -+do { \ -+ hyptop_text_mode(); \ -+ fflush(stdout); \ -+ fprintf(stderr, "%s: ", g.prog_name);\ -+ fprintf(stderr, x); \ -+} while (0) -+ -+#define ERR_EXIT(x...) \ -+do { \ -+ hyptop_text_mode(); \ -+ fflush(stdout); \ -+ fprintf(stderr, "%s: ", g.prog_name); \ -+ fprintf(stderr, x); \ -+ hyptop_exit(1); \ -+ exit(1); \ -+} while (0) -+ -+#define ERR_EXIT_ERRNO(x...) \ -+do { \ -+ fflush(stdout); \ -+ fprintf(stderr, "%s: ", g.prog_name); \ -+ fprintf(stderr, x); \ -+ fprintf(stderr, " (%s)", strerror(errno)); \ -+ fprintf(stderr, "\n"); \ -+ hyptop_exit(1); \ -+} while (0) -+ -+#endif /* HELPER_H */ -diff --git a/hyptop/hyptop.8 b/hyptop/hyptop.8 -new file mode 100644 -index 0000000..99a729c ---- /dev/null -+++ b/hyptop/hyptop.8 -@@ -0,0 +1,213 @@ -+.TH HYPTOP 8 "Nov 2009" "s390-tools" -+.SH NAME -+hyptop \- Show hypervisor performance data on System z -+ -+.SH SYNOPSIS -+.B hyptop -+[OPTIONS] -+ -+.SH DESCRIPTION -+.B hyptop -+provides a dynamic real-time view of a hypervisor environment on System z. -+It works with either the z/VM or the LPAR hypervisor. Depending on the available -+data it shows for example CPU and memory information about running LPARs or -+z/VM guests. -+ -+hyptop provides two windows: -+.IP " -" -+sys_list: Shows a list of systems that the hypervisor is currently running -+.IP " -" -+sys: Shows one system in more detail. -+ -+.PP -+You can run hyptop in interactive mode (default) or in batch mode with -+the "\-b" option. For how to use the interactive mode, see the online help -+(enter "?" after hyptop is started). -+ -+.SH OPTIONS -+.TP -+.BR "\-h" " or " "\-\-help" -+Print usage information, then exit. -+ -+.TP -+.BR "\-v" " or " "\-\-version" -+Print version information, then exit. -+ -+.TP -+.BR "\-w " " or " "\-\-window=" -+Select current window. Use the options "--sys", "--fields", and "--sort" to -+modify the current window. The last window specified with the "--window" option -+will be used as start window. The default window is "sys_list". -+.TP -+.BR "\-s ,..." " or " "\-\-sys=,..." -+Select systems for current window. If this option is specified, only the -+selected systems are shown for the window. For window "sys" only one -+system can be specified. -+.TP -+.BR "\-f [:],..." " or " "\-\-fields=[:],..." -+Select fields and units in the current window. "F_LETTER" is the field -+letter that identifies uniquely a field (for example "c" for CPU time). -+"UNIT" is the used entity for displaying data for the field (for example "us" -+for microseconds). See FIELDS and UNITS below for definitions. -+If the "--fields" option is specified, only the selected fields are -+shown. -+.TP -+.BR "\-S " " or " "\-\-sort=" -+Select sort field for current window. To reverse the sort order, specify the -+option twice. See FIELDS below for definitions. -+.TP -+.BR "\-t ,..." " or " "\-\-cpu_types=,..." -+Select CPU types that are used for CPU time calculations. See CPU TYPES -+below for definitions. -+.TP -+.BR "\-b" " or " "\-\-batch_mode" -+Use batch mode (no curses). This can be useful for sending output from hyptop -+to another program, a file, or a line mode terminal. -+In this mode no user input is accepted. -+.TP -+.BR "\-d " " or " "\-\-delay=" -+Specifies the delay between screen updates. -+.TP -+.BR "\-n " " or " "\-\-iterations=" -+Specifies the maximum number of iterations before ending. -+ -+.SH PREREQUISITES -+The following things are required to run hyptop: -+ -+.IP " -" -+The Linux kernel must have the required support to provide the -+performance data. -+.IP " -" -+debugfs has to be mounted. -+.IP " -" -+The hyptop user must have read permission for the required debugfs files. -+ -+.PP -+To mount debugfs, you can use this command: -+ -+# mount none -t debugfs /sys/kernel/debug -+ -+To make this persistent, add the following to "/etc/fstab": -+ -+none /sys/kernel/debug debugfs defaults 0 0 -+ -+ -+.SH FIELDS -+The supported fields depend on the available data on the hypervisor. -+This is different between LPAR and z/VM. It might also depend on -+machine type, z/VM version and kernel version. Each field has a unique -+field letter that can be used to select the field in interactive mode -+or through the "--fields" command line option. -+ -+The following fields are available under LPAR: -+ -+ In "sys_list" and "sys" window: -+ 'c' - CPU time per second -+ 'm' - Management time per second -+ 'C' - Total CPU time -+ 'M' - Total management time -+ 'o' - Online time -+ -+ In "sys_list" window: -+ '#' - Number of CPUs -+ -+ In "sys" window: -+ 'p' - CPU type -+ 'v' - Visualization of CPU time per second -+ -+The following fields are available under z/VM: -+ -+ In "sys_list" and "sys" window: -+ 'c' - CPU time per second -+ 'C' - Total CPU time -+ 'o' - Online time -+ -+ In "sys_list" window: -+ '#' - Number of CPUs -+ 'u' - Used memory -+ 'a' - Maximum memory -+ 'n' - Minimum weight -+ 't' - Current weight -+ 'x' - Maximum weight -+ -+ In "sys" window: -+ 'v' - Visualization of CPU time per second -+ -+.SH UNITS -+Depending on the field type the values can be displayed in different units. -+The following units are supported: -+ -+ Time: -+ 'us' - Microseconds (10^-6 seconds) -+ 'ms' - Millisconds (10^-3 seconds) -+ '%' - Hundreds of a second (10^-2 seconds) or percent -+ 's' - Seconds -+ 'm' - Minutes -+ 'hm' - Hours & Minutes -+ 'dhm' - Days & Hours & Minutes -+ -+ Memory: -+ 'kib' - Kibibytes (1.024 bytes) -+ 'mib' - Mebibytes (1.048.576 bytes) -+ 'gib' - Gibibytes (1.073.741.824 bytes) -+ -+ Miscellaneous: -+ 'str' - String -+ '#' - Count/Number -+ 'vis' - Visualization -+ -+.SH CPU TYPES -+Depending on the hypervisor different CPU types are supported. These CPU -+types can be selected either interactively or with the "--cpu_types" -+command line option. The calculation of the CPU data only uses CPUs of -+the specified types. -+ -+On LPAR the following CPU types are supported: -+ 'IFL' - Integrated Facility for Linux -+ 'CP' - CP processor type -+ 'UN' - Unspecified processor type (other than CP or IFL) -+ -+NOTE: It is possible that on older machines also IFLs are shown as CPs. -+On z/VM currently only the processor type 'UN' is available. -+ -+.SH EXAMPLES -+To start hyptop with the "sys_list" window in interactive mode, enter: -+.br -+ -+ # hyptop -+ -+.br -+To start hyptop with the "sys_list" window in batch mode, enter: -+.br -+ -+ # hyptop -b -+ -+.br -+To start hyptop with the "sys_list" window in interactive mode with the fields -+CPU time (in milliseconds) and online time (unit default) and sort the -+output according to online time, enter: -+.br -+ -+ # hyptop -f c:ms,o -S o -+ -+.br -+To start hyptop with the "sys" window with system "MYLPAR" with the fields CPU -+time (unit milliseconds) and online time (unit default) and sort the -+output reverse according the online time, enter: -+.br -+ -+ # hyptop -w sys -s MYLPAR -f c:ms,o -S o -S o -+ -+.br -+To start hyptop with the "sys_list" window in batch mode with update delay 5 -+seconds and 10 iterations, enter: -+.br -+ -+ # hyptop -b -d 5 -n 10 -+ -+.br -+To start hyptop with the "sys_list" window and use only CPU types IFL and CP -+for CPU time calculation, enter: -+.br -+ -+ # hyptop -t ifl,cp -diff --git a/hyptop/hyptop.c b/hyptop/hyptop.c -new file mode 100644 -index 0000000..c42e8b0 ---- /dev/null -+++ b/hyptop/hyptop.c -@@ -0,0 +1,352 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Main & init functions -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "helper.h" -+#include "sd.h" -+#include "hyptop.h" -+#include "win_cpu_types.h" -+#include "opts.h" -+#include "dg_debugfs.h" -+ -+#ifdef WITH_HYPFS -+#include "dg_hypfs.h" -+#endif -+ -+/* -+ * Globals for the whole program -+ */ -+struct hyptop_globals g; -+ -+/* -+ * Get current terminal size and tell curses about it -+ */ -+static void l_term_size_get(void) -+{ -+ struct winsize ws; -+ -+ g.c.col_cnt = 80; -+ g.c.row_cnt = 24; -+ -+ if (ioctl(1, TIOCGWINSZ, &ws) != -1) { -+ if ((ws.ws_col != 0) && (ws.ws_row != 0)) { -+ g.c.col_cnt = ws.ws_col; -+ g.c.row_cnt = ws.ws_row; -+ } -+ } -+ resizeterm(g.c.row_cnt, g.c.col_cnt); -+} -+ -+/* -+ * Process input -+ */ -+static enum hyptop_win_action l_process_input(struct hyptop_win *win) -+{ -+ int c; -+ -+ /* Skip all resize events */ -+ while ((c = wgetch(stdscr)) == KEY_RESIZE) {} -+ return win->process_input(win, c); -+} -+ -+/* -+ * Process input with timeout -+ */ -+static enum hyptop_win_action l_process_input_timeout(time_t time_s, -+ long time_us) -+{ -+ struct timeval tv; -+ fd_set fds; -+ int rc; -+ -+ while (1) { -+ FD_ZERO(&fds); -+ FD_SET(0, &fds); -+ tv.tv_sec = time_s; -+ tv.tv_usec = time_us; -+ rc = select(1, &fds, NULL, NULL, &tv); -+ switch (rc) { -+ case 0: -+ /* Timeout */ -+ return WIN_KEEP; -+ case 1: -+ /* Input */ -+ if (l_process_input(g.w.cur) == WIN_SWITCH) -+ return WIN_SWITCH; -+ continue; -+ case -1: -+ if (errno != EINTR) -+ ERR_EXIT_ERRNO("Select call failed"); -+ /* Signal: Resize */ -+ hyptop_update_term(); -+ continue; -+ default: -+ assert(0); -+ } -+ } -+} -+ -+/* -+ * Sleep -+ */ -+static enum hyptop_win_action l_sleep(time_t time_s, long time_us) -+{ -+ struct timespec ts; -+ -+ ts.tv_sec = time_s; -+ ts.tv_nsec = time_us * 1000; -+ -+ nanosleep(&ts, NULL); -+ return WIN_KEEP; -+} -+ -+/* -+ * External process input with timeout funciton -+ */ -+enum hyptop_win_action hyptop_process_input_timeout(void) -+{ -+ enum hyptop_win_action rc; -+ -+ if (g.o.batch_mode_specified) { -+ opts_iterations_next(); -+ rc = l_sleep(g.o.delay_s, g.o.delay_us); -+ } else { -+ rc = l_process_input_timeout(g.o.delay_s, g.o.delay_us); -+ opts_iterations_next(); -+ } -+ return rc; -+} -+ -+/* -+ * External process input funciton -+ */ -+enum hyptop_win_action hyptop_process_input(void) -+{ -+ return l_process_input_timeout(-1U, 0); -+} -+ -+/* -+ * Signal handler for exiting hyptop -+ */ -+static void l_sig_exit(int sig) -+{ -+ (void) sig; -+ -+ hyptop_exit(0); -+} -+ -+/* -+ * Install signal handler -+ */ -+static void l_sig_handler_init(void) -+{ -+ struct sigaction sigact; -+ -+ /* Ignore signals SIGUSR1 and SIGUSR2 */ -+ if (sigemptyset(&sigact.sa_mask) < 0) -+ goto fail; -+ sigact.sa_flags = 0; -+ sigact.sa_handler = SIG_IGN; -+ if (sigaction(SIGUSR1, &sigact, NULL) < 0) -+ goto fail; -+ if (sigaction(SIGUSR2, &sigact, NULL) < 0) -+ goto fail; -+ -+ /* Exit on SIGINT, SIGTERM, SIGHUP, ... */ -+ if (sigemptyset(&sigact.sa_mask) < 0) -+ goto fail; -+ sigact.sa_handler = l_sig_exit; -+ if (sigaction(SIGINT, &sigact, NULL) < 0) -+ goto fail; -+ if (sigaction(SIGTERM, &sigact, NULL) < 0) -+ goto fail; -+ if (sigaction(SIGHUP, &sigact, NULL) < 0) -+ goto fail; -+ if (sigaction(SIGQUIT, &sigact, NULL) < 0) -+ goto fail; -+ if (sigaction(SIGALRM, &sigact, NULL) < 0) -+ goto fail; -+ if (sigaction(SIGPIPE, &sigact, NULL) < 0) -+ goto fail; -+ return; -+fail: -+ ERR_EXIT_ERRNO("Could not initialize signal handler"); -+} -+ -+/* -+ * Start curses -+ */ -+static int l_initscr(void) -+{ -+ if (!initscr()) -+ return ERR; -+ g.c.initialized = 1; -+ atexit(hyptop_text_mode); -+ return 0; -+} -+ -+/* -+ * Init curses -+ */ -+static void l_term_init(void) -+{ -+ if (g.o.batch_mode_specified) -+ return; -+ -+ if (l_initscr() == ERR) -+ goto fail; -+ if (noecho() == ERR) -+ goto fail; -+ if (nodelay(stdscr, TRUE) == ERR) -+ goto fail; -+ if (cbreak() == ERR) /* Line buffering disabled. pass on everything */ -+ goto fail; -+ if (keypad(stdscr, TRUE) == ERR) -+ goto fail; -+ curs_set(0); /* prevent cursor from blinking */ -+ l_term_size_get(); -+ l_sig_handler_init(); -+ return; -+fail: -+ ERR_EXIT("Could not initialize curses, try \"--batchmode\"\n"); -+} -+ -+/* -+ * Initialize data gatherer -+ */ -+#ifdef WITH_HYPFS -+static void l_dg_init(void) -+{ -+ if (dg_debugfs_init(0) == 0) -+ return; -+ if (dg_hypfs_init() == 0) -+ return; -+ ERR_EXIT("Could not initialize data gatherer\n"); -+} -+#else -+static void l_dg_init(void) -+{ -+ dg_debugfs_init(1); -+} -+#endif -+ -+/* -+ * Windows event loop -+ */ -+static void l_event_loop(void) -+{ -+ while (1) -+ g.w.cur->run(g.w.cur); -+} -+ -+/* -+ * Clear terminal and write new window content to it -+ */ -+void l_update_term_curses(void) -+{ -+ /* Init screen */ -+ l_term_size_get(); -+ curs_set(0); /* pervent cursor from blinking */ -+ move(0, 0); -+ erase(); -+ hyptop_printf_init(); -+ /* Write window to screen */ -+ g.w.cur->update_term(g.w.cur); -+ refresh(); -+} -+ -+/* -+ * Write window content in line mode -+ */ -+void l_update_term_batch(void) -+{ -+ g.w.cur->update_term(g.w.cur); -+ printf("\n"); -+} -+ -+/* -+ * Update terminal with new window content -+ */ -+void hyptop_update_term(void) -+{ -+ if (g.o.batch_mode_specified) -+ l_update_term_batch(); -+ else -+ l_update_term_curses(); -+} -+ -+/* -+ * Switch to new window "win" -+ */ -+enum hyptop_win_action win_switch(struct hyptop_win *win) -+{ -+ assert(g.w.prev_cnt < sizeof(g.w.prev) / sizeof(void *)); -+ g.w.prev[g.w.prev_cnt] = g.w.cur; -+ g.w.prev_cnt++; -+ g.w.cur = win; -+ return WIN_SWITCH; -+} -+ -+/* -+ * Switch back to previous window -+ */ -+enum hyptop_win_action win_back(void) -+{ -+ g.w.prev_cnt--; -+ g.w.cur = g.w.prev[g.w.prev_cnt]; -+ return WIN_SWITCH; -+} -+ -+/* -+ * Switch to text mode -+ */ -+void hyptop_text_mode(void) -+{ -+ if (!g.c.initialized) -+ return; -+ g.c.initialized = 0; -+ clear(); -+ refresh(); -+ endwin(); -+} -+ -+/* -+ * Exit hyptop -+ */ -+void hyptop_exit(int rc) -+{ -+ hyptop_text_mode(); -+ exit(rc); -+} -+ -+/* -+ * Initialize all modules and start first window -+ */ -+int main(int argc, char *argv[]) -+{ -+ opts_parse(argc, argv); -+ hyptop_helper_init(); -+ sd_init(); -+ l_dg_init(); -+ opt_verify_systems(); -+ l_term_init(); -+ -+ win_sys_list_init(); -+ win_sys_init(); -+ g.win_cpu_types = win_cpu_types_new(); -+ l_event_loop(); -+ return 0; -+} -diff --git a/hyptop/hyptop.h b/hyptop/hyptop.h -new file mode 100644 -index 0000000..fe39976 ---- /dev/null -+++ b/hyptop/hyptop.h -@@ -0,0 +1,220 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Command line options, window definition, print functions, etc. -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#ifndef HYPTOP_H -+#define HYPTOP_H -+ -+#include -+#include -+#include -+#include -+#include "list.h" -+#include "helper.h" -+#include "table.h" -+#include "nav_desc.h" -+ -+#define HYPTOP_OPT_DEFAULT_DELAY 2 -+#define HYPTOP_MAX_WIN_DEPTH 4 -+#define HYPTOP_MAX_LINE 512 -+#define PROG_NAME "hyptop" -+ -+/* -+ * Options info -+ */ -+struct hyptop_str_vec_opt { -+ unsigned int specified; -+ char **vec; -+ unsigned int cnt; -+}; -+ -+struct hyptop_col_vec_opt { -+ unsigned int specified; -+ struct table_col_spec **vec; -+ unsigned int cnt; -+}; -+ -+struct hyptop_win_opts { -+ struct hyptop_str_vec_opt sys; -+ struct hyptop_col_vec_opt fields; -+ unsigned int sort_field_specified; -+ char sort_field; -+}; -+ -+struct hyptop_opts { -+ unsigned int win_specified; -+ unsigned int batch_mode_specified; -+ unsigned int iterations_specified; -+ unsigned int iterations; -+ unsigned int iterations_act; -+ -+ struct hyptop_win *cur_win; -+ struct hyptop_str_vec_opt cpu_types; -+ -+ int delay_s; -+ int delay_us; -+}; -+ -+/* -+ * Curses info -+ */ -+struct hyptop_curses { -+ int row_cnt; -+ int col_cnt; -+ char line[HYPTOP_MAX_LINE]; -+ int x; -+ int y; -+ int initialized; -+}; -+ -+/* -+ * Window info -+ */ -+struct hyptop_win_info { -+ struct hyptop_win *cur; -+ struct hyptop_win *prev[HYPTOP_MAX_WIN_DEPTH]; -+ unsigned int prev_cnt; -+}; -+ -+/* -+ * Globals definition -+ */ -+struct hyptop_globals { -+ struct hyptop_opts o; -+ struct hyptop_curses c; -+ struct hyptop_win_info w; -+ const char *prog_name; -+ struct hyptop_win *win_cpu_types; -+}; -+ -+extern struct hyptop_globals g; -+ -+/* -+ * Print functions -+ */ -+#define hyptop_printf_pos(y, x, p...) \ -+ do { \ -+ if (g.o.batch_mode_specified) \ -+ printf(p); \ -+ else { \ -+ int len; \ -+ len = snprintf(g.c.line, sizeof(g.c.line) - 1, p); \ -+ len = MIN(len, (g.c.col_cnt - (x))); \ -+ if (len > 0) { \ -+ mvaddnstr((y), (x), g.c.line, len); \ -+ } \ -+ } \ -+ } while (0) -+ -+#define hyptop_printf(p...) \ -+ do { \ -+ if (g.o.batch_mode_specified) \ -+ printf(p); \ -+ else { \ -+ int len; \ -+ len = snprintf(g.c.line, sizeof(g.c.line) - 1, p); \ -+ len = MIN(len, (g.c.col_cnt - g.c.x)); \ -+ if (len > 0) { \ -+ mvaddnstr(g.c.y, g.c.x, g.c.line, len); \ -+ g.c.x += len; \ -+ } \ -+ } \ -+ } while (0) -+ -+static inline void hyptop_printf_init(void) -+{ -+ g.c.x = 0; -+ g.c.y = 0; -+} -+ -+static inline void hyptop_print_seek_back(int i) -+{ -+ unsigned int cnt = MAX(g.c.col_cnt - g.c.x - i, 0); -+ -+ if (g.o.batch_mode_specified) -+ return; -+ if (cnt) { -+ memset(g.c.line, ' ', cnt); -+ assert(cnt < sizeof(g.c.line)); -+ g.c.line[cnt] = 0; -+ addstr(g.c.line); -+ } -+ g.c.x = g.c.col_cnt - i; -+} -+ -+static inline void hyptop_print_nl(void) -+{ -+ unsigned int cnt = MAX(g.c.col_cnt - g.c.x, 0); -+ -+ if (g.o.batch_mode_specified) { -+ printf("\n"); -+ return; -+ } -+ if (cnt) { -+ memset(g.c.line, ' ', g.c.col_cnt - g.c.x); -+ assert(cnt < sizeof(g.c.line)); -+ g.c.line[cnt] = 0; -+ addstr(g.c.line); -+ } -+ g.c.x = 0; -+ g.c.y++; -+} -+ -+/* -+ * hyptop windows -+ */ -+ -+enum hyptop_win_action { -+ WIN_SWITCH, -+ WIN_KEEP, -+}; -+ -+extern enum hyptop_win_action hyptop_process_input_timeout(void); -+extern enum hyptop_win_action hyptop_process_input(void); -+extern enum hyptop_win_action win_switch(struct hyptop_win *w); -+extern enum hyptop_win_action win_back(void); -+ -+struct hyptop_win; -+struct hyptop_win { -+ enum hyptop_win_action (*process_input)(struct hyptop_win *w, int c); -+ void (*update_term)(struct hyptop_win *w); -+ void (*run)(struct hyptop_win *w); -+ const char *id; -+ const char *desc; -+ struct nav_desc **desc_normal_vec; -+ struct nav_desc **desc_select_vec; -+ struct nav_desc **desc_general_vec; -+ struct hyptop_win_opts opts; -+}; -+ -+/* -+ * Window sys_list -+ */ -+extern struct hyptop_win win_sys_list; -+extern void win_sys_list_init(void); -+ -+/* -+ * Window sys -+ */ -+extern struct hyptop_win win_sys; -+extern void win_sys_set(const char *sys_id); -+extern void win_sys_init(void); -+ -+/* -+ * Window cpu_types -+ */ -+extern void win_cpu_types_init(void); -+ -+/* -+ * Misc functions -+ */ -+extern void hyptop_update_term(void); -+extern void hyptop_exit(int rc); -+extern void hyptop_text_mode(void); -+ -+#endif /* HYPTOP_H */ -diff --git a/hyptop/nav_desc.c b/hyptop/nav_desc.c -new file mode 100644 -index 0000000..703489f ---- /dev/null -+++ b/hyptop/nav_desc.c -@@ -0,0 +1,243 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Description of navigation keys -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include -+#include -+#include -+#include "nav_desc.h" -+#include "tbox.h" -+ -+#define L_KEY_LEN 14 -+#define L_KEY_FMT "%-14s" -+ -+/* Select mode */ -+ -+struct nav_desc nav_desc_select_mode_enter = { -+ .desc = "Enter select mode", -+ .keys = {"RIGHT", "l", NULL}, -+}; -+ -+struct nav_desc nav_desc_select_mode_leave = { -+ .desc = "Leave select mode", -+ .keys = {"LEFT", "h", NULL}, -+}; -+ -+/* "sys" Window */ -+ -+struct nav_desc nav_desc_win_enter_sys = { -+ .desc = "Go to the \"sys\" window for selected system", -+ .keys = {"RIGHT", "l", NULL}, -+}; -+ -+struct nav_desc nav_desc_win_leave_sys = { -+ .desc = "Go to the previous window", -+ .keys = {"LEFT", "h", "q", NULL}, -+}; -+ -+struct nav_desc nav_desc_win_leave_sys_fast = { -+ .desc = "Go to the previous window", -+ .keys = {"q", NULL}, -+}; -+ -+/* "fields" window */ -+ -+struct nav_desc nav_desc_win_enter_fields = { -+ .desc = "Go to the \"fields\" window", -+ .keys = {"f", NULL}, -+} ; -+ -+struct nav_desc nav_desc_win_leave_fields = { -+ .desc = "Go to the previous window", -+ .keys = {"LEFT", "ENTER", "h", "f", "q", NULL}, -+}; -+ -+struct nav_desc nav_desc_win_leave_fields_fast = { -+ .desc = "Go to the previous window", -+ .keys = {"f", "q", NULL}, -+}; -+ -+/* "cpu_types" window */ -+ -+struct nav_desc nav_desc_win_enter_cpu_types = { -+ .desc = "Go to the \"cpu_types\" window", -+ .keys = {"t", NULL}, -+}; -+ -+struct nav_desc nav_desc_win_leave_cpu_types = { -+ .desc = "Go to the previous window", -+ .keys = {"LEFT", "ENTER", "h", "t", "q", NULL}, -+}; -+ -+struct nav_desc nav_desc_win_leave_cpu_types_fast = { -+ .desc = "Go to the previous window", -+ .keys = {"t", "q", NULL}, -+}; -+ -+/* Marks */ -+ -+struct nav_desc nav_desc_marks_clear = { -+ .desc = "Clear all marked rows", -+ .keys = {"SPACE", NULL}, -+}; -+ -+struct nav_desc nav_desc_mark_toggle = { -+ .desc = "Toggle mark for selected row", -+ .keys = {"SPACE", NULL}, -+}; -+ -+struct nav_desc nav_desc_mark_toggle_view = { -+ .desc = "Toggle view for marked rows", -+ .keys = {".", NULL}, -+}; -+ -+/* Units */ -+ -+struct nav_desc nav_desc_col_unit_increase = { -+ .desc = "Increase unit type of selected column", -+ .keys = {"+", NULL}, -+}; -+ -+struct nav_desc nav_desc_col_unit_decrease = { -+ .desc = "Decrease unit type of selected column", -+ .keys = {"-", NULL}, -+}; -+ -+struct nav_desc nav_desc_row_unit_increase = { -+ .desc = "Increase unit type of selected row", -+ .keys = {"+", NULL}, -+}; -+ -+struct nav_desc nav_desc_row_unit_decrease = { -+ .desc = "Decrease unit type of selected row", -+ .keys = {"-", NULL}, -+}; -+ -+/* Select columns */ -+ -+struct nav_desc nav_desc_select_col_next = { -+ .desc = "Select next column", -+ .keys = {">", NULL}, -+}; -+ -+struct nav_desc nav_desc_select_col_prev = { -+ .desc = "Select previous column", -+ .keys = {"<", NULL}, -+}; -+ -+struct nav_desc nav_desc_select_col_hotkey = { -+ .desc = "Select column with hotkey", -+ .keys = {"", NULL}, -+}; -+ -+/* Quit */ -+ -+struct nav_desc nav_desc_quit = { -+ .desc = "Quit program", -+ .keys = {"q", NULL}, -+}; -+ -+/* Select rows */ -+ -+struct nav_desc nav_desc_toggle_mark_hotkey = { -+ .desc = "Toggle mark for row with hotkey", -+ .keys = {"", NULL}, -+}; -+ -+/* Navigation */ -+ -+struct nav_desc nav_desc_scroll_up_line = { -+ .desc = "Scroll up one line", -+ .keys = {"UP", "k", NULL}, -+}; -+ -+struct nav_desc nav_desc_scroll_down_line = { -+ .desc = "Scroll down one line", -+ .keys = {"DOWN", "j", NULL}, -+}; -+ -+struct nav_desc nav_desc_scroll_up_page = { -+ .desc = "Scroll up one page", -+ .keys = {"PGUP", NULL}, -+}; -+ -+struct nav_desc nav_desc_scroll_down_page = { -+ .desc = "Scroll down one page", -+ .keys = {"PGDOWN", NULL}, -+}; -+ -+struct nav_desc nav_desc_scroll_up_head = { -+ .desc = "Scroll up to head of window", -+ .keys = {"g", NULL}, -+}; -+ -+struct nav_desc nav_desc_scroll_down_tail = { -+ .desc = "Scroll down to tail of window", -+ .keys = {"G", NULL}, -+}; -+ -+/* -+ * Add navigation descriptons to text box -+ */ -+static void l_nav_desc_add(struct tbox *tb, struct nav_desc *desc) -+{ -+ char keys_str[L_KEY_LEN + 1]; -+ unsigned int i, first; -+ char *key; -+ -+ first = 1; -+ keys_str[0] = 0; -+ for (i = 0; (key = desc->keys[i]); i++) { -+ /* -+ * If we have used the whole space for the keys, -+ * we write the line and begin a new one -+ */ -+ if (strlen(desc->keys[i]) + strlen(keys_str) + 1 > L_KEY_LEN) { -+ tbox_printf(tb, " " L_KEY_FMT ": %s", keys_str, -+ desc->desc); -+ keys_str[0] = 0; -+ first = 1; -+ } -+ if (!first) -+ strcat(keys_str, ","); -+ else -+ first = 0; -+ strcat(keys_str, "'"); -+ strcat(keys_str, desc->keys[i]); -+ strcat(keys_str, "'"); -+ assert(strlen(keys_str) <= L_KEY_LEN); -+ } -+ tbox_printf(tb, " " L_KEY_FMT ": %s", keys_str, desc->desc); -+} -+ -+/* -+ * Add navigation descriptions for "normal", "select" and "general" to text box -+ */ -+void nav_desc_add(struct tbox *tb, -+ struct nav_desc **desc_normal, -+ struct nav_desc **desc_select, -+ struct nav_desc **desc_general) -+{ -+ unsigned int i; -+ -+ tbox_printf(tb, "\\BSupported keys in this window\\B"); -+ tbox_printf(tb, " "); -+ -+ tbox_printf(tb, "NORMAL MODE:"); -+ for (i = 0; (desc_normal[i]); i++) -+ l_nav_desc_add(tb, desc_normal[i]); -+ tbox_printf(tb, " "); -+ tbox_printf(tb, "SELECT MODE:"); -+ for (i = 0; (desc_select[i]); i++) -+ l_nav_desc_add(tb, desc_select[i]); -+ tbox_printf(tb, " "); -+ tbox_printf(tb, "GENERAL:"); -+ for (i = 0; (desc_general[i]); i++) -+ l_nav_desc_add(tb, desc_general[i]); -+ tbox_printf(tb, " "); -+} -diff --git a/hyptop/nav_desc.h b/hyptop/nav_desc.h -new file mode 100644 -index 0000000..05fcde9 ---- /dev/null -+++ b/hyptop/nav_desc.h -@@ -0,0 +1,55 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Description of navigation keys -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#ifndef NAV_DESC_H -+#define NAV_DESC_H -+ -+#include "tbox.h" -+ -+struct nav_desc { -+ char *desc; -+ char *keys[]; -+}; -+ -+void nav_desc_add(struct tbox *tb, -+ struct nav_desc **desc_normal, -+ struct nav_desc **desc_select, -+ struct nav_desc **desc_general); -+ -+struct nav_desc nav_desc_quit; -+struct nav_desc nav_desc_select_mode_enter; -+struct nav_desc nav_desc_select_mode_leave; -+struct nav_desc nav_desc_win_enter_sys; -+struct nav_desc nav_desc_win_leave_sys; -+struct nav_desc nav_desc_win_leave_sys_fast; -+struct nav_desc nav_desc_win_enter_fields; -+struct nav_desc nav_desc_win_leave_fields; -+struct nav_desc nav_desc_win_leave_fields_fast; -+struct nav_desc nav_desc_win_enter_cpu_types; -+struct nav_desc nav_desc_win_leave_cpu_types; -+struct nav_desc nav_desc_win_leave_cpu_types_fast; -+struct nav_desc nav_desc_marks_clear; -+struct nav_desc nav_desc_mark_toggle; -+struct nav_desc nav_desc_mark_toggle_view; -+struct nav_desc nav_desc_col_unit_increase; -+struct nav_desc nav_desc_col_unit_decrease; -+struct nav_desc nav_desc_row_unit_increase; -+struct nav_desc nav_desc_row_unit_decrease; -+struct nav_desc nav_desc_select_col_next; -+struct nav_desc nav_desc_select_col_prev; -+struct nav_desc nav_desc_select_col_hotkey; -+struct nav_desc nav_desc_toggle_mark_hotkey; -+struct nav_desc nav_desc_scroll_up_line; -+struct nav_desc nav_desc_scroll_down_line; -+struct nav_desc nav_desc_scroll_up_page; -+struct nav_desc nav_desc_scroll_down_page; -+struct nav_desc nav_desc_scroll_up_head; -+struct nav_desc nav_desc_scroll_down_tail; -+ -+#endif /* NAV_DESC_H */ -diff --git a/hyptop/opts.c b/hyptop/opts.c -new file mode 100644 -index 0000000..05a4126 ---- /dev/null -+++ b/hyptop/opts.c -@@ -0,0 +1,416 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Command line parsing -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include -+#include -+#include "zt_common.h" -+#include "helper.h" -+#include "hyptop.h" -+#include "getopt.h" -+#include "sd.h" -+ -+static const char l_copyright_str[] = "Copyright IBM Corp. 2010"; -+ -+/* -+ * Help text for tool -+ */ -+static char HELP_TEXT[] = -+"Usage: hyptop [OPTIONS]\n" -+"\n" -+"Show hypervisor performance data on System z.\n" -+"\n" -+"-h, --help Print this help, then exit\n" -+"-v, --version Print version information, then exit\n" -+"-w, --window WIN_NAME Current window (\"sys\" or \"sys_list\")\n" -+"-s, --sys SYSTEM[,..] Systems for current window\n" -+"-f, --fields LETTER[:UNIT][,..] Fields and units for current window\n" -+"-S, --sort LETTER Sort field for current window\n" -+"-t, --cpu_types TYPE[,..] CPU types used for time calculations\n" -+"-b, --batch_mode Use batch mode (no curses)\n" -+"-d, --delay SECONDS Delay time between screen updates\n" -+"-n, --iterations NUMBER Number of iterations before ending\n"; -+ -+/* -+ * Initialize default settings -+ */ -+static void l_init_defaults(void) -+{ -+ g.prog_name = PROG_NAME; -+ g.o.delay_s = HYPTOP_OPT_DEFAULT_DELAY; -+ g.w.cur = &win_sys_list; -+ g.o.cur_win = &win_sys_list; -+} -+ -+/* -+ * Print "help" hint -+ */ -+static void l_std_usage_exit(void) -+{ -+ fprintf(stderr, "Try '%s --help' for more information.\n", -+ g.prog_name); -+ hyptop_exit(1); -+} -+ -+/* -+ * Print help text -+ */ -+static void l_usage(void) -+{ -+ printf("%s", HELP_TEXT); -+} -+ -+/* -+ * Print version information -+ */ -+static void l_print_version(void) -+{ -+ printf("%s: Hypervisor Top version %s\n", g.prog_name, RELEASE_STRING); -+ printf("%s\n", l_copyright_str); -+} -+ -+/* -+ * Check if string is a number -+ */ -+static int l_number_check(const char *str) -+{ -+ const char *ptr = str; -+ while (*ptr) { -+ if (!isdigit(*ptr)) -+ ERR_EXIT("The argument \"%s\" is not an integer\n", -+ str); -+ ptr++; -+ } -+ return 1; -+} -+ -+/* -+ * Set delay option -+ */ -+static void l_delay_set(char *delay_string) -+{ -+ int secs; -+ -+ l_number_check(delay_string); -+ if (sscanf(delay_string, "%i", &secs) != 1) -+ ERR_EXIT("The delay value \"%s\" is invalid\n", delay_string); -+ g.o.delay_s = secs; -+ g.o.delay_us = 0; -+} -+ -+/* -+ * Get number of occurances of character 'c' in "str" -+ */ -+static int l_get_char_cnt(char *str, char c) -+{ -+ unsigned int i; -+ int cnt = 0; -+ -+ for (i = 0; str[i] != 0; i++) { -+ if (str[i] == c) -+ cnt++; -+ } -+ return cnt; -+} -+ -+/* -+ * Return copy of string with removed trailing and leading blanks -+ */ -+static char *l_trim_str_new(char *str) -+{ -+ char *rc; -+ int i; -+ -+ for (i = 0; *(str + i) == ' '; i++) {} -+ rc = ht_strdup(str + i); -+ ht_strstrip(rc); -+ if (strlen(rc) == 0) -+ ERR_EXIT("The argument \"%s\" is invalid\n", str); -+ return rc; -+} -+ -+/* -+ * Get column specification for string -+ */ -+static struct table_col_spec *l_get_col_spec(char *str) -+{ -+ struct table_col_spec *col_spec; -+ unsigned int i; -+ char *key_str; -+ -+ col_spec = ht_zalloc(sizeof(*col_spec)); -+ -+ for (i = strlen(str); i > 0; i--) { -+ if (str[i] == ':') { -+ col_spec->unit_str = l_trim_str_new(&str[i + 1]); -+ str[i] = 0; -+ } -+ } -+ key_str = l_trim_str_new(str); -+ if (strlen(key_str) > 1) -+ ERR_EXIT("The field key \"%s\" is invalid\n", key_str); -+ col_spec->hotkey = key_str[0]; -+ ht_free(key_str); -+ return col_spec; -+} -+ -+/* -+ * Set the "--fields" option -+ */ -+static void l_fields_set(char *str) -+{ -+ struct hyptop_col_vec_opt *opt = &g.o.cur_win->opts.fields; -+ unsigned int i, j; -+ -+ opt->cnt = l_get_char_cnt(str, ',') + 1; -+ opt->vec = ht_zalloc(sizeof(void *) * (opt->cnt + 1)); -+ -+ j = 0; -+ for (i = strlen(str); i > 0; i--) { -+ if (str[i] != ',') -+ continue; -+ opt->vec[j] = l_get_col_spec(&str[i + 1]); -+ str[i] = 0; -+ j++; -+ } -+ opt->vec[j] = l_get_col_spec(str); -+ opt->specified = 1; -+} -+ -+/* -+ * Set the "--sort_field" option -+ */ -+static void l_sort_field_set(char *str) -+{ -+ if (strlen(str) > 1) -+ ERR_EXIT("The sort field \"%s\" is invalid\n", str); -+ if (g.o.cur_win->opts.sort_field_specified && -+ g.o.cur_win->opts.sort_field != str[0]) -+ g.o.cur_win->opts.sort_field_specified = 0; -+ g.o.cur_win->opts.sort_field_specified++; -+ g.o.cur_win->opts.sort_field = str[0]; -+} -+ -+/* -+ * Setup a string vector out of a comma separated list in "str" -+ */ -+static void l_str_vec_set(char *str, struct hyptop_str_vec_opt *opt) -+{ -+ unsigned int i, j; -+ -+ opt->cnt = l_get_char_cnt(str, ',') + 1; -+ opt->vec = ht_zalloc(sizeof(void *) * (opt->cnt + 1)); -+ -+ j = 0; -+ for (i = strlen(str); i > 0; i--) { -+ if (str[i] != ',') -+ continue; -+ opt->vec[j] = l_trim_str_new(&str[i + 1]); -+ str[i] = 0; -+ j++; -+ } -+ opt->vec[j] = l_trim_str_new(str); -+ opt->specified = 1; -+} -+ -+/* -+ * Set the "--sys" option -+ */ -+static void l_sys_set(char *str) -+{ -+ l_str_vec_set(str, &g.o.cur_win->opts.sys); -+} -+ -+/* -+ * Set the "--cpu_types" option -+ */ -+static void l_cpu_types_set(char *str) -+{ -+ l_str_vec_set(str, &g.o.cpu_types); -+} -+ -+/* -+ * Set the "--window" option -+ */ -+static void l_window_set(const char *str) -+{ -+ g.o.win_specified = 1; -+ if (strcmp(str, win_sys_list.id) == 0) -+ g.o.cur_win = &win_sys_list; -+ else if (strcmp(str, win_sys.id) == 0) -+ g.o.cur_win = &win_sys; -+ else -+ ERR_EXIT("The window \"%s\" is unknown\n", str); -+} -+ -+/* -+ * Set the "--iterations" option -+ */ -+static void l_iterations_set(const char *str) -+{ -+ l_number_check(str); -+ g.o.iterations_specified = 1; -+ g.o.iterations = atoi(str); -+} -+ -+/* -+ * Set the "--batch_mode" option -+ */ -+static void l_batch_mode_set(void) -+{ -+ g.o.batch_mode_specified = 1; -+} -+ -+/* -+ * Make option consisteny checks at end of command line parsing -+ */ -+static void l_parse_finish(void) -+{ -+ if (g.o.iterations_specified && g.o.iterations == 0) -+ hyptop_exit(0); -+ if (g.o.cur_win != &win_sys) -+ return; -+ if (!win_sys.opts.sys.specified) -+ ERR_EXIT("Specify a system for window \"sys\"\n"); -+ if (win_sys.opts.sys.cnt != 1) -+ ERR_EXIT("More than one system for window \"sys\" has been " -+ "specified\n"); -+ win_switch(&win_sys); -+} -+ -+/* -+ * Main command line parsing function -+ */ -+void opts_parse(int argc, char *argv[]) -+{ -+ int opt, index; -+ static struct option long_options[] = { -+ { "version", no_argument, NULL, 'v'}, -+ { "help", no_argument, NULL, 'h'}, -+ { "batch_mode", no_argument, NULL, 'b'}, -+ { "delay", required_argument, NULL, 'd'}, -+ { "window", required_argument, NULL, 'w'}, -+ { "sys", required_argument, NULL, 's'}, -+ { "iterations", required_argument, NULL, 'n'}, -+ { "fields", required_argument, NULL, 'f'}, -+ { "sort_field", required_argument, NULL, 'S'}, -+ { "cpu_types", required_argument, NULL, 't'}, -+ { 0, 0, 0, 0 } -+ }; -+ static const char option_string[] = "vhbd:w:s:n:f:t:S:"; -+ -+ l_init_defaults(); -+ while (1) { -+ opt = getopt_long(argc, argv, option_string, -+ long_options, &index); -+ if (opt == -1) -+ break; -+ switch (opt) { -+ case 'v': -+ l_print_version(); -+ hyptop_exit(0); -+ case 'h': -+ l_usage(); -+ hyptop_exit(0); -+ case 'b': -+ l_batch_mode_set(); -+ break; -+ case 'd': -+ l_delay_set(optarg); -+ break; -+ case 'w': -+ l_window_set(optarg); -+ break; -+ case 's': -+ l_sys_set(optarg); -+ break; -+ case 'n': -+ l_iterations_set(optarg); -+ break; -+ case 't': -+ l_cpu_types_set(optarg); -+ break; -+ case 'f': -+ l_fields_set(optarg); -+ break; -+ case 'S': -+ l_sort_field_set(optarg); -+ break; -+ default: -+ l_std_usage_exit(); -+ } -+ } -+ if (optind != argc) -+ ERR_EXIT("Invalid positional parameter \"%s\" specified\n", -+ argv[optind]); -+ l_parse_finish(); -+} -+ -+/* -+ * Has "sys_name" been specified on command line? -+ */ -+int opts_sys_specified(struct hyptop_win *win, const char* sys_name) -+{ -+ unsigned int i; -+ -+ if (!win->opts.sys.specified) -+ return 1; -+ for (i = 0; i < win->opts.sys.cnt; i++) { -+ if (strcmp(win->opts.sys.vec[i], sys_name) == 0) -+ return 1; -+ } -+ return 0; -+} -+ -+/* -+ * Verify that all specified systems are available for window -+ */ -+static void l_verify_systems(struct hyptop_win *win) -+{ -+ char *sys_name; -+ unsigned int i; -+ -+ for (i = 0; i < win->opts.sys.cnt; i++) { -+ if (sd_sys_get(sd_sys_root_get(), win->opts.sys.vec[i])) -+ continue; -+ sys_name = ht_strdup(win->opts.sys.vec[i]); -+ ht_str_to_upper(win->opts.sys.vec[i]); -+ if (sd_sys_get(sd_sys_root_get(), win->opts.sys.vec[i])) { -+ ht_free(sys_name); -+ continue; -+ } -+ ERR_EXIT("System \"%s\" is not available\n", sys_name); -+ } -+} -+ -+/* -+ * Verify that all specified systems are available for all windows -+ */ -+void opt_verify_systems(void) -+{ -+ l_verify_systems(&win_sys_list); -+ l_verify_systems(&win_sys); -+ if (g.o.cur_win == &win_sys) -+ win_sys_set(win_sys.opts.sys.vec[0]); -+} -+ -+/* -+ * Increase iterations count and exit if necessary -+ */ -+void opts_iterations_next(void) -+{ -+ if (g.o.iterations_specified) { -+ g.o.iterations_act++; -+ if (g.o.iterations_act >= g.o.iterations) -+ hyptop_exit(0); -+ } -+ if (g.o.batch_mode_specified) -+ printf("---------------------------------------------------" -+ "----------------------------\n"); -+} -+ -diff --git a/hyptop/opts.h b/hyptop/opts.h -new file mode 100644 -index 0000000..babeda1 ---- /dev/null -+++ b/hyptop/opts.h -@@ -0,0 +1,20 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Command line parsing -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#ifndef OPTS_H -+#define OPTS_H -+ -+#include "hyptop.h" -+ -+extern void opts_parse(int argc, char *argv[]); -+extern void opts_iterations_next(void); -+extern int opts_sys_specified(struct hyptop_win *win, const char* sys_name); -+extern void opt_verify_systems(void); -+ -+#endif /* OPTS_H */ -diff --git a/hyptop/sd.h b/hyptop/sd.h -new file mode 100644 -index 0000000..7dd4c93 ---- /dev/null -+++ b/hyptop/sd.h -@@ -0,0 +1,479 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * System data module: Provide database for system data (e.g. CPU and memory) -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#ifndef SD_H -+#define SD_H -+ -+#include "helper.h" -+#include "table.h" -+ -+#define SD_DG_INIT_INTERVAL_MS 200 -+#define SD_SYS_ID_SIZE 9 -+ -+/* -+ * CPU info -+ */ -+struct sd_cpu_info { -+ u64 cpu_time_us; -+ u64 mgm_time_us; -+ u64 wait_time_us; -+ s64 steal_time_us; -+ u64 online_time_us; -+}; -+ -+/* -+ * Memory Info -+ */ -+struct sd_mem { -+ u64 min_kib; -+ u64 max_kib; -+ u64 use_kib; -+}; -+ -+/* -+ * Weight -+ */ -+struct sd_weight { -+ u16 cur; -+ u16 min; -+ u16 max; -+}; -+ -+/* -+ * System Name -+ */ -+struct sd_sys_name { -+ char os[9]; -+}; -+ -+struct sd_sys; -+ -+/* -+ * SD info -+ */ -+struct sd_info { -+ u8 active; -+ struct sd_sys *parent; -+}; -+ -+struct sd_cpu; -+ -+/* -+ * SD System (can be e.g. CEC, VM or guest/LPAR) -+ */ -+struct sd_sys { -+ struct list list; -+ struct sd_info i; -+ u64 update_time_us; -+ u32 child_cnt; -+ u32 child_cnt_active; -+ struct list child_list; -+ u32 cpu_cnt; -+ u32 cpu_cnt_active; -+ struct list cpu_list; -+ char id[SD_SYS_ID_SIZE]; -+ struct sd_sys_name name; -+ struct sd_mem mem; -+ struct sd_weight weight; -+}; -+ -+#define sd_sys_id(sys) ((sys)->id) -+#define sd_sys_name_os(sys) ((sys)->name.os) -+ -+void sd_sys_update_start(struct sd_sys *sys); -+void sd_sys_update_end(struct sd_sys *sys, u64 update_time_us); -+struct sd_sys *sd_sys_root_get(void); -+struct sd_sys *sd_sys_get(struct sd_sys *parent, const char *id); -+struct sd_sys *sd_sys_new(struct sd_sys *parent, const char *id); -+ -+static inline void sd_sys_weight_cur_set(struct sd_sys *sys, u64 value) -+{ -+ sys->weight.cur = value; -+} -+ -+static inline void sd_sys_weight_min_set(struct sd_sys *sys, u64 value) -+{ -+ sys->weight.min = value; -+} -+ -+static inline void sd_sys_weight_max_set(struct sd_sys *sys, u64 value) -+{ -+ sys->weight.max = value; -+} -+ -+static inline void sd_sys_mem_use_kib_set(struct sd_sys *sys, u64 value) -+{ -+ sys->mem.use_kib = value; -+} -+ -+static inline void sd_sys_mem_min_kib_set(struct sd_sys *sys, u64 value) -+{ -+ sys->mem.min_kib = value; -+} -+ -+static inline void sd_sys_mem_max_kib_set(struct sd_sys *sys, u64 value) -+{ -+ sys->mem.max_kib = value; -+} -+ -+static inline void sd_sys_update_time_us_set(struct sd_sys *sys, u64 value) -+{ -+ sys->update_time_us = value; -+} -+ -+/* -+ * CPU type -+ */ -+#define CPU_TYPE_ID_LEN 16 -+#define CPU_TYPE_DESC_LEN 64 -+ -+#define SD_CPU_TYPE_STR_IFL "IFL" -+#define SD_CPU_TYPE_STR_CP "CP" -+#define SD_CPU_TYPE_STR_UN "UN" -+ -+struct sd_cpu_type { -+ char id[CPU_TYPE_ID_LEN]; -+ char desc[CPU_TYPE_DESC_LEN]; -+ u32 idx; -+ int cpu_cnt; -+ char hotkey; -+}; -+ -+#define sd_cpu_type_id(type) (type->id) -+#define sd_cpu_type_desc(type) (type->desc) -+ -+int sd_cpu_type_selected(struct sd_cpu_type *cpu_type); -+void sd_cpu_type_select_toggle(struct sd_cpu_type *cpu_type); -+void sd_cpu_type_select(struct sd_cpu_type *cpu_type); -+void sd_cpu_type_select_all(void); -+void sd_cpu_type_select_none(void); -+struct sd_cpu_type *sd_cpu_type_by_id(const char *id); -+ -+static inline int sd_cpu_type_cpu_cnt(struct sd_cpu_type *type) -+{ -+ return type->cpu_cnt; -+} -+ -+static inline void sd_sys_commit(struct sd_sys *sys) -+{ -+ struct sd_sys *parent = sys->i.parent; -+ -+ sys->i.active = 1; -+ if (parent) -+ parent->child_cnt_active++; -+} -+ -+extern struct sd_cpu_type sd_cpu_type_ifl; -+extern struct sd_cpu_type sd_cpu_type_cp; -+extern struct sd_cpu_type sd_cpu_type_un; -+ -+/* -+ * SD CPU -+ */ -+enum sd_cpu_state { -+ SD_CPU_STATE_UNKNOWN = 0, -+ SD_CPU_STATE_OPERATING = 1, -+ SD_CPU_STATE_STOPPED = 2, -+ SD_CPU_STATE_DECONFIG = 3, -+}; -+ -+struct sd_cpu { -+ struct list list; -+ struct sd_info i; -+ char id[9]; -+ struct sd_cpu_type *type; -+ char real_type[CPU_TYPE_ID_LEN]; -+ struct sd_cpu_info d1; -+ struct sd_cpu_info d2; -+ struct sd_cpu_info *d_cur; -+ struct sd_cpu_info *d_prev; -+ u16 cnt; -+ enum sd_cpu_state state; -+}; -+ -+static inline char *sd_cpu_state_str(enum sd_cpu_state state) -+{ -+ static char *state_str[] = {"UK", "OP", "ST", "DC"}; -+ -+ return state_str[(int) state]; -+} -+ -+#define sd_cpu_has_diff(cpu) (cpu->d_prev != NULL) -+#define sd_cpu_diff(cpu, member) (cpu->d_cur->member - cpu->d_prev->member) -+ -+#define sd_cpu_id(cpu) (cpu->id) -+#define sd_cpu_cnt(cpu) (cpu->cnt) -+#define sd_cpu_type_str(cpu) (cpu->type->id) -+#define sd_cpu_state(cpu) (cpu->state) -+ -+struct sd_cpu *sd_cpu_get(struct sd_sys *sys, const char *cpu_id); -+struct sd_cpu *sd_cpu_new(struct sd_sys *parent, const char *id, -+ const char *type, int cnt); -+ -+static inline void sd_cpu_state_set(struct sd_cpu *cpu, enum sd_cpu_state state) -+{ -+ cpu->state = state; -+} -+ -+static inline void sd_cpu_real_type_set(struct sd_cpu *cpu, const char *type) -+{ -+ strncpy(cpu->real_type, type, sizeof(cpu->real_type)); -+} -+ -+static inline void sd_cpu_cpu_time_us_set(struct sd_cpu *cpu, u64 value) -+{ -+ cpu->d_cur->cpu_time_us = value; -+} -+ -+static inline void sd_cpu_mgm_time_us_set(struct sd_cpu *cpu, u64 value) -+{ -+ cpu->d_cur->mgm_time_us = value; -+} -+ -+static inline void sd_cpu_wait_time_us_set(struct sd_cpu *cpu, u64 value) -+{ -+ cpu->d_cur->wait_time_us = value; -+} -+ -+static inline void sd_cpu_steal_time_us_set(struct sd_cpu *cpu, s64 value) -+{ -+ cpu->d_cur->steal_time_us = value; -+} -+ -+static inline void sd_cpu_online_time_us_set(struct sd_cpu *cpu, u64 value) -+{ -+ cpu->d_cur->online_time_us = value; -+} -+ -+static inline void sd_cpu_commit(struct sd_cpu *cpu) -+{ -+ struct sd_sys *parent = cpu->i.parent; -+ -+ cpu->i.active = 1; -+ if (parent) -+ parent->cpu_cnt_active++; -+} -+ -+/* -+ * Item types -+ */ -+enum sd_item_type { -+ SD_TYPE_U16, -+ SD_TYPE_U32, -+ SD_TYPE_U64, -+ SD_TYPE_S64, -+ SD_TYPE_STR, -+}; -+ -+/* -+ * CPU item -+ */ -+struct sd_cpu_item { -+ struct table_col table_col; -+ enum sd_item_type type; -+ int offset; -+ char *desc; -+ u64 (*fn_u64)(struct sd_cpu_item *, struct sd_cpu *); -+ s64 (*fn_s64)(struct sd_cpu_item *, struct sd_cpu *); -+ char *(*fn_str)(struct sd_cpu_item *, struct sd_cpu *); -+}; -+ -+#define sd_cpu_item_type(x) ((x)->type) -+#define sd_cpu_item_table_col(item) (&(item)->table_col) -+ -+extern int sd_cpu_item_available(struct sd_cpu_item *item); -+extern int sd_cpu_item_cnt(void); -+ -+/* -+ * Item access functions -+ */ -+static inline u64 sd_cpu_item_u64(struct sd_cpu_item *item, -+ struct sd_cpu *cpu) -+{ -+ return item->fn_u64(item, cpu); -+} -+ -+static inline u64 sd_cpu_item_s64(struct sd_cpu_item *item, -+ struct sd_cpu *cpu) -+{ -+ return item->fn_s64(item, cpu); -+} -+ -+static inline char *sd_cpu_item_str(struct sd_cpu_item *item, -+ struct sd_cpu *cpu) -+{ -+ if (item->fn_str) -+ return item->fn_str(item, cpu); -+ else -+ return ((char *) cpu) + item->offset; -+} -+ -+/* -+ * Predefined CPU items -+ */ -+extern struct sd_cpu_item sd_cpu_item_type; -+extern struct sd_cpu_item sd_cpu_item_state; -+extern struct sd_cpu_item sd_cpu_item_cpu_diff; -+extern struct sd_cpu_item sd_cpu_item_mgm_diff; -+extern struct sd_cpu_item sd_cpu_item_wait_diff; -+extern struct sd_cpu_item sd_cpu_item_steal_diff; -+extern struct sd_cpu_item sd_cpu_item_cpu; -+extern struct sd_cpu_item sd_cpu_item_mgm; -+extern struct sd_cpu_item sd_cpu_item_wait; -+extern struct sd_cpu_item sd_cpu_item_steal; -+extern struct sd_cpu_item sd_cpu_item_online; -+ -+/* -+ * System item -+ */ -+struct sd_sys_item { -+ struct table_col table_col; -+ enum sd_item_type type; -+ int offset; -+ char *desc; -+ int info; -+ u64 (*fn_u64)(struct sd_sys_item *, struct sd_sys *); -+ s64 (*fn_s64)(struct sd_sys_item *, struct sd_sys *); -+}; -+ -+#define sd_sys_item_table_col(item) (&item->table_col) -+#define sd_sys_item_type(item) (item->type) -+ -+extern int sd_sys_item_available(struct sd_sys_item *item); -+extern int sd_sys_item_cnt(void); -+ -+/* -+ * Item access functions -+ */ -+static inline u64 sd_sys_item_u64(struct sd_sys *sys, -+ struct sd_sys_item *item) -+{ -+ return item->fn_u64(item, sys); -+} -+ -+static inline s64 sd_sys_item_s64(struct sd_sys *sys, -+ struct sd_sys_item *item) -+{ -+ return item->fn_s64(item, sys); -+} -+ -+static inline char *sd_sys_item_str(struct sd_sys *sys, -+ struct sd_sys_item *item) -+{ -+ return ((char *) sys) + item->offset; -+} -+ -+/* -+ * Predefined System items -+ */ -+extern struct sd_sys_item sd_sys_item_cpu_cnt; -+extern struct sd_sys_item sd_sys_item_cpu_oper_cnt; -+extern struct sd_sys_item sd_sys_item_cpu_deconf_cnt; -+extern struct sd_sys_item sd_sys_item_cpu_stop_cnt; -+extern struct sd_sys_item sd_sys_item_cpu_diff; -+extern struct sd_sys_item sd_sys_item_mgm_diff; -+extern struct sd_sys_item sd_sys_item_wait_diff; -+extern struct sd_sys_item sd_sys_item_steal_diff; -+ -+extern struct sd_sys_item sd_sys_item_cpu; -+extern struct sd_sys_item sd_sys_item_mgm; -+extern struct sd_sys_item sd_sys_item_wait; -+extern struct sd_sys_item sd_sys_item_steal; -+extern struct sd_sys_item sd_sys_item_online; -+ -+extern struct sd_sys_item sd_sys_item_mem_max; -+extern struct sd_sys_item sd_sys_item_mem_min; -+extern struct sd_sys_item sd_sys_item_mem_use; -+ -+extern struct sd_sys_item sd_sys_item_weight_cur; -+extern struct sd_sys_item sd_sys_item_weight_min; -+extern struct sd_sys_item sd_sys_item_weight_max; -+ -+extern struct sd_sys_item sd_sys_item_os_name; -+ -+extern struct sd_sys_item sd_sys_item_samples_total; -+extern struct sd_sys_item sd_sys_item_samples_cpu_using; -+ -+/* -+ * Data gatherer backend -+ */ -+struct sd_dg { -+ void (*update_sys)(void); -+ struct sd_cpu_type **cpu_type_vec; -+ struct sd_sys_item **sys_item_vec; -+ struct sd_sys_item **sys_item_enable_vec; -+ struct sd_cpu_item **cpu_item_vec; -+ struct sd_cpu_item **cpu_item_enable_vec; -+}; -+ -+void sd_dg_register(struct sd_dg *); -+ -+/* -+ * Iterators -+ */ -+#define sd_sys_iterate(parent, sys) \ -+ list_iterate(sys, &parent->child_list, list) -+ -+#define sd_cpu_iterate(parent, cpuptr) \ -+ list_iterate(cpu, &parent->cpu_list, list) -+ -+#define sd_sys_item_iterate(ptr, i) \ -+ for (i = 0; (ptr = sd.dg->sys_item_vec[i]); i++) -+ -+#define sd_sys_item_enable_iterate(ptr, i) \ -+ for (i = 0; (ptr = sd.dg->sys_item_enable_vec[i]); i++) -+ -+#define sd_cpu_item_iterate(ptr, i) \ -+ for (i = 0; (ptr = sd.dg->cpu_item_vec[i]); i++) -+ -+#define sd_cpu_item_enable_iterate(ptr, i) \ -+ for (i = 0; (ptr = sd.dg->cpu_item_enable_vec[i]); i++) -+ -+#define sd_cpu_type_iterate(ptr, i) \ -+ for (i = 0; (ptr = sd.dg->cpu_type_vec[i]); i++) -+ -+ -+/* -+ * Offset macros -+ */ -+#define SD_SYSTEM_OFFSET(x) \ -+ ((unsigned long)(void *)&(((struct sd_sys *) NULL)->x)) -+#define SD_CPU_INFO_OFFSET(x) \ -+ ((unsigned long)(void *)&(((struct sd_cpu_info *) NULL)->x)) -+ -+static inline u64 l_cpu_info_u64(struct sd_cpu_info *info, -+ unsigned long offset) -+{ -+ return *(u64 *)(((char *) info) + offset); -+} -+ -+static inline s64 l_cpu_info_s64(struct sd_cpu_info *info, -+ unsigned long offset) -+{ -+ return *(s64 *)(((char *) info) + offset); -+} -+ -+/* -+ * Misc -+ */ -+void sd_update(void); -+extern void sd_init(void); -+ -+static inline u64 l_sub_64(u64 x, u64 y) -+{ -+ return x < y ? 0 : x - y; -+} -+ -+struct sd_globals { -+ struct sd_dg *dg; -+}; -+ -+extern struct sd_globals sd; -+ -+#endif /* SD_H */ -diff --git a/hyptop/sd_core.c b/hyptop/sd_core.c -new file mode 100644 -index 0000000..c3aeaa0 ---- /dev/null -+++ b/hyptop/sd_core.c -@@ -0,0 +1,435 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * System data module: Provide backend independent database for system data -+ * (e.g. for CPU and memory data) -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include -+#include -+#include "sd.h" -+#include "hyptop.h" -+#include "helper.h" -+#include "opts.h" -+ -+/* -+ * Internal globals for system data -+ */ -+static u32 l_cpu_type_selected_mask; -+static int l_cpu_type_cnt; -+static int l_sys_item_cnt; -+static int l_cpu_item_cnt; -+static struct sd_sys *l_root_sys; -+ -+/* -+ * External globals for system data -+ */ -+struct sd_globals sd; -+ -+/* -+ * Get root system -+ */ -+struct sd_sys *sd_sys_root_get(void) -+{ -+ return l_root_sys; -+} -+ -+/* -+ * Get CPU type by it's ID -+ */ -+struct sd_cpu_type *sd_cpu_type_by_id(const char *id) -+{ -+ struct sd_cpu_type *type; -+ unsigned int i; -+ -+ sd_cpu_type_iterate(type, i) { -+ if (strcasecmp(id, type->id) == 0) -+ return type; -+ } -+ return NULL; -+} -+ -+/* -+ * Is CPU type selected? -+ */ -+int sd_cpu_type_selected(struct sd_cpu_type *cpu_type) -+{ -+ return l_cpu_type_selected_mask & cpu_type->idx; -+} -+ -+/* -+ * Toggle selection of CPU type -+ */ -+void sd_cpu_type_select_toggle(struct sd_cpu_type *cpu_type) -+{ -+ if (l_cpu_type_selected_mask & cpu_type->idx) -+ l_cpu_type_selected_mask &= ~cpu_type->idx; -+ else -+ l_cpu_type_selected_mask |= cpu_type->idx; -+} -+ -+/* -+ * Select exactly specified CPU type -+ */ -+void sd_cpu_type_select(struct sd_cpu_type *cpu_type) -+{ -+ l_cpu_type_selected_mask = cpu_type->idx; -+} -+ -+/* -+ * Select all available CPU types -+ */ -+void sd_cpu_type_select_all(void) -+{ -+ l_cpu_type_selected_mask = (u32)-1; -+} -+ -+/* -+ * Deselect all CPU types -+ */ -+void sd_cpu_type_select_none(void) -+{ -+ l_cpu_type_selected_mask = 0; -+} -+ -+/* -+ * Setup CPU types specified on command line -+ */ -+static void l_opts_cpu_types_init(void) -+{ -+ struct sd_cpu_type *type; -+ unsigned int i; -+ -+ if (!g.o.cpu_types.specified) -+ return; -+ -+ sd_cpu_type_select_none(); -+ for (i = 0; i < g.o.cpu_types.cnt; i++) { -+ type = sd_cpu_type_by_id(g.o.cpu_types.vec[i]); -+ if (!type) -+ ERR_EXIT("Invalid CPU type \"%s\"\n", -+ g.o.cpu_types.vec[i]); -+ sd_cpu_type_select_toggle(type); -+ } -+} -+ -+/* -+ * Init CPU count for all CPU types -+ */ -+static void l_cpu_types_init(void) -+{ -+ struct sd_sys *sys = sd_sys_root_get(); -+ struct sd_cpu_type *cpu_type; -+ unsigned int i; -+ -+ sd_cpu_type_iterate(cpu_type, i) { -+ sd_cpu_type_select(cpu_type); -+ cpu_type->cpu_cnt = sd_sys_item_u64(sys, &sd_sys_item_cpu_cnt); -+ } -+ sd_cpu_type_select_all(); -+ l_opts_cpu_types_init(); -+} -+ -+/* -+ * Update system data using the data gatherer -+ */ -+void sd_update(void) -+{ -+ sd.dg->update_sys(); -+} -+ -+/* -+ * Register a data gatherer -+ */ -+void sd_dg_register(struct sd_dg *dg) -+{ -+ struct timespec ts = {0, SD_DG_INIT_INTERVAL_MS * 1000000}; -+ struct sd_sys_item *sys_item; -+ struct sd_cpu_item *cpu_item; -+ unsigned int i; -+ -+ sd.dg = dg; -+ -+ for (i = 0; dg->cpu_type_vec[i]; i++) -+ dg->cpu_type_vec[i]->idx = (1UL << i); -+ l_cpu_type_cnt = i; -+ sd_sys_item_iterate(sys_item, i) -+ l_sys_item_cnt++; -+ sd_cpu_item_iterate(cpu_item, i) -+ l_cpu_item_cnt++; -+ -+ sd_update(); -+ nanosleep(&ts, NULL); -+ sd_update(); -+ -+ l_cpu_types_init(); -+} -+ -+/* -+ * Get CPU from sys by ID -+ */ -+struct sd_cpu *sd_cpu_get(struct sd_sys *sys, const char* id) -+{ -+ struct sd_cpu *cpu; -+ -+ list_iterate(cpu, &sys->cpu_list, list) { -+ if (strcmp(cpu->id, id) == 0) -+ return cpu; -+ } -+ return NULL; -+} -+ -+/* -+ * Get CPU type by ID -+ */ -+static struct sd_cpu_type *l_cpu_type_by_id(const char *id) -+{ -+ struct sd_cpu_type **cpu_type_vec = sd.dg->cpu_type_vec; -+ int i; -+ -+ for (i = 0; i < l_cpu_type_cnt; i++) { -+ if (strcmp(cpu_type_vec[i]->id, id) == 0) -+ return cpu_type_vec[i]; -+ } -+ return NULL; -+} -+ -+/* -+ * Allocate and initialize new CPU -+ */ -+struct sd_cpu *sd_cpu_new(struct sd_sys *parent, const char *id, -+ const char *type, int cnt) -+{ -+ struct sd_cpu *cpu; -+ -+ cpu = ht_zalloc(sizeof(*cpu)); -+ cpu->i.parent = parent; -+ strncpy(cpu->id, id, sizeof(cpu->id)); -+ cpu->type = l_cpu_type_by_id(type); -+ cpu->d_cur = &cpu->d1; -+ cpu->cnt = cnt; -+ -+ list_add_end(&cpu->list, &parent->cpu_list); -+ -+ return cpu; -+} -+ -+/* -+ * Get system by ID -+ */ -+struct sd_sys *sd_sys_get(struct sd_sys *parent, const char* id) -+{ -+ struct sd_sys *sys; -+ -+ list_iterate(sys, &parent->child_list, list) { -+ if (strcmp(sys->id, id) == 0) -+ return sys; -+ } -+ return NULL; -+} -+ -+/* -+ * Allocate and initialize new system -+ */ -+struct sd_sys *sd_sys_new(struct sd_sys *parent, const char *id) -+{ -+ struct sd_sys *sys_new; -+ -+ sys_new = ht_zalloc(sizeof(*sys_new)); -+ strncpy(sys_new->id, id, sizeof(sys_new->id)); -+ list_init(&sys_new->child_list); -+ list_init(&sys_new->cpu_list); -+ list_init(&sys_new->list); -+ -+ if (parent) { -+ sys_new->i.parent = parent; -+ parent->child_cnt++; -+ list_add_end(&sys_new->list, &parent->child_list); -+ } -+ return sys_new; -+} -+ -+/* -+ * Free system -+ */ -+static void sd_sys_free(struct sd_sys *sys) -+{ -+ ht_free(sys); -+} -+ -+/* -+ * Free CPU -+ */ -+static void sd_cpu_free(struct sd_cpu *cpu) -+{ -+ ht_free(cpu); -+} -+ -+/* -+ * Start update cycle for CPU -+ */ -+static void l_cpu_update_start(struct sd_cpu *cpu) -+{ -+ struct sd_cpu_info *tmp; -+ -+ cpu->i.active = 0; -+ if (!cpu->d_prev) { -+ cpu->d_prev = &cpu->d1; -+ cpu->d_cur = &cpu->d2; -+ } else { -+ tmp = cpu->d_prev; -+ cpu->d_prev = cpu->d_cur; -+ cpu->d_cur = tmp; -+ } -+} -+ -+/* -+ * Start update cycle for system -+ */ -+void sd_sys_update_start(struct sd_sys *sys) -+{ -+ struct sd_sys *child; -+ struct sd_cpu *cpu; -+ -+ sys->i.active = 0; -+ sys->child_cnt_active = 0; -+ sys->cpu_cnt_active = 0; -+ -+ list_iterate(cpu, &sys->cpu_list, list) -+ l_cpu_update_start(cpu); -+ list_iterate(child, &sys->child_list, list) -+ sd_sys_update_start(child); -+} -+ -+/* -+ * End update cycle for CPUs of a system -+ */ -+static void l_cpu_update_end(struct sd_sys *sys) -+{ -+ struct sd_cpu *cpu, *tmp; -+ -+ /* Has system not lost any CPU? */ -+ if (sys->cpu_cnt_active == sys->cpu_cnt) -+ return; -+ -+ list_iterate_safe(cpu, &sys->cpu_list, list, tmp) { -+ if (!cpu->i.active) { -+ /* CPU has not been updated, remove it */ -+ list_del(&cpu->list); -+ sd_cpu_free(cpu); -+ continue; -+ } -+ } -+} -+ -+/* -+ * End update cycle for system -+ */ -+static void l_sys_update_end(struct sd_sys *sys) -+{ -+ struct sd_sys *child, *tmp; -+ -+ if (sys->child_cnt_active == sys->child_cnt) -+ return; -+ -+ l_cpu_update_end(sys); -+ -+ list_iterate_safe(child, &sys->child_list, list, tmp) { -+ if (!child->i.active) { -+ /* child has not been updated, remove it */ -+ list_del(&child->list); -+ sd_sys_free(child); -+ continue; -+ } -+ /* Recursively update child */ -+ l_sys_update_end(child); -+ } -+ sys->child_cnt = sys->child_cnt_active; -+} -+ -+/* -+ * End update cycle for system -+ */ -+void sd_sys_update_end(struct sd_sys *sys, u64 update_time_us) -+{ -+ sys->update_time_us = update_time_us; -+ l_sys_update_end(sys); -+} -+ -+/* -+ * Is system item available? -+ */ -+int sd_sys_item_available(struct sd_sys_item *item) -+{ -+ struct sd_sys_item *ptr; -+ unsigned int i; -+ -+ sd_sys_item_iterate(ptr, i) { -+ if (item == ptr) -+ return 1; -+ } -+ return 0; -+} -+ -+/* -+ * Number of system items -+ */ -+int sd_sys_item_cnt(void) -+{ -+ return l_sys_item_cnt; -+} -+ -+/* -+ * Is CPU item avaiable? -+ */ -+int sd_cpu_item_available(struct sd_cpu_item *item) -+{ -+ struct sd_cpu_item *ptr; -+ unsigned int i; -+ -+ sd_cpu_item_iterate(ptr, i) { -+ if (item == ptr) -+ return 1; -+ } -+ return 0; -+} -+ -+/* -+ * Number of CPU items -+ */ -+int sd_cpu_item_cnt(void) -+{ -+ return l_cpu_item_cnt; -+} -+ -+/* -+ * Init system data module -+ */ -+void sd_init(void) -+{ -+ l_root_sys = sd_sys_new(NULL, "root"); -+} -+ -+/* -+ * CPU Types -+ */ -+struct sd_cpu_type sd_cpu_type_ifl = { -+ .id = SD_CPU_TYPE_STR_IFL, -+ .desc = "Integrated Facility for Linux", -+ .hotkey = 'i', -+}; -+ -+struct sd_cpu_type sd_cpu_type_cp = { -+ .id = SD_CPU_TYPE_STR_CP, -+ .desc = "Central processor", -+ .hotkey = 'p', -+}; -+ -+struct sd_cpu_type sd_cpu_type_un = { -+ .id = SD_CPU_TYPE_STR_UN, -+ .desc = "Unspecified processor type", -+ .hotkey = 'u', -+}; -diff --git a/hyptop/sd_cpu_items.c b/hyptop/sd_cpu_items.c -new file mode 100644 -index 0000000..803a9b9 ---- /dev/null -+++ b/hyptop/sd_cpu_items.c -@@ -0,0 +1,178 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Provide CPU Items -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include "sd.h" -+ -+/* -+ * Return CPU type of "cpu" -+ */ -+static char *l_cpu_type(struct sd_cpu_item *item, struct sd_cpu *cpu) -+{ -+ (void) item; -+ return sd_cpu_type_str(cpu); -+} -+ -+/* -+ * Return CPU state of "cpu" -+ */ -+static char *l_cpu_state(struct sd_cpu_item *item, struct sd_cpu *cpu) -+{ -+ (void) item; -+ return sd_cpu_state_str(sd_cpu_state(cpu)); -+} -+ -+/* -+ * value = (value_current - value_prev) / online_time_diff -+ */ -+static double l_cpu_diff(struct sd_cpu_item *item, struct sd_cpu *cpu, int sign) -+{ -+ u64 online_time_diff_us; -+ double factor, diff_us; -+ -+ if (sd_cpu_state(cpu) == SD_CPU_STATE_STOPPED) -+ return 0; -+ if (!cpu->d_prev || !cpu->d_cur) -+ return 0; -+ if (!sd_cpu_type_selected(cpu->type)) -+ return 0; -+ online_time_diff_us = l_sub_64(cpu->d_cur->online_time_us, -+ cpu->d_prev->online_time_us); -+ if (online_time_diff_us == 0) -+ return 0; -+ -+ factor = ((double) online_time_diff_us) / 1000000; -+ if (sign) -+ diff_us = l_cpu_info_s64(cpu->d_cur, item->offset) - -+ l_cpu_info_s64(cpu->d_prev, item->offset); -+ else -+ diff_us = l_sub_64(l_cpu_info_u64(cpu->d_cur, item->offset), -+ l_cpu_info_u64(cpu->d_prev, item->offset)); -+ diff_us /= factor; -+ return diff_us; -+} -+ -+/* -+ * unsigned value = (value_current - value_prev) / online_time_diff -+ */ -+static u64 l_cpu_diff_u64(struct sd_cpu_item *item, struct sd_cpu *cpu) -+{ -+ return l_cpu_diff(item, cpu, 0); -+} -+ -+/* -+ * signed value = (value_current - value_prev) / online_time_diff -+ */ -+static s64 l_cpu_diff_s64(struct sd_cpu_item *item, struct sd_cpu *cpu) -+{ -+ return l_cpu_diff(item, cpu, 1); -+} -+ -+/* -+ * Return cpu item value -+ */ -+static u64 l_cpu_item_64(struct sd_cpu_item *item, struct sd_cpu *cpu) -+{ -+ if (sd_cpu_state(cpu) == SD_CPU_STATE_STOPPED) -+ return 0; -+ if (!cpu->d_cur) -+ return 0; -+ if (!sd_cpu_type_selected(cpu->type)) -+ return 0; -+ return l_cpu_info_u64(cpu->d_cur, item->offset); -+} -+ -+/* -+ * CPU item definitions -+ */ -+struct sd_cpu_item sd_cpu_item_type = { -+ .table_col = TABLE_COL_STR('p', "type"), -+ .type = SD_TYPE_STR, -+ .desc = "CPU type", -+ .fn_str = l_cpu_type, -+}; -+ -+struct sd_cpu_item sd_cpu_item_state = { -+ .table_col = TABLE_COL_STR('a', "stat"), -+ .type = SD_TYPE_STR, -+ .desc = "CPU state", -+ .fn_str = l_cpu_state, -+}; -+ -+struct sd_cpu_item sd_cpu_item_cpu_diff = { -+ .table_col = TABLE_COL_TIME_DIFF_SUM(table_col_unit_perc, 'c', "cpu"), -+ .type = SD_TYPE_U64, -+ .offset = SD_CPU_INFO_OFFSET(cpu_time_us), -+ .desc = "CPU time per second", -+ .fn_u64 = l_cpu_diff_u64, -+}; -+ -+struct sd_cpu_item sd_cpu_item_mgm_diff = { -+ .table_col = TABLE_COL_TIME_DIFF_SUM(table_col_unit_perc, 'm', "mgm"), -+ .type = SD_TYPE_U64, -+ .offset = SD_CPU_INFO_OFFSET(mgm_time_us), -+ .desc = "Management time per second", -+ .fn_u64 = l_cpu_diff_u64, -+}; -+ -+struct sd_cpu_item sd_cpu_item_wait_diff = { -+ .table_col = TABLE_COL_TIME_DIFF_SUM(table_col_unit_perc, 'w', "wait"), -+ .type = SD_TYPE_U64, -+ .offset = SD_CPU_INFO_OFFSET(wait_time_us), -+ .desc = "Wait time per second", -+ .fn_u64 = l_cpu_diff_u64, -+}; -+ -+struct sd_cpu_item sd_cpu_item_steal_diff = { -+ .table_col = TABLE_COL_STIME_DIFF_SUM(table_col_unit_perc, 's', -+ "steal"), -+ .type = SD_TYPE_S64, -+ .offset = SD_CPU_INFO_OFFSET(steal_time_us), -+ .desc = "Steal time per second", -+ .fn_s64 = l_cpu_diff_s64, -+}; -+ -+struct sd_cpu_item sd_cpu_item_cpu = { -+ .table_col = TABLE_COL_TIME_SUM(table_col_unit_hm, 'C', "cpu+"), -+ .type = SD_TYPE_U64, -+ .offset = SD_CPU_INFO_OFFSET(cpu_time_us), -+ .desc = "Total CPU time", -+ .fn_u64 = l_cpu_item_64, -+}; -+ -+struct sd_cpu_item sd_cpu_item_mgm = { -+ .table_col = TABLE_COL_TIME_SUM(table_col_unit_hm, 'M', "mgm+"), -+ .type = SD_TYPE_U64, -+ .offset = SD_CPU_INFO_OFFSET(mgm_time_us), -+ .desc = "Total management time", -+ .fn_u64 = l_cpu_item_64, -+}; -+ -+struct sd_cpu_item sd_cpu_item_wait = { -+ .table_col = TABLE_COL_TIME_SUM(table_col_unit_hm, 'W', "wait+"), -+ .type = SD_TYPE_U64, -+ .offset = SD_CPU_INFO_OFFSET(wait_time_us), -+ .desc = "Total wait time", -+ .fn_u64 = l_cpu_item_64, -+}; -+ -+struct sd_cpu_item sd_cpu_item_steal = { -+ .table_col = TABLE_COL_STIME_SUM(table_col_unit_hm, 'S', "steal+"), -+ .type = SD_TYPE_U64, -+ .offset = SD_CPU_INFO_OFFSET(steal_time_us), -+ .desc = "Total steal time", -+ .fn_u64 = l_cpu_item_64, -+}; -+ -+struct sd_cpu_item sd_cpu_item_online = { -+ .table_col = TABLE_COL_TIME_MAX(table_col_unit_dhm, 'o', "online"), -+ .type = SD_TYPE_U64, -+ .offset = SD_CPU_INFO_OFFSET(online_time_us), -+ .desc = "Online time", -+ .fn_u64 = l_cpu_item_64, -+}; -diff --git a/hyptop/sd_sys_items.c b/hyptop/sd_sys_items.c -new file mode 100644 -index 0000000..046faf4 ---- /dev/null -+++ b/hyptop/sd_sys_items.c -@@ -0,0 +1,325 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Provide System Items -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include "sd.h" -+ -+/* -+ * Count CPUs of system according to active CPU types and requested CPU state -+ */ -+static u64 l_sys_cpu_cnt_gen(struct sd_sys *sys, enum sd_cpu_state state, -+ int all) -+{ -+ struct sd_cpu *cpu; -+ u32 cnt = 0; -+ -+ sd_cpu_iterate(sys, cpu) { -+ if (!sd_cpu_type_selected(cpu->type)) -+ continue; -+ if (all || sd_cpu_state(cpu) == state) -+ cnt += cpu->cnt; -+ } -+ return cnt; -+} -+ -+/* -+ * Count all CPUs of system -+ */ -+static u64 l_sys_cpu_cnt(struct sd_sys_item *item, struct sd_sys *sys) -+{ -+ (void) item; -+ return l_sys_cpu_cnt_gen(sys, SD_CPU_STATE_UNKNOWN, 1); -+} -+ -+/* -+ * Count CPUs of system with state stopped -+ */ -+static u64 l_sys_cpu_st_cnt(struct sd_sys_item *item, struct sd_sys *sys) -+{ -+ (void) item; -+ -+ return l_sys_cpu_cnt_gen(sys, SD_CPU_STATE_STOPPED, 0); -+} -+ -+/* -+ * Count CPUs of system with state operating -+ */ -+static u64 l_sys_cpu_op_cnt(struct sd_sys_item *item, struct sd_sys *sys) -+{ -+ (void) item; -+ -+ return l_sys_cpu_cnt_gen(sys, SD_CPU_STATE_OPERATING, 0); -+} -+ -+/* -+ * Count CPUs of system with state deconfigured -+ */ -+static u64 l_sys_cpu_dc_cnt(struct sd_sys_item *item, -+ struct sd_sys *sys) -+{ -+ (void) item; -+ -+ return l_sys_cpu_cnt_gen(sys, SD_CPU_STATE_DECONFIG, 0); -+} -+ -+/* -+ * Get u64 system item value from "sys" -+ */ -+static u64 l_sys_item_u64(struct sd_sys_item *item, struct sd_sys *sys) -+{ -+ switch (item->type) { -+ case SD_TYPE_U16: -+ return *(u16 *)(((char *) sys) + item->offset); -+ case SD_TYPE_U32: -+ return *(u32 *)(((char *) sys) + item->offset); -+ case SD_TYPE_U64: -+ return *(u64 *)(((char *) sys) + item->offset); -+ case SD_TYPE_S64: -+ case SD_TYPE_STR: -+ break; -+ } -+ assert(0); -+ return 0; -+} -+ -+/* -+ * Calculate system item out of sum of CPU info -+ */ -+static u64 l_sys_cpu_info_sum_u64(struct sd_sys_item *item, struct sd_sys *sys) -+{ -+ struct sd_cpu *cpu; -+ u64 rc = 0; -+ -+ sd_cpu_iterate(sys, cpu) { -+ if (!sd_cpu_type_selected(cpu->type)) -+ continue; -+ rc += l_cpu_info_u64(cpu->d_cur, item->offset); -+ } -+ return rc; -+} -+ -+/* -+ * Calculate system item out of MAX of CPU info -+ */ -+static u64 l_sys_cpu_info_max_u64(struct sd_sys_item *item, struct sd_sys *sys) -+{ -+ struct sd_cpu *cpu; -+ u64 rc = 0; -+ -+ sd_cpu_iterate(sys, cpu) { -+ if (!sd_cpu_type_selected(cpu->type)) -+ continue; -+ rc = MAX(rc, l_cpu_info_u64(cpu->d_cur, item->offset)); -+ } -+ return rc; -+} -+ -+/* -+ * value = (value_current - value_prev) / online_time_diff -+ */ -+static double l_cpu_info_diff_u64(struct sd_sys_item *item, struct sd_cpu *cpu, -+ int sign) -+{ -+ u64 online_time_diff_us; -+ double factor, diff_us; -+ -+ if (!sd_cpu_type_selected(cpu->type)) -+ return 0; -+ if (sd_cpu_state(cpu) == SD_CPU_STATE_STOPPED) -+ return 0; -+ online_time_diff_us = l_sub_64(cpu->d_cur->online_time_us, -+ cpu->d_prev->online_time_us); -+ if (online_time_diff_us == 0) -+ return 0; -+ if (sign) { -+ diff_us = l_cpu_info_s64(cpu->d_cur, item->offset) - -+ l_cpu_info_s64(cpu->d_prev, item->offset); -+ } else { -+ diff_us = l_sub_64(l_cpu_info_u64(cpu->d_cur, item->offset), -+ l_cpu_info_u64(cpu->d_prev, item->offset)); -+ } -+ factor = ((double) online_time_diff_us) / 1000000; -+ diff_us /= factor; -+ return diff_us; -+} -+ -+/* -+ * SUM over all CPUs: value = (value_current - value_prev) / online_time_diff -+ */ -+static u64 l_sys_cpu_info_diff_u64(struct sd_sys_item *item, struct sd_sys *sys) -+{ -+ struct sd_cpu *cpu; -+ u64 rc = 0; -+ -+ sd_cpu_iterate(sys, cpu) { -+ if (!cpu->d_prev || !cpu->d_cur) -+ return 0; -+ rc += l_cpu_info_diff_u64(item, cpu, 0); -+ } -+ return rc; -+} -+ -+/* -+ * SUM over all CPUs: value = (value_current - value_prev) / online_time_diff -+ */ -+static s64 l_sys_cpu_info_diff_s64(struct sd_sys_item *item, struct sd_sys *sys) -+{ -+ struct sd_cpu *cpu; -+ s64 rc = 0; -+ -+ sd_cpu_iterate(sys, cpu) { -+ if (!cpu->d_prev || !cpu->d_cur) -+ return 0; -+ rc += l_cpu_info_diff_u64(item, cpu, 1); -+ } -+ return rc; -+} -+ -+/* -+ * System item definitions -+ */ -+struct sd_sys_item sd_sys_item_cpu_cnt = { -+ .table_col = TABLE_COL_CNT_SUM('#', "#cpu"), -+ .type = SD_TYPE_U32, -+ .desc = "Number of CPUs", -+ .fn_u64 = l_sys_cpu_cnt, -+}; -+ -+struct sd_sys_item sd_sys_item_cpu_oper_cnt = { -+ .table_col = TABLE_COL_CNT_SUM('e', "#cpuope"), -+ .type = SD_TYPE_U32, -+ .desc = "Number of operating CPUs", -+ .fn_u64 = l_sys_cpu_op_cnt, -+}; -+ -+struct sd_sys_item sd_sys_item_cpu_stop_cnt = { -+ .table_col = TABLE_COL_CNT_SUM('p', "#cpusp"), -+ .type = SD_TYPE_U32, -+ .desc = "Number of stopped CPUs", -+ .fn_u64 = l_sys_cpu_st_cnt, -+}; -+ -+struct sd_sys_item sd_sys_item_cpu_deconf_cnt = { -+ .table_col = TABLE_COL_CNT_SUM('d', "#cpudc"), -+ .type = SD_TYPE_U32, -+ .desc = "Number of deconfigured CPUs", -+ .fn_u64 = l_sys_cpu_dc_cnt, -+}; -+ -+struct sd_sys_item sd_sys_item_cpu_diff = { -+ .table_col = TABLE_COL_TIME_DIFF_SUM(table_col_unit_perc, 'c', "cpu"), -+ .offset = SD_CPU_INFO_OFFSET(cpu_time_us), -+ .type = SD_TYPE_U64, -+ .desc = "CPU time per second", -+ .fn_u64 = l_sys_cpu_info_diff_u64, -+}; -+ -+struct sd_sys_item sd_sys_item_mgm_diff = { -+ .table_col = TABLE_COL_TIME_DIFF_SUM(table_col_unit_perc, 'm', "mgm"), -+ .offset = SD_CPU_INFO_OFFSET(mgm_time_us), -+ .type = SD_TYPE_U64, -+ .desc = "Management time per second", -+ .fn_u64 = l_sys_cpu_info_diff_u64, -+}; -+ -+struct sd_sys_item sd_sys_item_wait_diff = { -+ .table_col = TABLE_COL_TIME_DIFF_SUM(table_col_unit_perc, 'w', "wait"), -+ .offset = SD_CPU_INFO_OFFSET(wait_time_us), -+ .type = SD_TYPE_U64, -+ .desc = "Wait time per second", -+ .fn_u64 = l_sys_cpu_info_diff_u64, -+}; -+ -+struct sd_sys_item sd_sys_item_steal_diff = { -+ .table_col = TABLE_COL_STIME_DIFF_SUM(table_col_unit_perc, 's', -+ "steal"), -+ .offset = SD_CPU_INFO_OFFSET(steal_time_us), -+ .type = SD_TYPE_S64, -+ .desc = "Steal time per second", -+ .fn_s64 = l_sys_cpu_info_diff_s64, -+}; -+ -+struct sd_sys_item sd_sys_item_cpu = { -+ .table_col = TABLE_COL_TIME_SUM(table_col_unit_hm, 'C', "cpu+"), -+ .offset = SD_CPU_INFO_OFFSET(cpu_time_us), -+ .type = SD_TYPE_U64, -+ .desc = "Total CPU time", -+ .fn_u64 = l_sys_cpu_info_sum_u64, -+}; -+ -+struct sd_sys_item sd_sys_item_wait = { -+ .table_col = TABLE_COL_TIME_SUM(table_col_unit_hm, 'W', "wait+"), -+ .offset = SD_CPU_INFO_OFFSET(wait_time_us), -+ .type = SD_TYPE_U64, -+ .desc = "Total wait time", -+ .fn_u64 = l_sys_cpu_info_sum_u64, -+}; -+ -+struct sd_sys_item sd_sys_item_mgm = { -+ .table_col = TABLE_COL_TIME_SUM(table_col_unit_hm, 'M', "mgm+"), -+ .offset = SD_CPU_INFO_OFFSET(mgm_time_us), -+ .type = SD_TYPE_U64, -+ .desc = "Total management time", -+ .fn_u64 = l_sys_cpu_info_sum_u64, -+}; -+ -+struct sd_sys_item sd_sys_item_steal = { -+ .table_col = TABLE_COL_STIME_SUM(table_col_unit_hm, 'S', "steal+"), -+ .offset = SD_CPU_INFO_OFFSET(steal_time_us), -+ .type = SD_TYPE_U64, -+ .desc = "Total steal time", -+ .fn_u64 = l_sys_cpu_info_sum_u64, -+}; -+ -+struct sd_sys_item sd_sys_item_online = { -+ .table_col = TABLE_COL_TIME_MAX(table_col_unit_dhm, 'o', "online"), -+ .offset = SD_CPU_INFO_OFFSET(online_time_us), -+ .type = SD_TYPE_U64, -+ .desc = "Online time", -+ .fn_u64 = l_sys_cpu_info_max_u64, -+}; -+ -+struct sd_sys_item sd_sys_item_mem_max = { -+ .table_col = TABLE_COL_MEM_SUM(table_col_unit_gib, 'a', "memmax"), -+ .offset = SD_SYSTEM_OFFSET(mem.max_kib), -+ .type = SD_TYPE_U64, -+ .desc = "Maximum memory", -+ .fn_u64 = l_sys_item_u64, -+}; -+ -+struct sd_sys_item sd_sys_item_mem_use = { -+ .table_col = TABLE_COL_MEM_SUM(table_col_unit_gib, 'u', "memuse"), -+ .offset = SD_SYSTEM_OFFSET(mem.use_kib), -+ .type = SD_TYPE_U64, -+ .desc = "Used memory", -+ .fn_u64 = l_sys_item_u64, -+}; -+ -+struct sd_sys_item sd_sys_item_weight_cur = { -+ .table_col = TABLE_COL_CNT_MAX('r', "wcur"), -+ .offset = SD_SYSTEM_OFFSET(weight.cur), -+ .type = SD_TYPE_U16, -+ .desc = "Current weight", -+ .fn_u64 = l_sys_item_u64, -+}; -+ -+struct sd_sys_item sd_sys_item_weight_min = { -+ .table_col = TABLE_COL_CNT_MAX('n', "wmin"), -+ .offset = SD_SYSTEM_OFFSET(weight.min), -+ .type = SD_TYPE_U16, -+ .desc = "Minimum weight", -+ .fn_u64 = l_sys_item_u64, -+}; -+ -+struct sd_sys_item sd_sys_item_weight_max = { -+ .table_col = TABLE_COL_CNT_MAX('x', "wmax"), -+ .offset = SD_SYSTEM_OFFSET(weight.max), -+ .type = SD_TYPE_U16, -+ .desc = "Maximum weight", -+ .fn_u64 = l_sys_item_u64, -+}; -diff --git a/hyptop/table.c b/hyptop/table.c -new file mode 100644 -index 0000000..352960f ---- /dev/null -+++ b/hyptop/table.c -@@ -0,0 +1,1231 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Table module: Provide line mode and curses base table -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include "table.h" -+#include "hyptop.h" -+#include "helper.h" -+ -+#define L_ROWS_EXTRA 2 /* head + last */ -+ -+#define table_col_iterate(t, col, i) \ -+ for (i = 0, col = t->col_vec[0]; col != NULL; col = t->col_vec[++i]) -+ -+/* -+ * Is row marked? -+ */ -+static int l_row_is_marked(struct table *t, struct table_row *row) -+{ -+ struct table_mark_key *key; -+ -+ list_iterate(key, &t->mark_key_list, list) { -+ if (strcmp(row->entries[0].str, key->str) == 0) -+ return 1; -+ } -+ return 0; -+} -+ -+/* -+ * Add mark key to table -+ */ -+static void l_mark_key_add(struct table *t, char *str) -+{ -+ struct table_mark_key *key; -+ -+ key = ht_zalloc(sizeof(*key)); -+ strncpy(key->str, str, sizeof(key->str)); -+ list_add_end(&key->list, &t->mark_key_list); -+ t->mark_keys_cnt++; -+} -+ -+/* -+ * Remove mark key from table -+ */ -+static void l_mark_key_remove(struct table *t, char *str) -+{ -+ struct table_mark_key *key; -+ -+ list_iterate(key, &t->mark_key_list, list) { -+ if (strcmp(str, key->str) == 0) { -+ list_del(&key->list); -+ ht_free(key); -+ t->mark_keys_cnt--; -+ return; -+ } -+ } -+} -+ -+/* -+ * Delete all mark keys from table -+ */ -+void table_row_mark_del_all(struct table *t) -+{ -+ struct table_mark_key *key, *tmp; -+ struct table_row *row; -+ -+ list_iterate(row, &t->row_list, list) -+ row->marked = 0; -+ list_iterate_safe(key, &t->mark_key_list, list, tmp) { -+ list_del(&key->list); -+ ht_free(key); -+ } -+ t->mark_keys_cnt = 0; -+} -+ -+/* -+ * Toggle mark for "row" -+ */ -+void table_row_mark_toggle(struct table *t, struct table_row *row) -+{ -+ if (row->marked) { -+ l_mark_key_remove(t, row->entries[0].str); -+ row->marked = 0; -+ t->row_cnt_marked--; -+ if (t->row_cnt_marked == 0) -+ t->mode_hide_unmarked = 0; -+ } else { -+ l_mark_key_add(t, row->entries[0].str); -+ row->marked = 1; -+ t->row_cnt_marked++; -+ } -+} -+ -+/* -+ * Toggle mark by key -+ */ -+void table_row_mark_toggle_by_key(struct table *t, const char *str) -+{ -+ struct table_row *row; -+ -+ list_iterate(row, &t->row_list, list) { -+ if (strcmp(str, row->entries[0].str) == 0) -+ table_row_mark_toggle(t, row); -+ } -+} -+ -+/* -+ * Is column selected? -+ */ -+static int l_col_selected(struct table *t, struct table_col *col) -+{ -+ return t->col_selected == col; -+} -+ -+/* -+ * Get number of rows for table -+ */ -+static int l_row_cnt(struct table *t) -+{ -+ return t->mode_hide_unmarked ? t->row_cnt_marked : t->row_cnt; -+} -+ -+/* -+ * Get number of data rows that we can display on screen -+ */ -+static int l_row_cnt_displ(struct table *t) -+{ -+ return g.c.row_cnt - t->row_cnt_extra; -+} -+ -+/* -+ * Alloc a new row for table -+ */ -+struct table_row *table_row_alloc(struct table *t) -+{ -+ struct table_row *table_row; -+ -+ table_row = ht_zalloc(sizeof(*table_row)); -+ table_row->entries = ht_zalloc(sizeof(*table_row->entries) * -+ t->col_cnt); -+ list_init(&table_row->list); -+ return table_row; -+} -+ -+/* -+ * Free table row -+ */ -+static void table_row_free(struct table_row *table_row) -+{ -+ ht_free(table_row->entries); -+ ht_free(table_row); -+} -+ -+/* -+ * Allocate and initialize a new table -+ */ -+struct table *table_new(int extra_rows, int sorted, int first_bold, -+ int with_units) -+{ -+ struct table *t = ht_zalloc(sizeof(*t)); -+ -+ list_init(&t->row_list); -+ list_init(&t->mark_key_list); -+ t->row_cnt_marked = 0; -+ if (with_units) -+ t->row_cnt_extra = extra_rows + L_ROWS_EXTRA + 1; -+ else -+ t->row_cnt_extra = extra_rows + L_ROWS_EXTRA; -+ t->attr_with_units = with_units; -+ t->attr_sorted_table = sorted; -+ t->attr_first_bold = first_bold; -+ -+ return t; -+} -+ -+/* -+ * Initialize headline for one column -+ */ -+static void l_col_headline_init(struct table *t, struct table_col *col) -+{ -+ char *ptr; -+ -+ strcpy(col->p->head_first, col->head); -+ ptr = strchr(col->p->head_first, tolower(col->hotkey)); -+ assert(ptr != NULL); -+ *ptr = 0; -+ col->p->head_char[0] = col->hotkey; -+ strcpy(col->p->head_last, ++ptr); -+ if (!t->attr_sorted_table) { -+ ht_str_to_upper(col->p->head_first); -+ ht_str_to_upper(col->p->head_last); -+ col->p->head_char[0] = toupper(col->p->head_char[0]); -+ } -+} -+ -+/* -+ * Initialize the max width values for a column -+ */ -+static void l_col_max_width_init(struct table *t, struct table_col *col) -+{ -+ /* Units are displayed with brackets, therefore (+2) */ -+ if (t->attr_with_units) -+ col->p->max_width = MAX(strlen(col->head), -+ strlen(col->unit->str) + 2); -+ else -+ col->p->max_width = strlen(col->head); -+} -+ -+/* -+ * Add a new column to table -+ */ -+void table_col_add(struct table *t, struct table_col *col) -+{ -+ col->p = ht_zalloc(sizeof(*col->p)); -+ col->p->col_nr = t->col_cnt; -+ col->p->enabled = 1; -+ t->col_cnt++; -+ t->col_vec = ht_realloc(t->col_vec, sizeof(void *) * -+ (t->col_cnt + 1)); -+ t->col_vec[t->col_cnt - 1] = col; -+ t->col_vec[t->col_cnt] = NULL; -+ if (!t->col_selected && t->attr_sorted_table) -+ t->col_selected = col; -+ if (t->row_last) -+ table_row_free(t->row_last); -+ t->row_last = table_row_alloc(t); -+ l_col_headline_init(t, col); -+ l_col_max_width_init(t, col); -+} -+ -+/* -+ * Initialize last row -+ */ -+static void l_row_last_init(struct table *t) -+{ -+ memset(t->row_last->entries, 0, -+ t->col_cnt * sizeof(struct table_entry)); -+} -+ -+/* -+ * Delete all rows of a table -+ */ -+void table_row_del_all(struct table *t) -+{ -+ struct table_row *row, *tmp; -+ -+ list_iterate_safe(row, &t->row_list, list, tmp) { -+ list_del(&row->list); -+ table_row_free(row); -+ } -+ l_row_last_init(t); -+ t->row_cnt_marked = 0; -+ t->ready = 0; -+ t->row_cnt = 0; -+} -+ -+/* -+ * Reset table -+ */ -+void table_reset(struct table *t) -+{ -+ table_row_mark_del_all(t); -+ table_row_del_all(t); -+ t->mode_sort_inverse = 0; -+ t->mode_select = 0; -+} -+ -+/* -+ * Return true, if "e1" is less than "e2" -+ */ -+static int l_entry_less_than(enum table_col_type type, struct table_entry *e1, -+ struct table_entry *e2) -+{ -+ switch (type) { -+ case TABLE_COL_TYPE_U64: -+ return (e1->d.u64.v1 < e2->d.u64.v1); -+ case TABLE_COL_TYPE_S64: -+ return (e1->d.s64.v1 < e2->d.s64.v1); -+ case TABLE_COL_TYPE_STR: -+ return (strcmp(e1->str, e2->str) > 0); -+ } -+ return 0; /* Keep gcc quite */ -+} -+ -+/* -+ * Return true, if "row1" is less than "row2" -+ */ -+static int l_row_less_than(struct table *t, struct table_row *row1, -+ struct table_row *row2) -+{ -+ struct table_col *col = t->col_selected; -+ struct table_entry *e1 = &row1->entries[col->p->col_nr]; -+ struct table_entry *e2 = &row2->entries[col->p->col_nr]; -+ -+ if ((t->mode_sort_inverse && !col->p->rsort) || -+ (!t->mode_sort_inverse && col->p->rsort)) -+ return !l_entry_less_than(col->type, e1, e2); -+ else -+ return l_entry_less_than(col->type, e1, e2); -+} -+ -+/* -+ * Calculate: e1 = e1 + e2 -+ */ -+static void l_entry_sum(enum table_col_type type, struct table_entry *e1, -+ struct table_entry *e2) -+{ -+ switch (type) { -+ case TABLE_COL_TYPE_U64: -+ e1->d.u64.v1 += e2->d.u64.v1; -+ return; -+ case TABLE_COL_TYPE_S64: -+ e1->d.s64.v1 += e2->d.s64.v1; -+ return; -+ default: -+ assert(0); -+ return; -+ } -+} -+ -+/* -+ * Calculate: e1 = MAX(e1, e2) -+ */ -+static void l_entry_max(enum table_col_type type, struct table_entry *e1, -+ struct table_entry *e2) -+{ -+ switch (type) { -+ case TABLE_COL_TYPE_U64: -+ e1->d.u64.v1 = MAX(e1->d.u64.v1, e2->d.u64.v1); -+ return; -+ case TABLE_COL_TYPE_S64: -+ e1->d.s64.v1 = MAX(e1->d.s64.v1, e2->d.s64.v1); -+ return; -+ default: -+ assert(0); -+ return; -+ } -+} -+ -+/* -+ * Aggregate "row" to "last row" -+ */ -+static void l_row_last_agg(struct table *t, struct table_row *table_row) -+{ -+ struct table_col *col; -+ int col_nr; -+ -+ table_col_iterate(t, col, col_nr) { -+ struct table_entry *e_last = &t->row_last->entries[col_nr]; -+ struct table_entry *e_new = &table_row->entries[col_nr]; -+ -+ switch (col->agg) { -+ case TABLE_COL_AGG_SUM: -+ l_entry_sum(col->type, e_last, e_new); -+ break; -+ case TABLE_COL_AGG_MAX: -+ l_entry_max(col->type, e_last, e_new); -+ break; -+ case TABLE_COL_AGG_NONE: -+ break; -+ } -+ } -+} -+ -+/* -+ * Format row: Invoke unit callback and adjust max width of column -+ */ -+static void l_row_format(struct table *t, struct table_row *row) -+{ -+ unsigned int len, col_nr; -+ struct table_col *col; -+ -+ table_col_iterate(t, col, col_nr) { -+ struct table_entry *e = &row->entries[col_nr]; -+ if (col->agg == TABLE_COL_AGG_NONE && row == t->row_last) -+ len = 0; -+ else -+ len = col->unit->fn(col, e); -+ assert(len < TABLE_STR_MAX); -+ if (len > col->p->max_width) -+ col->p->max_width = len; -+ } -+} -+ -+/* -+ * Calculate last row -+ */ -+static void l_row_last_calc(struct table *t) -+{ -+ struct table_row *row; -+ -+ l_row_last_init(t); -+ list_iterate(row, &t->row_list, list) { -+ if (t->mode_hide_unmarked && !row->marked) -+ continue; -+ l_row_last_agg(t, row); -+ } -+ l_row_format(t, t->row_last); -+} -+ -+/* -+ * Finish table after all rows have been added -+ */ -+void table_finish(struct table *t) -+{ -+ l_row_last_calc(t); -+ t->ready = 1; -+} -+ -+/* -+ * Add new row to table -+ */ -+void table_row_add(struct table *t, struct table_row *row) -+{ -+ struct table_row *tmp; -+ -+ l_row_format(t, row); -+ -+ if (list_is_empty(&t->row_list) || !t->attr_sorted_table) { -+ list_add_end(&row->list, &t->row_list); -+ } else { -+ list_iterate(tmp, &t->row_list, list) { -+ if (l_row_less_than(t, tmp, row)) -+ break; -+ } -+ list_add_end(&row->list, &tmp->list); -+ } -+ if (l_row_is_marked(t, row)) { -+ row->marked = 1; -+ t->row_cnt_marked++; -+ } -+ t->row_cnt++; -+} -+ -+/* -+ * Rebuild table: Reformat all rows and adjust max width values -+ */ -+void table_rebuild(struct table *t) -+{ -+ struct table_col *col; -+ struct table_row *row; -+ unsigned int i; -+ -+ table_col_iterate(t, col, i) -+ l_col_max_width_init(t, col); -+ list_iterate(row, &t->row_list, list) -+ l_row_format(t, row); -+ l_row_format(t, t->row_last); -+} -+ -+/* -+ * Sort table (TODO: Use better sorting algorithm) -+ */ -+static void l_table_sort(struct table *t) -+{ -+ struct table_row *row_min; -+ struct table_row *row; -+ struct table_row *tmp; -+ struct list list; -+ -+ list_init(&list); -+ -+ /* -+ * Sort row list into temp list -+ */ -+ while (!list_is_empty(&t->row_list)) { -+ row_min = NULL; -+ -+ list_iterate(row, &t->row_list, list) { -+ if (row_min == NULL) -+ row_min = row; -+ else if (l_row_less_than(t, row, row_min)) -+ row_min = row; -+ } -+ list_del(&row_min->list); -+ list_add(&row_min->list, &list); -+ } -+ /* -+ * Copy temp list to original list -+ */ -+ list_iterate_safe(row, &list, list, tmp) { -+ list_del(&row->list); -+ list_add_end(&row->list, &t->row_list); -+ } -+} -+ -+/* -+ * Adjust table values for select mode (e.g. for window resize or scrolling) -+ */ -+static void l_adjust_values_select_mode(struct table *t) -+{ -+ int row_cnt_displ = l_row_cnt_displ(t); -+ int row_cnt = l_row_cnt(t); -+ -+ /* We went out of range with row selection */ -+ if (t->row_nr_select >= row_cnt) -+ t->row_nr_select = row_cnt - 1; -+ -+ /* Is selected row within visible area? */ -+ if (t->row_nr_select < t->row_nr_begin) { -+ /* Selected row is above area: Scroll up */ -+ t->row_nr_begin = t->row_nr_select; -+ } else if (t->row_nr_select - t->row_nr_begin >= row_cnt_displ) { -+ /* Selected row is below area: Scroll down */ -+ t->row_nr_begin = MAX(t->row_nr_select - row_cnt_displ + 1, 0); -+ } -+} -+ -+/* -+ * Adjust table values (e.g. for window resize or scrolling) -+ */ -+static void l_adjust_values(struct table *t) -+{ -+ int row_cnt_displ = l_row_cnt_displ(t); -+ int row_cnt = l_row_cnt(t); -+ -+ if (t->mode_select) -+ l_adjust_values_select_mode(t); -+ /* If we do not use the whole screen, scroll up */ -+ if (row_cnt - t->row_nr_begin < row_cnt_displ) -+ t->row_nr_begin = MAX(row_cnt - row_cnt_displ, 0); -+} -+ -+/* -+ * Number of rows to be scrolled for page scroll -+ */ -+static int l_scroll_page_row_cnt(struct table *t) -+{ -+ /* We have two rows overlap for scrolling pages */ -+ return l_row_cnt_displ(t) - 2; -+} -+ -+/* -+ * Scroll table down -+ */ -+void table_scroll_down(struct table *t, enum table_scroll_unit scroll_unit) -+{ -+ switch (scroll_unit) { -+ case TABLE_SCROLL_LINE: -+ t->row_nr_begin++; -+ break; -+ case TABLE_SCROLL_PAGE: -+ t->row_nr_begin += l_scroll_page_row_cnt(t); -+ break; -+ case TABLE_SCROLL_LAST: -+ t->row_nr_begin = t->row_cnt; -+ break; -+ } -+} -+ -+/* -+ * Scroll table up -+ */ -+void table_scroll_up(struct table *t, enum table_scroll_unit scroll_unit) -+{ -+ switch (scroll_unit) { -+ case TABLE_SCROLL_LINE: -+ t->row_nr_begin = MAX(t->row_nr_begin - 1, 0); -+ break; -+ case TABLE_SCROLL_PAGE: -+ t->row_nr_begin = -+ MAX(t->row_nr_begin - l_scroll_page_row_cnt(t), 0); -+ break; -+ case TABLE_SCROLL_LAST: -+ t->row_nr_begin = 0; -+ break; -+ } -+} -+ -+/* -+ * Return selected row -+ */ -+static struct table_row *l_selected_row(struct table *t) -+{ -+ struct table_row *row; -+ int row_nr = 0; -+ -+ list_iterate(row, &t->row_list, list) { -+ if (t->mode_hide_unmarked && !row->marked) -+ continue; -+ if (row_nr == t->row_nr_select) -+ return row; -+ row_nr++; -+ } -+ return NULL; -+} -+ -+/* -+ * Toggle mark for selected row -+ */ -+static void l_row_select_mark_toggle(struct table *t) -+{ -+ struct table_row *row; -+ -+ row = l_selected_row(t); -+ table_row_mark_toggle(t, row); -+ l_row_last_calc(t); -+} -+ -+/* -+ * Switch select mode off -+ */ -+static void l_select_mode_off(struct table *t) -+{ -+ t->mode_select = 0; -+} -+ -+/* -+ * Switch select mode on -+ */ -+static void l_select_mode_on(struct table *t) -+{ -+ t->mode_select = 1; -+ t->row_nr_select = t->row_nr_begin; -+} -+ -+/* -+ * Get key for selected row -+ */ -+void table_row_select_key_get(struct table *t, char str[TABLE_STR_MAX]) -+{ -+ struct table_row *row; -+ -+ row = l_selected_row(t); -+ strncpy(str, row->entries[0].str, TABLE_STR_MAX); -+} -+ -+/* -+ * Select row one page down -+ */ -+void table_row_select_down(struct table *t, enum table_scroll_unit scroll_unit) -+{ -+ switch (scroll_unit) { -+ case TABLE_SCROLL_LINE: -+ t->row_nr_select++; -+ break; -+ case TABLE_SCROLL_PAGE: -+ t->row_nr_select += g.c.row_cnt - t->row_cnt_extra; -+ break; -+ case TABLE_SCROLL_LAST: -+ t->row_nr_select = t->row_cnt; -+ break; -+ } -+} -+ -+/* -+ * Select row one page up -+ */ -+void table_row_select_up(struct table *t, enum table_scroll_unit scroll_unit) -+{ -+ switch (scroll_unit) { -+ case TABLE_SCROLL_LINE: -+ t->row_nr_select = MAX(t->row_nr_select - 1, 0); -+ break; -+ case TABLE_SCROLL_PAGE: -+ t->row_nr_select = MAX(t->row_nr_begin - -+ (g.c.row_cnt - t->row_cnt_extra), 0); -+ break; -+ case TABLE_SCROLL_LAST: -+ t->row_nr_select = 0; -+ break; -+ } -+} -+ -+/* -+ * Toggle "hide unmarked" mode -+ */ -+static int l_mode_hide_unmarked_toggle(struct table *t) -+{ -+ if (t->row_cnt_marked == 0) -+ return -ENODEV; -+ t->mode_hide_unmarked = t->mode_hide_unmarked ? 0 : 1; -+ t->row_nr_select = 0; -+ l_row_last_calc(t); -+ return 0; -+} -+ -+/* -+ * Is it possible to scroll down the table? -+ */ -+static int l_can_scroll_down(struct table *t) -+{ -+ int row_cnt = t->mode_hide_unmarked ? t->row_cnt_marked : t->row_cnt; -+ int row_cnt_real = g.c.row_cnt - t->row_cnt_extra; -+ -+ return (row_cnt - t->row_nr_begin > row_cnt_real); -+} -+ -+/* -+ * Is it possible to scroll up the table? -+ */ -+static int l_can_scroll_up(struct table *t) -+{ -+ return (t->row_nr_begin > 0); -+} -+ -+/* -+ * Update the status field -+ */ -+static void l_status_update(struct table *t) -+{ -+ struct table_entry *e_status = &t->row_last->entries[0]; -+ -+ if (g.o.batch_mode_specified) -+ return; -+ -+ if (l_can_scroll_down(t) && l_can_scroll_up(t)) -+ strcpy(e_status->str, "|"); -+ else if (l_can_scroll_up(t)) -+ strcpy(e_status->str, "^"); -+ else if (l_can_scroll_down(t)) -+ strcpy(e_status->str, "V"); -+ else -+ strcpy(e_status->str, "="); -+ -+ if (t->attr_sorted_table) { -+ strcat(e_status->str, ":"); -+ if (t->mode_sort_inverse) -+ strcat(e_status->str, "^"); -+ else -+ strcat(e_status->str, "V"); -+ } -+ strcat(e_status->str, ":"); -+ if (t->mode_select) -+ strcat(e_status->str, "S"); -+ else -+ strcat(e_status->str, "N"); -+} -+ -+/* -+ * Print string with alignment -+ */ -+static void l_str_print(struct table_col *col, const char *str) -+{ -+ char unit[10]; -+ -+ if (col->align == TABLE_COL_ALIGN_LEFT) -+ sprintf(unit, "%%-%ds", col->p->max_width); -+ else -+ sprintf(unit, "%%%ds", col->p->max_width); -+ hyptop_printf(unit, str); -+} -+ -+/* -+ * Print string for "col" -+ */ -+static void l_col_print(struct table *t, struct table_col *col, const char *str) -+{ -+ if (l_col_selected(t, col)) -+ ht_underline_on(); -+ if (col->p->col_nr == 0 && t->attr_first_bold) -+ ht_bold_on(); -+ -+ l_str_print(col, str); -+ -+ if (l_col_selected(t, col)) -+ ht_underline_off(); -+ if (col->p->col_nr == 0 && t->attr_first_bold) -+ ht_bold_off(); -+} -+ -+/* -+ * Print status field -+ */ -+static void l_status_print(struct table *t, const char *str) -+{ -+ ht_bold_on(); -+ l_str_print(t->col_vec[0], str); -+ ht_bold_off(); -+} -+ -+/* -+ * Print headline of column -+ */ -+static void l_col_headline_print(struct table *t, struct table_col *col) -+{ -+ unsigned int len = strlen(col->head); -+ char blank_str[TABLE_STR_MAX]; -+ (void) t; -+ -+ memset(blank_str, ' ', col->p->max_width - len); -+ blank_str[col->p->max_width - len] = 0; -+ -+ if (l_col_selected(t, col)) -+ ht_bold_on(); -+ if (col->align == TABLE_COL_ALIGN_RIGHT) -+ hyptop_printf("%s", blank_str); -+ hyptop_printf("%s", col->p->head_first); -+ if (t->attr_sorted_table) -+ ht_underline_on(); -+ hyptop_printf("%s", col->p->head_char); -+ if (t->attr_sorted_table) -+ ht_underline_off(); -+ hyptop_printf("%s", col->p->head_last); -+ if (col->align == TABLE_COL_ALIGN_LEFT) -+ hyptop_printf("%s", blank_str); -+ if (l_col_selected(t, col)) -+ ht_bold_off(); -+ -+} -+ -+/* -+ * Print headline for table -+ */ -+static void l_headline_print(struct table *t) -+{ -+ struct table_col *col; -+ int col_nr, first = 1; -+ -+ ht_reverse_on(); -+ /* Print all column headlines */ -+ table_col_iterate(t, col, col_nr) { -+ if (!col->p->enabled) -+ continue; -+ if (first) -+ first = 0; -+ else -+ hyptop_printf(" "); -+ l_col_headline_print(t, col); -+ } -+ /* This creates a black bar to the end of the line */ -+ hyptop_print_seek_back(0); -+ ht_reverse_off(); -+ hyptop_print_nl(); -+} -+ -+/* -+ * Print unit line for table -+ */ -+static void l_unitline_print(struct table *t) -+{ -+ struct table_col *col; -+ int col_nr, first = 1; -+ char unit_str[20]; -+ -+ if (!t->attr_with_units) -+ return; -+ ht_reverse_on(); -+ /* Print all column units */ -+ table_col_iterate(t, col, col_nr) { -+ if (!col->p->enabled) -+ continue; -+ if (first) -+ first = 0; -+ else -+ hyptop_printf(" "); -+ if (l_col_selected(t, col)) -+ ht_bold_on(); -+ snprintf(unit_str, sizeof(unit_str), "(%s)", col->unit->str); -+ l_str_print(col, unit_str); -+ if (l_col_selected(t, col)) -+ ht_bold_off(); -+ } -+ /* This creates a black bar to the end of the line */ -+ hyptop_print_seek_back(0); -+ ht_reverse_off(); -+ hyptop_print_nl(); -+} -+ -+/* -+ * Print one table row -+ */ -+static void l_row_print(struct table *t, struct table_row *row) -+{ -+ struct table_col *col; -+ int first = 1, col_nr; -+ -+ table_col_iterate(t, col, col_nr) { -+ struct table_entry *e = &row->entries[col_nr]; -+ if (!col->p->enabled) -+ continue; -+ if (!first) -+ hyptop_printf(" "); -+ else -+ first = 0; -+ if (row == t->row_last && col_nr == 0) -+ l_status_print(t, e->str); -+ else -+ l_col_print(t, col, e->str); -+ } -+} -+ -+/* -+ * Print table under curses -+ */ -+static void l_table_print_curses(struct table *t) -+{ -+ struct table_row *row; -+ int row_nr = 0; -+ -+ if (!t->ready) -+ return; -+ l_adjust_values(t); -+ l_status_update(t); -+ l_headline_print(t); -+ l_unitline_print(t); -+ list_iterate(row, &t->row_list, list) { -+ if (t->mode_hide_unmarked && !row->marked) -+ continue; -+ if (row_nr < t->row_nr_begin) { -+ row_nr++; -+ continue; -+ } -+ if (row_nr - t->row_nr_begin >= g.c.row_cnt - t->row_cnt_extra) -+ break; -+ if (t->mode_select && row_nr == t->row_nr_select) -+ ht_reverse_on(); -+ if (row->marked) -+ ht_bold_on(); -+ l_row_print(t, row); -+ if (t->mode_select && row_nr == t->row_nr_select) { -+#ifdef WITH_SCROLL_BAR -+ hyptop_print_seek_back(1); -+#else -+ hyptop_print_seek_back(0); -+#endif -+ ht_reverse_off(); -+ } -+ if (row->marked) -+ ht_bold_off(); -+ hyptop_print_nl(); -+ row_nr++; -+ } -+ ht_reverse_on(); -+ l_row_print(t, t->row_last); -+ hyptop_print_seek_back(0); -+ ht_reverse_off(); -+#ifdef WITH_SCROLL_BAR -+ if (t->mode_hide_unmarked) -+ ht_print_scroll_bar(t->row_cnt_marked, t->row_nr_begin, -+ t->row_cnt_extra - 1, 1, -+ l_can_scroll_up(t), -+ l_can_scroll_down(t), 1); -+ else -+ ht_print_scroll_bar(t->row_cnt, t->row_nr_begin, -+ t->row_cnt_extra - 1, 1, -+ l_can_scroll_up(t), -+ l_can_scroll_down(t), 1); -+#endif -+} -+ -+/* -+ * Print table under batch mode -+ */ -+static void l_table_print_all(struct table *t) -+{ -+ struct table_row *row; -+ -+ l_headline_print(t); -+ l_unitline_print(t); -+ list_iterate(row, &t->row_list, list) { -+ l_row_print(t, row); -+ hyptop_print_nl(); -+ } -+ l_row_print(t, t->row_last); -+} -+ -+/* -+ * Print table to screen -+ */ -+void table_print(struct table *t) -+{ -+ if (g.o.batch_mode_specified) -+ l_table_print_all(t); -+ else -+ l_table_print_curses(t); -+} -+ -+/* -+ * Return column by hotkey -+ */ -+static struct table_col *l_col_by_hotkey(struct table *t, char hotkey) -+{ -+ struct table_col *col; -+ int col_nr; -+ -+ table_col_iterate(t, col, col_nr) { -+ if (col->hotkey == hotkey) -+ return col; -+ } -+ return NULL; -+} -+ -+/* -+ * Select next unit for column with "hotkey" -+ */ -+void table_col_unit_next(struct table *t, char hotkey) -+{ -+ struct table_col *col; -+ int i; -+ -+ col = l_col_by_hotkey(t, hotkey); -+ if (!col || !col->unit_fam) -+ assert(0); -+ -+ for (i = 0; col->unit_fam[i] != NULL; i++) { -+ if (col->unit != col->unit_fam[i]) -+ continue; -+ -+ if (col->unit_fam[i + 1] == NULL) -+ col->unit = col->unit_fam[0]; -+ else -+ col->unit = col->unit_fam[i + 1]; -+ return; -+ } -+ assert(0); -+} -+ -+/* -+ * Select previous unit for column with "hotkey" -+ */ -+void table_col_unit_prev(struct table *t, char hotkey) -+{ -+ struct table_col *col; -+ int i; -+ -+ col = l_col_by_hotkey(t, hotkey); -+ if (!col || !col->unit_fam) -+ assert(0); -+ -+ for (i = 0; col->unit_fam[i] != NULL; i++) { -+ if (col->unit != col->unit_fam[i]) -+ continue; -+ -+ if (i == 0) { -+ int j; -+ -+ for (j = 0; col->unit_fam[j] != NULL; j++) {} -+ col->unit = col->unit_fam[j - 1]; -+ } else { -+ col->unit = col->unit_fam[i - 1]; -+ } -+ return; -+ } -+ assert(0); -+} -+ -+/* -+ * Set unit for column -+ */ -+int table_col_unit_set(struct table *t, char hotkey, const char *str) -+{ -+ struct table_col *col; -+ int i; -+ -+ col = l_col_by_hotkey(t, hotkey); -+ if (!col) -+ return -ENODEV; -+ -+ for (i = 0; col->unit_fam[i] != NULL; i++) { -+ if (strcasecmp(col->unit_fam[i]->str, str) == 0) { -+ col->unit = col->unit_fam[i]; -+ return 0; -+ } -+ } -+ return -EINVAL; -+} -+ -+/* -+ * Select column by hotkey -+ */ -+int table_col_select(struct table *t, char hotkey) -+{ -+ struct table_col *col; -+ -+ if (!t->attr_sorted_table) -+ assert(0); -+ col = l_col_by_hotkey(t, hotkey); -+ if (!col || !col->p->enabled) -+ return -ENODEV; -+ if (t->col_selected == col) { -+ t->mode_sort_inverse = t->mode_sort_inverse ? 0 : 1; -+ } else { -+ t->mode_sort_inverse = 0; -+ t->col_selected = col; -+ } -+ table_rebuild(t); -+ l_table_sort(t); -+ return 0; -+} -+ -+/* -+ * Select next column -+ */ -+void table_col_select_next(struct table *t) -+{ -+ int i; -+ -+ for (i = t->col_selected->p->col_nr + 1; i < t->col_cnt; i++) { -+ if (t->col_vec[i]->p->enabled) -+ goto found; -+ } -+ return; -+found: -+ t->col_selected = t->col_vec[i]; -+ l_table_sort(t); -+} -+ -+/* -+ * Select previous column -+ */ -+void table_col_select_prev(struct table *t) -+{ -+ int i; -+ -+ for (i = t->col_selected->p->col_nr - 1; i >= 0; i--) { -+ if (t->col_vec[i]->p->enabled) -+ goto found; -+ } -+ return; -+found: -+ t->col_selected = t->col_vec[i]; -+ l_table_sort(t); -+} -+ -+/* -+ * Toggle enabled status for column -+ */ -+void table_col_enable_toggle(struct table *t, char hotkey) -+{ -+ struct table_col *col; -+ -+ col = l_col_by_hotkey(t, hotkey); -+ if (!col || col->p->col_nr == 0) -+ return; -+ col->p->enabled = col->p->enabled ? 0 : 1; -+ if (col == t->col_selected) -+ t->col_selected = t->col_vec[0]; -+} -+ -+/* -+ * Process input for table -+ */ -+void table_process_input(struct table *t, int c) -+{ -+ switch (c) { -+ case '<': -+ if (t->attr_sorted_table) -+ table_col_select_prev(t); -+ break; -+ case '>': -+ if (t->attr_sorted_table) -+ table_col_select_next(t); -+ break; -+ case '.': -+ if (l_mode_hide_unmarked_toggle(t) == 0) -+ l_select_mode_off(t); -+ break; -+ case '+': -+ if (!t->attr_with_units) -+ break; -+ table_col_unit_next(t, t->col_selected->hotkey); -+ table_rebuild(t); -+ break; -+ case '-': -+ if (!t->attr_with_units) -+ break; -+ table_col_unit_prev(t, t->col_selected->hotkey); -+ table_rebuild(t); -+ break; -+ case 'G': -+ if (t->mode_select) -+ table_row_select_down(t, TABLE_SCROLL_LAST); -+ else -+ table_scroll_down(t, TABLE_SCROLL_LAST); -+ break; -+ case 'g': -+ if (t->mode_select) -+ table_row_select_up(t, TABLE_SCROLL_LAST); -+ else -+ table_scroll_up(t, TABLE_SCROLL_LAST); -+ break; -+ case KEY_NPAGE: -+ if (t->mode_select) -+ table_row_select_down(t, TABLE_SCROLL_PAGE); -+ else -+ table_scroll_down(t, TABLE_SCROLL_PAGE); -+ break; -+ case KEY_PPAGE: -+ if (t->mode_select) -+ table_row_select_up(t, TABLE_SCROLL_PAGE); -+ else -+ table_scroll_up(t, TABLE_SCROLL_PAGE); -+ break; -+ case 'j': -+ case KEY_DOWN: -+ if (t->mode_select) -+ table_row_select_down(t, TABLE_SCROLL_LINE); -+ else -+ table_scroll_down(t, TABLE_SCROLL_LINE); -+ break; -+ case 'k': -+ case KEY_UP: -+ if (t->mode_select) -+ table_row_select_up(t, TABLE_SCROLL_LINE); -+ else -+ table_scroll_up(t, TABLE_SCROLL_LINE); -+ break; -+ case ' ': -+ if (t->mode_select) { -+ l_row_select_mark_toggle(t); -+ } else { -+ table_row_mark_del_all(t); -+ t->mode_hide_unmarked = 0; -+ } -+ break; -+ case 'l': -+ case KEY_RIGHT: -+ if (!t->mode_select) -+ l_select_mode_on(t); -+ break; -+ case 'h': -+ case KEY_LEFT: -+ if (t->mode_select) -+ l_select_mode_off(t); -+ break; -+ default: -+ if (t->attr_sorted_table) -+ table_col_select(t, c); -+ } -+} -diff --git a/hyptop/table.h b/hyptop/table.h -new file mode 100644 -index 0000000..c441245 ---- /dev/null -+++ b/hyptop/table.h -@@ -0,0 +1,424 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Table module: Provide line mode and curses base table -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#ifndef TABLE_H -+#define TABLE_H -+ -+#include -+#include -+#include "list.h" -+#include "helper.h" -+ -+#define TABLE_STR_MAX 64 -+#define TABLE_HEADING_SIZE 20 -+ -+struct table_col; -+struct table_entry; -+ -+/* -+ * Table Column Unit -+ */ -+struct table_col_unit { -+ int (*fn)(struct table_col*, struct table_entry *); -+ const char *str; -+ char *desc; -+ char hotkey; -+}; -+ -+/* Predefined units */ -+extern struct table_col_unit table_col_unit_str; -+extern struct table_col_unit table_col_unit_cnt; -+extern struct table_col_unit table_col_unit_kib; -+extern struct table_col_unit table_col_unit_mib; -+extern struct table_col_unit table_col_unit_gib; -+extern struct table_col_unit table_col_unit_us; -+extern struct table_col_unit table_col_unit_ms; -+extern struct table_col_unit table_col_unit_s; -+extern struct table_col_unit table_col_unit_hm; -+extern struct table_col_unit table_col_unit_dhm; -+extern struct table_col_unit table_col_unit_perc; -+extern struct table_col_unit table_col_unit_vis; -+ -+/* Predefined families */ -+extern struct table_col_unit *table_col_unit_fam_str[]; -+extern struct table_col_unit *table_col_unit_fam_cnt[]; -+extern struct table_col_unit *table_col_unit_fam_mem[]; -+extern struct table_col_unit *table_col_unit_fam_time[]; -+extern struct table_col_unit *table_col_unit_fam_time_diff[]; -+extern struct table_col_unit *table_col_unit_fam_vis[]; -+ -+/* -+ * Table Column Type -+ */ -+enum table_col_type { -+ TABLE_COL_TYPE_U64, -+ TABLE_COL_TYPE_S64, -+ TABLE_COL_TYPE_STR, -+}; -+ -+/* -+ * Table Column Alignment -+ */ -+enum table_col_align { -+ TABLE_COL_ALIGN_LEFT, -+ TABLE_COL_ALIGN_RIGHT, -+}; -+ -+/* -+ * Table Column Aggregation -+ */ -+enum table_col_agg { -+ TABLE_COL_AGG_SUM, -+ TABLE_COL_AGG_MAX, -+ TABLE_COL_AGG_NONE, -+}; -+ -+static inline const char *table_col_agg_str(enum table_col_agg agg) -+{ -+ switch (agg) { -+ case TABLE_COL_AGG_SUM: -+ return "sum"; -+ case TABLE_COL_AGG_MAX: -+ return "max"; -+ case TABLE_COL_AGG_NONE: -+ return "none"; -+ } -+ return NULL; -+} -+ -+/* -+ * Table Column -+ */ -+struct table_col_priv { -+ unsigned int max_width; -+ int col_nr; -+ int enabled; -+ char head_first[TABLE_HEADING_SIZE]; -+ char head_char[2]; -+ char head_last[TABLE_HEADING_SIZE]; -+ int rsort; -+}; -+ -+/* -+ * Table Column Specification -+ */ -+struct table_col_spec { -+ char hotkey; -+ char *unit_str; -+}; -+ -+/* -+ * Table Column -+ */ -+struct table_col { -+ enum table_col_type type; -+ struct table_col_unit *unit; -+ struct table_col_unit **unit_fam; -+ enum table_col_align align; -+ enum table_col_agg agg; -+ char hotkey; -+ char head[TABLE_HEADING_SIZE]; -+ struct table_col_priv *p; -+}; -+ -+static inline int table_col_enabled(struct table_col *col) -+{ -+ return col->p->enabled; -+} -+ -+/* -+ * Table Column Constructor Macros -+ */ -+#define TABLE_COL_STR(l, h) \ -+{ \ -+ .type = TABLE_COL_TYPE_STR, \ -+ .unit = &table_col_unit_str, \ -+ .unit_fam = table_col_unit_fam_str, \ -+ .align = TABLE_COL_ALIGN_RIGHT, \ -+ .agg = TABLE_COL_AGG_NONE, \ -+ .hotkey = l, \ -+ .head = h, \ -+} -+ -+#define TABLE_COL_STR_LEFT(l, h) \ -+{ \ -+ .type = TABLE_COL_TYPE_STR, \ -+ .unit = &table_col_unit_str, \ -+ .unit_fam = table_col_unit_fam_str, \ -+ .align = TABLE_COL_ALIGN_LEFT, \ -+ .agg = TABLE_COL_AGG_NONE, \ -+ .hotkey = l, \ -+ .head = h, \ -+} -+ -+#define TABLE_COL_CNT_SUM(l, h) \ -+{ \ -+ .type = TABLE_COL_TYPE_U64, \ -+ .unit = &table_col_unit_cnt, \ -+ .unit_fam = table_col_unit_fam_cnt, \ -+ .align = TABLE_COL_ALIGN_RIGHT, \ -+ .agg = TABLE_COL_AGG_SUM, \ -+ .hotkey = l, \ -+ .head = h, \ -+} -+ -+#define TABLE_COL_CNT_NONE(l, h) \ -+{ \ -+ .type = TABLE_COL_TYPE_U64, \ -+ .unit = &table_col_unit_cnt, \ -+ .unit_fam = table_col_unit_fam_cnt, \ -+ .align = TABLE_COL_ALIGN_RIGHT, \ -+ .agg = TABLE_COL_AGG_NONE, \ -+ .hotkey = l, \ -+ .head = h, \ -+} -+ -+#define TABLE_COL_CNT_MAX(l, h) \ -+{ \ -+ .type = TABLE_COL_TYPE_U64, \ -+ .unit = &table_col_unit_cnt, \ -+ .unit_fam = table_col_unit_fam_cnt, \ -+ .align = TABLE_COL_ALIGN_RIGHT, \ -+ .agg = TABLE_COL_AGG_MAX, \ -+ .hotkey = l, \ -+ .head = h, \ -+} -+ -+#define TABLE_COL_MEM_SUM(f, l, h) \ -+{ \ -+ .type = TABLE_COL_TYPE_U64, \ -+ .unit = &f, \ -+ .unit_fam = table_col_unit_fam_mem, \ -+ .align = TABLE_COL_ALIGN_RIGHT, \ -+ .agg = TABLE_COL_AGG_SUM, \ -+ .hotkey = l, \ -+ .head = h, \ -+} -+ -+#define TABLE_COL_TIME_SUM(f, l, h) \ -+{ \ -+ .type = TABLE_COL_TYPE_U64, \ -+ .unit = &f, \ -+ .unit_fam = table_col_unit_fam_time, \ -+ .align = TABLE_COL_ALIGN_RIGHT, \ -+ .agg = TABLE_COL_AGG_SUM, \ -+ .hotkey = l, \ -+ .head = h, \ -+} -+ -+#define TABLE_COL_TIME_DIFF_SUM(f, l, h) \ -+{ \ -+ .type = TABLE_COL_TYPE_U64, \ -+ .unit = &f, \ -+ .unit_fam = table_col_unit_fam_time_diff, \ -+ .align = TABLE_COL_ALIGN_RIGHT, \ -+ .agg = TABLE_COL_AGG_SUM, \ -+ .hotkey = l, \ -+ .head = h, \ -+} -+ -+#define TABLE_COL_STIME_SUM(f, l, h) \ -+{ \ -+ .type = TABLE_COL_TYPE_S64, \ -+ .unit = &f, \ -+ .unit_fam = table_col_unit_fam_time, \ -+ .align = TABLE_COL_ALIGN_RIGHT, \ -+ .agg = TABLE_COL_AGG_SUM, \ -+ .hotkey = l, \ -+ .head = h, \ -+} -+ -+#define TABLE_COL_STIME_DIFF_SUM(f, l, h) \ -+{ \ -+ .type = TABLE_COL_TYPE_S64, \ -+ .unit = &f, \ -+ .unit_fam = table_col_unit_fam_time_diff, \ -+ .align = TABLE_COL_ALIGN_RIGHT, \ -+ .agg = TABLE_COL_AGG_SUM, \ -+ .hotkey = l, \ -+ .head = h, \ -+} -+ -+#define TABLE_COL_TIME_MAX(f, l, h) \ -+{ \ -+ .type = TABLE_COL_TYPE_U64, \ -+ .unit = &f, \ -+ .unit_fam = table_col_unit_fam_time, \ -+ .align = TABLE_COL_ALIGN_RIGHT, \ -+ .agg = TABLE_COL_AGG_MAX, \ -+ .hotkey = l, \ -+ .head = h, \ -+} -+ -+/* -+ * Set reverse sort property for column -+ */ -+static inline void table_col_rsort(struct table_col *col) -+{ -+ col->p->rsort = 1; -+} -+ -+/* -+ * Column member access macros -+ */ -+#define table_col_hotkey(col) ((col)->hotkey) -+#define table_col_head(col) ((col)->head) -+#define table_col_unit_str(col) ((col)->unit->str) -+ -+/* -+ * Table Entry -+ */ -+struct table_entry { -+ union { -+ struct { -+ u64 v1; -+ u64 v2; -+ } u64; -+ struct { -+ s64 v1; -+ s64 v2; -+ } s64; -+ } d; -+ char str[TABLE_STR_MAX]; -+}; -+ -+/* -+ * Table Row -+ */ -+struct table_row { -+ struct list list; -+ int entry_count; -+ struct table_entry *entries; -+ int marked; -+}; -+ -+/* -+ * Table Mark Key -+ */ -+struct table_mark_key { -+ struct list list; -+ char str[TABLE_STR_MAX]; -+}; -+ -+/* -+ * Table -+ */ -+struct table { -+ struct list row_list; -+ int col_cnt; -+ struct table_col **col_vec; -+ struct table_col *col_selected; -+ struct table_row *row_last; -+ int row_cnt; -+ int row_cnt_marked; -+ int row_cnt_extra; -+ int row_nr_begin; -+ int row_nr_select; -+ int ready; -+ struct list mark_key_list; -+ unsigned int mark_keys_cnt; -+ int attr_sorted_table; -+ int attr_first_bold; -+ int attr_with_units; -+ int mode_sort_inverse; -+ int mode_select; -+ int mode_hide_unmarked; -+}; -+ -+/* -+ * Return if we are in select mode -+ */ -+static inline int table_mode_select(struct table *t) -+{ -+ return t->mode_select; -+} -+ -+/* -+ * Table croll units -+ */ -+enum table_scroll_unit { -+ TABLE_SCROLL_LINE, -+ TABLE_SCROLL_PAGE, -+ TABLE_SCROLL_LAST, -+}; -+ -+/* -+ * Prototypes -+ */ -+extern struct table *table_new(int extra_rows, int sorted, int first_bold, -+ int with_units); -+extern void table_reset(struct table *t); -+extern void table_rebuild(struct table *t); -+extern void table_finish(struct table *t); -+extern void table_print(struct table *t); -+extern void table_process_input(struct table *t, int c); -+ -+extern void table_col_unit_next(struct table *t, char hotkey); -+extern void table_col_unit_prev(struct table *t, char hotkey); -+extern int table_col_unit_set(struct table *t, char hotkey, const char *unit); -+extern void table_col_add(struct table *t, struct table_col *col); -+extern int table_col_select(struct table *t, char hotkey); -+extern void table_col_select_next(struct table *t); -+extern void table_col_select_prev(struct table *t); -+extern void table_col_enable_toggle(struct table *t, char hotkey); -+ -+extern void table_row_del_all(struct table *t); -+extern void table_row_add(struct table *t, struct table_row *row); -+extern void table_row_mark(struct table *t, struct table_row *row); -+extern void table_row_mark_del_all(struct table *t); -+extern void table_row_mark_toggle(struct table *t, struct table_row *row); -+extern void table_row_mark_toggle_by_key(struct table *t, const char *mark_key); -+extern void table_row_select_down(struct table *t, enum table_scroll_unit unit); -+extern void table_row_select_up(struct table *t, enum table_scroll_unit unit); -+extern void table_row_select_key_get(struct table *t, char str[TABLE_STR_MAX]); -+extern struct table_row *table_row_alloc(struct table *t); -+ -+extern void table_scroll_down(struct table *t, enum table_scroll_unit unit); -+extern void table_scroll_up(struct table *t, enum table_scroll_unit unit); -+ -+/* -+ * Entry add functions -+ */ -+static inline void table_row_entry_u64_add(struct table_row *table_row, -+ struct table_col *table_col, -+ u64 value) -+{ -+ table_row->entries[table_col->p->col_nr].d.u64.v1 = value; -+} -+ -+static inline void table_row_entry_s64_add(struct table_row *table_row, -+ struct table_col *table_col, -+ s64 value) -+{ -+ table_row->entries[table_col->p->col_nr].d.s64.v1 = value; -+} -+ -+static inline void table_row_entry_u64_add_pair(struct table_row *table_row, -+ struct table_col *table_col, -+ u64 value1, u64 value2) -+{ -+ table_row->entries[table_col->p->col_nr].d.u64.v1 = value1; -+ table_row->entries[table_col->p->col_nr].d.u64.v2 = value2; -+} -+ -+static inline void table_row_entry_str_add(struct table_row *table_row, -+ struct table_col *table_col, -+ const char *str) -+{ -+ assert(strlen(str) < TABLE_STR_MAX); -+ strcpy(table_row->entries[table_col->p->col_nr].str, str); -+} -+ -+/* -+ * Interate over all mark keys -+ */ -+#define table_iterate_mark_keys(t, key) \ -+ list_iterate(key, &t->mark_key_list, list) -+ -+#endif /* TABLE_H */ -diff --git a/hyptop/table_col_unit.c b/hyptop/table_col_unit.c -new file mode 100644 -index 0000000..a8f5851 ---- /dev/null -+++ b/hyptop/table_col_unit.c -@@ -0,0 +1,369 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Table unit module: Provide different units for data -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include -+#include "table.h" -+ -+#define L_VISUAL_ROW_CNT 45 -+#define L_COL_FMT_STR_0 "%.0lf" -+#define L_COL_FMT_STR_2 "%.2lf" -+ -+/* -+ * Helper: Divide value and format it -+ */ -+static int l_unit_raw_div(struct table_col *col, struct table_entry *e, -+ unsigned int divisor, const char *fmt_str) -+{ -+ double v1; -+ -+ switch (col->type) { -+ case TABLE_COL_TYPE_U64: -+ v1 = ((double) e->d.u64.v1) / divisor; -+ break; -+ case TABLE_COL_TYPE_S64: -+ v1 = ((double) e->d.s64.v1) / divisor; -+ break; -+ default: -+ assert(0); -+ } -+ return snprintf(e->str, sizeof(e->str), fmt_str, v1); -+} -+ -+/* -+ * Helper: Format value as is -+ */ -+static int l_unit_raw(struct table_col *col, struct table_entry *e) -+{ -+ switch (col->type) { -+ case TABLE_COL_TYPE_U64: -+ return snprintf(e->str, sizeof(e->str), "%llu", e->d.u64.v1); -+ case TABLE_COL_TYPE_S64: -+ return snprintf(e->str, sizeof(e->str), "%lld", e->d.s64.v1); -+ default: -+ assert(0); -+ return 0; -+ } -+} -+ -+/* -+ * Format: String -+ */ -+static int l_str(struct table_col *col, struct table_entry *e) -+{ -+ (void) col; -+ return strlen(e->str); -+} -+ -+struct table_col_unit table_col_unit_str = { -+ .fn = l_str, -+ .hotkey = 'S', -+ .str = "str", -+ .desc = "String", -+}; -+ -+/* -+ * Format: Count -+ */ -+static int l_unit_cnt(struct table_col *col, struct table_entry *e) -+{ -+ return l_unit_raw(col, e); -+} -+ -+struct table_col_unit table_col_unit_cnt = { -+ .fn = l_unit_cnt, -+ .hotkey = '#', -+ .str = "#", -+ .desc = "Count", -+}; -+ -+/* -+ * Format: Kibibytes -+ */ -+static int l_unit_kib(struct table_col *col, struct table_entry *e) -+{ -+ return l_unit_raw(col, e); -+} -+ -+struct table_col_unit table_col_unit_kib = { -+ .fn = l_unit_kib, -+ .hotkey = 'k', -+ .str = "KiB", -+ .desc = "Kibibyte (1.024 bytes)", -+}; -+ -+/* -+ * Format: Mebibytes -+ */ -+static int l_unit_mib(struct table_col *col, struct table_entry *e) -+{ -+ return l_unit_raw_div(col, e, 1024, L_COL_FMT_STR_2); -+} -+ -+struct table_col_unit table_col_unit_mib = { -+ .fn = l_unit_mib, -+ .hotkey = 'M', -+ .str = "MiB", -+ .desc = "Mebibyte (1.048.576 bytes)", -+}; -+ -+/* -+ * Format: Gibibytes -+ */ -+static int l_unit_gib(struct table_col *col, struct table_entry *e) -+{ -+ return l_unit_raw_div(col, e, 1024 * 1024, L_COL_FMT_STR_2); -+} -+ -+struct table_col_unit table_col_unit_gib = { -+ .fn = l_unit_gib, -+ .hotkey = 'g', -+ .str = "GiB", -+ .desc = "Gibibyte (1.073.741.824 bytes)", -+}; -+ -+/* -+ * Format: Microseconds -+ */ -+static int l_unit_us(struct table_col *col, struct table_entry *e) -+{ -+ return l_unit_raw(col, e); -+} -+ -+struct table_col_unit table_col_unit_us = { -+ .fn = l_unit_us, -+ .hotkey = 'u', -+ .str = "us", -+}; -+ -+/* -+ * Format: Milliseconds -+ */ -+static int l_unit_ms(struct table_col *col, struct table_entry *e) -+{ -+ return l_unit_raw_div(col, e, 1000, L_COL_FMT_STR_2); -+} -+ -+struct table_col_unit table_col_unit_ms = { -+ .fn = l_unit_ms, -+ .hotkey = 'm', -+ .str = "ms", -+}; -+ -+/* -+ * Format: Percent (Hundreds) -+ */ -+static int l_unit_perc(struct table_col *col, struct table_entry *e) -+{ -+ return l_unit_raw_div(col, e, 10000, L_COL_FMT_STR_2); -+} -+ -+struct table_col_unit table_col_unit_perc = { -+ .fn = l_unit_perc, -+ .hotkey = '%', -+ .str = "%", -+ .desc = "Percent", -+}; -+ -+/* -+ * Format: Seconds -+ */ -+static int l_unit_s(struct table_col *col, struct table_entry *e) -+{ -+ return l_unit_raw_div(col, e, 1000000, L_COL_FMT_STR_2); -+} -+ -+struct table_col_unit table_col_unit_s = { -+ .fn = l_unit_s, -+ .hotkey = 's', -+ .str = "s", -+ .desc = "Seconds", -+}; -+ -+/* -+ * Format: Minutes -+ */ -+static int l_unit_m(struct table_col *col, struct table_entry *e) -+{ -+ return l_unit_raw_div(col, e, 1000000 * 60, L_COL_FMT_STR_0); -+} -+ -+struct table_col_unit table_col_unit_m = { -+ .fn = l_unit_m, -+ .hotkey = 'm', -+ .str = "m", -+ .desc = "Minutes", -+}; -+ -+/* -+ * Format: Hours:Minutes -+ */ -+static int l_unit_hm_u64(char *str, u64 v1, int negative) -+{ -+ u64 time_tmp, time_h, time_m; -+ -+ time_tmp = v1 / (1000000 * 60); -+ time_h = time_tmp / 60; -+ time_m = time_tmp - time_h * 60; -+ -+ if (negative) -+ return sprintf(str, "-%llu:%02llu", time_h, time_m); -+ else -+ return sprintf(str, "%llu:%02llu", time_h, time_m); -+} -+ -+static int l_unit_hm(struct table_col *col, struct table_entry *e) -+{ -+ switch (col->type) { -+ case TABLE_COL_TYPE_U64: -+ return l_unit_hm_u64(e->str, e->d.u64.v1, 0); -+ case TABLE_COL_TYPE_S64: -+ if (e->d.s64.v1 < 0) -+ return l_unit_hm_u64(e->str, -e->d.s64.v1, 1); -+ else -+ return l_unit_hm_u64(e->str, e->d.s64.v1, 0); -+ default: -+ assert(0); -+ return 0; -+ } -+} -+ -+struct table_col_unit table_col_unit_hm = { -+ .fn = l_unit_hm, -+ .hotkey = 'H', -+ .str = "hm", -+ .desc = "Hours:Minutes", -+}; -+ -+/* -+ * Format: Days:Hours:Minutes -+ */ -+static int l_unit_dhm_u64(char *str, u64 v1, int negative) -+{ -+ u64 time_tmp, time_d, time_h, time_m; -+ -+ time_tmp = v1 / (1000000 * 60); -+ time_d = time_tmp / (60 * 24); -+ time_h = time_tmp / 60 - time_d * 24; -+ time_m = time_tmp - time_h * 60 - time_d * 60 * 24; -+ -+ if (negative) -+ return sprintf(str, "-%llu:%02llu:%02llu", time_d, time_h, -+ time_m); -+ else -+ return sprintf(str, "%llu:%02llu:%02llu", time_d, time_h, -+ time_m); -+} -+ -+static int l_unit_dhm(struct table_col *col, struct table_entry *e) -+{ -+ switch (col->type) { -+ case TABLE_COL_TYPE_U64: -+ return l_unit_dhm_u64(e->str, e->d.u64.v1, 0); -+ case TABLE_COL_TYPE_S64: -+ if (e->d.s64.v1 < 0) -+ return l_unit_dhm_u64(e->str, -e->d.s64.v1, 1); -+ else -+ return l_unit_dhm_u64(e->str, e->d.s64.v1, 0); -+ default: -+ assert(0); -+ return 0; -+ } -+} -+ -+struct table_col_unit table_col_unit_dhm = { -+ .fn = l_unit_dhm, -+ .hotkey = 'D', -+ .str = "dhm", -+ .desc = "Days:Hours:Minutes", -+}; -+ -+/* -+ * Format: Visualization with bar chart -+ */ -+static int l_unit_vis(struct table_col *col, struct table_entry *e) -+{ -+ double val1_perc, val2_perc; -+ int val1_nr, val2_nr; -+ int i; -+ -+ assert(col->type == TABLE_COL_TYPE_U64); -+ -+ sprintf(e->str, "|"); -+ val1_perc = e->d.u64.v1; -+ val1_perc /= 1000000; -+ val2_perc = e->d.u64.v2; -+ val2_perc /= 1000000; -+ val1_nr = (val1_perc * L_VISUAL_ROW_CNT) + 0.5; -+ val2_nr = (val2_perc * L_VISUAL_ROW_CNT) + 0.5; -+ -+ if (val1_nr > L_VISUAL_ROW_CNT) -+ val1_nr = L_VISUAL_ROW_CNT; -+ if (val1_nr + val2_nr > L_VISUAL_ROW_CNT) -+ val2_nr = L_VISUAL_ROW_CNT - val1_nr; -+ -+ for (i = 0; i < val1_nr; i++) -+ strcat(e->str, "#"); -+ for (i = 0; i < val2_nr; i++) -+ strcat(e->str, "-"); -+ for (i = 0; i < L_VISUAL_ROW_CNT - val1_nr - val2_nr; i++) -+ strcat(e->str, " "); -+ strcat(e->str, "|"); -+ -+ return strlen(e->str); -+} -+ -+struct table_col_unit table_col_unit_vis = { -+ .fn = l_unit_vis, -+ .hotkey = 'v', -+ .str = "vis", -+ .desc = "Visualization with bar chart", -+}; -+ -+/* -+ * Families -+ */ -+struct table_col_unit *table_col_unit_fam_str[] = { -+ &table_col_unit_str, -+ NULL, -+}; -+ -+struct table_col_unit *table_col_unit_fam_cnt[] = { -+ &table_col_unit_cnt, -+ NULL, -+}; -+ -+struct table_col_unit *table_col_unit_fam_mem[] = { -+ &table_col_unit_kib, -+ &table_col_unit_mib, -+ &table_col_unit_gib, -+ NULL, -+}; -+ -+struct table_col_unit *table_col_unit_fam_time_diff[] = { -+ &table_col_unit_us, -+ &table_col_unit_ms, -+ &table_col_unit_perc, -+ &table_col_unit_s, -+ NULL, -+}; -+ -+struct table_col_unit *table_col_unit_fam_time[] = { -+ &table_col_unit_us, -+ &table_col_unit_ms, -+ &table_col_unit_s, -+ &table_col_unit_m, -+ &table_col_unit_hm, -+ &table_col_unit_dhm, -+ NULL, -+}; -+ -+struct table_col_unit *table_col_unit_fam_vis[] = { -+ &table_col_unit_vis, -+ NULL, -+}; -diff --git a/hyptop/tbox.c b/hyptop/tbox.c -new file mode 100644 -index 0000000..82eda0c ---- /dev/null -+++ b/hyptop/tbox.c -@@ -0,0 +1,240 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Text box: Provide scrollable text window under curses. -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include -+#include -+#include "hyptop.h" -+#include "tbox.h" -+#include "helper.h" -+ -+/* -+ * Delete one line -+ */ -+static void l_line_free(struct tbox_line *line) -+{ -+ ht_free(line->str); -+ ht_free(line); -+} -+ -+/* -+ * Delete all lines -+ */ -+void tbox_line_del_all(struct tbox *tb) -+{ -+ struct tbox_line *line, *tmp; -+ -+ list_iterate_safe(line, &tb->line_list, list, tmp) { -+ list_del(&line->list); -+ l_line_free(line); -+ } -+ tb->tbox_ready = 0; -+ tb->line_cnt = 0; -+} -+ -+/* -+ * Finish text box after all lines have been added -+ */ -+void tbox_finish(struct tbox *tb) -+{ -+ tb->tbox_ready = 1; -+} -+ -+/* -+ * Add one line to text box -+ */ -+void tbox_line_add(struct tbox *tb, const char *str) -+{ -+ struct tbox_line *line; -+ -+ if (strlen(str) > TBOX_MAX_STR) -+ assert(0); -+ line = ht_zalloc(sizeof(*line)); -+ line->str = ht_strdup(str); -+ if (list_is_empty(&tb->line_list)) -+ list_add(&line->list, &tb->line_list); -+ else -+ list_add(&line->list, &tb->last_line->list); -+ tb->last_line = line; -+ tb->line_cnt++; -+} -+ -+/* -+ * Adjust values, if we scrolled out of range -+ */ -+static void l_adjust_values(struct tbox *tb) -+{ -+ if (tb->line_cnt - tb->line_start < g.c.row_cnt) -+ tb->line_start = MAX(tb->line_cnt - g.c.row_cnt, 0); -+} -+ -+/* -+ * Scroll text box down -+ */ -+void tbox_scroll_down(struct tbox *tb, enum tbox_scroll_unit unit) -+{ -+ switch (unit) { -+ case TBOX_SCROLL_LINE: -+ tb->line_start++; -+ break; -+ case TBOX_SCROLL_PAGE: -+ tb->line_start += (g.c.row_cnt - 2); -+ break; -+ case TBOX_SCROLL_LAST: -+ tb->line_start = tb->line_cnt; -+ break; -+ } -+} -+ -+/* -+ * Scroll text box up -+ */ -+void tbox_scroll_up(struct tbox *tb, enum tbox_scroll_unit unit) -+{ -+ switch (unit) { -+ case TBOX_SCROLL_LINE: -+ tb->line_start = MAX(tb->line_start - 1, 0); -+ break; -+ case TBOX_SCROLL_PAGE: -+ tb->line_start = MAX(tb->line_start - (g.c.row_cnt - 2), 0); -+ break; -+ case TBOX_SCROLL_LAST: -+ tb->line_start = 0; -+ break; -+ } -+} -+ -+/* -+ * Resize text box -+ */ -+void tbox_term_resize(struct tbox *tb) -+{ -+ l_adjust_values(tb); -+} -+ -+/* -+ * Toggle bold curses format attribute -+ */ -+static void l_bold_toggle(void) -+{ -+ static int bold_on; -+ -+ if (bold_on) { -+ ht_bold_off(); -+ bold_on = 0; -+ } else { -+ ht_bold_on(); -+ bold_on = 1; -+ } -+} -+ -+/* -+ * Toggle underline curses format attribute -+ */ -+static void l_underline_toggle(void) -+{ -+ static int underline_on; -+ -+ if (underline_on) { -+ ht_underline_off(); -+ underline_on = 0; -+ } else { -+ ht_underline_on(); -+ underline_on = 1; -+ } -+} -+ -+/* -+ * Print one line with attributes (bold and underline) -+ */ -+void l_print_line(const char *line) -+{ -+ char line_cpy[TBOX_MAX_STR + 1]; -+ char *ptr_old, *ptr; -+ -+ strncpy(line_cpy, line, sizeof(line_cpy)); -+ ptr_old = ptr = line_cpy; -+ do { -+ ptr = strchr(ptr, '\\'); -+ if (ptr) { -+ *ptr = 0; -+ hyptop_printf("%s", ptr_old); -+ switch (ptr[1]) { -+ case 'B': -+ l_bold_toggle(); -+ break; -+ case 'U': -+ l_underline_toggle(); -+ break; -+ } -+ ptr += 2; -+ ptr_old = ptr; -+ } else { -+ hyptop_printf("%s", ptr_old); -+ return; -+ } -+ } while (*ptr); -+} -+ -+#ifdef WITH_SCROLL_BAR -+static int l_can_scroll_down(struct tbox *tb) -+{ -+ return (tb->line_cnt - tb->line_start > g.c.row_cnt); -+} -+ -+static int l_can_scroll_up(struct tbox *tb) -+{ -+ return (tb->line_start > 0); -+} -+#endif -+ -+/* -+ * Print text box to screen -+ */ -+void tbox_print(struct tbox *tb) -+{ -+ int line_nr = 0, first = 1; -+ struct tbox_line *line; -+ -+ if (!tb->tbox_ready) -+ return; -+ -+ l_adjust_values(tb); -+ list_iterate(line, &tb->line_list, list) { -+ if (line_nr < tb->line_start) { -+ line_nr++; -+ continue; -+ } -+ /* Have we printed the whole visible screen ? */ -+ if (line_nr - tb->line_start >= g.c.row_cnt) -+ break; -+ if (first) -+ first = 0; -+ else -+ hyptop_print_nl(); -+ l_print_line(line->str); -+ line_nr++; -+ } -+#ifdef WITH_SCROLL_BAR -+ ht_print_scroll_bar(tb->line_cnt, tb->line_start, 0, -+ 0, l_can_scroll_up(tb), l_can_scroll_down(tb), -+ 0); -+#endif -+} -+ -+/* -+ * Create new text box -+ */ -+struct tbox *tbox_new(void) -+{ -+ struct tbox *tb; -+ -+ tb = ht_zalloc(sizeof(*tb)); -+ list_init(&tb->line_list); -+ return tb; -+} -diff --git a/hyptop/tbox.h b/hyptop/tbox.h -new file mode 100644 -index 0000000..60397f3 ---- /dev/null -+++ b/hyptop/tbox.h -@@ -0,0 +1,52 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Text box: Provide scrollable text window under curses. -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#ifndef TBOX_H -+#define TBOX_H -+ -+#include "list.h" -+ -+#define TBOX_MAX_STR 120 -+ -+struct tbox_line { -+ struct list list; -+ char *str; -+}; -+ -+struct tbox { -+ struct list line_list; -+ int line_cnt; -+ int line_start; -+ int tbox_ready; -+ struct tbox_line *last_line; -+}; -+ -+enum tbox_scroll_unit { -+ TBOX_SCROLL_LINE, -+ TBOX_SCROLL_PAGE, -+ TBOX_SCROLL_LAST, -+}; -+ -+struct tbox *tbox_new(void); -+void tbox_line_del_all(struct tbox *tb); -+void tbox_line_add(struct tbox *tb, const char *str); -+void tbox_finish(struct tbox *tb); -+void tbox_scroll_down(struct tbox *tb, enum tbox_scroll_unit); -+void tbox_scroll_up(struct tbox *tb, enum tbox_scroll_unit); -+void tbox_term_resize(struct tbox *tb); -+void tbox_print(struct tbox *tb); -+ -+#define tbox_printf(tb, x...) \ -+{ \ -+ char line[TBOX_MAX_STR + 1]; \ -+ sprintf(line, x); \ -+ tbox_line_add(tb, line); \ -+} -+ -+#endif /* TBOX_H */ -diff --git a/hyptop/win_cpu_types.c b/hyptop/win_cpu_types.c -new file mode 100644 -index 0000000..7d3dff2 ---- /dev/null -+++ b/hyptop/win_cpu_types.c -@@ -0,0 +1,248 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Window "cpu_types": Select CPU types used for CPU data calculation. -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include "helper.h" -+#include "hyptop.h" -+#include "table.h" -+#include "win_cpu_types.h" -+#include "sd.h" -+#include "nav_desc.h" -+ -+/* -+ * Globals for cpu_types window -+ */ -+static struct table_col l_col_key = TABLE_COL_STR_LEFT('k', "k"); -+static struct table_col l_col_select = TABLE_COL_STR_LEFT('s', "s"); -+static struct table_col l_col_id = TABLE_COL_STR_LEFT('i', "id"); -+static struct table_col l_col_desc = TABLE_COL_STR_LEFT('d', "description"); -+ -+/* -+ * Online help text for cpu_types window -+ */ -+static const char l_help_str[] = -+"In the \"cpu_types\" window you can select the CPU types that are used for\n" -+"calculating CPU data. Toggle the selection of types either by pressing the\n" -+"corresponding hotkey or by selecting them in select mode using the SPACE bar.\n" -+"\n" -+"The table of the \"cpu_types\" window has the following columns:\n" -+" - K : Hotkey of CPU type\n" -+" - S : Shows if CPU type is selected\n" -+" - ID : Name of CPU type\n" -+" - DESC: Description of CPU type\n"; -+ -+/* -+ * Description of Navigation Keys (used for help window) -+ */ -+static struct nav_desc *l_nav_desc_normal_vec[] = { -+ &nav_desc_select_mode_enter, -+ &nav_desc_marks_clear, -+ &nav_desc_win_leave_cpu_types, -+ NULL, -+}; -+ -+static struct nav_desc *l_nav_desc_select_vec[] = { -+ &nav_desc_select_mode_leave, -+ &nav_desc_mark_toggle, -+ &nav_desc_win_leave_cpu_types_fast, -+ NULL, -+}; -+ -+static struct nav_desc *l_nav_desc_general_vec[] = { -+ &nav_desc_toggle_mark_hotkey, -+ &nav_desc_scroll_up_line, -+ &nav_desc_scroll_down_line, -+ &nav_desc_scroll_up_page, -+ &nav_desc_scroll_down_page, -+ &nav_desc_scroll_up_head, -+ &nav_desc_scroll_down_tail, -+ &nav_desc_mark_toggle_view, -+ NULL, -+}; -+ -+/* -+ * Add a CPU type to the table -+ */ -+static void l_add_cpu_type(struct win_cpu_types *win_cpu_types, -+ struct sd_cpu_type *cpu_type) -+{ -+ char char_str[2], select_str[2]; -+ struct table_row *table_row; -+ -+ if (sd_cpu_type_selected(cpu_type)) -+ sprintf(select_str, "*"); -+ else -+ sprintf(select_str, " "); -+ sprintf(char_str, "%c", cpu_type->hotkey); -+ -+ table_row = table_row_alloc(win_cpu_types->t); -+ table_row_entry_str_add(table_row, &l_col_select, select_str); -+ table_row_entry_str_add(table_row, &l_col_key, char_str); -+ table_row_entry_str_add(table_row, &l_col_id, cpu_type->id); -+ table_row_entry_str_add(table_row, &l_col_desc, cpu_type->desc); -+ table_row_add(win_cpu_types->t, table_row); -+ if (sd_cpu_type_selected(cpu_type)) -+ table_row_mark_toggle(win_cpu_types->t, table_row); -+} -+ -+/* -+ * Fill all available CPU types into table -+ */ -+static void l_table_create(struct win_cpu_types *win_cpu_types) -+{ -+ struct sd_cpu_type *cpu_type; -+ unsigned int i; -+ -+ table_row_del_all(win_cpu_types->t); -+ table_row_mark_del_all(win_cpu_types->t); -+ sd_cpu_type_iterate(cpu_type, i) -+ l_add_cpu_type(win_cpu_types, cpu_type); -+ table_finish(win_cpu_types->t); -+} -+ -+/* -+ * Toggle the cpu type specified by "key" in the system data module -+ */ -+static void l_toggle_cpu_type(char key) -+{ -+ struct sd_cpu_type *cpu_type; -+ unsigned int i; -+ -+ sd_cpu_type_iterate(cpu_type, i) { -+ if (key == cpu_type->hotkey) { -+ sd_cpu_type_select_toggle(cpu_type); -+ return; -+ } -+ } -+} -+ -+/* -+ * Process input for selection with SPACE key -+ */ -+static void l_process_input_select_space(struct win_cpu_types *win_cpu_types) -+{ -+ char cpu_type_key[TABLE_STR_MAX]; -+ -+ if (table_mode_select(win_cpu_types->t)) { -+ table_row_select_key_get(win_cpu_types->t, cpu_type_key); -+ l_toggle_cpu_type(cpu_type_key[0]); -+ } else { -+ struct table_mark_key *key; -+ -+ table_iterate_mark_keys(win_cpu_types->t, key) -+ l_toggle_cpu_type(key->str[0]); -+ } -+} -+ -+/* -+ * Process input for selection with hotkey -+ */ -+static void l_process_input_select_key(struct win_cpu_types *win_cpu_types, -+ int c) -+{ -+ char cpu_type_key[TABLE_STR_MAX]; -+ -+ sprintf(cpu_type_key, "%c", c); -+ table_row_mark_toggle_by_key(win_cpu_types->t, cpu_type_key); -+ l_toggle_cpu_type(cpu_type_key[0]); -+} -+ -+/* -+ * Process input and switch window if necessary -+ */ -+static enum hyptop_win_action l_process_input(struct hyptop_win *win, int c) -+{ -+ struct win_cpu_types *win_cpu_types = (struct win_cpu_types *) win; -+ -+ switch (c) { -+ case 't': -+ case 'q': -+ return win_back(); -+ case KEY_RETURN: -+ case KEY_ENTER: -+ case 'h': -+ case KEY_LEFT: -+ if (!table_mode_select(win_cpu_types->t)) -+ return win_back(); -+ break; -+ case '?': -+ return win_switch(win_cpu_types->win_help); -+ case ' ': -+ l_process_input_select_space(win_cpu_types); -+ break; -+ case ERR: -+ return WIN_KEEP; -+ default: -+ l_process_input_select_key(win_cpu_types, c); -+ break; -+ } -+ table_process_input(win_cpu_types->t, c); -+ hyptop_update_term(); -+ return WIN_KEEP; -+} -+ -+/* -+ * Event loop: We stay in hyptop_process_input() until fields menu -+ * is left. -+ */ -+static void l_run(struct hyptop_win *win) -+{ -+ struct win_cpu_types *win_cpu_types = (struct win_cpu_types *) win; -+ -+ table_reset(win_cpu_types->t); -+ while (1) { -+ hyptop_update_term(); -+ if (hyptop_process_input() == WIN_SWITCH) -+ return; -+ } -+} -+ -+/* -+ * Create table and print it to screen -+ */ -+static void l_update_term(struct hyptop_win *win) -+{ -+ struct win_cpu_types *win_cpu_types = (struct win_cpu_types *) win; -+ -+ l_table_create(win_cpu_types); -+ hyptop_printf("Select Processor Types"); -+ ht_print_help_icon(); -+ hyptop_print_nl(); -+ -+ table_print(win_cpu_types->t); -+} -+ -+/* -+ * Create new cpu_types window -+ */ -+struct hyptop_win *win_cpu_types_new(void) -+{ -+ struct win_cpu_types *win_cpu_types; -+ -+ win_cpu_types = ht_zalloc(sizeof(*win_cpu_types)); -+ -+ win_cpu_types->win.process_input = l_process_input; -+ win_cpu_types->win.update_term = l_update_term; -+ win_cpu_types->win.run = l_run; -+ win_cpu_types->win.desc = l_help_str; -+ win_cpu_types->win.desc_normal_vec = l_nav_desc_normal_vec; -+ win_cpu_types->win.desc_select_vec = l_nav_desc_select_vec; -+ win_cpu_types->win.desc_general_vec = l_nav_desc_general_vec; -+ win_cpu_types->win.id = "cpu_types"; -+ -+ win_cpu_types->t = table_new(1, 0, 0, 0); -+ table_col_add(win_cpu_types->t, &l_col_key); -+ table_col_add(win_cpu_types->t, &l_col_select); -+ table_col_add(win_cpu_types->t, &l_col_id); -+ table_col_add(win_cpu_types->t, &l_col_desc); -+ -+ win_cpu_types->win_help = -+ win_help_new((struct hyptop_win *) win_cpu_types); -+ l_table_create(win_cpu_types); -+ return (struct hyptop_win *) win_cpu_types; -+} -diff --git a/hyptop/win_cpu_types.h b/hyptop/win_cpu_types.h -new file mode 100644 -index 0000000..b0f29a7 ---- /dev/null -+++ b/hyptop/win_cpu_types.h -@@ -0,0 +1,26 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Window "cpu_types": Select CPU types used for CPU data calculation. -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#ifndef WIN_CPU_TYPES_H -+#define WIN_CPU_TYPES_H -+ -+#include "hyptop.h" -+#include "table.h" -+#include "win_help.h" -+ -+struct win_cpu_types { -+ struct hyptop_win win; -+ struct table *t; -+ int in_select; -+ struct hyptop_win *win_help; -+}; -+ -+extern struct hyptop_win *win_cpu_types_new(void); -+ -+#endif /* WIN_CPU_TYPES_H */ -diff --git a/hyptop/win_fields.c b/hyptop/win_fields.c -new file mode 100644 -index 0000000..8d5e233 ---- /dev/null -+++ b/hyptop/win_fields.c -@@ -0,0 +1,272 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Window "fields": Select fields dialog. -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include "helper.h" -+#include "hyptop.h" -+#include "table.h" -+#include "win_fields.h" -+ -+ -+/* -+ * Globals for fields window -+ */ -+static struct table_col l_col_select = TABLE_COL_STR_LEFT('s', "s"); -+static struct table_col l_col_id = TABLE_COL_STR_LEFT('i', "id"); -+static struct table_col l_col_key = TABLE_COL_STR_LEFT('k', "k"); -+static struct table_col l_col_unit = TABLE_COL_STR_LEFT('u', "unit"); -+static struct table_col l_col_agg = TABLE_COL_STR_LEFT('a', "agg"); -+static struct table_col l_col_desc = TABLE_COL_STR_LEFT('d', "description"); -+ -+/* -+ * Online help text for fields window -+ */ -+static const char l_help_str[] = -+"In the \"fields\" window you can select fields and units. Toggle the selection\n" -+"of fields either by pressing the corresponding hotkey or by selecting them\n" -+"in select mode using the SPACE bar. The units can be changed by selecting a\n" -+"field in select mode and by pressing '+' or '-'.\n" -+"\n" -+"The table of the \"fields\" window has the following columns:\n" -+" - K : Hotkey of field\n" -+" - S : Shows if field is selected\n" -+" - ID : Name of field\n" -+" - UNIT: Current unit used for field\n" -+" - AGG : Aggregation used for last line of table\n" -+" - DESC: Description of field\n"; -+ -+/* -+ * Description of Navigation Keys (used for help window) -+ */ -+static struct nav_desc *l_nav_desc_normal_vec[] = { -+ &nav_desc_select_mode_enter, -+ &nav_desc_marks_clear, -+ &nav_desc_win_leave_fields, -+ NULL, -+}; -+ -+static struct nav_desc *l_nav_desc_select_vec[] = { -+ &nav_desc_select_mode_leave, -+ &nav_desc_mark_toggle, -+ &nav_desc_row_unit_increase, -+ &nav_desc_row_unit_decrease, -+ &nav_desc_win_leave_fields_fast, -+ NULL, -+}; -+ -+static struct nav_desc *l_nav_desc_general_vec[] = { -+ &nav_desc_toggle_mark_hotkey, -+ &nav_desc_scroll_up_line, -+ &nav_desc_scroll_down_line, -+ &nav_desc_scroll_up_page, -+ &nav_desc_scroll_down_page, -+ &nav_desc_scroll_up_head, -+ &nav_desc_scroll_down_tail, -+ &nav_desc_mark_toggle_view, -+ NULL, -+}; -+ -+/* -+ * Add a field that is the column of the reference table to the table -+ */ -+static void l_add_field(struct win_fields *win_fields, struct table_col *col, -+ const char *desc) -+{ -+ char char_str[2], select_str[2]; -+ struct table_row *table_row; -+ -+ if (table_col_enabled(col)) -+ sprintf(select_str, "*"); -+ else -+ sprintf(select_str, " "); -+ sprintf(char_str, "%c", table_col_hotkey(col)); -+ -+ table_row = table_row_alloc(win_fields->t); -+ table_row_entry_str_add(table_row, &l_col_select, select_str); -+ table_row_entry_str_add(table_row, &l_col_key, char_str); -+ table_row_entry_str_add(table_row, &l_col_id, table_col_head(col)); -+ table_row_entry_str_add(table_row, &l_col_unit, -+ table_col_unit_str(col)); -+ table_row_entry_str_add(table_row, &l_col_agg, -+ table_col_agg_str(col->agg)); -+ table_row_entry_str_add(table_row, &l_col_desc, desc); -+ table_row_add(win_fields->t, table_row); -+ -+ if (table_col_enabled(col)) -+ table_row_mark_toggle(win_fields->t, table_row); -+} -+ -+/* -+ * Fill all field information into table -+ */ -+static void l_table_create(struct win_fields *win_fields) -+{ -+ unsigned int i; -+ -+ table_row_del_all(win_fields->t); -+ table_row_mark_del_all(win_fields->t); -+ for (i = 0; win_fields->col_vec[i]; i++) { -+ l_add_field(win_fields, win_fields->col_vec[i], -+ win_fields->col_desc_vec[i]); -+ } -+ table_finish(win_fields->t); -+} -+ -+/* -+ * Process input for selection with SPACE key -+ */ -+static void l_process_input_select_space(struct win_fields *win_fields) -+{ -+ char field_key[TABLE_STR_MAX]; -+ -+ if (table_mode_select(win_fields->t)) { -+ table_row_select_key_get(win_fields->t, field_key); -+ table_col_enable_toggle(win_fields->table, field_key[0]); -+ } else { -+ struct table_mark_key *key; -+ /* switch off all fields in reference table */ -+ table_iterate_mark_keys(win_fields->t, key) -+ table_col_enable_toggle(win_fields->table, -+ key->str[0]); -+ } -+} -+ -+/* -+ * Process input for selection with hotkey -+ */ -+static void l_process_input_select_key(struct win_fields *win_fields, int c) -+{ -+ char field_key[TABLE_STR_MAX]; -+ -+ sprintf(field_key, "%c", c); -+ table_row_mark_toggle_by_key(win_fields->t, field_key); -+ table_col_enable_toggle(win_fields->table, field_key[0]); -+} -+ -+/* -+ * Process input for unit selection -+ */ -+static void l_process_input_units(struct win_fields *win_fields, int c) -+{ -+ char field_key[TABLE_STR_MAX]; -+ -+ if (!table_mode_select(win_fields->t)) -+ return; -+ table_row_select_key_get(win_fields->t, field_key); -+ if (c == '+') -+ table_col_unit_next(win_fields->table, field_key[0]); -+ else -+ table_col_unit_prev(win_fields->table, field_key[0]); -+} -+ -+/* -+ * Process input and switch window if necessary -+ */ -+static enum hyptop_win_action l_process_input(struct hyptop_win *win, int c) -+{ -+ struct win_fields *win_fields = (struct win_fields *) win; -+ -+ switch (c) { -+ case 'f': -+ case 'q': -+ return win_back(); -+ case KEY_RETURN: -+ case KEY_ENTER: -+ case 'h': -+ case KEY_LEFT: -+ if (!table_mode_select(win_fields->t)) -+ return win_back(); -+ break; -+ case '?': -+ return win_switch(win_fields->win_help); -+ case ' ': -+ l_process_input_select_space(win_fields); -+ break; -+ case '+': -+ case '-': -+ l_process_input_units(win_fields, c); -+ break; -+ case ERR: -+ return WIN_KEEP; -+ default: -+ l_process_input_select_key(win_fields, c); -+ break; -+ } -+ table_process_input(win_fields->t, c); -+ hyptop_update_term(); -+ return WIN_KEEP; -+} -+ -+/* -+ * Event loop: We stay in hyptop_process_input() until fields menu -+ * is left. -+ */ -+static void l_run(struct hyptop_win *win) -+{ -+ struct win_fields *win_fields = (struct win_fields *) win; -+ -+ table_reset(win_fields->t); -+ while (1) { -+ hyptop_update_term(); -+ if (hyptop_process_input() == WIN_SWITCH) -+ return; -+ } -+} -+ -+/* -+ * Create table and print it to screen -+ */ -+static void l_update_term(struct hyptop_win *win) -+{ -+ struct win_fields *win_fields = (struct win_fields *) win; -+ -+ l_table_create(win_fields); -+ hyptop_printf("Select Fields and Units"); -+ ht_print_help_icon(); -+ hyptop_print_nl(); -+ table_print(win_fields->t); -+} -+ -+/* -+ * Create new fields window -+ * -+ * - t...........: Reference table -+ * - col_vec.....: Table column vector for fields -+ * - col_desc_vec: Vector with descriptions for fields -+ */ -+struct hyptop_win *win_fields_new(struct table *t, struct table_col **col_vec, -+ char **col_desc_vec) -+{ -+ struct win_fields *win_fields; -+ -+ win_fields = ht_zalloc(sizeof(*win_fields)); -+ -+ win_fields->win.process_input = l_process_input; -+ win_fields->win.update_term = l_update_term; -+ win_fields->win.run = l_run; -+ win_fields->win.desc = l_help_str; -+ win_fields->win.desc_normal_vec = l_nav_desc_normal_vec; -+ win_fields->win.desc_select_vec = l_nav_desc_select_vec; -+ win_fields->win.desc_general_vec = l_nav_desc_general_vec; -+ win_fields->win.id = "fields"; -+ -+ win_fields->t = table_new(1, 0, 0, 0); -+ table_col_add(win_fields->t, &l_col_key); -+ table_col_add(win_fields->t, &l_col_select); -+ table_col_add(win_fields->t, &l_col_id); -+ table_col_add(win_fields->t, &l_col_unit); -+ table_col_add(win_fields->t, &l_col_agg); -+ table_col_add(win_fields->t, &l_col_desc); -+ win_fields->col_desc_vec = col_desc_vec; -+ win_fields->col_vec = col_vec; -+ win_fields->table = t; -+ win_fields->win_help = win_help_new((struct hyptop_win *) win_fields); -+ -+ l_table_create(win_fields); -+ return (struct hyptop_win *) win_fields; -+} -diff --git a/hyptop/win_fields.h b/hyptop/win_fields.h -new file mode 100644 -index 0000000..b399203 ---- /dev/null -+++ b/hyptop/win_fields.h -@@ -0,0 +1,31 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Window "fields": Select fields dialog. -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#ifndef WIN_FIELDS_H -+#define WIN_FIELDS_H -+ -+#include "table.h" -+#include "hyptop.h" -+#include "win_help.h" -+ -+struct win_fields { -+ struct hyptop_win win; -+ struct table *t; -+ struct table *table; -+ struct table_col **col_vec; -+ char **col_desc_vec; -+ int mode_unit_change; -+ int in_select; -+ struct hyptop_win *win_help; -+}; -+ -+struct hyptop_win *win_fields_new(struct table *t, struct table_col **col_vec, -+ char **col_desc_vec); -+ -+#endif /* WIN_FIELDS_H */ -diff --git a/hyptop/win_help.c b/hyptop/win_help.c -new file mode 100644 -index 0000000..f18d0bb ---- /dev/null -+++ b/hyptop/win_help.c -@@ -0,0 +1,122 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Window "help": Show online help text. -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include "helper.h" -+#include "table.h" -+#include "hyptop.h" -+#include "win_help.h" -+#include "sd.h" -+ -+/* -+ * Print help text to screen -+ */ -+static void l_update_term(struct hyptop_win *win) -+{ -+ struct win_help *win_help = (struct win_help *) win; -+ tbox_print(win_help->tb); -+} -+ -+/* -+ * Process input and switch window if necessary -+ */ -+static enum hyptop_win_action l_process_input(struct hyptop_win *win, int c) -+{ -+ struct win_help *win_help = (struct win_help *) win; -+ -+ switch (c) { -+ case 'h': -+ case KEY_RETURN: -+ case KEY_ENTER: -+ case KEY_LEFT: -+ case '?': -+ case 'q': -+ return win_back(); -+ case 'G': -+ tbox_scroll_down(win_help->tb, TBOX_SCROLL_LAST); -+ break; -+ case 'g': -+ tbox_scroll_up(win_help->tb, TBOX_SCROLL_LAST); -+ break; -+ case KEY_NPAGE: -+ tbox_scroll_down(win_help->tb, TBOX_SCROLL_PAGE); -+ break; -+ case KEY_PPAGE: -+ tbox_scroll_up(win_help->tb, TBOX_SCROLL_PAGE); -+ break; -+ case 'k': -+ case KEY_UP: -+ tbox_scroll_up(win_help->tb, TBOX_SCROLL_LINE); -+ break; -+ case 'j': -+ case KEY_DOWN: -+ tbox_scroll_down(win_help->tb, TBOX_SCROLL_LINE); -+ break; -+ case ERR: -+ return WIN_KEEP; -+ default: -+ break; -+ } -+ hyptop_update_term(); -+ return WIN_KEEP; -+} -+ -+/* -+ * Event loop: wait for input and print help text -+ */ -+static void l_run(struct hyptop_win *win) -+{ -+ (void) win; -+ -+ while (1) { -+ hyptop_update_term(); -+ if (hyptop_process_input() == WIN_SWITCH) -+ return; -+ } -+} -+ -+/* -+ * Add text to text box -+ */ -+static void l_add_text(struct tbox *tb, const char *str) -+{ -+ char *line, *line_end, *str_cpy; -+ -+ str_cpy = line_end = ht_strdup(str); -+ for (line = str_cpy; line_end != NULL; line = line_end + 1) { -+ line_end = strchr(line, '\n'); -+ if (line_end) -+ *line_end = 0; -+ tbox_line_add(tb, line); -+ } -+ ht_free(str_cpy); -+} -+ -+/* -+ * Create new help window for "win" and init window description -+ */ -+struct hyptop_win *win_help_new(struct hyptop_win *win) -+{ -+ struct win_help *win_help; -+ -+ win_help = ht_zalloc(sizeof(*win_help)); -+ -+ win_help->tb = tbox_new(); -+ tbox_printf(win_help->tb, "\\BWindow: %s\\B", win->id); -+ tbox_printf(win_help->tb, " "); -+ l_add_text(win_help->tb, win->desc); -+ nav_desc_add(win_help->tb, win->desc_normal_vec, win->desc_select_vec, -+ win->desc_general_vec); -+ tbox_finish(win_help->tb); -+ -+ win_help->win.process_input = l_process_input; -+ win_help->win.update_term = l_update_term; -+ win_help->win.run = l_run; -+ -+ return (struct hyptop_win *) win_help; -+} -diff --git a/hyptop/win_help.h b/hyptop/win_help.h -new file mode 100644 -index 0000000..5f4f45d ---- /dev/null -+++ b/hyptop/win_help.h -@@ -0,0 +1,23 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Window "help": Show online help text. -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#ifndef WIN_HELP_H -+#define WIN_HELP_H -+ -+#include "tbox.h" -+#include "hyptop.h" -+ -+struct win_help { -+ struct hyptop_win win; -+ struct tbox *tb; -+}; -+ -+struct hyptop_win *win_help_new(struct hyptop_win *win); -+ -+#endif /* WIN_HELP_H */ -diff --git a/hyptop/win_sys.c b/hyptop/win_sys.c -new file mode 100644 -index 0000000..2039c72 ---- /dev/null -+++ b/hyptop/win_sys.c -@@ -0,0 +1,387 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Window "sys": Shows one system in more detail. -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include -+#include "helper.h" -+#include "table.h" -+#include "hyptop.h" -+#include "sd.h" -+#include "win_fields.h" -+#include "win_help.h" -+#include "opts.h" -+ -+/* -+ * Globals for sys_list window -+ */ -+static char l_sys_id[SD_SYS_ID_SIZE]; /* System to show */ -+static struct table_col l_cpu_col; /* CPU column */ -+static struct table_col l_vis_col; /* Visual column */ -+static struct table *l_t; /* Table */ -+static int l_initialized; /* Win initialized ? */ -+static struct hyptop_win *l_win_fields; /* Fields window */ -+static struct hyptop_win *l_win_help; /* Help window */ -+ -+/* CPU column */ -+static struct table_col l_cpu_col = { -+ .type = TABLE_COL_TYPE_U64, -+ .unit = &table_col_unit_cnt, -+ .unit_fam = table_col_unit_fam_cnt, -+ .align = TABLE_COL_ALIGN_LEFT, -+ .agg = TABLE_COL_AGG_NONE, -+ .hotkey = 'i', -+ .head = "cpuid", -+}; -+ -+/* Visual column */ -+static struct table_col l_vis_col = { -+ .type = TABLE_COL_TYPE_U64, -+ .unit = &table_col_unit_vis, -+ .unit_fam = table_col_unit_fam_vis, -+ .align = TABLE_COL_ALIGN_LEFT, -+ .agg = TABLE_COL_AGG_NONE, -+ .hotkey = 'v', -+ .head = "visual", -+}; -+ -+/* -+ * Online help text for sys window -+ */ -+static const char l_help_str[] = -+"The \"sys\" window displays CPU information about one selected system.\n" -+"Under z/VM you can only see aggregated CPU information and not information\n" -+"about single CPUs.\n" -+"\n" -+"Select a column by pressing the hotkey of the column. This key is underlined\n" -+"in the heading. The table is sorted according to the values in the selected\n" -+"column. If you press the hotkey again, the sort order is reversed.\n" -+"Alternatively you can select columns with the '<' and '>' keys.\n"; -+ -+/* -+ * Description of Navigation Keys (used for help window) -+ */ -+static struct nav_desc *l_nav_desc_normal_vec[] = { -+ &nav_desc_select_mode_enter, -+ &nav_desc_marks_clear, -+ &nav_desc_win_leave_sys, -+ NULL, -+}; -+ -+static struct nav_desc *l_nav_desc_select_vec[] = { -+ &nav_desc_select_mode_leave, -+ &nav_desc_mark_toggle, -+ &nav_desc_win_leave_sys_fast, -+ NULL, -+}; -+ -+static struct nav_desc *l_nav_desc_general_vec[] = { -+ &nav_desc_win_enter_fields, -+ &nav_desc_win_enter_cpu_types, -+ &nav_desc_col_unit_increase, -+ &nav_desc_col_unit_decrease, -+ &nav_desc_select_col_next, -+ &nav_desc_select_col_prev, -+ &nav_desc_select_col_hotkey, -+ &nav_desc_scroll_up_line, -+ &nav_desc_scroll_down_line, -+ &nav_desc_scroll_up_page, -+ &nav_desc_scroll_down_page, -+ &nav_desc_scroll_up_head, -+ &nav_desc_scroll_down_tail, -+ &nav_desc_mark_toggle_view, -+ NULL, -+}; -+ -+/* -+ * Add CPU item to table row -+ */ -+static void l_cpu_item_add(struct table_row *table_row, struct sd_cpu *cpu, -+ struct sd_cpu_item *item) -+{ -+ switch (sd_cpu_item_type(item)) { -+ case SD_TYPE_U16: -+ case SD_TYPE_U32: -+ assert(0); -+ break; -+ case SD_TYPE_U64: -+ table_row_entry_u64_add(table_row, -+ sd_cpu_item_table_col(item), -+ sd_cpu_item_u64(item, cpu)); -+ break; -+ case SD_TYPE_S64: -+ table_row_entry_s64_add(table_row, -+ sd_cpu_item_table_col(item), -+ sd_cpu_item_s64(item, cpu)); -+ break; -+ case SD_TYPE_STR: -+ table_row_entry_str_add(table_row, -+ sd_cpu_item_table_col(item), -+ sd_cpu_item_str(item, cpu)); -+ break; -+ } -+} -+ -+/* -+ * Add visualization of CPU time to table row -+ */ -+static void l_cpu_add_visual(struct table_row *table_row, struct sd_cpu *cpu) -+{ -+ s64 steal_us; -+ u64 cpu_us; -+ -+ cpu_us = sd_cpu_item_u64(&sd_cpu_item_cpu_diff, cpu) / sd_cpu_cnt(cpu); -+ steal_us = sd_cpu_item_s64(&sd_cpu_item_steal_diff, cpu) / -+ sd_cpu_cnt(cpu); -+ steal_us = MAX(steal_us, 0); -+ table_row_entry_u64_add_pair(table_row, &l_vis_col, cpu_us, steal_us); -+} -+ -+/* -+ * Add CPU to table -+ */ -+static void l_cpu_add(struct sd_cpu *cpu) -+{ -+ struct table_row *table_row; -+ struct sd_cpu_item *item; -+ unsigned int cpu_id; -+ unsigned int i; -+ -+ table_row = table_row_alloc(l_t); -+ cpu_id = atoi(sd_cpu_id(cpu)); -+ table_row_entry_u64_add(table_row, &l_cpu_col, cpu_id); -+ -+ sd_cpu_item_iterate(item, i) -+ l_cpu_item_add(table_row, cpu, item); -+ l_cpu_add_visual(table_row, cpu); -+ table_row_add(l_t, table_row); -+} -+ -+/* -+ * Fill system CPU data into table -+ */ -+static int l_table_create(void) -+{ -+ struct sd_sys *parent; -+ struct sd_cpu *cpu; -+ -+ parent = sd_sys_get(sd_sys_root_get(), l_sys_id); -+ if (!parent) -+ return -ENODEV; -+ table_row_del_all(l_t); -+ sd_cpu_iterate(parent, cpu) -+ l_cpu_add(cpu); -+ table_finish(l_t); -+ return 0; -+} -+ -+/* -+ * Print table to screen -+ */ -+static void l_table_update_term(struct hyptop_win *win) -+{ -+ (void) win; -+ -+ ht_print_head(l_sys_id); -+ table_print(l_t); -+} -+ -+/* -+ * Process input and switch window if necessary -+ */ -+static enum hyptop_win_action l_process_input(struct hyptop_win *win, int c) -+{ -+ (void) win; -+ -+ switch (c) { -+ case 't': -+ return win_switch(g.win_cpu_types); -+ case '?': -+ return win_switch(l_win_help); -+ case 'f': -+ return win_switch(l_win_fields); -+ case 'q': -+ return win_back(); -+ case 'h': -+ case KEY_LEFT: -+ if (!(table_mode_select(l_t))) -+ return win_back(); -+ break; -+ case ERR: -+ return WIN_KEEP; -+ } -+ table_process_input(l_t, c); -+ hyptop_update_term(); -+ return WIN_KEEP; -+} -+ -+/* -+ * Enable field and set unit -+ */ -+static void l_field_set(struct table_col_spec *col_spec) -+{ -+ table_col_enable_toggle(l_t, col_spec->hotkey); -+ if (!col_spec->unit_str) -+ return; -+ if (table_col_unit_set(l_t, col_spec->hotkey, col_spec->unit_str)) -+ ERR_EXIT("Invalid unit \"%s\" for field \"%c\"\n", -+ col_spec->unit_str, col_spec->hotkey); -+} -+ -+/* -+ * Enable field defined in "col_spec" -+ */ -+static void l_field_enable(struct table_col_spec *col_spec) -+{ -+ struct sd_cpu_item *item; -+ struct table_col *col; -+ unsigned int i; -+ -+ if (table_col_hotkey(&l_vis_col) == col_spec->hotkey) { -+ l_field_set(col_spec); -+ return; -+ } -+ sd_cpu_item_iterate(item, i) { -+ col = sd_cpu_item_table_col(item); -+ if (table_col_hotkey(col) != col_spec->hotkey) -+ continue; -+ l_field_set(col_spec); -+ return; -+ } -+ ERR_EXIT("Unknown field \"%c\"\n", col_spec->hotkey); -+} -+ -+/* -+ * Enable fields defined on command line -+ */ -+static void l_fields_enable_cmdline(void) -+{ -+ unsigned int i; -+ -+ table_col_enable_toggle(l_t, table_col_hotkey(&l_vis_col)); -+ for (i = 0; i < win_sys.opts.fields.cnt; i++) -+ l_field_enable(win_sys.opts.fields.vec[i]); -+} -+ -+/* -+ * Enable fields like defined in data gatherer -+ */ -+static void l_fields_enable_default(void) -+{ -+ struct sd_cpu_item *item; -+ struct table_col *col; -+ unsigned int i; -+ -+ sd_cpu_item_enable_iterate(item, i) { -+ col = sd_cpu_item_table_col(item); -+ table_col_enable_toggle(l_t, table_col_hotkey(col)); -+ } -+} -+ -+/* -+ * Event loop: Make regular updates of table -+ */ -+static void l_run(struct hyptop_win *win) -+{ -+ enum hyptop_win_action action; -+ (void) win; -+ -+ /* Reformat table when entering window */ -+ table_rebuild(l_t); -+ while (1) { -+ if (l_table_create()) { -+ if (g.o.batch_mode_specified) -+ ERR_EXIT("System \"%s\" not available.\n", -+ l_sys_id); -+ win_back(); -+ return; -+ } -+ hyptop_update_term(); -+ action = hyptop_process_input_timeout(); -+ if (action == WIN_SWITCH) -+ return; -+ -+ /* No updates in select mode */ -+ if (!table_mode_select(l_t)) -+ sd_update(); -+ } -+} -+ -+/* -+ * Define system for window -+ */ -+void win_sys_set(const char *sys_id) -+{ -+ if (l_initialized) -+ table_reset(l_t); -+ strncpy(l_sys_id, sys_id, sizeof(l_sys_id)); -+} -+ -+/* -+ * Initialize window -+ */ -+void win_sys_init(void) -+{ -+ struct table_col **col_vec; -+ struct sd_cpu_item *item; -+ struct table_col *col; -+ char **col_desc_vec; -+ unsigned int i, item_cnt; -+ -+ /* Alloc table and add columns */ -+ l_t = table_new(1, 1, 1, 1); -+ table_col_add(l_t, &l_cpu_col); -+ table_col_rsort(&l_cpu_col); -+ -+ item_cnt = sd_cpu_item_cnt() + 2; -+ col_vec = ht_zalloc(sizeof(void *) * item_cnt); -+ col_desc_vec = ht_zalloc(sizeof(void *) * item_cnt); -+ -+ sd_cpu_item_iterate(item, i) { -+ col = sd_cpu_item_table_col(item); -+ table_col_add(l_t, col); -+ table_col_enable_toggle(l_t, table_col_hotkey(col)); -+ col_vec[i] = col; -+ col_desc_vec[i] = item->desc; -+ } -+ col_vec[i] = &l_vis_col; -+ col_desc_vec[i] = "Visualization of CPU time per second"; -+ table_col_add(l_t, &l_vis_col); -+ -+ /* Enable fields */ -+ if (win_sys.opts.fields.specified) -+ l_fields_enable_cmdline(); -+ else -+ l_fields_enable_default(); -+ -+ /* Select sort field */ -+ if (win_sys.opts.sort_field_specified) { -+ for (i = 0; i < win_sys.opts.sort_field_specified; i++) { -+ if (table_col_select(l_t, win_sys.opts.sort_field)) -+ ERR_EXIT("Sort field \"%c\" is not available\n", -+ win_sys.opts.sort_field); -+ } -+ } -+ /* Initialize help and fields window */ -+ l_win_fields = win_fields_new(l_t, col_vec, col_desc_vec); -+ l_win_help = win_help_new(&win_sys); -+ l_initialized = 1; -+} -+ -+/* -+ * hyptop window structure definition -+ */ -+struct hyptop_win win_sys = { -+ .process_input = l_process_input, -+ .update_term = l_table_update_term, -+ .run = l_run, -+ .id = "sys", -+ .desc = l_help_str, -+ .desc_normal_vec = l_nav_desc_normal_vec, -+ .desc_select_vec = l_nav_desc_select_vec, -+ .desc_general_vec = l_nav_desc_general_vec, -+}; -diff --git a/hyptop/win_sys_list.c b/hyptop/win_sys_list.c -new file mode 100644 -index 0000000..79eb092 ---- /dev/null -+++ b/hyptop/win_sys_list.c -@@ -0,0 +1,380 @@ -+/* -+ * hyptop - Show hypervisor performance data on System z -+ * -+ * Window "sys_list": -+ * Shows a list of systems that the hypervisor is currently running. -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include "helper.h" -+#include "table.h" -+#include "hyptop.h" -+#include "win_fields.h" -+#include "win_help.h" -+#include "sd.h" -+#include "nav_desc.h" -+#include "opts.h" -+ -+/* -+ * Globals for sys_list window -+ */ -+static struct table *l_t; /* Table */ -+static struct hyptop_win *l_win_fields; /* Fields Window */ -+static struct hyptop_win *l_win_help; /* Herp Window */ -+ -+/* System column */ -+static struct table_col l_col_sys = TABLE_COL_STR_LEFT('y', "system"); -+ -+/* -+ * Online help text for sys_list window -+ */ -+static const char l_help_str[] = -+"The following windows can be accessed:\n" -+"\n" -+" +-----------+ RIGHT +----------+\n" -+" | | <----------------------> | |\n" -+" | | LEFT | |\n" -+" | | | |\n" -+" | sys_list | 't' +-----------+ | | 't' +-----------+\n" -+" | | <-------> | cpu_types | | sys | <-------> | cpu_types |\n" -+" | (start) | 't',LEFT +-----------+ | | 't',LEFT +-----------+\n" -+" | | | |\n" -+" | | 'f' +--------+ | | 'f' +--------+\n" -+" | | <-------> | fields | | | <-------> | fields |\n" -+" | | 'f',LEFT +--------+ | | 'f',LEFT +--------+\n" -+" +-----------+ +----------+\n" -+"\n" -+" * sys_list: Start window that shows a list of systems that the hypervisor\n" -+" is currently running.\n" -+" * sys: Shows one system in more detail.\n" -+" * cpu_types: Select CPU types that are used for calculating CPU data.\n" -+" * fields: Select fields and units for windows sys_list or sys.\n" -+"\n" -+"\\BNavigation\\B\n" -+"\n" -+"To navigate between the windows, use the arrow keys or 'hjkl'. The windows\n" -+"have two modes, \"normal mode\" and \"select mode\". When you start the " -+"program,\n" -+"the window is in normal mode where data is updated at regular intervals. Use\n" -+"the RIGHT arrow key to enter the select mode. In select mode you can select\n" -+"rows with with UP and DOWN arrow keys and mark them with the SPACE bar. From\n" -+"the \"sys_list\" window you can access the \"sys\" window in select mode\n" -+"with the arrow key RIGHT. Leave the select mode with the arrow key LEFT.\n" -+"If you are in normal mode, the arrow key LEFT goes to the previous window.\n" -+"You can scroll all windows using the arrow keys UP, DOWN, PAGEUP and\n" -+"PAGEDOWN. You can jump to the end of a window with 'G' and to the beginning\n" -+"with 'g'.\n" -+"\n" -+"Select a column by pressing the hotkey of the column. This key is underlined\n" -+"in the heading. The table is sorted according to the values in the selected\n" -+"column. If you press the hotkey again, the sort order is reversed.\n" -+"Alternatively you can select columns with the '<' and '>' keys.\n" -+"\n" -+"\\BTable layout\\B\n" -+"\n" -+"At the top left of the table the current time is shown. Then the CPU types\n" -+"with the physical CPU numbers that are used for CPU time calculation are\n" -+"displayed. The second row shows the units that are currently used for\n" -+"formatting the data. The last row shows the status display (see description\n" -+"below) and the aggregation of the the data columns. The last row aggregates\n" -+"all rows, not only the visible ones. If only the marked rows are shown\n" -+"(with '.') then only these rows are aggregated.\n" -+"\n" -+"\\BStatus display\\B\n" -+"\n" -+"At the left bottom of the screen a status display is shown.\n" -+"Example: \"V:V:N\"\n\n" -+"The first character shows, if the window can be scrolled:\n" -+" 'V': Window can be scrolled down\n" -+" '|': Window can be scrolled up/down\n" -+" '^': Window can be scrolled up\n" -+" '=': Window cannot be scrolled\n" -+"The second character shows the sort order for sorted tables:\n" -+" 'V': Higher values first\n" -+" '^': Lower values first\n" -+"The third character shows the current mode:\n" -+" 'N': Normal mode\n" -+" 'S': Select mode\n"; -+ -+/* -+ * Description of Navigation Keys (used for help window) -+ */ -+static struct nav_desc *l_nav_desc_normal_vec[] = { -+ &nav_desc_select_mode_enter, -+ &nav_desc_marks_clear, -+ NULL, -+}; -+ -+static struct nav_desc *l_nav_desc_select_vec[] = { -+ &nav_desc_select_mode_leave, -+ &nav_desc_win_enter_sys, -+ &nav_desc_mark_toggle, -+ NULL, -+}; -+ -+static struct nav_desc *l_nav_desc_general_vec[] = { -+ &nav_desc_win_enter_fields, -+ &nav_desc_win_enter_cpu_types, -+ &nav_desc_col_unit_increase, -+ &nav_desc_col_unit_decrease, -+ &nav_desc_select_col_next, -+ &nav_desc_select_col_prev, -+ &nav_desc_select_col_hotkey, -+ &nav_desc_scroll_up_line, -+ &nav_desc_scroll_down_line, -+ &nav_desc_scroll_up_page, -+ &nav_desc_scroll_down_page, -+ &nav_desc_scroll_up_head, -+ &nav_desc_scroll_down_tail, -+ &nav_desc_mark_toggle_view, -+ &nav_desc_quit, -+ NULL, -+}; -+ -+/* -+ * Add system item to table row -+ */ -+static void l_sys_item_add(struct table_row *table_row, struct sd_sys *sys, -+ struct sd_sys_item *item) -+{ -+ switch (sd_sys_item_type(item)) { -+ case SD_TYPE_U64: -+ case SD_TYPE_U32: -+ case SD_TYPE_U16: -+ table_row_entry_u64_add(table_row, -+ sd_sys_item_table_col(item), -+ sd_sys_item_u64(sys, item)); -+ break; -+ case SD_TYPE_S64: -+ table_row_entry_s64_add(table_row, -+ sd_sys_item_table_col(item), -+ sd_sys_item_s64(sys, item)); -+ break; -+ case SD_TYPE_STR: -+ table_row_entry_str_add(table_row, -+ sd_sys_item_table_col(item), -+ sd_sys_item_str(sys, item)); -+ break; -+ } -+} -+ -+/* -+ * Add system to table -+ */ -+static void l_sys_add(struct sd_sys *sys) -+{ -+ struct table_row *table_row; -+ struct sd_sys_item *item; -+ unsigned int i; -+ -+ table_row = table_row_alloc(l_t); -+ table_row_entry_str_add(table_row, &l_col_sys, sd_sys_id(sys)); -+ -+ sd_sys_item_iterate(item, i) -+ l_sys_item_add(table_row, sys, item); -+ table_row_add(l_t, table_row); -+} -+ -+/* -+ * Fill system data into table -+ */ -+static void l_table_create(void) -+{ -+ struct sd_sys *parent, *guest; -+ -+ table_row_del_all(l_t); -+ parent = sd_sys_root_get(); -+ sd_sys_iterate(parent, guest) { -+ if (!opts_sys_specified(&win_sys_list, sd_sys_id(guest))) -+ continue; -+ l_sys_add(guest); -+ } -+ table_finish(l_t); -+} -+ -+/* -+ * Print table to screen -+ */ -+static void l_table_update_term(struct hyptop_win *win) -+{ -+ (void) win; -+ -+ ht_print_head(NULL); -+ table_print(l_t); -+} -+ -+/* -+ * Process input and switch window if necessary -+ */ -+static enum hyptop_win_action l_process_input(struct hyptop_win *win, int c) -+{ -+ char selected_sys[TABLE_STR_MAX]; -+ (void) win; -+ -+ switch (c) { -+ case 'f': -+ return win_switch(l_win_fields); -+ case 't': -+ return win_switch(g.win_cpu_types); -+ case '?': -+ return win_switch(l_win_help); -+ case 'q': -+ hyptop_exit(0); -+ case 'l': -+ case KEY_RIGHT: -+ if (!table_mode_select(l_t)) -+ break; -+ table_row_select_key_get(l_t, selected_sys); -+ win_sys_set(selected_sys); -+ return win_switch(&win_sys); -+ case ERR: -+ break; -+ } -+ table_process_input(l_t, c); -+ hyptop_update_term(); -+ return WIN_KEEP; -+} -+ -+/* -+ * Enable field and set unit -+ */ -+static void l_field_set(struct table_col_spec *col_spec) -+{ -+ table_col_enable_toggle(l_t, col_spec->hotkey); -+ if (!col_spec->unit_str) -+ return; -+ if (table_col_unit_set(l_t, col_spec->hotkey, col_spec->unit_str)) -+ ERR_EXIT("Invalid unit \"%s\" for field \"%c\"\n", -+ col_spec->unit_str, col_spec->hotkey); -+} -+ -+/* -+ * Enable field defined in "col_spec" -+ */ -+static void l_field_enable(struct table_col_spec *col_spec) -+{ -+ struct sd_sys_item *item; -+ struct table_col *col; -+ unsigned int i; -+ -+ sd_sys_item_iterate(item, i) { -+ col = sd_sys_item_table_col(item); -+ if (table_col_hotkey(col) != col_spec->hotkey) -+ continue; -+ l_field_set(col_spec); -+ return; -+ } -+ ERR_EXIT("Unknown field \"%c\"\n", col_spec->hotkey); -+} -+ -+/* -+ * Enable fields defined on command line -+ */ -+static void l_fields_enable_cmdline(void) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < win_sys_list.opts.fields.cnt; i++) -+ l_field_enable(win_sys_list.opts.fields.vec[i]); -+} -+ -+/* -+ * Enable fields like defined in data gatherer -+ */ -+static void l_fields_enable_default(void) -+{ -+ struct sd_sys_item *item; -+ struct table_col *col; -+ unsigned int i; -+ -+ sd_sys_item_enable_iterate(item, i) { -+ col = sd_sys_item_table_col(item); -+ table_col_enable_toggle(l_t, table_col_hotkey(col)); -+ } -+} -+ -+/* -+ * Event loop: Make regular updates of table -+ */ -+static void l_run(struct hyptop_win *win) -+{ -+ enum hyptop_win_action action; -+ (void) win; -+ -+ /* Reformat table when entering window */ -+ table_rebuild(l_t); -+ while (1) { -+ l_table_create(); -+ hyptop_update_term(); -+ action = hyptop_process_input_timeout(); -+ if (action == WIN_SWITCH) -+ return; -+ /* No updates in select mode */ -+ if (!table_mode_select(l_t)) -+ sd_update(); -+ } -+} -+ -+/* -+ * Initialize window -+ */ -+void win_sys_list_init(void) -+{ -+ struct table_col **col_vec; -+ struct sd_sys_item *item; -+ struct table_col *col; -+ char **col_desc_vec; -+ unsigned int i; -+ int item_cnt; -+ -+ /* Alloc table and add columns */ -+ l_t = table_new(1, 1, 1, 1); -+ table_col_add(l_t, &l_col_sys); -+ -+ item_cnt = sd_sys_item_cnt() + 1; -+ col_vec = ht_zalloc(sizeof(void *) * item_cnt); -+ col_desc_vec = ht_zalloc(sizeof(void *) * item_cnt); -+ -+ sd_sys_item_iterate(item, i) { -+ col = sd_sys_item_table_col(item); -+ table_col_add(l_t, col); -+ table_col_enable_toggle(l_t, table_col_hotkey(col)); -+ col_vec[i] = col; -+ col_desc_vec[i] = item->desc; -+ } -+ /* Enable fields */ -+ if (win_sys_list.opts.fields.specified) -+ l_fields_enable_cmdline(); -+ else -+ l_fields_enable_default(); -+ -+ /* Select sort field */ -+ if (win_sys_list.opts.sort_field_specified) { -+ for (i = 0; i < win_sys_list.opts.sort_field_specified; i++) { -+ if (table_col_select(l_t, win_sys_list.opts.sort_field)) -+ ERR_EXIT("Sort field \"%c\" is not available\n", -+ win_sys_list.opts.sort_field); -+ } -+ } else { -+ table_col_select(l_t, sd_sys_item_cpu_diff.table_col.hotkey); -+ } -+ /* Initialize help and fields window */ -+ l_win_help = win_help_new(&win_sys_list); -+ l_win_fields = win_fields_new(l_t, col_vec, col_desc_vec); -+} -+ -+/* -+ * hyptop window structure definition -+ */ -+struct hyptop_win win_sys_list = { -+ .process_input = l_process_input, -+ .update_term = l_table_update_term, -+ .run = l_run, -+ .id = "sys_list", -+ .desc = l_help_str, -+ .desc_normal_vec = l_nav_desc_normal_vec, -+ .desc_select_vec = l_nav_desc_select_vec, -+ .desc_general_vec = l_nav_desc_general_vec, -+}; --- -1.7.3.5 - diff --git a/0050-cmsfs-fuse-support-for-CMS-EDF-filesystems-via-fuse.patch b/0050-cmsfs-fuse-support-for-CMS-EDF-filesystems-via-fuse.patch deleted file mode 100644 index 8c50277..0000000 --- a/0050-cmsfs-fuse-support-for-CMS-EDF-filesystems-via-fuse.patch +++ /dev/null @@ -1,6056 +0,0 @@ -From d7e1d7b005747e4ff08db77ab0eaade80e63636a Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 28 Jan 2011 13:19:48 +0100 -Subject: [PATCH 50/61] cmsfs-fuse: support for CMS EDF filesystems via fuse - -Summary: cmsfs-fuse: support for CMS EDF filesystems via fuse -Description: Use the cmsfs-fuse command to read and write files stored on a z/VM - CMS disk. The cmsfs-fuse file system translates the record-based EDF file - system on the CMS disk to UNIX semantics. It is possible to mount a CMS - disk and use common Linux tools to access the files on the disk. ---- - Makefile | 2 +- - README | 12 + - cmsfs-fuse/Makefile | 31 + - cmsfs-fuse/amap.c | 217 ++ - cmsfs-fuse/cmsfs-fuse.1 | 206 ++ - cmsfs-fuse/cmsfs-fuse.c | 4536 +++++++++++++++++++++++++++++++++++++++++ - cmsfs-fuse/cmsfs-fuse.h | 134 ++ - cmsfs-fuse/config.c | 122 ++ - cmsfs-fuse/dasd.c | 224 ++ - cmsfs-fuse/ebcdic.h | 153 ++ - cmsfs-fuse/edf.h | 123 ++ - cmsfs-fuse/etc/filetypes.conf | 107 + - cmsfs-fuse/helper.h | 54 + - 13 files changed, 5920 insertions(+), 1 deletions(-) - create mode 100644 cmsfs-fuse/Makefile - create mode 100644 cmsfs-fuse/amap.c - create mode 100644 cmsfs-fuse/cmsfs-fuse.1 - create mode 100644 cmsfs-fuse/cmsfs-fuse.c - create mode 100644 cmsfs-fuse/cmsfs-fuse.h - create mode 100644 cmsfs-fuse/config.c - create mode 100644 cmsfs-fuse/dasd.c - create mode 100644 cmsfs-fuse/ebcdic.h - create mode 100644 cmsfs-fuse/edf.h - create mode 100644 cmsfs-fuse/etc/filetypes.conf - create mode 100644 cmsfs-fuse/helper.h - -diff --git a/Makefile b/Makefile -index 89c5fc5..e1f6f83 100644 ---- a/Makefile -+++ b/Makefile -@@ -7,7 +7,7 @@ LIB_DIRS = libvtoc libu2s - SUB_DIRS = $(LIB_DIRS) zipl zdump fdasd dasdfmt dasdview tunedasd \ - tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \ - vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \ -- ziomon iucvterm hyptop -+ ziomon iucvterm hyptop cmsfs-fuse - - all: subdirs_make - -diff --git a/README b/README -index ffd5e54..4335b43 100644 ---- a/README -+++ b/README -@@ -157,6 +157,11 @@ s390-tools (1.8.2) - - ts-shell: Terminal server shell to authorize and control IUCV terminal - connections for individual Linux users. - -+ * cmsfs-fuse: -+ Use the cmsfs-fuse command to read and write files stored on a z/VM -+ CMS disk. The cmsfs-fuse file system translates the record-based EDF file -+ system on the CMS disk to UNIX semantics. It is possible to mount a CMS -+ disk and use common Linux tools to access the files on the disk. - - For more information refer to the following publications: - * "Device Drivers, Features, and Commands" chapter "Useful Linux commands" -@@ -179,6 +184,13 @@ Dependencies: - For executing the ziomon tools an installed blktrace package is required. - See: git://git.kernel.dk/blktrace.git - -+ * cmsfs-fuse: -+ cmsfs-fuse depends on FUSE. FUSE is provided by installing the fuse and -+ libfuse packages and by a kernel compiled with CONFIG_FUSE_FS. -+ For compiling the s390-tools package the fuse-devel package is required. -+ For further information about FUSE see: http://fuse.sourceforge.net/ -+ cmsfs-fuse requires FUSE version 2.8.1 or newer for full functionality. -+ - Release History: - ================ - 1.8.2 -diff --git a/cmsfs-fuse/Makefile b/cmsfs-fuse/Makefile -new file mode 100644 -index 0000000..7df81e0 ---- /dev/null -+++ b/cmsfs-fuse/Makefile -@@ -0,0 +1,31 @@ -+#!/usr/bin/make -f -+ -+include ../common.mak -+ -+CPPFLAGS += -I../include -+ -+all: cmsfs-fuse -+ -+CFLAGS += -D_FILE_OFFSET_BITS=64 -DHAVE_SETXATTR -I/usr/include/fuse -+LDLIBS += -lfuse -lpthread -lrt -ldl -lm -+ -+OBJECTS = cmsfs-fuse.o dasd.o amap.o config.o -+$(OBJECTS): *.h Makefile -+ -+CMSFS_FUSE_DIR = $(SYSCONFDIR)/cmsfs-fuse -+CONFIG_FILES = filetypes.conf -+ -+cmsfs-fuse: $(OBJECTS) -+ -+install: all -+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 cmsfs-fuse $(USRBINDIR) -+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 cmsfs-fuse.1 $(MANDIR)/man1 -+ $(INSTALL) -g $(GROUP) -o $(OWNER) -d $(CMSFS_FUSE_DIR) -+ for cnf in $(CONFIG_FILES); do \ -+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 etc/$$cnf $(CMSFS_FUSE_DIR) ; \ -+ done -+ -+clean: -+ rm -f cmsfs-fuse *.o -+ -+.PHONY: all install clean -diff --git a/cmsfs-fuse/amap.c b/cmsfs-fuse/amap.c -new file mode 100644 -index 0000000..04f83fc ---- /dev/null -+++ b/cmsfs-fuse/amap.c -@@ -0,0 +1,217 @@ -+/* -+ * cmsfs-fuse - CMS EDF filesystem support for Linux -+ * Allocation map functions. -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Jan Glauber -+ */ -+ -+#define _GNU_SOURCE -+#include -+#include -+#include -+#include -+#include -+#include -+#include "zt_common.h" -+#include "helper.h" -+#include "edf.h" -+#include "cmsfs-fuse.h" -+ -+/* -+ * Get block number from address. -+ */ -+static int amap_blocknumber(off_t addr) -+{ -+ return addr / BYTES_PER_BLOCK; -+} -+ -+/* -+ * Get the block number for a specific level. -+ */ -+static int amap_blocknumber_level(int level, off_t addr) -+{ -+ int entry = amap_blocknumber(addr); -+ -+ while (level-- > 1) -+ entry /= PTRS_PER_BLOCK; -+ return entry; -+} -+ -+/* -+ * Return address of to the allocation map for a block number > 0. -+ */ -+static off_t get_amap_addr(int level, off_t addr, off_t ptr) -+{ -+ int block = amap_blocknumber_level(level, addr); -+ -+ if (cmsfs.amap_levels == 0) -+ return cmsfs.amap; -+ -+ if (level--) { -+ ptr = get_fixed_pointer(ptr + block * PTR_SIZE); -+ if (!ptr) -+ DIE("amap invalid ptr at addr: %lx\n", -+ ptr + block * PTR_SIZE); -+ return get_amap_addr(level, addr, ptr); -+ } -+ return ptr; -+} -+ -+/* -+ * Mark disk address as allocated in alloc map. Unaligned addr is tolerated. -+ */ -+static void amap_block_set(off_t addr) -+{ -+ off_t amap = get_amap_addr(cmsfs.amap_levels, addr, cmsfs.amap); -+ int rc, block = amap_blocknumber(addr); -+ unsigned int byte, bit; -+ u8 entry; -+ -+ if (block > 0) -+ addr -= block * BYTES_PER_BLOCK; -+ -+ addr >>= BITS_PER_DATA_BLOCK; -+ byte = addr / 8; -+ bit = addr % 8; -+ -+ rc = _read(&entry, sizeof(entry), amap + byte); -+ BUG(rc < 0); -+ -+ /* already used */ -+ BUG(entry & (1 << (7 - bit))); -+ -+ entry |= (1 << (7 - bit)); -+ rc = _write(&entry, sizeof(entry), amap + byte); -+ BUG(rc < 0); -+} -+ -+/* -+ * Mark disk address as free in alloc map. Unaligned addr is tolerated. -+ */ -+static void amap_block_clear(off_t addr) -+{ -+ off_t amap = get_amap_addr(cmsfs.amap_levels, addr, cmsfs.amap); -+ int rc, block = amap_blocknumber(addr); -+ unsigned int byte, bit; -+ u8 entry; -+ -+ if (block > 0) -+ addr -= block * BYTES_PER_BLOCK; -+ -+ addr >>= BITS_PER_DATA_BLOCK; -+ byte = addr / 8; -+ bit = addr % 8; -+ -+ rc = _read(&entry, sizeof(entry), amap + byte); -+ BUG(rc < 0); -+ -+ /* already cleared */ -+ BUG(!(entry & (1 << (7 - bit)))); -+ -+ entry &= ~(1 << (7 - bit)); -+ rc = _write(&entry, sizeof(entry), amap + byte); -+ BUG(rc < 0); -+} -+ -+/* -+ * Return the first free bit in one byte. -+ */ -+static int find_first_empty_bit(u8 entry) -+{ -+ u8 i; -+ -+ for (i = 0; i < 8; i++) -+ if (!(entry & 1 << (7 - i))) -+ return i; -+ /* unreachable */ -+ return -1; -+} -+ -+/* -+ * Look for the first unallocated block and return addr of allocated block. -+ */ -+static off_t __get_free_block(int level, off_t amap, int block_nr) -+{ -+ off_t ptr, addr = amap; -+ unsigned int bit; -+ int left, rc, i; -+ u8 entry; -+ -+ if (level > 0) { -+ level--; -+ left = PTRS_PER_BLOCK; -+ while (left--) { -+ ptr = get_fixed_pointer(addr); -+ if (!ptr) -+ return 0; -+ ptr = __get_free_block(level, ptr, block_nr); -+ if (ptr) -+ return ptr; -+ addr += PTR_SIZE; -+ block_nr++; -+ } -+ return 0; -+ } -+ -+ for (i = 0; i < cmsfs.blksize; i++) { -+ rc = _read(&entry, sizeof(entry), amap + i); -+ BUG(rc < 0); -+ -+ if (entry != 0xff) { -+ /* get first empty bit and add to addr */ -+ bit = find_first_empty_bit(entry); -+ -+ /* bit -> addr */ -+ addr = (i * cmsfs.blksize * 8 + cmsfs.blksize * bit) -+ + (block_nr * BYTES_PER_BLOCK); -+ amap_block_set(addr); -+ return addr; -+ } -+ } -+ return 0; -+} -+ -+/* -+ * Allocate a free block and increment label block counter. -+ */ -+off_t get_free_block(void) -+{ -+ off_t addr; -+ -+ if (cmsfs.used_blocks + cmsfs.reserved_blocks >= cmsfs.total_blocks) -+ return -ENOSPC; -+ addr = __get_free_block(cmsfs.amap_levels, cmsfs.amap, 0); -+ BUG(!addr); -+ -+ cmsfs.used_blocks++; -+ return addr; -+} -+ -+/* -+ * Allocate a zero-filled block and increment label block counter. -+ */ -+off_t get_zero_block(void) -+{ -+ off_t addr = get_free_block(); -+ int rc; -+ -+ if (addr < 0) -+ return -ENOSPC; -+ -+ rc = _zero(addr, cmsfs.blksize); -+ if (rc < 0) -+ return rc; -+ return addr; -+} -+ -+/* -+ * Free a block and decrement label block counter. -+ */ -+void free_block(off_t addr) -+{ -+ if (addr) { -+ amap_block_clear(addr); -+ cmsfs.used_blocks--; -+ } -+} -diff --git a/cmsfs-fuse/cmsfs-fuse.1 b/cmsfs-fuse/cmsfs-fuse.1 -new file mode 100644 -index 0000000..2dc825d ---- /dev/null -+++ b/cmsfs-fuse/cmsfs-fuse.1 -@@ -0,0 +1,206 @@ -+.\" Copyright 2010 Jan Glauber (jan.glauber@de.ibm.com) -+.\" -+.TH CMSFS-FUSE 1 "February 2010" "s390-tools" -+ -+.SH NAME -+cmsfs-fuse \- File system for z/VM CMS disks -+ -+.SH SYNOPSIS -+.SS mounting: -+.TP -+\fBcmsfs-fuse\fP DEVICE MOUNTPOINT [OPTIONS] -+.SS unmounting: -+.TP -+\fBfusermount\fP -u MOUNTPOINT -+ -+.SH DESCRIPTION -+Use the \fBcmsfs-fuse\fP command to provide read and write access -+to files stored on a z/VM CMS disk. -+The cmsfs-fuse file system translates the record-based EDF file system on -+the CMS disk to UNIX semantics. -+After mounting the CMS disk, you can use common Linux tools to access -+the files on the disk. You can enable automatic conversions of text files from -+EBCDIC to ASCII. -+ -+Attention: You can inadvertently damage files and lose data when directly -+writing to files within the cmsfs-fuse file system. To avoid problems when writing, -+multiple restrictions must be observed, especially with regard to linefeeds (see -+section RESTRICTIONS). -+ -+If you are unsure about how to safely write to a file on the cmsfs-fuse file -+system, copy the file to a location outside the cmsfs-fuse file system, edit the file, -+and then copy it back to its original location. -+ -+.SH OPTIONS -+.SS "general options:" -+.TP -+\fB\-o\fR opt,[opt...] -+Fuse or mount command options. For fuse options see below, for mount options -+see \fBmount(8)\fP. -+.TP -+\fB\-h\fR or \fB\-\-help\fR -+Print usage information, then exit. -+.TP -+\fB\-v\fR or \fB\-\-version\fR -+Print version information, then exit. -+.SS "cmsfs-fuse options:" -+.TP -+\fB\-a\fR or \fB\-\-ascii\fR -+Interpret all files on the CMS disk as text files and convert them from -+EBCDIC to ASCII. -+.TP -+\fB--from\fR -+The codepage of the files on the CMS disk. If this option is not -+specified the default codepage CP1047 is used. For a list of all available -+codepages see iconv --list. -+.TP -+\fB--to\fR -+The codepage to which CMS files should be converted to. If this option is not -+specified the default codepage ISO-8859-1 is used. For a list of all available -+codepages see iconv --list. -+.TP -+\fB\-t\fR or \fB\-\-filetype\fR -+Interpret files on the CMS disk as text files based on the file type -+and convert them from EBCDIC to ASCII. The file types that are treated -+as text files are taken from a configuration file (see section CONFIGURATION FILES). -+ -+.SS "Applicable FUSE options (version 2.8):" -+.TP -+\fB\-d\fR or \fB\-o\fR debug -+Enable debug output (implies \fB\-f\fR) -+.TP -+\fB\-f\fR -+Foreground operation -+.TP -+\fB\-o\fR allow_other -+Allow access by other users -+.TP -+\fB\-o\fR allow_root -+Allow access by root -+.TP -+\fB\-o\fR nonempty -+Allow mounts over non\-empty file/dir -+.TP -+\fB\-o\fR default_permissions -+Enable permission checking by kernel -+.TP -+.TP -+\fB\-o\fR max_read=N -+Set maximum size of read requests -+.TP -+\fB\-o\fR kernel_cache -+Cache files in kernel -+.TP -+\fB\-o\fR [no]auto_cache -+Enable caching based on modification times -+.TP -+\fB\-o\fR umask=M -+Set file permissions (octal) -+.TP -+\fB\-o\fR uid=N -+Set file owner -+.TP -+\fB\-o\fR gid=N -+Set file group -+.TP -+\fB\-o\fR max_write=N -+Set maximum size of write requests -+.TP -+\fB\-o\fR max_readahead=N -+Set maximum readahead -+.TP -+\fB\-o\fR async_read -+Perform reads asynchronously (default) -+.TP -+\fB\-o\fR sync_read -+Perform reads synchronously -+.TP -+\fB\-o big_writes\fR -+Enable write operations with more than 4 KB -+ -+.SH EXTENDED ATTRIBUTES -+Use the following extended attributes to handle the CMS characteristics of a file: -+ -+\fBuser.record_format\fR: The format of a file. Allowed values are F for fixed record length files -+and V for variable record length files. This attribute can be set only if the file is empty. -+ -+\fBuser.record_lrecl\fR: The record length of a file. This attribute can be set only for a fixed -+record length file and if the file is empty. A valid record length is an integer in the range 1-65535. -+ -+\fBuser.file_mode\fR: The file mode of a file which is interpreted by CMS. The file mode consists -+of a mode letter from A-Z and mode number from 0-6. -+ -+New files are created by default as variable files with file mode A1. -+ -+.SH RESTRICTIONS -+\fBrename\fR and \fBcreat\fR: -+Uppercase file names are enforced. -+ -+\fBtruncate\fR: -+Only shrinking of a file is supported. For fixed length record files, the new file size must -+be a multiple of the record length. -+ -+\fBunlink\fR: -+Creating a file with the name of a previously unlinked file which is still in use is not supported -+and will fail with -ENOENT. -+ -+\fBwrite\fR: -+Writes are supported only at the end of the file. -+A write on a fixed length record file always writes a multiple -+of the record length. If additional bytes are added, the -+bytes are filled with zero in binary mode or with spaces in ASCII mode. Sparse files are not supported. -+If the cp tool is used to write files to a CMS disk the option "--sparse=never" must be specified. -+ -+If ASCII translation is enabled for a file a linefeed character determines the end of a record. -+The following restrictions must be observed for writing files in ASCII mode: -+For fixed record length files a linefeed must occur exactly after a record of the length specified in the fixed record length. -+For variable record length files a linefeed must occur after the maximum record length is reached or earlier. -+If a record of a variable record length file consists only of a linefeed character cmsfs-fuse adds a space to this record since -+empty records are not supported by the CMS file system. -+ -+.SH CONFIGURATION FILES -+cmsfs-fuse uses a configuration file for automatic translation based on the file type. -+Upon startup, cmsfs-fuse evaluates the file .cmsfs-fuse/filetypes.conf in the user's home directory. If the file does not -+exist cmsfs-fuse evaluates the file /etc/cmsfs-fuse/filetypes.conf. -+ -+The filetypes.conf file contains the CMS file types that are automaticaly translated to ASCII if cmsfs-fuse is started -+with the -t option. The syntax of the configuration file is one file type per line. Lines that start with a # followed by a space are treated as -+comments and are ignored. The file type is 8 characters long and must consist of valid CMS file name characters only. -+ -+The default file types in the configuration file were taken from the z/VM TCPIP.DATA file -+(z/VM version 5.4.0). -+ -+.SH EXAMPLES -+To mount the CMS disk with the name dasde enter: -+.br -+ -+ # cmsfs-fuse /dev/dasde /mnt -+ -+.br -+To mount the CMS disk with the name dasde and enable automatic translation -+of known text files enter: -+.br -+ -+ # cmsfs-fuse -t /dev/dasde /mnt -+ -+To mount the CMS disk with the name dasde and enable automatic translation -+of all files to UTF-8 enter: -+.br -+ -+ # cmsfs-fuse --to=UTF-8 -a /dev/dasde /mnt -+ -+To unmount the CMS disk mounted on /mnt enter: -+.br -+ -+ # fusermount -u /mnt -+ -+To show the record format of file PROFILE.EXEC assuming the CMS disk was mounted on /mnt: -+ -+ # getfattr -n user.record_format /mnt/PROFILE.EXEC -+ -+The following example assumes that an empty, fixed record format file, PROFILE.EXEC, can be accessed on a CMS disk that has been mounted on /mnt. To set the record length of PROFILE.EXEC to 80 bytes: -+ -+ # setfattr -n user.record_lrecl -v 80 /mnt/PROFILE.EXEC -+ -+.SH SEE ALSO -+attr (5), getfattr (1), setfattr(1), iconv(1) and Linux on System z: Device Drivers, Features and Commands -diff --git a/cmsfs-fuse/cmsfs-fuse.c b/cmsfs-fuse/cmsfs-fuse.c -new file mode 100644 -index 0000000..6c5b0b5 ---- /dev/null -+++ b/cmsfs-fuse/cmsfs-fuse.c -@@ -0,0 +1,4536 @@ -+/* -+ * cmsfs-fuse - CMS EDF filesystem support for Linux -+ * Main functions. -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Jan Glauber -+ */ -+ -+#define FUSE_USE_VERSION 26 -+#define _GNU_SOURCE -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#ifdef HAVE_SETXATTR -+#include -+#endif -+#include -+#include -+ -+#include "zt_common.h" -+#include "list.h" -+#include "helper.h" -+#include "edf.h" -+#include "cmsfs-fuse.h" -+#include "ebcdic.h" -+ -+struct cmsfs cmsfs; -+struct list open_file_list; -+struct list text_type_list; -+FILE *logfile; -+ -+#define PAGE_SIZE 0xfff -+#define FSNAME_MAX_LEN 50 -+#define MAX_FNAME 18 -+ -+#define CMSFS_OPT(t, p, v) { t, offsetof(struct cmsfs, p), v } -+ -+enum { -+ KEY_HELP, -+ KEY_VERSION, -+}; -+ -+static const struct fuse_opt cmsfs_opts[] = { -+ CMSFS_OPT("-a", mode, TEXT_MODE), -+ CMSFS_OPT("--ascii", mode, TEXT_MODE), -+ CMSFS_OPT("-t", mode, TYPE_MODE), -+ CMSFS_OPT("--filetype", mode, TYPE_MODE), -+ CMSFS_OPT("--from=%s", codepage_from, 0), -+ CMSFS_OPT("--to=%s", codepage_to, 0), -+ -+ FUSE_OPT_KEY("-h", KEY_HELP), -+ FUSE_OPT_KEY("--help", KEY_HELP), -+ FUSE_OPT_KEY("-v", KEY_VERSION), -+ FUSE_OPT_KEY("--version", KEY_VERSION), -+ FUSE_OPT_END -+}; -+ -+static void usage(const char *progname) -+{ -+ fprintf(stderr, -+"Usage: %s DEVICE MOUNTPOINT [OPTIONS]\n" -+"\n" -+"Use the cmsfs-fuse command to read and write files stored on a z/VM CMS disk.\n" -+"\n" -+"General options:\n" -+" -o opt,[opt...] Mount options\n" -+" -h --help Print help, then exit\n" -+" -v --version Print version, then exit\n" -+" -t --filetype ASCII translation based on file type\n" -+" -a --ascii Force ascii translation\n" -+" --from= Codepage used on the CMS disk\n" -+" --to= Codepage used for conversion to Linux\n" -+"\n", progname); -+} -+ -+static char CODEPAGE_EDF[] = "CP1047"; -+static char CODEPAGE_LINUX[] = "ISO-8859-1"; -+ -+#define USED_BLOCK_ADDR (cmsfs.blksize * 2 + 32) -+ -+#define READDIR_FILE_ENTRY -1 -+#define READDIR_END_OF_DIR -2 -+#define READDIR_DIR_ENTRY -3 -+#define READDIR_MAP_ENTRY -4 -+ -+#define LINEFEED_OFFSET ((struct record *) -1) -+#define LINEFEED_ASCII 0xa -+#define LINEFEED_EBCDIC 0x25 -+#define LINEFEED_NOT_FOUND -1 -+#define FILLER_EBCDIC 0x40 -+#define FILLER_ASCII 0x20 -+ -+#define RSS_HEADER_STARTED 0x1 -+#define RSS_HEADER_COMPLETE 0x2 -+#define RSS_DATA_BLOCK_STARTED 0x4 -+#define RSS_DATA_BLOCK_EXT 0x8 -+ -+#define RWS_HEADER_STARTED 0x1 -+#define RWS_HEADER_COMPLETE 0x2 -+#define RWS_RECORD_INCOMPLETE 0x4 -+#define RWS_RECORD_COMPLETE 0x8 -+ -+#define BWS_BLOCK_NEW 0x1 -+#define BWS_BLOCK_USED 0x2 -+ -+#define WCACHE_MAX (MAX_RECORD_LEN + 1) -+ -+struct block { -+ off_t disk_addr; -+ unsigned int disp; -+ int hi_record_nr; -+}; -+ -+struct record_ext { -+ /* start addr of the extension */ -+ off_t disk_start; -+ /* length of extension in this disk block */ -+ int len; -+ /* null block start flag */ -+ int null_block_started; -+ /* corresponding disk block number */ -+ int block_nr; -+ -+ struct record_ext *prev; -+ struct record_ext *next; -+}; -+ -+struct record { -+ /* length of the complete record */ -+ unsigned int total_len; -+ /* offset of first record block on the disk */ -+ off_t disk_start; -+ /* bytes in first record block */ -+ int first_block_len; -+ /* logical offset, dependent on line feed mode */ -+ off_t file_start; -+ /* null block start flag */ -+ int null_block_started; -+ /* spanned record extension */ -+ struct record_ext *ext; -+ /* corresponding disk block number */ -+ int block_nr; -+}; -+ -+struct file; -+ -+struct file_operations { -+ int (*cache_data) (struct file *f, off_t addr, int level, int *block, -+ unsigned int *disp, int *record, size_t *total); -+ int (*write_data) (struct file *f, const char *buf, int len, size_t size, -+ int rlen); -+ int (*delete_pointers) (struct file *f, int level, off_t addr); -+ int (*write_pointers) (struct file *f, int level, off_t dst, int offset); -+}; -+ -+static struct file_operations fops_fixed; -+static struct file_operations fops_variable; -+ -+/* -+ * File object for operations that follow open -+ */ -+struct file { -+ /* pointer to the fst entry */ -+ struct fst_entry *fst; -+ /* fst address on disk */ -+ off_t fst_addr; -+ /* translate mode enabled */ -+ int translate; -+ /* linefeed mode enabled */ -+ int linefeed; -+ /* list of records */ -+ struct record *rlist; -+ /* record scan state machine flag */ -+ int record_scan_state; -+ /* next record for sequential reads */ -+ int next_record_hint; -+ /* counter for null bytes to detect block start */ -+ int null_ctr; -+ /* list of disk blocks */ -+ struct block *blist; -+ /* disk address of next byte to write */ -+ off_t write_ptr; -+ /* the filesize while the file is opened */ -+ ssize_t session_size; -+ /* number of null blocks for fixed files */ -+ int nr_null_blocks; -+ /* number of written padding bytes for a fixed file */ -+ int pad_bytes; -+ /* old levels value, needed to rewrite pointers */ -+ int old_levels; -+ /* record write state for variable headers */ -+ struct var_record_state *vrstate; -+ /* path name for open and unlink */ -+ char path[MAX_FNAME + 1]; -+ /* counter for pseudo null length records */ -+ int null_records; -+ /* write cache for text mode */ -+ char *wcache; -+ /* used bytes in write cache */ -+ int wcache_used; -+ /* commited written bytes to FUSE */ -+ int wcache_commited; -+ /* dirty flag for file meta data */ -+ int ptr_dirty; -+ /* fops pointers */ -+ struct file_operations *fops; -+ /* pointers per block constant */ -+ int ptr_per_block; -+ /* open list head */ -+ struct list list; -+ /* usage counter for all openers */ -+ int use_count; -+ /* usage counter for all writers */ -+ int write_count; -+ /* unlink flag */ -+ int unlinked; -+}; -+ -+struct var_record_state { -+ int rlen; -+ int record_state; -+ int block_state; -+}; -+ -+struct xattr { -+ char name[20]; -+ size_t size; -+}; -+ -+/* -+ * Record format: 'F' (fixed) or 'V' (variable), 1 byte -+ * Record lrecl: 0-65535, 5 bytes -+ * Record mode: [A-Z][0-6], 2 bytes -+ */ -+struct xattr xattr_format = { .name = "user.record_format", .size = 1 }; -+struct xattr xattr_lrecl = { .name = "user.record_lrecl", .size = 5 }; -+struct xattr xattr_mode = { .name = "user.file_mode", .size = 2 }; -+ -+#define SHOW_UNLINKED 0 -+#define HIDE_UNLINKED 1 -+ -+#define WALK_FLAG_LOOKUP 0x1 -+#define WALK_FLAG_READDIR 0x2 -+#define WALK_FLAG_LOCATE_EMPTY 0x4 -+#define WALK_FLAG_CACHE_DBLOCKS 0x8 -+ -+struct walk_file { -+ int flag; -+ char *name; -+ char *type; -+ void *buf; -+ off_t addr; -+ fuse_fill_dir_t filler; -+ off_t *dlist; -+ int dlist_used; -+}; -+ -+/* -+ * Prototypes -+ */ -+static struct file *create_file_object(struct fst_entry *fst, int *rc); -+static void destroy_file_object(struct file *f); -+ -+static unsigned long dec_to_hex(unsigned long long num) -+{ -+ unsigned long res; -+ -+ asm volatile("cvb %0,%1" : "=d" (res) : "m" (num)); -+ return res & 0xffffffff; -+} -+ -+static unsigned int hex_to_dec(unsigned int num) -+{ -+ unsigned long long res; -+ -+ asm volatile("cvd %1,%0" : "=m" (res) : "d" (num)); -+ return res & 0xffffffff; -+} -+ -+static void setup_iconv(iconv_t *conv, const char *from, const char *to) -+{ -+ *conv = iconv_open(to, from); -+ if (*conv == ((iconv_t) -1)) -+ DIE("Could not initialize conversion table %s->%s.\n", -+ from, to); -+} -+ -+static inline struct file *get_fobj(struct fuse_file_info *fi) -+{ -+ return (struct file *) fi->fh; -+} -+ -+int _read(void *buf, size_t size, off_t addr) -+{ -+ if (((addr + (off_t) size - 1) & ~DATA_BLOCK_MASK) > -+ (addr & ~DATA_BLOCK_MASK)) -+ DIE("read: crossing blocks addr: %lx size: %ld\n", -+ addr, size); -+ if ((addr < cmsfs.fdir) || ((size_t) addr > cmsfs.size)) -+ return -EIO; -+ -+ memcpy(buf, cmsfs.map + addr, size); -+ return 0; -+} -+ -+int _write(const void *buf, size_t size, off_t addr) -+{ -+ if (((addr + (off_t) size - 1) & ~DATA_BLOCK_MASK) > -+ (addr & ~DATA_BLOCK_MASK)) -+ DIE("write: crossing blocks addr: %x size: %d\n", -+ (int)addr, (int)size); -+ -+ if ((addr < (2 * cmsfs.blksize)) || ((size_t) addr > cmsfs.size)) -+ return -EIO; -+ -+ if (buf == NULL) -+ memset(cmsfs.map + addr, 0, size); -+ else -+ memcpy(cmsfs.map + addr, buf, size); -+ return 0; -+} -+ -+int _zero(off_t addr, size_t size) -+{ -+ return _write(NULL, size, addr); -+} -+ -+off_t get_filled_block(void) -+{ -+ off_t addr = get_free_block(); -+ -+ if (addr < 0) -+ return -ENOSPC; -+ -+ memset(cmsfs.map + addr, FILLER_EBCDIC, cmsfs.blksize); -+ return addr; -+} -+ -+static int get_fop(off_t addr) -+{ -+ struct fst_entry fst; -+ int rc; -+ -+ rc = _read(&fst, sizeof(fst), addr); -+ BUG(rc < 0); -+ return ABS(fst.fop); -+} -+ -+static int get_levels(off_t addr) -+{ -+ struct fst_entry fst; -+ int rc; -+ -+ rc = _read(&fst, sizeof(fst), addr); -+ BUG(rc < 0); -+ return fst.levels; -+} -+ -+static int get_files_count(off_t addr) -+{ -+ struct fst_entry fst; -+ int rc; -+ -+ rc = _read(&fst, sizeof(fst), addr); -+ BUG(rc < 0); -+ /* ignore director and allocmap entries */ -+ return fst.nr_records - 2; -+} -+ -+static int get_order(int shift) -+{ -+ int count = 0; -+ -+ while (!(shift & 0x1)) { -+ shift >>= 1; -+ count++; -+ } -+ return count; -+} -+ -+/* -+ * Read pointer from fixed size pointer block and return -+ * absolute address on disk. -+ */ -+off_t get_fixed_pointer(off_t addr) -+{ -+ struct fixed_ptr ptr; -+ int rc; -+ -+ if (!addr) -+ return NULL_BLOCK; -+ rc = _read(&ptr, sizeof(ptr), addr); -+ if (rc < 0) -+ return -EIO; -+ if (!ptr.next) -+ return NULL_BLOCK; -+ else -+ return ABS((off_t)ptr.next); -+} -+ -+/* -+ * Read variable pointer from block and return absolute address on disk -+ * and highest record number. -+ */ -+static off_t get_var_pointer(off_t addr, int *max_record, -+ unsigned int *disp) -+{ -+ struct var_ptr vptr; -+ off_t ptr = 0; -+ int rc; -+ -+ BUG(!addr); -+ -+ rc = _read(&vptr, VPTR_SIZE, addr); -+ if (rc < 0) -+ return -EIO; -+ ptr = (off_t) vptr.next; -+ -+ *max_record = vptr.hi_record_nr; -+ *disp = vptr.disp; -+ -+ if (!ptr) { -+ if (vptr.hi_record_nr) -+ return NULL_BLOCK; -+ else -+ return VAR_FILE_END; -+ } else -+ return ABS(ptr); -+} -+ -+int is_edf_char(int c) -+{ -+ switch (c) { -+ case 'A' ... 'Z': -+ break; -+ case 'a' ... 'z': -+ break; -+ case '0' ... '9': -+ break; -+ case '#': -+ break; -+ case '@': -+ break; -+ case '+': -+ break; -+ case '$': -+ break; -+ case '-': -+ break; -+ case ':': -+ break; -+ case '_': -+ break; -+ default: -+ return 0; -+ } -+ return 1; -+} -+ -+/* -+ * Force conversion to upper case since lower case file names although -+ * valid are not accepted by many CMS tools. -+ */ -+static void str_toupper(char *str) -+{ -+ int i; -+ -+ for (i = 0; i < (int) strlen(str); i++) -+ str[i] = toupper(str[i]); -+} -+ -+/* -+ * Set the FST date to the specified date. -+ */ -+static void update_fst_date(struct fst_entry *fst, struct tm *tm) -+{ -+ unsigned int num; -+ int i; -+ -+ if (tm->tm_year >= 100) -+ fst->flag |= FST_FLAG_CENTURY; -+ else -+ fst->flag &= ~FST_FLAG_CENTURY; -+ fst->date[0] = tm->tm_year; -+ fst->date[1] = tm->tm_mon + 1; -+ fst->date[2] = tm->tm_mday; -+ fst->date[3] = tm->tm_hour + 1; -+ fst->date[4] = tm->tm_min; -+ fst->date[5] = tm->tm_sec; -+ -+ /* convert hex to decimal */ -+ for (i = 0; i < 6; i++) { -+ num = fst->date[i]; -+ num = hex_to_dec(num); -+ fst->date[i] = num >> 4; -+ } -+} -+ -+/* -+ * Set the FST date to the current date. -+ */ -+static int set_fst_date_current(struct fst_entry *fst) -+{ -+ struct timeval tv; -+ struct tm tm; -+ -+ /* convert timespec to tm */ -+ memset(&tm, 0, sizeof(struct tm)); -+ -+ if (gettimeofday(&tv, NULL) < 0) { -+ perror(COMP "gettimeofday failed"); -+ return -EINVAL; -+ } -+ -+ if (localtime_r(&tv.tv_sec, &tm) == NULL) -+ return -EINVAL; -+ -+ update_fst_date(fst, &tm); -+ return 0; -+} -+ -+/* -+ * Check if the file is on the opened list. -+ */ -+static struct file *file_open(const char *name) -+{ -+ char uc_name[MAX_FNAME]; -+ struct file *f; -+ -+ strncpy(uc_name, name, MAX_FNAME); -+ str_toupper(uc_name); -+ -+ list_iterate(f, &open_file_list, list) -+ if (strncmp(f->path + 1, uc_name, MAX_FNAME) == 0) -+ return f; -+ return NULL; -+} -+ -+/* -+ * Check if the file is open and unlinked. -+ */ -+static int file_unlinked(const char *name) -+{ -+ struct file *f = file_open(name); -+ -+ if (f && f->unlinked) -+ return 1; -+ else -+ return 0; -+} -+ -+/* -+ * Convert EDF date to time_t. -+ */ -+static time_t fst_date_to_time_t(char *date, int century) -+{ -+ unsigned long long num; -+ unsigned int res[6]; -+ struct tm tm; -+ time_t time; -+ int i; -+ -+ /* -+ * date : YY MM DD HH MM SS (decimal!) -+ * century: 0=19, 1=20, dead=21 -+ * convert decimal to hex -+ */ -+ for (i = 0; i < 6; i++) { -+ num = date[i]; -+ num <<= 4; -+ num += 0xc; /* plus */ -+ res[i] = dec_to_hex(num); -+ } -+ -+ memset(&tm, 0, sizeof(tm)); -+ tm.tm_year = res[0]; -+ tm.tm_mon = res[1]; -+ tm.tm_mday = res[2]; -+ tm.tm_hour = res[3]; -+ tm.tm_min = res[4]; -+ tm.tm_sec = res[5]; -+ /* see man 3 tzset */ -+ tm.tm_isdst = daylight; -+ -+ /* prepare for mktime */ -+ tm.tm_hour--; -+ tm.tm_mon--; -+ if (century == FST_FLAG_CENTURY) -+ tm.tm_year += 100; -+ -+ time = mktime(&tm); -+ if (time == -1) { -+ fprintf(stderr, COMP "mktime failed!\n"); -+ memset(&time, 0, sizeof(time)); -+ } -+ return time; -+} -+ -+/* -+ * Read one FST entry into *fst from offset on disk addr and detect type. -+ * -+ * Return values: -+ * ret > 0 : disk address of additional FOP block -+ * ret = -1 : file entry filled -+ * ret = -2 : end of directory -+ * ret = -3 : directory entry -+ * ret = -4 : allocmap entry -+ */ -+static int readdir_entry(struct fst_entry *fst, off_t addr) -+{ -+ int rc; -+ -+ BUG(addr & (sizeof(struct fst_entry) - 1)); -+ -+ rc = _read(fst, sizeof(*fst), addr); -+ BUG(rc < 0); -+ -+ if (is_directory(fst->name, fst->type)) { -+ /* check for multi-block directory */ -+ if (ABS(fst->fop) != addr) -+ return ABS(fst->fop); -+ return READDIR_DIR_ENTRY; -+ } -+ -+ if (is_allocmap(fst->name, fst->type)) -+ return READDIR_MAP_ENTRY; -+ -+ if (is_file((unsigned long long *) fst->name, -+ (unsigned long long *) fst->type)) -+ return READDIR_FILE_ENTRY; -+ -+ return READDIR_END_OF_DIR; -+} -+ -+/* -+ * Return number of characters excluding trailing spaces. -+ */ -+static inline int strip_right(const char *str, int size) -+{ -+ while (str[size - 1] == 0x20) -+ size--; -+ return size; -+} -+ -+/* -+ * Convert ASCII name to EBCDIC name. -+ */ -+static int encode_edf_name(const char *name, char *fname, char *ftype) -+{ -+ int dot_pos, tlen; -+ char *tmp; -+ -+ /* -+ * name is ascii string "FILE.EXT" -+ * readdir_entry returns fst.name fst.type as EBCDIC including spaces -+ * pre-fill name and type with ascii spaces, remove dot and convert -+ * to EBCDIC. -+ */ -+ memset(fname, 0x20, 8); -+ memset(ftype, 0x20, 8); -+ -+ tmp = index(name, '.'); -+ /* filenames without a dot are invalid! */ -+ if (tmp == NULL) -+ return -EINVAL; -+ -+ dot_pos = tmp - name; -+ if (dot_pos == 0 || dot_pos > 8) -+ return -EINVAL; -+ memcpy(fname, name, dot_pos); -+ ebcdic_enc(fname, fname, 8); -+ -+ tlen = strlen(name) - (dot_pos + 1); -+ if (tlen == 0 || tlen > 8) -+ return -EINVAL; -+ -+ memcpy(ftype, name + dot_pos + 1, tlen); -+ ebcdic_enc(ftype, ftype, 8); -+ return 0; -+} -+ -+/* -+ * Convert EBCDIC name to ASCII name. -+ */ -+static void decode_edf_name(char *file, char *fname, char *ftype) -+{ -+ int len, pos = 0; -+ -+ ebcdic_dec(fname, fname, 8); -+ ebcdic_dec(ftype, ftype, 8); -+ -+ /* strip spaces but only from the end */ -+ len = strip_right(fname, 8); -+ memcpy(file, fname, len); -+ -+ /* add dot */ -+ pos += len; -+ file[pos] = '.'; -+ pos++; -+ -+ len = strip_right(ftype, 8); -+ memcpy(&file[pos], ftype, len); -+ pos += len; -+ -+ /* terminate string */ -+ file[pos] ='\0'; -+} -+ -+static int edf_name_valid(const char *name) -+{ -+ int name_len, i; -+ char *dot; -+ -+ /* name must contain . */ -+ dot = index(name, '.'); -+ if (dot == NULL) -+ return -EINVAL; -+ -+ name_len = dot - name; -+ -+ for (i = 0; i < name_len; i++) -+ if (!is_edf_char(name[i])) -+ return -EINVAL; -+ for (i = name_len + 1; i < (int) strlen(name); i++) -+ if (!is_edf_char(name[i])) -+ return -EINVAL; -+ return 0; -+} -+ -+/* -+ * Summarize the number of bytes used in the last data block. -+ */ -+static int walk_last_var_data_block(off_t addr, ssize_t *total) -+{ -+ ssize_t left = cmsfs.blksize; -+ u16 len; -+ int rc; -+ -+ /* subtract displacement */ -+ left -= addr & DATA_BLOCK_MASK; -+ -+ while (left >= (int) sizeof(len)) { -+ -+ rc = _read(&len, sizeof(len), addr); -+ if (rc < 0) -+ return rc; -+ -+ /* -+ * Null length means no more records follow. -+ * Assumption: the last block is zero-padded. -+ */ -+ if (!len) -+ return 0; -+ -+ /* add length of record with the header length */ -+ *total += len + sizeof(len); -+ -+ left -= len + sizeof(len); -+ -+ /* point to next record */ -+ addr += len + sizeof(len); -+ } -+ return 0; -+} -+ -+/* -+ * Return struct record for record number nr. -+ */ -+static struct record *get_record(struct file *f, int nr) -+{ -+ BUG(nr > f->fst->nr_records - 1); -+ return &f->rlist[nr]; -+} -+ -+static int skip_header_byte(struct file *f) -+{ -+ if (f->fst->record_format == RECORD_LEN_FIXED) -+ return 0; -+ -+ if (f->record_scan_state == RSS_HEADER_STARTED) -+ return 1; -+ else -+ return 0; -+} -+ -+static void set_record_len_upper(struct file *f, int record, u8 len) -+{ -+ struct record *r = &f->rlist[record]; -+ -+ if (f->record_scan_state != RSS_DATA_BLOCK_STARTED && -+ f->record_scan_state != RSS_DATA_BLOCK_EXT) -+ DIE("%s: internal error\n", __func__); -+ -+ r->total_len = len << 8; -+ f->record_scan_state = RSS_HEADER_STARTED; -+} -+ -+static void set_record_len_lower(struct file *f, int record, u8 len) -+{ -+ struct record *r = &f->rlist[record]; -+ -+ if (f->record_scan_state != RSS_HEADER_STARTED) -+ DIE("%s: internal error\n", __func__); -+ -+ r->total_len += len; -+ f->record_scan_state = RSS_HEADER_COMPLETE; -+} -+ -+static void set_record_len(struct file *f, int record, u16 len) -+{ -+ struct record *r = &f->rlist[record]; -+ -+ if (f->fst->nr_records && f->fst->nr_records == record) -+ DIE("%s: record nr: %d out of bounds\n", __func__, record); -+ -+ if (f->record_scan_state != RSS_DATA_BLOCK_STARTED && -+ f->record_scan_state != RSS_DATA_BLOCK_EXT) -+ DIE("%s: internal error\n", __func__); -+ -+ r->total_len = len; -+ f->record_scan_state = RSS_HEADER_COMPLETE; -+} -+ -+static void set_record(struct file *f, int *record, off_t addr, int len, -+ size_t *total, int block) -+{ -+ struct record *r = &f->rlist[*record]; -+ -+ if (f->record_scan_state != RSS_HEADER_COMPLETE) -+ DIE("%s: internal error\n", __func__); -+ -+ r->first_block_len = len; -+ r->disk_start = addr; -+ r->block_nr = block; -+ -+ if (addr == NULL_BLOCK) { -+ if (f->null_ctr % cmsfs.blksize == 0) -+ r->null_block_started = 1; -+ f->null_ctr += len; -+ } else -+ f->null_ctr = 0; -+ -+ /* add previous record linefeed but not for the first record */ -+ if (f->linefeed && *record) -+ (*total)++; -+ r->file_start = *total; -+ (*total) += r->total_len; -+ f->record_scan_state = RSS_DATA_BLOCK_STARTED; -+} -+ -+static void add_record_ext(struct record *rec, struct record_ext *ext) -+{ -+ struct record_ext *tmp; -+ int i = 0; -+ -+ if (rec->ext == NULL) { -+ rec->ext = ext; -+ ext->prev = NULL; -+ ext->next = NULL; -+ } else { -+ tmp = rec->ext; -+ i++; -+ while (tmp->next != NULL) { -+ i++; -+ tmp = tmp->next; -+ } -+ tmp->next = ext; -+ ext->prev = tmp; -+ ext->next = NULL; -+ } -+} -+ -+static void set_record_extension(struct file *f, int *record, off_t addr, -+ int len, int block) -+{ -+ struct record *rec = &f->rlist[*record]; -+ struct record_ext *ext; -+ -+ if (f->record_scan_state != RSS_DATA_BLOCK_STARTED && -+ f->record_scan_state != RSS_DATA_BLOCK_EXT) -+ DIE("%s: interal error\n", __func__); -+ -+ BUG(*record >= f->fst->nr_records); -+ -+ ext = malloc(sizeof(struct record_ext)); -+ if (ext == NULL) -+ DIE_PERROR("malloc failed\n"); -+ memset(ext, 0, sizeof(*ext)); -+ ext->len = len - skip_header_byte(f); -+ ext->disk_start = addr + skip_header_byte(f); -+ ext->block_nr = block; -+ -+ if (ext->disk_start == NULL_BLOCK) { -+ if (f->null_ctr % cmsfs.blksize == 0) -+ ext->null_block_started = 1; -+ f->null_ctr += len; -+ } else -+ f->null_ctr = 0; -+ -+ add_record_ext(rec, ext); -+ f->record_scan_state = RSS_DATA_BLOCK_EXT; -+} -+ -+static int end_of_file(struct file *f, int record) -+{ -+ if (record == f->fst->nr_records) -+ return 1; -+ return 0; -+} -+ -+static void walk_fixed_data_block(struct file *f, off_t addr, int *record, -+ size_t *total, int block, int disp) -+{ -+ off_t offset = block * cmsfs.blksize + disp; -+ int rlen = (f->fst->record_len > cmsfs.blksize) ? -+ cmsfs.blksize : f->fst->record_len; -+ int first = (offset % f->fst->record_len) ? -+ rlen - (offset % rlen) : 0; -+ int left = cmsfs.blksize - disp; -+ -+ if (first) { -+ BUG(first > left); -+ set_record_extension(f, record, addr, first, block); -+ left -= first; -+ if (addr != NULL_BLOCK) -+ addr += first; -+ } -+ -+ while (left >= rlen) { -+ /* -+ * Increment record number only after adding a possible -+ * extension. *record starts with -1 so the first is 0. -+ */ -+ (*record)++; -+ if (end_of_file(f, *record)) -+ return; -+ -+ set_record_len(f, *record, f->fst->record_len); -+ set_record(f, record, addr, rlen, total, block); -+ -+ left -= rlen; -+ if (addr != NULL_BLOCK) -+ addr += rlen; -+ } -+ -+ /* partial record left */ -+ if (left > 0) { -+ (*record)++; -+ if (end_of_file(f, *record)) -+ return; -+ -+ set_record_len(f, *record, f->fst->record_len); -+ set_record(f, record, addr, left, total, block); -+ return; -+ } -+} -+ -+static int get_record_unused_bytes(struct file *f, int nr) -+{ -+ struct record *rec = get_record(f, nr); -+ struct record_ext *rext; -+ int used = 0; -+ -+ /* no data bytes yet */ -+ if (f->record_scan_state == RSS_HEADER_COMPLETE) -+ return rec->total_len; -+ -+ used = rec->first_block_len; -+ -+ /* only first block */ -+ if (f->record_scan_state == RSS_DATA_BLOCK_STARTED) -+ goto out; -+ rext = rec->ext; -+ while (rext != NULL) { -+ used += rext->len; -+ rext = rext->next; -+ } -+out: -+ return rec->total_len - used; -+} -+ -+static int walk_var_data_block(struct file *f, off_t addr, unsigned int disp, -+ int *record, size_t *total, int block, int skip) -+{ -+ ssize_t left = cmsfs.blksize - skip; -+ int last, rc; -+ u8 half_len; -+ u16 len; -+ -+ /* -+ * If records are skipped on this block there is no record extension, -+ * overwrite disp and start with scanning the record. -+ */ -+ if (skip) -+ disp = 0; -+ -+ /* -+ * disp set means 1 or 2 header bytes and possibly data bytes on the -+ * last block or a null block. -+ */ -+ if (disp) { -+ if (addr == NULL_BLOCK) { -+ last = cmsfs.blksize; -+ -+ /* -+ * Special case: last block can be a null block with -+ * not all bytes used on it. -+ */ -+ if (f->fst->nr_blocks - 1 == block) -+ last = get_record_unused_bytes(f, *record); -+ -+ /* -+ * Special case: record header on last block wo. data. -+ * That means no record data yet for this block. -+ */ -+ if (f->record_scan_state == RSS_HEADER_COMPLETE) -+ set_record(f, record, addr, last, total, block); -+ else -+ set_record_extension(f, record, addr, last, -+ block); -+ return 0; -+ } -+ -+ if (disp == VAR_RECORD_SPANNED) -+ len = cmsfs.blksize; -+ else -+ len = disp; -+ -+ -+ /* split header -> read second length byte */ -+ if (f->record_scan_state == RSS_HEADER_STARTED) { -+ rc = _read(&half_len, sizeof(half_len), addr); -+ if (rc < 0) -+ return rc; -+ set_record_len_lower(f, *record, half_len); -+ left--; -+ len--; -+ addr++; -+ } -+ -+ if (f->record_scan_state == RSS_HEADER_COMPLETE) -+ set_record(f, record, addr, len, total, block); -+ else -+ set_record_extension(f, record, addr, len, block); -+ -+ if (disp == VAR_RECORD_SPANNED) -+ return 0; -+ -+ left -= len; -+ addr += len; -+ } -+ -+ /* at least one data byte */ -+ while (left >= (int) sizeof(len) + 1) { -+ -+ rc = _read(&len, sizeof(len), addr); -+ if (rc < 0) -+ return rc; -+ -+ /* -+ * Null length means no more records follow. -+ * Assumption: the last block is zero-padded. -+ */ -+ if (!len) -+ return 0; -+ -+ /* -+ * Increment record number only after adding a possible -+ * extension. *record starts with -1 so the first is 0. -+ */ -+ (*record)++; -+ set_record_len(f, *record, len); -+ -+ /* account consumed header bytes */ -+ left -= sizeof(len); -+ addr += sizeof(len); -+ -+ /* limit to block end */ -+ if (len > left) -+ len = left; -+ -+ /* add length of record including the header length */ -+ set_record(f, record, addr, len, total, block); -+ -+ left -= len; -+ /* point to next record header */ -+ addr += len; -+ } -+ -+ /* 2 header bytes left */ -+ if (left == 2) { -+ rc = _read(&len, sizeof(len), addr); -+ if (rc < 0) -+ return rc; -+ if (!len) -+ return 0; -+ -+ (*record)++; -+ set_record_len(f, *record, len); -+ return 0; -+ } -+ -+ /* split header */ -+ if (left == 1) { -+ if (end_of_file(f, *record + 1)) -+ return 0; -+ rc = _read(&half_len, sizeof(half_len), addr); -+ if (rc < 0) -+ return rc; -+ (*record)++; -+ set_record_len_upper(f, *record, half_len); -+ f->record_scan_state = RSS_HEADER_STARTED; -+ } -+ return 0; -+} -+ -+static void cache_fixed_data_block(struct file *f, off_t addr, int *block, -+ int *record, size_t *total, int disp) -+{ -+ /* -+ * Cannot distinguish null block pointers from not existing pointers, -+ * so this fn is called for the whole pointer block and maybe for -+ * non-existing blocks and records too. Check and bail out if EOF. -+ */ -+ if (*block >= f->fst->nr_blocks) -+ return; -+ -+ walk_fixed_data_block(f, addr, record, total, *block, disp); -+ f->blist[*block].disk_addr = addr & ~DATA_BLOCK_MASK; -+ f->blist[*block].hi_record_nr = *record + 1; -+ (*block)++; -+} -+ -+/* -+ * Walk all pointer blocks of a fixed file and call function for every -+ * data block respecting the sequence of the data. -+ */ -+static int cache_file_fixed(struct file *f, off_t addr, int level, int *block, -+ unsigned int *disp, int *record, size_t *total) -+{ -+ int left = f->ptr_per_block; -+ off_t ptr; -+ -+ if (level > 0) { -+ level--; -+ while (left--) { -+ ptr = get_fixed_pointer(addr); -+ if (ptr < 0) -+ return ptr; -+ cache_file_fixed(f, ptr, level, block, disp, record, total); -+ /* don't increment for null block pointers */ -+ if (addr) -+ addr += PTR_SIZE; -+ } -+ return 0; -+ } -+ cache_fixed_data_block(f, addr, block, record, total, 0); -+ return 0; -+} -+ -+static int cache_variable_data_block(struct file *f, off_t addr, int *block, -+ int *record, int disp, size_t *total, int skip) -+{ -+ int rc; -+ -+ /* -+ * Cannot distinguish null block pointers from not existing pointers, -+ * so this fn is called for the whole pointer block and maybe for -+ * non-existing blocks and records too. Check and bail out if EOF. -+ */ -+ if (*block >= f->fst->nr_blocks || -+ *record >= f->fst->nr_records) -+ return 0; -+ -+ rc = walk_var_data_block(f, addr, disp, record, total, *block, skip); -+ if (rc < 0) -+ return rc; -+ -+ f->blist[*block].disk_addr = addr & ~DATA_BLOCK_MASK; -+ /* record starts with 0 but on-disk record number with 1 */ -+ f->blist[*block].hi_record_nr = *record + 1; -+ f->blist[*block].disp = disp; -+ (*block)++; -+ return 0; -+} -+ -+/* -+ * Walk all pointer blocks of a variable file and call function for every -+ * data block respecting the sequence of the data. -+ */ -+static int cache_file_variable(struct file *f, off_t addr, int level, -+ int *block, unsigned int *disp, -+ int *record, size_t *total) -+{ -+ int nr, left = f->ptr_per_block; -+ off_t ptr; -+ -+ if (level > 0) { -+ level--; -+ /* 4 or 8 bytes are left at the end (offset) which we ignore */ -+ while (left--) { -+ ptr = get_var_pointer(addr, &nr, disp); -+ if (ptr < 0) -+ return ptr; -+ if (ptr == VAR_FILE_END) -+ return 0; -+ cache_file_variable(f, ptr, level, block, -+ disp, record, total); -+ addr += VPTR_SIZE; -+ } -+ return 0; -+ } -+ return cache_variable_data_block(f, addr, block, record, *disp, total, 0); -+} -+ -+static int locate_last_data_vptr(off_t addr, int level, -+ struct fst_entry *fst, struct var_ptr *vptr) -+{ -+ int last, rc; -+ -+ if (!level) -+ return 0; -+ level--; -+ -+ /* read offset pointer from the end of the var pointer block */ -+ rc = _read(&last, sizeof(last), addr + cmsfs.blksize - sizeof(last)); -+ if (rc < 0) -+ return rc; -+ -+ if (last % VPTR_SIZE || last > cmsfs.blksize) -+ return -EIO; -+ rc = _read(vptr, VPTR_SIZE, addr + last); -+ if (rc < 0) -+ return rc; -+ if (vptr->hi_record_nr != fst->nr_records) -+ return -EIO; -+ -+ /* vptr should contain the highest data block pointer */ -+ if (!level) -+ return 0; -+ -+ if (vptr->next == NULL_BLOCK) -+ return 0; -+ -+ return locate_last_data_vptr(ABS(vptr->next), level, fst, vptr); -+} -+ -+static int is_textfile(struct fst_entry *fst) -+{ -+ char type[MAX_TYPE_LEN]; -+ struct filetype *ft; -+ -+ if (!fst) -+ return 0; -+ -+ memset(type, 0, sizeof(type)); -+ ebcdic_dec(type, fst->type, 8); -+ -+ list_iterate(ft, &text_type_list, list) -+ if (strncmp(ft->name, type, strlen(ft->name)) == 0) -+ return 1; -+ return 0; -+} -+ -+/* -+ * Decide if linefeeds are needed for this file type. -+ */ -+static int linefeed_mode_enabled(struct fst_entry *fst) -+{ -+ if (cmsfs.mode == BINARY_MODE) -+ return 0; -+ if (cmsfs.mode == TEXT_MODE) -+ return 1; -+ return is_textfile(fst); -+} -+ -+/* -+ * Workaround glibc 2.9 bug with less than 3 files and give room for some -+ * new files. If cache is full it will be purged and rebuild. -+ */ -+static int max_cache_entries(void) -+{ -+ return cmsfs.files + 10 + cmsfs.files / 4; -+} -+ -+static void resize_htab(void) -+{ -+ int i; -+ -+ for (i = 0; i < cmsfs.fcache_used; i++) -+ free(cmsfs.fcache[i].str); -+ hdestroy_r(&cmsfs.htab); -+ free(cmsfs.fcache); -+ cmsfs.fcache_used = 0; -+ cmsfs.fcache_max = max_cache_entries(); -+ -+ cmsfs.fcache = calloc(cmsfs.fcache_max, sizeof(struct fcache_entry)); -+ if (!hcreate_r(cmsfs.fcache_max, &cmsfs.htab)) -+ DIE("hcreate failed\n"); -+} -+ -+static void cache_fst_addr(off_t addr, const char *file) -+{ -+ struct fcache_entry *fce; -+ ENTRY e, *eptr; -+ -+ e.key = strdup(file); -+ -+again: -+ if (hsearch_r(e, FIND, &eptr, &cmsfs.htab) == 0) { -+ /* cache it */ -+ if (cmsfs.fcache_used == cmsfs.fcache_max - 1) { -+ DEBUG("hsearch: hash table full: %d\n", cmsfs.fcache_used); -+ resize_htab(); -+ goto again; -+ } -+ -+ fce = &cmsfs.fcache[cmsfs.fcache_used]; -+ cmsfs.fcache_used++; -+ fce->fst_addr = addr; -+ fce->str = e.key; -+ -+ e.data = fce; -+ if (hsearch_r(e, ENTER, &eptr, &cmsfs.htab) == 0) -+ DIE("hsearch: hash table full\n"); -+ } else -+ free(e.key); -+} -+ -+static void update_htab_entry(off_t addr, const char *file) -+{ -+ struct fcache_entry *fce; -+ ENTRY e, *eptr; -+ -+ e.key = strdup(file); -+ -+ if (hsearch_r(e, FIND, &eptr, &cmsfs.htab) == 0) { -+ /* not yet cached, nothing to do */ -+ free(e.key); -+ return; -+ } else { -+ /* update it */ -+ fce = eptr->data; -+ fce->fst_addr = addr; -+ e.data = fce; -+ if (hsearch_r(e, ENTER, &eptr, &cmsfs.htab) == 0) -+ DIE("%s: hash table full\n", __func__); -+ } -+} -+ -+static void invalidate_htab_entry(const char *name) -+{ -+ struct fcache_entry *fce; -+ ENTRY e, *eptr; -+ -+ e.key = strdup(name); -+ -+ if (hsearch_r(e, FIND, &eptr, &cmsfs.htab) == 0) { -+ /* nothing to do if not cached */ -+ free(e.key); -+ return; -+ } -+ -+ fce = eptr->data; -+ fce->fst_addr = 0; -+ e.data = fce; -+ if (hsearch_r(e, ENTER, &eptr, &cmsfs.htab) == 0) -+ DIE("hsearch: hash table full\n"); -+} -+ -+/* -+ * For each FST entry in a directory block do action. -+ * -+ * Return: -+ * hit == NULL : lookup file not found -+ * hit != NULL : lookup file found, addr of the fst entry -+ */ -+static void walk_dir_block(struct fst_entry *fst, struct walk_file *walk, -+ int level, off_t *hit) -+{ -+ off_t ptr, addr = walk->addr; -+ char file[MAX_FNAME]; -+ int ret, left; -+ -+ /* handle higher level directory pointer blocks */ -+ if (level > 0) { -+ level--; -+ left = PTRS_PER_BLOCK; -+ while (left--) { -+ ptr = get_fixed_pointer(addr); -+ BUG(ptr < 0); -+ if (!ptr) -+ break; -+ walk->addr = ptr; -+ walk_dir_block(fst, walk, level, hit); -+ if (hit != NULL && *hit) -+ return; -+ addr += PTR_SIZE; -+ } -+ return; -+ } -+ -+ if (walk->flag == WALK_FLAG_CACHE_DBLOCKS) { -+ walk->dlist[walk->dlist_used++] = walk->addr; -+ return; -+ } -+ -+ left = cmsfs.blksize / sizeof(struct fst_entry); -+ while (left--) { -+ ret = readdir_entry(fst, walk->addr); -+ -+ /* directory and allocmap type are skipped */ -+ -+ if (ret == READDIR_FILE_ENTRY) { -+ if (walk->flag == WALK_FLAG_LOOKUP) { -+ if ((memcmp(fst->name, walk->name, 8) == 0) && -+ (memcmp(fst->type, walk->type, 8) == 0)) { -+ /* got it */ -+ *hit = walk->addr; -+ return; -+ } -+ } -+ -+ if (walk->flag == WALK_FLAG_READDIR) { -+ memset(file, 0, sizeof(file)); -+ decode_edf_name(file, fst->name, fst->type); -+ if (!file_unlinked(file)) { -+ cache_fst_addr(walk->addr, file); -+ walk->filler(walk->buf, file, NULL, 0); -+ } -+ } -+ } -+ -+ if (ret == READDIR_END_OF_DIR) { -+ if (walk->flag == WALK_FLAG_LOCATE_EMPTY) { -+ *hit = walk->addr; -+ return; -+ } -+ break; -+ } -+ walk->addr += sizeof(struct fst_entry); -+ }; -+ return; -+} -+ -+static void walk_directory(struct fst_entry *fst, struct walk_file *walk, -+ off_t *hit) -+{ -+ if (cmsfs.dir_levels == 0) -+ walk->addr = cmsfs.fdir; -+ else -+ walk->addr = get_fop(cmsfs.fdir); -+ walk_dir_block(fst, walk, cmsfs.dir_levels, hit); -+} -+ -+/* -+ * Check FST record format only when reading FST entry from disk. -+ */ -+static int check_fst_valid(struct fst_entry *fst) -+{ -+ if (fst->record_format != RECORD_LEN_FIXED && -+ fst->record_format != RECORD_LEN_VARIABLE) -+ return 0; -+ else -+ return 1; -+} -+ -+/* -+ * Locate the file's fst_entry in any of the directory blocks. -+ */ -+static off_t lookup_file(const char *name, struct fst_entry *fst, int flag) -+{ -+ struct fcache_entry *fce; -+ char uc_name[MAX_FNAME]; -+ char fname[8], ftype[8]; -+ struct walk_file walk; -+ ENTRY e, *eptr; -+ off_t faddr = 0; -+ int rc; -+ -+ strncpy(uc_name, name, MAX_FNAME); -+ str_toupper(uc_name); -+ -+ if (flag == HIDE_UNLINKED && file_unlinked(uc_name)) -+ return 0; -+ -+ e.key = strdup(uc_name); -+ -+ /* already cached ? */ -+ if (hsearch_r(e, FIND, &eptr, &cmsfs.htab)) { -+ fce = eptr->data; -+ -+ /* check if fst is valid, may be zero for a stale entry */ -+ if (!fce->fst_addr) -+ goto renew; -+ -+ /* read in the fst entry */ -+ rc = _read(fst, sizeof(*fst), fce->fst_addr); -+ BUG(rc < 0); -+ -+ if (!check_fst_valid(fst)) -+ DIE("Invalid file format in file: %s\n", uc_name); -+ -+ free(e.key); -+ return fce->fst_addr; -+ } -+ -+renew: -+ free(e.key); -+ if (encode_edf_name(uc_name, fname, ftype)) -+ return 0; -+ memset(&walk, 0, sizeof(walk)); -+ walk.flag = WALK_FLAG_LOOKUP; -+ walk.name = fname; -+ walk.type = ftype; -+ walk_directory(fst, &walk, &faddr); -+ if (!faddr) -+ return 0; -+ if (!check_fst_valid(fst)) -+ DIE("Invalid file format in file: %s\n", uc_name); -+ cache_fst_addr(faddr, uc_name); -+ return faddr; -+} -+ -+static int cache_file(struct file *f) -+{ -+ int block = 0, record = -1; -+ unsigned int disp = 0; -+ size_t total = 0; -+ -+ return f->fops->cache_data(f, ABS(f->fst->fop), f->fst->levels, -+ &block, &disp, &record, &total); -+} -+ -+/* -+ * Caveat: for fixed files nr_blocks is excluding null blocks, -+ * for variable files nr_blocks is including null blocks. -+ * Add null blocks for fixed files so allocation and file end -+ * checks work identical for both variants. -+ */ -+static void workaround_nr_blocks(struct file *f) -+{ -+ int nr; -+ -+ if (f->fst->record_format == RECORD_LEN_VARIABLE) -+ return; -+ nr = f->fst->nr_records * f->fst->record_len / cmsfs.blksize; -+ if (f->fst->nr_records * f->fst->record_len % cmsfs.blksize) -+ nr++; -+ f->nr_null_blocks = nr - f->fst->nr_blocks; -+ f->fst->nr_blocks = nr; -+} -+ -+static ssize_t get_file_size_fixed(struct fst_entry *fst) -+{ -+ return fst->nr_records * fst->record_len; -+} -+ -+static ssize_t get_file_size_variable_slow(struct fst_entry *fst) -+{ -+ struct record *rec; -+ ssize_t total = 0; -+ int rc = 0; -+ struct file *f = create_file_object(fst, &rc); -+ -+ if (f == NULL) -+ return rc; -+ -+ rec = get_record(f, f->fst->nr_records - 1); -+ total = rec->file_start + rec->total_len; -+ -+ /* -+ * Note: need to add header bytes since the record information does -+ * not contain them but get_file_size_logical will remove them... -+ */ -+ total += f->fst->nr_records * VAR_RECORD_HEADER_SIZE; -+ destroy_file_object(f); -+ return total; -+} -+ -+static ssize_t get_file_size_variable(struct fst_entry *fst) -+{ -+ struct var_ptr vptr; -+ ssize_t total = 0; -+ off_t ptr; -+ int rc; -+ -+ if (fst->levels > 0) { -+ rc = locate_last_data_vptr(ABS(fst->fop), fst->levels, fst, -+ &vptr); -+ if (rc < 0) -+ return rc; -+ if (vptr.next == 0) { -+ /* -+ * Last block is a null block. Cannot scan that block, -+ * need to scan the whole file instead... -+ */ -+ total = get_file_size_variable_slow(fst); -+ goto skip; -+ } -+ ptr = ABS(vptr.next); -+ if (vptr.disp != VAR_RECORD_SPANNED) { -+ ptr += vptr.disp; -+ /* count displacement as used space */ -+ total += vptr.disp; -+ } else { -+ total += cmsfs.blksize; -+ goto skip_scan; -+ } -+ } else -+ ptr = ABS(fst->fop); -+ -+ /* now count the remaining used space in the last block */ -+ rc = walk_last_var_data_block(ptr, &total); -+ if (rc < 0) -+ return rc; -+ -+skip_scan: -+ /* -+ * Add the full blocks. For variable record file nr_blocks contains -+ * also null blocks. -+ */ -+ if (fst->nr_blocks) -+ total += (fst->nr_blocks - 1) * cmsfs.blksize; -+skip: -+ return total; -+} -+ -+/* -+ * Return the file size as it is on the disk. Includes headers for -+ * variable records. -+ */ -+static ssize_t get_file_size(struct fst_entry *fst) -+{ -+ if (fst->record_format == RECORD_LEN_FIXED) -+ return get_file_size_fixed(fst); -+ else if (fst->record_format == RECORD_LEN_VARIABLE) -+ return get_file_size_variable(fst); -+ return 0; -+} -+ -+static ssize_t get_file_size_logical(struct fst_entry *fst) -+{ -+ ssize_t total; -+ -+ if (fst->nr_records == 0) -+ return 0; -+ if (!fst->fop) -+ return -EIO; -+ total = get_file_size(fst); -+ if (total < 0) -+ return -EIO; -+ -+ /* subtract the record headers */ -+ if (fst->record_format == RECORD_LEN_VARIABLE) -+ total -= fst->nr_records * VAR_RECORD_HEADER_SIZE; -+ -+ if (linefeed_mode_enabled(fst)) -+ total += fst->nr_records; -+ return total; -+} -+ -+static int cmsfs_getattr(const char *path, struct stat *stbuf) -+{ -+ int mask = (cmsfs.allow_other) ? 0444 : 0440; -+ struct fst_entry fst; -+ -+ if (!cmsfs.readonly) -+ mask |= ((cmsfs.allow_other) ? 0222 : 0220); -+ -+ memset(stbuf, 0, sizeof(*stbuf)); -+ stbuf->st_uid = getuid(); -+ stbuf->st_gid = getgid(); -+ stbuf->st_blksize = cmsfs.blksize; -+ -+ if (strcmp(path, "/") == 0) { -+ stbuf->st_mode = S_IFDIR | mask | -+ ((cmsfs.allow_other) ? 0111 : 0110); -+ stbuf->st_nlink = 2; -+ -+ readdir_entry(&fst, cmsfs.fdir); -+ -+ /* date */ -+ stbuf->st_mtime = fst_date_to_time_t(&fst.date[0], -+ fst.flag & FST_FLAG_CENTURY); -+ stbuf->st_atime = stbuf->st_ctime = stbuf->st_mtime; -+ -+ /* size */ -+ stbuf->st_size = fst.record_len * fst.nr_records; -+ stbuf->st_blocks = max(stbuf->st_size, cmsfs.blksize); -+ stbuf->st_blocks = ((stbuf->st_blocks + cmsfs.data_block_mask) & -+ ~cmsfs.data_block_mask) >> 9; -+ } else { -+ if (!lookup_file(path + 1, &fst, HIDE_UNLINKED)) -+ return -ENOENT; -+ -+ stbuf->st_mode = S_IFREG | mask; -+ stbuf->st_nlink = 1; -+ -+ /* date */ -+ stbuf->st_mtime = stbuf->st_atime = stbuf->st_ctime = -+ fst_date_to_time_t(&fst.date[0], -+ fst.flag & FST_FLAG_CENTURY); -+ /* size */ -+ stbuf->st_size = get_file_size_logical(&fst); -+ if (stbuf->st_size < 0) -+ return -EIO; -+ /* -+ * Include potential sparse blocks for variable files which -+ * are included in nr_blocks to avoid scanning the whole file. -+ */ -+ stbuf->st_blocks = fst.nr_blocks * cmsfs.nr_blocks_512; -+ } -+ return 0; -+} -+ -+static int cmsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, -+ off_t offset, struct fuse_file_info *fi) -+{ -+ struct walk_file walk; -+ struct fst_entry fst; -+ -+ (void) offset; -+ (void) fi; -+ -+ /* -+ * Offset is ignored and 0 passed to the filler fn so the whole -+ * directory is read at once. -+ */ -+ -+ /* EDF knows only the root directory */ -+ if (strcmp(path, "/") != 0) -+ return -ENOENT; -+ -+ filler(buf, ".", NULL, 0); -+ filler(buf, "..", NULL, 0); -+ -+ memset(&walk, 0, sizeof(walk)); -+ /* readdir is possible without open so fi->fh is not set */ -+ walk.flag = WALK_FLAG_READDIR; -+ walk.buf = buf; -+ walk.filler = filler; -+ walk_directory(&fst, &walk, NULL); -+ return 0; -+} -+ -+static int cmsfs_open(const char *path, struct fuse_file_info *fi) -+{ -+ struct fst_entry fst; -+ struct file *f; -+ off_t fst_addr; -+ int rc = 0; -+ -+ /* -+ * open flags: -+ * O_DIRECTORY: FUSE captures open on / so not needed. -+ * O_NOATIME: ignored because there is no atime in EDF. -+ * O_NOFOLLOW: can be ignored since EDF has no links. -+ * O_SYNC: ignored since IO is alwasy sync. -+ * O_TRUNC, O_CREAT, O_EXCL: avoided by FUSE. -+ */ -+ fst_addr = lookup_file(path + 1, &fst, SHOW_UNLINKED); -+ if (!fst_addr) -+ return -ENOENT; -+ -+ f = file_open(path + 1); -+ if (f == NULL) { -+ f = create_file_object(&fst, &rc); -+ if (f == NULL) -+ return rc; -+ f->fst_addr = fst_addr; -+ -+ /* -+ * Store file size in file object. Needed for write of fixed record -+ * length files when the write is not a multiple of the record length. -+ * In this case a second write would fail since the file size would -+ * be calculated by lrecl * nr_records. Use session_size therefore. -+ */ -+ f->session_size = get_file_size_logical(&fst); -+ if (f->session_size < 0) -+ return -EIO; -+ -+ f->wcache = malloc(WCACHE_MAX); -+ if (f->wcache == NULL) -+ return -ENOMEM; -+ -+ strncpy(f->path, path, MAX_FNAME + 1); -+ str_toupper(f->path); -+ -+ f->use_count = 1; -+ list_add(&f->list, &open_file_list); -+ } else -+ f->use_count++; -+ -+ if (fi->flags & O_RDWR || fi->flags & O_WRONLY) -+ f->write_count++; -+ -+ fi->fh = (u64) f; -+ return 0; -+} -+ -+static void set_fdir_date_current(void) -+{ -+ struct fst_entry fst; -+ int rc; -+ -+ rc = _read(&fst, sizeof(fst), cmsfs.fdir); -+ BUG(rc < 0); -+ set_fst_date_current(&fst); -+ rc = _write(&fst, sizeof(fst), cmsfs.fdir); -+ BUG(rc < 0); -+} -+ -+static void increase_file_count(void) -+{ -+ struct fst_entry fst; -+ int rc; -+ -+ rc = _read(&fst, sizeof(fst), cmsfs.fdir); -+ BUG(rc < 0); -+ fst.nr_records = ++cmsfs.files + 2; -+ set_fst_date_current(&fst); -+ rc = _write(&fst, sizeof(fst), cmsfs.fdir); -+ BUG(rc < 0); -+} -+ -+static void decrease_file_count(void) -+{ -+ struct fst_entry fst; -+ int rc; -+ -+ rc = _read(&fst, sizeof(fst), cmsfs.fdir); -+ BUG(rc < 0); -+ fst.nr_records = --cmsfs.files + 2; -+ set_fst_date_current(&fst); -+ rc = _write(&fst, sizeof(fst), cmsfs.fdir); -+ BUG(rc < 0); -+} -+ -+static off_t get_reserved_block(void) -+{ -+ off_t addr; -+ -+ if (cmsfs.reserved_blocks > 0) -+ cmsfs.reserved_blocks--; -+ addr = get_zero_block(); -+ BUG(addr < 0); -+ return addr; -+} -+ -+static void cache_dblocks(struct walk_file *walk) -+{ -+ double dblocks; -+ -+ /* calculate number of data blocks used for FST entries */ -+ dblocks = (cmsfs.files + 2) * sizeof(struct fst_entry); -+ dblocks = ceil(dblocks / cmsfs.blksize); -+ /* add a spare one in case of create file */ -+ dblocks++; -+ -+ memset(walk, 0, sizeof(*walk)); -+ walk->flag = WALK_FLAG_CACHE_DBLOCKS; -+ walk->dlist = calloc(dblocks, sizeof(off_t)); -+ if (walk->dlist == NULL) -+ DIE_PERROR("malloc failed"); -+ walk_directory(NULL, walk, NULL); -+} -+ -+static void free_dblocks(struct walk_file *walk) -+{ -+ free(walk->dlist); -+} -+ -+static void purge_dblock_ptrs(int level, off_t addr) -+{ -+ int left = PTRS_PER_BLOCK; -+ off_t ptr, start = addr; -+ -+ if (!level) -+ return; -+ level--; -+ while (left--) { -+ ptr = get_fixed_pointer(addr); -+ BUG(ptr < 0); -+ if (ptr != NULL_BLOCK) -+ purge_dblock_ptrs(level, ptr); -+ -+ /* don't increment for null block pointers */ -+ if (addr) -+ addr += PTR_SIZE; -+ } -+ free_block(start); -+} -+ -+/* -+ * Return total number of pointer entries for level. -+ */ -+static int pointers_per_level(struct file *f, int level, int nr_blocks) -+{ -+ double entries = nr_blocks; -+ -+ if (!level || nr_blocks < 2) -+ return 0; -+ -+ if (level == 1) -+ return nr_blocks; -+ -+ level--; -+ while (level--) -+ entries = ceil(entries / f->ptr_per_block); -+ return (int) entries; -+} -+ -+static int per_level_fixed(int level, int entries) -+{ -+ double val = entries; -+ -+ while (level--) -+ val = ceil(val / PTRS_PER_BLOCK); -+ return (int) val; -+} -+ -+static void rewrite_dir_ptr_block(struct walk_file *walk, -+ int level, off_t dst, int start) -+{ -+ struct fixed_ptr ptr; -+ int rc, i, end; -+ off_t addr; -+ -+ if (!level) -+ return; -+ -+ end = min(start + PTRS_PER_BLOCK, -+ per_level_fixed(level - 1, walk->dlist_used)); -+ BUG(start > end); -+ -+ for (i = start; i < end; i++) { -+ if (level == 1) { -+ addr = walk->dlist[i]; -+ if (addr) -+ ptr.next = REL(addr); -+ else -+ ptr.next = 0; -+ } else { -+ addr = get_zero_block(); -+ BUG(addr < 0); -+ ptr.next = REL(addr); -+ } -+ -+ rc = _write(&ptr, sizeof(ptr), dst); -+ BUG(rc < 0); -+ dst += sizeof(ptr); -+ -+ rewrite_dir_ptr_block(walk, level - 1, addr, -+ i * PTRS_PER_BLOCK); -+ } -+} -+ -+static int update_dir_levels(int blocks) -+{ -+ int levels = 1; -+ -+ if (blocks < 2) -+ return 0; -+ -+ while (blocks / (PTRS_PER_BLOCK + 1)) { -+ levels++; -+ blocks /= PTRS_PER_BLOCK; -+ } -+ return levels; -+} -+ -+static void rewrite_dblock_ptrs(struct walk_file *walk) -+{ -+ int rc, nr_blocks = walk->dlist_used; -+ struct fst_entry fst; -+ off_t dst; -+ -+ BUG(!nr_blocks); -+ -+ /* read in the directory FST */ -+ rc = _read(&fst, sizeof(fst), cmsfs.fdir); -+ BUG(rc < 0); -+ -+ if (nr_blocks == 1) { -+ fst.fop = REL(cmsfs.fdir); -+ fst.levels = 0; -+ cmsfs.dir_levels = fst.levels; -+ goto store; -+ } -+ -+ dst = get_zero_block(); -+ BUG(dst < 0); -+ fst.fop = REL(dst); -+ -+ fst.levels = update_dir_levels(walk->dlist_used); -+ cmsfs.dir_levels = fst.levels; -+ rewrite_dir_ptr_block(walk, fst.levels, dst, 0); -+store: -+ rc = _write(&fst, sizeof(fst), cmsfs.fdir); -+ BUG(rc < 0); -+} -+ -+/* -+ * Update used block count in disk label. -+ */ -+static void update_block_count(void) -+{ -+ int rc; -+ -+ rc = _write(&cmsfs.used_blocks, sizeof(unsigned int), USED_BLOCK_ADDR); -+ BUG(rc < 0); -+} -+ -+static int cmsfs_create(const char *path, mode_t mode, -+ struct fuse_file_info *fi) -+{ -+ char fname[8], ftype[8]; -+ char uc_name[MAX_FNAME]; -+ struct walk_file walk; -+ struct fst_entry fst; -+ off_t fst_addr = 0; -+ int rc; -+ -+ /* no permissions in EDF */ -+ (void) mode; -+ -+ /* -+ * Note: creating a file that was unlinked but not yet deleted from -+ * disk is not supported. That means the unlinked file can still be -+ * opened. -+ */ -+ if (lookup_file(path + 1, &fst, SHOW_UNLINKED)) -+ return cmsfs_open(path, fi); -+ -+ if (cmsfs.readonly) -+ return -EACCES; -+ -+ rc = edf_name_valid(path + 1); -+ if (rc) -+ return rc; -+ -+ /* force uppercase */ -+ strncpy(uc_name, path + 1, MAX_FNAME); -+ str_toupper(uc_name); -+ -+ rc = encode_edf_name(uc_name, fname, ftype); -+ if (rc) -+ return rc; -+ -+ /* find free fst entry */ -+ memset(&walk, 0, sizeof(walk)); -+ walk.flag = WALK_FLAG_LOCATE_EMPTY; -+ walk_directory(&fst, &walk, &fst_addr); -+ -+ /* no free slot found, allocate new directory block */ -+ if (!fst_addr) { -+ /* -+ * Be conservative and check if enough blocks for all -+ * directory levels are available. -+ */ -+ if (cmsfs.total_blocks - cmsfs.used_blocks < cmsfs.dir_levels + 2) -+ return -ENOSPC; -+ -+ fst_addr = get_zero_block(); -+ if (fst_addr < 0) -+ return fst_addr; -+ -+ cache_dblocks(&walk); -+ /* add the newly allocated block to dlist */ -+ walk.dlist[walk.dlist_used++] = fst_addr; -+ -+ purge_dblock_ptrs(cmsfs.dir_levels, get_fop(cmsfs.fdir)); -+ rewrite_dblock_ptrs(&walk); -+ free_dblocks(&walk); -+ update_block_count(); -+ } -+ -+ /* -+ * Fill fst entry. Default template: -+ * format: variable -+ * record_len: 0 -+ * mode: A1 -+ * flag: 0, century bit (0x8) set by following utimens -+ * fop: 0 -+ * nr_records: 0 -+ * nr_blocks: 0 -+ * levels: 0 -+ * ptr_size: 0xc for variable format -+ * date: set to current date -+ */ -+ memset(&fst, 0, sizeof(fst)); -+ memcpy(fst.name, fname, 8); -+ memcpy(fst.type, ftype, 8); -+ ebcdic_enc((char *) &fst.mode, "A1", 2); -+ fst.record_format = RECORD_LEN_VARIABLE; -+ fst.ptr_size = sizeof(struct var_ptr); -+ -+ rc = set_fst_date_current(&fst); -+ if (rc != 0) -+ return rc; -+ -+ rc = _write(&fst, sizeof(fst), fst_addr); -+ BUG(rc < 0); -+ cache_fst_addr(fst_addr, uc_name); -+ increase_file_count(); -+ return cmsfs_open(path, fi); -+} -+ -+static int purge_pointer_block_fixed(struct file *f, int level, off_t addr) -+{ -+ int left = f->ptr_per_block; -+ off_t ptr, start = addr; -+ -+ if (!level) -+ return 0; -+ -+ level--; -+ while (left--) { -+ if (!level) -+ break; -+ -+ ptr = get_fixed_pointer(addr); -+ if (ptr < 0) -+ return ptr; -+ if (ptr != NULL_BLOCK) -+ purge_pointer_block_fixed(f, level, ptr); -+ -+ /* don't increment for null block pointers */ -+ if (addr) -+ addr += PTR_SIZE; -+ } -+ free_block(start); -+ return 0; -+} -+ -+static int purge_pointer_block_variable(struct file *f, int level, -+ off_t addr) -+{ -+ int nr, left = f->ptr_per_block; -+ off_t ptr, start = addr; -+ unsigned int disp; -+ -+ if (!level) -+ return 0; -+ -+ level--; -+ while (left--) { -+ if (!level) -+ break; -+ -+ ptr = get_var_pointer(addr, &nr, &disp); -+ if (ptr < 0) -+ return ptr; -+ if (ptr == VAR_FILE_END) -+ break; -+ if (ptr != NULL_BLOCK) -+ purge_pointer_block_variable(f, level, ptr); -+ -+ /* don't increment for null block pointers */ -+ if (addr) -+ addr += VPTR_SIZE; -+ } -+ free_block(start); -+ return 0; -+} -+ -+/* -+ * Store the back pointer for a variable pointer block. -+ * Pointer is offset of last VPTR to block start. -+ */ -+static int store_back_pointer(off_t dst, int entries) -+{ -+ unsigned int back; -+ -+ back = (entries - 1) * VPTR_SIZE; -+ return _write(&back, sizeof(back), -+ ((dst | DATA_BLOCK_MASK) + 1) - sizeof(back)); -+} -+ -+/* -+ * Rewrite one pointer block starting from the highest level. -+ */ -+static int rewrite_pointer_block_fixed(struct file *f, int level, off_t dst, -+ int start) -+{ -+ struct fixed_ptr ptr; -+ int i, end, rc = 0; -+ off_t addr; -+ -+ if (!level) -+ return 0; -+ -+ /* -+ * start is always the first entry of a pointer block, -+ * end is the last used entry in this pointer block. -+ */ -+ end = min(start + f->ptr_per_block, -+ per_level_fixed(level - 1, f->fst->nr_blocks)); -+ BUG(start > end); -+ -+ for (i = start; i < end; i++) { -+ if (level == 1) { -+ addr = f->blist[i].disk_addr; -+ if (addr) -+ ptr.next = REL(addr); -+ else -+ ptr.next = 0; -+ } else { -+ addr = get_reserved_block(); -+ ptr.next = REL(addr); -+ } -+ -+ rc = _write(&ptr, sizeof(ptr), dst); -+ if (rc < 0) -+ return rc; -+ dst += sizeof(ptr); -+ -+ rc = rewrite_pointer_block_fixed(f, level - 1, addr, -+ i * f->ptr_per_block); -+ if (rc < 0) -+ return rc; -+ } -+ return rc; -+} -+ -+static int get_first_block_nr(int level, int entry) -+{ -+ while (level-- > 1) -+ entry *= VPTRS_PER_BLOCK; -+ return entry; -+} -+ -+static int get_last_block_nr(int level, int entry, int nr_blocks) -+{ -+ while (level-- > 1) -+ entry *= VPTRS_PER_BLOCK; -+ entry--; -+ if (entry > nr_blocks - 1) -+ entry = nr_blocks - 1; -+ return entry; -+} -+ -+static int per_level_var(int level, int entries) -+{ -+ double val = entries; -+ -+ while (level--) -+ val = ceil(val / VPTRS_PER_BLOCK); -+ return (int) val; -+} -+ -+/* -+ * Rewrite one pointer block starting from the highest level. -+ */ -+static int rewrite_pointer_block_variable(struct file *f, int level, -+ off_t dst, int start) -+{ -+ int i, bnr, end, rc = 0; -+ struct var_ptr ptr; -+ off_t addr; -+ -+ if (!level) -+ return 0; -+ -+ /* -+ * start is always the first entry of a pointer block, -+ * end is the last used entry in this pointer block. -+ */ -+ end = min(start + f->ptr_per_block, -+ per_level_var(level - 1, f->fst->nr_blocks)); -+ BUG(start > end); -+ -+ for (i = start; i < end; i++) { -+ if (level == 1) { -+ addr = f->blist[i].disk_addr; -+ if (addr) -+ ptr.next = REL(addr); -+ else -+ ptr.next = 0; -+ } else { -+ addr = get_reserved_block(); -+ ptr.next = REL(addr); -+ } -+ -+ bnr = get_first_block_nr(level, i); -+ ptr.disp = f->blist[bnr].disp; -+ -+ bnr = get_last_block_nr(level, i + 1, f->fst->nr_blocks); -+ ptr.hi_record_nr = f->blist[bnr].hi_record_nr; -+ -+ rc = _write(&ptr, sizeof(ptr), dst); -+ if (rc < 0) -+ return rc; -+ dst += sizeof(ptr); -+ -+ rc = rewrite_pointer_block_variable(f, level - 1, addr, -+ i * f->ptr_per_block); -+ if (rc < 0) -+ return rc; -+ } -+ return store_back_pointer(dst, end - start); -+} -+ -+/* -+ * Update fop and pointer blocks if needed. -+ */ -+static int rewrite_pointers(struct file *f) -+{ -+ struct record *rec; -+ off_t dst; -+ -+ if (f->fst->nr_blocks == 0) { -+ f->fst->fop = 0; -+ return 0; -+ } -+ -+ if (f->fst->nr_blocks == 1) { -+ rec = get_record(f, 0); -+ if (rec->disk_start == NULL_BLOCK) -+ f->fst->fop = 0; -+ else -+ f->fst->fop = REL(rec->disk_start); -+ return 0; -+ } -+ -+ /* allocate root block for fst */ -+ dst = get_reserved_block(); -+ f->fst->fop = REL(dst); -+ return f->fops->write_pointers(f, f->fst->levels, dst, 0); -+} -+ -+/* -+ * Guess position in record table. -+ */ -+static int guess_record_number(struct file *f, off_t offset) -+{ -+ int nr; -+ -+ if (f->linefeed) -+ nr = (offset / (f->fst->record_len + 1)); -+ else -+ nr = (offset / f->fst->record_len); -+ if (nr >= f->fst->nr_records) -+ nr = f->fst->nr_records - 1; -+ return nr; -+} -+ -+static int offset_is_linefeed(off_t offset, struct record *prev, -+ struct record *next) -+{ -+ if ((offset < next->file_start) && -+ (offset >= prev->file_start + prev->total_len)) -+ return 1; -+ return 0; -+} -+ -+static int offset_in_record(off_t offset, struct record *rec) -+{ -+ if (offset >= rec->file_start && -+ offset < rec->file_start + rec->total_len) -+ return 1; -+ return 0; -+} -+ -+static void set_hint(struct file *f, int hint) -+{ -+ f->next_record_hint = hint; -+ -+ /* limit hint to last record */ -+ if (f->next_record_hint >= f->fst->nr_records) -+ f->next_record_hint = f->fst->nr_records - 1; -+} -+ -+/* -+ * Find record by file offset. -+ * -+ * Returns: record number in *nr -+ * > 0 : ptr to found record -+ * -1 : linefeed offset -+ * NULL : error -+ */ -+static struct record *find_record(struct file *f, off_t offset, int *nr) -+{ -+ int i, start, step, max = f->fst->nr_records; -+ struct record *rec; -+ -+ /* -+ * next_record_hint is a guess which is optimal for sequential -+ * single-threaded reads. -+ */ -+ i = f->next_record_hint; -+ rec = &f->rlist[i]; -+ -+ if (offset_in_record(offset, rec)) { -+ /* increment hint for sequential read, fails for extensions */ -+ set_hint(f, i + 1); -+ *nr = i; -+ return rec; -+ } -+ -+ /* look out for previous record linefeed from sequential read hint */ -+ if (f->linefeed && i > 0) -+ if (offset_is_linefeed(offset, &f->rlist[i - 1], rec)) -+ return LINEFEED_OFFSET; -+ -+ start = guess_record_number(f, offset); -+ -+ /* because of potential linefeed we need to check the next record */ -+ rec = &f->rlist[start]; -+ if (offset < rec->file_start) -+ step = -1; -+ else -+ step = 1; -+ -+ for (i = start; i >= 0 && i < max; i += step) { -+ rec = &f->rlist[i]; -+ if (offset_in_record(offset, rec)) { -+ set_hint(f, i + 1); -+ *nr = i; -+ return rec; -+ } -+ -+ /* last record reached, only one linefeed can follow */ -+ if (i == max - 1) { -+ if (f->linefeed && -+ (offset == rec->file_start + rec->total_len)) -+ return LINEFEED_OFFSET; -+ else -+ return NULL; -+ } -+ -+ /* check for linefeed byte between two records */ -+ if (step == 1) { -+ if (offset_is_linefeed(offset, rec, &f->rlist[i + 1])) -+ return LINEFEED_OFFSET; -+ } else -+ /* -+ * No need to check if i > 0 since no linefeed before -+ * record 0 possible. -+ */ -+ if (offset_is_linefeed(offset, &f->rlist[i - 1], rec)) -+ return LINEFEED_OFFSET; -+ -+ } -+ DEBUG("find: record not found!\n"); -+ return NULL; -+} -+ -+/* -+ * Get disk address and block size from a record. -+ */ -+static void get_block_data_from_record(struct record *rec, off_t offset, -+ off_t *addr, int *chunk) -+{ -+ int record_off = offset - rec->file_start; -+ struct record_ext *rext; -+ -+ if (record_off >= rec->first_block_len) { -+ record_off -= rec->first_block_len; -+ rext = rec->ext; -+ -+ BUG(rext == NULL); -+ -+ while (record_off >= rext->len) { -+ record_off -= rext->len; -+ rext = rext->next; -+ BUG(rext == NULL); -+ } -+ -+ /* found the right record extension */ -+ if (rext->disk_start == NULL_BLOCK) -+ *addr = NULL_BLOCK; -+ else -+ *addr = rext->disk_start + record_off; -+ *chunk = rext->len - record_off; -+ } else { -+ if (rec->disk_start == NULL_BLOCK) -+ *addr = NULL_BLOCK; -+ else -+ *addr = rec->disk_start + record_off; -+ *chunk = rec->first_block_len - record_off; -+ } -+} -+ -+static int convert_text(iconv_t conv, char *buf, int size) -+{ -+ size_t out_count = size; -+ size_t in_count = size; -+ char *data_ptr = buf; -+ int rc; -+ -+ rc = iconv(conv, &data_ptr, &in_count, &data_ptr, &out_count); -+ if ((rc == -1) || (in_count != 0)) { -+ DEBUG("Code page translation EBCDIC-ASCII failed\n"); -+ return -EIO; -+ } -+ return 0; -+} -+ -+static int cmsfs_read(const char *path, char *buf, size_t size, off_t offset, -+ struct fuse_file_info *fi) -+{ -+ struct file *f = get_fobj(fi); -+ size_t len, copied = 0; -+ struct record *rec; -+ int chunk, nr, rc; -+ off_t addr; -+ -+ (void) path; -+ -+ len = f->session_size; -+ if ((size_t) offset >= len) -+ return 0; -+ -+ if (offset + size > len) -+ size = len - offset; -+ -+ while (size > 0) { -+ rec = find_record(f, offset, &nr); -+ if (rec == NULL) { -+ copied = -EINVAL; -+ DEBUG("%s: invalid addr, size: %lu copied: %lu len: %lu\n", -+ __func__, size, copied, len); -+ goto out; -+ } -+ -+ /* write linefeed directly to buffer and go to next record */ -+ if (rec == LINEFEED_OFFSET) { -+ BUG(!f->linefeed); -+ if (f->translate) -+ *buf = LINEFEED_ASCII; -+ else -+ *buf = LINEFEED_EBCDIC; -+ buf++; -+ copied++; -+ offset++; -+ size--; -+ continue; -+ } -+ -+ /* get addr and block size from record */ -+ get_block_data_from_record(rec, offset, &addr, &chunk); -+ if (chunk <= 0 || addr < 0) -+ DIE("Invalid record data\n"); -+ -+ /* copy less if there is not enough space in the fuse buffer */ -+ if (size < (size_t) chunk) -+ chunk = size; -+ -+ /* read one record */ -+ if (addr == NULL_BLOCK) -+ memset(buf, 0, chunk); -+ else { -+ rc = _read(buf, chunk, addr); -+ if (rc < 0) -+ return rc; -+ } -+ -+ if (f->translate) { -+ rc = convert_text(cmsfs.iconv_from, buf, chunk); -+ if (rc < 0) -+ return rc; -+ } -+ -+ copied += chunk; -+ size -= chunk; -+ buf += chunk; -+ offset += chunk; -+ } -+out: -+ DEBUG("%s: copied: %lu\n", __func__, copied); -+ return copied; -+} -+ -+static int cmsfs_statfs(const char *path, struct statvfs *buf) -+{ -+ unsigned int inode_size = cmsfs.blksize + sizeof(struct fst_entry); -+ unsigned int free_blocks = cmsfs.total_blocks - cmsfs.used_blocks; -+ -+ (void) path; -+ -+ buf->f_bsize = buf->f_frsize = cmsfs.blksize; -+ buf->f_blocks = cmsfs.total_blocks; -+ buf->f_bfree = buf->f_bavail = free_blocks; -+ /* number of possible inodes */ -+ buf->f_files = cmsfs.total_blocks * cmsfs.blksize / inode_size; -+ -+ buf->f_ffree = free_blocks * cmsfs.blksize / inode_size; -+ buf->f_namemax = MAX_FNAME - 1; -+ return 0; -+} -+ -+static int cmsfs_utimens(const char *path, const struct timespec ts[2]) -+{ -+ struct fst_entry fst; -+ off_t fst_addr; -+ struct tm tm; -+ int rc; -+ -+ if (cmsfs.readonly) -+ return -EACCES; -+ -+ fst_addr = lookup_file(path + 1, &fst, HIDE_UNLINKED); -+ if (!fst_addr) -+ return -ENOENT; -+ -+ /* convert timespec to tm */ -+ memset(&tm, 0, sizeof(struct tm)); -+ if (localtime_r(&ts[0].tv_sec, &tm) == NULL) -+ return -EINVAL; -+ -+ update_fst_date(&fst, &tm); -+ rc = _write(&fst, sizeof(fst), fst_addr); -+ BUG(rc < 0); -+ return 0; -+} -+ -+static int cmsfs_rename(const char *path, const char *new_path) -+{ -+ char uc_old_name[MAX_FNAME]; -+ char fname[8], ftype[8]; -+ struct fst_entry fst; -+ char *uc_new_name; -+ struct file *f; -+ off_t fst_addr; -+ int rc; -+ -+ if (cmsfs.readonly) -+ return -EACCES; -+ -+ fst_addr = lookup_file(path + 1, &fst, HIDE_UNLINKED); -+ if (!fst_addr) -+ return -ENOENT; -+ -+ rc = edf_name_valid(new_path + 1); -+ if (rc) -+ return rc; -+ -+ /* force uppercase */ -+ uc_new_name = strdup(new_path + 1); -+ if (uc_new_name == NULL) -+ return -ENOMEM; -+ str_toupper(uc_new_name); -+ -+ rc = encode_edf_name(uc_new_name, fname, ftype); -+ if (rc) -+ return rc; -+ -+ memcpy(&fst.name[0], fname, 8); -+ memcpy(&fst.type[0], ftype, 8); -+ -+ strncpy(uc_old_name, path + 1, MAX_FNAME); -+ str_toupper(uc_old_name); -+ invalidate_htab_entry(uc_old_name); -+ -+ /* update name in file object if the file is opened */ -+ f = file_open(uc_old_name); -+ if (f != NULL) { -+ strncpy(f->path, new_path, MAX_FNAME + 1); -+ str_toupper(f->path); -+ memcpy(f->fst->name, fname, 8); -+ memcpy(f->fst->type, ftype, 8); -+ } -+ -+ rc = _write(&fst, sizeof(fst), fst_addr); -+ BUG(rc < 0); -+ return 0; -+} -+ -+static int cmsfs_fsync(const char *path, int datasync, -+ struct fuse_file_info *fi) -+{ -+ (void) path; -+ (void) datasync; -+ (void) fi; -+ -+ if (cmsfs.readonly) -+ return -EROFS; -+ return msync(cmsfs.map, cmsfs.size, MS_SYNC); -+} -+ -+/* -+ * Detect whether the whole block can be freed. -+ */ -+static int block_started(struct file *f, struct record *rec) -+{ -+ if (rec->disk_start == NULL_BLOCK) { -+ if (rec->null_block_started) -+ return 1; -+ else -+ return 0; -+ } -+ -+ if (f->fst->record_format == RECORD_LEN_FIXED) { -+ if (rec->disk_start % cmsfs.blksize == 0) -+ return 1; -+ else -+ return 0; -+ } -+ -+ if (f->fst->record_format == RECORD_LEN_VARIABLE) { -+ if ((rec->disk_start % cmsfs.blksize == 0) || -+ (rec->disk_start % cmsfs.blksize == 1) || -+ (rec->disk_start % cmsfs.blksize == 2)) -+ return 1; -+ else -+ return 0; -+ } -+ return 0; -+} -+ -+/* -+ * Note: only called for the very last record of a file. That means if the -+ * data starts on a block offset the block can be freed. It does not free -+ * header bytes for variable record files if they are on the previous block! -+ */ -+static void free_record(struct file *f, struct record *rec) -+{ -+ struct record_ext *rext = rec->ext; -+ -+ f->fst->nr_records--; -+ -+ if (block_started(f, rec)) { -+ free_block(rec->disk_start); -+ f->fst->nr_blocks--; -+ if (!rec->disk_start && f->fst->record_format == RECORD_LEN_FIXED) -+ f->nr_null_blocks--; -+ } -+ -+ while (rext != NULL) { -+ /* extensions always start on a new block */ -+ free_block(rext->disk_start); -+ f->fst->nr_blocks--; -+ if (!rext->disk_start && f->fst->record_format == RECORD_LEN_FIXED) -+ f->nr_null_blocks--; -+ rext = rext->next; -+ } -+} -+ -+static int update_var_header_len(struct file *f, u16 *header, -+ struct record *rec) -+{ -+ off_t prev_block_end; -+ int rc, split = 0; -+ -+ if (rec->disk_start % cmsfs.blksize == 0) -+ split = 2; -+ if (rec->disk_start % cmsfs.blksize == 1) -+ split = 1; -+ -+ /* header is completely in this block */ -+ if (!split) { -+ rc = _write(header, sizeof(*header), -+ rec->disk_start - sizeof(*header)); -+ if (rc < 0) -+ return rc; -+ return 0; -+ } -+ -+ BUG(!rec->block_nr); -+ prev_block_end = f->blist[rec->block_nr - 1].disk_addr | DATA_BLOCK_MASK; -+ -+ if (split == 1) { -+ rc = _write((char *) header + 1, 1, rec->disk_start - 1); -+ if (rc < 0) -+ return rc; -+ rc = _write((char *) header, 1, prev_block_end); -+ if (rc < 0) -+ return rc; -+ return 0; -+ } -+ -+ if (split == 2) { -+ rc = _write(header, sizeof(*header), prev_block_end - 1); -+ if (rc < 0) -+ return rc; -+ return 0; -+ } -+ return 0; -+} -+ -+/* -+ * Update the displacement of the last block if the block was spanned and -+ * the new end is inside the previously spanned block. The displacement -+ * must point after the last record to a null length header. -+ * If the block wasn't spanned the displacement of the trimmed record needs -+ * no update. -+ */ -+static void adjust_displacement(struct file *f, int bnr, unsigned int disp) -+{ -+ if (f->blist[bnr].disp == VAR_RECORD_SPANNED) -+ f->blist[bnr].disp = disp; -+} -+ -+/* -+ * Split the last record if needed and wipe until block end. -+ * offset points to the last byte of the trimmed record that is -+ * not a line feed. -+ */ -+static int trim_record(struct file *f, off_t offset, struct record *rec) -+{ -+ int rc, wipe_off, wipe_len, free = 0; -+ off_t file_start = rec->file_start; -+ struct record_ext *rext; -+ u16 header; -+ -+ BUG(!offset_in_record(offset, rec)); -+ -+ if (offset >= rec->file_start && -+ offset < rec->file_start + rec->first_block_len) { -+ wipe_off = offset + 1 - rec->file_start; -+ wipe_len = cmsfs.blksize - ((rec->disk_start & DATA_BLOCK_MASK) + wipe_off); -+ if (!wipe_len) -+ goto ext; -+ if (rec->disk_start) { -+ rc = _zero(rec->disk_start + wipe_off, wipe_len); -+ BUG(rc < 0); -+ } -+ -+ if (f->fst->record_format == RECORD_LEN_VARIABLE) -+ adjust_displacement(f, rec->block_nr, -+ (rec->disk_start + wipe_off) & DATA_BLOCK_MASK); -+ free = 1; -+ } -+ -+ext: -+ if (rec->ext == NULL) -+ goto header; -+ -+ file_start += rec->first_block_len; -+ rext = rec->ext; -+ do { -+ if (free) { -+ free_block(rext->disk_start); -+ f->fst->nr_blocks--; -+ if (!rext->disk_start && f->fst->record_format == RECORD_LEN_FIXED) -+ f->nr_null_blocks--; -+ } else { -+ if (offset >= file_start && -+ offset < file_start + rext->len) { -+ wipe_off = offset + 1 - file_start; -+ wipe_len = cmsfs.blksize - ((rec->disk_start & DATA_BLOCK_MASK) + wipe_off); -+ if (!wipe_len) -+ continue; -+ if (rext->disk_start) { -+ rc = _zero(rext->disk_start + wipe_off, wipe_len); -+ BUG(rc < 0); -+ } -+ -+ if (f->fst->record_format == RECORD_LEN_VARIABLE) -+ adjust_displacement(f, rext->block_nr, -+ (rext->disk_start + wipe_off) & DATA_BLOCK_MASK); -+ free = 1; -+ } -+ } -+ file_start += rext->len; -+ rext = rext->next; -+ } while (rext != NULL); -+ -+header: -+ /* update variable record header with new record length */ -+ if (f->fst->record_format == RECORD_LEN_VARIABLE) { -+ header = offset + 1 - rec->file_start; -+ rc = update_var_header_len(f, &header, rec); -+ if (rc < 0) -+ return rc; -+ -+ /* update total_len in rlist, needed to recalculate lrecl */ -+ rec->total_len = header; -+ } -+ return 0; -+} -+ -+/* -+ * Update levels count. -+ */ -+static void update_levels(struct file *f) -+{ -+ int per_block = f->ptr_per_block; -+ int levels = 1, blocks = f->fst->nr_blocks; -+ -+ if (blocks < 2) { -+ f->fst->levels = 0; -+ return; -+ } -+ -+ while (blocks / (per_block + 1)) { -+ levels++; -+ blocks /= per_block; -+ } -+ -+ f->fst->levels = levels; -+} -+ -+/* -+ * Called by write only using the cached value. -+ */ -+static void update_lrecl_fast(struct file *f, int rlen) -+{ -+ if (rlen > (int) f->fst->record_len) -+ f->fst->record_len = rlen; -+} -+ -+/* -+ * Update longest record length for variable files. -+ */ -+static void update_lrecl(struct file *f) -+{ -+ unsigned int lrecl = 0; -+ struct record *rec; -+ int i; -+ -+ if (f->fst->record_format == RECORD_LEN_FIXED) -+ return; -+ -+ if (!f->fst->nr_records) { -+ f->fst->record_len = 0; -+ return; -+ } -+ -+ for (i = 0; i < f->fst->nr_records; i++) { -+ rec = get_record(f, i); -+ if (rec->total_len > lrecl) -+ lrecl = rec->total_len; -+ } -+ f->fst->record_len = lrecl; -+} -+ -+static int shrink_file(struct file *f, off_t size) -+{ -+ struct record *new_end_rec, *end_rec; -+ int rlen = f->fst->record_len; -+ off_t offset = size; -+ int new_end_nr, rc; -+ -+ /* truncate MUST be aligned to record length for fixed files */ -+ if (f->fst->record_format == RECORD_LEN_FIXED) { -+ if (f->linefeed) -+ rlen++; -+ if (size % rlen) -+ return -EINVAL; -+ } -+ -+ if (size == 0) { -+ new_end_nr = -1; -+ new_end_rec = NULL; -+ goto free; -+ } -+ -+ /* -+ * offset may point to the linefeed after a record, let it point to the -+ * last byte of the new last record instead. The linefeed is virtual -+ * and will be generated automatically. -+ */ -+ if (f->linefeed) { -+ new_end_rec = find_record(f, offset - 1, &new_end_nr); -+ if (new_end_rec == LINEFEED_OFFSET) -+ offset--; -+ } -+ -+ /* get the new last record of the file */ -+ new_end_rec = find_record(f, offset - 1, &new_end_nr); -+ BUG(new_end_rec == NULL || new_end_rec == LINEFEED_OFFSET); -+ -+free: -+ /* free from the end until new_end_rec */ -+ while (f->fst->nr_records - 1 > new_end_nr) { -+ /* get the currently last record of the file */ -+ end_rec = get_record(f, f->fst->nr_records - 1); -+ free_record(f, end_rec); -+ } -+ -+ if (new_end_rec != NULL) { -+ rc = trim_record(f, offset - 1, new_end_rec); -+ if (rc < 0) -+ return rc; -+ } -+ -+ f->session_size = size; -+ if (f->fst->fop) -+ f->fops->delete_pointers(f, f->fst->levels, ABS(f->fst->fop)); -+ -+ update_levels(f); -+ update_lrecl(f); -+ rc = rewrite_pointers(f); -+ if (rc < 0) -+ return rc; -+ update_block_count(); -+ return 0; -+} -+ -+static void hide_null_blocks(struct file *f) -+{ -+ if (f->fst->record_format == RECORD_LEN_VARIABLE) -+ return; -+ f->fst->nr_blocks -= f->nr_null_blocks; -+} -+ -+static void unhide_null_blocks(struct file *f) -+{ -+ if (f->fst->record_format == RECORD_LEN_VARIABLE) -+ return; -+ f->fst->nr_blocks += f->nr_null_blocks; -+} -+ -+static void update_fst(struct file *f, off_t addr) -+{ -+ int rc; -+ -+ hide_null_blocks(f); -+ rc = _write(f->fst, sizeof(*f->fst), addr); -+ BUG(rc < 0); -+ unhide_null_blocks(f); -+} -+ -+static int cmsfs_truncate(const char *path, off_t size) -+{ -+ struct fst_entry fst; -+ off_t fst_addr; -+ struct file *f; -+ ssize_t len; -+ int rc = 0; -+ -+ if (cmsfs.readonly) -+ return -EROFS; -+ -+ /* -+ * If file is opened and modified disk content may be obsolete. -+ * Must use the file object to get the current version of the file. -+ */ -+ f = file_open(path + 1); -+ if (f != NULL) { -+ fst_addr = f->fst_addr; -+ len = f->session_size; -+ } else { -+ fst_addr = lookup_file(path + 1, &fst, HIDE_UNLINKED); -+ if (!fst_addr) -+ return -ENOENT; -+ len = get_file_size_logical(&fst); -+ if (len < 0) -+ return -EIO; -+ f = create_file_object(&fst, &rc); -+ if (f == NULL) -+ return rc; -+ } -+ -+ if (len == size) -+ return 0; -+ if (size < len) { -+ rc = shrink_file(f, size); -+ if (rc != 0) -+ return rc; -+ } else -+ return -EINVAL; -+ -+ rc = set_fst_date_current(f->fst); -+ if (rc != 0) -+ return rc; -+ -+ update_fst(f, fst_addr); -+ if (!f->use_count) -+ destroy_file_object(f); -+ return rc; -+} -+ -+#ifdef HAVE_SETXATTR -+static int cmsfs_setxattr(const char *path, const char *name, const char *value, -+ size_t size, int flags) -+{ -+ struct fst_entry fst; -+ off_t fst_addr; -+ int mode_n, rc; -+ long int rlen; -+ char mode_l; -+ char *in; -+ -+ /* meaningless since our xattrs are virtual and not stored on disk */ -+ (void) flags; -+ -+ if (cmsfs.readonly) -+ return -EROFS; -+ -+ fst_addr = lookup_file(path + 1, &fst, HIDE_UNLINKED); -+ if (!fst_addr) -+ return -ENOENT; -+ -+ if (strcmp(name, xattr_format.name) == 0) { -+ /* only allowed for empty files */ -+ if (fst.nr_records != 0) -+ return -EINVAL; -+ if (size != xattr_format.size) -+ return -ERANGE; -+ if (*value == 'F') { -+ fst.record_format = RECORD_LEN_FIXED; -+ fst.ptr_size = 0x4; -+ } else if (*value == 'V') { -+ fst.record_format = RECORD_LEN_VARIABLE; -+ fst.ptr_size = 0xc; -+ } else -+ return -EINVAL; -+ goto write; -+ } -+ -+ if (strcmp(name, xattr_lrecl.name) == 0) { -+ /* done by fs for variable files */ -+ if (fst.record_format == RECORD_LEN_VARIABLE) -+ return -EINVAL; -+ /* only allowed for empty files */ -+ if (fst.nr_records != 0) -+ return -EINVAL; -+ if (size > xattr_lrecl.size) -+ return -ERANGE; -+ in = calloc(size + 1, 1); -+ if (in == NULL) -+ return -ENOMEM; -+ memcpy(in, value, size); -+ errno = 0; -+ rlen = strtol(in, (char **) NULL, 10); -+ free(in); -+ if (errno != 0 || rlen == 0 || rlen > MAX_RECORD_LEN) -+ return -EINVAL; -+ fst.record_len = rlen; -+ goto write; -+ } -+ -+ if (strcmp(name, xattr_mode.name) == 0) { -+ if (size != xattr_mode.size) -+ return -ERANGE; -+ mode_l = value[0]; -+ if (mode_l < 'A' || mode_l > 'Z') -+ return -EINVAL; -+ mode_n = atoi(&value[1]); -+ if (!isdigit(value[1]) || mode_n < 0 || mode_n > 6) -+ return -EINVAL; -+ ebcdic_enc((char *) &fst.mode, value, sizeof(fst.mode)); -+ goto write; -+ } -+ return -ENODATA; -+ -+write: -+ rc = _write(&fst, sizeof(fst), fst_addr); -+ BUG(rc < 0); -+ return 0; -+} -+ -+static int cmsfs_getxattr(const char *path, const char *name, char *value, -+ size_t size) -+{ -+ char buf[xattr_lrecl.size + 1]; -+ struct fst_entry fst; -+ -+ /* nothing for root directory but clear error code needed */ -+ if (strcmp(path, "/") == 0) -+ return -ENODATA; -+ -+ if (!lookup_file(path + 1, &fst, HIDE_UNLINKED)) -+ return -ENOENT; -+ -+ /* null terminate strings */ -+ memset(value, 0, size); -+ -+ /* format */ -+ if (strcmp(name, xattr_format.name) == 0) { -+ if (size == 0) -+ return xattr_format.size; -+ if (size < xattr_format.size) -+ return -ERANGE; -+ if (fst.record_format == RECORD_LEN_FIXED) -+ *value = 'F'; -+ else if (fst.record_format == RECORD_LEN_VARIABLE) -+ *value = 'V'; -+ return xattr_format.size; -+ } -+ -+ /* lrecl */ -+ if (strcmp(name, xattr_lrecl.name) == 0) { -+ if (size == 0) -+ return xattr_lrecl.size; -+ if (size < xattr_lrecl.size) -+ return -ERANGE; -+ memset(buf, 0, sizeof(buf)); -+ snprintf(buf, sizeof(buf), "%d", fst.record_len); -+ memcpy(value, buf, strlen(buf)); -+ return strlen(buf); -+ } -+ -+ /* mode */ -+ if (strcmp(name, xattr_mode.name) == 0) { -+ if (size == 0) -+ return xattr_mode.size; -+ if (size < xattr_mode.size) -+ return -ERANGE; -+ ebcdic_dec(value, (char *) &fst.mode, 2); -+ return xattr_mode.size; -+ } -+ return -ENODATA; -+} -+ -+static int cmsfs_listxattr(const char *path, char *list, size_t size) -+{ -+ struct fst_entry fst; -+ size_t list_len; -+ int pos = 0; -+ -+ if (!lookup_file(path + 1, &fst, HIDE_UNLINKED)) -+ return -ENOENT; -+ -+ list_len = strlen(xattr_format.name) + 1 + -+ strlen(xattr_lrecl.name) + 1 + -+ strlen(xattr_mode.name); -+ if (!size) -+ return list_len; -+ if (size < list_len) -+ return -ERANGE; -+ -+ strcpy(list, xattr_format.name); -+ pos += strlen(xattr_format.name) + 1; -+ strcpy(&list[pos], xattr_lrecl.name); -+ pos += strlen(xattr_lrecl.name) + 1; -+ strcpy(&list[pos], xattr_mode.name); -+ pos += strlen(xattr_mode.name) + 1; -+ return pos; -+} -+#endif /* HAVE_SETXATTR */ -+ -+/* -+ * Return the number of unused bytes in the last block. -+ */ -+static int examine_last_block(struct file *f) -+{ -+ struct record_ext *rext; -+ struct record *rec; -+ int last_bnr, len; -+ off_t start; -+ -+ if (!f->fst->nr_blocks || !f->fst->nr_records) -+ return 0; -+ -+ last_bnr = f->fst->nr_blocks - 1; -+ rec = &f->rlist[f->fst->nr_records - 1]; -+ -+ /* last block may be an extension */ -+ if (rec->block_nr == last_bnr) { -+ start = rec->disk_start; -+ len = rec->first_block_len; -+ goto out; -+ } -+ -+ /* if write is split and exactly the extension not yet started */ -+ if (rec->ext == NULL) -+ return 0; -+ -+ rext = rec->ext; -+ do { -+ if (rext->block_nr == last_bnr) { -+ start = rext->disk_start; -+ len = rext->len; -+ goto out; -+ } -+ rext = rext->next; -+ } while (rext != NULL); -+ -+ return 0; -+out: -+ if (start == NULL_BLOCK) -+ start = f->null_ctr; -+ return ((start | DATA_BLOCK_MASK) + 1) - (start + len); -+} -+ -+static int get_record_len(struct file *f, size_t size) -+{ -+ int chunk = 0; -+ -+ if (f->fst->record_format == RECORD_LEN_FIXED) { -+ if (!f->fst->record_len) { -+ chunk = (size > MAX_RECORD_LEN) ? -+ MAX_RECORD_LEN : size; -+ f->fst->record_len = chunk; -+ } else -+ chunk = f->fst->record_len; -+ -+ if (chunk > MAX_RECORD_LEN) -+ return -EINVAL; -+ if (size < (size_t) chunk) -+ chunk = size; -+ } else if (f->fst->record_format == RECORD_LEN_VARIABLE) { -+ chunk = size; -+ if (chunk > MAX_RECORD_LEN) -+ chunk = MAX_RECORD_LEN; -+ } -+ return chunk; -+} -+ -+/* -+ * Get the disk address of the first byte after the last record. -+ */ -+static off_t disk_end(struct file *f) -+{ -+ struct record_ext *rext; -+ struct record *rec; -+ -+ if (!f->fst->nr_records) -+ return 0; -+ -+ rec = &f->rlist[f->fst->nr_records - 1]; -+ if (rec->ext == NULL) { -+ if (rec->disk_start == NULL_BLOCK) -+ return 0; -+ else -+ return rec->disk_start + rec->first_block_len; -+ } -+ -+ rext = rec->ext; -+ while (rext->next != NULL) -+ rext = rext->next; -+ if (rext->disk_start == NULL_BLOCK) -+ return 0; -+ else -+ return rext->disk_start + rext->len; -+} -+ -+/* -+ * Store the displacement for the first write to a new block. -+ * nr_blocks already contains the new block. If the block is completely filled -+ * the displacement is spanned, also if one header byte is used -+ * since the header belongs to the record regarding the -+ * displacement but not if both header bytes are on the block. -+ */ -+void store_displacement(struct file *f, int disp) -+{ -+ f->blist[f->fst->nr_blocks - 1].disp = disp; -+ f->vrstate->block_state = BWS_BLOCK_USED; -+} -+ -+/* -+ * Allocate a new block if needed, set write pointer and return the number -+ * of bytes available on the block. -+ */ -+static int write_prepare_block(struct file *f, int null_block, ssize_t size) -+{ -+ int len, new_block = 0; -+ -+ if (null_block) { -+ /* -+ * TODO: need to write header before killing write_ptr for -+ * sparse files. -+ */ -+ BUG(f->fst->record_format == RECORD_LEN_VARIABLE); -+ -+ f->write_ptr = 0; -+ /* new null block started ? */ -+ if (f->null_ctr % cmsfs.blksize == 0) { -+ new_block = 1; -+ len = cmsfs.blksize; -+ -+ /* -+ * Prevent allocating a null block if the block would -+ * not be complete. Use a normal block instead. -+ */ -+ if (size < cmsfs.blksize) { -+ f->write_ptr = get_zero_block(); -+ if (f->write_ptr < 0) -+ return f->write_ptr; -+ } -+ -+ } else -+ len = ((f->null_ctr | DATA_BLOCK_MASK) + 1) - f->null_ctr; -+ } else { -+ if (f->write_ptr % cmsfs.blksize == 0) { -+ /* -+ * For fixed files use a different padding in text -+ * mode to pad records with spaces. -+ */ -+ if (f->fst->record_format == RECORD_LEN_FIXED && -+ f->translate) -+ f->write_ptr = get_filled_block(); -+ else -+ f->write_ptr = get_zero_block(); -+ if (f->write_ptr < 0) -+ return f->write_ptr; -+ new_block = 1; -+ len = cmsfs.blksize; -+ } else -+ len = ((f->write_ptr | DATA_BLOCK_MASK) + 1) - f->write_ptr; -+ } -+ -+ if (new_block) { -+ if (f->fst->record_format == RECORD_LEN_VARIABLE) -+ f->vrstate->block_state = BWS_BLOCK_NEW; -+ f->blist[f->fst->nr_blocks].disk_addr = f->write_ptr; -+ -+ if (!f->write_ptr && f->fst->record_format == RECORD_LEN_FIXED) -+ f->nr_null_blocks++; -+ -+ f->fst->nr_blocks++; -+ } -+ return len; -+} -+ -+/* -+ * Write variable record length header and return number of written bytes. -+ */ -+static int write_var_header(struct file *f, int len, u16 vheader) -+{ -+ u8 half_vheader; -+ int rc; -+ -+ if (f->vrstate->record_state == RWS_HEADER_COMPLETE || -+ f->vrstate->record_state == RWS_RECORD_INCOMPLETE) -+ return 0; -+ -+ if (f->vrstate->record_state == RWS_HEADER_STARTED) { -+ /* write secord header byte */ -+ half_vheader = vheader & 0xff; -+ rc = _write(&half_vheader, 1, f->write_ptr); -+ if (rc < 0) -+ return rc; -+ f->write_ptr++; -+ f->vrstate->record_state = RWS_HEADER_COMPLETE; -+ return 1; -+ } -+ -+ /* block cannot be spanned if a header starts on it */ -+ if (f->vrstate->block_state == BWS_BLOCK_NEW) -+ store_displacement(f, f->write_ptr & DATA_BLOCK_MASK); -+ -+ if (len >= 2) { -+ rc = _write(&vheader, 2, f->write_ptr); -+ if (rc < 0) -+ return rc; -+ f->write_ptr += 2; -+ f->vrstate->rlen = vheader; -+ f->vrstate->record_state = RWS_HEADER_COMPLETE; -+ return 2; -+ } else { -+ /* len = 1, write first header byte */ -+ half_vheader = vheader >> 8; -+ rc = _write(&half_vheader, 1, f->write_ptr); -+ if (rc < 0) -+ return rc; -+ f->write_ptr++; -+ f->vrstate->rlen = vheader; -+ f->vrstate->record_state = RWS_HEADER_STARTED; -+ return 1; -+ } -+} -+ -+static int extend_block_fixed(struct file *f, const char *buf, int len, -+ size_t size, int rlen) -+{ -+ int rc; -+ -+ (void) rlen; -+ -+ if (size < (size_t) len) -+ len = size; -+ if (f->write_ptr) { -+ rc = _write(buf, len, f->write_ptr); -+ if (rc < 0) -+ return rc; -+ f->write_ptr += len; -+ } -+ return len; -+} -+ -+static int extend_block_variable(struct file *f, const char *buf, int len, -+ size_t size, int rlen) -+{ -+ int rc, copied = 0, vh_len = 0, max = cmsfs.blksize; -+ -+ if (!f->write_ptr) -+ return len; -+ -+ while (len > 0) { -+ /* record may be party written already */ -+ if (size < (size_t) rlen) -+ rlen = size; -+ -+ vh_len = write_var_header(f, len, rlen); -+ if (vh_len < 0) -+ return vh_len; -+ len -= vh_len; -+ if (!len) -+ return copied; -+ /* record does not fit on block */ -+ if (len < rlen) -+ rlen = len; -+ /* remaining record data less than block len */ -+ if (f->vrstate->rlen < rlen) -+ rlen = f->vrstate->rlen; -+ rc = _write(buf, rlen, f->write_ptr); -+ if (rc < 0) -+ return rc; -+ -+ f->write_ptr += rlen; -+ -+ if (f->vrstate->block_state == BWS_BLOCK_NEW) { -+ /* -+ * If the second byte of a split header was written -+ * (blocksize - 1) is enough to make the block spanned. -+ */ -+ if (vh_len == 1) -+ max--; -+ if (rlen >= max) -+ store_displacement(f, VAR_RECORD_SPANNED); -+ else -+ store_displacement(f, f->write_ptr & DATA_BLOCK_MASK); -+ } -+ -+ -+ copied += rlen; -+ size -= rlen; -+ len -= rlen; -+ f->vrstate->rlen -= rlen; -+ -+ BUG(f->vrstate->rlen < 0); -+ if (!f->vrstate->rlen) -+ f->vrstate->record_state = RWS_RECORD_COMPLETE; -+ -+ DEBUG("%s: wrote %d record bytes\n", __func__, rlen); -+ if (size <= 0) -+ return copied; -+ } -+ -+ /* record is not yet finished */ -+ f->vrstate->record_state = RWS_RECORD_INCOMPLETE; -+ return copied; -+} -+ -+/* -+ * Extend an existing block or write data on a new block. -+ * -+ * size: requestes bytes to write to disk -+ * rlen: projected record len -+ * len: bytes left on the block -+ */ -+static int extend_block(struct file *f, const char *buf, size_t size, int rlen) -+{ -+ int len = write_prepare_block(f, (buf == NULL) ? 1 : 0, size); -+ -+ if (len < 0) -+ return -ENOSPC; -+ BUG(!len); -+ return f->fops->write_data(f, buf, len, size, rlen); -+} -+ -+/* -+ * Delete the record data from rlist and free extensions. -+ */ -+static void delete_record(struct file *f, int nr) -+{ -+ struct record *rec = &f->rlist[nr]; -+ struct record_ext *tmp, *rext = rec->ext; -+ -+ memset(rec, 0, sizeof(struct record)); -+ while (rext != NULL) { -+ tmp = rext->next; -+ free(rext); -+ rext = tmp; -+ } -+} -+ -+/* -+ * Update records from a start record to the end. The start record is one less -+ * than the previous last record since the previous last record may be -+ * incomplete. -+ */ -+static int update_records(struct file *f, int nrecords) -+{ -+ int i, rc, rnr, bnr = 0, skip = 0; -+ size_t total = 0; -+ -+ rnr = f->fst->nr_records - 1; -+ if (rnr >= 0) { -+ total = f->rlist[rnr].file_start; -+ if (f->linefeed && total) -+ total--; -+ bnr = f->rlist[rnr].block_nr; -+ skip = f->rlist[rnr].disk_start & DATA_BLOCK_MASK; -+ -+ /* skip must point before a variable header */ -+ if (f->fst->record_format == RECORD_LEN_VARIABLE) { -+ if (skip >= VAR_RECORD_HEADER_SIZE) -+ skip -= VAR_RECORD_HEADER_SIZE; -+ else if (skip == 1) { -+ bnr--; -+ skip = cmsfs.blksize - 1; -+ } else if (skip == 0 && f->rlist[rnr].disk_start) { -+ bnr--; -+ skip = cmsfs.blksize - 2; -+ } -+ } -+ delete_record(f, rnr); -+ rnr--; -+ } -+ -+ if (rnr < -1) { -+ rnr = -1; -+ skip = 0; -+ total = 0; -+ bnr = 0; -+ } -+ -+ f->fst->nr_records += nrecords; -+ f->record_scan_state = RSS_DATA_BLOCK_STARTED; -+ for (i = bnr; i < f->fst->nr_blocks; i++) { -+ if (f->fst->record_format == RECORD_LEN_FIXED) -+ cache_fixed_data_block(f, f->blist[i].disk_addr + skip, -+ &bnr, &rnr, &total, skip); -+ else { -+ rc = cache_variable_data_block(f, -+ f->blist[i].disk_addr + skip, -+ &bnr, &rnr, f->blist[i].disp, &total, skip); -+ if (rc < 0) -+ return rc; -+ } -+ skip = 0; -+ } -+ return 0; -+} -+ -+/* -+ * Calculate the number of new records. -+ */ -+static int new_records(struct file *f, size_t size, int rlen) -+{ -+ double tmp = size; -+ int len; -+ -+ if (f->fst->record_format == RECORD_LEN_FIXED) { -+ len = f->fst->record_len; -+ if (f->linefeed) -+ len++; -+ -+ /* need to fill a previously started record first */ -+ if (f->session_size && -+ f->session_size % len) -+ tmp = tmp - (len - (f->session_size % len)); -+ } -+ -+ if (tmp <= 0) -+ return 0; -+ -+ tmp = ceil(tmp / rlen); -+ return (int) tmp; -+} -+ -+/* -+ * Calculate number of new blocks. -+ */ -+static int new_blocks(struct file *f, size_t size, int last, int nrecords) -+{ -+ int last_usable = last; -+ double tmp = size; -+ -+ /* subtract header bytes */ -+ if (last_usable && f->fst->record_format == RECORD_LEN_VARIABLE) { -+ if (last_usable == 1) -+ last_usable = 0; -+ else -+ last_usable -= VAR_RECORD_HEADER_SIZE; -+ } -+ -+ if ((int) size <= last_usable) -+ return 0; -+ -+ if (f->fst->record_format == RECORD_LEN_VARIABLE) -+ tmp += nrecords * VAR_RECORD_HEADER_SIZE; -+ -+ tmp -= last; -+ if (tmp <= 0) -+ return 0; -+ -+ tmp = ceil(tmp / cmsfs.blksize); -+ return (int) tmp; -+} -+ -+/* -+ * Increase record list count. -+ */ -+static void resize_rlist(struct file *f, int new) -+{ -+ if (!new) -+ return; -+ -+ f->rlist = realloc(f->rlist, (f->fst->nr_records + new) * -+ sizeof(struct record)); -+ if (f->rlist == NULL) -+ DIE_PERROR("realloc failed"); -+ memset(&f->rlist[f->fst->nr_records], 0, -+ new * sizeof(struct record)); -+} -+ -+/* -+ * Increase block list count. -+ */ -+static void resize_blist(struct file *f, int new) -+{ -+ if (!new) -+ return; -+ -+ f->blist = realloc(f->blist, (f->fst->nr_blocks + new) * -+ sizeof(struct block)); -+ if (f->blist == NULL) -+ DIE_PERROR("realloc failed"); -+ memset(&f->blist[f->fst->nr_blocks], 0, -+ new * sizeof(struct block)); -+} -+ -+/* -+ * Reserve blocks for meta data (pointer blocks) of a file so that -+ * blocks for meta-data are available if the disk runs full. -+ */ -+static void reserve_meta_blocks(struct file *f, int old, int new, int level) -+{ -+ double nentries, oentries; -+ int newp; -+ -+ if (!new) -+ return; -+ -+ newp = pointers_per_level(f, level, old + new); -+ oentries = pointers_per_level(f, level, old); -+ -+ oentries = ceil(oentries / f->ptr_per_block); -+ nentries = newp; -+ nentries = ceil(nentries / f->ptr_per_block); -+ -+ cmsfs.reserved_blocks += nentries - oentries; -+ -+ if (newp > f->ptr_per_block) -+ reserve_meta_blocks(f, oentries, nentries, ++level); -+} -+ -+/* -+ * Update the max record number in the last pointer block per level. -+ */ -+static int update_last_block_vptr(struct file *f, off_t addr, int level, -+ struct var_ptr *vptr) -+{ -+ int last, rc; -+ -+ if (!level) -+ return 0; -+ level--; -+ -+ /* read offset pointer from the end of the var pointer block */ -+ rc = _read(&last, sizeof(last), addr + cmsfs.blksize - sizeof(last)); -+ if (rc < 0) -+ return rc; -+ -+ if (last % sizeof(*vptr) || last > cmsfs.blksize) -+ return -EIO; -+ rc = _read(vptr, sizeof(*vptr), addr + last); -+ if (rc < 0) -+ return rc; -+ -+ /* update max record number */ -+ vptr->hi_record_nr = f->fst->nr_records; -+ rc = _write(vptr, sizeof(*vptr), addr + last); -+ if (rc < 0) -+ return rc; -+ -+ if (!level) -+ return 0; -+ if (vptr->next == NULL_BLOCK) -+ return 0; -+ return update_last_block_vptr(f, ABS(vptr->next), level, vptr); -+} -+ -+/* -+ * Append records at current file end. If buf is NULL write zero bytes. -+ */ -+static int write_append(struct file *f, const char *buf, size_t size) -+{ -+ int rc, i, nrecords, nblocks, last, len, copied = 0; -+ int rlen = get_record_len(f, size); -+ -+ if (rlen < 0) -+ return rlen; -+ nrecords = new_records(f, size, rlen); -+ -+ /* get last block unused bytes for block count */ -+ last = examine_last_block(f); -+ -+ /* initialize write_ptr once */ -+ f->write_ptr = disk_end(f); -+ -+ nblocks = new_blocks(f, size, last, nrecords); -+ if (nblocks > 0) -+ f->ptr_dirty = 1; -+ -+ resize_rlist(f, nrecords); -+ resize_blist(f, nblocks); -+ if (f->fst->nr_blocks + nblocks > 1) -+ reserve_meta_blocks(f, f->fst->nr_blocks, nblocks, 1); -+ -+ if (f->fst->record_format == RECORD_LEN_VARIABLE) -+ f->vrstate->record_state = RWS_RECORD_COMPLETE; -+ -+ /* first use existing last block */ -+ if (last) { -+ len = extend_block(f, buf, size, rlen); -+ if (len < 0) -+ return len; -+ copied += len; -+ size -= len; -+ if (buf != NULL) -+ buf += len; -+ } -+ -+ for (i = 0; i < nblocks; i++) { -+ len = extend_block(f, buf, size, rlen); -+ if (len < 0) { -+ if (copied > 0) -+ goto out; -+ else -+ return len; -+ } -+ copied += len; -+ size -= len; -+ if (buf != NULL) -+ buf += len; -+ DEBUG("%s: wrote: %d bytes\n", __func__, copied); -+ } -+out: -+ rc = update_records(f, nrecords); -+ if (rc < 0) -+ return rc; -+ if (f->fst->record_format == RECORD_LEN_VARIABLE) -+ update_lrecl_fast(f, rlen); -+ return copied; -+} -+ -+static int do_write(struct file *f, const char *buf, size_t size, off_t offset) -+{ -+ ssize_t len, copied = 0; -+ struct var_ptr vptr; -+ int rc; -+ -+ if (!size) -+ return 0; -+ -+ len = f->session_size; -+ if (f->linefeed) -+ len -= f->fst->nr_records; -+ BUG(len < 0); -+ -+ if (offset < len) -+ return -EINVAL; -+ -+ /* -+ * Writes with null blocks (sparse files) may be prevented by tools -+ * which call lseek instead. Since we don't implement lseek fuse may -+ * call us with an offset after file. We don't support sparse file -+ * writes currently. -+ */ -+ if (offset > len) -+ return -EINVAL; -+ -+ copied = write_append(f, buf, size); -+ if (copied <= 0) -+ return copied; -+ f->session_size += copied; -+ -+ /* add linefeed byte */ -+ if (f->linefeed) -+ f->session_size++; -+ -+ if (f->ptr_dirty) { -+ f->old_levels = f->fst->levels; -+ update_levels(f); -+ -+ if (f->fst->fop) -+ f->fops->delete_pointers(f, f->old_levels, -+ ABS(f->fst->fop)); -+ rc = rewrite_pointers(f); -+ if (rc < 0) -+ return rc; -+ f->ptr_dirty = 0; -+ } else -+ if (f->fst->levels > 0) { -+ rc = update_last_block_vptr(f, ABS(f->fst->fop), -+ f->fst->levels, &vptr); -+ if (rc < 0) -+ return rc; -+ } -+ -+ rc = set_fst_date_current(f->fst); -+ if (rc != 0) -+ return rc; -+ -+ update_fst(f, f->fst_addr); -+ set_fdir_date_current(); -+ update_block_count(); -+ return copied; -+} -+ -+static void cache_write_data(struct file *f, const char *buf, int len) -+{ -+ if (f->wcache_used + len > WCACHE_MAX) -+ len = WCACHE_MAX - f->wcache_used; -+ if (buf == NULL) -+ memset(&f->wcache[f->wcache_used], FILLER_ASCII, len); -+ else -+ memcpy(&f->wcache[f->wcache_used], buf, len); -+ f->wcache_used += len; -+} -+ -+static void purge_wcache(struct file *f) -+{ -+ f->wcache_used = 0; -+ f->wcache_commited = 0; -+} -+ -+/* -+ * Scan for the next newline character and return the number of bytes in -+ * this record. -+ */ -+static ssize_t find_newline(const char *buf, int len) -+{ -+ char *pos; -+ -+ pos = memchr(buf, LINEFEED_ASCII, len); -+ if (pos == NULL) -+ return LINEFEED_NOT_FOUND; -+ else -+ return pos - buf; -+} -+ -+static int cmsfs_write(const char *path, const char *buf, size_t size, -+ off_t offset, struct fuse_file_info *fi) -+{ -+ int scan_len = min(size, MAX_RECORD_LEN + 1); -+ int rc, nl_byte = 1, null_record = 0, pad = 0; -+ struct file *f = get_fobj(fi); -+ ssize_t rsize; -+ -+ (void) path; -+ -+ if (cmsfs.readonly) -+ return -EROFS; -+ -+ if (!f->linefeed) -+ return do_write(f, buf, size, offset); -+ -+ /* remove already comitted bytes */ -+ offset -= f->wcache_used; -+ -+ /* write offset must be at the end of the file */ -+ if (offset + f->null_records + f->pad_bytes != f->session_size) -+ return -EINVAL; -+ -+ if (f->fst->record_format == RECORD_LEN_FIXED && -+ f->fst->record_len) -+ scan_len = min(scan_len, f->fst->record_len + 1); -+ -+ rsize = find_newline(buf, scan_len); -+ BUG(rsize < LINEFEED_NOT_FOUND); -+ -+ if (rsize == LINEFEED_NOT_FOUND) { -+ if (f->wcache_used + scan_len >= WCACHE_MAX) { -+ purge_wcache(f); -+ return -EINVAL; -+ } else { -+ if (f->fst->record_format == RECORD_LEN_FIXED && -+ f->wcache_commited + scan_len >= f->fst->record_len) { -+ purge_wcache(f); -+ return -EINVAL; -+ } -+ cache_write_data(f, buf, scan_len); -+ f->wcache_commited += scan_len; -+ return scan_len; -+ } -+ } -+ -+ cache_write_data(f, buf, rsize); -+ -+ if (f->fst->record_format == RECORD_LEN_FIXED && -+ f->wcache_used < f->fst->record_len) { -+ pad = f->fst->record_len - f->wcache_used; -+ cache_write_data(f, NULL, pad); -+ } -+ -+ /* translate */ -+ rc = convert_text(cmsfs.iconv_to, f->wcache, f->wcache_used); -+ if (rc < 0) -+ return rc; -+ -+ /* -+ * Note: empty records are forbidden by design. CMS converts -+ * an empty record to a single space. Follow that convention. -+ */ -+ if (!f->wcache_used) { -+ *f->wcache = FILLER_EBCDIC; -+ f->wcache_used = 1; -+ nl_byte = 0; -+ null_record = 1; -+ } -+ -+ /* correct file offset by removing the virtual linefeeds */ -+ offset -= (f->fst->nr_records - f->null_records); -+ offset += f->pad_bytes; -+ BUG(offset < 0); -+ -+ rc = do_write(f, f->wcache, f->wcache_used, offset); -+ if (rc < 0) -+ return rc; -+ -+ rc += nl_byte; -+ if (null_record) -+ f->null_records++; -+ rc -= f->wcache_commited; -+ rc -= pad; -+ BUG(rc < 0); -+ purge_wcache(f); -+ f->pad_bytes += pad; -+ return rc; -+} -+ -+/* -+ * Get the address of the last directory entry. -+ */ -+off_t find_last_fdir_entry(off_t addr, int level) -+{ -+ struct fst_entry fst; -+ int left, rc; -+ off_t ptr; -+ -+ if (level > 0) { -+ level--; -+ left = PTRS_PER_BLOCK; -+ while (left--) { -+ ptr = get_fixed_pointer(addr + left * PTR_SIZE); -+ BUG(ptr < 0); -+ if (ptr) -+ return find_last_fdir_entry(ptr, level); -+ } -+ DIE("Directory entry not found\n"); -+ return 0; -+ } -+ -+ left = cmsfs.blksize / sizeof(struct fst_entry); -+ while (left--) { -+ rc = _read(&fst, sizeof(fst), -+ addr + left * sizeof(struct fst_entry)); -+ BUG(rc < 0); -+ if (is_file((unsigned long long *) fst.name, -+ (unsigned long long *) fst.type)) -+ return addr + left * sizeof(struct fst_entry); -+ } -+ DIE("Directory entry not found\n"); -+} -+ -+static int delete_file(const char *path) -+{ -+ off_t fst_kill, fst_last; -+ struct walk_file walk; -+ struct file *f_moved; -+ char file[MAX_FNAME]; -+ struct fst_entry fst; -+ struct file *f; -+ int rc = 0, i; -+ -+ if (cmsfs.readonly) -+ return -EROFS; -+ -+ fst_kill = lookup_file(path + 1, &fst, SHOW_UNLINKED); -+ if (!fst_kill) -+ return -ENOENT; -+ f = create_file_object(&fst, &rc); -+ if (f == NULL) -+ return rc; -+ -+ /* delete all data blocks */ -+ for (i = 0; i < f->fst->nr_blocks; i++) -+ free_block(f->blist[i].disk_addr); -+ -+ if (f->fst->fop) { -+ rc = f->fops->delete_pointers(f, f->fst->levels, ABS(f->fst->fop)); -+ if (rc < 0) -+ goto error; -+ } -+ -+ if (cmsfs.dir_levels) -+ fst_last = find_last_fdir_entry(get_fop(cmsfs.fdir), cmsfs.dir_levels); -+ else -+ fst_last = find_last_fdir_entry(cmsfs.fdir, cmsfs.dir_levels); -+ -+ /* remove unlinked file from fcache */ -+ strncpy(file, path + 1, MAX_FNAME); -+ str_toupper(file); -+ invalidate_htab_entry(file); -+ -+ if (fst_last == fst_kill) -+ goto skip_copy; -+ -+ /* copy last entry over unlinked entry */ -+ rc = _read(&fst, sizeof(struct fst_entry), fst_last); -+ BUG(rc < 0); -+ rc = _write(&fst, sizeof(struct fst_entry), fst_kill); -+ BUG(rc < 0); -+ -+ /* update moved fcache entry */ -+ memset(file, 0, sizeof(file)); -+ decode_edf_name(file, fst.name, fst.type); -+ update_htab_entry(fst_kill, file); -+ /* update cached address of moved FST */ -+ f_moved = file_open(file); -+ if (f_moved != NULL) -+ f->fst_addr = fst_kill; -+ -+skip_copy: -+ /* delete last entry */ -+ rc = _zero(fst_last, sizeof(struct fst_entry)); -+ BUG(rc < 0); -+ -+ /* if the deleted entry was the first of a block, free the block */ -+ if (fst_last % cmsfs.blksize == 0) { -+ cache_dblocks(&walk); -+ /* delete the last block from dlist */ -+ walk.dlist_used--; -+ free_block(fst_last); -+ purge_dblock_ptrs(cmsfs.dir_levels, get_fop(cmsfs.fdir)); -+ rewrite_dblock_ptrs(&walk); -+ free_dblocks(&walk); -+ } -+ -+ destroy_file_object(f); -+ decrease_file_count(); -+ update_block_count(); -+ return 0; -+ -+error: -+ destroy_file_object(f); -+ return rc; -+} -+ -+static int cmsfs_unlink(const char *path) -+{ -+ struct fst_entry fst; -+ off_t fst_addr; -+ struct file *f; -+ -+ if (cmsfs.readonly) -+ return -EROFS; -+ -+ fst_addr = lookup_file(path + 1, &fst, HIDE_UNLINKED); -+ if (!fst_addr) -+ return -ENOENT; -+ -+ f = file_open(path + 1); -+ if (f != NULL) { -+ f->unlinked = 1; -+ return 0; -+ } -+ return delete_file(path); -+} -+ -+static int flush_wcache(struct file *f) -+{ -+ off_t offset = f->session_size; -+ int rc; -+ -+ /* translate */ -+ rc = convert_text(cmsfs.iconv_to, f->wcache, f->wcache_used); -+ if (rc < 0) -+ return rc; -+ -+ /* correct file offset by removing the virtual linefeeds */ -+ offset -= (f->fst->nr_records - f->null_records); -+ BUG(offset < 0); -+ -+ rc = do_write(f, f->wcache, f->wcache_used, offset); -+ purge_wcache(f); -+ f->null_records = 0; -+ if (rc < 0) -+ return rc; -+ return 0; -+} -+ -+static int cmsfs_release(const char *path, struct fuse_file_info *fi) -+{ -+ struct file *f = get_fobj(fi); -+ int rc = 0; -+ -+ (void) path; -+ -+ if (f == NULL) { -+ DEBUG("release internal error\n"); -+ return -EINVAL; -+ } -+ -+ if (fi->flags & O_RDWR || fi->flags & O_WRONLY) { -+ f->write_count--; -+ if (f->wcache_used) -+ rc = flush_wcache(f); -+ } -+ -+ if (f->use_count == 1) { -+ if (f->unlinked) -+ delete_file(f->path); -+ list_del(&f->list); -+ destroy_file_object(f); -+ } else -+ f->use_count--; -+ -+ fi->fh = 0; -+ return rc; -+} -+ -+static void init_fops(struct file *f) -+{ -+ if (f->fst->record_format == RECORD_LEN_FIXED) -+ f->fops = &fops_fixed; -+ else -+ f->fops = &fops_variable; -+} -+ -+/* -+ * Create a file object to cache all needed file data. -+ * Note: the caller must ensure that the file exists. -+ */ -+static struct file *create_file_object(struct fst_entry *fst, int *rc) -+{ -+ struct file *f; -+ -+ f = malloc(sizeof(*f)); -+ if (f == NULL) -+ goto oom; -+ memset(f, 0, sizeof(*f)); -+ -+ f->fst = malloc(sizeof(struct fst_entry)); -+ if (f->fst == NULL) -+ goto oom_f; -+ -+ memcpy(f->fst, fst, sizeof(*fst)); -+ workaround_nr_blocks(f); -+ init_fops(f); -+ -+ f->linefeed = linefeed_mode_enabled(f->fst); -+ f->translate = f->linefeed; -+ -+ f->record_scan_state = RSS_DATA_BLOCK_STARTED; -+ -+ if (f->fst->record_format == RECORD_LEN_FIXED) -+ f->ptr_per_block = cmsfs.fixed_ptrs_per_block; -+ else -+ f->ptr_per_block = cmsfs.var_ptrs_per_block; -+ -+ f->vrstate = malloc(sizeof(*f->vrstate)); -+ if (f->vrstate == NULL) -+ goto oom_fst; -+ memset(f->vrstate, 0, sizeof(*f->vrstate)); -+ -+ /* -+ * Prevent calloc for zero records since it returns a pointer != NULL -+ * which causes trouble at free. Also don't call cache_file. -+ */ -+ if (f->fst->nr_records == 0) -+ return f; -+ -+ f->rlist = calloc(f->fst->nr_records, sizeof(struct record)); -+ if (f->rlist == NULL) -+ goto oom_vrstate; -+ -+ f->blist = calloc(f->fst->nr_blocks, sizeof(struct block)); -+ if (f->blist == NULL) -+ goto oom_rlist; -+ -+ *rc = cache_file(f); -+ if (*rc < 0) -+ goto error; -+ return f; -+ -+error: -+ if (*rc == 0) -+ *rc = -ENOMEM; -+oom_rlist: -+ free(f->rlist); -+oom_vrstate: -+ free(f->vrstate); -+oom_fst: -+ free(f->fst); -+oom_f: -+ free(f); -+oom: -+ return NULL; -+} -+ -+static void destroy_file_object(struct file *f) -+{ -+ struct record_ext *rext, *tmp; -+ struct record *rec; -+ int i; -+ -+ free(f->wcache); -+ free(f->vrstate); -+ -+ for (i = 0; i < f->fst->nr_records; i++) { -+ rec = &f->rlist[i]; -+ rext = rec->ext; -+ while (rext != NULL) { -+ tmp = rext->next; -+ free(rext); -+ rext = tmp; -+ } -+ } -+ -+ free(f->rlist); -+ free(f->blist); -+ free(f->fst); -+ free(f); -+} -+ -+static struct file_operations fops_fixed = { -+ .cache_data = cache_file_fixed, -+ .write_data = extend_block_fixed, -+ .delete_pointers = purge_pointer_block_fixed, -+ .write_pointers = rewrite_pointer_block_fixed, -+}; -+ -+static struct file_operations fops_variable = { -+ .cache_data = cache_file_variable, -+ .write_data = extend_block_variable, -+ .delete_pointers = purge_pointer_block_variable, -+ .write_pointers = rewrite_pointer_block_variable, -+}; -+ -+static struct fuse_operations cmsfs_oper = { -+ .getattr = cmsfs_getattr, -+ .statfs = cmsfs_statfs, -+ .readdir = cmsfs_readdir, -+ .open = cmsfs_open, -+ .release = cmsfs_release, -+ .read = cmsfs_read, -+ .utimens = cmsfs_utimens, -+ .rename = cmsfs_rename, -+ .fsync = cmsfs_fsync, -+ .truncate = cmsfs_truncate, -+ .create = cmsfs_create, -+ .write = cmsfs_write, -+ .unlink = cmsfs_unlink, -+#ifdef HAVE_SETXATTR -+ .listxattr = cmsfs_listxattr, -+ .getxattr = cmsfs_getxattr, -+ .setxattr = cmsfs_setxattr, -+ /* no removexattr since our xattrs are virtual */ -+#endif -+}; -+ -+static int cmsfs_fuse_main(struct fuse_args *args, -+ struct fuse_operations *cmsfs_oper) -+{ -+#if FUSE_VERSION >= 26 -+ return fuse_main(args->argc, args->argv, cmsfs_oper, NULL); -+#else -+ return fuse_main(args->argc, args->argv, cmsfs_oper); -+#endif -+} -+ -+static int cmsfs_process_args(void *data, const char *arg, int key, -+ struct fuse_args *outargs) -+{ -+ (void) data; -+ -+ switch (key) { -+ case FUSE_OPT_KEY_OPT: -+ if (strcmp(arg, "allow_other") == 0) -+ cmsfs.allow_other = 1; -+ return 1; -+ case FUSE_OPT_KEY_NONOPT: -+ if (cmsfs.device == NULL) { -+ cmsfs.device = strdup(arg); -+ return 0; -+ } -+ return 1; -+ case KEY_HELP: -+ usage(outargs->argv[0]); -+ fuse_opt_add_arg(outargs, "-ho"); -+ cmsfs_fuse_main(outargs, &cmsfs_oper); -+ exit(0); -+ case KEY_VERSION: -+ fprintf(stderr, COMP "FUSE file system for CMS disks " -+ "program version %s\n", RELEASE_STRING); -+ fprintf(stderr, "Copyright IBM Corp. 2010\n"); -+ fuse_opt_add_arg(outargs, "--version"); -+ exit(0); -+ -+ default: -+ DIE("Process arguments error\n"); -+ } -+} -+ -+static void map_device(int fd) -+{ -+ int prot; -+ -+ /* fstat on block device says st_size = 0... */ -+ lseek(fd, 0, SEEK_SET); -+ cmsfs.size = lseek(fd, 0, SEEK_END); -+ DEBUG("mmap size: %lu", cmsfs.size); -+ -+ /* map the whole block device for speeding-up access */ -+ if (cmsfs.readonly) -+ prot = PROT_READ; -+ else -+ prot = PROT_READ | PROT_WRITE; -+ cmsfs.map = mmap(NULL, cmsfs.size, prot, MAP_SHARED, fd, 0); -+ if (cmsfs.map == MAP_FAILED) -+ DIE_PERROR("mmap failed"); -+ DEBUG(" addr: %p read-only: %d\n", cmsfs.map, cmsfs.readonly); -+} -+ -+static void cmsfs_init(int fd) -+{ -+ map_device(fd); -+ -+ /* calculate blocksize dependent values */ -+ cmsfs.data_block_mask = cmsfs.blksize - 1; -+ cmsfs.nr_blocks_512 = cmsfs.blksize / 512; -+ -+ cmsfs.fixed_ptrs_per_block = cmsfs.blksize / sizeof(struct fixed_ptr); -+ cmsfs.var_ptrs_per_block = cmsfs.blksize / sizeof(struct var_ptr); -+ -+ cmsfs.bits_per_data_block = get_order(cmsfs.blksize); -+ -+ /* store directory information */ -+ cmsfs.dir_levels = get_levels(cmsfs.fdir); -+ cmsfs.files = get_files_count(cmsfs.fdir); -+ -+ /* alloc cache entries for all files */ -+ cmsfs.fcache_max = max_cache_entries(); -+ cmsfs.fcache = calloc(cmsfs.fcache_max, sizeof(struct fcache_entry)); -+ -+ cmsfs.amap = get_fop(cmsfs.fdir + sizeof(struct fst_entry)); -+ cmsfs.amap_levels = get_levels(cmsfs.fdir + sizeof(struct fst_entry)); -+ cmsfs.amap_bytes_per_block = cmsfs.blksize * 8 * cmsfs.blksize; -+ -+ if (!hcreate_r(cmsfs.fcache_max, &cmsfs.htab)) -+ DIE("hcreate failed\n"); -+ -+ list_init(&text_type_list); -+ scan_conf_file(&text_type_list); -+ list_init(&open_file_list); -+} -+ -+int main(int argc, char *argv[]) -+{ -+ struct fuse_args args = FUSE_ARGS_INIT(argc, argv); -+ char *fsname; -+ int rc, fd; -+ -+#ifdef DEBUG_ENABLED -+ logfile = fopen(DEBUG_LOGFILE, "w"); -+ if (logfile == NULL) -+ DIE_PERROR("Cannot open file " DEBUG_LOGFILE " for writing"); -+#endif -+ -+ if (fuse_opt_parse(&args, &cmsfs, cmsfs_opts, -+ cmsfs_process_args) == -1) -+ DIE("Failed to parse option\n"); -+ -+ if (!cmsfs.device) -+ DIE("Missing device\n" -+ "Try '%s --help' for more information\n", argv[0]); -+ -+ DEBUG("using device: %s", cmsfs.device); -+ fd = get_device_info(&cmsfs); -+ DEBUG(" blocksize: %d\n", cmsfs.blksize); -+ -+ fsname = malloc(FSNAME_MAX_LEN); -+ if (fsname == NULL) -+ DIE_PERROR("malloc failed"); -+ -+#if FUSE_VERSION >= 27 -+ snprintf(fsname, FSNAME_MAX_LEN, "-osubtype=cmsfs,fsname=%s", -+ cmsfs.device); -+#else -+ snprintf(fsname, FSNAME_MAX_LEN, "-ofsname=%s", cmsfs.device); -+#endif -+ fuse_opt_add_arg(&args, fsname); -+ free(fsname); -+ -+ cmsfs_init(fd); -+ cmsfs.fd = fd; -+ -+ if (cmsfs.readonly) -+ fuse_opt_add_arg(&args, "-oro"); -+ /* force single threaded mode which requires no locking */ -+ fuse_opt_add_arg(&args, "-s"); -+ /* force immediate file removal */ -+ fuse_opt_add_arg(&args, "-ohard_remove"); -+ -+ if (cmsfs.mode == BINARY_MODE && -+ (cmsfs.codepage_from != NULL || cmsfs.codepage_to != NULL)) -+ DIE("Incompatible options, select -a or -t if using --from or --to\n"); -+ -+ if (cmsfs.mode != BINARY_MODE) { -+ if (cmsfs.codepage_from == NULL) -+ cmsfs.codepage_from = CODEPAGE_EDF; -+ if (cmsfs.codepage_to == NULL) -+ cmsfs.codepage_to = CODEPAGE_LINUX; -+ -+ setup_iconv(&cmsfs.iconv_from, cmsfs.codepage_from, -+ cmsfs.codepage_to); -+ setup_iconv(&cmsfs.iconv_to, cmsfs.codepage_to, -+ cmsfs.codepage_from); -+ } -+ -+ rc = cmsfs_fuse_main(&args, &cmsfs_oper); -+ -+ fuse_opt_free_args(&args); -+#ifdef DEBUG_ENABLED -+ fclose(logfile); -+#endif -+ hdestroy_r(&cmsfs.htab); -+ return rc; -+} -diff --git a/cmsfs-fuse/cmsfs-fuse.h b/cmsfs-fuse/cmsfs-fuse.h -new file mode 100644 -index 0000000..6e7fda6 ---- /dev/null -+++ b/cmsfs-fuse/cmsfs-fuse.h -@@ -0,0 +1,134 @@ -+/* -+ * cmsfs-fuse - CMS EDF filesystem support for Linux -+ * Data structures. -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Jan Glauber -+ */ -+ -+#ifndef _CMSFS_H -+#define _CMSFS_H -+ -+#define _GNU_SOURCE -+#include -+#include -+#include "list.h" -+#include "list.h" -+ -+#define COMP "cmsfs-fuse: " -+extern struct cmsfs cmsfs; -+ -+/* conversion between absolute and relative addresses */ -+#define ABS(x) ((x - 1) * cmsfs.blksize) -+#define REL(x) ((x / cmsfs.blksize) + 1) -+ -+struct fcache_entry { -+ /* filename used as hash key */ -+ char name[18]; -+ /* location of fst entry */ -+ off_t fst_addr; -+ /* filename string address */ -+ char *str; -+}; -+ -+enum cmsfs_mode { -+ BINARY_MODE, -+ TEXT_MODE, -+ TYPE_MODE, -+}; -+ -+/* the per device global struture */ -+struct cmsfs { -+ /* name of the block device, e.g. /dev/dasde */ -+ const char *device; -+ /* global file descriptor of the underlying block device */ -+ int fd; -+ /* start of mmap of the whole block device */ -+ char *map; -+ /* size of the disk */ -+ size_t size; -+ /* formatted blocksize */ -+ int blksize; -+ /* number of 512 byte blocks per block */ -+ int nr_blocks_512; -+ /* disk info */ -+ unsigned int format; -+ /* device is read only */ -+ int readonly; -+ /* access permission for other users */ -+ int allow_other; -+ /* offset to file directory root FST */ -+ off_t fdir; -+ /* offset to allocation map */ -+ off_t amap; -+ /* depth of directories */ -+ int dir_levels; -+ /* depth of allocation maps */ -+ int amap_levels; -+ /* files count on the device */ -+ int files; -+ /* conversion mode */ -+ enum cmsfs_mode mode; -+ /* iconv codepage options */ -+ const char *codepage_from; -+ const char *codepage_to; -+ iconv_t iconv_from; -+ iconv_t iconv_to; -+ -+ /* disk stats */ -+ int total_blocks; -+ int used_blocks; -+ /* blocks reserved for outstanding meta data */ -+ int reserved_blocks; -+ -+ /* constants */ -+ int fixed_ptrs_per_block; -+ int var_ptrs_per_block; -+ int bits_per_data_block; -+ int bits_per_ptr_block; -+ int data_block_mask; -+ int amap_bytes_per_block; -+ -+ /* file cache */ -+ struct fcache_entry *fcache; -+ int fcache_used; -+ int fcache_max; -+ struct hsearch_data htab; -+}; -+#define MAX_TYPE_LEN 9 -+ -+struct filetype { -+ char name[MAX_TYPE_LEN]; -+ struct list list; -+}; -+ -+ -+#define MAX_TYPE_LEN 9 -+ -+#define NULL_BLOCK 0 -+#define VAR_FILE_END 1 -+ -+#define PTRS_PER_BLOCK (cmsfs.fixed_ptrs_per_block) -+#define VPTRS_PER_BLOCK (cmsfs.var_ptrs_per_block) -+#define DATA_BLOCK_MASK (cmsfs.data_block_mask) -+#define BITS_PER_DATA_BLOCK (cmsfs.bits_per_data_block) -+extern int scan_conf_file(struct list *list); -+extern int is_edf_char(int c); -+#define BYTES_PER_BLOCK (cmsfs.amap_bytes_per_block) -+ -+extern int get_device_info(struct cmsfs *cmsfs); -+extern int scan_conf_file(struct list *list); -+extern int is_edf_char(int c); -+ -+#ifndef _CMSFS_FSCK -+int _read(void *, size_t, off_t); -+int _write(const void *, size_t, off_t); -+int _zero(off_t, size_t); -+off_t get_fixed_pointer(off_t); -+ -+off_t get_free_block(void); -+off_t get_zero_block(void); -+void free_block(off_t); -+#endif -+ -+#endif -diff --git a/cmsfs-fuse/config.c b/cmsfs-fuse/config.c -new file mode 100644 -index 0000000..9f9de45 ---- /dev/null -+++ b/cmsfs-fuse/config.c -@@ -0,0 +1,122 @@ -+/* -+ * cmsfs-fuse - CMS EDF filesystem support for Linux -+ * Config option parsing. -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Jan Glauber -+ */ -+ -+#define _GNU_SOURCE -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "cmsfs-fuse.h" -+#include "helper.h" -+#include "zt_common.h" -+ -+#define MAX_LINE_LEN 80 -+ -+char *conffile; -+ -+int open_conf_file(FILE **fh) -+{ -+ const char *home_env = getenv("HOME"); -+ -+ if (home_env == NULL) -+ goto no_home; -+ -+ conffile = malloc(4096); -+ if (conffile == NULL) -+ DIE_PERROR("malloc failed"); -+ sprintf(conffile, "%s/.cmsfs-fuse/filetypes.conf", home_env); -+ *fh = fopen(conffile, "r"); -+ if (*fh == NULL) -+ goto no_home; -+out: -+ DEBUG("using config file: %s\n", conffile); -+ return 0; -+ -+no_home: -+ sprintf(conffile, "%s/%s", TOOLS_SYSCONFDIR, -+ "/cmsfs-fuse/filetypes.conf"); -+ *fh = fopen(conffile, "r"); -+ if (*fh == NULL) { -+ free(conffile); -+ return -ENOENT; -+ } -+ goto out; -+} -+ -+void add_filetype(char *name, struct list *head) -+{ -+ struct filetype *entry; -+ -+ entry = malloc(sizeof(*entry)); -+ if (entry == NULL) -+ DIE_PERROR("malloc failed"); -+ strncpy(entry->name, name, MAX_TYPE_LEN); -+ list_add(&entry->list, head); -+} -+ -+int filetype_valid(const char *type, int line) -+{ -+ unsigned int i; -+ -+ if (strlen(type) > 8) { -+ WARN("entry too long in line: %d in config file: %s\n", -+ line, conffile); -+ return 0; -+ } -+ -+ for (i = 0; i < strlen(type); i++) -+ if (!is_edf_char(*(type + i))) { -+ WARN("invalid character in line: %d in config file: %s\n", -+ line, conffile); -+ return 0; -+ } -+ -+ return 1; -+} -+ -+int scan_conf_file(struct list *head) -+{ -+ char buf[MAX_LINE_LEN], *tmp; -+ int line = 0; -+ FILE *fh; -+ -+ if (open_conf_file(&fh) < 0) -+ return -ENOENT; -+ -+ while (fgets(buf, MAX_LINE_LEN, fh) != NULL) { -+ line++; -+ tmp = buf; -+ while (isblank(*tmp)) -+ tmp++; -+ -+ if (*tmp == '\n') -+ continue; -+ -+ /* -+ * Skip comments, comment must be "# " because # is a valid -+ * EDF character. -+ */ -+ if (strlen(tmp) > 1 && *tmp == '#' && *(tmp + 1) == ' ') -+ continue; -+ -+ /* remove trailing \n */ -+ if (strlen(tmp) && *(tmp + strlen(tmp) - 1) == '\n') -+ *(tmp + strlen(tmp) - 1) = '\0'; -+ -+ if (filetype_valid(tmp, line)) -+ add_filetype(tmp, head); -+ } -+ fclose(fh); -+ free(conffile); -+ return 0; -+} -diff --git a/cmsfs-fuse/dasd.c b/cmsfs-fuse/dasd.c -new file mode 100644 -index 0000000..d79d34d ---- /dev/null -+++ b/cmsfs-fuse/dasd.c -@@ -0,0 +1,224 @@ -+/* -+ * cmsfs-fuse - CMS EDF filesystem support for Linux -+ * DASD specific functions. -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Jan Glauber -+ */ -+ -+#define _GNU_SOURCE -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "helper.h" -+#include "edf.h" -+#include "cmsfs-fuse.h" -+ -+typedef struct dasd_info { -+ unsigned int devno; /* S/390 devno */ -+ unsigned int real_devno; /* for aliases */ -+ unsigned int schid; /* S/390 subchannel identifier */ -+ unsigned int cu_type :16; /* from SenseID */ -+ unsigned int cu_model :8; /* from SenseID */ -+ unsigned int dev_type :16; /* from SenseID */ -+ unsigned int dev_model :8; /* from SenseID */ -+ unsigned int open_count; -+ unsigned int req_queue_len; -+ unsigned int chanq_len; /* length of chanq */ -+ char type[4]; /* from discipline.name, 'none' for unknown */ -+ unsigned int status; /* current device level */ -+ unsigned int label_block; /* where to find the VOLSER */ -+ unsigned int FBA_layout; /* fixed block size (like AIXVOL) */ -+ unsigned int characteristics_size; -+ unsigned int confdata_size; -+ char characteristics[64]; /* from read_device_characteristics */ -+ char configuration_data[256]; /* from read_configuration_data */ -+} dasd_info_t; -+ -+typedef struct dasd_info2 { -+ unsigned int devno; /* S/390 devno */ -+ unsigned int real_devno; /* for aliases */ -+ unsigned int schid; /* S/390 subchannel identifier */ -+ unsigned int cu_type :16; /* from SenseID */ -+ unsigned int cu_model :8; /* from SenseID */ -+ unsigned int dev_type :16; /* from SenseID */ -+ unsigned int dev_model :8; /* from SenseID */ -+ unsigned int open_count; -+ unsigned int req_queue_len; -+ unsigned int chanq_len; /* length of chanq */ -+ char type[4]; /* from discipline.name, 'none' for unknown */ -+ unsigned int status; /* current device level */ -+ unsigned int label_block; /* where to find the VOLSER */ -+ unsigned int FBA_layout; /* fixed block size (like AIXVOL) */ -+ unsigned int characteristics_size; -+ unsigned int confdata_size; -+ char characteristics[64]; /* from read_device_characteristics */ -+ char configuration_data[256]; /* from read_configuration_data */ -+ unsigned int format; /* format info like formatted/cdl/ldl/... */ -+ unsigned int features; /* dasd features like 'ro',... */ -+ unsigned int reserved[8]; -+} dasd_info2_t; -+ -+#define HDIO_GETGEO 0x0301 -+#define BLKSSZGET _IO(0x12, 104) -+#define BIODASDINFO _IOR('D', 1, dasd_info_t) -+#define BIODASDINFO2 _IOR('D', 3, dasd_info2_t) -+ -+/* CMS disk label starts with ASCII string "CMS1" */ -+#define VOL_LABEL_EBCDIC 0xc3d4e2f1 -+ -+#define DASD_FORMAT_LDL 1 -+ -+static int disk_supported(int fd, struct cmsfs *cmsfs) -+{ -+ unsigned int cms_id = VOL_LABEL_EBCDIC; -+ struct cms_label label; -+ int offset, rc; -+ -+ /* check that this is a ldl disk */ -+ if (cmsfs->format != DASD_FORMAT_LDL) { -+ fprintf(stderr, COMP "Disk not LDL formatted\n"); -+ return 0; -+ } -+ -+ /* label is on block number 3 */ -+ offset = 2 * cmsfs->blksize; -+ -+ rc = lseek(fd, offset, SEEK_SET); -+ if (rc < 0) { -+ perror(COMP "lseek failed"); -+ return 0; -+ } -+ -+ rc = read(fd, &label, sizeof(label)); -+ if (rc < 0) { -+ perror(COMP "read failed"); -+ return 0; -+ } -+ -+ /* check that the label contains the CMS1 string */ -+ if (memcmp(label.id, &cms_id, sizeof(cms_id)) != 0) { -+ fprintf(stderr, COMP "Disk is not a CMS disk\n"); -+ return 0; -+ } -+ -+ DEBUG(" DOP: %d", label.dop); -+ /* block number 5 means 0x4000... */ -+ cmsfs->fdir = (label.dop - 1) * cmsfs->blksize; -+ DEBUG(" fdir: %lx", cmsfs->fdir); -+ /* get disk usage for statfs */ -+ cmsfs->total_blocks = label.total_blocks; -+ cmsfs->used_blocks = label.used_blocks; -+ DEBUG(" Total blocks: %d Used blocks: %d", -+ cmsfs->total_blocks, cmsfs->used_blocks); -+ return 1; -+} -+ -+static void get_device_info_bdev(int fd, struct cmsfs *cmsfs) -+{ -+ struct dasd_info2 *info = NULL; -+ -+ if (ioctl(fd, BLKSSZGET, &cmsfs->blksize) != 0) -+ DIE("ioctl error get blocksize\n"); -+ -+ info = malloc(sizeof(struct dasd_info2)); -+ if (info == NULL) -+ DIE_PERROR("malloc failed"); -+ -+ /* get disk information */ -+ if (ioctl(fd, BIODASDINFO2, info) == 0) { -+ /* INFO2 failed - try INFO using the same (larger) buffer */ -+ if (ioctl(fd, BIODASDINFO, info) != 0) -+ DIE("ioctl dasd info failed\n"); -+ } -+ cmsfs->format = info->format; -+ free(info); -+} -+ -+static int blocksizes[] = { 4096, 512, 2048, 1024 }; -+ -+static void get_device_info_file(int fd, struct cmsfs *cmsfs) -+{ -+ unsigned int cms_id = VOL_LABEL_EBCDIC; -+ unsigned int i; -+ char label[4]; -+ off_t offset; -+ int rc; -+ -+ cmsfs->blksize = 0; -+ -+ /* -+ * Read the blocksize from label. Unfortunately the blocksize -+ * position depends on the blocksize... time for some heuristics. -+ */ -+ for (i = 0; i < ARRAY_SIZE(blocksizes); i++) { -+ offset = blocksizes[i] * 2; -+ -+ rc = lseek(fd, offset, SEEK_SET); -+ if (rc < 0) -+ DIE_PERROR("lseek failed"); -+ -+ rc = read(fd, &label, 4); -+ if (rc < 0) -+ DIE_PERROR("read failed"); -+ -+ /* check if the label contains the CMS1 string */ -+ if (memcmp(label, &cms_id, sizeof(cms_id)) == 0) { -+ cmsfs->blksize = blocksizes[i]; -+ break; -+ } -+ } -+ -+ if (!cmsfs->blksize) -+ DIE("Error detecting blocksize from file!\n"); -+ -+ /* -+ * Hardcoded since the label doesn't contain that info. -+ * Checking the disk identifier must be sufficient. -+ */ -+ cmsfs->format = DASD_FORMAT_LDL; -+} -+ -+int get_device_info(struct cmsfs *cmsfs) -+{ -+ struct stat stat; -+ int fd; -+ -+ /* -+ * Open writable, if write access is not granted fall back to -+ * read only. -+ */ -+ fd = open(cmsfs->device, O_RDWR); -+ if (fd < 0) { -+ if (errno == EROFS) { -+ cmsfs->readonly = 1; -+ fd = open(cmsfs->device, O_RDONLY); -+ if (fd < 0) -+ DIE_PERROR("open failed"); -+ } else -+ DIE_PERROR("open failed"); -+ } -+ -+ if (fstat(fd, &stat) < 0) -+ DIE_PERROR("fstat failed"); -+ -+ if (S_ISBLK(stat.st_mode)) -+ get_device_info_bdev(fd, cmsfs); -+ else if (S_ISREG(stat.st_mode)) -+ get_device_info_file(fd, cmsfs); -+ else -+ goto error; -+ -+ if (!disk_supported(fd, cmsfs)) -+ goto error; -+ return fd; -+ -+error: -+ DIE("Unsupported disk\n"); -+} -diff --git a/cmsfs-fuse/ebcdic.h b/cmsfs-fuse/ebcdic.h -new file mode 100644 -index 0000000..9183f09 ---- /dev/null -+++ b/cmsfs-fuse/ebcdic.h -@@ -0,0 +1,153 @@ -+/* -+ * cmsfs-fuse - CMS EDF filesystem support for Linux -+ * EBCDIC to ASCII conversion. -+ * EDF uses an EBCDIC codepage based on 037 with some modifications. -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Jan Glauber -+ */ -+ -+#ifndef _EBCDIC_H -+#define _EBCDIC_H -+ -+#include -+#include -+ -+/* -+ * EBCDIC 037 -> ISO8859-1 -+ * changes: -+ * 0x5f: 0xaa -> 0x5e ^ -+ * 0xad: 0x07 -> 0x5b [ -+ * 0xbd: 0x07 -> 0x5d ] -+ */ -+ -+static char ebc2asc[256] = { -+/* 0x00 */ -+ 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, -+ 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, -+/* 0x10 */ -+ 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, -+ 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, -+/* 0x20 */ -+ 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, -+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, -+/* 0x30 */ -+ 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, -+ 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, -+/* 0x40 */ -+ 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, -+ 0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, -+/* 0x50 */ -+ 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, -+ 0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0x5E, -+/* 0x60 */ -+ 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, -+ 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, -+/* 0x70 */ -+ 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, -+ 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, -+/* 0x80 */ -+ 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, -+ 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, -+/* 0x90 */ -+ 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, -+ 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, -+/* 0xa0 */ -+ 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, -+ 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x5B, 0x07, 0x07, -+/* 0xb0 */ -+ 0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, -+ 0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x5D, 0x07, 0x07, -+/* 0xc0 */ -+ 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, -+ 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, -+/* 0xd0 */ -+ 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, -+ 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, -+/* 0xe0 */ -+ 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, -+ 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, -+/* 0xf0 */ -+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, -+ 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07 -+}; -+ -+/* ISO8859-1 -> EBCDIC 037 */ -+static char asc2ebc[256] = { -+/* 0x00 */ -+ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, -+ 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, -+/* 0x10 */ -+ 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, -+ 0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F, -+/* 0x20 */ -+ 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, -+ 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, -+/* 0x30 */ -+ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, -+ 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, -+/* 0x40 */ -+ 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, -+ 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, -+/* 0x50 */ -+ 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, -+ 0xE7, 0xE8, 0xE9, 0xBA, 0xE0, 0xBB, 0xB0, 0x6D, -+/* 0x60 */ -+ 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, -+ 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, -+/* 0x70 */ -+ 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, -+ 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, -+/* 0x80 */ -+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, -+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, -+/* 0x90 */ -+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, -+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, -+/* 0xa0 */ -+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, -+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, -+/* 0xb0 */ -+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, -+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, -+/* 0xc0 */ -+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, -+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, -+/* 0xd0 */ -+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, -+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, -+/* 0xe0 */ -+ 0x3F, 0x59, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, -+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, -+/* 0xf0 */ -+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, -+ 0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0xEA, 0x3F, 0xFF -+}; -+ -+#define EBCDIC_ENCODE 0x0 -+#define EBCDIC_DECODE 0x1 -+ -+static inline void a2e(char *dst, const char *src, int len, int to) -+{ -+ char *conv; -+ int i; -+ -+ if (to == EBCDIC_ENCODE) -+ conv = asc2ebc; -+ else -+ conv = ebc2asc; -+ for (i = 0; i < len; i++) -+ dst[i] = conv[(unsigned int)src[i]]; -+} -+ -+static inline void ebcdic_enc(char *dst, const char *src, int len) -+{ -+ a2e(dst, src, len, EBCDIC_ENCODE); -+} -+ -+static inline void ebcdic_dec(char *dst, const char *src, int len) -+{ -+ a2e(dst, src, len, EBCDIC_DECODE); -+} -+ -+#endif -diff --git a/cmsfs-fuse/edf.h b/cmsfs-fuse/edf.h -new file mode 100644 -index 0000000..8c74a4e ---- /dev/null -+++ b/cmsfs-fuse/edf.h -@@ -0,0 +1,123 @@ -+/* -+ * cmsfs-fuse - CMS EDF filesystem support for Linux -+ * EDF and label structures. -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Jan Glauber -+ */ -+ -+#ifndef _EDF_H -+#define _EDF_H -+ -+#include "helper.h" -+ -+/* -+ * File status table entry -+ */ -+struct fst_entry { -+ char name[8]; -+ char type[8]; -+ char res1[8]; -+ -+ short int mode; -+ char res2[4]; -+ -+ char record_format; -+ char flag; -+ int record_len; -+ char res3[4]; -+ -+ int fop; -+ /* number of data blocks (not incl. pointer blocks) */ -+ int nr_blocks; -+ int nr_records; -+ char levels; -+ char ptr_size; -+ char date[6]; -+ char res4[4]; -+}; -+ -+struct cms_label { -+ char id[6]; -+ char user_id[6]; -+ -+ unsigned int blocksize; -+ unsigned int dop; -+ unsigned int f_cylinders; -+ unsigned int max_cylinders; -+ -+ unsigned int total_blocks; -+ unsigned int used_blocks; -+ -+ unsigned int fst_entry_size; -+ unsigned int fst_per_block; -+ -+ char date[6]; -+ unsigned int res1[3]; -+ char res2[8]; -+}; -+ -+#define RECORD_LEN_VARIABLE 0xe5 -+#define RECORD_LEN_FIXED 0xc6 -+ -+/* TODO: correct for fixed? */ -+#define MAX_RECORD_LEN 0xffff -+ -+#define FST_ENTRY_SIZE sizeof(struct fst_entry) -+#define FST_ENTRY_DIR_NAME 0x0000000100000000ULL -+#define FST_ENTRY_DIR_TYPE 0xc4c9d9c5c3e3d6d9ULL /* 'DIRECTOR' */ -+#define FST_ENTRY_ALLOC_NAME 0x0000000200000000ULL -+#define FST_ENTRY_ALLOC_TYPE 0xc1d3d3d6c3d4c1d7ULL /* 'ALLOCMAP' */ -+ -+#define FST_FLAG_CENTURY 0x0008 -+#define FST_FOP_OFFSET 0x28 -+#define FST_LEVEL_OFFSET 0x34 -+ -+#define VAR_RECORD_HEADER_SIZE 0x2 -+#define VAR_RECORD_SPANNED 0xffffffff -+ -+#define PTR_SIZE (sizeof(struct fixed_ptr)) -+#define VPTR_SIZE (sizeof(struct var_ptr)) -+ -+struct fixed_ptr { -+ unsigned int next; -+}; -+ -+struct var_ptr { -+ unsigned int next; -+ int hi_record_nr; -+ unsigned int disp; -+}; -+ -+static inline int is_directory(const char *name, -+ const char *type) -+{ -+ if ((*(unsigned long long *) name) != FST_ENTRY_DIR_NAME) -+ return 0; -+ if ((*(unsigned long long *) type) != FST_ENTRY_DIR_TYPE) -+ return 0; -+ return 1; -+} -+ -+static inline int is_allocmap(const char *name, -+ const char *type) -+{ -+ if ((*(unsigned long long *) name) != FST_ENTRY_ALLOC_NAME) -+ return 0; -+ if ((*(unsigned long long *) type) != FST_ENTRY_ALLOC_TYPE) -+ return 0; -+ return 1; -+} -+ -+static inline int is_file(unsigned long long *name, unsigned long long *type) -+{ -+ if (*name == 0ULL) -+ return 0; -+ -+ /* Assumption: type = 0 is not legal */ -+ if (*type == 0ULL) -+ return 0; -+ return 1; -+} -+ -+#endif -diff --git a/cmsfs-fuse/etc/filetypes.conf b/cmsfs-fuse/etc/filetypes.conf -new file mode 100644 -index 0000000..6de94dc ---- /dev/null -+++ b/cmsfs-fuse/etc/filetypes.conf -@@ -0,0 +1,107 @@ -+# -+# Filetypes that are interpreted as text files. If you want an EBCDIC -+# file translated to ASCII, add the extension here. -+# -+# Comments must include a space after the # -+ -+# Add your extensions here: -+PRM -+CONF -+ -+# The following types were taken from the z/VM TCPIP.DATA file: -+$EXEC -+$REXX -+$XEDIT -+AMS -+AMSERV -+ANN -+ANNOUNCE -+APP -+APPEND -+ASC -+ASCII -+ASM -+ASM3705 -+ASSEMBLE -+AVL -+AVAIL -+A37 -+BASDATA -+BASIC -+BKS -+BKSHELF -+C -+C++ -+CAT -+CATALOG -+CNTRL -+COB -+COBOL -+COPY -+CPP -+DIRECT -+DLCS -+DOCUMENT -+ESERV -+EXC -+EXEC -+FFT -+FOR -+FORM -+FORTRAN -+FREEFORT -+GCS -+GROUP -+H -+HPP -+HTM -+HTML -+H++ -+JOB -+LISTING -+LOG -+LST -+MAC -+MACLIB -+MACRO -+MAK -+MAKE -+ME -+MEMBER -+MEMO -+MODULE -+NAM -+NAMES -+NETLOG -+NONE -+NOT -+NOTE -+NOTEBOOK -+OFS -+OPT -+OPTIONS -+PACKAGE -+PASCAL -+PKG -+PLAS -+PLI -+PLIOPT -+PLS -+PVT -+REXX -+RPG -+SCR -+SCRIPT -+STY -+STYLE -+TEXT -+TEXTXXXX -+TXT -+TXTXXXX -+UPDATE -+UPDT -+VMT -+VSBASIC -+VSBDATA -+XED -+XEDIT -diff --git a/cmsfs-fuse/helper.h b/cmsfs-fuse/helper.h -new file mode 100644 -index 0000000..714b8a0 ---- /dev/null -+++ b/cmsfs-fuse/helper.h -@@ -0,0 +1,54 @@ -+/* -+ * cmsfs-fuse - CMS EDF filesystem support for Linux -+ * Common helper functions. -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Jan Glauber -+ */ -+ -+#ifndef _HELPER_H -+#define _HELPER_H -+ -+extern FILE *logfile; -+#define DEBUG_LOGFILE "/tmp/cmsfs-fuse.log" -+ -+#ifdef DEBUG_ENABLED -+#define DEBUG(...) \ -+ do { \ -+ fprintf(logfile, __VA_ARGS__); \ -+ fflush(logfile); \ -+ } while (0) -+#else -+#define DEBUG(...) -+#endif -+ -+#define DIE(...) \ -+ do { \ -+ fprintf(stderr, COMP __VA_ARGS__); \ -+ exit(1); \ -+ } while (0) -+ -+#define DIE_PERROR(...) \ -+ do { \ -+ perror(COMP __VA_ARGS__); \ -+ exit(1); \ -+ } while (0) -+ -+#define BUG(x) \ -+ if (x) { \ -+ fprintf(stderr, COMP " assert failed at " \ -+ __FILE__ ":%d in %s()\n", __LINE__, __func__); \ -+ exit(1); \ -+ } -+ -+#define WARN(...) \ -+ do { \ -+ fprintf(stderr, COMP "Warning, " __VA_ARGS__); \ -+ } while (0) -+ -+#define min(x, y) ((x) < (y) ? (x) : (y)) -+#define max(x, y) ((x) > (y) ? (x) : (y)) -+ -+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) -+ -+#endif --- -1.7.3.5 - diff --git a/0051-lsmem-chmem-Tools-to-manage-memory-hotplug.patch b/0051-lsmem-chmem-Tools-to-manage-memory-hotplug.patch deleted file mode 100644 index 682c9dc..0000000 --- a/0051-lsmem-chmem-Tools-to-manage-memory-hotplug.patch +++ /dev/null @@ -1,729 +0,0 @@ -From 411a47d37b69a0763d1d7b1e3e132cfab67815cd Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 28 Jan 2011 14:15:39 +0100 -Subject: [PATCH 51/61] lsmem/chmem: Tools to manage memory hotplug - -Summary: lsmem/chmem: Tools to manage memory hotplug. -Description: With lsmem, you can display the online status of all available - memory. With chmem, you can set memory online or offline. ---- - README | 2 + - zconf/Makefile | 17 +++- - zconf/chmem | 325 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - zconf/chmem.8 | 71 ++++++++++++ - zconf/lsmem | 158 +++++++++++++++++++++++++++ - zconf/lsmem.8 | 69 ++++++++++++ - 6 files changed, 639 insertions(+), 3 deletions(-) - create mode 100644 zconf/chmem - create mode 100644 zconf/chmem.8 - create mode 100644 zconf/lsmem - create mode 100644 zconf/lsmem.8 - -diff --git a/README b/README -index 4335b43..dbb1475 100644 ---- a/README -+++ b/README -@@ -112,6 +112,8 @@ s390-tools (1.8.2) - adapters. - - cio_ignore: Query and modify the contents of the CIO device driver - blacklist. -+ - lsmem: Display the online status of the available memory. -+ - chmem: Set hotplug memory online or offline. - - * dumpconf: - Allows to configure the dump device used for system dump in case a kernel -diff --git a/zconf/Makefile b/zconf/Makefile -index 9fe8b42..10f2b87 100644 ---- a/zconf/Makefile -+++ b/zconf/Makefile -@@ -5,14 +5,16 @@ include ../common.mak - - SCRIPTS = lsdasd lstape lscss chccwdev lsqeth lszfcp lschp chchp lszcrypt \ - chzcrypt lsluns cio_ignore znetconf -+USRSBIN_SCRIPTS = lsmem chmem - MANPAGES= lsdasd.8 lstape.8 lscss.8 chccwdev.8 lsqeth.8 lszfcp.8 lschp.8 \ -- chchp.8 lszcrypt.8 chzcrypt.8 lsluns.8 cio_ignore.8 znetconf.8 -+ chchp.8 lszcrypt.8 chzcrypt.8 lsluns.8 cio_ignore.8 znetconf.8 \ -+ chmem.8 lsmem.8 - - all: - - clean: - --install: install-scripts install-manpages -+install: install-scripts install-manpages install-usrsbin-scripts - $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 lsznet.raw $(TOOLS_LIBDIR) - $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 znetcontrolunits \ - $(TOOLS_LIBDIR) -@@ -26,6 +28,15 @@ install-scripts: $(SCRIPTS) - chmod 755 $(BINDIR)/$$i; \ - done - -+install-usrsbin-scripts: $(USRSBIN_SCRIPTS) -+ @for i in $^; do \ -+ cat $$i | \ -+ sed -e 's+%S390_TOOLS_VERSION%+$(S390_TOOLS_RELEASE)+' \ -+ >$(USRSBINDIR)/$$i; \ -+ chown $(OWNER).$(GROUP) $(USRSBINDIR)/$$i; \ -+ chmod 755 $(USRSBINDIR)/$$i; \ -+ done -+ - install-manpages: $(MANPAGES) - @if [ ! -d $(MANDIR) ]; then \ - mkdir -p $(MANDIR)/man8; \ -@@ -38,4 +49,4 @@ install-manpages: $(MANPAGES) - install -o $(OWNER) -g $(GROUP) -m 644 $$i $(MANDIR)/man8; \ - done - --.PHONY: all install clean install-scripts install-manpages -+.PHONY: all install clean install-scripts install-manpages install-usrsbin-scripts -diff --git a/zconf/chmem b/zconf/chmem -new file mode 100644 -index 0000000..bdc25a4 ---- /dev/null -+++ b/zconf/chmem -@@ -0,0 +1,325 @@ -+#!/usr/bin/perl -+############################################################################### -+# chmem - script to show memory hotplug status. -+# -+# Copyright IBM Corp. 2010 -+# Author(s): Gerald Schaefer -+############################################################################### -+ -+use strict; -+use warnings; -+use Getopt::Long qw(:config no_ignore_case no_auto_abbrev); -+use File::Basename; -+ -+my $script_name = fileparse($0); -+my $online = 0; -+my $offline = 0; -+my $memdir = "/sys/devices/system/memory"; -+my $block_size = 0; -+my $total_blocks = 0; -+my $devices = {}; -+my $dev_size; -+my $blocks_per_dev = 0; -+my $devs_per_block = 0; -+my $ret = 0; -+ -+sub chmem_usage() -+{ -+ print <[m|M|g|G]. With m or M, specifies the memory -+size in MB (1024 x 1024 bytes). With g or G, specifies the memory size -+in GB (1024 x 1024 x 1024 bytes). The default unit is MB. -+ -+Specify RANGE in the form 0x-0x as shown in the output of the -+lsmem command. is the hexadecimal address of the first byte and -+is the hexadecimal address of the last byte in the memory range. -+ -+SIZE and RANGE must be aligned to the Linux memory block size, as shown in -+the output of the lsmem command. -+ -+OPTIONS -+ -e, --enable -+ Set the given RANGE or SIZE of memory online. -+ -+ -d, --disable -+ Set the given RANGE or SIZE of memory offline. -+ -+ -h, --help -+ Print a short help text, then exit. -+ -+ -v, --version -+ Print the version number, then exit. -+HERE -+} -+ -+sub chmem_version() -+{ -+ print "$script_name: version %S390_TOOLS_VERSION%\n"; -+ print "Copyright IBM Corp. 2010\n"; -+} -+ -+sub chmem_get_dev_size() -+{ -+ my $i = 0; -+ my $device = 0; -+ my $old_device = 0; -+ -+ while (-d "$memdir/memory$i") { -+ $device = `cat $memdir/memory$i/phys_device`; -+ chomp($device); -+ if ($device > $old_device) { -+ $dev_size = int($dev_size / ($device - $old_device)); -+ last; -+ } -+ $dev_size += $block_size; -+ $i++; -+ } -+} -+ -+sub chmem_online($) -+{ -+ my $block = shift; -+ -+ qx(echo online > $memdir/memory$block/state 2>/dev/null); -+ return $? >> 8; -+} -+ -+sub chmem_offline($) -+{ -+ my $block = shift; -+ -+ qx(echo offline > $memdir/memory$block/state 2>/dev/null); -+ return $? >> 8;; -+} -+ -+sub chmem_read_attr($$$) -+# parameters: state, device, block -+{ -+ my @attributes = qw(state phys_device); -+ foreach (0..1) { -+ $_[$_] = `cat $memdir/memory$_[2]/$attributes[$_]`; -+ chomp($_[$_]); -+ } -+} -+ -+sub chmem_read_devices() -+{ -+ my $i = 0; -+ my $device = 0; -+ my $old_device = 0; -+ my $blocks = 0; -+ my $state; -+ -+ while (-d "$memdir/memory$i") { -+ chmem_read_attr($state, $device, $i); -+ if ($device != $old_device) { -+ $devices->{$old_device}->{'id'} = $old_device; -+ $devices->{$old_device}->{'blocks'} = $blocks; -+ $old_device = $device; -+ $blocks = 0; -+ } -+ if ($state eq "online") { -+ $blocks++; -+ } -+ $i++; -+ } -+ $devices->{$old_device}->{'blocks'} = $blocks; -+ $devices->{$old_device}->{'id'} = $old_device; -+} -+ -+sub chmem_dev_action($$) -+{ -+ my ($dev_id, $blocks) = @_; -+ my ($start_block, $end_block, $tmp_block, $max_blocks); -+ my $state; -+ my $i = 0; -+ my $count = 0; -+ -+ if ($blocks_per_dev > 0) { -+ $start_block = $dev_id * $blocks_per_dev; -+ $end_block = $start_block + $blocks_per_dev - 1; -+ $max_blocks = $blocks_per_dev; -+ } else { -+ $start_block = int($dev_id / $devs_per_block); -+ $end_block = $start_block; -+ $max_blocks = 1; -+ } -+ if ($blocks > $max_blocks) { -+ $blocks = $max_blocks; -+ } -+ while ($count < $blocks && $i < $max_blocks) { -+ $tmp_block = $online ? $start_block + $i : $end_block - $i; -+ $state = `cat $memdir/memory$tmp_block/state`; -+ chomp($state); -+ if ($offline && $state eq "online") { -+ $count++ unless chmem_offline($tmp_block); -+ } -+ if ($online && $state eq "offline") { -+ $count++ unless chmem_online($tmp_block); -+ } -+ $i++; -+ } -+ return $count; -+} -+ -+sub chmem_size($) -+{ -+ my $size = shift; -+ my ($blocks, $dev_blocks, $dev_id); -+ -+ $blocks = int($size / $block_size); -+ if ($online) { -+ foreach my $device (sort {$b->{'blocks'} <=> $a->{'blocks'} || -+ $a->{'id'} <=> $b->{'id'}} -+ values %{$devices}) { -+ $dev_blocks = $device->{'blocks'}; -+ $dev_id = $device->{'id'}; -+ if ($dev_blocks < $blocks_per_dev || $dev_blocks == 0) { -+ $blocks -= chmem_dev_action($dev_id, $blocks); -+ if ($blocks == 0) { -+ last; -+ } -+ } -+ } -+ if ($blocks > 0) { -+ printf(STDERR "chmem: Could only set %lu MB of memory ". -+ "online.\n", $size - $blocks * $block_size); -+ $ret = 1; -+ } -+ } else { -+ foreach my $device (sort {$a->{'blocks'} <=> $b->{'blocks'} || -+ $b->{'id'} <=> $a->{'id'}} -+ values %{$devices}) { -+ $dev_blocks = $device->{'blocks'}; -+ $dev_id = $device->{'id'}; -+ if ($dev_blocks > 0) { -+ $blocks -= chmem_dev_action($dev_id, $blocks); -+ if ($blocks == 0) { -+ last; -+ } -+ } -+ } -+ if ($blocks > 0) { -+ printf(STDERR "chmem: Could only set %lu MB of memory ". -+ "offline.\n", $size - $blocks * $block_size); -+ $ret = 1; -+ } -+ } -+} -+ -+sub chmem_range($$) -+{ -+ my ($start, $end) = @_; -+ my $block = 0; -+ my $state; -+ -+ while ($start < $end && $block < $total_blocks - 1) { -+ $block = int($start / ($block_size << 20)); -+ $state = `cat $memdir/memory$block/state`; -+ chomp($state); -+ if ($online && $state eq "offline") { -+ if (chmem_online($block)) { -+ printf(STDERR "chmem: Could not set ". -+ "0x%016x-0x%016x online\n", $start, -+ $start + ($block_size << 20) - 1); -+ $ret = 1; -+ } -+ } -+ if ($offline && $state eq "online") { -+ if (chmem_offline($block)) { -+ printf(STDERR "chmem: Could not set ". -+ "0x%016x-0x%016x offline\n", $start, -+ $start + ($block_size << 20) - 1); -+ $ret = 1; -+ } -+ } -+ $start += $block_size << 20; -+ } -+} -+ -+sub chmem_check() -+{ -+ unless (-d $memdir) { -+ die "chmem: No memory hotplug interface in sysfs ($memdir).\n"; -+ } -+ $block_size = `cat $memdir/block_size_bytes`; -+ chomp($block_size); -+ if ($block_size =~ /(?:0x)?([[:xdigit:]]+)/) { -+ $block_size = unpack("Q", pack("H16", -+ substr("0" x 16 . $1, -16))); -+ $block_size = $block_size >> 20; -+ } else { -+ die "chmem: Unknown block size format in sysfs.\n"; -+ } -+ if ($online == 0 && $offline == 0) { -+ die "chmem: Please specify one of the options -e or -d.\n"; -+ } -+ if ($online == 1 && $offline == 1) { -+ die "chmem: You cannot specify both options -e and -d.\n"; -+ } -+ -+ while (-d "$memdir/memory$total_blocks") { -+ $total_blocks++; -+ } -+ chmem_get_dev_size(); -+ if ($dev_size >= $block_size) { -+ $blocks_per_dev = int($dev_size / $block_size); -+ } else { -+ $devs_per_block = int($block_size / $dev_size); -+ } -+} -+ -+sub chmem_action() -+{ -+ my ($start, $end, $size, $unit); -+ -+ if (!defined($ARGV[0])) { -+ die "chmem: Missing size or range.\n"; -+ } -+ if ($ARGV[0] =~ /^0x([[:xdigit:]]+)-0x([[:xdigit:]]+)$/) { -+ $start = unpack("Q", pack("H16", substr("0" x 16 . $1, -16))); -+ $end = unpack("Q", pack("H16", substr("0" x 16 . $2, -16))); -+ if ($start % ($block_size << 20) || -+ ($end + 1) % ($block_size << 20)) { -+ die "chmem: Start address and (end address + 1) must ". -+ "be aligned to memory block size ($block_size MB).\n"; -+ } -+ chmem_range($start, $end); -+ } else { -+ if ($ARGV[0] =~ m/^(\d+)([mg]?)$/i) { -+ $size = $1; -+ $unit = $2 || ""; -+ if ($unit =~ /g/i) { -+ $size = $size << 10; -+ } -+ if ($size % $block_size) { -+ die "chmem: Size must be aligned to memory ". -+ "block size ($block_size MB).\n"; -+ } -+ chmem_size($size); -+ } else { -+ printf(STDERR "chmem: Invalid size or range: %s\n", -+ $ARGV[0]); -+ exit 1; -+ } -+ } -+} -+ -+ -+# Main -+unless (GetOptions('v|version' => sub {chmem_version(); exit 0;}, -+ 'h|help' => sub {chmem_usage(); exit 0;}, -+ 'e|enable' => \$online, -+ 'd|disable' => \$offline)) { -+ die "Try '$script_name --help' for more information.\n"; -+}; -+ -+chmem_read_devices(); -+chmem_check(); -+chmem_action(); -+exit $ret; -diff --git a/zconf/chmem.8 b/zconf/chmem.8 -new file mode 100644 -index 0000000..34bea3c ---- /dev/null -+++ b/zconf/chmem.8 -@@ -0,0 +1,71 @@ -+.TH CHMEM 8 "Apr 2010" "s390-tools" -+. -+. -+.SH NAME -+chmem \- set memory online or offline. -+. -+.SH SYNOPSIS -+.B chmem -+.RB OPTIONS -+.RB [SIZE|RANGE] -+. -+. -+.SH DESCRIPTION -+The chmem command sets a particular size or range of memory online or offline. -+. -+.IP "\(hy" 2 -+Specify SIZE as [m|M|g|G]. With m or M, specifies the memory -+size in MB (1024 x 1024 bytes). With g or G, specifies the memory size -+in GB (1024 x 1024 x 1024 bytes). The default unit is MB. -+. -+.IP "\(hy" 2 -+Specify RANGE in the form 0x-0x as shown in the output of the -+lsmem command. is the hexadecimal address of the first byte and -+is the hexadecimal address of the last byte in the memory range. -+. -+.PP -+SIZE and RANGE must be aligned to the Linux memory block size, as shown in -+the output of the lsmem command. -+ -+Setting memory online can fail if the hypervisor does not have enough memory -+left, for example because memory was overcommitted. Setting memory offline can -+fail if Linux cannot free the memory. If only part of the requested memory can -+be set online or offline, a message tells you how much memory was set online -+or offline instead of the requested amount. -+. -+. -+.SH OPTIONS -+.TP -+.BR \-h ", " \-\-help -+Print a short help text, then exit. -+. -+.TP -+.BR \-v ", " \-\-version -+Print the version number, then exit. -+. -+.TP -+.BR \-e ", " \-\-enable -+Set the given RANGE or SIZE of memory online. -+. -+.TP -+.BR \-d ", " \-\-disable -+Set the given RANGE or SIZE of memory offline. -+. -+. -+.SH EXAMPLES -+.TP -+.B chmem --enable 1024 -+This command requests 1024 MB of memory to be set online. -+. -+.TP -+.B chmem -e 2g -+This command requests 2 GB of memory to be set online. -+. -+.TP -+.B chmem --disable 0x00000000e4000000-0x00000000f3ffffff -+This command requests the memory range starting with 0x00000000e4000000 -+and ending with 0x00000000f3ffffff to be set offline. -+. -+. -+.SH SEE ALSO -+.BR lsmem (8) -diff --git a/zconf/lsmem b/zconf/lsmem -new file mode 100644 -index 0000000..e6ed1fa ---- /dev/null -+++ b/zconf/lsmem -@@ -0,0 +1,158 @@ -+#!/usr/bin/perl -+############################################################################### -+# lsmem - script to show memory hotplug status. -+# -+# Copyright IBM Corp. 2010 -+# Author(s): Gerald Schaefer -+############################################################################### -+ -+use strict; -+use warnings; -+use Getopt::Long qw(:config no_ignore_case no_auto_abbrev); -+use File::Basename; -+ -+my $script_name = fileparse($0); -+my $memdir = "/sys/devices/system/memory"; -+my $block_size = 0; -+my $list_all = 0; -+my $dev_size = 0; -+ -+ -+sub lsmem_read_attr($$$$) -+# parameters: state, rem, device, block_nr -+{ -+ my @attributes = qw(state removable phys_device); -+ foreach (0..2) { -+ $_[$_] = `cat $memdir/memory$_[3]/$attributes[$_]`; -+ chomp($_[$_]); -+ } -+} -+ -+sub lsmem_get_dev_size() -+{ -+ my $i = 0; -+ my ($device, $old_device) = (0, 0); -+ -+ while (-d "$memdir/memory$i") { -+ $device = `cat $memdir/memory$i/phys_device`; -+ chomp($device); -+ if ($device > $old_device) { -+ $dev_size = int($dev_size / ($device - $old_device)); -+ last; -+ } -+ $dev_size += $block_size; -+ $i++; -+ } -+} -+ -+sub lsmem_list() -+{ -+ my $i = 0; -+ my ($start, $end, $size) = (0, 0, 0); -+ my ($state, $old_state) = (0, 0); -+ my ($rem, $old_rem) = (0, 0); -+ my ($device, $old_device) = (0, 0); -+ my ($mem_online, $mem_offline) = (0, 0); -+ my ($last_block, $end_dev) = (0, 0); -+ -+ if (-d "$memdir/memory0") { -+ lsmem_read_attr($old_state, $old_rem, $old_device, 0); -+ } else { -+ die "lsmem: No memory hotplug interface in sysfs ($memdir).\n"; -+ } -+ -+ $block_size = `cat $memdir/block_size_bytes`; -+ chomp($block_size); -+ if ($block_size =~ /(?:0x)?([[:xdigit:]]+)/) { -+ $block_size = unpack("Q", pack("H16", -+ substr("0" x 16 . $1, -16))); -+ $block_size = $block_size >> 20; -+ } else { -+ die "lsmem: Unknown block size format in sysfs.\n"; -+ } -+ lsmem_get_dev_size(); -+ -+ print <> 20; -+ if ($old_state eq "going-offline") { -+ $old_state = "on->off"; -+ } -+ printf("0x%016x-0x%016x %10lu %-7s ", $start, $end, -+ $size, $old_state); -+ if ($old_state eq "online") { -+ printf(" %-9s ", $old_rem ? "yes" : "no"); -+ $mem_online += $size; -+ } else { -+ printf(" %-9s ", "-"); -+ $mem_offline += $size; -+ } -+ $end_dev = ($end / $dev_size) >> 20; -+ if ($old_device == $end_dev) { -+ printf("%d\n", $old_device); -+ } else { -+ printf("%d-%d\n", $old_device, $end_dev); -+ } -+ $old_state = $state; -+ $old_rem = $rem; -+ $old_device = $device; -+ $start = $end + 1; -+ } -+ } -+ printf("\n"); -+ printf("Memory device size : %lu MB\n", $dev_size); -+ printf("Memory block size : %lu MB\n", $block_size); -+ printf("Total online memory : %lu MB\n", $mem_online); -+ printf("Total offline memory: %lu MB\n", $mem_offline); -+} -+ -+sub lsmem_usage() -+{ -+ print < sub {lsmem_version(); exit 0;}, -+ 'h|help' => sub {lsmem_usage(); exit 0;}, -+ 'a|all' => \$list_all)) { -+ die "Try '$script_name --help' for more information.\n"; -+}; -+ -+lsmem_list(); -diff --git a/zconf/lsmem.8 b/zconf/lsmem.8 -new file mode 100644 -index 0000000..ed052ea ---- /dev/null -+++ b/zconf/lsmem.8 -@@ -0,0 +1,69 @@ -+.TH LSMEM 8 "Apr 2010" s390\-tools -+. -+. -+.SH NAME -+lsmem \- list the ranges of available memory with their online status. -+. -+. -+.SH SYNOPSIS -+.B lsmem -+.RB [OPTIONS] -+. -+. -+.SH DESCRIPTION -+The lsmem command lists the ranges of available memory with their online -+status. The listed memory blocks correspond to the memory block representation -+in sysfs. The command also shows the memory block size, the device size, and -+the amount of memory in online and offline state. -+. -+.SS "Column description" -+. -+.TP 4 -+Address Range -+Start and end address of the memory range. -+. -+.TP 4 -+Size -+Size of the memory range in MB (1024 x 1024 bytes). -+. -+.TP 4 -+State -+Indication of the online status of the memory range. State on->off means -+that the address range is in transition from online to offline. -+. -+.TP 4 -+Removable -+"yes" if the memory range can be set offline, "no" if it cannot be set offline. -+A dash ("\-") means that the range is already offline. -+. -+.TP 4 -+Device -+Device number or numbers that correspond to the memory range. -+ -+Each device represents a memory unit for the hypervisor in control of the -+memory. The hypervisor cannot reuse a memory unit unless the corresponding -+memory range is completely offline. For best memory utilization, each device -+should either be completely online or completely offline. -+ -+The chmem command with the size parameter automatically chooses the best suited -+device or devices when setting memory online or offline. The device size depends -+on the hypervisor and on the amount of total online and offline memory. -+. -+. -+.SH OPTIONS -+.TP -+.BR \-a ", " \-\-all -+List each individual memory block, instead of combining memory blocks with -+similar attributes. -+. -+.TP -+.BR \-h ", " \-\-help -+Print a short help text, then exit. -+. -+.TP -+.BR \-v ", " \-\-version -+Print the version number, then exit. -+. -+. -+.SH SEE ALSO -+.BR chmem (8) --- -1.7.3.5 - diff --git a/0052-dumpconf-Prevent-re-IPL-loop-for-dump-on-panic.patch b/0052-dumpconf-Prevent-re-IPL-loop-for-dump-on-panic.patch deleted file mode 100644 index 71d7f16..0000000 --- a/0052-dumpconf-Prevent-re-IPL-loop-for-dump-on-panic.patch +++ /dev/null @@ -1,562 +0,0 @@ -From 9d93b66b6eda5f3dbaf6804663af21927c3aab8f Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 28 Jan 2011 14:17:36 +0100 -Subject: [PATCH 52/61] dumpconf: Prevent re-IPL loop for dump on panic - -Summary: dumpconf: Prevent re-IPL loop for dump on panic. -Description: A new keyword DELAY_MINUTES is introduced in the dumpconf. - configuration file. Using this keyword the activation of dumpconf - can be delayed in order to prevent potential re-IPL loops. ---- - etc/init.d/dumpconf | 271 ++++++++++++++++++++++++++++++++++-------------- - etc/sysconfig/dumpconf | 10 ++- - 2 files changed, 202 insertions(+), 79 deletions(-) - -diff --git a/etc/init.d/dumpconf b/etc/init.d/dumpconf -index 1dd898d..27f52e4 100755 ---- a/etc/init.d/dumpconf -+++ b/etc/init.d/dumpconf -@@ -15,25 +15,48 @@ - # chkconfig: 0123456 01 99 - - DUMP_CONFIG_FILE=/etc/sysconfig/dumpconf -+CMDFULL=$0 -+CMD="dumpconf" -+LOCKFILE=/var/lock/subsys/$CMD -+PIDFILE=/var/run/$CMD.pid - ERRMSG="Check $DUMP_CONFIG_FILE!" - - RETVAL=0 -+BACKGROUND=0 -+ -+pr_info() -+{ -+ if [ $BACKGROUND -eq 0 ]; then -+ echo "$@" -+ else -+ echo "$@" | logger -t dumpconf -+ fi -+} -+ -+pr_error() -+{ -+ if [ $BACKGROUND -eq 0 ]; then -+ echo "$@" >&2 -+ else -+ echo "$@" | logger -t dumpconf -+ fi -+} - - check_environment() - { - if [ ! -f $DUMP_CONFIG_FILE ]; then -- echo "no config file found: $DUMP_CONFIG_FILE" -+ pr_error "no config file found: $DUMP_CONFIG_FILE" - exit 1 - fi - - if [ "$(cat /proc/filesystems|grep sysfs)" = "" ]; then -- echo "no sysfs found" >&2 -+ pr_error "no sysfs found" - exit 1 - fi - - SYSFSDIR=$(cat /proc/mounts|awk '$3=="sysfs"{print $2; exit}') - if [ "$SYSFSDIR" = "" ]; then -- echo "sysfs not mounted" >&2 -+ pr_error "sysfs not mounted" - exit 1 - fi - -@@ -41,12 +64,12 @@ check_environment() - ON_PANIC_CONFIG_FILE=/$SYSFSDIR/firmware/shutdown_act\ - ions/on_panic - if [ ! -d $DUMP_CONFIG_DIR ]; then -- echo "kernel has no dump on panic support" -+ pr_info "kernel has no dump on panic support" - exit 0 - fi - REIPL_CONFIG_DIR=/$SYSFSDIR/firmware/reipl - if [ ! -d $REIPL_CONFIG_DIR ]; then -- echo "kernel has no dump on panic support" -+ pr_info "kernel has no dump on panic support" - exit 0 - fi - VMCMD_CONFIG_DIR=/$SYSFSDIR/firmware/vmcmd -@@ -90,6 +113,43 @@ Try 'dumpconf --help' for more information. - EOF - } - -+cleanup_pidfile() -+{ -+ if [ $(ps $1 | grep $CMD | wc -l) -eq 0 ]; then -+ rm -f $PIDFILE -+ fi -+} -+ -+handle_stop_request() -+{ -+ rm -f $PIDFILE 2>/dev/null -+ exit 0 -+} -+ -+delay_activation() -+{ -+ # Open lock file with file descriptor 123 -+ exec 123>$LOCKFILE -+ if flock -n -x 123; then -+ if [ -f $PIDFILE ]; then -+ # concurrent process was faster -+ exit 0 -+ fi -+ trap handle_stop_request TERM -+ echo $$ > $PIDFILE -+ else -+ # Nothing to do, "dumpconf start" is already in progress -+ exit 0 -+ fi -+ # Close file descriptor 123 -+ exec 123>&- -+ # Do multiple sleeps in order to be interruptible -+ for ((i=0; i < $DELAY_MINUTES * 60; i++)); do -+ sleep 1 -+ done -+ rm -f $PIDFILE -+} -+ - # $1: dump device bus id (e.g. 0.0.4711) - verify_ccw_dump_device() - { -@@ -98,7 +158,7 @@ verify_ccw_dump_device() - line=$(lsdasd $1) - fi - if [ "$line" == "" ]; then -- echo "WARNING: device $1 not found!" -+ pr_info "WARNING: device $1 not found!" - return 1 - fi - found=false -@@ -115,7 +175,7 @@ verify_ccw_dump_device() - if [ $? == 0 ]; then - return 0 - else -- echo "WARNING: $1 is no valid dump device!" -+ pr_info "WARNING: $1 is no valid dump device!" - return 1 - fi - } -@@ -166,28 +226,28 @@ setup_device() - echo $DEV > $1/$2/device - else - RETVAL=1 -- echo "ERROR: Invalid DEVICE '$DEVICE'." $ERRMSG >&2 -+ pr_error "ERROR: Invalid DEVICE '$DEVICE'." $ERRMSG - return - fi - if [ $2 == "fcp" ]; then - echo $WWPN > $1/fcp/wwpn 2>/dev/null || RETVAL=1 - if [ $RETVAL -eq 1 ]; then -- echo "ERROR: Invalid WWPN '$WWPN'." $ERRMSG >&2 -+ pr_error "ERROR: Invalid WWPN '$WWPN'." $ERRMSG - return - fi - echo $LUN > $1/fcp/lun 2>/dev/null || RETVAL=1 - if [ $RETVAL -eq 1 ]; then -- echo "ERROR: Invalid LUN '$LUN'." $ERRMSG >&2 -+ pr_error "ERROR: Invalid LUN '$LUN'." $ERRMSG - return - fi - echo $BOOTPROG > $1/fcp/bootprog 2>/dev/null || RETVAL=1 - if [ $RETVAL -eq 1 ]; then -- echo "ERROR: Invalid BOOTPROG '$BOOTPROG'." $ERRMSG >&2 -+ pr_error "ERROR: Invalid BOOTPROG '$BOOTPROG'." $ERRMSG - return - fi - echo $BR_LBA > $1/fcp/br_lba 2>/dev/null || RETVAL=1 - if [ $RETVAL -eq 1 ]; then -- echo "ERROR: Invalid BR_LBA '$BR_LBA'." $ERRMSG >&2 -+ pr_error "ERROR: Invalid BR_LBA '$BR_LBA'." $ERRMSG - return - fi - fi -@@ -201,7 +261,7 @@ setup_nss_device() - setup_reipl() - { - if [ "$REIPL_TYPE" == "" ]; then -- echo "reipl on panic configured: Using default reipl values." -+ pr_info "reipl on panic configured: Using default reipl values." - return - fi - -@@ -210,7 +270,7 @@ setup_reipl() - elif [ "$REIPL_TYPE" == "nss" ]; then - setup_nss_device $REIPL_CONFIG_DIR - else -- echo "ERROR: Unknown reipl type '$REIPL_TYPE'." $ERRMSG >&2 -+ pr_error "ERROR: Unknown reipl type '$REIPL_TYPE'." $ERRMSG - RETVAL=1 - return - fi -@@ -221,7 +281,7 @@ setup_reipl() - return - fi - -- echo "$REIPL_TYPE reipl device configured." -+ pr_info "$REIPL_TYPE reipl device configured." - } - - setup_dump() -@@ -229,7 +289,7 @@ setup_dump() - if [ "$DUMP_TYPE" == "ccw" ] || [ "$DUMP_TYPE" == "fcp" ]; then - setup_device $DUMP_CONFIG_DIR $DUMP_TYPE - elif [ "$DUMP_TYPE" != "none" ]; then -- echo "ERROR: Unknown dump type '$DUMP_TYPE'." $ERRMSG >&2 -+ pr_error "ERROR: Unknown dump type '$DUMP_TYPE'." $ERRMSG - RETVAL=1 - return - fi -@@ -241,7 +301,7 @@ setup_dump() - return - fi - -- echo "$ON_PANIC on panic configured: Using $DUMP_TYPE dump device." -+ pr_info "$ON_PANIC on panic configured: Using $DUMP_TYPE dump device." - } - - setup_on_panic_vmcmd() -@@ -257,69 +317,69 @@ setup_on_panic_vmcmd() - fi - done - if [ ! -d $VMCMD_CONFIG_DIR ]; then -- echo "ERROR: No vmcmd support. Are you running on LPAR?" >&2 -+ pr_error "ERROR: No vmcmd support. Are you running on LPAR?" - RETVAL=1 - elif [ "$VMCMD" == "" ]; then -- echo "ERROR: No VMCMD_x keyword specified." $ERRMSG >&2 -+ pr_error "ERROR: No VMCMD_x keyword specified." $ERRMSG - RETVAL=1 - else - echo -en "$VMCMD" | cat > $VMCMD_CONFIG_DIR/on_panic || RETVAL=1 - fi - - if [ $RETVAL -eq 0 ]; then -- echo "vmcmd on panic configured:" -- echo -e "$VMCMD" -+ pr_info "vmcmd on panic configured:" -+ pr_info -e "$VMCMD" - fi - } - - print_fcp_device() - { - DEVICE=$(cat $1/fcp/device) || RETVAL=1 -- echo "device..: $DEVICE" -+ pr_info "device..: $DEVICE" - WWPN=$(cat $1/fcp/wwpn) || RETVAL=1 -- echo "wwpn....: $WWPN" -+ pr_info "wwpn....: $WWPN" - LUN=$(cat $1/fcp/lun) || RETVAL=1 -- echo "lun.....: $LUN" -+ pr_info "lun.....: $LUN" - BOOTPROG=$(cat $1/fcp/bootprog) || RETVAL=1 -- echo "bootprog: $BOOTPROG" -+ pr_info "bootprog: $BOOTPROG" - BR_LBA=$(cat $1/fcp/br_lba) || RETVAL=1 -- echo "br_lba..: $BR_LBA" -+ pr_info "br_lba..: $BR_LBA" - } - - print_ccw_device() - { - DEVICE=$(cat $1/ccw/device) || RETVAL=1 -- echo "device..: $DEVICE" -+ pr_info "device..: $DEVICE" - } - - print_nss_name() - { - NAME=$(cat $1/nss/device) || RETVAL=1 -- echo "device..: $NAME" -+ pr_info "device..: $NAME" - } - - status_dump() - { - CONF_DUMP_TYPE=$(cat $DUMP_CONFIG_DIR/dump_type) || RETVAL=1 - if [ "$CONF_DUMP_TYPE" == "none" ]; then -- echo "type....: no dump device configured" -+ pr_info "type....: no dump device configured" - elif [ "$CONF_DUMP_TYPE" == "ccw" ]; then -- echo "type....: ccw" -+ pr_info "type....: ccw" - print_ccw_device $DUMP_CONFIG_DIR - verify_ccw_dump_device $(cat $DUMP_CONFIG_DIR/ccw/device) - elif [ "$CONF_DUMP_TYPE" == "fcp" ]; then -- echo "type....: fcp" -+ pr_info "type....: fcp" - print_fcp_device $DUMP_CONFIG_DIR - else -- echo "ERROR: Unknown dump device type '$CONF_DUMP_TYPE'!" >&2 -- echo " Please check if you have the latest dumpconf package!" >&2 -+ pr_error "ERROR: Unknown dump device type '$CONF_DUMP_TYPE'!" -+ pr_error " Please check if you have the latest dumpconf package!" - fi - } - - status_reipl() - { - REIPL_TYPE=$(cat $REIPL_CONFIG_DIR/reipl_type) || RETVAL=1 -- echo "type....: $REIPL_TYPE" -+ pr_info "type....: $REIPL_TYPE" - if [ "$REIPL_TYPE" == "ccw" ]; then - print_ccw_device $REIPL_CONFIG_DIR - elif [ "$REIPL_TYPE" == "fcp" ]; then -@@ -327,16 +387,16 @@ status_reipl() - elif [ "$REIPL_TYPE" == "nss" ]; then - print_nss_name $REIPL_CONFIG_DIR - else -- echo "ERROR: Unknown reipl device type '$REIPL_TYPE'!" >&2 -- echo " Please check if you have the latest dumpconf package!" >&2 -+ pr_error "ERROR: Unknown reipl device type '$REIPL_TYPE'!" -+ pr_error " Please check if you have the latest dumpconf package!" - fi - } - - status_dump_reipl() - { -- echo -e "\ndump:" -+ pr_info -e "\ndump:" - status_dump -- echo -e "\nreipl:" -+ pr_info -e "\nreipl:" - status_reipl - } - -@@ -345,33 +405,65 @@ status_vmcmd() - { - VMCMD=$(cat $VMCMD_CONFIG_DIR/on_panic) || RETVAL=1 - if [ "$VMCMD" == "" ]; then -- echo "WARNING: No VM command specified!" -+ pr_info "WARNING: No VM command specified!" - else -- echo "---------------" -- echo "$VMCMD" -+ pr_info "---------------" -+ pr_info "$VMCMD" - fi - } - - start() - { -+ if [ "$1" == "background" ]; then -+ BACKGROUND=1 -+ fi -+ test -n "$DELAY_MINUTES" || DELAY_MINUTES=0 -+ test "$DELAY_MINUTES" -ge 0 2>/dev/null || RETVAL=1 -+ if [ $RETVAL -eq 1 ]; then -+ pr_error "ERROR: Invalid DELAY_MINUTES parameter" \ -+ "'$DELAY_MINUTES'." $ERRMSG -+ return -+ fi -+ if [ $DELAY_MINUTES -gt 0 ]; then -+ if [ -f $PIDFILE ]; then -+ pr_info "A delayed instance of" $CMD \ -+ "is already active." -+ return -+ fi -+ if [ $BACKGROUND -eq 1 ]; then -+ delay_activation -+ else -+ pr_info "The activation of dumpconf is being delayed" \ -+ "for" $DELAY_MINUTES "minutes" -+ $CMDFULL start background > /dev/null 2>&1 & -+ return -+ fi -+ fi - if [ "$ON_PANIC" == "" ]; then - ON_PANIC="stop" - fi - -- if [ "$ON_PANIC" == "reipl" ]; then -- setup_reipl -- elif [ "$ON_PANIC" == "dump" ] || [ "$ON_PANIC" == "dump_reipl" ]; then -- setup_dump -- elif [ "$ON_PANIC" == "vmcmd" ]; then -- setup_on_panic_vmcmd -- elif [ "$ON_PANIC" == "stop" ]; then -- echo "stop on panic configured." -- else -- echo "ERROR: Unknown 'on panic' type '$ON_PANIC'." $ERRMSG >&2 -- RETVAL=1 -- fi -+ case "$ON_PANIC" in -+ reipl) -+ setup_reipl -+ ;; -+ dump|dump_reipl) -+ setup_dump -+ ;; -+ vmcmd) -+ setup_on_panic_vmcmd -+ ;; -+ stop) -+ pr_info "stop on panic configured." -+ ;; -+ *) -+ pr_error "ERROR: Unknown 'on panic'" \ -+ "type '$ON_PANIC'." $ERRMSG -+ RETVAL=1 -+ ;; -+ esac - if [ $RETVAL -eq 1 ]; then -- return $RETVAL -+ return - fi - - echo $ON_PANIC > $ON_PANIC_CONFIG_FILE 2> /dev/null || RETVAL=1 -@@ -380,20 +472,21 @@ start() - - if [ $RETVAL -eq 1 ]; then - echo stop > $ON_PANIC_CONFIG_FILE -- echo "ERROR: $ON_PANIC not supported by hardware!" >&2 -+ pr_error "ERROR: $ON_PANIC not supported by hardware!" - fi -- -- return $RETVAL - } - - stop() - { -+ if [ -f $PIDFILE ]; then -+ kill -TERM $(cat $PIDFILE) -+ fi - echo none > $DUMP_CONFIG_DIR/dump_type || RETVAL=1 - echo stop > $ON_PANIC_CONFIG_FILE || RETVAL=1 - if [ $RETVAL -eq 0 ]; then -- echo "Dump on panic is disabled now" -+ pr_info "Dump on panic is disabled now" - else -- echo "Disabling dump on panic failed" >&2 -+ pr_error "Disabling dump on panic failed" - fi - return $RETVAL - } -@@ -401,34 +494,55 @@ stop() - status() - { - ON_PANIC=$(cat $ON_PANIC_CONFIG_FILE) || RETVAL=1 -- echo "on_panic: $ON_PANIC" -- if [ "$ON_PANIC" == "vmcmd" ]; then -- status_vmcmd -- elif [ "$ON_PANIC" == "reipl" ]; then -- status_reipl -- elif [ "$ON_PANIC" == "dump" ]; then -- status_dump -- elif [ "$ON_PANIC" == "dump_reipl" ]; then -- status_dump_reipl -- elif [ "$ON_PANIC" != "stop" ]; then -- echo "ERROR: Unknown on_panic type '$ON_PANIC'" >&2 -+ if [ -f $PIDFILE ]; then -+ pr_info "on_panic: $ON_PANIC - dumpconf activation is being" \ -+ "delayed for $DELAY_MINUTES minutes" -+ else -+ pr_info "on_panic: $ON_PANIC" - fi -+ case "$ON_PANIC" in -+ vmcmd) -+ status_vmcmd -+ ;; -+ reipl) -+ status_reipl -+ ;; -+ dump) -+ status_dump -+ ;; -+ dump_reipl) -+ status_dump_reipl -+ ;; -+ stop) -+ ;; -+ *) -+ pr_error "ERROR: Unknown on_panic type '$ON_PANIC'" -+ ;; -+ esac - } - --if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then -- printhelp -- exit 0 --elif [ "$1" = "-v" ] || [ "$1" = "--version" ]; then -- printversion -- exit 0 --fi -+case "$1" in -+ -h|--help) -+ printhelp -+ exit 0 -+ ;; -+ -v|--version) -+ printversion -+ exit 0 -+ ;; -+esac - - check_environment - -+# If system crashed, an invalid $PIDFILE might still exist -+if [ -f $PIDFILE ]; then -+ cleanup_pidfile $(cat $PIDFILE) -+fi -+ - # See how we were called. - case "$1" in - start|restart|reload|force-reload|try-restart) -- start -+ start $2 - ;; - stop) - stop -@@ -439,6 +553,7 @@ case "$1" in - *) - print_invalid_option $1 - RETVAL=1 -+ ;; - esac - - exit $RETVAL -diff --git a/etc/sysconfig/dumpconf b/etc/sysconfig/dumpconf -index cef621b..155a2cc 100644 ---- a/etc/sysconfig/dumpconf -+++ b/etc/sysconfig/dumpconf -@@ -13,13 +13,19 @@ - # /sys/firmware/reipl - # - --# -+# For the actions "reipl" and "dump_reipl" the DELAY_MINUTES keyword may -+# be used to delay the activation of dumpconf. -+# Thus potential reipl loops caused by kernel panics -+# which persistently occur early in the boot process can be prevented. -+ - # Dump on ccw device (DASD) and re-IPL after dump is complete. - # The re-IPL device, as specified under "/sys/firmware/reipl", is used. -+# The activation of dumpconf is delayed by 5 minutes. - # - # ON_PANIC=dump_reipl - # DUMP_TYPE=ccw - # DEVICE=0.0.4e13 -+# DELAY_MINUTES=5 - - # - # Dump on fcp device (SCSI Disk) -@@ -48,5 +54,7 @@ - # - # Re-IPL on panic - # The re-IPL device, as specified under "/sys/firmware/reipl", is used. -+# Since the DELAY_MINUTES keyword is omitted, there is no delay and -+# dumpconf becomes active immediately during system startup. - # - # ON_PANIC=reipl --- -1.7.3.5 - diff --git a/0053-ttyrun-run-a-program-if-a-terminal-device-is-availab.patch b/0053-ttyrun-run-a-program-if-a-terminal-device-is-availab.patch deleted file mode 100644 index 3af7a01..0000000 --- a/0053-ttyrun-run-a-program-if-a-terminal-device-is-availab.patch +++ /dev/null @@ -1,410 +0,0 @@ -From bc6e654149018090b7954e6667d3c7e7654625f6 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 28 Jan 2011 14:18:39 +0100 -Subject: [PATCH 53/61] ttyrun: run a program if a terminal device is available - -Summary: ttyrun: run a program if a terminal device is available -Description: Depending on your setup, Linux on System z might or might not - provide a particular terminal or console. ttyrun safely starts - getty programs and prevents respawns through the init program - if a terminal is not available. ---- - iucvterm/doc/Makefile | 2 +- - iucvterm/doc/ttyrun.8 | 146 +++++++++++++++++++++++++++++++++++++++ - iucvterm/src/Makefile | 11 +++- - iucvterm/src/ttyrun.c | 183 +++++++++++++++++++++++++++++++++++++++++++++++++ - 4 files changed, 339 insertions(+), 3 deletions(-) - create mode 100644 iucvterm/doc/ttyrun.8 - create mode 100644 iucvterm/src/ttyrun.c - -diff --git a/iucvterm/doc/Makefile b/iucvterm/doc/Makefile -index 5815f21..a646765 100644 ---- a/iucvterm/doc/Makefile -+++ b/iucvterm/doc/Makefile -@@ -2,7 +2,7 @@ - - include ../../common.mak - --MANS = iucvconn.1 iucvtty.1 ts-shell.1 hvc_iucv.9 chiucvallow.8 -+MANS = iucvconn.1 iucvtty.1 ts-shell.1 hvc_iucv.9 chiucvallow.8 ttyrun.8 - - all: - -diff --git a/iucvterm/doc/ttyrun.8 b/iucvterm/doc/ttyrun.8 -new file mode 100644 -index 0000000..fc7a16f ---- /dev/null -+++ b/iucvterm/doc/ttyrun.8 -@@ -0,0 +1,146 @@ -+.\" ttyrun.8 -+.\" -+.\" -+.\" Copyright IBM Corp. 2010 -+.\" Author(s): Hendrik Brueckner -+.\" ------------------------------------------------------------------------- -+.TH "ttyrun" "8" "April 2010" "s390-tools" "System Management Commands" -+.LO 8 -+. -+.ds s ttyrun -+. -+. -+.SH NAME -+ttyrun \- Start a program if a specified terminal device is available -+. -+. -+. -+.SH SYNOPSIS -+.B \*s -+.RB [ \-e | \-\-exitstatus -+.IR status ] -+.I term -+.I program -+.RI [ "program_options" ] -+.br -+.B \*s -+.RB [ \-h | \-\-help ] -+.br -+.B \*s -+.RB [ \-v | \-\-version ] -+. -+. -+. -+.SH DESCRIPTION -+\fB\*s\fP is typically started during system initialization and is used -+to prevent a respawn through the -+.BR init (8) -+program when a terminal is not available. -+ -+\fIterm\fP is the name of the terminal device and is a path relative to -+the \f(CW/dev\fP directory, for example, specify \f(CWhvc0\fP for -+\f(CW/dev/hvc0\fP. -+.br -+If the specified terminal device can be opened, \fB\*s\fP starts the -+specified program. -+ -+If the terminal device cannot be opened, the behavior of \fB\*s\fP -+depends on the \fB\-e\fP option: -+. -+.RS 2 -+.IP "\(bu" 2 -+If the \fB\-e\fP option has been specified, \fB\*s\fP exits with the -+specified return value, or -+.IP "\(bu" 2 -+If the \fB\-e\fP option has not been specified, \fB\*s\fP sleeps until -+it receives a signal that causes an exit. -+.RE -+.PP -+\fIprogram\fP is an absolute path to the program to be started by -+\fB\*s\fP and \fIprogram_options\fP specify additional arguments. -+Depending on the program, arguments might be required. The variable -+\f(CW%t\fP in the \fIprogram_options\fP is resolved to the terminal -+device specified with \fIterm\fP. -+. -+. -+. -+.SH OPTIONS -+.TP -+.BR \-e ", " \-\-exitstatus\~\fIstatus\fP -+Specifies an exit status that is returned when the terminal device -+is not available. \fIstatus\fP must be an integer in the range 1 to 255. -+ -+You can use this status value in an upstart job file to prevent -+respawning. -+. -+.TP -+.BR \-h ", " \-\-help -+Displays a short help text, then exits. -+. -+.TP -+.BR \-v ", " \-\-version -+Displays the version number of \fB\*s\fP, then exits. -+. -+. -+. -+.SH "RETURN VALUES" -+\fB\*s\fP exits with one of the following return values to report an -+error condition: -+.TP -+.B 1 -+\fB\*s\fP has been started with an argument that is not valid or -+required but missing. -+.TP -+.B 2 -+\fB\*s\fP could open the file specified for \fIterm\fP but the -+file is not a terminal device. -+.TP -+.B 3 -+\fB\*s\fP could not start the specified program. -+.PP -+The return values 1 to 3 might also be returned when the \fB\-e\fP -+option is used and the terminal device is not available. -+.TP -+.B 4 \- 255 -+The terminal device is not available and the \fB\-e\fP option -+specifies an exit status in this range. -+. -+. -+. -+.SH "EXAMPLES" -+.SS inittab -+To start \fB/sbin/agetty\fP on terminal device "hvc1", specify: -+.PP -+.ft CW -+.in +0.25in -+.nf -+h1:2345:respawn:/sbin/\*s hvc1 /sbin/agetty -L 9600 %t linux -+.fi -+.in -0.25in -+.ft -+. -+.SS upstart job/event files -+To start \fB/sbin/agetty\fP on terminal device "hvc1", add the following -+settings to the job file: -+.PP -+.ft CW -+.in +0.25in -+.nf -+respawn -+normal exit 42 -+exec /sbin/\*s -e 42 hvc1 /sbin/agetty -L 9600 %t linux -+.fi -+.in -0.25in -+.ft -+.PP -+With the normal exit statement, you specify an exit status that will -+prevent upstart from respawning the program. To prevent respawning with -+\fB\*s\fP, you must specify the same value for the \fB\-e\fP option. -+. -+. -+. -+.SH "SEE ALSO" -+.BR agetty (8), -+.BR mingetty (8), -+.BR inittab (5), -+.BR events (5) -diff --git a/iucvterm/src/Makefile b/iucvterm/src/Makefile -index f1f8f7c..369c887 100644 ---- a/iucvterm/src/Makefile -+++ b/iucvterm/src/Makefile -@@ -11,20 +11,27 @@ CPPFLAGS += -DUSE_NLS -DGETTEXT_TEXTDOMAIN=\"$(GETTEXT_TEXTDOMAIN)\" - #CPPFLAGS += -D__DEBUG__ - - PROGRAMS = iucvconn iucvtty -+SYSTOOLS = ttyrun - --all: $(PROGRAMS) -+all: $(PROGRAMS) $(SYSTOOLS) - check: - install: - for prg in $(PROGRAMS); do \ - $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $$prg $(USRBINDIR) ; \ - done -+ for prg in $(SYSTOOLS); do \ -+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $$prg $(BINDIR) ; \ -+ done - - clean: -- -rm -f *.o $(PROGRAMS) -+ -rm -f *.o $(PROGRAMS) $(SYSTOOLS) - - iucvconn: iucvconn.o getopt.o auditlog.o functions.o - - iucvtty: LDLIBS = -lutil - iucvtty: iucvtty.o getopt.o auditlog.o functions.o - -+ttyrun: GETTEXT_TEXTDOMAIN = ttyrun -+ttyrun: ttyrun.o -+ - .PHONY: install clean -diff --git a/iucvterm/src/ttyrun.c b/iucvterm/src/ttyrun.c -new file mode 100644 -index 0000000..55c2bc2 ---- /dev/null -+++ b/iucvterm/src/ttyrun.c -@@ -0,0 +1,183 @@ -+/* -+ * ttyrun - Start a program if a specified terminal device is available -+ * -+ * -+ * ttyrun is typically used to prevent a respawn through the init(8) -+ * program when a terminal is not available. -+ * ttyrun runs the specific program if the specified terminal device -+ * can be opened successfully. Otherwise the program enters a sleep or -+ * exits with a specified return value. -+ * -+ * Example: To start /sbin/agetty on terminal device hvc1, use: -+ * -+ * h1:2345:respawn:/sbin/ttyrun hvc1 /sbin/agetty -L 9600 %t linux -+ * -+ * Note: %t is resolved to the terminal device "hvc1" before /sbin/agetty -+ * is started. -+ * -+ * Return values: -+ * 1 - invalid argument or parameter is missing -+ * 2 - terminal does not resolve to a terminal device -+ * 3 - starting the specified program failed -+ * 1..255 - terminal is not available and the return code is -+ * specified with the -e option -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Hendrik Brueckner -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "zt_common.h" -+ -+ -+#define TTY_ESCAPE_STR "%t" -+ -+#define EXIT_INVALID_ARG 1 -+#define EXIT_NO_TERMINAL 2 -+#define EXIT_EXEC_FAILED 3 -+ -+ -+static const char usage[] = -+"Usage: %s [-e status] []\n" -+" %s [-h|--help] [-v|--version]\n" -+"\n" -+"Start the program if the specified terminal device is available.\n" -+"If the terminal device cannot be opened, sleep until a signal is received\n" -+"that causes an exit or exit with the return value specified with status.\n" -+"\n" -+"-e, --exitstatus Specifies an exit status in the range 1 to 255.\n" -+"-h, --help Displays this help, then exits.\n" -+"-v, --version Displays version information, then exits.\n"; -+ -+static void help_exit(const char *prg) -+{ -+ printf(usage, prg, prg); -+ exit(EXIT_SUCCESS); -+} -+ -+static void version_exit(const char *prg) -+{ -+ printf("%s: Start a program if a terminal device is available, " -+ "version %s\n", prg, RELEASE_STRING); -+ printf("Copyright IBM Corp. 2010\n"); -+ exit(EXIT_SUCCESS); -+} -+ -+static void err_exit(const char *prg, const char *msg) -+{ -+ fprintf(stderr, "%s: %s\n", prg, msg); -+ exit(EXIT_INVALID_ARG); -+} -+ -+static void wait_and_exit(void) -+{ -+ /* sleep until a signal is received, then exit */ -+ pause(); -+ exit(EXIT_SUCCESS); -+} -+ -+static const struct option prog_opts[] = { -+ { "help", no_argument, NULL, 'h'}, -+ { "version", no_argument, NULL, 'v'}, -+ { "exitstatus", required_argument, NULL, 'e'}, -+ { NULL, no_argument, NULL, 0 }, -+}; -+ -+int main(int argc, char *argv[]) -+{ -+ int rc, tty, i, c, index, done, term_index; -+ char terminal[PATH_MAX] = ""; -+ unsigned long exitstatus; -+ -+ -+ /* parse command options */ -+ if (argc == 1) -+ err_exit(argv[0], "One or more options are required but missing"); -+ -+ exitstatus = done = term_index = 0; -+ while (!done) { -+ c = getopt_long(argc, argv, "-hve:", prog_opts, NULL); -+ switch (c) { -+ case -1: -+ done = 1; -+ break; -+ case 1: -+ /* the first non-optional argument must be the -+ * terminal device */ -+ if (!strncmp(optarg, "/", 1)) -+ strncpy(terminal, optarg, PATH_MAX - 1); -+ else -+ snprintf(terminal, PATH_MAX, "/dev/%s", optarg); -+ terminal[PATH_MAX - 1] = 0; -+ term_index = optind - 1; -+ done = 1; -+ break; -+ case 'e': -+ errno = 0; -+ exitstatus = strtoul(optarg, (char **) NULL, 10); -+ if (errno == ERANGE) -+ err_exit(argv[0], "The exit status must be " -+ "an integer in the range 1 to 255"); -+ -+ if (!exitstatus || exitstatus > 255) -+ err_exit(argv[0], "The exit status must be " -+ "in the range 1 to 255"); -+ break; -+ case 'h': -+ help_exit(argv[0]); -+ case 'v': -+ version_exit(argv[0]); -+ case '?': -+ fprintf(stderr, "Try %s --help for more information\n", -+ argv[0]); -+ exit(EXIT_INVALID_ARG); -+ } -+ } -+ index = optind; -+ -+ /* check terminal */ -+ if (!strlen(terminal)) -+ err_exit(argv[0], "You must specify the name of " -+ "a terminal device"); -+ -+ /* any program to start? */ -+ if (index == argc) -+ err_exit(argv[0], "You must specify a program to start"); -+ -+ /* open and check terminal device */ -+ tty = open(terminal, O_NOCTTY | O_RDONLY | O_NONBLOCK); -+ if (tty == -1) { -+ openlog(argv[0], LOG_PID, LOG_DAEMON); -+ syslog(LOG_INFO, "Could not open tty %s (%s)", terminal, -+ strerror(errno)); -+ closelog(); -+ -+ /* enter wait or exit */ -+ if (exitstatus) -+ exit(exitstatus); -+ wait_and_exit(); -+ } -+ rc = !isatty(tty); -+ close(tty); -+ if (rc) -+ exit(EXIT_NO_TERMINAL); -+ -+ /* start getty program */ -+ for (i = index; i < argc; i++) -+ if (!strcmp(argv[i], TTY_ESCAPE_STR) && term_index) -+ argv[i] = argv[term_index]; -+ if (execv(argv[index], argv + index)) -+ exit(EXIT_EXEC_FAILED); -+ -+ exit(EXIT_SUCCESS); -+} --- -1.7.3.5 - diff --git a/0054-zgetdump-zipl-Add-ELF-dump-support-needed-for-makedu.patch b/0054-zgetdump-zipl-Add-ELF-dump-support-needed-for-makedu.patch deleted file mode 100644 index 57312af..0000000 --- a/0054-zgetdump-zipl-Add-ELF-dump-support-needed-for-makedu.patch +++ /dev/null @@ -1,8702 +0,0 @@ -From d401e50fb13e62e1d97f17e3a53e6d73bff6f587 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 28 Jan 2011 14:20:46 +0100 -Subject: [PATCH 54/61] zgetdump/zipl: Add ELF dump support (needed for makedumpfile) - -Summary: zgetdump/zipl: Add ELF dump support (needed for makedumpfile) -Description: The zgetdump tool can be used now for dump format conversion. - It can read ELF, s390, and LKCD and write ELF and s390 format - dumps. A mount option based on "fuse" is added to zgetdump that - allows dumps to be converted in memory on the fly without the - need of copying them. The following two options are added to - zgetdump: - * fmt: Specify output dump format (elf or s390) - * mount: Mount dump instead of copying it to standard output - - The zipl dump tools now store the prefix registers in the dump - header. - - With this patch also multi-volume support for tape dump is - removed, because today's tape drives have enough capacity to - store a dump on a single volume. ---- - zdump/Makefile | 26 +- - zdump/df_elf.h | 100 ++++ - zdump/df_lkcd.h | 77 +++ - zdump/df_s390.c | 128 +++++ - zdump/df_s390.h | 148 +++++ - zdump/dfi.c | 613 ++++++++++++++++++++ - zdump/dfi.h | 212 +++++++ - zdump/dfi_elf.c | 291 ++++++++++ - zdump/dfi_kdump.c | 122 ++++ - zdump/dfi_lkcd.c | 333 +++++++++++ - zdump/dfi_s390.c | 95 ++++ - zdump/dfi_s390mv.c | 547 ++++++++++++++++++ - zdump/dfi_s390tape.c | 198 +++++++ - zdump/dfo.c | 204 +++++++ - zdump/dfo.h | 54 ++ - zdump/dfo_elf.c | 299 ++++++++++ - zdump/dfo_s390.c | 200 +++++++ - zdump/dt.c | 131 +++++ - zdump/dt.h | 29 + - zdump/dt_s390mv.c | 19 + - zdump/dt_s390sv.c | 129 +++++ - zdump/opts.c | 242 ++++++++ - zdump/stdout.c | 38 ++ - zdump/zfuse.c | 238 ++++++++ - zdump/zg.c | 411 ++++++++++++++ - zdump/zg.h | 185 ++++++ - zdump/zgetdump.8 | 334 ++++++++++-- - zdump/zgetdump.c | 1431 ++++------------------------------------------- - zdump/zgetdump.h | 250 +++------ - zipl/boot/dumpcommon.S | 136 ++++- - zipl/boot/eckd2dump.S | 32 +- - zipl/boot/eckd2mvdump.S | 19 +- - zipl/boot/fba0.S | 30 +- - zipl/boot/fba2dump.S | 30 +- - zipl/boot/tapedump.S | 392 ++------------ - zipl/include/boot.h | 4 +- - zipl/src/boot.c | 2 +- - zipl/src/install.c | 21 +- - 38 files changed, 5766 insertions(+), 1984 deletions(-) - create mode 100644 zdump/df_elf.h - create mode 100644 zdump/df_lkcd.h - create mode 100644 zdump/df_s390.c - create mode 100644 zdump/df_s390.h - create mode 100644 zdump/dfi.c - create mode 100644 zdump/dfi.h - create mode 100644 zdump/dfi_elf.c - create mode 100644 zdump/dfi_kdump.c - create mode 100644 zdump/dfi_lkcd.c - create mode 100644 zdump/dfi_s390.c - create mode 100644 zdump/dfi_s390mv.c - create mode 100644 zdump/dfi_s390tape.c - create mode 100644 zdump/dfo.c - create mode 100644 zdump/dfo.h - create mode 100644 zdump/dfo_elf.c - create mode 100644 zdump/dfo_s390.c - create mode 100644 zdump/dt.c - create mode 100644 zdump/dt.h - create mode 100644 zdump/dt_s390mv.c - create mode 100644 zdump/dt_s390sv.c - create mode 100644 zdump/opts.c - create mode 100644 zdump/stdout.c - create mode 100644 zdump/zfuse.c - create mode 100644 zdump/zg.c - create mode 100644 zdump/zg.h - -diff --git a/zdump/Makefile b/zdump/Makefile -index cb546de..83c54ef 100644 ---- a/zdump/Makefile -+++ b/zdump/Makefile -@@ -1,18 +1,34 @@ - include ../common.mak - --CPPFLAGS += -D_FILE_OFFSET_BITS=64 -I../include -+CPPFLAGS += -D_FILE_OFFSET_BITS=64 -I../include -I/usr/include/fuse -+LDLIBS += -lz - - all: zgetdump - --zgetdump.o: zgetdump.h --zgetdump: zgetdump.o -+OBJECTS = zgetdump.o opts.o zg.o \ -+ dfi.o dfi_lkcd.o dfi_elf.o dfi_s390.o dfi_s390mv.o dfi_s390tape.o \ -+ dfo.o dfo_elf.o dfo_s390.o \ -+ df_s390.o \ -+ dt.o dt_s390sv.o dt_s390mv.o \ -+ stdout.o -+ -+ifneq ("$(WITHOUT_FUSE)","1") -+LDLIBS += -lfuse -+OBJECTS += zfuse.o -+else -+CPPFLAGS += -DWITHOUT_FUSE -+endif -+ -+$(OBJECTS): *.h Makefile -+ -+zgetdump: $(OBJECTS) - - install: all - $(INSTALL) -d -m 755 $(MANDIR)/man8 $(BINDIR) - $(INSTALL) -m 755 zgetdump $(BINDIR) -- $(INSTALL) -m 644 zgetdump.8 $(MANDIR)/man8 -+ $(INSTALL) -m 644 zgetdump.8 $(MANDIR)/man8 - - clean: -- rm -f *.o *~ zgetdump core -+ rm -f *.o *~ zgetdump core.* - - .PHONY: all install clean -diff --git a/zdump/df_elf.h b/zdump/df_elf.h -new file mode 100644 -index 0000000..13121c3 ---- /dev/null -+++ b/zdump/df_elf.h -@@ -0,0 +1,100 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * ELF core dump format definitions -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#ifndef DF_ELF_H -+#define DF_ELF_H -+ -+#include -+#include -+#include "zg.h" -+#include "dfo.h" -+ -+/* -+ * S390 CPU timer note (u64) -+ */ -+#ifndef NT_S390_TIMER -+#define NT_S390_TIMER 0x301 -+#endif -+ -+/* -+ * S390 TOD clock comparator note (u64) -+ */ -+#ifndef NT_S390_TODCMP -+#define NT_S390_TODCMP 0x302 -+#endif -+ -+/* -+ * S390 TOD programmable register note (u32) -+ */ -+#ifndef NT_S390_TODPREG -+#define NT_S390_TODPREG 0x303 -+#endif -+ -+/* -+ * S390 control registers note (16 * u32) -+ */ -+#ifndef NT_S390_CTRS -+#define NT_S390_CTRS 0x304 -+#endif -+ -+/* -+ * S390 prefix note (u32) -+ */ -+#ifndef NT_S390_PREFIX -+#define NT_S390_PREFIX 0x305 -+#endif -+ -+/* -+ * prstatus ELF Note -+ */ -+struct nt_prstatus_64 { -+ u8 pad1[32]; -+ u32 pr_pid; -+ u8 pad2[76]; -+ u64 psw[2]; -+ u64 gprs[16]; -+ u32 acrs[16]; -+ u64 orig_gpr2; -+ u32 pr_fpvalid; -+ u8 pad3[4]; -+} __attribute__ ((packed)); -+ -+/* -+ * fpregset ELF Note -+ */ -+struct nt_fpregset_64 { -+ u32 fpc; -+ u32 pad; -+ u64 fprs[16]; -+} __attribute__ ((packed)); -+ -+/* -+ * prpsinfo ELF Note -+ */ -+struct nt_prpsinfo_64 { -+ char pr_state; -+ char pr_sname; -+ char pr_zomb; -+ char pr_nice; -+ u64 pr_flag; -+ u32 pr_uid; -+ u32 pr_gid; -+ u32 pr_pid, pr_ppid, pr_pgrp, pr_sid; -+ char pr_fname[16]; -+ char pr_psargs[80]; -+}; -+ -+static inline void df_elf_ensure_s390x(void) -+{ -+#ifndef __s390x__ -+ ERR_EXIT("The ELF dump format is only supported on s390x (64 bit)"); -+#endif -+} -+ -+#endif /* DF_ELF_H */ -diff --git a/zdump/df_lkcd.h b/zdump/df_lkcd.h -new file mode 100644 -index 0000000..ebb9eef ---- /dev/null -+++ b/zdump/df_lkcd.h -@@ -0,0 +1,77 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * LKCD dump format definitions -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#ifndef DF_LKCD_H -+#define DF_LKCD_H -+ -+#define DF_LKCD_MAGIC 0xa8190173618f23edULL -+#define DF_LKCD_MAGIC_ASM 0x733339302d64756dULL -+#define DF_LKCD_VERSION 0x8 /* dump version number */ -+#define DF_LKCD_PANIC_LEN 0x100 /* dump panic string length */ -+#define DF_LKCD_HDR_SIZE 0x10000 /* Max space for the dump header */ -+ -+#define DF_LKCD_COMPRESS_NONE 0x0 /* don't compress this dump */ -+#define DF_LKCD_COMPRESS_GZIP 0x2 /* use GZIP compression */ -+ -+#define DF_LKCD_DH_RAW 0x1 /* raw pg (no compression) */ -+#define DF_LKCD_DH_COMPRESSED 0x2 /* pg is compressed */ -+#define DF_LKCD_DH_END 0x4 /* end marker on a full dump */ -+ -+#define DF_LKCD_UCP_SIZE (PAGE_SIZE + sizeof(struct df_lkcd_pg_hdr)) -+ -+/* -+ * LKCD standard header -+ */ -+struct df_lkcd_hdr { -+ u64 magic; -+ u32 version; -+ u32 hdr_size; -+ u32 dump_level; -+ u32 page_size; -+ u64 mem_size; -+ u64 mem_start; -+ u64 mem_end; -+ u32 num_dump_pgs; -+ char panic_string[0x100]; -+ u64 time_tv_sec; -+ u64 time_tv_usec; -+ char utsname_sysname[65]; -+ char utsname_nodename[65]; -+ char utsname_release[65]; -+ char utsname_version[65]; -+ char utsname_machine[65]; -+ char utsname_domainname[65]; -+ u64 current_task; -+ u32 dump_compress; -+ u32 dump_flags; -+ u32 dump_device; -+} __attribute__((packed)); -+ -+/* -+ * s390 LKCD asm header -+ */ -+struct df_lkcd_hdr_asm { -+ u64 magic; -+ u32 version; -+ u32 hdr_size; -+ u16 cpu_cnt; -+ u16 real_cpu_cnt; -+ u32 lc_vec[512]; -+} __attribute__((packed)); -+ -+/* -+ * Page header -+ */ -+struct df_lkcd_pg_hdr { -+ u64 addr; /* Address of dump page */ -+ u32 size; /* Size of dump page */ -+ u32 flags; /* flags (DF_LKCD_COMPRESSED, DF_LKCD_RAW,...) */ -+} __attribute__((packed)); -+ -+#endif /* DF_LKCD_H */ -diff --git a/zdump/df_s390.c b/zdump/df_s390.c -new file mode 100644 -index 0000000..b1807bb ---- /dev/null -+++ b/zdump/df_s390.c -@@ -0,0 +1,128 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * S390 dump format common functions -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "zgetdump.h" -+ -+/* -+ * Check, if we can access the lowcore information in the dump -+ */ -+static int check_addr_max(struct df_s390_hdr *hdr, u64 addr_max) -+{ -+ unsigned int i, lc_size; -+ -+ lc_size = dfi_lc_size(df_s390_to_dfi_arch(hdr->arch)); -+ for (i = 0; i < hdr->cpu_cnt; i++) { -+ if (hdr->lc_vec[i] + lc_size > addr_max) -+ return -1; -+ } -+ return 0; -+} -+ -+/* -+ * Convert lowcore information into internal CPU representation -+ */ -+void df_s390_cpu_info_add(struct df_s390_hdr *hdr, u64 addr_max) -+{ -+ unsigned int i; -+ -+ if (hdr->version < 5) { -+ /* No Prefix registers in header */ -+ hdr->cpu_cnt = 0; -+ dfi_cpu_info_init(DFI_CPU_CONTENT_NONE); -+ } else if (check_addr_max(hdr, addr_max) != 0) { -+ /* Only lowcore pointers available */ -+ dfi_cpu_info_init(DFI_CPU_CONTENT_LC); -+ } else { -+ /* All register info available */ -+ dfi_cpu_info_init(DFI_CPU_CONTENT_ALL); -+ } -+ -+ for (i = 0; i < hdr->cpu_cnt; i++) -+ dfi_cpu_add_from_lc(hdr->lc_vec[i]); -+} -+ -+/* -+ * Convert s390 TOD clock into timeval structure -+ */ -+static void tod2timeval(struct timeval *xtime, u64 todval) -+{ -+ /* adjust todclock to 1970 */ -+ todval -= 0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096); -+ -+ todval >>= 12; -+ xtime->tv_sec = todval / 1000000; -+ xtime->tv_usec = todval % 1000000; -+} -+ -+/* -+ * Convert s390 header information into internal representation -+ */ -+void df_s390_hdr_add(struct df_s390_hdr *hdr) -+{ -+ struct timeval timeval; -+ -+ if (hdr->tod) { -+ tod2timeval(&timeval, hdr->tod); -+ dfi_attr_time_set(&timeval); -+ } -+ dfi_attr_version_set(hdr->version); -+ dfi_arch_set(df_s390_to_dfi_arch(hdr->arch)); -+ if (hdr->cpu_id) -+ dfi_attr_cpu_id_set(hdr->cpu_id); -+ if (hdr->version >= 3 && hdr->mem_size_real) -+ dfi_attr_mem_size_real_set(hdr->mem_size_real); -+ if (hdr->version >= 2 && hdr->build_arch) -+ dfi_attr_build_arch_set(df_s390_to_dfi_arch(hdr->build_arch)); -+ if (hdr->version >= 5 && hdr->real_cpu_cnt) -+ dfi_attr_real_cpu_cnt_set(hdr->real_cpu_cnt); -+} -+ -+/* -+ * Add end marker information to internal representation -+ */ -+void df_s390_em_add(struct df_s390_em *em) -+{ -+ struct timeval timeval; -+ -+ if (em->tod) { -+ tod2timeval(&timeval, em->tod); -+ dfi_attr_time_end_set(&timeval); -+ } -+} -+ -+/* -+ * Verify end marker -+ */ -+int df_s390_em_verify(struct df_s390_em *em, struct df_s390_hdr *hdr) -+{ -+ if (strncmp(em->str, DF_S390_EM_STR, strlen(DF_S390_EM_STR)) != 0) -+ return -EINVAL; -+ if (hdr->tod > em->tod) -+ return -EINVAL; -+ return 0; -+} -+ -+/* -+ * Read s390 dump tool from DASD with given block size -+ */ -+void df_s390_dumper_read(struct zg_fh *fh, int blk_size, -+ struct df_s390_dumper *dumper) -+{ -+ int offset = DF_S390_MAGIC_BLK_ECKD * blk_size; -+ -+ zg_seek(fh, offset, ZG_CHECK); -+ zg_read(fh, dumper, sizeof(*dumper), ZG_CHECK); -+} -diff --git a/zdump/df_s390.h b/zdump/df_s390.h -new file mode 100644 -index 0000000..81b519b ---- /dev/null -+++ b/zdump/df_s390.h -@@ -0,0 +1,148 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * S390 dump format common functions -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#ifndef DF_S390_H -+#define DF_S390_H -+ -+#include "dt.h" -+#include "zg.h" -+ -+#define DF_S390_MAGIC 0xa8190173618f23fdULL -+#define DF_S390_HDR_SIZE 0x1000 -+#define DF_S390_EM_SIZE 16 -+#define DF_S390_EM_STR "DUMP_END" -+#define DF_S390_CPU_MAX 512 -+#define DF_S390_MAGIC_BLK_ECKD 3 -+ -+/* -+ * Architecture of dumped system -+ */ -+enum df_s390_arch { -+ DF_S390_ARCH_32 = 1, -+ DF_S390_ARCH_64 = 2, -+}; -+ -+/* -+ * s390 dump header format -+ */ -+struct df_s390_hdr { -+ u64 magic; /* 0x000 */ -+ u32 version; /* 0x008 */ -+ u32 hdr_size; /* 0x00c */ -+ u32 dump_level; /* 0x010 */ -+ u32 page_size; /* 0x014 */ -+ u64 mem_size; /* 0x018 */ -+ u64 mem_start; /* 0x020 */ -+ u64 mem_end; /* 0x028 */ -+ u32 num_pages; /* 0x030 */ -+ u32 pad; /* 0x034 */ -+ u64 tod; /* 0x038 */ -+ u64 cpu_id; /* 0x040 */ -+ u32 arch; /* 0x048 */ -+ u32 volnr; /* 0x04c */ -+ u32 build_arch; /* 0x050 */ -+ u64 mem_size_real; /* 0x054 */ -+ u8 mvdump; /* 0x05c */ -+ u16 cpu_cnt; /* 0x05d */ -+ u16 real_cpu_cnt; /* 0x05f */ -+ u8 end_pad1[0x200-0x061]; /* 0x061 */ -+ u64 mvdump_sign; /* 0x200 */ -+ u64 mvdump_zipl_time; /* 0x208 */ -+ u8 end_pad2[0x800-0x210]; /* 0x210 */ -+ u32 lc_vec[DF_S390_CPU_MAX]; /* 0x800 */ -+} __attribute__((packed)); -+ -+/* -+ * End marker: Should be at the end of every valid s390 crash dump. -+ */ -+struct df_s390_em { -+ char str[8]; -+ u64 tod; -+} __attribute__((packed)); -+ -+/* -+ * Convert DFI arch to s390 arch -+ */ -+static inline enum df_s390_arch df_s390_from_dfi_arch(enum dfi_arch dfi_arch) -+{ -+ return dfi_arch == DFI_ARCH_64 ? DF_S390_ARCH_64 : DF_S390_ARCH_32; -+} -+ -+/* -+ * Convert s390 arch to DFI arch -+ */ -+static inline enum dfi_arch df_s390_to_dfi_arch(enum df_s390_arch df_s390_arch) -+{ -+ return df_s390_arch == DF_S390_ARCH_64 ? DFI_ARCH_64 : DFI_ARCH_32; -+} -+ -+/* -+ * Dump tool structure (version 1) -+ */ -+struct df_s390_dumper_v1 { -+ char code[0xff7 - 0x8]; -+ u8 force; -+ u64 mem; -+} __attribute__ ((packed)); -+ -+#define DF_S390_DUMPER_SIZE_V1 0x1000 -+ -+/* -+ * Dump tool structure (version 2) -+ */ -+struct df_s390_dumper_v2 { -+ char code[0x1ff7 - 0x8]; -+ u8 force; -+ u64 mem; -+} __attribute__ ((packed)); -+ -+#define DF_S390_DUMPER_SIZE_V2 0x2000 -+ -+/* -+ * Dump tool structure -+ */ -+struct df_s390_dumper { -+ char magic[7]; -+ u8 version; -+ union { -+ struct df_s390_dumper_v1 v1; -+ struct df_s390_dumper_v2 v2; -+ } d; -+} __attribute__ ((packed)); -+ -+/* -+ * Dumper member access helpers -+ */ -+#define df_s390_dumper_magic(dumper) ((dumper).magic) -+#define df_s390_dumper_version(dumper) ((dumper).version) -+#define df_s390_dumper_mem(dumper) \ -+ ((dumper).version == 1 ? dumper.d.v1.mem : dumper.d.v2.mem) -+#define df_s390_dumper_force(dumper) \ -+ ((dumper).version == 1 ? dumper.d.v1.force : dumper.d.v2.force) -+#define df_s390_dumper_size(dumper) \ -+ ((dumper).version == 1 ? 0x1000 : 0x2000) -+ -+/* -+ * s390 dump helpers -+ */ -+extern void df_s390_hdr_add(struct df_s390_hdr *hdr); -+extern void df_s390_em_add(struct df_s390_em *em); -+extern void df_s390_cpu_info_add(struct df_s390_hdr *hdr, u64 addr_max); -+extern int df_s390_em_verify(struct df_s390_em *em, struct df_s390_hdr *hdr); -+extern void df_s390_dumper_read(struct zg_fh *fh, int32_t blk_size, -+ struct df_s390_dumper *dumper); -+ -+/* -+ * DASD multi-volume dumper functions -+ */ -+extern int dt_s390mv_init(void); -+extern void dt_s390mv_exit(void); -+extern void dt_s390mv_info(void); -+ -+#endif /* DF_S390_H */ -diff --git a/zdump/dfi.c b/zdump/dfi.c -new file mode 100644 -index 0000000..fc7bf12 ---- /dev/null -+++ b/zdump/dfi.c -@@ -0,0 +1,613 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * Generic input dump format functions (DFI - Dump Format Input) -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include -+#include "zgetdump.h" -+ -+#define TIME_FMT_STR "%a, %d %b %Y %H:%M:%S %z" -+#define PROGRESS_HASH_CNT 50 -+ -+/* -+ * DFI vector - ensure that tape is the first in the list! -+ */ -+static struct dfi *dfi_vec[] = { -+ &dfi_s390tape, -+ &dfi_s390mv, -+ &dfi_s390, -+ &dfi_lkcd, -+ &dfi_elf, -+ NULL, -+}; -+ -+/* -+ * CPU information -+ */ -+struct cpus { -+ struct list list; -+ enum dfi_cpu_content content; -+ unsigned int cnt; -+}; -+ -+/* -+ * Memory information -+ */ -+struct mem { -+ struct dfi_mem_chunk *chunk_cache; -+ u64 start_addr; -+ u64 end_addr; -+ unsigned int chunk_cnt; -+ struct list chunk_list; -+}; -+ -+/* -+ * Dump header attribute information -+ */ -+struct attr { -+ unsigned int *dfi_version; -+ struct timeval *time; -+ struct timeval *time_end; -+ u64 *cpu_id; -+ u64 *mem_size_real; -+ enum dfi_arch *build_arch; -+ unsigned int *vol_nr; -+ u32 *real_cpu_cnt; -+}; -+ -+/* -+ * File local static data -+ */ -+static struct { -+ enum dfi_arch arch; -+ struct attr attr; -+ struct mem mem; -+ struct cpus cpus; -+ struct dfi *dfi; -+} l; -+ -+/* -+ * Print Dump date -+ */ -+static void date_print(void) -+{ -+ char time_str[80]; -+ struct tm *tmp; -+ -+ if (l.attr.time) { -+ tmp = localtime(&l.attr.time->tv_sec); -+ strftime(time_str, sizeof(time_str), TIME_FMT_STR, tmp); -+ STDERR(" Dump created.......: %s\n", time_str); -+ } -+ if (l.attr.time_end) { -+ tmp = localtime(&l.attr.time_end->tv_sec); -+ strftime(time_str, sizeof(time_str), TIME_FMT_STR, tmp); -+ STDERR(" Dump ended.........: %s\n", time_str); -+ } -+} -+ -+/* -+ * Print memory map -+ */ -+static void mem_map_print(void) -+{ -+ struct dfi_mem_chunk *mem_chunk; -+ -+ STDERR("\nMemory map:\n"); -+ dfi_mem_chunk_iterate(mem_chunk) { -+ STDERR(" %016llx - %016llx (%llu MB)\n", mem_chunk->start, -+ mem_chunk->end, TO_MIB(mem_chunk->size)); -+ } -+} -+ -+/* -+ * Print dump information (--info option) -+ */ -+void dfi_info_print(void) -+{ -+ STDERR("General dump info:\n"); -+ STDERR(" Dump format........: %s\n", l.dfi->name); -+ if (l.attr.dfi_version) -+ STDERR(" Version............: %d\n", *l.attr.dfi_version); -+ date_print(); -+ if (l.attr.cpu_id) -+ STDERR(" Dump CPU ID........: %llx\n", *l.attr.cpu_id); -+ if (l.attr.vol_nr) -+ STDERR(" Volume number......: %d\n", *l.attr.vol_nr); -+ if (l.attr.build_arch) -+ STDERR(" Build arch.........: %s\n", -+ dfi_arch_str(*l.attr.build_arch)); -+ STDERR(" System arch........: %s\n", dfi_arch_str(l.arch)); -+ if (l.cpus.cnt) -+ STDERR(" CPU count (online).: %d\n", l.cpus.cnt); -+ if (l.attr.real_cpu_cnt) -+ STDERR(" CPU count (real)...: %d\n", *l.attr.real_cpu_cnt); -+ STDERR(" Dump memory range..: %lld MB\n", TO_MIB(dfi_mem_range())); -+ if (l.attr.mem_size_real) -+ STDERR(" Real memory range..: %lld MB\n", -+ TO_MIB(*l.attr.mem_size_real)); -+ mem_map_print(); -+ if (l.dfi->info_dump) { -+ STDERR("\nDump device info:\n"); -+ l.dfi->info_dump(); -+ } -+} -+ -+/* -+ * Add memory chunk -+ */ -+void dfi_mem_chunk_add(u64 start, u64 size, void *data, -+ dfi_mem_chunk_read_fn read_fn) -+{ -+ struct dfi_mem_chunk *mem_chunk; -+ -+ mem_chunk = zg_alloc(sizeof(*mem_chunk)); -+ mem_chunk->start = start; -+ mem_chunk->end = start + size - 1; -+ mem_chunk->size = size; -+ mem_chunk->read_fn = read_fn; -+ mem_chunk->data = data; -+ -+ list_add_end(&mem_chunk->list, &l.mem.chunk_list); -+ l.mem.start_addr = MIN(l.mem.start_addr, mem_chunk->start); -+ l.mem.end_addr = MAX(l.mem.end_addr, mem_chunk->end); -+ l.mem.chunk_cache = mem_chunk; -+ l.mem.chunk_cnt++; -+} -+ -+/* -+ * Return mem_chunk list head -+ */ -+struct list *dfi_mem_chunk_list(void) -+{ -+ return &l.mem.chunk_list; -+} -+ -+/* -+ * Return number of memory chunks in input dump -+ */ -+unsigned int dfi_mem_chunk_cnt(void) -+{ -+ return l.mem.chunk_cnt; -+} -+ -+/* -+ * Return maximum memory range -+ */ -+u64 dfi_mem_range(void) -+{ -+ return l.mem.end_addr - l.mem.start_addr + 1; -+} -+ -+/* -+ * Return first memory chunk -+ */ -+struct dfi_mem_chunk *dfi_mem_chunk_first(void) -+{ -+ if (list_is_empty(&l.mem.chunk_list)) -+ return NULL; -+ return list_entry_first(&l.mem.chunk_list, struct dfi_mem_chunk, list); -+} -+ -+/* -+ * Return next memory chunk -+ */ -+struct dfi_mem_chunk *dfi_mem_chunk_next(struct dfi_mem_chunk *mem_chunk) -+{ -+ if (mem_chunk->list.next == &l.mem.chunk_list) -+ return NULL; -+ return list_entry_next(&mem_chunk->list, struct dfi_mem_chunk, list); -+} -+ -+/* -+ * Return previous memory chunk -+ */ -+struct dfi_mem_chunk *dfi_mem_chunk_prev(struct dfi_mem_chunk *mem_chunk) -+{ -+ if (mem_chunk->list.prev == &l.mem.chunk_list) -+ return NULL; -+ return list_entry_prev(&mem_chunk->list, struct dfi_mem_chunk, list); -+} -+ -+/* -+ * Check if memory chunk contains address -+ */ -+static int mem_chunk_has_addr(struct dfi_mem_chunk *mem_chunk, u64 addr) -+{ -+ return (addr >= mem_chunk->start && addr <= mem_chunk->end); -+} -+ -+/* -+ * Find memory chunk for given address -+ */ -+struct dfi_mem_chunk *dfi_mem_chunk_find(u64 addr) -+{ -+ struct dfi_mem_chunk *mem_chunk; -+ -+ if (mem_chunk_has_addr(l.mem.chunk_cache, addr)) -+ return l.mem.chunk_cache; -+ dfi_mem_chunk_iterate(mem_chunk) { -+ if (mem_chunk_has_addr(mem_chunk, addr)) { -+ l.mem.chunk_cache = mem_chunk; -+ return mem_chunk; -+ } -+ } -+ return NULL; -+} -+ -+/* -+ * Initialize CPU info -+ */ -+void dfi_cpu_info_init(enum dfi_cpu_content cpu_content) -+{ -+ l.cpus.content = cpu_content; -+} -+ -+/* -+ * Allocate new DFI CPU -+ */ -+struct dfi_cpu *dfi_cpu_alloc(void) -+{ -+ return zg_alloc(sizeof(struct dfi_cpu)); -+} -+ -+/* -+ * Add DFI CPU -+ */ -+void dfi_cpu_add(struct dfi_cpu *cpu) -+{ -+ list_add_end(&cpu->list, &l.cpus.list); -+ l.cpus.cnt++; -+} -+ -+/* -+ * Return CPU with number cpu_nr -+ */ -+struct dfi_cpu *dfi_cpu(unsigned int cpu_nr) -+{ -+ struct dfi_cpu *cpu; -+ unsigned int i = 0; -+ -+ dfi_cpu_iterate(cpu) { -+ if (i == cpu_nr) -+ return cpu; -+ i++; -+ } -+ return NULL; -+} -+ -+/* -+ * Return CPU count -+ */ -+unsigned int dfi_cpu_cnt(void) -+{ -+ return l.cpus.cnt; -+} -+ -+/* -+ * Return CPU content -+ */ -+enum dfi_cpu_content dfi_cpu_content(void) -+{ -+ return l.cpus.content; -+} -+ -+/* -+ * Set DFI architecture -+ */ -+void dfi_arch_set(enum dfi_arch arch) -+{ -+ l.arch = arch; -+} -+ -+/* -+ * Return DFI architecture -+ */ -+enum dfi_arch dfi_arch(void) -+{ -+ return l.arch; -+} -+ -+/* -+ * Return DFI CPU list -+ */ -+struct list *dfi_cpu_list(void) -+{ -+ return &l.cpus.list; -+} -+ -+/* -+ * Read memory at given address -+ */ -+void dfi_mem_read(u64 addr, void *buf, size_t cnt) -+{ -+ struct dfi_mem_chunk *mem_chunk; -+ u64 size, copied = 0; -+ -+ while (copied != cnt) { -+ mem_chunk = dfi_mem_chunk_find(addr); -+ size = MIN(cnt - copied, mem_chunk->end - addr + 1); -+ mem_chunk->read_fn(mem_chunk, addr - mem_chunk->start, -+ buf + copied, size); -+ copied += size; -+ addr += size; -+ } -+} -+ -+/* -+ * Get input dump format name -+ */ -+const char *dfi_name(void) -+{ -+ return l.dfi->name; -+} -+ -+/* -+ * Can input dump format seek? -+ */ -+int dfi_feat_seek(void) -+{ -+ return l.dfi->feat_bits & DFI_FEAT_SEEK; -+}; -+ -+/* -+ * Can input dump format be used for copying? -+ */ -+int dfi_feat_copy(void) -+{ -+ return l.dfi->feat_bits & DFI_FEAT_COPY; -+}; -+ -+/* -+ * Return DFI arch string -+ */ -+const char *dfi_arch_str(enum dfi_arch arch) -+{ -+ switch (arch) { -+ case DFI_ARCH_32: -+ return "s390 (32 bit)"; -+ case DFI_ARCH_64: -+ return "s390x (64 bit)"; -+ case DFI_ARCH_UNKNOWN: -+ return "unknown"; -+ } -+ ABORT("dfi_arch_str: Invalid dfi arch: %d", arch); -+} -+ -+/* -+ * Initialize input dump format. -+ */ -+int dfi_init(void) -+{ -+ struct dfi *dfi; -+ int i = 0, rc; -+ -+ l.mem.start_addr = U64_MAX; -+ l.mem.end_addr = 0; -+ list_init(&l.mem.chunk_list); -+ list_init(&l.cpus.list); -+ while ((dfi = dfi_vec[i])) { -+ l.dfi = dfi; -+ g.fh = zg_open(g.opts.device, O_RDONLY, ZG_CHECK); -+ rc = dfi->init(); -+ if (rc == 0 || rc == -EINVAL) -+ return rc; -+ zg_close(g.fh); -+ i++; -+ } -+ ERR_EXIT("No valid dump found on \"%s\"", g.opts.device); -+} -+ -+/* -+ * Attribute: Dump time -+ */ -+void dfi_attr_time_set(struct timeval *time) -+{ -+ l.attr.time = zg_alloc(sizeof(*l.attr.time)); -+ *l.attr.time = *time; -+} -+ -+struct timeval *dfi_attr_time(void) -+{ -+ return l.attr.time; -+} -+ -+/* -+ * Attribute: Dump end time -+ */ -+void dfi_attr_time_end_set(struct timeval *time_end) -+{ -+ l.attr.time_end = zg_alloc(sizeof(*l.attr.time_end)); -+ *l.attr.time_end = *time_end; -+} -+ -+struct timeval *dfi_attr_time_end(void) -+{ -+ return l.attr.time_end; -+} -+ -+/* -+ * Attribute: Volume number -+ */ -+void dfi_attr_vol_nr_set(unsigned int vol_nr) -+{ -+ l.attr.vol_nr = zg_alloc(sizeof(*l.attr.vol_nr)); -+ *l.attr.vol_nr = vol_nr; -+} -+ -+/* -+ * Attribute: DFI version -+ */ -+void dfi_attr_version_set(unsigned int dfi_version) -+{ -+ l.attr.dfi_version = zg_alloc(sizeof(*l.attr.dfi_version)); -+ *l.attr.dfi_version = dfi_version; -+} -+ -+/* -+ * Attribute: CPU ID -+ */ -+void dfi_attr_cpu_id_set(u64 cpu_id) -+{ -+ l.attr.cpu_id = zg_alloc(sizeof(*l.attr.cpu_id)); -+ *l.attr.cpu_id = cpu_id; -+} -+ -+u64 *dfi_attr_cpu_id(void) -+{ -+ return l.attr.cpu_id; -+} -+ -+/* -+ * Attribute: Real memory size -+ */ -+void dfi_attr_mem_size_real_set(u64 mem_size_real) -+{ -+ l.attr.mem_size_real = zg_alloc(sizeof(*l.attr.mem_size_real)); -+ *l.attr.mem_size_real = mem_size_real; -+} -+ -+u64 *dfi_attr_mem_size_real(void) -+{ -+ return l.attr.mem_size_real; -+} -+ -+/* -+ * Attribute: Build architecture -+ */ -+void dfi_attr_build_arch_set(enum dfi_arch build_arch) -+{ -+ l.attr.build_arch = zg_alloc(sizeof(*l.attr.build_arch)); -+ *l.attr.build_arch = build_arch; -+} -+ -+enum dfi_arch *dfi_attr_build_arch(void) -+{ -+ return l.attr.build_arch; -+} -+ -+/* -+ * Attribute: Real CPU count -+ */ -+void dfi_attr_real_cpu_cnt_set(unsigned int real_cnt_cnt) -+{ -+ l.attr.real_cpu_cnt = zg_alloc(sizeof(*l.attr.real_cpu_cnt)); -+ *l.attr.real_cpu_cnt = real_cnt_cnt; -+} -+ -+unsigned int *dfi_attr_real_cpu_cnt(void) -+{ -+ return l.attr.real_cpu_cnt; -+} -+ -+/* -+ * Convert 32 bit CPU register set to 64 bit -+ */ -+static void cpu_32_to_64(struct dfi_cpu *cpu_64, struct dfi_cpu_32 *cpu_32) -+{ -+ int i; -+ -+ for (i = 0; i < 16; i++) { -+ cpu_64->gprs[i] = cpu_32->gprs[i]; -+ cpu_64->ctrs[i] = cpu_32->ctrs[i]; -+ cpu_64->acrs[i] = cpu_32->acrs[i]; -+ if (i < 4) -+ cpu_64->fprs[i] = cpu_32->fprs[i]; -+ } -+ cpu_64->psw[0] = cpu_32->psw[0]; -+ cpu_64->psw[1] = cpu_32->psw[1]; -+ cpu_64->prefix = cpu_32->prefix; -+ cpu_64->timer = cpu_32->timer; -+ cpu_64->todcmp = cpu_32->todcmp; -+} -+ -+/* -+ * Convert 64 bit CPU register set to 32 bit -+ */ -+void dfi_cpu_64_to_32(struct dfi_cpu_32 *cpu_32, struct dfi_cpu *cpu_64) -+{ -+ int i; -+ -+ for (i = 0; i < 16; i++) { -+ cpu_32->gprs[i] = (u32) cpu_64->gprs[i]; -+ cpu_32->ctrs[i] = (u32) cpu_64->ctrs[i]; -+ cpu_32->acrs[i] = (u32) cpu_64->acrs[i]; -+ if (i < 4) -+ cpu_32->fprs[i] = (u32) cpu_64->fprs[i]; -+ } -+ cpu_32->psw[0] = (u32) cpu_64->psw[0]; -+ cpu_32->psw[1] = (u32) cpu_64->psw[1]; -+ cpu_32->prefix = cpu_64->prefix; -+ cpu_32->timer = cpu_64->timer; -+ cpu_32->todcmp = cpu_64->todcmp; -+} -+ -+/* -+ * Copy 64 bit lowcore to internal register set -+ */ -+static void lc2cpu_64(struct dfi_cpu *cpu, struct dfi_lowcore_64 *lc) -+{ -+ memcpy(&cpu->gprs, lc->gpregs_save_area, sizeof(cpu->gprs)); -+ memcpy(&cpu->ctrs, lc->cregs_save_area, sizeof(cpu->ctrs)); -+ memcpy(&cpu->acrs, lc->access_regs_save_area, sizeof(cpu->acrs)); -+ memcpy(&cpu->fprs, lc->floating_pt_save_area, sizeof(cpu->fprs)); -+ memcpy(&cpu->fpc, &lc->fpt_creg_save_area, sizeof(cpu->fpc)); -+ memcpy(&cpu->psw, lc->st_status_fixed_logout, sizeof(cpu->psw)); -+ memcpy(&cpu->prefix, &lc->prefixreg_save_area, sizeof(cpu->prefix)); -+ memcpy(&cpu->timer, lc->timer_save_area, sizeof(cpu->timer)); -+ memcpy(&cpu->todpreg, &lc->tod_progreg_save_area, sizeof(cpu->todpreg)); -+ memcpy(&cpu->todcmp, lc->clock_comp_save_area, sizeof(cpu->todcmp)); -+} -+ -+/* -+ * Copy 32 bit lowcore to internal 32 bit cpu -+ */ -+static void lc2cpu_32(struct dfi_cpu_32 *cpu, struct dfi_lowcore_32 *lc) -+{ -+ memcpy(&cpu->gprs, lc->gpregs_save_area, sizeof(cpu->gprs)); -+ memcpy(&cpu->ctrs, lc->cregs_save_area, sizeof(cpu->ctrs)); -+ memcpy(&cpu->acrs, lc->access_regs_save_area, sizeof(cpu->acrs)); -+ memcpy(&cpu->fprs, lc->floating_pt_save_area, sizeof(cpu->fprs)); -+ memcpy(&cpu->psw, lc->st_status_fixed_logout, sizeof(cpu->psw)); -+ memcpy(&cpu->prefix, &lc->prefixreg_save_area, sizeof(cpu->prefix)); -+ memcpy(&cpu->timer, lc->timer_save_area, sizeof(cpu->timer)); -+ memcpy(&cpu->todcmp, lc->clock_comp_save_area, sizeof(cpu->todcmp)); -+} -+ -+/* -+ * Initialize and add a new CPU with given lowcore pointer -+ * -+ * Note: When this function is called, the memory chunks have to be already -+ * defined by the DFI dump specific code. -+ */ -+void dfi_cpu_add_from_lc(u32 lc_addr) -+{ -+ struct dfi_cpu *cpu = dfi_cpu_alloc(); -+ -+ switch (l.cpus.content) { -+ case DFI_CPU_CONTENT_LC: -+ cpu->prefix = lc_addr; -+ break; -+ case DFI_CPU_CONTENT_ALL: -+ if (l.arch == DFI_ARCH_32) { -+ struct dfi_cpu_32 cpu_32; -+ struct dfi_lowcore_32 lc; -+ dfi_mem_read(lc_addr, &lc, sizeof(lc)); -+ lc2cpu_32(&cpu_32, &lc); -+ cpu_32_to_64(cpu, &cpu_32); -+ } else { -+ struct dfi_lowcore_64 lc; -+ dfi_mem_read(lc_addr, &lc, sizeof(lc)); -+ lc2cpu_64(cpu, &lc); -+ } -+ break; -+ case DFI_CPU_CONTENT_NONE: -+ ABORT("dfi_cpu_add_from_lc() called for CONTENT_NONE"); -+ } -+ dfi_cpu_add(cpu); -+} -+ -diff --git a/zdump/dfi.h b/zdump/dfi.h -new file mode 100644 -index 0000000..0b9d849 ---- /dev/null -+++ b/zdump/dfi.h -@@ -0,0 +1,212 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * Generic input dump format functions (DFI - Dump Format Input) -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#ifndef DFI_H -+#define DFI_H -+ -+#include "zg.h" -+#include "list.h" -+ -+/* -+ * CPU info functions and definitions -+ */ -+ -+enum dfi_arch { -+ DFI_ARCH_32 = 0, -+ DFI_ARCH_64 = 1, -+ DFI_ARCH_UNKNOWN = 2, -+}; -+ -+struct dfi_lowcore_32 { -+ u8 pad_0x0000[0x0084 - 0x0000]; /* 0x0000 */ -+ u16 cpu_addr; /* 0x0084 */ -+ u8 pad_0x0086[0x00d4 - 0x0086]; /* 0x0086 */ -+ u32 extended_save_area_addr; /* 0x00d4 */ -+ u32 timer_save_area[2]; /* 0x00d8 */ -+ u32 clock_comp_save_area[2]; /* 0x00e0 */ -+ u32 mcck_interruption_code[2]; /* 0x00e8 */ -+ u8 pad_0x00f0[0x00f4-0x00f0]; /* 0x00f0 */ -+ u32 external_damage_code; /* 0x00f4 */ -+ u32 failing_storage_address; /* 0x00f8 */ -+ u8 pad_0x00fc[0x0100-0x00fc]; /* 0x00fc */ -+ u32 st_status_fixed_logout[2]; /* 0x0100 */ -+ u32 prefixreg_save_area; /* 0x0108 */ -+ u8 pad_0x0110[0x0120-0x010c]; /* 0x010c */ -+ u32 access_regs_save_area[16]; /* 0x0120 */ -+ u32 floating_pt_save_area[8]; /* 0x0160 */ -+ u32 gpregs_save_area[16]; /* 0x0180 */ -+ u32 cregs_save_area[16]; /* 0x01c0 */ -+ u8 pad_0x0200[0x1000 - 0x0200]; /* 0x0200 */ -+}; -+ -+struct dfi_lowcore_64 { -+ u8 pad_0x0000[0x0084 - 0x0000]; /* 0x0000 */ -+ u16 cpu_addr; /* 0x0084 */ -+ u8 pad_0x0086[0x1200 - 0x0086]; /* 0x0086 */ -+ u64 floating_pt_save_area[16]; /* 0x1200 */ -+ u64 gpregs_save_area[16]; /* 0x1280 */ -+ u32 st_status_fixed_logout[4]; /* 0x1300 */ -+ u8 pad_0x1310[0x1318-0x1310]; /* 0x1310 */ -+ u32 prefixreg_save_area; /* 0x1318 */ -+ u32 fpt_creg_save_area; /* 0x131c */ -+ u8 pad_0x1320[0x1324-0x1320]; /* 0x1320 */ -+ u32 tod_progreg_save_area; /* 0x1324 */ -+ u32 timer_save_area[2]; /* 0x1328 */ -+ u32 clock_comp_save_area[2]; /* 0x1330 */ -+ u8 pad_0x1338[0x1340-0x1338]; /* 0x1338 */ -+ u32 access_regs_save_area[16]; /* 0x1340 */ -+ u64 cregs_save_area[16]; /* 0x1380 */ -+ u8 pad_0x1400[0x2000-0x1400]; /* 0x1400 */ -+} __attribute__((packed)); -+ -+static inline u64 dfi_lc_size(enum dfi_arch arch) -+{ -+ if (arch == DFI_ARCH_64) -+ return 0x2000; -+ else -+ return 0x1000; -+} -+ -+struct dfi_cpu { -+ struct list list; -+ u64 gprs[16]; -+ u64 ctrs[16]; -+ u32 acrs[16]; -+ u64 fprs[16]; -+ u32 fpc; -+ u64 psw[2]; -+ u32 prefix; -+ u64 timer; -+ u64 todcmp; -+ u32 todpreg; -+}; -+ -+struct dfi_cpu_32 { -+ u32 gprs[16]; -+ u32 ctrs[16]; -+ u32 acrs[16]; -+ u64 fprs[4]; -+ u32 psw[2]; -+ u32 prefix; -+ u64 timer; -+ u64 todcmp; -+}; -+ -+extern void dfi_cpu_64_to_32(struct dfi_cpu_32 *cpu_32, struct dfi_cpu *cpu_64); -+ -+extern enum dfi_arch dfi_arch(void); -+extern void dfi_arch_set(enum dfi_arch arch); -+extern const char *dfi_arch_str(enum dfi_arch arch); -+ -+enum dfi_cpu_content { -+ DFI_CPU_CONTENT_NONE, /* No register information available */ -+ DFI_CPU_CONTENT_LC, /* Only lowcore information available */ -+ DFI_CPU_CONTENT_ALL, /* Complete register information available */ -+}; -+ -+#define dfi_cpu_iterate(cpu) \ -+ list_iterate(cpu, dfi_cpu_list(), list) -+ -+extern struct list *dfi_cpu_list(void); -+extern void dfi_cpu_info_init(enum dfi_cpu_content content); -+extern struct dfi_cpu *dfi_cpu_alloc(void); -+extern struct dfi_cpu *dfi_cpu(unsigned int cpu_nr); -+extern void dfi_cpu_add(struct dfi_cpu *cpu); -+extern unsigned int dfi_cpu_cnt(void); -+extern enum dfi_cpu_content dfi_cpu_content(void); -+extern void dfi_cpu_add_from_lc(u32 lc_addr); -+ -+/* -+ * Mem chunk functions and definitions -+ */ -+struct dfi_mem_chunk; -+ -+typedef void (*dfi_mem_chunk_read_fn)(struct dfi_mem_chunk *mem_chunk, -+ u64 off, void *buf, u64 cnt); -+ -+struct dfi_mem_chunk { -+ struct list list; /* List */ -+ u64 start; /* Start address in memory */ -+ u64 end; /* End address in memory */ -+ u64 size; /* Size of chunk in dump file */ -+ u64 out_start; /* Start offset in dump file */ -+ u64 out_end; /* End offset in dump file */ -+ dfi_mem_chunk_read_fn read_fn; /* Chunk read callback */ -+ void *data; /* Data for callback */ -+}; -+ -+extern void dfi_mem_chunk_add(u64 start, u64 size, void *data, -+ dfi_mem_chunk_read_fn read_fn); -+extern u64 dfi_mem_range(void); -+extern unsigned int dfi_mem_chunk_cnt(void); -+extern struct dfi_mem_chunk *dfi_mem_chunk_first(void); -+extern struct dfi_mem_chunk *dfi_mem_chunk_next(struct dfi_mem_chunk *chunk); -+extern struct dfi_mem_chunk *dfi_mem_chunk_prev(struct dfi_mem_chunk *chunk); -+extern struct dfi_mem_chunk *dfi_mem_chunk_find(u64 addr); -+ -+extern struct list *dfi_mem_chunk_list(void); -+#define dfi_mem_chunk_iterate(mem_chunk) \ -+ list_iterate(mem_chunk, dfi_mem_chunk_list(), list) -+ -+/* -+ * Dump header attribute set/get functions -+ */ -+extern void dfi_attr_time_set(struct timeval *time); -+extern struct timeval *dfi_attr_time(void); -+ -+extern void dfi_attr_time_end_set(struct timeval *time_end); -+extern struct timeval *dfi_attr_time_end(void); -+ -+extern void dfi_attr_cpu_id_set(u64 cpu_id); -+extern u64 *dfi_attr_cpu_id(void); -+ -+extern void dfi_attr_mem_size_real_set(u64 mem_size_real); -+extern u64 *dfi_attr_mem_size_real(); -+ -+extern void dfi_attr_vol_nr_set(unsigned int vol_nr); -+extern unsigned int *dfi_attr_vol_nr(void); -+ -+extern void dfi_attr_version_set(unsigned int dfi_version); -+extern unsigned int *dfi_attr_dfi_version(void); -+ -+extern void dfi_attr_build_arch_set(enum dfi_arch build_arch); -+extern enum dfi_arch *dfi_attr_build_arch(void); -+ -+extern void dfi_attr_real_cpu_cnt_set(u32 real_cpu_cnt); -+extern u32 *dfi_attr_real_cpu_cnt(void); -+ -+/* -+ * DFI external functions -+ */ -+extern void dfi_mem_read(u64 addr, void *buf, size_t cnt); -+extern void dfi_info_print(void); -+ -+/* -+ * DFI feature bits -+ */ -+#define DFI_FEAT_SEEK 0x1 /* Necessary for fuse mount */ -+#define DFI_FEAT_COPY 0x2 /* Necessary for stdout */ -+ -+extern int dfi_feat_seek(void); -+extern int dfi_feat_copy(void); -+ -+/* -+ * DFI operations -+ */ -+struct dfi { -+ const char *name; -+ int (*init)(void); -+ void (*info_dump)(void); -+ int feat_bits; -+}; -+ -+extern const char *dfi_name(void); -+extern int dfi_init(void); -+ -+#endif /* DFI_H */ -diff --git a/zdump/dfi_elf.c b/zdump/dfi_elf.c -new file mode 100644 -index 0000000..866411b ---- /dev/null -+++ b/zdump/dfi_elf.c -@@ -0,0 +1,291 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * ELF core dump input format -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include "zgetdump.h" -+ -+/* -+ * Read memory for given memory chunk -+ */ -+static void dfi_elf_mem_chunk_read_fn(struct dfi_mem_chunk *mem_chunk, u64 off, -+ void *buf, u64 cnt) -+{ -+ u64 elf_load_off = *((u64 *) mem_chunk->data); -+ -+ zg_seek(g.fh, elf_load_off + off, ZG_CHECK); -+ zg_read(g.fh, buf, cnt, ZG_CHECK); -+} -+ -+/* -+ * Add load (memory chunk) to DFI dump -+ */ -+static int pt_load_add(Elf64_Phdr *phdr) -+{ -+ u64 *off_ptr; -+ -+ if (phdr->p_paddr != phdr->p_vaddr) { -+ phdr->p_paddr = phdr->p_vaddr; -+ STDERR("Dump file \"%s\" is a user space core dump\n", -+ g.opts.device); -+ } -+ if (phdr->p_filesz == 0) /* Skip null pt loads */ -+ return 0; -+ off_ptr = zg_alloc(sizeof(*off_ptr)); -+ *off_ptr = phdr->p_offset; -+ dfi_mem_chunk_add(phdr->p_paddr, phdr->p_memsz, off_ptr, -+ dfi_elf_mem_chunk_read_fn); -+ if (phdr->p_offset + phdr->p_memsz > zg_size(g.fh)) -+ return -EINVAL; -+ return 0; -+} -+ -+/* -+ * Skip name of note -+ */ -+static void nt_name_skip(Elf64_Nhdr *note) -+{ -+ zg_seek_cur(g.fh, ROUNDUP(note->n_namesz, 4), ZG_CHECK); -+} -+ -+/* -+ * Read note -+ */ -+static int nt_read(Elf64_Nhdr *note, void *buf) -+{ -+ off_t buf_len = ROUNDUP(note->n_descsz, 4); -+ char tmp_buf[buf_len]; -+ -+ nt_name_skip(note); -+ if (zg_read(g.fh, tmp_buf, buf_len, ZG_CHECK_ERR) != buf_len) -+ return -EINVAL; -+ if (buf) -+ memcpy(buf, tmp_buf, note->n_descsz); -+ return 0; -+} -+ -+/* -+ * Skip note -+ */ -+static int nt_skip(Elf64_Nhdr *note) -+{ -+ return nt_read(note, NULL); -+} -+ -+/* -+ * Ensure that CPU is already defined by prstatus note -+ */ -+static void check_cpu(struct dfi_cpu *cpu, const char *note_str) -+{ -+ if (cpu) -+ return; -+ ERR_EXIT("Invalid ELF dump (%s before prstatus found)", note_str); -+} -+ -+/* -+ * Read prstatus note and return new DFI CPU -+ */ -+static struct dfi_cpu *nt_prstatus_read(Elf64_Nhdr *note) -+{ -+ struct dfi_cpu *cpu = dfi_cpu_alloc(); -+ struct nt_prstatus_64 nt_prstatus; -+ -+ if (nt_read(note, &nt_prstatus)) -+ return NULL; -+ -+ memcpy(cpu->gprs, &nt_prstatus.gprs, sizeof(cpu->gprs)); -+ memcpy(cpu->psw, &nt_prstatus.psw, sizeof(cpu->psw)); -+ memcpy(cpu->acrs, &nt_prstatus.acrs, sizeof(cpu->acrs)); -+ -+ dfi_cpu_add(cpu); -+ return cpu; -+} -+ -+/* -+ * Read fpregset note -+ */ -+static int nt_fpregset_read(struct dfi_cpu *cpu, Elf64_Nhdr *note) -+{ -+ struct nt_fpregset_64 nt_fpregset; -+ -+ check_cpu(cpu, "FPREGSET"); -+ if (nt_read(note, &nt_fpregset)) -+ return -EINVAL; -+ -+ memcpy(&cpu->fpc, &nt_fpregset.fpc, sizeof(cpu->fpc)); -+ memcpy(cpu->fprs, &nt_fpregset.fprs, sizeof(cpu->fprs)); -+ return 0; -+} -+ -+/* -+ * Read s390 timer note -+ */ -+static int nt_s390_timer_read(struct dfi_cpu *cpu, Elf64_Nhdr *note) -+{ -+ check_cpu(cpu, "S390_TIMER"); -+ return nt_read(note, &cpu->timer); -+} -+ -+/* -+ * Read s390 todcmp note -+ */ -+static int nt_s390_todcmp_read(struct dfi_cpu *cpu, Elf64_Nhdr *note) -+{ -+ check_cpu(cpu, "S390_TODCMP"); -+ return nt_read(note, &cpu->todcmp); -+} -+ -+/* -+ * Read s390 todpreg note -+ */ -+static int nt_s390_todpreg_read(struct dfi_cpu *cpu, Elf64_Nhdr *note) -+{ -+ check_cpu(cpu, "S390_TODPREG"); -+ return nt_read(note, &cpu->todpreg); -+} -+ -+/* -+ * Read s390 ctrs note -+ */ -+static int nt_s390_ctrs_read(struct dfi_cpu *cpu, Elf64_Nhdr *note) -+{ -+ check_cpu(cpu, "S390_CTRS"); -+ return nt_read(note, &cpu->ctrs); -+} -+ -+/* -+ * Read s390 prefix note -+ */ -+static int nt_s390_prefix_read(struct dfi_cpu *cpu, Elf64_Nhdr *note) -+{ -+ check_cpu(cpu, "S390_PREFIX"); -+ return nt_read(note, &cpu->prefix); -+} -+ -+/* -+ * Add all notes for notes phdr -+ */ -+static int pt_notes_add(Elf64_Phdr *phdr) -+{ -+ u64 start_off = zg_tell(g.fh, ZG_CHECK); -+ struct dfi_cpu *cpu_current = NULL; -+ u64 notes_start_off; -+ Elf64_Nhdr note; -+ int rc; -+ -+ zg_seek(g.fh, phdr->p_offset, ZG_CHECK); -+ notes_start_off = zg_tell(g.fh, ZG_CHECK); -+ while (zg_tell(g.fh, ZG_CHECK) - notes_start_off < phdr->p_filesz) { -+ rc = zg_read(g.fh, ¬e, sizeof(note), ZG_CHECK_ERR); -+ if (rc != sizeof(note)) -+ return -EINVAL; -+ switch (note.n_type) { -+ case NT_PRSTATUS: -+ cpu_current = nt_prstatus_read(¬e); -+ if (!cpu_current) -+ return -EINVAL; -+ break; -+ case NT_FPREGSET: -+ if (nt_fpregset_read(cpu_current, ¬e)) -+ return -EINVAL; -+ break; -+ case NT_S390_TIMER: -+ if (nt_s390_timer_read(cpu_current, ¬e)) -+ return -EINVAL; -+ break; -+ case NT_S390_TODCMP: -+ if (nt_s390_todcmp_read(cpu_current, ¬e)) -+ return -EINVAL; -+ break; -+ case NT_S390_TODPREG: -+ if (nt_s390_todpreg_read(cpu_current, ¬e)) -+ return -EINVAL; -+ break; -+ case NT_S390_CTRS: -+ if (nt_s390_ctrs_read(cpu_current, ¬e)) -+ return -EINVAL; -+ break; -+ case NT_S390_PREFIX: -+ if (nt_s390_prefix_read(cpu_current, ¬e)) -+ return -EINVAL; -+ break; -+ default: -+ if (nt_skip(¬e)) -+ return -EINVAL; -+ break; -+ } -+ } -+ zg_seek(g.fh, start_off, ZG_CHECK); -+ return 0; -+} -+ -+/* -+ * Read ELF header -+ */ -+static int read_elf_hdr(Elf64_Ehdr *ehdr) -+{ -+ if (zg_size(g.fh) < sizeof(*ehdr)) -+ return -ENODEV; -+ zg_read(g.fh, ehdr, sizeof(*ehdr), ZG_CHECK); -+ if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) -+ return -ENODEV; -+ if (ehdr->e_type != ET_CORE) -+ return -ENODEV; -+ if (ehdr->e_machine != EM_S390 || ehdr->e_ident[EI_CLASS] != ELFCLASS64) -+ ERR_EXIT("Only s390x (64 bit) core dump files are supported"); -+ return 0; -+} -+ -+/* -+ * Initialize ELF input dump format -+ */ -+static int dfi_elf_init(void) -+{ -+ Elf64_Ehdr ehdr; -+ Elf64_Phdr phdr; -+ int i; -+ -+ if (read_elf_hdr(&ehdr) != 0) -+ return -ENODEV; -+ -+ df_elf_ensure_s390x(); -+ dfi_arch_set(DFI_ARCH_64); -+ dfi_cpu_info_init(DFI_CPU_CONTENT_ALL); -+ -+ for (i = 0; i < ehdr.e_phnum; i++) { -+ zg_read(g.fh, &phdr, sizeof(phdr), ZG_CHECK); -+ switch (phdr.p_type) { -+ case PT_LOAD: -+ if (pt_load_add(&phdr)) -+ return -EINVAL; -+ break; -+ case PT_NOTE: -+ if (pt_notes_add(&phdr)) -+ return -EINVAL; -+ break; -+ default: -+ break; -+ } -+ } -+ dfi_attr_version_set(ehdr.e_ident[EI_VERSION]); -+ return 0; -+} -+ -+/* -+ * ELF DFI operations -+ */ -+struct dfi dfi_elf = { -+ .name = "elf", -+ .init = dfi_elf_init, -+ .feat_bits = DFI_FEAT_COPY | DFI_FEAT_SEEK, -+}; -diff --git a/zdump/dfi_kdump.c b/zdump/dfi_kdump.c -new file mode 100644 -index 0000000..537ea55 ---- /dev/null -+++ b/zdump/dfi_kdump.c -@@ -0,0 +1,122 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * kdump input format -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include "zgetdump.h" -+ -+struct l_new_utsname { -+ char sysname[65]; -+ char nodename[65]; -+ char release[65]; -+ char version[65]; -+ char machine[65]; -+ char domainname[65]; -+}; -+ -+struct df_kdump_hdr { -+ char signature[8]; -+ int header_version; -+ struct l_new_utsname utsname; -+ struct timeval timestamp; -+ unsigned int status; -+ int block_size; -+ int sub_hdr_size; -+ unsigned int bitmap_blocks; -+ unsigned int max_mapnr; -+ unsigned int total_ram_blocks; -+ unsigned int device_blocks; -+ unsigned int written_blocks; -+ unsigned int current_cpu; -+ int nr_cpus; -+ void *tasks[0]; -+}; -+ -+struct df_kdump_sub_hdr { -+ unsigned long phys_base; -+ int dump_level; -+ int split; -+ unsigned long start_pfn; -+ unsigned long end_pfn; -+ off_t offset_vmcoreinfo; -+ unsigned long size_vmcoreinfo; -+}; -+ -+/* -+ * File local static data -+ */ -+static struct { -+ struct df_kdump_hdr hdr; /* kdump (diskdump) dump header */ -+ struct df_kdump_sub_hdr shdr; /* kdump subheader */ -+} l; -+ -+#ifdef DEBUG -+static void print_header(void) -+{ -+ STDERR("diskdump main header\n"); -+ STDERR(" signature : %s\n", l.hdr.signature); -+ STDERR(" header_version : %d\n", l.hdr.header_version); -+ STDERR(" status : %d\n", l.hdr.status); -+ STDERR(" block_size : %d\n", l.hdr.block_size); -+ STDERR(" sub_hdr_size : %d\n", l.hdr.sub_hdr_size); -+ STDERR(" bitmap_blocks : %d\n", l.hdr.bitmap_blocks); -+ STDERR(" max_mapnr : 0x%x\n", l.hdr.max_mapnr); -+ STDERR(" total_ram_blocks : %d\n", l.hdr.total_ram_blocks); -+ STDERR(" device_blocks : %d\n", l.hdr.device_blocks); -+ STDERR(" written_blocks : %d\n", l.hdr.written_blocks); -+ STDERR(" current_cpu : %d\n", l.hdr.current_cpu); -+ STDERR(" nr_cpus : %d\n", l.hdr.nr_cpus); -+ STDERR("kdump sub header\n"); -+ STDERR(" phys_base : 0x%lx\n", l.shdr.phys_base); -+ STDERR(" dump_level : %d\n", l.shdr.dump_level); -+ STDERR(" split : %d\n", l.shdr.split); -+ STDERR(" start_pfn : 0x%lx\n", l.shdr.start_pfn); -+ STDERR(" end_pfn : 0x%lx\n", l.shdr.end_pfn); -+} -+#endif -+ -+/* -+ * Read kdump dump header -+ */ -+static int read_kdump_hdr(void) -+{ -+ if ((zg_type(g.fh) == ZG_TYPE_FILE) && (zg_size(g.fh) < sizeof(l.hdr))) -+ return -ENODEV; -+ zg_read(g.fh, &l.hdr, sizeof(l.hdr), ZG_CHECK); -+ if (memcmp(l.hdr.signature, "KDUMP", 5) != 0) -+ return -ENODEV; -+ zg_seek(g.fh, l.hdr.block_size, ZG_CHECK); -+ zg_read(g.fh, &l.shdr, sizeof(l.shdr), ZG_CHECK); -+ dfi_attr_version_set(l.hdr.header_version); -+ dfi_attr_real_cpu_cnt_set(l.hdr.nr_cpus); -+ dfi_arch_set(DFI_ARCH_64); -+#ifdef DEBUG -+ print_header(); -+#endif -+ return 0; -+} -+ -+/* -+ * Initialize kdump DFI -+ */ -+static int dfi_kdump_init(void) -+{ -+ if (read_kdump_hdr() != 0) -+ return -ENODEV; -+ dfi_mem_chunk_add(0, l.hdr.max_mapnr * PAGE_SIZE, NULL, NULL); -+ return 0; -+ -+} -+ -+/* -+ * S390 DFI operations -+ */ -+struct dfi dfi_kdump = { -+ .name = "kdump", -+ .init = dfi_kdump_init, -+ .feat_bits = 0, -+}; -diff --git a/zdump/dfi_lkcd.c b/zdump/dfi_lkcd.c -new file mode 100644 -index 0000000..0c5e8a9 ---- /dev/null -+++ b/zdump/dfi_lkcd.c -@@ -0,0 +1,333 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * LKCD dump input format -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include -+#include "zgetdump.h" -+ -+#define MEM_HOLE_SIZE_MIN (1024 * 1024) -+#define IDX_KIB 64 /* One index entry per IDX_KIB */ -+#define IDX_TO_ADDR(idx) (idx * IDX_KIB * 1024) -+#define ADDR_TO_IDX(addr) (addr / (1024 * IDX_KIB)) -+ -+/* -+ * File local static data -+ */ -+static struct { -+ u64 *pg_hdr_idx; -+ u64 page_last; -+ struct df_lkcd_hdr hdr; -+ struct df_lkcd_hdr_asm hdr_asm; -+ int dump_full; -+} l; -+ -+/* -+ * Read LKCD page buffer, either compressed or uncompressed -+ */ -+static void read_page_buf(struct df_lkcd_pg_hdr *pg_hdr, void *buf) -+{ -+ unsigned long size = PAGE_SIZE; -+ unsigned char cbuf[PAGE_SIZE]; -+ -+ switch (pg_hdr->flags) { -+ case DF_LKCD_DH_RAW: -+ zg_read(g.fh, buf, pg_hdr->size, ZG_CHECK); -+ break; -+ case DF_LKCD_DH_COMPRESSED: -+ zg_read(g.fh, cbuf, pg_hdr->size, ZG_CHECK); -+ uncompress(buf, &size, cbuf, pg_hdr->size); -+ if (size != PAGE_SIZE) -+ ABORT("Invalid page size: %ld", size); -+ break; -+ default: -+ ERR_EXIT("Unsupported page flags: %x at addr %Lx", -+ pg_hdr->flags, pg_hdr->addr); -+ } -+} -+ -+/* -+ * Read next LKCD page from current file position -+ * -+ * If we find the page, we copy the page content. If the page is not present -+ * we copy zeroes and skip it. If the page address is not yet reached, we just -+ * skip it. -+ */ -+static int read_next_page(u64 addr, void *buf) -+{ -+ struct df_lkcd_pg_hdr pg_hdr; -+ -+ zg_read(g.fh, &pg_hdr, sizeof(pg_hdr), ZG_CHECK); -+ l.page_last = pg_hdr.addr / PAGE_SIZE; -+ if (pg_hdr.addr == addr) { -+ read_page_buf(&pg_hdr, buf); -+ return 0; -+ } -+ if (pg_hdr.addr > addr) { -+ memset(buf, 0, PAGE_SIZE); -+ zg_seek_cur(g.fh, pg_hdr.size, ZG_CHECK); -+ return 0; -+ } -+ zg_seek_cur(g.fh, pg_hdr.size, ZG_CHECK); -+ return -ENODEV; -+} -+ -+/* -+ * Read LKCD dump page for flex dump -+ * -+ * If the page after the last read page should be read, we just read -+ * the next one. Otherwise we seek to the beginning of the page cluster -+ * of the page index and search the page there. -+ */ -+static void read_page_flex(u64 pg_num, void *buf) -+{ -+ u64 addr = pg_num * PAGE_SIZE; -+ -+ if (l.pg_hdr_idx[ADDR_TO_IDX(addr)] == 0) -+ ABORT("Dump page index broken"); -+ -+ if (l.page_last == pg_num - 1) { -+ read_next_page(addr, buf); -+ return; -+ } -+ -+ zg_seek(g.fh, l.pg_hdr_idx[ADDR_TO_IDX(addr)], ZG_CHECK); -+ do { -+ if (read_next_page(addr, buf) == 0) -+ break; -+ } while (1); -+} -+ -+/* -+ * Read lkcd page for full dump -+ */ -+static void read_page_full(u64 pg_num, void *buf) -+{ -+ zg_seek(g.fh, DF_LKCD_HDR_SIZE + pg_num * DF_LKCD_UCP_SIZE + -+ sizeof(struct df_lkcd_pg_hdr), ZG_CHECK); -+ zg_read(g.fh, buf, PAGE_SIZE, ZG_CHECK); -+} -+ -+/* -+ * Read lkcd page -+ */ -+static void read_page(u64 pg_num, void *buf) -+{ -+ if (l.dump_full) -+ read_page_full(pg_num, buf); -+ else -+ read_page_flex(pg_num, buf); -+} -+ -+/* -+ * LKCD mem chunk read callback -+ */ -+static void dfi_lkcd_mem_chunk_read_fn(struct dfi_mem_chunk *mem_chunk, u64 off, -+ void *buf, u64 cnt) -+{ -+ u64 copied = 0, size, pg_nr, addr = off + mem_chunk->start; -+ char pg_buf[PAGE_SIZE]; -+ unsigned int pg_off; -+ -+ while (copied != cnt) { -+ pg_nr = (addr + copied) / PAGE_SIZE; -+ pg_off = (addr + copied) % PAGE_SIZE; -+ size = MIN(cnt - copied, PAGE_SIZE - pg_off); -+ read_page(pg_nr, pg_buf); -+ memcpy(buf + copied, &pg_buf[pg_off], size); -+ copied += size; -+ } -+} -+ -+/* -+ * Did we find the end of the LCKD dump? -+ */ -+static int dump_end(u64 addr, struct df_lkcd_pg_hdr *pg_hdr) -+{ -+ if (addr == pg_hdr->addr) { -+ /* -+ * This is a workaroud for a bug in vmconvert, -+ * where instaed of the end marker the last -+ * page was written twice. Sorry for that... -+ */ -+ return 1; -+ } -+ if (pg_hdr->addr == 0 && pg_hdr->size == 4 && pg_hdr->flags == 0) { -+ /* -+ * zfcpdump bug (wrong end marker) -+ */ -+ return 1; -+ } -+ if (pg_hdr->flags == DF_LKCD_DH_END) -+ return 1; -+ return 0; -+} -+ -+/* -+ * Init memory chunks for full dump -+ * -+ * Full dump: It is not compressed and it does not have any memory holes. -+ */ -+static int mem_init_full(void) -+{ -+ dfi_mem_chunk_add(0, l.hdr.mem_end, NULL, dfi_lkcd_mem_chunk_read_fn); -+ l.dump_full = 1; -+ return 0; -+} -+ -+/* -+ * Init memory chunks for flex dump -+ * -+ * Flex dump: It is compressed and/or it has memory holes. -+ */ -+static int mem_init_flex(void) -+{ -+ u64 addr = U64_MAX, idx = 0, mem_chunk_start = 0, rc; -+ struct df_lkcd_pg_hdr pg_hdr; -+ int dump_incomplete = 0; -+ -+ l.pg_hdr_idx = zg_alloc(sizeof(u64) * (ADDR_TO_IDX(l.hdr.mem_end) + 1)); -+ zg_seek(g.fh, DF_LKCD_HDR_SIZE, ZG_CHECK_NONE); -+ zg_progress_init("Analyzing dump", l.hdr.mem_end); -+ do { -+ rc = zg_read(g.fh, &pg_hdr, sizeof(pg_hdr), ZG_CHECK_ERR); -+ if (rc != sizeof(pg_hdr)) { -+ dump_incomplete = 1; -+ break; -+ } -+ if (dump_end(addr, &pg_hdr)) -+ break; -+ if (pg_hdr.addr - addr > MEM_HOLE_SIZE_MIN) { -+ dfi_mem_chunk_add(mem_chunk_start, -+ addr + PAGE_SIZE - mem_chunk_start, -+ NULL, -+ dfi_lkcd_mem_chunk_read_fn); -+ mem_chunk_start = pg_hdr.addr; -+ } -+ addr = pg_hdr.addr; -+ zg_progress(addr); -+ if (addr >= IDX_TO_ADDR(idx)) { -+ idx = ADDR_TO_IDX(addr); -+ l.pg_hdr_idx[idx] = zg_tell(g.fh, ZG_CHECK) - -+ sizeof(pg_hdr); -+ idx++; -+ } -+ zg_seek_cur(g.fh, pg_hdr.size, ZG_CHECK); -+ } while (1); -+ -+ if (addr != mem_chunk_start) { -+ dfi_mem_chunk_add(mem_chunk_start, -+ l.hdr.mem_end - mem_chunk_start, -+ NULL, -+ dfi_lkcd_mem_chunk_read_fn); -+ } -+ zg_progress(l.hdr.mem_end); -+ if (g.opts.action != ZG_ACTION_MOUNT) -+ fprintf(stderr, "\n"); -+ if (dump_incomplete) -+ return -EINVAL; -+ return 0; -+} -+ -+/* -+ * Do we have a full dump? -+ */ -+static int is_full_dump() -+{ -+ u64 full_size; -+ int pages; -+ -+ if (l.hdr.dump_compress != DF_LKCD_COMPRESS_NONE) -+ return 0; -+ pages = l.hdr.mem_end / PAGE_SIZE; -+ full_size = DF_LKCD_HDR_SIZE + pages * DF_LKCD_UCP_SIZE + -+ sizeof(struct df_lkcd_pg_hdr); -+ if (zg_size(g.fh) != full_size) -+ return 0; -+ return 1; -+} -+ -+/* -+ * Init memory chunks -+ */ -+static int mem_init(void) -+{ -+ if (is_full_dump()) -+ return mem_init_full(); -+ else -+ return mem_init_flex(); -+} -+ -+/* -+ * Initialize CPU information -+ */ -+static void cpu_init(void) -+{ -+ unsigned int i; -+ -+ if (l.hdr_asm.magic != DF_LKCD_MAGIC_ASM) { -+ /* Old LKCD dump without asm header */ -+ dfi_cpu_info_init(DFI_CPU_CONTENT_NONE); -+ return; -+ } -+ -+ dfi_cpu_info_init(DFI_CPU_CONTENT_ALL); -+ for (i = 0; i < l.hdr_asm.cpu_cnt; i++) -+ dfi_cpu_add_from_lc(l.hdr_asm.lc_vec[i]); -+} -+ -+/* -+ * Read LKCD dump header and dump asm header -+ */ -+static int read_lkcd_hdr(void) -+{ -+ if (zg_size(g.fh) < DF_LKCD_HDR_SIZE) -+ return -ENODEV; -+ -+ /* Read dump header */ -+ zg_read(g.fh, &l.hdr, sizeof(l.hdr), ZG_CHECK); -+ -+ if (l.hdr.magic != DF_LKCD_MAGIC) -+ return -ENODEV; -+ -+ /* Read asm header */ -+ zg_seek(g.fh, l.hdr.hdr_size, ZG_CHECK); -+ zg_read(g.fh, &l.hdr_asm, sizeof(l.hdr_asm), ZG_CHECK); -+ if (strncmp(l.hdr.utsname_machine, "s390x", sizeof("s390x")) == 0) -+ dfi_arch_set(DFI_ARCH_64); -+ else if (strncmp(l.hdr.utsname_machine, "s390", sizeof("s390")) == 0) -+ dfi_arch_set(DFI_ARCH_32); -+ else -+ ERR_EXIT("Dump architecture \"%s\" is not supported", -+ l.hdr.utsname_machine); -+ if (l.hdr_asm.magic == DF_LKCD_MAGIC_ASM) -+ dfi_attr_real_cpu_cnt_set(l.hdr_asm.real_cpu_cnt); -+ dfi_attr_version_set(l.hdr.version); -+ return 0; -+} -+ -+/* -+ * Initialize LKCD DFI -+ */ -+static int dfi_lkcd_init(void) -+{ -+ if (read_lkcd_hdr() != 0) -+ return -ENODEV; -+ if (mem_init() != 0) -+ return -EINVAL; -+ cpu_init(); -+ return 0; -+} -+ -+/* -+ * LKCD DFI operations -+ */ -+struct dfi dfi_lkcd = { -+ .name = "lkcd", -+ .init = dfi_lkcd_init, -+ .feat_bits = DFI_FEAT_COPY | DFI_FEAT_SEEK, -+}; -diff --git a/zdump/dfi_s390.c b/zdump/dfi_s390.c -new file mode 100644 -index 0000000..8a69849 ---- /dev/null -+++ b/zdump/dfi_s390.c -@@ -0,0 +1,95 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * S390 dump input format -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "zgetdump.h" -+ -+/* -+ * File local static data -+ */ -+static struct { -+ struct df_s390_hdr hdr; /* s390 dump header */ -+ struct df_s390_em em; /* s390 end marker */ -+} l; -+ -+/* -+ * S390 mem chunk read callback -+ */ -+static void dfi_s390_mem_chunk_read(struct dfi_mem_chunk *mem_chunk, u64 off, -+ void *buf, u64 cnt) -+{ -+ (void) mem_chunk; -+ -+ zg_seek(g.fh, off + DF_S390_HDR_SIZE, ZG_CHECK); -+ zg_read(g.fh, buf, cnt, ZG_CHECK); -+} -+ -+/* -+ * Read s390 dump header -+ */ -+static int read_s390_hdr(void) -+{ -+ if ((zg_type(g.fh) == ZG_TYPE_FILE) && (zg_size(g.fh) < sizeof(l.hdr))) -+ return -ENODEV; -+ zg_read(g.fh, &l.hdr, sizeof(l.hdr), ZG_CHECK); -+ if (l.hdr.magic != DF_S390_MAGIC) -+ return -ENODEV; -+ df_s390_hdr_add(&l.hdr); -+ return 0; -+} -+ -+/* -+ * Init end marker -+ */ -+static int read_s390_em(void) -+{ -+ u64 rc; -+ -+ rc = zg_seek(g.fh, l.hdr.mem_size + DF_S390_HDR_SIZE, ZG_CHECK_NONE); -+ if (rc != l.hdr.mem_size + DF_S390_HDR_SIZE) -+ return -EINVAL; -+ rc = zg_read(g.fh, &l.em, sizeof(l.em), ZG_CHECK_ERR); -+ if (rc != sizeof(l.em)) -+ return -EINVAL; -+ if (df_s390_em_verify(&l.em, &l.hdr) != 0) -+ return -EINVAL; -+ df_s390_em_add(&l.em); -+ return 0; -+} -+ -+/* -+ * Initialize s390 DFI -+ */ -+static int dfi_s390_init(void) -+{ -+ if (read_s390_hdr() != 0) -+ return -ENODEV; -+ dfi_mem_chunk_add(0, l.hdr.mem_size, NULL, dfi_s390_mem_chunk_read); -+ if (read_s390_em() != 0) -+ return -EINVAL; -+ df_s390_cpu_info_add(&l.hdr, l.hdr.mem_size); -+ zg_seek(g.fh, sizeof(l.hdr), ZG_CHECK); -+ return 0; -+} -+ -+/* -+ * S390 DFI operations -+ */ -+struct dfi dfi_s390 = { -+ .name = "s390", -+ .init = dfi_s390_init, -+ .feat_bits = DFI_FEAT_COPY | DFI_FEAT_SEEK, -+}; -diff --git a/zdump/dfi_s390mv.c b/zdump/dfi_s390mv.c -new file mode 100644 -index 0000000..f79188d ---- /dev/null -+++ b/zdump/dfi_s390mv.c -@@ -0,0 +1,547 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * S390 multi-volume dump input format -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "zgetdump.h" -+ -+#define SYSFS_BUSDIR "/sys/bus/ccw/devices" -+#define MAX_VOLUMES 32 -+ -+/* -+ * Parameter for DASD multi-volume dump -+ */ -+struct vol_parm { -+ u16 devno; -+ u32 start_blk; -+ u32 end_blk; -+ u8 blk_size; -+ u8 end_sec; -+ u8 num_heads; -+} __attribute__ ((packed)); -+ -+struct vol_parm_table { -+ u64 timestamp; -+ u16 vol_cnt; -+ struct vol_parm vol_parm[MAX_VOLUMES]; -+} __attribute__ ((packed)); -+ -+/* -+ * Device signature -+ */ -+enum dev_sign { -+ SIGN_INVALID = 0, /* No dumper installed */ -+ SIGN_VALID = 1, /* dumper installed, but volume not used */ -+ SIGN_ACTIVE = 2, /* dumper installed and volume userd */ -+}; -+ -+static char *dev_sign_str[] = {"invalid", "valid", "active"}; -+#define dev_sign_str(x) (dev_sign_str[x]) -+ -+/* -+ * Device status -+ */ -+enum dev_status { -+ DEV_ONLINE = 0, -+ DEV_OFFLINE = 1, -+ DEV_UNDEFINED = 2, -+}; -+ -+static char *dev_status_str[] = {"online", "offline", "undefined"}; -+#define dev_status_str(x) (dev_status_str[x]) -+ -+/* -+ * Volume information -+ */ -+struct vol { -+ dev_t dev; -+ struct zg_fh *fh; -+ char *devnode; -+ enum dev_status status; -+ enum dev_sign sign; -+ off_t part_off; -+ u64 part_size; -+ u64 mem_start; -+ u64 mem_end; -+ char bus_id[9]; -+ u32 nr; -+ u16 blk_size; -+ struct df_s390_dumper dumper; -+ struct df_s390_hdr hdr; -+}; -+ -+/* -+ * File local static data -+ */ -+static struct { -+ struct df_s390_hdr hdr; -+ struct df_s390_em em; -+ struct vol vol_vec[MAX_VOLUMES]; -+ struct vol_parm_table table; -+ int blk_size; -+ struct df_s390_dumper dumper; -+ int dump_incomplete; -+} l; -+ -+/* -+ * Read volume parameter table -+ */ -+static void table_read(struct zg_fh *fh, u16 blk_size, -+ struct vol_parm_table *table) -+{ -+ int off; -+ -+ off = DF_S390_MAGIC_BLK_ECKD * blk_size + df_s390_dumper_size(l.dumper); -+ zg_seek(fh, off, ZG_CHECK); -+ zg_read(fh, table, sizeof(*table), ZG_CHECK); -+} -+ -+/* -+ * Initialize dump end marker -+ */ -+static void em_init(struct vol *vol) -+{ -+ off_t em_off; -+ -+ em_off = vol->part_off + (vol->mem_end + 1 - vol->mem_start) + -+ DF_S390_HDR_SIZE; -+ zg_seek(vol->fh, em_off, ZG_CHECK); -+ zg_read(vol->fh, &l.em, sizeof(l.em), ZG_CHECK); -+ if (df_s390_em_verify(&l.em, &l.hdr) != 0) -+ l.dump_incomplete = 1; -+} -+ -+/* -+ * Check sysfs, whether a device specified by its bus ID is defined and online. -+ * Find out the corresponding dev_t -+ */ -+static enum dev_status dev_from_busid(char *bus_id, dev_t *dev) -+{ -+ char tmp_file[PATH_MAX], dev_file[PATH_MAX]; -+ struct dirent *direntp; -+ int fh, minor, major; -+ char buf[10]; -+ DIR *fh_dir; -+ -+ snprintf(dev_file, PATH_MAX, "%s/%s", SYSFS_BUSDIR, bus_id); -+ fh_dir = opendir(dev_file); -+ if (!fh_dir) -+ return DEV_UNDEFINED; -+ -+ snprintf(tmp_file, PATH_MAX, "%s/online", dev_file); -+ fh = open(tmp_file, O_RDONLY); -+ if (read(fh, buf, 1) == -1) -+ ERR_EXIT_ERRNO("Could not read online attribute"); -+ close(fh); -+ -+ if (buf[0] != '1') -+ return DEV_OFFLINE; -+ -+ while ((direntp = readdir(fh_dir))) -+ if (strncmp(direntp->d_name, "block:", 6) == 0) -+ break; -+ closedir(fh_dir); -+ -+ if (direntp == NULL) { -+ snprintf(dev_file, PATH_MAX, "%s/%s/block", SYSFS_BUSDIR, -+ bus_id); -+ fh_dir = opendir(dev_file); -+ if (!fh_dir) -+ ERR_EXIT_ERRNO("Could not open \"%s\"", dev_file); -+ while ((direntp = readdir(fh_dir))) -+ if (strncmp(direntp->d_name, "dasd", 4) == 0) -+ break; -+ closedir(fh_dir); -+ if (direntp == NULL) -+ ERR_EXIT("Problem with contents of \"%s\"", dev_file); -+ } -+ snprintf(tmp_file, PATH_MAX, "%s/%s/dev", dev_file, direntp->d_name); -+ fh = open(tmp_file, O_RDONLY); -+ if (read(fh, buf, sizeof(buf)) == -1) -+ ERR_EXIT_ERRNO("Could not read dev file"); -+ close(fh); -+ if (sscanf(buf, "%i:%i", &major, &minor) != 2) -+ ERR_EXIT("Malformed content of \"%s\": %s", tmp_file, buf); -+ *dev = makedev(major, minor); -+ return DEV_ONLINE; -+} -+ -+/* -+ * Check whether dump table on user specified dump device is -+ * identical to the one found on this device. -+ */ -+static void check_vol_table(struct vol *vol) -+{ -+ struct vol_parm_table vol_table; -+ -+ table_read(vol->fh, vol->blk_size, &vol_table); -+ if (memcmp(&vol_table, &l.table, sizeof(vol_table))) -+ ERR_EXIT("Orphaned multi-volume dump device '%s'", -+ g.opts.device); -+} -+ -+/* -+ * Read dump tool, multi-volume dump parameter table, and dump header from the -+ * input dump volume. Check input dump volume for: -+ * - identical dump parameter table (that is it belongs to the same dump set) -+ * - valid magic number in the dump tool -+ * - valid dump sign in the dump header -+ * -+ * We read partition data via the device node. If another process -+ * has changed partition data via the partition node, the corresponding -+ * device node might still have old data in its buffers. Flush buffers -+ * to keep things in sync. -+ */ -+void vol_read(struct vol *vol) -+{ -+ zg_ioctl(vol->fh, BLKFLSBUF, NULL, "BLKFLSBUF", ZG_CHECK); -+ df_s390_dumper_read(vol->fh, vol->blk_size, &vol->dumper); -+ check_vol_table(vol); -+ zg_seek(vol->fh, vol->part_off, ZG_CHECK); -+ zg_read(vol->fh, &vol->hdr, DF_S390_HDR_SIZE, ZG_CHECK); -+} -+ -+/* -+ * Read memory -+ */ -+static void df_s390mv_mem_read(struct dfi_mem_chunk *mem_chunk, u64 off, -+ void *buf, u64 cnt) -+{ -+ struct vol *vol = mem_chunk->data; -+ -+ zg_seek(vol->fh, vol->part_off + off + DF_S390_HDR_SIZE, ZG_CHECK); -+ zg_read(vol->fh, buf, cnt, ZG_CHECK); -+} -+ -+/* -+ * Initilize DASD volume -+ */ -+static void vol_init(struct vol *vol, struct vol_parm *vol_parm, u64 *mem_off) -+{ -+ u64 blk_cnt = vol_parm->end_blk - vol_parm->start_blk + 1; -+ -+ sprintf(vol->bus_id, "0.0.%04x", vol_parm->devno); -+ vol->blk_size = vol_parm->blk_size << 8; -+ vol->part_off = vol_parm->start_blk * vol->blk_size; -+ vol->part_size = blk_cnt * vol->blk_size; -+ vol->status = dev_from_busid(vol->bus_id, &vol->dev); -+ vol->sign = SIGN_VALID; -+ -+ if (vol->status != DEV_ONLINE) -+ return; -+ -+ vol->devnode = zg_devnode_create(vol->dev); -+ vol->fh = zg_open(vol->devnode, O_RDONLY, ZG_CHECK); -+ -+ vol_read(vol); -+ -+ if ((vol->hdr.volnr == vol->nr) && (vol->hdr.mem_size != 0)) -+ vol->sign = SIGN_ACTIVE; -+ -+ if (vol->hdr.mvdump_sign != DF_S390_MAGIC) { -+ vol->sign = SIGN_INVALID; -+ l.dump_incomplete = 1; -+ } -+ -+ if (strncmp(df_s390_dumper_magic(vol->dumper), "ZMULT64", 7) != 0) { -+ vol->sign = SIGN_INVALID; -+ l.dump_incomplete = 1; -+ } -+ -+ if (vol->nr == 0) -+ l.hdr = vol->hdr; -+ -+ if (*mem_off == l.hdr.mem_size) { -+ /* Unused volume */ -+ vol->mem_start = 0; -+ vol->mem_end = 0; -+ if (vol->sign == SIGN_ACTIVE) -+ vol->sign = SIGN_VALID; -+ } else { -+ /* Used volume */ -+ vol->mem_start = *mem_off; -+ vol->mem_end = *mem_off + PAGE_ALIGN(vol->part_size) - -+ DF_S390_HDR_SIZE - 1; -+ vol->mem_end = MIN(vol->mem_end, l.hdr.mem_size - 1); -+ if (vol->mem_end == l.hdr.mem_size - 1) -+ em_init(vol); -+ *mem_off += vol->mem_end - vol->mem_start + 1; -+ } -+} -+ -+/* -+ * Print volume information -+ */ -+static void vol_print(struct vol *vol) -+{ -+ STDERR(" Volume %i: %s (%s", vol->nr, vol->bus_id, -+ dev_status_str(vol->status)); -+ if (vol->status == DEV_ONLINE) -+ STDERR("/%s)\n", dev_sign_str(vol->sign)); -+ else -+ STDERR(")\n"); -+} -+ -+/* -+ * Print information for all volumes -+ */ -+static void vol_print_all(void) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < l.table.vol_cnt; i++) -+ vol_print(&l.vol_vec[i]); -+} -+ -+/* -+ * Add memory chunks -+ */ -+static void mem_chunks_add(void) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < l.table.vol_cnt; i++) { -+ struct vol *vol = &l.vol_vec[i]; -+ if (vol->sign != SIGN_ACTIVE) -+ continue; -+ dfi_mem_chunk_add(vol->mem_start, -+ vol->mem_end - vol->mem_start + 1, -+ vol, df_s390mv_mem_read); -+ } -+} -+ -+/* -+ * Print hint for setting all offline volumes online -+ */ -+static void vol_offline_msg(void) -+{ -+ unsigned int i, first = 1; -+ -+ STDERR("\n"); -+ STDERR("Set all devices online using:\n"); -+ STDERR("# chccwdev -e "); -+ for (i = 0; i < l.table.vol_cnt; i++) { -+ if (l.vol_vec[i].status == DEV_OFFLINE) { -+ if (first) -+ first = 0; -+ else -+ STDERR(","); -+ STDERR("%s", l.vol_vec[i].bus_id); -+ } -+ } -+ STDERR("\n"); -+} -+ -+/* -+ * Print error for all undefined volumes -+ */ -+static void vol_undefined_msg(void) -+{ -+ unsigned int i; -+ -+ STDERR("\n"); -+ STDERR("Ensure that the following devices are available to the" -+ "system:\n"); -+ for (i = 0; i < l.table.vol_cnt; i++) { -+ if (l.vol_vec[i].status == DEV_UNDEFINED) -+ STDERR("* %s\n", l.vol_vec[i].bus_id); -+ } -+} -+ -+/* -+ * Check that all volumes are in online state -+ */ -+static int vol_online_check(void) -+{ -+ unsigned int i, offline = 0, undefined = 0; -+ -+ for (i = 0; i < l.table.vol_cnt; i++) { -+ if (l.vol_vec[i].status == DEV_OFFLINE) -+ offline = 1; -+ if (l.vol_vec[i].status == DEV_UNDEFINED) -+ undefined = 1; -+ } -+ if (!offline && !undefined) -+ return 0; -+ -+ STDERR("Found multi-volume dump tool:\n\n"); -+ vol_print_all(); -+ if (offline) -+ vol_offline_msg(); -+ if (undefined) -+ vol_undefined_msg(); -+ return -ENODEV; -+} -+ -+/* -+ * Check if on device is a multi-volume dump -+ */ -+static int mvdump_hdr_check(const char *file) -+{ -+ struct df_s390_hdr hdr; -+ struct zg_fh *fh; -+ int rc = -ENODEV; -+ -+ fh = zg_open(file, O_RDONLY, ZG_CHECK); -+ zg_read(fh, &hdr, sizeof(hdr), ZG_CHECK); -+ if (hdr.magic != DF_S390_MAGIC) -+ goto fail; -+ if (hdr.mvdump_sign != DF_S390_MAGIC) -+ goto fail; -+ rc = 0; -+fail: -+ zg_close(fh); -+ return rc; -+} -+ -+/* -+ * Check if sysfs is available -+ */ -+static void check_sysfs(void) -+{ -+ DIR *fh_dir; -+ -+ fh_dir = opendir(SYSFS_BUSDIR); -+ if (!fh_dir) -+ ERR_EXIT_ERRNO("Could not open %s\n", SYSFS_BUSDIR); -+ closedir(fh_dir); -+} -+ -+/* -+ * Print dump information (dfi operation) -+ */ -+static void dfi_s390mvfo_dump(void) -+{ -+ vol_print_all(); -+} -+ -+/* -+ * Read dump tool from DASD and check if we have a multi-volume dump tool -+ */ -+static int mv_dumper_read(void) -+{ -+ if (zg_ioctl(g.fh, BLKSSZGET, &l.blk_size, "BLKSSZGET", -+ ZG_CHECK_NONE) == -1) -+ return -ENODEV; -+ df_s390_dumper_read(g.fh, l.blk_size, &l.dumper); -+ if (memcmp(df_s390_dumper_magic(l.dumper), "ZMULT64", 7) != 0) -+ return -ENODEV; -+ table_read(g.fh, l.blk_size, &l.table); -+ return 0; -+} -+ -+/* -+ * Initialize all volumes -+ */ -+static void volumes_init(void) -+{ -+ u64 mem_off = 0; -+ unsigned int i; -+ -+ check_sysfs(); -+ -+ for (i = 0; i < l.table.vol_cnt; i++) { -+ l.vol_vec[i].nr = i; -+ vol_init(&l.vol_vec[i], &l.table.vol_parm[i], &mem_off); -+ } -+ if (mem_off != l.hdr.mem_size) -+ l.dump_incomplete = 1; -+} -+ -+/* -+ * Open dump - If partition is specified open device instead -+ */ -+static int open_dump(void) -+{ -+ const struct stat *stat = zg_stat(g.fh); -+ unsigned dev_minor; -+ enum zg_type type; -+ char *path; -+ -+ type = zg_type(g.fh); -+ if (type != ZG_TYPE_DASD && type != ZG_TYPE_DASD_PART) -+ return -ENODEV; -+ -+ if (type == ZG_TYPE_DASD_PART) { -+ dev_minor = minor(stat->st_rdev) - (minor(stat->st_rdev) % 4); -+ if (mvdump_hdr_check(zg_path(g.fh)) != 0) -+ return -ENODEV; -+ path = zg_devnode_create(makedev(major(stat->st_rdev), -+ dev_minor)); -+ zg_close(g.fh); -+ g.fh = zg_open(path, O_RDONLY, ZG_CHECK); -+ } -+ if (mv_dumper_read() != 0) -+ return -ENODEV; -+ return 0; -+} -+ -+/* -+ * Initialize s390 multi-volume input dump format -+ */ -+static int dfi_s390mv_init(void) -+{ -+ if (open_dump() != 0) -+ return -ENODEV; -+ volumes_init(); -+ if (vol_online_check() != 0) -+ zg_exit(1); -+ if (l.hdr.mem_size == 0) -+ return -ENODEV; -+ df_s390_hdr_add(&l.hdr); -+ mem_chunks_add(); -+ if (l.dump_incomplete) -+ return -EINVAL; -+ df_s390_cpu_info_add(&l.hdr, l.hdr.mem_end); -+ df_s390_em_add(&l.em); -+ return 0; -+} -+ -+/* -+ * Initialize s390 multi-volume dump tool (for -d option) -+ */ -+int dt_s390mv_init(void) -+{ -+ if (open_dump() != 0) -+ return -ENODEV; -+ volumes_init(); -+ dt_arch_set(DFI_ARCH_64); -+ dt_version_set(df_s390_dumper_version(l.dumper)); -+ if (df_s390_dumper_mem(l.dumper) != U64_MAX) -+ dt_attr_mem_limit_set(df_s390_dumper_mem(l.dumper)); -+ -+ dt_attr_force_set(df_s390_dumper_force(l.dumper)); -+ return 0; -+} -+ -+/* -+ * s390 multi-volume dump tool info function (for -d option) -+ */ -+void dt_s390mv_info(void) -+{ -+ vol_print_all(); -+} -+ -+/* -+ * S390 multi-volume DFI operations -+ */ -+struct dfi dfi_s390mv = { -+ .name = "s390mv", -+ .init = dfi_s390mv_init, -+ .info_dump = dfi_s390mvfo_dump, -+ .feat_bits = DFI_FEAT_COPY | DFI_FEAT_SEEK, -+}; -diff --git a/zdump/dfi_s390tape.c b/zdump/dfi_s390tape.c -new file mode 100644 -index 0000000..8528cce ---- /dev/null -+++ b/zdump/dfi_s390tape.c -@@ -0,0 +1,198 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * S390 tape dump input format -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "zgetdump.h" -+ -+#define TAPE_BLK_SIZE 32768 /* Defined by zipl tape dumper */ -+ -+/* -+ * File local static data -+ * -+ * blk_buf_addr: Memory address of last read memory block -+ * blk_buf: Content of the last read memory block -+ * blk: The next block number that will be read relative to blk_start -+ * blk_start: The absolute block number on the tape where the dump starts -+ */ -+static struct { -+ char blk_buf[TAPE_BLK_SIZE]; -+ u64 blk_buf_addr; -+ u64 blk; -+ int blk_start; -+} l; -+ -+/* -+ * MT ioctls -+ */ -+struct mtioctl { -+ int op; -+ const char *desc; -+}; -+ -+static struct mtioctl mt_fsfm = {MTFSFM, "forward space file"}; -+static struct mtioctl mt_bsr = {MTBSR, "backward space record"}; -+static struct mtioctl mt_tell = {MTTELL, "tell"}; -+static struct mtioctl mt_seek = {MTSEEK, "seek"}; -+ -+/* -+ * Do MT ioctl with count argument -+ */ -+static int mtioctl(struct mtioctl *op, int cnt, enum zg_check check) -+{ -+ struct mtop mtop; -+ -+ mtop.mt_count = cnt; -+ mtop.mt_op = op->op; -+ return zg_ioctl(g.fh, MTIOCTOP, &mtop, op->desc, check); -+} -+ -+/* -+ * Verify end marker -+ */ -+static int em_verify(struct df_s390_em *em) -+{ -+ if ((memcmp(em->str, "DUMP_END", 8) == 0)) { -+ df_s390_em_add(em); -+ return 0; -+ } else { -+ return -EINVAL; -+ } -+} -+ -+/* -+ * Verify dump header -+ */ -+static void hdr_verify(struct df_s390_hdr *hdr) -+{ -+ if (hdr->magic != DF_S390_MAGIC) -+ ERR_EXIT("No valid dump found on tape"); -+ if (hdr->volnr != 0) { -+ STDERR_PR("Found volume number: %d\n", hdr->volnr); -+ ERR_EXIT("Multi-volume dumps are no longer supported"); -+ } -+} -+ -+/* -+ * Seek to relative block number in dump (block 0 is the dump header) -+ */ -+static void seek_blk(u64 blk) -+{ -+ if (l.blk == blk) -+ return; -+ mtioctl(&mt_seek, l.blk_start + blk, ZG_CHECK); -+ l.blk = blk; -+} -+ -+/* -+ * Read memory from cartridge -+ */ -+static void df_s390tape_mem_read(struct dfi_mem_chunk *mem_chunk, u64 addr, -+ void *buf, u64 cnt) -+{ -+ unsigned int copied = 0, size; -+ (void) mem_chunk; -+ u64 blk, off; -+ -+ do { -+ blk = addr / TAPE_BLK_SIZE + 1; -+ if (addr >= l.blk_buf_addr + TAPE_BLK_SIZE || -+ addr < l.blk_buf_addr) { -+ seek_blk(blk); -+ zg_read(g.fh, l.blk_buf, sizeof(l.blk_buf), -+ ZG_CHECK); -+ l.blk_buf_addr = (l.blk - 1) * TAPE_BLK_SIZE; -+ l.blk++; -+ } -+ off = addr - l.blk_buf_addr; -+ size = MIN(cnt - copied, TAPE_BLK_SIZE - off); -+ memcpy(buf + copied, &l.blk_buf[off], size); -+ addr += size; -+ copied += size; -+ } while (copied != cnt); -+} -+ -+/* -+ * Initialize cache for memory read (block 0 is the dump header) -+ */ -+static void mem_read_init(void) -+{ -+ mtioctl(&mt_seek, l.blk_start + 1, ZG_CHECK); -+ zg_read(g.fh, l.blk_buf, sizeof(l.blk_buf), ZG_CHECK); -+ l.blk_buf_addr = 0; -+ l.blk = 2; -+} -+ -+/* -+ * Init a new tape volume -+ */ -+static int vol_init(void) -+{ -+ struct df_s390_hdr hdr; -+ struct df_s390_em em; -+ int rc; -+ -+ STDERR("Checking tape, this can take a while...\n"); -+ /* Init dump header */ -+ l.blk_start = mtioctl(&mt_tell, 1, ZG_CHECK); -+ zg_read(g.fh, &hdr, sizeof(hdr), ZG_CHECK); -+ hdr_verify(&hdr); -+ df_s390_hdr_add(&hdr); -+ dfi_mem_chunk_add(0, hdr.mem_size, NULL, df_s390tape_mem_read); -+ -+ /* Init end marker */ -+ mtioctl(&mt_fsfm, 1, ZG_CHECK_NONE); -+ mtioctl(&mt_bsr, 1, ZG_CHECK); -+ rc = zg_read(g.fh, &em, sizeof(em), ZG_CHECK_ERR); -+ if (rc != 8 && rc != 16) -+ return -EINVAL; -+ if (em_verify(&em) != 0) -+ return -EINVAL; -+ -+ /* Init memory read & CPU info */ -+ mem_read_init(); -+ df_s390_cpu_info_add(&hdr, hdr.mem_size - 1); -+ return 0; -+} -+ -+/* -+ * Exit function: Seek to block 0 -+ */ -+static void dfi_s390tape_exit(void) -+{ -+ seek_blk(0); -+} -+ -+/* -+ * Initialize s390 tape DFI -+ */ -+static int dfi_s390tape_init(void) -+{ -+ if (zg_type(g.fh) != ZG_TYPE_TAPE) -+ return -ENODEV; -+ if (vol_init() != 0) -+ return -EINVAL; -+ zg_atexit(dfi_s390tape_exit); -+ return 0; -+} -+ -+/* -+ * S390 tape DFI operations -+ */ -+struct dfi dfi_s390tape = { -+ .name = "s390tape", -+ .init = dfi_s390tape_init, -+ .feat_bits = DFI_FEAT_SEEK | DFI_FEAT_COPY, -+}; -diff --git a/zdump/dfo.c b/zdump/dfo.c -new file mode 100644 -index 0000000..333f5f2 ---- /dev/null -+++ b/zdump/dfo.c -@@ -0,0 +1,204 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * Generic output dump format functions (DFO - Dump Format Output) -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include -+#include "zgetdump.h" -+ -+#define dfo_chunk_iterate(dfo_chunk) \ -+ list_iterate(dfo_chunk, &l.dump.chunk_list, list) -+ -+/* -+ * DFO vector -+ */ -+static struct dfo *dfo_vec[] = { -+ &dfo_s390, -+ &dfo_elf, -+ NULL, -+}; -+ -+/* -+ * Dump (output) information -+ */ -+struct dump { -+ u64 off; /* Current file offset in dump */ -+ u64 size; /* Size of dump in bytes */ -+ unsigned int chunk_cnt; /* Number of dump chunks */ -+ struct list chunk_list; /* DFO chunk list */ -+}; -+ -+/* -+ * File local static data -+ */ -+static struct { -+ struct dump dump; -+ struct dfo *dfo; -+} l; -+ -+/* -+ * Add dump chunk -+ */ -+void dfo_chunk_add(u64 start, u64 size, void *data, dfo_chunk_read_fn read_fn) -+{ -+ struct dfo_chunk *dfo_chunk; -+ -+ dfo_chunk = zg_alloc(sizeof(*dfo_chunk)); -+ dfo_chunk->start = start; -+ dfo_chunk->end = start + size - 1; -+ dfo_chunk->size = size; -+ dfo_chunk->data = data; -+ dfo_chunk->read_fn = read_fn; -+ list_add(&dfo_chunk->list, &l.dump.chunk_list); -+ l.dump.chunk_cnt++; -+ l.dump.size = MAX(l.dump.size, dfo_chunk->end + 1); -+} -+ -+/* -+ * Dump chunk function: Copy zero pages for chunk -+ */ -+void dfo_chunk_zero_fn(struct dfo_chunk *dfo_chunk, u64 off, void *buf, u64 cnt) -+{ -+ (void) dfo_chunk; -+ (void) off; -+ -+ memset(buf, 0, cnt); -+} -+ -+/* -+ * Dump chunk function: Copy given buffer for chunk -+ */ -+void dfo_chunk_buf_fn(struct dfo_chunk *dfo_chunk, u64 off, void *buf, u64 cnt) -+{ -+ memcpy(buf, dfo_chunk->data + off, cnt); -+} -+ -+/* -+ * Dump chunk function: Copy given memory range for chunk -+ */ -+void dfo_chunk_mem_fn(struct dfo_chunk *dfo_chunk, u64 off, void *buf, u64 cnt) -+{ -+ struct dfi_mem_chunk *mem_chunk = dfo_chunk->data; -+ -+ mem_chunk->read_fn(mem_chunk, off, buf, cnt); -+} -+ -+/* -+ * Get DFO name -+ */ -+const char *dfo_name(void) -+{ -+ return l.dfo->name; -+} -+ -+/* -+ * Set DFO by name -+ */ -+int dfo_set(const char *dfo_name) -+{ -+ struct dfo *dfo; -+ int i = 0; -+ -+ while ((dfo = dfo_vec[i])) { -+ if (strcmp(dfo->name, dfo_name) == 0) { -+ l.dfo = dfo; -+ return 0; -+ } -+ i++; -+ } -+ return -ENODEV; -+} -+ -+/* -+ * Initialize output dump format -+ */ -+void dfo_init(void) -+{ -+ if (!l.dfo) -+ ABORT("DFO not set"); -+ list_init(&l.dump.chunk_list); -+ l.dfo->init(); -+} -+ -+/* -+ * Find dump chunk for offset "off" -+ * -+ * This function is a bit hacky. DFO chunks can overlap. If two DFO chunks -+ * overlap, the last registered chunk wins. The dfo_chunk_find() function -+ * reflects that by returning the first memory chunk that is found in -+ * the dfo chunk list. -+ * -+ * In addition to that it calculates the "virtual end" of that chunk. An -+ * overlapping chunk can limit the "virtual end" of an underlying chunk so -+ * that the "virtual end" of that chunk is lower than the "real end". -+ * -+ * Example: -+ * -+ * chunk 1.: |------| -+ * chunk 2.: |---------------------| -+ * off.....: ^ -+ * virt end: ^ -+ * real end: ^ -+ * -+ * In this case chunk 2 will be returned and "end" is set to the start of -+ * chunk 1. -+ */ -+static struct dfo_chunk *dfo_chunk_find(u64 off, u64 *end) -+{ -+ struct dfo_chunk *dfo_chunk; -+ -+ *end = U64_MAX; -+ dfo_chunk_iterate(dfo_chunk) { -+ if (dfo_chunk->start <= off && dfo_chunk->end >= off) { -+ *end = MIN(*end, dfo_chunk->end); -+ return dfo_chunk; -+ } else if (dfo_chunk->start > off) { -+ *end = MIN(*end, dfo_chunk->start - 1); -+ } -+ } -+ return NULL; -+} -+ -+/* -+ * Seek to output dump offset "off" -+ */ -+void dfo_seek(u64 off) -+{ -+ l.dump.off = off; -+} -+ -+/* -+ * Read "cnt" bytes of output dump at current offest -+ */ -+u64 dfo_read(void *buf, u64 cnt) -+{ -+ struct dfo_chunk *dfo_chunk; -+ u64 copied = 0, end, size; -+ u64 off = l.dump.off; -+ -+ while (copied != cnt) { -+ dfo_chunk = dfo_chunk_find(off, &end); -+ if (!dfo_chunk) -+ goto out; -+ size = MIN(cnt - copied, end - off + 1); -+ dfo_chunk->read_fn(dfo_chunk, off - dfo_chunk->start, -+ buf + copied, size); -+ copied += size; -+ off += size; -+ } -+out: -+ l.dump.off = off; -+ return copied; -+} -+ -+/* -+ * Return output dump size -+ */ -+u64 dfo_size(void) -+{ -+ return l.dump.size; -+} -diff --git a/zdump/dfo.h b/zdump/dfo.h -new file mode 100644 -index 0000000..70ca318 ---- /dev/null -+++ b/zdump/dfo.h -@@ -0,0 +1,54 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * Generic output dump format functions (DFO - Dump Format Output) -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#ifndef DFO_H -+#define DFO_H -+ -+#include "list.h" -+#include "zg.h" -+ -+struct dfo_chunk; -+ -+typedef void (*dfo_chunk_read_fn)(struct dfo_chunk *chunk, u64 off, -+ void *buf, u64 cnt); -+ -+struct dfo_chunk { -+ struct list list; -+ u64 start; -+ u64 end; -+ u64 size; -+ dfo_chunk_read_fn read_fn; -+ void *data; -+}; -+ -+extern void dfo_chunk_zero_fn(struct dfo_chunk *chunk, u64 off, void *buf, -+ u64 cnt); -+extern void dfo_chunk_buf_fn(struct dfo_chunk *chunk, u64 off, void *buf, -+ u64 cnt); -+extern void dfo_chunk_mem_fn(struct dfo_chunk *chunk, u64 off, void *buf, -+ u64 cnt); -+extern void dfo_chunk_add(u64 start, u64 size, void *data, -+ dfo_chunk_read_fn read_fn); -+ -+extern u64 dfo_read(void *buf, u64 cnt); -+extern void dfo_seek(u64 addr); -+extern u64 dfo_size(void); -+extern const char *dfo_name(void); -+extern void dfo_init(void); -+extern int dfo_set(const char *dfo_name); -+ -+/* -+ * DFO operations -+ */ -+struct dfo { -+ const char *name; -+ void (*init)(void); -+}; -+ -+#endif /* DFO_H */ -diff --git a/zdump/dfo_elf.c b/zdump/dfo_elf.c -new file mode 100644 -index 0000000..bf3bc13 ---- /dev/null -+++ b/zdump/dfo_elf.c -@@ -0,0 +1,299 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * ELF core dump output format -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include "zgetdump.h" -+ -+#define HDR_PER_CPU_SIZE 0x200 -+#define HDR_PER_MEMC_SIZE 0x100 -+#define HDR_BASE_SIZE 0x2000 -+ -+/* -+ * File local static data -+ */ -+static struct { -+ void *hdr; -+ u32 hdr_size; -+} l; -+ -+/* -+ * Initialize ELF header -+ */ -+static void *ehdr_init(Elf64_Ehdr *ehdr) -+{ -+ memcpy(ehdr->e_ident, ELFMAG, SELFMAG); -+ ehdr->e_ident[EI_CLASS] = ELFCLASS64; -+ ehdr->e_ident[EI_DATA] = ELFDATA2MSB; -+ ehdr->e_ident[EI_VERSION] = EV_CURRENT; -+ ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV; -+ ehdr->e_ident[EI_ABIVERSION] = 0; -+ memset(ehdr->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD); -+ ehdr->e_type = ET_CORE; -+ ehdr->e_machine = EM_S390; -+ ehdr->e_version = EV_CURRENT; -+ ehdr->e_entry = 0; -+ ehdr->e_phoff = sizeof(Elf64_Ehdr); -+ ehdr->e_shoff = 0; -+ ehdr->e_flags = 0; -+ ehdr->e_ehsize = sizeof(Elf64_Ehdr); -+ ehdr->e_phentsize = sizeof(Elf64_Phdr); -+ ehdr->e_phnum = dfi_mem_chunk_cnt() + 1; -+ ehdr->e_shentsize = 0; -+ ehdr->e_shnum = 0; -+ ehdr->e_shstrndx = 0; -+ return ehdr + 1; -+} -+ -+/* -+ * Initialize ELF loads -+ */ -+static u64 loads_init(Elf64_Phdr *phdr, u64 loads_offset) -+{ -+ struct dfi_mem_chunk *mem_chunk; -+ u64 mem_size = 0; -+ -+ dfi_mem_chunk_iterate(mem_chunk) { -+ phdr->p_type = PT_LOAD; -+ phdr->p_offset = loads_offset; -+ phdr->p_vaddr = mem_chunk->start; -+ phdr->p_paddr = mem_chunk->start; -+ phdr->p_filesz = mem_chunk->end - mem_chunk->start + 1; -+ phdr->p_memsz = phdr->p_filesz; -+ phdr->p_flags = PF_R | PF_W | PF_X; -+ phdr->p_align = PAGE_SIZE; -+ loads_offset += phdr->p_filesz; -+ mem_size += phdr->p_memsz; -+ phdr++; -+ } -+ return mem_size; -+} -+ -+/* -+ * Initialize ELF note -+ */ -+static void *nt_init(void *buf, Elf64_Word type, void *desc, int d_len, -+ const char *name) -+{ -+ Elf64_Nhdr *note; -+ u64 len; -+ -+ note = (Elf64_Nhdr *)buf; -+ note->n_namesz = strlen(name) + 1; -+ note->n_descsz = d_len; -+ note->n_type = type; -+ len = sizeof(Elf64_Nhdr); -+ -+ memcpy(buf + len, name, note->n_namesz); -+ len = ROUNDUP(len + note->n_namesz, 4); -+ -+ memcpy(buf + len, desc, note->n_descsz); -+ len = ROUNDUP(len + note->n_descsz, 4); -+ -+ return PTR_ADD(buf, len); -+} -+ -+/* -+ * Initialize prstatus note -+ */ -+static void *nt_prstatus(void *ptr, struct dfi_cpu *cpu) -+{ -+ struct nt_prstatus_64 nt_prstatus; -+ static int cpu_nr = 1; -+ -+ memset(&nt_prstatus, 0, sizeof(nt_prstatus)); -+ memcpy(&nt_prstatus.gprs, cpu->gprs, sizeof(cpu->gprs)); -+ memcpy(&nt_prstatus.psw, cpu->psw, sizeof(cpu->psw)); -+ memcpy(&nt_prstatus.acrs, cpu->acrs, sizeof(cpu->acrs)); -+ nt_prstatus.pr_pid = cpu_nr; -+ cpu_nr++; -+ -+ return nt_init(ptr, NT_PRSTATUS, &nt_prstatus, sizeof(nt_prstatus), -+ "CORE"); -+} -+ -+/* -+ * Initialize fpregset (floating point) note -+ */ -+static void *nt_fpregset(void *ptr, struct dfi_cpu *cpu) -+{ -+ struct nt_fpregset_64 nt_fpregset; -+ -+ memset(&nt_fpregset, 0, sizeof(nt_fpregset)); -+ memcpy(&nt_fpregset.fpc, &cpu->fpc, sizeof(cpu->fpc)); -+ memcpy(&nt_fpregset.fprs, &cpu->fprs, sizeof(cpu->fprs)); -+ -+ return nt_init(ptr, NT_FPREGSET, &nt_fpregset, sizeof(nt_fpregset), -+ "CORE"); -+} -+ -+/* -+ * Initialize timer note -+ */ -+static void *nt_s390_timer(void *ptr, struct dfi_cpu *cpu) -+{ -+ return nt_init(ptr, NT_S390_TIMER, &cpu->timer, sizeof(cpu->timer), -+ "LINUX"); -+} -+ -+/* -+ * Initialize TOD clock comparator note -+ */ -+static void *nt_s390_tod_cmp(void *ptr, struct dfi_cpu *cpu) -+{ -+ return nt_init(ptr, NT_S390_TODCMP, &cpu->todcmp, -+ sizeof(cpu->todcmp), "LINUX"); -+} -+ -+/* -+ * Initialize TOD programmable register note -+ */ -+static void *nt_s390_tod_preg(void *ptr, struct dfi_cpu *cpu) -+{ -+ return nt_init(ptr, NT_S390_TODPREG, &cpu->todpreg, -+ sizeof(cpu->todpreg), "LINUX"); -+} -+ -+/* -+ * Initialize control register note -+ */ -+static void *nt_s390_ctrs(void *ptr, struct dfi_cpu *cpu) -+{ -+ return nt_init(ptr, NT_S390_CTRS, &cpu->ctrs, sizeof(cpu->ctrs), -+ "LINUX"); -+} -+ -+/* -+ * Initialize prefix register note -+ */ -+static void *nt_s390_prefix(void *ptr, struct dfi_cpu *cpu) -+{ -+ return nt_init(ptr, NT_S390_PREFIX, &cpu->prefix, -+ sizeof(cpu->prefix), "LINUX"); -+} -+ -+/* -+ * Initialize prpsinfo note -+ */ -+static void *nt_prpsinfo(void *ptr) -+{ -+ struct nt_prpsinfo_64 prpsinfo; -+ -+ memset(&prpsinfo, 0, sizeof(prpsinfo)); -+ prpsinfo.pr_state = 0; -+ prpsinfo.pr_sname = 'R'; -+ prpsinfo.pr_zomb = 0; -+ strcpy(prpsinfo.pr_fname, "vmlinux"); -+ -+ return nt_init(ptr, NT_PRPSINFO, &prpsinfo, sizeof(prpsinfo), "CORE"); -+} -+ -+/* -+ * Initialize notes -+ */ -+static void *notes_init(Elf64_Phdr *phdr, void *ptr, u64 notes_offset) -+{ -+ void *ptr_start = ptr; -+ struct dfi_cpu *cpu; -+ -+ ptr = nt_prpsinfo(ptr); -+ -+ if (dfi_cpu_content() != DFI_CPU_CONTENT_ALL) -+ goto out; -+ -+ dfi_cpu_iterate(cpu) { -+ ptr = nt_prstatus(ptr, cpu); -+ ptr = nt_fpregset(ptr, cpu); -+ ptr = nt_s390_timer(ptr, cpu); -+ ptr = nt_s390_tod_cmp(ptr, cpu); -+ ptr = nt_s390_tod_preg(ptr, cpu); -+ ptr = nt_s390_ctrs(ptr, cpu); -+ ptr = nt_s390_prefix(ptr, cpu); -+ } -+out: -+ memset(phdr, 0, sizeof(*phdr)); -+ phdr->p_type = PT_NOTE; -+ phdr->p_offset = notes_offset; -+ phdr->p_filesz = (unsigned long) PTR_SUB(ptr, ptr_start); -+ return ptr; -+} -+ -+/* -+ * Setup dump chunks -+ */ -+static void dump_chunks_init(void) -+{ -+ struct dfi_mem_chunk *mem_chunk; -+ u64 off = 0; -+ -+ dfo_chunk_add(0, l.hdr_size, l.hdr, dfo_chunk_buf_fn); -+ off = l.hdr_size; -+ dfi_mem_chunk_iterate(mem_chunk) { -+ dfo_chunk_add(off, mem_chunk->size, mem_chunk, -+ dfo_chunk_mem_fn); -+ off += mem_chunk->size; -+ } -+} -+ -+/* -+ * ELF DFO is only supported for 64 bit (s390x) -+ */ -+static void ensure_s390x(void) -+{ -+ if (dfi_arch() != DFI_ARCH_64) -+ ERR_EXIT("Error: The ELF dump format is only supported for " -+ "s390x source dumps"); -+ df_elf_ensure_s390x(); -+} -+ -+/* -+ * Initialize ELF output dump format -+ */ -+static void dfo_elf_init(void) -+{ -+ Elf64_Phdr *phdr_notes, *phdr_loads; -+ u64 mem_size, hdr_off; -+ u32 alloc_size; -+ void *ptr; -+ -+ ensure_s390x(); -+ alloc_size = HDR_BASE_SIZE + -+ dfi_cpu_cnt() * HDR_PER_CPU_SIZE + -+ dfi_mem_chunk_cnt() * HDR_PER_MEMC_SIZE; -+ l.hdr = zg_alloc(alloc_size); -+ /* Init elf header */ -+ ptr = ehdr_init(l.hdr); -+ /* Init program headers */ -+ phdr_notes = ptr; -+ ptr = PTR_ADD(ptr, sizeof(Elf64_Phdr)); -+ phdr_loads = ptr; -+ ptr = PTR_ADD(ptr, sizeof(Elf64_Phdr) * dfi_mem_chunk_cnt()); -+ /* Init notes */ -+ hdr_off = PTR_DIFF(ptr, l.hdr); -+ ptr = notes_init(phdr_notes, ptr, hdr_off); -+ /* Init loads */ -+ hdr_off = PTR_DIFF(ptr, l.hdr); -+ mem_size = loads_init(phdr_loads, hdr_off); -+ l.hdr_size = hdr_off; -+ if (l.hdr_size > alloc_size) -+ ABORT("hdr_size=%u alloc_size=%u", l.hdr_size, alloc_size); -+ dump_chunks_init(); -+} -+ -+/* -+ * ELF DFO operations -+ */ -+struct dfo dfo_elf = { -+ .name = "elf", -+ .init = dfo_elf_init, -+}; -diff --git a/zdump/dfo_s390.c b/zdump/dfo_s390.c -new file mode 100644 -index 0000000..7f51605 ---- /dev/null -+++ b/zdump/dfo_s390.c -@@ -0,0 +1,200 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * S390 dump output format -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "zgetdump.h" -+ -+/* -+ * File local static data -+ */ -+struct { -+ struct df_s390_hdr hdr; -+ struct df_s390_em em; -+} l; -+ -+/* -+ * Copy internal register set to 64 bit lowcore -+ */ -+static void cpu2lc_64(void *lc_64, struct dfi_cpu *cpu) -+{ -+ struct dfi_lowcore_64 *lc = lc_64; -+ memcpy(lc->gpregs_save_area, &cpu->gprs, sizeof(cpu->gprs)); -+ memcpy(lc->cregs_save_area, &cpu->ctrs, sizeof(cpu->ctrs)); -+ memcpy(lc->access_regs_save_area, &cpu->acrs, sizeof(cpu->acrs)); -+ memcpy(lc->floating_pt_save_area, &cpu->fprs, sizeof(cpu->fprs)); -+ memcpy(&lc->fpt_creg_save_area, &cpu->fpc, sizeof(cpu->fpc)); -+ memcpy(lc->st_status_fixed_logout, &cpu->psw, sizeof(cpu->psw)); -+ memcpy(&lc->prefixreg_save_area, &cpu->prefix, sizeof(cpu->prefix)); -+ memcpy(lc->timer_save_area, &cpu->timer, sizeof(cpu->timer)); -+ memcpy(lc->clock_comp_save_area, &cpu->todcmp, sizeof(cpu->todcmp)); -+} -+ -+/* -+ * Copy internal register set to 32 bit lowcore -+ */ -+static void cpu2lc_32(void *lc_32, struct dfi_cpu *cpu_64) -+{ -+ struct dfi_lowcore_32 *lc = lc_32; -+ struct dfi_cpu_32 cpu; -+ -+ dfi_cpu_64_to_32(&cpu, cpu_64); -+ memcpy(lc->gpregs_save_area, &cpu.gprs, sizeof(cpu.gprs)); -+ memcpy(lc->cregs_save_area, &cpu.ctrs, sizeof(cpu.ctrs)); -+ memcpy(lc->access_regs_save_area, &cpu.acrs, sizeof(cpu.acrs)); -+ memcpy(lc->floating_pt_save_area, &cpu.fprs, sizeof(cpu.fprs)); -+ memcpy(lc->st_status_fixed_logout, &cpu.psw, sizeof(cpu.psw)); -+ memcpy(&lc->prefixreg_save_area, &cpu.prefix, sizeof(cpu.prefix)); -+ memcpy(lc->timer_save_area, &cpu.timer, sizeof(cpu.timer)); -+ memcpy(lc->clock_comp_save_area, &cpu.todcmp, sizeof(cpu.todcmp)); -+} -+ -+/* -+ * Convert timeval to s390 TOD clock -+ */ -+static void timeval2tod(u64 *tod, struct timeval *xtime) -+{ -+ u64 us = xtime->tv_sec * 1000000 + xtime->tv_usec; -+ *tod = (us << 12); -+ *tod += 0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096); -+} -+ -+/* -+ * Setup lowcore array in dump header -+ */ -+static void lc_setup(struct df_s390_hdr *dh) -+{ -+ struct dfi_cpu *cpu; -+ unsigned int i = 0; -+ -+ dfi_cpu_iterate(cpu) { -+ if (i > DF_S390_CPU_MAX) -+ ERR_EXIT("Too many CPUs in source dump (%i)", i); -+ dh->lc_vec[i] = cpu->prefix; -+ i++; -+ } -+} -+ -+/* -+ * Copy register set to prefix page -+ */ -+static void dfo_s390_dump_chunk_lc_fn(struct dfo_chunk *dump_chunk, -+ u64 off, void *buf, u64 cnt) -+{ -+ struct dfi_cpu *cpu = dump_chunk->data; -+ char lc[0x2000]; -+ -+ dfi_mem_read(cpu->prefix + off, &lc[off], cnt); -+ if (dfi_arch() == DFI_ARCH_64) -+ cpu2lc_64(lc, cpu); -+ else -+ cpu2lc_32(lc, cpu); -+ memcpy(buf, &lc[off], cnt); -+} -+ -+/* -+ * Add register set to dump layout. We copy the register sets to the -+ * lowcore pages. -+ */ -+static void add_cpu_to_dfo(struct dfi_cpu *cpu) -+{ -+ if (dfi_cpu_content() != DFI_CPU_CONTENT_ALL) -+ return; -+ -+ dfo_chunk_add(cpu->prefix + DF_S390_HDR_SIZE, -+ dfi_lc_size(dfi_arch()), cpu, -+ dfo_s390_dump_chunk_lc_fn); -+} -+ -+/* -+ * Add memory chunk to dump layout -+ */ -+static void add_mem_chunk_to_dfo(struct dfi_mem_chunk *mem_chunk) -+{ -+ struct dfi_mem_chunk *mem_chunk_prev = dfi_mem_chunk_prev(mem_chunk); -+ -+ if (mem_chunk_prev && (mem_chunk_prev->end + 1 != mem_chunk->start)) -+ dfo_chunk_add(mem_chunk_prev->end + 1 + DF_S390_HDR_SIZE, -+ mem_chunk->start - mem_chunk_prev->end - 1, -+ NULL, dfo_chunk_zero_fn); -+ -+ dfo_chunk_add(mem_chunk->start + DF_S390_HDR_SIZE, mem_chunk->size, -+ mem_chunk, dfo_chunk_mem_fn); -+} -+ -+/* -+ * Setup dump chunks -+ */ -+static void dump_chunks_init(void) -+{ -+ struct dfi_mem_chunk *mem_chunk; -+ struct dfi_cpu *cpu; -+ -+ dfo_chunk_add(0, DF_S390_HDR_SIZE, &l.hdr, dfo_chunk_buf_fn); -+ dfi_mem_chunk_iterate(mem_chunk) -+ add_mem_chunk_to_dfo(mem_chunk); -+ dfi_cpu_iterate(cpu) -+ add_cpu_to_dfo(cpu); -+ dfo_chunk_add(dfi_mem_range() + DF_S390_HDR_SIZE, -+ DF_S390_EM_SIZE, -+ &l.em, dfo_chunk_buf_fn); -+} -+ -+/* -+ * Initialize s390 output dump format -+ */ -+static void df_s390_dump_init(void) -+{ -+ struct df_s390_hdr *dh = &l.hdr; -+ struct df_s390_em *em = &l.em; -+ -+ dh->magic = DF_S390_MAGIC; -+ dh->hdr_size = DF_S390_HDR_SIZE; -+ dh->page_size = PAGE_SIZE; -+ dh->dump_level = 4; -+ if (dfi_cpu_content() == DFI_CPU_CONTENT_NONE) -+ dh->version = 4; -+ else -+ dh->version = 5; -+ dh->mem_start = 0; -+ dh->mem_size = dh->mem_end = dfi_mem_range(); -+ dh->num_pages = dh->mem_size / PAGE_SIZE; -+ dh->arch = df_s390_from_dfi_arch(dfi_arch()); -+ if (dfi_attr_build_arch()) -+ dh->build_arch = df_s390_from_dfi_arch(*dfi_attr_build_arch()); -+ dh->cpu_cnt = dfi_cpu_cnt(); -+ if (dfi_attr_real_cpu_cnt()) -+ dh->real_cpu_cnt = *dfi_attr_real_cpu_cnt(); -+ if (dfi_attr_cpu_id()) -+ dh->cpu_id = *dfi_attr_cpu_id(); -+ if (dfi_attr_mem_size_real()) -+ dh->mem_size_real = *dfi_attr_mem_size_real(); -+ if (dfi_attr_time()) { -+ timeval2tod(&dh->tod, dfi_attr_time()); -+ timeval2tod(&em->tod, dfi_attr_time()); -+ } -+ if (dfi_attr_time_end()) -+ timeval2tod(&em->tod, dfi_attr_time_end()); -+ lc_setup(dh); -+ memcpy(em->str, DF_S390_EM_STR, sizeof(em->str)); -+ dump_chunks_init(); -+} -+ -+/* -+ * S390 DFO operations -+ */ -+struct dfo dfo_s390 = { -+ .name = "s390", -+ .init = df_s390_dump_init, -+}; -diff --git a/zdump/dt.c b/zdump/dt.c -new file mode 100644 -index 0000000..b19aa80 ---- /dev/null -+++ b/zdump/dt.c -@@ -0,0 +1,131 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * Dump tool info generic functions -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include "zgetdump.h" -+ -+/* -+ * Supported dump tools -+ */ -+static struct dt *dt_vec[] = { -+ &dt_s390mv, -+ &dt_s390sv, -+ NULL, -+}; -+ -+/* -+ * Dumper attribute information -+ */ -+struct attr { -+ int *force; -+ u64 *mem_limit; -+ char *dasd_type; -+}; -+ -+/* -+ * File local static data -+ */ -+struct { -+ int version; -+ enum dfi_arch arch; -+ struct attr attr; -+ struct dt *dt; -+} l; -+ -+/* -+ * Init dump tool backends -+ */ -+void dt_init(void) -+{ -+ struct dt *dt; -+ int i = 0; -+ -+ while ((dt = dt_vec[i])) { -+ g.fh = zg_open(g.opts.device, O_RDONLY, ZG_CHECK); -+ if (zg_type(g.fh) != ZG_TYPE_DASD) -+ ERR_EXIT("Please specify DASD device node (e.g. " -+ "/dev/dasdd)"); -+ if (dt->init() == 0) { -+ l.dt = dt; -+ return; -+ } -+ zg_close(g.fh); -+ i++; -+ } -+ ERR_EXIT("No dump tool found on \"%s\"", g.opts.device); -+} -+ -+/* -+ * Print info about dump tool -+ */ -+void dt_info_print(void) -+{ -+ STDERR("Dump device info:\n"); -+ STDERR(" Dump tool.........: %s\n", l.dt->desc); -+ STDERR(" Version...........: %d\n", l.version); -+ STDERR(" Architecture......: %s\n", dfi_arch_str(l.arch)); -+ if (l.attr.dasd_type) -+ STDERR(" DASD type.........: %s\n", l.attr.dasd_type); -+ if (l.attr.mem_limit) -+ STDERR(" Dump size limit...: %lld MB\n", -+ TO_MIB(*l.attr.mem_limit)); -+ else -+ STDERR(" Dump size limit...: none\n"); -+ if (l.attr.force) { -+ if (*l.attr.force == 0) -+ STDERR(" Force specified...: no\n"); -+ else -+ STDERR(" Force specified...: yes\n"); -+ } -+ if (l.dt->info) { -+ STDERR("\n"); -+ l.dt->info(); -+ } -+} -+ -+/* -+ * Set DT architecture -+ */ -+void dt_arch_set(enum dfi_arch arch) -+{ -+ l.arch = arch; -+} -+ -+/* -+ * Set DT version -+ */ -+void dt_version_set(int version) -+{ -+ l.version = version; -+} -+ -+/* -+ * Set DT memory limit attribute -+ */ -+void dt_attr_mem_limit_set(u64 mem_limit) -+{ -+ l.attr.mem_limit = zg_alloc(sizeof(*l.attr.mem_limit)); -+ *l.attr.mem_limit = mem_limit; -+} -+ -+/* -+ * Set DT force attribute -+ */ -+void dt_attr_force_set(int force) -+{ -+ l.attr.force = zg_alloc(sizeof(*l.attr.force)); -+ *l.attr.force = force; -+} -+ -+/* -+ * Set DT DASD type attribute -+ */ -+void dt_attr_dasd_type_set(const char *dasd_type) -+{ -+ l.attr.dasd_type = zg_strdup(dasd_type); -+} -diff --git a/zdump/dt.h b/zdump/dt.h -new file mode 100644 -index 0000000..44e0a09 ---- /dev/null -+++ b/zdump/dt.h -@@ -0,0 +1,29 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * Dump tool info generic functions -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#ifndef DT_H -+#define DT_H -+ -+#include "dfi.h" -+ -+struct dt { -+ const char *desc; -+ int (*init)(void); -+ void (*info)(void); -+}; -+ -+extern void dt_init(void); -+extern void dt_info_print(void); -+extern void dt_arch_set(enum dfi_arch arch); -+extern void dt_version_set(int version); -+extern void dt_attr_mem_limit_set(u64 mem_limit); -+extern void dt_attr_force_set(int value); -+extern void dt_attr_dasd_type_set(const char *dasd_type); -+ -+#endif -diff --git a/zdump/dt_s390mv.c b/zdump/dt_s390mv.c -new file mode 100644 -index 0000000..17ac1fc ---- /dev/null -+++ b/zdump/dt_s390mv.c -@@ -0,0 +1,19 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * S390 multi-volume DASD dump tool -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include "zgetdump.h" -+ -+/* -+ * DT operations -+ */ -+struct dt dt_s390mv = { -+ .desc = "Multi-volume DASD dump tool", -+ .init = dt_s390mv_init, -+ .info = dt_s390mv_info, -+}; -diff --git a/zdump/dt_s390sv.c b/zdump/dt_s390sv.c -new file mode 100644 -index 0000000..22966cd ---- /dev/null -+++ b/zdump/dt_s390sv.c -@@ -0,0 +1,129 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * S390 single-volume DASD dump tool -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include -+#include "zgetdump.h" -+ -+#define HEXINSTR "\x0d\x10\x47\xf0" /* BASR + 1st halfword of BC */ -+ -+/* -+ * File local static data -+ */ -+static struct { -+ struct df_s390_dumper dumper; -+ enum dfi_arch dumper_arch; -+} l; -+ -+/* -+ * Read dump tool from ECKD DASD device -+ */ -+static int dumper_read_eckd(int blk_size) -+{ -+ df_s390_dumper_read(g.fh, blk_size, &l.dumper); -+ -+ if (strncmp(l.dumper.magic, "ZECKD31", 7) == 0) { -+ l.dumper_arch = DFI_ARCH_32; -+ } else if (strncmp(l.dumper.magic, "ZECKD64", 7) == 0) { -+ l.dumper_arch = DFI_ARCH_64; -+ } else if ((memcmp(l.dumper.magic, HEXINSTR, 4) == 0) && -+ (l.dumper.d.v1.code[0] == '\x0d') && -+ (l.dumper.d.v1.code[1] == '\xd0')) { -+ /* We found basr r13,0 (old dumper) */ -+ l.dumper.version = 0; -+ l.dumper_arch = DFI_ARCH_UNKNOWN; -+ } else { -+ return -ENODEV; -+ } -+ return 0; -+} -+ -+/* -+ * Read dump tool from FBA DASD device -+ */ -+static void dumper_read_fba_gen(int size, void *buffer) -+{ -+ zg_seek_end(g.fh, -size, ZG_CHECK); -+ zg_read(g.fh, buffer, size, ZG_CHECK); -+} -+ -+/* -+ * Read dump tool on FBA disk and check its magic number -+ */ -+int dumper_check_fba(void) -+{ -+ if (strncmp(l.dumper.magic, "ZDFBA31", 7) == 0) { -+ l.dumper_arch = DFI_ARCH_32; -+ } else if (strncmp(l.dumper.magic, "ZDFBA64", 7) == 0) { -+ l.dumper_arch = DFI_ARCH_64; -+ } else if ((memcmp(l.dumper.magic, HEXINSTR, 4) == 0) && -+ (l.dumper.d.v1.code[0] == '\x0d') && -+ (l.dumper.d.v1.code[1] == '\xd0')) { -+ /* We found basr r13,0 (old dumper) */ -+ l.dumper.version = 0; -+ l.dumper_arch = DFI_ARCH_UNKNOWN; -+ } else { -+ return -ENODEV; -+ } -+ return 0; -+} -+ -+/* -+ * Read dump tool on FBA disk and check its magic number -+ */ -+static int dumper_read_fba(void) -+{ -+ dumper_read_fba_gen(DF_S390_DUMPER_SIZE_V1, &l.dumper); -+ if (dumper_check_fba() == 0) -+ return 0; -+ dumper_read_fba_gen(DF_S390_DUMPER_SIZE_V2, &l.dumper); -+ if (dumper_check_fba() == 0) -+ return 0; -+ return -ENODEV; -+} -+ -+/* -+ * Read single volume dumper from disk -+ */ -+static int sv_dumper_read(void) -+{ -+ int blk_size; -+ -+ zg_ioctl(g.fh, BLKSSZGET, &blk_size, "BLKSSZGET", ZG_CHECK); -+ if (dumper_read_eckd(blk_size) == 0) { -+ dt_attr_dasd_type_set("ECKD"); -+ return 0; -+ } -+ if (dumper_read_fba() == 0) { -+ dt_attr_dasd_type_set("FBA"); -+ return 0; -+ } -+ return -ENODEV; -+} -+ -+/* -+ * Initialize s390 single-volume dump tool (for -d option) -+ */ -+static int dt_s390sv_init(void) -+{ -+ if (sv_dumper_read() != 0) -+ return -ENODEV; -+ dt_arch_set(l.dumper_arch); -+ dt_version_set(df_s390_dumper_version(l.dumper)); -+ if (df_s390_dumper_mem(l.dumper) != U64_MAX) -+ dt_attr_mem_limit_set(df_s390_dumper_mem(l.dumper)); -+ return 0; -+} -+ -+/* -+ * s390 single-volume DT operations -+ */ -+struct dt dt_s390sv = { -+ .desc = "Single-volume DASD dump tool", -+ .init = dt_s390sv_init, -+}; -diff --git a/zdump/opts.c b/zdump/opts.c -new file mode 100644 -index 0000000..d00f5b1 ---- /dev/null -+++ b/zdump/opts.c -@@ -0,0 +1,242 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * Option parsing -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include -+#include -+#include -+#include -+#include "zgetdump.h" -+#include "zt_common.h" -+ -+/* -+ * Text for --help option -+ */ -+static char help_text[] = -+"Usage: zgetdump [OPTIONS] [DUMPDEV] [DIR]\n" -+"\n" -+"The zgetdump tool takes as source a dump device or dump file (DUMPDEV)\n" -+"and writes its contents to standard output, which you can redirect to a\n" -+"specific file. Alternatively you can also mount the dump content.\n" -+"Because zgetdump is able to read and write different dump formats, it\n" -+"can be used to convert a dump from one format to another. zgetdump can\n" -+"also verify if a dump is valid or check, whether a DASD device contains\n" -+"a valid dump tool.\n" -+"\n" -+"-h, --help Print this help, then exit.\n" -+"-v, --version Print version information, then exit.\n" -+"-m, --mount Mount dump to mount point DIR\n" -+"-u, --umount Unmount dump from mount point DIR\n" -+"-f, --fmt Specify target dump format FMT (\"elf\" or \"s390\")\n" -+"-i, --info Print dump information\n" -+"-d, --device Print dump device information\n"; -+ -+static const char copyright_str[] = "Copyright IBM Corp. 2001, 2010"; -+ -+/* -+ * Initialize default settings -+ */ -+static void init_defaults(void) -+{ -+ g.prog_name = "zgetdump"; -+ g.opts.action = ZG_ACTION_STDOUT; -+ g.opts.fmt = "s390"; -+ dfo_set(g.opts.fmt); -+} -+ -+/* -+ * Print "help" hint -+ */ -+static void print_usage_exit(void) -+{ -+ STDERR("Try '%s --help' for more information.\n", g.prog_name); -+ zg_exit(1); -+} -+ -+/* -+ * Print help text -+ */ -+static void print_help_exit(void) -+{ -+ STDOUT(help_text); -+ zg_exit(0); -+} -+ -+/* -+ * Print version information -+ */ -+static void print_version_exit(void) -+{ -+ STDOUT("%s: Tool for copying and converting dumps version %s\n", -+ g.prog_name, RELEASE_STRING); -+ STDOUT("%s\n", copyright_str); -+ zg_exit(0); -+} -+ -+/* -+ * Set "--fmt" option -+ */ -+static void fmt_set(const char *fmt) -+{ -+ if (dfo_set(fmt) != 0) -+ ERR_EXIT("Invalid target format \"%s\" specified", fmt); -+ g.opts.fmt_specified = 1; -+ g.opts.fmt = fmt; -+} -+ -+/* -+ * Set mount point -+ */ -+static void mount_point_set(const char *mount_point) -+{ -+ g.opts.mount_point = zg_strdup(mount_point); -+} -+ -+/* -+ * Set device -+ */ -+static void device_set(const char *path) -+{ -+ g.opts.device = zg_strdup(path); -+} -+ -+/* -+ * Set FUSE debug options -+ */ -+static void argv_fuse_set(char **argv, int argc) -+{ -+ int i; -+ -+ g.opts.argv_fuse = argv; -+ g.opts.argc_fuse = argc; -+ -+ STDERR_PR("Fuse Options: "); -+ for (i = 0; i < argc; i++) -+ STDERR("%s ", g.opts.argv_fuse[i]); -+ STDERR("\n"); -+} -+ -+/* -+ * Set action -+ */ -+static void action_set(enum zg_action action) -+{ -+ if (g.opts.action_specified) -+ ERR_EXIT("Please specifiy only one of the \"-i\", \"-d\", " -+ "\"-m\" or \"-u\" option"); -+ g.opts.action = action; -+ g.opts.action_specified = 1; -+} -+ -+/* -+ * Verify option combinations -+ */ -+static void verify_opts(void) -+{ -+ if (!g.opts.fmt_specified) -+ return; -+ -+ if (g.opts.action == ZG_ACTION_DUMP_INFO) -+ ERR_EXIT("The \"--fmt\" option cannot be specified " -+ "together with \"--info\""); -+ if (g.opts.action == ZG_ACTION_DEVICE_INFO) -+ ERR_EXIT("The \"--fmt\" option cannot be specified " -+ "together with \"--device\""); -+ if (g.opts.action == ZG_ACTION_UMOUNT) -+ ERR_EXIT("The \"--fmt\" option cannot be specified " -+ "together with \"--umount\""); -+} -+ -+/* -+ * Parse positional arguments -+ */ -+static void parse_pos_args(char *argv[], int argc) -+{ -+ int pos_args = argc - optind; -+ -+ switch (g.opts.action) { -+ case ZG_ACTION_STDOUT: -+ case ZG_ACTION_DUMP_INFO: -+ case ZG_ACTION_DEVICE_INFO: -+ if (pos_args == 0) -+ ERR_EXIT("No device or dump specified"); -+ if (pos_args > 1 && !g.opts.debug_specified) -+ ERR_EXIT("Too many positional paramters specified"); -+ device_set(argv[optind]); -+ break; -+ case ZG_ACTION_MOUNT: -+ if (pos_args == 0) -+ ERR_EXIT("No dump specified"); -+ if (pos_args == 1) -+ ERR_EXIT("No mount point specified"); -+ if (pos_args > 2 && !g.opts.debug_specified) -+ ERR_EXIT("Too many positional paramters specified"); -+ device_set(argv[optind]); -+ mount_point_set(argv[optind + 1]); -+ if (g.opts.debug_specified && pos_args > 2) -+ argv_fuse_set(&argv[optind + 2], pos_args - 2); -+ break; -+ case ZG_ACTION_UMOUNT: -+ if (pos_args == 0) -+ ERR_EXIT("No mount point specified"); -+ mount_point_set(argv[optind]); -+ break; -+ } -+} -+ -+/* -+ * Main command line parsing function -+ */ -+void opts_parse(int argc, char *argv[]) -+{ -+ int opt, idx; -+ static struct option long_opts[] = { -+ {"help", no_argument, NULL, 'h'}, -+ {"version", no_argument, NULL, 'v'}, -+ {"info", no_argument, NULL, 'i'}, -+ {"device", no_argument, NULL, 'd'}, -+ {"mount", no_argument, NULL, 'm'}, -+ {"umount", no_argument, NULL, 'u'}, -+ {"fmt", required_argument, NULL, 'f'}, -+ {"debug", no_argument, NULL, 'X'}, -+ {0, 0, 0, 0 } -+ }; -+ static const char optstr[] = "hvidmuf:X"; -+ -+ init_defaults(); -+ while ((opt = getopt_long(argc, argv, optstr, long_opts, &idx)) != -1) { -+ switch (opt) { -+ case 'h': -+ print_help_exit(); -+ case 'v': -+ print_version_exit(); -+ case 'i': -+ action_set(ZG_ACTION_DUMP_INFO); -+ break; -+ case 'd': -+ action_set(ZG_ACTION_DEVICE_INFO); -+ break; -+ case 'm': -+ action_set(ZG_ACTION_MOUNT); -+ break; -+ case 'u': -+ action_set(ZG_ACTION_UMOUNT); -+ break; -+ case 'f': -+ fmt_set(optarg); -+ break; -+ case 'X': -+ g.opts.debug_specified = 1; -+ break; -+ default: -+ print_usage_exit(); -+ } -+ } -+ parse_pos_args(argv, argc); -+ verify_opts(); -+} -diff --git a/zdump/stdout.c b/zdump/stdout.c -new file mode 100644 -index 0000000..1c3722d ---- /dev/null -+++ b/zdump/stdout.c -@@ -0,0 +1,38 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * Write dump to standard output (stdout) -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include "zgetdump.h" -+ -+int stdout_write_dump(void) -+{ -+ u64 cnt, written = 0; -+ char buf[32768]; -+ ssize_t rc; -+ -+ if (!dfi_feat_copy()) -+ ERR_EXIT("Copying not possible for %s dumps", dfi_name()); -+ STDERR("Format Info:\n"); -+ STDERR(" Source: %s\n", dfi_name()); -+ STDERR(" Target: %s\n", dfo_name()); -+ STDERR("\n"); -+ zg_progress_init("Copying dump", dfo_size()); -+ do { -+ cnt = dfo_read(buf, sizeof(buf)); -+ rc = write(STDOUT_FILENO, buf, cnt); -+ if (rc == -1) -+ ERR_EXIT_ERRNO("Error: Write failed"); -+ if (rc != (ssize_t) cnt) -+ ERR_EXIT("Error: Could not write full block"); -+ written += cnt; -+ zg_progress(written); -+ } while (written != dfo_size()); -+ STDERR("\n"); -+ STDERR("Success: Dump has been copied\n"); -+ return 0; -+} -diff --git a/zdump/zfuse.c b/zdump/zfuse.c -new file mode 100644 -index 0000000..d86e1c0 ---- /dev/null -+++ b/zdump/zfuse.c -@@ -0,0 +1,238 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * FUSE functions -+ * -+ * Copyright IBM Corp. 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#define FUSE_USE_VERSION 25 -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "zgetdump.h" -+ -+#define DUMP_PATH_MAX 100 -+ -+/* -+ * File local static data -+ */ -+static struct { -+ char path[DUMP_PATH_MAX]; -+ struct stat stat_root; -+ struct stat stat_dump; -+} l; -+ -+/* -+ * Initialize default values for stat buffer -+ */ -+static void stat_default_init(struct stat *stat) -+{ -+ if (dfi_attr_time()) { -+ stat->st_mtime = dfi_attr_time()->tv_sec; -+ stat->st_ctime = dfi_attr_time()->tv_sec; -+ stat->st_atime = dfi_attr_time()->tv_sec; -+ } else { -+ stat->st_mtime = zg_stat(g.fh)->st_mtime; -+ stat->st_ctime = zg_stat(g.fh)->st_ctime; -+ stat->st_atime = zg_stat(g.fh)->st_atime; -+ } -+ stat->st_uid = geteuid(); -+ stat->st_gid = getegid(); -+} -+ -+/* -+ * Initialize stat buffer for root directory -+ */ -+static void stat_root_init(void) -+{ -+ stat_default_init(&l.stat_root); -+ l.stat_root.st_mode = S_IFDIR | 0500; -+ l.stat_root.st_nlink = 2; -+} -+ -+/* -+ * Initialize stat buffer for dump -+ */ -+static void stat_dump_init(void) -+{ -+ stat_default_init(&l.stat_dump); -+ l.stat_dump.st_mode = S_IFREG | 0400; -+ l.stat_dump.st_nlink = 1; -+ l.stat_dump.st_size = dfo_size(); -+ l.stat_dump.st_blksize = 4096; -+ l.stat_dump.st_blocks = l.stat_dump.st_size / 4096; -+} -+ -+/* -+ * FUSE callback: Getattr -+ */ -+static int zfuse_getattr(const char *path, struct stat *stat) -+{ -+ if (strcmp(path, "/") == 0) { -+ *stat = l.stat_root; -+ return 0; -+ } -+ if (strcmp(path, l.path) == 0) { -+ *stat = l.stat_dump; -+ return 0; -+ } -+ return -ENOENT; -+} -+ -+/* -+ * FUSE callback: Readdir - Return ".", ".." and dump file -+ */ -+static int zfuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler, -+ off_t offset, struct fuse_file_info *fi) -+{ -+ (void) offset; -+ (void) fi; -+ -+ if (strcmp(path, "/") != 0) -+ return -ENOENT; -+ -+ filler(buf, ".", NULL, 0); -+ filler(buf, "..", NULL, 0); -+ filler(buf, &l.path[1], NULL, 0); -+ return 0; -+} -+ -+/* -+ * FUSE callback: Open -+ */ -+static int zfuse_open(const char *path, struct fuse_file_info *fi) -+{ -+ if (strcmp(path, l.path) != 0) -+ return -ENOENT; -+ if ((fi->flags & 3) != O_RDONLY) -+ return -EACCES; -+ l.stat_dump.st_atime = time(NULL); -+ return 0; -+} -+ -+/* -+ * FUSE callback: Read -+ */ -+static int zfuse_read(const char *path, char *buf, size_t size, off_t offset, -+ struct fuse_file_info *fi) -+{ -+ (void) fi; -+ -+ if (strcmp(path, l.path) != 0) -+ return -ENOENT; -+ dfo_seek(offset); -+ dfo_read(buf, size); -+ return size; -+} -+ -+/* -+ * FUSE callback: Statfs -+ */ -+static int zfuse_statfs(const char *path, struct statvfs *buf) -+{ -+ (void) path; -+ -+ buf->f_bsize = buf->f_frsize = 4096; -+ buf->f_blocks = dfo_size() / 4096; -+ buf->f_bfree = buf->f_bavail = 0; -+ buf->f_files = 1; -+ buf->f_ffree = 0; -+ buf->f_namemax = strlen(l.path) + 1; -+ return 0; -+} -+ -+/* -+ * FUSE operations -+ */ -+static struct fuse_operations zfuse_ops = { -+ .getattr = zfuse_getattr, -+ .readdir = zfuse_readdir, -+ .open = zfuse_open, -+ .read = zfuse_read, -+ .statfs = zfuse_statfs, -+}; -+ -+/* -+ * Add additional FUSE arguments -+ */ -+static void add_argv_fuse(struct fuse_args *args) -+{ -+ int i; -+ -+ if (g.opts.argc_fuse == 0) -+ return; -+ STDERR("Adding Fuse options: "); -+ for (i = 0; i < g.opts.argc_fuse; i++) { -+ STDERR("%s ", g.opts.argv_fuse[i]); -+ fuse_opt_add_arg(args, g.opts.argv_fuse[i]); -+ } -+ STDERR("\n"); -+} -+ -+/* -+ * Mount dump -+ * -+ * Add additional FUSE options: -+ * - s....................: Disable multi-threaded operation -+ * - o fsname.............: File system name (used for umount) -+ * - o ro.................: Read only -+ * - o default_permissions: Enable permission checking by kernel -+ */ -+int zfuse_mount_dump(void) -+{ -+ struct fuse_args args = FUSE_ARGS_INIT(0, NULL); -+ char tmp_str[PATH_MAX]; -+ -+ if (!dfi_feat_seek()) -+ ERR_EXIT("Mounting not possible for %s dumps", dfi_name()); -+ fuse_opt_add_arg(&args, "zgetdump"); -+ fuse_opt_add_arg(&args, "-s"); -+ snprintf(tmp_str, sizeof(tmp_str), -+ "-ofsname=%s,ro,default_permissions,nonempty", -+ g.opts.device); -+ fuse_opt_add_arg(&args, tmp_str); -+ fuse_opt_add_arg(&args, g.opts.mount_point); -+ add_argv_fuse(&args); -+ stat_root_init(); -+ stat_dump_init(); -+ snprintf(l.path, sizeof(l.path), "/dump.%s", dfo_name()); -+ return fuse_main(args.argc, args.argv, &zfuse_ops); -+} -+ -+/* -+ * Unmount dump -+ */ -+void zfuse_umount(void) -+{ -+ char umount_cmd[PATH_MAX]; -+ char *umount_tool; -+ struct stat sbuf; -+ int rc; -+ -+ if (stat("/usr/bin/fusermount", &sbuf) == 0) -+ umount_tool = "/usr/bin/fusermount -u"; -+ else if (stat("/bin/fusermount", &sbuf) == 0) -+ umount_tool = "/bin/fusermount -u"; -+ else -+ umount_tool = "umount"; -+ -+ snprintf(umount_cmd, sizeof(umount_cmd), "%s %s", umount_tool, -+ g.opts.mount_point); -+ rc = system(umount_cmd); -+ -+ if (rc == -1) -+ ERR_EXIT_ERRNO("\"%s\" failed", umount_cmd); -+ if (rc > 0) -+ ERR_EXIT("\"%s\" failed", umount_cmd); -+ exit(0); -+} -diff --git a/zdump/zg.c b/zdump/zg.c -new file mode 100644 -index 0000000..e249011 ---- /dev/null -+++ b/zdump/zg.c -@@ -0,0 +1,411 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * Helper functions -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#include -+#include -+#include "zgetdump.h" -+ -+#define MAX_EXIT_FN 10 -+#define MAX_DEV_RETRIES 1000 -+ -+/* -+ * Progress information -+ */ -+struct prog { -+ u64 addr_next; -+ u64 mem_size; -+}; -+ -+/* -+ * At exit information -+ */ -+struct atexit { -+ zg_atexit_fn_t fn_vec[MAX_EXIT_FN]; -+ unsigned int cnt; -+}; -+ -+/* -+ * Temp devnode information -+ */ -+struct devnode { -+ char **vec; -+ int cnt; -+}; -+ -+/* -+ * File local static data -+ */ -+static struct { -+ struct atexit atexit; -+ struct prog prog; -+ struct devnode devnode; -+} l; -+ -+/* -+ * Call all registered exit handlers -+ */ -+static void exit_fn(void) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < l.atexit.cnt; i++) -+ l.atexit.fn_vec[i](); -+} -+ -+/* -+ * Register exit handler -+ */ -+void zg_atexit(zg_atexit_fn_t fn) -+{ -+ if (l.atexit.cnt >= MAX_EXIT_FN) -+ ABORT("Too many atexit handlers (%d)", l.atexit.cnt + 1); -+ l.atexit.fn_vec[l.atexit.cnt] = fn; -+ if (l.atexit.cnt == 0) -+ atexit(exit_fn); -+ l.atexit.cnt++; -+} -+ -+/* -+ * Exit function (For having exit gdb break point) -+ */ -+void zg_exit(int rc) -+{ -+ exit(rc); -+} -+ -+/* -+ * Alloc memory and check for errors -+ */ -+void *zg_alloc(unsigned int size) -+{ -+ void *ptr = calloc(size, 1); -+ if (!ptr) -+ ERR_EXIT("Alloc: Out of memory (%i KiB)", TO_KIB(size)); -+ return ptr; -+} -+ -+/* -+ * Realloc memory and check for errors -+ */ -+void *zg_realloc(void *ptr, unsigned int size) -+{ -+ void *new_ptr = realloc(ptr, size); -+ if (!new_ptr) -+ ERR_EXIT("Realloc: Out of memory (%i KiB)", TO_KIB(size)); -+ return new_ptr; -+} -+ -+/* -+ * Create duplicate for string -+ */ -+char *zg_strdup(const char *str) -+{ -+ char *new_str = strdup(str); -+ -+ if (!new_str) -+ ERR_EXIT("Strdup: Out of memory (%s)\n", str); -+ return new_str; -+} -+ -+/* -+ * Free memory -+ */ -+void zg_free(void *ptr) -+{ -+ free(ptr); -+} -+ -+/* -+ * Return path name of open file -+ */ -+const char *zg_path(struct zg_fh *zg_fh) -+{ -+ return zg_fh->path; -+} -+ -+/* -+ * Return stat buffer of open file -+ */ -+const struct stat *zg_stat(struct zg_fh *zg_fh) -+{ -+ return &zg_fh->sb; -+} -+ -+/* -+ * Open file -+ */ -+struct zg_fh *zg_open(const char *path, int flags, enum zg_check check) -+{ -+ struct zg_fh *zg_fh = zg_alloc(sizeof(*zg_fh)); -+ -+ zg_fh->fh = open(path, flags); -+ if (zg_fh->fh == -1) { -+ if (check == ZG_CHECK_NONE) -+ goto fail; -+ ERR_EXIT_ERRNO("Could not open \"%s\"", path); -+ } -+ if (stat(path, &zg_fh->sb) == -1) { -+ if (check == ZG_CHECK_NONE) -+ goto fail; -+ ERR_EXIT_ERRNO("Could not access file \"%s\"", path); -+ } -+ zg_fh->path = zg_strdup(path); -+ return zg_fh; -+ -+fail: -+ zg_free(zg_fh); -+ return NULL; -+} -+ -+/* -+ * Close file -+ */ -+void zg_close(struct zg_fh *zg_fh) -+{ -+ close(zg_fh->fh); -+ free(zg_fh); -+} -+ -+/* -+ * Read file -+ */ -+ssize_t zg_read(struct zg_fh *zg_fh, void *buf, size_t cnt, enum zg_check check) -+{ -+ size_t copied = 0; -+ ssize_t rc; -+ -+ do { -+ rc = read(zg_fh->fh, buf + copied, cnt - copied); -+ if (rc == -1) { -+ if (check == ZG_CHECK_NONE) -+ return rc; -+ ERR_EXIT_ERRNO("Could not read \"%s\"", zg_fh->path); -+ } -+ if (rc == 0) { -+ if (check != ZG_CHECK) -+ return copied; -+ ERR_EXIT("Unexpected end of file for \"%s\"", -+ zg_fh->path); -+ } -+ copied += rc; -+ } while (copied != cnt); -+ return copied; -+} -+ -+/* -+ * Return file size -+ */ -+u64 zg_size(struct zg_fh *zg_fh) -+{ -+ return zg_fh->sb.st_size; -+} -+ -+/* -+ * Return file position -+ */ -+off_t zg_tell(struct zg_fh *zg_fh, enum zg_check check) -+{ -+ off_t rc; -+ -+ rc = lseek(zg_fh->fh, 0, SEEK_CUR); -+ if (rc == -1 && check != ZG_CHECK_NONE) -+ ERR_EXIT_ERRNO("Could not get file position for \"%s\"", -+ zg_fh->path); -+ return rc; -+} -+ -+/* -+ * Seek to "off" relative to END -+ */ -+off_t zg_seek_end(struct zg_fh *zg_fh, off_t off, enum zg_check check) -+{ -+ off_t rc; -+ -+ rc = lseek(zg_fh->fh, off, SEEK_END); -+ if (rc == -1 && check != ZG_CHECK_NONE) -+ ERR_EXIT_ERRNO("Could not seek \"%s\"", zg_fh->path); -+ return rc; -+} -+ -+/* -+ * Seek to "off" in file -+ */ -+off_t zg_seek(struct zg_fh *zg_fh, off_t off, enum zg_check check) -+{ -+ off_t rc; -+ -+ rc = lseek(zg_fh->fh, off, SEEK_SET); -+ if (rc == -1 && check != ZG_CHECK_NONE) -+ ERR_EXIT_ERRNO("Could not seek \"%s\"", zg_fh->path); -+ if (rc != off && check == ZG_CHECK) -+ ERR_EXIT("Could not seek \"%s\"", zg_fh->path); -+ return rc; -+} -+ -+/* -+ * Seek from current position -+ */ -+off_t zg_seek_cur(struct zg_fh *zg_fh, off_t off, enum zg_check check) -+{ -+ off_t rc; -+ -+ rc = lseek(zg_fh->fh, off, SEEK_CUR); -+ if (rc == -1 && check != ZG_CHECK_NONE) -+ ERR_EXIT_ERRNO("Could not seek \"%s\"", zg_fh->path); -+ return rc; -+} -+ -+/* -+ * Do ioctl and exit in case of an error -+ */ -+int zg_ioctl(struct zg_fh *zg_fh, int rq, void *data, const char *op, -+ enum zg_check check) -+{ -+ int rc; -+ -+ rc = ioctl(zg_fh->fh, rq, data); -+ if (rc == -1 && check != ZG_CHECK_NONE) -+ ERR_EXIT_ERRNO("Operation \"%s\" failed on \"%s\"", op, -+ zg_fh->path); -+ return rc; -+} -+ -+/* -+ * Return file type -+ */ -+enum zg_type zg_type(struct zg_fh *zg_fh) -+{ -+ struct mtop mtop; -+ struct stat *sb = &zg_fh->sb; -+ -+ if (S_ISREG(sb->st_mode)) -+ return ZG_TYPE_FILE; -+ if (S_ISBLK(sb->st_mode)) { -+ if (minor(sb->st_rdev) % 4 == 0) -+ return ZG_TYPE_DASD; -+ else -+ return ZG_TYPE_DASD_PART; -+ } -+ if (S_ISCHR(sb->st_mode)) { -+ mtop.mt_count = 1; -+ mtop.mt_op = MTTELL; -+ if (zg_ioctl(zg_fh, MTIOCTOP, &mtop, "MTIOCTOP", -+ ZG_CHECK_NONE) != -1) -+ return ZG_TYPE_TAPE; -+ } -+ return ZG_TYPE_UNKNOWN; -+} -+ -+/* -+ * Initialize progress messages -+ */ -+void zg_progress_init(const char *msg, u64 mem_size) -+{ -+ STDERR("%s:\n", msg); -+ l.prog.addr_next = 0; -+ l.prog.mem_size = mem_size; -+} -+ -+/* -+ * Print progress -+ */ -+void zg_progress(u64 addr) -+{ -+ if (addr < l.prog.addr_next) -+ return; -+ -+ STDERR(" %08Lu / %08Lu MB\n", TO_MIB(addr), TO_MIB(l.prog.mem_size)); -+ l.prog.addr_next += l.prog.mem_size / 6; -+ l.prog.addr_next = MIN(l.prog.addr_next, l.prog.mem_size); -+} -+ -+/* -+ * Try to create device node in "dir" -+ */ -+static char *devnode_create_dir(const char *dir, dev_t dev) -+{ -+ char file_path[PATH_MAX]; -+ int i, fh, rc; -+ -+ for (i = 0; i < MAX_DEV_RETRIES; i++) { -+ snprintf(file_path, PATH_MAX, "%s/zgetdump.%04d", dir, i); -+ rc = mknod(file_path, S_IFBLK | S_IRWXU, dev); -+ if (rc == -1) { -+ if (errno == EEXIST) -+ continue; -+ else -+ break; -+ } -+ -+ /* Need this test to cover 'nodev'-mounted filesystems */ -+ fh = open(file_path, O_RDWR); -+ if (fh == -1) { -+ remove(file_path); -+ break; -+ } -+ close(fh); -+ return zg_strdup(file_path); -+ } -+ return NULL; -+} -+ -+/* -+ * Delete temporary device node -+ */ -+static void devnode_remove(char *dev_node) -+{ -+ if (remove(dev_node)) -+ ERR("Warning: Could not remove temporary file %s: %s", -+ dev_node, strerror(errno)); -+ zg_free(dev_node); -+} -+ -+/* -+ * Remove all temporary device nodes -+ */ -+static void devnode_remove_all(void) -+{ -+ int i; -+ -+ for (i = 0; i < l.devnode.cnt; i++) -+ devnode_remove(l.devnode.vec[i]); -+ if (l.devnode.vec) { -+ zg_free(l.devnode.vec); -+ l.devnode.vec = NULL; -+ } -+ l.devnode.cnt = 0; -+} -+ -+/* -+ * Make temporary device node for input dev identified by its dev_t -+ */ -+char *zg_devnode_create(dev_t dev) -+{ -+ char *dir_vec[] = {getenv("TMPDIR"), "/tmp", getenv("HOME"), ".", "/"}; -+ char *file_path; -+ unsigned int i; -+ -+ for (i = 0; i < ARRAY_ELEMENT_CNT(dir_vec); i++) { -+ if (dir_vec[i] == NULL) -+ continue; -+ file_path = devnode_create_dir(dir_vec[i], dev); -+ if (file_path) -+ goto found; -+ } -+ ERR_EXIT_ERRNO("Unable to create temporary dev node"); -+ return NULL; -+found: -+ l.devnode.cnt++; -+ l.devnode.vec = zg_realloc(l.devnode.vec, l.devnode.cnt * -+ sizeof(void *)); -+ l.devnode.vec[l.devnode.cnt - 1] = file_path; -+ if (l.devnode.cnt == 1) -+ zg_atexit(devnode_remove_all); -+ return file_path; -+} -diff --git a/zdump/zg.h b/zdump/zg.h -new file mode 100644 -index 0000000..2532146 ---- /dev/null -+++ b/zdump/zg.h -@@ -0,0 +1,185 @@ -+/* -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * Helper functions -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ */ -+ -+#ifndef ZG_H -+#define ZG_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "zt_common.h" -+ -+#define U64_MAX ((u64) -1) -+#define U32_MAX ((u32) -1) -+#define U16_MAX ((u16) -1) -+#define U8_MAX ((u8) -1) -+ -+/* -+ * IEC definitions -+ */ -+#define KIB_DIFF (1024) -+#define MIB_DIFF (1024 * 1024) -+#define GIB_DIFF (1024 * 1024 * 1024) -+ -+#define TO_MIB(x) ((x + (MIB_DIFF / 2)) / MIB_DIFF) -+#define TO_KIB(x) ((x + (KIB_DIFF / 2)) / KIB_DIFF) -+ -+/* -+ * Memory functions -+ */ -+extern void *zg_alloc(unsigned int size); -+extern void *zg_realloc(void *ptr, unsigned int size); -+extern void zg_free(void *ptr); -+extern char *zg_strdup(const char *str); -+ -+/* -+ * At exit functions -+ */ -+typedef void (*zg_atexit_fn_t)(void); -+extern void zg_atexit(zg_atexit_fn_t fn); -+extern void zg_exit(int rc) __attribute__ ((__noreturn__)); -+ -+/* -+ * Temporary device node functions -+ */ -+extern char *zg_devnode_create(dev_t dev); -+ -+/* -+ * Progress bar functions -+ */ -+extern void zg_progress_init(const char *msg, u64 mem_size); -+extern void zg_progress(u64 addr); -+ -+/* -+ * Error and print functions -+ */ -+#define ERR(x...) \ -+do { \ -+ fprintf(stderr, "%s: ", "zgetdump"); \ -+ fprintf(stderr, x); \ -+ fprintf(stderr, "\n"); \ -+} while (0) -+ -+#define ERR_EXIT(x...) \ -+do { \ -+ ERR(x); \ -+ zg_exit(1); \ -+} while (0) -+ -+#define ABORT(x...) \ -+do { \ -+ ERR("Internal Error: " x); \ -+ abort(); \ -+} while (0) -+ -+#define ERR_EXIT_ERRNO(x...) \ -+ do { \ -+ fflush(stdout); \ -+ fprintf(stderr, "%s: ", "zgetdump"); \ -+ fprintf(stderr, x); \ -+ fprintf(stderr, " (%s)", strerror(errno)); \ -+ fprintf(stderr, "\n"); \ -+ zg_exit(1); \ -+ } while (0) -+ -+#define STDERR(x...) \ -+do { \ -+ fprintf(stderr, x); \ -+ fflush(stderr); \ -+} while (0) -+ -+#define STDERR_PR(x...) \ -+do { \ -+ fprintf(stderr, "\r%s: ", "zgetdump"); \ -+ fprintf(stderr, x); \ -+} while (0) -+ -+#define STDOUT(x...) \ -+do { \ -+ fprintf(stdout, x); \ -+ fflush(stdout); \ -+} while (0) -+ -+/* -+ * Misc -+ */ -+#define PAGE_SIZE 4096 -+#define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a)-1) -+#define __ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask)) -+#define PAGE_ALIGN(addr) ALIGN(addr, PAGE_SIZE) -+#define MIN(x, y) ((x) < (y) ? (x) : (y)) -+#define MAX(x, y) ((x) > (y) ? (x) : (y)) -+#define ARRAY_ELEMENT_CNT(x) (sizeof(x) / sizeof(x[0])) -+#define ROUNDUP(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) -+ -+/* -+ * Pointer atrithmetic -+ */ -+#define PTR_SUB(x, y) (((char *) (x)) - ((unsigned long) (y))) -+#define PTR_ADD(x, y) (((char *) (x)) + ((unsigned long) (y))) -+#define PTR_DIFF(x, y) ((unsigned long)(((char *) (x)) - ((unsigned long) (y)))) -+ -+/* -+ * File functions -+ */ -+struct zg_fh { -+ const char *path; -+ int fh; -+ struct stat sb; -+}; -+ -+enum zg_type { -+ ZG_TYPE_DASD, -+ ZG_TYPE_DASD_PART, -+ ZG_TYPE_FILE, -+ ZG_TYPE_TAPE, -+ ZG_TYPE_UNKNOWN, -+}; -+ -+enum zg_check { -+ ZG_CHECK, -+ ZG_CHECK_ERR, -+ ZG_CHECK_NONE, -+}; -+ -+extern const char *zg_path(struct zg_fh *zg_fh); -+extern const struct stat *zg_stat(struct zg_fh *zg_fh); -+extern struct zg_fh *zg_open(const char *path, int flags, enum zg_check check); -+extern void zg_close(struct zg_fh *zg_fh); -+extern ssize_t zg_read(struct zg_fh *zg_fh, void *buf, size_t cnt, -+ enum zg_check check); -+extern u64 zg_size(struct zg_fh *zg_fh); -+extern off_t zg_tell(struct zg_fh *zg_fh, enum zg_check check); -+extern off_t zg_seek(struct zg_fh *zg_fh, off_t off, enum zg_check check); -+extern off_t zg_seek_end(struct zg_fh *zg_fh, off_t off, enum zg_check check); -+extern off_t zg_seek_cur(struct zg_fh *zg_fh, off_t off, enum zg_check check); -+extern int zg_ioctl(struct zg_fh *zg_fh, int rq, void *data, const char *op, -+ enum zg_check check); -+extern enum zg_type zg_type(struct zg_fh *zg_fh); -+ -+/* -+ * zgetdump actions -+ */ -+enum zg_action { -+ ZG_ACTION_STDOUT, -+ ZG_ACTION_DUMP_INFO, -+ ZG_ACTION_DEVICE_INFO, -+ ZG_ACTION_MOUNT, -+ ZG_ACTION_UMOUNT, -+}; -+ -+#endif /* ZG_H */ -diff --git a/zdump/zgetdump.8 b/zdump/zgetdump.8 -index 81ea801..a9230b4 100644 ---- a/zdump/zgetdump.8 -+++ b/zdump/zgetdump.8 -@@ -1,69 +1,317 @@ --.TH ZGETDUMP 8 "Apr 2006" "s390-tools" -+.TH ZGETDUMP 8 "Jan 2010" "s390-tools" - .SH NAME --zgetdump \- tool for copying dumps. -+zgetdump \- Tool for copying and converting System z dumps - .SH SYNOPSIS --\fBzgetdump\fR [-d] [-h] [-i] [-a] [-v] \fIdumpdevice\fR -+\fBzgetdump\fR [OPTIONS] [DUMP/DUMPDEV] [DIR] - .SH DESCRIPTION --\fBzgetdump\fR takes as input the dump device and writes its contents --to standard output, which you can redirect to a specific file. --.br --\fBzgetdump\fR can also check, whether a DASD device contains a valid dumper. -+The \fBzgetdump\fR tool reads or converts a dump. The dump can be located -+either on a dump device or on a file system. By default the dump content is -+written to standard output, which you can redirect to a specific file. You -+can also mount the dump content, print dump information, or check -+whether a DASD device contains a valid dump tool. - .SH OPTIONS - .TP --\fB-d\fR --Check DASD device \fIdumpdevice\fR for valid dumper. -+.BR "\-h" " or " "\-\-help" -+Print usage information, then exit. -+ -+.TP -+.BR "\-v" " or " "\-\-version" -+Print version information, then exit. -+ -+.TP -+.BR "\-m " " or " "\-\-mount " -+Mount the DUMP to mount point DIR and generate a virtual target -+dump file instead of writing the content to standard output. The virtual dump -+file gets the name "dump.FMT", where FMT is the name of the specified -+dump format (see "--fmt" option). -+ -+.TP -+.BR "\-u " " or " "\-\-umount " -+Unmount the dump that is mounted at mount point DIR. This option is a wrapper -+for "fusermount -u". Instead of DIR also the the DUMP (e.g. /dev/dasdd1) -+can be specified. -+ -+.TP -+.BR "\-d " " or " "\-\-device " -+Check DASD device DUMPDEV for valid dump tool and print information about it. -+ -+.TP -+.BR "\-i " " or " "\-\-info " -+Print the dump header information reading from the DUMP and check if -+the dump is valid. See chapter DUMP INFORMATION below for more information. -+.TP -+.BR "\-f " " or " "\-\-fmt " -+Use the specified target dump format FMT when writing or mounting the dump. -+The following target dump formats are supported: -+ -+.BR "- elf:" -+Executable and Linking Format core dump (64 bit only) -+ -+.BR "- s390:" -+s390 dump (default) -+ -+.TP -+\fBDUMP\fR -+This parameter specifies the file, partition or tape device node where the -+dump is located: -+.TP -+.BR -+- Regular dump file (e.g. /testdir/dump.0) -+.TP -+.BR -+- DASD partition device node (e.g. /dev/dasdc1) -+.TP -+.BR -+- DASD device node for multi-volume dump (e.g. /dev/dasdc) -+.TP -+.BR -+- Tape device node (e.g. /dev/ntibm0) -+ -+Note: For DASD multi-volume dump it is sufficient to specify only one of the -+multi-volume DASD partitions as DUMP. -+ -+.TP -+\fBDUMPDEV\fR -+When using the "--device" option, DUMPDEV must be the DASD device node of -+the dump disk that should be verified. -+ -+.SH COPY DUMP -+The default action of zgetdump is to copy the DUMP to standard output. Read -+the examples section below for more information. -+ -+.SH MOUNT DUMP -+Instead of writing the dump content to standard output you can also mount the -+dump using the "--mount" option. With that option it is possible to convert -+the dump without the need of copying it. The zgetdump tool generates a -+virtual target dump file that contains the dump in the requested target -+format. The virtual dump file is generated by mounting the source dump as a -+user space file system to the directory specified by the "--mount" option. -+The virtual target dump file is called dump. where FMT denotes -+the format of the target dump. The virtual dump file exists as long as the -+directory containing the file is not unmounted. -+ -+Mounting can be useful when you want to process the dump with a tool that -+cannot read the original dump format. To do this, mount the dump and -+specify the required target dump format with the "--fmt" option. Mounting is -+also for useful for multi-volume DASD dumps. After a multi-volume dump has been -+mounted, it is shown as a single dump file that can be accessed directly with -+dump processing tools like "makedumpfile", "crash" or "lcrash". -+ -+Mounting is implemented with "fuse" (file system in user space). Therefore the -+"fuse" kernel module must to be loaded on the system before the "--mount" -+option can be used. -+ -+A DASD dump can be mounted e.g. with "zgetdump /dev/dasdd1 -m -+/mnt" and unmounted with either "zgetdump -u /mnt", "fusermount -u /mnt" or -+"umount /mnt" (root only). -+ -+.SH DUMP FORMATS -+zgetdump supports the following dump formats: -+.TP -+.BR "s390" -+This dump format is System z specific and is used for DASD and tape dumps. -+.TP -+.BR "elf" -+Executable and Linking Format core dump. This dump format is also used for -+Linux user space core dumps. The zgetdump tool supports this dump format only -+for 64 bit. -+.TP -+.BR "lkcd" -+This dump format has been used by the Linux Kernel Crash Dumps (LKCD) project -+and is used on System z for the vmconvert and zfcp (SCSI) dump tool. The -+zgetdump tool supports "lkcd" only as source format. -+ -+.TP -+The default target format of zgetdump is "s390". Use the "--fmt" option to change the target format. -+ -+.SH DUMP INFORMATION -+When calling zgetdump with the "--info" option depending on the dump format -+the following dump attributes are available: -+.TP -+.BR "Dump format" -+Name of the dump format. -+.TP -+.BR Version -+Version number of the dump format. -+.TP -+.BR "Dump created/ended" -+Time when the dump process was started or ended. The dump time information is -+printed in your local time zone. E.g. "Wed, 03 Feb 2010 10:47:37 +0100" shows -+the time at your location. The meaning of "+0100" is that your time zone is one -+hour behind GMT (Greenwich Mean Time). You can use the "TZ" environment -+variable or use the "tzselect" tool to change the time zone. For example, if you -+know that the dump has been created in Hawaii, you can get the correct -+time information with: -+.br -+ -+# TZ='Pacific/Honolulu' zgetdump -i DUMP -+.TP -+.BR "Dump CPU ID" -+Identifier of the CPU that executed the dump tool. -+.TP -+.BR "Build arch" -+Architecture (s390 or s390x) on which the dump tool was built. -+.TP -+.BR "System arch" -+Architecture (s390 or s390x) of the dumped Linux system. -+.TP -+.BR "CPU count (online)" -+Number of online CPUs. -+.TP -+.BR "CPU count (real)" -+Number of total CPUs (online and offline). -+.TP -+.BR "Dump memory range" -+Memory range that was dumped. This value is the difference between the last -+dumped and the first dumped memory address. -+.TP -+.BR "Real memory range" -+Memory range that was available on system. This value is the difference -+between the last and the first memory address of the dumped system. -+The "real memory range" can differ from the "dump memory range" when -+the SIZE parameter was used when preparing the dump device with the zipl -+tool (see man zipl). -+.TP -+.BR "Memory map" -+Available memory chunks in the dump. Depending on the dump tool there -+can be multiple memory chunks, when a system with memory holes is dumped. -+ -+.SH DUMP DEVICE INFORMATION -+When calling zgetdump with the "--device" option depending on the dump tool -+the following attributes are available: -+.TP -+.BR "Dump tool" -+Name of the dump tool. -+.TP -+.BR "Version" -+Version of the dump tool. - .TP --\fB-h\fR --Print usage and exit. -+.BR "Architecture" -+Architecture (s390 or s390x) of the dump tool. - .TP --\fB-i\fR --Print the dump header information reading from the \fIdumpdevice\fR and --check if the dump is valid. -+.BR "DASD type" -+Type of the DASD where the dump tool is installed (ECKD or FBA). - .TP --\fB-i -a\fR --Print the dump header information and check if the dump is valid when --\fIdumpdevice\fR is a multi-volume tape. --(Mount and check all cartridges in sequence.) -+.BR "Dump size limit" -+If this attribute is set, the dump tool will dump memory only up to that -+limit even if there is more memory available. - .TP --\fB-v\fR --Output version information and exit. -+.BR "Force specified" -+If that attribute is set to "yes", the multi-volume DASD dump tool will not -+verify the dump signature on dump partitions. This can be useful, if the dump -+partition is also used for swap. -+ -+.SH EXAMPLES - .TP --\fBdumpdevice\fR --This parameter specifies the device or partition where the dump is located. --.SH EXAMPLE --1. Scenario: DASD partition /dev/dasdx1 was prepared for dump by means of -+.B Copy single volume DASD dump -+ -+The DASD partition /dev/dasdx1 was prepared for dump with: - .br -- zipl -d /dev/dasdx1 -+ -+ # zipl -d /dev/dasdx1 -+ - .br --The corresponding single-volume dump tool was IPLed. --.RB "The respective " "zgetdump " "call to copy the dump from the DASD --partition to file dump_file is: -+The corresponding single-volume dump tool was IPLed. The respective zgetdump -+call to copy the dump from the DASD partition to file dump.s390 is: - .br - -- zgetdump /dev/dasdx1 > dump_file -+ # zgetdump /dev/dasdx1 > dump.s390 - --2. Scenario: DASD partitions /dev/dasdx1 and /dev/dasdy1 contained in file --dump_list_conf were prepared for dump by means of -+.TP -+.B Copy multi-volume DASD dump -+ -+DASD partitions /dev/dasdx1 and /dev/dasdy1 contained in file dev_list.conf -+were prepared for multi-volume dump with: - .br -- zipl -M dump_list_conf -+ -+ # zipl -M dev_list.conf -+ - .br --The corresponding multi-volume dump tool was IPLed. --.RB "The respective " "zgetdump " "call to copy the dump from the DASD --partitions to file dump_file is: -+The corresponding multi-volume dump tool was IPLed. The respective zgetdump -+call to copy the dump from the DASD partitions to file dump.s390 is: - .br - -- zgetdump /dev/dasdx > dump_file or equivalent -+ # zgetdump /dev/dasdx > dump.s390 -+ - .br -- zgetdump /dev/dasdy > dump_file -+.TP -+.B Copy tape dump - --3. Scenario: Tape device /dev/ntibm0 was prepared for dump by means of -+Tape device /dev/ntibm0 was prepared with: - .br -- zipl -d /dev/ntibm0 -+ -+ # zipl -d /dev/ntibm0 -+ -+.br -+The corresponding tape dump tool was IPLed. The respective zgetdump call to -+copy the dump from the tape to file dump.s390 is: -+.br -+ -+ # zgetdump /dev/ntibm0 > dump.s390 -+ -+.br -+.TP -+.B Using pipes for network transfer -+ -+You can redirect standard output to tools like ftp or ssh in order to -+transfer the dump over the network without copying it into the file system -+first. -+ -+Copy DASD dump using ssh: -+.br -+ -+ # zgetdump /dev/dasdd1 | ssh user@host "cat > dump.s390" -+ - .br --The corresponding tape dump tool was IPLed. --.RB "The respective " "zgetdump " "call to copy the dump from the tape --to file dump_file is: -+Copy and compress DASD dump using ftp and gzip (note that not all ftp clients -+can do this): - .br - -- zgetdump /dev/ntibm0 > dump_file -+ # ftp host -+ ftp> put |"zgetdump /dev/dasdd1 | gzip" dump.s390.gz - -+.br -+The same effect can also be achieved by using the "--mount" option and run -+scp or ftp directly on the mounted virtual dump file. -+ -+.TP -+.B Using the "--mount" option -+ -+Mount multi-volume DASD dump, process it with the "crash" tool and unmout -+it with zgetdump afterwards. -+.br -+ -+ # zgetdump -m -f elf /dev/dasdx /dumps -+ # crash vmlinux /dumps/dump.elf -+ # zgetdump -u /dumps -+ -+.br -+Convert an ELF dump to an s390 dump by mounting it with the "--fmt" option, -+process it with lcrash and unmount it with fusermount afterwards. -+.br -+ -+ # zgetdump -m -f s390 dump.elf /dumps -+ # lcrash System.map /dumps/dump.s390 Kerntypes -+ # fusermount -u /dumps -+ -+.br -+.TP -+.B Print dump information (--info) -+ -+Print information on DASD dump on /dev/dasdd1: -+.br -+ -+ # zgetdump -i /dev/dasdd1 -+ -+.br -+.TP -+.B Print DASD dump tool information (--device) -+ -+Print information on DASD dump tool on /dev/dasdd: -+.br -+ -+ # zgetdump -d /dev/dasdd -+ -+.br -+.SH SEE ALSO -+.BR zipl (8), crash (8), lcrash (8), dumpconf (8), vmconvert (8), vmur (8) -diff --git a/zdump/zgetdump.c b/zdump/zgetdump.c -index 1ef312c..b3db463 100644 ---- a/zdump/zgetdump.c -+++ b/zdump/zgetdump.c -@@ -1,1360 +1,159 @@ - /* -- * zgetdump -- * Description: The zgetdump tool takes as input the dump device -- * and writes its contents to standard output, -- * which you can redirect to a specific file. -+ * zgetdump - Tool for copying and converting System z dumps - * -- * Copyright IBM Corp. 2001, 2006. -- * Author(s): Despina Papadopoulou -- * Frank Munzert -+ * Main functions -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ * Frank Munzert -+ * Despina Papadopoulou - */ - --#include "zgetdump.h" --#include "zt_common.h" - #include - #include - #include --#include --#include --#include --#include --#include - #include - #include - #include - #include --#include - #include --#include - #include -+#include -+#include -+#include -+#include -+#include - #include -+#include -+#include "zgetdump.h" - --/* from linux/fs.h */ --#define BLKSSZGET _IO(0x12,104) --#define BLKFLSBUF _IO(0x12,97) -- --#define HEADER_SIZE 4096 --#define BLOCK_SIZE 32768 --#define MVDUMPER_SIZE 4096 --#define PARTN_MASK ((1 << 2) - 1) --#define MAGIC_BLOCK_OFFSET_ECKD 3 --#define MAGIC_OFFSET_FBA -0x1000 --#define HEXINSTR "\x0d\x10\x47\xf0" /* BASR + 1st halfword of BC */ --#define ARCH_S390 1 --#define ARCH_S390X 2 --#define VERSION_NO_DUMP_DEVICE -1 -- --#define SYSFS_BUSDIR "/sys/bus/ccw/devices" -- --#if defined(__s390x__) -- #define FMT64 "l" --#else -- #define FMT64 "ll" --#endif -- --/* definitions */ -- --char *help_text = --"The zgetdump tool takes as input the dump device and writes its contents\n"\ --"to standard output, which you can redirect to a specific file.\n"\ --"zgetdump can also check, whether a DASD device contains a valid dumper.\n\n"\ --"Usage:\n"\ --"Copy dump from to stdout:\n"\ --" > zgetdump \n"\ --"Print dump header and check if dump is valid - for single tape or DASD:\n"\ --" > zgetdump [-i | --info] \n"\ --"Print dump header and check if dump is valid - for all volumes of a\n" --"multi-volume tape dump:\n"\ --" > zgetdump [-i | --info] [-a | --all] \n"\ --"Check dump device:\n"\ --" > zgetdump [-d | --device] \n"\ --"Print version info:\n"\ --" > zgetdump [-v | --version]\n"\ --"Print this text:\n"\ --" > zgetdump [-h | --help]\n\n"\ --"Examples for single-volume DASD:\n"\ --"> zgetdump -d /dev/dasdc\n"\ --"> zgetdump -i /dev/dasdc1\n"\ --"> zgetdump /dev/dasdc1 > dump_file\n"; -- --char *usage_note = --"Usage:\n"\ --"> zgetdump \n"\ --"> zgetdump -i \n"\ --"> zgetdump -i -a \n"\ --"> zgetdump -d \n"\ --"More info:\n"\ --"> zgetdump -h\n"; -- --/* Version info */ --static const char version_text[] = "zgetdump: version "RELEASE_STRING; -- --/* Copyright notice */ --static const char copyright_notice[] = "Copyright IBM Corp. 2001, 2008"; -- --/* global variables */ -- --s390_dump_header_t header; --s390_dump_end_marker_t end_marker; --char read_buffer[BLOCK_SIZE]; --struct timeval h_time_begin, h_time_end; -- --int option_a_set; --int option_i_set; --int option_d_set; --char dump_device[PATH_MAX]; -- --/* end of definitions */ -- --/* Use uname to check whether we run s390x kernel */ --int check_kernel_mode() --{ -- struct utsname uname_struct; -- if (uname(&uname_struct)) { -- fprintf(stderr, "Unable to get name and information about " -- "current kernel. \n"); -- perror(""); -- return 1; -- } -- if (strncmp(uname_struct.machine, "s390x", 5) == 0) { -- fprintf(stderr, "==========================================" -- "=======\n"); -- fprintf(stderr, "WARNING: You are running an s390x (ESAME) " -- "kernel.\n"); -- fprintf(stderr, " Your dump tool however is s390 " -- "(ESA).\n"); -- fprintf(stderr, "==========================================" -- "=======\n"); -- } -- return 0; --} -- --/* Read dump tool from DASD device */ --int read_dumper(int fd, int32_t offset, struct dump_tool *buffer, int whence) --{ -- if (lseek(fd, offset, whence) == -1) { -- perror("Cannot seek on device"); -- return 1; -- } -- if (read(fd, buffer, sizeof(struct dump_tool)) != -- sizeof(struct dump_tool)) { -- perror("Cannot read dump tool from device"); -- return 1; -- } -- return 0; --} -- --/* Use stat to check whether user provided input is a block device or a -- * partition */ --enum devnode_type check_device(char *device, int print) --{ -- struct stat stat_struct; -- -- if (stat(device, &stat_struct)) { -- fprintf(stderr, "Unable to get device status for " -- "'%s'. \n", device); -- perror(""); -- return IS_NOBLOCK; -- } -- if (!(S_ISBLK(stat_struct.st_mode))) { -- fprintf(stderr, "'%s' is not a block device. \n", dump_device); -- return IS_NOBLOCK; -- } -- if (minor(stat_struct.st_rdev) & PARTN_MASK) { -- if (print) -- fprintf(stderr, "Partition '%s' (%d/%d) specified where" -- " device is required.\n", dump_device, -- (unsigned short) major(stat_struct.st_rdev), -- (unsigned short) minor(stat_struct.st_rdev)); -- return IS_PARTITION; -- } -- return IS_DEVICE; --} -- --/* Allocate SIZE bytes of memory. Upon success, return pointer to memory. -- * Return NULL otherwise. */ --void *misc_malloc(size_t size) --{ -- void* result; -- -- result = malloc(size); -- if (result == NULL) { -- fprintf(stderr, "Could not allocate %lld bytes of memory", -- (unsigned long long) size); -- } -- return result; --} -- --char* misc_make_path(char* dirname, char* filename) --{ -- char* result; -- size_t len; -- -- len = strlen(dirname) + strlen(filename) + 2; -- result = (char *) misc_malloc(len); -- if (result == NULL) -- return NULL; -- sprintf(result, "%s/%s", dirname, filename); -- return result; --} -- --#define TEMP_DEV_MAX_RETRIES 1000 -- --/* Make temporary device node for input device identified by its dev_t */ --int make_temp_devnode(dev_t dev, char** device_node) --{ -- char* result; -- char* pathname[] = { getenv("TMPDIR"), "/tmp", -- getenv("HOME"), "." , "/"}; -- char filename[] = "zgetdump0000"; -- mode_t mode; -- unsigned int path; -- int retry; -- int rc; -- int fd; -- -- mode = S_IFBLK | S_IRWXU; -- /* Try several locations as directory for the temporary device -- * node. */ -- for (path=0; path < sizeof(pathname) / sizeof(pathname[0]); path++) { -- if (pathname[path] == NULL) -- continue; -- for (retry=0; retry < TEMP_DEV_MAX_RETRIES; retry++) { -- sprintf(filename, "zgetdump%04d", retry); -- result = misc_make_path(pathname[path], filename); -- if (result == NULL) -- return 1; -- rc = mknod(result, mode, dev); -- if (rc == 0) { -- /* Need this test to cover 'nodev'-mounted -- * filesystems. */ -- fd = open(result, O_RDWR); -- if (fd != -1) { -- close(fd); -- *device_node = result; -- return 0; -- } -- remove(result); -- retry = TEMP_DEV_MAX_RETRIES; -- } else if (errno != EEXIST) -- retry = TEMP_DEV_MAX_RETRIES; -- free(result); -- } -- } -- fprintf(stderr, "Unable to create temporary device node: %s", -- strerror(errno)); -- return 1; --} -- --/* Delete temporary device node and free memory allocated for device name. */ --void free_temp_devnode(char* device_node) --{ -- if (remove(device_node)) { -- fprintf(stderr, "Warning: Could not remove " -- "temporary file %s: %s", -- device_node, strerror(errno)); -- } -- free(device_node); --} -- -- --int open_block_device(char *device) --{ -- int fd; -- -- if (check_device(device, 1) != IS_DEVICE) -- return -1; -- fd = open(device, O_RDONLY); -- if (fd == -1) { -- fprintf(stderr, "Cannot open device '%s'. \n", device); -- perror(""); -- } -- return fd; --} -- --/* Check sysfs, whether a device specified by its bus id is defined and online. -- * Find out the corresponding dev_t */ --enum device_status get_device_from_busid(char* bus_id, dev_t *device) --{ -- char dev_file[PATH_MAX]; -- char temp_file[PATH_MAX]; -- char buffer[10]; -- struct dirent *direntp; -- int fd, minor, major; -- DIR *fd1; -- -- fd1 = opendir(SYSFS_BUSDIR); -- if (!fd1) { -- fprintf(stderr, "Could not open %s (err = %i).\n", -- SYSFS_BUSDIR, errno); -- exit(1); /* sysfs info not available */ -- } -- closedir(fd1); -- snprintf(dev_file, PATH_MAX, "%s/%s", SYSFS_BUSDIR, bus_id); -- fd1 = opendir(dev_file); -- if (!fd1) -- return UNDEFINED; /* device with devno does not exist */ -- snprintf(temp_file, PATH_MAX, "%s/online", dev_file); -- fd = open(temp_file, O_RDONLY); -- if (read(fd, buffer, 1) == -1) { -- perror("Could not read online attribute."); -- exit(1); -- } -- close(fd); -- if (buffer[0] != '1') -- return OFFLINE; /* device with devno is not online */ -- while ((direntp = readdir(fd1))) -- if (strncmp(direntp->d_name, "block:", 6) == 0) -- break; -- closedir(fd1); -- if (direntp == NULL) { -- snprintf(dev_file, PATH_MAX, "%s/%s/block", SYSFS_BUSDIR, -- bus_id); -- fd1 = opendir(dev_file); -- if (!fd1) { -- fprintf(stderr, "Could not open %s (err = %i).\n", -- dev_file, errno); -- exit(1); -- } -- while ((direntp = readdir(fd1))) -- if (strncmp(direntp->d_name, "dasd", 4) == 0) -- break; -- closedir(fd1); -- if (direntp == NULL) { -- fprintf(stderr, "Problem with contents of %s.\n", -- dev_file); -- exit(1); -- } -- } -- snprintf(temp_file, PATH_MAX, "%s/%s/dev", dev_file, direntp->d_name); -- fd = open(temp_file, O_RDONLY); -- if (read(fd, buffer, sizeof(buffer)) == -1) { -- perror("Could not read dev file."); -- exit(1); -- } -- close(fd); -- if (sscanf(buffer, "%i:%i", &major, &minor) != 2) { -- fprintf(stderr, "Malformed content of %s: %s\n", -- temp_file, buffer); -- exit(1); -- } -- *device = makedev(major, minor); -- return ONLINE; --} -- --/* Read dump tool, multi-volume dump parameter table, and dump header from the -- * input dump volume. Check input dump volume for -- * - identical dump parameter table (that is it belongs to the same dump set) -- * - valid magic number in the dump tool -- * - valid dump signature in the dump header -- * and set the volume's signature accordingly */ --int get_mvdump_volume_info(struct disk_info *vol, uint32_t vol_nr, off_t offset, -- struct mvdump_parm_table *table) --{ -- int fd, rc; -- ssize_t n_read; -- char* temp_devnode; -- struct dump_tool dumper; -- struct mvdump_parm_table vol_table; -- -- vol->signature = INVALID; -- rc = make_temp_devnode(vol->device, &temp_devnode); -- if (rc) -- return 1; -- fd = open_block_device(temp_devnode); -- if (fd == -1) { -- free_temp_devnode(temp_devnode); -- return 1; -- } -- /* We read partition data via the device node. If another process -- * has changed partition data via the partition node, the corresponding -- * device node might still have old data in its buffers. Flush buffers -- * to keep things in sync */ -- if (ioctl(fd, BLKFLSBUF, 0)) { -- perror("BLKFLSBUF failed"); -- goto out; -- } -- if (read_dumper(fd, offset, &dumper, SEEK_SET)) -- goto out; -- if (lseek(fd, offset + MVDUMPER_SIZE, SEEK_SET) != -- offset + MVDUMPER_SIZE) { -- perror("Cannot seek on device"); -- goto out; -- } -- n_read = read(fd, &vol_table, sizeof(vol_table)); -- if (n_read == -1) { -- perror("Cannot read multi-volume dump table"); -- goto out; -- } -- /* Check whether dump table on user specified dump device is -- * identical to the one found on this device */ -- if (memcmp(&vol_table, table, sizeof(vol_table))) { -- printf("ERROR: Orphaned multi-volume dump device '%s'\n", -- dump_device); -- goto out; -- } -- if (lseek(fd, vol->start_offset, SEEK_SET) != vol->start_offset) { -- perror("Cannot seek on device"); -- goto out; -- } -- n_read = read(fd, &header, HEADER_SIZE); -- if (n_read == -1) { -- perror("Cannot read dump header"); -- goto out; -- } -- free_temp_devnode(temp_devnode); -- close(fd); -- if ((header.dh_mvdump_signature == DUMP_MAGIC_S390) && -- (strncmp(dumper.magic, "ZMULT64", 7) == 0)) { -- vol->signature = VALID; -- if ((header.dh_volnr == vol_nr) && (header.dh_memory_size != 0)) -- vol->signature = ACTIVE; -- } -- return 0; --out: -- free_temp_devnode(temp_devnode); -- close(fd); -- return 1; --} -- --/* Read multi-volume dump parameter table from dump device and fill in the -- * fields of the disk_info array */ --int get_mvdump_info(int fd, int block_size, int *count, -- struct disk_info vol[]) --{ -- int i, rc = 0; -- off_t offset; -- ssize_t n_read; -- struct mvdump_parm_table table; -- -- offset = MAGIC_BLOCK_OFFSET_ECKD * block_size + MVDUMPER_SIZE; -- if (lseek(fd, offset, SEEK_SET) != offset) { -- fprintf(stderr, "Cannot seek on device '%s'.\n", -- dump_device); -- perror(""); -- return 1; -- } -- n_read = read(fd, &table, sizeof(table)); -- if (n_read == -1) { -- perror("Cannot read multi-volume dump table"); -- return 1; -- } -- *count = table.num_param; -- for (i = 0; i < table.num_param; i++) { -- sprintf(vol[i].bus_id, "0.0.%04x", table.param[i].devno); -- vol[i].start_offset = table.param[i].start_blk; -- vol[i].start_offset *= table.param[i].blocksize << 8; -- vol[i].part_size = (table.param[i].end_blk - -- table.param[i].start_blk + 1); -- vol[i].part_size *= table.param[i].blocksize << 8; -- vol[i].status = get_device_from_busid(vol[i].bus_id, -- &vol[i].device); -- if (vol[i].status == ONLINE) { -- offset = MAGIC_BLOCK_OFFSET_ECKD * -- table.param[i].blocksize << 8; -- rc = get_mvdump_volume_info(&vol[i], i, offset, -- &table); -- if (rc) -- return rc; -- } -- } -- return 0; --} -- --/* Print dump size limit as specified in zipl -d or zipm -M */ --void print_size_limit_info(uint64_t memory) --{ -- fprintf(stderr, "Dump size limit: "); -- if (memory == (uint64_t) -1) -- fprintf(stderr, "none\n"); -- else -- fprintf(stderr, "%lldMB\n", (unsigned long long) memory / -- (1024LL * 1024LL)); --} -+/* -+ * Globals -+ */ -+struct zgetdump_globals g; - --/* Print multi-volume dump device information for --device option */ --void print_mvdump_info(int version, int count, struct disk_info vol[], -- uint64_t memory, int force) -+/* -+ * Signal handler for exiting zgetdump (the atexit handler will do the work) -+ */ -+static void sig_exit(int sig) - { -- int i; -+ (void) sig; - -- fprintf(stderr, "'%s' is part of Version %i multi-volume dump,\n" -- "which is spread along the following DASD volumes:\n", -- dump_device, version); -- for (i = 0; i < count; i++) { -- switch(vol[i].status) { -- case UNDEFINED: -- fprintf(stderr, "%s (not defined)\n", vol[i].bus_id); -- break; -- case OFFLINE: -- fprintf(stderr, "%s (offline)\n", vol[i].bus_id); -- break; -- case ONLINE: -- fprintf(stderr, "%s (online, ", vol[i].bus_id); -- if (vol[i].signature == INVALID) -- fprintf(stderr, "invalid)\n"); -- else -- fprintf(stderr, "valid)\n"); -- break; -- } -- } -- print_size_limit_info(memory); -- fprintf(stderr, "Force option specified: "); -- if (force) -- fprintf(stderr, "yes\n"); -- else -- fprintf(stderr, "no\n"); -+ STDERR("\n"); /* E.g. to get newline after '^C' */ -+ ERR_EXIT("Got signal %i, exiting...", sig); - } - --/* Print single-volume dump device information for --device option */ --int print_dump_info(int version, int dumper_arch, uint64_t memory) --{ -- int rc = 0; -- -- if (version > 0) { -- if (dumper_arch == ARCH_S390) { -- fprintf(stderr, "'%s' is Version %i s390 (ESA) " -- "dump device.\n", dump_device, version); -- if (check_kernel_mode()) -- rc = 1; -- } else -- fprintf(stderr, "'%s' is Version %i s390x (ESAME) " -- "dump device.\n", dump_device, version); -- } else -- fprintf(stderr, "'%s' is Version 0 dump device. \n", -- dump_device); -- print_size_limit_info(memory); -- return rc; -+/* -+ * Install signal handler -+ */ -+static void sig_handler_init(void) -+{ -+ struct sigaction sigact; -+ -+ /* Ignore signals SIGUSR1 and SIGUSR2 */ -+ if (sigemptyset(&sigact.sa_mask) < 0) -+ goto fail; -+ sigact.sa_handler = SIG_IGN; -+ if (sigaction(SIGUSR1, &sigact, NULL) < 0) -+ goto fail; -+ if (sigaction(SIGUSR2, &sigact, NULL) < 0) -+ goto fail; -+ -+ /* Exit on SIGINT, SIGTERM, SIGHUP, ... */ -+ if (sigemptyset(&sigact.sa_mask) < 0) -+ goto fail; -+ sigact.sa_handler = sig_exit; -+ if (sigaction(SIGINT, &sigact, NULL) < 0) -+ goto fail; -+ if (sigaction(SIGTERM, &sigact, NULL) < 0) -+ goto fail; -+ if (sigaction(SIGHUP, &sigact, NULL) < 0) -+ goto fail; -+ if (sigaction(SIGQUIT, &sigact, NULL) < 0) -+ goto fail; -+ if (sigaction(SIGALRM, &sigact, NULL) < 0) -+ goto fail; -+ if (sigaction(SIGPIPE, &sigact, NULL) < 0) -+ goto fail; -+ return; -+fail: -+ ERR_EXIT_ERRNO("Could not initialize signal handler"); - } - --/* Read dump tool on FBA disk and check its magic number */ --int check_dump_tool_fba(int fd, int *version, int *arch, uint64_t *memory) -+/* -+ * Run "--umount" action -+ */ -+static int do_umount(void) - { -- struct dump_tool dumper; -- -- if (read_dumper(fd, MAGIC_OFFSET_FBA, &dumper, SEEK_END)) -- return 1; -- *memory = dumper.mem; -- if (strncmp(dumper.magic, "ZDFBA31", 7) == 0) { -- *version = dumper.version; -- *arch = ARCH_S390; -- } else if (strncmp(dumper.magic, "ZDFBA64", 7) == 0) { -- *version = dumper.version; -- *arch = ARCH_S390X; -- } else if ((memcmp(dumper.magic, HEXINSTR, 4) == 0) && -- (dumper.code[0] == '\x0d') && (dumper.code[1] == '\xd0')) -- /* We found basr r13,0 (old dumper) */ -- *version = 0; -- else -- *version = VERSION_NO_DUMP_DEVICE; -+ zfuse_umount(); - return 0; - } - --/* Read dump tool on ECKD disk and check its magic number */ --int check_dump_tool_eckd(int fd, int *version, int *arch, int *dasd_mv_flag, -- int *block_size, int *force_specified, -- uint64_t *memory) -+/* -+ * Run "--device" action -+ */ -+static int do_device_info(void) - { -- struct dump_tool dumper; -- -- if (ioctl(fd, BLKSSZGET, block_size)) { -- fprintf(stderr, "Cannot get blocksize of device %s.\n", -- dump_device); -- perror(""); -- return 1; -- } -- if (read_dumper(fd, MAGIC_BLOCK_OFFSET_ECKD * *block_size, &dumper, -- SEEK_SET)) -- return 1; -- *memory = dumper.mem; -- if (strncmp(dumper.magic, "ZECKD31", 7) == 0) { -- *version = dumper.version; -- *arch = ARCH_S390; -- } else if (strncmp(dumper.magic, "ZECKD64", 7) == 0) { -- *version = dumper.version; -- *arch = ARCH_S390X; -- } else if (strncmp(dumper.magic, "ZMULT64", 7) == 0) { -- *version = dumper.version; -- *arch = ARCH_S390X; -- *dasd_mv_flag = 1; -- *force_specified = dumper.force; -- } else if ((memcmp(dumper.magic, HEXINSTR, 4) == 0) && -- (dumper.code[0] == '\x0d') && (dumper.code[1] == '\xd0')) -- /* We found basr r13,0 (old dumper) */ -- *version = 0; -- else -- *version = VERSION_NO_DUMP_DEVICE; -+ dt_init(); -+ dt_info_print(); - return 0; - } - --void s390_tod_to_timeval(uint64_t todval, struct timeval *xtime) --{ -- /* adjust todclock to 1970 */ -- todval -= 0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096); -- -- todval >>= 12; -- xtime->tv_sec = todval / 1000000; -- xtime->tv_usec = todval % 1000000; --} -- -- --int open_dump(char *pathname) --{ -- int fd; -- -- fd = open(pathname, O_RDONLY); -- if (fd == -1) { -- perror("Cannot open dump device"); -- exit(1); -- } else -- fprintf(stderr, "Dump device: %s\n", pathname); -- return fd; --} -- -- --/* check if device is dasd or tape */ --enum dump_type dev_type(int fd) --{ -- struct mtget mymtget; -- -- if (ioctl(fd, MTIOCGET, &mymtget) == -1) -- return IS_DASD; -- else -- return IS_TAPE; --} -- --/* print lkcd header information */ --void print_lkcd_header(int fd) --{ -- dump_header_4_1_t dump_header; -- -- lseek(fd, 0, SEEK_SET); -- if (read(fd, &dump_header, sizeof(dump_header)) == -1) { -- perror("Could not read dump header."); -- exit(1); -- } -- fprintf(stderr, "\nThis is a lkcd dump:\n\n"); -- fprintf(stderr, -- "Memory start : 0x%"FMT64"x\n", dump_header.dh_memory_start); -- fprintf(stderr, -- "Memory end : 0x%"FMT64"x\n", dump_header.dh_memory_end); -- fprintf(stderr, -- "Physical memory: %"FMT64"d\n", dump_header.dh_memory_size); -- fprintf(stderr, -- "Panic string : %s\n", dump_header.dh_panic_string); -- fprintf(stderr, -- "Number of pages: %d\n", dump_header.dh_num_dump_pages); -- fprintf(stderr, -- "Page size : %d\n", dump_header.dh_dump_page_size); -- fprintf(stderr, -- "Magic number : 0x%"FMT64"x\n", dump_header.dh_magic_number); -- fprintf(stderr, -- "Version number : %d\n", dump_header.dh_version); --} -- --void print_s390_header(enum dump_type d_type) --{ -- s390_tod_to_timeval(header.dh_tod, &h_time_begin); -- --/* as from version 2 of the dump tools */ --/* volume numbers are used */ -- -- if ((d_type == IS_TAPE) && (header.dh_version >= 2)) { -- fprintf(stderr, "\nTape Volume %i", header.dh_volnr); -- if (header.dh_volnr != 0) -- fprintf(stderr, " of a multi volume dump.\n"); -- else -- fprintf(stderr, "\n"); -- } -- --/* don't print header */ --/* for all subsequent tapes/disks */ --/* of a multi-volume tape/disk dump */ -- -- if ((d_type == IS_DASD) || (header.dh_volnr == 0)) { -- if (header.dh_magic_number != DUMP_MAGIC_S390) { -- fprintf(stderr, "====================================" -- "===============\n"); -- fprintf(stderr, "WARNING: This does not look like a " -- "valid s390 dump!\n"); -- fprintf(stderr, "====================================" -- "===============\n"); -- } -- fprintf(stderr, "\n>>> Dump header information <<<\n"); -- fprintf(stderr, "Dump created on: %s\n", -- ctime(&h_time_begin.tv_sec)); -- fprintf(stderr, "Magic number:\t 0x%"FMT64"x\n", -- header.dh_magic_number); -- fprintf(stderr, "Version number:\t %d\n", header.dh_version); -- fprintf(stderr, "Header size:\t %d\n", header.dh_header_size); -- fprintf(stderr, "Page size:\t %d\n", header.dh_page_size); -- fprintf(stderr, "Dumped memory:\t %"FMT64"d\n", -- header.dh_memory_size); -- fprintf(stderr, "Dumped pages:\t %u\n", header.dh_num_pages); -- if (header.dh_version >= 3) { -- fprintf(stderr, "Real memory:\t %"FMT64"d\n", -- header.dh_real_memory_size); -- } -- fprintf(stderr, "cpu id:\t\t 0x%"FMT64"x\n", header.dh_cpu_id); -- if (header.dh_version >= 2) { -- switch (header.dh_arch) { -- case 1: fprintf(stderr, "System Arch:\t s390 (ESA)\n"); -- break; -- case 2: fprintf(stderr, -- "System Arch:\t s390x (ESAME)\n"); -- break; -- default: -- fprintf(stderr, "System Arch:\t \n"); -- break; -- } -- switch (header.dh_build_arch) { -- case 1: fprintf(stderr, "Build Arch:\t s390 (ESA)\n"); -- break; -- case 2: fprintf(stderr, -- "Build Arch:\t s390x (ESAME)\n"); -- break; -- default: -- fprintf(stderr, "Build Arch:\t \n"); -- break; -- } -- } -- fprintf(stderr, ">>> End of Dump header <<<\n\n"); -- } --} -- --/* print header information */ --void get_header(int fd) --{ -- ssize_t n_read; -- -- n_read = read(fd, &header, HEADER_SIZE); -- if (n_read == -1) { -- perror("Cannot read dump header"); -- close(fd); -- exit(1); -- } --} -- --/* copy header to stdout */ --void write_header() -+/* -+ * Run "--info" action -+ */ -+static int do_dump_info(void) - { -- ssize_t rc; -- -- memcpy(read_buffer, &header, sizeof(header)); -- rc = write(STDOUT_FILENO, read_buffer, header.dh_header_size); -- if (rc == -1) { -- perror("\nwrite failed"); -- exit(1); -- } -- if (rc < header.dh_header_size) { -- fprintf(stderr, "\nwrite failed: No space left on device\n"); -- exit(1); -+ if (dfi_init() != 0) { -+ dfi_info_print(); -+ STDERR("\nERROR: Dump is not complete\n"); -+ zg_exit(1); - } --} -- --/* copy partition containing multi-volume dump data to stdout */ --int mvdump_copy(int fd, uint64_t partsize, uint64_t *totalsize) --{ -- ssize_t n_read, n_written; -- uint64_t part_offset; -- int done = 0; -- size_t count; -- -- part_offset = HEADER_SIZE; -- do { -- count = MIN(header.dh_memory_size - *totalsize, BLOCK_SIZE); -- if (count < BLOCK_SIZE) -- done = 1; -- if (partsize - part_offset < count) { -- count = partsize - part_offset; -- done = 1; -- } -- n_read = read(fd, read_buffer, count); -- if (n_read == -1) { -- perror("\nread failed"); -- return 1; -- } -- n_read = (n_read >> 12) << 12; -- n_written = write(STDOUT_FILENO, read_buffer, n_read); -- if (n_written == -1) { -- perror("\nwrite failed"); -- return 1; -- } -- if (n_written < n_read) { -- fprintf(stderr, "\nwrite failed: " -- "No space left on device\n"); -- return 1; -- } -- part_offset += n_written; -- *totalsize += n_written; -- if (part_offset % (header.dh_memory_size / 32) == HEADER_SIZE) -- fprintf(stderr, "."); -- } while (!done); -- fprintf(stderr, "\n"); -+ dfi_info_print(); - return 0; - } - --/* copy the dump to stdout */ --int get_dump(int fd, int d_type) --{ -- int ret, bsr; -- ssize_t n_read, n_written; -- struct mtop mymtop; -- uint64_t i; -- -- ret = 0; -- if (d_type == IS_DASD) { -- i = 0; -- do { -- n_read = read(fd, read_buffer, BLOCK_SIZE); -- n_written = write(STDOUT_FILENO, read_buffer, n_read); -- if (n_written == -1) { -- perror("\nwrite failed"); -- exit(1); -- } -- if (n_written < n_read) { -- fprintf(stderr, "\nwrite failed: " -- "No space left on device\n"); -- exit(1); -- } -- i += n_read; -- if (i % (header.dh_memory_size / 32) == 0) -- fprintf(stderr, "."); -- } while (i < header.dh_memory_size && n_read != 0 -- && n_written >= 0); -- } else if (d_type == IS_TAPE) { -- /* write to stdout while not ENDOFVOL or DUMP_END */ -- if (header.dh_volnr != 0) -- fprintf(stderr, "Reading dump content "); -- for (i = 0; i < (header.dh_memory_size/BLOCK_SIZE); i++) { -- n_read = read(fd, read_buffer, BLOCK_SIZE); -- if (i % ((header.dh_memory_size/BLOCK_SIZE) / 32) == 0) -- fprintf(stderr, "."); -- if (strncmp(read_buffer, "ENDOFVOL", 8) == 0) { -- fprintf(stderr, "\nEnd of Volume reached.\n"); -- ret = 1; -- break; -- } else if (strncmp(read_buffer, "DUMP_END", 8) == 0) { -- ret = 2; -- break; -- } else { -- n_written = write(STDOUT_FILENO, read_buffer, -- n_read); -- if (n_written == -1) { -- perror("\nwrite failed"); -- exit(1); -- } -- if (n_written < n_read) { -- fprintf(stderr, "\nwrite failed: " -- "No space left on device\n"); -- exit(1); -- } -- } -- } -- if (ret == 2) { -- /* we go back a record, so dump_end_times gets called */ -- mymtop.mt_count = 1; -- mymtop.mt_op = MTBSR; -- bsr = ioctl(fd, MTIOCTOP, &mymtop); -- if (bsr != 0) { -- fprintf(stderr, -- "Tape operation MTBSR failed.\n"); -- exit(1); -- } -- } -- } -- return ret; --} -- --/* check for DUMP_END and see */ --/* if dump ended after it started (!!!) */ --int dump_end_times(int fd) --{ -- int ret; -- -- if (read(fd, &end_marker, sizeof(end_marker)) == -1) { -- perror("Could not read end marker."); -- exit(1); -- } -- s390_tod_to_timeval(end_marker.end_time, &h_time_end); -- if ((strncmp(end_marker.end_string, "DUMP_END", 8) == 0) && -- ((h_time_end.tv_sec - h_time_begin.tv_sec) >= 0)) { -- fprintf(stderr, "\nDump ended on:\t %s\n", -- ctime(&h_time_end.tv_sec)); -- ret = 0; -- } else -- ret = -1; -- return ret; --} -- --int check_and_write_end_marker(int fd) --{ -- if (dump_end_times(fd) == 0) { -- ssize_t rc; -- rc = write(STDOUT_FILENO, &end_marker, -- sizeof(end_marker)); -- if (rc == -1) { -- perror("\nwrite failed"); -- return 1; -- } -- if (rc < (ssize_t) sizeof(end_marker)) { -- fprintf(stderr, "\nwrite failed: " -- "No space left on device\n"); -- return 1; -- } -- fprintf(stderr, "\nDump End Marker found: " -- "this dump is valid.\n"); -- return 0; -- } else { -- fprintf(stderr, "\nThis dump is NOT valid.\n"); -- return 1; -- } --} -- --/* if a tape is part of the dump (not the last) */ --/* it should have and ENDOFVOL marker */ --int vol_end(void) --{ -- int ret; -- -- ret = strncmp(end_marker.end_string, "ENDOFVOL", 8); -- return ret; --} -- --/* position the tape in front of an end marker */ --/* with FSFM and BSR */ --void tape_forwards(int fd) --{ -- int ret; -- struct mtop mymtop; -- -- mymtop.mt_count = 1; -- mymtop.mt_op = MTFSFM; -- ret = ioctl(fd, MTIOCTOP, &mymtop); -- if (ret != 0) { -- fprintf(stderr, "Tape operation FSFM failed.\n"); -- exit(1); -- } -- -- mymtop.mt_count = 1; -- mymtop.mt_op = MTBSR; -- ret = ioctl(fd, MTIOCTOP, &mymtop); -- if (ret != 0) { -- fprintf(stderr, "Tape operation BSR failed.\n"); -- exit(1); -- } --} -- --/* put current tape offline */ --/* load & rewind next tape */ --void load_next(int fd) -+/* -+ * Run "--mount" action -+ */ -+static int do_mount(void) - { -- int ret; -- struct mtop mymtop; -- -- mymtop.mt_count = 1; -- mymtop.mt_op = MTOFFL; -- ret = ioctl(fd, MTIOCTOP, &mymtop); -- if (ret != 0) { -- fprintf(stderr, "Tape operation OFFL failed.\n"); -- exit(1); -- } -- -- mymtop.mt_count = 1; -- mymtop.mt_op = MTLOAD; -- ret = ioctl(fd, MTIOCTOP, &mymtop); -- if (ret != 0) { -- fprintf(stderr, "Tape operation LOAD failed.\n"); -- exit(1); -- } else -- fprintf(stderr, "done\n"); -- -- mymtop.mt_count = 1; -- mymtop.mt_op = MTREW; -- ret = ioctl(fd, MTIOCTOP, &mymtop); -- if (ret != 0) { -- fprintf(stderr, "Tape operation REW failed.\n"); -- exit(1); -- } -+ if (dfi_init() != 0) -+ ERR_EXIT("Dump cannot be processed (is not complete)"); -+ dfo_init(); -+ return zfuse_mount_dump(); - } - --/* parse the commandline options */ --void parse_opts(int argc, char *argv[]) --{ -- int opt, index; -- static struct option long_options[] = { -- {"info", no_argument, 0, 'i'}, -- {"help", no_argument, 0, 'h'}, -- {"version", no_argument, 0, 'v'}, -- {"all", no_argument, 0, 'a'}, -- {"device", no_argument, 0, 'd'}, -- {0, 0, 0, 0 } -- }; -- static const char option_string[] = "iavhd"; -- -- while ((opt = getopt_long(argc, argv, option_string, long_options, -- &index)) != -1) { -- switch (opt) { -- case 'd': -- option_d_set = 1; -- break; -- case 'a': -- option_a_set = 1; -- break; -- case 'i': -- option_i_set = 1; -- break; -- case 'h': -- printf(help_text); -- exit(0); -- case 'v': -- printf("%s\n", version_text); -- printf("%s\n", copyright_notice); -- exit(0); -- default: -- fprintf(stderr, "Try 'zgetdump --help' for more" -- " information.\n"); -- exit(1); -- } -- } -- -- /* check if -a and -i options are used correctly and check */ -- /* if devicename has been specified */ -- -- if ((option_a_set && !option_i_set) || (optind != argc-1) -- || (option_d_set && option_i_set)) { -- printf(help_text); -- exit(1); -- -- -- } -- strcpy(dump_device, argv[optind]); --} -- --/* Loop along all involved volumes (dump partitions) and either check (for -- * option --info) or pick up dump data */ --int mvdump_check_or_copy(int vol_count, struct disk_info vol[]) -+/* -+ * Run "copy to stdout" action -+ */ -+static int do_stdout(void) - { -- int i, fd, rc = 1; -- uint64_t data_size, total_size = 0; -- char* temp_devnode; -- -- for (i = 0; i < vol_count; i++) { -- if (vol[i].status != ONLINE) { -- fprintf(stderr, "=============================" -- "=======================\n"); -- fprintf(stderr, "ERROR: Dump device %s is not " -- "available.\n", vol[i].bus_id); -- fprintf(stderr, "=============================" -- "=======================\n"); -- return 1; -- } -- if (vol[i].signature != ACTIVE) { -- fprintf(stderr, "=============================" -- "=======================\n"); -- fprintf(stderr, "ERROR: Invalid dump data on " -- "%s.\n", vol[i].bus_id); -- fprintf(stderr, "=============================" -- "=======================\n"); -- return 1; -- } -- if (make_temp_devnode(vol[i].device, &temp_devnode)) -- return 1; -- fd = open_block_device(temp_devnode); -- if (fd == -1) { -- free_temp_devnode(temp_devnode); -- return 1; -- } -- if (lseek(fd, vol[i].start_offset, SEEK_SET) != -- vol[i].start_offset) { -- perror("Cannot seek on device"); -- goto out; -- } -- get_header(fd); -- print_s390_header(IS_MULT_DASD); -- fprintf(stderr, "\nMulti-volume dump: Disk %i (of %i)\n", -- i + 1, vol_count); -- if (option_i_set) { -- data_size = ((vol[i].part_size >> 12) << 12) - -- HEADER_SIZE; -- if (total_size + data_size > header.dh_memory_size) { -- if (lseek(fd, header.dh_memory_size - -- total_size, SEEK_CUR) == -1) { -- perror("Cannot seek on device"); -- goto out; -- } -- fprintf(stderr, "Checking dump contents on " -- "%s\n", vol[i].bus_id); -- if (dump_end_times(fd) == 0) { -- fprintf(stderr, "Dump End Marker " -- "found: " -- "this dump is valid.\n\n"); -- rc = 0; -- goto out; -- } else { -- fprintf(stderr, "Dump End Marker not " -- "found: " -- "this dump is NOT valid.\n\n"); -- goto out; -- } -- } else if (i == vol_count - 1) { -- fprintf(stderr, "Dump End Marker not found: " -- "this dump is NOT valid.\n\n"); -- goto out; -- } -- total_size += data_size; -- fprintf(stderr, "Skipping dump contents on %s\n", -- vol[i].bus_id); -- } else { -- if (i == 0) -- write_header(); -- fprintf(stderr, "Reading dump contents from %s", -- vol[i].bus_id); -- if (mvdump_copy(fd, vol[i].part_size, &total_size)) -- goto out; -- if ((i == vol_count - 1) || -- (total_size == header.dh_memory_size)) { -- rc = check_and_write_end_marker(fd); -- goto out; -- } -- } -- free_temp_devnode(temp_devnode); -- close(fd); -- } -- return 0; --out: -- free_temp_devnode(temp_devnode); -- close(fd); -- return rc; -+ if (dfi_init() != 0) -+ ERR_EXIT("Dump cannot be processed (is not complete)"); -+ dfo_init(); -+ return stdout_write_dump(); - } - -+/* -+ * The zgetdump main function -+ */ - int main(int argc, char *argv[]) - { -- uint64_t cur_time, size_limit; -- int vol_count, fd = -1; -- int version, dumper_arch, dasd_mv_flag = 0, block_size, rc; -- int force_specified = 0; -- enum dump_type d_type; -- enum devnode_type type; -- struct disk_info vol[MAX_DUMP_VOLUMES]; -- uint32_t cur_volnr; -- -- rc = 0; -- parse_opts(argc, argv); -- -- if (option_d_set) { -- fd = open_block_device(dump_device); -- if (fd == -1) { -- rc = 1; -- goto out; -- } -- rc = check_dump_tool_fba(fd, &version, &dumper_arch, -- &size_limit); -- if (rc) -- goto out; -- if (version >= 0) -- goto is_dump_device; -- else -- rc = check_dump_tool_eckd(fd, &version, &dumper_arch, -- &dasd_mv_flag, &block_size, -- &force_specified, -- &size_limit); -- if (rc) -- goto out; -- if (version >= 0) -- goto is_dump_device; -- fprintf(stderr, "'%s' is no dump device.\n", dump_device); -- rc = 1; -- goto out; -- --is_dump_device: -- if (dasd_mv_flag) { -- rc = get_mvdump_info(fd, block_size, &vol_count, vol); -- if (rc) -- goto out; -- print_mvdump_info(version, vol_count, vol, size_limit, -- force_specified); -- } else -- rc = print_dump_info(version, dumper_arch, -- size_limit); -- goto out; /* do not consider any other options */ -- } -- -- fd = open_dump(dump_device); -- get_header(fd); -- d_type = dev_type(fd); -- if ((d_type == IS_DASD) && -- ((header.dh_magic_number == DUMP_MAGIC_LKCD) -- || (header.dh_magic_number == DUMP_MAGIC_LIVE))) { -- print_lkcd_header(fd); -- exit(0); -- } -- if (d_type != IS_TAPE) { -- type = check_device(dump_device, 0); -- if (type == IS_DEVICE) { -- /* This is a valid block device node, no partition */ -- rc = check_dump_tool_eckd(fd, &version, &dumper_arch, -- &dasd_mv_flag, &block_size, -- &force_specified, -- &size_limit); -- if (rc) -- goto out; -- if (!dasd_mv_flag) { -- fprintf(stderr, "Device '%s' specified where" -- " partition is required.\n", dump_device); -- rc = 1; -- goto out; -- } else -- d_type = IS_MULT_DASD; -- } else if ((type == IS_PARTITION) && -- (header.dh_mvdump_signature == DUMP_MAGIC_S390)) { -- fprintf(stderr, "'%s' is a multi-volume dump " -- "partition.\nSpecify the corresponding device " -- "node instead.\n", dump_device); -- rc = 1; -- goto out; -- } -- } -- -- if (dasd_mv_flag) { -- rc = get_mvdump_info(fd, block_size, &vol_count, vol); -- if (rc) -- goto out; -- rc = mvdump_check_or_copy(vol_count, vol); -- goto out; -- } -- -- if (!option_i_set) { /* copy the dump to stdout */ -- print_s390_header(d_type); -- write_header(); -- fprintf(stderr, "Reading dump content "); -- -- /* now get_dump returns 1 for all */ -- /* except the last tape of a multi-volume dump */ -- -- while (get_dump(fd, d_type) == 1) { -- fprintf(stderr, "\nWaiting for next volume to be " -- "loaded... "); -- load_next(fd); -- get_header(fd); -- print_s390_header(d_type); -- } -- -- /* if dev is DASD and dump is copied */ -- /* check if the dump is valid */ -- -- if (d_type == IS_DASD) -- lseek(fd, header.dh_header_size + header.dh_memory_size, -- SEEK_SET); -- -- if (!check_and_write_end_marker(fd)) -- goto out; -- } else if (!option_a_set) { /* "-i" option */ -- fprintf(stderr, "\n> \"zgetdump -i\" checks if a dump on " -- "either\n"); -- fprintf(stderr, "> a dasd volume or single tape is valid.\n"); -- fprintf(stderr, "> If the tape is part of a multi-volume tape " -- "dump,\n"); -- fprintf(stderr, "> it checks if it is a valid portion of " -- "the dump.\n"); -- print_s390_header(d_type); -- if (d_type == IS_DASD) -- lseek(fd, -- header.dh_header_size + header.dh_memory_size, -- SEEK_SET); -- else { -- fprintf(stderr, "Checking if the dump is valid - " -- "this might take a while...\n"); -- tape_forwards(fd); -- } -- if (dump_end_times(fd) == 0) { -- fprintf(stderr, "Dump End Marker found: "); -- if (header.dh_volnr != 0) -- fprintf(stderr, "this is a valid part of " -- "a dump.\n\n"); -- else -- fprintf(stderr, "this dump is valid.\n\n"); -- goto out; -- } else if (d_type == IS_DASD) { -- fprintf(stderr, "Dump End Marker not found: " -- "this dump is NOT valid.\n\n"); -- rc = 1; -- goto out; -- } else -- fprintf(stderr, "Checking for End of Volume...\n"); -- if (vol_end() != 0) { -- fprintf(stderr, "End of Volume not found: " -- "this dump is NOT valid.\n\n"); -- rc = 1; -- goto out; -- } else { -- fprintf(stderr, "Reached End of Volume %i of a " -- "multi-volume tape dump.\n", header.dh_volnr); -- fprintf(stderr, "This part of the dump is valid.\n\n"); -- goto out; -- } -- } else { /* "-i -a" option */ -- fprintf(stderr, "\n> \"zgetdump -i -a\" checks if a " -- "multi-volume tape dump is valid.\n"); -- fprintf(stderr, "> Please make sure that all volumes are " -- "loaded in sequence.\n"); -- if (d_type == IS_DASD) { -- fprintf(stderr, "\"-i -a\" is used for validation of " -- "multi-volume tape dumps.\n\n"); -- rc = 1; -- goto out; -- } -- print_s390_header(d_type); -- cur_volnr = header.dh_volnr; -- cur_time = header.dh_tod; -- fprintf(stderr, "\nChecking if the dump is valid - " -- "this might take a while...\n"); -- tape_forwards(fd); -- if (dump_end_times(fd) == 0) { -- fprintf(stderr, "Dump End Marker found: " -- "this dump is valid.\n\n"); -- goto out; -- } else if (vol_end() != 0) { -- fprintf(stderr, "End of Volume not found: " -- "this dump is NOT valid.\n\n"); -- rc = 1; -- goto out; -- } -- while (vol_end() == 0) { -- cur_volnr += 1; -- fprintf(stderr, "Reached End of Volume %i.\n", -- header.dh_volnr); -- fprintf(stderr, "Waiting for Volume %i to be " -- "loaded... ", cur_volnr); -- load_next(fd); -- get_header(fd); -- print_s390_header(d_type); -- if (header.dh_volnr != cur_volnr) { -- fprintf(stderr, "This is not Volume %i\n", -- cur_volnr); -- rc = 1; -- goto out; -- } else if (header.dh_tod != cur_time) { -- fprintf(stderr, "Time stamp of this volume " -- "does not match the previous one.\n"); -- rc = 1; -- goto out; -- } -- tape_forwards(fd); -- if (dump_end_times(fd) == 0) { -- fprintf(stderr, "Dump End found: " -- "this dump is valid.\n\n"); -- goto out; -- } else if (vol_end() != 0) { -- fprintf(stderr, "End of Volume not found: " -- "this dump is NOT valid.\n\n"); -- rc = 1; -- goto out; -- } -- } -- } --out: -- if (fd != -1) -- close(fd); -- return(rc); -+ sig_handler_init(); -+ opts_parse(argc, argv); -+ -+ switch (g.opts.action) { -+ case ZG_ACTION_STDOUT: -+ return do_stdout(); -+ case ZG_ACTION_DUMP_INFO: -+ return do_dump_info(); -+ case ZG_ACTION_DEVICE_INFO: -+ return do_device_info(); -+ case ZG_ACTION_MOUNT: -+ return do_mount(); -+ case ZG_ACTION_UMOUNT: -+ return do_umount(); -+ } -+ ABORT("Invalid action: %i", g.opts.action); - } -diff --git a/zdump/zgetdump.h b/zdump/zgetdump.h -index 46b427d..daf0ea1 100644 ---- a/zdump/zgetdump.h -+++ b/zdump/zgetdump.h -@@ -1,194 +1,90 @@ - /* -- * header file for zgetdump -- * Copyright IBM Corp. 2001, 2006. -- * Author(s): Despina Papadopoulou -+ * zgetdump - Tool for copying and converting System z dumps -+ * -+ * Main include file - Should be included by all source files -+ * -+ * Copyright IBM Corp. 2001, 2010 -+ * Author(s): Michael Holzheu -+ * Frank Munzert -+ * Despina Papadopoulou - */ - --/* This header file holds the architecture specific crash dump header */ --#ifndef _ZGETDUMP_H --#define _ZGETDUMP_H -+#ifndef ZGETDUMP_H -+#define ZGETDUMP_H - --#include --#include --#include -- --/* definitions (this has to match with vmdump.h of lcrash */ -- --#define DUMP_MAGIC_S390 0xa8190173618f23fdULL /* s390 magic number */ --#define DUMP_MAGIC_LKCD 0xa8190173618f23edULL /* lkcd magic number */ --#define DUMP_MAGIC_LIVE 0xa8190173618f23cdULL /* live magic number */ -- --#define S390_DUMP_HEADER_SIZE 4096 --#define MAX_DUMP_VOLUMES 32 --#define DUMP_ASM_MAGIC_NUMBER 0xdeaddeadULL /* magic number */ -- --#define MIN(x, y) ((x) < (y) ? (x) : (y)) -+#include "zg.h" -+#include "dfo.h" -+#include "dfi.h" -+#include "dt.h" -+#include "list.h" -+#include "df_s390.h" -+#include "df_elf.h" -+#include "df_lkcd.h" - - /* -- * Structure: s390_dump_header_t -- * Function: This is the header dumped at the top of every valid s390 crash -- * dump. -+ * zgetdump options - */ -- --typedef struct _s390_dump_header_s { -- /* the dump magic number -- unique to verify dump is valid */ -- uint64_t dh_magic_number; /* 0x000 */ -- -- /* the version number of this dump */ -- uint32_t dh_version; /* 0x008 */ -- -- /* the size of this header (in case we can't read it) */ -- uint32_t dh_header_size; /* 0x00c */ -- -- /* the level of this dump (just a header?) */ -- uint32_t dh_dump_level; /* 0x010 */ -- -- /* the size of a Linux memory page (4K, 8K, 16K, etc.) */ -- uint32_t dh_page_size; /* 0x014 */ -- -- /* the size of all physical memory */ -- uint64_t dh_memory_size; /* 0x018 */ -- -- /* the start of physical memory */ -- uint64_t dh_memory_start; /* 0x020 */ -- -- /* the end of physical memory */ -- uint64_t dh_memory_end; /* 0x028 */ -- -- /* the number of pages in this dump specifically */ -- uint32_t dh_num_pages; /* 0x030 */ -- -- /* ensure that dh_tod and dh_cpu_id are 8 byte aligned */ -- uint32_t dh_pad; /* 0x034 */ -- -- /* the time of the dump generation using stck */ -- uint64_t dh_tod; /* 0x038 */ -- -- /* cpu id */ -- uint64_t dh_cpu_id; /* 0x040 */ -- -- /* arch */ -- uint32_t dh_arch; /* 0x048 */ -- -- /* volume number */ -- uint32_t dh_volnr; /* 0x04c */ -- -- /* build arch */ -- uint32_t dh_build_arch; /* 0x050 */ -- -- /* real mem size */ -- uint64_t dh_real_memory_size; /* 0x054 */ -- -- /* multi-volume dump indicator */ -- uint8_t dh_mvdump; /* 0x05c */ -- -- /* fill up to 512 bytes */ -- unsigned char end_pad1[0x200-0x05d]; /* 0x05d */ -- -- /* the dump signature to verify a multi-volume dump partition */ -- uint64_t dh_mvdump_signature; /* 0x200 */ -- -- /* the time the partition was prepared for multi-volume dump */ -- uint64_t dh_mvdump_zipl_time; /* 0x208 */ -- -- /* fill up to 4096 byte */ -- unsigned char end_pad2[0x1000-0x210]; /* 0x210 */ -- --} __attribute__((packed)) s390_dump_header_t; -+struct options { -+ int action_specified; -+ enum zg_action action; -+ char *device; -+ char *mount_point; -+ int fmt_specified; -+ const char *fmt; -+ int debug_specified; -+ char **argv_fuse; -+ int argc_fuse; -+}; - - /* -- * Structure: s390_dump_end_marker_t -- * Function: This end marker should be at the end of every valid s390 crash -- * dump. -+ * zgetdump globals - */ -- --typedef struct _s390_dump_end_marker_{ -- char end_string[8]; -- unsigned long long end_time; --} __attribute__((packed)) s390_dump_end_marker_t; -+extern struct zgetdump_globals { -+ struct zg_fh *fh; -+ const char *prog_name; -+ struct options opts; -+} g; - - /* -- * Structure: lkcd 4.1 dump header -+ * Misc fuctions - */ -+extern void opts_parse(int argc, char *argv[]); -+extern int stdout_write_dump(void); -+ -+#ifndef WITHOUT_FUSE -+extern int zfuse_mount_dump(void); -+extern void zfuse_umount(void); -+#else -+static inline int zfuse_mount_dump(void) -+{ -+ ERR_EXIT("Program compiled without fuse support"); -+} -+static inline void zfuse_umount(void) -+{ -+ ERR_EXIT("Program compiled without fuse support"); -+} -+#endif - --typedef struct _dump_header_s { -- uint64_t dh_magic_number; -- uint32_t dh_version; -- uint32_t dh_header_size; -- uint32_t dh_dump_level; -- uint32_t dh_dump_page_size; -- uint64_t dh_memory_size; -- uint64_t dh_memory_start; -- uint64_t dh_memory_end; -- uint32_t dh_num_dump_pages; -- char dh_panic_string[0x100]; -- struct timeval dh_time; -- char dh_utsname_sysname[65]; -- char dh_utsname_nodename[65]; -- char dh_utsname_release[65]; -- char dh_utsname_version[65]; -- char dh_utsname_machine[65]; -- char dh_utsname_domainname[65]; -- void *dh_current_task; -- uint32_t dh_dump_compress; -- uint32_t dh_dump_flags; -- uint32_t dh_dump_device; --} dump_header_4_1_t; -- --struct dump_tool { -- char magic[7]; -- uint8_t version; -- char code[0xff7 - 0x8]; -- uint8_t force; -- uint64_t mem; --} __attribute__ ((packed)); -- --struct mvdump_param { -- uint16_t devno; -- uint32_t start_blk; -- uint32_t end_blk; -- uint8_t blocksize; -- uint8_t end_sec; -- uint8_t num_heads; --} __attribute__ ((packed)); -- --struct mvdump_parm_table { -- uint64_t timestamp; -- uint16_t num_param; -- struct mvdump_param param[MAX_DUMP_VOLUMES]; --} __attribute__ ((packed)); -- --enum dump_type { -- IS_TAPE = 0, -- IS_DASD = 1, -- IS_MULT_DASD = 2, --}; -- --enum devnode_type { -- IS_DEVICE = 0, -- IS_PARTITION = 1, -- IS_NOBLOCK = 2, --}; -- --enum device_status { -- ONLINE = 0, -- OFFLINE = 1, -- UNDEFINED = 2, --}; -+/* -+ * Supported DFI dump formats -+ */ -+extern struct dfi dfi_s390tape; -+extern struct dfi dfi_s390mv; -+extern struct dfi dfi_s390; -+extern struct dfi dfi_lkcd; -+extern struct dfi dfi_elf; -+extern struct dfi dfi_kdump; - --enum device_signature { -- INVALID = 0, -- VALID = 1, -- ACTIVE = 2, --}; -+/* -+ * Supported DFO dump formats -+ */ -+extern struct dfo dfo_s390; -+extern struct dfo dfo_elf; - --struct disk_info { -- dev_t device; -- enum device_status status; -- enum device_signature signature; -- off_t start_offset; -- uint64_t part_size; -- char bus_id[9]; --}; -+/* -+ * Supported s390 dumpers -+ */ -+extern struct dt dt_s390mv; -+extern struct dt dt_s390sv; - --#endif /* _ASM_VMDUMP_H */ -+#endif /* ZGETDUMP_H */ -diff --git a/zipl/boot/dumpcommon.S b/zipl/boot/dumpcommon.S -index d70473c..b37017c 100644 ---- a/zipl/boot/dumpcommon.S -+++ b/zipl/boot/dumpcommon.S -@@ -47,23 +47,27 @@ - - #define __LC_ARCH_MODE_ID 163 /* here is the arch flag in the lowcore */ - #define __LC_IPIB 0xe00 /* IPL Parameter Information Block */ -+#define __LC_CPU_ADDRESS 0x0084 /* CPU address in lowcore */ - #define DIAG308_IPL 3 /* Subcode 3 - Perform Load Clear */ - #define DIAG308_SET 5 /* Subcode 5 - Set IPL Parameters */ - --#define PARAM_START 0x3000 /* 8-byte time stamp plus 2-byte count */ -+#define PARAM_START 0x4000 /* 8-byte time stamp plus 2-byte count */ - /* plus 32 13-byte entries */ --#define IDA_LIST_START 0x3200 /* 64 8-byte IDAW's */ --#define CCW_CHAIN_START 0x3400 /* chained write CCW's */ --#define ZERO_MEM_START 0x4000 --#define ZERO_MEM_SIZE 0x3000 -+#define IDA_LIST_START 0x4200 /* 64 8-byte IDAW's */ -+#define CCW_CHAIN_START 0x4400 /* chained write CCW's */ -+#define ZERO_MEM_START 0x6000 /* Zero pages start */ -+#define ZERO_MEM_SIZE 0x3000 /* Init three zero pages */ - - #define SCPINCR1_OFF 8 - #define SCPA1_OFF 10 - #define SCPA2_OFF 100 - #define SCPINCR2_OFF 104 - --#define ZERO_PAGE_START 0x5000 --#define TMP_PAGE_START 0x6000 -+ -+#define ZERO_PAGE_START 0x6000 /* Zero page */ -+#define HEADER_PAGE_START 0x5000 /* Dump header page */ -+#define PREFIX_ARR_START 0x5800 /* Prefix page array in dump header */ -+#define TMP_PAGE_START 0x7000 /* Page for temp storage */ - - ################################################################################ - # MACRO: dump_header -@@ -77,7 +81,7 @@ - # - .Ldh_dumpheader: - .Ldh_magic_number:.long S390_DUMP_MAGIC --.Ldh_version: .long 0x00000004 -+.Ldh_version: .long 0x00000005 - .Ldh_header_size: .long HEADER_SIZE - .Ldh_dump_level: .long 0x00000004 # DUMP_ALL - .Ldh_page_size: .long PAGE_SIZE -@@ -97,6 +101,8 @@ - #endif - .Ldh_real_mem_size: .long 0x00000000,0x00000000 - .Ldh_mvdump: .byte 0x00 -+.Ldh_cpu_cnt: .byte 0x00,0x00 -+.Ldh_real_cpu_cnt:.byte 0x00,0x00 - - # - # Dump End Marker -@@ -258,10 +264,13 @@ _ssch_64: - lgr %r3,%r10 # and irb address as parameters - bas %r14,_wait4de_64-0b(%r13) # wait until DE or error - tm 9(%r10),0xff # test channel status -- bnz 5f-0b(%r13) -- tm 8(%r10),0xd2 # test device status -+ bz 3f-0b(%r13) -+ bct %r9,1b-0b(%r13) # something went wrong, retry. -+3: tm 8(%r10),0xd2 # test device status - bz 4f-0b(%r13) - bct %r9,1b-0b(%r13) # something went wrong, retry. -+ j 5f # retries failed -> panic -+ - 4: lmg %r6,%r15,248(%r15) - br %r14 - -@@ -364,7 +373,8 @@ _take_dump_64: - spt .Lcpu_timer-.Lbase(%r13) # set cpu timer to future - - lghi %r6,ZERO_MEM_START # clear memory -- lghi %r7,ZERO_MEM_START + ZERO_MEM_SIZE -+ lgr %r7,%r6 -+ aghi %r7,ZERO_MEM_SIZE - sgr %r7,%r6 - sgr %r8,%r8 - sgr %r9,%r9 -@@ -395,6 +405,14 @@ _take_dump_64: - - bas %r14,_count_mem_64-.Lbase(%r13) - -+ # copy dump header -+ -+ stck .Ldh_time-.Lbase(%r13) # store time -+ stidp .Ldh_cpuid-.Lbase(%r13) # store CPU ID -+ -+ lghi %r3,HEADER_PAGE_START -+ mvc 0(256,%r3),.Ldh_dumpheader-.Lbase(%r13) -+ - # dump memory - - llgf %r14,.Ldump_mem_64-.Lbase(%r13) -@@ -448,7 +466,8 @@ _count_mem_64: - mlgr %r2,%r1 # mem size in bytes in %r3 - - stg %r3,.Ldh_real_mem_size-0b(%r13) -- lg %r6,.Lmem_upper_limit-0b(%r13) # check if we have an upper limit -+ larl %r7,.Lmem_upper_limit -+ lg %r6,0(%r7) # check if we have an upper limit - clgr %r3,%r6 - bl .Lsavemem-0b(%r13) - lgr %r3,%r6 # upper mem limit set -> use it! -@@ -458,6 +477,11 @@ _count_mem_64: - srlg %r12,%r3,12 # calculate page count (/ 4096) - st %r12,.Ldh_num_pages-0b(%r13) # store page count - -+ clgr %r6,%r3 -+ bne .Lexit-0b(%r13) -+ larl %r2,.Lmsg_mem_limit_set # print mem limit warning -+ bras %r14,_sclp_print -+.Lexit: - lmg %r6,%r15,248(%r15) - br %r14 - .Lonemb: -@@ -473,29 +497,31 @@ _store_status_64: - stmg %r6,%r15,48(%r15) - basr %r13,0 # base register - 0: aghi %r15,-200 -- lghi %r7,0x0 # base register for 0 page -- -- ######## move lowcore info (assume user has made store ######## -- ######## status) to prefix-page ######## -- -- bas %r14,_copy_lowcore_64-0b(%r13) - - ######## stop all cpus and store status in prefix pages ######## - - lghi %r8,0 # first cpu - stap .Lcurrent_cpu_64+2-0b(%r13) # store current cpu address -+ llgh %r6,.Lcurrent_cpu_64+2-0b(%r13) -+ -+ ######## move lowcore info (assume user has made store ######## -+ ######## status) to prefix-page ######## -+ -+ sth %r6,__LC_CPU_ADDRESS(%r0) -+ bas %r14,_copy_lowcore_64-0b(%r13) - - 1: - cl %r8,.Lcurrent_cpu_64-0b(%r13) # is ipl cpu ? - be 4f-0b(%r13) # if yes get next cpu - 2: -- lgr %r9,%r7 -+ lghi %r9,0 - sigp %r9,%r8,0x9 # store status of cpu - bc 8,3f-0b(%r13) # accepted - bc 4,4f-0b(%r13) # status stored: next cpu - bc 2,2b-0b(%r13) # busy: try again - bc 1,4f-0b(%r13) # not op: next cpu - 3: -+ sth %r8,__LC_CPU_ADDRESS(%r0) - bas %r14,_copy_lowcore_64-0b(%r13) - 4: - aghi %r8,1 # next cpu (r8 +=1) -@@ -534,8 +560,17 @@ _copy_lowcore_64: - ###### copy lowcore ###### - - llgf %r3,792(%r2) # get prefix page of current cpu -- lghi %r5,0x1000 # first page -- agr %r3,%r5 # get base register for second -+ -+ lgh %r8,.Ldh_cpu_cnt-0b(%r13) # Save lowcore pointer (32 bit) -+ sll %r8,2 # in dump header -+ lghi %r9,PREFIX_ARR_START -+ agr %r9,%r8 -+ st %r3,0(%r9) -+ -+ lgh %r8,__LC_CPU_ADDRESS(%r0) # copy cpu address -+ sth %r8,__LC_CPU_ADDRESS(%r3) # to lowcore -+ -+ aghi %r3,0x1000 # get base register for second - # page of prefix pages - - # |-----------------------------------------------------------| -@@ -562,7 +597,14 @@ _copy_lowcore_64: - mvc 804(20,%r3),804(%r2) # 4900 - mvc 832(192,%r3),832(%r2) # 4928 - -+ lgh %r8,.Ldh_cpu_cnt-0b(%r13) # Increment (online) CPU count -+ aghi %r8,1 -+ sth %r8,.Ldh_cpu_cnt-0b(%r13) - .Lcpy_locore_exit_64: -+ lgh %r10,.Ldh_real_cpu_cnt-0b(%r13) # Increment real CPU count -+ aghi %r10,1 -+ sth %r10,.Ldh_real_cpu_cnt-0b(%r13) -+ - lmg %r6,%r15,248(%r15) - br %r14 # return to caller - .Lpage_align_64: -@@ -755,10 +797,12 @@ _ssch_32: - lr %r3,%r10 # and irb address as parameters - bas %r14,_wait4de_32-0b(%r13) # wait until DE or error - tm 9(%r10),0xff # test channel status -- bnz 5f-0b(%r13) -- tm 8(%r10),0xd2 # f3 test device status -+ bz 3f-0b(%r13) -+ bct %r9,1b-0b(%r13) # something went wrong, retry. -+3: tm 8(%r10),0xd2 # test device status - bz 4f-0b(%r13) - bct %r9,1b-0b(%r13) # something went wrong, retry. -+ j 5f # retries failed -> panic - - 4: lm %r6,%r15,120(%r15) - br %r14 -@@ -864,7 +908,8 @@ _take_dump_32: - spt .Lcpu_timer-.Lbase(%r13) # set cpu timer to future - - lhi %r6,ZERO_MEM_START # clear memory -- lhi %r7,ZERO_MEM_START + ZERO_MEM_SIZE -+ lhi %r7,ZERO_MEM_START -+ ahi %r7,ZERO_MEM_SIZE - sr %r7,%r6 - sr %r8,%r8 - sr %r9,%r9 -@@ -897,6 +942,14 @@ _take_dump_32: - - bas %r14,_count_mem_32-.Lbase(%r13) - -+ # copy dump header -+ -+ stck .Ldh_time-.Lbase(%r13) # store time -+ stidp .Ldh_cpuid-.Lbase(%r13) # store CPU ID -+ -+ lhi %r3,HEADER_PAGE_START -+ mvc 0(256,%r3),.Ldh_dumpheader-.Lbase(%r13) -+ - # dump memory - - l %r14,.Ldump_mem_32-.Lbase(%r13) -@@ -952,15 +1005,22 @@ _count_mem_32: - mr %r2,%r1 # mem size in bytes in %r3 - - st %r3,.Ldh_real_mem_size+4-0b(%r13) -- cl %r6,.Lmem_upper_limit+4-0b(%r13) # check if we have an upper limit -+ larl %r7,.Lmem_upper_limit+4 -+ l %r6,0(%r7) # check if we have an upper limit -+ clr %r3,%r6 - bl .Lsavemem-0b(%r13) -- l %r3,.Lmem_upper_limit+4-0b(%r13) # upper mem limit set -> use it! -+ lr %r3,%r6 # upper mem limit set -> use it! - .Lsavemem: - st %r3,.Ldh_mem_size+4-0b(%r13) # store memory size - st %r3,.Ldh_mem_end+4-0b(%r13) # store memory end - srl %r3,12 # calculate page count (/ 4096) - st %r3,.Ldh_num_pages-0b(%r13) # store page count - -+ clr %r6,%r3 -+ bne .Lexit-0b(%r13) -+ larl %r2,.Lmsg_mem_limit_set # print mem limit warning -+ bras %r14,_sclp_print -+.Lexit: - lm %r6,%r15,120(%r15) - br %r14 - .Lonemb: -@@ -1112,6 +1172,15 @@ _copy_lowcore_32: - - ###### copy lowcore ###### - -+ lh %r8,.Ldh_cpu_cnt-0b(%r13) # Save lowcore pointer (32 bit) -+ sll %r8,2 # in dump header -+ lhi %r9,PREFIX_ARR_START -+ ar %r9,%r8 -+ st %r3,0(%r9) -+ -+ lh %r8,__LC_CPU_ADDRESS(%r0) # copy cpu address -+ sth %r8,__LC_CPU_ADDRESS(%r3) # to lowcore -+ - # |-----------------------------------------------------------| - # | Decimal | Length | Data | - # | Address | in Bytes | | -@@ -1131,7 +1200,14 @@ _copy_lowcore_32: - mvc 256(12,%r3),256(%r0) - mvc 288(224,%r3),288(%r0) - -+ lh %r8,.Ldh_cpu_cnt-0b(%r13) # Increment (online) CPU count -+ ahi %r8,1 -+ sth %r8,.Ldh_cpu_cnt-0b(%r13) - .Lcpy_locore_exit: -+ lh %r10,.Ldh_real_cpu_cnt-0b(%r13) # Increment real CPU count -+ ahi %r10,1 -+ sth %r10,.Ldh_real_cpu_cnt-0b(%r13) -+ - lm %r6,%r15,120(%r15) - br %r14 # return to caller - .Lpage_align: -@@ -1303,6 +1379,14 @@ _print_exit_message: - .byte 0xf4, 0x40, 0x82, 0x89, 0xa3, 0x40, 0xd6, 0xe2 - .byte 0x00 - -+# INFO: Using memory limit -+ -+.Lmsg_mem_limit_set: -+ .byte 0xc9, 0xd5, 0xc6, 0xd6, 0x7a -+ .byte 0x40, 0xe4, 0xa2, 0x89, 0x95, 0x87, 0x40, 0x94 -+ .byte 0x85, 0x94, 0x96, 0x99, 0xa8, 0x40, 0x93, 0x89 -+ .byte 0x94, 0x89, 0xa3, 0x25, 0x00 -+ - # "00000000 / 00000000 MB" - - .Lmsg_progress_mb: -diff --git a/zipl/boot/eckd2dump.S b/zipl/boot/eckd2dump.S -index bb5a6f5..5c437bc 100644 ---- a/zipl/boot/eckd2dump.S -+++ b/zipl/boot/eckd2dump.S -@@ -21,7 +21,7 @@ - - /* General defines */ - --#define IPL_BS 0x1000 -+#define IPL_BS 0x2000 - #define BLOCKS_PER_WRITE 64 /* makes 256K with 4K blksize */ - - ################################################################################ -@@ -32,9 +32,9 @@ - ################################################################################ - - #if defined(__s390x__) --dump_magic: .long 0x5a45434b, 0x44363401 # "ZECKD64", version 1 -+dump_magic: .long 0x5a45434b, 0x44363402 # "ZECKD64", version 2 - #else --dump_magic: .long 0x5a45434b, 0x44333101 # "ZECKD31", version 1 -+dump_magic: .long 0x5a45434b, 0x44333102 # "ZECKD31", version 2 - #endif - - #if defined(__s390x__) -@@ -153,20 +153,14 @@ _dump_mem_64: - # write header - - .Lheaders: # write dump headers -- stck .Ldh_time-0b(%r13) # store time -- stidp .Ldh_cpuid-0b(%r13) # store cpu id -- - llgf %r11,.Ldev_start_blk-0b(%r13) # start block - - lgr %r2,%r11 -- lghi %r3,TMP_PAGE_START -- mvc 0(256,%r3),.Ldh_dumpheader-0b(%r13) -- # copy dump header to page -- # boundary -- llgf %r4,.Lheader_size-0b(%r13) -+ lghi %r3,HEADER_PAGE_START -+ lghi %r4,HEADER_SIZE - srda %r4,32 # shift ==> 64 bit number - llgf %r6,.Ldev_blk_size-0b(%r13) # get blocksize -- -+ - dr %r4,%r6 # nr of blocks for header = - # HEADER_SIZE / BLOCKSIZE = r5 - lgr %r4,%r5 -@@ -208,7 +202,6 @@ _dump_mem_64: - lmg %r6,%r15,248(%r15) - br %r14 # return to caller - .Lbytes_per_write: .long 0x00000000 --.Lheader_size: .long HEADER_SIZE - .Lblocks_per_write: .word BLOCKS_PER_WRITE - - ################################################################################ -@@ -503,20 +496,14 @@ _dump_mem_32: - # write header - - .Lheaders: # write dump headers -- stck .Ldh_time-0b(%r13) # store time -- stidp .Ldh_cpuid-0b(%r13) # store cpu id -- - l %r11,.Ldev_start_blk-0b(%r13) # start block - - lr %r2,%r11 -- lhi %r3,TMP_PAGE_START -- mvc 0(256,%r3),.Ldh_dumpheader-0b(%r13) -- # copy dump header to page -- # boundary -- l %r4,.Lheader_size-0b(%r13) -+ lhi %r3,HEADER_PAGE_START -+ lhi %r4,HEADER_SIZE - srda %r4,32 # shift ==> 64 bit number - l %r6,.Ldev_blk_size-0b(%r13) # get blocksize -- -+ - dr %r4,%r6 # nr of blocks for header = - # HEADER_SIZE / BLOCKSIZE = r5 - lr %r4,%r5 -@@ -559,7 +546,6 @@ _dump_mem_32: - lm %r6,%r15,120(%r15) - br %r14 # return to caller - .Lbytes_per_write: .long 0x00000000 --.Lheader_size: .long HEADER_SIZE - .Lblocks_per_write: .word BLOCKS_PER_WRITE - - ################################################################################ -diff --git a/zipl/boot/eckd2mvdump.S b/zipl/boot/eckd2mvdump.S -index 2b1be43..17b959d 100644 ---- a/zipl/boot/eckd2mvdump.S -+++ b/zipl/boot/eckd2mvdump.S -@@ -13,7 +13,7 @@ - - /* General defines */ - --#define MVDUMP_TOOL_SIZE 0x1000 /* length of dump tool without parmtable */ -+#define MVDUMP_TOOL_SIZE 0x2000 /* length of dump tool without parmtable */ - #define BLOCKS_PER_WRITE 64 /* makes 256K with 4Ki blksize */ - #define PTE_LENGTH 13 /* length of parameter table entry */ - #define MAGIC_BLOCK_OFFSET 3 /* dump tool starts on track 0, block 3 */ -@@ -25,7 +25,7 @@ - # %r4 : load address - ################################################################################ - --dump_magic: .long 0x5a4d554c, 0x54363401 # "ZMULT64", version 1 -+dump_magic: .long 0x5a4d554c, 0x54363402 # "ZMULT64", version 1 - - /******************************** 64 BIT only**********************************/ - -@@ -102,8 +102,6 @@ _dump_mem_64: - bras %r14,_init_print_progress_64 - - # prepare dump header info -- stck .Ldh_time-0b(%r13) # store time -- stidp .Ldh_cpuid-0b(%r13) # store cpu id - mvi .Ldh_mvdump-0b(%r13),0x01 # store mvdump indicator - - .Lnextvol: -@@ -133,7 +131,9 @@ _dump_mem_64: - lghi %r2,CCW_CHAIN_START # point to 1st CCW in chain - mvi 16(%r2),0x86 # move read opcode into CCW - lhi %r2,MAGIC_BLOCK_OFFSET # start block of dump tool -- ar %r2,%r5 # start block of parameter table -+ lgr %r10,%r5 # mvdumptool size = 0x2000 -+ sll %r10,1 # (header size * 2) -+ ar %r2,%r10 # start block of parameter table - lghi %r3,TMP_PAGE_START # destination of read operation - lghi %r4,1 # number of blocks to read - bas %r14,_ioblock_64-0b(%r13) # read parameter table -@@ -152,7 +152,8 @@ _dump_mem_64: - # The dump signature is located at offset 512 relative to the partition start - - .Lcheck_sign: -- tm .Lforce-0b(%r13),0x01 # was zipl --force specified? -+ larl %r7,.Lforce -+ tm 0(%r7),0x01 # was zipl --force specified? - bo .Lheaders-0b(%r13) # yes, skip signature check - llgf %r2,.Ldev_start_blk-0b(%r13) # start block of partition - lghi %r3,TMP_PAGE_START # destination of read operation -@@ -178,9 +179,7 @@ _dump_mem_64: - llgf %r11,.Ldev_start_blk-0b(%r13) # start block - - lgr %r2,%r11 -- lghi %r3,TMP_PAGE_START -- mvc 0(256,%r3),.Ldh_dumpheader-0b(%r13) # copy dump header to page -- # boundary -+ lghi %r3,HEADER_PAGE_START - mvc 512(8,%r3),.Ldh_magic_number-0b(%r13) # preserve signature - lgr %r4,%r5 - lgr %r12,%r5 # save nr of blocks -@@ -198,6 +197,8 @@ _dump_mem_64: - l %r12,.Ldh_vol_nr-0b(%r13) # get current volume number - ahi %r12,1 # increment volume number - st %r12,.Ldh_vol_nr-0b(%r13) # store next volume number -+ lghi %r3,HEADER_PAGE_START -+ mvc 0(256,%r3),.Ldh_dumpheader-0b(%r13) - lhi %r11,PARAM_START # - ch %r12,8(%r11) # last dump target? - bl .Lmloop2-0b(%r13) # no, initialize next target -diff --git a/zipl/boot/fba0.S b/zipl/boot/fba0.S -index 71f508f..14e65dc 100644 ---- a/zipl/boot/fba0.S -+++ b/zipl/boot/fba0.S -@@ -35,9 +35,25 @@ _start: - .long 0x43000000+.Llo6+512,0x40000008 # locate record 6 - .long 0x42002C00,0x60000200 # bytes 3072-3584 of 2nd stage - .long 0x43000000+.Llo7+512,0x40000008 # locate record 7 -- .long 0x42002E00,0x20000200 # bytes 3584-4096 of 2nd stage --# offset 2 in .Llo[0-7]: block count (unsigned short) = 1 --# offset 4 in .Llo[0-7]: block number (unsigned long) -+ .long 0x42002E00,0x60000200 # bytes 3584-4096 of 2nd stage -+ .long 0x43000000+.Llo8+512,0x40000008 # locate record 8 -+ .long 0x42003000,0x60000200 # bytes 4096-4608 of 2nd stage -+ .long 0x43000000+.Llo9+512,0x40000008 # locate record 9 -+ .long 0x42003200,0x60000200 # bytes 4608-5120 of 2nd stage -+ .long 0x43000000+.Llo10+512,0x40000008 # locate record 10 -+ .long 0x42003400,0x60000200 # bytes 5120-5632 of 2nd stage -+ .long 0x43000000+.Llo11+512,0x40000008 # locate record 11 -+ .long 0x42003600,0x60000200 # bytes 5632-6144 of 2nd stage -+ .long 0x43000000+.Llo12+512,0x40000008 # locate record 12 -+ .long 0x42003800,0x60000200 # bytes 6144-6656 of 2nd stage -+ .long 0x43000000+.Llo13+512,0x40000008 # locate record 13 -+ .long 0x42003A00,0x60000200 # bytes 6656-7168 of 2nd stage -+ .long 0x43000000+.Llo14+512,0x40000008 # locate record 14 -+ .long 0x42003C00,0x60000200 # bytes 7168-7680 of 2nd stage -+ .long 0x43000000+.Llo15+512,0x40000008 # locate record 15 -+ .long 0x42003E00,0x20000200 # bytes 7680-8192 of 2nd stage -+# offset 2 in .Llo[0-15]: block count (unsigned short) = 1 -+# offset 4 in .Llo[0-15]: block number (unsigned long) - .Llo0: .long 0x06000001,0x00000000 - .Llo1: .long 0x06000001,0x00000000 - .Llo2: .long 0x06000001,0x00000000 -@@ -46,4 +62,12 @@ _start: - .Llo5: .long 0x06000001,0x00000000 - .Llo6: .long 0x06000001,0x00000000 - .Llo7: .long 0x06000001,0x00000000 -+.Llo8: .long 0x06000001,0x00000000 -+.Llo9: .long 0x06000001,0x00000000 -+.Llo10: .long 0x06000001,0x00000000 -+.Llo11: .long 0x06000001,0x00000000 -+.Llo12: .long 0x06000001,0x00000000 -+.Llo13: .long 0x06000001,0x00000000 -+.Llo14: .long 0x06000001,0x00000000 -+.Llo15: .long 0x06000001,0x00000000 - .Lend: -diff --git a/zipl/boot/fba2dump.S b/zipl/boot/fba2dump.S -index e14d047..c0366b6 100644 ---- a/zipl/boot/fba2dump.S -+++ b/zipl/boot/fba2dump.S -@@ -20,7 +20,7 @@ - - /* General defines */ - --#define IPL_BS 0x1000 -+#define IPL_BS 0x2000 - #define BLOCKS_PER_WRITE 64 - #define FBA_BLK_SIZE 0x200 - #define STAGE2_DESC 0x218 -@@ -33,9 +33,9 @@ - ################################################################################ - - #if defined(__s390x__) --dump_magic: .long 0x5a444642, 0x41363401 # ZDFBA64, version 1 -+dump_magic: .long 0x5a444642, 0x41363402 # ZDFBA64, version 2 - #else --dump_magic: .long 0x5a444642, 0x41333101 # ZDFBA31, version 1 -+dump_magic: .long 0x5a444642, 0x41333102 # ZDFBA31, version 2 - #endif - - #if defined(__s390x__) -@@ -129,18 +129,14 @@ _dump_mem_64: - - # write header - -- stck .Ldh_time-0b(%r13) # store time -- stidp .Ldh_cpuid-0b(%r13) # store cpu id -- - llgf %r11,.Ldev_start_blk-0b(%r13) # start block - - lgr %r2,%r11 -- lghi %r3, TMP_PAGE_START -- mvc 0(256,%r3),.Ldh_dumpheader-0b(%r13) # move to 4k boundary -- llgf %r4,.Lheader_size-0b(%r13) -+ lghi %r3,HEADER_PAGE_START -+ lghi %r4,HEADER_SIZE - srda %r4,32 # shift ==> 64 bit number - llgf %r6,.Ldev_blk_size-0b(%r13) # get blocksize -- -+ - dr %r4,%r6 # nr of blocks for header = - # HEADER_SIZE / BLOCKSIZE = r5 - lgr %r4,%r5 -@@ -181,7 +177,6 @@ _dump_mem_64: - lmg %r6,%r15,248(%r15) - br %r14 # return to caller - .Lbytes_per_write: .long 0x00000000 --.Lheader_size: .long HEADER_SIZE - .Lblocks_per_write: .word BLOCKS_PER_WRITE - - ################################################################################ -@@ -363,20 +358,14 @@ _dump_mem_32: - st %r11,.Lbytes_per_write-0b(%r13) - - # write header -- -- stck .Ldh_time-0b(%r13) # store time -- stidp .Ldh_cpuid-0b(%r13) # store cpu id -- - l %r11,.Ldev_start_blk-0b(%r13) # start block - - lr %r2,%r11 -- lhi %r3, TMP_PAGE_START -- mvc 0(256,%r3),.Ldh_dumpheader-0b(%r13) # move to 4k boundary -- -- l %r4,.Lheader_size-0b(%r13) -+ lhi %r3,HEADER_PAGE_START -+ lhi %r4,HEADER_SIZE - srda %r4,32 # shift ==> 64 bit number - l %r6,.Ldev_blk_size-0b(%r13) # get blocksize -- -+ - dr %r4,%r6 # nr of blocks for header = - # HEADER_SIZE / BLOCKSIZE = r5 - lr %r4,%r5 -@@ -418,7 +407,6 @@ _dump_mem_32: - lm %r6,%r15,120(%r15) - br %r14 # return to caller - .Lbytes_per_write: .long 0x00000000 --.Lheader_size: .long HEADER_SIZE - .Lblocks_per_write: .word BLOCKS_PER_WRITE - - ################################################################################ -diff --git a/zipl/boot/tapedump.S b/zipl/boot/tapedump.S -index f7f25e6..aded0a8 100644 ---- a/zipl/boot/tapedump.S -+++ b/zipl/boot/tapedump.S -@@ -21,20 +21,15 @@ - #endif - #include "sclp.S" - --#define IPL_BS 0x1000 -+#define IPL_BS 0x2000 - #define BLOCK_SIZE 0x8000 /* 32 KB */ - #define DUMP_TOOL_START 0x2000 /* tool is loaded to this address in order */ - /* not to overwrite page 0 */ --#define EOV_MARKER_SIZE 8 --#define EOV_LABEL 0x454e444f,0x46564f4c /* ENDOFVOL */ -- - /* Tape display messages */ - - #ifdef LOWER_CASE --#define DISP_NEXT_VOL 0x409585a7,0xa35ca596,0x93000000 /* next*vol */ - #define DISP_DUMP_END 0x2084a494,0x975c8595,0x84000000 /* dump*end */ - #else --#define DISP_NEXT_VOL 0x40d5c5e7,0xe35ce5d6,0xd3000000 /* NEXT*VOL */ - #define DISP_DUMP_END 0x20c4e4d4,0xd75cc5d5,0xc4000000 /* DUMP*END */ - #endif - -@@ -73,18 +68,16 @@ - _start: - basr %r13,0 - .Linit_base: -- la %r9,0 -- st %r9,.Ldh_arch-.Linit_base(%r13) # init arch -+ xc .Ldh_arch-.Linit_base(4,%r13),.Ldh_arch-.Linit_base(%r13) - l %r15,1f-.Linit_base(%r13) # load end of stack address -- tm __LC_ARCH_MODE_ID(%r9),0x01 # check arch mode -+ tm __LC_ARCH_MODE_ID(%r0),0x01 # check arch mode - bnz .Larch_64-.Linit_base(%r13) - - /* 32 bit store status */ - -- l %r14,.Lstore_status_32-.Linit_base(%r13) -+ larl %r14,.Lstore_status_32 - basr %r14,%r14 -- la %r10,ARCH_S390_ID -- st %r10,.Ldh_arch-.Linit_base(%r13) -+ mvi .Ldh_arch+3-.Linit_base(%r13),ARCH_S390_ID - .Larch_64: - la %r7,2 # first try code 2: - la %r6,0 # 64 bit psws are restored -@@ -103,16 +96,16 @@ _start: - - /* 64 bit store status */ - -- llgf %r14,.Lstore_status_64-0b(%r13) -+ larl %r14,_store_status_64 - basr %r14,%r14 - lghi %r10,ARCH_S390X_ID - st %r10,.Ldh_arch-0b(%r13) --.Larch_32: -+.Larch_32: - llgf %r2,IPL_SC # load ipl device subchannel id -- llgf %r14,.Lenable_device_64-0b(%r13) -+ larl %r14,_enable_device_64 - basr %r14,%r14 -- bas %r14,_get_device_characteristics_64-0b(%r13) -- llgf %r14,.Ltake_dump_64-0b(%r13) -+ bas %r14,_get_device_characteristics_64-0b(%r13) -+ larl %r14,_take_dump_64 - basr %r14,%r14 - 1: .long 0x10000-128 # end of stack - -@@ -138,11 +131,8 @@ _dump_mem_64: - # - # write header - # -- stck .Ldh_time-0b(%r13) # store time -- stidp .Ldh_cpuid-0b(%r13) # store cpu id -- lghi %r2, TMP_PAGE_START -- mvc 0(256,%r2),.Ldh_dumpheader-0b(%r13) # move to 4k boundary -- llgf %r3,.Lheader_size-0b(%r13) # size of header -+ lghi %r2,HEADER_PAGE_START -+ lghi %r3,HEADER_SIZE # size of header - lgr %r4,%r3 # blocksize - bas %r14,_writer_64-0b(%r13) - -@@ -153,24 +143,21 @@ _dump_mem_64: - lgr %r12,%r10 # save mem size - lghi %r11,0 # start - --1: - lgr %r2,%r11 # start - lgr %r3,%r10 # length - llgf %r4,.Lblock_size-0b(%r13) # blocksize - bas %r14,_writer_64-0b(%r13) # write page -- -+ - clgr %r2,%r12 -- bhe 2f-0b(%r13) -+ bhe 1f-0b(%r13) - -- # Next Volume -+ # Cartridge full - -- lgr %r11,%r2 # save next start addr -- bas %r14,_next_vol_64-0b(%r13) -- lgr %r10,%r12 # update length: -- sgr %r10,%r11 # memsize-act written -- b 1b-0b(%r13) -+ la %r2,EMEM -+ larl %r14,_panik_64 -+ basr %r14,%r14 - --2: # All memory written -+1: # All memory written - - # - # write end marker -@@ -191,56 +178,12 @@ _dump_mem_64: - - lmg %r6,%r15,248(%r15) - br %r14 # return to caller --.Lheader_size: -- .long HEADER_SIZE - .Lblock_size: - .long BLOCK_SIZE - .Lend_text: - .long DISP_DUMP_END - - ################################################################################ --# _next_vol --# - no parameters --################################################################################ -- --_next_vol_64: -- stmg %r6,%r15,48(%r15) -- basr %r13,0 # base register --0: aghi %r15,-200 # create stack frame -- -- /* write end of volume marker */ -- -- lghi %r2, TMP_PAGE_START -- mvc 0(256,%r2),.Leov_marker-0b(%r13) # move to 4k boundary -- lghi %r3,EOV_MARKER_SIZE -- lghi %r4,EOV_MARKER_SIZE -- bas %r14,_writer_64-0b(%r13) -- -- /* write two tape marks (End of Tape) */ -- -- bas %r14,_tapemark_64-0b(%r13) -- bas %r14,_tapemark_64-0b(%r13) -- -- /* rewind unload */ -- -- bas %r14,_rewind_unload_64-0b(%r13) -- -- /* write header to next volume */ -- -- l %r10,.Ldh_vol_nr-0b(%r13) -- ahi %r10,1 -- st %r10,.Ldh_vol_nr-0b(%r13) -- la %r2,.Ldh_dumpheader-0b(%r13) -- llgf %r3,.Ldh_header_size-0b(%r13) -- llgf %r4,.Ldh_header_size-0b(%r13) -- bas %r14,_writer_64-0b(%r13) -- -- lmg %r6,%r15,248(%r15) -- br %r14 # return to caller --.Leov_marker: -- .long EOV_LABEL -- --################################################################################ - # subroutine for writing to tape - # Parameters: - # -r2: start address -@@ -303,8 +246,7 @@ _writer_64: - - /* build error code: first byte ERA, last byte our error code */ - -- lghi %r2,0 -- ic %r2,.Ltmp_data+3-0b(%r13) # get ERA -+ llgc %r2,.Ltmp_data+3-0b(%r13) # get ERA - sll %r2,24 # move it to first byte - ahi %r2,ETAPE_WRITE - -@@ -318,8 +260,7 @@ _writer_64: - - # unit exception: We reached End of Tape - -- lgr %r2,%r10 # r2 contains -- agr %r2,%r12 # next write addr -+ la %r2,0(%r12,%r10) # r2 contains next write addr - b 3f-0b(%r13) # return - - 1: -@@ -356,6 +297,9 @@ _writer_64: - .Lorbwrite: - .long 0x00000000,0x0082ff00,.Lccwwrite - .align 8 -+.Lorbsense: -+ .long 0x00000000,0x0080ff00,.Lccwsense -+ .align 8 - .Lccwwrite_compressed: /* note that 3480 does not support IDRC */ - .long 0xdb400001,.Lmodsetbyte - .Lccwwrite: -@@ -365,7 +309,8 @@ _writer_64: - .Lmodsetbyte: - .long 0x08000000 - .align 8 -- -+.Lccwsense: -+ .long 0x04200020,.Ltmp_data - ################################################################################ - # Translate binary hex to decimal ebcdic - # -r2: value (bin) -@@ -385,125 +330,6 @@ _hex_to_ebcdic_64: - .long 0x0,0x0 - - ################################################################################ --# rewind unload tape --# - no parameters --################################################################################ -- --_rewind_unload_64: -- stmg %r6,%r15,48(%r15) -- basr %r13,0 # base register --0: aghi %r15,-200 # create stack frame -- -- /* 3480/3490/3590: rewind unload */ -- -- llgf %r2,IPL_SC # subchannel id -- la %r3,.Lorbrew_unload-0b(%r13) -- la %r4,.Lirb-0b(%r13) -- lghi %r5,1 # 1 retries -- bas %r14,_ssch_64-0b(%r13) # do the rewind unload -- -- /* check for 3590 */ -- -- lh %r9,.Ltape_type-0b(%r13) -- chi %r9,TAPE_3590 -- bne .Lnot3590-0b(%r13) -- -- tm .Lirb+8-0b(%r13),0x2 # unit check? -- bz 3f-0b(%r13) # no unit check: rewunl worked -- -- /* 3590: retry rewind unload */ -- -- llgf %r2,IPL_SC -- la %r3,.Lorbrew_unload-0b(%r13) -- la %r4,.Lirb-0b(%r13) -- la %r5,1 -- bas %r14,_ssch_64-0b(%r13) -- -- b 3f-0b(%r13) -- -- /* 3480/90 */ -- --.Lnot3590: -- -- /* 3480/3490 sense */ -- -- llgf %r2,IPL_SC -- la %r3,.Lorbsense-0b(%r13) -- la %r4,.Lirb-0b(%r13) -- lghi %r5,1 -- bas %r14,_ssch_64-0b(%r13) -- -- cli .Ltmp_data+3-0b(%r13),0x2b # check load sense byte 3 -- # 3480: ERA: rewunl completed (2b) -- be 3f-0b(%r13) -- -- lghi %r2,ETAPE_REWUNL_1 -- cli .Ltmp_data+3-0b(%r13),0x51 # check load sense byte 3 -- # 3490: ERA: EOV (51) -- bne 2f-0b(%r13) -- -- /* 3490: retry rewind unload */ -- -- llgf %r2,IPL_SC -- la %r3,.Lorbrew_unload-0b(%r13) -- la %r4,.Lirb-0b(%r13) -- la %r5,1 -- bas %r14,_ssch_64-0b(%r13) -- -- /* 3490: sense */ -- -- l %r2,IPL_SC -- la %r3,.Lorbsense-0b(%r13) -- la %r4,.Lirb-0b(%r13) -- lghi %r5,1 -- bas %r14,_ssch_64-0b(%r13) -- -- cli .Ltmp_data+3-0b(%r13),0x52 # check load sense byte 3 -- # 3490: ERA: EOV Complete (52) -- be 3f-0b(%r13) -- -- lghi %r2,ETAPE_REWUNL_2 -- --2: -- /* Something went wrong --> panik */ -- -- l %r14,.Lpanik_64-0b(%r13) -- basr %r14,%r14 -- --3: -- /* Tell operator to insert next cartridge */ -- -- la %r2,.Lnext_vol_text-0b(%r13) -- bas %r14,_load_display_64-0b(%r13) -- -- /* wait for UException/DE/Attention (85) */ -- --.Lwait_loop: -- llgf %r2,IPL_SC -- la %r3,.Lirb-0b(%r13) -- bas %r14,_wait4de_64-0b(%r13) -- cli .Lirb+8-0b(%r13),0x85 -- bne .Lwait_loop-0b(%r13) -- --4: lmg %r6,%r15,248(%r15) -- br %r14 -- .align 8 --.Lorbsense: -- .long 0x00000000,0x0080ff00,.Lccwsense -- .align 8 --.Lorbrew_unload: -- .long 0x00000000,0x0080ff00,.Lccwrew_unload -- .align 8 --.Lccwrew_unload: -- .long 0x0f200000,0x00000000 -- .align 8 --.Lnext_vol_text: -- .long DISP_NEXT_VOL -- .align 8 --.Lccwsense: -- .long 0x04200020,.Ltmp_data -- --################################################################################ - # subroutine for reading the device characteristics - ################################################################################ - -@@ -682,11 +508,9 @@ _dump_mem_32: - # - # write header - # -- stck .Ldh_time-0b(%r13) # store time -- stidp .Ldh_cpuid-0b(%r13) # store cpu id -- lhi %r2, TMP_PAGE_START -+ lhi %r2,HEADER_PAGE_START - mvc 0(256,%r2),.Ldh_dumpheader-0b(%r13) # move to 4k boundary -- l %r3,.Lheader_size-0b(%r13) # size of header -+ lhi %r3,HEADER_SIZE # size of header - lr %r4,%r3 # blocksize - bas %r14,_writer_32-0b(%r13) - -@@ -697,25 +521,21 @@ _dump_mem_32: - lr %r12,%r10 # save mem size - la %r11,0 # start - --1: -- - lr %r2,%r11 # start - lr %r3,%r10 # length - l %r4,.Lblock_size-0b(%r13) # blocksize - bas %r14,_writer_32-0b(%r13) # write page - - clr %r2,%r12 -- bhe 2f-0b(%r13) -+ bhe 1f-0b(%r13) - -- # Next Volume -+ # Cartridge full - -- lr %r11,%r2 # save next start addr -- bas %r14,_next_vol_32-0b(%r13) -- lr %r10,%r12 # update length: -- sr %r10,%r11 # memsize-act written -- b 1b-0b(%r13) -- --2: # All memory written -+ la %r2,EMEM -+ larl %r14,_panik_32 -+ basr %r14,%r14 -+ -+1: # All memory written - # - # write end marker - # -@@ -735,8 +555,6 @@ _dump_mem_32: - - lm %r6,%r15,120(%r15) - br %r14 # return to caller --.Lheader_size: -- .long HEADER_SIZE - .Lblock_size: - .long BLOCK_SIZE - .Lend_text: -@@ -767,47 +585,6 @@ _init_tape_32: - .long 0x00000000 /* buffered mode + IDRC */ - - ################################################################################ --# _next_vol --################################################################################ -- --_next_vol_32: -- stm %r6,%r15,24(%r15) -- basr %r13,0 # base register --0: ahi %r15,-96 # create stack frame -- -- /* write end of volume marker */ -- la %r2,.Leov_marker-0b(%r13) -- lhi %r2, TMP_PAGE_START -- mvc 0(256,%r2),.Leov_marker-0b(%r13) # move to 4k boundary -- la %r3,EOV_MARKER_SIZE -- la %r4,EOV_MARKER_SIZE -- bas %r14,_writer_32-0b(%r13) -- -- /* write two tape marks (End of Tape) */ -- -- bas %r14,_tapemark_32-0b(%r13) -- bas %r14,_tapemark_32-0b(%r13) -- -- /* rewind unload */ -- -- bas %r14,_rewind_unload_32-0b(%r13) -- -- /* write header to next volume */ -- -- l %r10,.Ldh_vol_nr-0b(%r13) -- ahi %r10,1 -- st %r10,.Ldh_vol_nr-0b(%r13) -- la %r2,.Ldh_dumpheader-0b(%r13) -- l %r3,.Ldh_header_size-0b(%r13) -- l %r4,.Ldh_header_size-0b(%r13) -- bas %r14,_writer_32-0b(%r13) -- -- lm %r6,%r15,120(%r15) -- br %r14 # return to caller --.Leov_marker: -- .long EOV_LABEL -- --################################################################################ - # subroutine for writing to tape - # Parameters: - # -r2: start address -@@ -925,6 +702,9 @@ _writer_32: - .Lorbwrite: - .long 0x00000000,0x0080ff00,.Lccwwrite - .align 8 -+.Lorbsense: -+ .long 0x00000000,0x0080ff00,.Lccwsense -+ .align 8 - .Lccwwrite_compressed: /* note that 3480 does not support IDRC */ - .long 0xdb400001,.Lmodsetbyte - .Lccwwrite: -@@ -933,6 +713,8 @@ _writer_32: - .long 0x20000000,0x00000000,0x00000000 - .Lmodsetbyte: - .long 0x08000000 -+.Lccwsense: -+ .long 0x04200020,.Ltmp_data - - ################################################################################ - # Translate binary hex to decimal ebcdic -@@ -952,100 +734,6 @@ _hex_to_ebcdic_32: - .Lout_packed: - .long 0x0,0x0 - --################################################################################ --# rewind unload tape --# - no parameters --################################################################################ -- --_rewind_unload_32: -- stm %r6,%r15,24(%r15) -- basr %r13,0 # base register --0: ahi %r15,-96 # create stack frame -- -- /* 3480/3490: rewind unload */ -- -- l %r2,IPL_SC # subchannel id -- la %r3,.Lorbrew_unload-0b(%r13) -- la %r4,.Lirb-0b(%r13) -- la %r5,1 # no retries -- bas %r14,_ssch_32-0b(%r13) # do the rewind unload -- -- /* 3480/3490: sense */ -- -- l %r2,IPL_SC -- la %r3,.Lorbsense-0b(%r13) -- la %r4,.Lirb-0b(%r13) -- la %r5,1 -- bas %r14,_ssch_32-0b(%r13) -- -- cli .Ltmp_data+3-0b(%r13),0x2b # check load sense byte 3 -- # 3480: ERA: rewunl completed (2b) -- be 3f-0b(%r13) -- -- la %r2,ETAPE_REWUNL_1 -- cli .Ltmp_data+3-0b(%r13),0x51 # check load sense byte 3 -- # 3490: ERA: EOV (51) -- bne 2f-0b(%r13) -- -- /* 3490: retry rewind unload */ -- -- l %r2,IPL_SC -- la %r3,.Lorbrew_unload-0b(%r13) -- la %r4,.Lirb-0b(%r13) -- la %r5,1 -- bas %r14,_ssch_32-0b(%r13) -- -- /* 3490: sense */ -- -- l %r2,IPL_SC -- la %r3,.Lorbsense-0b(%r13) -- la %r4,.Lirb-0b(%r13) -- la %r5,1 -- bas %r14,_ssch_32-0b(%r13) -- -- cli .Ltmp_data+3-0b(%r13),0x52 # check load sense byte 3 -- # 3490: ERA: EOV Complete (52) -- be 3f-0b(%r13) -- -- la %r2,ETAPE_REWUNL_2 -- --2: -- /* Something went wrong --> panik */ -- -- l %r14,.Lpanik_32-0b(%r13) -- basr %r14,%r14 -- --3: -- /* Tell operator to insert next cartridge */ -- -- la %r2,.Lnext_vol_text-0b(%r13) -- bas %r14,_load_display_32-0b(%r13) -- -- /* wait for UException/DE/Attention (85) */ -- -- l %r2,IPL_SC -- la %r3,.Lirb-0b(%r13) -- bas %r14,_wait4de_32-0b(%r13) -- -- --4: lm %r6,%r15,120(%r15) -- br %r14 -- .align 8 --.Lorbsense: -- .long 0x00000000,0x0080ff00,.Lccwsense -- .align 8 --.Lorbrew_unload: -- .long 0x00000000,0x0080ff00,.Lccwrew_unload -- .align 8 --.Lccwrew_unload: -- .long 0x0f200000,0x00000000 -- .align 8 --.Lnext_vol_text: -- .long DISP_NEXT_VOL -- .align 8 --.Lccwsense: -- .long 0x04200020,.Ltmp_data -- - ################################################################################# subroutine for reading the device characteristics - ################################################################################ - _get_device_characteristics_32: -diff --git a/zipl/include/boot.h b/zipl/include/boot.h -index a3cac61..e27c93a 100644 ---- a/zipl/include/boot.h -+++ b/zipl/include/boot.h -@@ -39,8 +39,8 @@ struct boot_fba_stage0 { - uint64_t TIC; - uint64_t param1; - uint64_t param2; -- struct boot_fba_locread locread[8]; -- struct boot_fba_locdata locdata[8]; -+ struct boot_fba_locread locread[16]; -+ struct boot_fba_locdata locdata[16]; - } __attribute__ ((packed)); - - -diff --git a/zipl/src/boot.c b/zipl/src/boot.c -index 8fe0b36..8c6314c 100644 ---- a/zipl/src/boot.c -+++ b/zipl/src/boot.c -@@ -107,7 +107,7 @@ boot_init_fba_stage0(struct boot_fba_stage0* stage0, - /* Initialize stage 0 data */ - memcpy(stage0, DATA_ADDR(fba0), sizeof(struct boot_fba_stage0)); - /* Fill in blocklist for stage 2 loader */ -- if (stage2_count > 8) { -+ if (stage2_count > 16) { - error_reason("Not enough room for FBA stage 2 loader " - "(try larger block size)"); - return -1; -diff --git a/zipl/src/install.c b/zipl/src/install.c -index ec84821..3f72ff5 100644 ---- a/zipl/src/install.c -+++ b/zipl/src/install.c -@@ -645,8 +645,12 @@ overwrite_partition_start(int fd, struct disk_info* info, int mv_dump_magic) - return 0; - } - -- --static int check_partition_bounds(struct disk_info* info) -+/* -+ * Ensure that end block is within bounds. -+ * Force block size of 4KiB because otherwise there is not enough space -+ * to write the dump tool. -+ */ -+static int check_eckd_dump_partition(struct disk_info* info) - { - unsigned long long end_blk = info->geo.start + info->phy_blocks - 1; - -@@ -658,6 +662,11 @@ static int check_partition_bounds(struct disk_info* info) - info->phy_block_size) >> 20); - return -1; - } -+ if (info->phy_block_size != 4096) { -+ error_reason("unsupported DASD block size %d (should be 4096)", -+ info->phy_block_size); -+ return -1; -+ } - return 0; - } - -@@ -863,7 +872,7 @@ static int - install_dump_fba(int fd, struct disk_info* info, uint64_t mem) - { - struct boot_fba_stage0 stage0; -- disk_blockptr_t stage2_list[8]; -+ disk_blockptr_t stage2_list[16]; - blocknum_t block; - blocknum_t count; - void* buffer; -@@ -880,7 +889,7 @@ install_dump_fba(int fd, struct disk_info* info, uint64_t mem) - if (rc) - return rc; - count = (size + info->phy_block_size - 1) / info->phy_block_size; -- if (count > 8) { -+ if (count > 16) { - error_reason("FBA dump record is too large"); - free(buffer); - return -1; -@@ -1017,7 +1026,7 @@ install_dump(const char* device, struct job_target_data* target, uint64_t mem) - switch (info->type) { - case disk_type_eckd_classic: - case disk_type_eckd_compatible: -- if (check_partition_bounds(info)) { -+ if (check_eckd_dump_partition(info)) { - error_text("Dump target '%s'", device); - rc = -1; - break; -@@ -1128,7 +1137,7 @@ install_mvdump(char* const device[], struct job_target_data* target, int count, - info[i]->device, device[i]); - if (rc) - goto out; -- if (check_partition_bounds(info[i])) { -+ if (check_eckd_dump_partition(info[i])) { - error_text("Dump target '%s'", device[i]); - rc = -1; - goto out; --- -1.7.3.5 - diff --git a/0055-znetconf-support-for-OSA-CHPID-types-OSX-and-OSM.patch b/0055-znetconf-support-for-OSA-CHPID-types-OSX-and-OSM.patch deleted file mode 100644 index 68fadd0..0000000 --- a/0055-znetconf-support-for-OSA-CHPID-types-OSX-and-OSM.patch +++ /dev/null @@ -1,91 +0,0 @@ -From 7700e2333725199a42d929ceb7af803c609df196 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 28 Jan 2011 14:22:16 +0100 -Subject: [PATCH 55/61] znetconf: support for OSA CHPID types OSX and OSM - -Summary: znetconf: support for OSA CHPID types OSX and OSM -Description: New feature to enable znetconf to support the - new OSA CHPID types OSX and OSM. ---- - zconf/lsznet.raw | 12 ++++++++++++ - zconf/znetcontrolunits | 6 +++++- - 2 files changed, 17 insertions(+), 1 deletions(-) - -diff --git a/zconf/lsznet.raw b/zconf/lsznet.raw -index 9821ba3..a9f1247 100755 ---- a/zconf/lsznet.raw -+++ b/zconf/lsznet.raw -@@ -41,6 +41,8 @@ readonly -a CU_CARDTYPE=( - "LCS OSA" - "LCS CLAW" - "OSN" -+ "OSX" -+ "OSM" - ) - - readonly -a CU_DEVNAME=( -@@ -53,6 +55,8 @@ readonly -a CU_DEVNAME=( - eth - eth - osn -+ eth -+ eth - ) - - readonly -a CU_GROUPCHANNELS=( -@@ -65,6 +69,8 @@ readonly -a CU_GROUPCHANNELS=( - 2 - 2 - 3 -+ 3 -+ 3 - ) - - readonly -a CHPIDTYPES=( -@@ -72,6 +78,8 @@ readonly -a CHPIDTYPES=( - [0x11]=OSD - [0x15]=OSN - [0x24]=IQD -+ [0x30]=OSM -+ [0x31]=OSX - ) - - # whitelist of network devices for TCP/IP stack, e.g. for Linux installers -@@ -83,6 +91,10 @@ readonly -a CU_TCPIP=( - 3088/1e - 3088/01 - 3088/60 -+ 3088/61 -+ 1731/06 -+ 1731/02 -+ 1731/02 - ) - - readonly PREFIXFORMAT=[[:xdigit:]]* -diff --git a/zconf/znetcontrolunits b/zconf/znetcontrolunits -index e54e34b..7ee65f7 100644 ---- a/zconf/znetcontrolunits -+++ b/zconf/znetcontrolunits -@@ -23,6 +23,8 @@ readonly -a CU=( - 3088/60 - 3088/61 - 1731/06 -+ 1731/02 -+ 1731/02 - ) - - readonly -a CU_DEVDRV=( -@@ -33,7 +35,9 @@ readonly -a CU_DEVDRV=( - ctcm - lcs - lcs -- lcs -+ claw -+ qeth -+ qeth - qeth - ) - --- -1.7.3.5 - diff --git a/0056-iucvtty-do-not-specify-z-VM-user-ID-as-argument-to-l.patch b/0056-iucvtty-do-not-specify-z-VM-user-ID-as-argument-to-l.patch deleted file mode 100644 index 108100a..0000000 --- a/0056-iucvtty-do-not-specify-z-VM-user-ID-as-argument-to-l.patch +++ /dev/null @@ -1,68 +0,0 @@ -From ed12fec98c2365593e3b8bac14701112051028ad Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 28 Jan 2011 14:24:23 +0100 -Subject: [PATCH 56/61] iucvtty: do not specify z/VM user ID as argument to login -h - -Description: iucvtty: do not specify z/VM user ID as argument to login -h -Symptom: When establishing a terminal connection to an iucvtty - instance on a target system, iucvconn disconnects due to a - timeout of the login program. Users are not able to log in. -Problem: iucvtty passes the z/VM user ID of the originating guest - virtual machine as argument to the -h option of the login - program. The -h option specifies a host name that is used - when writing [uw]tmp records. Depending on the implementation - of the login program, login calls gethostbyname() to get the - FQDN of the hostname. If the target system does not have any - working network connection, the DNS query times out. The DNS - timeout might be greater than the timeout the login program - waits for user input. The login timeout is caused by a SIGALRM - signal that is registered before login calls gethostbyname(). - If the DNS timeout is greater, login exits. -Solution: Do not specify the z/VM user ID when iucvtty starts the login - program. The workaround to avoid timeouts is to explicitly - specify the login program when starting iucvtty. - For example, use "iucvtty TERMID -- /bin/login". ---- - iucvterm/src/iucvtty.c | 12 +++--------- - 1 files changed, 3 insertions(+), 9 deletions(-) - -diff --git a/iucvterm/src/iucvtty.c b/iucvterm/src/iucvtty.c -index b9a2754..ef7e212 100644 ---- a/iucvterm/src/iucvtty.c -+++ b/iucvterm/src/iucvtty.c -@@ -48,21 +48,15 @@ static void sig_handler(int sig) - /** - * exec_login_prog() - execute a login program - * @cmd: Path to the (login) program executable -- * @host: Originator host that is passed to /bin/login - */ --static int exec_login_prog(char *cmd[], const char *host) -+static int exec_login_prog(char *cmd[]) - { - int rc; - - if (cmd != NULL) - rc = execv(cmd[0], cmd); - else -- /* for root only: write hostname to [uw]tmp if set */ -- if (geteuid() == 0 && host != NULL) -- rc = execl("/bin/login", "/bin/login", "-h", host, -- (char *) NULL); -- else -- rc = execl("/bin/login", "/bin/login", (char *) NULL); -+ rc = execl("/bin/login", "/bin/login", (char *) NULL); - return rc; - } - -@@ -110,7 +104,7 @@ static int iucvtty_worker(int client, int master, int slave, - exit(2); - } - setenv("TERM", term_env, 1); -- if (exec_login_prog(cfg->cmd_parms, host)) { -+ if (exec_login_prog(cfg->cmd_parms)) { - print_error("Running the login program failed"); - iucvtty_tx_error(client, ERR_CANNOT_EXEC_LOGIN); - } --- -1.7.3.5 - diff --git a/0057-tunedasd-add-new-option-Q-query_reserve.patch b/0057-tunedasd-add-new-option-Q-query_reserve.patch deleted file mode 100644 index 097cad4..0000000 --- a/0057-tunedasd-add-new-option-Q-query_reserve.patch +++ /dev/null @@ -1,340 +0,0 @@ -From a3fd27a0ce5920b66afbf89cb83a9b61db9460c7 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 28 Jan 2011 14:26:53 +0100 -Subject: [PATCH 57/61] tunedasd: add new option -Q / --query_reserve - -Summary: tunedasd: add new option -Q / --query_reserve -Description: The new option -Q / --query_reserve uses the BIODASDSNID ioctl to - determine the device reservation status for the given device and - prints it to stdout. ---- - tunedasd/include/disk.h | 1 + - tunedasd/man/tunedasd.8 | 22 +++++++++- - tunedasd/src/disk.c | 65 +++++++++++++++++++++++++++++ - tunedasd/src/tunedasd.c | 104 +++++++++++++++++++++++++++-------------------- - 4 files changed, 147 insertions(+), 45 deletions(-) - -diff --git a/tunedasd/include/disk.h b/tunedasd/include/disk.h -index 4b98fb0..ece8cb5 100644 ---- a/tunedasd/include/disk.h -+++ b/tunedasd/include/disk.h -@@ -25,6 +25,7 @@ int disk_set_cache (char* device, char* cache, char* no_cyl); - int disk_reserve (char* device); - int disk_release (char* device); - int disk_slock (char* device); -+int disk_query_reserve_status(char* device); - int disk_profile (char* device, char* prof_item); - int disk_reset_prof (char* device); - -diff --git a/tunedasd/man/tunedasd.8 b/tunedasd/man/tunedasd.8 -index 2d99bdc..a96913e 100644 ---- a/tunedasd/man/tunedasd.8 -+++ b/tunedasd/man/tunedasd.8 -@@ -66,7 +66,6 @@ Enterprise Storage Servers (ESS): - (Record Access) - .IP "" 7 - More details about caching can be found in the 'Storage Control Reference' of the attached storage server. -- - .TP - .BR "\-n" " or " "\-\-no_cyl" " " - Number of cylinders to be cached (only valid together with --cache). -@@ -91,6 +90,27 @@ Profile info must be available and enabled - ('echo set on > /proc/dasd/statistics') - in the kernel to get valid results out of the profile commands. - .TP -+.BR "\-Q" " or " "\-\-query_reserve" -+Query the current reserve status of the device. -+The following states are defined: -+.br -+.IP " \(bu" 12 -+.I none: -+The device is not reserved. -+.IP " \(bu" 12 -+.I implicit: -+The device is not reserved, but there is a contingent or implicit -+allegiance to this Linux instance. -+.IP " \(bu" 12 -+.I other: -+The device is reserved to another operating system instance. -+.IP " \(bu" 12 -+.I reserved: -+The device is reserved to this Linux instance. -+.IP "" 7 -+More details about reserve/release can be found in the 'Storage Control Reference' of the attached storage server. -+ -+.TP - .BR "\-I" " or " "\-\-prof_item " - Print single profile item data row (without header). - .br -diff --git a/tunedasd/src/disk.c b/tunedasd/src/disk.c -index afbe851..3eaa4f3 100644 ---- a/tunedasd/src/disk.c -+++ b/tunedasd/src/disk.c -@@ -68,6 +68,25 @@ typedef struct attrib_data_t { - #define DASD_REC_ACCESS 0x5 - - /* -+ * Data returned by Sense Path Group ID (SNID) -+ */ -+struct dasd_snid_data { -+ struct { -+ __u8 group:2; -+ __u8 reserve:2; -+ __u8 mode:1; -+ __u8 res:3; -+ } __attribute__ ((packed)) path_state; -+ __u8 pgid[11]; -+} __attribute__ ((packed)); -+ -+struct dasd_snid_ioctl_data { -+ struct dasd_snid_data data; -+ __u8 path_mask; -+} __attribute__ ((packed)); -+ -+ -+/* - * DASD-IOCTLs (copied from dasd.h) - */ - /* Issue a reserve/release command, rsp. */ -@@ -85,6 +104,9 @@ typedef struct attrib_data_t { - /* Set Attributes (cache operations) */ - #define BIODASDSATTR _IOW (DASD_IOCTL_LETTER,2,attrib_data_t) - -+/* Get Sense Path Group ID (SNID) data */ -+#define BIODASDSNID _IOWR(DASD_IOCTL_LETTER, 1, struct dasd_snid_ioctl_data) -+ - - /* id definition for profile items */ - enum prof_id { -@@ -378,6 +400,49 @@ disk_slock (char* device) - return 0; - } - -+ -+/* -+ * Uses the Sense Path Group ID (SNID) ioctl to find out if -+ * a device is reserved to it's path group. -+ */ -+int -+disk_query_reserve_status(char* device) -+{ -+ int fd; -+ struct dasd_snid_ioctl_data snid; -+ -+ /* Open device file */ -+ fd = open (device, O_RDONLY); -+ if (fd == -1) { -+ error_print ("<%s> - %s", device, strerror (errno)); -+ return -1; -+ } -+ snid.path_mask = 0; -+ /* Release device */ -+ if (ioctl(fd, BIODASDSNID, &snid)) { -+ error_print("Could not read reserve status" -+ " for device <%s>", device); -+ close (fd); -+ return -1; -+ } -+ switch (snid.data.path_state.reserve) { -+ case 0: -+ printf("none\n"); -+ break; -+ case 1: -+ printf("implicit\n"); -+ break; -+ case 2: -+ printf("other\n"); -+ break; -+ case 3: -+ printf("reserved\n"); -+ break; -+ } -+ close (fd); -+ return 0; -+} -+ - int - disk_profile_summary (dasd_profile_info_t dasd_profile_info) - { -diff --git a/tunedasd/src/tunedasd.c b/tunedasd/src/tunedasd.c -index 05e0344..fbb7a1e 100644 ---- a/tunedasd/src/tunedasd.c -+++ b/tunedasd/src/tunedasd.c -@@ -47,6 +47,7 @@ static const char* usage_text[] = { - "-O, --slock Unconditional reserve device", - " Note: Use with care, this breaks an existing " - "lock", -+ "-Q, --query_reserve Print reserve status of device ", - "-P, --profile Print profile info of device", - "-I, --prof_item Print single profile item", - " (reqs/sects/sizes/total/totsect/start/irq/", -@@ -54,21 +55,22 @@ static const char* usage_text[] = { - "-R, --reset_prof Reset profile info of device" - }; - --#define CMD_KEYWORD_NUM 11 -+#define CMD_KEYWORD_NUM 12 - #define DEVICES_NUM 256 - - enum cmd_keyword_id { -- cmd_keyword_help = 0, -- cmd_keyword_version = 1, -- cmd_keyword_get_cache = 2, -- cmd_keyword_cache = 3, -- cmd_keyword_no_cyl = 4, -- cmd_keyword_reserve = 5, -- cmd_keyword_release = 6, -- cmd_keyword_slock = 7, -- cmd_keyword_profile = 8, -- cmd_keyword_prof_item = 9, -- cmd_keyword_reset_prof = 10, -+ cmd_keyword_help = 0, -+ cmd_keyword_version = 1, -+ cmd_keyword_get_cache = 2, -+ cmd_keyword_cache = 3, -+ cmd_keyword_no_cyl = 4, -+ cmd_keyword_reserve = 5, -+ cmd_keyword_release = 6, -+ cmd_keyword_slock = 7, -+ cmd_keyword_profile = 8, -+ cmd_keyword_prof_item = 9, -+ cmd_keyword_reset_prof = 10, -+ cmd_keyword_query_reserve = 11, - }; - - -@@ -77,17 +79,18 @@ static const struct { - char* keyword; - enum cmd_keyword_id id; - } keyword_list[] = { -- { "help", cmd_keyword_help }, -- { "version", cmd_keyword_version }, -- { "get_cache", cmd_keyword_get_cache }, -- { "cache", cmd_keyword_cache }, -- { "no_cyl", cmd_keyword_no_cyl }, -- { "reserve", cmd_keyword_reserve }, -- { "release", cmd_keyword_release }, -- { "slock", cmd_keyword_slock }, -- { "profile", cmd_keyword_profile }, -- { "prof_item", cmd_keyword_prof_item }, -- { "reset_prof", cmd_keyword_reset_prof } -+ { "help", cmd_keyword_help }, -+ { "version", cmd_keyword_version }, -+ { "get_cache", cmd_keyword_get_cache }, -+ { "cache", cmd_keyword_cache }, -+ { "no_cyl", cmd_keyword_no_cyl }, -+ { "reserve", cmd_keyword_reserve }, -+ { "release", cmd_keyword_release }, -+ { "slock", cmd_keyword_slock }, -+ { "profile", cmd_keyword_profile }, -+ { "prof_item", cmd_keyword_prof_item }, -+ { "reset_prof", cmd_keyword_reset_prof }, -+ { "query_reserve", cmd_keyword_query_reserve } - }; - - -@@ -100,21 +103,22 @@ enum cmd_key_state { - - /* Determines which combination of keywords are valid */ - enum cmd_key_state cmd_key_table[CMD_KEYWORD_NUM][CMD_KEYWORD_NUM] = { -- /* help vers get_ cach no_c rese rele sloc prof prof rese -- * ion cach e yl rve ase k ile _ite t_pr -- * e m of -- */ -- /* help */ { req, opt, opt, opt, opt, opt, opt, opt, opt, opt, opt }, -- /* version */ { inv, req, inv, inv, inv, inv, inv, inv, inv, inv, inv }, -- /* get_cache */ { opt, opt, req, inv, inv, inv, inv, inv, inv, inv, inv }, -- /* cache */ { opt, opt, inv, req, opt, inv, inv, inv, inv, inv, inv }, -- /* no_cyl */ { opt, opt, inv, req, req, inv, inv, inv, inv, inv, inv }, -- /* reserve */ { opt, opt, inv, inv, inv, req, inv, inv, inv, inv, inv }, -- /* release */ { opt, opt, inv, inv, inv, inv, req, inv, inv, inv, inv }, -- /* slock */ { opt, opt, inv, inv, inv, inv, inv, req, inv, inv, inv }, -- /* profile */ { opt, opt, inv, inv, inv, inv, inv, inv, req, opt, inv }, -- /* prof_item */ { opt, opt, inv, inv, inv, inv, inv, inv, req, req, inv }, -- /* reset_prof */ { opt, opt, inv, inv, inv, inv, inv, inv, inv, inv, req }, -+ /* help vers get_ cach no_c rese rele sloc prof prof rese quer -+ * ion cach e yl rve ase k ile _ite t_pr y_re -+ * e m of serv -+ */ -+ /* help */ { req, opt, opt, opt, opt, opt, opt, opt, opt, opt, opt, inv }, -+ /* version */ { inv, req, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv }, -+ /* get_cache */ { opt, opt, req, inv, inv, inv, inv, inv, inv, inv, inv, inv }, -+ /* cache */ { opt, opt, inv, req, opt, inv, inv, inv, inv, inv, inv, inv }, -+ /* no_cyl */ { opt, opt, inv, req, req, inv, inv, inv, inv, inv, inv, inv }, -+ /* reserve */ { opt, opt, inv, inv, inv, req, inv, inv, inv, inv, inv, inv }, -+ /* release */ { opt, opt, inv, inv, inv, inv, req, inv, inv, inv, inv, inv }, -+ /* slock */ { opt, opt, inv, inv, inv, inv, inv, req, inv, inv, inv, inv }, -+ /* profile */ { opt, opt, inv, inv, inv, inv, inv, inv, req, opt, inv, inv }, -+ /* prof_item */ { opt, opt, inv, inv, inv, inv, inv, inv, req, req, inv, inv }, -+ /* reset_prof */ { opt, opt, inv, inv, inv, inv, inv, inv, inv, inv, req, inv }, -+ /* query_reserve */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req }, - }; - - struct parameter { -@@ -141,11 +145,12 @@ static struct option options[] = { - { "profile", no_argument, NULL, 'P'}, - { "prof_item", required_argument, NULL, 'I'}, - { "reset_prof", no_argument, NULL, 'R'}, -+ { "query_reserve", no_argument, NULL, 'Q'}, - { NULL, 0, NULL, 0 } - }; - - /* Command line option abbreviations */ --static const char option_string[] = "hvgc:n:SLOPI:R"; -+static const char option_string[] = "hvgc:n:SLOPI:RQ"; - - - /* Error message string */ -@@ -372,6 +377,11 @@ get_command_line (int argc, char* argv[], struct command_line* line) - rc = store_option (&cmdline, cmd_keyword_reset_prof, - optarg); - break; -+ case 'Q': -+ rc = store_option (&cmdline, cmd_keyword_query_reserve, -+ optarg); -+ break; -+ - case -1: - /* End of options string - start of devices list */ - cmdline.device_id = optind; -@@ -431,8 +441,11 @@ do_command (char* device, struct command_line cmdline) - rc = disk_reset_prof (device); - break; - case cmd_keyword_prof_item: -- break; -- default: -+ break; -+ case cmd_keyword_query_reserve: -+ rc = disk_query_reserve_status(device); -+ break; -+ default: - error_print ("Unknown command '%s' specified", - get_keyword_name (i)); - break; -@@ -449,7 +462,7 @@ int - main (int argc, char* argv[]) - { - struct command_line cmdline; -- int rc; -+ int rc, finalrc; - - /* Find out what we're supposed to do */ - rc = get_command_line (argc, argv, &cmdline); -@@ -484,9 +497,12 @@ main (int argc, char* argv[]) - return 1; - } - -- while (cmdline.device_id < argc) { -+ finalrc = 0; -+ while (cmdline.device_id < argc) { - rc = do_command (argv[cmdline.device_id], cmdline); -+ if (rc && !finalrc) -+ finalrc = rc; - cmdline.device_id++; - } -- return 0; -+ return finalrc; - } --- -1.7.3.5 - diff --git a/0058-fdasd-dasdfmt-fix-format-7-label.patch b/0058-fdasd-dasdfmt-fix-format-7-label.patch deleted file mode 100644 index 5b234ec..0000000 --- a/0058-fdasd-dasdfmt-fix-format-7-label.patch +++ /dev/null @@ -1,96 +0,0 @@ -From f877ca62c13e475d55f6fe3fac5c9732ed44b49e Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 28 Jan 2011 14:27:39 +0100 -Subject: [PATCH 58/61] fdasd/dasdfmt: fix format 7 label - -Description: fdasd/dasdfmt: fix format 7 label -Symptom: Backups of Linux on System z disks from z/OS do not work - when the disk is not fully partitioned. -Problem: The format 7 label written by fdasd and dasdfmt is incorrect. - The extend for free space has one track less than required - which is recognized as inconsistent vtoc state by z/OS tools. -Solution: Fix libvtoc to write the format 7 label correctly. ---- - libvtoc/vtoc.c | 22 +++++++++++++--------- - 1 files changed, 13 insertions(+), 9 deletions(-) - -diff --git a/libvtoc/vtoc.c b/libvtoc/vtoc.c -index cebd5a4..36269a4 100644 ---- a/libvtoc/vtoc.c -+++ b/libvtoc/vtoc.c -@@ -1204,7 +1204,7 @@ void vtoc_update_format7_label_add (format7_label_t *f7, int verbose, - if ((ext->a + ext->b) == 0x00000000) - continue; - -- if ((ext->b + 1) == tmp->a) { -+ if ((ext->b) == tmp->a) { - /* this extent precedes the new one */ - ext->b = tmp->b; - bzero(tmp, sizeof(ds7ext_t)); -@@ -1216,7 +1216,7 @@ void vtoc_update_format7_label_add (format7_label_t *f7, int verbose, - continue; - } - -- if (ext->a == (tmp->b + 1)) -+ if (ext->a == (tmp->b)) - { - /* this extent succeeds the new one */ - ext->a = tmp->a; -@@ -1240,7 +1240,7 @@ void vtoc_update_format7_label_del (format7_label_t *f7, int verbose, - { - ds7ext_t *ext; - int i, counter=0; -- -+ - for (i=0; i<16; i++) { - if (i<5) - ext = &f7->DS7EXTNT[i]; -@@ -1258,7 +1258,7 @@ void vtoc_update_format7_label_del (format7_label_t *f7, int verbose, - - if ((a == ext->a) && (b < ext->b)) { - /* left-bounded in free space gap */ -- ext->a = b + 1; -+ ext->a = b; - if (verbose) - printf("FMT7 add extent: left-bounded\n"); - counter++; -@@ -1267,7 +1267,7 @@ void vtoc_update_format7_label_del (format7_label_t *f7, int verbose, - - if ((a > ext->a) && (b == ext->b)) { - /* right-bounded in free space gap */ -- ext->b = a - 1; -+ ext->b = a; - if (verbose) - printf("FMT7 add extent: right-bounded\n"); - counter++; -@@ -1277,8 +1277,8 @@ void vtoc_update_format7_label_del (format7_label_t *f7, int verbose, - if ((a > ext->a) && (b < ext->b)) { - /* partition devides free space into 2 pieces */ - vtoc_update_format7_label_add(f7, verbose, -- b+1, ext->b); -- ext->b = a - 1; -+ b, ext->b); -+ ext->b = a; - if (verbose) - printf("FMT7 add extent: 2 pieces\n"); - counter++; -@@ -1311,10 +1311,14 @@ void vtoc_set_freespace(format4_label_t *f4, format5_label_t *f5, - { - if ((cyl * trk) > BIG_DISK_SIZE) { - if (ch == '+') { -- vtoc_update_format7_label_add(f7, verbose, start,stop); -+ vtoc_update_format7_label_add(f7, verbose, start, -+ /* ds7ext RTA + 1 */ -+ stop + 1); - } - else if (ch == '-') { -- vtoc_update_format7_label_del(f7, verbose, start,stop); -+ vtoc_update_format7_label_del(f7, verbose, start, -+ /* ds7ext RTA + 1 */ -+ stop + 1); - } - else { - printf("BUG: syntax error in vtoc_set_freespace.\n"); --- -1.7.3.5 - diff --git a/0059-cpuplugd-cmm_pages-not-set-and-restored-correctly.patch b/0059-cpuplugd-cmm_pages-not-set-and-restored-correctly.patch deleted file mode 100644 index 0ea4cac..0000000 --- a/0059-cpuplugd-cmm_pages-not-set-and-restored-correctly.patch +++ /dev/null @@ -1,69 +0,0 @@ -From f127d0df43f5fe5709f068e0c79bef0b759cb6fe Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 28 Jan 2011 14:28:35 +0100 -Subject: [PATCH 59/61] cpuplugd: cmm_pages not set and restored correctly - -Description: cpuplugd: cmm_pages not set and restored correctly -Symptom: /proc/sys/vm/cmm_pages will be restored to 0 after program exit, - regardless of previous value, if cpuplugd is run with an invalid - memory configuration, e.g. cmm_min commented out. Also, cmm_pages - will not correctly reach a cmm_min of 0 during run-time, in a case - where cmm_pages is equal to cmm_inc. -Problem: Incorrect checks on program exit, and in the memplug function. -Solution: Fix checks on program exit and memplug. ---- - cpuplugd/config.c | 1 + - cpuplugd/daemon.c | 4 ++-- - cpuplugd/mem.c | 2 +- - 3 files changed, 4 insertions(+), 3 deletions(-) - -diff --git a/cpuplugd/config.c b/cpuplugd/config.c -index e853ea7..bda7780 100644 ---- a/cpuplugd/config.c -+++ b/cpuplugd/config.c -@@ -358,6 +358,7 @@ void check_config(struct config *cfg) - if (foreground == 0) - syslog(LOG_INFO, "No valid memory hotplug " - "configuration detected\n"); -+ memory = 0; - } else { - memory = 1; - /* -diff --git a/cpuplugd/daemon.c b/cpuplugd/daemon.c -index 391acba..4dab372 100644 ---- a/cpuplugd/daemon.c -+++ b/cpuplugd/daemon.c -@@ -125,7 +125,7 @@ void clean_up() - syslog(LOG_INFO, "terminated\n"); - remove(pid_file); - reactivate_cpus(); -- if (check_cmmfiles() == 0) -+ if (memory) - cleanup_cmm(); - exit(1); - } -@@ -139,7 +139,7 @@ void kill_daemon(int a) - syslog(LOG_INFO, "shutting down\n"); - remove(pid_file); - reactivate_cpus(); -- if (check_cmmfiles() == 0) -+ if (memory) - cleanup_cmm(); - exit(0); - } -diff --git a/cpuplugd/mem.c b/cpuplugd/mem.c -index 13f902d..b15c7e2 100644 ---- a/cpuplugd/mem.c -+++ b/cpuplugd/mem.c -@@ -163,7 +163,7 @@ int memplug(int size) - * new value: previous value - size - */ - new_size = old_size - size; -- if (new_size <= 0) -+ if (new_size < 0) - return -1; - filp = fopen("/proc/sys/vm/cmm_pages", "w"); - if (!filp) { --- -1.7.3.5 - diff --git a/0060-lsluns-Fix-LUN-reporting-for-SAN-volume-controller-S.patch b/0060-lsluns-Fix-LUN-reporting-for-SAN-volume-controller-S.patch deleted file mode 100644 index e5803da..0000000 --- a/0060-lsluns-Fix-LUN-reporting-for-SAN-volume-controller-S.patch +++ /dev/null @@ -1,69 +0,0 @@ -From bdc3b89850bb437f48cb2f9fa31a8f51d3cd88b5 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 28 Jan 2011 14:30:22 +0100 -Subject: [PATCH 60/61] lsluns: Fix LUN reporting for SAN volume controller (SVC) - -Description: lsluns: Fix LUN reporting for SAN volume controller (SVC) -Symptom: lsluns fails to report LUNs from SVC -Problem: The SCSI Architecture Model (SAM) specifies that a storage - server only has to support the REPORT LUNs command for LUN 0 - or the "REPORT LUNS Well-Known-LUN" (WLUN). The SAN Volume - Controller (SVC) only supports REPORT LUNS on LUN 0. The - approach of lsluns of sending the REPORT LUNS to any attached - LUN does not work for the SVC. -Solution: Change the strategy of lsluns to check if the LUN 0 or the - WLUN is already available. If both LUNs are not available, - first try to attach LUN 0 and issue the command; if this - fails, try the WLUN. ---- - zconf/lsluns | 14 +++++++++----- - 1 files changed, 9 insertions(+), 5 deletions(-) - -diff --git a/zconf/lsluns b/zconf/lsluns -index 769b846..acbdcd7 100755 ---- a/zconf/lsluns -+++ b/zconf/lsluns -@@ -45,16 +45,16 @@ sub list_luns - next; - } - if (!defined($lun_hash{$a}{$p})) { -- `echo $wlun >> $drv_dir/$a/$p/unit_add 2>/dev/null`; -+ `echo $lun0 >> $drv_dir/$a/$p/unit_add 2>/dev/null`; - select(undef, undef, undef, 0.1); - %lun_hash = get_lun_hash(); - if (!defined($lun_hash{$a}{$p})) { -- `echo $wlun >> $drv_dir/$a/$p/unit_remove 2>/dev/null`; -- `echo $lun0 >> $drv_dir/$a/$p/unit_add 2>/dev/null`; -+ `echo $lun0 >> $drv_dir/$a/$p/unit_remove 2>/dev/null`; -+ `echo $wlun >> $drv_dir/$a/$p/unit_add 2>/dev/null`; - select(undef, undef, undef, 0.1); - %lun_hash = get_lun_hash(); - if (!defined($lun_hash{$a}{$p})) { -- `echo $lun0 >> $drv_dir/$a/$p/unit_remove 2>/dev/null`; -+ `echo $wlun >> $drv_dir/$a/$p/unit_remove 2>/dev/null`; - print"\tat port $p:\n"; - print "\t\tCannot attach WLUN / LUN0 for scanning.\n"; - next; -@@ -92,6 +92,8 @@ sub list_luns - } - } - -+# Look only for LUN0 and the REPORT LUNs WLUN. SAM specifies that the storage -+# only has to response on one of those to the REPORT LUNs command. - sub get_lun_hash - { - my %lun_hash; -@@ -105,7 +107,9 @@ sub get_lun_hash - $p =~ s/(0x\w{16})*\n/$1/; - chomp($a); - -- $lun_hash{$a}{$p}{$l} = ${[split('/', $device)]}[-1]; -+ if ($l eq $lun0 or $l eq $wlun) { -+ $lun_hash{$a}{$p}{$l} = ${[split('/', $device)]}[-1]; -+ } - } - return %lun_hash; - } --- -1.7.3.5 - diff --git a/0061-lsluns-Accept-uppercase-and-lowercase-hex-digits.patch b/0061-lsluns-Accept-uppercase-and-lowercase-hex-digits.patch deleted file mode 100644 index 4a82c8b..0000000 --- a/0061-lsluns-Accept-uppercase-and-lowercase-hex-digits.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 7fd37ae55c104cf62f8d62da79d89a4c59087a6d Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 28 Jan 2011 14:31:06 +0100 -Subject: [PATCH 61/61] lsluns: Accept uppercase and lowercase hex digits - -Description: lsluns: Accept uppercase and lowercase hex digits -Symptom: lsluns does not accept uppercase letters in hex-digits for - FCP device id and WWPN. -Problem: lsluns compares the FCP device id and the WWPN with the - sysfs entries that are always lowercase. -Solution: Convert the input from the command line to lowercase, so - that lsluns accepts both, uppercase and lowercase for the - hex digits in the FCP device and the WWPN. ---- - zconf/lsluns | 7 +++++++ - 1 files changed, 7 insertions(+), 0 deletions(-) - -diff --git a/zconf/lsluns b/zconf/lsluns -index acbdcd7..436ea34 100755 ---- a/zconf/lsluns -+++ b/zconf/lsluns -@@ -252,7 +252,14 @@ GetOptions('c|ccw=s' => \@adapter, - }; - - @adapter = split(',', join(',', @adapter)); -+foreach (@adapter) { -+ $_ =~ tr/A-Z/a-z/; -+} -+ - @port = split(',', join(',', @port)); -+foreach (@port) { -+ $_ =~ tr/A-Z/a-z/; -+} - - %res_hash = get_env_list(\@adapter, \@port); - --- -1.7.3.5 - diff --git a/0062-dumpconf-Add-DELAY_MINUTES-description-to-man-page.patch b/0062-dumpconf-Add-DELAY_MINUTES-description-to-man-page.patch deleted file mode 100644 index a24a939..0000000 --- a/0062-dumpconf-Add-DELAY_MINUTES-description-to-man-page.patch +++ /dev/null @@ -1,225 +0,0 @@ -From 9e35e49ec56880c9f62cce2ff79849e1e409bc2b Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Mon, 14 Feb 2011 11:03:03 +0100 -Subject: [PATCH] dumpconf: Add DELAY_MINUTES description to man page - -Description: dumpconf: Add DELAY_MINUTES description to man page -Symptom: User has no online documentation for the DELAY_MINUTES keyword. -Problem: Description of the DELAY_MINUTES keyword is missing in dumpconf - man page. -Solution: Add description of DELAY_MINUTES keyword to dumpconf man page. - Also add some minor man page cleanups from upstream version. ---- - man/dumpconf.8 | 104 +++++++++++++++++++++++-------------------------------- - 1 files changed, 44 insertions(+), 60 deletions(-) - -diff --git a/man/dumpconf.8 b/man/dumpconf.8 -index b8dcd00..c795568 100644 ---- a/man/dumpconf.8 -+++ b/man/dumpconf.8 -@@ -1,7 +1,7 @@ --.TH DUMPCONF 8 "Nov 2006" "s390-tools" -+.TH DUMPCONF 8 "Nov 2009" "s390-tools" - - .SH NAME --dumpconf \- Configure automatic dump for Linux on z/Series. -+dumpconf \- Configure an ON_PANIC action for Linux on System z. - - .SH SYNOPSIS - .br -@@ -10,8 +10,8 @@ dumpconf \- Configure automatic dump for Linux on z/Series. - \fBdumpconf\fR [-h|-v] - - .SH DESCRIPTION --\fBdumpconf\fR reads /etc/sysconfig/dumpconf and initializes the automatic dump --feature according to the configuration file. -+\fBdumpconf\fR reads the /etc/sysconfig/dumpconf file -+and establishes the action to be taken in case a kernel panic occurs. - - The following keywords can be used in the dumpconf file: - -@@ -20,21 +20,22 @@ The following keywords can be used in the dumpconf file: - Shutdown action in case of a kernel panic. Possible values are 'dump', 'reipl', 'dump_reipl', 'stop' and 'vmcmd': - .br - --dump: trigger dump according to configuration in /etc/sysconfig/dumpconf. -+dump: trigger dump according to the configuration in /etc/sysconfig/dumpconf. - .br - --reipl: trigger re-IPL according to configuration under /sys/firmware/reipl. -+reipl: trigger re-IPL according to the configuration under /sys/firmware/reipl. - .br - --dump_reipl: first trigger dump according to configuration in --/etc/sysconfig/dumpconf, then trigger re-IPL according to configuration under --/sys/firmware/reipl. -+dump_reipl: first trigger dump according to the configuration in -+/etc/sysconfig/dumpconf, then trigger re-IPL according to the configuration -+under /sys/firmware/reipl. - .br - - stop: stop Linux and enter disabled wait (default). - .br - --vmcmd: trigger vm command according to 'VMCMD_X' configuration in /etc/sysconfig/dumpconf. -+vmcmd: trigger CP command according to the 'VMCMD_X' configuration in -+/etc/sysconfig/dumpconf. - - .TP - \fB - DUMP_TYPE:\fR -@@ -46,15 +47,15 @@ Device number of dump device. - - .TP - \fB - WWPN\fR --WWPN for scsi dump device. -+WWPN for SCSI dump device. - - .TP - \fB - LUN\fR --LUN for scsi dump device. -+LUN for SCSI dump device. - - .TP - \fB - BOOTPROG:\fR --Boot program selector -+Boot program selector. - - .TP - \fB - BR_LBA:\fR -@@ -64,46 +65,25 @@ Boot record logical block address. - \fB - VMCMD_1, VMCMD_2 ... VMCMD_5:\fR - Up to five CP commands, which are triggered in case of a kernel panic. - --.SH Reboot loop considerations -- --If you select the shutdown actions "reipl" or "dump_reipl", in rare cases a --"reboot loop" can occur, if the Linux kernel crashes everytime after the --reboot. If you want to prevent that scenario, one of the following two --approaches can be taken: -- --1. Manual activation of dumpconf -- --Ensure that the dumpconf service is not active by default: --.br --# chkconfig --del dumpconf -- --Start dumpconf service manually: --.br --# service dumpconf start -- --When your Linux system crashes, the system will be rebooted (after creating --a dump in case of dump_reipl). Because the dumpconf script will then not be --activated automatically, a second crash will stop the system. -- --2. Automatic delayed activation of dumpconf -- --Ensure that the dumpconf service is not active by default: --.br --# chkconfig --del dumpconf -- --To enable delayed activation one of the following methods can be used: -- a) Use a init script (e.g. /etc/rc.d/boot.local): -- (sleep 10m; /sbin/service dumpconf start) & -- -- b) Use a init script (e.g. /etc/rc.d/boot.local) together with "at": -- echo 'echo /sbin/service dumpconf start' |at now+10min -- -- c) Use the following crontab entry: -- @reboot sleep 10m && /sbin/service dumpconf start -- --In these examples, when your Linux system crashes within 10 minutes after --the reboot, the dumpconf script is not active and a second crash will stop --the system. -+.TP -+\fB - DELAY_MINUTES:\fR -+Number of minutes the activation of dumpconf is to be delayed. If this keyword -+is omitted, the default is zero, which means that -+dumpconf activates immediately during system startup. -+Specify a non-zero delay time only if you specified -+shutdown action "reipl" or "dump_reipl". -+These actions might cause a reboot loop -+if the Linux kernel crashes persistently during (or shortly after) each reboot. -+ -+A non-zero delay time causes dumpconf to sleep in the background until the -+delay time has expired. In this case messages are written to /var/log/messages. -+By default (DELAY_MINUTES is omitted or zero) dumpconf runs in the foreground -+and informational messages are written to sysout, while -+error messages are written to syserr. -+ -+Example: If you specified DELAY_MINUTES=10 and -+your Linux system crashes within 10 minutes after the reboot, -+then dumpconf is not yet active and the default action (stop) is triggered. - - .SH COMMANDS - .TP -@@ -128,12 +108,12 @@ Print usage information, then exit. - Print version information, then exit. - - .SH EXAMPLES: --The following are examples for /etc/sysconfig/dumpconf: -+The following are examples of the /etc/sysconfig/dumpconf file: - .br - - # - .br --# Example config for CCW dump device (DASD) -+# Example configuration for a CCW dump device (DASD) - .br - # - .br -@@ -141,12 +121,14 @@ ON_PANIC=dump_reipl - .br - DUMP_TYPE=ccw - .br --DEVICE=0.0.4714 -+DEVICE=0.0.1234 -+.br -+DELAY_MINUTES=5 - .br - - # - .br --# Example config for FCP dump device (SCSI Disk) -+# Example configuration for an FCP dump device (SCSI Disk) - .br - # - .br -@@ -154,7 +136,7 @@ ON_PANIC=dump - .br - DUMP_TYPE=fcp - .br --DEVICE=0.0.4711 -+DEVICE=0.0.2345 - .br - WWPN=0x5005076303004712 - .br -@@ -167,7 +149,7 @@ BR_LBA=0 - - # - .br --# Example config for vm command on panic -+# Example configuration for CP commands on panic - .br - # - .br -@@ -177,7 +159,7 @@ VMCMD_1="MESSAGE * Starting VMDUMP" - .br - VMCMD_2="VMDUMP" - .br --VMCMD_3="IPL 4711" -+VMCMD_3="IPL 3456" - - # - .br -@@ -186,6 +168,8 @@ VMCMD_3="IPL 4711" - # - .br - ON_PANIC=reipl -+.br -+DELAY_MINUTES=5 - - .SH SEE ALSO - Linux on zSeries: Using the Dump Tools --- -1.7.4 - diff --git a/0063-cmsfs-fuse-fix-read-and-write-errors-in-text-mode.patch b/0063-cmsfs-fuse-fix-read-and-write-errors-in-text-mode.patch deleted file mode 100644 index 05c4c9e..0000000 --- a/0063-cmsfs-fuse-fix-read-and-write-errors-in-text-mode.patch +++ /dev/null @@ -1,164 +0,0 @@ -From c55983415ae3bd360deb04ede20bc482bd2c41b7 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Mon, 7 Mar 2011 15:13:45 +0100 -Subject: [PATCH] cmsfs-fuse: fix read and write errors in text mode - -Description: cmsfs-fuse: fix read and write errors in text mode. -Symptom: Copying a file in text mode fails with read or write errors. -Problem: Miscalculation of file size in text mode and off-by-one error - in record length check for fixed files. -Solution: Correct the calculation of the file size by using the displacement - value for the last block of a variable file and by limiting - the size of the last record of a fixed file to the actual size. - Additionally scan for the correct length of a fixed record in text - mode. -Problem-ID: 70230 ---- - cmsfs-fuse/cmsfs-fuse.c | 67 ++++++++++++++++++++++++++--------------------- - cmsfs-fuse/dasd.c | 2 +- - 2 files changed, 38 insertions(+), 31 deletions(-) - -diff --git a/cmsfs-fuse/cmsfs-fuse.c b/cmsfs-fuse/cmsfs-fuse.c -index 6c5b0b5..fd87774 100644 ---- a/cmsfs-fuse/cmsfs-fuse.c -+++ b/cmsfs-fuse/cmsfs-fuse.c -@@ -917,6 +917,33 @@ static void set_record_extension(struct file *f, int *record, off_t addr, - f->record_scan_state = RSS_DATA_BLOCK_EXT; - } - -+/* check for file end via byte count and return count of bytes left */ -+static size_t crop_file_end(struct file *f, int record, size_t done, -+ int first) -+{ -+ size_t filesize = f->fst->nr_records * f->fst->record_len; -+ struct record *rec = &f->rlist[record]; -+ struct record_ext *rext = rec->ext; -+ -+ /* done already includes the complete record length incl. extensions */ -+ done -= rec->total_len; -+ /* remove possible linefeeds before comparing with on-disk file size */ -+ if (f->linefeed && record) -+ done -= record; -+ done += rec->first_block_len; -+ -+ /* add length of all existing extensions */ -+ while (rext != NULL) { -+ done += rext->len; -+ rext = rext->next; -+ } -+ -+ if (done + first > filesize) -+ first = filesize - done; -+ return first; -+} -+ -+/* check for file end by record number */ - static int end_of_file(struct file *f, int record) - { - if (record == f->fst->nr_records) -@@ -936,6 +963,8 @@ static void walk_fixed_data_block(struct file *f, off_t addr, int *record, - - if (first) { - BUG(first > left); -+ -+ first = crop_file_end(f, *record, *total, first); - set_record_extension(f, record, addr, first, block); - left -= first; - if (addr != NULL_BLOCK) -@@ -1572,28 +1601,6 @@ static ssize_t get_file_size_fixed(struct fst_entry *fst) - return fst->nr_records * fst->record_len; - } - --static ssize_t get_file_size_variable_slow(struct fst_entry *fst) --{ -- struct record *rec; -- ssize_t total = 0; -- int rc = 0; -- struct file *f = create_file_object(fst, &rc); -- -- if (f == NULL) -- return rc; -- -- rec = get_record(f, f->fst->nr_records - 1); -- total = rec->file_start + rec->total_len; -- -- /* -- * Note: need to add header bytes since the record information does -- * not contain them but get_file_size_logical will remove them... -- */ -- total += f->fst->nr_records * VAR_RECORD_HEADER_SIZE; -- destroy_file_object(f); -- return total; --} -- - static ssize_t get_file_size_variable(struct fst_entry *fst) - { - struct var_ptr vptr; -@@ -1608,11 +1615,11 @@ static ssize_t get_file_size_variable(struct fst_entry *fst) - return rc; - if (vptr.next == 0) { - /* -- * Last block is a null block. Cannot scan that block, -- * need to scan the whole file instead... -+ * Last block is a null block. No more records can -+ * follow, so the displacement value points to EOF. - */ -- total = get_file_size_variable_slow(fst); -- goto skip; -+ total = vptr.disp; -+ goto skip_scan; - } - ptr = ABS(vptr.next); - if (vptr.disp != VAR_RECORD_SPANNED) { -@@ -1638,7 +1645,6 @@ skip_scan: - */ - if (fst->nr_blocks) - total += (fst->nr_blocks - 1) * cmsfs.blksize; --skip: - return total; - } - -@@ -3896,7 +3902,8 @@ static int do_write(struct file *f, const char *buf, size_t size, off_t offset) - return rc; - f->ptr_dirty = 0; - } else -- if (f->fst->levels > 0) { -+ if (f->fst->levels > 0 && -+ f->fst->record_format == RECORD_LEN_VARIABLE) { - rc = update_last_block_vptr(f, ABS(f->fst->fop), - f->fst->levels, &vptr); - if (rc < 0) -@@ -3962,7 +3969,7 @@ static int cmsfs_write(const char *path, const char *buf, size_t size, - return do_write(f, buf, size, offset); - - /* remove already comitted bytes */ -- offset -= f->wcache_used; -+ offset -= f->wcache_commited; - - /* write offset must be at the end of the file */ - if (offset + f->null_records + f->pad_bytes != f->session_size) -@@ -3981,7 +3988,7 @@ static int cmsfs_write(const char *path, const char *buf, size_t size, - return -EINVAL; - } else { - if (f->fst->record_format == RECORD_LEN_FIXED && -- f->wcache_commited + scan_len >= f->fst->record_len) { -+ f->wcache_commited + scan_len > f->fst->record_len) { - purge_wcache(f); - return -EINVAL; - } -diff --git a/cmsfs-fuse/dasd.c b/cmsfs-fuse/dasd.c -index d79d34d..1b9af9a 100644 ---- a/cmsfs-fuse/dasd.c -+++ b/cmsfs-fuse/dasd.c -@@ -196,7 +196,7 @@ int get_device_info(struct cmsfs *cmsfs) - */ - fd = open(cmsfs->device, O_RDWR); - if (fd < 0) { -- if (errno == EROFS) { -+ if (errno == EROFS || errno == EACCES) { - cmsfs->readonly = 1; - fd = open(cmsfs->device, O_RDONLY); - if (fd < 0) --- -1.7.4 - diff --git a/0064-switch-to-using-udevadm-settle.patch b/0064-switch-to-using-udevadm-settle.patch deleted file mode 100644 index 0d9a65b..0000000 --- a/0064-switch-to-using-udevadm-settle.patch +++ /dev/null @@ -1,25 +0,0 @@ -From c4a38de57376a6ddf03906afeac142525837aab0 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 18 Mar 2011 16:35:17 +0100 -Subject: [PATCH 64/66] switch to using udevadm settle - ---- - etc/init.d/mon_statd | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/etc/init.d/mon_statd b/etc/init.d/mon_statd -index 60bcf00..b6699c7 100755 ---- a/etc/init.d/mon_statd -+++ b/etc/init.d/mon_statd -@@ -39,7 +39,7 @@ load_kernel_module() - if [ $? -ne 0 ]; then - exit 1 - fi -- udevsettle -+ udevadm settle - if [ $? -ne 0 ]; then - exit 1 - fi --- -1.7.4 - diff --git a/0065-hyptop-Fix-man-page-typo-for-current-weight.patch b/0065-hyptop-Fix-man-page-typo-for-current-weight.patch deleted file mode 100644 index ab9fcde..0000000 --- a/0065-hyptop-Fix-man-page-typo-for-current-weight.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 983f8cd4337de2ca5377ed64121c55d27367beca Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 18 Mar 2011 16:37:15 +0100 -Subject: [PATCH 65/66] hyptop: Fix man page typo for "current weight" - -Description: hyptop: Fix man page typo for "current weight" -Symptom: The hyptop man page says that the 't' character is used for - "current weight" under z/VM. -Problem: The correct character for "current weight" is 'r'. -Solution: Document the correct character 'r' for "current weight". ---- - hyptop/hyptop.8 | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/hyptop/hyptop.8 b/hyptop/hyptop.8 -index 99a729c..325613b 100644 ---- a/hyptop/hyptop.8 -+++ b/hyptop/hyptop.8 -@@ -127,7 +127,7 @@ The following fields are available under z/VM: - 'u' - Used memory - 'a' - Maximum memory - 'n' - Minimum weight -- 't' - Current weight -+ 'r' - Current weight - 'x' - Maximum weight - - In "sys" window: --- -1.7.4 - diff --git a/0066-fdasd-buffer-overflow-when-writing-to-read-only-devi.patch b/0066-fdasd-buffer-overflow-when-writing-to-read-only-devi.patch deleted file mode 100644 index 2bfa24a..0000000 --- a/0066-fdasd-buffer-overflow-when-writing-to-read-only-devi.patch +++ /dev/null @@ -1,61 +0,0 @@ -From f5a80bd5d3d478354d6044b6d2b9951fd29a8d59 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Fri, 18 Mar 2011 16:37:54 +0100 -Subject: [PATCH 66/66] fdasd: buffer overflow when writing to read-only device - -Description: fdasd: buffer overflow when writing to read-only device -Symptom: When fdasd tries to write to a read-only disk, it fails with - a '*** buffer overflow detected ***' error message. -Problem: It is expected that fdasd cannot write to a read-only disk, and - such an operation should end with a proper error message. The - libvtoc code, which writes this message, contains the bug that - causes the buffer overflow. -Solution: Directly print the error message, without formatting it first in - a buffer. ---- - libvtoc/vtoc.c | 15 +++++---------- - 1 files changed, 5 insertions(+), 10 deletions(-) - -diff --git a/libvtoc/vtoc.c b/libvtoc/vtoc.c -index 36269a4..ae1de8c 100644 ---- a/libvtoc/vtoc.c -+++ b/libvtoc/vtoc.c -@@ -146,30 +146,25 @@ static char buffer[85]; - */ - static void vtoc_error(enum failure why, char *s1, char *s2) - { -- char error[LINE_LENGTH]; -- - switch (why) { - case unable_to_open: -- sprintf(error, "%s opening device '%s' failed.\n%s\n", -+ fprintf(stderr, "\n%s opening device '%s' failed.\n%s\n", - VTOC_ERROR, s1, s2); - break; - case unable_to_seek: -- sprintf(error, "%s seeking device '%s' failed.\n%s\n", -+ fprintf(stderr, "\n%s seeking device '%s' failed.\n%s\n", - VTOC_ERROR, s1, s2); - break; - case unable_to_write: -- sprintf(error, "%s writing to device '%s' failed,\n%s\n", -+ fprintf(stderr, "\n%s writing to device '%s' failed,\n%s\n", - VTOC_ERROR, s1, s2); - break; - case unable_to_read: -- sprintf(error, "%s reading from device '%s' failed.\n%s\n", -+ fprintf(stderr, "\n%s reading from device '%s' failed.\n%s\n", - VTOC_ERROR, s1, s2); - break; -- default: sprintf(error, "Fatal error\n"); -+ default: fprintf(stderr, "\nFatal error\n"); - } -- -- fputc('\n', stderr); -- fputs(error, stderr); - exit(1); - } - --- -1.7.4 - diff --git a/0067-cmsfs-fuse-Delete-old-file-if-renaming-to-an-existin.patch b/0067-cmsfs-fuse-Delete-old-file-if-renaming-to-an-existin.patch deleted file mode 100644 index 0dbfc02..0000000 --- a/0067-cmsfs-fuse-Delete-old-file-if-renaming-to-an-existin.patch +++ /dev/null @@ -1,297 +0,0 @@ -From f6494a1210439d591a1319498026ffcdd91a2ebf Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Tue, 29 Mar 2011 10:49:25 +0200 -Subject: [PATCH 67/70] cmsfs-fuse: Delete old file if renaming to an existing file - -Description: cmsfs-fuse: Delete old file if renaming to an existing file. -Symptom: Stale old file if renaming a file to an existing file. -Problem: In case rename is used to overwrite an existing file the old - file entry was not deleted resulting in a duplicated file. -Solution: If the target of a rename operation exists delete the target - file before the rename operation. ---- - cmsfs-fuse/cmsfs-fuse.c | 248 ++++++++++++++++++++++++----------------------- - 1 files changed, 128 insertions(+), 120 deletions(-) - -diff --git a/cmsfs-fuse/cmsfs-fuse.c b/cmsfs-fuse/cmsfs-fuse.c -index fd87774..9f1aa1a 100644 ---- a/cmsfs-fuse/cmsfs-fuse.c -+++ b/cmsfs-fuse/cmsfs-fuse.c -@@ -2638,14 +2638,132 @@ static int cmsfs_utimens(const char *path, const struct timespec ts[2]) - return 0; - } - -+/* -+ * Get the address of the last directory entry. -+ */ -+off_t find_last_fdir_entry(off_t addr, int level) -+{ -+ struct fst_entry fst; -+ int left, rc; -+ off_t ptr; -+ -+ if (level > 0) { -+ level--; -+ left = PTRS_PER_BLOCK; -+ while (left--) { -+ ptr = get_fixed_pointer(addr + left * PTR_SIZE); -+ BUG(ptr < 0); -+ if (ptr) -+ return find_last_fdir_entry(ptr, level); -+ } -+ DIE("Directory entry not found\n"); -+ return 0; -+ } -+ -+ left = cmsfs.blksize / sizeof(struct fst_entry); -+ while (left--) { -+ rc = _read(&fst, sizeof(fst), -+ addr + left * sizeof(struct fst_entry)); -+ BUG(rc < 0); -+ if (is_file((unsigned long long *) fst.name, -+ (unsigned long long *) fst.type)) -+ return addr + left * sizeof(struct fst_entry); -+ } -+ DIE("Directory entry not found\n"); -+} -+ -+static int delete_file(const char *path) -+{ -+ off_t fst_kill, fst_last; -+ struct walk_file walk; -+ struct file *f_moved; -+ char file[MAX_FNAME]; -+ struct fst_entry fst; -+ struct file *f; -+ int rc = 0, i; -+ -+ if (cmsfs.readonly) -+ return -EROFS; -+ -+ fst_kill = lookup_file(path + 1, &fst, SHOW_UNLINKED); -+ if (!fst_kill) -+ return -ENOENT; -+ f = create_file_object(&fst, &rc); -+ if (f == NULL) -+ return rc; -+ -+ /* delete all data blocks */ -+ for (i = 0; i < f->fst->nr_blocks; i++) -+ free_block(f->blist[i].disk_addr); -+ -+ if (f->fst->fop) { -+ rc = f->fops->delete_pointers(f, f->fst->levels, ABS(f->fst->fop)); -+ if (rc < 0) -+ goto error; -+ } -+ -+ if (cmsfs.dir_levels) -+ fst_last = find_last_fdir_entry(get_fop(cmsfs.fdir), cmsfs.dir_levels); -+ else -+ fst_last = find_last_fdir_entry(cmsfs.fdir, cmsfs.dir_levels); -+ -+ /* remove unlinked file from fcache */ -+ strncpy(file, path + 1, MAX_FNAME); -+ str_toupper(file); -+ invalidate_htab_entry(file); -+ -+ if (fst_last == fst_kill) -+ goto skip_copy; -+ -+ /* copy last entry over unlinked entry */ -+ rc = _read(&fst, sizeof(struct fst_entry), fst_last); -+ BUG(rc < 0); -+ rc = _write(&fst, sizeof(struct fst_entry), fst_kill); -+ BUG(rc < 0); -+ -+ /* update moved fcache entry */ -+ memset(file, 0, sizeof(file)); -+ decode_edf_name(file, fst.name, fst.type); -+ update_htab_entry(fst_kill, file); -+ /* update cached address of moved FST */ -+ f_moved = file_open(file); -+ if (f_moved != NULL) -+ f->fst_addr = fst_kill; -+ -+skip_copy: -+ /* delete last entry */ -+ rc = _zero(fst_last, sizeof(struct fst_entry)); -+ BUG(rc < 0); -+ -+ /* if the deleted entry was the first of a block, free the block */ -+ if (fst_last % cmsfs.blksize == 0) { -+ cache_dblocks(&walk); -+ /* delete the last block from dlist */ -+ walk.dlist_used--; -+ free_block(fst_last); -+ purge_dblock_ptrs(cmsfs.dir_levels, get_fop(cmsfs.fdir)); -+ rewrite_dblock_ptrs(&walk); -+ free_dblocks(&walk); -+ } -+ -+ destroy_file_object(f); -+ decrease_file_count(); -+ update_block_count(); -+ return 0; -+ -+error: -+ destroy_file_object(f); -+ return rc; -+} -+ - static int cmsfs_rename(const char *path, const char *new_path) - { -+ struct fst_entry fst, fst_new; -+ off_t fst_addr, fst_addr_new; - char uc_old_name[MAX_FNAME]; - char fname[8], ftype[8]; -- struct fst_entry fst; - char *uc_new_name; - struct file *f; -- off_t fst_addr; - int rc; - - if (cmsfs.readonly) -@@ -2655,6 +2773,14 @@ static int cmsfs_rename(const char *path, const char *new_path) - if (!fst_addr) - return -ENOENT; - -+ /* if new file already exists it must be overwritten so delete it */ -+ fst_addr_new = lookup_file(new_path + 1, &fst_new, HIDE_UNLINKED); -+ if (fst_addr_new) { -+ delete_file(new_path); -+ /* fst_addr may have changed due to copy-up */ -+ fst_addr = lookup_file(path + 1, &fst, HIDE_UNLINKED); -+ } -+ - rc = edf_name_valid(new_path + 1); - if (rc) - return rc; -@@ -4042,124 +4168,6 @@ static int cmsfs_write(const char *path, const char *buf, size_t size, - return rc; - } - --/* -- * Get the address of the last directory entry. -- */ --off_t find_last_fdir_entry(off_t addr, int level) --{ -- struct fst_entry fst; -- int left, rc; -- off_t ptr; -- -- if (level > 0) { -- level--; -- left = PTRS_PER_BLOCK; -- while (left--) { -- ptr = get_fixed_pointer(addr + left * PTR_SIZE); -- BUG(ptr < 0); -- if (ptr) -- return find_last_fdir_entry(ptr, level); -- } -- DIE("Directory entry not found\n"); -- return 0; -- } -- -- left = cmsfs.blksize / sizeof(struct fst_entry); -- while (left--) { -- rc = _read(&fst, sizeof(fst), -- addr + left * sizeof(struct fst_entry)); -- BUG(rc < 0); -- if (is_file((unsigned long long *) fst.name, -- (unsigned long long *) fst.type)) -- return addr + left * sizeof(struct fst_entry); -- } -- DIE("Directory entry not found\n"); --} -- --static int delete_file(const char *path) --{ -- off_t fst_kill, fst_last; -- struct walk_file walk; -- struct file *f_moved; -- char file[MAX_FNAME]; -- struct fst_entry fst; -- struct file *f; -- int rc = 0, i; -- -- if (cmsfs.readonly) -- return -EROFS; -- -- fst_kill = lookup_file(path + 1, &fst, SHOW_UNLINKED); -- if (!fst_kill) -- return -ENOENT; -- f = create_file_object(&fst, &rc); -- if (f == NULL) -- return rc; -- -- /* delete all data blocks */ -- for (i = 0; i < f->fst->nr_blocks; i++) -- free_block(f->blist[i].disk_addr); -- -- if (f->fst->fop) { -- rc = f->fops->delete_pointers(f, f->fst->levels, ABS(f->fst->fop)); -- if (rc < 0) -- goto error; -- } -- -- if (cmsfs.dir_levels) -- fst_last = find_last_fdir_entry(get_fop(cmsfs.fdir), cmsfs.dir_levels); -- else -- fst_last = find_last_fdir_entry(cmsfs.fdir, cmsfs.dir_levels); -- -- /* remove unlinked file from fcache */ -- strncpy(file, path + 1, MAX_FNAME); -- str_toupper(file); -- invalidate_htab_entry(file); -- -- if (fst_last == fst_kill) -- goto skip_copy; -- -- /* copy last entry over unlinked entry */ -- rc = _read(&fst, sizeof(struct fst_entry), fst_last); -- BUG(rc < 0); -- rc = _write(&fst, sizeof(struct fst_entry), fst_kill); -- BUG(rc < 0); -- -- /* update moved fcache entry */ -- memset(file, 0, sizeof(file)); -- decode_edf_name(file, fst.name, fst.type); -- update_htab_entry(fst_kill, file); -- /* update cached address of moved FST */ -- f_moved = file_open(file); -- if (f_moved != NULL) -- f->fst_addr = fst_kill; -- --skip_copy: -- /* delete last entry */ -- rc = _zero(fst_last, sizeof(struct fst_entry)); -- BUG(rc < 0); -- -- /* if the deleted entry was the first of a block, free the block */ -- if (fst_last % cmsfs.blksize == 0) { -- cache_dblocks(&walk); -- /* delete the last block from dlist */ -- walk.dlist_used--; -- free_block(fst_last); -- purge_dblock_ptrs(cmsfs.dir_levels, get_fop(cmsfs.fdir)); -- rewrite_dblock_ptrs(&walk); -- free_dblocks(&walk); -- } -- -- destroy_file_object(f); -- decrease_file_count(); -- update_block_count(); -- return 0; -- --error: -- destroy_file_object(f); -- return rc; --} -- - static int cmsfs_unlink(const char *path) - { - struct fst_entry fst; --- -1.7.4 - diff --git a/0068-cmsfs-fuse-Enlarge-fsname-string.patch b/0068-cmsfs-fuse-Enlarge-fsname-string.patch deleted file mode 100644 index 8802f4e..0000000 --- a/0068-cmsfs-fuse-Enlarge-fsname-string.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 62c2c000645613cb6be82f48fc641c07ca25370c Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Tue, 29 Mar 2011 10:50:08 +0200 -Subject: [PATCH 68/70] cmsfs-fuse: Enlarge fsname string - -Description: cmsfs-fuse: Enlarge fsname string -Symptom: Truncated device part of file system name -Problem: The device part of the file system name was limited to 50 - characters and gets truncated for long device names, for - instance if /dev/disk/by-path/ device names are used. -Solution: Increase the fsname string limit to 200 characters. ---- - cmsfs-fuse/cmsfs-fuse.c | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/cmsfs-fuse/cmsfs-fuse.c b/cmsfs-fuse/cmsfs-fuse.c -index 9f1aa1a..a3a16d0 100644 ---- a/cmsfs-fuse/cmsfs-fuse.c -+++ b/cmsfs-fuse/cmsfs-fuse.c -@@ -46,7 +46,7 @@ struct list text_type_list; - FILE *logfile; - - #define PAGE_SIZE 0xfff --#define FSNAME_MAX_LEN 50 -+#define FSNAME_MAX_LEN 200 - #define MAX_FNAME 18 - - #define CMSFS_OPT(t, p, v) { t, offsetof(struct cmsfs, p), v } --- -1.7.4 - diff --git a/0069-cmsfs-fuse-Unable-to-use-cmsfs-fuse-if-HOME-is-not-s.patch b/0069-cmsfs-fuse-Unable-to-use-cmsfs-fuse-if-HOME-is-not-s.patch deleted file mode 100644 index eabdc02..0000000 --- a/0069-cmsfs-fuse-Unable-to-use-cmsfs-fuse-if-HOME-is-not-s.patch +++ /dev/null @@ -1,62 +0,0 @@ -From 3a7a5e5eaec3aff2e078a91b76f09eb4ae7f8778 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Tue, 29 Mar 2011 10:50:37 +0200 -Subject: [PATCH 69/70] cmsfs-fuse: Unable to use cmsfs-fuse if $HOME is not set - -Description: cmsfs-fuse: Unable to use cmsfs-fuse if $HOME is not set. -Symptom: Segmentation fault while starting cmsfs-fuse. -Problem: Missing malloc if $HOME environment variable is not set. -Solution: Allocate the string buffer before the $HOME query. ---- - cmsfs-fuse/config.c | 22 ++++++++++++---------- - 1 files changed, 12 insertions(+), 10 deletions(-) - -diff --git a/cmsfs-fuse/config.c b/cmsfs-fuse/config.c -index 9f9de45..f89e444 100644 ---- a/cmsfs-fuse/config.c -+++ b/cmsfs-fuse/config.c -@@ -26,21 +26,20 @@ char *conffile; - - int open_conf_file(FILE **fh) - { -- const char *home_env = getenv("HOME"); -- -- if (home_env == NULL) -- goto no_home; -+ const char *home_env; - - conffile = malloc(4096); - if (conffile == NULL) - DIE_PERROR("malloc failed"); -+ -+ home_env = getenv("HOME"); -+ if (home_env == NULL) -+ goto no_home; -+ - sprintf(conffile, "%s/.cmsfs-fuse/filetypes.conf", home_env); - *fh = fopen(conffile, "r"); -- if (*fh == NULL) -- goto no_home; --out: -- DEBUG("using config file: %s\n", conffile); -- return 0; -+ if (*fh != NULL) -+ goto out; - - no_home: - sprintf(conffile, "%s/%s", TOOLS_SYSCONFDIR, -@@ -50,7 +49,10 @@ no_home: - free(conffile); - return -ENOENT; - } -- goto out; -+out: -+ DEBUG("using config file: %s\n", conffile); -+ free(conffile); -+ return 0; - } - - void add_filetype(char *name, struct list *head) --- -1.7.4 - diff --git a/0070-hyptop-Prevent-interactive-mode-on-s390-line-mode-te.patch b/0070-hyptop-Prevent-interactive-mode-on-s390-line-mode-te.patch deleted file mode 100644 index e416b9e..0000000 --- a/0070-hyptop-Prevent-interactive-mode-on-s390-line-mode-te.patch +++ /dev/null @@ -1,81 +0,0 @@ -From 7ec17ba524709c44561ed6016ba2940473bfa48f Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Tue, 29 Mar 2011 10:51:45 +0200 -Subject: [PATCH 70/70] hyptop: Prevent interactive mode on s390 line mode terminals - -Description: hyptop: Prevent interactive mode on s390 line mode terminals -Symptom: Unreadable output, when hyptop is started on line mode terminals. -Problem: Check for line mode terminal is missing. -Solution: To prevent hyptop starting in interactive mode on line mode - terminals, the TERM variable is checked. If TERM is unset or - set to "dumb" interactive mode is not allowed. ---- - hyptop/hyptop.8 | 8 ++++++++ - hyptop/hyptop.c | 21 ++++++++++++++++++++- - 2 files changed, 28 insertions(+), 1 deletions(-) - -diff --git a/hyptop/hyptop.8 b/hyptop/hyptop.8 -index 325613b..19c9d7c 100644 ---- a/hyptop/hyptop.8 -+++ b/hyptop/hyptop.8 -@@ -211,3 +211,11 @@ for CPU time calculation, enter: - .br - - # hyptop -t ifl,cp -+ -+.SH ENVIRONMENT -+.TP -+.B TERM -+The TERM environment variable specifies your terminal type. To run -+\fBhyptop\fP in interactive mode the TERM environment variable has -+to be set. The interactive mode is not available for terminals that -+have TERM=dumb (e.g. line mode terminals). -diff --git a/hyptop/hyptop.c b/hyptop/hyptop.c -index c42e8b0..c558a21 100644 ---- a/hyptop/hyptop.c -+++ b/hyptop/hyptop.c -@@ -199,6 +199,23 @@ static int l_initscr(void) - } - - /* -+ * Check if terminal is able to run hyptop in curses mode -+ */ -+static void l_term_check(void) -+{ -+ char *term_str = getenv("TERM"); -+ -+ if (!term_str) -+ ERR_EXIT("Please set TERM environment variable or " -+ "try \"--batch_mode\"\n"); -+ -+ /* S390 line mode terminals normally have TERM=dumb */ -+ if (strcmp(term_str, "dumb") == 0) -+ ERR_EXIT("Terminal of type \"dumb\" is not supported," -+ " try \"--batch_mode\"\n"); -+} -+ -+/* - * Init curses - */ - static void l_term_init(void) -@@ -206,6 +223,8 @@ static void l_term_init(void) - if (g.o.batch_mode_specified) - return; - -+ l_term_check(); -+ - if (l_initscr() == ERR) - goto fail; - if (noecho() == ERR) -@@ -221,7 +240,7 @@ static void l_term_init(void) - l_sig_handler_init(); - return; - fail: -- ERR_EXIT("Could not initialize curses, try \"--batchmode\"\n"); -+ ERR_EXIT("Could not initialize curses, try \"--batch_mode\"\n"); - } - - /* --- -1.7.4 - diff --git a/0071-cpuplugd-Fix-incorrect-multiplication-in-rules-evalu.patch b/0071-cpuplugd-Fix-incorrect-multiplication-in-rules-evalu.patch deleted file mode 100644 index 61cae2a..0000000 --- a/0071-cpuplugd-Fix-incorrect-multiplication-in-rules-evalu.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 07e1c6ff7f6deb2c35c436c5d5def61e6aac32ed Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Tue, 5 Apr 2011 14:52:24 +0200 -Subject: [PATCH] cpuplugd: Fix incorrect multiplication in rules evaluation - -Description: cpuplugd: Fix incorrect multiplication in rules evaluation -Symptom: Rules don't evaluate correctly when multiplication (*) is used. -Problem: Missing return statement in switch/case block. -Solution: Add return statement. ---- - cpuplugd/terms.c | 1 + - 1 files changed, 1 insertions(+), 0 deletions(-) - -diff --git a/cpuplugd/terms.c b/cpuplugd/terms.c -index 0d08e9e..2371efa 100644 ---- a/cpuplugd/terms.c -+++ b/cpuplugd/terms.c -@@ -339,6 +339,7 @@ static double eval_double(struct term *fn, struct symbols *symbols) - a = eval_double(fn->left, symbols); - b = eval_double(fn->right, symbols); - sum = a*b; -+ return sum; - /*return eval_double(fn->left, symbols) * - eval_double(fn->right, symbols);*/ - case OP_DIV: --- -1.7.4 - diff --git a/cpuplugd.initd b/cpuplugd.initd index 4e6ad9f..16bfa97 100644 --- a/cpuplugd.initd +++ b/cpuplugd.initd @@ -1,6 +1,6 @@ #! /bin/sh # -# chkconfig: 2345 90 10 +# chkconfig: - 90 10 # description: Start the cpu hotplug daemon for Linux on System z # processname: cpuplugd # config: /etc/sysconfig/cpuplugd @@ -10,10 +10,8 @@ # Provides: cpuplugd # Required-Start: $local_fs $remote_fs # Required-Stop: $local_fs $remote_fs -# Should-Start: -# Should-Stop: -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 +# Default-Start: +# Default-Stop: 0 1 2 3 4 5 6 # Short-Description: Start the cpu hotplug daemon for Linux on System z # Description: Starts the cpuplugd. It uses the configuration # file /etc/sysconfig/cpuplugd diff --git a/lib-zfcp-hbaapi-2.1-HBA_FreeLibrary.patch b/lib-zfcp-hbaapi-2.1-HBA_FreeLibrary.patch new file mode 100644 index 0000000..fb7a660 --- /dev/null +++ b/lib-zfcp-hbaapi-2.1-HBA_FreeLibrary.patch @@ -0,0 +1,69 @@ +Description: zfcp-hbaapi: Fix crash on HBA_FreeLibrary call. +Symptom: zfcp_ping segmentation fault without any online adapters. +Problem: Segmentation fault happens on libzfcphbaapi if it build as vendor + library at this time when SNIA HBAAPI performs dlclose of + zfcp-hbaapi after clean-up function HBA_FreeLibrary. + zfcp-hbaapi has missing the event thread clean-up. +Solution: zfcp-hbaapi event thread cleanup has been coded using + pthread_cancel and pthread_join in HBA_FreeLibrary function. +Problem-ID: 72524 +--- + lib-zfcp-hbaapi-2.1/vlib.c | 16 ++++++++++++++++ + lib-zfcp-hbaapi-2.1/vlib.h | 2 ++ + lib-zfcp-hbaapi-2.1/vlib_events.c | 4 +--- + 3 files changed, 19 insertions(+), 3 deletions(-) + +--- a/lib-zfcp-hbaapi-2.1/vlib.c ++++ b/lib-zfcp-hbaapi-2.1/vlib.c +@@ -169,6 +169,7 @@ HBA_STATUS HBA_LoadLibrary(void) + */ + HBA_STATUS HBA_FreeLibrary(void) + { ++ void *res; + + VLIB_MUTEX_LOCK(&vlib_data.mutex); + if (!vlib_data.isLoaded) { +@@ -183,6 +184,21 @@ HBA_STATUS HBA_FreeLibrary(void) + } + vlib_data.unloading = 1; + ++ if (pthread_cancel(vlib_data.id) != 0) { ++ VLIB_MUTEX_UNLOCK(&vlib_data.mutex); ++ return HBA_STATUS_ERROR; ++ } ++ ++ if (pthread_join(vlib_data.id, &res) != 0) { ++ VLIB_MUTEX_UNLOCK(&vlib_data.mutex); ++ return HBA_STATUS_ERROR; ++ } ++ ++ if (res != PTHREAD_CANCELED) { ++ VLIB_MUTEX_UNLOCK(&vlib_data.mutex); ++ return HBA_STATUS_ERROR; ++ } ++ + closeAllAdapters(); + + vlib_data.isLoaded = 0; +--- a/lib-zfcp-hbaapi-2.1/vlib.h ++++ b/lib-zfcp-hbaapi-2.1/vlib.h +@@ -494,6 +494,8 @@ struct vlib_data { + struct block adapters; /**< @brief List of adapters + In fact this is the anchor of + the library's repository. */ ++ pthread_t id; /**< @brief Pthread ID of event ++ handling thread*/ + pthread_mutex_t mutex; /**< @brief Protects this structure */ + }; + +--- a/lib-zfcp-hbaapi-2.1/vlib_events.c ++++ b/lib-zfcp-hbaapi-2.1/vlib_events.c +@@ -241,7 +241,5 @@ void cleanup_event_thread() + + void start_event_thread() + { +- pthread_t id; +- +- pthread_create(&id, NULL, &establish_listener, NULL); ++ pthread_create(&vlib_data.id, NULL, &establish_listener, NULL); + } diff --git a/s390-tools-1.14.0-fedora.patch b/s390-tools-1.14.0-fedora.patch new file mode 100644 index 0000000..0bbe5b3 --- /dev/null +++ b/s390-tools-1.14.0-fedora.patch @@ -0,0 +1,119 @@ +From 804e5362c0205f4fd1c060cae9b6ac5972d097a7 Mon Sep 17 00:00:00 2001 +From: Dan Horak +Date: Sun, 20 Jul 2008 09:24:05 +0200 +Subject: [PATCH 1/4] s390-tools-1.5.3-zipl-zfcpdump-2 + +--- + common.mak | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/common.mak b/common.mak +index 589deb4..8ab0225 100644 +--- a/common.mak ++++ b/common.mak +@@ -62,8 +62,8 @@ GROUP = $(shell id -gn) + export INSTROOT BINDIR LIBDIR MANDIR OWNER GROUP + + # Special defines for zfcpdump +-ZFCPDUMP_DIR = /usr/local/share/zfcpdump +-ZFCPDUMP_IMAGE = zfcpdump.image ++ZFCPDUMP_DIR = /boot ++ZFCPDUMP_IMAGE = zfcpdump + ZFCPDUMP_RD = zfcpdump.rd + export ZFCPDUMP_DIR ZFCPDUMP_IMAGE ZFCPDUMP_RD + +-- +1.7.4.4 + + +From dfde1173c937c1bf7a344bcc21ac51fb9b9ca6c1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Thu, 23 Apr 2009 11:46:01 +0200 +Subject: [PATCH 2/4] s390-tools-1.8.1-fdasd-su + +--- + fdasd/fdasd.c | 10 ++++++---- + 1 files changed, 6 insertions(+), 4 deletions(-) + +diff --git a/fdasd/fdasd.c b/fdasd/fdasd.c +index d8cf584..7a61022 100644 +--- a/fdasd/fdasd.c ++++ b/fdasd/fdasd.c +@@ -2093,10 +2093,12 @@ fdasd_get_geometry (fdasd_anchor_t *anc) + if (anc->verbose) printf("disk type check : ok\n"); + + if (dasd_info.FBA_layout != 0) { +- snprintf(err_str, ERROR_STRING_SIZE, +- "%s is not formatted with z/OS compatible " +- "disk layout!", options.device); +- fdasd_error(anc, wrong_disk_format, err_str); ++ if(!anc->silent) { ++ snprintf(err_str, ERROR_STRING_SIZE, ++ "%s is not formatted with z/OS compatible " ++ "disk layout!", options.device); ++ fdasd_error(anc, wrong_disk_format, err_str); ++ } + } + + if (anc->verbose) printf("disk layout check : ok\n"); +-- +1.7.4.4 + + +From f8a90e35a49848bfdcc4c984567b83fcb5df379a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Fri, 19 Jun 2009 10:01:30 +0200 +Subject: [PATCH 3/4] s390-tools-1.8.1-zipl-kdump-man + +Description: Add kdump kernel installation instruction to zipl man page. +Symptom: User wants to prepare SCSI disk for dump, but has not installed + the kdump kernel rpm. +Problem: The installation of the kdump kernel rpm is prereq for preparing + a SCSI dump disk for dump. +Solution: Document that in the zipl man page. +--- + zipl/man/zipl.8 | 2 ++ + 1 files changed, 2 insertions(+), 0 deletions(-) + +diff --git a/zipl/man/zipl.8 b/zipl/man/zipl.8 +index 4c12d59..26b79cf 100644 +--- a/zipl/man/zipl.8 ++++ b/zipl/man/zipl.8 +@@ -263,6 +263,8 @@ will be incomplete. + 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. + ++.B Note that before using this option the "kernel-kdump" rpm has to be ++.B installed. + .TP + .BR "\-M " " or " "--mvdump=" + Install a multi-volume dump record on each device associated with one of the +-- +1.7.4.4 + + +From 30e79c2abe06873666b7fc159eb2643bad61ef96 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Fri, 18 Mar 2011 16:35:17 +0100 +Subject: [PATCH 4/4] switch to using udevadm settle + +--- + etc/init.d/mon_statd | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +diff --git a/etc/init.d/mon_statd b/etc/init.d/mon_statd +index 60bcf00..b6699c7 100755 +--- a/etc/init.d/mon_statd ++++ b/etc/init.d/mon_statd +@@ -39,7 +39,7 @@ load_kernel_module() + if [ $? -ne 0 ]; then + exit 1 + fi +- udevsettle ++ udevadm settle + if [ $? -ne 0 ]; then + exit 1 + fi +-- +1.7.4.4 + diff --git a/s390utils.spec b/s390utils.spec index 0f01377..f1c8231 100644 --- a/s390utils.spec +++ b/s390utils.spec @@ -7,8 +7,8 @@ Name: s390utils Summary: Utilities and daemons for IBM System/z Group: System Environment/Base -Version: 1.8.2 -Release: 32%{?dist} +Version: 1.14.0 +Release: 1%{?dist} Epoch: 2 License: GPLv2 and GPLv2+ and CPL Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) @@ -38,77 +38,7 @@ Source17: ccw.udev Source18: cpuplugd.initd Source19: mon_statd.initd -Patch1: 0001-s390-tools-1.5.3-zipl-zfcpdump-2.patch -Patch2: 0002-s390-tools-1.8.1-zipl-automenu.patch -Patch3: 0003-s390-tools-1.8.1-fdasd-su.patch -Patch4: 0004-s390-tools-1.8.1-fdasd-raid-lvm.patch -Patch5: 0005-don-t-create-automenu-when-default-menu-exists.patch -Patch6: 0006-s390-tools-1.8.1-zipl-kdump-man.patch -Patch7: 0007-s390-tools-1.8.1-lszfcp-perf.patch -Patch8: 0008-fix-string-overflow-in-vtoc_volume_label_init.patch -Patch9: 0009-change-default-load-address-for-ramdisk.patch -Patch10: 0010-improve-mon_statd-init-script.patch -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 -Patch18: 0018-fix-uppercase-conversion-in-lscss.patch -Patch19: 0019-ziorep-fix-return-codes.patch -Patch20: 0020-lstape-fix-return-code.patch -Patch21: 0021-cpuplugd-fix-reading-the-size-of-proc-sys-vm-cmm_pag.patch -Patch22: 0022-lsqeth-support-new-attributes.patch -Patch23: 0023-znetconf-use-hex-index-for-chpidtype-table.patch -Patch24: 0024-zipl-handle-SSCH-status.patch -Patch25: 0025-vmconvert-shows-garbage-in-progress-bar.patch -Patch26: 0026-zipl-zfcp-dump-partition-error.patch -Patch27: 0027-zfcpdump-disable-memory-cgroups.patch -Patch28: 0028-fix-df-usage-in-ziomon.patch -Patch29: 0029-ziomon-remove-check-for-ziorep_config-availability.patch -Patch30: 0030-ziomon-fix-multipathing.patch -Patch31: 0031-mismatch-between-man-and-h-in-chshut.patch -Patch32: 0032-lsdasd-update-man-page.patch -Patch33: 0033-reinitialize-array-in-lsqeth.patch -Patch34: 0034-check-the-length-of-the-parameters-line.patch -Patch35: 0035-ziorep-follow-symlink.patch -Patch36: 0036-ts-shell-do-not-restrict-group-names-to-be-alphanume.patch -Patch37: 0037-znetconf-unknown-driver-for-qeth.patch -Patch38: 0038-cpuplugd-fix-stack-overflow.patch -Patch39: 0039-cpuplugd-fix-cmm-limits-checks.patch -Patch40: 0040-cpuplugd-set-cpu_min-to-1-by-default.patch -Patch41: 0041-fix-dates-option-on-zfcpdbf.patch -Patch42: 0042-lsluns-uninitialized-value-on-adapter-offline.patch -Patch43: 0043-zfcpdbf-Fix-Use-of-uninitialized-value-and-output-is.patch -Patch44: 0044-xcec-bridge-fix-multicast-forwarding.patch -Patch45: 0045-ziomon-wrong-return-codes.patch -Patch46: 0046-qethconf-process-devices-with-non-zero-subchannel.patch -Patch47: 0047-wait-for-completion-of-any-pending-actions-affecting.patch -Patch48: 0048-add-infrastructure-code-for-new-features.patch -Patch49: 0049-hyptop-Show-hypervisor-performance-data-on-System-z.patch -Patch50: 0050-cmsfs-fuse-support-for-CMS-EDF-filesystems-via-fuse.patch -Patch51: 0051-lsmem-chmem-Tools-to-manage-memory-hotplug.patch -Patch52: 0052-dumpconf-Prevent-re-IPL-loop-for-dump-on-panic.patch -Patch53: 0053-ttyrun-run-a-program-if-a-terminal-device-is-availab.patch -Patch54: 0054-zgetdump-zipl-Add-ELF-dump-support-needed-for-makedu.patch -Patch55: 0055-znetconf-support-for-OSA-CHPID-types-OSX-and-OSM.patch -Patch56: 0056-iucvtty-do-not-specify-z-VM-user-ID-as-argument-to-l.patch -Patch57: 0057-tunedasd-add-new-option-Q-query_reserve.patch -Patch58: 0058-fdasd-dasdfmt-fix-format-7-label.patch -Patch59: 0059-cpuplugd-cmm_pages-not-set-and-restored-correctly.patch -Patch60: 0060-lsluns-Fix-LUN-reporting-for-SAN-volume-controller-S.patch -Patch61: 0061-lsluns-Accept-uppercase-and-lowercase-hex-digits.patch -Patch62: 0062-dumpconf-Add-DELAY_MINUTES-description-to-man-page.patch -Patch63: 0063-cmsfs-fuse-fix-read-and-write-errors-in-text-mode.patch -Patch64: 0064-switch-to-using-udevadm-settle.patch -Patch65: 0065-hyptop-Fix-man-page-typo-for-current-weight.patch -Patch66: 0066-fdasd-buffer-overflow-when-writing-to-read-only-devi.patch -Patch67: 0067-cmsfs-fuse-Delete-old-file-if-renaming-to-an-existin.patch -Patch68: 0068-cmsfs-fuse-Enlarge-fsname-string.patch -Patch69: 0069-cmsfs-fuse-Unable-to-use-cmsfs-fuse-if-HOME-is-not-s.patch -Patch70: 0070-hyptop-Prevent-interactive-mode-on-s390-line-mode-te.patch -Patch71: 0071-cpuplugd-Fix-incorrect-multiplication-in-rules-evalu.patch +Patch1: s390-tools-1.14.0-fedora.patch Patch1000: 1000-ziomon-linker.patch @@ -121,6 +51,7 @@ Patch200: src_vipa-2.0.4-locations.patch Patch301: lib-zfcp-hbaapi-2.1-module.patch Patch302: lib-zfcp-hbaapi-2.1-u8.patch Patch303: lib-zfcp-hbaapi-2.1-vendorlib.patch +Patch304: lib-zfcp-hbaapi-2.1-HBA_FreeLibrary.patch Requires: s390utils-base = %{epoch}:%{version}-%{release} Requires: s390utils-osasnmpd = %{epoch}:%{version}-%{release} @@ -142,221 +73,12 @@ be used together with the zSeries (s390) Linux kernel and device drivers. %prep %setup -q -n s390-tools-%{version} -a 4 -a 6 -a 9 -# Use rpm PATH variables for installation and set correct zfcpdump path -%patch1 -p1 -b .common-mak +# Fedora/RHEL changes +%patch1 -p1 -b .fedora -# Patch to maintain backwards compatibility with older zipl multiboot feature -%patch2 -p1 -b .zipl-automenu - -# Fix to honor the silent flag for wrongly formated disks -%patch3 -p1 -b .fdasd-su - -# Enhancement to add raid partiton support to dasds -%patch4 -p1 -b .fdasd-raid-lvm - -# Don't build automenu iff default menu exists (#486444) -%patch5 -p1 -b .defaultmenu - -# Update zipl man page -%patch6 -p1 -b .zipl-kdump-man - -# Check only ZFCP devices in lszfcp (#518669) -%patch7 -p1 -b .lszfcp-perf - -# Fix string overflow in vtoc_volume_label_init (#525318) -%patch8 -p1 -b .vtoc-label - -# Change default load address for ramdisk (#526339) -%patch9 -p1 -b .ramdisk-address - -# Improve mon_statd init script -%patch10 -p1 -b .improve-mon_statd - -# Update readahead value for better performance -%patch11 -p1 -b .readahead - -# Fix multipath device detection in ziomon (#533955) -%patch12 -p1 -b .ziomon-multipath - -# Handle status during ipl in zipl (#537142) -%patch13 -p1 -b .zipl-status - -# 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 - -# Fixed uppercase conversion in lscss (#554768) -%patch18 -p1 -b .uppercase - -# Fixed return codes in ziorep (#556849) -%patch19 -p1 -b .ziorep-returncodes - -# Fixed return code in lstape (#556910) -%patch20 -p1 -b .lstape-returncode - -# Fixed reading the size of /proc/sys/vm/cmm_pages in cpuplugd (#556911) -%patch21 -p1 -b .cpuplugd-fscanf - -# Support new attributes in lsqeth (#556915) -%patch22 -p1 -b .lsqeth-new-attrs - -# Use hex index for chpidtype table in znetconf (#561056) -%patch23 -p1 -b .znetconf-hex-chpidtype - -# Handle status during IPL SSCH (#559250) -%patch24 -p1 -b .zipl-handle-ssch-status - -# Don't show garbage in vmconvert's progress bar (#567681) -%patch25 -p1 -b .vmconvert-progress-bar - -# Fix zfcp dump partition error (#572313) -%patch26 -p1 -b .zfcp-dump-partition - -# Don't use memory cgroups in zfcpdump kernel (#575183) -%patch27 -p1 -b .zfcpdump-cgroups - -# Fix df usage in ziomon (#575833) -%patch28 -p1 -b .ziomon-df - -# Remove check for ziorep_config availability (#576579) -%patch29 -p1 -b .ziorep_config - -# Fix multipathing in ziomon (#577318) -%patch30 -p1 -b .ziomon-multipath-2 - -# Fixed mismatch between man and -h in chshut (#563625) -%patch31 -p1 -b .man-mismatch - -# Update lsdasd man page (#587044) -%patch32 -p1 -b .lsdasd-man - -# Reinitialize array in lsqeth (#587599) -%patch33 -p1 -b .lsqeth-reinit-array - -# Check the length of the parameters line (#594031) -%patch34 -p1 -b .zipl-max-parmline - -# Follow symlinks in ziorep (#598574) -%patch35 -p1 -b .ziorep-follow-symlinks - -# Do not restrict group names to be alphanumeric in ts-shell (#598641) -%patch36 -p1 -b .ts-shell-groups - -# znetconf --drive|-d option returning 'unknown driver' for qeth (#601846) -%patch37 -p1 -b .znetconf-driver-option - -# Fix stack overwrite in cpuplugd (#601847) -%patch38 -p1 -b .cpuplugd-stack-overwrite - -# Fix cmm_min/max limit checks in cpuplugd (#606366) -%patch39 -p1 -b .cpuplugd-cmm-limits - -# Set cpu_min to 1 by default in cpuplugd (#606416) -%patch40 -p1 -b .cpuplugd-cpu_min - -# Fix --dates option in zfcpdbf (#609092) -%patch41 -p1 -b .zfcpdbf-dates - -# lsluns: uninitialized value on adapter offline (#611795) -%patch42 -p1 -b .lsluns-adapter-offline - -# zfcpdbf: Fix 'Use of uninitialized value' and output issues (#612622) -%patch43 -p1 -b .zfcpdbf-uninitialized-value - -# xcec-bridge: fix multicast forwarding (#619504) -%patch44 -p1 -b .xcec-bridge-multicast - -# ziomon: wrong return codes (#623250) -%patch45 -p1 -b .ziomon-return-codes - -# qethconf: process devices with non-zero subchannel (#627692) -%patch46 -p1 -b .qetgconf-nonzero-subchannel - -# wait for completion of any pending actions affecting device (#631527) -%patch47 -p1 -b .cio_settle - -# add infrastructure code for new features (#631541) -%patch48 -p1 -b .feature-infrastructure - -# hyptop: Show hypervisor performance data on System z (#631541) -%patch49 -p1 -b .hyptop - -# cmsfs-fuse: support for CMS EDF filesystems via fuse (#631546) -%patch50 -p1 -b .cmsfs-fuse - -# lsmem/chmem: Tools to manage memory hotplug (#631561) -%patch51 -p1 -b .lsmem-chmem - -# dumpconf: Prevent re-IPL loop for dump on panic (#633411) -%patch52 -p1 -b .dumpconf-reipl - -# ttyrun: run a program if a terminal device is available (#633420) -%patch53 -p1 -b .ttyrun - -# zgetdump/zipl: Add ELF dump support (needed for makedumpfile) (#633437) -%patch54 -p1 -b .elf-dump - -# znetconf: support for OSA CHPID types OSX and OSM (#633534) -%patch55 -p1 -b .znetconf-osx-osm - -# iucvtty: do not specify z/VM user ID as argument to login -h (#636204) -%patch56 -p1 -b .iucvtty-login - -# tunedasd: add new option -Q / --query_reserve (#644935) -%patch57 -p1 -b .tunedasd-q - -# fdasd/dasdfmt: fix format 7 label (#649787) -%patch58 -p1 -b .vtoc-format-7 - -# cpuplugd: cmm_pages not set and restored correctly (#658517) -%patch59 -p1 -b .cpuplugd-cmm_pages - -# lsluns: Fix LUN reporting for SAN volume controller (SVC) (#659828) -%patch60 -p1 -b .lsluns-svc - -# lsluns: Accept uppercase and lowercase hex digits (#660361) -%patch61 -p1 -b .lsluns-ignore-case - -# dumpconf: Add DELAY_MINUTES description to man page (#676706) -%patch62 -p1 -b .dumpconf-update-man - -# cmsfs-fuse: fix read and write errors in text mode (#680465) -%patch63 -p1 -b .cmsfs-fuse-text-mode-errors - -# mon_statd: switch to using udevadm settle (#688140) -%patch64 -p1 -b .mon_statd-udevadm-settle - -# hyptop: Fix man page typo for "current weight" (#684244) -%patch65 -p1 -b .hyptop-man-page-typo - -# fdasd: buffer overflow when writing to read-only device (#688340) -%patch66 -p1 -b .fdasd-buffer-overflow - -# cmsfs-fuse: Delete old file if renaming to an existing file. -%patch67 -p1 -b .cmsfs-fuse-rename-existing - -# cmsfs-fuse: Enlarge fsname string -%patch68 -p1 -b .cmsfs-fuse-fsname-length - -# cmsfs-fuse: Unable to use cmsfs-fuse if $HOME is not set -%patch69 -p1 -b .cmsfs-fuse-config-nohome - -# hyptop: Prevent interactive mode on s390 line mode terminals -%patch70 -p1 -b .hytop-line-mode - -# cpuplugd: Fix incorrect multiplication in rules evaluation (#693365) -%patch71 -p1 -b .cpuplugd-multiplication # Fix linking with --no-add-needed -%patch1000 -p1 -b .linker +#%patch1000 -p1 -b .linker # # cmsfs @@ -711,7 +433,6 @@ fi /sbin/lschp /sbin/lscss /sbin/lsdasd -/sbin/lsluns /sbin/lsqeth /sbin/lstape /sbin/lszcrypt @@ -728,6 +449,7 @@ fi /sbin/zgetdump /sbin/znetconf /sbin/dbginfo.sh +%{_sbindir}/lsluns %{_sbindir}/lsmem %{_sbindir}/lsreipl %{_sbindir}/lsshut @@ -948,6 +670,8 @@ License: GPLv2 Summary: z/VM IUCV terminal applications Group: Applications/System Requires(pre): shadow-utils +Requires(post): grep +Requires(postun): grep BuildRequires: gettext %description iucvterm @@ -1014,8 +738,20 @@ BuildRequires: sg3_utils-devel BuildRequires: kernel-devel BuildRequires: libhbaapi-devel Requires: libhbaapi +Requires(post): grep +Requires(postun): grep sed Obsoletes: %{name}-libzfcphbaapi-devel < 2:1.8.2-4 +%post libzfcphbaapi +grep -q -e "^libzfcphbaapi" /etc/hba.conf || + echo "libzfcphbaapi %{_libdir}/libzfcphbaapi-%{hbaapiver}.so" >> /etc/hba.conf +: + +%preun libzfcphbaapi +grep -q -e "^libzfcphbaapi" /etc/hba.conf && + sed -i.orig -e "/^libzfcphbaapi/d" /etc/hba.conf +fi +: %description libzfcphbaapi ZFCP HBA API Library is an implementation of FC-HBA (see www.t11.org ) for @@ -1122,6 +858,9 @@ User-space development files for the s390/s390x architecture. %changelog +* Tue Aug 16 2011 Dan Horák 2:1.14.0-1 +- rebased to 1.14.0 + * Wed Apr 27 2011 Dan Horák 2:1.8.2-32 - updated ccw udev rules - converted cio_free_device from an upstart job to systemd unit (jstodola) diff --git a/sources b/sources index 280ea2d..c7c900f 100644 --- a/sources +++ b/sources @@ -1,4 +1 @@ -856ecdd42ad358433eb3fcc886b58a89 s390-tools-1.8.2.tar.bz2 -71a8ee5918f2c44c385fcfe8350cdc98 cmsfs-1.1.8c.tar.gz -ecf3ff0ac4469db7297ebd6f7607fb48 lib-zfcp-hbaapi-2.1.tar.gz -ba42772e5b305b5e147344442cd70826 src_vipa-2.0.4.tar.gz +053bb34cd7dcf26a4fef6d1635437f9a s390-tools-1.14.0.tar.bz2