@@ -79,6 +79,13 @@ pub(crate) struct UpgradeOpts {
7979 #[ clap( long, conflicts_with = "check" ) ]
8080 pub ( crate ) apply : bool ,
8181
82+ /// Queue a soft reboot after staging the deployment.
83+ ///
84+ /// This will prepare the system for a soft reboot instead of a full reboot,
85+ /// which allows userspace to restart without rebooting the kernel.
86+ #[ clap( long, conflicts_with = "check" ) ]
87+ pub ( crate ) queue_soft_reboot : bool ,
88+
8289 #[ clap( flatten) ]
8390 pub ( crate ) progress : ProgressOptions ,
8491}
@@ -98,6 +105,13 @@ pub(crate) struct SwitchOpts {
98105 #[ clap( long) ]
99106 pub ( crate ) apply : bool ,
100107
108+ /// Queue a soft reboot after staging the deployment.
109+ ///
110+ /// This will prepare the system for a soft reboot instead of a full reboot,
111+ /// which allows userspace to restart without rebooting the kernel.
112+ #[ clap( long) ]
113+ pub ( crate ) queue_soft_reboot : bool ,
114+
101115 /// The transport; e.g. oci, oci-archive, containers-storage. Defaults to `registry`.
102116 #[ clap( long, default_value = "registry" ) ]
103117 pub ( crate ) transport : String ,
@@ -141,6 +155,13 @@ pub(crate) struct RollbackOpts {
141155 /// a userspace-only restart.
142156 #[ clap( long) ]
143157 pub ( crate ) apply : bool ,
158+
159+ /// Queue a soft reboot after performing the rollback.
160+ ///
161+ /// This will prepare the system for a soft reboot instead of a full reboot,
162+ /// which allows userspace to restart without rebooting the kernel.
163+ #[ clap( long) ]
164+ pub ( crate ) queue_soft_reboot : bool ,
144165}
145166
146167/// Perform an edit operation
@@ -554,7 +575,7 @@ pub(crate) enum Opt {
554575 Note on Rollbacks and the `/etc` Directory:
555576
556577 When you perform a rollback (e.g., with `bootc rollback`), any
557- changes made to files in the `/etc` directory won’ t carry over
578+ changes made to files in the `/etc` directory won' t carry over
558579 to the rolled-back deployment. The `/etc` files will revert
559580 to their state from that previous deployment instead.
560581
@@ -733,6 +754,40 @@ pub(crate) fn require_root(is_container: bool) -> Result<()> {
733754 Ok ( ( ) )
734755}
735756
757+ /// Check if a deployment can perform a soft reboot
758+ fn can_perform_soft_reboot ( deployment : Option < & crate :: spec:: BootEntry > ) -> bool {
759+ deployment. map ( |d| d. soft_reboot_capable ) . unwrap_or ( false )
760+ }
761+
762+ /// Prepare a soft reboot for the given deployment
763+ #[ context( "Preparing soft reboot" ) ]
764+ fn prepare_soft_reboot (
765+ sysroot : & crate :: store:: Storage ,
766+ deployment : & ostree:: Deployment ,
767+ ) -> Result < ( ) > {
768+ let cancellable = ostree:: gio:: Cancellable :: NONE ;
769+ sysroot
770+ . sysroot
771+ . deployment_set_soft_reboot ( deployment, false , cancellable)
772+ . context ( "Failed to prepare soft-reboot" ) ?;
773+ Ok ( ( ) )
774+ }
775+
776+ /// Perform a soft reboot for a staged deployment
777+ #[ context( "Soft reboot staged deployment" ) ]
778+ fn soft_reboot_staged ( sysroot : & crate :: store:: Storage ) -> Result < ( ) > {
779+ println ! ( "Staged deployment is soft-reboot capable, performing soft-reboot..." ) ;
780+
781+ let deployments_list = sysroot. deployments ( ) ;
782+ let staged_deployment = deployments_list
783+ . iter ( )
784+ . find ( |d| d. is_staged ( ) )
785+ . ok_or_else ( || anyhow:: anyhow!( "Failed to find staged deployment" ) ) ?;
786+
787+ prepare_soft_reboot ( sysroot, staged_deployment) ?;
788+ Ok ( ( ) )
789+ }
790+
736791/// A few process changes that need to be made for writing.
737792/// IMPORTANT: This may end up re-executing the current process,
738793/// so anything that happens before this should be idempotent.
@@ -851,7 +906,11 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
851906 . unwrap_or_default ( ) ;
852907 if staged_unchanged {
853908 println ! ( "Staged update present, not changed." ) ;
854-
909+ if opts. queue_soft_reboot {
910+ if can_perform_soft_reboot ( host. status . staged . as_ref ( ) ) {
911+ soft_reboot_staged ( sysroot) ?;
912+ }
913+ }
855914 if opts. apply {
856915 crate :: reboot:: reboot ( ) ?;
857916 }
@@ -873,6 +932,12 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
873932 if changed {
874933 sysroot. update_mtime ( ) ?;
875934
935+ if opts. queue_soft_reboot {
936+ if can_perform_soft_reboot ( host. status . staged . as_ref ( ) ) {
937+ soft_reboot_staged ( sysroot) ?;
938+ }
939+ }
940+
876941 if opts. apply {
877942 crate :: reboot:: reboot ( ) ?;
878943 }
@@ -948,6 +1013,12 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
9481013
9491014 sysroot. update_mtime ( ) ?;
9501015
1016+ if opts. queue_soft_reboot {
1017+ if can_perform_soft_reboot ( host. status . staged . as_ref ( ) ) {
1018+ soft_reboot_staged ( sysroot) ?;
1019+ }
1020+ }
1021+
9511022 if opts. apply {
9521023 crate :: reboot:: reboot ( ) ?;
9531024 }
@@ -961,6 +1032,22 @@ async fn rollback(opts: RollbackOpts) -> Result<()> {
9611032 let sysroot = & get_storage ( ) . await ?;
9621033 crate :: deploy:: rollback ( sysroot) . await ?;
9631034
1035+ if opts. queue_soft_reboot {
1036+ // Get status before rollback to check soft-reboot capability
1037+ let host = crate :: status:: get_status_require_booted ( sysroot) ?. 2 ;
1038+
1039+ if can_perform_soft_reboot ( host. status . rollback . as_ref ( ) ) {
1040+ println ! ( "Rollback deployment is soft-reboot capable, preparing for soft-reboot..." ) ;
1041+
1042+ let deployments_list = sysroot. deployments ( ) ;
1043+ let target_deployment = deployments_list
1044+ . first ( )
1045+ . ok_or_else ( || anyhow:: anyhow!( "No deployments found after rollback" ) ) ?;
1046+
1047+ prepare_soft_reboot ( sysroot, target_deployment) ?;
1048+ }
1049+ }
1050+
9641051 if opts. apply {
9651052 crate :: reboot:: reboot ( ) ?;
9661053 }
0 commit comments