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 crate::event::KeyModifier;
use std::collections::HashSet;
use log::{error, LevelFilter};
use log::{error};
use std::fmt;
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 crate::bridge::linux::*;

View File

@ -8,7 +8,7 @@ use std::time::Duration;
use clap::{App, Arg, SubCommand, ArgMatches};
use fs2::FileExt;
use log::{error, info, LevelFilter};
use log::{error, info, warn, LevelFilter};
use simplelog::{CombinedLogger, SharedLogger, TerminalMode, TermLogger};
use crate::config::ConfigSet;
@ -69,6 +69,10 @@ fn main() {
.about("Start the daemon without spawning a new process."))
.subcommand(SubCommand::with_name("start")
.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")
.about("Check if the espanso daemon is running or not."))
.get_matches();
@ -98,33 +102,43 @@ fn main() {
// 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);
return;
}
if let Some(matches) = matches.subcommand_matches("detect") {
if let Some(_) = matches.subcommand_matches("detect") {
detect_main();
return;
}
if let Some(matches) = matches.subcommand_matches("daemon") {
if let Some(_) = matches.subcommand_matches("daemon") {
daemon_main(config_set);
return;
}
if let Some(matches) = matches.subcommand_matches("start") {
if let Some(_) = matches.subcommand_matches("start") {
start_main(config_set);
return;
}
if let Some(matches) = matches.subcommand_matches("status") {
if let Some(_) = matches.subcommand_matches("status") {
status_main();
return;
}
if let Some(matches) = matches.subcommand_matches("cmd") {
cmd_main(matches);
if let Some(_) = matches.subcommand_matches("stop") {
stop_main(config_set);
return;
}
if let Some(_) = matches.subcommand_matches("restart") {
restart_main(config_set);
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
/// to create configuration filters.
fn detect_main() {
@ -289,23 +346,23 @@ fn detect_main() {
}
}
fn cmd_main(matches: &ArgMatches) {
let command = if let Some(matches) = matches.subcommand_matches("exit") {
fn cmd_main(config_set: ConfigSet, matches: &ArgMatches) {
let command = if let Some(_) = matches.subcommand_matches("exit") {
Some(IPCCommand {
id: String::from("exit"),
payload: String::from(""),
})
}else if let Some(matches) = matches.subcommand_matches("toggle") {
}else if let Some(_) = matches.subcommand_matches("toggle") {
Some(IPCCommand {
id: String::from("toggle"),
payload: String::from(""),
})
}else if let Some(matches) = matches.subcommand_matches("enable") {
}else if let Some(_) = matches.subcommand_matches("enable") {
Some(IPCCommand {
id: String::from("enable"),
payload: String::from(""),
})
}else if let Some(matches) = matches.subcommand_matches("disable") {
}else if let Some(_) = matches.subcommand_matches("disable") {
Some(IPCCommand {
id: String::from("disable"),
payload: String::from(""),
@ -315,15 +372,23 @@ fn cmd_main(matches: &ArgMatches) {
};
if let Some(command) = command {
let ipc_client = protocol::get_ipc_client();
ipc_client.send_command(command);
let res = send_command(config_set, command);
exit(0);
if res.is_ok() {
exit(0);
}else{
println!("{}", res.unwrap_err());
}
}
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> {
let espanso_dir = context::get_data_dir();
let lock_file_path = espanso_dir.join("espanso.lock");

View File

@ -1,10 +1,7 @@
use serde::{Deserialize, Serialize};
use serde_json::Result;
use std::sync::mpsc::Sender;
use crate::event::Event;
use crate::event::Event::*;
use crate::event::ActionType;
use crate::event::ActionType::*;
#[cfg(target_os = "windows")]
mod windows;
@ -17,7 +14,7 @@ pub trait IPCServer {
}
pub trait IPCClient {
fn send_command(&self, command: IPCCommand);
fn send_command(&self, command: IPCCommand) -> Result<(), String>;
}
#[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::os::unix::net::{UnixStream,UnixListener};
use std::thread;
use log::{info, error};
use log::{info, error, warn};
use std::sync::mpsc::Sender;
use super::IPCCommand;
use crate::context;
use crate::context::get_data_dir;
use crate::event::*;
const UNIX_SOCKET_NAME : &str = "espanso.sock";
@ -29,7 +27,9 @@ impl super::IPCServer for UnixIPCServer {
let espanso_dir = context::get_data_dir();
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");
info!("Binded to IPC unix socket: {}", unix_socket.as_path().display());
@ -39,19 +39,21 @@ impl super::IPCServer for UnixIPCServer {
Ok(stream) => {
let mut json_str= String::new();
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);
let command : Result<IPCCommand, serde_json::Error> = serde_json::from_str(&json_str);
match command {
Ok(command) => {
let event = command.to_event();
if let Some(event) = event {
event_channel.send(event).expect("Broken event channel");
}
},
Err(e) => {
error!("Error deserializing JSON command: {}", e);
},
if res.is_ok() {
let command : Result<IPCCommand, serde_json::Error> = serde_json::from_str(&json_str);
match command {
Ok(command) => {
let event = command.to_event();
if let Some(event) = event {
event_channel.send(event).expect("Broken event channel");
}
},
Err(e) => {
error!("Error deserializing JSON command: {}", e);
},
}
}
}
Err(err) => {
@ -75,24 +77,27 @@ impl 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 unix_socket = espanso_dir.join(UNIX_SOCKET_NAME);
// Open the stream
let mut stream = UnixStream::connect(unix_socket);
let stream = UnixStream::connect(unix_socket);
match stream {
Ok(mut stream) => {
let json_str = serde_json::to_string(&command);
if let Ok(json_str) = json_str {
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) => {
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>) {
unimplemented!()
fn show_menu(&self, _menu: Vec<MenuItem>) {
// Not implemented on linux
}
}