btrfs_cli/replace/
status.rs1use crate::{
2 Format, Runnable,
3 util::{format_time_short, open_path},
4};
5use anyhow::{Context, Result};
6use btrfs_uapi::replace::{ReplaceState, replace_status};
7use clap::Parser;
8use std::{
9 io::Write, os::unix::io::AsFd, path::PathBuf, thread, time::Duration,
10};
11
12#[derive(Parser, Debug)]
18pub struct ReplaceStatusCommand {
19 #[clap(short = '1', long)]
21 pub once: bool,
22
23 pub mount_point: PathBuf,
25}
26
27impl Runnable for ReplaceStatusCommand {
28 fn run(&self, _format: Format, _dry_run: bool) -> Result<()> {
29 let file = open_path(&self.mount_point)?;
30 let fd = file.as_fd();
31
32 loop {
33 let status = replace_status(fd).with_context(|| {
34 format!(
35 "failed to get replace status on '{}'",
36 self.mount_point.display()
37 )
38 })?;
39
40 let line = match status.state {
41 ReplaceState::NeverStarted => "Never started".to_string(),
42 ReplaceState::Started => {
43 let pct = status.progress_1000 as f64 / 10.0;
44 format!(
45 "{pct:.1}% done, {} write errs, {} uncorr. read errs",
46 status.num_write_errors,
47 status.num_uncorrectable_read_errors,
48 )
49 }
50 ReplaceState::Finished => {
51 let started = status
52 .time_started
53 .map(|t| format_time_short(&t))
54 .unwrap_or_default();
55 let stopped = status
56 .time_stopped
57 .map(|t| format_time_short(&t))
58 .unwrap_or_default();
59 format!(
60 "Started on {started}, finished on {stopped}, \
61 {} write errs, {} uncorr. read errs",
62 status.num_write_errors,
63 status.num_uncorrectable_read_errors,
64 )
65 }
66 ReplaceState::Canceled => {
67 let started = status
68 .time_started
69 .map(|t| format_time_short(&t))
70 .unwrap_or_default();
71 let stopped = status
72 .time_stopped
73 .map(|t| format_time_short(&t))
74 .unwrap_or_default();
75 let pct = status.progress_1000 as f64 / 10.0;
76 format!(
77 "Started on {started}, canceled on {stopped} at {pct:.1}%, \
78 {} write errs, {} uncorr. read errs",
79 status.num_write_errors,
80 status.num_uncorrectable_read_errors,
81 )
82 }
83 ReplaceState::Suspended => {
84 let started = status
85 .time_started
86 .map(|t| format_time_short(&t))
87 .unwrap_or_default();
88 let stopped = status
89 .time_stopped
90 .map(|t| format_time_short(&t))
91 .unwrap_or_default();
92 let pct = status.progress_1000 as f64 / 10.0;
93 format!(
94 "Started on {started}, suspended on {stopped} at {pct:.1}%, \
95 {} write errs, {} uncorr. read errs",
96 status.num_write_errors,
97 status.num_uncorrectable_read_errors,
98 )
99 }
100 };
101
102 print!("\r{line}");
103 std::io::stdout().flush()?;
104
105 if self.once || status.state != ReplaceState::Started {
107 println!();
108 break;
109 }
110
111 thread::sleep(Duration::from_secs(1));
112 }
113
114 Ok(())
115 }
116}