summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Watson <tim@rabbitmq.com>2012-05-16 12:02:03 +0100
committerTim Watson <tim@rabbitmq.com>2012-05-16 12:02:03 +0100
commit09b1ff83a1cef206551b2308b0d9eece7bed106b (patch)
tree0b89b97614cf114103eb6f7b878a50fff8b94686
parentc0ae7544adfa9ee332da7b2117c28ec764b5ec46 (diff)
downloadrabbitmq-server-09b1ff83a1cef206551b2308b0d9eece7bed106b.tar.gz
split rabbit_plugins into client and server side modules
-rwxr-xr-xscripts/rabbitmq-plugins2
-rwxr-xr-xscripts/rabbitmq-plugins.bat2
-rw-r--r--src/rabbit_plugins.erl344
-rw-r--r--src/rabbit_plugins_main.erl271
4 files changed, 334 insertions, 285 deletions
diff --git a/scripts/rabbitmq-plugins b/scripts/rabbitmq-plugins
index 14a18d57..97c74791 100755
--- a/scripts/rabbitmq-plugins
+++ b/scripts/rabbitmq-plugins
@@ -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 "$@"
diff --git a/scripts/rabbitmq-plugins.bat b/scripts/rabbitmq-plugins.bat
index 66a900a1..bc198393 100755
--- a/scripts/rabbitmq-plugins.bat
+++ b/scripts/rabbitmq-plugins.bat
@@ -45,7 +45,7 @@ if "!RABBITMQ_ENABLED_PLUGINS_FILE!"=="" (
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
diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl
index 8a8e7052..06773cdb 100644
--- a/src/rabbit_plugins.erl
+++ b/src/rabbit_plugins.erl
@@ -17,69 +17,27 @@
-module(rabbit_plugins).
-include("rabbit.hrl").
--export([start/0, stop/0, prepare_plugins/0, active_plugins/0]).
-
--define(VERBOSE_OPT, "-v").
--define(MINIMAL_OPT, "-m").
--define(ENABLED_OPT, "-E").
--define(ENABLED_ALL_OPT, "-e").
+-export([prepare_plugins/0, active_plugins/0, read_enabled_plugins/1,
+ find_plugins/1, calculate_plugin_dependencies/3]).
%%----------------------------------------------------------------------------
-ifdef(use_specs).
--spec(start/0 :: () -> no_return()).
--spec(stop/0 :: () -> 'ok').
-spec(prepare_plugins/0 :: () -> [atom()]).
-spec(active_plugins/0 :: () -> [atom()]).
+-spec(find_plugins/1 :: (string()) -> [#plugin{}]).
+-spec(read_enabled_plugins/1 :: (file:filename()) -> [atom()]).
+-spec(calculate_plugin_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,
-
- 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.
-
+%%
+%% @doc Prepares the file system and installs all enabled plugins.
+%%
prepare_plugins() ->
{ok, PluginDir} = application:get_env(rabbit, plugins_dir),
{ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir),
@@ -89,18 +47,69 @@ prepare_plugins() ->
[prepare_dir_plugin(PluginName) ||
PluginName <- filelib:wildcard(ExpandDir ++ "/*/ebin/*.app")].
+%% @doc Lists the plugins which are currently running.
active_plugins() ->
{ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir),
InstalledPlugins = [ P#plugin.name || P <- find_plugins(ExpandDir) ],
[App || {App, _, _} <- application:which_applications(),
lists:member(App, InstalledPlugins)].
+%% @doc Get the list of plugins which are ready to be enabled.
+find_plugins(PluginsDir) ->
+ EZs = [{ez, EZ} || EZ <- filelib:wildcard("*.ez", PluginsDir)],
+ FreeApps = [{app, App} ||
+ App <- filelib:wildcard("*/ebin/*.app", PluginsDir)],
+ {Plugins, Problems} =
+ lists:foldl(fun ({error, EZ, Reason}, {Plugins1, Problems1}) ->
+ {Plugins1, [{EZ, Reason} | Problems1]};
+ (Plugin = #plugin{}, {Plugins1, Problems1}) ->
+ {[Plugin|Plugins1], Problems1}
+ end, {[], []},
+ [get_plugin_info(PluginsDir, Plug) ||
+ Plug <- EZs ++ FreeApps]),
+ case Problems of
+ [] -> ok;
+ _ -> io:format("Warning: Problem reading some plugins: ~p~n",
+ [Problems])
+ end,
+ Plugins.
+
+%% @doc Read the list of enabled plugins from the supplied term file.
+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.
+
+%%
+%% @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.
+%%
+calculate_plugin_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 = find_plugins(PluginsDistDir),
Enabled = read_enabled_plugins(EnabledPluginsFile),
- ToUnpack = calculate_required_plugins(Enabled, AllPlugins),
+ ToUnpack = calculate_plugin_dependencies(false, Enabled, AllPlugins),
ToUnpackPlugins = lookup_plugins(ToUnpack, AllPlugins),
Missing = Enabled -- plugin_names(ToUnpackPlugins),
@@ -136,75 +145,6 @@ prepare_dir_plugin(PluginAppDescFn) ->
%%----------------------------------------------------------------------------
-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.
-
-%%----------------------------------------------------------------------------
-
-print_error(Format, Args) ->
- rabbit_misc:format_stderr("Error: " ++ Format ++ "~n", Args).
-
-usage() ->
- io:format("~s", [rabbit_plugins_usage:usage()]),
- rabbit_misc:quit(1).
-
delete_recursively(Fn) ->
case rabbit_file:recursive_delete([Fn]) of
ok -> ok;
@@ -219,26 +159,6 @@ prepare_plugin(#plugin{type = dir, name = Name, location = Location},
rabbit_file:recursive_copy(Location,
filename:join([PluginsDestDir, Name])).
-%% Get the #plugin{}s ready to be enabled.
-find_plugins(PluginsDir) ->
- EZs = [{ez, EZ} || EZ <- filelib:wildcard("*.ez", PluginsDir)],
- FreeApps = [{app, App} ||
- App <- filelib:wildcard("*/ebin/*.app", PluginsDir)],
- {Plugins, Problems} =
- lists:foldl(fun ({error, EZ, Reason}, {Plugins1, Problems1}) ->
- {Plugins1, [{EZ, Reason} | Problems1]};
- (Plugin = #plugin{}, {Plugins1, Problems1}) ->
- {[Plugin|Plugins1], Problems1}
- end, {[], []},
- [get_plugin_info(PluginsDir, Plug) ||
- Plug <- EZs ++ FreeApps]),
- case Problems of
- [] -> ok;
- _ -> io:format("Warning: Problem reading some plugins: ~p~n",
- [Problems])
- end,
- Plugins.
-
%% Get the #plugin{} from an .ez.
get_plugin_info(Base, {ez, EZ0}) ->
EZ = filename:join([Base, EZ0]),
@@ -298,82 +218,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,
@@ -397,71 +241,5 @@ plugin_names(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.
diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl
new file mode 100644
index 00000000..a27ad986
--- /dev/null
+++ b/src/rabbit_plugins_main.erl
@@ -0,0 +1,271 @@
+%% 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").
+
+%%----------------------------------------------------------------------------
+
+-ifdef(use_specs).
+
+-spec(start/0 :: () -> no_return()).
+-spec(stop/0 :: () -> 'ok').
+
+-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,
+
+ 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:find_plugins(PluginsDir),
+ Enabled = rabbit_plugins:read_enabled_plugins(PluginsFile),
+ ImplicitlyEnabled = rabbit_plugins:calculate_plugin_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:calculate_plugin_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_plugins(PluginsFile),
+ AllPlugins = rabbit_plugins: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 = rabbit_plugins:calculate_plugin_dependencies(true, ToDisable, AllPlugins),
+ NewEnabled = Enabled -- ToDisableDeps,
+ case length(Enabled) =:= length(NewEnabled) of
+ true -> io:format("Plugin configuration unchanged.~n");
+ false -> ImplicitlyEnabled =
+ rabbit_plugins:calculate_plugin_dependencies(false, Enabled, AllPlugins),
+ NewImplicitlyEnabled =
+ rabbit_plugins:calculate_plugin_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:find_plugins(PluginsDir),
+ EnabledExplicitly = rabbit_plugins:read_enabled_plugins(PluginsFile),
+ EnabledImplicitly =
+ rabbit_plugins:calculate_plugin_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);
+ 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}.
+
+%% 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"),
+ 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.
+