Erlang error in process with exit value undef - erlang

Ive been trying to write a simple concurrency exercise in Erlang that invlolves communicating across different terminals/shells.
However this error appears everytime I run init_chat() and entering my name.
=ERROR REPORT==== 15-Nov-2021::08:13:11.849169 === Error in process <0.91.0> on node kei#osboxes with exit value:
{undef,[{chat,chat_handle,"Jeano",[]}]}
I have no idea what I'm doing wrong.
The whole program:
-module(chat).
-compile(export_all).
init_chat() ->
In = io:get_line("Name please: "),
Name = string:trim(In),
register(chat_handle, spawn(chat, chat_handle, [Name])).
chat_handle(Name) ->
spawn(chat, message_handle, [Name]),
receive
{message, Sender, Message} ->
io:format("~p: ~p", [Sender, Message]),
chat_handle(Name);
{dc, Sender} ->
io:format("~p Has disconnected. ~n", [Sender]),
chat_handle(Name);
quit ->
io:format("Disconnecting... ~n"),
erlang:halt()
end.
message_handle(Name) ->
Message = io:get_line("You: "),
if
Message == "bye/n" ->
disconnect(nodes(), Name);
true ->
send_message(nodes(), Name, Message)
end.
send_message([Head | Tail], Name, Message) ->
{chat_handle, Head} ! {message, Name, Message},
send_message(Tail, Name, Message);
send_message([], Name, Message) ->
message_handle(Name).
disconnect([Head | Tail], Name) ->
{chat_handle, Head} ! {dc, Name},
disconnect(Tail, Name);
disconnect([], Name) ->
{chat_handle, node()} ! quit.

