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