@@ -80,6 +80,13 @@ pub(crate) struct UpgradeOpts {
8080 #[ clap( long, conflicts_with = "check" ) ]
8181 pub ( crate ) apply : bool ,
8282
83+ /// Queue a soft reboot after staging the deployment.
84+ ///
85+ /// This will prepare the system for a soft reboot instead of a full reboot,
86+ /// which allows userspace to restart without rebooting the kernel.
87+ #[ clap( long, conflicts_with = "check" ) ]
88+ pub ( crate ) queue_soft_reboot : bool ,
89+
8390 #[ clap( flatten) ]
8491 pub ( crate ) progress : ProgressOptions ,
8592}
@@ -99,6 +106,13 @@ pub(crate) struct SwitchOpts {
99106 #[ clap( long) ]
100107 pub ( crate ) apply : bool ,
101108
109+ /// Queue a soft reboot after staging the deployment.
110+ ///
111+ /// This will prepare the system for a soft reboot instead of a full reboot,
112+ /// which allows userspace to restart without rebooting the kernel.
113+ #[ clap( long) ]
114+ pub ( crate ) queue_soft_reboot : bool ,
115+
102116 /// The transport; e.g. oci, oci-archive, containers-storage. Defaults to `registry`.
103117 #[ clap( long, default_value = "registry" ) ]
104118 pub ( crate ) transport : String ,
@@ -142,6 +156,13 @@ pub(crate) struct RollbackOpts {
142156 /// a userspace-only restart.
143157 #[ clap( long) ]
144158 pub ( crate ) apply : bool ,
159+
160+ /// Queue a soft reboot after performing the rollback.
161+ ///
162+ /// This will prepare the system for a soft reboot instead of a full reboot,
163+ /// which allows userspace to restart without rebooting the kernel.
164+ #[ clap( long) ]
165+ pub ( crate ) queue_soft_reboot : bool ,
145166}
146167
147168/// Perform an edit operation
@@ -562,7 +583,7 @@ pub(crate) enum Opt {
562583 Note on Rollbacks and the `/etc` Directory:
563584
564585 When you perform a rollback (e.g., with `bootc rollback`), any
565- changes made to files in the `/etc` directory won’ t carry over
586+ changes made to files in the `/etc` directory won' t carry over
566587 to the rolled-back deployment. The `/etc` files will revert
567588 to their state from that previous deployment instead.
568589
@@ -741,6 +762,40 @@ pub(crate) fn require_root(is_container: bool) -> Result<()> {
741762 Ok ( ( ) )
742763}
743764
765+ /// Check if a deployment has soft reboot capability
766+ fn has_soft_reboot_capability ( deployment : Option < & crate :: spec:: BootEntry > ) -> bool {
767+ deployment. map ( |d| d. soft_reboot_capable ) . unwrap_or ( false )
768+ }
769+
770+ /// Prepare a soft reboot for the given deployment
771+ #[ context( "Preparing soft reboot" ) ]
772+ fn prepare_soft_reboot (
773+ sysroot : & crate :: store:: Storage ,
774+ deployment : & ostree:: Deployment ,
775+ ) -> Result < ( ) > {
776+ let cancellable = ostree:: gio:: Cancellable :: NONE ;
777+ sysroot
778+ . sysroot
779+ . deployment_set_soft_reboot ( deployment, false , cancellable)
780+ . context ( "Failed to prepare soft-reboot" ) ?;
781+ Ok ( ( ) )
782+ }
783+
784+ /// Perform a soft reboot for a staged deployment
785+ #[ context( "Soft reboot staged deployment" ) ]
786+ fn soft_reboot_staged ( sysroot : & crate :: store:: Storage ) -> Result < ( ) > {
787+ println ! ( "Staged deployment is soft-reboot capable, preparing for soft-reboot..." ) ;
788+
789+ let deployments_list = sysroot. deployments ( ) ;
790+ let staged_deployment = deployments_list
791+ . iter ( )
792+ . find ( |d| d. is_staged ( ) )
793+ . ok_or_else ( || anyhow:: anyhow!( "Failed to find staged deployment" ) ) ?;
794+
795+ prepare_soft_reboot ( sysroot, staged_deployment) ?;
796+ Ok ( ( ) )
797+ }
798+
744799/// A few process changes that need to be made for writing.
745800/// IMPORTANT: This may end up re-executing the current process,
746801/// so anything that happens before this should be idempotent.
@@ -859,7 +914,11 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
859914 . unwrap_or_default ( ) ;
860915 if staged_unchanged {
861916 println ! ( "Staged update present, not changed." ) ;
862-
917+ if opts. queue_soft_reboot {
918+ if has_soft_reboot_capability ( host. status . staged . as_ref ( ) ) {
919+ soft_reboot_staged ( sysroot) ?;
920+ }
921+ }
863922 if opts. apply {
864923 crate :: reboot:: reboot ( ) ?;
865924 }
@@ -881,6 +940,15 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
881940 if changed {
882941 sysroot. update_mtime ( ) ?;
883942
943+ if opts. queue_soft_reboot {
944+ // At this point we have new staged deployment and the host definition has changed.
945+ // We need the updated host status before we check if we can prepare the soft-reboot.
946+ let updated_host = crate :: status:: get_status ( sysroot, Some ( & booted_deployment) ) ?. 1 ;
947+ if has_soft_reboot_capability ( updated_host. status . staged . as_ref ( ) ) {
948+ soft_reboot_staged ( sysroot) ?;
949+ }
950+ }
951+
884952 if opts. apply {
885953 crate :: reboot:: reboot ( ) ?;
886954 }
@@ -956,6 +1024,15 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
9561024
9571025 sysroot. update_mtime ( ) ?;
9581026
1027+ if opts. queue_soft_reboot {
1028+ // At this point we have staged the deployment and the host definition has changed.
1029+ // We need the updated host status before we check if we can prepare the soft-reboot.
1030+ let updated_host = crate :: status:: get_status ( sysroot, Some ( & booted_deployment) ) ?. 1 ;
1031+ if has_soft_reboot_capability ( updated_host. status . staged . as_ref ( ) ) {
1032+ soft_reboot_staged ( sysroot) ?;
1033+ }
1034+ }
1035+
9591036 if opts. apply {
9601037 crate :: reboot:: reboot ( ) ?;
9611038 }
@@ -969,6 +1046,22 @@ async fn rollback(opts: RollbackOpts) -> Result<()> {
9691046 let sysroot = & get_storage ( ) . await ?;
9701047 crate :: deploy:: rollback ( sysroot) . await ?;
9711048
1049+ if opts. queue_soft_reboot {
1050+ // Get status of rollback deployment to check soft-reboot capability
1051+ let host = crate :: status:: get_status_require_booted ( sysroot) ?. 2 ;
1052+
1053+ if has_soft_reboot_capability ( host. status . rollback . as_ref ( ) ) {
1054+ println ! ( "Rollback deployment is soft-reboot capable, preparing for soft-reboot..." ) ;
1055+
1056+ let deployments_list = sysroot. deployments ( ) ;
1057+ let target_deployment = deployments_list
1058+ . first ( )
1059+ . ok_or_else ( || anyhow:: anyhow!( "No rollback deployment found!" ) ) ?;
1060+
1061+ prepare_soft_reboot ( sysroot, target_deployment) ?;
1062+ }
1063+ }
1064+
9721065 if opts. apply {
9731066 crate :: reboot:: reboot ( ) ?;
9741067 }
0 commit comments