@@ -82,25 +82,6 @@ fn unset_boot_counter_at(grub_path: &str) -> Result<()> {
8282 unset_grub_var ( "boot_counter" , grub_path)
8383}
8484
85- /// sets greenboot_rollback_trigger=1 and fallback=1
86- pub fn set_rollback_trigger ( ) -> Result < ( ) > {
87- set_rollback_trigger_at ( GRUB_PATH )
88- }
89-
90- fn set_rollback_trigger_at ( grub_path : & str ) -> Result < ( ) > {
91- set_grub_var ( "greenboot_rollback_trigger" , 1 , grub_path) ?;
92- set_grub_var ( "fallback" , 1 , grub_path)
93- }
94-
95- /// unsets greenboot_rollback_trigger
96- pub fn unset_rollback_trigger ( ) -> Result < ( ) > {
97- unset_rollback_trigger_at ( GRUB_PATH )
98- }
99-
100- fn unset_rollback_trigger_at ( grub_path : & str ) -> Result < ( ) > {
101- unset_grub_var ( "greenboot_rollback_trigger" , grub_path)
102- }
103-
10485/// sets fallback=1 for GRUB-level kernel fallback protection
10586pub fn set_fallback ( ) -> Result < ( ) > {
10687 set_fallback_at ( GRUB_PATH )
@@ -128,13 +109,31 @@ fn get_fallback_at(grub_path: &str) -> Result<bool> {
128109 get_grub_bool_var ( "fallback" , grub_path)
129110}
130111
131- /// gets greenboot_rollback_trigger value, returns true if set to 1
132- pub fn get_rollback_trigger ( ) -> Result < bool > {
133- get_rollback_trigger_at ( GRUB_PATH )
112+ /// sets greenboot_next_deployment_id to the given deployment ID string
113+ pub fn set_next_deployment_id ( id : & str ) -> Result < ( ) > {
114+ set_next_deployment_id_at ( id, GRUB_PATH )
115+ }
116+
117+ fn set_next_deployment_id_at ( id : & str , grub_path : & str ) -> Result < ( ) > {
118+ set_grub_str_var ( "greenboot_next_deployment_id" , id, grub_path)
134119}
135120
136- fn get_rollback_trigger_at ( grub_path : & str ) -> Result < bool > {
137- get_grub_bool_var ( "greenboot_rollback_trigger" , grub_path)
121+ /// unsets greenboot_next_deployment_id
122+ pub fn unset_next_deployment_id ( ) -> Result < ( ) > {
123+ unset_next_deployment_id_at ( GRUB_PATH )
124+ }
125+
126+ fn unset_next_deployment_id_at ( grub_path : & str ) -> Result < ( ) > {
127+ unset_grub_var ( "greenboot_next_deployment_id" , grub_path)
128+ }
129+
130+ /// returns the stored deployment ID, or None if not set
131+ pub fn get_next_deployment_id ( ) -> Result < Option < String > > {
132+ get_next_deployment_id_at ( GRUB_PATH )
133+ }
134+
135+ fn get_next_deployment_id_at ( grub_path : & str ) -> Result < Option < String > > {
136+ get_grub_str_var ( "greenboot_next_deployment_id" , grub_path)
138137}
139138
140139fn get_grub_bool_var ( key : & str , grub_path : & str ) -> Result < bool > {
@@ -194,12 +193,56 @@ fn set_grub_var(key: &str, val: u16, grub_path: &str) -> Result<()> {
194193 Ok ( ( ) )
195194}
196195
196+ fn set_grub_str_var ( key : & str , val : & str , grub_path : & str ) -> Result < ( ) > {
197+ let grub_result = Command :: new ( "grub2-editenv" )
198+ . arg ( grub_path)
199+ . arg ( "set" )
200+ . arg ( format ! ( "{key}={val}" ) )
201+ . status ( )
202+ . context ( "Unable to set grubenv" ) ?;
203+
204+ if !grub_result. success ( ) {
205+ bail ! ( "Failed to set grubenv key: {key}" ) ;
206+ }
207+
208+ log:: info!( "Set grubenv: {key}={val}" ) ;
209+ Ok ( ( ) )
210+ }
211+
212+ fn get_grub_str_var ( key : & str , grub_path : & str ) -> Result < Option < String > > {
213+ let grub_vars = Command :: new ( "grub2-editenv" )
214+ . arg ( grub_path)
215+ . arg ( "list" )
216+ . output ( )
217+ . context ( format ! ( "Unable to list grubenv variables for key: {key}" ) ) ?;
218+
219+ if !grub_vars. status . success ( ) {
220+ bail ! (
221+ "grub2-editenv failed to list variables: {}" ,
222+ String :: from_utf8_lossy( & grub_vars. stderr)
223+ ) ;
224+ }
225+
226+ let prefix = format ! ( "{key}=" ) ;
227+ let output = String :: from_utf8_lossy ( & grub_vars. stdout ) ;
228+ for line in output. lines ( ) {
229+ if let Some ( value) = line. strip_prefix ( & prefix) {
230+ let trimmed = value. trim ( ) ;
231+ if trimmed. is_empty ( ) {
232+ return Ok ( None ) ;
233+ }
234+ return Ok ( Some ( trimmed. to_string ( ) ) ) ;
235+ }
236+ }
237+ Ok ( None )
238+ }
239+
197240#[ cfg( test) ]
198241mod tests {
199242 use super :: {
200- get_boot_counter_at, get_fallback_at, get_rollback_trigger_at , set_boot_counter_at,
201- set_fallback_at, set_rollback_trigger_at , unset_boot_counter_at, unset_fallback_at,
202- unset_rollback_trigger_at ,
243+ get_boot_counter_at, get_fallback_at, get_next_deployment_id_at , set_boot_counter_at,
244+ set_fallback_at, set_next_deployment_id_at , unset_boot_counter_at, unset_fallback_at,
245+ unset_next_deployment_id_at ,
203246 } ;
204247 use anyhow:: Context ;
205248 use std:: fs;
@@ -273,41 +316,66 @@ mod tests {
273316 }
274317
275318 #[ test]
276- fn test_rollback_trigger_functions ( ) {
319+ fn test_next_deployment_id_set_and_get ( ) {
277320 let ( _temp_dir, grubenv) = setup_test_paths ( ) ;
278321
279- assert ! ( !get_rollback_trigger_at( & grubenv) . unwrap( ) ) ;
280- assert ! ( !get_fallback_at( & grubenv) . unwrap( ) ) ;
322+ assert_eq ! ( get_next_deployment_id_at( & grubenv) . unwrap( ) , None ) ;
281323
282- set_rollback_trigger_at ( & grubenv) . unwrap ( ) ;
283- assert ! ( get_rollback_trigger_at( & grubenv) . unwrap( ) ) ;
284- assert ! ( get_fallback_at( & grubenv) . unwrap( ) ) ;
324+ let id = "sha256:abc123def456" ;
325+ set_next_deployment_id_at ( id, & grubenv) . unwrap ( ) ;
326+ assert_eq ! (
327+ get_next_deployment_id_at( & grubenv) . unwrap( ) ,
328+ Some ( id. to_string( ) )
329+ ) ;
330+ }
285331
286- // unset_rollback_trigger only clears the trigger, not fallback
287- unset_rollback_trigger_at ( & grubenv) . unwrap ( ) ;
288- assert ! ( !get_rollback_trigger_at( & grubenv) . unwrap( ) ) ;
289- assert ! ( get_fallback_at( & grubenv) . unwrap( ) ) ;
332+ #[ test]
333+ fn test_next_deployment_id_unset ( ) {
334+ let ( _temp_dir, grubenv) = setup_test_paths ( ) ;
290335
291- // fallback has its own lifecycle, unset independently
292- unset_fallback_at ( & grubenv) . unwrap ( ) ;
293- assert ! ( !get_fallback_at( & grubenv) . unwrap( ) ) ;
336+ let id = "sha256:abc123def456" ;
337+ set_next_deployment_id_at ( id, & grubenv) . unwrap ( ) ;
338+ assert ! ( get_next_deployment_id_at( & grubenv) . unwrap( ) . is_some( ) ) ;
339+
340+ unset_next_deployment_id_at ( & grubenv) . unwrap ( ) ;
341+ assert_eq ! ( get_next_deployment_id_at( & grubenv) . unwrap( ) , None ) ;
294342 }
295343
296344 #[ test]
297- fn test_rollback_trigger_with_other_vars ( ) {
345+ fn test_next_deployment_id_coexists_with_boot_counter ( ) {
298346 let ( _temp_dir, grubenv) = setup_test_paths ( ) ;
299347
348+ let id = "sha256:abc123def456" ;
300349 set_boot_counter_at ( 3 , & grubenv) . unwrap ( ) ;
301- set_rollback_trigger_at ( & grubenv) . unwrap ( ) ;
350+ set_next_deployment_id_at ( id , & grubenv) . unwrap ( ) ;
302351
303352 assert_eq ! ( get_boot_counter_at( & grubenv) . unwrap( ) , Some ( 3 ) ) ;
304- assert ! ( get_rollback_trigger_at( & grubenv) . unwrap( ) ) ;
305- assert ! ( get_fallback_at( & grubenv) . unwrap( ) ) ;
353+ assert_eq ! (
354+ get_next_deployment_id_at( & grubenv) . unwrap( ) ,
355+ Some ( id. to_string( ) )
356+ ) ;
306357
307- // unset_rollback_trigger leaves boot_counter and fallback intact
308- unset_rollback_trigger_at ( & grubenv) . unwrap ( ) ;
358+ unset_next_deployment_id_at ( & grubenv) . unwrap ( ) ;
309359 assert_eq ! ( get_boot_counter_at( & grubenv) . unwrap( ) , Some ( 3 ) ) ;
310- assert ! ( !get_rollback_trigger_at( & grubenv) . unwrap( ) ) ;
360+ assert_eq ! ( get_next_deployment_id_at( & grubenv) . unwrap( ) , None ) ;
361+ }
362+
363+ #[ test]
364+ fn test_next_deployment_id_coexists_with_fallback ( ) {
365+ let ( _temp_dir, grubenv) = setup_test_paths ( ) ;
366+
367+ let id = "sha256:abc123def456" ;
368+ set_next_deployment_id_at ( id, & grubenv) . unwrap ( ) ;
369+ set_fallback_at ( & grubenv) . unwrap ( ) ;
370+
371+ assert_eq ! (
372+ get_next_deployment_id_at( & grubenv) . unwrap( ) ,
373+ Some ( id. to_string( ) )
374+ ) ;
375+ assert ! ( get_fallback_at( & grubenv) . unwrap( ) ) ;
376+
377+ unset_next_deployment_id_at ( & grubenv) . unwrap ( ) ;
378+ assert_eq ! ( get_next_deployment_id_at( & grubenv) . unwrap( ) , None ) ;
311379 assert ! ( get_fallback_at( & grubenv) . unwrap( ) ) ;
312380 }
313381
@@ -340,36 +408,30 @@ mod tests {
340408 assert ! ( !get_fallback_at( & grubenv) . unwrap( ) ) ;
341409 }
342410
343- #[ test]
344- fn test_fallback_set_via_rollback_trigger ( ) {
345- let ( _temp_dir, grubenv) = setup_test_paths ( ) ;
346-
347- set_rollback_trigger_at ( & grubenv) . unwrap ( ) ;
348- assert ! ( get_fallback_at( & grubenv) . unwrap( ) ) ;
349- assert ! ( get_rollback_trigger_at( & grubenv) . unwrap( ) ) ;
350- }
351-
352411 #[ test]
353412 fn test_fallback_coexists_with_boot_counter ( ) {
354413 let ( _temp_dir, grubenv) = setup_test_paths ( ) ;
355414
356415 set_boot_counter_at ( 3 , & grubenv) . unwrap ( ) ;
357- set_rollback_trigger_at ( & grubenv) . unwrap ( ) ;
416+ set_fallback_at ( & grubenv) . unwrap ( ) ;
358417
359418 assert_eq ! ( get_boot_counter_at( & grubenv) . unwrap( ) , Some ( 3 ) ) ;
360419 assert ! ( get_fallback_at( & grubenv) . unwrap( ) ) ;
361- assert ! ( get_rollback_trigger_at( & grubenv) . unwrap( ) ) ;
362420 }
363421
364422 #[ test]
365- fn test_fallback_independent_unset ( ) {
423+ fn test_fallback_independent_of_deployment_id ( ) {
366424 let ( _temp_dir, grubenv) = setup_test_paths ( ) ;
367425
368- set_rollback_trigger_at ( & grubenv) . unwrap ( ) ;
426+ let id = "sha256:abc123def456" ;
427+ set_next_deployment_id_at ( id, & grubenv) . unwrap ( ) ;
428+ set_fallback_at ( & grubenv) . unwrap ( ) ;
429+
430+ unset_next_deployment_id_at ( & grubenv) . unwrap ( ) ;
431+ assert_eq ! ( get_next_deployment_id_at( & grubenv) . unwrap( ) , None ) ;
369432 assert ! ( get_fallback_at( & grubenv) . unwrap( ) ) ;
370433
371434 unset_fallback_at ( & grubenv) . unwrap ( ) ;
372435 assert ! ( !get_fallback_at( & grubenv) . unwrap( ) ) ;
373- assert ! ( get_rollback_trigger_at( & grubenv) . unwrap( ) ) ;
374436 }
375437}
0 commit comments