Move udev creation to lib
Remove root check Also create udev rules in tray app
This commit is contained in:
11
Cargo.lock
generated
11
Cargo.lock
generated
@@ -361,7 +361,6 @@ dependencies = [
|
|||||||
"ksni",
|
"ksni",
|
||||||
"shell-escape",
|
"shell-escape",
|
||||||
"thistermination",
|
"thistermination",
|
||||||
"users",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -641,16 +640,6 @@ version = "0.1.14"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "users"
|
|
||||||
version = "0.11.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"log",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
|||||||
@@ -12,5 +12,4 @@ hidapi = { path = "vendor/hidapi" }
|
|||||||
thistermination = "1.0.0"
|
thistermination = "1.0.0"
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
ksni = "0.2.0"
|
ksni = "0.2.0"
|
||||||
users = "0.11"
|
|
||||||
shell-escape = "0.1.5"
|
shell-escape = "0.1.5"
|
||||||
|
|||||||
@@ -5,118 +5,15 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use clap::{Arg, Command};
|
use clap::{Arg, Command};
|
||||||
use hyper_headset::{debug_println, devices::connect_compatible_device};
|
use hyper_headset::{
|
||||||
use std::process::Command as CommandShell;
|
check_rule, debug_println, devices::connect_compatible_device, prompt_user_for_udev_rule,
|
||||||
|
update_rule, RuleState, UDEV_RULES, UDEV_RULE_PATH_SYSTEM, UDEV_RULE_PATH_USER,
|
||||||
|
};
|
||||||
|
|
||||||
const SHOW_ALL_OPTIONS: bool = false;
|
const SHOW_ALL_OPTIONS: bool = false;
|
||||||
|
|
||||||
const UDEV_RULE_PATH_SYSTEM: &str = "/etc/udev/rules.d/99-HyperHeadset.rules";
|
|
||||||
const UDEV_RULE_PATH_USER: &str = "/usr/lib/udev/rules.d/99-HyperHeadset.rules";
|
|
||||||
const UDEV_RULES: &str = include_str!("./../../99-HyperHeadset.rules");
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum RuleState {
|
|
||||||
RuleExists(bool),
|
|
||||||
RuleMatch(bool),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_rule(path: &str, rules: &str) -> RuleState {
|
|
||||||
let mut rule_state;
|
|
||||||
|
|
||||||
if !fs::exists(path).unwrap_or(false) {
|
|
||||||
rule_state = RuleState::RuleExists(false);
|
|
||||||
} else {
|
|
||||||
rule_state = RuleState::RuleExists(true);
|
|
||||||
if let Ok(content) = fs::read_to_string(path) {
|
|
||||||
if content.trim() != rules.trim() {
|
|
||||||
rule_state = RuleState::RuleMatch(false);
|
|
||||||
} else {
|
|
||||||
rule_state = RuleState::RuleMatch(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rule_state
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_rule(path: &str, rules: &str) {
|
|
||||||
let status = CommandShell::new("sudo")
|
|
||||||
.arg("sh")
|
|
||||||
.arg("-c")
|
|
||||||
.arg(format!(
|
|
||||||
"echo {} > {}",
|
|
||||||
shell_escape::escape(rules.into()),
|
|
||||||
path
|
|
||||||
))
|
|
||||||
.status();
|
|
||||||
|
|
||||||
match status {
|
|
||||||
Ok(exit_status) if exit_status.success() => {
|
|
||||||
println!("created rule at {path}.\nYou may need to replug your headset for the udev rules to take effect.");
|
|
||||||
}
|
|
||||||
Ok(e) => {
|
|
||||||
println!("Failed to create rule at {path}: {}", e);
|
|
||||||
println!("Your headset may not be recognized without the correct udev rules.");
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("Failed to create rule at {path}: {}", e);
|
|
||||||
println!("Your headset may not be recognized without the correct udev rules.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let user_rule_state = check_rule(UDEV_RULE_PATH_USER, UDEV_RULES);
|
prompt_user_for_udev_rule();
|
||||||
let system_rule_state = check_rule(UDEV_RULE_PATH_SYSTEM, UDEV_RULES);
|
|
||||||
|
|
||||||
if users::get_current_uid() != 0 {
|
|
||||||
debug_println!("user rule: {user_rule_state:?}, system rule: {system_rule_state:?}");
|
|
||||||
match (user_rule_state, system_rule_state) {
|
|
||||||
(RuleState::RuleMatch(true), _) => (),
|
|
||||||
(_, RuleState::RuleMatch(true)) => (),
|
|
||||||
|
|
||||||
(RuleState::RuleMatch(false), _) | (RuleState::RuleExists(true), _) => {
|
|
||||||
print!(
|
|
||||||
"Udev rules at {UDEV_RULE_PATH_USER} do not have the expected value. Do you want to recreate them? (y/N): "
|
|
||||||
);
|
|
||||||
io::Write::flush(&mut io::stdout()).unwrap();
|
|
||||||
let mut input = String::new();
|
|
||||||
io::stdin().read_line(&mut input).unwrap();
|
|
||||||
if matches!(input.trim(), "y" | "Y") {
|
|
||||||
update_rule(UDEV_RULE_PATH_USER, UDEV_RULES);
|
|
||||||
} else {
|
|
||||||
println!("Your headset may not be recognized without the correct udev rules.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(RuleState::RuleExists(false), RuleState::RuleMatch(false))
|
|
||||||
| (RuleState::RuleExists(false), RuleState::RuleExists(true)) => {
|
|
||||||
print!(
|
|
||||||
"Udev rules at {UDEV_RULE_PATH_SYSTEM} do not have the expected value. Do you want to recreate them? (y/N): "
|
|
||||||
);
|
|
||||||
io::Write::flush(&mut io::stdout()).unwrap();
|
|
||||||
let mut input = String::new();
|
|
||||||
io::stdin().read_line(&mut input).unwrap();
|
|
||||||
if matches!(input.trim(), "y" | "Y") {
|
|
||||||
update_rule(UDEV_RULE_PATH_SYSTEM, UDEV_RULES);
|
|
||||||
} else {
|
|
||||||
println!("Your headset may not be recognized without the correct udev rules.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(RuleState::RuleExists(false), RuleState::RuleExists(false)) => {
|
|
||||||
print!("No udev rules found. Do you want to create {UDEV_RULE_PATH_USER}? (y/N): ");
|
|
||||||
io::Write::flush(&mut io::stdout()).unwrap();
|
|
||||||
let mut input = String::new();
|
|
||||||
io::stdin().read_line(&mut input).unwrap();
|
|
||||||
if matches!(input.trim(), "y" | "Y") {
|
|
||||||
update_rule(UDEV_RULE_PATH_USER, UDEV_RULES);
|
|
||||||
} else {
|
|
||||||
println!(
|
|
||||||
"Without udev rules your headset can only be accessed when running as root."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut device = match connect_compatible_device() {
|
let mut device = match connect_compatible_device() {
|
||||||
Ok(device) => device,
|
Ok(device) => device,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
|
|||||||
112
src/lib.rs
112
src/lib.rs
@@ -1,3 +1,6 @@
|
|||||||
|
use std::{fs, io, process::Command, time::Duration};
|
||||||
|
|
||||||
|
// #![warn(missing_docs)]
|
||||||
pub mod devices;
|
pub mod devices;
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
@@ -7,3 +10,112 @@ macro_rules! debug_println {
|
|||||||
println!($($args)*);
|
println!($($args)*);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const UDEV_RULE_PATH_SYSTEM: &str = "/etc/udev/rules.d/99-HyperHeadset.rules";
|
||||||
|
pub const UDEV_RULE_PATH_USER: &str = "/usr/lib/udev/rules.d/99-HyperHeadset.rules";
|
||||||
|
pub const UDEV_RULES: &str = include_str!("./../99-HyperHeadset.rules");
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum RuleState {
|
||||||
|
RuleExists(bool),
|
||||||
|
RuleMatch(bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_rule(path: &str, rules: &str) -> RuleState {
|
||||||
|
let mut rule_state;
|
||||||
|
|
||||||
|
if !fs::exists(path).unwrap_or(false) {
|
||||||
|
rule_state = RuleState::RuleExists(false);
|
||||||
|
} else {
|
||||||
|
rule_state = RuleState::RuleExists(true);
|
||||||
|
if let Ok(content) = fs::read_to_string(path) {
|
||||||
|
if content.trim() != rules.trim() {
|
||||||
|
rule_state = RuleState::RuleMatch(false);
|
||||||
|
} else {
|
||||||
|
rule_state = RuleState::RuleMatch(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rule_state
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_rule(path: &str, rules: &str) {
|
||||||
|
let status = Command::new("sudo")
|
||||||
|
.arg("sh")
|
||||||
|
.arg("-c")
|
||||||
|
.arg(format!(
|
||||||
|
"echo {} > {} && udevadm control --reload-rules && udevadm trigger",
|
||||||
|
shell_escape::escape(rules.into()),
|
||||||
|
shell_escape::escape(path.into())
|
||||||
|
))
|
||||||
|
.status();
|
||||||
|
// a little delay so the rules are active before trying to connect
|
||||||
|
std::thread::sleep(Duration::from_millis(500));
|
||||||
|
|
||||||
|
match status {
|
||||||
|
Ok(exit_status) if exit_status.success() => {
|
||||||
|
println!("created rule at {path}.\nYou may need to replug your headset for the udev rules to take effect.");
|
||||||
|
}
|
||||||
|
Ok(e) => {
|
||||||
|
println!("Failed to create rule at {path}: {}", e);
|
||||||
|
println!("Your headset may not be recognized without the correct udev rules.");
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!("Failed to create rule at {path}: {}", e);
|
||||||
|
println!("Your headset may not be recognized without the correct udev rules.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prompt_user_for_udev_rule() {
|
||||||
|
let user_rule_state = check_rule(UDEV_RULE_PATH_USER, UDEV_RULES);
|
||||||
|
let system_rule_state = check_rule(UDEV_RULE_PATH_SYSTEM, UDEV_RULES);
|
||||||
|
|
||||||
|
debug_println!("user rule: {user_rule_state:?}, system rule: {system_rule_state:?}");
|
||||||
|
match (user_rule_state, system_rule_state) {
|
||||||
|
(RuleState::RuleMatch(true), _) => (),
|
||||||
|
(_, RuleState::RuleMatch(true)) => (),
|
||||||
|
|
||||||
|
(RuleState::RuleMatch(false), _) | (RuleState::RuleExists(true), _) => {
|
||||||
|
print!(
|
||||||
|
"Udev rules at {UDEV_RULE_PATH_USER} do not have the expected value. Do you want to recreate them? (y/N): "
|
||||||
|
);
|
||||||
|
io::Write::flush(&mut io::stdout()).unwrap();
|
||||||
|
let mut input = String::new();
|
||||||
|
io::stdin().read_line(&mut input).unwrap();
|
||||||
|
if matches!(input.trim(), "y" | "Y") {
|
||||||
|
update_rule(UDEV_RULE_PATH_USER, UDEV_RULES);
|
||||||
|
} else {
|
||||||
|
println!("Your headset may not be recognized without the correct udev rules.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(RuleState::RuleExists(false), RuleState::RuleMatch(false))
|
||||||
|
| (RuleState::RuleExists(false), RuleState::RuleExists(true)) => {
|
||||||
|
print!(
|
||||||
|
"Udev rules at {UDEV_RULE_PATH_SYSTEM} do not have the expected value. Do you want to recreate them? (y/N): "
|
||||||
|
);
|
||||||
|
io::Write::flush(&mut io::stdout()).unwrap();
|
||||||
|
let mut input = String::new();
|
||||||
|
io::stdin().read_line(&mut input).unwrap();
|
||||||
|
if matches!(input.trim(), "y" | "Y") {
|
||||||
|
update_rule(UDEV_RULE_PATH_SYSTEM, UDEV_RULES);
|
||||||
|
} else {
|
||||||
|
println!("Your headset may not be recognized without the correct udev rules.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(RuleState::RuleExists(false), RuleState::RuleExists(false)) => {
|
||||||
|
print!("No udev rules found. Do you want to create {UDEV_RULE_PATH_USER}? (y/N): ");
|
||||||
|
io::Write::flush(&mut io::stdout()).unwrap();
|
||||||
|
let mut input = String::new();
|
||||||
|
io::stdin().read_line(&mut input).unwrap();
|
||||||
|
if matches!(input.trim(), "y" | "Y") {
|
||||||
|
update_rule(UDEV_RULE_PATH_USER, UDEV_RULES);
|
||||||
|
} else {
|
||||||
|
println!(
|
||||||
|
"Without udev rules your headset can only be accessed when running as root."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ use enigo::{Direction, Enigo, Key, Keyboard, Settings};
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
mod status_tray;
|
mod status_tray;
|
||||||
use hyper_headset::devices::connect_compatible_device;
|
use hyper_headset::{devices::connect_compatible_device, prompt_user_for_udev_rule};
|
||||||
use status_tray::{StatusTray, TrayHandler};
|
use status_tray::{StatusTray, TrayHandler};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
prompt_user_for_udev_rule();
|
||||||
let matches = Command::new(env!("CARGO_PKG_NAME"))
|
let matches = Command::new(env!("CARGO_PKG_NAME"))
|
||||||
.version(env!("CARGO_PKG_VERSION"))
|
.version(env!("CARGO_PKG_VERSION"))
|
||||||
.author(env!("CARGO_PKG_AUTHORS"))
|
.author(env!("CARGO_PKG_AUTHORS"))
|
||||||
|
|||||||
Reference in New Issue
Block a user