summaryrefslogtreecommitdiff
path: root/usr/src/boot/forth/beadm.4th
blob: 6ad4162c1e2e3587dcac5816119c9a816bc045dc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
\
\ 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 2017 Toomas Soome <tsoome@me.com>
\ Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
\ Copyright 2019 Joyent, Inc.

\ 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 ." Type    Device" 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
		name_buffer strget type
		name_buffer strget s" bootfs" compare 0= if 2 spaces then
		name_buffer strget s" chain" compare 0= if 3 spaces then
		value_buffer strget type cr
		free_buffers
	repeat
	fd @ fclose
	drop
;

\ we are called with strings be_name menu_file, to simplify the stack
\ management, we open the menu and free the menu_file.
: beadm_bootfs ( be_addr be_len maddr mlen -- addr len taddr tlen flag | flag )
	0 to end_of_file?
	2dup O_RDONLY fopen fd !
	drop free-memory
	fd @ -1 = if EOPEN throw then
	reset_line_reading
	begin
		end_of_file? 0=
	while
		free_buffers
		read_line
		get_name_value
		2dup value_buffer strget compare
		0= if ( title == be )
			2drop		\ drop be_name
			free_buffers
			read_line
			get_name_value
			value_buffer strget strdup
			name_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 ( be_addr be_len )
		2drop
		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.
\ if be name was not given, set currdev
\ otherwize, we query device:/boot/menu.lst for bootfs and
\ if found, and bootfs type is chain, attempt chainload.
\ set currdev to bootfs.
\ if we were able to set currdev, reload the config

: activate-dev ( dev.addr dev.len be.addr be.len -- )

	dup 0= if
		2drop
		colon-			\ remove : at the end of the dev name
		dup 1+ allocate if ENOMEM throw then
		dup 2swap 0 -rot strcat
		colon+
		s" currdev" setenv	\ setenv currdev = device
		free-memory
	else
		2swap menu.lst
		beadm_bootfs if ( addr len taddr tlen )
			2dup s" chain" compare 0= if
				drop free-memory	\ free type
				2dup
				dup 6 + allocate if ENOMEM throw then
				dup >R
				0 s" chain " strcat
				2swap strcat ['] evaluate catch drop
				\ We are still there?
				R> free-memory		\ free chain command
				drop free-memory	\ free addr
				exit
			then
			drop free-memory		\ free type
			\ check last char in the name
			2dup + c@ [char] : <> if
				\ have dataset and need to get zfs:pool/ROOT/be:
				dup 5 + allocate if ENOMEM throw then
				0 s" zfs:" strcat
				2swap strcat
				colon+
			then
			2dup s" currdev" setenv
			drop free-memory
		else
			." No such BE in menu.lst or menu.lst is missing." cr
			exit
		then
	then

	\ reset BE menu
	0 page_count !
	\ need to do:
	0 unload drop
	free-module-options
	\ unset the env variables with kernel arguments
	s" acpi-user-options" unsetenv
	s" boot-args" unsetenv
	s" boot_ask" unsetenv
	s" boot_single" unsetenv
	s" boot_verbose" unsetenv
	s" boot_kmdb" unsetenv
	s" boot_drop_into_kmdb" unsetenv
	s" boot_reconfigure" unsetenv
	s" boot_noncluster" unsetenv
	start			\ load config, kernel and modules
	." Current boot device: " s" currdev" getenv type cr
;

\ beadm list [device]
\ beadm activate BE [device] | device
\
\ 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] | 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 the prefix is fd, cd, net or disk and we have :
			\ in the name, it is device and inject empty be name
			over 2 s" fd" compare 0= >R
			over 2 s" cd" compare 0= R> or >R
			over 3 s" net" compare 0= R> or >R
			over 4 s" disk" compare 0= R> or
			if ( prefix is fd or cd or net or disk )
				2dup [char] : strchr nip
				if ( its : in name )
					true
				else
					false
				then
			else
				false
			then

			if ( it is device name )
				0 0 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
		n2s s" zfs_be_pages" setenv
		TRUE
	then
;

: be-set-page { | entry count n device -- }
	page_count @ 0= if
		be-pages
		page_count @ 0= if exit then
	then

	0 to device
	1 s" zfs_be_currpage" getenvn
	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
			\ Use reverse loop to display descending order
			\ for BE list.
			0 count 1- 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

				free_buffers
				read_line		\ read value line
				get_name_value

				\ set menu entry command
				name_buffer strget s" chain" compare
				0= if
					s" set_be_chain"
				else
					s" set_bootenv"
				then
				52 i +			\ ascii 4 + i
				s" bootenvmenu_command[4]" 20 +c! setenv

				\ set device name
				name_buffer strget s" chain" compare
				0= if
					\ for chain, use the value as is
					value_buffer strget
				else
					\ check last char in the name
					value_buffer strget 2dup + c@
					[char] : <> if
						\ make zfs device name
						swap drop
						5 + allocate if
							ENOMEM throw
						then
						s" zfs:" ( addr addr' len' )
						2 pick swap move ( addr )
						dup to device
						4 value_buffer strget
						strcat	( addr len )
						s" :" strcat
					then
				then

				52 i +			\ ascii 4 + i
				s" bootenv_root[4]" 13 +c! setenv
				device free-memory 0 to device
				free_buffers
				-1
			+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
;