Compare commits

...

3 commits

Author SHA1 Message Date
e6a998fa37
Update PEMenu 2025-02-16 23:16:26 -08:00
2b2a5160fd
Add boot diag tasks 2025-02-15 20:30:40 -08:00
f272b0c482
WIP: New data structs for boot diags 2025-02-15 17:06:19 -08:00
14 changed files with 421 additions and 61 deletions

View file

@ -13,6 +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;
use core::{ use core::{
action::Action, action::Action,
components::{ components::{
@ -25,14 +26,16 @@ use core::{
system::{ system::{
boot::{self, SafeMode}, boot::{self, SafeMode},
cpu::get_cpu_name, cpu::get_cpu_name,
disk::PartitionTableType,
drivers, drivers,
}, },
tasks::{TaskType, Tasks}, tasks::{Task, TaskResult, 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},
}; };
@ -44,7 +47,7 @@ use ratatui::{
style::Color, style::Color,
}; };
use tokio::sync::mpsc; use tokio::sync::mpsc;
use tracing::{debug, info}; use tracing::{debug, info, warn};
pub struct App { pub struct App {
// TUI // TUI
@ -52,6 +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: 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,
@ -74,7 +78,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::BootDiags, Mode::BootScan,
Mode::BootSetup, Mode::BootSetup,
Mode::InjectDrivers, Mode::InjectDrivers,
Mode::SetBootMode, Mode::SetBootMode,
@ -92,6 +96,7 @@ 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,
@ -134,7 +139,8 @@ 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, // TODO: add Mode::ProgressReport Mode::BootDiags | Mode::BootSetup => Mode::DiagMenu,
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,
@ -175,6 +181,128 @@ 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() {
@ -294,16 +422,8 @@ 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)
match self.cur_mode { if let Some(task) = self.tasks.poll()? {
Mode::BootDiags => { self.handle_task(&task)?;
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,
@ -317,6 +437,7 @@ 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))?;
} }
@ -407,6 +528,87 @@ 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())[..] {
@ -484,9 +686,13 @@ 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 | Mode::BootSetup | Mode::Home | Mode::PEMenu | Mode::ScanDisks => { Mode::BootDiags => String::from("(r) to refresh / (q) to quit"),
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")
} }
@ -547,7 +753,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::ScanDisks => { Mode::BootScan | Mode::ScanDisks => {
select_num = 0; select_num = 0;
title = String::from("Processing"); title = String::from("Processing");
} }
@ -565,7 +771,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;
@ -620,6 +838,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],
@ -679,6 +905,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")],
@ -747,7 +987,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::BootDiags => ( Mode::BootScan | 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"),
), ),

102
boot_diags/src/diags.rs Normal file
View file

@ -0,0 +1,102 @@
// 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
}

View file

@ -20,6 +20,7 @@ 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<()> {

View file

@ -109,6 +109,13 @@
"<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",

View file

@ -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)
// TODO: Add actions? BootScan,
// App (Clone) // App (Clone)
Highlight(usize), Highlight(usize),
InstallDriver, InstallDriver,

View file

@ -33,6 +33,7 @@ pub enum Mode {
// Boot Diags // Boot Diags
DiagMenu, DiagMenu,
BootDiags, BootDiags,
BootScan,
BootSetup, BootSetup,
InjectDrivers, InjectDrivers,
SetBootMode, SetBootMode,

View file

@ -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::{TaskResult, TaskType, Tasks}, tasks::{Task, TaskResult, TaskType, Tasks},
tui::{Event, Tui}, tui::{Event, Tui},
}; };
use std::{ use std::{
@ -116,6 +116,7 @@ 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
@ -140,6 +141,7 @@ 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
@ -171,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") {
@ -185,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")),
@ -373,41 +375,9 @@ impl App {
match action { match action {
Action::Tick => { Action::Tick => {
self.last_tick_key_events.drain(..); self.last_tick_key_events.drain(..);
match self.cur_mode { // Check background task(s)
Mode::ScanDisks | Mode::PreClone | Mode::Clone | Mode::PostClone => { if let Some(task) = self.tasks.poll()? {
// Check background task (Action::NextScreen is sent when task(s) are done) self.handle_task(&task)?;
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,
@ -548,6 +518,37 @@ 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())[..] {
@ -638,6 +639,7 @@ 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
@ -708,6 +710,7 @@ 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

View file

@ -0,0 +1,5 @@
name = 'Boot-Diagnostics'
command = 'X:\tools\boot-diags.exe'
description = "Boot issue assessment tool"
use_conemu = true
separator = false

View file

@ -376,8 +376,9 @@ 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("-new_console:n")); cmd_args.push(String::from("-run"));
cmd_args.push(tool.command.clone()); cmd_args.push(tool.command.clone());
cmd_args.push(String::from("-new_console:n"));
start_index = 1; start_index = 1;
} else { } else {
cmd_path = PathBuf::from(tool.command.clone()); cmd_path = PathBuf::from(tool.command.clone());