@@ -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, preparing for 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,15 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
873932 if changed {
874933 sysroot. update_mtime ( ) ?;
875934
935+ if opts. queue_soft_reboot {
936+ // At this point we have new staged deployment and the host definition has changed.
937+ // We need the updated host status before we check if we can prepare the soft-reboot.
938+ let updated_host = crate :: status:: get_status ( sysroot, Some ( & booted_deployment) ) ?. 1 ;
939+ if can_perform_soft_reboot ( updated_host. status . staged . as_ref ( ) ) {
940+ soft_reboot_staged ( sysroot) ?;
941+ }
942+ }
943+
876944 if opts. apply {
877945 crate :: reboot:: reboot ( ) ?;
878946 }
@@ -948,6 +1016,15 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
9481016
9491017 sysroot. update_mtime ( ) ?;
9501018
1019+ if opts. queue_soft_reboot {
1020+ // At this point we have staged the deployment and the host definition has changed.
1021+ // We need the updated host status before we check if we can prepare the soft-reboot.
1022+ let updated_host = crate :: status:: get_status ( sysroot, Some ( & booted_deployment) ) ?. 1 ;
1023+ if can_perform_soft_reboot ( updated_host. status . staged . as_ref ( ) ) {
1024+ soft_reboot_staged ( sysroot) ?;
1025+ }
1026+ }
1027+
9511028 if opts. apply {
9521029 crate :: reboot:: reboot ( ) ?;
9531030 }
@@ -961,6 +1038,22 @@ async fn rollback(opts: RollbackOpts) -> Result<()> {
9611038 let sysroot = & get_storage ( ) . await ?;
9621039 crate :: deploy:: rollback ( sysroot) . await ?;
9631040
1041+ if opts. queue_soft_reboot {
1042+ // Get status after the rollback is done to check soft-reboot capability
1043+ let host = crate :: status:: get_status_require_booted ( sysroot) ?. 2 ;
1044+
1045+ if can_perform_soft_reboot ( host. status . rollback . as_ref ( ) ) {
1046+ println ! ( "Rollback deployment is soft-reboot capable, preparing for soft-reboot..." ) ;
1047+
1048+ let deployments_list = sysroot. deployments ( ) ;
1049+ let target_deployment = deployments_list
1050+ . first ( )
1051+ . ok_or_else ( || anyhow:: anyhow!( "No rollback deployment found!" ) ) ?;
1052+
1053+ prepare_soft_reboot ( sysroot, target_deployment) ?;
1054+ }
1055+ }
1056+
9641057 if opts. apply {
9651058 crate :: reboot:: reboot ( ) ?;
9661059 }
0 commit comments