beadm.4th revision 199767f8919635c4928607450d9e0abb932109ce
\
\ This file and its contents are supplied under the terms of the
\ Common Development and Distribution License ("CDDL"), version 1.0.
\ You may only use this file in accordance with the terms of version
\ 1.0 of the CDDL.
\
\ A full copy of the text of the CDDL should have accompanied this
\ source. A copy of the CDDL is also available via the Internet at
\ http://www.illumos.org/license/CDDL.
\ Copyright 2015 Toomas Soome <tsoome@me.com>
\ This module is implementing the beadm user command to support listing
\ and switching Boot Environments (BE) from command line and
\ support words to provide data for BE menu in loader menu system.
\ Note: this module needs an update to provide proper BE vocabulary.
only forth also support-functions also file-processing
also file-processing definitions also parser
also line-reading definitions also builtins definitions
variable page_count
variable page_remainder
0 page_count !
0 page_remainder !
\ from menu.4th
: +c! ( N C-ADDR/U K -- C-ADDR/U )
3 pick 3 pick ( n c-addr/u k -- n c-addr/u k n c-addr )
rot + c! ( n c-addr/u k n c-addr -- n c-addr/u )
rot drop ( n c-addr/u -- c-addr/u )
;
: get_value ( -- )
eat_space
line_pointer
skip_to_end_of_line
line_pointer over -
strdup value_buffer strset
['] exit to parsing_function
;
: get_name ( -- )
read_name
['] get_value to parsing_function
;
: get_name_value
line_buffer strget + to end_of_line
line_buffer .addr @ to line_pointer
['] get_name to parsing_function
begin
end_of_line? 0=
while
parsing_function execute
repeat
;
\ beadm support
: beadm_longest_title ( addr len -- width )
0 to end_of_file?
O_RDONLY fopen fd !
reset_line_reading
fd @ -1 = if EOPEN throw then
0 >r \ length into return stack
begin
end_of_file? 0=
while
free_buffers
read_line
get_name_value
value_buffer .len @ r@ > if r> drop value_buffer .len @ >r then
free_buffers
read_line
repeat
fd @ fclose
r> 1 + \ space between columns
;
\ Pretty print BE list
: beadm_list ( width addr len -- )
0 to end_of_file?
O_RDONLY fopen fd !
reset_line_reading
fd @ -1 = if EOPEN throw then
." BE" dup 2 - spaces ." bootfs" cr
begin
end_of_file? 0=
while
free_buffers
read_line
get_name_value
value_buffer strget type
dup value_buffer .len @ - spaces
free_buffers
read_line
get_name_value
value_buffer strget type cr
free_buffers
repeat
fd @ fclose
drop
;
: beadm_bootfs ( be_addr be_len menu_addr menu_len -- addr len flag )
0 to end_of_file?
O_RDONLY fopen fd !
reset_line_reading
fd @ -1 = if EOPEN throw then
2swap
begin
end_of_file? 0=
while
free_buffers
read_line
get_name_value
2dup value_buffer strget compare
0= if ( title == be )
2drop
free_buffers
read_line
get_name_value
value_buffer strget strdup -1
free_buffers
1 to end_of_file? \ mark end of file to skip the rest
else
read_line \ skip over next line
then
repeat
fd @ fclose
line_buffer strfree
read_buffer strfree
dup -1 > if ( dev_addr dev_len )
2drop
0 0 0
then
;
: current-dev ( -- addr len ) \ return current dev
s" currdev" getenv
2dup [char] / strchr nip
dup 0> if ( strchr '/' != NULL ) - else drop then
\ we have now zfs:pool or diskname:
;
\ chop trailing ':'
: colon- ( addr len -- addr len - 1 | addr len )
2dup 1 - + C@ [char] : = if ( string[len-1] == ':' ) 1 - then
;
\ add trailing ':'
: colon+ ( addr len -- addr len+1 )
2dup + \ addr len -- addr+len
[char] : swap c! \ save ':' at the end of the string
1+ \ addr len -- addr len+1
;
\ make menu.lst path
: menu.lst ( addr len -- addr' len' )
colon-
\ need to allocate space for len + 16
dup 16 + allocate if ENOMEM throw then
swap 2dup 2>R \ copy of new addr len to return stack
move 2R>
s" :/boot/menu.lst" strcat
;
\ list be's on device
: list-dev ( addr len -- )
menu.lst 2dup 2>R
beadm_longest_title
line_buffer strfree
read_buffer strfree
R@ swap 2R> \ addr width addr len
beadm_list free-memory
." Current boot device: " s" currdev" getenv type cr
line_buffer strfree
read_buffer strfree
;
\ activate be on device.
\ in case of zfs, we query device:/boot/menu.lst for bootfs and
\ use zfs:bootfs: for currdev
\ in case of ufs we have device name without ':', so we just
\ set currdev=device: and hope for best - there are no multiple BE's on ufs
: activate-dev ( dev.addr dev.len be.addr be.len -- )
2swap colon- \ remove : at the end of the dev name
2dup [char] : strchr nip
0= if ( no ':' in dev name, its ufs )
2swap 2drop
dup 1+ allocate if ENOMEM throw then
dup 2swap 0 -rot strcat
colon+
s" currdev" setenv \ setenv currdev = device
free-memory
else
dup 16 + allocate if ENOMEM throw then
swap 2dup 2>R \ copy of new addr len to return stack
move 2R> \ copy dev name and concat file name
s" :/boot/menu.lst" strcat 2dup \ leave copy to stack
beadm_bootfs if ( dev_addr dev_len addr len )
2swap \ addr len dev_addr dev_len
drop
free-memory
\ have dataset and need to get zfs:pool/ROOT/be:
dup 5 + allocate if ENOMEM throw then
0 s" zfs:" strcat
2swap strcat
colon+
2dup s" currdev" setenv
drop free-memory
else
2drop drop free \ free the file name
." Failed to process BE/dev" cr abort
then
then
\ need to do:
0 unload drop
free-module-options
\ unset kernel env?
start \ load config, kernel and modules
." Current boot device: " s" currdev" getenv type cr
;
\ beadm list [device]
\ beadm activate BE [device] BE
\
\ lists BE's from current or specified device /boot/menu.lst file
\ activates specified BE by unloading modules, setting currdev and
\ running start to load configuration.
: beadm ( -- ) ( throws: abort )
0= if ( interpreted ) get_arguments then
dup 0= if
." Usage:" cr
." beadm activate beName [device]" cr
." beadm list [device]" cr
." Use lsdev to get device names." cr
drop exit
then
\ First argument is 0 when we're interprated. See support.4th
\ for get_arguments reading the rest of the line and parsing it
\ stack: argN lenN ... arg1 len1 N
\ rotate arg1 len1, dont use argv[] as we want to get arg1 out of stack
-rot 2dup
s" list" compare-insensitive 0= if ( list )
2drop
argc 1 = if ( list currdev )
\ add dev to list of args and switch to case 2
current-dev rot 1 +
then
2 = if ( list device ) list-dev exit then
." too many arguments" cr abort
then
s" activate" compare-insensitive 0= if ( activate )
argc 1 = if ( missing be )
drop ." missing bName" cr abort
then
argc 2 = if ( activate be )
\ need to set arg list into proper order
1 + >R \ save argc+1 to return stack
\ if we have : in name, its device, inject
\ dummy be name, as it must be ufs device
2dup [char] : strchr nip
if ( its : in name )
s" ufs" R>
else
\ add device, swap with be and receive argc
current-dev 2swap R>
then
then
3 = if ( activate be device ) activate-dev exit then
." too many arguments" cr abort
then
." Unknown argument" cr abort
;
also forth definitions also builtins
\ make beadm available as user command.
builtin: beadm
\ count the pages of BE list
\ leave FALSE in stack in case of error
: be-pages ( -- flag )
1 local flag
0 0 2local currdev
0 0 2local title
end-locals
current-dev menu.lst 2dup 2>R
0 to end_of_file?
O_RDONLY fopen fd !
2R> drop free-memory
reset_line_reading
fd @ -1 = if FALSE else
s" currdev" getenv
over ( addr len addr )
4 s" zfs:" compare 0= if
5 - \ len -= 5
swap 4 + \ addr += 4
swap to currdev
then
0
begin
end_of_file? 0=
while
read_line
get_name_value
s" title" name_buffer strget compare
0= if 1+ then
flag if \ check for title
value_buffer strget strdup to title free_buffers
read_line \ get bootfs
get_name_value
value_buffer strget currdev compare 0= if
title s" zfs_be_active" setenv
0 to flag
then
title drop free-memory 0 0 to title
free_buffers
else
free_buffers
read_line \ get bootfs
then
repeat
fd @ fclose
line_buffer strfree
read_buffer strfree
5 /mod swap dup page_remainder ! \ save remainder
if 1+ then
dup page_count ! \ save count
s>d <# #s #> s" zfs_be_pages" setenv
TRUE
then
;
: be-set-page { | entry count n -- }
page_count @ 0= if
be-pages
page_count @ 0= if exit then
then
s" zfs_be_currpage" getenv dup -1 = if
drop s" 1"
then
0 s>d 2swap
>number ( ud caddr/u -- ud' caddr'/u' )
2drop
1 um/mod nip 5 *
page_count @ 5 *
page_remainder @ if
5 page_remainder @ - -
then
swap -
dup to entry
0 < if
entry 5 + to count
0 to entry
else
5 to count
then
current-dev menu.lst 2dup 2>R
0 to end_of_file?
O_RDONLY fopen fd !
2R> drop free-memory
reset_line_reading
fd @ -1 = if EOPEN throw then
0 to n
begin
end_of_file? 0=
while
n entry < if
read_line \ skip title
read_line \ skip bootfs
n 1+ to n
else
count 0 do
read_line \ read title line
get_name_value
value_buffer strget
52 i + \ ascii 4 + i
s" bootenvmenu_caption[4]" 20 +c! setenv
value_buffer strget
52 i + \ ascii 4 + i
s" bootenvansi_caption[4]" 20 +c! setenv
s" set_bootenv"
52 i + \ ascii 4 + i
s" bootenvmenu_command[4]" 20 +c! setenv
free_buffers
read_line \ read value line
get_name_value
52 i + \ ascii 4 + i
value_buffer strget swap drop
5 + allocate if ENOMEM throw then
s" zfs:" ( N addr addr1 len )
2 pick swap move ( N addr )
swap over ( addr N addr )
4 value_buffer
strget ( addr N addr 4 addr1 len )
strcat ( addr N addr 4+len )
s" :" strcat ( addr N addr 5+len )
rot ( addr addr 5+len N )
s" bootenv_root[4]" 13 +c! setenv
free-memory
free_buffers
loop
5 count do \ unset unused entries
52 i + \ ascii 4 + i
dup s" bootenvmenu_caption[4]" 20 +c! unsetenv
dup s" bootenvansi_caption[4]" 20 +c! unsetenv
dup s" bootenvmenu_command[4]" 20 +c! unsetenv
s" bootenv_root[4]" 13 +c! unsetenv
loop
1 to end_of_file? \ we are done
then
repeat
fd @ fclose
line_buffer strfree
read_buffer strfree
;