Merge with EMQ X project

This commit is contained in:
Feng Lee 2017-10-09 19:43:06 +08:00
parent d9f14dacaf
commit 4b8cd18f5d
112 changed files with 3063 additions and 1821 deletions

4
.gitignore vendored
View File

@ -17,13 +17,13 @@ log/
*.so *.so
.erlang.mk/ .erlang.mk/
cover/ cover/
emqttd.d emqx.d
eunit.coverdata eunit.coverdata
test/ct.cover.spec test/ct.cover.spec
logs logs
ct.coverdata ct.coverdata
.idea/ .idea/
emqttd.iml emqx.iml
_rel/ _rel/
data/ data/
_build _build

View File

@ -2,6 +2,7 @@ language: erlang
otp_release: otp_release:
- 20.0 - 20.0
- 20.1
script: script:
- make - make

455
LICENSE-MPL-RabbitMQ Normal file
View File

@ -0,0 +1,455 @@
MOZILLA PUBLIC LICENSE
Version 1.1
---------------
1. Definitions.
1.0.1. "Commercial Use" means distribution or otherwise making the
Covered Code available to a third party.
1.1. "Contributor" means each entity that creates or contributes to
the creation of Modifications.
1.2. "Contributor Version" means the combination of the Original
Code, prior Modifications used by a Contributor, and the Modifications
made by that particular Contributor.
1.3. "Covered Code" means the Original Code or Modifications or the
combination of the Original Code and Modifications, in each case
including portions thereof.
1.4. "Electronic Distribution Mechanism" means a mechanism generally
accepted in the software development community for the electronic
transfer of data.
1.5. "Executable" means Covered Code in any form other than Source
Code.
1.6. "Initial Developer" means the individual or entity identified
as the Initial Developer in the Source Code notice required by Exhibit
A.
1.7. "Larger Work" means a work which combines Covered Code or
portions thereof with code not governed by the terms of this License.
1.8. "License" means this document.
1.8.1. "Licensable" means having the right to grant, to the maximum
extent possible, whether at the time of the initial grant or
subsequently acquired, any and all of the rights conveyed herein.
1.9. "Modifications" means any addition to or deletion from the
substance or structure of either the Original Code or any previous
Modifications. When Covered Code is released as a series of files, a
Modification is:
A. Any addition to or deletion from the contents of a file
containing Original Code or previous Modifications.
B. Any new file that contains any part of the Original Code or
previous Modifications.
1.10. "Original Code" means Source Code of computer software code
which is described in the Source Code notice required by Exhibit A as
Original Code, and which, at the time of its release under this
License is not already Covered Code governed by this License.
1.10.1. "Patent Claims" means any patent claim(s), now owned or
hereafter acquired, including without limitation, method, process,
and apparatus claims, in any patent Licensable by grantor.
1.11. "Source Code" means the preferred form of the Covered Code for
making modifications to it, including all modules it contains, plus
any associated interface definition files, scripts used to control
compilation and installation of an Executable, or source code
differential comparisons against either the Original Code or another
well known, available Covered Code of the Contributor's choice. The
Source Code can be in a compressed or archival form, provided the
appropriate decompression or de-archiving software is widely available
for no charge.
1.12. "You" (or "Your") means an individual or a legal entity
exercising rights under, and complying with all of the terms of, this
License or a future version of this License issued under Section 6.1.
For legal entities, "You" includes any entity which controls, is
controlled by, or is under common control with You. For purposes of
this definition, "control" means (a) the power, direct or indirect,
to cause the direction or management of such entity, whether by
contract or otherwise, or (b) ownership of more than fifty percent
(50%) of the outstanding shares or beneficial ownership of such
entity.
2. Source Code License.
2.1. The Initial Developer Grant.
The Initial Developer hereby grants You a world-wide, royalty-free,
non-exclusive license, subject to third party intellectual property
claims:
(a) under intellectual property rights (other than patent or
trademark) Licensable by Initial Developer to use, reproduce,
modify, display, perform, sublicense and distribute the Original
Code (or portions thereof) with or without Modifications, and/or
as part of a Larger Work; and
(b) under Patents Claims infringed by the making, using or
selling of Original Code, to make, have made, use, practice,
sell, and offer for sale, and/or otherwise dispose of the
Original Code (or portions thereof).
(c) the licenses granted in this Section 2.1(a) and (b) are
effective on the date Initial Developer first distributes
Original Code under the terms of this License.
(d) Notwithstanding Section 2.1(b) above, no patent license is
granted: 1) for code that You delete from the Original Code; 2)
separate from the Original Code; or 3) for infringements caused
by: i) the modification of the Original Code or ii) the
combination of the Original Code with other software or devices.
2.2. Contributor Grant.
Subject to third party intellectual property claims, each Contributor
hereby grants You a world-wide, royalty-free, non-exclusive license
(a) under intellectual property rights (other than patent or
trademark) Licensable by Contributor, to use, reproduce, modify,
display, perform, sublicense and distribute the Modifications
created by such Contributor (or portions thereof) either on an
unmodified basis, with other Modifications, as Covered Code
and/or as part of a Larger Work; and
(b) under Patent Claims infringed by the making, using, or
selling of Modifications made by that Contributor either alone
and/or in combination with its Contributor Version (or portions
of such combination), to make, use, sell, offer for sale, have
made, and/or otherwise dispose of: 1) Modifications made by that
Contributor (or portions thereof); and 2) the combination of
Modifications made by that Contributor with its Contributor
Version (or portions of such combination).
(c) the licenses granted in Sections 2.2(a) and 2.2(b) are
effective on the date Contributor first makes Commercial Use of
the Covered Code.
(d) Notwithstanding Section 2.2(b) above, no patent license is
granted: 1) for any code that Contributor has deleted from the
Contributor Version; 2) separate from the Contributor Version;
3) for infringements caused by: i) third party modifications of
Contributor Version or ii) the combination of Modifications made
by that Contributor with other software (except as part of the
Contributor Version) or other devices; or 4) under Patent Claims
infringed by Covered Code in the absence of Modifications made by
that Contributor.
3. Distribution Obligations.
3.1. Application of License.
The Modifications which You create or to which You contribute are
governed by the terms of this License, including without limitation
Section 2.2. The Source Code version of Covered Code may be
distributed only under the terms of this License or a future version
of this License released under Section 6.1, and You must include a
copy of this License with every copy of the Source Code You
distribute. You may not offer or impose any terms on any Source Code
version that alters or restricts the applicable version of this
License or the recipients' rights hereunder. However, You may include
an additional document offering the additional rights described in
Section 3.5.
3.2. Availability of Source Code.
Any Modification which You create or to which You contribute must be
made available in Source Code form under the terms of this License
either on the same media as an Executable version or via an accepted
Electronic Distribution Mechanism to anyone to whom you made an
Executable version available; and if made available via Electronic
Distribution Mechanism, must remain available for at least twelve (12)
months after the date it initially became available, or at least six
(6) months after a subsequent version of that particular Modification
has been made available to such recipients. You are responsible for
ensuring that the Source Code version remains available even if the
Electronic Distribution Mechanism is maintained by a third party.
3.3. Description of Modifications.
You must cause all Covered Code to which You contribute to contain a
file documenting the changes You made to create that Covered Code and
the date of any change. You must include a prominent statement that
the Modification is derived, directly or indirectly, from Original
Code provided by the Initial Developer and including the name of the
Initial Developer in (a) the Source Code, and (b) in any notice in an
Executable version or related documentation in which You describe the
origin or ownership of the Covered Code.
3.4. Intellectual Property Matters
(a) Third Party Claims.
If Contributor has knowledge that a license under a third party's
intellectual property rights is required to exercise the rights
granted by such Contributor under Sections 2.1 or 2.2,
Contributor must include a text file with the Source Code
distribution titled "LEGAL" which describes the claim and the
party making the claim in sufficient detail that a recipient will
know whom to contact. If Contributor obtains such knowledge after
the Modification is made available as described in Section 3.2,
Contributor shall promptly modify the LEGAL file in all copies
Contributor makes available thereafter and shall take other steps
(such as notifying appropriate mailing lists or newsgroups)
reasonably calculated to inform those who received the Covered
Code that new knowledge has been obtained.
(b) Contributor APIs.
If Contributor's Modifications include an application programming
interface and Contributor has knowledge of patent licenses which
are reasonably necessary to implement that API, Contributor must
also include this information in the LEGAL file.
(c) Representations.
Contributor represents that, except as disclosed pursuant to
Section 3.4(a) above, Contributor believes that Contributor's
Modifications are Contributor's original creation(s) and/or
Contributor has sufficient rights to grant the rights conveyed by
this License.
3.5. Required Notices.
You must duplicate the notice in Exhibit A in each file of the Source
Code. If it is not possible to put such notice in a particular Source
Code file due to its structure, then You must include such notice in a
location (such as a relevant directory) where a user would be likely
to look for such a notice. If You created one or more Modification(s)
You may add your name as a Contributor to the notice described in
Exhibit A. You must also duplicate this License in any documentation
for the Source Code where You describe recipients' rights or ownership
rights relating to Covered Code. You may choose to offer, and to
charge a fee for, warranty, support, indemnity or liability
obligations to one or more recipients of Covered Code. However, You
may do so only on Your own behalf, and not on behalf of the Initial
Developer or any Contributor. You must make it absolutely clear than
any such warranty, support, indemnity or liability obligation is
offered by You alone, and You hereby agree to indemnify the Initial
Developer and every Contributor for any liability incurred by the
Initial Developer or such Contributor as a result of warranty,
support, indemnity or liability terms You offer.
3.6. Distribution of Executable Versions.
You may distribute Covered Code in Executable form only if the
requirements of Section 3.1-3.5 have been met for that Covered Code,
and if You include a notice stating that the Source Code version of
the Covered Code is available under the terms of this License,
including a description of how and where You have fulfilled the
obligations of Section 3.2. The notice must be conspicuously included
in any notice in an Executable version, related documentation or
collateral in which You describe recipients' rights relating to the
Covered Code. You may distribute the Executable version of Covered
Code or ownership rights under a license of Your choice, which may
contain terms different from this License, provided that You are in
compliance with the terms of this License and that the license for the
Executable version does not attempt to limit or alter the recipient's
rights in the Source Code version from the rights set forth in this
License. If You distribute the Executable version under a different
license You must make it absolutely clear that any terms which differ
from this License are offered by You alone, not by the Initial
Developer or any Contributor. You hereby agree to indemnify the
Initial Developer and every Contributor for any liability incurred by
the Initial Developer or such Contributor as a result of any such
terms You offer.
3.7. Larger Works.
You may create a Larger Work by combining Covered Code with other code
not governed by the terms of this License and distribute the Larger
Work as a single product. In such a case, You must make sure the
requirements of this License are fulfilled for the Covered Code.
4. Inability to Comply Due to Statute or Regulation.
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Code due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description
must be included in the LEGAL file described in Section 3.4 and must
be included with all distributions of the Source Code. Except to the
extent prohibited by statute or regulation, such description must be
sufficiently detailed for a recipient of ordinary skill to be able to
understand it.
5. Application of this License.
This License applies to code to which the Initial Developer has
attached the notice in Exhibit A and to related Covered Code.
6. Versions of the License.
6.1. New Versions.
Netscape Communications Corporation ("Netscape") may publish revised
and/or new versions of the License from time to time. Each version
will be given a distinguishing version number.
6.2. Effect of New Versions.
Once Covered Code has been published under a particular version of the
License, You may always continue to use it under the terms of that
version. You may also choose to use such Covered Code under the terms
of any subsequent version of the License published by Netscape. No one
other than Netscape has the right to modify the terms applicable to
Covered Code created under this License.
6.3. Derivative Works.
If You create or use a modified version of this License (which you may
only do in order to apply it to code which is not already Covered Code
governed by this License), You must (a) rename Your license so that
the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
"MPL", "NPL" or any confusingly similar phrase do not appear in your
license (except to note that your license differs from this License)
and (b) otherwise make it clear that Your version of the license
contains terms which differ from the Mozilla Public License and
Netscape Public License. (Filling in the name of the Initial
Developer, Original Code or Contributor in the notice described in
Exhibit A shall not of themselves be deemed to be modifications of
this License.)
7. DISCLAIMER OF WARRANTY.
COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
8. TERMINATION.
8.1. This License and the rights granted hereunder will terminate
automatically if You fail to comply with terms herein and fail to cure
such breach within 30 days of becoming aware of the breach. All
sublicenses to the Covered Code which are properly granted shall
survive any termination of this License. Provisions which, by their
nature, must remain in effect beyond the termination of this License
shall survive.
8.2. If You initiate litigation by asserting a patent infringement
claim (excluding declatory judgment actions) against Initial Developer
or a Contributor (the Initial Developer or Contributor against whom
You file such action is referred to as "Participant") alleging that:
(a) such Participant's Contributor Version directly or indirectly
infringes any patent, then any and all rights granted by such
Participant to You under Sections 2.1 and/or 2.2 of this License
shall, upon 60 days notice from Participant terminate prospectively,
unless if within 60 days after receipt of notice You either: (i)
agree in writing to pay Participant a mutually agreeable reasonable
royalty for Your past and future use of Modifications made by such
Participant, or (ii) withdraw Your litigation claim with respect to
the Contributor Version against such Participant. If within 60 days
of notice, a reasonable royalty and payment arrangement are not
mutually agreed upon in writing by the parties or the litigation claim
is not withdrawn, the rights granted by Participant to You under
Sections 2.1 and/or 2.2 automatically terminate at the expiration of
the 60 day notice period specified above.
(b) any software, hardware, or device, other than such Participant's
Contributor Version, directly or indirectly infringes any patent, then
any rights granted to You by such Participant under Sections 2.1(b)
and 2.2(b) are revoked effective as of the date You first made, used,
sold, distributed, or had made, Modifications made by that
Participant.
8.3. If You assert a patent infringement claim against Participant
alleging that such Participant's Contributor Version directly or
indirectly infringes any patent where such claim is resolved (such as
by license or settlement) prior to the initiation of patent
infringement litigation, then the reasonable value of the licenses
granted by such Participant under Sections 2.1 or 2.2 shall be taken
into account in determining the amount or value of any payment or
license.
8.4. In the event of termination under Sections 8.1 or 8.2 above,
all end user license agreements (excluding distributors and resellers)
which have been validly granted by You or any distributor hereunder
prior to termination shall survive termination.
9. LIMITATION OF LIABILITY.
UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
(INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
10. U.S. GOVERNMENT END USERS.
The Covered Code is a "commercial item," as that term is defined in
48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
software" and "commercial computer software documentation," as such
terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
all U.S. Government End Users acquire Covered Code with only those
rights set forth herein.
11. MISCELLANEOUS.
This License represents the complete agreement concerning subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. This License shall be governed by
California law provisions (except to the extent applicable law, if
any, provides otherwise), excluding its conflict-of-law provisions.
With respect to disputes in which at least one party is a citizen of,
or an entity chartered or registered to do business in the United
States of America, any litigation relating to this License shall be
subject to the jurisdiction of the Federal Courts of the Northern
District of California, with venue lying in Santa Clara County,
California, with the losing party responsible for costs, including
without limitation, court costs and reasonable attorneys' fees and
expenses. The application of the United Nations Convention on
Contracts for the International Sale of Goods is expressly excluded.
Any law or regulation which provides that the language of a contract
shall be construed against the drafter shall not apply to this
License.
12. RESPONSIBILITY FOR CLAIMS.
As between Initial Developer and the Contributors, each party is
responsible for claims and damages arising, directly or indirectly,
out of its utilization of rights under this License and You agree to
work with Initial Developer and Contributors to distribute such
responsibility on an equitable basis. Nothing herein is intended or
shall be deemed to constitute any admission of liability.
13. MULTIPLE-LICENSED CODE.
Initial Developer may designate portions of the Covered Code as
"Multiple-Licensed". "Multiple-Licensed" means that the Initial
Developer permits you to utilize portions of the Covered Code under
Your choice of the NPL or the alternative licenses, if any, specified
by the Initial Developer in the file described in Exhibit A.
EXHIBIT A -Mozilla Public License.
``The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.
The Original Code is RabbitMQ.
The Initial Developer of the Original Code is Pivotal Software, Inc.
Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.''
[NOTE: The text of this Exhibit A may differ slightly from the text of
the notices in the Source Code files of the Original Code. You should
use the text of this Exhibit A rather than the text found in the
Original Code Source Code for Your Modifications.]

View File

@ -1,32 +1,34 @@
PROJECT = emqttd PROJECT = emqx
PROJECT_DESCRIPTION = Erlang MQTT Broker PROJECT_DESCRIPTION = EMQ X Broker
PROJECT_VERSION = 2.3 PROJECT_VERSION = 2.4
DEPS = goldrush gproc lager esockd ekka mochiweb pbkdf2 lager_syslog bcrypt clique jsx NO_AUTOPATCH = gen_rpc cuttlefish
DEPS = goldrush gproc gen_rpc lager esockd ekka mochiweb pbkdf2 lager_syslog bcrypt clique jsx
dep_goldrush = git https://github.com/basho/goldrush 0.1.9 dep_goldrush = git https://github.com/basho/goldrush 0.1.9
dep_gproc = git https://github.com/uwiger/gproc dep_gproc = git https://github.com/uwiger/gproc
dep_gen_rpc = git https://github.com/priestjim/gen_rpc
dep_getopt = git https://github.com/jcomellas/getopt v0.8.2 dep_getopt = git https://github.com/jcomellas/getopt v0.8.2
dep_lager = git https://github.com/basho/lager master dep_lager = git https://github.com/basho/lager master
dep_lager_syslog = git https://github.com/basho/lager_syslog
dep_jsx = git https://github.com/talentdeficit/jsx
dep_esockd = git https://github.com/emqtt/esockd master dep_esockd = git https://github.com/emqtt/esockd master
dep_ekka = git https://github.com/emqtt/ekka master dep_ekka = git https://github.com/emqtt/ekka master
dep_mochiweb = git https://github.com/emqtt/mochiweb master dep_mochiweb = git https://github.com/emqtt/mochiweb master
dep_pbkdf2 = git https://github.com/emqtt/pbkdf2 2.0.1 dep_pbkdf2 = git https://github.com/emqtt/pbkdf2 2.0.1
dep_lager_syslog = git https://github.com/basho/lager_syslog
dep_bcrypt = git https://github.com/smarkets/erlang-bcrypt master dep_bcrypt = git https://github.com/smarkets/erlang-bcrypt master
dep_clique = git https://github.com/emqtt/clique dep_clique = git https://github.com/emqtt/clique
dep_jsx = git https://github.com/talentdeficit/jsx
ERLC_OPTS += +debug_info ERLC_OPTS += +debug_info
ERLC_OPTS += +'{parse_transform, lager_transform}' ERLC_OPTS += +'{parse_transform, lager_transform}'
NO_AUTOPATCH = cuttlefish
BUILD_DEPS = cuttlefish BUILD_DEPS = cuttlefish
dep_cuttlefish = git https://github.com/emqtt/cuttlefish dep_cuttlefish = git https://github.com/emqtt/cuttlefish
TEST_DEPS = emqttc TEST_DEPS = websocket_client emqttc
dep_emqttc = git https://github.com/emqtt/emqttc dep_emqttc = git https://github.com/emqtt/emqttc
dep_websocket_client = git https://github.com/jeremyong/websocket_client
TEST_ERLC_OPTS += +debug_info TEST_ERLC_OPTS += +debug_info
TEST_ERLC_OPTS += +'{parse_transform, lager_transform}' TEST_ERLC_OPTS += +'{parse_transform, lager_transform}'
@ -34,11 +36,10 @@ TEST_ERLC_OPTS += +'{parse_transform, lager_transform}'
EUNIT_OPTS = verbose EUNIT_OPTS = verbose
# EUNIT_ERL_OPTS = # EUNIT_ERL_OPTS =
CT_SUITES = emqttd emqttd_access emqttd_lib emqttd_inflight emqttd_mod \ CT_SUITES = emqx emqx_lib emqx_topic emqx_trie emqx_mqueue emqx_inflight \
emqttd_net emqttd_mqueue emqttd_protocol emqttd_topic \ emqx_vm emqx_net emqx_protocol emqx_access emqx_config
emqttd_trie emqttd_vm emqttd_config
CT_OPTS = -cover test/ct.cover.spec -erl_args -name emqttd_ct@127.0.0.1 CT_OPTS = -cover test/ct.cover.spec -erl_args -name emqxct@127.0.0.1
COVER = true COVER = true
@ -49,8 +50,6 @@ DIALYZER_OPTS := --verbose --statistics -Werror_handling \
include erlang.mk include erlang.mk
app:: rebar.config
app.config:: app.config::
./deps/cuttlefish/cuttlefish -l info -e etc/ -c etc/emq.conf -i priv/emq.schema -d data/ ./deps/cuttlefish/cuttlefish -l info -e etc/ -c etc/emqx.conf -i priv/emqx.schema -d data/

2
erlang.mk vendored
View File

@ -2174,7 +2174,7 @@ help::
CT_RUN = ct_run \ CT_RUN = ct_run \
-no_auto_compile \ -no_auto_compile \
-noinput \ -noinput \
-pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \ -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(DEPS_DIR)/gen_rpc/_build/dev/lib/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
-dir $(TEST_DIR) \ -dir $(TEST_DIR) \
-logdir $(CURDIR)/logs -logdir $(CURDIR)/logs

View File

@ -1,5 +1,5 @@
##=================================================================== ##===================================================================
## EMQ Configuration R2.3 ## EMQ X Configuration R2.4
##=================================================================== ##===================================================================
##-------------------------------------------------------------------- ##--------------------------------------------------------------------
@ -7,7 +7,7 @@
##-------------------------------------------------------------------- ##--------------------------------------------------------------------
## Cluster name ## Cluster name
cluster.name = emqcl cluster.name = emqxcl
## Cluster discovery strategy: manual | static | mcast | dns | etcd | k8s ## Cluster discovery strategy: manual | static | mcast | dns | etcd | k8s
cluster.discovery = manual cluster.discovery = manual
@ -70,10 +70,10 @@ cluster.autoclean = 5m
##-------------------------------------------------------------------- ##--------------------------------------------------------------------
## Node name ## Node name
node.name = emq@127.0.0.1 node.name = emqx@127.0.0.1
## Cookie for distributed node ## Cookie for distributed node
node.cookie = emqsecretcookie node.cookie = emqxsecretcookie
## SMP support: enable, auto, disable ## SMP support: enable, auto, disable
node.smp = auto node.smp = auto
@ -93,7 +93,7 @@ node.async_threads = 32
node.process_limit = 256000 node.process_limit = 256000
## Sets the maximum number of simultaneously existing ports for this system ## Sets the maximum number of simultaneously existing ports for this system
node.max_ports = 65536 node.max_ports = 256000
## Set the distribution buffer busy limit (dist_buf_busy_limit) ## Set the distribution buffer busy limit (dist_buf_busy_limit)
node.dist_buffer_size = 32MB node.dist_buffer_size = 32MB
@ -113,7 +113,40 @@ node.dist_net_ticktime = 60
## Distributed node port range ## Distributed node port range
node.dist_listen_min = 6369 node.dist_listen_min = 6369
node.dist_listen_max = 6379 node.dist_listen_max = 6369
##--------------------------------------------------------------------
## RPC Args
##--------------------------------------------------------------------
## TCP server port.
rpc.tcp_server_port = 5369
## Default TCP port for outgoing connections
rpc.tcp_client_port = 5369
## Client connect timeout
rpc.connect_timeout = 5000
## Client and Server send timeout
rpc.send_timeout = 5000
## Authentication timeout
rpc.authentication_timeout = 5000
## Default receive timeout for call() functions
rpc.call_receive_timeout = 15000
## Socket keepalive configuration
rpc.socket_keepalive_idle = 7200
## Seconds between probes
rpc.socket_keepalive_interval = 75
## Probes lost to close the connection
rpc.socket_keepalive_count = 9
## TODO: sndbuf, rcvbuf and buffer
##-------------------------------------------------------------------- ##--------------------------------------------------------------------
## Log ## Log
@ -311,6 +344,9 @@ listener.tcp.external.acceptors = 16
## Maximum number of concurrent clients ## Maximum number of concurrent clients
listener.tcp.external.max_clients = 102400 listener.tcp.external.max_clients = 102400
## TODO:
## listener.tcp.external.zone = external
#listener.tcp.external.mountpoint = external/ #listener.tcp.external.mountpoint = external/
## Rate Limit. Format is 'burst,rate', Unit is KB/Sec ## Rate Limit. Format is 'burst,rate', Unit is KB/Sec
@ -347,6 +383,8 @@ listener.tcp.internal.acceptors = 16
## Maximum number of concurrent clients ## Maximum number of concurrent clients
listener.tcp.internal.max_clients = 102400 listener.tcp.internal.max_clients = 102400
#listener.tcp.internal.zone = internal
#listener.tcp.external.mountpoint = internal/ #listener.tcp.external.mountpoint = internal/
## Rate Limit. Format is 'burst,rate', Unit is KB/Sec ## Rate Limit. Format is 'burst,rate', Unit is KB/Sec
@ -375,7 +413,10 @@ listener.ssl.external = 8883
listener.ssl.external.acceptors = 16 listener.ssl.external.acceptors = 16
## Maximum number of concurrent clients ## Maximum number of concurrent clients
listener.ssl.external.max_clients = 1024 listener.ssl.external.max_clients = 102400
## Authentication Zone
## listener.ssl.external.zone = external
## listener.ssl.external.mountpoint = inbound/ ## listener.ssl.external.mountpoint = inbound/
@ -478,6 +519,8 @@ listener.ws.external.acceptors = 4
listener.ws.external.max_clients = 64 listener.ws.external.max_clients = 64
## listener.ws.external.zone = external
listener.ws.external.access.1 = allow all listener.ws.external.access.1 = allow all
## TCP Options ## TCP Options
@ -500,6 +543,8 @@ listener.wss.external.acceptors = 4
listener.wss.external.max_clients = 64 listener.wss.external.max_clients = 64
## listener.wss.external.zone = external
listener.wss.external.access.1 = allow all listener.wss.external.access.1 = allow all
## SSL Options ## SSL Options

View File

@ -24,7 +24,7 @@
-define(PROTOCOL_VERSION, "MQTT/5.0"). -define(PROTOCOL_VERSION, "MQTT/5.0").
-define(ERTS_MINIMUM, "8.0"). -define(ERTS_MINIMUM, "9.0").
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Sys/Queue/Share Topics' Prefix %% Sys/Queue/Share Topics' Prefix

View File

@ -35,19 +35,19 @@
-define(UNEXPECTED_REQ(Req, State), -define(UNEXPECTED_REQ(Req, State),
(begin (begin
lager:error("Unexpected Request: ~p", [Req]), lager:error("[~s] Unexpected Request: ~p", [?MODULE, Req]),
{reply, {error, unexpected_request}, State} {reply, {error, unexpected_request}, State}
end)). end)).
-define(UNEXPECTED_MSG(Msg, State), -define(UNEXPECTED_MSG(Msg, State),
(begin (begin
lager:error("Unexpected Message: ~p", [Msg]), lager:error("[~s] Unexpected Message: ~p", [?MODULE, Msg]),
{noreply, State} {noreply, State}
end)). end)).
-define(UNEXPECTED_INFO(Info, State), -define(UNEXPECTED_INFO(Info, State),
(begin (begin
lager:error("Unexpected Info: ~p", [Info]), lager:error("[~s] Unexpected Info: ~p", [?MODULE, Info]),
{noreply, State} {noreply, State}
end)). end)).
@ -59,19 +59,3 @@
-define(FULLSWEEP_OPTS, [{fullsweep_after, 10}]). -define(FULLSWEEP_OPTS, [{fullsweep_after, 10}]).
-define(SUCCESS, 0). %% Success
-define(ERROR1, 101). %% badrpc
-define(ERROR2, 102). %% Unknown error
-define(ERROR3, 103). %% Username or password error
-define(ERROR4, 104). %% Empty username or password
-define(ERROR5, 105). %% User does not exist
-define(ERROR6, 106). %% Admin can not be deleted
-define(ERROR7, 107). %% Missing request parameter
-define(ERROR8, 108). %% Request parameter type error
-define(ERROR9, 109). %% Request parameter is not a json
-define(ERROR10, 110). %% Plugin has been loaded
-define(ERROR11, 111). %% Plugin has been loaded
-define(ERROR12, 112). %% Client not online
-define(ERROR13, 113). %% User already exist
-define(ERROR14, 114). %% OldPassword error

17
include/emqx_rest.hrl Normal file
View File

@ -0,0 +1,17 @@
-define(SUCCESS, 0). %% Success
-define(ERROR1, 101). %% badrpc
-define(ERROR2, 102). %% Unknown error
-define(ERROR3, 103). %% Username or password error
-define(ERROR4, 104). %% Empty username or password
-define(ERROR5, 105). %% User does not exist
-define(ERROR6, 106). %% Admin can not be deleted
-define(ERROR7, 107). %% Missing request parameter
-define(ERROR8, 108). %% Request parameter type error
-define(ERROR9, 109). %% Request parameter is not a json
-define(ERROR10, 110). %% Plugin has been loaded
-define(ERROR11, 111). %% Plugin has been loaded
-define(ERROR12, 112). %% Client not online
-define(ERROR13, 113). %% User already exist
-define(ERROR14, 114). %% OldPassword error

View File

@ -1,5 +1,5 @@
%%-*- mode: erlang -*- %%-*- mode: erlang -*-
%% EMQ R2.3 config mapping %% EMQ X config mapping
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Cluster %% Cluster
@ -7,7 +7,7 @@
%% @doc Cluster name %% @doc Cluster name
{mapping, "cluster.name", "ekka.cluster_name", [ {mapping, "cluster.name", "ekka.cluster_name", [
{default, emqcl}, {default, emqxcl},
{datatype, atom} {datatype, atom}
]}. ]}.
@ -165,12 +165,12 @@ end}.
%% @doc Erlang node name %% @doc Erlang node name
{mapping, "node.name", "vm_args.-name", [ {mapping, "node.name", "vm_args.-name", [
{default, "emq@127.0.0.1"} {default, "emqx@127.0.0.1"}
]}. ]}.
%% @doc Secret cookie for distributed erlang node %% @doc Secret cookie for distributed erlang node
{mapping, "node.cookie", "vm_args.-setcookie", [ {mapping, "node.cookie", "vm_args.-setcookie", [
{default, "emqsecretcookie"} {default, "emqxsecretcookie"}
]}. ]}.
%% @doc SMP Support %% @doc SMP Support
@ -302,6 +302,64 @@ end}.
hidden hidden
]}. ]}.
%%--------------------------------------------------------------------
%% RPC Args
%%--------------------------------------------------------------------
%% RPC server port.
{mapping, "rpc.tcp_server_port", "gen_rpc.tcp_server_port", [
{default, 5369},
{datatype, integer}
]}.
%% Default TCP port for outgoing connections
{mapping, "rpc.tcp_client_port", "gen_rpc.tcp_client_port", [
{default, 5369},
{datatype, integer}
]}.
%% Client connect timeout
{mapping, "rpc.connect_timeout", "gen_rpc.connect_timeout", [
{default, 5000},
{datatype, integer}
]}.
%% Client and Server send timeout
{mapping, "rpc.send_timeout", "gen_rpc.send_timeout", [
{default, 5000},
{datatype, integer}
]}.
%% Authentication timeout
{mapping, "rpc.authentication_timeout", "gen_rpc.authentication_timeout", [
{default, 5000},
{datatype, integer}
]}.
%% Default receive timeout for call() functions
{mapping, "rpc.call_receive_timeout", "gen_rpc.call_receive_timeout", [
{default, 15000},
{datatype, integer}
]}.
%% Socket keepalive configuration
{mapping, "rpc.socket_keepalive_idle", "gen_rpc.socket_keepalive_idle", [
{default, 7200},
{datatype, integer}
]}.
%% Seconds between probes
{mapping, "rpc.socket_keepalive_interval", "gen_rpc.socket_keepalive_interval", [
{default, 75},
{datatype, integer}
]}.
%% Probes lost to close the connection
{mapping, "rpc.socket_keepalive_count", "gen_rpc.socket_keepalive_count", [
{default, 9},
{datatype, integer}
]}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Log %% Log
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
@ -331,13 +389,17 @@ end}.
{datatype, file} {datatype, file}
]}. ]}.
{mapping, "log.info.file", "lager.handlers", [
{datatype, file}
]}.
{mapping, "log.syslog", "lager.handlers", [ {mapping, "log.syslog", "lager.handlers", [
{default, off}, {default, off},
{datatype, flag} {datatype, flag}
]}. ]}.
{mapping, "log.syslog.identity", "lager.handlers", [ {mapping, "log.syslog.identity", "lager.handlers", [
{default, "emqttd"}, {default, "emqx"},
{datatype, string} {datatype, string}
]}. ]}.
@ -366,7 +428,7 @@ end}.
{translation, {translation,
"lager.handlers", "lager.handlers",
fun(Conf) -> fun(Conf) ->
ErrorHandler = case cuttlefish:conf_get("log.error.file", Conf) of ErrorHandler = case cuttlefish:conf_get("log.error.file", Conf, undefined) of
undefined -> []; undefined -> [];
ErrorFilename -> [{lager_file_backend, [{file, ErrorFilename}, ErrorFilename -> [{lager_file_backend, [{file, ErrorFilename},
{level, error}, {level, error},
@ -374,11 +436,19 @@ end}.
{date, "$D0"}, {date, "$D0"},
{count, 5}]}] {count, 5}]}]
end, end,
InfoHandler = case cuttlefish:conf_get("log.info.file", Conf, undefined) of
undefined -> [];
InfoFilename -> [{lager_file_backend, [{file, InfoFilename},
{level, info},
{size, 10485760},
{date, "$D0"},
{count, 5}]}]
end,
ConsoleLogLevel = cuttlefish:conf_get("log.console.level", Conf), ConsoleLogLevel = cuttlefish:conf_get("log.console.level", Conf),
ConsoleLogFile = cuttlefish:conf_get("log.console.file", Conf), ConsoleLogFile = cuttlefish:conf_get("log.console.file", Conf),
ConsoleHandler = {lager_console_backend, ConsoleLogLevel}, ConsoleHandler = {lager_console_backend, [{level, ConsoleLogLevel}]},
ConsoleFileHandler = {lager_file_backend, [{file, ConsoleLogFile}, ConsoleFileHandler = {lager_file_backend, [{file, ConsoleLogFile},
{level, ConsoleLogLevel}, {level, ConsoleLogLevel},
{size, 10485760}, {size, 10485760},
@ -400,7 +470,7 @@ end}.
cuttlefish:conf_get("log.syslog.level", Conf)]}] cuttlefish:conf_get("log.syslog.level", Conf)]}]
end, end,
ConsoleHandlers ++ ErrorHandler ++ SyslogHandler ConsoleHandlers ++ ErrorHandler ++ InfoHandler ++SyslogHandler
end end
}. }.
@ -435,25 +505,25 @@ end}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @doc Allow Anonymous %% @doc Allow Anonymous
{mapping, "mqtt.allow_anonymous", "emqttd.allow_anonymous", [ {mapping, "mqtt.allow_anonymous", "emqx.allow_anonymous", [
{default, false}, {default, false},
{datatype, {enum, [true, false]}} {datatype, {enum, [true, false]}}
]}. ]}.
%% @doc ACL nomatch %% @doc ACL nomatch
{mapping, "mqtt.acl_nomatch", "emqttd.acl_nomatch", [ {mapping, "mqtt.acl_nomatch", "emqx.acl_nomatch", [
{default, allow}, {default, allow},
{datatype, {enum, [allow, deny]}} {datatype, {enum, [allow, deny]}}
]}. ]}.
%% @doc Default ACL File %% @doc Default ACL File
{mapping, "mqtt.acl_file", "emqttd.acl_file", [ {mapping, "mqtt.acl_file", "emqx.acl_file", [
{datatype, string}, {datatype, string},
hidden hidden
]}. ]}.
%% @doc Cache ACL for PUBLISH %% @doc Cache ACL for PUBLISH
{mapping, "mqtt.cache_acl", "emqttd.cache_acl", [ {mapping, "mqtt.cache_acl", "emqx.cache_acl", [
{default, true}, {default, true},
{datatype, {enum, [true, false]}} {datatype, {enum, [true, false]}}
]}. ]}.
@ -463,13 +533,13 @@ end}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @doc Set the Max ClientId Length Allowed. %% @doc Set the Max ClientId Length Allowed.
{mapping, "mqtt.max_clientid_len", "emqttd.protocol", [ {mapping, "mqtt.max_clientid_len", "emqx.protocol", [
{default, 1024}, {default, 1024},
{datatype, integer} {datatype, integer}
]}. ]}.
%% @doc Max Packet Size Allowed, 64K by default. %% @doc Max Packet Size Allowed, 64K by default.
{mapping, "mqtt.max_packet_size", "emqttd.protocol", [ {mapping, "mqtt.max_packet_size", "emqx.protocol", [
{default, "64KB"}, {default, "64KB"},
{datatype, bytesize} {datatype, bytesize}
]}. ]}.
@ -480,13 +550,13 @@ end}.
{datatype, float} {datatype, float}
]}. ]}.
{translation, "emqttd.protocol", fun(Conf) -> {translation, "emqx.protocol", fun(Conf) ->
[{max_clientid_len, cuttlefish:conf_get("mqtt.max_clientid_len", Conf)}, [{max_clientid_len, cuttlefish:conf_get("mqtt.max_clientid_len", Conf)},
{max_packet_size, cuttlefish:conf_get("mqtt.max_packet_size", Conf)}, {max_packet_size, cuttlefish:conf_get("mqtt.max_packet_size", Conf)},
{keepalive_backoff, cuttlefish:conf_get("mqtt.keepalive_backoff", Conf)}] {keepalive_backoff, cuttlefish:conf_get("mqtt.keepalive_backoff", Conf)}]
end}. end}.
{mapping, "mqtt.websocket_protocol_header", "emqttd.websocket_protocol_header", [ {mapping, "mqtt.websocket_protocol_header", "emqx.websocket_protocol_header", [
{default, on}, {default, on},
{datatype, flag} {datatype, flag}
]}. ]}.
@ -496,7 +566,7 @@ end}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @doc Force the client to GC: integer %% @doc Force the client to GC: integer
{mapping, "mqtt.conn.force_gc_count", "emqttd.conn_force_gc_count", [ {mapping, "mqtt.conn.force_gc_count", "emqx.conn_force_gc_count", [
{datatype, integer} {datatype, integer}
]}. ]}.
@ -505,24 +575,24 @@ end}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @doc Max Publish Rate of Message %% @doc Max Publish Rate of Message
{mapping, "mqtt.client.max_publish_rate", "emqttd.client", [ {mapping, "mqtt.client.max_publish_rate", "emqx.client", [
{default, 0}, {default, 0},
{datatype, integer} {datatype, integer}
]}. ]}.
%% @doc Client Idle Timeout. %% @doc Client Idle Timeout.
{mapping, "mqtt.client.idle_timeout", "emqttd.client", [ {mapping, "mqtt.client.idle_timeout", "emqx.client", [
{default, "30s"}, {default, "30s"},
{datatype, {duration, ms}} {datatype, {duration, ms}}
]}. ]}.
%% @doc Enable Stats of Client. %% @doc Enable Stats of Client.
{mapping, "mqtt.client.enable_stats", "emqttd.client", [ {mapping, "mqtt.client.enable_stats", "emqx.client", [
{default, off}, {default, off},
{datatype, flag} {datatype, flag}
]}. ]}.
{translation, "emqttd.client", fun(Conf) -> {translation, "emqx.client", fun(Conf) ->
[{max_publish_rate, cuttlefish:conf_get("mqtt.client.max_publish_rate", Conf)}, [{max_publish_rate, cuttlefish:conf_get("mqtt.client.max_publish_rate", Conf)},
{client_idle_timeout, cuttlefish:conf_get("mqtt.client.idle_timeout", Conf)}, {client_idle_timeout, cuttlefish:conf_get("mqtt.client.idle_timeout", Conf)},
{client_enable_stats, cuttlefish:conf_get("mqtt.client.enable_stats", Conf)}] {client_enable_stats, cuttlefish:conf_get("mqtt.client.enable_stats", Conf)}]
@ -533,61 +603,61 @@ end}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @doc Max Number of Subscriptions Allowed %% @doc Max Number of Subscriptions Allowed
{mapping, "mqtt.session.max_subscriptions", "emqttd.session", [ {mapping, "mqtt.session.max_subscriptions", "emqx.session", [
{default, 0}, {default, 0},
{datatype, integer} {datatype, integer}
]}. ]}.
%% @doc Upgrade QoS? %% @doc Upgrade QoS?
{mapping, "mqtt.session.upgrade_qos", "emqttd.session", [ {mapping, "mqtt.session.upgrade_qos", "emqx.session", [
{default, off}, {default, off},
{datatype, flag} {datatype, flag}
]}. ]}.
%% @doc Max number of QoS 1 and 2 messages that can be “inflight” at one time. %% @doc Max number of QoS 1 and 2 messages that can be “inflight” at one time.
%% 0 means no limit %% 0 means no limit
{mapping, "mqtt.session.max_inflight", "emqttd.session", [ {mapping, "mqtt.session.max_inflight", "emqx.session", [
{default, 100}, {default, 100},
{datatype, integer} {datatype, integer}
]}. ]}.
%% @doc Retry interval for redelivering QoS1/2 messages. %% @doc Retry interval for redelivering QoS1/2 messages.
{mapping, "mqtt.session.retry_interval", "emqttd.session", [ {mapping, "mqtt.session.retry_interval", "emqx.session", [
{default, "20s"}, {default, "20s"},
{datatype, {duration, ms}} {datatype, {duration, ms}}
]}. ]}.
%% @doc Max Packets that Awaiting PUBREL, 0 means no limit %% @doc Max Packets that Awaiting PUBREL, 0 means no limit
{mapping, "mqtt.session.max_awaiting_rel", "emqttd.session", [ {mapping, "mqtt.session.max_awaiting_rel", "emqx.session", [
{default, 0}, {default, 0},
{datatype, integer} {datatype, integer}
]}. ]}.
%% @doc Awaiting PUBREL Timeout %% @doc Awaiting PUBREL Timeout
{mapping, "mqtt.session.await_rel_timeout", "emqttd.session", [ {mapping, "mqtt.session.await_rel_timeout", "emqx.session", [
{default, "20s"}, {default, "20s"},
{datatype, {duration, ms}} {datatype, {duration, ms}}
]}. ]}.
%% @doc Enable Stats %% @doc Enable Stats
{mapping, "mqtt.session.enable_stats", "emqttd.session", [ {mapping, "mqtt.session.enable_stats", "emqx.session", [
{default, off}, {default, off},
{datatype, flag} {datatype, flag}
]}. ]}.
%% @doc Session Expiry Interval %% @doc Session Expiry Interval
{mapping, "mqtt.session.expiry_interval", "emqttd.session", [ {mapping, "mqtt.session.expiry_interval", "emqx.session", [
{default, "2h"}, {default, "2h"},
{datatype, {duration, ms}} {datatype, {duration, ms}}
]}. ]}.
%% @doc Ignore message from self publish %% @doc Ignore message from self publish
{mapping, "mqtt.session.ignore_loop_deliver", "emqttd.session", [ {mapping, "mqtt.session.ignore_loop_deliver", "emqx.session", [
{default, false}, {default, false},
{datatype, {enum, [true, false]}} {datatype, {enum, [true, false]}}
]}. ]}.
{translation, "emqttd.session", fun(Conf) -> {translation, "emqx.session", fun(Conf) ->
[{max_subscriptions, cuttlefish:conf_get("mqtt.session.max_subscriptions", Conf)}, [{max_subscriptions, cuttlefish:conf_get("mqtt.session.max_subscriptions", Conf)},
{upgrade_qos, cuttlefish:conf_get("mqtt.session.upgrade_qos", Conf)}, {upgrade_qos, cuttlefish:conf_get("mqtt.session.upgrade_qos", Conf)},
{max_inflight, cuttlefish:conf_get("mqtt.session.max_inflight", Conf)}, {max_inflight, cuttlefish:conf_get("mqtt.session.max_inflight", Conf)},
@ -604,42 +674,42 @@ end}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @doc Type: simple | priority %% @doc Type: simple | priority
{mapping, "mqtt.mqueue.type", "emqttd.mqueue", [ {mapping, "mqtt.mqueue.type", "emqx.mqueue", [
{default, simple}, {default, simple},
{datatype, atom} {datatype, atom}
]}. ]}.
%% @doc Topic Priority: 0~255, Default is 0 %% @doc Topic Priority: 0~255, Default is 0
{mapping, "mqtt.mqueue.priority", "emqttd.mqueue", [ {mapping, "mqtt.mqueue.priority", "emqx.mqueue", [
{default, ""}, {default, ""},
{datatype, string} {datatype, string}
]}. ]}.
%% @doc Max queue length. Enqueued messages when persistent client disconnected, or inflight window is full. 0 means no limit. %% @doc Max queue length. Enqueued messages when persistent client disconnected, or inflight window is full. 0 means no limit.
{mapping, "mqtt.mqueue.max_length", "emqttd.mqueue", [ {mapping, "mqtt.mqueue.max_length", "emqx.mqueue", [
{default, 0}, {default, 0},
{datatype, integer} {datatype, integer}
]}. ]}.
%% @doc Low-water mark of queued messages %% @doc Low-water mark of queued messages
{mapping, "mqtt.mqueue.low_watermark", "emqttd.mqueue", [ {mapping, "mqtt.mqueue.low_watermark", "emqx.mqueue", [
{default, "20%"}, {default, "20%"},
{datatype, {percent, float}} {datatype, {percent, float}}
]}. ]}.
%% @doc High-water mark of queued messages %% @doc High-water mark of queued messages
{mapping, "mqtt.mqueue.high_watermark", "emqttd.mqueue", [ {mapping, "mqtt.mqueue.high_watermark", "emqx.mqueue", [
{default, "60%"}, {default, "60%"},
{datatype, {percent, float}} {datatype, {percent, float}}
]}. ]}.
%% @doc Queue Qos0 messages? %% @doc Queue Qos0 messages?
{mapping, "mqtt.mqueue.store_qos0", "emqttd.mqueue", [ {mapping, "mqtt.mqueue.store_qos0", "emqx.mqueue", [
{default, true}, {default, true},
{datatype, {enum, [true, false]}} {datatype, {enum, [true, false]}}
]}. ]}.
{translation, "emqttd.mqueue", fun(Conf) -> {translation, "emqx.mqueue", fun(Conf) ->
Opts = [{type, cuttlefish:conf_get("mqtt.mqueue.type", Conf, simple)}, Opts = [{type, cuttlefish:conf_get("mqtt.mqueue.type", Conf, simple)},
{max_length, cuttlefish:conf_get("mqtt.mqueue.max_length", Conf)}, {max_length, cuttlefish:conf_get("mqtt.mqueue.max_length", Conf)},
{low_watermark, cuttlefish:conf_get("mqtt.mqueue.low_watermark", Conf)}, {low_watermark, cuttlefish:conf_get("mqtt.mqueue.low_watermark", Conf)},
@ -658,7 +728,7 @@ end}.
%% MQTT Broker %% MQTT Broker
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
{mapping, "mqtt.broker.sys_interval", "emqttd.broker_sys_interval", [ {mapping, "mqtt.broker.sys_interval", "emqx.broker_sys_interval", [
{default, 60}, {default, 60},
{datatype, integer} {datatype, integer}
]}. ]}.
@ -667,22 +737,22 @@ end}.
%% MQTT PubSub %% MQTT PubSub
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
{mapping, "mqtt.pubsub.pool_size", "emqttd.pubsub", [ {mapping, "mqtt.pubsub.pool_size", "emqx.pubsub", [
{default, 8}, {default, 8},
{datatype, integer} {datatype, integer}
]}. ]}.
{mapping, "mqtt.pubsub.by_clientid", "emqttd.pubsub", [ {mapping, "mqtt.pubsub.by_clientid", "emqx.pubsub", [
{default, true}, {default, true},
{datatype, {enum, [true, false]}} {datatype, {enum, [true, false]}}
]}. ]}.
{mapping, "mqtt.pubsub.async", "emqttd.pubsub", [ {mapping, "mqtt.pubsub.async", "emqx.pubsub", [
{default, true}, {default, true},
{datatype, {enum, [true, false]}} {datatype, {enum, [true, false]}}
]}. ]}.
{translation, "emqttd.pubsub", fun(Conf) -> {translation, "emqx.pubsub", fun(Conf) ->
[{pool_size, cuttlefish:conf_get("mqtt.pubsub.pool_size", Conf)}, [{pool_size, cuttlefish:conf_get("mqtt.pubsub.pool_size", Conf)},
{by_clientid, cuttlefish:conf_get("mqtt.pubsub.by_clientid", Conf)}, {by_clientid, cuttlefish:conf_get("mqtt.pubsub.by_clientid", Conf)},
{async, cuttlefish:conf_get("mqtt.pubsub.async", Conf)}] {async, cuttlefish:conf_get("mqtt.pubsub.async", Conf)}]
@ -692,17 +762,17 @@ end}.
%% MQTT Bridge %% MQTT Bridge
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
{mapping, "mqtt.bridge.max_queue_len", "emqttd.bridge", [ {mapping, "mqtt.bridge.max_queue_len", "emqx.bridge", [
{default, 10000}, {default, 10000},
{datatype, integer} {datatype, integer}
]}. ]}.
{mapping, "mqtt.bridge.ping_down_interval", "emqttd.bridge", [ {mapping, "mqtt.bridge.ping_down_interval", "emqx.bridge", [
{default, 1}, {default, 1},
{datatype, integer} {datatype, integer}
]}. ]}.
{translation, "emqttd.bridge", fun(Conf) -> {translation, "emqx.bridge", fun(Conf) ->
[{max_queue_len, cuttlefish:conf_get("mqtt.bridge.max_queue_len", Conf)}, [{max_queue_len, cuttlefish:conf_get("mqtt.bridge.max_queue_len", Conf)},
{ping_down_interval, cuttlefish:conf_get("mqtt.bridge.ping_down_interval", Conf)}] {ping_down_interval, cuttlefish:conf_get("mqtt.bridge.ping_down_interval", Conf)}]
end}. end}.
@ -711,11 +781,11 @@ end}.
%% MQTT Plugins %% MQTT Plugins
%%------------------------------------------------------------------- %%-------------------------------------------------------------------
{mapping, "mqtt.plugins.etc_dir", "emqttd.plugins_etc_dir", [ {mapping, "mqtt.plugins.etc_dir", "emqx.plugins_etc_dir", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "mqtt.plugins.loaded_file", "emqttd.plugins_loaded_file", [ {mapping, "mqtt.plugins.loaded_file", "emqx.plugins_loaded_file", [
{datatype, string} {datatype, string}
]}. ]}.
@ -726,73 +796,73 @@ end}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% TCP Listeners %% TCP Listeners
{mapping, "listener.tcp.$name", "emqttd.listeners", [ {mapping, "listener.tcp.$name", "emqx.listeners", [
{datatype, [integer, ip]} {datatype, [integer, ip]}
]}. ]}.
{mapping, "listener.tcp.$name.acceptors", "emqttd.listeners", [ {mapping, "listener.tcp.$name.acceptors", "emqx.listeners", [
{default, 8}, {default, 8},
{datatype, integer} {datatype, integer}
]}. ]}.
{mapping, "listener.tcp.$name.max_clients", "emqttd.listeners", [ {mapping, "listener.tcp.$name.max_clients", "emqx.listeners", [
{default, 1024}, {default, 1024},
{datatype, integer} {datatype, integer}
]}. ]}.
{mapping, "listener.tcp.$name.zone", "emqttd.listeners", [ {mapping, "listener.tcp.$name.zone", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.tcp.$name.mountpoint", "emqttd.listeners", [ {mapping, "listener.tcp.$name.mountpoint", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.tcp.$name.rate_limit", "emqttd.listeners", [ {mapping, "listener.tcp.$name.rate_limit", "emqx.listeners", [
{default, undefined}, {default, undefined},
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.tcp.$name.access.$id", "emqttd.listeners", [ {mapping, "listener.tcp.$name.access.$id", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.tcp.$name.proxy_protocol", "emqttd.listeners", [ {mapping, "listener.tcp.$name.proxy_protocol", "emqx.listeners", [
%%{default, off}, %%{default, off},
{datatype, flag} {datatype, flag}
]}. ]}.
{mapping, "listener.tcp.$name.proxy_protocol_timeout", "emqttd.listeners", [ {mapping, "listener.tcp.$name.proxy_protocol_timeout", "emqx.listeners", [
%%{default, "5s"}, %%{default, "5s"},
{datatype, {duration, ms}} {datatype, {duration, ms}}
]}. ]}.
{mapping, "listener.tcp.$name.backlog", "emqttd.listeners", [ {mapping, "listener.tcp.$name.backlog", "emqx.listeners", [
{default, 1024}, {default, 1024},
{datatype, integer} {datatype, integer}
]}. ]}.
{mapping, "listener.tcp.$name.recbuf", "emqttd.listeners", [ {mapping, "listener.tcp.$name.recbuf", "emqx.listeners", [
{datatype, bytesize}, {datatype, bytesize},
hidden hidden
]}. ]}.
{mapping, "listener.tcp.$name.sndbuf", "emqttd.listeners", [ {mapping, "listener.tcp.$name.sndbuf", "emqx.listeners", [
{datatype, bytesize}, {datatype, bytesize},
hidden hidden
]}. ]}.
{mapping, "listener.tcp.$name.buffer", "emqttd.listeners", [ {mapping, "listener.tcp.$name.buffer", "emqx.listeners", [
{datatype, bytesize}, {datatype, bytesize},
hidden hidden
]}. ]}.
{mapping, "listener.tcp.$name.tune_buffer", "emqttd.listeners", [ {mapping, "listener.tcp.$name.tune_buffer", "emqx.listeners", [
{datatype, flag}, {datatype, flag},
hidden hidden
]}. ]}.
{mapping, "listener.tcp.$name.nodelay", "emqttd.listeners", [ {mapping, "listener.tcp.$name.nodelay", "emqx.listeners", [
{datatype, {enum, [true, false]}}, {datatype, {enum, [true, false]}},
hidden hidden
]}. ]}.
@ -800,186 +870,186 @@ end}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% SSL Listeners %% SSL Listeners
{mapping, "listener.ssl.$name", "emqttd.listeners", [ {mapping, "listener.ssl.$name", "emqx.listeners", [
{datatype, [integer, ip]} {datatype, [integer, ip]}
]}. ]}.
{mapping, "listener.ssl.$name.acceptors", "emqttd.listeners", [ {mapping, "listener.ssl.$name.acceptors", "emqx.listeners", [
{default, 8}, {default, 8},
{datatype, integer} {datatype, integer}
]}. ]}.
{mapping, "listener.ssl.$name.max_clients", "emqttd.listeners", [ {mapping, "listener.ssl.$name.max_clients", "emqx.listeners", [
{default, 1024}, {default, 1024},
{datatype, integer} {datatype, integer}
]}. ]}.
{mapping, "listener.ssl.$name.zone", "emqttd.listeners", [ {mapping, "listener.ssl.$name.zone", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.ssl.$name.mountpoint", "emqttd.listeners", [ {mapping, "listener.ssl.$name.mountpoint", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.ssl.$name.rate_limit", "emqttd.listeners", [ {mapping, "listener.ssl.$name.rate_limit", "emqx.listeners", [
{default, undefined}, {default, undefined},
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.ssl.$name.access.$id", "emqttd.listeners", [ {mapping, "listener.ssl.$name.access.$id", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.ssl.$name.proxy_protocol", "emqttd.listeners", [ {mapping, "listener.ssl.$name.proxy_protocol", "emqx.listeners", [
%%{default, off}, %%{default, off},
{datatype, flag} {datatype, flag}
]}. ]}.
{mapping, "listener.ssl.$name.proxy_protocol_timeout", "emqttd.listeners", [ {mapping, "listener.ssl.$name.proxy_protocol_timeout", "emqx.listeners", [
%%{default, "5s"}, %%{default, "5s"},
{datatype, {duration, ms}} {datatype, {duration, ms}}
]}. ]}.
{mapping, "listener.ssl.$name.backlog", "emqttd.listeners", [ {mapping, "listener.ssl.$name.backlog", "emqx.listeners", [
{default, 1024}, {default, 1024},
{datatype, integer} {datatype, integer}
]}. ]}.
{mapping, "listener.ssl.$name.recbuf", "emqttd.listeners", [ {mapping, "listener.ssl.$name.recbuf", "emqx.listeners", [
{datatype, bytesize}, {datatype, bytesize},
hidden hidden
]}. ]}.
{mapping, "listener.ssl.$name.sndbuf", "emqttd.listeners", [ {mapping, "listener.ssl.$name.sndbuf", "emqx.listeners", [
{datatype, bytesize}, {datatype, bytesize},
hidden hidden
]}. ]}.
{mapping, "listener.ssl.$name.buffer", "emqttd.listeners", [ {mapping, "listener.ssl.$name.buffer", "emqx.listeners", [
{datatype, bytesize}, {datatype, bytesize},
hidden hidden
]}. ]}.
{mapping, "listener.ssl.$name.tune_buffer", "emqttd.listeners", [ {mapping, "listener.ssl.$name.tune_buffer", "emqx.listeners", [
{datatype, flag}, {datatype, flag},
hidden hidden
]}. ]}.
{mapping, "listener.ssl.$name.nodelay", "emqttd.listeners", [ {mapping, "listener.ssl.$name.nodelay", "emqx.listeners", [
{datatype, {enum, [true, false]}}, {datatype, {enum, [true, false]}},
hidden hidden
]}. ]}.
{mapping, "listener.ssl.$name.tls_versions", "emqttd.listeners", [ {mapping, "listener.ssl.$name.tls_versions", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.ssl.$name.ciphers", "emqttd.listeners", [ {mapping, "listener.ssl.$name.ciphers", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.ssl.$name.handshake_timeout", "emqttd.listeners", [ {mapping, "listener.ssl.$name.handshake_timeout", "emqx.listeners", [
{default, "15s"}, {default, "15s"},
{datatype, {duration, ms}} {datatype, {duration, ms}}
]}. ]}.
{mapping, "listener.ssl.$name.dhfile", "emqttd.listeners", [ {mapping, "listener.ssl.$name.dhfile", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.ssl.$name.keyfile", "emqttd.listeners", [ {mapping, "listener.ssl.$name.keyfile", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.ssl.$name.certfile", "emqttd.listeners", [ {mapping, "listener.ssl.$name.certfile", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.ssl.$name.cacertfile", "emqttd.listeners", [ {mapping, "listener.ssl.$name.cacertfile", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.ssl.$name.verify", "emqttd.listeners", [ {mapping, "listener.ssl.$name.verify", "emqx.listeners", [
{datatype, atom} {datatype, atom}
]}. ]}.
{mapping, "listener.ssl.$name.fail_if_no_peer_cert", "emqttd.listeners", [ {mapping, "listener.ssl.$name.fail_if_no_peer_cert", "emqx.listeners", [
{datatype, {enum, [true, false]}} {datatype, {enum, [true, false]}}
]}. ]}.
{mapping, "listener.ssl.$name.secure_renegotiate", "emqttd.listeners", [ {mapping, "listener.ssl.$name.secure_renegotiate", "emqx.listeners", [
{datatype, flag} {datatype, flag}
]}. ]}.
{mapping, "listener.ssl.$name.reuse_sessions", "emqttd.listeners", [ {mapping, "listener.ssl.$name.reuse_sessions", "emqx.listeners", [
{default, on}, {default, on},
{datatype, flag} {datatype, flag}
]}. ]}.
{mapping, "listener.ssl.$name.honor_cipher_order", "emqttd.listeners", [ {mapping, "listener.ssl.$name.honor_cipher_order", "emqx.listeners", [
{datatype, flag} {datatype, flag}
]}. ]}.
{mapping, "listener.ssl.$name.peer_cert_as_username", "emqttd.listeners", [ {mapping, "listener.ssl.$name.peer_cert_as_username", "emqx.listeners", [
{datatype, {enum, [cn, dn]}} {datatype, {enum, [cn, dn]}}
]}. ]}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% MQTT/WebSocket Listeners %% MQTT/WebSocket Listeners
{mapping, "listener.ws.$name", "emqttd.listeners", [ {mapping, "listener.ws.$name", "emqx.listeners", [
{datatype, [integer, ip]} {datatype, [integer, ip]}
]}. ]}.
{mapping, "listener.ws.$name.acceptors", "emqttd.listeners", [ {mapping, "listener.ws.$name.acceptors", "emqx.listeners", [
{default, 8}, {default, 8},
{datatype, integer} {datatype, integer}
]}. ]}.
{mapping, "listener.ws.$name.max_clients", "emqttd.listeners", [ {mapping, "listener.ws.$name.max_clients", "emqx.listeners", [
{default, 1024}, {default, 1024},
{datatype, integer} {datatype, integer}
]}. ]}.
{mapping, "listener.ws.$name.rate_limit", "emqttd.listeners", [ {mapping, "listener.ws.$name.rate_limit", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.ws.$name.zone", "emqttd.listeners", [ {mapping, "listener.ws.$name.zone", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.ws.$name.access.$id", "emqttd.listeners", [ {mapping, "listener.ws.$name.access.$id", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.ws.$name.backlog", "emqttd.listeners", [ {mapping, "listener.ws.$name.backlog", "emqx.listeners", [
{default, 1024}, {default, 1024},
{datatype, integer} {datatype, integer}
]}. ]}.
{mapping, "listener.ws.$name.recbuf", "emqttd.listeners", [ {mapping, "listener.ws.$name.recbuf", "emqx.listeners", [
{datatype, bytesize}, {datatype, bytesize},
hidden hidden
]}. ]}.
{mapping, "listener.ws.$name.sndbuf", "emqttd.listeners", [ {mapping, "listener.ws.$name.sndbuf", "emqx.listeners", [
{datatype, bytesize}, {datatype, bytesize},
hidden hidden
]}. ]}.
{mapping, "listener.ws.$name.buffer", "emqttd.listeners", [ {mapping, "listener.ws.$name.buffer", "emqx.listeners", [
{datatype, bytesize}, {datatype, bytesize},
hidden hidden
]}. ]}.
{mapping, "listener.ws.$name.tune_buffer", "emqttd.listeners", [ {mapping, "listener.ws.$name.tune_buffer", "emqx.listeners", [
{datatype, flag}, {datatype, flag},
hidden hidden
]}. ]}.
{mapping, "listener.ws.$name.nodelay", "emqttd.listeners", [ {mapping, "listener.ws.$name.nodelay", "emqx.listeners", [
{datatype, {enum, [true, false]}}, {datatype, {enum, [true, false]}},
hidden hidden
]}. ]}.
@ -987,92 +1057,92 @@ end}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% MQTT/WebSocket/SSL Listeners %% MQTT/WebSocket/SSL Listeners
{mapping, "listener.wss.$name", "emqttd.listeners", [ {mapping, "listener.wss.$name", "emqx.listeners", [
{datatype, [integer, ip]} {datatype, [integer, ip]}
]}. ]}.
{mapping, "listener.wss.$name.acceptors", "emqttd.listeners", [ {mapping, "listener.wss.$name.acceptors", "emqx.listeners", [
{default, 8}, {default, 8},
{datatype, integer} {datatype, integer}
]}. ]}.
{mapping, "listener.wss.$name.max_clients", "emqttd.listeners", [ {mapping, "listener.wss.$name.max_clients", "emqx.listeners", [
{default, 1024}, {default, 1024},
{datatype, integer} {datatype, integer}
]}. ]}.
{mapping, "listener.wss.$name.zone", "emqttd.listeners", [ {mapping, "listener.wss.$name.zone", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.wss.$name.mountpoint", "emqttd.listeners", [ {mapping, "listener.wss.$name.mountpoint", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.wss.$name.rate_limit", "emqttd.listeners", [ {mapping, "listener.wss.$name.rate_limit", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.wss.$name.access.$id", "emqttd.listeners", [ {mapping, "listener.wss.$name.access.$id", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.wss.$name.backlog", "emqttd.listeners", [ {mapping, "listener.wss.$name.backlog", "emqx.listeners", [
{default, 1024}, {default, 1024},
{datatype, integer} {datatype, integer}
]}. ]}.
{mapping, "listener.wss.$name.recbuf", "emqttd.listeners", [ {mapping, "listener.wss.$name.recbuf", "emqx.listeners", [
{datatype, bytesize}, {datatype, bytesize},
hidden hidden
]}. ]}.
{mapping, "listener.wss.$name.sndbuf", "emqttd.listeners", [ {mapping, "listener.wss.$name.sndbuf", "emqx.listeners", [
{datatype, bytesize}, {datatype, bytesize},
hidden hidden
]}. ]}.
{mapping, "listener.wss.$name.buffer", "emqttd.listeners", [ {mapping, "listener.wss.$name.buffer", "emqx.listeners", [
{datatype, bytesize}, {datatype, bytesize},
hidden hidden
]}. ]}.
{mapping, "listener.wss.$name.tune_buffer", "emqttd.listeners", [ {mapping, "listener.wss.$name.tune_buffer", "emqx.listeners", [
{datatype, flag}, {datatype, flag},
hidden hidden
]}. ]}.
{mapping, "listener.wss.$name.nodelay", "emqttd.listeners", [ {mapping, "listener.wss.$name.nodelay", "emqx.listeners", [
{datatype, {enum, [true, false]}}, {datatype, {enum, [true, false]}},
hidden hidden
]}. ]}.
{mapping, "listener.wss.$name.handshake_timeout", "emqttd.listeners", [ {mapping, "listener.wss.$name.handshake_timeout", "emqx.listeners", [
{default, "15s"}, {default, "15s"},
{datatype, {duration, ms}} {datatype, {duration, ms}}
]}. ]}.
{mapping, "listener.wss.$name.keyfile", "emqttd.listeners", [ {mapping, "listener.wss.$name.keyfile", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.wss.$name.certfile", "emqttd.listeners", [ {mapping, "listener.wss.$name.certfile", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.wss.$name.cacertfile", "emqttd.listeners", [ {mapping, "listener.wss.$name.cacertfile", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.wss.$name.verify", "emqttd.listeners", [ {mapping, "listener.wss.$name.verify", "emqx.listeners", [
{datatype, atom} {datatype, atom}
]}. ]}.
{mapping, "listener.wss.$name.fail_if_no_peer_cert", "emqttd.listeners", [ {mapping, "listener.wss.$name.fail_if_no_peer_cert", "emqx.listeners", [
{datatype, {enum, [true, false]}} {datatype, {enum, [true, false]}}
]}. ]}.
{translation, "emqttd.listeners", fun(Conf) -> {translation, "emqx.listeners", fun(Conf) ->
Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end, Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end,
@ -1188,79 +1258,79 @@ end}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% MQTT REST API Listeners %% MQTT REST API Listeners
{mapping, "listener.api.$name", "emqttd.listeners", [ {mapping, "listener.api.$name", "emqx.listeners", [
{datatype, [integer, ip]} {datatype, [integer, ip]}
]}. ]}.
{mapping, "listener.api.$name.acceptors", "emqttd.listeners", [ {mapping, "listener.api.$name.acceptors", "emqx.listeners", [
{default, 8}, {default, 8},
{datatype, integer} {datatype, integer}
]}. ]}.
{mapping, "listener.api.$name.max_clients", "emqttd.listeners", [ {mapping, "listener.api.$name.max_clients", "emqx.listeners", [
{default, 1024}, {default, 1024},
{datatype, integer} {datatype, integer}
]}. ]}.
{mapping, "listener.api.$name.rate_limit", "emqttd.listeners", [ {mapping, "listener.api.$name.rate_limit", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.api.$name.access.$id", "emqttd.listeners", [ {mapping, "listener.api.$name.access.$id", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.api.$name.backlog", "emqttd.listeners", [ {mapping, "listener.api.$name.backlog", "emqx.listeners", [
{default, 1024}, {default, 1024},
{datatype, integer} {datatype, integer}
]}. ]}.
{mapping, "listener.api.$name.recbuf", "emqttd.listeners", [ {mapping, "listener.api.$name.recbuf", "emqx.listeners", [
{datatype, bytesize}, {datatype, bytesize},
hidden hidden
]}. ]}.
{mapping, "listener.api.$name.sndbuf", "emqttd.listeners", [ {mapping, "listener.api.$name.sndbuf", "emqx.listeners", [
{datatype, bytesize}, {datatype, bytesize},
hidden hidden
]}. ]}.
{mapping, "listener.api.$name.buffer", "emqttd.listeners", [ {mapping, "listener.api.$name.buffer", "emqx.listeners", [
{datatype, bytesize}, {datatype, bytesize},
hidden hidden
]}. ]}.
{mapping, "listener.api.$name.tune_buffer", "emqttd.listeners", [ {mapping, "listener.api.$name.tune_buffer", "emqx.listeners", [
{datatype, flag}, {datatype, flag},
hidden hidden
]}. ]}.
{mapping, "listener.api.$name.nodelay", "emqttd.listeners", [ {mapping, "listener.api.$name.nodelay", "emqx.listeners", [
{datatype, {enum, [true, false]}}, {datatype, {enum, [true, false]}},
hidden hidden
]}. ]}.
{mapping, "listener.api.$name.handshake_timeout", "emqttd.listeners", [ {mapping, "listener.api.$name.handshake_timeout", "emqx.listeners", [
{datatype, {duration, ms}} {datatype, {duration, ms}}
]}. ]}.
{mapping, "listener.api.$name.keyfile", "emqttd.listeners", [ {mapping, "listener.api.$name.keyfile", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.api.$name.certfile", "emqttd.listeners", [ {mapping, "listener.api.$name.certfile", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.api.$name.cacertfile", "emqttd.listeners", [ {mapping, "listener.api.$name.cacertfile", "emqx.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "listener.api.$name.verify", "emqttd.listeners", [ {mapping, "listener.api.$name.verify", "emqx.listeners", [
{datatype, atom} {datatype, atom}
]}. ]}.
{mapping, "listener.api.$name.fail_if_no_peer_cert", "emqttd.listeners", [ {mapping, "listener.api.$name.fail_if_no_peer_cert", "emqx.listeners", [
{datatype, {enum, [true, false]}} {datatype, {enum, [true, false]}}
]}. ]}.
@ -1270,36 +1340,36 @@ end}.
%% @doc Long GC, don't monitor in production mode for: %% @doc Long GC, don't monitor in production mode for:
%% https://github.com/erlang/otp/blob/feb45017da36be78d4c5784d758ede619fa7bfd3/erts/emulator/beam/erl_gc.c#L421 %% https://github.com/erlang/otp/blob/feb45017da36be78d4c5784d758ede619fa7bfd3/erts/emulator/beam/erl_gc.c#L421
{mapping, "sysmon.long_gc", "emqttd.sysmon", [ {mapping, "sysmon.long_gc", "emqx.sysmon", [
{default, false}, {default, false},
{datatype, {enum, [true, false]}} {datatype, {enum, [true, false]}}
]}. ]}.
%% @doc Long Schedule(ms) %% @doc Long Schedule(ms)
{mapping, "sysmon.long_schedule", "emqttd.sysmon", [ {mapping, "sysmon.long_schedule", "emqx.sysmon", [
{default, 1000}, {default, 1000},
{datatype, integer} {datatype, integer}
]}. ]}.
%% @doc Large Heap %% @doc Large Heap
{mapping, "sysmon.large_heap", "emqttd.sysmon", [ {mapping, "sysmon.large_heap", "emqx.sysmon", [
{default, "8MB"}, {default, "8MB"},
{datatype, bytesize} {datatype, bytesize}
]}. ]}.
%% @doc Monitor Busy Port %% @doc Monitor Busy Port
{mapping, "sysmon.busy_port", "emqttd.sysmon", [ {mapping, "sysmon.busy_port", "emqx.sysmon", [
{default, false}, {default, false},
{datatype, {enum, [true, false]}} {datatype, {enum, [true, false]}}
]}. ]}.
%% @doc Monitor Busy Dist Port %% @doc Monitor Busy Dist Port
{mapping, "sysmon.busy_dist_port", "emqttd.sysmon", [ {mapping, "sysmon.busy_dist_port", "emqx.sysmon", [
{default, true}, {default, true},
{datatype, {enum, [true, false]}} {datatype, {enum, [true, false]}}
]}. ]}.
{translation, "emqttd.sysmon", fun(Conf) -> {translation, "emqx.sysmon", fun(Conf) ->
[{long_gc, cuttlefish:conf_get("sysmon.long_gc", Conf)}, [{long_gc, cuttlefish:conf_get("sysmon.long_gc", Conf)},
{long_schedule, cuttlefish:conf_get("sysmon.long_schedule", Conf)}, {long_schedule, cuttlefish:conf_get("sysmon.long_schedule", Conf)},
{large_heap, cuttlefish:conf_get("sysmon.large_heap", Conf)}, {large_heap, cuttlefish:conf_get("sysmon.large_heap", Conf)},

View File

@ -1,12 +0,0 @@
{application,emqttd,
[{description,"Erlang MQTT Broker"},
{vsn,"2.3"},
{modules,[]},
{registered,[emqttd_sup]},
{applications,[kernel,stdlib,gproc,lager,esockd,mochiweb,
lager_syslog,pbkdf2,bcrypt]},
{env,[]},
{mod,{emqttd_app,[]}},
{maintainers,["Feng Lee <feng@emqtt.io>"]},
{licenses,["Apache-2.0"]},
{links,[{"Github","https://github.com/emqtt/emqttd"}]}]}.

View File

@ -1,187 +0,0 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io)
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%--------------------------------------------------------------------
%% @doc EMQ Main Module.
-module(emqttd).
-author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl").
-include("emqttd_protocol.hrl").
-export([start/0, env/1, env/2, is_running/1, stop/0]).
%% PubSub API
-export([subscribe/1, subscribe/2, subscribe/3, publish/1,
unsubscribe/1, unsubscribe/2]).
%% PubSub Management API
-export([setqos/3, topics/0, subscriptions/1, subscribers/1,
is_subscribed/2, subscriber_down/1]).
%% Hooks API
-export([hook/4, hook/3, unhook/2, run_hooks/2, run_hooks/3]).
%% Debug API
-export([dump/0]).
%% Shutdown and reboot
-export([shutdown/0, shutdown/1, reboot/0]).
-type(subscriber() :: pid() | binary()).
-type(suboption() :: local | {qos, non_neg_integer()} | {share, {'$queue' | binary()}}).
-type(pubsub_error() :: {error, {already_subscribed, binary()}
| {subscription_not_found, binary()}}).
-export_type([subscriber/0, suboption/0, pubsub_error/0]).
-define(APP, ?MODULE).
%%--------------------------------------------------------------------
%% Bootstrap, environment, configuration, is_running...
%%--------------------------------------------------------------------
%% @doc Start emqttd application.
-spec(start() -> ok | {error, any()}).
start() -> application:start(?APP).
%% @doc Stop emqttd application.
-spec(stop() -> ok | {error, any()}).
stop() -> application:stop(?APP).
%% @doc Environment
-spec(env(Key:: atom()) -> {ok, any()} | undefined).
env(Key) -> application:get_env(?APP, Key).
%% @doc Get environment
-spec(env(Key:: atom(), Default:: any()) -> undefined | any()).
env(Key, Default) -> application:get_env(?APP, Key, Default).
%% @doc Is running?
-spec(is_running(node()) -> boolean()).
is_running(Node) ->
case rpc:call(Node, erlang, whereis, [?APP]) of
{badrpc, _} -> false;
undefined -> false;
Pid when is_pid(Pid) -> true
end.
%%--------------------------------------------------------------------
%% PubSub APIs
%%--------------------------------------------------------------------
%% @doc Subscribe
-spec(subscribe(iodata()) -> ok | {error, any()}).
subscribe(Topic) ->
subscribe(Topic, self()).
-spec(subscribe(iodata(), subscriber()) -> ok | {error, any()}).
subscribe(Topic, Subscriber) ->
subscribe(Topic, Subscriber, []).
-spec(subscribe(iodata(), subscriber(), [suboption()]) -> ok | pubsub_error()).
subscribe(Topic, Subscriber, Options) ->
emqttd_server:subscribe(iolist_to_binary(Topic), Subscriber, Options).
%% @doc Publish MQTT Message
-spec(publish(mqtt_message()) -> {ok, mqtt_delivery()} | ignore).
publish(Msg) ->
emqttd_server:publish(Msg).
%% @doc Unsubscribe
-spec(unsubscribe(iodata()) -> ok | pubsub_error()).
unsubscribe(Topic) ->
unsubscribe(Topic, self()).
-spec(unsubscribe(iodata(), subscriber()) -> ok | pubsub_error()).
unsubscribe(Topic, Subscriber) ->
emqttd_server:unsubscribe(iolist_to_binary(Topic), Subscriber).
-spec(setqos(binary(), subscriber(), mqtt_qos()) -> ok).
setqos(Topic, Subscriber, Qos) ->
emqttd_server:setqos(iolist_to_binary(Topic), Subscriber, Qos).
-spec(topics() -> [binary()]).
topics() -> emqttd_router:topics().
-spec(subscribers(iodata()) -> list(subscriber())).
subscribers(Topic) ->
emqttd_server:subscribers(iolist_to_binary(Topic)).
-spec(subscriptions(subscriber()) -> [{binary(), binary(), list(suboption())}]).
subscriptions(Subscriber) ->
emqttd_server:subscriptions(Subscriber).
-spec(is_subscribed(iodata(), subscriber()) -> boolean()).
is_subscribed(Topic, Subscriber) ->
emqttd_server:is_subscribed(iolist_to_binary(Topic), Subscriber).
-spec(subscriber_down(subscriber()) -> ok).
subscriber_down(Subscriber) ->
emqttd_server:subscriber_down(Subscriber).
%%--------------------------------------------------------------------
%% Hooks API
%%--------------------------------------------------------------------
-spec(hook(atom(), function() | {emqttd_hooks:hooktag(), function()}, list(any()))
-> ok | {error, any()}).
hook(Hook, TagFunction, InitArgs) ->
emqttd_hooks:add(Hook, TagFunction, InitArgs).
-spec(hook(atom(), function() | {emqttd_hooks:hooktag(), function()}, list(any()), integer())
-> ok | {error, any()}).
hook(Hook, TagFunction, InitArgs, Priority) ->
emqttd_hooks:add(Hook, TagFunction, InitArgs, Priority).
-spec(unhook(atom(), function() | {emqttd_hooks:hooktag(), function()})
-> ok | {error, any()}).
unhook(Hook, TagFunction) ->
emqttd_hooks:delete(Hook, TagFunction).
-spec(run_hooks(atom(), list(any())) -> ok | stop).
run_hooks(Hook, Args) ->
emqttd_hooks:run(Hook, Args).
-spec(run_hooks(atom(), list(any()), any()) -> {ok | stop, any()}).
run_hooks(Hook, Args, Acc) ->
emqttd_hooks:run(Hook, Args, Acc).
%%--------------------------------------------------------------------
%% Shutdown and reboot
%%--------------------------------------------------------------------
shutdown() ->
shutdown(normal).
shutdown(Reason) ->
lager:error("EMQ shutdown for ~s", [Reason]),
emqttd_plugins:unload(),
lists:foreach(fun application:stop/1, [emqttd, ekka, mochiweb, esockd, gproc]).
reboot() ->
lists:foreach(fun application:start/1, [gproc, esockd, mochiweb, ekka, emqttd]).
%%--------------------------------------------------------------------
%% Debug
%%--------------------------------------------------------------------
dump() -> lists:append([emqttd_server:dump(), emqttd_router:dump()]).

View File

@ -1,242 +0,0 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io)
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%--------------------------------------------------------------------
-module(emqttd_app).
-behaviour(application).
-author("Feng Lee <feng@emqtt.io>").
-include("emqttd_cli.hrl").
-include("emqttd_protocol.hrl").
%% Application callbacks
-export([start/2, stop/1]).
-export([start_listener/1, stop_listener/1, restart_listener/1]).
-type(listener() :: {atom(), esockd:listen_on(), [esockd:option()]}).
-define(APP, emqttd).
%%--------------------------------------------------------------------
%% Application Callbacks
%%--------------------------------------------------------------------
start(_Type, _Args) ->
print_banner(),
ekka:start(),
{ok, Sup} = emqttd_sup:start_link(),
start_servers(Sup),
emqttd_cli:load(),
register_acl_mod(),
start_autocluster(),
register(emqttd, self()),
print_vsn(),
{ok, Sup}.
-spec(stop(State :: term()) -> term()).
stop(_State) ->
catch stop_listeners().
%%--------------------------------------------------------------------
%% Print Banner
%%--------------------------------------------------------------------
print_banner() ->
?PRINT("starting ~s on node '~s'~n", [?APP, node()]).
print_vsn() ->
{ok, Vsn} = application:get_key(vsn),
?PRINT("~s ~s is running now~n", [?APP, Vsn]).
%%--------------------------------------------------------------------
%% Start Servers
%%--------------------------------------------------------------------
start_servers(Sup) ->
Servers = [{"emqttd ctl", emqttd_ctl},
{"emqttd hook", emqttd_hooks},
{"emqttd router", emqttd_router},
{"emqttd pubsub", {supervisor, emqttd_pubsub_sup}},
{"emqttd stats", emqttd_stats},
{"emqttd metrics", emqttd_metrics},
{"emqttd pooler", {supervisor, emqttd_pooler}},
{"emqttd trace", {supervisor, emqttd_trace_sup}},
{"emqttd client manager", {supervisor, emqttd_cm_sup}},
{"emqttd session manager", {supervisor, emqttd_sm_sup}},
{"emqttd session supervisor", {supervisor, emqttd_session_sup}},
{"emqttd wsclient supervisor", {supervisor, emqttd_ws_client_sup}},
{"emqttd broker", emqttd_broker},
{"emqttd alarm", emqttd_alarm},
{"emqttd mod supervisor", emqttd_mod_sup},
{"emqttd bridge supervisor", {supervisor, emqttd_bridge_sup_sup}},
{"emqttd access control", emqttd_access_control},
{"emqttd system monitor", {supervisor, emqttd_sysmon_sup}}],
[start_server(Sup, Server) || Server <- Servers].
start_server(_Sup, {Name, F}) when is_function(F) ->
?PRINT("~s is starting...", [Name]),
F(),
?PRINT_MSG("[ok]~n");
start_server(Sup, {Name, Server}) ->
?PRINT("~s is starting...", [Name]),
start_child(Sup, Server),
?PRINT_MSG("[ok]~n");
start_server(Sup, {Name, Server, Opts}) ->
?PRINT("~s is starting...", [ Name]),
start_child(Sup, Server, Opts),
?PRINT_MSG("[ok]~n").
start_child(Sup, {supervisor, Module}) ->
supervisor:start_child(Sup, supervisor_spec(Module));
start_child(Sup, Module) when is_atom(Module) ->
{ok, _ChiId} = supervisor:start_child(Sup, worker_spec(Module)).
start_child(Sup, {supervisor, Module}, Opts) ->
supervisor:start_child(Sup, supervisor_spec(Module, Opts));
start_child(Sup, Module, Opts) when is_atom(Module) ->
supervisor:start_child(Sup, worker_spec(Module, Opts)).
supervisor_spec(Module) when is_atom(Module) ->
supervisor_spec(Module, start_link, []).
supervisor_spec(Module, Opts) ->
supervisor_spec(Module, start_link, [Opts]).
supervisor_spec(M, F, A) ->
{M, {M, F, A}, permanent, infinity, supervisor, [M]}.
worker_spec(Module) when is_atom(Module) ->
worker_spec(Module, start_link, []).
worker_spec(Module, Opts) when is_atom(Module) ->
worker_spec(Module, start_link, [Opts]).
worker_spec(M, F, A) ->
{M, {M, F, A}, permanent, 10000, worker, [M]}.
%%--------------------------------------------------------------------
%% Register default ACL File
%%--------------------------------------------------------------------
register_acl_mod() ->
case emqttd:env(acl_file) of
{ok, File} -> emqttd_access_control:register_mod(acl, emqttd_acl_internal, [File]);
undefined -> ok
end.
%%--------------------------------------------------------------------
%% Autocluster
%%--------------------------------------------------------------------
start_autocluster() ->
ekka:callback(prepare, fun emqttd:shutdown/1),
ekka:callback(reboot, fun emqttd:reboot/0),
ekka:autocluster(?APP, fun after_autocluster/0).
after_autocluster() ->
emqttd_plugins:init(),
emqttd_plugins:load(),
start_listeners().
%%--------------------------------------------------------------------
%% Start Listeners
%%--------------------------------------------------------------------
%% @doc Start Listeners of the broker.
-spec(start_listeners() -> any()).
start_listeners() -> lists:foreach(fun start_listener/1, emqttd:env(listeners, [])).
%% Start mqtt listener
-spec(start_listener(listener()) -> any()).
start_listener({tcp, ListenOn, Opts}) ->
start_listener('mqtt:tcp', ListenOn, Opts);
%% Start mqtt(SSL) listener
start_listener({ssl, ListenOn, Opts}) ->
start_listener('mqtt:ssl', ListenOn, Opts);
%% Start http listener
start_listener({Proto, ListenOn, Opts}) when Proto == http; Proto == ws ->
mochiweb:start_http('mqtt:ws', ListenOn, Opts, {emqttd_ws, handle_request, []});
%% Start https listener
start_listener({Proto, ListenOn, Opts}) when Proto == https; Proto == wss ->
mochiweb:start_http('mqtt:wss', ListenOn, Opts, {emqttd_ws, handle_request, []});
start_listener({Proto, ListenOn, Opts}) when Proto == api ->
mochiweb:start_http('mqtt:api', ListenOn, Opts, emqttd_http:http_handler()).
start_listener(Proto, ListenOn, Opts) ->
Env = lists:append(emqttd:env(client, []), emqttd:env(protocol, [])),
MFArgs = {emqttd_client, start_link, [Env]},
{ok, _} = esockd:open(Proto, ListenOn, merge_sockopts(Opts), MFArgs).
merge_sockopts(Options) ->
SockOpts = emqttd_misc:merge_opts(
?MQTT_SOCKOPTS, proplists:get_value(sockopts, Options, [])),
emqttd_misc:merge_opts(Options, [{sockopts, SockOpts}]).
%%--------------------------------------------------------------------
%% Stop Listeners
%%--------------------------------------------------------------------
%% @doc Stop Listeners
stop_listeners() -> lists:foreach(fun stop_listener/1, emqttd:env(listeners, [])).
%% @private
stop_listener({tcp, ListenOn, _Opts}) ->
esockd:close('mqtt:tcp', ListenOn);
stop_listener({ssl, ListenOn, _Opts}) ->
esockd:close('mqtt:ssl', ListenOn);
stop_listener({Proto, ListenOn, _Opts}) when Proto == http; Proto == ws ->
mochiweb:stop_http('mqtt:ws', ListenOn);
stop_listener({Proto, ListenOn, _Opts}) when Proto == https; Proto == wss ->
mochiweb:stop_http('mqtt:wss', ListenOn);
stop_listener({Proto, ListenOn, _Opts}) when Proto == api ->
mochiweb:stop_http('mqtt:api', ListenOn);
stop_listener({Proto, ListenOn, _Opts}) ->
esockd:close(Proto, ListenOn).
%% @doc Restart Listeners
restart_listener({tcp, ListenOn, _Opts}) ->
esockd:reopen('mqtt:tcp', ListenOn);
restart_listener({ssl, ListenOn, _Opts}) ->
esockd:reopen('mqtt:ssl', ListenOn);
restart_listener({Proto, ListenOn, _Opts}) when Proto == http; Proto == ws ->
mochiweb:restart_http('mqtt:ws', ListenOn);
restart_listener({Proto, ListenOn, _Opts}) when Proto == https; Proto == wss ->
mochiweb:restart_http('mqtt:wss', ListenOn);
restart_listener({Proto, ListenOn, _Opts}) when Proto == api ->
mochiweb:restart_http('mqtt:api', ListenOn);
restart_listener({Proto, ListenOn, _Opts}) ->
esockd:reopen(Proto, ListenOn).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
merge_sockopts_test_() ->
Opts = [{acceptors, 16}, {max_clients, 512}],
?_assert(merge_sockopts(Opts) == [{sockopts, ?MQTT_SOCKOPTS} | Opts]).
-endif.

View File

@ -1,55 +0,0 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io)
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%--------------------------------------------------------------------
-module(emqttd_sup).
-behaviour(supervisor).
-author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl").
%% 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(Mod, Type), {Mod, {Mod, start_link, []},
permanent, 5000, Type, [Mod]}).
%%--------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
start_child(ChildSpec) when is_tuple(ChildSpec) ->
supervisor:start_child(?MODULE, ChildSpec).
-spec(start_child(Mod::atom(), Type :: worker | supervisor) -> {ok, pid()}).
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, 0, 1}, []}}.

279
src/emqx.erl Normal file
View File

@ -0,0 +1,279 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io)
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%--------------------------------------------------------------------
%% @doc EMQ X Main Module.
-module(emqx).
-author("Feng Lee <feng@emqtt.io>").
-include("emqx.hrl").
-include("emqx_mqtt.hrl").
%% Start/Stop Application
-export([start/0, env/1, env/2, is_running/1, stop/0]).
%% Start/Stop Listeners
-export([start_listeners/0, start_listener/1, listeners/0,
stop_listeners/0, stop_listener/1,
restart_listeners/0, restart_listener/1]).
%% PubSub API
-export([subscribe/1, subscribe/2, subscribe/3, publish/1,
unsubscribe/1, unsubscribe/2]).
%% PubSub Management API
-export([setqos/3, topics/0, subscriptions/1, subscribers/1,
is_subscribed/2, subscriber_down/1]).
%% Hooks API
-export([hook/4, hook/3, unhook/2, run_hooks/2, run_hooks/3]).
%% Debug API
-export([dump/0]).
%% Shutdown and reboot
-export([shutdown/0, shutdown/1, reboot/0]).
-type(listener() :: {atom(), esockd:listen_on(), [esockd:option()]}).
-type(subscriber() :: pid() | binary()).
-type(suboption() :: local | {qos, non_neg_integer()} | {share, {'$queue' | binary()}}).
-export_type([subscriber/0, suboption/0]).
-define(APP, ?MODULE).
%%--------------------------------------------------------------------
%% Bootstrap, environment, configuration, is_running...
%%--------------------------------------------------------------------
%% @doc Start emqx application.
-spec(start() -> ok | {error, any()}).
start() -> application:start(?APP).
%% @doc Stop emqx application.
-spec(stop() -> ok | {error, any()}).
stop() -> application:stop(?APP).
%% @doc Get Environment
-spec(env(Key:: atom()) -> {ok, any()} | undefined).
env(Key) -> application:get_env(?APP, Key).
%% @doc Get environment with default
-spec(env(Key:: atom(), Default:: any()) -> undefined | any()).
env(Key, Default) -> application:get_env(?APP, Key, Default).
%% @doc Is running?
-spec(is_running(node()) -> boolean()).
is_running(Node) ->
case rpc:call(Node, erlang, whereis, [?APP]) of
{badrpc, _} -> false;
undefined -> false;
Pid when is_pid(Pid) -> true
end.
%%--------------------------------------------------------------------
%% Start/Stop Listeners
%%--------------------------------------------------------------------
%% @doc Start Listeners.
-spec(start_listeners() -> ok).
start_listeners() -> lists:foreach(fun start_listener/1, env(listeners, [])).
%% Start mqtt listener
-spec(start_listener(listener()) -> {ok, pid()} | {error, any()}).
start_listener({tcp, ListenOn, Opts}) ->
start_listener('mqtt:tcp', ListenOn, Opts);
%% Start mqtt(SSL) listener
start_listener({ssl, ListenOn, Opts}) ->
start_listener('mqtt:ssl', ListenOn, Opts);
%% Start http listener
start_listener({Proto, ListenOn, Opts}) when Proto == http; Proto == ws ->
{ok, _} = mochiweb:start_http('mqtt:ws', ListenOn, Opts, {emqx_ws, handle_request, []});
%% Start https listener
start_listener({Proto, ListenOn, Opts}) when Proto == https; Proto == wss ->
{ok, _} = mochiweb:start_http('mqtt:wss', ListenOn, Opts, {emqx_ws, handle_request, []});
start_listener({Proto, ListenOn, Opts}) when Proto == api ->
{ok, _} = mochiweb:start_http('mqtt:api', ListenOn, Opts, emqx_http:http_handler()).
start_listener(Proto, ListenOn, Opts) ->
Env = lists:append(emqx:env(client, []), emqx:env(protocol, [])),
MFArgs = {emqx_client, start_link, [Env]},
{ok, _} = esockd:open(Proto, ListenOn, merge_sockopts(Opts), MFArgs).
listeners() ->
[Listener || Listener = {{Proto, _}, _Pid} <- esockd:listeners(), is_mqtt(Proto)].
is_mqtt('mqtt:tcp') -> true;
is_mqtt('mqtt:ssl') -> true;
is_mqtt('mqtt:ws') -> true;
is_mqtt('mqtt:wss') -> true;
is_mqtt(_Proto) -> false.
%% @doc Stop Listeners
-spec(stop_listeners() -> ok).
stop_listeners() -> lists:foreach(fun stop_listener/1, env(listeners, [])).
-spec(stop_listener(listener()) -> ok | {error, any()}).
stop_listener({tcp, ListenOn, _Opts}) ->
esockd:close('mqtt:tcp', ListenOn);
stop_listener({ssl, ListenOn, _Opts}) ->
esockd:close('mqtt:ssl', ListenOn);
stop_listener({Proto, ListenOn, _Opts}) when Proto == http; Proto == ws ->
mochiweb:stop_http('mqtt:ws', ListenOn);
stop_listener({Proto, ListenOn, _Opts}) when Proto == https; Proto == wss ->
mochiweb:stop_http('mqtt:wss', ListenOn);
stop_listener({Proto, ListenOn, _Opts}) when Proto == api ->
mochiweb:stop_http('mqtt:api', ListenOn);
stop_listener({Proto, ListenOn, _Opts}) ->
esockd:close(Proto, ListenOn).
%% @doc Restart Listeners
-spec(restart_listeners() -> ok).
restart_listeners() -> lists:foreach(fun restart_listener/1, env(listeners, [])).
-spec(restart_listener(listener()) -> any()).
restart_listener({tcp, ListenOn, _Opts}) ->
esockd:reopen('mqtt:tcp', ListenOn);
restart_listener({ssl, ListenOn, _Opts}) ->
esockd:reopen('mqtt:ssl', ListenOn);
restart_listener({Proto, ListenOn, _Opts}) when Proto == http; Proto == ws ->
mochiweb:restart_http('mqtt:ws', ListenOn);
restart_listener({Proto, ListenOn, _Opts}) when Proto == https; Proto == wss ->
mochiweb:restart_http('mqtt:wss', ListenOn);
restart_listener({Proto, ListenOn, _Opts}) when Proto == api ->
mochiweb:restart_http('mqtt:api', ListenOn);
restart_listener({Proto, ListenOn, _Opts}) ->
esockd:reopen(Proto, ListenOn).
merge_sockopts(Options) ->
SockOpts = emqx_misc:merge_opts(
?MQTT_SOCKOPTS, proplists:get_value(sockopts, Options, [])),
emqx_misc:merge_opts(Options, [{sockopts, SockOpts}]).
%%--------------------------------------------------------------------
%% PubSub APIs
%%--------------------------------------------------------------------
%% @doc Subscribe
-spec(subscribe(iodata()) -> ok | {error, any()}).
subscribe(Topic) ->
subscribe(Topic, self()).
-spec(subscribe(iodata(), subscriber()) -> ok | {error, any()}).
subscribe(Topic, Subscriber) ->
subscribe(Topic, Subscriber, []).
-spec(subscribe(iodata(), subscriber(), [suboption()]) -> ok | {error, term()}).
subscribe(Topic, Subscriber, Options) ->
emqx_server:subscribe(iolist_to_binary(Topic), Subscriber, Options).
%% @doc Publish MQTT Message
-spec(publish(mqtt_message()) -> {ok, mqtt_delivery()} | ignore).
publish(Msg) ->
emqx_server:publish(Msg).
%% @doc Unsubscribe
-spec(unsubscribe(iodata()) -> ok | {error, term()}).
unsubscribe(Topic) ->
unsubscribe(Topic, self()).
-spec(unsubscribe(iodata(), subscriber()) -> ok | {error, term()}).
unsubscribe(Topic, Subscriber) ->
emqx_server:unsubscribe(iolist_to_binary(Topic), Subscriber).
%%--------------------------------------------------------------------
%% PubSub Management API
%%--------------------------------------------------------------------
-spec(setqos(binary(), subscriber(), mqtt_qos()) -> ok).
setqos(Topic, Subscriber, Qos) ->
emqx_server:setqos(iolist_to_binary(Topic), Subscriber, Qos).
-spec(topics() -> [binary()]).
topics() -> emqx_router:topics().
-spec(subscribers(iodata()) -> list(subscriber())).
subscribers(Topic) ->
emqx_server:subscribers(iolist_to_binary(Topic)).
-spec(subscriptions(subscriber()) -> [{binary(), binary(), list(suboption())}]).
subscriptions(Subscriber) ->
emqx_server:subscriptions(Subscriber).
-spec(is_subscribed(iodata(), subscriber()) -> boolean()).
is_subscribed(Topic, Subscriber) ->
emqx_server:is_subscribed(iolist_to_binary(Topic), Subscriber).
-spec(subscriber_down(subscriber()) -> ok).
subscriber_down(Subscriber) ->
emqx_server:subscriber_down(Subscriber).
%%--------------------------------------------------------------------
%% Hooks API
%%--------------------------------------------------------------------
-spec(hook(atom(), function() | {emqx_hooks:hooktag(), function()}, list(any()))
-> ok | {error, any()}).
hook(Hook, TagFunction, InitArgs) ->
emqx_hooks:add(Hook, TagFunction, InitArgs).
-spec(hook(atom(), function() | {emqx_hooks:hooktag(), function()}, list(any()), integer())
-> ok | {error, any()}).
hook(Hook, TagFunction, InitArgs, Priority) ->
emqx_hooks:add(Hook, TagFunction, InitArgs, Priority).
-spec(unhook(atom(), function() | {emqx_hooks:hooktag(), function()})
-> ok | {error, any()}).
unhook(Hook, TagFunction) ->
emqx_hooks:delete(Hook, TagFunction).
-spec(run_hooks(atom(), list(any())) -> ok | stop).
run_hooks(Hook, Args) ->
emqx_hooks:run(Hook, Args).
-spec(run_hooks(atom(), list(any()), any()) -> {ok | stop, any()}).
run_hooks(Hook, Args, Acc) ->
emqx_hooks:run(Hook, Args, Acc).
%%--------------------------------------------------------------------
%% Shutdown and reboot
%%--------------------------------------------------------------------
shutdown() ->
shutdown(normal).
shutdown(Reason) ->
lager:error("EMQ shutdown for ~s", [Reason]),
emqx_plugins:unload(),
lists:foreach(fun application:stop/1, [emqx, ekka, mochiweb, esockd, gproc]).
reboot() ->
lists:foreach(fun application:start/1, [gproc, esockd, mochiweb, ekka, emqx]).
%%--------------------------------------------------------------------
%% Debug
%%--------------------------------------------------------------------
dump() -> lists:append([emqx_server:dump(), emqx_router:dump()]).

View File

@ -14,13 +14,13 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_access_control). -module(emqx_access_control).
-behaviour(gen_server). -behaviour(gen_server).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
%% API Function Exports %% API Function Exports
-export([start_link/0, auth/2, check_acl/3, reload_acl/0, lookup_mods/1, -export([start_link/0, auth/2, check_acl/3, reload_acl/0, lookup_mods/1,
@ -52,7 +52,7 @@ start_link() ->
auth(Client, Password) when is_record(Client, mqtt_client) -> auth(Client, Password) when is_record(Client, mqtt_client) ->
auth(Client, Password, lookup_mods(auth)). auth(Client, Password, lookup_mods(auth)).
auth(_Client, _Password, []) -> auth(_Client, _Password, []) ->
case emqttd:env(allow_anonymous, false) of case emqx:env(allow_anonymous, false) of
true -> ok; true -> ok;
false -> {error, "No auth module to check!"} false -> {error, "No auth module to check!"}
end; end;
@ -74,7 +74,7 @@ check_acl(Client, PubSub, Topic) when ?PS(PubSub) ->
check_acl(Client, PubSub, Topic, lookup_mods(acl)). check_acl(Client, PubSub, Topic, lookup_mods(acl)).
check_acl(_Client, _PubSub, _Topic, []) -> check_acl(_Client, _PubSub, _Topic, []) ->
emqttd:env(acl_nomatch, allow); emqx:env(acl_nomatch, allow);
check_acl(Client, PubSub, Topic, [{Mod, State, _Seq}|AclMods]) -> check_acl(Client, PubSub, Topic, [{Mod, State, _Seq}|AclMods]) ->
case Mod:check_acl({Client, PubSub, Topic}, State) of case Mod:check_acl({Client, PubSub, Topic}, State) of
allow -> allow; allow -> allow;

View File

@ -14,12 +14,11 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_access_rule). -module(emqx_access_rule).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
-type(who() :: all | binary() | -type(who() :: all | binary() |
{ipaddr, esockd_cidr:cidr_string()} | {ipaddr, esockd_cidr:cidr_string()} |
@ -69,9 +68,9 @@ compile(who, {'or', Conds}) when is_list(Conds) ->
{'or', [compile(who, Cond) || Cond <- Conds]}; {'or', [compile(who, Cond) || Cond <- Conds]};
compile(topic, {eq, Topic}) -> compile(topic, {eq, Topic}) ->
{eq, emqttd_topic:words(bin(Topic))}; {eq, emqx_topic:words(bin(Topic))};
compile(topic, Topic) -> compile(topic, Topic) ->
Words = emqttd_topic:words(bin(Topic)), Words = emqx_topic:words(bin(Topic)),
case 'pattern?'(Words) of case 'pattern?'(Words) of
true -> {pattern, Words}; true -> {pattern, Words};
false -> Words false -> Words
@ -126,12 +125,12 @@ match_topics(_Client, _Topic, []) ->
false; false;
match_topics(Client, Topic, [{pattern, PatternFilter}|Filters]) -> match_topics(Client, Topic, [{pattern, PatternFilter}|Filters]) ->
TopicFilter = feed_var(Client, PatternFilter), TopicFilter = feed_var(Client, PatternFilter),
case match_topic(emqttd_topic:words(Topic), TopicFilter) of case match_topic(emqx_topic:words(Topic), TopicFilter) of
true -> true; true -> true;
false -> match_topics(Client, Topic, Filters) false -> match_topics(Client, Topic, Filters)
end; end;
match_topics(Client, Topic, [TopicFilter|Filters]) -> match_topics(Client, Topic, [TopicFilter|Filters]) ->
case match_topic(emqttd_topic:words(Topic), TopicFilter) of case match_topic(emqx_topic:words(Topic), TopicFilter) of
true -> true; true -> true;
false -> match_topics(Client, Topic, Filters) false -> match_topics(Client, Topic, Filters)
end. end.
@ -139,7 +138,7 @@ match_topics(Client, Topic, [TopicFilter|Filters]) ->
match_topic(Topic, {eq, TopicFilter}) -> match_topic(Topic, {eq, TopicFilter}) ->
Topic =:= TopicFilter; Topic =:= TopicFilter;
match_topic(Topic, TopicFilter) -> match_topic(Topic, TopicFilter) ->
emqttd_topic:match(Topic, TopicFilter). emqx_topic:match(Topic, TopicFilter).
feed_var(Client, Pattern) -> feed_var(Client, Pattern) ->
feed_var(Client, Pattern, []). feed_var(Client, Pattern, []).

View File

@ -14,14 +14,15 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_acl_internal). -module(emqx_acl_internal).
-behaviour(emqttd_acl_mod). -behaviour(emqx_acl_mod).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
-include("emqttd_cli.hrl").
-include("emqx_cli.hrl").
-export([all_rules/0]). -export([all_rules/0]).
@ -37,7 +38,7 @@
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @doc Read all rules %% @doc Read all rules
-spec(all_rules() -> list(emqttd_access_rule:rule())). -spec(all_rules() -> list(emqx_access_rule:rule())).
all_rules() -> all_rules() ->
case ets:lookup(?ACL_RULE_TAB, all_rules) of case ets:lookup(?ACL_RULE_TAB, all_rules) of
[] -> []; [] -> [];
@ -58,7 +59,7 @@ init([File]) ->
load_rules_from_file(#state{config = AclFile}) -> load_rules_from_file(#state{config = AclFile}) ->
{ok, Terms} = file:consult(AclFile), {ok, Terms} = file:consult(AclFile),
Rules = [emqttd_access_rule:compile(Term) || Term <- Terms], Rules = [emqx_access_rule:compile(Term) || Term <- Terms],
lists:foreach(fun(PubSub) -> lists:foreach(fun(PubSub) ->
ets:insert(?ACL_RULE_TAB, {PubSub, ets:insert(?ACL_RULE_TAB, {PubSub,
lists:filter(fun(Rule) -> filter(PubSub, Rule) end, Rules)}) lists:filter(fun(Rule) -> filter(PubSub, Rule) end, Rules)})
@ -103,7 +104,7 @@ match(_Client, _Topic, []) ->
nomatch; nomatch;
match(Client, Topic, [Rule|Rules]) -> match(Client, Topic, [Rule|Rules]) ->
case emqttd_access_rule:match(Client, Topic, Rule) of case emqx_access_rule:match(Client, Topic, Rule) of
nomatch -> match(Client, Topic, Rules); nomatch -> match(Client, Topic, Rules);
{matched, AllowDeny} -> {matched, AllowDeny} {matched, AllowDeny} -> {matched, AllowDeny}
end. end.

View File

@ -14,11 +14,11 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_acl_mod). -module(emqx_acl_mod).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% ACL behavihour %% ACL behavihour

View File

@ -14,13 +14,13 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_alarm). -module(emqx_alarm).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-behaviour(gen_event). -behaviour(gen_event).
-include("emqttd.hrl"). -include("emqx.hrl").
-define(ALARM_MGR, ?MODULE). -define(ALARM_MGR, ?MODULE).
@ -82,7 +82,7 @@ delete_alarm_handler(Module) when is_atom(Module) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
init(_) -> {ok, []}. init(_) -> {ok, []}.
handle_event({set_alarm, Alarm = #mqtt_alarm{id = AlarmId, handle_event({set_alarm, Alarm = #mqtt_alarm{id = AlarmId,
severity = Severity, severity = Severity,
title = Title, title = Title,
@ -92,13 +92,13 @@ handle_event({set_alarm, Alarm = #mqtt_alarm{id = AlarmId,
{severity, Severity}, {severity, Severity},
{title, iolist_to_binary(Title)}, {title, iolist_to_binary(Title)},
{summary, iolist_to_binary(Summary)}, {summary, iolist_to_binary(Summary)},
{ts, emqttd_time:now_secs(TS)}]), {ts, emqx_time:now_secs(TS)}]),
emqttd:publish(alarm_msg(alert, AlarmId, Json)), emqx:publish(alarm_msg(alert, AlarmId, Json)),
{ok, [Alarm#mqtt_alarm{timestamp = TS} | Alarms]}; {ok, [Alarm#mqtt_alarm{timestamp = TS} | Alarms]};
handle_event({clear_alarm, AlarmId}, Alarms) -> handle_event({clear_alarm, AlarmId}, Alarms) ->
Json = mochijson2:encode([{id, AlarmId}, {ts, emqttd_time:now_secs()}]), Json = mochijson2:encode([{id, AlarmId}, {ts, emqx_time:now_secs()}]),
emqttd:publish(alarm_msg(clear, AlarmId, Json)), emqx:publish(alarm_msg(clear, AlarmId, Json)),
{ok, lists:keydelete(AlarmId, 2, Alarms), hibernate}; {ok, lists:keydelete(AlarmId, 2, Alarms), hibernate};
handle_event(_, Alarms)-> handle_event(_, Alarms)->
@ -127,14 +127,12 @@ code_change(_OldVsn, State, _Extra) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
alarm_msg(Type, AlarmId, Json) -> alarm_msg(Type, AlarmId, Json) ->
Msg = emqttd_message:make(alarm, Msg = emqx_message:make(alarm, topic(Type, AlarmId), iolist_to_binary(Json)),
topic(Type, AlarmId), emqx_message:set_flag(sys, Msg).
iolist_to_binary(Json)),
emqttd_message:set_flag(sys, Msg).
topic(alert, AlarmId) -> topic(alert, AlarmId) ->
emqttd_topic:systop(<<"alarms/", AlarmId/binary, "/alert">>); emqx_topic:systop(<<"alarms/", AlarmId/binary, "/alert">>);
topic(clear, AlarmId) -> topic(clear, AlarmId) ->
emqttd_topic:systop(<<"alarms/", AlarmId/binary, "/clear">>). emqx_topic:systop(<<"alarms/", AlarmId/binary, "/clear">>).

85
src/emqx_app.erl Normal file
View File

@ -0,0 +1,85 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io)
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_app).
-behaviour(application).
-author("Feng Lee <feng@emqtt.io>").
-include("emqx_cli.hrl").
-include("emqx_mqtt.hrl").
%% Application callbacks
-export([start/2, stop/1]).
-define(APP, emqx).
%%--------------------------------------------------------------------
%% Application Callbacks
%%--------------------------------------------------------------------
start(_Type, _Args) ->
print_banner(),
ekka:start(),
{ok, Sup} = emqx_sup:start_link(),
ok = emqx_cli:load(),
ok = register_acl_mod(),
start_autocluster(),
register(emqx, self()),
print_vsn(),
{ok, Sup}.
-spec(stop(State :: term()) -> term()).
stop(_State) ->
catch emqx:stop_listeners().
%%--------------------------------------------------------------------
%% Print Banner
%%--------------------------------------------------------------------
print_banner() ->
?PRINT("Starting ~s on node ~s~n", [?APP, node()]).
print_vsn() ->
{ok, Vsn} = application:get_key(vsn),
?PRINT("~s ~s is running now!~n", [?APP, Vsn]).
%%--------------------------------------------------------------------
%% Register default ACL File
%%--------------------------------------------------------------------
register_acl_mod() ->
case emqx:env(acl_file) of
{ok, File} -> emqx_access_control:register_mod(acl, emqx_acl_internal, [File]);
undefined -> ok
end.
%%--------------------------------------------------------------------
%% Autocluster
%%--------------------------------------------------------------------
start_autocluster() ->
ekka:callback(prepare, fun emqx:shutdown/1),
ekka:callback(reboot, fun emqx:reboot/0),
ekka:autocluster(?APP, fun after_autocluster/0).
after_autocluster() ->
emqx_plugins:init(),
emqx_plugins:load(),
emqx:start_listeners().

View File

@ -14,11 +14,11 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_auth_mod). -module(emqx_auth_mod).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
-export([passwd_hash/2]). -export([passwd_hash/2]).
@ -35,7 +35,7 @@
-callback(check(Client :: mqtt_client(), -callback(check(Client :: mqtt_client(),
Password :: binary(), Password :: binary(),
State :: any()) State :: any())
-> ok | | {ok, boolean()} | ignore | {error, string()}). -> ok | {ok, boolean()} | ignore | {error, string()}).
-callback(description() -> string()). -callback(description() -> string()).
@ -63,12 +63,12 @@ passwd_hash(sha256, Password) ->
passwd_hash(pbkdf2, {Salt, Password, Macfun, Iterations, Dklen}) -> passwd_hash(pbkdf2, {Salt, Password, Macfun, Iterations, Dklen}) ->
case pbkdf2:pbkdf2(Macfun, Password, Salt, Iterations, Dklen) of case pbkdf2:pbkdf2(Macfun, Password, Salt, Iterations, Dklen) of
{ok, Hexstring} -> pbkdf2:to_hex(Hexstring); {ok, Hexstring} -> pbkdf2:to_hex(Hexstring);
{error, Error} -> lager:error("PasswdHash with pbkdf2 error:~p", [Error]), error {error, Error} -> lager:error("PasswdHash with pbkdf2 error:~p", [Error]), <<>>
end; end;
passwd_hash(bcrypt, {Salt, Password}) -> passwd_hash(bcrypt, {Salt, Password}) ->
case bcrypt:hashpw(Password, Salt) of case bcrypt:hashpw(Password, Salt) of
{ok, HashPassword} -> list_to_binary(HashPassword); {ok, HashPassword} -> list_to_binary(HashPassword);
{error, Error}-> lager:error("PasswdHash with bcrypt error:~p", [Error]), error {error, Error}-> lager:error("PasswdHash with bcrypt error:~p", [Error]), <<>>
end. end.
hexstring(<<X:128/big-unsigned-integer>>) -> hexstring(<<X:128/big-unsigned-integer>>) ->

View File

@ -14,7 +14,7 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_base62). -module(emqx_base62).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").

View File

@ -14,7 +14,7 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_boot). -module(emqx_boot).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").

View File

@ -14,17 +14,17 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_bridge). -module(emqx_bridge).
-behaviour(gen_server2).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -behaviour(gen_server2).
-include("emqttd_protocol.hrl"). -include("emqx.hrl").
-include("emqttd_internal.hrl"). -include("emqx_mqtt.hrl").
-include("emqx_internal.hrl").
%% API Function Exports %% API Function Exports
-export([start_link/5]). -export([start_link/5]).
@ -37,14 +37,16 @@
-record(state, {pool, id, -record(state, {pool, id,
node, subtopic, node, subtopic,
qos = ?QOS_0,
topic_suffix = <<>>, topic_suffix = <<>>,
topic_prefix = <<>>, topic_prefix = <<>>,
mqueue :: emqttd_mqueue:mqueue(), mqueue :: emqx_mqueue:mqueue(),
max_queue_len = 10000, max_queue_len = 10000,
ping_down_interval = ?PING_DOWN_INTERVAL, ping_down_interval = ?PING_DOWN_INTERVAL,
status = up}). status = up}).
-type(option() :: {topic_suffix, binary()} | -type(option() :: {qos, mqtt_qos()} |
{topic_suffix, binary()} |
{topic_prefix, binary()} | {topic_prefix, binary()} |
{max_queue_len, pos_integer()} | {max_queue_len, pos_integer()} |
{ping_down_interval, pos_integer()}). {ping_down_interval, pos_integer()}).
@ -72,11 +74,11 @@ init([Pool, Id, Node, Topic, Options]) ->
true -> true ->
true = erlang:monitor_node(Node, true), true = erlang:monitor_node(Node, true),
Share = iolist_to_binary(["$bridge:", atom_to_list(Node), ":", Topic]), Share = iolist_to_binary(["$bridge:", atom_to_list(Node), ":", Topic]),
emqttd:subscribe(Topic, self(), [local, {share, Share}, {qos, ?QOS_0}]), emqx_server:subscribe(Topic, self(), [local, {share, Share}, {qos, ?QOS_0}]),
State = parse_opts(Options, #state{node = Node, subtopic = Topic}), State = parse_opts(Options, #state{node = Node, subtopic = Topic}),
MQueue = emqttd_mqueue:new(qname(Node, Topic), MQueue = emqx_mqueue:new(qname(Node, Topic),
[{max_len, State#state.max_queue_len}], [{max_len, State#state.max_queue_len}],
emqttd_alarm:alarm_fun()), emqx_alarm:alarm_fun()),
{ok, State#state{pool = Pool, id = Id, mqueue = MQueue}, {ok, State#state{pool = Pool, id = Id, mqueue = MQueue},
hibernate, {backoff, 1000, 1000, 10000}}; hibernate, {backoff, 1000, 1000, 10000}};
false -> false ->
@ -85,6 +87,8 @@ init([Pool, Id, Node, Topic, Options]) ->
parse_opts([], State) -> parse_opts([], State) ->
State; State;
parse_opts([{qos, Qos} | Opts], State) ->
parse_opts(Opts, State#state{qos = Qos});
parse_opts([{topic_suffix, Suffix} | Opts], State) -> parse_opts([{topic_suffix, Suffix} | Opts], State) ->
parse_opts(Opts, State#state{topic_suffix= Suffix}); parse_opts(Opts, State#state{topic_suffix= Suffix});
parse_opts([{topic_prefix, Prefix} | Opts], State) -> parse_opts([{topic_prefix, Prefix} | Opts], State) ->
@ -108,10 +112,10 @@ handle_cast(Msg, State) ->
?UNEXPECTED_MSG(Msg, State). ?UNEXPECTED_MSG(Msg, State).
handle_info({dispatch, _Topic, Msg}, State = #state{mqueue = MQ, status = down}) -> handle_info({dispatch, _Topic, Msg}, State = #state{mqueue = MQ, status = down}) ->
{noreply, State#state{mqueue = emqttd_mqueue:in(Msg, MQ)}}; {noreply, State#state{mqueue = emqx_mqueue:in(Msg, MQ)}};
handle_info({dispatch, _Topic, Msg}, State = #state{node = Node, status = up}) -> handle_info({dispatch, _Topic, Msg}, State = #state{node = Node, status = up}) ->
rpc:cast(Node, emqttd, publish, [transform(Msg, State)]), emqx_rpc:cast(Node, emqx, publish, [transform(Msg, State)]),
{noreply, State, hibernate}; {noreply, State, hibernate};
handle_info({nodedown, Node}, State = #state{node = Node, ping_down_interval = Interval}) -> handle_info({nodedown, Node}, State = #state{node = Node, ping_down_interval = Interval}) ->
@ -121,7 +125,7 @@ handle_info({nodedown, Node}, State = #state{node = Node, ping_down_interval = I
handle_info({nodeup, Node}, State = #state{node = Node}) -> handle_info({nodeup, Node}, State = #state{node = Node}) ->
%% TODO: Really fast?? %% TODO: Really fast??
case emqttd:is_running(Node) of case emqx:is_running(Node) of
true -> true ->
lager:warning("Bridge Node Up: ~p", [Node]), lager:warning("Bridge Node Up: ~p", [Node]),
{noreply, dequeue(State#state{status = up})}; {noreply, dequeue(State#state{status = up})};
@ -160,7 +164,7 @@ code_change(_OldVsn, State, _Extra) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
dequeue(State = #state{mqueue = MQ}) -> dequeue(State = #state{mqueue = MQ}) ->
case emqttd_mqueue:out(MQ) of case emqx_mqueue:out(MQ) of
{empty, MQ1} -> {empty, MQ1} ->
State#state{mqueue = MQ1}; State#state{mqueue = MQ1};
{{value, Msg}, MQ1} -> {{value, Msg}, MQ1} ->

View File

@ -14,7 +14,9 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_bridge_sup). -module(emqx_bridge_sup).
-author("Feng Lee <feng@emqtt.io>").
-export([start_link/3]). -export([start_link/3]).
@ -23,8 +25,9 @@
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @doc Start bridge pool supervisor %% @doc Start bridge pool supervisor
-spec(start_link(atom(), binary(), [emqttd_bridge:option()]) -> {ok, pid()} | {error, any()}). -spec(start_link(atom(), binary(), [emqx_bridge:option()]) ->
{ok, pid()} | {error, any()}).
start_link(Node, Topic, Options) -> start_link(Node, Topic, Options) ->
MFA = {emqttd_bridge, start_link, [Node, Topic, Options]}, MFA = {emqx_bridge, start_link, [Node, Topic, Options]},
emqttd_pool_sup:start_link({bridge, Node, Topic}, random, MFA). emqx_pool_sup:start_link({bridge, Node, Topic}, random, MFA).

View File

@ -14,7 +14,7 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_bridge_sup_sup). -module(emqx_bridge_sup_sup).
-behavior(supervisor). -behavior(supervisor).
@ -44,12 +44,12 @@ bridges() ->
start_bridge(Node, Topic) when is_atom(Node) andalso is_binary(Topic) -> start_bridge(Node, Topic) when is_atom(Node) andalso is_binary(Topic) ->
start_bridge(Node, Topic, []). start_bridge(Node, Topic, []).
-spec(start_bridge(atom(), binary(), [emqttd_bridge:option()]) -> {ok, pid()} | {error, any()}). -spec(start_bridge(atom(), binary(), [emqx_bridge:option()]) -> {ok, pid()} | {error, any()}).
start_bridge(Node, _Topic, _Options) when Node =:= node() -> start_bridge(Node, _Topic, _Options) when Node =:= node() ->
{error, bridge_to_self}; {error, bridge_to_self};
start_bridge(Node, Topic, Options) when is_atom(Node) andalso is_binary(Topic) -> start_bridge(Node, Topic, Options) when is_atom(Node) andalso is_binary(Topic) ->
{ok, BridgeEnv} = emqttd:env(bridge), {ok, BridgeEnv} = emqx:env(bridge),
Options1 = emqttd_misc:merge_opts(BridgeEnv, Options), Options1 = emqx_misc:merge_opts(BridgeEnv, Options),
supervisor:start_child(?MODULE, bridge_spec(Node, Topic, Options1)). supervisor:start_child(?MODULE, bridge_spec(Node, Topic, Options1)).
%% @doc Stop a bridge %% @doc Stop a bridge
@ -66,10 +66,10 @@ stop_bridge(Node, Topic) when is_atom(Node) andalso is_binary(Topic) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
init([]) -> init([]) ->
{ok, {{one_for_one, 10, 100}, []}}. {ok, {{one_for_one, 10, 3600}, []}}.
bridge_spec(Node, Topic, Options) -> bridge_spec(Node, Topic, Options) ->
{?CHILD_ID(Node, Topic), {?CHILD_ID(Node, Topic),
{emqttd_bridge_sup, start_link, [Node, Topic, Options]}, {emqx_bridge_sup, start_link, [Node, Topic, Options]},
permanent, infinity, supervisor, [emqttd_bridge_sup]}. permanent, infinity, supervisor, [emqx_bridge_sup]}.

View File

@ -14,15 +14,15 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_broker). -module(emqx_broker).
-behaviour(gen_server). -behaviour(gen_server).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
-include("emqttd_internal.hrl"). -include("emqx_internal.hrl").
%% API Function Exports %% API Function Exports
-export([start_link/0]). -export([start_link/0]).
@ -42,7 +42,7 @@
-record(state, {started_at, sys_interval, heartbeat, ticker, version, sysdescr}). -record(state, {started_at, sys_interval, heartbeat, ticker, version, sysdescr}).
-define(APP, emqttd). -define(APP, emqx).
-define(SERVER, ?MODULE). -define(SERVER, ?MODULE).
@ -60,7 +60,7 @@
%% API %% API
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @doc Start emqttd broker %% @doc Start the broker
-spec(start_link() -> {ok, pid()} | ignore | {error, any()}). -spec(start_link() -> {ok, pid()} | ignore | {error, any()}).
start_link() -> start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
@ -107,7 +107,7 @@ datetime() ->
%% @doc Start a tick timer %% @doc Start a tick timer
start_tick(Msg) -> start_tick(Msg) ->
start_tick(timer:seconds(emqttd:env(broker_sys_interval, 60)), Msg). start_tick(timer:seconds(emqx:env(broker_sys_interval, 60)), Msg).
start_tick(0, _Msg) -> start_tick(0, _Msg) ->
undefined; undefined;
@ -125,7 +125,7 @@ stop_tick(TRef) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
init([]) -> init([]) ->
emqttd_time:seed(), emqx_time:seed(),
ets:new(?BROKER_TAB, [set, public, named_table]), ets:new(?BROKER_TAB, [set, public, named_table]),
% Tick % Tick
{ok, #state{started_at = os:timestamp(), {ok, #state{started_at = os:timestamp(),
@ -172,16 +172,16 @@ code_change(_OldVsn, State, _Extra) ->
retain(brokers) -> retain(brokers) ->
Payload = list_to_binary(string:join([atom_to_list(N) || Payload = list_to_binary(string:join([atom_to_list(N) ||
N <- ekka_mnesia:running_nodes()], ",")), N <- ekka_mnesia:running_nodes()], ",")),
Msg = emqttd_message:make(broker, <<"$SYS/brokers">>, Payload), Msg = emqx_message:make(broker, <<"$SYS/brokers">>, Payload),
emqttd:publish(emqttd_message:set_flag(sys, emqttd_message:set_flag(retain, Msg))). emqx:publish(emqx_message:set_flag(sys, emqx_message:set_flag(retain, Msg))).
retain(Topic, Payload) when is_binary(Payload) -> retain(Topic, Payload) when is_binary(Payload) ->
Msg = emqttd_message:make(broker, emqttd_topic:systop(Topic), Payload), Msg = emqx_message:make(broker, emqx_topic:systop(Topic), Payload),
emqttd:publish(emqttd_message:set_flag(sys, emqttd_message:set_flag(retain, Msg))). emqx:publish(emqx_message:set_flag(sys, emqx_message:set_flag(retain, Msg))).
publish(Topic, Payload) when is_binary(Payload) -> publish(Topic, Payload) when is_binary(Payload) ->
Msg = emqttd_message:make(broker, emqttd_topic:systop(Topic), Payload), Msg = emqx_message:make(broker, emqx_topic:systop(Topic), Payload),
emqttd:publish(emqttd_message:set_flag(sys, Msg)). emqx:publish(emqx_message:set_flag(sys, Msg)).
uptime(#state{started_at = Ts}) -> uptime(#state{started_at = Ts}) ->
Secs = timer:now_diff(os:timestamp(), Ts) div 1000000, Secs = timer:now_diff(os:timestamp(), Ts) div 1000000,

View File

@ -14,15 +14,15 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_cli). -module(emqx_cli).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
-include("emqttd_cli.hrl"). -include("emqx_mqtt.hrl").
-include("emqttd_protocol.hrl"). -include("emqx_cli.hrl").
-import(lists, [foreach/2]). -import(lists, [foreach/2]).
@ -44,12 +44,13 @@
-define(MAX_LIMIT, 10000). -define(MAX_LIMIT, 10000).
-define(APP, emqttd). -define(APP, emqx).
-spec(load() -> ok).
load() -> load() ->
Cmds = [Fun || {Fun, _} <- ?MODULE:module_info(exports), is_cmd(Fun)], Cmds = [Fun || {Fun, _} <- ?MODULE:module_info(exports), is_cmd(Fun)],
[emqttd_ctl:register_cmd(Cmd, {?MODULE, Cmd}, []) || Cmd <- Cmds], lists:foreach(fun(Cmd) -> emqx_ctl:register_cmd(Cmd, {?MODULE, Cmd}, []) end, Cmds),
emqttd_cli_config:register_config(). emqx_cli_config:register_config().
is_cmd(Fun) -> is_cmd(Fun) ->
not lists:member(Fun, [init, load, module_info]). not lists:member(Fun, [init, load, module_info]).
@ -66,9 +67,9 @@ status([]) ->
?PRINT("Node ~p is ~p~n", [node(), InternalStatus]), ?PRINT("Node ~p is ~p~n", [node(), InternalStatus]),
case lists:keysearch(?APP, 1, application:which_applications()) of case lists:keysearch(?APP, 1, application:which_applications()) of
false -> false ->
?PRINT_MSG("emqttd is not running~n"); ?PRINT("~s is not running~n", [?APP]);
{value, {?APP, _Desc, Vsn}} -> {value, {?APP, _Desc, Vsn}} ->
?PRINT("emqttd ~s is running~n", [Vsn]) ?PRINT("~s ~s is running~n", [?APP, Vsn])
end; end;
status(_) -> status(_) ->
?PRINT_CMD("status", "Show broker status"). ?PRINT_CMD("status", "Show broker status").
@ -79,21 +80,21 @@ status(_) ->
broker([]) -> broker([]) ->
Funs = [sysdescr, version, uptime, datetime], Funs = [sysdescr, version, uptime, datetime],
foreach(fun(Fun) -> foreach(fun(Fun) ->
?PRINT("~-10s: ~s~n", [Fun, emqttd_broker:Fun()]) ?PRINT("~-10s: ~s~n", [Fun, emqx_broker:Fun()])
end, Funs); end, Funs);
broker(["stats"]) -> broker(["stats"]) ->
foreach(fun({Stat, Val}) -> foreach(fun({Stat, Val}) ->
?PRINT("~-20s: ~w~n", [Stat, Val]) ?PRINT("~-20s: ~w~n", [Stat, Val])
end, emqttd_stats:getstats()); end, emqx_stats:getstats());
broker(["metrics"]) -> broker(["metrics"]) ->
foreach(fun({Metric, Val}) -> foreach(fun({Metric, Val}) ->
?PRINT("~-24s: ~w~n", [Metric, Val]) ?PRINT("~-24s: ~w~n", [Metric, Val])
end, lists:sort(emqttd_metrics:all())); end, lists:sort(emqx_metrics:all()));
broker(["pubsub"]) -> broker(["pubsub"]) ->
Pubsubs = supervisor:which_children(emqttd_pubsub_sup:pubsub_pool()), Pubsubs = supervisor:which_children(emqx_pubsub_sup:pubsub_pool()),
foreach(fun({{_, Id}, Pid, _, _}) -> foreach(fun({{_, Id}, Pid, _, _}) ->
ProcInfo = erlang:process_info(Pid, ?PROC_INFOKEYS), ProcInfo = erlang:process_info(Pid, ?PROC_INFOKEYS),
?PRINT("pubsub: ~w~n", [Id]), ?PRINT("pubsub: ~w~n", [Id]),
@ -154,9 +155,9 @@ cluster(_) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @doc Users usage %% @doc Users usage
users(Args) -> emq_auth_username:cli(Args). users(Args) -> emqx_auth_username:cli(Args).
acl(["reload"]) -> emqttd_access_control:reload_acl(); acl(["reload"]) -> emqx_access_control:reload_acl();
acl(_) -> ?USAGE([{"acl reload", "reload etc/acl.conf"}]). acl(_) -> ?USAGE([{"acl reload", "reload etc/acl.conf"}]).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
@ -169,7 +170,7 @@ clients(["show", ClientId]) ->
if_client(ClientId, fun print/1); if_client(ClientId, fun print/1);
clients(["kick", ClientId]) -> clients(["kick", ClientId]) ->
if_client(ClientId, fun(#mqtt_client{client_pid = Pid}) -> emqttd_client:kick(Pid) end); if_client(ClientId, fun(#mqtt_client{client_pid = Pid}) -> emqx_client:kick(Pid) end);
clients(_) -> clients(_) ->
?USAGE([{"clients list", "List all clients"}, ?USAGE([{"clients list", "List all clients"},
@ -177,7 +178,7 @@ clients(_) ->
{"clients kick <ClientId>", "Kick out a client"}]). {"clients kick <ClientId>", "Kick out a client"}]).
if_client(ClientId, Fun) -> if_client(ClientId, Fun) ->
case emqttd_cm:lookup(bin(ClientId)) of case emqx_cm:lookup(bin(ClientId)) of
undefined -> ?PRINT_MSG("Not Found.~n"); undefined -> ?PRINT_MSG("Not Found.~n");
Client -> Fun(Client) Client -> Fun(Client)
end. end.
@ -214,7 +215,7 @@ sessions(_) ->
%% @doc Routes Command %% @doc Routes Command
routes(["list"]) -> routes(["list"]) ->
Routes = emqttd_router:dump(), Routes = emqx_router:dump(),
foreach(fun print/1, Routes); foreach(fun print/1, Routes);
routes(["show", Topic]) -> routes(["show", Topic]) ->
@ -228,7 +229,7 @@ routes(_) ->
%% @doc Topics Command %% @doc Topics Command
topics(["list"]) -> topics(["list"]) ->
lists:foreach(fun(Topic) -> ?PRINT("~s~n", [Topic]) end, emqttd:topics()); lists:foreach(fun(Topic) -> ?PRINT("~s~n", [Topic]) end, emqx:topics());
topics(["show", Topic]) -> topics(["show", Topic]) ->
print(mnesia:dirty_read(mqtt_route, bin(Topic))); print(mnesia:dirty_read(mqtt_route, bin(Topic)));
@ -251,26 +252,25 @@ subscriptions(["show", ClientId]) ->
subscriptions(["add", ClientId, Topic, QoS]) -> subscriptions(["add", ClientId, Topic, QoS]) ->
Add = fun(IntQos) -> Add = fun(IntQos) ->
case emqttd:subscribe(bin(Topic), bin(ClientId), [{qos, IntQos}]) of case emqx:subscribe(bin(Topic), bin(ClientId), [{qos, IntQos}]) of
ok -> ok ->
?PRINT_MSG("ok~n"); ?PRINT_MSG("ok~n");
{error, already_existed} ->
?PRINT_MSG("Error: already existed~n");
{error, Reason} -> {error, Reason} ->
?PRINT("Error: ~p~n", [Reason]) ?PRINT("Error: ~p~n", [Reason])
end end
end, end,
if_valid_qos(QoS, Add); if_valid_qos(QoS, Add);
subscriptions(["del", ClientId]) -> subscriptions(["del", ClientId]) ->
Ok = emqttd:subscriber_down(bin(ClientId)), Ok = emqx_server:subscriber_down(bin(ClientId)),
?PRINT("~p~n", [Ok]); ?PRINT("~p~n", [Ok]);
subscriptions(["del", ClientId, Topic]) -> subscriptions(["del", ClientId, Topic]) ->
Ok = emqttd:unsubscribe(bin(Topic), bin(ClientId)), Ok = emqx:unsubscribe(bin(Topic), bin(ClientId)),
?PRINT("~p~n", [Ok]); ?PRINT("~p~n", [Ok]);
subscriptions(_) -> subscriptions(_) ->
?USAGE([{"subscriptions list", "List all subscriptions"}, ?USAGE([{"subscriptions list", "List all subscriptions"},
{"subscriptions show <ClientId>", "Show subscriptions of a client"}, {"subscriptions show <ClientId>", "Show subscriptions of a client"},
@ -278,7 +278,7 @@ subscriptions(_) ->
{"subscriptions del <ClientId>", "Delete static subscriptions manually"}, {"subscriptions del <ClientId>", "Delete static subscriptions manually"},
{"subscriptions del <ClientId> <Topic>", "Delete a static subscription manually"}]). {"subscriptions del <ClientId> <Topic>", "Delete a static subscription manually"}]).
% if_could_print(Tab, Fun) -> %if_could_print(Tab, Fun) ->
% case mnesia:table_info(Tab, size) of % case mnesia:table_info(Tab, size) of
% Size when Size >= ?MAX_LIMIT -> % Size when Size >= ?MAX_LIMIT ->
% ?PRINT("Could not list, too many ~ss: ~p~n", [Tab, Size]); % ?PRINT("Could not list, too many ~ss: ~p~n", [Tab, Size]);
@ -296,10 +296,10 @@ if_valid_qos(QoS, Fun) ->
end. end.
plugins(["list"]) -> plugins(["list"]) ->
foreach(fun print/1, emqttd_plugins:list()); foreach(fun print/1, emqx_plugins:list());
plugins(["load", Name]) -> plugins(["load", Name]) ->
case emqttd_plugins:load(list_to_atom(Name)) of case emqx_plugins:load(list_to_atom(Name)) of
{ok, StartedApps} -> {ok, StartedApps} ->
?PRINT("Start apps: ~p~nPlugin ~s loaded successfully.~n", [StartedApps, Name]); ?PRINT("Start apps: ~p~nPlugin ~s loaded successfully.~n", [StartedApps, Name]);
{error, Reason} -> {error, Reason} ->
@ -307,7 +307,7 @@ plugins(["load", Name]) ->
end; end;
plugins(["unload", Name]) -> plugins(["unload", Name]) ->
case emqttd_plugins:unload(list_to_atom(Name)) of case emqx_plugins:unload(list_to_atom(Name)) of
ok -> ok ->
?PRINT("Plugin ~s unloaded successfully.~n", [Name]); ?PRINT("Plugin ~s unloaded successfully.~n", [Name]);
{error, Reason} -> {error, Reason} ->
@ -325,31 +325,32 @@ plugins(_) ->
bridges(["list"]) -> bridges(["list"]) ->
foreach(fun({Node, Topic, _Pid}) -> foreach(fun({Node, Topic, _Pid}) ->
?PRINT("bridge: ~s--~s-->~s~n", [node(), Topic, Node]) ?PRINT("bridge: ~s--~s-->~s~n", [node(), Topic, Node])
end, emqttd_bridge_sup_sup:bridges()); end, emqx_bridge_sup_sup:bridges());
bridges(["options"]) -> bridges(["options"]) ->
?PRINT_MSG("Options:~n"), ?PRINT_MSG("Options:~n"),
?PRINT_MSG(" qos = 0 | 1 | 2~n"),
?PRINT_MSG(" prefix = string~n"), ?PRINT_MSG(" prefix = string~n"),
?PRINT_MSG(" suffix = string~n"), ?PRINT_MSG(" suffix = string~n"),
?PRINT_MSG(" queue = integer~n"), ?PRINT_MSG(" queue = integer~n"),
?PRINT_MSG("Example:~n"), ?PRINT_MSG("Example:~n"),
?PRINT_MSG(" prefix=abc/,suffix=/yxz,queue=1000~n"); ?PRINT_MSG(" qos=2,prefix=abc/,suffix=/yxz,queue=1000~n");
bridges(["start", SNode, Topic]) -> bridges(["start", SNode, Topic]) ->
case emqttd_bridge_sup_sup:start_bridge(list_to_atom(SNode), list_to_binary(Topic)) of case emqx_bridge_sup_sup:start_bridge(list_to_atom(SNode), list_to_binary(Topic)) of
{ok, _} -> ?PRINT_MSG("bridge is started.~n"); {ok, _} -> ?PRINT_MSG("bridge is started.~n");
{error, Error} -> ?PRINT("error: ~p~n", [Error]) {error, Error} -> ?PRINT("error: ~p~n", [Error])
end; end;
bridges(["start", SNode, Topic, OptStr]) -> bridges(["start", SNode, Topic, OptStr]) ->
Opts = parse_opts(bridge, OptStr), Opts = parse_opts(bridge, OptStr),
case emqttd_bridge_sup_sup:start_bridge(list_to_atom(SNode), list_to_binary(Topic), Opts) of case emqx_bridge_sup_sup:start_bridge(list_to_atom(SNode), list_to_binary(Topic), Opts) of
{ok, _} -> ?PRINT_MSG("bridge is started.~n"); {ok, _} -> ?PRINT_MSG("bridge is started.~n");
{error, Error} -> ?PRINT("error: ~p~n", [Error]) {error, Error} -> ?PRINT("error: ~p~n", [Error])
end; end;
bridges(["stop", SNode, Topic]) -> bridges(["stop", SNode, Topic]) ->
case emqttd_bridge_sup_sup:stop_bridge(list_to_atom(SNode), list_to_binary(Topic)) of case emqx_bridge_sup_sup:stop_bridge(list_to_atom(SNode), list_to_binary(Topic)) of
ok -> ?PRINT_MSG("bridge is stopped.~n"); ok -> ?PRINT_MSG("bridge is stopped.~n");
{error, Error} -> ?PRINT("error: ~p~n", [Error]) {error, Error} -> ?PRINT("error: ~p~n", [Error])
end; end;
@ -365,6 +366,8 @@ parse_opts(Cmd, OptStr) ->
Tokens = string:tokens(OptStr, ","), Tokens = string:tokens(OptStr, ","),
[parse_opt(Cmd, list_to_atom(Opt), Val) [parse_opt(Cmd, list_to_atom(Opt), Val)
|| [Opt, Val] <- [string:tokens(S, "=") || S <- Tokens]]. || [Opt, Val] <- [string:tokens(S, "=") || S <- Tokens]].
parse_opt(bridge, qos, Qos) ->
{qos, list_to_integer(Qos)};
parse_opt(bridge, suffix, Suffix) -> parse_opt(bridge, suffix, Suffix) ->
{topic_suffix, bin(Suffix)}; {topic_suffix, bin(Suffix)};
parse_opt(bridge, prefix, Prefix) -> parse_opt(bridge, prefix, Prefix) ->
@ -384,7 +387,7 @@ vm(["all"]) ->
[vm([Name]) || Name <- ["load", "memory", "process", "io", "ports"]]; [vm([Name]) || Name <- ["load", "memory", "process", "io", "ports"]];
vm(["load"]) -> vm(["load"]) ->
[?PRINT("cpu/~-20s: ~s~n", [L, V]) || {L, V} <- emqttd_vm:loads()]; [?PRINT("cpu/~-20s: ~s~n", [L, V]) || {L, V} <- emqx_vm:loads()];
vm(["memory"]) -> vm(["memory"]) ->
[?PRINT("memory/~-17s: ~w~n", [Cat, Val]) || {Cat, Val} <- erlang:memory()]; [?PRINT("memory/~-17s: ~w~n", [Cat, Val]) || {Cat, Val} <- erlang:memory()];
@ -428,7 +431,7 @@ mnesia(_) ->
trace(["list"]) -> trace(["list"]) ->
foreach(fun({{Who, Name}, LogFile}) -> foreach(fun({{Who, Name}, LogFile}) ->
?PRINT("trace ~s ~s -> ~s~n", [Who, Name, LogFile]) ?PRINT("trace ~s ~s -> ~s~n", [Who, Name, LogFile])
end, emqttd_trace:all_traces()); end, emqx_trace:all_traces());
trace(["client", ClientId, "off"]) -> trace(["client", ClientId, "off"]) ->
trace_off(client, ClientId); trace_off(client, ClientId);
@ -450,7 +453,7 @@ trace(_) ->
{"trace topic <Topic> off", "Stop tracing a Topic"}]). {"trace topic <Topic> off", "Stop tracing a Topic"}]).
trace_on(Who, Name, LogFile) -> trace_on(Who, Name, LogFile) ->
case emqttd_trace:start_trace({Who, iolist_to_binary(Name)}, LogFile) of case emqx_trace:start_trace({Who, iolist_to_binary(Name)}, LogFile) of
ok -> ok ->
?PRINT("trace ~s ~s successfully.~n", [Who, Name]); ?PRINT("trace ~s ~s successfully.~n", [Who, Name]);
{error, Error} -> {error, Error} ->
@ -458,7 +461,7 @@ trace_on(Who, Name, LogFile) ->
end. end.
trace_off(Who, Name) -> trace_off(Who, Name) ->
case emqttd_trace:stop_trace({Who, iolist_to_binary(Name)}) of case emqx_trace:stop_trace({Who, iolist_to_binary(Name)}) of
ok -> ok ->
?PRINT("stop tracing ~s ~s successfully.~n", [Who, Name]); ?PRINT("stop tracing ~s ~s successfully.~n", [Who, Name]);
{error, Error} -> {error, Error} ->
@ -485,7 +488,7 @@ listeners(["restart", Proto, ListenOn]) ->
[Port] -> list_to_integer(Port); [Port] -> list_to_integer(Port);
[IP, Port] -> {IP, list_to_integer(Port)} [IP, Port] -> {IP, list_to_integer(Port)}
end, end,
case emqttd_app:restart_listener({list_to_atom(Proto), ListenOn1, []}) of case emqx:restart_listener({list_to_atom(Proto), ListenOn1, []}) of
{ok, _Pid} -> {ok, _Pid} ->
io:format("Restart ~s listener on ~s successfully.~n", [Proto, ListenOn]); io:format("Restart ~s listener on ~s successfully.~n", [Proto, ListenOn]);
{error, Error} -> {error, Error} ->
@ -497,7 +500,7 @@ listeners(["stop", Proto, ListenOn]) ->
[Port] -> list_to_integer(Port); [Port] -> list_to_integer(Port);
[IP, Port] -> {IP, list_to_integer(Port)} [IP, Port] -> {IP, list_to_integer(Port)}
end, end,
case emqttd_app:stop_listener({list_to_atom(Proto), ListenOn1, []}) of case emqx:stop_listener({list_to_atom(Proto), ListenOn1, []}) of
ok -> ok ->
io:format("Stop ~s listener on ~s successfully.~n", [Proto, ListenOn]); io:format("Stop ~s listener on ~s successfully.~n", [Proto, ListenOn]);
{error, Error} -> {error, Error} ->
@ -548,8 +551,8 @@ print(#mqtt_plugin{name = Name, version = Ver, descr = Descr, active = Active})
print(#mqtt_client{client_id = ClientId, clean_sess = CleanSess, username = Username, print(#mqtt_client{client_id = ClientId, clean_sess = CleanSess, username = Username,
peername = Peername, connected_at = ConnectedAt}) -> peername = Peername, connected_at = ConnectedAt}) ->
?PRINT("Client(~s, clean_sess=~s, username=~s, peername=~s, connected_at=~p)~n", ?PRINT("Client(~s, clean_sess=~s, username=~s, peername=~s, connected_at=~p)~n",
[ClientId, CleanSess, Username, emqttd_net:format(Peername), [ClientId, CleanSess, Username, emqx_net:format(Peername),
emqttd_time:now_secs(ConnectedAt)]); emqx_time:now_secs(ConnectedAt)]);
%% print(#mqtt_topic{topic = Topic, flags = Flags}) -> %% print(#mqtt_topic{topic = Topic, flags = Flags}) ->
%% ?PRINT("~s: ~s~n", [Topic, string:join([atom_to_list(F) || F <- Flags], ",")]); %% ?PRINT("~s: ~s~n", [Topic, string:join([atom_to_list(F) || F <- Flags], ",")]);
@ -563,7 +566,7 @@ print({Topic, Node}) ->
?PRINT("~s -> ~s~n", [Topic, Node]); ?PRINT("~s -> ~s~n", [Topic, Node]);
print({ClientId, _ClientPid, _Persistent, SessInfo}) -> print({ClientId, _ClientPid, _Persistent, SessInfo}) ->
Data = lists:append(SessInfo, emqttd_stats:get_session_stats(ClientId)), Data = lists:append(SessInfo, emqx_stats:get_session_stats(ClientId)),
InfoKeys = [clean_sess, InfoKeys = [clean_sess,
subscriptions, subscriptions,
max_inflight, max_inflight,
@ -589,7 +592,7 @@ print(subscription, {Sub, Topic}) ->
?PRINT("~s -> ~s~n", [Sub, Topic]). ?PRINT("~s -> ~s~n", [Sub, Topic]).
format(created_at, Val) -> format(created_at, Val) ->
emqttd_time:now_secs(Val); emqx_time:now_secs(Val);
format(_, Val) -> format(_, Val) ->
Val. Val.

View File

@ -14,7 +14,7 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module (emqttd_cli_config). -module (emqx_cli_config).
-export ([register_config_cli/0, -export ([register_config_cli/0,
register_config/0, register_config/0,
@ -26,8 +26,9 @@
read_config/1, read_config/1,
write_config/2]). write_config/2]).
-define(APP, emqttd). -define(APP, emqx).
-define(TAB, emqttd_config).
-define(TAB, emqx_config).
register_config() -> register_config() ->
application:start(clique), application:start(clique),
@ -40,7 +41,7 @@ create_config_tab() ->
case ets:info(?TAB, name) of case ets:info(?TAB, name) of
undefined -> undefined ->
ets:new(?TAB, [named_table, public]), ets:new(?TAB, [named_table, public]),
{ok, PluginsEtcDir} = emqttd:env(plugins_etc_dir), {ok, PluginsEtcDir} = emqx:env(plugins_etc_dir),
Files = filelib:wildcard("*.conf", PluginsEtcDir), Files = filelib:wildcard("*.conf", PluginsEtcDir),
lists:foreach(fun(File) -> lists:foreach(fun(File) ->
[FileName | _] = string:tokens(File, "."), [FileName | _] = string:tokens(File, "."),
@ -173,7 +174,7 @@ protocol_config_callback(_App, websocket_protocol_header, Value) ->
application:set_env(?APP, websocket_protocol_header, Value), application:set_env(?APP, websocket_protocol_header, Value),
" successfully\n"; " successfully\n";
protocol_config_callback(App, Key, Value) -> protocol_config_callback(App, Key, Value) ->
{ok, Env} = emqttd:env(App), {ok, Env} = emqx:env(App),
application:set_env(?APP, App, lists:keyreplace(Key, 1, Env, {Key, Value})), application:set_env(?APP, App, lists:keyreplace(Key, 1, Env, {Key, Value})),
" successfully\n". " successfully\n".
@ -215,15 +216,15 @@ client_config_callback([_, AppStr, KeyStr], Value) ->
client_config_callback(l2a(AppStr), l2a(KeyStr), Value). client_config_callback(l2a(AppStr), l2a(KeyStr), Value).
client_config_callback(App, idle_timeout, Value) -> client_config_callback(App, idle_timeout, Value) ->
{ok, Env} = emqttd:env(App), {ok, Env} = emqx:env(App),
application:set_env(?APP, App, lists:keyreplace(client_idle_timeout, 1, Env, {client_idle_timeout, Value})), application:set_env(?APP, App, lists:keyreplace(client_idle_timeout, 1, Env, {client_idle_timeout, Value})),
" successfully\n"; " successfully\n";
client_config_callback(App, enable_stats, Value) -> client_config_callback(App, enable_stats, Value) ->
{ok, Env} = emqttd:env(App), {ok, Env} = emqx:env(App),
application:set_env(?APP, App, lists:keyreplace(client_enable_stats, 1, Env, {client_enable_stats, Value})), application:set_env(?APP, App, lists:keyreplace(client_enable_stats, 1, Env, {client_enable_stats, Value})),
" successfully\n"; " successfully\n";
client_config_callback(App, Key, Value) -> client_config_callback(App, Key, Value) ->
{ok, Env} = emqttd:env(App), {ok, Env} = emqx:env(App),
application:set_env(?APP, App, lists:keyreplace(Key, 1, Env, {Key, Value})), application:set_env(?APP, App, lists:keyreplace(Key, 1, Env, {Key, Value})),
" successfully\n". " successfully\n".
@ -262,7 +263,7 @@ register_session_config() ->
session_config_callback([_, AppStr, KeyStr], Value) -> session_config_callback([_, AppStr, KeyStr], Value) ->
session_config_callback(l2a(AppStr), l2a(KeyStr), Value). session_config_callback(l2a(AppStr), l2a(KeyStr), Value).
session_config_callback(App, Key, Value) -> session_config_callback(App, Key, Value) ->
{ok, Env} = emqttd:env(App), {ok, Env} = emqx:env(App),
application:set_env(?APP, App, lists:keyreplace(Key, 1, Env, {Key, Value})), application:set_env(?APP, App, lists:keyreplace(Key, 1, Env, {Key, Value})),
" successfully\n". " successfully\n".
@ -298,15 +299,15 @@ queue_config_callback([_, AppStr, KeyStr], Value) ->
queue_config_callback(l2a(AppStr), l2a(KeyStr), Value). queue_config_callback(l2a(AppStr), l2a(KeyStr), Value).
queue_config_callback(App, low_watermark, Value) -> queue_config_callback(App, low_watermark, Value) ->
{ok, Env} = emqttd:env(App), {ok, Env} = emqx:env(App),
application:set_env(?APP, App, lists:keyreplace(low_watermark, 1, Env, {low_watermark, Value})), application:set_env(?APP, App, lists:keyreplace(low_watermark, 1, Env, {low_watermark, Value})),
" successfully\n"; " successfully\n";
queue_config_callback(App, high_watermark, Value) -> queue_config_callback(App, high_watermark, Value) ->
{ok, Env} = emqttd:env(App), {ok, Env} = emqx:env(App),
application:set_env(?APP, App, lists:keyreplace(high_watermark, 1, Env, {high_watermark, Value})), application:set_env(?APP, App, lists:keyreplace(high_watermark, 1, Env, {high_watermark, Value})),
" successfully\n"; " successfully\n";
queue_config_callback(App, Key, Value) -> queue_config_callback(App, Key, Value) ->
{ok, Env} = emqttd:env(App), {ok, Env} = emqx:env(App),
application:set_env(?APP, App, lists:keyreplace(Key, 1, Env, {Key, Value})), application:set_env(?APP, App, lists:keyreplace(Key, 1, Env, {Key, Value})),
" successfully\n". " successfully\n".

View File

@ -16,17 +16,17 @@
%% @doc MQTT/TCP Connection. %% @doc MQTT/TCP Connection.
-module(emqttd_client). -module(emqx_client).
-behaviour(gen_server2). -behaviour(gen_server2).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
-include("emqttd_protocol.hrl"). -include("emqx_mqtt.hrl").
-include("emqttd_internal.hrl"). -include("emqx_internal.hrl").
-import(proplists, [get_value/2, get_value/3]). -import(proplists, [get_value/2, get_value/3]).
@ -114,11 +114,11 @@ do_init(Conn, Env, Peername) ->
SendFun = send_fun(Conn, Peername), SendFun = send_fun(Conn, Peername),
RateLimit = get_value(rate_limit, Conn:opts()), RateLimit = get_value(rate_limit, Conn:opts()),
PacketSize = get_value(max_packet_size, Env, ?MAX_PACKET_SIZE), PacketSize = get_value(max_packet_size, Env, ?MAX_PACKET_SIZE),
Parser = emqttd_parser:initial_state(PacketSize), Parser = emqx_parser:initial_state(PacketSize),
ProtoState = emqttd_protocol:init(Conn, Peername, SendFun, Env), ProtoState = emqx_protocol:init(Conn, Peername, SendFun, Env),
EnableStats = get_value(client_enable_stats, Env, false), EnableStats = get_value(client_enable_stats, Env, false),
IdleTimout = get_value(client_idle_timeout, Env, 30000), IdleTimout = get_value(client_idle_timeout, Env, 30000),
ForceGcCount = emqttd_gc:conn_max_gc_count(), ForceGcCount = emqx_gc:conn_max_gc_count(),
State = run_socket(#client_state{connection = Conn, State = run_socket(#client_state{connection = Conn,
peername = Peername, peername = Peername,
await_recv = false, await_recv = false,
@ -136,9 +136,9 @@ do_init(Conn, Env, Peername) ->
send_fun(Conn, Peername) -> send_fun(Conn, Peername) ->
Self = self(), Self = self(),
fun(Packet) -> fun(Packet) ->
Data = emqttd_serializer:serialize(Packet), Data = emqx_serializer:serialize(Packet),
?LOG(debug, "SEND ~p", [Data], #client_state{peername = Peername}), ?LOG(debug, "SEND ~p", [Data], #client_state{peername = Peername}),
emqttd_metrics:inc('bytes/sent', iolist_size(Data)), emqx_metrics:inc('bytes/sent', iolist_size(Data)),
try Conn:async_send(Data) of try Conn:async_send(Data) of
true -> ok true -> ok
catch catch
@ -153,17 +153,17 @@ prioritise_info(Msg, _Len, _State) ->
case Msg of {redeliver, _} -> 5; _ -> 0 end. case Msg of {redeliver, _} -> 5; _ -> 0 end.
handle_pre_hibernate(State) -> handle_pre_hibernate(State) ->
{hibernate, emqttd_gc:reset_conn_gc_count(#client_state.force_gc_count, emit_stats(State))}. {hibernate, emqx_gc:reset_conn_gc_count(#client_state.force_gc_count, emit_stats(State))}.
handle_call(info, From, State = #client_state{proto_state = ProtoState}) -> handle_call(info, From, State = #client_state{proto_state = ProtoState}) ->
ProtoInfo = emqttd_protocol:info(ProtoState), ProtoInfo = emqx_protocol:info(ProtoState),
ClientInfo = ?record_to_proplist(client_state, State, ?INFO_KEYS), ClientInfo = ?record_to_proplist(client_state, State, ?INFO_KEYS),
{reply, Stats, _, _} = handle_call(stats, From, State), {reply, Stats, _, _} = handle_call(stats, From, State),
reply(lists:append([ClientInfo, ProtoInfo, Stats]), State); reply(lists:append([ClientInfo, ProtoInfo, Stats]), State);
handle_call(stats, _From, State = #client_state{proto_state = ProtoState}) -> handle_call(stats, _From, State = #client_state{proto_state = ProtoState}) ->
reply(lists:append([emqttd_misc:proc_stats(), reply(lists:append([emqx_misc:proc_stats(),
emqttd_protocol:stats(ProtoState), emqx_protocol:stats(ProtoState),
sock_stats(State)]), State); sock_stats(State)]), State);
handle_call(kick, _From, State) -> handle_call(kick, _From, State) ->
@ -176,7 +176,7 @@ handle_call(get_rate_limit, _From, State = #client_state{rate_limit = Rl}) ->
reply(Rl, State); reply(Rl, State);
handle_call(session, _From, State = #client_state{proto_state = ProtoState}) -> handle_call(session, _From, State = #client_state{proto_state = ProtoState}) ->
reply(emqttd_protocol:session(ProtoState), State); reply(emqx_protocol:session(ProtoState), State);
handle_call({clean_acl_cache, Topic}, _From, State) -> handle_call({clean_acl_cache, Topic}, _From, State) ->
erase({acl, publish, Topic}), erase({acl, publish, Topic}),
@ -191,13 +191,13 @@ handle_cast(Msg, State) ->
handle_info({subscribe, TopicTable}, State) -> handle_info({subscribe, TopicTable}, State) ->
with_proto( with_proto(
fun(ProtoState) -> fun(ProtoState) ->
emqttd_protocol:subscribe(TopicTable, ProtoState) emqx_protocol:subscribe(TopicTable, ProtoState)
end, State); end, State);
handle_info({unsubscribe, Topics}, State) -> handle_info({unsubscribe, Topics}, State) ->
with_proto( with_proto(
fun(ProtoState) -> fun(ProtoState) ->
emqttd_protocol:unsubscribe(Topics, ProtoState) emqx_protocol:unsubscribe(Topics, ProtoState)
end, State); end, State);
%% Asynchronous SUBACK %% Asynchronous SUBACK
@ -205,19 +205,23 @@ handle_info({suback, PacketId, GrantedQos}, State) ->
with_proto( with_proto(
fun(ProtoState) -> fun(ProtoState) ->
Packet = ?SUBACK_PACKET(PacketId, GrantedQos), Packet = ?SUBACK_PACKET(PacketId, GrantedQos),
emqttd_protocol:send(Packet, ProtoState) emqx_protocol:send(Packet, ProtoState)
end, State); end, State);
%% Fastlane
handle_info({dispatch, _Topic, Message}, State) ->
handle_info({deliver, Message#mqtt_message{qos = ?QOS_0}}, State);
handle_info({deliver, Message}, State) -> handle_info({deliver, Message}, State) ->
with_proto( with_proto(
fun(ProtoState) -> fun(ProtoState) ->
emqttd_protocol:send(Message, ProtoState) emqx_protocol:send(Message, ProtoState)
end, State); end, State);
handle_info({redeliver, {?PUBREL, PacketId}}, State) -> handle_info({redeliver, {?PUBREL, PacketId}}, State) ->
with_proto( with_proto(
fun(ProtoState) -> fun(ProtoState) ->
emqttd_protocol:pubrel(PacketId, ProtoState) emqx_protocol:pubrel(PacketId, ProtoState)
end, State); end, State);
handle_info(emit_stats, State) -> handle_info(emit_stats, State) ->
@ -240,7 +244,7 @@ handle_info(activate_sock, State) ->
handle_info({inet_async, _Sock, _Ref, {ok, Data}}, State) -> handle_info({inet_async, _Sock, _Ref, {ok, Data}}, State) ->
Size = iolist_size(Data), Size = iolist_size(Data),
?LOG(debug, "RECV ~p", [Data], State), ?LOG(debug, "RECV ~p", [Data], State),
emqttd_metrics:inc('bytes/received', Size), emqx_metrics:inc('bytes/received', Size),
received(Data, rate_limit(Size, State#client_state{await_recv = false})); received(Data, rate_limit(Size, State#client_state{await_recv = false}));
handle_info({inet_async, _Sock, _Ref, {error, Reason}}, State) -> handle_info({inet_async, _Sock, _Ref, {error, Reason}}, State) ->
@ -260,7 +264,7 @@ handle_info({keepalive, start, Interval}, State = #client_state{connection = Con
{error, Error} -> {error, Error} {error, Error} -> {error, Error}
end end
end, end,
case emqttd_keepalive:start(StatFun, Interval, {keepalive, check}) of case emqx_keepalive:start(StatFun, Interval, {keepalive, check}) of
{ok, KeepAlive} -> {ok, KeepAlive} ->
{noreply, State#client_state{keepalive = KeepAlive}, hibernate}; {noreply, State#client_state{keepalive = KeepAlive}, hibernate};
{error, Error} -> {error, Error} ->
@ -269,7 +273,7 @@ handle_info({keepalive, start, Interval}, State = #client_state{connection = Con
end; end;
handle_info({keepalive, check}, State = #client_state{keepalive = KeepAlive}) -> handle_info({keepalive, check}, State = #client_state{keepalive = KeepAlive}) ->
case emqttd_keepalive:check(KeepAlive) of case emqx_keepalive:check(KeepAlive) of
{ok, KeepAlive1} -> {ok, KeepAlive1} ->
{noreply, State#client_state{keepalive = KeepAlive1}, hibernate}; {noreply, State#client_state{keepalive = KeepAlive1}, hibernate};
{error, timeout} -> {error, timeout} ->
@ -289,14 +293,14 @@ terminate(Reason, State = #client_state{connection = Conn,
?LOG(debug, "Terminated for ~p", [Reason], State), ?LOG(debug, "Terminated for ~p", [Reason], State),
Conn:fast_close(), Conn:fast_close(),
emqttd_keepalive:cancel(KeepAlive), emqx_keepalive:cancel(KeepAlive),
case {ProtoState, Reason} of case {ProtoState, Reason} of
{undefined, _} -> {undefined, _} ->
ok; ok;
{_, {shutdown, Error}} -> {_, {shutdown, Error}} ->
emqttd_protocol:shutdown(Error, ProtoState); emqx_protocol:shutdown(Error, ProtoState);
{_, Reason} -> {_, Reason} ->
emqttd_protocol:shutdown(Reason, ProtoState) emqx_protocol:shutdown(Reason, ProtoState)
end. end.
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
@ -314,14 +318,14 @@ received(Bytes, State = #client_state{parser = Parser,
packet_size = PacketSize, packet_size = PacketSize,
proto_state = ProtoState, proto_state = ProtoState,
idle_timeout = IdleTimeout}) -> idle_timeout = IdleTimeout}) ->
case catch emqttd_parser:parse(Bytes, Parser) of case catch emqx_parser:parse(Bytes, Parser) of
{more, NewParser} -> {more, NewParser} ->
{noreply, run_socket(State#client_state{parser = NewParser}), IdleTimeout}; {noreply, run_socket(State#client_state{parser = NewParser}), IdleTimeout};
{ok, Packet, Rest} -> {ok, Packet, Rest} ->
emqttd_metrics:received(Packet), emqx_metrics:received(Packet),
case emqttd_protocol:received(Packet, ProtoState) of case emqx_protocol:received(Packet, ProtoState) of
{ok, ProtoState1} -> {ok, ProtoState1} ->
received(Rest, State#client_state{parser = emqttd_parser:initial_state(PacketSize), received(Rest, State#client_state{parser = emqx_parser:initial_state(PacketSize),
proto_state = ProtoState1}); proto_state = ProtoState1});
{error, Error} -> {error, Error} ->
?LOG(error, "Protocol error - ~p", [Error], State), ?LOG(error, "Protocol error - ~p", [Error], State),
@ -365,7 +369,7 @@ with_proto(Fun, State = #client_state{proto_state = ProtoState}) ->
{noreply, State#client_state{proto_state = ProtoState1}, hibernate}. {noreply, State#client_state{proto_state = ProtoState1}, hibernate}.
emit_stats(State = #client_state{proto_state = ProtoState}) -> emit_stats(State = #client_state{proto_state = ProtoState}) ->
emit_stats(emqttd_protocol:clientid(ProtoState), State). emit_stats(emqx_protocol:clientid(ProtoState), State).
emit_stats(_ClientId, State = #client_state{enable_stats = false}) -> emit_stats(_ClientId, State = #client_state{enable_stats = false}) ->
State; State;
@ -373,7 +377,7 @@ emit_stats(undefined, State) ->
State; State;
emit_stats(ClientId, State) -> emit_stats(ClientId, State) ->
{reply, Stats, _, _} = handle_call(stats, undefined, State), {reply, Stats, _, _} = handle_call(stats, undefined, State),
emqttd_stats:set_client_stats(ClientId, Stats), emqx_stats:set_client_stats(ClientId, Stats),
State. State.
sock_stats(#client_state{connection = Conn}) -> sock_stats(#client_state{connection = Conn}) ->
@ -390,5 +394,5 @@ stop(Reason, State) ->
gc(State = #client_state{connection = Conn}) -> gc(State = #client_state{connection = Conn}) ->
Cb = fun() -> Conn:gc(), emit_stats(State) end, Cb = fun() -> Conn:gc(), emit_stats(State) end,
emqttd_gc:maybe_force_gc(#client_state.force_gc_count, State, Cb). emqx_gc:maybe_force_gc(#client_state.force_gc_count, State, Cb).

View File

@ -16,15 +16,15 @@
%% @doc MQTT Client Manager %% @doc MQTT Client Manager
-module(emqttd_cm). -module(emqx_cm).
-behaviour(gen_server2). -behaviour(gen_server2).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
-include("emqttd_internal.hrl"). -include("emqx_internal.hrl").
%% API Exports %% API Exports
-export([start_link/3]). -export([start_link/3]).
@ -123,7 +123,7 @@ handle_info({'DOWN', MRef, process, DownPid, _Reason}, State) ->
{ok, {ClientId, DownPid}} -> {ok, {ClientId, DownPid}} ->
case lookup_proc(ClientId) of case lookup_proc(ClientId) of
DownPid -> DownPid ->
emqttd_stats:del_client_stats(ClientId), emqx_stats:del_client_stats(ClientId),
ets:delete(mqtt_client, ClientId); ets:delete(mqtt_client, ClientId);
_ -> _ ->
ignore ignore

View File

@ -16,13 +16,13 @@
%% @doc Client Manager Supervisor. %% @doc Client Manager Supervisor.
-module(emqttd_cm_sup). -module(emqx_cm_sup).
-behaviour(supervisor). -behaviour(supervisor).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
%% API %% API
-export([start_link/0]). -export([start_link/0]).
@ -30,7 +30,7 @@
%% Supervisor callbacks %% Supervisor callbacks
-export([init/1]). -export([init/1]).
-define(CM, emqttd_cm). -define(CM, emqx_cm).
-define(TAB, mqtt_client). -define(TAB, mqtt_client).
@ -42,8 +42,8 @@ init([]) ->
create_client_tab(), create_client_tab(),
%% CM Pool Sup %% CM Pool Sup
MFA = {?CM, start_link, [emqttd_stats:statsfun('clients/count', 'clients/max')]}, MFA = {?CM, start_link, [emqx_stats:statsfun('clients/count', 'clients/max')]},
PoolSup = emqttd_pool_sup:spec([?CM, hash, erlang:system_info(schedulers), MFA]), PoolSup = emqx_pool_sup:spec([?CM, hash, erlang:system_info(schedulers), MFA]),
{ok, {{one_for_all, 10, 3600}, [PoolSup]}}. {ok, {{one_for_all, 10, 3600}, [PoolSup]}}.

View File

@ -23,7 +23,7 @@
%% 3. Store in data/app.config? %% 3. Store in data/app.config?
%% %%
-module(emqttd_config). -module(emqx_config).
-export([read/1, write/2, dump/2, reload/1, get/2, get/3, set/3]). -export([read/1, write/2, dump/2, reload/1, get/2, get/3, set/3]).
@ -57,7 +57,7 @@ write(App, Terms) ->
Schema = cuttlefish_schema:files([Path]), Schema = cuttlefish_schema:files([Path]),
case cuttlefish_generator:map(Schema, Configs) of case cuttlefish_generator:map(Schema, Configs) of
[{App, Configs1}] -> [{App, Configs1}] ->
emqttd_cli_config:write_config(App, Configs), emqx_cli_config:write_config(App, Configs),
lists:foreach(fun({Key, Val}) -> application:set_env(App, Key, Val) end, Configs1); lists:foreach(fun({Key, Val}) -> application:set_env(App, Key, Val) end, Configs1);
_ -> _ ->
error error
@ -70,25 +70,25 @@ dump(_App, _Terms) ->
-spec(set(atom(), list(), list()) -> ok). -spec(set(atom(), list(), list()) -> ok).
set(App, Par, Val) -> set(App, Par, Val) ->
emqttd_cli_config:run(["config", emqx_cli_config:run(["config",
"set", "set",
lists:concat([Par, "=", Val]), lists:concat([Par, "=", Val]),
lists:concat(["--app=", App])]). lists:concat(["--app=", App])]).
-spec(get(atom(), list()) -> undefined | {ok, term()}). -spec(get(atom(), list()) -> undefined | {ok, term()}).
get(App, Par) -> get(App, Par) ->
case emqttd_cli_config:get_cfg(App, Par) of case emqx_cli_config:get_cfg(App, Par) of
undefined -> undefined; undefined -> undefined;
Val -> {ok, Val} Val -> {ok, Val}
end. end.
-spec(get(atom(), list(), atom()) -> term()). -spec(get(atom(), list(), atom()) -> term()).
get(App, Par, Def) -> get(App, Par, Def) ->
emqttd_cli_config:get_cfg(App, Par, Def). emqx_cli_config:get_cfg(App, Par, Def).
read_(App) -> read_(App) ->
Configs = emqttd_cli_config:read_config(App), Configs = emqx_cli_config:read_config(App),
Path = lists:concat([code:priv_dir(App), "/", App, ".schema"]), Path = lists:concat([code:priv_dir(App), "/", App, ".schema"]),
case filelib:is_file(Path) of case filelib:is_file(Path) of
false -> false ->
@ -112,3 +112,4 @@ read_(App) ->
end, [], Configs), end, [], Configs),
RequiredCfg ++ OptionalCfg RequiredCfg ++ OptionalCfg
end. end.

View File

@ -14,15 +14,15 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_ctl). -module(emqx_ctl).
-behaviour(gen_server). -behaviour(gen_server).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
-include("emqttd_cli.hrl"). -include("emqx_cli.hrl").
-define(SERVER, ?MODULE). -define(SERVER, ?MODULE).
@ -69,13 +69,13 @@ run([]) -> usage(), ok;
run(["help"]) -> usage(), ok; run(["help"]) -> usage(), ok;
run(["set"] = CmdS) when length(CmdS) =:= 1 -> run(["set"] = CmdS) when length(CmdS) =:= 1 ->
emqttd_cli_config:set_usage(), ok; emqx_cli_config:set_usage(), ok;
run(["set" | _] = CmdS) -> run(["set" | _] = CmdS) ->
emqttd_cli_config:run(["config" | CmdS]), ok; emqx_cli_config:run(["config" | CmdS]), ok;
run(["show" | _] = CmdS) -> run(["show" | _] = CmdS) ->
emqttd_cli_config:run(["config" | CmdS]), ok; emqx_cli_config:run(["config" | CmdS]), ok;
run([CmdS|Args]) -> run([CmdS|Args]) ->
case lookup(list_to_atom(CmdS)) of case lookup(list_to_atom(CmdS)) of
@ -161,14 +161,14 @@ next_seq(State = #state{seq = Seq}) ->
register_cmd_test_() -> register_cmd_test_() ->
{setup, {setup,
fun() -> fun() ->
{ok, InitState} = emqttd_ctl:init([]), {ok, InitState} = emqx_ctl:init([]),
InitState InitState
end, end,
fun(State) -> fun(State) ->
ok = emqttd_ctl:terminate(shutdown, State) ok = emqx_ctl:terminate(shutdown, State)
end, end,
fun(State = #state{seq = Seq}) -> fun(State = #state{seq = Seq}) ->
emqttd_ctl:handle_cast({register_cmd, test0, {?MODULE, test0}, []}, State), emqx_ctl:handle_cast({register_cmd, test0, {?MODULE, test0}, []}, State),
[?_assertMatch([{{0,test0},{?MODULE, test0}, []}], ets:lookup(?CMD_TAB, {Seq,test0}))] [?_assertMatch([{{0,test0},{?MODULE, test0}, []}], ets:lookup(?CMD_TAB, {Seq,test0}))]
end end
}. }.

View File

@ -16,7 +16,7 @@
%% GC Utility functions. %% GC Utility functions.
-module(emqttd_gc). -module(emqx_gc).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
@ -25,7 +25,7 @@
-spec(conn_max_gc_count() -> integer()). -spec(conn_max_gc_count() -> integer()).
conn_max_gc_count() -> conn_max_gc_count() ->
case emqttd:env(conn_force_gc_count) of case emqx:env(conn_force_gc_count) of
{ok, I} when I > 0 -> I + rand:uniform(I); {ok, I} when I > 0 -> I + rand:uniform(I);
{ok, I} when I =< 0 -> undefined; {ok, I} when I =< 0 -> undefined;
undefined -> undefined undefined -> undefined

View File

@ -14,14 +14,10 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @doc emqttd gen_mod behaviour -module(emqx_gen_mod).
-module(emqttd_gen_mod).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl").
-ifdef(use_specs). -ifdef(use_specs).
-callback(load(Opts :: any()) -> ok | {error, any()}). -callback(load(Opts :: any()) -> ok | {error, any()}).

View File

@ -28,7 +28,7 @@
%% %%
%% @end %% @end
-module(emqttd_guid). -module(emqx_guid).
-export([gen/0, new/0, timestamp/1, to_hexstr/1, from_hexstr/1, to_base62/1, from_base62/1]). -export([gen/0, new/0, timestamp/1, to_hexstr/1, from_hexstr/1, to_base62/1, from_base62/1]).
@ -128,8 +128,8 @@ from_hexstr(S) ->
I = list_to_integer(binary_to_list(S), 16), <<I:128>>. I = list_to_integer(binary_to_list(S), 16), <<I:128>>.
to_base62(<<I:128>>) -> to_base62(<<I:128>>) ->
emqttd_base62:encode(I). emqx_base62:encode(I).
from_base62(S) -> from_base62(S) ->
I = emqttd_base62:decode(S), <<I:128>>. I = emqx_base62:decode(S), <<I:128>>.

View File

@ -14,7 +14,7 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_hooks). -module(emqx_hooks).
-behaviour(gen_server). -behaviour(gen_server).

View File

@ -16,19 +16,21 @@
%% @doc HTTP publish API and websocket client. %% @doc HTTP publish API and websocket client.
-module(emqttd_http). -module(emqx_http).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
-include("emqttd_protocol.hrl"). -include("emqx_mqtt.hrl").
-import(proplists, [get_value/2, get_value/3]). -import(proplists, [get_value/2, get_value/3]).
-export([http_handler/0, handle_request/2, http_api/0, inner_handle_request/2]). -export([http_handler/0, handle_request/2, http_api/0, inner_handle_request/2]).
-include("emqttd_internal.hrl"). -include("emqx_rest.hrl").
-include("emqx_internal.hrl").
-record(state, {dispatch}). -record(state, {dispatch}).
@ -38,7 +40,7 @@ http_handler() ->
{?MODULE, handle_request, [State]}. {?MODULE, handle_request, [State]}.
http_api() -> http_api() ->
Attr = emqttd_rest_api:module_info(attributes), Attr = emqx_rest_api:module_info(attributes),
[{Regexp, Method, Function, Args} || {http_api, [{Regexp, Method, Function, Args}]} <- Attr]. [{Regexp, Method, Function, Args} || {http_api, [{Regexp, Method, Function, Args}]} <- Attr].
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
@ -51,7 +53,7 @@ handle_request(Req, State) ->
handle_request("/status", Req, Req:get(method)); handle_request("/status", Req, Req:get(method));
"/" -> "/" ->
handle_request("/", Req, Req:get(method)); handle_request("/", Req, Req:get(method));
"/api/v2/auth" -> "/api/v2/auth" -> %%TODO: Security Issue!
handle_request(Path, Req, State); handle_request(Path, Req, State);
_ -> _ ->
if_authorized(Req, fun() -> handle_request(Path, Req, State) end) if_authorized(Req, fun() -> handle_request(Path, Req, State) end)
@ -67,11 +69,11 @@ handle_request("/api/v2/" ++ Url, Req, #state{dispatch = Dispatch}) ->
handle_request("/status", Req, Method) when Method =:= 'HEAD'; Method =:= 'GET' -> handle_request("/status", Req, Method) when Method =:= 'HEAD'; Method =:= 'GET' ->
{InternalStatus, _ProvidedStatus} = init:get_status(), {InternalStatus, _ProvidedStatus} = init:get_status(),
AppStatus = case lists:keysearch(emqttd, 1, application:which_applications()) of AppStatus = case lists:keysearch(emqx, 1, application:which_applications()) of
false -> not_running; false -> not_running;
{value, _Val} -> running {value, _Val} -> running
end, end,
Status = io_lib:format("Node ~s is ~s~nemqttd is ~s", Status = io_lib:format("Node ~s is ~s~nemqx is ~s",
[node(), InternalStatus, AppStatus]), [node(), InternalStatus, AppStatus]),
Req:ok({"text/plain", iolist_to_binary(Status)}); Req:ok({"text/plain", iolist_to_binary(Status)});
@ -95,8 +97,8 @@ dispatcher(APIs) ->
{true, true} -> {true, true} ->
{match, [MatchList]} = re:run(Url, Regexp, [global, {capture, all_but_first, list}]), {match, [MatchList]} = re:run(Url, Regexp, [global, {capture, all_but_first, list}]),
Args = lists:append([[Method, Params], MatchList]), Args = lists:append([[Method, Params], MatchList]),
lager:debug("Mod:~p, Fun:~p, Args:~p", [emqttd_rest_api, Function, Args]), lager:debug("Mod:~p, Fun:~p, Args:~p", [emqx_rest_api, Function, Args]),
case catch apply(emqttd_rest_api, Function, Args) of case catch apply(emqx_rest_api, Function, Args) of
{ok, Data} -> {ok, Data} ->
respond(Req, 200, [{code, ?SUCCESS}, {result, Data}]); respond(Req, 200, [{code, ?SUCCESS}, {result, Data}]);
{error, Error} -> {error, Error} ->
@ -132,7 +134,7 @@ authorized(Req) ->
false; false;
"Basic " ++ BasicAuth -> "Basic " ++ BasicAuth ->
{Username, Password} = user_passwd(BasicAuth), {Username, Password} = user_passwd(BasicAuth),
case emqttd_mgmt:check_user(Username, Password) of case emq_mgmt:check_user(Username, Password) of
ok -> ok ->
true; true;
{error, Reason} -> {error, Reason} ->

View File

@ -16,7 +16,7 @@
%% @doc Inflight Window that wraps the gb_trees. %% @doc Inflight Window that wraps the gb_trees.
-module(emqttd_inflight). -module(emqx_inflight).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").

View File

@ -16,7 +16,7 @@
%% @doc Client Keepalive %% @doc Client Keepalive
-module(emqttd_keepalive). -module(emqx_keepalive).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").

View File

@ -14,7 +14,7 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(lager_emqtt_backend). -module(emqx_lager_backend).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
@ -79,10 +79,10 @@ publish_log(Message, State = #state{formatter = Formatter,
format_config = FormatConfig}) -> format_config = FormatConfig}) ->
Severity = lager_msg:severity(Message), Severity = lager_msg:severity(Message),
Payload = Formatter:format(Message, FormatConfig), Payload = Formatter:format(Message, FormatConfig),
Msg = emqttd_message:make(log, topic(Severity), iolist_to_binary(Payload)), Msg = emqx_message:make(log, topic(Severity), iolist_to_binary(Payload)),
emqttd:publish(emqttd_message:set_flag(sys, Msg)), emqx:publish(emqx_message:set_flag(sys, Msg)),
{ok, State}. {ok, State}.
topic(Severity) -> topic(Severity) ->
emqttd_topic:systop(list_to_binary(lists:concat(['logs/', Severity]))). emqx_topic:systop(list_to_binary(lists:concat(['logs/', Severity]))).

View File

@ -16,13 +16,13 @@
%% @doc MQTT Message Functions %% @doc MQTT Message Functions
-module(emqttd_message). -module(emqx_message).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
-include("emqttd_protocol.hrl"). -include("emqx_mqtt.hrl").
-export([make/3, make/4, from_packet/1, from_packet/2, from_packet/3, -export([make/3, make/4, from_packet/1, from_packet/2, from_packet/3,
to_packet/1]). to_packet/1]).
@ -91,9 +91,9 @@ from_packet(Username, ClientId, Packet) ->
Msg = from_packet(Packet), Msg = from_packet(Packet),
Msg#mqtt_message{from = {ClientId, Username}}. Msg#mqtt_message{from = {ClientId, Username}}.
msgid() -> emqttd_guid:gen(). msgid() -> emqx_guid:gen().
%% @doc Message to packet %% @doc Message to Packet
-spec(to_packet(mqtt_message()) -> mqtt_packet()). -spec(to_packet(mqtt_message()) -> mqtt_packet()).
to_packet(#mqtt_message{pktid = PkgId, to_packet(#mqtt_message{pktid = PkgId,
qos = Qos, qos = Qos,

View File

@ -14,15 +14,15 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_metrics). -module(emqx_metrics).
-behaviour(gen_server). -behaviour(gen_server).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
-include("emqttd_protocol.hrl"). -include("emqx_mqtt.hrl").
-define(SERVER, ?MODULE). -define(SERVER, ?MODULE).
@ -89,7 +89,8 @@
{counter, 'messages/qos2/sent'}, % QoS2 Messages sent {counter, 'messages/qos2/sent'}, % QoS2 Messages sent
{counter, 'messages/qos2/dropped'}, % QoS2 Messages dropped {counter, 'messages/qos2/dropped'}, % QoS2 Messages dropped
{gauge, 'messages/retained'}, % Messagea retained {gauge, 'messages/retained'}, % Messagea retained
{counter, 'messages/dropped'} % Messages dropped {counter, 'messages/dropped'}, % Messages dropped
{counter, 'messages/forward'} % Messages forward
]). ]).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
@ -242,7 +243,7 @@ key(counter, Metric) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
init([]) -> init([]) ->
emqttd_time:seed(), emqx_time:seed(),
Metrics = ?SYSTOP_BYTES ++ ?SYSTOP_PACKETS ++ ?SYSTOP_MESSAGES, Metrics = ?SYSTOP_BYTES ++ ?SYSTOP_PACKETS ++ ?SYSTOP_MESSAGES,
% Create metrics table % Create metrics table
ets:new(?METRIC_TAB, [set, public, named_table, {write_concurrency, true}]), ets:new(?METRIC_TAB, [set, public, named_table, {write_concurrency, true}]),
@ -251,7 +252,7 @@ init([]) ->
% $SYS Topics for metrics % $SYS Topics for metrics
% [ok = emqttd:create(topic, metric_topic(Topic)) || {_, Topic} <- Metrics], % [ok = emqttd:create(topic, metric_topic(Topic)) || {_, Topic} <- Metrics],
% Tick to publish metrics % Tick to publish metrics
{ok, #state{tick = emqttd_broker:start_tick(tick)}, hibernate}. {ok, #state{tick = emqx_broker:start_tick(tick)}, hibernate}.
handle_call(_Req, _From, State) -> handle_call(_Req, _From, State) ->
{reply, error, State}. {reply, error, State}.
@ -268,7 +269,7 @@ handle_info(_Info, State) ->
{noreply, State}. {noreply, State}.
terminate(_Reason, #state{tick = TRef}) -> terminate(_Reason, #state{tick = TRef}) ->
emqttd_broker:stop_tick(TRef). emqx_broker:stop_tick(TRef).
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
{ok, State}. {ok, State}.
@ -278,8 +279,8 @@ code_change(_OldVsn, State, _Extra) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
publish(Metric, Val) -> publish(Metric, Val) ->
Msg = emqttd_message:make(metrics, metric_topic(Metric), bin(Val)), Msg = emqx_message:make(metrics, metric_topic(Metric), bin(Val)),
emqttd:publish(emqttd_message:set_flag(sys, Msg)). emqx:publish(emqx_message:set_flag(sys, Msg)).
create_metric({gauge, Name}) -> create_metric({gauge, Name}) ->
ets:insert(?METRIC_TAB, {{Name, 0}, 0}); ets:insert(?METRIC_TAB, {{Name, 0}, 0});
@ -289,7 +290,7 @@ create_metric({counter, Name}) ->
ets:insert(?METRIC_TAB, [{{Name, I}, 0} || I <- Schedulers]). ets:insert(?METRIC_TAB, [{{Name, I}, 0} || I <- Schedulers]).
metric_topic(Metric) -> metric_topic(Metric) ->
emqttd_topic:systop(list_to_binary(lists:concat(['metrics/', Metric]))). emqx_topic:systop(list_to_binary(lists:concat(['metrics/', Metric]))).
bin(I) when is_integer(I) -> list_to_binary(integer_to_list(I)). bin(I) when is_integer(I) -> list_to_binary(integer_to_list(I)).

View File

@ -14,15 +14,17 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_mgmt). -module(emqx_mgmt).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
-include("emqttd_protocol.hrl"). -include("emqx_mqtt.hrl").
-include("emqttd_internal.hrl"). -include("emqx_internal.hrl").
-include("emqx_rest.hrl").
-include_lib("stdlib/include/qlc.hrl"). -include_lib("stdlib/include/qlc.hrl").
@ -61,7 +63,7 @@ brokers() ->
[{Node, broker(Node)} || Node <- ekka_mnesia:running_nodes()]. [{Node, broker(Node)} || Node <- ekka_mnesia:running_nodes()].
broker(Node) when Node =:= node() -> broker(Node) when Node =:= node() ->
emqttd_broker:info(); emqx_broker:info();
broker(Node) -> broker(Node) ->
rpc_call(Node, broker, [Node]). rpc_call(Node, broker, [Node]).
@ -69,7 +71,7 @@ metrics() ->
[{Node, metrics(Node)} || Node <- ekka_mnesia:running_nodes()]. [{Node, metrics(Node)} || Node <- ekka_mnesia:running_nodes()].
metrics(Node) when Node =:= node() -> metrics(Node) when Node =:= node() ->
emqttd_metrics:all(); emqx_metrics:all();
metrics(Node) -> metrics(Node) ->
rpc_call(Node, metrics, [Node]). rpc_call(Node, metrics, [Node]).
@ -77,7 +79,7 @@ stats() ->
[{Node, stats(Node)} || Node <- ekka_mnesia:running_nodes()]. [{Node, stats(Node)} || Node <- ekka_mnesia:running_nodes()].
stats(Node) when Node =:= node() -> stats(Node) when Node =:= node() ->
emqttd_stats:getstats(); emqx_stats:getstats();
stats(Node) -> stats(Node) ->
rpc_call(Node, stats, [Node]). rpc_call(Node, stats, [Node]).
@ -85,7 +87,7 @@ plugins() ->
[{Node, plugins(Node)} || Node <- ekka_mnesia:running_nodes()]. [{Node, plugins(Node)} || Node <- ekka_mnesia:running_nodes()].
plugins(Node) when Node =:= node() -> plugins(Node) when Node =:= node() ->
emqttd_plugins:list(Node); emqx_plugins:list(Node);
plugins(Node) -> plugins(Node) ->
rpc_call(Node, plugins, [Node]). rpc_call(Node, plugins, [Node]).
@ -111,8 +113,8 @@ nodes_info() ->
[node_info(Node) || Node <- Running] ++ DownNodes. [node_info(Node) || Node <- Running] ++ DownNodes.
node_info(Node) when Node =:= node() -> node_info(Node) when Node =:= node() ->
CpuInfo = [{K, list_to_binary(V)} || {K, V} <- emqttd_vm:loads()], CpuInfo = [{K, list_to_binary(V)} || {K, V} <- emqx_vm:loads()],
Memory = emqttd_vm:get_memory(), Memory = emqx_vm:get_memory(),
OtpRel = "R" ++ erlang:system_info(otp_release) ++ "/" ++ erlang:system_info(version), OtpRel = "R" ++ erlang:system_info(otp_release) ++ "/" ++ erlang:system_info(version),
[{name, node()}, [{name, node()},
{otp_release, list_to_binary(OtpRel)}, {otp_release, list_to_binary(OtpRel)},
@ -133,17 +135,17 @@ stop_node(Node) ->
%% plugins %% plugins
%%-------------------------------------------------------- %%--------------------------------------------------------
plugin_list(Node) when Node =:= node() -> plugin_list(Node) when Node =:= node() ->
emqttd_plugins:list(); emqx_plugins:list();
plugin_list(Node) -> plugin_list(Node) ->
rpc_call(Node, plugin_list, [Node]). rpc_call(Node, plugin_list, [Node]).
plugin_load(Node, PluginName) when Node =:= node() -> plugin_load(Node, PluginName) when Node =:= node() ->
emqttd_plugins:load(PluginName); emqx_plugins:load(PluginName);
plugin_load(Node, PluginName) -> plugin_load(Node, PluginName) ->
rpc_call(Node, plugin_load, [Node, PluginName]). rpc_call(Node, plugin_load, [Node, PluginName]).
plugin_unload(Node, PluginName) when Node =:= node() -> plugin_unload(Node, PluginName) when Node =:= node() ->
emqttd_plugins:unload(PluginName); emqx_plugins:unload(PluginName);
plugin_unload(Node, PluginName) -> plugin_unload(Node, PluginName) ->
rpc_call(Node, plugin_unload, [Node, PluginName]). rpc_call(Node, plugin_unload, [Node, PluginName]).
@ -189,7 +191,7 @@ route(Key) -> route_list(Key, 1, 20).
%% alarm %% alarm
%%-------------------------------------------------------- %%--------------------------------------------------------
alarm_list() -> alarm_list() ->
emqttd_alarm:get_alarms(). emqx_alarm:get_alarms().
query_table(Qh, PageNo, PageSize, TotalNum) -> query_table(Qh, PageNo, PageSize, TotalNum) ->
Cursor = qlc:cursor(Qh), Cursor = qlc:cursor(Qh),
@ -220,8 +222,8 @@ lookup_table(LookupFun, _PageNo, _PageSize) ->
publish({ClientId, Topic, Payload, Qos, Retain}) -> publish({ClientId, Topic, Payload, Qos, Retain}) ->
case validate(topic, Topic) of case validate(topic, Topic) of
true -> true ->
Msg = emqttd_message:make(ClientId, Qos, Topic, Payload), Msg = emqx_message:make(ClientId, Qos, Topic, Payload),
emqttd:publish(Msg#mqtt_message{retain = Retain}), emqx:publish(Msg#mqtt_message{retain = Retain}),
ok; ok;
false -> false ->
{error, format_error(Topic, "validate topic: ${0} fail")} {error, format_error(Topic, "validate topic: ${0} fail")}
@ -230,11 +232,11 @@ publish({ClientId, Topic, Payload, Qos, Retain}) ->
subscribe({ClientId, Topic, Qos}) -> subscribe({ClientId, Topic, Qos}) ->
case validate(topic, Topic) of case validate(topic, Topic) of
true -> true ->
case emqttd_sm:lookup_session(ClientId) of case emqx_sm:lookup_session(ClientId) of
undefined -> undefined ->
{error, format_error(ClientId, "Clientid: ${0} not found")}; {error, format_error(ClientId, "Clientid: ${0} not found")};
#mqtt_session{sess_pid = SessPid} -> #mqtt_session{sess_pid = SessPid} ->
emqttd_session:subscribe(SessPid, [{Topic, [{qos, Qos}]}]), emqx_session:subscribe(SessPid, [{Topic, [{qos, Qos}]}]),
ok ok
end; end;
false -> false ->
@ -244,16 +246,53 @@ subscribe({ClientId, Topic, Qos}) ->
unsubscribe({ClientId, Topic}) -> unsubscribe({ClientId, Topic}) ->
case validate(topic, Topic) of case validate(topic, Topic) of
true -> true ->
case emqttd_sm:lookup_session(ClientId) of case emqx_sm:lookup_session(ClientId) of
undefined -> undefined ->
{error, format_error(ClientId, "Clientid: ${0} not found")}; {error, format_error(ClientId, "Clientid: ${0} not found")};
#mqtt_session{sess_pid = SessPid} -> #mqtt_session{sess_pid = SessPid} ->
emqttd_session:unsubscribe(SessPid, [{Topic, []}]), emqx_session:unsubscribe(SessPid, [{Topic, []}]),
ok ok
end; end;
false -> false ->
{error, format_error(Topic, "validate topic: ${0} fail")} {error, format_error(Topic, "validate topic: ${0} fail")}
end. end.
% publish(Messages) ->
% lists:foldl(
% fun({ClientId, Topic, Payload, Qos, Retain}, {Success, Failed}) ->
% case validate(topic, Topic) of
% true ->
% Msg = emqx_message:make(ClientId, Qos, Topic, Payload),
% emqx:publish(Msg#mqtt_message{retain = Retain}),
% {[[{topic, Topic}]| Success], Failed};
% false ->
% {Success, [[{topic, Topic}]| Failed]}
% end
% end, {[], []}, Messages).
% subscribers(Subscribers) ->
% lists:foldl(
% fun({ClientId, Topic, Qos}, {Success, Failed}) ->
% case emqx_sm:lookup_session(ClientId) of
% undefined ->
% {Success, [[{client_id, ClientId}]|Failed]};
% #mqtt_session{sess_pid = SessPid} ->
% emqx_session:subscribe(SessPid, [{Topic, [{qos, Qos}]}]),
% {[[{client_id, ClientId}]| Success], Failed}
% end
% end,{[], []}, Subscribers).
% unsubscribers(UnSubscribers)->
% lists:foldl(
% fun({ClientId, Topic}, {Success, Failed}) ->
% case emqx_sm:lookup_session(ClientId) of
% undefined ->
% {Success, [[{client_id, ClientId}]|Failed]};
% #mqtt_session{sess_pid = SessPid} ->
% emqx_session:unsubscriber(SessPid, [{Topic, []}]),
% {[[{client_id, ClientId}]| Success], Failed}
% end
% end, {[], []}, UnSubscribers).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% manager API %% manager API
@ -263,9 +302,9 @@ kick_client(ClientId) ->
lists:any(fun(Item) -> Item =:= ok end, Result). lists:any(fun(Item) -> Item =:= ok end, Result).
kick_client(Node, ClientId) when Node =:= node() -> kick_client(Node, ClientId) when Node =:= node() ->
case emqttd_cm:lookup(ClientId) of case emqx_cm:lookup(ClientId) of
undefined -> error; undefined -> error;
#mqtt_client{client_pid = Pid}-> emqttd_client:kick(Pid) #mqtt_client{client_pid = Pid}-> emqx_client:kick(Pid)
end; end;
kick_client(Node, ClientId) -> kick_client(Node, ClientId) ->
rpc_call(Node, kick_client, [Node, ClientId]). rpc_call(Node, kick_client, [Node, ClientId]).
@ -276,9 +315,9 @@ clean_acl_cache(ClientId, Topic) ->
lists:any(fun(Item) -> Item =:= ok end, Result). lists:any(fun(Item) -> Item =:= ok end, Result).
clean_acl_cache(Node, ClientId, Topic) when Node =:= node() -> clean_acl_cache(Node, ClientId, Topic) when Node =:= node() ->
case emqttd_cm:lookup(ClientId) of case emqx_cm:lookup(ClientId) of
undefined -> error; undefined -> error;
#mqtt_client{client_pid = Pid}-> emqttd_client:clean_acl_cache(Pid, Topic) #mqtt_client{client_pid = Pid}-> emqx_client:clean_acl_cache(Pid, Topic)
end; end;
clean_acl_cache(Node, ClientId, Topic) -> clean_acl_cache(Node, ClientId, Topic) ->
rpc_call(Node, clean_acl_cache, [Node, ClientId, Topic]). rpc_call(Node, clean_acl_cache, [Node, ClientId, Topic]).
@ -287,14 +326,14 @@ clean_acl_cache(Node, ClientId, Topic) ->
%% Config ENV %% Config ENV
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
modify_config(App, Terms) -> modify_config(App, Terms) ->
emqttd_config:write(App, Terms). emqx_config:write(App, Terms).
modify_config(App, Key, Value) -> modify_config(App, Key, Value) ->
Result = [modify_config(Node, App, Key, Value) || Node <- ekka_mnesia:running_nodes()], Result = [modify_config(Node, App, Key, Value) || Node <- ekka_mnesia:running_nodes()],
lists:any(fun(Item) -> Item =:= ok end, Result). lists:any(fun(Item) -> Item =:= ok end, Result).
modify_config(Node, App, Key, Value) when Node =:= node() -> modify_config(Node, App, Key, Value) when Node =:= node() ->
emqttd_config:set(App, Key, Value); emqx_config:set(App, Key, Value);
modify_config(Node, App, Key, Value) -> modify_config(Node, App, Key, Value) ->
rpc_call(Node, modify_config, [Node, App, Key, Value]). rpc_call(Node, modify_config, [Node, App, Key, Value]).
@ -302,17 +341,17 @@ get_configs() ->
[{Node, get_config(Node)} || Node <- ekka_mnesia:running_nodes()]. [{Node, get_config(Node)} || Node <- ekka_mnesia:running_nodes()].
get_config(Node) when Node =:= node()-> get_config(Node) when Node =:= node()->
emqttd_cli_config:all_cfgs(); emqx_cli_config:all_cfgs();
get_config(Node) -> get_config(Node) ->
rpc_call(Node, get_config, [Node]). rpc_call(Node, get_config, [Node]).
get_plugin_config(PluginName) -> get_plugin_config(PluginName) ->
emqttd_config:read(PluginName). emqx_config:read(PluginName).
get_plugin_config(Node, PluginName) -> get_plugin_config(Node, PluginName) ->
rpc_call(Node, get_plugin_config, [PluginName]). rpc_call(Node, get_plugin_config, [PluginName]).
modify_plugin_config(PluginName, Terms) -> modify_plugin_config(PluginName, Terms) ->
emqttd_config:write(PluginName, Terms). emqx_config:write(PluginName, Terms).
modify_plugin_config(Node, PluginName, Terms) -> modify_plugin_config(Node, PluginName, Terms) ->
rpc_call(Node, modify_plugin_config, [PluginName, Terms]). rpc_call(Node, modify_plugin_config, [PluginName, Terms]).
@ -428,7 +467,7 @@ validate(qos, Qos) ->
(Qos >= ?QOS_0) and (Qos =< ?QOS_2); (Qos >= ?QOS_0) and (Qos =< ?QOS_2);
validate(topic, Topic) -> validate(topic, Topic) ->
emqttd_topic:validate({name, Topic}). emqx_topic:validate({name, Topic}).
client_list(ClientId, PageNo, PageSize) when ?EMPTY_KEY(ClientId) -> client_list(ClientId, PageNo, PageSize) when ?EMPTY_KEY(ClientId) ->
TotalNum = ets:info(mqtt_client, size), TotalNum = ets:info(mqtt_client, size),

View File

@ -14,7 +14,7 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_misc). -module(emqx_misc).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").

View File

@ -14,11 +14,11 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_mod_sup). -module(emqx_mod_sup).
-behaviour(supervisor). -behaviour(supervisor).
-include("emqttd.hrl"). -include("emqx.hrl").
%% API %% API
-export([start_link/0, start_child/1, start_child/2, stop_child/1]). -export([start_link/0, start_child/1, start_child/2, stop_child/1]).

View File

@ -41,13 +41,13 @@
%% %%
%% @end %% @end
-module(emqttd_mqueue). -module(emqx_mqueue).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
-include("emqttd_protocol.hrl"). -include("emqx_mqtt.hrl").
-import(proplists, [get_value/3]). -import(proplists, [get_value/3]).
@ -58,7 +58,7 @@
-define(HIGH_WM, 0.6). -define(HIGH_WM, 0.6).
-define(PQUEUE, priority_queue). -define(PQUEUE, emqx_pqueue).
-type(priority() :: {iolist(), pos_integer()}). -type(priority() :: {iolist(), pos_integer()}).

View File

@ -14,7 +14,9 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_net). -module(emqx_net).
-author("Feng Lee <feng@emqtt.io>").
-include_lib("kernel/include/inet.hrl"). -include_lib("kernel/include/inet.hrl").

View File

@ -14,13 +14,13 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_packet). -module(emqx_packet).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
-include("emqttd_protocol.hrl"). -include("emqx_mqtt.hrl").
%% API %% API
-export([protocol_name/1, type_name/1, connack_name/1]). -export([protocol_name/1, type_name/1, connack_name/1]).
@ -70,18 +70,18 @@ format_variable(Variable, Payload) ->
io_lib:format("~s, Payload=~p", [format_variable(Variable), Payload]). io_lib:format("~s, Payload=~p", [format_variable(Variable), Payload]).
format_variable(#mqtt_packet_connect{ format_variable(#mqtt_packet_connect{
proto_ver = ProtoVer, proto_ver = ProtoVer,
proto_name = ProtoName, proto_name = ProtoName,
will_retain = WillRetain, will_retain = WillRetain,
will_qos = WillQoS, will_qos = WillQoS,
will_flag = WillFlag, will_flag = WillFlag,
clean_sess = CleanSess, clean_sess = CleanSess,
keep_alive = KeepAlive, keep_alive = KeepAlive,
client_id = ClientId, client_id = ClientId,
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, format_password(Password)], Args = [ClientId, ProtoName, ProtoVer, CleanSess, KeepAlive, Username, format_password(Password)],
{Format1, Args1} = if {Format1, Args1} = if

View File

@ -15,13 +15,13 @@
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @doc MQTT Packet Parser %% @doc MQTT Packet Parser
-module(emqttd_parser). -module(emqx_parser).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
-include("emqttd_protocol.hrl"). -include("emqx_mqtt.hrl").
%% API %% API
-export([initial_state/0, initial_state/1, parse/2]). -export([initial_state/0, initial_state/1, parse/2]).

View File

@ -14,11 +14,11 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_plugins). -module(emqx_plugins).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
-export([init/0]). -export([init/0]).
@ -31,7 +31,7 @@
%% @doc Init plugins' config %% @doc Init plugins' config
-spec(init() -> ok). -spec(init() -> ok).
init() -> init() ->
case emqttd:env(plugins_etc_dir) of case emqx:env(plugins_etc_dir) of
{ok, PluginsEtc} -> {ok, PluginsEtc} ->
CfgFiles = [filename:join(PluginsEtc, File) || CfgFiles = [filename:join(PluginsEtc, File) ||
File <- filelib:wildcard("*.config", PluginsEtc)], File <- filelib:wildcard("*.config", PluginsEtc)],
@ -49,7 +49,7 @@ init_config(CfgFile) ->
%% @doc Load all plugins when the broker started. %% @doc Load all plugins when the broker started.
-spec(load() -> list() | {error, any()}). -spec(load() -> list() | {error, any()}).
load() -> load() ->
case emqttd:env(plugins_loaded_file) of case emqx:env(plugins_loaded_file) of
{ok, File} -> {ok, File} ->
ensure_file(File), ensure_file(File),
with_loaded_file(File, fun(Names) -> load_plugins(Names, false) end); with_loaded_file(File, fun(Names) -> load_plugins(Names, false) end);
@ -82,7 +82,7 @@ load_plugins(Names, Persistent) ->
%% @doc Unload all plugins before broker stopped. %% @doc Unload all plugins before broker stopped.
-spec(unload() -> list() | {error, any()}). -spec(unload() -> list() | {error, any()}).
unload() -> unload() ->
case emqttd:env(plugins_loaded_file) of case emqx:env(plugins_loaded_file) of
{ok, File} -> {ok, File} ->
with_loaded_file(File, fun stop_plugins/1); with_loaded_file(File, fun stop_plugins/1);
undefined -> undefined ->
@ -96,7 +96,7 @@ stop_plugins(Names) ->
%% @doc List all available plugins %% @doc List all available plugins
-spec(list() -> [mqtt_plugin()]). -spec(list() -> [mqtt_plugin()]).
list() -> list() ->
case emqttd:env(plugins_etc_dir) of case emqx:env(plugins_etc_dir) of
{ok, PluginsEtc} -> {ok, PluginsEtc} ->
CfgFiles = filelib:wildcard("*.{conf,config}", PluginsEtc), CfgFiles = filelib:wildcard("*.{conf,config}", PluginsEtc),
Plugins = [plugin(CfgFile) || CfgFile <- CfgFiles], Plugins = [plugin(CfgFile) || CfgFile <- CfgFiles],
@ -251,7 +251,7 @@ plugin_unloaded(Name, true) ->
end. end.
read_loaded() -> read_loaded() ->
case emqttd:env(plugins_loaded_file) of case emqx:env(plugins_loaded_file) of
{ok, File} -> read_loaded(File); {ok, File} -> read_loaded(File);
undefined -> {error, not_found} undefined -> {error, not_found}
end. end.
@ -259,7 +259,7 @@ read_loaded() ->
read_loaded(File) -> file:consult(File). read_loaded(File) -> file:consult(File).
write_loaded(AppNames) -> write_loaded(AppNames) ->
{ok, File} = emqttd:env(plugins_loaded_file), {ok, File} = emqx:env(plugins_loaded_file),
case file:open(File, [binary, write]) of case file:open(File, [binary, write]) of
{ok, Fd} -> {ok, Fd} ->
lists:foreach(fun(Name) -> lists:foreach(fun(Name) ->

View File

@ -14,7 +14,7 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_pmon). -module(emqx_pmon).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").

View File

@ -15,7 +15,7 @@
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @doc Common Pool Supervisor %% @doc Common Pool Supervisor
-module(emqttd_pool_sup). -module(emqx_pool_sup).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
@ -41,7 +41,7 @@ start_link(Pool, Type, MFA) ->
Schedulers = erlang:system_info(schedulers), Schedulers = erlang:system_info(schedulers),
start_link(Pool, Type, Schedulers, MFA). start_link(Pool, Type, Schedulers, MFA).
-spec(start_link(atom(), atom(), pos_integer(), mfa()) -> {ok, pid()} | {error, any()}). -spec(start_link(atom() | tuple(), atom(), pos_integer(), mfa()) -> {ok, pid()} | {error, any()}).
start_link(Pool, Type, Size, MFA) -> start_link(Pool, Type, Size, MFA) ->
supervisor:start_link(?MODULE, [Pool, Type, Size, MFA]). supervisor:start_link(?MODULE, [Pool, Type, Size, MFA]).

View File

@ -14,11 +14,13 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_pooler). -module(emqx_pooler).
-author("Feng Lee <feng@emqtt.io>").
-behaviour(gen_server). -behaviour(gen_server).
-include("emqttd_internal.hrl"). -include("emqx_internal.hrl").
%% Start the pool supervisor %% Start the pool supervisor
-export([start_link/0]). -export([start_link/0]).
@ -34,7 +36,7 @@
%% @doc Start Pooler Supervisor. %% @doc Start Pooler Supervisor.
start_link() -> start_link() ->
emqttd_pool_sup:start_link(pooler, random, {?MODULE, start_link, []}). emqx_pool_sup:start_link(pooler, random, {?MODULE, start_link, []}).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% API %% API

View File

@ -37,46 +37,27 @@
%% calls into the same function knowing that ordinary queues represent %% calls into the same function knowing that ordinary queues represent
%% a base case. %% a base case.
-module(priority_queue). -module(emqx_pqueue).
-export([new/0, is_queue/1, is_empty/1, len/1, plen/2, to_list/1, from_list/1, -export([new/0, is_queue/1, is_empty/1, len/1, plen/2, to_list/1, from_list/1,
in/2, in/3, out/1, out/2, out_p/1, join/2, filter/2, fold/3, highest/1]). in/2, in/3, out/1, out/2, out_p/1, join/2, filter/2, fold/3, highest/1]).
%%---------------------------------------------------------------------------- %%----------------------------------------------------------------------------
-ifdef(use_specs).
-type(q() :: pqueue()).
-type(priority() :: integer() | 'infinity'). -type(priority() :: integer() | 'infinity').
-type(squeue() :: {queue, [any()], [any()], non_neg_integer()}). -type(squeue() :: {queue, [any()], [any()], non_neg_integer()}).
-type(pqueue() :: squeue() | {pqueue, [{priority(), squeue()}]}). -type(pqueue() :: squeue() | {pqueue, [{priority(), squeue()}]}).
-type(q() :: pqueue()).
-export_type([q/0]). -export_type([q/0]).
-spec(new/0 :: () -> pqueue()).
-spec(is_queue/1 :: (any()) -> boolean()).
-spec(is_empty/1 :: (pqueue()) -> boolean()).
-spec(len/1 :: (pqueue()) -> non_neg_integer()).
-spec(plen/2 :: (priority(), pqueue()) -> non_neg_integer()).
-spec(to_list/1 :: (pqueue()) -> [{priority(), any()}]).
-spec(from_list/1 :: ([{priority(), any()}]) -> pqueue()).
-spec(in/2 :: (any(), pqueue()) -> pqueue()).
-spec(in/3 :: (any(), priority(), pqueue()) -> pqueue()).
-spec(out/1 :: (pqueue()) -> {empty | {value, any()}, pqueue()}).
-spec(out_p/1 :: (pqueue()) -> {empty | {value, any(), priority()}, pqueue()}).
-spec(join/2 :: (pqueue(), pqueue()) -> pqueue()).
-spec(filter/2 :: (fun ((any()) -> boolean()), pqueue()) -> pqueue()).
-spec(fold/3 ::
(fun ((any(), priority(), A) -> A), A, pqueue()) -> A).
-spec(highest/1 :: (pqueue()) -> priority() | 'empty').
-endif.
%%---------------------------------------------------------------------------- %%----------------------------------------------------------------------------
-spec(new() -> pqueue()).
new() -> new() ->
{queue, [], [], 0}. {queue, [], [], 0}.
-spec(is_queue(any()) -> boolean()).
is_queue({queue, R, F, L}) when is_list(R), is_list(F), is_integer(L) -> is_queue({queue, R, F, L}) when is_list(R), is_list(F), is_integer(L) ->
true; true;
is_queue({pqueue, Queues}) when is_list(Queues) -> is_queue({pqueue, Queues}) when is_list(Queues) ->
@ -86,16 +67,19 @@ is_queue({pqueue, Queues}) when is_list(Queues) ->
is_queue(_) -> is_queue(_) ->
false. false.
-spec(is_empty(pqueue()) -> boolean()).
is_empty({queue, [], [], 0}) -> is_empty({queue, [], [], 0}) ->
true; true;
is_empty(_) -> is_empty(_) ->
false. false.
-spec(len(pqueue()) -> non_neg_integer()).
len({queue, _R, _F, L}) -> len({queue, _R, _F, L}) ->
L; L;
len({pqueue, Queues}) -> len({pqueue, Queues}) ->
lists:sum([len(Q) || {_, Q} <- Queues]). lists:sum([len(Q) || {_, Q} <- Queues]).
-spec(plen(priority(), pqueue()) -> non_neg_integer()).
plen(0, {queue, _R, _F, L}) -> plen(0, {queue, _R, _F, L}) ->
L; L;
plen(_, {queue, _R, _F, _}) -> plen(_, {queue, _R, _F, _}) ->
@ -106,18 +90,22 @@ plen(P, {pqueue, Queues}) ->
false -> 0 false -> 0
end. end.
-spec(to_list(pqueue()) -> [{priority(), any()}]).
to_list({queue, In, Out, _Len}) when is_list(In), is_list(Out) -> to_list({queue, In, Out, _Len}) when is_list(In), is_list(Out) ->
[{0, V} || V <- Out ++ lists:reverse(In, [])]; [{0, V} || V <- Out ++ lists:reverse(In, [])];
to_list({pqueue, Queues}) -> to_list({pqueue, Queues}) ->
[{maybe_negate_priority(P), V} || {P, Q} <- Queues, [{maybe_negate_priority(P), V} || {P, Q} <- Queues,
{0, V} <- to_list(Q)]. {0, V} <- to_list(Q)].
-spec(from_list([{priority(), any()}]) -> pqueue()).
from_list(L) -> from_list(L) ->
lists:foldl(fun ({P, E}, Q) -> in(E, P, Q) end, new(), L). lists:foldl(fun ({P, E}, Q) -> in(E, P, Q) end, new(), L).
-spec(in(any(), pqueue()) -> pqueue()).
in(Item, Q) -> in(Item, Q) ->
in(Item, 0, Q). in(Item, 0, Q).
-spec(in(any(), priority(), pqueue()) -> pqueue()).
in(X, 0, {queue, [_] = In, [], 1}) -> in(X, 0, {queue, [_] = In, [], 1}) ->
{queue, [X], In, 2}; {queue, [X], In, 2};
in(X, 0, {queue, In, Out, Len}) when is_list(In), is_list(Out) -> in(X, 0, {queue, In, Out, Len}) when is_list(In), is_list(Out) ->
@ -143,6 +131,7 @@ in(X, Priority, {pqueue, Queues}) ->
end end
end}. end}.
-spec(out(pqueue()) -> {empty | {value, any()}, pqueue()}).
out({queue, [], [], 0} = Q) -> out({queue, [], [], 0} = Q) ->
{empty, Q}; {empty, Q};
out({queue, [V], [], 1}) -> out({queue, [V], [], 1}) ->
@ -166,6 +155,7 @@ out({pqueue, [{P, Q} | Queues]}) ->
end, end,
{R, NewQ}. {R, NewQ}.
-spec(out_p(pqueue()) -> {empty | {value, any(), priority()}, pqueue()}).
out_p({queue, _, _, _} = Q) -> add_p(out(Q), 0); out_p({queue, _, _, _} = Q) -> add_p(out(Q), 0);
out_p({pqueue, [{P, _} | _]} = Q) -> add_p(out(Q), maybe_negate_priority(P)). out_p({pqueue, [{P, _} | _]} = Q) -> add_p(out(Q), maybe_negate_priority(P)).
@ -196,6 +186,7 @@ add_p(R, P) -> case R of
{{value, V}, Q} -> {{value, V, P}, Q} {{value, V}, Q} -> {{value, V, P}, Q}
end. end.
-spec(join(pqueue(), pqueue()) -> pqueue()).
join(A, {queue, [], [], 0}) -> join(A, {queue, [], [], 0}) ->
A; A;
join({queue, [], [], 0}, B) -> join({queue, [], [], 0}, B) ->
@ -234,6 +225,7 @@ merge([{PA, A}|As], Bs = [{PB, _}|_], Acc) when PA < PB orelse PA == infinity ->
merge(As = [{_, _}|_], [{PB, B}|Bs], Acc) -> merge(As = [{_, _}|_], [{PB, B}|Bs], Acc) ->
merge(As, Bs, [ {PB, B} | Acc ]). merge(As, Bs, [ {PB, B} | Acc ]).
-spec(filter(fun ((any()) -> boolean()), pqueue()) -> pqueue()).
filter(Pred, Q) -> fold(fun(V, P, Acc) -> filter(Pred, Q) -> fold(fun(V, P, Acc) ->
case Pred(V) of case Pred(V) of
true -> in(V, P, Acc); true -> in(V, P, Acc);
@ -241,11 +233,13 @@ filter(Pred, Q) -> fold(fun(V, P, Acc) ->
end end
end, new(), Q). end, new(), Q).
-spec(fold(fun ((any(), priority(), A) -> A), A, pqueue()) -> A).
fold(Fun, Init, Q) -> case out_p(Q) of fold(Fun, Init, Q) -> case out_p(Q) of
{empty, _Q} -> Init; {empty, _Q} -> Init;
{{value, V, P}, Q1} -> fold(Fun, Fun(V, P, Init), Q1) {{value, V, P}, Q1} -> fold(Fun, Fun(V, P, Init), Q1)
end. end.
-spec(highest(pqueue()) -> priority() | 'empty').
highest({queue, [], [], 0}) -> empty; highest({queue, [], [], 0}) -> empty;
highest({queue, _, _, _}) -> 0; highest({queue, _, _, _}) -> 0;
highest({pqueue, [{P, _} | _]}) -> maybe_negate_priority(P). highest({pqueue, [{P, _} | _]}) -> maybe_negate_priority(P).
@ -257,3 +251,4 @@ r2f([X,Y|R], L) -> {queue, [X,Y], lists:reverse(R, []), L}.
maybe_negate_priority(infinity) -> infinity; maybe_negate_priority(infinity) -> infinity;
maybe_negate_priority(P) -> -P. maybe_negate_priority(P) -> -P.

View File

@ -14,15 +14,15 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_protocol). -module(emqx_protocol).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
-include("emqttd_protocol.hrl"). -include("emqx_mqtt.hrl").
-include("emqttd_internal.hrl"). -include("emqx_internal.hrl").
-import(proplists, [get_value/2, get_value/3]). -import(proplists, [get_value/2, get_value/3]).
@ -42,14 +42,15 @@
%% ws_initial_headers: Headers from first HTTP request for WebSocket Client. %% ws_initial_headers: Headers from first HTTP request for WebSocket Client.
-record(proto_state, {peername, sendfun, connected = false, client_id, client_pid, -record(proto_state, {peername, sendfun, connected = false, client_id, client_pid,
clean_sess, proto_ver, proto_name, username, is_superuser, clean_sess, proto_ver, proto_name, username, is_superuser,
will_msg, keepalive, keepalive_backoff, max_clientid_len, will_msg, keepalive, max_clientid_len, session, stats_data,
session, stats_data, mountpoint, ws_initial_headers, keepalive_backoff, peercert_username, ws_initial_headers,
connected_at}). mountpoint, connected_at}).
-type(proto_state() :: #proto_state{}). -type(proto_state() :: #proto_state{}).
-define(INFO_KEYS, [client_id, username, clean_sess, proto_ver, proto_name, -define(INFO_KEYS, [client_id, username, clean_sess, proto_ver, proto_name,
keepalive, will_msg, ws_initial_headers, mountpoint, connected_at]). keepalive, will_msg, ws_initial_headers, mountpoint,
peercert_username, connected_at]).
-define(STATS_KEYS, [recv_pkt, recv_msg, send_pkt, send_msg]). -define(STATS_KEYS, [recv_pkt, recv_msg, send_pkt, send_msg]).
@ -68,6 +69,7 @@ init(Peername, SendFun, Opts) ->
max_clientid_len = MaxLen, max_clientid_len = MaxLen,
is_superuser = false, is_superuser = false,
client_pid = self(), client_pid = self(),
peercert_username = false,
ws_initial_headers = WsInitialHeaders, ws_initial_headers = WsInitialHeaders,
keepalive_backoff = Backoff, keepalive_backoff = Backoff,
stats_data = #proto_stats{enable_stats = EnableStats}}. stats_data = #proto_stats{enable_stats = EnableStats}}.
@ -79,9 +81,28 @@ enrich_opt([], _Conn, State) ->
State; State;
enrich_opt([{mountpoint, MountPoint} | ConnOpts], Conn, State) -> enrich_opt([{mountpoint, MountPoint} | ConnOpts], Conn, State) ->
enrich_opt(ConnOpts, Conn, State#proto_state{mountpoint = MountPoint}); enrich_opt(ConnOpts, Conn, State#proto_state{mountpoint = MountPoint});
enrich_opt([{peer_cert_as_username, N} | ConnOpts], Conn, State) ->
case Conn:type() of
ssl -> enrich_opt(ConnOpts, Conn, State#proto_state{
peercert_username = peercert_username(N, Conn:peercert())});
_ -> enrich_opt(ConnOpts, Conn, State)
end;
enrich_opt([_ | ConnOpts], Conn, State) -> enrich_opt([_ | ConnOpts], Conn, State) ->
enrich_opt(ConnOpts, Conn, State). enrich_opt(ConnOpts, Conn, State).
peercert_username(cn, Cert) ->
case emqx_ssl:peer_cert_common_name(Cert) of
not_found -> undefined;
CN -> iolist_to_binary(CN)
end;
peercert_username(dn, Cert) ->
iolist_to_binary(emqx_ssl:peer_cert_subject(Cert)).
repl_username_with_peercert(State = #proto_state{peercert_username = false}) ->
State;
repl_username_with_peercert(State = #proto_state{peercert_username = PeerCert}) ->
State#proto_state{username = PeerCert}.
info(ProtoState) -> info(ProtoState) ->
?record_to_proplist(proto_state, ProtoState, ?INFO_KEYS). ?record_to_proplist(proto_state, ProtoState, ?INFO_KEYS).
@ -150,9 +171,9 @@ subscribe(RawTopicTable, ProtoState = #proto_state{client_id = ClientId,
username = Username, username = Username,
session = Session}) -> session = Session}) ->
TopicTable = parse_topic_table(RawTopicTable), TopicTable = parse_topic_table(RawTopicTable),
case emqttd_hooks:run('client.subscribe', [ClientId, Username], TopicTable) of case emqx_hooks:run('client.subscribe', [ClientId, Username], TopicTable) of
{ok, TopicTable1} -> {ok, TopicTable1} ->
emqttd_session:subscribe(Session, TopicTable1); emqx_session:subscribe(Session, TopicTable1);
{stop, _} -> {stop, _} ->
ok ok
end, end,
@ -161,9 +182,9 @@ subscribe(RawTopicTable, ProtoState = #proto_state{client_id = ClientId,
unsubscribe(RawTopics, ProtoState = #proto_state{client_id = ClientId, unsubscribe(RawTopics, ProtoState = #proto_state{client_id = ClientId,
username = Username, username = Username,
session = Session}) -> session = Session}) ->
case emqttd_hooks:run('client.unsubscribe', [ClientId, Username], parse_topics(RawTopics)) of case emqx_hooks:run('client.unsubscribe', [ClientId, Username], parse_topics(RawTopics)) of
{ok, TopicTable} -> {ok, TopicTable} ->
emqttd_session:unsubscribe(Session, TopicTable); emqx_session:unsubscribe(Session, TopicTable);
{stop, _} -> {stop, _} ->
ok ok
end, end,
@ -182,14 +203,15 @@ process(?CONNECT_PACKET(Var), State0) ->
keep_alive = KeepAlive, keep_alive = KeepAlive,
client_id = ClientId} = Var, client_id = ClientId} = Var,
State1 = State0#proto_state{proto_ver = ProtoVer, State1 = repl_username_with_peercert(
proto_name = ProtoName, State0#proto_state{proto_ver = ProtoVer,
username = Username, proto_name = ProtoName,
client_id = ClientId, username = Username,
clean_sess = CleanSess, client_id = ClientId,
keepalive = KeepAlive, clean_sess = CleanSess,
will_msg = willmsg(Var, State0), keepalive = KeepAlive,
connected_at = os:timestamp()}, will_msg = willmsg(Var, State0),
connected_at = os:timestamp()}),
{ReturnCode1, SessPresent, State3} = {ReturnCode1, SessPresent, State3} =
case validate_connect(Var, State1) of case validate_connect(Var, State1) of
@ -200,10 +222,10 @@ process(?CONNECT_PACKET(Var), State0) ->
State2 = maybe_set_clientid(State1), State2 = maybe_set_clientid(State1),
%% Start session %% Start session
case emqttd_sm:start_session(CleanSess, {clientid(State2), Username}) of case emqx_sm:start_session(CleanSess, {clientid(State2), Username}) of
{ok, Session, SP} -> {ok, Session, SP} ->
%% Register the client %% Register the client
emqttd_cm:reg(client(State2)), emqx_cm:reg(client(State2)),
%% Start keepalive %% Start keepalive
start_keepalive(KeepAlive, State2), start_keepalive(KeepAlive, State2),
%% Emit Stats %% Emit Stats
@ -221,7 +243,7 @@ process(?CONNECT_PACKET(Var), State0) ->
{ReturnCode, false, State1} {ReturnCode, false, State1}
end, end,
%% Run hooks %% Run hooks
emqttd_hooks:run('client.connected', [ReturnCode1], client(State3)), emqx_hooks:run('client.connected', [ReturnCode1], client(State3)),
%% Send connack %% Send connack
send(?CONNACK_PACKET(ReturnCode1, sp(SessPresent)), State3), send(?CONNACK_PACKET(ReturnCode1, sp(SessPresent)), State3),
%% stop if authentication failure %% stop if authentication failure
@ -235,19 +257,19 @@ process(Packet = ?PUBLISH_PACKET(_Qos, Topic, _PacketId, _Payload), State = #pro
{ok, State}; {ok, State};
process(?PUBACK_PACKET(?PUBACK, PacketId), State = #proto_state{session = Session}) -> process(?PUBACK_PACKET(?PUBACK, PacketId), State = #proto_state{session = Session}) ->
emqttd_session:puback(Session, PacketId), emqx_session:puback(Session, PacketId),
{ok, State}; {ok, State};
process(?PUBACK_PACKET(?PUBREC, PacketId), State = #proto_state{session = Session}) -> process(?PUBACK_PACKET(?PUBREC, PacketId), State = #proto_state{session = Session}) ->
emqttd_session:pubrec(Session, PacketId), emqx_session:pubrec(Session, PacketId),
send(?PUBREL_PACKET(PacketId), State); send(?PUBREL_PACKET(PacketId), State);
process(?PUBACK_PACKET(?PUBREL, PacketId), State = #proto_state{session = Session}) -> process(?PUBACK_PACKET(?PUBREL, PacketId), State = #proto_state{session = Session}) ->
emqttd_session:pubrel(Session, PacketId), emqx_session:pubrel(Session, PacketId),
send(?PUBACK_PACKET(?PUBCOMP, PacketId), State); send(?PUBACK_PACKET(?PUBCOMP, PacketId), State);
process(?PUBACK_PACKET(?PUBCOMP, PacketId), State = #proto_state{session = Session})-> process(?PUBACK_PACKET(?PUBCOMP, PacketId), State = #proto_state{session = Session})->
emqttd_session:pubcomp(Session, PacketId), {ok, State}; emqx_session:pubcomp(Session, PacketId), {ok, State};
%% Protect from empty topic table %% Protect from empty topic table
process(?SUBSCRIBE_PACKET(PacketId, []), State) -> process(?SUBSCRIBE_PACKET(PacketId, []), State) ->
@ -270,9 +292,9 @@ process(?SUBSCRIBE_PACKET(PacketId, RawTopicTable),
?LOG(error, "Cannot SUBSCRIBE ~p for ACL Deny", [TopicTable], State), ?LOG(error, "Cannot SUBSCRIBE ~p for ACL Deny", [TopicTable], State),
send(?SUBACK_PACKET(PacketId, [16#80 || _ <- TopicTable]), State); send(?SUBACK_PACKET(PacketId, [16#80 || _ <- TopicTable]), State);
false -> false ->
case emqttd_hooks:run('client.subscribe', [ClientId, Username], TopicTable) of case emqx_hooks:run('client.subscribe', [ClientId, Username], TopicTable) of
{ok, TopicTable1} -> {ok, TopicTable1} ->
emqttd_session:subscribe(Session, PacketId, mount(MountPoint, TopicTable1)), emqx_session:subscribe(Session, PacketId, mount(MountPoint, TopicTable1)),
{ok, State}; {ok, State};
{stop, _} -> {stop, _} ->
{ok, State} {ok, State}
@ -288,9 +310,9 @@ process(?UNSUBSCRIBE_PACKET(PacketId, RawTopics),
username = Username, username = Username,
mountpoint = MountPoint, mountpoint = MountPoint,
session = Session}) -> session = Session}) ->
case emqttd_hooks:run('client.unsubscribe', [ClientId, Username], parse_topics(RawTopics)) of case emqx_hooks:run('client.unsubscribe', [ClientId, Username], parse_topics(RawTopics)) of
{ok, TopicTable} -> {ok, TopicTable} ->
emqttd_session:unsubscribe(Session, mount(MountPoint, TopicTable)); emqx_session:unsubscribe(Session, mount(MountPoint, TopicTable));
{stop, _} -> {stop, _} ->
ok ok
end, end,
@ -308,8 +330,8 @@ publish(Packet = ?PUBLISH_PACKET(?QOS_0, _PacketId),
username = Username, username = Username,
mountpoint = MountPoint, mountpoint = MountPoint,
session = Session}) -> session = Session}) ->
Msg = emqttd_message:from_packet(Username, ClientId, Packet), Msg = emqx_message:from_packet(Username, ClientId, Packet),
emqttd_session:publish(Session, mount(MountPoint, Msg)); emqx_session:publish(Session, mount(MountPoint, Msg));
publish(Packet = ?PUBLISH_PACKET(?QOS_1, _PacketId), State) -> publish(Packet = ?PUBLISH_PACKET(?QOS_1, _PacketId), State) ->
with_puback(?PUBACK, Packet, State); with_puback(?PUBACK, Packet, State);
@ -322,8 +344,8 @@ with_puback(Type, Packet = ?PUBLISH_PACKET(_Qos, PacketId),
username = Username, username = Username,
mountpoint = MountPoint, mountpoint = MountPoint,
session = Session}) -> session = Session}) ->
Msg = emqttd_message:from_packet(Username, ClientId, Packet), Msg = emqx_message:from_packet(Username, ClientId, Packet),
case emqttd_session:publish(Session, mount(MountPoint, Msg)) of case emqx_session:publish(Session, mount(MountPoint, Msg)) of
ok -> ok ->
send(?PUBACK_PACKET(Type, PacketId), State); send(?PUBACK_PACKET(Type, PacketId), State);
{error, Error} -> {error, Error} ->
@ -335,22 +357,22 @@ send(Msg, State = #proto_state{client_id = ClientId,
username = Username, username = Username,
mountpoint = MountPoint}) mountpoint = MountPoint})
when is_record(Msg, mqtt_message) -> when is_record(Msg, mqtt_message) ->
emqttd_hooks:run('message.delivered', [ClientId, Username], Msg), emqx_hooks:run('message.delivered', [ClientId, Username], Msg),
send(emqttd_message:to_packet(unmount(MountPoint, Msg)), State); send(emqx_message:to_packet(unmount(MountPoint, Msg)), State);
send(Packet = ?PACKET(Type), send(Packet = ?PACKET(Type),
State = #proto_state{sendfun = SendFun, stats_data = Stats}) -> State = #proto_state{sendfun = SendFun, stats_data = Stats}) ->
trace(send, Packet, State), trace(send, Packet, State),
emqttd_metrics:sent(Packet), emqx_metrics:sent(Packet),
SendFun(Packet), SendFun(Packet),
Stats1 = inc_stats(send, Type, Stats), Stats1 = inc_stats(send, Type, Stats),
{ok, State#proto_state{stats_data = Stats1}}. {ok, State#proto_state{stats_data = Stats1}}.
trace(recv, Packet, ProtoState) -> trace(recv, Packet, ProtoState) ->
?LOG(debug, "RECV ~s", [emqttd_packet:format(Packet)], ProtoState); ?LOG(info, "RECV ~s", [emqx_packet:format(Packet)], ProtoState);
trace(send, Packet, ProtoState) -> trace(send, Packet, ProtoState) ->
?LOG(debug, "SEND ~s", [emqttd_packet:format(Packet)], ProtoState). ?LOG(info, "SEND ~s", [emqx_packet:format(Packet)], ProtoState).
inc_stats(_Direct, _Type, Stats = #proto_stats{enable_stats = false}) -> inc_stats(_Direct, _Type, Stats = #proto_stats{enable_stats = false}) ->
Stats; Stats;
@ -381,20 +403,20 @@ shutdown(_Error, #proto_state{client_id = undefined}) ->
shutdown(conflict, #proto_state{client_id = _ClientId}) -> shutdown(conflict, #proto_state{client_id = _ClientId}) ->
%% let it down %% let it down
%% emqttd_cm:unreg(ClientId); %% emqx_cm:unreg(ClientId);
ignore; ignore;
shutdown(Error, State = #proto_state{will_msg = WillMsg}) -> shutdown(Error, State = #proto_state{will_msg = WillMsg}) ->
?LOG(debug, "Shutdown for ~p", [Error], State), ?LOG(info, "Shutdown for ~p", [Error], State),
Client = client(State), Client = client(State),
send_willmsg(Client, WillMsg), send_willmsg(Client, WillMsg),
emqttd_hooks:run('client.disconnected', [Error], Client), emqx_hooks:run('client.disconnected', [Error], Client),
%% let it down %% let it down
%% emqttd_cm:unreg(ClientId). %% emqx_cm:unreg(ClientId).
ok. ok.
willmsg(Packet, #proto_state{mountpoint = MountPoint}) when is_record(Packet, mqtt_packet_connect) -> willmsg(Packet, #proto_state{mountpoint = MountPoint}) when is_record(Packet, mqtt_packet_connect) ->
case emqttd_message:from_packet(Packet) of case emqx_message:from_packet(Packet) of
undefined -> undefined; undefined -> undefined;
Msg -> mount(MountPoint, Msg) Msg -> mount(MountPoint, Msg)
end. end.
@ -402,8 +424,8 @@ willmsg(Packet, #proto_state{mountpoint = MountPoint}) when is_record(Packet, mq
%% Generate a client if if nulll %% Generate a client if if nulll
maybe_set_clientid(State = #proto_state{client_id = NullId}) maybe_set_clientid(State = #proto_state{client_id = NullId})
when NullId =:= undefined orelse NullId =:= <<>> -> when NullId =:= undefined orelse NullId =:= <<>> ->
{_, NPid, _} = emqttd_guid:new(), {_, NPid, _} = emqx_guid:new(),
ClientId = iolist_to_binary(["emqttd_", integer_to_list(NPid)]), ClientId = iolist_to_binary(["emqx_", integer_to_list(NPid)]),
State#proto_state{client_id = ClientId}; State#proto_state{client_id = ClientId};
maybe_set_clientid(State) -> maybe_set_clientid(State) ->
@ -412,7 +434,7 @@ maybe_set_clientid(State) ->
send_willmsg(_Client, undefined) -> send_willmsg(_Client, undefined) ->
ignore; ignore;
send_willmsg(#mqtt_client{client_id = ClientId, username = Username}, WillMsg) -> send_willmsg(#mqtt_client{client_id = ClientId, username = Username}, WillMsg) ->
emqttd:publish(WillMsg#mqtt_message{from = {ClientId, Username}}). emqx_server:publish(WillMsg#mqtt_message{from = {ClientId, Username}}).
start_keepalive(0, _State) -> ignore; start_keepalive(0, _State) -> ignore;
@ -463,7 +485,7 @@ validate_clientid(#mqtt_packet_connect{proto_ver = ProtoVer,
false. false.
validate_packet(?PUBLISH_PACKET(_Qos, Topic, _PacketId, _Payload)) -> validate_packet(?PUBLISH_PACKET(_Qos, Topic, _PacketId, _Payload)) ->
case emqttd_topic:validate({name, Topic}) of case emqx_topic:validate({name, Topic}) of
true -> ok; true -> ok;
false -> {error, badtopic} false -> {error, badtopic}
end; end;
@ -483,7 +505,7 @@ validate_topics(_Type, []) ->
validate_topics(Type, TopicTable = [{_Topic, _Qos}|_]) validate_topics(Type, TopicTable = [{_Topic, _Qos}|_])
when Type =:= name orelse Type =:= filter -> when Type =:= name orelse Type =:= filter ->
Valid = fun(Topic, Qos) -> Valid = fun(Topic, Qos) ->
emqttd_topic:validate({Type, Topic}) and validate_qos(Qos) emqx_topic:validate({Type, Topic}) and validate_qos(Qos)
end, end,
case [Topic || {Topic, Qos} <- TopicTable, not Valid(Topic, Qos)] of case [Topic || {Topic, Qos} <- TopicTable, not Valid(Topic, Qos)] of
[] -> ok; [] -> ok;
@ -491,7 +513,7 @@ validate_topics(Type, TopicTable = [{_Topic, _Qos}|_])
end; end;
validate_topics(Type, Topics = [Topic0|_]) when is_binary(Topic0) -> validate_topics(Type, Topics = [Topic0|_]) when is_binary(Topic0) ->
case [Topic || Topic <- Topics, not emqttd_topic:validate({Type, Topic})] of case [Topic || Topic <- Topics, not emqx_topic:validate({Type, Topic})] of
[] -> ok; [] -> ok;
_ -> {error, badtopic} _ -> {error, badtopic}
end. end.
@ -505,15 +527,15 @@ validate_qos(_) ->
parse_topic_table(TopicTable) -> parse_topic_table(TopicTable) ->
lists:map(fun({Topic0, Qos}) -> lists:map(fun({Topic0, Qos}) ->
{Topic, Opts} = emqttd_topic:parse(Topic0), {Topic, Opts} = emqx_topic:parse(Topic0),
{Topic, [{qos, Qos}|Opts]} {Topic, [{qos, Qos}|Opts]}
end, TopicTable). end, TopicTable).
parse_topics(Topics) -> parse_topics(Topics) ->
[emqttd_topic:parse(Topic) || Topic <- Topics]. [emqx_topic:parse(Topic) || Topic <- Topics].
authenticate(Client, Password) -> authenticate(Client, Password) ->
case emqttd_access_control:auth(Client, Password) of case emqx_access_control:auth(Client, Password) of
ok -> {ok, false}; ok -> {ok, false};
{ok, IsSuper} -> {ok, IsSuper}; {ok, IsSuper} -> {ok, IsSuper};
{error, Error} -> {error, Error} {error, Error} -> {error, Error}
@ -521,20 +543,20 @@ authenticate(Client, Password) ->
%% PUBLISH ACL is cached in process dictionary. %% PUBLISH ACL is cached in process dictionary.
check_acl(publish, Topic, Client) -> check_acl(publish, Topic, Client) ->
IfCache = emqttd:env(cache_acl, true), IfCache = emqx:env(cache_acl, true),
case {IfCache, get({acl, publish, Topic})} of case {IfCache, get({acl, publish, Topic})} of
{true, undefined} -> {true, undefined} ->
AllowDeny = emqttd_access_control:check_acl(Client, publish, Topic), AllowDeny = emqx_access_control:check_acl(Client, publish, Topic),
put({acl, publish, Topic}, AllowDeny), put({acl, publish, Topic}, AllowDeny),
AllowDeny; AllowDeny;
{true, AllowDeny} -> {true, AllowDeny} ->
AllowDeny; AllowDeny;
{false, _} -> {false, _} ->
emqttd_access_control:check_acl(Client, publish, Topic) emqx_access_control:check_acl(Client, publish, Topic)
end; end;
check_acl(subscribe, Topic, Client) -> check_acl(subscribe, Topic, Client) ->
emqttd_access_control:check_acl(Client, subscribe, Topic). emqx_access_control:check_acl(Client, subscribe, Topic).
sp(true) -> 1; sp(true) -> 1;
sp(false) -> 0. sp(false) -> 0.

View File

@ -14,15 +14,15 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_pubsub). -module(emqx_pubsub).
-behaviour(gen_server2). -behaviour(gen_server2).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
-include("emqttd_internal.hrl"). -include("emqx_internal.hrl").
-export([start_link/3]). -export([start_link/3]).
@ -55,22 +55,23 @@ start_link(Pool, Id, Env) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @doc Subscribe a Topic %% @doc Subscribe a Topic
-spec(subscribe(binary(), emqttd:subscriber(), [emqttd:suboption()]) -> ok). -spec(subscribe(binary(), emqx:subscriber(), [emqx:suboption()]) -> ok).
subscribe(Topic, Subscriber, Options) -> subscribe(Topic, Subscriber, Options) ->
call(pick(Topic), {subscribe, Topic, Subscriber, Options}). call(pick(Topic), {subscribe, Topic, Subscriber, Options}).
-spec(async_subscribe(binary(), emqttd:subscriber(), [emqttd:suboption()]) -> ok). -spec(async_subscribe(binary(), emqx:subscriber(), [emqx:suboption()]) -> ok).
async_subscribe(Topic, Subscriber, Options) -> async_subscribe(Topic, Subscriber, Options) ->
cast(pick(Topic), {subscribe, Topic, Subscriber, Options}). cast(pick(Topic), {subscribe, Topic, Subscriber, Options}).
%% @doc Publish MQTT Message to Topic %% @doc Publish Message to a Topic
-spec(publish(binary(), any()) -> {ok, mqtt_delivery()} | ignore). -spec(publish(binary(), any()) -> {ok, mqtt_delivery()} | ignore).
publish(Topic, Msg) -> publish(Topic, Msg) ->
route(lists:append(emqttd_router:match(Topic), route(lists:append(emqx_router:match(Topic),
emqttd_router:match_local(Topic)), delivery(Msg)). emqx_router:match_local(Topic)), delivery(Msg)).
route([], #mqtt_delivery{message = #mqtt_message{topic = Topic}}) -> route([], #mqtt_delivery{message = Msg}) ->
dropped(Topic), ignore; emqx_hooks:run('message.offline', [undefined, Msg]),
dropped(Msg#mqtt_message.topic), ignore;
%% Dispatch on the local node %% Dispatch on the local node
route([#mqtt_route{topic = To, node = Node}], route([#mqtt_route{topic = To, node = Node}],
@ -90,13 +91,14 @@ delivery(Msg) -> #mqtt_delivery{sender = self(), message = Msg, flows = []}.
%% @doc Forward message to another node... %% @doc Forward message to another node...
forward(Node, To, Delivery) -> forward(Node, To, Delivery) ->
rpc:cast(Node, ?PUBSUB, dispatch, [To, Delivery]), {ok, Delivery}. emqx_rpc:cast(Node, ?PUBSUB, dispatch, [To, Delivery]), {ok, Delivery}.
%% @doc Dispatch Message to Subscribers %% @doc Dispatch Message to Subscribers
-spec(dispatch(binary(), mqtt_delivery()) -> mqtt_delivery()). -spec(dispatch(binary(), mqtt_delivery()) -> mqtt_delivery()).
dispatch(Topic, Delivery = #mqtt_delivery{message = Msg, flows = Flows}) -> dispatch(Topic, Delivery = #mqtt_delivery{message = Msg, flows = Flows}) ->
case subscribers(Topic) of case subscribers(Topic) of
[] -> [] ->
emqx_hooks:run('message.offline', [undefined, Msg]),
dropped(Topic), {ok, Delivery}; dropped(Topic), {ok, Delivery};
[Sub] -> %% optimize? [Sub] -> %% optimize?
dispatch(Sub, Topic, Msg), dispatch(Sub, Topic, Msg),
@ -110,7 +112,7 @@ dispatch(Topic, Delivery = #mqtt_delivery{message = Msg, flows = Flows}) ->
dispatch(Pid, Topic, Msg) when is_pid(Pid) -> dispatch(Pid, Topic, Msg) when is_pid(Pid) ->
Pid ! {dispatch, Topic, Msg}; Pid ! {dispatch, Topic, Msg};
dispatch(SubId, Topic, Msg) when is_binary(SubId) -> dispatch(SubId, Topic, Msg) when is_binary(SubId) ->
emqttd_sm:dispatch(SubId, Topic, Msg); emqx_sm:dispatch(SubId, Topic, Msg);
dispatch({_Share, [Sub]}, Topic, Msg) -> dispatch({_Share, [Sub]}, Topic, Msg) ->
dispatch(Sub, Topic, Msg); dispatch(Sub, Topic, Msg);
dispatch({_Share, []}, _Topic, _Msg) -> dispatch({_Share, []}, _Topic, _Msg) ->
@ -138,14 +140,14 @@ group_by_share(Subscribers) ->
dropped(<<"$SYS/", _/binary>>) -> dropped(<<"$SYS/", _/binary>>) ->
ok; ok;
dropped(_Topic) -> dropped(_Topic) ->
emqttd_metrics:inc('messages/dropped'). emqx_metrics:inc('messages/dropped').
%% @doc Unsubscribe %% @doc Unsubscribe
-spec(unsubscribe(binary(), emqttd:subscriber(), [emqttd:suboption()]) -> ok). -spec(unsubscribe(binary(), emqx:subscriber(), [emqx:suboption()]) -> ok).
unsubscribe(Topic, Subscriber, Options) -> unsubscribe(Topic, Subscriber, Options) ->
call(pick(Topic), {unsubscribe, Topic, Subscriber, Options}). call(pick(Topic), {unsubscribe, Topic, Subscriber, Options}).
-spec(async_unsubscribe(binary(), emqttd:subscriber(), [emqttd:suboption()]) -> ok). -spec(async_unsubscribe(binary(), emqx:subscriber(), [emqx:suboption()]) -> ok).
async_unsubscribe(Topic, Subscriber, Options) -> async_unsubscribe(Topic, Subscriber, Options) ->
cast(pick(Topic), {unsubscribe, Topic, Subscriber, Options}). cast(pick(Topic), {unsubscribe, Topic, Subscriber, Options}).
@ -210,11 +212,11 @@ add_subscriber(Topic, Subscriber, Options) ->
end. end.
add_subscriber_(Share, Topic, Subscriber) -> add_subscriber_(Share, Topic, Subscriber) ->
(not ets:member(mqtt_subscriber, Topic)) andalso emqttd_router:add_route(Topic), (not ets:member(mqtt_subscriber, Topic)) andalso emqx_router:add_route(Topic),
ets:insert(mqtt_subscriber, {Topic, shared(Share, Subscriber)}). ets:insert(mqtt_subscriber, {Topic, shared(Share, Subscriber)}).
add_local_subscriber_(Share, Topic, Subscriber) -> add_local_subscriber_(Share, Topic, Subscriber) ->
(not ets:member(mqtt_subscriber, {local, Topic})) andalso emqttd_router:add_local_route(Topic), (not ets:member(mqtt_subscriber, {local, Topic})) andalso emqx_router:add_local_route(Topic),
ets:insert(mqtt_subscriber, {{local, Topic}, shared(Share, Subscriber)}). ets:insert(mqtt_subscriber, {{local, Topic}, shared(Share, Subscriber)}).
del_subscriber(Topic, Subscriber, Options) -> del_subscriber(Topic, Subscriber, Options) ->
@ -226,11 +228,11 @@ del_subscriber(Topic, Subscriber, Options) ->
del_subscriber_(Share, Topic, Subscriber) -> del_subscriber_(Share, Topic, Subscriber) ->
ets:delete_object(mqtt_subscriber, {Topic, shared(Share, Subscriber)}), ets:delete_object(mqtt_subscriber, {Topic, shared(Share, Subscriber)}),
(not ets:member(mqtt_subscriber, Topic)) andalso emqttd_router:del_route(Topic). (not ets:member(mqtt_subscriber, Topic)) andalso emqx_router:del_route(Topic).
del_local_subscriber_(Share, Topic, Subscriber) -> del_local_subscriber_(Share, Topic, Subscriber) ->
ets:delete_object(mqtt_subscriber, {{local, Topic}, shared(Share, Subscriber)}), ets:delete_object(mqtt_subscriber, {{local, Topic}, shared(Share, Subscriber)}),
(not ets:member(mqtt_subscriber, {local, Topic})) andalso emqttd_router:del_local_route(Topic). (not ets:member(mqtt_subscriber, {local, Topic})) andalso emqx_router:del_local_route(Topic).
shared(undefined, Subscriber) -> shared(undefined, Subscriber) ->
Subscriber; Subscriber;
@ -238,6 +240,6 @@ shared(Share, Subscriber) ->
{Share, Subscriber}. {Share, Subscriber}.
setstats(State) -> setstats(State) ->
emqttd_stats:setstats('subscribers/count', 'subscribers/max', ets:info(mqtt_subscriber, size)), emqx_stats:setstats('subscribers/count', 'subscribers/max', ets:info(mqtt_subscriber, size)),
State. State.

View File

@ -15,7 +15,7 @@
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @doc PubSub Supervisor. %% @doc PubSub Supervisor.
-module(emqttd_pubsub_sup). -module(emqx_pubsub_sup).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
@ -44,7 +44,7 @@ pubsub_pool() ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
init([]) -> init([]) ->
{ok, Env} = emqttd:env(pubsub), {ok, Env} = emqx:env(pubsub),
%% Create ETS Tables %% Create ETS Tables
[create_tab(Tab) || Tab <- [mqtt_subproperty, mqtt_subscriber, mqtt_subscription]], [create_tab(Tab) || Tab <- [mqtt_subproperty, mqtt_subscriber, mqtt_subscription]],
{ok, { {one_for_all, 10, 3600}, [pool_sup(pubsub, Env), pool_sup(server, Env)]} }. {ok, { {one_for_all, 10, 3600}, [pool_sup(pubsub, Env), pool_sup(server, Env)]} }.
@ -59,9 +59,9 @@ pool_size(Env) ->
pool_sup(Name, Env) -> pool_sup(Name, Env) ->
Pool = list_to_atom(atom_to_list(Name) ++ "_pool"), Pool = list_to_atom(atom_to_list(Name) ++ "_pool"),
Mod = list_to_atom("emqttd_" ++ atom_to_list(Name)), Mod = list_to_atom("emqx_" ++ atom_to_list(Name)),
MFA = {Mod, start_link, [Env]}, MFA = {Mod, start_link, [Env]},
emqttd_pool_sup:spec(Pool, [Name, hash, pool_size(Env), MFA]). emqx_pool_sup:spec(Pool, [Name, hash, pool_size(Env), MFA]).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Create PubSub Tables %% Create PubSub Tables

View File

@ -13,11 +13,12 @@
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module (emqttd_rest_api).
-include("emqttd.hrl"). -module (emqx_rest_api).
-include("emqttd_internal.hrl"). -include("emqx.hrl").
-include("emqx_rest.hrl").
-http_api({"^nodes/(.+?)/alarms/?$", 'GET', alarm_list, []}). -http_api({"^nodes/(.+?)/alarms/?$", 'GET', alarm_list, []}).
@ -94,7 +95,7 @@
%% alarm %% alarm
%%-------------------------------------------------------------------------- %%--------------------------------------------------------------------------
alarm_list('GET', _Req, _Node) -> alarm_list('GET', _Req, _Node) ->
Alarms = emqttd_mgmt:alarm_list(), Alarms = emqx_mgmt:alarm_list(),
{ok, lists:map(fun alarm_row/1, Alarms)}. {ok, lists:map(fun alarm_row/1, Alarms)}.
alarm_row(#mqtt_alarm{id = AlarmId, alarm_row(#mqtt_alarm{id = AlarmId,
@ -112,12 +113,12 @@ alarm_row(#mqtt_alarm{id = AlarmId,
%% client %% client
%%-------------------------------------------------------------------------- %%--------------------------------------------------------------------------
client('GET', _Params, Key) -> client('GET', _Params, Key) ->
Data = emqttd_mgmt:client(l2b(Key)), Data = emqx_mgmt:client(l2b(Key)),
{ok, [{objects, [client_row(Row) || Row <- Data]}]}. {ok, [{objects, [client_row(Row) || Row <- Data]}]}.
client_list('GET', Params, Node) -> client_list('GET', Params, Node) ->
{PageNo, PageSize} = page_params(Params), {PageNo, PageSize} = page_params(Params),
Data = emqttd_mgmt:client_list(l2a(Node), undefined, PageNo, PageSize), Data = emqx_mgmt:client_list(l2a(Node), undefined, PageNo, PageSize),
Rows = get_value(result, Data), Rows = get_value(result, Data),
TotalPage = get_value(totalPage, Data), TotalPage = get_value(totalPage, Data),
TotalNum = get_value(totalNum, Data), TotalNum = get_value(totalNum, Data),
@ -129,11 +130,11 @@ client_list('GET', Params, Node) ->
client_list('GET', Params, Node, Key) -> client_list('GET', Params, Node, Key) ->
{PageNo, PageSize} = page_params(Params), {PageNo, PageSize} = page_params(Params),
Data = emqttd_mgmt:client_list(l2a(Node), l2b(Key), PageNo, PageSize), Data = emqx_mgmt:client_list(l2a(Node), l2b(Key), PageNo, PageSize),
{ok, [{objects, [client_row(Row) || Row <- Data]}]}. {ok, [{objects, [client_row(Row) || Row <- Data]}]}.
kick_client('DELETE', _Params, Key) -> kick_client('DELETE', _Params, Key) ->
case emqttd_mgmt:kick_client(l2b(Key)) of case emqx_mgmt:kick_client(l2b(Key)) of
true -> {ok, []}; true -> {ok, []};
false -> {error, [{code, ?ERROR12}]} false -> {error, [{code, ?ERROR12}]}
end. end.
@ -141,7 +142,7 @@ kick_client('DELETE', _Params, Key) ->
clean_acl_cache('PUT', Params, Key0) -> clean_acl_cache('PUT', Params, Key0) ->
Topic = get_value(<<"topic">>, Params), Topic = get_value(<<"topic">>, Params),
[Key | _] = string:tokens(Key0, "/"), [Key | _] = string:tokens(Key0, "/"),
case emqttd_mgmt:clean_acl_cache(l2b(Key), Topic) of case emqx_mgmt:clean_acl_cache(l2b(Key), Topic) of
true -> {ok, []}; true -> {ok, []};
false -> {error, [{code, ?ERROR12}]} false -> {error, [{code, ?ERROR12}]}
end. end.
@ -166,12 +167,12 @@ client_row(#mqtt_client{client_id = ClientId,
%% route %% route
%%-------------------------------------------------------------------------- %%--------------------------------------------------------------------------
route('GET', _Params, Key) -> route('GET', _Params, Key) ->
Data = emqttd_mgmt:route(l2b(Key)), Data = emqx_mgmt:route(l2b(Key)),
{ok, [{objects, [route_row(Row) || Row <- Data]}]}. {ok, [{objects, [route_row(Row) || Row <- Data]}]}.
route_list('GET', Params) -> route_list('GET', Params) ->
{PageNo, PageSize} = page_params(Params), {PageNo, PageSize} = page_params(Params),
Data = emqttd_mgmt:route_list(undefined, PageNo, PageSize), Data = emqx_mgmt:route_list(undefined, PageNo, PageSize),
Rows = get_value(result, Data), Rows = get_value(result, Data),
TotalPage = get_value(totalPage, Data), TotalPage = get_value(totalPage, Data),
TotalNum = get_value(totalNum, Data), TotalNum = get_value(totalNum, Data),
@ -191,12 +192,12 @@ route_row({Topic, Node}) ->
%% session %% session
%%-------------------------------------------------------------------------- %%--------------------------------------------------------------------------
session('GET', _Params, Key) -> session('GET', _Params, Key) ->
Data = emqttd_mgmt:session(l2b(Key)), Data = emqx_mgmt:session(l2b(Key)),
{ok, [{objects, [session_row(Row) || Row <- Data]}]}. {ok, [{objects, [session_row(Row) || Row <- Data]}]}.
session_list('GET', Params, Node) -> session_list('GET', Params, Node) ->
{PageNo, PageSize} = page_params(Params), {PageNo, PageSize} = page_params(Params),
Data = emqttd_mgmt:session_list(l2a(Node), undefined, PageNo, PageSize), Data = emqx_mgmt:session_list(l2a(Node), undefined, PageNo, PageSize),
Rows = get_value(result, Data), Rows = get_value(result, Data),
TotalPage = get_value(totalPage, Data), TotalPage = get_value(totalPage, Data),
TotalNum = get_value(totalNum, Data), TotalNum = get_value(totalNum, Data),
@ -208,7 +209,7 @@ session_list('GET', Params, Node) ->
session_list('GET', Params, Node, ClientId) -> session_list('GET', Params, Node, ClientId) ->
{PageNo, PageSize} = page_params(Params), {PageNo, PageSize} = page_params(Params),
Data = emqttd_mgmt:session_list(l2a(Node), l2b(ClientId), PageNo, PageSize), Data = emqx_mgmt:session_list(l2a(Node), l2b(ClientId), PageNo, PageSize),
{ok, [{objects, [session_row(Row) || Row <- Data]}]}. {ok, [{objects, [session_row(Row) || Row <- Data]}]}.
session_row({ClientId, _Pid, _Persistent, Session}) -> session_row({ClientId, _Pid, _Persistent, Session}) ->
@ -220,12 +221,12 @@ session_row({ClientId, _Pid, _Persistent, Session}) ->
%% subscription %% subscription
%%-------------------------------------------------------------------------- %%--------------------------------------------------------------------------
subscription('GET', _Params, Key) -> subscription('GET', _Params, Key) ->
Data = emqttd_mgmt:subscription(l2b(Key)), Data = emqx_mgmt:subscription(l2b(Key)),
{ok, [{objects, [subscription_row(Row) || Row <- Data]}]}. {ok, [{objects, [subscription_row(Row) || Row <- Data]}]}.
subscription_list('GET', Params, Node) -> subscription_list('GET', Params, Node) ->
{PageNo, PageSize} = page_params(Params), {PageNo, PageSize} = page_params(Params),
Data = emqttd_mgmt:subscription_list(l2a(Node), undefined, PageNo, PageSize), Data = emqx_mgmt:subscription_list(l2a(Node), undefined, PageNo, PageSize),
Rows = get_value(result, Data), Rows = get_value(result, Data),
TotalPage = get_value(totalPage, Data), TotalPage = get_value(totalPage, Data),
TotalNum = get_value(totalNum, Data), TotalNum = get_value(totalNum, Data),
@ -237,7 +238,7 @@ subscription_list('GET', Params, Node) ->
subscription_list('GET', Params, Node, Key) -> subscription_list('GET', Params, Node, Key) ->
{PageNo, PageSize} = page_params(Params), {PageNo, PageSize} = page_params(Params),
Data = emqttd_mgmt:subscription_list(l2a(Node), l2b(Key), PageNo, PageSize), Data = emqx_mgmt:subscription_list(l2a(Node), l2b(Key), PageNo, PageSize),
{ok, [{objects, [subscription_row(Row) || Row <- Data]}]}. {ok, [{objects, [subscription_row(Row) || Row <- Data]}]}.
subscription_row({{Topic, ClientId}, Option}) when is_pid(ClientId) -> subscription_row({{Topic, ClientId}, Option}) when is_pid(ClientId) ->
@ -250,43 +251,43 @@ subscription_row({{Topic, ClientId}, Option}) ->
%% management/monitoring %% management/monitoring
%%-------------------------------------------------------------------------- %%--------------------------------------------------------------------------
nodes('GET', _Params) -> nodes('GET', _Params) ->
Data = emqttd_mgmt:nodes_info(), Data = emqx_mgmt:nodes_info(),
{ok, Data}. {ok, Data}.
node('GET', _Params, Node) -> node('GET', _Params, Node) ->
Data = emqttd_mgmt:node_info(l2a(Node)), Data = emqx_mgmt:node_info(l2a(Node)),
{ok, Data}. {ok, Data}.
brokers('GET', _Params) -> brokers('GET', _Params) ->
Data = emqttd_mgmt:brokers(), Data = emqx_mgmt:brokers(),
{ok, [format_broker(Node, Broker) || {Node, Broker} <- Data]}. {ok, [format_broker(Node, Broker) || {Node, Broker} <- Data]}.
broker('GET', _Params, Node) -> broker('GET', _Params, Node) ->
Data = emqttd_mgmt:broker(l2a(Node)), Data = emqx_mgmt:broker(l2a(Node)),
{ok, format_broker(Data)}. {ok, format_broker(Data)}.
listeners('GET', _Params) -> listeners('GET', _Params) ->
Data = emqttd_mgmt:listeners(), Data = emqx_mgmt:listeners(),
{ok, [[{Node, format_listeners(Listeners, [])} || {Node, Listeners} <- Data]]}. {ok, [{Node, format_listeners(Listeners, [])} || {Node, Listeners} <- Data]}.
listener('GET', _Params, Node) -> listener('GET', _Params, Node) ->
Data = emqttd_mgmt:listener(l2a(Node)), Data = emqx_mgmt:listener(l2a(Node)),
{ok, [format_listener(Listeners) || Listeners <- Data]}. {ok, [format_listener(Listeners) || Listeners <- Data]}.
metrics('GET', _Params) -> metrics('GET', _Params) ->
Data = emqttd_mgmt:metrics(), Data = emqx_mgmt:metrics(),
{ok, [Data]}. {ok, Data}.
metric('GET', _Params, Node) -> metric('GET', _Params, Node) ->
Data = emqttd_mgmt:metrics(l2a(Node)), Data = emqx_mgmt:metrics(l2a(Node)),
{ok, Data}. {ok, Data}.
stats('GET', _Params) -> stats('GET', _Params) ->
Data = emqttd_mgmt:stats(), Data = emqx_mgmt:stats(),
{ok, [Data]}. {ok, [Data]}.
stat('GET', _Params, Node) -> stat('GET', _Params, Node) ->
Data = emqttd_mgmt:stats(l2a(Node)), Data = emqx_mgmt:stats(l2a(Node)),
{ok, Data}. {ok, Data}.
format_broker(Node, Broker) -> format_broker(Node, Broker) ->
@ -321,12 +322,12 @@ format_listener({Protocol, ListenOn, Info}) ->
%% mqtt %% mqtt
%%-------------------------------------------------------------------------- %%--------------------------------------------------------------------------
publish('POST', Params) -> publish('POST', Params) ->
Topic = get_value(<<"topic">>, Params), Topic = get_value(<<"topic">>, Params),
ClientId = get_value(<<"client_id">>, Params, http), ClientId = get_value(<<"client_id">>, Params, http),
Payload = get_value(<<"payload">>, Params, <<>>), Payload = get_value(<<"payload">>, Params, <<>>),
Qos = get_value(<<"qos">>, Params, 0), Qos = get_value(<<"qos">>, Params, 0),
Retain = get_value(<<"retain">>, Params, false), Retain = get_value(<<"retain">>, Params, false),
case emqttd_mgmt:publish({ClientId, Topic, Payload, Qos, Retain}) of case emqx_mgmt:publish({ClientId, Topic, Payload, Qos, Retain}) of
ok -> ok ->
{ok, []}; {ok, []};
{error, Error} -> {error, Error} ->
@ -337,7 +338,7 @@ subscribe('POST', Params) ->
ClientId = get_value(<<"client_id">>, Params), ClientId = get_value(<<"client_id">>, Params),
Topic = get_value(<<"topic">>, Params), Topic = get_value(<<"topic">>, Params),
Qos = get_value(<<"qos">>, Params, 0), Qos = get_value(<<"qos">>, Params, 0),
case emqttd_mgmt:subscribe({ClientId, Topic, Qos}) of case emqx_mgmt:subscribe({ClientId, Topic, Qos}) of
ok -> ok ->
{ok, []}; {ok, []};
{error, Error} -> {error, Error} ->
@ -347,7 +348,7 @@ subscribe('POST', Params) ->
unsubscribe('POST', Params) -> unsubscribe('POST', Params) ->
ClientId = get_value(<<"client_id">>, Params), ClientId = get_value(<<"client_id">>, Params),
Topic = get_value(<<"topic">>, Params), Topic = get_value(<<"topic">>, Params),
case emqttd_mgmt:unsubscribe({ClientId, Topic})of case emqx_mgmt:unsubscribe({ClientId, Topic})of
ok -> ok ->
{ok, []}; {ok, []};
{error, Error} -> {error, Error} ->
@ -358,16 +359,16 @@ unsubscribe('POST', Params) ->
%% plugins %% plugins
%%-------------------------------------------------------------------------- %%--------------------------------------------------------------------------
plugin_list('GET', _Params, Node) -> plugin_list('GET', _Params, Node) ->
Plugins = lists:map(fun plugin/1, emqttd_mgmt:plugin_list(l2a(Node))), Plugins = lists:map(fun plugin/1, emqx_mgmt:plugin_list(l2a(Node))),
{ok, Plugins}. {ok, Plugins}.
enabled('PUT', Params, Node, PluginName) -> enabled('PUT', Params, Node, PluginName) ->
Active = get_value(<<"active">>, Params), Active = get_value(<<"active">>, Params),
case Active of case Active of
true -> true ->
return(emqttd_mgmt:plugin_load(l2a(Node), l2a(PluginName))); return(emqx_mgmt:plugin_load(l2a(Node), l2a(PluginName)));
false -> false ->
return(emqttd_mgmt:plugin_unload(l2a(Node), l2a(PluginName))) return(emqx_mgmt:plugin_unload(l2a(Node), l2a(PluginName)))
end. end.
return(Result) -> return(Result) ->
@ -397,7 +398,7 @@ plugin(#mqtt_plugin{name = Name, version = Ver, descr = Descr,
modify_config('PUT', Params, App) -> modify_config('PUT', Params, App) ->
Key = get_value(<<"key">>, Params, <<"">>), Key = get_value(<<"key">>, Params, <<"">>),
Value = get_value(<<"value">>, Params, <<"">>), Value = get_value(<<"value">>, Params, <<"">>),
case emqttd_mgmt:modify_config(l2a(App), b2l(Key), b2l(Value)) of case emqx_mgmt:modify_config(l2a(App), b2l(Key), b2l(Value)) of
true -> {ok, []}; true -> {ok, []};
false -> {error, [{code, ?ERROR2}]} false -> {error, [{code, ?ERROR2}]}
end. end.
@ -405,26 +406,26 @@ modify_config('PUT', Params, App) ->
modify_config('PUT', Params, Node, App) -> modify_config('PUT', Params, Node, App) ->
Key = get_value(<<"key">>, Params, <<"">>), Key = get_value(<<"key">>, Params, <<"">>),
Value = get_value(<<"value">>, Params, <<"">>), Value = get_value(<<"value">>, Params, <<"">>),
case emqttd_mgmt:modify_config(l2a(Node), l2a(App), b2l(Key), b2l(Value)) of case emqx_mgmt:modify_config(l2a(Node), l2a(App), b2l(Key), b2l(Value)) of
ok -> {ok, []}; ok -> {ok, []};
_ -> {error, [{code, ?ERROR2}]} _ -> {error, [{code, ?ERROR2}]}
end. end.
config_list('GET', _Params) -> config_list('GET', _Params) ->
Data = emqttd_mgmt:get_configs(), Data = emqx_mgmt:get_configs(),
{ok, [{Node, format_config(Config, [])} || {Node, Config} <- Data]}. {ok, [{Node, format_config(Config, [])} || {Node, Config} <- Data]}.
config_list('GET', _Params, Node) -> config_list('GET', _Params, Node) ->
Data = emqttd_mgmt:get_config(l2a(Node)), Data = emqx_mgmt:get_config(l2a(Node)),
{ok, [format_config(Config) || Config <- lists:reverse(Data)]}. {ok, [format_config(Config) || Config <- lists:reverse(Data)]}.
plugin_config_list('GET', _Params, Node, App) -> plugin_config_list('GET', _Params, Node, App) ->
{ok, Data} = emqttd_mgmt:get_plugin_config(l2a(Node), l2a(App)), {ok, Data} = emqx_mgmt:get_plugin_config(l2a(Node), l2a(App)),
{ok, [format_plugin_config(Config) || Config <- lists:reverse(Data)]}. {ok, [format_plugin_config(Config) || Config <- lists:reverse(Data)]}.
modify_plugin_config('PUT', Params, Node, App) -> modify_plugin_config('PUT', Params, Node, App) ->
PluginName = l2a(App), PluginName = l2a(App),
case emqttd_mgmt:modify_plugin_config(l2a(Node), PluginName, Params) of case emqx_mgmt:modify_plugin_config(l2a(Node), PluginName, Params) of
ok -> ok ->
Plugins = emqttd_plugins:list(), Plugins = emqttd_plugins:list(),
{_, _, _, _, Status} = lists:keyfind(PluginName, 2, Plugins), {_, _, _, _, Status} = lists:keyfind(PluginName, 2, Plugins),
@ -465,7 +466,7 @@ format_plugin_config({Key, Value, Desc, Required}) ->
auth('POST', Params) -> auth('POST', Params) ->
Username = get_value(<<"username">>, Params), Username = get_value(<<"username">>, Params),
Password = get_value(<<"password">>, Params), Password = get_value(<<"password">>, Params),
case emqttd_mgmt:check_user(Username, Password) of case emqx_mgmt:check_user(Username, Password) of
ok -> ok ->
{ok, []}; {ok, []};
{error, Reason} -> {error, Reason} ->
@ -476,26 +477,26 @@ users('POST', Params) ->
Username = get_value(<<"username">>, Params), Username = get_value(<<"username">>, Params),
Password = get_value(<<"password">>, Params), Password = get_value(<<"password">>, Params),
Tag = get_value(<<"tags">>, Params), Tag = get_value(<<"tags">>, Params),
code(emqttd_mgmt:add_user(Username, Password, Tag)); code(emqx_mgmt:add_user(Username, Password, Tag));
users('GET', _Params) -> users('GET', _Params) ->
{ok, [Admin || Admin <- emqttd_mgmt:user_list()]}. {ok, [Admin || Admin <- emqx_mgmt:user_list()]}.
users('GET', _Params, Username) -> users('GET', _Params, Username) ->
{ok, emqttd_mgmt:lookup_user(list_to_binary(Username))}; {ok, emqx_mgmt:lookup_user(list_to_binary(Username))};
users('PUT', Params, Username) -> users('PUT', Params, Username) ->
code(emqttd_mgmt:update_user(list_to_binary(Username), Params)); code(emqx_mgmt:update_user(list_to_binary(Username), Params));
users('DELETE', _Params, "admin") -> users('DELETE', _Params, "admin") ->
{error, [{code, ?ERROR6}, {message, <<"admin cannot be deleted">>}]}; {error, [{code, ?ERROR6}, {message, <<"admin cannot be deleted">>}]};
users('DELETE', _Params, Username) -> users('DELETE', _Params, Username) ->
code(emqttd_mgmt:remove_user(list_to_binary(Username))). code(emqx_mgmt:remove_user(list_to_binary(Username))).
change_pwd('PUT', Params, Username) -> change_pwd('PUT', Params, Username) ->
OldPwd = get_value(<<"old_pwd">>, Params), OldPwd = get_value(<<"old_pwd">>, Params),
NewPwd = get_value(<<"new_pwd">>, Params), NewPwd = get_value(<<"new_pwd">>, Params),
code(emqttd_mgmt:change_password(list_to_binary(Username), OldPwd, NewPwd)). code(emqx_mgmt:change_password(list_to_binary(Username), OldPwd, NewPwd)).
code(ok) -> {ok, []}; code(ok) -> {ok, []};
code(error) -> {error, [{code, ?ERROR2}]}; code(error) -> {error, [{code, ?ERROR2}]};

View File

@ -14,13 +14,13 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_router). -module(emqx_router).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-behaviour(gen_server). -behaviour(gen_server).
-include("emqttd.hrl"). -include("emqx.hrl").
%% Mnesia Bootstrap %% Mnesia Bootstrap
-export([mnesia/1]). -export([mnesia/1]).
@ -89,7 +89,7 @@ local_topics() ->
%% @doc Match Routes. %% @doc Match Routes.
-spec(match(Topic:: binary()) -> [mqtt_route()]). -spec(match(Topic:: binary()) -> [mqtt_route()]).
match(Topic) when is_binary(Topic) -> match(Topic) when is_binary(Topic) ->
Matched = mnesia:async_dirty(fun emqttd_trie:match/1, [Topic]), Matched = mnesia:async_dirty(fun emqx_trie:match/1, [Topic]),
%% Optimize: route table will be replicated to all nodes. %% Optimize: route table will be replicated to all nodes.
lists:append([ets:lookup(mqtt_route, To) || To <- [Topic | Matched]]). lists:append([ets:lookup(mqtt_route, To) || To <- [Topic | Matched]]).
@ -123,8 +123,8 @@ add_routes(Routes) ->
add_route_(Route = #mqtt_route{topic = Topic}) -> add_route_(Route = #mqtt_route{topic = Topic}) ->
case mnesia:wread({mqtt_route, Topic}) of case mnesia:wread({mqtt_route, Topic}) of
[] -> [] ->
case emqttd_topic:wildcard(Topic) of case emqx_topic:wildcard(Topic) of
true -> emqttd_trie:insert(Topic); true -> emqx_trie:insert(Topic);
false -> ok false -> ok
end, end,
mnesia:write(Route), mnesia:write(Route),
@ -163,8 +163,8 @@ del_route_(Route = #mqtt_route{topic = Topic}) ->
[Route] -> [Route] ->
%% Remove route and trie %% Remove route and trie
mnesia:delete_object(Route), mnesia:delete_object(Route),
case emqttd_topic:wildcard(Topic) of case emqx_topic:wildcard(Topic) of
true -> emqttd_trie:delete(Topic); true -> emqx_trie:delete(Topic);
false -> ok false -> ok
end, end,
mnesia:delete({mqtt_topic, Topic}); mnesia:delete({mqtt_topic, Topic});
@ -206,7 +206,7 @@ del_local_route(Topic) ->
match_local(Name) -> match_local(Name) ->
[#mqtt_route{topic = {local, Filter}, node = Node} [#mqtt_route{topic = {local, Filter}, node = Node}
|| {Filter, Node} <- ets:tab2list(mqtt_local_route), || {Filter, Node} <- ets:tab2list(mqtt_local_route),
emqttd_topic:match(Name, Filter)]. emqx_topic:match(Name, Filter)].
dump() -> dump() ->
[{route, ets:tab2list(mqtt_route)}, {local_route, ets:tab2list(mqtt_local_route)}]. [{route, ets:tab2list(mqtt_route)}, {local_route, ets:tab2list(mqtt_local_route)}].
@ -281,5 +281,5 @@ clean_routes_(Node) ->
mnesia:transaction(Clean). mnesia:transaction(Clean).
update_stats_() -> update_stats_() ->
emqttd_stats:setstats('routes/count', 'routes/max', mnesia:table_info(mqtt_route, size)). emqx_stats:setstats('routes/count', 'routes/max', mnesia:table_info(mqtt_route, size)).

17
src/emqx_rpc.erl Normal file
View File

@ -0,0 +1,17 @@
%%
%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. All Rights Reserved.
%%
%% @doc EMQ X Distributed RPC.
%%
-module(emqx_rpc).
-author("Feng Lee <feng@emqtt.io>").
-export([cast/4]).
%% @doc Wraps gen_rpc first.
cast(Node, Mod, Fun, Args) ->
emqx_metrics:inc('messages/forward'),
gen_rpc:cast({Node, erlang:system_info(scheduler_id)}, Mod, Fun, Args).

View File

@ -15,13 +15,13 @@
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @doc MQTT Packet Serializer %% @doc MQTT Packet Serializer
-module(emqttd_serializer). -module(emqx_serializer).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
-include("emqttd_protocol.hrl"). -include("emqx_mqtt.hrl").
%% API %% API
-export([serialize/1]). -export([serialize/1]).
@ -46,18 +46,18 @@ serialize_header(#mqtt_packet_header{type = Type,
[<<Type:4, (opt(Dup)):1, (opt(Qos)):2, (opt(Retain)):1>>, [<<Type:4, (opt(Dup)):1, (opt(Qos)):2, (opt(Retain)):1>>,
serialize_len(Len), VariableBin, PayloadBin]. serialize_len(Len), VariableBin, PayloadBin].
serialize_variable(?CONNECT, #mqtt_packet_connect{client_id = ClientId, serialize_variable(?CONNECT, #mqtt_packet_connect{client_id = ClientId,
proto_ver = ProtoVer, proto_ver = ProtoVer,
proto_name = ProtoName, proto_name = ProtoName,
will_retain = WillRetain, will_retain = WillRetain,
will_qos = WillQos, will_qos = WillQos,
will_flag = WillFlag, will_flag = WillFlag,
clean_sess = CleanSess, clean_sess = CleanSess,
keep_alive = KeepAlive, keep_alive = KeepAlive,
will_topic = WillTopic, will_topic = WillTopic,
will_msg = WillMsg, will_msg = WillMsg,
username = Username, username = Username,
password = Password}, undefined) -> password = Password}, undefined) ->
VariableBin = <<(byte_size(ProtoName)):16/big-unsigned-integer, VariableBin = <<(byte_size(ProtoName)):16/big-unsigned-integer,
ProtoName/binary, ProtoName/binary,
ProtoVer:8, ProtoVer:8,

View File

@ -14,17 +14,17 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_server). -module(emqx_server).
-behaviour(gen_server2). -behaviour(gen_server2).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
-include("emqttd_protocol.hrl"). -include("emqx_mqtt.hrl").
-include("emqttd_internal.hrl"). -include("emqx_internal.hrl").
-export([start_link/3]). -export([start_link/3]).
@ -47,9 +47,9 @@
-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, {pool, id, env, submon :: emqttd_pmon:pmon()}). -record(state, { pool, id, env, submon :: emqx_pmon:pmon() }).
%% @doc Start server %% @doc Start a Server
-spec(start_link(atom(), pos_integer(), list()) -> {ok, pid()} | ignore | {error, any()}). -spec(start_link(atom(), pos_integer(), list()) -> {ok, pid()} | ignore | {error, any()}).
start_link(Pool, Id, Env) -> start_link(Pool, Id, Env) ->
gen_server2:start_link({local, ?PROC_NAME(?MODULE, Id)}, ?MODULE, [Pool, Id, Env], []). gen_server2:start_link({local, ?PROC_NAME(?MODULE, Id)}, ?MODULE, [Pool, Id, Env], []).
@ -59,16 +59,16 @@ start_link(Pool, Id, Env) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @doc Subscribe a Topic %% @doc Subscribe a Topic
-spec(subscribe(binary()) -> ok | emqttd:pubsub_error()). -spec(subscribe(binary()) -> ok | {error, any()}).
subscribe(Topic) when is_binary(Topic) -> subscribe(Topic) when is_binary(Topic) ->
subscribe(Topic, self()). subscribe(Topic, self()).
-spec(subscribe(binary(), emqttd:subscriber()) -> ok | emqttd:pubsub_error()). -spec(subscribe(binary(), emqx:subscriber()) -> ok | {error, any()}).
subscribe(Topic, Subscriber) when is_binary(Topic) -> subscribe(Topic, Subscriber) when is_binary(Topic) ->
subscribe(Topic, Subscriber, []). subscribe(Topic, Subscriber, []).
-spec(subscribe(binary(), emqttd:subscriber(), [emqttd:suboption()]) -> -spec(subscribe(binary(), emqx:subscriber(), [emqx:suboption()]) ->
ok | emqttd:pubsub_error()). ok | {error, any()}).
subscribe(Topic, Subscriber, Options) when is_binary(Topic) -> subscribe(Topic, Subscriber, Options) when is_binary(Topic) ->
call(pick(Subscriber), {subscribe, Topic, Subscriber, Options}). call(pick(Subscriber), {subscribe, Topic, Subscriber, Options}).
@ -77,23 +77,23 @@ subscribe(Topic, Subscriber, Options) when is_binary(Topic) ->
async_subscribe(Topic) when is_binary(Topic) -> async_subscribe(Topic) when is_binary(Topic) ->
async_subscribe(Topic, self()). async_subscribe(Topic, self()).
-spec(async_subscribe(binary(), emqttd:subscriber()) -> ok). -spec(async_subscribe(binary(), emqx:subscriber()) -> ok).
async_subscribe(Topic, Subscriber) when is_binary(Topic) -> async_subscribe(Topic, Subscriber) when is_binary(Topic) ->
async_subscribe(Topic, Subscriber, []). async_subscribe(Topic, Subscriber, []).
-spec(async_subscribe(binary(), emqttd:subscriber(), [emqttd:suboption()]) -> ok). -spec(async_subscribe(binary(), emqx:subscriber(), [emqx:suboption()]) -> ok).
async_subscribe(Topic, Subscriber, Options) when is_binary(Topic) -> async_subscribe(Topic, Subscriber, Options) when is_binary(Topic) ->
cast(pick(Subscriber), {subscribe, Topic, Subscriber, Options}). cast(pick(Subscriber), {subscribe, Topic, Subscriber, Options}).
%% @doc Publish message to Topic. %% @doc Publish a message
-spec(publish(mqtt_message()) -> {ok, mqtt_delivery()} | ignore). -spec(publish(mqtt_message()) -> {ok, mqtt_delivery()} | ignore).
publish(Msg = #mqtt_message{from = From}) -> publish(Msg = #mqtt_message{from = From}) ->
trace(publish, From, Msg), trace(publish, From, Msg),
case emqttd_hooks:run('message.publish', [], Msg) of case emqx_hooks:run('message.publish', [], Msg) of
{ok, Msg1 = #mqtt_message{topic = Topic}} -> {ok, Msg1 = #mqtt_message{topic = Topic}} ->
emqttd_pubsub:publish(Topic, Msg1); emqx_pubsub:publish(Topic, Msg1);
{stop, Msg1} -> {stop, Msg1} ->
lager:warning("Stop publishing: ~s", [emqttd_message:format(Msg1)]), lager:warning("Stop publishing: ~s", [emqx_message:format(Msg1)]),
ignore ignore
end. end.
@ -102,19 +102,19 @@ trace(publish, From, _Msg) when is_atom(From) ->
%% Dont' trace '$SYS' publish %% Dont' trace '$SYS' publish
ignore; ignore;
trace(publish, {ClientId, Username}, #mqtt_message{topic = Topic, payload = Payload}) -> trace(publish, {ClientId, Username}, #mqtt_message{topic = Topic, payload = Payload}) ->
lager:debug([{client, ClientId}, {topic, Topic}], lager:info([{client, ClientId}, {topic, Topic}],
"~s/~s PUBLISH to ~s: ~p", [ClientId, Username, Topic, Payload]); "~s/~s PUBLISH to ~s: ~p", [ClientId, Username, Topic, Payload]);
trace(publish, From, #mqtt_message{topic = Topic, payload = Payload}) -> trace(publish, From, #mqtt_message{topic = Topic, payload = Payload}) when is_binary(From); is_list(From) ->
lager:debug([{client, From}, {topic, Topic}], lager:info([{client, From}, {topic, Topic}],
"~s PUBLISH to ~s: ~p", [From, Topic, Payload]). "~s PUBLISH to ~s: ~p", [From, Topic, Payload]).
%% @doc Unsubscribe %% @doc Unsubscribe
-spec(unsubscribe(binary()) -> ok | emqttd:pubsub_error()). -spec(unsubscribe(binary()) -> ok | {error, any()}).
unsubscribe(Topic) when is_binary(Topic) -> unsubscribe(Topic) when is_binary(Topic) ->
unsubscribe(Topic, self()). unsubscribe(Topic, self()).
%% @doc Unsubscribe %% @doc Unsubscribe
-spec(unsubscribe(binary(), emqttd:subscriber()) -> ok | emqttd:pubsub_error()). -spec(unsubscribe(binary(), emqx:subscriber()) -> ok | {error, any()}).
unsubscribe(Topic, Subscriber) when is_binary(Topic) -> unsubscribe(Topic, Subscriber) when is_binary(Topic) ->
call(pick(Subscriber), {unsubscribe, Topic, Subscriber}). call(pick(Subscriber), {unsubscribe, Topic, Subscriber}).
@ -123,14 +123,14 @@ unsubscribe(Topic, Subscriber) when is_binary(Topic) ->
async_unsubscribe(Topic) when is_binary(Topic) -> async_unsubscribe(Topic) when is_binary(Topic) ->
async_unsubscribe(Topic, self()). async_unsubscribe(Topic, self()).
-spec(async_unsubscribe(binary(), emqttd:subscriber()) -> ok). -spec(async_unsubscribe(binary(), emqx:subscriber()) -> ok).
async_unsubscribe(Topic, Subscriber) when is_binary(Topic) -> async_unsubscribe(Topic, Subscriber) when is_binary(Topic) ->
cast(pick(Subscriber), {unsubscribe, Topic, Subscriber}). cast(pick(Subscriber), {unsubscribe, Topic, Subscriber}).
setqos(Topic, Subscriber, Qos) when is_binary(Topic) -> setqos(Topic, Subscriber, Qos) when is_binary(Topic) ->
call(pick(Subscriber), {setqos, Topic, Subscriber, Qos}). call(pick(Subscriber), {setqos, Topic, Subscriber, Qos}).
-spec(subscriptions(emqttd:subscriber()) -> [{binary(), binary(), list(emqttd:suboption())}]). -spec(subscriptions(emqx:subscriber()) -> [{binary(), list(emqx:suboption())}]).
subscriptions(Subscriber) -> subscriptions(Subscriber) ->
lists:map(fun({_, {_Share, Topic}}) -> lists:map(fun({_, {_Share, Topic}}) ->
subscription(Topic, Subscriber); subscription(Topic, Subscriber);
@ -142,13 +142,13 @@ subscription(Topic, Subscriber) ->
{Topic, Subscriber, ets:lookup_element(mqtt_subproperty, {Topic, Subscriber}, 2)}. {Topic, Subscriber, ets:lookup_element(mqtt_subproperty, {Topic, Subscriber}, 2)}.
subscribers(Topic) -> subscribers(Topic) ->
emqttd_pubsub:subscribers(Topic). emqx_pubsub:subscribers(Topic).
-spec(is_subscribed(binary(), emqttd:subscriber()) -> boolean()). -spec(is_subscribed(binary(), emqx:subscriber()) -> boolean()).
is_subscribed(Topic, Subscriber) when is_binary(Topic) -> is_subscribed(Topic, Subscriber) when is_binary(Topic) ->
ets:member(mqtt_subproperty, {Topic, Subscriber}). ets:member(mqtt_subproperty, {Topic, Subscriber}).
-spec(subscriber_down(emqttd:subscriber()) -> ok). -spec(subscriber_down(emqx:subscriber()) -> ok).
subscriber_down(Subscriber) -> subscriber_down(Subscriber) ->
cast(pick(Subscriber), {subscriber_down, Subscriber}). cast(pick(Subscriber), {subscriber_down, Subscriber}).
@ -170,7 +170,7 @@ dump() ->
init([Pool, Id, Env]) -> init([Pool, Id, Env]) ->
?GPROC_POOL(join, Pool, Id), ?GPROC_POOL(join, Pool, Id),
{ok, #state{pool = Pool, id = Id, env = Env, submon = emqttd_pmon:new()}}. {ok, #state{pool = Pool, id = Id, env = Env, submon = emqx_pmon:new()}}.
handle_call({subscribe, Topic, Subscriber, Options}, _From, State) -> handle_call({subscribe, Topic, Subscriber, Options}, _From, State) ->
case do_subscribe_(Topic, Subscriber, Options, State) of case do_subscribe_(Topic, Subscriber, Options, State) of
@ -237,7 +237,7 @@ code_change(_OldVsn, State, _Extra) ->
do_subscribe_(Topic, Subscriber, Options, State) -> do_subscribe_(Topic, Subscriber, Options, State) ->
case ets:lookup(mqtt_subproperty, {Topic, Subscriber}) of case ets:lookup(mqtt_subproperty, {Topic, Subscriber}) of
[] -> [] ->
emqttd_pubsub:async_subscribe(Topic, Subscriber, Options), emqx_pubsub:async_subscribe(Topic, Subscriber, Options),
Share = proplists:get_value(share, Options), Share = proplists:get_value(share, Options),
add_subscription_(Share, Subscriber, Topic), add_subscription_(Share, Subscriber, Topic),
ets:insert(mqtt_subproperty, {{Topic, Subscriber}, Options}), ets:insert(mqtt_subproperty, {{Topic, Subscriber}, Options}),
@ -259,7 +259,7 @@ monitor_subpid(_SubPid, State) ->
do_unsubscribe_(Topic, Subscriber, State) -> do_unsubscribe_(Topic, Subscriber, State) ->
case ets:lookup(mqtt_subproperty, {Topic, Subscriber}) of case ets:lookup(mqtt_subproperty, {Topic, Subscriber}) of
[{_, Options}] -> [{_, Options}] ->
emqttd_pubsub:async_unsubscribe(Topic, Subscriber, Options), emqx_pubsub:async_unsubscribe(Topic, Subscriber, Options),
Share = proplists:get_value(share, Options), Share = proplists:get_value(share, Options),
del_subscription_(Share, Subscriber, Topic), del_subscription_(Share, Subscriber, Topic),
ets:delete(mqtt_subproperty, {Topic, Subscriber}), ets:delete(mqtt_subproperty, {Topic, Subscriber}),
@ -294,13 +294,13 @@ subscriber_down_(Share, Subscriber, Topic) ->
[] -> [] ->
%% TODO:....??? %% TODO:....???
Options = if Share == undefined -> []; true -> [{share, Share}] end, Options = if Share == undefined -> []; true -> [{share, Share}] end,
emqttd_pubsub:async_unsubscribe(Topic, Subscriber, Options); emqx_pubsub:async_unsubscribe(Topic, Subscriber, Options);
[{_, Options}] -> [{_, Options}] ->
emqttd_pubsub:async_unsubscribe(Topic, Subscriber, Options), emqx_pubsub:async_unsubscribe(Topic, Subscriber, Options),
ets:delete(mqtt_subproperty, {Topic, Subscriber}) ets:delete(mqtt_subproperty, {Topic, Subscriber})
end. end.
setstats(State) -> setstats(State) ->
emqttd_stats:setstats('subscriptions/count', 'subscriptions/max', emqx_stats:setstats('subscriptions/count', 'subscriptions/max',
ets:info(mqtt_subscription, size)), State. ets:info(mqtt_subscription, size)), State.

View File

@ -43,19 +43,19 @@
%% @end %% @end
%% %%
-module(emqttd_session). -module(emqx_session).
-behaviour(gen_server2). -behaviour(gen_server2).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
-include("emqttd_protocol.hrl"). -include("emqx_mqtt.hrl").
-include("emqttd_internal.hrl"). -include("emqx_internal.hrl").
-import(emqttd_misc, [start_timer/2]). -import(emqx_misc, [start_timer/2]).
-import(proplists, [get_value/2, get_value/3]). -import(proplists, [get_value/2, get_value/3]).
@ -77,7 +77,7 @@
-export([prioritise_call/4, prioritise_cast/3, prioritise_info/3, -export([prioritise_call/4, prioritise_cast/3, prioritise_info/3,
handle_pre_hibernate/1]). handle_pre_hibernate/1]).
-define(MQueue, emqttd_mqueue). -define(MQueue, emqx_mqueue).
-record(state, -record(state,
{ {
@ -111,7 +111,7 @@
upgrade_qos = false :: boolean(), upgrade_qos = false :: boolean(),
%% Client <- Broker: Inflight QoS1, QoS2 messages sent to the client but unacked. %% Client <- Broker: Inflight QoS1, QoS2 messages sent to the client but unacked.
inflight :: emqttd_inflight:inflight(), inflight :: emqx_inflight:inflight(),
%% Max Inflight Size %% Max Inflight Size
max_inflight = 32 :: non_neg_integer(), max_inflight = 32 :: non_neg_integer(),
@ -152,9 +152,10 @@
%% Force GC Count %% Force GC Count
force_gc_count :: undefined | integer(), force_gc_count :: undefined | integer(),
created_at :: erlang:timestamp(), %% Ignore loop deliver?
ignore_loop_deliver = false :: boolean(),
ignore_loop_deliver = false :: boolean() created_at :: erlang:timestamp()
}). }).
-define(TIMEOUT, 60000). -define(TIMEOUT, 60000).
@ -181,11 +182,11 @@ start_link(CleanSess, {ClientId, Username}, ClientPid) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @doc Subscribe topics %% @doc Subscribe topics
-spec(subscribe(pid(), [{binary(), [emqttd_topic:option()]}]) -> ok). -spec(subscribe(pid(), [{binary(), [emqx_topic:option()]}]) -> ok).
subscribe(Session, TopicTable) -> %%TODO: the ack function??... subscribe(Session, TopicTable) ->%%TODO: the ack function??...
gen_server2:cast(Session, {subscribe, self(), TopicTable, fun(_) -> ok end}). gen_server2:cast(Session, {subscribe, self(), TopicTable, fun(_) -> ok end}).
-spec(subscribe(pid(), mqtt_packet_id(), [{binary(), [emqttd_topic:option()]}]) -> ok). -spec(subscribe(pid(), mqtt_packet_id(), [{binary(), [emqx_topic:option()]}]) -> ok).
subscribe(Session, PacketId, TopicTable) -> %%TODO: the ack function??... subscribe(Session, PacketId, TopicTable) -> %%TODO: the ack function??...
From = self(), From = self(),
AckFun = fun(GrantedQos) -> From ! {suback, PacketId, GrantedQos} end, AckFun = fun(GrantedQos) -> From ! {suback, PacketId, GrantedQos} end,
@ -195,11 +196,11 @@ subscribe(Session, PacketId, TopicTable) -> %%TODO: the ack function??...
-spec(publish(pid(), mqtt_message()) -> ok | {error, any()}). -spec(publish(pid(), mqtt_message()) -> ok | {error, any()}).
publish(_Session, Msg = #mqtt_message{qos = ?QOS_0}) -> publish(_Session, Msg = #mqtt_message{qos = ?QOS_0}) ->
%% Publish QoS0 Directly %% Publish QoS0 Directly
emqttd_server:publish(Msg), ok; emqx_server:publish(Msg), ok;
publish(_Session, Msg = #mqtt_message{qos = ?QOS_1}) -> publish(_Session, Msg = #mqtt_message{qos = ?QOS_1}) ->
%% Publish QoS1 message directly for client will PubAck automatically %% Publish QoS1 message directly for client will PubAck automatically
emqttd_server:publish(Msg), ok; emqx_server:publish(Msg), ok;
publish(Session, Msg = #mqtt_message{qos = ?QOS_2}) -> publish(Session, Msg = #mqtt_message{qos = ?QOS_2}) ->
%% Publish QoS2 to Session %% Publish QoS2 to Session
@ -223,7 +224,7 @@ pubcomp(Session, PacketId) ->
gen_server2:cast(Session, {pubcomp, PacketId}). gen_server2:cast(Session, {pubcomp, PacketId}).
%% @doc Unsubscribe the topics %% @doc Unsubscribe the topics
-spec(unsubscribe(pid(), [{binary(), [emqttd_topic:option()]}]) -> ok). -spec(unsubscribe(pid(), [{binary(), [emqx_topic:option()]}]) -> ok).
unsubscribe(Session, TopicTable) -> unsubscribe(Session, TopicTable) ->
gen_server2:cast(Session, {unsubscribe, self(), TopicTable}). gen_server2:cast(Session, {unsubscribe, self(), TopicTable}).
@ -255,7 +256,7 @@ stats(#state{max_subscriptions = MaxSubscriptions,
mqueue = MQueue, mqueue = MQueue,
max_awaiting_rel = MaxAwaitingRel, max_awaiting_rel = MaxAwaitingRel,
awaiting_rel = AwaitingRel}) -> awaiting_rel = AwaitingRel}) ->
lists:append(emqttd_misc:proc_stats(), lists:append(emqx_misc:proc_stats(),
[{max_subscriptions, MaxSubscriptions}, [{max_subscriptions, MaxSubscriptions},
{subscriptions, maps:size(Subscriptions)}, {subscriptions, maps:size(Subscriptions)},
{max_inflight, MaxInflight}, {max_inflight, MaxInflight},
@ -281,13 +282,13 @@ init([CleanSess, {ClientId, Username}, ClientPid]) ->
process_flag(trap_exit, true), process_flag(trap_exit, true),
true = link(ClientPid), true = link(ClientPid),
init_stats([deliver_msg, enqueue_msg]), init_stats([deliver_msg, enqueue_msg]),
{ok, Env} = emqttd:env(session), {ok, Env} = emqx:env(session),
{ok, QEnv} = emqttd:env(mqueue), {ok, QEnv} = emqx:env(mqueue),
MaxInflight = get_value(max_inflight, Env, 0), MaxInflight = get_value(max_inflight, Env, 0),
EnableStats = get_value(enable_stats, Env, false), EnableStats = get_value(enable_stats, Env, false),
ForceGcCount = emqx_gc:conn_max_gc_count(),
IgnoreLoopDeliver = get_value(ignore_loop_deliver, Env, false), IgnoreLoopDeliver = get_value(ignore_loop_deliver, Env, false),
ForceGcCount = emqttd_gc:conn_max_gc_count(), MQueue = ?MQueue:new(ClientId, QEnv, emqx_alarm:alarm_fun()),
MQueue = ?MQueue:new(ClientId, QEnv, emqttd_alarm:alarm_fun()),
State = #state{clean_sess = CleanSess, State = #state{clean_sess = CleanSess,
binding = binding(ClientPid), binding = binding(ClientPid),
client_id = ClientId, client_id = ClientId,
@ -297,7 +298,7 @@ init([CleanSess, {ClientId, Username}, ClientPid]) ->
max_subscriptions = get_value(max_subscriptions, Env, 0), max_subscriptions = get_value(max_subscriptions, Env, 0),
upgrade_qos = get_value(upgrade_qos, Env, false), upgrade_qos = get_value(upgrade_qos, Env, false),
max_inflight = MaxInflight, max_inflight = MaxInflight,
inflight = emqttd_inflight:new(MaxInflight), inflight = emqx_inflight:new(MaxInflight),
mqueue = MQueue, mqueue = MQueue,
retry_interval = get_value(retry_interval, Env), retry_interval = get_value(retry_interval, Env),
awaiting_rel = #{}, awaiting_rel = #{},
@ -306,10 +307,10 @@ init([CleanSess, {ClientId, Username}, ClientPid]) ->
expiry_interval = get_value(expiry_interval, Env), expiry_interval = get_value(expiry_interval, Env),
enable_stats = EnableStats, enable_stats = EnableStats,
force_gc_count = ForceGcCount, force_gc_count = ForceGcCount,
created_at = os:timestamp(), ignore_loop_deliver = IgnoreLoopDeliver,
ignore_loop_deliver = IgnoreLoopDeliver}, created_at = os:timestamp()},
emqttd_sm:register_session(ClientId, CleanSess, info(State)), emqx_sm:register_session(ClientId, CleanSess, info(State)),
emqttd_hooks:run('session.created', [ClientId, Username]), emqx_hooks:run('session.created', [ClientId, Username]),
{ok, emit_stats(State), hibernate, {backoff, 1000, 1000, 10000}}. {ok, emit_stats(State), hibernate, {backoff, 1000, 1000, 10000}}.
init_stats(Keys) -> init_stats(Keys) ->
@ -343,7 +344,7 @@ prioritise_info(Msg, _Len, _State) ->
end. end.
handle_pre_hibernate(State) -> handle_pre_hibernate(State) ->
{hibernate, emqttd_gc:reset_conn_gc_count(#state.force_gc_count, emit_stats(State))}. {hibernate, emqx_gc:reset_conn_gc_count(#state.force_gc_count, emit_stats(State))}.
handle_call({publish, Msg = #mqtt_message{qos = ?QOS_2, pktid = PacketId}}, _From, handle_call({publish, Msg = #mqtt_message{qos = ?QOS_2, pktid = PacketId}}, _From,
State = #state{awaiting_rel = AwaitingRel, State = #state{awaiting_rel = AwaitingRel,
@ -358,7 +359,7 @@ handle_call({publish, Msg = #mqtt_message{qos = ?QOS_2, pktid = PacketId}}, _Fro
reply(ok, State1#state{awaiting_rel = maps:put(PacketId, Msg, AwaitingRel)}); reply(ok, State1#state{awaiting_rel = maps:put(PacketId, Msg, AwaitingRel)});
true -> true ->
?LOG(warning, "Dropped Qos2 Message for too many awaiting_rel: ~p", [Msg], State), ?LOG(warning, "Dropped Qos2 Message for too many awaiting_rel: ~p", [Msg], State),
emqttd_metrics:inc('messages/qos2/dropped'), emqx_metrics:inc('messages/qos2/dropped'),
reply({error, dropped}, State) reply({error, dropped}, State)
end; end;
@ -374,27 +375,31 @@ handle_call(state, _From, State) ->
handle_call(Req, _From, State) -> handle_call(Req, _From, State) ->
?UNEXPECTED_REQ(Req, State). ?UNEXPECTED_REQ(Req, State).
handle_cast({subscribe, _From, TopicTable, AckFun}, handle_cast({subscribe, From, TopicTable, AckFun},
State = #state{client_id = ClientId, State = #state{client_id = ClientId,
username = Username, username = Username,
subscriptions = Subscriptions}) -> subscriptions = Subscriptions}) ->
?LOG(debug, "Subscribe ~p", [TopicTable], State), ?LOG(info, "Subscribe ~p", [TopicTable], State),
{GrantedQos, Subscriptions1} = {GrantedQos, Subscriptions1} =
lists:foldl(fun({Topic, Opts}, {QosAcc, SubMap}) -> lists:foldl(fun({Topic, Opts}, {QosAcc, SubMap}) ->
NewQos = proplists:get_value(qos, Opts), Fastlane = lists:member(fastlane, Opts),
NewQos = if Fastlane == true -> ?QOS_0; true -> get_value(qos, Opts) end,
SubMap1 = SubMap1 =
case maps:find(Topic, SubMap) of case maps:find(Topic, SubMap) of
{ok, NewQos} -> {ok, NewQos} ->
?LOG(warning, "Duplicated subscribe: ~s, qos = ~w", [Topic, NewQos], State), ?LOG(warning, "Duplicated subscribe: ~s, qos = ~w", [Topic, NewQos], State),
SubMap; SubMap;
{ok, OldQos} -> {ok, OldQos} ->
emqttd:setqos(Topic, ClientId, NewQos), emqx_server:setqos(Topic, ClientId, NewQos),
?LOG(warning, "Duplicated subscribe ~s, old_qos=~w, new_qos=~w", ?LOG(warning, "Duplicated subscribe ~s, old_qos=~w, new_qos=~w",
[Topic, OldQos, NewQos], State), [Topic, OldQos, NewQos], State),
maps:put(Topic, NewQos, SubMap); maps:put(Topic, NewQos, SubMap);
error -> error ->
emqttd:subscribe(Topic, ClientId, Opts), case Fastlane of
emqttd_hooks:run('session.subscribed', [ClientId, Username], {Topic, Opts}), true -> emqx:subscribe(Topic, From, Opts);
false -> emqx:subscribe(Topic, ClientId, Opts)
end,
emqx_hooks:run('session.subscribed', [ClientId, Username], {Topic, Opts}),
maps:put(Topic, NewQos, SubMap) maps:put(Topic, NewQos, SubMap)
end, end,
{[NewQos|QosAcc], SubMap1} {[NewQos|QosAcc], SubMap1}
@ -402,17 +407,21 @@ handle_cast({subscribe, _From, TopicTable, AckFun},
AckFun(lists:reverse(GrantedQos)), AckFun(lists:reverse(GrantedQos)),
hibernate(emit_stats(State#state{subscriptions = Subscriptions1})); hibernate(emit_stats(State#state{subscriptions = Subscriptions1}));
handle_cast({unsubscribe, _From, TopicTable}, handle_cast({unsubscribe, From, TopicTable},
State = #state{client_id = ClientId, State = #state{client_id = ClientId,
username = Username, username = Username,
subscriptions = Subscriptions}) -> subscriptions = Subscriptions}) ->
?LOG(debug, "Unsubscribe ~p", [TopicTable], State), ?LOG(info, "Unsubscribe ~p", [TopicTable], State),
Subscriptions1 = Subscriptions1 =
lists:foldl(fun({Topic, Opts}, SubMap) -> lists:foldl(fun({Topic, Opts}, SubMap) ->
Fastlane = lists:member(fastlane, Opts),
case maps:find(Topic, SubMap) of case maps:find(Topic, SubMap) of
{ok, _Qos} -> {ok, _Qos} ->
emqttd:unsubscribe(Topic, ClientId), case Fastlane of
emqttd_hooks:run('session.unsubscribed', [ClientId, Username], {Topic, Opts}), true -> emqx:unsubscribe(Topic, From);
false -> emqx:unsubscribe(Topic, ClientId)
end,
emqx_hooks:run('session.unsubscribed', [ClientId, Username], {Topic, Opts}),
maps:remove(Topic, SubMap); maps:remove(Topic, SubMap);
error -> error ->
SubMap SubMap
@ -429,7 +438,7 @@ handle_cast({puback, PacketId}, State = #state{inflight = Inflight}) ->
false -> false ->
?LOG(warning, "PUBACK ~p missed inflight: ~p", ?LOG(warning, "PUBACK ~p missed inflight: ~p",
[PacketId, Inflight:window()], State), [PacketId, Inflight:window()], State),
emqttd_metrics:inc('packets/puback/missed'), emqx_metrics:inc('packets/puback/missed'),
State State
end, hibernate}; end, hibernate};
@ -442,7 +451,7 @@ handle_cast({pubrec, PacketId}, State = #state{inflight = Inflight}) ->
false -> false ->
?LOG(warning, "PUBREC ~p missed inflight: ~p", ?LOG(warning, "PUBREC ~p missed inflight: ~p",
[PacketId, Inflight:window()], State), [PacketId, Inflight:window()], State),
emqttd_metrics:inc('packets/pubrec/missed'), emqx_metrics:inc('packets/pubrec/missed'),
State State
end, hibernate}; end, hibernate};
@ -451,11 +460,12 @@ handle_cast({pubrel, PacketId}, State = #state{awaiting_rel = AwaitingRel}) ->
{noreply, {noreply,
case maps:take(PacketId, AwaitingRel) of case maps:take(PacketId, AwaitingRel) of
{Msg, AwaitingRel1} -> {Msg, AwaitingRel1} ->
spawn(emqttd_server, publish, [Msg]), %%:) %% TODO: woker pool to publish the qos2 messages?
spawn(emqx_server, publish, [Msg]), %%:)
gc(State#state{awaiting_rel = AwaitingRel1}); gc(State#state{awaiting_rel = AwaitingRel1});
error -> error ->
?LOG(warning, "Cannot find PUBREL: ~p", [PacketId], State), ?LOG(warning, "Cannot find PUBREL: ~p", [PacketId], State),
emqttd_metrics:inc('packets/pubrel/missed'), emqx_metrics:inc('packets/pubrel/missed'),
State State
end, hibernate}; end, hibernate};
@ -468,7 +478,7 @@ handle_cast({pubcomp, PacketId}, State = #state{inflight = Inflight}) ->
false -> false ->
?LOG(warning, "The PUBCOMP ~p is not inflight: ~p", ?LOG(warning, "The PUBCOMP ~p is not inflight: ~p",
[PacketId, Inflight:window()], State), [PacketId, Inflight:window()], State),
emqttd_metrics:inc('packets/pubcomp/missed'), emqx_metrics:inc('packets/pubcomp/missed'),
State State
end, hibernate}; end, hibernate};
@ -481,10 +491,10 @@ handle_cast({resume, ClientId, ClientPid},
await_rel_timer = AwaitTimer, await_rel_timer = AwaitTimer,
expiry_timer = ExpireTimer}) -> expiry_timer = ExpireTimer}) ->
?LOG(debug, "Resumed by ~p", [ClientPid], State), ?LOG(info, "Resumed by ~p", [ClientPid], State),
%% Cancel Timers %% Cancel Timers
lists:foreach(fun emqttd_misc:cancel_timer/1, lists:foreach(fun emqx_misc:cancel_timer/1,
[RetryTimer, AwaitTimer, ExpireTimer]), [RetryTimer, AwaitTimer, ExpireTimer]),
case kick(ClientId, OldClientPid, ClientPid) of case kick(ClientId, OldClientPid, ClientPid) of
@ -507,7 +517,7 @@ handle_cast({resume, ClientId, ClientPid},
if if
CleanSess =:= true -> CleanSess =:= true ->
?LOG(error, "CleanSess changed to false.", [], State1), ?LOG(error, "CleanSess changed to false.", [], State1),
emqttd_sm:register_session(ClientId, false, info(State1)); emqx_sm:register_session(ClientId, false, info(State1));
CleanSess =:= false -> CleanSess =:= false ->
ok ok
end, end,
@ -528,17 +538,14 @@ handle_cast({destroy, ClientId},
handle_cast(Msg, State) -> handle_cast(Msg, State) ->
?UNEXPECTED_MSG(Msg, State). ?UNEXPECTED_MSG(Msg, State).
%% Dispatch message from self publish %% Ignore Messages delivered by self
handle_info({dispatch, Topic, Msg = #mqtt_message{from = {ClientId, _}}}, handle_info({dispatch, _Topic, #mqtt_message{from = {ClientId, _}}},
State = #state{client_id = ClientId, State = #state{client_id = ClientId, ignore_loop_deliver = true}) ->
ignore_loop_deliver = IgnoreLoopDeliver}) when is_record(Msg, mqtt_message) -> hibernate(State);
case IgnoreLoopDeliver of
true -> {noreply, State, hibernate};
false -> {noreply, gc(dispatch(tune_qos(Topic, Msg, State), State)), hibernate}
end;
%% Dispatch Message %% Dispatch Message
handle_info({dispatch, Topic, Msg}, State) when is_record(Msg, mqtt_message) -> handle_info({dispatch, Topic, Msg}, State) when is_record(Msg, mqtt_message) ->
{noreply, gc(dispatch(tune_qos(Topic, Msg, State), State)), hibernate}; hibernate(gc(dispatch(tune_qos(Topic, Msg, State), State)));
%% Do nothing if the client has been disconnected. %% Do nothing if the client has been disconnected.
handle_info({timeout, _Timer, retry_delivery}, State = #state{client_pid = undefined}) -> handle_info({timeout, _Timer, retry_delivery}, State = #state{client_pid = undefined}) ->
@ -551,7 +558,7 @@ handle_info({timeout, _Timer, check_awaiting_rel}, State) ->
hibernate(expire_awaiting_rel(emit_stats(State#state{await_rel_timer = undefined}))); hibernate(expire_awaiting_rel(emit_stats(State#state{await_rel_timer = undefined})));
handle_info({timeout, _Timer, expired}, State) -> handle_info({timeout, _Timer, expired}, State) ->
?LOG(debug, "Expired, shutdown now.", [], State), ?LOG(info, "Expired, shutdown now.", [], State),
shutdown(expired, State); shutdown(expired, State);
handle_info({'EXIT', ClientPid, _Reason}, handle_info({'EXIT', ClientPid, _Reason},
@ -562,7 +569,7 @@ handle_info({'EXIT', ClientPid, Reason},
State = #state{clean_sess = false, State = #state{clean_sess = false,
client_pid = ClientPid, client_pid = ClientPid,
expiry_interval = Interval}) -> expiry_interval = Interval}) ->
?LOG(debug, "Client ~p EXIT for ~p", [ClientPid, Reason], State), ?LOG(info, "Client ~p EXIT for ~p", [ClientPid, Reason], State),
ExpireTimer = start_timer(Interval, expired), ExpireTimer = start_timer(Interval, expired),
State1 = State#state{client_pid = undefined, expiry_timer = ExpireTimer}, State1 = State#state{client_pid = undefined, expiry_timer = ExpireTimer},
hibernate(emit_stats(State1)); hibernate(emit_stats(State1));
@ -581,10 +588,10 @@ handle_info(Info, Session) ->
?UNEXPECTED_INFO(Info, Session). ?UNEXPECTED_INFO(Info, Session).
terminate(Reason, #state{client_id = ClientId, username = Username}) -> terminate(Reason, #state{client_id = ClientId, username = Username}) ->
emqttd_stats:del_session_stats(ClientId), emqx_stats:del_session_stats(ClientId),
emqttd_hooks:run('session.terminated', [ClientId, Username, Reason]), emqx_hooks:run('session.terminated', [ClientId, Username, Reason]),
emqttd_server:subscriber_down(ClientId), emqx_server:subscriber_down(ClientId),
emqttd_sm:unregister_session(ClientId). emqx_sm:unregister_session(ClientId).
code_change(_OldVsn, Session, _Extra) -> code_change(_OldVsn, Session, _Extra) ->
{ok, Session}. {ok, Session}.
@ -687,8 +694,11 @@ is_awaiting_full(#state{awaiting_rel = AwaitingRel, max_awaiting_rel = MaxLen})
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Enqueue message if the client has been disconnected %% Enqueue message if the client has been disconnected
dispatch(Msg, State = #state{client_pid = undefined}) -> dispatch(Msg, State = #state{client_id = ClientId, client_pid = undefined}) ->
enqueue_msg(Msg, State); case emqx_hooks:run('message.offline', [ClientId, Msg]) of
ok -> enqueue_msg(Msg, State);
stop -> State
end;
%% Deliver qos0 message directly to client %% Deliver qos0 message directly to client
dispatch(Msg = #mqtt_message{qos = ?QOS0}, State) -> dispatch(Msg = #mqtt_message{qos = ?QOS0}, State) ->
@ -717,9 +727,10 @@ enqueue_msg(Msg, State = #state{mqueue = Q}) ->
redeliver(Msg = #mqtt_message{qos = QoS}, State) -> redeliver(Msg = #mqtt_message{qos = QoS}, State) ->
deliver(Msg#mqtt_message{dup = if QoS =:= ?QOS2 -> false; true -> true end}, State). deliver(Msg#mqtt_message{dup = if QoS =:= ?QOS2 -> false; true -> true end}, State).
deliver(Msg, #state{client_pid = Pid}) -> deliver(Msg, #state{client_pid = Pid, binding = local}) ->
inc_stats(deliver_msg), inc_stats(deliver_msg), Pid ! {deliver, Msg};
Pid ! {deliver, Msg}. deliver(Msg, #state{client_pid = Pid, binding = remote}) ->
inc_stats(deliver_msg), emqx_rpc:cast(node(Pid), erlang, send, [Pid, {deliver, Msg}]).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Awaiting ACK for QoS1/QoS2 Messages %% Awaiting ACK for QoS1/QoS2 Messages
@ -738,7 +749,7 @@ acked(puback, PacketId, State = #state{client_id = ClientId,
inflight = Inflight}) -> inflight = Inflight}) ->
case Inflight:lookup(PacketId) of case Inflight:lookup(PacketId) of
{publish, Msg, _Ts} -> {publish, Msg, _Ts} ->
emqttd_hooks:run('message.acked', [ClientId, Username], Msg), emqx_hooks:run('message.acked', [ClientId, Username], Msg),
State#state{inflight = Inflight:delete(PacketId)}; State#state{inflight = Inflight:delete(PacketId)};
_ -> _ ->
?LOG(warning, "Duplicated PUBACK Packet: ~p", [PacketId], State), ?LOG(warning, "Duplicated PUBACK Packet: ~p", [PacketId], State),
@ -750,7 +761,7 @@ acked(pubrec, PacketId, State = #state{client_id = ClientId,
inflight = Inflight}) -> inflight = Inflight}) ->
case Inflight:lookup(PacketId) of case Inflight:lookup(PacketId) of
{publish, Msg, _Ts} -> {publish, Msg, _Ts} ->
emqttd_hooks:run('message.acked', [ClientId, Username], Msg), emqx_hooks:run('message.acked', [ClientId, Username], Msg),
State#state{inflight = Inflight:update(PacketId, {pubrel, PacketId, os:timestamp()})}; State#state{inflight = Inflight:update(PacketId, {pubrel, PacketId, os:timestamp()})};
{pubrel, PacketId, _Ts} -> {pubrel, PacketId, _Ts} ->
?LOG(warning, "Duplicated PUBREC Packet: ~p", [PacketId], State), ?LOG(warning, "Duplicated PUBREC Packet: ~p", [PacketId], State),
@ -817,7 +828,7 @@ next_msg_id(State = #state{next_msg_id = Id}) ->
emit_stats(State = #state{enable_stats = false}) -> emit_stats(State = #state{enable_stats = false}) ->
State; State;
emit_stats(State = #state{client_id = ClientId}) -> emit_stats(State = #state{client_id = ClientId}) ->
emqttd_stats:set_session_stats(ClientId, stats(State)), emqx_stats:set_session_stats(ClientId, stats(State)),
State. State.
inc_stats(Key) -> put(Key, get(Key) + 1). inc_stats(Key) -> put(Key, get(Key) + 1).
@ -836,5 +847,5 @@ shutdown(Reason, State) ->
{stop, {shutdown, Reason}, State}. {stop, {shutdown, Reason}, State}.
gc(State) -> gc(State) ->
emqttd_gc:maybe_force_gc(#state.force_gc_count, State). emqx_gc:maybe_force_gc(#state.force_gc_count, State).

View File

@ -15,7 +15,7 @@
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @doc Session Supervisor. %% @doc Session Supervisor.
-module(emqttd_session_sup). -module(emqx_session_sup).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
@ -41,5 +41,6 @@ start_session(CleanSess, {ClientId, Username}, ClientPid) ->
init([]) -> init([]) ->
{ok, {{simple_one_for_one, 0, 1}, {ok, {{simple_one_for_one, 0, 1},
[{session, {emqttd_session, start_link, []}, [{session, {emqx_session, start_link, []},
temporary, 5000, worker, [emqttd_session]}]}}. temporary, 5000, worker, [emqx_session]}]}}.

View File

@ -14,15 +14,15 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_sm). -module(emqx_sm).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-behaviour(gen_server2). -behaviour(gen_server2).
-include("emqttd.hrl"). -include("emqx.hrl").
-include("emqttd_internal.hrl"). -include("emqx_internal.hrl").
%% Mnesia Callbacks %% Mnesia Callbacks
-export([mnesia/1]). -export([mnesia/1]).
@ -116,7 +116,9 @@ dispatch(ClientId, Topic, Msg) ->
try ets:lookup_element(mqtt_local_session, ClientId, 2) of try ets:lookup_element(mqtt_local_session, ClientId, 2) of
Pid -> Pid ! {dispatch, Topic, Msg} Pid -> Pid ! {dispatch, Topic, Msg}
catch catch
error:badarg -> ok %%FIXME Later. error:badarg ->
emqx_hooks:run('message.offline', [ClientId, Msg]),
ok %%TODO: How??
end. end.
call(SM, Req) -> call(SM, Req) ->
@ -187,7 +189,7 @@ handle_info({'DOWN', MRef, process, DownPid, _Reason}, State) ->
[] -> [] ->
ok; ok;
[Sess = #mqtt_session{sess_pid = DownPid}] -> [Sess = #mqtt_session{sess_pid = DownPid}] ->
emqttd_stats:del_session_stats(ClientId), emqx_stats:del_session_stats(ClientId),
mnesia:delete_object(mqtt_session, Sess, write); mnesia:delete_object(mqtt_session, Sess, write);
[_Sess] -> [_Sess] ->
ok ok
@ -223,7 +225,7 @@ create_session({CleanSess, {ClientId, Username}, ClientPid}, State) ->
end. end.
create_session(CleanSess, {ClientId, Username}, ClientPid) -> create_session(CleanSess, {ClientId, Username}, ClientPid) ->
case emqttd_session_sup:start_session(CleanSess, {ClientId, Username}, ClientPid) of case emqx_session_sup:start_session(CleanSess, {ClientId, Username}, ClientPid) of
{ok, SessPid} -> {ok, SessPid} ->
Session = #mqtt_session{client_id = ClientId, sess_pid = SessPid, clean_sess = CleanSess}, Session = #mqtt_session{client_id = ClientId, sess_pid = SessPid, clean_sess = CleanSess},
case insert_session(Session) of case insert_session(Session) of
@ -255,7 +257,7 @@ resume_session(Session = #mqtt_session{client_id = ClientId, sess_pid = SessPid}
case is_process_alive(SessPid) of case is_process_alive(SessPid) of
true -> true ->
emqttd_session:resume(SessPid, ClientId, ClientPid), emqx_session:resume(SessPid, ClientId, ClientPid),
{ok, SessPid}; {ok, SessPid};
false -> false ->
?LOG(error, "Cannot resume ~p which seems already dead!", [SessPid], Session), ?LOG(error, "Cannot resume ~p which seems already dead!", [SessPid], Session),
@ -265,7 +267,7 @@ resume_session(Session = #mqtt_session{client_id = ClientId, sess_pid = SessPid}
%% Remote node %% Remote node
resume_session(Session = #mqtt_session{client_id = ClientId, sess_pid = SessPid}, ClientPid) -> resume_session(Session = #mqtt_session{client_id = ClientId, sess_pid = SessPid}, ClientPid) ->
Node = node(SessPid), Node = node(SessPid),
case rpc:call(Node, emqttd_session, resume, [SessPid, ClientId, ClientPid]) of case rpc:call(Node, emqx_session, resume, [SessPid, ClientId, ClientPid]) of
ok -> ok ->
{ok, SessPid}; {ok, SessPid};
{badrpc, nodedown} -> {badrpc, nodedown} ->
@ -278,16 +280,16 @@ resume_session(Session = #mqtt_session{client_id = ClientId, sess_pid = SessPid}
end. end.
%% Local node %% Local node
destroy_session(Session = #mqtt_session{client_id = ClientId, sess_pid = SessPid}) destroy_session(Session = #mqtt_session{client_id = ClientId, sess_pid = SessPid})
when node(SessPid) =:= node() -> when node(SessPid) =:= node() ->
emqttd_session:destroy(SessPid, ClientId), emqx_session:destroy(SessPid, ClientId),
remove_session(Session); remove_session(Session);
%% Remote node %% Remote node
destroy_session(Session = #mqtt_session{client_id = ClientId, destroy_session(Session = #mqtt_session{client_id = ClientId,
sess_pid = SessPid}) -> sess_pid = SessPid}) ->
Node = node(SessPid), Node = node(SessPid),
case rpc:call(Node, emqttd_session, destroy, [SessPid, ClientId]) of case rpc:call(Node, emqx_session, destroy, [SessPid, ClientId]) of
ok -> ok ->
remove_session(Session); remove_session(Session);
{badrpc, nodedown} -> {badrpc, nodedown} ->

View File

@ -15,15 +15,15 @@
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @doc Session Helper. %% @doc Session Helper.
-module(emqttd_sm_helper). -module(emqx_sm_helper).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-behaviour(gen_server). -behaviour(gen_server).
-include("emqttd.hrl"). -include("emqx.hrl").
-include("emqttd_internal.hrl"). -include("emqx_internal.hrl").
-include_lib("stdlib/include/ms_transform.hrl"). -include_lib("stdlib/include/ms_transform.hrl").

View File

@ -16,17 +16,17 @@
%% @doc Session Manager Supervisor. %% @doc Session Manager Supervisor.
-module(emqttd_sm_sup). -module(emqx_sm_sup).
-behaviour(supervisor). -behaviour(supervisor).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
-define(SM, emqttd_sm). -define(SM, emqx_sm).
-define(HELPER, emqttd_sm_helper). -define(HELPER, emqx_sm_helper).
%% API %% API
-export([start_link/0]). -export([start_link/0]).
@ -42,13 +42,13 @@ init([]) ->
ets:new(mqtt_local_session, [public, ordered_set, named_table, {write_concurrency, true}]), ets:new(mqtt_local_session, [public, ordered_set, named_table, {write_concurrency, true}]),
%% Helper %% Helper
StatsFun = emqttd_stats:statsfun('sessions/count', 'sessions/max'), StatsFun = emqx_stats:statsfun('sessions/count', 'sessions/max'),
Helper = {?HELPER, {?HELPER, start_link, [StatsFun]}, Helper = {?HELPER, {?HELPER, start_link, [StatsFun]},
permanent, 5000, worker, [?HELPER]}, permanent, 5000, worker, [?HELPER]},
%% SM Pool Sup %% SM Pool Sup
MFA = {?SM, start_link, []}, MFA = {?SM, start_link, []},
PoolSup = emqttd_pool_sup:spec([?SM, hash, erlang:system_info(schedulers), MFA]), PoolSup = emqx_pool_sup:spec([?SM, hash, erlang:system_info(schedulers), MFA]),
{ok, {{one_for_all, 10, 3600}, [Helper, PoolSup]}}. {ok, {{one_for_all, 10, 3600}, [Helper, PoolSup]}}.

259
src/emqx_ssl.erl Normal file
View File

@ -0,0 +1,259 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io)
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%--------------------------------------------------------------------
%%
%% @doc SSL Utility Functions. This module is copied from rabbit_ssl.erl
%%
-module(emqx_ssl).
-include_lib("public_key/include/public_key.hrl").
-type(certificate() :: binary()).
-export([peer_cert_issuer/1, peer_cert_subject/1, peer_cert_common_name/1,
peer_cert_subject_items/2, peer_cert_validity/1]).
%% Return a string describing the certificate's issuer.
-spec(peer_cert_issuer(certificate()) -> string()).
peer_cert_issuer(Cert) ->
cert_info(fun(#'OTPCertificate' {
tbsCertificate = #'OTPTBSCertificate' {
issuer = Issuer }}) ->
format_rdn_sequence(Issuer)
end, Cert).
%% Return a string describing the certificate's subject, as per RFC4514.
-spec(peer_cert_subject(certificate()) -> string()).
peer_cert_subject(Cert) ->
cert_info(fun(#'OTPCertificate' {
tbsCertificate = #'OTPTBSCertificate' {
subject = Subject }}) ->
format_rdn_sequence(Subject)
end, Cert).
-spec(peer_cert_common_name(certificate()) -> string() | 'not_found').
peer_cert_common_name(Cert) ->
case peer_cert_subject_items(Cert, ?'id-at-commonName') of
not_found -> not_found;
CNs -> string:join(CNs, ",")
end.
%% Return the parts of the certificate's subject.
-spec(peer_cert_subject_items(certificate(), tuple()) -> [string()] | 'undefined').
peer_cert_subject_items(Cert, Type) ->
cert_info(fun(#'OTPCertificate' {
tbsCertificate = #'OTPTBSCertificate' {
subject = Subject }}) ->
find_by_type(Type, Subject)
end, Cert).
%% Return a string describing the certificate's validity.
-spec(peer_cert_validity(certificate()) -> string()).
peer_cert_validity(Cert) ->
cert_info(fun(#'OTPCertificate' {
tbsCertificate = #'OTPTBSCertificate' {
validity = {'Validity', Start, End} }}) ->
format("~s - ~s", [format_asn1_value(Start),
format_asn1_value(End)])
end, Cert).
cert_info(F, {ok, Cert}) ->
F(case public_key:pkix_decode_cert(Cert, otp) of
{ok, DecCert} -> DecCert; %%pre R14B
DecCert -> DecCert %%R14B onwards
end).
find_by_type(Type, {rdnSequence, RDNs}) ->
case [V || #'AttributeTypeAndValue'{type = T, value = V}
<- lists:flatten(RDNs),
T == Type] of
[] -> not_found;
L -> [format_asn1_value(V) || V <- L]
end.
%%--------------------------------------------------------------------------
%% Formatting functions.
%%--------------------------------------------------------------------------
%% Format and rdnSequence as a RFC4514 subject string.
format_rdn_sequence({rdnSequence, Seq}) ->
string:join(lists:reverse([format_complex_rdn(RDN) || RDN <- Seq]), ",").
%% Format an RDN set.
format_complex_rdn(RDNs) ->
string:join([format_rdn(RDN) || RDN <- RDNs], "+").
%% Format an RDN. If the type name is unknown, use the dotted decimal
%% representation. See RFC4514, section 2.3.
format_rdn(#'AttributeTypeAndValue'{type = T, value = V}) ->
FV = escape_rdn_value(format_asn1_value(V)),
Fmts = [{?'id-at-surname' , "SN"},
{?'id-at-givenName' , "GIVENNAME"},
{?'id-at-initials' , "INITIALS"},
{?'id-at-generationQualifier' , "GENERATIONQUALIFIER"},
{?'id-at-commonName' , "CN"},
{?'id-at-localityName' , "L"},
{?'id-at-stateOrProvinceName' , "ST"},
{?'id-at-organizationName' , "O"},
{?'id-at-organizationalUnitName' , "OU"},
{?'id-at-title' , "TITLE"},
{?'id-at-countryName' , "C"},
{?'id-at-serialNumber' , "SERIALNUMBER"},
{?'id-at-pseudonym' , "PSEUDONYM"},
{?'id-domainComponent' , "DC"},
{?'id-emailAddress' , "EMAILADDRESS"},
{?'street-address' , "STREET"},
{{0,9,2342,19200300,100,1,1} , "UID"}], %% Not in public_key.hrl
case proplists:lookup(T, Fmts) of
{_, Fmt} ->
format(Fmt ++ "=~s", [FV]);
none when is_tuple(T) ->
TypeL = [format("~w", [X]) || X <- tuple_to_list(T)],
format("~s=~s", [string:join(TypeL, "."), FV]);
none ->
format("~p=~s", [T, FV])
end.
%% Escape a string as per RFC4514.
escape_rdn_value(V) ->
escape_rdn_value(V, start).
escape_rdn_value([], _) ->
[];
escape_rdn_value([C | S], start) when C =:= $ ; C =:= $# ->
[$\\, C | escape_rdn_value(S, middle)];
escape_rdn_value(S, start) ->
escape_rdn_value(S, middle);
escape_rdn_value([$ ], middle) ->
[$\\, $ ];
escape_rdn_value([C | S], middle) when C =:= $"; C =:= $+; C =:= $,; C =:= $;;
C =:= $<; C =:= $>; C =:= $\\ ->
[$\\, C | escape_rdn_value(S, middle)];
escape_rdn_value([C | S], middle) when C < 32 ; C >= 126 ->
%% Of ASCII characters only U+0000 needs escaping, but for display
%% purposes it's handy to escape all non-printable chars. All non-ASCII
%% characters get converted to UTF-8 sequences and then escaped. We've
%% already got a UTF-8 sequence here, so just escape it.
rabbit_misc:format("\\~2.16.0B", [C]) ++ escape_rdn_value(S, middle);
escape_rdn_value([C | S], middle) ->
[C | escape_rdn_value(S, middle)].
%% Get the string representation of an OTPCertificate field.
format_asn1_value({ST, S}) when ST =:= teletexString; ST =:= printableString;
ST =:= universalString; ST =:= utf8String;
ST =:= bmpString ->
format_directory_string(ST, S);
format_asn1_value({utcTime, [Y1, Y2, M1, M2, D1, D2, H1, H2,
Min1, Min2, S1, S2, $Z]}) ->
format("20~c~c-~c~c-~c~cT~c~c:~c~c:~c~cZ",
[Y1, Y2, M1, M2, D1, D2, H1, H2, Min1, Min2, S1, S2]);
%% We appear to get an untagged value back for an ia5string
%% (e.g. domainComponent).
format_asn1_value(V) when is_list(V) ->
V;
format_asn1_value(V) when is_binary(V) ->
%% OTP does not decode some values when combined with an unknown
%% type. That's probably wrong, so as a last ditch effort let's
%% try manually decoding. 'DirectoryString' is semi-arbitrary -
%% but it is the type which covers the various string types we
%% handle below.
try
{ST, S} = public_key:der_decode('DirectoryString', V),
format_directory_string(ST, S)
catch _:_ ->
format("~p", [V])
end;
format_asn1_value(V) ->
format("~p", [V]).
%% DirectoryString { INTEGER : maxSize } ::= CHOICE {
%% teletexString TeletexString (SIZE (1..maxSize)),
%% printableString PrintableString (SIZE (1..maxSize)),
%% bmpString BMPString (SIZE (1..maxSize)),
%% universalString UniversalString (SIZE (1..maxSize)),
%% uTF8String UTF8String (SIZE (1..maxSize)) }
%%
%% Precise definitions of printable / teletexString are hard to come
%% by. This is what I reconstructed:
%%
%% printableString:
%% "intended to represent the limited character sets available to
%% mainframe input terminals"
%% A-Z a-z 0-9 ' ( ) + , - . / : = ? [space]
%% http://msdn.microsoft.com/en-us/library/bb540814(v=vs.85).aspx
%%
%% teletexString:
%% "a sizable volume of software in the world treats TeletexString
%% (T61String) as a simple 8-bit string with mostly Windows Latin 1
%% (superset of iso-8859-1) encoding"
%% http://www.mail-archive.com/asn1@asn1.org/msg00460.html
%%
%% (However according to that link X.680 actually defines
%% TeletexString in some much more involved and crazy way. I suggest
%% we treat it as ISO-8859-1 since Erlang does not support Windows
%% Latin 1).
%%
%% bmpString:
%% UCS-2 according to RFC 3641. Hence cannot represent Unicode
%% characters above 65535 (outside the "Basic Multilingual Plane").
%%
%% universalString:
%% UCS-4 according to RFC 3641.
%%
%% utf8String:
%% UTF-8 according to RFC 3641.
%%
%% Within Rabbit we assume UTF-8 encoding. Since printableString is a
%% subset of ASCII it is also a subset of UTF-8. The others need
%% converting. Fortunately since the Erlang SSL library does the
%% decoding for us (albeit into a weird format, see below), we just
%% need to handle encoding into UTF-8. Note also that utf8Strings come
%% back as binary.
%%
%% Note for testing: the default Ubuntu configuration for openssl will
%% only create printableString or teletexString types no matter what
%% you do. Edit string_mask in the [req] section of
%% /etc/ssl/openssl.cnf to change this (see comments there). You
%% probably also need to set utf8 = yes to get it to accept UTF-8 on
%% the command line. Also note I could not get openssl to generate a
%% universalString.
format_directory_string(printableString, S) -> S;
format_directory_string(teletexString, S) -> utf8_list_from(S);
format_directory_string(bmpString, S) -> utf8_list_from(S);
format_directory_string(universalString, S) -> utf8_list_from(S);
format_directory_string(utf8String, S) -> binary_to_list(S).
utf8_list_from(S) ->
binary_to_list(
unicode:characters_to_binary(flatten_ssl_list(S), utf32, utf8)).
%% The Erlang SSL implementation invents its own representation for
%% non-ascii strings - looking like [97,{0,0,3,187}] (that's LATIN
%% SMALL LETTER A followed by GREEK SMALL LETTER LAMDA). We convert
%% this into a list of unicode characters, which we can tell
%% unicode:characters_to_binary is utf32.
flatten_ssl_list(L) -> [flatten_ssl_list_item(I) || I <- L].
flatten_ssl_list_item({A, B, C, D}) ->
A * (1 bsl 24) + B * (1 bsl 16) + C * (1 bsl 8) + D;
flatten_ssl_list_item(N) when is_number (N) ->
N.
format(Fmt, Args) ->
lists:flatten(io_lib:format(Fmt, Args)).

View File

@ -14,13 +14,13 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_stats). -module(emqx_stats).
-behaviour(gen_server). -behaviour(gen_server).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
-export([start_link/0, stop/0]). -export([start_link/0, stop/0]).
@ -87,7 +87,7 @@ stop() ->
-spec(set_client_stats(binary(), stats()) -> true). -spec(set_client_stats(binary(), stats()) -> true).
set_client_stats(ClientId, Stats) -> set_client_stats(ClientId, Stats) ->
ets:insert(?CLIENT_STATS_TAB, {ClientId, [{'$ts', emqttd_time:now_secs()}|Stats]}). ets:insert(?CLIENT_STATS_TAB, {ClientId, [{'$ts', emqx_time:now_secs()}|Stats]}).
-spec(get_client_stats(binary()) -> stats()). -spec(get_client_stats(binary()) -> stats()).
get_client_stats(ClientId) -> get_client_stats(ClientId) ->
@ -102,7 +102,7 @@ del_client_stats(ClientId) ->
-spec(set_session_stats(binary(), stats()) -> true). -spec(set_session_stats(binary(), stats()) -> true).
set_session_stats(ClientId, Stats) -> set_session_stats(ClientId, Stats) ->
ets:insert(?SESSION_STATS_TAB, {ClientId, [{'$ts', emqttd_time:now_secs()}|Stats]}). ets:insert(?SESSION_STATS_TAB, {ClientId, [{'$ts', emqx_time:now_secs()}|Stats]}).
-spec(get_session_stats(binary()) -> stats()). -spec(get_session_stats(binary()) -> stats()).
get_session_stats(ClientId) -> get_session_stats(ClientId) ->
@ -152,7 +152,7 @@ setstats(Stat, MaxStat, Val) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
init([]) -> init([]) ->
emqttd_time:seed(), emqx_time:seed(),
lists:foreach( lists:foreach(
fun(Tab) -> fun(Tab) ->
Tab = ets:new(Tab, [set, public, named_table, {write_concurrency, true}]) Tab = ets:new(Tab, [set, public, named_table, {write_concurrency, true}])
@ -160,7 +160,7 @@ init([]) ->
Topics = ?SYSTOP_CLIENTS ++ ?SYSTOP_SESSIONS ++ ?SYSTOP_PUBSUB ++ ?SYSTOP_RETAINED, Topics = ?SYSTOP_CLIENTS ++ ?SYSTOP_SESSIONS ++ ?SYSTOP_PUBSUB ++ ?SYSTOP_RETAINED,
ets:insert(?STATS_TAB, [{Topic, 0} || Topic <- Topics]), ets:insert(?STATS_TAB, [{Topic, 0} || Topic <- Topics]),
% Tick to publish stats % Tick to publish stats
{ok, #state{tick = emqttd_broker:start_tick(tick)}, hibernate}. {ok, #state{tick = emqx_broker:start_tick(tick)}, hibernate}.
handle_call(stop, _From, State) -> handle_call(stop, _From, State) ->
{stop, normal, ok, State}; {stop, normal, ok, State};
@ -191,7 +191,7 @@ handle_info(_Info, State) ->
{noreply, State}. {noreply, State}.
terminate(_Reason, #state{tick = TRef}) -> terminate(_Reason, #state{tick = TRef}) ->
emqttd_broker:stop_tick(TRef). emqx_broker:stop_tick(TRef).
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
{ok, State}. {ok, State}.
@ -201,11 +201,11 @@ code_change(_OldVsn, State, _Extra) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
publish(Stat, Val) -> publish(Stat, Val) ->
Msg = emqttd_message:make(stats, stats_topic(Stat), bin(Val)), Msg = emqx_message:make(stats, stats_topic(Stat), bin(Val)),
emqttd:publish(emqttd_message:set_flag(sys, Msg)). emqx:publish(emqx_message:set_flag(sys, Msg)).
stats_topic(Stat) -> stats_topic(Stat) ->
emqttd_topic:systop(list_to_binary(lists:concat(['stats/', Stat]))). emqx_topic:systop(list_to_binary(lists:concat(['stats/', Stat]))).
bin(I) when is_integer(I) -> list_to_binary(integer_to_list(I)). bin(I) when is_integer(I) -> list_to_binary(integer_to_list(I)).

83
src/emqx_sup.erl Normal file
View File

@ -0,0 +1,83 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io)
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_sup).
-behaviour(supervisor).
-author("Feng Lee <feng@emqtt.io>").
-export([start_link/0, start_child/1, start_child/2, stop_child/1]).
%% Supervisor callbacks
-export([init/1]).
-type(startchild_ret() :: {ok, supervisor:child()}
| {ok, supervisor:child(), term()}
| {error, term()}).
-define(SUPERVISOR, ?MODULE).
-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).
start_link() ->
supervisor:start_link({local, ?SUPERVISOR}, ?MODULE, []).
-spec(start_child(atom(), worker | supervisor) -> startchild_ret()).
start_child(Mod, Type) when Type == worker orelse Type == supervisor ->
start_child(?CHILD(Mod, Type)).
-spec(start_child(supervisor:child_spec()) -> startchild_ret()).
start_child(ChildSpec) when is_tuple(ChildSpec) ->
supervisor:start_child(?SUPERVISOR, ChildSpec).
-spec(start_child(Mod::atom(), Type :: worker | supervisor) -> {ok, pid()}).
start_child(Mod, Type) when is_atom(Mod) and is_atom(Type) ->
supervisor:start_child(?MODULE, ?CHILD(Mod, Type)).
-spec(stop_child(supervisor:child_id()) -> ok | {error, any()}).
stop_child(ChildId) ->
case supervisor:terminate_child(?SUPERVISOR, ChildId) of
ok -> supervisor:delete_child(?SUPERVISOR, ChildId);
Error -> Error
end.
%%--------------------------------------------------------------------
%% Supervisor callbacks
%%--------------------------------------------------------------------
init([]) ->
{ok, {{one_for_all, 0, 1},
[?CHILD(emqx_ctl, worker),
?CHILD(emqx_hooks, worker),
?CHILD(emqx_router, worker),
?CHILD(emqx_pubsub_sup, supervisor),
?CHILD(emqx_stats, worker),
?CHILD(emqx_metrics, worker),
?CHILD(emqx_pooler, supervisor),
?CHILD(emqx_trace_sup, supervisor),
?CHILD(emqx_cm_sup, supervisor),
?CHILD(emqx_sm_sup, supervisor),
?CHILD(emqx_session_sup, supervisor),
?CHILD(emqx_ws_client_sup, supervisor),
?CHILD(emqx_broker, worker),
?CHILD(emqx_alarm, worker),
?CHILD(emqx_mod_sup, supervisor),
?CHILD(emqx_bridge_sup_sup, supervisor),
?CHILD(emqx_access_control, worker),
?CHILD(emqx_sysmon_sup, supervisor)]
}}.

View File

@ -14,14 +14,13 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @doc VM System Monitor -module(emqx_sysmon).
-module(emqttd_sysmon).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-behavior(gen_server). -behavior(gen_server).
-include("emqttd_internal.hrl"). -include("emqx_internal.hrl").
-export([start_link/1]). -export([start_link/1]).
@ -143,26 +142,24 @@ code_change(_OldVsn, State, _Extra) ->
suppress(Key, SuccFun, State = #state{events = Events}) -> suppress(Key, SuccFun, State = #state{events = Events}) ->
case lists:member(Key, Events) of case lists:member(Key, Events) of
true -> true -> {noreply, State};
{noreply, State}; false -> SuccFun(),
false -> {noreply, State#state{events = [Key|Events]}}
SuccFun(),
{noreply, State#state{events = [Key|Events]}}
end. end.
procinfo(Pid) -> procinfo(Pid) ->
case {emqttd_vm:get_process_info(Pid), emqttd_vm:get_process_gc(Pid)} of case {emqx_vm:get_process_info(Pid), emqx_vm:get_process_gc(Pid)} of
{undefined, _} -> undefined; {undefined, _} -> undefined;
{_, undefined} -> undefined; {_, undefined} -> undefined;
{Info, GcInfo} -> Info ++ GcInfo {Info, GcInfo} -> Info ++ GcInfo
end. end.
publish(Sysmon, WarnMsg) -> publish(Sysmon, WarnMsg) ->
Msg = emqttd_message:make(sysmon, topic(Sysmon), iolist_to_binary(WarnMsg)), Msg = emqx_message:make(sysmon, topic(Sysmon), iolist_to_binary(WarnMsg)),
emqttd:publish(emqttd_message:set_flag(sys, Msg)). emqx:publish(emqx_message:set_flag(sys, Msg)).
topic(Sysmon) -> topic(Sysmon) ->
emqttd_topic:systop(list_to_binary(lists:concat(['sysmon/', Sysmon]))). emqx_topic:systop(list_to_binary(lists:concat(['sysmon/', Sysmon]))).
%% start_tracelog(undefined) -> %% start_tracelog(undefined) ->
%% {ok, undefined}; %% {ok, undefined};

View File

@ -14,7 +14,9 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_sysmon_sup). -module(emqx_sysmon_sup).
-author("Feng Lee <feng@emqtt.io>").
-behaviour(supervisor). -behaviour(supervisor).
@ -28,8 +30,8 @@ start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []). supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) -> init([]) ->
{ok, Env} = emqttd:env(sysmon), {ok, Env} = emqx:env(sysmon),
Sysmon = {sysmon, {emqttd_sysmon, start_link, [Env]}, Sysmon = {sysmon, {emqx_sysmon, start_link, [Env]},
permanent, 5000, worker, [emqttd_sysmon]}, permanent, 5000, worker, [emqx_sysmon]},
{ok, {{one_for_one, 10, 100}, [Sysmon]}}. {ok, {{one_for_one, 10, 100}, [Sysmon]}}.

View File

@ -14,7 +14,7 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_time). -module(emqx_time).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").

View File

@ -14,13 +14,13 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_topic). -module(emqx_topic).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd_protocol.hrl"). -include("emqx_mqtt.hrl").
-include("emqttd_internal.hrl"). -include("emqx_internal.hrl").
-import(lists, [reverse/1]). -import(lists, [reverse/1]).
@ -186,6 +186,11 @@ parse(<<"$local/", Topic1/binary>>, Options) ->
parse(Topic1, [local | Options]) parse(Topic1, [local | Options])
end); end);
parse(<<"$fastlane/", Topic1/binary>>, Options) ->
if_not_contain(fastlane, Options, fun() ->
parse(Topic1, [fastlane | Options])
end);
parse(<<"$queue/", Topic1/binary>>, Options) -> parse(<<"$queue/", Topic1/binary>>, Options) ->
if_not_contain(share, Options,fun() -> if_not_contain(share, Options,fun() ->
parse(Topic1, [{share, '$queue'} | Options]) parse(Topic1, [{share, '$queue'} | Options])
@ -200,8 +205,8 @@ parse(<<"$share/", Topic1/binary>>, Options) ->
parse(Topic, Options) -> parse(Topic, Options) ->
{Topic, Options}. {Topic, Options}.
if_not_contain(local, Options, Fun) -> if_not_contain(Key, Options, Fun) when Key == local; Key == fastlane ->
?IF(lists:member(local, Options), error(invalid_topic), Fun()); ?IF(lists:member(Key, Options), error(invalid_topic), Fun());
if_not_contain(share, Options, Fun) -> if_not_contain(share, Options, Fun) ->
?IF(lists:keyfind(share, 1, Options), error(invalid_topic), Fun()). ?IF(lists:keyfind(share, 1, Options), error(invalid_topic), Fun()).

View File

@ -14,14 +14,13 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @docTrace MQTT packets/messages by ClientID or Topic. -module(emqx_trace).
-module(emqttd_trace).
-behaviour(gen_server). -behaviour(gen_server).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd_internal.hrl"). -include("emqx_internal.hrl").
%% API Function Exports %% API Function Exports
-export([start_link/0]). -export([start_link/0]).

View File

@ -14,7 +14,7 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_trace_sup). -module(emqx_trace_sup).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
@ -30,7 +30,7 @@ start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []). supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) -> init([]) ->
Trace = {trace, {emqttd_trace, start_link, []}, Trace = {trace, {emqx_trace, start_link, []},
permanent, 5000, worker, [emqttd_trace]}, permanent, 5000, worker, [emqx_trace]},
{ok, {{one_for_one, 10, 3600}, [Trace]}}. {ok, {{one_for_one, 10, 3600}, [Trace]}}.

View File

@ -18,11 +18,11 @@
%% [Trie](http://en.wikipedia.org/wiki/Trie) %% [Trie](http://en.wikipedia.org/wiki/Trie)
%% @end %% @end
-module(emqttd_trie). -module(emqx_trie).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd_trie.hrl"). -include("emqx_trie.hrl").
%% Mnesia Callbacks %% Mnesia Callbacks
-export([mnesia/1]). -export([mnesia/1]).
@ -71,7 +71,7 @@ insert(Topic) when is_binary(Topic) ->
write_trie_node(TrieNode#trie_node{topic=Topic}); write_trie_node(TrieNode#trie_node{topic=Topic});
[] -> [] ->
% Add trie path % Add trie path
lists:foreach(fun add_path/1, emqttd_topic:triples(Topic)), lists:foreach(fun add_path/1, emqx_topic:triples(Topic)),
% Add last node % Add last node
write_trie_node(#trie_node{node_id=Topic, topic=Topic}) write_trie_node(#trie_node{node_id=Topic, topic=Topic})
end. end.
@ -79,7 +79,7 @@ insert(Topic) when is_binary(Topic) ->
%% @doc Find trie nodes that match topic %% @doc Find trie nodes that match topic
-spec(match(Topic :: binary()) -> list(MatchedTopic :: binary())). -spec(match(Topic :: binary()) -> list(MatchedTopic :: binary())).
match(Topic) when is_binary(Topic) -> match(Topic) when is_binary(Topic) ->
TrieNodes = match_node(root, emqttd_topic:words(Topic)), TrieNodes = match_node(root, emqx_topic:words(Topic)),
[Name || #trie_node{topic=Name} <- TrieNodes, Name =/= undefined]. [Name || #trie_node{topic=Name} <- TrieNodes, Name =/= undefined].
%% @doc Lookup a Trie Node %% @doc Lookup a Trie Node
@ -93,7 +93,7 @@ delete(Topic) when is_binary(Topic) ->
case mnesia:read(mqtt_trie_node, Topic) of case mnesia:read(mqtt_trie_node, Topic) of
[#trie_node{edge_count=0}] -> [#trie_node{edge_count=0}] ->
mnesia:delete({mqtt_trie_node, Topic}), mnesia:delete({mqtt_trie_node, Topic}),
delete_path(lists:reverse(emqttd_topic:triples(Topic))); delete_path(lists:reverse(emqx_topic:triples(Topic)));
[TrieNode] -> [TrieNode] ->
write_trie_node(TrieNode#trie_node{topic = undefined}); write_trie_node(TrieNode#trie_node{topic = undefined});
[] -> [] ->

View File

@ -14,7 +14,7 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_vm). -module(emqx_vm).
-export([schedulers/0]). -export([schedulers/0]).

View File

@ -14,11 +14,11 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_ws). -module(emqx_ws).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd_protocol.hrl"). -include("emqx_protocol.hrl").
-import(proplists, [get_value/3]). -import(proplists, [get_value/3]).
@ -44,12 +44,12 @@ handle_request('GET', "/mqtt", Req) ->
Proto = check_protocol_header(Req), Proto = check_protocol_header(Req),
case {is_websocket(Upgrade), Proto} of case {is_websocket(Upgrade), Proto} of
{true, "mqtt" ++ _Vsn} -> {true, "mqtt" ++ _Vsn} ->
{ok, ProtoEnv} = emqttd:env(protocol), {ok, ProtoEnv} = emqx:env(protocol),
PacketSize = get_value(max_packet_size, ProtoEnv, ?MAX_PACKET_SIZE), PacketSize = get_value(max_packet_size, ProtoEnv, ?MAX_PACKET_SIZE),
Parser = emqttd_parser:initial_state(PacketSize), Parser = emqx_parser:initial_state(PacketSize),
%% Upgrade WebSocket. %% Upgrade WebSocket.
{ReentryWs, ReplyChannel} = mochiweb_websocket:upgrade_connection(Req, fun ?MODULE:ws_loop/3), {ReentryWs, ReplyChannel} = mochiweb_websocket:upgrade_connection(Req, fun ?MODULE:ws_loop/3),
{ok, ClientPid} = emqttd_ws_client_sup:start_client(self(), Req, ReplyChannel), {ok, ClientPid} = emqx_ws_client_sup:start_client(self(), Req, ReplyChannel),
ReentryWs(#wsocket_state{peername = Req:get(peername), parser = Parser, ReentryWs(#wsocket_state{peername = Req:get(peername), parser = Parser,
max_packet_size = PacketSize, client_pid = ClientPid}); max_packet_size = PacketSize, client_pid = ClientPid});
{false, _} -> {false, _} ->
@ -68,7 +68,7 @@ is_websocket(Upgrade) ->
Upgrade =/= undefined andalso string:to_lower(Upgrade) =:= "websocket". Upgrade =/= undefined andalso string:to_lower(Upgrade) =:= "websocket".
check_protocol_header(Req) -> check_protocol_header(Req) ->
case emqttd:env(websocket_protocol_header, false) of case emqx:env(websocket_protocol_header, false) of
true -> get_protocol_header(Req); true -> get_protocol_header(Req);
false -> "mqtt-v3.1.1" false -> "mqtt-v3.1.1"
end. end.
@ -90,8 +90,8 @@ ws_loop([<<>>], State, _ReplyChannel) ->
State; State;
ws_loop(Data, State = #wsocket_state{client_pid = ClientPid, parser = Parser}, ReplyChannel) -> ws_loop(Data, State = #wsocket_state{client_pid = ClientPid, parser = Parser}, ReplyChannel) ->
?WSLOG(debug, "RECV ~p", [Data], State), ?WSLOG(debug, "RECV ~p", [Data], State),
emqttd_metrics:inc('bytes/received', iolist_size(Data)), emqx_metrics:inc('bytes/received', iolist_size(Data)),
case catch emqttd_parser:parse(iolist_to_binary(Data), Parser) of case catch emqx_parser:parse(iolist_to_binary(Data), Parser) of
{more, NewParser} -> {more, NewParser} ->
State#wsocket_state{parser = NewParser}; State#wsocket_state{parser = NewParser};
{ok, Packet, Rest} -> {ok, Packet, Rest} ->
@ -107,5 +107,5 @@ ws_loop(Data, State = #wsocket_state{client_pid = ClientPid, parser = Parser}, R
end. end.
reset_parser(State = #wsocket_state{max_packet_size = PacketSize}) -> reset_parser(State = #wsocket_state{max_packet_size = PacketSize}) ->
State#wsocket_state{parser = emqttd_parser:initial_state(PacketSize)}. State#wsocket_state{parser = emqx_parser:initial_state(PacketSize)}.

View File

@ -16,19 +16,19 @@
%% @doc MQTT WebSocket Connection. %% @doc MQTT WebSocket Connection.
-module(emqttd_ws_client). -module(emqx_ws_client).
-behaviour(gen_server2). -behaviour(gen_server2).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
-include("emqttd.hrl"). -include("emqx.hrl").
-include("emqttd_protocol.hrl"). -include("emqx_mqtt.hrl").
-include("emqttd_internal.hrl"). -include("emqx_internal.hrl").
-import(proplists, [get_value/3]). -import(proplists, [get_value/2, get_value/3]).
%% API Exports %% API Exports
-export([start_link/4]). -export([start_link/4]).
@ -96,11 +96,11 @@ init([Env, WsPid, Req, ReplyChannel]) ->
Headers = mochiweb_headers:to_list( Headers = mochiweb_headers:to_list(
mochiweb_request:get(headers, Req)), mochiweb_request:get(headers, Req)),
Conn = Req:get(connection), Conn = Req:get(connection),
ProtoState = emqttd_protocol:init(Conn, Peername, send_fun(ReplyChannel), ProtoState = emqx_protocol:init(Conn, Peername, send_fun(ReplyChannel),
[{ws_initial_headers, Headers} | Env]), [{ws_initial_headers, Headers} | Env]),
IdleTimeout = get_value(client_idle_timeout, Env, 30000), IdleTimeout = get_value(client_idle_timeout, Env, 30000),
EnableStats = get_value(client_enable_stats, Env, false), EnableStats = get_value(client_enable_stats, Env, false),
ForceGcCount = emqttd_gc:conn_max_gc_count(), ForceGcCount = emqx_gc:conn_max_gc_count(),
{ok, #wsclient_state{connection = Conn, {ok, #wsclient_state{connection = Conn,
ws_pid = WsPid, ws_pid = WsPid,
peername = Peername, peername = Peername,
@ -117,24 +117,24 @@ prioritise_info(Msg, _Len, _State) ->
handle_pre_hibernate(State = #wsclient_state{ws_pid = WsPid}) -> handle_pre_hibernate(State = #wsclient_state{ws_pid = WsPid}) ->
erlang:garbage_collect(WsPid), erlang:garbage_collect(WsPid),
{hibernate, emqttd_gc:reset_conn_gc_count(#wsclient_state.force_gc_count, emit_stats(State))}. {hibernate, emqx_gc:reset_conn_gc_count(#wsclient_state.force_gc_count, emit_stats(State))}.
handle_call(info, From, State = #wsclient_state{peername = Peername, handle_call(info, From, State = #wsclient_state{peername = Peername,
proto_state = ProtoState}) -> proto_state = ProtoState}) ->
Info = [{websocket, true}, {peername, Peername} | emqttd_protocol:info(ProtoState)], Info = [{websocket, true}, {peername, Peername} | emqx_protocol:info(ProtoState)],
{reply, Stats, _, _} = handle_call(stats, From, State), {reply, Stats, _, _} = handle_call(stats, From, State),
reply(lists:append(Info, Stats), State); reply(lists:append(Info, Stats), State);
handle_call(stats, _From, State = #wsclient_state{proto_state = ProtoState}) -> handle_call(stats, _From, State = #wsclient_state{proto_state = ProtoState}) ->
reply(lists:append([emqttd_misc:proc_stats(), reply(lists:append([emqx_misc:proc_stats(),
wsock_stats(State), wsock_stats(State),
emqttd_protocol:stats(ProtoState)]), State); emqx_protocol:stats(ProtoState)]), State);
handle_call(kick, _From, State) -> handle_call(kick, _From, State) ->
{stop, {shutdown, kick}, ok, State}; {stop, {shutdown, kick}, ok, State};
handle_call(session, _From, State = #wsclient_state{proto_state = ProtoState}) -> handle_call(session, _From, State = #wsclient_state{proto_state = ProtoState}) ->
reply(emqttd_protocol:session(ProtoState), State); reply(emqx_protocol:session(ProtoState), State);
handle_call({clean_acl_cache, Topic}, _From, State) -> handle_call({clean_acl_cache, Topic}, _From, State) ->
erase({acl, publish, Topic}), erase({acl, publish, Topic}),
@ -145,8 +145,8 @@ handle_call(Req, _From, State) ->
reply({error, unexpected_request}, State). reply({error, unexpected_request}, State).
handle_cast({received, Packet}, State = #wsclient_state{proto_state = ProtoState}) -> handle_cast({received, Packet}, State = #wsclient_state{proto_state = ProtoState}) ->
emqttd_metrics:received(Packet), emqx_metrics:received(Packet),
case emqttd_protocol:received(Packet, ProtoState) of case emqx_protocol:received(Packet, ProtoState) of
{ok, ProtoState1} -> {ok, ProtoState1} ->
{noreply, gc(State#wsclient_state{proto_state = ProtoState1}), hibernate}; {noreply, gc(State#wsclient_state{proto_state = ProtoState1}), hibernate};
{error, Error} -> {error, Error} ->
@ -165,32 +165,36 @@ handle_cast(Msg, State) ->
handle_info({subscribe, TopicTable}, State) -> handle_info({subscribe, TopicTable}, State) ->
with_proto( with_proto(
fun(ProtoState) -> fun(ProtoState) ->
emqttd_protocol:subscribe(TopicTable, ProtoState) emqx_protocol:subscribe(TopicTable, ProtoState)
end, State); end, State);
handle_info({unsubscribe, Topics}, State) -> handle_info({unsubscribe, Topics}, State) ->
with_proto( with_proto(
fun(ProtoState) -> fun(ProtoState) ->
emqttd_protocol:unsubscribe(Topics, ProtoState) emqx_protocol:unsubscribe(Topics, ProtoState)
end, State); end, State);
handle_info({suback, PacketId, GrantedQos}, State) -> handle_info({suback, PacketId, GrantedQos}, State) ->
with_proto( with_proto(
fun(ProtoState) -> fun(ProtoState) ->
Packet = ?SUBACK_PACKET(PacketId, GrantedQos), Packet = ?SUBACK_PACKET(PacketId, GrantedQos),
emqttd_protocol:send(Packet, ProtoState) emqx_protocol:send(Packet, ProtoState)
end, State); end, State);
%% Fastlane
handle_info({dispatch, _Topic, Message}, State) ->
handle_info({deliver, Message#mqtt_message{qos = ?QOS_0}}, State);
handle_info({deliver, Message}, State) -> handle_info({deliver, Message}, State) ->
with_proto( with_proto(
fun(ProtoState) -> fun(ProtoState) ->
emqttd_protocol:send(Message, ProtoState) emqx_protocol:send(Message, ProtoState)
end, gc(State)); end, gc(State));
handle_info({redeliver, {?PUBREL, PacketId}}, State) -> handle_info({redeliver, {?PUBREL, PacketId}}, State) ->
with_proto( with_proto(
fun(ProtoState) -> fun(ProtoState) ->
emqttd_protocol:pubrel(PacketId, ProtoState) emqx_protocol:pubrel(PacketId, ProtoState)
end, State); end, State);
handle_info(emit_stats, State) -> handle_info(emit_stats, State) ->
@ -205,7 +209,7 @@ handle_info({shutdown, conflict, {ClientId, NewPid}}, State) ->
handle_info({keepalive, start, Interval}, State = #wsclient_state{connection = Conn}) -> handle_info({keepalive, start, Interval}, State = #wsclient_state{connection = Conn}) ->
?WSLOG(debug, "Keepalive at the interval of ~p", [Interval], State), ?WSLOG(debug, "Keepalive at the interval of ~p", [Interval], State),
case emqttd_keepalive:start(stat_fun(Conn), Interval, {keepalive, check}) of case emqx_keepalive:start(stat_fun(Conn), Interval, {keepalive, check}) of
{ok, KeepAlive} -> {ok, KeepAlive} ->
{noreply, State#wsclient_state{keepalive = KeepAlive}, hibernate}; {noreply, State#wsclient_state{keepalive = KeepAlive}, hibernate};
{error, Error} -> {error, Error} ->
@ -214,7 +218,7 @@ handle_info({keepalive, start, Interval}, State = #wsclient_state{connection = C
end; end;
handle_info({keepalive, check}, State = #wsclient_state{keepalive = KeepAlive}) -> handle_info({keepalive, check}, State = #wsclient_state{keepalive = KeepAlive}) ->
case emqttd_keepalive:check(KeepAlive) of case emqx_keepalive:check(KeepAlive) of
{ok, KeepAlive1} -> {ok, KeepAlive1} ->
{noreply, emit_stats(State#wsclient_state{keepalive = KeepAlive1}), hibernate}; {noreply, emit_stats(State#wsclient_state{keepalive = KeepAlive1}), hibernate};
{error, timeout} -> {error, timeout} ->
@ -234,7 +238,7 @@ handle_info({'EXIT', WsPid, Reason}, State = #wsclient_state{ws_pid = WsPid}) ->
%% The session process exited unexpectedly. %% The session process exited unexpectedly.
handle_info({'EXIT', Pid, Reason}, State = #wsclient_state{proto_state = ProtoState}) -> handle_info({'EXIT', Pid, Reason}, State = #wsclient_state{proto_state = ProtoState}) ->
case emqttd_protocol:session(ProtoState) of case emqx_protocol:session(ProtoState) of
Pid -> stop(Reason, State); Pid -> stop(Reason, State);
_ -> ?WSLOG(error, "Unexpected EXIT: ~p, Reason: ~p", [Pid, Reason], State), _ -> ?WSLOG(error, "Unexpected EXIT: ~p, Reason: ~p", [Pid, Reason], State),
{noreply, State, hibernate} {noreply, State, hibernate}
@ -245,12 +249,12 @@ handle_info(Info, State) ->
{noreply, State, hibernate}. {noreply, State, hibernate}.
terminate(Reason, #wsclient_state{proto_state = ProtoState, keepalive = KeepAlive}) -> terminate(Reason, #wsclient_state{proto_state = ProtoState, keepalive = KeepAlive}) ->
emqttd_keepalive:cancel(KeepAlive), emqx_keepalive:cancel(KeepAlive),
case Reason of case Reason of
{shutdown, Error} -> {shutdown, Error} ->
emqttd_protocol:shutdown(Error, ProtoState); emqx_protocol:shutdown(Error, ProtoState);
_ -> _ ->
emqttd_protocol:shutdown(Reason, ProtoState) emqx_protocol:shutdown(Reason, ProtoState)
end. end.
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
@ -262,8 +266,8 @@ code_change(_OldVsn, State, _Extra) ->
send_fun(ReplyChannel) -> send_fun(ReplyChannel) ->
fun(Packet) -> fun(Packet) ->
Data = emqttd_serializer:serialize(Packet), Data = emqx_serializer:serialize(Packet),
emqttd_metrics:inc('bytes/sent', iolist_size(Data)), emqx_metrics:inc('bytes/sent', iolist_size(Data)),
ReplyChannel({binary, Data}) ReplyChannel({binary, Data})
end. end.
@ -276,7 +280,7 @@ stat_fun(Conn) ->
end. end.
emit_stats(State = #wsclient_state{proto_state = ProtoState}) -> emit_stats(State = #wsclient_state{proto_state = ProtoState}) ->
emit_stats(emqttd_protocol:clientid(ProtoState), State). emit_stats(emqx_protocol:clientid(ProtoState), State).
emit_stats(_ClientId, State = #wsclient_state{enable_stats = false}) -> emit_stats(_ClientId, State = #wsclient_state{enable_stats = false}) ->
State; State;
@ -284,7 +288,7 @@ emit_stats(undefined, State) ->
State; State;
emit_stats(ClientId, State) -> emit_stats(ClientId, State) ->
{reply, Stats, _, _} = handle_call(stats, undefined, State), {reply, Stats, _, _} = handle_call(stats, undefined, State),
emqttd_stats:set_client_stats(ClientId, Stats), emqx_stats:set_client_stats(ClientId, Stats),
State. State.
wsock_stats(#wsclient_state{connection = Conn}) -> wsock_stats(#wsclient_state{connection = Conn}) ->
@ -308,5 +312,5 @@ stop(Reason, State) ->
gc(State) -> gc(State) ->
Cb = fun() -> emit_stats(State) end, Cb = fun() -> emit_stats(State) end,
emqttd_gc:maybe_force_gc(#wsclient_state.force_gc_count, State, Cb). emqx_gc:maybe_force_gc(#wsclient_state.force_gc_count, State, Cb).

View File

@ -14,7 +14,7 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_ws_client_sup). -module(emqx_ws_client_sup).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
@ -39,8 +39,8 @@ start_client(WsPid, Req, ReplyChannel) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
init([]) -> init([]) ->
Env = lists:append(emqttd:env(client, []), emqttd:env(protocol, [])), Env = lists:append(emqx:env(client, []), emqx:env(protocol, [])),
{ok, {{simple_one_for_one, 0, 1}, {ok, {{simple_one_for_one, 0, 1},
[{ws_client, {emqttd_ws_client, start_link, [Env]}, [{ws_client, {emqx_ws_client, start_link, [Env]},
temporary, 5000, worker, [emqttd_ws_client]}]}}. temporary, 5000, worker, [emqx_ws_client]}]}}.

View File

@ -1,51 +0,0 @@
%%
%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io)
%%
-module(emqttd_inflight_SUITE).
-author("Feng Lee <feng@emqtt.io>").
-include_lib("eunit/include/eunit.hrl").
%% CT
-compile(export_all).
all() -> [t_contain, t_lookup, t_insert, t_update, t_delete, t_window,
t_is_full, t_is_empty].
t_contain(_) ->
Inflight = emqttd_inflight:new(0),
?assertNot(Inflight:contain(k)),
Inflight1 = Inflight:insert(k, v),
?assert(Inflight1:contain(k)).
t_lookup(_) ->
Inflight = (emqttd_inflight:new(0)):insert(k, v),
?assertEqual(v, Inflight:lookup(k)).
t_insert(_) ->
Inflight = ((emqttd_inflight:new(0)):insert(k1, v1)):insert(k2, v2),
?assertEqual(v2, Inflight:lookup(k2)).
t_update(_) ->
Inflight = ((emqttd_inflight:new(0)):insert(k, v1)):update(k, v2),
?assertEqual(v2, Inflight:lookup(k)).
t_delete(_) ->
Inflight = ((emqttd_inflight:new(0)):insert(k, v1)):delete(k),
?assert(Inflight:is_empty()).
t_window(_) ->
?assertEqual([], (emqttd_inflight:new(10)):window()),
Inflight = ((emqttd_inflight:new(0)):insert(1, 1)):insert(2, 2),
?assertEqual([1, 2], Inflight:window()).
t_is_full(_) ->
Inflight = ((emqttd_inflight:new(1)):insert(k, v1)),
?assert(Inflight:is_full()).
t_is_empty(_) ->
Inflight = ((emqttd_inflight:new(1)):insert(k, v1)),
?assertNot(Inflight:is_empty()).

View File

@ -14,18 +14,20 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_SUITE). -module(emqx_SUITE).
-compile(export_all). -compile(export_all).
-include("emqttd.hrl"). -include("emqx.hrl").
-include("emqx_mqtt.hrl").
-define(APP, emqx).
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl"). -include_lib("common_test/include/ct.hrl").
-define(APP, emqttd).
-define(CONTENT_TYPE, "application/json"). -define(CONTENT_TYPE, "application/json").
-define(MQTT_SSL_TWOWAY, [{cacertfile, "certs/cacert.pem"}, -define(MQTT_SSL_TWOWAY, [{cacertfile, "certs/cacert.pem"},
@ -67,15 +69,14 @@ all() ->
{group, http}, {group, http},
{group, alarms}, {group, alarms},
{group, cli}, {group, cli},
{group, get_api}, {group, rest_api},
{group, cleanSession}]. {group, cleanSession}].
groups() -> groups() ->
[{protocol, [sequence], [{protocol, [sequence],
[mqtt_connect, [mqtt_connect,
mqtt_ssl_twoway, mqtt_ssl_oneway,
mqtt_ssl_oneway mqtt_ssl_twoway]},
]},
{pubsub, [sequence], {pubsub, [sequence],
[subscribe_unsubscribe, [subscribe_unsubscribe,
publish, pubsub, publish, pubsub,
@ -121,18 +122,22 @@ groups() ->
conflict_listeners conflict_listeners
]}, ]},
cli_vm]}, cli_vm]},
{cleanSession, [sequence], {cleanSession, [sequence],
[cleanSession_validate]}, [cleanSession_validate,
{get_api, [sequence], [get_api_lists]}]. cleanSession_validate1]
},
{rest_api, [sequence], [get_api_lists]}
].
init_per_suite(Config) -> init_per_suite(Config) ->
NewConfig = generate_config(), NewConfig = generate_config(),
lists:foreach(fun set_app_env/1, NewConfig), lists:foreach(fun set_app_env/1, NewConfig),
application:ensure_all_started(?APP), Apps = application:ensure_all_started(?APP),
ct:log("Apps:~p", [Apps]),
Config. Config.
end_per_suite(_Config) -> end_per_suite(_Config) ->
emqttd:shutdown(). emqx:shutdown().
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Protocol Test %% Protocol Test
@ -153,12 +158,12 @@ connect_broker_(Packet, RecvSize) ->
Data. Data.
mqtt_ssl_oneway(_) -> mqtt_ssl_oneway(_) ->
emqttd:stop(), emqx:stop(),
change_opts(ssl_oneway), change_opts(ssl_oneway),
emqttd:start(), emqx:start(),
timer:sleep(6000),
{ok, SslOneWay} = emqttc:start_link([{host, "localhost"}, {ok, SslOneWay} = emqttc:start_link([{host, "localhost"},
{port, 8883}, {port, 8883},
{logger, debug},
{client_id, <<"ssloneway">>}, ssl]), {client_id, <<"ssloneway">>}, ssl]),
timer:sleep(100), timer:sleep(100),
emqttc:subscribe(SslOneWay, <<"topic">>, qos1), emqttc:subscribe(SslOneWay, <<"topic">>, qos1),
@ -174,13 +179,12 @@ mqtt_ssl_oneway(_) ->
emqttc:disconnect(SslOneWay), emqttc:disconnect(SslOneWay),
emqttc:disconnect(Pub). emqttc:disconnect(Pub).
mqtt_ssl_twoway(_) -> mqtt_ssl_twoway(_Config) ->
emqttd:stop(), emqx:stop(),
change_opts(ssl_twoway), change_opts(ssl_twoway),
emqttd:start(), emqx:start(),
timer:sleep(6000), timer:sleep(3000),
ClientSSl = [{Key, local_path(["etc", File])} || ClientSSl = [{Key, local_path(["etc", File])} || {Key, File} <- ?MQTT_SSL_CLIENT],
{Key, File} <- ?MQTT_SSL_CLIENT],
{ok, SslTwoWay} = emqttc:start_link([{host, "localhost"}, {ok, SslTwoWay} = emqttc:start_link([{host, "localhost"},
{port, 8883}, {port, 8883},
{client_id, <<"ssltwoway">>}, {client_id, <<"ssltwoway">>},
@ -202,82 +206,82 @@ mqtt_ssl_twoway(_) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
subscribe_unsubscribe(_) -> subscribe_unsubscribe(_) ->
ok = emqttd:subscribe(<<"topic">>, <<"clientId">>), ok = emqx:subscribe(<<"topic">>, <<"clientId">>),
ok = emqttd:subscribe(<<"topic/1">>, <<"clientId">>, [{qos, 1}]), ok = emqx:subscribe(<<"topic/1">>, <<"clientId">>, [{qos, 1}]),
ok = emqttd:subscribe(<<"topic/2">>, <<"clientId">>, [{qos, 2}]), ok = emqx:subscribe(<<"topic/2">>, <<"clientId">>, [{qos, 2}]),
ok = emqttd:unsubscribe(<<"topic">>, <<"clientId">>), ok = emqx:unsubscribe(<<"topic">>, <<"clientId">>),
ok = emqttd:unsubscribe(<<"topic/1">>, <<"clientId">>), ok = emqx:unsubscribe(<<"topic/1">>, <<"clientId">>),
ok = emqttd:unsubscribe(<<"topic/2">>, <<"clientId">>). ok = emqx:unsubscribe(<<"topic/2">>, <<"clientId">>).
publish(_) -> publish(_) ->
Msg = emqttd_message:make(ct, <<"test/pubsub">>, <<"hello">>), Msg = emqx_message:make(ct, <<"test/pubsub">>, <<"hello">>),
ok = emqttd:subscribe(<<"test/+">>), ok = emqx:subscribe(<<"test/+">>),
timer:sleep(10), timer:sleep(10),
emqttd:publish(Msg), emqx:publish(Msg),
?assert(receive {dispatch, <<"test/+">>, Msg} -> true after 5 -> false end). ?assert(receive {dispatch, <<"test/+">>, Msg} -> true after 5 -> false end).
pubsub(_) -> pubsub(_) ->
Self = self(), Self = self(),
ok = emqttd:subscribe(<<"a/b/c">>, Self, [{qos, 1}]), ok = emqx:subscribe(<<"a/b/c">>, Self, [{qos, 1}]),
?assertMatch({error, _}, emqttd:subscribe(<<"a/b/c">>, Self, [{qos, 2}])), ?assertMatch({error, _}, emqx:subscribe(<<"a/b/c">>, Self, [{qos, 2}])),
timer:sleep(10), timer:sleep(10),
[{Self, <<"a/b/c">>}] = ets:lookup(mqtt_subscription, Self), [{Self, <<"a/b/c">>}] = ets:lookup(mqtt_subscription, Self),
[{<<"a/b/c">>, Self}] = ets:lookup(mqtt_subscriber, <<"a/b/c">>), [{<<"a/b/c">>, Self}] = ets:lookup(mqtt_subscriber, <<"a/b/c">>),
emqttd:publish(emqttd_message:make(ct, <<"a/b/c">>, <<"hello">>)), emqx:publish(emqx_message:make(ct, <<"a/b/c">>, <<"hello">>)),
?assert(receive {dispatch, <<"a/b/c">>, _} -> true after 2 -> false end), ?assert(receive {dispatch, <<"a/b/c">>, _} -> true after 2 -> false end),
spawn(fun() -> spawn(fun() ->
emqttd:subscribe(<<"a/b/c">>), emqx:subscribe(<<"a/b/c">>),
emqttd:subscribe(<<"c/d/e">>), emqx:subscribe(<<"c/d/e">>),
timer:sleep(10), timer:sleep(10),
emqttd:unsubscribe(<<"a/b/c">>) emqx:unsubscribe(<<"a/b/c">>)
end), end),
timer:sleep(20), timer:sleep(20),
emqttd:unsubscribe(<<"a/b/c">>). emqx:unsubscribe(<<"a/b/c">>).
t_local_subscribe(_) -> t_local_subscribe(_) ->
emqttd:subscribe("$local/topic0"), emqx:subscribe("$local/topic0"),
emqttd:subscribe("$local/topic1", <<"x">>), emqx:subscribe("$local/topic1", <<"x">>),
emqttd:subscribe("$local/topic2", <<"x">>, [{qos, 2}]), emqx:subscribe("$local/topic2", <<"x">>, [{qos, 2}]),
timer:sleep(10), timer:sleep(10),
?assertEqual([self()], emqttd:subscribers("$local/topic0")), ?assertEqual([self()], emqx:subscribers("$local/topic0")),
?assertEqual([<<"x">>], emqttd:subscribers("$local/topic1")), ?assertEqual([<<"x">>], emqx:subscribers("$local/topic1")),
?assertEqual([{<<"$local/topic1">>,<<"x">>,[]},{<<"$local/topic2">>,<<"x">>,[{qos,2}]}], emqttd:subscriptions(<<"x">>)), ?assertEqual([{<<"$local/topic1">>,<<"x">>,[]},{<<"$local/topic2">>,<<"x">>,[{qos,2}]}], emqx:subscriptions(<<"x">>)),
?assertEqual(ok, emqttd:unsubscribe("$local/topic0")), ?assertEqual(ok, emqx:unsubscribe("$local/topic0")),
?assertMatch({error, {subscription_not_found, _}}, emqttd:unsubscribe("$local/topic0")), ?assertMatch({error, {subscription_not_found, _}}, emqx:unsubscribe("$local/topic0")),
?assertEqual(ok, emqttd:unsubscribe("$local/topic1", <<"x">>)), ?assertEqual(ok, emqx:unsubscribe("$local/topic1", <<"x">>)),
?assertEqual(ok, emqttd:unsubscribe("$local/topic2", <<"x">>)), ?assertEqual(ok, emqx:unsubscribe("$local/topic2", <<"x">>)),
?assertEqual([], emqttd:subscribers("topic1")), ?assertEqual([], emqx:subscribers("topic1")),
?assertEqual([], emqttd:subscriptions(<<"x">>)). ?assertEqual([], emqx:subscriptions(<<"x">>)).
t_shared_subscribe(_) -> t_shared_subscribe(_) ->
emqttd:subscribe("$local/$share/group1/topic1"), emqx:subscribe("$local/$share/group1/topic1"),
emqttd:subscribe("$share/group2/topic2"), emqx:subscribe("$share/group2/topic2"),
emqttd:subscribe("$queue/topic3"), emqx:subscribe("$queue/topic3"),
timer:sleep(10), timer:sleep(10),
?assertEqual([self()], emqttd:subscribers(<<"$local/$share/group1/topic1">>)), ?assertEqual([self()], emqx:subscribers(<<"$local/$share/group1/topic1">>)),
?assertEqual([{<<"$local/$share/group1/topic1">>, self(), []}, ?assertEqual([{<<"$local/$share/group1/topic1">>, self(), []},
{<<"$queue/topic3">>, self(), []}, {<<"$queue/topic3">>, self(), []},
{<<"$share/group2/topic2">>, self(), []}], {<<"$share/group2/topic2">>, self(), []}],
lists:sort(emqttd:subscriptions(self()))), lists:sort(emqx:subscriptions(self()))),
emqttd:unsubscribe("$local/$share/group1/topic1"), emqx:unsubscribe("$local/$share/group1/topic1"),
emqttd:unsubscribe("$share/group2/topic2"), emqx:unsubscribe("$share/group2/topic2"),
emqttd:unsubscribe("$queue/topic3"), emqx:unsubscribe("$queue/topic3"),
?assertEqual([], lists:sort(emqttd:subscriptions(self()))). ?assertEqual([], lists:sort(emqx:subscriptions(self()))).
'pubsub#'(_) -> 'pubsub#'(_) ->
emqttd:subscribe(<<"a/#">>), emqx:subscribe(<<"a/#">>),
timer:sleep(10), timer:sleep(10),
emqttd:publish(emqttd_message:make(ct, <<"a/b/c">>, <<"hello">>)), emqx:publish(emqx_message:make(ct, <<"a/b/c">>, <<"hello">>)),
?assert(receive {dispatch, <<"a/#">>, _} -> true after 2 -> false end), ?assert(receive {dispatch, <<"a/#">>, _} -> true after 2 -> false end),
emqttd:unsubscribe(<<"a/#">>). emqx:unsubscribe(<<"a/#">>).
'pubsub+'(_) -> 'pubsub+'(_) ->
emqttd:subscribe(<<"a/+/+">>), emqx:subscribe(<<"a/+/+">>),
timer:sleep(10), timer:sleep(10),
emqttd:publish(emqttd_message:make(ct, <<"a/b/c">>, <<"hello">>)), emqx:publish(emqx_message:make(ct, <<"a/b/c">>, <<"hello">>)),
?assert(receive {dispatch, <<"a/+/+">>, _} -> true after 1 -> false end), ?assert(receive {dispatch, <<"a/+/+">>, _} -> true after 1 -> false end),
emqttd:unsubscribe(<<"a/+/+">>). emqx:unsubscribe(<<"a/+/+">>).
loop_recv(Topic, Timeout) -> loop_recv(Topic, Timeout) ->
loop_recv(Topic, Timeout, []). loop_recv(Topic, Timeout, []).
@ -296,42 +300,42 @@ loop_recv(Topic, Timeout, Acc) ->
router_add_del(_) -> router_add_del(_) ->
%% Add %% Add
emqttd_router:add_route(<<"#">>), emqx_router:add_route(<<"#">>),
emqttd_router:add_route(<<"a/b/c">>), emqx_router:add_route(<<"a/b/c">>),
emqttd_router:add_route(<<"+/#">>, node()), emqx_router:add_route(<<"+/#">>, node()),
Routes = [R1, R2 | _] = [ Routes = [R1, R2 | _] = [
#mqtt_route{topic = <<"#">>, node = node()}, #mqtt_route{topic = <<"#">>, node = node()},
#mqtt_route{topic = <<"+/#">>, node = node()}, #mqtt_route{topic = <<"+/#">>, node = node()},
#mqtt_route{topic = <<"a/b/c">>, node = node()}], #mqtt_route{topic = <<"a/b/c">>, node = node()}],
Routes = lists:sort(emqttd_router:match(<<"a/b/c">>)), Routes = lists:sort(emqx_router:match(<<"a/b/c">>)),
%% Batch Add %% Batch Add
emqttd_router:add_routes(Routes), emqx_router:add_routes(Routes),
Routes = lists:sort(emqttd_router:match(<<"a/b/c">>)), Routes = lists:sort(emqx_router:match(<<"a/b/c">>)),
%% Del %% Del
emqttd_router:del_route(<<"a/b/c">>), emqx_router:del_route(<<"a/b/c">>),
[R1, R2] = lists:sort(emqttd_router:match(<<"a/b/c">>)), [R1, R2] = lists:sort(emqx_router:match(<<"a/b/c">>)),
{atomic, []} = mnesia:transaction(fun emqttd_trie:lookup/1, [<<"a/b/c">>]), {atomic, []} = mnesia:transaction(fun emqx_trie:lookup/1, [<<"a/b/c">>]),
%% Batch Del %% Batch Del
R3 = #mqtt_route{topic = <<"#">>, node = 'a@127.0.0.1'}, R3 = #mqtt_route{topic = <<"#">>, node = 'a@127.0.0.1'},
emqttd_router:add_route(R3), emqx_router:add_route(R3),
emqttd_router:del_routes([R1, R2]), emqx_router:del_routes([R1, R2]),
emqttd_router:del_route(R3), emqx_router:del_route(R3),
[] = lists:sort(emqttd_router:match(<<"a/b/c">>)). [] = lists:sort(emqx_router:match(<<"a/b/c">>)).
router_print(_) -> router_print(_) ->
Routes = [#mqtt_route{topic = <<"a/b/c">>, node = node()}, Routes = [#mqtt_route{topic = <<"a/b/c">>, node = node()},
#mqtt_route{topic = <<"#">>, node = node()}, #mqtt_route{topic = <<"#">>, node = node()},
#mqtt_route{topic = <<"+/#">>, node = node()}], #mqtt_route{topic = <<"+/#">>, node = node()}],
emqttd_router:add_routes(Routes), emqx_router:add_routes(Routes),
emqttd_router:print(<<"a/b/c">>). emqx_router:print(<<"a/b/c">>).
router_unused(_) -> router_unused(_) ->
gen_server:call(emqttd_router, bad_call), gen_server:call(emqx_router, bad_call),
gen_server:cast(emqttd_router, bad_msg), gen_server:cast(emqx_router, bad_msg),
emqttd_router ! bad_info. emqx_router ! bad_info.
recv_loop(Msgs) -> recv_loop(Msgs) ->
receive receive
@ -346,17 +350,17 @@ recv_loop(Msgs) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
start_session(_) -> start_session(_) ->
{ok, ClientPid} = emqttd_mock_client:start_link(<<"clientId">>), {ok, ClientPid} = emqx_mock_client:start_link(<<"clientId">>),
{ok, SessPid} = emqttd_mock_client:start_session(ClientPid), {ok, SessPid} = emqx_mock_client:start_session(ClientPid),
Message = emqttd_message:make(<<"clientId">>, 2, <<"topic">>, <<"hello">>), Message = emqx_message:make(<<"clientId">>, 2, <<"topic">>, <<"hello">>),
Message1 = Message#mqtt_message{pktid = 1}, Message1 = Message#mqtt_message{pktid = 1},
emqttd_session:publish(SessPid, Message1), emqx_session:publish(SessPid, Message1),
emqttd_session:pubrel(SessPid, 1), emqx_session:pubrel(SessPid, 1),
emqttd_session:subscribe(SessPid, [{<<"topic/session">>, [{qos, 2}]}]), emqx_session:subscribe(SessPid, [{<<"topic/session">>, [{qos, 2}]}]),
Message2 = emqttd_message:make(<<"clientId">>, 1, <<"topic/session">>, <<"test">>), Message2 = emqx_message:make(<<"clientId">>, 1, <<"topic/session">>, <<"test">>),
emqttd_session:publish(SessPid, Message2), emqx_session:publish(SessPid, Message2),
emqttd_session:unsubscribe(SessPid, [{<<"topic/session">>, []}]), emqx_session:unsubscribe(SessPid, [{<<"topic/session">>, []}]),
emqttd_mock_client:stop(ClientPid). emqx_mock_client:stop(ClientPid).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Broker Group %% Broker Group
@ -368,55 +372,55 @@ hook_unhook(_) ->
%% Metric Group %% Metric Group
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
inc_dec_metric(_) -> inc_dec_metric(_) ->
emqttd_metrics:inc(gauge, 'messages/retained', 10), emqx_metrics:inc(gauge, 'messages/retained', 10),
emqttd_metrics:dec(gauge, 'messages/retained', 10). emqx_metrics:dec(gauge, 'messages/retained', 10).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Stats Group %% Stats Group
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
set_get_stat(_) -> set_get_stat(_) ->
emqttd_stats:setstat('retained/max', 99), emqx_stats:setstat('retained/max', 99),
99 = emqttd_stats:getstat('retained/max'). 99 = emqx_stats:getstat('retained/max').
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Hook Test %% Hook Test
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
add_delete_hook(_) -> add_delete_hook(_) ->
ok = emqttd:hook(test_hook, fun ?MODULE:hook_fun1/1, []), ok = emqx:hook(test_hook, fun ?MODULE:hook_fun1/1, []),
ok = emqttd:hook(test_hook, {tag, fun ?MODULE:hook_fun2/1}, []), ok = emqx:hook(test_hook, {tag, fun ?MODULE:hook_fun2/1}, []),
{error, already_hooked} = emqttd:hook(test_hook, {tag, fun ?MODULE:hook_fun2/1}, []), {error, already_hooked} = emqx:hook(test_hook, {tag, fun ?MODULE:hook_fun2/1}, []),
Callbacks = [{callback, undefined, fun ?MODULE:hook_fun1/1, [], 0}, Callbacks = [{callback, undefined, fun ?MODULE:hook_fun1/1, [], 0},
{callback, tag, fun ?MODULE:hook_fun2/1, [], 0}], {callback, tag, fun ?MODULE:hook_fun2/1, [], 0}],
Callbacks = emqttd_hooks:lookup(test_hook), Callbacks = emqx_hooks:lookup(test_hook),
ok = emqttd:unhook(test_hook, fun ?MODULE:hook_fun1/1), ok = emqx:unhook(test_hook, fun ?MODULE:hook_fun1/1),
ct:print("Callbacks: ~p~n", [emqttd_hooks:lookup(test_hook)]), ct:print("Callbacks: ~p~n", [emqx_hooks:lookup(test_hook)]),
ok = emqttd:unhook(test_hook, {tag, fun ?MODULE:hook_fun2/1}), ok = emqx:unhook(test_hook, {tag, fun ?MODULE:hook_fun2/1}),
{error, not_found} = emqttd:unhook(test_hook1, {tag, fun ?MODULE:hook_fun2/1}), {error, not_found} = emqx:unhook(test_hook1, {tag, fun ?MODULE:hook_fun2/1}),
[] = emqttd_hooks:lookup(test_hook), [] = emqx_hooks:lookup(test_hook),
ok = emqttd:hook(emqttd_hook, fun ?MODULE:hook_fun1/1, [], 9), ok = emqx:hook(emqx_hook, fun ?MODULE:hook_fun1/1, [], 9),
ok = emqttd:hook(emqttd_hook, {"tag", fun ?MODULE:hook_fun2/1}, [], 8), ok = emqx:hook(emqx_hook, {"tag", fun ?MODULE:hook_fun2/1}, [], 8),
Callbacks2 = [{callback, "tag", fun ?MODULE:hook_fun2/1, [], 8}, Callbacks2 = [{callback, "tag", fun ?MODULE:hook_fun2/1, [], 8},
{callback, undefined, fun ?MODULE:hook_fun1/1, [], 9}], {callback, undefined, fun ?MODULE:hook_fun1/1, [], 9}],
Callbacks2 = emqttd_hooks:lookup(emqttd_hook), Callbacks2 = emqx_hooks:lookup(emqx_hook),
ok = emqttd:unhook(emqttd_hook, fun ?MODULE:hook_fun1/1), ok = emqx:unhook(emqx_hook, fun ?MODULE:hook_fun1/1),
ok = emqttd:unhook(emqttd_hook, {"tag", fun ?MODULE:hook_fun2/1}), ok = emqx:unhook(emqx_hook, {"tag", fun ?MODULE:hook_fun2/1}),
[] = emqttd_hooks:lookup(emqttd_hook). [] = emqx_hooks:lookup(emqx_hook).
run_hooks(_) -> run_hooks(_) ->
ok = emqttd:hook(foldl_hook, fun ?MODULE:hook_fun3/4, [init]), ok = emqx:hook(foldl_hook, fun ?MODULE:hook_fun3/4, [init]),
ok = emqttd:hook(foldl_hook, {tag, fun ?MODULE:hook_fun3/4}, [init]), ok = emqx:hook(foldl_hook, {tag, fun ?MODULE:hook_fun3/4}, [init]),
ok = emqttd:hook(foldl_hook, fun ?MODULE:hook_fun4/4, [init]), ok = emqx:hook(foldl_hook, fun ?MODULE:hook_fun4/4, [init]),
ok = emqttd:hook(foldl_hook, fun ?MODULE:hook_fun5/4, [init]), ok = emqx:hook(foldl_hook, fun ?MODULE:hook_fun5/4, [init]),
{stop, [r3, r2]} = emqttd:run_hooks(foldl_hook, [arg1, arg2], []), {stop, [r3, r2]} = emqx:run_hooks(foldl_hook, [arg1, arg2], []),
{ok, []} = emqttd:run_hooks(unknown_hook, [], []), {ok, []} = emqx:run_hooks(unknown_hook, [], []),
ok = emqttd:hook(foreach_hook, fun ?MODULE:hook_fun6/2, [initArg]), ok = emqx:hook(foreach_hook, fun ?MODULE:hook_fun6/2, [initArg]),
ok = emqttd:hook(foreach_hook, {tag, fun ?MODULE:hook_fun6/2}, [initArg]), ok = emqx:hook(foreach_hook, {tag, fun ?MODULE:hook_fun6/2}, [initArg]),
ok = emqttd:hook(foreach_hook, fun ?MODULE:hook_fun7/2, [initArg]), ok = emqx:hook(foreach_hook, fun ?MODULE:hook_fun7/2, [initArg]),
ok = emqttd:hook(foreach_hook, fun ?MODULE:hook_fun8/2, [initArg]), ok = emqx:hook(foreach_hook, fun ?MODULE:hook_fun8/2, [initArg]),
stop = emqttd:run_hooks(foreach_hook, [arg]). stop = emqx:run_hooks(foreach_hook, [arg]).
hook_fun1([]) -> ok. hook_fun1([]) -> ok.
hook_fun2([]) -> {ok, []}. hook_fun2([]) -> {ok, []}.
@ -440,7 +444,7 @@ request_status(_) ->
false -> not_running; false -> not_running;
{value, _Val} -> running {value, _Val} -> running
end, end,
Status = iolist_to_binary(io_lib:format("Node ~s is ~s~nemqttd is ~s", Status = iolist_to_binary(io_lib:format("Node ~s is ~s~nemqx is ~s",
[node(), InternalStatus, AppStatus])), [node(), InternalStatus, AppStatus])),
Url = "http://127.0.0.1:8080/status", Url = "http://127.0.0.1:8080/status",
{ok, {{"HTTP/1.1", 200, "OK"}, _, Return}} = {ok, {{"HTTP/1.1", 200, "OK"}, _, Return}} =
@ -462,7 +466,7 @@ request_publish(_) ->
UnSubParams = "{\"topic\" : \"a\/b\/c\", \"client_id\" :\"random\"}", UnSubParams = "{\"topic\" : \"a\/b\/c\", \"client_id\" :\"random\"}",
?assert(connect_emqttd_pubsub_(post, "api/v2/mqtt/unsubscribe", UnSubParams, auth_header_("", ""))). ?assert(connect_emqttd_pubsub_(post, "api/v2/mqtt/unsubscribe", UnSubParams, auth_header_("", ""))).
connect_emqttd_pubsub_(Method, Api, Params, Auth) -> connect_emqx_publish_(Method, Api, Params, Auth) ->
Url = "http://127.0.0.1:8080/" ++ Api, Url = "http://127.0.0.1:8080/" ++ Api,
case httpc:request(Method, {Url, [Auth], ?CONTENT_TYPE, Params}, [], []) of case httpc:request(Method, {Url, [Auth], ?CONTENT_TYPE, Params}, [], []) of
{error, socket_closed_remotely} -> {error, socket_closed_remotely} ->
@ -479,33 +483,34 @@ auth_header_(User, Pass) ->
Encoded = base64:encode_to_string(lists:append([User,":",Pass])), Encoded = base64:encode_to_string(lists:append([User,":",Pass])),
{"Authorization","Basic " ++ Encoded}. {"Authorization","Basic " ++ Encoded}.
%%TODO: ...
websocket_test(_) -> websocket_test(_) ->
Conn = esockd_connection:new(esockd_transport, nil, []), Conn = esockd_connection:new(esockd_transport, nil, []),
Req = mochiweb_request:new(Conn, 'GET', "/mqtt", {1, 1}, Req = mochiweb_request:new(Conn, 'GET', "/mqtt", {1, 1},
mochiweb_headers:make([{"Sec-WebSocket-Key","Xn3fdKyc3qEXPuj2A3O+ZA=="}])), mochiweb_headers:make([{"Sec-WebSocket-Key","Xn3fdKyc3qEXPuj2A3O+ZA=="}])),
ct:log("Req:~p", [Req]), ct:log("Req:~p", [Req]).
emqttd_http:handle_request(Req). %%emqx_http:handle_request(Req).
set_alarms(_) -> set_alarms(_) ->
AlarmTest = #mqtt_alarm{id = <<"1">>, severity = error, title="alarm title", summary="alarm summary"}, AlarmTest = #mqtt_alarm{id = <<"1">>, severity = error, title="alarm title", summary="alarm summary"},
emqttd_alarm:set_alarm(AlarmTest), emqx_alarm:set_alarm(AlarmTest),
Alarms = emqttd_alarm:get_alarms(), Alarms = emqx_alarm:get_alarms(),
?assertEqual(1, length(Alarms)), ?assertEqual(1, length(Alarms)),
emqttd_alarm:clear_alarm(<<"1">>), emqx_alarm:clear_alarm(<<"1">>),
[] = emqttd_alarm:get_alarms(). [] = emqx_alarm:get_alarms().
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Cli group %% Cli group
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
ctl_register_cmd(_) -> ctl_register_cmd(_) ->
emqttd_ctl:register_cmd(test_cmd, {?MODULE, test_cmd}), emqx_ctl:register_cmd(test_cmd, {?MODULE, test_cmd}),
erlang:yield(), erlang:yield(),
timer:sleep(5), timer:sleep(5),
[{?MODULE, test_cmd}] = emqttd_ctl:lookup(test_cmd), [{?MODULE, test_cmd}] = emqx_ctl:lookup(test_cmd),
emqttd_ctl:run(["test_cmd", "arg1", "arg2"]), emqx_ctl:run(["test_cmd", "arg1", "arg2"]),
emqttd_ctl:unregister_cmd(test_cmd). emqx_ctl:unregister_cmd(test_cmd).
test_cmd(["arg1", "arg2"]) -> test_cmd(["arg1", "arg2"]) ->
ct:print("test_cmd is called"); ct:print("test_cmd is called");
@ -514,55 +519,55 @@ test_cmd([]) ->
io:format("test command"). io:format("test command").
cli_status(_) -> cli_status(_) ->
emqttd_cli:status([]). emqx_cli:status([]).
cli_broker(_) -> cli_broker(_) ->
emqttd_cli:broker([]), emqx_cli:broker([]),
emqttd_cli:broker(["stats"]), emqx_cli:broker(["stats"]),
emqttd_cli:broker(["metrics"]), emqx_cli:broker(["metrics"]),
emqttd_cli:broker(["pubsub"]). emqx_cli:broker(["pubsub"]).
cli_clients(_) -> cli_clients(_) ->
emqttd_cli:clients(["list"]), emqx_cli:clients(["list"]),
emqttd_cli:clients(["show", "clientId"]), emqx_cli:clients(["show", "clientId"]),
emqttd_cli:clients(["kick", "clientId"]). emqx_cli:clients(["kick", "clientId"]).
cli_sessions(_) -> cli_sessions(_) ->
emqttd_cli:sessions(["list"]), emqx_cli:sessions(["list"]),
emqttd_cli:sessions(["list", "persistent"]), emqx_cli:sessions(["list", "persistent"]),
emqttd_cli:sessions(["list", "transient"]), emqx_cli:sessions(["list", "transient"]),
emqttd_cli:sessions(["show", "clientId"]). emqx_cli:sessions(["show", "clientId"]).
cli_routes(_) -> cli_routes(_) ->
emqttd:subscribe(<<"topic/route">>), emqx:subscribe(<<"topic/route">>),
emqttd_cli:routes(["list"]), emqx_cli:routes(["list"]),
emqttd_cli:routes(["show", "topic/route"]), emqx_cli:routes(["show", "topic/route"]),
emqttd:unsubscribe(<<"topic/route">>). emqx:unsubscribe(<<"topic/route">>).
cli_topics(_) -> cli_topics(_) ->
emqttd:subscribe(<<"topic">>), emqx:subscribe(<<"topic">>),
emqttd_cli:topics(["list"]), emqx_cli:topics(["list"]),
emqttd_cli:topics(["show", "topic"]), emqx_cli:topics(["show", "topic"]),
emqttd:unsubscribe(<<"topic">>). emqx:unsubscribe(<<"topic">>).
cli_subscriptions(_) -> cli_subscriptions(_) ->
emqttd_cli:subscriptions(["list"]), emqx_cli:subscriptions(["list"]),
emqttd_cli:subscriptions(["show", "clientId"]), emqx_cli:subscriptions(["show", "clientId"]),
emqttd_cli:subscriptions(["add", "clientId", "topic", "2"]), emqx_cli:subscriptions(["add", "clientId", "topic", "2"]),
emqttd_cli:subscriptions(["del", "clientId", "topic"]). emqx_cli:subscriptions(["del", "clientId", "topic"]).
cli_plugins(_) -> cli_plugins(_) ->
emqttd_cli:plugins(["list"]), emqx_cli:plugins(["list"]),
emqttd_cli:plugins(["load", "emqttd_plugin_template"]), emqx_cli:plugins(["load", "emqx_plugin_template"]),
emqttd_cli:plugins(["unload", "emqttd_plugin_template"]). emqx_cli:plugins(["unload", "emqx_plugin_template"]).
cli_bridges(_) -> cli_bridges(_) ->
emqttd_cli:bridges(["list"]), emqx_cli:bridges(["list"]),
emqttd_cli:bridges(["start", "a@127.0.0.1", "topic"]), emqx_cli:bridges(["start", "a@127.0.0.1", "topic"]),
emqttd_cli:bridges(["stop", "a@127.0.0.1", "topic"]). emqx_cli:bridges(["stop", "a@127.0.0.1", "topic"]).
cli_listeners(_) -> cli_listeners(_) ->
emqttd_cli:listeners([]). emqx_cli:listeners([]).
conflict_listeners(_) -> conflict_listeners(_) ->
F = F =
@ -595,8 +600,8 @@ conflict_listeners(_) ->
emqttc:disconnect(C2). emqttc:disconnect(C2).
cli_vm(_) -> cli_vm(_) ->
emqttd_cli:vm([]), emqx_cli:vm([]),
emqttd_cli:vm(["ports"]). emqx_cli:vm(["ports"]).
cleanSession_validate(_) -> cleanSession_validate(_) ->
{ok, C1} = emqttc:start_link([{host, "localhost"}, {ok, C1} = emqttc:start_link([{host, "localhost"},
@ -605,6 +610,7 @@ cleanSession_validate(_) ->
{clean_sess, false}]), {clean_sess, false}]),
timer:sleep(10), timer:sleep(10),
emqttc:subscribe(C1, <<"topic">>, qos0), emqttc:subscribe(C1, <<"topic">>, qos0),
ok = emqx_cli:sessions(["list", "persistent"]),
emqttc:disconnect(C1), emqttc:disconnect(C1),
{ok, Pub} = emqttc:start_link([{host, "localhost"}, {ok, Pub} = emqttc:start_link([{host, "localhost"},
{port, 1883}, {port, 1883},
@ -617,15 +623,136 @@ cleanSession_validate(_) ->
{client_id, <<"c1">>}, {client_id, <<"c1">>},
{clean_sess, false}]), {clean_sess, false}]),
timer:sleep(100), timer:sleep(100),
Metrics = emqttd_metrics:all(), Metrics = emqx_metrics:all(),
ct:log("Metrics:~p~n", [Metrics]),
?assertEqual(1, proplists:get_value('messages/qos0/sent', Metrics)), ?assertEqual(1, proplists:get_value('messages/qos0/sent', Metrics)),
?assertEqual(1, proplists:get_value('messages/qos0/received', Metrics)), ?assertEqual(1, proplists:get_value('messages/qos0/received', Metrics)),
emqttc:disconnect(Pub), emqttc:disconnect(Pub),
emqttc:disconnect(C11). emqttc:disconnect(C11).
cleanSession_validate1(_) ->
{ok, C1} = emqttc:start_link([{host, "localhost"},
{port, 1883},
{client_id, <<"c1">>},
{clean_sess, true}]),
timer:sleep(10),
emqttc:subscribe(C1, <<"topic">>, qos1),
ok = emqx_cli:sessions(["list", "transient"]),
emqttc:disconnect(C1),
{ok, Pub} = emqttc:start_link([{host, "localhost"},
{port, 1883},
{client_id, <<"pub">>}]),
emqttc:publish(Pub, <<"topic">>, <<"m1">>, [{qos, 1}]),
timer:sleep(10),
{ok, C11} = emqttc:start_link([{host, "localhost"},
{port, 1883},
{client_id, <<"c1">>},
{clean_sess, false}]),
timer:sleep(100),
Metrics = emqx_metrics:all(),
?assertEqual(0, proplists:get_value('messages/qos1/sent', Metrics)),
?assertEqual(1, proplists:get_value('messages/qos1/received', Metrics)),
emqttc:disconnect(Pub),
emqttc:disconnect(C11).
get_api_lists(_Config) -> get_api_lists(_Config) ->
lists:foreach(fun request/1, ?GET_API). lists:foreach(fun request/1, ?GET_API).
request_publish(_) ->
emqttc:start_link([{host, "localhost"},
{port, 1883},
{client_id, <<"random">>},
{clean_sess, false}]),
SubParams = "{\"qos\":1, \"topic\" : \"a\/b\/c\", \"client_id\" :\"random\"}",
?assert(connect_emqx_pubsub_(post, "api/v2/mqtt/subscribe", SubParams, auth_header_("", ""))),
ok = emqx:subscribe(<<"a/b/c">>, self(), [{qos, 1}]),
Params = "{\"qos\":1, \"retain\":false, \"topic\" : \"a\/b\/c\", \"messages\" :\"hello\"}",
?assert(connect_emqx_pubsub_(post, "api/v2/mqtt/publish", Params, auth_header_("", ""))),
?assert(receive {dispatch, <<"a/b/c">>, _} -> true after 2 -> false end),
UnSubParams = "{\"topic\" : \"a\/b\/c\", \"client_id\" :\"random\"}",
?assert(connect_emqx_pubsub_(post, "api/v2/mqtt/unsubscribe", UnSubParams, auth_header_("", ""))).
connect_emqx_pubsub_(Method, Api, Params, Auth) ->
Url = "http://127.0.0.1:8080/" ++ Api,
case httpc:request(Method, {Url, [Auth], ?CONTENT_TYPE, Params}, [], []) of
{error, socket_closed_remotely} ->
false;
{ok, {{"HTTP/1.1", 200, "OK"}, _, _Return} } ->
true;
{ok, {{"HTTP/1.1", 400, _}, _, []}} ->
false;
{ok, {{"HTTP/1.1", 404, _}, _, []}} ->
false
end.
request(Path) ->
http_get(get, Path).
http_get(Method, Path) ->
req(Method, Path, []).
http_put(Method, Path, Params) ->
req(Method, Path, format_for_upload(Params)).
http_post(Method, Path, Params) ->
req(Method, Path, format_for_upload(Params)).
req(Method, Path, Body) ->
Url = ?URL ++ Path,
Headers = auth_header_("", ""),
case httpc:request(Method, {Url, [Headers]}, [], []) of
{error, R} ->
ct:log("R:~p~n", [R]),
false;
{ok, {{"HTTP/1.1", 200, "OK"}, _, _Return} } ->
true;
{ok, {{"HTTP/1.1", 400, _}, _, []}} ->
false;
{ok, {{"HTTP/1.1", 404, _}, _, []}} ->
false
end.
format_for_upload(none) ->
<<"">>;
format_for_upload(List) ->
iolist_to_binary(mochijson2:encode(List)).
ensure_ok(ok) -> ok;
ensure_ok({error, {already_started, _}}) -> ok.
host() -> ct:print("!!!! Node: ~p~n", [node()]), [_, Host] = string:tokens(atom_to_list(node()), "@"), Host.
wait_running(Node) ->
wait_running(Node, 30000).
wait_running(Node, Timeout) when Timeout < 0 ->
throw({wait_timeout, Node});
wait_running(Node, Timeout) ->
case rpc:call(Node, emqx, is_running, [Node]) of
true -> ok;
false -> timer:sleep(100),
wait_running(Node, Timeout - 100)
end.
slave(emqx, Node) ->
{ok, Slave} = slave:start(host(), Node, "-config ../../test/emqx_SUITE_data/slave.config " ++ ensure_slave()),
ct:log("Slave:~p~n", [Slave]),
rpc:call(Slave, application, ensure_all_started, [emqx]),
Slave;
slave(node, Node) ->
{ok, N} = slave:start(host(), Node, ensure_slave()),
N.
ensure_slave() ->
EbinDir = local_path(["ebin"]),
DepsDir = local_path(["deps", "*", "ebin"]),
RpcDir = local_path(["deps", "gen_rpc", "_build", "dev", "lib", "*", "ebin"]),
"-pa " ++ EbinDir ++ " -pa " ++ DepsDir ++ " -pa " ++ RpcDir.
change_opts(SslType) -> change_opts(SslType) ->
{ok, Listeners} = application:get_env(?APP, listeners), {ok, Listeners} = application:get_env(?APP, listeners),
NewListeners = NewListeners =
@ -658,8 +785,8 @@ change_opts(SslType) ->
application:set_env(?APP, listeners, NewListeners). application:set_env(?APP, listeners, NewListeners).
generate_config() -> generate_config() ->
Schema = cuttlefish_schema:files([local_path(["priv", "emq.schema"])]), Schema = cuttlefish_schema:files([local_path(["priv", "emqx.schema"])]),
Conf = conf_parse:file([local_path(["etc", "emq.conf"])]), Conf = conf_parse:file([local_path(["etc", "emqx.conf"])]),
cuttlefish_generator:map(Schema, Conf). cuttlefish_generator:map(Schema, Conf).
get_base_dir(Module) -> get_base_dir(Module) ->
@ -710,3 +837,4 @@ format_for_upload(none) ->
<<"">>; <<"">>;
format_for_upload(List) -> format_for_upload(List) ->
iolist_to_binary(mochijson2:encode(List)). iolist_to_binary(mochijson2:encode(List)).

View File

@ -0,0 +1,29 @@
%%--------------------------------------------------------------------
%%
%% [ACL](https://github.com/emqtt/emqttd/wiki/ACL)
%%
%% -type who() :: all | binary() |
%% {ipaddr, esockd_access:cidr()} |
%% {client, binary()} |
%% {user, binary()}.
%%
%% -type access() :: subscribe | publish | pubsub.
%%
%% -type topic() :: binary().
%%
%% -type rule() :: {allow, all} |
%% {allow, who(), access(), list(topic())} |
%% {deny, all} |
%% {deny, who(), access(), list(topic())}.
%%
%%--------------------------------------------------------------------
{allow, {user, "dashboard"}, subscribe, ["$SYS/#"]}.
{allow, {ipaddr, "127.0.0.1"}, pubsub, ["$SYS/#", "#"]}.
{deny, all, subscribe, ["$SYS/#", {eq, "#"}]}.
{allow, all}.

View File

@ -0,0 +1,60 @@
[{emqx,
[{plugins_loaded_file,"loaded_plugins"},
{plugins_etc_dir,"plugins/"},
{broker_sys_interval,60},
{cache_acl,true},
{allow_anonymous,true},
{license_file,"../../etc/emqx.lic"},
{protocol,[{max_clientid_len,1024},{max_packet_size,65536}]},
{client,
[{max_publish_rate,5},{idle_timeout,30000},{enable_stats,60000}]},
{session,
[{max_subscriptions,0},
{upgrade_qos,false},
{max_inflight,32},
{retry_interval,20000},
{max_awaiting_rel,100},
{await_rel_timeout,20000},
{enable_stats,60000},
{expiry_interval,7200000}]},
{mqueue,
[{priority,[]},
{type,simple},
{max_length,infinity},
{low_watermark,0.2},
{high_watermark,0.6},
{store_qos0,true}]},
{pubsub,[{pool_size,8},{by_clientid,true},{async,true}]},
{bridge,[{max_queue_len,10000},{ping_down_interval,1}]},
{listeners, []},
{sysmon,
[{long_gc,false},
{long_schedule,240},
{large_heap,8388608},
{busy_port,false},
{busy_dist_port,true}]}]},
{sasl,[{sasl_error_logger,false}]},
{lager,
[{error_logger_hwm,1000},
{error_logger_redirect,true},
{log_dir,"{{ platform_log_dir }}"},
{handlers,
[{lager_console_backend,error},
{lager_file_backend,
[{file,"{{ platform_log_dir }}/error.log"},
{level,error},
{size,10485760},
{date,"$D0"},
{count,5}]},
{lager_syslog_backend,["emq",local0,error]}]},
{crash_log,"{{ platform_log_dir }}/crash.log"}]},
{gen_rpc,
[{socket_keepalive_count,2},
{socket_keepalive_interval,5},
{socket_keepalive_idle,5},
{call_receive_timeout,15000},
{authentication_timeout,5000},
{send_timeout,5000},
{connect_timeout,5000},
{tcp_client_port,5369},
{tcp_server_port,7369}]}].

View File

@ -14,15 +14,15 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_access_SUITE). -module(emqx_access_SUITE).
-compile(export_all). -compile(export_all).
-include("emqttd.hrl"). -include("emqx.hrl").
-define(AC, emqttd_access_control). -define(AC, emqx_access_control).
-import(emqttd_access_rule, [compile/1, match/3]). -import(emqx_access_rule, [compile/1, match/3]).
all() -> all() ->
[{group, access_control}, [{group, access_control},
@ -39,7 +39,7 @@ groups() ->
match_rule]}]. match_rule]}].
init_per_group(access_control, Config) -> init_per_group(access_control, Config) ->
application:load(emqttd), application:load(emqx),
prepare_config(), prepare_config(),
Config; Config;
@ -59,8 +59,8 @@ prepare_config() ->
Config = [{auth, anonymous, []}, Config = [{auth, anonymous, []},
{acl, internal, [{config, "access_SUITE_acl.conf"}, {acl, internal, [{config, "access_SUITE_acl.conf"},
{nomatch, allow}]}], {nomatch, allow}]}],
write_config("access_SUITE_emqttd.conf", Config), write_config("access_SUITE_emqx.conf", Config),
application:set_env(emqttd, conf, "access_SUITE_emqttd.conf"). application:set_env(emqx, conf, "access_SUITE_emqx.conf").
write_config(Filename, Terms) -> write_config(Filename, Terms) ->
file:write_file(Filename, [io_lib:format("~tp.~n", [Term]) || Term <- Terms]). file:write_file(Filename, [io_lib:format("~tp.~n", [Term]) || Term <- Terms]).
@ -87,31 +87,31 @@ end_per_testcase(_TestCase, _Config) ->
ok. ok.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% emqttd_access_control %% emqx_access_control
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
reload_acl(_) -> reload_acl(_) ->
[] = ?AC:reload_acl(). [ok] = ?AC:reload_acl().
register_mod(_) -> register_mod(_) ->
ok = ?AC:register_mod(acl, emqttd_acl_test_mod, []), ok = ?AC:register_mod(acl, emqx_acl_test_mod, []),
{error, already_existed} = ?AC:register_mod(acl, emqttd_acl_test_mod, []), {error, already_existed} = ?AC:register_mod(acl, emqx_acl_test_mod, []),
[{emqttd_acl_test_mod, _, 0}] = ?AC:lookup_mods(acl), {emqx_acl_test_mod, _, 0} = hd(?AC:lookup_mods(acl)),
ok = ?AC:register_mod(auth, emqttd_auth_anonymous_test_mod,[]), ok = ?AC:register_mod(auth, emqx_auth_anonymous_test_mod,[]),
ok = ?AC:register_mod(auth, emqttd_auth_dashboard, [], 99), ok = ?AC:register_mod(auth, emqx_auth_dashboard, [], 99),
[{emqttd_auth_dashboard, _, 99}, [{emqx_auth_dashboard, _, 99},
{emqttd_auth_anonymous_test_mod, _, 0}] = ?AC:lookup_mods(auth). {emqx_auth_anonymous_test_mod, _, 0}] = ?AC:lookup_mods(auth).
unregister_mod(_) -> unregister_mod(_) ->
ok = ?AC:register_mod(acl, emqttd_acl_test_mod, []), ok = ?AC:register_mod(acl, emqx_acl_test_mod, []),
[{emqttd_acl_test_mod, _, 0}] = ?AC:lookup_mods(acl), {emqx_acl_test_mod, _, 0} = hd(?AC:lookup_mods(acl)),
ok = ?AC:unregister_mod(acl, emqttd_acl_test_mod), ok = ?AC:unregister_mod(acl, emqx_acl_test_mod),
timer:sleep(5), timer:sleep(5),
[] = ?AC:lookup_mods(acl), {emqx_acl_internal, _, 0}= hd(?AC:lookup_mods(acl)),
ok = ?AC:register_mod(auth, emqttd_auth_anonymous_test_mod,[]), ok = ?AC:register_mod(auth, emqx_auth_anonymous_test_mod,[]),
[{emqttd_auth_anonymous_test_mod, _, 0}] = ?AC:lookup_mods(auth), [{emqx_auth_anonymous_test_mod, _, 0}] = ?AC:lookup_mods(auth),
ok = ?AC:unregister_mod(auth, emqttd_auth_anonymous_test_mod), ok = ?AC:unregister_mod(auth, emqx_auth_anonymous_test_mod),
timer:sleep(5), timer:sleep(5),
[] = ?AC:lookup_mods(auth). [] = ?AC:lookup_mods(auth).
@ -126,7 +126,7 @@ check_acl(_) ->
allow = ?AC:check_acl(User2, subscribe, <<"a/b/c">>). allow = ?AC:check_acl(User2, subscribe, <<"a/b/c">>).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% emqttd_access_rule %% emqx_access_rule
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
compile_rule(_) -> compile_rule(_) ->

View File

@ -0,0 +1,16 @@
{allow, {ipaddr, "127.0.0.1"}, subscribe, ["$SYS/#", "#"]}.
{allow, {user, "testuser"}, subscribe, ["a/b/c", "d/e/f/#"]}.
{allow, {user, "admin"}, pubsub, ["a/b/c", "d/e/f/#"]}.
{allow, {client, "testClient"}, subscribe, ["testTopics/testClient"]}.
{allow, all, subscribe, ["clients/%c"]}.
{allow, all, pubsub, ["users/%u/#"]}.
{deny, all, subscribe, ["$SYS/#", "#"]}.
{deny, all}.

View File

@ -14,7 +14,7 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_acl_test_mod). -module(emqx_acl_test_mod).
%% ACL callbacks %% ACL callbacks
-export([init/1, check_acl/2, reload_acl/1, description/0]). -export([init/1, check_acl/2, reload_acl/1, description/0]).
@ -30,3 +30,4 @@ reload_acl(_State) ->
description() -> description() ->
"Test ACL Mod". "Test ACL Mod".

View File

@ -14,7 +14,7 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_auth_anonymous_test_mod). -module(emqx_auth_anonymous_test_mod).
%% ACL callbacks %% ACL callbacks
-export([init/1, check/3, description/0]). -export([init/1, check/3, description/0]).
@ -26,4 +26,4 @@ check(_Client, _Password, _Opts) ->
allow. allow.
description() -> description() ->
"Test emqttd_auth_anonymous Mod". "Test emqx_auth_anonymous Mod".

View File

@ -14,7 +14,7 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqttd_auth_dashboard). -module(emqx_auth_dashboard).
%% Auth callbacks %% Auth callbacks
-export([init/1, check/3, description/0]). -export([init/1, check/3, description/0]).
@ -26,4 +26,5 @@ check(_Client, _Password, _Opts) ->
allow. allow.
description() -> description() ->
"Test emqttd_auth_dashboard Mod". "Test Auth Mod".

Some files were not shown because too many files have changed in this diff Show More