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
;
|