xref: /illumos-gate/usr/src/boot/forth/beadm.4th (revision 28703145)
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	s" boot_noncluster" unsetenv
253	start			\ load config, kernel and modules
254	." Current boot device: " s" currdev" getenv type cr
255;
256
257\ beadm list [device]
258\ beadm activate BE [device] | device
259\
260\ lists BE's from current or specified device /boot/menu.lst file
261\ activates specified BE by unloading modules, setting currdev and
262\ running start to load configuration.
263: beadm ( -- ) ( throws: abort )
264	0= if ( interpreted ) get_arguments then
265
266	dup 0= if
267		." Usage:" cr
268		." beadm activate {beName [device] | device}" cr
269		." beadm list [device]" cr
270		." Use lsdev to get device names." cr
271		drop exit
272	then
273	\ First argument is 0 when we're interprated.  See support.4th
274	\ for get_arguments reading the rest of the line and parsing it
275	\ stack: argN lenN ... arg1 len1 N
276	\ rotate arg1 len1, dont use argv[] as we want to get arg1 out of stack
277	-rot 2dup
278
279	s" list" compare-insensitive 0= if ( list )
280		2drop
281		argc 1 = if ( list currdev )
282			\ add dev to list of args and switch to case 2
283			current-dev rot 1 +
284		then
285		2 = if ( list device ) list-dev exit then
286		." too many arguments" cr abort
287	then
288	s" activate" compare-insensitive 0= if ( activate )
289		argc 1 = if ( missing be )
290			drop ." missing bName" cr abort
291		then
292		argc 2 = if ( activate be )
293			\ need to set arg list into proper order
294			1+ >R	\ save argc+1 to return stack
295
296			\ if the prefix is fd, cd, net or disk and we have :
297			\ in the name, it is device and inject empty be name
298			over 2 s" fd" compare 0= >R
299			over 2 s" cd" compare 0= R> or >R
300			over 3 s" net" compare 0= R> or >R
301			over 4 s" disk" compare 0= R> or
302			if ( prefix is fd or cd or net or disk )
303				2dup [char] : strchr nip
304				if ( its : in name )
305					true
306				else
307					false
308				then
309			else
310				false
311			then
312
313			if ( it is device name )
314				0 0 R>
315			else
316				\ add device, swap with be and receive argc
317				current-dev 2swap R>
318			then
319		then
320		3 = if ( activate be device ) activate-dev exit then
321		." too many arguments" cr abort
322	then
323	." Unknown argument" cr abort
324;
325
326also forth definitions also builtins
327
328\ make beadm available as user command.
329builtin: beadm
330
331\ count the pages of BE list
332\ leave FALSE in stack in case of error
333: be-pages ( -- flag )
334	1 local flag
335	0 0 2local currdev
336	0 0 2local title
337	end-locals
338
339	current-dev menu.lst 2dup 2>R
340	0 to end_of_file?
341	O_RDONLY fopen fd !
342	2R> drop free-memory
343	reset_line_reading
344	fd @ -1 = if FALSE else
345		s" currdev" getenv
346		over			( addr len addr )
347		4 s" zfs:" compare 0= if
348			5 -			\ len -= 5
349			swap 4 +		\ addr += 4
350			swap to currdev
351		then
352
353		0
354		begin
355			end_of_file? 0=
356		while
357			read_line
358			get_name_value
359			s" title" name_buffer strget compare
360			0= if 1+ then
361
362			flag if		\ check for title
363				value_buffer strget strdup to title free_buffers
364				read_line		\ get bootfs
365				get_name_value
366				value_buffer strget currdev compare 0= if
367					title s" zfs_be_active" setenv
368					0 to flag
369				then
370				title drop free-memory 0 0 to title
371				free_buffers
372			else
373				free_buffers
374				read_line		\ get bootfs
375			then
376		repeat
377		fd @ fclose
378		line_buffer strfree
379		read_buffer strfree
380		5 /mod swap dup page_remainder !		\ save remainder
381		if 1+ then
382		dup page_count !				\ save count
383		n2s s" zfs_be_pages" setenv
384		TRUE
385	then
386;
387
388: be-set-page { | entry count n device -- }
389	page_count @ 0= if
390		be-pages
391		page_count @ 0= if exit then
392	then
393
394	0 to device
395	1 s" zfs_be_currpage" getenvn
396	5 *
397	page_count @ 5 *
398	page_remainder @ if
399		5 page_remainder @ - -
400	then
401	swap -
402	dup to entry
403	0 < if
404		entry 5 + to count
405		0 to entry
406	else
407		5 to count
408	then
409	current-dev menu.lst 2dup 2>R
410	0 to end_of_file?
411	O_RDONLY fopen fd !
412	2R> drop free-memory
413	reset_line_reading
414	fd @ -1 = if EOPEN throw then
415	0 to n
416	begin
417		end_of_file? 0=
418	while
419		n entry < if
420			read_line		\ skip title
421			read_line		\ skip bootfs
422			n 1+ to n
423		else
424			\ Use reverse loop to display descending order
425			\ for BE list.
426			0 count 1- do
427				read_line		\ read title line
428				get_name_value
429				value_buffer strget
430				52 i +			\ ascii 4 + i
431				s" bootenvmenu_caption[4]" 20 +c! setenv
432				value_buffer strget
433				52 i +			\ ascii 4 + i
434				s" bootenvansi_caption[4]" 20 +c! setenv
435
436				free_buffers
437				read_line		\ read value line
438				get_name_value
439
440				\ set menu entry command
441				name_buffer strget s" chain" compare
442				0= if
443					s" set_be_chain"
444				else
445					s" set_bootenv"
446				then
447				52 i +			\ ascii 4 + i
448				s" bootenvmenu_command[4]" 20 +c! setenv
449
450				\ set device name
451				name_buffer strget s" chain" compare
452				0= if
453					\ for chain, use the value as is
454					value_buffer strget
455				else
456					\ check last char in the name
457					value_buffer strget 2dup + c@
458					[char] : <> if
459						\ make zfs device name
460						swap drop
461						5 + allocate if
462							ENOMEM throw
463						then
464						s" zfs:" ( addr addr' len' )
465						2 pick swap move ( addr )
466						dup to device
467						4 value_buffer strget
468						strcat	( addr len )
469						s" :" strcat
470					then
471				then
472
473				52 i +			\ ascii 4 + i
474				s" bootenv_root[4]" 13 +c! setenv
475				device free-memory 0 to device
476				free_buffers
477				-1
478			+loop
479
480			5 count do		\ unset unused entries
481				52 i +			\ ascii 4 + i
482				dup s" bootenvmenu_caption[4]" 20 +c! unsetenv
483				dup s" bootenvansi_caption[4]" 20 +c! unsetenv
484				dup s" bootenvmenu_command[4]" 20 +c! unsetenv
485				s" bootenv_root[4]" 13 +c! unsetenv
486			loop
487
488			1 to end_of_file?		\ we are done
489		then
490	repeat
491	fd @ fclose
492	line_buffer strfree
493	read_buffer strfree
494;
495