mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Add generation of shell autocompletion scripts. Acked-by: Steffen Eiden <seiden@linux.ibm.com> Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com> Signed-off-by: Szabina Korbai <szkorbai@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
532 lines
11 KiB
C
532 lines
11 KiB
C
/*
|
|
* zipl-editenv: Environment Editor for zSeries Initial Program Loader
|
|
*
|
|
* Copyright IBM Corp. 2020
|
|
*
|
|
* 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 <string.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/ioctl.h>
|
|
#include <assert.h>
|
|
|
|
#include "lib/util_prg.h"
|
|
#include "lib/util_opt.h"
|
|
#include "lib/util_part.h"
|
|
#include "lib/util_path.h"
|
|
#include "lib/util_proc.h"
|
|
|
|
#include "bootmap.h"
|
|
#include "disk.h"
|
|
#include "envblk.h"
|
|
#include "error.h"
|
|
#include "misc.h"
|
|
#include "zipl_editenv_cli.h"
|
|
|
|
/* from linux/fs.h */
|
|
#define FIBMAP _IO(0x00, 1)
|
|
|
|
static const struct util_prg prg = {
|
|
.desc = "zipl-editenv: Environment Editor for zSeries Initial Program Loader",
|
|
.copyright_vec = {
|
|
{
|
|
.owner = "IBM Corp.",
|
|
.pub_first = 2021,
|
|
},
|
|
UTIL_PRG_COPYRIGHT_END
|
|
}
|
|
};
|
|
|
|
int verbose;
|
|
static char *bootmap_dir;
|
|
static char *site_id;
|
|
static char *eff_site_id;
|
|
|
|
enum op_id {
|
|
INVALID_OP_ID,
|
|
SET_OP_ID,
|
|
UNSET_OP_ID,
|
|
RESET_OP_ID,
|
|
LIST_OP_ID,
|
|
LAST_OP_ID
|
|
};
|
|
|
|
static char *op_desc[LAST_OP_ID] = {"invalid", "set", "unset", "reset", "list"};
|
|
|
|
struct zipl_envblk {
|
|
struct misc_fd mfd;
|
|
char *buf;
|
|
off_t offset;
|
|
int size;
|
|
unsigned int need_update:1;
|
|
};
|
|
|
|
static char *opcode2desc(enum op_id opcode)
|
|
{
|
|
return op_desc[opcode];
|
|
}
|
|
|
|
static void zipl_envblk_init(struct zipl_envblk *zeb)
|
|
{
|
|
memset(zeb, 0, sizeof(*zeb));
|
|
zeb->mfd.fd = -1;
|
|
}
|
|
|
|
static int do_list(char *envblk, unsigned int envblk_size)
|
|
{
|
|
assert(!site_id || !eff_site_id);
|
|
|
|
if (site_id)
|
|
return envblk_list_site(envblk,
|
|
envblk_size,
|
|
atoi(site_id));
|
|
if (eff_site_id)
|
|
return envblk_list_effective_site(envblk,
|
|
envblk_size,
|
|
atoi(eff_site_id));
|
|
return envblk_list_all(envblk, envblk_size, 0);
|
|
}
|
|
|
|
/**
|
|
* sync the content of the environment block with disk
|
|
*/
|
|
static int envblk_update(struct zipl_envblk *zeb)
|
|
{
|
|
struct util_proc_part_entry part_entry;
|
|
struct stat info;
|
|
blocknum_t blknr;
|
|
char *dev_name;
|
|
int dev_fd;
|
|
|
|
if (fstat(zeb->mfd.fd, &info))
|
|
return -1;
|
|
|
|
if (util_proc_part_get_entry(info.st_dev, &part_entry) != 0)
|
|
return -1;
|
|
|
|
dev_name = misc_make_path("/dev", part_entry.name);
|
|
if (!dev_name) {
|
|
error_reason("Could not make path for %s", part_entry.name);
|
|
return -1;
|
|
}
|
|
dev_fd = open(dev_name, O_RDWR | O_DIRECT);
|
|
if (dev_fd < 0) {
|
|
error_reason("Could not open file %s", dev_name);
|
|
free(dev_name);
|
|
goto error;
|
|
}
|
|
free(dev_name);
|
|
/*
|
|
* det disk address of the environment block
|
|
*/
|
|
if (fs_map(zeb->mfd.fd, zeb->offset, &blknr, zeb->size) != 0)
|
|
goto error_close;
|
|
|
|
if (lseek(dev_fd, blknr * (off_t)zeb->size, SEEK_SET) < 0) {
|
|
error_reason(strerror(errno));
|
|
goto error_close;
|
|
}
|
|
if (write(dev_fd, zeb->buf, zeb->size) != zeb->size) {
|
|
error_reason("Could not update physical block %ld on %s",
|
|
blknr, part_entry.name);
|
|
goto error_close;
|
|
}
|
|
if (verbose)
|
|
printf("Physical block %ld on %s got updated\n",
|
|
blknr, part_entry.name);
|
|
|
|
util_proc_part_free_entry(&part_entry);
|
|
return 0;
|
|
error_close:
|
|
close(dev_fd);
|
|
error:
|
|
util_proc_part_free_entry(&part_entry);
|
|
return -1;
|
|
}
|
|
|
|
static int envblk_close(struct zipl_envblk *zeb)
|
|
{
|
|
if (zeb->buf != NULL) {
|
|
free(zeb->buf);
|
|
zeb->buf = NULL;
|
|
}
|
|
if (zeb->mfd.fd < 0)
|
|
return 0;
|
|
|
|
if (close(zeb->mfd.fd) == 0) {
|
|
zeb->mfd.fd = -1;
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Open a regular file, which contains environment block, and
|
|
* read that block to a buffer. Fill in environment block info
|
|
* ZEB with valid values
|
|
*/
|
|
static int envblk_open(struct zipl_envblk *zeb)
|
|
{
|
|
char *bootmap_file;
|
|
|
|
if (zeb->mfd.fd >= 0)
|
|
/* it was opened before */
|
|
return 0;
|
|
bootmap_file =
|
|
misc_make_path(bootmap_dir != NULL ? bootmap_dir : "/boot",
|
|
"bootmap");
|
|
if (!bootmap_file) {
|
|
error_reason("Could not make path for bootmap file");
|
|
return -1;
|
|
}
|
|
zeb->mfd.fd = open(bootmap_file, O_RDONLY);
|
|
if (zeb->mfd.fd < 0) {
|
|
error_reason("Could not open bootmap file %s: %s",
|
|
bootmap_file, strerror(errno));
|
|
free(bootmap_file);
|
|
bootmap_file = NULL;
|
|
return -1;
|
|
}
|
|
if (verbose)
|
|
printf("Processing bootmap file at %s\n", bootmap_file);
|
|
|
|
if (envblk_size_get(&zeb->mfd, &zeb->size)) {
|
|
error_reason("Could not get environment block size");
|
|
goto error;
|
|
}
|
|
if (envblk_offset_get(&zeb->mfd, &zeb->offset)) {
|
|
error_reason("Could not get environment block location");
|
|
goto error;
|
|
}
|
|
if (zeb->offset == 0) {
|
|
error_reason("Environment not installed - please run zipl");
|
|
goto error;
|
|
}
|
|
/* reopen for direct operations */
|
|
close(zeb->mfd.fd);
|
|
zeb->mfd.fd = open(bootmap_file, O_RDWR | O_DIRECT);
|
|
if (zeb->mfd.fd < 0) {
|
|
error_reason("Could not open environment block at %s",
|
|
bootmap_file);
|
|
goto error;
|
|
}
|
|
free(bootmap_file);
|
|
bootmap_file = NULL;
|
|
|
|
zeb->buf = aligned_alloc(zeb->size, zeb->size);
|
|
if (!zeb->buf) {
|
|
error_reason("Could not allocate aligned memory region");
|
|
goto error;
|
|
}
|
|
if (lseek(zeb->mfd.fd, zeb->offset, SEEK_SET) < 0) {
|
|
error_reason(strerror(errno));
|
|
goto error;
|
|
}
|
|
if (read(zeb->mfd.fd, zeb->buf, zeb->size) != zeb->size) {
|
|
error_reason("Could not read environment block");
|
|
goto error;
|
|
}
|
|
if (memcmp(zeb->buf, ZIPL_ENVBLK_SIGNATURE,
|
|
sizeof(ZIPL_ENVBLK_SIGNATURE) - 1) != 0) {
|
|
error_reason("Found corrupted environment block - please run zipl");
|
|
goto error;
|
|
}
|
|
return 0;
|
|
error:
|
|
if (bootmap_file)
|
|
free(bootmap_file);
|
|
envblk_close(zeb);
|
|
return -1;
|
|
}
|
|
|
|
static char *build_prefixed_name(char *arg)
|
|
{
|
|
char *name = NULL;
|
|
|
|
if (!site_id)
|
|
return strdup(arg);
|
|
|
|
name = misc_malloc(strlen(arg) + 1);
|
|
if (name) {
|
|
name[0] = *site_id;
|
|
strcpy(name + 1, arg);
|
|
}
|
|
return name;
|
|
}
|
|
|
|
static int env_list(struct zipl_envblk *zeb)
|
|
{
|
|
if (envblk_open(zeb))
|
|
return -1;
|
|
return do_list(zeb->buf, strnlen(zeb->buf, zeb->size));
|
|
}
|
|
|
|
static int env_set(struct zipl_envblk *zeb, char *arg)
|
|
{
|
|
char *pname;
|
|
char *value;
|
|
|
|
if (envblk_open(zeb))
|
|
return -1;
|
|
|
|
pname = build_prefixed_name(arg);
|
|
if (!pname) {
|
|
error_reason(strerror(errno));
|
|
return -1;
|
|
}
|
|
/*
|
|
* identify name and locate value
|
|
*/
|
|
value = strchr(pname, '=');
|
|
if (!value || value == pname) {
|
|
/* name is not identified */
|
|
error_reason("Invalid argument %s", arg);
|
|
goto error;
|
|
}
|
|
*value = '\0';
|
|
value++;
|
|
|
|
if (envblk_check_name(get_name(site_id, pname),
|
|
get_name_len(site_id, pname))) {
|
|
error_reason("Unacceptable name '%s'",
|
|
get_name(site_id, pname));
|
|
goto error;
|
|
}
|
|
if (envblk_set(zeb->buf, zeb->size, pname, value) == 0) {
|
|
free(pname);
|
|
zeb->need_update = 1;
|
|
return 0;
|
|
}
|
|
error:
|
|
free(pname);
|
|
return -1;
|
|
}
|
|
|
|
static int env_unset(struct zipl_envblk *zeb, char *arg)
|
|
{
|
|
char *pname;
|
|
|
|
if (envblk_open(zeb))
|
|
return -1;
|
|
/*
|
|
* check name for validness
|
|
*/
|
|
if (strchr(arg, '=') != NULL) {
|
|
error_reason("Invalid argument %s", arg);
|
|
return -1;
|
|
}
|
|
pname = build_prefixed_name(arg);
|
|
if (!pname) {
|
|
error_reason(strerror(errno));
|
|
return -1;
|
|
}
|
|
if (envblk_unset(zeb->buf, strnlen(zeb->buf, zeb->size),
|
|
pname, site_id) == 0) {
|
|
zeb->need_update = 1;
|
|
free(pname);
|
|
return 0;
|
|
}
|
|
free(pname);
|
|
return -1;
|
|
}
|
|
|
|
static int env_reset(struct zipl_envblk *zeb)
|
|
{
|
|
if (envblk_open(zeb))
|
|
return -1;
|
|
if (site_id)
|
|
envblk_remove_namespace(zeb->buf, zeb->size, site_id);
|
|
else
|
|
envblk_blank(zeb->buf, zeb->size);
|
|
zeb->need_update = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int save_op(enum op_id *dst, enum op_id src)
|
|
{
|
|
if (*dst != INVALID_OP_ID) {
|
|
error_reason("Can't perform more than one operation (%s and %s) at a time\n",
|
|
opcode2desc(*dst), opcode2desc(src));
|
|
return -EINVAL;
|
|
}
|
|
*dst = src;
|
|
return 0;
|
|
}
|
|
|
|
static int save_op_arg(enum op_id *dst, enum op_id src,
|
|
char **dst_arg, char *src_arg)
|
|
{
|
|
int ret;
|
|
|
|
ret = save_op(dst, src);
|
|
if (ret)
|
|
return ret;
|
|
*dst_arg = strdup(src_arg);
|
|
if (*dst_arg == NULL) {
|
|
error_reason("Couldn't copy the argument\n");
|
|
return -ENOMEM;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int set_site_id_common(char *arg, char **id)
|
|
{
|
|
long val;
|
|
char *endptr;
|
|
|
|
val = strtol(arg, &endptr, 10);
|
|
if (endptr == arg || *endptr != '\0') {
|
|
error_reason("Site-ID '%s' is not a decimal number", arg);
|
|
return -EINVAL;
|
|
}
|
|
if (val < 0 || val > 9) {
|
|
error_reason("Unsupported site-ID '%s'", arg);
|
|
return -EINVAL;
|
|
}
|
|
if (endptr - arg > 1) {
|
|
/* case of '00...0' */
|
|
error_reason("Site-ID '%s' is not a decimal number", arg);
|
|
return -EINVAL;
|
|
}
|
|
*id = strdup(arg);
|
|
if (!*id) {
|
|
error_reason(strerror(errno));
|
|
return -ENOMEM;
|
|
}
|
|
/* larger IDs are unsupported */
|
|
assert(strlen(*id) == 1);
|
|
return 0;
|
|
}
|
|
|
|
#define warn_on_incompat_site_opts \
|
|
error_reason("options %s and %s are incompatible", \
|
|
"'-S (--site)'", "'-E (--effective-site)'")
|
|
|
|
static int set_site_id(char *arg)
|
|
{
|
|
if (eff_site_id) {
|
|
warn_on_incompat_site_opts;
|
|
return -EINVAL;
|
|
}
|
|
return set_site_id_common(arg, &site_id);
|
|
}
|
|
|
|
static int set_effective_site_id(char *arg)
|
|
{
|
|
if (site_id) {
|
|
warn_on_incompat_site_opts;
|
|
return -EINVAL;
|
|
}
|
|
return set_site_id_common(arg, &eff_site_id);
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
enum op_id opcode = INVALID_OP_ID;
|
|
struct zipl_envblk zeb;
|
|
char *saved_arg = NULL;
|
|
int ret = 0;
|
|
int c;
|
|
|
|
zipl_envblk_init(&zeb);
|
|
util_prg_init(&prg);
|
|
util_opt_init(opt_vec, NULL);
|
|
|
|
while (1) {
|
|
c = util_opt_getopt_long(argc, argv);
|
|
if (c == -1)
|
|
break;
|
|
switch (c) {
|
|
case 'h':
|
|
util_prg_print_help();
|
|
util_opt_print_help();
|
|
goto out;
|
|
case 'v':
|
|
util_prg_print_version();
|
|
goto out;
|
|
case 'V':
|
|
verbose = 1;
|
|
break;
|
|
case 't':
|
|
bootmap_dir = strdup(optarg);
|
|
if (!bootmap_dir)
|
|
ret = -1;
|
|
break;
|
|
case 'S':
|
|
ret = set_site_id(optarg);
|
|
break;
|
|
case 'E':
|
|
ret = set_effective_site_id(optarg);
|
|
break;
|
|
case 'r':
|
|
ret = save_op(&opcode, RESET_OP_ID);
|
|
break;
|
|
case 'l':
|
|
ret = save_op(&opcode, LIST_OP_ID);
|
|
break;
|
|
case 's':
|
|
ret = save_op_arg(&opcode, SET_OP_ID,
|
|
&saved_arg, optarg);
|
|
break;
|
|
case 'u':
|
|
ret = save_op_arg(&opcode, UNSET_OP_ID,
|
|
&saved_arg, optarg);
|
|
break;
|
|
default:
|
|
util_opt_print_parse_error(c, argv);
|
|
ret = -1;
|
|
}
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
if (optind < argc) {
|
|
fprintf(stderr, "%s: Unknown command", argv[0]);
|
|
while (optind < argc)
|
|
printf(" %s", argv[optind++]);
|
|
fprintf(stderr, "\n");
|
|
goto out;
|
|
}
|
|
switch (opcode) {
|
|
case SET_OP_ID:
|
|
ret = env_set(&zeb, saved_arg);
|
|
break;
|
|
case UNSET_OP_ID:
|
|
ret = env_unset(&zeb, saved_arg);
|
|
break;
|
|
case RESET_OP_ID:
|
|
ret = env_reset(&zeb);
|
|
break;
|
|
case LIST_OP_ID:
|
|
ret = env_list(&zeb);
|
|
break;
|
|
default:
|
|
fprintf(stderr,
|
|
"%s: Missing command\nUse '%s --help' for more information\n",
|
|
argv[0], argv[0]);
|
|
break;
|
|
}
|
|
out:
|
|
if (zeb.need_update)
|
|
ret = envblk_update(&zeb);
|
|
|
|
envblk_close(&zeb);
|
|
free(saved_arg);
|
|
free(bootmap_dir);
|
|
free(site_id);
|
|
free(eff_site_id);
|
|
if (ret) {
|
|
error_print();
|
|
return EXIT_FAILURE;
|
|
}
|
|
return EXIT_SUCCESS;
|
|
}
|