From 57e6eee888658252f5f5e7b46124384830d89725 Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Wed, 7 May 2025 12:10:08 +0000 Subject: [PATCH 01/66] Implement RFC, T2 APIs + Implement Utils Lib --- Cargo.lock | 29 ++++++++++ Cargo.toml | 8 +++ crash_upload/Cargo.toml | 11 ++++ crash_upload/src/main.rs | 3 ++ crates/curl_interface/Cargo.toml | 8 +++ crates/curl_interface/src/lib.rs | 14 +++++ crates/json_parser_interface/Cargo.toml | 8 +++ crates/json_parser_interface/src/lib.rs | 14 +++++ crates/platform_interface/Cargo.toml | 8 +++ crates/platform_interface/src/lib.rs | 6 +++ crates/platform_interface/src/rfc_api.rs | 67 ++++++++++++++++++++++++ crates/platform_interface/src/t2_api.rs | 58 ++++++++++++++++++++ crates/utils/Cargo.toml | 8 +++ crates/utils/src/device_info.rs | 41 +++++++++++++++ crates/utils/src/lib.rs | 4 ++ 15 files changed, 287 insertions(+) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 crash_upload/Cargo.toml create mode 100644 crash_upload/src/main.rs create mode 100644 crates/curl_interface/Cargo.toml create mode 100644 crates/curl_interface/src/lib.rs create mode 100644 crates/json_parser_interface/Cargo.toml create mode 100644 crates/json_parser_interface/src/lib.rs create mode 100644 crates/platform_interface/Cargo.toml create mode 100644 crates/platform_interface/src/lib.rs create mode 100644 crates/platform_interface/src/rfc_api.rs create mode 100644 crates/platform_interface/src/t2_api.rs create mode 100644 crates/utils/Cargo.toml create mode 100644 crates/utils/src/device_info.rs create mode 100644 crates/utils/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..6683a13 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,29 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "crash_upload" +version = "0.1.0" +dependencies = [ + "curl_interface", + "json_parser_interface", + "platform_interface", + "utils", +] + +[[package]] +name = "curl_interface" +version = "0.1.0" + +[[package]] +name = "json_parser_interface" +version = "0.1.0" + +[[package]] +name = "platform_interface" +version = "0.1.0" + +[[package]] +name = "utils" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..21f4689 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] +members = [ + "crash_upload", + "crates/curl_interface", + "crates/json_parser_interface", + "crates/platform_interface", + "crates/utils" +] \ No newline at end of file diff --git a/crash_upload/Cargo.toml b/crash_upload/Cargo.toml new file mode 100644 index 0000000..c0af03e --- /dev/null +++ b/crash_upload/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "crash_upload" +version = "0.1.0" +edition = "2024" + +[dependencies] +# Internal crates +curl_interface = { path = "../crates/curl_interface" } +json_parser_interface = { path = "../crates/json_parser_interface" } +platform_interface = { path = "../crates/platform_interface" } +utils = { path = "../crates/utils" } diff --git a/crash_upload/src/main.rs b/crash_upload/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/crash_upload/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/crates/curl_interface/Cargo.toml b/crates/curl_interface/Cargo.toml new file mode 100644 index 0000000..9c6c67b --- /dev/null +++ b/crates/curl_interface/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "curl_interface" +version = "0.1.0" +edition = "2024" + +[dependencies] +# Uncomment to Link this library dynamically +#crate_type = ["dynlib"] diff --git a/crates/curl_interface/src/lib.rs b/crates/curl_interface/src/lib.rs new file mode 100644 index 0000000..b93cf3f --- /dev/null +++ b/crates/curl_interface/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/crates/json_parser_interface/Cargo.toml b/crates/json_parser_interface/Cargo.toml new file mode 100644 index 0000000..e8c0d79 --- /dev/null +++ b/crates/json_parser_interface/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "json_parser_interface" +version = "0.1.0" +edition = "2024" + +[dependencies] +# Uncomment to Link this library dynamically +#crate_type = ["dynlib"] \ No newline at end of file diff --git a/crates/json_parser_interface/src/lib.rs b/crates/json_parser_interface/src/lib.rs new file mode 100644 index 0000000..b93cf3f --- /dev/null +++ b/crates/json_parser_interface/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/crates/platform_interface/Cargo.toml b/crates/platform_interface/Cargo.toml new file mode 100644 index 0000000..c8c124b --- /dev/null +++ b/crates/platform_interface/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "platform_interface" +version = "0.1.0" +edition = "2024" + +[dependencies] +# Uncomment to Link this library dynamically +#crate_type = ["dynlib"] \ No newline at end of file diff --git a/crates/platform_interface/src/lib.rs b/crates/platform_interface/src/lib.rs new file mode 100644 index 0000000..efd0cdd --- /dev/null +++ b/crates/platform_interface/src/lib.rs @@ -0,0 +1,6 @@ +mod t2_api; +mod rfc_api; + +pub const PLATFORM_LIB_VER: &str = "v1.0"; +pub use t2_api::{t2_count_notify, t2_val_notify}; +pub use rfc_api::{set_rfc_param, get_rfc_param}; diff --git a/crates/platform_interface/src/rfc_api.rs b/crates/platform_interface/src/rfc_api.rs new file mode 100644 index 0000000..cc77525 --- /dev/null +++ b/crates/platform_interface/src/rfc_api.rs @@ -0,0 +1,67 @@ +// platform_interface/src/rfc_api.rs +use std::path::Path; +use std::process::Command; + +// Module-level constants +const TR181_BIN: &str = "tr181"; +//const TR181_SET_BIN: &str = "tr181Set"; TODO + +fn rfc_bin_path() -> &'static Path { + Path::new(TR181_BIN) +} + +/// Set a TR-181 RFC parameter to a value +pub fn set_rfc_param(rfc: R, value: V) -> bool +where + R: AsRef, + V: AsRef, +{ + let rfc_bin = rfc_bin_path(); + if rfc_bin.exists() { + match Command::new(rfc_bin).arg("-s").arg("-v").arg(value.as_ref()).arg(rfc.as_ref()).spawn() + { + Ok(_) => true, + Err(err) => { + eprintln!("{} get failed with {}", rfc_bin.display(), err); + false + } + } + } + else + { + false + } +} + +/// Get a TR-181 RFC parameter into a mutable string +pub fn get_rfc_param(rfc: R, res: &mut String) -> bool +where + R: AsRef, + +{ + let rfc_bin = rfc_bin_path(); + if rfc_bin.exists() { + match Command::new(rfc_bin).arg("-g").arg(rfc.as_ref()).output() + { + Ok(output) => { + if output.status.success() { + // Convert command output to string and update res + let output_str = String::from_utf8_lossy(&output.stdout); + *res = output_str.trim().to_string(); // Update the mutable reference + true + } + else{ + false + } + } + Err(err) => { + println!("{} get failed with {}", TR181_BIN, err); + false + } + } + } + else + { + false + } +} \ No newline at end of file diff --git a/crates/platform_interface/src/t2_api.rs b/crates/platform_interface/src/t2_api.rs new file mode 100644 index 0000000..8d08e79 --- /dev/null +++ b/crates/platform_interface/src/t2_api.rs @@ -0,0 +1,58 @@ +// platform_interface/src/t2.rs +use std::path::Path; +use std::process::Command; + +// Module-level constants +const T2_MSG_CLIENT_PATH: &str = "/usr/bin/telemetry2_0_client"; + +fn t2_msg_client_path() -> &'static Path { + Path::new(T2_MSG_CLIENT_PATH) +} + +/// Notify a telemetry marker with a count (default 1 if None) +pub fn t2_count_notify(marker: M, count: Option) -> bool +where + M: AsRef, + C: AsRef, +{ + let t2_client = t2_msg_client_path(); + if t2_client.exists() { + let count_val = count.as_ref().map(|c| c.as_ref()).unwrap_or("1"); + match Command::new(t2_client).arg(marker.as_ref()).arg(count_val).spawn() + { + Ok(_) => true, + Err(err) => { + eprintln!("{} execution failed with {}", t2_client.display(), err); + false + } + } + } + else + { + false + } +} + +/// Notify a telemetry marker with a value +pub fn t2_val_notify(marker: M, value: V) -> bool +where + M: AsRef, + V: AsRef, + +{ + let t2_client = t2_msg_client_path(); + if t2_client.exists() { + match Command::new(t2_client).arg(marker.as_ref()).arg(value.as_ref()).spawn() + { + Ok(_) => true, + Err(err) => { + eprintln!("{} execution failed with {}", t2_client.display(), err); + false + } + } + } + else + { + false + } +} diff --git a/crates/utils/Cargo.toml b/crates/utils/Cargo.toml new file mode 100644 index 0000000..92c8c32 --- /dev/null +++ b/crates/utils/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "utils" +version = "0.1.0" +edition = "2024" + +[dependencies] +# Uncomment to Link this library dynamically +#crate_type = ["dynlib"] \ No newline at end of file diff --git a/crates/utils/src/device_info.rs b/crates/utils/src/device_info.rs new file mode 100644 index 0000000..0577388 --- /dev/null +++ b/crates/utils/src/device_info.rs @@ -0,0 +1,41 @@ +// utils/src/device_info.rs +use std::fs::File; +use std::io::{BufRead, BufReader}; + +// Module-level constants +const DEVICE_PROP_FILE: &str = "/etc/device.properties"; +const INCLUDE_PROP_FILE: &str = "/etc/include.properties"; +const COMMON_PROP_FILE: &str = "/etc/common.properties"; + + +pub fn get_property_value(key: M, val: &mut String) -> bool +where + M: AsRef, +{ + let key = key.as_ref().trim(); + *val = String::new(); + + for path in [DEVICE_PROP_FILE, INCLUDE_PROP_FILE, COMMON_PROP_FILE] { + let file = match File::open(path){ + Ok(f) => f, + Err(_) => continue, + }; + + let reader = BufReader::new(file); + for line in reader.lines().flatten(){ + if let Some((k, v)) = line.split_once('=') { + if k.trim() == key { + let value = v.trim(); + if !value.is_empty() { + val.push_str(value); + return true; + } + else{ + return false; + } + } + } + } + } + false +} \ No newline at end of file diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs new file mode 100644 index 0000000..c239449 --- /dev/null +++ b/crates/utils/src/lib.rs @@ -0,0 +1,4 @@ +mod device_info; + +pub const UTILS_LIB_VER: &str = "v1.0"; +pub use device_info::{get_property_value}; From b213e61775ffdad464075f660adcb286a818f640 Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Fri, 9 May 2025 08:10:38 +0000 Subject: [PATCH 02/66] Add more API support Signed-off-by: Gomathi Shankar --- crash_upload/src/main.rs | 2 + crash_upload/src/utils.rs | 120 +++++++++++++++++++++++ crates/platform_interface/src/rfc_api.rs | 10 +- crates/platform_interface/src/t2_api.rs | 8 +- crates/utils/src/command.rs | 20 ++++ crates/utils/src/device_info.rs | 43 ++++---- crates/utils/src/lib.rs | 4 +- 7 files changed, 170 insertions(+), 37 deletions(-) create mode 100644 crash_upload/src/utils.rs create mode 100644 crates/utils/src/command.rs diff --git a/crash_upload/src/main.rs b/crash_upload/src/main.rs index e7a11a9..4b867a4 100644 --- a/crash_upload/src/main.rs +++ b/crash_upload/src/main.rs @@ -1,3 +1,5 @@ +mod utils; + fn main() { println!("Hello, world!"); } diff --git a/crash_upload/src/utils.rs b/crash_upload/src/utils.rs new file mode 100644 index 0000000..33e2dc6 --- /dev/null +++ b/crash_upload/src/utils.rs @@ -0,0 +1,120 @@ +// src/utils.rs +use std::{path::Path}; +use std::time::{SystemTime, UNIX_EPOCH}; +use std::fs; + +use platform_interface::*; +use utils::*; + +// Module-level constants +const SECUREDUMP_TR181_NAME: &str = "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.SecDump.Enable"; +const SECUREDUMP_ENABLE_FILE: &str = "/tmp/.SecureDumpEnable"; +const SECUREDUMP_DISABLE_FILE: &str = "/tmp/.SecureDumpDisable"; +const CRASH_UPLOAD_REBOOT_FLAG: &str = "/tmp/set_crash_reboot_flag"; +const DENY_UPLOAD_FILE: &str = "/tmp/.deny_dump_uploads_till"; +const SHA1_DEFAULT_VALUE: &str = "0000000000000000000000000000000000000000"; +const TIMESTAMP_DEFAULT_VALUE: &str ="2000-01-01-00-00-00"; +const MAC_DEFAULT_VALUE: &str ="000000000000"; +const MODEL_NUM_DEFAULT_VALUE: &str ="UNKNOWN"; + +pub struct DumpPaths{ + pub core_path: String, + pub minidumps_path: String, + pub core_back_path: String, + pub persistent_sec_path: String, +} + +fn set_secure_dump_flag(is_sec_dump_enabled: &mut bool) { + if Path::new(SECUREDUMP_ENABLE_FILE).exists() { + *is_sec_dump_enabled = true; + } + else if Path::new(SECUREDUMP_DISABLE_FILE).exists() + { + *is_sec_dump_enabled = false; + } + else + { + let mut rfc_value = String::new(); + if get_rfc_param(SECUREDUMP_TR181_NAME, &mut rfc_value) { + *is_sec_dump_enabled = rfc_value.trim().eq_ignore_ascii_case("true"); + } + } +} + +pub fn get_secure_dump_status() -> DumpPaths { + let mut is_sec_dump_enabled = false; + set_secure_dump_flag(&mut is_sec_dump_enabled); + + if !is_sec_dump_enabled { + if Path::new(SECUREDUMP_ENABLE_FILE).exists() { + touch(SECUREDUMP_DISABLE_FILE); + println!("[SECUREDUMP] Disabled"); + } + if Path::new(SECUREDUMP_ENABLE_FILE).exists() { + rm(SECUREDUMP_ENABLE_FILE); + } + DumpPaths { + core_path: "/var/lib/systemd/coredump".to_string(), + minidumps_path: "/opt/minidumps".to_string(), + core_back_path: "/opt/corefiles_back".to_string(), + persistent_sec_path: "/opt".to_string(), + } + } + else { + if !Path::new(SECUREDUMP_ENABLE_FILE).exists() { + touch(SECUREDUMP_ENABLE_FILE); + println!("[SECUREDUMP] Enabled. Dump location changed to /opt/secure."); + } + if Path::new(SECUREDUMP_DISABLE_FILE).exists() { + rm(SECUREDUMP_DISABLE_FILE); + } + + DumpPaths { + core_path: "/opt/secure/corefiles".to_string(), + minidumps_path: "/opt/secure/minidumps".to_string(), + core_back_path: "/opt/secure/corefiles_back".to_string(), + persistent_sec_path: "/opt/secure".to_string(), + } + } +} + +pub fn is_box_rebooting() -> bool { + if Path::new(CRASH_UPLOAD_REBOOT_FLAG).exists() { + println!("Skipping Upload, Since Box is Rebooting now..."); + t2_count_notify("SYST_INFO_CoreUpldSkipped", None::<&str>); + println!("Upload will happen on next reboot"); + return true; + } + false +} + +pub fn set_recovery_time() { + let now = SystemTime::now().duration_since(UNIX_EPOCH).expect("SystemTime before UNIX_EPOCH").as_secs(); + let dont_upload_for_sec = 600; + let recovery_time = now + dont_upload_for_sec; + fs::write(DENY_UPLOAD_FILE, recovery_time.to_string()); + +} + +pub fn is_recovery_time_reached() -> bool { + if !Path::new(DENY_UPLOAD_FILE).exists() { + return true; + } + let content = match fs::read_to_string(DENY_UPLOAD_FILE){ + Ok(val) => val.trim().to_string(), + Err(_) => return true, + }; + + let upload_denied_till = match content.parse::() { + Ok(val) => val, + Err(_) => return true + }; + + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_secs(); + + now > upload_denied_till +} + diff --git a/crates/platform_interface/src/rfc_api.rs b/crates/platform_interface/src/rfc_api.rs index cc77525..8ef45d1 100644 --- a/crates/platform_interface/src/rfc_api.rs +++ b/crates/platform_interface/src/rfc_api.rs @@ -11,10 +11,7 @@ fn rfc_bin_path() -> &'static Path { } /// Set a TR-181 RFC parameter to a value -pub fn set_rfc_param(rfc: R, value: V) -> bool -where - R: AsRef, - V: AsRef, +pub fn set_rfc_param, V: AsRef>(rfc: R, value: V) -> bool { let rfc_bin = rfc_bin_path(); if rfc_bin.exists() { @@ -34,10 +31,7 @@ where } /// Get a TR-181 RFC parameter into a mutable string -pub fn get_rfc_param(rfc: R, res: &mut String) -> bool -where - R: AsRef, - +pub fn get_rfc_param>(rfc: R, res: &mut String) -> bool { let rfc_bin = rfc_bin_path(); if rfc_bin.exists() { diff --git a/crates/platform_interface/src/t2_api.rs b/crates/platform_interface/src/t2_api.rs index 8d08e79..1bc5662 100644 --- a/crates/platform_interface/src/t2_api.rs +++ b/crates/platform_interface/src/t2_api.rs @@ -10,7 +10,7 @@ fn t2_msg_client_path() -> &'static Path { } /// Notify a telemetry marker with a count (default 1 if None) -pub fn t2_count_notify(marker: M, count: Option) -> bool +pub fn t2_count_notify, C: AsRef>(marker: M, count: Option) -> bool where M: AsRef, C: AsRef, @@ -34,11 +34,7 @@ where } /// Notify a telemetry marker with a value -pub fn t2_val_notify(marker: M, value: V) -> bool -where - M: AsRef, - V: AsRef, - +pub fn t2_val_notify, V: AsRef>(marker: M, value: V) -> bool { let t2_client = t2_msg_client_path(); if t2_client.exists() { diff --git a/crates/utils/src/command.rs b/crates/utils/src/command.rs new file mode 100644 index 0000000..15040ce --- /dev/null +++ b/crates/utils/src/command.rs @@ -0,0 +1,20 @@ +use std::fs::{self, OpenOptions}; +use std::path::Path; + +pub fn touch>(path: P){ + let _ = OpenOptions::new().create(true).write(true).open(path); +} + +pub fn rm>(path: P) { + let _ = fs::remove_file(path); +} + +pub fn rm_rf>(path: P) { + let path_ref = path.as_ref(); + let _ = if path_ref.is_dir() { + fs::remove_dir_all(path_ref) + } else { + rm(path_ref); + Ok(()) + }; +} \ No newline at end of file diff --git a/crates/utils/src/device_info.rs b/crates/utils/src/device_info.rs index 0577388..0300909 100644 --- a/crates/utils/src/device_info.rs +++ b/crates/utils/src/device_info.rs @@ -1,5 +1,6 @@ // utils/src/device_info.rs use std::fs::File; +use std::path::Path; use std::io::{BufRead, BufReader}; // Module-level constants @@ -8,31 +9,29 @@ const INCLUDE_PROP_FILE: &str = "/etc/include.properties"; const COMMON_PROP_FILE: &str = "/etc/common.properties"; -pub fn get_property_value(key: M, val: &mut String) -> bool -where - M: AsRef, +pub fn get_property_value_from_file, K: AsRef>(path: P, key: K, val: &mut String) -> bool { let key = key.as_ref().trim(); *val = String::new(); - - for path in [DEVICE_PROP_FILE, INCLUDE_PROP_FILE, COMMON_PROP_FILE] { - let file = match File::open(path){ - Ok(f) => f, - Err(_) => continue, - }; - - let reader = BufReader::new(file); - for line in reader.lines().flatten(){ - if let Some((k, v)) = line.split_once('=') { - if k.trim() == key { - let value = v.trim(); - if !value.is_empty() { - val.push_str(value); - return true; - } - else{ - return false; - } + let prop_file = match File::open(path) { + Ok(f) => f, + Err(_) => return false, + }; + let reader = BufReader::new(prop_file); + for line in reader.lines().flatten() + { + if let Some((k, v)) = line.split_once('=') + { + if k.trim() == key + { + let value = v.trim(); + if !value.is_empty() + { + val.push_str(value); + return true; + } + else{ + return false; } } } diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index c239449..bdc5cdd 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -1,4 +1,6 @@ mod device_info; +mod command; pub const UTILS_LIB_VER: &str = "v1.0"; -pub use device_info::{get_property_value}; +pub use device_info::{get_property_value_from_file}; +pub use command::*; From 8e421c472cd170d549469d85556fb1ca09c0c152 Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Mon, 12 May 2025 08:06:14 +0000 Subject: [PATCH 03/66] Add More APIs Signed-off-by: Gomathi Shankar --- crash_upload/src/utils.rs | 62 ++++++++++++++++++++++++++++++++++--- crates/utils/src/command.rs | 7 +++++ 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/crash_upload/src/utils.rs b/crash_upload/src/utils.rs index 33e2dc6..de4ac37 100644 --- a/crash_upload/src/utils.rs +++ b/crash_upload/src/utils.rs @@ -1,7 +1,9 @@ // src/utils.rs -use std::{path::Path}; -use std::time::{SystemTime, UNIX_EPOCH}; use std::fs; +use std::path::{Path, PathBuf}; +use std::time::{SystemTime, UNIX_EPOCH}; +use std::{thread, time}; +use std::process; use platform_interface::*; use utils::*; @@ -92,8 +94,7 @@ pub fn set_recovery_time() { let now = SystemTime::now().duration_since(UNIX_EPOCH).expect("SystemTime before UNIX_EPOCH").as_secs(); let dont_upload_for_sec = 600; let recovery_time = now + dont_upload_for_sec; - fs::write(DENY_UPLOAD_FILE, recovery_time.to_string()); - + let _ = fs::write(DENY_UPLOAD_FILE, recovery_time.to_string()); } pub fn is_recovery_time_reached() -> bool { @@ -118,3 +119,56 @@ pub fn is_recovery_time_reached() -> bool { now > upload_denied_till } +fn lock_path>(path: P) -> PathBuf { + let mut p = path.as_ref().to_path_buf(); + p.set_extension("lock.d"); + p +} + +fn is_another_instance_running>(path: P) -> bool { + lock_path(path).is_dir() +} + +pub fn create_lock_or_exit>(path: P) -> bool { + let lock = lock_path(&path); + if is_another_instance_running(&path) { + println!("Script is already working. {:?}. Skip launching another instance...", lock); + process::exit(0); + } + + match fs::create_dir(&lock) { + Ok(_) => true, + Err(err) => { + println!("Error creating {:?}: {}", lock, err); + false + } + } +} + +pub fn create_lock_or_wait>(path: P) -> bool { + let lock = lock_path(&path); + loop { + if is_another_instance_running(&path) { + println!("Script is already working. {:?}. Waiting to launch another instance...", lock); + thread::sleep(time::Duration::from_secs(2)); + continue; + } + + match fs::create_dir(&lock) { + Ok(_) => return true, + Err(err) => { + println!("Error creating {:?}: {}", lock, err); + return false; + } + } + } +} + +pub fn remove_lock>(path: P) { + let lock = lock_path(&path); + if lock.is_dir() { + if let Err(err) = fs::remove_dir(&lock) { + println!("Error deleting {:?}: {}", lock, err); + } + } +} \ No newline at end of file diff --git a/crates/utils/src/command.rs b/crates/utils/src/command.rs index 15040ce..38243ca 100644 --- a/crates/utils/src/command.rs +++ b/crates/utils/src/command.rs @@ -1,5 +1,7 @@ use std::fs::{self, OpenOptions}; use std::path::Path; +use std::thread; +use std::time::Duration; pub fn touch>(path: P){ let _ = OpenOptions::new().create(true).write(true).open(path); @@ -17,4 +19,9 @@ pub fn rm_rf>(path: P) { rm(path_ref); Ok(()) }; +} + +pub fn sleep(seconds: u64) -> bool { + thread::sleep(Duration::from_secs(seconds)); + true } \ No newline at end of file From 0cdf945c937879d378fc7d05018e731f91ee82de Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Mon, 12 May 2025 08:14:40 +0000 Subject: [PATCH 04/66] TODO Signed-off-by: Gomathi Shankar --- TODO.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 TODO.md diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..1897934 --- /dev/null +++ b/TODO.md @@ -0,0 +1,15 @@ +TODO: +- Separate Logging log.rs, with WARN, INFO, ERROR, etc log levels +- upload dump utils + - sanitize() + - checkParam() + - finalize() + - sigkill_function() and sigterm_function() with trap usage + - truncateTimeStampFile() + - isUploadLimitReached() + - coreUpload() -> find origin + - saveDumps() + - processDumps() + - shouldProcessFile() + - get_crash_log_file() + \ No newline at end of file From 2fdbc597c4efb4f3ba609f8fd40f1d1eb596607d Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Mon, 12 May 2025 11:17:29 +0000 Subject: [PATCH 05/66] Added APIs Signed-off-by: Gomathi Shankar --- TODO.md | 2 +- crash_upload/src/main.rs | 17 ++++++- crash_upload/src/utils.rs | 103 +++++++++++++++++++++++++++++++++++++- 3 files changed, 119 insertions(+), 3 deletions(-) diff --git a/TODO.md b/TODO.md index 1897934..6fe7efd 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,7 @@ TODO: - Separate Logging log.rs, with WARN, INFO, ERROR, etc log levels - upload dump utils - - sanitize() + - ~~sanitize()~~ - checkParam() - finalize() - sigkill_function() and sigterm_function() with trap usage diff --git a/crash_upload/src/main.rs b/crash_upload/src/main.rs index 4b867a4..c51f0e8 100644 --- a/crash_upload/src/main.rs +++ b/crash_upload/src/main.rs @@ -1,5 +1,20 @@ +use std::env; + mod utils; + + fn main() { - println!("Hello, world!"); + println!("Starting Crash Upload Binary..."); + let args: Vec = env::args().collect(); + if args.len() != 3 { + eprintln!("Usage: "); + std::process::exit(1); + } + let arg1 = &args[1]; // the string + let dump_flag = args[2].parse::().expect("Second argument must be a number"); + let dump_name = if dump_flag == 1 {"coredump"} else {"minidump"}; + let timestamp_filename = utils::get_timestamp_filename(dump_name); } + + diff --git a/crash_upload/src/utils.rs b/crash_upload/src/utils.rs index de4ac37..1b86c0d 100644 --- a/crash_upload/src/utils.rs +++ b/crash_upload/src/utils.rs @@ -1,6 +1,7 @@ // src/utils.rs -use std::fs; +use std::fs::{self, File, OpenOptions}; use std::path::{Path, PathBuf}; +use std::io::{BufRead, BufReader, BufWriter, Read, Seek, SeekFrom, Write}; use std::time::{SystemTime, UNIX_EPOCH}; use std::{thread, time}; use std::process; @@ -18,6 +19,7 @@ const SHA1_DEFAULT_VALUE: &str = "0000000000000000000000000000000000000000"; const TIMESTAMP_DEFAULT_VALUE: &str ="2000-01-01-00-00-00"; const MAC_DEFAULT_VALUE: &str ="000000000000"; const MODEL_NUM_DEFAULT_VALUE: &str ="UNKNOWN"; +const TIMESTAMP_FILENAME: &str = "/tmp/.${DUMP_NAME}_upload_timestamps"; pub struct DumpPaths{ pub core_path: String, @@ -43,6 +45,10 @@ fn set_secure_dump_flag(is_sec_dump_enabled: &mut bool) { } } +pub fn get_timestamp_filename(dump_name: &str) -> String { + format!("/tmp/.{}_upload_timestamps", dump_name) +} + pub fn get_secure_dump_status() -> DumpPaths { let mut is_sec_dump_enabled = false; set_secure_dump_flag(&mut is_sec_dump_enabled); @@ -171,4 +177,99 @@ pub fn remove_lock>(path: P) { println!("Error deleting {:?}: {}", lock, err); } } +} + +pub fn sanitize(input: &str) -> String { + input.chars().filter(|c| match *c { + 'a'..='z' | 'A'..='Z' | '0'..='9' | + '/' | ' ' | ':' | '+' | '.' | '_' | ',' | '=' | '-' => true, + _ => false, + }).collect() +} + +pub fn upload_timestamp(ts_file: &String) { + let mut dev_type = String::new(); + get_property_value_from_file("/etc/device.properties", "BUILD_TYPE", &mut dev_type); + if dev_type == "prod" { + let now = SystemTime::now().duration_since(UNIX_EPOCH).expect("SystemTime before UNIX_EPOCH").as_secs(); + + let _ = fs::write(ts_file, now.to_string()); + truncate_timestamp_file(ts_file); + } +} + +pub fn truncate_timestamp_file(ts_file: &String) { + if let Err(err) = OpenOptions::new().create(true).append(true).open(ts_file) { + eprintln!("Failed to create or open {}: {}", ts_file, err); + return; + } + + let file = match File::open(ts_file) { + Ok(f) => f, + Err(err) => { + eprintln!("Error opening file {}: {}", ts_file, err); + return; + } + }; + let reader = BufReader::new(file); + let lines: Vec = reader.lines().filter_map(Result::ok).collect(); + + // Take the last 10 lines + let last_10_lines = lines.iter().rev().take(10).cloned().collect::>(); + let last_10_lines = last_10_lines.into_iter().rev().collect::>(); + + // Write to temporary file + let tmp_file_path = format!("{}_tmp", ts_file); + match File::create(&tmp_file_path) { + Ok(tmp_file) => { + let mut writer = BufWriter::new(tmp_file); + for line in last_10_lines { + if let Err(err) = writeln!(writer, "{}", line){ + eprintln!("Failed to write to temp file: {}", err); + return; + } + } + } + Err(err) => { + eprintln!("Failed to create temp file {}: {}", tmp_file_path, err); + return; + } + } + + // Replace original with temp file + if let Err(err) = fs::rename(&tmp_file_path, ts_file) { + eprintln!("Failed to replace original file with temp file: {}", err); + } +} + +pub fn is_upload_limit_reached(ts_file: &String) -> bool { + let limit_seconds = 600; + if let Err(err) = OpenOptions::new().create(true).append(true).open(ts_file) { + eprintln!("Failed to create or open {}: {}", ts_file, err); + return false; + } + + let mut file = match File::open(ts_file){ + Ok(f) => f, + Err(_) => return false + }; + let mut reader: BufReader<&File> = BufReader::new(&file); + + let line_count = reader.by_ref().lines().count(); + if line_count < 0 { return false; } + + file.seek(SeekFrom::Start(0)); + + let mut reader = BufReader::new(&file); + let mut first_line = String::new(); + reader.read_line(&mut first_line); + + let tenth_newest_crash_time: u64 = first_line.split_whitespace().next().expect("No data in first line").parse().expect("Failed to parse timestamp"); + let now = SystemTime::now().duration_since(UNIX_EPOCH).expect("SystemTime before UNIX_EPOCH").as_secs(); + + if (now - tenth_newest_crash_time) < limit_seconds { + println!("Not uploading the dump. Too many dumps."); + return true; + } + false } \ No newline at end of file From effadb93cc4c4136b9d68ce585f60cf4877488f3 Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Tue, 13 May 2025 12:20:06 +0000 Subject: [PATCH 06/66] Added more API Support Signed-off-by: Gomathi Shankar --- Cargo.lock | 288 ++++++++++++++++++++++++++++++++ TODO.md | 3 + crash_upload/Cargo.toml | 1 + crash_upload/src/constants.rs | 5 + crash_upload/src/main.rs | 5 +- crash_upload/src/utils.rs | 68 +++++++- crates/utils/src/device_info.rs | 2 +- 7 files changed, 364 insertions(+), 8 deletions(-) create mode 100644 crash_upload/src/constants.rs diff --git a/Cargo.lock b/Cargo.lock index 6683a13..3ddb5e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,10 +2,73 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "cc" +version = "1.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "crash_upload" version = "0.1.0" dependencies = [ + "chrono", "curl_interface", "json_parser_interface", "platform_interface", @@ -16,14 +79,239 @@ dependencies = [ name = "curl_interface" version = "0.1.0" +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "json_parser_interface" version = "0.1.0" +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + [[package]] name = "platform_interface" version = "0.1.0" +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + [[package]] name = "utils" version = "0.1.0" + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "windows-core" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] diff --git a/TODO.md b/TODO.md index 6fe7efd..439b0d0 100644 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,8 @@ TODO: - Separate Logging log.rs, with WARN, INFO, ERROR, etc log levels +- get_sha1() -> using sha1 crate/ process::Command() if single use +- get_last_modified_time_of_file() -> using chrono crate/ process::Command() if single use +- args parsing in main app - upload dump utils - ~~sanitize()~~ - checkParam() diff --git a/crash_upload/Cargo.toml b/crash_upload/Cargo.toml index c0af03e..353b4fa 100644 --- a/crash_upload/Cargo.toml +++ b/crash_upload/Cargo.toml @@ -9,3 +9,4 @@ curl_interface = { path = "../crates/curl_interface" } json_parser_interface = { path = "../crates/json_parser_interface" } platform_interface = { path = "../crates/platform_interface" } utils = { path = "../crates/utils" } +chrono = "0.4" diff --git a/crash_upload/src/constants.rs b/crash_upload/src/constants.rs new file mode 100644 index 0000000..bfffe79 --- /dev/null +++ b/crash_upload/src/constants.rs @@ -0,0 +1,5 @@ +pub const S3_BUCKET_URL: &str = "s3.amazonaws.com"; +pub const HTTP_CODE_FILE: &str = "/tmp/httpcode"; +pub const CURL_UPLOAD_TIMEOUT: u32 = 45; +pub const FOUR_EIGHTY_SECS: u32 = 480; +pub const MAX_CORE_FILES: u32 = 4; \ No newline at end of file diff --git a/crash_upload/src/main.rs b/crash_upload/src/main.rs index c51f0e8..7e32c46 100644 --- a/crash_upload/src/main.rs +++ b/crash_upload/src/main.rs @@ -1,8 +1,7 @@ use std::env; mod utils; - - +mod constants; fn main() { println!("Starting Crash Upload Binary..."); @@ -13,6 +12,8 @@ fn main() { } let arg1 = &args[1]; // the string let dump_flag = args[2].parse::().expect("Second argument must be a number"); + let upload_flag = &args[3]; // the string + let wait_for_lock = &args[4]; // the string let dump_name = if dump_flag == 1 {"coredump"} else {"minidump"}; let timestamp_filename = utils::get_timestamp_filename(dump_name); } diff --git a/crash_upload/src/utils.rs b/crash_upload/src/utils.rs index 1b86c0d..6f295cf 100644 --- a/crash_upload/src/utils.rs +++ b/crash_upload/src/utils.rs @@ -1,10 +1,11 @@ // src/utils.rs use std::fs::{self, File, OpenOptions}; use std::path::{Path, PathBuf}; -use std::io::{BufRead, BufReader, BufWriter, Read, Seek, SeekFrom, Write}; +use std::io::{self, BufRead, BufReader, BufWriter, Read, Seek, SeekFrom, Write}; use std::time::{SystemTime, UNIX_EPOCH}; use std::{thread, time}; use std::process; +use chrono::{DateTime, Local}; use platform_interface::*; use utils::*; @@ -19,7 +20,9 @@ const SHA1_DEFAULT_VALUE: &str = "0000000000000000000000000000000000000000"; const TIMESTAMP_DEFAULT_VALUE: &str ="2000-01-01-00-00-00"; const MAC_DEFAULT_VALUE: &str ="000000000000"; const MODEL_NUM_DEFAULT_VALUE: &str ="UNKNOWN"; -const TIMESTAMP_FILENAME: &str = "/tmp/.${DUMP_NAME}_upload_timestamps"; +const LOGMAPPER_FILE: &str = "/etc/breakpad-logmapper.conf"; +const LOG_FILES: &str = "/tmp/minidump_log_files.txt"; +const LOG_PATH: &str = "/opt/rdk"; pub struct DumpPaths{ pub core_path: String, @@ -258,11 +261,11 @@ pub fn is_upload_limit_reached(ts_file: &String) -> bool { let line_count = reader.by_ref().lines().count(); if line_count < 0 { return false; } - file.seek(SeekFrom::Start(0)); + let _ = file.seek(SeekFrom::Start(0)); let mut reader = BufReader::new(&file); let mut first_line = String::new(); - reader.read_line(&mut first_line); + let _ = reader.read_line(&mut first_line); let tenth_newest_crash_time: u64 = first_line.split_whitespace().next().expect("No data in first line").parse().expect("Failed to parse timestamp"); let now = SystemTime::now().duration_since(UNIX_EPOCH).expect("SystemTime before UNIX_EPOCH").as_secs(); @@ -272,4 +275,59 @@ pub fn is_upload_limit_reached(ts_file: &String) -> bool { return true; } false -} \ No newline at end of file +} + +pub fn get_last_modified_time_of_file>(path: P) -> Option{ + let path_ref = Path::new(path.as_ref()); + + if !path_ref.is_file() { + return None; + } + let metadata = fs::metadata(path_ref).ok()?; + let modified_time: SystemTime = metadata.modified().ok()?; + // NOTE: this can be done with std::process::Command as well + let datetime: DateTime = modified_time.into(); + + Some(datetime.format("%Y-%m-%d-%H-%M-%S").to_string()) +} + +//pub fn process_crash_t2_info>(file_path: P) { +// TODO: +//} + +fn get_crashed_log_file>(file_path: P) -> io::Result<()>{ + let file = file_path.as_ref(); + + let process_name = file.rsplitn(2, '_').nth(1).unwrap_or(file).trim_start_matches("./"); + println!("Process crashed = {}", process_name); + + let app_name = file.split('_').nth(1).and_then(|s| s.split('-').next()).unwrap_or(""); + + let breakpad_mapper = BufReader::new(File::open(LOGMAPPER_FILE)?); + let mut log_files = String::new(); + for line in breakpad_mapper.lines() { + let line = line?; + if let Some((key, val)) = line.split_once('='){ + if key.contains(process_name){ + log_files = val.to_string(); + break; + } + } + } + println!("Crashed process log file(s): {}", log_files); + if !app_name.is_empty() { + println!("Appname, Process_Crashed = {} {}", app_name, process_name); + } + // Write each log file to LOG_FILES + let mut output = OpenOptions::new() + .create(true) + .append(true) + .open(LOG_FILES)?; + + for log_file in log_files.split(',').map(str::trim).filter(|s| !s.is_empty()) { + writeln!(output, "{}/{}", LOG_PATH, log_file)?; + } + + Ok(()) +} + \ No newline at end of file diff --git a/crates/utils/src/device_info.rs b/crates/utils/src/device_info.rs index 0300909..51a38d7 100644 --- a/crates/utils/src/device_info.rs +++ b/crates/utils/src/device_info.rs @@ -8,7 +8,7 @@ const DEVICE_PROP_FILE: &str = "/etc/device.properties"; const INCLUDE_PROP_FILE: &str = "/etc/include.properties"; const COMMON_PROP_FILE: &str = "/etc/common.properties"; - +// use this fn to getModel() using "MODEL_NUM" key pub fn get_property_value_from_file, K: AsRef>(path: P, key: K, val: &mut String) -> bool { let key = key.as_ref().trim(); From 41a5b2f2dd40475017481c3f6e6c3050f29eec83 Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Wed, 14 May 2025 11:55:21 +0000 Subject: [PATCH 07/66] Add Test Directory, Code Format with rustfmt Signed-off-by: Gomathi Shankar --- Cargo.lock | 35 ++ Cargo.toml | 7 +- crash_upload/src/constants.rs | 61 ++- .../src/{utils.rs => crashupload_utils.rs} | 417 +++++++++++------- crash_upload/src/main.rs | 85 +++- crates/platform_interface/src/lib.rs | 4 +- crates/platform_interface/src/rfc_api.rs | 31 +- crates/platform_interface/src/t2_api.rs | 27 +- crates/utils/src/command.rs | 4 +- crates/utils/src/device_info.rs | 22 +- crates/utils/src/lib.rs | 6 +- test/crash_upload_test/Cargo.toml | 7 + test/crash_upload_test/src/main.rs | 3 + test/curl_interface_test/Cargo.toml | 7 + test/curl_interface_test/src/main.rs | 3 + test/json_parser_interface_test/Cargo.toml | 7 + test/json_parser_interface_test/src/main.rs | 3 + test/platform_interface_test/Cargo.toml | 7 + test/platform_interface_test/src/main.rs | 3 + test/utils_test/Cargo.toml | 7 + test/utils_test/src/main.rs | 86 ++++ 21 files changed, 596 insertions(+), 236 deletions(-) rename crash_upload/src/{utils.rs => crashupload_utils.rs} (52%) create mode 100644 test/crash_upload_test/Cargo.toml create mode 100644 test/crash_upload_test/src/main.rs create mode 100644 test/curl_interface_test/Cargo.toml create mode 100644 test/curl_interface_test/src/main.rs create mode 100644 test/json_parser_interface_test/Cargo.toml create mode 100644 test/json_parser_interface_test/src/main.rs create mode 100644 test/platform_interface_test/Cargo.toml create mode 100644 test/platform_interface_test/src/main.rs create mode 100644 test/utils_test/Cargo.toml create mode 100644 test/utils_test/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 3ddb5e1..15ff0f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -75,10 +75,24 @@ dependencies = [ "utils", ] +[[package]] +name = "crash_upload_test" +version = "0.1.0" +dependencies = [ + "crash_upload", +] + [[package]] name = "curl_interface" version = "0.1.0" +[[package]] +name = "curl_interface_test" +version = "0.1.0" +dependencies = [ + "curl_interface", +] + [[package]] name = "iana-time-zone" version = "0.1.63" @@ -117,6 +131,13 @@ dependencies = [ name = "json_parser_interface" version = "0.1.0" +[[package]] +name = "json_parser_interface_test" +version = "0.1.0" +dependencies = [ + "json_parser_interface", +] + [[package]] name = "libc" version = "0.2.172" @@ -148,6 +169,13 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" name = "platform_interface" version = "0.1.0" +[[package]] +name = "platform_interface_test" +version = "0.1.0" +dependencies = [ + "platform_interface", +] + [[package]] name = "proc-macro2" version = "1.0.95" @@ -199,6 +227,13 @@ checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" name = "utils" version = "0.1.0" +[[package]] +name = "utils_test" +version = "0.1.0" +dependencies = [ + "utils", +] + [[package]] name = "wasm-bindgen" version = "0.2.100" diff --git a/Cargo.toml b/Cargo.toml index 21f4689..bc09585 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,5 +4,10 @@ members = [ "crates/curl_interface", "crates/json_parser_interface", "crates/platform_interface", - "crates/utils" + "crates/utils", + "test/crash_upload_test", + "test/curl_interface_test", + "test/json_parser_interface_test", + "test/platform_interface_test", + "test/utils_test", ] \ No newline at end of file diff --git a/crash_upload/src/constants.rs b/crash_upload/src/constants.rs index bfffe79..aec40a7 100644 --- a/crash_upload/src/constants.rs +++ b/crash_upload/src/constants.rs @@ -1,5 +1,64 @@ +// src/constants.rs +pub const LOGMAPPER_FILE: &str = "/etc/breakpad-logmapper.conf"; +pub const LOG_FILES: &str = "/tmp/minidump_log_files.txt"; +pub const LOG_PATH: &str = "/opt/rdk"; +pub const CORE_LOG: &str = "/opt/rdk/core_log.txt"; + pub const S3_BUCKET_URL: &str = "s3.amazonaws.com"; pub const HTTP_CODE_FILE: &str = "/tmp/httpcode"; pub const CURL_UPLOAD_TIMEOUT: u32 = 45; pub const FOUR_EIGHTY_SECS: u32 = 480; -pub const MAX_CORE_FILES: u32 = 4; \ No newline at end of file +pub const MAX_CORE_FILES: u32 = 4; + +pub const TLS: &str = "--tlsv1.2"; + +pub const enable_oscp_stapling: &str = "/tmp/.EnableOCSPStapling"; +pub const enable_oscp: &str = "/tmp/.EnableOCSPCA"; + +pub const SECUREDUMP_TR181_NAME: &str = "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.SecDump.Enable"; +pub const SECUREDUMP_ENABLE_FILE: &str = "/tmp/.SecureDumpEnable"; +pub const SECUREDUMP_DISABLE_FILE: &str = "/tmp/.SecureDumpDisable"; +pub const CRASH_UPLOAD_REBOOT_FLAG: &str = "/tmp/set_crash_reboot_flag"; +pub const DENY_UPLOAD_FILE: &str = "/tmp/.deny_dump_uploads_till"; + +pub struct DumpPaths { + pub core_path: String, + pub minidumps_path: String, + pub core_back_path: String, + pub persistent_sec_path: String, +} + +impl DumpPaths { + pub fn set_core_path(&mut self, path: String) { + self.core_path = path; + } + pub fn set_minidumps_path(&mut self, path: String) { + self.minidumps_path = path; + } + pub fn set_core_back_path(&mut self, path: String) { + self.core_back_path = path; + } + pub fn set_persistent_sec_path(&mut self, path: String) { + self.persistent_sec_path = path; + } + pub fn get_core_path(&self) -> &String { + &self.core_path + } + pub fn get_minidumps_path(&self) -> &String { + &self.minidumps_path + } + pub fn get_core_back_path(&self) -> &String { + &self.core_back_path + } + pub fn get_persistent_sec_path(&self) -> &String { + &self.persistent_sec_path + } + pub fn new() -> Self { + DumpPaths { + core_path: String::new(), + minidumps_path: String::new(), + core_back_path: String::new(), + persistent_sec_path: String::new(), + } + } +} diff --git a/crash_upload/src/utils.rs b/crash_upload/src/crashupload_utils.rs similarity index 52% rename from crash_upload/src/utils.rs rename to crash_upload/src/crashupload_utils.rs index 6f295cf..3d088a9 100644 --- a/crash_upload/src/utils.rs +++ b/crash_upload/src/crashupload_utils.rs @@ -1,46 +1,80 @@ // src/utils.rs +use chrono::{DateTime, Local}; use std::fs::{self, File, OpenOptions}; -use std::path::{Path, PathBuf}; use std::io::{self, BufRead, BufReader, BufWriter, Read, Seek, SeekFrom, Write}; +use std::path::{Path, PathBuf}; +use std::process; use std::time::{SystemTime, UNIX_EPOCH}; use std::{thread, time}; -use std::process; -use chrono::{DateTime, Local}; use platform_interface::*; use utils::*; +use crate::constants::*; // Module-level constants -const SECUREDUMP_TR181_NAME: &str = "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.SecDump.Enable"; -const SECUREDUMP_ENABLE_FILE: &str = "/tmp/.SecureDumpEnable"; -const SECUREDUMP_DISABLE_FILE: &str = "/tmp/.SecureDumpDisable"; -const CRASH_UPLOAD_REBOOT_FLAG: &str = "/tmp/set_crash_reboot_flag"; -const DENY_UPLOAD_FILE: &str = "/tmp/.deny_dump_uploads_till"; const SHA1_DEFAULT_VALUE: &str = "0000000000000000000000000000000000000000"; -const TIMESTAMP_DEFAULT_VALUE: &str ="2000-01-01-00-00-00"; -const MAC_DEFAULT_VALUE: &str ="000000000000"; -const MODEL_NUM_DEFAULT_VALUE: &str ="UNKNOWN"; -const LOGMAPPER_FILE: &str = "/etc/breakpad-logmapper.conf"; -const LOG_FILES: &str = "/tmp/minidump_log_files.txt"; -const LOG_PATH: &str = "/opt/rdk"; - -pub struct DumpPaths{ - pub core_path: String, - pub minidumps_path: String, - pub core_back_path: String, - pub persistent_sec_path: String, +const TIMESTAMP_DEFAULT_VALUE: &str = "2000-01-01-00-00-00"; +const MAC_DEFAULT_VALUE: &str = "000000000000"; +const MODEL_NUM_DEFAULT_VALUE: &str = "UNKNOWN"; + +pub fn should_exit_crash_upload(dump_paths: &DumpPaths) -> bool { + let minidumps_exists = check_minidumps_exist(&dump_paths.minidumps_path); + let core_exists = check_core_exist(&dump_paths.core_path); + + !(minidumps_exists || core_exists) +} + +fn check_minidumps_exist(minidump_dir: &str) -> bool { + let path = Path::new(minidump_dir); + if let Ok(entries) = fs::read_dir(path) { + for entry in entries.flatten() { + if let Ok(file_name) = entry.file_name().into_string() { + if file_name.ends_with(".dmp") || file_name.contains(".dmp") { + return true; + } + } + } + } + false +} + +fn check_core_exist(core_dir: &str) -> bool { + let path = Path::new(core_dir); + if let Ok(entries) = fs::read_dir(path) { + for entry in entries.flatten() { + if let Ok(file_name) = entry.file_name().into_string() { + if file_name.contains("_core") { + return true; + } + } + } + } + false +} + +pub fn set_log_file(sha1: &str, mac: &str, log_mod_ts: &str, box_type: &str, mod_num: &str, line: &str) { + let file_name = line.split('/').last().unwrap_or_else(|| line); + let file_processed = file_name.contains("_mac") + || file_name.contains("_dat") + || file_name.contains("_box") + || file_name.contains("_mod"); + if file_processed { + println!("{}", file_name); + println!("Core name is already processed"); + } else { + println!( + "{}_mac{}_dat{}_box{}_mod{}_{}", + sha1, mac, log_mod_ts, box_type, mod_num, file_name + ); + } } fn set_secure_dump_flag(is_sec_dump_enabled: &mut bool) { if Path::new(SECUREDUMP_ENABLE_FILE).exists() { *is_sec_dump_enabled = true; - } - else if Path::new(SECUREDUMP_DISABLE_FILE).exists() - { + } else if Path::new(SECUREDUMP_DISABLE_FILE).exists() { *is_sec_dump_enabled = false; - } - else - { + } else { let mut rfc_value = String::new(); if get_rfc_param(SECUREDUMP_TR181_NAME, &mut rfc_value) { *is_sec_dump_enabled = rfc_value.trim().eq_ignore_ascii_case("true"); @@ -48,11 +82,7 @@ fn set_secure_dump_flag(is_sec_dump_enabled: &mut bool) { } } -pub fn get_timestamp_filename(dump_name: &str) -> String { - format!("/tmp/.{}_upload_timestamps", dump_name) -} - -pub fn get_secure_dump_status() -> DumpPaths { +pub fn get_secure_dump_status(dump_paths: &mut DumpPaths) { let mut is_sec_dump_enabled = false; set_secure_dump_flag(&mut is_sec_dump_enabled); @@ -64,14 +94,11 @@ pub fn get_secure_dump_status() -> DumpPaths { if Path::new(SECUREDUMP_ENABLE_FILE).exists() { rm(SECUREDUMP_ENABLE_FILE); } - DumpPaths { - core_path: "/var/lib/systemd/coredump".to_string(), - minidumps_path: "/opt/minidumps".to_string(), - core_back_path: "/opt/corefiles_back".to_string(), - persistent_sec_path: "/opt".to_string(), - } - } - else { + dump_paths.set_core_path("/var/lib/systemd/coredump".to_string()); + dump_paths.set_minidumps_path("/opt/minidumps".to_string()); + dump_paths.set_core_back_path("/opt/corefiles_back".to_string()); + dump_paths.set_persistent_sec_path("/opt".to_string()); + } else { if !Path::new(SECUREDUMP_ENABLE_FILE).exists() { touch(SECUREDUMP_ENABLE_FILE); println!("[SECUREDUMP] Enabled. Dump location changed to /opt/secure."); @@ -79,69 +106,20 @@ pub fn get_secure_dump_status() -> DumpPaths { if Path::new(SECUREDUMP_DISABLE_FILE).exists() { rm(SECUREDUMP_DISABLE_FILE); } - - DumpPaths { - core_path: "/opt/secure/corefiles".to_string(), - minidumps_path: "/opt/secure/minidumps".to_string(), - core_back_path: "/opt/secure/corefiles_back".to_string(), - persistent_sec_path: "/opt/secure".to_string(), - } + dump_paths.set_core_path("/opt/secure/corefiles".to_string()); + dump_paths.set_minidumps_path("/opt/secure/minidumps".to_string()); + dump_paths.set_core_back_path("/opt/secure/corefiles_back".to_string()); + dump_paths.set_persistent_sec_path("/opt/secure".to_string()); } } -pub fn is_box_rebooting() -> bool { - if Path::new(CRASH_UPLOAD_REBOOT_FLAG).exists() { - println!("Skipping Upload, Since Box is Rebooting now..."); - t2_count_notify("SYST_INFO_CoreUpldSkipped", None::<&str>); - println!("Upload will happen on next reboot"); - return true; - } - false -} - -pub fn set_recovery_time() { - let now = SystemTime::now().duration_since(UNIX_EPOCH).expect("SystemTime before UNIX_EPOCH").as_secs(); - let dont_upload_for_sec = 600; - let recovery_time = now + dont_upload_for_sec; - let _ = fs::write(DENY_UPLOAD_FILE, recovery_time.to_string()); -} - -pub fn is_recovery_time_reached() -> bool { - if !Path::new(DENY_UPLOAD_FILE).exists() { - return true; - } - let content = match fs::read_to_string(DENY_UPLOAD_FILE){ - Ok(val) => val.trim().to_string(), - Err(_) => return true, - }; - - let upload_denied_till = match content.parse::() { - Ok(val) => val, - Err(_) => return true - }; - - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap_or_default() - .as_secs(); - - now > upload_denied_till -} - -fn lock_path>(path: P) -> PathBuf { - let mut p = path.as_ref().to_path_buf(); - p.set_extension("lock.d"); - p -} - -fn is_another_instance_running>(path: P) -> bool { - lock_path(path).is_dir() -} - pub fn create_lock_or_exit>(path: P) -> bool { let lock = lock_path(&path); if is_another_instance_running(&path) { - println!("Script is already working. {:?}. Skip launching another instance...", lock); + println!( + "Script is already working. {:?}. Skip launching another instance...", + lock + ); process::exit(0); } @@ -158,7 +136,10 @@ pub fn create_lock_or_wait>(path: P) -> bool { let lock = lock_path(&path); loop { if is_another_instance_running(&path) { - println!("Script is already working. {:?}. Waiting to launch another instance...", lock); + println!( + "Script is already working. {:?}. Waiting to launch another instance...", + lock + ); thread::sleep(time::Duration::from_secs(2)); continue; } @@ -182,84 +163,54 @@ pub fn remove_lock>(path: P) { } } -pub fn sanitize(input: &str) -> String { - input.chars().filter(|c| match *c { - 'a'..='z' | 'A'..='Z' | '0'..='9' | - '/' | ' ' | ':' | '+' | '.' | '_' | ',' | '=' | '-' => true, - _ => false, - }).collect() -} - -pub fn upload_timestamp(ts_file: &String) { - let mut dev_type = String::new(); - get_property_value_from_file("/etc/device.properties", "BUILD_TYPE", &mut dev_type); - if dev_type == "prod" { - let now = SystemTime::now().duration_since(UNIX_EPOCH).expect("SystemTime before UNIX_EPOCH").as_secs(); - - let _ = fs::write(ts_file, now.to_string()); - truncate_timestamp_file(ts_file); +pub fn is_box_rebooting() -> bool { + if Path::new(CRASH_UPLOAD_REBOOT_FLAG).exists() { + println!("Skipping Upload, Since Box is Rebooting now..."); + t2_count_notify("SYST_INFO_CoreUpldSkipped", None::<&str>); + println!("Upload will happen on next reboot"); + return true; } + false } -pub fn truncate_timestamp_file(ts_file: &String) { - if let Err(err) = OpenOptions::new().create(true).append(true).open(ts_file) { - eprintln!("Failed to create or open {}: {}", ts_file, err); - return; - } - - let file = match File::open(ts_file) { - Ok(f) => f, - Err(err) => { - eprintln!("Error opening file {}: {}", ts_file, err); - return; - } - }; - let reader = BufReader::new(file); - let lines: Vec = reader.lines().filter_map(Result::ok).collect(); - - // Take the last 10 lines - let last_10_lines = lines.iter().rev().take(10).cloned().collect::>(); - let last_10_lines = last_10_lines.into_iter().rev().collect::>(); - - // Write to temporary file - let tmp_file_path = format!("{}_tmp", ts_file); - match File::create(&tmp_file_path) { - Ok(tmp_file) => { - let mut writer = BufWriter::new(tmp_file); - for line in last_10_lines { - if let Err(err) = writeln!(writer, "{}", line){ - eprintln!("Failed to write to temp file: {}", err); - return; - } - } - } - Err(err) => { - eprintln!("Failed to create temp file {}: {}", tmp_file_path, err); - return; - } - } - - // Replace original with temp file - if let Err(err) = fs::rename(&tmp_file_path, ts_file) { - eprintln!("Failed to replace original file with temp file: {}", err); - } +pub fn sanitize(input: &str) -> String { + input + .chars() + .filter(|c| match *c { + 'a'..='z' + | 'A'..='Z' + | '0'..='9' + | '/' + | ' ' + | ':' + | '+' + | '.' + | '_' + | ',' + | '=' + | '-' => true, + _ => false, + }) + .collect() } pub fn is_upload_limit_reached(ts_file: &String) -> bool { let limit_seconds = 600; - if let Err(err) = OpenOptions::new().create(true).append(true).open(ts_file) { + if let Err(err) = OpenOptions::new().create(true).append(true).open(ts_file) { eprintln!("Failed to create or open {}: {}", ts_file, err); return false; } - let mut file = match File::open(ts_file){ + let mut file = match File::open(ts_file) { Ok(f) => f, - Err(_) => return false + Err(_) => return false, }; let mut reader: BufReader<&File> = BufReader::new(&file); let line_count = reader.by_ref().lines().count(); - if line_count < 0 { return false; } + if line_count < 0 { + return false; + } let _ = file.seek(SeekFrom::Start(0)); @@ -267,8 +218,16 @@ pub fn is_upload_limit_reached(ts_file: &String) -> bool { let mut first_line = String::new(); let _ = reader.read_line(&mut first_line); - let tenth_newest_crash_time: u64 = first_line.split_whitespace().next().expect("No data in first line").parse().expect("Failed to parse timestamp"); - let now = SystemTime::now().duration_since(UNIX_EPOCH).expect("SystemTime before UNIX_EPOCH").as_secs(); + let tenth_newest_crash_time: u64 = first_line + .split_whitespace() + .next() + .expect("No data in first line") + .parse() + .expect("Failed to parse timestamp"); + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("SystemTime before UNIX_EPOCH") + .as_secs(); if (now - tenth_newest_crash_time) < limit_seconds { println!("Not uploading the dump. Too many dumps."); @@ -277,7 +236,112 @@ pub fn is_upload_limit_reached(ts_file: &String) -> bool { false } -pub fn get_last_modified_time_of_file>(path: P) -> Option{ +pub fn set_recovery_time() { + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("SystemTime before UNIX_EPOCH") + .as_secs(); + let dont_upload_for_sec = 600; + let recovery_time = now + dont_upload_for_sec; + let _ = fs::write(DENY_UPLOAD_FILE, recovery_time.to_string()); +} + +pub fn is_recovery_time_reached() -> bool { + if !Path::new(DENY_UPLOAD_FILE).exists() { + return true; + } + let content = match fs::read_to_string(DENY_UPLOAD_FILE) { + Ok(val) => val.trim().to_string(), + Err(_) => return true, + }; + + let upload_denied_till = match content.parse::() { + Ok(val) => val, + Err(_) => return true, + }; + + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_secs(); + + now > upload_denied_till +} +// ============================================ + +pub fn get_timestamp_filename(dump_name: &str) -> String { + format!("/tmp/.{}_upload_timestamps", dump_name) +} + +fn lock_path>(path: P) -> PathBuf { + let mut p = path.as_ref().to_path_buf(); + p.set_extension("lock.d"); + p +} + +fn is_another_instance_running>(path: P) -> bool { + lock_path(path).is_dir() +} + +pub fn upload_timestamp(ts_file: &String) { + let mut dev_type = String::new(); + get_property_value_from_file("/etc/device.properties", "BUILD_TYPE", &mut dev_type); + if dev_type == "prod" { + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("SystemTime before UNIX_EPOCH") + .as_secs(); + + let _ = fs::write(ts_file, now.to_string()); + truncate_timestamp_file(ts_file); + } +} + +pub fn truncate_timestamp_file(ts_file: &String) { + // if let Err(err) = OpenOptions::new().create(true).append(true).open(ts_file) { + // eprintln!("Failed to create or open {}: {}", ts_file, err); + // return; + // } + + // let file = match File::open(ts_file) { + // Ok(f) => f, + // Err(err) => { + // eprintln!("Error opening file {}: {}", ts_file, err); + // return; + // } + // }; + // let reader = BufReader::new(file); + // let lines: Vec = reader.lines().filter_map(Result::ok).collect(); + + // // Take the last 10 lines + // let last_10_lines = lines.iter().rev().take(10).cloned().collect::>(); + // let last_10_lines = last_10_lines.into_iter().rev().collect::>(); + + // // Write to temporary file + // let tmp_file_path = format!("{}_tmp", ts_file); + // match File::create(&tmp_file_path) { + // Ok(tmp_file) => { + // let mut writer = BufWriter::new(tmp_file); + // for line in last_10_lines { + // if let Err(err) = writeln!(writer, "{}", line){ + // eprintln!("Failed to write to temp file: {}", err); + // return; + // } + // } + // } + // Err(err) => { + // eprintln!("Failed to create temp file {}: {}", tmp_file_path, err); + // return; + // } + // } + + // // Replace original with temp file + // if let Err(err) = fs::rename(&tmp_file_path, ts_file) { + // eprintln!("Failed to replace original file with temp file: {}", err); + // } +} + +pub fn get_last_modified_time_of_file>(path: P) -> Option { let path_ref = Path::new(path.as_ref()); if !path_ref.is_file() { @@ -285,7 +349,7 @@ pub fn get_last_modified_time_of_file>(path: P) -> Option{ } let metadata = fs::metadata(path_ref).ok()?; let modified_time: SystemTime = metadata.modified().ok()?; - // NOTE: this can be done with std::process::Command as well + // NOTE: this can be done with std::process::Command as well let datetime: DateTime = modified_time.into(); Some(datetime.format("%Y-%m-%d-%H-%M-%S").to_string()) @@ -295,20 +359,28 @@ pub fn get_last_modified_time_of_file>(path: P) -> Option{ // TODO: //} -fn get_crashed_log_file>(file_path: P) -> io::Result<()>{ +fn get_crashed_log_file>(file_path: P) -> io::Result<()> { let file = file_path.as_ref(); - let process_name = file.rsplitn(2, '_').nth(1).unwrap_or(file).trim_start_matches("./"); + let process_name = file + .rsplitn(2, '_') + .nth(1) + .unwrap_or(file) + .trim_start_matches("./"); println!("Process crashed = {}", process_name); - let app_name = file.split('_').nth(1).and_then(|s| s.split('-').next()).unwrap_or(""); - + let app_name = file + .split('_') + .nth(1) + .and_then(|s| s.split('-').next()) + .unwrap_or(""); + let breakpad_mapper = BufReader::new(File::open(LOGMAPPER_FILE)?); let mut log_files = String::new(); for line in breakpad_mapper.lines() { let line = line?; - if let Some((key, val)) = line.split_once('='){ - if key.contains(process_name){ + if let Some((key, val)) = line.split_once('=') { + if key.contains(process_name) { log_files = val.to_string(); break; } @@ -324,10 +396,13 @@ fn get_crashed_log_file>(file_path: P) -> io::Result<()>{ .append(true) .open(LOG_FILES)?; - for log_file in log_files.split(',').map(str::trim).filter(|s| !s.is_empty()) { + for log_file in log_files + .split(',') + .map(str::trim) + .filter(|s| !s.is_empty()) + { writeln!(output, "{}/{}", LOG_PATH, log_file)?; } Ok(()) } - \ No newline at end of file diff --git a/crash_upload/src/main.rs b/crash_upload/src/main.rs index 7e32c46..c57fa2f 100644 --- a/crash_upload/src/main.rs +++ b/crash_upload/src/main.rs @@ -1,21 +1,86 @@ +// standard library imports use std::env; +use std::fs::create_dir; +use std::path::Path; -mod utils; +// crashupload internal module imports mod constants; +mod crashupload_utils; fn main() { println!("Starting Crash Upload Binary..."); + + // EXEC: Command line argument parsing let args: Vec = env::args().collect(); - if args.len() != 3 { - eprintln!("Usage: "); + if args.len() != 5 { + eprintln!("Usage: "); + eprintln!("Example: \"example_string\" 1 \"example_string\" \"example_string\""); std::process::exit(1); } - let arg1 = &args[1]; // the string - let dump_flag = args[2].parse::().expect("Second argument must be a number"); - let upload_flag = &args[3]; // the string - let wait_for_lock = &args[4]; // the string - let dump_name = if dump_flag == 1 {"coredump"} else {"minidump"}; - let timestamp_filename = utils::get_timestamp_filename(dump_name); -} + //let crash_timestamp = utils::get_crash_timestamp(); + let dump_flag = args[2] + .parse::() + .expect("Second argument must be a number"); + let upload_flag = &args[3]; + let wait_for_lock = &args[4]; + + // Instantiate the DumpPaths struct + let mut dump_paths = constants::DumpPaths::new(); + if upload_flag != "secure" { + dump_paths.set_core_path("/opt/secure/corefiles".to_string()); + dump_paths.set_minidumps_path("/opt/secure/minidumps".to_string()); + } else { + dump_paths.set_core_path("/var/lib/systemd/coredump".to_string()); + dump_paths.set_minidumps_path("/opt/minidumps".to_string()); + } + + if crashupload_utils::should_exit_crash_upload(&dump_paths) { + println!("Crash upload process is already running. Exiting..."); + std::process::exit(0); + } + + // EXEC: Secure Dump Status & path Set + //let _ = crashupload_utils::get_secure_dump_status(&mut dump_paths); + + // core_log.txt file logging can be handled using syslog-ng + let dump_name = if dump_flag == 1 { + "coredump" + } else { + "minidump" + }; + // ============================== + /* TODO: Implementations for below functions + * logMessage() + * tlsLog() + * checkParameter() + * deleteAllButTheMostRecentFiles() + * cleanup() + * finalize() + * sigkill_function() + * sigterm_function() + * logUploadTimestamp() + * truncateTimeStampFile() + * removePendingDumps() + * markAsCrashLoopedAndUpload() + * saveDump() + * shouldProcessFile() + * get_crashed_log_file() + * processCrashTelemtryInfo() + * add_crashed_log_file() + * copy_log_files_tmp_dir() + * processDumps() + */ + // ============================== + + // let timestamp_filename = crashupload_utils::get_timestamp_filename(dump_name); + + // let working_dir = if dump_flag == 1 { + // crashupload_utils::get_core_path(upload_flag) + // } + // else + // { + // crashupload_utils::get_minidumps_path(upload_flag) + // }; +} diff --git a/crates/platform_interface/src/lib.rs b/crates/platform_interface/src/lib.rs index efd0cdd..5c94b94 100644 --- a/crates/platform_interface/src/lib.rs +++ b/crates/platform_interface/src/lib.rs @@ -1,6 +1,6 @@ -mod t2_api; mod rfc_api; +mod t2_api; pub const PLATFORM_LIB_VER: &str = "v1.0"; +pub use rfc_api::{get_rfc_param, set_rfc_param}; pub use t2_api::{t2_count_notify, t2_val_notify}; -pub use rfc_api::{set_rfc_param, get_rfc_param}; diff --git a/crates/platform_interface/src/rfc_api.rs b/crates/platform_interface/src/rfc_api.rs index 8ef45d1..9adcf76 100644 --- a/crates/platform_interface/src/rfc_api.rs +++ b/crates/platform_interface/src/rfc_api.rs @@ -11,11 +11,15 @@ fn rfc_bin_path() -> &'static Path { } /// Set a TR-181 RFC parameter to a value -pub fn set_rfc_param, V: AsRef>(rfc: R, value: V) -> bool -{ +pub fn set_rfc_param, V: AsRef>(rfc: R, value: V) -> bool { let rfc_bin = rfc_bin_path(); if rfc_bin.exists() { - match Command::new(rfc_bin).arg("-s").arg("-v").arg(value.as_ref()).arg(rfc.as_ref()).spawn() + match Command::new(rfc_bin) + .arg("-s") + .arg("-v") + .arg(value.as_ref()) + .arg(rfc.as_ref()) + .spawn() { Ok(_) => true, Err(err) => { @@ -23,28 +27,23 @@ pub fn set_rfc_param, V: AsRef>(rfc: R, value: V) -> bool false } } - } - else - { + } else { false } } /// Get a TR-181 RFC parameter into a mutable string -pub fn get_rfc_param>(rfc: R, res: &mut String) -> bool -{ +pub fn get_rfc_param>(rfc: R, res: &mut String) -> bool { let rfc_bin = rfc_bin_path(); if rfc_bin.exists() { - match Command::new(rfc_bin).arg("-g").arg(rfc.as_ref()).output() - { + match Command::new(rfc_bin).arg("-g").arg(rfc.as_ref()).output() { Ok(output) => { if output.status.success() { // Convert command output to string and update res let output_str = String::from_utf8_lossy(&output.stdout); - *res = output_str.trim().to_string(); // Update the mutable reference + *res = output_str.trim().to_string(); // Update the mutable reference true - } - else{ + } else { false } } @@ -53,9 +52,7 @@ pub fn get_rfc_param>(rfc: R, res: &mut String) -> bool false } } - } - else - { + } else { false } -} \ No newline at end of file +} diff --git a/crates/platform_interface/src/t2_api.rs b/crates/platform_interface/src/t2_api.rs index 1bc5662..84eeff2 100644 --- a/crates/platform_interface/src/t2_api.rs +++ b/crates/platform_interface/src/t2_api.rs @@ -10,15 +10,14 @@ fn t2_msg_client_path() -> &'static Path { } /// Notify a telemetry marker with a count (default 1 if None) -pub fn t2_count_notify, C: AsRef>(marker: M, count: Option) -> bool -where - M: AsRef, - C: AsRef, -{ +pub fn t2_count_notify, C: AsRef>(marker: M, count: Option) -> bool { let t2_client = t2_msg_client_path(); if t2_client.exists() { let count_val = count.as_ref().map(|c| c.as_ref()).unwrap_or("1"); - match Command::new(t2_client).arg(marker.as_ref()).arg(count_val).spawn() + match Command::new(t2_client) + .arg(marker.as_ref()) + .arg(count_val) + .spawn() { Ok(_) => true, Err(err) => { @@ -26,19 +25,19 @@ where false } } - } - else - { + } else { false } } /// Notify a telemetry marker with a value -pub fn t2_val_notify, V: AsRef>(marker: M, value: V) -> bool -{ +pub fn t2_val_notify, V: AsRef>(marker: M, value: V) -> bool { let t2_client = t2_msg_client_path(); if t2_client.exists() { - match Command::new(t2_client).arg(marker.as_ref()).arg(value.as_ref()).spawn() + match Command::new(t2_client) + .arg(marker.as_ref()) + .arg(value.as_ref()) + .spawn() { Ok(_) => true, Err(err) => { @@ -46,9 +45,7 @@ pub fn t2_val_notify, V: AsRef>(marker: M, value: V) -> bool false } } - } - else - { + } else { false } } diff --git a/crates/utils/src/command.rs b/crates/utils/src/command.rs index 38243ca..41b5931 100644 --- a/crates/utils/src/command.rs +++ b/crates/utils/src/command.rs @@ -3,7 +3,7 @@ use std::path::Path; use std::thread; use std::time::Duration; -pub fn touch>(path: P){ +pub fn touch>(path: P) { let _ = OpenOptions::new().create(true).write(true).open(path); } @@ -24,4 +24,4 @@ pub fn rm_rf>(path: P) { pub fn sleep(seconds: u64) -> bool { thread::sleep(Duration::from_secs(seconds)); true -} \ No newline at end of file +} diff --git a/crates/utils/src/device_info.rs b/crates/utils/src/device_info.rs index 51a38d7..0ed722e 100644 --- a/crates/utils/src/device_info.rs +++ b/crates/utils/src/device_info.rs @@ -1,7 +1,7 @@ // utils/src/device_info.rs use std::fs::File; -use std::path::Path; use std::io::{BufRead, BufReader}; +use std::path::Path; // Module-level constants const DEVICE_PROP_FILE: &str = "/etc/device.properties"; @@ -9,8 +9,7 @@ const INCLUDE_PROP_FILE: &str = "/etc/include.properties"; const COMMON_PROP_FILE: &str = "/etc/common.properties"; // use this fn to getModel() using "MODEL_NUM" key -pub fn get_property_value_from_file, K: AsRef>(path: P, key: K, val: &mut String) -> bool -{ +pub fn get_property_value_from_file, K: AsRef>(path: P, key: K, val: &mut String) -> bool { let key = key.as_ref().trim(); *val = String::new(); let prop_file = match File::open(path) { @@ -18,23 +17,18 @@ pub fn get_property_value_from_file, K: AsRef>(path: P, key: Err(_) => return false, }; let reader = BufReader::new(prop_file); - for line in reader.lines().flatten() - { - if let Some((k, v)) = line.split_once('=') - { - if k.trim() == key - { + for line in reader.lines().flatten() { + if let Some((k, v)) = line.split_once('=') { + if k.trim() == key { let value = v.trim(); - if !value.is_empty() - { + if !value.is_empty() { val.push_str(value); return true; - } - else{ + } else { return false; } } } } false -} \ No newline at end of file +} diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index bdc5cdd..17cc659 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -1,6 +1,6 @@ -mod device_info; mod command; +mod device_info; pub const UTILS_LIB_VER: &str = "v1.0"; -pub use device_info::{get_property_value_from_file}; -pub use command::*; +pub use command::{rm, rm_rf, sleep, touch}; +pub use device_info::get_property_value_from_file; diff --git a/test/crash_upload_test/Cargo.toml b/test/crash_upload_test/Cargo.toml new file mode 100644 index 0000000..2bbf5e2 --- /dev/null +++ b/test/crash_upload_test/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "crash_upload_test" +version = "0.1.0" +edition = "2024" + +[dependencies] +crash_upload = {path = "../../crash_upload"} diff --git a/test/crash_upload_test/src/main.rs b/test/crash_upload_test/src/main.rs new file mode 100644 index 0000000..78de2b7 --- /dev/null +++ b/test/crash_upload_test/src/main.rs @@ -0,0 +1,3 @@ +fn main(){ + println!("Hello, World!") +} \ No newline at end of file diff --git a/test/curl_interface_test/Cargo.toml b/test/curl_interface_test/Cargo.toml new file mode 100644 index 0000000..af42f66 --- /dev/null +++ b/test/curl_interface_test/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "curl_interface_test" +version = "0.1.0" +edition = "2024" + +[dependencies] +curl_interface = {path = "../../crates/curl_interface"} \ No newline at end of file diff --git a/test/curl_interface_test/src/main.rs b/test/curl_interface_test/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/test/curl_interface_test/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/test/json_parser_interface_test/Cargo.toml b/test/json_parser_interface_test/Cargo.toml new file mode 100644 index 0000000..885736b --- /dev/null +++ b/test/json_parser_interface_test/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "json_parser_interface_test" +version = "0.1.0" +edition = "2024" + +[dependencies] +json_parser_interface = {path = "../../crates/json_parser_interface"} \ No newline at end of file diff --git a/test/json_parser_interface_test/src/main.rs b/test/json_parser_interface_test/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/test/json_parser_interface_test/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/test/platform_interface_test/Cargo.toml b/test/platform_interface_test/Cargo.toml new file mode 100644 index 0000000..3f3007a --- /dev/null +++ b/test/platform_interface_test/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "platform_interface_test" +version = "0.1.0" +edition = "2024" + +[dependencies] +platform_interface = {path = "../../crates/platform_interface"} \ No newline at end of file diff --git a/test/platform_interface_test/src/main.rs b/test/platform_interface_test/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/test/platform_interface_test/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/test/utils_test/Cargo.toml b/test/utils_test/Cargo.toml new file mode 100644 index 0000000..610aadb --- /dev/null +++ b/test/utils_test/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "utils_test" +version = "0.1.0" +edition = "2024" + +[dependencies] +utils = {path = "../../crates/utils"} diff --git a/test/utils_test/src/main.rs b/test/utils_test/src/main.rs new file mode 100644 index 0000000..b9aab89 --- /dev/null +++ b/test/utils_test/src/main.rs @@ -0,0 +1,86 @@ +use std::fs::{self, File}; +use std::io::Write; +use std::path::PathBuf; + +fn create_mock_property_file() { + let mut mock_props_file = File::create("device.properties").unwrap(); + let content = "MODEL_NUM=TestModel\nOTHER_KEY=OtherValue"; + mock_props_file.write_all(content.as_bytes()).unwrap(); +} + +fn remove_mock_property_file() { + if PathBuf::from("device.properties").exists() { + fs::remove_file("device.properties").unwrap(); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use utils::*; + + // Test for get_property_value_from_file function + #[test] + fn test_get_property_value_from_file() { + create_mock_property_file(); + let mut value = String::new(); + let res = get_property_value_from_file("device.properties", "MODEL_NUM", &mut value); + assert_eq!(res, true); + assert_eq!(value, "TestModel"); + + remove_mock_property_file(); + } + + #[test] + fn test_get_property_value_from_file_no_key() { + create_mock_property_file(); + let mut value = String::new(); + let res = get_property_value_from_file("device.properties", "DEVICE_TYPE", &mut value); + assert_eq!(res, false); + assert!(value.is_empty()); + + remove_mock_property_file(); + } + + // Test for touch function + #[test] + fn test_touch() { + touch("test_file.txt"); + assert!(PathBuf::from("test_file.txt").exists()); + rm("test_file.txt"); + } + + // Test for rm function + #[test] + fn test_rm() { + touch("test_file.txt"); + rm("test_file.txt"); + assert!(!PathBuf::from("test_file.txt").exists()); + } + + // Test for rm_rf function + #[test] + fn test_rm_rf() { + touch("test_file.txt"); + rm_rf("test_file.txt"); + assert!(!PathBuf::from("test_file.txt").exists()); + + let dir_path = "test_dir"; + fs::create_dir(dir_path).unwrap(); + touch(&format!("{}/test_file.txt", dir_path)); + rm_rf(dir_path); + assert!(!PathBuf::from(dir_path).exists()); + } + + // Test for sleep function + #[test] + fn test_sleep() { + let start = std::time::Instant::now(); + sleep(5); + let duration = start.elapsed(); + assert!(duration.as_secs() >= 5); + assert!(duration.as_secs() < 6); + } +} + +fn main() {} From 6bc4a11c2243a0231fdb8cecf1c983a9eb82977c Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Thu, 15 May 2025 13:38:05 +0000 Subject: [PATCH 08/66] update main.rs Signed-off-by: Gomathi Shankar --- crash_upload/src/constants.rs | 47 +++++++++++++++++++++-- crash_upload/src/crashupload_utils.rs | 40 +++++++++++++++++++- crash_upload/src/main.rs | 54 ++++++++++++++++++++++----- 3 files changed, 127 insertions(+), 14 deletions(-) diff --git a/crash_upload/src/constants.rs b/crash_upload/src/constants.rs index aec40a7..46e66fa 100644 --- a/crash_upload/src/constants.rs +++ b/crash_upload/src/constants.rs @@ -6,9 +6,9 @@ pub const CORE_LOG: &str = "/opt/rdk/core_log.txt"; pub const S3_BUCKET_URL: &str = "s3.amazonaws.com"; pub const HTTP_CODE_FILE: &str = "/tmp/httpcode"; -pub const CURL_UPLOAD_TIMEOUT: u32 = 45; -pub const FOUR_EIGHTY_SECS: u32 = 480; -pub const MAX_CORE_FILES: u32 = 4; +pub const CURL_UPLOAD_TIMEOUT: usize = 45; +pub const FOUR_EIGHTY_SECS: usize = 480; +pub const MAX_CORE_FILES: usize = 4; pub const TLS: &str = "--tlsv1.2"; @@ -26,6 +26,11 @@ pub struct DumpPaths { pub minidumps_path: String, pub core_back_path: String, pub persistent_sec_path: String, + pub working_dir: String, + pub dumps_extn: String, + pub tar_extn: String, + pub lock_dir_prefix: String, + pub crash_portal_path: String, } impl DumpPaths { @@ -53,12 +58,48 @@ impl DumpPaths { pub fn get_persistent_sec_path(&self) -> &String { &self.persistent_sec_path } + pub fn get_working_dir(&self) -> &String { + &self.working_dir + } + pub fn set_working_dir(&mut self, path: String) { + self.working_dir = path; + } + pub fn get_dumps_extn(&self) -> &String { + &self.dumps_extn + } + pub fn set_dumps_extn(&mut self, extn: String) { + self.dumps_extn = extn; + } + pub fn get_tar_extn(&self) -> &String { + &self.tar_extn + } + pub fn set_tar_extn(&mut self, extn: String) { + self.tar_extn = extn; + } + pub fn get_lock_dir_prefix(&self) -> &String { + &self.lock_dir_prefix + } + pub fn set_lock_dir_prefix(&mut self, prefix: String) { + self.lock_dir_prefix = prefix; + } + pub fn get_crash_portal_path(&self) -> &String { + &self.crash_portal_path + } + pub fn set_crash_portal_path(&mut self, path: String) { + self.crash_portal_path = path; + } + /// Creates a new `DumpPaths` instance with default values. pub fn new() -> Self { DumpPaths { core_path: String::new(), minidumps_path: String::new(), core_back_path: String::new(), persistent_sec_path: String::new(), + working_dir: String::new(), + dumps_extn: String::new(), + tar_extn: String::new(), + lock_dir_prefix: String::new(), + crash_portal_path: String::new(), } } } diff --git a/crash_upload/src/crashupload_utils.rs b/crash_upload/src/crashupload_utils.rs index 3d088a9..a89e75c 100644 --- a/crash_upload/src/crashupload_utils.rs +++ b/crash_upload/src/crashupload_utils.rs @@ -267,8 +267,46 @@ pub fn is_recovery_time_reached() -> bool { now > upload_denied_till } -// ============================================ +// ============================================ In Progress Start ============================================ +pub fn delete_all_but_most_recent_files>(dir_path: P) { + let path = Path::new(dir_path.as_ref()); + + let num_of_files = match fs::read_dir(path) { + Ok(entries) => { + let file_count = entries.count(); + file_count + } + Err(e) => { + 1 + } + }; + if num_of_files > MAX_CORE_FILES { + let val = num_of_files - MAX_CORE_FILES; + // TODO: delete the oldest files + rm_rf("/tmp/dumps_to_delete.txt"); + } + +} + +// TODO: Implement this function +pub const CRASH_LOOP_FLAG_FILE: &str = ""; + +pub fn finalize(dump_paths: &DumpPaths) { + cleanup(); + let loop_file = Path::new(CRASH_LOOP_FLAG_FILE); + if loop_file.exists() { + rm_rf(loop_file); + } + remove_lock(dump_paths.get_lock_dir_prefix()); + // TODO: Implement Time Stamp file cleanup +} + +pub fn cleanup() { + // TODO: Implement cleanup logic +} + +// =========================================== In Progress End ============================================ pub fn get_timestamp_filename(dump_name: &str) -> String { format!("/tmp/.{}_upload_timestamps", dump_name) } diff --git a/crash_upload/src/main.rs b/crash_upload/src/main.rs index c57fa2f..7529902 100644 --- a/crash_upload/src/main.rs +++ b/crash_upload/src/main.rs @@ -1,8 +1,12 @@ // standard library imports -use std::env; +use std::{env, fs}; use std::fs::create_dir; +use std::os::unix::thread; use std::path::Path; +use platform_interface::{get_rfc_param, set_rfc_param}; +use utils::sleep; + // crashupload internal module imports mod constants; mod crashupload_utils; @@ -25,9 +29,10 @@ fn main() { let upload_flag = &args[3]; let wait_for_lock = &args[4]; - // Instantiate the DumpPaths struct + // EXEC: Instantiate the DumpPaths struct let mut dump_paths = constants::DumpPaths::new(); + // EXEC: Set the core and minidump paths based on the upload flag if upload_flag != "secure" { dump_paths.set_core_path("/opt/secure/corefiles".to_string()); dump_paths.set_minidumps_path("/opt/secure/minidumps".to_string()); @@ -55,7 +60,7 @@ fn main() { * logMessage() * tlsLog() * checkParameter() - * deleteAllButTheMostRecentFiles() + * deleteAllButTheMostRecentFiles() - In progress * cleanup() * finalize() * sigkill_function() @@ -76,11 +81,40 @@ fn main() { // let timestamp_filename = crashupload_utils::get_timestamp_filename(dump_name); - // let working_dir = if dump_flag == 1 { - // crashupload_utils::get_core_path(upload_flag) - // } - // else - // { - // crashupload_utils::get_minidumps_path(upload_flag) - // }; + if dump_flag == 1 { + println!("starting core dump processing..."); + dump_paths.set_working_dir(dump_paths.get_core_path().to_string()); + dump_paths.set_dumps_extn("*core.prog*.gz*".to_string()); + dump_paths.set_tar_extn(".core.tgz".to_string()); + dump_paths.set_lock_dir_prefix("/tmp/.uploadCoredumps".to_string()); + dump_paths.set_crash_portal_path("/opt/crashportal_uploads/coredumps/".to_string()); + } + else + { + println!("starting minidump processing..."); + dump_paths.set_working_dir(dump_paths.get_minidumps_path().to_string()); + dump_paths.set_dumps_extn("*.dmp*".to_string()); + dump_paths.set_tar_extn(".dmp.tgz".to_string()); + dump_paths.set_lock_dir_prefix("/tmp/.uploadMinidumps".to_string()); + dump_paths.set_crash_portal_path("/opt/crashportal_uploads/coredumps/".to_string()); + sleep(5); + }; + + let w_dir = dump_paths.get_working_dir(); + match fs::read_dir(w_dir) { + Ok(mut entries) => entries.next().is_none(), + Err(_) => { + eprintln!("working dir is empty : {}", w_dir); + std::process::exit(1); + } + }; + + let mut portal_url = String::new(); + get_rfc_param("Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.CrashUpload.crashPortalSTBUrl", &mut portal_url); + let req_type = 17; + + let mut encryption_enabled = false; + if Path::new("/etc/os-release").exists() { encryption_enabled = set_rfc_param("Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.CrashUpload.encryptionEnabled", "true") }; + + } From 66a8908063a861a836b2d1884708cfa5581a0060 Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Wed, 21 May 2025 10:51:39 +0000 Subject: [PATCH 09/66] Update More API support for crash upload Signed-off-by: Gomathi Shankar --- TODO.md | 5 +- crash_upload/src/constants.rs | 57 +++++++ crash_upload/src/crashupload_utils.rs | 231 +++++++++++++++++++++----- crash_upload/src/main.rs | 154 +++++++++++++---- crates/utils/src/device_info.rs | 37 ++++- crates/utils/src/lib.rs | 2 +- test/utils_test/src/main.rs | 57 +++++-- 7 files changed, 448 insertions(+), 95 deletions(-) diff --git a/TODO.md b/TODO.md index 439b0d0..8c4b653 100644 --- a/TODO.md +++ b/TODO.md @@ -1,8 +1,11 @@ TODO: +- Capture existing uploadDumps.sh and uploadDumpsToS3.sh functionality wise in detail +- Same process multiple crashes, Multiple process crashes (corner cases) + - Separate Logging log.rs, with WARN, INFO, ERROR, etc log levels - get_sha1() -> using sha1 crate/ process::Command() if single use - get_last_modified_time_of_file() -> using chrono crate/ process::Command() if single use -- args parsing in main app +- ~~args parsing in main app~~ - upload dump utils - ~~sanitize()~~ - checkParam() diff --git a/crash_upload/src/constants.rs b/crash_upload/src/constants.rs index 46e66fa..e4f572e 100644 --- a/crash_upload/src/constants.rs +++ b/crash_upload/src/constants.rs @@ -1,7 +1,11 @@ +use std::{alloc::System, time::SystemTime}; + // src/constants.rs pub const LOGMAPPER_FILE: &str = "/etc/breakpad-logmapper.conf"; pub const LOG_FILES: &str = "/tmp/minidump_log_files.txt"; + pub const LOG_PATH: &str = "/opt/rdk"; +pub const IS_T2_ENABLED: bool = false; pub const CORE_LOG: &str = "/opt/rdk/core_log.txt"; pub const S3_BUCKET_URL: &str = "s3.amazonaws.com"; @@ -9,6 +13,8 @@ pub const HTTP_CODE_FILE: &str = "/tmp/httpcode"; pub const CURL_UPLOAD_TIMEOUT: usize = 45; pub const FOUR_EIGHTY_SECS: usize = 480; pub const MAX_CORE_FILES: usize = 4; +pub const CORE_PROPS_FILE: &str = "/opt/coredump.properties"; +pub const S3_FILENAME: &str = "s3filename"; pub const TLS: &str = "--tlsv1.2"; @@ -21,7 +27,22 @@ pub const SECUREDUMP_DISABLE_FILE: &str = "/tmp/.SecureDumpDisable"; pub const CRASH_UPLOAD_REBOOT_FLAG: &str = "/tmp/set_crash_reboot_flag"; pub const DENY_UPLOAD_FILE: &str = "/tmp/.deny_dump_uploads_till"; +pub const UPLOAD_ON_STARTUP: &str = "/opt/.upload_on_startup"; +pub const ON_STARTUP_DUMPS_CLEANED_UP_BASE: &str ="/tmp/.on_startup_dumps_cleaned_up"; +pub const CRASH_LOOP_FLAG_FILE: &str = ""; // TODO + +pub const POTOMAC_USER: &str = "ccpstbscp"; +pub const COREDUMP_MTX_FILE: &str = "/tmp/coredump_mutex_release"; + +pub const NETWORK_FILE: &str = "/tmp/route_available"; +pub const SYSTEM_TIME_FILE: &str = "/tmp/stt_received"; +pub const NETWORK_CHECK_ITERATION: usize = 18; +pub const NETWORK_CHECK_TIMEOUT: usize = 10; +pub const SYSTEM_TIME_ITERATION: usize = 10; +pub const SYSTEM_TIME_TIMEOUT: usize = 1; + pub struct DumpPaths { + pub dump_name: String, pub core_path: String, pub minidumps_path: String, pub core_back_path: String, @@ -31,6 +52,7 @@ pub struct DumpPaths { pub tar_extn: String, pub lock_dir_prefix: String, pub crash_portal_path: String, + pub ts_file: String, } impl DumpPaths { @@ -88,9 +110,13 @@ impl DumpPaths { pub fn set_crash_portal_path(&mut self, path: String) { self.crash_portal_path = path; } + pub fn get_ts_file(&self) -> &String { + &self.ts_file + } /// Creates a new `DumpPaths` instance with default values. pub fn new() -> Self { DumpPaths { + dump_name: String::new(), core_path: String::new(), minidumps_path: String::new(), core_back_path: String::new(), @@ -100,6 +126,37 @@ impl DumpPaths { tar_extn: String::new(), lock_dir_prefix: String::new(), crash_portal_path: String::new(), + ts_file: String::new(), } } } + + +// TODO: Add device_name member as well +pub struct DeviceData { + pub device_type: String, + pub box_type: String, + pub model_num: String, + pub sha1: String, + pub mac_addr: String, + pub t2_enabled: bool, + pub tls: String, + pub encryption_enabled: bool, + pub build_type: String, +} + +impl DeviceData { + pub fn new() -> Self { + DeviceData { + device_type: String::new(), + box_type: String::new(), + model_num: String::new(), + sha1: String::new(), + mac_addr: String::new(), + t2_enabled: false, + tls: String::new(), + encryption_enabled: false, + build_type: String::new(), + } + } +} \ No newline at end of file diff --git a/crash_upload/src/crashupload_utils.rs b/crash_upload/src/crashupload_utils.rs index a89e75c..be9e9ab 100644 --- a/crash_upload/src/crashupload_utils.rs +++ b/crash_upload/src/crashupload_utils.rs @@ -3,13 +3,13 @@ use chrono::{DateTime, Local}; use std::fs::{self, File, OpenOptions}; use std::io::{self, BufRead, BufReader, BufWriter, Read, Seek, SeekFrom, Write}; use std::path::{Path, PathBuf}; -use std::process; +use std::{process, usize}; use std::time::{SystemTime, UNIX_EPOCH}; use std::{thread, time}; use platform_interface::*; use utils::*; -use crate::constants::*; +use crate::constants::{self, *}; // Module-level constants const SHA1_DEFAULT_VALUE: &str = "0000000000000000000000000000000000000000"; @@ -17,6 +17,24 @@ const TIMESTAMP_DEFAULT_VALUE: &str = "2000-01-01-00-00-00"; const MAC_DEFAULT_VALUE: &str = "000000000000"; const MODEL_NUM_DEFAULT_VALUE: &str = "UNKNOWN"; + +pub fn set_device_data(device_data: &mut DeviceData) { + get_property_value_from_file(DEVICE_PROP_FILE, "BOX_TYPE",&mut device_data.box_type); + get_property_value_from_file(DEVICE_PROP_FILE, "MODEL_NUM",&mut device_data.model_num); + get_property_value_from_file(DEVICE_PROP_FILE, "DEVICE_TYPE", &mut device_data.device_type); + get_sha1_value(&mut device_data.sha1); + get_device_mac(&mut device_data.mac_addr); + get_property_value_from_file(DEVICE_PROP_FILE, "BUILD_TYPE", &mut device_data.build_type); + device_data.t2_enabled = Path::new("/lib/rdk/t2Shared_api.sh").exists(); + device_data.tls = if Path::new("/etc/os-release").exists() { "--tlsv1.2".to_string() } else { "".to_string() }; + device_data.encryption_enabled = if Path::new("/etc/encryption_enabled").exists() { + set_rfc_param("Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.CrashUpload.encryptionEnabled", "true"); + true + } else { + false + }; +} + pub fn should_exit_crash_upload(dump_paths: &DumpPaths) -> bool { let minidumps_exists = check_minidumps_exist(&dump_paths.minidumps_path); let core_exists = check_core_exist(&dump_paths.core_path); @@ -52,21 +70,18 @@ fn check_core_exist(core_dir: &str) -> bool { false } -pub fn set_log_file(sha1: &str, mac: &str, log_mod_ts: &str, box_type: &str, mod_num: &str, line: &str) { +pub fn set_log_file(device_data: &DeviceData, log_mod_ts: &str, line: &str){ let file_name = line.split('/').last().unwrap_or_else(|| line); let file_processed = file_name.contains("_mac") || file_name.contains("_dat") || file_name.contains("_box") - || file_name.contains("_mod"); + || file_name.contains("_mod"); if file_processed { println!("{}", file_name); println!("Core name is already processed"); } else { - println!( - "{}_mac{}_dat{}_box{}_mod{}_{}", - sha1, mac, log_mod_ts, box_type, mod_num, file_name - ); - } + println!("{}_mac{}_dat{}_box{}_mod{}_{}", device_data.sha1, device_data.mac_addr, log_mod_ts, device_data.box_type, device_data.model_num, file_name); + } } fn set_secure_dump_flag(is_sec_dump_enabled: &mut bool) { @@ -113,13 +128,16 @@ pub fn get_secure_dump_status(dump_paths: &mut DumpPaths) { } } +fn lock_path>(path: P) -> PathBuf { + let mut p = path.as_ref().to_path_buf(); + p.set_extension("lock.d"); + p +} + pub fn create_lock_or_exit>(path: P) -> bool { let lock = lock_path(&path); if is_another_instance_running(&path) { - println!( - "Script is already working. {:?}. Skip launching another instance...", - lock - ); + println!("Script is already working. {:?}. Skip launching another instance...", lock); process::exit(0); } @@ -268,55 +286,184 @@ pub fn is_recovery_time_reached() -> bool { now > upload_denied_till } -// ============================================ In Progress Start ============================================ -pub fn delete_all_but_most_recent_files>(dir_path: P) { + +pub fn delete_all_but_most_recent_files>(dir_path: P) -> Result<(), Box> { let path = Path::new(dir_path.as_ref()); + let mut files: Vec<(PathBuf, SystemTime)> = fs::read_dir(path)? + .filter_map(|entry| { + let entry = match entry { + Ok(entry) => entry, + Err(_) => return None, + }; + let metadata = match entry.metadata() { + Ok(metadata) => metadata, + Err(_) => return None, + }; + let modified = match metadata.modified() { + Ok(time) => time, + Err(_) => return None, + }; + Some((entry.path(), modified)) + }) + .collect(); - let num_of_files = match fs::read_dir(path) { - Ok(entries) => { - let file_count = entries.count(); - file_count - } - Err(e) => { - 1 + files.sort_by(|a, b| b.1.cmp(&a.1)); + + // Calculate number of files to delete + if files.len() > MAX_CORE_FILES { + let num_files_to_delete = files.len() - MAX_CORE_FILES; + + // Delete the oldest files + for (file_path, _) in files.iter().rev().take(num_files_to_delete) { + fs::remove_file(file_path)?; + println!("Deleted file: {:?}", file_path); } - }; - if num_of_files > MAX_CORE_FILES { - let val = num_of_files - MAX_CORE_FILES; - // TODO: delete the oldest files - rm_rf("/tmp/dumps_to_delete.txt"); + } else { + println!("No files need to be deleted. Total files: {}", files.len()); } - + Ok(()) } -// TODO: Implement this function -pub const CRASH_LOOP_FLAG_FILE: &str = ""; - pub fn finalize(dump_paths: &DumpPaths) { - cleanup(); + //cleanup(dump_paths); let loop_file = Path::new(CRASH_LOOP_FLAG_FILE); if loop_file.exists() { rm_rf(loop_file); } remove_lock(dump_paths.get_lock_dir_prefix()); - // TODO: Implement Time Stamp file cleanup + remove_lock(dump_paths.get_ts_file()); } -pub fn cleanup() { - // TODO: Implement cleanup logic +pub fn sig_term_function(dump_paths: &DumpPaths) { + println!("systemd terminating, Removing the script locks"); + let loop_file = Path::new(CRASH_LOOP_FLAG_FILE); + if loop_file.exists() { + rm_rf(loop_file); + } + remove_lock(dump_paths.get_lock_dir_prefix()); + remove_lock(dump_paths.get_ts_file()); } -// =========================================== In Progress End ============================================ -pub fn get_timestamp_filename(dump_name: &str) -> String { - format!("/tmp/.{}_upload_timestamps", dump_name) +pub fn should_process_dump>(dump_paths: &DumpPaths, device_data: &DeviceData, file_name: P) -> bool { + let f_name = file_name.as_ref(); + let status = if dump_paths.dump_name == "minidump" || device_data.device_type != "prod" || f_name.contains("Receiver") { + true + } + else { + println!("Not Processing dump file {}", f_name); + false + }; + status } +/* +// ============================================ In Progress Start ============================================ +// TODO: Implement this function +pub fn process_dump(dump_paths: &DumpPaths) { -fn lock_path>(path: P) -> PathBuf { - let mut p = path.as_ref().to_path_buf(); - p.set_extension("lock.d"); - p + +} + +pub fn add_crashed_log_file(device_data: &DeviceData) { + let files = vec!["file1", "file2", "file3"]; // Update + + let mut line_count = 5000; + if device_data.build_type == "prod" { + line_count = 500; + } + + +} + +pub fn copy_log_files_to_tmp(tmp_dir: &String) { + let tmp_directory = format!("/tmp{}",tmp_dir).as_str(); + let res = 0; + let limit = 70; + let mut usage_percent = 0; + + let tmp_path = Path::new("/tmp"); + + if tmp_path.is_dir() { + usage_percent = get_usage_percent(tmp_path); + } + else{ + println!("{} is not a directory", tmp_path.display()); + } + + if usage_percent > limit { + println!("Skipping copying Logs to tmp dir due to limited Memory"); + // TODO + } + else { + println!("Copying logs to tmp dir as Memory available. used size = {}% limit = {}%", usage_percent, limit); + /* mkdir $TmpDirectory 2> /dev/null + cp $Logfiles $TmpDirectory 2> /dev/null + */ + println!("Logs copied to {} Temporary", tmp_directory); + + // Loop + + } + +} +// pub fn save_dump(dump_paths: &DumpPaths, new_name: Option<&str>) { +// if let Some(new_name) = new_name { +// let original_path = Path::new(&dump_paths.minidumps_path).join(&dump_paths.tar_extn); +// let new_path = Path::new(&dump_paths.minidumps_path).join(new_name); +// } + +// let dump_files: Vec = fs::read_dir(&dump_paths.minidumps_path)? +// .filter_map(|entry| { +// let entry = entry.ok()?; +// let file_type = entry.file_type().ok()?; +// if file_type.is_file() && entry.path().extension() == Some("tgz".as_ref()) { +// Some(entry.path()) +// } else { +// None +// } +// }) +// .collect(); +// let mut count = dump_files.len(); +// while count > 5 { +// if let Some(oldest_dump) = dump_files.iter() +// .min_by_key(|path| fs::metadata(path).and_then(|m| m.modified()).unwrap_or(SystemTime::UNIX_EPOCH)) { + +// println!("Removing old dump {:?}", oldest_dump); +// fs::remove_file(oldest_dump)?; + +// count -= 1; // Decrement count +// } +// } +// println!("Total pending Minidumps: {}", count); +// } + +pub fn cleanup(dump_paths: &DumpPaths) { + let work_dir = Path::new(dump_paths.get_working_dir()); + if !work_dir.exists() || !work_dir.is_dir() || work_dir.read_dir().unwrap().next().is_none() { + println!("Working directory {} is empty", work_dir.display()); + return; + } + println!("Cleanup {} directory {}", dump_paths.dump_name, dump_paths.working_dir); + + // Loop deletes + + if !Path::new(UPLOAD_ON_STARTUP).exists() { + rm_rf(format!("{}/version.txt", dump_paths.working_dir)); + let on_startup_dumps_cleaned_up_str = format!("{}_{}", ON_STARTUP_DUMPS_CLEANED_UP_BASE, if dump_paths.dump_name == "coredump" { "1" } else { "" }); + let on_startup_dumps_cleaned_up_path = Path::new(on_startup_dumps_cleaned_up_str.as_str()); + if !on_startup_dumps_cleaned_up_path.exists() { + // Find and Call delete_all_but_most_recent_files() + } + } + else { + if dump_paths.dump_name == "coredump" { + rm_rf(UPLOAD_ON_STARTUP); + } + } } +// =========================================== In Progress End ============================================ +*/ + fn is_another_instance_running>(path: P) -> bool { lock_path(path).is_dir() } diff --git a/crash_upload/src/main.rs b/crash_upload/src/main.rs index 7529902..b7cc91f 100644 --- a/crash_upload/src/main.rs +++ b/crash_upload/src/main.rs @@ -1,10 +1,14 @@ +use std::process::exit; // standard library imports -use std::{env, fs}; +use std::{env, fs, process}; use std::fs::create_dir; use std::os::unix::thread; use std::path::Path; -use platform_interface::{get_rfc_param, set_rfc_param}; +use chrono::Local; +use constants::{DeviceData, COREDUMP_MTX_FILE, CRASH_UPLOAD_REBOOT_FLAG, NETWORK_CHECK_TIMEOUT}; +use crashupload_utils::*; +use platform_interface::*; use utils::sleep; // crashupload internal module imports @@ -13,27 +17,27 @@ mod crashupload_utils; fn main() { println!("Starting Crash Upload Binary..."); - // EXEC: Command line argument parsing let args: Vec = env::args().collect(); - if args.len() != 5 { - eprintln!("Usage: "); - eprintln!("Example: \"example_string\" 1 \"example_string\" \"example_string\""); - std::process::exit(1); + if args.len() != 4 { + eprintln!("Usage: {} ", args[0]); + process::exit(1); } - //let crash_timestamp = utils::get_crash_timestamp(); - let dump_flag = args[2] - .parse::() - .expect("Second argument must be a number"); - let upload_flag = &args[3]; - let wait_for_lock = &args[4]; - - // EXEC: Instantiate the DumpPaths struct + // EXEC: Instantiate the DumpPaths & DeviceData structs + println!("Instantiating DumpPaths and DeviceData structs..."); + let mut device_data = constants::DeviceData::new(); let mut dump_paths = constants::DumpPaths::new(); + crashupload_utils::set_device_data(&mut device_data); + + //let crash_timestamp = utils::get_crash_timestamp(); + let dump_flag = args[1].parse::().expect("Dump flag must be a number"); + let upload_flag = &args[2]; + let wait_for_lock = &args[3]; + // EXEC: Set the core and minidump paths based on the upload flag - if upload_flag != "secure" { + if upload_flag == "secure" { dump_paths.set_core_path("/opt/secure/corefiles".to_string()); dump_paths.set_minidumps_path("/opt/secure/minidumps".to_string()); } else { @@ -50,11 +54,7 @@ fn main() { //let _ = crashupload_utils::get_secure_dump_status(&mut dump_paths); // core_log.txt file logging can be handled using syslog-ng - let dump_name = if dump_flag == 1 { - "coredump" - } else { - "minidump" - }; + // ============================== /* TODO: Implementations for below functions * logMessage() @@ -80,32 +80,57 @@ fn main() { // ============================== // let timestamp_filename = crashupload_utils::get_timestamp_filename(dump_name); - + let crash_ts = Local::now().format("%Y-%m-%d-%H-%M-%S").to_string(); + if dump_flag == 1 { println!("starting core dump processing..."); - dump_paths.set_working_dir(dump_paths.get_core_path().to_string()); - dump_paths.set_dumps_extn("*core.prog*.gz*".to_string()); - dump_paths.set_tar_extn(".core.tgz".to_string()); - dump_paths.set_lock_dir_prefix("/tmp/.uploadCoredumps".to_string()); - dump_paths.set_crash_portal_path("/opt/crashportal_uploads/coredumps/".to_string()); + dump_paths.dump_name = "coredump".to_string(); + dump_paths.working_dir = dump_paths.get_core_path().to_string(); + dump_paths.dumps_extn = "*core.prog*.gz*".to_string(); + dump_paths.tar_extn = ".core.tgz".to_string(); + dump_paths.lock_dir_prefix = "/tmp/.uploadCoredumps".to_string(); + dump_paths.crash_portal_path = "/opt/crashportal_uploads/coredumps/".to_string(); } else { println!("starting minidump processing..."); - dump_paths.set_working_dir(dump_paths.get_minidumps_path().to_string()); - dump_paths.set_dumps_extn("*.dmp*".to_string()); - dump_paths.set_tar_extn(".dmp.tgz".to_string()); - dump_paths.set_lock_dir_prefix("/tmp/.uploadMinidumps".to_string()); - dump_paths.set_crash_portal_path("/opt/crashportal_uploads/coredumps/".to_string()); + dump_paths.dump_name = "minidump".to_string(); + dump_paths.working_dir = dump_paths.get_minidumps_path().to_string(); + dump_paths.dumps_extn ="*.dmp*".to_string(); + dump_paths.tar_extn = "*.dmp.tgz".to_string(); + dump_paths.lock_dir_prefix = "/tmp/.uploadMinidumps".to_string(); + dump_paths.crash_portal_path = "/opt/crashportal_uploads/coredumps/".to_string(); sleep(5); }; + dump_paths.ts_file = format!("/tmp/.{}_upload_timestamps", dump_paths.dump_name); + if wait_for_lock == "wait_for_lock" { + create_lock_or_wait(dump_paths.get_lock_dir_prefix()); + } + else { + create_lock_or_exit(dump_paths.get_lock_dir_prefix()); + } + + if device_data.device_type == "hybrid" || device_data.device_type == "mediaclient" { + let uptime_str = fs::read_to_string("/proc/uptime").expect("Unable to read uptime"); + let uptime_val = uptime_str.split('.').next().unwrap_or("0").trim().parse::().unwrap_or(0); + if uptime_val < 480 { + let sleep_time = 480 - uptime_val; + println!("Deferring reboot for {} seconds", sleep_time); + sleep(sleep_time); + if Path::new(CRASH_UPLOAD_REBOOT_FLAG).exists() { + println!("Process crashed exiting from the Deferring reboot"); + } + } + } + + let w_dir = dump_paths.get_working_dir(); match fs::read_dir(w_dir) { Ok(mut entries) => entries.next().is_none(), Err(_) => { eprintln!("working dir is empty : {}", w_dir); - std::process::exit(1); + std::process::exit(0); } }; @@ -113,8 +138,67 @@ fn main() { get_rfc_param("Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.CrashUpload.crashPortalSTBUrl", &mut portal_url); let req_type = 17; - let mut encryption_enabled = false; - if Path::new("/etc/os-release").exists() { encryption_enabled = set_rfc_param("Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.CrashUpload.encryptionEnabled", "true") }; + let mut counter = 1; + let mut no_network = false; + let route_file = Path::new(constants::NETWORK_FILE); + + while counter <= constants::NETWORK_CHECK_ITERATION { + println!("Check network status count {}", counter); + if route_file.exists() { + println!("Network is available"); + break; + } else { + println!("Network is not available, Sleep for {} seconds", NETWORK_CHECK_TIMEOUT); + sleep(constants::NETWORK_CHECK_TIMEOUT as u64); + counter += 1; + } + } + + if !route_file.exists() { + println!("Network is not available. tar dump and save it, as max wait reached"); + no_network = true; + } + + println!("IP Acquisition completed, Check if system time is received"); + + let stt_file = Path::new(constants::SYSTEM_TIME_FILE); + if !stt_file.exists() { + while counter <= constants::SYSTEM_TIME_ITERATION { + if !stt_file.exists() { + println!("Waiting for STT, iteration {}", counter); + sleep(constants::SYSTEM_TIME_TIMEOUT as u64); + } else { + println!("Received {} flag", constants::SYSTEM_TIME_FILE); + break; + } + + if counter == constants::SYSTEM_TIME_ITERATION { + println!("Continue without {} flag", constants::SYSTEM_TIME_FILE); + } + counter += 1; + } + } + else { + println!("Received {} flag", constants::SYSTEM_TIME_FILE); + } + + // trap finalize EXIT + + if !Path::new(COREDUMP_MTX_FILE).exists() && dump_flag == 1 { + println!("Waiting for Coredump completion"); + sleep(21); + } + + println!("Mac Address is {}", device_data.mac_addr); + + let count = 2; // TODO: get the count from the file + + if count == 0 { + println!("No {} for uploading exist", dump_paths.dump_name); + exit(0); + } + //cleanup(&dump_paths); + println!("Portal URL is {}", portal_url); } diff --git a/crates/utils/src/device_info.rs b/crates/utils/src/device_info.rs index 0ed722e..183a545 100644 --- a/crates/utils/src/device_info.rs +++ b/crates/utils/src/device_info.rs @@ -1,12 +1,15 @@ // utils/src/device_info.rs use std::fs::File; -use std::io::{BufRead, BufReader}; +use std::io::{self, BufRead, BufReader, Read}; use std::path::Path; +use std::process::Command; // Module-level constants -const DEVICE_PROP_FILE: &str = "/etc/device.properties"; -const INCLUDE_PROP_FILE: &str = "/etc/include.properties"; -const COMMON_PROP_FILE: &str = "/etc/common.properties"; +pub const DEVICE_PROP_FILE: &str = "/etc/device.properties"; +pub const INCLUDE_PROP_FILE: &str = "/etc/include.properties"; +pub const COMMON_PROP_FILE: &str = "/etc/common.properties"; +pub const DEVICE_MAC_FILE: &str = "/tmp/.macAddress"; +pub const VERSION_FILE: &str = "/version.txt"; // use this fn to getModel() using "MODEL_NUM" key pub fn get_property_value_from_file, K: AsRef>(path: P, key: K, val: &mut String) -> bool { @@ -32,3 +35,29 @@ pub fn get_property_value_from_file, K: AsRef>(path: P, key: } false } + +// TODO +pub fn get_sha1_value(sha1_val: &mut String) -> bool { + let output = match Command::new("sha1sum") + .arg(VERSION_FILE) + .output() { + Ok(output) => output, + Err(_) => return false, + }; + + let stdout = String::from_utf8_lossy(&output.stdout); + let sha1_hash = stdout.split_whitespace().next().unwrap_or("").to_string(); + *sha1_val = sha1_hash; + true +} + +pub fn get_device_mac(mac: &mut String) -> io::Result<()> { + let mut mac_val = String::new(); + let mut file = File::open(DEVICE_MAC_FILE)?; + + file.read_to_string(&mut mac_val)?; + + *mac = mac_val.trim().replace(":", ""); + Ok(()) +} + diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index 17cc659..3f65271 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -3,4 +3,4 @@ mod device_info; pub const UTILS_LIB_VER: &str = "v1.0"; pub use command::{rm, rm_rf, sleep, touch}; -pub use device_info::get_property_value_from_file; +pub use device_info::*; diff --git a/test/utils_test/src/main.rs b/test/utils_test/src/main.rs index b9aab89..47bd8dc 100644 --- a/test/utils_test/src/main.rs +++ b/test/utils_test/src/main.rs @@ -2,15 +2,15 @@ use std::fs::{self, File}; use std::io::Write; use std::path::PathBuf; -fn create_mock_property_file() { - let mut mock_props_file = File::create("device.properties").unwrap(); - let content = "MODEL_NUM=TestModel\nOTHER_KEY=OtherValue"; - mock_props_file.write_all(content.as_bytes()).unwrap(); + +fn create_file_with_content(file_path: &str, content: &str) { + let mut file = File::create(file_path).unwrap(); + file.write_all(content.as_bytes()).unwrap(); } -fn remove_mock_property_file() { - if PathBuf::from("device.properties").exists() { - fs::remove_file("device.properties").unwrap(); +fn remove_file(file_path: &str) { + if PathBuf::from(file_path).exists() { + fs::remove_file(file_path).unwrap(); } } @@ -19,27 +19,60 @@ mod tests { use super::*; use utils::*; - // Test for get_property_value_from_file function + // Test for get_property_value_from_file() function #[test] fn test_get_property_value_from_file() { - create_mock_property_file(); + create_file_with_content("device.properties","MODEL_NUM=TestModel"); let mut value = String::new(); let res = get_property_value_from_file("device.properties", "MODEL_NUM", &mut value); assert_eq!(res, true); assert_eq!(value, "TestModel"); - remove_mock_property_file(); + remove_file("device.properties"); } #[test] fn test_get_property_value_from_file_no_key() { - create_mock_property_file(); + create_file_with_content("device.properties","MODEL_NUM=TestModel"); let mut value = String::new(); let res = get_property_value_from_file("device.properties", "DEVICE_TYPE", &mut value); assert_eq!(res, false); assert!(value.is_empty()); - remove_mock_property_file(); + remove_file("device.properties"); + } + + // Test for get_device_mac() function + #[test] + fn test_get_device_mac_success_with_colon() { + create_file_with_content("/tmp/.macAddress", "00:11:22:33:44:55"); + let mut mac = String::new(); + let res = get_device_mac(&mut mac); + assert_eq!(res.is_ok(), true); + assert_eq!(mac, "001122334455"); + + remove_file("/tmp/.macAddress"); + } + + fn test_get_device_mac_success_no_colon() { + create_file_with_content("/tmp/.macAddress", "001122334455"); + let mut mac = String::new(); + let res = get_device_mac(&mut mac); + assert_eq!(res.is_ok(), true); + assert_eq!(mac, "001122334455"); + + remove_file("/tmp/.macAddress"); + } + + #[test] + fn test_get_device_mac_failure() { + create_file_with_content("/tmp/.macAddress", ""); + let mut mac = String::new(); + let res = get_device_mac(&mut mac); + assert_eq!(res.is_err(), false); + assert_eq!(mac.is_empty(), true); + + remove_file("/tmp/.macAddress"); } // Test for touch function From d4bef0c84a9f73c891b87fbbca1a71ce0714cba7 Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Mon, 26 May 2025 20:23:54 +0530 Subject: [PATCH 10/66] Update APIs for crashupload_utils.rs Signed-off-by: gomathishankar37 --- crash_upload/src/crashupload_utils.rs | 331 +++++++++++++++++++------- crash_upload/src/main.rs | 12 +- 2 files changed, 249 insertions(+), 94 deletions(-) diff --git a/crash_upload/src/crashupload_utils.rs b/crash_upload/src/crashupload_utils.rs index be9e9ab..9628ca7 100644 --- a/crash_upload/src/crashupload_utils.rs +++ b/crash_upload/src/crashupload_utils.rs @@ -1,10 +1,11 @@ // src/utils.rs use chrono::{DateTime, Local}; -use std::fs::{self, File, OpenOptions}; +use std::ffi::OsStr; +use std::fs::{self, File, Metadata, OpenOptions}; use std::io::{self, BufRead, BufReader, BufWriter, Read, Seek, SeekFrom, Write}; use std::path::{Path, PathBuf}; use std::{process, usize}; -use std::time::{SystemTime, UNIX_EPOCH}; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; use std::{thread, time}; use platform_interface::*; @@ -17,13 +18,22 @@ const TIMESTAMP_DEFAULT_VALUE: &str = "2000-01-01-00-00-00"; const MAC_DEFAULT_VALUE: &str = "000000000000"; const MODEL_NUM_DEFAULT_VALUE: &str = "UNKNOWN"; +fn lock_path>(path: P) -> PathBuf { + let mut p = path.as_ref().to_path_buf(); + p.set_extension("lock.d"); + p +} + +fn is_another_instance_running>(path: P) -> bool { + lock_path(path).is_dir() +} pub fn set_device_data(device_data: &mut DeviceData) { get_property_value_from_file(DEVICE_PROP_FILE, "BOX_TYPE",&mut device_data.box_type); get_property_value_from_file(DEVICE_PROP_FILE, "MODEL_NUM",&mut device_data.model_num); get_property_value_from_file(DEVICE_PROP_FILE, "DEVICE_TYPE", &mut device_data.device_type); get_sha1_value(&mut device_data.sha1); - get_device_mac(&mut device_data.mac_addr); + let _ = get_device_mac(&mut device_data.mac_addr); get_property_value_from_file(DEVICE_PROP_FILE, "BUILD_TYPE", &mut device_data.build_type); device_data.t2_enabled = Path::new("/lib/rdk/t2Shared_api.sh").exists(); device_data.tls = if Path::new("/etc/os-release").exists() { "--tlsv1.2".to_string() } else { "".to_string() }; @@ -128,12 +138,6 @@ pub fn get_secure_dump_status(dump_paths: &mut DumpPaths) { } } -fn lock_path>(path: P) -> PathBuf { - let mut p = path.as_ref().to_path_buf(); - p.set_extension("lock.d"); - p -} - pub fn create_lock_or_exit>(path: P) -> bool { let lock = lock_path(&path); if is_another_instance_running(&path) { @@ -226,7 +230,7 @@ pub fn is_upload_limit_reached(ts_file: &String) -> bool { let mut reader: BufReader<&File> = BufReader::new(&file); let line_count = reader.by_ref().lines().count(); - if line_count < 0 { + if line_count < 10 { return false; } @@ -325,7 +329,7 @@ pub fn delete_all_but_most_recent_files>(dir_path: P) -> Result<() } pub fn finalize(dump_paths: &DumpPaths) { - //cleanup(dump_paths); + let _ = cleanup(&dump_paths.working_dir, &dump_paths.dump_name, &dump_paths.dumps_extn); let loop_file = Path::new(CRASH_LOOP_FLAG_FILE); if loop_file.exists() { rm_rf(loop_file); @@ -344,6 +348,16 @@ pub fn sig_term_function(dump_paths: &DumpPaths) { remove_lock(dump_paths.get_ts_file()); } +pub fn sig_kill_function(dump_paths: &DumpPaths) { + println!("systemd killing, Removing the script locks"); + let loop_file = Path::new(CRASH_LOOP_FLAG_FILE); + if loop_file.exists() { + rm_rf(loop_file); + } + remove_lock(dump_paths.get_lock_dir_prefix()); + remove_lock(dump_paths.get_ts_file()); +} + pub fn should_process_dump>(dump_paths: &DumpPaths, device_data: &DeviceData, file_name: P) -> bool { let f_name = file_name.as_ref(); let status = if dump_paths.dump_name == "minidump" || device_data.device_type != "prod" || f_name.contains("Receiver") { @@ -355,11 +369,206 @@ pub fn should_process_dump>(dump_paths: &DumpPaths, device_data: & }; status } + +pub fn get_dump_count(path: &String, extn: &String) -> io::Result { + let mut count = 0; + let dir_path = Path::new(path); + + if dir_path.exists() && dir_path.is_dir() { + for entry in fs::read_dir(path)? { + let entry = entry?; + let file_path = entry.path(); + if file_path.is_file(){ + if let Some(ext) = file_path.extension() { + if ext.to_string_lossy() == extn.to_string() { + count += 1; + } + } + } + } + } + Ok(count) +} + +pub fn cleanup(work_dir: &String, dump_name: &String, dump_extn: &String) -> io::Result<()>{ + let work_dir_path = Path::new(work_dir); + + if !work_dir_path.exists() || !work_dir_path.is_dir() || work_dir_path.read_dir()?.next().is_none() { + println!("Working directory {} is empty", work_dir); + return Ok(()); + } + + println!("Cleanup {} directory {}", dump_name, work_dir); + + let cut_off_time = SystemTime::now() - Duration::from_secs(60 * 60 * 24 * 2); // 2 days + for entry in fs::read_dir(work_dir_path)? { + let entry = entry?; + let file_path = entry.path(); + if file_path.is_file() { + if let Some(file_name) = file_path.file_name() { + let file_name_str = file_name.to_string_lossy(); + + // _mac comes befire _dat in the wildcard matching + if let Some(mac_pos) = file_name_str.find("_mac") { + if let Some(dat_pos) = file_name_str.find("_dat") { + if mac_pos < dat_pos { + let metadata = fs::metadata(&file_path)?; + if let Ok(modified_time) = metadata.modified() { + if modified_time < cut_off_time { + fs::remove_file(&file_path)?; + println!("Removed file: {}", file_path.display()); + } + } + } + } + } + } + } + } + // find and while loop logic + if !Path::new(UPLOAD_ON_STARTUP).exists() { + rm_rf(format!("{}/version.txt", work_dir)); + let on_startup_dumps_cleaned_up_str = format!("{}_{}", ON_STARTUP_DUMPS_CLEANED_UP_BASE, if dump_name == "coredump" { "1" } else { "" }); + let on_startup_dumps_cleaned_up_path = Path::new(&on_startup_dumps_cleaned_up_str); + + if !on_startup_dumps_cleaned_up_path.exists() { + let path = Path::new(UPLOAD_ON_STARTUP); + + let mut deleted_files = Vec::new(); + for entry in fs::read_dir(path)? { + let entry = entry?; + let file_path = entry.path(); + + if file_path.is_file() { + if let Some(file_name) = file_path.file_name() { + let file_name_str = file_name.to_string_lossy(); + + if let Some(mac_pos) = file_name_str.find("_mac") { + if let Some(dat_pos) = file_name_str.find("_dat") { + if mac_pos < dat_pos { + fs::remove_file(&file_path)?; + deleted_files.push(file_path.to_string_lossy().into_owned()); + } + } + } + } + } + } + println!("Deleting unfinished files: {:?}", deleted_files); + + let mut non_dump_files = Vec::new(); + for entry in fs::read_dir(path)? { + let entry = entry?; + let file_path = entry.path(); + if file_path.is_file() { + if let Some(ext) = file_path.extension() { + if ext.to_string_lossy() != dump_extn.as_str() { + fs::remove_file(&file_path)?; + non_dump_files.push(file_path.to_string_lossy().into_owned()); + } + } + } + } + println!("Deleting non-dump files: {:?}", non_dump_files); + let _ = delete_all_but_most_recent_files(path.to_str().unwrap_or_default()); + touch(on_startup_dumps_cleaned_up_str.as_str()); + } + } + else { + if dump_name == "coredump" { + rm_rf(UPLOAD_ON_STARTUP); + } + } + Ok(()) +} + + +pub fn remove_pending_dumps(path: &String, extn: &String) -> io::Result<()>{ + let dir_path = Path::new(path); + + if dir_path.exists() && dir_path.is_dir() { + for entry in fs::read_dir(dir_path)? { + let entry = entry?; + let file_path = entry.path(); + if file_path.is_file() { + if let Some(ext) = file_path.extension() { + let ext_str = ext.to_string_lossy(); + if ext_str == extn.as_str() || ext_str == "tgz" { + println!("Removing {} because upload limit has reached or build is blacklisted or TelemetryOptOut is set", file_path.display()); + rm_rf(path); + } + } + } + } + } + Ok(()) +} + +fn process_crash_t2_info(file_path: &String) { + println!("Processing the crash telemetry info"); + let file = Path::new(file_path); + let mut file_name_str = file_path.clone(); + let container_delimiter = "<#=#>"; + let backward_delimiter = "-"; + let mut is_tar = false; + let extension = file.extension().and_then(OsStr::to_str); + + if extension == Some("tgz") { + println!("The File is already a tarball, this might be a retry or crash during shutdown"); + is_tar = true; + + if let Some(pos) = file_name_str.find("_mod_") { + file_name_str = file_name_str.split_off(pos + "_mod_".len()); + } + println!("Original Filename: {}", file.display()); + println!("Removing the meta information New Filename: {}", file_name_str); + println!("This could be a retry or crash from previous boot the appname can be truncated"); + t2_count_notify("SYS_INFO_TGZDUMP", Some("1")); + } + let mut is_container = false; + if file_name_str.contains(container_delimiter) { + is_container = true; + + let parts: Vec<&str> = file_name_str.split(container_delimiter).collect(); + println!("From the file name crashed process is a container"); + + if parts.len() >= 2 { + let container_name = parts[0]; + let container_time = parts[1]; + + let (container_status, file): (String, String) = if parts.len() > 2 { + (parts[1].to_string(), container_name.to_string()) + } else { + ("unknown".to_string(), container_name.to_string()) + }; + + let app_name = container_name.split('_').nth(1).unwrap_or(container_name); + let process_name = container_name.split('_').next().unwrap_or(container_name); + t2_val_notify("crashedContainerName_split", container_name); + t2_val_notify("crashedContainerStatus_split", &container_status); + t2_val_notify("crashedContainerAppname_split", app_name); + t2_val_notify("crashedContainerProcessName_split", process_name); + t2_count_notify("SYS_INFO_CrashedContainer", Some("1")); + + println!("Container crash info Basic: {}, {}", app_name, process_name); + println!("Container crash info Advanced: {}, {}", container_name, container_status); + println!("NEW Appname, Process_Crashed, Status = {}, {}, {}", app_name, process_name, container_status); + println!("NEW Processname, App Name, AppState = {}, {}, {}", process_name, app_name, container_status); + println!("ContainerName, ContainerStatus = {}, {}", container_name, container_status); + println!("NewProcessCrash_split {}, {}", container_name, container_status); + } + } + let _ = get_crashed_log_file(&file_name_str); +} + /* // ============================================ In Progress Start ============================================ // TODO: Implement this function -pub fn process_dump(dump_paths: &DumpPaths) { +pub fn mark_as_crash_loop_and_upload() { +} +// TODO: Implement this function +pub fn process_dump(dump_paths: &DumpPaths) { } @@ -436,39 +645,10 @@ pub fn copy_log_files_to_tmp(tmp_dir: &String) { // println!("Total pending Minidumps: {}", count); // } -pub fn cleanup(dump_paths: &DumpPaths) { - let work_dir = Path::new(dump_paths.get_working_dir()); - if !work_dir.exists() || !work_dir.is_dir() || work_dir.read_dir().unwrap().next().is_none() { - println!("Working directory {} is empty", work_dir.display()); - return; - } - println!("Cleanup {} directory {}", dump_paths.dump_name, dump_paths.working_dir); - - // Loop deletes - - if !Path::new(UPLOAD_ON_STARTUP).exists() { - rm_rf(format!("{}/version.txt", dump_paths.working_dir)); - let on_startup_dumps_cleaned_up_str = format!("{}_{}", ON_STARTUP_DUMPS_CLEANED_UP_BASE, if dump_paths.dump_name == "coredump" { "1" } else { "" }); - let on_startup_dumps_cleaned_up_path = Path::new(on_startup_dumps_cleaned_up_str.as_str()); - if !on_startup_dumps_cleaned_up_path.exists() { - // Find and Call delete_all_but_most_recent_files() - } - } - else { - if dump_paths.dump_name == "coredump" { - rm_rf(UPLOAD_ON_STARTUP); - } - } -} - -// =========================================== In Progress End ============================================ */ +// =========================================== In Progress End ============================================ -fn is_another_instance_running>(path: P) -> bool { - lock_path(path).is_dir() -} - -pub fn upload_timestamp(ts_file: &String) { +pub fn log_upload_timestamp(ts_file: &String) { let mut dev_type = String::new(); get_property_value_from_file("/etc/device.properties", "BUILD_TYPE", &mut dev_type); if dev_type == "prod" { @@ -478,52 +658,25 @@ pub fn upload_timestamp(ts_file: &String) { .as_secs(); let _ = fs::write(ts_file, now.to_string()); - truncate_timestamp_file(ts_file); - } -} - -pub fn truncate_timestamp_file(ts_file: &String) { - // if let Err(err) = OpenOptions::new().create(true).append(true).open(ts_file) { - // eprintln!("Failed to create or open {}: {}", ts_file, err); - // return; - // } - - // let file = match File::open(ts_file) { - // Ok(f) => f, - // Err(err) => { - // eprintln!("Error opening file {}: {}", ts_file, err); - // return; - // } - // }; - // let reader = BufReader::new(file); - // let lines: Vec = reader.lines().filter_map(Result::ok).collect(); - - // // Take the last 10 lines - // let last_10_lines = lines.iter().rev().take(10).cloned().collect::>(); - // let last_10_lines = last_10_lines.into_iter().rev().collect::>(); - - // // Write to temporary file - // let tmp_file_path = format!("{}_tmp", ts_file); - // match File::create(&tmp_file_path) { - // Ok(tmp_file) => { - // let mut writer = BufWriter::new(tmp_file); - // for line in last_10_lines { - // if let Err(err) = writeln!(writer, "{}", line){ - // eprintln!("Failed to write to temp file: {}", err); - // return; - // } - // } - // } - // Err(err) => { - // eprintln!("Failed to create temp file {}: {}", tmp_file_path, err); - // return; - // } - // } - - // // Replace original with temp file - // if let Err(err) = fs::rename(&tmp_file_path, ts_file) { - // eprintln!("Failed to replace original file with temp file: {}", err); - // } + let _ = truncate_timestamp_file(ts_file); + } +} + +fn truncate_timestamp_file(ts_file: &String) -> io::Result<()> { + let ts_path = Path::new(ts_file); + + let _ = OpenOptions::new().create(true).write(true).read(true).open(ts_path); + let file = fs::File::open(ts_path)?; + let reader = io::BufReader::new(file); + + let lines: Vec = reader.lines().map(|line| line.unwrap_or_default()).collect(); + let last_10_lines: Vec = lines.iter().rev().take(10).cloned().collect(); + let mut tmp_file = OpenOptions::new().write(true).truncate(true).open(ts_path)?; + + for line in last_10_lines { + writeln!(tmp_file, "{}", line)?; + } + Ok(()) } pub fn get_last_modified_time_of_file>(path: P) -> Option { diff --git a/crash_upload/src/main.rs b/crash_upload/src/main.rs index b7cc91f..efba1c8 100644 --- a/crash_upload/src/main.rs +++ b/crash_upload/src/main.rs @@ -191,14 +191,16 @@ fn main() { println!("Mac Address is {}", device_data.mac_addr); - let count = 2; // TODO: get the count from the file - - if count == 0 { + let mut dump_count = 0; + dump_count = match get_dump_count(&dump_paths.working_dir, &dump_paths.dumps_extn) { + Ok(e) => e, + Err(_) => 0, + }; + if dump_count == 0 { println!("No {} for uploading exist", dump_paths.dump_name); exit(0); } - - //cleanup(&dump_paths); + let _ = cleanup(&dump_paths.working_dir, &dump_paths.dump_name, &dump_paths.dumps_extn); println!("Portal URL is {}", portal_url); } From 5a631ff12297814d94df60bd115cdc9265b3c881 Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Tue, 27 May 2025 12:43:15 +0530 Subject: [PATCH 11/66] Sample PR Actions Signed-off-by: gomathishankar37 --- test/utils_test/src/main.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/utils_test/src/main.rs b/test/utils_test/src/main.rs index 47bd8dc..3639ab1 100644 --- a/test/utils_test/src/main.rs +++ b/test/utils_test/src/main.rs @@ -16,12 +16,15 @@ fn remove_file(file_path: &str) { #[cfg(test)] mod tests { + use std::{thread, time::Duration}; + use super::*; use utils::*; // Test for get_property_value_from_file() function #[test] fn test_get_property_value_from_file() { + thread::sleep(Duration::from_secs(2)); create_file_with_content("device.properties","MODEL_NUM=TestModel"); let mut value = String::new(); let res = get_property_value_from_file("device.properties", "MODEL_NUM", &mut value); @@ -33,6 +36,7 @@ mod tests { #[test] fn test_get_property_value_from_file_no_key() { + thread::sleep(Duration::from_secs(2)); create_file_with_content("device.properties","MODEL_NUM=TestModel"); let mut value = String::new(); let res = get_property_value_from_file("device.properties", "DEVICE_TYPE", &mut value); @@ -45,6 +49,7 @@ mod tests { // Test for get_device_mac() function #[test] fn test_get_device_mac_success_with_colon() { + thread::sleep(Duration::from_secs(2)); create_file_with_content("/tmp/.macAddress", "00:11:22:33:44:55"); let mut mac = String::new(); let res = get_device_mac(&mut mac); @@ -55,6 +60,7 @@ mod tests { } fn test_get_device_mac_success_no_colon() { + thread::sleep(Duration::from_secs(2)); create_file_with_content("/tmp/.macAddress", "001122334455"); let mut mac = String::new(); let res = get_device_mac(&mut mac); @@ -66,6 +72,7 @@ mod tests { #[test] fn test_get_device_mac_failure() { + thread::sleep(Duration::from_secs(2)); create_file_with_content("/tmp/.macAddress", ""); let mut mac = String::new(); let res = get_device_mac(&mut mac); @@ -78,6 +85,7 @@ mod tests { // Test for touch function #[test] fn test_touch() { + thread::sleep(Duration::from_secs(2)); touch("test_file.txt"); assert!(PathBuf::from("test_file.txt").exists()); rm("test_file.txt"); @@ -86,6 +94,7 @@ mod tests { // Test for rm function #[test] fn test_rm() { + thread::sleep(Duration::from_secs(2)); touch("test_file.txt"); rm("test_file.txt"); assert!(!PathBuf::from("test_file.txt").exists()); @@ -94,6 +103,7 @@ mod tests { // Test for rm_rf function #[test] fn test_rm_rf() { + thread::sleep(Duration::from_secs(2)); touch("test_file.txt"); rm_rf("test_file.txt"); assert!(!PathBuf::from("test_file.txt").exists()); @@ -108,6 +118,7 @@ mod tests { // Test for sleep function #[test] fn test_sleep() { + thread::sleep(Duration::from_secs(2)); let start = std::time::Instant::now(); sleep(5); let duration = start.elapsed(); From deaecc543f40fa3cd19c1d8f1b23464230b995ef Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Tue, 27 May 2025 12:47:29 +0530 Subject: [PATCH 12/66] Update GitHub workflows for Rust project --- .github/workflows/L1-Test.yaml | 54 ++++++++ .github/workflows/native_full_build.yml | 45 ++++++ .github/workflows/release_docs.yaml | 175 ++++++++++++++++++++++++ 3 files changed, 274 insertions(+) create mode 100644 .github/workflows/L1-Test.yaml create mode 100644 .github/workflows/native_full_build.yml create mode 100644 .github/workflows/release_docs.yaml diff --git a/.github/workflows/L1-Test.yaml b/.github/workflows/L1-Test.yaml new file mode 100644 index 0000000..87f6111 --- /dev/null +++ b/.github/workflows/L1-Test.yaml @@ -0,0 +1,54 @@ +name: L1 Testing & Coverage + +on: + pull_request: + branches: [ main, develop ] + push: + branches: [ main, develop ] + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + +jobs: + test-and-coverage: + name: Test & Coverage + runs-on: ubuntu-latest + container: + image: rust:latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + apt-get update && apt-get install -y git pkg-config libssl-dev + cargo install cargo-tarpaulin + + - name: Run all tests + run: cargo test --all-features --workspace --verbose + + - name: Run tests with tarpaulin for coverage + run: | + cargo tarpaulin --all-features --workspace --timeout 120 \ + --out xml --output-dir ./coverage/ \ + --exclude-files "tests/*" "benches/*" "**/tests.rs" "**/test_*.rs" \ + --verbose + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + file: ./coverage/cobertura.xml + flags: unittests + name: codecov-umbrella + fail_ci_if_error: false + + - name: Archive code coverage results + uses: actions/upload-artifact@v4 + with: + name: code-coverage-report + path: coverage/ + + - name: Clean target directory (ensure it's not persisted) + run: rm -rf target/ \ No newline at end of file diff --git a/.github/workflows/native_full_build.yml b/.github/workflows/native_full_build.yml new file mode 100644 index 0000000..d5c29e4 --- /dev/null +++ b/.github/workflows/native_full_build.yml @@ -0,0 +1,45 @@ +name: Native Full Build + +on: + pull_request: + branches: [ main, develop ] + push: + branches: [ main, develop ] + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + +jobs: + build: + name: Build Check + runs-on: ubuntu-latest + container: + image: rust:latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install additional components + run: | + rustup component add rustfmt clippy + apt-get update && apt-get install -y git + + - name: Check formatting + run: cargo fmt --all -- --check + + - name: Run clippy + run: cargo clippy --all-targets --all-features -- -D warnings + + - name: Run cargo check + run: cargo check --all-targets --all-features + + - name: Build all libraries and binaries (debug) + run: cargo build --all-targets --all-features + + - name: Build all libraries and binaries (release) + run: cargo build --release --all-targets --all-features + + - name: Clean target directory (ensure it's not persisted) + run: rm -rf target/ \ No newline at end of file diff --git a/.github/workflows/release_docs.yaml b/.github/workflows/release_docs.yaml new file mode 100644 index 0000000..2dcdb69 --- /dev/null +++ b/.github/workflows/release_docs.yaml @@ -0,0 +1,175 @@ +name: Release Documentation + +#on: + push: + branches: [ main ] + release: + types: [ published ] + +env: + CARGO_TERM_COLOR: always + +permissions: + contents: write + pages: write + id-token: write + +jobs: + docs: + name: Generate and Deploy Documentation + runs-on: ubuntu-latest + container: + image: rust:latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install dependencies + run: | + apt-get update && apt-get install -y git pkg-config libssl-dev + cargo install cargo-readme + + - name: Generate documentation + run: | + cargo doc --all-features --no-deps --workspace + echo '' > target/doc/index.html + + - name: Copy additional files to docs + run: | + cp README.md target/doc/ 2>/dev/null || true + cp LICENSE* target/doc/ 2>/dev/null || true + + - name: Setup Pages + uses: actions/configure-pages@v4 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: 'target/doc' + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 + + - name: Generate README from lib.rs + run: | + # Configure git for commit + git config --global --add safe.directory /__w/*/ + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + + # Find the main library crate and generate README + if [ -f "src/lib.rs" ]; then + cargo readme --input src/lib.rs --output README.md + elif find . -name "lib.rs" -path "*/src/lib.rs" | head -1 | xargs -I{} dirname {} | xargs -I{} dirname {} | head -1; then + # Handle workspace with multiple crates - use first one found + CRATE_DIR=$(find . -name "lib.rs" -path "*/src/lib.rs" | head -1 | xargs -I{} dirname {} | xargs -I{} dirname {}) + cd "$CRATE_DIR" + cargo readme --input src/lib.rs --output ../README.md + cd - > /dev/null + fi + + - name: Check if README changed and commit + run: | + if [ -f "README.md" ]; then + if ! git diff --quiet README.md 2>/dev/null; then + git add README.md + git commit -m "docs: auto-update README.md from lib.rs documentation" || echo "No changes to commit" + git push || echo "Nothing to push" + else + echo "README.md unchanged" + fi + else + echo "README.md not found" + fi + + - name: Clean target directory (ensure it's not persisted) + run: rm -rf target/ }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + + - name: Cache cargo + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-docs-${{ hashFiles('**/Cargo.lock') }} + + - name: Install cargo-readme + run: cargo install cargo-readme + + - name: Generate documentation + run: | + cargo doc --all-features --no-deps --workspace + echo '' > target/doc/index.html + + - name: Copy additional files to docs + run: | + cp README.md target/doc/ || true + cp LICENSE* target/doc/ || true + + - name: Setup Pages + uses: actions/configure-pages@v4 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: 'target/doc' + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 + + - name: Generate README from lib.rs + run: | + # Find the main library crate + if [ -f "src/lib.rs" ]; then + cargo readme --input src/lib.rs --output README.md + elif [ -f "*/src/lib.rs" ]; then + # Handle workspace with multiple crates + for crate_dir in */; do + if [ -f "${crate_dir}src/lib.rs" ] && [ -f "${crate_dir}Cargo.toml" ]; then + cd "$crate_dir" + cargo readme --input src/lib.rs --output ../README.md + cd .. + break + fi + done + fi + + - name: Check if README changed + id: readme_check + run: | + if git diff --quiet README.md; then + echo "changed=false" >> $GITHUB_OUTPUT + else + echo "changed=true" >> $GITHUB_OUTPUT + fi + + - name: Commit updated README + if: steps.readme_check.outputs.changed == 'true' + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git add README.md + git commit -m "docs: auto-update README.md from lib.rs documentation" + git push + + - name: Clean target directory + run: cargo clean \ No newline at end of file From b4ed2b485111c46af2f3e29f1e439332f2c0bf0f Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Tue, 27 May 2025 12:59:54 +0530 Subject: [PATCH 13/66] Run Cargo fmt Signed-off-by: gomathishankar37 --- crash_upload/src/constants.rs | 8 +- crash_upload/src/crashupload_utils.rs | 162 ++++++++++++++++++-------- crash_upload/src/main.rs | 52 +++++---- crates/utils/src/device_info.rs | 11 +- test/crash_upload_test/src/main.rs | 4 +- test/utils_test/src/main.rs | 5 +- 6 files changed, 159 insertions(+), 83 deletions(-) diff --git a/crash_upload/src/constants.rs b/crash_upload/src/constants.rs index e4f572e..aacb096 100644 --- a/crash_upload/src/constants.rs +++ b/crash_upload/src/constants.rs @@ -21,14 +21,15 @@ pub const TLS: &str = "--tlsv1.2"; pub const enable_oscp_stapling: &str = "/tmp/.EnableOCSPStapling"; pub const enable_oscp: &str = "/tmp/.EnableOCSPCA"; -pub const SECUREDUMP_TR181_NAME: &str = "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.SecDump.Enable"; +pub const SECUREDUMP_TR181_NAME: &str = + "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.SecDump.Enable"; pub const SECUREDUMP_ENABLE_FILE: &str = "/tmp/.SecureDumpEnable"; pub const SECUREDUMP_DISABLE_FILE: &str = "/tmp/.SecureDumpDisable"; pub const CRASH_UPLOAD_REBOOT_FLAG: &str = "/tmp/set_crash_reboot_flag"; pub const DENY_UPLOAD_FILE: &str = "/tmp/.deny_dump_uploads_till"; pub const UPLOAD_ON_STARTUP: &str = "/opt/.upload_on_startup"; -pub const ON_STARTUP_DUMPS_CLEANED_UP_BASE: &str ="/tmp/.on_startup_dumps_cleaned_up"; +pub const ON_STARTUP_DUMPS_CLEANED_UP_BASE: &str = "/tmp/.on_startup_dumps_cleaned_up"; pub const CRASH_LOOP_FLAG_FILE: &str = ""; // TODO pub const POTOMAC_USER: &str = "ccpstbscp"; @@ -131,7 +132,6 @@ impl DumpPaths { } } - // TODO: Add device_name member as well pub struct DeviceData { pub device_type: String, @@ -159,4 +159,4 @@ impl DeviceData { build_type: String::new(), } } -} \ No newline at end of file +} diff --git a/crash_upload/src/crashupload_utils.rs b/crash_upload/src/crashupload_utils.rs index 9628ca7..d973e63 100644 --- a/crash_upload/src/crashupload_utils.rs +++ b/crash_upload/src/crashupload_utils.rs @@ -4,13 +4,13 @@ use std::ffi::OsStr; use std::fs::{self, File, Metadata, OpenOptions}; use std::io::{self, BufRead, BufReader, BufWriter, Read, Seek, SeekFrom, Write}; use std::path::{Path, PathBuf}; -use std::{process, usize}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use std::{process, usize}; use std::{thread, time}; +use crate::constants::{self, *}; use platform_interface::*; use utils::*; -use crate::constants::{self, *}; // Module-level constants const SHA1_DEFAULT_VALUE: &str = "0000000000000000000000000000000000000000"; @@ -29,17 +29,28 @@ fn is_another_instance_running>(path: P) -> bool { } pub fn set_device_data(device_data: &mut DeviceData) { - get_property_value_from_file(DEVICE_PROP_FILE, "BOX_TYPE",&mut device_data.box_type); - get_property_value_from_file(DEVICE_PROP_FILE, "MODEL_NUM",&mut device_data.model_num); - get_property_value_from_file(DEVICE_PROP_FILE, "DEVICE_TYPE", &mut device_data.device_type); + get_property_value_from_file(DEVICE_PROP_FILE, "BOX_TYPE", &mut device_data.box_type); + get_property_value_from_file(DEVICE_PROP_FILE, "MODEL_NUM", &mut device_data.model_num); + get_property_value_from_file( + DEVICE_PROP_FILE, + "DEVICE_TYPE", + &mut device_data.device_type, + ); get_sha1_value(&mut device_data.sha1); let _ = get_device_mac(&mut device_data.mac_addr); get_property_value_from_file(DEVICE_PROP_FILE, "BUILD_TYPE", &mut device_data.build_type); device_data.t2_enabled = Path::new("/lib/rdk/t2Shared_api.sh").exists(); - device_data.tls = if Path::new("/etc/os-release").exists() { "--tlsv1.2".to_string() } else { "".to_string() }; - device_data.encryption_enabled = if Path::new("/etc/encryption_enabled").exists() { - set_rfc_param("Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.CrashUpload.encryptionEnabled", "true"); - true + device_data.tls = if Path::new("/etc/os-release").exists() { + "--tlsv1.2".to_string() + } else { + "".to_string() + }; + device_data.encryption_enabled = if Path::new("/etc/encryption_enabled").exists() { + set_rfc_param( + "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.CrashUpload.encryptionEnabled", + "true", + ); + true } else { false }; @@ -80,18 +91,26 @@ fn check_core_exist(core_dir: &str) -> bool { false } -pub fn set_log_file(device_data: &DeviceData, log_mod_ts: &str, line: &str){ +pub fn set_log_file(device_data: &DeviceData, log_mod_ts: &str, line: &str) { let file_name = line.split('/').last().unwrap_or_else(|| line); let file_processed = file_name.contains("_mac") || file_name.contains("_dat") || file_name.contains("_box") - || file_name.contains("_mod"); + || file_name.contains("_mod"); if file_processed { println!("{}", file_name); println!("Core name is already processed"); } else { - println!("{}_mac{}_dat{}_box{}_mod{}_{}", device_data.sha1, device_data.mac_addr, log_mod_ts, device_data.box_type, device_data.model_num, file_name); - } + println!( + "{}_mac{}_dat{}_box{}_mod{}_{}", + device_data.sha1, + device_data.mac_addr, + log_mod_ts, + device_data.box_type, + device_data.model_num, + file_name + ); + } } fn set_secure_dump_flag(is_sec_dump_enabled: &mut bool) { @@ -141,7 +160,10 @@ pub fn get_secure_dump_status(dump_paths: &mut DumpPaths) { pub fn create_lock_or_exit>(path: P) -> bool { let lock = lock_path(&path); if is_another_instance_running(&path) { - println!("Script is already working. {:?}. Skip launching another instance...", lock); + println!( + "Script is already working. {:?}. Skip launching another instance...", + lock + ); process::exit(0); } @@ -290,8 +312,9 @@ pub fn is_recovery_time_reached() -> bool { now > upload_denied_till } - -pub fn delete_all_but_most_recent_files>(dir_path: P) -> Result<(), Box> { +pub fn delete_all_but_most_recent_files>( + dir_path: P, +) -> Result<(), Box> { let path = Path::new(dir_path.as_ref()); let mut files: Vec<(PathBuf, SystemTime)> = fs::read_dir(path)? .filter_map(|entry| { @@ -329,7 +352,11 @@ pub fn delete_all_but_most_recent_files>(dir_path: P) -> Result<() } pub fn finalize(dump_paths: &DumpPaths) { - let _ = cleanup(&dump_paths.working_dir, &dump_paths.dump_name, &dump_paths.dumps_extn); + let _ = cleanup( + &dump_paths.working_dir, + &dump_paths.dump_name, + &dump_paths.dumps_extn, + ); let loop_file = Path::new(CRASH_LOOP_FLAG_FILE); if loop_file.exists() { rm_rf(loop_file); @@ -358,12 +385,18 @@ pub fn sig_kill_function(dump_paths: &DumpPaths) { remove_lock(dump_paths.get_ts_file()); } -pub fn should_process_dump>(dump_paths: &DumpPaths, device_data: &DeviceData, file_name: P) -> bool { +pub fn should_process_dump>( + dump_paths: &DumpPaths, + device_data: &DeviceData, + file_name: P, +) -> bool { let f_name = file_name.as_ref(); - let status = if dump_paths.dump_name == "minidump" || device_data.device_type != "prod" || f_name.contains("Receiver") { + let status = if dump_paths.dump_name == "minidump" + || device_data.device_type != "prod" + || f_name.contains("Receiver") + { true - } - else { + } else { println!("Not Processing dump file {}", f_name); false }; @@ -377,8 +410,8 @@ pub fn get_dump_count(path: &String, extn: &String) -> io::Result { if dir_path.exists() && dir_path.is_dir() { for entry in fs::read_dir(path)? { let entry = entry?; - let file_path = entry.path(); - if file_path.is_file(){ + let file_path = entry.path(); + if file_path.is_file() { if let Some(ext) = file_path.extension() { if ext.to_string_lossy() == extn.to_string() { count += 1; @@ -390,10 +423,13 @@ pub fn get_dump_count(path: &String, extn: &String) -> io::Result { Ok(count) } -pub fn cleanup(work_dir: &String, dump_name: &String, dump_extn: &String) -> io::Result<()>{ +pub fn cleanup(work_dir: &String, dump_name: &String, dump_extn: &String) -> io::Result<()> { let work_dir_path = Path::new(work_dir); - if !work_dir_path.exists() || !work_dir_path.is_dir() || work_dir_path.read_dir()?.next().is_none() { + if !work_dir_path.exists() + || !work_dir_path.is_dir() + || work_dir_path.read_dir()?.next().is_none() + { println!("Working directory {} is empty", work_dir); return Ok(()); } @@ -407,9 +443,9 @@ pub fn cleanup(work_dir: &String, dump_name: &String, dump_extn: &String) -> io: if file_path.is_file() { if let Some(file_name) = file_path.file_name() { let file_name_str = file_name.to_string_lossy(); - + // _mac comes befire _dat in the wildcard matching - if let Some(mac_pos) = file_name_str.find("_mac") { + if let Some(mac_pos) = file_name_str.find("_mac") { if let Some(dat_pos) = file_name_str.find("_dat") { if mac_pos < dat_pos { let metadata = fs::metadata(&file_path)?; @@ -428,12 +464,16 @@ pub fn cleanup(work_dir: &String, dump_name: &String, dump_extn: &String) -> io: // find and while loop logic if !Path::new(UPLOAD_ON_STARTUP).exists() { rm_rf(format!("{}/version.txt", work_dir)); - let on_startup_dumps_cleaned_up_str = format!("{}_{}", ON_STARTUP_DUMPS_CLEANED_UP_BASE, if dump_name == "coredump" { "1" } else { "" }); + let on_startup_dumps_cleaned_up_str = format!( + "{}_{}", + ON_STARTUP_DUMPS_CLEANED_UP_BASE, + if dump_name == "coredump" { "1" } else { "" } + ); let on_startup_dumps_cleaned_up_path = Path::new(&on_startup_dumps_cleaned_up_str); - + if !on_startup_dumps_cleaned_up_path.exists() { let path = Path::new(UPLOAD_ON_STARTUP); - + let mut deleted_files = Vec::new(); for entry in fs::read_dir(path)? { let entry = entry?; @@ -455,7 +495,7 @@ pub fn cleanup(work_dir: &String, dump_name: &String, dump_extn: &String) -> io: } } println!("Deleting unfinished files: {:?}", deleted_files); - + let mut non_dump_files = Vec::new(); for entry in fs::read_dir(path)? { let entry = entry?; @@ -473,8 +513,7 @@ pub fn cleanup(work_dir: &String, dump_name: &String, dump_extn: &String) -> io: let _ = delete_all_but_most_recent_files(path.to_str().unwrap_or_default()); touch(on_startup_dumps_cleaned_up_str.as_str()); } - } - else { + } else { if dump_name == "coredump" { rm_rf(UPLOAD_ON_STARTUP); } @@ -482,8 +521,7 @@ pub fn cleanup(work_dir: &String, dump_name: &String, dump_extn: &String) -> io: Ok(()) } - -pub fn remove_pending_dumps(path: &String, extn: &String) -> io::Result<()>{ +pub fn remove_pending_dumps(path: &String, extn: &String) -> io::Result<()> { let dir_path = Path::new(path); if dir_path.exists() && dir_path.is_dir() { @@ -521,7 +559,10 @@ fn process_crash_t2_info(file_path: &String) { file_name_str = file_name_str.split_off(pos + "_mod_".len()); } println!("Original Filename: {}", file.display()); - println!("Removing the meta information New Filename: {}", file_name_str); + println!( + "Removing the meta information New Filename: {}", + file_name_str + ); println!("This could be a retry or crash from previous boot the appname can be truncated"); t2_count_notify("SYS_INFO_TGZDUMP", Some("1")); } @@ -551,11 +592,26 @@ fn process_crash_t2_info(file_path: &String) { t2_count_notify("SYS_INFO_CrashedContainer", Some("1")); println!("Container crash info Basic: {}, {}", app_name, process_name); - println!("Container crash info Advanced: {}, {}", container_name, container_status); - println!("NEW Appname, Process_Crashed, Status = {}, {}, {}", app_name, process_name, container_status); - println!("NEW Processname, App Name, AppState = {}, {}, {}", process_name, app_name, container_status); - println!("ContainerName, ContainerStatus = {}, {}", container_name, container_status); - println!("NewProcessCrash_split {}, {}", container_name, container_status); + println!( + "Container crash info Advanced: {}, {}", + container_name, container_status + ); + println!( + "NEW Appname, Process_Crashed, Status = {}, {}, {}", + app_name, process_name, container_status + ); + println!( + "NEW Processname, App Name, AppState = {}, {}, {}", + process_name, app_name, container_status + ); + println!( + "ContainerName, ContainerStatus = {}, {}", + container_name, container_status + ); + println!( + "NewProcessCrash_split {}, {}", + container_name, container_status + ); } } let _ = get_crashed_log_file(&file_name_str); @@ -580,7 +636,7 @@ pub fn add_crashed_log_file(device_data: &DeviceData) { line_count = 500; } - + } pub fn copy_log_files_to_tmp(tmp_dir: &String) { @@ -610,7 +666,7 @@ pub fn copy_log_files_to_tmp(tmp_dir: &String) { println!("Logs copied to {} Temporary", tmp_directory); // Loop - + } } @@ -635,10 +691,10 @@ pub fn copy_log_files_to_tmp(tmp_dir: &String) { // while count > 5 { // if let Some(oldest_dump) = dump_files.iter() // .min_by_key(|path| fs::metadata(path).and_then(|m| m.modified()).unwrap_or(SystemTime::UNIX_EPOCH)) { - + // println!("Removing old dump {:?}", oldest_dump); // fs::remove_file(oldest_dump)?; - + // count -= 1; // Decrement count // } // } @@ -665,13 +721,23 @@ pub fn log_upload_timestamp(ts_file: &String) { fn truncate_timestamp_file(ts_file: &String) -> io::Result<()> { let ts_path = Path::new(ts_file); - let _ = OpenOptions::new().create(true).write(true).read(true).open(ts_path); + let _ = OpenOptions::new() + .create(true) + .write(true) + .read(true) + .open(ts_path); let file = fs::File::open(ts_path)?; let reader = io::BufReader::new(file); - let lines: Vec = reader.lines().map(|line| line.unwrap_or_default()).collect(); + let lines: Vec = reader + .lines() + .map(|line| line.unwrap_or_default()) + .collect(); let last_10_lines: Vec = lines.iter().rev().take(10).cloned().collect(); - let mut tmp_file = OpenOptions::new().write(true).truncate(true).open(ts_path)?; + let mut tmp_file = OpenOptions::new() + .write(true) + .truncate(true) + .open(ts_path)?; for line in last_10_lines { writeln!(tmp_file, "{}", line)?; diff --git a/crash_upload/src/main.rs b/crash_upload/src/main.rs index efba1c8..2937966 100644 --- a/crash_upload/src/main.rs +++ b/crash_upload/src/main.rs @@ -1,9 +1,9 @@ use std::process::exit; // standard library imports -use std::{env, fs, process}; use std::fs::create_dir; use std::os::unix::thread; use std::path::Path; +use std::{env, fs, process}; use chrono::Local; use constants::{DeviceData, COREDUMP_MTX_FILE, CRASH_UPLOAD_REBOOT_FLAG, NETWORK_CHECK_TIMEOUT}; @@ -30,7 +30,7 @@ fn main() { let mut dump_paths = constants::DumpPaths::new(); crashupload_utils::set_device_data(&mut device_data); - + //let crash_timestamp = utils::get_crash_timestamp(); let dump_flag = args[1].parse::().expect("Dump flag must be a number"); let upload_flag = &args[2]; @@ -81,22 +81,20 @@ fn main() { // let timestamp_filename = crashupload_utils::get_timestamp_filename(dump_name); let crash_ts = Local::now().format("%Y-%m-%d-%H-%M-%S").to_string(); - + if dump_flag == 1 { println!("starting core dump processing..."); - dump_paths.dump_name = "coredump".to_string(); + dump_paths.dump_name = "coredump".to_string(); dump_paths.working_dir = dump_paths.get_core_path().to_string(); dump_paths.dumps_extn = "*core.prog*.gz*".to_string(); dump_paths.tar_extn = ".core.tgz".to_string(); dump_paths.lock_dir_prefix = "/tmp/.uploadCoredumps".to_string(); dump_paths.crash_portal_path = "/opt/crashportal_uploads/coredumps/".to_string(); - } - else - { + } else { println!("starting minidump processing..."); - dump_paths.dump_name = "minidump".to_string(); + dump_paths.dump_name = "minidump".to_string(); dump_paths.working_dir = dump_paths.get_minidumps_path().to_string(); - dump_paths.dumps_extn ="*.dmp*".to_string(); + dump_paths.dumps_extn = "*.dmp*".to_string(); dump_paths.tar_extn = "*.dmp.tgz".to_string(); dump_paths.lock_dir_prefix = "/tmp/.uploadMinidumps".to_string(); dump_paths.crash_portal_path = "/opt/crashportal_uploads/coredumps/".to_string(); @@ -106,14 +104,19 @@ fn main() { if wait_for_lock == "wait_for_lock" { create_lock_or_wait(dump_paths.get_lock_dir_prefix()); - } - else { + } else { create_lock_or_exit(dump_paths.get_lock_dir_prefix()); } if device_data.device_type == "hybrid" || device_data.device_type == "mediaclient" { let uptime_str = fs::read_to_string("/proc/uptime").expect("Unable to read uptime"); - let uptime_val = uptime_str.split('.').next().unwrap_or("0").trim().parse::().unwrap_or(0); + let uptime_val = uptime_str + .split('.') + .next() + .unwrap_or("0") + .trim() + .parse::() + .unwrap_or(0); if uptime_val < 480 { let sleep_time = 480 - uptime_val; println!("Deferring reboot for {} seconds", sleep_time); @@ -123,8 +126,7 @@ fn main() { } } } - - + let w_dir = dump_paths.get_working_dir(); match fs::read_dir(w_dir) { Ok(mut entries) => entries.next().is_none(), @@ -135,7 +137,10 @@ fn main() { }; let mut portal_url = String::new(); - get_rfc_param("Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.CrashUpload.crashPortalSTBUrl", &mut portal_url); + get_rfc_param( + "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.CrashUpload.crashPortalSTBUrl", + &mut portal_url, + ); let req_type = 17; let mut counter = 1; @@ -148,7 +153,10 @@ fn main() { println!("Network is available"); break; } else { - println!("Network is not available, Sleep for {} seconds", NETWORK_CHECK_TIMEOUT); + println!( + "Network is not available, Sleep for {} seconds", + NETWORK_CHECK_TIMEOUT + ); sleep(constants::NETWORK_CHECK_TIMEOUT as u64); counter += 1; } @@ -176,9 +184,8 @@ fn main() { println!("Continue without {} flag", constants::SYSTEM_TIME_FILE); } counter += 1; - } - } - else { + } + } else { println!("Received {} flag", constants::SYSTEM_TIME_FILE); } @@ -200,7 +207,10 @@ fn main() { println!("No {} for uploading exist", dump_paths.dump_name); exit(0); } - let _ = cleanup(&dump_paths.working_dir, &dump_paths.dump_name, &dump_paths.dumps_extn); + let _ = cleanup( + &dump_paths.working_dir, + &dump_paths.dump_name, + &dump_paths.dumps_extn, + ); println!("Portal URL is {}", portal_url); - } diff --git a/crates/utils/src/device_info.rs b/crates/utils/src/device_info.rs index 183a545..d660fdb 100644 --- a/crates/utils/src/device_info.rs +++ b/crates/utils/src/device_info.rs @@ -12,7 +12,11 @@ pub const DEVICE_MAC_FILE: &str = "/tmp/.macAddress"; pub const VERSION_FILE: &str = "/version.txt"; // use this fn to getModel() using "MODEL_NUM" key -pub fn get_property_value_from_file, K: AsRef>(path: P, key: K, val: &mut String) -> bool { +pub fn get_property_value_from_file, K: AsRef>( + path: P, + key: K, + val: &mut String, +) -> bool { let key = key.as_ref().trim(); *val = String::new(); let prop_file = match File::open(path) { @@ -38,9 +42,7 @@ pub fn get_property_value_from_file, K: AsRef>(path: P, key: // TODO pub fn get_sha1_value(sha1_val: &mut String) -> bool { - let output = match Command::new("sha1sum") - .arg(VERSION_FILE) - .output() { + let output = match Command::new("sha1sum").arg(VERSION_FILE).output() { Ok(output) => output, Err(_) => return false, }; @@ -60,4 +62,3 @@ pub fn get_device_mac(mac: &mut String) -> io::Result<()> { *mac = mac_val.trim().replace(":", ""); Ok(()) } - diff --git a/test/crash_upload_test/src/main.rs b/test/crash_upload_test/src/main.rs index 78de2b7..891ede1 100644 --- a/test/crash_upload_test/src/main.rs +++ b/test/crash_upload_test/src/main.rs @@ -1,3 +1,3 @@ -fn main(){ +fn main() { println!("Hello, World!") -} \ No newline at end of file +} diff --git a/test/utils_test/src/main.rs b/test/utils_test/src/main.rs index 3639ab1..6deb34c 100644 --- a/test/utils_test/src/main.rs +++ b/test/utils_test/src/main.rs @@ -2,7 +2,6 @@ use std::fs::{self, File}; use std::io::Write; use std::path::PathBuf; - fn create_file_with_content(file_path: &str, content: &str) { let mut file = File::create(file_path).unwrap(); file.write_all(content.as_bytes()).unwrap(); @@ -25,7 +24,7 @@ mod tests { #[test] fn test_get_property_value_from_file() { thread::sleep(Duration::from_secs(2)); - create_file_with_content("device.properties","MODEL_NUM=TestModel"); + create_file_with_content("device.properties", "MODEL_NUM=TestModel"); let mut value = String::new(); let res = get_property_value_from_file("device.properties", "MODEL_NUM", &mut value); assert_eq!(res, true); @@ -37,7 +36,7 @@ mod tests { #[test] fn test_get_property_value_from_file_no_key() { thread::sleep(Duration::from_secs(2)); - create_file_with_content("device.properties","MODEL_NUM=TestModel"); + create_file_with_content("device.properties", "MODEL_NUM=TestModel"); let mut value = String::new(); let res = get_property_value_from_file("device.properties", "DEVICE_TYPE", &mut value); assert_eq!(res, false); From a735f83a7632f47f30882c7aad2d13641ce4f81e Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Tue, 27 May 2025 13:06:43 +0530 Subject: [PATCH 14/66] Update native_full_build.yml --- .github/workflows/native_full_build.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/native_full_build.yml b/.github/workflows/native_full_build.yml index d5c29e4..c785840 100644 --- a/.github/workflows/native_full_build.yml +++ b/.github/workflows/native_full_build.yml @@ -23,12 +23,9 @@ jobs: - name: Install additional components run: | - rustup component add rustfmt clippy + rustup component add clippy apt-get update && apt-get install -y git - - name: Check formatting - run: cargo fmt --all -- --check - - name: Run clippy run: cargo clippy --all-targets --all-features -- -D warnings @@ -42,4 +39,4 @@ jobs: run: cargo build --release --all-targets --all-features - name: Clean target directory (ensure it's not persisted) - run: rm -rf target/ \ No newline at end of file + run: rm -rf target/ From bfe3fac4b2692d57484b4de686ed4f181793b42b Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Tue, 27 May 2025 13:07:17 +0530 Subject: [PATCH 15/66] Update main.rs --- test/utils_test/src/main.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/test/utils_test/src/main.rs b/test/utils_test/src/main.rs index 6deb34c..9b36b5d 100644 --- a/test/utils_test/src/main.rs +++ b/test/utils_test/src/main.rs @@ -81,15 +81,6 @@ mod tests { remove_file("/tmp/.macAddress"); } - // Test for touch function - #[test] - fn test_touch() { - thread::sleep(Duration::from_secs(2)); - touch("test_file.txt"); - assert!(PathBuf::from("test_file.txt").exists()); - rm("test_file.txt"); - } - // Test for rm function #[test] fn test_rm() { From 17f0846974ae0cde283e431d3271ef2cb4116d2b Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Tue, 27 May 2025 16:23:16 +0530 Subject: [PATCH 16/66] Updated Crashupload Repo Signed-off-by: gomathishankar37 --- TODO.md | 50 +++++++++++++------ crash_upload/src/constants.rs | 6 +++ crash_upload/src/crashupload_utils.rs | 57 +++++++--------------- crash_upload/src/main.rs | 69 ++++++++++----------------- 4 files changed, 85 insertions(+), 97 deletions(-) diff --git a/TODO.md b/TODO.md index 8c4b653..09a3a77 100644 --- a/TODO.md +++ b/TODO.md @@ -1,21 +1,43 @@ TODO: - Capture existing uploadDumps.sh and uploadDumpsToS3.sh functionality wise in detail - Same process multiple crashes, Multiple process crashes (corner cases) - - Separate Logging log.rs, with WARN, INFO, ERROR, etc log levels - get_sha1() -> using sha1 crate/ process::Command() if single use - get_last_modified_time_of_file() -> using chrono crate/ process::Command() if single use - ~~args parsing in main app~~ -- upload dump utils - - ~~sanitize()~~ - - checkParam() - - finalize() - - sigkill_function() and sigterm_function() with trap usage - - truncateTimeStampFile() - - isUploadLimitReached() - - coreUpload() -> find origin - - saveDumps() - - processDumps() - - shouldProcessFile() - - get_crash_log_file() - \ No newline at end of file + +#### Function Implementations +---- +**COMPLETED** +- ~~setLogFile()~~ +- ~~create_lock_or_exit()~~ +- ~~create_lock_or_wait()~~ +- ~~remove_lock()~~ +- ~~sanitize()~~ +- ~~deleteAllButTheMostRecentFiles()~~ +- ~~cleanup()~~ +- ~~finalize()~~ +- ~~sigkill_function()~~ +- ~~sigterm_function()~~ +- ~~logUploadTimestamp()~~ +- ~~truncateTimeStampFile()~~ +- ~~isUploadLimitReached()~~ +- ~~setRecoveryTime()~~ +- ~~isRecoveryTimeReached()~~ +- ~~removePendingDumps()~~ +- ~~is_box_rebooting()~~ +- ~~shouldProcessFile()~~ +- ~~get_crashed_log_file()~~ +- ~~processCrashTelemtryInfo()~~ +---- +**TODO** +- add_crashed_log_file() +- copy_log_files_tmp_dir() +- processDumps() +- saveDump() +- markAsCrashLoopedAndUpload() +- checkParameter() +- logMessage() +- tlsLog() +- logStdout() +---- \ No newline at end of file diff --git a/crash_upload/src/constants.rs b/crash_upload/src/constants.rs index aacb096..b3494de 100644 --- a/crash_upload/src/constants.rs +++ b/crash_upload/src/constants.rs @@ -42,6 +42,12 @@ pub const NETWORK_CHECK_TIMEOUT: usize = 10; pub const SYSTEM_TIME_ITERATION: usize = 10; pub const SYSTEM_TIME_TIMEOUT: usize = 1; +pub const ENCRYPTION_RFC: &str = "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.CrashUpload.encryptionEnabled"; + +pub const SHA1_DEFAULT_VALUE: &str = "0000000000000000000000000000000000000000"; +pub const TIMESTAMP_DEFAULT_VALUE: &str = "2000-01-01-00-00-00"; +pub const MAC_DEFAULT_VALUE: &str = "000000000000"; +pub const MODEL_NUM_DEFAULT_VALUE: &str = "UNKNOWN"; pub struct DumpPaths { pub dump_name: String, pub core_path: String, diff --git a/crash_upload/src/crashupload_utils.rs b/crash_upload/src/crashupload_utils.rs index d973e63..7235206 100644 --- a/crash_upload/src/crashupload_utils.rs +++ b/crash_upload/src/crashupload_utils.rs @@ -8,16 +8,10 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH}; use std::{process, usize}; use std::{thread, time}; -use crate::constants::{self, *}; +use crate::constants::*; use platform_interface::*; use utils::*; -// Module-level constants -const SHA1_DEFAULT_VALUE: &str = "0000000000000000000000000000000000000000"; -const TIMESTAMP_DEFAULT_VALUE: &str = "2000-01-01-00-00-00"; -const MAC_DEFAULT_VALUE: &str = "000000000000"; -const MODEL_NUM_DEFAULT_VALUE: &str = "UNKNOWN"; - fn lock_path>(path: P) -> PathBuf { let mut p = path.as_ref().to_path_buf(); p.set_extension("lock.d"); @@ -31,34 +25,23 @@ fn is_another_instance_running>(path: P) -> bool { pub fn set_device_data(device_data: &mut DeviceData) { get_property_value_from_file(DEVICE_PROP_FILE, "BOX_TYPE", &mut device_data.box_type); get_property_value_from_file(DEVICE_PROP_FILE, "MODEL_NUM", &mut device_data.model_num); - get_property_value_from_file( - DEVICE_PROP_FILE, - "DEVICE_TYPE", - &mut device_data.device_type, - ); + get_property_value_from_file(DEVICE_PROP_FILE, "DEVICE_TYPE", &mut device_data.device_type); get_sha1_value(&mut device_data.sha1); let _ = get_device_mac(&mut device_data.mac_addr); get_property_value_from_file(DEVICE_PROP_FILE, "BUILD_TYPE", &mut device_data.build_type); device_data.t2_enabled = Path::new("/lib/rdk/t2Shared_api.sh").exists(); - device_data.tls = if Path::new("/etc/os-release").exists() { - "--tlsv1.2".to_string() - } else { - "".to_string() - }; + device_data.tls = if Path::new("/etc/os-release").exists() {"--tlsv1.2".to_string()} else {"".to_string()}; device_data.encryption_enabled = if Path::new("/etc/encryption_enabled").exists() { - set_rfc_param( - "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.CrashUpload.encryptionEnabled", - "true", - ); + set_rfc_param(ENCRYPTION_RFC,"true"); true } else { false - }; + } } -pub fn should_exit_crash_upload(dump_paths: &DumpPaths) -> bool { - let minidumps_exists = check_minidumps_exist(&dump_paths.minidumps_path); - let core_exists = check_core_exist(&dump_paths.core_path); +pub fn should_exit_crash_upload(minidumps_path: &String, core_path: &String) -> bool { + let minidumps_exists = check_minidumps_exist(&minidumps_path); + let core_exists = check_core_exist(&core_path); !(minidumps_exists || core_exists) } @@ -361,8 +344,8 @@ pub fn finalize(dump_paths: &DumpPaths) { if loop_file.exists() { rm_rf(loop_file); } - remove_lock(dump_paths.get_lock_dir_prefix()); - remove_lock(dump_paths.get_ts_file()); + remove_lock(&dump_paths.lock_dir_prefix); + remove_lock(&dump_paths.ts_file); } pub fn sig_term_function(dump_paths: &DumpPaths) { @@ -371,8 +354,8 @@ pub fn sig_term_function(dump_paths: &DumpPaths) { if loop_file.exists() { rm_rf(loop_file); } - remove_lock(dump_paths.get_lock_dir_prefix()); - remove_lock(dump_paths.get_ts_file()); + remove_lock(&dump_paths.lock_dir_prefix); + remove_lock(&dump_paths.ts_file); } pub fn sig_kill_function(dump_paths: &DumpPaths) { @@ -381,18 +364,18 @@ pub fn sig_kill_function(dump_paths: &DumpPaths) { if loop_file.exists() { rm_rf(loop_file); } - remove_lock(dump_paths.get_lock_dir_prefix()); - remove_lock(dump_paths.get_ts_file()); + remove_lock(&dump_paths.lock_dir_prefix); + remove_lock(&dump_paths.ts_file); } pub fn should_process_dump>( - dump_paths: &DumpPaths, - device_data: &DeviceData, + dump_name: &String, + device_type: &String, file_name: P, ) -> bool { let f_name = file_name.as_ref(); - let status = if dump_paths.dump_name == "minidump" - || device_data.device_type != "prod" + let status = if dump_name == "minidump" + || device_type != "prod" || f_name.contains("Receiver") { true @@ -759,10 +742,6 @@ pub fn get_last_modified_time_of_file>(path: P) -> Option Some(datetime.format("%Y-%m-%d-%H-%M-%S").to_string()) } -//pub fn process_crash_t2_info>(file_path: P) { -// TODO: -//} - fn get_crashed_log_file>(file_path: P) -> io::Result<()> { let file = file_path.as_ref(); diff --git a/crash_upload/src/main.rs b/crash_upload/src/main.rs index 2937966..f00df26 100644 --- a/crash_upload/src/main.rs +++ b/crash_upload/src/main.rs @@ -1,30 +1,32 @@ use std::process::exit; // standard library imports -use std::fs::create_dir; -use std::os::unix::thread; use std::path::Path; use std::{env, fs, process}; +// external crate imports use chrono::Local; -use constants::{DeviceData, COREDUMP_MTX_FILE, CRASH_UPLOAD_REBOOT_FLAG, NETWORK_CHECK_TIMEOUT}; -use crashupload_utils::*; -use platform_interface::*; -use utils::sleep; // crashupload internal module imports mod constants; mod crashupload_utils; +// crashupload_utils library module imports +use crashupload_utils::*; +use platform_interface::*; +use utils::*; + fn main() { println!("Starting Crash Upload Binary..."); - // EXEC: Command line argument parsing let args: Vec = env::args().collect(); if args.len() != 4 { eprintln!("Usage: {} ", args[0]); process::exit(1); } - // EXEC: Instantiate the DumpPaths & DeviceData structs + let dump_flag = args[1].parse::().expect("Dump flag must be a number"); + let upload_flag = &args[2]; + let wait_for_lock = &args[3]; + println!("Instantiating DumpPaths and DeviceData structs..."); let mut device_data = constants::DeviceData::new(); let mut dump_paths = constants::DumpPaths::new(); @@ -32,10 +34,6 @@ fn main() { crashupload_utils::set_device_data(&mut device_data); //let crash_timestamp = utils::get_crash_timestamp(); - let dump_flag = args[1].parse::().expect("Dump flag must be a number"); - let upload_flag = &args[2]; - let wait_for_lock = &args[3]; - // EXEC: Set the core and minidump paths based on the upload flag if upload_flag == "secure" { dump_paths.set_core_path("/opt/secure/corefiles".to_string()); @@ -45,43 +43,19 @@ fn main() { dump_paths.set_minidumps_path("/opt/minidumps".to_string()); } - if crashupload_utils::should_exit_crash_upload(&dump_paths) { + if crashupload_utils::should_exit_crash_upload(&dump_paths.minidumps_path, &dump_paths.core_path) { println!("Crash upload process is already running. Exiting..."); std::process::exit(0); } // EXEC: Secure Dump Status & path Set - //let _ = crashupload_utils::get_secure_dump_status(&mut dump_paths); + let _ = crashupload_utils::get_secure_dump_status(&mut dump_paths); - // core_log.txt file logging can be handled using syslog-ng - - // ============================== - /* TODO: Implementations for below functions - * logMessage() - * tlsLog() - * checkParameter() - * deleteAllButTheMostRecentFiles() - In progress - * cleanup() - * finalize() - * sigkill_function() - * sigterm_function() - * logUploadTimestamp() - * truncateTimeStampFile() - * removePendingDumps() - * markAsCrashLoopedAndUpload() - * saveDump() - * shouldProcessFile() - * get_crashed_log_file() - * processCrashTelemtryInfo() - * add_crashed_log_file() - * copy_log_files_tmp_dir() - * processDumps() - */ + // ============================== // let timestamp_filename = crashupload_utils::get_timestamp_filename(dump_name); let crash_ts = Local::now().format("%Y-%m-%d-%H-%M-%S").to_string(); - if dump_flag == 1 { println!("starting core dump processing..."); dump_paths.dump_name = "coredump".to_string(); @@ -103,9 +77,9 @@ fn main() { dump_paths.ts_file = format!("/tmp/.{}_upload_timestamps", dump_paths.dump_name); if wait_for_lock == "wait_for_lock" { - create_lock_or_wait(dump_paths.get_lock_dir_prefix()); + create_lock_or_wait(&dump_paths.lock_dir_prefix); } else { - create_lock_or_exit(dump_paths.get_lock_dir_prefix()); + create_lock_or_exit(&dump_paths.lock_dir_prefix); } if device_data.device_type == "hybrid" || device_data.device_type == "mediaclient" { @@ -121,7 +95,7 @@ fn main() { let sleep_time = 480 - uptime_val; println!("Deferring reboot for {} seconds", sleep_time); sleep(sleep_time); - if Path::new(CRASH_UPLOAD_REBOOT_FLAG).exists() { + if Path::new(constants::CRASH_UPLOAD_REBOOT_FLAG).exists() { println!("Process crashed exiting from the Deferring reboot"); } } @@ -155,7 +129,7 @@ fn main() { } else { println!( "Network is not available, Sleep for {} seconds", - NETWORK_CHECK_TIMEOUT + constants::NETWORK_CHECK_TIMEOUT ); sleep(constants::NETWORK_CHECK_TIMEOUT as u64); counter += 1; @@ -191,7 +165,7 @@ fn main() { // trap finalize EXIT - if !Path::new(COREDUMP_MTX_FILE).exists() && dump_flag == 1 { + if !Path::new(constants::COREDUMP_MTX_FILE).exists() && dump_flag == 1 { println!("Waiting for Coredump completion"); sleep(21); } @@ -213,4 +187,11 @@ fn main() { &dump_paths.dumps_extn, ); println!("Portal URL is {}", portal_url); + + println!("buildID is {}", device_data.sha1); + if !Path::new(w_dir).is_dir() { + exit(1); + } + + // For Loop to processDumps } From 8dfd44da923c7183517c49e12fefbd1111a3bcf8 Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Tue, 27 May 2025 16:24:18 +0530 Subject: [PATCH 17/66] Update native_full_build.yml --- .github/workflows/native_full_build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/native_full_build.yml b/.github/workflows/native_full_build.yml index c785840..7a6ed84 100644 --- a/.github/workflows/native_full_build.yml +++ b/.github/workflows/native_full_build.yml @@ -26,8 +26,8 @@ jobs: rustup component add clippy apt-get update && apt-get install -y git - - name: Run clippy - run: cargo clippy --all-targets --all-features -- -D warnings + # - name: Run clippy + # run: cargo clippy --all-targets --all-features -- -D warnings - name: Run cargo check run: cargo check --all-targets --all-features From 0c213c92a386eabac6329bd9efce27594e6a5ff9 Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Tue, 3 Jun 2025 18:33:14 +0530 Subject: [PATCH 18/66] Updated APIs with docstrings Signed-off-by: gomathishankar37 --- TODO.md | 19 +- crash_upload/src/constants.rs | 161 +- crash_upload/src/crashupload_utils.rs | 1721 ++++++++++++++++------ crash_upload/src/main.rs | 169 ++- crates/platform_interface/src/lib.rs | 10 + crates/platform_interface/src/rfc_api.rs | 53 +- crates/platform_interface/src/t2_api.rs | 49 +- crates/utils/src/command.rs | 62 +- crates/utils/src/device_info.rs | 69 +- crates/utils/src/lib.rs | 12 +- test/utils_test/src/main.rs | 20 - 11 files changed, 1656 insertions(+), 689 deletions(-) diff --git a/TODO.md b/TODO.md index 09a3a77..6c1df62 100644 --- a/TODO.md +++ b/TODO.md @@ -29,15 +29,16 @@ TODO: - ~~shouldProcessFile()~~ - ~~get_crashed_log_file()~~ - ~~processCrashTelemtryInfo()~~ +- ~~add_crashed_log_file()~~ - optional args +- ~~copy_log_files_tmp_dir()~~ - optional args +- ~~processDumps()~~ - requires add_crashed_log_file() and copy_log_files_tmp_dir() +- ~~saveDump()~~ +- ~~markAsCrashLoopedAndUpload()~~ +- checkParameter() - NA +- logMessage() - Not req +- tlsLog() - Not req +- logStdout() - Not req ---- **TODO** -- add_crashed_log_file() -- copy_log_files_tmp_dir() -- processDumps() -- saveDump() -- markAsCrashLoopedAndUpload() -- checkParameter() -- logMessage() -- tlsLog() -- logStdout() +- Signal Handling TRAPs ---- \ No newline at end of file diff --git a/crash_upload/src/constants.rs b/crash_upload/src/constants.rs index b3494de..7f28899 100644 --- a/crash_upload/src/constants.rs +++ b/crash_upload/src/constants.rs @@ -1,13 +1,20 @@ -use std::{alloc::System, time::SystemTime}; +//! Crash upload system constants and configuration structures. +//! +//! This module defines constants and configuration structs used throughout the crash upload system, +//! including file paths, RFC names, and device/dump metadata. -// src/constants.rs pub const LOGMAPPER_FILE: &str = "/etc/breakpad-logmapper.conf"; pub const LOG_FILES: &str = "/tmp/minidump_log_files.txt"; pub const LOG_PATH: &str = "/opt/rdk"; -pub const IS_T2_ENABLED: bool = false; pub const CORE_LOG: &str = "/opt/rdk/core_log.txt"; +pub const DEVICE_PROP_FILE: &str = "/opt/device.properties"; +pub const INCLUDE_PROP_FILE: &str = "/opt/include.properties"; +pub const COREDUMP_PROP_FILE: &str = "/opt/coredump.properties"; + +pub const T2_SHARED_SCRIPT: &str = "/lib/rdk/t2Shared_api.sh"; + pub const S3_BUCKET_URL: &str = "s3.amazonaws.com"; pub const HTTP_CODE_FILE: &str = "/tmp/httpcode"; pub const CURL_UPLOAD_TIMEOUT: usize = 45; @@ -18,11 +25,10 @@ pub const S3_FILENAME: &str = "s3filename"; pub const TLS: &str = "--tlsv1.2"; -pub const enable_oscp_stapling: &str = "/tmp/.EnableOCSPStapling"; -pub const enable_oscp: &str = "/tmp/.EnableOCSPCA"; +pub const ENABLE_OSCP_STAPLING: &str = "/tmp/.EnableOCSPStapling"; +pub const ENABLE_OSCP: &str = "/tmp/.EnableOCSPCA"; -pub const SECUREDUMP_TR181_NAME: &str = - "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.SecDump.Enable"; +pub const SECUREDUMP_TR181_NAME: &str = "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.SecDump.Enable"; pub const SECUREDUMP_ENABLE_FILE: &str = "/tmp/.SecureDumpEnable"; pub const SECUREDUMP_DISABLE_FILE: &str = "/tmp/.SecureDumpDisable"; pub const CRASH_UPLOAD_REBOOT_FLAG: &str = "/tmp/set_crash_reboot_flag"; @@ -43,11 +49,15 @@ pub const SYSTEM_TIME_ITERATION: usize = 10; pub const SYSTEM_TIME_TIMEOUT: usize = 1; pub const ENCRYPTION_RFC: &str = "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.CrashUpload.encryptionEnabled"; +pub const CRASH_PORTAL_URL_RFC: &str = "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.CrashUpload.crashPortalSTBUrl"; pub const SHA1_DEFAULT_VALUE: &str = "0000000000000000000000000000000000000000"; pub const TIMESTAMP_DEFAULT_VALUE: &str = "2000-01-01-00-00-00"; pub const MAC_DEFAULT_VALUE: &str = "000000000000"; pub const MODEL_NUM_DEFAULT_VALUE: &str = "UNKNOWN"; + +/// Holds all relevant paths and extensions for dump processing. +#[derive(Debug)] pub struct DumpPaths { pub dump_name: String, pub core_path: String, @@ -63,66 +73,9 @@ pub struct DumpPaths { } impl DumpPaths { - pub fn set_core_path(&mut self, path: String) { - self.core_path = path; - } - pub fn set_minidumps_path(&mut self, path: String) { - self.minidumps_path = path; - } - pub fn set_core_back_path(&mut self, path: String) { - self.core_back_path = path; - } - pub fn set_persistent_sec_path(&mut self, path: String) { - self.persistent_sec_path = path; - } - pub fn get_core_path(&self) -> &String { - &self.core_path - } - pub fn get_minidumps_path(&self) -> &String { - &self.minidumps_path - } - pub fn get_core_back_path(&self) -> &String { - &self.core_back_path - } - pub fn get_persistent_sec_path(&self) -> &String { - &self.persistent_sec_path - } - pub fn get_working_dir(&self) -> &String { - &self.working_dir - } - pub fn set_working_dir(&mut self, path: String) { - self.working_dir = path; - } - pub fn get_dumps_extn(&self) -> &String { - &self.dumps_extn - } - pub fn set_dumps_extn(&mut self, extn: String) { - self.dumps_extn = extn; - } - pub fn get_tar_extn(&self) -> &String { - &self.tar_extn - } - pub fn set_tar_extn(&mut self, extn: String) { - self.tar_extn = extn; - } - pub fn get_lock_dir_prefix(&self) -> &String { - &self.lock_dir_prefix - } - pub fn set_lock_dir_prefix(&mut self, prefix: String) { - self.lock_dir_prefix = prefix; - } - pub fn get_crash_portal_path(&self) -> &String { - &self.crash_portal_path - } - pub fn set_crash_portal_path(&mut self, path: String) { - self.crash_portal_path = path; - } - pub fn get_ts_file(&self) -> &String { - &self.ts_file - } /// Creates a new `DumpPaths` instance with default values. pub fn new() -> Self { - DumpPaths { + Self { dump_name: String::new(), core_path: String::new(), minidumps_path: String::new(), @@ -136,33 +89,95 @@ impl DumpPaths { ts_file: String::new(), } } + // Getters + pub fn get_dump_name(&self) -> &str { &self.dump_name } + pub fn get_core_path(&self) -> &str { &self.core_path } + pub fn get_minidumps_path(&self) -> &str { &self.minidumps_path } + pub fn get_core_back_path(&self) -> &str { &self.core_back_path } + pub fn get_persistent_sec_path(&self) -> &str { &self.persistent_sec_path } + pub fn get_working_dir(&self) -> &str { &self.working_dir } + pub fn get_dumps_extn(&self) -> &str { &self.dumps_extn } + pub fn get_tar_extn(&self) -> &str { &self.tar_extn } + pub fn get_lock_dir_prefix(&self) -> &str { &self.lock_dir_prefix } + pub fn get_crash_portal_path(&self) -> &str { &self.crash_portal_path } + pub fn get_ts_file(&self) -> &str { &self.ts_file } + + // Setters + pub fn set_dump_name(&mut self, value: impl Into) { self.dump_name = value.into(); } + pub fn set_core_path(&mut self, value: impl Into) { self.core_path = value.into(); } + pub fn set_minidumps_path(&mut self, value: impl Into) { self.minidumps_path = value.into(); } + pub fn set_core_back_path(&mut self, value: impl Into) { self.core_back_path = value.into(); } + pub fn set_persistent_sec_path(&mut self, value: impl Into) { self.persistent_sec_path = value.into(); } + pub fn set_working_dir(&mut self, value: impl Into) { self.working_dir = value.into(); } + pub fn set_dumps_extn(&mut self, value: impl Into) { self.dumps_extn = value.into(); } + pub fn set_tar_extn(&mut self, value: impl Into) { self.tar_extn = value.into(); } + pub fn set_lock_dir_prefix(&mut self, value: impl Into) { self.lock_dir_prefix = value.into(); } + pub fn set_crash_portal_path(&mut self, value: impl Into) { self.crash_portal_path = value.into(); } + pub fn set_ts_file(&mut self, value: impl Into) { self.ts_file = value.into(); } } -// TODO: Add device_name member as well +/// Holds device-specific metadata and configuration. +#[derive(Debug)] pub struct DeviceData { pub device_type: String, pub box_type: String, pub model_num: String, pub sha1: String, pub mac_addr: String, - pub t2_enabled: bool, - pub tls: String, - pub encryption_enabled: bool, + pub is_t2_enabled: bool, + pub is_tls_enabled: String, + pub is_encryption_enabled: bool, pub build_type: String, + pub portal_url: String, + //pub device_name: String, } impl DeviceData { + /// Creates a new `DeviceData` instance with default values. pub fn new() -> Self { - DeviceData { + Self { device_type: String::new(), box_type: String::new(), model_num: String::new(), sha1: String::new(), mac_addr: String::new(), - t2_enabled: false, - tls: String::new(), - encryption_enabled: false, + is_t2_enabled: false, + is_tls_enabled: String::new(), + is_encryption_enabled: false, build_type: String::new(), + portal_url: String::new(), + //device_name: String::new(), } } + // Getters + pub fn get_device_type(&self) -> &str { &self.device_type } + pub fn get_box_type(&self) -> &str { &self.box_type } + pub fn get_model_num(&self) -> &str { &self.model_num } + pub fn get_sha1(&self) -> &str { &self.sha1 } + pub fn get_mac_addr(&self) -> &str { &self.mac_addr } + pub fn get_is_t2_enabled(&self) -> bool { self.is_t2_enabled } + pub fn get_is_tls_enabled(&self) -> &str { &self.is_tls_enabled } + pub fn get_is_encryption_enabled(&self) -> bool { self.is_encryption_enabled } + pub fn get_build_type(&self) -> &str { &self.build_type } + pub fn get_portal_url(&self) -> &str { &self.portal_url } + //pub fn device_name(&self) -> &str { &self.device_name } + + // Setters + pub fn set_device_type(&mut self, value: impl Into) { self.device_type = value.into(); } + pub fn set_box_type(&mut self, value: impl Into) { self.box_type = value.into(); } + pub fn set_model_num(&mut self, value: impl Into) { self.model_num = value.into(); } + pub fn set_sha1(&mut self, value: impl Into) { self.sha1 = value.into(); } + pub fn set_mac_addr(&mut self, value: impl Into) { self.mac_addr = value.into(); } + pub fn set_t2_enabled(&mut self, value: bool) { self.is_t2_enabled = value; } + pub fn set_tls(&mut self, value: impl Into) { self.is_tls_enabled = value.into(); } + pub fn set_encryption_enabled(&mut self, value: bool) { self.is_encryption_enabled = value; } + pub fn set_build_type(&mut self, value: impl Into) { self.build_type = value.into(); } + pub fn set_portal_url(&mut self, value: impl Into) { self.portal_url = value.into(); } + //pub fn set_device_name(&mut self, value: impl Into) { self.device_name = value.into(); } +} + +/// Enum representing the type of signal received. +pub enum CrashSignal { + Term, + Kill, } diff --git a/crash_upload/src/crashupload_utils.rs b/crash_upload/src/crashupload_utils.rs index 7235206..2899349 100644 --- a/crash_upload/src/crashupload_utils.rs +++ b/crash_upload/src/crashupload_utils.rs @@ -1,9 +1,9 @@ // src/utils.rs use chrono::{DateTime, Local}; -use std::ffi::OsStr; -use std::fs::{self, File, Metadata, OpenOptions}; -use std::io::{self, BufRead, BufReader, BufWriter, Read, Seek, SeekFrom, Write}; +use std::fs::{self, File, OpenOptions}; +use std::io::{self, BufRead, BufReader, Write}; use std::path::{Path, PathBuf}; +use std::process::Command; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use std::{process, usize}; use std::{thread, time}; @@ -12,60 +12,102 @@ use crate::constants::*; use platform_interface::*; use utils::*; -fn lock_path>(path: P) -> PathBuf { - let mut p = path.as_ref().to_path_buf(); - p.set_extension("lock.d"); - p -} - -fn is_another_instance_running>(path: P) -> bool { - lock_path(path).is_dir() -} - +/// Populates a [`DeviceData`] struct with device properties from system files and TR-181. +/// +/// This function mirrors the environment setup in `uploadDumps.sh`, reading values from +/// device properties, version file, MAC address, and TR-181 parameters. It uses only +/// setters on the struct for encapsulation and future-proofing. +/// +/// # Arguments +/// * `device_data` - Mutable reference to a [`DeviceData`] struct to populate. +/// +/// # Side Effects +/// - Reads from the filesystem and TR-181. +/// - May set RFC parameters if encryption is enabled. pub fn set_device_data(device_data: &mut DeviceData) { - get_property_value_from_file(DEVICE_PROP_FILE, "BOX_TYPE", &mut device_data.box_type); - get_property_value_from_file(DEVICE_PROP_FILE, "MODEL_NUM", &mut device_data.model_num); - get_property_value_from_file(DEVICE_PROP_FILE, "DEVICE_TYPE", &mut device_data.device_type); - get_sha1_value(&mut device_data.sha1); - let _ = get_device_mac(&mut device_data.mac_addr); - get_property_value_from_file(DEVICE_PROP_FILE, "BUILD_TYPE", &mut device_data.build_type); - device_data.t2_enabled = Path::new("/lib/rdk/t2Shared_api.sh").exists(); - device_data.tls = if Path::new("/etc/os-release").exists() {"--tlsv1.2".to_string()} else {"".to_string()}; - device_data.encryption_enabled = if Path::new("/etc/encryption_enabled").exists() { - set_rfc_param(ENCRYPTION_RFC,"true"); + // Populate from device.properties + let mut box_type = String::new(); + let mut model_num = String::new(); + let mut device_type = String::new(); + let mut build_type = String::new(); + get_property_value_from_file(DEVICE_PROP_FILE, "BOX_TYPE", &mut box_type); + get_property_value_from_file(DEVICE_PROP_FILE, "MODEL_NUM", &mut model_num); + get_property_value_from_file(DEVICE_PROP_FILE, "DEVICE_TYPE", &mut device_type); + device_data.set_box_type(box_type); + device_data.set_model_num(model_num); + device_data.set_device_type(device_type); + device_data.set_build_type(build_type); + + // SHA1 from version.txt + let mut sha1 = String::new(); + get_sha1_value(&mut sha1); + device_data.set_sha1(sha1); + + // MAC address + let mut mac_addr = String::new(); + let _ = get_device_mac(&mut mac_addr); + device_data.set_mac_addr(mac_addr); + + // T2 enabled if script exists + device_data.set_t2_enabled(Path::new(T2_SHARED_SCRIPT).exists()); // TODO: Check T2 Binary instead of script? + + // TLS flag for Yocto + let tls = if Path::new("/etc/os-release").exists() { "--tlsv1.2" } else { "" }; + device_data.set_tls(tls); + + // Encryption enabled if file exists, and set RFC param + let encryption_enabled = if Path::new("/etc/encryption_enabled").exists() { + set_rfc_param(ENCRYPTION_RFC, "true"); true } else { false - } -} + }; + device_data.set_encryption_enabled(encryption_enabled); -pub fn should_exit_crash_upload(minidumps_path: &String, core_path: &String) -> bool { - let minidumps_exists = check_minidumps_exist(&minidumps_path); - let core_exists = check_core_exist(&core_path); - !(minidumps_exists || core_exists) + // Portal URL from TR-181 + let mut portal_url = String::new(); + get_rfc_param(CRASH_PORTAL_URL_RFC, &mut portal_url); + device_data.set_portal_url(portal_url); } -fn check_minidumps_exist(minidump_dir: &str) -> bool { - let path = Path::new(minidump_dir); - if let Ok(entries) = fs::read_dir(path) { - for entry in entries.flatten() { - if let Ok(file_name) = entry.file_name().into_string() { - if file_name.ends_with(".dmp") || file_name.contains(".dmp") { - return true; - } - } - } - } - false +/// Determines if the crash upload process should exit early due to no dumps being present. +/// +/// This function checks for the existence of minidump and coredump files in their respective +/// directories, mimicking the shell logic: +/// `if [ ! -e $MINIDUMPS_PATH/*.dmp* -a ! -e $CORE_PATH/*_core*.* ]; then exit 0; fi` +/// +/// # Arguments +/// * `minidumps_path` - Path to the minidumps directory. +/// * `core_path` - Path to the coredumps directory. +/// +/// # Returns +/// * `true` if neither minidumps nor coredumps exist (should exit), `false` otherwise. +pub fn should_exit_crash_upload(minidumps_path: &str, core_path: &str) -> bool { + let minidumps_exists = check_dumps_exist(&minidumps_path, ".dmp"); // for minidumps + let core_exists = check_dumps_exist(&core_path, "_core"); // for core + !(minidumps_exists || core_exists) } -fn check_core_exist(core_dir: &str) -> bool { - let path = Path::new(core_dir); - if let Ok(entries) = fs::read_dir(path) { +/// Checks if any dump files matching a pattern exist in a directory. +/// +/// This function scans the given directory for files matching the provided wildcard pattern, +/// similar to the shell logic: `[ -e $MINIDUMPS_PATH/*.dmp* ]` or `[ -e $CORE_PATH/*_core*.* ]`. +/// +/// # Arguments +/// * `dir` - Directory path to search. +/// * `wildcard` - Substring to match in file names (e.g., ".dmp" or "_core"). +/// +/// # Returns +/// * `true` if at least one matching file exists, `false` otherwise. +#[inline] +fn check_dumps_exist(dir: &str, wildcard: &str) -> bool { + let path = std::path::Path::new(dir); + if let Ok(entries) = std::fs::read_dir(path) { for entry in entries.flatten() { if let Ok(file_name) = entry.file_name().into_string() { - if file_name.contains("_core") { + // Only check files, not directories + if entry.path().is_file() && file_name.contains(wildcard) { return true; } } @@ -74,41 +116,18 @@ fn check_core_exist(core_dir: &str) -> bool { false } -pub fn set_log_file(device_data: &DeviceData, log_mod_ts: &str, line: &str) { - let file_name = line.split('/').last().unwrap_or_else(|| line); - let file_processed = file_name.contains("_mac") - || file_name.contains("_dat") - || file_name.contains("_box") - || file_name.contains("_mod"); - if file_processed { - println!("{}", file_name); - println!("Core name is already processed"); - } else { - println!( - "{}_mac{}_dat{}_box{}_mod{}_{}", - device_data.sha1, - device_data.mac_addr, - log_mod_ts, - device_data.box_type, - device_data.model_num, - file_name - ); - } -} - -fn set_secure_dump_flag(is_sec_dump_enabled: &mut bool) { - if Path::new(SECUREDUMP_ENABLE_FILE).exists() { - *is_sec_dump_enabled = true; - } else if Path::new(SECUREDUMP_DISABLE_FILE).exists() { - *is_sec_dump_enabled = false; - } else { - let mut rfc_value = String::new(); - if get_rfc_param(SECUREDUMP_TR181_NAME, &mut rfc_value) { - *is_sec_dump_enabled = rfc_value.trim().eq_ignore_ascii_case("true"); - } - } -} - +/// Determines and sets secure dump paths and flags based on SecureDump enablement. +/// +/// This function checks for SecureDump enable/disable flags and RFC, and updates the provided +/// `DumpPaths` struct accordingly. It mirrors the logic in `uploadDumps.sh` for handling +/// secure and non-secure dump locations and flags. +/// +/// # Arguments +/// * `dump_paths` - Mutable reference to a [`DumpPaths`] struct to update. +/// +/// # Side Effects +/// - Touches or removes SecureDump enable/disable flag files. +/// - Updates dump paths for secure or non-secure operation. pub fn get_secure_dump_status(dump_paths: &mut DumpPaths) { let mut is_sec_dump_enabled = false; set_secure_dump_flag(&mut is_sec_dump_enabled); @@ -119,27 +138,85 @@ pub fn get_secure_dump_status(dump_paths: &mut DumpPaths) { println!("[SECUREDUMP] Disabled"); } if Path::new(SECUREDUMP_ENABLE_FILE).exists() { - rm(SECUREDUMP_ENABLE_FILE); + let _ = fs::remove_file(SECUREDUMP_ENABLE_FILE); } - dump_paths.set_core_path("/var/lib/systemd/coredump".to_string()); - dump_paths.set_minidumps_path("/opt/minidumps".to_string()); - dump_paths.set_core_back_path("/opt/corefiles_back".to_string()); - dump_paths.set_persistent_sec_path("/opt".to_string()); + dump_paths.set_core_path("/var/lib/systemd/coredump"); + dump_paths.set_minidumps_path("/opt/minidumps"); + dump_paths.set_core_back_path("/opt/corefiles_back"); + dump_paths.set_persistent_sec_path("/opt"); } else { if !Path::new(SECUREDUMP_ENABLE_FILE).exists() { touch(SECUREDUMP_ENABLE_FILE); println!("[SECUREDUMP] Enabled. Dump location changed to /opt/secure."); } if Path::new(SECUREDUMP_DISABLE_FILE).exists() { - rm(SECUREDUMP_DISABLE_FILE); + let _ = fs::remove_file(SECUREDUMP_DISABLE_FILE); } - dump_paths.set_core_path("/opt/secure/corefiles".to_string()); - dump_paths.set_minidumps_path("/opt/secure/minidumps".to_string()); - dump_paths.set_core_back_path("/opt/secure/corefiles_back".to_string()); - dump_paths.set_persistent_sec_path("/opt/secure".to_string()); + dump_paths.set_core_path("/opt/secure/corefiles"); + dump_paths.set_minidumps_path("/opt/secure/minidumps"); + dump_paths.set_core_back_path("/opt/secure/corefiles_back"); + dump_paths.set_persistent_sec_path("/opt/secure"); } } +/// Sets the secure dump enabled flag based on file presence or RFC value. +/// +/// Checks for the presence of SecureDump enable/disable files, or queries the RFC if neither +/// file is present. Updates the provided boolean accordingly. +/// +/// # Arguments +/// * `is_sec_dump_enabled` - Mutable reference to a boolean to set. +fn set_secure_dump_flag(is_sec_dump_enabled: &mut bool) { + if Path::new(SECUREDUMP_ENABLE_FILE).exists() { + *is_sec_dump_enabled = true; + } else if Path::new(SECUREDUMP_DISABLE_FILE).exists() { + *is_sec_dump_enabled = false; + } else { + let mut rfc_value = String::new(); + if get_rfc_param(SECUREDUMP_TR181_NAME, &mut rfc_value) { + *is_sec_dump_enabled = rfc_value.trim().eq_ignore_ascii_case("true"); + } + } +} + +/// Returns the lock directory path for a given resource path. +/// +/// Appends `.lock.d` as the extension to the provided path to create a unique lock directory. +/// +/// # Arguments +/// * `path` - Path to be locked. +/// +/// # Returns +/// * `PathBuf` representing the lock directory. +#[inline] +fn lock_path>(path: P) -> PathBuf { + let mut p = path.as_ref().to_path_buf(); + p.set_extension("lock.d"); + p +} + +/// Checks if another instance is running by testing for the lock directory. +/// +/// # Arguments +/// * `path` - Path to check for lock. +/// +/// # Returns +/// * `true` if the lock directory exists, `false` otherwise. +#[inline] +fn is_another_instance_running>(path: P) -> bool { + lock_path(path).is_dir() +} + +/// Attempts to create a lock directory for the given path. Exits if already locked. +/// +/// If another instance is running (lock exists), prints a message and exits the process. +/// Otherwise, creates the lock directory and returns `true` on success. +/// +/// # Arguments +/// * `path` - Path to lock. +/// +/// # Returns +/// * `true` if lock was created, otherwise exits or returns `false`. pub fn create_lock_or_exit>(path: P) -> bool { let lock = lock_path(&path); if is_another_instance_running(&path) { @@ -159,6 +236,16 @@ pub fn create_lock_or_exit>(path: P) -> bool { } } +/// Attempts to create a lock directory for the given path, waiting if already locked. +/// +/// If another instance is running (lock exists), waits and retries every 2 seconds. +/// Returns `true` if lock is acquired, `false` if creation fails. +/// +/// # Arguments +/// * `path` - Path to lock. +/// +/// # Returns +/// * `true` if lock was created, `false` otherwise. pub fn create_lock_or_wait>(path: P) -> bool { let lock = lock_path(&path); loop { @@ -181,6 +268,10 @@ pub fn create_lock_or_wait>(path: P) -> bool { } } +/// Removes the lock directory for the given path, if it exists. +/// +/// # Arguments +/// * `path` - Path whose lock should be removed. pub fn remove_lock>(path: P) { let lock = lock_path(&path); if lock.is_dir() { @@ -190,6 +281,47 @@ pub fn remove_lock>(path: P) { } } +/// Generates a standardized log file name for a crash, or returns the original if already processed. +/// +/// If the file name already contains any of the tags `_mac`, `_dat`, `_box`, or `_mod`, +/// it is considered already processed and returned as-is. Otherwise, constructs a new +/// file name using device metadata and the original file name. +/// +/// # Arguments +/// * `device_data` - Reference to the device metadata. +/// * `log_mod_ts` - Last modified timestamp string. +/// * `line` - The original file path or name. +/// +/// # Returns +/// * A `String` with the new or original file name. +pub fn set_log_file(device_data: &DeviceData, log_mod_ts: &str, line: &str) -> String { + let file_name = line.split('/').last().unwrap_or(line); + if file_name.contains("_mac") + || file_name.contains("_dat") + || file_name.contains("_box") + || file_name.contains("_mod") + { + println!("Core name is already processed: {}", file_name); + return String::from(file_name); + } + format!( + "{}_mac{}_dat{}_box{}_mod{}_{}", + device_data.sha1, + device_data.mac_addr, + log_mod_ts, + device_data.box_type, + device_data.model_num, + file_name + ) +} + +/// Checks if the box is currently rebooting by looking for the reboot flag file. +/// +/// If the reboot flag exists, logs a message, sends a T2 notification, and returns `true`. +/// Otherwise, returns `false`. +/// +/// # Returns +/// * `true` if the box is rebooting (flag file exists), `false` otherwise. pub fn is_box_rebooting() -> bool { if Path::new(CRASH_UPLOAD_REBOOT_FLAG).exists() { println!("Skipping Upload, Since Box is Rebooting now..."); @@ -200,6 +332,16 @@ pub fn is_box_rebooting() -> bool { false } +/// Sanitizes a string by removing all characters except a safe set. +/// +/// Only allows alphanumeric characters and the following: `/ :+._,=-` +/// This matches the shell's `sed` logic for cleaning file/process names. +/// +/// # Arguments +/// * `input` - The input string to sanitize. +/// +/// # Returns +/// * A sanitized `String` containing only allowed characters. pub fn sanitize(input: &str) -> String { input .chars() @@ -221,184 +363,195 @@ pub fn sanitize(input: &str) -> String { .collect() } -pub fn is_upload_limit_reached(ts_file: &String) -> bool { - let limit_seconds = 600; - if let Err(err) = OpenOptions::new().create(true).append(true).open(ts_file) { - eprintln!("Failed to create or open {}: {}", ts_file, err); - return false; +/// Checks if the recovery time (upload denial window) has been reached or expired. +/// +/// Reads the timestamp from the deny upload file. If the file does not exist or contains +/// invalid data, returns `true` (uploads allowed). If the current time is greater than the +/// stored timestamp, returns `true` (uploads allowed). Otherwise, returns `false`. +/// +/// # Returns +/// * `true` if uploads are allowed, `false` if still in the denial window. +pub fn is_recovery_time_reached() -> bool { + let path = Path::new(DENY_UPLOAD_FILE); + if !path.exists() { + return true; } - - let mut file = match File::open(ts_file) { - Ok(f) => f, - Err(_) => return false, + let content = match fs::read_to_string(path) { + Ok(val) => val.trim().to_string(), + Err(_) => return true, + }; + let upload_denied_till = match content.parse::() { + Ok(val) => val, + Err(_) => return true, }; - let mut reader: BufReader<&File> = BufReader::new(&file); - - let line_count = reader.by_ref().lines().count(); - if line_count < 10 { - return false; - } - - let _ = file.seek(SeekFrom::Start(0)); - - let mut reader = BufReader::new(&file); - let mut first_line = String::new(); - let _ = reader.read_line(&mut first_line); - - let tenth_newest_crash_time: u64 = first_line - .split_whitespace() - .next() - .expect("No data in first line") - .parse() - .expect("Failed to parse timestamp"); let now = SystemTime::now() .duration_since(UNIX_EPOCH) - .expect("SystemTime before UNIX_EPOCH") + .unwrap_or_default() .as_secs(); - - if (now - tenth_newest_crash_time) < limit_seconds { - println!("Not uploading the dump. Too many dumps."); - return true; - } - false + now > upload_denied_till } +/// Sets the recovery time (upload denial window) to now + 10 minutes. +/// +/// Writes the future timestamp to the deny upload file. This prevents further uploads +/// until the specified time has passed. +/// +/// # Side Effects +/// - Overwrites the deny upload file with the new timestamp. pub fn set_recovery_time() { + const DONT_UPLOAD_FOR_SEC: u64 = 600; let now = SystemTime::now() .duration_since(UNIX_EPOCH) .expect("SystemTime before UNIX_EPOCH") .as_secs(); - let dont_upload_for_sec = 600; - let recovery_time = now + dont_upload_for_sec; + let recovery_time = now + DONT_UPLOAD_FOR_SEC; let _ = fs::write(DENY_UPLOAD_FILE, recovery_time.to_string()); } -pub fn is_recovery_time_reached() -> bool { - if !Path::new(DENY_UPLOAD_FILE).exists() { - return true; +/// Determines if the upload rate limit has been reached (10 uploads in 10 minutes). +/// +/// Checks the timestamp file for the last 10 upload times. If there are fewer than 10, +/// returns `false`. If the 10th newest timestamp is less than 10 minutes ago, returns `true`. +/// +/// # Arguments +/// * `ts_file` - Path to the timestamp file. +/// +/// # Returns +/// * `true` if the upload rate limit is reached, `false` otherwise. +pub fn is_upload_limit_reached(ts_file: &str) -> bool { + const LIMIT_SECONDS: u64 = 600; + let path = Path::new(ts_file); + + + if OpenOptions::new().create(true).append(true).open(path).is_err() { + println!("Failed to create or open {}", ts_file); + return false; } - let content = match fs::read_to_string(DENY_UPLOAD_FILE) { - Ok(val) => val.trim().to_string(), - Err(_) => return true, - }; - let upload_denied_till = match content.parse::() { - Ok(val) => val, - Err(_) => return true, + let file = match File::open(path) { + Ok(f) => f, + Err(_) => return false, }; + let reader = BufReader::new(&file); - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap_or_default() - .as_secs(); - - now > upload_denied_till -} - -pub fn delete_all_but_most_recent_files>( - dir_path: P, -) -> Result<(), Box> { - let path = Path::new(dir_path.as_ref()); - let mut files: Vec<(PathBuf, SystemTime)> = fs::read_dir(path)? - .filter_map(|entry| { - let entry = match entry { - Ok(entry) => entry, - Err(_) => return None, - }; - let metadata = match entry.metadata() { - Ok(metadata) => metadata, - Err(_) => return None, - }; - let modified = match metadata.modified() { - Ok(time) => time, - Err(_) => return None, - }; - Some((entry.path(), modified)) - }) + // Collect upto 10 timestamps (oldest first) + let lines: Vec = reader + .lines() + .filter_map(|line| line.ok()?.split_whitespace().next()?.parse().ok()) .collect(); - files.sort_by(|a, b| b.1.cmp(&a.1)); + if lines.len() < 10 { + return false; + } - // Calculate number of files to delete - if files.len() > MAX_CORE_FILES { - let num_files_to_delete = files.len() - MAX_CORE_FILES; + let tenth_newest_crash_time = lines[0]; + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("SystemTime before UNIX_EPOCH") + .as_secs(); - // Delete the oldest files - for (file_path, _) in files.iter().rev().take(num_files_to_delete) { - fs::remove_file(file_path)?; - println!("Deleted file: {:?}", file_path); - } - } else { - println!("No files need to be deleted. Total files: {}", files.len()); + if now.saturating_sub(tenth_newest_crash_time) < LIMIT_SECONDS { + println!("Not uploading the dump. Too many dumps."); + return true; } - Ok(()) + false } -pub fn finalize(dump_paths: &DumpPaths) { - let _ = cleanup( - &dump_paths.working_dir, - &dump_paths.dump_name, - &dump_paths.dumps_extn, - ); +/// Removes the crash loop flag and all relevant lock files for the given dump paths. +/// +/// This function is inlined for performance, as it is small and called from multiple places. +/// +/// # Arguments +/// * `dump_paths` - Reference to the dump paths struct. +#[inline] +fn remove_crash_locks_and_flag(dump_paths: &DumpPaths) { let loop_file = Path::new(CRASH_LOOP_FLAG_FILE); if loop_file.exists() { rm_rf(loop_file); } - remove_lock(&dump_paths.lock_dir_prefix); - remove_lock(&dump_paths.ts_file); + remove_lock(dump_paths.get_lock_dir_prefix()); + remove_lock(dump_paths.get_ts_file()); } -pub fn sig_term_function(dump_paths: &DumpPaths) { - println!("systemd terminating, Removing the script locks"); - let loop_file = Path::new(CRASH_LOOP_FLAG_FILE); - if loop_file.exists() { - rm_rf(loop_file); - } - remove_lock(&dump_paths.lock_dir_prefix); - remove_lock(&dump_paths.ts_file); +/// Finalizes the crash upload process by cleaning up dumps, removing locks, and clearing the crash loop flag. +/// +/// This function should be called on normal exit to ensure all resources are released and +/// any temporary files or locks are cleaned up. Uses only getters for `DumpPaths`. +/// +/// # Arguments +/// * `dump_paths` - Reference to the dump paths struct. +pub fn finalize(dump_paths: &DumpPaths) { + let _ = cleanup( + dump_paths.get_working_dir(), + dump_paths.get_dump_name(), + dump_paths.get_dumps_extn(), + ); + remove_crash_locks_and_flag(dump_paths); } -pub fn sig_kill_function(dump_paths: &DumpPaths) { - println!("systemd killing, Removing the script locks"); - let loop_file = Path::new(CRASH_LOOP_FLAG_FILE); - if loop_file.exists() { - rm_rf(loop_file); +/// Handles cleanup on receiving a crash-related signal (SIGTERM or SIGKILL). +/// +/// Logs the signal type, removes the crash loop flag, and removes all relevant locks. +/// Uses only getters for `DumpPaths`. +/// +/// # Arguments +/// * `signal` - The signal type (CrashSignal::Term or CrashSignal::Kill). +/// * `dump_paths` - Reference to the dump paths struct. +pub fn handle_crash_signal(signal: CrashSignal, dump_paths: &DumpPaths) { + match signal { + CrashSignal::Term => println!("systemd terminating, Removing the script locks"), + CrashSignal::Kill => println!("systemd killing, Removing the script locks"), } - remove_lock(&dump_paths.lock_dir_prefix); - remove_lock(&dump_paths.ts_file); + remove_crash_locks_and_flag(dump_paths); } -pub fn should_process_dump>( - dump_name: &String, - device_type: &String, - file_name: P, -) -> bool { - let f_name = file_name.as_ref(); - let status = if dump_name == "minidump" - || device_type != "prod" - || f_name.contains("Receiver") - { +/// Determines if a dump file should be processed/uploaded based on dump type, device type, and file name. +/// +/// - Always returns `true` for minidumps. +/// - For coredumps, returns `true` if not a "prod" build or if the file name does not contain "Receiver". +/// - Otherwise, logs and returns `false`. +/// +/// # Arguments +/// * `dump_name` - The dump type ("coredump" or "minidump"). +/// * `build_type` - The build type (e.g., "prod"). +/// * `file_name` - The file name to check. +/// +/// # Returns +/// * `true` if the file should be processed, `false` otherwise. +pub fn should_process_dump(dump_name: &str, build_type: &str, file_name: &str) -> bool { + if dump_name == "minidump" { + true + } else if build_type != "prod" { + true + } else if !file_name.contains("Receiver") { true } else { - println!("Not Processing dump file {}", f_name); + println!("Not processing dump file {}", file_name); false - }; - status + } } -pub fn get_dump_count(path: &String, extn: &String) -> io::Result { +/// Counts the number of dump files in a directory matching a given extension substring. +/// +/// # Arguments +/// * `dir` - Directory path to search. +/// * `pattern` - Substring to match in file names (e.g., ".dmp" or "core"). +/// +/// # Returns +/// * `Ok(count)` with the number of matching files, or an error if the directory can't be read. +pub fn get_dump_count(dir: &str, pattern: &str) -> std::io::Result { + let dir_path = Path::new(dir); + if !dir_path.exists() || !dir_path.is_dir() { + return Ok(0); + } let mut count = 0; - let dir_path = Path::new(path); - - if dir_path.exists() && dir_path.is_dir() { - for entry in fs::read_dir(path)? { - let entry = entry?; - let file_path = entry.path(); - if file_path.is_file() { - if let Some(ext) = file_path.extension() { - if ext.to_string_lossy() == extn.to_string() { - count += 1; - } + for entry in fs::read_dir(dir_path)? { + let entry = entry?; + let file_path = entry.path(); + if file_path.is_file() { + if let Some(name) = file_path.file_name().and_then(|n| n.to_str()) { + if name.contains(pattern) { + count += 1; } } } @@ -406,344 +559,235 @@ pub fn get_dump_count(path: &String, extn: &String) -> io::Result { Ok(count) } -pub fn cleanup(work_dir: &String, dump_name: &String, dump_extn: &String) -> io::Result<()> { - let work_dir_path = Path::new(work_dir); - - if !work_dir_path.exists() - || !work_dir_path.is_dir() - || work_dir_path.read_dir()?.next().is_none() - { - println!("Working directory {} is empty", work_dir); +/// Removes pending dump files (matching extension or .tgz) from the given directory. +/// +/// This function is used when the upload limit is reached, the build is blacklisted, +/// or TelemetryOptOut is set. It removes all files matching the given extension or `.tgz`. +/// +/// # Arguments +/// * `dir` - Directory path to search. +/// * `extn` - Extension to match (e.g., "dmp" or "core"). +/// +/// # Returns +/// * `Ok(())` on success, or an error if file operations fail. +pub fn remove_pending_dumps(path: &str, extn: &str) -> io::Result<()> { + let dir_path = Path::new(path); + if !dir_path.exists() || !dir_path.is_dir() { return Ok(()); } - - println!("Cleanup {} directory {}", dump_name, work_dir); - - let cut_off_time = SystemTime::now() - Duration::from_secs(60 * 60 * 24 * 2); // 2 days - for entry in fs::read_dir(work_dir_path)? { + for entry in fs::read_dir(dir_path)? { let entry = entry?; let file_path = entry.path(); if file_path.is_file() { - if let Some(file_name) = file_path.file_name() { - let file_name_str = file_name.to_string_lossy(); - - // _mac comes befire _dat in the wildcard matching - if let Some(mac_pos) = file_name_str.find("_mac") { - if let Some(dat_pos) = file_name_str.find("_dat") { - if mac_pos < dat_pos { - let metadata = fs::metadata(&file_path)?; - if let Ok(modified_time) = metadata.modified() { - if modified_time < cut_off_time { - fs::remove_file(&file_path)?; - println!("Removed file: {}", file_path.display()); - } - } - } - } - } - } - } - } - // find and while loop logic - if !Path::new(UPLOAD_ON_STARTUP).exists() { - rm_rf(format!("{}/version.txt", work_dir)); - let on_startup_dumps_cleaned_up_str = format!( - "{}_{}", - ON_STARTUP_DUMPS_CLEANED_UP_BASE, - if dump_name == "coredump" { "1" } else { "" } - ); - let on_startup_dumps_cleaned_up_path = Path::new(&on_startup_dumps_cleaned_up_str); - - if !on_startup_dumps_cleaned_up_path.exists() { - let path = Path::new(UPLOAD_ON_STARTUP); - - let mut deleted_files = Vec::new(); - for entry in fs::read_dir(path)? { - let entry = entry?; - let file_path = entry.path(); - - if file_path.is_file() { - if let Some(file_name) = file_path.file_name() { - let file_name_str = file_name.to_string_lossy(); - - if let Some(mac_pos) = file_name_str.find("_mac") { - if let Some(dat_pos) = file_name_str.find("_dat") { - if mac_pos < dat_pos { - fs::remove_file(&file_path)?; - deleted_files.push(file_path.to_string_lossy().into_owned()); - } - } - } - } - } - } - println!("Deleting unfinished files: {:?}", deleted_files); - - let mut non_dump_files = Vec::new(); - for entry in fs::read_dir(path)? { - let entry = entry?; - let file_path = entry.path(); - if file_path.is_file() { - if let Some(ext) = file_path.extension() { - if ext.to_string_lossy() != dump_extn.as_str() { - fs::remove_file(&file_path)?; - non_dump_files.push(file_path.to_string_lossy().into_owned()); - } - } - } - } - println!("Deleting non-dump files: {:?}", non_dump_files); - let _ = delete_all_but_most_recent_files(path.to_str().unwrap_or_default()); - touch(on_startup_dumps_cleaned_up_str.as_str()); - } - } else { - if dump_name == "coredump" { - rm_rf(UPLOAD_ON_STARTUP); - } - } - Ok(()) -} - -pub fn remove_pending_dumps(path: &String, extn: &String) -> io::Result<()> { - let dir_path = Path::new(path); - - if dir_path.exists() && dir_path.is_dir() { - for entry in fs::read_dir(dir_path)? { - let entry = entry?; - let file_path = entry.path(); - if file_path.is_file() { - if let Some(ext) = file_path.extension() { - let ext_str = ext.to_string_lossy(); - if ext_str == extn.as_str() || ext_str == "tgz" { - println!("Removing {} because upload limit has reached or build is blacklisted or TelemetryOptOut is set", file_path.display()); - rm_rf(path); - } - } + let file_name = file_path.file_name().and_then(|n| n.to_str()).unwrap_or(""); + if file_name.ends_with(extn) || file_name.ends_with(".tgz") { + println!("Removing {} because upload limit has reached or build is blacklisted or TelemetryOptOut is set", file_path.display()); + let _ = fs::remove_file(&file_path); } } } Ok(()) } -fn process_crash_t2_info(file_path: &String) { +/// Processes crash telemetry info for a given dump file. +/// +/// - Detects if the file is a tarball and logs accordingly. +/// - Extracts container crash info if present in the filename. +/// - Sends T2 notifications for container/app/process info. +/// - Calls `get_crashed_log_file` for further log mapping. +/// +/// # Arguments +/// * `file_path` - Path to the crash dump file (as &str). +pub fn process_crash_t2_info(file_path: &str) { println!("Processing the crash telemetry info"); let file = Path::new(file_path); - let mut file_name_str = file_path.clone(); + let mut file_name_str = file_path.to_string(); let container_delimiter = "<#=#>"; let backward_delimiter = "-"; - let mut is_tar = false; - let extension = file.extension().and_then(OsStr::to_str); - if extension == Some("tgz") { + // Check if file is a tarball + if file.extension().and_then(|e| e.to_str()) == Some("tgz") { println!("The File is already a tarball, this might be a retry or crash during shutdown"); - is_tar = true; - if let Some(pos) = file_name_str.find("_mod_") { file_name_str = file_name_str.split_off(pos + "_mod_".len()); } println!("Original Filename: {}", file.display()); - println!( - "Removing the meta information New Filename: {}", - file_name_str - ); - println!("This could be a retry or crash from previous boot the appname can be truncated"); + println!("Removing the meta information New Filename: {}", file_name_str); + println!("This could be a retry or crash from previous boot; the appname can be truncated"); t2_count_notify("SYS_INFO_TGZDUMP", Some("1")); } - let mut is_container = false; - if file_name_str.contains(container_delimiter) { - is_container = true; + // Check for container delimiter in file name + if file_name_str.contains(container_delimiter) { let parts: Vec<&str> = file_name_str.split(container_delimiter).collect(); println!("From the file name crashed process is a container"); if parts.len() >= 2 { let container_name = parts[0]; - let container_time = parts[1]; - - let (container_status, file): (String, String) = if parts.len() > 2 { - (parts[1].to_string(), container_name.to_string()) - } else { - ("unknown".to_string(), container_name.to_string()) - }; - + let container_status = if parts.len() > 2 { parts[1] } else { "unknown" }; let app_name = container_name.split('_').nth(1).unwrap_or(container_name); let process_name = container_name.split('_').next().unwrap_or(container_name); + t2_val_notify("crashedContainerName_split", container_name); - t2_val_notify("crashedContainerStatus_split", &container_status); + t2_val_notify("crashedContainerStatus_split", container_status); t2_val_notify("crashedContainerAppname_split", app_name); t2_val_notify("crashedContainerProcessName_split", process_name); t2_count_notify("SYS_INFO_CrashedContainer", Some("1")); println!("Container crash info Basic: {}, {}", app_name, process_name); - println!( - "Container crash info Advanced: {}, {}", - container_name, container_status - ); - println!( - "NEW Appname, Process_Crashed, Status = {}, {}, {}", - app_name, process_name, container_status - ); - println!( - "NEW Processname, App Name, AppState = {}, {}, {}", - process_name, app_name, container_status - ); - println!( - "ContainerName, ContainerStatus = {}, {}", - container_name, container_status - ); - println!( - "NewProcessCrash_split {}, {}", - container_name, container_status - ); + println!("Container crash info Advanced: {}, {}", container_name, container_status); + println!("NEW Appname, Process_Crashed, Status = {}, {}, {}", app_name, process_name, container_status); + println!("NEW Processname, App Name, AppState = {}, {}, {}", process_name, app_name, container_status); + println!("ContainerName, ContainerStatus = {}, {}", container_name, container_status); + println!("NewProcessCrash_split {}, {}", container_name, container_status); } } let _ = get_crashed_log_file(&file_name_str); } -/* -// ============================================ In Progress Start ============================================ -// TODO: Implement this function -pub fn mark_as_crash_loop_and_upload() { -} - -// TODO: Implement this function -pub fn process_dump(dump_paths: &DumpPaths) { - -} - -pub fn add_crashed_log_file(device_data: &DeviceData) { - let files = vec!["file1", "file2", "file3"]; // Update - - let mut line_count = 5000; - if device_data.build_type == "prod" { - line_count = 500; +/// Renames a tarball to mark it as crashlooped and (optionally) uploads it to the crash portal. +/// +/// # Arguments +/// * `tgz_file` - Path to the tarball file. +/// * `portal_url` - Crash portal URL. +/// * `crash_portal_path` - Crash portal path. +/// +/// # Side Effects +/// - Renames the file to `.crashloop.dmp.tgz`. +pub fn mark_as_crash_loop_and_upload(tgz_file: &str) { // portal_url: &str, crash_portal_path: &str) { + let tgz_path = Path::new(tgz_file); + let new_tgz_name = tgz_path.with_extension("crashloop.dmp.tgz"); + println!("Renaming {} to {}", tgz_path.display(), new_tgz_name.display()); + if let Err(e) = fs::rename(tgz_path, &new_tgz_name) { + println!("Failed to rename crashloop tarball: {}", e); + return; } - - + // TODO: Not used yet Implement upload logic using portal_url and crash_portal_path if needed. } -pub fn copy_log_files_to_tmp(tmp_dir: &String) { - let tmp_directory = format!("/tmp{}",tmp_dir).as_str(); - let res = 0; - let limit = 70; - let mut usage_percent = 0; - - let tmp_path = Path::new("/tmp"); +/// Finds the oldest file in a directory. +/// +/// # Arguments +/// * `dir` - Directory path to search. +/// +/// # Returns +/// * `Ok(Some(path))` with the oldest file, or `Ok(None)` if no files found. +pub fn find_oldest_dump(dir: &str) -> io::Result> { + let mut files: Vec<_> = fs::read_dir(dir)? + .filter_map(|entry| entry.ok()) + .map(|entry| entry.path()) + .filter(|path| path.is_file()) + .collect(); - if tmp_path.is_dir() { - usage_percent = get_usage_percent(tmp_path); - } - else{ - println!("{} is not a directory", tmp_path.display()); - } + files.sort_by_key(|path| fs::metadata(path).and_then(|m| m.modified()).ok()); + Ok(files.into_iter().next()) +} - if usage_percent > limit { - println!("Skipping copying Logs to tmp dir due to limited Memory"); - // TODO +/// Saves a dump file, renaming if needed, and ensures only the most recent 5 minidumps are kept. +/// +/// # Arguments +/// * `minidumps_path` - Directory containing minidumps. +/// * `s3_filename` - The current filename. +/// * `new_name` - Optional new name to rename to (to retain container info). +pub fn save_dump(minidumps_path: &str, s3_filename: &str, new_name: Option<&str>) { + if let Some(new_name) = new_name { + println!("Saving dump with original name to retain container info"); + let original_path = format!("{}/{}", minidumps_path, s3_filename); + let new_path = format!("{}/{}", minidumps_path, new_name); + if let Err(e) = fs::rename(&original_path, &new_path) { + println!("Failed to rename file: {}", e); + } } - else { - println!("Copying logs to tmp dir as Memory available. used size = {}% limit = {}%", usage_percent, limit); - /* mkdir $TmpDirectory 2> /dev/null - cp $Logfiles $TmpDirectory 2> /dev/null - */ - println!("Logs copied to {} Temporary", tmp_directory); - // Loop + let dump_extn = "dmp.tgz"; + let mut count = get_dump_count(minidumps_path, dump_extn).unwrap_or(0); + while count > 5 { + match find_oldest_dump(minidumps_path) { + Ok(Some(oldest)) => { + println!("Removing old dump {}", oldest.display()); + if let Err(e) = fs::remove_file(&oldest) { + println!("Failed to remove old dump: {}", e); + } + count -= 1; + } + _ => break, + } } - + println!("Total pending Minidumps: {}", count); } -// pub fn save_dump(dump_paths: &DumpPaths, new_name: Option<&str>) { -// if let Some(new_name) = new_name { -// let original_path = Path::new(&dump_paths.minidumps_path).join(&dump_paths.tar_extn); -// let new_path = Path::new(&dump_paths.minidumps_path).join(new_name); -// } - -// let dump_files: Vec = fs::read_dir(&dump_paths.minidumps_path)? -// .filter_map(|entry| { -// let entry = entry.ok()?; -// let file_type = entry.file_type().ok()?; -// if file_type.is_file() && entry.path().extension() == Some("tgz".as_ref()) { -// Some(entry.path()) -// } else { -// None -// } -// }) -// .collect(); -// let mut count = dump_files.len(); -// while count > 5 { -// if let Some(oldest_dump) = dump_files.iter() -// .min_by_key(|path| fs::metadata(path).and_then(|m| m.modified()).unwrap_or(SystemTime::UNIX_EPOCH)) { - -// println!("Removing old dump {:?}", oldest_dump); -// fs::remove_file(oldest_dump)?; - -// count -= 1; // Decrement count -// } -// } -// println!("Total pending Minidumps: {}", count); -// } - -*/ -// =========================================== In Progress End ============================================ -pub fn log_upload_timestamp(ts_file: &String) { - let mut dev_type = String::new(); - get_property_value_from_file("/etc/device.properties", "BUILD_TYPE", &mut dev_type); - if dev_type == "prod" { +/// Logs the current upload timestamp to the timestamp file and truncates it to the last 10 entries. +/// +/// # Arguments +/// * `ts_file` - Path to the timestamp file. +pub fn log_upload_timestamp(ts_file: &str) { + let mut build_type = String::new(); + get_property_value_from_file("/etc/device.properties", "BUILD_TYPE", &mut build_type); + if build_type == "prod" { let now = SystemTime::now() .duration_since(UNIX_EPOCH) .expect("SystemTime before UNIX_EPOCH") .as_secs(); - - let _ = fs::write(ts_file, now.to_string()); - let _ = truncate_timestamp_file(ts_file); + if let Err(e) = OpenOptions::new().append(true).create(true).open(ts_file) + .and_then(|mut f| writeln!(f, "{}", now)) { + println!("Failed to write timestamp: {}", e); + } + if let Err(e) = truncate_timestamp_file(ts_file) { + println!("Failed to truncate timestamp file: {}", e); + } } } -fn truncate_timestamp_file(ts_file: &String) -> io::Result<()> { +/// Truncates the timestamp file to the last 10 lines. +/// +/// # Arguments +/// * `ts_file` - Path to the timestamp file. +/// +/// # Returns +/// * `Ok(())` on success, or an error if file operations fail. +pub fn truncate_timestamp_file(ts_file: &str) -> io::Result<()> { let ts_path = Path::new(ts_file); + let file = File::open(ts_path)?; + let reader = BufReader::new(file); - let _ = OpenOptions::new() - .create(true) - .write(true) - .read(true) - .open(ts_path); - let file = fs::File::open(ts_path)?; - let reader = io::BufReader::new(file); - - let lines: Vec = reader - .lines() - .map(|line| line.unwrap_or_default()) - .collect(); - let last_10_lines: Vec = lines.iter().rev().take(10).cloned().collect(); - let mut tmp_file = OpenOptions::new() - .write(true) - .truncate(true) - .open(ts_path)?; + let lines: Vec = reader.lines().filter_map(Result::ok).collect(); + let last_10_lines = lines.iter().rev().take(10).cloned().collect::>(); + let mut tmp_file = OpenOptions::new().write(true).truncate(true).open(ts_path)?; - for line in last_10_lines { + for line in last_10_lines.into_iter().rev() { writeln!(tmp_file, "{}", line)?; } Ok(()) } -pub fn get_last_modified_time_of_file>(path: P) -> Option { - let path_ref = Path::new(path.as_ref()); - +/// Gets the last modified time of a file as a formatted string. +/// +/// # Arguments +/// * `path` - Path to the file. +/// +/// # Returns +/// * `Some(String)` with the formatted time, or `None` if not available. +pub fn get_last_modified_time_of_file(path: &str) -> Option { + let path_ref = Path::new(path); if !path_ref.is_file() { return None; } let metadata = fs::metadata(path_ref).ok()?; - let modified_time: SystemTime = metadata.modified().ok()?; - // NOTE: this can be done with std::process::Command as well + let modified_time = metadata.modified().ok()?; let datetime: DateTime = modified_time.into(); - Some(datetime.format("%Y-%m-%d-%H-%M-%S").to_string()) } -fn get_crashed_log_file>(file_path: P) -> io::Result<()> { - let file = file_path.as_ref(); +/// Maps a crashed process to its log files using the logmapper config and writes them to LOG_FILES. +/// +/// # Arguments +/// * `file_path` - Path or name of the crashed file. +/// +/// # Returns +/// * `Ok(())` on success, or an error if file operations fail. +pub fn get_crashed_log_file(file_path: &str) -> io::Result<()> { + let file = file_path; let process_name = file .rsplitn(2, '_') @@ -789,3 +833,706 @@ fn get_crashed_log_file>(file_path: P) -> io::Result<()> { Ok(()) } + +/// Deletes all but the most recent N files in a directory, sorted by modification time (descending). +/// +/// This function is used to enforce a retention policy for core/minidump files, +/// keeping only the newest `MAX_CORE_FILES` files and deleting the rest. +/// +/// # Arguments +/// * `dir_path` - Directory path as &str. +/// +/// # Returns +/// * `Ok(())` on success, or an error if file operations fail. +pub fn delete_all_but_most_recent_files(dir_path: &str) -> io::Result<()> { + let path = Path::new(dir_path); + if !path.is_dir() { + return Ok(()); + } + + // Collect all files with their modification times + let mut files: Vec<_> = fs::read_dir(path)? + .filter_map(|entry| { + let entry = entry.ok()?; + let meta = entry.metadata().ok()?; + let mtime = meta.modified().ok()?; + if meta.is_file() { + Some((entry.path(), mtime)) + } else { + None + } + }) + .collect(); + + files.sort_by(|a, b| b.1.cmp(&a.1)); + + // Calculate number of files to delete + if files.len() > MAX_CORE_FILES { + // Delete the oldest files + for (file_path, _) in files.iter().skip(MAX_CORE_FILES) { + if let Err(e) = fs::remove_file(file_path) { + println!("Failed to delete file {:?}: {}", file_path, e); + } else { + println!("Deleted old dump file: {:?}", file_path); + } + } + } else { + println!("No files need to be deleted. Total files: {}", files.len()); + } + Ok(()) +} + +/// /// Cleans up the working directory by removing old, unfinished, and non-dump files, +/// and limits the number of dump files to the configured maximum. +/// +/// - Removes files matching `*_mac*_dat*` older than 2 days. +/// - On first startup, removes unfinished and non-dump files, and limits dump file count. +/// - Removes version.txt if not uploading on startup. +/// - Uses only `&str` for arguments for efficiency. +/// +/// # Arguments +/// * `work_dir` - Working directory path. +/// * `dump_name` - Dump type ("coredump" or "minidump"). +/// * `dump_extn` - Dump file extension pattern (e.g., "*.dmp*"). +/// +/// # Returns +/// * `Ok(())` on success, or an error if file operations fail. +pub fn cleanup(work_dir: &str, dump_name: &str, dump_extn: &str) -> std::io::Result<()> { + let work_dir_path = Path::new(work_dir); + + // Early exit if directory is missing or empty + if !work_dir_path.exists() || !work_dir_path.is_dir() || work_dir_path.read_dir()?.next().is_none() + { + println!("Working directory {} is empty", work_dir); + return Ok(()); + } + + println!("Cleanup {} directory {}", dump_name, work_dir); + + // Remove files matching '*_mac*_dat*' older than 2 days + let cutoff = SystemTime::now() - Duration::from_secs(60 * 60 * 24 * 2); // 2 days + for entry in fs::read_dir(work_dir_path)? { + let entry = entry?; + let path = entry.path(); + if path.is_file() { + let fname = path.file_name().and_then(|n| n.to_str()).unwrap_or(""); + if fname.contains("_mac") && fname.contains("_dat") { + if let Ok(meta) = fs::metadata(&path) { + if let Ok(modified) = meta.modified() { + if modified < cutoff { + fs::remove_file(&path)?; + println!("Removed file: {}", path.display()); + } + } + } + } + } + } + + // find and while loop logic + if !Path::new(UPLOAD_ON_STARTUP).exists() { + let version_txt = Path::new(work_dir).join("version.txt"); + if version_txt.exists() { + let _ = fs::remove_file(&version_txt); + } + + let on_startup_flag = format!( + "{}_{}", + ON_STARTUP_DUMPS_CLEANED_UP_BASE, + if dump_name == "coredump" { "1" } else { "" } + ); + let on_startup_flag_path = Path::new(&on_startup_flag); + + if !on_startup_flag_path.exists() { + // Remove unfinished files: '*_mac*_dat*' + for entry in fs::read_dir(work_dir_path)? { + let entry = entry?; + let path = entry.path(); + let fname = path.file_name().and_then(|n| n.to_str()).unwrap_or(""); + if fname.contains("_mac") && fname.contains("_dat") { + fs::remove_file(&path)?; + println!("Deleting unfinished file: {}", path.display()); + } + } + + // Remove non-dump files (not matching dump_extn) + for entry in fs::read_dir(work_dir_path)? { + let entry = entry?; + let path = entry.path(); + if path.is_file() { + let fname = path.file_name().and_then(|n| n.to_str()).unwrap_or(""); + if !fname.contains(dump_extn.trim_matches('*')) { + fs::remove_file(&path)?; + println!("Deleting non-dump file: {}", path.display()); + } + } + } + + // Limit number of dump files + let _ = delete_all_but_most_recent_files(work_dir); + + touch(&on_startup_flag); + } + } else if dump_name == "coredump" { + let _ = fs::remove_file(UPLOAD_ON_STARTUP); + } + Ok(()) +} + +/// Returns the usage percent of the /tmp partition by invoking `df`. +/// +/// Mimics the shell logic: `df -h /tmp | grep '\tmp' | awk '{print $5}'` +/// +/// # Returns +/// * `Some(u8)` with the usage percent, or `None` if it cannot be determined. +#[inline] +fn get_tmp_usage_percent() -> Option { + let output = Command::new("df") + .arg("-h") + .arg("/tmp") + .output() + .ok()?; + + let stdout = String::from_utf8_lossy(&output.stdout); + + // Skip the header, look for the line containing "/tmp" + for line in stdout.lines().skip(1) { + if line.contains("/tmp") { + let fields: Vec<&str> = line.split_whitespace().collect(); + if let Some(usage) = fields.get(4) { + // usage is like "12%" + return usage.trim_end_matches('%').parse::().ok(); + } + } + } + None +} + +/// Adds crashed log files to the minidump tarball, processing each log file as needed. +/// +/// For each file in `log_files`, if it exists, extracts the last N lines (N=500 for prod, 5000 otherwise), +/// writes them to a sanitized process log file, and logs the action. +/// After processing, removes the original log files. +/// This function is reusable and accepts any slice of log file paths. +/// +/// # Arguments +/// * `device_data` - Reference to device metadata (for log file naming). +/// * `log_files` - Slice of log file paths (`&[&str]`). +/// +/// # Returns +/// * `Ok(())` on success, or an error if file operations fail. +pub fn add_crashed_log_file(device_data: &DeviceData, log_files: &[&str]) -> io::Result<()> { + let line_count = if device_data.build_type == "prod" { 500 } else { 5000 }; + + for file_path in log_files { + let path = Path::new(file_path); + if path.is_file() { + if let Some(log_mod_ts) = get_last_modified_time_of_file(file_path) { + let process_log = set_log_file(device_data, &log_mod_ts, file_path); + + // Use std::fs and BufReader for minimal memory usage + let file = File::open(path)?; + let lines: Vec = BufReader::new(file) + .lines() + .filter_map(Result::ok) + .collect(); + + // Take the last N lines + let start = lines.len().saturating_sub(line_count); + let mut output = File::create(&process_log)?; + for line in &lines[start..] { + writeln!(output, "{}", line)?; + } + + println!("Adding File: {} to minidump tarball", process_log); + } + } + } + + // Remove the original log files after processing + for &file_path in log_files { + let path = Path::new(file_path); + if path.exists() { + let _ = fs::remove_file(path); + } + } + Ok(()) +} + + +/// Copies log files to a temporary directory under /tmp if there is enough free space. +/// +/// If /tmp usage is below the threshold (70%), copies each log file to `/tmp/`. +/// Otherwise, returns the original file paths. Returns the paths of the files to use for archiving. +/// +/// # Arguments +/// * `tmp_dir_name` - Name for the temporary directory under /tmp. +/// * `logfiles` - Slice of log file paths (`&[&str]`). +/// +/// # Returns +/// * `Vec`: Paths to use for archiving (either in /tmp or original). +pub fn copy_log_files_to_tmp(tmp_dir_name: &str, logfiles: &[&str]) -> Vec { + let tmp_dir = format!("/tmp/{}", tmp_dir_name); + let usage_percent = get_tmp_usage_percent().unwrap_or(0); + let limit = 70; + let mut out_files = Vec::new(); + + if usage_percent >= limit { + println!( + "Skipping copying logs to tmp dir due to limited memory (used: {}%, limit: {}%)", + usage_percent, limit + ); + for &file in logfiles { + if Path::new(file).exists() { + out_files.push(file.to_string()); + } + } + } else { + println!( + "Copying logs to tmp dir as memory available (used: {}%, limit: {}%)", + usage_percent, limit + ); + if let Err(e) = fs::create_dir_all(&tmp_dir) { + println!("Failed to create tmp dir {}: {}", tmp_dir, e); + } + for &file in logfiles { + let src = Path::new(file); + if src.exists() { + let dest = Path::new(&tmp_dir).join(src.file_name().unwrap()); + if let Err(e) = fs::copy(src, &dest) { + println!("Failed to copy {:?} to {:?}: {}", src, dest, e); + } else { + out_files.push(dest.to_string_lossy().to_string()); + } + } + } + println!("Logs copied to {} temporary", tmp_dir); + } + out_files +} + +/// Calls the uploadDumpsToS3.sh script with the given arguments if it exists. +/// +/// # Arguments +/// * `args` - Arguments to pass to the script. +/// +/// # Returns +/// * `Ok(exit_status)` if the script ran, or an error if not found or failed. +pub fn upload_to_s3(args: &[&str]) -> std::io::Result { + let script = "/lib/rdk/uploadDumpsToS3.sh"; + if Path::new(script).exists() { + Command::new(script).args(args).status() + } else { + Err(std::io::Error::new( + std::io::ErrorKind::NotFound, + "uploadDumpsToS3.sh not found", + )) + } +} + +/// Calls getPrivacyControlMode from utils.sh and returns its output as a String. +/// +/// # Returns +/// * `Some(String)` if successful, `None` otherwise. +pub fn get_privacy_control_mode() -> Option { + let script = "/lib/rdk/utils.sh"; + if Path::new(script).exists() { + let output = Command::new("sh") + .arg("-c") + .arg(format!(". {}; getPrivacyControlMode", script)) + .output() + .ok()?; + if output.status.success() { + Some(String::from_utf8_lossy(&output.stdout).trim().to_string()) + } else { + None + } + } else { + None + } +} + +/// Processes all dump files in the working directory: sanitizes, compresses, and uploads or saves them. +/// +/// This function mirrors the main dump processing logic from `uploadDumps.sh`, including: +/// - Sanitizing and renaming dump files +/// - Processing telemetry info for minidumps +/// - Skipping files that are already tarballs +/// - Using `crash_ts` for log/tarball naming +/// - Compressing dumps and associated log files into tarballs +/// - Handling fallback if compression fails +/// - Cleaning up temporary files and logs +/// - Delegating tarball upload or save logic to `handle_tarballs` +/// +/// # Arguments +/// * `device_data` - Reference to device metadata (for naming and telemetry). +/// * `dump_paths` - Reference to dump paths and configuration. +/// * `crash_ts` - Crash timestamp string for naming files (faithful to shell script). +/// * `no_network` - If true, skips upload and just saves the dump locally. +/// +/// # Side Effects +/// - Modifies files in the working directory (renames, compresses, deletes). +/// - May create or remove log files and temporary directories. +/// - Calls `handle_tarballs` for tarball upload/save logic. +pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: &str, no_network: bool) { + utils::flush_logger(); + let files = match find_dump_files(dump_paths.get_working_dir(), dump_paths.get_dumps_extn()) { + Ok(f) => f, + Err(e) => { + println!("Error finding dump files: {}", e); + return; + } + }; + + for file in files { + let sanitized = match sanitize_and_rename(&file) { + Ok(f) => f, + Err(e) => { + println!("Sanitize/rename failed for {:?}: {}", file, e); + continue; + } + }; + + if dump_paths.dump_name != "coredump" { + process_crash_t2_info(&sanitized.to_string_lossy()); + } + + if is_tarball(&sanitized) { + println!("Skip archiving {:?} as it is a tarball already.", sanitized); + continue; + } + + // Use crash_ts for naming + let mut dump_file_name = set_log_file(device_data, crash_ts, &sanitized.to_string_lossy()); + if dump_file_name.len() >= 135 { + if let Some(pos) = dump_file_name.find('_') { + dump_file_name = dump_file_name[pos + 1..].to_string(); + } + } + + let tgz_file = if dump_paths.dump_name == "coredump" { + format!("{}.core.tgz", dump_file_name) + } else { + format!("{}.tgz", dump_file_name) + }; + + let dump_file_name = dump_file_name.replace("<#=#>", "_"); + + if let Err(e) = fs::rename(&sanitized, &dump_file_name) { + println!("Failed to rename {:?} to {}: {}", sanitized, dump_file_name, e); + continue; + } + + let version_file_path = Path::new(dump_paths.get_working_dir()).join("version.txt"); + if !version_file_path.exists() { + let _ = fs::copy(VERSION_FILE, &version_file_path); + } + + let logfiles: Vec = if dump_paths.dump_name == "coredump" { + vec![VERSION_FILE.to_string(), CORE_LOG.to_string()] + } else { + let crashed_url_file = format!("{}/crashed_url.txt", LOG_PATH); + vec![VERSION_FILE.to_string(), CORE_LOG.to_string(), crashed_url_file] + }; + let logfiles_refs: Vec<&str> = logfiles.iter().map(|s| s.as_str()).collect(); + + let tar_result = compress_files(&tgz_file, &[&dump_file_name], &logfiles_refs); + + if tar_result.is_err() { + let out_files = copy_log_files_to_tmp(&dump_file_name, &logfiles_refs); + let out_files_refs: Vec<&str> = out_files.iter().map(|s| s.as_str()).collect(); + let _ = compress_files(&tgz_file, &[&dump_file_name], &out_files_refs); + } + + let tmp_dir = format!("/tmp/{}", dump_file_name); + if Path::new(&tmp_dir).is_dir() { + rm_rf(&tmp_dir); + println!("Temporary Directory Deleted: {}", tmp_dir); + } + rm_rf(&dump_file_name); + + if dump_paths.dump_name != "coredump" { + let _ = remove_logs(dump_paths.get_working_dir()); + } + } + + handle_tarballs(device_data, dump_paths, no_network, crash_ts); +} + +/// Compresses the given files into a tarball using the `tar` command. +/// +/// # Arguments +/// * `tgz_file` - Output tarball file name. +/// * `main_files` - Main files to include. +/// * `extra_files` - Additional files to include. +/// +/// # Returns +/// * `Ok(())` if compression succeeded, error otherwise. +#[inline] +fn compress_files(tgz_file: &str, main_files: &[&str], extra_files: &[&str]) -> io::Result<()> { + let mut args = vec!["-zcvf", tgz_file]; + args.extend(main_files.iter().copied()); + args.extend(extra_files.iter().copied()); + let status = Command::new("tar").args(&args).status()?; + if status.success() { + Ok(()) + } else { + Err(io::Error::new(io::ErrorKind::Other, "Compression Failed")) + } +} + +/// Checks if the given file is a tarball (ends with .tgz). +#[inline] +fn is_tarball(file: &Path) -> bool { + file.extension().map(|e| e == "tgz").unwrap_or(false) +} + +/// Sanitizes the file name and renames the file if needed. +/// +/// # Arguments +/// * `file` - Path to the file. +/// +/// # Returns +/// * `Ok(new_path)` with the sanitized path, or error. +#[inline] +fn sanitize_and_rename(file: &Path) -> io::Result { + let orig = file.to_string_lossy(); + let sanitized = sanitize(&orig); + if sanitized != orig { + fs::rename(&*orig, &sanitized)?; + Ok(PathBuf::from(sanitized)) + } else { + Ok(file.to_path_buf()) + } +} + +/// Removes all .log and .txt files from the given directory. +/// +/// # Arguments +/// * `working_dir` - Directory to clean. +/// +/// # Returns +/// * `Ok(())` on success, or error. +#[inline] +fn remove_logs(working_dir: &str) -> io::Result<()> { + for entry in fs::read_dir(working_dir)? { + let entry = entry?; + let path = entry.path(); + if path.is_file() { + let name = path.file_name().unwrap_or_default().to_string_lossy(); + if name.ends_with(".log") || name.ends_with(".txt") { + rm_rf(path.to_str().unwrap()); + } + } + } + Ok(()) +} + +/// Finds all files in a directory matching the given extension substring. +/// +/// # Arguments +/// * `working_dir` - Directory to search. +/// * `dumps_extn` - Substring to match in file names. +/// +/// # Returns +/// * `Ok(Vec)` with matching files, or error. +#[inline] +pub fn find_dump_files(working_dir: &str, dumps_extn: &str) -> io::Result> { + let mut files = Vec::new(); + for entry in fs::read_dir(working_dir)? { + let entry = entry?; + let path = entry.path(); + if path.is_file() && path.file_name().map(|n| n.to_string_lossy().contains(dumps_extn)).unwrap_or(false) { + files.push(path); + } + } + Ok(files) +} + +/// Handles all tarballs in the working directory: applies rate limiting, privacy checks, +/// uploads with retries, and performs post-upload cleanup. +/// +/// # Arguments +/// * `device_data` - Reference to device metadata. +/// * `dump_paths` - Reference to dump paths and config. +fn handle_tarballs(device_data: &DeviceData, dump_paths: &DumpPaths, no_network: bool, crash_ts: &str) { + let tarballs = match find_dump_files(dump_paths.get_working_dir(), dump_paths.get_tar_extn()) { + Ok(t) => t, + Err(e) => { + println!("Error finding tarballs: {}", e); + return; + } + }; + + for tarball in tarballs { + if let Err(e) = handle_single_tarball(device_data, dump_paths, &tarball, no_network, crash_ts) { + println!("Error handling tarball {:?}: {}", tarball, e); + } + } +} + +/// Handles a single tarball: checks rate limits, privacy, uploads with retries, and cleans up. +/// +/// # Arguments +/// * `device_data` - Reference to device metadata. +/// * `dump_paths` - Reference to dump paths and config. +/// * `tarball` - Path to the tarball file. +/// +/// # Returns +/// * `Ok(())` on success, or error. +fn handle_single_tarball(device_data: &DeviceData, dump_paths: &DumpPaths, tarball: &Path, no_network: bool, crash_ts: &str) -> std::io::Result<()> { + let tarball_str = tarball.to_string_lossy(); + let s3_filename = tarball.file_name().and_then(|n| n.to_str()).unwrap_or(""); + let s3_filename_sanitized = s3_filename.replace("<#=#>", "_"); + + // 1. Rate limiting and recovery time + if !is_recovery_time_reached() { + println!("Shifting the recovery time forward."); + set_recovery_time(); + let _ = remove_pending_dumps( + dump_paths.get_working_dir(), + dump_paths.get_dumps_extn(), + ); + return Ok(()); + } + + if dump_paths.get_dump_name() == "minidump" + && is_upload_limit_reached(dump_paths.get_ts_file()) + { + println!("Upload rate limit has been reached."); + mark_as_crash_loop_and_upload(&tarball_str); + set_recovery_time(); + let _ = remove_pending_dumps( + dump_paths.get_working_dir(), + dump_paths.get_dumps_extn(), + ); + return Ok(()); + } + + // 2. Privacy mode check + if device_data.get_device_type() == "mediaclient" && is_privacy_mode_do_not_share() { + println!("Privacy Mode is DO_NOT_SHARE. Stop Uploading the data to the cloud"); + let _ = remove_pending_dumps( + dump_paths.get_working_dir(), + dump_paths.get_dumps_extn(), + ); + return Ok(()); + } + + // 3. Ensure tarball filename is sanitized for S3 + if s3_filename != s3_filename_sanitized { + rename_tarball_for_s3(tarball, &s3_filename_sanitized)?; + } + + // 4. no_network logic: skip upload and just save the dump + if dump_paths.get_dump_name() == "minidump" && no_network { + println!("Network is not available, skipping upload and saving dump."); + save_dump(dump_paths.get_minidumps_path(), s3_filename, Some(crash_ts)); + return Ok(()); + } + + // 5. Upload with retries + let upload_success = upload_tarball_with_retries(&s3_filename_sanitized, dump_paths); + + // 6. Post-upload cleanup + post_upload_cleanup( + upload_success, + dump_paths, + tarball, + &s3_filename_sanitized, + ); + + Ok(()) +} + +/// Returns true if privacy mode is DO_NOT_SHARE (from utils.sh). +#[inline] +fn is_privacy_mode_do_not_share() -> bool { + matches!(get_privacy_control_mode().as_deref(), Some("DO_NOT_SHARE")) +} + +/// Renames a tarball file to a sanitized name for S3 upload. +/// +/// # Arguments +/// * `tarball` - Path to the original tarball. +/// * `sanitized_name` - Sanitized file name. +/// +/// # Returns +/// * `Ok(())` on success, or error. +fn rename_tarball_for_s3(tarball: &Path, sanitized_name: &str) -> std::io::Result<()> { + let parent = tarball.parent().unwrap_or_else(|| Path::new("")); + let orig_path = parent.join(tarball.file_name().unwrap()); + let new_path = parent.join(sanitized_name); + fs::rename(&orig_path, &new_path) +} + +/// Uploads a tarball to S3, retrying up to 3 times. +/// +/// # Arguments +/// * `s3_filename` - Name of the tarball file to upload. +/// * `dump_paths` - Reference to dump paths and config. +/// +/// # Returns +/// * `true` if upload succeeded, `false` otherwise. +fn upload_tarball_with_retries(s3_filename: &str, dump_paths: &DumpPaths) -> bool { + let mut upload_status = false; + for attempt in 1..=3 { + println!( + "[{}]: {}: {} S3 Upload Attempt {}", + file!(), + attempt, + dump_paths.get_dump_name(), + s3_filename + ); + match upload_to_s3(&[s3_filename]) { + Ok(exit_status) if exit_status.success() => { + println!( + "{} uploadToS3 SUCCESS: status: {:?}", + dump_paths.get_dump_name(), + exit_status + ); + upload_status = true; + break; + } + Ok(exit_status) => { + println!( + "Execution Status: {:?}, S3 Amazon Upload of {} Failed", + exit_status, + dump_paths.get_dump_name() + ); + } + Err(e) => { + println!("Upload to S3 failed: {}", e); + } + } + std::thread::sleep(std::time::Duration::from_secs(2)); + } + upload_status +} + +/// Cleans up after upload: removes tarball if successful, logs timestamp, or saves/removes on failure. +/// +/// # Arguments +/// * `upload_success` - Whether the upload succeeded. +/// * `dump_paths` - Reference to dump paths and config. +/// * `tarball` - Path to the tarball file. +/// * `s3_filename` - Name of the tarball file. +fn post_upload_cleanup(upload_success: bool, dump_paths: &DumpPaths, tarball: &Path, s3_filename: &str) { + let parent = tarball.parent().unwrap_or_else(|| Path::new("")); + let s3_path = parent.join(s3_filename); + + if upload_success { + println!("Removing file {}", s3_filename); + let _ = fs::remove_file(&s3_path); + log_upload_timestamp(dump_paths.get_ts_file()); + } else { + println!("S3 Amazon Upload of {} Failed..!", dump_paths.get_dump_name()); + if dump_paths.get_dump_name() == "minidump" { + println!("Check and save the dump {}", s3_filename); + save_dump(dump_paths.get_minidumps_path(), s3_filename, None); + } else { + println!("Removing file {}", s3_filename); + let _ = fs::remove_file(&s3_path); + } + } +} diff --git a/crash_upload/src/main.rs b/crash_upload/src/main.rs index f00df26..5ee0a69 100644 --- a/crash_upload/src/main.rs +++ b/crash_upload/src/main.rs @@ -1,7 +1,8 @@ use std::process::exit; // standard library imports use std::path::Path; -use std::{env, fs, process}; +use std::time::Duration; +use std::{env, fs, process, thread}; // external crate imports use chrono::Local; @@ -10,16 +11,18 @@ use chrono::Local; mod constants; mod crashupload_utils; -// crashupload_utils library module imports +// Utility crates use crashupload_utils::*; -use platform_interface::*; -use utils::*; fn main() { println!("Starting Crash Upload Binary..."); + + // TODO: Signal handling (trap/finalize on SIGTERM/SIGKILL/EXIT) + + // Parse command-line arguments let args: Vec = env::args().collect(); if args.len() != 4 { - eprintln!("Usage: {} ", args[0]); + println!("Usage: {} ", args[0]); process::exit(1); } @@ -27,62 +30,68 @@ fn main() { let upload_flag = &args[2]; let wait_for_lock = &args[3]; - println!("Instantiating DumpPaths and DeviceData structs..."); + // Instantiate configuration structs + println!("Instantiating DumpPaths and DeviceData instances..."); let mut device_data = constants::DeviceData::new(); let mut dump_paths = constants::DumpPaths::new(); + // Populate device data from system properties crashupload_utils::set_device_data(&mut device_data); - //let crash_timestamp = utils::get_crash_timestamp(); - // EXEC: Set the core and minidump paths based on the upload flag + // Set dump paths based on upload flag if upload_flag == "secure" { - dump_paths.set_core_path("/opt/secure/corefiles".to_string()); - dump_paths.set_minidumps_path("/opt/secure/minidumps".to_string()); + dump_paths.set_core_path("/opt/secure/corefiles"); + dump_paths.set_minidumps_path("/opt/secure/minidumps"); } else { - dump_paths.set_core_path("/var/lib/systemd/coredump".to_string()); - dump_paths.set_minidumps_path("/opt/minidumps".to_string()); + dump_paths.set_core_path("/var/lib/systemd/coredump"); + dump_paths.set_minidumps_path("/opt/minidumps"); } - if crashupload_utils::should_exit_crash_upload(&dump_paths.minidumps_path, &dump_paths.core_path) { + // Exit early if no dumps exist + if crashupload_utils::should_exit_crash_upload(dump_paths.get_minidumps_path(), dump_paths.get_core_path()) { println!("Crash upload process is already running. Exiting..."); - std::process::exit(0); + exit(0); } - // EXEC: Secure Dump Status & path Set - let _ = crashupload_utils::get_secure_dump_status(&mut dump_paths); + // Set secure dump status if needed + crashupload_utils::get_secure_dump_status(&mut dump_paths); - - // ============================== - - // let timestamp_filename = crashupload_utils::get_timestamp_filename(dump_name); + // Generate crash timestamp let crash_ts = Local::now().format("%Y-%m-%d-%H-%M-%S").to_string(); + + // Configure dump paths and metadata based on dump_flag if dump_flag == 1 { println!("starting core dump processing..."); - dump_paths.dump_name = "coredump".to_string(); - dump_paths.working_dir = dump_paths.get_core_path().to_string(); - dump_paths.dumps_extn = "*core.prog*.gz*".to_string(); - dump_paths.tar_extn = ".core.tgz".to_string(); - dump_paths.lock_dir_prefix = "/tmp/.uploadCoredumps".to_string(); - dump_paths.crash_portal_path = "/opt/crashportal_uploads/coredumps/".to_string(); + dump_paths.set_dump_name("coredump"); + let core_path = dump_paths.get_core_path().to_string(); + dump_paths.set_working_dir(&core_path); + dump_paths.set_dumps_extn("*core.prog*.gz*"); + dump_paths.set_tar_extn(".core.tgz"); + dump_paths.set_lock_dir_prefix("/tmp/.uploadCoredumps"); + dump_paths.set_crash_portal_path("/opt/crashportal_uploads/coredumps/"); } else { - println!("starting minidump processing..."); - dump_paths.dump_name = "minidump".to_string(); - dump_paths.working_dir = dump_paths.get_minidumps_path().to_string(); - dump_paths.dumps_extn = "*.dmp*".to_string(); - dump_paths.tar_extn = "*.dmp.tgz".to_string(); - dump_paths.lock_dir_prefix = "/tmp/.uploadMinidumps".to_string(); - dump_paths.crash_portal_path = "/opt/crashportal_uploads/coredumps/".to_string(); - sleep(5); + println!("Starting minidump processing..."); + dump_paths.set_dump_name("minidump"); + let minidumps_path = dump_paths.get_minidumps_path().to_string(); + dump_paths.set_working_dir(&minidumps_path); + dump_paths.set_dumps_extn("*.dmp*"); + dump_paths.set_tar_extn("*.dmp.tgz"); + dump_paths.set_lock_dir_prefix("/tmp/.uploadMinidumps"); + dump_paths.set_crash_portal_path("/opt/crashportal_uploads/coredumps/"); + thread::sleep(Duration::from_secs(5)); }; - dump_paths.ts_file = format!("/tmp/.{}_upload_timestamps", dump_paths.dump_name); + dump_paths.set_ts_file(format!("/tmp/.{}_upload_timestamps", dump_paths.get_dump_name())); + // Locking logic if wait_for_lock == "wait_for_lock" { - create_lock_or_wait(&dump_paths.lock_dir_prefix); + create_lock_or_wait(dump_paths.get_lock_dir_prefix()); } else { - create_lock_or_exit(&dump_paths.lock_dir_prefix); + create_lock_or_exit(dump_paths.get_lock_dir_prefix()); } - if device_data.device_type == "hybrid" || device_data.device_type == "mediaclient" { + // Defer upload if device just booted (hybrid/mediaclient) + let tmp_device_type = device_data.get_device_type(); + if tmp_device_type == "hybrid" || tmp_device_type == "mediaclient" { let uptime_str = fs::read_to_string("/proc/uptime").expect("Unable to read uptime"); let uptime_val = uptime_str .split('.') @@ -94,29 +103,33 @@ fn main() { if uptime_val < 480 { let sleep_time = 480 - uptime_val; println!("Deferring reboot for {} seconds", sleep_time); - sleep(sleep_time); + thread::sleep(Duration::from_secs(sleep_time)); if Path::new(constants::CRASH_UPLOAD_REBOOT_FLAG).exists() { println!("Process crashed exiting from the Deferring reboot"); + finalize(&dump_paths); // TODO: Should we finalize & exit? + exit(0); } } } - + + // Check if working directory is empty let w_dir = dump_paths.get_working_dir(); match fs::read_dir(w_dir) { - Ok(mut entries) => entries.next().is_none(), + Ok(mut entries) => { + if entries.next().is_none() { + println!("Working dir is empty: {}", w_dir); + finalize(&dump_paths); // TODO: Should we finalize & exit? + exit(0); + } + } Err(_) => { - eprintln!("working dir is empty : {}", w_dir); - std::process::exit(0); + println!("working dir is empty : {}", w_dir); + finalize(&dump_paths); // TODO: Should we finalize & exit? + exit(0); } }; - let mut portal_url = String::new(); - get_rfc_param( - "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.CrashUpload.crashPortalSTBUrl", - &mut portal_url, - ); - let req_type = 17; - + // Network availability check let mut counter = 1; let mut no_network = false; let route_file = Path::new(constants::NETWORK_FILE); @@ -131,7 +144,7 @@ fn main() { "Network is not available, Sleep for {} seconds", constants::NETWORK_CHECK_TIMEOUT ); - sleep(constants::NETWORK_CHECK_TIMEOUT as u64); + thread::sleep(Duration::from_secs(constants::NETWORK_CHECK_TIMEOUT as u64)); counter += 1; } } @@ -141,14 +154,14 @@ fn main() { no_network = true; } + // System time availability check println!("IP Acquisition completed, Check if system time is received"); - let stt_file = Path::new(constants::SYSTEM_TIME_FILE); if !stt_file.exists() { while counter <= constants::SYSTEM_TIME_ITERATION { if !stt_file.exists() { println!("Waiting for STT, iteration {}", counter); - sleep(constants::SYSTEM_TIME_TIMEOUT as u64); + thread::sleep(Duration::from_secs(constants::SYSTEM_TIME_TIMEOUT as u64)); } else { println!("Received {} flag", constants::SYSTEM_TIME_FILE); break; @@ -165,33 +178,59 @@ fn main() { // trap finalize EXIT + // Wait for coredump completion if needed if !Path::new(constants::COREDUMP_MTX_FILE).exists() && dump_flag == 1 { println!("Waiting for Coredump completion"); - sleep(21); + thread::sleep(Duration::from_secs(21)); + } + + // Early exit if box is rebooting + if is_box_rebooting() { + finalize(&dump_paths); // TODO: Should we finalize & exit? + exit(0); } - println!("Mac Address is {}", device_data.mac_addr); + // Print device MAC address + println!("Mac Address is {}", device_data.get_mac_addr()); - let mut dump_count = 0; - dump_count = match get_dump_count(&dump_paths.working_dir, &dump_paths.dumps_extn) { - Ok(e) => e, + // Count dumps using utility function + let dump_count = match get_dump_count(dump_paths.get_working_dir(), dump_paths.get_dumps_extn()) { + Ok(dump_cnt) => dump_cnt, Err(_) => 0, }; if dump_count == 0 { - println!("No {} for uploading exist", dump_paths.dump_name); + println!("No {} for uploading exist", dump_paths.get_dump_name()); + finalize(&dump_paths); // TODO: Should we finalize & exit? exit(0); } + + // Cleanup old dumps using utility function let _ = cleanup( - &dump_paths.working_dir, - &dump_paths.dump_name, - &dump_paths.dumps_extn, + dump_paths.get_working_dir(), + dump_paths.get_dump_name(), + dump_paths.get_dumps_extn(), ); - println!("Portal URL is {}", portal_url); - println!("buildID is {}", device_data.sha1); + // Print portal URL and build ID using getters + println!("Portal URL {}", device_data.get_portal_url()); + println!("buildID is {}", device_data.get_sha1()); + + // Final check: working directory must be a directory if !Path::new(w_dir).is_dir() { exit(1); } - // For Loop to processDumps + // Main processing loop (up to 3 attempts, as in shell script) + for _ in 0..3 { + let files = crashupload_utils::find_dump_files( + dump_paths.get_working_dir(), + dump_paths.get_dumps_extn(), + ).unwrap_or_default(); + if files.is_empty() { + break; + } + process_dumps(&device_data, &dump_paths, &crash_ts, no_network); + } + + finalize(&dump_paths); } diff --git a/crates/platform_interface/src/lib.rs b/crates/platform_interface/src/lib.rs index 5c94b94..92c32c1 100644 --- a/crates/platform_interface/src/lib.rs +++ b/crates/platform_interface/src/lib.rs @@ -1,6 +1,16 @@ +//! Platform interface library. +//! +//! This crate provides APIs for interacting with platform features such as RFC (TR-181) and Telemetry2 (T2). +//! It exposes convenient functions for RFC parameter access and telemetry marker notification. + mod rfc_api; mod t2_api; +/// Platform interface library version. pub const PLATFORM_LIB_VER: &str = "v1.0"; + +// Re-export RFC API functions for external use. pub use rfc_api::{get_rfc_param, set_rfc_param}; + +// Re-export T2 API functions for external use. pub use t2_api::{t2_count_notify, t2_val_notify}; diff --git a/crates/platform_interface/src/rfc_api.rs b/crates/platform_interface/src/rfc_api.rs index 9adcf76..83b8702 100644 --- a/crates/platform_interface/src/rfc_api.rs +++ b/crates/platform_interface/src/rfc_api.rs @@ -1,16 +1,38 @@ -// platform_interface/src/rfc_api.rs +//! RFC (Remote Feature Control) API integration for TR-181 parameter access. +//! +//! This module provides functions to get and set TR-181 parameters using the `tr181` binary. +//! It is used for interacting with device configuration at runtime. + use std::path::Path; use std::process::Command; -// Module-level constants +/// Path to the TR-181 binary used for RFC parameter access. const TR181_BIN: &str = "tr181"; -//const TR181_SET_BIN: &str = "tr181Set"; TODO +// const TR181_SET_BIN: &str = "tr181Set"; // TODO: Implement if needed +/// Returns the path to the TR-181 binary as a `Path`. +#[inline] fn rfc_bin_path() -> &'static Path { Path::new(TR181_BIN) } -/// Set a TR-181 RFC parameter to a value +/// Sets a TR-181 RFC parameter to a specified value. +/// +/// This function invokes the `tr181` binary with the `-s` (set) and `-v` (value) flags +/// to set the given RFC parameter to the provided value. +/// +/// # Arguments +/// * `rfc` - The TR-181 parameter name. +/// * `value` - The value to set for the parameter. +/// +/// # Returns +/// * `true` if the parameter was successfully set, `false` otherwise. +/// +/// # Example +/// ``` +/// use platform_interface::rfc_api::set_rfc_param; +/// let success = set_rfc_param("Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.Foo.Enable", "true"); +/// ``` pub fn set_rfc_param, V: AsRef>(rfc: R, value: V) -> bool { let rfc_bin = rfc_bin_path(); if rfc_bin.exists() { @@ -23,7 +45,7 @@ pub fn set_rfc_param, V: AsRef>(rfc: R, value: V) -> bool { { Ok(_) => true, Err(err) => { - eprintln!("{} get failed with {}", rfc_bin.display(), err); + println!("{} set failed with {}", rfc_bin.display(), err); false } } @@ -32,7 +54,26 @@ pub fn set_rfc_param, V: AsRef>(rfc: R, value: V) -> bool { } } -/// Get a TR-181 RFC parameter into a mutable string +/// Gets a TR-181 RFC parameter value into a mutable string. +/// +/// This function invokes the `tr181` binary with the `-g` (get) flag to retrieve +/// the value of the given RFC parameter and stores it in the provided mutable string reference. +/// +/// # Arguments +/// * `rfc` - The TR-181 parameter name. +/// * `res` - Mutable reference to a string to store the retrieved value. +/// +/// # Returns +/// * `true` if the parameter was successfully retrieved, `false` otherwise. +/// +/// # Example +/// ``` +/// use platform_interface::rfc_api::get_rfc_param; +/// let mut value = String::new(); +/// if get_rfc_param("Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.Foo.Enable", &mut value) { +/// println!("RFC value: {}", value); +/// } +/// ``` pub fn get_rfc_param>(rfc: R, res: &mut String) -> bool { let rfc_bin = rfc_bin_path(); if rfc_bin.exists() { diff --git a/crates/platform_interface/src/t2_api.rs b/crates/platform_interface/src/t2_api.rs index 84eeff2..746d30e 100644 --- a/crates/platform_interface/src/t2_api.rs +++ b/crates/platform_interface/src/t2_api.rs @@ -1,15 +1,37 @@ -// platform_interface/src/t2.rs +//! Telemetry2 (T2) API integration for sending telemetry markers to the platform. +//! +//! This module provides functions to notify T2 markers with count or value semantics +//! by invoking the `telemetry2_0_client` binary if present on the system. + use std::path::Path; use std::process::Command; -// Module-level constants +/// Path to the Telemetry2 client binary. const T2_MSG_CLIENT_PATH: &str = "/usr/bin/telemetry2_0_client"; +/// Returns the path to the Telemetry2 client as a `Path`. +#[inline] fn t2_msg_client_path() -> &'static Path { Path::new(T2_MSG_CLIENT_PATH) } -/// Notify a telemetry marker with a count (default 1 if None) +/// Notify a telemetry marker with a count value. +/// +/// This function invokes the T2 client with the given marker and count. +/// If `count` is `None`, a default value of `"1"` is used. +/// +/// # Arguments +/// * `marker` - The telemetry marker name. +/// * `count` - Optional count value as a string (defaults to `"1"`). +/// +/// # Returns +/// * `true` if the notification was successfully sent, `false` otherwise. +/// +/// # Example +/// ``` +/// use platform_interface::t2_api::t2_count_notify; +/// t2_count_notify("SYST_INFO_minidumpUpld", None); +/// ``` pub fn t2_count_notify, C: AsRef>(marker: M, count: Option) -> bool { let t2_client = t2_msg_client_path(); if t2_client.exists() { @@ -21,7 +43,7 @@ pub fn t2_count_notify, C: AsRef>(marker: M, count: Option { Ok(_) => true, Err(err) => { - eprintln!("{} execution failed with {}", t2_client.display(), err); + println!("{} execution failed with {}", t2_client.display(), err); false } } @@ -30,7 +52,22 @@ pub fn t2_count_notify, C: AsRef>(marker: M, count: Option } } -/// Notify a telemetry marker with a value +/// Notify a telemetry marker with a value. +/// +/// This function invokes the T2 client with the given marker and value. +/// +/// # Arguments +/// * `marker` - The telemetry marker name. +/// * `value` - The value to associate with the marker. +/// +/// # Returns +/// * `true` if the notification was successfully sent, `false` otherwise. +/// +/// # Example +/// ``` +/// use platform_interface::t2_api::t2_val_notify; +/// t2_val_notify("SYST_INFO_minidumpUpld", "42"); +/// ``` pub fn t2_val_notify, V: AsRef>(marker: M, value: V) -> bool { let t2_client = t2_msg_client_path(); if t2_client.exists() { @@ -41,7 +78,7 @@ pub fn t2_val_notify, V: AsRef>(marker: M, value: V) -> bool { Ok(_) => true, Err(err) => { - eprintln!("{} execution failed with {}", t2_client.display(), err); + println!("{} execution failed with {}", t2_client.display(), err); false } } diff --git a/crates/utils/src/command.rs b/crates/utils/src/command.rs index 41b5931..2037365 100644 --- a/crates/utils/src/command.rs +++ b/crates/utils/src/command.rs @@ -1,27 +1,69 @@ +//! File operation helpers. +//! +//! This module provides utility functions for common file operations such as +//! creating files (touch), removing files, and recursively removing directories. + use std::fs::{self, OpenOptions}; use std::path::Path; -use std::thread; -use std::time::Duration; +use std::process::Command; + +use crate::get_property_value_from_file; +/// Creates a file if it does not exist, or updates its modification time if it does (like Unix `touch`). +/// +/// # Arguments +/// * `path` - Path to the file to touch. +/// +/// # Example +/// ``` +/// use utils::touch; +/// touch("/tmp/somefile"); +/// ``` pub fn touch>(path: P) { let _ = OpenOptions::new().create(true).write(true).open(path); } -pub fn rm>(path: P) { - let _ = fs::remove_file(path); -} - +/// Recursively removes a directory and its contents, or removes a file if the path is not a directory. +/// Ignores errors if the path does not exist. +/// +/// # Arguments +/// * `path` - Path to the directory or file to remove. +/// +/// # Example +/// ``` +/// use utils::rm_rf; +/// rm_rf("/tmp/somedir"); +/// rm_rf("/tmp/somefile"); +/// ``` pub fn rm_rf>(path: P) { let path_ref = path.as_ref(); let _ = if path_ref.is_dir() { fs::remove_dir_all(path_ref) } else { - rm(path_ref); + fs::remove_file(path); Ok(()) }; } -pub fn sleep(seconds: u64) -> bool { - thread::sleep(Duration::from_secs(seconds)); - true +/// Flushes system logs, mimicking the flushLogger shell function. +/// +/// - Logs a message using println!(). +/// - Flushes journald buffers if available. +/// - Calls dumpLogs.sh if SYSLOG_NG_ENABLED is not true in device.properties. +pub fn flush_logger() { + println!("flush_logger is called"); + + // Flush journald if available + if Path::new("/etc/os-release").exists() && Command::new("which").arg("journalctl").output().is_ok() { + let _ = Command::new("journalctl").args(&["--sync", "--flush"]).status(); + } + + // Check SYSLOG_NG_ENABLED from device.properties + let mut syslog_ng_enabled = String::new(); + let _ = get_property_value_from_file("/etc/device.properties", "SYSLOG_NG_ENABLED", &mut syslog_ng_enabled); + if syslog_ng_enabled.trim() != "true" { + let _ = Command::new("nice") + .args(&["-n", "19", "/lib/rdk/dumpLogs.sh"]) + .status(); + } } diff --git a/crates/utils/src/device_info.rs b/crates/utils/src/device_info.rs index d660fdb..8b45d05 100644 --- a/crates/utils/src/device_info.rs +++ b/crates/utils/src/device_info.rs @@ -1,22 +1,38 @@ -// utils/src/device_info.rs +//! Device information utilities. +//! +//! This module provides functions to retrieve device properties such as the MAC address, +//! version file SHA1, and arbitrary property values from property files. + use std::fs::File; use std::io::{self, BufRead, BufReader, Read}; use std::path::Path; use std::process::Command; -// Module-level constants -pub const DEVICE_PROP_FILE: &str = "/etc/device.properties"; -pub const INCLUDE_PROP_FILE: &str = "/etc/include.properties"; -pub const COMMON_PROP_FILE: &str = "/etc/common.properties"; +/// Path to the file containing the device MAC address. pub const DEVICE_MAC_FILE: &str = "/tmp/.macAddress"; + +/// Path to the file containing the image version. pub const VERSION_FILE: &str = "/version.txt"; -// use this fn to getModel() using "MODEL_NUM" key -pub fn get_property_value_from_file, K: AsRef>( - path: P, - key: K, - val: &mut String, -) -> bool { +/// Retrieves the value for a given key from a property file. +/// +/// # Arguments +/// * `path` - Path to the property file. +/// * `key` - The property key to search for. +/// * `val` - Mutable reference to a string to store the value if found. +/// +/// # Returns +/// * `true` if the key is found and value is non-empty, `false` otherwise. +/// +/// # Example +/// ``` +/// let mut value = String::new(); +/// let found = get_property_value_from_file("/opt/device.properties", "MODEL_NUM", &mut value); +/// if found { +/// println!("Model number: {}", value); +/// } +/// ``` +pub fn get_property_value_from_file, K: AsRef>(path: P, key: K, val: &mut String) -> bool { let key = key.as_ref().trim(); *val = String::new(); let prop_file = match File::open(path) { @@ -40,7 +56,21 @@ pub fn get_property_value_from_file, K: AsRef>( false } -// TODO +/// Calculates the SHA1 hash of the version file and stores it in `sha1_val`. +/// +/// # Arguments +/// * `sha1_val` - Mutable reference to a string to store the SHA1 hash. +/// +/// # Returns +/// * `true` if the SHA1 hash was successfully calculated, `false` otherwise. +/// +/// # Example +/// ``` +/// let mut sha1 = String::new(); +/// if get_sha1_value(&mut sha1) { +/// println!("SHA1: {}", sha1); +/// } +/// ``` pub fn get_sha1_value(sha1_val: &mut String) -> bool { let output = match Command::new("sha1sum").arg(VERSION_FILE).output() { Ok(output) => output, @@ -53,6 +83,21 @@ pub fn get_sha1_value(sha1_val: &mut String) -> bool { true } +/// Reads the device MAC address from the device MAC file and stores it in `mac`. +/// +/// # Arguments +/// * `mac` - Mutable reference to a string to store the MAC address (colons removed). +/// +/// # Returns +/// * `Ok(())` if the MAC address was successfully read, or an `io::Error` otherwise. +/// +/// # Example +/// ``` +/// let mut mac = String::new(); +/// if get_device_mac(&mut mac).is_ok() { +/// println!("MAC: {}", mac); +/// } +/// ``` pub fn get_device_mac(mac: &mut String) -> io::Result<()> { let mut mac_val = String::new(); let mut file = File::open(DEVICE_MAC_FILE)?; diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index 3f65271..d1dbd35 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -1,6 +1,16 @@ +//! Utilities library. +//! +//! This crate provides utility functions for file operations, device information retrieval, +//! and other helpers used throughout the crash upload system. + mod command; mod device_info; +/// Utilities library version. pub const UTILS_LIB_VER: &str = "v1.0"; -pub use command::{rm, rm_rf, sleep, touch}; + +// Re-export file operation helpers. +pub use command::*; + +// Re-export all device information utilities. pub use device_info::*; diff --git a/test/utils_test/src/main.rs b/test/utils_test/src/main.rs index 9b36b5d..5c615bd 100644 --- a/test/utils_test/src/main.rs +++ b/test/utils_test/src/main.rs @@ -81,15 +81,6 @@ mod tests { remove_file("/tmp/.macAddress"); } - // Test for rm function - #[test] - fn test_rm() { - thread::sleep(Duration::from_secs(2)); - touch("test_file.txt"); - rm("test_file.txt"); - assert!(!PathBuf::from("test_file.txt").exists()); - } - // Test for rm_rf function #[test] fn test_rm_rf() { @@ -104,17 +95,6 @@ mod tests { rm_rf(dir_path); assert!(!PathBuf::from(dir_path).exists()); } - - // Test for sleep function - #[test] - fn test_sleep() { - thread::sleep(Duration::from_secs(2)); - let start = std::time::Instant::now(); - sleep(5); - let duration = start.elapsed(); - assert!(duration.as_secs() >= 5); - assert!(duration.as_secs() < 6); - } } fn main() {} From b3b25ce97f7661943506311600b0642308ce1ed7 Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Wed, 4 Jun 2025 12:57:04 +0530 Subject: [PATCH 19/66] Update Code Signed-off-by: gomathishankar37 --- crash_upload/src/constants.rs | 1 - crash_upload/src/crashupload_utils.rs | 50 +++++++++++++++++-------- crash_upload/src/main.rs | 4 +- crates/platform_interface/src/t2_api.rs | 17 +++------ 4 files changed, 41 insertions(+), 31 deletions(-) diff --git a/crash_upload/src/constants.rs b/crash_upload/src/constants.rs index 7f28899..9e248a0 100644 --- a/crash_upload/src/constants.rs +++ b/crash_upload/src/constants.rs @@ -15,7 +15,6 @@ pub const COREDUMP_PROP_FILE: &str = "/opt/coredump.properties"; pub const T2_SHARED_SCRIPT: &str = "/lib/rdk/t2Shared_api.sh"; -pub const S3_BUCKET_URL: &str = "s3.amazonaws.com"; pub const HTTP_CODE_FILE: &str = "/tmp/httpcode"; pub const CURL_UPLOAD_TIMEOUT: usize = 45; pub const FOUR_EIGHTY_SECS: usize = 480; diff --git a/crash_upload/src/crashupload_utils.rs b/crash_upload/src/crashupload_utils.rs index 2899349..8021d07 100644 --- a/crash_upload/src/crashupload_utils.rs +++ b/crash_upload/src/crashupload_utils.rs @@ -217,13 +217,17 @@ fn is_another_instance_running>(path: P) -> bool { /// /// # Returns /// * `true` if lock was created, otherwise exits or returns `false`. -pub fn create_lock_or_exit>(path: P) -> bool { +pub fn create_lock_or_exit>(path: P, is_t2_enabled: bool) -> bool { let lock = lock_path(&path); if is_another_instance_running(&path) { + if is_t2_enabled { + t2_count_notify("SYST_WARN_NoMinidump", Some("1")); + } println!( "Script is already working. {:?}. Skip launching another instance...", lock ); + // TODO: add wait process::exit(0); } @@ -322,11 +326,14 @@ pub fn set_log_file(device_data: &DeviceData, log_mod_ts: &str, line: &str) -> S /// /// # Returns /// * `true` if the box is rebooting (flag file exists), `false` otherwise. -pub fn is_box_rebooting() -> bool { +pub fn is_box_rebooting(is_t2_enabled: bool) -> bool { if Path::new(CRASH_UPLOAD_REBOOT_FLAG).exists() { println!("Skipping Upload, Since Box is Rebooting now..."); - t2_count_notify("SYST_INFO_CoreUpldSkipped", None::<&str>); + if is_t2_enabled { + t2_count_notify("SYST_INFO_CoreUpldSkipped", Some("1")); + } println!("Upload will happen on next reboot"); + // TODO: Add wait return true; } false @@ -598,7 +605,7 @@ pub fn remove_pending_dumps(path: &str, extn: &str) -> io::Result<()> { /// /// # Arguments /// * `file_path` - Path to the crash dump file (as &str). -pub fn process_crash_t2_info(file_path: &str) { +pub fn process_crash_t2_info(file_path: &str, is_t2_enabled: bool) { println!("Processing the crash telemetry info"); let file = Path::new(file_path); let mut file_name_str = file_path.to_string(); @@ -628,21 +635,21 @@ pub fn process_crash_t2_info(file_path: &str) { let app_name = container_name.split('_').nth(1).unwrap_or(container_name); let process_name = container_name.split('_').next().unwrap_or(container_name); - t2_val_notify("crashedContainerName_split", container_name); - t2_val_notify("crashedContainerStatus_split", container_status); - t2_val_notify("crashedContainerAppname_split", app_name); - t2_val_notify("crashedContainerProcessName_split", process_name); t2_count_notify("SYS_INFO_CrashedContainer", Some("1")); println!("Container crash info Basic: {}, {}", app_name, process_name); println!("Container crash info Advanced: {}, {}", container_name, container_status); println!("NEW Appname, Process_Crashed, Status = {}, {}, {}", app_name, process_name, container_status); + + t2_val_notify("APP_ERROR_Crashed_split", &[&app_name, &process_name, &container_status]); + t2_val_notify("APP_ERROR_Crashed_accum", &[&app_name, &process_name, &container_status]); + println!("NEW Processname, App Name, AppState = {}, {}, {}", process_name, app_name, container_status); println!("ContainerName, ContainerStatus = {}, {}", container_name, container_status); - println!("NewProcessCrash_split {}, {}", container_name, container_status); + t2_val_notify("APP_ERROR_CrashInfo_accum", &[&container_name, &container_status]); } } - let _ = get_crashed_log_file(&file_name_str); + let _ = get_crashed_log_file(&file_name_str, is_t2_enabled); } /// Renames a tarball to mark it as crashlooped and (optionally) uploads it to the crash portal. @@ -786,7 +793,7 @@ pub fn get_last_modified_time_of_file(path: &str) -> Option { /// /// # Returns /// * `Ok(())` on success, or an error if file operations fail. -pub fn get_crashed_log_file(file_path: &str) -> io::Result<()> { +pub fn get_crashed_log_file(file_path: &str, is_t2_enabled: bool) -> io::Result<()> { let file = file_path; let process_name = file @@ -796,6 +803,12 @@ pub fn get_crashed_log_file(file_path: &str) -> io::Result<()> { .trim_start_matches("./"); println!("Process crashed = {}", process_name); + if is_t2_enabled { + t2_val_notify("processCrash_split", &[&process_name]); + t2_val_notify("SYST_ERR_Process_Crash_accum", &[&process_name]); + t2_count_notify("SYST_ERR_ProcessCrash", Some("1")); + } + let app_name = file .split('_') .nth(1) @@ -1174,7 +1187,7 @@ pub fn get_privacy_control_mode() -> Option { /// - Modifies files in the working directory (renames, compresses, deletes). /// - May create or remove log files and temporary directories. /// - Calls `handle_tarballs` for tarball upload/save logic. -pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: &str, no_network: bool) { +pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: &str, no_network: bool) { // TODO: Review and add code changes faithful to script utils::flush_logger(); let files = match find_dump_files(dump_paths.get_working_dir(), dump_paths.get_dumps_extn()) { Ok(f) => f, @@ -1193,10 +1206,9 @@ pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: } }; - if dump_paths.dump_name != "coredump" { - process_crash_t2_info(&sanitized.to_string_lossy()); + if dump_paths.get_dump_name() != "coredump" { + process_crash_t2_info(&sanitized.to_string_lossy(), device_data.is_t2_enabled); } - if is_tarball(&sanitized) { println!("Skip archiving {:?} as it is a tarball already.", sanitized); continue; @@ -1228,10 +1240,15 @@ pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: let _ = fs::copy(VERSION_FILE, &version_file_path); } + if device_data.get_is_t2_enabled() && !dump_paths.get_dump_name().is_empty() { + t2_count_notify("SYST_ERR_MINIDPZEROSIZE", Some("1")); + } + let logfiles: Vec = if dump_paths.dump_name == "coredump" { vec![VERSION_FILE.to_string(), CORE_LOG.to_string()] } else { - let crashed_url_file = format!("{}/crashed_url.txt", LOG_PATH); + let crash_url_file = format!("{}/crashed_url.txt", LOG_PATH); + let crashed_url_file = if Path::new(&crash_url_file).exists() { crash_url_file.clone() } else { "".to_string() }; vec![VERSION_FILE.to_string(), CORE_LOG.to_string(), crashed_url_file] }; let logfiles_refs: Vec<&str> = logfiles.iter().map(|s| s.as_str()).collect(); @@ -1242,6 +1259,7 @@ pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: let out_files = copy_log_files_to_tmp(&dump_file_name, &logfiles_refs); let out_files_refs: Vec<&str> = out_files.iter().map(|s| s.as_str()).collect(); let _ = compress_files(&tgz_file, &[&dump_file_name], &out_files_refs); + // TODO: Add failure case handling for compression } let tmp_dir = format!("/tmp/{}", dump_file_name); diff --git a/crash_upload/src/main.rs b/crash_upload/src/main.rs index 5ee0a69..4564935 100644 --- a/crash_upload/src/main.rs +++ b/crash_upload/src/main.rs @@ -86,7 +86,7 @@ fn main() { if wait_for_lock == "wait_for_lock" { create_lock_or_wait(dump_paths.get_lock_dir_prefix()); } else { - create_lock_or_exit(dump_paths.get_lock_dir_prefix()); + create_lock_or_exit(dump_paths.get_lock_dir_prefix(), device_data.get_is_t2_enabled()); } // Defer upload if device just booted (hybrid/mediaclient) @@ -185,7 +185,7 @@ fn main() { } // Early exit if box is rebooting - if is_box_rebooting() { + if is_box_rebooting(device_data.get_is_t2_enabled()) { finalize(&dump_paths); // TODO: Should we finalize & exit? exit(0); } diff --git a/crates/platform_interface/src/t2_api.rs b/crates/platform_interface/src/t2_api.rs index 746d30e..e79d28a 100644 --- a/crates/platform_interface/src/t2_api.rs +++ b/crates/platform_interface/src/t2_api.rs @@ -52,28 +52,21 @@ pub fn t2_count_notify, C: AsRef>(marker: M, count: Option } } -/// Notify a telemetry marker with a value. -/// -/// This function invokes the T2 client with the given marker and value. +/// Notify a telemetry marker with one or more values (comma-separated). /// /// # Arguments /// * `marker` - The telemetry marker name. -/// * `value` - The value to associate with the marker. +/// * `values` - The values to associate with the marker (comma-separated). /// /// # Returns /// * `true` if the notification was successfully sent, `false` otherwise. -/// -/// # Example -/// ``` -/// use platform_interface::t2_api::t2_val_notify; -/// t2_val_notify("SYST_INFO_minidumpUpld", "42"); -/// ``` -pub fn t2_val_notify, V: AsRef>(marker: M, value: V) -> bool { +pub fn t2_val_notify>(marker: M, values: &[&str]) -> bool { let t2_client = t2_msg_client_path(); if t2_client.exists() { + let joined = values.join(", "); match Command::new(t2_client) .arg(marker.as_ref()) - .arg(value.as_ref()) + .arg(&joined) .spawn() { Ok(_) => true, From 33eff719656d3063ed455e7d6b00420ee7e06559 Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Wed, 4 Jun 2025 12:58:17 +0530 Subject: [PATCH 20/66] Update native_full_build.yml --- .github/workflows/native_full_build.yml | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/.github/workflows/native_full_build.yml b/.github/workflows/native_full_build.yml index 7a6ed84..23b9bf7 100644 --- a/.github/workflows/native_full_build.yml +++ b/.github/workflows/native_full_build.yml @@ -1,4 +1,4 @@ -name: Native Full Build +name: Build Crash Upload Component in Native Environment on: pull_request: @@ -15,23 +15,33 @@ jobs: name: Build Check runs-on: ubuntu-latest container: - image: rust:latest + image: ghcr.io/rdkcentral/docker-device-mgt-service-test/native-platform:latest steps: - name: Checkout code uses: actions/checkout@v4 - - name: Install additional components + - name: Install Rust Toolchain components run: | - rustup component add clippy - apt-get update && apt-get install -y git + if ! command -v cargo &> /dev/null; then + curl https://sh.rustup.rs -sSf | sh -s -- -y + export PATH="$HOME/.cargo/bin:$PATH" + fi + rustup update stable + rustup default stable + + - name: Add Rust to PATH + run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH - # - name: Run clippy - # run: cargo clippy --all-targets --all-features -- -D warnings + - name: Install additional components + run: rustup component add clippy - name: Run cargo check run: cargo check --all-targets --all-features + - name: Run clippy (do not fail on warnings) + run: cargo clippy --all-targets --all-features -- -D warnings || true + - name: Build all libraries and binaries (debug) run: cargo build --all-targets --all-features From a57d1ac8437484467b291fc9e6fffe38fe659aaa Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Wed, 4 Jun 2025 15:05:30 +0530 Subject: [PATCH 21/66] Update Code Signed-off-by: gomathishankar37 --- crates/utils/src/command.rs | 2 +- test/utils_test/src/main.rs | 89 ------------------------------------- 2 files changed, 1 insertion(+), 90 deletions(-) diff --git a/crates/utils/src/command.rs b/crates/utils/src/command.rs index 2037365..96e1474 100644 --- a/crates/utils/src/command.rs +++ b/crates/utils/src/command.rs @@ -40,7 +40,7 @@ pub fn rm_rf>(path: P) { let _ = if path_ref.is_dir() { fs::remove_dir_all(path_ref) } else { - fs::remove_file(path); + let _ = fs::remove_file(path); Ok(()) }; } diff --git a/test/utils_test/src/main.rs b/test/utils_test/src/main.rs index 5c615bd..f98ee0d 100644 --- a/test/utils_test/src/main.rs +++ b/test/utils_test/src/main.rs @@ -2,99 +2,10 @@ use std::fs::{self, File}; use std::io::Write; use std::path::PathBuf; -fn create_file_with_content(file_path: &str, content: &str) { - let mut file = File::create(file_path).unwrap(); - file.write_all(content.as_bytes()).unwrap(); -} -fn remove_file(file_path: &str) { - if PathBuf::from(file_path).exists() { - fs::remove_file(file_path).unwrap(); - } -} #[cfg(test)] mod tests { - use std::{thread, time::Duration}; - - use super::*; - use utils::*; - - // Test for get_property_value_from_file() function - #[test] - fn test_get_property_value_from_file() { - thread::sleep(Duration::from_secs(2)); - create_file_with_content("device.properties", "MODEL_NUM=TestModel"); - let mut value = String::new(); - let res = get_property_value_from_file("device.properties", "MODEL_NUM", &mut value); - assert_eq!(res, true); - assert_eq!(value, "TestModel"); - - remove_file("device.properties"); - } - - #[test] - fn test_get_property_value_from_file_no_key() { - thread::sleep(Duration::from_secs(2)); - create_file_with_content("device.properties", "MODEL_NUM=TestModel"); - let mut value = String::new(); - let res = get_property_value_from_file("device.properties", "DEVICE_TYPE", &mut value); - assert_eq!(res, false); - assert!(value.is_empty()); - - remove_file("device.properties"); - } - - // Test for get_device_mac() function - #[test] - fn test_get_device_mac_success_with_colon() { - thread::sleep(Duration::from_secs(2)); - create_file_with_content("/tmp/.macAddress", "00:11:22:33:44:55"); - let mut mac = String::new(); - let res = get_device_mac(&mut mac); - assert_eq!(res.is_ok(), true); - assert_eq!(mac, "001122334455"); - - remove_file("/tmp/.macAddress"); - } - - fn test_get_device_mac_success_no_colon() { - thread::sleep(Duration::from_secs(2)); - create_file_with_content("/tmp/.macAddress", "001122334455"); - let mut mac = String::new(); - let res = get_device_mac(&mut mac); - assert_eq!(res.is_ok(), true); - assert_eq!(mac, "001122334455"); - - remove_file("/tmp/.macAddress"); - } - - #[test] - fn test_get_device_mac_failure() { - thread::sleep(Duration::from_secs(2)); - create_file_with_content("/tmp/.macAddress", ""); - let mut mac = String::new(); - let res = get_device_mac(&mut mac); - assert_eq!(res.is_err(), false); - assert_eq!(mac.is_empty(), true); - - remove_file("/tmp/.macAddress"); - } - - // Test for rm_rf function - #[test] - fn test_rm_rf() { - thread::sleep(Duration::from_secs(2)); - touch("test_file.txt"); - rm_rf("test_file.txt"); - assert!(!PathBuf::from("test_file.txt").exists()); - - let dir_path = "test_dir"; - fs::create_dir(dir_path).unwrap(); - touch(&format!("{}/test_file.txt", dir_path)); - rm_rf(dir_path); - assert!(!PathBuf::from(dir_path).exists()); - } } fn main() {} From af5021bdc42afeed483bc638cf08767991683728 Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Sat, 7 Jun 2025 19:45:06 +0530 Subject: [PATCH 22/66] Update process_dumps() in crashupload_utils.rs Signed-off-by: gomathishankar37 --- crash_upload/src/constants.rs | 25 ++- crash_upload/src/crashupload_utils.rs | 226 +++++++++++++++----------- uploadDumps.sh | 187 +++++++++++---------- 3 files changed, 237 insertions(+), 201 deletions(-) diff --git a/crash_upload/src/constants.rs b/crash_upload/src/constants.rs index 9e248a0..dc4f66e 100644 --- a/crash_upload/src/constants.rs +++ b/crash_upload/src/constants.rs @@ -10,22 +10,9 @@ pub const LOG_PATH: &str = "/opt/rdk"; pub const CORE_LOG: &str = "/opt/rdk/core_log.txt"; pub const DEVICE_PROP_FILE: &str = "/opt/device.properties"; -pub const INCLUDE_PROP_FILE: &str = "/opt/include.properties"; -pub const COREDUMP_PROP_FILE: &str = "/opt/coredump.properties"; pub const T2_SHARED_SCRIPT: &str = "/lib/rdk/t2Shared_api.sh"; - -pub const HTTP_CODE_FILE: &str = "/tmp/httpcode"; -pub const CURL_UPLOAD_TIMEOUT: usize = 45; -pub const FOUR_EIGHTY_SECS: usize = 480; pub const MAX_CORE_FILES: usize = 4; -pub const CORE_PROPS_FILE: &str = "/opt/coredump.properties"; -pub const S3_FILENAME: &str = "s3filename"; - -pub const TLS: &str = "--tlsv1.2"; - -pub const ENABLE_OSCP_STAPLING: &str = "/tmp/.EnableOCSPStapling"; -pub const ENABLE_OSCP: &str = "/tmp/.EnableOCSPCA"; pub const SECUREDUMP_TR181_NAME: &str = "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.SecDump.Enable"; pub const SECUREDUMP_ENABLE_FILE: &str = "/tmp/.SecureDumpEnable"; @@ -37,7 +24,7 @@ pub const UPLOAD_ON_STARTUP: &str = "/opt/.upload_on_startup"; pub const ON_STARTUP_DUMPS_CLEANED_UP_BASE: &str = "/tmp/.on_startup_dumps_cleaned_up"; pub const CRASH_LOOP_FLAG_FILE: &str = ""; // TODO -pub const POTOMAC_USER: &str = "ccpstbscp"; + pub const COREDUMP_MTX_FILE: &str = "/tmp/coredump_mutex_release"; pub const NETWORK_FILE: &str = "/tmp/route_available"; @@ -50,10 +37,20 @@ pub const SYSTEM_TIME_TIMEOUT: usize = 1; pub const ENCRYPTION_RFC: &str = "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.CrashUpload.encryptionEnabled"; pub const CRASH_PORTAL_URL_RFC: &str = "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.CrashUpload.crashPortalSTBUrl"; +/* UNUSED +pub const INCLUDE_PROP_FILE: &str = "/opt/include.properties"; +pub const COREDUMP_PROP_FILE: &str = "/opt/coredump.properties"; +pub const HTTP_CODE_FILE: &str = "/tmp/httpcode"; +pub const CURL_UPLOAD_TIMEOUT: usize = 45; +pub const S3_FILENAME: &str = "s3filename"; +pub const ENABLE_OSCP_STAPLING: &str = "/tmp/.EnableOCSPStapling"; +pub const ENABLE_OSCP: &str = "/tmp/.EnableOCSPCA"; +pub const POTOMAC_USER: &str = "ccpstbscp"; pub const SHA1_DEFAULT_VALUE: &str = "0000000000000000000000000000000000000000"; pub const TIMESTAMP_DEFAULT_VALUE: &str = "2000-01-01-00-00-00"; pub const MAC_DEFAULT_VALUE: &str = "000000000000"; pub const MODEL_NUM_DEFAULT_VALUE: &str = "UNKNOWN"; +*/ /// Holds all relevant paths and extensions for dump processing. #[derive(Debug)] diff --git a/crash_upload/src/crashupload_utils.rs b/crash_upload/src/crashupload_utils.rs index 8021d07..85aa1b7 100644 --- a/crash_upload/src/crashupload_utils.rs +++ b/crash_upload/src/crashupload_utils.rs @@ -33,6 +33,7 @@ pub fn set_device_data(device_data: &mut DeviceData) { get_property_value_from_file(DEVICE_PROP_FILE, "BOX_TYPE", &mut box_type); get_property_value_from_file(DEVICE_PROP_FILE, "MODEL_NUM", &mut model_num); get_property_value_from_file(DEVICE_PROP_FILE, "DEVICE_TYPE", &mut device_type); + get_property_value_from_file(DEVICE_PROP_FILE, "BUILD_TYPE", &mut build_type); device_data.set_box_type(box_type); device_data.set_model_num(model_num); device_data.set_device_type(device_type); @@ -223,10 +224,7 @@ pub fn create_lock_or_exit>(path: P, is_t2_enabled: bool) -> bool if is_t2_enabled { t2_count_notify("SYST_WARN_NoMinidump", Some("1")); } - println!( - "Script is already working. {:?}. Skip launching another instance...", - lock - ); + println!("Script is already working. {:?}. Skip launching another instance...", lock); // TODO: add wait process::exit(0); } @@ -426,18 +424,24 @@ pub fn set_recovery_time() { /// # Returns /// * `true` if the upload rate limit is reached, `false` otherwise. pub fn is_upload_limit_reached(ts_file: &str) -> bool { + create_lock_or_wait(ts_file); // TODO + const LIMIT_SECONDS: u64 = 600; let path = Path::new(ts_file); if OpenOptions::new().create(true).append(true).open(path).is_err() { println!("Failed to create or open {}", ts_file); + remove_lock(ts_file); // TODO return false; } let file = match File::open(path) { Ok(f) => f, - Err(_) => return false, + Err(_) => { + remove_lock(ts_file); // TODO + return false; + } }; let reader = BufReader::new(&file); @@ -448,6 +452,7 @@ pub fn is_upload_limit_reached(ts_file: &str) -> bool { .collect(); if lines.len() < 10 { + remove_lock(ts_file); // TODO return false; } @@ -457,11 +462,12 @@ pub fn is_upload_limit_reached(ts_file: &str) -> bool { .expect("SystemTime before UNIX_EPOCH") .as_secs(); - if now.saturating_sub(tenth_newest_crash_time) < LIMIT_SECONDS { + let limit_reached = now.saturating_sub(tenth_newest_crash_time) < LIMIT_SECONDS; + if limit_reached { println!("Not uploading the dump. Too many dumps."); - return true; } - false + remove_lock(ts_file); // TODO + limit_reached } /// Removes the crash loop flag and all relevant lock files for the given dump paths. @@ -512,6 +518,7 @@ pub fn handle_crash_signal(signal: CrashSignal, dump_paths: &DumpPaths) { remove_crash_locks_and_flag(dump_paths); } +/* UNUSED */ /// Determines if a dump file should be processed/uploaded based on dump type, device type, and file name. /// /// - Always returns `true` for minidumps. @@ -610,7 +617,6 @@ pub fn process_crash_t2_info(file_path: &str, is_t2_enabled: bool) { let file = Path::new(file_path); let mut file_name_str = file_path.to_string(); let container_delimiter = "<#=#>"; - let backward_delimiter = "-"; // Check if file is a tarball if file.extension().and_then(|e| e.to_str()) == Some("tgz") { @@ -729,6 +735,8 @@ pub fn save_dump(minidumps_path: &str, s3_filename: &str, new_name: Option<&str> /// # Arguments /// * `ts_file` - Path to the timestamp file. pub fn log_upload_timestamp(ts_file: &str) { + create_lock_or_wait(ts_file); + let mut build_type = String::new(); get_property_value_from_file("/etc/device.properties", "BUILD_TYPE", &mut build_type); if build_type == "prod" { @@ -744,6 +752,7 @@ pub fn log_upload_timestamp(ts_file: &str) { println!("Failed to truncate timestamp file: {}", e); } } + remove_lock(ts_file); } /// Truncates the timestamp file to the last 10 lines. @@ -754,6 +763,7 @@ pub fn log_upload_timestamp(ts_file: &str) { /// # Returns /// * `Ok(())` on success, or an error if file operations fail. pub fn truncate_timestamp_file(ts_file: &str) -> io::Result<()> { + create_lock_or_wait(ts_file); let ts_path = Path::new(ts_file); let file = File::open(ts_path)?; let reader = BufReader::new(file); @@ -765,6 +775,8 @@ pub fn truncate_timestamp_file(ts_file: &str) -> io::Result<()> { for line in last_10_lines.into_iter().rev() { writeln!(tmp_file, "{}", line)?; } + + remove_lock(ts_file); Ok(()) } @@ -1189,6 +1201,7 @@ pub fn get_privacy_control_mode() -> Option { /// - Calls `handle_tarballs` for tarball upload/save logic. pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: &str, no_network: bool) { // TODO: Review and add code changes faithful to script utils::flush_logger(); + let files = match find_dump_files(dump_paths.get_working_dir(), dump_paths.get_dumps_extn()) { Ok(f) => f, Err(e) => { @@ -1209,71 +1222,95 @@ pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: if dump_paths.get_dump_name() != "coredump" { process_crash_t2_info(&sanitized.to_string_lossy(), device_data.is_t2_enabled); } - if is_tarball(&sanitized) { - println!("Skip archiving {:?} as it is a tarball already.", sanitized); - continue; - } + + if file.is_file() { + if is_tarball(&sanitized) { + println!("Skip archiving {:?} as it is a tarball already.", sanitized); + continue; + } - // Use crash_ts for naming - let mut dump_file_name = set_log_file(device_data, crash_ts, &sanitized.to_string_lossy()); - if dump_file_name.len() >= 135 { - if let Some(pos) = dump_file_name.find('_') { - dump_file_name = dump_file_name[pos + 1..].to_string(); + let mod_date = get_last_modified_time_of_file(&sanitized.to_string_lossy()).unwrap_or_else(|| crash_ts.to_string()); + let ts_for_naming = if crash_ts.is_empty() { &mod_date } else { crash_ts }; + + let mut dump_file_name = set_log_file(device_data, ts_for_naming, &sanitized.to_string_lossy()); + + if dump_file_name.len() >= 135 { + if let Some(pos) = dump_file_name.find('_') { + dump_file_name = dump_file_name[pos + 1..].to_string(); + } } - } - let tgz_file = if dump_paths.dump_name == "coredump" { - format!("{}.core.tgz", dump_file_name) - } else { - format!("{}.tgz", dump_file_name) - }; + let tgz_file = if dump_paths.dump_name == "coredump" { + format!("{}.core.tgz", dump_file_name) + } else { + format!("{}.tgz", dump_file_name) + }; - let dump_file_name = dump_file_name.replace("<#=#>", "_"); + let dump_file_name = dump_file_name.replace("<#=#>", "_"); - if let Err(e) = fs::rename(&sanitized, &dump_file_name) { - println!("Failed to rename {:?} to {}: {}", sanitized, dump_file_name, e); - continue; - } + if let Err(e) = fs::rename(&sanitized, &dump_file_name) { + println!("Failed to rename {:?} to {}: {}", sanitized, dump_file_name, e); + continue; + } - let version_file_path = Path::new(dump_paths.get_working_dir()).join("version.txt"); - if !version_file_path.exists() { - let _ = fs::copy(VERSION_FILE, &version_file_path); - } + let version_file_path = Path::new(dump_paths.get_working_dir()).join("version.txt"); + if !version_file_path.exists() { + let _ = fs::copy(VERSION_FILE, &version_file_path); + } - if device_data.get_is_t2_enabled() && !dump_paths.get_dump_name().is_empty() { - t2_count_notify("SYST_ERR_MINIDPZEROSIZE", Some("1")); - } + // Log size of the file before compression + if let Ok(metadata) = std::fs::metadata(&dump_file_name) { + println!("Size of the file: {} bytes",metadata.len()); + } - let logfiles: Vec = if dump_paths.dump_name == "coredump" { - vec![VERSION_FILE.to_string(), CORE_LOG.to_string()] - } else { - let crash_url_file = format!("{}/crashed_url.txt", LOG_PATH); - let crashed_url_file = if Path::new(&crash_url_file).exists() { crash_url_file.clone() } else { "".to_string() }; - vec![VERSION_FILE.to_string(), CORE_LOG.to_string(), crashed_url_file] - }; - let logfiles_refs: Vec<&str> = logfiles.iter().map(|s| s.as_str()).collect(); + if device_data.get_is_t2_enabled() && !dump_paths.get_dump_name().is_empty() { + t2_count_notify("SYST_ERR_MINIDPZEROSIZE", Some("1")); + } - let tar_result = compress_files(&tgz_file, &[&dump_file_name], &logfiles_refs); + let logfiles: Vec = if dump_paths.dump_name == "coredump" { + vec![VERSION_FILE.to_string(), CORE_LOG.to_string()] + } else { + // add_crashed_log_file() - TODO/ UNUSED + let crash_url_file = format!("{}/crashed_url.txt", LOG_PATH); + let crashed_url_file = if Path::new(&crash_url_file).exists() { crash_url_file.clone() } else { "".to_string() }; + vec![VERSION_FILE.to_string(), CORE_LOG.to_string(), crashed_url_file] + }; - if tar_result.is_err() { - let out_files = copy_log_files_to_tmp(&dump_file_name, &logfiles_refs); - let out_files_refs: Vec<&str> = out_files.iter().map(|s| s.as_str()).collect(); - let _ = compress_files(&tgz_file, &[&dump_file_name], &out_files_refs); - // TODO: Add failure case handling for compression - } + let logfiles_refs: Vec<&str> = logfiles.iter().map(|s| s.as_str()).collect(); - let tmp_dir = format!("/tmp/{}", dump_file_name); - if Path::new(&tmp_dir).is_dir() { - rm_rf(&tmp_dir); - println!("Temporary Directory Deleted: {}", tmp_dir); - } - rm_rf(&dump_file_name); + let tar_result = compress_files(&tgz_file, &[&dump_file_name], &logfiles_refs); - if dump_paths.dump_name != "coredump" { - let _ = remove_logs(dump_paths.get_working_dir()); - } - } + if tar_result.is_ok() { + println!("Success Compressing the files, {} {} {} {}", tgz_file, dump_file_name, VERSION_FILE, CORE_LOG); + } + else { + println!("Compression failed, will retry after copying logs to /tmp"); + let out_files = copy_log_files_to_tmp(&dump_file_name, &logfiles_refs); + let out_files_refs: Vec<&str> = out_files.iter().map(|s| s.as_str()).collect(); + let retry_tar_result = compress_files(&tgz_file, &[&dump_file_name], &out_files_refs); + if retry_tar_result.is_ok() { + println!("Success Compressing the files, {} {}", tgz_file, dump_file_name); + } else { + println!("Compression Failed ."); + } + } + + if let Ok(metadata) = std::fs::metadata(&tgz_file) { + println!("Size of the compressed file: {} bytes", metadata.len()); + } + + let tmp_dir = format!("/tmp/{}", dump_file_name); + if Path::new(&tmp_dir).is_dir() { + rm_rf(&tmp_dir); + println!("Temporary Directory Deleted: {}", tmp_dir); + } + rm_rf(&dump_file_name); + if dump_paths.dump_name != "coredump" { + let _ = remove_logs(dump_paths.get_working_dir()); + } + } + } handle_tarballs(device_data, dump_paths, no_network, crash_ts); } @@ -1339,6 +1376,7 @@ fn remove_logs(working_dir: &str) -> io::Result<()> { if path.is_file() { let name = path.file_name().unwrap_or_default().to_string_lossy(); if name.ends_with(".log") || name.ends_with(".txt") { + println!("Removing {}", path.display()); rm_rf(path.to_str().unwrap()); } } @@ -1374,6 +1412,10 @@ pub fn find_dump_files(working_dir: &str, dumps_extn: &str) -> io::Result t, Err(e) => { @@ -1404,7 +1446,9 @@ fn handle_single_tarball(device_data: &DeviceData, dump_paths: &DumpPaths, tarba let s3_filename_sanitized = s3_filename.replace("<#=#>", "_"); // 1. Rate limiting and recovery time - if !is_recovery_time_reached() { + if is_recovery_time_reached() { + rm_rf(DENY_UPLOAD_FILE); + } else { println!("Shifting the recovery time forward."); set_recovery_time(); let _ = remove_pending_dumps( @@ -1412,10 +1456,10 @@ fn handle_single_tarball(device_data: &DeviceData, dump_paths: &DumpPaths, tarba dump_paths.get_dumps_extn(), ); return Ok(()); + // TODO: Should Exit? } - if dump_paths.get_dump_name() == "minidump" - && is_upload_limit_reached(dump_paths.get_ts_file()) + if dump_paths.get_dump_name() == "minidump" && is_upload_limit_reached(dump_paths.get_ts_file()) { println!("Upload rate limit has been reached."); mark_as_crash_loop_and_upload(&tarball_str); @@ -1425,9 +1469,17 @@ fn handle_single_tarball(device_data: &DeviceData, dump_paths: &DumpPaths, tarba dump_paths.get_dumps_extn(), ); return Ok(()); + // TODO: Should Exit? + } + + // 2. no_network logic: skip upload and just save the dump + if dump_paths.get_dump_name() == "minidump" && no_network { + println!("Network is not available, skipping upload and saving dump."); + save_dump(dump_paths.get_minidumps_path(), s3_filename, Some(crash_ts)); + return Ok(()); } - // 2. Privacy mode check + // 3. Privacy mode check if device_data.get_device_type() == "mediaclient" && is_privacy_mode_do_not_share() { println!("Privacy Mode is DO_NOT_SHARE. Stop Uploading the data to the cloud"); let _ = remove_pending_dumps( @@ -1437,28 +1489,16 @@ fn handle_single_tarball(device_data: &DeviceData, dump_paths: &DumpPaths, tarba return Ok(()); } - // 3. Ensure tarball filename is sanitized for S3 + // 4. Ensure tarball filename is sanitized for S3 if s3_filename != s3_filename_sanitized { rename_tarball_for_s3(tarball, &s3_filename_sanitized)?; } - // 4. no_network logic: skip upload and just save the dump - if dump_paths.get_dump_name() == "minidump" && no_network { - println!("Network is not available, skipping upload and saving dump."); - save_dump(dump_paths.get_minidumps_path(), s3_filename, Some(crash_ts)); - return Ok(()); - } - // 5. Upload with retries - let upload_success = upload_tarball_with_retries(&s3_filename_sanitized, dump_paths); + let upload_success = upload_tarball_with_retries(&s3_filename_sanitized, dump_paths, device_data.get_is_t2_enabled()); // 6. Post-upload cleanup - post_upload_cleanup( - upload_success, - dump_paths, - tarball, - &s3_filename_sanitized, - ); + post_upload_cleanup(upload_success, dump_paths, tarball, &s3_filename_sanitized); Ok(()) } @@ -1492,32 +1532,21 @@ fn rename_tarball_for_s3(tarball: &Path, sanitized_name: &str) -> std::io::Resul /// /// # Returns /// * `true` if upload succeeded, `false` otherwise. -fn upload_tarball_with_retries(s3_filename: &str, dump_paths: &DumpPaths) -> bool { +fn upload_tarball_with_retries(s3_filename: &str, dump_paths: &DumpPaths, is_t2_enabled: bool) -> bool { let mut upload_status = false; for attempt in 1..=3 { - println!( - "[{}]: {}: {} S3 Upload Attempt {}", - file!(), - attempt, - dump_paths.get_dump_name(), - s3_filename - ); + println!("[{}]: {}: {} S3 Upload Attempt {}", file!(), attempt, dump_paths.get_dump_name(), s3_filename); match upload_to_s3(&[s3_filename]) { Ok(exit_status) if exit_status.success() => { - println!( - "{} uploadToS3 SUCCESS: status: {:?}", - dump_paths.get_dump_name(), - exit_status - ); + println!("{} uploadToS3 SUCCESS: status: {:?}", dump_paths.get_dump_name(), exit_status); upload_status = true; + if dump_paths.get_dump_name() == "minidump" && is_t2_enabled { + t2_count_notify("SYST_INFO_minidumpUpld", Some("1")); + } break; } Ok(exit_status) => { - println!( - "Execution Status: {:?}, S3 Amazon Upload of {} Failed", - exit_status, - dump_paths.get_dump_name() - ); + println!("Execution Status: {:?}, S3 Amazon Upload of {} Failed", exit_status, dump_paths.get_dump_name()); } Err(e) => { println!("Upload to S3 failed: {}", e); @@ -1552,5 +1581,6 @@ fn post_upload_cleanup(upload_success: bool, dump_paths: &DumpPaths, tarball: &P println!("Removing file {}", s3_filename); let _ = fs::remove_file(&s3_path); } + // TODO: Should exit? } } diff --git a/uploadDumps.sh b/uploadDumps.sh index 30012bf..541b9a6 100755 --- a/uploadDumps.sh +++ b/uploadDumps.sh @@ -793,23 +793,25 @@ processDumps() mv "$f" "$f1" f="$f1" fi + if [ "$DUMP_FLAG" == "0" ]; then processCrashTelemtryInfo "$f" fi + if [ -f "$f" ]; then # Checking whether is it a tarball so we should continue without further processing - #if [[ "$f" =~ '.+_mac.+_dat.+_box.+_mod.+' ]]; then - ext=${f##*.} - if [[ "$ext" = 'tgz' ]]; then - logMessage "Skip archiving $f as it is a tarball already." - continue - fi - #last modification date of a core dump, to ease refusing of already uploaded core dumps on a server side + #if [[ "$f" =~ '.+_mac.+_dat.+_box.+_mod.+' ]]; then + ext=${f##*.} + if [[ "$ext" = 'tgz' ]]; then + logMessage "Skip archiving $f as it is a tarball already." + continue + fi + #last modification date of a core dump, to ease refusing of already uploaded core dumps on a server side modDate=`getLastModifiedTimeOfFile $f` if [ -z "$CRASHTS" ]; then - CRASHTS=$modDate - # Ensure timestamp is not empty - checkParameter CRASHTS + CRASHTS=$modDate + # Ensure timestamp is not empty + checkParameter CRASHTS fi if [ "$DUMP_FLAG" == "1" ] ; then @@ -819,18 +821,18 @@ processDumps() else dumpName=`setLogFile $sha1 $MAC $CRASHTS $boxType $modNum $f` fi - if [ "${#dumpName}" -ge "135" ]; then - #Removing the HEADER of the corefile due to ecryptfs limitation as file can't be open when it exceeds 140 characters. - dumpName="${dumpName#*_}" - fi + if [ "${#dumpName}" -ge "135" ]; then + #Removing the HEADER of the corefile due to ecryptfs limitation as file can't be open when it exceeds 140 characters. + dumpName="${dumpName#*_}" + fi tgzFile=$dumpName".core.tgz" else dumpName=`setLogFile $sha1 $MAC $CRASHTS $boxType $modNum $f` - if [ "${#dumpName}" -ge "135" ]; then - #Removing the HEADER of the corefile due to ecryptfs limitation as file can't be open when it exceeds 140 characters. - dumpName="${dumpName#*_}" - fi - tgzFile=$dumpName".tgz" + if [ "${#dumpName}" -ge "135" ]; then + #Removing the HEADER of the corefile due to ecryptfs limitation as file can't be open when it exceeds 140 characters. + dumpName="${dumpName#*_}" + fi + tgzFile=$dumpName".tgz" fi #remove <#=#> characters from the dumpname to avoid processing issues. @@ -841,40 +843,43 @@ processDumps() TMP_DIR_NAME=$dumpName logMessage "Size of the file: $(ls -l $dumpName)" - if [ "$DUMP_FLAG" == "1" ] ; then - logfiles="$VERSION_FILE $CORE_LOG" + + if [ "$DUMP_FLAG" == "1" ] ; then + logfiles="$VERSION_FILE $CORE_LOG" if [ -f /tmp/set_crash_reboot_flag ];then - logMessage "Compression without nice" - tar -zcvf $tgzFile $dumpName $logfiles 2>&1 | logStdout - else - logMessage "Compression with nice" - nice -n 19 tar -zcvf $tgzFile $dumpName $logfiles 2>&1 | logStdout + logMessage "Compression without nice" + tar -zcvf $tgzFile $dumpName $logfiles 2>&1 | logStdout + else + logMessage "Compression with nice" + nice -n 19 tar -zcvf $tgzFile $dumpName $logfiles 2>&1 | logStdout fi else - crashedUrlFile=$LOG_PATH/crashed_url.txt - files="$VERSION_FILE $CORE_LOG $crashedUrlFile" - add_crashed_log_file $files - nice -n 19 tar -zcvf $tgzFile $dumpName $files 2>&1 | logStdout - fi - if [ $? -eq 0 ]; then - logMessage "Success Compressing the files, $tgzFile $dumpName $VERSION_FILE $CORE_LOG " - else - # If the tar creation failed then will create new tar after copying logs files to /tmp - OUT_FILES="$dumpName" - [ "$DUMP_FLAG" == "1" ] && copy_log_files_tmp_dir $logfiles || copy_log_files_tmp_dir $files + crashedUrlFile=$LOG_PATH/crashed_url.txt + files="$VERSION_FILE $CORE_LOG $crashedUrlFile" + add_crashed_log_file $files + nice -n 19 tar -zcvf $tgzFile $dumpName $files 2>&1 | logStdout + fi + + if [ $? -eq 0 ]; then + logMessage "Success Compressing the files, $tgzFile $dumpName $VERSION_FILE $CORE_LOG " + else + # If the tar creation failed then will create new tar after copying logs files to /tmp + OUT_FILES="$dumpName" + [ "$DUMP_FLAG" == "1" ] && copy_log_files_tmp_dir $logfiles || copy_log_files_tmp_dir $files nice -n 19 tar -zcvf $tgzFile $OUT_FILES 2>&1 | logStdout - if [ $? -eq 0 ]; then - logMessage "Success Compressing the files, $tgzFile $OUT_FILES" - else - logMessage "Compression Failed ." - fi - fi + if [ $? -eq 0 ]; then + logMessage "Success Compressing the files, $tgzFile $OUT_FILES" + else + logMessage "Compression Failed ." + fi + fi logMessage "Size of the compressed file: $(ls -l $tgzFile)" - if [ ! -z "$TMP_DIR_NAME" ] && [ -d "/tmp/$TMP_DIR_NAME" ]; then - rm -rf /tmp/$TMP_DIR_NAME - logMessage "Temporary Directory Deleted:/tmp/$TMP_DIR_NAME" + if [ ! -z "$TMP_DIR_NAME" ] && [ -d "/tmp/$TMP_DIR_NAME" ]; then + rm -rf /tmp/$TMP_DIR_NAME + logMessage "Temporary Directory Deleted:/tmp/$TMP_DIR_NAME" fi + rm $dumpName if [ "$DUMP_FLAG" == "0" ]; then @@ -901,18 +906,20 @@ processDumps() removePendingDumps exit fi + if [ "$DUMP_NAME" = "minidump" ]; then - if isUploadLimitReached; then + if isUploadLimitReached; then logMessage "Upload rate limit has been reached." markAsCrashLoopedAndUpload $f logMessage "Setting recovery time" setRecoveryTime removePendingDumps exit - fi + fi else logMessage "Coredump File `echo $f`" fi + S3_FILENAME=`echo ${f##*/}` count=1 @@ -943,61 +950,63 @@ processDumps() fi logMessage "[$0]: $count: $DUMP_NAME S3 Upload " - if [ -f /lib/rdk/uploadDumpsToS3.sh ]; then + if [ -f /lib/rdk/uploadDumpsToS3.sh ]; then echo "$WORKING_DIR $partnerId $DUMP_NAME $DEVICE_TYPE $VERSION_FILE $encryptionEnable $EnableOCSPStapling $EnableOCSP $TLS $BUILD_TYPE $modNum ${CURL_LOG_OPTION}" > /tmp/uploadtos3params uploadToS3 "`echo $S3_FILENAME`" status=$? - rm /tmp/uploadtos3params - else + rm /tmp/uploadtos3params + else # A secure upload logic is required to upload the crash dumps to the cloud database server. - # The upload script can take in parameters such as Device model, upload options related to security and the dump file to be uploaded - # The return value from the upload logic can be checked to determine a successful coredump upload to server. - # Retries can be triggered at regular intervals if needed, in case the upload fails at the initial try. - echo "A secure core/minidump upload logic can be implemented here to upload the crash dumps to cloud database." - echo "Parameters such as device type, secure upload options and the file to be uploaded can be passed to the upload function/script." - fi + # The upload script can take in parameters such as Device model, upload options related to security and the dump file to be uploaded + # The return value from the upload logic can be checked to determine a successful coredump upload to server. + # Retries can be triggered at regular intervals if needed, in case the upload fails at the initial try. + echo "A secure core/minidump upload logic can be implemented here to upload the crash dumps to cloud database." + echo "Parameters such as device type, secure upload options and the file to be uploaded can be passed to the upload function/script." + fi while [ $count -le 3 ] do # S3 amazon fail over recovery - count=$(( count +1)) + count=$(( count +1)) if [ $status -ne 0 ];then - logMessage "[$0]: Execution Status: $status, S3 Amazon Upload of $DUMP_NAME Failed" - logMessage "[$0]: $count: (Retry), $DUMP_NAME S3 Upload" - sleep 2 - if [ -f /lib/rdk/uploadDumpsToS3.sh ]; then - echo "$WORKING_DIR $partnerId $DUMP_NAME $DEVICE_TYPE $VERSION_FILE $encryptionEnable $EnableOCSPStapling $EnableOCSP $TLS $BUILD_TYPE $modNum ${CURL_LOG_OPTION}" > /tmp/uploadtos3params - uploadToS3 "`echo $S3_FILENAME`" - status=$? - rm /tmp/uploadtos3params - else - # A secure upload logic is required to upload the crash dumps to the cloud database server. - # The upload script can take in parameters such as Device model, upload options related to security and the dump file to be uploaded - # The return value from the upload logic can be checked to determine a successful coredump upload to server. - # Retries can be triggered at regular intervals if needed, in case the upload fails at the initial try. - echo "A secure core/minidump upload logic can be implemented here to upload the crash dumps to cloud database." - echo "Parameters such as device type, secure upload options and the file to be uploaded can be passed to the upload function/script." - fi + logMessage "[$0]: Execution Status: $status, S3 Amazon Upload of $DUMP_NAME Failed" + logMessage "[$0]: $count: (Retry), $DUMP_NAME S3 Upload" + sleep 2 + if [ -f /lib/rdk/uploadDumpsToS3.sh ]; then + echo "$WORKING_DIR $partnerId $DUMP_NAME $DEVICE_TYPE $VERSION_FILE $encryptionEnable $EnableOCSPStapling $EnableOCSP $TLS $BUILD_TYPE $modNum ${CURL_LOG_OPTION}" > /tmp/uploadtos3params + uploadToS3 "`echo $S3_FILENAME`" + status=$? + rm /tmp/uploadtos3params + else + # A secure upload logic is required to upload the crash dumps to the cloud database server. + # The upload script can take in parameters such as Device model, upload options related to security and the dump file to be uploaded + # The return value from the upload logic can be checked to determine a successful coredump upload to server. + # Retries can be triggered at regular intervals if needed, in case the upload fails at the initial try. + echo "A secure core/minidump upload logic can be implemented here to upload the crash dumps to cloud database." + echo "Parameters such as device type, secure upload options and the file to be uploaded can be passed to the upload function/script." + fi else - logMessage "[$0]: $DUMP_NAME uploadToS3 SUCESS: status: $status" - if [ "$DUMP_NAME" == "minidump" ] && [ "$IS_T2_ENABLED" == "true" ]; then - t2CountNotify "SYST_INFO_minidumpUpld" - fi - break + logMessage "[$0]: $DUMP_NAME uploadToS3 SUCESS: status: $status" + if [ "$DUMP_NAME" == "minidump" ] && [ "$IS_T2_ENABLED" == "true" ]; then + t2CountNotify "SYST_INFO_minidumpUpld" + fi + break fi done + if [ $status -ne 0 ];then - logMessage "[$0]: S3 Amazon Upload of $DUMP_NAME Failed..!" - if [ "$DUMP_NAME" == "minidump" ]; then - logMessage "Check and save the dump $S3_FILENAME" - saveDump "$ORGINAL_FILENAME" - else - logMessage "Removing file $S3_FILENAME" - rm -f $S3_FILENAME - fi - exit 1 + logMessage "[$0]: S3 Amazon Upload of $DUMP_NAME Failed..!" + if [ "$DUMP_NAME" == "minidump" ]; then + logMessage "Check and save the dump $S3_FILENAME" + saveDump "$ORGINAL_FILENAME" + else + logMessage "Removing file $S3_FILENAME" + rm -f $S3_FILENAME + fi + exit 1 else - echo "[$0]: Execution Status: $status, S3 Amazon Upload of $DUMP_NAME Success" + echo "[$0]: Execution Status: $status, S3 Amazon Upload of $DUMP_NAME Success" fi + ORGINAL_FILENAME="" logMessage "Removing file $S3_FILENAME" rm -f $S3_FILENAME From ab7c295d82bbd2144d612580d08abc2a930dc4ff Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Tue, 10 Jun 2025 17:17:42 +0530 Subject: [PATCH 23/66] Update Code changes Signed-off-by: gomathishankar37 --- crash_upload/src/crashupload_utils.rs | 76 ++++++++++++------------ crash_upload/src/main.rs | 2 +- crates/platform_interface/src/rfc_api.rs | 32 ++++++++++ crates/utils/src/command.rs | 6 +- crates/utils/src/device_info.rs | 2 +- test/utils_test/src/main.rs | 6 -- 6 files changed, 76 insertions(+), 48 deletions(-) diff --git a/crash_upload/src/crashupload_utils.rs b/crash_upload/src/crashupload_utils.rs index 85aa1b7..67e196e 100644 --- a/crash_upload/src/crashupload_utils.rs +++ b/crash_upload/src/crashupload_utils.rs @@ -5,13 +5,16 @@ use std::io::{self, BufRead, BufReader, Write}; use std::path::{Path, PathBuf}; use std::process::Command; use std::time::{Duration, SystemTime, UNIX_EPOCH}; -use std::{process, usize}; +use std::process; use std::{thread, time}; use crate::constants::*; use platform_interface::*; use utils::*; +// #[cfg(feature = "shared_api")] +// pub use crate::upload_to_s3::upload_to_s3; + /// Populates a [`DeviceData`] struct with device properties from system files and TR-181. /// /// This function mirrors the environment setup in `uploadDumps.sh`, reading values from @@ -85,8 +88,8 @@ pub fn set_device_data(device_data: &mut DeviceData) { /// # Returns /// * `true` if neither minidumps nor coredumps exist (should exit), `false` otherwise. pub fn should_exit_crash_upload(minidumps_path: &str, core_path: &str) -> bool { - let minidumps_exists = check_dumps_exist(&minidumps_path, ".dmp"); // for minidumps - let core_exists = check_dumps_exist(&core_path, "_core"); // for core + let minidumps_exists = check_dumps_exist(minidumps_path, ".dmp"); // for minidumps + let core_exists = check_dumps_exist(core_path, "_core"); // for core !(minidumps_exists || core_exists) } @@ -297,7 +300,7 @@ pub fn remove_lock>(path: P) { /// # Returns /// * A `String` with the new or original file name. pub fn set_log_file(device_data: &DeviceData, log_mod_ts: &str, line: &str) -> String { - let file_name = line.split('/').last().unwrap_or(line); + let file_name = line.split('/').next_back().unwrap_or(line); if file_name.contains("_mac") || file_name.contains("_dat") || file_name.contains("_box") @@ -350,21 +353,21 @@ pub fn is_box_rebooting(is_t2_enabled: bool) -> bool { pub fn sanitize(input: &str) -> String { input .chars() - .filter(|c| match *c { + .filter(|c| matches!( + c, 'a'..='z' - | 'A'..='Z' - | '0'..='9' - | '/' - | ' ' - | ':' - | '+' - | '.' - | '_' - | ',' - | '=' - | '-' => true, - _ => false, - }) + | 'A'..='Z' + | '0'..='9' + | '/' + | ' ' + | ':' + | '+' + | '.' + | '_' + | ',' + | '=' + | '-' + )) .collect() } @@ -533,11 +536,7 @@ pub fn handle_crash_signal(signal: CrashSignal, dump_paths: &DumpPaths) { /// # Returns /// * `true` if the file should be processed, `false` otherwise. pub fn should_process_dump(dump_name: &str, build_type: &str, file_name: &str) -> bool { - if dump_name == "minidump" { - true - } else if build_type != "prod" { - true - } else if !file_name.contains("Receiver") { + if dump_name == "minidump" || build_type != "prod" || !file_name.contains("Receiver") { true } else { println!("Not processing dump file {}", file_name); @@ -553,7 +552,7 @@ pub fn should_process_dump(dump_name: &str, build_type: &str, file_name: &str) - /// /// # Returns /// * `Ok(count)` with the number of matching files, or an error if the directory can't be read. -pub fn get_dump_count(dir: &str, pattern: &str) -> std::io::Result { +pub fn get_file_count(dir: &str, pattern: &str, is_pattern: bool) -> std::io::Result { let dir_path = Path::new(dir); if !dir_path.exists() || !dir_path.is_dir() { return Ok(0); @@ -564,8 +563,12 @@ pub fn get_dump_count(dir: &str, pattern: &str) -> std::io::Result { let file_path = entry.path(); if file_path.is_file() { if let Some(name) = file_path.file_name().and_then(|n| n.to_str()) { - if name.contains(pattern) { - count += 1; + if is_pattern { + if name.contains(pattern) { + count += 1; + } + } else if name == pattern { + count += 1; } } } @@ -647,12 +650,12 @@ pub fn process_crash_t2_info(file_path: &str, is_t2_enabled: bool) { println!("Container crash info Advanced: {}, {}", container_name, container_status); println!("NEW Appname, Process_Crashed, Status = {}, {}, {}", app_name, process_name, container_status); - t2_val_notify("APP_ERROR_Crashed_split", &[&app_name, &process_name, &container_status]); - t2_val_notify("APP_ERROR_Crashed_accum", &[&app_name, &process_name, &container_status]); + t2_val_notify("APP_ERROR_Crashed_split", &[app_name, process_name, container_status]); + t2_val_notify("APP_ERROR_Crashed_accum", &[app_name, process_name, container_status]); println!("NEW Processname, App Name, AppState = {}, {}, {}", process_name, app_name, container_status); println!("ContainerName, ContainerStatus = {}, {}", container_name, container_status); - t2_val_notify("APP_ERROR_CrashInfo_accum", &[&container_name, &container_status]); + t2_val_notify("APP_ERROR_CrashInfo_accum", &[container_name, container_status]); } } let _ = get_crashed_log_file(&file_name_str, is_t2_enabled); @@ -673,9 +676,7 @@ pub fn mark_as_crash_loop_and_upload(tgz_file: &str) { // portal_url: &str, cras println!("Renaming {} to {}", tgz_path.display(), new_tgz_name.display()); if let Err(e) = fs::rename(tgz_path, &new_tgz_name) { println!("Failed to rename crashloop tarball: {}", e); - return; } - // TODO: Not used yet Implement upload logic using portal_url and crash_portal_path if needed. } /// Finds the oldest file in a directory. @@ -713,7 +714,7 @@ pub fn save_dump(minidumps_path: &str, s3_filename: &str, new_name: Option<&str> } let dump_extn = "dmp.tgz"; - let mut count = get_dump_count(minidumps_path, dump_extn).unwrap_or(0); + let mut count = get_file_count(minidumps_path, dump_extn, true).unwrap_or(0); while count > 5 { match find_oldest_dump(minidumps_path) { @@ -768,7 +769,7 @@ pub fn truncate_timestamp_file(ts_file: &str) -> io::Result<()> { let file = File::open(ts_path)?; let reader = BufReader::new(file); - let lines: Vec = reader.lines().filter_map(Result::ok).collect(); + let lines: Vec = reader.lines().map_while(Result::ok).collect(); let last_10_lines = lines.iter().rev().take(10).cloned().collect::>(); let mut tmp_file = OpenOptions::new().write(true).truncate(true).open(ts_path)?; @@ -816,8 +817,8 @@ pub fn get_crashed_log_file(file_path: &str, is_t2_enabled: bool) -> io::Result< println!("Process crashed = {}", process_name); if is_t2_enabled { - t2_val_notify("processCrash_split", &[&process_name]); - t2_val_notify("SYST_ERR_Process_Crash_accum", &[&process_name]); + t2_val_notify("processCrash_split", &[process_name]); + t2_val_notify("SYST_ERR_Process_Crash_accum", &[process_name]); t2_count_notify("SYST_ERR_ProcessCrash", Some("1")); } @@ -1059,7 +1060,7 @@ pub fn add_crashed_log_file(device_data: &DeviceData, log_files: &[&str]) -> io let file = File::open(path)?; let lines: Vec = BufReader::new(file) .lines() - .filter_map(Result::ok) + .map_while(Result::ok) .collect(); // Take the last N lines @@ -1143,6 +1144,7 @@ pub fn copy_log_files_to_tmp(tmp_dir_name: &str, logfiles: &[&str]) -> Vec std::io::Result { let script = "/lib/rdk/uploadDumpsToS3.sh"; if Path::new(script).exists() { @@ -1332,7 +1334,7 @@ fn compress_files(tgz_file: &str, main_files: &[&str], extra_files: &[&str]) -> if status.success() { Ok(()) } else { - Err(io::Error::new(io::ErrorKind::Other, "Compression Failed")) + Err(io::Error::other("Compression Failed")) } } diff --git a/crash_upload/src/main.rs b/crash_upload/src/main.rs index 4564935..59cfa51 100644 --- a/crash_upload/src/main.rs +++ b/crash_upload/src/main.rs @@ -194,7 +194,7 @@ fn main() { println!("Mac Address is {}", device_data.get_mac_addr()); // Count dumps using utility function - let dump_count = match get_dump_count(dump_paths.get_working_dir(), dump_paths.get_dumps_extn()) { + let dump_count = match get_file_count(dump_paths.get_working_dir(), dump_paths.get_dumps_extn(), true) { Ok(dump_cnt) => dump_cnt, Err(_) => 0, }; diff --git a/crates/platform_interface/src/rfc_api.rs b/crates/platform_interface/src/rfc_api.rs index 83b8702..f12233d 100644 --- a/crates/platform_interface/src/rfc_api.rs +++ b/crates/platform_interface/src/rfc_api.rs @@ -97,3 +97,35 @@ pub fn get_rfc_param>(rfc: R, res: &mut String) -> bool { false } } + + +pub fn dmcli_get(param: &str, result: &mut String) { + use std::process::Command; + + let output = Command::new("dmcli") + .args(["eRT", "getv", param]) + .output(); + + if let Ok(output) = output { + if output.status.success() { + let stdout = String::from_utf8_lossy(&output.stdout); + // Faithfully mimic: grep string | cut -d":" -f3- | cut -d" " -f2- | tr -d ' ' + for line in stdout.lines() { + if line.contains("string") { + // Split by ':' and get the 3rd field onward + let after_colon = line.splitn(4, ':').nth(3).unwrap_or("").trim(); + // Split by space and get the 2nd field onward + let after_space = after_colon.splitn(2, ' ').nth(1).unwrap_or("").trim(); + // Remove all spaces + let cleaned = after_space.replace(' ', ""); + *result = cleaned; + return; + } + } + } else { + eprintln!("dmcli_get: dmcli command failed: {}", String::from_utf8_lossy(&output.stderr)); + } + } else { + eprintln!("dmcli_get: failed to execute dmcli command"); + } +} diff --git a/crates/utils/src/command.rs b/crates/utils/src/command.rs index 96e1474..01bd129 100644 --- a/crates/utils/src/command.rs +++ b/crates/utils/src/command.rs @@ -20,7 +20,7 @@ use crate::get_property_value_from_file; /// touch("/tmp/somefile"); /// ``` pub fn touch>(path: P) { - let _ = OpenOptions::new().create(true).write(true).open(path); + let _ = OpenOptions::new().create(true).truncate(true).write(true).open(path); } /// Recursively removes a directory and its contents, or removes a file if the path is not a directory. @@ -55,7 +55,7 @@ pub fn flush_logger() { // Flush journald if available if Path::new("/etc/os-release").exists() && Command::new("which").arg("journalctl").output().is_ok() { - let _ = Command::new("journalctl").args(&["--sync", "--flush"]).status(); + let _ = Command::new("journalctl").args(["--sync", "--flush"]).status(); } // Check SYSLOG_NG_ENABLED from device.properties @@ -63,7 +63,7 @@ pub fn flush_logger() { let _ = get_property_value_from_file("/etc/device.properties", "SYSLOG_NG_ENABLED", &mut syslog_ng_enabled); if syslog_ng_enabled.trim() != "true" { let _ = Command::new("nice") - .args(&["-n", "19", "/lib/rdk/dumpLogs.sh"]) + .args(["-n", "19", "/lib/rdk/dumpLogs.sh"]) .status(); } } diff --git a/crates/utils/src/device_info.rs b/crates/utils/src/device_info.rs index 8b45d05..9b38fc2 100644 --- a/crates/utils/src/device_info.rs +++ b/crates/utils/src/device_info.rs @@ -40,7 +40,7 @@ pub fn get_property_value_from_file, K: AsRef>(path: P, key: Err(_) => return false, }; let reader = BufReader::new(prop_file); - for line in reader.lines().flatten() { + for line in reader.lines().map_while(Result::ok) { if let Some((k, v)) = line.split_once('=') { if k.trim() == key { let value = v.trim(); diff --git a/test/utils_test/src/main.rs b/test/utils_test/src/main.rs index f98ee0d..6343731 100644 --- a/test/utils_test/src/main.rs +++ b/test/utils_test/src/main.rs @@ -1,9 +1,3 @@ -use std::fs::{self, File}; -use std::io::Write; -use std::path::PathBuf; - - - #[cfg(test)] mod tests { } From 9d6ce6935bd4377f22bed23cd0b6f960e9ef46c6 Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Thu, 12 Jun 2025 16:47:09 +0530 Subject: [PATCH 24/66] Add Yocto compatible Cargo manifest Signed-off-by: gomathishankar37 --- .gitignore | 1 + Cargo.lock | 72 +++++++++---------- Cargo.toml | 18 ++--- crash-upload/Cargo.toml | 15 ++++ .../src/constants.rs | 0 .../src/crashupload_utils.rs | 0 {crash_upload => crash-upload}/src/main.rs | 0 crash_upload/Cargo.toml | 12 ---- crates/curl-interface/Cargo.toml | 11 +++ .../src/lib.rs | 0 crates/curl_interface/Cargo.toml | 8 --- crates/json-parser-interface/Cargo.toml | 11 +++ .../src/lib.rs | 0 crates/json_parser_interface/Cargo.toml | 8 --- crates/platform-interface/Cargo.toml | 11 +++ .../src/lib.rs | 0 .../src/rfc_api.rs | 0 .../src/t2_api.rs | 0 crates/platform_interface/Cargo.toml | 8 --- crates/utils/Cargo.toml | 5 +- test/crash-upload-test/Cargo.toml | 7 ++ .../src/main.rs | 0 test/crash_upload_test/Cargo.toml | 7 -- test/curl-interface-test/Cargo.toml | 7 ++ .../src/main.rs | 0 test/curl_interface_test/Cargo.toml | 7 -- test/json-parser-interface-test/Cargo.toml | 7 ++ .../src/main.rs | 0 test/json_parser_interface_test/Cargo.toml | 7 -- test/platform-interface-test/Cargo.toml | 7 ++ .../src/main.rs | 0 test/platform_interface_test/Cargo.toml | 7 -- test/{utils_test => utils-test}/Cargo.toml | 4 +- test/{utils_test => utils-test}/src/main.rs | 0 34 files changed, 128 insertions(+), 112 deletions(-) create mode 100644 .gitignore create mode 100644 crash-upload/Cargo.toml rename {crash_upload => crash-upload}/src/constants.rs (100%) rename {crash_upload => crash-upload}/src/crashupload_utils.rs (100%) rename {crash_upload => crash-upload}/src/main.rs (100%) delete mode 100644 crash_upload/Cargo.toml create mode 100644 crates/curl-interface/Cargo.toml rename crates/{curl_interface => curl-interface}/src/lib.rs (100%) delete mode 100644 crates/curl_interface/Cargo.toml create mode 100644 crates/json-parser-interface/Cargo.toml rename crates/{json_parser_interface => json-parser-interface}/src/lib.rs (100%) delete mode 100644 crates/json_parser_interface/Cargo.toml create mode 100644 crates/platform-interface/Cargo.toml rename crates/{platform_interface => platform-interface}/src/lib.rs (100%) rename crates/{platform_interface => platform-interface}/src/rfc_api.rs (100%) rename crates/{platform_interface => platform-interface}/src/t2_api.rs (100%) delete mode 100644 crates/platform_interface/Cargo.toml create mode 100644 test/crash-upload-test/Cargo.toml rename test/{crash_upload_test => crash-upload-test}/src/main.rs (100%) delete mode 100644 test/crash_upload_test/Cargo.toml create mode 100644 test/curl-interface-test/Cargo.toml rename test/{curl_interface_test => curl-interface-test}/src/main.rs (100%) delete mode 100644 test/curl_interface_test/Cargo.toml create mode 100644 test/json-parser-interface-test/Cargo.toml rename test/{json_parser_interface_test => json-parser-interface-test}/src/main.rs (100%) delete mode 100644 test/json_parser_interface_test/Cargo.toml create mode 100644 test/platform-interface-test/Cargo.toml rename test/{platform_interface_test => platform-interface-test}/src/main.rs (100%) delete mode 100644 test/platform_interface_test/Cargo.toml rename test/{utils_test => utils-test}/Cargo.toml (68%) rename test/{utils_test => utils-test}/src/main.rs (100%) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/Cargo.lock b/Cargo.lock index 15ff0f7..7ae65eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 4 +version = 3 [[package]] name = "android-tzdata" @@ -25,24 +25,24 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" [[package]] name = "cc" -version = "1.2.22" +version = "1.2.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1" +checksum = "956a5e21988b87f372569b66183b78babf23ebc2e744b733e4350a752c4dafac" dependencies = [ "shlex", ] [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "chrono" @@ -65,32 +65,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] -name = "crash_upload" +name = "crash-upload" version = "0.1.0" dependencies = [ "chrono", - "curl_interface", - "json_parser_interface", - "platform_interface", + "curl-interface", + "json-parser-interface", + "platform-interface", "utils", ] [[package]] -name = "crash_upload_test" +name = "crash-upload-test" version = "0.1.0" dependencies = [ - "crash_upload", + "crash-upload", ] [[package]] -name = "curl_interface" +name = "curl-interface" version = "0.1.0" [[package]] -name = "curl_interface_test" +name = "curl-interface-test" version = "0.1.0" dependencies = [ - "curl_interface", + "curl-interface", ] [[package]] @@ -128,14 +128,14 @@ dependencies = [ ] [[package]] -name = "json_parser_interface" +name = "json-parser-interface" version = "0.1.0" [[package]] -name = "json_parser_interface_test" +name = "json-parser-interface-test" version = "0.1.0" dependencies = [ - "json_parser_interface", + "json-parser-interface", ] [[package]] @@ -166,14 +166,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] -name = "platform_interface" +name = "platform-interface" version = "0.1.0" [[package]] -name = "platform_interface_test" +name = "platform-interface-test" version = "0.1.0" dependencies = [ - "platform_interface", + "platform-interface", ] [[package]] @@ -196,9 +196,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "shlex" @@ -208,9 +208,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "syn" -version = "2.0.101" +version = "2.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "f6397daf94fa90f058bd0fd88429dd9e5738999cca8d701813c80723add80462" dependencies = [ "proc-macro2", "quote", @@ -228,7 +228,7 @@ name = "utils" version = "0.1.0" [[package]] -name = "utils_test" +name = "utils-test" version = "0.1.0" dependencies = [ "utils", @@ -294,9 +294,9 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.61.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement", "windows-interface", @@ -329,24 +329,24 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "d3bfe459f85da17560875b8bf1423d6f113b7a87a5d942e7da0ac71be7c61f8b" [[package]] name = "windows-result" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ "windows-link", ] [[package]] name = "windows-strings" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ "windows-link", -] +] \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index bc09585..55ce654 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,13 @@ [workspace] members = [ - "crash_upload", - "crates/curl_interface", - "crates/json_parser_interface", - "crates/platform_interface", + "crash-upload", + "crates/curl-interface", + "crates/json-parser-interface", + "crates/platform-interface", "crates/utils", - "test/crash_upload_test", - "test/curl_interface_test", - "test/json_parser_interface_test", - "test/platform_interface_test", - "test/utils_test", + "test/crash-upload-test", + "test/curl-interface-test", + "test/json-parser-interface-test", + "test/platform-interface-test", + "test/utils-test", ] \ No newline at end of file diff --git a/crash-upload/Cargo.toml b/crash-upload/Cargo.toml new file mode 100644 index 0000000..d2a123e --- /dev/null +++ b/crash-upload/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "crash-upload" +version = "0.1.0" +edition = "2021" +description = "Crash dump uploader" +repository = "https://github.com/rdkcentral/crashupload/tree/feature-sample-rust/crash-upload" +homepage = "https://github.com/rdkcentral/crashupload/tree/feature-sample-rust/crash-upload" + +[dependencies] +# Internal crates +curl-interface = { path = "../crates/curl-interface" } +json_parser-interface = { path = "../crates/json-parser-interface" } +platform-interface = { path = "../crates/platform-interface" } +utils = { path = "../crates/utils" } +chrono = "0.4" diff --git a/crash_upload/src/constants.rs b/crash-upload/src/constants.rs similarity index 100% rename from crash_upload/src/constants.rs rename to crash-upload/src/constants.rs diff --git a/crash_upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs similarity index 100% rename from crash_upload/src/crashupload_utils.rs rename to crash-upload/src/crashupload_utils.rs diff --git a/crash_upload/src/main.rs b/crash-upload/src/main.rs similarity index 100% rename from crash_upload/src/main.rs rename to crash-upload/src/main.rs diff --git a/crash_upload/Cargo.toml b/crash_upload/Cargo.toml deleted file mode 100644 index 353b4fa..0000000 --- a/crash_upload/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "crash_upload" -version = "0.1.0" -edition = "2024" - -[dependencies] -# Internal crates -curl_interface = { path = "../crates/curl_interface" } -json_parser_interface = { path = "../crates/json_parser_interface" } -platform_interface = { path = "../crates/platform_interface" } -utils = { path = "../crates/utils" } -chrono = "0.4" diff --git a/crates/curl-interface/Cargo.toml b/crates/curl-interface/Cargo.toml new file mode 100644 index 0000000..e057a09 --- /dev/null +++ b/crates/curl-interface/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "curl-interface" +version = "0.1.0" +edition = "2021" +description = "Curl interface" +repository = "https://github.com/rdkcentral/crashupload/tree/feature-sample-rust/crates/curl-interface" +homepage = "https://github.com/rdkcentral/crashupload/tree/feature-sample-rust/crates/curl-interface" + +[dependencies] +# Uncomment to Link this library dynamically +#crate_type = ["dynlib"] diff --git a/crates/curl_interface/src/lib.rs b/crates/curl-interface/src/lib.rs similarity index 100% rename from crates/curl_interface/src/lib.rs rename to crates/curl-interface/src/lib.rs diff --git a/crates/curl_interface/Cargo.toml b/crates/curl_interface/Cargo.toml deleted file mode 100644 index 9c6c67b..0000000 --- a/crates/curl_interface/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "curl_interface" -version = "0.1.0" -edition = "2024" - -[dependencies] -# Uncomment to Link this library dynamically -#crate_type = ["dynlib"] diff --git a/crates/json-parser-interface/Cargo.toml b/crates/json-parser-interface/Cargo.toml new file mode 100644 index 0000000..4a6ba05 --- /dev/null +++ b/crates/json-parser-interface/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "json_parser-interface" +version = "0.1.0" +edition = "2021" +description = "JSON Parsing interface" +repository = "https://github.com/rdkcentral/crashupload/tree/feature-sample-rust/crates/json-parser-interface" +homepage = "https://github.com/rdkcentral/crashupload/tree/feature-sample-rust/crates/json-parser-interface" + +[dependencies] +# Uncomment to Link this library dynamically +#crate_type = ["dynlib"] \ No newline at end of file diff --git a/crates/json_parser_interface/src/lib.rs b/crates/json-parser-interface/src/lib.rs similarity index 100% rename from crates/json_parser_interface/src/lib.rs rename to crates/json-parser-interface/src/lib.rs diff --git a/crates/json_parser_interface/Cargo.toml b/crates/json_parser_interface/Cargo.toml deleted file mode 100644 index e8c0d79..0000000 --- a/crates/json_parser_interface/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "json_parser_interface" -version = "0.1.0" -edition = "2024" - -[dependencies] -# Uncomment to Link this library dynamically -#crate_type = ["dynlib"] \ No newline at end of file diff --git a/crates/platform-interface/Cargo.toml b/crates/platform-interface/Cargo.toml new file mode 100644 index 0000000..d4facec --- /dev/null +++ b/crates/platform-interface/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "platform-interface" +version = "0.1.0" +edition = "2021" +description = "Platform abstraction interface" +repository = "https://github.com/rdkcentral/crashupload/tree/feature-sample-rust/crates/platform-interface" +homepage = "https://github.com/rdkcentral/crashupload/tree/feature-sample-rust/crates/platform-interface" + +[dependencies] +# Uncomment to Link this library dynamically +#crate_type = ["dynlib"] \ No newline at end of file diff --git a/crates/platform_interface/src/lib.rs b/crates/platform-interface/src/lib.rs similarity index 100% rename from crates/platform_interface/src/lib.rs rename to crates/platform-interface/src/lib.rs diff --git a/crates/platform_interface/src/rfc_api.rs b/crates/platform-interface/src/rfc_api.rs similarity index 100% rename from crates/platform_interface/src/rfc_api.rs rename to crates/platform-interface/src/rfc_api.rs diff --git a/crates/platform_interface/src/t2_api.rs b/crates/platform-interface/src/t2_api.rs similarity index 100% rename from crates/platform_interface/src/t2_api.rs rename to crates/platform-interface/src/t2_api.rs diff --git a/crates/platform_interface/Cargo.toml b/crates/platform_interface/Cargo.toml deleted file mode 100644 index c8c124b..0000000 --- a/crates/platform_interface/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "platform_interface" -version = "0.1.0" -edition = "2024" - -[dependencies] -# Uncomment to Link this library dynamically -#crate_type = ["dynlib"] \ No newline at end of file diff --git a/crates/utils/Cargo.toml b/crates/utils/Cargo.toml index 92c8c32..ca124e4 100644 --- a/crates/utils/Cargo.toml +++ b/crates/utils/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "utils" version = "0.1.0" -edition = "2024" +edition = "2021" +description = "Common Utility crates" +repository = "https://github.com/rdkcentral/crashupload/tree/feature-sample-rust/crates/utils" +homepage = "https://github.com/rdkcentral/crashupload/tree/feature-sample-rust/crates/utils" [dependencies] # Uncomment to Link this library dynamically diff --git a/test/crash-upload-test/Cargo.toml b/test/crash-upload-test/Cargo.toml new file mode 100644 index 0000000..82a0280 --- /dev/null +++ b/test/crash-upload-test/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "crash-upload-test" +version = "0.1.0" +edition = "2021" + +[dependencies] +crash-upload = {path = "../../crash-upload"} diff --git a/test/crash_upload_test/src/main.rs b/test/crash-upload-test/src/main.rs similarity index 100% rename from test/crash_upload_test/src/main.rs rename to test/crash-upload-test/src/main.rs diff --git a/test/crash_upload_test/Cargo.toml b/test/crash_upload_test/Cargo.toml deleted file mode 100644 index 2bbf5e2..0000000 --- a/test/crash_upload_test/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "crash_upload_test" -version = "0.1.0" -edition = "2024" - -[dependencies] -crash_upload = {path = "../../crash_upload"} diff --git a/test/curl-interface-test/Cargo.toml b/test/curl-interface-test/Cargo.toml new file mode 100644 index 0000000..3e80c55 --- /dev/null +++ b/test/curl-interface-test/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "curl-interface-test" +version = "0.1.0" +edition = "2021" + +[dependencies] +curl-interface = {path = "../../crates/curl-interface"} \ No newline at end of file diff --git a/test/curl_interface_test/src/main.rs b/test/curl-interface-test/src/main.rs similarity index 100% rename from test/curl_interface_test/src/main.rs rename to test/curl-interface-test/src/main.rs diff --git a/test/curl_interface_test/Cargo.toml b/test/curl_interface_test/Cargo.toml deleted file mode 100644 index af42f66..0000000 --- a/test/curl_interface_test/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "curl_interface_test" -version = "0.1.0" -edition = "2024" - -[dependencies] -curl_interface = {path = "../../crates/curl_interface"} \ No newline at end of file diff --git a/test/json-parser-interface-test/Cargo.toml b/test/json-parser-interface-test/Cargo.toml new file mode 100644 index 0000000..84b42b2 --- /dev/null +++ b/test/json-parser-interface-test/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "json-parser-interface-test" +version = "0.1.0" +edition = "2021" + +[dependencies] +json-parser-interface = {path = "../../crates/json-parser-interface"} \ No newline at end of file diff --git a/test/json_parser_interface_test/src/main.rs b/test/json-parser-interface-test/src/main.rs similarity index 100% rename from test/json_parser_interface_test/src/main.rs rename to test/json-parser-interface-test/src/main.rs diff --git a/test/json_parser_interface_test/Cargo.toml b/test/json_parser_interface_test/Cargo.toml deleted file mode 100644 index 885736b..0000000 --- a/test/json_parser_interface_test/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "json_parser_interface_test" -version = "0.1.0" -edition = "2024" - -[dependencies] -json_parser_interface = {path = "../../crates/json_parser_interface"} \ No newline at end of file diff --git a/test/platform-interface-test/Cargo.toml b/test/platform-interface-test/Cargo.toml new file mode 100644 index 0000000..ef1bd9d --- /dev/null +++ b/test/platform-interface-test/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "platform-interface-test" +version = "0.1.0" +edition = "2021" + +[dependencies] +platform-interface = {path = "../../crates/platform-interface"} \ No newline at end of file diff --git a/test/platform_interface_test/src/main.rs b/test/platform-interface-test/src/main.rs similarity index 100% rename from test/platform_interface_test/src/main.rs rename to test/platform-interface-test/src/main.rs diff --git a/test/platform_interface_test/Cargo.toml b/test/platform_interface_test/Cargo.toml deleted file mode 100644 index 3f3007a..0000000 --- a/test/platform_interface_test/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "platform_interface_test" -version = "0.1.0" -edition = "2024" - -[dependencies] -platform_interface = {path = "../../crates/platform_interface"} \ No newline at end of file diff --git a/test/utils_test/Cargo.toml b/test/utils-test/Cargo.toml similarity index 68% rename from test/utils_test/Cargo.toml rename to test/utils-test/Cargo.toml index 610aadb..98bd8a7 100644 --- a/test/utils_test/Cargo.toml +++ b/test/utils-test/Cargo.toml @@ -1,7 +1,7 @@ [package] -name = "utils_test" +name = "utils-test" version = "0.1.0" -edition = "2024" +edition = "2021" [dependencies] utils = {path = "../../crates/utils"} diff --git a/test/utils_test/src/main.rs b/test/utils-test/src/main.rs similarity index 100% rename from test/utils_test/src/main.rs rename to test/utils-test/src/main.rs From 092c44d8a24a0002a91510997d90c987c1238e7f Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Thu, 12 Jun 2025 18:46:24 +0530 Subject: [PATCH 25/66] Update Cargo.toml --- crash-upload/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash-upload/Cargo.toml b/crash-upload/Cargo.toml index d2a123e..c984544 100644 --- a/crash-upload/Cargo.toml +++ b/crash-upload/Cargo.toml @@ -9,7 +9,7 @@ homepage = "https://github.com/rdkcentral/crashupload/tree/feature-sample-rust/c [dependencies] # Internal crates curl-interface = { path = "../crates/curl-interface" } -json_parser-interface = { path = "../crates/json-parser-interface" } +json-parser-interface = { path = "../crates/json-parser-interface" } platform-interface = { path = "../crates/platform-interface" } utils = { path = "../crates/utils" } chrono = "0.4" From 4223e196febaf94122965b2a2e6aea506957837d Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Thu, 12 Jun 2025 18:57:58 +0530 Subject: [PATCH 26/66] Update Cargo.toml --- crates/json-parser-interface/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/json-parser-interface/Cargo.toml b/crates/json-parser-interface/Cargo.toml index 4a6ba05..d87de3f 100644 --- a/crates/json-parser-interface/Cargo.toml +++ b/crates/json-parser-interface/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "json_parser-interface" +name = "json-parser-interface" version = "0.1.0" edition = "2021" description = "JSON Parsing interface" @@ -8,4 +8,4 @@ homepage = "https://github.com/rdkcentral/crashupload/tree/feature-sample-rust/c [dependencies] # Uncomment to Link this library dynamically -#crate_type = ["dynlib"] \ No newline at end of file +#crate_type = ["dynlib"] From d27fa0d6779c3dc72e3bac82ce649387453d8dc1 Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Thu, 12 Jun 2025 13:40:17 +0000 Subject: [PATCH 27/66] Fix Build Errors and Warnings in Rust Code Signed-off-by: Gomathi Shankar --- crash-upload/src/crashupload_utils.rs | 2 +- crates/platform-interface/src/rfc_api.rs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index 67e196e..6b43c2a 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -1334,7 +1334,7 @@ fn compress_files(tgz_file: &str, main_files: &[&str], extra_files: &[&str]) -> if status.success() { Ok(()) } else { - Err(io::Error::other("Compression Failed")) + Err(io::Error::new(io::ErrorKind::Other, "Compression Failed")) } } diff --git a/crates/platform-interface/src/rfc_api.rs b/crates/platform-interface/src/rfc_api.rs index f12233d..a936e17 100644 --- a/crates/platform-interface/src/rfc_api.rs +++ b/crates/platform-interface/src/rfc_api.rs @@ -100,8 +100,6 @@ pub fn get_rfc_param>(rfc: R, res: &mut String) -> bool { pub fn dmcli_get(param: &str, result: &mut String) { - use std::process::Command; - let output = Command::new("dmcli") .args(["eRT", "getv", param]) .output(); From e5e6dcc682c82d991a1868cd1889e5937ce3c9f0 Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Mon, 16 Jun 2025 11:41:27 +0000 Subject: [PATCH 28/66] Update crashupload_utils.rs and main.rs Signed-off-by: Gomathi Shankar --- crash-upload/src/crashupload_utils.rs | 174 +++++++++++++++++--------- crash-upload/src/main.rs | 54 ++++---- 2 files changed, 136 insertions(+), 92 deletions(-) diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index 6b43c2a..fee33c1 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -12,6 +12,52 @@ use crate::constants::*; use platform_interface::*; use utils::*; +#[inline] +fn is_minidump_file(name: &str) -> bool { + // *.dmp* matches any file with ".dmp" after the first character + name.find(".dmp").is_some() +} + +#[inline] +fn is_minidump_tarball(name: &str) -> bool { + // *.dmp.tgz matches files ending with ".dmp.tgz" + name.ends_with(".dmp.tgz") +} + +#[inline] +fn is_coredump_file(name: &str) -> bool { + // *core.prog*.gz* matches files containing "core.prog" and ".gz" (in that order) + if let Some(core_idx) = name.find("core.prog") { + if let Some(gz_idx) = name[core_idx..].find(".gz") { + return true; + } + } + false +} + +#[inline] +fn is_coredump_tarball(name: &str) -> bool { + // *.core.tgz matches files ending with ".core.tgz" + name.ends_with(".core.tgz") +} + +pub fn is_core_pattern_file(name: &str) -> bool { + if let Some(core_idx) = name.find("_core") { + // There must be a '.' after the '_core' + let after_core = &name[core_idx + 5..]; // 5 = len("_core") + after_core.contains('.') + } else { + false + } +} + +fn is_dir_empty_or_unreadable>(dir: P) -> bool { + match fs::read_dir(dir) { + Ok(mut entries) => entries.next().is_none(), + Err(_) => true, // treat unreadable as empty + } +} + // #[cfg(feature = "shared_api")] // pub use crate::upload_to_s3::upload_to_s3; @@ -75,24 +121,6 @@ pub fn set_device_data(device_data: &mut DeviceData) { device_data.set_portal_url(portal_url); } -/// Determines if the crash upload process should exit early due to no dumps being present. -/// -/// This function checks for the existence of minidump and coredump files in their respective -/// directories, mimicking the shell logic: -/// `if [ ! -e $MINIDUMPS_PATH/*.dmp* -a ! -e $CORE_PATH/*_core*.* ]; then exit 0; fi` -/// -/// # Arguments -/// * `minidumps_path` - Path to the minidumps directory. -/// * `core_path` - Path to the coredumps directory. -/// -/// # Returns -/// * `true` if neither minidumps nor coredumps exist (should exit), `false` otherwise. -pub fn should_exit_crash_upload(minidumps_path: &str, core_path: &str) -> bool { - let minidumps_exists = check_dumps_exist(minidumps_path, ".dmp"); // for minidumps - let core_exists = check_dumps_exist(core_path, "_core"); // for core - !(minidumps_exists || core_exists) -} - /// Checks if any dump files matching a pattern exist in a directory. /// /// This function scans the given directory for files matching the provided wildcard pattern, @@ -104,20 +132,29 @@ pub fn should_exit_crash_upload(minidumps_path: &str, core_path: &str) -> bool { /// /// # Returns /// * `true` if at least one matching file exists, `false` otherwise. -#[inline] -fn check_dumps_exist(dir: &str, wildcard: &str) -> bool { - let path = std::path::Path::new(dir); - if let Ok(entries) = std::fs::read_dir(path) { - for entry in entries.flatten() { - if let Ok(file_name) = entry.file_name().into_string() { - // Only check files, not directories - if entry.path().is_file() && file_name.contains(wildcard) { - return true; - } - } - } - } - false +pub fn check_dumps_exist(minidumps_path: &str, core_path: &str) -> bool { + let minidumps_dir = std::path::Path::new(minidumps_path); + let core_dir = std::path::Path::new(core_path); + + let minidump_exists = minidumps_dir.is_dir() && std::fs::read_dir(minidumps_dir) + .map(|iter| iter.flatten().any(|entry| { + let name = entry.file_name(); + let name = name.to_string_lossy(); + // Faithful to *.dmp* shell pattern + is_minidump_file(&name) + })) + .unwrap_or(false); + + let core_exists = core_dir.is_dir() && std::fs::read_dir(core_dir) + .map(|iter| iter.flatten().any(|entry| { + let name = entry.file_name(); + let name = name.to_string_lossy(); + // Faithful to *_core*.* shell pattern + is_core_pattern_file(&name) + })) + .unwrap_or(false); + + minidump_exists || core_exists } /// Determines and sets secure dump paths and flags based on SecureDump enablement. @@ -427,7 +464,7 @@ pub fn set_recovery_time() { /// # Returns /// * `true` if the upload rate limit is reached, `false` otherwise. pub fn is_upload_limit_reached(ts_file: &str) -> bool { - create_lock_or_wait(ts_file); // TODO + create_lock_or_wait(ts_file); const LIMIT_SECONDS: u64 = 600; let path = Path::new(ts_file); @@ -435,14 +472,14 @@ pub fn is_upload_limit_reached(ts_file: &str) -> bool { if OpenOptions::new().create(true).append(true).open(path).is_err() { println!("Failed to create or open {}", ts_file); - remove_lock(ts_file); // TODO + remove_lock(ts_file); return false; } let file = match File::open(path) { Ok(f) => f, Err(_) => { - remove_lock(ts_file); // TODO + remove_lock(ts_file); return false; } }; @@ -455,7 +492,7 @@ pub fn is_upload_limit_reached(ts_file: &str) -> bool { .collect(); if lines.len() < 10 { - remove_lock(ts_file); // TODO + remove_lock(ts_file); return false; } @@ -469,7 +506,7 @@ pub fn is_upload_limit_reached(ts_file: &str) -> bool { if limit_reached { println!("Not uploading the dump. Too many dumps."); } - remove_lock(ts_file); // TODO + remove_lock(ts_file); limit_reached } @@ -544,33 +581,42 @@ pub fn should_process_dump(dump_name: &str, build_type: &str, file_name: &str) - } } +fn matches_pattern(name: &str, pattern: &str) -> bool { + match pattern { + "*.dmp*" => name.contains(".dmp"), + "*.dmp.tgz" => name.ends_with(".dmp.tgz"), + "*core.prog*.gz*" => { + if let Some(core_idx) = name.find("core.prog") { + name[core_idx + "core.prog".len()..].contains(".gz") + } else { + false + } + }, + "*.core.tgz" => name.ends_with(".core.tgz"), + _ => false, + } +} + /// Counts the number of dump files in a directory matching a given extension substring. /// /// # Arguments /// * `dir` - Directory path to search. -/// * `pattern` - Substring to match in file names (e.g., ".dmp" or "core"). +/// * `extn` - Substring to match in file names (e.g., ".dmp" or "core"). /// /// # Returns /// * `Ok(count)` with the number of matching files, or an error if the directory can't be read. -pub fn get_file_count(dir: &str, pattern: &str, is_pattern: bool) -> std::io::Result { - let dir_path = Path::new(dir); - if !dir_path.exists() || !dir_path.is_dir() { +pub fn get_file_count(dir: &str, pattern: &str) -> std::io::Result { + let dir_path = std::path::Path::new(dir); + if !dir_path.is_dir() { return Ok(0); } let mut count = 0; - for entry in fs::read_dir(dir_path)? { + for entry in std::fs::read_dir(dir_path)? { let entry = entry?; - let file_path = entry.path(); - if file_path.is_file() { - if let Some(name) = file_path.file_name().and_then(|n| n.to_str()) { - if is_pattern { - if name.contains(pattern) { - count += 1; - } - } else if name == pattern { - count += 1; - } - } + let name = entry.file_name(); + let name = name.to_string_lossy(); + if matches_pattern(&name, pattern) { + count += 1; } } Ok(count) @@ -1201,10 +1247,10 @@ pub fn get_privacy_control_mode() -> Option { /// - Modifies files in the working directory (renames, compresses, deletes). /// - May create or remove log files and temporary directories. /// - Calls `handle_tarballs` for tarball upload/save logic. -pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: &str, no_network: bool) { // TODO: Review and add code changes faithful to script +pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: &str, no_network: bool) { utils::flush_logger(); - let files = match find_dump_files(dump_paths.get_working_dir(), dump_paths.get_dumps_extn()) { + let files = match find_dump_files(dump_paths.get_working_dir(), dump_paths.get_dumps_extn(), true) { Ok(f) => f, Err(e) => { println!("Error finding dump files: {}", e); @@ -1394,14 +1440,18 @@ fn remove_logs(working_dir: &str) -> io::Result<()> { /// /// # Returns /// * `Ok(Vec)` with matching files, or error. -#[inline] -pub fn find_dump_files(working_dir: &str, dumps_extn: &str) -> io::Result> { +pub fn find_dump_files(dir: &str, pattern: &str) -> std::io::Result> { + let dir_path = std::path::Path::new(dir); let mut files = Vec::new(); - for entry in fs::read_dir(working_dir)? { + if !dir_path.is_dir() { + return Ok(files); + } + for entry in std::fs::read_dir(dir_path)? { let entry = entry?; - let path = entry.path(); - if path.is_file() && path.file_name().map(|n| n.to_string_lossy().contains(dumps_extn)).unwrap_or(false) { - files.push(path); + let name = entry.file_name(); + let name = name.to_string_lossy(); + if matches_pattern(&name, pattern) { + files.push(entry.path()); } } Ok(files) @@ -1418,7 +1468,7 @@ fn handle_tarballs(device_data: &DeviceData, dump_paths: &DumpPaths, no_network: return; } - let tarballs = match find_dump_files(dump_paths.get_working_dir(), dump_paths.get_tar_extn()) { + let tarballs = match find_dump_files(dump_paths.get_working_dir(), dump_paths.get_tar_extn(), true) { Ok(t) => t, Err(e) => { println!("Error finding tarballs: {}", e); diff --git a/crash-upload/src/main.rs b/crash-upload/src/main.rs index 59cfa51..ca53b79 100644 --- a/crash-upload/src/main.rs +++ b/crash-upload/src/main.rs @@ -23,7 +23,7 @@ fn main() { let args: Vec = env::args().collect(); if args.len() != 4 { println!("Usage: {} ", args[0]); - process::exit(1); + std::process::exit(1); } let dump_flag = args[1].parse::().expect("Dump flag must be a number"); @@ -48,9 +48,9 @@ fn main() { } // Exit early if no dumps exist - if crashupload_utils::should_exit_crash_upload(dump_paths.get_minidumps_path(), dump_paths.get_core_path()) { - println!("Crash upload process is already running. Exiting..."); - exit(0); + if !crashupload_utils::check_dumps_exist(dump_paths.get_minidumps_path(), dump_paths.get_core_path()) { + println!("No dumps found. Exiting..."); + std::process::exit(0); } // Set secure dump status if needed @@ -66,7 +66,7 @@ fn main() { let core_path = dump_paths.get_core_path().to_string(); dump_paths.set_working_dir(&core_path); dump_paths.set_dumps_extn("*core.prog*.gz*"); - dump_paths.set_tar_extn(".core.tgz"); + dump_paths.set_tar_extn("*.core.tgz"); dump_paths.set_lock_dir_prefix("/tmp/.uploadCoredumps"); dump_paths.set_crash_portal_path("/opt/crashportal_uploads/coredumps/"); } else { @@ -84,9 +84,9 @@ fn main() { // Locking logic if wait_for_lock == "wait_for_lock" { - create_lock_or_wait(dump_paths.get_lock_dir_prefix()); + crashupload_utils::create_lock_or_wait(dump_paths.get_lock_dir_prefix()); } else { - create_lock_or_exit(dump_paths.get_lock_dir_prefix(), device_data.get_is_t2_enabled()); + crashupload_utils::create_lock_or_exit(dump_paths.get_lock_dir_prefix(), device_data.get_is_t2_enabled()); } // Defer upload if device just booted (hybrid/mediaclient) @@ -106,28 +106,19 @@ fn main() { thread::sleep(Duration::from_secs(sleep_time)); if Path::new(constants::CRASH_UPLOAD_REBOOT_FLAG).exists() { println!("Process crashed exiting from the Deferring reboot"); - finalize(&dump_paths); // TODO: Should we finalize & exit? - exit(0); + finalize(&dump_paths); + std::process::exit(0); } } } // Check if working directory is empty let w_dir = dump_paths.get_working_dir(); - match fs::read_dir(w_dir) { - Ok(mut entries) => { - if entries.next().is_none() { - println!("Working dir is empty: {}", w_dir); - finalize(&dump_paths); // TODO: Should we finalize & exit? - exit(0); - } - } - Err(_) => { - println!("working dir is empty : {}", w_dir); - finalize(&dump_paths); // TODO: Should we finalize & exit? - exit(0); - } - }; + if is_dir_empty_or_unreadable(w_dir) { + println!("Working directory is empty or unreadable: {}", w_dir); + crashupload_utils::finalize(&dump_paths); + std::process::exit(0); // or exit(1) if you want to signal error + } // Network availability check let mut counter = 1; @@ -186,22 +177,25 @@ fn main() { // Early exit if box is rebooting if is_box_rebooting(device_data.get_is_t2_enabled()) { - finalize(&dump_paths); // TODO: Should we finalize & exit? - exit(0); + crashupload_utils::finalize(&dump_paths); + std::process::exit(0); } // Print device MAC address println!("Mac Address is {}", device_data.get_mac_addr()); // Count dumps using utility function - let dump_count = match get_file_count(dump_paths.get_working_dir(), dump_paths.get_dumps_extn(), true) { + let dump_count = match crashupload_utils::get_file_count( + dump_paths.get_working_dir(), + dump_paths.get_dumps_extn(), + ) { Ok(dump_cnt) => dump_cnt, Err(_) => 0, }; if dump_count == 0 { println!("No {} for uploading exist", dump_paths.get_dump_name()); - finalize(&dump_paths); // TODO: Should we finalize & exit? - exit(0); + crashupload_utils::finalize(&dump_paths); + std::process::exit(0); } // Cleanup old dumps using utility function @@ -217,7 +211,7 @@ fn main() { // Final check: working directory must be a directory if !Path::new(w_dir).is_dir() { - exit(1); + std::process::exit(1); } // Main processing loop (up to 3 attempts, as in shell script) @@ -229,7 +223,7 @@ fn main() { if files.is_empty() { break; } - process_dumps(&device_data, &dump_paths, &crash_ts, no_network); + crashupload_utils::process_dumps(&device_data, &dump_paths, &crash_ts, no_network); } finalize(&dump_paths); From 378e3911b204ebfd3744b8ecd09054022ee4e484 Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Mon, 16 Jun 2025 11:55:17 +0000 Subject: [PATCH 29/66] Update rs files Signed-off-by: Gomathi Shankar --- crash-upload/src/crashupload_utils.rs | 8 ++++---- crash-upload/src/main.rs | 5 ++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index fee33c1..03229fb 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -51,7 +51,7 @@ pub fn is_core_pattern_file(name: &str) -> bool { } } -fn is_dir_empty_or_unreadable>(dir: P) -> bool { +pub fn is_dir_empty_or_unreadable>(dir: P) -> bool { match fs::read_dir(dir) { Ok(mut entries) => entries.next().is_none(), Err(_) => true, // treat unreadable as empty @@ -760,7 +760,7 @@ pub fn save_dump(minidumps_path: &str, s3_filename: &str, new_name: Option<&str> } let dump_extn = "dmp.tgz"; - let mut count = get_file_count(minidumps_path, dump_extn, true).unwrap_or(0); + let mut count = get_file_count(minidumps_path, dump_extn).unwrap_or(0); while count > 5 { match find_oldest_dump(minidumps_path) { @@ -1250,7 +1250,7 @@ pub fn get_privacy_control_mode() -> Option { pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: &str, no_network: bool) { utils::flush_logger(); - let files = match find_dump_files(dump_paths.get_working_dir(), dump_paths.get_dumps_extn(), true) { + let files = match find_dump_files(dump_paths.get_working_dir(), dump_paths.get_dumps_extn()) { Ok(f) => f, Err(e) => { println!("Error finding dump files: {}", e); @@ -1468,7 +1468,7 @@ fn handle_tarballs(device_data: &DeviceData, dump_paths: &DumpPaths, no_network: return; } - let tarballs = match find_dump_files(dump_paths.get_working_dir(), dump_paths.get_tar_extn(), true) { + let tarballs = match find_dump_files(dump_paths.get_working_dir(), dump_paths.get_tar_extn()) { Ok(t) => t, Err(e) => { println!("Error finding tarballs: {}", e); diff --git a/crash-upload/src/main.rs b/crash-upload/src/main.rs index ca53b79..b5df81d 100644 --- a/crash-upload/src/main.rs +++ b/crash-upload/src/main.rs @@ -1,8 +1,7 @@ -use std::process::exit; // standard library imports use std::path::Path; use std::time::Duration; -use std::{env, fs, process, thread}; +use std::{env, fs, thread}; // external crate imports use chrono::Local; @@ -114,7 +113,7 @@ fn main() { // Check if working directory is empty let w_dir = dump_paths.get_working_dir(); - if is_dir_empty_or_unreadable(w_dir) { + if crashupload_utils::is_dir_empty_or_unreadable(w_dir) { println!("Working directory is empty or unreadable: {}", w_dir); crashupload_utils::finalize(&dump_paths); std::process::exit(0); // or exit(1) if you want to signal error From b9fcf6a20eb1f7142045f1264d323b3ebe6d47e8 Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Mon, 16 Jun 2025 13:04:18 +0000 Subject: [PATCH 30/66] Update crashupload_utils.rs Signed-off-by: Gomathi Shankar --- crash-upload/src/crashupload_utils.rs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index 03229fb..8c7ac0f 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -58,6 +58,18 @@ pub fn is_dir_empty_or_unreadable>(dir: P) -> bool { } } +pub fn safe_rename, D: AsRef>(src: S, dst: D) -> io::Result<()> { + match fs::rename(&src, &dst) { + Ok(_) => Ok(()), + Err(e) if e.raw_os_error() == Some(18) || e.kind() == io::ErrorKind::CrossDeviceLink => { + fs::copy(&src, &dst)?; + fs::remove_file(&src)?; + Ok(()) + } + Err(e) => Err(e), + } +} + // #[cfg(feature = "shared_api")] // pub use crate::upload_to_s3::upload_to_s3; @@ -720,7 +732,7 @@ pub fn mark_as_crash_loop_and_upload(tgz_file: &str) { // portal_url: &str, cras let tgz_path = Path::new(tgz_file); let new_tgz_name = tgz_path.with_extension("crashloop.dmp.tgz"); println!("Renaming {} to {}", tgz_path.display(), new_tgz_name.display()); - if let Err(e) = fs::rename(tgz_path, &new_tgz_name) { + if let Err(e) = safe_rename(tgz_path, &new_tgz_name) { println!("Failed to rename crashloop tarball: {}", e); } } @@ -754,7 +766,7 @@ pub fn save_dump(minidumps_path: &str, s3_filename: &str, new_name: Option<&str> println!("Saving dump with original name to retain container info"); let original_path = format!("{}/{}", minidumps_path, s3_filename); let new_path = format!("{}/{}", minidumps_path, new_name); - if let Err(e) = fs::rename(&original_path, &new_path) { + if let Err(e) = safe_rename(&original_path, &new_path) { println!("Failed to rename file: {}", e); } } @@ -1296,7 +1308,7 @@ pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: let dump_file_name = dump_file_name.replace("<#=#>", "_"); - if let Err(e) = fs::rename(&sanitized, &dump_file_name) { + if let Err(e) = safe_rename(&sanitized, &dump_file_name) { println!("Failed to rename {:?} to {}: {}", sanitized, dump_file_name, e); continue; } @@ -1402,7 +1414,7 @@ fn sanitize_and_rename(file: &Path) -> io::Result { let orig = file.to_string_lossy(); let sanitized = sanitize(&orig); if sanitized != orig { - fs::rename(&*orig, &sanitized)?; + safe_rename(&*orig, &sanitized)?; Ok(PathBuf::from(sanitized)) } else { Ok(file.to_path_buf()) @@ -1573,7 +1585,7 @@ fn rename_tarball_for_s3(tarball: &Path, sanitized_name: &str) -> std::io::Resul let parent = tarball.parent().unwrap_or_else(|| Path::new("")); let orig_path = parent.join(tarball.file_name().unwrap()); let new_path = parent.join(sanitized_name); - fs::rename(&orig_path, &new_path) + safe_rename(&orig_path, &new_path) } /// Uploads a tarball to S3, retrying up to 3 times. From d0b7f4e5ff0c5e7b1c84c8ade80fe7196933bb58 Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Mon, 16 Jun 2025 13:12:01 +0000 Subject: [PATCH 31/66] Update Error code for cross device link rename failure Signed-off-by: Gomathi Shankar --- crash-upload/src/crashupload_utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index 8c7ac0f..95a6890 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -61,7 +61,7 @@ pub fn is_dir_empty_or_unreadable>(dir: P) -> bool { pub fn safe_rename, D: AsRef>(src: S, dst: D) -> io::Result<()> { match fs::rename(&src, &dst) { Ok(_) => Ok(()), - Err(e) if e.raw_os_error() == Some(18) || e.kind() == io::ErrorKind::CrossDeviceLink => { + Err(e) if e.raw_os_error() == Some(18) => { fs::copy(&src, &dst)?; fs::remove_file(&src)?; Ok(()) From 10e27e46955de51c11e1fd5140a0c306cbd55c25 Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Tue, 17 Jun 2025 09:39:30 +0530 Subject: [PATCH 32/66] Update safe_rename and add utils Signed-off-by: gomathishankar37 --- crash-upload/src/crashupload_utils.rs | 52 ++++++++++++++++++--------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index 95a6890..03906f4 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -59,17 +59,34 @@ pub fn is_dir_empty_or_unreadable>(dir: P) -> bool { } pub fn safe_rename, D: AsRef>(src: S, dst: D) -> io::Result<()> { - match fs::rename(&src, &dst) { + let src_path = src.as_ref(); + let dst_path = { + let dst_ref = dst.as_ref(); + if dst_ref.is_absolute() { + dst_ref.to_path_buf() + } else { + src_path.parent().unwrap_or_else(|| Path::new("")).join(dst_ref) + } + }; + match fs::rename(&src_path, &dst_path) { Ok(_) => Ok(()), Err(e) if e.raw_os_error() == Some(18) => { - fs::copy(&src, &dst)?; - fs::remove_file(&src)?; + fs::copy(&src_path, &dst_path)?; + fs::remove_file(&src_path)?; Ok(()) } Err(e) => Err(e), } } +#[inline] +pub fn basename>(path: P) -> &str { + path.as_ref() + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or_else(|| path.as_ref().to_str().unwrap_or("")) +} + // #[cfg(feature = "shared_api")] // pub use crate::upload_to_s3::upload_to_s3; @@ -656,7 +673,7 @@ pub fn remove_pending_dumps(path: &str, extn: &str) -> io::Result<()> { if file_path.is_file() { let file_name = file_path.file_name().and_then(|n| n.to_str()).unwrap_or(""); if file_name.ends_with(extn) || file_name.ends_with(".tgz") { - println!("Removing {} because upload limit has reached or build is blacklisted or TelemetryOptOut is set", file_path.display()); + println!("Removing {} because upload limit has reached or build is blacklisted or TelemetryOptOut is set", basename(&file_path)); let _ = fs::remove_file(&file_path); } } @@ -685,8 +702,8 @@ pub fn process_crash_t2_info(file_path: &str, is_t2_enabled: bool) { if let Some(pos) = file_name_str.find("_mod_") { file_name_str = file_name_str.split_off(pos + "_mod_".len()); } - println!("Original Filename: {}", file.display()); - println!("Removing the meta information New Filename: {}", file_name_str); + println!("Original Filename: {}", basename(file)); + println!("Removing the meta information New Filename: {}", basename(&file_name_str)); println!("This could be a retry or crash from previous boot; the appname can be truncated"); t2_count_notify("SYS_INFO_TGZDUMP", Some("1")); } @@ -763,7 +780,7 @@ pub fn find_oldest_dump(dir: &str) -> io::Result> { /// * `new_name` - Optional new name to rename to (to retain container info). pub fn save_dump(minidumps_path: &str, s3_filename: &str, new_name: Option<&str>) { if let Some(new_name) = new_name { - println!("Saving dump with original name to retain container info"); + println!("Saving dump with original name to retain container info: {}", basename(new_name)); let original_path = format!("{}/{}", minidumps_path, s3_filename); let new_path = format!("{}/{}", minidumps_path, new_name); if let Err(e) = safe_rename(&original_path, &new_path) { @@ -865,13 +882,14 @@ pub fn get_last_modified_time_of_file(path: &str) -> Option { /// # Returns /// * `Ok(())` on success, or an error if file operations fail. pub fn get_crashed_log_file(file_path: &str, is_t2_enabled: bool) -> io::Result<()> { - let file = file_path; + let basename = basename(file_path); - let process_name = file + let process_name = basename .rsplitn(2, '_') .nth(1) - .unwrap_or(file) + .unwrap_or(basename) .trim_start_matches("./"); + println!("Process crashed = {}", process_name); if is_t2_enabled { @@ -880,7 +898,7 @@ pub fn get_crashed_log_file(file_path: &str, is_t2_enabled: bool) -> io::Result< t2_count_notify("SYST_ERR_ProcessCrash", Some("1")); } - let app_name = file + let app_name = basename .split('_') .nth(1) .and_then(|s| s.split('-').next()) @@ -899,7 +917,7 @@ pub fn get_crashed_log_file(file_path: &str, is_t2_enabled: bool) -> io::Result< } println!("Crashed process log file(s): {}", log_files); if !app_name.is_empty() { - println!("Appname, Process_Crashed = {} {}", app_name, process_name); + println!("Appname, Process_Crashed = {}, {}", app_name, process_name); } // Write each log file to LOG_FILES let mut output = OpenOptions::new() @@ -1285,7 +1303,7 @@ pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: if file.is_file() { if is_tarball(&sanitized) { - println!("Skip archiving {:?} as it is a tarball already.", sanitized); + println!("Skip archiving {} as it is a tarball already.", basename(&sanitized)); continue; } @@ -1309,7 +1327,7 @@ pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: let dump_file_name = dump_file_name.replace("<#=#>", "_"); if let Err(e) = safe_rename(&sanitized, &dump_file_name) { - println!("Failed to rename {:?} to {}: {}", sanitized, dump_file_name, e); + println!("Failed to rename {} to {}: {}", basename(&sanitized), basename(&dump_file_name), e); continue; } @@ -1341,7 +1359,7 @@ pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: let tar_result = compress_files(&tgz_file, &[&dump_file_name], &logfiles_refs); if tar_result.is_ok() { - println!("Success Compressing the files, {} {} {} {}", tgz_file, dump_file_name, VERSION_FILE, CORE_LOG); + println!("Success Compressing the files, {} {} {} {}", basename(&tgz_file), basename(&dump_file_name), basename(VERSION_FILE), basename(CORE_LOG)); } else { println!("Compression failed, will retry after copying logs to /tmp"); @@ -1349,7 +1367,7 @@ pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: let out_files_refs: Vec<&str> = out_files.iter().map(|s| s.as_str()).collect(); let retry_tar_result = compress_files(&tgz_file, &[&dump_file_name], &out_files_refs); if retry_tar_result.is_ok() { - println!("Success Compressing the files, {} {}", tgz_file, dump_file_name); + println!("Success Compressing the files, {} {}", basename(&tgz_file), basename(&dump_file_name)); } else { println!("Compression Failed ."); } @@ -1362,7 +1380,7 @@ pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: let tmp_dir = format!("/tmp/{}", dump_file_name); if Path::new(&tmp_dir).is_dir() { rm_rf(&tmp_dir); - println!("Temporary Directory Deleted: {}", tmp_dir); + println!("Temporary Directory Deleted: {}", basename(&tmp_dir)); } rm_rf(&dump_file_name); From 79dd2fdb75b4eec603a8263aa977c0ee52403235 Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Tue, 17 Jun 2025 11:34:55 +0530 Subject: [PATCH 33/66] Update basename() util to return owned String Signed-off-by: gomathishankar37 --- crash-upload/src/crashupload_utils.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index 03906f4..4efbf36 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -80,11 +80,12 @@ pub fn safe_rename, D: AsRef>(src: S, dst: D) -> io::Result } #[inline] -pub fn basename>(path: P) -> &str { +pub fn basename>(path: P) -> String { path.as_ref() .file_name() .and_then(|n| n.to_str()) - .unwrap_or_else(|| path.as_ref().to_str().unwrap_or("")) + .map(|s| s.to_string()) + .unwrap_or_else(|| path.as_ref().to_string_lossy().to_string()) } // #[cfg(feature = "shared_api")] From 67897e7e7ff8ae23b065b738109689b9e7f7de7d Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Tue, 17 Jun 2025 11:38:40 +0530 Subject: [PATCH 34/66] Update basename() util Signed-off-by: gomathishankar37 --- crash-upload/src/crashupload_utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index 4efbf36..e06e265 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -888,7 +888,7 @@ pub fn get_crashed_log_file(file_path: &str, is_t2_enabled: bool) -> io::Result< let process_name = basename .rsplitn(2, '_') .nth(1) - .unwrap_or(basename) + .unwrap_or(&basename) .trim_start_matches("./"); println!("Process crashed = {}", process_name); From b3dd541e41610c6f32881b9956826242ff904a19 Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Tue, 17 Jun 2025 12:59:23 +0530 Subject: [PATCH 35/66] Update Compress files logic Signed-off-by: gomathishankar37 --- crash-upload/src/constants.rs | 2 +- crash-upload/src/crashupload_utils.rs | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/crash-upload/src/constants.rs b/crash-upload/src/constants.rs index dc4f66e..8766cf2 100644 --- a/crash-upload/src/constants.rs +++ b/crash-upload/src/constants.rs @@ -9,7 +9,7 @@ pub const LOG_FILES: &str = "/tmp/minidump_log_files.txt"; pub const LOG_PATH: &str = "/opt/rdk"; pub const CORE_LOG: &str = "/opt/rdk/core_log.txt"; -pub const DEVICE_PROP_FILE: &str = "/opt/device.properties"; +pub const DEVICE_PROP_FILE: &str = "/etc/device.properties"; pub const T2_SHARED_SCRIPT: &str = "/lib/rdk/t2Shared_api.sh"; pub const MAX_CORE_FILES: usize = 4; diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index e06e265..0c78339 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -1318,16 +1318,17 @@ pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: dump_file_name = dump_file_name[pos + 1..].to_string(); } } - + let dump_dir = Path::new(dump_paths.get_working_dir()); let tgz_file = if dump_paths.dump_name == "coredump" { - format!("{}.core.tgz", dump_file_name) + dump_dir.join(format!("{}.core.tgz", dump_file_name)) } else { - format!("{}.tgz", dump_file_name) + dump_dir.join(format!("{}.tgz", dump_file_name)) }; let dump_file_name = dump_file_name.replace("<#=#>", "_"); + let dump_file_path = dump_dir.join(&dump_file_name); - if let Err(e) = safe_rename(&sanitized, &dump_file_name) { + if let Err(e) = safe_rename(&sanitized, &dump_file_path) { println!("Failed to rename {} to {}: {}", basename(&sanitized), basename(&dump_file_name), e); continue; } @@ -1357,7 +1358,7 @@ pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: let logfiles_refs: Vec<&str> = logfiles.iter().map(|s| s.as_str()).collect(); - let tar_result = compress_files(&tgz_file, &[&dump_file_name], &logfiles_refs); + let tar_result = compress_files(tgz_file.to_str().unwrap(), &[&dump_file_name], &logfiles_refs); if tar_result.is_ok() { println!("Success Compressing the files, {} {} {} {}", basename(&tgz_file), basename(&dump_file_name), basename(VERSION_FILE), basename(CORE_LOG)); @@ -1366,7 +1367,7 @@ pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: println!("Compression failed, will retry after copying logs to /tmp"); let out_files = copy_log_files_to_tmp(&dump_file_name, &logfiles_refs); let out_files_refs: Vec<&str> = out_files.iter().map(|s| s.as_str()).collect(); - let retry_tar_result = compress_files(&tgz_file, &[&dump_file_name], &out_files_refs); + let retry_tar_result = compress_files(tgz_file.to_str().unwrap(), &[&dump_file_name], &out_files_refs); if retry_tar_result.is_ok() { println!("Success Compressing the files, {} {}", basename(&tgz_file), basename(&dump_file_name)); } else { From 9727ebc5fa47dc1043444d3653e8e3ef5e190df7 Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Tue, 17 Jun 2025 17:04:17 +0530 Subject: [PATCH 36/66] Update Unit Files and Log lines in src/ Signed-off-by: gomathishankar37 --- coredump-upload.service | 2 +- crash-upload/src/crashupload_utils.rs | 165 +++++++++++++------------- crash-upload/src/main.rs | 8 +- minidump-on-bootup-upload.service | 2 +- 4 files changed, 87 insertions(+), 90 deletions(-) diff --git a/coredump-upload.service b/coredump-upload.service index 25ca2f0..2e9fc18 100644 --- a/coredump-upload.service +++ b/coredump-upload.service @@ -26,4 +26,4 @@ Requires=network-online.target #Environment="TS=" #Type=oneshot #RemainAfterExit=yes -ExecStart=/bin/busybox sh -c '/lib/rdk/uploadDumps.sh "" 0 ' +ExecStart=/usr/bin/crash-upload 1 "" "" diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index 0c78339..c7a7de2 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -149,6 +149,7 @@ pub fn set_device_data(device_data: &mut DeviceData) { let mut portal_url = String::new(); get_rfc_param(CRASH_PORTAL_URL_RFC, &mut portal_url); device_data.set_portal_url(portal_url); + println!("set_device_data(): [DEBUG] {:?}", device_data); } /// Checks if any dump files matching a pattern exist in a directory. @@ -206,7 +207,7 @@ pub fn get_secure_dump_status(dump_paths: &mut DumpPaths) { if !is_sec_dump_enabled { if Path::new(SECUREDUMP_ENABLE_FILE).exists() { touch(SECUREDUMP_DISABLE_FILE); - println!("[SECUREDUMP] Disabled"); + println!("get_secure_dump_status(): [SECUREDUMP] Disabled"); } if Path::new(SECUREDUMP_ENABLE_FILE).exists() { let _ = fs::remove_file(SECUREDUMP_ENABLE_FILE); @@ -218,7 +219,7 @@ pub fn get_secure_dump_status(dump_paths: &mut DumpPaths) { } else { if !Path::new(SECUREDUMP_ENABLE_FILE).exists() { touch(SECUREDUMP_ENABLE_FILE); - println!("[SECUREDUMP] Enabled. Dump location changed to /opt/secure."); + println!("get_secure_dump_status(): [SECUREDUMP] Enabled. Dump location changed to /opt/secure."); } if Path::new(SECUREDUMP_DISABLE_FILE).exists() { let _ = fs::remove_file(SECUREDUMP_DISABLE_FILE); @@ -294,7 +295,7 @@ pub fn create_lock_or_exit>(path: P, is_t2_enabled: bool) -> bool if is_t2_enabled { t2_count_notify("SYST_WARN_NoMinidump", Some("1")); } - println!("Script is already working. {:?}. Skip launching another instance...", lock); + println!("create_lock_or_exit(): Script is already working. {:?}. Skip launching another instance...", lock); // TODO: add wait process::exit(0); } @@ -302,7 +303,7 @@ pub fn create_lock_or_exit>(path: P, is_t2_enabled: bool) -> bool match fs::create_dir(&lock) { Ok(_) => true, Err(err) => { - println!("Error creating {:?}: {}", lock, err); + println!("create_lock_or_exit(): Error creating {:?}: {}", lock, err); false } } @@ -322,10 +323,7 @@ pub fn create_lock_or_wait>(path: P) -> bool { let lock = lock_path(&path); loop { if is_another_instance_running(&path) { - println!( - "Script is already working. {:?}. Waiting to launch another instance...", - lock - ); + println!("create_lock_or_wait(): Script is already working. {:?}. Waiting to launch another instance...", lock); thread::sleep(time::Duration::from_secs(2)); continue; } @@ -333,7 +331,7 @@ pub fn create_lock_or_wait>(path: P) -> bool { match fs::create_dir(&lock) { Ok(_) => return true, Err(err) => { - println!("Error creating {:?}: {}", lock, err); + println!("create_lock_or_wait(): Error creating {:?}: {}", lock, err); return false; } } @@ -348,7 +346,7 @@ pub fn remove_lock>(path: P) { let lock = lock_path(&path); if lock.is_dir() { if let Err(err) = fs::remove_dir(&lock) { - println!("Error deleting {:?}: {}", lock, err); + println!("remove_lock(): Error deleting {:?}: {}", lock, err); } } } @@ -373,7 +371,7 @@ pub fn set_log_file(device_data: &DeviceData, log_mod_ts: &str, line: &str) -> S || file_name.contains("_box") || file_name.contains("_mod") { - println!("Core name is already processed: {}", file_name); + println!("set_log_file(): Core name is already processed: {}", file_name); return String::from(file_name); } format!( @@ -396,11 +394,11 @@ pub fn set_log_file(device_data: &DeviceData, log_mod_ts: &str, line: &str) -> S /// * `true` if the box is rebooting (flag file exists), `false` otherwise. pub fn is_box_rebooting(is_t2_enabled: bool) -> bool { if Path::new(CRASH_UPLOAD_REBOOT_FLAG).exists() { - println!("Skipping Upload, Since Box is Rebooting now..."); + println!("is_box_rebooting(): Skipping Upload, Since Box is Rebooting now..."); if is_t2_enabled { t2_count_notify("SYST_INFO_CoreUpldSkipped", Some("1")); } - println!("Upload will happen on next reboot"); + println!("is_box_rebooting(): Upload will happen on next reboot"); // TODO: Add wait return true; } @@ -501,7 +499,7 @@ pub fn is_upload_limit_reached(ts_file: &str) -> bool { if OpenOptions::new().create(true).append(true).open(path).is_err() { - println!("Failed to create or open {}", ts_file); + println!("is_upload_limit_reached(): Failed to create or open {}", ts_file); remove_lock(ts_file); return false; } @@ -534,7 +532,7 @@ pub fn is_upload_limit_reached(ts_file: &str) -> bool { let limit_reached = now.saturating_sub(tenth_newest_crash_time) < LIMIT_SECONDS; if limit_reached { - println!("Not uploading the dump. Too many dumps."); + println!("is_upload_limit_reached(): Not uploading the dump. Too many dumps."); } remove_lock(ts_file); limit_reached @@ -606,7 +604,7 @@ pub fn should_process_dump(dump_name: &str, build_type: &str, file_name: &str) - if dump_name == "minidump" || build_type != "prod" || !file_name.contains("Receiver") { true } else { - println!("Not processing dump file {}", file_name); + println!("should_process_dump(): Not processing dump file {}", file_name); false } } @@ -674,7 +672,7 @@ pub fn remove_pending_dumps(path: &str, extn: &str) -> io::Result<()> { if file_path.is_file() { let file_name = file_path.file_name().and_then(|n| n.to_str()).unwrap_or(""); if file_name.ends_with(extn) || file_name.ends_with(".tgz") { - println!("Removing {} because upload limit has reached or build is blacklisted or TelemetryOptOut is set", basename(&file_path)); + println!("remove_pending_dumps(): Removing {} because upload limit has reached or build is blacklisted or TelemetryOptOut is set", basename(&file_path)); let _ = fs::remove_file(&file_path); } } @@ -699,20 +697,20 @@ pub fn process_crash_t2_info(file_path: &str, is_t2_enabled: bool) { // Check if file is a tarball if file.extension().and_then(|e| e.to_str()) == Some("tgz") { - println!("The File is already a tarball, this might be a retry or crash during shutdown"); + println!("process_crash_t2_info(): The File is already a tarball, this might be a retry or crash during shutdown"); if let Some(pos) = file_name_str.find("_mod_") { file_name_str = file_name_str.split_off(pos + "_mod_".len()); } - println!("Original Filename: {}", basename(file)); - println!("Removing the meta information New Filename: {}", basename(&file_name_str)); - println!("This could be a retry or crash from previous boot; the appname can be truncated"); + println!("process_crash_t2_info(): Original Filename: {}", basename(file)); + println!("process_crash_t2_info(): Removing the meta information New Filename: {}", basename(&file_name_str)); + println!("process_crash_t2_info(): This could be a retry or crash from previous boot; the appname can be truncated"); t2_count_notify("SYS_INFO_TGZDUMP", Some("1")); } // Check for container delimiter in file name if file_name_str.contains(container_delimiter) { let parts: Vec<&str> = file_name_str.split(container_delimiter).collect(); - println!("From the file name crashed process is a container"); + println!("process_crash_t2_info(): From the file name crashed process is a container"); if parts.len() >= 2 { let container_name = parts[0]; @@ -722,15 +720,15 @@ pub fn process_crash_t2_info(file_path: &str, is_t2_enabled: bool) { t2_count_notify("SYS_INFO_CrashedContainer", Some("1")); - println!("Container crash info Basic: {}, {}", app_name, process_name); - println!("Container crash info Advanced: {}, {}", container_name, container_status); - println!("NEW Appname, Process_Crashed, Status = {}, {}, {}", app_name, process_name, container_status); + println!("process_crash_t2_info(): Container crash info Basic: {}, {}", app_name, process_name); + println!("process_crash_t2_info(): Container crash info Advanced: {}, {}", container_name, container_status); + println!("process_crash_t2_info(): NEW Appname, Process_Crashed, Status = {}, {}, {}", app_name, process_name, container_status); t2_val_notify("APP_ERROR_Crashed_split", &[app_name, process_name, container_status]); t2_val_notify("APP_ERROR_Crashed_accum", &[app_name, process_name, container_status]); - println!("NEW Processname, App Name, AppState = {}, {}, {}", process_name, app_name, container_status); - println!("ContainerName, ContainerStatus = {}, {}", container_name, container_status); + println!("process_crash_t2_info(): NEW Processname, App Name, AppState = {}, {}, {}", process_name, app_name, container_status); + println!("process_crash_t2_info(): ContainerName, ContainerStatus = {}, {}", container_name, container_status); t2_val_notify("APP_ERROR_CrashInfo_accum", &[container_name, container_status]); } } @@ -749,9 +747,9 @@ pub fn process_crash_t2_info(file_path: &str, is_t2_enabled: bool) { pub fn mark_as_crash_loop_and_upload(tgz_file: &str) { // portal_url: &str, crash_portal_path: &str) { let tgz_path = Path::new(tgz_file); let new_tgz_name = tgz_path.with_extension("crashloop.dmp.tgz"); - println!("Renaming {} to {}", tgz_path.display(), new_tgz_name.display()); + println!("mark_as_crash_loop_and_upload(): Renaming {} to {}", tgz_path.display(), new_tgz_name.display()); if let Err(e) = safe_rename(tgz_path, &new_tgz_name) { - println!("Failed to rename crashloop tarball: {}", e); + println!("mark_as_crash_loop_and_upload(): Failed to rename crashloop tarball: {}", e); } } @@ -781,11 +779,11 @@ pub fn find_oldest_dump(dir: &str) -> io::Result> { /// * `new_name` - Optional new name to rename to (to retain container info). pub fn save_dump(minidumps_path: &str, s3_filename: &str, new_name: Option<&str>) { if let Some(new_name) = new_name { - println!("Saving dump with original name to retain container info: {}", basename(new_name)); + println!("save_dump(): Saving dump with original name to retain container info: {}", basename(new_name)); let original_path = format!("{}/{}", minidumps_path, s3_filename); let new_path = format!("{}/{}", minidumps_path, new_name); if let Err(e) = safe_rename(&original_path, &new_path) { - println!("Failed to rename file: {}", e); + println!("save_dump(): Failed to rename file: {}", e); } } @@ -795,7 +793,7 @@ pub fn save_dump(minidumps_path: &str, s3_filename: &str, new_name: Option<&str> while count > 5 { match find_oldest_dump(minidumps_path) { Ok(Some(oldest)) => { - println!("Removing old dump {}", oldest.display()); + println!("save_dump(): Removing old dump {}", oldest.display()); if let Err(e) = fs::remove_file(&oldest) { println!("Failed to remove old dump: {}", e); } @@ -804,7 +802,7 @@ pub fn save_dump(minidumps_path: &str, s3_filename: &str, new_name: Option<&str> _ => break, } } - println!("Total pending Minidumps: {}", count); + println!("save_dump(): Total pending Minidumps: {}", count); } /// Logs the current upload timestamp to the timestamp file and truncates it to the last 10 entries. @@ -823,10 +821,10 @@ pub fn log_upload_timestamp(ts_file: &str) { .as_secs(); if let Err(e) = OpenOptions::new().append(true).create(true).open(ts_file) .and_then(|mut f| writeln!(f, "{}", now)) { - println!("Failed to write timestamp: {}", e); + println!("log_upload_timestamp(): Failed to write timestamp: {}", e); } if let Err(e) = truncate_timestamp_file(ts_file) { - println!("Failed to truncate timestamp file: {}", e); + println!("log_upload_timestamp(): Failed to truncate timestamp file: {}", e); } } remove_lock(ts_file); @@ -885,13 +883,12 @@ pub fn get_last_modified_time_of_file(path: &str) -> Option { pub fn get_crashed_log_file(file_path: &str, is_t2_enabled: bool) -> io::Result<()> { let basename = basename(file_path); - let process_name = basename - .rsplitn(2, '_') - .nth(1) - .unwrap_or(&basename) - .trim_start_matches("./"); + let process_name = match basename.rfind('_') { + Some(idx) => &basename[..idx], + None => &basename, + }; - println!("Process crashed = {}", process_name); + println!("get_crashed_log_file(): Process crashed = {}", process_name); if is_t2_enabled { t2_val_notify("processCrash_split", &[process_name]); @@ -916,9 +913,9 @@ pub fn get_crashed_log_file(file_path: &str, is_t2_enabled: bool) -> io::Result< } } } - println!("Crashed process log file(s): {}", log_files); + println!("get_crashed_log_file(): Crashed process log file(s): {}", log_files); if !app_name.is_empty() { - println!("Appname, Process_Crashed = {}, {}", app_name, process_name); + println!("get_crashed_log_file(): Appname, Process_Crashed = {}, {}", app_name, process_name); } // Write each log file to LOG_FILES let mut output = OpenOptions::new() @@ -974,13 +971,13 @@ pub fn delete_all_but_most_recent_files(dir_path: &str) -> io::Result<()> { // Delete the oldest files for (file_path, _) in files.iter().skip(MAX_CORE_FILES) { if let Err(e) = fs::remove_file(file_path) { - println!("Failed to delete file {:?}: {}", file_path, e); + println!("delete_all_but_most_recent_files(): Failed to delete file {:?}: {}", file_path, e); } else { - println!("Deleted old dump file: {:?}", file_path); + println!("delete_all_but_most_recent_files(): Deleted old dump file: {:?}", file_path); } } } else { - println!("No files need to be deleted. Total files: {}", files.len()); + println!("delete_all_but_most_recent_files(): No files need to be deleted. Total files: {}", files.len()); } Ok(()) } @@ -1006,11 +1003,11 @@ pub fn cleanup(work_dir: &str, dump_name: &str, dump_extn: &str) -> std::io::Res // Early exit if directory is missing or empty if !work_dir_path.exists() || !work_dir_path.is_dir() || work_dir_path.read_dir()?.next().is_none() { - println!("Working directory {} is empty", work_dir); + println!("cleanup(): Working directory {} is empty", work_dir); return Ok(()); } - println!("Cleanup {} directory {}", dump_name, work_dir); + println!("cleanup(): Cleaning up {} directory {}", dump_name, work_dir); // Remove files matching '*_mac*_dat*' older than 2 days let cutoff = SystemTime::now() - Duration::from_secs(60 * 60 * 24 * 2); // 2 days @@ -1024,7 +1021,7 @@ pub fn cleanup(work_dir: &str, dump_name: &str, dump_extn: &str) -> std::io::Res if let Ok(modified) = meta.modified() { if modified < cutoff { fs::remove_file(&path)?; - println!("Removed file: {}", path.display()); + println!("cleanup(): Removed file: {}", path.display()); } } } @@ -1054,7 +1051,7 @@ pub fn cleanup(work_dir: &str, dump_name: &str, dump_extn: &str) -> std::io::Res let fname = path.file_name().and_then(|n| n.to_str()).unwrap_or(""); if fname.contains("_mac") && fname.contains("_dat") { fs::remove_file(&path)?; - println!("Deleting unfinished file: {}", path.display()); + println!("cleanup(): Deleting unfinished file: {}", path.display()); } } @@ -1066,7 +1063,7 @@ pub fn cleanup(work_dir: &str, dump_name: &str, dump_extn: &str) -> std::io::Res let fname = path.file_name().and_then(|n| n.to_str()).unwrap_or(""); if !fname.contains(dump_extn.trim_matches('*')) { fs::remove_file(&path)?; - println!("Deleting non-dump file: {}", path.display()); + println!("cleanup(): Deleting non-dump file: {}", path.display()); } } } @@ -1147,7 +1144,7 @@ pub fn add_crashed_log_file(device_data: &DeviceData, log_files: &[&str]) -> io writeln!(output, "{}", line)?; } - println!("Adding File: {} to minidump tarball", process_log); + println!("add_log_file(): Adding File: {} to minidump tarball", process_log); } } } @@ -1182,7 +1179,7 @@ pub fn copy_log_files_to_tmp(tmp_dir_name: &str, logfiles: &[&str]) -> Vec= limit { println!( - "Skipping copying logs to tmp dir due to limited memory (used: {}%, limit: {}%)", + "copy_log_files_to_tmp(): Skipping copying logs to tmp dir due to limited memory (used: {}%, limit: {}%)", usage_percent, limit ); for &file in logfiles { @@ -1192,24 +1189,24 @@ pub fn copy_log_files_to_tmp(tmp_dir_name: &str, logfiles: &[&str]) -> Vec f, Err(e) => { - println!("Error finding dump files: {}", e); + println!("process_dumps(): Error finding dump files: {}", e); return; } }; @@ -1293,7 +1290,7 @@ pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: let sanitized = match sanitize_and_rename(&file) { Ok(f) => f, Err(e) => { - println!("Sanitize/rename failed for {:?}: {}", file, e); + println!("process_dumps(): Sanitize/rename failed for {:?}: {}", file, e); continue; } }; @@ -1304,7 +1301,7 @@ pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: if file.is_file() { if is_tarball(&sanitized) { - println!("Skip archiving {} as it is a tarball already.", basename(&sanitized)); + println!("process_dumps(): Skip archiving {} as it is a tarball already.", basename(&sanitized)); continue; } @@ -1329,7 +1326,7 @@ pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: let dump_file_path = dump_dir.join(&dump_file_name); if let Err(e) = safe_rename(&sanitized, &dump_file_path) { - println!("Failed to rename {} to {}: {}", basename(&sanitized), basename(&dump_file_name), e); + println!("process_dumps(): Failed to rename {} to {}: {}", basename(&sanitized), basename(&dump_file_name), e); continue; } @@ -1340,7 +1337,7 @@ pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: // Log size of the file before compression if let Ok(metadata) = std::fs::metadata(&dump_file_name) { - println!("Size of the file: {} bytes",metadata.len()); + println!("process_dumps(): Size of the file: {} bytes",metadata.len()); } if device_data.get_is_t2_enabled() && !dump_paths.get_dump_name().is_empty() { @@ -1361,28 +1358,28 @@ pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: let tar_result = compress_files(tgz_file.to_str().unwrap(), &[&dump_file_name], &logfiles_refs); if tar_result.is_ok() { - println!("Success Compressing the files, {} {} {} {}", basename(&tgz_file), basename(&dump_file_name), basename(VERSION_FILE), basename(CORE_LOG)); + println!("process_dumps(): Success Compressing the files, {} {} {} {}", basename(&tgz_file), basename(&dump_file_name), basename(VERSION_FILE), basename(CORE_LOG)); } else { - println!("Compression failed, will retry after copying logs to /tmp"); + println!("process_dumps(): Compression failed, will retry after copying logs to /tmp"); let out_files = copy_log_files_to_tmp(&dump_file_name, &logfiles_refs); let out_files_refs: Vec<&str> = out_files.iter().map(|s| s.as_str()).collect(); let retry_tar_result = compress_files(tgz_file.to_str().unwrap(), &[&dump_file_name], &out_files_refs); if retry_tar_result.is_ok() { - println!("Success Compressing the files, {} {}", basename(&tgz_file), basename(&dump_file_name)); + println!("process_dumps(): Success Compressing the files, {} {}", basename(&tgz_file), basename(&dump_file_name)); } else { - println!("Compression Failed ."); + println!("process_dumps(): Compression Failed ."); } } if let Ok(metadata) = std::fs::metadata(&tgz_file) { - println!("Size of the compressed file: {} bytes", metadata.len()); + println!("process_dumps(): Size of the compressed file: {} bytes", metadata.len()); } let tmp_dir = format!("/tmp/{}", dump_file_name); if Path::new(&tmp_dir).is_dir() { rm_rf(&tmp_dir); - println!("Temporary Directory Deleted: {}", basename(&tmp_dir)); + println!("process_dumps(): Temporary Directory Deleted: {}", basename(&tmp_dir)); } rm_rf(&dump_file_name); @@ -1412,7 +1409,7 @@ fn compress_files(tgz_file: &str, main_files: &[&str], extra_files: &[&str]) -> if status.success() { Ok(()) } else { - Err(io::Error::new(io::ErrorKind::Other, "Compression Failed")) + Err(io::Error::new(io::ErrorKind::Other, "compress_files(): Compression Failed")) } } @@ -1456,7 +1453,7 @@ fn remove_logs(working_dir: &str) -> io::Result<()> { if path.is_file() { let name = path.file_name().unwrap_or_default().to_string_lossy(); if name.ends_with(".log") || name.ends_with(".txt") { - println!("Removing {}", path.display()); + println!("remove_logs(): Removing {}", path.display()); rm_rf(path.to_str().unwrap()); } } @@ -1503,14 +1500,14 @@ fn handle_tarballs(device_data: &DeviceData, dump_paths: &DumpPaths, no_network: let tarballs = match find_dump_files(dump_paths.get_working_dir(), dump_paths.get_tar_extn()) { Ok(t) => t, Err(e) => { - println!("Error finding tarballs: {}", e); + println!("handle_tarballs(): Error finding tarballs: {}", e); return; } }; for tarball in tarballs { if let Err(e) = handle_single_tarball(device_data, dump_paths, &tarball, no_network, crash_ts) { - println!("Error handling tarball {:?}: {}", tarball, e); + println!("handle_tarballs(): Error handling tarball {:?}: {}", tarball, e); } } } @@ -1533,7 +1530,7 @@ fn handle_single_tarball(device_data: &DeviceData, dump_paths: &DumpPaths, tarba if is_recovery_time_reached() { rm_rf(DENY_UPLOAD_FILE); } else { - println!("Shifting the recovery time forward."); + println!("handle_single_tarball(): Shifting the recovery time forward."); set_recovery_time(); let _ = remove_pending_dumps( dump_paths.get_working_dir(), @@ -1545,7 +1542,7 @@ fn handle_single_tarball(device_data: &DeviceData, dump_paths: &DumpPaths, tarba if dump_paths.get_dump_name() == "minidump" && is_upload_limit_reached(dump_paths.get_ts_file()) { - println!("Upload rate limit has been reached."); + println!("handle_single_tarball(): Upload rate limit has been reached."); mark_as_crash_loop_and_upload(&tarball_str); set_recovery_time(); let _ = remove_pending_dumps( @@ -1558,14 +1555,14 @@ fn handle_single_tarball(device_data: &DeviceData, dump_paths: &DumpPaths, tarba // 2. no_network logic: skip upload and just save the dump if dump_paths.get_dump_name() == "minidump" && no_network { - println!("Network is not available, skipping upload and saving dump."); + println!("handle_single_tarball(): Network is not available, skipping upload and saving dump."); save_dump(dump_paths.get_minidumps_path(), s3_filename, Some(crash_ts)); return Ok(()); } // 3. Privacy mode check if device_data.get_device_type() == "mediaclient" && is_privacy_mode_do_not_share() { - println!("Privacy Mode is DO_NOT_SHARE. Stop Uploading the data to the cloud"); + println!("handle_single_tarball(): Privacy Mode is DO_NOT_SHARE. Stop Uploading the data to the cloud"); let _ = remove_pending_dumps( dump_paths.get_working_dir(), dump_paths.get_dumps_extn(), @@ -1619,10 +1616,10 @@ fn rename_tarball_for_s3(tarball: &Path, sanitized_name: &str) -> std::io::Resul fn upload_tarball_with_retries(s3_filename: &str, dump_paths: &DumpPaths, is_t2_enabled: bool) -> bool { let mut upload_status = false; for attempt in 1..=3 { - println!("[{}]: {}: {} S3 Upload Attempt {}", file!(), attempt, dump_paths.get_dump_name(), s3_filename); + println!("upload_tarball_with_retries(): {}: {} S3 Upload Attempt {}", attempt, dump_paths.get_dump_name(), s3_filename); match upload_to_s3(&[s3_filename]) { Ok(exit_status) if exit_status.success() => { - println!("{} uploadToS3 SUCCESS: status: {:?}", dump_paths.get_dump_name(), exit_status); + println!("upload_tarball_with_retries(): {} uploadToS3 SUCCESS: status: {:?}", dump_paths.get_dump_name(), exit_status); upload_status = true; if dump_paths.get_dump_name() == "minidump" && is_t2_enabled { t2_count_notify("SYST_INFO_minidumpUpld", Some("1")); @@ -1630,10 +1627,10 @@ fn upload_tarball_with_retries(s3_filename: &str, dump_paths: &DumpPaths, is_t2_ break; } Ok(exit_status) => { - println!("Execution Status: {:?}, S3 Amazon Upload of {} Failed", exit_status, dump_paths.get_dump_name()); + println!("upload_tarball_with_retries(): Execution Status: {:?}, S3 Amazon Upload of {} Failed", exit_status, dump_paths.get_dump_name()); } Err(e) => { - println!("Upload to S3 failed: {}", e); + println!("upload_tarball_with_retries(): Upload to S3 failed: {}", e); } } std::thread::sleep(std::time::Duration::from_secs(2)); @@ -1653,16 +1650,16 @@ fn post_upload_cleanup(upload_success: bool, dump_paths: &DumpPaths, tarball: &P let s3_path = parent.join(s3_filename); if upload_success { - println!("Removing file {}", s3_filename); + println!("post_upload_cleanup(): Removing file {}", s3_filename); let _ = fs::remove_file(&s3_path); log_upload_timestamp(dump_paths.get_ts_file()); } else { - println!("S3 Amazon Upload of {} Failed..!", dump_paths.get_dump_name()); + println!("post_upload_cleanup(): S3 Amazon Upload of {} Failed..!", dump_paths.get_dump_name()); if dump_paths.get_dump_name() == "minidump" { - println!("Check and save the dump {}", s3_filename); + println!("post_upload_cleanup(): Check and save the dump {}", s3_filename); save_dump(dump_paths.get_minidumps_path(), s3_filename, None); } else { - println!("Removing file {}", s3_filename); + println!("post_upload_cleanup(): Removing file {}", s3_filename); let _ = fs::remove_file(&s3_path); } // TODO: Should exit? diff --git a/crash-upload/src/main.rs b/crash-upload/src/main.rs index b5df81d..77e2e97 100644 --- a/crash-upload/src/main.rs +++ b/crash-upload/src/main.rs @@ -127,11 +127,11 @@ fn main() { while counter <= constants::NETWORK_CHECK_ITERATION { println!("Check network status count {}", counter); if route_file.exists() { - println!("Network is available"); + println!("Route is Available break the loop"); break; } else { println!( - "Network is not available, Sleep for {} seconds", + "Route is not available, Sleep for {} seconds", constants::NETWORK_CHECK_TIMEOUT ); thread::sleep(Duration::from_secs(constants::NETWORK_CHECK_TIMEOUT as u64)); @@ -140,12 +140,12 @@ fn main() { } if !route_file.exists() { - println!("Network is not available. tar dump and save it, as max wait reached"); + println!("Route is not available. tar dump and save it, as max wait reached"); no_network = true; } // System time availability check - println!("IP Acquisition completed, Check if system time is received"); + println!("IP Acquisition completed, Test if system time is received"); let stt_file = Path::new(constants::SYSTEM_TIME_FILE); if !stt_file.exists() { while counter <= constants::SYSTEM_TIME_ITERATION { diff --git a/minidump-on-bootup-upload.service b/minidump-on-bootup-upload.service index 7b92149..ec57ab6 100644 --- a/minidump-on-bootup-upload.service +++ b/minidump-on-bootup-upload.service @@ -24,5 +24,5 @@ Requires=network-online.target [Service] #Environment="TS=" -ExecStart=/bin/sh -c '/lib/rdk/uploadDumps.sh "" 0 ' +ExecStart=/usr/bin/crash-upload 2 "" "" ExecStop=/bin/sh -c 'x=`/bin/pidof uploadDumps.sh` ; if [ $? -eq 0 ] ; then /bin/kill -9 $x; fi ' From a44ecbd217b862de5842e0468823539b512af913 Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Wed, 18 Jun 2025 08:23:30 +0530 Subject: [PATCH 37/66] Update process_dumps() to use abs path Signed-off-by: gomathishankar37 --- crash-upload/src/crashupload_utils.rs | 165 ++++++++++++++++++++------ crash-upload/src/main.rs | 5 +- 2 files changed, 135 insertions(+), 35 deletions(-) diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index c7a7de2..10a95d2 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -2,6 +2,7 @@ use chrono::{DateTime, Local}; use std::fs::{self, File, OpenOptions}; use std::io::{self, BufRead, BufReader, Write}; +use std::os::unix::fs::PermissionsExt; use std::path::{Path, PathBuf}; use std::process::Command; use std::time::{Duration, SystemTime, UNIX_EPOCH}; @@ -88,6 +89,16 @@ pub fn basename>(path: P) -> String { .unwrap_or_else(|| path.as_ref().to_string_lossy().to_string()) } +fn ensure_core_log_exists() { + use std::fs::OpenOptions; + use std::os::unix::fs::PermissionsExt; + if !Path::new(CORE_LOG).exists() { + if let Ok(f) = OpenOptions::new().create(true).write(true).open(CORE_LOG) { + let _ = f.set_permissions(fs::Permissions::from_mode(0o666)); + } + } +} + // #[cfg(feature = "shared_api")] // pub use crate::upload_to_s3::upload_to_s3; @@ -144,12 +155,10 @@ pub fn set_device_data(device_data: &mut DeviceData) { }; device_data.set_encryption_enabled(encryption_enabled); - // Portal URL from TR-181 let mut portal_url = String::new(); get_rfc_param(CRASH_PORTAL_URL_RFC, &mut portal_url); device_data.set_portal_url(portal_url); - println!("set_device_data(): [DEBUG] {:?}", device_data); } /// Checks if any dump files matching a pattern exist in a directory. @@ -201,6 +210,7 @@ pub fn check_dumps_exist(minidumps_path: &str, core_path: &str) -> bool { /// - Touches or removes SecureDump enable/disable flag files. /// - Updates dump paths for secure or non-secure operation. pub fn get_secure_dump_status(dump_paths: &mut DumpPaths) { + println!("get_secure_dump_status(): Checking SecureDump status..."); let mut is_sec_dump_enabled = false; set_secure_dump_flag(&mut is_sec_dump_enabled); @@ -295,7 +305,7 @@ pub fn create_lock_or_exit>(path: P, is_t2_enabled: bool) -> bool if is_t2_enabled { t2_count_notify("SYST_WARN_NoMinidump", Some("1")); } - println!("create_lock_or_exit(): Script is already working. {:?}. Skip launching another instance...", lock); + println!("create_lock_or_exit(): crash-upload is already running. {:?}. Skip launching another instance...", lock); // TODO: add wait process::exit(0); } @@ -323,7 +333,7 @@ pub fn create_lock_or_wait>(path: P) -> bool { let lock = lock_path(&path); loop { if is_another_instance_running(&path) { - println!("create_lock_or_wait(): Script is already working. {:?}. Waiting to launch another instance...", lock); + println!("create_lock_or_wait(): crash-upload is already running. {:?}. Waiting to launch another instance...", lock); thread::sleep(time::Duration::from_secs(2)); continue; } @@ -394,7 +404,7 @@ pub fn set_log_file(device_data: &DeviceData, log_mod_ts: &str, line: &str) -> S /// * `true` if the box is rebooting (flag file exists), `false` otherwise. pub fn is_box_rebooting(is_t2_enabled: bool) -> bool { if Path::new(CRASH_UPLOAD_REBOOT_FLAG).exists() { - println!("is_box_rebooting(): Skipping Upload, Since Box is Rebooting now..."); + println!("is_box_rebooting(): Skipping Upload, Since the box is rebooting now..."); if is_t2_enabled { t2_count_notify("SYST_INFO_CoreUpldSkipped", Some("1")); } @@ -690,7 +700,7 @@ pub fn remove_pending_dumps(path: &str, extn: &str) -> io::Result<()> { /// # Arguments /// * `file_path` - Path to the crash dump file (as &str). pub fn process_crash_t2_info(file_path: &str, is_t2_enabled: bool) { - println!("Processing the crash telemetry info"); + println!("process_crash_t2_info(): Processing the crash telemetry info"); let file = Path::new(file_path); let mut file_name_str = file_path.to_string(); let container_delimiter = "<#=#>"; @@ -1275,7 +1285,12 @@ pub fn get_privacy_control_mode() -> Option { /// - Modifies files in the working directory (renames, compresses, deletes). /// - May create or remove log files and temporary directories. /// - Calls `handle_tarballs` for tarball upload/save logic. -pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: &str, no_network: bool) { +pub fn process_dumps( + device_data: &DeviceData, + dump_paths: &DumpPaths, + crash_ts: &str, + no_network: bool, +) { utils::flush_logger(); let files = match find_dump_files(dump_paths.get_working_dir(), dump_paths.get_dumps_extn()) { @@ -1287,6 +1302,10 @@ pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: }; for file in files { + if !should_process_dump(dump_paths.get_dump_name(), device_data.get_build_type(), &basename(&file)) { + println!("process_dumps(): Skipping file {} ", basename(&file)); + continue; + } let sanitized = match sanitize_and_rename(&file) { Ok(f) => f, Err(e) => { @@ -1298,23 +1317,29 @@ pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: if dump_paths.get_dump_name() != "coredump" { process_crash_t2_info(&sanitized.to_string_lossy(), device_data.is_t2_enabled); } - + if file.is_file() { if is_tarball(&sanitized) { - println!("process_dumps(): Skip archiving {} as it is a tarball already.", basename(&sanitized)); + println!( + "process_dumps(): Skip archiving {} as it is a tarball already.", + basename(&sanitized) + ); continue; } - let mod_date = get_last_modified_time_of_file(&sanitized.to_string_lossy()).unwrap_or_else(|| crash_ts.to_string()); + let mod_date = get_last_modified_time_of_file(&sanitized.to_string_lossy()) + .unwrap_or_else(|| crash_ts.to_string()); let ts_for_naming = if crash_ts.is_empty() { &mod_date } else { crash_ts }; - let mut dump_file_name = set_log_file(device_data, ts_for_naming, &sanitized.to_string_lossy()); + let mut dump_file_name = + set_log_file(device_data, ts_for_naming, &sanitized.to_string_lossy()); if dump_file_name.len() >= 135 { if let Some(pos) = dump_file_name.find('_') { dump_file_name = dump_file_name[pos + 1..].to_string(); } } + let dump_dir = Path::new(dump_paths.get_working_dir()); let tgz_file = if dump_paths.dump_name == "coredump" { dump_dir.join(format!("{}.core.tgz", dump_file_name)) @@ -1325,19 +1350,31 @@ pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: let dump_file_name = dump_file_name.replace("<#=#>", "_"); let dump_file_path = dump_dir.join(&dump_file_name); - if let Err(e) = safe_rename(&sanitized, &dump_file_path) { - println!("process_dumps(): Failed to rename {} to {}: {}", basename(&sanitized), basename(&dump_file_name), e); + let dump_file_path_abs = dump_file_path.clone(); + let tgz_file_abs = tgz_file.clone(); + + if let Err(e) = safe_rename(&sanitized, &dump_file_path_abs) { + println!( + "process_dumps(): Failed to rename {} to {}: {}", + basename(&sanitized), + basename(&dump_file_name), + e + ); continue; } - let version_file_path = Path::new(dump_paths.get_working_dir()).join("version.txt"); + ensure_core_log_exists(); + + let version_file_path = dump_dir.join("version.txt"); if !version_file_path.exists() { let _ = fs::copy(VERSION_FILE, &version_file_path); } - // Log size of the file before compression - if let Ok(metadata) = std::fs::metadata(&dump_file_name) { - println!("process_dumps(): Size of the file: {} bytes",metadata.len()); + if let Ok(metadata) = std::fs::metadata(&dump_file_path_abs) { + println!( + "process_dumps(): Size of the file: {} bytes", + metadata.len() + ); } if device_data.get_is_t2_enabled() && !dump_paths.get_dump_name().is_empty() { @@ -1347,46 +1384,97 @@ pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: let logfiles: Vec = if dump_paths.dump_name == "coredump" { vec![VERSION_FILE.to_string(), CORE_LOG.to_string()] } else { - // add_crashed_log_file() - TODO/ UNUSED let crash_url_file = format!("{}/crashed_url.txt", LOG_PATH); - let crashed_url_file = if Path::new(&crash_url_file).exists() { crash_url_file.clone() } else { "".to_string() }; + let crashed_url_file = if Path::new(&crash_url_file).exists() { + crash_url_file.clone() + } else { + "".to_string() + }; vec![VERSION_FILE.to_string(), CORE_LOG.to_string(), crashed_url_file] }; - let logfiles_refs: Vec<&str> = logfiles.iter().map(|s| s.as_str()).collect(); + let logfiles_abs: Vec = logfiles + .iter() + .filter(|s| !s.is_empty()) + .map(|s| { + if Path::new(s).is_absolute() { + s.clone() + } else { + format!("{}/{}", LOG_PATH, s) + } + }) + .collect(); - let tar_result = compress_files(tgz_file.to_str().unwrap(), &[&dump_file_name], &logfiles_refs); + let logfiles_refs: Vec<&str> = logfiles_abs.iter().map(|s| s.as_str()).collect(); - if tar_result.is_ok() { - println!("process_dumps(): Success Compressing the files, {} {} {} {}", basename(&tgz_file), basename(&dump_file_name), basename(VERSION_FILE), basename(CORE_LOG)); + if dump_paths.dump_name == "minidump" { + if let Err(e) = add_crashed_log_file(device_data, &logfiles_refs) { + println!("process_dumps(): Failed to add crashed log file: {}", e); + } } - else { + + let tar_result = compress_files( + tgz_file_abs.to_str().unwrap(), + &[dump_file_path_abs.to_str().unwrap()], + &logfiles_refs, + ); + + if tar_result.is_ok() { + println!( + "process_dumps(): Success Compressing the files, {} {} {} {}", + basename(&tgz_file_abs), + basename(&dump_file_path_abs), + basename(VERSION_FILE), + basename(CORE_LOG) + ); + } else { println!("process_dumps(): Compression failed, will retry after copying logs to /tmp"); + // --- PATCH: Copy dump file and logs to /tmp for retry --- let out_files = copy_log_files_to_tmp(&dump_file_name, &logfiles_refs); - let out_files_refs: Vec<&str> = out_files.iter().map(|s| s.as_str()).collect(); - let retry_tar_result = compress_files(tgz_file.to_str().unwrap(), &[&dump_file_name], &out_files_refs); + let tmp_dir = format!("/tmp/{}", dump_file_name); + let tmp_dump_file = Path::new(&tmp_dir).join(&dump_file_name); + if let Err(e) = fs::copy(&dump_file_path_abs, &tmp_dump_file) { + println!("process_dumps(): Failed to copy dump file to tmp: {}", e); + } + let mut retry_files = vec![tmp_dump_file.to_str().unwrap()]; + retry_files.extend(out_files.iter().map(|s| s.as_str())); + let retry_tar_result = compress_files( + tgz_file_abs.to_str().unwrap(), + &retry_files, + &[], + ); if retry_tar_result.is_ok() { - println!("process_dumps(): Success Compressing the files, {} {}", basename(&tgz_file), basename(&dump_file_name)); + println!( + "process_dumps(): Success Compressing the files, {} {}", + basename(&tgz_file_abs), + basename(&tmp_dump_file) + ); } else { println!("process_dumps(): Compression Failed ."); } } - if let Ok(metadata) = std::fs::metadata(&tgz_file) { - println!("process_dumps(): Size of the compressed file: {} bytes", metadata.len()); + if let Ok(metadata) = std::fs::metadata(&tgz_file_abs) { + println!( + "process_dumps(): Size of the compressed file: {} bytes", + metadata.len() + ); } let tmp_dir = format!("/tmp/{}", dump_file_name); if Path::new(&tmp_dir).is_dir() { rm_rf(&tmp_dir); - println!("process_dumps(): Temporary Directory Deleted: {}", basename(&tmp_dir)); + println!( + "process_dumps(): Temporary Directory Deleted: {}", + basename(&tmp_dir) + ); } - rm_rf(&dump_file_name); + rm_rf(dump_file_path_abs.to_str().unwrap()); if dump_paths.dump_name != "coredump" { let _ = remove_logs(dump_paths.get_working_dir()); } - } + } } handle_tarballs(device_data, dump_paths, no_network, crash_ts); } @@ -1401,7 +1489,11 @@ pub fn process_dumps(device_data: &DeviceData, dump_paths: &DumpPaths, crash_ts: /// # Returns /// * `Ok(())` if compression succeeded, error otherwise. #[inline] -fn compress_files(tgz_file: &str, main_files: &[&str], extra_files: &[&str]) -> io::Result<()> { +fn compress_files( + tgz_file: &str, + main_files: &[&str], + extra_files: &[&str], +) -> std::io::Result<()> { let mut args = vec!["-zcvf", tgz_file]; args.extend(main_files.iter().copied()); args.extend(extra_files.iter().copied()); @@ -1409,10 +1501,15 @@ fn compress_files(tgz_file: &str, main_files: &[&str], extra_files: &[&str]) -> if status.success() { Ok(()) } else { - Err(io::Error::new(io::ErrorKind::Other, "compress_files(): Compression Failed")) + Err(io::Error::new( + io::ErrorKind::Other, + "compress_files(): Compression Failed", + )) } } + + /// Checks if the given file is a tarball (ends with .tgz). #[inline] fn is_tarball(file: &Path) -> bool { diff --git a/crash-upload/src/main.rs b/crash-upload/src/main.rs index 77e2e97..e86e282 100644 --- a/crash-upload/src/main.rs +++ b/crash-upload/src/main.rs @@ -48,7 +48,7 @@ fn main() { // Exit early if no dumps exist if !crashupload_utils::check_dumps_exist(dump_paths.get_minidumps_path(), dump_paths.get_core_path()) { - println!("No dumps found. Exiting..."); + println!("main(): No dumps found. Exiting..."); std::process::exit(0); } @@ -81,6 +81,9 @@ fn main() { }; dump_paths.set_ts_file(format!("/tmp/.{}_upload_timestamps", dump_paths.get_dump_name())); + println!("main(): [DEBUG] dump_paths: {:#?}\n", dump_paths); + println!("main(): [DEBUG] device_data: {:#?}\n ", device_data); + // Locking logic if wait_for_lock == "wait_for_lock" { crashupload_utils::create_lock_or_wait(dump_paths.get_lock_dir_prefix()); From 046deb92e9bb9ea2cdb5d0455da99e13a8aace4e Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Wed, 18 Jun 2025 13:02:12 +0530 Subject: [PATCH 38/66] Update add_crashed_log_file() in crashupload_utils.rs Signed-off-by: gomathishankar37 --- crash-upload/src/constants.rs | 2 +- crash-upload/src/crashupload_utils.rs | 16 +++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/crash-upload/src/constants.rs b/crash-upload/src/constants.rs index 8766cf2..bb71daf 100644 --- a/crash-upload/src/constants.rs +++ b/crash-upload/src/constants.rs @@ -7,7 +7,7 @@ pub const LOGMAPPER_FILE: &str = "/etc/breakpad-logmapper.conf"; pub const LOG_FILES: &str = "/tmp/minidump_log_files.txt"; pub const LOG_PATH: &str = "/opt/rdk"; -pub const CORE_LOG: &str = "/opt/rdk/core_log.txt"; +pub const CORE_LOG: &str = "/opt/logs/core_log.txt"; pub const DEVICE_PROP_FILE: &str = "/etc/device.properties"; diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index 10a95d2..8d7a0fc 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -1131,35 +1131,33 @@ fn get_tmp_usage_percent() -> Option { /// /// # Returns /// * `Ok(())` on success, or an error if file operations fail. -pub fn add_crashed_log_file(device_data: &DeviceData, log_files: &[&str]) -> io::Result<()> { +pub fn add_crashed_log_file(device_data: &DeviceData, log_files: &[&str], working_dir: &str) -> io::Result<()> { let line_count = if device_data.build_type == "prod" { 500 } else { 5000 }; for file_path in log_files { let path = Path::new(file_path); if path.is_file() { if let Some(log_mod_ts) = get_last_modified_time_of_file(file_path) { - let process_log = set_log_file(device_data, &log_mod_ts, file_path); + let process_log_name = set_log_file(device_data, &log_mod_ts, file_path); + let process_log_path = Path::new(working_dir).join(&process_log_name); - // Use std::fs and BufReader for minimal memory usage let file = File::open(path)?; let lines: Vec = BufReader::new(file) .lines() .map_while(Result::ok) .collect(); - // Take the last N lines let start = lines.len().saturating_sub(line_count); - let mut output = File::create(&process_log)?; + let mut output = File::create(&process_log_path)?; for line in &lines[start..] { writeln!(output, "{}", line)?; } - println!("add_log_file(): Adding File: {} to minidump tarball", process_log); + println!("add_log_file(): Adding File: {} to minidump tarball", process_log_path.display()); } } } - // Remove the original log files after processing for &file_path in log_files { let path = Path::new(file_path); if path.exists() { @@ -1408,11 +1406,11 @@ pub fn process_dumps( let logfiles_refs: Vec<&str> = logfiles_abs.iter().map(|s| s.as_str()).collect(); if dump_paths.dump_name == "minidump" { - if let Err(e) = add_crashed_log_file(device_data, &logfiles_refs) { + if let Err(e) = add_crashed_log_file(device_data, &logfiles_refs, dump_paths.get_working_dir()) { println!("process_dumps(): Failed to add crashed log file: {}", e); } } - + let tar_result = compress_files( tgz_file_abs.to_str().unwrap(), &[dump_file_path_abs.to_str().unwrap()], From f4511838afcde8e764aed1666f449146414536c2 Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Thu, 19 Jun 2025 20:33:24 +0530 Subject: [PATCH 39/66] Update uploadtos3params file update logic Signed-off-by: gomathishankar37 --- crash-upload/src/constants.rs | 6 ++-- crash-upload/src/crashupload_utils.rs | 47 ++++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/crash-upload/src/constants.rs b/crash-upload/src/constants.rs index bb71daf..0740360 100644 --- a/crash-upload/src/constants.rs +++ b/crash-upload/src/constants.rs @@ -19,6 +19,7 @@ pub const SECUREDUMP_ENABLE_FILE: &str = "/tmp/.SecureDumpEnable"; pub const SECUREDUMP_DISABLE_FILE: &str = "/tmp/.SecureDumpDisable"; pub const CRASH_UPLOAD_REBOOT_FLAG: &str = "/tmp/set_crash_reboot_flag"; pub const DENY_UPLOAD_FILE: &str = "/tmp/.deny_dump_uploads_till"; +pub const S3_UPLOAD_PARAM_FILE: &str = "/tmp/uploadtos3params"; pub const UPLOAD_ON_STARTUP: &str = "/opt/.upload_on_startup"; pub const ON_STARTUP_DUMPS_CLEANED_UP_BASE: &str = "/tmp/.on_startup_dumps_cleaned_up"; @@ -36,6 +37,8 @@ pub const SYSTEM_TIME_TIMEOUT: usize = 1; pub const ENCRYPTION_RFC: &str = "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.CrashUpload.encryptionEnabled"; pub const CRASH_PORTAL_URL_RFC: &str = "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.CrashUpload.crashPortalSTBUrl"; +pub const ENABLE_OSCP_STAPLING: &str = "/tmp/.EnableOCSPStapling"; +pub const ENABLE_OSCP: &str = "/tmp/.EnableOCSPCA"; /* UNUSED pub const INCLUDE_PROP_FILE: &str = "/opt/include.properties"; @@ -43,8 +46,7 @@ pub const COREDUMP_PROP_FILE: &str = "/opt/coredump.properties"; pub const HTTP_CODE_FILE: &str = "/tmp/httpcode"; pub const CURL_UPLOAD_TIMEOUT: usize = 45; pub const S3_FILENAME: &str = "s3filename"; -pub const ENABLE_OSCP_STAPLING: &str = "/tmp/.EnableOCSPStapling"; -pub const ENABLE_OSCP: &str = "/tmp/.EnableOCSPCA"; + pub const POTOMAC_USER: &str = "ccpstbscp"; pub const SHA1_DEFAULT_VALUE: &str = "0000000000000000000000000000000000000000"; pub const TIMESTAMP_DEFAULT_VALUE: &str = "2000-01-01-00-00-00"; diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index 8d7a0fc..099c90b 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -99,6 +99,32 @@ fn ensure_core_log_exists() { } } +fn write_uploadtos3params( + dump_paths: &DumpPaths, + device_data: &DeviceData, + partner_id: &str, + enable_ocsp_stapling: &str, + enable_ocsp: &str, + curl_log_option: &str, +) -> std::io::Result<()> { + let params = format!( + "{} {} {} {} {} {} {} {} {} {} {} {}", + dump_paths.get_working_dir(), + partner_id, + dump_paths.get_dump_name(), + device_data.get_device_type(), + VERSION_FILE, + if device_data.get_is_encryption_enabled() { "true" } else { "false" }, + enable_ocsp_stapling, + enable_ocsp, + device_data.get_is_tls_enabled(), + device_data.get_build_type(), + device_data.get_model_num(), + curl_log_option + ); + std::fs::write(S3_UPLOAD_PARAM_FILE, params) +} + // #[cfg(feature = "shared_api")] // pub use crate::upload_to_s3::upload_to_s3; @@ -1153,7 +1179,7 @@ pub fn add_crashed_log_file(device_data: &DeviceData, log_files: &[&str], worki writeln!(output, "{}", line)?; } - println!("add_log_file(): Adding File: {} to minidump tarball", process_log_path.display()); + println!("add_crashed_log_file(): Adding File: {} to minidump tarball", process_log_path.display()); } } } @@ -1671,7 +1697,7 @@ fn handle_single_tarball(device_data: &DeviceData, dump_paths: &DumpPaths, tarba } // 5. Upload with retries - let upload_success = upload_tarball_with_retries(&s3_filename_sanitized, dump_paths, device_data.get_is_t2_enabled()); + let upload_success = upload_tarball_with_retries(&s3_filename_sanitized, dump_paths, device_data); // 6. Post-upload cleanup post_upload_cleanup(upload_success, dump_paths, tarball, &s3_filename_sanitized); @@ -1708,15 +1734,26 @@ fn rename_tarball_for_s3(tarball: &Path, sanitized_name: &str) -> std::io::Resul /// /// # Returns /// * `true` if upload succeeded, `false` otherwise. -fn upload_tarball_with_retries(s3_filename: &str, dump_paths: &DumpPaths, is_t2_enabled: bool) -> bool { +fn upload_tarball_with_retries(s3_filename: &str, dump_paths: &DumpPaths, device_data: &DeviceData) -> bool { let mut upload_status = false; for attempt in 1..=3 { println!("upload_tarball_with_retries(): {}: {} S3 Upload Attempt {}", attempt, dump_paths.get_dump_name(), s3_filename); - match upload_to_s3(&[s3_filename]) { + let curl_log_option = "%{remote_ip} %{remote_port}"; + if let Err(e) = write_uploadtos3params(dump_paths, device_data, "", ENABLE_OSCP_STAPLING, ENABLE_OSCP, curl_log_option) { + println!("upload_tarball_with_retries(): Failed to write upload parameters: {}", e); + continue; + } + let upload_res = upload_to_s3(&[s3_filename]); + + if let Err(e) = std::fs::remove_file(S3_UPLOAD_PARAM_FILE) { + println!("upload_tarball_with_retries(): Failed to remove {}: {}", S3_UPLOAD_PARAM_FILE, e); + } + + match upload_res { Ok(exit_status) if exit_status.success() => { println!("upload_tarball_with_retries(): {} uploadToS3 SUCCESS: status: {:?}", dump_paths.get_dump_name(), exit_status); upload_status = true; - if dump_paths.get_dump_name() == "minidump" && is_t2_enabled { + if dump_paths.get_dump_name() == "minidump" && device_data.get_is_t2_enabled() { t2_count_notify("SYST_INFO_minidumpUpld", Some("1")); } break; From 78a99079bf51a30396ac98e79e400e12afca8bbf Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Thu, 19 Jun 2025 20:46:13 +0530 Subject: [PATCH 40/66] Update Script call Signed-off-by: gomathishankar37 --- crash-upload/src/crashupload_utils.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index 099c90b..31d2d4e 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -1256,7 +1256,9 @@ pub fn copy_log_files_to_tmp(tmp_dir_name: &str, logfiles: &[&str]) -> Vec std::io::Result { let script = "/lib/rdk/uploadDumpsToS3.sh"; if Path::new(script).exists() { - Command::new(script).args(args).status() + let mut cmd_args: Vec<&str> = args.to_vec(); + cmd_args.push("crash-upload"); + Command::new(script).args(&cmd_args).status() } else { Err(std::io::Error::new( std::io::ErrorKind::NotFound, From 9f33f6164ad753de5e36329ec84bcf3fb87974b7 Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Fri, 20 Jun 2025 15:25:27 +0530 Subject: [PATCH 41/66] Update Code Signed-off-by: gomathishankar37 --- crash-upload/Cargo.toml | 5 + crash-upload/src/constants.rs | 1 + crash-upload/src/crashupload_utils.rs | 307 +++++++++++++++----------- 3 files changed, 188 insertions(+), 125 deletions(-) diff --git a/crash-upload/Cargo.toml b/crash-upload/Cargo.toml index c984544..5f1ec05 100644 --- a/crash-upload/Cargo.toml +++ b/crash-upload/Cargo.toml @@ -13,3 +13,8 @@ json-parser-interface = { path = "../crates/json-parser-interface" } platform-interface = { path = "../crates/platform-interface" } utils = { path = "../crates/utils" } chrono = "0.4" +#crash-upload-cpc = { path = "../../../cpg-utils-cpc/crash-upload-cpc", optional = true } + +#[features] +#default = [] +#shared_api = ["crash-upload-cpc/shared_api"] diff --git a/crash-upload/src/constants.rs b/crash-upload/src/constants.rs index 0740360..b6f2821 100644 --- a/crash-upload/src/constants.rs +++ b/crash-upload/src/constants.rs @@ -13,6 +13,7 @@ pub const DEVICE_PROP_FILE: &str = "/etc/device.properties"; pub const T2_SHARED_SCRIPT: &str = "/lib/rdk/t2Shared_api.sh"; pub const MAX_CORE_FILES: usize = 4; +pub const S3_UPLOAD_SCRIPT: &str = "/lib/rdk/uploadDumpsToS3.sh"; pub const SECUREDUMP_TR181_NAME: &str = "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.SecDump.Enable"; pub const SECUREDUMP_ENABLE_FILE: &str = "/tmp/.SecureDumpEnable"; diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index 31d2d4e..9e6b4ff 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -236,14 +236,14 @@ pub fn check_dumps_exist(minidumps_path: &str, core_path: &str) -> bool { /// - Touches or removes SecureDump enable/disable flag files. /// - Updates dump paths for secure or non-secure operation. pub fn get_secure_dump_status(dump_paths: &mut DumpPaths) { - println!("get_secure_dump_status(): Checking SecureDump status..."); + println!("get_secure_dump_status: Checking SecureDump status..."); let mut is_sec_dump_enabled = false; set_secure_dump_flag(&mut is_sec_dump_enabled); if !is_sec_dump_enabled { if Path::new(SECUREDUMP_ENABLE_FILE).exists() { touch(SECUREDUMP_DISABLE_FILE); - println!("get_secure_dump_status(): [SECUREDUMP] Disabled"); + println!("get_secure_dump_status: [SECUREDUMP] Disabled"); } if Path::new(SECUREDUMP_ENABLE_FILE).exists() { let _ = fs::remove_file(SECUREDUMP_ENABLE_FILE); @@ -255,7 +255,7 @@ pub fn get_secure_dump_status(dump_paths: &mut DumpPaths) { } else { if !Path::new(SECUREDUMP_ENABLE_FILE).exists() { touch(SECUREDUMP_ENABLE_FILE); - println!("get_secure_dump_status(): [SECUREDUMP] Enabled. Dump location changed to /opt/secure."); + println!("get_secure_dump_status: [SECUREDUMP] Enabled. Dump location changed to /opt/secure."); } if Path::new(SECUREDUMP_DISABLE_FILE).exists() { let _ = fs::remove_file(SECUREDUMP_DISABLE_FILE); @@ -331,7 +331,7 @@ pub fn create_lock_or_exit>(path: P, is_t2_enabled: bool) -> bool if is_t2_enabled { t2_count_notify("SYST_WARN_NoMinidump", Some("1")); } - println!("create_lock_or_exit(): crash-upload is already running. {:?}. Skip launching another instance...", lock); + println!("create_lock_or_exit: crash-upload is already running. {:?}. Skip launching another instance...", lock); // TODO: add wait process::exit(0); } @@ -339,7 +339,7 @@ pub fn create_lock_or_exit>(path: P, is_t2_enabled: bool) -> bool match fs::create_dir(&lock) { Ok(_) => true, Err(err) => { - println!("create_lock_or_exit(): Error creating {:?}: {}", lock, err); + println!("create_lock_or_exit: Error creating {:?}: {}", lock, err); false } } @@ -359,7 +359,7 @@ pub fn create_lock_or_wait>(path: P) -> bool { let lock = lock_path(&path); loop { if is_another_instance_running(&path) { - println!("create_lock_or_wait(): crash-upload is already running. {:?}. Waiting to launch another instance...", lock); + println!("create_lock_or_wait: crash-upload is already running. {:?}. Waiting to launch another instance...", lock); thread::sleep(time::Duration::from_secs(2)); continue; } @@ -367,7 +367,7 @@ pub fn create_lock_or_wait>(path: P) -> bool { match fs::create_dir(&lock) { Ok(_) => return true, Err(err) => { - println!("create_lock_or_wait(): Error creating {:?}: {}", lock, err); + println!("create_lock_or_wait: Error creating {:?}: {}", lock, err); return false; } } @@ -382,7 +382,7 @@ pub fn remove_lock>(path: P) { let lock = lock_path(&path); if lock.is_dir() { if let Err(err) = fs::remove_dir(&lock) { - println!("remove_lock(): Error deleting {:?}: {}", lock, err); + println!("remove_lock: Error deleting {:?}: {}", lock, err); } } } @@ -407,7 +407,7 @@ pub fn set_log_file(device_data: &DeviceData, log_mod_ts: &str, line: &str) -> S || file_name.contains("_box") || file_name.contains("_mod") { - println!("set_log_file(): Core name is already processed: {}", file_name); + println!("set_log_file: Core name is already processed: {}", file_name); return String::from(file_name); } format!( @@ -430,11 +430,11 @@ pub fn set_log_file(device_data: &DeviceData, log_mod_ts: &str, line: &str) -> S /// * `true` if the box is rebooting (flag file exists), `false` otherwise. pub fn is_box_rebooting(is_t2_enabled: bool) -> bool { if Path::new(CRASH_UPLOAD_REBOOT_FLAG).exists() { - println!("is_box_rebooting(): Skipping Upload, Since the box is rebooting now..."); + println!("is_box_rebooting: Skipping Upload, Since the box is rebooting now..."); if is_t2_enabled { t2_count_notify("SYST_INFO_CoreUpldSkipped", Some("1")); } - println!("is_box_rebooting(): Upload will happen on next reboot"); + println!("is_box_rebooting: Upload will happen on next reboot"); // TODO: Add wait return true; } @@ -535,7 +535,7 @@ pub fn is_upload_limit_reached(ts_file: &str) -> bool { if OpenOptions::new().create(true).append(true).open(path).is_err() { - println!("is_upload_limit_reached(): Failed to create or open {}", ts_file); + println!("is_upload_limit_reached: Failed to create or open {}", ts_file); remove_lock(ts_file); return false; } @@ -568,7 +568,7 @@ pub fn is_upload_limit_reached(ts_file: &str) -> bool { let limit_reached = now.saturating_sub(tenth_newest_crash_time) < LIMIT_SECONDS; if limit_reached { - println!("is_upload_limit_reached(): Not uploading the dump. Too many dumps."); + println!("is_upload_limit_reached: Not uploading the dump. Too many dumps."); } remove_lock(ts_file); limit_reached @@ -640,7 +640,7 @@ pub fn should_process_dump(dump_name: &str, build_type: &str, file_name: &str) - if dump_name == "minidump" || build_type != "prod" || !file_name.contains("Receiver") { true } else { - println!("should_process_dump(): Not processing dump file {}", file_name); + println!("should_process_dump: Not processing dump file {}", file_name); false } } @@ -708,7 +708,7 @@ pub fn remove_pending_dumps(path: &str, extn: &str) -> io::Result<()> { if file_path.is_file() { let file_name = file_path.file_name().and_then(|n| n.to_str()).unwrap_or(""); if file_name.ends_with(extn) || file_name.ends_with(".tgz") { - println!("remove_pending_dumps(): Removing {} because upload limit has reached or build is blacklisted or TelemetryOptOut is set", basename(&file_path)); + println!("remove_pending_dumps: Removing {} because upload limit has reached or build is blacklisted or TelemetryOptOut is set", basename(&file_path)); let _ = fs::remove_file(&file_path); } } @@ -726,27 +726,27 @@ pub fn remove_pending_dumps(path: &str, extn: &str) -> io::Result<()> { /// # Arguments /// * `file_path` - Path to the crash dump file (as &str). pub fn process_crash_t2_info(file_path: &str, is_t2_enabled: bool) { - println!("process_crash_t2_info(): Processing the crash telemetry info"); + println!("process_crash_t2_info: Processing the crash telemetry info"); let file = Path::new(file_path); let mut file_name_str = file_path.to_string(); let container_delimiter = "<#=#>"; // Check if file is a tarball if file.extension().and_then(|e| e.to_str()) == Some("tgz") { - println!("process_crash_t2_info(): The File is already a tarball, this might be a retry or crash during shutdown"); + println!("process_crash_t2_info: The File is already a tarball, this might be a retry or crash during shutdown"); if let Some(pos) = file_name_str.find("_mod_") { file_name_str = file_name_str.split_off(pos + "_mod_".len()); } - println!("process_crash_t2_info(): Original Filename: {}", basename(file)); - println!("process_crash_t2_info(): Removing the meta information New Filename: {}", basename(&file_name_str)); - println!("process_crash_t2_info(): This could be a retry or crash from previous boot; the appname can be truncated"); + println!("process_crash_t2_info: Original Filename: {}", basename(file)); + println!("process_crash_t2_info: Removing the meta information New Filename: {}", basename(&file_name_str)); + println!("process_crash_t2_info: This could be a retry or crash from previous boot; the appname can be truncated"); t2_count_notify("SYS_INFO_TGZDUMP", Some("1")); } // Check for container delimiter in file name if file_name_str.contains(container_delimiter) { let parts: Vec<&str> = file_name_str.split(container_delimiter).collect(); - println!("process_crash_t2_info(): From the file name crashed process is a container"); + println!("process_crash_t2_info: From the file name crashed process is a container"); if parts.len() >= 2 { let container_name = parts[0]; @@ -756,15 +756,15 @@ pub fn process_crash_t2_info(file_path: &str, is_t2_enabled: bool) { t2_count_notify("SYS_INFO_CrashedContainer", Some("1")); - println!("process_crash_t2_info(): Container crash info Basic: {}, {}", app_name, process_name); - println!("process_crash_t2_info(): Container crash info Advanced: {}, {}", container_name, container_status); - println!("process_crash_t2_info(): NEW Appname, Process_Crashed, Status = {}, {}, {}", app_name, process_name, container_status); + println!("process_crash_t2_info: Container crash info Basic: {}, {}", app_name, process_name); + println!("process_crash_t2_info: Container crash info Advanced: {}, {}", container_name, container_status); + println!("process_crash_t2_info: NEW Appname, Process_Crashed, Status = {}, {}, {}", app_name, process_name, container_status); t2_val_notify("APP_ERROR_Crashed_split", &[app_name, process_name, container_status]); t2_val_notify("APP_ERROR_Crashed_accum", &[app_name, process_name, container_status]); - println!("process_crash_t2_info(): NEW Processname, App Name, AppState = {}, {}, {}", process_name, app_name, container_status); - println!("process_crash_t2_info(): ContainerName, ContainerStatus = {}, {}", container_name, container_status); + println!("process_crash_t2_info: NEW Processname, App Name, AppState = {}, {}, {}", process_name, app_name, container_status); + println!("process_crash_t2_info: ContainerName, ContainerStatus = {}, {}", container_name, container_status); t2_val_notify("APP_ERROR_CrashInfo_accum", &[container_name, container_status]); } } @@ -783,9 +783,9 @@ pub fn process_crash_t2_info(file_path: &str, is_t2_enabled: bool) { pub fn mark_as_crash_loop_and_upload(tgz_file: &str) { // portal_url: &str, crash_portal_path: &str) { let tgz_path = Path::new(tgz_file); let new_tgz_name = tgz_path.with_extension("crashloop.dmp.tgz"); - println!("mark_as_crash_loop_and_upload(): Renaming {} to {}", tgz_path.display(), new_tgz_name.display()); + println!("mark_as_crash_loop_and_upload: Renaming {} to {}", tgz_path.display(), new_tgz_name.display()); if let Err(e) = safe_rename(tgz_path, &new_tgz_name) { - println!("mark_as_crash_loop_and_upload(): Failed to rename crashloop tarball: {}", e); + println!("mark_as_crash_loop_and_upload: Failed to rename crashloop tarball: {}", e); } } @@ -815,11 +815,11 @@ pub fn find_oldest_dump(dir: &str) -> io::Result> { /// * `new_name` - Optional new name to rename to (to retain container info). pub fn save_dump(minidumps_path: &str, s3_filename: &str, new_name: Option<&str>) { if let Some(new_name) = new_name { - println!("save_dump(): Saving dump with original name to retain container info: {}", basename(new_name)); + println!("save_dump: Saving dump with original name to retain container info: {}", basename(new_name)); let original_path = format!("{}/{}", minidumps_path, s3_filename); let new_path = format!("{}/{}", minidumps_path, new_name); if let Err(e) = safe_rename(&original_path, &new_path) { - println!("save_dump(): Failed to rename file: {}", e); + println!("save_dump: Failed to rename file: {}", e); } } @@ -829,7 +829,7 @@ pub fn save_dump(minidumps_path: &str, s3_filename: &str, new_name: Option<&str> while count > 5 { match find_oldest_dump(minidumps_path) { Ok(Some(oldest)) => { - println!("save_dump(): Removing old dump {}", oldest.display()); + println!("save_dump: Removing old dump {}", oldest.display()); if let Err(e) = fs::remove_file(&oldest) { println!("Failed to remove old dump: {}", e); } @@ -838,7 +838,7 @@ pub fn save_dump(minidumps_path: &str, s3_filename: &str, new_name: Option<&str> _ => break, } } - println!("save_dump(): Total pending Minidumps: {}", count); + println!("save_dump: Total pending Minidumps: {}", count); } /// Logs the current upload timestamp to the timestamp file and truncates it to the last 10 entries. @@ -857,10 +857,10 @@ pub fn log_upload_timestamp(ts_file: &str) { .as_secs(); if let Err(e) = OpenOptions::new().append(true).create(true).open(ts_file) .and_then(|mut f| writeln!(f, "{}", now)) { - println!("log_upload_timestamp(): Failed to write timestamp: {}", e); + println!("log_upload_timestamp: Failed to write timestamp: {}", e); } if let Err(e) = truncate_timestamp_file(ts_file) { - println!("log_upload_timestamp(): Failed to truncate timestamp file: {}", e); + println!("log_upload_timestamp: Failed to truncate timestamp file: {}", e); } } remove_lock(ts_file); @@ -924,7 +924,7 @@ pub fn get_crashed_log_file(file_path: &str, is_t2_enabled: bool) -> io::Result< None => &basename, }; - println!("get_crashed_log_file(): Process crashed = {}", process_name); + println!("get_crashed_log_file: Process crashed = {}", process_name); if is_t2_enabled { t2_val_notify("processCrash_split", &[process_name]); @@ -949,9 +949,9 @@ pub fn get_crashed_log_file(file_path: &str, is_t2_enabled: bool) -> io::Result< } } } - println!("get_crashed_log_file(): Crashed process log file(s): {}", log_files); + println!("get_crashed_log_file: Crashed process log file(s): {}", log_files); if !app_name.is_empty() { - println!("get_crashed_log_file(): Appname, Process_Crashed = {}, {}", app_name, process_name); + println!("get_crashed_log_file: Appname, Process_Crashed = {}, {}", app_name, process_name); } // Write each log file to LOG_FILES let mut output = OpenOptions::new() @@ -1007,13 +1007,13 @@ pub fn delete_all_but_most_recent_files(dir_path: &str) -> io::Result<()> { // Delete the oldest files for (file_path, _) in files.iter().skip(MAX_CORE_FILES) { if let Err(e) = fs::remove_file(file_path) { - println!("delete_all_but_most_recent_files(): Failed to delete file {:?}: {}", file_path, e); + println!("delete_all_but_most_recent_files: Failed to delete file {:?}: {}", file_path, e); } else { - println!("delete_all_but_most_recent_files(): Deleted old dump file: {:?}", file_path); + println!("delete_all_but_most_recent_files: Deleted old dump file: {:?}", file_path); } } } else { - println!("delete_all_but_most_recent_files(): No files need to be deleted. Total files: {}", files.len()); + println!("delete_all_but_most_recent_files: No files need to be deleted. Total files: {}", files.len()); } Ok(()) } @@ -1039,11 +1039,11 @@ pub fn cleanup(work_dir: &str, dump_name: &str, dump_extn: &str) -> std::io::Res // Early exit if directory is missing or empty if !work_dir_path.exists() || !work_dir_path.is_dir() || work_dir_path.read_dir()?.next().is_none() { - println!("cleanup(): Working directory {} is empty", work_dir); + println!("cleanup: Working directory {} is empty", work_dir); return Ok(()); } - println!("cleanup(): Cleaning up {} directory {}", dump_name, work_dir); + println!("cleanup: Cleaning up {} directory {}", dump_name, work_dir); // Remove files matching '*_mac*_dat*' older than 2 days let cutoff = SystemTime::now() - Duration::from_secs(60 * 60 * 24 * 2); // 2 days @@ -1057,7 +1057,7 @@ pub fn cleanup(work_dir: &str, dump_name: &str, dump_extn: &str) -> std::io::Res if let Ok(modified) = meta.modified() { if modified < cutoff { fs::remove_file(&path)?; - println!("cleanup(): Removed file: {}", path.display()); + println!("cleanup: Removed file: {}", path.display()); } } } @@ -1087,7 +1087,7 @@ pub fn cleanup(work_dir: &str, dump_name: &str, dump_extn: &str) -> std::io::Res let fname = path.file_name().and_then(|n| n.to_str()).unwrap_or(""); if fname.contains("_mac") && fname.contains("_dat") { fs::remove_file(&path)?; - println!("cleanup(): Deleting unfinished file: {}", path.display()); + println!("cleanup: Deleting unfinished file: {}", path.display()); } } @@ -1099,7 +1099,7 @@ pub fn cleanup(work_dir: &str, dump_name: &str, dump_extn: &str) -> std::io::Res let fname = path.file_name().and_then(|n| n.to_str()).unwrap_or(""); if !fname.contains(dump_extn.trim_matches('*')) { fs::remove_file(&path)?; - println!("cleanup(): Deleting non-dump file: {}", path.display()); + println!("cleanup: Deleting non-dump file: {}", path.display()); } } } @@ -1179,7 +1179,7 @@ pub fn add_crashed_log_file(device_data: &DeviceData, log_files: &[&str], worki writeln!(output, "{}", line)?; } - println!("add_crashed_log_file(): Adding File: {} to minidump tarball", process_log_path.display()); + println!("add_crashed_log_file: Adding File: {} to minidump tarball", process_log_path.display()); } } } @@ -1213,7 +1213,7 @@ pub fn copy_log_files_to_tmp(tmp_dir_name: &str, logfiles: &[&str]) -> Vec= limit { println!( - "copy_log_files_to_tmp(): Skipping copying logs to tmp dir due to limited memory (used: {}%, limit: {}%)", + "copy_log_files_to_tmp: Skipping copying logs to tmp dir due to limited memory (used: {}%, limit: {}%)", usage_percent, limit ); for &file in logfiles { @@ -1223,28 +1223,51 @@ pub fn copy_log_files_to_tmp(tmp_dir_name: &str, logfiles: &[&str]) -> Vec std::io::Result<()> { + // Call the Rust implementation from crash-upload-cpc + let mut force_mtls = String::new(); + let _ = utils::get_property_value_from_file(DEVICE_PROP_FILE, "FORCE_MTLS", &mut force_mtls); + crash_upload_cpc::upload_to_s3(file, + dump_paths.get_working_dir(), + "", + dump_paths.get_dump_name(), + device_data.get_device_type(), + VERSION_FILE, + device_data.get_is_encryption_enabled(), + ENABLE_OSCP_STAPLING, + ENABLE_OSCP, + device_data.get_is_tls_enabled(), + device_data.get_build_type(), + device_data.get_model_num(), + curl_log_option, + device_data.get_is_t2_enabled(), + force_mtls.as_str(), +) +} + /// Calls the uploadDumpsToS3.sh script with the given arguments if it exists. /// /// # Arguments @@ -1254,11 +1277,18 @@ pub fn copy_log_files_to_tmp(tmp_dir_name: &str, logfiles: &[&str]) -> Vec std::io::Result { - let script = "/lib/rdk/uploadDumpsToS3.sh"; - if Path::new(script).exists() { + if Path::new(constants::S3_UPLOAD_SCRIPT).exists() { let mut cmd_args: Vec<&str> = args.to_vec(); cmd_args.push("crash-upload"); - Command::new(script).args(&cmd_args).status() + let status = std::process::Command::new(constants::S3_UPLOAD_SCRIPT).args(&cmd_args).status()?; + if status.success() { + Ok(()) + } else { + Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!("Script failed with status: {:?}", status), + )) + } } else { Err(std::io::Error::new( std::io::ErrorKind::NotFound, @@ -1318,24 +1348,27 @@ pub fn process_dumps( no_network: bool, ) { utils::flush_logger(); + let work_dir = dump_paths.get_working_dir(); - let files = match find_dump_files(dump_paths.get_working_dir(), dump_paths.get_dumps_extn()) { + let files = match find_dump_files(work_dir, dump_paths.get_dumps_extn()) { Ok(f) => f, Err(e) => { - println!("process_dumps(): Error finding dump files: {}", e); + println!("process_dumps: Error finding dump files: {}", e); return; } }; for file in files { - if !should_process_dump(dump_paths.get_dump_name(), device_data.get_build_type(), &basename(&file)) { - println!("process_dumps(): Skipping file {} ", basename(&file)); + let orig_file_name = basename(&file); + + if !should_process_dump(dump_paths.get_dump_name(), device_data.get_build_type(), &orig_file_name) { + println!("process_dumps: Skipping file {} ", orig_file_name); continue; } let sanitized = match sanitize_and_rename(&file) { Ok(f) => f, Err(e) => { - println!("process_dumps(): Sanitize/rename failed for {:?}: {}", file, e); + println!("process_dumps: Sanitize/rename failed for {:?}: {}", file, e); continue; } }; @@ -1347,7 +1380,7 @@ pub fn process_dumps( if file.is_file() { if is_tarball(&sanitized) { println!( - "process_dumps(): Skip archiving {} as it is a tarball already.", + "process_dumps: Skip archiving {} as it is a tarball already.", basename(&sanitized) ); continue; @@ -1365,28 +1398,21 @@ pub fn process_dumps( dump_file_name = dump_file_name[pos + 1..].to_string(); } } - - let dump_dir = Path::new(dump_paths.get_working_dir()); - let tgz_file = if dump_paths.dump_name == "coredump" { - dump_dir.join(format!("{}.core.tgz", dump_file_name)) - } else { - dump_dir.join(format!("{}.tgz", dump_file_name)) - }; - let dump_file_name = dump_file_name.replace("<#=#>", "_"); - let dump_file_path = dump_dir.join(&dump_file_name); - let dump_file_path_abs = dump_file_path.clone(); - let tgz_file_abs = tgz_file.clone(); + let dump_dir = Path::new(work_dir); + let dump_file_path = dump_dir.join(&dump_file_name); - if let Err(e) = safe_rename(&sanitized, &dump_file_path_abs) { - println!( - "process_dumps(): Failed to rename {} to {}: {}", - basename(&sanitized), - basename(&dump_file_name), - e - ); - continue; + if sanitized != dump_file_path { + if let Err(e) = safe_rename(&sanitized, &dump_file_path) { + println!( + "process_dumps: Failed to rename {} to {}: {}", + basename(&sanitized), + basename(&dump_file_name), + e + ); + continue; + } } ensure_core_log_exists(); @@ -1398,7 +1424,7 @@ pub fn process_dumps( if let Ok(metadata) = std::fs::metadata(&dump_file_path_abs) { println!( - "process_dumps(): Size of the file: {} bytes", + "process_dumps: Size of the file: {} bytes", metadata.len() ); } @@ -1434,11 +1460,17 @@ pub fn process_dumps( let logfiles_refs: Vec<&str> = logfiles_abs.iter().map(|s| s.as_str()).collect(); if dump_paths.dump_name == "minidump" { - if let Err(e) = add_crashed_log_file(device_data, &logfiles_refs, dump_paths.get_working_dir()) { - println!("process_dumps(): Failed to add crashed log file: {}", e); + if let Err(e) = add_crashed_log_file(device_data, &logfiles_refs, work_dir) { + println!("process_dumps: Failed to add crashed log file: {}", e); } } + let tgz_file = if dump_paths.dump_name == "coredump" { + dump_dir.join(format!("{}.core.tgz", dump_file_name)) + } else { + dump_dir.join(format!("{}.tgz", dump_file_name)) + }; + let tar_result = compress_files( tgz_file_abs.to_str().unwrap(), &[dump_file_path_abs.to_str().unwrap()], @@ -1447,58 +1479,61 @@ pub fn process_dumps( if tar_result.is_ok() { println!( - "process_dumps(): Success Compressing the files, {} {} {} {}", + "process_dumps: Success Compressing the files, {} {} {} {}", basename(&tgz_file_abs), basename(&dump_file_path_abs), basename(VERSION_FILE), basename(CORE_LOG) ); } else { - println!("process_dumps(): Compression failed, will retry after copying logs to /tmp"); - // --- PATCH: Copy dump file and logs to /tmp for retry --- + println!("process_dumps: Compression failed, will retry after copying logs to /tmp"); let out_files = copy_log_files_to_tmp(&dump_file_name, &logfiles_refs); let tmp_dir = format!("/tmp/{}", dump_file_name); let tmp_dump_file = Path::new(&tmp_dir).join(&dump_file_name); - if let Err(e) = fs::copy(&dump_file_path_abs, &tmp_dump_file) { - println!("process_dumps(): Failed to copy dump file to tmp: {}", e); + if let Err(e) = fs::copy(&dump_file_path, &tmp_dump_file) { + println!("process_dumps: Failed to copy dump file to tmp: {}", e); } let mut retry_files = vec![tmp_dump_file.to_str().unwrap()]; retry_files.extend(out_files.iter().map(|s| s.as_str())); let retry_tar_result = compress_files( - tgz_file_abs.to_str().unwrap(), + tgz_file.to_str().unwrap(), &retry_files, &[], ); if retry_tar_result.is_ok() { println!( - "process_dumps(): Success Compressing the files, {} {}", - basename(&tgz_file_abs), + "process_dumps: Success Compressing the files, {} {}", + basename(&tgz_file), basename(&tmp_dump_file) ); } else { - println!("process_dumps(): Compression Failed ."); + println!("process_dumps: Compression Failed ."); } } - if let Ok(metadata) = std::fs::metadata(&tgz_file_abs) { + if let Ok(metadata) = std::fs::metadata(&tgz_file) { println!( - "process_dumps(): Size of the compressed file: {} bytes", + "process_dumps: Size of the compressed file: {} bytes", metadata.len() ); } + // 21. Remove /tmp/ temp dir if it exists let tmp_dir = format!("/tmp/{}", dump_file_name); if Path::new(&tmp_dir).is_dir() { rm_rf(&tmp_dir); println!( - "process_dumps(): Temporary Directory Deleted: {}", + "process_dumps: Temporary Directory Deleted: {}", basename(&tmp_dir) ); } - rm_rf(dump_file_path_abs.to_str().unwrap()); + // 22. Remove the original dump file after compression + rm_rf(dump_file_path.to_str().unwrap()); + + // 23. For minidump, remove logs from working dir if dump_paths.dump_name != "coredump" { - let _ = remove_logs(dump_paths.get_working_dir()); + let _ = remove_logs(work_dir); } } } @@ -1515,21 +1550,26 @@ pub fn process_dumps( /// # Returns /// * `Ok(())` if compression succeeded, error otherwise. #[inline] -fn compress_files( - tgz_file: &str, - main_files: &[&str], - extra_files: &[&str], +pub fn compress_files( + tarball_path: &str, + dump_files: &[&str], + log_files: &[&str], ) -> std::io::Result<()> { - let mut args = vec!["-zcvf", tgz_file]; - args.extend(main_files.iter().copied()); - args.extend(extra_files.iter().copied()); + let mut args = vec!["-zcvf", tarball_path]; + for file in dump_files { + args.push(file); + } + for file in log_files { + args.push(file); + } let status = Command::new("tar").args(&args).status()?; + if status.success() { Ok(()) } else { Err(io::Error::new( io::ErrorKind::Other, - "compress_files(): Compression Failed", + "compress_files: tar compression failed", )) } } @@ -1576,7 +1616,7 @@ fn remove_logs(working_dir: &str) -> io::Result<()> { if path.is_file() { let name = path.file_name().unwrap_or_default().to_string_lossy(); if name.ends_with(".log") || name.ends_with(".txt") { - println!("remove_logs(): Removing {}", path.display()); + println!("remove_logs: Removing {}", path.display()); rm_rf(path.to_str().unwrap()); } } @@ -1623,14 +1663,15 @@ fn handle_tarballs(device_data: &DeviceData, dump_paths: &DumpPaths, no_network: let tarballs = match find_dump_files(dump_paths.get_working_dir(), dump_paths.get_tar_extn()) { Ok(t) => t, Err(e) => { - println!("handle_tarballs(): Error finding tarballs: {}", e); + println!("handle_tarballs: Error finding tarballs: {}", e); return; } }; + println!("handle_tarballs: Found tarballs: {:?}", tarballs); for tarball in tarballs { if let Err(e) = handle_single_tarball(device_data, dump_paths, &tarball, no_network, crash_ts) { - println!("handle_tarballs(): Error handling tarball {:?}: {}", tarball, e); + println!("handle_tarballs: Error handling tarball {:?}: {}", tarball, e); } } } @@ -1648,12 +1689,14 @@ fn handle_single_tarball(device_data: &DeviceData, dump_paths: &DumpPaths, tarba let tarball_str = tarball.to_string_lossy(); let s3_filename = tarball.file_name().and_then(|n| n.to_str()).unwrap_or(""); let s3_filename_sanitized = s3_filename.replace("<#=#>", "_"); + let parent = tarball.parent().unwrap_or_else(|| Path::new(dump_paths.get_working_dir())); + let s3_full_path = parent.join(&s3_filename_sanitized); // 1. Rate limiting and recovery time if is_recovery_time_reached() { rm_rf(DENY_UPLOAD_FILE); } else { - println!("handle_single_tarball(): Shifting the recovery time forward."); + println!("handle_single_tarball: Shifting the recovery time forward."); set_recovery_time(); let _ = remove_pending_dumps( dump_paths.get_working_dir(), @@ -1665,7 +1708,7 @@ fn handle_single_tarball(device_data: &DeviceData, dump_paths: &DumpPaths, tarba if dump_paths.get_dump_name() == "minidump" && is_upload_limit_reached(dump_paths.get_ts_file()) { - println!("handle_single_tarball(): Upload rate limit has been reached."); + println!("handle_single_tarball: Upload rate limit has been reached."); mark_as_crash_loop_and_upload(&tarball_str); set_recovery_time(); let _ = remove_pending_dumps( @@ -1678,14 +1721,14 @@ fn handle_single_tarball(device_data: &DeviceData, dump_paths: &DumpPaths, tarba // 2. no_network logic: skip upload and just save the dump if dump_paths.get_dump_name() == "minidump" && no_network { - println!("handle_single_tarball(): Network is not available, skipping upload and saving dump."); + println!("handle_single_tarball: Network is not available, skipping upload and saving dump."); save_dump(dump_paths.get_minidumps_path(), s3_filename, Some(crash_ts)); return Ok(()); } // 3. Privacy mode check if device_data.get_device_type() == "mediaclient" && is_privacy_mode_do_not_share() { - println!("handle_single_tarball(): Privacy Mode is DO_NOT_SHARE. Stop Uploading the data to the cloud"); + println!("handle_single_tarball: Privacy Mode is DO_NOT_SHARE. Stop Uploading the data to the cloud"); let _ = remove_pending_dumps( dump_paths.get_working_dir(), dump_paths.get_dumps_extn(), @@ -1699,10 +1742,10 @@ fn handle_single_tarball(device_data: &DeviceData, dump_paths: &DumpPaths, tarba } // 5. Upload with retries - let upload_success = upload_tarball_with_retries(&s3_filename_sanitized, dump_paths, device_data); + let upload_success = upload_tarball_with_retries(s3_full_path.to_str().unwrap(), dump_paths, device_data); // 6. Post-upload cleanup - post_upload_cleanup(upload_success, dump_paths, tarball, &s3_filename_sanitized); + post_upload_cleanup(upload_success, dump_paths, &s3_full_path, &s3_filename_sanitized); Ok(()) } @@ -1736,24 +1779,34 @@ fn rename_tarball_for_s3(tarball: &Path, sanitized_name: &str) -> std::io::Resul /// /// # Returns /// * `true` if upload succeeded, `false` otherwise. -fn upload_tarball_with_retries(s3_filename: &str, dump_paths: &DumpPaths, device_data: &DeviceData) -> bool { +fn upload_tarball_with_retries( + s3_full_path: &str, + dump_paths: &DumpPaths, + device_data: &DeviceData, +) -> bool { let mut upload_status = false; for attempt in 1..=3 { - println!("upload_tarball_with_retries(): {}: {} S3 Upload Attempt {}", attempt, dump_paths.get_dump_name(), s3_filename); + println!("upload_tarball_with_retries: {}: {} S3 Upload Attempt {}", attempt, dump_paths.get_dump_name(), s3_full_path); + let curl_log_option = "%{remote_ip} %{remote_port}"; + if let Err(e) = write_uploadtos3params(dump_paths, device_data, "", ENABLE_OSCP_STAPLING, ENABLE_OSCP, curl_log_option) { - println!("upload_tarball_with_retries(): Failed to write upload parameters: {}", e); + println!("upload_tarball_with_retries: Failed to write upload parameters: {}", e); continue; } - let upload_res = upload_to_s3(&[s3_filename]); + let upload_res = upload_to_s3(&[s3_full_path]); if let Err(e) = std::fs::remove_file(S3_UPLOAD_PARAM_FILE) { - println!("upload_tarball_with_retries(): Failed to remove {}: {}", S3_UPLOAD_PARAM_FILE, e); + println!("upload_tarball_with_retries: Failed to remove {}: {}", S3_UPLOAD_PARAM_FILE, e); } match upload_res { Ok(exit_status) if exit_status.success() => { - println!("upload_tarball_with_retries(): {} uploadToS3 SUCCESS: status: {:?}", dump_paths.get_dump_name(), exit_status); + println!( + "upload_tarball_with_retries: {} uploadToS3 SUCCESS: status: {:?}", + dump_paths.get_dump_name(), + exit_status + ); upload_status = true; if dump_paths.get_dump_name() == "minidump" && device_data.get_is_t2_enabled() { t2_count_notify("SYST_INFO_minidumpUpld", Some("1")); @@ -1761,10 +1814,14 @@ fn upload_tarball_with_retries(s3_filename: &str, dump_paths: &DumpPaths, device break; } Ok(exit_status) => { - println!("upload_tarball_with_retries(): Execution Status: {:?}, S3 Amazon Upload of {} Failed", exit_status, dump_paths.get_dump_name()); + println!( + "upload_tarball_with_retries: Execution Status: {:?}, S3 Amazon Upload of {} Failed", + exit_status, + dump_paths.get_dump_name() + ); } Err(e) => { - println!("upload_tarball_with_retries(): Upload to S3 failed: {}", e); + println!("upload_tarball_with_retries: Upload to S3 failed: {}", e); } } std::thread::sleep(std::time::Duration::from_secs(2)); @@ -1784,16 +1841,16 @@ fn post_upload_cleanup(upload_success: bool, dump_paths: &DumpPaths, tarball: &P let s3_path = parent.join(s3_filename); if upload_success { - println!("post_upload_cleanup(): Removing file {}", s3_filename); + println!("post_upload_cleanup: Removing file {}", s3_filename); let _ = fs::remove_file(&s3_path); log_upload_timestamp(dump_paths.get_ts_file()); } else { - println!("post_upload_cleanup(): S3 Amazon Upload of {} Failed..!", dump_paths.get_dump_name()); + println!("post_upload_cleanup: S3 Amazon Upload of {} Failed..!", dump_paths.get_dump_name()); if dump_paths.get_dump_name() == "minidump" { - println!("post_upload_cleanup(): Check and save the dump {}", s3_filename); + println!("post_upload_cleanup: Check and save the dump {}", s3_filename); save_dump(dump_paths.get_minidumps_path(), s3_filename, None); } else { - println!("post_upload_cleanup(): Removing file {}", s3_filename); + println!("post_upload_cleanup: Removing file {}", s3_filename); let _ = fs::remove_file(&s3_path); } // TODO: Should exit? From d44092740c4a63e6d674b078b08719bf765a984a Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Sat, 21 Jun 2025 15:23:32 +0530 Subject: [PATCH 42/66] Update Cargo.toml --- crash-upload/Cargo.toml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/crash-upload/Cargo.toml b/crash-upload/Cargo.toml index 5f1ec05..c984544 100644 --- a/crash-upload/Cargo.toml +++ b/crash-upload/Cargo.toml @@ -13,8 +13,3 @@ json-parser-interface = { path = "../crates/json-parser-interface" } platform-interface = { path = "../crates/platform-interface" } utils = { path = "../crates/utils" } chrono = "0.4" -#crash-upload-cpc = { path = "../../../cpg-utils-cpc/crash-upload-cpc", optional = true } - -#[features] -#default = [] -#shared_api = ["crash-upload-cpc/shared_api"] From 2065a9d150da9e7dd5d0b1ad378e4c9ce6a13164 Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Mon, 23 Jun 2025 14:42:44 +0530 Subject: [PATCH 43/66] Update crashupload_utils.rs Signed-off-by: gomathishankar37 --- crash-upload/src/crashupload_utils.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index 9e6b4ff..e80f118 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -90,8 +90,6 @@ pub fn basename>(path: P) -> String { } fn ensure_core_log_exists() { - use std::fs::OpenOptions; - use std::os::unix::fs::PermissionsExt; if !Path::new(CORE_LOG).exists() { if let Ok(f) = OpenOptions::new().create(true).write(true).open(CORE_LOG) { let _ = f.set_permissions(fs::Permissions::from_mode(0o666)); @@ -1277,12 +1275,12 @@ pub fn upload_to_s3(args: &[&str], dump_paths: &DumpPaths, device_data: &DeviceD /// * `Ok(exit_status)` if the script ran, or an error if not found or failed. #[cfg(not(feature = "shared_api"))] pub fn upload_to_s3(args: &[&str]) -> std::io::Result { - if Path::new(constants::S3_UPLOAD_SCRIPT).exists() { + if Path::new(S3_UPLOAD_SCRIPT).exists() { let mut cmd_args: Vec<&str> = args.to_vec(); cmd_args.push("crash-upload"); - let status = std::process::Command::new(constants::S3_UPLOAD_SCRIPT).args(&cmd_args).status()?; + let status = std::process::Command::new(S3_UPLOAD_SCRIPT).args(&cmd_args).status()?; if status.success() { - Ok(()) + Ok(status) } else { Err(std::io::Error::new( std::io::ErrorKind::Other, @@ -1422,7 +1420,7 @@ pub fn process_dumps( let _ = fs::copy(VERSION_FILE, &version_file_path); } - if let Ok(metadata) = std::fs::metadata(&dump_file_path_abs) { + if let Ok(metadata) = std::fs::metadata(&dump_file_path) { println!( "process_dumps: Size of the file: {} bytes", metadata.len() @@ -1472,16 +1470,16 @@ pub fn process_dumps( }; let tar_result = compress_files( - tgz_file_abs.to_str().unwrap(), - &[dump_file_path_abs.to_str().unwrap()], + tgz_file.to_str().unwrap(), + &[dump_file_path.to_str().unwrap()], &logfiles_refs, ); if tar_result.is_ok() { println!( "process_dumps: Success Compressing the files, {} {} {} {}", - basename(&tgz_file_abs), - basename(&dump_file_path_abs), + basename(&tgz_file), + basename(&dump_file_path), basename(VERSION_FILE), basename(CORE_LOG) ); From 9904207c3d3fed15a6c6361edeb9bb46aba32768 Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Mon, 23 Jun 2025 19:05:48 +0530 Subject: [PATCH 44/66] Update lib.rs --- crates/platform-interface/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/platform-interface/src/lib.rs b/crates/platform-interface/src/lib.rs index 92c32c1..cf602a1 100644 --- a/crates/platform-interface/src/lib.rs +++ b/crates/platform-interface/src/lib.rs @@ -10,7 +10,7 @@ mod t2_api; pub const PLATFORM_LIB_VER: &str = "v1.0"; // Re-export RFC API functions for external use. -pub use rfc_api::{get_rfc_param, set_rfc_param}; +pub use rfc_api::{get_rfc_param, set_rfc_param, dmcli_get}; // Re-export T2 API functions for external use. pub use t2_api::{t2_count_notify, t2_val_notify}; From bafc65c2b8d96bb33577a2ed3f2141c4d60a53a1 Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Mon, 23 Jun 2025 14:25:35 +0000 Subject: [PATCH 45/66] Update crashupload_utils.rs Signed-off-by: Gomathi Shankar --- crash-upload/src/crashupload_utils.rs | 65 ++++++++++++++++++--------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index e80f118..8e9be69 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -1244,8 +1244,9 @@ pub fn copy_log_files_to_tmp(tmp_dir_name: &str, logfiles: &[&str]) -> Vec std::io::Result<()> { +pub fn upload_to_s3_lib(args: &[&str], dump_paths: &DumpPaths, device_data: &DeviceData, curl_log_option: &str) -> std::io::Result<()> { // Call the Rust implementation from crash-upload-cpc + let file = args.get(0).copied().unwrap_or(""); let mut force_mtls = String::new(); let _ = utils::get_property_value_from_file(DEVICE_PROP_FILE, "FORCE_MTLS", &mut force_mtls); crash_upload_cpc::upload_to_s3(file, @@ -1263,7 +1264,7 @@ pub fn upload_to_s3(args: &[&str], dump_paths: &DumpPaths, device_data: &DeviceD curl_log_option, device_data.get_is_t2_enabled(), force_mtls.as_str(), -) + ) } /// Calls the uploadDumpsToS3.sh script with the given arguments if it exists. @@ -1274,7 +1275,7 @@ pub fn upload_to_s3(args: &[&str], dump_paths: &DumpPaths, device_data: &DeviceD /// # Returns /// * `Ok(exit_status)` if the script ran, or an error if not found or failed. #[cfg(not(feature = "shared_api"))] -pub fn upload_to_s3(args: &[&str]) -> std::io::Result { +pub fn upload_to_s3_script(args: &[&str]) -> std::io::Result { if Path::new(S3_UPLOAD_SCRIPT).exists() { let mut cmd_args: Vec<&str> = args.to_vec(); cmd_args.push("crash-upload"); @@ -1777,7 +1778,7 @@ fn rename_tarball_for_s3(tarball: &Path, sanitized_name: &str) -> std::io::Resul /// /// # Returns /// * `true` if upload succeeded, `false` otherwise. -fn upload_tarball_with_retries( +n upload_tarball_with_retries( s3_full_path: &str, dump_paths: &DumpPaths, device_data: &DeviceData, @@ -1787,35 +1788,55 @@ fn upload_tarball_with_retries( println!("upload_tarball_with_retries: {}: {} S3 Upload Attempt {}", attempt, dump_paths.get_dump_name(), s3_full_path); let curl_log_option = "%{remote_ip} %{remote_port}"; - - if let Err(e) = write_uploadtos3params(dump_paths, device_data, "", ENABLE_OSCP_STAPLING, ENABLE_OSCP, curl_log_option) { - println!("upload_tarball_with_retries: Failed to write upload parameters: {}", e); - continue; - } - let upload_res = upload_to_s3(&[s3_full_path]); - if let Err(e) = std::fs::remove_file(S3_UPLOAD_PARAM_FILE) { - println!("upload_tarball_with_retries: Failed to remove {}: {}", S3_UPLOAD_PARAM_FILE, e); - } + let upload_res: Result<(), String> = { + #[cfg(feature = "shared_api")] + { + match upload_to_s3_lib(&[s3_full_path], dump_paths, device_data, curl_log_option) { + Ok(()) => Ok(()), + Err(e) => Err(format!("upload_to_s3 failed: {}", e)), + } + } + #[cfg(not(feature = "shared_api"))] + { + if let Err(e) = write_uploadtos3params(dump_paths, device_data, "", ENABLE_OSCP_STAPLING, ENABLE_OSCP, curl_log_option) { + println!("upload_tarball_with_retries: Failed to write upload parameters: {}", e); + return false; + } + let res = upload_to_s3_script(&[s3_full_path]); + if let Err(e) = std::fs::remove_file(S3_UPLOAD_PARAM_FILE) { + println!("upload_tarball_with_retries: Failed to remove {}: {}", S3_UPLOAD_PARAM_FILE, e); + } + match res { + Ok(exit_status) if exit_status.success() => Ok(()), + Ok(exit_status) => Err(format!( + "Script ran but failed with status: {:?}", + exit_status + )), + Err(e) => Err(format!("upload_to_s3_script failed: {}", e)), + } + } + }; match upload_res { - Ok(exit_status) if exit_status.success() => { + Ok(()) => { println!( - "upload_tarball_with_retries: {} uploadToS3 SUCCESS: status: {:?}", - dump_paths.get_dump_name(), - exit_status + "upload_tarball_with_retries: {} uploadToS3 SUCCESS", + dump_paths.get_dump_name() ); upload_status = true; - if dump_paths.get_dump_name() == "minidump" && device_data.get_is_t2_enabled() { + if dump_paths.get_dump_name() == "minidump" + && device_data.get_is_t2_enabled() + { t2_count_notify("SYST_INFO_minidumpUpld", Some("1")); } break; } - Ok(exit_status) => { + Err(ref e) if e.contains("Script ran but failed with status") => { println!( - "upload_tarball_with_retries: Execution Status: {:?}, S3 Amazon Upload of {} Failed", - exit_status, - dump_paths.get_dump_name() + "upload_tarball_with_retries: S3 Amazon Upload of {} failed with script exit status: {}", + dump_paths.get_dump_name(), + e ); } Err(e) => { From 9e308c589a6ba48a2fd039e6013d859e77d736e2 Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Mon, 23 Jun 2025 14:28:57 +0000 Subject: [PATCH 46/66] Update crashupload_utils.rs Signed-off-by: Gomathi Shankar --- crash-upload/src/crashupload_utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index 8e9be69..eb100f2 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -1778,7 +1778,7 @@ fn rename_tarball_for_s3(tarball: &Path, sanitized_name: &str) -> std::io::Resul /// /// # Returns /// * `true` if upload succeeded, `false` otherwise. -n upload_tarball_with_retries( +fn upload_tarball_with_retries( s3_full_path: &str, dump_paths: &DumpPaths, device_data: &DeviceData, From 1a6841c32d22dc7e2ab3c3262cc46a57f852cff9 Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Tue, 24 Jun 2025 12:24:16 +0530 Subject: [PATCH 47/66] Update code Signed-off-by: gomathishankar37 --- crash-upload/src/crashupload_utils.rs | 13 ++++++++----- crash-upload/src/main.rs | 4 ++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index eb100f2..64c0eb3 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -95,6 +95,7 @@ fn ensure_core_log_exists() { let _ = f.set_permissions(fs::Permissions::from_mode(0o666)); } } + let _ = Command::new("touch").arg(CORE_LOG).status(); } fn write_uploadtos3params( @@ -171,9 +172,9 @@ pub fn set_device_data(device_data: &mut DeviceData) { device_data.set_tls(tls); // Encryption enabled if file exists, and set RFC param - let encryption_enabled = if Path::new("/etc/encryption_enabled").exists() { - set_rfc_param(ENCRYPTION_RFC, "true"); - true + let encryption_enabled = if Path::new("/etc/os-release").exists() { + if set_rfc_param(ENCRYPTION_RFC, "true") { true } + else { false } } else { false }; @@ -241,7 +242,7 @@ pub fn get_secure_dump_status(dump_paths: &mut DumpPaths) { if !is_sec_dump_enabled { if Path::new(SECUREDUMP_ENABLE_FILE).exists() { touch(SECUREDUMP_DISABLE_FILE); - println!("get_secure_dump_status: [SECUREDUMP] Disabled"); + println!("get_secure_dump_status: Disabled"); } if Path::new(SECUREDUMP_ENABLE_FILE).exists() { let _ = fs::remove_file(SECUREDUMP_ENABLE_FILE); @@ -250,10 +251,11 @@ pub fn get_secure_dump_status(dump_paths: &mut DumpPaths) { dump_paths.set_minidumps_path("/opt/minidumps"); dump_paths.set_core_back_path("/opt/corefiles_back"); dump_paths.set_persistent_sec_path("/opt"); + println!("get_secure_dump_status: Dump location changed /opt."); } else { if !Path::new(SECUREDUMP_ENABLE_FILE).exists() { touch(SECUREDUMP_ENABLE_FILE); - println!("get_secure_dump_status: [SECUREDUMP] Enabled. Dump location changed to /opt/secure."); + println!("get_secure_dump_status: Enabled"); } if Path::new(SECUREDUMP_DISABLE_FILE).exists() { let _ = fs::remove_file(SECUREDUMP_DISABLE_FILE); @@ -262,6 +264,7 @@ pub fn get_secure_dump_status(dump_paths: &mut DumpPaths) { dump_paths.set_minidumps_path("/opt/secure/minidumps"); dump_paths.set_core_back_path("/opt/secure/corefiles_back"); dump_paths.set_persistent_sec_path("/opt/secure"); + println!("get_secure_dump_status: Dump location changed /opt/secure."); } } diff --git a/crash-upload/src/main.rs b/crash-upload/src/main.rs index e86e282..c0c03a2 100644 --- a/crash-upload/src/main.rs +++ b/crash-upload/src/main.rs @@ -48,7 +48,7 @@ fn main() { // Exit early if no dumps exist if !crashupload_utils::check_dumps_exist(dump_paths.get_minidumps_path(), dump_paths.get_core_path()) { - println!("main(): No dumps found. Exiting..."); + println!("main(): No dumps found in {} or {}. Exiting...", dump_paths.get_minidumps_path(), dump_paths.get_core_path()); std::process::exit(0); } @@ -60,7 +60,7 @@ fn main() { // Configure dump paths and metadata based on dump_flag if dump_flag == 1 { - println!("starting core dump processing..."); + println!("Starting coredump processing..."); dump_paths.set_dump_name("coredump"); let core_path = dump_paths.get_core_path().to_string(); dump_paths.set_working_dir(&core_path); From e5b983a50e7ba2d8a597b739bab6c785395787c3 Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Wed, 25 Jun 2025 17:45:36 +0530 Subject: [PATCH 48/66] Update version_file changes Signed-off-by: gomathishankar37 --- crash-upload/src/crashupload_utils.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index 64c0eb3..e53a859 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -1070,6 +1070,7 @@ pub fn cleanup(work_dir: &str, dump_name: &str, dump_extn: &str) -> std::io::Res if !Path::new(UPLOAD_ON_STARTUP).exists() { let version_txt = Path::new(work_dir).join("version.txt"); if version_txt.exists() { + println!("cleanup: Removing version.txt file: {}", version_txt.display()); let _ = fs::remove_file(&version_txt); } @@ -1252,12 +1253,14 @@ pub fn upload_to_s3_lib(args: &[&str], dump_paths: &DumpPaths, device_data: &Dev let file = args.get(0).copied().unwrap_or(""); let mut force_mtls = String::new(); let _ = utils::get_property_value_from_file(DEVICE_PROP_FILE, "FORCE_MTLS", &mut force_mtls); + let image_name = std::fs::read_to_string("/version.txt").unwrap().lines().next().unwrap().split("imagename:").nth(1).unwrap().trim().to_string(); crash_upload_cpc::upload_to_s3(file, dump_paths.get_working_dir(), "", dump_paths.get_dump_name(), device_data.get_device_type(), VERSION_FILE, + &image_name, device_data.get_is_encryption_enabled(), ENABLE_OSCP_STAPLING, ENABLE_OSCP, From 8b43f55473a3028711ca741378ce4f2418e67b14 Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Wed, 25 Jun 2025 18:01:29 +0530 Subject: [PATCH 49/66] Update version_file debug Signed-off-by: gomathishankar37 --- crash-upload/src/crashupload_utils.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index e53a859..5de8ad6 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -1422,9 +1422,12 @@ pub fn process_dumps( ensure_core_log_exists(); - let version_file_path = dump_dir.join("version.txt"); + let version_file_path = Path::new(work_dir).join("version.txt"); + println!("####Before copy Version file path: {}", version_file_path.display()); if !version_file_path.exists() { + println!("####Before copy, Version file path (exist): {}", version_file_path.display()); let _ = fs::copy(VERSION_FILE, &version_file_path); + println!("####After copy Version file path: {}", version_file_path.display()); } if let Ok(metadata) = std::fs::metadata(&dump_file_path) { From e449af561e76dcbc9fdd1b8f0f7c4d08446606c5 Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Wed, 25 Jun 2025 18:26:44 +0530 Subject: [PATCH 50/66] Update debugs Signed-off-by: gomathishankar37 --- crash-upload/src/crashupload_utils.rs | 14 +++++++++----- crash-upload/src/main.rs | 2 ++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index 5de8ad6..fda4f2c 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -1363,6 +1363,9 @@ pub fn process_dumps( } }; + let image_name_proc = std::fs::read_to_string("/version.txt").unwrap().lines().next().unwrap().split("imagename:").nth(1).unwrap().trim().to_string(); + println!("#### ProcessDumps Image Name: {}", image_name_proc); + for file in files { let orig_file_name = basename(&file); @@ -1407,6 +1410,9 @@ pub fn process_dumps( let dump_dir = Path::new(work_dir); let dump_file_path = dump_dir.join(&dump_file_name); + println!("#### Dump file path: {}", dump_file_path.display()); + println!("#### Dump File Name: {}", dump_file_name); + if sanitized != dump_file_path { if let Err(e) = safe_rename(&sanitized, &dump_file_path) { @@ -1423,11 +1429,9 @@ pub fn process_dumps( ensure_core_log_exists(); let version_file_path = Path::new(work_dir).join("version.txt"); - println!("####Before copy Version file path: {}", version_file_path.display()); if !version_file_path.exists() { - println!("####Before copy, Version file path (exist): {}", version_file_path.display()); let _ = fs::copy(VERSION_FILE, &version_file_path); - println!("####After copy Version file path: {}", version_file_path.display()); + println!("Version file path: {}", version_file_path.display()); } if let Ok(metadata) = std::fs::metadata(&dump_file_path) { @@ -1487,7 +1491,7 @@ pub fn process_dumps( if tar_result.is_ok() { println!( - "process_dumps: Success Compressing the files, {} {} {} {}", + "process_dumps: Success Compressing the files, {}\n {}\n {}\n {}\n", basename(&tgz_file), basename(&dump_file_path), basename(VERSION_FILE), @@ -1625,7 +1629,7 @@ fn remove_logs(working_dir: &str) -> io::Result<()> { let name = path.file_name().unwrap_or_default().to_string_lossy(); if name.ends_with(".log") || name.ends_with(".txt") { println!("remove_logs: Removing {}", path.display()); - rm_rf(path.to_str().unwrap()); + fs::remove_file(path)?; } } } diff --git a/crash-upload/src/main.rs b/crash-upload/src/main.rs index c0c03a2..4d5447b 100644 --- a/crash-upload/src/main.rs +++ b/crash-upload/src/main.rs @@ -215,6 +215,8 @@ fn main() { if !Path::new(w_dir).is_dir() { std::process::exit(1); } + let image_name_main = std::fs::read_to_string("/version.txt").unwrap().lines().next().unwrap().split("imagename:").nth(1).unwrap().trim().to_string(); + println!("#### Main Image Name: {}", image_name_main); // Main processing loop (up to 3 attempts, as in shell script) for _ in 0..3 { From ee393e9730091195eab7e117820ad5f43fb774a6 Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Wed, 25 Jun 2025 18:48:37 +0530 Subject: [PATCH 51/66] Update Debugs Signed-off-by: gomathishankar37 --- crash-upload/src/crashupload_utils.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index fda4f2c..b8d5273 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -770,6 +770,7 @@ pub fn process_crash_t2_info(file_path: &str, is_t2_enabled: bool) { } } let _ = get_crashed_log_file(&file_name_str, is_t2_enabled); + } /// Renames a tarball to mark it as crashlooped and (optionally) uploads it to the crash portal. @@ -1189,6 +1190,7 @@ pub fn add_crashed_log_file(device_data: &DeviceData, log_files: &[&str], worki for &file_path in log_files { let path = Path::new(file_path); if path.exists() { + println!("add_crashed_log_file: Removing original log file: {}", path.display()); let _ = fs::remove_file(path); } } @@ -1367,6 +1369,7 @@ pub fn process_dumps( println!("#### ProcessDumps Image Name: {}", image_name_proc); for file in files { + println!("process_dumps: -- Processing file: {}", file.display()); let orig_file_name = basename(&file); if !should_process_dump(dump_paths.get_dump_name(), device_data.get_build_type(), &orig_file_name) { @@ -1432,6 +1435,8 @@ pub fn process_dumps( if !version_file_path.exists() { let _ = fs::copy(VERSION_FILE, &version_file_path); println!("Version file path: {}", version_file_path.display()); + let image_name_main = std::fs::read_to_string("/version.txt").unwrap().lines().next().unwrap().split("imagename:").nth(1).unwrap().trim().to_string(); + println!("#### Device Image Name: {} [{}]", image_name_main, VERSION_FILE); } if let Ok(metadata) = std::fs::metadata(&dump_file_path) { @@ -1469,8 +1474,17 @@ pub fn process_dumps( }) .collect(); + println!("process_dumps: Log files (ABS) to be added: \n{:#?}\n", + logfiles_abs + ); + let logfiles_refs: Vec<&str> = logfiles_abs.iter().map(|s| s.as_str()).collect(); + println!( + "process_dumps: Log files (REF) to be added: \n{:#?}\n", + logfiles_refs + ); + if dump_paths.dump_name == "minidump" { if let Err(e) = add_crashed_log_file(device_data, &logfiles_refs, work_dir) { println!("process_dumps: Failed to add crashed log file: {}", e); @@ -1494,7 +1508,7 @@ pub fn process_dumps( "process_dumps: Success Compressing the files, {}\n {}\n {}\n {}\n", basename(&tgz_file), basename(&dump_file_path), - basename(VERSION_FILE), + VERSION_FILE, basename(CORE_LOG) ); } else { @@ -1540,6 +1554,9 @@ pub fn process_dumps( ); } + let image_name_pd2 = std::fs::read_to_string("/version.txt").unwrap().lines().next().unwrap().split("imagename:").nth(1).unwrap().trim().to_string(); + println!("#### ProcessDumps 2 Image Name: {}", image_name_pd2); + // 22. Remove the original dump file after compression rm_rf(dump_file_path.to_str().unwrap()); @@ -1547,8 +1564,12 @@ pub fn process_dumps( if dump_paths.dump_name != "coredump" { let _ = remove_logs(work_dir); } + let image_name_pd3 = std::fs::read_to_string("/version.txt").unwrap().lines().next().unwrap().split("imagename:").nth(1).unwrap().trim().to_string(); + println!("#### ProcessDumps 3 Image Name: {}", image_name_pd3); } } + let image_name_pd4 = std::fs::read_to_string("/version.txt").unwrap().lines().next().unwrap().split("imagename:").nth(1).unwrap().trim().to_string(); + println!("#### ProcessDumps 4 Image Name: {}", image_name_pd4); handle_tarballs(device_data, dump_paths, no_network, crash_ts); } From 1c18c2922a35daae448a23241c27b6cf4f8d15f9 Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Wed, 25 Jun 2025 19:15:00 +0530 Subject: [PATCH 52/66] Update Logs Signed-off-by: gomathishankar37 --- crash-upload/src/crashupload_utils.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index b8d5273..bd6b6c8 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -1186,14 +1186,6 @@ pub fn add_crashed_log_file(device_data: &DeviceData, log_files: &[&str], worki } } } - - for &file_path in log_files { - let path = Path::new(file_path); - if path.exists() { - println!("add_crashed_log_file: Removing original log file: {}", path.display()); - let _ = fs::remove_file(path); - } - } Ok(()) } From 01b8f44dc796600cd61b2a9da64a14788bbafa89 Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Thu, 26 Jun 2025 11:13:57 +0530 Subject: [PATCH 53/66] Add debugs to bypass cleanup (DEBUG) (TO BE REVERTED) Signed-off-by: gomathishankar37 --- crash-upload/src/crashupload_utils.rs | 31 ++++++++----------- crash-upload/src/main.rs | 44 +++++++++++++-------------- 2 files changed, 34 insertions(+), 41 deletions(-) diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index bd6b6c8..7da1413 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -599,6 +599,8 @@ fn remove_crash_locks_and_flag(dump_paths: &DumpPaths) { /// # Arguments /// * `dump_paths` - Reference to the dump paths struct. pub fn finalize(dump_paths: &DumpPaths) { + println!("finalize: ##DEBUG## Skipping cleanup and removing crash locks and flag for debugging purposes"); + if false { let _ = cleanup( dump_paths.get_working_dir(), dump_paths.get_dump_name(), @@ -606,6 +608,7 @@ pub fn finalize(dump_paths: &DumpPaths) { ); remove_crash_locks_and_flag(dump_paths); } +} /// Handles cleanup on receiving a crash-related signal (SIGTERM or SIGKILL). /// @@ -1357,9 +1360,6 @@ pub fn process_dumps( } }; - let image_name_proc = std::fs::read_to_string("/version.txt").unwrap().lines().next().unwrap().split("imagename:").nth(1).unwrap().trim().to_string(); - println!("#### ProcessDumps Image Name: {}", image_name_proc); - for file in files { println!("process_dumps: -- Processing file: {}", file.display()); let orig_file_name = basename(&file); @@ -1405,8 +1405,8 @@ pub fn process_dumps( let dump_dir = Path::new(work_dir); let dump_file_path = dump_dir.join(&dump_file_name); - println!("#### Dump file path: {}", dump_file_path.display()); - println!("#### Dump File Name: {}", dump_file_name); + println!("process_dumps: Dump file path: {}", dump_file_path.display()); + println!("process_dumps: Dump File Name: {}", dump_file_name); if sanitized != dump_file_path { @@ -1426,9 +1426,7 @@ pub fn process_dumps( let version_file_path = Path::new(work_dir).join("version.txt"); if !version_file_path.exists() { let _ = fs::copy(VERSION_FILE, &version_file_path); - println!("Version file path: {}", version_file_path.display()); - let image_name_main = std::fs::read_to_string("/version.txt").unwrap().lines().next().unwrap().split("imagename:").nth(1).unwrap().trim().to_string(); - println!("#### Device Image Name: {} [{}]", image_name_main, VERSION_FILE); + println!("process_dumps: Version file path: {}", version_file_path.display()); } if let Ok(metadata) = std::fs::metadata(&dump_file_path) { @@ -1466,10 +1464,6 @@ pub fn process_dumps( }) .collect(); - println!("process_dumps: Log files (ABS) to be added: \n{:#?}\n", - logfiles_abs - ); - let logfiles_refs: Vec<&str> = logfiles_abs.iter().map(|s| s.as_str()).collect(); println!( @@ -1546,9 +1540,6 @@ pub fn process_dumps( ); } - let image_name_pd2 = std::fs::read_to_string("/version.txt").unwrap().lines().next().unwrap().split("imagename:").nth(1).unwrap().trim().to_string(); - println!("#### ProcessDumps 2 Image Name: {}", image_name_pd2); - // 22. Remove the original dump file after compression rm_rf(dump_file_path.to_str().unwrap()); @@ -1556,12 +1547,8 @@ pub fn process_dumps( if dump_paths.dump_name != "coredump" { let _ = remove_logs(work_dir); } - let image_name_pd3 = std::fs::read_to_string("/version.txt").unwrap().lines().next().unwrap().split("imagename:").nth(1).unwrap().trim().to_string(); - println!("#### ProcessDumps 3 Image Name: {}", image_name_pd3); } } - let image_name_pd4 = std::fs::read_to_string("/version.txt").unwrap().lines().next().unwrap().split("imagename:").nth(1).unwrap().trim().to_string(); - println!("#### ProcessDumps 4 Image Name: {}", image_name_pd4); handle_tarballs(device_data, dump_paths, no_network, crash_ts); } @@ -1884,6 +1871,11 @@ fn upload_tarball_with_retries( fn post_upload_cleanup(upload_success: bool, dump_paths: &DumpPaths, tarball: &Path, s3_filename: &str) { let parent = tarball.parent().unwrap_or_else(|| Path::new("")); let s3_path = parent.join(s3_filename); + println!("post_upload_cleanup: s3_path: {}", s3_path.display()); + println!("post_upload_cleanup: s3_filename: {}", s3_filename); + + println!("post_upload_cleanup: ##DEBUG## Skipping Cleanup intentionally for debugging purposes"); + if false { if upload_success { println!("post_upload_cleanup: Removing file {}", s3_filename); @@ -1898,6 +1890,7 @@ fn post_upload_cleanup(upload_success: bool, dump_paths: &DumpPaths, tarball: &P println!("post_upload_cleanup: Removing file {}", s3_filename); let _ = fs::remove_file(&s3_path); } + } // TODO: Should exit? } } diff --git a/crash-upload/src/main.rs b/crash-upload/src/main.rs index 4d5447b..d0754b9 100644 --- a/crash-upload/src/main.rs +++ b/crash-upload/src/main.rs @@ -14,7 +14,7 @@ mod crashupload_utils; use crashupload_utils::*; fn main() { - println!("Starting Crash Upload Binary..."); + println!("main(): Starting Crash Upload Binary..."); // TODO: Signal handling (trap/finalize on SIGTERM/SIGKILL/EXIT) @@ -30,7 +30,7 @@ fn main() { let wait_for_lock = &args[3]; // Instantiate configuration structs - println!("Instantiating DumpPaths and DeviceData instances..."); + println!("main(): Instantiating DumpPaths and DeviceData instances..."); let mut device_data = constants::DeviceData::new(); let mut dump_paths = constants::DumpPaths::new(); @@ -60,7 +60,7 @@ fn main() { // Configure dump paths and metadata based on dump_flag if dump_flag == 1 { - println!("Starting coredump processing..."); + println!("main(): Starting coredump processing..."); dump_paths.set_dump_name("coredump"); let core_path = dump_paths.get_core_path().to_string(); dump_paths.set_working_dir(&core_path); @@ -69,7 +69,7 @@ fn main() { dump_paths.set_lock_dir_prefix("/tmp/.uploadCoredumps"); dump_paths.set_crash_portal_path("/opt/crashportal_uploads/coredumps/"); } else { - println!("Starting minidump processing..."); + println!("main(): Starting minidump processing..."); dump_paths.set_dump_name("minidump"); let minidumps_path = dump_paths.get_minidumps_path().to_string(); dump_paths.set_working_dir(&minidumps_path); @@ -104,10 +104,10 @@ fn main() { .unwrap_or(0); if uptime_val < 480 { let sleep_time = 480 - uptime_val; - println!("Deferring reboot for {} seconds", sleep_time); + println!("main(): Deferring reboot for {} seconds", sleep_time); thread::sleep(Duration::from_secs(sleep_time)); if Path::new(constants::CRASH_UPLOAD_REBOOT_FLAG).exists() { - println!("Process crashed exiting from the Deferring reboot"); + println!("main(): Process crashed exiting from the Deferring reboot"); finalize(&dump_paths); std::process::exit(0); } @@ -117,7 +117,7 @@ fn main() { // Check if working directory is empty let w_dir = dump_paths.get_working_dir(); if crashupload_utils::is_dir_empty_or_unreadable(w_dir) { - println!("Working directory is empty or unreadable: {}", w_dir); + println!("main(): Working directory is empty or unreadable: {}", w_dir); crashupload_utils::finalize(&dump_paths); std::process::exit(0); // or exit(1) if you want to signal error } @@ -128,13 +128,13 @@ fn main() { let route_file = Path::new(constants::NETWORK_FILE); while counter <= constants::NETWORK_CHECK_ITERATION { - println!("Check network status count {}", counter); + println!("main(): Check network status count {}", counter); if route_file.exists() { - println!("Route is Available break the loop"); + println!("main(): Route is Available break the loop"); break; } else { println!( - "Route is not available, Sleep for {} seconds", + "main(): Route is not available, Sleep for {} seconds", constants::NETWORK_CHECK_TIMEOUT ); thread::sleep(Duration::from_secs(constants::NETWORK_CHECK_TIMEOUT as u64)); @@ -143,37 +143,37 @@ fn main() { } if !route_file.exists() { - println!("Route is not available. tar dump and save it, as max wait reached"); + println!("main(): Route is not available. tar dump and save it, as max wait reached"); no_network = true; } // System time availability check - println!("IP Acquisition completed, Test if system time is received"); + println!("main(): IP Acquisition completed, Test if system time is received"); let stt_file = Path::new(constants::SYSTEM_TIME_FILE); if !stt_file.exists() { while counter <= constants::SYSTEM_TIME_ITERATION { if !stt_file.exists() { - println!("Waiting for STT, iteration {}", counter); + println!("main(): Waiting for STT, iteration {}", counter); thread::sleep(Duration::from_secs(constants::SYSTEM_TIME_TIMEOUT as u64)); } else { - println!("Received {} flag", constants::SYSTEM_TIME_FILE); + println!("main(): Received {} flag", constants::SYSTEM_TIME_FILE); break; } if counter == constants::SYSTEM_TIME_ITERATION { - println!("Continue without {} flag", constants::SYSTEM_TIME_FILE); + println!("main(): Continue without {} flag", constants::SYSTEM_TIME_FILE); } counter += 1; } } else { - println!("Received {} flag", constants::SYSTEM_TIME_FILE); + println!("main(): Received {} flag", constants::SYSTEM_TIME_FILE); } // trap finalize EXIT // Wait for coredump completion if needed if !Path::new(constants::COREDUMP_MTX_FILE).exists() && dump_flag == 1 { - println!("Waiting for Coredump completion"); + println!("main(): Waiting for Coredump completion"); thread::sleep(Duration::from_secs(21)); } @@ -184,7 +184,7 @@ fn main() { } // Print device MAC address - println!("Mac Address is {}", device_data.get_mac_addr()); + println!("main(): Mac Address is {}", device_data.get_mac_addr()); // Count dumps using utility function let dump_count = match crashupload_utils::get_file_count( @@ -195,7 +195,7 @@ fn main() { Err(_) => 0, }; if dump_count == 0 { - println!("No {} for uploading exist", dump_paths.get_dump_name()); + println!("main(): No {} for uploading exist", dump_paths.get_dump_name()); crashupload_utils::finalize(&dump_paths); std::process::exit(0); } @@ -208,15 +208,15 @@ fn main() { ); // Print portal URL and build ID using getters - println!("Portal URL {}", device_data.get_portal_url()); - println!("buildID is {}", device_data.get_sha1()); + println!("main(): Portal URL {}", device_data.get_portal_url()); + println!("main(): buildID is {}", device_data.get_sha1()); // Final check: working directory must be a directory if !Path::new(w_dir).is_dir() { std::process::exit(1); } let image_name_main = std::fs::read_to_string("/version.txt").unwrap().lines().next().unwrap().split("imagename:").nth(1).unwrap().trim().to_string(); - println!("#### Main Image Name: {}", image_name_main); + println!("main(): Device Firmware: {}", image_name_main); // Main processing loop (up to 3 attempts, as in shell script) for _ in 0..3 { From 6f0eeb9dfb6a5c66eba477d77ebad1f5ec1773eb Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Thu, 26 Jun 2025 12:19:53 +0530 Subject: [PATCH 54/66] Debug checkin Signed-off-by: gomathishankar37 --- crash-upload/src/crashupload_utils.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index 7da1413..a0214f0 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -702,6 +702,8 @@ pub fn get_file_count(dir: &str, pattern: &str) -> std::io::Result { /// # Returns /// * `Ok(())` on success, or an error if file operations fail. pub fn remove_pending_dumps(path: &str, extn: &str) -> io::Result<()> { + println!("remove_pending_dumps: ###DEBUG### Skipping pending dumps removal for debugging purposes"); + if false { let dir_path = Path::new(path); if !dir_path.exists() || !dir_path.is_dir() { return Ok(()); @@ -717,6 +719,7 @@ pub fn remove_pending_dumps(path: &str, extn: &str) -> io::Result<()> { } } } + } Ok(()) } @@ -1541,11 +1544,17 @@ pub fn process_dumps( } // 22. Remove the original dump file after compression + println!("process_dumps: Skipping original dump file removal for debugging purposes"); + if false { rm_rf(dump_file_path.to_str().unwrap()); + } // 23. For minidump, remove logs from working dir if dump_paths.dump_name != "coredump" { + println!("process_dumps: ###DEBUG### Skipping log removal in working dir for debugging purposes "); + if false { let _ = remove_logs(work_dir); + } } } } From 4bc7f1e7c5874dd024b0f083f782c7bfeaceed5e Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Thu, 26 Jun 2025 13:19:00 +0530 Subject: [PATCH 55/66] backup tars (debug) Signed-off-by: gomathishankar37 --- crash-upload/src/crashupload_utils.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index a0214f0..fdd218e 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -1250,7 +1250,18 @@ pub fn copy_log_files_to_tmp(tmp_dir_name: &str, logfiles: &[&str]) -> Vec std::io::Result<()> { // Call the Rust implementation from crash-upload-cpc + println!("upload_to_s3_lib: Uploading to S3 using Rust implementation"); + println!("upload_to_s3_lib: Tars to be uploaded: {:#?}", args); let file = args.get(0).copied().unwrap_or(""); + println!("upload_to_s3_lib: ###DEBUG### File to upload: {:#?}", file); + if !file.is_empty() { + let backup_path = format!("{}.backup", file); + println!("upload_to_s3_lib: ###DEBUG### Backing up file to: {}", backup_path); + if let Err(e) = std::fs::copy(file, &backup_path) { + println!("upload_to_s3_lib: Warning - Failed to create backup: {}", e); + // Continue with upload even if backup fails + } + } let mut force_mtls = String::new(); let _ = utils::get_property_value_from_file(DEVICE_PROP_FILE, "FORCE_MTLS", &mut force_mtls); let image_name = std::fs::read_to_string("/version.txt").unwrap().lines().next().unwrap().split("imagename:").nth(1).unwrap().trim().to_string(); From 0bb71365896d0ece99dda3cacab37a8a5135a8a3 Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Thu, 26 Jun 2025 15:36:17 +0530 Subject: [PATCH 56/66] Update Release opt fields in Cargo manifest Signed-off-by: gomathishankar37 --- Cargo.toml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 55ce654..85841b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,11 @@ members = [ "test/json-parser-interface-test", "test/platform-interface-test", "test/utils-test", -] \ No newline at end of file +] + +[profile.release] +strip = true +opt-level = "z" +codegen-units = 1 +lto = true +panic = "abort" \ No newline at end of file From 4dc8b24ac941a72bbf017a751c543174713adb5d Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Thu, 26 Jun 2025 15:47:10 +0530 Subject: [PATCH 57/66] Update native_full_build.yml From 3559e594f2af37a3dde54216fcda44c3203112d5 Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Thu, 26 Jun 2025 16:09:07 +0530 Subject: [PATCH 58/66] Update Cargo.toml --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 85841b4..9d93972 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,4 +17,3 @@ strip = true opt-level = "z" codegen-units = 1 lto = true -panic = "abort" \ No newline at end of file From f3c4274aa9e8145d3f9768fc7275d39910dc399c Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Thu, 26 Jun 2025 17:45:48 +0530 Subject: [PATCH 59/66] Update Cargo.toml --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9d93972..de5fc8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,6 @@ members = [ ] [profile.release] -strip = true opt-level = "z" codegen-units = 1 lto = true From afc5b124bce2a026164f0363d7b4445ad108363e Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Thu, 26 Jun 2025 19:45:18 +0530 Subject: [PATCH 60/66] Update docstrings Signed-off-by: gomathishankar37 --- crash-upload/src/constants.rs | 50 +- crash-upload/src/crashupload_utils.rs | 714 ++++++++++++++++++----- crash-upload/src/main.rs | 56 ++ crates/curl-interface/src/lib.rs | 2 + crates/json-parser-interface/src/lib.rs | 2 + crates/platform-interface/src/lib.rs | 25 +- crates/platform-interface/src/rfc_api.rs | 37 +- crates/platform-interface/src/t2_api.rs | 27 +- crates/utils/src/command.rs | 37 +- crates/utils/src/device_info.rs | 32 +- crates/utils/src/lib.rs | 17 +- 11 files changed, 812 insertions(+), 187 deletions(-) diff --git a/crash-upload/src/constants.rs b/crash-upload/src/constants.rs index b6f2821..17eba72 100644 --- a/crash-upload/src/constants.rs +++ b/crash-upload/src/constants.rs @@ -2,38 +2,74 @@ //! //! This module defines constants and configuration structs used throughout the crash upload system, //! including file paths, RFC names, and device/dump metadata. +//! +//! ## Constants Categories +//! +//! * **Log Files & Paths**: Constants for log file locations and related configurations +//! * **Property Files**: Paths to device and system property files +//! * **Feature Toggle Files**: Files that enable/disable certain features like secure dump +//! * **Network & System Time**: Constants related to network availability and system time checks +//! * **TR-181 Parameters**: RFC parameter paths for various configuration options +//! +//! ## Configuration Structures +//! +//! * [`DumpPaths`]: Holds paths and file extensions for dump processing operations +//! * [`DeviceData`]: Contains device-specific metadata like MAC address, model number, etc. +/// Path to the log mapper configuration file. pub const LOGMAPPER_FILE: &str = "/etc/breakpad-logmapper.conf"; +/// Path to the file listing minidump log files. pub const LOG_FILES: &str = "/tmp/minidump_log_files.txt"; +/// Base path for RDK logs. pub const LOG_PATH: &str = "/opt/rdk"; +/// Path to the core dump log file. pub const CORE_LOG: &str = "/opt/logs/core_log.txt"; +/// Path to the device properties file. pub const DEVICE_PROP_FILE: &str = "/etc/device.properties"; +/// Path to the Telemetry2 shared script. pub const T2_SHARED_SCRIPT: &str = "/lib/rdk/t2Shared_api.sh"; +/// Maximum number of core files to process. pub const MAX_CORE_FILES: usize = 4; +/// Path to the script for uploading dumps to S3. pub const S3_UPLOAD_SCRIPT: &str = "/lib/rdk/uploadDumpsToS3.sh"; +/// TR-181 parameter name for secure dump feature toggle. pub const SECUREDUMP_TR181_NAME: &str = "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.SecDump.Enable"; +/// File indicating secure dump is enabled. pub const SECUREDUMP_ENABLE_FILE: &str = "/tmp/.SecureDumpEnable"; +/// File indicating secure dump is disabled. pub const SECUREDUMP_DISABLE_FILE: &str = "/tmp/.SecureDumpDisable"; +/// Flag file to trigger reboot after crash upload. pub const CRASH_UPLOAD_REBOOT_FLAG: &str = "/tmp/set_crash_reboot_flag"; +/// File containing timestamp until which dump uploads are denied. pub const DENY_UPLOAD_FILE: &str = "/tmp/.deny_dump_uploads_till"; +/// File containing parameters for S3 uploads. pub const S3_UPLOAD_PARAM_FILE: &str = "/tmp/uploadtos3params"; +/// Flag file to indicate crash uploads should happen on startup. pub const UPLOAD_ON_STARTUP: &str = "/opt/.upload_on_startup"; +/// Base path for file indicating startup dumps were cleaned up. pub const ON_STARTUP_DUMPS_CLEANED_UP_BASE: &str = "/tmp/.on_startup_dumps_cleaned_up"; +/// File to flag crash loop detection (TODO: implement). pub const CRASH_LOOP_FLAG_FILE: &str = ""; // TODO - +/// File used for coredump mutex coordination. pub const COREDUMP_MTX_FILE: &str = "/tmp/coredump_mutex_release"; +/// File indicating network route availability. pub const NETWORK_FILE: &str = "/tmp/route_available"; +/// File indicating system time has been received. pub const SYSTEM_TIME_FILE: &str = "/tmp/stt_received"; +/// Number of iterations for network availability check. pub const NETWORK_CHECK_ITERATION: usize = 18; +/// Timeout in seconds between network check iterations. pub const NETWORK_CHECK_TIMEOUT: usize = 10; +/// Number of iterations for system time check. pub const SYSTEM_TIME_ITERATION: usize = 10; +/// Timeout in seconds between system time check iterations. pub const SYSTEM_TIME_TIMEOUT: usize = 1; pub const ENCRYPTION_RFC: &str = "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.CrashUpload.encryptionEnabled"; @@ -56,6 +92,9 @@ pub const MODEL_NUM_DEFAULT_VALUE: &str = "UNKNOWN"; */ /// Holds all relevant paths and extensions for dump processing. +/// +/// This structure stores all the path information needed for processing crash dumps, +/// including source and destination directories, file extensions, and working directories. #[derive(Debug)] pub struct DumpPaths { pub dump_name: String, @@ -116,6 +155,10 @@ impl DumpPaths { } /// Holds device-specific metadata and configuration. +/// +/// This structure contains information about the device itself, such as its +/// type, model, identifiers, and configuration settings that affect the +/// crash upload process. #[derive(Debug)] pub struct DeviceData { pub device_type: String, @@ -176,7 +219,12 @@ impl DeviceData { } /// Enum representing the type of signal received. +/// +/// Used to differentiate between different types of termination signals +/// that may be received by the crash upload process. pub enum CrashSignal { + /// Normal termination signal (SIGTERM) Term, + /// Kill signal (SIGKILL) Kill, } diff --git a/crash-upload/src/crashupload_utils.rs b/crash-upload/src/crashupload_utils.rs index fdd218e..6388ada 100644 --- a/crash-upload/src/crashupload_utils.rs +++ b/crash-upload/src/crashupload_utils.rs @@ -1,4 +1,50 @@ -// src/utils.rs +//! Crash upload utility functions and implementation. +//! +//! This module provides the core functionality for the crash upload system, including: +//! +//! - **Dump file detection and processing**: Functions to identify, sanitize, and process +//! various types of crash dumps (minidumps and coredumps) +//! +//! - **Tarball creation and management**: Utilities to compress dump files with relevant +//! logs into tarballs for upload +//! +//! - **Upload control and rate limiting**: Functions to manage upload rates, recovery time, +//! and crash loop detection +//! +//! - **Security and privacy controls**: Implementation of secure dump handling and privacy +//! mode checks +//! +//! - **Lock management**: Mutex-like functionality to prevent concurrent execution of +//! critical sections +//! +//! - **File and path utilities**: Specialized functions for file manipulation, path handling, +//! and directory management +//! +//! - **Telemetry integration**: Functions to report crash events and upload status to the +//! Telemetry2 (T2) system +//! +//! ## Key Workflows +//! +//! The main workflow in this module is the dump processing pipeline: +//! 1. Detecting crash dumps in configured directories +//! 2. Sanitizing file names and paths +//! 3. Collecting relevant log files and device information +//! 4. Creating compressed archives (tarballs) containing dumps and logs +//! 5. Uploading archives to cloud storage (S3) with retries +//! 6. Managing cleanup and retention of local dumps +//! +//! ## Integration Points +//! +//! This module integrates with several platform components: +//! - **TR-181/RFC system** (via platform-interface): For configuration parameters +//! - **Telemetry2 (T2)**: For reporting crash and upload events +//! - **S3 storage**: For uploading crash dumps to the cloud +//! - **Device property system**: For obtaining device metadata +//! +//! Most functions in this module are designed to be self-contained and follow +//! the single responsibility principle for maintainability. + +// crashupload/src/crashupload_utils.rs use chrono::{DateTime, Local}; use std::fs::{self, File, OpenOptions}; use std::io::{self, BufRead, BufReader, Write}; @@ -13,35 +59,33 @@ use crate::constants::*; use platform_interface::*; use utils::*; +/// Determines if a file is a minidump based on its name. +/// +/// Checks if the filename contains the substring ".dmp" anywhere in the string, +/// which matches the shell pattern "*.dmp*". +/// +/// # Arguments +/// * `name` - The filename to check. +/// +/// # Returns +/// * `true` if the filename contains ".dmp", `false` otherwise. #[inline] fn is_minidump_file(name: &str) -> bool { // *.dmp* matches any file with ".dmp" after the first character name.find(".dmp").is_some() } -#[inline] -fn is_minidump_tarball(name: &str) -> bool { - // *.dmp.tgz matches files ending with ".dmp.tgz" - name.ends_with(".dmp.tgz") -} - -#[inline] -fn is_coredump_file(name: &str) -> bool { - // *core.prog*.gz* matches files containing "core.prog" and ".gz" (in that order) - if let Some(core_idx) = name.find("core.prog") { - if let Some(gz_idx) = name[core_idx..].find(".gz") { - return true; - } - } - false -} - -#[inline] -fn is_coredump_tarball(name: &str) -> bool { - // *.core.tgz matches files ending with ".core.tgz" - name.ends_with(".core.tgz") -} - +/// Determines if a file is a core pattern file based on its name. +/// +/// A file is considered a core pattern file if it contains "_core" followed by +/// a dot somewhere after the "_core" substring. This matches the shell pattern +/// "*_core*.*". +/// +/// # Arguments +/// * `name` - The filename to check. +/// +/// # Returns +/// * `true` if the filename matches the core pattern format, `false` otherwise. pub fn is_core_pattern_file(name: &str) -> bool { if let Some(core_idx) = name.find("_core") { // There must be a '.' after the '_core' @@ -52,6 +96,16 @@ pub fn is_core_pattern_file(name: &str) -> bool { } } +/// Checks if a directory is empty or cannot be read. +/// +/// This function tries to read the directory entries. If the directory cannot be read +/// (due to permissions or other issues), it's treated as if it were empty. +/// +/// # Arguments +/// * `dir` - Path to the directory to check. +/// +/// # Returns +/// * `true` if the directory is empty or unreadable, `false` otherwise. pub fn is_dir_empty_or_unreadable>(dir: P) -> bool { match fs::read_dir(dir) { Ok(mut entries) => entries.next().is_none(), @@ -59,6 +113,19 @@ pub fn is_dir_empty_or_unreadable>(dir: P) -> bool { } } +/// Safely renames a file, with fallback to copy-and-delete if rename fails with EXDEV error. +/// +/// This function attempts to rename a file from `src` to `dst`. If the rename fails with +/// error code 18 (EXDEV, "Cross-device link"), it falls back to copying the file and then +/// deleting the original. This handles cases where the source and destination are on +/// different filesystems. +/// +/// # Arguments +/// * `src` - Source path. +/// * `dst` - Destination path. If relative, it's resolved relative to `src`'s parent. +/// +/// # Returns +/// * `Ok(())` on success, or an error if both rename and copy-delete fail. pub fn safe_rename, D: AsRef>(src: S, dst: D) -> io::Result<()> { let src_path = src.as_ref(); let dst_path = { @@ -80,6 +147,16 @@ pub fn safe_rename, D: AsRef>(src: S, dst: D) -> io::Result } } +/// Extracts the basename (filename) from a path. +/// +/// This function extracts just the filename portion from a path. If the path doesn't +/// have a valid filename component, returns the entire path as a string. +/// +/// # Arguments +/// * `path` - Path to extract the basename from. +/// +/// # Returns +/// * A `String` containing the basename (filename without directory components). #[inline] pub fn basename>(path: P) -> String { path.as_ref() @@ -89,6 +166,15 @@ pub fn basename>(path: P) -> String { .unwrap_or_else(|| path.as_ref().to_string_lossy().to_string()) } +/// Ensures the core log file exists, creating it with appropriate permissions if needed. +/// +/// This function checks if the core log file exists at the path defined by `CORE_LOG`. +/// If not, it creates the file with 0666 permissions (readable and writable by everyone). +/// It then uses `touch` to update the file's timestamp regardless. +/// +/// # Side Effects +/// - May create a new file at `CORE_LOG` path. +/// - Updates the file's timestamp. fn ensure_core_log_exists() { if !Path::new(CORE_LOG).exists() { if let Ok(f) = OpenOptions::new().create(true).write(true).open(CORE_LOG) { @@ -98,6 +184,23 @@ fn ensure_core_log_exists() { let _ = Command::new("touch").arg(CORE_LOG).status(); } +#[cfg(not(feature = "shared_api"))] +/// Writes parameters for S3 upload to a parameter file for shell script consumption. +/// +/// Creates a space-separated parameter string with all necessary arguments for the S3 upload +/// process and writes it to the file specified by `S3_UPLOAD_PARAM_FILE`. Used when the +/// `shared_api` feature is not enabled. +/// +/// # Arguments +/// * `dump_paths` - Reference to crash dump path configuration. +/// * `device_data` - Reference to device metadata. +/// * `partner_id` - Partner identifier string. +/// * `enable_ocsp_stapling` - OCSP stapling configuration flag. +/// * `enable_ocsp` - OCSP configuration flag. +/// * `curl_log_option` - Curl logging option string. +/// +/// # Returns +/// * `Ok(())` on success, or an error if file write fails. fn write_uploadtos3params( dump_paths: &DumpPaths, device_data: &DeviceData, @@ -124,21 +227,24 @@ fn write_uploadtos3params( std::fs::write(S3_UPLOAD_PARAM_FILE, params) } -// #[cfg(feature = "shared_api")] -// pub use crate::upload_to_s3::upload_to_s3; - /// Populates a [`DeviceData`] struct with device properties from system files and TR-181. /// -/// This function mirrors the environment setup in `uploadDumps.sh`, reading values from -/// device properties, version file, MAC address, and TR-181 parameters. It uses only -/// setters on the struct for encapsulation and future-proofing. +/// This function reads various device properties from multiple sources: +/// - Device properties file (box type, model number, device type, build type) +/// - Version file (SHA1 value) +/// - Network configuration (MAC address) +/// - System files (T2 enablement, TLS configuration, encryption settings) +/// - TR-181 parameters (portal URL) +/// +/// The collected information is essential for crash upload identification, processing, and routing. /// /// # Arguments /// * `device_data` - Mutable reference to a [`DeviceData`] struct to populate. /// /// # Side Effects -/// - Reads from the filesystem and TR-181. -/// - May set RFC parameters if encryption is enabled. +/// - Reads from filesystem and TR-181 parameters +/// - May set RFC parameters if encryption is enabled +/// - No files are modified pub fn set_device_data(device_data: &mut DeviceData) { // Populate from device.properties let mut box_type = String::new(); @@ -186,17 +292,18 @@ pub fn set_device_data(device_data: &mut DeviceData) { device_data.set_portal_url(portal_url); } -/// Checks if any dump files matching a pattern exist in a directory. +/// Checks if any crash dump files exist in the specified directories. /// -/// This function scans the given directory for files matching the provided wildcard pattern, -/// similar to the shell logic: `[ -e $MINIDUMPS_PATH/*.dmp* ]` or `[ -e $CORE_PATH/*_core*.* ]`. +/// This function examines two types of dumps in their respective directories: +/// - Minidumps: Files containing ".dmp" in their name in the minidumps directory +/// - Core dumps: Files matching the "*_core*.*" pattern in the core dump directory /// /// # Arguments -/// * `dir` - Directory path to search. -/// * `wildcard` - Substring to match in file names (e.g., ".dmp" or "_core"). +/// * `minidumps_path` - Directory path to search for minidump files +/// * `core_path` - Directory path to search for core dump files /// /// # Returns -/// * `true` if at least one matching file exists, `false` otherwise. +/// * `true` if at least one dump file (of either type) exists, `false` otherwise pub fn check_dumps_exist(minidumps_path: &str, core_path: &str) -> bool { let minidumps_dir = std::path::Path::new(minidumps_path); let core_dir = std::path::Path::new(core_path); @@ -222,18 +329,22 @@ pub fn check_dumps_exist(minidumps_path: &str, core_path: &str) -> bool { minidump_exists || core_exists } -/// Determines and sets secure dump paths and flags based on SecureDump enablement. +/// Configures secure or non-secure dump paths based on SecureDump enablement status. +/// +/// This function determines if SecureDump is enabled by checking flag files and TR-181 parameters, +/// then updates the dump paths accordingly: +/// - When enabled: Uses paths under /opt/secure/ +/// - When disabled: Uses standard paths under /opt/ and /var/ /// -/// This function checks for SecureDump enable/disable flags and RFC, and updates the provided -/// `DumpPaths` struct accordingly. It mirrors the logic in `uploadDumps.sh` for handling -/// secure and non-secure dump locations and flags. +/// It also manages the appropriate flag files to maintain the system's secure dump state. /// /// # Arguments -/// * `dump_paths` - Mutable reference to a [`DumpPaths`] struct to update. +/// * `dump_paths` - Mutable reference to a [`DumpPaths`] struct to update with appropriate paths /// /// # Side Effects -/// - Touches or removes SecureDump enable/disable flag files. -/// - Updates dump paths for secure or non-secure operation. +/// - Creates or removes SecureDump enable/disable flag files +/// - Updates all path settings in the provided `dump_paths` struct +/// - Logs the SecureDump status pub fn get_secure_dump_status(dump_paths: &mut DumpPaths) { println!("get_secure_dump_status: Checking SecureDump status..."); let mut is_sec_dump_enabled = false; @@ -268,13 +379,19 @@ pub fn get_secure_dump_status(dump_paths: &mut DumpPaths) { } } -/// Sets the secure dump enabled flag based on file presence or RFC value. +/// Determines if SecureDump is enabled by checking flag files or TR-181 parameters. /// -/// Checks for the presence of SecureDump enable/disable files, or queries the RFC if neither -/// file is present. Updates the provided boolean accordingly. +/// This function implements a priority-based check for SecureDump enablement: +/// 1. If SECUREDUMP_ENABLE_FILE exists, SecureDump is enabled +/// 2. If SECUREDUMP_DISABLE_FILE exists, SecureDump is disabled +/// 3. Otherwise, check the TR-181 parameter for the setting /// /// # Arguments -/// * `is_sec_dump_enabled` - Mutable reference to a boolean to set. +/// * `is_sec_dump_enabled` - Mutable reference to a boolean to set based on the determination +/// +/// # Side Effects +/// - Reads from the filesystem and possibly TR-181 configuration +/// - Modifies the passed boolean reference fn set_secure_dump_flag(is_sec_dump_enabled: &mut bool) { if Path::new(SECUREDUMP_ENABLE_FILE).exists() { *is_sec_dump_enabled = true; @@ -288,15 +405,17 @@ fn set_secure_dump_flag(is_sec_dump_enabled: &mut bool) { } } -/// Returns the lock directory path for a given resource path. +/// Creates a standard lock directory path for a given resource path. /// -/// Appends `.lock.d` as the extension to the provided path to create a unique lock directory. +/// This function converts any path into a corresponding lock directory path +/// by changing its extension to ".lock.d". This lock directory is used +/// to implement a filesystem-based mutex mechanism. /// /// # Arguments -/// * `path` - Path to be locked. +/// * `path` - Path to be locked (can be any file or directory path) /// /// # Returns -/// * `PathBuf` representing the lock directory. +/// * `PathBuf` representing the derived lock directory path #[inline] fn lock_path>(path: P) -> PathBuf { let mut p = path.as_ref().to_path_buf(); @@ -304,28 +423,40 @@ fn lock_path>(path: P) -> PathBuf { p } -/// Checks if another instance is running by testing for the lock directory. +/// Checks if another process instance is running by testing for an existing lock directory. /// -/// # Arguments -/// * `path` - Path to check for lock. +/// This function is used to implement mutual exclusion between process instances. +/// It determines if another instance is running by checking for the existence of +/// a lock directory associated with the given path. /// +/// # Arguments +/// * `path` - Path to check for an existing lock /// # Returns -/// * `true` if the lock directory exists, `false` otherwise. +/// * `true` if a lock directory exists (another instance is running), `false` otherwise #[inline] fn is_another_instance_running>(path: P) -> bool { lock_path(path).is_dir() } -/// Attempts to create a lock directory for the given path. Exits if already locked. +/// Creates a lock directory or exits the process if already locked. /// -/// If another instance is running (lock exists), prints a message and exits the process. -/// Otherwise, creates the lock directory and returns `true` on success. +/// This function implements an "exit on contention" locking strategy: +/// - If another instance is running (lock exists), it logs a message, +/// potentially sends a T2 notification, and exits the process +/// - Otherwise, it creates the lock directory and returns true on success /// /// # Arguments -/// * `path` - Path to lock. +/// * `path` - Path to lock +/// * `is_t2_enabled` - Whether T2 telemetry is enabled (for sending notifications) /// /// # Returns -/// * `true` if lock was created, otherwise exits or returns `false`. +/// * `true` if lock was created successfully +/// * `false` if lock creation failed due to filesystem errors +/// +/// # Side Effects +/// - May exit the process if lock already exists +/// - May send T2 telemetry notification if enabled +/// - Creates a lock directory on the filesystem if successful pub fn create_lock_or_exit>(path: P, is_t2_enabled: bool) -> bool { let lock = lock_path(&path); if is_another_instance_running(&path) { @@ -346,16 +477,22 @@ pub fn create_lock_or_exit>(path: P, is_t2_enabled: bool) -> bool } } -/// Attempts to create a lock directory for the given path, waiting if already locked. +/// Creates a lock directory, waiting and retrying if already locked. /// -/// If another instance is running (lock exists), waits and retries every 2 seconds. -/// Returns `true` if lock is acquired, `false` if creation fails. +/// This function implements a "wait on contention" locking strategy: +/// - If another instance is running (lock exists), it waits and retries every 2 seconds +/// - It continues retrying indefinitely until either the lock is acquired or creation fails /// /// # Arguments -/// * `path` - Path to lock. +/// * `path` - Path to lock /// /// # Returns -/// * `true` if lock was created, `false` otherwise. +/// * `true` if lock was created successfully +/// * `false` if lock creation failed due to filesystem errors +/// +/// # Side Effects +/// - May block thread execution while waiting for lock +/// - Creates a lock directory on the filesystem if successful pub fn create_lock_or_wait>(path: P) -> bool { let lock = lock_path(&path); loop { @@ -375,10 +512,18 @@ pub fn create_lock_or_wait>(path: P) -> bool { } } -/// Removes the lock directory for the given path, if it exists. +/// Removes a lock directory for the given resource path. +/// +/// This function cleans up the lock directory created by `create_lock_or_exit` or +/// `create_lock_or_wait`, effectively releasing the lock. It silently handles the +/// case where the lock directory doesn't exist. /// /// # Arguments -/// * `path` - Path whose lock should be removed. +/// * `path` - Path whose associated lock should be removed +/// +/// # Side Effects +/// - Deletes the lock directory from the filesystem if it exists +/// - Logs any errors that occur during removal pub fn remove_lock>(path: P) { let lock = lock_path(&path); if lock.is_dir() { @@ -427,8 +572,13 @@ pub fn set_log_file(device_data: &DeviceData, log_mod_ts: &str, line: &str) -> S /// If the reboot flag exists, logs a message, sends a T2 notification, and returns `true`. /// Otherwise, returns `false`. /// +/// # Arguments +/// * `is_t2_enabled` - Whether T2 telemetry is enabled (for sending notifications) +/// /// # Returns /// * `true` if the box is rebooting (flag file exists), `false` otherwise. +/// # Side Effects +/// - May send T2 telemetry notification if enabled pub fn is_box_rebooting(is_t2_enabled: bool) -> bool { if Path::new(CRASH_UPLOAD_REBOOT_FLAG).exists() { println!("is_box_rebooting: Skipping Upload, Since the box is rebooting now..."); @@ -481,6 +631,9 @@ pub fn sanitize(input: &str) -> String { /// /// # Returns /// * `true` if uploads are allowed, `false` if still in the denial window. +/// +/// # Side Effects +/// - Reads from the deny upload file on the filesystem pub fn is_recovery_time_reached() -> bool { let path = Path::new(DENY_UPLOAD_FILE); if !path.exists() { @@ -528,6 +681,10 @@ pub fn set_recovery_time() { /// /// # Returns /// * `true` if the upload rate limit is reached, `false` otherwise. +/// +/// # Side Effects +/// - Acquires and releases a lock on the timestamp file +/// - Reads from the timestamp file pub fn is_upload_limit_reached(ts_file: &str) -> bool { create_lock_or_wait(ts_file); @@ -581,6 +738,10 @@ pub fn is_upload_limit_reached(ts_file: &str) -> bool { /// /// # Arguments /// * `dump_paths` - Reference to the dump paths struct. +/// +/// # Side Effects +/// - Deletes the crash loop flag file if it exists +/// - Removes lock directories associated with the dump paths #[inline] fn remove_crash_locks_and_flag(dump_paths: &DumpPaths) { let loop_file = Path::new(CRASH_LOOP_FLAG_FILE); @@ -598,6 +759,10 @@ fn remove_crash_locks_and_flag(dump_paths: &DumpPaths) { /// /// # Arguments /// * `dump_paths` - Reference to the dump paths struct. +/// +/// # Side Effects +/// - Cleans up dump files in the working directory +/// - Removes lock files and crash loop flags pub fn finalize(dump_paths: &DumpPaths) { println!("finalize: ##DEBUG## Skipping cleanup and removing crash locks and flag for debugging purposes"); if false { @@ -618,6 +783,10 @@ pub fn finalize(dump_paths: &DumpPaths) { /// # Arguments /// * `signal` - The signal type (CrashSignal::Term or CrashSignal::Kill). /// * `dump_paths` - Reference to the dump paths struct. +/// +/// # Side Effects +/// - Logs the signal type +/// - Removes crash loop flag and lock files pub fn handle_crash_signal(signal: CrashSignal, dump_paths: &DumpPaths) { match signal { CrashSignal::Term => println!("systemd terminating, Removing the script locks"), @@ -626,7 +795,6 @@ pub fn handle_crash_signal(signal: CrashSignal, dump_paths: &DumpPaths) { remove_crash_locks_and_flag(dump_paths); } -/* UNUSED */ /// Determines if a dump file should be processed/uploaded based on dump type, device type, and file name. /// /// - Always returns `true` for minidumps. @@ -649,6 +817,21 @@ pub fn should_process_dump(dump_name: &str, build_type: &str, file_name: &str) - } } +/// Determines if a file name matches a specified pattern. +/// +/// This function implements shell-like wildcard pattern matching for specific crash-related +/// file patterns. It supports the following patterns: +/// - "*.dmp*": Files containing ".dmp" anywhere in the name +/// - "*.dmp.tgz": Files ending with ".dmp.tgz" +/// - "*core.prog*.gz*": Files containing "core.prog" followed by ".gz" somewhere later +/// - "*.core.tgz": Files ending with ".core.tgz" +/// +/// # Arguments +/// * `name` - The file name to check against the pattern. +/// * `pattern` - The pattern to match against (one of the supported patterns above). +/// +/// # Returns +/// * `true` if the file name matches the pattern, `false` otherwise. fn matches_pattern(name: &str, pattern: &str) -> bool { match pattern { "*.dmp*" => name.contains(".dmp"), @@ -665,11 +848,11 @@ fn matches_pattern(name: &str, pattern: &str) -> bool { } } -/// Counts the number of dump files in a directory matching a given extension substring. +/// Counts the number of dump files in a directory matching a given pattern. /// /// # Arguments /// * `dir` - Directory path to search. -/// * `extn` - Substring to match in file names (e.g., ".dmp" or "core"). +/// * `pattern` - Pattern to match in file names (e.g., "*.dmp*" or "*.core.tgz"). /// /// # Returns /// * `Ok(count)` with the number of matching files, or an error if the directory can't be read. @@ -696,11 +879,14 @@ pub fn get_file_count(dir: &str, pattern: &str) -> std::io::Result { /// or TelemetryOptOut is set. It removes all files matching the given extension or `.tgz`. /// /// # Arguments -/// * `dir` - Directory path to search. +/// * `path` - Directory path to search. /// * `extn` - Extension to match (e.g., "dmp" or "core"). /// /// # Returns /// * `Ok(())` on success, or an error if file operations fail. +/// +/// # Side Effects +/// - Deletes files from the filesystem that match the criteria pub fn remove_pending_dumps(path: &str, extn: &str) -> io::Result<()> { println!("remove_pending_dumps: ###DEBUG### Skipping pending dumps removal for debugging purposes"); if false { @@ -732,6 +918,12 @@ pub fn remove_pending_dumps(path: &str, extn: &str) -> io::Result<()> { /// /// # Arguments /// * `file_path` - Path to the crash dump file (as &str). +/// * `is_t2_enabled` - Whether T2 telemetry is enabled (for sending notifications) +/// +/// # Side Effects +/// - May send multiple T2 telemetry notifications +/// - Logs crash information +/// - Calls `get_crashed_log_file` which may write to log files pub fn process_crash_t2_info(file_path: &str, is_t2_enabled: bool) { println!("process_crash_t2_info: Processing the crash telemetry info"); let file = Path::new(file_path); @@ -783,11 +975,10 @@ pub fn process_crash_t2_info(file_path: &str, is_t2_enabled: bool) { /// /// # Arguments /// * `tgz_file` - Path to the tarball file. -/// * `portal_url` - Crash portal URL. -/// * `crash_portal_path` - Crash portal path. /// /// # Side Effects /// - Renames the file to `.crashloop.dmp.tgz`. +/// - Logs the rename operation and any errors. pub fn mark_as_crash_loop_and_upload(tgz_file: &str) { // portal_url: &str, crash_portal_path: &str) { let tgz_path = Path::new(tgz_file); let new_tgz_name = tgz_path.with_extension("crashloop.dmp.tgz"); @@ -799,11 +990,15 @@ pub fn mark_as_crash_loop_and_upload(tgz_file: &str) { // portal_url: &str, cras /// Finds the oldest file in a directory. /// +/// Scans a directory for files, sorts them by modification time, and returns the oldest one. +/// This is typically used for retention policies and cleanup of old dumps. +/// /// # Arguments /// * `dir` - Directory path to search. /// /// # Returns -/// * `Ok(Some(path))` with the oldest file, or `Ok(None)` if no files found. +/// * `Ok(Some(path))` with the oldest file, or `Ok(None)` if no files found or directory is empty. +/// * `Err` if there was an error reading the directory or file metadata. pub fn find_oldest_dump(dir: &str) -> io::Result> { let mut files: Vec<_> = fs::read_dir(dir)? .filter_map(|entry| entry.ok()) @@ -817,10 +1012,18 @@ pub fn find_oldest_dump(dir: &str) -> io::Result> { /// Saves a dump file, renaming if needed, and ensures only the most recent 5 minidumps are kept. /// +/// This function has two main responsibilities: +/// 1. Renames the dump file to preserve container information (if `new_name` is provided) +/// 2. Enforces a retention policy by keeping only the 5 most recent dump files +/// /// # Arguments -/// * `minidumps_path` - Directory containing minidumps. -/// * `s3_filename` - The current filename. -/// * `new_name` - Optional new name to rename to (to retain container info). +/// * `minidumps_path` - Directory containing minidumps +/// * `s3_filename` - The current filename of the dump +/// * `new_name` - Optional new name to rename to (to retain container info) +/// +/// # Side Effects +/// - May rename files in the minidumps directory +/// - May delete old dump files to maintain the retention limit pub fn save_dump(minidumps_path: &str, s3_filename: &str, new_name: Option<&str>) { if let Some(new_name) = new_name { println!("save_dump: Saving dump with original name to retain container info: {}", basename(new_name)); @@ -851,8 +1054,17 @@ pub fn save_dump(minidumps_path: &str, s3_filename: &str, new_name: Option<&str> /// Logs the current upload timestamp to the timestamp file and truncates it to the last 10 entries. /// +/// This function is used to maintain a record of recent uploads for rate limiting purposes. +/// For production builds, it adds the current time to the timestamp file and calls +/// `truncate_timestamp_file()` to keep only the 10 most recent entries. +/// /// # Arguments -/// * `ts_file` - Path to the timestamp file. +/// * `ts_file` - Path to the timestamp file +/// +/// # Side Effects +/// - Acquires and releases a lock on the timestamp file +/// - Appends the current timestamp to the file +/// - Truncates the file to keep only the 10 most recent entries pub fn log_upload_timestamp(ts_file: &str) { create_lock_or_wait(ts_file); @@ -876,11 +1088,19 @@ pub fn log_upload_timestamp(ts_file: &str) { /// Truncates the timestamp file to the last 10 lines. /// +/// This function maintains a fixed-size history of upload timestamps by keeping +/// only the 10 most recent entries. It reads all lines, selects the 10 newest, +/// and overwrites the file with just those lines. +/// /// # Arguments -/// * `ts_file` - Path to the timestamp file. +/// * `ts_file` - Path to the timestamp file /// /// # Returns -/// * `Ok(())` on success, or an error if file operations fail. +/// * `Ok(())` on success, or an error if file operations fail +/// +/// # Side Effects +/// - Acquires and releases a lock on the timestamp file +/// - Modifies the content of the timestamp file pub fn truncate_timestamp_file(ts_file: &str) -> io::Result<()> { create_lock_or_wait(ts_file); let ts_path = Path::new(ts_file); @@ -901,11 +1121,17 @@ pub fn truncate_timestamp_file(ts_file: &str) -> io::Result<()> { /// Gets the last modified time of a file as a formatted string. /// +/// Retrieves the file's modification timestamp and formats it as +/// "YYYY-MM-DD-HH-MM-SS" for use in log file naming and comparisons. +/// /// # Arguments -/// * `path` - Path to the file. +/// * `path` - Path to the file /// /// # Returns -/// * `Some(String)` with the formatted time, or `None` if not available. +/// * `Some(String)` with the formatted time string, or `None` if: +/// - The path doesn't exist +/// - The path isn't a regular file +/// - The metadata or modification time couldn't be retrieved pub fn get_last_modified_time_of_file(path: &str) -> Option { let path_ref = Path::new(path); if !path_ref.is_file() { @@ -919,11 +1145,22 @@ pub fn get_last_modified_time_of_file(path: &str) -> Option { /// Maps a crashed process to its log files using the logmapper config and writes them to LOG_FILES. /// +/// This function performs several key operations: +/// 1. Extracts the process name from the crashed file path +/// 2. Sends telemetry notifications about the crashed process +/// 3. Looks up associated log files in the LOGMAPPER_FILE configuration +/// 4. Writes the full paths of those log files to the LOG_FILES file +/// /// # Arguments -/// * `file_path` - Path or name of the crashed file. +/// * `file_path` - Path or name of the crashed file +/// * `is_t2_enabled` - Whether T2 telemetry is enabled (for sending notifications) /// /// # Returns -/// * `Ok(())` on success, or an error if file operations fail. +/// * `Ok(())` on success, or an error if file operations fail +/// +/// # Side Effects +/// - May send multiple T2 telemetry notifications +/// - Writes to the LOG_FILES file pub fn get_crashed_log_file(file_path: &str, is_t2_enabled: bool) -> io::Result<()> { let basename = basename(file_path); @@ -984,10 +1221,14 @@ pub fn get_crashed_log_file(file_path: &str, is_t2_enabled: bool) -> io::Result< /// keeping only the newest `MAX_CORE_FILES` files and deleting the rest. /// /// # Arguments -/// * `dir_path` - Directory path as &str. +/// * `dir_path` - Directory path as &str /// /// # Returns -/// * `Ok(())` on success, or an error if file operations fail. +/// * `Ok(())` on success, or an error if file operations fail +/// +/// # Side Effects +/// - Deletes files from the filesystem that exceed the retention limit +/// - Logs the deletion operations or their failure pub fn delete_all_but_most_recent_files(dir_path: &str) -> io::Result<()> { let path = Path::new(dir_path); if !path.is_dir() { @@ -1026,21 +1267,25 @@ pub fn delete_all_but_most_recent_files(dir_path: &str) -> io::Result<()> { Ok(()) } -/// /// Cleans up the working directory by removing old, unfinished, and non-dump files, +/// Cleans up the working directory by removing old, unfinished, and non-dump files, /// and limits the number of dump files to the configured maximum. /// -/// - Removes files matching `*_mac*_dat*` older than 2 days. -/// - On first startup, removes unfinished and non-dump files, and limits dump file count. -/// - Removes version.txt if not uploading on startup. -/// - Uses only `&str` for arguments for efficiency. +/// This function performs several cleanup operations: +/// - Removes files matching `*_mac*_dat*` older than 2 days +/// - On first startup, removes unfinished and non-dump files, and limits dump file count +/// - Removes version.txt if not uploading on startup /// /// # Arguments -/// * `work_dir` - Working directory path. -/// * `dump_name` - Dump type ("coredump" or "minidump"). -/// * `dump_extn` - Dump file extension pattern (e.g., "*.dmp*"). +/// * `work_dir` - Working directory path +/// * `dump_name` - Dump type ("coredump" or "minidump") +/// * `dump_extn` - Dump file extension pattern (e.g., "*.dmp*") /// /// # Returns -/// * `Ok(())` on success, or an error if file operations fail. +/// * `Ok(())` on success, or an error if file operations fail +/// +/// # Side Effects +/// - Deletes files from the filesystem based on various criteria +/// - Creates a flag file to indicate cleanup has been performed pub fn cleanup(work_dir: &str, dump_name: &str, dump_extn: &str) -> std::io::Result<()> { let work_dir_path = Path::new(work_dir); @@ -1127,9 +1372,13 @@ pub fn cleanup(work_dir: &str, dump_name: &str, dump_extn: &str) -> std::io::Res /// Returns the usage percent of the /tmp partition by invoking `df`. /// /// Mimics the shell logic: `df -h /tmp | grep '\tmp' | awk '{print $5}'` +/// This is used to determine if there's enough space to copy log files to /tmp. /// /// # Returns -/// * `Some(u8)` with the usage percent, or `None` if it cannot be determined. +/// * `Some(u8)` with the usage percent (0-100), or `None` if: +/// - The `df` command fails to execute +/// - The output doesn't contain expected data +/// - The percentage value cannot be parsed #[inline] fn get_tmp_usage_percent() -> Option { let output = Command::new("df") @@ -1155,17 +1404,23 @@ fn get_tmp_usage_percent() -> Option { /// Adds crashed log files to the minidump tarball, processing each log file as needed. /// -/// For each file in `log_files`, if it exists, extracts the last N lines (N=500 for prod, 5000 otherwise), -/// writes them to a sanitized process log file, and logs the action. -/// After processing, removes the original log files. -/// This function is reusable and accepts any slice of log file paths. +/// For each log file: +/// 1. Gets the file's last modified timestamp +/// 2. Creates a sanitized process log file name using device metadata +/// 3. Extracts the last N lines (N=500 for prod, 5000 otherwise) +/// 4. Writes those lines to a new file in the working directory /// /// # Arguments -/// * `device_data` - Reference to device metadata (for log file naming). -/// * `log_files` - Slice of log file paths (`&[&str]`). +/// * `device_data` - Reference to device metadata (for log file naming) +/// * `log_files` - Slice of log file paths to process and add +/// * `working_dir` - Directory where processed log files will be written /// /// # Returns -/// * `Ok(())` on success, or an error if file operations fail. +/// * `Ok(())` on success, or an error if file operations fail +/// +/// # Side Effects +/// - Creates new log files in the working directory +/// - Logs each file addition pub fn add_crashed_log_file(device_data: &DeviceData, log_files: &[&str], working_dir: &str) -> io::Result<()> { let line_count = if device_data.build_type == "prod" { 500 } else { 5000 }; @@ -1195,18 +1450,22 @@ pub fn add_crashed_log_file(device_data: &DeviceData, log_files: &[&str], worki Ok(()) } - /// Copies log files to a temporary directory under /tmp if there is enough free space. /// -/// If /tmp usage is below the threshold (70%), copies each log file to `/tmp/`. -/// Otherwise, returns the original file paths. Returns the paths of the files to use for archiving. +/// This function implements a space-aware strategy for log file handling: +/// - If /tmp usage is below 70%, copies each log file to a temp directory +/// - Otherwise, uses the original file paths to avoid filling /tmp /// /// # Arguments -/// * `tmp_dir_name` - Name for the temporary directory under /tmp. -/// * `logfiles` - Slice of log file paths (`&[&str]`). +/// * `tmp_dir_name` - Name for the temporary directory under /tmp +/// * `logfiles` - Slice of log file paths to potentially copy /// /// # Returns -/// * `Vec`: Paths to use for archiving (either in /tmp or original). +/// * `Vec` containing paths to use for archiving (either in /tmp or original) +/// +/// # Side Effects +/// - May create a directory in /tmp +/// - May copy files to the temporary directory pub fn copy_log_files_to_tmp(tmp_dir_name: &str, logfiles: &[&str]) -> Vec { let tmp_dir = format!("/tmp/{}", tmp_dir_name); let usage_percent = get_tmp_usage_percent().unwrap_or(0); @@ -1247,6 +1506,25 @@ pub fn copy_log_files_to_tmp(tmp_dir_name: &str, logfiles: &[&str]) -> Vec std::io::Result<()> { // Call the Rust implementation from crash-upload-cpc @@ -1286,11 +1564,19 @@ pub fn upload_to_s3_lib(args: &[&str], dump_paths: &DumpPaths, device_data: &Dev /// Calls the uploadDumpsToS3.sh script with the given arguments if it exists. /// +/// This function is only available when the "shared_api" feature is NOT enabled. +/// It delegates the upload responsibility to a shell script. +/// /// # Arguments -/// * `args` - Arguments to pass to the script. +/// * `args` - Arguments to pass to the script, with args[0] being the file to upload /// /// # Returns -/// * `Ok(exit_status)` if the script ran, or an error if not found or failed. +/// * `Ok(exit_status)` if the script ran successfully +/// * `Err` if the script wasn't found or failed with a non-zero exit code +/// +/// # Side Effects +/// - Executes an external shell script +/// - The shell script will make network requests and manipulate files #[cfg(not(feature = "shared_api"))] pub fn upload_to_s3_script(args: &[&str]) -> std::io::Result { if Path::new(S3_UPLOAD_SCRIPT).exists() { @@ -1315,8 +1601,18 @@ pub fn upload_to_s3_script(args: &[&str]) -> std::io::Result Option { let script = "/lib/rdk/utils.sh"; if Path::new(script).exists() { @@ -1337,26 +1633,25 @@ pub fn get_privacy_control_mode() -> Option { /// Processes all dump files in the working directory: sanitizes, compresses, and uploads or saves them. /// -/// This function mirrors the main dump processing logic from `uploadDumps.sh`, including: -/// - Sanitizing and renaming dump files -/// - Processing telemetry info for minidumps -/// - Skipping files that are already tarballs -/// - Using `crash_ts` for log/tarball naming -/// - Compressing dumps and associated log files into tarballs -/// - Handling fallback if compression fails -/// - Cleaning up temporary files and logs -/// - Delegating tarball upload or save logic to `handle_tarballs` +/// This function implements the main dump processing workflow: +/// 1. Finds all dump files in the working directory +/// 2. For each file: +/// - Sanitizes and renames it +/// - Processes telemetry info for minidumps +/// - Creates a tarball with the dump and relevant log files +/// - Cleans up temporary files +/// 3. Hands off tarballs for upload or local storage /// /// # Arguments -/// * `device_data` - Reference to device metadata (for naming and telemetry). -/// * `dump_paths` - Reference to dump paths and configuration. -/// * `crash_ts` - Crash timestamp string for naming files (faithful to shell script). -/// * `no_network` - If true, skips upload and just saves the dump locally. +/// * `device_data` - Reference to device metadata (for naming and telemetry) +/// * `dump_paths` - Reference to dump paths and configuration +/// * `crash_ts` - Crash timestamp string for naming files +/// * `no_network` - If true, skips upload and just saves the dump locally /// /// # Side Effects -/// - Modifies files in the working directory (renames, compresses, deletes). -/// - May create or remove log files and temporary directories. -/// - Calls `handle_tarballs` for tarball upload/save logic. +/// - Modifies files in the working directory (renames, compresses, deletes) +/// - May create or remove log files and temporary directories +/// - Calls other functions that may upload files or send telemetry pub fn process_dumps( device_data: &DeviceData, dump_paths: &DumpPaths, @@ -1574,13 +1869,20 @@ pub fn process_dumps( /// Compresses the given files into a tarball using the `tar` command. /// +/// This function creates a gzipped tarball containing both the primary dump files +/// and any associated log files by executing the system `tar` command. +/// /// # Arguments -/// * `tgz_file` - Output tarball file name. -/// * `main_files` - Main files to include. -/// * `extra_files` - Additional files to include. +/// * `tarball_path` - Output tarball file path +/// * `dump_files` - Primary dump files to include in the tarball +/// * `log_files` - Additional log files to include in the tarball /// /// # Returns -/// * `Ok(())` if compression succeeded, error otherwise. +/// * `Ok(())` if compression succeeded, error otherwise +/// +/// # Side Effects +/// - Creates a new tarball file at the specified path +/// - Executes the system `tar` command #[inline] pub fn compress_files( tarball_path: &str, @@ -1606,9 +1908,16 @@ pub fn compress_files( } } - - /// Checks if the given file is a tarball (ends with .tgz). +/// +/// A simple utility function that examines the file extension to determine +/// if a file is a compressed tarball archive. +/// +/// # Arguments +/// * `file` - Reference to a `Path` to check +/// +/// # Returns +/// * `true` if the file has a .tgz extension, `false` otherwise #[inline] fn is_tarball(file: &Path) -> bool { file.extension().map(|e| e == "tgz").unwrap_or(false) @@ -1616,11 +1925,20 @@ fn is_tarball(file: &Path) -> bool { /// Sanitizes the file name and renames the file if needed. /// +/// This function sanitizes the file path by removing unsafe characters, then +/// renames the file on disk if the sanitized name differs from the original. +/// This ensures filenames are compatible with upload and storage systems. +/// /// # Arguments -/// * `file` - Path to the file. +/// * `file` - Reference to the `Path` of the file to sanitize /// /// # Returns -/// * `Ok(new_path)` with the sanitized path, or error. +/// * `Ok(PathBuf)` containing the sanitized path (which may be the same as the +/// original if no sanitization was needed) +/// * `Err` if the rename operation fails +/// +/// # Side Effects +/// - May rename the file on the filesystem if sanitization changes the name #[inline] fn sanitize_and_rename(file: &Path) -> io::Result { let orig = file.to_string_lossy(); @@ -1635,11 +1953,18 @@ fn sanitize_and_rename(file: &Path) -> io::Result { /// Removes all .log and .txt files from the given directory. /// +/// This function is used to clean up log files after they've been packaged +/// into a tarball. It scans the directory and deletes any file with a .log or +/// .txt extension. +/// /// # Arguments -/// * `working_dir` - Directory to clean. +/// * `working_dir` - Directory path to clean /// /// # Returns -/// * `Ok(())` on success, or error. +/// * `Ok(())` on success, or an error if directory operations fail +/// +/// # Side Effects +/// - Deletes .log and .txt files from the specified directory #[inline] fn remove_logs(working_dir: &str) -> io::Result<()> { for entry in fs::read_dir(working_dir)? { @@ -1656,14 +1981,18 @@ fn remove_logs(working_dir: &str) -> io::Result<()> { Ok(()) } -/// Finds all files in a directory matching the given extension substring. +/// Finds all files in a directory matching the given pattern. +/// +/// This function scans a directory and collects all files whose names match +/// the specified pattern (using the `matches_pattern` function). /// /// # Arguments -/// * `working_dir` - Directory to search. -/// * `dumps_extn` - Substring to match in file names. +/// * `dir` - Directory path to search +/// * `pattern` - Pattern to match in file names (e.g., "*.dmp*", "*.core.tgz") /// /// # Returns -/// * `Ok(Vec)` with matching files, or error. +/// * `Ok(Vec)` containing paths to all matching files +/// * `Err` if there was an error reading the directory pub fn find_dump_files(dir: &str, pattern: &str) -> std::io::Result> { let dir_path = std::path::Path::new(dir); let mut files = Vec::new(); @@ -1684,9 +2013,21 @@ pub fn find_dump_files(dir: &str, pattern: &str) -> std::io::Result std::io::Result<()> { let tarball_str = tarball.to_string_lossy(); let s3_filename = tarball.file_name().and_then(|n| n.to_str()).unwrap_or(""); @@ -1783,6 +2140,15 @@ fn handle_single_tarball(device_data: &DeviceData, dump_paths: &DumpPaths, tarba } /// Returns true if privacy mode is DO_NOT_SHARE (from utils.sh). +/// +/// This function checks if the device's privacy mode is set to "DO_NOT_SHARE", +/// which indicates that no data should be uploaded to the cloud. +/// +/// # Returns +/// * `true` if privacy mode is "DO_NOT_SHARE", `false` otherwise +/// +/// # Side Effects +/// - Calls `get_privacy_control_mode()` which executes a shell command #[inline] fn is_privacy_mode_do_not_share() -> bool { matches!(get_privacy_control_mode().as_deref(), Some("DO_NOT_SHARE")) @@ -1790,12 +2156,19 @@ fn is_privacy_mode_do_not_share() -> bool { /// Renames a tarball file to a sanitized name for S3 upload. /// +/// This function replaces special characters in the tarball filename that might +/// cause problems during S3 upload. It performs the actual filesystem rename +/// operation. +/// /// # Arguments -/// * `tarball` - Path to the original tarball. -/// * `sanitized_name` - Sanitized file name. +/// * `tarball` - Path to the original tarball +/// * `sanitized_name` - Sanitized file name to use /// /// # Returns -/// * `Ok(())` on success, or error. +/// * `Ok(())` on success, or an error if the rename fails +/// +/// # Side Effects +/// - Renames a file on the filesystem fn rename_tarball_for_s3(tarball: &Path, sanitized_name: &str) -> std::io::Result<()> { let parent = tarball.parent().unwrap_or_else(|| Path::new("")); let orig_path = parent.join(tarball.file_name().unwrap()); @@ -1805,12 +2178,23 @@ fn rename_tarball_for_s3(tarball: &Path, sanitized_name: &str) -> std::io::Resul /// Uploads a tarball to S3, retrying up to 3 times. /// +/// This function attempts to upload a tarball to S3, with up to 3 retry attempts +/// if the upload fails. It handles both the Rust and shell script upload implementations +/// based on the feature flags, and logs the upload status. +/// /// # Arguments -/// * `s3_filename` - Name of the tarball file to upload. -/// * `dump_paths` - Reference to dump paths and config. +/// * `s3_full_path` - Full path to the tarball file to upload +/// * `dump_paths` - Reference to dump paths and configuration +/// * `device_data` - Reference to device metadata for upload identification /// /// # Returns -/// * `true` if upload succeeded, `false` otherwise. +/// * `true` if any upload attempt succeeded, `false` if all attempts failed +/// +/// # Side Effects +/// - Makes network requests to S3 +/// - May create temporary files for upload parameters +/// - May send telemetry notifications +/// - Logs upload status fn upload_tarball_with_retries( s3_full_path: &str, dump_paths: &DumpPaths, @@ -1883,11 +2267,21 @@ fn upload_tarball_with_retries( /// Cleans up after upload: removes tarball if successful, logs timestamp, or saves/removes on failure. /// +/// This function handles the post-upload cleanup process, with different behavior based on +/// whether the upload succeeded: +/// - On success: Removes the tarball and logs the upload timestamp +/// - On failure: For minidumps, saves the dump locally; for other types, removes the file +/// /// # Arguments -/// * `upload_success` - Whether the upload succeeded. -/// * `dump_paths` - Reference to dump paths and config. -/// * `tarball` - Path to the tarball file. -/// * `s3_filename` - Name of the tarball file. +/// * `upload_success` - Whether the upload succeeded +/// * `dump_paths` - Reference to dump paths and configuration +/// * `tarball` - Path to the tarball file +/// * `s3_filename` - Name of the tarball file +/// +/// # Side Effects +/// - May delete files from the filesystem +/// - May log timestamps to the timestamp file +/// - May save dumps to the minidumps directory fn post_upload_cleanup(upload_success: bool, dump_paths: &DumpPaths, tarball: &Path, s3_filename: &str) { let parent = tarball.parent().unwrap_or_else(|| Path::new("")); let s3_path = parent.join(s3_filename); diff --git a/crash-upload/src/main.rs b/crash-upload/src/main.rs index d0754b9..55b30f8 100644 --- a/crash-upload/src/main.rs +++ b/crash-upload/src/main.rs @@ -1,3 +1,36 @@ +//! Crash Upload Binary - Main Entry Point +//! +//! This module provides the entry point for the crash upload utility, which detects, +//! processes, and uploads crash dumps (minidumps and coredumps) to cloud storage. +//! +//! # Command-Line Usage +//! +//! The binary accepts three required command-line arguments: +//! ``` +//! crash-upload +//! ``` +//! +//! - `dump_flag`: Integer (1 for coredump processing, any other value for minidump) +//! - `upload_flag`: String ("secure" for secure paths, any other value for standard paths) +//! - `wait_for_lock`: String ("wait_for_lock" to wait if locked, any other value to exit) +//! +//! # Workflow +//! +//! The main workflow consists of: +//! 1. Configuration and initialization (loading device data, setting paths) +//! 2. Early exit checks (no dumps exist, box is rebooting) +//! 3. Network and system time verification +//! 4. Dump processing (finding, compressing, and uploading dumps) +//! 5. Cleanup and finalization +//! +//! # Integration Points +//! +//! - Device property system (for device metadata) +//! - Filesystem (for finding and manipulating dump files) +//! - Network subsystem (for upload capabilities) +//! - System time service (for timestamp generation) +//! - Mutex-like locking (for preventing concurrent execution) +//! // standard library imports use std::path::Path; use std::time::Duration; @@ -13,6 +46,29 @@ mod crashupload_utils; // Utility crates use crashupload_utils::*; +/// Main entry point for the crash upload binary. +/// +/// This function implements the complete crash upload workflow: +/// 1. Parse command-line arguments and initialize configuration +/// 2. Configure paths based on dump type (minidump or coredump) +/// 3. Check for early exit conditions (no dumps, box rebooting) +/// 4. Wait for network and system time if needed +/// 5. Process and upload dumps with retry capability +/// 6. Clean up and exit +/// +/// # Command-Line Arguments +/// +/// * `args[1]`: Dump flag (1 for coredump, any other value for minidump) +/// * `args[2]`: Upload flag ("secure" for secure paths, any other value for standard) +/// * `args[3]`: Wait flag ("wait_for_lock" to wait if locked, any other to exit) +/// +/// # Side Effects +/// +/// - Creates and removes lock files +/// - May upload files to cloud storage +/// - May delete or compress files on the filesystem +/// - Logs progress to stdout +/// - May exit process early under certain conditions fn main() { println!("main(): Starting Crash Upload Binary..."); diff --git a/crates/curl-interface/src/lib.rs b/crates/curl-interface/src/lib.rs index b93cf3f..35b19ea 100644 --- a/crates/curl-interface/src/lib.rs +++ b/crates/curl-interface/src/lib.rs @@ -12,3 +12,5 @@ mod tests { assert_eq!(result, 4); } } + +// Placeholder for future functionality \ No newline at end of file diff --git a/crates/json-parser-interface/src/lib.rs b/crates/json-parser-interface/src/lib.rs index b93cf3f..a5e0f28 100644 --- a/crates/json-parser-interface/src/lib.rs +++ b/crates/json-parser-interface/src/lib.rs @@ -12,3 +12,5 @@ mod tests { assert_eq!(result, 4); } } + +// TODO: Placeholder for future functionality \ No newline at end of file diff --git a/crates/platform-interface/src/lib.rs b/crates/platform-interface/src/lib.rs index cf602a1..e06fede 100644 --- a/crates/platform-interface/src/lib.rs +++ b/crates/platform-interface/src/lib.rs @@ -1,16 +1,29 @@ //! Platform interface library. //! //! This crate provides APIs for interacting with platform features such as RFC (TR-181) and Telemetry2 (T2). -//! It exposes convenient functions for RFC parameter access and telemetry marker notification. - -mod rfc_api; -mod t2_api; +//! It serves as an abstraction layer between the application and underlying platform-specific mechanisms. +//! +//! ## Modules +//! +//! - [`rfc_api`]: Functions for interacting with TR-181 parameters through Remote Feature Control (RFC) +//! Used for reading and writing device configuration values at runtime. +//! +//! - [`t2_api`]: Functions for sending telemetry markers to the Telemetry2 system +//! Used for reporting events and metrics to the telemetry collection service. +//! +//! ## Usage +//! +//! This library re-exports all functions from its modules at the crate root for convenience. +//! This allows for simpler imports like `use platform_interface::set_rfc_param` instead of +//! `use platform_interface::rfc_api::set_rfc_param`. +pub mod rfc_api; +pub mod t2_api; /// Platform interface library version. pub const PLATFORM_LIB_VER: &str = "v1.0"; // Re-export RFC API functions for external use. -pub use rfc_api::{get_rfc_param, set_rfc_param, dmcli_get}; +pub use rfc_api::*; // Re-export T2 API functions for external use. -pub use t2_api::{t2_count_notify, t2_val_notify}; +pub use t2_api::*; diff --git a/crates/platform-interface/src/rfc_api.rs b/crates/platform-interface/src/rfc_api.rs index a936e17..379e217 100644 --- a/crates/platform-interface/src/rfc_api.rs +++ b/crates/platform-interface/src/rfc_api.rs @@ -2,6 +2,20 @@ //! //! This module provides functions to get and set TR-181 parameters using the `tr181` binary. //! It is used for interacting with device configuration at runtime. +//! +//! ## TR-181 and RFC +//! TR-181 is a standard for device data model used in broadband networks. Remote Feature +//! Control (RFC) allows configuring device behavior through standardized parameters. +//! This module abstracts the interaction with these parameters through simple get/set functions. +//! +//! ## Key Functions +//! - [`set_rfc_param`]: Set a TR-181 parameter to a specified value +//! - [`get_rfc_param`]: Get the value of a TR-181 parameter +//! - [`dmcli_get`]: Alternative method to get parameter values using the dmcli utility +//! +//! ## Common Parameter Paths +//! TR-181 parameters typically follow a hierarchical structure such as: +//! `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature..Enable` use std::path::Path; use std::process::Command; @@ -98,7 +112,28 @@ pub fn get_rfc_param>(rfc: R, res: &mut String) -> bool { } } - +/// Retrieves a parameter value using the `dmcli` command-line utility. +/// +/// This function executes the `dmcli` command with the "eRT getv" arguments to retrieve +/// the value of the specified parameter. It then parses the output to extract just the +/// parameter value from the response string. +/// +/// # Arguments +/// * `param` - The parameter name to retrieve. +/// * `result` - Mutable reference to a string where the retrieved value will be stored. +/// +/// # Example +/// ``` +/// use platform_interface::rfc_api::dmcli_get; +/// let mut value = String::new(); +/// dmcli_get("Device.DeviceInfo.ModelName", &mut value); +/// println!("Model name: {}", value); +/// ``` +/// +/// # Notes +/// The function specifically looks for lines containing "string" in the output +/// and extracts the value using string manipulation equivalent to: +/// `grep string | cut -d":" -f3- | cut -d" " -f2- | tr -d ' '` pub fn dmcli_get(param: &str, result: &mut String) { let output = Command::new("dmcli") .args(["eRT", "getv", param]) diff --git a/crates/platform-interface/src/t2_api.rs b/crates/platform-interface/src/t2_api.rs index e79d28a..3038ea9 100644 --- a/crates/platform-interface/src/t2_api.rs +++ b/crates/platform-interface/src/t2_api.rs @@ -2,7 +2,23 @@ //! //! This module provides functions to notify T2 markers with count or value semantics //! by invoking the `telemetry2_0_client` binary if present on the system. - +//! +//! ## Telemetry2 Overview +//! +//! Telemetry2 is a platform component used for collecting and aggregating operational +//! metrics from devices in the field. These metrics help monitor system health, track +//! feature usage, and identify potential issues. +//! +//! ## Marker Types +//! +//! This module supports two types of telemetry markers: +//! - **Count markers**: Simple counters that increment a value on the server +//! - **Value markers**: Send specific values or strings to the telemetry system +//! +//! ## Usage Notes +//! +//! The telemetry client must be installed at `/usr/bin/telemetry2_0_client` for these +//! functions to work. If the client is not found, the functions will return `false`. use std::path::Path; use std::process::Command; @@ -54,12 +70,21 @@ pub fn t2_count_notify, C: AsRef>(marker: M, count: Option /// Notify a telemetry marker with one or more values (comma-separated). /// +/// This function invokes the T2 client with the given marker and values joined +/// with commas. It's used for reporting specific values rather than simple counts. +/// /// # Arguments /// * `marker` - The telemetry marker name. /// * `values` - The values to associate with the marker (comma-separated). /// /// # Returns /// * `true` if the notification was successfully sent, `false` otherwise. +/// +/// # Example +/// ``` +/// use platform_interface::t2_api::t2_val_notify; +/// t2_val_notify("SYST_INFO_crashUploadStatus", &["success", "minidump"]); +/// ``` pub fn t2_val_notify>(marker: M, values: &[&str]) -> bool { let t2_client = t2_msg_client_path(); if t2_client.exists() { diff --git a/crates/utils/src/command.rs b/crates/utils/src/command.rs index 01bd129..d666c17 100644 --- a/crates/utils/src/command.rs +++ b/crates/utils/src/command.rs @@ -1,7 +1,11 @@ -//! File operation helpers. +//! File and system operation utilities. //! -//! This module provides utility functions for common file operations such as -//! creating files (touch), removing files, and recursively removing directories. +//! This module provides utility functions for common operations: +//! - File operations such as creating files (touch) and removing files/directories +//! - System operations such as flushing system logs +//! +//! These functions provide Rust implementations of common shell operations +//! to avoid spawning external processes when possible. use std::fs::{self, OpenOptions}; use std::path::Path; @@ -11,6 +15,9 @@ use crate::get_property_value_from_file; /// Creates a file if it does not exist, or updates its modification time if it does (like Unix `touch`). /// +/// This function creates an empty file at the specified path if it doesn't exist, +/// or updates the modification time if the file already exists. +/// /// # Arguments /// * `path` - Path to the file to touch. /// @@ -26,6 +33,9 @@ pub fn touch>(path: P) { /// Recursively removes a directory and its contents, or removes a file if the path is not a directory. /// Ignores errors if the path does not exist. /// +/// This function behaves like the Unix `rm -rf` command, removing files or directories +/// regardless of their contents, and suppressing errors for non-existent paths. +/// /// # Arguments /// * `path` - Path to the directory or file to remove. /// @@ -45,11 +55,22 @@ pub fn rm_rf>(path: P) { }; } -/// Flushes system logs, mimicking the flushLogger shell function. +/// Flushes system logs to ensure they're written to disk. +/// +/// This function implements the behavior of the flushLogger shell function: +/// - Logs a message indicating the function was called +/// - Flushes journald buffers if the system uses journald +/// - Calls dumpLogs.sh if SYSLOG_NG_ENABLED is not set to "true" in device.properties +/// +/// # Example +/// ``` +/// use utils::flush_logger; +/// flush_logger(); +/// ``` /// -/// - Logs a message using println!(). -/// - Flushes journald buffers if available. -/// - Calls dumpLogs.sh if SYSLOG_NG_ENABLED is not true in device.properties. +/// # Notes +/// This function is primarily used to ensure logs are persisted before potentially +/// disruptive operations like reboots or crashes. pub fn flush_logger() { println!("flush_logger is called"); @@ -60,7 +81,7 @@ pub fn flush_logger() { // Check SYSLOG_NG_ENABLED from device.properties let mut syslog_ng_enabled = String::new(); - let _ = get_property_value_from_file("/etc/device.properties", "SYSLOG_NG_ENABLED", &mut syslog_ng_enabled); + let _ = get_property_value_from_file( "/etc/device.properties", "SYSLOG_NG_ENABLED", &mut syslog_ng_enabled); if syslog_ng_enabled.trim() != "true" { let _ = Command::new("nice") .args(["-n", "19", "/lib/rdk/dumpLogs.sh"]) diff --git a/crates/utils/src/device_info.rs b/crates/utils/src/device_info.rs index 9b38fc2..208dd26 100644 --- a/crates/utils/src/device_info.rs +++ b/crates/utils/src/device_info.rs @@ -1,7 +1,8 @@ //! Device information utilities. //! //! This module provides functions to retrieve device properties such as the MAC address, -//! version file SHA1, and arbitrary property values from property files. +//! version file SHA1, and arbitrary property values from property files. These utilities +//! are essential for device identification and configuration in the crash upload system. use std::fs::File; use std::io::{self, BufRead, BufReader, Read}; @@ -9,13 +10,18 @@ use std::path::Path; use std::process::Command; /// Path to the file containing the device MAC address. +/// This file is expected to contain the device's MAC address in colon-separated format. pub const DEVICE_MAC_FILE: &str = "/tmp/.macAddress"; /// Path to the file containing the image version. +/// This file contains version information for the device firmware/software. pub const VERSION_FILE: &str = "/version.txt"; /// Retrieves the value for a given key from a property file. /// +/// Property files are expected to contain key-value pairs in the format `KEY=VALUE`. +/// This function searches for the specified key and returns its associated value. +/// /// # Arguments /// * `path` - Path to the property file. /// * `key` - The property key to search for. @@ -58,6 +64,9 @@ pub fn get_property_value_from_file, K: AsRef>(path: P, key: /// Calculates the SHA1 hash of the version file and stores it in `sha1_val`. /// +/// This function executes the `sha1sum` command on the VERSION_FILE constant +/// and parses the output to extract the hash. +/// /// # Arguments /// * `sha1_val` - Mutable reference to a string to store the SHA1 hash. /// @@ -85,19 +94,32 @@ pub fn get_sha1_value(sha1_val: &mut String) -> bool { /// Reads the device MAC address from the device MAC file and stores it in `mac`. /// +/// This function opens the file located at `DEVICE_MAC_FILE` ("/tmp/.macAddress"), +/// reads its contents, and processes the MAC address by removing any colons. +/// The processed MAC address is then stored in the provided string reference. +/// /// # Arguments -/// * `mac` - Mutable reference to a string to store the MAC address (colons removed). +/// * `mac` - Mutable reference to a string where the processed MAC address will be stored. +/// The function will clear any existing content in this string. /// /// # Returns -/// * `Ok(())` if the MAC address was successfully read, or an `io::Error` otherwise. +/// * `Ok(())` if the MAC address was successfully read and processed +/// * `Err(io::Error)` if the file cannot be opened or read /// /// # Example /// ``` +/// use utils::device_info::get_device_mac; +/// /// let mut mac = String::new(); -/// if get_device_mac(&mut mac).is_ok() { -/// println!("MAC: {}", mac); +/// match get_device_mac(&mut mac) { +/// Ok(()) => println!("Device MAC: {}", mac), +/// Err(e) => eprintln!("Failed to get MAC address: {}", e), /// } /// ``` +/// +/// # Note +/// The function removes all colons from the MAC address, converting a format +/// like "00:11:22:33:44:55" to "001122334455". pub fn get_device_mac(mac: &mut String) -> io::Result<()> { let mut mac_val = String::new(); let mut file = File::open(DEVICE_MAC_FILE)?; diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index d1dbd35..f4a7004 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -1,10 +1,17 @@ -//! Utilities library. +//! Utilities library for the crash upload system. //! -//! This crate provides utility functions for file operations, device information retrieval, -//! and other helpers used throughout the crash upload system. +//! This crate provides various utility functions and helpers used throughout the crash upload system: +//! +//! - **File operations**: Functions for creating files, removing files/directories, and other +//! filesystem operations (see the [`command`] module) +//! +//! - **Device information**: Utilities for retrieving device properties such as MAC address, +//! version information, and config values from property files (see the [`device_info`] module) +//! +//! Most commonly used functions are re-exported at the crate root for convenience. -mod command; -mod device_info; +pub mod command; +pub mod device_info; /// Utilities library version. pub const UTILS_LIB_VER: &str = "v1.0"; From 0a7f61d4f90986b6260714832280bc71513eb73f Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Thu, 26 Jun 2025 19:55:47 +0530 Subject: [PATCH 61/66] Fix Doctest Signed-off-by: gomathishankar37 --- crates/platform-interface/src/t2_api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/platform-interface/src/t2_api.rs b/crates/platform-interface/src/t2_api.rs index 3038ea9..6ebab45 100644 --- a/crates/platform-interface/src/t2_api.rs +++ b/crates/platform-interface/src/t2_api.rs @@ -46,7 +46,7 @@ fn t2_msg_client_path() -> &'static Path { /// # Example /// ``` /// use platform_interface::t2_api::t2_count_notify; -/// t2_count_notify("SYST_INFO_minidumpUpld", None); +/// t2_count_notify("SYST_INFO_minidumpUpld", None::<&str>); /// ``` pub fn t2_count_notify, C: AsRef>(marker: M, count: Option) -> bool { let t2_client = t2_msg_client_path(); From 775fd0cc96f137c82476e29a12bcf0dbea3d1ec3 Mon Sep 17 00:00:00 2001 From: gomathishankar37 Date: Thu, 26 Jun 2025 20:01:56 +0530 Subject: [PATCH 62/66] Fix doctest Signed-off-by: gomathishankar37 --- crates/utils/src/device_info.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/utils/src/device_info.rs b/crates/utils/src/device_info.rs index 208dd26..8db3bd9 100644 --- a/crates/utils/src/device_info.rs +++ b/crates/utils/src/device_info.rs @@ -32,6 +32,7 @@ pub const VERSION_FILE: &str = "/version.txt"; /// /// # Example /// ``` +/// use utils::get_property_value_from_file; /// let mut value = String::new(); /// let found = get_property_value_from_file("/opt/device.properties", "MODEL_NUM", &mut value); /// if found { @@ -75,6 +76,7 @@ pub fn get_property_value_from_file, K: AsRef>(path: P, key: /// /// # Example /// ``` +/// use utils::get_sha1_value; /// let mut sha1 = String::new(); /// if get_sha1_value(&mut sha1) { /// println!("SHA1: {}", sha1); From de75641c7a077f923e2e4808fed90ca85069b946 Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Thu, 26 Jun 2025 20:07:54 +0530 Subject: [PATCH 63/66] Update L1-Test.yaml --- .github/workflows/L1-Test.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/L1-Test.yaml b/.github/workflows/L1-Test.yaml index 87f6111..1b99488 100644 --- a/.github/workflows/L1-Test.yaml +++ b/.github/workflows/L1-Test.yaml @@ -34,6 +34,7 @@ jobs: cargo tarpaulin --all-features --workspace --timeout 120 \ --out xml --output-dir ./coverage/ \ --exclude-files "tests/*" "benches/*" "**/tests.rs" "**/test_*.rs" \ + --skip-clean \ --verbose - name: Upload coverage to Codecov @@ -51,4 +52,4 @@ jobs: path: coverage/ - name: Clean target directory (ensure it's not persisted) - run: rm -rf target/ \ No newline at end of file + run: rm -rf target/ From 86018a7159ee540512c32fb3f49e6920ee276ea2 Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Fri, 27 Jun 2025 09:44:37 +0530 Subject: [PATCH 64/66] Update L1-Test.yaml --- .github/workflows/L1-Test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/L1-Test.yaml b/.github/workflows/L1-Test.yaml index 1b99488..ff16363 100644 --- a/.github/workflows/L1-Test.yaml +++ b/.github/workflows/L1-Test.yaml @@ -31,7 +31,7 @@ jobs: - name: Run tests with tarpaulin for coverage run: | - cargo tarpaulin --all-features --workspace --timeout 120 \ + sudo -E cargo tarpaulin --all-features --workspace --timeout 120 \ --out xml --output-dir ./coverage/ \ --exclude-files "tests/*" "benches/*" "**/tests.rs" "**/test_*.rs" \ --skip-clean \ From 7a05f1de63d6c736711d1d8da05d0e118cfef2f8 Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Fri, 27 Jun 2025 09:58:04 +0530 Subject: [PATCH 65/66] Update L1-Test.yaml --- .github/workflows/L1-Test.yaml | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/.github/workflows/L1-Test.yaml b/.github/workflows/L1-Test.yaml index ff16363..6dc7045 100644 --- a/.github/workflows/L1-Test.yaml +++ b/.github/workflows/L1-Test.yaml @@ -15,26 +15,39 @@ jobs: name: Test & Coverage runs-on: ubuntu-latest container: - image: rust:latest + image: ghcr.io/rdkcentral/docker-device-mgt-service-test/native-platform:latest steps: - name: Checkout code uses: actions/checkout@v4 - - name: Install dependencies + - name: Install Rust Toolchain components run: | - apt-get update && apt-get install -y git pkg-config libssl-dev - cargo install cargo-tarpaulin + if ! command -v cargo &> /dev/null; then + curl https://sh.rustup.rs -sSf | sh -s -- -y + export PATH="$HOME/.cargo/bin:$PATH" + fi + rustup update stable + rustup default stable + + - name: Add Rust to PATH + run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH + + - name: Install cargo-tarpaulin + run: cargo install cargo-tarpaulin + + - name: Install additional dependencies + run: | + apt-get update && apt-get install -y git pkg-config libssl-dev || true - name: Run all tests run: cargo test --all-features --workspace --verbose - name: Run tests with tarpaulin for coverage run: | - sudo -E cargo tarpaulin --all-features --workspace --timeout 120 \ + cargo tarpaulin --all-features --workspace --timeout 120 \ --out xml --output-dir ./coverage/ \ --exclude-files "tests/*" "benches/*" "**/tests.rs" "**/test_*.rs" \ - --skip-clean \ --verbose - name: Upload coverage to Codecov From 29f34317772f6133a53bf9db7260e0c66c30d442 Mon Sep 17 00:00:00 2001 From: Gomathi Shankar Date: Fri, 27 Jun 2025 10:07:16 +0530 Subject: [PATCH 66/66] Update L1-Test.yaml --- .github/workflows/L1-Test.yaml | 40 ++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/.github/workflows/L1-Test.yaml b/.github/workflows/L1-Test.yaml index 6dc7045..951bdc3 100644 --- a/.github/workflows/L1-Test.yaml +++ b/.github/workflows/L1-Test.yaml @@ -29,12 +29,13 @@ jobs: fi rustup update stable rustup default stable + rustup component add llvm-tools-preview - name: Add Rust to PATH run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH - - name: Install cargo-tarpaulin - run: cargo install cargo-tarpaulin + - name: Install cargo-llvm-cov + run: cargo install cargo-llvm-cov - name: Install additional dependencies run: | @@ -43,26 +44,27 @@ jobs: - name: Run all tests run: cargo test --all-features --workspace --verbose - - name: Run tests with tarpaulin for coverage + - name: Run tests with llvm-cov for coverage run: | - cargo tarpaulin --all-features --workspace --timeout 120 \ - --out xml --output-dir ./coverage/ \ - --exclude-files "tests/*" "benches/*" "**/tests.rs" "**/test_*.rs" \ - --verbose + mkdir -p ./coverage + # Generate both XML and summary report + cargo llvm-cov --all-features --workspace --cobertura --output-path ./coverage/cobertura.xml + # Print coverage summary to console + cargo llvm-cov --all-features --workspace --summary-only - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 - with: - file: ./coverage/cobertura.xml - flags: unittests - name: codecov-umbrella - fail_ci_if_error: false + # - name: Upload coverage to Codecov + # uses: codecov/codecov-action@v3 + # with: + # file: ./coverage/cobertura.xml + # flags: unittests + # name: codecov-umbrella + # fail_ci_if_error: false - - name: Archive code coverage results - uses: actions/upload-artifact@v4 - with: - name: code-coverage-report - path: coverage/ + # - name: Archive code coverage results + # uses: actions/upload-artifact@v4 + # with: + # name: code-coverage-report + # path: coverage/ - name: Clean target directory (ensure it's not persisted) run: rm -rf target/