make multiple daemons possible and make commands time out

This commit is contained in:
elkowar 2021-03-02 12:23:18 +01:00
parent 9a8bbf4114
commit 5b3344fc5b
9 changed files with 91 additions and 30 deletions

17
Cargo.lock generated
View file

@ -100,6 +100,12 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "base64"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]] [[package]]
name = "beef" name = "beef"
version = "0.4.4" version = "0.4.4"
@ -279,6 +285,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-stream", "async-stream",
"base64",
"bincode", "bincode",
"debug_stub_derive", "debug_stub_derive",
"derive_more", "derive_more",
@ -314,6 +321,7 @@ dependencies = [
"tokio-stream", "tokio-stream",
"tokio-util", "tokio-util",
"unescape", "unescape",
"wait-timeout",
"x11rb", "x11rb",
] ]
@ -1723,6 +1731,15 @@ version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
[[package]]
name = "wait-timeout"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.9.0+wasi-snapshot-preview1" version = "0.9.0+wasi-snapshot-preview1"

View file

@ -58,6 +58,8 @@ tokio-util = "0.6"
nom = "6.1" nom = "6.1"
dyn-clone = "1.0" dyn-clone = "1.0"
base64 = "0.13"
wait-timeout = "0.2"
[target.'cfg(target_os="linux")'.dependencies] [target.'cfg(target_os="linux")'.dependencies]
inotify = "0.9" inotify = "0.9"

View file

