From 33d2f78d21265cbf89386fe1bce306fb765479da Mon Sep 17 00:00:00 2001 From: Austin Date: Thu, 26 Dec 2024 13:59:26 -0500 Subject: [PATCH 1/5] meshtasticd-docker: simplify, add USB compose (#5662) --- .env.example | 4 ++ .github/workflows/build_docker.yml | 21 --------- Dockerfile | 70 +++++++++++++++--------------- docker-compose.yml | 29 +++++++++---- 4 files changed, 61 insertions(+), 63 deletions(-) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 000000000..72d95970a --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +# Absolute path to the local meshtastic config.yaml file +CONFIG_PATH=/path/to/meshtastic/config.yaml +# USB device to passthrough (`lsusb -t`: look for `ch341`) +USB_DEVICE=/dev/bus/usb/001/037 diff --git a/.github/workflows/build_docker.yml b/.github/workflows/build_docker.yml index bb5a394fd..13817a8cf 100644 --- a/.github/workflows/build_docker.yml +++ b/.github/workflows/build_docker.yml @@ -10,12 +10,6 @@ jobs: build-native: runs-on: ubuntu-latest steps: - - name: Install libs needed for native build - shell: bash - run: | - sudo apt-get update --fix-missing - sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev libusb-1.0-0-dev - - name: Checkout code uses: actions/checkout@v4 with: @@ -23,21 +17,6 @@ jobs: ref: ${{github.event.pull_request.head.ref}} repository: ${{github.event.pull_request.head.repo.full_name}} - - name: Upgrade python tools - shell: bash - run: | - python -m pip install --upgrade pip - pip install -U platformio adafruit-nrfutil - pip install -U meshtastic --pre - - - name: Upgrade platformio - shell: bash - run: | - pio upgrade - - - name: Build Native - run: bin/build-native.sh - - name: Get release version string run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT id: version diff --git a/Dockerfile b/Dockerfile index ca216e04b..f3b294a5b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,32 +1,29 @@ -FROM debian:bookworm-slim AS builder +# trunk-ignore-all(terrascan/AC_DOCKER_0002): Known terrascan issue +# trunk-ignore-all(hadolint/DL3008): Use latest version of apt packages for buildchain +# trunk-ignore-all(trivy/DS002): We must run as root for this container +# trunk-ignore-all(checkov/CKV_DOCKER_8): We must run as root for this container +# trunk-ignore-all(hadolint/DL3002): We must run as root for this container +FROM python:3.12-bookworm AS builder ENV DEBIAN_FRONTEND=noninteractive ENV TZ=Etc/UTC -# http://bugs.python.org/issue19846 -# > At the moment, setting "LANG=C" on a Linux system *fundamentally breaks Python 3*, and that's not OK. -ENV LANG C.UTF-8 - -# Install build deps -USER root - -# trunk-ignore(terrascan/AC_DOCKER_0002): Known terrascan issue -# trunk-ignore(hadolint/DL3008): Use latest version of packages for buildchain -RUN apt-get update && apt-get install --no-install-recommends -y wget python3 python3-pip python3-wheel python3-venv g++ zip git \ - ca-certificates libgpiod-dev libyaml-cpp-dev libbluetooth-dev \ - libusb-1.0-0-dev libulfius-dev liborcania-dev libssl-dev pkg-config && \ - apt-get clean && rm -rf /var/lib/apt/lists/* && mkdir /tmp/firmware - -RUN groupadd -g 1000 mesh && useradd -ml -u 1000 -g 1000 mesh && chown mesh:mesh /tmp/firmware -USER mesh +# Install Dependencies +ENV PIP_ROOT_USER_ACTION=ignore +RUN apt-get update && apt-get install --no-install-recommends -y wget g++ zip git ca-certificates \ + libgpiod-dev libyaml-cpp-dev libbluetooth-dev libi2c-dev \ + libusb-1.0-0-dev libulfius-dev liborcania-dev libssl-dev pkg-config && \ + apt-get clean && rm -rf /var/lib/apt/lists/* && \ + pip install --no-cache-dir -U platformio==6.1.16 && \ + mkdir /tmp/firmware +# Copy source code WORKDIR /tmp/firmware -RUN python3 -m venv /tmp/firmware -RUN bash -o pipefail -c "source bin/activate; pip3 install --no-cache-dir -U platformio==6.1.15" -# trunk-ignore(terrascan/AC_DOCKER_00024): We would actually like these files to be owned by mesh tyvm -COPY --chown=mesh:mesh . /tmp/firmware -RUN bash -o pipefail -c "source ./bin/activate && bash ./bin/build-native.sh" -RUN cp "/tmp/firmware/release/meshtasticd_linux_$(uname -m)" "/tmp/firmware/release/meshtasticd" +COPY . /tmp/firmware + +# Build +RUN bash ./bin/build-native.sh && \ + cp "/tmp/firmware/release/meshtasticd_linux_$(uname -m)" "/tmp/firmware/release/meshtasticd" ##### PRODUCTION BUILD ############# @@ -35,20 +32,25 @@ FROM debian:bookworm-slim ENV DEBIAN_FRONTEND=noninteractive ENV TZ=Etc/UTC -# trunk-ignore(terrascan/AC_DOCKER_0002): Known terrascan issue -# trunk-ignore(hadolint/DL3008): Use latest version of packages for buildchain -RUN apt-get update && apt-get --no-install-recommends -y install libc-bin libc6 libgpiod2 libyaml-cpp0.7 libulfius2.7 libusb-1.0-0-dev liborcania2.3 libssl3 && \ - apt-get clean && rm -rf /var/lib/apt/lists/* +# nosemgrep: dockerfile.security.last-user-is-root.last-user-is-root +USER root -RUN groupadd -g 1000 mesh && useradd -ml -u 1000 -g 1000 mesh -USER mesh +RUN apt-get update && apt-get --no-install-recommends -y install libc-bin libc6 libgpiod2 libyaml-cpp0.7 libi2c0 libulfius2.7 libusb-1.0-0-dev liborcania2.3 libssl3 && \ + apt-get clean && rm -rf /var/lib/apt/lists/* \ + && mkdir -p /var/lib/meshtasticd \ + && mkdir -p /etc/meshtasticd/config.d -WORKDIR /home/mesh -COPY --from=builder /tmp/firmware/release/meshtasticd /home/mesh/ +# Fetch compiled binary from the builder +COPY --from=builder /tmp/firmware/release/meshtasticd /usr/sbin/ +# Copy config templates +COPY ./bin/config.d /etc/meshtasticd/available.d -RUN mkdir data -VOLUME /home/mesh/data +WORKDIR /var/lib/meshtasticd +VOLUME /var/lib/meshtasticd -CMD [ "sh", "-cx", "./meshtasticd -d /home/mesh/data --hwid=${HWID:-$RANDOM}" ] +# Expose Meshtastic TCP API port from the host +EXPOSE 4403 + +CMD [ "sh", "-cx", "meshtasticd -d /var/lib/meshtasticd" ] HEALTHCHECK NONE \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 82f2647e8..4aac318c5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,13 +1,26 @@ -version: "3.7" +# USB-Based Meshtastic container-node! + +# Copy .env.example to .env and set the USB_DEVICE and CONFIG_PATH variables services: meshtastic-node: build: . - deploy: - mode: replicated - replicas: 4 - networks: - - mesh + container_name: meshtasticd -networks: - mesh: + # Pass USB device through to the container + devices: + - "${USB_DEVICE}" + + # Mount local config file and named volume for data persistence + volumes: + - "${CONFIG_PATH}:/etc/meshtasticd/config.yaml:ro" + - meshtastic_data:/var/lib/meshtasticd + + # Forward the container’s port 4403 to the host + ports: + - 4403:4403 + + restart: unless-stopped + +volumes: + meshtastic_data: From b12ac6d564be4558047231d5ad946a908dfbbd7f Mon Sep 17 00:00:00 2001 From: Austin Date: Thu, 26 Dec 2024 14:00:50 -0500 Subject: [PATCH 2/5] meshtasticd-docker: Alpine container (#5659) --- alpine.Dockerfile | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 alpine.Dockerfile diff --git a/alpine.Dockerfile b/alpine.Dockerfile new file mode 100644 index 000000000..115602b3b --- /dev/null +++ b/alpine.Dockerfile @@ -0,0 +1,42 @@ +# trunk-ignore-all(trivy/DS002): We must run as root for this container +# trunk-ignore-all(checkov/CKV_DOCKER_8): We must run as root for this container +# trunk-ignore-all(hadolint/DL3002): We must run as root for this container + +FROM python:3.12-alpine3.21 AS builder + +ENV PIP_ROOT_USER_ACTION=ignore +RUN apk add bash g++ libstdc++-dev linux-headers zip git ca-certificates libgpiod-dev yaml-cpp-dev bluez-dev \ + libusb-dev i2c-tools-dev openssl-dev pkgconf argp-standalone && \ + pip install --no-cache-dir -U platformio==6.1.16 && \ + mkdir /tmp/firmware + +WORKDIR /tmp/firmware +COPY . /tmp/firmware + +# Create small package (no debugging symbols) +# Add `argp` for musl +ENV PLATFORMIO_BUILD_FLAGS="-Os -ffunction-sections -fdata-sections -Wl,--gc-sections -largp" + +RUN bash ./bin/build-native.sh && \ + cp "/tmp/firmware/release/meshtasticd_linux_$(uname -m)" "/tmp/firmware/release/meshtasticd" + +# ##### PRODUCTION BUILD ############# + +FROM alpine:3.21 + +# nosemgrep: dockerfile.security.last-user-is-root.last-user-is-root +USER root + +RUN apk add libstdc++ libgpiod yaml-cpp libusb i2c-tools \ + && mkdir -p /var/lib/meshtasticd \ + && mkdir -p /etc/meshtasticd/config.d +COPY --from=builder /tmp/firmware/release/meshtasticd /usr/sbin/ + +WORKDIR /var/lib/meshtasticd +VOLUME /var/lib/meshtasticd + +EXPOSE 4403 + +CMD [ "sh", "-cx", "meshtasticd --fsdir=/var/lib/meshtasticd" ] + +HEALTHCHECK NONE \ No newline at end of file From b1d25ac7b72045a82c3c1820a4bc878e9c9e4032 Mon Sep 17 00:00:00 2001 From: Tavis Date: Thu, 26 Dec 2024 15:08:31 -0800 Subject: [PATCH 3/5] fix for nrf52 lfs assert boot loop (#5670) * fix for nrf52 lfs assert boot loop * guard format in ifdef FSCom block * add ifndef portduino for format call --- src/FSCommon.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/FSCommon.cpp b/src/FSCommon.cpp index 6cd17dac8..df46c1941 100644 --- a/src/FSCommon.cpp +++ b/src/FSCommon.cpp @@ -55,6 +55,15 @@ extern "C" void lfs_assert(const char *reason) { LOG_ERROR("LFS assert: %s", reason); lfs_assert_failed = true; + +#ifndef ARCH_PORTDUINO +#ifdef FSCom + // CORRUPTED FILESYSTEM. This causes bootloop so + // might as well try formatting now. + LOG_ERROR("Trying FSCom.format()"); + FSCom.format(); +#endif +#endif } /** From cd198fcf3f0f608cfbe2ee34ad08fba7c85a2af1 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Fri, 27 Dec 2024 10:46:21 +1100 Subject: [PATCH 4/5] cherry-pick: device-ui persistency (#5570) * device-ui persistency * review update --------- Co-authored-by: mverch67 --- src/mesh/NodeDB.cpp | 8 ++++++++ src/mesh/NodeDB.h | 1 + src/mesh/PhoneAPI.cpp | 11 +++++++++-- src/mesh/PhoneAPI.h | 1 + src/modules/AdminModule.cpp | 30 ++++++++++++++++++++++++++++-- src/modules/AdminModule.h | 2 ++ 6 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 2af85e4f5..54ea570ff 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -57,6 +57,7 @@ NodeDB *nodeDB = nullptr; EXT_RAM_BSS_ATTR meshtastic_DeviceState devicestate; meshtastic_MyNodeInfo &myNodeInfo = devicestate.my_node; meshtastic_LocalConfig config; +meshtastic_DeviceUIConfig uiconfig{.screen_brightness = 153, .screen_timeout = 30}; meshtastic_LocalModuleConfig moduleConfig; meshtastic_ChannelFile channelFile; @@ -895,6 +896,7 @@ void NodeDB::pickNewNodeNum() static const char *prefFileName = "/prefs/db.proto"; static const char *configFileName = "/prefs/config.proto"; +static const char *uiconfigFileName = "/prefs/uiconfig.proto"; static const char *moduleConfigFileName = "/prefs/module.proto"; static const char *channelFileName = "/prefs/channels.proto"; @@ -1054,6 +1056,12 @@ void NodeDB::loadFromDisk() } } + state = loadProto(uiconfigFileName, meshtastic_DeviceUIConfig_size, sizeof(meshtastic_DeviceUIConfig), + &meshtastic_DeviceUIConfig_msg, &uiconfig); + if (state == LoadFileResult::LOAD_SUCCESS) { + LOG_INFO("Loaded UIConfig\n"); + } + // 2.4.X - configuration migration to update new default intervals if (moduleConfig.version < 23) { LOG_DEBUG("ModuleConfig version %d is stale, upgrading to new default intervals", moduleConfig.version); diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index 7e51a1240..c8c0d3170 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -29,6 +29,7 @@ extern meshtastic_DeviceState devicestate; extern meshtastic_ChannelFile channelFile; extern meshtastic_MyNodeInfo &myNodeInfo; extern meshtastic_LocalConfig config; +extern meshtastic_DeviceUIConfig uiconfig; extern meshtastic_LocalModuleConfig moduleConfig; extern meshtastic_User &owner; extern meshtastic_Position localPosition; diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index f49718c5e..c665c60bb 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -188,7 +188,6 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) case STATE_SEND_NOTHING: LOG_DEBUG("FromRadio=STATE_SEND_NOTHING"); break; - case STATE_SEND_MY_INFO: LOG_DEBUG("FromRadio=STATE_SEND_MY_INFO"); // If the user has specified they don't want our node to share its location, make sure to tell the phone @@ -196,11 +195,18 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) fromRadioScratch.which_payload_variant = meshtastic_FromRadio_my_info_tag; strncpy(myNodeInfo.pio_env, optstr(APP_ENV), sizeof(myNodeInfo.pio_env)); fromRadioScratch.my_info = myNodeInfo; - state = STATE_SEND_OWN_NODEINFO; + state = STATE_SEND_UIDATA; service->refreshLocalMeshNode(); // Update my NodeInfo because the client will be asking for it soon. break; + case STATE_SEND_UIDATA: + LOG_INFO("getFromRadio=STATE_SEND_UIDATA\n"); + fromRadioScratch.which_payload_variant = meshtastic_FromRadio_deviceuiConfig_tag; + fromRadioScratch.deviceuiConfig = uiconfig; + state = STATE_SEND_OWN_NODEINFO; + break; + case STATE_SEND_OWN_NODEINFO: { LOG_DEBUG("Send My NodeInfo"); auto us = nodeDB->readNextMeshNode(readIndex); @@ -518,6 +524,7 @@ bool PhoneAPI::available() case STATE_SEND_NOTHING: return false; case STATE_SEND_MY_INFO: + case STATE_SEND_UIDATA: case STATE_SEND_CHANNELS: case STATE_SEND_CONFIG: case STATE_SEND_MODULECONFIG: diff --git a/src/mesh/PhoneAPI.h b/src/mesh/PhoneAPI.h index 3247fee5c..31538a0ab 100644 --- a/src/mesh/PhoneAPI.h +++ b/src/mesh/PhoneAPI.h @@ -34,6 +34,7 @@ class PhoneAPI { enum State { STATE_SEND_NOTHING, // Initial state, don't send anything until the client starts asking for config + STATE_SEND_UIDATA, // send stored data for device-ui STATE_SEND_MY_INFO, // send our my info record STATE_SEND_OWN_NODEINFO, STATE_SEND_METADATA, diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 69b2c0a38..7f737a205 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -175,6 +175,12 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta LOG_INFO("Client set ham mode"); handleSetHamMode(r->set_ham_mode); break; + case meshtastic_AdminMessage_get_ui_config_request_tag: { + LOG_INFO("Client is getting device-ui config\n"); + handleGetDeviceUIConfig(mp); + handled = true; + break; + } /** * Other @@ -234,6 +240,11 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta reboot(DEFAULT_REBOOT_SECONDS); break; } + case meshtastic_AdminMessage_store_ui_config_tag: { + LOG_INFO("Storing device-ui config\n"); + handleStoreDeviceUIConfig(r->store_ui_config); + break; + } case meshtastic_AdminMessage_begin_edit_settings_tag: { LOG_INFO("Begin transaction for editing settings"); hasOpenEditTransaction = true; @@ -995,6 +1006,14 @@ void AdminModule::handleGetChannel(const meshtastic_MeshPacket &req, uint32_t ch } } +void AdminModule::handleGetDeviceUIConfig(const meshtastic_MeshPacket &req) +{ + meshtastic_AdminMessage r = meshtastic_AdminMessage_init_default; + r.which_payload_variant = meshtastic_AdminMessage_get_ui_config_response_tag; + r.store_ui_config = uiconfig; + myReply = allocDataProtobuf(r); +} + void AdminModule::reboot(int32_t seconds) { LOG_INFO("Reboot in %d seconds", seconds); @@ -1015,6 +1034,11 @@ void AdminModule::saveChanges(int saveWhat, bool shouldReboot) } } +void AdminModule::handleStoreDeviceUIConfig(const meshtastic_DeviceUIConfig &uicfg) +{ + nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uicfg); +} + void AdminModule::handleSetHamMode(const meshtastic_HamParameters &p) { // Set call sign and override lora limitations for licensed use @@ -1081,7 +1105,8 @@ bool AdminModule::messageIsResponse(const meshtastic_AdminMessage *r) r->which_payload_variant == meshtastic_AdminMessage_get_ringtone_response_tag || r->which_payload_variant == meshtastic_AdminMessage_get_device_connection_status_response_tag || r->which_payload_variant == meshtastic_AdminMessage_get_node_remote_hardware_pins_response_tag || - r->which_payload_variant == meshtastic_NodeRemoteHardwarePinsResponse_node_remote_hardware_pins_tag) + r->which_payload_variant == meshtastic_NodeRemoteHardwarePinsResponse_node_remote_hardware_pins_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_ui_config_response_tag) return true; else return false; @@ -1097,7 +1122,8 @@ bool AdminModule::messageIsRequest(const meshtastic_AdminMessage *r) r->which_payload_variant == meshtastic_AdminMessage_get_device_metadata_request_tag || r->which_payload_variant == meshtastic_AdminMessage_get_ringtone_request_tag || r->which_payload_variant == meshtastic_AdminMessage_get_device_connection_status_request_tag || - r->which_payload_variant == meshtastic_AdminMessage_get_node_remote_hardware_pins_request_tag) + r->which_payload_variant == meshtastic_AdminMessage_get_node_remote_hardware_pins_request_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_ui_config_request_tag) return true; else return false; diff --git a/src/modules/AdminModule.h b/src/modules/AdminModule.h index b99e86707..ee2ebfd96 100644 --- a/src/modules/AdminModule.h +++ b/src/modules/AdminModule.h @@ -43,6 +43,7 @@ class AdminModule : public ProtobufModule, public Obser void handleGetDeviceMetadata(const meshtastic_MeshPacket &req); void handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &req); void handleGetNodeRemoteHardwarePins(const meshtastic_MeshPacket &req); + void handleGetDeviceUIConfig(const meshtastic_MeshPacket &req); /** * Setters */ @@ -52,6 +53,7 @@ class AdminModule : public ProtobufModule, public Obser void handleSetModuleConfig(const meshtastic_ModuleConfig &c); void handleSetChannel(); void handleSetHamMode(const meshtastic_HamParameters &req); + void handleStoreDeviceUIConfig(const meshtastic_DeviceUIConfig &uicfg); void reboot(int32_t seconds); void setPassKey(meshtastic_AdminMessage *res); From 8f8e304216c3b1af259066805f3b4337ca213d68 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Thu, 26 Dec 2024 18:58:26 -0600 Subject: [PATCH 5/5] Add packet length to printPacket() (#5672) --- src/mesh/RadioInterface.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 5161ac41f..5a18ab0c0 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -313,6 +313,7 @@ void printPacket(const char *prefix, const meshtastic_MeshPacket *p) out += DEBUG_PORT.mt_sprintf(" failId=%08x", s.ackVariant.fail_id); */ } else { out += " encrypted"; + out += DEBUG_PORT.mt_sprintf(" len=%d", p->encrypted.size + sizeof(PacketHeader)); } if (p->rx_time != 0) @@ -622,4 +623,4 @@ size_t RadioInterface::beginSending(meshtastic_MeshPacket *p) sendingPacket = p; return p->encrypted.size + sizeof(PacketHeader); -} \ No newline at end of file +}