Add boot diag tasks

This commit is contained in:
2Shirt 2025-02-15 20:30:40 -08:00
parent f272b0c482
commit 2b2a5160fd
Signed by: 2Shirt
GPG key ID: 152FAC923B0E132C
3 changed files with 219 additions and 31 deletions

View file

@ -13,7 +13,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>.
// //
use crate::diags::Groups as DiagGroups; use crate::diags;
use core::{ use core::{
action::Action, action::Action,
components::{ components::{
@ -26,6 +26,7 @@ use core::{
system::{ system::{
boot::{self, SafeMode}, boot::{self, SafeMode},
cpu::get_cpu_name, cpu::get_cpu_name,
disk::PartitionTableType,
drivers, drivers,
}, },
tasks::{Task, TaskResult, TaskType, Tasks}, tasks::{Task, TaskResult, TaskType, Tasks},
@ -34,6 +35,7 @@ use core::{
use std::{ use std::{
env, env,
iter::zip, iter::zip,
path::PathBuf,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
@ -53,7 +55,7 @@ pub struct App {
action_tx: mpsc::UnboundedSender<Action>, action_tx: mpsc::UnboundedSender<Action>,
components: Vec<Box<dyn Component>>, components: Vec<Box<dyn Component>>,
config: Config, config: Config,
diag_groups: DiagGroups, diag_groups: diags::Groups,
frame_rate: f64, frame_rate: f64,
last_tick_key_events: Vec<KeyEvent>, last_tick_key_events: Vec<KeyEvent>,
should_quit: bool, should_quit: bool,
@ -94,7 +96,7 @@ impl App {
Box::new(popup::Popup::new()), Box::new(popup::Popup::new()),
], ],
config: Config::new()?, config: Config::new()?,
diag_groups: DiagGroups::new(), diag_groups: diags::Groups::new(),
frame_rate, frame_rate,
last_tick_key_events: Vec::new(), last_tick_key_events: Vec::new(),
should_quit: false, should_quit: false,
@ -180,13 +182,126 @@ impl App {
self.list.select_first_item(); self.list.select_first_item();
} }
Mode::BootScan => { Mode::BootScan => {
if self.tasks.idle() {
self.tasks.add(TaskType::Sleep);
}
self.action_tx.send(Action::DisplayPopup( self.action_tx.send(Action::DisplayPopup(
popup::Type::Info, popup::Type::Info,
String::from("Gathering info..."), String::from("Gathering info..."),
))?; ))?;
// Get System32 path
let system32 = if cfg!(windows) {
if let Ok(path) = env::var("SYSTEMROOT") {
format!("{path}/System32")
} else {
self.action_tx.send(Action::Error(String::from(
"ERROR\n\n\nFailed to find SYSTEMROOT",
)))?;
return Ok(());
}
} else {
String::from(".")
};
// Add tasks
let disk_list = self.clone.disk_list.lock().unwrap();
if let Some(disk_index) = self.clone.disk_index_dest {
if let Some(disk) = disk_list.get(disk_index) {
let table_type = disk.part_type.clone();
let letter_boot = disk.get_part_letter(self.clone.part_index_boot.unwrap());
let letter_os = disk.get_part_letter(self.clone.part_index_os.unwrap());
// Safety check
if letter_boot.is_empty() || letter_os.is_empty() {
self.action_tx.send(Action::Error(String::from(
"ERROR\n\n\nFailed to get drive letters for the destination",
)))?;
return Ok(());
}
// BCD
self.tasks.add(TaskType::Command(
PathBuf::from(format!("{system32}/bcdedit.exe")),
vec![
String::from("/store"),
format!(
"{letter_boot}{}\\Boot\\BCD",
if table_type == PartitionTableType::Guid {
"\\EFI\\Microsoft"
} else {
""
}
),
String::from("/enum"),
],
));
// Bitlocker
self.tasks.add(TaskType::Command(
PathBuf::from(format!("{system32}/manage-bde.exe")),
vec![String::from("-status"), format!("{letter_os}:")],
));
self.tasks.add(TaskType::Command(
PathBuf::from(format!("{system32}/manage-bde.exe")),
vec![
String::from("-protectors"),
String::from("-get"),
format!("{letter_os}:"),
],
));
// DISM Health
self.tasks.add(TaskType::Command(
PathBuf::from(format!("{system32}/dism.exe")),
vec![format!("{letter_os}:")],
));
// Filesystem Health
self.tasks.add(TaskType::Command(
PathBuf::from(format!("{system32}/chkdsk.exe")),
vec![format!("{letter_os}:")],
));
// Registry
self.tasks.add(TaskType::Command(
PathBuf::from(format!("{system32}/reg.exe")),
vec![
String::from("load"),
String::from("HKLM\\TmpSoftware"),
format!("{letter_os}:\\Windows\\System32\\config\\SOFTWARE"),
],
));
self.tasks.add(TaskType::Command(
PathBuf::from(format!("{system32}/reg.exe")),
vec![
String::from("load"),
String::from("HKLM\\TmpSystem"),
format!("{letter_os}:\\Windows\\System32\\config\\SYSTEM"),
],
));
self.tasks.add(TaskType::Command(
PathBuf::from(format!("{system32}/reg.exe")),
vec![
String::from("load"),
String::from("HKU\\TmpDefault"),
format!("{letter_os}:\\Windows\\System32\\config\\DEFAULT"),
],
));
self.tasks.add(TaskType::Command(
PathBuf::from(format!("{system32}/reg.exe")),
vec![String::from("unload"), String::from("HKLM\\TmpSoftware")],
));
self.tasks.add(TaskType::Command(
PathBuf::from(format!("{system32}/reg.exe")),
vec![String::from("unload"), String::from("HKLM\\TmpSystem")],
));
self.tasks.add(TaskType::Command(
PathBuf::from(format!("{system32}/reg.exe")),
vec![String::from("unload"), String::from("HKU\\TmpDefault")],
));
// Files/Folders
// TODO: Check for critical folders (e.g. /Windows, /Windows/System32, etc)
}
}
} }
Mode::InjectDrivers | Mode::InstallDrivers => self.clone.scan_drivers(), Mode::InjectDrivers | Mode::InstallDrivers => self.clone.scan_drivers(),
Mode::ScanDisks => { Mode::ScanDisks => {
@ -414,8 +529,9 @@ impl App {
} }
fn handle_task(&mut self, task: &Task) -> Result<()> { fn handle_task(&mut self, task: &Task) -> Result<()> {
info!("Handling Task: {task:?}");
match self.cur_mode { match self.cur_mode {
Mode::BootDiags => { Mode::BootScan => {
if let TaskType::Command(cmd_path, cmd_args) = &task.task_type { if let TaskType::Command(cmd_path, cmd_args) = &task.task_type {
let mut cmd_name = ""; let mut cmd_name = "";
if let Some(path) = cmd_path.file_name() { if let Some(path) = cmd_path.file_name() {
@ -423,9 +539,9 @@ impl App {
cmd_name = cmd_str; cmd_name = cmd_str;
} }
}; };
match cmd_name { let diag_type = diags::get_type(cmd_name);
"bcdedit" => { match diag_type {
// Boot config diags::Type::BootConfigData => {
let title = "Boot Files"; let title = "Boot Files";
if let Some(result) = &task.result { if let Some(result) = &task.result {
let passed: bool; let passed: bool;
@ -449,18 +565,10 @@ impl App {
.update(title.to_string(), passed, info.to_owned()); .update(title.to_string(), passed, info.to_owned());
} }
} }
"manage-bde" => { diags::Type::Bitlocker => {}
// Bitlocker diags::Type::FileSystem => {}
} diags::Type::Registry => {}
"chkdsk" => { diags::Type::FilesAndFolders => {}
// File system
}
"reg" => {
// Registry
}
"dir" => {
// Files/Folders
}
_ => { _ => {
warn!("Unrecognized command: {:?}", &cmd_path) warn!("Unrecognized command: {:?}", &cmd_path)
} }
@ -660,7 +768,19 @@ fn build_left_items(app: &App) -> Action {
} }
} }
} }
Mode::BootDiags | Mode::BootSetup => { Mode::BootDiags => {
select_num = 0;
let (new_title, _) = get_mode_strings(app.cur_mode);
title = new_title;
app.diag_groups.get().iter().for_each(|group| {
items.push(if group.passed {
group.title.clone()
} else {
format!("{} - Issues detected!", group.title)
});
});
}
Mode::BootSetup => {
select_num = 0; select_num = 0;
let (new_title, _) = get_mode_strings(app.cur_mode); let (new_title, _) = get_mode_strings(app.cur_mode);
title = new_title; title = new_title;
@ -715,6 +835,14 @@ fn build_right_items(app: &App) -> Action {
)], )],
line_colors: vec![Color::Reset], line_colors: vec![Color::Reset],
}, },
DVLine {
line_parts: vec![String::from("-----")],
line_colors: vec![Color::Reset],
},
DVLine {
line_parts: vec![format!("diag_groups: {:?}", &app.diag_groups)],
line_colors: vec![Color::Yellow],
},
DVLine { DVLine {
line_parts: vec![String::from("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")], line_parts: vec![String::from("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")],
line_colors: vec![Color::Reset], line_colors: vec![Color::Reset],
@ -774,6 +902,20 @@ fn build_right_items(app: &App) -> Action {
]); ]);
}); });
} }
Mode::BootDiags => {
app.diag_groups.get().iter().for_each(|group| {
let mut lines = Vec::new();
group.info.iter().for_each(|text| {
text.lines().for_each(|line| {
lines.push(DVLine {
line_parts: vec![String::from(line)],
line_colors: vec![Color::Reset],
});
});
});
items.push(lines);
});
}
Mode::InjectDrivers | Mode::InstallDrivers => { Mode::InjectDrivers | Mode::InstallDrivers => {
items.push(vec![DVLine { items.push(vec![DVLine {
line_parts: vec![String::from("CPU")], line_parts: vec![String::from("CPU")],

View file

@ -15,21 +15,44 @@
use std::collections::HashMap; use std::collections::HashMap;
pub enum Type {
Bitlocker,
BootConfigData,
FileSystem,
FilesAndFolders,
Registry,
Unknown,
}
#[derive(Debug)]
pub struct Groups { pub struct Groups {
items: HashMap<String, Line>, items: HashMap<String, Line>,
order: Vec<String>,
} }
impl Groups { impl Groups {
pub fn new() -> Self { pub fn new() -> Self {
Groups { Groups {
items: HashMap::new(), items: HashMap::new(),
order: Vec::new(),
} }
} }
pub fn get(&self) -> Vec<&Line> {
let mut lines = Vec::new();
self.order.iter().for_each(|key| {
if let Some(line) = self.items.get(key) {
lines.push(line);
}
});
lines
}
pub fn update(&mut self, title: String, passed: bool, info: String) { pub fn update(&mut self, title: String, passed: bool, info: String) {
if let Some(line) = self.items.get_mut(&title) { if let Some(line) = self.items.get_mut(&title) {
line.update(passed, info); line.update(passed, info);
} else { } else {
self.order.push(title.clone());
self.items.insert( self.items.insert(
title.clone(), title.clone(),
Line { Line {
@ -42,10 +65,11 @@ impl Groups {
} }
} }
#[derive(Clone, Debug)]
pub struct Line { pub struct Line {
title: String, pub title: String,
passed: bool, pub passed: bool,
info: Vec<String>, pub info: Vec<String>,
} }
impl Line { impl Line {
@ -54,3 +78,25 @@ impl Line {
self.info.push(info); self.info.push(info);
} }
} }
pub fn get_type(cmd_name: &str) -> Type {
if cmd_name == "exa" {
return Type::BootConfigData;
}
if cmd_name == "bcdedit" {
return Type::BootConfigData;
}
if cmd_name == "dir" {
return Type::FilesAndFolders;
}
if cmd_name == "reg" {
return Type::Registry;
}
if cmd_name == "chkdsk" {
return Type::FileSystem;
}
if cmd_name == "manage-bde" {
return Type::Bitlocker;
}
Type::Unknown
}

View file

@ -173,6 +173,11 @@ impl App {
))?; ))?;
} }
Mode::PreClone => { Mode::PreClone => {
self.action_tx.send(Action::DisplayPopup(
popup::Type::Info,
String::from("Formatting destination disk"),
))?;
// Get System32 path // Get System32 path
let system32 = if cfg!(windows) { let system32 = if cfg!(windows) {
if let Ok(path) = env::var("SYSTEMROOT") { if let Ok(path) = env::var("SYSTEMROOT") {
@ -187,11 +192,6 @@ impl App {
String::from(".") String::from(".")
}; };
self.action_tx.send(Action::DisplayPopup(
popup::Type::Info,
String::from("Formatting destination disk"),
))?;
// (Re)Enable volume mounting // (Re)Enable volume mounting
self.tasks.add(TaskType::Command( self.tasks.add(TaskType::Command(
PathBuf::from(format!("{system32}/mountvol.exe")), PathBuf::from(format!("{system32}/mountvol.exe")),