Compare commits
No commits in common. "e6a998fa37143a7940bb6495e0630a31fc0fd17b" and "121beeaa78746408e1ae100f776c3d9d4d1ef2ea" have entirely different histories.
e6a998fa37
...
121beeaa78
14 changed files with 61 additions and 421 deletions
|
|
@ -13,7 +13,6 @@
|
||||||
// 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;
|
|
||||||
use core::{
|
use core::{
|
||||||
action::Action,
|
action::Action,
|
||||||
components::{
|
components::{
|
||||||
|
|
@ -26,16 +25,14 @@ 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::{TaskType, Tasks},
|
||||||
tui::{Event, Tui},
|
tui::{Event, Tui},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
env,
|
env,
|
||||||
iter::zip,
|
iter::zip,
|
||||||
path::PathBuf,
|
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -47,7 +44,7 @@ use ratatui::{
|
||||||
style::Color,
|
style::Color,
|
||||||
};
|
};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use tracing::{debug, info, warn};
|
use tracing::{debug, info};
|
||||||
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
// TUI
|
// TUI
|
||||||
|
|
@ -55,7 +52,6 @@ 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: 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,
|
||||||
|
|
@ -78,7 +74,7 @@ impl App {
|
||||||
let tasks = Tasks::new(action_tx.clone(), disk_list_arc.clone());
|
let tasks = Tasks::new(action_tx.clone(), disk_list_arc.clone());
|
||||||
let mut list = StatefulList::default();
|
let mut list = StatefulList::default();
|
||||||
list.set_items(vec![
|
list.set_items(vec![
|
||||||
Mode::BootScan,
|
Mode::BootDiags,
|
||||||
Mode::BootSetup,
|
Mode::BootSetup,
|
||||||
Mode::InjectDrivers,
|
Mode::InjectDrivers,
|
||||||
Mode::SetBootMode,
|
Mode::SetBootMode,
|
||||||
|
|
@ -96,7 +92,6 @@ impl App {
|
||||||
Box::new(popup::Popup::new()),
|
Box::new(popup::Popup::new()),
|
||||||
],
|
],
|
||||||
config: Config::new()?,
|
config: Config::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,
|
||||||
|
|
@ -139,8 +134,7 @@ impl App {
|
||||||
Mode::ScanDisks => Mode::SelectDisks,
|
Mode::ScanDisks => Mode::SelectDisks,
|
||||||
Mode::SelectDisks => Mode::SelectParts,
|
Mode::SelectDisks => Mode::SelectParts,
|
||||||
Mode::SelectParts => Mode::DiagMenu,
|
Mode::SelectParts => Mode::DiagMenu,
|
||||||
Mode::BootDiags | Mode::BootSetup => Mode::DiagMenu,
|
Mode::BootDiags | Mode::BootSetup => Mode::DiagMenu, // TODO: add Mode::ProgressReport
|
||||||
Mode::BootScan => Mode::BootDiags,
|
|
||||||
Mode::InjectDrivers | Mode::SetBootMode => Mode::DiagMenu,
|
Mode::InjectDrivers | Mode::SetBootMode => Mode::DiagMenu,
|
||||||
Mode::Done => Mode::DiagMenu,
|
Mode::Done => Mode::DiagMenu,
|
||||||
Mode::Failed => Mode::Failed,
|
Mode::Failed => Mode::Failed,
|
||||||
|
|
@ -181,128 +175,6 @@ impl App {
|
||||||
self.selections[1] = None;
|
self.selections[1] = None;
|
||||||
self.list.select_first_item();
|
self.list.select_first_item();
|
||||||
}
|
}
|
||||||
Mode::BootScan => {
|
|
||||||
self.action_tx.send(Action::DisplayPopup(
|
|
||||||
popup::Type::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 => {
|
||||||
if self.tasks.idle() {
|
if self.tasks.idle() {
|
||||||
|
|
@ -422,8 +294,16 @@ impl App {
|
||||||
Action::Tick => {
|
Action::Tick => {
|
||||||
self.last_tick_key_events.drain(..);
|
self.last_tick_key_events.drain(..);
|
||||||
// Check background task(s)
|
// Check background task(s)
|
||||||
if let Some(task) = self.tasks.poll()? {
|
match self.cur_mode {
|
||||||
self.handle_task(&task)?;
|
Mode::BootDiags => {
|
||||||
|
if let Some(task) = self.tasks.poll()? {
|
||||||
|
// TODO: Impl logic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Mode::ScanDisks => {
|
||||||
|
let _ = self.tasks.poll()?;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Action::Quit => self.should_quit = true,
|
Action::Quit => self.should_quit = true,
|
||||||
|
|
@ -437,7 +317,6 @@ impl App {
|
||||||
.send(Action::DisplayPopup(popup::Type::Error, msg.clone()))?;
|
.send(Action::DisplayPopup(popup::Type::Error, msg.clone()))?;
|
||||||
self.action_tx.send(Action::SetMode(Mode::Failed))?;
|
self.action_tx.send(Action::SetMode(Mode::Failed))?;
|
||||||
}
|
}
|
||||||
Action::BootScan => self.action_tx.send(Action::SetMode(Mode::BootScan))?,
|
|
||||||
Action::InstallDriver => {
|
Action::InstallDriver => {
|
||||||
self.action_tx.send(Action::SetMode(Mode::InstallDrivers))?;
|
self.action_tx.send(Action::SetMode(Mode::InstallDrivers))?;
|
||||||
}
|
}
|
||||||
|
|
@ -528,87 +407,6 @@ impl App {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_task(&mut self, task: &Task) -> Result<()> {
|
|
||||||
info!("Handling Task: {task:?}");
|
|
||||||
match self.cur_mode {
|
|
||||||
Mode::BootScan => {
|
|
||||||
if let TaskType::Command(cmd_path, cmd_args) = &task.task_type {
|
|
||||||
let mut cmd_name = "";
|
|
||||||
if let Some(path) = cmd_path.file_name() {
|
|
||||||
if let Some(cmd_str) = path.to_str() {
|
|
||||||
cmd_name = cmd_str;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let diag_type = diags::get_type(cmd_name);
|
|
||||||
match diag_type {
|
|
||||||
diags::Type::BootConfigData => {
|
|
||||||
let title = "Boot Files";
|
|
||||||
if let Some(result) = &task.result {
|
|
||||||
let passed: bool;
|
|
||||||
let info: String;
|
|
||||||
match result {
|
|
||||||
TaskResult::Error(msg) => {
|
|
||||||
passed = false;
|
|
||||||
info = msg.to_owned();
|
|
||||||
}
|
|
||||||
TaskResult::Output(stdout, stderr, success) => {
|
|
||||||
passed = *success;
|
|
||||||
let div = if !(stdout.is_empty() || stderr.is_empty()) {
|
|
||||||
"\n\n-----------\n\n"
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
};
|
|
||||||
info = format!("{stdout}{div}{stderr}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.diag_groups
|
|
||||||
.update(title.to_string(), passed, info.to_owned());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
diags::Type::Bitlocker => {}
|
|
||||||
diags::Type::FileSystem => {}
|
|
||||||
diags::Type::Registry => {}
|
|
||||||
diags::Type::FilesAndFolders => {}
|
|
||||||
_ => {
|
|
||||||
warn!("Unrecognized command: {:?}", &cmd_path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
match task.task_type {
|
|
||||||
TaskType::Command(_, _) | TaskType::Diskpart(_) => {
|
|
||||||
// Check result
|
|
||||||
if let Some(result) = &task.result {
|
|
||||||
match result {
|
|
||||||
TaskResult::Error(msg) => {
|
|
||||||
self.action_tx
|
|
||||||
.send(Action::Error(format!("{task:?} Failed: {msg}")))?;
|
|
||||||
}
|
|
||||||
TaskResult::Output(stdout, stderr, success) => {
|
|
||||||
if !success {
|
|
||||||
let msg = if !stdout.is_empty() {
|
|
||||||
stdout.clone()
|
|
||||||
} else if !stderr.is_empty() {
|
|
||||||
stderr.clone()
|
|
||||||
} else {
|
|
||||||
String::from("Unknown Error")
|
|
||||||
};
|
|
||||||
self.action_tx.send(Action::Error(format!(
|
|
||||||
"{task:?} Failed: {msg}"
|
|
||||||
)))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, tui: &mut Tui) -> Result<()> {
|
fn render(&mut self, tui: &mut Tui) -> Result<()> {
|
||||||
tui.draw(|frame| {
|
tui.draw(|frame| {
|
||||||
if let [header, _body, footer, left, right, popup] = get_chunks(frame.area())[..] {
|
if let [header, _body, footer, left, right, popup] = get_chunks(frame.area())[..] {
|
||||||
|
|
@ -686,13 +484,9 @@ fn get_chunks(r: Rect) -> Vec<Rect> {
|
||||||
|
|
||||||
fn build_footer_string(cur_mode: Mode) -> String {
|
fn build_footer_string(cur_mode: Mode) -> String {
|
||||||
match cur_mode {
|
match cur_mode {
|
||||||
Mode::BootDiags => String::from("(r) to refresh / (q) to quit"),
|
Mode::BootDiags | Mode::BootSetup | Mode::Home | Mode::PEMenu | Mode::ScanDisks => {
|
||||||
Mode::BootScan | Mode::BootSetup | Mode::Home | Mode::ScanDisks => {
|
|
||||||
String::from("(q) to quit")
|
String::from("(q) to quit")
|
||||||
}
|
}
|
||||||
Mode::PEMenu => {
|
|
||||||
String::from("(Enter) to select / (t) for terminal / (p) to power off / (r) to restart")
|
|
||||||
}
|
|
||||||
Mode::InstallDrivers | Mode::InjectDrivers | Mode::SetBootMode => {
|
Mode::InstallDrivers | Mode::InjectDrivers | Mode::SetBootMode => {
|
||||||
String::from("(Enter) to select / (q) to quit")
|
String::from("(Enter) to select / (q) to quit")
|
||||||
}
|
}
|
||||||
|
|
@ -753,7 +547,7 @@ fn build_left_items(app: &App) -> Action {
|
||||||
.iter()
|
.iter()
|
||||||
.for_each(|disk| items.push(disk.description.to_string()));
|
.for_each(|disk| items.push(disk.description.to_string()));
|
||||||
}
|
}
|
||||||
Mode::BootScan | Mode::ScanDisks => {
|
Mode::ScanDisks => {
|
||||||
select_num = 0;
|
select_num = 0;
|
||||||
title = String::from("Processing");
|
title = String::from("Processing");
|
||||||
}
|
}
|
||||||
|
|
@ -771,19 +565,7 @@ fn build_left_items(app: &App) -> Action {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Mode::BootDiags => {
|
Mode::BootDiags | Mode::BootSetup => {
|
||||||
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;
|
||||||
|
|
@ -838,14 +620,6 @@ 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],
|
||||||
|
|
@ -905,20 +679,6 @@ 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")],
|
||||||
|
|
@ -987,7 +747,7 @@ fn build_right_items(app: &App) -> Action {
|
||||||
|
|
||||||
fn get_mode_strings(mode: Mode) -> (String, String) {
|
fn get_mode_strings(mode: Mode) -> (String, String) {
|
||||||
match mode {
|
match mode {
|
||||||
Mode::BootScan | Mode::BootDiags => (
|
Mode::BootDiags => (
|
||||||
String::from("Boot Diagnostics"),
|
String::from("Boot Diagnostics"),
|
||||||
String::from("Check for common Windows boot issues"),
|
String::from("Check for common Windows boot issues"),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,102 +0,0 @@
|
||||||
// This file is part of Deja-Vu.
|
|
||||||
//
|
|
||||||
// Deja-Vu is free software: you can redistribute it and/or modify it
|
|
||||||
// under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Deja-Vu is distributed in the hope that it will be useful, but
|
|
||||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
// See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
pub enum Type {
|
|
||||||
Bitlocker,
|
|
||||||
BootConfigData,
|
|
||||||
FileSystem,
|
|
||||||
FilesAndFolders,
|
|
||||||
Registry,
|
|
||||||
Unknown,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Groups {
|
|
||||||
items: HashMap<String, Line>,
|
|
||||||
order: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Groups {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Groups {
|
|
||||||
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) {
|
|
||||||
if let Some(line) = self.items.get_mut(&title) {
|
|
||||||
line.update(passed, info);
|
|
||||||
} else {
|
|
||||||
self.order.push(title.clone());
|
|
||||||
self.items.insert(
|
|
||||||
title.clone(),
|
|
||||||
Line {
|
|
||||||
title,
|
|
||||||
passed,
|
|
||||||
info: vec![info],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Line {
|
|
||||||
pub title: String,
|
|
||||||
pub passed: bool,
|
|
||||||
pub info: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Line {
|
|
||||||
pub fn update(&mut self, passed: bool, info: String) {
|
|
||||||
self.passed &= passed; // We fail if any tests in this group fail
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
@ -20,7 +20,6 @@ use core;
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
|
|
||||||
mod app;
|
mod app;
|
||||||
mod diags;
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
|
|
|
||||||
|
|
@ -109,13 +109,6 @@
|
||||||
"<Enter>": "Process",
|
"<Enter>": "Process",
|
||||||
"<Up>": "KeyUp",
|
"<Up>": "KeyUp",
|
||||||
"<Down>": "KeyDown",
|
"<Down>": "KeyDown",
|
||||||
"<r>": "BootScan",
|
|
||||||
"<q>": "Quit",
|
|
||||||
"<Ctrl-d>": "Quit",
|
|
||||||
"<Ctrl-c>": "Quit",
|
|
||||||
"<Ctrl-z>": "Suspend"
|
|
||||||
},
|
|
||||||
"BootScan": {
|
|
||||||
"<q>": "Quit",
|
"<q>": "Quit",
|
||||||
"<Ctrl-d>": "Quit",
|
"<Ctrl-d>": "Quit",
|
||||||
"<Ctrl-c>": "Quit",
|
"<Ctrl-c>": "Quit",
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ use crate::{components::popup::Type, line::DVLine, state::Mode, system::disk::Di
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Display, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Display, Serialize, Deserialize)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
// App (Boot-Diags)
|
// App (Boot-Diags)
|
||||||
BootScan,
|
// TODO: Add actions?
|
||||||
// App (Clone)
|
// App (Clone)
|
||||||
Highlight(usize),
|
Highlight(usize),
|
||||||
InstallDriver,
|
InstallDriver,
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ pub enum Mode {
|
||||||
// Boot Diags
|
// Boot Diags
|
||||||
DiagMenu,
|
DiagMenu,
|
||||||
BootDiags,
|
BootDiags,
|
||||||
BootScan,
|
|
||||||
BootSetup,
|
BootSetup,
|
||||||
InjectDrivers,
|
InjectDrivers,
|
||||||
SetBootMode,
|
SetBootMode,
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ use core::{
|
||||||
boot, cpu::get_cpu_name, disk::PartitionTableType, diskpart::build_dest_format_script,
|
boot, cpu::get_cpu_name, disk::PartitionTableType, diskpart::build_dest_format_script,
|
||||||
drivers,
|
drivers,
|
||||||
},
|
},
|
||||||
tasks::{Task, TaskResult, TaskType, Tasks},
|
tasks::{TaskResult, TaskType, Tasks},
|
||||||
tui::{Event, Tui},
|
tui::{Event, Tui},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
|
|
@ -116,7 +116,6 @@ impl App {
|
||||||
| Mode::PostClone => None,
|
| Mode::PostClone => None,
|
||||||
// Invalid states
|
// Invalid states
|
||||||
Mode::BootDiags
|
Mode::BootDiags
|
||||||
| Mode::BootScan
|
|
||||||
| Mode::BootSetup
|
| Mode::BootSetup
|
||||||
| Mode::DiagMenu
|
| Mode::DiagMenu
|
||||||
| Mode::InjectDrivers
|
| Mode::InjectDrivers
|
||||||
|
|
@ -141,7 +140,6 @@ impl App {
|
||||||
Mode::Failed => Mode::Failed,
|
Mode::Failed => Mode::Failed,
|
||||||
// Invalid states
|
// Invalid states
|
||||||
Mode::BootDiags
|
Mode::BootDiags
|
||||||
| Mode::BootScan
|
|
||||||
| Mode::BootSetup
|
| Mode::BootSetup
|
||||||
| Mode::DiagMenu
|
| Mode::DiagMenu
|
||||||
| Mode::InjectDrivers
|
| Mode::InjectDrivers
|
||||||
|
|
@ -173,11 +171,6 @@ 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") {
|
||||||
|
|
@ -192,6 +185,11 @@ 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")),
|
||||||
|
|
@ -375,9 +373,41 @@ impl App {
|
||||||
match action {
|
match action {
|
||||||
Action::Tick => {
|
Action::Tick => {
|
||||||
self.last_tick_key_events.drain(..);
|
self.last_tick_key_events.drain(..);
|
||||||
// Check background task(s)
|
match self.cur_mode {
|
||||||
if let Some(task) = self.tasks.poll()? {
|
Mode::ScanDisks | Mode::PreClone | Mode::Clone | Mode::PostClone => {
|
||||||
self.handle_task(&task)?;
|
// Check background task (Action::NextScreen is sent when task(s) are done)
|
||||||
|
if let Some(task) = self.tasks.poll()? {
|
||||||
|
match task.task_type {
|
||||||
|
TaskType::Command(_, _) | TaskType::Diskpart(_) => {
|
||||||
|
if let Some(result) = &task.result {
|
||||||
|
match result {
|
||||||
|
TaskResult::Error(msg) => {
|
||||||
|
self.action_tx.send(Action::Error(format!(
|
||||||
|
"{task:?} Failed: {msg}"
|
||||||
|
)))?;
|
||||||
|
}
|
||||||
|
TaskResult::Output(stdout, stderr, success) => {
|
||||||
|
if !success {
|
||||||
|
let msg = if !stdout.is_empty() {
|
||||||
|
stdout.clone()
|
||||||
|
} else if !stderr.is_empty() {
|
||||||
|
stderr.clone()
|
||||||
|
} else {
|
||||||
|
String::from("Unknown Error")
|
||||||
|
};
|
||||||
|
self.action_tx.send(Action::Error(
|
||||||
|
format!("{task:?} Failed: {msg}"),
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Action::Quit => self.should_quit = true,
|
Action::Quit => self.should_quit = true,
|
||||||
|
|
@ -518,37 +548,6 @@ impl App {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_task(&mut self, task: &Task) -> Result<()> {
|
|
||||||
match task.task_type {
|
|
||||||
TaskType::Command(_, _) | TaskType::Diskpart(_) => {
|
|
||||||
// Check result
|
|
||||||
if let Some(result) = &task.result {
|
|
||||||
match result {
|
|
||||||
TaskResult::Error(msg) => {
|
|
||||||
self.action_tx
|
|
||||||
.send(Action::Error(format!("{task:?} Failed: {msg}")))?;
|
|
||||||
}
|
|
||||||
TaskResult::Output(stdout, stderr, success) => {
|
|
||||||
if !success {
|
|
||||||
let msg = if !stdout.is_empty() {
|
|
||||||
stdout.clone()
|
|
||||||
} else if !stderr.is_empty() {
|
|
||||||
stderr.clone()
|
|
||||||
} else {
|
|
||||||
String::from("Unknown Error")
|
|
||||||
};
|
|
||||||
self.action_tx
|
|
||||||
.send(Action::Error(format!("{task:?} Failed: {msg}")))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, tui: &mut Tui) -> Result<()> {
|
fn render(&mut self, tui: &mut Tui) -> Result<()> {
|
||||||
tui.draw(|frame| {
|
tui.draw(|frame| {
|
||||||
if let [header, _body, footer, left, right, popup] = get_chunks(frame.area())[..] {
|
if let [header, _body, footer, left, right, popup] = get_chunks(frame.area())[..] {
|
||||||
|
|
@ -639,7 +638,6 @@ fn build_footer_string(cur_mode: Mode) -> String {
|
||||||
Mode::Done | Mode::Failed => String::from("(Enter) or (q) to quit"),
|
Mode::Done | Mode::Failed => String::from("(Enter) or (q) to quit"),
|
||||||
// Invalid states
|
// Invalid states
|
||||||
Mode::BootDiags
|
Mode::BootDiags
|
||||||
| Mode::BootScan
|
|
||||||
| Mode::BootSetup
|
| Mode::BootSetup
|
||||||
| Mode::DiagMenu
|
| Mode::DiagMenu
|
||||||
| Mode::InjectDrivers
|
| Mode::InjectDrivers
|
||||||
|
|
@ -710,7 +708,6 @@ fn build_left_items(app: &App, cur_mode: Mode) -> Action {
|
||||||
}
|
}
|
||||||
// Invalid states
|
// Invalid states
|
||||||
Mode::BootDiags
|
Mode::BootDiags
|
||||||
| Mode::BootScan
|
|
||||||
| Mode::BootSetup
|
| Mode::BootSetup
|
||||||
| Mode::DiagMenu
|
| Mode::DiagMenu
|
||||||
| Mode::InjectDrivers
|
| Mode::InjectDrivers
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
name = 'Boot-Diagnostics'
|
|
||||||
command = 'X:\tools\boot-diags.exe'
|
|
||||||
description = "Boot issue assessment tool"
|
|
||||||
use_conemu = true
|
|
||||||
separator = false
|
|
||||||
|
|
@ -376,9 +376,8 @@ pub fn build_command(app: &App, tool: &Tool) -> TaskType {
|
||||||
let start_index: usize;
|
let start_index: usize;
|
||||||
if tool.use_conemu {
|
if tool.use_conemu {
|
||||||
cmd_path = app.config.conemu_path.clone();
|
cmd_path = app.config.conemu_path.clone();
|
||||||
cmd_args.push(String::from("-run"));
|
|
||||||
cmd_args.push(tool.command.clone());
|
|
||||||
cmd_args.push(String::from("-new_console:n"));
|
cmd_args.push(String::from("-new_console:n"));
|
||||||
|
cmd_args.push(tool.command.clone());
|
||||||
start_index = 1;
|
start_index = 1;
|
||||||
} else {
|
} else {
|
||||||
cmd_path = PathBuf::from(tool.command.clone());
|
cmd_path = PathBuf::from(tool.command.clone());
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue