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(())
|
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");
|
log::info!("Forwarding options to server");
|
||||||
stream.set_nonblocking(false).context("Failed to set stream to non-blocking")?;
|
stream.set_nonblocking(false).context("Failed to set stream to non-blocking")?;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
use std::sync::{Arc, Mutex};
|
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 eww_shared_util::Span;
|
||||||
use simplexpr::eval::EvalError;
|
use simplexpr::eval::EvalError;
|
||||||
use yuck::{config::file_provider::YuckFiles, error::AstError, format_diagnostic::ToDiagnostic, gen_diagnostic};
|
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;
|
extern crate gtk_layer_shell as gtk_layer_shell;
|
||||||
|
|
||||||
use anyhow::*;
|
use anyhow::*;
|
||||||
|
use opts::ActionWithServer;
|
||||||
use std::{
|
use std::{
|
||||||
os::unix::net,
|
os::unix::net,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::server::ForkResult;
|
||||||
|
|
||||||
pub mod app;
|
pub mod app;
|
||||||
pub mod application_lifecycle;
|
pub mod application_lifecycle;
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod display_backend;
|
pub mod display_backend;
|
||||||
|
pub mod error;
|
||||||
mod error_handling_ctx;
|
mod error_handling_ctx;
|
||||||
pub mod eww_state;
|
pub mod eww_state;
|
||||||
pub mod geometry;
|
pub mod geometry;
|
||||||
|
@ -33,9 +37,9 @@ pub mod script_var_handler;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
pub mod widgets;
|
pub mod widgets;
|
||||||
pub mod error;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
let eww_binary_name = std::env::args().next().unwrap();
|
||||||
let opts: opts::Opt = opts::Opt::from_env();
|
let opts: opts::Opt = opts::Opt::from_env();
|
||||||
|
|
||||||
let log_level_filter = if opts.log_debug { log::LevelFilter::Debug } else { log::LevelFilter::Info };
|
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)
|
.unwrap_or_else(EwwPaths::default)
|
||||||
.context("Failed to initialize eww paths")?;
|
.context("Failed to initialize eww paths")?;
|
||||||
|
|
||||||
match opts.action {
|
let would_show_logs = match opts.action {
|
||||||
opts::Action::ClientOnly(action) => {
|
opts::Action::ClientOnly(action) => {
|
||||||
client::handle_client_only_action(&paths, 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) => {
|
opts::Action::WithServer(action) => {
|
||||||
log::info!("Trying to find server process at socket {}", paths.get_ipc_socket_file().display());
|
if let Err(err) = handle_server_command(&paths, &action, 5) {
|
||||||
match net::UnixStream::connect(&paths.get_ipc_socket_file()) {
|
// connecting to the daemon failed. Thus, start the daemon here!
|
||||||
Ok(stream) => {
|
log::warn!("Failed to connect to daemon: {}", err);
|
||||||
log::info!("Connected to Eww server ({}).", &paths.get_ipc_socket_file().display());
|
log::info!("Initializing 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());
|
|
||||||
let _ = std::fs::remove_file(paths.get_ipc_socket_file());
|
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.");
|
let (command, response_recv) = action.into_daemon_command();
|
||||||
server::initialize_server(paths)?;
|
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.
|
/// 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 {
|
fn check_server_running(socket_path: impl AsRef<Path>) -> bool {
|
||||||
let response = net::UnixStream::connect(socket_path)
|
let response = net::UnixStream::connect(socket_path)
|
||||||
.ok()
|
.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()
|
response.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ use crate::app;
|
||||||
#[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 show_logs: bool,
|
||||||
pub config_path: Option<std::path::PathBuf>,
|
pub config_path: Option<std::path::PathBuf>,
|
||||||
pub action: Action,
|
pub action: Action,
|
||||||
}
|
}
|
||||||
|
@ -25,6 +26,10 @@ struct RawOpt {
|
||||||
#[structopt(short, long, global = true)]
|
#[structopt(short, long, global = true)]
|
||||||
config: Option<std::path::PathBuf>,
|
config: Option<std::path::PathBuf>,
|
||||||
|
|
||||||
|
/// Watch the log output after executing the command
|
||||||
|
#[structopt(long = "logs", global = true)]
|
||||||
|
show_logs: bool,
|
||||||
|
|
||||||
#[structopt(subcommand)]
|
#[structopt(subcommand)]
|
||||||
action: Action,
|
action: Action,
|
||||||
}
|
}
|
||||||
|
@ -136,8 +141,8 @@ 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, config } = other;
|
let RawOpt { action, log_debug, show_logs, config } = other;
|
||||||
Opt { action, log_debug, config_path: config }
|
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 anyhow::*;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -9,7 +14,7 @@ use std::{
|
||||||
};
|
};
|
||||||
use tokio::sync::mpsc::*;
|
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();
|
let (ui_send, mut ui_recv) = tokio::sync::mpsc::unbounded_channel();
|
||||||
|
|
||||||
std::env::set_current_dir(&paths.get_config_dir())
|
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!(
|
println!(
|
||||||
r#"
|
r#"
|
||||||
|
@ -76,6 +85,9 @@ pub fn initialize_server(paths: EwwPaths) -> Result<()> {
|
||||||
init_async_part(app.paths.clone(), ui_send);
|
init_async_part(app.paths.clone(), ui_send);
|
||||||
|
|
||||||
glib::MainContext::default().spawn_local(async move {
|
glib::MainContext::default().spawn_local(async move {
|
||||||
|
if let Some(action) = action {
|
||||||
|
app.handle_command(action);
|
||||||
|
}
|
||||||
while let Some(event) = ui_recv.recv().await {
|
while let Some(event) = ui_recv.recv().await {
|
||||||
app.handle_command(event);
|
app.handle_command(event);
|
||||||
}
|
}
|
||||||
|
@ -84,7 +96,7 @@ pub fn initialize_server(paths: EwwPaths) -> Result<()> {
|
||||||
gtk::main();
|
gtk::main();
|
||||||
log::info!("main application thread finished");
|
log::info!("main application thread finished");
|
||||||
|
|
||||||
Ok(())
|
Ok(ForkResult::Child)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_async_part(paths: EwwPaths, ui_send: UnboundedSender<app::DaemonCommand>) {
|
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 {
|
tokio::spawn(async move {
|
||||||
match daemon_resp_response.recv().await {
|
match daemon_resp_response.recv().await {
|
||||||
Some(app::DaemonResponse::Success(_)) => log::info!("Reloaded config successfully"),
|
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"),
|
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(());
|
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
|
/// 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
|
// detach from terminal
|
||||||
match unsafe { nix::unistd::fork()? } {
|
match unsafe { nix::unistd::fork()? } {
|
||||||
nix::unistd::ForkResult::Parent { .. } => {
|
|
||||||
std::process::exit(0);
|
|
||||||
}
|
|
||||||
nix::unistd::ForkResult::Child => {}
|
nix::unistd::ForkResult::Child => {}
|
||||||
|
nix::unistd::ForkResult::Parent { .. } => {
|
||||||
|
return Ok(ForkResult::Parent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let file = std::fs::OpenOptions::new()
|
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())?;
|
nix::unistd::dup2(fd, std::io::stderr().as_raw_fd())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(ForkResult::Child)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue