Add start/stop/restart subcommands. Fix #24. Fix #23. Fix #26

This commit is contained in:
Federico Terzi 2019-09-14 21:38:47 +02:00
parent 2b4835171a
commit 7a679c6395
6 changed files with 111 additions and 46 deletions

View File

@ -8,7 +8,7 @@ use std::io::Read;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::event::KeyModifier; use crate::event::KeyModifier;
use std::collections::HashSet; use std::collections::HashSet;
use log::{error, LevelFilter}; use log::{error};
use std::fmt; use std::fmt;
use std::error::Error; use std::error::Error;

View File

@ -1,5 +1,3 @@
use std::sync::mpsc;
use std::os::raw::{c_void};
use std::ffi::CString; use std::ffi::CString;
use crate::bridge::linux::*; use crate::bridge::linux::*;

View File

@ -8,7 +8,7 @@ use std::time::Duration;
use clap::{App, Arg, SubCommand, ArgMatches}; use clap::{App, Arg, SubCommand, ArgMatches};
use fs2::FileExt; use fs2::FileExt;
use log::{error, info, LevelFilter}; use log::{error, info, warn, LevelFilter};
use simplelog::{CombinedLogger, SharedLogger, TerminalMode, TermLogger}; use simplelog::{CombinedLogger, SharedLogger, TerminalMode, TermLogger};
use crate::config::ConfigSet; use crate::config::ConfigSet;
@ -69,6 +69,10 @@ fn main() {
.about("Start the daemon without spawning a new process.")) .about("Start the daemon without spawning a new process."))
.subcommand(SubCommand::with_name("start") .subcommand(SubCommand::with_name("start")
.about("Start the daemon spawning a new process in the background.")) .about("Start the daemon spawning a new process in the background."))
.subcommand(SubCommand::with_name("stop")
.about("Stop the espanso daemon."))
.subcommand(SubCommand::with_name("restart")
.about("Restart the espanso daemon."))
.subcommand(SubCommand::with_name("status") .subcommand(SubCommand::with_name("status")
.about("Check if the espanso daemon is running or not.")) .about("Check if the espanso daemon is running or not."))
.get_matches(); .get_matches();
@ -98,33 +102,43 @@ fn main() {
// Match the correct subcommand // Match the correct subcommand
if let Some(matches) = matches.subcommand_matches("dump") { if let Some(matches) = matches.subcommand_matches("cmd") {
cmd_main(config_set, matches);
return;
}
if let Some(_) = matches.subcommand_matches("dump") {
println!("{:#?}", config_set); println!("{:#?}", config_set);
return; return;
} }
if let Some(matches) = matches.subcommand_matches("detect") { if let Some(_) = matches.subcommand_matches("detect") {
detect_main(); detect_main();
return; return;
} }
if let Some(matches) = matches.subcommand_matches("daemon") { if let Some(_) = matches.subcommand_matches("daemon") {
daemon_main(config_set); daemon_main(config_set);
return; return;
} }
if let Some(matches) = matches.subcommand_matches("start") { if let Some(_) = matches.subcommand_matches("start") {
start_main(config_set); start_main(config_set);
return; return;
} }
if let Some(matches) = matches.subcommand_matches("status") { if let Some(_) = matches.subcommand_matches("status") {
status_main(); status_main();
return; return;
} }
if let Some(matches) = matches.subcommand_matches("cmd") { if let Some(_) = matches.subcommand_matches("stop") {
cmd_main(matches); stop_main(config_set);
return;
}
if let Some(_) = matches.subcommand_matches("restart") {
restart_main(config_set);
return; return;
} }
} }
@ -255,6 +269,49 @@ fn status_main() {
} }
} }
/// Stop subcommand, used to stop the daemon.
fn stop_main(config_set: ConfigSet) {
// Try to acquire lock file
let lock_file = acquire_lock();
if lock_file.is_some() {
println!("espanso daemon is not running.");
release_lock(lock_file.unwrap());
exit(3);
}
let res = send_command(config_set, IPCCommand{
id: "exit".to_owned(),
payload: "".to_owned(),
});
if let Err(e) = res {
println!("{}", e);
exit(1);
}else{
exit(0);
}
}
fn restart_main(config_set: ConfigSet) {
// Kill the daemon if running
let lock_file = acquire_lock();
if lock_file.is_none() {
// Terminate the current espanso daemon
send_command(config_set.clone(), IPCCommand{
id: "exit".to_owned(),
payload: "".to_owned(),
}).unwrap_or_else(|e| warn!("Unable to send IPC command to daemon: {}", e));
}else{
release_lock(lock_file.unwrap());
}
std::thread::sleep(Duration::from_millis(300));
// Restart the daemon
start_main(config_set);
}
/// Cli tool used to analyze active windows to extract useful information /// Cli tool used to analyze active windows to extract useful information
/// to create configuration filters. /// to create configuration filters.
fn detect_main() { fn detect_main() {
@ -289,23 +346,23 @@ fn detect_main() {
} }
} }
fn cmd_main(matches: &ArgMatches) { fn cmd_main(config_set: ConfigSet, matches: &ArgMatches) {
let command = if let Some(matches) = matches.subcommand_matches("exit") { let command = if let Some(_) = matches.subcommand_matches("exit") {
Some(IPCCommand { Some(IPCCommand {
id: String::from("exit"), id: String::from("exit"),
payload: String::from(""), payload: String::from(""),
}) })
}else if let Some(matches) = matches.subcommand_matches("toggle") { }else if let Some(_) = matches.subcommand_matches("toggle") {
Some(IPCCommand { Some(IPCCommand {
id: String::from("toggle"), id: String::from("toggle"),
payload: String::from(""), payload: String::from(""),
}) })
}else if let Some(matches) = matches.subcommand_matches("enable") { }else if let Some(_) = matches.subcommand_matches("enable") {
Some(IPCCommand { Some(IPCCommand {
id: String::from("enable"), id: String::from("enable"),
payload: String::from(""), payload: String::from(""),
}) })
}else if let Some(matches) = matches.subcommand_matches("disable") { }else if let Some(_) = matches.subcommand_matches("disable") {
Some(IPCCommand { Some(IPCCommand {
id: String::from("disable"), id: String::from("disable"),
payload: String::from(""), payload: String::from(""),
@ -315,15 +372,23 @@ fn cmd_main(matches: &ArgMatches) {
}; };
if let Some(command) = command { if let Some(command) = command {
let ipc_client = protocol::get_ipc_client(); let res = send_command(config_set, command);
ipc_client.send_command(command);
if res.is_ok() {
exit(0); exit(0);
}else{
println!("{}", res.unwrap_err());
}
} }
exit(1); exit(1);
} }
fn send_command(config_set: ConfigSet, command: IPCCommand) -> Result<(), String> {
let ipc_client = protocol::get_ipc_client();
ipc_client.send_command(command)
}
fn acquire_lock() -> Option<File> { fn acquire_lock() -> Option<File> {
let espanso_dir = context::get_data_dir(); let espanso_dir = context::get_data_dir();
let lock_file_path = espanso_dir.join("espanso.lock"); let lock_file_path = espanso_dir.join("espanso.lock");

View File

@ -1,10 +1,7 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::Result;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use crate::event::Event; use crate::event::Event;
use crate::event::Event::*;
use crate::event::ActionType; use crate::event::ActionType;
use crate::event::ActionType::*;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
mod windows; mod windows;
@ -17,7 +14,7 @@ pub trait IPCServer {
} }
pub trait IPCClient { pub trait IPCClient {
fn send_command(&self, command: IPCCommand); fn send_command(&self, command: IPCCommand) -> Result<(), String>;
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]

View File

@ -1,13 +1,11 @@
use std::io::{BufRead, BufReader, Read}; use std::io::{BufReader, Read};
use std::io::Write; use std::io::Write;
use std::os::unix::net::{UnixStream,UnixListener}; use std::os::unix::net::{UnixStream,UnixListener};
use std::thread; use log::{info, error, warn};
use log::{info, error};
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use super::IPCCommand; use super::IPCCommand;
use crate::context; use crate::context;
use crate::context::get_data_dir;
use crate::event::*; use crate::event::*;
const UNIX_SOCKET_NAME : &str = "espanso.sock"; const UNIX_SOCKET_NAME : &str = "espanso.sock";
@ -29,7 +27,9 @@ impl super::IPCServer for UnixIPCServer {
let espanso_dir = context::get_data_dir(); let espanso_dir = context::get_data_dir();
let unix_socket = espanso_dir.join(UNIX_SOCKET_NAME); let unix_socket = espanso_dir.join(UNIX_SOCKET_NAME);
std::fs::remove_file(unix_socket.clone()); std::fs::remove_file(unix_socket.clone()).unwrap_or_else(|e| {
warn!("Unable to delete Unix socket: {}", e);
});
let listener = UnixListener::bind(unix_socket.clone()).expect("Can't bind to Unix Socket"); let listener = UnixListener::bind(unix_socket.clone()).expect("Can't bind to Unix Socket");
info!("Binded to IPC unix socket: {}", unix_socket.as_path().display()); info!("Binded to IPC unix socket: {}", unix_socket.as_path().display());
@ -39,8 +39,9 @@ impl super::IPCServer for UnixIPCServer {
Ok(stream) => { Ok(stream) => {
let mut json_str= String::new(); let mut json_str= String::new();
let mut buf_reader = BufReader::new(stream); let mut buf_reader = BufReader::new(stream);
buf_reader.read_to_string(&mut json_str); let res = buf_reader.read_to_string(&mut json_str);
if res.is_ok() {
let command : Result<IPCCommand, serde_json::Error> = serde_json::from_str(&json_str); let command : Result<IPCCommand, serde_json::Error> = serde_json::from_str(&json_str);
match command { match command {
Ok(command) => { Ok(command) => {
@ -54,6 +55,7 @@ impl super::IPCServer for UnixIPCServer {
}, },
} }
} }
}
Err(err) => { Err(err) => {
println!("Error: {}", err); println!("Error: {}", err);
break; break;
@ -75,24 +77,27 @@ impl UnixIPCClient {
} }
impl super::IPCClient for UnixIPCClient { impl super::IPCClient for UnixIPCClient {
fn send_command(&self, command: IPCCommand) { fn send_command(&self, command: IPCCommand) -> Result<(), String> {
let espanso_dir = context::get_data_dir(); let espanso_dir = context::get_data_dir();
let unix_socket = espanso_dir.join(UNIX_SOCKET_NAME); let unix_socket = espanso_dir.join(UNIX_SOCKET_NAME);
// Open the stream // Open the stream
let mut stream = UnixStream::connect(unix_socket); let stream = UnixStream::connect(unix_socket);
match stream { match stream {
Ok(mut stream) => { Ok(mut stream) => {
let json_str = serde_json::to_string(&command); let json_str = serde_json::to_string(&command);
if let Ok(json_str) = json_str { if let Ok(json_str) = json_str {
stream.write_all(json_str.as_bytes()).unwrap_or_else(|e| { stream.write_all(json_str.as_bytes()).unwrap_or_else(|e| {
println!("Can't write to IPC socket"); println!("Can't write to IPC socket: {}", e);
}); });
return Ok(())
} }
}, },
Err(e) => { Err(e) => {
println!("Can't connect to daemon: {}", e); return Err(format!("Can't connect to daemon: {}", e))
} }
} }
Err("Can't send command".to_owned())
} }
} }

View File

@ -14,8 +14,8 @@ impl super::UIManager for LinuxUIManager {
} }
} }
fn show_menu(&self, menu: Vec<MenuItem>) { fn show_menu(&self, _menu: Vec<MenuItem>) {
unimplemented!() // Not implemented on linux
} }
} }