mirror of
https://gitlab.com/veilid/veilid.git
synced 2025-03-15 02:16:38 -04:00
add connect command
This commit is contained in:
parent
5f594e2aa7
commit
06b7b60fb7
@ -57,6 +57,7 @@ struct CommandProcessorInner {
|
||||
#[derive(Clone)]
|
||||
pub struct CommandProcessor {
|
||||
inner: Arc<Mutex<CommandProcessorInner>>,
|
||||
settings: Arc<Settings>,
|
||||
}
|
||||
|
||||
impl CommandProcessor {
|
||||
@ -75,6 +76,7 @@ impl CommandProcessor {
|
||||
last_call_id: None,
|
||||
enable_app_messages: false,
|
||||
})),
|
||||
settings: Arc::new(settings.clone()),
|
||||
}
|
||||
}
|
||||
pub fn set_client_api_connection(&self, capi: ClientApiConnection) {
|
||||
@ -186,6 +188,54 @@ Core Debug Commands:
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn cmd_connect(&self, rest: Option<String>, callback: UICallback) -> Result<(), String> {
|
||||
trace!("CommandProcessor::cmd_connect");
|
||||
let capi = self.capi();
|
||||
let ui = self.ui_sender();
|
||||
|
||||
let this = self.clone();
|
||||
spawn_detached_local("cmd connect", async move {
|
||||
capi.disconnect().await;
|
||||
|
||||
if let Some(rest) = rest {
|
||||
if let Ok(subnode_index) = u16::from_str(&rest) {
|
||||
let ipc_path = this
|
||||
.settings
|
||||
.resolve_ipc_path(this.settings.ipc_path.clone(), subnode_index);
|
||||
this.set_ipc_path(ipc_path);
|
||||
this.set_network_address(None);
|
||||
} else if let Some(ipc_path) =
|
||||
this.settings.resolve_ipc_path(Some(rest.clone().into()), 0)
|
||||
{
|
||||
this.set_ipc_path(Some(ipc_path));
|
||||
this.set_network_address(None);
|
||||
} else if let Ok(Some(network_address)) =
|
||||
this.settings.resolve_network_address(Some(rest.clone()))
|
||||
{
|
||||
if let Some(addr) = network_address.first() {
|
||||
this.set_network_address(Some(*addr));
|
||||
this.set_ipc_path(None);
|
||||
} else {
|
||||
ui.add_node_event(
|
||||
Level::Error,
|
||||
&format!("Invalid network address: {}", rest),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
ui.add_node_event(
|
||||
Level::Error,
|
||||
&format!("Invalid connection string: {}", rest),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.start_connection();
|
||||
ui.send_callback(callback);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn cmd_debug(&self, command_line: String, callback: UICallback) -> Result<(), String> {
|
||||
trace!("CommandProcessor::cmd_debug");
|
||||
let capi = self.capi();
|
||||
@ -331,6 +381,7 @@ Core Debug Commands:
|
||||
"exit" => self.cmd_exit(callback),
|
||||
"quit" => self.cmd_exit(callback),
|
||||
"disconnect" => self.cmd_disconnect(callback),
|
||||
"connect" => self.cmd_connect(rest, callback),
|
||||
"shutdown" => self.cmd_shutdown(callback),
|
||||
"change_log_level" => self.cmd_change_log_level(rest, callback),
|
||||
"change_log_ignore" => self.cmd_change_log_ignore(rest, callback),
|
||||
|
@ -28,10 +28,11 @@ pub struct InteractiveUIInner {
|
||||
#[derive(Clone)]
|
||||
pub struct InteractiveUI {
|
||||
inner: Arc<Mutex<InteractiveUIInner>>,
|
||||
_settings: Arc<Settings>,
|
||||
}
|
||||
|
||||
impl InteractiveUI {
|
||||
pub fn new(_settings: &Settings) -> (Self, InteractiveUISender) {
|
||||
pub fn new(settings: &Settings) -> (Self, InteractiveUISender) {
|
||||
let (cssender, csreceiver) = flume::unbounded::<ConnectionState>();
|
||||
|
||||
let term = Term::stdout();
|
||||
@ -45,9 +46,10 @@ impl InteractiveUI {
|
||||
error: None,
|
||||
done: Some(StopSource::new()),
|
||||
connection_state_receiver: csreceiver,
|
||||
log_enabled: false,
|
||||
log_enabled: true,
|
||||
enable_color,
|
||||
})),
|
||||
_settings: Arc::new(settings.clone()),
|
||||
};
|
||||
|
||||
let ui_sender = InteractiveUISender {
|
||||
@ -169,7 +171,6 @@ impl InteractiveUI {
|
||||
eprintln!("Error: {:?}", e);
|
||||
self.inner.lock().done.take();
|
||||
}
|
||||
self.inner.lock().log_enabled = true;
|
||||
}
|
||||
} else if line == "log warn" {
|
||||
let opt_cmdproc = self.inner.lock().cmdproc.clone();
|
||||
@ -181,7 +182,6 @@ impl InteractiveUI {
|
||||
eprintln!("Error: {:?}", e);
|
||||
self.inner.lock().done.take();
|
||||
}
|
||||
self.inner.lock().log_enabled = true;
|
||||
}
|
||||
} else if line == "log info" {
|
||||
let opt_cmdproc = self.inner.lock().cmdproc.clone();
|
||||
@ -193,7 +193,6 @@ impl InteractiveUI {
|
||||
eprintln!("Error: {:?}", e);
|
||||
self.inner.lock().done.take();
|
||||
}
|
||||
self.inner.lock().log_enabled = true;
|
||||
}
|
||||
} else if line == "log debug" || line == "log" {
|
||||
let opt_cmdproc = self.inner.lock().cmdproc.clone();
|
||||
@ -205,6 +204,8 @@ impl InteractiveUI {
|
||||
eprintln!("Error: {:?}", e);
|
||||
self.inner.lock().done.take();
|
||||
}
|
||||
}
|
||||
if line == "log" {
|
||||
self.inner.lock().log_enabled = true;
|
||||
}
|
||||
} else if line == "log trace" {
|
||||
@ -217,7 +218,6 @@ impl InteractiveUI {
|
||||
eprintln!("Error: {:?}", e);
|
||||
self.inner.lock().done.take();
|
||||
}
|
||||
self.inner.lock().log_enabled = true;
|
||||
}
|
||||
} else if line == "log off" {
|
||||
let opt_cmdproc = self.inner.lock().cmdproc.clone();
|
||||
@ -229,9 +229,27 @@ impl InteractiveUI {
|
||||
eprintln!("Error: {:?}", e);
|
||||
self.inner.lock().done.take();
|
||||
}
|
||||
self.inner.lock().log_enabled = false;
|
||||
}
|
||||
} else if line == "log hide" || line == "log disable" {
|
||||
self.inner.lock().log_enabled = false;
|
||||
} else if line == "log show" || line == "log enable" {
|
||||
self.inner.lock().log_enabled = true;
|
||||
} else if !line.is_empty() {
|
||||
if line == "help" {
|
||||
let _ = writeln!(
|
||||
stdout,
|
||||
r#"
|
||||
Interactive Mode Commands:
|
||||
help - Display this help
|
||||
clear - Clear the screen
|
||||
log [level] - Set the client api log level for the node to one of: error,warn,info,debug,trace,off
|
||||
hide|disable - Turn off viewing the log without changing the log level for the node
|
||||
show|enable - Turn on viewing the log without changing the log level for the node
|
||||
- With no option, 'log' turns on viewing the log and sets the level to 'debug'
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
let cmdproc = self.inner.lock().cmdproc.clone();
|
||||
if let Some(cmdproc) = &cmdproc {
|
||||
if let Err(e) = cmdproc.run_command(
|
||||
|
@ -3,7 +3,7 @@
|
||||
#![deny(unused_must_use)]
|
||||
#![recursion_limit = "256"]
|
||||
|
||||
use crate::{settings::NamedSocketAddrs, tools::*, ui::*};
|
||||
use crate::{tools::*, ui::*};
|
||||
|
||||
use clap::{Parser, ValueEnum};
|
||||
use flexi_logger::*;
|
||||
@ -37,7 +37,7 @@ struct CmdlineArgs {
|
||||
ipc_path: Option<PathBuf>,
|
||||
/// Subnode index to use when connecting
|
||||
#[arg(short('n'), long, default_value = "0")]
|
||||
subnode_index: usize,
|
||||
subnode_index: u16,
|
||||
/// Address to connect to
|
||||
#[arg(long, short = 'a')]
|
||||
address: Option<String>,
|
||||
@ -47,9 +47,9 @@ struct CmdlineArgs {
|
||||
/// Specify a configuration file to use
|
||||
#[arg(short = 'c', long, value_name = "FILE")]
|
||||
config_file: Option<PathBuf>,
|
||||
/// log level
|
||||
#[arg(value_enum)]
|
||||
log_level: Option<LogLevel>,
|
||||
/// Log level for the CLI itself (not for the Veilid node)
|
||||
#[arg(long, value_enum)]
|
||||
cli_log_level: Option<LogLevel>,
|
||||
/// interactive
|
||||
#[arg(long, short = 'i', group = "execution_mode")]
|
||||
interactive: bool,
|
||||
@ -93,11 +93,11 @@ fn main() -> Result<(), String> {
|
||||
.map_err(|e| format!("configuration is invalid: {}", e))?;
|
||||
|
||||
// Set config from command line
|
||||
if let Some(LogLevel::Debug) = args.log_level {
|
||||
if let Some(LogLevel::Debug) = args.cli_log_level {
|
||||
settings.logging.level = settings::LogLevel::Debug;
|
||||
settings.logging.terminal.enabled = true;
|
||||
}
|
||||
if let Some(LogLevel::Trace) = args.log_level {
|
||||
if let Some(LogLevel::Trace) = args.cli_log_level {
|
||||
settings.logging.level = settings::LogLevel::Trace;
|
||||
settings.logging.terminal.enabled = true;
|
||||
}
|
||||
@ -248,59 +248,14 @@ fn main() -> Result<(), String> {
|
||||
// Determine IPC path to try
|
||||
let mut client_api_ipc_path = None;
|
||||
if enable_ipc {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(windows)] {
|
||||
if let Some(ipc_path) = args.ipc_path.or(settings.ipc_path.clone()) {
|
||||
if is_ipc_socket_path(&ipc_path) {
|
||||
// try direct path
|
||||
enable_network = false;
|
||||
client_api_ipc_path = Some(ipc_path);
|
||||
} else {
|
||||
// try subnode index inside path
|
||||
let ipc_path = ipc_path.join(args.subnode_index.to_string());
|
||||
if is_ipc_socket_path(&ipc_path) {
|
||||
// subnode indexed path exists
|
||||
enable_network = false;
|
||||
client_api_ipc_path = Some(ipc_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let Some(ipc_path) = args.ipc_path.or(settings.ipc_path.clone()) {
|
||||
if is_ipc_socket_path(&ipc_path) {
|
||||
// try direct path
|
||||
enable_network = false;
|
||||
client_api_ipc_path = Some(ipc_path);
|
||||
} else if ipc_path.exists() && ipc_path.is_dir() {
|
||||
// try subnode index inside path
|
||||
let ipc_path = ipc_path.join(args.subnode_index.to_string());
|
||||
if is_ipc_socket_path(&ipc_path) {
|
||||
// subnode indexed path exists
|
||||
enable_network = false;
|
||||
client_api_ipc_path = Some(ipc_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
client_api_ipc_path = settings.resolve_ipc_path(args.ipc_path, args.subnode_index);
|
||||
if client_api_ipc_path.is_some() {
|
||||
enable_network = false;
|
||||
}
|
||||
}
|
||||
let mut client_api_network_addresses = None;
|
||||
if enable_network {
|
||||
let args_address = if let Some(args_address) = args.address {
|
||||
match NamedSocketAddrs::try_from(args_address) {
|
||||
Ok(v) => Some(v),
|
||||
Err(e) => {
|
||||
return Err(format!("Invalid server address: {}", e));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(address_arg) = args_address.or(settings.address.clone()) {
|
||||
client_api_network_addresses = Some(address_arg.addrs);
|
||||
} else if let Some(address) = settings.address.clone() {
|
||||
client_api_network_addresses = Some(address.addrs.clone());
|
||||
}
|
||||
client_api_network_addresses = settings.resolve_network_address(args.address)?;
|
||||
}
|
||||
|
||||
// Create command processor
|
||||
|
@ -1,5 +1,6 @@
|
||||
use directories::*;
|
||||
|
||||
use crate::tools::*;
|
||||
use serde_derive::*;
|
||||
use std::ffi::OsStr;
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
@ -118,7 +119,7 @@ pub fn convert_loglevel(log_level: LogLevel) -> log::LevelFilter {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NamedSocketAddrs {
|
||||
pub _name: String,
|
||||
pub addrs: Vec<SocketAddr>,
|
||||
@ -148,26 +149,26 @@ impl<'de> serde::Deserialize<'de> for NamedSocketAddrs {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct Terminal {
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct File {
|
||||
pub enabled: bool,
|
||||
pub directory: String,
|
||||
pub append: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct Logging {
|
||||
pub terminal: Terminal,
|
||||
pub file: File,
|
||||
pub level: LogLevel,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct Colors {
|
||||
pub background: String,
|
||||
pub shadow: String,
|
||||
@ -182,7 +183,7 @@ pub struct Colors {
|
||||
pub highlight_text: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct LogColors {
|
||||
pub trace: String,
|
||||
pub debug: String,
|
||||
@ -191,7 +192,7 @@ pub struct LogColors {
|
||||
pub error: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct Theme {
|
||||
pub shadow: bool,
|
||||
pub borders: String,
|
||||
@ -199,24 +200,24 @@ pub struct Theme {
|
||||
pub log_colors: LogColors,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct NodeLog {
|
||||
pub scrollback: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct CommandLine {
|
||||
pub history_size: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct Interface {
|
||||
pub theme: Theme,
|
||||
pub node_log: NodeLog,
|
||||
pub command_line: CommandLine,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct Settings {
|
||||
pub enable_ipc: bool,
|
||||
pub ipc_path: Option<PathBuf>,
|
||||
@ -229,6 +230,90 @@ pub struct Settings {
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub fn new(config_file: Option<&OsStr>) -> Result<Self, config::ConfigError> {
|
||||
// Load the default config
|
||||
let mut cfg = load_default_config()?;
|
||||
|
||||
// Merge in the config file if we have one
|
||||
if let Some(config_file) = config_file {
|
||||
let config_file_path = Path::new(config_file);
|
||||
// If the user specifies a config file on the command line then it must exist
|
||||
cfg = load_config(cfg, config_file_path)?;
|
||||
}
|
||||
|
||||
// Generate config
|
||||
cfg.try_deserialize()
|
||||
}
|
||||
|
||||
pub fn resolve_ipc_path(
|
||||
&self,
|
||||
ipc_path: Option<PathBuf>,
|
||||
subnode_index: u16,
|
||||
) -> Option<PathBuf> {
|
||||
let mut client_api_ipc_path = None;
|
||||
// Determine IPC path to try
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(windows)] {
|
||||
if let Some(ipc_path) = ipc_path.or(self.ipc_path.clone()) {
|
||||
if is_ipc_socket_path(&ipc_path) {
|
||||
// try direct path
|
||||
enable_network = false;
|
||||
client_api_ipc_path = Some(ipc_path);
|
||||
} else {
|
||||
// try subnode index inside path
|
||||
let ipc_path = ipc_path.join(subnode_index.to_string());
|
||||
if is_ipc_socket_path(&ipc_path) {
|
||||
// subnode indexed path exists
|
||||
client_api_ipc_path = Some(ipc_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let Some(ipc_path) = ipc_path.or(self.ipc_path.clone()) {
|
||||
if is_ipc_socket_path(&ipc_path) {
|
||||
// try direct path
|
||||
client_api_ipc_path = Some(ipc_path);
|
||||
} else if ipc_path.exists() && ipc_path.is_dir() {
|
||||
// try subnode index inside path
|
||||
let ipc_path = ipc_path.join(subnode_index.to_string());
|
||||
if is_ipc_socket_path(&ipc_path) {
|
||||
// subnode indexed path exists
|
||||
client_api_ipc_path = Some(ipc_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
client_api_ipc_path
|
||||
}
|
||||
|
||||
pub fn resolve_network_address(
|
||||
&self,
|
||||
address: Option<String>,
|
||||
) -> Result<Option<Vec<SocketAddr>>, String> {
|
||||
let mut client_api_network_addresses = None;
|
||||
|
||||
let args_address = if let Some(args_address) = address {
|
||||
match NamedSocketAddrs::try_from(args_address) {
|
||||
Ok(v) => Some(v),
|
||||
Err(e) => {
|
||||
return Err(format!("Invalid server address: {}", e));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(address_arg) = args_address.or(self.address.clone()) {
|
||||
client_api_network_addresses = Some(address_arg.addrs);
|
||||
} else if let Some(address) = self.address.clone() {
|
||||
client_api_network_addresses = Some(address.addrs.clone());
|
||||
}
|
||||
Ok(client_api_network_addresses)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
#[cfg_attr(windows, expect(dead_code))]
|
||||
fn get_server_default_directory(subpath: &str) -> PathBuf {
|
||||
#[cfg(unix)]
|
||||
@ -284,21 +369,6 @@ impl Settings {
|
||||
|
||||
default_log_directory
|
||||
}
|
||||
|
||||
pub fn new(config_file: Option<&OsStr>) -> Result<Self, config::ConfigError> {
|
||||
// Load the default config
|
||||
let mut cfg = load_default_config()?;
|
||||
|
||||
// Merge in the config file if we have one
|
||||
if let Some(config_file) = config_file {
|
||||
let config_file_path = Path::new(config_file);
|
||||
// If the user specifies a config file on the command line then it must exist
|
||||
cfg = load_config(cfg, config_file_path)?;
|
||||
}
|
||||
|
||||
// Generate config
|
||||
cfg.try_deserialize()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -33,10 +33,10 @@ use veilid_logs::*;
|
||||
#[derive(Args, Debug, Clone)]
|
||||
#[group(multiple = false)]
|
||||
pub struct Logging {
|
||||
/// Turn on debug logging on the terminal
|
||||
/// Turn on debug logging on the terminal and over the client api
|
||||
#[arg(long)]
|
||||
debug: bool,
|
||||
/// Turn on trace logging on the terminal
|
||||
/// Turn on trace logging on the terminal and over the client api
|
||||
#[arg(long)]
|
||||
trace: bool,
|
||||
}
|
||||
@ -217,10 +217,14 @@ fn main() -> EyreResult<()> {
|
||||
if args.logging.debug {
|
||||
settingsrw.logging.terminal.enabled = true;
|
||||
settingsrw.logging.terminal.level = LogLevel::Debug;
|
||||
settingsrw.logging.api.enabled = true;
|
||||
settingsrw.logging.api.level = LogLevel::Debug;
|
||||
}
|
||||
if args.logging.trace {
|
||||
settingsrw.logging.terminal.enabled = true;
|
||||
settingsrw.logging.terminal.level = LogLevel::Trace;
|
||||
settingsrw.logging.api.enabled = true;
|
||||
settingsrw.logging.api.level = LogLevel::Trace;
|
||||
}
|
||||
|
||||
if let Some(subnode_index) = args.subnode_index {
|
||||
|
@ -42,7 +42,9 @@ fn io_error_kind_from_error<T>(e: io::Error) -> io::Result<NetworkResult<T>> {
|
||||
io::ErrorKind::InvalidInput | io::ErrorKind::InvalidData => {
|
||||
Ok(NetworkResult::InvalidMessage(e.to_string()))
|
||||
}
|
||||
io::ErrorKind::AddrNotAvailable => Ok(NetworkResult::AlreadyExists(e)),
|
||||
io::ErrorKind::AddrNotAvailable | io::ErrorKind::AddrInUse => {
|
||||
Ok(NetworkResult::AlreadyExists(e))
|
||||
}
|
||||
_ => Err(e),
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user