mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
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:
committed by
Jan Höppner
parent
c6443cf977
commit
e97c85bb80
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user