Skip to content

Commit

Permalink
Extend erl_call with a new flag to access a node at HOSTNAME:PORT
Browse files Browse the repository at this point in the history
This commit adds the flag -address to the erl_call program. This flag
makes it possible for the user to use erl_call to interact with a node
even if no EPMD instance is running on the node's host.
  • Loading branch information
kjellwinblad committed Dec 19, 2019
1 parent 14109f1 commit ce4fcf0
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 47 deletions.
25 changes: 22 additions & 3 deletions lib/erl_interface/doc/src/erl_call.xml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,22 @@
<c>Fun</c>, and <c>Args</c> in a manner
dependent on the behavior of your command shell.</p>
</item>
<tag><c>-address [Hostname:]Port</c></tag>
<item>
<p>(One of <c>-n</c>, <c>-name</c>, <c>-sname</c> or
<c>-address</c> is required.) <c>Hostname</c> is the
hostname of the machine that is running the node that
<c>erl_call</c> shall communicate with. The default
hostname is the hostname of the local machine. <c>Port</c>
is the port number of the node that <c>erl_call</c> shall
communicate with. The <c>-address</c> flag cannot be
combined with any of the flags <c>-n</c>, <c>-name</c>,
<c>-sname</c> or <c>-s</c>.</p>
<p>The <c>-address</c> flag is typically useful when one
wants to call a node that is running on machine without an
accessible <seealso marker="epmd">epmd</seealso>
instance.</p>
</item>
<tag><c>-c Cookie</c></tag>
<item>
<p>(<em>Optional.</em>) Use this option to specify a certain cookie.
Expand Down Expand Up @@ -112,13 +128,15 @@
</item>
<tag><c>-n Node</c></tag>
<item>
<p>(One of <c>-n, -name, -sname</c> is required.)
<p>(One of <c>-n</c>, <c>-name</c>, <c>-sname</c> or
<c>-address</c> is required.)
Has the same meaning as <c>-name</c> and can still be
used for backward compatibility reasons.</p>
</item>
<tag><c>-name Node</c></tag>
<item>
<p>(One of <c>-n, -name, -sname</c> is required.)
<p>(One of <c>-n</c>, <c>-name</c>, <c>-sname</c> or
<c>-address</c> is required.)
<c>Node</c> is the name of the node to be
started or communicated with. It is assumed that
<c>Node</c> is started with
Expand Down Expand Up @@ -149,7 +167,8 @@
</item>
<tag><c>-sname Node</c></tag>
<item>
<p>(One of <c>-n, -name, -sname</c> is required.)
<p>(One of <c>-n</c>, <c>-name</c>, <c>-sname</c> or
<c>-address</c> is required.)
<c>Node</c> is the name of the node to be started
or communicated with. It is assumed that <c>Node</c>
is started with <c>erl -sname</c>, which means that
Expand Down
108 changes: 85 additions & 23 deletions lib/erl_interface/src/prog/erl_call.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ struct call_flags {
int debugp;
int verbosep;
int haltp;
long port;
char *hostname;
char *cookie;
char *node;
char *hidden;
Expand Down Expand Up @@ -152,6 +154,8 @@ int erl_call(int argc, char **argv)
struct call_flags flags = {0}; /* Default 0 and NULL in all fields */
char* progname = argv[0];
ei_cnode ec;
flags.port = -1;
flags.hostname = NULL;

ei_init();

Expand All @@ -177,6 +181,29 @@ int erl_call(int argc, char **argv)
flags.node = ei_chk_strdup(argv[i+1]);
i++;
flags.use_long_name = 1;
} else if (strcmp(argv[i], "-address") == 0) { /* -address [HOST:]PORT */
if (i+1 >= argc) {
usage_arg(progname, "-address ");
}
{
char* hostname_port_arg = ei_chk_strdup(argv[i+1]);
char* address_string_end = strchr(hostname_port_arg, ':');
if (address_string_end == NULL) {
flags.port = strtol(hostname_port_arg, NULL, 10);
} else {
flags.port = strtol(address_string_end + 1, NULL, 10);
/* Remove port part from hostname_port_arg*/
*address_string_end = '\0';
if (strlen(hostname_port_arg) > 0) {
flags.hostname = hostname_port_arg;
}
}

if (flags.port < 1 || flags.port > 65535) {
usage_error(progname, "-address");
}
i++;
}
} else {
if (strlen(argv[i]) != 2) {
usage_error(progname, argv[i]);
Expand Down Expand Up @@ -251,11 +278,12 @@ int erl_call(int argc, char **argv)

} /* while */


/*
* Can't have them both !
*/
if (flags.modp && flags.evalp) {
if ((flags.modp && flags.evalp) ||
(flags.port != -1 && flags.startp) ||
(flags.port != -1 && flags.node)) {
usage(progname);
}

Expand Down Expand Up @@ -284,7 +312,7 @@ int erl_call(int argc, char **argv)
/*
* What we, at least, requires !
*/
if (flags.node == NULL) {
if (flags.node == NULL && flags.port == -1) {
usage(progname);
}

Expand Down Expand Up @@ -345,10 +373,15 @@ int erl_call(int argc, char **argv)
}

}
if ((p = strchr((const char *)flags.node, (int) '@')) == 0) {
if (flags.port != -1 && flags.hostname != NULL) {
host = flags.hostname;
strcpy(host_name, flags.hostname);
} else if ((flags.port != -1 && flags.hostname == NULL) ||
(strchr((const char *)flags.node, (int) '@') == 0)) {
strcpy(host_name, ei_thishostname(&ec));
host = host_name;
} else {
p = strchr((const char *)flags.node, (int) '@');
*p = 0;
host = p+1;
}
Expand All @@ -367,28 +400,45 @@ int erl_call(int argc, char **argv)
}
strncpy(host_name, hp->h_name, EI_MAXHOSTNAMELEN);
host_name[EI_MAXHOSTNAMELEN] = '\0';
if (strlen(flags.node) + strlen(host_name) + 2 > sizeof(nodename)) {
fprintf(stderr,"erl_call: nodename too long: %s\n", flags.node);
exit(1);
if (flags.port == -1) {
if (strlen(flags.node) + strlen(host_name) + 2 > sizeof(nodename)) {
fprintf(stderr,"erl_call: nodename too long: %s\n", flags.node);
exit(1);
}
sprintf(nodename, "%s@%s", flags.node, host_name);
}
sprintf(nodename, "%s@%s", flags.node, host_name);

/*
* Try to connect. Start an Erlang system if the
* start option is on and no system is running.
*/
if (flags.startp && !flags.haltp) {
fd = do_connect(&ec, nodename, &flags);
} else if ((fd = ei_connect(&ec, nodename)) < 0) {
/* We failed to connect ourself */
/* FIXME do we really know we failed because of node not up? */
if (flags.haltp) {
exit(0);
} else {
fprintf(stderr,"erl_call: failed to connect to node %s\n",
nodename);
exit(1);
}
} else if (flags.port == -1) {
if ((fd = ei_connect(&ec, nodename)) < 0) {
/* We failed to connect ourself */
/* FIXME do we really know we failed because of node not up? */
if (flags.haltp) {
exit(0);
} else {
fprintf(stderr,"erl_call: failed to connect to node %s\n",
nodename);
exit(1);
}
}
} else {
/* Connect using address:port */
if ((fd = ei_connect_host_port(&ec, host, (int)flags.port)) < 0) {
/* We failed to connect ourself */
/* FIXME do we really know we failed because of node not up? */
if (flags.haltp) {
exit(0);
} else {
fprintf(stderr,"erl_call: failed to connect to node with address \"%s:%ld\"\n",
flags.hostname == NULL ? "" : flags.hostname,
flags.port);
exit(1);
}
}
}

/* If we are connected and the halt switch is set */
Expand All @@ -414,8 +464,14 @@ int erl_call(int argc, char **argv)
}

if (flags.verbosep) {
fprintf(stderr,"erl_call: we are now connected to node \"%s\"\n",
nodename);
if (flags.port == -1) {
fprintf(stderr,"erl_call: we are now connected to node \"%s\"\n",
nodename);
} else {
fprintf(stderr,"erl_call: we are now connected to node with address \"%s:%ld\"\n",
flags.hostname == NULL ? "": flags.hostname,
flags.port);
}
}

/*
Expand Down Expand Up @@ -808,20 +864,26 @@ static int get_module(char **mbuf, char **mname)
static void usage_noexit(const char *progname) {
fprintf(stderr,"\nUsage: %s [-[demqrsv]] [-c Cookie] [-h HiddenName] \n", progname);
fprintf(stderr," [-x ErlScript] [-a [Mod [Fun [Args]]]]\n");
fprintf(stderr," (-n Node | -sname Node | -name Node)\n\n");
fprintf(stderr," (-n Node | -sname Node | -name Node | -address [HOSTNAME:]PORT)\n\n");
#ifdef __WIN32__
fprintf(stderr," where: -a apply(Mod,Fun,Args) (e.g -a \"erlang length [[a,b,c]]\"\n");
#else
fprintf(stderr," where: -a apply(Mod,Fun,Args) (e.g -a 'erlang length [[a,b,c]]'\n");
#endif
fprintf(stderr," -c cookie string; by default read from ~/.erlang.cookie\n");
fprintf(stderr," -d direct Erlang output to ~/.erl_call.out.<Nodename>\n");
fprintf(stderr," -e evaluate contents of standard input (e.g echo \"X=1,Y=2,{X,Y}.\"|erl_call -e ...)\n");
fprintf(stderr," -e evaluate contents of standard input (e.g., echo \"X=1,Y=2,{X,Y}.\"|%s -e ...)\n",
progname);
fprintf(stderr," -h specify a name for the erl_call client node\n");
fprintf(stderr," -m read and compile Erlang module from stdin\n");
fprintf(stderr," -n name of Erlang node, same as -name\n");
fprintf(stderr," -name name of Erlang node, expanded to a fully qualified\n");
fprintf(stderr," -sname name of Erlang node, short form will be used\n");
fprintf(stderr," -address [HOSTNAME:]PORT of Erlang node\n"
" (the default hostname is the hostname of the local manchine)\n"
" (e.g., %s -address my_host:36303 ...)\n"
" (cannot be combinated with -s, -n, -name and -sname)\n",
progname);
fprintf(stderr," -q halt the Erlang node (overrides the -s switch)\n");
fprintf(stderr," -r use a random name for the erl_call client node\n");
fprintf(stderr," -s start a new Erlang node if necessary\n");
Expand Down
97 changes: 76 additions & 21 deletions lib/erl_interface/test/erl_call_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -23,42 +23,88 @@

-include_lib("common_test/include/ct.hrl").

-export([all/0, smoke/1]).
-export([all/0, smoke/1, test_connect_to_host_port/1]).

all() ->
[smoke].
all() ->
[smoke,
test_connect_to_host_port].

smoke(Config) when is_list(Config) ->
ErlCall = find_erl_call(),
NameSwitch = case net_kernel:longnames() of
true ->
"-name";
false ->
"-sname"
end,
Name = atom_to_list(?MODULE)
++ "-"
++ integer_to_list(erlang:system_time(microsecond)),

ArgsList = ["-s", "-a", "erlang node", NameSwitch, Name],
io:format("erl_call: \"~ts\"\n~nargs list: ~p~n", [ErlCall, ArgsList]),
CmdRes = get_smoke_port_res(open_port({spawn_executable, ErlCall},
[{args, ArgsList}, eof]), []),
io:format("CmdRes: ~p~n", [CmdRes]),
RetNodeName = start_node_and_get_node_name(Name),

halt_node(Name),

[_, Hostname] = string:lexemes(atom_to_list(node()), "@"),
NodeName = list_to_atom(Name ++ "@" ++ Hostname),
io:format("NodeName: ~p~n~n", [NodeName]),
NodeName = list_to_atom(RetNodeName),
ok.

pong = net_adm:ping(NodeName),
rpc:cast(NodeName, erlang, halt, []),
NodeName = list_to_atom(string:trim(CmdRes, both, "'")),

test_connect_to_host_port(Config) when is_list(Config) ->
Name = atom_to_list(?MODULE)
++ "-"
++ integer_to_list(erlang:system_time(microsecond)),
Port = start_node_and_get_port(Name),
AddressCaller =
fun(Address) ->
get_erl_call_result(["-address",
Address,
"-a",
"erlang length [[1,2,3,4,5,6,7,8,9]]"])
end,
"9" = AddressCaller(erlang:integer_to_list(Port)),
"9" = AddressCaller(":" ++ erlang:integer_to_list(Port)),
[_, Hostname] = string:lexemes(atom_to_list(node()), "@"),
"9" = AddressCaller(Hostname ++ ":" ++ erlang:integer_to_list(Port)),
FailedRes = AddressCaller("80"),
case string:find(FailedRes, "80") of
nomatch -> ct:fail("Incorrect error message");
_ -> ok
end,
halt_node(Name),
ok.

%
% Utility functions...
%


halt_node(Name) ->
[_, Hostname] = string:lexemes(atom_to_list(node()), "@"),
NodeName = list_to_atom(Name ++ "@" ++ Hostname),
io:format("NodeName: ~p~n~n", [NodeName]),

pong = net_adm:ping(NodeName),
rpc:cast(NodeName, erlang, halt, []).

start_node_and_get_node_name(Name) ->
NameSwitch = case net_kernel:longnames() of
true ->
"-name";
false ->
"-sname"
end,
string:trim(get_erl_call_result(["-s",
NameSwitch,
Name, "-a",
"erlang node"]),
both,
"'").

start_node_and_get_port(Name) ->
start_node_and_get_node_name(Name),
{ok, NamePortList} = net_adm:names(),
{value, {_, Port}}
= lists:search(fun({N, _}) ->
string:equal(N, Name)
end,
NamePortList),
Port.

find_erl_call() ->
ErlCallName = case os:type() of
{win32, _} -> "erl_call.exe";
Expand Down Expand Up @@ -86,10 +132,19 @@ find_erl_call() ->
ErlCall
end.

get_smoke_port_res(Port, Acc) when is_port(Port) ->

get_erl_call_result(ArgsList) ->
ErlCall = find_erl_call(),
io:format("erl_call: \"~ts\"\n~nargs list: ~p~n", [ErlCall, ArgsList]),
CmdRes = get_port_res(open_port({spawn_executable, ErlCall},
[{args, ArgsList}, eof, stderr_to_stdout]), []),
io:format("CmdRes: ~p~n", [CmdRes]),
CmdRes.

get_port_res(Port, Acc) when is_port(Port) ->
receive
{Port, {data, Data}} ->
get_smoke_port_res(Port, [Acc|Data]);
get_port_res(Port, [Acc|Data]);
{Port, eof} ->
lists:flatten(Acc)
end.
Expand Down

0 comments on commit ce4fcf0

Please sign in to comment.