From de4531e8c73cdde3a1332a40e89e55decfe9f856 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Wed, 26 Jun 2024 12:57:41 +0200 Subject: [PATCH] feat(mosq): Initial moquitto v2.0.18 port (TCP only) --- .github/workflows/mosq__build.yml | 31 ++ .gitmodules | 3 + .pre-commit-config.yaml | 2 +- ci/check_copyright_config.yaml | 7 + components/mosquitto/.cz.yaml | 7 + components/mosquitto/CMakeLists.txt | 84 ++++++ components/mosquitto/LICENSE.txt | 2 + components/mosquitto/README.md | 3 + components/mosquitto/edl-v10 | 30 ++ components/mosquitto/epl-v20 | 277 ++++++++++++++++++ .../mosquitto/examples/broker/CMakeLists.txt | 6 + .../mosquitto/examples/broker/README.md | 38 +++ .../examples/broker/main/CMakeLists.txt | 2 + .../examples/broker/main/Kconfig.projbuild | 22 ++ .../mosquitto/examples/broker/main/broker.c | 89 ++++++ .../examples/broker/main/idf_component.yml | 6 + .../mosquitto/examples/broker/sdkconfig.ci | 3 + components/mosquitto/idf_component.yml | 5 + components/mosquitto/mosquitto | 1 + components/mosquitto/port/broker.c | 199 +++++++++++++ components/mosquitto/port/callbacks.c | 48 +++ components/mosquitto/port/config.c | 243 +++++++++++++++ components/mosquitto/port/files.c | 22 ++ components/mosquitto/port/ifaddrs.c | 20 ++ .../mosquitto/port/include/mosq_broker.h | 16 + .../mosquitto/port/priv_include/config.h | 24 ++ .../mosquitto/port/priv_include/dlfcn.h | 11 + .../mosquitto/port/priv_include/ifaddrs.h | 17 ++ components/mosquitto/port/priv_include/poll.h | 8 + .../mosquitto/port/priv_include/sys/syslog.h | 35 +++ components/mosquitto/port/signals.c | 13 + 31 files changed, 1273 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/mosq__build.yml create mode 100644 components/mosquitto/.cz.yaml create mode 100644 components/mosquitto/CMakeLists.txt create mode 100644 components/mosquitto/LICENSE.txt create mode 100644 components/mosquitto/README.md create mode 100644 components/mosquitto/edl-v10 create mode 100644 components/mosquitto/epl-v20 create mode 100644 components/mosquitto/examples/broker/CMakeLists.txt create mode 100644 components/mosquitto/examples/broker/README.md create mode 100644 components/mosquitto/examples/broker/main/CMakeLists.txt create mode 100644 components/mosquitto/examples/broker/main/Kconfig.projbuild create mode 100644 components/mosquitto/examples/broker/main/broker.c create mode 100644 components/mosquitto/examples/broker/main/idf_component.yml create mode 100644 components/mosquitto/examples/broker/sdkconfig.ci create mode 100644 components/mosquitto/idf_component.yml create mode 160000 components/mosquitto/mosquitto create mode 100644 components/mosquitto/port/broker.c create mode 100644 components/mosquitto/port/callbacks.c create mode 100644 components/mosquitto/port/config.c create mode 100644 components/mosquitto/port/files.c create mode 100644 components/mosquitto/port/ifaddrs.c create mode 100644 components/mosquitto/port/include/mosq_broker.h create mode 100644 components/mosquitto/port/priv_include/config.h create mode 100644 components/mosquitto/port/priv_include/dlfcn.h create mode 100644 components/mosquitto/port/priv_include/ifaddrs.h create mode 100644 components/mosquitto/port/priv_include/poll.h create mode 100644 components/mosquitto/port/priv_include/sys/syslog.h create mode 100644 components/mosquitto/port/signals.c diff --git a/.github/workflows/mosq__build.yml b/.github/workflows/mosq__build.yml new file mode 100644 index 000000000..26064ead8 --- /dev/null +++ b/.github/workflows/mosq__build.yml @@ -0,0 +1,31 @@ +name: "mosq: build-tests" + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened, labeled] + +jobs: + build_mosq: + if: contains(github.event.pull_request.labels.*.name, 'Comp::mosq') || github.event_name == 'push' + name: Build + strategy: + matrix: + idf_ver: ["latest", "release-v5.3"] + runs-on: ubuntu-22.04 + container: espressif/idf:${{ matrix.idf_ver }} + steps: + - name: Checkout esp-protocols + uses: actions/checkout@v4 + with: + submodules: recursive + - name: Build with IDF-${{ matrix.idf_ver }} + env: + EXPECTED_WARNING: "\"_GNU_SOURCE\" redefined" + shell: bash + run: | + . ${IDF_PATH}/export.sh + pip install idf-component-manager idf-build-apps --upgrade + python ci/build_apps.py components/mosquitto/examples/ -c diff --git a/.gitmodules b/.gitmodules index 6ecce627b..88f827fee 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "components/asio/asio"] path = components/asio/asio url = https://github.com/espressif/asio +[submodule "components/mosq/mosquitto"] + path = components/mosquitto/mosquitto + url = https://github.com/eclipse/mosquitto diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 19a05a735..8b985ab99 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -62,7 +62,7 @@ repos: hooks: - id: commit message scopes name: "commit message must be scoped with: mdns, modem, websocket, asio, mqtt_cxx, console, common, eppp, wifi_remote, tls_cxx" - entry: '\A(?!(feat|fix|ci|bump|test|docs)\((mdns|modem|common|console|websocket|asio|mqtt_cxx|examples|eppp|wifi_remote|tls_cxx)\)\:)' + entry: '\A(?!(feat|fix|ci|bump|test|docs)\((mdns|modem|common|console|websocket|asio|mqtt_cxx|examples|eppp|wifi_remote|tls_cxx|mosq)\)\:)' language: pygrep args: [--multiline] stages: [commit-msg] diff --git a/ci/check_copyright_config.yaml b/ci/check_copyright_config.yaml index 77f9f8802..43f2ad701 100644 --- a/ci/check_copyright_config.yaml +++ b/ci/check_copyright_config.yaml @@ -47,6 +47,13 @@ asio_component: - Apache-2.0 - BSL-1.0 +mosquitto_component: + include: + - 'components/mosquitto/port/**' + allowed_licenses: + - EPL-2.0 + - Apache-2.0 + slim_modem_examples: include: - 'examples/esp_netif/slip_custom_netif/**' diff --git a/components/mosquitto/.cz.yaml b/components/mosquitto/.cz.yaml new file mode 100644 index 000000000..eb0094849 --- /dev/null +++ b/components/mosquitto/.cz.yaml @@ -0,0 +1,7 @@ +commitizen: + bump_message: 'bump(mosq): $current_version -> $new_version' + pre_bump_hooks: python ../../ci/changelog.py mosq + tag_format: mosq-v$version + version: 2.0.27 + version_files: + - idf_component.yml diff --git a/components/mosquitto/CMakeLists.txt b/components/mosquitto/CMakeLists.txt new file mode 100644 index 000000000..eccfaa913 --- /dev/null +++ b/components/mosquitto/CMakeLists.txt @@ -0,0 +1,84 @@ +set(m_dir mosquitto) +set(m_src_dir ${m_dir}/src) +set(m_incl_dir ${m_dir}/include) +set(m_lib_dir ${m_dir}/lib) +set(m_deps_dir ${m_dir}/deps) + +set(m_srcs + ${m_lib_dir}/memory_mosq.c + ${m_lib_dir}/util_mosq.c + ${m_lib_dir}/net_mosq.c + ${m_lib_dir}/will_mosq.c + ${m_lib_dir}/alias_mosq.c + ${m_lib_dir}/send_mosq.c + ${m_lib_dir}/strings_mosq.c + ${m_lib_dir}/packet_mosq.c + ${m_lib_dir}/packet_datatypes.c + ${m_lib_dir}/property_mosq.c + ${m_lib_dir}/util_topic.c + ${m_lib_dir}/send_publish.c + ${m_lib_dir}/send_disconnect.c + ${m_lib_dir}/handle_pubackcomp.c + ${m_lib_dir}/handle_pubrec.c + ${m_lib_dir}/handle_pubrel.c + ${m_lib_dir}/handle_ping.c + ${m_lib_dir}/time_mosq.c + ${m_lib_dir}/utf8_mosq.c + + ${m_src_dir}/bridge.c + ${m_src_dir}/bridge_topic.c + ${m_src_dir}/conf_includedir.c + ${m_src_dir}/context.c + ${m_src_dir}/control.c + ${m_src_dir}/database.c + ${m_src_dir}/handle_auth.c + ${m_src_dir}/handle_connack.c + ${m_src_dir}/handle_connect.c + ${m_src_dir}/handle_disconnect.c + ${m_src_dir}/handle_publish.c + ${m_src_dir}/handle_subscribe.c + ${m_src_dir}/handle_unsubscribe.c + ${m_src_dir}/keepalive.c + ${m_src_dir}/logging.c + ${m_src_dir}/loop.c + ${m_src_dir}/memory_public.c + ${m_src_dir}/mosquitto.c + ${m_src_dir}/mux.c + ${m_src_dir}/mux_epoll.c + ${m_src_dir}/mux_poll.c + ${m_src_dir}/net.c + ${m_src_dir}/password_mosq.c + ${m_src_dir}/persist_read.c + ${m_src_dir}/persist_read_v234.c + ${m_src_dir}/persist_read_v5.c + ${m_src_dir}/persist_write.c + ${m_src_dir}/persist_write_v5.c + ${m_src_dir}/property_broker.c + ${m_src_dir}/read_handle.c + ${m_src_dir}/retain.c + ${m_src_dir}/security.c + ${m_src_dir}/security_default.c + ${m_src_dir}/send_auth.c + ${m_src_dir}/send_connack.c + ${m_src_dir}/send_suback.c + ${m_src_dir}/send_unsuback.c + ${m_src_dir}/service.c + ${m_src_dir}/session_expiry.c + ${m_src_dir}/signals.c + ${m_src_dir}/subs.c + ${m_src_dir}/sys_tree.c + ${m_src_dir}/topic_tok.c + ${m_src_dir}/websockets.c + ${m_src_dir}/will_delay.c + ${m_src_dir}/xtreport.c) + +idf_component_register(SRCS ${m_srcs} + port/callbacks.c port/config.c port/signals.c port/ifaddrs.c port/broker.c port/files.c + PRIV_INCLUDE_DIRS port/priv_include port/priv_include/sys ${m_dir} ${m_src_dir} + ${m_incl_dir} ${m_lib_dir} ${m_deps_dir} + INCLUDE_DIRS ${m_incl_dir} port/include + PRIV_REQUIRES newlib + ) + +target_compile_definitions(${COMPONENT_LIB} PRIVATE "WITH_BROKER") +target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") diff --git a/components/mosquitto/LICENSE.txt b/components/mosquitto/LICENSE.txt new file mode 100644 index 000000000..8bb1e27bb --- /dev/null +++ b/components/mosquitto/LICENSE.txt @@ -0,0 +1,2 @@ +This project is dual licensed under the Eclipse Public License 2.0 and the +Eclipse Distribution License 1.0 as described in the epl-v20 and edl-v10 files. diff --git a/components/mosquitto/README.md b/components/mosquitto/README.md new file mode 100644 index 000000000..b4e448ab8 --- /dev/null +++ b/components/mosquitto/README.md @@ -0,0 +1,3 @@ +# ESP32 Mosquitto port (mosq) + +This is a simplified port of the mosquitto broker running on ESP32. It support only a single listener and TCP transport only diff --git a/components/mosquitto/edl-v10 b/components/mosquitto/edl-v10 new file mode 100644 index 000000000..52646c2d1 --- /dev/null +++ b/components/mosquitto/edl-v10 @@ -0,0 +1,30 @@ +Eclipse Distribution License - v 1.0 + +Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + Neither the name of the Eclipse Foundation, Inc. nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/components/mosquitto/epl-v20 b/components/mosquitto/epl-v20 new file mode 100644 index 000000000..e48e09634 --- /dev/null +++ b/components/mosquitto/epl-v20 @@ -0,0 +1,277 @@ +Eclipse Public License - v 2.0 + + THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE + PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION + OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + + a) in the case of the initial Contributor, the initial content + Distributed under this Agreement, and + + b) in the case of each subsequent Contributor: + i) changes to the Program, and + ii) additions to the Program; + where such changes and/or additions to the Program originate from + and are Distributed by that particular Contributor. A Contribution + "originates" from a Contributor if it was added to the Program by + such Contributor itself or anyone acting on such Contributor's behalf. + Contributions do not include changes or additions to the Program that + are not Modified Works. + +"Contributor" means any person or entity that Distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which +are necessarily infringed by the use or sale of its Contribution alone +or when combined with the Program. + +"Program" means the Contributions Distributed in accordance with this +Agreement. + +"Recipient" means anyone who receives the Program under this Agreement +or any Secondary License (as applicable), including Contributors. + +"Derivative Works" shall mean any work, whether in Source Code or other +form, that is based on (or derived from) the Program and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. + +"Modified Works" shall mean any work in Source Code or other form that +results from an addition to, deletion from, or modification of the +contents of the Program, including, for purposes of clarity any new file +in Source Code form that contains any contents of the Program. Modified +Works shall not include works that contain only declarations, +interfaces, types, classes, structures, or files of the Program solely +in each case in order to link to, bind by name, or subclass the Program +or Modified Works thereof. + +"Distribute" means the acts of a) distributing or b) making available +in any manner that enables the transfer of a copy. + +"Source Code" means the form of a Program preferred for making +modifications, including but not limited to software source code, +documentation source, and configuration files. + +"Secondary License" means either the GNU General Public License, +Version 2.0, or any later versions of that license, including any +exceptions or additional permissions as identified by the initial +Contributor. + +2. GRANT OF RIGHTS + + a) Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free copyright + license to reproduce, prepare Derivative Works of, publicly display, + publicly perform, Distribute and sublicense the Contribution of such + Contributor, if any, and such Derivative Works. + + b) Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free patent + license under Licensed Patents to make, use, sell, offer to sell, + import and otherwise transfer the Contribution of such Contributor, + if any, in Source Code or other form. This patent license shall + apply to the combination of the Contribution and the Program if, at + the time the Contribution is added by the Contributor, such addition + of the Contribution causes such combination to be covered by the + Licensed Patents. The patent license shall not apply to any other + combinations which include the Contribution. No hardware per se is + licensed hereunder. + + c) Recipient understands that although each Contributor grants the + licenses to its Contributions set forth herein, no assurances are + provided by any Contributor that the Program does not infringe the + patent or other intellectual property rights of any other entity. + Each Contributor disclaims any liability to Recipient for claims + brought by any other entity based on infringement of intellectual + property rights or otherwise. As a condition to exercising the + rights and licenses granted hereunder, each Recipient hereby + assumes sole responsibility to secure any other intellectual + property rights needed, if any. For example, if a third party + patent license is required to allow Recipient to Distribute the + Program, it is Recipient's responsibility to acquire that license + before distributing the Program. + + d) Each Contributor represents that to its knowledge it has + sufficient copyright rights in its Contribution, if any, to grant + the copyright license set forth in this Agreement. + + e) Notwithstanding the terms of any Secondary License, no + Contributor makes additional grants to any Recipient (other than + those set forth in this Agreement) as a result of such Recipient's + receipt of the Program under the terms of a Secondary License + (if permitted under the terms of Section 3). + +3. REQUIREMENTS + +3.1 If a Contributor Distributes the Program in any form, then: + + a) the Program must also be made available as Source Code, in + accordance with section 3.2, and the Contributor must accompany + the Program with a statement that the Source Code for the Program + is available under this Agreement, and informs Recipients how to + obtain it in a reasonable manner on or through a medium customarily + used for software exchange; and + + b) the Contributor may Distribute the Program under a license + different than this Agreement, provided that such license: + i) effectively disclaims on behalf of all other Contributors all + warranties and conditions, express and implied, including + warranties or conditions of title and non-infringement, and + implied warranties or conditions of merchantability and fitness + for a particular purpose; + + ii) effectively excludes on behalf of all other Contributors all + liability for damages, including direct, indirect, special, + incidental and consequential damages, such as lost profits; + + iii) does not attempt to limit or alter the recipients' rights + in the Source Code under section 3.2; and + + iv) requires any subsequent distribution of the Program by any + party to be under a license that satisfies the requirements + of this section 3. + +3.2 When the Program is Distributed as Source Code: + + a) it must be made available under this Agreement, or if the + Program (i) is combined with other material in a separate file or + files made available under a Secondary License, and (ii) the initial + Contributor attached to the Source Code the notice described in + Exhibit A of this Agreement, then the Program may be made available + under the terms of such Secondary Licenses, and + + b) a copy of this Agreement must be included with each copy of + the Program. + +3.3 Contributors may not remove or alter any copyright, patent, +trademark, attribution notices, disclaimers of warranty, or limitations +of liability ("notices") contained within the Program from any copy of +the Program which they Distribute, provided that Contributors may add +their own appropriate notices. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities +with respect to end users, business partners and the like. While this +license is intended to facilitate the commercial use of the Program, +the Contributor who includes the Program in a commercial product +offering should do so in a manner which does not create potential +liability for other Contributors. Therefore, if a Contributor includes +the Program in a commercial product offering, such Contributor +("Commercial Contributor") hereby agrees to defend and indemnify every +other Contributor ("Indemnified Contributor") against any losses, +damages and costs (collectively "Losses") arising from claims, lawsuits +and other legal actions brought by a third party against the Indemnified +Contributor to the extent caused by the acts or omissions of such +Commercial Contributor in connection with its distribution of the Program +in a commercial product offering. The obligations in this section do not +apply to any claims or Losses relating to any actual or alleged +intellectual property infringement. In order to qualify, an Indemnified +Contributor must: a) promptly notify the Commercial Contributor in +writing of such claim, and b) allow the Commercial Contributor to control, +and cooperate with the Commercial Contributor in, the defense and any +related settlement negotiations. The Indemnified Contributor may +participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial +product offering, Product X. That Contributor is then a Commercial +Contributor. If that Commercial Contributor then makes performance +claims, or offers warranties related to Product X, those performance +claims and warranties are such Commercial Contributor's responsibility +alone. Under this section, the Commercial Contributor would have to +defend claims against the other Contributors related to those performance +claims and warranties, and if a court requires any other Contributor to +pay any damages as a result, the Commercial Contributor must pay +those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT +PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS" +BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR +IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF +TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR +PURPOSE. Each Recipient is solely responsible for determining the +appropriateness of using and distributing the Program and assumes all +risks associated with its exercise of rights under this Agreement, +including but not limited to the risks and costs of program errors, +compliance with applicable laws, damage to or loss of data, programs +or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT +PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS +SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST +PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE +EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of +the remainder of the terms of this Agreement, and without further +action by the parties hereto, such provision shall be reformed to the +minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity +(including a cross-claim or counterclaim in a lawsuit) alleging that the +Program itself (excluding combinations of the Program with other software +or hardware) infringes such Recipient's patent(s), then such Recipient's +rights granted under Section 2(b) shall terminate as of the date such +litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it +fails to comply with any of the material terms or conditions of this +Agreement and does not cure such failure in a reasonable period of +time after becoming aware of such noncompliance. If all Recipient's +rights under this Agreement terminate, Recipient agrees to cease use +and distribution of the Program as soon as reasonably practicable. +However, Recipient's obligations under this Agreement and any licenses +granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, +but in order to avoid inconsistency the Agreement is copyrighted and +may only be modified in the following manner. The Agreement Steward +reserves the right to publish new versions (including revisions) of +this Agreement from time to time. No one other than the Agreement +Steward has the right to modify this Agreement. The Eclipse Foundation +is the initial Agreement Steward. The Eclipse Foundation may assign the +responsibility to serve as the Agreement Steward to a suitable separate +entity. Each new version of the Agreement will be given a distinguishing +version number. The Program (including Contributions) may always be +Distributed subject to the version of the Agreement under which it was +received. In addition, after a new version of the Agreement is published, +Contributor may elect to Distribute the Program (including its +Contributions) under the new version. + +Except as expressly stated in Sections 2(a) and 2(b) above, Recipient +receives no rights or licenses to the intellectual property of any +Contributor under this Agreement, whether expressly, by implication, +estoppel or otherwise. All rights in the Program not expressly granted +under this Agreement are reserved. Nothing in this Agreement is intended +to be enforceable by any entity that is not a Contributor or Recipient. +No third-party beneficiary rights are created under this Agreement. + +Exhibit A - Form of Secondary Licenses Notice + +"This Source Code may also be made available under the following +Secondary Licenses when the conditions for such availability set forth +in the Eclipse Public License, v. 2.0 are satisfied: {name license(s), +version(s), and exceptions or additional permissions here}." + + Simply including a copy of this Agreement, including this Exhibit A + is not sufficient to license the Source Code under Secondary Licenses. + + If it is not possible or desirable to put the notice in a particular + file, then You may include the notice in a location (such as a LICENSE + file in a relevant directory) where a recipient would be likely to + look for such a notice. + + You may add additional accurate notices of copyright ownership. diff --git a/components/mosquitto/examples/broker/CMakeLists.txt b/components/mosquitto/examples/broker/CMakeLists.txt new file mode 100644 index 000000000..5efb12ce9 --- /dev/null +++ b/components/mosquitto/examples/broker/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(broker) diff --git a/components/mosquitto/examples/broker/README.md b/components/mosquitto/examples/broker/README.md new file mode 100644 index 000000000..6d8545505 --- /dev/null +++ b/components/mosquitto/examples/broker/README.md @@ -0,0 +1,38 @@ +# MQTT broker example + +## Overview + +This example runs a TCP broker on a specified host and port. + +### How to use this example + +Choose the preferred connection in the project configuration menu, build and run the example. +After it connects via the specified network interface, it starts the mosquitto broker, which by default listens on all IPv4 interfaces ("0.0.0.0"). + +If you enabled also the mqtt client, this example will connect to the local broker, subscribe and publish to a topic. + +You can connect to the ESP32 mosquitto broker using some other client using the ESP32 IPv4 address and the port specified in the project configuration menu. + +### Test version + +This example is also used for testing on loopback interface only, disabling any actual connection, just using the local mqtt client to the loopback interface. + +### Example output + +```console +I (477) main_task: Calling app_main() +0: mosquitto version 0.1.0 starting +0: Using default config. +0: Opening ipv4 listen socket on port 1883. +0: mosquitto version 0.1.0 running +I (1507) test: Other event id:7 +1: New connection from 127.0.0.1:56424 on port 1883. +1: New client connected from 127.0.0.1:56424 as ESP32_d58788 (p2, c1, k120). +I (1528) mqtt_broker: MQTT_EVENT_CONNECTED +I (1528) mqtt_broker: sent subscribe successful, msg_id=54859 +I (1538) mqtt_broker: MQTT_EVENT_SUBSCRIBED, msg_id=54859 +I (1538) mqtt_broker: sent publish successful, msg_id=0 +I (1548) mqtt_broker: MQTT_EVENT_DATA +I (1548) mqtt_broker: TOPIC=/topic/qos0 +I (1548) mqtt_broker: DATA=data +``` diff --git a/components/mosquitto/examples/broker/main/CMakeLists.txt b/components/mosquitto/examples/broker/main/CMakeLists.txt new file mode 100644 index 000000000..b76a5b530 --- /dev/null +++ b/components/mosquitto/examples/broker/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "broker.c" + PRIV_REQUIRES newlib nvs_flash esp_netif esp_event mqtt) diff --git a/components/mosquitto/examples/broker/main/Kconfig.projbuild b/components/mosquitto/examples/broker/main/Kconfig.projbuild new file mode 100644 index 000000000..2e5efc912 --- /dev/null +++ b/components/mosquitto/examples/broker/main/Kconfig.projbuild @@ -0,0 +1,22 @@ +menu "Example Configuration" + + config EXAMPLE_BROKER_HOST + string "Broker host address" + default "0.0.0.0" + help + Host name of the endpoint to bind the mosquitto listener. + + config EXAMPLE_BROKER_PORT + int "Broker port" + default 1883 + help + Port of the endpoint to bind the mosquitto listener + + config EXAMPLE_BROKER_RUN_LOCAL_MQTT_CLIENT + bool "Run a local mqtt client" + default y + help + If enabled, it runs a local mqtt client connecting + to the same endpoint ans the broker listens to + +endmenu diff --git a/components/mosquitto/examples/broker/main/broker.c b/components/mosquitto/examples/broker/main/broker.c new file mode 100644 index 000000000..7b8e293c2 --- /dev/null +++ b/components/mosquitto/examples/broker/main/broker.c @@ -0,0 +1,89 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include +#include "nvs_flash.h" +#include "esp_event.h" +#include "esp_netif.h" +#include "mqtt_client.h" +#include "esp_log.h" +#include "mosq_broker.h" +#include "protocol_examples_common.h" + +const static char *TAG = "mqtt_broker"; + +#if CONFIG_EXAMPLE_BROKER_RUN_LOCAL_MQTT_CLIENT +static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) +{ + ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRIi32 "", base, event_id); + esp_mqtt_event_handle_t event = event_data; + esp_mqtt_client_handle_t client = event->client; + int msg_id; + switch ((esp_mqtt_event_id_t)event_id) { + case MQTT_EVENT_BEFORE_CONNECT: + ESP_LOGI(TAG, "MQTT_EVENT_BEFORE_CONNECT"); + vTaskDelay(pdMS_TO_TICKS(1000)); // wait a second for the broker to start + break; + case MQTT_EVENT_CONNECTED: + ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); + msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0); + ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); + break; + case MQTT_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); + break; + case MQTT_EVENT_SUBSCRIBED: + ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); + msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0); + ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); + break; + case MQTT_EVENT_UNSUBSCRIBED: + ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_PUBLISHED: + ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_DATA: + ESP_LOGI(TAG, "MQTT_EVENT_DATA"); + ESP_LOGI(TAG, "TOPIC=%.*s", event->topic_len, event->topic); + ESP_LOGI(TAG, "DATA=%.*s", event->data_len, event->data); + break; + case MQTT_EVENT_ERROR: + ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); + break; + default: + ESP_LOGI(TAG, "Other event id:%d", event->event_id); + break; + } +} + +static void mqtt_app_start(struct mosq_broker_config *config) +{ + esp_mqtt_client_config_t mqtt_cfg = { + .broker.address.hostname = "127.0.0.1", + .broker.address.transport = MQTT_TRANSPORT_OVER_TCP, // we support only TCP transport now + .broker.address.port = config->port, + }; + esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); + esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL); + esp_mqtt_client_start(client); +} +#endif // CONFIG_EXAMPLE_BROKER_RUN_LOCAL_MQTT_CLIENT + +void app_main(void) +{ + ESP_ERROR_CHECK(nvs_flash_init()); + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + ESP_ERROR_CHECK(example_connect()); + + struct mosq_broker_config config = { .host = CONFIG_EXAMPLE_BROKER_HOST, .port = CONFIG_EXAMPLE_BROKER_PORT }; + +#if CONFIG_EXAMPLE_BROKER_RUN_LOCAL_MQTT_CLIENT + mqtt_app_start(&config); +#endif + // broker continues to run in this task + run_broker(&config); +} diff --git a/components/mosquitto/examples/broker/main/idf_component.yml b/components/mosquitto/examples/broker/main/idf_component.yml new file mode 100644 index 000000000..2ca4aac01 --- /dev/null +++ b/components/mosquitto/examples/broker/main/idf_component.yml @@ -0,0 +1,6 @@ +dependencies: + espressif/mosquitto: + version: "*" + override_path: "../../.." + protocol_examples_common: + path: ${IDF_PATH}/examples/common_components/protocol_examples_common diff --git a/components/mosquitto/examples/broker/sdkconfig.ci b/components/mosquitto/examples/broker/sdkconfig.ci new file mode 100644 index 000000000..efa237ef8 --- /dev/null +++ b/components/mosquitto/examples/broker/sdkconfig.ci @@ -0,0 +1,3 @@ +CONFIG_EXAMPLE_CONNECT_WIFI=n +CONFIG_EXAMPLE_CONNECT_ETHERNET=n +CONFIG_EXAMPLE_BROKER_RUN_LOCAL_MQTT_CLIENT=y diff --git a/components/mosquitto/idf_component.yml b/components/mosquitto/idf_component.yml new file mode 100644 index 000000000..347cc8f32 --- /dev/null +++ b/components/mosquitto/idf_component.yml @@ -0,0 +1,5 @@ +version: "2.0.28~0" +url: https://github.com/espressif/esp-protocols/tree/master/components/mosq +description: The component provides a simple ESP32 port of mosquitto broker +dependencies: + idf: '>=5.1' diff --git a/components/mosquitto/mosquitto b/components/mosquitto/mosquitto new file mode 160000 index 000000000..3923526c6 --- /dev/null +++ b/components/mosquitto/mosquitto @@ -0,0 +1 @@ +Subproject commit 3923526c6b4c048bbecad2506c4c9963bc46cd36 diff --git a/components/mosquitto/port/broker.c b/components/mosquitto/port/broker.c new file mode 100644 index 000000000..294c31b68 --- /dev/null +++ b/components/mosquitto/port/broker.c @@ -0,0 +1,199 @@ +/* + * SPDX-FileCopyrightText: 2024 Roger Light + * + * SPDX-License-Identifier: EPL-2.0 + * + * SPDX-FileContributor: 2024 Espressif Systems (Shanghai) CO LTD + */ +#include "mosquitto.h" +#include "mosquitto_broker_internal.h" +#include "memory_mosq.h" +#include "mosq_broker.h" + +static struct mosquitto__listener_sock *listensock = NULL; +static int listensock_count = 0; +static int listensock_index = 0; +extern int run; + +static int listeners__start_single_mqtt(struct mosquitto__listener *listener) +{ + int i; + struct mosquitto__listener_sock *listensock_new; + + if (net__socket_listen(listener)) { + return 1; + } + listensock_count += listener->sock_count; + listensock_new = mosquitto__realloc(listensock, sizeof(struct mosquitto__listener_sock) * (size_t)listensock_count); + if (!listensock_new) { + return 1; + } + listensock = listensock_new; + + for (i = 0; i < listener->sock_count; i++) { + if (listener->socks[i] == INVALID_SOCKET) { + return 1; + } + listensock[listensock_index].sock = listener->socks[i]; + listensock[listensock_index].listener = listener; +#ifdef WITH_EPOLL + listensock[listensock_index].ident = id_listener; +#endif + listensock_index++; + } + return MOSQ_ERR_SUCCESS; +} + +static int listeners__add_local(const char *host, uint16_t port) +{ + struct mosquitto__listener *listeners; + listeners = mosquitto__realloc(db.config->listeners, sizeof(struct mosquitto__listener)); + if (listeners == NULL) { + return MOSQ_ERR_NOMEM; + } + memset(listeners, 0, sizeof(struct mosquitto__listener)); + db.config->listener_count = 0; + db.config->listeners = listeners; + + listeners = db.config->listeners; + + listener__set_defaults(&listeners[db.config->listener_count]); + listeners[db.config->listener_count].security_options.allow_anonymous = true; + listeners[db.config->listener_count].port = port; + listeners[db.config->listener_count].host = mosquitto__strdup(host); + if (listeners[db.config->listener_count].host == NULL) { + return MOSQ_ERR_NOMEM; + } + if (listeners__start_single_mqtt(&listeners[db.config->listener_count])) { + mosquitto__free(listeners[db.config->listener_count].host); + listeners[db.config->listener_count].host = NULL; + return MOSQ_ERR_UNKNOWN; + } + db.config->listener_count++; + return MOSQ_ERR_SUCCESS; +} + +static void listeners__stop(void) +{ + int i; + + for (i = 0; i < db.config->listener_count; i++) { +#ifdef WITH_WEBSOCKETS + if (db.config->listeners[i].ws_context) { + lws_context_destroy(db.config->listeners[i].ws_context); + } + mosquitto__free(db.config->listeners[i].ws_protocol); +#endif + } + + for (i = 0; i < listensock_count; i++) { + if (listensock[i].sock != INVALID_SOCKET) { + COMPAT_CLOSE(listensock[i].sock); + } + } + mosquitto__free(listensock); +} + +int run_broker(struct mosq_broker_config *broker_config) +{ + + struct mosquitto__config config; +#ifdef WITH_BRIDGE + int i; +#endif + int rc; + struct timeval tv; + struct mosquitto *ctxt, *ctxt_tmp; + + gettimeofday(&tv, NULL); + + memset(&db, 0, sizeof(struct mosquitto_db)); + db.now_s = mosquitto_time(); + db.now_real_s = time(NULL); + + net__broker_init(); + + config__init(&config); + + db.config = &config; + + rc = db__open(&config); + if (rc != MOSQ_ERR_SUCCESS) { + log__printf(NULL, MOSQ_LOG_ERR, "Error: Couldn't open database."); + return rc; + } + + if (log__init(&config)) { + rc = 1; + return rc; + } + log__printf(NULL, MOSQ_LOG_INFO, "mosquitto version %s starting", VERSION); + if (db.config_file) { + log__printf(NULL, MOSQ_LOG_INFO, "Config loaded from %s.", db.config_file); + } else { + log__printf(NULL, MOSQ_LOG_INFO, "Using default config."); + } + + if (listeners__add_local(broker_config->host, broker_config->port)) { + return 1; + } + + rc = mux__init(listensock, listensock_count); + if (rc) { + return rc; + } + +#ifdef WITH_BRIDGE + bridge__start_all(); +#endif + + log__printf(NULL, MOSQ_LOG_INFO, "mosquitto version %s running", VERSION); + + run = 1; + rc = mosquitto_main_loop(listensock, listensock_count); + + log__printf(NULL, MOSQ_LOG_INFO, "mosquitto version %s terminating", VERSION); + + HASH_ITER(hh_id, db.contexts_by_id, ctxt, ctxt_tmp) { + context__send_will(ctxt); + } + will_delay__send_all(); + +#ifdef WITH_PERSISTENCE + persist__backup(true); +#endif + session_expiry__remove_all(); + + listeners__stop(); + + HASH_ITER(hh_id, db.contexts_by_id, ctxt, ctxt_tmp) { +#ifdef WITH_WEBSOCKETS + if (!ctxt->wsi) +#endif + { + context__cleanup(ctxt, true); + } + } + HASH_ITER(hh_sock, db.contexts_by_sock, ctxt, ctxt_tmp) { + context__cleanup(ctxt, true); + } +#ifdef WITH_BRIDGE + for (i = 0; i < db.bridge_count; i++) { + if (db.bridges[i]) { + context__cleanup(db.bridges[i], true); + } + } + mosquitto__free(db.bridges); +#endif + context__free_disused(); + + db__close(); + + mosquitto_security_module_cleanup(); + + log__close(&config); + config__cleanup(db.config); + net__broker_cleanup(); + + return rc; +} diff --git a/components/mosquitto/port/callbacks.c b/components/mosquitto/port/callbacks.c new file mode 100644 index 000000000..cf6e5f9a2 --- /dev/null +++ b/components/mosquitto/port/callbacks.c @@ -0,0 +1,48 @@ +/* + * SPDX-FileCopyrightText: 2024 Roger Light + * + * SPDX-License-Identifier: EPL-2.0 + * + * SPDX-FileContributor: 2024 Espressif Systems (Shanghai) CO LTD + */ +#include "mosquitto_internal.h" +#include "mosquitto_broker.h" +#include "memory_mosq.h" +#include "mqtt_protocol.h" +#include "send_mosq.h" +#include "util_mosq.h" +#include "utlist.h" +#include "lib_load.h" + + +int mosquitto_callback_register( + mosquitto_plugin_id_t *identifier, + int event, + MOSQ_FUNC_generic_callback cb_func, + const void *event_data, + void *userdata) +{ + return MOSQ_ERR_INVAL; +} + +int mosquitto_callback_unregister( + mosquitto_plugin_id_t *identifier, + int event, + MOSQ_FUNC_generic_callback cb_func, + const void *event_data) +{ + return MOSQ_ERR_INVAL; +} + +void plugin__handle_tick(void) +{ +} + +void plugin__handle_disconnect(struct mosquitto *context, int reason) +{ +} + +int plugin__handle_message(struct mosquitto *context, struct mosquitto_msg_store *stored) +{ + return MOSQ_ERR_SUCCESS; +} diff --git a/components/mosquitto/port/config.c b/components/mosquitto/port/config.c new file mode 100644 index 000000000..04ab00b97 --- /dev/null +++ b/components/mosquitto/port/config.c @@ -0,0 +1,243 @@ +/* + * SPDX-FileCopyrightText: 2024 Roger Light + * + * SPDX-License-Identifier: EPL-2.0 + * + * SPDX-FileContributor: 2024 Espressif Systems (Shanghai) CO LTD + */ +#include "mosquitto_internal.h" +#include "mosquitto_broker.h" +#include "memory_mosq.h" +#include "mqtt_protocol.h" +#include "send_mosq.h" +#include "util_mosq.h" +#include "utlist.h" +#include "lib_load.h" +#include "syslog.h" + + +void config__init(struct mosquitto__config *config) +{ + memset(config, 0, sizeof(struct mosquitto__config)); + for (int i = 0; i < config->listener_count; i++) { + mosquitto__free(config->listeners[i].security_options.acl_file); + config->listeners[i].security_options.acl_file = NULL; + + mosquitto__free(config->listeners[i].security_options.password_file); + config->listeners[i].security_options.password_file = NULL; + + mosquitto__free(config->listeners[i].security_options.psk_file); + config->listeners[i].security_options.psk_file = NULL; + + config->listeners[i].security_options.allow_anonymous = -1; + config->listeners[i].security_options.allow_zero_length_clientid = true; + config->listeners[i].security_options.auto_id_prefix = NULL; + config->listeners[i].security_options.auto_id_prefix_len = 0; + } + + config->local_only = true; + config->allow_duplicate_messages = false; + + mosquitto__free(config->security_options.acl_file); + config->security_options.acl_file = NULL; + + config->security_options.allow_anonymous = -1; + config->security_options.allow_zero_length_clientid = true; + config->security_options.auto_id_prefix = NULL; + config->security_options.auto_id_prefix_len = 0; + + mosquitto__free(config->security_options.password_file); + config->security_options.password_file = NULL; + + mosquitto__free(config->security_options.psk_file); + config->security_options.psk_file = NULL; + + config->autosave_interval = 1800; + config->autosave_on_changes = false; + mosquitto__free(config->clientid_prefixes); + config->connection_messages = true; + config->clientid_prefixes = NULL; + config->per_listener_settings = false; + if (config->log_fptr) { + fclose(config->log_fptr); + config->log_fptr = NULL; + } + mosquitto__free(config->log_file); + config->log_file = NULL; + + config->log_facility = LOG_DAEMON; + config->log_dest = MQTT3_LOG_STDERR | MQTT3_LOG_DLT; + if (db.verbose) { + config->log_type = UINT_MAX; + } else { + config->log_type = MOSQ_LOG_ERR | MOSQ_LOG_WARNING | MOSQ_LOG_NOTICE | MOSQ_LOG_INFO; + } + + config->log_timestamp = true; + mosquitto__free(config->log_timestamp_format); + config->log_timestamp_format = NULL; + config->max_keepalive = 0; + config->max_packet_size = 0; + config->max_inflight_messages = 20; + config->max_queued_messages = 1000; + config->max_inflight_bytes = 0; + config->max_queued_bytes = 0; + config->persistence = false; + mosquitto__free(config->persistence_location); + config->persistence_location = NULL; + mosquitto__free(config->persistence_file); + config->persistence_file = NULL; + config->persistent_client_expiration = 0; + config->queue_qos0_messages = false; + config->retain_available = true; + config->set_tcp_nodelay = false; + config->sys_interval = 10; + config->upgrade_outgoing_qos = false; + + config->daemon = false; + memset(&config->default_listener, 0, sizeof(struct mosquitto__listener)); + listener__set_defaults(&config->default_listener); +} + +int config__read(struct mosquitto__config *config, bool reload) +{ + return MOSQ_ERR_SUCCESS; +} + +void config__cleanup(struct mosquitto__config *config) +{ + int i; +#ifdef WITH_BRIDGE + int j; +#endif + + mosquitto__free(config->clientid_prefixes); + mosquitto__free(config->persistence_location); + mosquitto__free(config->persistence_file); + mosquitto__free(config->persistence_filepath); + mosquitto__free(config->security_options.auto_id_prefix); + mosquitto__free(config->security_options.acl_file); + mosquitto__free(config->security_options.password_file); + mosquitto__free(config->security_options.psk_file); + mosquitto__free(config->pid_file); + mosquitto__free(config->user); + mosquitto__free(config->log_timestamp_format); + if (config->listeners) { + for (i = 0; i < config->listener_count; i++) { + mosquitto__free(config->listeners[i].host); + mosquitto__free(config->listeners[i].bind_interface); + mosquitto__free(config->listeners[i].mount_point); + mosquitto__free(config->listeners[i].socks); + mosquitto__free(config->listeners[i].security_options.auto_id_prefix); + mosquitto__free(config->listeners[i].security_options.acl_file); + mosquitto__free(config->listeners[i].security_options.password_file); + mosquitto__free(config->listeners[i].security_options.psk_file); +#ifdef WITH_TLS + mosquitto__free(config->listeners[i].cafile); + mosquitto__free(config->listeners[i].capath); + mosquitto__free(config->listeners[i].certfile); + mosquitto__free(config->listeners[i].keyfile); + mosquitto__free(config->listeners[i].ciphers); + mosquitto__free(config->listeners[i].ciphers_tls13); + mosquitto__free(config->listeners[i].psk_hint); + mosquitto__free(config->listeners[i].crlfile); + mosquitto__free(config->listeners[i].dhparamfile); + mosquitto__free(config->listeners[i].tls_version); + mosquitto__free(config->listeners[i].tls_engine); + mosquitto__free(config->listeners[i].tls_engine_kpass_sha1); +#ifdef WITH_WEBSOCKETS + if (!config->listeners[i].ws_context) /* libwebsockets frees its own SSL_CTX */ +#endif + { + SSL_CTX_free(config->listeners[i].ssl_ctx); + } +#endif +#ifdef WITH_WEBSOCKETS + mosquitto__free(config->listeners[i].http_dir); +#endif + + } + mosquitto__free(config->listeners); + } +#ifdef WITH_BRIDGE + if (config->bridges) { + for (i = 0; i < config->bridge_count; i++) { + mosquitto__free(config->bridges[i].name); + if (config->bridges[i].addresses) { + for (j = 0; j < config->bridges[i].address_count; j++) { + mosquitto__free(config->bridges[i].addresses[j].address); + } + mosquitto__free(config->bridges[i].addresses); + } + mosquitto__free(config->bridges[i].remote_clientid); + mosquitto__free(config->bridges[i].remote_username); + mosquitto__free(config->bridges[i].remote_password); + mosquitto__free(config->bridges[i].local_clientid); + mosquitto__free(config->bridges[i].local_username); + mosquitto__free(config->bridges[i].local_password); + if (config->bridges[i].topics) { + for (j = 0; j < config->bridges[i].topic_count; j++) { + mosquitto__free(config->bridges[i].topics[j].topic); + mosquitto__free(config->bridges[i].topics[j].local_prefix); + mosquitto__free(config->bridges[i].topics[j].remote_prefix); + mosquitto__free(config->bridges[i].topics[j].local_topic); + mosquitto__free(config->bridges[i].topics[j].remote_topic); + } + mosquitto__free(config->bridges[i].topics); + } + mosquitto__free(config->bridges[i].notification_topic); +#ifdef WITH_TLS + mosquitto__free(config->bridges[i].tls_version); + mosquitto__free(config->bridges[i].tls_cafile); + mosquitto__free(config->bridges[i].tls_alpn); +#ifdef FINAL_WITH_TLS_PSK + mosquitto__free(config->bridges[i].tls_psk_identity); + mosquitto__free(config->bridges[i].tls_psk); +#endif +#endif + } + mosquitto__free(config->bridges); + } +#endif + + if (config->log_fptr) { + fclose(config->log_fptr); + config->log_fptr = NULL; + } + if (config->log_file) { + mosquitto__free(config->log_file); + config->log_file = NULL; + } +} + +const char *mosquitto_client_username(const struct mosquitto *client) +{ + return client->id; +} + +char *misc__trimblanks(char *str) +{ + char *endptr; + + if (str == NULL) { + return NULL; + } + + while (isspace((int)str[0])) { + str++; + } + endptr = &str[strlen(str) - 1]; + while (endptr > str && isspace((int)endptr[0])) { + endptr[0] = '\0'; + endptr--; + } + return str; +} + +// Dummy definition of fork() to work around IDF warning: " warning: _fork is not implemented and will always fail" +// fork() is used in mosquitto.c to deamonize the broker, which we do not call. +pid_t fork (void) +{ + abort(); + return 0; +} diff --git a/components/mosquitto/port/files.c b/components/mosquitto/port/files.c new file mode 100644 index 000000000..b71fa0543 --- /dev/null +++ b/components/mosquitto/port/files.c @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2024 Roger Light + * + * SPDX-License-Identifier: EPL-2.0 + * + * SPDX-FileContributor: 2024 Espressif Systems (Shanghai) CO LTD + */ +#include +#include "mosquitto.h" +#include "mosquitto_broker_internal.h" + +// Dummy implementation of file access +// This needs to be implemented if we need to load/store config from files +FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read) +{ + return NULL; +} + +char *fgets_extending(char **buf, int *buflen, FILE *stream) +{ + return NULL; +} diff --git a/components/mosquitto/port/ifaddrs.c b/components/mosquitto/port/ifaddrs.c new file mode 100644 index 000000000..1e746416b --- /dev/null +++ b/components/mosquitto/port/ifaddrs.c @@ -0,0 +1,20 @@ +/* + * SPDX-FileCopyrightText: 2024 Roger Light + * + * SPDX-License-Identifier: EPL-2.0 + * + * SPDX-FileContributor: 2024 Espressif Systems (Shanghai) CO LTD + */ +#include "ifaddrs.h" + +// Dummy implementation of getifaddrs() +// TODO: Implement this if we need to use bind_interface option of listener's config +int getifaddrs(struct ifaddrs **ifap) +{ + return -1; +} + +void freeifaddrs(struct ifaddrs *ifa) +{ + +} diff --git a/components/mosquitto/port/include/mosq_broker.h b/components/mosquitto/port/include/mosq_broker.h new file mode 100644 index 000000000..72804a1e0 --- /dev/null +++ b/components/mosquitto/port/include/mosq_broker.h @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once +#include "mosquitto.h" + +struct mosquitto__config; + +struct mosq_broker_config { + char *host; + int port; +}; + +int run_broker(struct mosq_broker_config *config); diff --git a/components/mosquitto/port/priv_include/config.h b/components/mosquitto/port/priv_include/config.h new file mode 100644 index 000000000..8680e88f7 --- /dev/null +++ b/components/mosquitto/port/priv_include/config.h @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once +#include + +#undef isspace +#define isspace(__c) (__ctype_lookup((int)__c)&_S) + +#include_next "config.h" +#define VERSION "v2.0.18~0" + +#define _SC_OPEN_MAX_OVERRIDE 4 + +// used to override syscall for obtaining max open fds +static inline long sysconf(int arg) +{ + if (arg == _SC_OPEN_MAX_OVERRIDE) { + return 64; + } + return -1; +} diff --git a/components/mosquitto/port/priv_include/dlfcn.h b/components/mosquitto/port/priv_include/dlfcn.h new file mode 100644 index 000000000..f41e2ba6a --- /dev/null +++ b/components/mosquitto/port/priv_include/dlfcn.h @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#define dlopen(a, b) NULL +#define dlclose(A) +#define dlsym(HANDLE, SYM) NULL +#define dlerror() "dlerror not supported" diff --git a/components/mosquitto/port/priv_include/ifaddrs.h b/components/mosquitto/port/priv_include/ifaddrs.h new file mode 100644 index 000000000..604628cba --- /dev/null +++ b/components/mosquitto/port/priv_include/ifaddrs.h @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#define gai_strerror(x) "gai_strerror() not supported" + +struct ifaddrs { + struct ifaddrs *ifa_next; /* Next item in list */ + char *ifa_name; /* Name of interface */ + struct sockaddr *ifa_addr; /* Address of interface */ +}; + +int getifaddrs(struct ifaddrs **ifap); +void freeifaddrs(struct ifaddrs *ifa); diff --git a/components/mosquitto/port/priv_include/poll.h b/components/mosquitto/port/priv_include/poll.h new file mode 100644 index 000000000..1ac76a5b4 --- /dev/null +++ b/components/mosquitto/port/priv_include/poll.h @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include_next "sys/poll.h" diff --git a/components/mosquitto/port/priv_include/sys/syslog.h b/components/mosquitto/port/priv_include/sys/syslog.h new file mode 100644 index 000000000..a609fa623 --- /dev/null +++ b/components/mosquitto/port/priv_include/sys/syslog.h @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + + +#define EAI_SYSTEM 11 /* system error returned in errno */ + +#include "esp_log.h" +#define LOG_EMERG (0) +#define LOG_ALERT (1) +#define LOG_CRIT (2) +#define LOG_ERR (3) +#define LOG_WARNING (4) +#define LOG_NOTICE (5) +#define LOG_INFO (6) +#define LOG_DEBUG (7) +#define LOG_DAEMON (8) + +#define LOG_LOCAL0 (0) +#define LOG_LOCAL1 (1) +#define LOG_LOCAL2 (2) +#define LOG_LOCAL3 (3) +#define LOG_LOCAL4 (4) +#define LOG_LOCAL5 (5) +#define LOG_LOCAL6 (6) +#define LOG_LOCAL7 (7) + +#define syslog(sev, format, ... ) ESP_LOG_LEVEL_LOCAL(sev-2, "mosquitto", format, ##__VA_ARGS__) + +#define gai_strerror(x) "gai_strerror() not supported" +#define openlog(a, b, c) +#define closelog() diff --git a/components/mosquitto/port/signals.c b/components/mosquitto/port/signals.c new file mode 100644 index 000000000..f1559b51a --- /dev/null +++ b/components/mosquitto/port/signals.c @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2024 Roger Light + * + * SPDX-License-Identifier: EPL-2.0 + * + * SPDX-FileContributor: 2024 Espressif Systems (Shanghai) CO LTD + */ +#include "signal.h" + +int sigprocmask (int, const sigset_t *, sigset_t *) +{ + return 0; +}