2 \ This file and its contents are supplied under the terms of the
3 \ Common Development and Distribution License ("CDDL"), version 1.0.
4 \ You may only use this file in accordance with the terms of version
7 \ A full copy of the text of the CDDL should have accompanied this
8 \ source. A copy of the CDDL is also available via the Internet at
9 \ http://www.illumos.org/license/CDDL.
11 \ Copyright 2015 Toomas Soome <tsoome@me.com>
13 \ This module is implementing the beadm user command to support listing
14 \ and switching Boot Environments (BE) from command line and
15 \ support words to provide data for BE menu in loader menu system.
16 \ Note: this module needs an update to provide proper BE vocabulary.
18 only forth also support-functions also file-processing
19 also file-processing definitions also parser
20 also line-reading definitions also builtins definitions
23 variable page_remainder
28 : +c! ( N C-ADDR/U K -- C-ADDR/U )
29 3 pick 3 pick ( n c-addr/u k -- n c-addr/u k n c-addr )
30 rot + c! ( n c-addr/u k n c-addr -- n c-addr/u )
31 rot drop ( n c-addr/u -- c-addr/u )
39 strdup value_buffer strset
40 ['] exit to parsing_function
45 ['] get_value to parsing_function
49 line_buffer strget + to end_of_line
50 line_buffer .addr @ to line_pointer
51 ['] get_name to parsing_function
55 parsing_function execute
60 : beadm_longest_title ( addr len -- width )
64 fd @ -1 = if EOPEN throw then
65 0 >r \ length into return stack
72 value_buffer .len @ r@ > if r> drop value_buffer .len @ >r then
77 r> 1 + \ space between columns
80 \ Pretty print BE list
81 : beadm_list ( width addr len -- )
85 fd @ -1 = if EOPEN throw then
86 ." BE" dup 2 - spaces ." bootfs" cr
93 value_buffer strget type
94 dup value_buffer .len @ - spaces
98 value_buffer strget type cr
105 : beadm_bootfs ( be_addr be_len menu_addr menu_len -- addr len flag )
109 fd @ -1 = if EOPEN throw then
117 2dup value_buffer strget compare
118 0= if ( title == be )
123 value_buffer strget strdup -1
125 1 to end_of_file? \ mark end of file to skip the rest
127 read_line \ skip over next line
133 dup -1 > if ( dev_addr dev_len )
139 : current-dev ( -- addr len ) \ return current dev
141 2dup [char] / strchr nip
142 dup 0> if ( strchr '/' != NULL ) - else drop then
143 \ we have now zfs:pool or diskname:
147 : colon- ( addr len -- addr len - 1 | addr len )
148 2dup 1 - + C@ [char] : = if ( string[len-1] == ':' ) 1 - then
152 : colon+ ( addr len -- addr len+1 )
153 2dup + \ addr len -- addr+len
154 [char] : swap c! \ save ':' at the end of the string
155 1+ \ addr len -- addr len+1
159 : menu.lst ( addr len -- addr' len' )
161 \ need to allocate space for len + 16
162 dup 16 + allocate if ENOMEM throw then
163 swap 2dup 2>R \ copy of new addr len to return stack
165 s" :/boot/menu.lst" strcat
168 \ list be's on device
169 : list-dev ( addr len -- )
174 R@ swap 2R> \ addr width addr len
175 beadm_list free-memory
176 ." Current boot device: " s" currdev" getenv type cr
181 \ activate be on device.
182 \ in case of zfs, we query device:/boot/menu.lst for bootfs and
183 \ use zfs:bootfs: for currdev
184 \ in case of ufs we have device name without ':', so we just
185 \ set currdev=device: and hope for best - there are no multiple BE's on ufs
187 : activate-dev ( dev.addr dev.len be.addr be.len -- )
188 2swap colon- \ remove : at the end of the dev name
189 2dup [char] : strchr nip
190 0= if ( no ':' in dev name, its ufs )
192 dup 1+ allocate if ENOMEM throw then
193 dup 2swap 0 -rot strcat
195 s" currdev" setenv \ setenv currdev = device
198 dup 16 + allocate if ENOMEM throw then
199 swap 2dup 2>R \ copy of new addr len to return stack
200 move 2R> \ copy dev name and concat file name
201 s" :/boot/menu.lst" strcat 2dup \ leave copy to stack
202 beadm_bootfs if ( dev_addr dev_len addr len )
203 2swap \ addr len dev_addr dev_len
206 \ have dataset and need to get zfs:pool/ROOT/be:
207 dup 5 + allocate if ENOMEM throw then
211 2dup s" currdev" setenv
214 2drop drop free \ free the file name
215 ." Failed to process BE/dev" cr abort
223 start \ load config, kernel and modules
224 ." Current boot device: " s" currdev" getenv type cr
227 \ beadm list [device]
228 \ beadm activate BE [device] BE
230 \ lists BE's from current or specified device /boot/menu.lst file
231 \ activates specified BE by unloading modules, setting currdev and
232 \ running start to load configuration.
233 : beadm ( -- ) ( throws: abort )
234 0= if ( interpreted ) get_arguments then
238 ." beadm activate beName [device]" cr
239 ." beadm list [device]" cr
240 ." Use lsdev to get device names." cr
243 \ First argument is 0 when we're interprated. See support.4th
244 \ for get_arguments reading the rest of the line and parsing it
245 \ stack: argN lenN ... arg1 len1 N
246 \ rotate arg1 len1, dont use argv[] as we want to get arg1 out of stack
249 s" list" compare-insensitive 0= if ( list )
251 argc 1 = if ( list currdev )
252 \ add dev to list of args and switch to case 2
255 2 = if ( list device ) list-dev exit then
256 ." too many arguments" cr abort
258 s" activate" compare-insensitive 0= if ( activate )
259 argc 1 = if ( missing be )
260 drop ." missing bName" cr abort
262 argc 2 = if ( activate be )
263 \ need to set arg list into proper order
264 1 + >R \ save argc+1 to return stack
265 \ if we have : in name, its device, inject
266 \ dummy be name, as it must be ufs device
267 2dup [char] : strchr nip
271 \ add device, swap with be and receive argc
275 3 = if ( activate be device ) activate-dev exit then
276 ." too many arguments" cr abort
278 ." Unknown argument" cr abort
281 also forth definitions also builtins
283 \ make beadm available as user command.
286 \ count the pages of BE list
287 \ leave FALSE in stack in case of error
288 : be-pages ( -- flag )
294 current-dev menu.lst 2dup 2>R
299 fd @ -1 = if FALSE else
301 over ( addr len addr )
302 4 s" zfs:" compare 0= if
314 s" title" name_buffer strget compare
317 flag if \ check for title
318 value_buffer strget strdup to title free_buffers
319 read_line \ get bootfs
321 value_buffer strget currdev compare 0= if
322 title s" zfs_be_active" setenv
325 title drop free-memory 0 0 to title
329 read_line \ get bootfs
335 5 /mod swap dup page_remainder ! \ save remainder
337 dup page_count ! \ save count
338 s>d <# #s #> s" zfs_be_pages" setenv
343 : be-set-page { | entry count n -- }
346 page_count @ 0= if exit then
349 s" zfs_be_currpage" getenv dup -1 = if
353 >number ( ud caddr/u -- ud' caddr'/u' )
358 5 page_remainder @ - -
368 current-dev menu.lst 2dup 2>R
373 fd @ -1 = if EOPEN throw then
379 read_line \ skip title
380 read_line \ skip bootfs
383 \ Use reverse loop to display descending order
386 read_line \ read title line
390 s" bootenvmenu_caption[4]" 20 +c! setenv
393 s" bootenvansi_caption[4]" 20 +c! setenv
396 s" bootenvmenu_command[4]" 20 +c! setenv
398 read_line \ read value line
401 value_buffer strget swap drop
402 5 + allocate if ENOMEM throw then
403 s" zfs:" ( N addr addr1 len )
404 2 pick swap move ( N addr )
405 swap over ( addr N addr )
407 strget ( addr N addr 4 addr1 len )
408 strcat ( addr N addr 4+len )
409 s" :" strcat ( addr N addr 5+len )
410 rot ( addr addr 5+len N )
411 s" bootenv_root[4]" 13 +c! setenv
417 5 count do \ unset unused entries
419 dup s" bootenvmenu_caption[4]" 20 +c! unsetenv
420 dup s" bootenvansi_caption[4]" 20 +c! unsetenv
421 dup s" bootenvmenu_command[4]" 20 +c! unsetenv
422 s" bootenv_root[4]" 13 +c! unsetenv
425 1 to end_of_file? \ we are done