diff --git a/zipl/include/envblk.h b/zipl/include/envblk.h index 4f552413..07bf6fe1 100644 --- a/zipl/include/envblk.h +++ b/zipl/include/envblk.h @@ -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); diff --git a/zipl/src/envblk.c b/zipl/src/envblk.c index f2a85864..c4839f75 100644 --- a/zipl/src/envblk.c +++ b/zipl/src/envblk.c @@ -19,6 +19,7 @@ #include #include +#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; } diff --git a/zipl/src/zipl-editenv.c b/zipl/src/zipl-editenv.c index 91e0f3d2..a55e9df6 100644 --- a/zipl/src/zipl-editenv.c +++ b/zipl/src/zipl-editenv.c @@ -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;