Changeset View
Changeset View
Standalone View
Standalone View
sbin/zfsbootcfg/zfsbootcfg.c
Show All 26 Lines | |||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <sys/types.h> | #include <sys/types.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
#include <limits.h> | #include <limits.h> | ||||
#include <inttypes.h> | #include <inttypes.h> | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <stdbool.h> | |||||
#include <string.h> | #include <string.h> | ||||
#include <kenv.h> | #include <kenv.h> | ||||
#include <unistd.h> | |||||
#include <libzfs.h> | #include <libzfsbootenv.h> | ||||
/* Keep in sync with zfsboot.c. */ | #ifndef ZFS_MAXNAMELEN | ||||
#define MAX_COMMAND_LEN 512 | #define ZFS_MAXNAMELEN 256 | ||||
#endif | |||||
int | static int | ||||
install_bootonce(libzfs_handle_t *hdl, uint64_t pool_guid, nvlist_t *nv, | add_pair(const char *name, const char *nvlist, const char *key, | ||||
const char * const data) | const char *type, const char *value) | ||||
imp: nit: int should be on a line by itself...
| |||||
{ | { | ||||
nvlist_t **child; | void *data, *nv; | ||||
uint_t children = 0; | size_t size; | ||||
uint64_t guid; | |||||
int rv; | int rv; | ||||
char *end; | |||||
(void) nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child, | rv = lzbe_nvlist_get(name, nvlist, &nv); | ||||
&children); | if (rv != 0) | ||||
return (rv); | |||||
for (int c = 0; c < children; c++) { | data = NULL; | ||||
rv = install_bootonce(hdl, pool_guid, child[c], data); | rv = EINVAL; | ||||
if (strcmp(type, "DATA_TYPE_STRING") == 0) { | |||||
data = __DECONST(void *, value); | |||||
Done Inline ActionsI think for backwards compatibility, we need to make print the default if 0 args are specified so running just 'zfsbootcfg' prints the currently selected bootenv (this is what it did before our changes) allanjude: I think for backwards compatibility, we need to make print the default if 0 args are specified… | |||||
Done Inline ActionsActually it did not, it did require argc == 2 and only wrote the argument string to pad2:) but the idea is still OK tsoome: Actually it did not, it did require argc == 2 and only wrote the argument string to pad2:) but… | |||||
size = strlen(data) + 1; | |||||
rv = lzbe_add_pair(nv, key, type, data, size); | |||||
} else if (strcmp(type, "DATA_TYPE_UINT64") == 0) { | |||||
uint64_t v; | |||||
v = strtoull(value, &end, 0); | |||||
if (errno != 0 || *end != '\0') | |||||
goto done; | |||||
size = sizeof (v); | |||||
rv = lzbe_add_pair(nv, key, type, &v, size); | |||||
} else if (strcmp(type, "DATA_TYPE_INT64") == 0) { | |||||
int64_t v; | |||||
v = strtoll(value, &end, 0); | |||||
if (errno != 0 || *end != '\0') | |||||
goto done; | |||||
size = sizeof (v); | |||||
rv = lzbe_add_pair(nv, key, type, &v, size); | |||||
} else if (strcmp(type, "DATA_TYPE_UINT32") == 0) { | |||||
uint32_t v; | |||||
v = strtoul(value, &end, 0); | |||||
if (errno != 0 || *end != '\0') | |||||
goto done; | |||||
size = sizeof (v); | |||||
rv = lzbe_add_pair(nv, key, type, &v, size); | |||||
} else if (strcmp(type, "DATA_TYPE_INT32") == 0) { | |||||
int32_t v; | |||||
v = strtol(value, &end, 0); | |||||
if (errno != 0 || *end != '\0') | |||||
goto done; | |||||
size = sizeof (v); | |||||
rv = lzbe_add_pair(nv, key, type, &v, size); | |||||
} else if (strcmp(type, "DATA_TYPE_UINT16") == 0) { | |||||
uint16_t v; | |||||
v = strtoul(value, &end, 0); | |||||
if (errno != 0 || *end != '\0') | |||||
goto done; | |||||
size = sizeof (v); | |||||
rv = lzbe_add_pair(nv, key, type, &v, size); | |||||
} else if (strcmp(type, "DATA_TYPE_INT16") == 0) { | |||||
int16_t v; | |||||
v = strtol(value, &end, 0); | |||||
if (errno != 0 || *end != '\0') | |||||
goto done; | |||||
size = sizeof (v); | |||||
rv = lzbe_add_pair(nv, key, type, &v, size); | |||||
} else if (strcmp(type, "DATA_TYPE_UINT8") == 0) { | |||||
uint8_t v; | |||||
v = strtoul(value, &end, 0); | |||||
if (errno != 0 || *end != '\0') | |||||
goto done; | |||||
size = sizeof (v); | |||||
rv = lzbe_add_pair(nv, key, type, &v, size); | |||||
} else if (strcmp(type, "DATA_TYPE_INT8") == 0) { | |||||
int8_t v; | |||||
v = strtol(value, &end, 0); | |||||
if (errno != 0 || *end != '\0') | |||||
goto done; | |||||
size = sizeof (v); | |||||
rv = lzbe_add_pair(nv, key, type, &v, size); | |||||
} else if (strcmp(type, "DATA_TYPE_BYTE") == 0) { | |||||
uint8_t v; | |||||
v = strtoul(value, &end, 0); | |||||
if (errno != 0 || *end != '\0') | |||||
goto done; | |||||
size = sizeof (v); | |||||
rv = lzbe_add_pair(nv, key, type, &v, size); | |||||
} else if (strcmp(type, "DATA_TYPE_BOOLEAN_VALUE") == 0) { | |||||
int32_t v; | |||||
v = strtol(value, &end, 0); | |||||
if (errno != 0 || *end != '\0') { | |||||
if (strcasecmp(value, "YES") == 0) | |||||
v = 1; | |||||
else if (strcasecmp(value, "NO") == 0) | |||||
v = 0; | |||||
if (strcasecmp(value, "true") == 0) | |||||
v = 1; | |||||
else if (strcasecmp(value, "false") == 0) | |||||
v = 0; | |||||
else goto done; | |||||
} | } | ||||
size = sizeof (v); | |||||
rv = lzbe_add_pair(nv, key, type, &v, size); | |||||
} | |||||
if (children > 0) | if (rv == 0) | ||||
rv = lzbe_nvlist_set(name, nvlist, nv); | |||||
done: | |||||
lzbe_nvlist_free(nv); | |||||
return (rv); | return (rv); | ||||
} | |||||
if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) != 0) { | static int | ||||
perror("can't get vdev guid"); | delete_pair(const char *name, const char *nvlist, const char *key) | ||||
return (1); | { | ||||
void *nv; | |||||
int rv; | |||||
rv = lzbe_nvlist_get(name, nvlist, &nv); | |||||
if (rv == 0) { | |||||
rv = lzbe_remove_pair(nv, key); | |||||
} | } | ||||
if (zpool_nextboot(hdl, pool_guid, guid, data) != 0) { | if (rv == 0) | ||||
perror("ZFS_IOC_NEXTBOOT failed"); | rv = lzbe_nvlist_set(name, nvlist, nv); | ||||
return (1); | |||||
lzbe_nvlist_free(nv); | |||||
return (rv); | |||||
} | } | ||||
return (0); | |||||
} | |||||
int main(int argc, const char * const *argv) | /* | ||||
* Usage: zfsbootcfg [-z pool] [-d key] [-k key -t type -v value] [-p] | |||||
* zfsbootcfg [-z pool] -n nvlist [-d key] [-k key -t type -v value] [-p] | |||||
* | |||||
* if nvlist is set, we will update nvlist in bootenv. | |||||
* if nvlist is not set, we update pairs in bootenv. | |||||
*/ | |||||
int | |||||
main(int argc, char * const *argv) | |||||
{ | { | ||||
char buf[32], *name; | char buf[ZFS_MAXNAMELEN], *name; | ||||
libzfs_handle_t *hdl; | const char *key, *value, *type, *nvlist; | ||||
zpool_handle_t *zphdl; | |||||
uint64_t pool_guid; | |||||
nvlist_t *nv, *config; | |||||
int rv; | int rv; | ||||
int len; | bool print, delete; | ||||
if (argc != 2) { | nvlist = NULL; | ||||
fprintf(stderr, "usage: zfsbootcfg <boot.config(5) options>\n"); | name = NULL; | ||||
return (1); | key = NULL; | ||||
type = NULL; | |||||
value = NULL; | |||||
print = delete = false; | |||||
while ((rv = getopt(argc, argv, "d:k:n:pt:v:z:")) != -1) { | |||||
switch (rv) { | |||||
case 'd': | |||||
delete = true; | |||||
key = optarg; | |||||
break; | |||||
case 'k': | |||||
key = optarg; | |||||
break; | |||||
case 'n': | |||||
nvlist = optarg; | |||||
break; | |||||
case 'p': | |||||
print = true; | |||||
break; | |||||
case 't': | |||||
type = optarg; | |||||
break; | |||||
case 'v': | |||||
value = optarg; | |||||
break; | |||||
case 'z': | |||||
name = optarg; | |||||
break; | |||||
} | } | ||||
} | |||||
len = strlen(argv[1]); | argc -= optind; | ||||
if (len >= MAX_COMMAND_LEN) { | argv += optind; | ||||
fprintf(stderr, "options string is too long\n"); | |||||
if (argc == 1) | |||||
value = argv[0]; | |||||
if (argc > 1) { | |||||
fprintf(stderr, "usage: zfsbootcfg <boot.config(5) options>\n"); | |||||
return (1); | return (1); | ||||
} | } | ||||
if (kenv(KENV_GET, "vfs.root.mountfrom", buf, sizeof(buf)) <= 0) { | if (name == NULL) { | ||||
rv = kenv(KENV_GET, "vfs.root.mountfrom", buf, sizeof(buf)); | |||||
if (rv <= 0) { | |||||
perror("can't get vfs.root.mountfrom"); | perror("can't get vfs.root.mountfrom"); | ||||
return (1); | return (1); | ||||
} | } | ||||
if (strncmp(buf, "zfs:", 4) == 0) { | if (strncmp(buf, "zfs:", 4) == 0) { | ||||
name = strchr(buf + 4, '/'); | name = strchr(buf + 4, '/'); | ||||
if (name != NULL) | if (name != NULL) | ||||
*name = '\0'; | *name = '\0'; | ||||
name = buf + 4; | name = buf + 4; | ||||
} else { | } else { | ||||
perror("not a zfs root"); | perror("not a zfs root"); | ||||
return (1); | return (1); | ||||
} | } | ||||
if ((hdl = libzfs_init()) == NULL) { | |||||
(void) fprintf(stderr, "internal error: failed to " | |||||
"initialize ZFS library\n"); | |||||
return (1); | |||||
} | } | ||||
zphdl = zpool_open(hdl, name); | rv = 0; | ||||
if (zphdl == NULL) { | if (key != NULL || value != NULL) { | ||||
perror("can't open pool"); | if (type == NULL) | ||||
libzfs_fini(hdl); | type = "DATA_TYPE_STRING"; | ||||
return (1); | |||||
} | |||||
pool_guid = zpool_get_prop_int(zphdl, ZPOOL_PROP_GUID, NULL); | if (delete) | ||||
rv = delete_pair(name, nvlist, key); | |||||
else if (key == NULL || strcmp(key, "command") == 0) | |||||
rv = lzbe_set_boot_device(name, value); | |||||
else | |||||
rv = add_pair(name, nvlist, key, type, value); | |||||
config = zpool_get_config(zphdl, NULL); | if (rv == 0) | ||||
if (config == NULL) { | printf("zfs bootenv is successfully written\n"); | ||||
perror("can't get pool config"); | else | ||||
zpool_close(zphdl); | printf("error: %d\n", rv); | ||||
libzfs_fini(hdl); | } else if (!print) { | ||||
return (1); | char *ptr; | ||||
if (lzbe_get_boot_device(name, &ptr) == 0) { | |||||
printf("zfs:%s:\n", ptr); | |||||
free(ptr); | |||||
} | } | ||||
} | |||||
if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nv) != 0) { | if (print) { | ||||
perror("failed to get vdev tree"); | rv = lzbe_bootenv_print(name, nvlist, stdout); | ||||
zpool_close(zphdl); | |||||
libzfs_fini(hdl); | |||||
return (1); | |||||
} | } | ||||
rv = install_bootonce(hdl, pool_guid, nv, argv[1]); | |||||
zpool_close(zphdl); | |||||
libzfs_fini(hdl); | |||||
if (rv == 0) | |||||
printf("zfs next boot options are successfully written\n"); | |||||
return (rv); | return (rv); | ||||
} | } |
nit: int should be on a line by itself...