diff --git a/src/lib.rs b/src/lib.rs index d67c1259..8d32039c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -251,6 +251,16 @@ impl Device { .map_err(Into::into) } + /// Get our node info. + pub async fn self_node(&self) -> Result { + self.runtime + .control + .ask(ts_runtime::control_runner::SelfNode) + .await + .map_err(ts_runtime::Error::from)? + .ok_or(Error::RuntimeDegraded) + } + /// Attempt to gracefully shut down this device's runtime. /// /// Reports whether the device was fully shut down before the timeout. It is still shut diff --git a/ts_elixir/lib/tailscale.ex b/ts_elixir/lib/tailscale.ex index 00e4a2d3..b0775b37 100644 --- a/ts_elixir/lib/tailscale.ex +++ b/ts_elixir/lib/tailscale.ex @@ -82,4 +82,10 @@ defmodule Tailscale do Returns `{:ok, nil}` if there was no such peer. `:error` if the lookup encountered an error. """ def peer_by_name(dev, name), do: Tailscale.Native.peer_by_name(dev, name) + + @spec self_node(t()) :: {:ok, Tailscale.NodeInfo.t()} | {:error, any()} + @doc """ + Get this node's `m:Tailscale.NodeInfo`. + """ + defdelegate self_node(dev), to: Tailscale.Native end diff --git a/ts_elixir/lib/tailscale/native.ex b/ts_elixir/lib/tailscale/native.ex index e2f1b73d..057a5b91 100644 --- a/ts_elixir/lib/tailscale/native.ex +++ b/ts_elixir/lib/tailscale/native.ex @@ -163,4 +163,10 @@ defmodule Tailscale.Native do """ @spec peer_by_name(device(), String.t()) :: {:ok, %{} | nil} | {:error, any()} def peer_by_name(_dev, _name), do: err() + + @doc """ + Retrieve this node's info + """ + @spec self_node(device()) :: {:ok, %{}} | {:error, any()} + def self_node(_dev), do: err() end diff --git a/ts_elixir/native/ts_elixir/src/lib.rs b/ts_elixir/native/ts_elixir/src/lib.rs index 23a29f26..4de90cc2 100644 --- a/ts_elixir/native/ts_elixir/src/lib.rs +++ b/ts_elixir/native/ts_elixir/src/lib.rs @@ -147,6 +147,16 @@ fn peer_by_name(env: rustler::Env<'_>, dev: ResourceArc, name: &str) -> } } +#[rustler::nif(schedule = "DirtyIo")] +fn self_node(env: rustler::Env<'_>, dev: ResourceArc) -> impl Encoder { + let dev = dev.inner.clone(); + + match TOKIO_RUNTIME.block_on(async move { dev.self_node().await }) { + Err(e) => (atoms::error(), e.to_string()).encode(env), + Ok(peer) => (atoms::ok(), NodeInfo::from_node(env, peer)).encode(env), + } +} + fn ip_to_erl(env: rustler::Env, ip: impl Into) -> Term { match ip.into() { IpAddr::V4(ip) => { diff --git a/ts_python/src/lib.rs b/ts_python/src/lib.rs index c6f44165..33a5c1a3 100644 --- a/ts_python/src/lib.rs +++ b/ts_python/src/lib.rs @@ -168,6 +168,16 @@ impl Device { Ok(node.map(|node| NodeInfo::from(&node))) }) } + + /// Get this device's node info. + pub fn self_node<'p>(&self, py: Python<'p>) -> PyFut<'p> { + let dev = self.dev.clone(); + + future_into_py(py, async move { + let node = dev.self_node().await.map_err(py_value_err)?; + Ok(NodeInfo::from(&node)) + }) + } } fn sockaddr_as_tuple(s: SocketAddr) -> (IpAddr, u16) { diff --git a/ts_runtime/src/control_runner.rs b/ts_runtime/src/control_runner.rs index 92b41a28..14f94cbb 100644 --- a/ts_runtime/src/control_runner.rs +++ b/ts_runtime/src/control_runner.rs @@ -152,6 +152,26 @@ impl ControlRunner { deleg } + + /// Fetch the self node for this tailscale device. + #[message(ctx)] + pub fn self_node( + &self, + ctx: &mut Context>>, + ) -> DelegatedReply> { + let (deleg, replier) = ctx.reply_sender(); + + if let Some(replier) = replier { + let node = self.with_self_node(|node| node.clone()); + + tokio::spawn(async move { + let node = node.await; + replier.send(node) + }); + } + + deleg + } } impl Message, (), ()>> for ControlRunner {