The error message says {chat,chat_handle,"Jeano", ... (which is missing list brackets around the string) but your code actually says spawn(chat, chat_handle, [Name]), which looks correct, so I think you just haven't reloaded the recompiled version of the module.

Related

Erlang error: no function clause matching io:request

I'm an experienced programmer new to Erlang and I'm stuck on the following:
myread() ->
{_, MyData } = file:read_file( "hands.txt" ),
io:format( "hands-out.txt", "~w", MyData ).
yields, when myread() is invoked from the shell:
** exception error: no function clause matching io:request("hands-out.txt",
{format,"~w", <<"3h 5h 7h 8h 3h 5h 7h 8h q"...>>})
(io.erl, line 556) in function io:o_request/3 (io.erl, line 63)
Any help would be appreciated.
Two things:
"hands-out.txt", "~w" needs to be one string: "hands-out.txt: ~w"
and the data that's replacing the ~w needs to be a list. So:
io:format( "hands-out.txt: ~w", [MyData] ).
See http://erlang.org/doc/man/io.html#format-2
Also, you should pattern match on the status value in the return from file:read_file/1. In your version, an error, which would be returned as {error, Reason} would match here, since you're using _, and you'd print the error reason rather than the file, which might be confusing.
So either make it {ok, MyData } = file:read_file( "hands.txt" ) if you want to crash on read error, or something like the following if you want to handle that case:
myread() ->
case file:read_file( "hands.txt" ) of
{ok, MyData } ->
io:format( "hands-out.txt: ~w", [MyData] );
{error, Error} ->
io:format("Error: ~w~n", [Error])
end.

Ejabberd get room online occupants within module

I am currently trying to get the occupants of a room in a custom Ejabberd module within the muc_filter_message hook, but all attempts to get the room state are timing out.
error:
2016-07-25 10:43:04.802 [error] <0.13909.0>#ejabberd_hooks:run_fold1:368 {timeout,{gen_fsm,sync_send_all_state_event,[<0.13909.0>,get_state]}}
imports + hook function (cut down)
-behaviour(gen_mod).
-include("ejabberd.hrl").
-include("logger.hrl").
-include("jlib.hrl").
-include("mod_muc_room.hrl").
-include("mod_muc.hrl").
-include("ejabberd_http.hrl").
-include("ejabberd_web_admin.hrl").
-include("ejabberd_commands.hrl").
muc_message_sent(Stanza, MUCState, RoomJID, FromJID, FromNick) ->
{_, RoomName, Host, _, _, _, _} = RoomJID,
OccuList = get_room_occupants(RoomName, Host),
?INFO_MSG("muc_message_sent OccuList ~p~n", [OccuList]),
Stanza.
Code for room occupants retrieval:
get_room_occupants(Room, Host) ->
case get_room_pid(Room, Host) of
room_not_found ->
?INFO_MSG("muc_message_sent get_room_occ ~p~n", [room]);
Pid -> get_room_occupants(Pid)
end.
get_room_occupants(Pid) ->
?INFO_MSG("muc_message_sent get_room_pstate ~p~n", [Pid]),
S = get_room_state(Pid),
?INFO_MSG("muc_message_sent get_room_state S ~p~n", [S]),
lists:map(
fun({_LJID, Info}) ->
{jid:to_string(Info#user.jid),
Info#user.nick,
atom_to_list(Info#user.role)}
end,
dict:to_list(S#state.users)).
%% #doc Get the Pid of an existing MUC room, or 'room_not_found'.
get_room_pid(Name, Service) ->
case mnesia:dirty_read(muc_online_room, {Name, Service}) of
[] ->
?INFO_MSG("muc_message_sent get_room_pid ~p~n", [failed]),
room_not_found;
[Room] ->
?INFO_MSG("muc_message_sent get_room_pid ~p~n", [pid]),
Room#muc_online_room.pid
end.
get_room_state(Room_pid) ->
{ok, R} = gen_fsm:sync_send_all_state_event(Room_pid, get_state),
R.
The get_room_occupants code is pulled directly from mod_muc_admin where it works fine (but I couldn't find a way to directly use the functions within that module). Been stuck on this for a while now so any ideas appreciated.

How to implement in erlang

I was thinking of using a list to store elements(files)
How do i change the code below so that it would lock one process(user) from accessing one file(element in list), while allowing another process(user) to access the file.
-module(bank).
-export([account/1, start/0, stop/0, deposit/1, get_bal/0, set_bal/1]).
account(Balance) ->
receive
{set, NewBalance} ->
account(NewBalance);
{get, From} ->
From ! {balance, Balance},
account(Balance);
stop -> ok
end.
start() ->
Account_PID = spawn(bank, account, [0]),
register(account_process, Account_PID).
stop() ->
account_process ! stop,
unregister(account_process).
set_bal(B) ->
account_process ! {set, B}.
get_bal() ->
account_process ! {get, self()},
receive
{balance, B} -> B
end.
deposit(Amount) ->
OldBalance = get_bal(),
NewBalance = OldBalance + Amount,
set_bal(NewBalance).
and
-module(banktest).
-export([start/0, client/1]).
start() ->
bank:start(),
mutex:start(),
register(tester_process, self()),
loop(10, 20, 100),
unregister(tester_process),
mutex:stop(),
bank:stop().
loop(_, _, 0) ->
true;
loop(Amount1, Amount2, N) ->
bank:set_bal(0),
spawn(banktest, client, [Amount1]),
spawn(banktest, client, [Amount2]),
receive
done -> true
end,
receive
done -> true
end,
io:format("Expected balance = ~B, actual balance = ~B~n~n",
[Amount1 + Amount2, bank:get_bal()]),
loop(Amount1, Amount2, N-1).
client(Amount) ->
bank:deposit(Amount),
tester_process ! done.
If you want to serialize write calls but allow read ones, you can change the code as follow:
account(Balance, WriteLocker) ->
receive
free_lock ->
account(Balance, undefined),
{set, NewBalance, Caller} when WriteLocker =:= undefined ->
Caller ! done,
account(NewBalance, Caller),
{get, From} ->
From ! {balance, Balance},
account(Balance, WriteLocker);
stop -> ok
end.
wait() ->
receive
{done, WriteLock} -> WriteLock ! free_lock.
end.
set_bal(B) ->
account_process ! {set, B, self()},
wait().
If you want to lock reader too, add the guard to {get, From} message too.
OK, still not quite understand the question but I will try to clarify and then propose a solution based on that:
You want to manage access to a set of files rather than a bank account
You want one user to have a write access to a file while other uses still have a read access to that file
If that's what you want then you can simply treat each file similarly to a bank account. It means that you should start one process (analogous to the code in the bank module) per file. In other words:
for 1 bank account you need 1 process
for 3 bank accounts you need 3 processes
for 10 files you need 10 processes
Implementation details would depend on how you plan to manage the files on the server, but in the simplistic scenario the process could work analogously to the existing code:
file(FileName) ->
receive
{write, From, Data} ->
ok = file:write_file(FileName, Data),
From ! ok,
file(FileName);
{read, From} ->
{ok, Data} = file:read_file(FileName),
From ! Data,
file(FileName);
stop -> ok
end.
start() ->
File_PID = spawn(file, file, ["somefile.txt"]),
register(file_process, File_PID).

Suppressing Erlang “unused function” warnings in header files

I have a header file with some functionality in it.
It's meant to be for expandibility ;)
In this header file I use -spec for my functions.
Because my header file is included in more than one erl file and has some functionality which are not used by all of the erl files, I get warnings of functions not being used (which are being used but in another erl file).
For example:
I get for this example a warning for module a that function headerExecute is not being used and for module c that function headerCount is not being used.
%% Shell %%
BPid = b:create(),
APid = a:create(BPid),
CPid = c:create(BPid).
-module(a).
-export([create/1, count/2, stop/1]).
-export([loop/1]).
-include("header.hrl").
create(Id) ->
spawn(?MODULE, loop, [Id]).
count(Pid, Counter) ->
Pid!{count, Ref = make_ref(), self(), Counter},
receive
{Ref, Result} -> Result
end.
stop(Pid) ->
Pid!{stop}.
loop(Id) ->
receive
{count, Ref, From, Counter}
Result = headerCount(Id, Counter),
From!{Ref, Result},
?MODULE:loop(Id);
{stop} ->
ok;
_ ->
?MODULE:loop(Id)
end.
-define(header, header).
-spec headerCount(pid(), integer()) -> integer().
-spec headerExecute(pid(), atom()) -> no_return().
headerCount(Pid, Counter) ->
Pid!{count, Ref = make_ref(), self(), Counter},
receive
{Ref, Result} -> Result
end.
headerExecute(Pid, Cmd) ->
Pid!{execute, Cmd}.
-module(b).
-export([create/0, stop/1]).
-export([loop/0]).
create() ->
spawn(?MODULE, loop, []).
stop(Pid) ->
Pid!{stop}.
loop() ->
receive
{count, Ref, From, Counter} ->
From!{Ref, Counter + 1},
?MODULE:loop();
{execute, Cmd} ->
%% Execute something with the command Cmd %%
?MODULE:loop();
{stop} ->
ok;
_ ->
?MODULE:loop()
end.
-module(c).
-export([create/1, execution/2, stop/1]).
-export([loop/1]).
-include(header).
create(Id) ->
spawn(?MODULE, loop, [Id]).
execution(Pid, Cmd) ->
Pid!{execution, Cmd}.
stop(Pid) ->
Pid!{stop}.
loop(Id) ->
receive
{execution, Cmd} ->
headerExecute(Id, Cmd),
?MODULE:loop(Id);
{stop} ->
ok;
_ ->
?MODULE:loop(Id)
end.
For scalability it's not an option to export these in the erl files and exporting these functions in a header file result in errors.
How can I discard or get rid off these warnings?
EDIT: I put this functionality in the headers so that new erl modules can easily be added to the code and adjust the existing erl modules without touching the other erl modules.
Thanks in advance.
Use nowarn_unused_function compiler parameter in your header file.
-compile({nowarn_unused_function, [headerCount/2, headerExecute/2]})
But it doesn't have anything with scalability. You are using this word wrong.

Erl file not compiling due to syntax errors

I am trying to figure out what is wrong with this code, cause it is giving me errors preventing it from compiling properly into a beam file. I do not see what is wrong with the syntax. Is there an IDE which could help me out?
These are the errors:
parallels#parallels-Parallels-Virtual-Platform:/var/backend/ejabberd_modules# erlc -I /var/tmp/ejabberd/src/ mod_stanza_ack.erl
./mod_stanza_ack.erl:97: syntax error before: '.'
./mod_stanza_ack.erl:98: syntax error before: Body
./mod_stanza_ack.erl:16: function route/3 undefined
./mod_stanza_ack.erl:3: Warning: behaviour gen_mod undefined
./mod_stanza_ack.erl:111: Warning: function strip_bom/1 is unused
./mod_stanza_ack.erl:114: Warning: function send_presence/3 is unused
./mod_stanza_ack.erl:120: Warning: function echo/3 is unused
./mod_stanza_ack.erl:123: Warning: function send_message/4 is unused
This is the code:
-module(mod_stanza_ack).
-behavior(gen_server).
-behavior(gen_mod).
-export([start_link/2]).
-export([start/2,
stop/1,
init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3]).
-export([route/3]).
-include("ejabberd.hrl").
-define(PROCNAME, ejabberd_mod_stanza_ack).
-define(BOTNAME, stanza_ack).
start_link(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
start(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
ChildSpec = {Proc,
{?MODULE, start_link, [Host, Opts]},
temporary,
1000,
worker,
[?MODULE]},
supervisor:start_child(ejabberd_sup, ChildSpec).
stop(Host) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:call(Proc, stop),
supervisor:terminate_child(ejabberd_sup, Proc),
supervisor:delete_child(ejabberd_sup, Proc).
init([Host, Opts]) ->
?DEBUG("ECHO_BOT: Starting echo_bot", []),
% add a new virtual host / subdomain "echo".example.com
MyHost = gen_mod:get_opt_host(Host, Opts, "echo.#HOST#"),
ejabberd_router:register_route(MyHost, {apply, ?MODULE, route}),
{ok, Host}.
handle_call(stop, _From, Host) ->
{stop, normal, ok, Host}.
handle_cast(_Msg, Host) ->
{noreply, Host}.
handle_info(_Msg, Host) ->
{noreply, Host}.
terminate(_Reason, Host) ->
ejabberd_router:unregister_route(Host),
ok.
code_change(_OldVsn, Host, _Extra) ->
{ok, Host}.
% Checks a presence /subscription/ is a part of this.
% we may want to impliment blacklisting / some kind of
% protection here to prevent malicious users
%route(From, #jid{luser = ?BOTNAME} = To, {xmlelement, "presence", _, _} = Packet) ->
route(From, To, {xmlelement, "presence", _, _} = Packet) ->
case xml:get_tag_attr_s("type", Packet) of
"subscribe" ->
send_presence(To, From, "subscribe");
"subscribed" ->
send_presence(To, From, "subscribed"),
send_presence(To, From, "");
"unsubscribe" ->
send_presence(To, From, "unsubscribed"),
send_presence(To, From, "unsubscribe");
"unsubscribed" ->
send_presence(To, From, "unsubscribed");
"" ->
send_presence(To, From, "");
"unavailable" ->
ok;
"probe" ->
send_presence(To, From, "");
_Other ->
?INFO_MSG("Other kind of presence~n~p", [Packet])
end,
ok;
%route(From, #jid{luser = ?BOTNAME} = To, {xmlelement, "message", _, _} = Packet) ->
route(From, To, {xmlelement, "message", _, _} = Packet) ->
case xml:get_subtag_cdata(Packet, "body") of
"" ->
ok.
Body ->
case xml:get_tag_attr_s("type", Packet) of
"error" ->
?ERROR_MSG("Received error message~n~p -> ~p~n~p", [From, To, Packet]);
_ ->
echo(To, From, strip_bom(Body))
end
end,
ok.
%% HELPER FUNCTIONS
strip_bom([239,187,191|C]) -> C;
strip_bom(C) -> C.
send_presence(From, To, "") ->
ejabberd_router:route(From, To, {xmlelement, "presence", [], []});
send_presence(From, To, TypeStr) ->
ejabberd_router:route(From, To, {xmlelement, "presence", [{"type", TypeStr}], []}).
echo(From, To, Body) ->
send_message(From, To, "chat", Body).
send_message(From, To, TypeStr, BodyStr) ->
XmlBody = {xmlelement, "message",
[{"type", TypeStr},
{"from", jlib:jid_to_string(From)},
{"to", jlib:jid_to_string(To)}],
[{xmlelement, "body", [],
[{xmlcdata, BodyStr}]}]},
ejabberd_router:route(From, To, XmlBody).
./mod_stanza_ack.erl:97: syntax error before: '.' is saying the error is in line 97.
At line 97 change ok. to ok;. This will fix the issue.
Erlide plugin for eclipse is a good IDE to try out.
On line 97 in the function route/3 the first clause of the case is
"" ->
ok.
The . here ends the function which is illegal in the middle of a case, it should be a ;. This also means that the parser assumes that the variable Body on the next line starts a new function which is also illegal as a function name cannot be a variable.
Seeing the function route/3 has not been defined it cannot be exported which is the cause of the undefined function error on line 16.
Sometimes the compiler error messages are a bit cryptic but the line numbers usually help.

Resources