170 lines
5.5 KiB
Erlang
170 lines
5.5 KiB
Erlang
%%--------------------------------------------------------------------
|
|
%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved.
|
|
%%
|
|
%% Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
|
%%
|
|
%% Unless required by applicable law or agreed to in writing, software
|
|
%% distributed under the License is distributed on an "AS IS" BASIS,
|
|
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
%% See the License for the specific language governing permissions and
|
|
%% limitations under the License.
|
|
%%--------------------------------------------------------------------
|
|
|
|
-module(emqx_modules).
|
|
|
|
-include("logger.hrl").
|
|
|
|
-logger_header("[Modules]").
|
|
|
|
-export([ list/0
|
|
, load/0
|
|
, load/1
|
|
, unload/0
|
|
, unload/1
|
|
, reload/1
|
|
, find_module/1
|
|
, load_module/2
|
|
]).
|
|
|
|
%% @doc List all available plugins
|
|
-spec(list() -> [{atom(), boolean()}]).
|
|
list() ->
|
|
ets:tab2list(?MODULE).
|
|
|
|
%% @doc Load all the extended modules.
|
|
-spec(load() -> ok).
|
|
load() ->
|
|
case emqx:get_env(modules_loaded_file) of
|
|
undefined -> ok;
|
|
File ->
|
|
load_modules(File)
|
|
end.
|
|
|
|
load(ModuleName) ->
|
|
case find_module(ModuleName) of
|
|
[] ->
|
|
?LOG(alert, "Module ~s not found, cannot load it", [ModuleName]),
|
|
{error, not_found};
|
|
[{ModuleName, true}] ->
|
|
?LOG(notice, "Module ~s is already started", [ModuleName]),
|
|
{error, already_started};
|
|
[{ModuleName, false}] ->
|
|
emqx_modules:load_module(ModuleName, true)
|
|
end.
|
|
|
|
%% @doc Unload all the extended modules.
|
|
-spec(unload() -> ok).
|
|
unload() ->
|
|
case emqx:get_env(modules_loaded_file) of
|
|
undefined -> ignore;
|
|
File ->
|
|
unload_modules(File)
|
|
end.
|
|
|
|
unload(ModuleName) ->
|
|
case find_module(ModuleName) of
|
|
[] ->
|
|
?LOG(alert, "Module ~s not found, cannot load it", [ModuleName]),
|
|
{error, not_found};
|
|
[{ModuleName, false}] ->
|
|
?LOG(error, "Module ~s is not started", [ModuleName]),
|
|
{error, not_started};
|
|
[{ModuleName, true}] ->
|
|
unload_module(ModuleName, true)
|
|
end.
|
|
|
|
reload(emqx_mod_acl_internal) ->
|
|
Modules = emqx:get_env(modules, []),
|
|
Env = proplists:get_value(emqx_mod_acl_internal, Modules, undefined),
|
|
case emqx_mod_acl_internal:reload(Env) of
|
|
ok ->
|
|
?LOG(info, "Reload ~s module successfully.", [emqx_mod_acl_internal]);
|
|
{error, Error} ->
|
|
?LOG(error, "Reload module ~s failed, cannot start for ~0p", [emqx_mod_acl_internal, Error])
|
|
end;
|
|
reload(_) ->
|
|
ignore.
|
|
|
|
find_module(ModuleName) ->
|
|
ets:lookup(?MODULE, ModuleName).
|
|
|
|
filter_module(ModuleNames) ->
|
|
filter_module(ModuleNames, emqx:get_env(modules, [])).
|
|
filter_module([], Acc) ->
|
|
Acc;
|
|
filter_module([{ModuleName, true} | ModuleNames], Acc) ->
|
|
filter_module(ModuleNames, lists:keydelete(ModuleName, 1, Acc));
|
|
filter_module([{_, false} | ModuleNames], Acc) ->
|
|
filter_module(ModuleNames, Acc).
|
|
|
|
load_modules(File) ->
|
|
case file:consult(File) of
|
|
{ok, ModuleNames} ->
|
|
lists:foreach(fun({ModuleName, _}) ->
|
|
ets:insert(?MODULE, {ModuleName, false})
|
|
end, filter_module(ModuleNames)),
|
|
lists:foreach(fun load_module/1, ModuleNames);
|
|
{error, Error} ->
|
|
?LOG(alert, "Failed to read: ~p, error: ~p", [File, Error])
|
|
end.
|
|
|
|
load_module({ModuleName, true}) ->
|
|
emqx_modules:load_module(ModuleName, false);
|
|
load_module({ModuleName, false}) ->
|
|
ets:insert(?MODULE, {ModuleName, false});
|
|
load_module(ModuleName) ->
|
|
load_module({ModuleName, true}).
|
|
|
|
load_module(ModuleName, Persistent) ->
|
|
Modules = emqx:get_env(modules, []),
|
|
Env = proplists:get_value(ModuleName, Modules, undefined),
|
|
case ModuleName:load(Env) of
|
|
ok ->
|
|
ets:insert(?MODULE, {ModuleName, true}),
|
|
write_loaded(Persistent),
|
|
?LOG(info, "Load ~s module successfully.", [ModuleName]);
|
|
{error, Error} ->
|
|
?LOG(error, "Load module ~s failed, cannot load for ~0p", [ModuleName, Error]),
|
|
{error, Error}
|
|
end.
|
|
|
|
unload_modules(File) ->
|
|
case file:consult(File) of
|
|
{ok, ModuleNames} ->
|
|
lists:foreach(fun unload_module/1, ModuleNames);
|
|
{error, Error} ->
|
|
?LOG(alert, "Failed to read: ~p, error: ~p", [File, Error])
|
|
end.
|
|
unload_module({ModuleName, true}) ->
|
|
unload_module(ModuleName, false);
|
|
unload_module({ModuleName, false}) ->
|
|
ets:insert(?MODULE, {ModuleName, false});
|
|
unload_module(ModuleName) ->
|
|
unload_module({ModuleName, true}).
|
|
|
|
unload_module(ModuleName, Persistent) ->
|
|
Modules = emqx:get_env(modules, []),
|
|
Env = proplists:get_value(ModuleName, Modules, undefined),
|
|
case ModuleName:unload(Env) of
|
|
ok ->
|
|
ets:insert(?MODULE, {ModuleName, false}),
|
|
write_loaded(Persistent),
|
|
?LOG(info, "Unload ~s module successfully.", [ModuleName]);
|
|
{error, Error} ->
|
|
?LOG(error, "Unload module ~s failed, cannot unload for ~0p", [ModuleName, Error])
|
|
end.
|
|
|
|
write_loaded(true) ->
|
|
FilePath = emqx:get_env(modules_loaded_file),
|
|
case file:write_file(FilePath, [io_lib:format("~p.~n", [Name]) || Name <- list()]) of
|
|
ok -> ok;
|
|
{error, Error} ->
|
|
?LOG(error, "Write File ~p Error: ~p", [FilePath, Error]),
|
|
{error, Error}
|
|
end;
|
|
write_loaded(false) -> ok.
|