@@ -80,6 +80,13 @@ pub(crate) struct UpgradeOpts {
80
80
#[ clap( long, conflicts_with = "check" ) ]
81
81
pub ( crate ) apply : bool ,
82
82
83
+ /// Configure soft reboot behavior.
84
+ ///
85
+ /// 'required' will fail if soft reboot is not available.
86
+ /// 'auto' will use soft reboot if available, otherwise fall back to regular reboot.
87
+ #[ clap( long = "soft-reboot" , conflicts_with = "check" ) ]
88
+ pub ( crate ) soft_reboot : Option < SoftRebootMode > ,
89
+
83
90
#[ clap( flatten) ]
84
91
pub ( crate ) progress : ProgressOptions ,
85
92
}
@@ -99,6 +106,13 @@ pub(crate) struct SwitchOpts {
99
106
#[ clap( long) ]
100
107
pub ( crate ) apply : bool ,
101
108
109
+ /// Configure soft reboot behavior.
110
+ ///
111
+ /// 'required' will fail if soft reboot is not available.
112
+ /// 'auto' will use soft reboot if available, otherwise fall back to regular reboot.
113
+ #[ clap( long = "soft-reboot" ) ]
114
+ pub ( crate ) soft_reboot : Option < SoftRebootMode > ,
115
+
102
116
/// The transport; e.g. oci, oci-archive, containers-storage. Defaults to `registry`.
103
117
#[ clap( long, default_value = "registry" ) ]
104
118
pub ( crate ) transport : String ,
@@ -142,6 +156,13 @@ pub(crate) struct RollbackOpts {
142
156
/// a userspace-only restart.
143
157
#[ clap( long) ]
144
158
pub ( crate ) apply : bool ,
159
+
160
+ /// Configure soft reboot behavior.
161
+ ///
162
+ /// 'required' will fail if soft reboot is not available.
163
+ /// 'auto' will use soft reboot if available, otherwise fall back to regular reboot.
164
+ #[ clap( long = "soft-reboot" ) ]
165
+ pub ( crate ) soft_reboot : Option < SoftRebootMode > ,
145
166
}
146
167
147
168
/// Perform an edit operation
@@ -167,6 +188,15 @@ pub(crate) enum OutputFormat {
167
188
Json ,
168
189
}
169
190
191
+ #[ derive( Debug , Clone , Copy , ValueEnum , PartialEq , Eq ) ]
192
+ #[ clap( rename_all = "lowercase" ) ]
193
+ pub ( crate ) enum SoftRebootMode {
194
+ /// Require a soft reboot; fail if not possible
195
+ Required ,
196
+ /// Automatically use soft reboot if possible, otherwise use regular reboot
197
+ Auto ,
198
+ }
199
+
170
200
/// Perform an status operation
171
201
#[ derive( Debug , Parser , PartialEq , Eq ) ]
172
202
pub ( crate ) struct StatusOpts {
@@ -562,7 +592,7 @@ pub(crate) enum Opt {
562
592
Note on Rollbacks and the `/etc` Directory:
563
593
564
594
When you perform a rollback (e.g., with `bootc rollback`), any
565
- changes made to files in the `/etc` directory won’ t carry over
595
+ changes made to files in the `/etc` directory won' t carry over
566
596
to the rolled-back deployment. The `/etc` files will revert
567
597
to their state from that previous deployment instead.
568
598
@@ -741,6 +771,76 @@ pub(crate) fn require_root(is_container: bool) -> Result<()> {
741
771
Ok ( ( ) )
742
772
}
743
773
774
+ /// Check if a deployment has soft reboot capability
775
+ fn has_soft_reboot_capability ( deployment : Option < & crate :: spec:: BootEntry > ) -> bool {
776
+ deployment. map ( |d| d. soft_reboot_capable ) . unwrap_or ( false )
777
+ }
778
+
779
+ /// Prepare a soft reboot for the given deployment
780
+ #[ context( "Preparing soft reboot" ) ]
781
+ fn prepare_soft_reboot (
782
+ sysroot : & crate :: store:: Storage ,
783
+ deployment : & ostree:: Deployment ,
784
+ ) -> Result < ( ) > {
785
+ let cancellable = ostree:: gio:: Cancellable :: NONE ;
786
+ sysroot
787
+ . sysroot
788
+ . deployment_set_soft_reboot ( deployment, false , cancellable)
789
+ . context ( "Failed to prepare soft-reboot" ) ?;
790
+ Ok ( ( ) )
791
+ }
792
+
793
+ /// Handle soft reboot based on the configured mode
794
+ #[ context( "Handling soft reboot" ) ]
795
+ fn handle_soft_reboot < F > (
796
+ soft_reboot_mode : Option < SoftRebootMode > ,
797
+ entry : Option < & crate :: spec:: BootEntry > ,
798
+ deployment_type : & str ,
799
+ execute_soft_reboot : F ,
800
+ ) -> Result < ( ) >
801
+ where
802
+ F : FnOnce ( ) -> Result < ( ) > ,
803
+ {
804
+ let Some ( mode) = soft_reboot_mode else {
805
+ return Ok ( ( ) ) ;
806
+ } ;
807
+
808
+ let can_soft_reboot = has_soft_reboot_capability ( entry) ;
809
+ match mode {
810
+ SoftRebootMode :: Required => {
811
+ if can_soft_reboot {
812
+ execute_soft_reboot ( ) ?;
813
+ } else {
814
+ anyhow:: bail!(
815
+ "Soft reboot was required but {} deployment is not soft-reboot capable" ,
816
+ deployment_type
817
+ ) ;
818
+ }
819
+ }
820
+ SoftRebootMode :: Auto => {
821
+ if can_soft_reboot {
822
+ execute_soft_reboot ( ) ?;
823
+ }
824
+ }
825
+ }
826
+ Ok ( ( ) )
827
+ }
828
+
829
+ /// Perform a soft reboot for a staged deployment
830
+ #[ context( "Soft reboot staged deployment" ) ]
831
+ fn soft_reboot_staged ( sysroot : & crate :: store:: Storage ) -> Result < ( ) > {
832
+ println ! ( "Staged deployment is soft-reboot capable, preparing for soft-reboot..." ) ;
833
+
834
+ let deployments_list = sysroot. deployments ( ) ;
835
+ let staged_deployment = deployments_list
836
+ . iter ( )
837
+ . find ( |d| d. is_staged ( ) )
838
+ . ok_or_else ( || anyhow:: anyhow!( "Failed to find staged deployment" ) ) ?;
839
+
840
+ prepare_soft_reboot ( sysroot, staged_deployment) ?;
841
+ Ok ( ( ) )
842
+ }
843
+
744
844
/// A few process changes that need to be made for writing.
745
845
/// IMPORTANT: This may end up re-executing the current process,
746
846
/// so anything that happens before this should be idempotent.
@@ -859,7 +959,12 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
859
959
. unwrap_or_default ( ) ;
860
960
if staged_unchanged {
861
961
println ! ( "Staged update present, not changed." ) ;
862
-
962
+ handle_soft_reboot (
963
+ opts. soft_reboot ,
964
+ host. status . staged . as_ref ( ) ,
965
+ "staged" ,
966
+ || soft_reboot_staged ( sysroot) ,
967
+ ) ?;
863
968
if opts. apply {
864
969
crate :: reboot:: reboot ( ) ?;
865
970
}
@@ -881,6 +986,18 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
881
986
if changed {
882
987
sysroot. update_mtime ( ) ?;
883
988
989
+ if opts. soft_reboot . is_some ( ) {
990
+ // At this point we have new staged deployment and the host definition has changed.
991
+ // We need the updated host status before we check if we can prepare the soft-reboot.
992
+ let updated_host = crate :: status:: get_status ( sysroot, Some ( & booted_deployment) ) ?. 1 ;
993
+ handle_soft_reboot (
994
+ opts. soft_reboot ,
995
+ updated_host. status . staged . as_ref ( ) ,
996
+ "staged" ,
997
+ || soft_reboot_staged ( sysroot) ,
998
+ ) ?;
999
+ }
1000
+
884
1001
if opts. apply {
885
1002
crate :: reboot:: reboot ( ) ?;
886
1003
}
@@ -956,6 +1073,18 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
956
1073
957
1074
sysroot. update_mtime ( ) ?;
958
1075
1076
+ if opts. soft_reboot . is_some ( ) {
1077
+ // At this point we have staged the deployment and the host definition has changed.
1078
+ // We need the updated host status before we check if we can prepare the soft-reboot.
1079
+ let updated_host = crate :: status:: get_status ( sysroot, Some ( & booted_deployment) ) ?. 1 ;
1080
+ handle_soft_reboot (
1081
+ opts. soft_reboot ,
1082
+ updated_host. status . staged . as_ref ( ) ,
1083
+ "staged" ,
1084
+ || soft_reboot_staged ( sysroot) ,
1085
+ ) ?;
1086
+ }
1087
+
959
1088
if opts. apply {
960
1089
crate :: reboot:: reboot ( ) ?;
961
1090
}
@@ -969,6 +1098,27 @@ async fn rollback(opts: RollbackOpts) -> Result<()> {
969
1098
let sysroot = & get_storage ( ) . await ?;
970
1099
crate :: deploy:: rollback ( sysroot) . await ?;
971
1100
1101
+ if opts. soft_reboot . is_some ( ) {
1102
+ // Get status of rollback deployment to check soft-reboot capability
1103
+ let host = crate :: status:: get_status_require_booted ( sysroot) ?. 2 ;
1104
+
1105
+ handle_soft_reboot (
1106
+ opts. soft_reboot ,
1107
+ host. status . rollback . as_ref ( ) ,
1108
+ "rollback" ,
1109
+ || {
1110
+ println ! (
1111
+ "Rollback deployment is soft-reboot capable, preparing for soft-reboot..."
1112
+ ) ;
1113
+ let deployments_list = sysroot. deployments ( ) ;
1114
+ let target_deployment = deployments_list
1115
+ . first ( )
1116
+ . ok_or_else ( || anyhow:: anyhow!( "No rollback deployment found!" ) ) ?;
1117
+ prepare_soft_reboot ( sysroot, target_deployment)
1118
+ } ,
1119
+ ) ?;
1120
+ }
1121
+
972
1122
if opts. apply {
973
1123
crate :: reboot:: reboot ( ) ?;
974
1124
}
0 commit comments