xref: /illumos-gate/usr/src/boot/forth/beadm.4th (revision aa88555e2aa3d01aff5e421451572bdfcf722282)
1\
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
5\ 1.0 of the CDDL.
6\
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.
10
11\ Copyright 2017 Toomas Soome <tsoome@me.com>
12\ Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
13\ Copyright 2019 Joyent, Inc.
14
15\ This module is implementing the beadm user command to support listing
16\ and switching Boot Environments (BE) from command line and
17\ support words to provide data for BE menu in loader menu system.
18\ Note: this module needs an update to provide proper BE vocabulary.
19
20only forth also support-functions also file-processing
21also file-processing definitions also parser
22also line-reading definitions also builtins definitions
23
24variable page_count
25variable page_remainder
260 page_count !
270 page_remainder !
28
29\ from menu.4th
30: +c! ( N C-ADDR/U K -- C-ADDR/U )
31	3 pick 3 pick	( n c-addr/u k -- n c-addr/u k n c-addr )
32	rot + c!	( n c-addr/u k n c-addr -- n c-addr/u )
33	rot drop	( n c-addr/u -- c-addr/u )
34;
35
36: get_value ( -- )
37	eat_space
38	line_pointer
39	skip_to_end_of_line
40	line_pointer over -
41	strdup value_buffer strset
42	['] exit to parsing_function
43;
44
45: get_name ( -- )
46	read_name
47	['] get_value to parsing_function
48;
49
50: get_name_value
51	line_buffer strget + to end_of_line
52	line_buffer .addr @ to line_pointer
53	['] get_name to parsing_function
54	begin
55		end_of_line? 0=
56	while
57		parsing_function execute
58	repeat
59;
60
61\ beadm support
62: beadm_longest_title ( addr len -- width )
63	0 to end_of_file?
64	O_RDONLY fopen fd !
65	reset_line_reading
66	fd @ -1 = if EOPEN throw then
67	0 >r		\ length into return stack
68	begin
69		end_of_file? 0=
70	while
71		free_buffers
72		read_line
73		get_name_value
74		value_buffer .len @ r@ > if r> drop value_buffer .len @ >r then
75		free_buffers
76		read_line
77	repeat
78	fd @ fclose
79	r> 1 +		\ space between columns
80;
81
82\ Pretty print BE list
83: beadm_list ( width addr len -- )
84	0 to end_of_file?
85	O_RDONLY fopen fd !
86	reset_line_reading
87	fd @ -1 = if EOPEN throw then
88	." BE" dup 2 - spaces ." Type    Device" cr
89	begin
90		end_of_file? 0=
91	while
92		free_buffers
93		read_line
94		get_name_value
95		value_buffer strget type
96		dup value_buffer .len @ - spaces
97		free_buffers
98		read_line
99		get_name_value
100		name_buffer strget type
101		name_buffer strget s" bootfs" compare 0= if 2 spaces then
102		name_buffer strget s" chain" compare 0= if 3 spaces then
103		value_buffer strget type cr
104		free_buffers
105	repeat
106	fd @ fclose
107	drop
108;
109
110\ we are called with strings be_name menu_file, to simplify the stack
111\ management, we open the menu and free the menu_file.
112: beadm_bootfs ( be_addr be_len maddr mlen -- addr len taddr tlen flag | flag )
113	0 to end_of_file?
114	2dup O_RDONLY fopen fd !
115	drop free-memory
116	fd @ -1 = if EOPEN throw then
117	reset_line_reading
118	begin
119		end_of_file? 0=
120	while
121		free_buffers
122		read_line
123		get_name_value
124		2dup value_buffer strget compare
125		0= if ( title == be )
126			2drop		\ drop be_name
127			free_buffers
128			read_line
129			get_name_value
130			value_buffer strget strdup
131			name_buffer strget strdup -1
132			free_buffers
133			1 to end_of_file? \ mark end of file to skip the rest
134		else
135			read_line	\ skip over next line
136		then
137	repeat
138	fd @ fclose
139	line_buffer strfree
140	read_buffer strfree
141	dup -1 > if ( be_addr be_len )
142		2drop
143		0
144	then
145;
146
147: current-dev ( -- addr len ) \ return current dev
148	s" currdev" getenv
149	2dup [char] / strchr nip
150	dup 0> if ( strchr '/' != NULL ) - else drop then
151	\ we have now zfs:pool or diskname:
152;
153
154\ chop trailing ':'
155: colon- ( addr len -- addr len - 1 | addr len )
156	2dup 1 - + C@ [char] : = if ( string[len-1] == ':' ) 1 - then
157;
158
159\ add trailing ':'
160: colon+ ( addr len -- addr len+1 )
161	2dup +			\ addr len -- addr+len
162	[char] : swap c!	\ save ':' at the end of the string
163	1+			\ addr len -- addr len+1
164;
165
166\ make menu.lst path
167: menu.lst ( addr len -- addr' len' )
168	colon-
169	\ need to allocate space for len + 16
170	dup 16 + allocate if ENOMEM throw then
171	swap 2dup 2>R	\ copy of new addr len to return stack
172	move 2R>
173	s" :/boot/menu.lst" strcat
174;
175
176\ list be's on device
177: list-dev ( addr len -- )
178	menu.lst 2dup 2>R
179	beadm_longest_title
180	line_buffer strfree
181	read_buffer strfree
182	R@ swap 2R>	\ addr width addr len
183	beadm_list free-memory
184	." Current boot device: " s" currdev" getenv type cr
185	line_buffer strfree
186	read_buffer strfree
187;
188
189\ activate be on device.
190\ if be name was not given, set currdev
191\ otherwize, we query device:/boot/menu.lst for bootfs and
192\ if found, and bootfs type is chain, attempt chainload.
193\ set currdev to bootfs.
194\ if we were able to set currdev, reload the config
195
196: activate-dev ( dev.addr dev.len be.addr be.len -- )
197
198	dup 0= if
199		2drop
200		colon-			\ remove : at the end of the dev name
201		dup 1+ allocate if ENOMEM throw then
202		dup 2swap 0 -rot strcat
203		colon+
204		s" currdev" setenv	\ setenv currdev = device
205		free-memory
206	else
207		2swap menu.lst
208		beadm_bootfs if ( addr len taddr tlen )
209			2dup s" chain" compare 0= if
210				drop free-memory	\ free type
211				2dup
212				dup 6 + allocate if ENOMEM throw then
213				dup >R
214				0 s" chain " strcat
215				2swap strcat ['] evaluate catch drop
216				\ We are still there?
217				R> free-memory		\ free chain command
218				drop free-memory	\ free addr
219				exit
220			then
221			drop free-memory		\ free type
222			\ check last char in the name
223			2dup + c@ [char] : <> if
224				\ have dataset and need to get zfs:pool/ROOT/be:
225				dup 5 + allocate if ENOMEM throw then
226				0 s" zfs:" strcat
227				2swap strcat
228				colon+
229			then
230			2dup s" currdev" setenv
231			drop free-memory
232		else
233			." No such BE in menu.lst or menu.lst is missing." cr
234			exit
235		then
236	then
237
238	\ reset BE menu
239	0 page_count !
240	\ need to do:
241	0 unload drop
242	free-module-options
243	\ unset the env variables with kernel arguments
244	s" acpi-user-options" unsetenv
245	s" boot-args" unsetenv
246	s" boot_ask" unsetenv
247	s" boot_single" unsetenv
248	s" boot_verbose" unsetenv
249	s" boot_kmdb" unsetenv
250	s" boot_drop_into_kmdb" unsetenv
251	s" boot_reconfigure" unsetenv
252	start			\ load config, kernel and modules
253	." Current boot device: " s" currdev" getenv type cr
254;
255
256\ beadm list [device]
257\ beadm activate BE [device] | device
258\
259\ lists BE's from current or specified device /boot/menu.lst file
260\ activates specified BE by unloading modules, setting currdev and
261\ running start to load configuration.
262: beadm ( -- ) ( throws: abort )
263	0= if ( interpreted ) get_arguments then
264
265	dup 0= if
266		." Usage:" cr
267		." beadm activate {beName [device] | device}" cr
268		." beadm list [device]" cr
269		." Use lsdev to get device names." cr
270		drop exit
271	then
272	\ First argument is 0 when we're interprated.  See support.4th
273	\ for get_arguments reading the rest of the line and parsing it
274	\ stack: argN lenN ... arg1 len1 N
275	\ rotate arg1 len1, dont use argv[] as we want to get arg1 out of stack
276	-rot 2dup
277
278	s" list" compare-insensitive 0= if ( list )
279		2drop
280		argc 1 = if ( list currdev )
281			\ add dev to list of args and switch to case 2
282			current-dev rot 1 +
283		then
284		2 = if ( list device ) list-dev exit then
285		." too many arguments" cr abort
286	then
287	s" activate" compare-insensitive 0= if ( activate )
288		argc 1 = if ( missing be )
289			drop ." missing bName" cr abort
290		then
291		argc 2 = if ( activate be )
292			\ need to set arg list into proper order
293			1+ >R	\ save argc+1 to return stack
294
295			\ if the prefix is fd, cd, net or disk and we have :
296			\ in the name, it is device and inject empty be name
297			over 2 s" fd" compare 0= >R
298			over 2 s" cd" compare 0= R> or >R
299			over 3 s" net" compare 0= R> or >R
300			over 4 s" disk" compare 0= R> or
301			if ( prefix is fd or cd or net or disk )
302				2dup [char] : strchr nip
303				if ( its : in name )
304					true
305				else
306					false
307				then
308			else
309				false
310			then
311
312			if ( it is device name )
313				0 0 R>
314			else
315				\ add device, swap with be and receive argc
316				current-dev 2swap R>
317			then
318		then
319		3 = if ( activate be device ) activate-dev exit then
320		." too many arguments" cr abort
321	then
322	." Unknown argument" cr abort
323;
324
325also forth definitions also builtins
326
327\ make beadm available as user command.
328builtin: beadm
329
330\ count the pages of BE list
331\ leave FALSE in stack in case of error
332: be-pages ( -- flag )
333	1 local flag
334	0 0 2local currdev
335	0 0 2local title
336	end-locals
337
338	current-dev menu.lst 2dup 2>R
339	0 to end_of_file?
340	O_RDONLY fopen fd !
341	2R> drop free-memory
342	reset_line_reading
343	fd @ -1 = if FALSE else
344		s" currdev" getenv
345		over			( addr len addr )
346		4 s" zfs:" compare 0= if
347			5 -			\ len -= 5
348			swap 4 +		\ addr += 4
349			swap to currdev
350		then
351
352		0
353		begin
354			end_of_file? 0=
355		while
356			read_line
357			get_name_value
358			s" title" name_buffer strget compare
359			0= if 1+ then
360
361			flag if		\ check for title
362				value_buffer strget strdup to title free_buffers
363				read_line		\ get bootfs
364				get_name_value
365				value_buffer strget currdev compare 0= if
366					title s" zfs_be_active" setenv
367					0 to flag
368				then
369				title drop free-memory 0 0 to title
370				free_buffers
371			else
372				free_buffers
373				read_line		\ get bootfs
374			then
375		repeat
376		fd @ fclose
377		line_buffer strfree
378		read_buffer strfree
379		5 /mod swap dup page_remainder !		\ save remainder
380		if 1+ then
381		dup page_count !				\ save count
382		n2s s" zfs_be_pages" setenv
383		TRUE
384	then
385;
386
387: be-set-page { | entry count n device -- }
388	page_count @ 0= if
389		be-pages
390		page_count @ 0= if exit then
391	then
392
393	0 to device
394	1 s" zfs_be_currpage" getenvn
395	5 *
396	page_count @ 5 *
397	page_remainder @ if
398		5 page_remainder @ - -
399	then
400	swap -
401	dup to entry
402	0 < if
403		entry 5 + to count
404		0 to entry
405	else
406		5 to count
407	then
408	current-dev menu.lst 2dup 2>R
409	0 to end_of_file?
410	O_RDONLY fopen fd !
411	2R> drop free-memory
412	reset_line_reading
413	fd @ -1 = if EOPEN throw then
414	0 to n
415	begin
416		end_of_file? 0=
417	while
418		n entry < if
419			read_line		\ skip title
420			read_line		\ skip bootfs
421			n 1+ to n
422		else
423			\ Use reverse loop to display descending order
424			\ for BE list.
425			0 count 1- do
426				read_line		\ read title line
427				get_name_value
428				value_buffer strget
429				52 i +			\ ascii 4 + i
430				s" bootenvmenu_caption[4]" 20 +c! setenv
431				value_buffer strget
432				52 i +			\ ascii 4 + i
433				s" bootenvansi_caption[4]" 20 +c! setenv
434
435				free_buffers
436				read_line		\ read value line
437				get_name_value
438
439				\ set menu entry command
440				name_buffer strget s" chain" compare
441				0= if
442					s" set_be_chain"
443				else
444					s" set_bootenv"
445				then
446				52 i +			\ ascii 4 + i
447				s" bootenvmenu_command[4]" 20 +c! setenv
448
449				\ set device name
450				name_buffer strget s" chain" compare
451				0= if
452					\ for chain, use the value as is
453					value_buffer strget
454				else
455					\ check last char in the name
456					value_buffer strget 2dup + c@
457					[char] : <> if
458						\ make zfs device name
459						swap drop
460						5 + allocate if
461							ENOMEM throw
462						then
463						s" zfs:" ( addr addr' len' )
464						2 pick swap move ( addr )
465						dup to device
466						4 value_buffer strget
467						strcat	( addr len )
468						s" :" strcat
469					then
470				then
471
472				52 i +			\ ascii 4 + i
473				s" bootenv_root[4]" 13 +c! setenv
474				device free-memory 0 to device
475				free_buffers
476				-1
477			+loop
478
479			5 count do		\ unset unused entries
480				52 i +			\ ascii 4 + i
481				dup s" bootenvmenu_caption[4]" 20 +c! unsetenv
482				dup s" bootenvansi_caption[4]" 20 +c! unsetenv
483				dup s" bootenvmenu_command[4]" 20 +c! unsetenv
484				s" bootenv_root[4]" 13 +c! unsetenv
485			loop
486
487			1 to end_of_file?		\ we are done
488		then
489	repeat
490	fd @ fclose
491	line_buffer strfree
492	read_buffer strfree
493;
494