@ -64,6 +64,7 @@ impl ScriptVar {
/// Run a command and get the output /// Run a command and get the output
fn run_command(cmd: &str) -> Result<PrimitiveValue> { fn run_command(cmd: &str) -> Result<PrimitiveValue> {
log::debug!("Running command: {}", cmd);
let output = String::from_utf8(Command::new("/bin/sh").arg("-c").arg(cmd).output()?.stdout)?; let output = String::from_utf8(Command::new("/bin/sh").arg("-c").arg(cmd).output()?.stdout)?;
let output = output.trim_matches('\n'); let output = output.trim_matches('\n');
Ok(PrimitiveValue::from(output)) Ok(PrimitiveValue::from(output))

View file

@ -6,8 +6,9 @@ use tokio::{
sync::mpsc::*, sync::mpsc::*,
}; };
pub async fn run_server(evt_send: UnboundedSender<app::DaemonCommand>) -> Result<()> { pub async fn run_server<P: AsRef<std::path::Path>>(evt_send: UnboundedSender<app::DaemonCommand>, socket_path: P) -> Result<()> {
let listener = tokio::net::UnixListener::bind(&*crate::IPC_SOCKET_PATH)?; let socket_path = socket_path.as_ref();
let listener = { tokio::net::UnixListener::bind(socket_path)? };
log::info!("IPC server initialized"); log::info!("IPC server initialized");
crate::loop_select_exiting! { crate::loop_select_exiting! {
connection = listener.accept() => match connection { connection = listener.accept() => match connection {

View file

@ -31,7 +31,7 @@ pub mod value;
pub mod widgets; pub mod widgets;
lazy_static::lazy_static! { lazy_static::lazy_static! {
pub static ref IPC_SOCKET_PATH: std::path::PathBuf = std::env::var("XDG_RUNTIME_DIR") static ref IPC_SOCKET_PATH: std::path::PathBuf = std::env::var("XDG_RUNTIME_DIR")
.map(std::path::PathBuf::from) .map(std::path::PathBuf::from)
.unwrap_or_else(|_| std::path::PathBuf::from("/tmp")) .unwrap_or_else(|_| std::path::PathBuf::from("/tmp"))
.join("eww-server"); .join("eww-server");
@ -47,6 +47,21 @@ lazy_static::lazy_static! {
.join("eww.log"); .join("eww.log");
} }
pub struct Paths {}
pub fn calculate_socket_path<P: AsRef<std::path::Path>>(config_file_path: P) -> std::path::PathBuf {
let daemon_id = base64::encode(format!("{}", config_file_path.as_ref().display()));
let socket_filename = format!(
"{}_{}",
&*crate::IPC_SOCKET_PATH
.file_name()
.and_then(|x| x.to_str())
.expect("Invalid socket path"),
daemon_id,
);
crate::IPC_SOCKET_PATH.with_file_name(socket_filename).to_path_buf()
}
fn main() { fn main() {
let opts: opts::Opt = opts::Opt::from_env(); let opts: opts::Opt = opts::Opt::from_env();
@ -61,15 +76,16 @@ fn main() {
.init(); .init();
let result: Result<_> = try { let result: Result<_> = try {
let socket_path = calculate_socket_path(opts.config_path.clone().unwrap_or(CONFIG_DIR.join("eww.xml")));
match opts.action { match opts.action {
opts::Action::ClientOnly(action) => { opts::Action::ClientOnly(action) => {
client::handle_client_only_action(action)?; client::handle_client_only_action(action)?;
} }
opts::Action::WithServer(action) => { opts::Action::WithServer(action) => {
log::info!("Trying to find server process"); log::info!("Trying to find server process at socket {}", socket_path.display());
match net::UnixStream::connect(&*IPC_SOCKET_PATH) { match net::UnixStream::connect(&socket_path) {
Ok(stream) => { Ok(stream) => {
log::info!("Connected to Eww server."); log::info!("Connected to Eww server ({}).", &socket_path.display());
let response = let response =
client::do_server_call(stream, action).context("Error while forwarding command to server")?; client::do_server_call(stream, action).context("Error while forwarding command to server")?;
if let Some(response) = response { if let Some(response) = response {
@ -87,17 +103,17 @@ fn main() {
} }
} }
opts::Action::Daemon { config } => { opts::Action::Daemon => {
// make sure that there isn't already a Eww daemon running. // make sure that there isn't already a Eww daemon running.
if check_server_running(&*IPC_SOCKET_PATH) { if check_server_running(&socket_path) {
eprintln!("Eww server already running."); eprintln!("Eww server already running.");
std::process::exit(1); std::process::exit(1);
} else { } else {
log::info!("Initializing Eww server."); log::info!("Initializing Eww server. ({})", socket_path.display());
let _ = std::fs::remove_file(&*crate::IPC_SOCKET_PATH); let _ = std::fs::remove_file(socket_path);
println!("Run `eww logs` to see any errors, warnings or information while editing your configuration."); println!("Run `eww logs` to see any errors, warnings or information while editing your configuration.");
server::initialize_server(config)?; server::initialize_server(opts.config_path)?;
} }
} }
} }

View file

@ -12,6 +12,7 @@ use crate::{
#[derive(Debug, Serialize, Deserialize, PartialEq)] #[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct Opt { pub struct Opt {
pub log_debug: bool, pub log_debug: bool,
pub config_path: Option<std::path::PathBuf>,
pub action: Action, pub action: Action,
} }
@ -21,6 +22,10 @@ struct RawOpt {
#[structopt(long = "debug", global = true)] #[structopt(long = "debug", global = true)]
log_debug: bool, log_debug: bool,
/// override config-file path (path to eww.xml)
#[structopt(short, long, global = true)]
config: Option<std::path::PathBuf>,
#[structopt(subcommand)] #[structopt(subcommand)]
action: Action, action: Action,
} }
@ -29,11 +34,7 @@ struct RawOpt {
pub enum Action { pub enum Action {
/// Start the Eww daemon. /// Start the Eww daemon.
#[structopt(name = "daemon", alias = "d")] #[structopt(name = "daemon", alias = "d")]
Daemon { Daemon,
/// Custom Config Path
#[structopt(short, long)]
config: Option<std::path::PathBuf>,
},
#[structopt(flatten)] #[structopt(flatten)]
ClientOnly(ActionClientOnly), ClientOnly(ActionClientOnly),
@ -128,8 +129,16 @@ impl Opt {
impl From<RawOpt> for Opt { impl From<RawOpt> for Opt {
fn from(other: RawOpt) -> Self { fn from(other: RawOpt) -> Self {
let RawOpt { action, log_debug } = other; let RawOpt {
Opt { action, log_debug } action,
log_debug,
config,
} = other;
Opt {
action,
log_debug,
config_path: config,
}
} }
} }

View file

@ -89,12 +89,14 @@ fn init_async_part(config_file_path: PathBuf, scss_file_path: PathBuf, ui_send:
rt.block_on(async { rt.block_on(async {
let filewatch_join_handle = { let filewatch_join_handle = {
let ui_send = ui_send.clone(); let ui_send = ui_send.clone();
let config_file_path = config_file_path.clone();
tokio::spawn(async move { run_filewatch(config_file_path, scss_file_path, ui_send).await }) tokio::spawn(async move { run_filewatch(config_file_path, scss_file_path, ui_send).await })
}; };
let ipc_server_join_handle = { let ipc_server_join_handle = {
let ui_send = ui_send.clone(); let ui_send = ui_send.clone();
tokio::spawn(async move { ipc_server::run_server(ui_send).await }) let socket_path = crate::calculate_socket_path(config_file_path);
tokio::spawn(async move { ipc_server::run_server(ui_send, socket_path).await })
}; };
let forward_exit_to_app_handle = { let forward_exit_to_app_handle = {

View file

@ -1,4 +1,4 @@
use crate::{config::window_definition::WindowName, eww_state::*, print_result_err, value::AttrName}; use crate::{config::window_definition::WindowName, eww_state::*, value::AttrName};
use anyhow::*; use anyhow::*;
use gtk::prelude::*; use gtk::prelude::*;
use itertools::Itertools; use itertools::Itertools;
@ -13,14 +13,27 @@ const CMD_STRING_PLACEHODLER: &str = "{}";
/// Run a command that was provided as an attribute. This command may use a /// Run a command that was provided as an attribute. This command may use a
/// placeholder ('{}') which will be replaced by the value provided as [`arg`] /// placeholder ('{}') which will be replaced by the value provided as [`arg`]
pub(self) fn run_command<T: std::fmt::Display>(cmd: &str, arg: T) { pub(self) fn run_command<T: 'static + std::fmt::Display + Send + Sync>(cmd: &str, arg: T) {
use wait_timeout::ChildExt;
let cmd = cmd.to_string();
std::thread::spawn(move || {
let cmd = cmd.replace(CMD_STRING_PLACEHODLER, &format!("{}", arg)); let cmd = cmd.replace(CMD_STRING_PLACEHODLER, &format!("{}", arg));
let command_result = Command::new("/bin/sh") log::debug!("Running command from widget: {}", cmd);
.arg("-c") let child = Command::new("/bin/sh").arg("-c").arg(&cmd).spawn();
.arg(&cmd) match child {
.spawn() Ok(mut child) => match child.wait_timeout(std::time::Duration::from_millis(200)) {
.and_then(|mut child| child.wait()); // child timed out
print_result_err!(format!("executing command {}", &cmd), command_result); Ok(None) => {
eprintln!("WARNING: command {} timed out", &cmd);
let _ = child.kill();
let _ = child.wait();
}
Err(err) => eprintln!("Failed to execute command {}: {}", cmd, err),
Ok(Some(_)) => {}
},
Err(err) => eprintln!("Failed to launch child process: {}", err),
}
});
} }
struct BuilderArgs<'a, 'b, 'c, 'd> { struct BuilderArgs<'a, 'b, 'c, 'd> {

View file

@ -258,8 +258,8 @@ fn build_gtk_color_chooser(bargs: &mut BuilderArgs) -> Result<gtk::ColorChooserW
// @prop onchange - runs the code when the color was selected // @prop onchange - runs the code when the color was selected
prop(onchange: as_string) { prop(onchange: as_string) {
let old_id = on_change_handler_id.replace(Some( let old_id = on_change_handler_id.replace(Some(
gtk_widget.connect_color_activated(move |_a, gtk_widget| { gtk_widget.connect_color_activated(move |_a, color| {
run_command(&onchange, gtk_widget); run_command(&onchange, color.clone());
}) })
)); ));
old_id.map(|id| gtk_widget.disconnect(id)); old_id.map(|id| gtk_widget.disconnect(id));