rename project name from 'emqtt' to 'emqttd'
This commit is contained in:
parent
83c376ad83
commit
aca536cd8a
|
@ -6,7 +6,7 @@ deps
|
||||||
*.plt
|
*.plt
|
||||||
erl_crash.dump
|
erl_crash.dump
|
||||||
ebin
|
ebin
|
||||||
rel/emqtt
|
rel/emqttd
|
||||||
.concrete/DEV_MODE
|
.concrete/DEV_MODE
|
||||||
.rebar
|
.rebar
|
||||||
test/ebin/*.beam
|
test/ebin/*.beam
|
||||||
|
|
|
@ -2,8 +2,14 @@
|
||||||
eMQTT ChangeLog
|
eMQTT ChangeLog
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
v0.5.0-alpha (2015-03-20)
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
rename 'eMQTT' to 'eMQTTD'
|
||||||
|
|
||||||
|
|
||||||
v0.4.0-alpha (2015-03-10)
|
v0.4.0-alpha (2015-03-10)
|
||||||
------------------------
|
-------------------------
|
||||||
|
|
||||||
Support [$SYS Topics of Broker](https://github.com/emqtt/emqtt/wiki/$SYS-Topics-of-Broker) Now!
|
Support [$SYS Topics of Broker](https://github.com/emqtt/emqtt/wiki/$SYS-Topics-of-Broker) Now!
|
||||||
|
|
||||||
|
|
|
@ -1,128 +0,0 @@
|
||||||
%%-----------------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2012-2015, Feng Lee <feng@emqtt.io>
|
|
||||||
%%
|
|
||||||
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
%% of this software and associated documentation files (the "Software"), to deal
|
|
||||||
%% in the Software without restriction, including without limitation the rights
|
|
||||||
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
%% copies of the Software, and to permit persons to whom the Software is
|
|
||||||
%% furnished to do so, subject to the following conditions:
|
|
||||||
%%
|
|
||||||
%% The above copyright notice and this permission notice shall be included in all
|
|
||||||
%% copies or substantial portions of the Software.
|
|
||||||
%%
|
|
||||||
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
%% SOFTWARE.
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(emqtt_app).
|
|
||||||
|
|
||||||
-author('feng@emqtt.io').
|
|
||||||
|
|
||||||
-behaviour(application).
|
|
||||||
|
|
||||||
%% Application callbacks
|
|
||||||
-export([start/2, stop/1]).
|
|
||||||
|
|
||||||
-define(PRINT_MSG(Msg), io:format(Msg)).
|
|
||||||
|
|
||||||
-define(PRINT(Format, Args), io:format(Format, Args)).
|
|
||||||
|
|
||||||
%% ===================================================================
|
|
||||||
%% Application callbacks
|
|
||||||
%% ===================================================================
|
|
||||||
|
|
||||||
%%
|
|
||||||
%% @spec start(atom(), list()) -> {ok, pid()}
|
|
||||||
%%
|
|
||||||
start(_StartType, _StartArgs) ->
|
|
||||||
print_banner(),
|
|
||||||
{ok, Sup} = emqtt_sup:start_link(),
|
|
||||||
start_servers(Sup),
|
|
||||||
{ok, Listeners} = application:get_env(listen),
|
|
||||||
emqtt:open(Listeners),
|
|
||||||
register(emqtt, self()),
|
|
||||||
print_vsn(),
|
|
||||||
{ok, Sup}.
|
|
||||||
|
|
||||||
print_banner() ->
|
|
||||||
?PRINT("starting emqtt on node '~s'~n", [node()]).
|
|
||||||
|
|
||||||
print_vsn() ->
|
|
||||||
{ok, Vsn} = application:get_key(vsn),
|
|
||||||
{ok, Desc} = application:get_key(description),
|
|
||||||
?PRINT("~s ~s is running now~n", [Desc, Vsn]).
|
|
||||||
|
|
||||||
start_servers(Sup) ->
|
|
||||||
{ok, SessOpts} = application:get_env(session),
|
|
||||||
{ok, RetainOpts} = application:get_env(retain),
|
|
||||||
{ok, BrokerOpts} = application:get_env(broker),
|
|
||||||
{ok, MetricOpts} = application:get_env(metrics),
|
|
||||||
lists:foreach(
|
|
||||||
fun({Name, F}) when is_function(F) ->
|
|
||||||
?PRINT("~s is starting...", [Name]),
|
|
||||||
F(),
|
|
||||||
?PRINT_MSG("[done]~n");
|
|
||||||
({Name, Server}) ->
|
|
||||||
?PRINT("~s is starting...", [Name]),
|
|
||||||
start_child(Sup, Server),
|
|
||||||
?PRINT_MSG("[done]~n");
|
|
||||||
({Name, Server, Opts}) ->
|
|
||||||
?PRINT("~s is starting...", [ Name]),
|
|
||||||
start_child(Sup, Server, Opts),
|
|
||||||
?PRINT_MSG("[done]~n")
|
|
||||||
end,
|
|
||||||
[{"emqtt config", emqtt_config},
|
|
||||||
{"emqtt server", emqtt_server, RetainOpts},
|
|
||||||
{"emqtt client manager", emqtt_cm},
|
|
||||||
{"emqtt session manager", emqtt_sm},
|
|
||||||
{"emqtt session supervisor", {supervisor, emqtt_session_sup}, SessOpts},
|
|
||||||
{"emqtt auth", emqtt_auth},
|
|
||||||
{"emqtt pubsub", emqtt_pubsub},
|
|
||||||
{"emqtt router", emqtt_router},
|
|
||||||
{"emqtt broker", emqtt_broker, BrokerOpts},
|
|
||||||
{"emqtt metrics", emqtt_metrics, MetricOpts},
|
|
||||||
{"emqtt monitor", emqtt_monitor}
|
|
||||||
]).
|
|
||||||
|
|
||||||
start_child(Sup, {supervisor, Name}) ->
|
|
||||||
supervisor:start_child(Sup, supervisor_spec(Name));
|
|
||||||
start_child(Sup, Name) when is_atom(Name) ->
|
|
||||||
{ok, _ChiId} = supervisor:start_child(Sup, worker_spec(Name)).
|
|
||||||
|
|
||||||
start_child(Sup, {supervisor, Name}, Opts) ->
|
|
||||||
supervisor:start_child(Sup, supervisor_spec(Name, Opts));
|
|
||||||
start_child(Sup, Name, Opts) when is_atom(Name) ->
|
|
||||||
{ok, _ChiId} = supervisor:start_child(Sup, worker_spec(Name, Opts)).
|
|
||||||
|
|
||||||
%%TODO: refactor...
|
|
||||||
supervisor_spec(Name) ->
|
|
||||||
{Name,
|
|
||||||
{Name, start_link, []},
|
|
||||||
permanent, infinity, supervisor, [Name]}.
|
|
||||||
|
|
||||||
supervisor_spec(Name, Opts) ->
|
|
||||||
{Name,
|
|
||||||
{Name, start_link, [Opts]},
|
|
||||||
permanent, infinity, supervisor, [Name]}.
|
|
||||||
|
|
||||||
worker_spec(Name) ->
|
|
||||||
{Name,
|
|
||||||
{Name, start_link, []},
|
|
||||||
permanent, 5000, worker, [Name]}.
|
|
||||||
worker_spec(Name, Opts) ->
|
|
||||||
{Name,
|
|
||||||
{Name, start_link, [Opts]},
|
|
||||||
permanent, 5000, worker, [Name]}.
|
|
||||||
|
|
||||||
%%
|
|
||||||
%% @spec stop(atom) -> 'ok'
|
|
||||||
%%
|
|
||||||
stop(_State) ->
|
|
||||||
ok.
|
|
||||||
|
|
|
@ -1,93 +0,0 @@
|
||||||
%%-----------------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2012-2015, Feng Lee <feng@emqtt.io>
|
|
||||||
%%
|
|
||||||
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
%% of this software and associated documentation files (the "Software"), to deal
|
|
||||||
%% in the Software without restriction, including without limitation the rights
|
|
||||||
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
%% copies of the Software, and to permit persons to whom the Software is
|
|
||||||
%% furnished to do so, subject to the following conditions:
|
|
||||||
%%
|
|
||||||
%% The above copyright notice and this permission notice shall be included in all
|
|
||||||
%% copies or substantial portions of the Software.
|
|
||||||
%%
|
|
||||||
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
%% SOFTWARE.
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(emqtt_auth).
|
|
||||||
|
|
||||||
-author('feng@emqtt.io').
|
|
||||||
|
|
||||||
-include("emqtt.hrl").
|
|
||||||
|
|
||||||
-export([start_link/0,
|
|
||||||
add/2,
|
|
||||||
check/1, check/2,
|
|
||||||
delete/1]).
|
|
||||||
|
|
||||||
-behavior(gen_server).
|
|
||||||
|
|
||||||
-export([init/1,
|
|
||||||
handle_call/3,
|
|
||||||
handle_cast/2,
|
|
||||||
handle_info/2,
|
|
||||||
terminate/2,
|
|
||||||
code_change/3]).
|
|
||||||
|
|
||||||
-define(TAB, ?MODULE).
|
|
||||||
|
|
||||||
start_link() ->
|
|
||||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
|
||||||
|
|
||||||
-spec check({Usename :: binary(), Password :: binary()}) -> true | false.
|
|
||||||
check({Username, Password}) ->
|
|
||||||
execute(check, [Username, Password]).
|
|
||||||
|
|
||||||
-spec check(Usename :: binary(), Password :: binary()) -> true | false.
|
|
||||||
check(Username, Password) ->
|
|
||||||
execute(check, [Username, Password]).
|
|
||||||
|
|
||||||
-spec add(Usename :: binary(), Password :: binary()) -> ok.
|
|
||||||
add(Username, Password) ->
|
|
||||||
execute(add, [Username, Password]).
|
|
||||||
|
|
||||||
-spec delete(Username :: binary()) -> ok.
|
|
||||||
delete(Username) ->
|
|
||||||
execute(delete, [Username]).
|
|
||||||
|
|
||||||
execute(F, Args) ->
|
|
||||||
[{_, M}] = ets:lookup(?TAB, mod),
|
|
||||||
apply(M, F, Args).
|
|
||||||
|
|
||||||
init([]) ->
|
|
||||||
{ok, {Name, Opts}} = application:get_env(auth),
|
|
||||||
AuthMod = authmod(Name),
|
|
||||||
ok = AuthMod:init(Opts),
|
|
||||||
ets:new(?TAB, [named_table, protected]),
|
|
||||||
ets:insert(?TAB, {mod, AuthMod}),
|
|
||||||
{ok, undefined}.
|
|
||||||
|
|
||||||
authmod(Name) when is_atom(Name) ->
|
|
||||||
list_to_atom(lists:concat(["emqtt_auth_", Name])).
|
|
||||||
|
|
||||||
handle_call(Req, _From, State) ->
|
|
||||||
{stop, {badreq, Req}, State}.
|
|
||||||
|
|
||||||
handle_cast(Msg, State) ->
|
|
||||||
{stop, {badmsg, Msg}, State}.
|
|
||||||
|
|
||||||
handle_info(Info, State) ->
|
|
||||||
{stop, {badinfo, Info}, State}.
|
|
||||||
|
|
||||||
terminate(_Reason, _State) ->
|
|
||||||
ok.
|
|
||||||
|
|
||||||
code_change(_OldVsn, State, _Extra) ->
|
|
||||||
{ok, State}.
|
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
%%-----------------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2012-2015, Feng Lee <feng@emqtt.io>
|
|
||||||
%%
|
|
||||||
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
%% of this software and associated documentation files (the "Software"), to deal
|
|
||||||
%% in the Software without restriction, including without limitation the rights
|
|
||||||
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
%% copies of the Software, and to permit persons to whom the Software is
|
|
||||||
%% furnished to do so, subject to the following conditions:
|
|
||||||
%%
|
|
||||||
%% The above copyright notice and this permission notice shall be included in all
|
|
||||||
%% copies or substantial portions of the Software.
|
|
||||||
%%
|
|
||||||
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
%% SOFTWARE.
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(emqtt_auth_anonymous).
|
|
||||||
|
|
||||||
-author('feng@emqtt.io').
|
|
||||||
|
|
||||||
-export([init/1,
|
|
||||||
add/2,
|
|
||||||
check/2,
|
|
||||||
delete/1]).
|
|
||||||
|
|
||||||
init(_Opts) -> ok.
|
|
||||||
|
|
||||||
check(_, _) -> true.
|
|
||||||
|
|
||||||
add(_, _) -> ok.
|
|
||||||
|
|
||||||
delete(_Username) -> ok.
|
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
%%-----------------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2012-2015, Feng Lee <feng@emqtt.io>
|
|
||||||
%%
|
|
||||||
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
%% of this software and associated documentation files (the "Software"), to deal
|
|
||||||
%% in the Software without restriction, including without limitation the rights
|
|
||||||
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
%% copies of the Software, and to permit persons to whom the Software is
|
|
||||||
%% furnished to do so, subject to the following conditions:
|
|
||||||
%%
|
|
||||||
%% The above copyright notice and this permission notice shall be included in all
|
|
||||||
%% copies or substantial portions of the Software.
|
|
||||||
%%
|
|
||||||
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
%% SOFTWARE.
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(emqtt_auth_internal).
|
|
||||||
|
|
||||||
-author('feng@emqtt.io').
|
|
||||||
|
|
||||||
-include("emqtt.hrl").
|
|
||||||
|
|
||||||
-export([init/1,
|
|
||||||
add/2,
|
|
||||||
check/2,
|
|
||||||
delete/1]).
|
|
||||||
|
|
||||||
init(_Opts) ->
|
|
||||||
mnesia:create_table(mqtt_user, [
|
|
||||||
{ram_copies, [node()]},
|
|
||||||
{attributes, record_info(fields, mqtt_user)}]),
|
|
||||||
mnesia:add_table_copy(mqtt_user, node(), ram_copies),
|
|
||||||
ok.
|
|
||||||
|
|
||||||
check(undefined, _) -> false;
|
|
||||||
|
|
||||||
check(_, undefined) -> false;
|
|
||||||
|
|
||||||
check(Username, Password) when is_binary(Username), is_binary(Password) ->
|
|
||||||
PasswdHash = crypto:hash(md5, Password),
|
|
||||||
case mnesia:dirty_read(mqtt_user, Username) of
|
|
||||||
[#mqtt_user{passwdhash=PasswdHash}] -> true;
|
|
||||||
_ -> false
|
|
||||||
end.
|
|
||||||
|
|
||||||
add(Username, Password) when is_binary(Username) and is_binary(Password) ->
|
|
||||||
mnesia:dirty_write(
|
|
||||||
#mqtt_user{
|
|
||||||
username=Username,
|
|
||||||
passwdhash=crypto:hash(md5, Password)
|
|
||||||
}
|
|
||||||
).
|
|
||||||
|
|
||||||
delete(Username) when is_binary(Username) ->
|
|
||||||
mnesia:dirty_delete(mqtt_user, Username).
|
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
%%-----------------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2012-2015, Feng Lee <feng@emqtt.io>
|
|
||||||
%%
|
|
||||||
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
%% of this software and associated documentation files (the "Software"), to deal
|
|
||||||
%% in the Software without restriction, including without limitation the rights
|
|
||||||
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
%% copies of the Software, and to permit persons to whom the Software is
|
|
||||||
%% furnished to do so, subject to the following conditions:
|
|
||||||
%%
|
|
||||||
%% The above copyright notice and this permission notice shall be included in all
|
|
||||||
%% copies or substantial portions of the Software.
|
|
||||||
%%
|
|
||||||
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
%% SOFTWARE.
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(emqtt_bridge_sup).
|
|
||||||
|
|
||||||
-author('feng@emqtt.io').
|
|
||||||
|
|
||||||
-behavior(supervisor).
|
|
||||||
|
|
||||||
-export([start_link/0, start_bridge/2, stop_bridge/1, init/1]).
|
|
||||||
|
|
||||||
start_link() ->
|
|
||||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
|
||||||
|
|
||||||
%%TODO: FIXME LATER.
|
|
||||||
start_bridge(Name, Opts) when is_atom(Name) ->
|
|
||||||
supervisor:start_child(?MODULE, {{bridge, Name},
|
|
||||||
{eqmtt_bridge, start_link, [Opts]},
|
|
||||||
transient, 16#fffff, worker, [emqtt_bridge]}).
|
|
||||||
|
|
||||||
stop_bridge(Name) ->
|
|
||||||
ChildId = {bridge, Name},
|
|
||||||
case supervisor:terminate_child(?MODULE, ChildId) of
|
|
||||||
ok ->
|
|
||||||
supervisor:delete_child(?MODULE, ChildId);
|
|
||||||
{error, Reason} ->
|
|
||||||
{error, Reason}
|
|
||||||
end.
|
|
||||||
|
|
||||||
init([]) ->
|
|
||||||
{ok, {{one_for_one, 10, 1000}, []}}.
|
|
||||||
|
|
|
@ -1,82 +0,0 @@
|
||||||
%%-----------------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2012-2015, Feng Lee <feng@emqtt.io>
|
|
||||||
%%
|
|
||||||
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
%% of this software and associated documentation files (the "Software"), to deal
|
|
||||||
%% in the Software without restriction, including without limitation the rights
|
|
||||||
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
%% copies of the Software, and to permit persons to whom the Software is
|
|
||||||
%% furnished to do so, subject to the following conditions:
|
|
||||||
%%
|
|
||||||
%% The above copyright notice and this permission notice shall be included in all
|
|
||||||
%% copies or substantial portions of the Software.
|
|
||||||
%%
|
|
||||||
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
%% SOFTWARE.
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(emqtt_config).
|
|
||||||
|
|
||||||
-export([lookup/1]).
|
|
||||||
|
|
||||||
-behaviour(gen_server).
|
|
||||||
|
|
||||||
-define(SERVER, ?MODULE).
|
|
||||||
|
|
||||||
%% ------------------------------------------------------------------
|
|
||||||
%% API Function Exports
|
|
||||||
%% ------------------------------------------------------------------
|
|
||||||
|
|
||||||
-export([start_link/0]).
|
|
||||||
|
|
||||||
|
|
||||||
%% ------------------------------------------------------------------
|
|
||||||
%% gen_server Function Exports
|
|
||||||
%% ------------------------------------------------------------------
|
|
||||||
|
|
||||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
|
||||||
terminate/2, code_change/3]).
|
|
||||||
|
|
||||||
%% ------------------------------------------------------------------
|
|
||||||
%% API Function Definitions
|
|
||||||
%% ------------------------------------------------------------------
|
|
||||||
|
|
||||||
start_link() ->
|
|
||||||
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
|
||||||
|
|
||||||
%%TODO: fix later...
|
|
||||||
lookup(Key) -> {ok, Key}.
|
|
||||||
|
|
||||||
%% ------------------------------------------------------------------
|
|
||||||
%% gen_server Function Definitions
|
|
||||||
%% ------------------------------------------------------------------
|
|
||||||
|
|
||||||
init(_Args) ->
|
|
||||||
ets:new(?MODULE, [set, protected, named_table]),
|
|
||||||
%%TODO: Load application config.
|
|
||||||
{ok, none}.
|
|
||||||
|
|
||||||
handle_call(_Request, _From, State) ->
|
|
||||||
{reply, ok, State}.
|
|
||||||
|
|
||||||
handle_cast(_Msg, State) ->
|
|
||||||
{noreply, State}.
|
|
||||||
|
|
||||||
handle_info(_Info, State) ->
|
|
||||||
{noreply, State}.
|
|
||||||
|
|
||||||
terminate(_Reason, _State) ->
|
|
||||||
ok.
|
|
||||||
|
|
||||||
code_change(_OldVsn, State, _Extra) ->
|
|
||||||
{ok, State}.
|
|
||||||
|
|
||||||
%% ------------------------------------------------------------------
|
|
||||||
%% Internal Function Definitions
|
|
||||||
%% ------------------------------------------------------------------
|
|
||||||
|
|
|
@ -1,93 +0,0 @@
|
||||||
%%-----------------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2012-2015, Feng Lee <feng@emqtt.io>
|
|
||||||
%%
|
|
||||||
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
%% of this software and associated documentation files (the "Software"), to deal
|
|
||||||
%% in the Software without restriction, including without limitation the rights
|
|
||||||
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
%% copies of the Software, and to permit persons to whom the Software is
|
|
||||||
%% furnished to do so, subject to the following conditions:
|
|
||||||
%%
|
|
||||||
%% The above copyright notice and this permission notice shall be included in all
|
|
||||||
%% copies or substantial portions of the Software.
|
|
||||||
%%
|
|
||||||
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
%% SOFTWARE.
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(emqtt_ctl).
|
|
||||||
|
|
||||||
-author('feng@emqtt.io').
|
|
||||||
|
|
||||||
-include("emqtt.hrl").
|
|
||||||
|
|
||||||
-define(PRINT_MSG(Msg),
|
|
||||||
io:format(Msg)).
|
|
||||||
|
|
||||||
-define(PRINT(Format, Args),
|
|
||||||
io:format(Format, Args)).
|
|
||||||
|
|
||||||
-export([status/1,
|
|
||||||
cluster/1,
|
|
||||||
useradd/1,
|
|
||||||
userdel/1]).
|
|
||||||
|
|
||||||
status([]) ->
|
|
||||||
{InternalStatus, _ProvidedStatus} = init:get_status(),
|
|
||||||
?PRINT("Node ~p is ~p~n", [node(), InternalStatus]),
|
|
||||||
case lists:keysearch(emqtt, 1, application:which_applications()) of
|
|
||||||
false ->
|
|
||||||
?PRINT_MSG("emqtt is not running~n");
|
|
||||||
{value,_Version} ->
|
|
||||||
?PRINT_MSG("emqtt is running~n")
|
|
||||||
end.
|
|
||||||
|
|
||||||
cluster([]) ->
|
|
||||||
Nodes = [node()|nodes()],
|
|
||||||
?PRINT("cluster nodes: ~p~n", [Nodes]);
|
|
||||||
|
|
||||||
cluster([SNode]) ->
|
|
||||||
Node = node_name(SNode),
|
|
||||||
case net_adm:ping(Node) of
|
|
||||||
pong ->
|
|
||||||
application:stop(emqtt),
|
|
||||||
application:stop(esockd),
|
|
||||||
mnesia:stop(),
|
|
||||||
mnesia:start(),
|
|
||||||
mnesia:change_config(extra_db_nodes, [Node]),
|
|
||||||
application:start(esockd),
|
|
||||||
application:start(emqtt),
|
|
||||||
?PRINT("cluster with ~p successfully.~n", [Node]);
|
|
||||||
pang ->
|
|
||||||
?PRINT("failed to connect to ~p~n", [Node])
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
useradd([Username, Password]) ->
|
|
||||||
?PRINT("~p", [emqtt_auth:add(list_to_binary(Username), list_to_binary(Password))]).
|
|
||||||
|
|
||||||
userdel([Username]) ->
|
|
||||||
?PRINT("~p", [emqtt_auth:delete(list_to_binary(Username))]).
|
|
||||||
|
|
||||||
node_name(SNode) ->
|
|
||||||
SNode1 =
|
|
||||||
case string:tokens(SNode, "@") of
|
|
||||||
[_Node, _Server] ->
|
|
||||||
SNode;
|
|
||||||
_ ->
|
|
||||||
case net_kernel:longnames() of
|
|
||||||
true ->
|
|
||||||
SNode ++ "@" ++ inet_db:gethostname() ++
|
|
||||||
"." ++ inet_db:res_option(domain);
|
|
||||||
false ->
|
|
||||||
SNode ++ "@" ++ inet_db:gethostname();
|
|
||||||
_ ->
|
|
||||||
SNode
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
list_to_atom(SNode1).
|
|
|
@ -1,40 +0,0 @@
|
||||||
%%-----------------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2012-2015, Feng Lee <feng@emqtt.io>
|
|
||||||
%%
|
|
||||||
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
%% of this software and associated documentation files (the "Software"), to deal
|
|
||||||
%% in the Software without restriction, including without limitation the rights
|
|
||||||
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
%% copies of the Software, and to permit persons to whom the Software is
|
|
||||||
%% furnished to do so, subject to the following conditions:
|
|
||||||
%%
|
|
||||||
%% The above copyright notice and this permission notice shall be included in all
|
|
||||||
%% copies or substantial portions of the Software.
|
|
||||||
%%
|
|
||||||
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
%% SOFTWARE.
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(emqtt_db).
|
|
||||||
|
|
||||||
-author('feng@emqtt.io').
|
|
||||||
|
|
||||||
-export([init/0, stop/0]).
|
|
||||||
|
|
||||||
init() ->
|
|
||||||
case mnesia:system_info(extra_db_nodes) of
|
|
||||||
[] -> mnesia:create_schema([node()]);
|
|
||||||
_ -> ok
|
|
||||||
end,
|
|
||||||
ok = mnesia:start(),
|
|
||||||
mnesia:wait_for_tables(mnesia:system_info(local_tables), infinity).
|
|
||||||
|
|
||||||
stop() ->
|
|
||||||
mnesia:stop().
|
|
||||||
|
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2012-2015, Feng Lee <feng@emqtt.io>
|
|
||||||
%%
|
|
||||||
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
%% of this software and associated documentation files (the "Software"), to deal
|
|
||||||
%% in the Software without restriction, including without limitation the rights
|
|
||||||
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
%% copies of the Software, and to permit persons to whom the Software is
|
|
||||||
%% furnished to do so, subject to the following conditions:
|
|
||||||
%%
|
|
||||||
%% The above copyright notice and this permission notice shall be included in all
|
|
||||||
%% copies or substantial portions of the Software.
|
|
||||||
%%
|
|
||||||
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
%% SOFTWARE.
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(emqtt_http).
|
|
||||||
|
|
||||||
-author('feng@emqtt.io').
|
|
||||||
|
|
||||||
-include("emqtt.hrl").
|
|
||||||
|
|
||||||
-include("emqtt_packet.hrl").
|
|
||||||
|
|
||||||
-import(proplists, [get_value/2, get_value/3]).
|
|
||||||
|
|
||||||
-export([handle/1]).
|
|
||||||
|
|
||||||
handle(Req) ->
|
|
||||||
case authorized(Req) of
|
|
||||||
true ->
|
|
||||||
Path = Req:get(path),
|
|
||||||
Method = Req:get(method),
|
|
||||||
handle(Method, Path, Req);
|
|
||||||
false ->
|
|
||||||
error_logger:info_msg("Fobbidden"),
|
|
||||||
Req:respond({401, [], <<"Fobbiden">>})
|
|
||||||
end.
|
|
||||||
|
|
||||||
handle('POST', "/mqtt/publish", Req) ->
|
|
||||||
Params = mochiweb_request:parse_post(Req),
|
|
||||||
lager:info("HTTP Publish: ~p~n", [Params]),
|
|
||||||
Qos = int(get_value("qos", Params, "0")),
|
|
||||||
Retain = bool(get_value("retain", Params, "0")),
|
|
||||||
Topic = list_to_binary(get_value("topic", Params)),
|
|
||||||
Message = list_to_binary(get_value("message", Params)),
|
|
||||||
case {validate(qos, Qos), validate(topic, Topic)} of
|
|
||||||
{true, true} ->
|
|
||||||
emqtt_router:route(
|
|
||||||
#mqtt_message { qos = Qos,
|
|
||||||
retain = Retain,
|
|
||||||
topic = Topic,
|
|
||||||
payload = Message }),
|
|
||||||
Req:ok({"text/plan", <<"ok\n">>});
|
|
||||||
{false, _} ->
|
|
||||||
Req:respond({400, [], <<"Bad QoS">>});
|
|
||||||
{_, false} ->
|
|
||||||
Req:respond({400, [], <<"Bad Topic">>})
|
|
||||||
end;
|
|
||||||
|
|
||||||
handle(_Method, _Path, Req) ->
|
|
||||||
Req:not_found().
|
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
%% basic authorization
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
authorized(Req) ->
|
|
||||||
case mochiweb_request:get_header_value("Authorization", Req) of
|
|
||||||
undefined ->
|
|
||||||
false;
|
|
||||||
"Basic " ++ BasicAuth ->
|
|
||||||
emqtt_auth:check(user_passwd(BasicAuth))
|
|
||||||
end.
|
|
||||||
|
|
||||||
user_passwd(BasicAuth) ->
|
|
||||||
list_to_tuple(binary:split(base64:decode(BasicAuth), <<":">>)).
|
|
||||||
|
|
||||||
validate(qos, Qos) ->
|
|
||||||
(Qos >= ?QOS_0) and (Qos =< ?QOS_2);
|
|
||||||
|
|
||||||
validate(topic, Topic) ->
|
|
||||||
emqtt_topic:validate({name, Topic}).
|
|
||||||
|
|
||||||
int(S) -> list_to_integer(S).
|
|
||||||
|
|
||||||
bool("0") -> false;
|
|
||||||
bool("1") -> true.
|
|
||||||
|
|
|
@ -1,133 +0,0 @@
|
||||||
%%-----------------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2012-2015, Feng Lee <feng@emqtt.io>
|
|
||||||
%%
|
|
||||||
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
%% of this software and associated documentation files (the "Software"), to deal
|
|
||||||
%% in the Software without restriction, including without limitation the rights
|
|
||||||
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
%% copies of the Software, and to permit persons to whom the Software is
|
|
||||||
%% furnished to do so, subject to the following conditions:
|
|
||||||
%%
|
|
||||||
%% The above copyright notice and this permission notice shall be included in all
|
|
||||||
%% copies or substantial portions of the Software.
|
|
||||||
%%
|
|
||||||
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
%% SOFTWARE.
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(emqtt_message).
|
|
||||||
|
|
||||||
-author('feng@emqtt.io').
|
|
||||||
|
|
||||||
-include("emqtt.hrl").
|
|
||||||
|
|
||||||
-include("emqtt_packet.hrl").
|
|
||||||
|
|
||||||
-export([from_packet/1, to_packet/1]).
|
|
||||||
|
|
||||||
-export([set_flag/1, set_flag/2, unset_flag/1, unset_flag/2]).
|
|
||||||
|
|
||||||
-export([dump/1]).
|
|
||||||
|
|
||||||
%%----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-ifdef(use_specs).
|
|
||||||
|
|
||||||
-spec( from_packet( mqtt_packet() ) -> mqtt_message() | undefined ).
|
|
||||||
|
|
||||||
-spec( to_packet( mqtt_message() ) -> mqtt_packet() ).
|
|
||||||
|
|
||||||
-sepc( set_flag(atom(), mqtt_message() ) -> mqtt_message().
|
|
||||||
|
|
||||||
-sepc( unset_flag(atom(), mqtt_message() ) -> mqtt_message().
|
|
||||||
|
|
||||||
-endif.
|
|
||||||
|
|
||||||
%%----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
%%
|
|
||||||
%% @doc message from packet
|
|
||||||
%%
|
|
||||||
from_packet(#mqtt_packet{ header = #mqtt_packet_header{ type = ?PUBLISH,
|
|
||||||
retain = Retain,
|
|
||||||
qos = Qos,
|
|
||||||
dup = Dup },
|
|
||||||
variable = #mqtt_packet_publish{ topic_name = Topic,
|
|
||||||
packet_id = PacketId },
|
|
||||||
payload = Payload }) ->
|
|
||||||
#mqtt_message{ msgid = PacketId,
|
|
||||||
qos = Qos,
|
|
||||||
retain = Retain,
|
|
||||||
dup = Dup,
|
|
||||||
topic = Topic,
|
|
||||||
payload = Payload };
|
|
||||||
|
|
||||||
from_packet(#mqtt_packet_connect{ will_flag = false }) ->
|
|
||||||
undefined;
|
|
||||||
|
|
||||||
from_packet(#mqtt_packet_connect{ will_retain = Retain,
|
|
||||||
will_qos = Qos,
|
|
||||||
will_topic = Topic,
|
|
||||||
will_msg = Msg }) ->
|
|
||||||
#mqtt_message{ retain = Retain,
|
|
||||||
qos = Qos,
|
|
||||||
topic = Topic,
|
|
||||||
dup = false,
|
|
||||||
payload = Msg }.
|
|
||||||
|
|
||||||
%%
|
|
||||||
%% @doc message to packet
|
|
||||||
%%
|
|
||||||
to_packet(#mqtt_message{ msgid = MsgId,
|
|
||||||
qos = Qos,
|
|
||||||
retain = Retain,
|
|
||||||
dup = Dup,
|
|
||||||
topic = Topic,
|
|
||||||
payload = Payload }) ->
|
|
||||||
|
|
||||||
PacketId = if
|
|
||||||
Qos =:= ?QOS_0 -> undefined;
|
|
||||||
true -> MsgId
|
|
||||||
end,
|
|
||||||
|
|
||||||
#mqtt_packet{ header = #mqtt_packet_header { type = ?PUBLISH,
|
|
||||||
qos = Qos,
|
|
||||||
retain = Retain,
|
|
||||||
dup = Dup },
|
|
||||||
variable = #mqtt_packet_publish { topic_name = Topic,
|
|
||||||
packet_id = PacketId },
|
|
||||||
payload = Payload }.
|
|
||||||
|
|
||||||
%%
|
|
||||||
%% @doc set dup, retain flag
|
|
||||||
%%
|
|
||||||
set_flag(Msg) ->
|
|
||||||
Msg#mqtt_message{dup = true, retain = true}.
|
|
||||||
set_flag(dup, Msg = #mqtt_message{dup = false}) ->
|
|
||||||
Msg#mqtt_message{dup = true};
|
|
||||||
set_flag(retain, Msg = #mqtt_message{retain = false}) ->
|
|
||||||
Msg#mqtt_message{retain = true};
|
|
||||||
set_flag(Flag, Msg) when Flag =:= dup orelse Flag =:= retain -> Msg.
|
|
||||||
|
|
||||||
unset_flag(Msg) ->
|
|
||||||
Msg#mqtt_message{dup = false, retain = false}.
|
|
||||||
unset_flag(dup, Msg = #mqtt_message{dup = true}) ->
|
|
||||||
Msg#mqtt_message{dup = false};
|
|
||||||
unset_flag(retain, Msg = #mqtt_message{retain = true}) ->
|
|
||||||
Msg#mqtt_message{retain = false};
|
|
||||||
unset_flag(Flag, Msg) when Flag =:= dup orelse Flag =:= retain -> Msg.
|
|
||||||
|
|
||||||
|
|
||||||
%%
|
|
||||||
%% @doc dump message
|
|
||||||
%%
|
|
||||||
dump(#mqtt_message{msgid= MsgId, qos = Qos, retain = Retain, dup = Dup, topic = Topic}) ->
|
|
||||||
io_lib:format("Message(MsgId=~p, Qos=~p, Retain=~s, Dup=~s, Topic=~s)",
|
|
||||||
[ MsgId, Qos, Retain, Dup, Topic ]).
|
|
||||||
|
|
||||||
|
|
|
@ -1,115 +0,0 @@
|
||||||
%%-----------------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2012-2015, Feng Lee <feng@emqtt.io>
|
|
||||||
%%
|
|
||||||
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
%% of this software and associated documentation files (the "Software"), to deal
|
|
||||||
%% in the Software without restriction, including without limitation the rights
|
|
||||||
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
%% copies of the Software, and to permit persons to whom the Software is
|
|
||||||
%% furnished to do so, subject to the following conditions:
|
|
||||||
%%
|
|
||||||
%% The above copyright notice and this permission notice shall be included in all
|
|
||||||
%% copies or substantial portions of the Software.
|
|
||||||
%%
|
|
||||||
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
%% SOFTWARE.
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(emqtt_monitor).
|
|
||||||
|
|
||||||
-author('feng@emqtt.io').
|
|
||||||
|
|
||||||
-behavior(gen_server).
|
|
||||||
|
|
||||||
-export([start_link/0]).
|
|
||||||
|
|
||||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
|
|
||||||
|
|
||||||
-record(state, {ok}).
|
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
|
|
||||||
%% Description: Starts the server
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
start_link() ->
|
|
||||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Function: init(Args) -> {ok, State} |
|
|
||||||
%% {ok, State, Timeout} |
|
|
||||||
%% ignore |
|
|
||||||
%% {stop, Reason}
|
|
||||||
%% Description: Initiates the server
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
init([]) ->
|
|
||||||
erlang:system_monitor(self(), [{long_gc, 5000}, {large_heap, 1000000}, busy_port]),
|
|
||||||
{ok, #state{}}.
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
|
|
||||||
%% {reply, Reply, State, Timeout} |
|
|
||||||
%% {noreply, State} |
|
|
||||||
%% {noreply, State, Timeout} |
|
|
||||||
%% {stop, Reason, Reply, State} |
|
|
||||||
%% {stop, Reason, State}
|
|
||||||
%% Description: Handling call messages
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
handle_call(Request, _From, State) ->
|
|
||||||
lager:error("unexpected request: ~p", [Request]),
|
|
||||||
{reply, {error, unexpected_request}, State}.
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Function: handle_cast(Msg, State) -> {noreply, State} |
|
|
||||||
%% {noreply, State, Timeout} |
|
|
||||||
%% {stop, Reason, State}
|
|
||||||
%% Description: Handling cast messages
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
handle_cast(Msg, State) ->
|
|
||||||
lager:error("unexpected msg: ~p", [Msg]),
|
|
||||||
{noreply, State}.
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Function: handle_info(Info, State) -> {noreply, State} |
|
|
||||||
%% {noreply, State, Timeout} |
|
|
||||||
%% {stop, Reason, State}
|
|
||||||
%% Description: Handling all non call/cast messages
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
handle_info({monitor, GcPid, long_gc, Info}, State) ->
|
|
||||||
lager:error("long_gc: gcpid = ~p, ~p ~n ~p", [GcPid, process_info(GcPid,
|
|
||||||
[registered_name, memory, message_queue_len,heap_size,total_heap_size]), Info]),
|
|
||||||
{noreply, State};
|
|
||||||
|
|
||||||
handle_info({monitor, GcPid, large_heap, Info}, State) ->
|
|
||||||
lager:error("large_heap: gcpid = ~p,~p ~n ~p", [GcPid, process_info(GcPid,
|
|
||||||
[registered_name, memory, message_queue_len,heap_size,total_heap_size]), Info]),
|
|
||||||
{noreply, State};
|
|
||||||
|
|
||||||
handle_info({monitor, SusPid, busy_port, Port}, State) ->
|
|
||||||
lager:error("busy_port: suspid = ~p, port = ~p", [process_info(SusPid,
|
|
||||||
[registered_name, memory, message_queue_len,heap_size,total_heap_size]), Port]),
|
|
||||||
{noreply, State};
|
|
||||||
|
|
||||||
handle_info(Info, State) ->
|
|
||||||
lager:error("unexpected info: ~p", [Info]),
|
|
||||||
{noreply, State}.
|
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Function: terminate(Reason, State) -> void()
|
|
||||||
%% Description: This function is called by a gen_server when it is about to
|
|
||||||
%% terminate. It should be the opposite of Module:init/1 and do any necessary
|
|
||||||
%% cleaning up. When it returns, the gen_server terminates with Reason.
|
|
||||||
%% The return value is ignored.
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
terminate(_Reason, _State) ->
|
|
||||||
ok.
|
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
|
|
||||||
%% Description: Convert process state when code is changed
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
code_change(_OldVsn, State, _Extra) ->
|
|
||||||
{ok, State}.
|
|
||||||
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
-module(emqtt_plugin).
|
|
|
@ -1,74 +0,0 @@
|
||||||
%%-----------------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2012-2015, Feng Lee <feng@emqtt.io>
|
|
||||||
%%
|
|
||||||
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
%% of this software and associated documentation files (the "Software"), to deal
|
|
||||||
%% in the Software without restriction, including without limitation the rights
|
|
||||||
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
%% copies of the Software, and to permit persons to whom the Software is
|
|
||||||
%% furnished to do so, subject to the following conditions:
|
|
||||||
%%
|
|
||||||
%% The above copyright notice and this permission notice shall be included in all
|
|
||||||
%% copies or substantial portions of the Software.
|
|
||||||
%%
|
|
||||||
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
%% SOFTWARE.
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
-module(emqtt_queue).
|
|
||||||
|
|
||||||
-include("emqtt_packet.hrl").
|
|
||||||
|
|
||||||
-export([new/1, new/2, in/3, all/1, clear/1]).
|
|
||||||
|
|
||||||
%%----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-ifdef(use_specs).
|
|
||||||
|
|
||||||
-type(mqtt_queue() :: #mqtt_queue_wrapper{}).
|
|
||||||
|
|
||||||
-spec(new(non_neg_intger()) -> mqtt_queue()).
|
|
||||||
|
|
||||||
-spec(in(binary(), mqtt_message(), mqtt_queue()) -> mqtt_queue()).
|
|
||||||
|
|
||||||
-spec(all(mqtt_queue()) -> list()).
|
|
||||||
|
|
||||||
-spec(clear(mqtt_queue()) -> mqtt_queue()).
|
|
||||||
|
|
||||||
-endif.
|
|
||||||
|
|
||||||
%%----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-define(DEFAULT_MAX_LEN, 1000).
|
|
||||||
|
|
||||||
-record(mqtt_queue_wrapper, { queue = queue:new(), max_len = ?DEFAULT_MAX_LEN, store_qos0 = false }).
|
|
||||||
|
|
||||||
new(MaxLen) -> #mqtt_queue_wrapper{ max_len = MaxLen }.
|
|
||||||
|
|
||||||
new(MaxLen, StoreQos0) -> #mqtt_queue_wrapper{ max_len = MaxLen, store_qos0 = StoreQos0 }.
|
|
||||||
|
|
||||||
in(ClientId, Message = #mqtt_message{qos = Qos},
|
|
||||||
Wrapper = #mqtt_queue_wrapper{ queue = Queue, max_len = MaxLen}) ->
|
|
||||||
case queue:len(Queue) < MaxLen of
|
|
||||||
true ->
|
|
||||||
Wrapper#mqtt_queue_wrapper{ queue = queue:in(Message, Queue) };
|
|
||||||
false -> % full
|
|
||||||
if
|
|
||||||
Qos =:= ?QOS_0 ->
|
|
||||||
lager:warning("Queue ~s drop qos0 message: ~p", [ClientId, Message]),
|
|
||||||
Wrapper;
|
|
||||||
true ->
|
|
||||||
{{value, Msg}, Queue1} = queue:drop(Queue),
|
|
||||||
lager:warning("Queue ~s drop message: ~p", [ClientId, Msg]),
|
|
||||||
Wrapper#mqtt_queue_wrapper{ queue = Queue1 }
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
all(#mqtt_queue_wrapper { queue = Queue }) -> queue:to_list(Queue).
|
|
||||||
|
|
||||||
clear(Queue) -> Queue#mqtt_queue_wrapper{ queue = queue:new() }.
|
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
%%-----------------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2012-2015, Feng Lee <feng@emqtt.io>
|
|
||||||
%%
|
|
||||||
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
%% of this software and associated documentation files (the "Software"), to deal
|
|
||||||
%% in the Software without restriction, including without limitation the rights
|
|
||||||
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
%% copies of the Software, and to permit persons to whom the Software is
|
|
||||||
%% furnished to do so, subject to the following conditions:
|
|
||||||
%%
|
|
||||||
%% The above copyright notice and this permission notice shall be included in all
|
|
||||||
%% copies or substantial portions of the Software.
|
|
||||||
%%
|
|
||||||
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
%% SOFTWARE.
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
-module(emqtt_queue_sup).
|
|
||||||
|
|
||||||
-author('feng@emqtt.io').
|
|
||||||
|
|
||||||
-behavior(supervisor).
|
|
||||||
|
|
||||||
-export([start_link/0, start_queue/0, init/1]).
|
|
||||||
|
|
||||||
start_link() ->
|
|
||||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
|
||||||
|
|
||||||
start_queue() ->
|
|
||||||
supervisor:start_child(?MODULE, []).
|
|
||||||
|
|
||||||
init([]) ->
|
|
||||||
{ok, {{simple_one_for_one, 0, 1},
|
|
||||||
[{queue, {emqtt_queue, start_link, []},
|
|
||||||
transient, 10000, worker, [emqtt_queue]}]}}.
|
|
||||||
|
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
%%-----------------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2012-2015, Feng Lee <feng@emqtt.io>
|
|
||||||
%%
|
|
||||||
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
%% of this software and associated documentation files (the "Software"), to deal
|
|
||||||
%% in the Software without restriction, including without limitation the rights
|
|
||||||
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
%% copies of the Software, and to permit persons to whom the Software is
|
|
||||||
%% furnished to do so, subject to the following conditions:
|
|
||||||
%%
|
|
||||||
%% The above copyright notice and this permission notice shall be included in all
|
|
||||||
%% copies or substantial portions of the Software.
|
|
||||||
%%
|
|
||||||
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
%% SOFTWARE.
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(emqtt_session_sup).
|
|
||||||
|
|
||||||
-author('feng@emqtt.io').
|
|
||||||
|
|
||||||
-behavior(supervisor).
|
|
||||||
|
|
||||||
-export([start_link/1, start_session/2]).
|
|
||||||
|
|
||||||
-export([init/1]).
|
|
||||||
|
|
||||||
%%----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-ifdef(use_specs).
|
|
||||||
|
|
||||||
-spec(start_link/1 :: (list(tuple())) -> {ok, pid()}).
|
|
||||||
|
|
||||||
-spec(start_session/2 :: (binary(), pid()) -> {ok, pid()}).
|
|
||||||
|
|
||||||
-endif.
|
|
||||||
|
|
||||||
%%----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
start_link(SessOpts) ->
|
|
||||||
supervisor:start_link({local, ?MODULE}, ?MODULE, [SessOpts]).
|
|
||||||
|
|
||||||
start_session(ClientId, ClientPid) ->
|
|
||||||
supervisor:start_child(?MODULE, [ClientId, ClientPid]).
|
|
||||||
|
|
||||||
init([SessOpts]) ->
|
|
||||||
{ok, {{simple_one_for_one, 0, 1},
|
|
||||||
[{session, {emqtt_session, start_link, [SessOpts]},
|
|
||||||
transient, 10000, worker, [emqtt_session]}]}}.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
%%-----------------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2012-2015, Feng Lee <feng@emqtt.io>
|
|
||||||
%%
|
|
||||||
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
%% of this software and associated documentation files (the "Software"), to deal
|
|
||||||
%% in the Software without restriction, including without limitation the rights
|
|
||||||
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
%% copies of the Software, and to permit persons to whom the Software is
|
|
||||||
%% furnished to do so, subject to the following conditions:
|
|
||||||
%%
|
|
||||||
%% The above copyright notice and this permission notice shall be included in all
|
|
||||||
%% copies or substantial portions of the Software.
|
|
||||||
%%
|
|
||||||
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
%% SOFTWARE.
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(emqtt_sup).
|
|
||||||
|
|
||||||
-author('feng@emqtt.io').
|
|
||||||
|
|
||||||
-include("emqtt.hrl").
|
|
||||||
|
|
||||||
-behaviour(supervisor).
|
|
||||||
|
|
||||||
%% API
|
|
||||||
-export([start_link/0,
|
|
||||||
start_child/1,
|
|
||||||
start_child/2]).
|
|
||||||
|
|
||||||
%% Supervisor callbacks
|
|
||||||
-export([init/1]).
|
|
||||||
|
|
||||||
%% Helper macro for declaring children of supervisor
|
|
||||||
-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).
|
|
||||||
|
|
||||||
%% ===================================================================
|
|
||||||
%% API functions
|
|
||||||
%% ===================================================================
|
|
||||||
start_link() ->
|
|
||||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
|
||||||
|
|
||||||
start_child(ChildSpec) when is_tuple(ChildSpec) ->
|
|
||||||
supervisor:start_child(?MODULE, ChildSpec).
|
|
||||||
|
|
||||||
%%
|
|
||||||
%% start_child(Mod::atom(), Type::type()) -> {ok, pid()}
|
|
||||||
%% @type type() = worker | supervisor
|
|
||||||
%%
|
|
||||||
start_child(Mod, Type) when is_atom(Mod) and is_atom(Type) ->
|
|
||||||
supervisor:start_child(?MODULE, ?CHILD(Mod, Type)).
|
|
||||||
|
|
||||||
%% ===================================================================
|
|
||||||
%% Supervisor callbacks
|
|
||||||
%% ===================================================================
|
|
||||||
|
|
||||||
init([]) ->
|
|
||||||
{ok, { {one_for_all, 5, 10}, [] } }.
|
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
%%-----------------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2012-2015, Feng Lee <feng@emqtt.io>
|
|
||||||
%%
|
|
||||||
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
%% of this software and associated documentation files (the "Software"), to deal
|
|
||||||
%% in the Software without restriction, including without limitation the rights
|
|
||||||
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
%% copies of the Software, and to permit persons to whom the Software is
|
|
||||||
%% furnished to do so, subject to the following conditions:
|
|
||||||
%%
|
|
||||||
%% The above copyright notice and this permission notice shall be included in all
|
|
||||||
%% copies or substantial portions of the Software.
|
|
||||||
%%
|
|
||||||
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
%% SOFTWARE.
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(emqtt_throttle).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
%%-----------------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2012-2015, Feng Lee <feng@emqtt.io>
|
|
||||||
%%
|
|
||||||
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
%% of this software and associated documentation files (the "Software"), to deal
|
|
||||||
%% in the Software without restriction, including without limitation the rights
|
|
||||||
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
%% copies of the Software, and to permit persons to whom the Software is
|
|
||||||
%% furnished to do so, subject to the following conditions:
|
|
||||||
%%
|
|
||||||
%% The above copyright notice and this permission notice shall be included in all
|
|
||||||
%% copies or substantial portions of the Software.
|
|
||||||
%%
|
|
||||||
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
%% SOFTWARE.
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(emqtt_vm).
|
|
|
@ -59,6 +59,13 @@
|
||||||
|
|
||||||
-type mqtt_session() :: #mqtt_session{}.
|
-type mqtt_session() :: #mqtt_session{}.
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% MQTT Retained Message
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-record(mqtt_retained, {topic, qos, payload}).
|
||||||
|
|
||||||
|
-type mqtt_retained() :: #mqtt_retained{}.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% MQTT User Management
|
%% MQTT User Management
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
|
@ -1,12 +1,12 @@
|
||||||
{application, emqtt,
|
{application, emqttd,
|
||||||
[
|
[
|
||||||
{description, "Erlang MQTT Broker"},
|
{description, "Erlang MQTT Broker"},
|
||||||
{vsn, "0.4.0"},
|
{vsn, "0.5.0"},
|
||||||
{modules, []},
|
{modules, []},
|
||||||
{registered, []},
|
{registered, []},
|
||||||
{applications, [kernel,
|
{applications, [kernel,
|
||||||
stdlib]},
|
stdlib]},
|
||||||
{mod, {emqtt_app, []}},
|
{mod, {emqttd_app, []}},
|
||||||
{env, []}
|
{env, []}
|
||||||
]}.
|
]}.
|
||||||
|
|
|
@ -20,11 +20,13 @@
|
||||||
%%% SOFTWARE.
|
%%% SOFTWARE.
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% @doc
|
%%% @doc
|
||||||
%%% emqtt main module.
|
%%% emqttd main module.
|
||||||
%%%
|
%%%
|
||||||
%%% @end
|
%%% @end
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
-module(emqtt).
|
-module(emqttd).
|
||||||
|
|
||||||
|
-author('feng@emqtt.io').
|
||||||
|
|
||||||
-export([start/0, open/1]).
|
-export([start/0, open/1]).
|
||||||
|
|
||||||
|
@ -36,22 +38,35 @@
|
||||||
{nodelay, true}
|
{nodelay, true}
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
-type listener() :: {atom(), inet:port_number(), [esockd:option()]}.
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% @doc
|
||||||
|
%% Start emqttd application.
|
||||||
|
%%
|
||||||
|
%% @end
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
-spec start() -> ok | {error, any()}.
|
-spec start() -> ok | {error, any()}.
|
||||||
start() ->
|
start() ->
|
||||||
application:start(emqtt).
|
application:start(emqttd).
|
||||||
|
|
||||||
|
-spec open([listener()] | listener()) -> any().
|
||||||
open(Listeners) when is_list(Listeners) ->
|
open(Listeners) when is_list(Listeners) ->
|
||||||
[open(Listener) || Listener <- Listeners];
|
[open(Listener) || Listener <- Listeners];
|
||||||
|
|
||||||
|
%% open mqtt port
|
||||||
open({mqtt, Port, Options}) ->
|
open({mqtt, Port, Options}) ->
|
||||||
MFArgs = {emqtt_client, start_link, []},
|
MFArgs = {emqttd_client, start_link, []},
|
||||||
esockd:open(mqtt, Port, emqtt_opts:merge(?MQTT_SOCKOPTS, Options) , MFArgs);
|
esockd:open(mqtt, Port, emqttd_opts:merge(?MQTT_SOCKOPTS, Options) , MFArgs);
|
||||||
|
|
||||||
|
%% open mqtt(SSL) port
|
||||||
open({mqtts, Port, Options}) ->
|
open({mqtts, Port, Options}) ->
|
||||||
MFArgs = {emqtt_client, start_link, []},
|
MFArgs = {emqttd_client, start_link, []},
|
||||||
esockd:open(mqtts, Port, emqtt_opts:merge(?MQTT_SOCKOPTS, Options) , MFArgs);
|
esockd:open(mqtts, Port, emqttd_opts:merge(?MQTT_SOCKOPTS, Options) , MFArgs);
|
||||||
|
|
||||||
|
%% open http port
|
||||||
open({http, Port, Options}) ->
|
open({http, Port, Options}) ->
|
||||||
MFArgs = {emqtt_http, handle, []},
|
MFArgs = {emqttd_http, handle, []},
|
||||||
mochiweb:start_http(Port, Options, MFArgs).
|
mochiweb:start_http(Port, Options, MFArgs).
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>
|
||||||
|
%%%
|
||||||
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
%%% in the Software without restriction, including without limitation the rights
|
||||||
|
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
%%% copies of the Software, and to permit persons to whom the Software is
|
||||||
|
%%% furnished to do so, subject to the following conditions:
|
||||||
|
%%%
|
||||||
|
%%% The above copyright notice and this permission notice shall be included in all
|
||||||
|
%%% copies or substantial portions of the Software.
|
||||||
|
%%%
|
||||||
|
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
%%% SOFTWARE.
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @doc
|
||||||
|
%%% emqttd application.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
-module(emqttd_app).
|
||||||
|
|
||||||
|
-author('feng@emqtt.io').
|
||||||
|
|
||||||
|
-define(PRINT_MSG(Msg), io:format(Msg)).
|
||||||
|
|
||||||
|
-define(PRINT(Format, Args), io:format(Format, Args)).
|
||||||
|
|
||||||
|
-behaviour(application).
|
||||||
|
|
||||||
|
%% Application callbacks
|
||||||
|
-export([start/2, stop/1]).
|
||||||
|
|
||||||
|
%%%=============================================================================
|
||||||
|
%%% Application callbacks
|
||||||
|
%%%=============================================================================
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% @private
|
||||||
|
%% @doc
|
||||||
|
%% This function is called whenever an application is started using
|
||||||
|
%% application:start/[1,2], and should start the processes of the
|
||||||
|
%% application. If the application is structured according to the OTP
|
||||||
|
%% design principles as a supervision tree, this means starting the
|
||||||
|
%% top supervisor of the tree.
|
||||||
|
%%
|
||||||
|
%% @end
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-spec start(StartType, StartArgs) -> {ok, pid()} | {ok, pid(), State} | {error, Reason} when
|
||||||
|
StartType :: normal | {takeover, node()} | {failover, node()},
|
||||||
|
StartArgs :: term(),
|
||||||
|
State :: term(),
|
||||||
|
Reason :: term().
|
||||||
|
start(_StartType, _StartArgs) ->
|
||||||
|
print_banner(),
|
||||||
|
{ok, Sup} = emqttd_sup:start_link(),
|
||||||
|
start_servers(Sup),
|
||||||
|
ok = emqttd_mnesia:wait(),
|
||||||
|
{ok, Listeners} = application:get_env(listen),
|
||||||
|
emqttd:open(Listeners),
|
||||||
|
register(emqtt, self()),
|
||||||
|
print_vsn(),
|
||||||
|
{ok, Sup}.
|
||||||
|
|
||||||
|
print_banner() ->
|
||||||
|
?PRINT("starting emqttd on node '~s'~n", [node()]).
|
||||||
|
|
||||||
|
print_vsn() ->
|
||||||
|
{ok, Vsn} = application:get_key(vsn),
|
||||||
|
{ok, Desc} = application:get_key(description),
|
||||||
|
?PRINT("~s ~s is running now~n", [Desc, Vsn]).
|
||||||
|
|
||||||
|
start_servers(Sup) ->
|
||||||
|
{ok, SessOpts} = application:get_env(session),
|
||||||
|
{ok, RetainOpts} = application:get_env(retain),
|
||||||
|
{ok, BrokerOpts} = application:get_env(broker),
|
||||||
|
{ok, MetricOpts} = application:get_env(metrics),
|
||||||
|
lists:foreach(
|
||||||
|
fun({Name, F}) when is_function(F) ->
|
||||||
|
?PRINT("~s is starting...", [Name]),
|
||||||
|
F(),
|
||||||
|
?PRINT_MSG("[done]~n");
|
||||||
|
({Name, Server}) ->
|
||||||
|
?PRINT("~s is starting...", [Name]),
|
||||||
|
start_child(Sup, Server),
|
||||||
|
?PRINT_MSG("[done]~n");
|
||||||
|
({Name, Server, Opts}) ->
|
||||||
|
?PRINT("~s is starting...", [ Name]),
|
||||||
|
start_child(Sup, Server, Opts),
|
||||||
|
?PRINT_MSG("[done]~n")
|
||||||
|
end,
|
||||||
|
[{"emqttd config", emqttd_config},
|
||||||
|
{"emqttd server", emqttd_server, RetainOpts},
|
||||||
|
{"emqttd client manager", emqttd_cm},
|
||||||
|
{"emqttd session manager", emqttd_sm},
|
||||||
|
{"emqttd session supervisor", {supervisor, emqttd_session_sup}, SessOpts},
|
||||||
|
{"emqttd auth", emqttd_auth},
|
||||||
|
{"emqttd pubsub", emqttd_pubsub},
|
||||||
|
{"emqttd router", emqttd_router},
|
||||||
|
{"emqttd broker", emqttd_broker, BrokerOpts},
|
||||||
|
{"emqttd metrics", emqttd_metrics, MetricOpts},
|
||||||
|
{"emqttd monitor", emqttd_monitor}
|
||||||
|
]).
|
||||||
|
|
||||||
|
start_child(Sup, {supervisor, Name}) ->
|
||||||
|
supervisor:start_child(Sup, supervisor_spec(Name));
|
||||||
|
start_child(Sup, Name) when is_atom(Name) ->
|
||||||
|
{ok, _ChiId} = supervisor:start_child(Sup, worker_spec(Name)).
|
||||||
|
|
||||||
|
start_child(Sup, {supervisor, Name}, Opts) ->
|
||||||
|
supervisor:start_child(Sup, supervisor_spec(Name, Opts));
|
||||||
|
start_child(Sup, Name, Opts) when is_atom(Name) ->
|
||||||
|
{ok, _ChiId} = supervisor:start_child(Sup, worker_spec(Name, Opts)).
|
||||||
|
|
||||||
|
%%TODO: refactor...
|
||||||
|
supervisor_spec(Name) ->
|
||||||
|
{Name,
|
||||||
|
{Name, start_link, []},
|
||||||
|
permanent, infinity, supervisor, [Name]}.
|
||||||
|
|
||||||
|
supervisor_spec(Name, Opts) ->
|
||||||
|
{Name,
|
||||||
|
{Name, start_link, [Opts]},
|
||||||
|
permanent, infinity, supervisor, [Name]}.
|
||||||
|
|
||||||
|
worker_spec(Name) ->
|
||||||
|
{Name,
|
||||||
|
{Name, start_link, []},
|
||||||
|
permanent, 5000, worker, [Name]}.
|
||||||
|
worker_spec(Name, Opts) ->
|
||||||
|
{Name,
|
||||||
|
{Name, start_link, [Opts]},
|
||||||
|
permanent, 5000, worker, [Name]}.
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% @private
|
||||||
|
%% @doc
|
||||||
|
%% This function is called whenever an application has stopped. It
|
||||||
|
%% is intended to be the opposite of Module:start/2 and should do
|
||||||
|
%% any necessary cleaning up. The return value is ignored.
|
||||||
|
%%
|
||||||
|
%% @end
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-spec stop(State :: term()) -> term().
|
||||||
|
stop(_State) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>
|
||||||
|
%%%
|
||||||
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
%%% in the Software without restriction, including without limitation the rights
|
||||||
|
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
%%% copies of the Software, and to permit persons to whom the Software is
|
||||||
|
%%% furnished to do so, subject to the following conditions:
|
||||||
|
%%%
|
||||||
|
%%% The above copyright notice and this permission notice shall be included in all
|
||||||
|
%%% copies or substantial portions of the Software.
|
||||||
|
%%%
|
||||||
|
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
%%% SOFTWARE.
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @doc
|
||||||
|
%%% emqttd authentication.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
-module(emqttd_auth).
|
||||||
|
|
||||||
|
-author('feng@emqtt.io').
|
||||||
|
|
||||||
|
-include("emqttd.hrl").
|
||||||
|
|
||||||
|
-export([start_link/0,
|
||||||
|
add/2,
|
||||||
|
check/1, check/2,
|
||||||
|
delete/1]).
|
||||||
|
|
||||||
|
-behavior(gen_server).
|
||||||
|
|
||||||
|
-export([init/1,
|
||||||
|
handle_call/3,
|
||||||
|
handle_cast/2,
|
||||||
|
handle_info/2,
|
||||||
|
terminate/2,
|
||||||
|
code_change/3]).
|
||||||
|
|
||||||
|
-define(TAB, ?MODULE).
|
||||||
|
|
||||||
|
start_link() ->
|
||||||
|
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||||
|
|
||||||
|
-spec check({Usename :: binary(), Password :: binary()}) -> true | false.
|
||||||
|
check({Username, Password}) ->
|
||||||
|
execute(check, [Username, Password]).
|
||||||
|
|
||||||
|
-spec check(Usename :: binary(), Password :: binary()) -> true | false.
|
||||||
|
check(Username, Password) ->
|
||||||
|
execute(check, [Username, Password]).
|
||||||
|
|
||||||
|
-spec add(Usename :: binary(), Password :: binary()) -> ok.
|
||||||
|
add(Username, Password) ->
|
||||||
|
execute(add, [Username, Password]).
|
||||||
|
|
||||||
|
-spec delete(Username :: binary()) -> ok.
|
||||||
|
delete(Username) ->
|
||||||
|
execute(delete, [Username]).
|
||||||
|
|
||||||
|
execute(F, Args) ->
|
||||||
|
[{_, M}] = ets:lookup(?TAB, mod),
|
||||||
|
apply(M, F, Args).
|
||||||
|
|
||||||
|
init([]) ->
|
||||||
|
{ok, {Name, Opts}} = application:get_env(auth),
|
||||||
|
AuthMod = authmod(Name),
|
||||||
|
ok = AuthMod:init(Opts),
|
||||||
|
ets:new(?TAB, [named_table, protected]),
|
||||||
|
ets:insert(?TAB, {mod, AuthMod}),
|
||||||
|
{ok, undefined}.
|
||||||
|
|
||||||
|
authmod(Name) when is_atom(Name) ->
|
||||||
|
list_to_atom(lists:concat(["emqttd_auth_", Name])).
|
||||||
|
|
||||||
|
handle_call(Req, _From, State) ->
|
||||||
|
{stop, {badreq, Req}, State}.
|
||||||
|
|
||||||
|
handle_cast(Msg, State) ->
|
||||||
|
{stop, {badmsg, Msg}, State}.
|
||||||
|
|
||||||
|
handle_info(Info, State) ->
|
||||||
|
{stop, {badinfo, Info}, State}.
|
||||||
|
|
||||||
|
terminate(_Reason, _State) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
|
{ok, State}.
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>
|
||||||
|
%%%
|
||||||
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
%%% in the Software without restriction, including without limitation the rights
|
||||||
|
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
%%% copies of the Software, and to permit persons to whom the Software is
|
||||||
|
%%% furnished to do so, subject to the following conditions:
|
||||||
|
%%%
|
||||||
|
%%% The above copyright notice and this permission notice shall be included in all
|
||||||
|
%%% copies or substantial portions of the Software.
|
||||||
|
%%%
|
||||||
|
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
%%% SOFTWARE.
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @doc
|
||||||
|
%%% emqttd anonymous authentication.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
-module(emqttd_auth_anonymous).
|
||||||
|
|
||||||
|
-author('feng@emqtt.io').
|
||||||
|
|
||||||
|
-export([init/1, add/2, check/2, delete/1]).
|
||||||
|
|
||||||
|
init(_Opts) -> ok.
|
||||||
|
|
||||||
|
check(_, _) -> true.
|
||||||
|
|
||||||
|
add(_, _) -> ok.
|
||||||
|
|
||||||
|
delete(_Username) -> ok.
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>
|
||||||
|
%%%
|
||||||
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
%%% in the Software without restriction, including without limitation the rights
|
||||||
|
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
%%% copies of the Software, and to permit persons to whom the Software is
|
||||||
|
%%% furnished to do so, subject to the following conditions:
|
||||||
|
%%%
|
||||||
|
%%% The above copyright notice and this permission notice shall be included in all
|
||||||
|
%%% copies or substantial portions of the Software.
|
||||||
|
%%%
|
||||||
|
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
%%% SOFTWARE.
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @doc
|
||||||
|
%%% emqttd internal authentication.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
-module(emqttd_auth_internal).
|
||||||
|
|
||||||
|
-author('feng@emqtt.io').
|
||||||
|
|
||||||
|
-include("emqttd.hrl").
|
||||||
|
|
||||||
|
-export([init/1, add/2, check/2, delete/1]).
|
||||||
|
|
||||||
|
-define(USER_TAB, mqtt_user).
|
||||||
|
|
||||||
|
init(_Opts) ->
|
||||||
|
mnesia:create_table(?USER_TAB, [
|
||||||
|
{ram_copies, [node()]},
|
||||||
|
{attributes, record_info(fields, mqtt_user)}]),
|
||||||
|
mnesia:add_table_copy(?USER_TAB, node(), ram_copies),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
check(undefined, _) -> false;
|
||||||
|
|
||||||
|
check(_, undefined) -> false;
|
||||||
|
|
||||||
|
check(Username, Password) when is_binary(Username), is_binary(Password) ->
|
||||||
|
PasswdHash = crypto:hash(md5, Password),
|
||||||
|
case mnesia:dirty_read(?USER_TAB, Username) of
|
||||||
|
[#mqtt_user{passwdhash=PasswdHash}] -> true;
|
||||||
|
_ -> false
|
||||||
|
end.
|
||||||
|
|
||||||
|
add(Username, Password) when is_binary(Username) and is_binary(Password) ->
|
||||||
|
mnesia:dirty_write(
|
||||||
|
#mqtt_user{
|
||||||
|
username=Username,
|
||||||
|
passwdhash=crypto:hash(md5, Password)
|
||||||
|
}
|
||||||
|
).
|
||||||
|
|
||||||
|
delete(Username) when is_binary(Username) ->
|
||||||
|
mnesia:dirty_delete(?USER_TAB, Username).
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>
|
||||||
|
%%%
|
||||||
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
%%% in the Software without restriction, including without limitation the rights
|
||||||
|
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
%%% copies of the Software, and to permit persons to whom the Software is
|
||||||
|
%%% furnished to do so, subject to the following conditions:
|
||||||
|
%%%
|
||||||
|
%%% The above copyright notice and this permission notice shall be included in all
|
||||||
|
%%% copies or substantial portions of the Software.
|
||||||
|
%%%
|
||||||
|
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
%%% SOFTWARE.
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @doc
|
||||||
|
%%% emqttd bridge.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
-module(emqttd_bridge).
|
||||||
|
|
||||||
|
-author('feng@emqtt.io').
|
||||||
|
|
||||||
|
-behaviour(gen_server).
|
||||||
|
|
||||||
|
-include("emqttd_packet.hrl").
|
||||||
|
|
||||||
|
%% API Function Exports
|
||||||
|
-export([start_link/2]).
|
||||||
|
|
||||||
|
%% gen_server Function Exports
|
||||||
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||||
|
terminate/2, code_change/3]).
|
||||||
|
|
||||||
|
-record(state, {node, local_topic, status = running}).
|
||||||
|
|
||||||
|
%%%=============================================================================
|
||||||
|
%%% API
|
||||||
|
%%%=============================================================================
|
||||||
|
|
||||||
|
start_link(Node, LocalTopic) ->
|
||||||
|
gen_server:start_link(?MODULE, [Node, LocalTopic], []).
|
||||||
|
|
||||||
|
%%%=============================================================================
|
||||||
|
%%% gen_server callbacks
|
||||||
|
%%%=============================================================================
|
||||||
|
|
||||||
|
init([Node, LocalTopic]) ->
|
||||||
|
emqttd_pubsub:subscribe({LocalTopic, ?QOS_0}, self()),
|
||||||
|
%%TODO: monitor nodes...
|
||||||
|
{ok, #state{node = Node, local_topic = LocalTopic}}.
|
||||||
|
|
||||||
|
handle_call(_Request, _From, State) ->
|
||||||
|
{reply, ok, State}.
|
||||||
|
|
||||||
|
handle_cast(_Msg, State) ->
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
handle_info({nodedown, Node}, State = #state{node = Node}) ->
|
||||||
|
%%....
|
||||||
|
{noreply, State#state{status = down}};
|
||||||
|
|
||||||
|
handle_info({dispatch, {_From, Msg}}, State = #state{node = Node}) ->
|
||||||
|
%%TODO: CAST
|
||||||
|
rpc:call(Node, emqttd_router, route, [Msg]),
|
||||||
|
{noreply, State};
|
||||||
|
|
||||||
|
handle_info(Info, State) ->
|
||||||
|
lager:error("Unexpected Info: ~p", [Info]),
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
terminate(_Reason, _State) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
|
{ok, State}.
|
||||||
|
|
||||||
|
%%%=============================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%=============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>
|
||||||
|
%%%
|
||||||
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
%%% in the Software without restriction, including without limitation the rights
|
||||||
|
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
%%% copies of the Software, and to permit persons to whom the Software is
|
||||||
|
%%% furnished to do so, subject to the following conditions:
|
||||||
|
%%%
|
||||||
|
%%% The above copyright notice and this permission notice shall be included in all
|
||||||
|
%%% copies or substantial portions of the Software.
|
||||||
|
%%%
|
||||||
|
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
%%% SOFTWARE.
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @doc
|
||||||
|
%%% emqttd bridge supervisor.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
-module(emqttd_bridge_sup).
|
||||||
|
|
||||||
|
-author('feng@emqtt.io').
|
||||||
|
|
||||||
|
-behavior(supervisor).
|
||||||
|
|
||||||
|
-export([start_link/0, start_bridge/2, stop_bridge/2]).
|
||||||
|
|
||||||
|
-export([init/1]).
|
||||||
|
|
||||||
|
%%%=============================================================================
|
||||||
|
%%% API
|
||||||
|
%%%=============================================================================
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% @doc
|
||||||
|
%% Start bridge supervisor.
|
||||||
|
%%
|
||||||
|
%% @end
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
start_link() ->
|
||||||
|
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% @doc
|
||||||
|
%% Start a bridge.
|
||||||
|
%%
|
||||||
|
%% @end
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-spec start_bridge(atom(), binary()) -> {ok, pid()} | {error, any()}.
|
||||||
|
start_bridge(Node, LocalTopic) when is_atom(Node) and is_binary(LocalTopic) ->
|
||||||
|
%%TODO: mv this code to emqttd_bridge???
|
||||||
|
case net_kernel:connect_node(Node) of
|
||||||
|
true ->
|
||||||
|
supervisor:start_child(?MODULE, bridge_spec(Node, LocalTopic));
|
||||||
|
false ->
|
||||||
|
{error, {cannot_connect, Node}}
|
||||||
|
end.
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% @doc
|
||||||
|
%% Stop a bridge.
|
||||||
|
%%
|
||||||
|
%% @end
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-spec stop_bridge(atom(), binary()) -> {ok, pid()} | ok.
|
||||||
|
stop_bridge(Node, LocalTopic) ->
|
||||||
|
ChildId = bridge_id(Node, LocalTopic),
|
||||||
|
case supervisor:terminate_child(ChildId) of
|
||||||
|
ok ->
|
||||||
|
supervisor:delete_child(?MODULE, ChildId);
|
||||||
|
{error, Reason} ->
|
||||||
|
{error, Reason}
|
||||||
|
end.
|
||||||
|
|
||||||
|
%%%=============================================================================
|
||||||
|
%%% Supervisor callbacks
|
||||||
|
%%%=============================================================================
|
||||||
|
|
||||||
|
init([]) ->
|
||||||
|
{ok, {{one_for_one, 10, 100}, []}}.
|
||||||
|
|
||||||
|
bridge_id(Node, LocalTopic) ->
|
||||||
|
{bridge, Node, LocalTopic}.
|
||||||
|
|
||||||
|
bridge_spec(Node, LocalTopic) ->
|
||||||
|
ChildId = bridge_id(Node, LocalTopic),
|
||||||
|
{ChildId, {emqttd_bridge, start_link, [Node, LocalTopic]},
|
||||||
|
transient, 10000, worker, [emqttd_bridge]}.
|
||||||
|
|
||||||
|
|
|
@ -20,21 +20,21 @@
|
||||||
%%% SOFTWARE.
|
%%% SOFTWARE.
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% @doc
|
%%% @doc
|
||||||
%%% emqtt broker.
|
%%% emqttd broker.
|
||||||
%%%
|
%%%
|
||||||
%%% @end
|
%%% @end
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
-module(emqtt_broker).
|
-module(emqttd_broker).
|
||||||
|
|
||||||
-include("emqtt_packet.hrl").
|
-include("emqttd_packet.hrl").
|
||||||
|
|
||||||
-include("emqtt_systop.hrl").
|
-include("emqttd_systop.hrl").
|
||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
|
|
||||||
-define(SERVER, ?MODULE).
|
-define(SERVER, ?MODULE).
|
||||||
|
|
||||||
-define(BROKER_TAB, ?MODULE).
|
-define(BROKER_TAB, mqtt_broker).
|
||||||
|
|
||||||
%% API Function Exports
|
%% API Function Exports
|
||||||
-export([start_link/1]).
|
-export([start_link/1]).
|
||||||
|
@ -56,7 +56,7 @@
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% @doc
|
%% @doc
|
||||||
%% Start emqtt broker.
|
%% Start emqttd broker.
|
||||||
%%
|
%%
|
||||||
%% @end
|
%% @end
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
@ -72,7 +72,7 @@ start_link(Options) ->
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-spec version() -> string().
|
-spec version() -> string().
|
||||||
version() ->
|
version() ->
|
||||||
{ok, Version} = application:get_key(emqtt, vsn), Version.
|
{ok, Version} = application:get_key(emqttd, vsn), Version.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% @doc
|
%% @doc
|
||||||
|
@ -82,7 +82,7 @@ version() ->
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-spec sysdescr() -> string().
|
-spec sysdescr() -> string().
|
||||||
sysdescr() ->
|
sysdescr() ->
|
||||||
{ok, Descr} = application:get_key(emqtt, description), Descr.
|
{ok, Descr} = application:get_key(emqttd, description), Descr.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% @doc
|
%% @doc
|
||||||
|
@ -191,15 +191,15 @@ systop(Name) when is_atom(Name) ->
|
||||||
list_to_binary(lists:concat(["$SYS/brokers/", node(), "/", Name])).
|
list_to_binary(lists:concat(["$SYS/brokers/", node(), "/", Name])).
|
||||||
|
|
||||||
create(Topic) ->
|
create(Topic) ->
|
||||||
emqtt_pubsub:create(Topic).
|
emqttd_pubsub:create(Topic).
|
||||||
|
|
||||||
retain(Topic, Payload) when is_binary(Payload) ->
|
retain(Topic, Payload) when is_binary(Payload) ->
|
||||||
emqtt_router:route(#mqtt_message{retain = true,
|
emqttd_router:route(#mqtt_message{retain = true,
|
||||||
topic = Topic,
|
topic = Topic,
|
||||||
payload = Payload}).
|
payload = Payload}).
|
||||||
|
|
||||||
publish(Topic, Payload) when is_binary(Payload) ->
|
publish(Topic, Payload) when is_binary(Payload) ->
|
||||||
emqtt_router:route(#mqtt_message{topic = Topic,
|
emqttd_router:route(#mqtt_message{topic = Topic,
|
||||||
payload = Payload}).
|
payload = Payload}).
|
||||||
|
|
||||||
uptime(#state{started_at = Ts}) ->
|
uptime(#state{started_at = Ts}) ->
|
|
@ -1,25 +1,30 @@
|
||||||
%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%% Copyright (c) 2012-2015, Feng Lee <feng@emqtt.io>
|
%%% @Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>
|
||||||
%%
|
%%%
|
||||||
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
%% in the Software without restriction, including without limitation the rights
|
%%% in the Software without restriction, including without limitation the rights
|
||||||
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
%% copies of the Software, and to permit persons to whom the Software is
|
%%% copies of the Software, and to permit persons to whom the Software is
|
||||||
%% furnished to do so, subject to the following conditions:
|
%%% furnished to do so, subject to the following conditions:
|
||||||
%%
|
%%%
|
||||||
%% The above copyright notice and this permission notice shall be included in all
|
%%% The above copyright notice and this permission notice shall be included in all
|
||||||
%% copies or substantial portions of the Software.
|
%%% copies or substantial portions of the Software.
|
||||||
%%
|
%%%
|
||||||
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
%% SOFTWARE.
|
%%% SOFTWARE.
|
||||||
%%------------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
-module(emqtt_client).
|
%%% @doc
|
||||||
|
%%% emqttd client.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
-module(emqttd_client).
|
||||||
|
|
||||||
-author('feng@emqtt.io').
|
-author('feng@emqtt.io').
|
||||||
|
|
||||||
|
@ -34,9 +39,9 @@
|
||||||
code_change/3,
|
code_change/3,
|
||||||
terminate/2]).
|
terminate/2]).
|
||||||
|
|
||||||
-include("emqtt.hrl").
|
-include("emqttd.hrl").
|
||||||
|
|
||||||
-include("emqtt_packet.hrl").
|
-include("emqttd_packet.hrl").
|
||||||
|
|
||||||
%%Client State...
|
%%Client State...
|
||||||
-record(state, {transport,
|
-record(state, {transport,
|
||||||
|
@ -60,8 +65,8 @@ info(Pid) ->
|
||||||
init(SockArgs = {Transport, Sock, _SockFun}) ->
|
init(SockArgs = {Transport, Sock, _SockFun}) ->
|
||||||
%transform if ssl.
|
%transform if ssl.
|
||||||
{ok, NewSock} = esockd_connection:accept(SockArgs),
|
{ok, NewSock} = esockd_connection:accept(SockArgs),
|
||||||
{ok, Peername} = emqtt_net:peer_string(Sock),
|
{ok, Peername} = emqttd_net:peer_string(Sock),
|
||||||
{ok, ConnStr} = emqtt_net:connection_string(Sock, inbound),
|
{ok, ConnStr} = emqttd_net:connection_string(Sock, inbound),
|
||||||
lager:info("Connect from ~s", [ConnStr]),
|
lager:info("Connect from ~s", [ConnStr]),
|
||||||
State = control_throttle(#state{transport = Transport,
|
State = control_throttle(#state{transport = Transport,
|
||||||
socket = NewSock,
|
socket = NewSock,
|
||||||
|
@ -70,14 +75,14 @@ init(SockArgs = {Transport, Sock, _SockFun}) ->
|
||||||
await_recv = false,
|
await_recv = false,
|
||||||
conn_state = running,
|
conn_state = running,
|
||||||
conserve = false,
|
conserve = false,
|
||||||
parse_state = emqtt_parser:init(),
|
parse_state = emqttd_parser:init(),
|
||||||
proto_state = emqtt_protocol:init(Transport, NewSock, Peername)}),
|
proto_state = emqttd_protocol:init(Transport, NewSock, Peername)}),
|
||||||
gen_server:enter_loop(?MODULE, [], State, 10000).
|
gen_server:enter_loop(?MODULE, [], State, 10000).
|
||||||
|
|
||||||
%%TODO: Not enough...
|
%%TODO: Not enough...
|
||||||
handle_call(info, _From, State = #state{conn_name=ConnName,
|
handle_call(info, _From, State = #state{conn_name=ConnName,
|
||||||
proto_state = ProtoState}) ->
|
proto_state = ProtoState}) ->
|
||||||
{reply, [{conn_name, ConnName} | emqtt_protocol:info(ProtoState)], State};
|
{reply, [{conn_name, ConnName} | emqttd_protocol:info(ProtoState)], State};
|
||||||
|
|
||||||
handle_call(Req, _From, State) ->
|
handle_call(Req, _From, State) ->
|
||||||
{stop, {badreq, Req}, State}.
|
{stop, {badreq, Req}, State}.
|
||||||
|
@ -92,18 +97,18 @@ handle_info({stop, duplicate_id, _NewPid}, State=#state{proto_state = ProtoState
|
||||||
conn_name=ConnName}) ->
|
conn_name=ConnName}) ->
|
||||||
%% TODO: to...
|
%% TODO: to...
|
||||||
%% need transfer data???
|
%% need transfer data???
|
||||||
%% emqtt_client:transfer(NewPid, Data),
|
%% emqttd_client:transfer(NewPid, Data),
|
||||||
lager:error("Shutdown for duplicate clientid: ~s, conn:~s",
|
lager:error("Shutdown for duplicate clientid: ~s, conn:~s",
|
||||||
[emqtt_protocol:client_id(ProtoState), ConnName]),
|
[emqttd_protocol:client_id(ProtoState), ConnName]),
|
||||||
stop({shutdown, duplicate_id}, State);
|
stop({shutdown, duplicate_id}, State);
|
||||||
|
|
||||||
%%TODO: ok??
|
%%TODO: ok??
|
||||||
handle_info({dispatch, {From, Message}}, #state{proto_state = ProtoState} = State) ->
|
handle_info({dispatch, {From, Message}}, #state{proto_state = ProtoState} = State) ->
|
||||||
{ok, ProtoState1} = emqtt_protocol:send({From, Message}, ProtoState),
|
{ok, ProtoState1} = emqttd_protocol:send({From, Message}, ProtoState),
|
||||||
{noreply, State#state{proto_state = ProtoState1}};
|
{noreply, State#state{proto_state = ProtoState1}};
|
||||||
|
|
||||||
handle_info({redeliver, {?PUBREL, PacketId}}, #state{proto_state = ProtoState} = State) ->
|
handle_info({redeliver, {?PUBREL, PacketId}}, #state{proto_state = ProtoState} = State) ->
|
||||||
{ok, ProtoState1} = emqtt_protocol:redeliver({?PUBREL, PacketId}, ProtoState),
|
{ok, ProtoState1} = emqttd_protocol:redeliver({?PUBREL, PacketId}, ProtoState),
|
||||||
{noreply, State#state{proto_state = ProtoState1}};
|
{noreply, State#state{proto_state = ProtoState1}};
|
||||||
|
|
||||||
handle_info({inet_reply, _Ref, ok}, State) ->
|
handle_info({inet_reply, _Ref, ok}, State) ->
|
||||||
|
@ -111,7 +116,7 @@ handle_info({inet_reply, _Ref, ok}, State) ->
|
||||||
|
|
||||||
handle_info({inet_async, Sock, _Ref, {ok, Data}}, State = #state{peer_name = PeerName, socket = Sock}) ->
|
handle_info({inet_async, Sock, _Ref, {ok, Data}}, State = #state{peer_name = PeerName, socket = Sock}) ->
|
||||||
lager:debug("RECV from ~s: ~p", [PeerName, Data]),
|
lager:debug("RECV from ~s: ~p", [PeerName, Data]),
|
||||||
emqtt_metrics:inc('bytes/received', size(Data)),
|
emqttd_metrics:inc('bytes/received', size(Data)),
|
||||||
process_received_bytes(Data,
|
process_received_bytes(Data,
|
||||||
control_throttle(State #state{await_recv = false}));
|
control_throttle(State #state{await_recv = false}));
|
||||||
|
|
||||||
|
@ -124,11 +129,11 @@ handle_info({inet_reply, _Sock, {error, Reason}}, State = #state{peer_name = Pee
|
||||||
|
|
||||||
handle_info({keepalive, start, TimeoutSec}, State = #state{transport = Transport, socket = Socket}) ->
|
handle_info({keepalive, start, TimeoutSec}, State = #state{transport = Transport, socket = Socket}) ->
|
||||||
lager:info("Client ~s: Start KeepAlive with ~p seconds", [State#state.peer_name, TimeoutSec]),
|
lager:info("Client ~s: Start KeepAlive with ~p seconds", [State#state.peer_name, TimeoutSec]),
|
||||||
KeepAlive = emqtt_keepalive:new({Transport, Socket}, TimeoutSec, {keepalive, timeout}),
|
KeepAlive = emqttd_keepalive:new({Transport, Socket}, TimeoutSec, {keepalive, timeout}),
|
||||||
{noreply, State#state{ keepalive = KeepAlive }};
|
{noreply, State#state{ keepalive = KeepAlive }};
|
||||||
|
|
||||||
handle_info({keepalive, timeout}, State = #state{keepalive = KeepAlive}) ->
|
handle_info({keepalive, timeout}, State = #state{keepalive = KeepAlive}) ->
|
||||||
case emqtt_keepalive:resume(KeepAlive) of
|
case emqttd_keepalive:resume(KeepAlive) of
|
||||||
timeout ->
|
timeout ->
|
||||||
lager:info("Client ~s: Keepalive Timeout!", [State#state.peer_name]),
|
lager:info("Client ~s: Keepalive Timeout!", [State#state.peer_name]),
|
||||||
stop({shutdown, keepalive_timeout}, State#state{keepalive = undefined});
|
stop({shutdown, keepalive_timeout}, State#state{keepalive = undefined});
|
||||||
|
@ -143,11 +148,11 @@ handle_info(Info, State = #state{peer_name = PeerName}) ->
|
||||||
|
|
||||||
terminate(Reason, #state{peer_name = PeerName, keepalive = KeepAlive, proto_state = ProtoState}) ->
|
terminate(Reason, #state{peer_name = PeerName, keepalive = KeepAlive, proto_state = ProtoState}) ->
|
||||||
lager:info("Client ~s: ~p terminated, reason: ~p~n", [PeerName, self(), Reason]),
|
lager:info("Client ~s: ~p terminated, reason: ~p~n", [PeerName, self(), Reason]),
|
||||||
emqtt_keepalive:cancel(KeepAlive),
|
emqttd_keepalive:cancel(KeepAlive),
|
||||||
case {ProtoState, Reason} of
|
case {ProtoState, Reason} of
|
||||||
{undefined, _} -> ok;
|
{undefined, _} -> ok;
|
||||||
{_, {shutdown, Error}} ->
|
{_, {shutdown, Error}} ->
|
||||||
emqtt_protocol:shutdown(Error, ProtoState);
|
emqttd_protocol:shutdown(Error, ProtoState);
|
||||||
{_, _} ->
|
{_, _} ->
|
||||||
ok
|
ok
|
||||||
end,
|
end,
|
||||||
|
@ -165,16 +170,16 @@ process_received_bytes(<<>>, State) ->
|
||||||
process_received_bytes(Bytes, State = #state{parse_state = ParseState,
|
process_received_bytes(Bytes, State = #state{parse_state = ParseState,
|
||||||
proto_state = ProtoState,
|
proto_state = ProtoState,
|
||||||
conn_name = ConnStr}) ->
|
conn_name = ConnStr}) ->
|
||||||
case emqtt_parser:parse(Bytes, ParseState) of
|
case emqttd_parser:parse(Bytes, ParseState) of
|
||||||
{more, ParseState1} ->
|
{more, ParseState1} ->
|
||||||
{noreply,
|
{noreply,
|
||||||
control_throttle(State #state{parse_state = ParseState1}),
|
control_throttle(State #state{parse_state = ParseState1}),
|
||||||
hibernate};
|
hibernate};
|
||||||
{ok, Packet, Rest} ->
|
{ok, Packet, Rest} ->
|
||||||
received_stats(Packet),
|
received_stats(Packet),
|
||||||
case emqtt_protocol:received(Packet, ProtoState) of
|
case emqttd_protocol:received(Packet, ProtoState) of
|
||||||
{ok, ProtoState1} ->
|
{ok, ProtoState1} ->
|
||||||
process_received_bytes(Rest, State#state{parse_state = emqtt_parser:init(),
|
process_received_bytes(Rest, State#state{parse_state = emqttd_parser:init(),
|
||||||
proto_state = ProtoState1});
|
proto_state = ProtoState1});
|
||||||
{error, Error} ->
|
{error, Error} ->
|
||||||
lager:error("MQTT protocol error ~p for connection ~p~n", [Error, ConnStr]),
|
lager:error("MQTT protocol error ~p for connection ~p~n", [Error, ConnStr]),
|
||||||
|
@ -214,21 +219,21 @@ stop(Reason, State ) ->
|
||||||
{stop, Reason, State}.
|
{stop, Reason, State}.
|
||||||
|
|
||||||
received_stats(?PACKET(Type)) ->
|
received_stats(?PACKET(Type)) ->
|
||||||
emqtt_metrics:inc('packets/received'),
|
emqttd_metrics:inc('packets/received'),
|
||||||
inc(Type).
|
inc(Type).
|
||||||
inc(?CONNECT) ->
|
inc(?CONNECT) ->
|
||||||
emqtt_metrics:inc('packets/connect');
|
emqttd_metrics:inc('packets/connect');
|
||||||
inc(?PUBLISH) ->
|
inc(?PUBLISH) ->
|
||||||
emqtt_metrics:inc('messages/received'),
|
emqttd_metrics:inc('messages/received'),
|
||||||
emqtt_metrics:inc('packets/publish/received');
|
emqttd_metrics:inc('packets/publish/received');
|
||||||
inc(?SUBSCRIBE) ->
|
inc(?SUBSCRIBE) ->
|
||||||
emqtt_metrics:inc('packets/subscribe');
|
emqttd_metrics:inc('packets/subscribe');
|
||||||
inc(?UNSUBSCRIBE) ->
|
inc(?UNSUBSCRIBE) ->
|
||||||
emqtt_metrics:inc('packets/unsubscribe');
|
emqttd_metrics:inc('packets/unsubscribe');
|
||||||
inc(?PINGREQ) ->
|
inc(?PINGREQ) ->
|
||||||
emqtt_metrics:inc('packets/pingreq');
|
emqttd_metrics:inc('packets/pingreq');
|
||||||
inc(?DISCONNECT) ->
|
inc(?DISCONNECT) ->
|
||||||
emqtt_metrics:inc('packets/disconnect');
|
emqttd_metrics:inc('packets/disconnect');
|
||||||
inc(_) ->
|
inc(_) ->
|
||||||
ignore.
|
ignore.
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
%%%
|
%%%
|
||||||
%%% @end
|
%%% @end
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
-module(emqtt_cm).
|
-module(emqttd_cm).
|
||||||
|
|
||||||
-author('feng@emqtt.io').
|
-author('feng@emqtt.io').
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@
|
||||||
|
|
||||||
-define(SERVER, ?MODULE).
|
-define(SERVER, ?MODULE).
|
||||||
|
|
||||||
-define(CLIENT_TAB, emqtt_client).
|
-define(CLIENT_TAB, mqtt_client).
|
||||||
|
|
||||||
%% API Exports
|
%% API Exports
|
||||||
-export([start_link/0]).
|
-export([start_link/0]).
|
||||||
|
@ -42,7 +42,6 @@
|
||||||
-export([getstats/0]).
|
-export([getstats/0]).
|
||||||
|
|
||||||
%% gen_server Function Exports
|
%% gen_server Function Exports
|
||||||
|
|
||||||
-export([init/1,
|
-export([init/1,
|
||||||
handle_call/3,
|
handle_call/3,
|
||||||
handle_cast/2,
|
handle_cast/2,
|
||||||
|
@ -74,7 +73,7 @@ start_link() ->
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-spec lookup(ClientId :: binary()) -> pid() | undefined.
|
-spec lookup(ClientId :: binary()) -> pid() | undefined.
|
||||||
lookup(ClientId) when is_binary(ClientId) ->
|
lookup(ClientId) when is_binary(ClientId) ->
|
||||||
case ets:lookup(emqtt_client, ClientId) of
|
case ets:lookup(?CLIENT_TAB, ClientId) of
|
||||||
[{_, Pid, _}] -> Pid;
|
[{_, Pid, _}] -> Pid;
|
||||||
[] -> undefined
|
[] -> undefined
|
||||||
end.
|
end.
|
||||||
|
@ -175,10 +174,10 @@ insert(ClientId, Pid) ->
|
||||||
|
|
||||||
setstats(State = #state{max = Max}) ->
|
setstats(State = #state{max = Max}) ->
|
||||||
Count = ets:info(?CLIENT_TAB, size),
|
Count = ets:info(?CLIENT_TAB, size),
|
||||||
emqtt_broker:setstat('clients/count', Count),
|
emqttd_broker:setstat('clients/count', Count),
|
||||||
if
|
if
|
||||||
Count > Max ->
|
Count > Max ->
|
||||||
emqtt_broker:setstat('clients/max', Count),
|
emqttd_broker:setstat('clients/max', Count),
|
||||||
State#state{max = Count};
|
State#state{max = Count};
|
||||||
true ->
|
true ->
|
||||||
State
|
State
|
|
@ -0,0 +1,78 @@
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>
|
||||||
|
%%%
|
||||||
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
%%% in the Software without restriction, including without limitation the rights
|
||||||
|
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
%%% copies of the Software, and to permit persons to whom the Software is
|
||||||
|
%%% furnished to do so, subject to the following conditions:
|
||||||
|
%%%
|
||||||
|
%%% The above copyright notice and this permission notice shall be included in all
|
||||||
|
%%% copies or substantial portions of the Software.
|
||||||
|
%%%
|
||||||
|
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
%%% SOFTWARE.
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @doc
|
||||||
|
%%% emqttd config manager.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
-module(emqttd_config).
|
||||||
|
|
||||||
|
-define(SERVER, ?MODULE).
|
||||||
|
|
||||||
|
-behaviour(gen_server).
|
||||||
|
|
||||||
|
%% API Function Exports
|
||||||
|
-export([start_link/0, lookup/1]).
|
||||||
|
|
||||||
|
%% gen_server Function Exports
|
||||||
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||||
|
terminate/2, code_change/3]).
|
||||||
|
|
||||||
|
%%%=============================================================================
|
||||||
|
%%% API
|
||||||
|
%%%=============================================================================
|
||||||
|
|
||||||
|
start_link() ->
|
||||||
|
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
||||||
|
|
||||||
|
%%TODO: fix later...
|
||||||
|
lookup(Key) -> {ok, Key}.
|
||||||
|
|
||||||
|
%%%=============================================================================
|
||||||
|
%%% gen_server callbacks
|
||||||
|
%%%=============================================================================
|
||||||
|
|
||||||
|
init(_Args) ->
|
||||||
|
%%TODO: Load application config.
|
||||||
|
ets:new(?MODULE, [set, protected, named_table]),
|
||||||
|
{ok, none}.
|
||||||
|
|
||||||
|
handle_call(_Request, _From, State) ->
|
||||||
|
{reply, ok, State}.
|
||||||
|
|
||||||
|
handle_cast(_Msg, State) ->
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
handle_info(_Info, State) ->
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
terminate(_Reason, _State) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
|
{ok, State}.
|
||||||
|
|
||||||
|
%%%=============================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%=============================================================================
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>
|
||||||
|
%%%
|
||||||
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
%%% in the Software without restriction, including without limitation the rights
|
||||||
|
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
%%% copies of the Software, and to permit persons to whom the Software is
|
||||||
|
%%% furnished to do so, subject to the following conditions:
|
||||||
|
%%%
|
||||||
|
%%% The above copyright notice and this permission notice shall be included in all
|
||||||
|
%%% copies or substantial portions of the Software.
|
||||||
|
%%%
|
||||||
|
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
%%% SOFTWARE.
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @doc
|
||||||
|
%%% emqttd control commands.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
-module(emqttd_ctl).
|
||||||
|
|
||||||
|
-author('feng@emqtt.io').
|
||||||
|
|
||||||
|
-include("emqttd.hrl").
|
||||||
|
|
||||||
|
-define(PRINT_MSG(Msg),
|
||||||
|
io:format(Msg)).
|
||||||
|
|
||||||
|
-define(PRINT(Format, Args),
|
||||||
|
io:format(Format, Args)).
|
||||||
|
|
||||||
|
-export([status/1,
|
||||||
|
cluster/1,
|
||||||
|
useradd/1,
|
||||||
|
userdel/1]).
|
||||||
|
|
||||||
|
%TODO: add comment
|
||||||
|
% bridge
|
||||||
|
% metrics
|
||||||
|
% broker
|
||||||
|
% sockets
|
||||||
|
|
||||||
|
status([]) ->
|
||||||
|
{InternalStatus, _ProvidedStatus} = init:get_status(),
|
||||||
|
?PRINT("Node ~p is ~p~n", [node(), InternalStatus]),
|
||||||
|
case lists:keysearch(emqttd, 1, application:which_applications()) of
|
||||||
|
false ->
|
||||||
|
?PRINT_MSG("emqttd is not running~n");
|
||||||
|
{value,_Version} ->
|
||||||
|
?PRINT_MSG("emqttd is running~n")
|
||||||
|
end.
|
||||||
|
|
||||||
|
cluster([]) ->
|
||||||
|
Nodes = [node()|nodes()],
|
||||||
|
?PRINT("cluster nodes: ~p~n", [Nodes]);
|
||||||
|
|
||||||
|
cluster([SNode]) ->
|
||||||
|
Node = node_name(SNode),
|
||||||
|
case net_adm:ping(Node) of
|
||||||
|
pong ->
|
||||||
|
application:stop(emqttd),
|
||||||
|
application:stop(esockd),
|
||||||
|
mnesia:stop(),
|
||||||
|
mnesia:start(),
|
||||||
|
mnesia:change_config(extra_db_nodes, [Node]),
|
||||||
|
application:start(esockd),
|
||||||
|
application:start(emqttd),
|
||||||
|
?PRINT("cluster with ~p successfully.~n", [Node]);
|
||||||
|
pang ->
|
||||||
|
?PRINT("failed to connect to ~p~n", [Node])
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
useradd([Username, Password]) ->
|
||||||
|
?PRINT("~p", [emqttd_auth:add(list_to_binary(Username), list_to_binary(Password))]).
|
||||||
|
|
||||||
|
userdel([Username]) ->
|
||||||
|
?PRINT("~p", [emqttd_auth:delete(list_to_binary(Username))]).
|
||||||
|
|
||||||
|
node_name(SNode) ->
|
||||||
|
SNode1 =
|
||||||
|
case string:tokens(SNode, "@") of
|
||||||
|
[_Node, _Server] ->
|
||||||
|
SNode;
|
||||||
|
_ ->
|
||||||
|
case net_kernel:longnames() of
|
||||||
|
true ->
|
||||||
|
SNode ++ "@" ++ inet_db:gethostname() ++
|
||||||
|
"." ++ inet_db:res_option(domain);
|
||||||
|
false ->
|
||||||
|
SNode ++ "@" ++ inet_db:gethostname();
|
||||||
|
_ ->
|
||||||
|
SNode
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
list_to_atom(SNode1).
|
|
@ -0,0 +1,33 @@
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>
|
||||||
|
%%%
|
||||||
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
%%% in the Software without restriction, including without limitation the rights
|
||||||
|
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
%%% copies of the Software, and to permit persons to whom the Software is
|
||||||
|
%%% furnished to do so, subject to the following conditions:
|
||||||
|
%%%
|
||||||
|
%%% The above copyright notice and this permission notice shall be included in all
|
||||||
|
%%% copies or substantial portions of the Software.
|
||||||
|
%%%
|
||||||
|
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
%%% SOFTWARE.
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @doc
|
||||||
|
%%% emqttd event manager.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
-module(emqttd_event).
|
||||||
|
|
||||||
|
-export([start_link/0]).
|
||||||
|
|
||||||
|
start_link() ->
|
||||||
|
gen_event:start_link({local, ?MODULE}).
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>
|
||||||
|
%%%
|
||||||
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
%%% in the Software without restriction, including without limitation the rights
|
||||||
|
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
%%% copies of the Software, and to permit persons to whom the Software is
|
||||||
|
%%% furnished to do so, subject to the following conditions:
|
||||||
|
%%%
|
||||||
|
%%% The above copyright notice and this permission notice shall be included in all
|
||||||
|
%%% copies or substantial portions of the Software.
|
||||||
|
%%%
|
||||||
|
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
%%% SOFTWARE.
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @doc
|
||||||
|
%%% emqttd http handler.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
-module(emqttd_http).
|
||||||
|
|
||||||
|
-author('feng@emqtt.io').
|
||||||
|
|
||||||
|
-include("emqttd.hrl").
|
||||||
|
|
||||||
|
-include("emqttd_packet.hrl").
|
||||||
|
|
||||||
|
-import(proplists, [get_value/2, get_value/3]).
|
||||||
|
|
||||||
|
-export([handle/1]).
|
||||||
|
|
||||||
|
handle(Req) ->
|
||||||
|
case authorized(Req) of
|
||||||
|
true ->
|
||||||
|
Path = Req:get(path),
|
||||||
|
Method = Req:get(method),
|
||||||
|
handle(Method, Path, Req);
|
||||||
|
false ->
|
||||||
|
Req:respond({401, [], <<"Fobbiden">>})
|
||||||
|
end.
|
||||||
|
|
||||||
|
handle('POST', "/mqtt/publish", Req) ->
|
||||||
|
Params = mochiweb_request:parse_post(Req),
|
||||||
|
lager:info("HTTP Publish: ~p~n", [Params]),
|
||||||
|
Qos = int(get_value("qos", Params, "0")),
|
||||||
|
Retain = bool(get_value("retain", Params, "0")),
|
||||||
|
Topic = list_to_binary(get_value("topic", Params)),
|
||||||
|
Message = list_to_binary(get_value("message", Params)),
|
||||||
|
case {validate(qos, Qos), validate(topic, Topic)} of
|
||||||
|
{true, true} ->
|
||||||
|
emqttd_router:route(#mqtt_message{qos = Qos,
|
||||||
|
retain = Retain,
|
||||||
|
topic = Topic,
|
||||||
|
payload = Message}),
|
||||||
|
Req:ok({"text/plan", <<"ok\n">>});
|
||||||
|
{false, _} ->
|
||||||
|
Req:respond({400, [], <<"Bad QoS">>});
|
||||||
|
{_, false} ->
|
||||||
|
Req:respond({400, [], <<"Bad Topic">>})
|
||||||
|
end;
|
||||||
|
|
||||||
|
handle(_Method, _Path, Req) ->
|
||||||
|
Req:not_found().
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% basic authorization
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
authorized(Req) ->
|
||||||
|
case mochiweb_request:get_header_value("Authorization", Req) of
|
||||||
|
undefined ->
|
||||||
|
false;
|
||||||
|
"Basic " ++ BasicAuth ->
|
||||||
|
emqttd_auth:check(user_passwd(BasicAuth))
|
||||||
|
end.
|
||||||
|
|
||||||
|
user_passwd(BasicAuth) ->
|
||||||
|
list_to_tuple(binary:split(base64:decode(BasicAuth), <<":">>)).
|
||||||
|
|
||||||
|
validate(qos, Qos) ->
|
||||||
|
(Qos >= ?QOS_0) and (Qos =< ?QOS_2);
|
||||||
|
|
||||||
|
validate(topic, Topic) ->
|
||||||
|
emqttd_topic:validate({name, Topic}).
|
||||||
|
|
||||||
|
int(S) -> list_to_integer(S).
|
||||||
|
|
||||||
|
bool("0") -> false;
|
||||||
|
bool("1") -> true.
|
||||||
|
|
||||||
|
|
|
@ -20,11 +20,11 @@
|
||||||
%%% SOFTWARE.
|
%%% SOFTWARE.
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% @doc
|
%%% @doc
|
||||||
%%% emqtt keepalive.
|
%%% emqttd keepalive.
|
||||||
%%%
|
%%%
|
||||||
%%% @end
|
%%% @end
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
-module(emqtt_keepalive).
|
-module(emqttd_keepalive).
|
||||||
|
|
||||||
-author('feng@emqtt.io').
|
-author('feng@emqtt.io').
|
||||||
|
|
|
@ -0,0 +1,147 @@
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>
|
||||||
|
%%%
|
||||||
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
%%% in the Software without restriction, including without limitation the rights
|
||||||
|
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
%%% copies of the Software, and to permit persons to whom the Software is
|
||||||
|
%%% furnished to do so, subject to the following conditions:
|
||||||
|
%%%
|
||||||
|
%%% The above copyright notice and this permission notice shall be included in all
|
||||||
|
%%% copies or substantial portions of the Software.
|
||||||
|
%%%
|
||||||
|
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
%%% SOFTWARE.
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @doc
|
||||||
|
%%% emqttd message.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
-module(emqttd_message).
|
||||||
|
|
||||||
|
-author('feng@emqtt.io').
|
||||||
|
|
||||||
|
-include("emqttd.hrl").
|
||||||
|
|
||||||
|
-include("emqttd_packet.hrl").
|
||||||
|
|
||||||
|
-export([from_packet/1, to_packet/1]).
|
||||||
|
|
||||||
|
-export([set_flag/1, set_flag/2, unset_flag/1, unset_flag/2]).
|
||||||
|
|
||||||
|
-export([dump/1]).
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% @doc
|
||||||
|
%% Message from Packet.
|
||||||
|
%%
|
||||||
|
%% @end
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-spec from_packet(mqtt_packet()) -> mqtt_message().
|
||||||
|
from_packet(#mqtt_packet{header = #mqtt_packet_header{type = ?PUBLISH,
|
||||||
|
retain = Retain,
|
||||||
|
qos = Qos,
|
||||||
|
dup = Dup},
|
||||||
|
variable = #mqtt_packet_publish{topic_name = Topic,
|
||||||
|
packet_id = PacketId},
|
||||||
|
payload = Payload}) ->
|
||||||
|
#mqtt_message{msgid = PacketId,
|
||||||
|
qos = Qos,
|
||||||
|
retain = Retain,
|
||||||
|
dup = Dup,
|
||||||
|
topic = Topic,
|
||||||
|
payload = Payload};
|
||||||
|
|
||||||
|
from_packet(#mqtt_packet_connect{will_flag = false}) ->
|
||||||
|
undefined;
|
||||||
|
|
||||||
|
from_packet(#mqtt_packet_connect{will_retain = Retain,
|
||||||
|
will_qos = Qos,
|
||||||
|
will_topic = Topic,
|
||||||
|
will_msg = Msg}) ->
|
||||||
|
#mqtt_message{retain = Retain,
|
||||||
|
qos = Qos,
|
||||||
|
topic = Topic,
|
||||||
|
dup = false,
|
||||||
|
payload = Msg}.
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% @doc
|
||||||
|
%% Message to packet
|
||||||
|
%%
|
||||||
|
%% @end
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-spec( to_packet( mqtt_message() ) -> mqtt_packet() ).
|
||||||
|
|
||||||
|
to_packet(#mqtt_message{msgid = MsgId,
|
||||||
|
qos = Qos,
|
||||||
|
retain = Retain,
|
||||||
|
dup = Dup,
|
||||||
|
topic = Topic,
|
||||||
|
payload = Payload}) ->
|
||||||
|
|
||||||
|
PacketId = if
|
||||||
|
Qos =:= ?QOS_0 -> undefined;
|
||||||
|
true -> MsgId
|
||||||
|
end,
|
||||||
|
|
||||||
|
#mqtt_packet{header = #mqtt_packet_header{type = ?PUBLISH,
|
||||||
|
qos = Qos,
|
||||||
|
retain = Retain,
|
||||||
|
dup = Dup},
|
||||||
|
variable = #mqtt_packet_publish{topic_name = Topic,
|
||||||
|
packet_id = PacketId},
|
||||||
|
payload = Payload}.
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% @doc
|
||||||
|
%% set dup, retain flag
|
||||||
|
%%
|
||||||
|
%% @end
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-spec set_flag(mqtt_message()) -> mqtt_message().
|
||||||
|
set_flag(Msg) ->
|
||||||
|
Msg#mqtt_message{dup = true, retain = true}.
|
||||||
|
|
||||||
|
-spec set_flag(atom(), mqtt_message()) -> mqtt_message().
|
||||||
|
set_flag(dup, Msg = #mqtt_message{dup = false}) ->
|
||||||
|
Msg#mqtt_message{dup = true};
|
||||||
|
set_flag(retain, Msg = #mqtt_message{retain = false}) ->
|
||||||
|
Msg#mqtt_message{retain = true};
|
||||||
|
set_flag(Flag, Msg) when Flag =:= dup orelse Flag =:= retain -> Msg.
|
||||||
|
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% @doc
|
||||||
|
%% Unset dup, retain flag
|
||||||
|
%%
|
||||||
|
%% @end
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-spec unset_flag(mqtt_message()) -> mqtt_message().
|
||||||
|
unset_flag(Msg) ->
|
||||||
|
Msg#mqtt_message{dup = false, retain = false}.
|
||||||
|
|
||||||
|
-spec unset_flag(dup | retain | atom(), mqtt_message()) -> mqtt_message().
|
||||||
|
unset_flag(dup, Msg = #mqtt_message{dup = true}) ->
|
||||||
|
Msg#mqtt_message{dup = false};
|
||||||
|
unset_flag(retain, Msg = #mqtt_message{retain = true}) ->
|
||||||
|
Msg#mqtt_message{retain = false};
|
||||||
|
unset_flag(Flag, Msg) when Flag =:= dup orelse Flag =:= retain -> Msg.
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% @doc
|
||||||
|
%% Dump mqtt message.
|
||||||
|
%%
|
||||||
|
%% @end
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
dump(#mqtt_message{msgid= MsgId, qos = Qos, retain = Retain, dup = Dup, topic = Topic}) ->
|
||||||
|
io_lib:format("Message(MsgId=~p, Qos=~p, Retain=~s, Dup=~s, Topic=~s)",
|
||||||
|
[MsgId, Qos, Retain, Dup, Topic]).
|
||||||
|
|
|
@ -20,21 +20,21 @@
|
||||||
%%% SOFTWARE.
|
%%% SOFTWARE.
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% @doc
|
%%% @doc
|
||||||
%%% emqtt metrics module. responsible for collecting broker metrics.
|
%%% emqttd metrics. responsible for collecting broker metrics.
|
||||||
%%%
|
%%%
|
||||||
%%% @end
|
%%% @end
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
-module(emqtt_metrics).
|
-module(emqttd_metrics).
|
||||||
|
|
||||||
-include("emqtt_packet.hrl").
|
-include("emqttd_packet.hrl").
|
||||||
|
|
||||||
-include("emqtt_systop.hrl").
|
-include("emqttd_systop.hrl").
|
||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
|
|
||||||
-define(SERVER, ?MODULE).
|
-define(SERVER, ?MODULE).
|
||||||
|
|
||||||
-define(METRIC_TAB, ?MODULE).
|
-define(METRIC_TAB, mqtt_broker_metric).
|
||||||
|
|
||||||
%% API Function Exports
|
%% API Function Exports
|
||||||
-export([start_link/1]).
|
-export([start_link/1]).
|
||||||
|
@ -182,7 +182,7 @@ init(Options) ->
|
||||||
% Init metrics
|
% Init metrics
|
||||||
[new_metric(Metric) || Metric <- Metrics],
|
[new_metric(Metric) || Metric <- Metrics],
|
||||||
% $SYS Topics for metrics
|
% $SYS Topics for metrics
|
||||||
[{atomic, _} = emqtt_pubsub:create(systop(Topic)) || {_, Topic} <- Metrics],
|
[{atomic, _} = emqttd_pubsub:create(systop(Topic)) || {_, Topic} <- Metrics],
|
||||||
PubInterval = proplists:get_value(pub_interval, Options, 60),
|
PubInterval = proplists:get_value(pub_interval, Options, 60),
|
||||||
{ok, tick(random:uniform(PubInterval), #state{pub_interval = PubInterval}), hibernate}.
|
{ok, tick(random:uniform(PubInterval), #state{pub_interval = PubInterval}), hibernate}.
|
||||||
|
|
||||||
|
@ -214,7 +214,7 @@ systop(Name) when is_atom(Name) ->
|
||||||
list_to_binary(lists:concat(["$SYS/brokers/", node(), "/", Name])).
|
list_to_binary(lists:concat(["$SYS/brokers/", node(), "/", Name])).
|
||||||
|
|
||||||
publish(Topic, Payload) ->
|
publish(Topic, Payload) ->
|
||||||
emqtt_router:route(#mqtt_message{topic = Topic, payload = Payload}).
|
emqttd_router:route(#mqtt_message{topic = Topic, payload = Payload}).
|
||||||
|
|
||||||
new_metric({gauge, Name}) ->
|
new_metric({gauge, Name}) ->
|
||||||
ets:insert(?METRIC_TAB, {{Name, 0}, 0});
|
ets:insert(?METRIC_TAB, {{Name, 0}, 0});
|
|
@ -0,0 +1,48 @@
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>
|
||||||
|
%%%
|
||||||
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
%%% in the Software without restriction, including without limitation the rights
|
||||||
|
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
%%% copies of the Software, and to permit persons to whom the Software is
|
||||||
|
%%% furnished to do so, subject to the following conditions:
|
||||||
|
%%%
|
||||||
|
%%% The above copyright notice and this permission notice shall be included in all
|
||||||
|
%%% copies or substantial portions of the Software.
|
||||||
|
%%%
|
||||||
|
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
%%% SOFTWARE.
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @doc
|
||||||
|
%%% emqttd mnesia.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
-module(emqttd_mnesia).
|
||||||
|
|
||||||
|
-author('feng@emqtt.io').
|
||||||
|
|
||||||
|
-export([init/0, wait/0, stop/0]).
|
||||||
|
|
||||||
|
init() ->
|
||||||
|
case mnesia:system_info(extra_db_nodes) of
|
||||||
|
[] -> mnesia:create_schema([node()]);
|
||||||
|
_ -> ok
|
||||||
|
end,
|
||||||
|
ok = mnesia:start(),
|
||||||
|
mnesia:wait_for_tables(mnesia:system_info(local_tables), infinity).
|
||||||
|
|
||||||
|
%TODO: timeout should be configured?
|
||||||
|
wait() ->
|
||||||
|
mnesia:wait_for_tables([topic, topic_trie, topic_trie_node, mqtt_user], 60000).
|
||||||
|
|
||||||
|
stop() ->
|
||||||
|
mnesia:stop().
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>
|
||||||
|
%%%
|
||||||
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
%%% in the Software without restriction, including without limitation the rights
|
||||||
|
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
%%% copies of the Software, and to permit persons to whom the Software is
|
||||||
|
%%% furnished to do so, subject to the following conditions:
|
||||||
|
%%%
|
||||||
|
%%% The above copyright notice and this permission notice shall be included in all
|
||||||
|
%%% copies or substantial portions of the Software.
|
||||||
|
%%%
|
||||||
|
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
%%% SOFTWARE.
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @doc
|
||||||
|
%%% emqttd vm monitor.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
%%TODO: this is a demo module....
|
||||||
|
|
||||||
|
-module(emqttd_monitor).
|
||||||
|
|
||||||
|
-author('feng@emqtt.io').
|
||||||
|
|
||||||
|
-behavior(gen_server).
|
||||||
|
|
||||||
|
-export([start_link/0]).
|
||||||
|
|
||||||
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
|
||||||
|
|
||||||
|
-record(state, {ok}).
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% @doc
|
||||||
|
%% Start emqttd monitor.
|
||||||
|
%%
|
||||||
|
%% @end
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-spec start_link() -> {ok, pid()} | ignore | {error, term()}.
|
||||||
|
start_link() ->
|
||||||
|
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||||
|
|
||||||
|
%%%=============================================================================
|
||||||
|
%%% gen_server callbacks
|
||||||
|
%%%=============================================================================
|
||||||
|
|
||||||
|
init([]) ->
|
||||||
|
erlang:system_monitor(self(), [{long_gc, 5000}, {large_heap, 1000000}, busy_port]),
|
||||||
|
{ok, #state{}}.
|
||||||
|
|
||||||
|
handle_call(Request, _From, State) ->
|
||||||
|
lager:error("unexpected request: ~p", [Request]),
|
||||||
|
{stop, {error, unexpected_request}, State}.
|
||||||
|
|
||||||
|
handle_cast(Msg, State) ->
|
||||||
|
lager:error("unexpected msg: ~p", [Msg]),
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
handle_info({monitor, GcPid, long_gc, Info}, State) ->
|
||||||
|
lager:error("long_gc: gcpid = ~p, ~p ~n ~p", [GcPid, process_info(GcPid,
|
||||||
|
[registered_name, memory, message_queue_len,heap_size,total_heap_size]), Info]),
|
||||||
|
{noreply, State};
|
||||||
|
|
||||||
|
handle_info({monitor, GcPid, large_heap, Info}, State) ->
|
||||||
|
lager:error("large_heap: gcpid = ~p,~p ~n ~p", [GcPid, process_info(GcPid,
|
||||||
|
[registered_name, memory, message_queue_len,heap_size,total_heap_size]), Info]),
|
||||||
|
{noreply, State};
|
||||||
|
|
||||||
|
handle_info({monitor, SusPid, busy_port, Port}, State) ->
|
||||||
|
lager:error("busy_port: suspid = ~p, port = ~p", [process_info(SusPid,
|
||||||
|
[registered_name, memory, message_queue_len,heap_size,total_heap_size]), Port]),
|
||||||
|
{noreply, State};
|
||||||
|
|
||||||
|
handle_info(Info, State) ->
|
||||||
|
lager:error("unexpected info: ~p", [Info]),
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
terminate(_Reason, _State) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
|
{ok, State}.
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,30 @@
|
||||||
%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%% Copyright (c) 2012-2015, Feng Lee <feng@emqtt.io>
|
%%% @Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>
|
||||||
%%
|
%%%
|
||||||
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
%% in the Software without restriction, including without limitation the rights
|
%%% in the Software without restriction, including without limitation the rights
|
||||||
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
%% copies of the Software, and to permit persons to whom the Software is
|
%%% copies of the Software, and to permit persons to whom the Software is
|
||||||
%% furnished to do so, subject to the following conditions:
|
%%% furnished to do so, subject to the following conditions:
|
||||||
%%
|
%%%
|
||||||
%% The above copyright notice and this permission notice shall be included in all
|
%%% The above copyright notice and this permission notice shall be included in all
|
||||||
%% copies or substantial portions of the Software.
|
%%% copies or substantial portions of the Software.
|
||||||
%%
|
%%%
|
||||||
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
%% SOFTWARE.
|
%%% SOFTWARE.
|
||||||
%%------------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
-module(emqtt_net).
|
%%% @doc
|
||||||
|
%%% emqttd net utility functions. some functions copied from rabbitmq.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
-module(emqttd_net).
|
||||||
|
|
||||||
-author('feng@emqtt.io').
|
-author('feng@emqtt.io').
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
%%-----------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Copyright (c) 2012-2015, Feng Lee <feng@emqtt.io>
|
%% Copyright (c) 2012-2015, Feng Lee <feng@emqtt.io>
|
||||||
%%
|
%%
|
||||||
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
@ -19,8 +19,9 @@
|
||||||
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
%% SOFTWARE.
|
%% SOFTWARE.
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
%%% @doc
|
||||||
-module(emqtt_bridge).
|
%%% clusted nodes monitor.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
-module(emqttd_nodes_monitor).
|
|
@ -20,11 +20,11 @@
|
||||||
%%% SOFTWARE.
|
%%% SOFTWARE.
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% @doc
|
%%% @doc
|
||||||
%%% emqtt options handler.
|
%%% emqttd options handler.
|
||||||
%%%
|
%%%
|
||||||
%%% @end
|
%%% @end
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
-module(emqtt_opts).
|
-module(emqttd_opts).
|
||||||
|
|
||||||
-export([merge/2]).
|
-export([merge/2]).
|
||||||
|
|
|
@ -20,15 +20,15 @@
|
||||||
%%% SOFTWARE.
|
%%% SOFTWARE.
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% @doc
|
%%% @doc
|
||||||
%%% emqtt packet.
|
%%% emqttd packet.
|
||||||
%%%
|
%%%
|
||||||
%%% @end
|
%%% @end
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
-module(emqtt_packet).
|
-module(emqttd_packet).
|
||||||
|
|
||||||
-author("feng@emqtt.io").
|
-author("feng@emqtt.io").
|
||||||
|
|
||||||
-include("emqtt_packet.hrl").
|
-include("emqttd_packet.hrl").
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([protocol_name/1, type_name/1, connack_name/1]).
|
-export([protocol_name/1, type_name/1, connack_name/1]).
|
||||||
|
@ -108,7 +108,7 @@ dump_variable(#mqtt_packet_connect{
|
||||||
will_topic = WillTopic,
|
will_topic = WillTopic,
|
||||||
will_msg = WillMsg,
|
will_msg = WillMsg,
|
||||||
username = Username,
|
username = Username,
|
||||||
password = Password} ) ->
|
password = Password}) ->
|
||||||
Format = "ClientId=~s, ProtoName=~s, ProtoVsn=~p, CleanSess=~s, KeepAlive=~p, Username=~s, Password=~s",
|
Format = "ClientId=~s, ProtoName=~s, ProtoVsn=~p, CleanSess=~s, KeepAlive=~p, Username=~s, Password=~s",
|
||||||
Args = [ClientId, ProtoName, ProtoVer, CleanSess, KeepAlive, Username, dump_password(Password)],
|
Args = [ClientId, ProtoName, ProtoVer, CleanSess, KeepAlive, Username, dump_password(Password)],
|
||||||
{Format1, Args1} = if
|
{Format1, Args1} = if
|
||||||
|
@ -118,37 +118,30 @@ dump_variable(#mqtt_packet_connect{
|
||||||
end,
|
end,
|
||||||
io_lib:format(Format1, Args1);
|
io_lib:format(Format1, Args1);
|
||||||
|
|
||||||
dump_variable(#mqtt_packet_connack{
|
dump_variable(#mqtt_packet_connack{ack_flags = AckFlags,
|
||||||
ack_flags = AckFlags,
|
return_code = ReturnCode } ) ->
|
||||||
return_code = ReturnCode } ) ->
|
|
||||||
io_lib:format("AckFlags=~p, RetainCode=~p", [AckFlags, ReturnCode]);
|
io_lib:format("AckFlags=~p, RetainCode=~p", [AckFlags, ReturnCode]);
|
||||||
|
|
||||||
dump_variable(#mqtt_packet_publish{
|
dump_variable(#mqtt_packet_publish{topic_name = TopicName,
|
||||||
topic_name = TopicName,
|
packet_id = PacketId}) ->
|
||||||
packet_id = PacketId} ) ->
|
|
||||||
io_lib:format("TopicName=~s, PacketId=~p", [TopicName, PacketId]);
|
io_lib:format("TopicName=~s, PacketId=~p", [TopicName, PacketId]);
|
||||||
|
|
||||||
dump_variable(#mqtt_packet_puback{
|
dump_variable(#mqtt_packet_puback{packet_id = PacketId}) ->
|
||||||
packet_id = PacketId } ) ->
|
|
||||||
io_lib:format("PacketId=~p", [PacketId]);
|
io_lib:format("PacketId=~p", [PacketId]);
|
||||||
|
|
||||||
dump_variable(#mqtt_packet_subscribe{
|
dump_variable(#mqtt_packet_subscribe{packet_id = PacketId,
|
||||||
packet_id = PacketId,
|
topic_table = TopicTable}) ->
|
||||||
topic_table = TopicTable }) ->
|
|
||||||
io_lib:format("PacketId=~p, TopicTable=~p", [PacketId, TopicTable]);
|
io_lib:format("PacketId=~p, TopicTable=~p", [PacketId, TopicTable]);
|
||||||
|
|
||||||
dump_variable(#mqtt_packet_unsubscribe{
|
dump_variable(#mqtt_packet_unsubscribe{packet_id = PacketId,
|
||||||
packet_id = PacketId,
|
topics = Topics}) ->
|
||||||
topics = Topics }) ->
|
|
||||||
io_lib:format("PacketId=~p, Topics=~p", [PacketId, Topics]);
|
io_lib:format("PacketId=~p, Topics=~p", [PacketId, Topics]);
|
||||||
|
|
||||||
dump_variable(#mqtt_packet_suback{
|
dump_variable(#mqtt_packet_suback{packet_id = PacketId,
|
||||||
packet_id = PacketId,
|
qos_table = QosTable}) ->
|
||||||
qos_table = QosTable} ) ->
|
|
||||||
io_lib:format("PacketId=~p, QosTable=~p", [PacketId, QosTable]);
|
io_lib:format("PacketId=~p, QosTable=~p", [PacketId, QosTable]);
|
||||||
|
|
||||||
dump_variable(#mqtt_packet_unsuback{
|
dump_variable(#mqtt_packet_unsuback{packet_id = PacketId}) ->
|
||||||
packet_id = PacketId } ) ->
|
|
||||||
io_lib:format("PacketId=~p", [PacketId]);
|
io_lib:format("PacketId=~p", [PacketId]);
|
||||||
|
|
||||||
dump_variable(PacketId) when is_integer(PacketId) ->
|
dump_variable(PacketId) when is_integer(PacketId) ->
|
|
@ -20,15 +20,15 @@
|
||||||
%%% SOFTWARE.
|
%%% SOFTWARE.
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% @doc
|
%%% @doc
|
||||||
%%% emqtt received packet parser.
|
%%% emqttd received packet parser.
|
||||||
%%%
|
%%%
|
||||||
%%% @end
|
%%% @end
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
-module(emqtt_parser).
|
-module(emqttd_parser).
|
||||||
|
|
||||||
-author("feng@emqtt.io").
|
-author("feng@emqtt.io").
|
||||||
|
|
||||||
-include("emqtt_packet.hrl").
|
-include("emqttd_packet.hrl").
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([init/0, parse/2]).
|
-export([init/0, parse/2]).
|
|
@ -0,0 +1,27 @@
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>
|
||||||
|
%%%
|
||||||
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
%%% in the Software without restriction, including without limitation the rights
|
||||||
|
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
%%% copies of the Software, and to permit persons to whom the Software is
|
||||||
|
%%% furnished to do so, subject to the following conditions:
|
||||||
|
%%%
|
||||||
|
%%% The above copyright notice and this permission notice shall be included in all
|
||||||
|
%%% copies or substantial portions of the Software.
|
||||||
|
%%%
|
||||||
|
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
%%% SOFTWARE.
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @doc
|
||||||
|
%%% emqtt plugin framework.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
-module(emqttd_plugin).
|
|
@ -24,11 +24,11 @@
|
||||||
%%%
|
%%%
|
||||||
%%% @end
|
%%% @end
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
-module(emqtt_protocol).
|
-module(emqttd_protocol).
|
||||||
|
|
||||||
-include("emqtt.hrl").
|
-include("emqttd.hrl").
|
||||||
|
|
||||||
-include("emqtt_packet.hrl").
|
-include("emqttd_packet.hrl").
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([init/3, client_id/1]).
|
-export([init/3, client_id/1]).
|
||||||
|
@ -62,7 +62,7 @@ init(Transport, Socket, Peername) ->
|
||||||
|
|
||||||
client_id(#proto_state{client_id = ClientId}) -> ClientId.
|
client_id(#proto_state{client_id = ClientId}) -> ClientId.
|
||||||
|
|
||||||
%%SHOULD be registered in emqtt_cm
|
%%SHOULD be registered in emqttd_cm
|
||||||
info(#proto_state{proto_vsn = ProtoVsn,
|
info(#proto_state{proto_vsn = ProtoVsn,
|
||||||
proto_name = ProtoName,
|
proto_name = ProtoName,
|
||||||
client_id = ClientId,
|
client_id = ClientId,
|
||||||
|
@ -90,7 +90,7 @@ received(_Packet, State = #proto_state{connected = false}) ->
|
||||||
|
|
||||||
received(Packet = ?PACKET(_Type), State = #proto_state{peer_name = PeerName,
|
received(Packet = ?PACKET(_Type), State = #proto_state{peer_name = PeerName,
|
||||||
client_id = ClientId}) ->
|
client_id = ClientId}) ->
|
||||||
lager:info("RECV from ~s@~s: ~s", [ClientId, PeerName, emqtt_packet:dump(Packet)]),
|
lager:info("RECV from ~s@~s: ~s", [ClientId, PeerName, emqttd_packet:dump(Packet)]),
|
||||||
case validate_packet(Packet) of
|
case validate_packet(Packet) of
|
||||||
ok ->
|
ok ->
|
||||||
handle(Packet, State);
|
handle(Packet, State);
|
||||||
|
@ -106,16 +106,16 @@ handle(Packet = ?CONNECT_PACKET(Var), State = #proto_state{peer_name = PeerName}
|
||||||
keep_alive = KeepAlive,
|
keep_alive = KeepAlive,
|
||||||
client_id = ClientId} = Var,
|
client_id = ClientId} = Var,
|
||||||
|
|
||||||
lager:info("RECV from ~s@~s: ~s", [ClientId, PeerName, emqtt_packet:dump(Packet)]),
|
lager:info("RECV from ~s@~s: ~s", [ClientId, PeerName, emqttd_packet:dump(Packet)]),
|
||||||
|
|
||||||
{ReturnCode1, State1} =
|
{ReturnCode1, State1} =
|
||||||
case validate_connect(Var) of
|
case validate_connect(Var) of
|
||||||
?CONNACK_ACCEPT ->
|
?CONNACK_ACCEPT ->
|
||||||
case emqtt_auth:check(Username, Password) of
|
case emqttd_auth:check(Username, Password) of
|
||||||
true ->
|
true ->
|
||||||
ClientId1 = clientid(ClientId, State),
|
ClientId1 = clientid(ClientId, State),
|
||||||
start_keepalive(KeepAlive),
|
start_keepalive(KeepAlive),
|
||||||
emqtt_cm:register(ClientId1, self()),
|
emqttd_cm:register(ClientId1, self()),
|
||||||
{?CONNACK_ACCEPT, State#proto_state{will_msg = willmsg(Var),
|
{?CONNACK_ACCEPT, State#proto_state{will_msg = willmsg(Var),
|
||||||
clean_sess = CleanSess,
|
clean_sess = CleanSess,
|
||||||
client_id = ClientId1}};
|
client_id = ClientId1}};
|
||||||
|
@ -128,27 +128,27 @@ handle(Packet = ?CONNECT_PACKET(Var), State = #proto_state{peer_name = PeerName}
|
||||||
end,
|
end,
|
||||||
send(?CONNACK_PACKET(ReturnCode1), State1),
|
send(?CONNACK_PACKET(ReturnCode1), State1),
|
||||||
%%Starting session
|
%%Starting session
|
||||||
{ok, Session} = emqtt_session:start({CleanSess, ClientId, self()}),
|
{ok, Session} = emqttd_session:start({CleanSess, ClientId, self()}),
|
||||||
{ok, State1#proto_state{session = Session}};
|
{ok, State1#proto_state{session = Session}};
|
||||||
|
|
||||||
handle(Packet = ?PUBLISH_PACKET(?QOS_0, _Topic, _PacketId, _Payload),
|
handle(Packet = ?PUBLISH_PACKET(?QOS_0, _Topic, _PacketId, _Payload),
|
||||||
State = #proto_state{session = Session}) ->
|
State = #proto_state{session = Session}) ->
|
||||||
emqtt_session:publish(Session, {?QOS_0, emqtt_message:from_packet(Packet)}),
|
emqttd_session:publish(Session, {?QOS_0, emqttd_message:from_packet(Packet)}),
|
||||||
{ok, State};
|
{ok, State};
|
||||||
|
|
||||||
handle(Packet = ?PUBLISH_PACKET(?QOS_1, _Topic, PacketId, _Payload),
|
handle(Packet = ?PUBLISH_PACKET(?QOS_1, _Topic, PacketId, _Payload),
|
||||||
State = #proto_state{session = Session}) ->
|
State = #proto_state{session = Session}) ->
|
||||||
emqtt_session:publish(Session, {?QOS_1, emqtt_message:from_packet(Packet)}),
|
emqttd_session:publish(Session, {?QOS_1, emqttd_message:from_packet(Packet)}),
|
||||||
send(?PUBACK_PACKET(?PUBACK, PacketId), State);
|
send(?PUBACK_PACKET(?PUBACK, PacketId), State);
|
||||||
|
|
||||||
handle(Packet = ?PUBLISH_PACKET(?QOS_2, _Topic, PacketId, _Payload),
|
handle(Packet = ?PUBLISH_PACKET(?QOS_2, _Topic, PacketId, _Payload),
|
||||||
State = #proto_state{session = Session}) ->
|
State = #proto_state{session = Session}) ->
|
||||||
NewSession = emqtt_session:publish(Session, {?QOS_2, emqtt_message:from_packet(Packet)}),
|
NewSession = emqttd_session:publish(Session, {?QOS_2, emqttd_message:from_packet(Packet)}),
|
||||||
send(?PUBACK_PACKET(?PUBREC, PacketId), State#proto_state{session = NewSession});
|
send(?PUBACK_PACKET(?PUBREC, PacketId), State#proto_state{session = NewSession});
|
||||||
|
|
||||||
handle(?PUBACK_PACKET(Type, PacketId), State = #proto_state{session = Session})
|
handle(?PUBACK_PACKET(Type, PacketId), State = #proto_state{session = Session})
|
||||||
when Type >= ?PUBACK andalso Type =< ?PUBCOMP ->
|
when Type >= ?PUBACK andalso Type =< ?PUBCOMP ->
|
||||||
NewSession = emqtt_session:puback(Session, {Type, PacketId}),
|
NewSession = emqttd_session:puback(Session, {Type, PacketId}),
|
||||||
NewState = State#proto_state{session = NewSession},
|
NewState = State#proto_state{session = NewSession},
|
||||||
if
|
if
|
||||||
Type =:= ?PUBREC ->
|
Type =:= ?PUBREC ->
|
||||||
|
@ -161,11 +161,11 @@ handle(?PUBACK_PACKET(Type, PacketId), State = #proto_state{session = Session})
|
||||||
{ok, NewState};
|
{ok, NewState};
|
||||||
|
|
||||||
handle(?SUBSCRIBE_PACKET(PacketId, TopicTable), State = #proto_state{session = Session}) ->
|
handle(?SUBSCRIBE_PACKET(PacketId, TopicTable), State = #proto_state{session = Session}) ->
|
||||||
{ok, NewSession, GrantedQos} = emqtt_session:subscribe(Session, TopicTable),
|
{ok, NewSession, GrantedQos} = emqttd_session:subscribe(Session, TopicTable),
|
||||||
send(?SUBACK_PACKET(PacketId, GrantedQos), State#proto_state{session = NewSession});
|
send(?SUBACK_PACKET(PacketId, GrantedQos), State#proto_state{session = NewSession});
|
||||||
|
|
||||||
handle(?UNSUBSCRIBE_PACKET(PacketId, Topics), State = #proto_state{session = Session}) ->
|
handle(?UNSUBSCRIBE_PACKET(PacketId, Topics), State = #proto_state{session = Session}) ->
|
||||||
{ok, NewSession} = emqtt_session:unsubscribe(Session, Topics),
|
{ok, NewSession} = emqttd_session:unsubscribe(Session, Topics),
|
||||||
send(?UNSUBACK_PACKET(PacketId), State#proto_state{session = NewSession});
|
send(?UNSUBACK_PACKET(PacketId), State#proto_state{session = NewSession});
|
||||||
|
|
||||||
handle(?PACKET(?PINGREQ), State) ->
|
handle(?PACKET(?PINGREQ), State) ->
|
||||||
|
@ -179,24 +179,24 @@ handle(?PACKET(?DISCONNECT), State) ->
|
||||||
-spec send({pid() | tuple(), mqtt_message()} | mqtt_packet(), proto_state()) -> {ok, proto_state()}.
|
-spec send({pid() | tuple(), mqtt_message()} | mqtt_packet(), proto_state()) -> {ok, proto_state()}.
|
||||||
%% qos0 message
|
%% qos0 message
|
||||||
send({_From, Message = #mqtt_message{qos = ?QOS_0}}, State) ->
|
send({_From, Message = #mqtt_message{qos = ?QOS_0}}, State) ->
|
||||||
send(emqtt_message:to_packet(Message), State);
|
send(emqttd_message:to_packet(Message), State);
|
||||||
|
|
||||||
%% message from session
|
%% message from session
|
||||||
send({_From = SessPid, Message}, State = #proto_state{session = SessPid}) when is_pid(SessPid) ->
|
send({_From = SessPid, Message}, State = #proto_state{session = SessPid}) when is_pid(SessPid) ->
|
||||||
send(emqtt_message:to_packet(Message), State);
|
send(emqttd_message:to_packet(Message), State);
|
||||||
|
|
||||||
%% message(qos1, qos2) not from session
|
%% message(qos1, qos2) not from session
|
||||||
send({_From, Message = #mqtt_message{qos = Qos}}, State = #proto_state{session = Session})
|
send({_From, Message = #mqtt_message{qos = Qos}}, State = #proto_state{session = Session})
|
||||||
when (Qos =:= ?QOS_1) orelse (Qos =:= ?QOS_2) ->
|
when (Qos =:= ?QOS_1) orelse (Qos =:= ?QOS_2) ->
|
||||||
{Message1, NewSession} = emqtt_session:store(Session, Message),
|
{Message1, NewSession} = emqttd_session:store(Session, Message),
|
||||||
send(emqtt_message:to_packet(Message1), State#proto_state{session = NewSession});
|
send(emqttd_message:to_packet(Message1), State#proto_state{session = NewSession});
|
||||||
|
|
||||||
send(Packet, State = #proto_state{transport = Transport, socket = Sock, peer_name = PeerName, client_id = ClientId}) when is_record(Packet, mqtt_packet) ->
|
send(Packet, State = #proto_state{transport = Transport, socket = Sock, peer_name = PeerName, client_id = ClientId}) when is_record(Packet, mqtt_packet) ->
|
||||||
lager:info("SENT to ~s@~s: ~s", [ClientId, PeerName, emqtt_packet:dump(Packet)]),
|
lager:info("SENT to ~s@~s: ~s", [ClientId, PeerName, emqttd_packet:dump(Packet)]),
|
||||||
sent_stats(Packet),
|
sent_stats(Packet),
|
||||||
Data = emqtt_serialiser:serialise(Packet),
|
Data = emqttd_serialiser:serialise(Packet),
|
||||||
lager:debug("SENT to ~s: ~p", [PeerName, Data]),
|
lager:debug("SENT to ~s: ~p", [PeerName, Data]),
|
||||||
emqtt_metrics:inc('bytes/sent', size(Data)),
|
emqttd_metrics:inc('bytes/sent', size(Data)),
|
||||||
Transport:send(Sock, Data),
|
Transport:send(Sock, Data),
|
||||||
{ok, State}.
|
{ok, State}.
|
||||||
|
|
||||||
|
@ -213,7 +213,7 @@ shutdown(Error, #proto_state{peer_name = PeerName, client_id = ClientId, will_ms
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
willmsg(Packet) when is_record(Packet, mqtt_packet_connect) ->
|
willmsg(Packet) when is_record(Packet, mqtt_packet_connect) ->
|
||||||
emqtt_message:from_packet(Packet).
|
emqttd_message:from_packet(Packet).
|
||||||
|
|
||||||
clientid(<<>>, #proto_state{peer_name = PeerName}) ->
|
clientid(<<>>, #proto_state{peer_name = PeerName}) ->
|
||||||
<<"eMQTT/", (base64:encode(PeerName))/binary>>;
|
<<"eMQTT/", (base64:encode(PeerName))/binary>>;
|
||||||
|
@ -224,7 +224,7 @@ clientid(ClientId, _State) -> ClientId.
|
||||||
|
|
||||||
send_willmsg(undefined) -> ignore;
|
send_willmsg(undefined) -> ignore;
|
||||||
%%TODO:should call session...
|
%%TODO:should call session...
|
||||||
send_willmsg(WillMsg) -> emqtt_router:route(WillMsg).
|
send_willmsg(WillMsg) -> emqttd_router:route(WillMsg).
|
||||||
|
|
||||||
start_keepalive(0) -> ignore;
|
start_keepalive(0) -> ignore;
|
||||||
start_keepalive(Sec) when Sec > 0 ->
|
start_keepalive(Sec) when Sec > 0 ->
|
||||||
|
@ -264,7 +264,7 @@ validate_clientid(#mqtt_packet_connect {proto_ver = Ver, clean_sess = CleanSess,
|
||||||
|
|
||||||
validate_packet(#mqtt_packet{header = #mqtt_packet_header{type = ?PUBLISH},
|
validate_packet(#mqtt_packet{header = #mqtt_packet_header{type = ?PUBLISH},
|
||||||
variable = #mqtt_packet_publish{topic_name = Topic}}) ->
|
variable = #mqtt_packet_publish{topic_name = Topic}}) ->
|
||||||
case emqtt_topic:validate({name, Topic}) of
|
case emqttd_topic:validate({name, Topic}) of
|
||||||
true -> ok;
|
true -> ok;
|
||||||
false -> lager:warning("Error publish topic: ~p", [Topic]), {error, badtopic}
|
false -> lager:warning("Error publish topic: ~p", [Topic]), {error, badtopic}
|
||||||
end;
|
end;
|
||||||
|
@ -288,7 +288,7 @@ validate_topics(Type, []) when Type =:= name orelse Type =:= filter ->
|
||||||
|
|
||||||
validate_topics(Type, Topics) when Type =:= name orelse Type =:= filter ->
|
validate_topics(Type, Topics) when Type =:= name orelse Type =:= filter ->
|
||||||
ErrTopics = [Topic || {Topic, Qos} <- Topics,
|
ErrTopics = [Topic || {Topic, Qos} <- Topics,
|
||||||
not (emqtt_topic:validate({Type, Topic}) and validate_qos(Qos))],
|
not (emqttd_topic:validate({Type, Topic}) and validate_qos(Qos))],
|
||||||
case ErrTopics of
|
case ErrTopics of
|
||||||
[] -> ok;
|
[] -> ok;
|
||||||
_ -> lager:error("Error Topics: ~p", [ErrTopics]), {error, badtopic}
|
_ -> lager:error("Error Topics: ~p", [ErrTopics]), {error, badtopic}
|
||||||
|
@ -299,22 +299,22 @@ validate_qos(Qos) when Qos =< ?QOS_2 -> true;
|
||||||
validate_qos(_) -> false.
|
validate_qos(_) -> false.
|
||||||
|
|
||||||
try_unregister(undefined, _) -> ok;
|
try_unregister(undefined, _) -> ok;
|
||||||
try_unregister(ClientId, _) -> emqtt_cm:unregister(ClientId, self()).
|
try_unregister(ClientId, _) -> emqttd_cm:unregister(ClientId, self()).
|
||||||
|
|
||||||
sent_stats(?PACKET(Type)) ->
|
sent_stats(?PACKET(Type)) ->
|
||||||
emqtt_metrics:inc('packets/sent'),
|
emqttd_metrics:inc('packets/sent'),
|
||||||
inc(Type).
|
inc(Type).
|
||||||
inc(?CONNACK) ->
|
inc(?CONNACK) ->
|
||||||
emqtt_metrics:inc('packets/connack');
|
emqttd_metrics:inc('packets/connack');
|
||||||
inc(?PUBLISH) ->
|
inc(?PUBLISH) ->
|
||||||
emqtt_metrics:inc('messages/sent'),
|
emqttd_metrics:inc('messages/sent'),
|
||||||
emqtt_metrics:inc('packets/publish/sent');
|
emqttd_metrics:inc('packets/publish/sent');
|
||||||
inc(?SUBACK) ->
|
inc(?SUBACK) ->
|
||||||
emqtt_metrics:inc('packets/suback');
|
emqttd_metrics:inc('packets/suback');
|
||||||
inc(?UNSUBACK) ->
|
inc(?UNSUBACK) ->
|
||||||
emqtt_metrics:inc('packets/unsuback');
|
emqttd_metrics:inc('packets/unsuback');
|
||||||
inc(?PINGRESP) ->
|
inc(?PINGRESP) ->
|
||||||
emqtt_metrics:inc('packets/pingresp');
|
emqttd_metrics:inc('packets/pingresp');
|
||||||
inc(_) ->
|
inc(_) ->
|
||||||
ingore.
|
ingore.
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
%%%
|
%%%
|
||||||
%%% @end
|
%%% @end
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
-module(emqtt_pubsub).
|
-module(emqttd_pubsub).
|
||||||
|
|
||||||
-author('feng@emqtt.io').
|
-author('feng@emqtt.io').
|
||||||
|
|
||||||
|
@ -32,11 +32,11 @@
|
||||||
|
|
||||||
-define(SERVER, ?MODULE).
|
-define(SERVER, ?MODULE).
|
||||||
|
|
||||||
-include("emqtt.hrl").
|
-include("emqttd.hrl").
|
||||||
|
|
||||||
-include("emqtt_topic.hrl").
|
-include("emqttd_topic.hrl").
|
||||||
|
|
||||||
-include("emqtt_packet.hrl").
|
-include("emqttd_packet.hrl").
|
||||||
|
|
||||||
-include_lib("stdlib/include/qlc.hrl").
|
-include_lib("stdlib/include/qlc.hrl").
|
||||||
|
|
||||||
|
@ -184,7 +184,7 @@ dispatch(Topic, Msg = #mqtt_message{qos = Qos}) when is_binary(Topic) ->
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-spec match(Topic :: binary()) -> [topic()].
|
-spec match(Topic :: binary()) -> [topic()].
|
||||||
match(Topic) when is_binary(Topic) ->
|
match(Topic) when is_binary(Topic) ->
|
||||||
TrieNodes = mnesia:async_dirty(fun trie_match/1, [emqtt_topic:words(Topic)]),
|
TrieNodes = mnesia:async_dirty(fun trie_match/1, [emqttd_topic:words(Topic)]),
|
||||||
Names = [Name || #topic_trie_node{topic=Name} <- TrieNodes, Name=/= undefined],
|
Names = [Name || #topic_trie_node{topic=Name} <- TrieNodes, Name=/= undefined],
|
||||||
lists:flatten([mnesia:dirty_read(topic, Name) || Name <- Names]).
|
lists:flatten([mnesia:dirty_read(topic, Name) || Name <- Names]).
|
||||||
|
|
||||||
|
@ -306,7 +306,7 @@ try_remove_subscriber({Topic, Qos}, SubPid) ->
|
||||||
try_remove_topic(Name) when is_binary(Name) ->
|
try_remove_topic(Name) when is_binary(Name) ->
|
||||||
case ets:member(topic_subscriber, Name) of
|
case ets:member(topic_subscriber, Name) of
|
||||||
false ->
|
false ->
|
||||||
Topic = emqtt_topic:new(Name),
|
Topic = emqttd_topic:new(Name),
|
||||||
Fun = fun() ->
|
Fun = fun() ->
|
||||||
mnesia:delete_object(Topic),
|
mnesia:delete_object(Topic),
|
||||||
case mnesia:read(topic, Name) of
|
case mnesia:read(topic, Name) of
|
||||||
|
@ -320,7 +320,7 @@ try_remove_topic(Name) when is_binary(Name) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
trie_add(Topic) when is_binary(Topic) ->
|
trie_add(Topic) when is_binary(Topic) ->
|
||||||
mnesia:write(emqtt_topic:new(Topic)),
|
mnesia:write(emqttd_topic:new(Topic)),
|
||||||
case mnesia:read(topic_trie_node, Topic) of
|
case mnesia:read(topic_trie_node, Topic) of
|
||||||
[TrieNode=#topic_trie_node{topic=undefined}] ->
|
[TrieNode=#topic_trie_node{topic=undefined}] ->
|
||||||
mnesia:write(TrieNode#topic_trie_node{topic=Topic});
|
mnesia:write(TrieNode#topic_trie_node{topic=Topic});
|
||||||
|
@ -328,7 +328,7 @@ trie_add(Topic) when is_binary(Topic) ->
|
||||||
{atomic, already_exist};
|
{atomic, already_exist};
|
||||||
[] ->
|
[] ->
|
||||||
%add trie path
|
%add trie path
|
||||||
[trie_add_path(Triple) || Triple <- emqtt_topic:triples(Topic)],
|
[trie_add_path(Triple) || Triple <- emqttd_topic:triples(Topic)],
|
||||||
%add last node
|
%add last node
|
||||||
mnesia:write(#topic_trie_node{node_id=Topic, topic=Topic})
|
mnesia:write(#topic_trie_node{node_id=Topic, topic=Topic})
|
||||||
end.
|
end.
|
||||||
|
@ -337,7 +337,7 @@ trie_delete(Topic) when is_binary(Topic) ->
|
||||||
case mnesia:read(topic_trie_node, Topic) of
|
case mnesia:read(topic_trie_node, Topic) of
|
||||||
[#topic_trie_node{edge_count=0}] ->
|
[#topic_trie_node{edge_count=0}] ->
|
||||||
mnesia:delete({topic_trie_node, Topic}),
|
mnesia:delete({topic_trie_node, Topic}),
|
||||||
trie_delete_path(lists:reverse(emqtt_topic:triples(Topic)));
|
trie_delete_path(lists:reverse(emqttd_topic:triples(Topic)));
|
||||||
[TrieNode] ->
|
[TrieNode] ->
|
||||||
mnesia:write(TrieNode#topic_trie_node{topic=Topic});
|
mnesia:write(TrieNode#topic_trie_node{topic=Topic});
|
||||||
[] ->
|
[] ->
|
||||||
|
@ -400,19 +400,19 @@ trie_delete_path([{NodeId, Word, _} | RestPath]) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
setstats(State = #state{max_subs = Max}) ->
|
setstats(State = #state{max_subs = Max}) ->
|
||||||
emqtt_broker:setstat('topics/count', mnesia:table_info(topic, size)),
|
emqttd_broker:setstat('topics/count', mnesia:table_info(topic, size)),
|
||||||
SubCount = ets:info(topic_subscriber, size),
|
SubCount = ets:info(topic_subscriber, size),
|
||||||
emqtt_broker:setstat('subscribers/count', SubCount),
|
emqttd_broker:setstat('subscribers/count', SubCount),
|
||||||
if
|
if
|
||||||
SubCount > Max ->
|
SubCount > Max ->
|
||||||
emqtt_broker:setstat('subscribers/max', SubCount),
|
emqttd_broker:setstat('subscribers/max', SubCount),
|
||||||
State#state{max_subs = SubCount};
|
State#state{max_subs = SubCount};
|
||||||
true ->
|
true ->
|
||||||
State
|
State
|
||||||
end.
|
end.
|
||||||
|
|
||||||
dropped(true) ->
|
dropped(true) ->
|
||||||
emqtt_metrics:inc('messages/dropped');
|
emqttd_metrics:inc('messages/dropped');
|
||||||
dropped(false) ->
|
dropped(false) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>
|
||||||
|
%%%
|
||||||
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
%%% in the Software without restriction, including without limitation the rights
|
||||||
|
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
%%% copies of the Software, and to permit persons to whom the Software is
|
||||||
|
%%% furnished to do so, subject to the following conditions:
|
||||||
|
%%%
|
||||||
|
%%% The above copyright notice and this permission notice shall be included in all
|
||||||
|
%%% copies or substantial portions of the Software.
|
||||||
|
%%%
|
||||||
|
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
%%% SOFTWARE.
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @doc
|
||||||
|
%%% emqttd simple queue.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
%% TODO: this module should be removed...
|
||||||
|
|
||||||
|
-module(emqttd_queue).
|
||||||
|
|
||||||
|
-include("emqttd_packet.hrl").
|
||||||
|
|
||||||
|
-export([new/1, new/2, in/3, all/1, clear/1]).
|
||||||
|
|
||||||
|
-define(DEFAULT_MAX_LEN, 1000).
|
||||||
|
|
||||||
|
-record(mqtt_queue_wrapper, {queue = queue:new(),
|
||||||
|
max_len = ?DEFAULT_MAX_LEN,
|
||||||
|
store_qos0 = false}).
|
||||||
|
|
||||||
|
-type mqtt_queue() :: #mqtt_queue_wrapper{}.
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% @doc
|
||||||
|
%% New Queue.
|
||||||
|
%%
|
||||||
|
%% @end
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-spec new(non_neg_integer()) -> mqtt_queue().
|
||||||
|
new(MaxLen) -> #mqtt_queue_wrapper{max_len = MaxLen}.
|
||||||
|
|
||||||
|
new(MaxLen, StoreQos0) -> #mqtt_queue_wrapper{max_len = MaxLen, store_qos0 = StoreQos0}.
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% @doc
|
||||||
|
%% Queue one message.
|
||||||
|
%%
|
||||||
|
%% @end
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-spec in(binary(), mqtt_message(), mqtt_queue()) -> mqtt_queue().
|
||||||
|
in(ClientId, Message = #mqtt_message{qos = Qos},
|
||||||
|
Wrapper = #mqtt_queue_wrapper{queue = Queue, max_len = MaxLen}) ->
|
||||||
|
case queue:len(Queue) < MaxLen of
|
||||||
|
true ->
|
||||||
|
Wrapper#mqtt_queue_wrapper{queue = queue:in(Message, Queue)};
|
||||||
|
false -> % full
|
||||||
|
if
|
||||||
|
Qos =:= ?QOS_0 ->
|
||||||
|
lager:warning("Queue ~s drop qos0 message: ~p", [ClientId, Message]),
|
||||||
|
Wrapper;
|
||||||
|
true ->
|
||||||
|
{{value, Msg}, Queue1} = queue:drop(Queue),
|
||||||
|
lager:warning("Queue ~s drop message: ~p", [ClientId, Msg]),
|
||||||
|
Wrapper#mqtt_queue_wrapper{queue = Queue1}
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% @doc
|
||||||
|
%% Get all messages in queue.
|
||||||
|
%%
|
||||||
|
%% @end
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-spec all(mqtt_queue()) -> list().
|
||||||
|
all(#mqtt_queue_wrapper { queue = Queue }) -> queue:to_list(Queue).
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% @doc
|
||||||
|
%% Clear queue.
|
||||||
|
%%
|
||||||
|
%% @end
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-spec clear(mqtt_queue()) -> mqtt_queue().
|
||||||
|
clear(Queue) -> Queue#mqtt_queue_wrapper{queue = queue:new()}.
|
||||||
|
|
|
@ -21,62 +21,60 @@
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
%%route chain... statistics
|
%%route chain... statistics
|
||||||
-module(emqtt_router).
|
-module(emqttd_router).
|
||||||
|
|
||||||
-include("emqtt.hrl").
|
-include("emqttd_packet.hrl").
|
||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
|
|
||||||
-define(SERVER, ?MODULE).
|
-define(SERVER, ?MODULE).
|
||||||
|
|
||||||
%% ------------------------------------------------------------------
|
|
||||||
%% API Function Exports
|
%% API Function Exports
|
||||||
%% ------------------------------------------------------------------
|
|
||||||
|
|
||||||
-export([start_link/0]).
|
-export([start_link/0]).
|
||||||
|
|
||||||
%%Router Chain-->
|
%%Router Chain--> --->In Out<---
|
||||||
%%--->In
|
|
||||||
%%Out<---
|
|
||||||
-export([route/1]).
|
-export([route/1]).
|
||||||
|
|
||||||
%% ------------------------------------------------------------------
|
|
||||||
%% gen_server Function Exports
|
%% gen_server Function Exports
|
||||||
%% ------------------------------------------------------------------
|
|
||||||
|
|
||||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||||
terminate/2, code_change/3]).
|
terminate/2, code_change/3]).
|
||||||
|
|
||||||
%%----------------------------------------------------------------------------
|
-record(state, {}).
|
||||||
|
|
||||||
-ifdef(use_specs).
|
%%%=============================================================================
|
||||||
|
%%% API
|
||||||
-spec(start_link/1 :: () -> {ok, pid()}).
|
%%%=============================================================================
|
||||||
|
|
||||||
-spec route(mqtt_message()) -> ok.
|
|
||||||
|
|
||||||
-endif.
|
|
||||||
|
|
||||||
%% ------------------------------------------------------------------
|
|
||||||
%% API Function Definitions
|
|
||||||
%% ------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% @doc
|
||||||
|
%% Start emqttd router.
|
||||||
|
%%
|
||||||
|
%% @end
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-spec start_link() -> {ok, pid()} | ignore | {error, term()}.
|
||||||
start_link() ->
|
start_link() ->
|
||||||
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% @doc
|
||||||
|
%% Route mqtt message.
|
||||||
|
%%
|
||||||
|
%% @end
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-spec route(mqtt_message()) -> ok.
|
||||||
route(Msg) ->
|
route(Msg) ->
|
||||||
lager:info("Route ~s", [emqtt_message:dump(Msg)]),
|
lager:info("Route ~s", [emqttd_message:dump(Msg)]),
|
||||||
% need to retain?
|
% TODO: need to retain?
|
||||||
emqtt_server:retain(Msg),
|
emqttd_server:retain(Msg),
|
||||||
% unset flag and pubsub
|
% unset flag and pubsub
|
||||||
emqtt_pubsub:publish( emqtt_message:unset_flag(Msg) ).
|
emqttd_pubsub:publish(emqttd_message:unset_flag(Msg)).
|
||||||
|
|
||||||
%% ------------------------------------------------------------------
|
%%%=============================================================================
|
||||||
%% gen_server Function Definitions
|
%%% gen_server callbacks
|
||||||
%% ------------------------------------------------------------------
|
%%%=============================================================================
|
||||||
|
|
||||||
init(Args) ->
|
init([]) ->
|
||||||
{ok, Args, hibernate}.
|
{ok, #state{}, hibernate}.
|
||||||
|
|
||||||
handle_call(_Request, _From, State) ->
|
handle_call(_Request, _From, State) ->
|
||||||
{reply, ok, State}.
|
{reply, ok, State}.
|
||||||
|
@ -93,7 +91,7 @@ terminate(_Reason, _State) ->
|
||||||
code_change(_OldVsn, State, _Extra) ->
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
{ok, State}.
|
{ok, State}.
|
||||||
|
|
||||||
%% ------------------------------------------------------------------
|
%%%=============================================================================
|
||||||
%% Internal Function Definitions
|
%%% Internal functions
|
||||||
%% ------------------------------------------------------------------
|
%%%=============================================================================
|
||||||
|
|
|
@ -24,11 +24,11 @@
|
||||||
%%%
|
%%%
|
||||||
%%% @end
|
%%% @end
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
-module(emqtt_serialiser).
|
-module(emqttd_serialiser).
|
||||||
|
|
||||||
-author("feng@emqtt.io").
|
-author("feng@emqtt.io").
|
||||||
|
|
||||||
-include("emqtt_packet.hrl").
|
-include("emqttd_packet.hrl").
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([serialise/1]).
|
-export([serialise/1]).
|
|
@ -24,7 +24,7 @@
|
||||||
%%% TODO: redesign...
|
%%% TODO: redesign...
|
||||||
%%% @end
|
%%% @end
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
-module(emqtt_server).
|
-module(emqttd_server).
|
||||||
|
|
||||||
-author('feng@slimpp.io').
|
-author('feng@slimpp.io').
|
||||||
|
|
||||||
|
@ -32,17 +32,15 @@
|
||||||
|
|
||||||
-define(SERVER, ?MODULE).
|
-define(SERVER, ?MODULE).
|
||||||
|
|
||||||
-include("emqtt.hrl").
|
-include("emqttd.hrl").
|
||||||
|
|
||||||
-include("emqtt_topic.hrl").
|
-include("emqttd_topic.hrl").
|
||||||
|
|
||||||
-include("emqtt_packet.hrl").
|
-include("emqttd_packet.hrl").
|
||||||
|
|
||||||
-record(emqtt_retained, {topic, qos, payload}).
|
|
||||||
|
|
||||||
-record(state, {store_limit}).
|
-record(state, {store_limit}).
|
||||||
|
|
||||||
-define(RETAINED_TAB, emqtt_retained).
|
-define(RETAINED_TAB, mqtt_retained).
|
||||||
|
|
||||||
-define(STORE_LIMIT, 1000000).
|
-define(STORE_LIMIT, 1000000).
|
||||||
|
|
||||||
|
@ -85,7 +83,7 @@ init([Opts]) ->
|
||||||
mnesia:create_table(?RETAINED_TAB, [
|
mnesia:create_table(?RETAINED_TAB, [
|
||||||
{type, ordered_set},
|
{type, ordered_set},
|
||||||
{ram_copies, [node()]},
|
{ram_copies, [node()]},
|
||||||
{attributes, record_info(fields, emqtt_retained)}]),
|
{attributes, record_info(fields, mqtt_retained)}]),
|
||||||
mnesia:add_table_copy(?RETAINED_TAB, node(), ram_copies),
|
mnesia:add_table_copy(?RETAINED_TAB, node(), ram_copies),
|
||||||
Limit = proplists:get_value(store_limit, Opts, ?STORE_LIMIT),
|
Limit = proplists:get_value(store_limit, Opts, ?STORE_LIMIT),
|
||||||
{ok, #state{store_limit = Limit}}.
|
{ok, #state{store_limit = Limit}}.
|
||||||
|
@ -102,10 +100,10 @@ handle_cast({retain, Msg = #mqtt_message{topic = Topic,
|
||||||
lager:error("Dropped message(retain) for table is full: ~p", [Msg]);
|
lager:error("Dropped message(retain) for table is full: ~p", [Msg]);
|
||||||
_ ->
|
_ ->
|
||||||
lager:debug("Retained message: ~p", [Msg]),
|
lager:debug("Retained message: ~p", [Msg]),
|
||||||
mnesia:dirty_write(#emqtt_retained{topic = Topic,
|
mnesia:dirty_write(#mqtt_retained{topic = Topic,
|
||||||
qos = Qos,
|
qos = Qos,
|
||||||
payload = Payload}),
|
payload = Payload}),
|
||||||
emqtt_metrics:set('messages/retained/count',
|
emqttd_metrics:set('messages/retained/count',
|
||||||
mnesia:table_info(?RETAINED_TAB, size))
|
mnesia:table_info(?RETAINED_TAB, size))
|
||||||
end,
|
end,
|
||||||
{noreply, State};
|
{noreply, State};
|
||||||
|
@ -131,14 +129,14 @@ match(Topics) ->
|
||||||
lists:flatten([match(Topic, RetainedTopics) || Topic <- Topics]).
|
lists:flatten([match(Topic, RetainedTopics) || Topic <- Topics]).
|
||||||
|
|
||||||
match(Topic, RetainedTopics) ->
|
match(Topic, RetainedTopics) ->
|
||||||
case emqtt_topic:type(#topic{name=Topic}) of
|
case emqttd_topic:type(#topic{name=Topic}) of
|
||||||
direct -> %% FIXME
|
direct -> %% FIXME
|
||||||
[Topic];
|
[Topic];
|
||||||
wildcard ->
|
wildcard ->
|
||||||
[T || T <- RetainedTopics, emqtt_topic:match(T, Topic)]
|
[T || T <- RetainedTopics, emqttd_topic:match(T, Topic)]
|
||||||
end.
|
end.
|
||||||
|
|
||||||
retained_msg(#emqtt_retained{topic = Topic, qos = Qos, payload = Payload}) ->
|
retained_msg(#mqtt_retained{topic = Topic, qos = Qos, payload = Payload}) ->
|
||||||
#mqtt_message{qos = Qos, retain = true, topic = Topic, payload = Payload}.
|
#mqtt_message{qos = Qos, retain = true, topic = Topic, payload = Payload}.
|
||||||
|
|
||||||
|
|
|
@ -24,11 +24,11 @@
|
||||||
%%%
|
%%%
|
||||||
%%% @end
|
%%% @end
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
-module(emqtt_session).
|
-module(emqttd_session).
|
||||||
|
|
||||||
-include("emqtt.hrl").
|
-include("emqttd.hrl").
|
||||||
|
|
||||||
-include("emqtt_packet.hrl").
|
-include("emqttd_packet.hrl").
|
||||||
|
|
||||||
%% API Function Exports
|
%% API Function Exports
|
||||||
-export([start/1,
|
-export([start/1,
|
||||||
|
@ -75,11 +75,11 @@
|
||||||
-spec start({boolean(), binary(), pid()}) -> {ok, session()}.
|
-spec start({boolean(), binary(), pid()}) -> {ok, session()}.
|
||||||
start({true = _CleanSess, ClientId, _ClientPid}) ->
|
start({true = _CleanSess, ClientId, _ClientPid}) ->
|
||||||
%%Destroy old session if CleanSess is true before.
|
%%Destroy old session if CleanSess is true before.
|
||||||
ok = emqtt_sm:destroy_session(ClientId),
|
ok = emqttd_sm:destroy_session(ClientId),
|
||||||
{ok, initial_state(ClientId)};
|
{ok, initial_state(ClientId)};
|
||||||
|
|
||||||
start({false = _CleanSess, ClientId, ClientPid}) ->
|
start({false = _CleanSess, ClientId, ClientPid}) ->
|
||||||
{ok, SessPid} = emqtt_sm:start_session(ClientId, ClientPid),
|
{ok, SessPid} = emqttd_sm:start_session(ClientId, ClientPid),
|
||||||
{ok, SessPid}.
|
{ok, SessPid}.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
@ -103,10 +103,10 @@ resume(SessPid, ClientId, ClientPid) when is_pid(SessPid) ->
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-spec publish(session(), {mqtt_qos(), mqtt_message()}) -> session().
|
-spec publish(session(), {mqtt_qos(), mqtt_message()}) -> session().
|
||||||
publish(Session, {?QOS_0, Message}) ->
|
publish(Session, {?QOS_0, Message}) ->
|
||||||
emqtt_router:route(Message), Session;
|
emqttd_router:route(Message), Session;
|
||||||
|
|
||||||
publish(Session, {?QOS_1, Message}) ->
|
publish(Session, {?QOS_1, Message}) ->
|
||||||
emqtt_router:route(Message), Session;
|
emqttd_router:route(Message), Session;
|
||||||
|
|
||||||
publish(SessState = #session_state{awaiting_rel = AwaitingRel},
|
publish(SessState = #session_state{awaiting_rel = AwaitingRel},
|
||||||
{?QOS_2, Message = #mqtt_message{msgid = MsgId}}) ->
|
{?QOS_2, Message = #mqtt_message{msgid = MsgId}}) ->
|
||||||
|
@ -151,7 +151,7 @@ puback(SessPid, {?PUBREC, PacketId}) when is_pid(SessPid) ->
|
||||||
puback(SessState = #session_state{client_id = ClientId,
|
puback(SessState = #session_state{client_id = ClientId,
|
||||||
awaiting_rel = Awaiting}, {?PUBREL, PacketId}) ->
|
awaiting_rel = Awaiting}, {?PUBREL, PacketId}) ->
|
||||||
case maps:find(PacketId, Awaiting) of
|
case maps:find(PacketId, Awaiting) of
|
||||||
{ok, Msg} -> emqtt_router:route(Msg);
|
{ok, Msg} -> emqttd_router:route(Msg);
|
||||||
error -> lager:warning("Session ~s: PUBREL PacketId '~p' not found!", [ClientId, PacketId])
|
error -> lager:warning("Session ~s: PUBREL PacketId '~p' not found!", [ClientId, PacketId])
|
||||||
end,
|
end,
|
||||||
SessState#session_state{awaiting_rel = maps:remove(PacketId, Awaiting)};
|
SessState#session_state{awaiting_rel = maps:remove(PacketId, Awaiting)};
|
||||||
|
@ -185,9 +185,9 @@ subscribe(SessState = #session_state{client_id = ClientId, submap = SubMap}, Top
|
||||||
_ -> lager:warning("~s resubscribe ~p", [ClientId, Resubs])
|
_ -> lager:warning("~s resubscribe ~p", [ClientId, Resubs])
|
||||||
end,
|
end,
|
||||||
SubMap1 = lists:foldl(fun({Name, Qos}, Acc) -> maps:put(Name, Qos, Acc) end, SubMap, Topics),
|
SubMap1 = lists:foldl(fun({Name, Qos}, Acc) -> maps:put(Name, Qos, Acc) end, SubMap, Topics),
|
||||||
{ok, GrantedQos} = emqtt_pubsub:subscribe(Topics, self()),
|
{ok, GrantedQos} = emqttd_pubsub:subscribe(Topics, self()),
|
||||||
%%TODO: should be gen_event and notification...
|
%%TODO: should be gen_event and notification...
|
||||||
emqtt_server:subscribe([ Name || {Name, _} <- Topics ], self()),
|
emqttd_server:subscribe([ Name || {Name, _} <- Topics ], self()),
|
||||||
{ok, SessState#session_state{submap = SubMap1}, GrantedQos};
|
{ok, SessState#session_state{submap = SubMap1}, GrantedQos};
|
||||||
|
|
||||||
subscribe(SessPid, Topics) when is_pid(SessPid) ->
|
subscribe(SessPid, Topics) when is_pid(SessPid) ->
|
||||||
|
@ -208,7 +208,7 @@ unsubscribe(SessState = #session_state{client_id = ClientId, submap = SubMap}, T
|
||||||
BadUnsubs -> lager:warning("~s should not unsubscribe ~p", [ClientId, BadUnsubs])
|
BadUnsubs -> lager:warning("~s should not unsubscribe ~p", [ClientId, BadUnsubs])
|
||||||
end,
|
end,
|
||||||
%%unsubscribe from topic tree
|
%%unsubscribe from topic tree
|
||||||
ok = emqtt_pubsub:unsubscribe(Topics, self()),
|
ok = emqttd_pubsub:unsubscribe(Topics, self()),
|
||||||
SubMap1 = lists:foldl(fun(Topic, Acc) -> maps:remove(Topic, Acc) end, SubMap, Topics),
|
SubMap1 = lists:foldl(fun(Topic, Acc) -> maps:remove(Topic, Acc) end, SubMap, Topics),
|
||||||
{ok, SessState#session_state{submap = SubMap1}};
|
{ok, SessState#session_state{submap = SubMap1}};
|
||||||
|
|
||||||
|
@ -263,7 +263,7 @@ init([SessOpts, ClientId, ClientPid]) ->
|
||||||
true = link(ClientPid),
|
true = link(ClientPid),
|
||||||
State = initial_state(ClientId, ClientPid),
|
State = initial_state(ClientId, ClientPid),
|
||||||
Expires = proplists:get_value(expires, SessOpts, 1) * 3600,
|
Expires = proplists:get_value(expires, SessOpts, 1) * 3600,
|
||||||
MsgQueue = emqtt_queue:new(proplists:get_value(max_queue, SessOpts, 1000),
|
MsgQueue = emqttd_queue:new(proplists:get_value(max_queue, SessOpts, 1000),
|
||||||
proplists:get_value(store_qos0, SessOpts, false)),
|
proplists:get_value(store_qos0, SessOpts, false)),
|
||||||
{ok, State#session_state{expires = Expires,
|
{ok, State#session_state{expires = Expires,
|
||||||
msg_queue = MsgQueue}, hibernate}.
|
msg_queue = MsgQueue}, hibernate}.
|
||||||
|
@ -304,10 +304,10 @@ handle_cast({resume, ClientId, ClientPid}, State = #session_state{
|
||||||
%% send offline messages
|
%% send offline messages
|
||||||
lists:foreach(fun(Msg) ->
|
lists:foreach(fun(Msg) ->
|
||||||
ClientPid ! {dispatch, {self(), Msg}}
|
ClientPid ! {dispatch, {self(), Msg}}
|
||||||
end, emqtt_queue:all(Queue)),
|
end, emqttd_queue:all(Queue)),
|
||||||
|
|
||||||
{noreply, State#session_state{client_pid = ClientPid,
|
{noreply, State#session_state{client_pid = ClientPid,
|
||||||
msg_queue = emqtt_queue:clear(Queue),
|
msg_queue = emqttd_queue:clear(Queue),
|
||||||
expire_timer = undefined}, hibernate};
|
expire_timer = undefined}, hibernate};
|
||||||
|
|
||||||
handle_cast({publish, ?QOS_2, Message}, State) ->
|
handle_cast({publish, ?QOS_2, Message}, State) ->
|
||||||
|
@ -379,7 +379,7 @@ dispatch(Message = #mqtt_message{qos = Qos}, State = #session_state{client_pid =
|
||||||
NewState.
|
NewState.
|
||||||
|
|
||||||
queue(ClientId, Message, State = #session_state{msg_queue = Queue}) ->
|
queue(ClientId, Message, State = #session_state{msg_queue = Queue}) ->
|
||||||
State#session_state{msg_queue = emqtt_queue:in(ClientId, Message, Queue)}.
|
State#session_state{msg_queue = emqttd_queue:in(ClientId, Message, Queue)}.
|
||||||
|
|
||||||
next_msg_id(State = #session_state{message_id = 16#ffff}) ->
|
next_msg_id(State = #session_state{message_id = 16#ffff}) ->
|
||||||
State#session_state{message_id = 1};
|
State#session_state{message_id = 1};
|
|
@ -0,0 +1,54 @@
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>
|
||||||
|
%%%
|
||||||
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
%%% in the Software without restriction, including without limitation the rights
|
||||||
|
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
%%% copies of the Software, and to permit persons to whom the Software is
|
||||||
|
%%% furnished to do so, subject to the following conditions:
|
||||||
|
%%%
|
||||||
|
%%% The above copyright notice and this permission notice shall be included in all
|
||||||
|
%%% copies or substantial portions of the Software.
|
||||||
|
%%%
|
||||||
|
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
%%% SOFTWARE.
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @doc
|
||||||
|
%%% emqttd session supervisor.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
-module(emqttd_session_sup).
|
||||||
|
|
||||||
|
-author('feng@emqtt.io').
|
||||||
|
|
||||||
|
-behavior(supervisor).
|
||||||
|
|
||||||
|
-export([start_link/1, start_session/2]).
|
||||||
|
|
||||||
|
-export([init/1]).
|
||||||
|
|
||||||
|
%TODO: FIX COMMENTS...
|
||||||
|
|
||||||
|
-spec start_link([tuple()]) -> {ok, pid()}.
|
||||||
|
start_link(SessOpts) ->
|
||||||
|
supervisor:start_link({local, ?MODULE}, ?MODULE, [SessOpts]).
|
||||||
|
|
||||||
|
-spec start_session(binary(), pid()) -> {ok, pid()}.
|
||||||
|
start_session(ClientId, ClientPid) ->
|
||||||
|
supervisor:start_child(?MODULE, [ClientId, ClientPid]).
|
||||||
|
|
||||||
|
%%%=============================================================================
|
||||||
|
%%% Supervisor callbacks
|
||||||
|
%%%=============================================================================
|
||||||
|
init([SessOpts]) ->
|
||||||
|
{ok, {{simple_one_for_one, 0, 1},
|
||||||
|
[{session, {emqttd_session, start_link, [SessOpts]},
|
||||||
|
transient, 10000, worker, [emqttd_session]}]}}.
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
%%% SOFTWARE.
|
%%% SOFTWARE.
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% @doc
|
%%% @doc
|
||||||
%%% emqtt session manager.
|
%%% emqttd session manager.
|
||||||
%%%
|
%%%
|
||||||
%%% The Session state in the Server consists of:
|
%%% The Session state in the Server consists of:
|
||||||
%%% The existence of a Session, even if the rest of the Session state is empty.
|
%%% The existence of a Session, even if the rest of the Session state is empty.
|
||||||
|
@ -34,17 +34,17 @@
|
||||||
%%%
|
%%%
|
||||||
%%% @end
|
%%% @end
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
-module(emqtt_sm).
|
-module(emqttd_sm).
|
||||||
|
|
||||||
%%cleanSess: true | false
|
%%cleanSess: true | false
|
||||||
|
|
||||||
-include("emqtt.hrl").
|
-include("emqttd.hrl").
|
||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
|
|
||||||
-define(SERVER, ?MODULE).
|
-define(SERVER, ?MODULE).
|
||||||
|
|
||||||
-define(SESSION_TAB, emqtt_session).
|
-define(SESSION_TAB, mqtt_session).
|
||||||
|
|
||||||
%% API Function Exports
|
%% API Function Exports
|
||||||
-export([start_link/0]).
|
-export([start_link/0]).
|
||||||
|
@ -110,10 +110,10 @@ handle_call({start_session, ClientId, ClientPid}, _From, State) ->
|
||||||
Reply =
|
Reply =
|
||||||
case ets:lookup(?SESSION_TAB, ClientId) of
|
case ets:lookup(?SESSION_TAB, ClientId) of
|
||||||
[{_, SessPid, _MRef}] ->
|
[{_, SessPid, _MRef}] ->
|
||||||
emqtt_session:resume(SessPid, ClientId, ClientPid),
|
emqttd_session:resume(SessPid, ClientId, ClientPid),
|
||||||
{ok, SessPid};
|
{ok, SessPid};
|
||||||
[] ->
|
[] ->
|
||||||
case emqtt_session_sup:start_session(ClientId, ClientPid) of
|
case emqttd_session_sup:start_session(ClientId, ClientPid) of
|
||||||
{ok, SessPid} ->
|
{ok, SessPid} ->
|
||||||
MRef = erlang:monitor(process, SessPid),
|
MRef = erlang:monitor(process, SessPid),
|
||||||
ets:insert(?SESSION_TAB, {ClientId, SessPid, MRef}),
|
ets:insert(?SESSION_TAB, {ClientId, SessPid, MRef}),
|
||||||
|
@ -128,7 +128,7 @@ handle_call({destroy_session, ClientId}, _From, State) ->
|
||||||
case ets:lookup(?SESSION_TAB, ClientId) of
|
case ets:lookup(?SESSION_TAB, ClientId) of
|
||||||
[{_, SessPid, MRef}] ->
|
[{_, SessPid, MRef}] ->
|
||||||
erlang:demonitor(MRef),
|
erlang:demonitor(MRef),
|
||||||
emqtt_session:destroy(SessPid, ClientId),
|
emqttd_session:destroy(SessPid, ClientId),
|
||||||
ets:delete(?SESSION_TAB, ClientId);
|
ets:delete(?SESSION_TAB, ClientId);
|
||||||
[] ->
|
[] ->
|
||||||
ignore
|
ignore
|
||||||
|
@ -160,10 +160,10 @@ code_change(_OldVsn, State, _Extra) ->
|
||||||
|
|
||||||
setstats(State = #state{max = Max}) ->
|
setstats(State = #state{max = Max}) ->
|
||||||
Count = ets:info(?SESSION_TAB, size),
|
Count = ets:info(?SESSION_TAB, size),
|
||||||
emqtt_broker:setstat('sessions/count', Count),
|
emqttd_broker:setstat('sessions/count', Count),
|
||||||
if
|
if
|
||||||
Count > Max ->
|
Count > Max ->
|
||||||
emqtt_broker:setstat('sessions/max', Count),
|
emqttd_broker:setstat('sessions/max', Count),
|
||||||
State#state{max = Count};
|
State#state{max = Count};
|
||||||
true ->
|
true ->
|
||||||
State
|
State
|
|
@ -0,0 +1,67 @@
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>
|
||||||
|
%%%
|
||||||
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
%%% in the Software without restriction, including without limitation the rights
|
||||||
|
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
%%% copies of the Software, and to permit persons to whom the Software is
|
||||||
|
%%% furnished to do so, subject to the following conditions:
|
||||||
|
%%%
|
||||||
|
%%% The above copyright notice and this permission notice shall be included in all
|
||||||
|
%%% copies or substantial portions of the Software.
|
||||||
|
%%%
|
||||||
|
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
%%% SOFTWARE.
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @doc
|
||||||
|
%%% emqttd supervisor.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
-module(emqttd_sup).
|
||||||
|
|
||||||
|
-author('feng@emqtt.io').
|
||||||
|
|
||||||
|
-include("emqttd.hrl").
|
||||||
|
|
||||||
|
-behaviour(supervisor).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([start_link/0, start_child/1, start_child/2]).
|
||||||
|
|
||||||
|
%% Supervisor callbacks
|
||||||
|
-export([init/1]).
|
||||||
|
|
||||||
|
%% Helper macro for declaring children of supervisor
|
||||||
|
-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).
|
||||||
|
|
||||||
|
%%%=============================================================================
|
||||||
|
%%% API
|
||||||
|
%%%=============================================================================
|
||||||
|
|
||||||
|
start_link() ->
|
||||||
|
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||||
|
|
||||||
|
start_child(ChildSpec) when is_tuple(ChildSpec) ->
|
||||||
|
supervisor:start_child(?MODULE, ChildSpec).
|
||||||
|
|
||||||
|
%%
|
||||||
|
%% start_child(Mod::atom(), Type::type()) -> {ok, pid()}
|
||||||
|
%% @type type() = worker | supervisor
|
||||||
|
%%
|
||||||
|
start_child(Mod, Type) when is_atom(Mod) and is_atom(Type) ->
|
||||||
|
supervisor:start_child(?MODULE, ?CHILD(Mod, Type)).
|
||||||
|
|
||||||
|
%%%=============================================================================
|
||||||
|
%%% Supervisor callbacks
|
||||||
|
%%%=============================================================================
|
||||||
|
|
||||||
|
init([]) ->
|
||||||
|
{ok, {{one_for_all, 10, 100}, []}}.
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>
|
||||||
|
%%%
|
||||||
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
%%% in the Software without restriction, including without limitation the rights
|
||||||
|
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
%%% copies of the Software, and to permit persons to whom the Software is
|
||||||
|
%%% furnished to do so, subject to the following conditions:
|
||||||
|
%%%
|
||||||
|
%%% The above copyright notice and this permission notice shall be included in all
|
||||||
|
%%% copies or substantial portions of the Software.
|
||||||
|
%%%
|
||||||
|
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
%%% SOFTWARE.
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @doc
|
||||||
|
%%% emqttd throttle.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
-module(emqttd_throttle).
|
||||||
|
|
||||||
|
%% TODO:... 0.6.0...
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
%% SOFTWARE.
|
%% SOFTWARE.
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
-module(emqtt_topic).
|
-module(emqttd_topic).
|
||||||
|
|
||||||
-author('feng@emqtt.io').
|
-author('feng@emqtt.io').
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@
|
||||||
%% There can be any number of root nodes; that is, there can be any number of topic trees.
|
%% There can be any number of root nodes; that is, there can be any number of topic trees.
|
||||||
%% ------------------------------------------------------------------------
|
%% ------------------------------------------------------------------------
|
||||||
|
|
||||||
-include("emqtt_topic.hrl").
|
-include("emqttd_topic.hrl").
|
||||||
|
|
||||||
-export([new/1, type/1, match/2, validate/1, triples/1, words/1]).
|
-export([new/1, type/1, match/2, validate/1, triples/1, words/1]).
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
%%-----------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Copyright (c) 2012-2015, Feng Lee <feng@emqtt.io>
|
%% Copyright (c) 2012-2015, Feng Lee <feng@emqtt.io>
|
||||||
%%
|
%%
|
||||||
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
@ -19,11 +19,9 @@
|
||||||
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
%% SOFTWARE.
|
%% SOFTWARE.
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
%%% @doc
|
||||||
-module(emqtt_event).
|
%%% emqttd erlang vm.
|
||||||
|
%%%
|
||||||
-export([start_link/0]).
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
start_link() ->
|
-module(emqttd_vm).
|
||||||
gen_event:start_link({local, ?MODULE}).
|
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
-module(x).
|
||||||
|
|
||||||
|
%% ------------------------------------------------------------------
|
||||||
|
%% API Function Exports
|
||||||
|
%% ------------------------------------------------------------------
|
||||||
|
|
||||||
|
-export([start_link/0]).
|
||||||
|
|
||||||
|
%% ------------------------------------------------------------------
|
||||||
|
%% gen_server Function Exports
|
||||||
|
%% ------------------------------------------------------------------
|
||||||
|
|
||||||
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||||
|
terminate/2, code_change/3]).
|
||||||
|
|
||||||
|
%% ------------------------------------------------------------------
|
||||||
|
%% API Function Definitions
|
||||||
|
%% ------------------------------------------------------------------
|
||||||
|
|
||||||
|
start_link() ->
|
||||||
|
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
||||||
|
|
||||||
|
%% ------------------------------------------------------------------
|
||||||
|
%% gen_server Function Definitions
|
||||||
|
%% ------------------------------------------------------------------
|
||||||
|
|
||||||
|
init(Args) ->
|
||||||
|
{ok, Args}.
|
||||||
|
|
||||||
|
handle_call(_Request, _From, State) ->
|
||||||
|
{reply, ok, State}.
|
||||||
|
|
||||||
|
handle_cast(_Msg, State) ->
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
handle_info(_Info, State) ->
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
terminate(_Reason, _State) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
|
{ok, State}.
|
||||||
|
|
||||||
|
%% ------------------------------------------------------------------
|
||||||
|
%% Internal Function Definitions
|
||||||
|
%% ------------------------------------------------------------------
|
||||||
|
|
|
@ -19,9 +19,7 @@
|
||||||
|
|
||||||
{sub_dirs, [
|
{sub_dirs, [
|
||||||
"rel",
|
"rel",
|
||||||
"apps/emqtt"]}.
|
"apps/emqttd"]}.
|
||||||
|
|
||||||
{lib_dirs, ["apps/emqtt"]}.
|
|
||||||
|
|
||||||
{deps, [
|
{deps, [
|
||||||
{lager, ".*", {git, "git://github.com/basho/lager.git", {branch, "master"}}},
|
{lager, ".*", {git, "git://github.com/basho/lager.git", {branch, "master"}}},
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
{start_pg2, true}
|
{start_pg2, true}
|
||||||
]},
|
]},
|
||||||
{sasl, [
|
{sasl, [
|
||||||
{sasl_error_logger, {file, "log/emqtt_sasl.log"}}
|
{sasl_error_logger, {file, "log/emqttd_sasl.log"}}
|
||||||
]},
|
]},
|
||||||
{mnesia, [
|
{mnesia, [
|
||||||
{dir, "data"}
|
{dir, "data"}
|
||||||
|
@ -15,18 +15,18 @@
|
||||||
]},
|
]},
|
||||||
{lager, [
|
{lager, [
|
||||||
{error_logger_redirect, false},
|
{error_logger_redirect, false},
|
||||||
{crash_log, "log/emqtt_crash.log"},
|
{crash_log, "log/emqttd_crash.log"},
|
||||||
{handlers, [
|
{handlers, [
|
||||||
{lager_console_backend, debug},
|
{lager_console_backend, debug},
|
||||||
{lager_file_backend, [
|
{lager_file_backend, [
|
||||||
{file, "log/emqtt_error.log"},
|
{file, "log/emqttd_error.log"},
|
||||||
{level, error},
|
{level, error},
|
||||||
{size, 10485760},
|
{size, 10485760},
|
||||||
{date, "$D0"},
|
{date, "$D0"},
|
||||||
{count, 5}
|
{count, 5}
|
||||||
]},
|
]},
|
||||||
{lager_file_backend, [
|
{lager_file_backend, [
|
||||||
{file, "log/emqtt_info.log"},
|
{file, "log/emqttd_info.log"},
|
||||||
{level, info},
|
{level, info},
|
||||||
{size, 10485760},
|
{size, 10485760},
|
||||||
{date, "$D0"},
|
{date, "$D0"},
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
{esockd, [
|
{esockd, [
|
||||||
{logger, {lager, info}}
|
{logger, {lager, info}}
|
||||||
]},
|
]},
|
||||||
{emqtt, [
|
{emqttd, [
|
||||||
%Authetication. Internal, Anonymous Default.
|
%Authetication. Internal, Anonymous Default.
|
||||||
{auth, {anonymous, []}},
|
{auth, {anonymous, []}},
|
||||||
{access, []},
|
{access, []},
|
||||||
|
|
|
@ -146,11 +146,11 @@ case "$1" in
|
||||||
"exec $RUNNER_SCRIPT_DIR/$SCRIPT console" 2>&1
|
"exec $RUNNER_SCRIPT_DIR/$SCRIPT console" 2>&1
|
||||||
|
|
||||||
# Wait for the node to come up. We can't just ping it because
|
# Wait for the node to come up. We can't just ping it because
|
||||||
# distributed erlang comes up for a second before emqtt crashes
|
# distributed erlang comes up for a second before emqttd crashes
|
||||||
# (eg. in the case of an unwriteable disk). Once the node comes
|
# (eg. in the case of an unwriteable disk). Once the node comes
|
||||||
# up we check for the node watcher process. If that's running
|
# up we check for the node watcher process. If that's running
|
||||||
# then we assume things are good enough. This will at least let
|
# then we assume things are good enough. This will at least let
|
||||||
# the user know when emqtt is crashing right after startup.
|
# the user know when emqttd is crashing right after startup.
|
||||||
WAIT=${WAIT_FOR_ERLANG:-15}
|
WAIT=${WAIT_FOR_ERLANG:-15}
|
||||||
while [ $WAIT -gt 0 ]; do
|
while [ $WAIT -gt 0 ]; do
|
||||||
WAIT=`expr $WAIT - 1`
|
WAIT=`expr $WAIT - 1`
|
||||||
|
@ -159,11 +159,11 @@ case "$1" in
|
||||||
if [ "$?" -ne 0 ]; then
|
if [ "$?" -ne 0 ]; then
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
echo "emqtt is started successfully!"
|
echo "emqttd is started successfully!"
|
||||||
exit 0
|
exit 0
|
||||||
done
|
done
|
||||||
echo "emqtt failed to start within ${WAIT_FOR_ERLANG:-15} seconds,"
|
echo "emqttd failed to start within ${WAIT_FOR_ERLANG:-15} seconds,"
|
||||||
echo "see the output of 'emqtt console' for more information."
|
echo "see the output of 'emqttd console' for more information."
|
||||||
echo "If you want to wait longer, set the environment variable"
|
echo "If you want to wait longer, set the environment variable"
|
||||||
echo "WAIT_FOR_ERLANG to the number of seconds to wait."
|
echo "WAIT_FOR_ERLANG to the number of seconds to wait."
|
||||||
exit 1
|
exit 1
|
|
@ -1,6 +1,6 @@
|
||||||
@setlocal
|
@setlocal
|
||||||
|
|
||||||
@set node_name=emqtt
|
@set node_name=emqttd
|
||||||
|
|
||||||
@rem Get the absolute path to the parent directory,
|
@rem Get the absolute path to the parent directory,
|
||||||
@rem which is assumed to be the node root.
|
@rem which is assumed to be the node root.
|
|
@ -94,7 +94,7 @@ case "$1" in
|
||||||
fi
|
fi
|
||||||
shift
|
shift
|
||||||
|
|
||||||
$NODETOOL rpc emqtt_ctl status $@
|
$NODETOOL rpc emqttd_ctl status $@
|
||||||
;;
|
;;
|
||||||
|
|
||||||
cluster)
|
cluster)
|
||||||
|
@ -106,12 +106,12 @@ case "$1" in
|
||||||
# Make sure the local node IS running
|
# Make sure the local node IS running
|
||||||
RES=`$NODETOOL ping`
|
RES=`$NODETOOL ping`
|
||||||
if [ "$RES" != "pong" ]; then
|
if [ "$RES" != "pong" ]; then
|
||||||
echo "emqtt is not running!"
|
echo "emqttd is not running!"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
shift
|
shift
|
||||||
|
|
||||||
$NODETOOL rpc emqtt_ctl cluster $@
|
$NODETOOL rpc emqttd_ctl cluster $@
|
||||||
;;
|
;;
|
||||||
|
|
||||||
useradd)
|
useradd)
|
||||||
|
@ -123,12 +123,12 @@ case "$1" in
|
||||||
# Make sure the local node IS running
|
# Make sure the local node IS running
|
||||||
RES=`$NODETOOL ping`
|
RES=`$NODETOOL ping`
|
||||||
if [ "$RES" != "pong" ]; then
|
if [ "$RES" != "pong" ]; then
|
||||||
echo "emqtt is not running!"
|
echo "emqttd is not running!"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
shift
|
shift
|
||||||
|
|
||||||
$NODETOOL rpc emqtt_ctl useradd $@
|
$NODETOOL rpc emqttd_ctl useradd $@
|
||||||
;;
|
;;
|
||||||
|
|
||||||
userdel)
|
userdel)
|
||||||
|
@ -140,17 +140,17 @@ case "$1" in
|
||||||
# Make sure the local node IS running
|
# Make sure the local node IS running
|
||||||
RES=`$NODETOOL ping`
|
RES=`$NODETOOL ping`
|
||||||
if [ "$RES" != "pong" ]; then
|
if [ "$RES" != "pong" ]; then
|
||||||
echo "emqtt is not running!"
|
echo "emqttd is not running!"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
shift
|
shift
|
||||||
|
|
||||||
$NODETOOL rpc emqtt_ctl userdel $@
|
$NODETOOL rpc emqttd_ctl userdel $@
|
||||||
;;
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
echo "Usage: $SCRIPT"
|
echo "Usage: $SCRIPT"
|
||||||
echo " status #query emqtt status"
|
echo " status #query emqttd status"
|
||||||
echo " cluster [<Node>] #query or cluster nodes"
|
echo " cluster [<Node>] #query or cluster nodes"
|
||||||
echo " useradd <Username> <Password> #add user"
|
echo " useradd <Username> <Password> #add user"
|
||||||
echo " userdel <Username> #delete user"
|
echo " userdel <Username> #delete user"
|
|
@ -1,8 +1,8 @@
|
||||||
## Name of the node
|
## Name of the node
|
||||||
-name emqtt@127.0.0.1
|
-name emqttd@127.0.0.1
|
||||||
|
|
||||||
## Cookie for distributed erlang
|
## Cookie for distributed erlang
|
||||||
-setcookie emqttsecretcookie
|
-setcookie emqttdsecretcookie
|
||||||
|
|
||||||
## Heartbeat management; auto-restarts VM if it dies or becomes unresponsive
|
## Heartbeat management; auto-restarts VM if it dies or becomes unresponsive
|
||||||
## (Disabled by default..use with caution!)
|
## (Disabled by default..use with caution!)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
{lib_dirs, ["../apps", "../deps", "../plugins"]},
|
{lib_dirs, ["../apps", "../deps", "../plugins"]},
|
||||||
{erts, [{mod_cond, derived}, {app_file, strip}]},
|
{erts, [{mod_cond, derived}, {app_file, strip}]},
|
||||||
{app_file, strip},
|
{app_file, strip},
|
||||||
{rel, "emqtt", "0.2.0",
|
{rel, "emqttd", "0.5.0",
|
||||||
[
|
[
|
||||||
kernel,
|
kernel,
|
||||||
stdlib,
|
stdlib,
|
||||||
|
@ -17,14 +17,14 @@
|
||||||
lager,
|
lager,
|
||||||
esockd,
|
esockd,
|
||||||
mochiweb,
|
mochiweb,
|
||||||
emqtt
|
emqttd
|
||||||
]},
|
]},
|
||||||
{rel, "start_clean", "",
|
{rel, "start_clean", "",
|
||||||
[
|
[
|
||||||
kernel,
|
kernel,
|
||||||
stdlib
|
stdlib
|
||||||
]},
|
]},
|
||||||
{boot_rel, "emqtt"},
|
{boot_rel, "emqttd"},
|
||||||
{profile, embedded},
|
{profile, embedded},
|
||||||
{incl_cond, derived},
|
{incl_cond, derived},
|
||||||
%{mod_cond, derived},
|
%{mod_cond, derived},
|
||||||
|
@ -47,10 +47,10 @@
|
||||||
{app, lager, [{mod_cond, app}, {incl_cond, include}]},
|
{app, lager, [{mod_cond, app}, {incl_cond, include}]},
|
||||||
{app, esockd, [{mod_cond, app}, {incl_cond, include}]},
|
{app, esockd, [{mod_cond, app}, {incl_cond, include}]},
|
||||||
{app, mochiweb, [{mod_cond, app}, {incl_cond, include}]},
|
{app, mochiweb, [{mod_cond, app}, {incl_cond, include}]},
|
||||||
{app, emqtt, [{mod_cond, app}, {incl_cond, include}]}
|
{app, emqttd, [{mod_cond, app}, {incl_cond, include}]}
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
{target_dir, "emqtt"}.
|
{target_dir, "emqttd"}.
|
||||||
|
|
||||||
{overlay_vars, "vars.config"}.
|
{overlay_vars, "vars.config"}.
|
||||||
|
|
||||||
|
@ -60,9 +60,9 @@
|
||||||
{mkdir, "data/"},
|
{mkdir, "data/"},
|
||||||
{copy, "files/erl", "\{\{erts_vsn\}\}/bin/erl"},
|
{copy, "files/erl", "\{\{erts_vsn\}\}/bin/erl"},
|
||||||
{template, "files/nodetool", "\{\{erts_vsn\}\}/bin/nodetool"},
|
{template, "files/nodetool", "\{\{erts_vsn\}\}/bin/nodetool"},
|
||||||
{template, "files/emqtt", "bin/emqtt"},
|
{template, "files/emqttd", "bin/emqttd"},
|
||||||
{template, "files/emqtt_ctl", "bin/emqtt_ctl"},
|
{template, "files/emqttd_ctl", "bin/emqttd_ctl"},
|
||||||
{template, "files/emqtt.cmd", "bin/emqtt.cmd"},
|
{template, "files/emqttd.cmd", "bin/emqttd.cmd"},
|
||||||
{copy, "files/start_erl.cmd", "bin/start_erl.cmd"},
|
{copy, "files/start_erl.cmd", "bin/start_erl.cmd"},
|
||||||
{copy, "files/install_upgrade.escript", "bin/install_upgrade.escript"},
|
{copy, "files/install_upgrade.escript", "bin/install_upgrade.escript"},
|
||||||
{copy, "files/ssl/ssl.crt", "etc/ssl.crt"},
|
{copy, "files/ssl/ssl.crt", "etc/ssl.crt"},
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
%%
|
%%
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% bin/emqtt
|
%% bin/emqttd
|
||||||
%%
|
%%
|
||||||
{runner_script_dir, "$(cd ${0%/*} && pwd)"}.
|
{runner_script_dir, "$(cd ${0%/*} && pwd)"}.
|
||||||
{runner_base_dir, "${RUNNER_SCRIPT_DIR%/*}"}.
|
{runner_base_dir, "${RUNNER_SCRIPT_DIR%/*}"}.
|
||||||
|
|
Loading…
Reference in New Issue