1## This Source Code Form is subject to the terms of the Mozilla Public
2## License, v. 2.0. If a copy of the MPL was not distributed with this
3## file, You can obtain one at https://mozilla.org/MPL/2.0/.
4##
5## Copyright (c) 2007-2021 VMware, Inc. or its affiliates.  All rights reserved.
6
7defmodule RabbitMQ.CLI.Core.CodePath do
8  alias RabbitMQ.CLI.Core.{Config, Paths, Platform}
9
10  def add_plugins_to_load_path(opts) do
11    with {:ok, plugins_dir} <- Paths.plugins_dir(opts) do
12      String.split(to_string(plugins_dir), Platform.path_separator())
13      |> Enum.map(&add_directory_plugins_to_load_path/1)
14
15      :ok
16    end
17  end
18
19  def add_directory_plugins_to_load_path(directory_with_plugins_inside_it) do
20    with {:ok, files} <- File.ls(directory_with_plugins_inside_it) do
21      Enum.map(
22        files,
23        fn filename ->
24          cond do
25            String.ends_with?(filename, [".ez"]) ->
26              Path.join([directory_with_plugins_inside_it, filename])
27              |> String.to_charlist()
28              |> add_archive_code_path()
29
30            File.dir?(filename) ->
31              Path.join([directory_with_plugins_inside_it, filename])
32              |> add_dir_code_path()
33
34            true ->
35              {:error, {:not_a_plugin, filename}}
36          end
37        end
38      )
39    end
40  end
41
42  defp add_archive_code_path(ez_dir) do
43    case :erl_prim_loader.list_dir(ez_dir) do
44      {:ok, [app_dir]} ->
45        app_in_ez = :filename.join(ez_dir, app_dir)
46        add_dir_code_path(app_in_ez)
47
48      _ ->
49        {:error, :no_app_dir}
50    end
51  end
52
53  defp add_dir_code_path(app_dir_0) do
54    app_dir = to_charlist(app_dir_0)
55
56    case :erl_prim_loader.list_dir(app_dir) do
57      {:ok, list} ->
58        case Enum.member?(list, 'ebin') do
59          true ->
60            ebin_dir = :filename.join(app_dir, 'ebin')
61            Code.append_path(ebin_dir)
62
63          false ->
64            {:error, :no_ebin}
65        end
66
67      _ ->
68        {:error, :app_dir_empty}
69    end
70  end
71
72  def require_rabbit_and_plugins(_, opts) do
73    require_rabbit_and_plugins(opts)
74  end
75
76  def require_rabbit_and_plugins(opts) do
77    with :ok <- require_rabbit(opts),
78         :ok <- add_plugins_to_load_path(opts),
79         do: :ok
80  end
81
82  def require_rabbit(_, opts) do
83    require_rabbit(opts)
84  end
85
86  def require_rabbit(opts) do
87    home = Config.get_option(:rabbitmq_home, opts)
88
89    case home do
90      nil ->
91        {:error, {:unable_to_load_rabbit, :rabbitmq_home_is_undefined}}
92
93      _ ->
94        case Application.load(:rabbit) do
95          :ok ->
96            Code.ensure_loaded(:rabbit_plugins)
97            :ok
98
99          {:error, {:already_loaded, :rabbit}} ->
100            Code.ensure_loaded(:rabbit_plugins)
101            :ok
102
103          {:error, err} ->
104            {:error, {:unable_to_load_rabbit, err}}
105        end
106    end
107  end
108end
109