//////////////////////////////////////////////////////////////// // Debugging use super::*; use routing_table::*; fn get_bucket_entry_state(text: &str) -> Option { if text == "dead" { Some(BucketEntryState::Dead) } else if text == "reliable" { Some(BucketEntryState::Reliable) } else if text == "unreliable" { Some(BucketEntryState::Unreliable) } else { None } } fn get_number(text: &str) -> Option { usize::from_str(text).ok() } fn get_dht_key(text: &str) -> Option { DHTKey::try_decode(text).ok() } fn get_debug_argument Option>( value: &str, context: &str, argument: &str, getter: G, ) -> Result { if let Some(val) = getter(value) { Ok(val) } else { Err(VeilidAPIError::InvalidArgument { context: context.to_owned(), argument: argument.to_owned(), value: value.to_owned(), }) } } fn get_debug_argument_at Option>( debug_args: &[String], pos: usize, context: &str, argument: &str, getter: G, ) -> Result { if pos >= debug_args.len() { return Err(VeilidAPIError::MissingArgument { context: context.to_owned(), argument: argument.to_owned(), }); } let value = &debug_args[pos]; if let Some(val) = getter(value) { Ok(val) } else { Err(VeilidAPIError::InvalidArgument { context: context.to_owned(), argument: argument.to_owned(), value: value.to_owned(), }) } } impl VeilidAPI { async fn debug_buckets(&self, args: String) -> Result { let args: Vec = args.split_whitespace().map(|s| s.to_owned()).collect(); let mut min_state = BucketEntryState::Unreliable; if args.len() == 1 { min_state = get_debug_argument( &args[0], "debug_buckets", "min_state", get_bucket_entry_state, )?; } // Dump routing table bucket info let rpc = self.rpc_processor()?; let routing_table = rpc.routing_table(); Ok(routing_table.debug_info_buckets(min_state)) } async fn debug_dialinfo(&self, _args: String) -> Result { // Dump routing table dialinfo let rpc = self.rpc_processor()?; let routing_table = rpc.routing_table(); Ok(routing_table.debug_info_dialinfo()) } async fn debug_entries(&self, args: String) -> Result { let args: Vec = args.split_whitespace().map(|s| s.to_owned()).collect(); let mut min_state = BucketEntryState::Unreliable; let mut limit = 20; for arg in args { if let Some(ms) = get_bucket_entry_state(&arg) { min_state = ms; } else if let Some(lim) = get_number(&arg) { limit = lim; } else { return Err(VeilidAPIError::InvalidArgument { context: "debug_entries".to_owned(), argument: "unknown".to_owned(), value: arg, }); } } // Dump routing table entries let rpc = self.rpc_processor()?; let routing_table = rpc.routing_table(); Ok(routing_table.debug_info_entries(limit, min_state)) } async fn debug_entry(&self, args: String) -> Result { let args: Vec = args.split_whitespace().map(|s| s.to_owned()).collect(); let node_id = get_debug_argument_at(&args, 0, "debug_entry", "node_id", get_dht_key)?; // Dump routing table entry let rpc = self.rpc_processor()?; let routing_table = rpc.routing_table(); Ok(routing_table.debug_info_entry(node_id)) } async fn debug_nodeinfo(&self, _args: String) -> Result { // Dump routing table entry let rpc = self.rpc_processor()?; let routing_table = rpc.routing_table(); Ok(routing_table.debug_info_nodeinfo()) } async fn debug_config(&self, args: String) -> Result { let config = self.config()?; let args = args.trim_start(); if args.is_empty() { return config .get_key_json("") .map_err(|e| VeilidAPIError::Internal { message: e }); } let (arg, rest) = args.split_once(' ').unwrap_or((args, "")); let rest = rest.trim_start().to_owned(); // Must be detached if matches!( self.get_state().await?.attachment, AttachmentState::Detached | AttachmentState::Detaching ) { return Err(VeilidAPIError::Internal { message: "Must be detached to change config".to_owned(), }); } // One argument is 'config get' if rest.is_empty() { return config .get_key_json(arg) .map_err(|e| VeilidAPIError::Internal { message: e }); } config .set_key_json(arg, &rest) .map_err(|e| VeilidAPIError::Internal { message: e })?; Ok("Config value set".to_owned()) } async fn debug_purge(&self, args: String) -> Result { let args: Vec = args.split_whitespace().map(|s| s.to_owned()).collect(); if !args.is_empty() { if args[0] == "buckets" { // Must be detached if matches!( self.get_state().await?.attachment, AttachmentState::Detached | AttachmentState::Detaching ) { return Err(VeilidAPIError::Internal { message: "Must be detached to purge".to_owned(), }); } self.network_manager()?.routing_table().purge(); Ok("Buckets purged".to_owned()) } else { Err(VeilidAPIError::InvalidArgument { context: "debug_purge".to_owned(), argument: "parameter".to_owned(), value: args[0].clone(), }) } } else { Err(VeilidAPIError::MissingArgument { context: "debug_purge".to_owned(), argument: "parameter".to_owned(), }) } } async fn debug_attach(&self, _args: String) -> Result { if !matches!( self.get_state().await?.attachment, AttachmentState::Detached ) { return Err(VeilidAPIError::Internal { message: "Not detached".to_owned(), }); }; self.attach().await?; Ok("Attached".to_owned()) } async fn debug_detach(&self, _args: String) -> Result { if matches!( self.get_state().await?.attachment, AttachmentState::Detaching ) { return Err(VeilidAPIError::Internal { message: "Not attached".to_owned(), }); }; self.detach().await?; Ok("Detached".to_owned()) } pub async fn debug_help(&self, _args: String) -> Result { Ok(r#">>> Debug commands: buckets [dead|reliable] dialinfo entries [dead|reliable] [limit] entry [node_id] nodeinfo config [key [new value]] purge buckets attach detach "# .to_owned()) } pub async fn debug(&self, args: String) -> Result { let args = args.trim_start(); if args.is_empty() { // No arguments runs help command return self.debug_help("".to_owned()).await; } let (arg, rest) = args.split_once(' ').unwrap_or((args, "")); let rest = rest.trim_start().to_owned(); let mut out = String::new(); if arg == "buckets" { out += self.debug_buckets(rest).await?.as_str(); } else if arg == "dialinfo" { out += self.debug_dialinfo(rest).await?.as_str(); } else if arg == "entries" { out += self.debug_entries(rest).await?.as_str(); } else if arg == "entry" { out += self.debug_entry(rest).await?.as_str(); } else if arg == "nodeinfo" { out += self.debug_nodeinfo(rest).await?.as_str(); } else if arg == "purge" { out += self.debug_purge(rest).await?.as_str(); } else if arg == "attach" { out += self.debug_attach(rest).await?.as_str(); } else if arg == "detach" { out += self.debug_detach(rest).await?.as_str(); } else if arg == "config" { out += self.debug_config(rest).await?.as_str(); } else { out += ">>> Unknown command\n"; } Ok(out) } }