| 
									
										
										
										
											2016-02-03 23:01:32 +08:00
										 |  |  | ## 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 GoPivotal, Inc. | 
					
						
							|  |  |  | ## Copyright (c) 2007-2016 Pivotal Software, Inc.  All rights reserved. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-03 22:42:45 +08:00
										 |  |  | defmodule RabbitMQCtl do | 
					
						
							| 
									
										
										
										
											2016-11-15 01:52:08 +08:00
										 |  |  |   alias RabbitMQ.CLI.Core.Distribution,  as: Distribution | 
					
						
							| 
									
										
										
										
											2016-06-09 08:34:56 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-10 07:54:22 +08:00
										 |  |  |   alias RabbitMQ.CLI.Ctl.Commands.HelpCommand, as: HelpCommand | 
					
						
							| 
									
										
										
										
											2016-11-15 01:52:08 +08:00
										 |  |  |   alias RabbitMQ.CLI.Core.Output, as: Output | 
					
						
							|  |  |  |   alias RabbitMQ.CLI.Core.ExitCodes, as: ExitCodes | 
					
						
							|  |  |  |   alias RabbitMQ.CLI.Core.CommandModules, as: CommandModules | 
					
						
							| 
									
										
										
										
											2016-06-10 07:54:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-15 01:52:08 +08:00
										 |  |  |   import RabbitMQ.CLI.Core.Helpers | 
					
						
							|  |  |  |   import  RabbitMQ.CLI.Core.Parser | 
					
						
							| 
									
										
										
										
											2016-08-22 19:58:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-11 01:19:06 +08:00
										 |  |  |   def main(["--auto-complete", "./rabbitmqctl " <> str]) do | 
					
						
							| 
									
										
										
										
											2016-07-29 00:46:37 +08:00
										 |  |  |     auto_complete(str) | 
					
						
							|  |  |  |   end | 
					
						
							| 
									
										
										
										
											2016-08-11 01:19:06 +08:00
										 |  |  |   def main(["--auto-complete", "rabbitmqctl " <> str]) do | 
					
						
							| 
									
										
										
										
											2016-07-29 00:46:37 +08:00
										 |  |  |     auto_complete(str) | 
					
						
							|  |  |  |   end | 
					
						
							| 
									
										
										
										
											2016-05-09 18:07:46 +08:00
										 |  |  |   def main(unparsed_command) do | 
					
						
							| 
									
										
										
										
											2016-11-04 02:10:18 +08:00
										 |  |  |     {parsed_cmd, parsed_options, invalid} = parse(unparsed_command) | 
					
						
							| 
									
										
										
										
											2016-11-11 01:40:43 +08:00
										 |  |  |     options = parsed_options |> merge_all_defaults |> normalize_options | 
					
						
							| 
									
										
										
										
											2016-10-31 19:21:10 +08:00
										 |  |  |     CommandModules.load(options) | 
					
						
							| 
									
										
										
										
											2016-06-09 08:34:56 +08:00
										 |  |  |     case {is_command?(parsed_cmd), invalid} do | 
					
						
							| 
									
										
										
										
											2016-07-29 00:46:37 +08:00
										 |  |  |       ## No such command | 
					
						
							| 
									
										
										
										
											2016-06-08 22:19:37 +08:00
										 |  |  |       {false, _}  -> | 
					
						
							| 
									
										
										
										
											2016-08-22 19:58:37 +08:00
										 |  |  |         usage_string = HelpCommand.all_usage() | 
					
						
							| 
									
										
										
										
											2016-11-04 19:11:03 +08:00
										 |  |  |         {:error, ExitCodes.exit_usage, usage_string}; | 
					
						
							| 
									
										
										
										
											2016-07-29 00:46:37 +08:00
										 |  |  |       ## Invalid options | 
					
						
							| 
									
										
										
										
											2016-06-08 22:19:37 +08:00
										 |  |  |       {_, [_|_]}  -> | 
					
						
							| 
									
										
										
										
											2016-11-04 19:11:03 +08:00
										 |  |  |         error = validation_error({:bad_option, invalid}, unparsed_command); | 
					
						
							| 
									
										
										
										
											2016-07-29 00:46:37 +08:00
										 |  |  |       ## Command valid | 
					
						
							| 
									
										
										
										
											2016-06-08 22:19:37 +08:00
										 |  |  |       {true, []}  -> | 
					
						
							| 
									
										
										
										
											2016-11-04 02:10:18 +08:00
										 |  |  |         Distribution.start(options) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-04 19:11:03 +08:00
										 |  |  |         [command_name | arguments] = parsed_cmd | 
					
						
							|  |  |  |         command = commands[command_name] | 
					
						
							|  |  |  |         case invalid_flags(command, options) do | 
					
						
							|  |  |  |           [] -> | 
					
						
							|  |  |  |             case run_command(options, command, arguments) do | 
					
						
							|  |  |  |               {:error, _, _} = err -> | 
					
						
							|  |  |  |                 err; | 
					
						
							|  |  |  |               {:validation_failure, err} -> | 
					
						
							|  |  |  |                 validation_error(err, unparsed_command); | 
					
						
							|  |  |  |               output  -> | 
					
						
							|  |  |  |                 formatter = get_formatter(command, options) | 
					
						
							|  |  |  |                 printer = get_printer(options) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 output | 
					
						
							|  |  |  |                 |> Output.format_output(formatter, options) | 
					
						
							|  |  |  |                 |> Output.print_output(printer, options) | 
					
						
							|  |  |  |             end; | 
					
						
							|  |  |  |           result -> | 
					
						
							|  |  |  |             error = validation_error({:bad_option, result}, unparsed_command); | 
					
						
							| 
									
										
										
										
											2016-11-04 02:10:18 +08:00
										 |  |  |         end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |     |> handle_shutdown | 
					
						
							|  |  |  |   end | 
					
						
							| 
									
										
										
										
											2016-06-08 22:19:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-04 02:10:18 +08:00
										 |  |  |   def handle_shutdown({:error, exit_code, output}) do | 
					
						
							| 
									
										
										
										
											2016-11-04 19:11:03 +08:00
										 |  |  |     output_device = case exit_code == ExitCodes.exit_ok do | 
					
						
							| 
									
										
										
										
											2016-11-04 19:12:14 +08:00
										 |  |  |       true  -> :stdio; | 
					
						
							| 
									
										
										
										
											2016-11-04 19:11:03 +08:00
										 |  |  |       false -> :stderr | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2016-11-04 02:10:18 +08:00
										 |  |  |     for line <- List.flatten([output]) do | 
					
						
							| 
									
										
										
										
											2016-11-04 19:11:03 +08:00
										 |  |  |       IO.puts(output_device, line) | 
					
						
							| 
									
										
										
										
											2016-03-05 05:35:54 +08:00
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2016-11-04 02:10:18 +08:00
										 |  |  |     exit_program(exit_code) | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  |   def handle_shutdown(_) do | 
					
						
							| 
									
										
										
										
											2016-11-04 19:11:03 +08:00
										 |  |  |     exit_program(ExitCodes.exit_ok) | 
					
						
							| 
									
										
										
										
											2016-02-03 05:32:35 +08:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-29 00:46:37 +08:00
										 |  |  |   def auto_complete(str) do | 
					
						
							| 
									
										
										
										
											2016-11-15 01:52:08 +08:00
										 |  |  |     Rabbitmq.CLI.AutoComplete.complete(str) | 
					
						
							| 
									
										
										
										
											2016-07-29 00:46:37 +08:00
										 |  |  |     |> Stream.map(&IO.puts/1) |> Stream.run | 
					
						
							| 
									
										
										
										
											2016-11-04 19:11:03 +08:00
										 |  |  |     exit_program(ExitCodes.exit_ok) | 
					
						
							| 
									
										
										
										
											2016-07-29 00:46:37 +08:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-09 02:43:09 +08:00
										 |  |  |   def merge_all_defaults(%{} = options) do | 
					
						
							| 
									
										
										
										
											2016-03-08 03:08:38 +08:00
										 |  |  |     options | 
					
						
							| 
									
										
										
										
											2016-05-24 19:33:11 +08:00
										 |  |  |     |> merge_defaults_node | 
					
						
							|  |  |  |     |> merge_defaults_timeout | 
					
						
							| 
									
										
										
										
											2016-06-08 22:19:37 +08:00
										 |  |  |     |> merge_defaults_longnames | 
					
						
							| 
									
										
										
										
											2016-03-08 03:08:38 +08:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-24 19:33:11 +08:00
										 |  |  |   defp merge_defaults_node(%{} = opts), do: Map.merge(%{node: get_rabbit_hostname}, opts) | 
					
						
							| 
									
										
										
										
											2016-03-08 06:45:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-24 19:33:11 +08:00
										 |  |  |   defp merge_defaults_timeout(%{} = opts), do: Map.merge(%{timeout: :infinity}, opts) | 
					
						
							| 
									
										
										
										
											2016-03-08 06:45:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-08 22:19:37 +08:00
										 |  |  |   defp merge_defaults_longnames(%{} = opts), do: Map.merge(%{longnames: false}, opts) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-11 01:40:43 +08:00
										 |  |  |   defp normalize_options(opts) do | 
					
						
							|  |  |  |     opts | 
					
						
							|  |  |  |     |> normalize_node | 
					
						
							|  |  |  |     |> normalize_timeout | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-10 05:55:59 +08:00
										 |  |  |   defp normalize_node(%{node: node} = opts) do | 
					
						
							|  |  |  |     Map.merge(opts, %{node: parse_node(node)}) | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-11 01:40:43 +08:00
										 |  |  |   defp normalize_timeout(%{timeout: timeout} = opts) | 
					
						
							|  |  |  |   when is_integer(timeout) do | 
					
						
							|  |  |  |     Map.put(opts, :timeout, timeout * 1000) | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  |   defp normalize_timeout(opts) do | 
					
						
							|  |  |  |     opts | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-04 19:11:03 +08:00
										 |  |  |   defp maybe_connect_to_rabbitmq(HelpCommand, _), do: nil | 
					
						
							| 
									
										
										
										
											2016-05-31 16:40:48 +08:00
										 |  |  |   defp maybe_connect_to_rabbitmq(_, node) do | 
					
						
							| 
									
										
										
										
											2016-06-09 08:34:56 +08:00
										 |  |  |     connect_to_rabbitmq(node) | 
					
						
							| 
									
										
										
										
											2016-05-31 16:40:48 +08:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-04 19:11:03 +08:00
										 |  |  |   defp run_command(options, command, arguments) do | 
					
						
							|  |  |  |     {arguments, options} = command.merge_defaults(arguments, options) | 
					
						
							|  |  |  |     case command.validate(arguments, options) do | 
					
						
							|  |  |  |       :ok -> | 
					
						
							| 
									
										
										
										
											2016-10-26 23:25:39 +08:00
										 |  |  |         maybe_print_banner(command, arguments, options) | 
					
						
							| 
									
										
										
										
											2016-11-04 19:11:03 +08:00
										 |  |  |         maybe_connect_to_rabbitmq(command, options[:node]) | 
					
						
							|  |  |  |         try do | 
					
						
							|  |  |  |           command.run(arguments, options) |> command.output(options) | 
					
						
							| 
									
										
										
										
											2016-11-04 20:38:35 +08:00
										 |  |  |         catch _error_type, error -> | 
					
						
							| 
									
										
										
										
											2016-11-04 19:11:03 +08:00
										 |  |  |           {:error, ExitCodes.exit_software, | 
					
						
							|  |  |  |            to_string(:io_lib.format("Error: ~n~p~n Stacktrace ~p~n", | 
					
						
							|  |  |  |                                     [error, System.stacktrace()]))} | 
					
						
							| 
									
										
										
										
											2016-11-04 02:10:18 +08:00
										 |  |  |         end | 
					
						
							| 
									
										
										
										
											2016-11-04 19:11:03 +08:00
										 |  |  |       {:validation_failure, _} = err -> err | 
					
						
							| 
									
										
										
										
											2016-11-04 02:10:18 +08:00
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2016-02-03 02:54:36 +08:00
										 |  |  |   end | 
					
						
							| 
									
										
										
										
											2016-02-26 05:16:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-04 02:10:18 +08:00
										 |  |  |   defp get_formatter(command, %{formatter: formatter}) do | 
					
						
							| 
									
										
										
										
											2016-11-16 00:14:22 +08:00
										 |  |  |     module_name = Module.safe_concat("RabbitMQ.CLI.Formatters", Macro.camelize(formatter)) | 
					
						
							| 
									
										
										
										
											2016-11-04 02:10:18 +08:00
										 |  |  |     case Code.ensure_loaded(module_name) do | 
					
						
							|  |  |  |       {:module, _}      -> module_name; | 
					
						
							|  |  |  |       {:error, :nofile} -> default_formatter(command) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							| 
									
										
										
										
											2016-11-04 19:11:03 +08:00
										 |  |  |   defp get_formatter(command, _) do | 
					
						
							| 
									
										
										
										
											2016-11-04 02:10:18 +08:00
										 |  |  |     default_formatter(command) | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def get_printer(%{printer: printer}) do | 
					
						
							| 
									
										
										
										
											2016-11-16 00:14:22 +08:00
										 |  |  |     module_name = String.to_atom("RabbitMQ.CLI.Printers." <> Macro.camelize(printer)) | 
					
						
							| 
									
										
										
										
											2016-11-04 02:10:18 +08:00
										 |  |  |     case Code.ensure_loaded(module_name) do | 
					
						
							|  |  |  |       {:module, _}      -> module_name; | 
					
						
							|  |  |  |       {:error, :nofile} -> default_printer | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  |   def get_printer(_) do | 
					
						
							|  |  |  |     default_printer | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def default_printer() do | 
					
						
							|  |  |  |     RabbitMQ.CLI.Printers.StdIO | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def default_formatter(command) do | 
					
						
							|  |  |  |     case function_exported?(command, :formatter, 0) do | 
					
						
							|  |  |  |       true  -> command.formatter; | 
					
						
							|  |  |  |       false -> RabbitMQ.CLI.Formatters.Inspect | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2016-05-05 06:31:11 +08:00
										 |  |  |   end | 
					
						
							| 
									
										
										
										
											2016-04-27 02:38:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-26 23:25:39 +08:00
										 |  |  |   ## Suppress banner if --quiet option is provided | 
					
						
							|  |  |  |   defp maybe_print_banner(_, _, %{quiet: true}) do | 
					
						
							|  |  |  |     nil | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  |   defp maybe_print_banner(command, args, opts) do | 
					
						
							| 
									
										
										
										
											2016-05-31 16:40:48 +08:00
										 |  |  |     case command.banner(args, opts) do | 
					
						
							| 
									
										
										
										
											2016-10-26 23:25:39 +08:00
										 |  |  |       nil -> nil | 
					
						
							|  |  |  |       banner -> | 
					
						
							|  |  |  |         case banner do | 
					
						
							|  |  |  |           list when is_list(list) -> | 
					
						
							|  |  |  |             for line <- list, do: IO.puts(line); | 
					
						
							|  |  |  |           binary when is_binary(binary) -> | 
					
						
							|  |  |  |             IO.puts(binary) | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2016-05-31 16:40:48 +08:00
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2016-05-24 19:33:11 +08:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-04 19:11:03 +08:00
										 |  |  |   defp validation_error(err_detail, unparsed_command) do | 
					
						
							| 
									
										
										
										
											2016-05-30 07:16:10 +08:00
										 |  |  |     {[command_name | _], _, _} = parse(unparsed_command) | 
					
						
							| 
									
										
										
										
											2016-08-19 00:38:49 +08:00
										 |  |  |     err = format_validation_error(err_detail, command_name) # TODO format the error better | 
					
						
							| 
									
										
										
										
											2016-08-22 19:58:37 +08:00
										 |  |  |     base_error = "Error: #{err}\nGiven:\n\t#{unparsed_command |> Enum.join(" ")}" | 
					
						
							| 
									
										
										
										
											2016-05-30 07:16:10 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-22 19:58:37 +08:00
										 |  |  |     usage = case is_command?(command_name) do | 
					
						
							| 
									
										
										
										
											2016-05-30 07:16:10 +08:00
										 |  |  |       true  -> | 
					
						
							| 
									
										
										
										
											2016-06-09 08:34:56 +08:00
										 |  |  |         command = commands[command_name] | 
					
						
							| 
									
										
										
										
											2016-08-22 19:58:37 +08:00
										 |  |  |         HelpCommand.base_usage(HelpCommand.program_name(), command) | 
					
						
							| 
									
										
										
										
											2016-05-30 07:16:10 +08:00
										 |  |  |       false -> | 
					
						
							| 
									
										
										
										
											2016-08-22 19:58:37 +08:00
										 |  |  |         HelpCommand.all_usage() | 
					
						
							| 
									
										
										
										
											2016-05-30 07:16:10 +08:00
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2016-11-04 19:11:03 +08:00
										 |  |  |     message = base_error <> "\n" <> usage | 
					
						
							|  |  |  |     {:error, ExitCodes.exit_code_for({:validation_failure, err_detail}), message} | 
					
						
							| 
									
										
										
										
											2016-04-02 01:58:47 +08:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 00:38:49 +08:00
										 |  |  |   defp format_validation_error(:not_enough_args, _), do: "not enough arguments." | 
					
						
							|  |  |  |   defp format_validation_error({:not_enough_args, detail}, _), do: "not enough arguments. #{detail}" | 
					
						
							|  |  |  |   defp format_validation_error(:too_many_args, _), do: "too many arguments." | 
					
						
							|  |  |  |   defp format_validation_error({:too_many_args, detail}, _), do: "too many arguments. #{detail}" | 
					
						
							|  |  |  |   defp format_validation_error(:bad_argument, _), do: "Bad argument." | 
					
						
							|  |  |  |   defp format_validation_error({:bad_argument, detail}, _), do: "Bad argument. #{detail}" | 
					
						
							|  |  |  |   defp format_validation_error({:bad_option, opts}, command_name) do | 
					
						
							|  |  |  |     header = case is_command?(command_name) do | 
					
						
							|  |  |  |       true  -> "Invalid options for this command:"; | 
					
						
							|  |  |  |       false -> "Invalid options:" | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |     Enum.join([header | for {key, val} <- opts do "#{key} : #{val}" end], "\n") | 
					
						
							| 
									
										
										
										
											2016-08-18 21:56:21 +08:00
										 |  |  |   end | 
					
						
							| 
									
										
										
										
											2016-08-22 19:58:37 +08:00
										 |  |  |   defp format_validation_error(err, _), do: inspect err | 
					
						
							| 
									
										
										
										
											2016-03-23 00:21:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-05 18:39:27 +08:00
										 |  |  |   defp invalid_flags(command, opts) do | 
					
						
							| 
									
										
										
										
											2016-08-18 21:56:21 +08:00
										 |  |  |     Map.take(opts, Map.keys(opts) -- (command.flags ++ global_flags)) | 
					
						
							|  |  |  |     |> Map.to_list | 
					
						
							| 
									
										
										
										
											2016-05-05 03:40:09 +08:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-23 00:21:12 +08:00
										 |  |  |   defp exit_program(code) do | 
					
						
							|  |  |  |     :net_kernel.stop | 
					
						
							| 
									
										
										
										
											2016-04-02 00:13:01 +08:00
										 |  |  |     exit({:shutdown, code}) | 
					
						
							| 
									
										
										
										
											2016-03-23 00:21:12 +08:00
										 |  |  |   end | 
					
						
							| 
									
										
										
										
											2016-02-03 02:54:36 +08:00
										 |  |  | end |