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]="Back to Main Menu [Backspace]" +set bootenvmenu_command[1]="1 goto_menu" +set bootenvmenu_keycode[1]=8 +set bootenvansi_caption[1]="Back to Main Menu ^[1m[Backspace]^[37m" + +set bootenvmenu_caption[2]="Choice: " +set bootenvmenu_command[2]="true" +set bootenvmenu_keycode[2]=27 +set bootenvansi_caption[2]="^[1m${bootenvmenu_caption[2]}^[37m" + +set bemenu_current="Choice: " +set beansi_current="^[1m${bemenu_current}^[37m" +: init_bootenv ( -- ) + s" set menu_caption[2]=${bemenu_current}${vfs.root.mountfrom}" evaluate + s" set ansi_caption[2]=${beansi_current}${vfs.root.mountfrom}" evaluate +; + +set bootenvmenu_init="init_bootenv" +unset bootenvmenu_caption[2] +unset bootenvansi_caption[2] + +: 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 + menu-refresh + TRUE +; + +set bootenvmenu_caption[3]="[A]ctive: ${vfs.root.mountfrom}" +set bootenvansi_caption[3]="^[1mA^[37mctive: ${vfs.root.mountfrom}" +set bootenvmenu_keycode[3]=97 +set bootenvmenu_command[3]="set_bootenv" +set bootenv_root[3]="${zfs_be_active}" + +set bootenvmenu_options=4 +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/forth/shortcuts.4th =================================================================== --- sys/boot/forth/shortcuts.4th +++ sys/boot/forth/shortcuts.4th @@ -48,3 +48,16 @@ menu-init \ Initialize menu and draw bounding box (in menu.4th) menu-display \ Launch interactive menu (in menu.4th) ; + +\ This "shortcut" word will fully redraw the current menu. +\ The regular menu-redraw word does not clear the screen, and if there is +\ any excess output, the menu will be redrawn incorrectly +\ +: menu-refresh ( -- ) + 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) +; 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 @@ -360,6 +390,27 @@ } 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 /* ISA bus access functions for PnP. */ Index: sys/boot/zfs/libzfs.h =================================================================== --- sys/boot/zfs/libzfs.h +++ sys/boot/zfs/libzfs.h @@ -62,6 +62,8 @@ 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_set_env(const char *name); 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 @@ -80,6 +80,8 @@ zap_leaf_phys_t *f_zap_leaf; /* zap leaf buffer */ }; +static int zfs_env_index = 0; + /* * Open a file. */ @@ -694,3 +696,93 @@ 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; + char *index_str; + int len; + int rv; + + setenv("zfs_beroot", name, 1); + + zfs_env_index = 4; + 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_set_env); + if (rv != 0) + return (rv); + index_str = malloc(20); + snprintf(index_str, 20, "%d", zfs_env_index - 1); + rv = setenv("bootenv_count", index_str, 1); + return (rv); +} + +int +zfs_set_env(const char *name) +{ + char *envname = NULL, *envval = NULL, *beroot; + int rv, i; + + beroot = getenv("zfs_beroot"); + if (beroot == NULL) + return (1); + + envname = malloc(32); + envval = malloc(256); + /* Don't overflow the menu, shuffle entries down to show the newest */ + if (zfs_env_index == 9) { + for (i = 4; i < 8; i++) { + snprintf(envname, 32, "bootenvmenu_caption[%d]", i); + snprintf(envval, 32, "bootenvmenu_caption[%d]", i + 1); + setenv(envname, getenv(envval), 1); + + snprintf(envname, 32, "bootenvansi_caption[%d]", i); + snprintf(envval, 32, "bootenvansi_caption[%d]", i + 1); + setenv(envname, getenv(envval), 1); + + snprintf(envname, 32, "bootenv_root[%d]", i); + snprintf(envval, 32, "bootenv_root[%d]", i + 1); + setenv(envname, getenv(envval), 1); + } + zfs_env_index = 8; + } + + snprintf(envname, 32, "bootenvmenu_caption[%d]", zfs_env_index); + snprintf(envval, 256, "%s", name); + rv = setenv(envname, envval, 1); + + snprintf(envname, 32, "bootenvansi_caption[%d]", zfs_env_index); + rv = setenv(envname, envval, 1); + + snprintf(envname, 32, "bootenvmenu_command[%d]", zfs_env_index); + rv = setenv(envname, "set_bootenv", 1); + + snprintf(envname, 32, "bootenv_root[%d]", zfs_env_index); + snprintf(envval, 256, "zfs:%s/%s", beroot, name); + rv = setenv(envname, envval, 1); + + if (zfs_env_index <= 8) + zfs_env_index++; + free(envname); + free(envval); + 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,19 @@ 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 +1593,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 +1864,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 /*