1 () () 2 3 4 5 This file and its contents are supplied under the terms of the Common 6 Development and Distribution License ("CDDL"), version 1.0. You may only use 7 this file in accordance with the terms of version 1.0 of the CDDL. A full 8 copy of the text of the CDDL should have accompanied this source. A copy of 9 the CDDL is also available via the Internet at 10 http://www.illumos.org/license/CDDL. 11 12 Copyright 2017 Toomas Soome <tsoome@me.com> Copyright 2019 OmniOS Community 13 Edition (OmniOSce) Association. Copyright 2019 Joyent, Inc. 14 15 This module is implementing the beadm user command to support listing and 16 switching Boot Environments (BE) from command line and support words to 17 provide data for BE menu in loader menu system. Note: this module needs an 18 update to provide proper BE vocabulary. 19 20 only forth also support-functions also file-processing also file-processing 21 definitions also parser also line-reading definitions also builtins 22 definitions 23 24 variable page_count variable page_remainder 0 page_count ! 0 page_remainder ! 25 26 from menu.4th : +c! ( N C-ADDR/U K -- C-ADDR/U ) 3 pick 3 pick ( n c- 27 addr/u k -- n c-addr/u k n c-addr ) rot + c! ( n c-addr/u k n c-addr -- n 28 c-addr/u ) rot drop ( n c-addr/u -- c-addr/u ) ; 29 30 : get_value ( -- ) eat_space line_pointer skip_to_end_of_line 31 line_pointer over - strdup value_buffer strset ['] exit to 32 parsing_function ; 33 34 : get_name ( -- ) read_name ['] get_value to parsing_function ; 35 36 : get_name_value line_buffer strget + to end_of_line line_buffer .addr 37 @ to line_pointer ['] get_name to parsing_function begin 38 end_of_line? 0= while parsing_function execute 39 repeat ; 40 41 beadm support : beadm_longest_title ( addr len -- width ) 0 to end_of_file? 42 O_RDONLY fopen fd ! reset_line_reading fd @ -1 = if EOPEN throw 43 then 0 >r length into return stack begin end_of_file? 44 0= while free_buffers read_line get_name_value 45 value_buffer .len @ r@ > if r> drop value_buffer .len @ >r then 46 free_buffers read_line repeat fd @ fclose r> 1 + 47 space between columns ; 48 49 Pretty print BE list : beadm_list ( width addr len -- ) 0 to end_of_file? 50 O_RDONLY fopen fd ! reset_line_reading fd @ -1 = if EOPEN throw 51 then ." BE" dup 2 - spaces ." Type Device" cr begin 52 end_of_file? 0= while free_buffers read_line 53 get_name_value value_buffer strget type dup 54 value_buffer .len @ - spaces free_buffers read_line 55 get_name_value name_buffer strget type name_buffer 56 strget s" bootfs" compare 0= if 2 spaces then name_buffer strget s" 57 chain" compare 0= if 3 spaces then value_buffer strget type cr 58 free_buffers repeat fd @ fclose drop ; 59 60 \[u00A0]we are called with strings be_name menu_file, to simplify the stack 61 management, we open the menu and free the menu_file. : beadm_bootfs ( be_addr 62 be_len maddr mlen -- addr len taddr tlen flag | flag ) 0 to end_of_file? 63 2dup O_RDONLY fopen fd ! drop free-memory fd @ -1 = if EOPEN 64 throw then reset_line_reading begin end_of_file? 0= while 65 free_buffers read_line get_name_value 66 2dup value_buffer strget compare 0= if ( title == be ) 67 2drop drop be_name free_buffers 68 read_line get_name_value 69 value_buffer strget strdup name_buffer strget 70 strdup -1 free_buffers 1 to end_of_file? mark end 71 of file to skip the rest else read_line skip over 72 next line then repeat fd @ fclose line_buffer strfree 73 read_buffer strfree dup -1 > if ( be_addr be_len ) 2drop 74 0 then ; 75 76 : current-dev ( -- addr len ) return current dev s" currdev" getenv 77 2dup [char] / strchr nip dup 0> if ( strchr '/' != NULL ) - else 78 drop then we have now zfs:pool or diskname: ; 79 80 chop trailing ':' : colon- ( addr len -- addr len - 1 | addr len ) 2dup 1 - 81 + C@ [char] : = if ( string[len-1] == ':' ) 1 - then ; 82 83 add trailing ':' : colon+ ( addr len -- addr len+1 ) 2dup + 84 addr len -- addr+len [char] : swap c! save ':' at the 85 end of the string 1+ addr len -- addr len+1 ; 86 87 make menu.lst path : menu.lst ( addr len -- addr' len' ) colon- need to 88 allocate space for len + 16 dup 16 + allocate if ENOMEM throw then swap 89 2dup 2>R copy of new addr len to return stack move 2R> s" 90 :/boot/menu.lst" strcat ; 91 92 list be's on device : list-dev ( addr len -- ) menu.lst 2dup 2>R 93 beadm_longest_title line_buffer strfree read_buffer strfree 94 R@ swap 2R> addr width addr len beadm_list free-memory ." 95 Current boot device: " s" currdev" getenv type cr line_buffer strfree 96 read_buffer strfree ; 97 98 activate be on device. if be name was not given, set currdev otherwize, we 99 query device:/boot/menu.lst for bootfs and if found, and bootfs type is 100 chain, attempt chainload. set currdev to bootfs. if we were able to set 101 currdev, reload the config 102 103 : activate-dev ( dev.addr dev.len be.addr be.len -- ) 104 105 dup 0= if 2drop colon- remove : at the 106 end of the dev name dup 1+ allocate if ENOMEM throw then dup 107 2swap 0 -rot strcat colon+ s" currdev" setenv setenv 108 currdev = device free-memory else 2swap menu.lst 109 beadm_bootfs if ( addr len taddr tlen ) 2dup s" 110 chain" compare 0= if drop free-memory free type 111 2dup dup 6 + allocate if ENOMEM throw 112 then dup >R 0 s" chain " strcat 113 2swap strcat ['] evaluate catch drop We 114 are still there? R> free-memory free chain command 115 drop free-memory free addr exit 116 then drop free-memory free type 117 check last char in the name 2dup + c@ [char] : <> 118 if have dataset and need to get zfs:pool/ROOT/be: 119 dup 5 + allocate if ENOMEM throw then 0 120 s" zfs:" strcat 2swap strcat colon+ 121 then 2dup s" currdev" setenv drop 122 free-memory else ." No such BE in menu.lst or menu.lst 123 is missing." cr exit then then 124 125 reset BE menu 0 page_count ! need to do: 0 unload drop 126 free-module-options unset the env variables with kernel arguments 127 s" acpi-user-options" unsetenv s" boot-args" unsetenv s" boot_ask" 128 unsetenv s" boot_single" unsetenv s" boot_verbose" unsetenv s" 129 boot_kmdb" unsetenv s" boot_drop_into_kmdb" unsetenv s" 130 boot_reconfigure" unsetenv start load config, kernel and 131 modules ." Current boot device: " s" currdev" getenv type cr ; 132 133 beadm list [device] beadm activate BE [device] | device lists BE's from 134 current or specified device /boot/menu.lst file activates specified BE by 135 unloading modules, setting currdev and running start to load configuration. 136 : beadm ( -- ) ( throws: abort ) 0= if ( interpreted ) get_arguments then 137 138 dup 0= if ." Usage:" cr ." beadm activate {beName 139 [device] | device}" cr ." beadm list [device]" cr ." Use lsdev 140 to get device names." cr drop exit then First argument is 141 0 when we're interprated. See support.4th for get_arguments reading the 142 rest of the line and parsing it stack: argN lenN ... arg1 len1 N rotate 143 arg1 len1, dont use argv[] as we want to get arg1 out of stack -rot 2dup 144 145 s" list" compare-insensitive 0= if ( list ) 2drop argc 1 = 146 if ( list currdev ) add dev to list of args and switch to case 147 2 current-dev rot 1 + then 2 = if ( list 148 device ) list-dev exit then ." too many arguments" cr abort then 149 s" activate" compare-insensitive 0= if ( activate ) argc 1 = if ( 150 missing be ) drop ." missing bName" cr abort then 151 argc 2 = if ( activate be ) need to set arg list into 152 proper order 1+ >R save argc+1 to return stack 153 154 if the prefix is fd, cd, net or disk and we have : 155 in the name, it is device and inject empty be name 156 over 2 s" fd" compare 0= >R over 2 s" cd" compare 157 0= R> or >R over 3 s" net" compare 0= R> or >R 158 over 4 s" disk" compare 0= R> or if ( prefix is fd 159 or cd or net or disk ) 2dup [char] : strchr nip 160 if ( its : in name ) true 161 else false 162 then else false 163 then 164 165 if ( it is device name ) 0 0 R> 166 else add device, swap with be and receive 167 argc current-dev 2swap R> then 168 then 3 = if ( activate be device ) activate-dev exit then 169 ." too many arguments" cr abort then ." Unknown argument" cr 170 abort ; 171 172 also forth definitions also builtins 173 174 make beadm available as user command. builtin: beadm 175 176 count the pages of BE list leave FALSE in stack in case of error : be-pages 177 ( -- flag ) 1 local flag 0 0 2local currdev 0 0 2local title end- 178 locals 179 180 current-dev menu.lst 2dup 2>R 0 to end_of_file? O_RDONLY fopen fd 181 ! 2R> drop free-memory reset_line_reading fd @ -1 = if FALSE else 182 s" currdev" getenv over ( addr len addr ) 4 183 s" zfs:" compare 0= if 5 - len -= 5 184 swap 4 + addr += 4 swap to currdev 185 then 186 187 0 begin end_of_file? 0= while 188 read_line get_name_value s" 189 title" name_buffer strget compare 0= if 1+ then 190 191 flag if check for title value_buffer 192 strget strdup to title free_buffers read_line get 193 bootfs get_name_value value_buffer 194 strget currdev compare 0= if title s" zfs_be_active" 195 setenv 0 to flag then 196 title drop free-memory 0 0 to title 197 free_buffers else 198 free_buffers read_line get bootfs 199 then repeat fd @ fclose line_buffer 200 strfree read_buffer strfree 5 /mod swap dup page_remainder ! 201 save remainder if 1+ then dup page_count ! 202 save count n2s s" zfs_be_pages" setenv 203 TRUE then ; 204 205 : be-set-page { | entry count n device -- } page_count @ 0= if be-pages 206 page_count @ 0= if exit then then 207 208 0 to device 1 s" zfs_be_currpage" getenvn 5 * page_count @ 5 * 209 page_remainder @ if 5 page_remainder @ - - then swap - 210 dup to entry 0 < if entry 5 + to count 0 to entry 211 else 5 to count then current-dev menu.lst 2dup 2>R 212 0 to end_of_file? O_RDONLY fopen fd ! 2R> drop free-memory 213 reset_line_reading fd @ -1 = if EOPEN throw then 0 to n begin 214 end_of_file? 0= while n entry < if 215 read_line skip title read_line skip 216 bootfs n 1+ to n else Use reverse loop 217 to display descending order for BE list. 0 count 218 1- do read_line read title line 219 get_name_value value_buffer strget 220 52 i + ascii 4 + i s" 221 bootenvmenu_caption[4]" 20 +c! setenv value_buffer strget 222 52 i + ascii 4 + i s" 223 bootenvansi_caption[4]" 20 +c! setenv 224 225 free_buffers read_line read value 226 line get_name_value 227 228 set menu entry command name_buffer strget 229 s" chain" compare 0= if s" 230 set_be_chain" else s" set_bootenv" 231 then 52 i + ascii 4 + i 232 s" bootenvmenu_command[4]" 20 +c! setenv 233 234 set device name name_buffer strget s" 235 chain" compare 0= if \[u00A0]for 236 chain, use the value as is value_buffer strget 237 else check last char in the name 238 value_buffer strget 2dup + c@ 239 [char] : <> if make 240 zfs device name swap drop 241 5 + allocate if 242 ENOMEM throw 243 then s" zfs:" ( 244 addr addr' len' ) 2 pick swap move ( addr ) 245 dup to device 4 246 value_buffer strget strcat ( addr len ) 247 s" :" strcat then 248 then 249 250 52 i + ascii 4 + i s" 251 bootenv_root[4]" 13 +c! setenv device free-memory 0 to 252 device free_buffers -1 +loop 253 254 5 count do unset unused entries 52 255 i + ascii 4 + i dup s" bootenvmenu_caption[4]" 20 256 +c! unsetenv dup s" bootenvansi_caption[4]" 20 +c! unsetenv 257 dup s" bootenvmenu_command[4]" 20 +c! unsetenv 258 s" bootenv_root[4]" 13 +c! unsetenv loop 259 260 1 to end_of_file? we are done then repeat 261 fd @ fclose line_buffer strfree read_buffer strfree ; 262 263 264 265 August 28, 2019 ()