Files
s390-tools/zipl/src/job.c
Mikhail Zaslonko c61783546b zipl: Add --no-compress option to zipl command
Add --no-compress option to explicitly omit compression for single-volume
DASD dumper. Used primarily for test purposes.

Since only the lowest byte of mvdump_force field (struct
stage2dump_parm_tail) has been used, split it in two byte fields and use
one for the new no_compress attribute.

Update zipl help and zipl man page with the new parameter info.

Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2023-09-27 18:29:06 +02:00

2045 lines
49 KiB
C

/*
* zipl - zSeries Initial Program Loader tool
*
* Functions and data structures representing the actual 'job' that the
* user wants us to execute
*
* Copyright IBM Corp. 2001, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <assert.h>
#include "lib/util_arch.h"
#include "error.h"
#include "job.h"
#include "misc.h"
#include "scan.h"
#include "zipl.h"
#include "envblk.h"
#define DEFAULT_DUMP_PARMLINE "root=/dev/ram0 possible_cpus=1 cgroup_disable=memory"
const char *blsdir;
/* Command line options */
static struct option options[] = {
{ "config", required_argument, NULL, 'c'},
{ "blsdir", required_argument, NULL, 'b'},
{ "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},
{ "environment", required_argument, NULL, 0xaf},
{ "image", required_argument, NULL, 'i'},
{ "ramdisk", required_argument, NULL, 'r'},
{ "parmfile", required_argument, NULL, 'p'},
{ "parameters", required_argument, NULL, 'P'},
{ "dumpto", required_argument, NULL, 'd'},
{ "no-compress", no_argument, NULL, 0xb1},
{ "dumptofs", required_argument, NULL, 'D'},
{ "mvdump", required_argument, NULL, 'M'},
{ "segment", required_argument, NULL, 's'},
{ "menu", required_argument, NULL, 'm'},
{ "help", no_argument, NULL, 'h'},
{ "noninteractive", no_argument, NULL, 'n'},
{ "version", no_argument, NULL, 'v'},
{ "verbose", no_argument, NULL, 'V'},
{ "add-files", no_argument, NULL, 'a'},
{ "tape", required_argument, NULL, 'T'},
{ "dry-run", no_argument, NULL, '0'},
{ "force", no_argument, NULL, 'f'},
{ "kdump", required_argument, NULL, 'k'},
{ "ldipl-dump", no_argument, NULL, 0xb0},
{ "secure", required_argument, NULL, 'S'},
{ NULL, 0, NULL, 0 }
};
/* Command line option abbreviations */
static const char option_string[] = "-c:b:t:i:r:p:P:d:D:M:s:S:m:hHnVvaT:fk:";
/* Locations of zipl.conf configuration file */
static const char *zipl_conf[] = {
ZIPL_RUNTIME_CONF,
ZIPL_DEFAULT_CONF,
ZIPL_MINIMAL_CONF,
NULL
};
struct command_line {
char* data[SCAN_KEYWORD_NUM];
char* config;
char *envblk_import_hint;
char *blsdir;
char* menu;
char* section;
int help;
int noninteractive;
int version;
int verbose;
int add_files;
int dry_run;
int force;
int no_compress;
int is_secure;
int is_ldipl_dump;
enum scan_section_type type;
};
static int
store_option(struct command_line* cmdline, enum scan_keyword_id keyword,
char* value)
{
if (cmdline->data[(int) keyword] != NULL) {
error_reason("Option '%s' specified more than once",
scan_keyword_name(keyword));
return -1;
}
cmdline->data[(int) keyword] = value;
return 0;
}
static int
set_secure_ipl(char *keyword, int *is_secure)
{
if (strcmp(keyword, "auto") == 0) {
*is_secure = SECURE_BOOT_AUTO;
} else if (strcmp(keyword, "0") == 0) {
*is_secure = SECURE_BOOT_DISABLED;
} else if (strcmp(keyword, "1") == 0) {
*is_secure = SECURE_BOOT_ENABLED;
} else {
error_reason("Invalid secure boot setting '%s'",
keyword);
return -1;
}
return 0;
}
static int
get_command_line(int argc, char* argv[], struct command_line* line)
{
struct command_line cmdline;
int is_keyword;
int opt;
int rc;
int i;
memset((void *) &cmdline, 0, sizeof(struct command_line));
cmdline.type = section_invalid;
is_keyword = 0;
cmdline.is_secure = SECURE_BOOT_UNDEFINED;
/* Process options */
do {
opt = getopt_long(argc, argv, option_string, options, NULL);
rc = 0;
switch (opt) {
case 'd':
is_keyword = 1;
rc = store_option(&cmdline, scan_keyword_dumpto,
optarg);
break;
case 'D':
error_reason("dumptofs has been deprecated, use "
"--dumpto instead");
rc = -1;
break;
case 'M':
is_keyword = 1;
rc = store_option(&cmdline, scan_keyword_mvdump,
optarg);
#ifndef __s390x__
error_reason("Option '%s' not supported on 31-bit",
scan_keyword_name(scan_keyword_mvdump));
rc = -1;
#endif
break;
case 'i':
is_keyword = 1;
rc = store_option(&cmdline, scan_keyword_image,
optarg);
break;
case 'P':
rc = store_option(&cmdline, scan_keyword_parameters,
optarg);
break;
case 'p':
is_keyword = 1;
rc = store_option(&cmdline, scan_keyword_parmfile,
optarg);
break;
case 'r':
is_keyword = 1;
rc = store_option(&cmdline, scan_keyword_ramdisk,
optarg);
break;
case 's':
is_keyword = 1;
rc = store_option(&cmdline, scan_keyword_segment,
optarg);
break;
case 't':
is_keyword = 1;
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 0xaf:
if (cmdline.envblk_import_hint != NULL) {
error_reason("Option 'environment' specified more than once");
rc = -1;
} else
cmdline.envblk_import_hint = optarg;
break;
case 'T':
is_keyword = 1;
rc = store_option(&cmdline, scan_keyword_tape,
optarg);
break;
case 'k':
is_keyword = 1;
rc = store_option(&cmdline, scan_keyword_kdump,
optarg);
break;
case 'c':
if (cmdline.config != NULL) {
error_reason("Option 'config' specified more "
"than once");
rc = -1;
} else
cmdline.config = optarg;
break;
case 'b':
if (cmdline.blsdir != NULL) {
error_reason("Option 'blsdir' specified more "
"than once");
rc = -1;
} else
cmdline.blsdir = optarg;
break;
case 'm':
if (cmdline.menu != NULL) {
error_reason("Option 'menu' specified more "
"than once");
rc = -1;
} else
cmdline.menu = optarg;
break;
case 'S':
rc = set_secure_ipl(optarg, &cmdline.is_secure);
break;
case 'h':
cmdline.help = 1;
break;
case 'n':
cmdline.noninteractive = 1;
break;
case 'v':
cmdline.version = 1;
break;
case 'V':
cmdline.verbose = 1;
break;
case 'a':
cmdline.add_files = 1;
break;
case '0':
cmdline.dry_run = 1;
break;
case 'f':
cmdline.force = 1;
break;
case 0xb0:
cmdline.is_ldipl_dump = 1;
break;
case 0xb1:
cmdline.no_compress = 1;
break;
case 1:
/* Non-option is interpreted as section name */
if (cmdline.section != NULL) {
error_reason("More than one section "
"specified on command line");
rc = -1;
} else
cmdline.section = optarg;
break;
case -1:
/* End of options string */
break;
default:
fprintf(stderr, "Try 'zipl --help' for "
"more information.\n");
exit(1);
}
if (rc)
return rc;
} while (opt != -1);
/* Check command line options */
if (cmdline.help || cmdline.version) {
/* Always accept --help and --version */
} else if ((cmdline.menu != NULL) || (cmdline.section != NULL)) {
/* Config file mode */
if ((cmdline.menu != NULL) && (cmdline.section != NULL)) {
error_reason("Option 'menu' cannot be used when "
"specifying a configuration section");
return -1;
}
/* Make sure no other keyword option was specified */
for (i=0; i < SCAN_KEYWORD_NUM; i++) {
/* Allow '--parameters' when specifying a section */
if ((i == (int) scan_keyword_parameters) &&
(cmdline.menu == NULL))
continue;
if (cmdline.data[i] != NULL) {
if (cmdline.menu != NULL) {
error_reason("Only one of options "
"'menu' and '%s' allowed",
scan_keyword_name(
(enum scan_keyword_id)
i));
} else {
error_reason("Option '%s' cannot be "
"used when specifying "
"a configuration section",
scan_keyword_name(
(enum scan_keyword_id)
i));
}
return -1;
}
}
} else if (is_keyword) {
/* Command line mode */
rc = scan_check_section_data(cmdline.data, NULL, NULL, 0,
&cmdline.type);
if (rc) {
if (cmdline.type == section_invalid) {
error_reason("Need one of options 'image', "
"'segment','dumpto', 'dumptofs', "
"'mvdump' or 'menu'");
}
return rc;
}
rc = scan_check_target_data(cmdline.data, NULL);
if (rc)
return rc;
}
*line = cmdline;
return 0;
}
static void
free_target_data(struct job_target_data* data)
{
free(data->targetbase);
}
static void
free_envblk_data(struct job_envblk_data *data)
{
free(data->buf);
}
static void
free_common_ipl_data(struct job_common_ipl_data *common)
{
free(common->image);
free(common->parmline);
free(common->ramdisk);
}
static void
free_ipl_data(struct job_ipl_data* data)
{
free_common_ipl_data(&data->common);
}
static void
free_ipl_tape_data(struct job_ipl_tape_data* data)
{
free(data->device);
free_common_ipl_data(&data->common);
}
static void
free_segment_data(struct job_segment_data* data)
{
free(data->segment);
}
static void
free_dump_data(struct job_dump_data* data)
{
free(data->device);
free_common_ipl_data(&data->common);
}
static void
free_menu_data(struct job_menu_data* data)
{
int i;
if (data->entry != NULL) {
for (i=0; i < data->num; i++) {
free(data->entry[i].name);
switch (data->entry[i].id) {
case job_ipl:
free_ipl_data(&data->entry[i].data.ipl);
break;
case job_dump_partition:
free_dump_data(
&data->entry[i].data.dump);
break;
default:
break;
}
}
free(data->entry);
}
}
static void
free_mvdump_data(struct job_mvdump_data* data)
{
int i;
free(data->device_list);
for (i = 0; i < data->device_count; i++)
free(data->device[i]);
}
void
job_free(struct job_data* job)
{
free(job->target.bootmap_dir);
free_target_data(&job->target);
free_envblk_data(&job->envblk);
free(job->name);
switch (job->id) {
case job_ipl:
free_ipl_data(&job->data.ipl);
break;
case job_menu:
free_menu_data(&job->data.menu);
break;
case job_segment:
free_segment_data(&job->data.segment);
break;
case job_dump_partition:
free_dump_data(&job->data.dump);
break;
case job_mvdump:
free_mvdump_data(&job->data.mvdump);
break;
case job_ipl_tape:
free_ipl_tape_data(&job->data.ipl_tape);
break;
default:
break;
}
free(job);
}
struct component_loc {
char *name;
address_t *addrp;
size_t size;
off_t align;
};
static int
set_cl_element(struct component_loc *cl, char *name, const char *filename,
address_t *addrp, size_t size, off_t off, off_t align)
{
struct stat stats;
cl->name = name;
cl->addrp = addrp;
cl->align = align;
if (size != 0) {
/* The loader works on blocks, so alignment is required */
cl->size = ALIGN(size, MAXIMUM_PHYSICAL_BLOCKSIZE);
return 0;
}
assert(filename != NULL);
/* Get size */
if (stat(filename, &stats)) {
error_reason(strerror(errno));
error_text("Could not get information for file '%s'", filename);
return -1;
}
cl->size = ALIGN(stats.st_size - off, MAXIMUM_PHYSICAL_BLOCKSIZE);
return 0;
}
static void
sort_cl_array(struct component_loc* cl, int elements)
{
int i, j, min;
struct component_loc swap;
/* Selection sort keeping sequence */
for (i = 0; i < elements - 1; i++) {
min = i;
for (j = i + 1; j < elements; j++) {
if (*(cl[j].addrp) == UNSPECIFIED_ADDRESS)
continue;
if ((*(cl[min].addrp) == UNSPECIFIED_ADDRESS) ||
(*(cl[j].addrp) < *(cl[min].addrp)))
min = j;
}
if (i != min) {
swap = cl[i];
cl[i] = cl[min];
for (j = min; j > i + 1; j--)
cl[j] = cl[j - 1];
cl[i + 1] = swap;
}
}
}
static int
get_common_components(struct job_common_ipl_data *common,
struct component_loc **clp, int *nump,
int extra)
{
struct component_loc *cl;
int num;
int rc;
/*
* Get memory for image, parmline, ramdisk, loader and
* possible extra components
*/
cl = misc_calloc(3 + extra, sizeof(struct component_loc));
if (cl == NULL)
return -1;
/* Fill in component data */
num = 0;
rc = set_cl_element(&cl[num++], "kernel image", common->image,
&common->image_addr, 0, 0,
MAXIMUM_PHYSICAL_BLOCKSIZE);
if (rc)
goto error;
if (common->parmline) {
rc = set_cl_element(&cl[num++], "parmline", NULL, &common->parm_addr,
MAXIMUM_PARMLINE_SIZE, 0,
MAXIMUM_PHYSICAL_BLOCKSIZE);
if (rc)
goto error;
}
if (common->ramdisk) {
rc = set_cl_element(&cl[num++], "initial ramdisk",
common->ramdisk, &common->ramdisk_addr,
0, 0, 0x10000);
if (rc)
goto error;
}
*clp = cl;
*nump = num;
return 0;
error:
free(cl);
return rc;
}
static int
get_ipl_components(struct job_ipl_data *ipl, struct component_loc **clp,
int *nump, struct job_envblk_data *envblk)
{
struct component_loc *cl;
int num;
int rc;
rc = get_common_components(&ipl->common, &cl, &num, 1);
if (rc)
return rc;
rc = set_cl_element(&cl[num++], "environment block",
NULL,
&ipl->envblk_addr, envblk->size, 0,
MAXIMUM_PHYSICAL_BLOCKSIZE);
if (rc)
goto error;
*clp = cl;
*nump = num;
return 0;
error:
free(cl);
return rc;
}
static int
check_component_address_data(struct component_loc *cl, int num, char *name,
unsigned long address_limit)
{
int i;
/* Check for address limit */
for (i = 0; i < num; i++) {
if (*cl[i].addrp == UNSPECIFIED_ADDRESS)
continue;
if (*cl[i].addrp + cl[i].size > address_limit) {
if (name != NULL)
error_text("Section '%s'", name);
error_reason("Component '%s' exceeds available address "
"space (limit is 0x%08x)", cl[i].name,
address_limit);
return -1;
}
if (*cl[i].addrp < IMAGE_LOAD_ADDRESS) {
if (name != NULL)
error_text("Section '%s'", name);
error_reason("Component '%s' falls below available "
"address space (limit is 0x%08x)",
cl[i].name, IMAGE_LOAD_ADDRESS);
return -1;
}
}
/* Check for overlap */
for (i = 0; i < num - 1; i++) {
if (*cl[i].addrp == UNSPECIFIED_ADDRESS ||
*cl[i + 1].addrp == UNSPECIFIED_ADDRESS)
continue;
if (*cl[i].addrp + cl[i].size > *cl[i + 1].addrp) {
if (name != NULL)
error_text("Section '%s'", name);
error_reason("Components '%s' and '%s' overlap",
cl[i].name, cl[i + 1].name);
return -1;
}
}
return 0;
}
static int
finalize_component_address_data(struct component_loc *cl, int num,
unsigned long address_limit)
{
struct component_loc swap;
address_t addr;
int i;
int j;
int k;
/* Calculate unspecified addresses */
for (i = 0; i < num; i++) {
if (*cl[i].addrp != UNSPECIFIED_ADDRESS)
continue;
for (j = -1; j < i; j++) {
if (j < 0) {
/* Try address before first component */
addr = IMAGE_LOAD_ADDRESS;
} else {
/* Try address after component j */
addr = *cl[j].addrp + cl[j].size;
if (addr < IMAGE_LOAD_ADDRESS)
addr = IMAGE_LOAD_ADDRESS;
}
addr = ALIGN(addr, cl[i].align);
if (addr + cl[i].size > address_limit) {
error_text("Could not fit component '%s' into "
"available address space",
cl[i].name);
return -1;
}
/* Check for enough room */
if (*cl[j + 1].addrp != UNSPECIFIED_ADDRESS &&
addr + cl[i].size > *cl[j + 1].addrp)
continue;
*cl[i].addrp = addr;
/* If there is no next component we are done */
if (i == j + 1)
break;
/* Restore sort order */
swap = cl[j + 1];
cl[j + 1] = cl[i];
for (k = i; k > j + 2; k--)
cl[k] = cl[k - 1];
cl[j + 2] = swap;
break;
}
}
return 0;
}
static int
finalize_ipl_address_data(struct job_ipl_data *ipl, char *name,
struct job_envblk_data *envblk)
{
unsigned long address_limit;
struct component_loc *cl;
int num;
int rc;
address_limit = ipl->is_kdump ?
MIN(util_arch_hsa_maxsize(), ADDRESS_LIMIT) : ADDRESS_LIMIT;
rc = get_ipl_components(ipl, &cl, &num, envblk);
if (rc)
return rc;
sort_cl_array(cl, num);
rc = check_component_address_data(cl, num, name, address_limit);
if (rc)
goto out_free;
rc = finalize_component_address_data(cl, num, address_limit);
out_free:
free(cl);
return rc;
}
static int
finalize_common_address_data(struct job_common_ipl_data *common, char *name)
{
struct component_loc *cl;
int num;
int rc;
rc = get_common_components(common, &cl, &num, 0);
if (rc)
return rc;
sort_cl_array(cl, num);
rc = check_component_address_data(cl, num, name, ADDRESS_LIMIT);
if (rc)
goto out_free;
rc = finalize_component_address_data(cl, num, ADDRESS_LIMIT);
out_free:
free(cl);
return rc;
}
static void error_text_section(const char *text, const char *section, const char *file)
{
if (section == NULL) {
error_text("%s '%s'", text, file);
} else {
error_text("%s '%s' in section '%s'", text, file, section);
}
}
static int
check_common_ipl_data(struct job_common_ipl_data *common, const char *section,
bool may_ignore)
{
uint64_t max_parm_size, len;
char *buffer = NULL;
size_t size;
int rc;
if (common->image != NULL) {
rc = misc_read_file(common->image, &buffer, &size, 0);
if (rc) {
if (may_ignore && common->optional) {
printf("Optional section '%s': Missing image file '%s'\n",
section, common->image);
error_clear_reason();
common->ignore = true;
rc = 0;
goto skip_image;
}
fprintf(stderr, "Section '%s': Could not read image file '%s'\n",
section, common->image);
return rc;
}
if (size < MAX_COMMAND_LINE_SIZE + sizeof(uint64_t)) {
error_text_section("Image file", section, common->image);
free(buffer);
return -1;
}
max_parm_size = *(uint64_t *)(buffer + MAX_COMMAND_LINE_SIZE);
if (!max_parm_size)
max_parm_size = LEGACY_MAXIMUM_PARMLINE_SIZE;
len = common->parmline ? strlen(common->parmline) : 0;
if (len > max_parm_size) {
error_text("The length of the parameters line "
"(%d bytes) exceeds the allowed maximum "
"(%d bytes) in section '%s'", len, max_parm_size, section);
free(buffer);
return -1;
}
free(buffer);
buffer = NULL;
}
skip_image:
if (common->ramdisk != NULL) {
rc = misc_check_readable_file(common->ramdisk);
if (rc) {
if (common->optional) {
printf("Optional section '%s': Missing ramdisk file '%s'\n",
section, common->ramdisk);
error_clear_reason();
common->ignore = true;
rc = 0;
} else {
fprintf(stderr, "Section '%s': Missing ramdisk file '%s'\n",
section, common->ramdisk);
return rc;
}
}
}
return 0;
}
static int
check_job_ipl_data(struct job_ipl_data *ipl, char *name,
struct job_envblk_data *envblk, bool may_ignore)
{
int rc;
rc = check_common_ipl_data(&ipl->common, name, may_ignore);
if (rc)
return rc;
if (ipl->common.ignore)
return 0;
return finalize_ipl_address_data(ipl, name, envblk);
}
static int
check_job_segment_data(struct job_segment_data* segment, char* name)
{
int rc;
if (segment->segment != NULL) {
rc = misc_check_readable_file(segment->segment);
if (rc) {
error_text_section("Segment file", name, segment->segment);
return rc;
}
}
return 0;
}
static int
check_job_dump_data(struct job_dump_data* dump, char* name)
{
int rc;
if (dump->device != NULL) {
rc = misc_check_writable_device(dump->device, 1, 1);
if (rc) {
error_text_section("Dump device", name, dump->device);
return rc;
}
}
return 0;
}
int
check_job_dump_images(struct job_dump_data* dump, char* name)
{
int rc;
/* Add data needed to convert fs dump job to IPL job */
rc = misc_check_readable_file(ZFCPDUMP_IMAGE);
if (rc) {
error_text("Need external file '%s' for partition dump",
ZFCPDUMP_IMAGE);
return rc;
}
dump->common.image = misc_strdup(ZFCPDUMP_IMAGE);
if (dump->common.image == NULL)
return -1;
dump->common.image_addr = IMAGE_LOAD_ADDRESS;
/* Ramdisk is no longer required with new initramfs dump system */
if (misc_check_readable_file(ZFCPDUMP_INITRD))
dump->common.ramdisk = NULL;
else {
dump->common.ramdisk = misc_strdup(ZFCPDUMP_INITRD);
if (dump->common.ramdisk == NULL)
return -1;
dump->common.ramdisk_addr = UNSPECIFIED_ADDRESS;
}
dump->common.parmline = misc_strdup(DEFAULT_DUMP_PARMLINE);
dump->common.parm_addr = UNSPECIFIED_ADDRESS;
return finalize_common_address_data(&dump->common, name);
}
int
check_job_images_ngdump(struct job_dump_data* dump, char *name)
{
const char *helper = TOOLS_LIBDIR "/zipl_helper.prepare-ngdump";
char *ppn_cmd = NULL;
char *line = NULL;
FILE *fp;
int rc;
misc_asprintf(&ppn_cmd, "%s %s %d", helper, dump->device, dry_run);
printf("Run %s\n", ppn_cmd);
fp = popen(ppn_cmd, "r");
if (fp == NULL) {
error_reason("Failed to run popen(%s,\"r\",)");
free(ppn_cmd);
return -1;
}
free(ppn_cmd);
while (fscanf(fp, "%m[^\n]\n", &line) != EOF) {
char *param = NULL;
char *value = NULL;
int n = sscanf(line, "%m[^=]=%m[^\n]\n", &param, &value);
if (n != 2) {
if (verbose)
printf("%s\n", line);
free(line);
free(param);
free(value);
continue;
}
if (strcmp(param, "kernel") == 0) {
dump->common.image = value;
value = NULL;
} else if (strcmp(param, "initrd") == 0) {
dump->common.ramdisk = value;
value = NULL;
} else if (strcmp(param, "cmdline") == 0) {
dump->common.parmline = value;
value = NULL;
} else {
printf("%s\n", line);
}
free(line);
free(param);
free(value);
}
switch (pclose(fp)) {
case 0:
/* success */
break;
case -1:
error_reason("Failed to run pclose");
return -1;
default:
error_reason("Script could not determine dump parameters");
return -1;
}
rc = misc_check_readable_file(dump->common.image);
if (rc) {
error_text("Need external file '%s' for dump",
dump->common.image);
return rc;
}
dump->common.image_addr = IMAGE_LOAD_ADDRESS;
rc = misc_check_readable_file(dump->common.ramdisk);
if (rc) {
error_text("Need external file '%s' for dump",
dump->common.ramdisk);
return rc;
}
dump->common.ramdisk_addr = UNSPECIFIED_ADDRESS;
dump->common.parm_addr = UNSPECIFIED_ADDRESS;
return finalize_common_address_data(&dump->common, name);
}
static int
check_job_menu_data(struct job_menu_data *menu, struct job_envblk_data *envblk)
{
int rc;
int i;
for (i=0; i<menu->num; i++) {
switch (menu->entry[i].id) {
case job_ipl:
rc = check_job_ipl_data(&menu->entry[i].data.ipl,
menu->entry[i].name,
envblk, true);
if (rc)
return rc;
/* default_pos is 1-indexed */
if (menu->default_pos == i + 1 &&
menu->entry[i].data.ipl.common.ignore) {
error_text("Cannot ignore default entry");
return -1;
}
break;
case job_print_usage:
case job_print_version:
case job_segment:
case job_dump_partition:
case job_mvdump:
case job_menu:
case job_ipl_tape:
break;
}
}
return 0;
}
static int
check_job_ipl_tape_data(struct job_ipl_tape_data *ipl, char* name)
{
int rc;
if (ipl->device != NULL) {
rc = misc_check_writable_device(ipl->device, 1, 1);
if (rc) {
error_text_section("Tape device", name, ipl->device);
return rc;
}
}
rc = check_common_ipl_data(&ipl->common, name, false);
if (rc)
return rc;
return finalize_common_address_data(&ipl->common, name);
}
static int
check_job_mvdump_data(struct job_mvdump_data* dump, char* name)
{
int rc;
size_t size, i, j, k;
char* buffer;
rc = misc_read_file(dump->device_list, &buffer, &size, 0);
if (rc) {
error_text_section("Dump target list", name, dump->device_list);
return rc;
}
if (size == 0) {
error_text("Dump target list '%s' is empty.",
dump->device_list);
free(buffer);
return -1;
}
for (i = 0, j = 0, k = 0 ; i < size; i++) {
if (buffer[i] != '\n')
buffer[j++] = buffer[i];
else if (j > 0) {
if (k == MAX_DUMP_VOLUMES) {
error_text("Dump target list '%s' contains "
"more than %d entries.",
dump->device_list, k);
free(buffer);
return -1;
}
buffer[j] = 0;
dump->device[k] = misc_strdup(buffer);
if (dump->device[k] == NULL)
return -1;
k++;
j = 0;
}
}
free(buffer);
dump->device_count = k;
for (i = 0; i < (size_t) dump->device_count; i++) {
rc = misc_check_writable_device(dump->device[i], 1, 0);
if (rc) {
error_text("Dump target '%s'", dump->device[i]);
return rc;
}
for (j = 0; j < i; j++) {
if (!strcmp(dump->device[i], dump->device[j])) {
error_text("Dump target list '%s' contains "
"duplicate entry '%s'.",
dump->device_list, dump->device[i]);
return -1;
}
}
}
return 0;
}
static int
check_secure_boot(struct job_data *job)
{
switch (job->is_secure) {
case SECURE_BOOT_UNDEFINED:
case SECURE_BOOT_DISABLED:
case SECURE_BOOT_ENABLED:
case SECURE_BOOT_AUTO:
return 0;
default:
error_reason("Invalid secure boot setting '%d'",
job->is_secure);
return -1;
}
}
static int
check_job_data(struct job_data* job)
{
int rc = -1;
/* Check for missing information */
if (job->target.bootmap_dir != NULL) {
rc = misc_check_writable_directory(job->target.bootmap_dir);
if (rc) {
error_text_section("Target directory", job->name, job->target.bootmap_dir);
return rc;
}
}
switch (job->id) {
case job_print_usage:
case job_print_version:
rc = 0;
break;
case job_ipl:
rc = check_job_ipl_data(&job->data.ipl, job->name,
&job->envblk, false);
break;
case job_menu:
rc = check_job_menu_data(&job->data.menu, &job->envblk);
break;
case job_segment:
rc = check_job_segment_data(&job->data.segment, job->name);
break;
case job_dump_partition:
rc = check_job_dump_data(&job->data.dump, job->name);
break;
case job_ipl_tape:
rc = check_job_ipl_tape_data(&job->data.ipl_tape, job->name);
break;
case job_mvdump:
rc = check_job_mvdump_data(&job->data.mvdump, job->name);
}
if (!rc)
rc = check_secure_boot(job);
return rc;
}
static int
extract_address (char* string, address_t* address)
{
unsigned long long result;
/* Find trailing comma */
string = strrchr(string, ',');
if (string != NULL) {
/* Try to scan a hexadecimal address */
if (sscanf(string + 1, "%llx", &result) == 1) {
/* Got a match, remove address from string */
*string = '\0';
*address = (address_t) result;
return 0;
}
}
return -1;
}
static int
extract_memsize(char* string, uint64_t* size)
{
unsigned long long result;
/* Find trailing comma */
string = strrchr(string, ',');
if (string == NULL)
return -1;
if (sscanf(string + 1, "%lld", &result) != 1)
return -1;
switch(string[strlen(string) - 1]) {
case 'G':
case 'g':
/* Number in gigabytes */
result *= 1024LL * 1024LL * 1024LL;
break;
case 'M':
case 'm':
/* Number in megabytes*/
result *= 1024LL * 1024LL;
break;
case 'K':
case 'k':
/* Number in kilobytes */
result *= 1024LL;
break;
default:
/* Number in bytes */
break;
}
*string = '\0';
*size = result;
return 0;
}
static char*
append_parmline(char* a, char* b)
{
char* buffer;
int insert_blank;
/* Insert blank if none is present at end of A */
if (strlen(a) == 0)
insert_blank = 0;
else
insert_blank = (a[strlen(a) - 1] != ' ');
buffer = misc_malloc(strlen(a) + strlen(b) + (insert_blank ? 2 : 1));
if (buffer != NULL) {
if (insert_blank)
sprintf(buffer, "%s %s", a, b);
else
sprintf(buffer, "%s%s", a, b);
}
return buffer;
}
/* Combine given parmfile FILENAME and parmline LINE into resulting PARMLINE
* by appending lines as necessary. Parmline load address will be stored in
* ADDRESS. */
static int
get_parmline(char* filename, char* line, char** parmline, address_t* address,
char* section)
{
char* buffer;
char* result;
address_t addr;
size_t len;
int rc;
int from;
int to;
int got_lf;
addr = UNSPECIFIED_ADDRESS;
if (filename != NULL) {
/* Need a filename copy to be able to change it */
filename = misc_strdup(filename);
if (filename == NULL)
return -1;
extract_address(filename, &addr);
rc = misc_read_file(filename, &buffer, &len, 1);
if (rc) {
error_text_section("Parmfile", section, filename);
free(filename);
return rc;
}
free(filename);
/* Remove \n's from parmfile */
got_lf = 0;
for (from=0, to=0; buffer[from] != 0; from++)
if (buffer[from] != '\n') {
buffer[to++] = buffer[from];
got_lf = 0;
} else {
if (!got_lf)
buffer[to++] = ' ';
got_lf = 1;
}
buffer[to] = 0;
/* Combine parmfile and parmline if present */
if (line == NULL)
result = buffer;
else {
/* Append parmline to end of parmfile content */
result = append_parmline(buffer, line);
free(buffer);
if (result == NULL)
return -1;
}
} else if (line != NULL) {
result = misc_strdup(line);
if (result == NULL)
return -1;
} else result = NULL;
*parmline = result;
*address = addr;
return 0;
}
#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_ldl;
return 0;
case target_type_cdl:
*type = disk_type_eckd_cdl;
return 0;
default:
return -1;
}
}
static int
get_job_from_section_data(char* data[], struct job_data* job, char* section)
{
int rc;
switch (scan_get_section_type(data)) {
case section_ipl:
/* IPL job */
job->id = job_ipl;
/* Fill in name of bootmap directory */
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;
}
if (data[(int) scan_keyword_targettype] != NULL) {
if (type_from_target(
data[(int) scan_keyword_targettype],
&job->target.targettype))
return -1;
}
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, ","));
}
if (data[(int) scan_keyword_targetblocksize] != NULL)
job->target.targetblocksize =
atoi(data[(int) scan_keyword_targetblocksize]);
if (data[(int) scan_keyword_targetoffset] != NULL)
job->target.targetoffset =
atol(data[(int) scan_keyword_targetoffset]);
/* Fill in name and address of image file */
job->data.ipl.common.image = misc_strdup(
data[(int) scan_keyword_image]);
if (job->data.ipl.common.image == NULL)
return -1;
if (extract_address(job->data.ipl.common.image,
&job->data.ipl.common.image_addr)) {
job->data.ipl.common.image_addr = IMAGE_LOAD_ADDRESS;
}
/* Fill in parmline */
rc = get_parmline(data[(int) scan_keyword_parmfile],
data[(int) scan_keyword_parameters],
&job->data.ipl.common.parmline,
&job->data.ipl.common.parm_addr, section);
if (rc)
return rc;
/* Fill in environment block */
job->data.ipl.envblk_addr = UNSPECIFIED_ADDRESS;
/* Fill in name and address of ramdisk file */
if (data[(int) scan_keyword_ramdisk] != NULL) {
job->data.ipl.common.ramdisk =
misc_strdup(data[(int) scan_keyword_ramdisk]);
if (job->data.ipl.common.ramdisk == NULL)
return -1;
if (extract_address(job->data.ipl.common.ramdisk,
&job->data.ipl.common.ramdisk_addr)) {
job->data.ipl.common.ramdisk_addr =
UNSPECIFIED_ADDRESS;
}
}
/* Fill in kdump */
if (data[(int) scan_keyword_kdump] != NULL) {
if (strcmp(data[(int) scan_keyword_kdump],
"auto") == 0) {
job->data.ipl.is_kdump = 1;
} else {
error_reason("Invalid kdump setting '%s'",
data[(int) scan_keyword_kdump]);
return -1;
}
}
/* Fill in secure boot */
if (data[(int) scan_keyword_secure] != NULL) {
rc = set_secure_ipl(data[(int) scan_keyword_secure],
&job->is_secure);
if (rc)
return rc;
}
job->data.ipl.common.optional = false;
if (data[(int) scan_keyword_optional] != NULL) {
job->data.ipl.common.optional =
atoi(data[(int) scan_keyword_optional]) == 1;
}
job->data.ipl.common.ignore = false;
break;
case section_ipl_tape:
/* Tape IPL job */
job->id = job_ipl_tape;
/* Fill in name of tape device */
job->data.ipl_tape.device =
misc_strdup(data[(int) scan_keyword_tape]);
if (job->data.ipl_tape.device == NULL)
return -1;
/* Fill in name and address of image file */
job->data.ipl_tape.common.image = misc_strdup(
data[(int) scan_keyword_image]);
if (job->data.ipl_tape.common.image == NULL)
return -1;
if (extract_address(job->data.ipl_tape.common.image,
&job->data.ipl_tape.common.image_addr)) {
job->data.ipl_tape.common.image_addr = IMAGE_LOAD_ADDRESS;
}
/* Fill in parmline */
rc = get_parmline(data[(int) scan_keyword_parmfile],
data[(int) scan_keyword_parameters],
&job->data.ipl_tape.common.parmline,
&job->data.ipl_tape.common.parm_addr, section);
if (rc)
return rc;
/* Fill in name and address of ramdisk file */
if (data[(int) scan_keyword_ramdisk] != NULL) {
job->data.ipl_tape.common.ramdisk =
misc_strdup(data[(int) scan_keyword_ramdisk]);
if (job->data.ipl_tape.common.ramdisk == NULL)
return -1;
if (extract_address(job->data.ipl_tape.common.ramdisk,
&job->data.ipl_tape.common.ramdisk_addr)) {
job->data.ipl_tape.common.ramdisk_addr =
UNSPECIFIED_ADDRESS;
}
}
break;
case section_segment:
/* SEGMENT LOAD job */
job->id = job_segment;
/* Fill in name of bootmap directory */
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 =
misc_strdup(data[(int) scan_keyword_segment]);
if (job->data.segment.segment == NULL)
return -1;
extract_address(job->data.segment.segment,
&job->data.segment.segment_addr);
break;
case section_dump:
/* DUMP TO PARTITION job */
job->id = job_dump_partition;
/* Fill in device node filename */
job->data.dump.device = misc_strdup(
data[(int) scan_keyword_dumpto]);
if (job->data.dump.device == NULL)
return -1;
/* Check for mem size specification */
if (extract_memsize(job->data.dump.device,
&job->data.dump.mem) == 0) {
/* Ensure megabyte alignment of size */
job->data.dump.mem =
(job->data.dump.mem + MEGABYTE_MASK) &
~MEGABYTE_MASK;
printf("Found specification of dump size limit "
"to %lldMB\n",
(unsigned long long) job->data.dump.mem /
(1024LL * 1024LL));
}
else
job->data.dump.mem = -1LL;
break;
case section_mvdump:
/* DUMP TO MULTI-VOLUME job */
job->id = job_mvdump;
/* Fill in name of file containing the multiple partitions */
job->data.mvdump.device_list = misc_strdup(
data[(int) scan_keyword_mvdump]);
if (job->data.mvdump.device_list == NULL)
return -1;
/* Check for mem size specification */
if (extract_memsize(job->data.mvdump.device_list,
&job->data.mvdump.mem) == 0) {
/* Ensure megabyte alignment of size */
job->data.mvdump.mem =
(job->data.mvdump.mem + MEGABYTE_MASK) &
~MEGABYTE_MASK;
printf("Found specification of dump size limit "
"to %lldMB\n",
(unsigned long long) job->data.mvdump.mem /
(1024LL * 1024LL));
} else
job->data.mvdump.mem = -1LL;
break;
default:
/* Should not happen */
job->id = job_print_usage;
break;
}
return 0;
}
static int
get_menu_job(struct scan_token* scan, char* menu, struct job_data* job)
{
char* data[SCAN_KEYWORD_NUM];
struct job_data* temp_job;
char* section;
int index;
int i;
int j;
int current;
int rc;
job->id = job_menu;
job->name = misc_strdup(menu);
if (job->name == NULL)
return -1;
/* Extract menu job from configuration data in SCAN */
index = scan_find_section(scan, menu, scan_id_menu_heading, 0);
if (index<0) {
error_reason("Menu section '%s' not found", menu);
return -1;
}
/* Count menu entries, find default entry and target directory */
job->data.menu.num = 0;
job->data.menu.default_pos = -1;
job->data.menu.prompt = MENU_DEFAULT_PROMPT;
job->data.menu.timeout = MENU_DEFAULT_TIMEOUT;
for (i=index+1; (scan[i].id != scan_id_empty) &&
(scan[i].id != scan_id_section_heading) &&
(scan[i].id != scan_id_menu_heading); i++) {
if (scan[i].id == scan_id_number_assignment) {
if (job->data.menu.default_pos < 0)
job->data.menu.default_pos =
scan[i].content.number.number;
job->data.menu.num++;
} else if (scan[i].id == scan_id_keyword_assignment) {
switch (scan[i].content.keyword.keyword) {
case scan_keyword_default:
job->data.menu.default_pos =
atol(scan[i].content.keyword.value);
break;
case scan_keyword_prompt:
job->data.menu.prompt =
atol(scan[i].content.keyword.value);
break;
case scan_keyword_timeout:
job->data.menu.timeout =
atol(scan[i].content.keyword.value);
break;
case scan_keyword_secure:
rc = set_secure_ipl(
scan[i].content.keyword.value,
&job->is_secure);
if (rc)
return rc;
break;
case scan_keyword_target:
job->target.bootmap_dir = misc_strdup(
scan[i].content.keyword.value);
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;
}
}
}
if (job->data.menu.num == 0) {
/* Should not happen */
error_reason("No entries found in menu '%s'", menu);
return -1;
}
/* Allocate array */
job->data.menu.entry = misc_malloc(sizeof(struct job_menu_entry) *
job->data.menu.num);
if (job->data.menu.entry == NULL)
return -1;
memset((void *) job->data.menu.entry, 0,
sizeof(struct job_menu_entry) * job->data.menu.num);
/* Fill in data */
current = 0;
job->data.menu.entry->is_secure = SECURE_BOOT_UNDEFINED;
for (i=index+1; (scan[i].id != scan_id_empty) &&
(scan[i].id != scan_id_section_heading) &&
(scan[i].id != scan_id_menu_heading); i++) {
if (scan[i].id != scan_id_number_assignment)
continue;
job->data.menu.entry[current].pos =
scan[i].content.number.number;
job->data.menu.entry[current].name =
misc_strdup(scan[i].content.number.value);
if (job->data.menu.entry[current].name == NULL)
return -1;
section = job->data.menu.entry[current].name;
/* Search for section in config file */
j = scan_find_section(scan, section, scan_id_section_heading,
0);
if (j<0) {
error_reason("Configuration section '%s' not found",
section);
return -1;
}
/* Get section_data from scan */
memset(&data, 0, sizeof(data));
for (j++; scan[j].id == scan_id_keyword_assignment; j++)
data[(int) scan[j].content.keyword.keyword] =
scan[j].content.keyword.value;
/* Get job from section_data */
temp_job = (struct job_data *) misc_malloc(
sizeof(struct job_data));
if (temp_job == NULL)
return -1;
memset((void *) temp_job, 0, sizeof(struct job_data));
temp_job->is_secure = SECURE_BOOT_UNDEFINED;
rc = get_job_from_section_data(data, temp_job,
job->data.menu.entry[current].name);
if (rc) {
job_free(temp_job);
return rc;
}
/* Copy data from temporary job */
switch (temp_job->id) {
case job_ipl:
job->data.menu.entry[current].id = job_ipl;
job->data.menu.entry[current].data.ipl =
temp_job->data.ipl;
job->data.menu.entry[current].is_secure =
temp_job->is_secure;
memset((void *) &temp_job->data.ipl, 0,
sizeof(struct job_ipl_data));
break;
default:
error_reason("Section '%s' cannot be included "
"in menu '%s'", section, menu);
rc = -1;
break;
}
job_free(temp_job);
if (rc)
return rc;
current++;
}
return 0;
}
static int
get_default_section(struct scan_token* scan, char** section, int* is_menu)
{
int i;
/* Find defaultboot section */
i = scan_find_section(scan, DEFAULTBOOT_SECTION,
scan_id_section_heading, 0);
if (i<0) {
error_reason("No '" DEFAULTBOOT_SECTION "' section found and "
"no section specified on command line");
return -1;
}
/* Find 'default' or 'defaultmenu' keyword */
for (i++; scan[i].id == scan_id_keyword_assignment; i++) {
if (scan[i].content.keyword.keyword == scan_keyword_default) {
*section = scan[i].content.keyword.value;
*is_menu = 0;
return 0;
}
if (scan[i].content.keyword.keyword ==
scan_keyword_defaultmenu) {
*section = scan[i].content.keyword.value;
*is_menu = 1;
return 0;
}
}
/* Should not happen */
error_reason("No default section specified");
return -1;
}
/* Extract job data from configuration data in SCAN. SECTION specifies the
* name of the section to use or NULL if the default section should be used.
* Upon success, return zero, store job data in JOB and set NAME to point to
* the section name. Return non-zero otherwise. */
static int
get_section_job(struct scan_token* scan, char* section, struct job_data* job,
char* extra_parmline)
{
char* data[SCAN_KEYWORD_NUM];
char* buffer;
int rc;
int i;
if (section == NULL) {
rc = get_default_section(scan, &section, &i);
if (rc)
return rc;
if (i) {
/* 'defaultmenu' was specified */
rc = get_menu_job(scan, section, job);
return rc;
}
}
if (strcmp(section, DEFAULTBOOT_SECTION) == 0) {
error_reason("Special section '" DEFAULTBOOT_SECTION "' cannot "
"be used as target section");
return -1;
}
/* Search for section in config file */
i = scan_find_section(scan, section, scan_id_section_heading, 0);
if (i<0) {
error_reason("Configuration section '%s' not found", section);
return -1;
}
job->name = misc_strdup(section);
if (job->name == NULL)
return -1;
/* Get section_data from scan */
memset(&data, 0, sizeof(data));
for (i++; scan[i].id == scan_id_keyword_assignment; i++)
data[(int) scan[i].content.keyword.keyword] =
scan[i].content.keyword.value;
/* Get job from section_data */
rc = get_job_from_section_data(data, job, job->name);
if (rc)
return rc;
/* Append extra parmline */
if (extra_parmline != NULL) {
switch (job->id) {
case job_ipl:
if (job->data.ipl.common.parmline == NULL)
buffer = misc_strdup(extra_parmline);
else {
buffer = append_parmline(
job->data.ipl.common.parmline,
extra_parmline);
free(job->data.ipl.common.parmline);
}
job->data.ipl.common.parmline = buffer;
if (buffer == NULL)
return -1;
break;
case job_segment:
error_reason("Option 'parameters' cannot be used with "
"section '%s'", section);
return -1;
break;
case job_dump_partition:
error_reason("Option 'parameters' cannot be used with "
"partition dump section '%s'", section);
return -1;
break;
default:
/* Should not happen */
break;
}
}
return 0;
}
static int
get_job_from_config_file(struct command_line* cmdline, struct job_data* job)
{
struct scan_token* scan;
struct scan_token* new_scan;
const char *filename = NULL;
char* source;
int i, rc, scan_size;
/* Read configuration file */
if (cmdline->config != NULL) {
/* Use config file as provided on command line */
filename = cmdline->config;
source = " (from command line)";
} else if (getenv(ZIPL_CONF_VAR) != NULL) {
/* Use config file specified by environment variable */
filename = getenv(ZIPL_CONF_VAR);
source = " (from environment variable "
ZIPL_CONF_VAR ")";
} else {
/* Use default config file */
for (i = 0; zipl_conf[i]; i++) {
if (misc_check_readable_file(zipl_conf[i]) == 0) {
filename = zipl_conf[i];
break;
}
}
if (filename == NULL) {
error_text("No zipl configuration was readable");
return -1;
}
source = "";
}
printf("Using config file '%s'%s\n", filename, source);
scan_size = scan_file(filename, &scan);
if (scan_size <= 0) {
error_text("Config file '%s'", filename);
return scan_size;
}
/* Check if a BLS directory was provided on command line */
if (cmdline->blsdir != NULL) {
blsdir = cmdline->blsdir;
} else {
blsdir = ZIPL_DEFAULT_BLSDIR;
}
rc = scan_bls(&scan, scan_size);
if (rc) {
error_text("BLS parsing '%s'", blsdir);
return rc;
}
if ((cmdline->menu == NULL) && (cmdline->section == NULL)) {
rc = scan_check_defaultboot(scan);
if (rc < 0) {
error_text("Config file '%s'", filename);
scan_free(scan);
return rc;
}
if (rc == 1) {
new_scan = scan_build_automenu(scan);
scan_free(scan);
if (new_scan == NULL) {
error_text("Config file '%s'", filename);
return -1;
}
scan = new_scan;
}
}
rc = scan_check(scan);
if (rc) {
error_text("Config file '%s'", filename);
scan_free(scan);
return rc;
}
/* maybe we need to update bls search path with target path */
scan_update_bls_path(scan);
/* Get job from config file data */
if (cmdline->menu != NULL)
rc = get_menu_job(scan, cmdline->menu, job);
else {
rc = get_section_job(scan, cmdline->section, job,
cmdline->data[(int) scan_keyword_parameters]);
}
/* Make sure no '--parameters' option was specified when writing a
* menu section. */
if (job->id == job_menu &&
cmdline->data[(int) scan_keyword_parameters]) {
error_text("Option 'parameters' cannot be used with a menu "
"section");
rc = -1;
}
scan_free(scan);
return rc;
}
static int get_job_envblk_data(struct job_data *job, char *import_hint)
{
struct job_envblk_data *data = &job->envblk;
int fd;
switch (job->id) {
case job_ipl:
case job_menu:
break;
default:
return 0;
}
fd = open(job->target.bootmap_dir, O_RDONLY);
if (fd < 0) {
error_reason(strerror(errno));
error_text("Could not open bootmap dir");
return -1;
}
if (envblk_size_get(fd, &data->size)) {
close(fd);
error_text("Could not get environment block size");
return -1;
}
close(fd);
data->buf = misc_malloc(data->size);
if (data->buf == NULL) {
error_text("Could not allocate environment block");
return -1;
}
envblk_create_blank(data->buf, data->size);
if (envblk_import(import_hint ?: ENVBLK_DEFAULT_IMPORT_SOURCE,
data->buf, data->size)) {
free(data->buf);
data->buf = NULL;
return -1;
}
return 0;
}
int
job_get(int argc, char* argv[], struct job_data** data)
{
struct command_line cmdline;
struct job_data* job;
int rc;
rc = get_command_line(argc, argv, &cmdline);
if (rc)
return rc;
job = (struct job_data *) misc_malloc(sizeof(struct job_data));
if (job == NULL)
return -1;
memset((void *) job, 0, sizeof(struct job_data));
/* Fill in global options */
job->noninteractive = cmdline.noninteractive;
job->verbose = cmdline.verbose;
job->add_files = cmdline.add_files;
job->data.dump.no_compress = cmdline.no_compress;
job->data.mvdump.force = cmdline.force;
job->dry_run = cmdline.dry_run;
job->is_secure = SECURE_BOOT_UNDEFINED;
job->is_ldipl_dump = cmdline.is_ldipl_dump;
if (job->verbose)
printf("Looking for components in '%s'\n", util_libdir());
/* Get job data from user input */
if (cmdline.help) {
job->command_line = 1;
job->id = job_print_usage;
} else if (cmdline.version) {
job->command_line = 1;
job->id = job_print_version;
} else if (cmdline.type != section_invalid) {
job->command_line = 1;
rc = get_job_from_section_data(cmdline.data, job, NULL);
} else {
job->command_line = 0;
rc = get_job_from_config_file(&cmdline, job);
}
if (rc) {
job_free(job);
return rc;
}
if (cmdline.is_secure != SECURE_BOOT_UNDEFINED)
job->is_secure = cmdline.is_secure;
else if (job->id != job_menu && job->is_secure == SECURE_BOOT_UNDEFINED)
job->is_secure = SECURE_BOOT_AUTO;
rc = get_job_envblk_data(job, cmdline.envblk_import_hint);
if (rc) {
job_free(job);
return -1;
}
/* Check job data for validity */
rc = check_job_data(job);
if (rc) {
job_free(job);
return rc;
}
*data = job;
return rc;
}
bool
is_ngdump_enabled(struct job_data *job)
{
if (job->is_ldipl_dump)
return true;
return disk_is_nvme(job->data.dump.device, &job->target);
}