zipl-editenv: added zIPL multienvironment support

. Support multiple namespaces in the installed environment block;
. Add option --site (-S) to specify a particular namespace
  when operating on the installed environment block;
. Add option --effective-site (-E) to specify a particular
  namespace. When using in a combination with the option -l
  (--list), it displays zIPL environment that would take place if
  the specified namespace was activated at boot time;
. Modify set/unset/reset/list operations over the installed
  environment block in the case when the option --site is specified

Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
Eduard Shishkin
2022-02-14 11:17:19 +01:00
committed by Jan Höppner
parent c6443cf977
commit e97c85bb80
3 changed files with 669 additions and 229 deletions

View File

@@ -22,13 +22,36 @@
#define ENVBLK_MAX_IMPORT_SIZE(envblk_size) \
(envblk_size - sizeof(ZIPL_ENVBLK_SIGNATURE) + 1)
/* get name by prefixed name PNAME and by logical prefix P */
static inline const char *get_name(const char *p, const char *pname)
{
return p ? pname + strlen(p) : pname;
}
/* get name length by prefixed name PNAME and by logical prefix P */
static inline size_t get_name_len(const char *p, const char *pname)
{
return p ? strlen(pname) - strlen(p) : strlen(pname);
}
int envblk_offset_get(int fd, off_t *off);
int envblk_offset_set(int fd, off_t off);
int envblk_size_get(int fd, int *size);
int envblk_check_name(char *name, int len);
int envblk_check_name(const char *name, int len);
int envblk_import(char *from, char *to, int size);
char *envblk_next_line(char *s, const char *end);
int envblk_scan(char *envblk, unsigned int envblk_size,
void (*actor)(char *name));
int (*actor)(char *name, void *data), void *data);
void envblk_print(char *envblk, unsigned int envblk_size);
void envblk_create_blank(char *envblk, int envblk_len);
void envblk_blank(char *envblk, int envblk_len);
int envblk_list_site(char *envblk, unsigned int envblk_size, int site_id);
int envblk_list_effective_site(char *envblk, unsigned int envblk_size,
int site_id);
int envblk_list_all(char *envblk, unsigned int envblk_size, int indent);
int envblk_set(char *envblk, unsigned int envblk_size, const char *name,
const char *new_val);
int envblk_unset(char *envblk, int envblk_len, const char *pname,
const char *site_id);
int envblk_remove_namespace(char *envblk, unsigned int envblk_size,
const char *site_id);

View File

