Index: sys/boot/forth/menu.rc =================================================================== --- sys/boot/forth/menu.rc +++ sys/boot/forth/menu.rc @@ -65,6 +65,11 @@ set mainmenu_keycode[6]=111 set mainansi_caption[6]="Configure Boot ^[1mO^[mptions..." +set mainmenu_caption[7]="Select Boot [E]nvironment..." +set mainmenu_command[7]="3 goto_menu" +set mainmenu_keycode[7]=101 +set mainansi_caption[7]="Select Boot ^[1mE^[37mnvironment..." + \ \ BOOT OPTIONS MENU \ @@ -116,6 +121,53 @@ set optionsansi_caption[6]="^[1mV^[merbose..... ^[34;1mOff^[m" set optionstoggled_ansi[6]="^[1mV^[merbose..... ^[32;7mOn^[m" +\ +\ BOOT ENVIRONMENT MENU +\ + +set menuset_name3="bootenv" + +set bootenvmenu_caption[1]="Selected: " +set bootenvmenu_command[1]="1 goto_menu" +set bootenvmenu_keycode[1]=8 +set bootenvansi_caption[1]="^[1mSelected: ^[37m" + +set bemenu_current="Selected: " +set beansi_current="^[1m${bemenu_current}^[37m" +: init_bootenv ( -- ) + s" set menu_caption[1]=${bemenu_current}${vfs.root.mountfrom}" evaluate + s" set ansi_caption[1]=${beansi_current}${vfs.root.mountfrom}" evaluate +; + +set bootenvmenu_init="init_bootenv" +unset bootenvmenu_caption[1] +unset bootenvansi_caption[1] + +: set_bootenv ( N -- N TRUE ) + dup s" set vfs.root.mountfrom=${bootenv_root[E]}" 38 +c! evaluate + s" set currdev=${vfs.root.mountfrom}:" evaluate + s" /boot/defaults/loader.conf" read-conf + s" /boot/loader.conf" read-conf + s" unload" evaluate + init_bootenv + clear \ Clear the screen (in screen.4th) + print_version \ print version string (bottom-right; see version.4th) + draw-beastie \ Draw FreeBSD logo at right (in beastie.4th) + draw-brand \ Draw brand.4th logo at top (in brand.4th) + menu-init \ Initialize menu and draw bounding box (in menu.4th) + menu-redraw \ Redraw menu (in menu.4th) + TRUE +; + +set bootenvmenu_caption[2]="[A]ctive: ${vfs.root.mountfrom}" +set bootenvansi_caption[2]="^[1mA^[37mctive: ${vfs.root.mountfrom}" +set bootenvmenu_keycode[2]=97 +set bootenvmenu_command[2]="set_bootenv" +set bootenv_root[2]="${zfs_be_active}" + +set bootenvmenu_options=3 +set bootenvmenu_optionstext="Boot Environments:" + \ Enable automatic booting (add ``autoboot_delay=N'' to loader.conf(5) to \ customize the timeout; default is 10-seconds) \ Index: sys/boot/i386/loader/main.c =================================================================== --- sys/boot/i386/loader/main.c +++ sys/boot/i386/loader/main.c @@ -69,6 +69,7 @@ static void isa_outb(int port, int value); void exit(int code); #ifdef LOADER_ZFS_SUPPORT +static void list_zfs_bootenv(char *currdev); static void i386_zfs_probe(void); #endif @@ -299,12 +300,41 @@ new_currdev.d_unit = 0; } +#ifdef LOADER_ZFS_SUPPORT + list_zfs_bootenv(zfs_fmtdev(&new_currdev)); +#endif + env_setenv("currdev", EV_VOLATILE, i386_fmtdev(&new_currdev), i386_setcurrdev, env_nounset); env_setenv("loaddev", EV_VOLATILE, i386_fmtdev(&new_currdev), env_noset, env_nounset); } +#ifdef LOADER_ZFS_SUPPORT +static void +list_zfs_bootenv(char *currdev) +{ + char *beroot; + + /* Remove the trailing : */ + currdev[strlen(currdev) - 1] = '\0'; + setenv("zfs_be_active", currdev, 1); + /* Do not overwrite if already set */ + setenv("vfs.root.mountfrom", currdev, 0); + /* Forward past zfs: */ + currdev = strchr(currdev, ':'); + currdev++; + /* Remove the last element (current bootenv) */ + beroot = strrchr(currdev, '/'); + beroot[0] = '\0'; + + beroot = currdev; + + if (beroot && beroot[0] != '\0') + zfs_bootenv(beroot); +} +#endif + COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot); static int @@ -358,6 +388,29 @@ command_errmsg = strerror(err); return (CMD_ERROR); } + + return (CMD_OK); +} + +COMMAND_SET(reloadbe, "reloadbe", "refresh the list of ZFS Boot Environments", + command_reloadbe); + +static int +command_reloadbe(int argc, char *argv[]) +{ + int err; + + if (argc != 2) { + command_errmsg = "wrong number of arguments"; + return (CMD_ERROR); + } + + err = zfs_bootenv(argv[1]); + if (err != 0) { + command_errmsg = strerror(err); + return (CMD_ERROR); + } + return (CMD_OK); } #endif Index: sys/boot/zfs/libzfs.h =================================================================== --- sys/boot/zfs/libzfs.h +++ sys/boot/zfs/libzfs.h @@ -62,6 +62,9 @@ char *zfs_fmtdev(void *vdev); int zfs_probe_dev(const char *devname, uint64_t *pool_guid); int zfs_list(const char *name); +int zfs_bootenv(const char *name); +int zfs_belist_add(const char *name); +int zfs_set_env(void); extern struct devsw zfs_dev; extern struct fs_ops zfs_fsops; Index: sys/boot/zfs/zfs.c =================================================================== --- sys/boot/zfs/zfs.c +++ sys/boot/zfs/zfs.c @@ -48,6 +48,10 @@ #include "zfsimpl.c" +/* Define the range of indexes to be populated with ZFS Boot Environments */ +#define ZFS_BE_FIRST 3 +#define ZFS_BE_LAST 8 + static int zfs_open(const char *path, struct open_file *f); static int zfs_write(struct open_file *f, void *buf, size_t size, size_t *resid); static int zfs_close(struct open_file *f); @@ -80,6 +84,15 @@ zap_leaf_phys_t *f_zap_leaf; /* zap leaf buffer */ }; +static int zfs_env_index; + +SLIST_HEAD(zfs_be_list, zfs_be_entry) zfs_be_head = SLIST_HEAD_INITIALIZER(zfs_be_head); +struct zfs_be_list *zfs_be_headp; +struct zfs_be_entry { + const char *name; + SLIST_ENTRY(zfs_be_entry) entries; +} *zfs_be, *zfs_be_tmp; + /* * Open a file. */ @@ -692,5 +705,107 @@ if (rv != 0) return (rv); rv = zfs_list_dataset(spa, objid); + + return (rv); +} + +int +zfs_bootenv(const char *name) +{ + static char poolname[ZFS_MAXNAMELEN]; + uint64_t objid; + spa_t *spa; + const char *dsname; + int len; + int rv; + + setenv("zfs_beroot", name, 1); + + SLIST_INIT(&zfs_be_head); + zfs_env_index = ZFS_BE_FIRST; + + len = strlen(name); + dsname = strchr(name, '/'); + if (dsname != NULL) { + len = dsname - name; + dsname++; + } else + dsname = ""; + memcpy(poolname, name, len); + poolname[len] = '\0'; + + spa = spa_find_by_name(poolname); + if (!spa) + return (ENXIO); + rv = zfs_lookup_dataset(spa, dsname, &objid); + if (rv != 0) + return (rv); + rv = zfs_callback_dataset(spa, objid, zfs_belist_add); + + /* Populate the environment */ + zfs_set_env(); + + /* Clean up the SLIST of ZFS BEs */ + while (!SLIST_EMPTY(&zfs_be_head)) { + zfs_be = SLIST_FIRST(&zfs_be_head); + SLIST_REMOVE_HEAD(&zfs_be_head, entries); + free(zfs_be); + } + return (rv); } + +int +zfs_belist_add(const char *name) +{ + + /* Add the boot environment to the head of the SLIST */ + zfs_be = malloc(sizeof(struct zfs_be_entry)); + zfs_be->name = name; + SLIST_INSERT_HEAD(&zfs_be_head, zfs_be, entries); + + return 0; +} + +int +zfs_set_env(void) +{ + char envname[32], envval[256]; + char *beroot; + int rv; + + beroot = getenv("zfs_beroot"); + if (beroot == NULL) + return (1); + + SLIST_FOREACH_SAFE(zfs_be, &zfs_be_head, entries, zfs_be_tmp) { + snprintf(envname, 32, "bootenvmenu_caption[%d]", zfs_env_index); + snprintf(envval, 256, "%s", zfs_be->name); + rv = setenv(envname, envval, 1); + if (rv) + return (rv); + + snprintf(envname, 32, "bootenvansi_caption[%d]", zfs_env_index); + rv = setenv(envname, envval, 1); + if (rv) + return (rv); + + snprintf(envname, 32, "bootenvmenu_command[%d]", zfs_env_index); + rv = setenv(envname, "set_bootenv", 1); + if (rv) + return (rv); + + snprintf(envname, 32, "bootenv_root[%d]", zfs_env_index); + snprintf(envval, 256, "zfs:%s/%s", beroot, zfs_be->name); + rv = setenv(envname, envval, 1); + if (rv) + return (rv); + + if (zfs_env_index >= ZFS_BE_LAST) + break; + + zfs_env_index++; + } + + return (rv); +} \ No newline at end of file Index: sys/boot/zfs/zfsimpl.c =================================================================== --- sys/boot/zfs/zfsimpl.c +++ sys/boot/zfs/zfsimpl.c @@ -1473,7 +1473,7 @@ * the directory contents. */ static int -mzap_list(const dnode_phys_t *dnode) +mzap_list(const dnode_phys_t *dnode, int (*callback)(const char *)) { const mzap_phys_t *mz; const mzap_ent_phys_t *mze; @@ -1492,7 +1492,7 @@ mze = &mz->mz_chunk[i]; if (mze->mze_name[0]) //printf("%-32s 0x%jx\n", mze->mze_name, (uintmax_t)mze->mze_value); - printf("%s\n", mze->mze_name); + callback(mze->mze_name); } return (0); @@ -1503,7 +1503,7 @@ * the directory header. */ static int -fzap_list(const spa_t *spa, const dnode_phys_t *dnode) +fzap_list(const spa_t *spa, const dnode_phys_t *dnode, int (*callback)(const char *)) { int bsize = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT; zap_phys_t zh = *(zap_phys_t *) zap_scratch; @@ -1566,13 +1566,21 @@ value = fzap_leaf_value(&zl, zc); //printf("%s 0x%jx\n", name, (uintmax_t)value); - printf("%s\n", name); + callback((const char *)name); } } return (0); } +static int zfs_printf(const char *name) +{ + + printf("%s\n", name); + + return (0); +} + /* * List a zap directory. */ @@ -1587,9 +1595,9 @@ zap_type = *(uint64_t *) zap_scratch; if (zap_type == ZBT_MICRO) - return mzap_list(dnode); + return mzap_list(dnode, zfs_printf); else - return fzap_list(spa, dnode); + return fzap_list(spa, dnode, zfs_printf); } static int @@ -1858,6 +1866,43 @@ return (zap_list(spa, &child_dir_zap) != 0); } + +int +zfs_callback_dataset(const spa_t *spa, uint64_t objnum, int (*callback)(const char *name)) +{ + uint64_t dir_obj, child_dir_zapobj, zap_type; + dnode_phys_t child_dir_zap, dir, dataset; + dsl_dataset_phys_t *ds; + dsl_dir_phys_t *dd; + + if (objset_get_dnode(spa, &spa->spa_mos, objnum, &dataset)) { + printf("ZFS: can't find dataset %ju\n", (uintmax_t)objnum); + return (EIO); + } + ds = (dsl_dataset_phys_t *) &dataset.dn_bonus; + dir_obj = ds->ds_dir_obj; + + if (objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir)) { + printf("ZFS: can't find dirobj %ju\n", (uintmax_t)dir_obj); + return (EIO); + } + dd = (dsl_dir_phys_t *)&dir.dn_bonus; + + child_dir_zapobj = dd->dd_child_dir_zapobj; + if (objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj, &child_dir_zap) != 0) { + printf("ZFS: can't find child zap %ju\n", (uintmax_t)dir_obj); + return (EIO); + } + + if (dnode_read(spa, &child_dir_zap, 0, zap_scratch, child_dir_zap.dn_datablkszsec * 512)) + return (EIO); + + zap_type = *(uint64_t *) zap_scratch; + if (zap_type == ZBT_MICRO) + return mzap_list(&child_dir_zap, callback); + else + return fzap_list(spa, &child_dir_zap, callback); +} #endif /*