Last active
April 5, 2018 23:39
-
-
Save karmajunkie/03f6949c839f55848d96a6fdc2aca7ba to your computer and use it in GitHub Desktop.
Implementation of an epmd replacement
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# A module containing the function that determines the port number | |
# based on a node name. | |
# Taken from https://www.erlang-solutions.com/blog/erlang-and-elixir-distribution-without-epmd.html | |
defmodule Epmdless do | |
def dist_port(name) when is_atom(name) do | |
dist_port Atom.to_string name | |
end | |
def dist_port(name) when is_list(name) do | |
dist_port List.to_string name | |
end | |
def dist_port(name) when is_binary(name) do | |
# Figure out the base port. If not specified using the | |
# inet_dist_base_port kernel environment variable, default to | |
# 4370, one above the epmd port. | |
base_port = :application.get_env :kernel, :inet_dist_base_port, 4370 | |
# Now, figure out our "offset" on top of the base port. The | |
# offset is the integer just to the left of the @ sign in our node | |
# name. If there is no such number, the offset is 0. | |
# | |
# Also handle the case when no hostname was specified. | |
node_name = Regex.replace ~r/@.*$/, name, "" | |
offset = | |
case Regex.run ~r/[0-9]+$/, node_name do | |
nil -> | |
0 | |
[offset_as_string] -> | |
String.to_integer offset_as_string | |
end | |
base_port + offset | |
end | |
end | |
defmodule Epmdless_dist do | |
def listen(name) do | |
# Here we figure out what port we want to listen on. | |
port = Epmdless.dist_port name | |
# Set both "min" and "max" variables, to force the port number to | |
# this one. | |
:ok = :application.set_env :kernel, :inet_dist_listen_min, port | |
:ok = :application.set_env :kernel, :inet_dist_listen_max, port | |
# Finally run the real function! | |
:inet_tcp_dist.listen name | |
end | |
def select(node) do | |
:inet_tcp_dist.select node | |
end | |
def accept(listen) do | |
:inet_tcp_dist.accept listen | |
end | |
def accept_connection(accept_pid, socket, my_node, allowed, setup_time) do | |
:inet_tcp_dist.accept_connection accept_pid, socket, my_node, allowed, setup_time | |
end | |
def setup(node, type, my_node, long_or_short_names, setup_time) do | |
:inet_tcp_dist.setup node, type, my_node, long_or_short_names, setup_time | |
end | |
def close(listen) do | |
:inet_tcp_dist.close listen | |
end | |
def childspecs do | |
:inet_tcp_dist.childspecs | |
end | |
end | |
defmodule Epmdless_epmd_client do | |
# erl_distribution wants us to start a worker process. We don't | |
# need one, though. | |
def start_link do | |
:ignore | |
end | |
# As of Erlang/OTP 19.1, register_node/3 is used instead of | |
# register_node/2, passing along the address family, 'inet_tcp' or | |
# 'inet6_tcp'. This makes no difference for our purposes. | |
def register_node(name, port, _family) do | |
register_node(name, port) | |
end | |
def register_node(_name, _port) do | |
# This is where we would connect to epmd and tell it which port | |
# we're listening on, but since we're epmd-less, we don't do that. | |
# Need to return a "creation" number between 1 and 3. | |
creation = :rand.uniform 3 | |
{:ok, creation} | |
end | |
def port_please(name, _ip) do | |
port = Epmdless.dist_port name | |
# The distribution protocol version number has been 5 ever since | |
# Erlang/OTP R6. | |
version = 5 | |
{:port, port, version} | |
end | |
def names(_hostname) do | |
# Since we don't have epmd, we don't really know what other nodes | |
# there are. | |
{:error, :address} | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
for anybody else reading this, note that it comes from this post
https://www.erlang-solutions.com/blog/erlang-and-elixir-distribution-without-epmd.html