add bin/ and some files
This commit is contained in:
parent
e1e7d4e421
commit
efc1d9f424
|
@ -1,3 +1,4 @@
|
||||||
lib
|
lib
|
||||||
ebin/*
|
ebin
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
The emqtt server is licensed under the MPL.
|
||||||
|
|
||||||
|
The files below copied from rabbitmq and licensed under LICENSE-MPL-RabbitMQ:
|
||||||
|
|
||||||
|
credit_flow.erl
|
||||||
|
file_handle_cache.erl
|
||||||
|
gen_server2.erl
|
||||||
|
priority_queue.erl
|
||||||
|
supervisor2.erl
|
||||||
|
tcp_acceptor.erl
|
||||||
|
tcp_acceptor_sup.erl
|
||||||
|
tcp_listener.erl
|
||||||
|
tcp_listener_sup.erl
|
||||||
|
|
||||||
|
If you have any questions regarding licensing, please contact ery.lee@gmail.com
|
|
@ -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 VMware, Inc.
|
||||||
|
Copyright (c) 2007-2012 VMware, 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.]
|
10
Makefile
10
Makefile
|
@ -1,10 +1,10 @@
|
||||||
all: compile
|
all: compile
|
||||||
|
|
||||||
run: compile
|
|
||||||
erl -pa ebin -pa lib/rabbitlib/ebin -config etc/emqtt.config -s emqtt_app start
|
|
||||||
|
|
||||||
compile: deps
|
compile: deps
|
||||||
rebar compile
|
./rebar compile
|
||||||
|
|
||||||
deps:
|
deps:
|
||||||
rebar get-deps
|
./rebar get-deps
|
||||||
|
|
||||||
|
clean:
|
||||||
|
./rebar clean
|
||||||
|
|
30
README.md
30
README.md
|
@ -1,4 +1,32 @@
|
||||||
emqtt
|
emqtt
|
||||||
=====
|
=====
|
||||||
|
|
||||||
erlang mqtt broker
|
erlang mqtt broker based on rabbitmq network layer.
|
||||||
|
|
||||||
|
compile
|
||||||
|
=======
|
||||||
|
|
||||||
|
require erlang R15B+ and git client
|
||||||
|
|
||||||
|
make
|
||||||
|
|
||||||
|
start
|
||||||
|
======
|
||||||
|
|
||||||
|
./bin/emqtt console
|
||||||
|
|
||||||
|
./bin/emqtt start
|
||||||
|
|
||||||
|
stop
|
||||||
|
====
|
||||||
|
|
||||||
|
./bin/emqtt stop
|
||||||
|
|
||||||
|
status
|
||||||
|
======
|
||||||
|
./bin/emqtt_ctl status
|
||||||
|
|
||||||
|
logs
|
||||||
|
====
|
||||||
|
|
||||||
|
log/*
|
||||||
|
|
4
TODO
4
TODO
|
@ -1,2 +1,2 @@
|
||||||
1. Topic Trie
|
OK 1. Topic Trie
|
||||||
2. MQTT frame parse
|
OK 2. MQTT frame parse
|
||||||
|
|
|
@ -0,0 +1,179 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# -*- tab-width:4;indent-tabs-mode:nil -*-
|
||||||
|
# ex: ts=4 sw=4 et
|
||||||
|
|
||||||
|
RUNNER_SCRIPT_DIR=$(cd ${0%/*} && pwd)
|
||||||
|
|
||||||
|
RUNNER_BASE_DIR=${RUNNER_SCRIPT_DIR%/*}
|
||||||
|
RUNNER_ETC_DIR=$RUNNER_BASE_DIR/etc
|
||||||
|
RUNNER_LOG_DIR=$RUNNER_BASE_DIR/log
|
||||||
|
# Note the trailing slash on $PIPE_DIR/
|
||||||
|
PIPE_DIR=/tmp/$RUNNER_BASE_DIR/
|
||||||
|
RUNNER_USER=
|
||||||
|
|
||||||
|
# Make sure this script is running as the appropriate user
|
||||||
|
if [ ! -z "$RUNNER_USER" ] && [ `whoami` != "$RUNNER_USER" ]; then
|
||||||
|
exec sudo -u $RUNNER_USER -i $0 $@
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Make sure CWD is set to runner base dir
|
||||||
|
cd $RUNNER_BASE_DIR
|
||||||
|
|
||||||
|
# Make sure log directory exists
|
||||||
|
mkdir -p $RUNNER_LOG_DIR
|
||||||
|
# Identify the script name
|
||||||
|
SCRIPT=`basename $0`
|
||||||
|
|
||||||
|
# Parse out release and erts info
|
||||||
|
ERLANG_BASE_DIR=/usr/local/lib/erlang
|
||||||
|
START_ERL=`cat $ERLANG_BASE_DIR/releases/start_erl.data`
|
||||||
|
ERTS_VSN=${START_ERL% *}
|
||||||
|
APP_VSN=${START_ERL#* }
|
||||||
|
|
||||||
|
VMARGS_PATH="$RUNNER_ETC_DIR/emqtt.args"
|
||||||
|
|
||||||
|
CONFIG_PATH="$RUNNER_ETC_DIR/emqtt.config"
|
||||||
|
|
||||||
|
# Extract the target node name from node.args
|
||||||
|
NAME_ARG=`egrep '^-s?name' $VMARGS_PATH`
|
||||||
|
if [ -z "$NAME_ARG" ]; then
|
||||||
|
echo "emqtt.args needs to have either -name or -sname parameter."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract the target cookie
|
||||||
|
COOKIE_ARG=`grep '^-setcookie' $VMARGS_PATH`
|
||||||
|
if [ -z "$COOKIE_ARG" ]; then
|
||||||
|
echo "emqtt.args needs to have a -setcookie parameter."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add ERTS bin dir to our path
|
||||||
|
ERTS_PATH=$ERLANG_BASE_DIR/erts-$ERTS_VSN/bin
|
||||||
|
|
||||||
|
# Setup command to control the node
|
||||||
|
NODETOOL="escript $RUNNER_BASE_DIR/bin/nodetool $NAME_ARG $COOKIE_ARG"
|
||||||
|
|
||||||
|
# Check the first argument for instructions
|
||||||
|
case "$1" in
|
||||||
|
start)
|
||||||
|
# Make sure there is not already a node running
|
||||||
|
RES=`$NODETOOL ping`
|
||||||
|
if [ "$RES" = "pong" ]; then
|
||||||
|
echo "Node is already running!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
HEART_COMMAND="$RUNNER_BASE_DIR/bin/$SCRIPT start"
|
||||||
|
export HEART_COMMAND
|
||||||
|
mkdir -p $PIPE_DIR
|
||||||
|
shift # remove $1
|
||||||
|
$ERTS_PATH/run_erl -daemon $PIPE_DIR $RUNNER_LOG_DIR "exec $RUNNER_BASE_DIR/bin/$SCRIPT console $@" 2>&1
|
||||||
|
;;
|
||||||
|
|
||||||
|
stop)
|
||||||
|
# Wait for the node to completely stop...
|
||||||
|
case `uname -s` in
|
||||||
|
Linux|Darwin|FreeBSD|DragonFly|NetBSD|OpenBSD)
|
||||||
|
# PID COMMAND
|
||||||
|
PID=`ps ax -o pid= -o command=|\
|
||||||
|
grep "$RUNNER_BASE_DIR/.*/[b]eam"|awk '{print $1}'`
|
||||||
|
;;
|
||||||
|
SunOS)
|
||||||
|
# PID COMMAND
|
||||||
|
PID=`ps -ef -o pid= -o args=|\
|
||||||
|
grep "$RUNNER_BASE_DIR/.*/[b]eam"|awk '{print $1}'`
|
||||||
|
;;
|
||||||
|
CYGWIN*)
|
||||||
|
# UID PID PPID TTY STIME COMMAND
|
||||||
|
PID=`ps -efW|grep "$RUNNER_BASE_DIR/.*/[b]eam"|awk '{print $2}'`
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
$NODETOOL stop
|
||||||
|
ES=$?
|
||||||
|
if [ "$ES" -ne 0 ]; then
|
||||||
|
exit $ES
|
||||||
|
fi
|
||||||
|
while `kill -0 $PID 2>/dev/null`;
|
||||||
|
do
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
|
||||||
|
restart)
|
||||||
|
## Restart the VM without exiting the process
|
||||||
|
$NODETOOL restart
|
||||||
|
ES=$?
|
||||||
|
if [ "$ES" -ne 0 ]; then
|
||||||
|
exit $ES
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
reboot)
|
||||||
|
## Restart the VM completely (uses heart to restart it)
|
||||||
|
$NODETOOL reboot
|
||||||
|
ES=$?
|
||||||
|
if [ "$ES" -ne 0 ]; then
|
||||||
|
exit $ES
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
ping)
|
||||||
|
## See if the VM is alive
|
||||||
|
$NODETOOL ping
|
||||||
|
ES=$?
|
||||||
|
if [ "$ES" -ne 0 ]; then
|
||||||
|
exit $ES
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
attach)
|
||||||
|
# Make sure a node IS running
|
||||||
|
RES=`$NODETOOL ping`
|
||||||
|
ES=$?
|
||||||
|
if [ "$ES" -ne 0 ]; then
|
||||||
|
echo "Node is not running!"
|
||||||
|
exit $ES
|
||||||
|
fi
|
||||||
|
|
||||||
|
shift
|
||||||
|
exec $ERTS_PATH/to_erl $PIPE_DIR
|
||||||
|
;;
|
||||||
|
|
||||||
|
console)
|
||||||
|
# .boot file typically just $SCRIPT (ie, the app name)
|
||||||
|
# however, for debugging, sometimes start_clean.boot is useful:
|
||||||
|
#case "$1" in
|
||||||
|
# console) BOOTFILE=$SCRIPT ;;
|
||||||
|
# console_clean) BOOTFILE=start_clean ;;
|
||||||
|
#esac
|
||||||
|
# Setup beam-required vars
|
||||||
|
ERL_LIBS=$RUNNER_BASE_DIR/lib
|
||||||
|
ROOTDIR=$RUNNER_BASE_DIR
|
||||||
|
BINDIR=$ERTS_PATH
|
||||||
|
EMU=beam
|
||||||
|
PROGNAME=`echo $0 | sed 's/.*\\///'`
|
||||||
|
CMD="$ERTS_PATH/erl -pa $RUNNER_BASE_DIR/ebin -config $CONFIG_PATH -args_file $VMARGS_PATH -- ${1+"$@"}"
|
||||||
|
export ERL_LIBS
|
||||||
|
export EMU
|
||||||
|
export ROOTDIR
|
||||||
|
export BINDIR
|
||||||
|
export PROGNAME
|
||||||
|
|
||||||
|
# Dump environment info for logging purposes
|
||||||
|
echo "Exec: $CMD"
|
||||||
|
echo "Root: $ROOTDIR"
|
||||||
|
|
||||||
|
# Log the startup
|
||||||
|
logger -t "$SCRIPT[$$]" "Starting up"
|
||||||
|
|
||||||
|
# Start the VM
|
||||||
|
exec $CMD
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "Usage: $SCRIPT {start|stop|restart|reboot|ping|console||attach}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit 0
|
|
@ -0,0 +1,123 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
RUNNER_SCRIPT_DIR=$(cd ${0%/*} && pwd)
|
||||||
|
|
||||||
|
RUNNER_BASE_DIR=${RUNNER_SCRIPT_DIR%/*}
|
||||||
|
RUNNER_ETC_DIR=$RUNNER_BASE_DIR/etc
|
||||||
|
RUNNER_BIN_DIR=$RUNNER_BASE_DIR/bin
|
||||||
|
RUNNER_LOG_DIR=$RUNNER_BASE_DIR/log
|
||||||
|
|
||||||
|
RUNNER_EBIN_DIR=$RUNNER_BASE_DIR/ebin
|
||||||
|
RUNNER_USER=
|
||||||
|
|
||||||
|
# Make sure CWD is set to runner base dir
|
||||||
|
cd $RUNNER_BASE_DIR
|
||||||
|
|
||||||
|
# Extract the target node name from node.args
|
||||||
|
NAME_ARG=`grep '\-[s]*name' $RUNNER_ETC_DIR/emqtt.args`
|
||||||
|
if [ -z "$NAME_ARG" ]; then
|
||||||
|
echo "emqtt.args needs to have either -name or -sname parameter."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Learn how to specify node name for connection from remote nodes
|
||||||
|
echo "$NAME_ARG" | grep '^-sname' > /dev/null 2>&1
|
||||||
|
if [ "X$?" = "X0" ]; then
|
||||||
|
NAME_PARAM="-sname"
|
||||||
|
NAME_HOST=""
|
||||||
|
else
|
||||||
|
NAME_PARAM="-name"
|
||||||
|
echo "$NAME_ARG" | grep '@.*' > /dev/null 2>&1
|
||||||
|
if [ "X$?" = "X0" ]; then
|
||||||
|
NAME_HOST=`echo "${NAME_ARG}" | sed -e 's/.*\(@.*\)$/\1/'`
|
||||||
|
else
|
||||||
|
NAME_HOST=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract the target cookie
|
||||||
|
COOKIE_ARG=`grep '\-setcookie' $RUNNER_ETC_DIR/emqtt.args`
|
||||||
|
if [ -z "$COOKIE_ARG" ]; then
|
||||||
|
echo "emqtt.args needs to have a -setcookie parameter."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Identify the script name
|
||||||
|
SCRIPT=`basename $0`
|
||||||
|
|
||||||
|
# Parse out release and erts info
|
||||||
|
ERLANG_BASE_DIR=/usr/local/lib/erlang
|
||||||
|
START_ERL=`cat $ERLANG_BASE_DIR/releases/start_erl.data`
|
||||||
|
ERTS_VSN=${START_ERL% *}
|
||||||
|
APP_VSN=${START_ERL#* }
|
||||||
|
|
||||||
|
# Add ERTS bin dir to our path
|
||||||
|
ERTS_PATH=$ERLANG_BASE_DIR/erts-$ERTS_VSN/bin
|
||||||
|
|
||||||
|
# Setup command to control the node
|
||||||
|
NODETOOL="$ERTS_PATH/escript $RUNNER_BIN_DIR/nodetool $NAME_ARG $COOKIE_ARG"
|
||||||
|
|
||||||
|
# Check the first argument for instructions
|
||||||
|
case "$1" in
|
||||||
|
status)
|
||||||
|
if [ $# -ne 1 ]; then
|
||||||
|
echo "Usage: $SCRIPT status"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Make sure the local node IS running
|
||||||
|
RES=`$NODETOOL ping`
|
||||||
|
if [ "$RES" != "pong" ]; then
|
||||||
|
echo "Node is not running!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
|
||||||
|
$NODETOOL rpc emqtt_ctl status $@
|
||||||
|
;;
|
||||||
|
|
||||||
|
cluster_info)
|
||||||
|
if [ $# -ne 1 ]; then
|
||||||
|
echo "Usage: $SCRIPT cluster_info"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Make sure the local node IS running
|
||||||
|
RES=`$NODETOOL ping`
|
||||||
|
if [ "$RES" != "pong" ]; then
|
||||||
|
echo "Node is not running!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
|
||||||
|
$NODETOOL rpc emqtt_ctl cluster_info $@
|
||||||
|
;;
|
||||||
|
|
||||||
|
|
||||||
|
cluster)
|
||||||
|
if [ $# -ne 2 ]; then
|
||||||
|
echo "Usage: $SCRIPT cluster <Node>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Make sure the local node IS running
|
||||||
|
RES=`$NODETOOL ping`
|
||||||
|
if [ "$RES" != "pong" ]; then
|
||||||
|
echo "emqtt is not running!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
|
||||||
|
$NODETOOL rpc emqtt_ctl cluster $@
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "Usage: $SCRIPT"
|
||||||
|
echo " status #query emqtt status"
|
||||||
|
echo " cluster_info #query cluster nodes"
|
||||||
|
echo " cluster <Node> #cluster node"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
|
||||||
|
esac
|
||||||
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
|
||||||
|
%% ex: ft=erlang ts=4 sw=4 et
|
||||||
|
%% -------------------------------------------------------------------
|
||||||
|
%%
|
||||||
|
%% nodetool: Helper Script for interacting with live nodes
|
||||||
|
%%
|
||||||
|
%% -------------------------------------------------------------------
|
||||||
|
|
||||||
|
main(Args) ->
|
||||||
|
ok = start_epmd(),
|
||||||
|
%% Extract the args
|
||||||
|
{RestArgs, TargetNode} = process_args(Args, [], undefined),
|
||||||
|
|
||||||
|
%% See if the node is currently running -- if it's not, we'll bail
|
||||||
|
case {net_kernel:hidden_connect_node(TargetNode), net_adm:ping(TargetNode)} of
|
||||||
|
{true, pong} ->
|
||||||
|
ok;
|
||||||
|
{_, pang} ->
|
||||||
|
io:format("Node ~p not responding to pings.\n", [TargetNode]),
|
||||||
|
halt(1)
|
||||||
|
end,
|
||||||
|
|
||||||
|
case RestArgs of
|
||||||
|
["ping"] ->
|
||||||
|
%% If we got this far, the node already responsed to a ping, so just dump
|
||||||
|
%% a "pong"
|
||||||
|
io:format("pong\n");
|
||||||
|
["stop"] ->
|
||||||
|
io:format("~p\n", [rpc:call(TargetNode, init, stop, [], 60000)]);
|
||||||
|
["restart"] ->
|
||||||
|
io:format("~p\n", [rpc:call(TargetNode, init, restart, [], 60000)]);
|
||||||
|
["reboot"] ->
|
||||||
|
io:format("~p\n", [rpc:call(TargetNode, init, reboot, [], 60000)]);
|
||||||
|
["rpc", Module, Function | RpcArgs] ->
|
||||||
|
case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function),
|
||||||
|
RpcArgs, 60000) of
|
||||||
|
ok ->
|
||||||
|
ok;
|
||||||
|
{badrpc, Reason} ->
|
||||||
|
io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]),
|
||||||
|
halt(1);
|
||||||
|
_ ->
|
||||||
|
halt(1)
|
||||||
|
end;
|
||||||
|
["rpcterms", Module, Function, ArgsAsString] ->
|
||||||
|
case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function),
|
||||||
|
consult(ArgsAsString), 60000) of
|
||||||
|
{badrpc, Reason} ->
|
||||||
|
io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]),
|
||||||
|
halt(1);
|
||||||
|
Other ->
|
||||||
|
io:format("~p\n", [Other])
|
||||||
|
end;
|
||||||
|
Other ->
|
||||||
|
io:format("Other: ~p\n", [Other]),
|
||||||
|
io:format("Usage: nodetool {ping|stop|restart|reboot}\n")
|
||||||
|
end,
|
||||||
|
net_kernel:stop().
|
||||||
|
|
||||||
|
process_args([], Acc, TargetNode) ->
|
||||||
|
{lists:reverse(Acc), TargetNode};
|
||||||
|
process_args(["-setcookie", Cookie | Rest], Acc, TargetNode) ->
|
||||||
|
erlang:set_cookie(node(), list_to_atom(Cookie)),
|
||||||
|
process_args(Rest, Acc, TargetNode);
|
||||||
|
process_args(["-name", TargetName | Rest], Acc, _) ->
|
||||||
|
ThisNode = append_node_suffix(TargetName, "_ctrl_"),
|
||||||
|
{ok, _} = net_kernel:start([ThisNode, longnames]),
|
||||||
|
process_args(Rest, Acc, nodename(TargetName));
|
||||||
|
process_args(["-sname", TargetName | Rest], Acc, _) ->
|
||||||
|
ThisNode = append_node_suffix(TargetName, "_ctrl_"),
|
||||||
|
{ok, _} = net_kernel:start([ThisNode, shortnames]),
|
||||||
|
process_args(Rest, Acc, nodename(TargetName));
|
||||||
|
process_args([Arg | Rest], Acc, Opts) ->
|
||||||
|
process_args(Rest, [Arg | Acc], Opts).
|
||||||
|
|
||||||
|
|
||||||
|
start_epmd() ->
|
||||||
|
[] = os:cmd(epmd_path() ++ " -daemon"),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
epmd_path() ->
|
||||||
|
ErtsBinDir = filename:dirname(escript:script_name()),
|
||||||
|
Name = "epmd",
|
||||||
|
case os:find_executable(Name, ErtsBinDir) of
|
||||||
|
false ->
|
||||||
|
case os:find_executable(Name) of
|
||||||
|
false ->
|
||||||
|
io:format("Could not find epmd.~n"),
|
||||||
|
halt(1);
|
||||||
|
GlobalEpmd ->
|
||||||
|
GlobalEpmd
|
||||||
|
end;
|
||||||
|
Epmd ->
|
||||||
|
Epmd
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
nodename(Name) ->
|
||||||
|
case string:tokens(Name, "@") of
|
||||||
|
[_Node, _Host] ->
|
||||||
|
list_to_atom(Name);
|
||||||
|
[Node] ->
|
||||||
|
[_, Host] = string:tokens(atom_to_list(node()), "@"),
|
||||||
|
list_to_atom(lists:concat([Node, "@", Host]))
|
||||||
|
end.
|
||||||
|
|
||||||
|
append_node_suffix(Name, Suffix) ->
|
||||||
|
case string:tokens(Name, "@") of
|
||||||
|
[Node, Host] ->
|
||||||
|
list_to_atom(lists:concat([Node, Suffix, os:getpid(), "@", Host]));
|
||||||
|
[Node] ->
|
||||||
|
list_to_atom(lists:concat([Node, Suffix, os:getpid()]))
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
%%
|
||||||
|
%% Given a string or binary, parse it into a list of terms, ala file:consult/0
|
||||||
|
%%
|
||||||
|
consult(Str) when is_list(Str) ->
|
||||||
|
consult([], Str, []);
|
||||||
|
consult(Bin) when is_binary(Bin)->
|
||||||
|
consult([], binary_to_list(Bin), []).
|
||||||
|
|
||||||
|
consult(Cont, Str, Acc) ->
|
||||||
|
case erl_scan:tokens(Cont, Str, 0) of
|
||||||
|
{done, Result, Remaining} ->
|
||||||
|
case Result of
|
||||||
|
{ok, Tokens, _} ->
|
||||||
|
{ok, Term} = erl_parse:parse_term(Tokens),
|
||||||
|
consult([], Remaining, [Term | Acc]);
|
||||||
|
{eof, _Other} ->
|
||||||
|
lists:reverse(Acc);
|
||||||
|
{error, Info, _} ->
|
||||||
|
{error, Info}
|
||||||
|
end;
|
||||||
|
{more, Cont1} ->
|
||||||
|
consult(Cont1, eof, Acc)
|
||||||
|
end.
|
|
@ -0,0 +1,28 @@
|
||||||
|
## Name of the node
|
||||||
|
-sname emqtt
|
||||||
|
|
||||||
|
## Cookie for distributed erlang
|
||||||
|
-setcookie emqttsecret
|
||||||
|
|
||||||
|
#-boot start_sasl
|
||||||
|
|
||||||
|
-s emqtt start
|
||||||
|
|
||||||
|
## Heartbeat management; auto-restarts VM if it dies or becomes unresponsive
|
||||||
|
## (Disabled by default..use with caution!)
|
||||||
|
##-heart
|
||||||
|
-smp auto
|
||||||
|
|
||||||
|
## Enable kernel poll and a few async threads
|
||||||
|
+K true
|
||||||
|
+A 64
|
||||||
|
+P 10000
|
||||||
|
|
||||||
|
## Increase number of concurrent ports/sockets
|
||||||
|
-env ERL_MAX_PORTS 4096
|
||||||
|
|
||||||
|
-env ERL_MAX_ETS_TABLES 1400
|
||||||
|
|
||||||
|
## Tweak GC to run more often
|
||||||
|
-env ERL_FULLSWEEP_AFTER 10
|
||||||
|
|
|
@ -10,16 +10,27 @@
|
||||||
{mnesia, [
|
{mnesia, [
|
||||||
{dir, "var/mnesia"}
|
{dir, "var/mnesia"}
|
||||||
]},
|
]},
|
||||||
|
{lager, [
|
||||||
|
{error_logger_redirect, false},
|
||||||
|
{crash_log, "log/emqtt_crash.log"},
|
||||||
|
{handlers, [
|
||||||
|
{lager_console_backend, info},
|
||||||
|
{lager_file_backend, [
|
||||||
|
{"log/emqtt_error.log", error, 10485760, "$D0", 5},
|
||||||
|
{"log/emqtt_info.log", info, 10485760, "$D0", 5}
|
||||||
|
]}
|
||||||
|
]}
|
||||||
|
]},
|
||||||
{emqtt, [
|
{emqtt, [
|
||||||
{tcp_listeners, [1883]},
|
{listeners, [
|
||||||
{tcp_listen_options, [binary,
|
{1883, [
|
||||||
{packet, raw},
|
binary,
|
||||||
{reuseaddr, true},
|
{packet, raw},
|
||||||
{backlog, 128},
|
{reuseaddr, true},
|
||||||
{nodelay, true},
|
{backlog, 128},
|
||||||
{linger, {true, 0}},
|
{nodelay, true}
|
||||||
{exit_on_close, false}]}
|
]}
|
||||||
|
]}
|
||||||
]}
|
]}
|
||||||
].
|
].
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,25 @@
|
||||||
|
%% 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 Initial Developer of the Original Code is ery.lee@gmail.com
|
||||||
|
%% Copyright (c) 2012 Ery Lee. All rights reserved.
|
||||||
|
%%
|
||||||
|
|
||||||
|
%% ---------------------------------
|
||||||
|
%% banner
|
||||||
|
%% ---------------------------------
|
||||||
|
-define(COPYRIGHT, "Copyright (C) 2012 Ery Lee.").
|
||||||
|
|
||||||
-define(COPYRIGHT, "Copyright (C) 2007-2012 VMware, Inc.").
|
|
||||||
-define(LICENSE_MESSAGE, "Licensed under the MPL.").
|
-define(LICENSE_MESSAGE, "Licensed under the MPL.").
|
||||||
|
|
||||||
-define(PROTOCOL_VERSION, "MQTT/3.1").
|
-define(PROTOCOL_VERSION, "MQTT/3.1").
|
||||||
-define(ERTS_MINIMUM, "5.6.3").
|
-define(ERTS_MINIMUM, "5.6.3").
|
||||||
|
|
||||||
|
@ -9,3 +28,66 @@
|
||||||
-record(subscriber, {topic, pid}).
|
-record(subscriber, {topic, pid}).
|
||||||
|
|
||||||
|
|
||||||
|
%% ---------------------------------
|
||||||
|
%% Logging mechanism
|
||||||
|
|
||||||
|
-define(PRINT(Format, Args),
|
||||||
|
io:format(Format, Args)).
|
||||||
|
|
||||||
|
-define(PRINT_MSG(Msg),
|
||||||
|
io:format(Msg)).
|
||||||
|
|
||||||
|
-define(DEBUG(Format, Args),
|
||||||
|
lager:debug(Format, Args)).
|
||||||
|
|
||||||
|
-define(DEBUG_TRACE(Dest, Format, Args),
|
||||||
|
lager:debug(Dest, Format, Args)).
|
||||||
|
|
||||||
|
-define(DEBUG_MSG(Msg),
|
||||||
|
lager:debug(Msg)).
|
||||||
|
|
||||||
|
-define(INFO(Format, Args),
|
||||||
|
lager:info(Format, Args)).
|
||||||
|
|
||||||
|
-define(INFO_TRACE(Dest, Format, Args),
|
||||||
|
lager:info(Dest, Format, Args)).
|
||||||
|
|
||||||
|
-define(INFO_MSG(Msg),
|
||||||
|
lager:info(Msg)).
|
||||||
|
|
||||||
|
-define(WARN(Format, Args),
|
||||||
|
lager:warning(Format, Args)).
|
||||||
|
|
||||||
|
-define(WARN_TRACE(Dest, Format, Args),
|
||||||
|
lager:warning(Dest, Format, Args)).
|
||||||
|
|
||||||
|
-define(WARN_MSG(Msg),
|
||||||
|
lager:warning(Msg)).
|
||||||
|
|
||||||
|
-define(WARNING(Format, Args),
|
||||||
|
lager:warning(Format, Args)).
|
||||||
|
|
||||||
|
-define(WARNING_TRACE(Dest, Format, Args),
|
||||||
|
lager:warning(Dest, Format, Args)).
|
||||||
|
|
||||||
|
-define(WARNING_MSG(Msg),
|
||||||
|
lager:warning(Msg)).
|
||||||
|
|
||||||
|
-define(ERROR(Format, Args),
|
||||||
|
lager:error(Format, Args)).
|
||||||
|
|
||||||
|
-define(ERROR_TRACE(Dest, Format, Args),
|
||||||
|
lager:error(Dest, Format, Args)).
|
||||||
|
|
||||||
|
-define(ERROR_MSG(Msg),
|
||||||
|
lager:error(Msg)).
|
||||||
|
|
||||||
|
-define(CRITICAL(Format, Args),
|
||||||
|
lager:critical(Format, Args)).
|
||||||
|
|
||||||
|
-define(CRITICAL_TRACE(Dest, Format, Args),
|
||||||
|
lager:critical(Dest, Format, Args)).
|
||||||
|
|
||||||
|
-define(CRITICAL_MSG(Msg),
|
||||||
|
lager:critical(Msg)).
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
|
{erl_opts, [debug_info, {parse_transform, lager_transform}]}.
|
||||||
|
|
||||||
|
{erl_opts, [{i, "include"}]}.
|
||||||
|
|
||||||
{lib_dirs,["lib"]}.
|
{lib_dirs,["lib"]}.
|
||||||
|
|
||||||
{deps_dir, ["lib"]}.
|
{deps_dir, ["lib"]}.
|
||||||
|
|
||||||
{deps, [
|
{deps, [
|
||||||
{'rabbitlib', ".*", {git, "git://github.com/emqtt/rabbitlib.git", {branch, "master"}}}
|
{lager, ".*", {git, "git://github.com/basho/lager.git", {branch, "master"}}}
|
||||||
]}.
|
]}.
|
||||||
|
|
3
run
3
run
|
@ -1,3 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
erl -pa ebin -pa lib/rabbitlib/ebin -config etc/emqtt.config -s emqtt_app start
|
|
Binary file not shown.
|
@ -0,0 +1,11 @@
|
||||||
|
-module(emqtt).
|
||||||
|
|
||||||
|
-export([start/0]).
|
||||||
|
|
||||||
|
start() ->
|
||||||
|
ok = application:start(sasl),
|
||||||
|
mnesia:create_schema([node()]),
|
||||||
|
mnesia:start(),
|
||||||
|
lager:start(),
|
||||||
|
ok = application:start(emqtt).
|
||||||
|
|
|
@ -1,32 +1,20 @@
|
||||||
|
|
||||||
-module(emqtt_app).
|
-module(emqtt_app).
|
||||||
|
|
||||||
-export([start/0]).
|
-include("emqtt.hrl").
|
||||||
|
|
||||||
-behaviour(application).
|
-behaviour(application).
|
||||||
|
|
||||||
%% Application callbacks
|
%% Application callbacks
|
||||||
-export([start/2, stop/1]).
|
-export([start/2, stop/1]).
|
||||||
|
|
||||||
-define(APPS, [sasl, mnesia, emqtt]).
|
|
||||||
|
|
||||||
start() ->
|
|
||||||
[start_app(App) || App <- ?APPS].
|
|
||||||
|
|
||||||
start_app(mnesia) ->
|
|
||||||
mnesia:create_schema([node()]),
|
|
||||||
mnesia:start();
|
|
||||||
|
|
||||||
start_app(App) ->
|
|
||||||
application:start(App).
|
|
||||||
|
|
||||||
%% ===================================================================
|
%% ===================================================================
|
||||||
%% Application callbacks
|
%% Application callbacks
|
||||||
%% ===================================================================
|
%% ===================================================================
|
||||||
|
|
||||||
start(_StartType, _StartArgs) ->
|
start(_StartType, _StartArgs) ->
|
||||||
{ok, Sup} = emqtt_sup:start_link(),
|
{ok, Listeners} = application:get_env(listeners),
|
||||||
emqtt_networking:boot(),
|
emqtt_sup:start_link(Listeners).
|
||||||
{ok, Sup}.
|
|
||||||
|
|
||||||
stop(_State) ->
|
stop(_State) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
|
@ -179,7 +179,7 @@ run_socket(State = #state{ socket = Sock }) ->
|
||||||
|
|
||||||
control_throttle(State = #state{ connection_state = Flow,
|
control_throttle(State = #state{ connection_state = Flow,
|
||||||
conserve = Conserve }) ->
|
conserve = Conserve }) ->
|
||||||
case {Flow, Conserve orelse credit_flow:blocked()} of
|
case {Flow, Conserve} of
|
||||||
{running, true} -> State #state{ connection_state = blocked };
|
{running, true} -> State #state{ connection_state = blocked };
|
||||||
{blocked, false} -> run_socket(State #state{
|
{blocked, false} -> run_socket(State #state{
|
||||||
connection_state = running });
|
connection_state = running });
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
-module(emqtt_client_sup).
|
-module(emqtt_client_sup).
|
||||||
|
|
||||||
-export([start_link/0]).
|
-export([start_link/0, start_client/1]).
|
||||||
|
|
||||||
-behaviour(supervisor2).
|
-behaviour(supervisor2).
|
||||||
|
|
||||||
|
@ -14,3 +14,13 @@ init([]) ->
|
||||||
[{client, {emqtt_client, start_link, []},
|
[{client, {emqtt_client, start_link, []},
|
||||||
temporary, 5000, worker, [emqtt_client]}]}}.
|
temporary, 5000, worker, [emqtt_client]}]}}.
|
||||||
|
|
||||||
|
start_client(Sock) ->
|
||||||
|
{ok, Client} = supervisor:start_child(?MODULE, []),
|
||||||
|
ok = gen_tcp:controlling_process(Sock, Client),
|
||||||
|
emqtt_client:go(Client, Sock),
|
||||||
|
|
||||||
|
%% see comment in rabbit_networking:start_client/2
|
||||||
|
gen_event:which_handlers(error_logger),
|
||||||
|
|
||||||
|
Client.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
-module(emqtt_ctl).
|
||||||
|
|
||||||
|
-include("emqtt.hrl").
|
||||||
|
|
||||||
|
-compile(export_all).
|
||||||
|
|
||||||
|
status() ->
|
||||||
|
{InternalStatus, _ProvidedStatus} = init:get_status(),
|
||||||
|
?PRINT("Node ~p is ~p.", [node(), InternalStatus]),
|
||||||
|
case lists:keysearch(wifioss, 1, application:which_applications()) of
|
||||||
|
false ->
|
||||||
|
?PRINT_MSG("wifioss is not running~n");
|
||||||
|
{value,_Version} ->
|
||||||
|
?PRINT_MSG("wifioss is running~n")
|
||||||
|
end.
|
||||||
|
|
||||||
|
cluster_info() ->
|
||||||
|
Nodes = [node()|nodes()],
|
||||||
|
?PRINT("cluster nodes: ~p~n", [Nodes]).
|
||||||
|
|
||||||
|
cluster(Node) ->
|
||||||
|
case net_adm:ping(list_to_atom(Node)) of
|
||||||
|
pong ->
|
||||||
|
?PRINT("cluster with ~p successfully.~n", [Node]);
|
||||||
|
pang ->
|
||||||
|
?PRINT("failed to cluster with ~p~n", [Node])
|
||||||
|
end.
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
-module(emqtt_listener).
|
||||||
|
|
||||||
|
-include("emqtt.hrl").
|
||||||
|
|
||||||
|
-export([spec/2, listener_started/3, listener_stopped/3]).
|
||||||
|
|
||||||
|
spec({Listener, SockOpts}, Callback) ->
|
||||||
|
[tcp_listener_spec(emqtt_tcp_listener_sup, Address, SockOpts,
|
||||||
|
mqtt, "TCP Listener", Callback) || Address <- tcp_listener_addresses(Listener)].
|
||||||
|
|
||||||
|
tcp_listener_spec(NamePrefix, {IPAddress, Port, Family}, SocketOpts,
|
||||||
|
Protocol, Label, OnConnect) ->
|
||||||
|
{emqtt_net:tcp_name(NamePrefix, IPAddress, Port),
|
||||||
|
{tcp_listener_sup, start_link,
|
||||||
|
[IPAddress, Port, [Family | SocketOpts],
|
||||||
|
{?MODULE, listener_started, [Protocol]},
|
||||||
|
{?MODULE, listener_stopped, [Protocol]},
|
||||||
|
OnConnect, Label]},
|
||||||
|
transient, infinity, supervisor, [tcp_listener_sup]}.
|
||||||
|
|
||||||
|
tcp_listener_addresses(Port) when is_integer(Port) ->
|
||||||
|
tcp_listener_addresses_auto(Port);
|
||||||
|
tcp_listener_addresses({"auto", Port}) ->
|
||||||
|
%% Variant to prevent lots of hacking around in bash and batch files
|
||||||
|
tcp_listener_addresses_auto(Port);
|
||||||
|
tcp_listener_addresses({Host, Port}) ->
|
||||||
|
%% auto: determine family IPv4 / IPv6 after converting to IP address
|
||||||
|
tcp_listener_addresses({Host, Port, auto});
|
||||||
|
tcp_listener_addresses({Host, Port, Family0})
|
||||||
|
when is_integer(Port) andalso (Port >= 0) andalso (Port =< 65535) ->
|
||||||
|
[{IPAddress, Port, Family} ||
|
||||||
|
{IPAddress, Family} <- emqtt_net:getaddr(Host, Family0)];
|
||||||
|
tcp_listener_addresses({_Host, Port, _Family0}) ->
|
||||||
|
?ERROR("invalid port ~p - not 0..65535~n", [Port]),
|
||||||
|
throw({error, {invalid_port, Port}}).
|
||||||
|
|
||||||
|
tcp_listener_addresses_auto(Port) ->
|
||||||
|
lists:append([tcp_listener_addresses(Listener) ||
|
||||||
|
Listener <- emqtt_net:port_to_listeners(Port)]).
|
||||||
|
|
||||||
|
%--------------------------------------------
|
||||||
|
%TODO: callback
|
||||||
|
%--------------------------------------------
|
||||||
|
listener_started(Protocol, IPAddress, Port) ->
|
||||||
|
%% We need the ip to distinguish e.g. 0.0.0.0 and 127.0.0.1
|
||||||
|
%% We need the host so we can distinguish multiple instances of the above
|
||||||
|
%% in a cluster.
|
||||||
|
?INFO("tcp listener started: ~p ~p:~p", [Protocol, IPAddress, Port]).
|
||||||
|
|
||||||
|
listener_stopped(Protocol, IPAddress, Port) ->
|
||||||
|
?INFO("tcp listener stopped: ~p ~p:~p", [Protocol, IPAddress, Port]).
|
||||||
|
|
|
@ -1,9 +1,137 @@
|
||||||
-module(emqtt_net).
|
-module(emqtt_net).
|
||||||
|
|
||||||
|
-export([tcp_name/3, tcp_host/1, getaddr/2, port_to_listeners/1]).
|
||||||
|
|
||||||
-export([tune_buffer_size/1, connection_string/2]).
|
-export([tune_buffer_size/1, connection_string/2]).
|
||||||
|
|
||||||
-include_lib("kernel/include/inet.hrl").
|
-include_lib("kernel/include/inet.hrl").
|
||||||
|
|
||||||
|
-define(FIRST_TEST_BIND_PORT, 10000).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
%% inet_parse:address takes care of ip string, like "0.0.0.0"
|
||||||
|
%% inet:getaddr returns immediately for ip tuple {0,0,0,0},
|
||||||
|
%% and runs 'inet_gethost' port process for dns lookups.
|
||||||
|
%% On Windows inet:getaddr runs dns resolver for ip string, which may fail.
|
||||||
|
getaddr(Host, Family) ->
|
||||||
|
case inet_parse:address(Host) of
|
||||||
|
{ok, IPAddress} -> [{IPAddress, resolve_family(IPAddress, Family)}];
|
||||||
|
{error, _} -> gethostaddr(Host, Family)
|
||||||
|
end.
|
||||||
|
|
||||||
|
gethostaddr(Host, auto) ->
|
||||||
|
Lookups = [{Family, inet:getaddr(Host, Family)} || Family <- [inet, inet6]],
|
||||||
|
case [{IP, Family} || {Family, {ok, IP}} <- Lookups] of
|
||||||
|
[] -> host_lookup_error(Host, Lookups);
|
||||||
|
IPs -> IPs
|
||||||
|
end;
|
||||||
|
|
||||||
|
gethostaddr(Host, Family) ->
|
||||||
|
case inet:getaddr(Host, Family) of
|
||||||
|
{ok, IPAddress} -> [{IPAddress, Family}];
|
||||||
|
{error, Reason} -> host_lookup_error(Host, Reason)
|
||||||
|
end.
|
||||||
|
|
||||||
|
host_lookup_error(Host, Reason) ->
|
||||||
|
error_logger:error_msg("invalid host ~p - ~p~n", [Host, Reason]),
|
||||||
|
throw({error, {invalid_host, Host, Reason}}).
|
||||||
|
|
||||||
|
resolve_family({_,_,_,_}, auto) -> inet;
|
||||||
|
resolve_family({_,_,_,_,_,_,_,_}, auto) -> inet6;
|
||||||
|
resolve_family(IP, auto) -> throw({error, {strange_family, IP}});
|
||||||
|
resolve_family(_, F) -> F.
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
%% There are three kinds of machine (for our purposes).
|
||||||
|
%%
|
||||||
|
%% * Those which treat IPv4 addresses as a special kind of IPv6 address
|
||||||
|
%% ("Single stack")
|
||||||
|
%% - Linux by default, Windows Vista and later
|
||||||
|
%% - We also treat any (hypothetical?) IPv6-only machine the same way
|
||||||
|
%% * Those which consider IPv6 and IPv4 to be completely separate things
|
||||||
|
%% ("Dual stack")
|
||||||
|
%% - OpenBSD, Windows XP / 2003, Linux if so configured
|
||||||
|
%% * Those which do not support IPv6.
|
||||||
|
%% - Ancient/weird OSes, Linux if so configured
|
||||||
|
%%
|
||||||
|
%% How to reconfigure Linux to test this:
|
||||||
|
%% Single stack (default):
|
||||||
|
%% echo 0 > /proc/sys/net/ipv6/bindv6only
|
||||||
|
%% Dual stack:
|
||||||
|
%% echo 1 > /proc/sys/net/ipv6/bindv6only
|
||||||
|
%% IPv4 only:
|
||||||
|
%% add ipv6.disable=1 to GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub then
|
||||||
|
%% sudo update-grub && sudo reboot
|
||||||
|
%%
|
||||||
|
%% This matters in (and only in) the case where the sysadmin (or the
|
||||||
|
%% app descriptor) has only supplied a port and we wish to bind to
|
||||||
|
%% "all addresses". This means different things depending on whether
|
||||||
|
%% we're single or dual stack. On single stack binding to "::"
|
||||||
|
%% implicitly includes all IPv4 addresses, and subsequently attempting
|
||||||
|
%% to bind to "0.0.0.0" will fail. On dual stack, binding to "::" will
|
||||||
|
%% only bind to IPv6 addresses, and we need another listener bound to
|
||||||
|
%% "0.0.0.0" for IPv4. Finally, on IPv4-only systems we of course only
|
||||||
|
%% want to bind to "0.0.0.0".
|
||||||
|
%%
|
||||||
|
%% Unfortunately it seems there is no way to detect single vs dual stack
|
||||||
|
%% apart from attempting to bind to the port.
|
||||||
|
port_to_listeners(Port) ->
|
||||||
|
IPv4 = {"0.0.0.0", Port, inet},
|
||||||
|
IPv6 = {"::", Port, inet6},
|
||||||
|
case ipv6_status(?FIRST_TEST_BIND_PORT) of
|
||||||
|
single_stack -> [IPv6];
|
||||||
|
ipv6_only -> [IPv6];
|
||||||
|
dual_stack -> [IPv6, IPv4];
|
||||||
|
ipv4_only -> [IPv4]
|
||||||
|
end.
|
||||||
|
|
||||||
|
ipv6_status(TestPort) ->
|
||||||
|
IPv4 = [inet, {ip, {0,0,0,0}}],
|
||||||
|
IPv6 = [inet6, {ip, {0,0,0,0,0,0,0,0}}],
|
||||||
|
case gen_tcp:listen(TestPort, IPv6) of
|
||||||
|
{ok, LSock6} ->
|
||||||
|
case gen_tcp:listen(TestPort, IPv4) of
|
||||||
|
{ok, LSock4} ->
|
||||||
|
%% Dual stack
|
||||||
|
gen_tcp:close(LSock6),
|
||||||
|
gen_tcp:close(LSock4),
|
||||||
|
dual_stack;
|
||||||
|
%% Checking the error here would only let us
|
||||||
|
%% distinguish single stack IPv6 / IPv4 vs IPv6 only,
|
||||||
|
%% which we figure out below anyway.
|
||||||
|
{error, _} ->
|
||||||
|
gen_tcp:close(LSock6),
|
||||||
|
case gen_tcp:listen(TestPort, IPv4) of
|
||||||
|
%% Single stack
|
||||||
|
{ok, LSock4} -> gen_tcp:close(LSock4),
|
||||||
|
single_stack;
|
||||||
|
%% IPv6-only machine. Welcome to the future.
|
||||||
|
{error, eafnosupport} -> ipv6_only; %% Linux
|
||||||
|
{error, eprotonosupport}-> ipv6_only; %% FreeBSD
|
||||||
|
%% Dual stack machine with something already
|
||||||
|
%% on IPv4.
|
||||||
|
{error, _} -> ipv6_status(TestPort + 1)
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
%% IPv4-only machine. Welcome to the 90s.
|
||||||
|
{error, eafnosupport} -> %% Linux
|
||||||
|
ipv4_only;
|
||||||
|
{error, eprotonosupport} -> %% FreeBSD
|
||||||
|
ipv4_only;
|
||||||
|
%% Port in use
|
||||||
|
{error, _} ->
|
||||||
|
ipv6_status(TestPort + 1)
|
||||||
|
end.
|
||||||
|
|
||||||
|
tcp_name(Prefix, IPAddress, Port)
|
||||||
|
when is_atom(Prefix) andalso is_number(Port) ->
|
||||||
|
list_to_atom(
|
||||||
|
lists:flatten(
|
||||||
|
io_lib:format(
|
||||||
|
"~w_~s:~w", [Prefix, inet_parse:ntoa(IPAddress), Port]))).
|
||||||
|
|
||||||
tune_buffer_size(Sock) ->
|
tune_buffer_size(Sock) ->
|
||||||
case getopts(Sock, [sndbuf, recbuf, buffer]) of
|
case getopts(Sock, [sndbuf, recbuf, buffer]) of
|
||||||
{ok, BufSizes} -> BufSz = lists:max([Sz || {_Opt, Sz} <- BufSizes]),
|
{ok, BufSizes} -> BufSz = lists:max([Sz || {_Opt, Sz} <- BufSizes]),
|
||||||
|
@ -14,16 +142,15 @@ tune_buffer_size(Sock) ->
|
||||||
connection_string(Sock, Direction) ->
|
connection_string(Sock, Direction) ->
|
||||||
case socket_ends(Sock, Direction) of
|
case socket_ends(Sock, Direction) of
|
||||||
{ok, {FromAddress, FromPort, ToAddress, ToPort}} ->
|
{ok, {FromAddress, FromPort, ToAddress, ToPort}} ->
|
||||||
{ok, format(
|
{ok, lists:flatten(
|
||||||
"~s:~p -> ~s:~p",
|
io_lib:format(
|
||||||
[maybe_ntoab(FromAddress), FromPort,
|
"~s:~p -> ~s:~p",
|
||||||
maybe_ntoab(ToAddress), ToPort])};
|
[maybe_ntoab(FromAddress), FromPort,
|
||||||
|
maybe_ntoab(ToAddress), ToPort]))};
|
||||||
Error ->
|
Error ->
|
||||||
Error
|
Error
|
||||||
end.
|
end.
|
||||||
|
|
||||||
format(Fmt, Args) -> lists:flatten(io_lib:format(Fmt, Args)).
|
|
||||||
|
|
||||||
socket_ends(Sock, Direction) ->
|
socket_ends(Sock, Direction) ->
|
||||||
{From, To} = sock_funs(Direction),
|
{From, To} = sock_funs(Direction),
|
||||||
case {From(Sock), To(Sock)} of
|
case {From(Sock), To(Sock)} of
|
||||||
|
@ -84,3 +211,5 @@ hostname() ->
|
||||||
{ok, #hostent{h_name = Name}} -> Name;
|
{ok, #hostent{h_name = Name}} -> Name;
|
||||||
{error, _Reason} -> Hostname
|
{error, _Reason} -> Hostname
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,257 +0,0 @@
|
||||||
-module(emqtt_networking).
|
|
||||||
|
|
||||||
-export([boot/0]).
|
|
||||||
|
|
||||||
-export([start_tcp_listener/1, stop_tcp_listener/1, tcp_host/1, ntoab/1]).
|
|
||||||
|
|
||||||
%callback.
|
|
||||||
|
|
||||||
-export([tcp_listener_started/3, tcp_listener_stopped/3, start_client/1]).
|
|
||||||
|
|
||||||
-include_lib("kernel/include/inet.hrl").
|
|
||||||
|
|
||||||
-define(FIRST_TEST_BIND_PORT, 10000).
|
|
||||||
|
|
||||||
boot() ->
|
|
||||||
{ok, TcpListeners} = application:get_env(tcp_listeners),
|
|
||||||
[ok = start_tcp_listener(Listener) || Listener <- TcpListeners].
|
|
||||||
|
|
||||||
start_tcp_listener(Listener) ->
|
|
||||||
start_listener(Listener, emqtt, "TCP Listener",
|
|
||||||
{?MODULE, start_client, []}).
|
|
||||||
|
|
||||||
start_listener(Listener, Protocol, Label, OnConnect) ->
|
|
||||||
[start_listener0(Address, Protocol, Label, OnConnect) ||
|
|
||||||
Address <- tcp_listener_addresses(Listener)],
|
|
||||||
ok.
|
|
||||||
|
|
||||||
start_listener0(Address, Protocol, Label, OnConnect) ->
|
|
||||||
Spec = tcp_listener_spec(emqtt_tcp_listener_sup, Address, tcp_opts(),
|
|
||||||
Protocol, Label, OnConnect),
|
|
||||||
case supervisor:start_child(emqtt_sup, Spec) of
|
|
||||||
{ok, _} -> ok;
|
|
||||||
{error, {shutdown, _}} -> {IPAddress, Port, _Family} = Address,
|
|
||||||
exit({could_not_start_tcp_listener,
|
|
||||||
{ntoa(IPAddress), Port}})
|
|
||||||
end.
|
|
||||||
|
|
||||||
stop_tcp_listener(Listener) ->
|
|
||||||
[stop_tcp_listener0(Address) ||
|
|
||||||
Address <- tcp_listener_addresses(Listener)],
|
|
||||||
ok.
|
|
||||||
|
|
||||||
stop_tcp_listener0({IPAddress, Port, _Family}) ->
|
|
||||||
Name = tcp_name(emqtt_tcp_listener_sup, IPAddress, Port),
|
|
||||||
ok = supervisor:terminate_child(emqtt_sup, Name),
|
|
||||||
ok = supervisor:delete_child(emqtt_sup, Name).
|
|
||||||
|
|
||||||
tcp_listener_addresses(Port) when is_integer(Port) ->
|
|
||||||
tcp_listener_addresses_auto(Port);
|
|
||||||
tcp_listener_addresses({"auto", Port}) ->
|
|
||||||
%% Variant to prevent lots of hacking around in bash and batch files
|
|
||||||
tcp_listener_addresses_auto(Port);
|
|
||||||
tcp_listener_addresses({Host, Port}) ->
|
|
||||||
%% auto: determine family IPv4 / IPv6 after converting to IP address
|
|
||||||
tcp_listener_addresses({Host, Port, auto});
|
|
||||||
tcp_listener_addresses({Host, Port, Family0})
|
|
||||||
when is_integer(Port) andalso (Port >= 0) andalso (Port =< 65535) ->
|
|
||||||
[{IPAddress, Port, Family} ||
|
|
||||||
{IPAddress, Family} <- getaddr(Host, Family0)];
|
|
||||||
tcp_listener_addresses({_Host, Port, _Family0}) ->
|
|
||||||
error_logger:error_msg("invalid port ~p - not 0..65535~n", [Port]),
|
|
||||||
throw({error, {invalid_port, Port}}).
|
|
||||||
|
|
||||||
tcp_listener_addresses_auto(Port) ->
|
|
||||||
lists:append([tcp_listener_addresses(Listener) ||
|
|
||||||
Listener <- port_to_listeners(Port)]).
|
|
||||||
|
|
||||||
tcp_listener_spec(NamePrefix, {IPAddress, Port, Family}, SocketOpts,
|
|
||||||
Protocol, Label, OnConnect) ->
|
|
||||||
{tcp_name(NamePrefix, IPAddress, Port),
|
|
||||||
{tcp_listener_sup, start_link,
|
|
||||||
[IPAddress, Port, [Family | SocketOpts],
|
|
||||||
{?MODULE, tcp_listener_started, [Protocol]},
|
|
||||||
{?MODULE, tcp_listener_stopped, [Protocol]},
|
|
||||||
OnConnect, Label]},
|
|
||||||
transient, infinity, supervisor, [tcp_listener_sup]}.
|
|
||||||
|
|
||||||
|
|
||||||
tcp_listener_started(Protocol, IPAddress, Port) ->
|
|
||||||
%% We need the ip to distinguish e.g. 0.0.0.0 and 127.0.0.1
|
|
||||||
%% We need the host so we can distinguish multiple instances of the above
|
|
||||||
%% in a cluster.
|
|
||||||
error_logger:info_msg("tcp listener started: ~p ~p:~p", [Protocol, IPAddress, Port]).
|
|
||||||
|
|
||||||
tcp_listener_stopped(Protocol, IPAddress, Port) ->
|
|
||||||
error_logger:info_msg("tcp listener stopped: ~p ~p:~p", [Protocol, IPAddress, Port]).
|
|
||||||
|
|
||||||
start_client(Sock) ->
|
|
||||||
{ok, Client} = supervisor:start_child(emqtt_client_sup, []),
|
|
||||||
ok = gen_tcp:controlling_process(Sock, Client),
|
|
||||||
emqtt_client:go(Client, Sock),
|
|
||||||
|
|
||||||
%% see comment in rabbit_networking:start_client/2
|
|
||||||
gen_event:which_handlers(error_logger),
|
|
||||||
|
|
||||||
Client.
|
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
tcp_host({0,0,0,0}) ->
|
|
||||||
hostname();
|
|
||||||
|
|
||||||
tcp_host({0,0,0,0,0,0,0,0}) ->
|
|
||||||
hostname();
|
|
||||||
|
|
||||||
tcp_host(IPAddress) ->
|
|
||||||
case inet:gethostbyaddr(IPAddress) of
|
|
||||||
{ok, #hostent{h_name = Name}} -> Name;
|
|
||||||
{error, _Reason} -> ntoa(IPAddress)
|
|
||||||
end.
|
|
||||||
|
|
||||||
hostname() ->
|
|
||||||
{ok, Hostname} = inet:gethostname(),
|
|
||||||
case inet:gethostbyname(Hostname) of
|
|
||||||
{ok, #hostent{h_name = Name}} -> Name;
|
|
||||||
{error, _Reason} -> Hostname
|
|
||||||
end.
|
|
||||||
|
|
||||||
tcp_opts() ->
|
|
||||||
{ok, Opts} = application:get_env(emqtt, tcp_listen_options),
|
|
||||||
Opts.
|
|
||||||
|
|
||||||
%% inet_parse:address takes care of ip string, like "0.0.0.0"
|
|
||||||
%% inet:getaddr returns immediately for ip tuple {0,0,0,0},
|
|
||||||
%% and runs 'inet_gethost' port process for dns lookups.
|
|
||||||
%% On Windows inet:getaddr runs dns resolver for ip string, which may fail.
|
|
||||||
getaddr(Host, Family) ->
|
|
||||||
case inet_parse:address(Host) of
|
|
||||||
{ok, IPAddress} -> [{IPAddress, resolve_family(IPAddress, Family)}];
|
|
||||||
{error, _} -> gethostaddr(Host, Family)
|
|
||||||
end.
|
|
||||||
|
|
||||||
gethostaddr(Host, auto) ->
|
|
||||||
Lookups = [{Family, inet:getaddr(Host, Family)} || Family <- [inet, inet6]],
|
|
||||||
case [{IP, Family} || {Family, {ok, IP}} <- Lookups] of
|
|
||||||
[] -> host_lookup_error(Host, Lookups);
|
|
||||||
IPs -> IPs
|
|
||||||
end;
|
|
||||||
|
|
||||||
gethostaddr(Host, Family) ->
|
|
||||||
case inet:getaddr(Host, Family) of
|
|
||||||
{ok, IPAddress} -> [{IPAddress, Family}];
|
|
||||||
{error, Reason} -> host_lookup_error(Host, Reason)
|
|
||||||
end.
|
|
||||||
|
|
||||||
host_lookup_error(Host, Reason) ->
|
|
||||||
error_logger:error_msg("invalid host ~p - ~p~n", [Host, Reason]),
|
|
||||||
throw({error, {invalid_host, Host, Reason}}).
|
|
||||||
|
|
||||||
resolve_family({_,_,_,_}, auto) -> inet;
|
|
||||||
resolve_family({_,_,_,_,_,_,_,_}, auto) -> inet6;
|
|
||||||
resolve_family(IP, auto) -> throw({error, {strange_family, IP}});
|
|
||||||
resolve_family(_, F) -> F.
|
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
|
|
||||||
%% There are three kinds of machine (for our purposes).
|
|
||||||
%%
|
|
||||||
%% * Those which treat IPv4 addresses as a special kind of IPv6 address
|
|
||||||
%% ("Single stack")
|
|
||||||
%% - Linux by default, Windows Vista and later
|
|
||||||
%% - We also treat any (hypothetical?) IPv6-only machine the same way
|
|
||||||
%% * Those which consider IPv6 and IPv4 to be completely separate things
|
|
||||||
%% ("Dual stack")
|
|
||||||
%% - OpenBSD, Windows XP / 2003, Linux if so configured
|
|
||||||
%% * Those which do not support IPv6.
|
|
||||||
%% - Ancient/weird OSes, Linux if so configured
|
|
||||||
%%
|
|
||||||
%% How to reconfigure Linux to test this:
|
|
||||||
%% Single stack (default):
|
|
||||||
%% echo 0 > /proc/sys/net/ipv6/bindv6only
|
|
||||||
%% Dual stack:
|
|
||||||
%% echo 1 > /proc/sys/net/ipv6/bindv6only
|
|
||||||
%% IPv4 only:
|
|
||||||
%% add ipv6.disable=1 to GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub then
|
|
||||||
%% sudo update-grub && sudo reboot
|
|
||||||
%%
|
|
||||||
%% This matters in (and only in) the case where the sysadmin (or the
|
|
||||||
%% app descriptor) has only supplied a port and we wish to bind to
|
|
||||||
%% "all addresses". This means different things depending on whether
|
|
||||||
%% we're single or dual stack. On single stack binding to "::"
|
|
||||||
%% implicitly includes all IPv4 addresses, and subsequently attempting
|
|
||||||
%% to bind to "0.0.0.0" will fail. On dual stack, binding to "::" will
|
|
||||||
%% only bind to IPv6 addresses, and we need another listener bound to
|
|
||||||
%% "0.0.0.0" for IPv4. Finally, on IPv4-only systems we of course only
|
|
||||||
%% want to bind to "0.0.0.0".
|
|
||||||
%%
|
|
||||||
%% Unfortunately it seems there is no way to detect single vs dual stack
|
|
||||||
%% apart from attempting to bind to the port.
|
|
||||||
port_to_listeners(Port) ->
|
|
||||||
IPv4 = {"0.0.0.0", Port, inet},
|
|
||||||
IPv6 = {"::", Port, inet6},
|
|
||||||
case ipv6_status(?FIRST_TEST_BIND_PORT) of
|
|
||||||
single_stack -> [IPv6];
|
|
||||||
ipv6_only -> [IPv6];
|
|
||||||
dual_stack -> [IPv6, IPv4];
|
|
||||||
ipv4_only -> [IPv4]
|
|
||||||
end.
|
|
||||||
|
|
||||||
ipv6_status(TestPort) ->
|
|
||||||
IPv4 = [inet, {ip, {0,0,0,0}}],
|
|
||||||
IPv6 = [inet6, {ip, {0,0,0,0,0,0,0,0}}],
|
|
||||||
case gen_tcp:listen(TestPort, IPv6) of
|
|
||||||
{ok, LSock6} ->
|
|
||||||
case gen_tcp:listen(TestPort, IPv4) of
|
|
||||||
{ok, LSock4} ->
|
|
||||||
%% Dual stack
|
|
||||||
gen_tcp:close(LSock6),
|
|
||||||
gen_tcp:close(LSock4),
|
|
||||||
dual_stack;
|
|
||||||
%% Checking the error here would only let us
|
|
||||||
%% distinguish single stack IPv6 / IPv4 vs IPv6 only,
|
|
||||||
%% which we figure out below anyway.
|
|
||||||
{error, _} ->
|
|
||||||
gen_tcp:close(LSock6),
|
|
||||||
case gen_tcp:listen(TestPort, IPv4) of
|
|
||||||
%% Single stack
|
|
||||||
{ok, LSock4} -> gen_tcp:close(LSock4),
|
|
||||||
single_stack;
|
|
||||||
%% IPv6-only machine. Welcome to the future.
|
|
||||||
{error, eafnosupport} -> ipv6_only; %% Linux
|
|
||||||
{error, eprotonosupport}-> ipv6_only; %% FreeBSD
|
|
||||||
%% Dual stack machine with something already
|
|
||||||
%% on IPv4.
|
|
||||||
{error, _} -> ipv6_status(TestPort + 1)
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
%% IPv4-only machine. Welcome to the 90s.
|
|
||||||
{error, eafnosupport} -> %% Linux
|
|
||||||
ipv4_only;
|
|
||||||
{error, eprotonosupport} -> %% FreeBSD
|
|
||||||
ipv4_only;
|
|
||||||
%% Port in use
|
|
||||||
{error, _} ->
|
|
||||||
ipv6_status(TestPort + 1)
|
|
||||||
end.
|
|
||||||
|
|
||||||
ntoa({0,0,0,0,0,16#ffff,AB,CD}) ->
|
|
||||||
inet_parse:ntoa({AB bsr 8, AB rem 256, CD bsr 8, CD rem 256});
|
|
||||||
ntoa(IP) ->
|
|
||||||
inet_parse:ntoa(IP).
|
|
||||||
|
|
||||||
ntoab(IP) ->
|
|
||||||
Str = ntoa(IP),
|
|
||||||
case string:str(Str, ":") of
|
|
||||||
0 -> Str;
|
|
||||||
_ -> "[" ++ Str ++ "]"
|
|
||||||
end.
|
|
||||||
|
|
||||||
tcp_name(Prefix, IPAddress, Port)
|
|
||||||
when is_atom(Prefix) andalso is_number(Port) ->
|
|
||||||
list_to_atom(
|
|
||||||
format("~w_~s:~w", [Prefix, inet_parse:ntoa(IPAddress), Port])).
|
|
||||||
|
|
||||||
format(Fmt, Args) -> lists:flatten(io_lib:format(Fmt, Args)).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
-module(emqtt_router).
|
-module(emqtt_router).
|
||||||
|
|
||||||
-include("emqtt.hrl").
|
-include("emqtt.hrl").
|
||||||
|
|
||||||
-include("emqtt_frame.hrl").
|
-include("emqtt_frame.hrl").
|
||||||
|
|
||||||
-export([start_link/0]).
|
-export([start_link/0]).
|
||||||
|
@ -44,8 +45,8 @@ delete(Sub) when is_record(Sub, subscriber) ->
|
||||||
gen_server:cast(?MODULE, {delete, Sub}).
|
gen_server:cast(?MODULE, {delete, Sub}).
|
||||||
|
|
||||||
init([]) ->
|
init([]) ->
|
||||||
Res = ets:new(subscriber, [bag, protected, named_table, {keypos, 2}]),
|
ets:new(subscriber, [bag, named_table, {keypos, 2}]),
|
||||||
error_logger:info_msg("emqtt_router is started: ~p~n", [Res]),
|
?INFO_MSG("emqtt_router is started."),
|
||||||
{ok, #state{}}.
|
{ok, #state{}}.
|
||||||
|
|
||||||
handle_call({insert, Sub}, _From, State) ->
|
handle_call({insert, Sub}, _From, State) ->
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
-behaviour(supervisor).
|
-behaviour(supervisor).
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([start_link/0]).
|
-export([start_link/1]).
|
||||||
|
|
||||||
%% Supervisor callbacks
|
%% Supervisor callbacks
|
||||||
-export([init/1]).
|
-export([init/1]).
|
||||||
|
@ -17,17 +17,25 @@
|
||||||
%% API functions
|
%% API functions
|
||||||
%% ===================================================================
|
%% ===================================================================
|
||||||
|
|
||||||
start_link() ->
|
start_link(Listeners) ->
|
||||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
supervisor:start_link({local, ?MODULE}, ?MODULE, [Listeners]).
|
||||||
|
|
||||||
%% ===================================================================
|
%% ===================================================================
|
||||||
%% Supervisor callbacks
|
%% Supervisor callbacks
|
||||||
%% ===================================================================
|
%% ===================================================================
|
||||||
|
|
||||||
init([]) ->
|
init([Listeners]) ->
|
||||||
{ok, { {one_for_all, 5, 10}, [
|
{ok, { {one_for_all, 5, 10}, [
|
||||||
?CHILD(emqtt_topic, worker),
|
?CHILD(emqtt_topic, worker),
|
||||||
?CHILD(emqtt_router, worker),
|
?CHILD(emqtt_router, worker),
|
||||||
?CHILD(emqtt_client_sup, supervisor)
|
?CHILD(emqtt_client_sup, supervisor)
|
||||||
]} }.
|
| listener_children(Listeners) ]}
|
||||||
|
}.
|
||||||
|
|
||||||
|
listener_children(Listeners) ->
|
||||||
|
lists:append([emqtt_listener:spec(Listener,
|
||||||
|
{emqtt_client_sup, start_client, []}) || Listener <- Listeners]).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ init([]) ->
|
||||||
{record_name, topic},
|
{record_name, topic},
|
||||||
{ram_copies, [node()]},
|
{ram_copies, [node()]},
|
||||||
{attributes, record_info(fields, topic)}]),
|
{attributes, record_info(fields, topic)}]),
|
||||||
error_logger:info_msg("emqtt_topic is started."),
|
?INFO_MSG("emqtt_topic is started."),
|
||||||
{ok, #state{}}.
|
{ok, #state{}}.
|
||||||
|
|
||||||
handle_call({insert, Topic}, _From, State) ->
|
handle_call({insert, Topic}, _From, State) ->
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,194 @@
|
||||||
|
%% 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 VMware, Inc.
|
||||||
|
%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved.
|
||||||
|
%%
|
||||||
|
|
||||||
|
%% Priority queues have essentially the same interface as ordinary
|
||||||
|
%% queues, except that a) there is an in/3 that takes a priority, and
|
||||||
|
%% b) we have only implemented the core API we need.
|
||||||
|
%%
|
||||||
|
%% Priorities should be integers - the higher the value the higher the
|
||||||
|
%% priority - but we don't actually check that.
|
||||||
|
%%
|
||||||
|
%% in/2 inserts items with priority 0.
|
||||||
|
%%
|
||||||
|
%% We optimise the case where a priority queue is being used just like
|
||||||
|
%% an ordinary queue. When that is the case we represent the priority
|
||||||
|
%% queue as an ordinary queue. We could just call into the 'queue'
|
||||||
|
%% module for that, but for efficiency we implement the relevant
|
||||||
|
%% functions directly in here, thus saving on inter-module calls and
|
||||||
|
%% eliminating a level of boxing.
|
||||||
|
%%
|
||||||
|
%% When the queue contains items with non-zero priorities, it is
|
||||||
|
%% represented as a sorted kv list with the inverted Priority as the
|
||||||
|
%% key and an ordinary queue as the value. Here again we use our own
|
||||||
|
%% ordinary queue implemention for efficiency, often making recursive
|
||||||
|
%% calls into the same function knowing that ordinary queues represent
|
||||||
|
%% a base case.
|
||||||
|
|
||||||
|
|
||||||
|
-module(priority_queue).
|
||||||
|
|
||||||
|
-export([new/0, is_queue/1, is_empty/1, len/1, to_list/1, in/2, in/3,
|
||||||
|
out/1, join/2]).
|
||||||
|
|
||||||
|
%%----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-ifdef(use_specs).
|
||||||
|
|
||||||
|
-export_type([q/0]).
|
||||||
|
|
||||||
|
-type(q() :: pqueue()).
|
||||||
|
-type(priority() :: integer() | 'infinity').
|
||||||
|
-type(squeue() :: {queue, [any()], [any()]}).
|
||||||
|
-type(pqueue() :: squeue() | {pqueue, [{priority(), squeue()}]}).
|
||||||
|
|
||||||
|
-spec(new/0 :: () -> pqueue()).
|
||||||
|
-spec(is_queue/1 :: (any()) -> boolean()).
|
||||||
|
-spec(is_empty/1 :: (pqueue()) -> boolean()).
|
||||||
|
-spec(len/1 :: (pqueue()) -> non_neg_integer()).
|
||||||
|
-spec(to_list/1 :: (pqueue()) -> [{priority(), any()}]).
|
||||||
|
-spec(in/2 :: (any(), pqueue()) -> pqueue()).
|
||||||
|
-spec(in/3 :: (any(), priority(), pqueue()) -> pqueue()).
|
||||||
|
-spec(out/1 :: (pqueue()) -> {empty | {value, any()}, pqueue()}).
|
||||||
|
-spec(join/2 :: (pqueue(), pqueue()) -> pqueue()).
|
||||||
|
|
||||||
|
-endif.
|
||||||
|
|
||||||
|
%%----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
new() ->
|
||||||
|
{queue, [], []}.
|
||||||
|
|
||||||
|
is_queue({queue, R, F}) when is_list(R), is_list(F) ->
|
||||||
|
true;
|
||||||
|
is_queue({pqueue, Queues}) when is_list(Queues) ->
|
||||||
|
lists:all(fun ({infinity, Q}) -> is_queue(Q);
|
||||||
|
({P, Q}) -> is_integer(P) andalso is_queue(Q)
|
||||||
|
end, Queues);
|
||||||
|
is_queue(_) ->
|
||||||
|
false.
|
||||||
|
|
||||||
|
is_empty({queue, [], []}) ->
|
||||||
|
true;
|
||||||
|
is_empty(_) ->
|
||||||
|
false.
|
||||||
|
|
||||||
|
len({queue, R, F}) when is_list(R), is_list(F) ->
|
||||||
|
length(R) + length(F);
|
||||||
|
len({pqueue, Queues}) ->
|
||||||
|
lists:sum([len(Q) || {_, Q} <- Queues]).
|
||||||
|
|
||||||
|
to_list({queue, In, Out}) when is_list(In), is_list(Out) ->
|
||||||
|
[{0, V} || V <- Out ++ lists:reverse(In, [])];
|
||||||
|
to_list({pqueue, Queues}) ->
|
||||||
|
[{maybe_negate_priority(P), V} || {P, Q} <- Queues,
|
||||||
|
{0, V} <- to_list(Q)].
|
||||||
|
|
||||||
|
in(Item, Q) ->
|
||||||
|
in(Item, 0, Q).
|
||||||
|
|
||||||
|
in(X, 0, {queue, [_] = In, []}) ->
|
||||||
|
{queue, [X], In};
|
||||||
|
in(X, 0, {queue, In, Out}) when is_list(In), is_list(Out) ->
|
||||||
|
{queue, [X|In], Out};
|
||||||
|
in(X, Priority, _Q = {queue, [], []}) ->
|
||||||
|
in(X, Priority, {pqueue, []});
|
||||||
|
in(X, Priority, Q = {queue, _, _}) ->
|
||||||
|
in(X, Priority, {pqueue, [{0, Q}]});
|
||||||
|
in(X, Priority, {pqueue, Queues}) ->
|
||||||
|
P = maybe_negate_priority(Priority),
|
||||||
|
{pqueue, case lists:keysearch(P, 1, Queues) of
|
||||||
|
{value, {_, Q}} ->
|
||||||
|
lists:keyreplace(P, 1, Queues, {P, in(X, Q)});
|
||||||
|
false when P == infinity ->
|
||||||
|
[{P, {queue, [X], []}} | Queues];
|
||||||
|
false ->
|
||||||
|
case Queues of
|
||||||
|
[{infinity, InfQueue} | Queues1] ->
|
||||||
|
[{infinity, InfQueue} |
|
||||||
|
lists:keysort(1, [{P, {queue, [X], []}} | Queues1])];
|
||||||
|
_ ->
|
||||||
|
lists:keysort(1, [{P, {queue, [X], []}} | Queues])
|
||||||
|
end
|
||||||
|
end}.
|
||||||
|
|
||||||
|
out({queue, [], []} = Q) ->
|
||||||
|
{empty, Q};
|
||||||
|
out({queue, [V], []}) ->
|
||||||
|
{{value, V}, {queue, [], []}};
|
||||||
|
out({queue, [Y|In], []}) ->
|
||||||
|
[V|Out] = lists:reverse(In, []),
|
||||||
|
{{value, V}, {queue, [Y], Out}};
|
||||||
|
out({queue, In, [V]}) when is_list(In) ->
|
||||||
|
{{value,V}, r2f(In)};
|
||||||
|
out({queue, In,[V|Out]}) when is_list(In) ->
|
||||||
|
{{value, V}, {queue, In, Out}};
|
||||||
|
out({pqueue, [{P, Q} | Queues]}) ->
|
||||||
|
{R, Q1} = out(Q),
|
||||||
|
NewQ = case is_empty(Q1) of
|
||||||
|
true -> case Queues of
|
||||||
|
[] -> {queue, [], []};
|
||||||
|
[{0, OnlyQ}] -> OnlyQ;
|
||||||
|
[_|_] -> {pqueue, Queues}
|
||||||
|
end;
|
||||||
|
false -> {pqueue, [{P, Q1} | Queues]}
|
||||||
|
end,
|
||||||
|
{R, NewQ}.
|
||||||
|
|
||||||
|
join(A, {queue, [], []}) ->
|
||||||
|
A;
|
||||||
|
join({queue, [], []}, B) ->
|
||||||
|
B;
|
||||||
|
join({queue, AIn, AOut}, {queue, BIn, BOut}) ->
|
||||||
|
{queue, BIn, AOut ++ lists:reverse(AIn, BOut)};
|
||||||
|
join(A = {queue, _, _}, {pqueue, BPQ}) ->
|
||||||
|
{Pre, Post} =
|
||||||
|
lists:splitwith(fun ({P, _}) -> P < 0 orelse P == infinity end, BPQ),
|
||||||
|
Post1 = case Post of
|
||||||
|
[] -> [ {0, A} ];
|
||||||
|
[ {0, ZeroQueue} | Rest ] -> [ {0, join(A, ZeroQueue)} | Rest ];
|
||||||
|
_ -> [ {0, A} | Post ]
|
||||||
|
end,
|
||||||
|
{pqueue, Pre ++ Post1};
|
||||||
|
join({pqueue, APQ}, B = {queue, _, _}) ->
|
||||||
|
{Pre, Post} =
|
||||||
|
lists:splitwith(fun ({P, _}) -> P < 0 orelse P == infinity end, APQ),
|
||||||
|
Post1 = case Post of
|
||||||
|
[] -> [ {0, B} ];
|
||||||
|
[ {0, ZeroQueue} | Rest ] -> [ {0, join(ZeroQueue, B)} | Rest ];
|
||||||
|
_ -> [ {0, B} | Post ]
|
||||||
|
end,
|
||||||
|
{pqueue, Pre ++ Post1};
|
||||||
|
join({pqueue, APQ}, {pqueue, BPQ}) ->
|
||||||
|
{pqueue, merge(APQ, BPQ, [])}.
|
||||||
|
|
||||||
|
merge([], BPQ, Acc) ->
|
||||||
|
lists:reverse(Acc, BPQ);
|
||||||
|
merge(APQ, [], Acc) ->
|
||||||
|
lists:reverse(Acc, APQ);
|
||||||
|
merge([{P, A}|As], [{P, B}|Bs], Acc) ->
|
||||||
|
merge(As, Bs, [ {P, join(A, B)} | Acc ]);
|
||||||
|
merge([{PA, A}|As], Bs = [{PB, _}|_], Acc) when PA < PB orelse PA == infinity ->
|
||||||
|
merge(As, Bs, [ {PA, A} | Acc ]);
|
||||||
|
merge(As = [{_, _}|_], [{PB, B}|Bs], Acc) ->
|
||||||
|
merge(As, Bs, [ {PB, B} | Acc ]).
|
||||||
|
|
||||||
|
r2f([]) -> {queue, [], []};
|
||||||
|
r2f([_] = R) -> {queue, [], R};
|
||||||
|
r2f([X,Y]) -> {queue, [X], [Y]};
|
||||||
|
r2f([X,Y|R]) -> {queue, [X,Y], lists:reverse(R, [])}.
|
||||||
|
|
||||||
|
maybe_negate_priority(infinity) -> infinity;
|
||||||
|
maybe_negate_priority(P) -> -P.
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,89 @@
|
||||||
|
%% 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 VMware, Inc.
|
||||||
|
%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved.
|
||||||
|
%%
|
||||||
|
|
||||||
|
-module(tcp_acceptor).
|
||||||
|
|
||||||
|
-behaviour(gen_server).
|
||||||
|
|
||||||
|
-export([start_link/2]).
|
||||||
|
|
||||||
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||||
|
terminate/2, code_change/3]).
|
||||||
|
|
||||||
|
-record(state, {callback, sock, ref}).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
start_link(Callback, LSock) ->
|
||||||
|
gen_server:start_link(?MODULE, {Callback, LSock}, []).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
init({Callback, LSock}) ->
|
||||||
|
gen_server:cast(self(), accept),
|
||||||
|
{ok, #state{callback=Callback, sock=LSock}}.
|
||||||
|
|
||||||
|
handle_call(_Request, _From, State) ->
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
handle_cast(accept, State) ->
|
||||||
|
ok = file_handle_cache:obtain(),
|
||||||
|
accept(State);
|
||||||
|
|
||||||
|
handle_cast(_Msg, State) ->
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
handle_info({inet_async, LSock, Ref, {ok, Sock}},
|
||||||
|
State = #state{callback={M,F,A}, sock=LSock, ref=Ref}) ->
|
||||||
|
|
||||||
|
%% patch up the socket so it looks like one we got from
|
||||||
|
%% gen_tcp:accept/1
|
||||||
|
{ok, Mod} = inet_db:lookup_socket(LSock),
|
||||||
|
inet_db:register_socket(Sock, Mod),
|
||||||
|
|
||||||
|
%% handle
|
||||||
|
file_handle_cache:transfer(apply(M, F, A ++ [Sock])),
|
||||||
|
ok = file_handle_cache:obtain(),
|
||||||
|
|
||||||
|
%% accept more
|
||||||
|
accept(State);
|
||||||
|
|
||||||
|
handle_info({inet_async, LSock, Ref, {error, closed}},
|
||||||
|
State=#state{sock=LSock, ref=Ref}) ->
|
||||||
|
%% It would be wrong to attempt to restart the acceptor when we
|
||||||
|
%% know this will fail.
|
||||||
|
{stop, normal, State};
|
||||||
|
|
||||||
|
handle_info({inet_async, LSock, Ref, {error, Reason}},
|
||||||
|
State=#state{sock=LSock, ref=Ref}) ->
|
||||||
|
{stop, {accept_failed, Reason}, State};
|
||||||
|
|
||||||
|
handle_info(_Info, State) ->
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
terminate(_Reason, _State) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
|
{ok, State}.
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
accept(State = #state{sock=LSock}) ->
|
||||||
|
case prim_inet:async_accept(LSock, -1) of
|
||||||
|
{ok, Ref} -> {noreply, State#state{ref=Ref}};
|
||||||
|
Error -> {stop, {cannot_accept, Error}, State}
|
||||||
|
end.
|
|
@ -0,0 +1,43 @@
|
||||||
|
%% 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 VMware, Inc.
|
||||||
|
%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved.
|
||||||
|
%%
|
||||||
|
|
||||||
|
-module(tcp_acceptor_sup).
|
||||||
|
|
||||||
|
-behaviour(supervisor).
|
||||||
|
|
||||||
|
-export([start_link/2]).
|
||||||
|
|
||||||
|
-export([init/1]).
|
||||||
|
|
||||||
|
%%----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-ifdef(use_specs).
|
||||||
|
|
||||||
|
-type(mfargs() :: {atom(), atom(), [any()]}).
|
||||||
|
|
||||||
|
-spec(start_link/2 :: (atom(), mfargs()) -> rabbit_types:ok_pid_or_error()).
|
||||||
|
|
||||||
|
-endif.
|
||||||
|
|
||||||
|
%%----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
start_link(Name, Callback) ->
|
||||||
|
supervisor:start_link({local,Name}, ?MODULE, Callback).
|
||||||
|
|
||||||
|
init(Callback) ->
|
||||||
|
{ok, {{simple_one_for_one, 10, 10},
|
||||||
|
[{tcp_acceptor, {tcp_acceptor, start_link, [Callback]},
|
||||||
|
transient, brutal_kill, worker, [tcp_acceptor]}]}}.
|
|
@ -0,0 +1,113 @@
|
||||||
|
%% 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 VMware, Inc.
|
||||||
|
%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved.
|
||||||
|
%%
|
||||||
|
|
||||||
|
-module(tcp_listener).
|
||||||
|
|
||||||
|
-behaviour(gen_server).
|
||||||
|
|
||||||
|
-export([start_link/8]).
|
||||||
|
|
||||||
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||||
|
terminate/2, code_change/3]).
|
||||||
|
|
||||||
|
-record(state, {sock, on_startup, on_shutdown, label}).
|
||||||
|
|
||||||
|
%%----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-ifdef(use_specs).
|
||||||
|
|
||||||
|
-type(mfargs() :: {atom(), atom(), [any()]}).
|
||||||
|
|
||||||
|
-spec(start_link/8 ::
|
||||||
|
(inet:ip_address(), inet:port_number(), [gen_tcp:listen_option()],
|
||||||
|
integer(), atom(), mfargs(), mfargs(), string()) ->
|
||||||
|
rabbit_types:ok_pid_or_error()).
|
||||||
|
|
||||||
|
-endif.
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
start_link(IPAddress, Port, SocketOpts,
|
||||||
|
ConcurrentAcceptorCount, AcceptorSup,
|
||||||
|
OnStartup, OnShutdown, Label) ->
|
||||||
|
gen_server:start_link(
|
||||||
|
?MODULE, {IPAddress, Port, SocketOpts,
|
||||||
|
ConcurrentAcceptorCount, AcceptorSup,
|
||||||
|
OnStartup, OnShutdown, Label}, []).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
init({IPAddress, Port, SocketOpts,
|
||||||
|
ConcurrentAcceptorCount, AcceptorSup,
|
||||||
|
{M,F,A} = OnStartup, OnShutdown, Label}) ->
|
||||||
|
process_flag(trap_exit, true),
|
||||||
|
case gen_tcp:listen(Port, SocketOpts ++ [{ip, IPAddress},
|
||||||
|
{active, false}]) of
|
||||||
|
{ok, LSock} ->
|
||||||
|
lists:foreach(fun (_) ->
|
||||||
|
{ok, _APid} = supervisor:start_child(
|
||||||
|
AcceptorSup, [LSock])
|
||||||
|
end,
|
||||||
|
lists:duplicate(ConcurrentAcceptorCount, dummy)),
|
||||||
|
{ok, {LIPAddress, LPort}} = inet:sockname(LSock),
|
||||||
|
error_logger:info_msg(
|
||||||
|
"started ~s on ~s:~p~n",
|
||||||
|
[Label, ntoab(LIPAddress), LPort]),
|
||||||
|
apply(M, F, A ++ [IPAddress, Port]),
|
||||||
|
{ok, #state{sock = LSock,
|
||||||
|
on_startup = OnStartup, on_shutdown = OnShutdown,
|
||||||
|
label = Label}};
|
||||||
|
{error, Reason} ->
|
||||||
|
error_logger:error_msg(
|
||||||
|
"failed to start ~s on ~s:~p - ~p (~s)~n",
|
||||||
|
[Label, ntoab(IPAddress), Port,
|
||||||
|
Reason, inet:format_error(Reason)]),
|
||||||
|
{stop, {cannot_listen, IPAddress, Port, Reason}}
|
||||||
|
end.
|
||||||
|
|
||||||
|
handle_call(_Request, _From, State) ->
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
handle_cast(_Msg, State) ->
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
handle_info(_Info, State) ->
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
terminate(_Reason, #state{sock=LSock, on_shutdown = {M,F,A}, label=Label}) ->
|
||||||
|
{ok, {IPAddress, Port}} = inet:sockname(LSock),
|
||||||
|
gen_tcp:close(LSock),
|
||||||
|
error_logger:info_msg("stopped ~s on ~s:~p~n",
|
||||||
|
[Label, ntoab(IPAddress), Port]),
|
||||||
|
apply(M, F, A ++ [IPAddress, Port]).
|
||||||
|
|
||||||
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
|
{ok, State}.
|
||||||
|
|
||||||
|
%% Format IPv4-mapped IPv6 addresses as IPv4, since they're what we see
|
||||||
|
%% when IPv6 is enabled but not used (i.e. 99% of the time).
|
||||||
|
ntoa({0,0,0,0,0,16#ffff,AB,CD}) ->
|
||||||
|
inet_parse:ntoa({AB bsr 8, AB rem 256, CD bsr 8, CD rem 256});
|
||||||
|
ntoa(IP) ->
|
||||||
|
inet_parse:ntoa(IP).
|
||||||
|
|
||||||
|
ntoab(IP) ->
|
||||||
|
Str = ntoa(IP),
|
||||||
|
case string:str(Str, ":") of
|
||||||
|
0 -> Str;
|
||||||
|
_ -> "[" ++ Str ++ "]"
|
||||||
|
end.
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
%% 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 VMware, Inc.
|
||||||
|
%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved.
|
||||||
|
%%
|
||||||
|
|
||||||
|
-module(tcp_listener_sup).
|
||||||
|
|
||||||
|
-behaviour(supervisor).
|
||||||
|
|
||||||
|
-export([start_link/7, start_link/8]).
|
||||||
|
|
||||||
|
-export([init/1]).
|
||||||
|
|
||||||
|
%%----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-ifdef(use_specs).
|
||||||
|
|
||||||
|
-type(mfargs() :: {atom(), atom(), [any()]}).
|
||||||
|
|
||||||
|
-spec(start_link/7 ::
|
||||||
|
(inet:ip_address(), inet:port_number(), [gen_tcp:listen_option()],
|
||||||
|
mfargs(), mfargs(), mfargs(), string()) ->
|
||||||
|
rabbit_types:ok_pid_or_error()).
|
||||||
|
-spec(start_link/8 ::
|
||||||
|
(inet:ip_address(), inet:port_number(), [gen_tcp:listen_option()],
|
||||||
|
mfargs(), mfargs(), mfargs(), integer(), string()) ->
|
||||||
|
rabbit_types:ok_pid_or_error()).
|
||||||
|
|
||||||
|
-endif.
|
||||||
|
|
||||||
|
%%----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
start_link(IPAddress, Port, SocketOpts, OnStartup, OnShutdown,
|
||||||
|
AcceptCallback, Label) ->
|
||||||
|
start_link(IPAddress, Port, SocketOpts, OnStartup, OnShutdown,
|
||||||
|
AcceptCallback, 1, Label).
|
||||||
|
|
||||||
|
start_link(IPAddress, Port, SocketOpts, OnStartup, OnShutdown,
|
||||||
|
AcceptCallback, ConcurrentAcceptorCount, Label) ->
|
||||||
|
supervisor:start_link(
|
||||||
|
?MODULE, {IPAddress, Port, SocketOpts, OnStartup, OnShutdown,
|
||||||
|
AcceptCallback, ConcurrentAcceptorCount, Label}).
|
||||||
|
|
||||||
|
init({IPAddress, Port, SocketOpts, OnStartup, OnShutdown,
|
||||||
|
AcceptCallback, ConcurrentAcceptorCount, Label}) ->
|
||||||
|
%% This is gross. The tcp_listener needs to know about the
|
||||||
|
%% tcp_acceptor_sup, and the only way I can think of accomplishing
|
||||||
|
%% that without jumping through hoops is to register the
|
||||||
|
%% tcp_acceptor_sup.
|
||||||
|
Name = tcp_name(tcp_acceptor_sup, IPAddress, Port),
|
||||||
|
{ok, {{one_for_all, 10, 10},
|
||||||
|
[{tcp_acceptor_sup, {tcp_acceptor_sup, start_link,
|
||||||
|
[Name, AcceptCallback]},
|
||||||
|
transient, infinity, supervisor, [tcp_acceptor_sup]},
|
||||||
|
{tcp_listener, {tcp_listener, start_link,
|
||||||
|
[IPAddress, Port, SocketOpts,
|
||||||
|
ConcurrentAcceptorCount, Name,
|
||||||
|
OnStartup, OnShutdown, Label]},
|
||||||
|
transient, 16#ffffffff, worker, [tcp_listener]}]}}.
|
||||||
|
|
||||||
|
tcp_name(Prefix, IPAddress, Port)
|
||||||
|
when is_atom(Prefix) andalso is_number(Port) ->
|
||||||
|
list_to_atom(
|
||||||
|
lists:flatten(
|
||||||
|
io_lib:format(
|
||||||
|
"~w_~s:~w", [Prefix, inet_parse:ntoa(IPAddress), Port]))).
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue