@@ -44,7 +44,7 @@ use ostree::gio;
44
44
use ostree_ext:: ostree;
45
45
use ostree_ext:: ostree_prepareroot:: { ComposefsState , Tristate } ;
46
46
use ostree_ext:: prelude:: Cast ;
47
- use ostree_ext:: sysroot:: SysrootLock ;
47
+ use ostree_ext:: sysroot:: { allocate_new_stateroot , list_stateroots , SysrootLock } ;
48
48
use ostree_ext:: { container as ostree_container, ostree_prepareroot} ;
49
49
#[ cfg( feature = "install-to-disk" ) ]
50
50
use rustix:: fs:: FileTypeExt ;
@@ -55,7 +55,10 @@ use serde::{Deserialize, Serialize};
55
55
use self :: baseline:: InstallBlockDeviceOpts ;
56
56
use crate :: boundimage:: { BoundImage , ResolvedBoundImage } ;
57
57
use crate :: containerenv:: ContainerExecutionInfo ;
58
- use crate :: deploy:: { prepare_for_pull, pull_from_prepared, PreparedImportMeta , PreparedPullResult } ;
58
+ use crate :: deploy:: {
59
+ prepare_for_pull, pull_from_prepared, MergeState , PreparedImportMeta , PreparedPullResult ,
60
+ } ;
61
+ use crate :: kernel_cmdline:: Cmdline ;
59
62
use crate :: lsm;
60
63
use crate :: progress_jsonl:: ProgressWriter ;
61
64
use crate :: spec:: ImageReference ;
@@ -352,6 +355,50 @@ pub(crate) struct InstallToExistingRootOpts {
352
355
pub ( crate ) root_path : Utf8PathBuf ,
353
356
}
354
357
358
+ #[ derive( Debug , clap:: Parser , PartialEq , Eq ) ]
359
+ pub ( crate ) struct InstallResetOpts {
360
+ /// Acknowledge that this command is experimental.
361
+ #[ clap( long) ]
362
+ pub ( crate ) experimental : bool ,
363
+
364
+ #[ clap( flatten) ]
365
+ pub ( crate ) source_opts : InstallSourceOpts ,
366
+
367
+ #[ clap( flatten) ]
368
+ pub ( crate ) target_opts : InstallTargetOpts ,
369
+
370
+ /// Name of the target stateroot. If not provided, one will be automatically
371
+ /// generated of the form s<year>-<serial> where <serial> starts at zero and
372
+ /// increments automatically.
373
+ #[ clap( long) ]
374
+ pub ( crate ) stateroot : Option < String > ,
375
+
376
+ /// Don't display progress
377
+ #[ clap( long) ]
378
+ pub ( crate ) quiet : bool ,
379
+
380
+ #[ clap( flatten) ]
381
+ pub ( crate ) progress : crate :: cli:: ProgressOptions ,
382
+
383
+ /// Restart or reboot into the new target image.
384
+ ///
385
+ /// Currently, this option always reboots. In the future this command
386
+ /// will detect the case where no kernel changes are queued, and perform
387
+ /// a userspace-only restart.
388
+ #[ clap( long) ]
389
+ pub ( crate ) apply : bool ,
390
+
391
+ /// Skip inheriting any automatically discovered root file system kernel arguments.
392
+ #[ clap( long) ]
393
+ no_root_kargs : bool ,
394
+
395
+ /// Add a kernel argument. This option can be provided multiple times.
396
+ ///
397
+ /// Example: --karg=nosmt --karg=console=ttyS0,114800n8
398
+ #[ clap( long) ]
399
+ karg : Option < Vec < String > > ,
400
+ }
401
+
355
402
/// Global state captured from the container.
356
403
#[ derive( Debug , Clone ) ]
357
404
pub ( crate ) struct SourceInfo {
@@ -385,6 +432,24 @@ pub(crate) struct State {
385
432
pub ( crate ) tempdir : TempDir ,
386
433
}
387
434
435
+ impl InstallTargetOpts {
436
+ pub ( crate ) fn imageref ( & self ) -> Result < Option < ostree_container:: OstreeImageReference > > {
437
+ let Some ( target_imgname) = self . target_imgref . as_deref ( ) else {
438
+ return Ok ( None ) ;
439
+ } ;
440
+ let target_transport =
441
+ ostree_container:: Transport :: try_from ( self . target_transport . as_str ( ) ) ?;
442
+ let target_imgref = ostree_container:: OstreeImageReference {
443
+ sigverify : ostree_container:: SignatureSource :: ContainerPolicyAllowInsecure ,
444
+ imgref : ostree_container:: ImageReference {
445
+ transport : target_transport,
446
+ name : target_imgname. to_string ( ) ,
447
+ } ,
448
+ } ;
449
+ Ok ( Some ( target_imgref) )
450
+ }
451
+ }
452
+
388
453
impl State {
389
454
#[ context( "Loading SELinux policy" ) ]
390
455
pub ( crate ) fn load_policy ( & self ) -> Result < Option < ostree:: SePolicy > > {
@@ -2019,6 +2084,94 @@ pub(crate) async fn install_to_existing_root(opts: InstallToExistingRootOpts) ->
2019
2084
install_to_filesystem ( opts, true , cleanup) . await
2020
2085
}
2021
2086
2087
+ pub ( crate ) async fn install_reset ( opts : InstallResetOpts ) -> Result < ( ) > {
2088
+ let rootfs = & Dir :: open_ambient_dir ( "/" , cap_std:: ambient_authority ( ) ) ?;
2089
+ if !opts. experimental {
2090
+ anyhow:: bail!( "This command requires --experimental" ) ;
2091
+ }
2092
+
2093
+ let prog: ProgressWriter = opts. progress . try_into ( ) ?;
2094
+
2095
+ let sysroot = & crate :: cli:: get_storage ( ) . await ?;
2096
+ let repo = & sysroot. repo ( ) ;
2097
+ let ( booted_deployment, _deployments, host) =
2098
+ crate :: status:: get_status_require_booted ( sysroot) ?;
2099
+
2100
+ let stateroots = list_stateroots ( sysroot) ?;
2101
+ dbg ! ( & stateroots) ;
2102
+ let target_stateroot = if let Some ( s) = opts. stateroot {
2103
+ s
2104
+ } else {
2105
+ let now = chrono:: Utc :: now ( ) ;
2106
+ let r = allocate_new_stateroot ( & sysroot, & stateroots, now) ?;
2107
+ r. name
2108
+ } ;
2109
+
2110
+ let booted_stateroot = booted_deployment. osname ( ) ;
2111
+ assert ! ( booted_stateroot. as_str( ) != target_stateroot) ;
2112
+ let ( fetched, spec) = if let Some ( target) = opts. target_opts . imageref ( ) ? {
2113
+ let mut new_spec = host. spec ;
2114
+ new_spec. image = Some ( target. into ( ) ) ;
2115
+ let fetched = crate :: deploy:: pull (
2116
+ repo,
2117
+ & new_spec. image . as_ref ( ) . unwrap ( ) ,
2118
+ None ,
2119
+ opts. quiet ,
2120
+ prog. clone ( ) ,
2121
+ )
2122
+ . await ?;
2123
+ ( fetched, new_spec)
2124
+ } else {
2125
+ let imgstate = host
2126
+ . status
2127
+ . booted
2128
+ . map ( |b| b. query_image ( repo) )
2129
+ . transpose ( ) ?
2130
+ . flatten ( )
2131
+ . ok_or_else ( || anyhow:: anyhow!( "No image source specified" ) ) ?;
2132
+ ( Box :: new ( ( * imgstate) . into ( ) ) , host. spec )
2133
+ } ;
2134
+ let spec = crate :: deploy:: RequiredHostSpec :: from_spec ( & spec) ?;
2135
+
2136
+ // Compute the kernel arguments to inherit. By default, that's only those involved
2137
+ // in the root filesystem.
2138
+ let root_kargs = if opts. no_root_kargs {
2139
+ Vec :: new ( )
2140
+ } else {
2141
+ let bootcfg = booted_deployment
2142
+ . bootconfig ( )
2143
+ . ok_or_else ( || anyhow ! ( "Missing bootcfg for booted deployment" ) ) ?;
2144
+ if let Some ( options) = bootcfg. get ( "options" ) {
2145
+ let options = options. split_ascii_whitespace ( ) . collect :: < Vec < _ > > ( ) ;
2146
+ crate :: kernel:: root_args_from_cmdline ( & options)
2147
+ . into_iter ( )
2148
+ . map ( ToOwned :: to_owned)
2149
+ . collect :: < Vec < _ > > ( )
2150
+ } else {
2151
+ Vec :: new ( )
2152
+ }
2153
+ } ;
2154
+
2155
+ let kargs = crate :: kargs:: get_kargs_in_root ( rootfs, std:: env:: consts:: ARCH ) ?
2156
+ . into_iter ( )
2157
+ . chain ( root_kargs. into_iter ( ) )
2158
+ . chain ( opts. karg . unwrap_or_default ( ) )
2159
+ . collect :: < Vec < _ > > ( ) ;
2160
+
2161
+ let from = MergeState :: Reset {
2162
+ stateroot : target_stateroot,
2163
+ kargs,
2164
+ } ;
2165
+ crate :: deploy:: stage ( sysroot, from, & fetched, & spec, prog. clone ( ) ) . await ?;
2166
+
2167
+ sysroot. update_mtime ( ) ?;
2168
+
2169
+ if opts. apply {
2170
+ crate :: reboot:: reboot ( ) ?;
2171
+ }
2172
+ Ok ( ( ) )
2173
+ }
2174
+
2022
2175
/// Implementation of `bootc install finalize`.
2023
2176
pub ( crate ) async fn install_finalize ( target : & Utf8Path ) -> Result < ( ) > {
2024
2177
// Log the installation finalization operation to systemd journal
0 commit comments