Merge bug24924

This commit is contained in:
Simon MacMullen 2012-06-01 11:00:49 +01:00
commit e07f7d6a50
32 changed files with 1144 additions and 981 deletions

View File

@ -720,7 +720,7 @@
</varlistentry>
<varlistentry>
<term><cmdsynopsis><command>list_user_permissions</command> <arg choice="opt">-p <replaceable>vhostpath</replaceable></arg> <arg choice="req"><replaceable>username</replaceable></arg></cmdsynopsis></term>
<term><cmdsynopsis><command>list_user_permissions</command> <arg choice="req"><replaceable>username</replaceable></arg></cmdsynopsis></term>
<listitem>
<variablelist>
<varlistentry>
@ -978,7 +978,8 @@
</varlistentry>
<varlistentry>
<term>type</term>
<listitem><para>The exchange type (one of [<command>direct</command>,
<listitem><para>The exchange type (such as
[<command>direct</command>,
<command>topic</command>, <command>headers</command>,
<command>fanout</command>]).</para></listitem>
</varlistentry>

View File

@ -2,7 +2,7 @@ Source: rabbitmq-server
Section: net
Priority: extra
Maintainer: RabbitMQ Team <packaging@rabbitmq.com>
Uploader: Emile Joubert <emile@rabbitmq.com>
Uploaders: Emile Joubert <emile@rabbitmq.com>
DM-Upload-Allowed: yes
Build-Depends: cdbs, debhelper (>= 5), erlang-dev, python-simplejson, xmlto, xsltproc, erlang-nox (>= 1:12.b.3), erlang-src (>= 1:12.b.3), unzip, zip
Standards-Version: 3.8.0

View File

@ -31,7 +31,7 @@ exec erl \
-noinput \
-hidden \
-sname rabbitmq-plugins$$ \
-s rabbit_plugins \
-s rabbit_plugins_main \
-enabled_plugins_file "$RABBITMQ_ENABLED_PLUGINS_FILE" \
-plugins_dist_dir "$RABBITMQ_PLUGINS_DIR" \
-extra "$@"

View File

@ -47,7 +47,7 @@ if "!RABBITMQ_PLUGINS_DIR!"=="" (
set RABBITMQ_PLUGINS_DIR=!TDP0!..\plugins
)
"!ERLANG_HOME!\bin\erl.exe" -pa "!TDP0!..\ebin" -noinput -hidden -sname rabbitmq-plugins!RANDOM! -s rabbit_plugins -enabled_plugins_file "!RABBITMQ_ENABLED_PLUGINS_FILE!" -plugins_dist_dir "!RABBITMQ_PLUGINS_DIR:\=/!" -extra !STAR!
"!ERLANG_HOME!\bin\erl.exe" -pa "!TDP0!..\ebin" -noinput -hidden -sname rabbitmq-plugins!RANDOM! -s rabbit_plugins_main -enabled_plugins_file "!RABBITMQ_ENABLED_PLUGINS_FILE!" -plugins_dist_dir "!RABBITMQ_PLUGINS_DIR:\=/!" -extra !STAR!
endlocal
endlocal

View File

@ -58,7 +58,8 @@ DEFAULT_NODE_PORT=5672
##--- End of overridden <var_name> variables
RABBITMQ_START_RABBIT=
[ "x" = "x$RABBITMQ_ALLOW_INPUT" ] && RABBITMQ_START_RABBIT='-noinput'
[ "x" = "x$RABBITMQ_ALLOW_INPUT" ] && RABBITMQ_START_RABBIT=" -noinput"
[ "x" = "x$RABBITMQ_NODE_ONLY" ] && RABBITMQ_START_RABBIT="$RABBITMQ_START_RABBIT -s rabbit boot "
case "$(uname -s)" in
CYGWIN*) # we make no attempt to record the cygwin pid; rabbitmqctl wait
@ -70,24 +71,16 @@ case "$(uname -s)" in
esac
RABBITMQ_EBIN_ROOT="${RABBITMQ_HOME}/ebin"
if [ "x" = "x$RABBITMQ_NODE_ONLY" ]; then
if erl \
-pa "$RABBITMQ_EBIN_ROOT" \
-noinput \
-hidden \
-s rabbit_prelaunch \
-sname rabbitmqprelaunch$$ \
-extra "$RABBITMQ_ENABLED_PLUGINS_FILE" "$RABBITMQ_PLUGINS_DIR" "${RABBITMQ_PLUGINS_EXPAND_DIR}" "${RABBITMQ_NODENAME}"
if ! erl -pa "$RABBITMQ_EBIN_ROOT" \
-noinput \
-hidden \
-s rabbit_prelaunch \
-sname rabbitmqprelaunch$$ \
-extra "${RABBITMQ_NODENAME}";
then
RABBITMQ_BOOT_FILE="${RABBITMQ_PLUGINS_EXPAND_DIR}/rabbit"
RABBITMQ_EBIN_PATH=""
else
exit 1
fi
else
RABBITMQ_BOOT_FILE=start_sasl
RABBITMQ_EBIN_PATH="-pa ${RABBITMQ_EBIN_ROOT}"
exit 1;
fi
RABBITMQ_CONFIG_ARG=
[ -f "${RABBITMQ_CONFIG_FILE}.config" ] && RABBITMQ_CONFIG_ARG="-config ${RABBITMQ_CONFIG_FILE}"
@ -100,10 +93,10 @@ RABBITMQ_LISTEN_ARG=
set -f
exec erl \
${RABBITMQ_EBIN_PATH} \
-pa ${RABBITMQ_EBIN_ROOT} \
${RABBITMQ_START_RABBIT} \
-sname ${RABBITMQ_NODENAME} \
-boot ${RABBITMQ_BOOT_FILE} \
-boot start_sasl \
${RABBITMQ_CONFIG_ARG} \
+W w \
${RABBITMQ_SERVER_ERL_ARGS} \
@ -112,6 +105,9 @@ exec erl \
-sasl sasl_error_logger false \
-rabbit error_logger '{file,"'${RABBITMQ_LOGS}'"}' \
-rabbit sasl_error_logger '{file,"'${RABBITMQ_SASL_LOGS}'"}' \
-rabbit enabled_plugins_file "\"$RABBITMQ_ENABLED_PLUGINS_FILE\"" \
-rabbit plugins_dir "\"$RABBITMQ_PLUGINS_DIR\"" \
-rabbit plugins_expand_dir "\"$RABBITMQ_PLUGINS_EXPAND_DIR\"" \
-os_mon start_cpu_sup false \
-os_mon start_disksup false \
-os_mon start_memsup false \

View File

@ -93,21 +93,17 @@ if "!RABBITMQ_PLUGINS_DIR!"=="" (
set RABBITMQ_EBIN_ROOT=!TDP0!..\ebin
"!ERLANG_HOME!\bin\erl.exe" ^
-pa "!RABBITMQ_EBIN_ROOT!" ^
-noinput -hidden ^
-s rabbit_prelaunch ^
-sname rabbitmqprelaunch!RANDOM! ^
-extra "!RABBITMQ_ENABLED_PLUGINS_FILE:\=/!" ^
"!RABBITMQ_PLUGINS_DIR:\=/!" ^
"!RABBITMQ_PLUGINS_EXPAND_DIR:\=/!" ^
"!RABBITMQ_NODENAME!"
-pa "!RABBITMQ_EBIN_ROOT!" ^
-noinput -hidden ^
-s rabbit_prelaunch ^
-sname rabbitmqprelaunch!RANDOM! ^
-extra "!RABBITMQ_NODENAME!"
set RABBITMQ_BOOT_FILE=!RABBITMQ_PLUGINS_EXPAND_DIR!\rabbit
if ERRORLEVEL 1 (
exit /B 1
)
set RABBITMQ_EBIN_PATH=
set RABBITMQ_EBIN_PATH="-pa !RABBITMQ_EBIN_ROOT!"
if "!RABBITMQ_CONFIG_FILE!"=="" (
set RABBITMQ_CONFIG_FILE=!RABBITMQ_BASE!\rabbitmq
@ -127,9 +123,10 @@ if not "!RABBITMQ_NODE_IP_ADDRESS!"=="" (
)
"!ERLANG_HOME!\bin\erl.exe" ^
!RABBITMQ_EBIN_PATH! ^
-pa "!RABBITMQ_EBIN_ROOT!" ^
-noinput ^
-boot "!RABBITMQ_BOOT_FILE!" ^
-boot start_sasl ^
-s rabbit boot ^
!RABBITMQ_CONFIG_ARG! ^
-sname !RABBITMQ_NODENAME! ^
+W w ^
@ -142,6 +139,9 @@ if not "!RABBITMQ_NODE_IP_ADDRESS!"=="" (
-sasl sasl_error_logger false ^
-rabbit error_logger {file,\""!LOGS:\=/!"\"} ^
-rabbit sasl_error_logger {file,\""!SASL_LOGS:\=/!"\"} ^
-rabbit enabled_plugins_file \""!RABBITMQ_ENABLED_PLUGINS_FILE:\=/!"\" ^
-rabbit plugins_dir \""!RABBITMQ_PLUGINS_DIR:\=/!"\" ^
-rabbit plugins_expand_dir \""!RABBITMQ_PLUGINS_EXPAND_DIR:\=/!"\" ^
-os_mon start_cpu_sup false ^
-os_mon start_disksup false ^
-os_mon start_memsup false ^

View File

@ -157,22 +157,6 @@ if "!RABBITMQ_ENABLED_PLUGINS_FILE!"=="" (
set RABBITMQ_PLUGINS_DIR=!TDP0!..\plugins
set RABBITMQ_EBIN_ROOT=!TDP0!..\ebin
"!ERLANG_HOME!\bin\erl.exe" ^
-pa "!RABBITMQ_EBIN_ROOT!" ^
-noinput -hidden ^
-s rabbit_prelaunch ^
-extra "!RABBITMQ_ENABLED_PLUGINS_FILE:\=/!" ^
"!RABBITMQ_PLUGINS_DIR:\=/!" ^
"!RABBITMQ_PLUGINS_EXPAND_DIR:\=/!" ^
""
set RABBITMQ_BOOT_FILE=!RABBITMQ_PLUGINS_EXPAND_DIR!\rabbit
if ERRORLEVEL 1 (
exit /B 1
)
set RABBITMQ_EBIN_PATH=
if "!RABBITMQ_CONFIG_FILE!"=="" (
set RABBITMQ_CONFIG_FILE=!RABBITMQ_BASE!\rabbitmq
)
@ -191,8 +175,9 @@ if not "!RABBITMQ_NODE_IP_ADDRESS!"=="" (
)
set ERLANG_SERVICE_ARGUMENTS= ^
!RABBITMQ_EBIN_PATH! ^
-boot "!RABBITMQ_BOOT_FILE!" ^
-pa "!RABBITMQ_EBIN_ROOT!" ^
-boot start_sasl ^
-s rabbit boot ^
!RABBITMQ_CONFIG_ARG! ^
+W w ^
+A30 ^
@ -204,6 +189,9 @@ set ERLANG_SERVICE_ARGUMENTS= ^
-sasl sasl_error_logger false ^
-rabbit error_logger {file,\""!LOGS:\=/!"\"} ^
-rabbit sasl_error_logger {file,\""!SASL_LOGS:\=/!"\"} ^
-rabbit enabled_plugins_file \""!RABBITMQ_ENABLED_PLUGINS_FILE:\=/!"\" ^
-rabbit plugins_dir \""!RABBITMQ_PLUGINS_DIR:\=/!"\" ^
-rabbit plugins_expand_dir \""!RABBITMQ_PLUGINS_EXPAND_DIR:\=/!"\" ^
-os_mon start_cpu_sup false ^
-os_mon start_disksup false ^
-os_mon start_memsup false ^

View File

@ -32,6 +32,6 @@ exec erl \
-hidden \
${RABBITMQ_CTL_ERL_ARGS} \
-sname rabbitmqctl$$ \
-s rabbit_control \
-s rabbit_control_main \
-nodename $RABBITMQ_NODENAME \
-extra "$@"

View File

@ -34,7 +34,7 @@ if "!RABBITMQ_NODENAME!"=="" (
if not exist "!ERLANG_HOME!\bin\erl.exe" (
echo.
echo ******************************
echo ERLANG_HOME not set correctly.
echo ERLANG_HOME not set correctly.
echo ******************************
echo.
echo Please either set ERLANG_HOME to point to your Erlang installation or place the
@ -43,7 +43,7 @@ if not exist "!ERLANG_HOME!\bin\erl.exe" (
exit /B
)
"!ERLANG_HOME!\bin\erl.exe" -pa "!TDP0!..\ebin" -noinput -hidden !RABBITMQ_CTL_ERL_ARGS! -sname rabbitmqctl!RANDOM! -s rabbit_control -nodename !RABBITMQ_NODENAME! -extra !STAR!
"!ERLANG_HOME!\bin\erl.exe" -pa "!TDP0!..\ebin" -noinput -hidden !RABBITMQ_CTL_ERL_ARGS! -sname rabbitmqctl!RANDOM! -s rabbit_control_main -nodename !RABBITMQ_NODENAME! -extra !STAR!
endlocal
endlocal

121
src/app_utils.erl Normal file
View File

@ -0,0 +1,121 @@
%% The contents of this file are subject to the Mozilla Public License
%% Version 1.1 (the "License"); you may not use this file except in
%% compliance with the License. You may obtain a copy of the License
%% at http://www.mozilla.org/MPL/
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and
%% limitations under the License.
%%
%% The Original Code is RabbitMQ.
%%
%% The Initial Developer of the Original Code is VMware, Inc.
%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved.
%%
-module(app_utils).
-export([load_applications/1, start_applications/1,
stop_applications/1, app_dependency_order/2,
wait_for_applications/1]).
-ifdef(use_specs).
-spec load_applications([atom()]) -> 'ok'.
-spec start_applications([atom()]) -> 'ok'.
-spec stop_applications([atom()]) -> 'ok'.
-spec wait_for_applications([atom()]) -> 'ok'.
-spec app_dependency_order([atom()], boolean()) -> [digraph:vertex()].
-endif.
%%---------------------------------------------------------------------------
%% Public API
load_applications(Apps) ->
load_applications(queue:from_list(Apps), sets:new()),
ok.
start_applications(Apps) ->
manage_applications(fun lists:foldl/3,
fun application:start/1,
fun application:stop/1,
already_started,
cannot_start_application,
Apps).
stop_applications(Apps) ->
manage_applications(fun lists:foldr/3,
fun application:stop/1,
fun application:start/1,
not_started,
cannot_stop_application,
Apps).
wait_for_applications(Apps) ->
[wait_for_application(App) || App <- Apps], ok.
app_dependency_order(RootApps, StripUnreachable) ->
{ok, G} = rabbit_misc:build_acyclic_graph(
fun (App, _Deps) -> [{App, App}] end,
fun (App, Deps) -> [{Dep, App} || Dep <- Deps] end,
[{App, app_dependencies(App)} ||
{App, _Desc, _Vsn} <- application:loaded_applications()]),
try
case StripUnreachable of
true -> digraph:del_vertices(G, digraph:vertices(G) --
digraph_utils:reachable(RootApps, G));
false -> ok
end,
digraph_utils:topsort(G)
after
true = digraph:delete(G)
end.
%%---------------------------------------------------------------------------
%% Private API
wait_for_application(Application) ->
case lists:keymember(Application, 1, application:which_applications()) of
true -> ok;
false -> timer:sleep(1000),
wait_for_application(Application)
end.
load_applications(Worklist, Loaded) ->
case queue:out(Worklist) of
{empty, _WorkList} ->
ok;
{{value, App}, Worklist1} ->
case sets:is_element(App, Loaded) of
true -> load_applications(Worklist1, Loaded);
false -> case application:load(App) of
ok -> ok;
{error, {already_loaded, App}} -> ok;
Error -> throw(Error)
end,
load_applications(
queue:join(Worklist1,
queue:from_list(app_dependencies(App))),
sets:add_element(App, Loaded))
end
end.
app_dependencies(App) ->
case application:get_key(App, applications) of
undefined -> [];
{ok, Lst} -> Lst
end.
manage_applications(Iterate, Do, Undo, SkipError, ErrorTag, Apps) ->
Iterate(fun (App, Acc) ->
case Do(App) of
ok -> [App | Acc];
{error, {SkipError, _}} -> Acc;
{error, Reason} ->
lists:foreach(Undo, Acc),
throw({error, {ErrorTag, App, Reason}})
end
end, [], Apps),
ok.

View File

@ -433,51 +433,47 @@
-spec(confirmed_broadcast/2 :: (pid(), any()) -> 'ok').
-spec(group_members/1 :: (pid()) -> [pid()]).
%% The joined, members_changed and handle_msg callbacks can all
%% return any of the following terms:
%% The joined, members_changed and handle_msg callbacks can all return
%% any of the following terms:
%%
%% 'ok' - the callback function returns normally
%%
%% {'stop', Reason} - the callback indicates the member should
%% stop with reason Reason and should leave the group.
%% {'stop', Reason} - the callback indicates the member should stop
%% with reason Reason and should leave the group.
%%
%% {'become', Module, Args} - the callback indicates that the
%% callback module should be changed to Module and that the
%% callback functions should now be passed the arguments
%% Args. This allows the callback module to be dynamically
%% changed.
%% {'become', Module, Args} - the callback indicates that the callback
%% module should be changed to Module and that the callback functions
%% should now be passed the arguments Args. This allows the callback
%% module to be dynamically changed.
%% Called when we've successfully joined the group. Supplied with
%% Args provided in start_link, plus current group members.
%% Called when we've successfully joined the group. Supplied with Args
%% provided in start_link, plus current group members.
-callback joined(Args :: term(), Members :: [pid()]) ->
ok | {stop, Reason :: term()} | {become, Module :: atom(), Args :: any()}.
%% Supplied with Args provided in start_link, the list of new
%% members and the list of members previously known to us that
%% have since died. Note that if a member joins and dies very
%% quickly, it's possible that we will never see that member
%% appear in either births or deaths. However we are guaranteed
%% that (1) we will see a member joining either in the births
%% here, or in the members passed to joined/2 before receiving
%% any messages from it; and (2) we will not see members die that
%% we have not seen born (or supplied in the members to
%% joined/2).
%% Supplied with Args provided in start_link, the list of new members
%% and the list of members previously known to us that have since
%% died. Note that if a member joins and dies very quickly, it's
%% possible that we will never see that member appear in either births
%% or deaths. However we are guaranteed that (1) we will see a member
%% joining either in the births here, or in the members passed to
%% joined/2 before receiving any messages from it; and (2) we will not
%% see members die that we have not seen born (or supplied in the
%% members to joined/2).
-callback members_changed(Args :: term(), Births :: [pid()],
Deaths :: [pid()]) ->
ok | {stop, Reason :: term()} | {become, Module :: atom(), Args :: any()}.
%% Supplied with Args provided in start_link, the sender, and the
%% message. This does get called for messages injected by this
%% member, however, in such cases, there is no special
%% significance of this invocation: it does not indicate that the
%% message has made it to any other members, let alone all other
%% members.
%% message. This does get called for messages injected by this member,
%% however, in such cases, there is no special significance of this
%% invocation: it does not indicate that the message has made it to
%% any other members, let alone all other members.
-callback handle_msg(Args :: term(), From :: pid(), Message :: term()) ->
ok | {stop, Reason :: term()} | {become, Module :: atom(), Args :: any()}.
%% Called on gm member termination as per rules in gen_server,
%% with the Args provided in start_link plus the termination
%% Reason.
%% Called on gm member termination as per rules in gen_server, with
%% the Args provided in start_link plus the termination Reason.
-callback terminate(Args :: term(), Reason :: term()) ->
ok | term().
@ -533,7 +529,7 @@ init([GroupName, Module, Args]) ->
group_name = GroupName,
module = Module,
view = undefined,
pub_count = 0,
pub_count = -1,
members_state = undefined,
callback_args = Args,
confirms = queue:new(),
@ -575,33 +571,39 @@ handle_call({add_on_right, NewMember}, _From,
members_state = MembersState,
module = Module,
callback_args = Args }) ->
Group = record_new_member_in_group(
GroupName, Self, NewMember,
fun (Group1) ->
View1 = group_to_view(Group1),
ok = send_right(NewMember, View1,
{catchup, Self, prepare_members_state(
MembersState)})
end),
{MembersState1, Group} =
record_new_member_in_group(
GroupName, Self, NewMember,
fun (Group1) ->
View1 = group_to_view(Group1),
MembersState1 = remove_erased_members(MembersState, View1),
ok = send_right(NewMember, View1,
{catchup, Self,
prepare_members_state(MembersState1)}),
MembersState1
end),
View2 = group_to_view(Group),
State1 = check_neighbours(State #state { view = View2 }),
State1 = check_neighbours(State #state { view = View2,
members_state = MembersState1 }),
Result = callback_view_changed(Args, Module, View, View2),
handle_callback_result({Result, {ok, Group}, State1}).
handle_cast({?TAG, ReqVer, Msg},
State = #state { view = View,
members_state = MembersState,
group_name = GroupName,
module = Module,
callback_args = Args }) ->
{Result, State1} =
case needs_view_update(ReqVer, View) of
true ->
View1 = group_to_view(read_group(GroupName)),
{callback_view_changed(Args, Module, View, View1),
check_neighbours(State #state { view = View1 })};
false ->
{ok, State}
true -> View1 = group_to_view(read_group(GroupName)),
MemberState1 = remove_erased_members(MembersState, View1),
{callback_view_changed(Args, Module, View, View1),
check_neighbours(
State #state { view = View1,
members_state = MemberState1 })};
false -> {ok, State}
end,
handle_callback_result(
if_callback_success(
@ -665,22 +667,21 @@ handle_info({'DOWN', MRef, process, _Pid, _Reason},
_ ->
View1 =
group_to_view(record_dead_member_in_group(Member, GroupName)),
State1 = State #state { view = View1 },
{Result, State2} =
case alive_view_members(View1) of
[Self] ->
maybe_erase_aliases(
State1 #state {
{Result1, State1} = maybe_erase_aliases(State, View1),
{Result1, State1 #state {
members_state = blank_member_state(),
confirms = purge_confirms(Confirms) });
confirms = purge_confirms(Confirms) }};
_ ->
%% here we won't be pointing out any deaths:
%% the concern is that there maybe births
%% which we'd otherwise miss.
{callback_view_changed(Args, Module, View, View1),
State1}
check_neighbours(State #state { view = View1 })}
end,
handle_callback_result({Result, check_neighbours(State2)})
handle_callback_result({Result, State2})
end.
@ -693,9 +694,13 @@ terminate(Reason, State = #state { module = Module,
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
prioritise_info(flush, _State) -> 1;
prioritise_info({'DOWN', _MRef, process, _Pid, _Reason}, _State) -> 1;
prioritise_info(_ , _State) -> 0.
prioritise_info(flush, _State) ->
1;
prioritise_info({'DOWN', _MRef, process, _Pid, _Reason},
#state { members_state = MS }) when MS /= undefined ->
1;
prioritise_info(_, _State) ->
0.
handle_msg(check_neighbours, State) ->
@ -795,8 +800,8 @@ handle_msg({activity, Left, Activity},
State1 = State #state { members_state = MembersState1,
confirms = Confirms1 },
Activity3 = activity_finalise(Activity1),
{Result, State2} = maybe_erase_aliases(State1),
ok = maybe_send_activity(Activity3, State2),
ok = maybe_send_activity(Activity3, State1),
{Result, State2} = maybe_erase_aliases(State1, View),
if_callback_success(
Result, fun activity_true/3, fun activity_false/3, Activity3, State2);
@ -829,13 +834,14 @@ internal_broadcast(Msg, From, State = #state { self = Self,
confirms = Confirms,
callback_args = Args,
broadcast_buffer = Buffer }) ->
PubCount1 = PubCount + 1,
Result = Module:handle_msg(Args, get_pid(Self), Msg),
Buffer1 = [{PubCount, Msg} | Buffer],
Buffer1 = [{PubCount1, Msg} | Buffer],
Confirms1 = case From of
none -> Confirms;
_ -> queue:in({PubCount, From}, Confirms)
_ -> queue:in({PubCount1, From}, Confirms)
end,
State1 = State #state { pub_count = PubCount + 1,
State1 = State #state { pub_count = PubCount1,
confirms = Confirms1,
broadcast_buffer = Buffer1 },
case From =/= none of
@ -850,14 +856,17 @@ flush_broadcast_buffer(State = #state { broadcast_buffer = [] }) ->
State;
flush_broadcast_buffer(State = #state { self = Self,
members_state = MembersState,
broadcast_buffer = Buffer }) ->
broadcast_buffer = Buffer,
pub_count = PubCount }) ->
[{PubCount, _Msg}|_] = Buffer, %% ASSERTION match on PubCount
Pubs = lists:reverse(Buffer),
Activity = activity_cons(Self, Pubs, [], activity_nil()),
ok = maybe_send_activity(activity_finalise(Activity), State),
MembersState1 = with_member(
fun (Member = #member { pending_ack = PA }) ->
PA1 = queue:join(PA, queue:from_list(Pubs)),
Member #member { pending_ack = PA1 }
Member #member { pending_ack = PA1,
last_pub = PubCount }
end, Self, MembersState),
State #state { members_state = MembersState1,
broadcast_buffer = [] }.
@ -1052,7 +1061,7 @@ record_dead_member_in_group(Member, GroupName) ->
Group.
record_new_member_in_group(GroupName, Left, NewMember, Fun) ->
{atomic, Group} =
{atomic, {Result, Group}} =
mnesia:sync_transaction(
fun () ->
[#gm_group { members = Members, version = Ver } = Group1] =
@ -1062,11 +1071,11 @@ record_new_member_in_group(GroupName, Left, NewMember, Fun) ->
Members1 = Prefix ++ [Left, NewMember | Suffix],
Group2 = Group1 #gm_group { members = Members1,
version = Ver + 1 },
ok = Fun(Group2),
Result = Fun(Group2),
mnesia:write(Group2),
Group2
{Result, Group2}
end),
Group.
{Result, Group}.
erase_members_in_group(Members, GroupName) ->
DeadMembers = [{dead, Id} || Id <- Members],
@ -1089,10 +1098,10 @@ erase_members_in_group(Members, GroupName) ->
maybe_erase_aliases(State = #state { self = Self,
group_name = GroupName,
view = View,
view = View0,
members_state = MembersState,
module = Module,
callback_args = Args }) ->
callback_args = Args }, View) ->
#view_member { aliases = Aliases } = fetch_view_member(Self, View),
{Erasable, MembersState1}
= ?SETS:fold(
@ -1107,11 +1116,11 @@ maybe_erase_aliases(State = #state { self = Self,
end, {[], MembersState}, Aliases),
State1 = State #state { members_state = MembersState1 },
case Erasable of
[] -> {ok, State1};
[] -> {ok, State1 #state { view = View }};
_ -> View1 = group_to_view(
erase_members_in_group(Erasable, GroupName)),
{callback_view_changed(Args, Module, View, View1),
State1 #state { view = View1 }}
{callback_view_changed(Args, Module, View0, View1),
check_neighbours(State1 #state { view = View1 })}
end.
can_erase_view_member(Self, Self, _LA, _LP) -> false;
@ -1257,6 +1266,12 @@ make_member(GroupName) ->
{error, not_found} -> ?VERSION_START
end, self()}.
remove_erased_members(MembersState, View) ->
lists:foldl(fun (Id, MembersState1) ->
store_member(Id, find_member_or_blank(Id, MembersState),
MembersState1)
end, blank_member_state(), all_known_members(View)).
get_pid({_Version, Pid}) -> Pid.
get_pids(Ids) -> [Pid || {_Version, Pid} <- Ids].
@ -1287,16 +1302,30 @@ send_right(Right, View, Msg) ->
ok = gen_server2:cast(get_pid(Right), {?TAG, view_version(View), Msg}).
callback(Args, Module, Activity) ->
lists:foldl(
fun ({Id, Pubs, _Acks}, ok) ->
lists:foldl(fun ({_PubNum, Pub}, ok) ->
Module:handle_msg(Args, get_pid(Id), Pub);
(_, Error) ->
Error
end, ok, Pubs);
(_, Error) ->
Error
end, ok, Activity).
Result =
lists:foldl(
fun ({Id, Pubs, _Acks}, {Args1, Module1, ok}) ->
lists:foldl(fun ({_PubNum, Pub}, Acc = {Args2, Module2, ok}) ->
case Module2:handle_msg(
Args2, get_pid(Id), Pub) of
ok ->
Acc;
{become, Module3, Args3} ->
{Args3, Module3, ok};
{stop, _Reason} = Error ->
Error
end;
(_, Error = {stop, _Reason}) ->
Error
end, {Args1, Module1, ok}, Pubs);
(_, Error = {stop, _Reason}) ->
Error
end, {Args, Module, ok}, Activity),
case Result of
{Args, Module, ok} -> ok;
{Args1, Module1, ok} -> {become, Module1, Args1};
{stop, _Reason} = Error -> Error
end.
callback_view_changed(Args, Module, OldView, NewView) ->
OldMembers = all_known_members(OldView),

View File

@ -225,8 +225,8 @@ which_children(Sup) -> fold(which_children, Sup, fun lists:append/2).
count_children(Sup) -> fold(count_children, Sup, fun add_proplists/2).
check_childspecs(Specs) -> ?SUPERVISOR:check_childspecs(Specs).
call(Sup, Msg) ->
?GEN_SERVER:call(child(Sup, mirroring), Msg, infinity).
call(Sup, Msg) -> ?GEN_SERVER:call(mirroring(Sup), Msg, infinity).
cast(Sup, Msg) -> ?GEN_SERVER:cast(mirroring(Sup), Msg).
find_call(Sup, Id, Msg) ->
Group = call(Sup, group),
@ -237,7 +237,7 @@ find_call(Sup, Id, Msg) ->
%% immediately after the tx - we can't be 100% here. So we may as
%% well dirty_select.
case mnesia:dirty_select(?TABLE, [{MatchHead, [], ['$1']}]) of
[Mirror] -> ?GEN_SERVER:call(Mirror, Msg, infinity);
[Mirror] -> call(Mirror, Msg);
[] -> {error, not_found}
end.
@ -246,13 +246,16 @@ fold(FunAtom, Sup, AggFun) ->
lists:foldl(AggFun, [],
[apply(?SUPERVISOR, FunAtom, [D]) ||
M <- ?PG2:get_members(Group),
D <- [?GEN_SERVER:call(M, delegate_supervisor, infinity)]]).
D <- [delegate(M)]]).
child(Sup, Id) ->
[Pid] = [Pid || {Id1, Pid, _, _} <- ?SUPERVISOR:which_children(Sup),
Id1 =:= Id],
Pid.
delegate(Sup) -> child(Sup, delegate).
mirroring(Sup) -> child(Sup, mirroring).
%%----------------------------------------------------------------------------
start_internal(Group, ChildSpecs) ->
@ -261,24 +264,19 @@ start_internal(Group, ChildSpecs) ->
%%----------------------------------------------------------------------------
init({overall, Group, Init}) ->
case Init of
{ok, {Restart, ChildSpecs}} ->
Delegate = {delegate, {?SUPERVISOR, start_link,
[?MODULE, {delegate, Restart}]},
temporary, 16#ffffffff, supervisor, [?SUPERVISOR]},
Mirroring = {mirroring, {?MODULE, start_internal,
[Group, ChildSpecs]},
permanent, 16#ffffffff, worker, [?MODULE]},
%% Important: Delegate MUST start before Mirroring so that
%% when we shut down from above it shuts down last, so
%% Mirroring does not see it die.
%%
%% See comment in handle_info('DOWN', ...) below
{ok, {{one_for_all, 0, 1}, [Delegate, Mirroring]}};
ignore ->
ignore
end;
init({overall, _Group, ignore}) -> ignore;
init({overall, Group, {ok, {Restart, ChildSpecs}}}) ->
%% Important: Delegate MUST start before Mirroring so that when we
%% shut down from above it shuts down last, so Mirroring does not
%% see it die.
%%
%% See comment in handle_info('DOWN', ...) below
{ok, {{one_for_all, 0, 1},
[{delegate, {?SUPERVISOR, start_link, [?MODULE, {delegate, Restart}]},
temporary, 16#ffffffff, supervisor, [?SUPERVISOR]},
{mirroring, {?MODULE, start_internal, [Group, ChildSpecs]},
permanent, 16#ffffffff, worker, [?MODULE]}]}};
init({delegate, Restart}) ->
{ok, {Restart, []}};
@ -293,28 +291,29 @@ handle_call({init, Overall}, _From,
initial_childspecs = ChildSpecs}) ->
process_flag(trap_exit, true),
?PG2:create(Group),
ok = ?PG2:join(Group, self()),
Rest = ?PG2:get_members(Group) -- [self()],
ok = ?PG2:join(Group, Overall),
Rest = ?PG2:get_members(Group) -- [Overall],
case Rest of
[] -> {atomic, _} = mnesia:transaction(fun() -> delete_all(Group) end);
_ -> ok
end,
[begin
?GEN_SERVER:cast(Pid, {ensure_monitoring, self()}),
?GEN_SERVER:cast(mirroring(Pid), {ensure_monitoring, Overall}),
erlang:monitor(process, Pid)
end || Pid <- Rest],
Delegate = child(Overall, delegate),
Delegate = delegate(Overall),
erlang:monitor(process, Delegate),
State1 = State#state{overall = Overall, delegate = Delegate},
case errors([maybe_start(Group, Delegate, S) || S <- ChildSpecs]) of
case errors([maybe_start(Group, Overall, Delegate, S) || S <- ChildSpecs]) of
[] -> {reply, ok, State1};
Errors -> {stop, {shutdown, {init, Errors, ChildSpecs}}, State1}
Errors -> {stop, {shutdown, Errors}, State1}
end;
handle_call({start_child, ChildSpec}, _From,
State = #state{delegate = Delegate,
State = #state{overall = Overall,
delegate = Delegate,
group = Group}) ->
{reply, case maybe_start(Group, Delegate, ChildSpec) of
{reply, case maybe_start(Group, Overall, Delegate, ChildSpec) of
already_in_mnesia -> {error, already_present};
{already_in_mnesia, Pid} -> {error, {already_started, Pid}};
Else -> Else
@ -327,9 +326,6 @@ handle_call({delete_child, Id}, _From, State = #state{delegate = Delegate,
handle_call({msg, F, A}, _From, State = #state{delegate = Delegate}) ->
{reply, apply(?SUPERVISOR, F, [Delegate | A]), State};
handle_call(delegate_supervisor, _From, State = #state{delegate = Delegate}) ->
{reply, Delegate, State};
handle_call(group, _From, State = #state{group = Group}) ->
{reply, Group, State};
@ -348,7 +344,7 @@ handle_cast(Msg, State) ->
{stop, {unexpected_cast, Msg}, State}.
handle_info({'DOWN', _Ref, process, Pid, Reason},
State = #state{delegate = Pid, group = Group}) ->
State = #state{overall = Pid, group = Group}) ->
%% Since the delegate is temporary, its death won't cause us to
%% die. Since the overall supervisor kills processes in reverse
%% order when shutting down "from above" and we started after the
@ -362,21 +358,20 @@ handle_info({'DOWN', _Ref, process, Pid, Reason},
{stop, Reason, State};
handle_info({'DOWN', _Ref, process, Pid, _Reason},
State = #state{delegate = Delegate, group = Group}) ->
State = #state{delegate = Delegate, group = Group,
overall = O}) ->
%% TODO load balance this
%% No guarantee pg2 will have received the DOWN before us.
Self = self(),
{R, Cs, X} =
case lists:sort(?PG2:get_members(Group)) -- [Pid] of
[Self | _] -> {atomic, {ChildSpecs, Extra}} =
mnesia:transaction(fun() -> update_all(Pid) end),
{[start(Delegate, ChildSpec) || ChildSpec <- ChildSpecs],
ChildSpecs, Extra};
_ -> {[], [], []}
R = case lists:sort(?PG2:get_members(Group)) -- [Pid] of
[O | _] -> {atomic, ChildSpecs} =
mnesia:transaction(
fun() -> update_all(O, Pid) end),
[start(Delegate, ChildSpec) || ChildSpec <- ChildSpecs];
_ -> []
end,
case errors(R) of
[] -> {noreply, State};
Errors -> {stop, {shutdown, {down, Errors, Cs, X}}, State}
Errors -> {stop, {shutdown, Errors}, State}
end;
handle_info(Info, State) ->
@ -391,13 +386,11 @@ code_change(_OldVsn, State, _Extra) ->
%%----------------------------------------------------------------------------
tell_all_peers_to_die(Group, Reason) ->
[?GEN_SERVER:cast(P, {die, Reason}) ||
P <- ?PG2:get_members(Group) -- [self()]].
[cast(P, {die, Reason}) || P <- ?PG2:get_members(Group) -- [self()]].
maybe_start(Group, Delegate, ChildSpec) ->
case mnesia:transaction(fun() ->
check_start(Group, Delegate, ChildSpec)
end) of
maybe_start(Group, Overall, Delegate, ChildSpec) ->
case mnesia:transaction(
fun() -> check_start(Group, Overall, Delegate, ChildSpec) end) of
{atomic, start} -> start(Delegate, ChildSpec);
{atomic, undefined} -> already_in_mnesia;
{atomic, Pid} -> {already_in_mnesia, Pid};
@ -405,31 +398,29 @@ maybe_start(Group, Delegate, ChildSpec) ->
{aborted, E} -> {error, E}
end.
check_start(Group, Delegate, ChildSpec) ->
check_start(Group, Overall, Delegate, ChildSpec) ->
case mnesia:wread({?TABLE, {Group, id(ChildSpec)}}) of
[] -> write(Group, ChildSpec),
[] -> write(Group, Overall, ChildSpec),
start;
[S] -> #mirrored_sup_childspec{key = {Group, Id},
mirroring_pid = Pid} = S,
case self() of
case Overall of
Pid -> child(Delegate, Id);
_ -> case supervisor(Pid) of
dead -> write(Group, ChildSpec),
dead -> write(Group, Overall, ChildSpec),
start;
Delegate0 -> child(Delegate0, Id)
end
end
end.
supervisor(Pid) ->
with_exit_handler(
fun() -> dead end,
fun() -> gen_server:call(Pid, delegate_supervisor, infinity) end).
supervisor(Pid) -> with_exit_handler(fun() -> dead end,
fun() -> delegate(Pid) end).
write(Group, ChildSpec) ->
write(Group, Overall, ChildSpec) ->
ok = mnesia:write(
#mirrored_sup_childspec{key = {Group, id(ChildSpec)},
mirroring_pid = self(),
mirroring_pid = Overall,
childspec = ChildSpec}),
ChildSpec.
@ -455,13 +446,13 @@ check_stop(Group, Delegate, Id) ->
id({Id, _, _, _, _, _}) -> Id.
update_all(OldPid) ->
MatchHead = #mirrored_sup_childspec{mirroring_pid = OldPid,
update_all(Overall, OldOverall) ->
MatchHead = #mirrored_sup_childspec{mirroring_pid = OldOverall,
key = '$1',
childspec = '$2',
_ = '_'},
Matches = mnesia:select(?TABLE, [{MatchHead, [], ['$$']}]),
{[write(Group, C) || [{Group, _Id}, C] <- Matches], {OldPid, Matches}}.
[write(Group, Overall, C) ||
[{Group, _Id}, C] <- mnesia:select(?TABLE, [{MatchHead, [], ['$$']}])].
delete_all(Group) ->
MatchHead = #mirrored_sup_childspec{key = {Group, '_'},
@ -474,8 +465,7 @@ errors(Results) -> [E || {error, E} <- Results].
%%----------------------------------------------------------------------------
create_tables() ->
create_tables([?TABLE_DEF]).
create_tables() -> create_tables([?TABLE_DEF]).
create_tables([]) ->
ok;

View File

@ -51,7 +51,7 @@ test_migrate() ->
with_sups(fun([A, _]) ->
?MS:start_child(a, childspec(worker)),
Pid1 = pid_of(worker),
kill(A, Pid1),
kill_registered(A, Pid1),
Pid2 = pid_of(worker),
false = (Pid1 =:= Pid2)
end, [a, b]).
@ -61,10 +61,10 @@ test_migrate_twice() ->
with_sups(fun([A, B]) ->
?MS:start_child(a, childspec(worker)),
Pid1 = pid_of(worker),
kill(A, Pid1),
kill_registered(A, Pid1),
{ok, C} = start_sup(c),
Pid2 = pid_of(worker),
kill(B, Pid2),
kill_registered(B, Pid2),
Pid3 = pid_of(worker),
false = (Pid1 =:= Pid3),
kill(C)
@ -124,7 +124,7 @@ test_large_group() ->
with_sups(fun([A, _, _, _]) ->
?MS:start_child(a, childspec(worker)),
Pid1 = pid_of(worker),
kill(A, Pid1),
kill_registered(A, Pid1),
Pid2 = pid_of(worker),
false = (Pid1 =:= Pid2)
end, [a, b, c, d]).
@ -134,7 +134,7 @@ test_childspecs_at_init() ->
S = childspec(worker),
with_sups(fun([A, _]) ->
Pid1 = pid_of(worker),
kill(A, Pid1),
kill_registered(A, Pid1),
Pid2 = pid_of(worker),
false = (Pid1 =:= Pid2)
end, [{a, [S]}, {b, [S]}]).
@ -143,7 +143,7 @@ test_anonymous_supervisors() ->
with_sups(fun([A, _B]) ->
?MS:start_child(A, childspec(worker)),
Pid1 = pid_of(worker),
kill(A, Pid1),
kill_registered(A, Pid1),
Pid2 = pid_of(worker),
false = (Pid1 =:= Pid2)
end, [anon, anon]).
@ -157,7 +157,7 @@ test_no_migration_on_shutdown() ->
with_sups(fun([Evil, _]) ->
?MS:start_child(Evil, childspec(worker)),
try
call(worker, ping, 10000, 100),
call(worker, ping, 1000, 100),
exit(worker_should_not_have_migrated)
catch exit:{timeout_waiting_for_server, _, _} ->
ok
@ -268,7 +268,7 @@ inc_group() ->
get_group(Group) ->
{Group, get(counter)}.
call(Id, Msg) -> call(Id, Msg, 5*24*60*60*1000, 100).
call(Id, Msg) -> call(Id, Msg, 10*1000, 100).
call(Id, Msg, 0, _Decr) ->
exit({timeout_waiting_for_server, {Id, Msg}, erlang:get_stacktrace()});
@ -285,10 +285,16 @@ kill(Pid, Wait) when is_pid(Wait) -> kill(Pid, [Wait]);
kill(Pid, Waits) ->
erlang:monitor(process, Pid),
[erlang:monitor(process, P) || P <- Waits],
exit(Pid, kill),
exit(Pid, bang),
kill_wait(Pid),
[kill_wait(P) || P <- Waits].
kill_registered(Pid, Child) ->
{registered_name, Name} = erlang:process_info(Child, registered_name),
kill(Pid, Child),
false = (Child =:= whereis(Name)),
ok.
kill_wait(Pid) ->
receive
{'DOWN', _Ref, process, Pid, _Reason} ->

View File

@ -18,9 +18,9 @@
-behaviour(application).
-export([maybe_hipe_compile/0, prepare/0, start/0, stop/0, stop_and_halt/0,
status/0, is_running/0, is_running/1, environment/0,
rotate_logs/1, force_event_refresh/0]).
-export([start/0, boot/0, stop/0,
stop_and_halt/0, await_startup/0, status/0, is_running/0,
is_running/1, environment/0, rotate_logs/1, force_event_refresh/0]).
-export([start/2, stop/1]).
@ -60,7 +60,8 @@
-rabbit_boot_step({worker_pool,
[{description, "worker pool"},
{mfa, {rabbit_sup, start_child, [worker_pool_sup]}},
{mfa, {rabbit_sup, start_supervisor_child,
[worker_pool_sup]}},
{requires, pre_boot},
{enables, external_infrastructure}]}).
@ -143,7 +144,8 @@
-rabbit_boot_step({mirror_queue_slave_sup,
[{description, "mirror queue slave sup"},
{mfa, {rabbit_mirror_queue_slave_sup, start, []}},
{mfa, {rabbit_sup, start_supervisor_child,
[rabbit_mirror_queue_slave_sup]}},
{requires, recovery},
{enables, routing_ready}]}).
@ -197,7 +199,7 @@
rabbit_queue_index, gen, dict, ordsets, file_handle_cache,
rabbit_msg_store, array, rabbit_msg_store_ets_index, rabbit_msg_file,
rabbit_exchange_type_fanout, rabbit_exchange_type_topic, mnesia,
mnesia_lib, rpc, mnesia_tm, qlc, sofs, proplists, credit_flow]).
mnesia_lib, rpc, mnesia_tm, qlc, sofs, proplists, credit_flow, pmon]).
%% HiPE compilation uses multiple cores anyway, but some bits are
%% IO-bound so we can go faster if we parallelise a bit more. In
@ -214,11 +216,11 @@
-type(log_location() :: 'tty' | 'undefined' | file:filename()).
-type(param() :: atom()).
-spec(maybe_hipe_compile/0 :: () -> 'ok').
-spec(prepare/0 :: () -> 'ok').
-spec(start/0 :: () -> 'ok').
-spec(boot/0 :: () -> 'ok').
-spec(stop/0 :: () -> 'ok').
-spec(stop_and_halt/0 :: () -> no_return()).
-spec(await_startup/0 :: () -> 'ok').
-spec(status/0 ::
() -> [{pid, integer()} |
{running_applications, [{atom(), string(), string()}]} |
@ -283,29 +285,51 @@ split(L, N) -> split0(L, [[] || _ <- lists:seq(1, N)]).
split0([], Ls) -> Ls;
split0([I | Is], [L | Ls]) -> split0(Is, Ls ++ [[I | L]]).
prepare() ->
ok = ensure_working_log_handlers(),
ok = rabbit_upgrade:maybe_upgrade_mnesia().
ensure_application_loaded() ->
%% We end up looking at the rabbit app's env for HiPE and log
%% handling, so it needs to be loaded. But during the tests, it
%% may end up getting loaded twice, so guard against that.
case application:load(rabbit) of
ok -> ok;
{error, {already_loaded, rabbit}} -> ok
end.
start() ->
start_it(fun() ->
%% We do not want to HiPE compile or upgrade
%% mnesia after just restarting the app
ok = ensure_application_loaded(),
ok = ensure_working_log_handlers(),
ok = app_utils:start_applications(app_startup_order()),
ok = print_plugin_info(rabbit_plugins:active())
end).
boot() ->
start_it(fun() ->
ok = ensure_application_loaded(),
maybe_hipe_compile(),
ok = ensure_working_log_handlers(),
ok = rabbit_upgrade:maybe_upgrade_mnesia(),
Plugins = rabbit_plugins:setup(),
ToBeLoaded = Plugins ++ ?APPS,
ok = app_utils:load_applications(ToBeLoaded),
StartupApps = app_utils:app_dependency_order(ToBeLoaded,
false),
ok = app_utils:start_applications(StartupApps),
ok = print_plugin_info(Plugins)
end).
start_it(StartFun) ->
try
%% prepare/1 ends up looking at the rabbit app's env, so it
%% needs to be loaded, but during the tests, it may end up
%% getting loaded twice, so guard against that
case application:load(rabbit) of
ok -> ok;
{error, {already_loaded, rabbit}} -> ok
end,
ok = prepare(),
ok = rabbit_misc:start_applications(application_load_order())
StartFun()
after
%%give the error loggers some time to catch up
%% give the error loggers some time to catch up
timer:sleep(100)
end.
stop() ->
rabbit_log:info("Stopping Rabbit~n"),
ok = rabbit_misc:stop_applications(application_load_order()).
ok = app_utils:stop_applications(app_shutdown_order()).
stop_and_halt() ->
try
@ -316,6 +340,9 @@ stop_and_halt() ->
end,
ok.
await_startup() ->
app_utils:wait_for_applications(app_startup_order()).
status() ->
S1 = [{pid, list_to_integer(os:getpid())},
{running_applications, application:which_applications(infinity)},
@ -392,46 +419,13 @@ stop(_State) ->
%%---------------------------------------------------------------------------
%% application life cycle
application_load_order() ->
ok = load_applications(),
{ok, G} = rabbit_misc:build_acyclic_graph(
fun (App, _Deps) -> [{App, App}] end,
fun (App, Deps) -> [{Dep, App} || Dep <- Deps] end,
[{App, app_dependencies(App)} ||
{App, _Desc, _Vsn} <- application:loaded_applications()]),
true = digraph:del_vertices(
G, digraph:vertices(G) -- digraph_utils:reachable(?APPS, G)),
Result = digraph_utils:topsort(G),
true = digraph:delete(G),
Result.
app_startup_order() ->
ok = app_utils:load_applications(?APPS),
app_utils:app_dependency_order(?APPS, false).
load_applications() ->
load_applications(queue:from_list(?APPS), sets:new()).
load_applications(Worklist, Loaded) ->
case queue:out(Worklist) of
{empty, _WorkList} ->
ok;
{{value, App}, Worklist1} ->
case sets:is_element(App, Loaded) of
true -> load_applications(Worklist1, Loaded);
false -> case application:load(App) of
ok -> ok;
{error, {already_loaded, App}} -> ok;
Error -> throw(Error)
end,
load_applications(
queue:join(Worklist1,
queue:from_list(app_dependencies(App))),
sets:add_element(App, Loaded))
end
end.
app_dependencies(App) ->
case application:get_key(App, applications) of
undefined -> [];
{ok, Lst} -> Lst
end.
app_shutdown_order() ->
Apps = ?APPS ++ rabbit_plugins:active(),
app_utils:app_dependency_order(Apps, true).
%%---------------------------------------------------------------------------
%% boot step logic
@ -477,7 +471,8 @@ sort_boot_steps(UnsortedSteps) ->
%% there is one, otherwise fail).
SortedSteps = lists:reverse(
[begin
{StepName, Step} = digraph:vertex(G, StepName),
{StepName, Step} = digraph:vertex(G,
StepName),
Step
end || StepName <- digraph_utils:topsort(G)]),
digraph:delete(G),
@ -538,7 +533,7 @@ boot_error(Format, Args) ->
boot_delegate() ->
{ok, Count} = application:get_env(rabbit, delegate_count),
rabbit_sup:start_child(delegate_sup, [Count]).
rabbit_sup:start_supervisor_child(delegate_sup, [Count]).
recover() ->
rabbit_binding:recover(rabbit_exchange:recover(), rabbit_amqqueue:start()).
@ -559,7 +554,8 @@ insert_default_data() ->
ok = rabbit_vhost:add(DefaultVHost),
ok = rabbit_auth_backend_internal:add_user(DefaultUser, DefaultPass),
ok = rabbit_auth_backend_internal:set_tags(DefaultUser, DefaultTags),
ok = rabbit_auth_backend_internal:set_permissions(DefaultUser, DefaultVHost,
ok = rabbit_auth_backend_internal:set_permissions(DefaultUser,
DefaultVHost,
DefaultConfigurePerm,
DefaultWritePerm,
DefaultReadPerm),
@ -621,15 +617,12 @@ log_location(Type) ->
rotate_logs(File, Suffix, Handler) ->
rotate_logs(File, Suffix, Handler, Handler).
rotate_logs(File, Suffix, OldHandler, NewHandler) ->
case File of
undefined -> ok;
tty -> ok;
_ -> gen_event:swap_handler(
error_logger,
{OldHandler, swap},
{NewHandler, {File, Suffix}})
end.
rotate_logs(undefined, _Suffix, _OldHandler, _NewHandler) -> ok;
rotate_logs(tty, _Suffix, _OldHandler, _NewHandler) -> ok;
rotate_logs(File, Suffix, OldHandler, NewHandler) ->
gen_event:swap_handler(error_logger,
{OldHandler, swap},
{NewHandler, {File, Suffix}}).
log_rotation_result({error, MainLogError}, {error, SaslLogError}) ->
{error, {{cannot_rotate_main_logs, MainLogError},
@ -650,6 +643,24 @@ force_event_refresh() ->
%%---------------------------------------------------------------------------
%% misc
print_plugin_info([]) ->
ok;
print_plugin_info(Plugins) ->
%% This gets invoked by rabbitmqctl start_app, outside the context
%% of the rabbit application
rabbit_misc:with_local_io(
fun() ->
io:format("~n-- plugins running~n"),
[print_plugin_info(
AppName, element(2, application:get_key(AppName, vsn)))
|| AppName <- Plugins],
ok
end).
print_plugin_info(Plugin, Vsn) ->
Len = 76 - length(Vsn),
io:format("~-" ++ integer_to_list(Len) ++ "s ~s~n", [Plugin, Vsn]).
erts_version_check() ->
FoundVer = erlang:system_info(version),
case rabbit_misc:version_compare(?ERTS_MINIMUM, FoundVer, lte) of

View File

@ -162,17 +162,17 @@ maybe_alert(UpdateFun, Node, Source,
end,
State#alarms{alarmed_nodes = AN1}.
alert_local(Alert, Alertees, _Source) ->
alert(Alertees, [Alert], fun erlang:'=:='/2).
alert_local(Alert, Alertees, Source) ->
alert(Alertees, Source, Alert, fun erlang:'=:='/2).
alert_remote(Alert, Alertees, Source) ->
alert(Alertees, [Source, Alert], fun erlang:'=/='/2).
alert(Alertees, Source, Alert, fun erlang:'=/='/2).
alert(Alertees, AlertArg, NodeComparator) ->
alert(Alertees, Source, Alert, NodeComparator) ->
Node = node(),
dict:fold(fun (Pid, {M, F, A}, ok) ->
case NodeComparator(Node, node(Pid)) of
true -> apply(M, F, A ++ [Pid] ++ AlertArg);
true -> apply(M, F, A ++ [Pid, Source, Alert]);
false -> ok
end
end, ok, Alertees).
@ -181,7 +181,7 @@ internal_register(Pid, {M, F, A} = HighMemMFA,
State = #alarms{alertees = Alertees}) ->
_MRef = erlang:monitor(process, Pid),
case dict:find(node(), State#alarms.alarmed_nodes) of
{ok, _Sources} -> apply(M, F, A ++ [Pid, true]);
{ok, Sources} -> [apply(M, F, A ++ [Pid, R, true]) || R <- Sources];
error -> ok
end,
NewAlertees = dict:store(Pid, HighMemMFA, Alertees),

View File

@ -984,8 +984,6 @@ prioritise_call(Msg, _From, _State) ->
info -> 9;
{info, _Items} -> 9;
consumers -> 9;
{basic_consume, _, _, _, _, _, _} -> 7;
{basic_cancel, _, _, _} -> 7;
stat -> 7;
_ -> 0
end.
@ -995,10 +993,6 @@ prioritise_cast(Msg, _State) ->
delete_immediately -> 8;
{set_ram_duration_target, _Duration} -> 8;
{set_maximum_since_use, _Age} -> 8;
{ack, _AckTags, _ChPid} -> 7;
{reject, _AckTags, _Requeue, _ChPid} -> 7;
{notify_sent, _ChPid, _Credit} -> 7;
{unblock, _ChPid} -> 7;
{run_backing_queue, _Mod, _Fun} -> 6;
_ -> 0
end.

View File

@ -26,10 +26,8 @@
('empty' |
%% Message, IsDelivered, AckTag, Remaining_Len
{rabbit_types:basic_message(), boolean(), Ack, non_neg_integer()})).
-type(is_durable() :: boolean()).
-type(attempt_recovery() :: boolean()).
-type(purged_msg_count() :: non_neg_integer()).
-type(confirm_required() :: boolean()).
-type(async_callback() :: fun ((atom(), fun ((atom(), state()) -> state())) -> 'ok')).
-type(duration() :: ('undefined' | 'infinity' | number())).

View File

@ -224,6 +224,5 @@ header_routes(HeadersTable) ->
{array, Routes} -> [Route || {longstr, Route} <- Routes];
undefined -> [];
{Type, _Val} -> throw({error, {unacceptable_type_in_header,
Type,
binary_to_list(HeaderKey)}})
binary_to_list(HeaderKey), Type}})
end || HeaderKey <- ?ROUTING_HEADERS]).

View File

@ -14,7 +14,7 @@
%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved.
%%
-module(rabbit_control).
-module(rabbit_control_main).
-include("rabbit.hrl").
-export([start/0, stop/0, action/5]).
@ -26,6 +26,61 @@
-define(NODE_OPT, "-n").
-define(VHOST_OPT, "-p").
-define(QUIET_DEF, {?QUIET_OPT, flag}).
-define(NODE_DEF(Node), {?NODE_OPT, {option, Node}}).
-define(VHOST_DEF, {?VHOST_OPT, {option, "/"}}).
-define(GLOBAL_DEFS(Node), [?QUIET_DEF, ?NODE_DEF(Node)]).
-define(COMMANDS,
[stop,
stop_app,
start_app,
wait,
reset,
force_reset,
rotate_logs,
cluster,
force_cluster,
cluster_status,
add_user,
delete_user,
change_password,
clear_password,
set_user_tags,
list_users,
add_vhost,
delete_vhost,
list_vhosts,
{set_permissions, [?VHOST_DEF]},
{clear_permissions, [?VHOST_DEF]},
{list_permissions, [?VHOST_DEF]},
list_user_permissions,
set_parameter,
clear_parameter,
list_parameters,
{list_queues, [?VHOST_DEF]},
{list_exchanges, [?VHOST_DEF]},
{list_bindings, [?VHOST_DEF]},
{list_connections, [?VHOST_DEF]},
list_channels,
{list_consumers, [?VHOST_DEF]},
status,
environment,
report,
eval,
close_connection,
{trace_on, [?VHOST_DEF]},
{trace_off, [?VHOST_DEF]},
set_vm_memory_high_watermark
]).
-define(GLOBAL_QUERIES,
[{"Connections", rabbit_networking, connection_info_all,
connection_info_keys},
@ -57,19 +112,18 @@
start() ->
{ok, [[NodeStr|_]|_]} = init:get_argument(nodename),
{[Command0 | Args], Opts} =
case rabbit_misc:get_options([{flag, ?QUIET_OPT},
{option, ?NODE_OPT, NodeStr},
{option, ?VHOST_OPT, "/"}],
init:get_plain_arguments()) of
{[], _Opts} -> usage();
CmdArgsAndOpts -> CmdArgsAndOpts
{Command, Opts, Args} =
case rabbit_misc:parse_arguments(?COMMANDS, ?GLOBAL_DEFS(NodeStr),
init:get_plain_arguments())
of
{ok, Res} -> Res;
no_command -> print_error("could not recognise command", []),
usage()
end,
Opts1 = [case K of
?NODE_OPT -> {?NODE_OPT, rabbit_nodes:make(V)};
_ -> {K, V}
end || {K, V} <- Opts],
Command = list_to_atom(Command0),
Quiet = proplists:get_bool(?QUIET_OPT, Opts1),
Node = proplists:get_value(?NODE_OPT, Opts1),
Inform = case Quiet of
@ -194,8 +248,7 @@ action(force_cluster, Node, ClusterNodeSs, _Opts, Inform) ->
action(wait, Node, [PidFile], _Opts, Inform) ->
Inform("Waiting for ~p", [Node]),
wait_for_application(Node, PidFile, rabbit, Inform);
wait_for_application(Node, PidFile, rabbit_and_plugins, Inform);
action(wait, Node, [PidFile, App], _Opts, Inform) ->
Inform("Waiting for ~p on ~p", [App, Node]),
wait_for_application(Node, PidFile, list_to_atom(App), Inform);
@ -216,33 +269,33 @@ action(rotate_logs, Node, [], _Opts, Inform) ->
Inform("Reopening logs for node ~p", [Node]),
call(Node, {rabbit, rotate_logs, [""]});
action(rotate_logs, Node, Args = [Suffix], _Opts, Inform) ->
Inform("Rotating logs to files with suffix ~p", [Suffix]),
Inform("Rotating logs to files with suffix \"~s\"", [Suffix]),
call(Node, {rabbit, rotate_logs, Args});
action(close_connection, Node, [PidStr, Explanation], _Opts, Inform) ->
Inform("Closing connection ~s", [PidStr]),
Inform("Closing connection \"~s\"", [PidStr]),
rpc_call(Node, rabbit_networking, close_connection,
[rabbit_misc:string_to_pid(PidStr), Explanation]);
action(add_user, Node, Args = [Username, _Password], _Opts, Inform) ->
Inform("Creating user ~p", [Username]),
Inform("Creating user \"~s\"", [Username]),
call(Node, {rabbit_auth_backend_internal, add_user, Args});
action(delete_user, Node, Args = [_Username], _Opts, Inform) ->
Inform("Deleting user ~p", Args),
Inform("Deleting user \"~s\"", Args),
call(Node, {rabbit_auth_backend_internal, delete_user, Args});
action(change_password, Node, Args = [Username, _Newpassword], _Opts, Inform) ->
Inform("Changing password for user ~p", [Username]),
Inform("Changing password for user \"~s\"", [Username]),
call(Node, {rabbit_auth_backend_internal, change_password, Args});
action(clear_password, Node, Args = [Username], _Opts, Inform) ->
Inform("Clearing password for user ~p", [Username]),
Inform("Clearing password for user \"~s\"", [Username]),
call(Node, {rabbit_auth_backend_internal, clear_password, Args});
action(set_user_tags, Node, [Username | TagsStr], _Opts, Inform) ->
Tags = [list_to_atom(T) || T <- TagsStr],
Inform("Setting tags for user ~p to ~p", [Username, Tags]),
Inform("Setting tags for user \"~s\" to ~p", [Username, Tags]),
rpc_call(Node, rabbit_auth_backend_internal, set_tags,
[list_to_binary(Username), Tags]);
@ -253,11 +306,11 @@ action(list_users, Node, [], _Opts, Inform) ->
rabbit_auth_backend_internal:user_info_keys());
action(add_vhost, Node, Args = [_VHostPath], _Opts, Inform) ->
Inform("Creating vhost ~p", Args),
Inform("Creating vhost \"~s\"", Args),
call(Node, {rabbit_vhost, add, Args});
action(delete_vhost, Node, Args = [_VHostPath], _Opts, Inform) ->
Inform("Deleting vhost ~p", Args),
Inform("Deleting vhost \"~s\"", Args),
call(Node, {rabbit_vhost, delete, Args});
action(list_vhosts, Node, Args, _Opts, Inform) ->
@ -319,12 +372,12 @@ action(list_consumers, Node, _Args, Opts, Inform) ->
action(trace_on, Node, [], Opts, Inform) ->
VHost = proplists:get_value(?VHOST_OPT, Opts),
Inform("Starting tracing for vhost ~p", [VHost]),
Inform("Starting tracing for vhost \"~s\"", [VHost]),
rpc_call(Node, rabbit_trace, start, [list_to_binary(VHost)]);
action(trace_off, Node, [], Opts, Inform) ->
VHost = proplists:get_value(?VHOST_OPT, Opts),
Inform("Stopping tracing for vhost ~p", [VHost]),
Inform("Stopping tracing for vhost \"~s\"", [VHost]),
rpc_call(Node, rabbit_trace, stop, [list_to_binary(VHost)]);
action(set_vm_memory_high_watermark, Node, [Arg], _Opts, Inform) ->
@ -337,19 +390,21 @@ action(set_vm_memory_high_watermark, Node, [Arg], _Opts, Inform) ->
action(set_permissions, Node, [Username, CPerm, WPerm, RPerm], Opts, Inform) ->
VHost = proplists:get_value(?VHOST_OPT, Opts),
Inform("Setting permissions for user ~p in vhost ~p", [Username, VHost]),
Inform("Setting permissions for user \"~s\" in vhost \"~s\"",
[Username, VHost]),
call(Node, {rabbit_auth_backend_internal, set_permissions,
[Username, VHost, CPerm, WPerm, RPerm]});
action(clear_permissions, Node, [Username], Opts, Inform) ->
VHost = proplists:get_value(?VHOST_OPT, Opts),
Inform("Clearing permissions for user ~p in vhost ~p", [Username, VHost]),
Inform("Clearing permissions for user \"~s\" in vhost \"~s\"",
[Username, VHost]),
call(Node, {rabbit_auth_backend_internal, clear_permissions,
[Username, VHost]});
action(list_permissions, Node, [], Opts, Inform) ->
VHost = proplists:get_value(?VHOST_OPT, Opts),
Inform("Listing permissions in vhost ~p", [VHost]),
Inform("Listing permissions in vhost \"~s\"", [VHost]),
display_info_list(call(Node, {rabbit_auth_backend_internal,
list_vhost_permissions, [VHost]}),
rabbit_auth_backend_internal:vhost_perms_info_keys());
@ -405,12 +460,22 @@ wait_for_application(Node, PidFile, Application, Inform) ->
Inform("pid is ~s", [Pid]),
wait_for_application(Node, Pid, Application).
wait_for_application(Node, Pid, rabbit_and_plugins) ->
wait_for_startup(Node, Pid);
wait_for_application(Node, Pid, Application) ->
while_process_is_alive(
Node, Pid, fun() -> rabbit_nodes:is_running(Node, Application) end).
wait_for_startup(Node, Pid) ->
while_process_is_alive(
Node, Pid, fun() -> rpc:call(Node, rabbit, await_startup, []) =:= ok end).
while_process_is_alive(Node, Pid, Activity) ->
case process_up(Pid) of
true -> case rabbit_nodes:is_running(Node, Application) of
true -> case Activity() of
true -> ok;
false -> timer:sleep(?EXTERNAL_CHECK_INTERVAL),
wait_for_application(Node, Pid, Application)
while_process_is_alive(Node, Pid, Activity)
end;
false -> {error, process_not_running}
end.
@ -425,7 +490,7 @@ wait_for_process_death(Pid) ->
read_pid_file(PidFile, Wait) ->
case {file:read_file(PidFile), Wait} of
{{ok, Bin}, _} ->
S = string:strip(binary_to_list(Bin), right, $\n),
S = re:replace(Bin, "\\s", "", [global, {return, list}]),
try list_to_integer(S)
catch error:badarg ->
exit({error, {garbage_in_pid_file, PidFile}})

View File

@ -47,16 +47,10 @@
%%----------------------------------------------------------------------------
boot() ->
{ok, _} =
supervisor2:start_child(
rabbit_sup,
{rabbit_direct_client_sup,
{rabbit_client_sup, start_link,
boot() -> rabbit_sup:start_supervisor_child(
rabbit_direct_client_sup, rabbit_client_sup,
[{local, rabbit_direct_client_sup},
{rabbit_channel_sup, start_link, []}]},
transient, infinity, supervisor, [rabbit_client_sup]}),
ok.
{rabbit_channel_sup, start_link, []}]).
force_event_refresh() ->
[Pid ! force_event_refresh || Pid<- list()],

View File

@ -178,8 +178,9 @@ parse_free_unix(CommandResult) ->
parse_free_win32(CommandResult) ->
LastLine = lists:last(string:tokens(CommandResult, "\r\n")),
[_, _Dir, Free, "bytes", "free"] = string:tokens(LastLine, " "),
list_to_integer(Free).
{match, [Free]} = re:run(lists:reverse(LastLine), "(\\d+)",
[{capture, all_but_first, list}]),
list_to_integer(lists:reverse(Free)).
interpret_limit({mem_relative, R}) ->
round(R * vm_memory_monitor:get_total_memory());

View File

@ -18,7 +18,7 @@
-behaviour(supervisor2).
-export([start/0, start_link/0, start_child/2]).
-export([start_link/0, start_child/2]).
-export([init/1]).
@ -26,20 +26,9 @@
-define(SERVER, ?MODULE).
start() ->
{ok, _} =
supervisor2:start_child(
rabbit_sup,
{rabbit_mirror_queue_slave_sup,
{rabbit_mirror_queue_slave_sup, start_link, []},
transient, infinity, supervisor, [rabbit_mirror_queue_slave_sup]}),
ok.
start_link() -> supervisor2:start_link({local, ?SERVER}, ?MODULE, []).
start_link() ->
supervisor2:start_link({local, ?SERVER}, ?MODULE, []).
start_child(Node, Args) ->
supervisor2:start_child({?SERVER, Node}, Args).
start_child(Node, Args) -> supervisor2:start_child({?SERVER, Node}, Args).
init([]) ->
{ok, {{simple_one_for_one_terminate, 10, 10},

View File

@ -19,7 +19,7 @@
-include("rabbit_framing.hrl").
-export([method_record_type/1, polite_pause/0, polite_pause/1]).
-export([die/1, frame_error/2, amqp_error/4,
-export([die/1, frame_error/2, amqp_error/4, quit/1, quit/2,
protocol_error/3, protocol_error/4, protocol_error/1]).
-export([not_found/1, assert_args_equivalence/4]).
-export([dirty_read/1]).
@ -42,14 +42,13 @@
-export([dirty_read_all/1, dirty_foreach_key/2, dirty_dump_log/1]).
-export([format/2, format_many/1, format_stderr/2]).
-export([with_local_io/1, local_info_msg/2]).
-export([start_applications/1, stop_applications/1]).
-export([unfold/2, ceil/1, queue_fold/3]).
-export([sort_field_table/1]).
-export([pid_to_string/1, string_to_pid/1]).
-export([version_compare/2, version_compare/3]).
-export([dict_cons/3, orddict_cons/3, gb_trees_cons/3]).
-export([gb_trees_fold/3, gb_trees_foreach/2]).
-export([get_options/2]).
-export([parse_arguments/3]).
-export([all_module_attributes/1, build_acyclic_graph/3]).
-export([now_ms/0]).
-export([const_ok/0, const/1]).
@ -59,7 +58,6 @@
-export([format_message_queue/2]).
-export([append_rpc_all_nodes/4]).
-export([multi_call/2]).
-export([quit/1]).
-export([os_cmd/1]).
-export([gb_sets_difference/2]).
@ -72,7 +70,7 @@
-type(ok_or_error() :: rabbit_types:ok_or_error(any())).
-type(thunk(T) :: fun(() -> T)).
-type(resource_name() :: binary()).
-type(optdef() :: {flag, string()} | {option, string(), any()}).
-type(optdef() :: flag | {option, string()}).
-type(channel_or_connection_exit()
:: rabbit_types:channel_exit() | rabbit_types:connection_exit()).
-type(digraph_label() :: term()).
@ -87,6 +85,10 @@
-spec(polite_pause/1 :: (non_neg_integer()) -> 'done').
-spec(die/1 ::
(rabbit_framing:amqp_exception()) -> channel_or_connection_exit()).
-spec(quit/1 :: (integer()) -> no_return()).
-spec(quit/2 :: (string(), [term()]) -> no_return()).
-spec(frame_error/2 :: (rabbit_framing:amqp_method_name(), binary())
-> rabbit_types:connection_exit()).
-spec(amqp_error/4 ::
@ -163,8 +165,6 @@
-spec(format_stderr/2 :: (string(), [any()]) -> 'ok').
-spec(with_local_io/1 :: (fun (() -> A)) -> A).
-spec(local_info_msg/2 :: (string(), [any()]) -> 'ok').
-spec(start_applications/1 :: ([atom()]) -> 'ok').
-spec(stop_applications/1 :: ([atom()]) -> 'ok').
-spec(unfold/2 :: (fun ((A) -> ({'true', B, A} | 'false')), A) -> {[B], A}).
-spec(ceil/1 :: (number()) -> integer()).
-spec(queue_fold/3 :: (fun ((any(), B) -> B), B, queue()) -> B).
@ -182,8 +182,12 @@
-spec(gb_trees_fold/3 :: (fun ((any(), any(), A) -> A), A, gb_tree()) -> A).
-spec(gb_trees_foreach/2 ::
(fun ((any(), any()) -> any()), gb_tree()) -> 'ok').
-spec(get_options/2 :: ([optdef()], [string()])
-> {[string()], [{string(), any()}]}).
-spec(parse_arguments/3 ::
([{atom(), [{string(), optdef()}]} | atom()],
[{string(), optdef()}],
[string()])
-> {'ok', {atom(), [{string(), string()}], [string()]}} |
'no_command').
-spec(all_module_attributes/1 :: (atom()) -> [{atom(), [term()]}]).
-spec(build_acyclic_graph/3 ::
(graph_vertex_fun(), graph_edge_fun(), [{atom(), [term()]}])
@ -206,7 +210,6 @@
-spec(append_rpc_all_nodes/4 :: ([node()], atom(), atom(), [any()]) -> [any()]).
-spec(multi_call/2 ::
([pid()], any()) -> {[{pid(), any()}], [{pid(), any()}]}).
-spec(quit/1 :: (integer() | string()) -> no_return()).
-spec(os_cmd/1 :: (string()) -> string()).
-spec(gb_sets_difference/2 :: (gb_set(), gb_set()) -> gb_set()).
@ -387,6 +390,28 @@ report_coverage_percentage(File, Cov, NotCov, Mod) ->
confirm_to_sender(Pid, MsgSeqNos) ->
gen_server2:cast(Pid, {confirm, MsgSeqNos, self()}).
%%
%% @doc Halts the emulator after printing out an error message io-formatted with
%% the supplied arguments. The exit status of the beam process will be set to 1.
%%
quit(Fmt, Args) ->
io:format("ERROR: " ++ Fmt ++ "~n", Args),
quit(1).
%%
%% @doc Halts the emulator returning the given status code to the os.
%% On Windows this function will block indefinitely so as to give the io
%% subsystem time to flush stdout completely.
%%
quit(Status) ->
case os:type() of
{unix, _} -> halt(Status);
{win32, _} -> init:stop(Status),
receive
after infinity -> ok
end
end.
throw_on_error(E, Thunk) ->
case Thunk() of
{error, Reason} -> throw({E, Reason});
@ -589,34 +614,6 @@ with_local_io(Fun) ->
local_info_msg(Format, Args) ->
with_local_io(fun () -> error_logger:info_msg(Format, Args) end).
manage_applications(Iterate, Do, Undo, SkipError, ErrorTag, Apps) ->
Iterate(fun (App, Acc) ->
case Do(App) of
ok -> [App | Acc];
{error, {SkipError, _}} -> Acc;
{error, Reason} ->
lists:foreach(Undo, Acc),
throw({error, {ErrorTag, App, Reason}})
end
end, [], Apps),
ok.
start_applications(Apps) ->
manage_applications(fun lists:foldl/3,
fun application:start/1,
fun application:stop/1,
already_started,
cannot_start_application,
Apps).
stop_applications(Apps) ->
manage_applications(fun lists:foldr/3,
fun application:stop/1,
fun application:start/1,
not_started,
cannot_stop_application,
Apps).
unfold(Fun, Init) ->
unfold(Fun, [], Init).
@ -736,39 +733,63 @@ gb_trees_fold1(Fun, Acc, {Key, Val, It}) ->
gb_trees_foreach(Fun, Tree) ->
gb_trees_fold(fun (Key, Val, Acc) -> Fun(Key, Val), Acc end, ok, Tree).
%% Separate flags and options from arguments.
%% get_options([{flag, "-q"}, {option, "-p", "/"}],
%% ["set_permissions","-p","/","guest",
%% "-q",".*",".*",".*"])
%% == {["set_permissions","guest",".*",".*",".*"],
%% [{"-q",true},{"-p","/"}]}
get_options(Defs, As) ->
lists:foldl(fun(Def, {AsIn, RsIn}) ->
{K, {AsOut, V}} =
case Def of
{flag, Key} ->
{Key, get_flag(Key, AsIn)};
{option, Key, Default} ->
{Key, get_option(Key, Default, AsIn)}
end,
{AsOut, [{K, V} | RsIn]}
end, {As, []}, Defs).
%% Takes:
%% * A list of [{atom(), [{string(), optdef()]} | atom()], where the atom()s
%% are the accepted commands and the optional [string()] is the list of
%% accepted options for that command
%% * A list [{string(), optdef()}] of options valid for all commands
%% * The list of arguments given by the user
%%
%% Returns either {ok, {atom(), [{string(), string()}], [string()]} which are
%% respectively the command, the key-value pairs of the options and the leftover
%% arguments; or no_command if no command could be parsed.
parse_arguments(Commands, GlobalDefs, As) ->
lists:foldl(maybe_process_opts(GlobalDefs, As), no_command, Commands).
get_option(K, _Default, [K, V | As]) ->
{As, V};
get_option(K, Default, [Nk | As]) ->
{As1, V} = get_option(K, Default, As),
{[Nk | As1], V};
get_option(_, Default, As) ->
{As, Default}.
maybe_process_opts(GDefs, As) ->
fun({C, Os}, no_command) ->
process_opts(atom_to_list(C), dict:from_list(GDefs ++ Os), As);
(C, no_command) ->
(maybe_process_opts(GDefs, As))({C, []}, no_command);
(_, {ok, Res}) ->
{ok, Res}
end.
get_flag(K, [K | As]) ->
{As, true};
get_flag(K, [Nk | As]) ->
{As1, V} = get_flag(K, As),
{[Nk | As1], V};
get_flag(_, []) ->
{[], false}.
process_opts(C, Defs, As0) ->
KVs0 = dict:map(fun (_, flag) -> false;
(_, {option, V}) -> V
end, Defs),
process_opts(Defs, C, As0, not_found, KVs0, []).
%% Consume flags/options until you find the correct command. If there are no
%% arguments or the first argument is not the command we're expecting, fail.
%% Arguments to this are: definitions, cmd we're looking for, args we
%% haven't parsed, whether we have found the cmd, options we've found,
%% plain args we've found.
process_opts(_Defs, C, [], found, KVs, Outs) ->
{ok, {list_to_atom(C), dict:to_list(KVs), lists:reverse(Outs)}};
process_opts(_Defs, _C, [], not_found, _, _) ->
no_command;
process_opts(Defs, C, [A | As], Found, KVs, Outs) ->
OptType = case dict:find(A, Defs) of
error -> none;
{ok, flag} -> flag;
{ok, {option, _}} -> option
end,
case {OptType, C, Found} of
{flag, _, _} -> process_opts(
Defs, C, As, Found, dict:store(A, true, KVs),
Outs);
{option, _, _} -> case As of
[] -> no_command;
[V | As1] -> process_opts(
Defs, C, As1, Found,
dict:store(A, V, KVs), Outs)
end;
{none, A, _} -> process_opts(Defs, C, As, found, KVs, Outs);
{none, _, found} -> process_opts(Defs, C, As, found, KVs, [A | Outs]);
{none, _, _} -> no_command
end.
now_ms() ->
timer:now_diff(now(), {0,0,0}) div 1000.
@ -909,13 +930,6 @@ receive_multi_call([{Mref, Pid} | MonitorPids], Good, Bad) ->
receive_multi_call(MonitorPids, Good, [{Pid, Reason} | Bad])
end.
%% the slower shutdown on windows required to flush stdout
quit(Status) ->
case os:type() of
{unix, _} -> halt(Status);
{win32, _} -> init:stop(Status)
end.
os_cmd(Command) ->
Exec = hd(string:tokens(Command, " ")),
case os:find_executable(Exec) of

View File

@ -20,7 +20,7 @@
-export([is_ssl/1, ssl_info/1, controlling_process/2, getstat/2,
recv/1, async_recv/3, port_command/2, getopts/2, setopts/2, send/2,
close/1, maybe_fast_close/1, sockname/1, peername/1, peercert/1,
connection_string/2]).
tune_buffer_size/1, connection_string/2]).
%%---------------------------------------------------------------------------
@ -69,6 +69,7 @@
-spec(peercert/1 ::
(socket())
-> 'nossl' | ok_val_or_error(rabbit_ssl:certificate())).
-spec(tune_buffer_size/1 :: (socket()) -> ok_or_any_error()).
-spec(connection_string/2 ::
(socket(), 'inbound' | 'outbound') -> ok_val_or_error(string())).
@ -159,6 +160,13 @@ peername(Sock) when is_port(Sock) -> inet:peername(Sock).
peercert(Sock) when ?IS_SSL(Sock) -> ssl:peercert(Sock#ssl_socket.ssl);
peercert(Sock) when is_port(Sock) -> nossl.
tune_buffer_size(Sock) ->
case getopts(Sock, [sndbuf, recbuf, buffer]) of
{ok, BufSizes} -> BufSz = lists:max([Sz || {_Opt, Sz} <- BufSizes]),
setopts(Sock, [{buffer, BufSz}]);
Err -> Err
end.
connection_string(Sock, Direction) ->
{From, To} = case Direction of
inbound -> {fun peername/1, fun sockname/1};

View File

@ -136,18 +136,13 @@ boot_ssl() ->
ok
end.
start() ->
{ok,_} = supervisor2:start_child(
rabbit_sup,
{rabbit_tcp_client_sup,
{rabbit_client_sup, start_link,
[{local, rabbit_tcp_client_sup},
{rabbit_connection_sup,start_link,[]}]},
transient, infinity, supervisor, [rabbit_client_sup]}),
ok.
start() -> rabbit_sup:start_supervisor_child(
rabbit_tcp_client_sup, rabbit_client_sup,
[{local, rabbit_tcp_client_sup},
{rabbit_connection_sup,start_link,[]}]).
ensure_ssl() ->
ok = rabbit_misc:start_applications([crypto, public_key, ssl]),
ok = app_utils:start_applications([crypto, public_key, ssl]),
{ok, SslOptsConfig} = application:get_env(rabbit, ssl_options),
% unknown_ca errors are silently ignored prior to R14B unless we

View File

@ -17,146 +17,57 @@
-module(rabbit_plugins).
-include("rabbit.hrl").
-export([start/0, stop/0, find_plugins/1, read_enabled_plugins/1,
lookup_plugins/2, calculate_required_plugins/2, plugin_names/1]).
-export([setup/0, active/0, read_enabled/1,
list/1, dependencies/3]).
-define(VERBOSE_OPT, "-v").
-define(MINIMAL_OPT, "-m").
-define(ENABLED_OPT, "-E").
-define(ENABLED_ALL_OPT, "-e").
-define(VERBOSE_DEF, {?VERBOSE_OPT, flag}).
-define(MINIMAL_DEF, {?MINIMAL_OPT, flag}).
-define(ENABLED_DEF, {?ENABLED_OPT, flag}).
-define(ENABLED_ALL_DEF, {?ENABLED_ALL_OPT, flag}).
-define(GLOBAL_DEFS, []).
-define(COMMANDS,
[{list, [?VERBOSE_DEF, ?MINIMAL_DEF, ?ENABLED_DEF, ?ENABLED_ALL_DEF]},
enable,
disable]).
%%----------------------------------------------------------------------------
-ifdef(use_specs).
-spec(start/0 :: () -> no_return()).
-spec(stop/0 :: () -> 'ok').
-spec(find_plugins/1 :: (file:filename()) -> [#plugin{}]).
-spec(read_enabled_plugins/1 :: (file:filename()) -> [atom()]).
-spec(lookup_plugins/2 :: ([atom()], [#plugin{}]) -> [#plugin{}]).
-spec(calculate_required_plugins/2 :: ([atom()], [#plugin{}]) -> [atom()]).
-spec(plugin_names/1 :: ([#plugin{}]) -> [atom()]).
-spec(setup/0 :: () -> [atom()]).
-spec(active/0 :: () -> [atom()]).
-spec(list/1 :: (string()) -> [#plugin{}]).
-spec(read_enabled/1 :: (file:filename()) -> [atom()]).
-spec(dependencies/3 ::
(boolean(), [atom()], [#plugin{}]) -> [atom()]).
-endif.
%%----------------------------------------------------------------------------
start() ->
{ok, [[PluginsFile|_]|_]} =
init:get_argument(enabled_plugins_file),
{ok, [[PluginsDir|_]|_]} = init:get_argument(plugins_dist_dir),
{[Command0 | Args], Opts} =
case rabbit_misc:get_options([{flag, ?VERBOSE_OPT},
{flag, ?MINIMAL_OPT},
{flag, ?ENABLED_OPT},
{flag, ?ENABLED_ALL_OPT}],
init:get_plain_arguments()) of
{[], _Opts} -> usage();
CmdArgsAndOpts -> CmdArgsAndOpts
end,
Command = list_to_atom(Command0),
PrintInvalidCommandError =
fun () ->
print_error("invalid command '~s'",
[string:join([atom_to_list(Command) | Args], " ")])
end,
%%
%% @doc Prepares the file system and installs all enabled plugins.
%%
setup() ->
{ok, PluginDir} = application:get_env(rabbit, plugins_dir),
{ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir),
{ok, EnabledPluginsFile} = application:get_env(rabbit,
enabled_plugins_file),
prepare_plugins(EnabledPluginsFile, PluginDir, ExpandDir),
[prepare_dir_plugin(PluginName) ||
PluginName <- filelib:wildcard(ExpandDir ++ "/*/ebin/*.app")].
case catch action(Command, Args, Opts, PluginsFile, PluginsDir) of
ok ->
rabbit_misc:quit(0);
{'EXIT', {function_clause, [{?MODULE, action, _} | _]}} ->
PrintInvalidCommandError(),
usage();
{'EXIT', {function_clause, [{?MODULE, action, _, _} | _]}} ->
PrintInvalidCommandError(),
usage();
{error, Reason} ->
print_error("~p", [Reason]),
rabbit_misc:quit(2);
{error_string, Reason} ->
print_error("~s", [Reason]),
rabbit_misc:quit(2);
Other ->
print_error("~p", [Other]),
rabbit_misc:quit(2)
end.
%% @doc Lists the plugins which are currently running.
active() ->
{ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir),
InstalledPlugins = [ P#plugin.name || P <- list(ExpandDir) ],
[App || {App, _, _} <- application:which_applications(),
lists:member(App, InstalledPlugins)].
stop() ->
ok.
print_error(Format, Args) ->
rabbit_misc:format_stderr("Error: " ++ Format ++ "~n", Args).
usage() ->
io:format("~s", [rabbit_plugins_usage:usage()]),
rabbit_misc:quit(1).
%%----------------------------------------------------------------------------
action(list, [], Opts, PluginsFile, PluginsDir) ->
action(list, [".*"], Opts, PluginsFile, PluginsDir);
action(list, [Pat], Opts, PluginsFile, PluginsDir) ->
format_plugins(Pat, Opts, PluginsFile, PluginsDir);
action(enable, ToEnable0, _Opts, PluginsFile, PluginsDir) ->
case ToEnable0 of
[] -> throw({error_string, "Not enough arguments for 'enable'"});
_ -> ok
end,
AllPlugins = find_plugins(PluginsDir),
Enabled = read_enabled_plugins(PluginsFile),
ImplicitlyEnabled = calculate_required_plugins(Enabled, AllPlugins),
ToEnable = [list_to_atom(Name) || Name <- ToEnable0],
Missing = ToEnable -- plugin_names(AllPlugins),
case Missing of
[] -> ok;
_ -> throw({error_string,
fmt_list("The following plugins could not be found:",
Missing)})
end,
NewEnabled = lists:usort(Enabled ++ ToEnable),
write_enabled_plugins(PluginsFile, NewEnabled),
NewImplicitlyEnabled = calculate_required_plugins(NewEnabled, AllPlugins),
maybe_warn_mochiweb(NewImplicitlyEnabled),
case NewEnabled -- ImplicitlyEnabled of
[] -> io:format("Plugin configuration unchanged.~n");
_ -> print_list("The following plugins have been enabled:",
NewImplicitlyEnabled -- ImplicitlyEnabled),
report_change()
end;
action(disable, ToDisable0, _Opts, PluginsFile, PluginsDir) ->
case ToDisable0 of
[] -> throw({error_string, "Not enough arguments for 'disable'"});
_ -> ok
end,
ToDisable = [list_to_atom(Name) || Name <- ToDisable0],
Enabled = read_enabled_plugins(PluginsFile),
AllPlugins = find_plugins(PluginsDir),
Missing = ToDisable -- plugin_names(AllPlugins),
case Missing of
[] -> ok;
_ -> print_list("Warning: the following plugins could not be found:",
Missing)
end,
ToDisableDeps = calculate_dependencies(true, ToDisable, AllPlugins),
NewEnabled = Enabled -- ToDisableDeps,
case length(Enabled) =:= length(NewEnabled) of
true -> io:format("Plugin configuration unchanged.~n");
false -> ImplicitlyEnabled =
calculate_required_plugins(Enabled, AllPlugins),
NewImplicitlyEnabled =
calculate_required_plugins(NewEnabled, AllPlugins),
print_list("The following plugins have been disabled:",
ImplicitlyEnabled -- NewImplicitlyEnabled),
write_enabled_plugins(PluginsFile, NewEnabled),
report_change()
end.
%%----------------------------------------------------------------------------
%% Get the #plugin{}s ready to be enabled.
find_plugins(PluginsDir) ->
%% @doc Get the list of plugins which are ready to be enabled.
list(PluginsDir) ->
EZs = [{ez, EZ} || EZ <- filelib:wildcard("*.ez", PluginsDir)],
FreeApps = [{app, App} ||
App <- filelib:wildcard("*/ebin/*.app", PluginsDir)],
@ -175,6 +86,91 @@ find_plugins(PluginsDir) ->
end,
Plugins.
%% @doc Read the list of enabled plugins from the supplied term file.
read_enabled(PluginsFile) ->
case rabbit_file:read_term_file(PluginsFile) of
{ok, [Plugins]} -> Plugins;
{ok, []} -> [];
{ok, [_|_]} -> throw({error, {malformed_enabled_plugins_file,
PluginsFile}});
{error, enoent} -> [];
{error, Reason} -> throw({error, {cannot_read_enabled_plugins_file,
PluginsFile, Reason}})
end.
%%
%% @doc Calculate the dependency graph from <i>Sources</i>.
%% When Reverse =:= true the bottom/leaf level applications are returned in
%% the resulting list, otherwise they're skipped.
%%
dependencies(Reverse, Sources, AllPlugins) ->
{ok, G} = rabbit_misc:build_acyclic_graph(
fun (App, _Deps) -> [{App, App}] end,
fun (App, Deps) -> [{App, Dep} || Dep <- Deps] end,
[{Name, Deps}
|| #plugin{name = Name, dependencies = Deps} <- AllPlugins]),
Dests = case Reverse of
false -> digraph_utils:reachable(Sources, G);
true -> digraph_utils:reaching(Sources, G)
end,
true = digraph:delete(G),
Dests.
%%----------------------------------------------------------------------------
prepare_plugins(EnabledPluginsFile, PluginsDistDir, DestDir) ->
AllPlugins = list(PluginsDistDir),
Enabled = read_enabled(EnabledPluginsFile),
ToUnpack = dependencies(false, Enabled, AllPlugins),
ToUnpackPlugins = lookup_plugins(ToUnpack, AllPlugins),
Missing = Enabled -- plugin_names(ToUnpackPlugins),
case Missing of
[] -> ok;
_ -> io:format("Warning: the following enabled plugins were "
"not found: ~p~n", [Missing])
end,
%% Eliminate the contents of the destination directory
case delete_recursively(DestDir) of
ok -> ok;
{error, E} -> rabbit_misc:quit("Could not delete dir ~s (~p)",
[DestDir, E])
end,
case filelib:ensure_dir(DestDir ++ "/") of
ok -> ok;
{error, E2} -> rabbit_misc:quit("Could not create dir ~s (~p)",
[DestDir, E2])
end,
[prepare_plugin(Plugin, DestDir) || Plugin <- ToUnpackPlugins].
prepare_dir_plugin(PluginAppDescFn) ->
%% Add the plugin ebin directory to the load path
PluginEBinDirN = filename:dirname(PluginAppDescFn),
code:add_path(PluginEBinDirN),
%% We want the second-last token
NameTokens = string:tokens(PluginAppDescFn,"/."),
PluginNameString = lists:nth(length(NameTokens) - 1, NameTokens),
list_to_atom(PluginNameString).
%%----------------------------------------------------------------------------
delete_recursively(Fn) ->
case rabbit_file:recursive_delete([Fn]) of
ok -> ok;
{error, {Path, E}} -> {error, {cannot_delete, Path, E}};
Error -> Error
end.
prepare_plugin(#plugin{type = ez, location = Location}, PluginDestDir) ->
zip:unzip(Location, [{cwd, PluginDestDir}]);
prepare_plugin(#plugin{type = dir, name = Name, location = Location},
PluginsDestDir) ->
rabbit_file:recursive_copy(Location,
filename:join([PluginsDestDir, Name])).
%% Get the #plugin{} from an .ez.
get_plugin_info(Base, {ez, EZ0}) ->
EZ = filename:join([Base, EZ0]),
@ -234,82 +230,6 @@ parse_binary(Bin) ->
Err -> {error, {invalid_app, Err}}
end.
%% Pretty print a list of plugins.
format_plugins(Pattern, Opts, PluginsFile, PluginsDir) ->
Verbose = proplists:get_bool(?VERBOSE_OPT, Opts),
Minimal = proplists:get_bool(?MINIMAL_OPT, Opts),
Format = case {Verbose, Minimal} of
{false, false} -> normal;
{true, false} -> verbose;
{false, true} -> minimal;
{true, true} -> throw({error_string,
"Cannot specify -m and -v together"})
end,
OnlyEnabled = proplists:get_bool(?ENABLED_OPT, Opts),
OnlyEnabledAll = proplists:get_bool(?ENABLED_ALL_OPT, Opts),
AvailablePlugins = find_plugins(PluginsDir),
EnabledExplicitly = read_enabled_plugins(PluginsFile),
EnabledImplicitly =
calculate_required_plugins(EnabledExplicitly, AvailablePlugins) --
EnabledExplicitly,
{ok, RE} = re:compile(Pattern),
Plugins = [ Plugin ||
Plugin = #plugin{name = Name} <- AvailablePlugins,
re:run(atom_to_list(Name), RE, [{capture, none}]) =:= match,
if OnlyEnabled -> lists:member(Name, EnabledExplicitly);
true -> true
end,
if OnlyEnabledAll ->
lists:member(Name, EnabledImplicitly) or
lists:member(Name, EnabledExplicitly);
true ->
true
end],
Plugins1 = usort_plugins(Plugins),
MaxWidth = lists:max([length(atom_to_list(Name)) ||
#plugin{name = Name} <- Plugins1] ++ [0]),
[format_plugin(P, EnabledExplicitly, EnabledImplicitly, Format,
MaxWidth) || P <- Plugins1],
ok.
format_plugin(#plugin{name = Name, version = Version,
description = Description, dependencies = Deps},
EnabledExplicitly, EnabledImplicitly, Format, MaxWidth) ->
Glyph = case {lists:member(Name, EnabledExplicitly),
lists:member(Name, EnabledImplicitly)} of
{true, false} -> "[E]";
{false, true} -> "[e]";
_ -> "[ ]"
end,
case Format of
minimal -> io:format("~s~n", [Name]);
normal -> io:format("~s ~-" ++ integer_to_list(MaxWidth) ++
"w ~s~n", [Glyph, Name, Version]);
verbose -> io:format("~s ~w~n", [Glyph, Name]),
io:format(" Version: \t~s~n", [Version]),
case Deps of
[] -> ok;
_ -> io:format(" Dependencies:\t~p~n", [Deps])
end,
io:format(" Description:\t~s~n", [Description]),
io:format("~n")
end.
print_list(Header, Plugins) ->
io:format(fmt_list(Header, Plugins)).
fmt_list(Header, Plugins) ->
lists:flatten(
[Header, $\n, [io_lib:format(" ~s~n", [P]) || P <- Plugins]]).
usort_plugins(Plugins) ->
lists:usort(fun plugins_cmp/2, Plugins).
plugins_cmp(#plugin{name = N1, version = V1},
#plugin{name = N2, version = V2}) ->
{N1, V1} =< {N2, V2}.
%% Filter out applications that can be loaded *right now*.
filter_applications(Applications) ->
[Application || Application <- Applications,
@ -332,72 +252,3 @@ plugin_names(Plugins) ->
%% Find plugins by name in a list of plugins.
lookup_plugins(Names, AllPlugins) ->
[P || P = #plugin{name = Name} <- AllPlugins, lists:member(Name, Names)].
%% Read the enabled plugin names from disk.
read_enabled_plugins(PluginsFile) ->
case rabbit_file:read_term_file(PluginsFile) of
{ok, [Plugins]} -> Plugins;
{ok, []} -> [];
{ok, [_|_]} -> throw({error, {malformed_enabled_plugins_file,
PluginsFile}});
{error, enoent} -> [];
{error, Reason} -> throw({error, {cannot_read_enabled_plugins_file,
PluginsFile, Reason}})
end.
%% Write the enabled plugin names on disk.
write_enabled_plugins(PluginsFile, Plugins) ->
case rabbit_file:write_term_file(PluginsFile, [Plugins]) of
ok -> ok;
{error, Reason} -> throw({error, {cannot_write_enabled_plugins_file,
PluginsFile, Reason}})
end.
calculate_required_plugins(Sources, AllPlugins) ->
calculate_dependencies(false, Sources, AllPlugins).
calculate_dependencies(Reverse, Sources, AllPlugins) ->
{ok, G} = rabbit_misc:build_acyclic_graph(
fun (App, _Deps) -> [{App, App}] end,
fun (App, Deps) -> [{App, Dep} || Dep <- Deps] end,
[{Name, Deps}
|| #plugin{name = Name, dependencies = Deps} <- AllPlugins]),
Dests = case Reverse of
false -> digraph_utils:reachable(Sources, G);
true -> digraph_utils:reaching(Sources, G)
end,
true = digraph:delete(G),
Dests.
maybe_warn_mochiweb(Enabled) ->
V = erlang:system_info(otp_release),
case lists:member(mochiweb, Enabled) andalso V < "R13B01" of
true ->
Stars = string:copies("*", 80),
io:format("~n~n~s~n"
" Warning: Mochiweb enabled and Erlang version ~s "
"detected.~n"
" Enabling plugins that depend on Mochiweb is not "
"supported on this Erlang~n"
" version. At least R13B01 is required.~n~n"
" RabbitMQ will not start successfully in this "
"configuration. You *must*~n"
" disable the Mochiweb plugin, or upgrade Erlang.~n"
"~s~n~n~n", [Stars, V, Stars]);
false ->
ok
end.
report_change() ->
io:format("Plugin configuration has changed. "
"Restart RabbitMQ for changes to take effect.~n"),
case os:type() of
{win32, _OsName} ->
io:format("If you have RabbitMQ running as a service then you must"
" reinstall by running~n rabbitmq-service.bat stop~n"
" rabbitmq-service.bat install~n"
" rabbitmq-service.bat start~n~n");
_ ->
ok
end.

273
src/rabbit_plugins_main.erl Normal file
View File

@ -0,0 +1,273 @@
%% The contents of this file are subject to the Mozilla Public License
%% Version 1.1 (the "License"); you may not use this file except in
%% compliance with the License. You may obtain a copy of the License
%% at http://www.mozilla.org/MPL/
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and
%% limitations under the License.
%%
%% The Original Code is RabbitMQ.
%%
%% The Initial Developer of the Original Code is VMware, Inc.
%% Copyright (c) 2011-2012 VMware, Inc. All rights reserved.
%%
-module(rabbit_plugins_main).
-include("rabbit.hrl").
-export([start/0, stop/0]).
-define(VERBOSE_OPT, "-v").
-define(MINIMAL_OPT, "-m").
-define(ENABLED_OPT, "-E").
-define(ENABLED_ALL_OPT, "-e").
-define(VERBOSE_DEF, {?VERBOSE_OPT, flag}).
-define(MINIMAL_DEF, {?MINIMAL_OPT, flag}).
-define(ENABLED_DEF, {?ENABLED_OPT, flag}).
-define(ENABLED_ALL_DEF, {?ENABLED_ALL_OPT, flag}).
-define(GLOBAL_DEFS, []).
-define(COMMANDS,
[{list, [?VERBOSE_DEF, ?MINIMAL_DEF, ?ENABLED_DEF, ?ENABLED_ALL_DEF]},
enable,
disable]).
%%----------------------------------------------------------------------------
-ifdef(use_specs).
-spec(start/0 :: () -> no_return()).
-spec(stop/0 :: () -> 'ok').
-spec(usage/0 :: () -> no_return()).
-endif.
%%----------------------------------------------------------------------------
start() ->
{ok, [[PluginsFile|_]|_]} =
init:get_argument(enabled_plugins_file),
{ok, [[PluginsDir|_]|_]} = init:get_argument(plugins_dist_dir),
{Command, Opts, Args} =
case rabbit_misc:parse_arguments(?COMMANDS, ?GLOBAL_DEFS,
init:get_plain_arguments())
of
{ok, Res} -> Res;
no_command -> print_error("could not recognise command", []),
usage()
end,
PrintInvalidCommandError =
fun () ->
print_error("invalid command '~s'",
[string:join([atom_to_list(Command) | Args], " ")])
end,
case catch action(Command, Args, Opts, PluginsFile, PluginsDir) of
ok ->
rabbit_misc:quit(0);
{'EXIT', {function_clause, [{?MODULE, action, _} | _]}} ->
PrintInvalidCommandError(),
usage();
{'EXIT', {function_clause, [{?MODULE, action, _, _} | _]}} ->
PrintInvalidCommandError(),
usage();
{error, Reason} ->
print_error("~p", [Reason]),
rabbit_misc:quit(2);
{error_string, Reason} ->
print_error("~s", [Reason]),
rabbit_misc:quit(2);
Other ->
print_error("~p", [Other]),
rabbit_misc:quit(2)
end.
stop() ->
ok.
%%----------------------------------------------------------------------------
action(list, [], Opts, PluginsFile, PluginsDir) ->
action(list, [".*"], Opts, PluginsFile, PluginsDir);
action(list, [Pat], Opts, PluginsFile, PluginsDir) ->
format_plugins(Pat, Opts, PluginsFile, PluginsDir);
action(enable, ToEnable0, _Opts, PluginsFile, PluginsDir) ->
case ToEnable0 of
[] -> throw({error_string, "Not enough arguments for 'enable'"});
_ -> ok
end,
AllPlugins = rabbit_plugins:list(PluginsDir),
Enabled = rabbit_plugins:read_enabled(PluginsFile),
ImplicitlyEnabled = rabbit_plugins:dependencies(false,
Enabled, AllPlugins),
ToEnable = [list_to_atom(Name) || Name <- ToEnable0],
Missing = ToEnable -- plugin_names(AllPlugins),
case Missing of
[] -> ok;
_ -> throw({error_string,
fmt_list("The following plugins could not be found:",
Missing)})
end,
NewEnabled = lists:usort(Enabled ++ ToEnable),
write_enabled_plugins(PluginsFile, NewEnabled),
NewImplicitlyEnabled = rabbit_plugins:dependencies(false,
NewEnabled, AllPlugins),
maybe_warn_mochiweb(NewImplicitlyEnabled),
case NewEnabled -- ImplicitlyEnabled of
[] -> io:format("Plugin configuration unchanged.~n");
_ -> print_list("The following plugins have been enabled:",
NewImplicitlyEnabled -- ImplicitlyEnabled),
report_change()
end;
action(disable, ToDisable0, _Opts, PluginsFile, PluginsDir) ->
case ToDisable0 of
[] -> throw({error_string, "Not enough arguments for 'disable'"});
_ -> ok
end,
ToDisable = [list_to_atom(Name) || Name <- ToDisable0],
Enabled = rabbit_plugins:read_enabled(PluginsFile),
AllPlugins = rabbit_plugins:list(PluginsDir),
Missing = ToDisable -- plugin_names(AllPlugins),
case Missing of
[] -> ok;
_ -> print_list("Warning: the following plugins could not be found:",
Missing)
end,
ToDisableDeps = rabbit_plugins:dependencies(true, ToDisable, AllPlugins),
NewEnabled = Enabled -- ToDisableDeps,
case length(Enabled) =:= length(NewEnabled) of
true -> io:format("Plugin configuration unchanged.~n");
false -> ImplicitlyEnabled =
rabbit_plugins:dependencies(false, Enabled, AllPlugins),
NewImplicitlyEnabled =
rabbit_plugins:dependencies(false,
NewEnabled, AllPlugins),
print_list("The following plugins have been disabled:",
ImplicitlyEnabled -- NewImplicitlyEnabled),
write_enabled_plugins(PluginsFile, NewEnabled),
report_change()
end.
%%----------------------------------------------------------------------------
print_error(Format, Args) ->
rabbit_misc:format_stderr("Error: " ++ Format ++ "~n", Args).
usage() ->
io:format("~s", [rabbit_plugins_usage:usage()]),
rabbit_misc:quit(1).
%% Pretty print a list of plugins.
format_plugins(Pattern, Opts, PluginsFile, PluginsDir) ->
Verbose = proplists:get_bool(?VERBOSE_OPT, Opts),
Minimal = proplists:get_bool(?MINIMAL_OPT, Opts),
Format = case {Verbose, Minimal} of
{false, false} -> normal;
{true, false} -> verbose;
{false, true} -> minimal;
{true, true} -> throw({error_string,
"Cannot specify -m and -v together"})
end,
OnlyEnabled = proplists:get_bool(?ENABLED_OPT, Opts),
OnlyEnabledAll = proplists:get_bool(?ENABLED_ALL_OPT, Opts),
AvailablePlugins = rabbit_plugins:list(PluginsDir),
EnabledExplicitly = rabbit_plugins:read_enabled(PluginsFile),
EnabledImplicitly =
rabbit_plugins:dependencies(false, EnabledExplicitly,
AvailablePlugins) -- EnabledExplicitly,
{ok, RE} = re:compile(Pattern),
Plugins = [ Plugin ||
Plugin = #plugin{name = Name} <- AvailablePlugins,
re:run(atom_to_list(Name), RE, [{capture, none}]) =:= match,
if OnlyEnabled -> lists:member(Name, EnabledExplicitly);
OnlyEnabledAll -> (lists:member(Name,
EnabledExplicitly) or
lists:member(Name, EnabledImplicitly));
true -> true
end],
Plugins1 = usort_plugins(Plugins),
MaxWidth = lists:max([length(atom_to_list(Name)) ||
#plugin{name = Name} <- Plugins1] ++ [0]),
[format_plugin(P, EnabledExplicitly, EnabledImplicitly, Format,
MaxWidth) || P <- Plugins1],
ok.
format_plugin(#plugin{name = Name, version = Version,
description = Description, dependencies = Deps},
EnabledExplicitly, EnabledImplicitly, Format, MaxWidth) ->
Glyph = case {lists:member(Name, EnabledExplicitly),
lists:member(Name, EnabledImplicitly)} of
{true, false} -> "[E]";
{false, true} -> "[e]";
_ -> "[ ]"
end,
case Format of
minimal -> io:format("~s~n", [Name]);
normal -> io:format("~s ~-" ++ integer_to_list(MaxWidth) ++
"w ~s~n", [Glyph, Name, Version]);
verbose -> io:format("~s ~w~n", [Glyph, Name]),
io:format(" Version: \t~s~n", [Version]),
case Deps of
[] -> ok;
_ -> io:format(" Dependencies:\t~p~n", [Deps])
end,
io:format(" Description:\t~s~n", [Description]),
io:format("~n")
end.
print_list(Header, Plugins) ->
io:format(fmt_list(Header, Plugins)).
fmt_list(Header, Plugins) ->
lists:flatten(
[Header, $\n, [io_lib:format(" ~s~n", [P]) || P <- Plugins]]).
usort_plugins(Plugins) ->
lists:usort(fun plugins_cmp/2, Plugins).
plugins_cmp(#plugin{name = N1, version = V1},
#plugin{name = N2, version = V2}) ->
{N1, V1} =< {N2, V2}.
%% Return the names of the given plugins.
plugin_names(Plugins) ->
[Name || #plugin{name = Name} <- Plugins].
%% Write the enabled plugin names on disk.
write_enabled_plugins(PluginsFile, Plugins) ->
case rabbit_file:write_term_file(PluginsFile, [Plugins]) of
ok -> ok;
{error, Reason} -> throw({error, {cannot_write_enabled_plugins_file,
PluginsFile, Reason}})
end.
maybe_warn_mochiweb(Enabled) ->
V = erlang:system_info(otp_release),
case lists:member(mochiweb, Enabled) andalso V < "R13B01" of
true ->
Stars = string:copies("*", 80),
io:format("~n~n~s~n"
" Warning: Mochiweb enabled and Erlang version ~s "
"detected.~n"
" Enabling plugins that depend on Mochiweb is not "
"supported on this Erlang~n"
" version. At least R13B01 is required.~n~n"
" RabbitMQ will not start successfully in this "
"configuration. You *must*~n"
" disable the Mochiweb plugin, or upgrade Erlang.~n"
"~s~n~n~n", [Stars, V, Stars]);
false ->
ok
end.
report_change() ->
io:format("Plugin configuration has changed. "
"Restart RabbitMQ for changes to take effect.~n").

View File

@ -31,212 +31,21 @@
-spec(start/0 :: () -> no_return()).
-spec(stop/0 :: () -> 'ok').
%% Shut dialyzer up
-spec(terminate/1 :: (string()) -> no_return()).
-spec(terminate/2 :: (string(), [any()]) -> no_return()).
-endif.
%%----------------------------------------------------------------------------
start() ->
io:format("Activating RabbitMQ plugins ...~n"),
%% Determine our various directories
[EnabledPluginsFile, PluginsDistDir, UnpackedPluginDir, NodeStr] =
init:get_plain_arguments(),
RootName = UnpackedPluginDir ++ "/rabbit",
prepare_plugins(EnabledPluginsFile, PluginsDistDir, UnpackedPluginDir),
%% Build a list of required apps based on the fixed set, and any plugins
PluginApps = find_plugins(UnpackedPluginDir),
RequiredApps = ?BaseApps ++ PluginApps,
%% Build the entire set of dependencies - this will load the
%% applications along the way
AllApps = case catch sets:to_list(expand_dependencies(RequiredApps)) of
{failed_to_load_app, App, Err} ->
terminate("failed to load application ~s:~n~p",
[App, Err]);
AppList ->
AppList
end,
AppVersions = [determine_version(App) || App <- AllApps],
RabbitVersion = proplists:get_value(rabbit, AppVersions),
%% Build the overall release descriptor
RDesc = {release,
{"rabbit", RabbitVersion},
{erts, erlang:system_info(version)},
AppVersions},
%% Write it out to $RABBITMQ_PLUGINS_EXPAND_DIR/rabbit.rel
rabbit_file:write_file(RootName ++ ".rel", io_lib:format("~p.~n", [RDesc])),
%% We exclude mochiweb due to its optional use of fdsrv.
XRefExclude = [mochiweb],
%% Compile the script
ScriptFile = RootName ++ ".script",
case systools:make_script(RootName, [local, silent,
{exref, AllApps -- XRefExclude}]) of
{ok, Module, Warnings} ->
%% This gets lots of spurious no-source warnings when we
%% have .ez files, so we want to supress them to prevent
%% hiding real issues. On Ubuntu, we also get warnings
%% about kernel/stdlib sources being out of date, which we
%% also ignore for the same reason.
WarningStr = Module:format_warning(
[W || W <- Warnings,
case W of
{warning, {source_not_found, _}} -> false;
{warning, {obj_out_of_date, {_,_,WApp,_,_}}}
when WApp == mnesia;
WApp == stdlib;
WApp == kernel;
WApp == sasl;
WApp == crypto;
WApp == os_mon -> false;
_ -> true
end]),
case length(WarningStr) of
0 -> ok;
_ -> S = string:copies("*", 80),
io:format("~n~s~n~s~s~n~n", [S, WarningStr, S])
end,
ok;
{error, Module, Error} ->
terminate("generation of boot script file ~s failed:~n~s",
[ScriptFile, Module:format_error(Error)])
end,
case post_process_script(ScriptFile) of
ok -> ok;
{error, Reason} ->
terminate("post processing of boot script file ~s failed:~n~w",
[ScriptFile, Reason])
end,
case systools:script2boot(RootName) of
ok -> ok;
error -> terminate("failed to compile boot script file ~s",
[ScriptFile])
end,
io:format("~w plugins activated:~n", [length(PluginApps)]),
[io:format("* ~s-~s~n", [App, proplists:get_value(App, AppVersions)])
|| App <- PluginApps],
io:nl(),
[NodeStr] = init:get_plain_arguments(),
ok = duplicate_node_check(NodeStr),
terminate(0),
rabbit_misc:quit(0),
ok.
stop() ->
ok.
determine_version(App) ->
application:load(App),
{ok, Vsn} = application:get_key(App, vsn),
{App, Vsn}.
delete_recursively(Fn) ->
case rabbit_file:recursive_delete([Fn]) of
ok -> ok;
{error, {Path, E}} -> {error, {cannot_delete, Path, E}};
Error -> Error
end.
prepare_plugins(EnabledPluginsFile, PluginsDistDir, DestDir) ->
AllPlugins = rabbit_plugins:find_plugins(PluginsDistDir),
Enabled = rabbit_plugins:read_enabled_plugins(EnabledPluginsFile),
ToUnpack = rabbit_plugins:calculate_required_plugins(Enabled, AllPlugins),
ToUnpackPlugins = rabbit_plugins:lookup_plugins(ToUnpack, AllPlugins),
Missing = Enabled -- rabbit_plugins:plugin_names(ToUnpackPlugins),
case Missing of
[] -> ok;
_ -> io:format("Warning: the following enabled plugins were "
"not found: ~p~n", [Missing])
end,
%% Eliminate the contents of the destination directory
case delete_recursively(DestDir) of
ok -> ok;
{error, E} -> terminate("Could not delete dir ~s (~p)", [DestDir, E])
end,
case filelib:ensure_dir(DestDir ++ "/") of
ok -> ok;
{error, E2} -> terminate("Could not create dir ~s (~p)", [DestDir, E2])
end,
[prepare_plugin(Plugin, DestDir) || Plugin <- ToUnpackPlugins].
prepare_plugin(#plugin{type = ez, location = Location}, PluginDestDir) ->
zip:unzip(Location, [{cwd, PluginDestDir}]);
prepare_plugin(#plugin{type = dir, name = Name, location = Location},
PluginsDestDir) ->
rabbit_file:recursive_copy(Location,
filename:join([PluginsDestDir, Name])).
find_plugins(PluginDir) ->
[prepare_dir_plugin(PluginName) ||
PluginName <- filelib:wildcard(PluginDir ++ "/*/ebin/*.app")].
prepare_dir_plugin(PluginAppDescFn) ->
%% Add the plugin ebin directory to the load path
PluginEBinDirN = filename:dirname(PluginAppDescFn),
code:add_path(PluginEBinDirN),
%% We want the second-last token
NameTokens = string:tokens(PluginAppDescFn,"/."),
PluginNameString = lists:nth(length(NameTokens) - 1, NameTokens),
list_to_atom(PluginNameString).
expand_dependencies(Pending) ->
expand_dependencies(sets:new(), Pending).
expand_dependencies(Current, []) ->
Current;
expand_dependencies(Current, [Next|Rest]) ->
case sets:is_element(Next, Current) of
true ->
expand_dependencies(Current, Rest);
false ->
case application:load(Next) of
ok ->
ok;
{error, {already_loaded, _}} ->
ok;
{error, Reason} ->
throw({failed_to_load_app, Next, Reason})
end,
{ok, Required} = application:get_key(Next, applications),
Unique = [A || A <- Required, not(sets:is_element(A, Current))],
expand_dependencies(sets:add_element(Next, Current), Rest ++ Unique)
end.
post_process_script(ScriptFile) ->
case file:consult(ScriptFile) of
{ok, [{script, Name, Entries}]} ->
NewEntries = lists:flatmap(fun process_entry/1, Entries),
case file:open(ScriptFile, [write]) of
{ok, Fd} ->
io:format(Fd, "%% script generated at ~w ~w~n~p.~n",
[date(), time(), {script, Name, NewEntries}]),
file:close(Fd),
ok;
{error, OReason} ->
{error, {failed_to_open_script_file_for_writing, OReason}}
end;
{error, Reason} ->
{error, {failed_to_load_script, Reason}}
end.
process_entry(Entry = {apply,{application,start_boot,[mnesia,permanent]}}) ->
[{apply,{rabbit,maybe_hipe_compile,[]}},
{apply,{rabbit,prepare,[]}}, Entry];
process_entry(Entry) ->
[Entry].
%%----------------------------------------------------------------------------
%% Check whether a node with the same name is already running
duplicate_node_check([]) ->
@ -252,11 +61,11 @@ duplicate_node_check(NodeStr) ->
"already running on ~p~n",
[NodeName, NodeHost]),
io:format(rabbit_nodes:diagnostics([Node]) ++ "~n"),
terminate(?ERROR_CODE);
rabbit_misc:quit(?ERROR_CODE);
false -> ok
end;
{error, EpmdReason} ->
terminate("epmd error for host ~p: ~p (~s)~n",
rabbit_misc:quit("epmd error for host ~p: ~p (~s)~n",
[NodeHost, EpmdReason,
case EpmdReason of
address -> "unable to establish tcp connection";
@ -264,16 +73,3 @@ duplicate_node_check(NodeStr) ->
_ -> inet:format_error(EpmdReason)
end])
end.
terminate(Fmt, Args) ->
io:format("ERROR: " ++ Fmt ++ "~n", Args),
terminate(?ERROR_CODE).
terminate(Status) ->
case os:type() of
{unix, _} -> halt(Status);
{win32, _} -> init:stop(Status),
receive
after infinity -> ok
end
end.

View File

@ -25,7 +25,7 @@
-export([init/4, mainloop/2]).
-export([conserve_resources/2, server_properties/1]).
-export([conserve_resources/3, server_properties/1]).
-define(HANDSHAKE_TIMEOUT, 10).
-define(NORMAL_TIMEOUT, 3).
@ -71,7 +71,7 @@
-spec(info/2 :: (pid(), rabbit_types:info_keys()) -> rabbit_types:infos()).
-spec(force_event_refresh/1 :: (pid()) -> 'ok').
-spec(shutdown/2 :: (pid(), string()) -> 'ok').
-spec(conserve_resources/2 :: (pid(), boolean()) -> 'ok').
-spec(conserve_resources/3 :: (pid(), atom(), boolean()) -> 'ok').
-spec(server_properties/1 :: (rabbit_types:protocol()) ->
rabbit_framing:amqp_table()).
@ -133,7 +133,7 @@ info(Pid, Items) ->
force_event_refresh(Pid) ->
gen_server:cast(Pid, force_event_refresh).
conserve_resources(Pid, Conserve) ->
conserve_resources(Pid, _Source, Conserve) ->
Pid ! {conserve_resources, Conserve},
ok.
@ -222,14 +222,7 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb,
last_blocked_by = none,
last_blocked_at = never},
try
BufSizes = inet_op(fun () ->
rabbit_net:getopts(
ClientSock, [sndbuf, recbuf, buffer])
end),
BufSz = lists:max([Sz || {_Opt, Sz} <- BufSizes]),
ok = inet_op(fun () ->
rabbit_net:setopts(ClientSock, [{buffer, BufSz}])
end),
ok = inet_op(fun () -> rabbit_net:tune_buffer_size(ClientSock) end),
recvloop(Deb, switch_callback(rabbit_event:init_stats_timer(
State, #v1.stats_timer),
handshake, 8)),

View File

@ -19,6 +19,8 @@
-behaviour(supervisor).
-export([start_link/0, start_child/1, start_child/2, start_child/3,
start_supervisor_child/1, start_supervisor_child/2,
start_supervisor_child/3,
start_restartable_child/1, start_restartable_child/2, stop_child/1]).
-export([init/1]).
@ -33,7 +35,11 @@
-spec(start_link/0 :: () -> rabbit_types:ok_pid_or_error()).
-spec(start_child/1 :: (atom()) -> 'ok').
-spec(start_child/2 :: (atom(), [any()]) -> 'ok').
-spec(start_child/3 :: (atom(), atom(), [any()]) -> 'ok').
-spec(start_supervisor_child/1 :: (atom()) -> 'ok').
-spec(start_supervisor_child/2 :: (atom(), [any()]) -> 'ok').
-spec(start_supervisor_child/3 :: (atom(), atom(), [any()]) -> 'ok').
-spec(start_restartable_child/1 :: (atom()) -> 'ok').
-spec(start_restartable_child/2 :: (atom(), [any()]) -> 'ok').
-spec(stop_child/1 :: (atom()) -> rabbit_types:ok_or_error(any())).
@ -42,22 +48,29 @@
%%----------------------------------------------------------------------------
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
start_link() -> supervisor:start_link({local, ?SERVER}, ?MODULE, []).
start_child(Mod) ->
start_child(Mod, []).
start_child(Mod) -> start_child(Mod, []).
start_child(Mod, Args) ->
start_child(Mod, Mod, Args).
start_child(Mod, Args) -> start_child(Mod, Mod, Args).
start_child(ChildId, Mod, Args) ->
child_reply(supervisor:start_child(?SERVER,
{ChildId, {Mod, start_link, Args},
transient, ?MAX_WAIT, worker, [Mod]})).
child_reply(supervisor:start_child(
?SERVER,
{ChildId, {Mod, start_link, Args},
transient, ?MAX_WAIT, worker, [Mod]})).
start_restartable_child(Mod) ->
start_restartable_child(Mod, []).
start_supervisor_child(Mod) -> start_supervisor_child(Mod, []).
start_supervisor_child(Mod, Args) -> start_supervisor_child(Mod, Mod, Args).
start_supervisor_child(ChildId, Mod, Args) ->
child_reply(supervisor:start_child(
?SERVER,
{ChildId, {Mod, start_link, Args},
transient, infinity, supervisor, [Mod]})).
start_restartable_child(Mod) -> start_restartable_child(Mod, []).
start_restartable_child(Mod, Args) ->
Name = list_to_atom(atom_to_list(Mod) ++ "_sup"),
@ -73,8 +86,7 @@ stop_child(ChildId) ->
E -> E
end.
init([]) ->
{ok, {{one_for_all, 0, 1}, []}}.
init([]) -> {ok, {{one_for_all, 0, 1}, []}}.
%%----------------------------------------------------------------------------

View File

@ -50,7 +50,7 @@ all_tests() ->
passed = test_app_management(),
passed = test_log_management_during_startup(),
passed = test_statistics(),
passed = test_option_parser(),
passed = test_arguments_parser(),
passed = test_cluster_management(),
passed = test_user_management(),
passed = test_runtime_parameters(),
@ -801,27 +801,57 @@ test_log_management_during_startup() ->
ok = control_action(start_app, []),
passed.
test_option_parser() ->
%% command and arguments should just pass through
ok = check_get_options({["mock_command", "arg1", "arg2"], []},
[], ["mock_command", "arg1", "arg2"]),
test_arguments_parser() ->
GlobalOpts1 = [{"-f1", flag}, {"-o1", {option, "foo"}}],
Commands1 = [command1, {command2, [{"-f2", flag}, {"-o2", {option, "bar"}}]}],
%% get flags
ok = check_get_options(
{["mock_command", "arg1"], [{"-f", true}, {"-f2", false}]},
[{flag, "-f"}, {flag, "-f2"}], ["mock_command", "arg1", "-f"]),
GetOptions =
fun (Args) ->
rabbit_misc:parse_arguments(Commands1, GlobalOpts1, Args)
end,
%% get options
ok = check_get_options(
{["mock_command"], [{"-foo", "bar"}, {"-baz", "notbaz"}]},
[{option, "-foo", "notfoo"}, {option, "-baz", "notbaz"}],
["mock_command", "-foo", "bar"]),
check_parse_arguments(no_command, GetOptions, []),
check_parse_arguments(no_command, GetOptions, ["foo", "bar"]),
check_parse_arguments(
{ok, {command1, [{"-f1", false}, {"-o1", "foo"}], []}},
GetOptions, ["command1"]),
check_parse_arguments(
{ok, {command1, [{"-f1", false}, {"-o1", "blah"}], []}},
GetOptions, ["command1", "-o1", "blah"]),
check_parse_arguments(
{ok, {command1, [{"-f1", true}, {"-o1", "foo"}], []}},
GetOptions, ["command1", "-f1"]),
check_parse_arguments(
{ok, {command1, [{"-f1", false}, {"-o1", "blah"}], []}},
GetOptions, ["-o1", "blah", "command1"]),
check_parse_arguments(
{ok, {command1, [{"-f1", false}, {"-o1", "blah"}], ["quux"]}},
GetOptions, ["-o1", "blah", "command1", "quux"]),
check_parse_arguments(
{ok, {command1, [{"-f1", true}, {"-o1", "blah"}], ["quux", "baz"]}},
GetOptions, ["command1", "quux", "-f1", "-o1", "blah", "baz"]),
%% For duplicate flags, the last one counts
check_parse_arguments(
{ok, {command1, [{"-f1", false}, {"-o1", "second"}], []}},
GetOptions, ["-o1", "first", "command1", "-o1", "second"]),
%% If the flag "eats" the command, the command won't be recognised
check_parse_arguments(no_command, GetOptions,
["-o1", "command1", "quux"]),
%% If a flag eats another flag, the eaten flag won't be recognised
check_parse_arguments(
{ok, {command1, [{"-f1", false}, {"-o1", "-f1"}], []}},
GetOptions, ["command1", "-o1", "-f1"]),
%% shuffled and interleaved arguments and options
ok = check_get_options(
{["a1", "a2", "a3"], [{"-o1", "hello"}, {"-o2", "noto2"}, {"-f", true}]},
[{option, "-o1", "noto1"}, {flag, "-f"}, {option, "-o2", "noto2"}],
["-f", "a1", "-o1", "hello", "a2", "a3"]),
%% Now for some command-specific flags...
check_parse_arguments(
{ok, {command2, [{"-f1", false}, {"-f2", false},
{"-o1", "foo"}, {"-o2", "bar"}], []}},
GetOptions, ["command2"]),
check_parse_arguments(
{ok, {command2, [{"-f1", false}, {"-f2", true},
{"-o1", "baz"}, {"-o2", "bar"}], ["quux", "foo"]}},
GetOptions, ["-f2", "command2", "quux", "-o1", "baz", "foo"]),
passed.
@ -1573,7 +1603,7 @@ control_action(Command, Args, NewOpts) ->
expand_options(default_options(), NewOpts)).
control_action(Command, Node, Args, Opts) ->
case catch rabbit_control:action(
case catch rabbit_control_main:action(
Command, Node, Args, Opts,
fun (Format, Args1) ->
io:format(Format ++ " ...~n", Args1)
@ -1605,10 +1635,13 @@ expand_options(As, Bs) ->
end
end, Bs, As).
check_get_options({ExpArgs, ExpOpts}, Defs, Args) ->
{ExpArgs, ResOpts} = rabbit_misc:get_options(Defs, Args),
true = lists:sort(ExpOpts) == lists:sort(ResOpts), % don't care about the order
ok.
check_parse_arguments(ExpRes, Fun, As) ->
SortRes =
fun (no_command) -> no_command;
({ok, {C, KVs, As1}}) -> {ok, {C, lists:sort(KVs), As1}}
end,
true = SortRes(ExpRes) =:= SortRes(Fun(As)).
empty_files(Files) ->
[case file:read_file_info(File) of

View File

@ -323,7 +323,6 @@
-type(timestamp() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}).
-type(seq_id() :: non_neg_integer()).
-type(ack() :: seq_id()).
-type(rates() :: #rates { egress :: {timestamp(), non_neg_integer()},
ingress :: {timestamp(), non_neg_integer()},
@ -335,6 +334,13 @@
count :: non_neg_integer(),
end_seq_id :: non_neg_integer() }).
%% The compiler (rightfully) complains that ack() and state() are
%% unused. For this reason we duplicate a -spec from
%% rabbit_backing_queue with the only intent being to remove
%% warnings. The problem here is that we can't parameterise the BQ
%% behaviour by these two types as we would like to. We still leave
%% these here for documentation purposes.
-type(ack() :: seq_id()).
-type(state() :: #vqstate {
q1 :: ?QUEUE:?QUEUE(),
q2 :: ?QUEUE:?QUEUE(),
@ -368,6 +374,8 @@
ack_out_counter :: non_neg_integer(),
ack_in_counter :: non_neg_integer(),
ack_rates :: rates() }).
%% Duplicated from rabbit_backing_queue
-spec(ack/2 :: ([ack()], state()) -> {[rabbit_guid:guid()], state()}).
-spec(multiple_routing_keys/0 :: () -> 'ok').
@ -1458,16 +1466,14 @@ reduce_memory_use(AlphaBetaFun, BetaDeltaFun, AckFun,
%% determined based on which is growing faster. Whichever
%% comes second may very well get a quota of 0 if the
%% first manages to push out the max number of messages.
S1 -> {_, State2} =
lists:foldl(fun (ReduceFun, {QuotaN, StateN}) ->
ReduceFun(QuotaN, StateN)
end,
{S1, State},
case (AvgAckIngress - AvgAckEgress) >
(AvgIngress - AvgEgress) of
true -> [AckFun, AlphaBetaFun];
false -> [AlphaBetaFun, AckFun]
end),
S1 -> Funs = case ((AvgAckIngress - AvgAckEgress) >
(AvgIngress - AvgEgress)) of
true -> [AckFun, AlphaBetaFun];
false -> [AlphaBetaFun, AckFun]
end,
{_, State2} = lists:foldl(fun (ReduceFun, {QuotaN, StateN}) ->
ReduceFun(QuotaN, StateN)
end, {S1, State}, Funs),
{true, State2}
end,