[rabbitmq-discuss] Creating an auth plugin (Kerberos)

Tim Watson tim at rabbitmq.com
Wed Dec 5 14:48:07 GMT 2012


Simon,

This is why I suggested using an insulator process. The process that calls open_port is the controlling process and when the port is closed, it will send the controlling process and exit signal. You override that by passing exit_status, but you also supply 'eof' and the ordering between the eof and exit_status messages is not guaranteed. When you time out in the after clause, you have *no idea* whether or not the port program is still running, which could lead to any of

- leaking resources such as file descriptors used for the port
- erroneous messages sent to the reader process (such as the 'EXIT' that is arriving when you call port_close)

I would strongly suggest considering an insulator process for this. Do you need some pointers to doing that? Let me cook something up for you.....

In this code, notice that the command I'm running is guaranteed to finish within the 2000 ms timeout. I don't care about that, because it is only the exit status that matters to me. If you need to examine the stdio, then that's another matter but you aren't currently doing that. 

Now if you change the command to something that will run for a *long* time such as `ls -laR /` it will *not* finish quickly enough and yes all is still well and there are no stray messages. BUT ..... that command `ls -laR /` will not respond to stdin and will therefore *continue running even AFTER the port is closed* - so you need to be very careful to ensure that your program will actually terminate. I use hack to enforce this sometimes:

Cmd = "/usr/bin/env sh -c \"(cat; kill 0) | " ++ Exec ++ " \""

Anyway, here is the module. Note that lots of the wait_for_this and that is just to demonstrate what's going on. Hope this helps.

<code>

-module(foo).

-export([main/1]).

main(_) ->
    Result = run_insulator(),
    io:format("Result = ~p~n", [Result]),
    wait_for_stray_messages().

wait_for_stray_messages() ->
    receive
        Any -> io:format("got stray message ~p~n", [Any]),
               wait_for_stray_messages()
    after 5000 ->
        io:format("we're done - no stray messages/EXIT's and we're good to go...~n")
    end.

run_insulator() ->
    {Pid, MRef} = spawn_monitor(fun insulated/0),
    receive
        {'DOWN', MRef, process, Pid, Reason} ->
            case Reason of
                {done, Result} -> Result;
                Other          -> {error, Other}
            end
    after 2000 ->
        %% get rid of any pending monitor notifications
        erlang:demonitor(MRef, [flush]),
        exit(Pid, shutdown),
        false
    end.

insulated() ->
    process_flag(trap_exit, true),
    Port = open_port({spawn, "ls -la"}, [
                     exit_status, use_stdio, eof,
                     {line, 1024}]),
    Result = loop(Port),
    erlang:port_close(Port),
    exit({done, Result}).

loop(Port) ->
    Self = self(),
    receive
        %% we do not appear to care about the stdio
        {Port, {data, {_, _}}}    -> loop(Port);
        {Port, eof}               -> loop(Port);
        {Port, {exit_status, 0}}  -> true;
        {Port, {exit_status, _N}} -> false;
        {'EXIT', Self, shutdown}  -> false
    end.

</code>

Cheers,

Tim



More information about the rabbitmq-discuss mailing list