Add launch crash detection with detailed error dialog, fix all warnings
Detect AppImages that crash immediately after spawning (within 1.5s) by capturing stderr and using try_wait(). Show a full AlertDialog with a plain-text explanation, scrollable error output, and a copy-to-clipboard button. Covers Qt plugin errors, missing libraries, segfaults, permission issues, and display connection failures. Move launch operations to background threads in both the detail view and context menu to avoid blocking the UI during the 1.5s crash detection window. Suppress all 57 compiler warnings across future-use modules (backup, notification, report, watcher) and individual unused fields/variants in other core modules.
This commit is contained in:
@@ -15,6 +15,7 @@ const MAX_CONCURRENT_ANALYSES: usize = 2;
|
||||
static RUNNING_ANALYSES: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
/// Returns the number of currently running background analyses.
|
||||
#[allow(dead_code)]
|
||||
pub fn running_count() -> usize {
|
||||
RUNNING_ANALYSES.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
@@ -405,6 +405,7 @@ fn summarize_content_rating(attrs: &[(String, String)]) -> String {
|
||||
// AppStream catalog generation - writes catalog XML for GNOME Software/Discover
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[allow(dead_code)]
|
||||
/// Generate an AppStream catalog XML from the Driftwood database.
|
||||
/// This allows GNOME Software / KDE Discover to see locally managed AppImages.
|
||||
pub fn generate_catalog(db: &Database) -> Result<String, AppStreamError> {
|
||||
@@ -462,6 +463,7 @@ pub fn generate_catalog(db: &Database) -> Result<String, AppStreamError> {
|
||||
Ok(xml)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
/// Install the AppStream catalog to the local swcatalog directory.
|
||||
/// GNOME Software reads from `~/.local/share/swcatalog/xml/`.
|
||||
pub fn install_catalog(db: &Database) -> Result<PathBuf, AppStreamError> {
|
||||
@@ -482,6 +484,7 @@ pub fn install_catalog(db: &Database) -> Result<PathBuf, AppStreamError> {
|
||||
Ok(catalog_path)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
/// Remove the AppStream catalog from the local swcatalog directory.
|
||||
pub fn uninstall_catalog() -> Result<(), AppStreamError> {
|
||||
let catalog_path = dirs::data_dir()
|
||||
@@ -498,6 +501,7 @@ pub fn uninstall_catalog() -> Result<(), AppStreamError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
/// Check if the AppStream catalog is currently installed.
|
||||
pub fn is_catalog_installed() -> bool {
|
||||
let catalog_path = dirs::data_dir()
|
||||
@@ -511,6 +515,7 @@ pub fn is_catalog_installed() -> bool {
|
||||
|
||||
// --- Utility functions ---
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn make_component_id(name: &str) -> String {
|
||||
name.chars()
|
||||
.map(|c| if c.is_alphanumeric() || c == '-' || c == '.' { c.to_ascii_lowercase() } else { '_' })
|
||||
@@ -519,6 +524,7 @@ fn make_component_id(name: &str) -> String {
|
||||
.to_string()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn xml_escape(s: &str) -> String {
|
||||
s.replace('&', "&")
|
||||
.replace('<', "<")
|
||||
@@ -530,6 +536,7 @@ fn xml_escape(s: &str) -> String {
|
||||
// --- Error types ---
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub enum AppStreamError {
|
||||
Database(String),
|
||||
Io(String),
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::fs;
|
||||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
@@ -185,6 +185,7 @@ pub struct ConfigBackupRecord {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub struct CatalogSourceRecord {
|
||||
pub id: i64,
|
||||
pub name: String,
|
||||
@@ -196,6 +197,7 @@ pub struct CatalogSourceRecord {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub struct CatalogAppRecord {
|
||||
pub id: i64,
|
||||
pub source_id: i64,
|
||||
|
||||
@@ -38,6 +38,7 @@ pub struct AppImageMetadata {
|
||||
pub app_version: Option<String>,
|
||||
pub description: Option<String>,
|
||||
pub developer: Option<String>,
|
||||
#[allow(dead_code)]
|
||||
pub icon_name: Option<String>,
|
||||
pub categories: Vec<String>,
|
||||
pub desktop_entry_content: String,
|
||||
|
||||
@@ -42,6 +42,7 @@ pub enum LaunchMethod {
|
||||
/// Extract-and-run fallback (APPIMAGE_EXTRACT_AND_RUN=1)
|
||||
ExtractAndRun,
|
||||
/// Via firejail sandbox
|
||||
#[allow(dead_code)]
|
||||
Sandboxed,
|
||||
}
|
||||
|
||||
@@ -58,11 +59,18 @@ impl LaunchMethod {
|
||||
/// Result of a launch attempt.
|
||||
#[derive(Debug)]
|
||||
pub enum LaunchResult {
|
||||
/// Successfully spawned the process.
|
||||
/// Successfully spawned the process and it's still running.
|
||||
Started {
|
||||
child: Child,
|
||||
method: LaunchMethod,
|
||||
},
|
||||
/// Process spawned but crashed immediately (within ~1 second).
|
||||
Crashed {
|
||||
exit_code: Option<i32>,
|
||||
stderr: String,
|
||||
#[allow(dead_code)]
|
||||
method: LaunchMethod,
|
||||
},
|
||||
/// Failed to launch.
|
||||
Failed(String),
|
||||
}
|
||||
@@ -155,20 +163,56 @@ fn execute_appimage(
|
||||
cmd.env(key, value);
|
||||
}
|
||||
|
||||
// Detach from our process group so the app runs independently
|
||||
// Capture stderr to detect crash messages, stdin detached
|
||||
cmd.stdin(Stdio::null());
|
||||
cmd.stderr(Stdio::piped());
|
||||
|
||||
match cmd.spawn() {
|
||||
Ok(child) => LaunchResult::Started {
|
||||
child,
|
||||
method: method.clone(),
|
||||
},
|
||||
Err(e) => LaunchResult::Failed(format!("Failed to spawn process: {}", e)),
|
||||
Ok(mut child) => {
|
||||
// Brief wait to detect immediate crashes (e.g. missing Qt plugins)
|
||||
std::thread::sleep(std::time::Duration::from_millis(1500));
|
||||
match child.try_wait() {
|
||||
Ok(Some(status)) => {
|
||||
// Process already exited - it crashed
|
||||
let stderr = child
|
||||
.stderr
|
||||
.take()
|
||||
.and_then(|mut err| {
|
||||
let mut buf = String::new();
|
||||
use std::io::Read;
|
||||
err.read_to_string(&mut buf).ok()?;
|
||||
Some(buf)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
LaunchResult::Crashed {
|
||||
exit_code: status.code(),
|
||||
stderr,
|
||||
method: method.clone(),
|
||||
}
|
||||
}
|
||||
Ok(None) => {
|
||||
// Still running - success
|
||||
LaunchResult::Started {
|
||||
child,
|
||||
method: method.clone(),
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
// Can't check status, assume it's running
|
||||
LaunchResult::Started {
|
||||
child,
|
||||
method: method.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => LaunchResult::Failed(format!("Failed to start: {}", e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a launch_args string from the database into a Vec of individual arguments.
|
||||
/// Splits on whitespace; returns an empty Vec if the input is None or empty.
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_launch_args(args: Option<&str>) -> Vec<String> {
|
||||
args.map(|s| s.split_whitespace().map(String::from).collect())
|
||||
.unwrap_or_default()
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use super::database::Database;
|
||||
use super::security;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use super::database::{CveSummary, Database};
|
||||
use crate::config::VERSION;
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ pub struct CveMatch {
|
||||
/// Result of a security scan for a single AppImage.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SecurityScanResult {
|
||||
#[allow(dead_code)]
|
||||
pub appimage_id: i64,
|
||||
pub libraries: Vec<BundledLibrary>,
|
||||
pub cve_matches: Vec<(BundledLibrary, Vec<CveMatch>)>,
|
||||
|
||||
@@ -390,6 +390,7 @@ fn extract_update_info_runtime(path: &Path) -> Option<String> {
|
||||
// -- GitHub/GitLab API types for JSON deserialization --
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[allow(dead_code)]
|
||||
struct GhRelease {
|
||||
tag_name: String,
|
||||
name: Option<String>,
|
||||
@@ -405,6 +406,7 @@ struct GhAsset {
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[allow(dead_code)]
|
||||
struct GlRelease {
|
||||
tag_name: String,
|
||||
name: Option<String>,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::sync::mpsc;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -307,9 +307,11 @@ pub fn detect_desktop_environment() -> String {
|
||||
/// Result of analyzing a running process for Wayland usage.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RuntimeAnalysis {
|
||||
#[allow(dead_code)]
|
||||
pub pid: u32,
|
||||
pub has_wayland_socket: bool,
|
||||
pub has_x11_connection: bool,
|
||||
#[allow(dead_code)]
|
||||
pub env_vars: Vec<(String, String)>,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user