mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-07-29 15:31:46 +08:00
161 lines
4.4 KiB
Rust
161 lines
4.4 KiB
Rust
use std::fs::{self, File, OpenOptions};
|
|
use std::io::Write;
|
|
use std::path::Path;
|
|
use std::process::Command;
|
|
|
|
use crate::error::{AppError, Result};
|
|
|
|
pub const CONFIGFS_PATH: &str = "/sys/kernel/config/usb_gadget";
|
|
pub const DEFAULT_GADGET_NAME: &str = "one-kvm";
|
|
pub const DEFAULT_USB_VENDOR_ID: u16 = 0x1d6b;
|
|
pub const DEFAULT_USB_PRODUCT_ID: u16 = 0x0104;
|
|
pub const DEFAULT_USB_BCD_DEVICE: u16 = 0x0100;
|
|
pub const USB_BCD_USB: u16 = 0x0200;
|
|
|
|
pub fn is_configfs_available() -> bool {
|
|
configfs_path().exists()
|
|
}
|
|
|
|
pub fn configfs_path() -> &'static Path {
|
|
Path::new(CONFIGFS_PATH)
|
|
}
|
|
|
|
/// Loads `libcomposite` if needed; does not mount configfs.
|
|
pub fn ensure_libcomposite_loaded() -> Result<()> {
|
|
if is_configfs_available() {
|
|
return Ok(());
|
|
}
|
|
|
|
if !Path::new("/sys/module/libcomposite").exists() {
|
|
let status = Command::new("modprobe")
|
|
.arg("libcomposite")
|
|
.status()
|
|
.map_err(|e| {
|
|
AppError::Internal(format!("Failed to run modprobe libcomposite: {}", e))
|
|
})?;
|
|
|
|
if !status.success() {
|
|
return Err(AppError::Internal(format!(
|
|
"modprobe libcomposite failed with status {}",
|
|
status
|
|
)));
|
|
}
|
|
}
|
|
|
|
if is_configfs_available() {
|
|
Ok(())
|
|
} else {
|
|
Err(AppError::Internal(
|
|
"libcomposite is not available after modprobe; check configfs mount and kernel support"
|
|
.to_string(),
|
|
))
|
|
}
|
|
}
|
|
|
|
pub fn find_udc() -> Option<String> {
|
|
list_udcs().into_iter().next()
|
|
}
|
|
|
|
pub fn list_udcs() -> Vec<String> {
|
|
let mut devices = Vec::new();
|
|
collect_dir_names(Path::new("/sys/class/udc"), &mut devices);
|
|
devices.sort();
|
|
devices.dedup();
|
|
devices
|
|
}
|
|
|
|
fn collect_dir_names(path: &Path, devices: &mut Vec<String>) {
|
|
if let Ok(entries) = fs::read_dir(path) {
|
|
for entry in entries.flatten() {
|
|
let name = entry.file_name().to_string_lossy().trim().to_string();
|
|
if !name.is_empty() {
|
|
devices.push(name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Sysfs/configfs: one write syscall with final buffer (incl. newline when needed).
|
|
pub fn write_file(path: &Path, content: &str) -> Result<()> {
|
|
let mut file = OpenOptions::new()
|
|
.write(true)
|
|
.open(path)
|
|
.or_else(|e| {
|
|
if path.exists() {
|
|
Err(e)
|
|
} else {
|
|
File::create(path)
|
|
}
|
|
})
|
|
.map_err(|e| AppError::Internal(format!("Failed to open {}: {}", path.display(), e)))?;
|
|
|
|
let data: std::borrow::Cow<[u8]> = if content.ends_with('\n') {
|
|
content.as_bytes().into()
|
|
} else {
|
|
let mut buf = content.as_bytes().to_vec();
|
|
buf.push(b'\n');
|
|
buf.into()
|
|
};
|
|
|
|
file.write_all(&data)
|
|
.map_err(|e| AppError::Internal(format!("Failed to write to {}: {}", path.display(), e)))?;
|
|
|
|
file.flush()
|
|
.map_err(|e| AppError::Internal(format!("Failed to flush {}: {}", path.display(), e)))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn write_bytes(path: &Path, data: &[u8]) -> Result<()> {
|
|
let mut file = File::create(path)
|
|
.map_err(|e| AppError::Internal(format!("Failed to create {}: {}", path.display(), e)))?;
|
|
|
|
file.write_all(data)
|
|
.map_err(|e| AppError::Internal(format!("Failed to write to {}: {}", path.display(), e)))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn create_dir(path: &Path) -> Result<()> {
|
|
fs::create_dir_all(path).map_err(|e| {
|
|
AppError::Internal(format!(
|
|
"Failed to create directory {}: {}",
|
|
path.display(),
|
|
e
|
|
))
|
|
})
|
|
}
|
|
|
|
pub fn remove_dir(path: &Path) -> Result<()> {
|
|
if path.exists() {
|
|
fs::remove_dir(path).map_err(|e| {
|
|
AppError::Internal(format!(
|
|
"Failed to remove directory {}: {}",
|
|
path.display(),
|
|
e
|
|
))
|
|
})?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn remove_file(path: &Path) -> Result<()> {
|
|
if path.exists() {
|
|
fs::remove_file(path).map_err(|e| {
|
|
AppError::Internal(format!("Failed to remove file {}: {}", path.display(), e))
|
|
})?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn create_symlink(src: &Path, dest: &Path) -> Result<()> {
|
|
std::os::unix::fs::symlink(src, dest).map_err(|e| {
|
|
AppError::Internal(format!(
|
|
"Failed to create symlink {} -> {}: {}",
|
|
dest.display(),
|
|
src.display(),
|
|
e
|
|
))
|
|
})
|
|
}
|