@@ -19,6 +19,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include "lib/util_libc.h"
#include "bootmap.h"
#include "envblk.h"
#include "error.h"
@@ -29,14 +30,28 @@
#define FIGETBSZ _IO(0x00, 2)
#define KEYWORD_SIZE strlen("[site X]\n")
#define MAX_NR_SITES 10
#define LINE_HASH_SIZE 16
#define PARAM_SIZE 8
struct iter_info {
struct line_hash_entry **table;
const char *site_id;
char *envblk;
int envblk_len;
int indent;
};
void envblk_blank(char *envblk, int envblk_len)
{
memset(envblk + sizeof(ZIPL_ENVBLK_SIGNATURE) - 1, '\0',
envblk_len - sizeof(ZIPL_ENVBLK_SIGNATURE) + 1);
}
void envblk_create_blank(char *envblk, int envblk_len)
{
memcpy(envblk, ZIPL_ENVBLK_SIGNATURE,
sizeof(ZIPL_ENVBLK_SIGNATURE) - 1);
memset(envblk + sizeof(ZIPL_ENVBLK_SIGNATURE) - 1, '\0',
envblk_len - sizeof(ZIPL_ENVBLK_SIGNATURE) + 1);
envblk_blank(envblk, envblk_len);
}
/**
@@ -92,16 +107,16 @@ int envblk_size_get(int fd, int *result)
return 0;
}
#define LINE_HASH_SIZE (16)
/*
* This defines "extended string", an object consisting of
* an optional prefix and a substring
* an optional logical prefix and a substring
*/
struct estr {
char *prefix; /* pointer to the prefix */
char *str; /* pointer to the substring */
int len; /* length of the substring */
char *prefix; /* pointer to the logical prefix */
char *str; /* pointer to the substring */
int hashed_len; /* number of bytes in the substring,
* participating in hash calculation
*/
};
/**
@@ -109,8 +124,8 @@ struct estr {
*/
static unsigned int hash_fn(struct estr *estr)
{
int len = estr->hashed_len;
char *str = estr->str;
int len = estr->len;
unsigned int sum;
sum = 0;
@@ -129,17 +144,17 @@ static unsigned int hash_fn(struct estr *estr)
static int cmp_fn(char *str, struct estr *estr)
{
if (!estr->prefix)
return strncmp(str, estr->str, estr->len);
return strncmp(str, estr->str, estr->hashed_len);
if (str[0] != *estr->prefix)
return -1;
return strncmp(str + 1, estr->str, estr->len);
return strncmp(str + 1, estr->str, estr->hashed_len);
}
struct line_hash_entry {
struct line_hash_entry *next;
char *line; /* pointer to an "environment line",
* a string containing NAME=VALUE with
* optional trailing end-of-line symbol
char *line; /* pointer to a string containing a pair 'KEY=VALUE'
* with an optional prefix and an optional trailing
* new line character
*/
};
@@ -188,6 +203,8 @@ static struct line_hash_entry *hash_table_find(struct line_hash_entry **table,
{
struct line_hash_entry *item;
if (!table)
return NULL;
item = table[hash_fn(estr)];
while (item) {
if (!cmp_fn(item->line, estr))
@@ -231,38 +248,47 @@ static int hash_entry_replace(struct line_hash_entry *item, struct estr *estr,
* Scan hash table TABLE, and call ACTOR for each its entry.
* The ACTOR is allowed even to release the entry.
*/
static void hash_table_scan(struct line_hash_entry **table,
void (*actor)(struct line_hash_entry *entry))
static int hash_table_scan(struct line_hash_entry **table,
int (*actor)(struct line_hash_entry *entry,
void *data), void *data)
{
int ret;
int i;
if (!table)
return 0;
for (i = 0; i < LINE_HASH_SIZE; i++) {
struct line_hash_entry *item = table[i];
while (item) {
struct line_hash_entry *next = item->next;
actor(item);
ret = actor(item, data);
if (ret)
return ret;
item = next;
}
}
return 0;
}
static void hash_entry_free(struct line_hash_entry *this)
static int hash_entry_free(struct line_hash_entry *this,
__attribute__ ((unused))void *data)
{
free(this->line);
free(this);
return 0;
}
static void hash_table_free(struct line_hash_entry **table)
{
hash_table_scan(table, hash_entry_free);
hash_table_scan(table, hash_entry_free, NULL);
}
/**
* Scan hash table TABLE, and for each its entry copy its line to the
* in-memory environment block specified by DST at the offset OFF.
* Insert end-of-line symbol, if needed. The caller should guarantee
* Insert a new line character, if needed. The caller should guarantee
* enough space in the buffer DST
*/
static void hash_table_flush(struct line_hash_entry **table, char *dst,
@@ -321,30 +347,46 @@ static int ignore_tail(const char *tail)
* represents a valid keyword (KW_VALID is returned), then SITE_ID
* contains ID of the namespace represented by that section.
*
* LINE: a null-terminated string with optional end-of-line symbol
* LINE: a null-terminated string with optional new line character
* LEN: number of characters in the LINE excluding NULL-termination
*/
static enum keyword_status check_keyword(char *line, size_t len,
char *site_id, long *val)
static enum keyword_status check_keyword(char *line, size_t len, char **site_id)
{
char *endptr;
long val;
if (len < KEYWORD_SIZE || line[len - 1] != '\n')
if (len < KEYWORD_SIZE - 1 || line[len - 1] != '\n')
return KW_UNKNOWN;
if ((line[0] == '[') &&
(line[1] == 's' || line[1] == 'S') &&
(line[2] == 'i' || line[2] == 'I') &&
(line[3] == 't' || line[3] == 'T') &&
(line[4] == 'e' || line[4] == 'E') &&
(line[5] == ' ') && isdigit(line[6])) {
*site_id = line[6];
*val = strtol(&line[6], &endptr, 10);
(line[4] == 'e' || line[4] == 'E')) {
if (line[5] != ' ')
return KW_MALFORMED;
*site_id = &line[6];
val = strtol(&line[6], &endptr, 10);
if (endptr == &line[6])
/* not a number */
return KW_MALFORMED;
if (val < 0) {
/* negative number */
*endptr = '\0';
return KW_UNSUPP_ID;
}
if (*endptr == ']') {
if (endptr - &line[6] > PARAM_SIZE ||
!ignore_tail(endptr + 1))
if (line[6] == '0' && endptr - &line[6] > 1) {
/* leading zeros are not allowed */
*endptr = '\0';
return KW_UNSUPP_ID;
}
if (!ignore_tail(endptr + 1))
/* unacceptable trailing characters */
return KW_MALFORMED;
return *val < MAX_NR_SITES ? KW_VALID : KW_UNSUPP_ID;
*endptr = '\0';
return val < MAX_NR_SITES ? KW_VALID : KW_UNSUPP_ID;
}
return KW_MALFORMED;
}
@@ -357,7 +399,7 @@ static enum keyword_status check_keyword(char *line, size_t len,
*
* Return 0, if it meets, and 1 otherwise
*/
int envblk_check_name(char *name, int len)
int envblk_check_name(const char *name, int len)
{
int i;
@@ -372,7 +414,28 @@ int envblk_check_name(char *name, int len)
}
/**
* Put a zIPL environment line 'NAME=VALUE' to a namespace referenced by NS_ID
* Acceps a string STR, containing a pair 'KEY=VALUE'.
* Returns length of the KEY.
*/
static int get_key_len(const char *str)
{
return strchr(str, '=') - str;
}
/**
* Acceps a string STR, containing a pair 'KEY=VALUE'.
* Returns number of characters in the string, participating in hash
* calculation.
*/
static int get_hashed_len(const char *str)
{
/* hash is calculated by the key with a trailing '=' */
return get_key_len(str) + 1;
}
/**
* Put a line containing a pair 'NAME=VALUE' to a namespace referenced
* by NS_ID
*
* If the line was successfully imported without replacement a line with
* the same NAME, that was added before, then return 0.
@@ -404,7 +467,7 @@ static int import_one_line(char *this, struct line_hash_entry **table,
}
estr.prefix = ns_id;
estr.str = this;
estr.len = p - this + 1;
estr.hashed_len = get_hashed_len(this);
item = hash_table_find(table, &estr);
return item != NULL ? hash_entry_replace(item, &estr, offset) :
@@ -415,7 +478,7 @@ static int import_one_line(char *this, struct line_hash_entry **table,
* Import environment line-by-line from a regular file specified
* by its name FROM_FILE to a pre-allocated buffer TO_BUF. Ignore
* malformed, or "commented" lines. If the file doesn't end with
* end-of-line symbol, then append that symbol in the destination
* new line character, then append that character in the destination
* buffer. Return 0 on success.
*
* ENVBLK_SIZE: size of the destination buffer TO_BUF.
@@ -427,7 +490,7 @@ int envblk_import(char *from_file, char *to_buf, int envblk_size)
unsigned int lines_imported = 0;
char *line = NULL;
char site_id = 0;
long site_id_val;
char *site_idp;
size_t len = 0;
FILE *stream;
ssize_t read;
@@ -438,7 +501,7 @@ int envblk_import(char *from_file, char *to_buf, int envblk_size)
if (stream == NULL)
return 0;
memset(table, 0, LINE_HASH_SIZE*sizeof(void *));
memset(table, 0, sizeof(table));
offset = sizeof(ZIPL_ENVBLK_SIGNATURE) - 1;
while ((read = getline(&line, &len, stream)) != -1) {
@@ -453,12 +516,13 @@ int envblk_import(char *from_file, char *to_buf, int envblk_size)
/* malformed line. Ignored */
continue;
switch (check_keyword(line, read, &site_id, &site_id_val)) {
switch (check_keyword(line, read, &site_idp)) {
case KW_VALID:
site_id = *site_idp;
continue;
case KW_MALFORMED:
/*
* any line not ended with line feed character
* any line not ended with a new line character
* has to be evaluated as KW_UNKNOWN
*/
assert(line[read - 1] == '\n');
@@ -468,8 +532,8 @@ int envblk_import(char *from_file, char *to_buf, int envblk_size)
ret = -EINVAL;
goto error;
case KW_UNSUPP_ID:
error_reason("%s %s: unsupported site ID '%li'",
err_prefix, from_file, site_id_val);
error_reason("%s %s: unsupported site ID '%s'",
err_prefix, from_file, site_idp);
ret = -EINVAL;
goto error;
case KW_UNKNOWN:
@@ -479,9 +543,9 @@ int envblk_import(char *from_file, char *to_buf, int envblk_size)
if (line[read - 1] != '\n' && read == envblk_size - offset) {
/*
* file's tail doesn't contain end-of-line symbol at
* file's tail doesn't contain a new line charactere at
* the end, whereas there is no enough space in the
* destination buffer to insert that symbol, which is
* destination buffer to insert that character, which is
* mandatory according to the environment block format
*/
error_reason("%s %s: maximal size (%d) exceeded",
@@ -536,12 +600,13 @@ char *envblk_next_line(char *s, const char *end)
* Return -1 on corrupted environment blocks. Otherwise, return 0
*/
int envblk_scan(char *envblk, unsigned int envblk_size,
void (*actor)(char *name))
int (*actor)(char *name, void *data), void *data)
{
unsigned int lines_scanned = 0;
const char *reason;
char *s, *end;
char *name;
int ret;
s = envblk + sizeof(ZIPL_ENVBLK_SIGNATURE) - 1;
end = envblk + envblk_size;
@@ -570,8 +635,10 @@ int envblk_scan(char *envblk, unsigned int envblk_size,
goto corrupted;
}
*s = '\0';
actor(name);
ret = actor(name, data);
*s = '\n';
if (ret)
return ret;
lines_scanned++;
s = envblk_next_line(s, end);
}
@@ -582,13 +649,400 @@ corrupted:
return -1;
}
static void print_name_value(char *this)
static const char *indent_by_size(int size)
{
printf(" %s\n", this);
switch (size) {
case 1:
return " ";
case 2:
return " ";
default:
return "";
}
}
/**
* Put a line containing a pair 'NAME=VALUE' to a respective namespace.
*
* THIS: line containing a pair 'NAME=VALUE', optionally prefixed
* with one numerical character indicating namespace ID
*/
static int add_line_namespace(char *this, void *data)
{
struct line_hash_entry ***tables = data;
struct estr estr;
char *endptr;
int val;
val = strtol(this, &endptr, 10);
if (endptr == this)
val = MAX_NR_SITES;
if (!tables[val]) {
tables[val] =
util_zalloc(LINE_HASH_SIZE * sizeof(*tables));
if (!tables[val])
return -ENOMEM;
}
estr.prefix = NULL;
estr.str = endptr;
estr.hashed_len = get_hashed_len(endptr);
return hash_table_add(tables[val], &estr);
}
static int hash_table_print_line(struct line_hash_entry *item, void *data)
{
struct iter_info *ii = data;
printf("%s%s\n", indent_by_size(ii->indent), item->line);
return 0;
}
static int hash_table_print_line_cond(struct line_hash_entry *item, void *data)
{
struct iter_info *ii = data;
struct estr estr;
estr.prefix = NULL;
estr.str = item->line;
estr.hashed_len = get_hashed_len(item->line);
if (!hash_table_find(ii->table, &estr))
hash_table_print_line(item, data);
return 0;
}
static int print_namespace(struct line_hash_entry **table, const char *title,
int indent)
{
struct iter_info ii;
if (!table)
return 0;
printf("%s%s", indent_by_size(indent), title);
ii.table = table;
ii.indent = indent + 1;
hash_table_scan(table, hash_table_print_line, &ii);
return 0;
}
/**
* Dump content of the environment block in human-readable form,
* sorting it by "sections"
*/
int envblk_list_all(char *envblk, unsigned int envblk_size, int indent)
{
struct line_hash_entry **tables[MAX_NR_SITES + 1];
int nr_sites = 0;
int ret = 0;
int i;
memset(tables, 0, sizeof(tables));
/*
* Scan environment block and sort the pairs 'NAME=VALUE'
* by namespaces
*/
ret = envblk_scan(envblk, envblk_size, add_line_namespace, tables);
if (ret)
goto out;
for (i = 0; i < MAX_NR_SITES; i++) {
if (tables[i])
nr_sites++;
}
/* print common namespace */
print_namespace(tables[MAX_NR_SITES],
nr_sites ? "Common variables:\n" : "",
nr_sites ? indent : indent - 1);
/* print site-specific namespaces */
for (i = 0; i < MAX_NR_SITES; i++) {
char title[10];
sprintf(title, "Site %d:\n", i);
print_namespace(tables[i], title, indent);
}
out:
for (i = 0; i <= MAX_NR_SITES; i++) {
hash_table_free(tables[i]);
free(tables[i]);
}
return ret;
}
/**
* Print environment determined by effective SITE_ID
*/
int envblk_list_effective_site(char *envblk, unsigned int envblk_size, int site_id)
{
struct line_hash_entry **tables[MAX_NR_SITES + 1];
struct iter_info ii;
int ret = 0;
int i;
memset(tables, 0, sizeof(tables));
/*
* Scan environment block and sort out the pairs 'NAME=VALUE'
* by namespaces
*/
ret = envblk_scan(envblk, envblk_size, add_line_namespace, tables);
if (ret)
goto out;
/*
* print all variables of the common namespace, which are not
* defined in the SITE_ID namespace
*/
ii.table = tables[site_id];
ii.indent = 0;
hash_table_scan(tables[MAX_NR_SITES], hash_table_print_line_cond,
&ii);
/*
* print all variables of the SITE_ID namespace
*/
ii.table = NULL;
ii.indent = 0;
hash_table_scan(tables[site_id], hash_table_print_line, &ii);
out:
for (i = 0; i <= MAX_NR_SITES; i++) {
hash_table_free(tables[i]);
free(tables[i]);
}
return ret;
}
/**
* List all variables defined by the namespace with SITE_ID
*/
int envblk_list_site(char *envblk, unsigned int envblk_size, int site_id)
{
struct line_hash_entry **tables[MAX_NR_SITES + 1];
struct iter_info ii;
int ret = 0;
int i;
memset(tables, 0, sizeof(tables));
/*
* Scan environment block and sort out the pairs 'NAME=VALUE'
* by namespaces
*/
ret = envblk_scan(envblk, envblk_size, add_line_namespace, tables);
if (ret)
goto out;
ii.table = NULL;
ii.indent = 0;
hash_table_scan(tables[site_id], hash_table_print_line, &ii);
out:
for (i = 0; i <= MAX_NR_SITES; i++) {
hash_table_free(tables[i]);
free(tables[i]);
}
return ret;
}
void envblk_print(char *envblk, unsigned int envblk_size)
{
printf("Environment block content:\n");
envblk_scan(envblk, envblk_size, print_name_value);
printf("zIPL environment block content:\n");
envblk_list_all(envblk, envblk_size, 1 /* indent */);
}
int envblk_set(char *envblk, unsigned int envblk_size, const char *name,
const char *new_val)
{
unsigned int name_len, new_val_len;
char *fss; /* free space start */
unsigned int lines_scanned = 0;
int name_found = 0;
char *s, *end;
name_len = strlen(name);
new_val_len = strlen(new_val);
s = envblk + sizeof(ZIPL_ENVBLK_SIGNATURE) - 1;
end = envblk + envblk_size;
/*
* find the start of free space
*/
for (fss = end - 1; *fss == '\0'; fss--)
;
if (*fss != '\n') {
error_reason("Found corrupted environment block - please run zipl");
return -1;
}
fss++;
while (fss - s > name_len) {
if (lines_scanned >= ENVBLK_MAX_LINES) {
error_reason("Found corrupted environment block - please run zipl");
return -1;
}
if (memcmp(s, name, name_len) == 0 && s[name_len] == '=') {
unsigned int cur_val_len;
/*
* such name exists, replace its current value
*/
s += (name_len + 1);
cur_val_len = 0;
while (s + cur_val_len < end && s[cur_val_len] != '\n')
cur_val_len++;
if (s + cur_val_len >= end) {
error_reason("Found corrupted environment block - please run zipl");
return -1;
}
if (new_val_len > cur_val_len &&
end - fss < new_val_len - cur_val_len) {
error_reason("Not enough space for new value");
return -1;
}
/*
* make a precise-sized room for the new value
*/
if (new_val_len < cur_val_len) {
memmove(s + new_val_len, s + cur_val_len,
end - (s + cur_val_len));
memset(fss + cur_val_len - new_val_len, '\0',
cur_val_len - new_val_len);
} else {
memmove(s + new_val_len, s + cur_val_len,
end - (s + new_val_len));
}
name_found = 1;
break;
}
lines_scanned++;
s = envblk_next_line(s, end);
}
assert(lines_scanned <= ENVBLK_MAX_LINES);
if (!name_found) {
if (lines_scanned == ENVBLK_MAX_LINES) {
error_reason("Maximum number of lines reached");
return -1;
}
/*
* append a new variable
*/
if (end - fss < name_len + new_val_len + 2) {
error_reason("Not enough space in environment block");
return -1;
}
memcpy(fss, name, name_len);
s = fss + name_len;
*s++ = '=';
}
/*
* copy the new value and terminate it with a new line character
*/
memcpy(s, new_val, new_val_len);
s[new_val_len] = '\n';
return 0;
}
int envblk_unset(char *envblk, int envblk_len,
const char *pname, const char *site_id)
{
unsigned int pname_len;
char *s, *end;
pname_len = strlen(pname);
s = envblk + sizeof(ZIPL_ENVBLK_SIGNATURE) - 1;
end = envblk + envblk_len;
while (end - s >= pname_len + 2 /* minimal length of
* pattern "name=foo"
*/) {
if (memcmp(s, pname, pname_len) == 0 && s[pname_len] == '=') {
/*
* prefixed name was found. Locate the whole
* "named" line and cut it including
* the trailing "\n"
*/
unsigned int cut_len = pname_len + 1;
while (s + cut_len < end) {
if (s[cut_len] == '\n')
break;
cut_len++;
}
if (s + cut_len >= end) {
/*
* trailing "\n" not found
*/
error_reason("Found corrupted environment block - please run zipl");
return -1;
}
cut_len++;
memmove(s, s + cut_len, end - (s + cut_len));
memset(end - cut_len, '\0', cut_len);
return 0;
}
s = envblk_next_line(s, end);
}
error_reason("Name '%s' not found in %s%s namespace",
get_name(site_id, pname),
site_id ? "site " : "common",
site_id ?: "");
return -1;
}
static int remove_name_value(struct line_hash_entry *item, void *data)
{
struct iter_info *ii = data;
char *pname;
int len;
int ret;
/*
* construct a prefixed null-terminated name by a line 'KEY=VALUE'
*/
len = get_key_len(item->line);
pname = misc_malloc(len + 2);
if (!pname) {
error_reason(strerror(errno));
return -ENOMEM;
}
pname[0] = *ii->site_id;
memcpy(pname + 1, item->line, len);
pname[len + 1] = '\0';
ret = envblk_unset(ii->envblk,
strnlen(ii->envblk, ii->envblk_len),
pname, ii->site_id);
free(pname);
return ret;
}
/**
* Remove all lines prefixed with SITE_ID from environment block
*/
int envblk_remove_namespace(char *envblk, unsigned int envblk_size,
const char *site_id)
{
struct line_hash_entry **tables[MAX_NR_SITES + 1];
struct iter_info ii;
int ret = 0;
int i;
memset(tables, 0, sizeof(tables));
/*
* Scan environment block and populate hash tables
* with the pairs 'NAME=VALUE'
*/
ret = envblk_scan(envblk, envblk_size, add_line_namespace, tables);
if (ret)
goto out;
ii.table = tables[atoi(site_id)];
ii.envblk_len = envblk_size;
ii.envblk = envblk;
ii.site_id = site_id;
ii.indent = 0;
ret = hash_table_scan(tables[atoi(site_id)], remove_name_value, &ii);
out:
for (i = 0; i <= MAX_NR_SITES; i++) {
hash_table_free(tables[i]);
free(tables[i]);
}
return ret;
}

View File

@@ -31,7 +31,6 @@
/* from linux/fs.h */
#define FIBMAP _IO(0x00, 1)
#define FIGETBSZ _IO(0x00, 2)
static const struct util_prg prg = {
.desc = "zipl-editenv: Environment Editor for zSeries Initial Program Loader",
@@ -46,6 +45,8 @@ static const struct util_prg prg = {
int verbose;
static char *bootmap_dir;
static char *site_id;
static char *eff_site_id;
enum op_id {
INVALID_OP_ID,
@@ -77,151 +78,19 @@ static void zipl_envblk_init(struct zipl_envblk *zeb)
zeb->fd = -1;
}
static void print_name_value(char *this)
{
printf("%s\n", this);
}
static int do_list(char *envblk, unsigned int envblk_size)
{
return envblk_scan(envblk, envblk_size, print_name_value);
}
assert(!site_id || !eff_site_id);
static int do_set(char *envblk, unsigned int envblk_size,
const char *name, const char *new_val)
{
unsigned int name_len, new_val_len;
char *fss; /* free space start */
unsigned int lines_scanned = 0;
int name_found = 0;
char *s, *end;
name_len = strlen(name);
new_val_len = strlen(new_val);
s = envblk + sizeof(ZIPL_ENVBLK_SIGNATURE) - 1;
end = envblk + envblk_size;
/*
* find the start of free space
*/
for (fss = end - 1; *fss == '\0'; fss--)
;
if (*fss != '\n') {
error_reason("Found corrupted environment block - please run zipl");
return -1;
}
fss++;
while (fss - s > name_len) {
if (lines_scanned >= ENVBLK_MAX_LINES) {
error_reason("Found corrupted environment block - please run zipl");
return -1;
}
if (memcmp(s, name, name_len) == 0 && s[name_len] == '=') {
unsigned int cur_val_len;
/*
* such name exists, replace its current value
*/
s += (name_len + 1);
cur_val_len = 0;
while (s + cur_val_len < end && s[cur_val_len] != '\n')
cur_val_len++;
if (s + cur_val_len >= end) {
error_reason("Found corrupted environment block - please run zipl");
return -1;
}
if (new_val_len > cur_val_len &&
end - fss < new_val_len - cur_val_len) {
error_reason("Not enough space for new value");
return -1;
}
/*
* make a precise-sized room for the new value
*/
if (new_val_len < cur_val_len) {
memmove(s + new_val_len, s + cur_val_len,
end - (s + cur_val_len));
memset(fss + cur_val_len - new_val_len, '\0',
cur_val_len - new_val_len);
} else
memmove(s + new_val_len, s + cur_val_len,
end - (s + new_val_len));
name_found = 1;
break;
}
lines_scanned++;
s = envblk_next_line(s, end);
}
assert(lines_scanned <= ENVBLK_MAX_LINES);
if (!name_found) {
if (lines_scanned == ENVBLK_MAX_LINES) {
error_reason("Maximum number of lines reached");
return -1;
}
/*
* append a new variable
*/
if (end - fss < name_len + new_val_len + 2) {
error_reason("Not enough space in environment block");
return -1;
}
memcpy(fss, name, name_len);
s = fss + name_len;
*s++ = '=';
}
/*
* copy the new value and terminate it with a new line symbol
*/
memcpy(s, new_val, new_val_len);
s[new_val_len] = '\n';
return 0;
}
static int do_unset(char *envblk, int envblk_len, const char *name)
{
unsigned int name_len;
char *s, *end;
name_len = strlen(name);
s = envblk + sizeof(ZIPL_ENVBLK_SIGNATURE) - 1;
end = envblk + envblk_len;
while (end - s >= name_len + 2 /* minimal length of
* pattern "name=foo"
*/) {
if (memcmp(s, name, name_len) == 0 && s[name_len] == '=') {
/*
* name was found. Locate the whole
* "named" line and cut it including
* the trailing "\n"
*/
unsigned int cut_len = name_len + 1;
while (s + cut_len < end) {
if (s[cut_len] == '\n')
break;
cut_len++;
}
if (s + cut_len >= end) {
/*
* trailing "\n" not found
*/
error_reason("Found corrupted environment block - please run zipl");
return -1;
}
cut_len++;
memmove(s, s + cut_len, end - (s + cut_len));
memset(end - cut_len, '\0', cut_len);
return 0;
}
s = envblk_next_line(s, end);
}
error_reason("Name %s not found", name);
return -1;
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);
}
/**
@@ -376,6 +245,21 @@ error:
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))
@@ -385,19 +269,22 @@ static int env_list(struct zipl_envblk *zeb)
static int env_set(struct zipl_envblk *zeb, char *arg)
{
char *name, *value;
char *pname;
char *value;
if (envblk_open(zeb))
return -1;
/*
* parse the argument, locate name and value
*/
name = strdup(arg);
if (!name)
return -1;
value = strchr(name, '=');
if (!value || value == name) {
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;
@@ -405,50 +292,58 @@ static int env_set(struct zipl_envblk *zeb, char *arg)
*value = '\0';
value++;
if (envblk_check_name(name, strlen(name))) {
error_reason("Unacceptable name '%s'", name);
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 (do_set(zeb->buf, zeb->size, name, value) == 0) {
free(name);
if (envblk_set(zeb->buf, zeb->size, pname, value) == 0) {
free(pname);
zeb->need_update = 1;
return 0;
}
error:
free(name);
free(pname);
return -1;
}
static int env_unset(struct zipl_envblk *zeb, char *name)
static int env_unset(struct zipl_envblk *zeb, char *arg)
{
char *pname;
if (envblk_open(zeb))
return -1;
/*
* check name for validness
*/
if (strchr(name, '=') != NULL) {
error_reason("Invalid argument %s", name);
if (strchr(arg, '=') != NULL) {
error_reason("Invalid argument %s", arg);
return -1;
}
if (do_unset(zeb->buf, strnlen(zeb->buf, zeb->size), name) == 0) {
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 void env_blank(char *envblk, int envblk_len)
{
memset(envblk + sizeof(ZIPL_ENVBLK_SIGNATURE) - 1, '\0',
envblk_len - sizeof(ZIPL_ENVBLK_SIGNATURE) + 1);
}
static int env_reset(struct zipl_envblk *zeb)
{
if (envblk_open(zeb))
return -1;
env_blank(zeb->buf, zeb->size);
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;
}
@@ -480,6 +375,57 @@ static int save_op_arg(enum op_id *dst, enum op_id src,
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);
}
static struct util_opt opt_vec[] = {
UTIL_OPT_SECTION("OPTIONS WITHOUT ARGUMENTS"),
{
@@ -500,6 +446,16 @@ static struct util_opt opt_vec[] = {
.argument = "DIR",
.desc = "specify directory, where bootmap file is located",
},
{
.option = { "site", required_argument, NULL, 'S'},
.argument = "SITE",
.desc = "specify site ID",
},
{
.option = { "effective-site", required_argument, NULL, 'E'},
.argument = "SITE",
.desc = "specify effective site ID",
},
{
.option = { "set", required_argument, NULL, 's'},
.argument = "NAME=VALUE",
@@ -528,12 +484,6 @@ int main(int argc, char *argv[])
util_prg_init(&prg);
util_opt_init(opt_vec, NULL);
if (argc < 2) {
fprintf(stderr,
"%s: Missing command\nUse 'zipl-editenv --help' for more information\n",
argv[0]);
return EXIT_FAILURE;
}
while (1) {
c = util_opt_getopt_long(argc, argv);
if (c == -1)
@@ -550,7 +500,15 @@ int main(int argc, char *argv[])
verbose = 1;
break;
case 't':
bootmap_dir = optarg;
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);
@@ -594,6 +552,9 @@ int main(int argc, char *argv[])
ret = env_list(&zeb);
break;
default:
fprintf(stderr,
"%s: Missing command\nUse '%s --help' for more information\n",
argv[0], argv[0]);
break;
}
out:
@@ -601,8 +562,10 @@ out:
ret = envblk_update(&zeb);
envblk_close(&zeb);
if (saved_arg != NULL)
free(saved_arg);
free(saved_arg);
free(bootmap_dir);
free(site_id);
free(eff_site_id);
if (ret) {
error_print();
return EXIT_FAILURE;