[erlang-questions] What's a solid way to have erlang automatically send email.

Raimo Niskanen <>
Wed Nov 17 10:47:19 CET 2010


On Tue, Nov 16, 2010 at 09:37:39PM +1100, Anthony Shipman wrote:
> On Mon, 15 Nov 2010 10:31:09 am G.S. wrote:
> > Hello,
> >
> > I have a website where the administrator needs to receive email alert (to
> > login and fix a problem) when certain events occur. Is there an Erlang
> > module that allows one to easily send out an email? Or something that can
> > be used in the same manner?
> >
> > Thanks,
> > -Gene
> 
> On UNIX, you could feed the text into stdin of /usr/sbin/sendmail 
> or /usr/bin/mutt. erlang:open_port() will let you do this.

Here is a part of a larger (escript) program that does that.
I have made a perhaps overflexible cmd/2,3 that returns
a lazy list which may look like overengineering in this
case, but makes sense in the larger program.

%% Send a completely formatted mail with all
%% headers through the sendmail command.
sendmail(Text) ->
    {_,Port,Cont} =
        cmd(
          ["sendmail","/usr/sbin/sendmail",
           "/usr/lib/sendmail","/usr/libexec/sendmail"],
          ["-t","-i"]),
    erlang:port_command(Port, Text),
    result(Cont).

result([_|Cont]) ->
    result(Cont);
result(Fun) when is_function(Fun) ->
    result(Fun());
result(Result) ->
    Result.

%% Generic execute command returning a lazy list
%%   -> {[Command|Args],Port,Cont}
%%
%%   Cont() -> [Line, ... | Cont] | []
%%   Cont() -> ExitStatus
cmd(Cmds, Args) ->
    cmd(Cmds, Args, []).
%%
cmd([Cmd|Cmds], Args, Opts) ->
    case os:find_executable(Cmd) of
        false ->
            case Cmds of
                [] ->
                    erlang:error({not_found_executable,Cmd});
                _ ->
                    cmd(Cmds, Args, Opts)
            end;
        Command ->
            cmd_open(Command, Args, Opts)
    end.

cmd_open(Command, Args, Opts) ->
    cmd_open_port(Command, {spawn_executable,Command}, [{args,Args}|Opts]).

cmd_open_port(Command, PortName, Opts) ->
    Port = erlang:open_port(PortName, [{line,80},exit_status,eof,hide|Opts]),
    {Command,Port,cmd_cont(Port, [], undefined, false)}.

cmd_cont(Port, Buf, Exit, Eof) ->
    fun () ->
            cmd_loop(Port, Buf, Exit, Eof)
    end.

cmd_loop(Port, Buf, Exit, Eof) ->
    receive
        {Port,{data,{Flag,Line}}} ->
            case Flag of
                noeol ->
                    cmd_loop(Port, lists:reverse(Line, Buf), Exit, Eof);
                eol ->
                    Data = lists:reverse(Buf, Line),
                    [Data|cmd_cont(Port, [], Exit, Eof)]
            end;
        {Port,{exit_status,Status}} when Exit =:= undefined ->
            case Eof of
                true ->
                    cmd_close(Port, Buf, Status);
                false ->
                    cmd_loop(Port, Buf, Status, Eof)
            end;
        {Port,eof} when Eof =:= false ->
            case Exit of
                undefined ->
                    cmd_loop(Port, Buf, Exit, true);
                Status ->
                    cmd_close(Port, Buf, Status)
            end;
        {Port,_} = Unexpected ->
            io:format("Unexpected from port: ~p~n~n", [Unexpected]),
            cmd_loop(Port, Buf, Exit, Eof)
    end.

cmd_close(Port, Buf, Status) ->
    catch port_close(Port),
    case Buf of
        [] ->
            Status;
        Buf ->
            [lists:reverse(Buf)|Status]
    end.


> 
> -- 
> Anthony Shipman                    Mamas don't let your babies 
>                    grow up to be outsourced.
> 
> ________________________________________________________________
> erlang-questions (at) erlang.org mailing list.
> See http://www.erlang.org/faq.html
> To unsubscribe; mailto:

-- 

/ Raimo Niskanen, Erlang/OTP, Ericsson AB


More information about the erlang-questions mailing list