Packages
livebook
0.13.3
0.19.8
0.19.7
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.6
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.3
0.17.2
0.17.1
0.17.0
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.7
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.14.0-rc.1
0.14.0-rc.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.2
0.1.1
0.1.0
Automate code & data workflows with interactive notebooks
Current section
Files
Jump to
Current section
Files
lib/livebook/epmd.ex
defmodule Livebook.EPMD do
# A custom EPMD module used to bypass the epmd OS daemon
# on both Livebook and the runtimes.
@after_compile __MODULE__
# From Erlang/OTP 23+
@epmd_dist_version 6
@external_resource "priv/epmd/Elixir.Livebook.EPMD.beam"
@doc """
Gets a random child node name.
"""
def random_child_node do
String.to_atom(Livebook.EPMD.NodePool.get_name())
end
@doc """
Updates the port information for the given node.
"""
def update_child_node(node, port) do
Livebook.EPMD.NodePool.update_name(Atom.to_string(node), port)
end
@doc """
Returns the Livebook distribution port, if Livebook.EPMD is running, otherwise 0.
"""
def dist_port do
:persistent_term.get(:livebook_dist_port, 0)
end
# Custom EPMD callbacks
# Custom callback that registers the parent information.
# We read this information when trying to connect to the parent.
def start_link() do
with {:ok, [[node, port]]} <- :init.get_argument(:livebook_parent) do
[name, host] = :string.split(node, ~c"@")
:persistent_term.put(
:livebook_parent,
{name, host, List.to_atom(node), List.to_integer(port)}
)
end
:erl_epmd.start_link()
end
# Custom callback to register our current node port.
def register_node(name, port), do: register_node(name, port, :inet)
def register_node(name, port, family) do
:persistent_term.put(:livebook_dist_port, port)
:erl_epmd.register_node(name, port, family)
end
# Custom callback that accesses the parent information.
def port_please(name, host), do: port_please(name, host, :infinity)
def port_please(name, host, timeout) do
case livebook_port(name) do
0 -> :erl_epmd.port_please(name, host, timeout)
port -> {:port, port, @epmd_dist_version}
end
end
# If we are running inside a Livebook Runtime,
# we should be able to reach the parent directly
# or reach siblings through the parent.
defp livebook_port(name) do
case :persistent_term.get(:livebook_parent, nil) do
{parent_name, parent_host, parent_node, parent_port} ->
case match_name(name, parent_name) do
:parent -> parent_port
:sibling -> sibling_port(parent_node, name, parent_host)
:none -> 0
end
_ ->
0
end
end
defp match_name([x | name], [x | parent_name]), do: match_name(name, parent_name)
defp match_name([?-, ?- | _name], _parent), do: :sibling
defp match_name([], []), do: :parent
defp match_name(_name, _parent), do: :none
defp sibling_port(parent_node, name, host) do
:gen_server.call(
{Livebook.EPMD.NodePool, parent_node},
{:get_port, :erlang.list_to_binary(name ++ [?@] ++ host)},
5000
)
catch
_, _ -> 0
end
# Default EPMD callbacks
defdelegate listen_port_please(name, host), to: :erl_epmd
defdelegate names(host_name), to: :erl_epmd
defdelegate address_please(name, host, address_family), to: :erl_epmd
# Store .beam file in priv as well
def __after_compile__(_env, binary) do
File.mkdir_p!("priv/epmd")
File.write!("priv/epmd/Elixir.Livebook.EPMD.beam", binary)
Mix.Project.build_structure()
end
end