Make daemon start on server-commands when it's not running
This commit is contained in:
parent
cf25f9eddb
commit
ec4506d9e4
5 changed files with 119 additions and 47 deletions
|
@ -24,7 +24,7 @@ pub fn handle_client_only_action(paths: &EwwPaths, action: ActionClientOnly) ->
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn do_server_call(mut stream: UnixStream, action: opts::ActionWithServer) -> Result<Option<app::DaemonResponse>> {
|
||||
pub fn do_server_call(stream: &mut UnixStream, action: &opts::ActionWithServer) -> Result<Option<app::DaemonResponse>> {
|
||||
log::info!("Forwarding options to server");
|
||||
stream.set_nonblocking(false).context("Failed to set stream to non-blocking")?;
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use codespan_reporting::{diagnostic::Diagnostic, term, term::Chars};
|
||||
use codespan_reporting::{
|
||||
diagnostic::Diagnostic,
|
||||
term::{self, Chars},
|
||||
};
|
||||
use eww_shared_util::Span;
|
||||
use simplexpr::eval::EvalError;
|
||||
use yuck::{config::file_provider::YuckFiles, error::AstError, format_diagnostic::ToDiagnostic, gen_diagnostic};
|
||||
|
|
|
@ -13,17 +13,21 @@ extern crate gtk;
|
|||
extern crate gtk_layer_shell as gtk_layer_shell;
|
||||
|
||||
use anyhow::*;
|
||||
use opts::ActionWithServer;
|
||||
use std::{
|
||||
os::unix::net,
|
||||
path::{Path, PathBuf},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use crate::server::ForkResult;
|
||||
|
||||
pub mod app;
|
||||
pub mod application_lifecycle;
|
||||
pub mod client;
|
||||
pub mod config;
|
||||
pub mod display_backend;
|
||||
pub mod error;
|
||||
mod error_handling_ctx;
|
||||
pub mod eww_state;
|
||||
pub mod geometry;
|
||||
|
@ -33,9 +37,9 @@ pub mod script_var_handler;
|
|||
pub mod server;
|
||||
pub mod util;
|
||||
pub mod widgets;
|
||||
pub mod error;
|
||||
|
||||
fn main() {
|
||||
let eww_binary_name = std::env::args().next().unwrap();
|
||||
let opts: opts::Opt = opts::Opt::from_env();
|
||||
|
||||
let log_level_filter = if opts.log_debug { log::LevelFilter::Debug } else { log::LevelFilter::Info };
|
||||
|
@ -52,45 +56,55 @@ fn main() {
|
|||
.unwrap_or_else(EwwPaths::default)
|
||||
.context("Failed to initialize eww paths")?;
|
||||
|
||||
match opts.action {
|
||||
let would_show_logs = match opts.action {
|
||||
opts::Action::ClientOnly(action) => {
|
||||
client::handle_client_only_action(&paths, action)?;
|
||||
false
|
||||
}
|
||||
opts::Action::WithServer(ActionWithServer::KillServer) => {
|
||||
handle_server_command(&paths, &ActionWithServer::KillServer, 1)?;
|
||||
false
|
||||
}
|
||||
opts::Action::WithServer(action) => {
|
||||
log::info!("Trying to find server process at socket {}", paths.get_ipc_socket_file().display());
|
||||
match net::UnixStream::connect(&paths.get_ipc_socket_file()) {
|
||||
Ok(stream) => {
|
||||
log::info!("Connected to Eww server ({}).", &paths.get_ipc_socket_file().display());
|
||||
let response =
|
||||
client::do_server_call(stream, action).context("Error while forwarding command to server")?;
|
||||
if let Some(response) = response {
|
||||
println!("{}", response);
|
||||
if response.is_failure() {
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
eprintln!("Failed to connect to the eww daemon.");
|
||||
eprintln!("Make sure to start the eww daemon process by running `eww daemon` first.");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
opts::Action::Daemon => {
|
||||
// make sure that there isn't already a Eww daemon running.
|
||||
if check_server_running(paths.get_ipc_socket_file()) {
|
||||
eprintln!("Eww server already running.");
|
||||
std::process::exit(1);
|
||||
} else {
|
||||
log::info!("Initializing Eww server. ({})", paths.get_ipc_socket_file().display());
|
||||
if let Err(err) = handle_server_command(&paths, &action, 5) {
|
||||
// connecting to the daemon failed. Thus, start the daemon here!
|
||||
log::warn!("Failed to connect to daemon: {}", err);
|
||||
log::info!("Initializing eww server. ({})", paths.get_ipc_socket_file().display());
|
||||
let _ = std::fs::remove_file(paths.get_ipc_socket_file());
|
||||
if !opts.show_logs {
|
||||
println!("Run `{} logs` to see any errors while editing your configuration.", eww_binary_name);
|
||||
}
|
||||
|
||||
println!("Run `eww logs` to see any errors, warnings or information while editing your configuration.");
|
||||
server::initialize_server(paths)?;
|
||||
let (command, response_recv) = action.into_daemon_command();
|
||||
let fork_result = server::initialize_server(paths.clone(), Some(command))?;
|
||||
let is_parent = fork_result == ForkResult::Parent;
|
||||
if let (Some(recv), true) = (response_recv, is_parent) {
|
||||
listen_for_daemon_response(recv);
|
||||
}
|
||||
is_parent
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// make sure that there isn't already a Eww daemon running.
|
||||
opts::Action::Daemon if check_server_running(paths.get_ipc_socket_file()) => {
|
||||
eprintln!("Eww server already running.");
|
||||
true
|
||||
}
|
||||
opts::Action::Daemon => {
|
||||
log::info!("Initializing Eww server. ({})", paths.get_ipc_socket_file().display());
|
||||
let _ = std::fs::remove_file(paths.get_ipc_socket_file());
|
||||
|
||||
if !opts.show_logs {
|
||||
println!("Run `{} logs` to see any errors while editing your configuration.", eww_binary_name);
|
||||
}
|
||||
let fork_result = server::initialize_server(paths.clone(), None)?;
|
||||
fork_result == ForkResult::Parent
|
||||
}
|
||||
};
|
||||
if would_show_logs && opts.show_logs {
|
||||
client::handle_client_only_action(&paths, opts::ActionClientOnly::Logs)?;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -100,11 +114,43 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
fn listen_for_daemon_response(mut recv: app::DaemonResponseReceiver) {
|
||||
let rt = tokio::runtime::Builder::new_current_thread().enable_time().build().expect("Failed to initialize tokio runtime");
|
||||
rt.block_on(async {
|
||||
if let Ok(Some(response)) = tokio::time::timeout(Duration::from_millis(100), recv.recv()).await {
|
||||
println!("{}", response);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_server_command(paths: &EwwPaths, action: &ActionWithServer, connect_attempts: usize) -> Result<()> {
|
||||
log::info!("Trying to find server process at socket {}", paths.get_ipc_socket_file().display());
|
||||
let mut stream = attempt_connect(&paths.get_ipc_socket_file(), connect_attempts).context("Failed to connect to daemon")?;
|
||||
log::info!("Connected to Eww server ({}).", &paths.get_ipc_socket_file().display());
|
||||
let response = client::do_server_call(&mut stream, action).context("Error while forwarding command to server")?;
|
||||
if let Some(response) = response {
|
||||
println!("{}", response);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn attempt_connect(socket_path: impl AsRef<Path>, attempts: usize) -> Option<net::UnixStream> {
|
||||
for _ in 0..attempts {
|
||||
if let Ok(mut con) = net::UnixStream::connect(&socket_path) {
|
||||
if client::do_server_call(&mut con, &opts::ActionWithServer::Ping).is_ok() {
|
||||
return net::UnixStream::connect(&socket_path).ok();
|
||||
}
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(200));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Check if a eww server is currently running by trying to send a ping message to it.
|
||||
fn check_server_running(socket_path: impl AsRef<Path>) -> bool {
|
||||
let response = net::UnixStream::connect(socket_path)
|
||||
.ok()
|
||||
.and_then(|stream| client::do_server_call(stream, opts::ActionWithServer::Ping).ok());
|
||||
.and_then(|mut stream| client::do_server_call(&mut stream, &opts::ActionWithServer::Ping).ok());
|
||||
response.is_some()
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ use crate::app;
|
|||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Opt {
|
||||
pub log_debug: bool,
|
||||
pub show_logs: bool,
|
||||
pub config_path: Option<std::path::PathBuf>,
|
||||
pub action: Action,
|
||||
}
|
||||
|
@ -25,6 +26,10 @@ struct RawOpt {
|
|||
#[structopt(short, long, global = true)]
|
||||
config: Option<std::path::PathBuf>,
|
||||
|
||||
/// Watch the log output after executing the command
|
||||
#[structopt(long = "logs", global = true)]
|
||||
show_logs: bool,
|
||||
|
||||
#[structopt(subcommand)]
|
||||
action: Action,
|
||||
}
|
||||
|
@ -136,8 +141,8 @@ impl Opt {
|
|||
|
||||
impl From<RawOpt> for Opt {
|
||||
fn from(other: RawOpt) -> Self {
|
||||
let RawOpt { action, log_debug, config } = other;
|
||||
Opt { action, log_debug, config_path: config }
|
||||
let RawOpt { action, log_debug, show_logs, config } = other;
|
||||
Opt { action, log_debug, show_logs, config_path: config }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
use crate::{app, config, error_handling_ctx, eww_state::*, ipc_server, script_var_handler, util, EwwPaths};
|
||||
use crate::{
|
||||
app::{self, DaemonCommand},
|
||||
config, error_handling_ctx,
|
||||
eww_state::*,
|
||||
ipc_server, script_var_handler, util, EwwPaths,
|
||||
};
|
||||
use anyhow::*;
|
||||
|
||||
use std::{
|
||||
|
@ -9,7 +14,7 @@ use std::{
|
|||
};
|
||||
use tokio::sync::mpsc::*;
|
||||
|
||||
pub fn initialize_server(paths: EwwPaths) -> Result<()> {
|
||||
pub fn initialize_server(paths: EwwPaths, action: Option<DaemonCommand>) -> Result<ForkResult> {
|
||||
let (ui_send, mut ui_recv) = tokio::sync::mpsc::unbounded_channel();
|
||||
|
||||
std::env::set_current_dir(&paths.get_config_dir())
|
||||
|
@ -31,7 +36,11 @@ pub fn initialize_server(paths: EwwPaths) -> Result<()> {
|
|||
}
|
||||
};
|
||||
|
||||
do_detach(&paths.get_log_file())?;
|
||||
let fork_result = do_detach(&paths.get_log_file())?;
|
||||
|
||||
if fork_result == ForkResult::Parent {
|
||||
return Ok(ForkResult::Parent);
|
||||
}
|
||||
|
||||
println!(
|
||||
r#"
|
||||
|
@ -76,6 +85,9 @@ pub fn initialize_server(paths: EwwPaths) -> Result<()> {
|
|||
init_async_part(app.paths.clone(), ui_send);
|
||||
|
||||
glib::MainContext::default().spawn_local(async move {
|
||||
if let Some(action) = action {
|
||||
app.handle_command(action);
|
||||
}
|
||||
while let Some(event) = ui_recv.recv().await {
|
||||
app.handle_command(event);
|
||||
}
|
||||
|
@ -84,7 +96,7 @@ pub fn initialize_server(paths: EwwPaths) -> Result<()> {
|
|||
gtk::main();
|
||||
log::info!("main application thread finished");
|
||||
|
||||
Ok(())
|
||||
Ok(ForkResult::Child)
|
||||
}
|
||||
|
||||
fn init_async_part(paths: EwwPaths, ui_send: UnboundedSender<app::DaemonCommand>) {
|
||||
|
@ -160,7 +172,7 @@ async fn run_filewatch<P: AsRef<Path>>(config_dir: P, evt_send: UnboundedSender<
|
|||
tokio::spawn(async move {
|
||||
match daemon_resp_response.recv().await {
|
||||
Some(app::DaemonResponse::Success(_)) => log::info!("Reloaded config successfully"),
|
||||
Some(app::DaemonResponse::Failure(e)) => log::error!("Failed to reload config: {}", e),
|
||||
Some(app::DaemonResponse::Failure(e)) => eprintln!("{}", e),
|
||||
None => log::error!("No response to reload configuration-reload request"),
|
||||
}
|
||||
});
|
||||
|
@ -171,14 +183,20 @@ async fn run_filewatch<P: AsRef<Path>>(config_dir: P, evt_send: UnboundedSender<
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum ForkResult {
|
||||
Parent,
|
||||
Child,
|
||||
}
|
||||
|
||||
/// detach the process from the terminal, also redirecting stdout and stderr to LOG_FILE
|
||||
fn do_detach(log_file_path: impl AsRef<Path>) -> Result<()> {
|
||||
fn do_detach(log_file_path: impl AsRef<Path>) -> Result<ForkResult> {
|
||||
// detach from terminal
|
||||
match unsafe { nix::unistd::fork()? } {
|
||||
nix::unistd::ForkResult::Parent { .. } => {
|
||||
std::process::exit(0);
|
||||
}
|
||||
nix::unistd::ForkResult::Child => {}
|
||||
nix::unistd::ForkResult::Parent { .. } => {
|
||||
return Ok(ForkResult::Parent);
|
||||
}
|
||||
}
|
||||
|
||||
let file = std::fs::OpenOptions::new()
|
||||
|
@ -195,5 +213,5 @@ fn do_detach(log_file_path: impl AsRef<Path>) -> Result<()> {
|
|||
nix::unistd::dup2(fd, std::io::stderr().as_raw_fd())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(ForkResult::Child)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue