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

View file

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

View file

@ -64,6 +64,7 @@ impl ScriptVar {
/// Run a command and get the output
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 = output.trim_matches('\n');
Ok(PrimitiveValue::from(output))

View file

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

View file

@ -31,7 +31,7 @@ pub mod value;
pub mod widgets;
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)
.unwrap_or_else(|_| std::path::PathBuf::from("/tmp"))
.join("eww-server");
@ -47,6 +47,21 @@ lazy_static::lazy_static! {
.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() {
let opts: opts::Opt = opts::Opt::from_env();
@ -61,15 +76,16 @@ fn main() {
.init();
let result: Result<_> = try {
let socket_path = calculate_socket_path(opts.config_path.clone().unwrap_or(CONFIG_DIR.join("eww.xml")));
match opts.action {
opts::Action::ClientOnly(action) => {
client::handle_client_only_action(action)?;
}
opts::Action::WithServer(action) => {
log::info!("Trying to find server process");
match net::UnixStream::connect(&*IPC_SOCKET_PATH) {
log::info!("Trying to find server process at socket {}", socket_path.display());
match net::UnixStream::connect(&socket_path) {
Ok(stream) => {
log::info!("Connected to Eww server.");
log::info!("Connected to Eww server ({}).", &socket_path.display());
let response =
client::do_server_call(stream, action).context("Error while forwarding command to server")?;
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.
if check_server_running(&*IPC_SOCKET_PATH) {
if check_server_running(&socket_path) {
eprintln!("Eww server already running.");
std::process::exit(1);
} else {
log::info!("Initializing Eww server.");
let _ = std::fs::remove_file(&*crate::IPC_SOCKET_PATH);
log::info!("Initializing Eww server. ({})", socket_path.display());
let _ = std::fs::remove_file(socket_path);
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)]
pub struct Opt {
pub log_debug: bool,
pub config_path: Option<std::path::PathBuf>,
pub action: Action,
}
@ -21,6 +22,10 @@ struct RawOpt {
#[structopt(long = "debug", global = true)]
log_debug: bool,
/// override config-file path (path to eww.xml)
#[structopt(short, long, global = true)]
config: Option<std::path::PathBuf>,
#[structopt(subcommand)]
action: Action,
}
@ -29,11 +34,7 @@ struct RawOpt {
pub enum Action {
/// Start the Eww daemon.
#[structopt(name = "daemon", alias = "d")]
Daemon {
/// Custom Config Path
#[structopt(short, long)]
config: Option<std::path::PathBuf>,
},
Daemon,
#[structopt(flatten)]
ClientOnly(ActionClientOnly),
@ -128,8 +129,16 @@ impl Opt {
impl From<RawOpt> for Opt {
fn from(other: RawOpt) -> Self {
let RawOpt { action, log_debug } = other;
Opt { action, log_debug }
let RawOpt {
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 {
let filewatch_join_handle = {
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 })
};
let ipc_server_join_handle = {
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 = {

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 gtk::prelude::*;
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
/// placeholder ('{}') which will be replaced by the value provided as [`arg`]
pub(self) fn run_command<T: std::fmt::Display>(cmd: &str, arg: T) {
let cmd = cmd.replace(CMD_STRING_PLACEHODLER, &format!("{}", arg));
let command_result = Command::new("/bin/sh")
.arg("-c")
.arg(&cmd)
.spawn()
.and_then(|mut child| child.wait());
print_result_err!(format!("executing command {}", &cmd), command_result);
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));
log::debug!("Running command from widget: {}", cmd);
let child = Command::new("/bin/sh").arg("-c").arg(&cmd).spawn();
match child {
Ok(mut child) => match child.wait_timeout(std::time::Duration::from_millis(200)) {
// child timed out
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> {

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: as_string) {
let old_id = on_change_handler_id.replace(Some(
gtk_widget.connect_color_activated(move |_a, gtk_widget| {
run_command(&onchange, gtk_widget);
gtk_widget.connect_color_activated(move |_a, color| {
run_command(&onchange, color.clone());
})
));
old_id.map(|id| gtk_widget.disconnect(id));