├── .clang-format ├── .devcontainer ├── Dockerfile ├── devcontainer.json └── reinstall-cmake.sh ├── .github ├── actions │ ├── build-container-image │ │ └── action.yaml │ └── build-native-binary │ │ └── action.yaml └── workflows │ ├── main.yaml │ ├── main_developer.yaml │ └── ocaas.yaml ├── .gitignore ├── .gitmodules ├── .ort.yml ├── .vscode ├── c_cpp_properties.json ├── launch.json └── settings.json ├── 3rdparty ├── CMakeLists.txt └── meson-cross-file-aarch64.txt ├── CMakeLists.txt ├── Dockerfile.amd64 ├── Dockerfile.arm64 ├── LICENSE ├── README.md ├── clang-format.sh ├── cmake ├── dependencies.cmake └── linux │ ├── amd64 │ └── toolchain.cmake │ └── arm64 │ └── toolchain.cmake ├── docs ├── bfb.md ├── building │ └── README.md ├── data-privacy-notice.md ├── deploying │ ├── README.md │ └── sua-service.yaml ├── migration_guide.md └── testing │ ├── README.md │ ├── fileserver │ ├── Dockerfile │ └── bundle │ │ └── bundle │ └── mqtt │ ├── command-activate.json │ ├── command-cleanup.json │ ├── command-download.json │ ├── command-rollback.json │ ├── command-update.json │ ├── current-state-get.json │ ├── mosquitto.conf │ └── start.json ├── project.spdx.yml ├── scripts ├── build.sh ├── build_glib_amd64.sh ├── build_glib_arm64.sh ├── build_openssl_amd64.sh └── build_openssl_arm64.sh ├── src ├── CMakeLists.txt ├── Context.h ├── Defaults.cpp ├── Defaults.h ├── Download │ ├── Downloader.cpp │ ├── Downloader.h │ └── IDownloader.h ├── FSM │ ├── FSM.cpp │ ├── FSM.h │ ├── State.cpp │ ├── State.h │ ├── StateFactory.cpp │ ├── StateFactory.h │ └── States │ │ ├── Activating.cpp │ │ ├── Activating.h │ │ ├── Cleaning.cpp │ │ ├── Cleaning.h │ │ ├── Connected.cpp │ │ ├── Connected.h │ │ ├── Downloading.cpp │ │ ├── Downloading.h │ │ ├── Failed.cpp │ │ ├── Failed.h │ │ ├── Idle.cpp │ │ ├── Idle.h │ │ ├── Installed.cpp │ │ ├── Installed.h │ │ ├── Installing.cpp │ │ ├── Installing.h │ │ ├── SendCurrentState.cpp │ │ ├── SendCurrentState.h │ │ ├── Uninitialized.cpp │ │ └── Uninitialized.h ├── FotaEvent.cpp ├── FotaEvent.h ├── Install │ ├── DBusRaucInstaller.cpp │ ├── DBusRaucInstaller.h │ ├── DummyRaucInstaller.cpp │ ├── DummyRaucInstaller.h │ ├── IRaucInstaller.h │ ├── Installer.cpp │ └── Installer.h ├── Logger.cpp ├── Logger.h ├── Mqtt │ ├── IMqttMessagingProtocol.h │ ├── IMqttProcessor.h │ ├── MqttConfiguration.h │ ├── MqttListener.h │ ├── MqttMessage.cpp │ ├── MqttMessage.h │ ├── MqttMessagingProtocolJSON.cpp │ ├── MqttMessagingProtocolJSON.h │ ├── MqttProcessor.cpp │ └── MqttProcessor.h ├── Patterns │ ├── Dispatcher.cpp │ └── Dispatcher.h ├── SelfUpdateAgent.cpp ├── SelfUpdateAgent.h ├── TechCodes.h ├── Utils │ ├── BundleChecker.cpp │ ├── BundleChecker.h │ ├── IBundleChecker.h │ ├── JsonUtils.cpp │ ├── JsonUtils.h │ ├── ServerAddressParser.cpp │ └── ServerAddressParser.h ├── main.cpp ├── version.cpp.in └── version.h └── utest ├── CMakeLists.txt ├── MockBundleChecker.h ├── MockDownloader.h ├── MockFSM.h ├── MockMqttMessagingProtocol.h ├── MockMqttProcessor.h ├── TestDispatcher.cpp ├── TestDownloader.cpp ├── TestFSM.cpp ├── TestLogger.cpp ├── TestMqttMessagingProtocolJSON.cpp ├── TestSelfUpdateScenarios.cpp ├── TestServerAddressParser.cpp ├── TestStateFactory.cpp ├── Utils.cpp ├── Utils.h ├── sua-apache2.conf └── sua-certificate.config /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: WebKit 2 | TabWidth: 4 3 | IndentWidth: 4 4 | UseTab: Never 5 | ColumnLimit: 100 6 | 7 | --- 8 | Language: Cpp 9 | 10 | DisableFormat: false 11 | Standard: Cpp11 12 | 13 | AccessModifierOffset: -4 14 | AlignAfterOpenBracket: true 15 | AlignConsecutiveAssignments: true 16 | AlignConsecutiveDeclarations: true 17 | AlignEscapedNewlinesLeft: false 18 | AlignOperands: true 19 | AlignTrailingComments: false 20 | AllowAllParametersOfDeclarationOnNextLine: true 21 | AllowShortBlocksOnASingleLine: false 22 | AllowShortCaseLabelsOnASingleLine: false 23 | AllowShortFunctionsOnASingleLine: Empty 24 | AllowShortIfStatementsOnASingleLine: false 25 | AllowShortLoopsOnASingleLine: false 26 | AlwaysBreakAfterDefinitionReturnType: false 27 | AlwaysBreakAfterReturnType: None 28 | AlwaysBreakBeforeMultilineStrings: false 29 | AlwaysBreakTemplateDeclarations: true 30 | BinPackArguments: false 31 | BinPackParameters: false 32 | 33 | BreakBeforeBraces: Custom 34 | BraceWrapping: { 35 | AfterClass: 'false' 36 | AfterControlStatement: 'false' 37 | AfterEnum: 'false' 38 | AfterFunction: 'true' 39 | AfterNamespace: 'false' 40 | AfterStruct: 'false' 41 | AfterUnion: 'false' 42 | BeforeCatch: 'false' 43 | BeforeElse: 'false' 44 | IndentBraces: 'false' 45 | AfterExternBlock: 'false' 46 | SplitEmptyFunction: 'false' 47 | SplitEmptyRecord: 'false' 48 | SplitEmptyNamespace: 'true' 49 | } 50 | 51 | BreakAfterJavaFieldAnnotations: true 52 | BreakBeforeInheritanceComma: true 53 | BreakBeforeBinaryOperators: None 54 | BreakBeforeTernaryOperators: true 55 | BreakConstructorInitializersBeforeComma: true 56 | BreakInheritanceList: BeforeColon 57 | BreakStringLiterals: true 58 | 59 | CommentPragmas: '^ IWYU pragma:' 60 | CompactNamespaces: false 61 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 62 | ConstructorInitializerIndentWidth: 4 63 | ContinuationIndentWidth: 4 64 | Cpp11BracedListStyle: true 65 | SpaceBeforeCpp11BracedList: false 66 | DerivePointerAlignment: false 67 | ExperimentalAutoDetectBinPacking: false 68 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 69 | IndentCaseLabels: false 70 | FixNamespaceComments: true 71 | IndentWrappedFunctionNames: false 72 | KeepEmptyLinesAtTheStartOfBlocks: true 73 | MacroBlockBegin: '' 74 | MacroBlockEnd: '' 75 | JavaScriptQuotes: Double 76 | MaxEmptyLinesToKeep: 1 77 | NamespaceIndentation: All 78 | ObjCBlockIndentWidth: 4 79 | ObjCSpaceAfterProperty: true 80 | ObjCSpaceBeforeProtocolList: true 81 | PenaltyBreakBeforeFirstCallParameter: 19 82 | PenaltyBreakComment: 300 83 | PenaltyBreakFirstLessLess: 120 84 | PenaltyBreakString: 1000 85 | 86 | PenaltyExcessCharacter: 1000000 87 | PenaltyReturnTypeOnItsOwnLine: 60 88 | PointerAlignment: Left 89 | SpaceAfterCStyleCast: false 90 | SpaceBeforeAssignmentOperators: true 91 | SpaceBeforeParens: Never 92 | SpaceInEmptyParentheses: false 93 | SpacesBeforeTrailingComments: 1 94 | SpacesInAngles: false 95 | SpacesInContainerLiterals: true 96 | SpacesInCStyleCastParentheses: false 97 | SpacesInParentheses: false 98 | SpacesInSquareBrackets: false 99 | SpaceAfterTemplateKeyword: true 100 | SpaceBeforeInheritanceColon: true 101 | 102 | SortUsingDeclarations: true 103 | SortIncludes: true 104 | 105 | ReflowComments: false 106 | 107 | UseCRLF: false 108 | 109 | IncludeBlocks: Preserve 110 | IndentPPDirectives: AfterHash 111 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.195.0/containers/cpp/.devcontainer/base.Dockerfile 2 | # [Choice] Debian / Ubuntu version (use Debian 11/9, Ubuntu 18.04/21.04 on local arm64/Apple Silicon): debian-11, debian-10, debian-9, ubuntu-21.04, ubuntu-20.04, ubuntu-18.04 3 | ARG VARIANT="bullseye" 4 | FROM mcr.microsoft.com/vscode/devcontainers/cpp:0-${VARIANT} 5 | 6 | # ENV http_proxy "http://172.17.0.1:3128" 7 | # ENV https_proxy "http://172.17.0.1:3128" 8 | 9 | # Required packages. 10 | RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 11 | && apt-get -y install python3-pip python3-setuptools python3-wheel ninja-build \ 12 | libblkid-dev libselinux-dev gcc-9-aarch64-linux-gnu g++-9-aarch64-linux-gnu \ 13 | mosquitto mosquitto-clients \ 14 | && pip3 install meson 15 | 16 | # Optional packages. 17 | RUN apt-get -y install vim file iputils-ping 18 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.195.0/containers/cpp 3 | { 4 | "name": "SDV Self Update DevContainer", 5 | "build": { 6 | "dockerfile": "Dockerfile", 7 | // Update 'VARIANT' to pick an Debian / Ubuntu OS version: debian-11, debian-10, debian-9, ubuntu-21.04, ubuntu-20.04, ubuntu-18.04 8 | // Use Debian 11, Debian 9, Ubuntu 18.04 or Ubuntu 21.04 on local arm64/Apple Silicon 9 | "args": { "VARIANT": "bullseye" } 10 | }, 11 | "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"], 12 | 13 | // Configure tool-specific properties. 14 | "customizations": { 15 | // Configure properties specific to VS Code. 16 | "vscode": { 17 | // Set *default* container specific settings.json values on container create. 18 | "settings": {}, 19 | 20 | // Add the IDs of extensions you want installed when the container is created. 21 | "extensions": [ 22 | "ms-vscode.cpptools", 23 | "ms-vscode.cmake-tools", 24 | "GitHub.copilot", 25 | "bierner.markdown-mermaid" 26 | ] 27 | } 28 | }, 29 | 30 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 31 | // "forwardPorts": [], 32 | 33 | // Use 'postCreateCommand' to run commands after the container is created. 34 | // "postCreateCommand": "gcc -v", 35 | 36 | // Comment out this line to run as root instead. 37 | "remoteUser": "vscode", 38 | "features": { 39 | "docker-in-docker": "latest" 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /.devcontainer/reinstall-cmake.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | #------------------------------------------------------------------------------------------------------------- 3 | # Copyright (c) Microsoft Corporation. All rights reserved. 4 | # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. 5 | #------------------------------------------------------------------------------------------------------------- 6 | # 7 | set -e 8 | 9 | CMAKE_VERSION=${1:-"none"} 10 | 11 | if [ "${CMAKE_VERSION}" = "none" ]; then 12 | echo "No CMake version specified, skipping CMake reinstallation" 13 | exit 0 14 | fi 15 | 16 | # Cleanup temporary directory and associated files when exiting the script. 17 | cleanup() { 18 | EXIT_CODE=$? 19 | set +e 20 | if [[ -n "${TMP_DIR}" ]]; then 21 | echo "Executing cleanup of tmp files" 22 | rm -Rf "${TMP_DIR}" 23 | fi 24 | exit $EXIT_CODE 25 | } 26 | trap cleanup EXIT 27 | 28 | 29 | echo "Installing CMake..." 30 | apt-get -y purge --auto-remove cmake 31 | mkdir -p /opt/cmake 32 | 33 | architecture=$(dpkg --print-architecture) 34 | case "${architecture}" in 35 | arm64) 36 | ARCH=aarch64 ;; 37 | amd64) 38 | ARCH=x86_64 ;; 39 | *) 40 | echo "Unsupported architecture ${architecture}." 41 | exit 1 42 | ;; 43 | esac 44 | 45 | CMAKE_BINARY_NAME="cmake-${CMAKE_VERSION}-linux-${ARCH}.sh" 46 | CMAKE_CHECKSUM_NAME="cmake-${CMAKE_VERSION}-SHA-256.txt" 47 | TMP_DIR=$(mktemp -d -t cmake-XXXXXXXXXX) 48 | 49 | echo "${TMP_DIR}" 50 | cd "${TMP_DIR}" 51 | 52 | curl -sSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_BINARY_NAME}" -O 53 | curl -sSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_CHECKSUM_NAME}" -O 54 | 55 | sha256sum -c --ignore-missing "${CMAKE_CHECKSUM_NAME}" 56 | sh "${TMP_DIR}/${CMAKE_BINARY_NAME}" --prefix=/opt/cmake --skip-license 57 | 58 | ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake 59 | -------------------------------------------------------------------------------- /.github/actions/build-container-image/action.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Contributors to the Eclipse Foundation 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | # SPDX-License-Identifier: Apache-2.0 16 | 17 | name: Build and push container image 18 | 19 | inputs: 20 | arch: 21 | required: true 22 | description: "Architecture amd64/arm64" 23 | run_number: 24 | required: true 25 | description: "Workflow run number" 26 | commit_hash: 27 | required: true 28 | description: "Git commit hash" 29 | username: 30 | required: true 31 | description: "User name" 32 | token: 33 | required: true 34 | description: "Auth token" 35 | suffix: 36 | required: true 37 | description: "Optional suffix for -developer build of container image" 38 | 39 | runs: 40 | using: "composite" 41 | steps: 42 | - id: repo_name 43 | uses: ASzc/change-string-case-action@v5 44 | with: 45 | string: ${{ github.repository }} 46 | 47 | - name: Checkout repository 48 | uses: actions/checkout@v3 49 | with: 50 | submodules: recursive 51 | 52 | # Workaround: https://github.com/docker/build-push-action/issues/461 53 | - name: Setup Docker buildx 54 | uses: docker/setup-buildx-action@v2 55 | 56 | # Login against a Docker registry except on PR 57 | - name: Log into registry ghcr.io 58 | uses: docker/login-action@v2 59 | with: 60 | registry: ghcr.io 61 | username: ${{ inputs.username }} 62 | password: ${{ inputs.token }} 63 | 64 | # https://github.com/docker/build-push-action 65 | - name: Build and push docker image to ghcr.io 66 | id: build-and-push 67 | uses: docker/build-push-action@v4 68 | with: 69 | push: ${{ (github.event_name == 'push') || (inputs.suffix == '-developer') }} 70 | context: . 71 | platforms: linux/${{ inputs.arch }} 72 | file: Dockerfile.${{ inputs.arch }} 73 | labels: ${{ steps.meta.outputs.labels }} 74 | build-args: | 75 | GITHUB_RUN_NUMBER=${{ inputs.run_number }} 76 | GITHUB_COMMIT_HASH=${{ inputs.commit_hash }} 77 | tags: | 78 | ghcr.io/${{ steps.repo_name.outputs.lowercase }}/self-update-agent${{ inputs.suffix }}:latest_${{ inputs.arch }} 79 | ghcr.io/${{ steps.repo_name.outputs.lowercase }}/self-update-agent${{ inputs.suffix }}:build-${{ inputs.run_number }}_${{ inputs.arch }} 80 | -------------------------------------------------------------------------------- /.github/workflows/main.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Contributors to the Eclipse Foundation 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | # SPDX-License-Identifier: Apache-2.0 16 | 17 | name: Build All 18 | 19 | on: 20 | push: 21 | branches: [ main ] 22 | pull_request: 23 | branches: [ main ] 24 | 25 | jobs: 26 | build-container-image: 27 | runs-on: ubuntu-latest 28 | permissions: 29 | contents: read 30 | packages: write 31 | id-token: write 32 | pull-requests: write 33 | strategy: 34 | matrix: 35 | arch: [ amd64, arm64 ] 36 | steps: 37 | - uses: actions/checkout@v3 38 | with: 39 | submodules: recursive 40 | 41 | - name: Build Docker image 42 | uses: ./.github/actions/build-container-image 43 | with: 44 | arch: ${{ matrix.arch }} 45 | run_number: ${{ github.run_number }} 46 | commit_hash: ${{ github.sha }} 47 | username: ${{ github.actor }} 48 | token: ${{ secrets.GITHUB_TOKEN }} 49 | suffix: "" 50 | 51 | push-container-image: 52 | if: github.event_name == 'push' 53 | runs-on: ubuntu-latest 54 | needs: build-container-image 55 | steps: 56 | - uses: actions/checkout@v3 57 | with: 58 | submodules: recursive 59 | 60 | - id: repo_name 61 | uses: ASzc/change-string-case-action@v5 62 | with: 63 | string: ${{ github.repository }} 64 | 65 | - name: Log into registry ghcr.io 66 | uses: docker/login-action@v2 67 | with: 68 | registry: ghcr.io 69 | username: ${{ github.actor }} 70 | password: ${{ secrets.GITHUB_TOKEN }} 71 | 72 | - name: Create merged manifest and push 73 | env: 74 | REPO: ghcr.io/${{ steps.repo_name.outputs.lowercase }} 75 | run: | 76 | docker buildx imagetools create -t ghcr.io/${{ steps.repo_name.outputs.lowercase }}/self-update-agent:build-${{ github.run_number }} ghcr.io/${{ steps.repo_name.outputs.lowercase }}/self-update-agent:build-${{ github.run_number }}_arm64 ghcr.io/${{ steps.repo_name.outputs.lowercase }}/self-update-agent:build-${{ github.run_number }}_amd64 77 | docker buildx imagetools create -t ghcr.io/${{ steps.repo_name.outputs.lowercase }}/self-update-agent:latest ghcr.io/${{ steps.repo_name.outputs.lowercase }}/self-update-agent:latest_arm64 ghcr.io/${{ steps.repo_name.outputs.lowercase }}/self-update-agent:latest_amd64 78 | 79 | build-native-binary: 80 | runs-on: ubuntu-latest 81 | strategy: 82 | matrix: 83 | arch: [ amd64, arm64 ] 84 | steps: 85 | - uses: actions/checkout@v3 86 | with: 87 | submodules: recursive 88 | 89 | - name: Build native binary 90 | id: build-native-binary 91 | uses: ./.github/actions/build-native-binary 92 | with: 93 | arch: ${{ matrix.arch }} 94 | run_number: ${{ github.run_number }} 95 | 96 | - name: Upload ${{ matrix.arch }} artifacts to workspace 97 | uses: actions/upload-artifact@v3 98 | with: 99 | path: self-update-agent-${{ matrix.arch }}-build-${{ github.run_number }}.tar.gz 100 | 101 | - name: Upload code-coverage results to workspace 102 | if: ${{ matrix.arch == 'amd64' }} 103 | uses: actions/upload-artifact@v3 104 | with: 105 | path: code-coverage.tar.gz 106 | 107 | - name: Upload unit tests results to workspace 108 | uses: actions/upload-artifact@v3 109 | with: 110 | path: unit_tests_report_${{ matrix.arch }}.* 111 | 112 | upload-native-binary: 113 | runs-on: ubuntu-latest 114 | needs: build-native-binary 115 | steps: 116 | - uses: actions/checkout@v3 117 | with: 118 | submodules: recursive 119 | 120 | - name: Download artifacts 121 | uses: actions/download-artifact@v3 122 | 123 | - name: Upload artifacts to build results 124 | if: github.event_name == 'push' 125 | uses: marvinpinto/action-automatic-releases@latest 126 | with: 127 | repo_token: ${{ secrets.GITHUB_TOKEN }} 128 | draft: false 129 | prerelease: true 130 | automatic_release_tag: build_${{ github.run_number }} 131 | title: "Build ${{ github.run_number }}" 132 | files: | 133 | ./artifact/unit_tests_report_amd64.* 134 | ./artifact/unit_tests_report_arm64.* 135 | ./artifact/code-coverage.tar.gz 136 | ./artifact/self-update-agent-amd64-build-${{ github.run_number }}.tar.gz 137 | ./artifact/self-update-agent-arm64-build-${{ github.run_number }}.tar.gz 138 | 139 | - name: Upload artifacts to build results (make latest tag) 140 | if: github.event_name == 'push' 141 | uses: marvinpinto/action-automatic-releases@latest 142 | with: 143 | repo_token: ${{ secrets.GITHUB_TOKEN }} 144 | draft: false 145 | prerelease: true 146 | automatic_release_tag: latest 147 | title: "Latest build (${{ github.run_number }})" 148 | files: | 149 | ./artifact/unit_tests_report_amd64.* 150 | ./artifact/unit_tests_report_arm64.* 151 | ./artifact/code-coverage.tar.gz 152 | ./artifact/self-update-agent-amd64-build-${{ github.run_number }}.tar.gz 153 | ./artifact/self-update-agent-arm64-build-${{ github.run_number }}.tar.gz 154 | 155 | -------------------------------------------------------------------------------- /.github/workflows/main_developer.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Contributors to the Eclipse Foundation 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | # SPDX-License-Identifier: Apache-2.0 16 | 17 | name: Build developer container 18 | 19 | on: 20 | workflow_dispatch: 21 | 22 | jobs: 23 | build-container-image: 24 | runs-on: ubuntu-latest 25 | permissions: 26 | contents: read 27 | packages: write 28 | id-token: write 29 | strategy: 30 | matrix: 31 | arch: [ amd64, arm64 ] 32 | steps: 33 | - uses: actions/checkout@v3 34 | with: 35 | submodules: recursive 36 | 37 | - name: Build Docker image 38 | uses: ./.github/actions/build-container-image 39 | with: 40 | arch: ${{ matrix.arch }} 41 | run_number: ${{ github.run_number }} 42 | commit_hash: ${{ github.sha }} 43 | username: ${{ github.actor }} 44 | token: ${{ secrets.GITHUB_TOKEN }} 45 | suffix: "-developer" 46 | 47 | push-container-image: 48 | runs-on: ubuntu-latest 49 | needs: build-container-image 50 | steps: 51 | - uses: actions/checkout@v3 52 | with: 53 | submodules: recursive 54 | 55 | - id: repo_name 56 | uses: ASzc/change-string-case-action@v1 57 | with: 58 | string: ${{ github.repository }} 59 | 60 | - name: Log into registry ghcr.io 61 | uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c 62 | with: 63 | registry: ghcr.io 64 | username: ${{ github.actor }} 65 | password: ${{ secrets.GITHUB_TOKEN }} 66 | 67 | - name: Create merged manifest and push 68 | env: 69 | REPO: ghcr.io/${{ steps.repo_name.outputs.lowercase }} 70 | run: | 71 | docker buildx imagetools create -t ghcr.io/${{ steps.repo_name.outputs.lowercase }}/self-update-agent-developer:build-${{ github.run_number }} ghcr.io/${{ steps.repo_name.outputs.lowercase }}/self-update-agent-developer:build-${{ github.run_number }}_arm64 ghcr.io/${{ steps.repo_name.outputs.lowercase }}/self-update-agent-developer:build-${{ github.run_number }}_amd64 72 | docker buildx imagetools create -t ghcr.io/${{ steps.repo_name.outputs.lowercase }}/self-update-agent-developer:latest ghcr.io/${{ steps.repo_name.outputs.lowercase }}/self-update-agent-developer:latest_arm64 ghcr.io/${{ steps.repo_name.outputs.lowercase }}/self-update-agent-developer:latest_amd64 73 | 74 | -------------------------------------------------------------------------------- /.github/workflows/ocaas.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Contributors to the Eclipse Foundation 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | # SPDX-License-Identifier: Apache-2.0 16 | 17 | name: OCaaS Compliance checks 18 | 19 | on: 20 | workflow_dispatch: 21 | push: 22 | pull_request: 23 | 24 | jobs: 25 | ocaas-scan: 26 | runs-on: ubuntu-latest 27 | if: github.repository_owner == 'SoftwareDefinedVehicle' 28 | steps: 29 | - name: setup env variables for OCaaS 30 | run: | 31 | echo "VCS_REVISION=${{ github.ref_name }}" >> $GITHUB_ENV 32 | - name: use different VCS_REVISION for pull requests 33 | if: github.event_name == 'pull_request' 34 | run: | 35 | echo "Workflow triggered by pull request. Scanning source branch only as merge revisions are not supported." 36 | echo "VCS_REVISION=${{ github.head_ref }}" >> $GITHUB_ENV 37 | - name: OCaaS Scans 38 | id: ocaas 39 | uses: docker://osmipublic.azurecr.io/ocaas-ci:latest 40 | continue-on-error: true # Built artifacts also should also be uploaded if the scan finds violations. 41 | with: 42 | args: auth generate-token run start download 43 | env: 44 | OCAAS_USERNAME: ${{ secrets.OCAAS_USERNAME }} 45 | OCAAS_PASSWORD: ${{ secrets.OCAAS_PASSWORD }} 46 | PROJECT_NAME: "Project LEDA - sdv-self-update-agent" 47 | PIPELINE_ID: 377 # Your pipeline ID. Provided by the OSMI team. 48 | VCS_URL: ${{ github.server_url }}/${{ github.repository }}.git 49 | # VCS_REVISION: is defined before 50 | APPLICATION_CATEGORY: "BT11" 51 | BLOCKING: true 52 | REPORT_FILES: DISCLOSURE_DOCUMENT_PDF,VULNERABILITY_REPORT_PDF,SCAN_REPORT_WEB_APP_HTML 53 | OUTPUT_DIR: reports/ 54 | - name: Upload reports 55 | id: upload 56 | uses: actions/upload-artifact@v3 57 | with: 58 | name: reports 59 | path: reports/ 60 | - name: Check for violations 61 | if: steps.ocaas.outcome != 'success' || steps.upload.outcome != 'success' 62 | run: exit 1 63 | 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeLists.txt.user 2 | CMakeCache.txt 3 | CMakeFiles 4 | CMakeScripts 5 | Testing 6 | Makefile 7 | cmake_install.cmake 8 | install_manifest.txt 9 | compile_commands.json 10 | CTestTestfile.cmake 11 | _deps 12 | build_amd64 13 | build_arm64 14 | dist_amd64 15 | dist_arm64 -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "3rdparty/paho.mqtt.c"] 2 | path = 3rdparty/paho.mqtt.c 3 | url = https://github.com/eclipse/paho.mqtt.c.git 4 | [submodule "3rdparty/paho.mqtt.cpp"] 5 | path = 3rdparty/paho.mqtt.cpp 6 | url = https://github.com/eclipse/paho.mqtt.cpp.git 7 | [submodule "3rdparty/curl"] 8 | path = 3rdparty/curl 9 | url = https://github.com/curl/curl.git 10 | [submodule "3rdparty/googletest"] 11 | path = 3rdparty/googletest 12 | url = https://github.com/google/googletest.git 13 | [submodule "3rdparty/glib"] 14 | path = 3rdparty/glib 15 | url = https://github.com/GNOME/glib.git 16 | [submodule "3rdparty/spdlog"] 17 | path = 3rdparty/spdlog 18 | url = https://github.com/gabime/spdlog.git 19 | [submodule "3rdparty/openssl"] 20 | path = 3rdparty/openssl 21 | url = https://github.com/openssl/openssl.git 22 | [submodule "3rdparty/nlohmann-json"] 23 | path = 3rdparty/nlohmann-json 24 | url = https://github.com/nlohmann/json 25 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Linux", 5 | "includePath": [ 6 | "${workspaceFolder}/**", 7 | "${workspaceFolder}/3rdparty" 8 | ], 9 | "defines": [], 10 | "compilerPath": "/usr/bin/gcc", 11 | "cStandard": "gnu17", 12 | "cppStandard": "gnu++17", 13 | "intelliSenseMode": "linux-gcc-x64", 14 | "configurationProvider": "ms-vscode.cmake-tools" 15 | } 16 | ], 17 | "version": 4 18 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch SDV update agent", 6 | "type": "cppdbg", 7 | "request": "launch", 8 | "program": "${workspaceFolder}/build_amd64/src/sdv-self-update-agent", 9 | "args": [], 10 | "stopAtEntry": false, 11 | "cwd": "${workspaceFolder}", 12 | "environment": [ 13 | { 14 | "name": "LD_LIBRARY_PATH", 15 | "value": "${workspaceFolder}/dist_amd64/lib" 16 | } 17 | ], 18 | "externalConsole": false, 19 | "MIMode": "gdb", 20 | "setupCommands": [ 21 | { 22 | "description": "Enable pretty-printing for gdb", 23 | "text": "-enable-pretty-printing", 24 | "ignoreFailures": true 25 | } 26 | ] 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "string": "cpp", 4 | "cstdlib": "cpp", 5 | "list": "cpp", 6 | "*.tcc": "cpp", 7 | "array": "cpp", 8 | "atomic": "cpp", 9 | "bit": "cpp", 10 | "cctype": "cpp", 11 | "chrono": "cpp", 12 | "clocale": "cpp", 13 | "cmath": "cpp", 14 | "condition_variable": "cpp", 15 | "cstdarg": "cpp", 16 | "cstddef": "cpp", 17 | "cstdint": "cpp", 18 | "cstdio": "cpp", 19 | "cstring": "cpp", 20 | "ctime": "cpp", 21 | "cwchar": "cpp", 22 | "cwctype": "cpp", 23 | "deque": "cpp", 24 | "map": "cpp", 25 | "unordered_map": "cpp", 26 | "vector": "cpp", 27 | "exception": "cpp", 28 | "algorithm": "cpp", 29 | "functional": "cpp", 30 | "iterator": "cpp", 31 | "memory": "cpp", 32 | "memory_resource": "cpp", 33 | "numeric": "cpp", 34 | "optional": "cpp", 35 | "random": "cpp", 36 | "ratio": "cpp", 37 | "string_view": "cpp", 38 | "system_error": "cpp", 39 | "tuple": "cpp", 40 | "type_traits": "cpp", 41 | "utility": "cpp", 42 | "fstream": "cpp", 43 | "future": "cpp", 44 | "initializer_list": "cpp", 45 | "iosfwd": "cpp", 46 | "iostream": "cpp", 47 | "istream": "cpp", 48 | "limits": "cpp", 49 | "mutex": "cpp", 50 | "new": "cpp", 51 | "ostream": "cpp", 52 | "sstream": "cpp", 53 | "stdexcept": "cpp", 54 | "streambuf": "cpp", 55 | "thread": "cpp", 56 | "cinttypes": "cpp", 57 | "typeinfo": "cpp", 58 | "any": "cpp", 59 | "forward_list": "cpp", 60 | "set": "cpp", 61 | "unordered_set": "cpp", 62 | "iomanip": "cpp", 63 | "variant": "cpp" 64 | } 65 | } -------------------------------------------------------------------------------- /3rdparty/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(PAHO_ENABLE_TESTING FALSE CACHE BOOL "") 2 | set(PAHO_BUILD_STATIC FALSE CACHE BOOL "") 3 | set(PAHO_BUILD_SHARED TRUE CACHE BOOL "") 4 | set(PAHO_BUILD_WITH_SSL FALSE CACHE BOOL "") 5 | add_subdirectory(./paho.mqtt.c) 6 | 7 | set(PAHO_MQTT_C_LIBRARIES ${CMAKE_BINARY_DIR}/3rdparty/paho.mqtt.c/src/libpaho-mqtt3c.so) 8 | set(PAHO_MQTT_C_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/3rdparty/paho.mqtt.c/src) 9 | 10 | add_subdirectory(./paho.mqtt.cpp) 11 | 12 | add_dependencies(paho-mqttpp3 paho-mqtt3a) 13 | add_dependencies(paho-mqttpp3 paho-mqtt3c) 14 | 15 | set(CMAKE_PREFIX_PATH ${CMAKE_BINARY_DIR}/lib/pkgconfig) 16 | find_package(OpenSSL REQUIRED) 17 | set(CURL_USE_OPENSSL ON) 18 | set(BUILD_SHARED_LIBS ON) 19 | set(BUILD_CURL_EXE OFF) 20 | add_subdirectory(curl) 21 | 22 | if(SUA_BUILD_TESTS) 23 | add_subdirectory(googletest) 24 | endif() 25 | 26 | install( 27 | FILES 28 | ${CMAKE_BINARY_DIR}/lib/libssl.so.3 29 | ${CMAKE_BINARY_DIR}/lib/libcrypto.so.3 30 | ${CMAKE_BINARY_DIR}/3rdparty/curl/lib/libcurl.so 31 | ${CMAKE_BINARY_DIR}/glib/subprojects/zlib-1.2.11/libz.so 32 | DESTINATION ${CMAKE_INSTALL_PREFIX}/lib 33 | ) 34 | 35 | install(FILES ${CMAKE_BINARY_DIR}/glib/gio/libgio-2.0.so.0.7800.0 DESTINATION ${CMAKE_INSTALL_PREFIX}/lib RENAME libgio-2.0.so.0) 36 | install(FILES ${CMAKE_BINARY_DIR}/glib/glib/libglib-2.0.so.0.7800.0 DESTINATION ${CMAKE_INSTALL_PREFIX}/lib RENAME libglib-2.0.so.0) 37 | install(FILES ${CMAKE_BINARY_DIR}/glib/gmodule/libgmodule-2.0.so.0.7800.0 DESTINATION ${CMAKE_INSTALL_PREFIX}/lib RENAME libgmodule-2.0.so.0) 38 | install(FILES ${CMAKE_BINARY_DIR}/glib/gobject/libgobject-2.0.so.0.7800.0 DESTINATION ${CMAKE_INSTALL_PREFIX}/lib RENAME libgobject-2.0.so.0) 39 | install(FILES ${CMAKE_BINARY_DIR}/glib/subprojects/libffi/src/libffi.so.7.1.0 DESTINATION ${CMAKE_INSTALL_PREFIX}/lib RENAME libffi.so.7) 40 | install(FILES ${CMAKE_BINARY_DIR}/glib/subprojects/proxy-libintl/libintl.so.8 DESTINATION ${CMAKE_INSTALL_PREFIX}/lib RENAME libintl.so.8) 41 | -------------------------------------------------------------------------------- /3rdparty/meson-cross-file-aarch64.txt: -------------------------------------------------------------------------------- 1 | [host_machine] 2 | system = 'linux' 3 | cpu_family = 'aarch64' 4 | cpu = 'aarch64' 5 | endian = 'little' 6 | 7 | [properties] 8 | c_args = [] 9 | c_link_args = [] 10 | 11 | [binaries] 12 | c = 'aarch64-linux-gnu-gcc-9' 13 | cpp = 'aarch64-linux-gnu-g++-9' 14 | ar = 'aarch64-linux-gnu-ar' 15 | ld = 'aarch64-linux-gnu-ld' 16 | objcopy = 'aarch64-linux-gnu-objcopy' 17 | strip = 'aarch64-linux-gnu-strip' 18 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(sdv-self-update-agent) 3 | 4 | set(CMAKE_CXX_STANDARD 14) 5 | 6 | set(CMAKE_SKIP_BUILD_RPATH TRUE) 7 | set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) 8 | set(CMAKE_INSTALL_RPATH ../lib) 9 | 10 | option(SUA_BUILD_TESTS "Build unit tests with code-coverage report enabled" OFF) 11 | option(SUA_MEASURE_CODE_COVERAGE "Measure code-coverage" OFF) 12 | 13 | include(cmake/dependencies.cmake) 14 | 15 | add_subdirectory(3rdparty) 16 | add_subdirectory(src) 17 | 18 | if(SUA_BUILD_TESTS) 19 | add_subdirectory(utest) 20 | endif() 21 | 22 | unset(SUA_BUILD_TESTS CACHE) 23 | -------------------------------------------------------------------------------- /Dockerfile.amd64: -------------------------------------------------------------------------------- 1 | # Dockerfile for building multi-arch images and for runtime of the SDV Self Update Agent 2 | 3 | FROM --platform=${BUILDPLATFORM} ubuntu AS build 4 | 5 | ARG TARGETPLATFORM 6 | ARG GITHUB_RUN_NUMBER 7 | ARG GITHUB_COMMIT_HASH 8 | 9 | RUN apt-get update && apt-get -y install \ 10 | autoconf binutils cmake file \ 11 | gcc g++ git libtool make \ 12 | build-essential libcurl4-openssl-dev \ 13 | binutils-aarch64-linux-gnu gcc-9-aarch64-linux-gnu g++-9-aarch64-linux-gnu \ 14 | python3 python3-pip python3-setuptools python3-wheel ninja-build meson \ 15 | libselinux1-dev libmount-dev libmount1 libblkid-dev \ 16 | ca-certificates 17 | 18 | # Copy the sources to the build container 19 | COPY .git /work/.git 20 | COPY src /work/src 21 | COPY cmake /work/cmake 22 | COPY 3rdparty /work/3rdparty 23 | COPY utest /work/utest 24 | COPY scripts /work/scripts 25 | COPY CMakeLists.txt /work/CMakeLists.txt 26 | 27 | # Build the Self Update Agent binaries and dependencies 28 | RUN cd /work \ 29 | && mkdir -p build_amd64/3rdparty/openssl \ 30 | && mkdir -p dist_amd64 \ 31 | && ./scripts/build_openssl_amd64.sh > /dev/null 32 | 33 | RUN cd /work \ 34 | && ./scripts/build_glib_amd64.sh > /dev/null 35 | 36 | RUN cd /work \ 37 | && cd build_amd64 \ 38 | && cmake \ 39 | -DCMAKE_INSTALL_PREFIX=../dist_amd64 \ 40 | -DCMAKE_TOOLCHAIN_FILE=../cmake/linux/amd64/toolchain.cmake \ 41 | -DOPENSSL_ROOT_DIR=../build_amd64 \ 42 | -DOPENSSL_CRYPTO_LIBRARY=../build_amd64/lib/libcrypto.so \ 43 | -DCMAKE_BUILD_TYPE="Release" \ 44 | -DSUA_BUILD_NUMBER=$GITHUB_RUN_NUMBER \ 45 | -DSUA_COMMIT_HASH=$GITHUB_COMMIT_HASH \ 46 | .. 47 | 48 | RUN cd /work \ 49 | && cd build_amd64 \ 50 | && make -j 51 | 52 | RUN cd /work \ 53 | && cd build_amd64 \ 54 | && make install/strip 55 | 56 | RUN cd /work \ 57 | && strip dist_amd64/lib/lib* \ 58 | && strip /work/build_amd64/glib/gio/libgio-2.0.so.0 \ 59 | && strip /work/build_amd64/glib/gobject/libgobject-2.0.so.0 \ 60 | && strip /work/build_amd64/glib/glib/libglib-2.0.so.0 \ 61 | && strip /work/build_amd64/glib/gmodule/libgmodule-2.0.so.0 \ 62 | && strip /work/build_amd64/glib/subprojects/libffi/src/libffi.so.7 \ 63 | && strip /work/build_amd64/glib/subprojects/zlib-1.2.11/libz.so 64 | 65 | #Define RUNTIME environment, the final image 66 | FROM --platform=${TARGETPLATFORM} scratch as runtime 67 | ARG TARGETPLATFORM 68 | COPY --from=build /work/dist_amd64/bin/sdv-self-update-agent /sua/bin/sdv-self-update-agent 69 | COPY --from=build /work/dist_amd64/lib/libpaho-mqttpp3.so.1 /sua/lib/ 70 | COPY --from=build /work/dist_amd64/lib/libpaho-mqtt3a.so.1 /sua/lib/ 71 | COPY --from=build /work/dist_amd64/lib/libpaho-mqtt3c.so.1 /sua/lib/ 72 | COPY --from=build /work/dist_amd64/lib/libcurl.so.4.8.0 /sua/lib/libcurl.so.4 73 | COPY --from=build /work/dist_amd64/lib/libcrypto.so.3 /sua/lib/ 74 | COPY --from=build /work/dist_amd64/lib/libssl.so.3 /sua/lib/ 75 | COPY --from=build /work/dist_amd64/lib/libsua.so /sua/lib/ 76 | COPY --from=build /work/build_amd64/glib/gio/libgio-2.0.so.0 /sua/lib/ 77 | COPY --from=build /work/build_amd64/glib/gobject/libgobject-2.0.so.0 /sua/lib/ 78 | COPY --from=build /work/build_amd64/glib/glib/libglib-2.0.so.0 /sua/lib/ 79 | COPY --from=build /work/build_amd64/glib/gmodule/libgmodule-2.0.so.0 /sua/lib/ 80 | COPY --from=build /work/build_amd64/glib/subprojects/libffi/src/libffi.so.7 /sua/lib/ 81 | COPY --from=build /work/build_amd64/glib/subprojects/zlib-1.2.11/libz.so /sua/lib/ 82 | COPY --from=build /work/build_amd64/glib/subprojects/proxy-libintl/libintl.so.8 /sua/lib/ 83 | COPY --from=build /etc/ssl/certs /etc/ssl/certs/ 84 | COPY --from=build /usr/share/ca-certificates /usr/share/ca-certificates 85 | COPY --from=build /lib64/ld-linux-x86-64.so.2 /lib64/ld-linux-x86-64.so.2 86 | COPY --from=build /lib/x86_64-linux-gnu/libgcc_s.so.1 /lib/libgcc_s.so.1 87 | COPY --from=build /lib/x86_64-linux-gnu/libstdc++.so.6 /lib/libstdc++.so.6 88 | COPY --from=build /lib/x86_64-linux-gnu/libc.so.6 /lib/libc.so.6 89 | COPY --from=build /lib/x86_64-linux-gnu/libz.so.1 /lib/libz.so.1 90 | COPY --from=build /lib/x86_64-linux-gnu/libm.so.6 /lib/libm.so.6 91 | 92 | WORKDIR /sua/bin 93 | ENV LD_LIBRARY_PATH ../lib 94 | CMD ["./sdv-self-update-agent"] 95 | -------------------------------------------------------------------------------- /Dockerfile.arm64: -------------------------------------------------------------------------------- 1 | # Dockerfile for building multi-arch images and for runtime of the SDV Self Update Agent 2 | 3 | FROM --platform=${BUILDPLATFORM} ubuntu AS build 4 | 5 | ARG TARGETPLATFORM 6 | ARG GITHUB_RUN_NUMBER 7 | ARG GITHUB_COMMIT_HASH 8 | 9 | RUN apt-get update && apt-get -y install \ 10 | autoconf binutils cmake file \ 11 | gcc g++ git libtool make \ 12 | build-essential libcurl4-openssl-dev \ 13 | binutils-aarch64-linux-gnu gcc-9-aarch64-linux-gnu g++-9-aarch64-linux-gnu \ 14 | python3 python3-pip python3-setuptools python3-wheel ninja-build meson \ 15 | libselinux1-dev libmount-dev libmount1 libblkid-dev \ 16 | ca-certificates 17 | 18 | # Copy the sources to the build container 19 | COPY .git /work/.git 20 | COPY src /work/src 21 | COPY cmake /work/cmake 22 | COPY 3rdparty /work/3rdparty 23 | COPY utest /work/utest 24 | COPY scripts /work/scripts 25 | COPY CMakeLists.txt /work/CMakeLists.txt 26 | 27 | # Build the Self Update Agent binaries and dependencies 28 | RUN cd /work \ 29 | && mkdir -p build_arm64/3rdparty/openssl \ 30 | && mkdir -p dist_arm64 \ 31 | && ./scripts/build_openssl_arm64.sh > /dev/null 32 | 33 | RUN cd /work \ 34 | && ./scripts/build_glib_arm64.sh > /dev/null 35 | 36 | RUN cd /work \ 37 | && cd build_arm64 \ 38 | && cmake \ 39 | -DCMAKE_INSTALL_PREFIX=../dist_arm64 \ 40 | -DCMAKE_TOOLCHAIN_FILE=../cmake/linux/arm64/toolchain.cmake \ 41 | -DOPENSSL_ROOT_DIR=../build_arm64 \ 42 | -DOPENSSL_CRYPTO_LIBRARY=../build_arm64/lib/libcrypto.so \ 43 | -DCMAKE_BUILD_TYPE="Release" \ 44 | -DSUA_BUILD_NUMBER=$GITHUB_RUN_NUMBER \ 45 | -DSUA_COMMIT_HASH=$GITHUB_COMMIT_HASH \ 46 | .. 47 | 48 | RUN cd /work \ 49 | && cd build_arm64 \ 50 | && make -j 51 | 52 | RUN cd /work \ 53 | && cd build_arm64 \ 54 | && make install/strip 55 | 56 | RUN cd /work \ 57 | && aarch64-linux-gnu-strip dist_arm64/lib/lib* \ 58 | && aarch64-linux-gnu-strip /work/build_arm64/glib/gio/libgio-2.0.so.0 \ 59 | && aarch64-linux-gnu-strip /work/build_arm64/glib/gobject/libgobject-2.0.so.0 \ 60 | && aarch64-linux-gnu-strip /work/build_arm64/glib/glib/libglib-2.0.so.0 \ 61 | && aarch64-linux-gnu-strip /work/build_arm64/glib/gmodule/libgmodule-2.0.so.0 \ 62 | && aarch64-linux-gnu-strip /work/build_arm64/glib/subprojects/libffi/src/libffi.so.7 \ 63 | && aarch64-linux-gnu-strip /work/build_arm64/glib/subprojects/zlib-1.2.11/libz.so 64 | 65 | #Define RUNTIME environment, the final image 66 | FROM --platform=${TARGETPLATFORM} scratch as runtime 67 | ARG TARGETPLATFORM 68 | COPY --from=build /work/dist_arm64/bin/sdv-self-update-agent /sua/bin/sdv-self-update-agent 69 | COPY --from=build /work/dist_arm64/lib/libpaho-mqttpp3.so.1 /sua/lib/ 70 | COPY --from=build /work/dist_arm64/lib/libpaho-mqtt3a.so.1 /sua/lib/ 71 | COPY --from=build /work/dist_arm64/lib/libpaho-mqtt3c.so.1 /sua/lib/ 72 | COPY --from=build /work/dist_arm64/lib/libcurl.so.4.8.0 /sua/lib/libcurl.so.4 73 | COPY --from=build /work/dist_arm64/lib/libcrypto.so.3 /sua/lib/ 74 | COPY --from=build /work/dist_arm64/lib/libssl.so.3 /sua/lib/ 75 | COPY --from=build /work/dist_arm64/lib/libsua.so /sua/lib/ 76 | COPY --from=build /work/build_arm64/glib/gio/libgio-2.0.so.0 /sua/lib/ 77 | COPY --from=build /work/build_arm64/glib/gobject/libgobject-2.0.so.0 /sua/lib/ 78 | COPY --from=build /work/build_arm64/glib/glib/libglib-2.0.so.0 /sua/lib/ 79 | COPY --from=build /work/build_arm64/glib/gmodule/libgmodule-2.0.so.0 /sua/lib/ 80 | COPY --from=build /work/build_arm64/glib/subprojects/libffi/src/libffi.so.7 /sua/lib/ 81 | COPY --from=build /work/build_arm64/glib/subprojects/zlib-1.2.11/libz.so /sua/lib/ 82 | COPY --from=build /work/build_arm64/glib/subprojects/proxy-libintl/libintl.so.8 /sua/lib/ 83 | COPY --from=build /etc/ssl/certs /etc/ssl/certs/ 84 | COPY --from=build /usr/share/ca-certificates /usr/share/ca-certificates 85 | COPY --from=build /usr/aarch64-linux-gnu/lib/ld-linux-aarch64.so.1 /lib/ld-linux-aarch64.so.1 86 | COPY --from=build /usr/aarch64-linux-gnu/lib/libstdc++.so.6 /lib/ 87 | COPY --from=build /usr/aarch64-linux-gnu/lib/libgcc_s.so.1 /lib/ 88 | COPY --from=build /usr/aarch64-linux-gnu/lib/libc.so.6 /lib/ 89 | COPY --from=build /usr/aarch64-linux-gnu/lib/libm.so.6 /lib/ 90 | 91 | WORKDIR /sua/bin 92 | ENV LD_LIBRARY_PATH ../lib 93 | CMD ["./sdv-self-update-agent"] 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # License 2 | Apache 2.0 3 | 4 | # Contribution 5 | Follow guidelines from Eclipse Leda: https://eclipse-leda.github.io/leda/docs/project-info/contribution-guidelines 6 | 7 | # Security Policy 8 | This project implements the Eclipse Foundation Security Policy: https://www.eclipse.org/security 9 | 10 | # Reporting a Vulnerability 11 | Please report vulnerabilities to the Eclipse Foundation Security Team at security@eclipse.org 12 | 13 | # Data privacy notice: 14 | Data privacy notice is here: [link](docs/data-privacy-notice.md) 15 | 16 | # SDV Self Update Agent 17 | The self update agent (SUA) is a component responsible for the OS Update process. 18 | SUA is communicating on MQTT interface via usage of defined messages. Internally, SUA uses [RAUC](https://rauc.io/) to perform the update. 19 | 20 | Following sequence diagram shows the happy path example of communication between components. 21 | 22 | ```mermaid 23 | sequenceDiagram 24 | participant m as MQTT Broker 25 | participant s as SUA 26 | participant r as RAUC 27 | 28 | s -->> m: connect 29 | 30 | loop Wait for OTA trigger 31 | 32 | Note left of s: Initial start 33 | s ->> m: Current state (installed version from booted partition) 34 | 35 | Note left of s: Trigger for OTA 36 | m ->> s: Desired state request (new version and url to bundle) 37 | s ->> m: Feedback (update actions are identified) 38 | 39 | Note left of s: Command for Download 40 | m ->> s: Download command 41 | s ->> s: Download bundle 42 | s ->> m: Feedback (downloading/downloaded/failed) 43 | 44 | Note left of s: Command for Update 45 | m ->> s: Update command 46 | s ->> r: Flash image to partition 47 | r ->> r: Flashing... 48 | s ->> m: Feedback (updating with percentage) 49 | r ->> s: Flash completed/failed 50 | s ->> m: Feedback (updated/failed) 51 | 52 | Note left of s: Command for Activate 53 | m ->> s: Activate command 54 | s ->> r: Switch partitions (booted <-> other) 55 | r ->> s: Switch completed/failed 56 | s ->> m: Feedback (activated/failed) 57 | 58 | Note left of s: Command for Cleanup 59 | m ->> s: Cleanup command 60 | s ->> s: Remove temporary files 61 | s ->> m: Cleanup completed + status from previously failed state
(completed/failed) 62 | 63 | end 64 | ``` 65 | 66 | ```mermaid 67 | stateDiagram 68 | Uninitialized --> Connected: Connected 69 | Connected --> Identified: Start (OTA trigger) 70 | Identified --> Downloading: Command download 71 | Identified --> Failed: If OTA trigger is invalid 72 | Downloading --> Updating: Command update 73 | Downloading --> Failed: If download has failed 74 | Updating --> Activate: Command activate 75 | Updating --> Failed: If update has failed 76 | Activate --> Cleanup: Command cleanup 77 | Activate --> Failed: If activate has failed 78 | Failed --> Cleanup: Command cleanup 79 | Cleanup --> Idle 80 | ``` 81 | Important: Uninitialized state is the default entry state or state in case connection is lost. To simplify reading of the diagram arrows from other states to Unitialized have been removed. 82 | 83 | MQTT communication is done over 5 MQTT topics: 84 | 85 | ## Trigger OTA 86 | | Topic | Direction | Description | 87 | |-------|-----------|-------------| 88 | | selfupdate/desiredstate | IN | This message triggers the update process. The payload shall contain all data necessary to obtain the update bundle and to install it. | 89 | 90 | ## Trigger self-update step/action 91 | | Topic | Direction | Description | 92 | |-------|-----------|-------------| 93 | | selfupdate/desiredstate/command | IN | This message triggers the single step in update process (download/flash/activate/cleanup). | 94 | 95 | ## Report current state 96 | | Topic | Direction | Description | 97 | |-------|-----------|-------------| 98 | | selfupdate/currentstate | OUT | This message is being sent either once on SUA start or as an answer to response received by selfupdate/currentstate/get. It contains information about the currently installed OS version. | 99 | 100 | ## Get current state 101 | | Topic | Direction | Description | 102 | |-------|-----------|-------------| 103 | | selfupdate/currentstate/get | IN | This message can be received at any point of time. Indicates that SUA should send back the version of the installed OS as current state. | 104 | 105 | ## Report status of self-update process 106 | | Topic | Direction | Description | 107 | |-------|-----------|-------------| 108 | | selfupdate/desiredstatefeedback | OUT | This message is being sent by SUA to share the current progress of the triggered update process. This is the *OUT* counterpart of *selfupdate/desiredstate* input message. | 109 | 110 | Detailed description of Update Agent API can be found here: [link](docs/bfb.md). 111 | Migration guide for users from old YAML payloads to new JSON format can be found here: [link](docs/migration_guide.md). 112 | 113 | # Checkout 114 | SUA links to some 3rd party libraries, which are fetched as submodules, therefore the cloning shall be performed with recursive option: 115 | 116 | ``` 117 | git clone --recursive https://github.com/eclipse-leda/leda-contrib-self-update-agent.git 118 | ``` 119 | or if was cloned non recursively 120 | ``` 121 | git submodule init 122 | git submodule update 123 | ``` 124 | 125 | # HowTo Build 126 | Instructions for building are available on: [link](docs/building/README.md) 127 | 128 | # HowTo Deploy 129 | Instructions for deploying are available on: [link](docs/deploying/README.md) 130 | 131 | # HowTo Test 132 | Instructions for testing are available on: [link](docs/testing/README.md) 133 | -------------------------------------------------------------------------------- /clang-format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | find src -iname *.h -o -iname *.cpp | xargs clang-format -i 4 | -------------------------------------------------------------------------------- /cmake/dependencies.cmake: -------------------------------------------------------------------------------- 1 | add_library(curl_lib SHARED IMPORTED) 2 | 3 | if (CMAKE_BUILD_TYPE STREQUAL "Debug") 4 | set_property( 5 | TARGET curl_lib 6 | PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/3rdparty/curl/lib/libcurl-d.so 7 | ) 8 | else () 9 | set_property( 10 | TARGET curl_lib 11 | PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/3rdparty/curl/lib/libcurl.so 12 | ) 13 | endif() 14 | 15 | add_library(ssl_lib SHARED IMPORTED) 16 | set_property( 17 | TARGET ssl_lib 18 | PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/lib/libssl.so.3 19 | ) 20 | 21 | add_library(crypto_lib SHARED IMPORTED) 22 | set_property( 23 | TARGET crypto_lib 24 | PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/lib/libcrypto.so.3 25 | ) 26 | 27 | add_library(gio_lib SHARED IMPORTED) 28 | set_property( 29 | TARGET gio_lib 30 | PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/glib/gio/libgio-2.0.so 31 | ) 32 | 33 | add_library(glib_lib SHARED IMPORTED) 34 | set_property( 35 | TARGET glib_lib 36 | PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/glib/glib/libglib-2.0.so 37 | ) 38 | 39 | add_library(gmodule_lib SHARED IMPORTED) 40 | set_property( 41 | TARGET gmodule_lib 42 | PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/glib/gmodule/libgmodule-2.0.so 43 | ) 44 | 45 | add_library(gobject_lib SHARED IMPORTED) 46 | set_property( 47 | TARGET gobject_lib 48 | PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/glib/gobject/libgobject-2.0.so 49 | ) 50 | 51 | add_library(ffi_lib SHARED IMPORTED) 52 | set_property( 53 | TARGET ffi_lib 54 | PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/glib/subprojects/libffi/src/libffi.so 55 | ) 56 | 57 | add_library(z_lib SHARED IMPORTED) 58 | set_property( 59 | TARGET z_lib 60 | PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/glib/subprojects/zlib-1.2.11/libz.so 61 | ) 62 | 63 | add_library(intl_lib SHARED IMPORTED) 64 | set_property( 65 | TARGET intl_lib 66 | PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/glib/subprojects/proxy-libintl/libintl.so 67 | ) 68 | -------------------------------------------------------------------------------- /cmake/linux/amd64/toolchain.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Linux) 2 | set(CMAKE_SYSTEM_VERSION 1) 3 | 4 | set(CMAKE_C_COMPILER gcc) 5 | set(CMAKE_CXX_COMPILER g++) 6 | 7 | set(SUA_PLATFORM_LIBS resolv blkid selinux) 8 | -------------------------------------------------------------------------------- /cmake/linux/arm64/toolchain.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Linux) 2 | set(CMAKE_SYSTEM_VERSION 1) 3 | 4 | set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc-9) 5 | set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++-9) 6 | 7 | set(SUA_PLATFORM_LIBS resolv) 8 | -------------------------------------------------------------------------------- /docs/bfb.md: -------------------------------------------------------------------------------- 1 | # Detailed description of bfb (binding-for-backend) communication protocol 2 | 3 | ## Current state message 4 | This is retained message sent by SUA on start-up or as an answer to response for current state (then it is supplied with activityId field). 5 | MQTT Topic: selfupdate/currentstate 6 | ``` 7 | { 8 | "activityId": "random-uuid", 9 | "timestamp": 123456789, 10 | "payload": { 11 | "softwareNodes": [ 12 | { 13 | "id": "self-update-agent", 14 | "version": "build-42, 15 | "name": "OTA NG Self Update Agent", 16 | "type": "APPLICATION" 17 | }, 18 | { 19 | "id": "self-update:leda-deviceimage", 20 | "version": "1.0", 21 | "name": "Official Leda device image", 22 | "type": "IMAGE" 23 | } 24 | ], 25 | "hardwareNodes": [], 26 | "associations": [ 27 | { 28 | "sourceId": "self-update-agent", 29 | "targetId": "self-update:leda-deviceimage" 30 | } 31 | ] 32 | } 33 | } 34 | ``` 35 | 36 | ## Current state request 37 | This message is a trigger to send back version of installed OS. Can be received at any point of time. 38 | MQTT Topic: selfupdate/currentstate/get 39 | ``` 40 | { 41 | "activityId": "random-uuid", 42 | "timestamp": 123456789 43 | } 44 | ``` 45 | 46 | ## Desired state 47 | This message indicates that SUA needs to perform an update operation. 48 | MQTT Topic: selfupdate/desiredstate 49 | ``` 50 | { 51 | "activityId": "random-uuid", 52 | "timestamp": 123456789, 53 | "payload": { 54 | "domains": [ 55 | { 56 | "id": "self-update", 57 | "components": [ 58 | { 59 | "id": "os-image", 60 | "version": "1.0", 61 | "config": [ 62 | { 63 | "key": "image", 64 | "value": "http://example.com/downloads/os-image-1.1.bin" 65 | } 66 | ] 67 | } 68 | ] 69 | } 70 | ] 71 | } 72 | } 73 | ``` 74 | 75 | ## Desired state feedback 76 | This message contains information about SUA state - what agent is doing - installing, downloading or idle. 77 | MQTT Topic: selfupdate/desiredstatefeedback 78 | ``` 79 | { 80 | "activityId": "random-uuid", 81 | "timestamp": 123456789, 82 | "payload": { 83 | "status": "payload_status", 84 | "message": "status message as a string", 85 | "actions": [ 86 | { 87 | "component": { 88 | "id": "self-update:os-image", 89 | "version": "1.0" 90 | }, 91 | "status": "action_status", 92 | "progress": 42, 93 | "message": "action message as a string" 94 | } 95 | ] 96 | } 97 | } 98 | ``` 99 | 100 | ## Command for single self-update action/step 101 | This message contains information about SUA action - what must be done as a single step - download, update, activate, cleanup. 102 | MQTT Topic: selfupdate/desiredstate/command 103 | ``` 104 | { 105 | "activityId": "random-uuid-as-string", 106 | "timestamp": 123456789, 107 | "payload": { 108 | "baseline": "BASELINE NAME", 109 | "command": "command_type" 110 | } 111 | } 112 | ``` 113 | 114 | ## Description of statuses 115 | Following combinations of payload_status and action_status are possible: 116 | | payload_status | action_status | Description | 117 | |-----------------------|------------------|-------------| 118 | | IDENTIFYING | | SUA has received request for update and evaluating it | 119 | | IDENTIFICATION_FAILED | | SUA has received request for update and is unable to perform self-update | 120 | | IDENTIFIED | IDENTIFIED | SUA has received request for update and will try to perform an update | 121 | | DOWNLOADING | DOWNLOADING | Downloading an image | 122 | | DOWNLOAD_SUCCESS | DOWNLOAD_SUCCESS | Image is downloaded without errors | 123 | | DOWNLOAD_FAILURE | DOWNLOAD_FAILURE | Image was not downloaded | 124 | | UPDATING | UPDATING | Installing the downloaded image | 125 | | UPDATE_SUCCESS | UPDATING | Image installed | 126 | | UPDATE_FAILURE | UPDATE_FAILURE | Image was not installed due to error | 127 | | ACTIVATING | UPDATING | SUA is activating the partition with new image | 128 | | ACTIVATION_SUCCESS | UPDATED | SUA has activated the partition with new image | 129 | | ACTIVATION_FAILURE | UPDATE_FAILURE | SUA has failed to activate the partition with new image | 130 | | COMPLETE | UPDATE_SUCCESS | Self-update finished without errors | 131 | | INCOMPLETE | UPDATE_FAILURE
DOWNLOAD_FAILURE | Self-update is incomplete. Action status and action
message will preserve error from one of the previous steps: download/flash/activate.| 132 | 133 | ## Description of command types 134 | | Command | Description | 135 | |----------|-------------| 136 | | DOWNLOAD | Start download of bundle | 137 | | UPDATE | Flash image to partition | 138 | | ACTIVATE | Make 'other' partition bootable | 139 | | CLEANUP | Remove temporary files | 140 | 141 | ## Description of other fields 142 | | Name | Description | 143 | |------------|-------------| 144 | | activityId | Random UUID as string (needs to be taken from desiredstate message and passed back in all feedback messages) | 145 | | timestamp | Epoch time (in seconds since January 1, 1970) | 146 | | version | Reflects version of SUA running on the device, OS version or bundle version | 147 | | progress | Percentage value of download/install progress | 148 | 149 | -------------------------------------------------------------------------------- /docs/building/README.md: -------------------------------------------------------------------------------- 1 | # Building 2 | 3 | The SUA can be used in both forms: 4 | 5 | - as a native application, 6 | - as an image running inside of a container. 7 | 8 | This section describes the building steps for both variants. 9 | 10 | If the SUA is to be used on the board with a different architecture than the host machine on which it is being built, the cross compilation is necessary. 11 | 12 | # Native 13 | 14 | ## Install prerequisites 15 | 16 | To be able to compile, following libraries and tools have to be installed: 17 | 18 | ### Toolchain 19 | 20 | ``` 21 | sudo apt install binutils cmake make autoconf file libtool git build-essential libcurl4-openssl-dev libselinux1-dev libmount-dev libmount1 libblkid-dev 22 | ``` 23 | 24 | ### Compilers (for x86_64) 25 | 26 | ``` 27 | sudo apt install gcc g++ 28 | ``` 29 | 30 | ### Compilers (for Raspberry Pi board) 31 | 32 | ``` 33 | sudo apt install gcc-9-aarch64-linux-gnu g++-9-aarch64-linux-gnu 34 | ``` 35 | 36 | ### Build tool Meson for glib 37 | 38 | ``` 39 | sudo apt-get install python3 python3-pip python3-setuptools python3-wheel ninja-build meson 40 | pip3 install meson 41 | ``` 42 | 43 | ## Build for amd64 or for arm64 44 | 45 | ``` 46 | ./scripts/build.sh amd64 47 | ``` 48 | or 49 | ``` 50 | ./scripts/build.sh arm64 51 | ``` 52 | 53 | ## Run 54 | 55 | The SUA binary will be placed under `dist_amd64/bin` / `dist_arm64/bin`. There is an optional parameter that can be specified: `-p path` location where the the downloaded bundles shall be stored: 56 | 57 | ``` 58 | cd dist_amd64/bin 59 | LD_LIBRARY_PATH=../lib ./sdv-self-update-agent 60 | ``` 61 | 62 | ``` 63 | cd dist_amd64/bin 64 | LD_LIBRARY_PATH=../lib ./sdv-self-update-agent -p /data/download 65 | ``` 66 | 67 | ## Hint about usage of devcontainer 68 | 69 | This project provides the `.devcontainer`, which some of required for building tools. You can select the *open in the container* option in VS and this way use predefined development environment, instead of installing all the tools natively. The `.devcontainer` will be also used when you develop in *Codespaces*. 70 | 71 | ### Windows 72 | 73 | If the docker and VS proxy settings are correctly set, the .devcontainer environment shall work on windows out of the box. 74 | 75 | # Containerized 76 | 77 | SUA can be built as multi-arch image, and run inside of the container. This is preferred way of deployment, as it provides a necessary sandbox runtime environment and also allows to easily adjust the configuration settings (ports, ENV variables) via yaml. 78 | 79 | Also the building process is easier, as all necessary tools are provided by the build platform - as also the build itself is being performed inside of the container. 80 | 81 | ## Build multi-arch image 82 | 83 | ### Build and load to local docker registry 84 | 85 | Useful for testing on host machine: (specify only the host's architecture): 86 | 87 | ``` 88 | docker buildx build -f Dockerfile.arm64 --progress plain --platform "linux/arm64" -t sua:arm64 --load . 89 | docker buildx build -f Dockerfile.amd64 --progress plain --platform "linux/amd64" -t sua:amd64 --load . 90 | ``` 91 | 92 | ### Build and push to remote container registry (ghrc) 93 | 94 | ``` 95 | docker buildx build -f Dockerfile.arm64 --progress plain --platform "linux/arm64" -t ghcr.io/softwaredefinedvehicle/sdv-self-update-agent/sua:testing_arm64 --push . 96 | docker buildx build -f Dockerfile.amd64 --progress plain --platform "linux/amd64" -t ghcr.io/softwaredefinedvehicle/sdv-self-update-agent/sua:testing_amd64 --push . 97 | ``` 98 | 99 | ### Create multiarch image using remote container registry (ghrc) 100 | 101 | ``` 102 | docker buildx imagetools create -t ghcr.io/softwaredefinedvehicle/sdv-self-update-agent/sua:testing ghcr.io/softwaredefinedvehicle/sdv-self-update-agent/sua:testing_arm64 ghcr.io/softwaredefinedvehicle/sdv-self-update-agent/sua:testing_amd64 103 | ``` 104 | 105 | ## Run 106 | 107 | ``` 108 | docker run -it sua:latest 109 | ``` 110 | -------------------------------------------------------------------------------- /docs/data-privacy-notice.md: -------------------------------------------------------------------------------- 1 | ### Privacy Customer Information 2 | 3 | Your privacy is important to us. The following information is to provide you with all information relevant to data protection in order to be able to use the software, in a data protection compliant manner. It is provided as an information source for your solution-specific data protection and data privacy topics. This is not intended to provide and should not be relied on for legal advice. 4 | 5 | ### Your Role 6 | 7 | First things first: when you choose and use our software, you are most likely acting in the role of data controller, if personal related data is being processed. Therefore, you must ensure that the processing of personal data complies with the respective local legal requirements, e.g. when processing data within the scope of General Data Protection Regulation (GDPR) the legal requirements for a controller from the GDPR. 8 | 9 | ### Where may the processing of personal related data be relevant? 10 | 11 | When using our software in combination with other software components, personal data or data categories may be collected for the purpose of developing, testing and running (in-)Vehicle Applications. Possible examples are the vehicle identification number (VIN), the number plate, GPS data, applications metrics, or other measurement data. You can determine which data or data categories are collected when configuring the software. These data are stored in volatile memory and are deleted by shutting down the system. 12 | 13 | However, you are responsible for the compliant handling of the data in accordance with the applicable local law. 14 | 15 | ### What have we done to make the software data protection friendly? 16 | 17 | This section describes the measures taken to integrate the requirements of the GDPR directly into product development. The technical measures described below follow a "privacy by design" approach. 18 | 19 | ## Authentication 20 | SharedAccessKey as part of the connection string or 21 | Device key and certificated. Depends how the device was registered in Azure IoT Hub 22 | Password control 23 | Customer can configure the local MQTT broker to require user/password authentication. The configuration is preinstalled on the device. 24 | Encryption 25 | Device-to-Cloud and Cloud-to-Device communication is done using MQTT over TLS. 26 | ## Deletion possibility 27 | Local data: The software may save data permanently in local virtual storage (eg when run in QEMU Emulator) or on local physical storage (SD-Card on Raspberry PI). All collected or processed data can be deleted by either deleting the virtual storage file (\*.qcow2), or by erasing the SD-Card. 28 | 29 | ## Cloud storage: The software may send data to cloud endpoints controlled by you or your organization. Examples include connectivity data, device identification, device health, device telemetry, application metrics and application logs. Collection and processing of example data on the device is enabled by default. Sending of device data to cloud endpoints must be explicitly enabled by performing the device provisioning process. The actual cloud endpoints are determined and configured during the device provisioning process. All collected or processed data can be deleted on the cloud side in the respective cloud endpoints. 30 | 31 | ## Vulnerabilities: The release process for this software is set up to always update to the newest package updates. The project will continuously release new versions of the software. To protect personal data, it is advisable to always use the latest version of the software. 32 | 33 | ### Important: When you use the self-update-agent within Eclipse Leda quickstart images for non-volatile setups, it is essential to reconfigure the system and harden it, this includes but is not limited to the following configuration items: 34 | 35 | Disable system user (root) password and login 36 | Disable SSH login with password 37 | Adding a new Linux user with restricted permissions 38 | Adding SSH key based authentication 39 | Kubernetes Resources: Secrets 40 | Device Identity Certificates for Cloud Connection 41 | Access credentials for private Container Registries 42 | -------------------------------------------------------------------------------- /docs/deploying/README.md: -------------------------------------------------------------------------------- 1 | # Deployment 2 | 3 | # Native 4 | 5 | For the native usage, no special deployment is needed. You can run and test the app as specified in [link](../building/README.md) (native part). 6 | 7 | # Container 8 | 9 | If you want to run the SUA as a service which can receive update requests any time the following [yaml](sua-service.yaml) is available. 10 | 11 | Apply the yaml config with... 12 | 13 | ``` 14 | kubectl apply -f sua-service.yaml 15 | ``` 16 | 17 | The yaml mounts system_bus_socket to allow access to the host dbus and a shared volume for holding the downloaded update bundle. 18 | 19 | By default the path for holding the downloaded update bundles is assumed to be /data/selfupdates. If you wish to choose another download location then update the yaml file (lines 21 & 40) with your desired path. 20 | 21 | There are also various port settings. You may need to adjust these values to match the setup that you are using. 22 | -------------------------------------------------------------------------------- /docs/deploying/sua-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: suaservice 5 | labels: 6 | app: suaservice 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: suaservice 12 | template: 13 | metadata: 14 | labels: 15 | app: suaservice 16 | spec: 17 | containers: 18 | - name: suaservice 19 | image: ghcr.io/eclipse-leda/leda-contrib-self-update-agent/self-update-agent:latest 20 | command: ["/app/sdv-self-update-agent"] 21 | args: ["-p", "/data/selfupdates"] 22 | imagePullPolicy: IfNotPresent 23 | volumeMounts: 24 | - mountPath: /var/run/dbus/system_bus_socket 25 | name: system-dbus-socket 26 | readOnly: true 27 | - mountPath: /RaucUpdate 28 | name: shared-rauc-vol 29 | ports: 30 | - name: default 31 | containerPort: 50052 32 | protocol: TCP 33 | imagePullSecrets: 34 | - name: ghcr-user 35 | volumes: 36 | - hostPath: 37 | path: /var/run/dbus/system_bus_socket 38 | name: system-dbus-socket 39 | - hostPath: 40 | path: /data/selfupdates 41 | name: shared-rauc-vol 42 | --- 43 | apiVersion: v1 44 | kind: Service 45 | metadata: 46 | name: suaservice-nodeport 47 | spec: 48 | type: NodePort 49 | selector: 50 | app: suaservice 51 | ports: 52 | - port: 50052 53 | targetPort: 50052 54 | nodePort: 30052 55 | -------------------------------------------------------------------------------- /docs/migration_guide.md: -------------------------------------------------------------------------------- 1 | # Migration guide from YAML payloads to JSON (bfb) protocol 2 | 3 | ## Background 4 | The communication protocol between SUA and backend was extended to support vehicle orchestration for large quantities of devices. Key differences are: 5 | * Transition from YAML to JSON 6 | * Fine-grained API (command-based approach instead of single-shot self-update) 7 | * Extension of statuses (for detailed reflection of success, progress or failure) 8 | Some fields are not relevant anymore meanwhile new were introduced. Below is a brief comparison between messages in YAML and in JSON format. 9 | 10 | ### Transition guide for current state message 11 | From the current state message in YAML payload there is only one field 'bundleVersion' which is relevant. The corresponding field in JSON variant is 'version' in 'softwareNodes' for the device image. *NOTE*: The actual name could differ depending on the distro configuration. 12 | ``` 13 | apiVersion: sdv.eclipse.org/v1 14 | kind: SelfUpdateBundle 15 | metadata: 16 | name: "self-update-bundle-example" 17 | spec: 18 | bundleVersion: 1.0 19 | ``` 20 | 21 | ``` 22 | { 23 | "timestamp": 42, 24 | "payload": { 25 | "softwareNodes": [ 26 | { 27 | "id": "self-update-agent", 28 | "version": "build-42", 29 | "name": "OTA NG Self Update Agent", 30 | "type": "APPLICATION" 31 | }, 32 | { 33 | "id": "self-update:leda-deviceimage", 34 | "version": "1.0", 35 | "name": "Official Leda device image", 36 | "type": "IMAGE" 37 | } 38 | ], 39 | "hardwareNodes": [], 40 | "associations": [ 41 | { 42 | "sourceId": "self-update-agent", 43 | "targetId": "self-update:leda-deviceimage" 44 | } 45 | ] 46 | } 47 | } 48 | ``` 49 | 50 | ### Transition guide for desired state message 51 | For the desired state message there are two relevant fields 'bundleDownloadUrl' and 'bundleVersion' for take-over. The corresponding values are 'version' under the 'components' and 'value' inside the object with 'key'='image'. *NOTE*: There could be multiple entries inside 'components' array and 'config' section. Your implementation has to search for 'os-image' and 'image' values respectively. 52 | ``` 53 | apiVersion: "sdv.eclipse.org/v1" 54 | kind: SelfUpdateBundle 55 | metadata: 56 | name: self-update-bundle-example 57 | spec: 58 | bundleDownloadUrl: http://url 59 | bundleName: arm64-bundle 60 | bundleTarget: base 61 | bundleVersion: 1.0 62 | ``` 63 | ``` 64 | { 65 | "activityId": "random-uuid-as-string", 66 | "timestamp": 123456789, 67 | "payload": { 68 | "domains": [ 69 | { 70 | "id": "self-update", 71 | "components": [ 72 | { 73 | "id": "os-image", 74 | "version": "1.1", 75 | "config": [ 76 | { 77 | "key": "image", 78 | "value": "http://example.com/downloads/os-image-1.1.bin" 79 | } 80 | ] 81 | } 82 | ] 83 | } 84 | ] 85 | } 86 | } 87 | ``` 88 | 89 | ### Transition guide for state feedback 90 | Below there is an example for state feedback ('techCode' is optional and available only in case of failure). For transition to JSON format all three fields from 'state' section are important (except 'techCode') and their corresponding places in JSON are 'status' and 'message' in sections 'payload' and 'actions', and 'progress' in section 'actions'. The detailed list of the available payload status and action status can be found in [link](docs/bfb.md) because for vehicle orchestration more sub-states were introduced for a fine-grained report of the self-update progress. 91 | ``` 92 | apiVersion: sdv.eclipse.org/v1 93 | kind: SelfUpdateBundle 94 | metadata: 95 | name: "self-update-bundle-example" 96 | spec: 97 | bundleDownloadUrl: "http://url" 98 | bundleName: "arm64-bundle" 99 | bundleTarget: base 100 | bundleVersion: 1.0 101 | state: 102 | message: Downloaded 10.0 MiB... 103 | name: downloading 104 | progress: 100 105 | techCode: 42 106 | ``` 107 | ``` 108 | { 109 | "activityId": "id", 110 | "timestamp": 42, 111 | "payload": { 112 | "status": "ACTIVATION_SUCCESS", 113 | "message": "Self-update agent has activated the new OS image.", 114 | "actions": [ 115 | { 116 | "component": { 117 | "id": "self-update:os-image", 118 | "version": "1.0" 119 | }, 120 | "status": "UPDATED", 121 | "progress": 0, 122 | "message": "Self-update agent has activated the new OS image." 123 | } 124 | ] 125 | } 126 | } 127 | ``` 128 | -------------------------------------------------------------------------------- /docs/testing/README.md: -------------------------------------------------------------------------------- 1 | # Testing 2 | 3 | In order to be able to test SUA, the proper test environment must be prepared, which would simulate/mock the components with which the SUA is interacting. Depending if the test is being done for the native version od containerized variant, the preparation steps will differ: 4 | 5 | # Native 6 | 7 | For the native variant all tools have to be installed on the host machine: 8 | 9 | ## Preconditions 10 | 11 | 1. Install the mosquitto server and client 12 | 13 | ``` 14 | sudo apt-get update 15 | sudo apt-get install mosquitto 16 | sudo apt-get install mosquitto-clients 17 | ``` 18 | 19 | 2. Ensure the python3 is installed. 20 | 21 | ## Start MQTT broker 22 | 23 | ``` 24 | mosquitto 25 | ``` 26 | 27 | ## Adjust the host ip in code 28 | 29 | Set the IP of the machine where the mosquitto broker is running, for the simplest case it would be the `localhost`: 30 | 31 | ``` 32 | SoftwareUpdateAgent::SoftwareUpdateAgent() 33 | { 34 | sua::MqttConfiguration conf; 35 | conf.brokerHost = "localhost" 36 | ``` 37 | 38 | ## Run native application 39 | 40 | ``` 41 | ./sdv-self-update-agent 42 | ``` 43 | 44 | ## Host test bundle file 45 | 46 | Ensure that the proper test bundle is put into hosted directory. Adjust the IP and Port as needed. 47 | 48 | ``` 49 | cd docs/testing/fileserver/bundle 50 | python3 -m http.server --bind 127.0.0.1 5555 51 | ``` 52 | 53 | ## Simulate sending the MQTT Start messages 54 | 55 | The content of the json file shall be adjusted, so that url of hosted bundle is valid. 56 | 57 | ``` 58 | mosquitto_pub -t "selfupdate/desiredstate" -f docs/testing/mqtt/start.json 59 | mosquitto_pub -t "selfupdate/desiredstate/command" -f docs/testing/mqtt/command-download.json 60 | ``` 61 | 62 | ## Subscribe to MQTT feedback messages 63 | 64 | To test if the SUA is behaving correctly and sending proper mqtt messages, subscribe to the topics: 65 | 66 | ``` 67 | mosquitto_sub -t "selfupdate/currentstate" 68 | mosquitto_sub -t "selfupdate/desiredstatefeedback" 69 | ``` 70 | 71 | ## Trigger current state request 72 | ``` 73 | mosquitto_pub -t "selfupdate/currentstate/get" -f docs/testing/mqtt/current-state-get.json 74 | ``` 75 | 76 | # Container 77 | 78 | For testing the container variant, it shall be ensured that all the components are connected to the same network and using proper ports mapping, so that they would be able to communicate with each other. 79 | 80 | ## Create the network 81 | 82 | ``` 83 | docker network create -d bridge my-network 84 | ``` 85 | 86 | ## Deploy Mosquitto broker 87 | 88 | ``` 89 | docker run -it --network=my-network -p 1883:1883 -v /absolute/path/sdv-self-update-agent/docs/testing/mqtt/mosquitto.conf:/mosquitto/config/mosquitto.conf -v /mosquitto/data -v /mosquitto/log --name mosquitto eclipse-mosquitto 90 | ``` 91 | 92 | Hint: it is required to use the absolute path to the mosquitto configuration file. 93 | 94 | ## Deploy python http server 95 | 96 | ``` 97 | cd docs/testing/fileserver 98 | docker build -t host . 99 | docker run -it --network=my-network -p 5555:5555 --name fileserver host 100 | ``` 101 | 102 | The bundle file will be available under: `fileserver:5555/bundle` url, so the value in `start.json` shall be adjusted. 103 | 104 | ## Deploy SUA 105 | 106 | ``` 107 | docker run -it --network=my-network sua:latest 108 | ``` 109 | 110 | Hint: before building and running sua, ensure that the broker host ia having the same value as the name parameter, specified for mosquitto broker: 111 | 112 | ``` 113 | SoftwareUpdateAgent::SoftwareUpdateAgent() 114 | { 115 | sua::MqttConfiguration conf; 116 | conf.brokerHost = "mosquitto" 117 | ... 118 | ``` 119 | 120 | # HowTo subscribe to relevant messages 121 | 122 | ``` 123 | mosquitto_sub -t "selfupdate/selfupdate/desiredstatefeedback" -h ipAddress_of_mosquitto_container 124 | mosquitto_sub -t "selfupdate/currentstate" -h ipAddress_of_mosquitto_container 125 | ``` 126 | 127 | From this tab console we can observe if the SUA is sending proper messages. 128 | 129 | Hint: to figure out the the ip address of mosquitto container, run following command: 130 | 131 | ``` 132 | docker container ls // copy the containerID of mosquitto container 133 | docker container inspect containerID 134 | ``` 135 | 136 | and locate the `IPAddress` value. 137 | 138 | # HowTo send the Start signal to trigger the process 139 | 140 | ``` 141 | mosquitto_pub -t "selfupdate/desiredstate" -f docs/testing/mqtt/start.json -h ipAddress_of_mosquitto_container 142 | ``` 143 | 144 | After sending this signal, you should be able to observe the SUA behavior on the console. 145 | -------------------------------------------------------------------------------- /docs/testing/fileserver/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:latest 2 | COPY bundle/ / 3 | EXPOSE 5555 4 | CMD python3 -m http.server 5555 5 | -------------------------------------------------------------------------------- /docs/testing/mqtt/command-activate.json: -------------------------------------------------------------------------------- 1 | { 2 | "activityId": "random-uuid-as-string", 3 | "timestamp": 123456789, 4 | "payload": { 5 | "baseline": "BASELINE NAME", 6 | "command": "ACTIVATE" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/testing/mqtt/command-cleanup.json: -------------------------------------------------------------------------------- 1 | { 2 | "activityId": "random-uuid-as-string", 3 | "timestamp": 123456789, 4 | "payload": { 5 | "baseline": "BASELINE NAME", 6 | "command": "CLEANUP" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/testing/mqtt/command-download.json: -------------------------------------------------------------------------------- 1 | { 2 | "activityId": "random-uuid-as-string", 3 | "timestamp": 123456789, 4 | "payload": { 5 | "baseline": "BASELINE NAME", 6 | "command": "DOWNLOAD" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/testing/mqtt/command-rollback.json: -------------------------------------------------------------------------------- 1 | { 2 | "activityId": "random-uuid-as-string", 3 | "timestamp": 123456789, 4 | "payload": { 5 | "baseline": "BASELINE NAME", 6 | "command": "ROLLBACK" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/testing/mqtt/command-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "activityId": "random-uuid-as-string", 3 | "timestamp": 123456789, 4 | "payload": { 5 | "baseline": "BASELINE NAME", 6 | "command": "UPDATE" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/testing/mqtt/current-state-get.json: -------------------------------------------------------------------------------- 1 | { 2 | "activityId": "random-uuid-as-string", 3 | "timestamp": 123456789 4 | } 5 | -------------------------------------------------------------------------------- /docs/testing/mqtt/mosquitto.conf: -------------------------------------------------------------------------------- 1 | persistence true 2 | persistence_location /mosquitto/data/ 3 | log_dest file /mosquitto/log/mosquitto.log 4 | allow_anonymous true 5 | listener 1883 0.0.0.0 -------------------------------------------------------------------------------- /docs/testing/mqtt/start.json: -------------------------------------------------------------------------------- 1 | { 2 | "activityId": "random-uuid-as-string", 3 | "timestamp": 123456789, 4 | "payload": { 5 | "domains": [ 6 | { 7 | "id": "self-update", 8 | "components": [ 9 | { 10 | "id": "os-image", 11 | "version": "v1beta3", 12 | "config": [ 13 | { 14 | "key": "image", 15 | "value": "http://127.0.0.1:5555/bundle" 16 | } 17 | ] 18 | } 19 | ] 20 | } 21 | ] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ "$1" = "amd64" ]; then 4 | ./scripts/build_openssl_amd64.sh 5 | ./scripts/build_glib_amd64.sh 6 | cd build_amd64 7 | cmake -DCMAKE_INSTALL_PREFIX=../dist_amd64 -DCMAKE_TOOLCHAIN_FILE=../cmake/linux/amd64/toolchain.cmake -DOPENSSL_ROOT_DIR=../build_amd64 -DOPENSSL_CRYPTO_LIBRARY=../build_amd64/lib/libcrypto.so .. 8 | make install 9 | exit 0 10 | fi 11 | 12 | if [ "$1" = "arm64" ]; then 13 | ./scripts/build_openssl_arm64.sh 14 | ./scripts/build_glib_arm64.sh 15 | cd build_arm64 16 | cmake -DCMAKE_INSTALL_PREFIX=../dist_arm64 -DCMAKE_TOOLCHAIN_FILE=../cmake/linux/arm64/toolchain.cmake -DOPENSSL_ROOT_DIR=../build_arm64 -DOPENSSL_CRYPTO_LIBRARY=../build_arm64/lib/libcrypto.so .. 17 | make install 18 | exit 0 19 | fi 20 | 21 | echo "Unknown architecture '$1'" 22 | 23 | -------------------------------------------------------------------------------- /scripts/build_glib_amd64.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd 3rdparty/glib 4 | meson subprojects download > /dev/null 5 | meson setup -Dwrap_mode=forcefallback ../../build_amd64/glib -Druntime_dir=/var/run > /dev/null 6 | meson compile -j `nproc` -C ../../build_amd64/glib > /dev/null 7 | -------------------------------------------------------------------------------- /scripts/build_glib_arm64.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd 3rdparty/glib 4 | meson subprojects download > /dev/null 5 | meson setup -Dwrap_mode=forcefallback -Druntime_dir=/var/run --cross-file=../meson-cross-file-aarch64.txt ../../build_arm64/glib > /dev/null 6 | meson compile -j `nproc` -C ../../build_arm64/glib > /dev/null 7 | -------------------------------------------------------------------------------- /scripts/build_openssl_amd64.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rootdir=`pwd` 4 | mkdir -p build_amd64/3rdparty/openssl 5 | cd build_amd64/3rdparty/openssl 6 | ./../../../3rdparty/openssl/Configure \ 7 | --prefix=$rootdir/build_amd64 \ 8 | --openssldir=$rootdir/build_amd64 \ 9 | --libdir=lib \ 10 | shared \ 11 | -Wl,-rpath=$rootdir/build_amd64/lib \ 12 | -Wl,--enable-new-dtags > /dev/null 13 | make -j > /dev/null 14 | make install_sw > /dev/null 15 | -------------------------------------------------------------------------------- /scripts/build_openssl_arm64.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rootdir=`pwd` 4 | mkdir -p build_arm64/3rdparty/openssl 5 | cd build_arm64/3rdparty/openssl 6 | CC=aarch64-linux-gnu-gcc-9 \ 7 | CXX=aarch64-linux-gnu-g++-9 \ 8 | LD=aarch64-linux-gnu-ld \ 9 | AR=aarch64-linux-gnu-ar \ 10 | RANLIB=aarch64-linux-gnu-ranlib \ 11 | ./../../../3rdparty/openssl/Configure linux-aarch64 \ 12 | --prefix=$rootdir/build_arm64 \ 13 | --openssldir=$rootdir/build_arm64 \ 14 | --libdir=lib \ 15 | shared \ 16 | -Wl,-rpath=$rootdir/build_arm64/lib \ 17 | -Wl,--enable-new-dtags > /dev/null 18 | make -j > /dev/null 19 | make install_sw > /dev/null 20 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(NOT DEFINED SUA_COMMIT_HASH) 2 | execute_process( 3 | COMMAND git describe --always --abbrev=40 --dirty 4 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE SUA_COMMIT_HASH 5 | ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE 6 | ) 7 | endif() 8 | 9 | if(NOT DEFINED SUA_BUILD_NUMBER) 10 | set(SUA_BUILD_NUMBER "local") 11 | endif() 12 | 13 | if(SUA_MEASURE_CODE_COVERAGE) 14 | add_compile_options( 15 | -fprofile-arcs 16 | -ftest-coverage 17 | -fPIC 18 | ) 19 | endif() 20 | 21 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/version.cpp @ONLY) 22 | 23 | include_directories( 24 | ${CMAKE_SOURCE_DIR}/src 25 | ${CMAKE_SOURCE_DIR}/3rdparty/spdlog/include 26 | ${CMAKE_SOURCE_DIR}/3rdparty/paho.mqtt.c/src 27 | ${CMAKE_SOURCE_DIR}/3rdparty/paho.mqtt.cpp/src 28 | ${CMAKE_SOURCE_DIR}/3rdparty/glib 29 | ${CMAKE_SOURCE_DIR}/3rdparty/glib/glib 30 | ${CMAKE_SOURCE_DIR}/3rdparty/glib/gmodule 31 | ${CMAKE_SOURCE_DIR}/3rdparty/glib/builddir/ 32 | ${CMAKE_SOURCE_DIR}/3rdparty/nlohmann-json/include 33 | ${CMAKE_SOURCE_DIR}/3rdparty/curl/include 34 | ${CMAKE_BINARY_DIR}/3rdparty/curl/lib 35 | ${CMAKE_BINARY_DIR}/glib 36 | ${CMAKE_BINARY_DIR}/glib/glib 37 | ) 38 | 39 | file(GLOB_RECURSE SRCS *.cpp *.h) 40 | list(FILTER SRCS EXCLUDE REGEX "main.cpp" ) 41 | list(APPEND SRCS ${CMAKE_CURRENT_BINARY_DIR}/version.cpp) 42 | 43 | add_library(sua SHARED ${SRCS}) 44 | set_target_properties(sua PROPERTIES LINK_FLAGS -s) 45 | 46 | add_executable(${PROJECT_NAME} ${SRCS} main.cpp) 47 | set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS -s) 48 | 49 | link_directories(${CMAKE_BINARY_DIR}/3rdparty/curl/lib) 50 | 51 | target_link_libraries(${PROJECT_NAME} 52 | curl_lib 53 | paho-mqttpp3 54 | paho-mqtt3a 55 | gio_lib 56 | gobject_lib 57 | gmodule_lib 58 | glib_lib 59 | ffi_lib 60 | z_lib 61 | intl_lib 62 | ${SUA_PLATFORM_LIBS} 63 | ssl_lib 64 | crypto_lib 65 | sua 66 | ) 67 | 68 | if(SUA_MEASURE_CODE_COVERAGE) 69 | target_link_libraries(sua gcov) 70 | endif() 71 | 72 | install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) 73 | install(TARGETS sua LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) 74 | 75 | -------------------------------------------------------------------------------- /src/Context.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_CONTEXT_H 18 | #define SDV_SUA_CONTEXT_H 19 | 20 | #include "FSM/FSM.h" 21 | #include "Download/IDownloader.h" 22 | #include "Install/IRaucInstaller.h" 23 | #include "Mqtt/IMqttMessagingProtocol.h" 24 | #include "Mqtt/IMqttProcessor.h" 25 | #include "Utils/IBundleChecker.h" 26 | #include "Defaults.h" 27 | 28 | #include 29 | 30 | namespace sua { 31 | 32 | struct DesiredState { 33 | std::string activityId; 34 | std::string bundleVersion; 35 | std::string bundleDownloadUrl; 36 | 37 | std::map metadata; 38 | 39 | uint64_t downloadBytesTotal = 0; 40 | uint64_t downloadBytesDownloaded = 0; 41 | int downloadProgressPercentage = 0; 42 | int installProgressPercentage = 0; 43 | 44 | std::string actionStatus = ""; 45 | std::string actionMessage = ""; 46 | }; 47 | 48 | struct CurrentState { 49 | std::string version; 50 | }; 51 | 52 | struct Context { 53 | std::shared_ptr stateMachine; 54 | std::shared_ptr downloaderAgent; 55 | std::shared_ptr installerAgent; 56 | std::shared_ptr messagingProtocol; 57 | std::shared_ptr mqttProcessor; 58 | std::shared_ptr bundleChecker; 59 | std::string updatesDirectory = SUA_DEFAULT_TEMP_DIRECTORY; 60 | std::string tempFileName = "/temp_file"; 61 | std::string caDirectory = SUA_DEFAULT_CA_DIRECTORY; 62 | std::string caFilepath = SUA_DEFAULT_CA_FILEPATH; 63 | bool downloadMode = true; 64 | bool fallbackMode = false; 65 | int feedbackInterval = SUA_DEFAULT_FEEDBACK_INTERVAL; // seconds 66 | 67 | DesiredState desiredState; 68 | CurrentState currentState; 69 | }; 70 | 71 | struct Command { 72 | std::string activityId; 73 | FotaEvent event; 74 | }; 75 | 76 | } // namespace sua 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /src/Defaults.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #include "Defaults.h" 18 | 19 | const std::string SUA_DEFAULT_MQTT_PROTOCOL = "tcp"; 20 | const std::string SUA_DEFAULT_MQTT_HOST = "mosquitto"; 21 | const int SUA_DEFAULT_MQTT_PORT = 1883; 22 | const std::string SUA_DEFAULT_MQTT_SERVER = "tcp://mosquitto:1883"; 23 | const std::string SUA_DEFAULT_MODE = "download"; 24 | const std::string SUA_DEFAULT_TEMP_DIRECTORY = "/data/selfupdates"; 25 | const std::string SUA_DEFAULT_CA_DIRECTORY = "/etc/ssl/certs"; 26 | const std::string SUA_DEFAULT_CA_FILEPATH = ""; 27 | const int SUA_DEFAULT_FEEDBACK_INTERVAL = 5; 28 | -------------------------------------------------------------------------------- /src/Defaults.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_DEFAULTS_H 18 | #define SDV_SUA_DEFAULTS_H 19 | 20 | #include 21 | 22 | extern const std::string SUA_DEFAULT_MQTT_PROTOCOL; 23 | extern const std::string SUA_DEFAULT_MQTT_HOST; 24 | extern const int SUA_DEFAULT_MQTT_PORT; 25 | extern const std::string SUA_DEFAULT_MQTT_SERVER; 26 | extern const std::string SUA_DEFAULT_MODE; 27 | extern const std::string SUA_DEFAULT_TEMP_DIRECTORY; 28 | extern const std::string SUA_DEFAULT_CA_DIRECTORY; 29 | extern const std::string SUA_DEFAULT_CA_FILEPATH; 30 | extern const int SUA_DEFAULT_FEEDBACK_INTERVAL; // seconds 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/Download/Downloader.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_DOWNLOADER_H 18 | #define SDV_SUA_DOWNLOADER_H 19 | 20 | #include "IDownloader.h" 21 | 22 | namespace sua { 23 | 24 | class Downloader : public IDownloader 25 | { 26 | public: 27 | Downloader(class Context & context); 28 | 29 | static const std::string EVENT_DOWNLOADING; 30 | 31 | DownloadResult start(const std::string & input) override; 32 | 33 | private: 34 | class Context & _context; 35 | }; 36 | 37 | } // namespace sua 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/Download/IDownloader.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_IDOWNLOADER_H 18 | #define SDV_SUA_IDOWNLOADER_H 19 | 20 | #include "TechCodes.h" 21 | 22 | #include 23 | #include 24 | 25 | namespace sua { 26 | 27 | using DownloadResult = std::tuple; 28 | 29 | class IDownloader { 30 | public: 31 | virtual ~IDownloader() = default; 32 | 33 | virtual DownloadResult start(const std::string & input) = 0; 34 | }; 35 | 36 | } // namespace sua 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /src/FSM/FSM.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #include "FSM/FSM.h" 18 | #include "FSM/States/Uninitialized.h" 19 | #include "Logger.h" 20 | 21 | namespace sua { 22 | 23 | FSM::FSM(Context & context) 24 | : _context(context) 25 | { } 26 | 27 | void FSM::transitTo(const std::string& name) 28 | { 29 | assert(_factory != nullptr); 30 | 31 | if(_currentState) { 32 | Logger::trace("Leave state '{}'", _currentState->name()); 33 | _currentState->onLeave(_context); 34 | } 35 | 36 | _currentState = _factory->createState(name); 37 | 38 | Logger::trace("Enter state '{}'", _currentState->name()); 39 | _currentState->onEnter(_context); 40 | } 41 | 42 | std::string FSM::activeState() const 43 | { 44 | assert(_currentState != nullptr); 45 | 46 | return _currentState->name(); 47 | } 48 | 49 | void FSM::setFactory(std::shared_ptr factory) 50 | { 51 | _factory = factory; 52 | } 53 | 54 | void FSM::setTransitions(std::initializer_list table) 55 | { 56 | _transitions = table; 57 | } 58 | 59 | void FSM::handleEvent(const FotaEvent e) 60 | { 61 | Logger::trace("Received event '{}'", toString(e)); 62 | 63 | FotaEvent output = FotaEvent::NotUsed; 64 | bool output_set = false; 65 | 66 | for(const auto& t : _transitions) { 67 | if(t.from != activeState()) { 68 | continue; 69 | } 70 | 71 | if(t.when != e) { 72 | continue; 73 | } 74 | 75 | if(t.output == FotaEvent::NotUsed) { 76 | if(activeState() != t.to) { 77 | transitTo(t.to); 78 | } 79 | break; 80 | } 81 | 82 | if(!output_set) { 83 | output = _currentState->body(_context); 84 | output_set = true; 85 | } 86 | 87 | if(t.output == output) { 88 | if(activeState() != t.to) { 89 | transitTo(t.to); 90 | } 91 | break; 92 | } 93 | } 94 | } 95 | 96 | } // namespace sua 97 | -------------------------------------------------------------------------------- /src/FSM/FSM.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_FSM_H 18 | #define SDV_SUA_FSM_H 19 | 20 | #include "StateFactory.h" 21 | #include "FotaEvent.h" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | namespace sua { 29 | 30 | struct FSMTransition { 31 | FotaEvent when; 32 | std::string from; 33 | std::string to; 34 | FotaEvent output = FotaEvent::NotUsed; 35 | }; 36 | 37 | class FSM { 38 | public: 39 | FSM(class Context & context); 40 | 41 | void handleEvent(FotaEvent e); 42 | 43 | virtual void transitTo(const std::string& name); 44 | std::string activeState() const; 45 | 46 | void setFactory(std::shared_ptr factory); 47 | void setTransitions(std::initializer_list table); 48 | 49 | private: 50 | Context & _context; 51 | 52 | std::shared_ptr _currentState; 53 | std::shared_ptr _factory; 54 | std::vector _transitions; 55 | }; 56 | } // namespace sua 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/FSM/State.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #include "FSM/State.h" 18 | #include "Context.h" 19 | 20 | namespace sua { 21 | 22 | State::State(const std::string& name) 23 | : _name(name) 24 | { } 25 | 26 | const std::string& State::name() const 27 | { 28 | return _name; 29 | } 30 | 31 | FotaEvent State::body(Context& /*ctx*/) 32 | { 33 | return FotaEvent::NotUsed; 34 | } 35 | 36 | void State::send(Context& ctx, const std::string& topic, MqttMessage message_type, bool retained) 37 | { 38 | ctx.mqttProcessor->send(topic, message_type, "", retained); 39 | } 40 | 41 | void State::send(Context& ctx, const std::string& topic, MqttMessage message_type, const std::string& message, bool retained) 42 | { 43 | ctx.mqttProcessor->send(topic, message_type, message, retained); 44 | } 45 | 46 | } // namespace sua 47 | -------------------------------------------------------------------------------- /src/FSM/State.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_STATE_H 18 | #define SDV_SUA_STATE_H 19 | 20 | #include "FotaEvent.h" 21 | #include "Mqtt/MqttMessage.h" 22 | 23 | namespace sua { 24 | 25 | class Context; 26 | 27 | class State { 28 | public: 29 | State(const std::string& name = ""); 30 | 31 | virtual ~State() = default; 32 | 33 | const std::string& name() const; 34 | 35 | virtual void onEnter(Context& ctx) {} 36 | virtual void onLeave(Context& ctx) {} 37 | 38 | virtual FotaEvent body(Context& ctx); 39 | 40 | void send(Context& ctx, const std::string& topic, MqttMessage message_type, bool retained = false); 41 | void send(Context& ctx, const std::string& topic, MqttMessage message_type, const std::string& message, bool retained = false); 42 | 43 | private: 44 | std::string _name; 45 | }; 46 | 47 | } // namespace sua 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/FSM/StateFactory.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #include "StateFactory.h" 18 | #include "State.h" 19 | 20 | #include 21 | 22 | namespace sua { 23 | 24 | std::shared_ptr StateFactory::createState(const std::string& name) 25 | { 26 | auto it = _states.find(name); 27 | 28 | if(it == _states.end()) { 29 | throw std::logic_error("Unknown state '" + name + "'"); 30 | } 31 | 32 | return it->second(); 33 | } 34 | 35 | } // namespace sua 36 | -------------------------------------------------------------------------------- /src/FSM/StateFactory.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_STATEFACTORY_H 18 | #define SDV_SUA_STATEFACTORY_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | namespace sua { 25 | 26 | class State; 27 | 28 | class StateFactory { 29 | public: 30 | template 31 | void addStateT(const std::string& name) 32 | { 33 | _states[name] = []() -> std::shared_ptr { return std::make_shared(); }; 34 | } 35 | 36 | std::shared_ptr createState(const std::string& name); 37 | 38 | private: 39 | std::map()>> _states; 40 | }; 41 | 42 | } // namespace sua 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/FSM/States/Activating.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #include "FSM/States/Activating.h" 18 | #include "Context.h" 19 | #include "Logger.h" 20 | 21 | namespace sua { 22 | 23 | Activating::Activating() 24 | : Connected("Activating") 25 | { } 26 | 27 | void Activating::onEnter(Context& ctx) 28 | { 29 | send(ctx, IMqttProcessor::TOPIC_FEEDBACK, MqttMessage::Activating); 30 | 31 | auto slots = ctx.installerAgent->getSlotStatus(); 32 | for(auto it : slots) { 33 | Logger::info("Slot '{}': state='{}' version='{}'", it.first, it.second["state"], it.second["version"]); 34 | } 35 | 36 | if(ctx.installerAgent->activateOther() == TechCode::OK) { 37 | ctx.desiredState.actionStatus = "UPDATE_SUCCESS"; 38 | ctx.desiredState.actionMessage = "Self-update agent has activated the new OS image."; 39 | send(ctx, IMqttProcessor::TOPIC_FEEDBACK, MqttMessage::Activated); 40 | } else { 41 | ctx.desiredState.actionStatus = "UPDATE_FAILURE"; 42 | ctx.desiredState.actionMessage = "Self-update agent failed to activate the new OS image."; 43 | send(ctx, IMqttProcessor::TOPIC_FEEDBACK, MqttMessage::ActivationFailed); 44 | } 45 | } 46 | 47 | } // namespace sua 48 | -------------------------------------------------------------------------------- /src/FSM/States/Activating.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_ACTIVATING_H 18 | #define SDV_SUA_ACTIVATING_H 19 | 20 | #include "FSM/States/Connected.h" 21 | 22 | namespace sua { 23 | 24 | class Activating : public Connected { 25 | public: 26 | Activating(); 27 | 28 | void onEnter(Context& /*ctx*/) override; 29 | }; 30 | 31 | } // namespace sua 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/FSM/States/Cleaning.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #include "FSM/States/Cleaning.h" 18 | #include "Context.h" 19 | #include "Logger.h" 20 | 21 | namespace sua { 22 | 23 | Cleaning::Cleaning() 24 | : Connected("Cleaning") 25 | { } 26 | 27 | void Cleaning::onEnter(Context& ctx) 28 | { 29 | const auto path = ctx.updatesDirectory + ctx.tempFileName; 30 | const auto result = remove(path.c_str()); 31 | 32 | if(result != 0) { 33 | Logger::error("Failed to remove temporary bundle file: '{}', reason: '{}'", path, strerror(errno)); 34 | } 35 | 36 | send(ctx, IMqttProcessor::TOPIC_FEEDBACK, MqttMessage::Cleaned); 37 | 38 | if(ctx.desiredState.actionStatus == "UPDATE_SUCCESS") { 39 | send(ctx, IMqttProcessor::TOPIC_FEEDBACK, MqttMessage::Complete); 40 | } else { 41 | send(ctx, IMqttProcessor::TOPIC_FEEDBACK, MqttMessage::Incomplete); 42 | } 43 | 44 | ctx.stateMachine->handleEvent(FotaEvent::Waiting); 45 | } 46 | 47 | } // namespace sua 48 | -------------------------------------------------------------------------------- /src/FSM/States/Cleaning.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_CLEANING_H 18 | #define SDV_SUA_CLEANING_H 19 | 20 | #include "FSM/States/Connected.h" 21 | 22 | namespace sua { 23 | 24 | class Cleaning : public Connected { 25 | public: 26 | Cleaning(); 27 | 28 | void onEnter(Context& /*ctx*/) override; 29 | }; 30 | 31 | } // namespace sua 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/FSM/States/Connected.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #include "FSM/States/Connected.h" 18 | #include "FSM/FSM.h" 19 | #include "Context.h" 20 | #include "FotaEvent.h" 21 | #include "Logger.h" 22 | 23 | namespace sua { 24 | 25 | Connected::Connected() 26 | : Connected("Connected") 27 | { } 28 | 29 | Connected::Connected(const std::string& name) 30 | : State(name) 31 | { } 32 | 33 | void Connected::onEnter(Context& ctx) 34 | { 35 | send(ctx, IMqttProcessor::TOPIC_STATE, MqttMessage::SystemVersion, true); 36 | } 37 | 38 | FotaEvent Connected::body(Context& ctx) 39 | { 40 | Logger::info("System version, installed: '{}'", ctx.currentState.version); 41 | 42 | send(ctx, IMqttProcessor::TOPIC_FEEDBACK, MqttMessage::Identifying); 43 | 44 | if(ctx.bundleChecker->isUpdateBundleVersionDifferent(ctx.desiredState.bundleVersion, 45 | ctx.installerAgent)) { 46 | Logger::info("Bundle file and slot versions differ."); 47 | send(ctx, IMqttProcessor::TOPIC_FEEDBACK, MqttMessage::Identified); 48 | return FotaEvent::BundleVersionOK; 49 | } 50 | 51 | Logger::info("Bundle version unchanged"); 52 | send(ctx, IMqttProcessor::TOPIC_FEEDBACK, MqttMessage::Skipped); 53 | return FotaEvent::BundleVersionUnchanged; 54 | } 55 | 56 | } // namespace sua 57 | -------------------------------------------------------------------------------- /src/FSM/States/Connected.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_CONNECTED_H 18 | #define SDV_SUA_CONNECTED_H 19 | 20 | #include "FSM/State.h" 21 | 22 | namespace sua { 23 | 24 | class Connected : public State { 25 | public: 26 | Connected(); 27 | Connected(const std::string& name); 28 | 29 | void onEnter(Context& ctx) override; 30 | 31 | FotaEvent body(class Context& ctx) override; 32 | }; 33 | 34 | } // namespace sua 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/FSM/States/Downloading.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #include "FSM/States/Downloading.h" 18 | #include "Download/Downloader.h" 19 | #include "Mqtt/IMqttMessagingProtocol.h" 20 | #include "TechCodes.h" 21 | #include "Context.h" 22 | #include "FotaEvent.h" 23 | #include "Logger.h" 24 | 25 | #include 26 | 27 | namespace sua { 28 | 29 | Downloading::Downloading() 30 | : State("Downloading") 31 | { } 32 | 33 | void Downloading::onEnter(Context& ctx) 34 | { 35 | ctx.desiredState.downloadBytesTotal = 0; 36 | ctx.desiredState.downloadBytesDownloaded = 0; 37 | ctx.desiredState.downloadProgressPercentage = 0; 38 | 39 | ctx.desiredState.actionStatus = ""; 40 | ctx.desiredState.actionMessage = ""; 41 | 42 | if(ctx.fallbackMode) { 43 | ctx.stateMachine->handleEvent(FotaEvent::DownloadStart); 44 | } 45 | } 46 | 47 | FotaEvent Downloading::body(Context& ctx) 48 | { 49 | subscribe(Downloader::EVENT_DOWNLOADING, [this, &ctx](const std::map& payload) { 50 | ctx.desiredState.downloadBytesTotal = std::stoll(payload.at("total")); 51 | ctx.desiredState.downloadBytesDownloaded = std::stoll(payload.at("downloaded")); 52 | ctx.desiredState.downloadProgressPercentage = std::stoi(payload.at("percentage")); 53 | 54 | const auto now = std::chrono::system_clock::now().time_since_epoch(); 55 | const auto now_in_seconds = std::chrono::duration_cast(now).count(); 56 | 57 | Logger::info("Download progress: {}%", ctx.desiredState.downloadProgressPercentage); 58 | 59 | if((ctx.desiredState.downloadProgressPercentage == 100) || (now_in_seconds - _timeLastUpdate) >= ctx.feedbackInterval) { 60 | send(ctx, IMqttProcessor::TOPIC_FEEDBACK, MqttMessage::Downloading); 61 | _timeLastUpdate = now_in_seconds; 62 | } 63 | }); 64 | 65 | if (true == ctx.downloadMode) { 66 | Logger::info("Downloading bundle: '{}'", ctx.desiredState.bundleDownloadUrl); 67 | const auto result = ctx.downloaderAgent->start(ctx.desiredState.bundleDownloadUrl); 68 | 69 | if(std::get<0>(result) == TechCode::OK) { 70 | Logger::info("Download progress: 100%"); 71 | send(ctx, IMqttProcessor::TOPIC_FEEDBACK, MqttMessage::Downloaded); 72 | 73 | const std::string pathDownloadedFile = ctx.updatesDirectory + ctx.tempFileName; 74 | Logger::trace("Downloaded bundle file should exist now as '{}'", pathDownloadedFile); 75 | 76 | return FotaEvent::DownloadSucceeded; 77 | } else { 78 | Logger::error("Download failed."); 79 | ctx.desiredState.actionStatus = "DOWNLOAD_FAILURE"; 80 | ctx.desiredState.actionMessage = "Download failed: " + std::get<1>(result); 81 | send(ctx, IMqttProcessor::TOPIC_FEEDBACK, MqttMessage::DownloadFailed); 82 | return FotaEvent::DownloadFailed; 83 | } 84 | } else { 85 | Logger::info("Checking bundle version directly in: '{}'", ctx.desiredState.bundleDownloadUrl); 86 | 87 | if(ctx.bundleChecker->isBundleVersionConsistent( 88 | ctx.desiredState.bundleVersion, ctx.installerAgent, ctx.desiredState.bundleDownloadUrl)) { 89 | Logger::info("Bundle version matches spec."); 90 | return FotaEvent::BundleVersionOK; 91 | } else { 92 | Logger::info("Bundle version does not match spec."); 93 | ctx.desiredState.actionStatus = "DOWNLOAD_FAILURE"; 94 | ctx.desiredState.actionMessage = "Bundle version does not match spec."; 95 | send(ctx, IMqttProcessor::TOPIC_FEEDBACK, MqttMessage::Rejected); 96 | return FotaEvent::BundleVersionInconsistent; 97 | } 98 | } 99 | } 100 | 101 | } // namespace sua 102 | -------------------------------------------------------------------------------- /src/FSM/States/Downloading.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_DOWNLOADING_H 18 | #define SDV_SUA_DOWNLOADING_H 19 | 20 | #include "FSM/State.h" 21 | #include "Patterns/Dispatcher.h" 22 | 23 | namespace sua { 24 | 25 | class Downloading 26 | : public State 27 | , public DispatcherSubscriber 28 | { 29 | public: 30 | Downloading(); 31 | 32 | void onEnter(Context& ctx) override; 33 | 34 | FotaEvent body(Context& ctx) override; 35 | 36 | private: 37 | long long _timeLastUpdate = 0; 38 | }; 39 | 40 | } // namespace sua 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/FSM/States/Failed.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #include "FSM/States/Failed.h" 18 | #include "FSM/FSM.h" 19 | #include "Context.h" 20 | #include "FotaEvent.h" 21 | 22 | namespace sua { 23 | 24 | Failed::Failed() 25 | : State("Failed") 26 | { } 27 | 28 | void Failed::onEnter(Context& ctx) 29 | { } 30 | 31 | } // namespace sua 32 | -------------------------------------------------------------------------------- /src/FSM/States/Failed.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_FAILED_H 18 | #define SDV_SUA_FAILED_H 19 | 20 | #include "FSM/State.h" 21 | 22 | namespace sua { 23 | 24 | class Failed : public State { 25 | public: 26 | Failed(); 27 | 28 | void onEnter(Context& ctx) override; 29 | }; 30 | 31 | } // namespace sua 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/FSM/States/Idle.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #include "FSM/States/Idle.h" 18 | #include "FSM/FSM.h" 19 | #include "Context.h" 20 | #include "FotaEvent.h" 21 | #include "Logger.h" 22 | 23 | namespace sua { 24 | 25 | Idle::Idle() 26 | : Connected("Idle") 27 | { } 28 | 29 | void Idle::onEnter(Context& /*ctx*/) 30 | { 31 | // Do not send current version (as it was sent from 'Connected' state) 32 | } 33 | 34 | } // namespace sua 35 | -------------------------------------------------------------------------------- /src/FSM/States/Idle.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_IDLE_H 18 | #define SDV_SUA_IDLE_H 19 | 20 | #include "FSM/States/Connected.h" 21 | 22 | namespace sua { 23 | 24 | class Idle : public Connected { 25 | public: 26 | Idle(); 27 | 28 | void onEnter(Context& /*ctx*/) override; 29 | }; 30 | 31 | } // namespace sua 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/FSM/States/Installed.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #include "FSM/States/Installed.h" 18 | #include "FSM/FSM.h" 19 | #include "Context.h" 20 | #include "FotaEvent.h" 21 | #include "Logger.h" 22 | 23 | namespace sua { 24 | 25 | void Installed::onEnter(Context& ctx) 26 | { 27 | // RAUC automatically activates 'other' partition for next boot after install 28 | // Therefore here we deactivate 'other' and activate current 'booted' and 29 | // wait for activation command 30 | ctx.installerAgent->activateBooted(); 31 | 32 | ctx.currentState.version = ctx.desiredState.bundleVersion; 33 | 34 | Logger::info("System version, installed: '{}'", ctx.currentState.version); 35 | send(ctx, IMqttProcessor::TOPIC_FEEDBACK, MqttMessage::CurrentState); 36 | } 37 | 38 | } // namespace sua 39 | -------------------------------------------------------------------------------- /src/FSM/States/Installed.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_INSTALLED_H 18 | #define SDV_SUA_INSTALLED_H 19 | 20 | #include "FSM/State.h" 21 | 22 | namespace sua { 23 | 24 | class Installed : public State { 25 | public: 26 | Installed() 27 | : State("Installed") 28 | { } 29 | 30 | void onEnter(Context& ctx) override; 31 | }; 32 | 33 | } // namespace sua 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/FSM/States/Installing.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #include "FSM/States/Installing.h" 18 | #include "FSM/FSM.h" 19 | #include "FotaEvent.h" 20 | #include "Install/Installer.h" 21 | #include "Context.h" 22 | #include "Logger.h" 23 | 24 | #include 25 | 26 | namespace sua { 27 | 28 | Installing::Installing() 29 | : State("Installing") 30 | { } 31 | 32 | void Installing::onEnter(Context& ctx) 33 | { 34 | ctx.desiredState.installProgressPercentage = 0; 35 | } 36 | 37 | FotaEvent Installing::body(Context& ctx) 38 | { 39 | subscribe(Installer::EVENT_INSTALLING, [this, &ctx](const std::map& payload) { 40 | ctx.desiredState.installProgressPercentage = std::stoi(payload.at("percentage")); 41 | Logger::info("Install progress: {}%", ctx.desiredState.installProgressPercentage); 42 | 43 | const auto now = std::chrono::system_clock::now().time_since_epoch(); 44 | const auto now_in_seconds = std::chrono::duration_cast(now).count(); 45 | 46 | if((ctx.desiredState.installProgressPercentage == 100) || (now_in_seconds - _timeLastUpdate) >= ctx.feedbackInterval) { 47 | send(ctx, IMqttProcessor::TOPIC_FEEDBACK, MqttMessage::Installing); 48 | _timeLastUpdate = now_in_seconds; 49 | } 50 | }); 51 | 52 | std::string install_input; 53 | if (true == ctx.downloadMode) 54 | { 55 | install_input = ctx.updatesDirectory + ctx.tempFileName; 56 | send(ctx, IMqttProcessor::TOPIC_FEEDBACK, MqttMessage::VersionChecking); 57 | 58 | if(ctx.bundleChecker->isBundleVersionConsistent( 59 | ctx.desiredState.bundleVersion, ctx.installerAgent, install_input)) { 60 | Logger::info("Downloaded bundle version matches spec."); 61 | } else { 62 | Logger::info("Downloaded bundle version does not match spec."); 63 | ctx.desiredState.actionStatus = "UPDATE_FAILURE"; 64 | ctx.desiredState.actionMessage = 65 | "Bundle version does not match version in desired state request."; 66 | send(ctx, IMqttProcessor::TOPIC_FEEDBACK, MqttMessage::Rejected); 67 | return FotaEvent::BundleVersionInconsistent; 68 | } 69 | } 70 | else 71 | { 72 | // installation streamed via download URL 73 | install_input = ctx.desiredState.bundleDownloadUrl; 74 | } 75 | 76 | auto installer = Installer(ctx.installerAgent); 77 | const auto result = installer.start(install_input); 78 | 79 | if(result == TechCode::OK) { 80 | Logger::info("Installation completed"); 81 | send(ctx, IMqttProcessor::TOPIC_FEEDBACK, MqttMessage::Installed); 82 | 83 | if(ctx.fallbackMode) { 84 | ctx.downloadMode = false; 85 | ctx.fallbackMode = false; 86 | } 87 | 88 | return FotaEvent::InstallCompleted; 89 | } 90 | 91 | const auto lastError = ctx.installerAgent->getLastError(); 92 | Logger::error("Installation failed: {}", lastError); 93 | 94 | // for download mode transit to fail state 95 | if (true == ctx.downloadMode) { 96 | send(ctx, IMqttProcessor::TOPIC_FEEDBACK, MqttMessage::InstallFailed, lastError); 97 | 98 | if(ctx.fallbackMode) { 99 | ctx.downloadMode = false; 100 | ctx.fallbackMode = false; 101 | } 102 | 103 | ctx.desiredState.actionStatus = "UPDATE_FAILURE"; 104 | ctx.desiredState.actionMessage = lastError; 105 | 106 | return FotaEvent::InstallFailed; 107 | } 108 | 109 | // for stream mode start again in download mode 110 | Logger::info("Trying normal download mode as fallback"); 111 | send(ctx, IMqttProcessor::TOPIC_FEEDBACK, MqttMessage::InstallFailedFallback, lastError); 112 | ctx.downloadMode = true; 113 | ctx.fallbackMode = true; 114 | return FotaEvent::InstallFailedFallback; 115 | } 116 | 117 | } // namespace sua 118 | -------------------------------------------------------------------------------- /src/FSM/States/Installing.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_INSTALLING_H 18 | #define SDV_SUA_INSTALLING_H 19 | 20 | #include "FSM/State.h" 21 | #include "Patterns/Dispatcher.h" 22 | 23 | namespace sua { 24 | 25 | class Installing 26 | : public State 27 | , public DispatcherSubscriber 28 | { 29 | public: 30 | Installing(); 31 | 32 | void onEnter(Context& ctx) override; 33 | 34 | FotaEvent body(Context& ctx) override; 35 | 36 | private: 37 | long long _timeLastUpdate = 0; 38 | }; 39 | 40 | } // namespace sua 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/FSM/States/SendCurrentState.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #include "FSM/States/SendCurrentState.h" 18 | #include "FSM/FSM.h" 19 | #include "Context.h" 20 | #include "FotaEvent.h" 21 | 22 | namespace sua { 23 | 24 | SendCurrentState::SendCurrentState() 25 | : SendCurrentState("SendCurrentState") 26 | { } 27 | 28 | SendCurrentState::SendCurrentState(const std::string& name) 29 | : State(name) 30 | { } 31 | 32 | void SendCurrentState::onEnter(Context& ctx) 33 | { 34 | send(ctx, IMqttProcessor::TOPIC_STATE, MqttMessage::SystemVersion, true); 35 | ctx.stateMachine->handleEvent(FotaEvent::Waiting); 36 | } 37 | 38 | } // namespace sua 39 | -------------------------------------------------------------------------------- /src/FSM/States/SendCurrentState.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_SENDCURRENTSTATE_H 18 | #define SDV_SUA_SENDCURRENTSTATE_H 19 | 20 | #include "FSM/State.h" 21 | 22 | namespace sua { 23 | 24 | class SendCurrentState : public State { 25 | public: 26 | SendCurrentState(); 27 | SendCurrentState(const std::string& name); 28 | 29 | void onEnter(Context& ctx) override; 30 | }; 31 | 32 | } // namespace sua 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/FSM/States/Uninitialized.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #include "FSM/States/Uninitialized.h" 18 | #include "Context.h" 19 | #include "Logger.h" 20 | 21 | namespace sua { 22 | 23 | Uninitialized::Uninitialized() 24 | : State("Uninitialized") 25 | { } 26 | 27 | void Uninitialized::onEnter(Context& ctx) 28 | { 29 | ctx.currentState.version = ctx.installerAgent->getBootedVersion(); 30 | Logger::info("System version (slot): '{}'", ctx.currentState.version); 31 | } 32 | 33 | } // namespace sua 34 | -------------------------------------------------------------------------------- /src/FSM/States/Uninitialized.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_UNINITIALIZED_H 18 | #define SDV_SUA_UNINITIALIZED_H 19 | 20 | #include "FSM/State.h" 21 | 22 | namespace sua { 23 | 24 | class Uninitialized : public State { 25 | public: 26 | Uninitialized(); 27 | 28 | void onEnter(Context& ctx) override; 29 | }; 30 | 31 | } // namespace sua 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/FotaEvent.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #include "FotaEvent.h" 18 | 19 | #include 20 | 21 | namespace sua { 22 | 23 | std::string toString(const FotaEvent e) 24 | { 25 | // clang-format off 26 | static const std::map names = { 27 | { Waiting , "Waiting" }, 28 | { ConnectivityEstablished , "ConnectivityEstablished" }, 29 | { ConnectivityLost , "ConnectivityLost" }, 30 | { Identify , "Identify" }, 31 | { GetCurrentState , "GetCurrentState" }, 32 | { BundleVersionOK , "BundleVersionOK" }, 33 | { BundleVersionUnchanged , "BundleVersionUnchanged" }, 34 | { BundleVersionInconsistent , "BundleVersionInconsistent" }, 35 | { DownloadStart , "DownloadStart" }, 36 | { DownloadFailed , "DownloadFailed" }, 37 | { DownloadSucceeded , "DownloadSucceeded" }, 38 | { InstallStart , "InstallStart" }, 39 | { InstallCompleted , "InstallCompleted" }, 40 | { InstallFailed , "InstallFailed" }, 41 | { InstallFailedFallback , "InstallFailedFallback" }, 42 | { Activate , "Activate" }, 43 | { Cleanup , "Cleanup" }, 44 | { Rollback , "Rollback" } 45 | }; 46 | // clang-format on 47 | 48 | return names.at(e); 49 | } 50 | 51 | } // namespace sua 52 | -------------------------------------------------------------------------------- /src/FotaEvent.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_FOTAEVENT_H 18 | #define SDV_SUA_FOTAEVENT_H 19 | 20 | #include 21 | 22 | namespace sua { 23 | 24 | enum FotaEvent { 25 | Waiting, 26 | ConnectivityEstablished, 27 | ConnectivityLost, 28 | Identify, 29 | GetCurrentState, 30 | BundleVersionOK, 31 | BundleVersionUnchanged, 32 | BundleVersionInconsistent, 33 | DownloadStart, 34 | DownloadFailed, 35 | DownloadSucceeded, 36 | InstallStart, 37 | InstallCompleted, 38 | InstallFailed, 39 | InstallFailedFallback, 40 | Activate, 41 | Cleanup, 42 | Rollback, 43 | NotUsed, 44 | }; 45 | 46 | std::string toString(FotaEvent e); 47 | 48 | } // namespace sua 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /src/Install/DBusRaucInstaller.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_DBUSRAUCINSTALLER_H 18 | #define SDV_SUA_DBUSRAUCINSTALLER_H 19 | 20 | #include "Install/IRaucInstaller.h" 21 | 22 | #include 23 | #include 24 | 25 | namespace sua { 26 | 27 | class DBusRaucInstaller : public IRaucInstaller { 28 | public: 29 | DBusRaucInstaller(); 30 | ~DBusRaucInstaller(); 31 | 32 | TechCode activateBooted() override; 33 | TechCode activateOther() override; 34 | TechCode installBundle(const std::string& input) override; 35 | int32_t getProgressPollInterval() const override; 36 | int32_t getInstallProgress() override; 37 | SlotStatus getSlotStatus() override; 38 | std::string getBootedVersion() override; 39 | std::string getBundleVersion(const std::string& input) override; 40 | std::string getLastError() override; 41 | 42 | bool installing() override; 43 | bool succeeded() override; 44 | 45 | void setInstalling(bool value); 46 | void setSuccess(bool value); 47 | 48 | private: 49 | GDBusConnection* connection{nullptr}; 50 | GMainLoop* loop{nullptr}; 51 | 52 | guint signalSubscriptionIdProperties; 53 | guint signalSubscriptionIdCompleted; 54 | 55 | std::atomic_bool is_installing; 56 | std::atomic_bool is_succeeded; 57 | 58 | void setupDBusRaucConnection(); 59 | void subscribeDBusSignals(); 60 | void unsubscribeDBusSignals(); 61 | 62 | TechCode callDBusRaucMark(const std::string& identifier, const std::string& state); 63 | TechCode callDBusRaucInstallBundle(const std::string& bundleName); 64 | int32_t getDBusRaucInstallProgress(); 65 | std::string getDBusRaucProperty(const gchar* propertyKey) const; 66 | SlotStatus getDBusRaucSlotStatus() const; 67 | std::string getDBusRaucBundleVersion(const std::string& input) const; 68 | std::string getOsVersionId(const std::string & version_key) const; 69 | }; 70 | 71 | } // namespace sua 72 | 73 | #endif // SDV_SUA_DBUSRAUCINSTALLER_H 74 | -------------------------------------------------------------------------------- /src/Install/DummyRaucInstaller.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #include "Install/DummyRaucInstaller.h" 18 | 19 | namespace sua { 20 | 21 | DummyRaucInstaller::DummyRaucInstaller() 22 | { } 23 | 24 | DummyRaucInstaller::~DummyRaucInstaller() 25 | { } 26 | 27 | TechCode DummyRaucInstaller::installBundle(const std::string& input) 28 | { 29 | _progress = 0; 30 | return TechCode::OK; 31 | } 32 | 33 | int32_t DummyRaucInstaller::getProgressPollInterval() const 34 | { 35 | return 0; 36 | } 37 | 38 | int32_t DummyRaucInstaller::getInstallProgress() 39 | { 40 | return _progress; 41 | } 42 | 43 | SlotStatus DummyRaucInstaller::getSlotStatus() 44 | { 45 | SlotStatus s; 46 | 47 | s["rootfs.0"]["version"] = getBootedVersion(); 48 | s["rootfs.0"]["state" ] = "booted"; 49 | 50 | return s; 51 | } 52 | 53 | std::string DummyRaucInstaller::getBootedVersion() 54 | { 55 | return "dummy_version0"; 56 | } 57 | 58 | std::string DummyRaucInstaller::getBundleVersion(const std::string& input) 59 | { 60 | return "dummy_version"; 61 | } 62 | 63 | std::string DummyRaucInstaller::getLastError() 64 | { 65 | return ""; 66 | } 67 | 68 | bool DummyRaucInstaller::installing() 69 | { 70 | _progress = std::min(100, _progress + 20); 71 | return _progress != 100; 72 | } 73 | 74 | bool DummyRaucInstaller::succeeded() 75 | { 76 | return true; 77 | } 78 | 79 | TechCode DummyRaucInstaller::activateBooted() 80 | { 81 | return TechCode::OK; 82 | } 83 | 84 | TechCode DummyRaucInstaller::activateOther() 85 | { 86 | return TechCode::OK; 87 | } 88 | 89 | } // namespace sua 90 | -------------------------------------------------------------------------------- /src/Install/DummyRaucInstaller.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_DUMMYRAUCINSTALLER_H 18 | #define SDV_SUA_DUMMYRAUCINSTALLER_H 19 | 20 | #include "Install/IRaucInstaller.h" 21 | 22 | namespace sua { 23 | 24 | // Only needed for initial setup & test, will be removed. 25 | class DummyRaucInstaller : public IRaucInstaller { 26 | public: 27 | DummyRaucInstaller(); 28 | ~DummyRaucInstaller(); 29 | 30 | TechCode activateBooted() override; 31 | TechCode activateOther() override; 32 | TechCode installBundle(const std::string& input) override; 33 | int32_t getProgressPollInterval() const override; 34 | int32_t getInstallProgress() override; 35 | SlotStatus getSlotStatus() override; 36 | std::string getBootedVersion() override; 37 | std::string getBundleVersion(const std::string& input) override; 38 | std::string getLastError() override; 39 | 40 | bool installing() override; 41 | bool succeeded() override; 42 | 43 | private: 44 | int _progress = 0; 45 | }; 46 | 47 | } // namespace sua 48 | 49 | #endif // SDV_SUA_DUMMYRAUCINSTALLER_H 50 | -------------------------------------------------------------------------------- /src/Install/IRaucInstaller.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_IRAUCINSTALLER_H 18 | #define SDV_SUA_IRAUCINSTALLER_H 19 | 20 | #include "TechCodes.h" 21 | 22 | #include 23 | #include 24 | 25 | namespace sua { 26 | 27 | // status["rootfs.0"]["version"] = "1.1" 28 | // status["rootfs.0"]["state" ] = "booted" 29 | // 30 | // status["rootfs.1"]["version"] = "1.0" 31 | // status["rootfs.1"]["state" ] = "inactive" 32 | using SlotStatus = std::map>; 33 | 34 | class IRaucInstaller { 35 | public: 36 | virtual ~IRaucInstaller() = default; 37 | 38 | virtual TechCode activateBooted() = 0; 39 | virtual TechCode activateOther() = 0; 40 | virtual TechCode installBundle(const std::string & input) = 0; 41 | 42 | // Returns configurable poll interval in milliseconds 43 | // Value is implementation defined 44 | // Do not poll install status more often than this interval 45 | virtual int32_t getProgressPollInterval() const = 0; 46 | 47 | virtual int32_t getInstallProgress() = 0; 48 | virtual SlotStatus getSlotStatus() = 0; 49 | virtual std::string getBootedVersion() = 0; 50 | virtual std::string getBundleVersion(const std::string & input) = 0; 51 | virtual std::string getLastError() = 0; 52 | 53 | virtual bool installing() = 0; 54 | virtual bool succeeded() = 0; 55 | }; 56 | 57 | } // namespace sua 58 | 59 | #endif // SDV_SUA_IRAUCINSTALLER_H 60 | -------------------------------------------------------------------------------- /src/Install/Installer.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #include "Install/Installer.h" 18 | #include "Logger.h" 19 | #include "Patterns/Dispatcher.h" 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | using namespace std::chrono_literals; 27 | 28 | namespace sua { 29 | 30 | const std::string Installer::EVENT_INSTALLING = "Installer/Installing"; 31 | 32 | Installer::Installer(std::shared_ptr installerAgent) 33 | : _installerAgent(installerAgent) 34 | { } 35 | 36 | TechCode Installer::start(const std::string input) 37 | { 38 | Logger::trace("Installer::start({})", input); 39 | 40 | if(_installerAgent->installBundle(input) == TechCode::InstallationFailed) { 41 | return TechCode::InstallationFailed; 42 | } 43 | 44 | int32_t progressPercentage = 0; 45 | int32_t progressNotificationLimiter = 0; 46 | uint32_t waitingTime = 0; // ms 47 | const uint32_t waitingTimeout = 15 * 60 * 1000; // ms 48 | 49 | while(_installerAgent->installing()) { 50 | progressPercentage = _installerAgent->getInstallProgress(); 51 | 52 | if(progressPercentage >= progressNotificationLimiter) { 53 | if(progressPercentage != 100) { 54 | std::map payload; 55 | payload["percentage"] = std::to_string(progressPercentage); 56 | sua::Dispatcher::instance().dispatch(EVENT_INSTALLING, payload); 57 | progressNotificationLimiter = progressPercentage + 1; 58 | } 59 | } 60 | 61 | if(waitingTime >= waitingTimeout) { 62 | Logger::error("Waiting for completion more than {} secs => assuming failed installation", waitingTimeout); 63 | return TechCode::InstallationFailed; 64 | } 65 | 66 | if(progressPercentage < 100) { 67 | std::this_thread::sleep_for(std::chrono::milliseconds(_installerAgent->getProgressPollInterval())); 68 | waitingTime += _installerAgent->getProgressPollInterval(); 69 | } 70 | } 71 | 72 | if(_installerAgent->succeeded()) { 73 | return TechCode::OK; 74 | } 75 | 76 | return TechCode::InstallationFailed; 77 | } 78 | 79 | } // namespace sua 80 | -------------------------------------------------------------------------------- /src/Install/Installer.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_INSTALLER_H 18 | #define SDV_SUA_INSTALLER_H 19 | 20 | #include "Install/IRaucInstaller.h" 21 | #include "TechCodes.h" 22 | 23 | #include 24 | 25 | namespace sua { 26 | 27 | class Installer { 28 | public: 29 | static const std::string EVENT_INSTALLING; 30 | 31 | Installer(std::shared_ptr installerAgent); 32 | 33 | TechCode start(const std::string input); 34 | 35 | protected: 36 | std::shared_ptr _installerAgent; 37 | }; 38 | 39 | } // namespace sua 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/Logger.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #include "Logger.h" 18 | 19 | #include 20 | #include 21 | 22 | namespace sua { 23 | 24 | Logger& Logger::instance() 25 | { 26 | static Logger logger; 27 | return logger; 28 | } 29 | 30 | Logger::Logger() 31 | { 32 | _log_level = Level::All; 33 | } 34 | 35 | void Logger::init() 36 | { 37 | auto stdout_sink = std::make_shared(); 38 | stdout_sink->set_level(spdlog::level::trace); 39 | stdout_sink->set_pattern("%^%Y-%m-%dT%H:%M:%S.%f%z %-8l %v%$"); 40 | 41 | auto json_file_sink = 42 | std::make_shared("../logs/log.json", true); 43 | json_file_sink->set_level(spdlog::level::trace); 44 | json_file_sink->set_pattern( 45 | "{\"time\": \"%Y-%m-%dT%H:%M:%S.%f%z\", \"level\": \"%^%l%$\", \"message\": \"%v\"},"); 46 | 47 | auto logger = std::make_shared( 48 | "sua_logger", spdlog::sinks_init_list{stdout_sink, json_file_sink}); 49 | logger->set_level(spdlog::level::trace); 50 | spdlog::set_default_logger(logger); 51 | } 52 | 53 | void Logger::shutdown() 54 | { 55 | spdlog::shutdown(); 56 | } 57 | 58 | void Logger::setLogLevel(int mask) 59 | { 60 | _log_level = mask; 61 | } 62 | 63 | int Logger::getLogLevel() const 64 | { 65 | return _log_level; 66 | } 67 | 68 | } // namespace sua 69 | -------------------------------------------------------------------------------- /src/Logger.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_LOGGER_H 18 | #define SDV_SUA_LOGGER_H 19 | 20 | #include 21 | 22 | namespace sua { 23 | 24 | class Logger { 25 | public: 26 | enum Level { 27 | None = 0x0000, 28 | Trace = 0x0001, 29 | Debug = 0x0002, 30 | Info = 0x0004, 31 | Warning = 0x0008, 32 | Error = 0x0010, 33 | Critical = 0x0020, 34 | All = 0xFFFF 35 | }; 36 | 37 | Logger(); 38 | 39 | void init(); 40 | void shutdown(); 41 | 42 | void setLogLevel(int mask); 43 | int getLogLevel() const; 44 | 45 | template 46 | static void trace(spdlog::format_string_t fmt, Args&&... args) 47 | { 48 | if(instance().getLogLevel() & Level::Trace) { 49 | spdlog::trace(fmt, std::forward(args)...); 50 | } 51 | } 52 | 53 | template 54 | static void debug(spdlog::format_string_t fmt, Args&&... args) 55 | { 56 | if(instance().getLogLevel() & Level::Debug) { 57 | spdlog::debug(fmt, std::forward(args)...); 58 | } 59 | } 60 | 61 | template 62 | static void info(spdlog::format_string_t fmt, Args&&... args) 63 | { 64 | if(instance().getLogLevel() & Level::Info) { 65 | spdlog::info(fmt, std::forward(args)...); 66 | } 67 | } 68 | 69 | template 70 | static void warning(spdlog::format_string_t fmt, Args&&... args) 71 | { 72 | if(instance().getLogLevel() & Level::Warning) { 73 | spdlog::warn(fmt, std::forward(args)...); 74 | } 75 | } 76 | 77 | template 78 | static void error(spdlog::format_string_t fmt, Args&&... args) 79 | { 80 | if(instance().getLogLevel() & Level::Error) { 81 | spdlog::error(fmt, std::forward(args)...); 82 | } 83 | } 84 | 85 | template 86 | static void critical(spdlog::format_string_t fmt, Args&&... args) 87 | { 88 | if(instance().getLogLevel() & Level::Critical) { 89 | spdlog::critical(fmt, std::forward(args)...); 90 | } 91 | } 92 | 93 | public: 94 | static Logger& instance(); 95 | 96 | private: 97 | int _log_level; 98 | }; 99 | 100 | } // namespace sua 101 | 102 | #endif 103 | -------------------------------------------------------------------------------- /src/Mqtt/IMqttMessagingProtocol.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_IMQTTMESSAGINGPROTOCOL_H 18 | #define SDV_SUA_IMQTTMESSAGINGPROTOCOL_H 19 | 20 | #include 21 | 22 | namespace sua { 23 | 24 | enum class MqttMessage; 25 | 26 | class IMqttMessagingProtocol { 27 | public: 28 | virtual ~IMqttMessagingProtocol() = default; 29 | 30 | virtual class Command readCommand(const std::string & input) = 0; 31 | 32 | virtual class DesiredState readDesiredState(const std::string & input) = 0; 33 | 34 | virtual class DesiredState readCurrentStateRequest(const std::string & input) = 0; 35 | 36 | virtual std::string createMessage(const class Context& ctx, MqttMessage message_type, const std::string& message = "") = 0; 37 | }; 38 | 39 | } // namespace sua 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/Mqtt/IMqttProcessor.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_IMQTTPROCESSOR_H 18 | #define SDV_SUA_IMQTTPROCESSOR_H 19 | 20 | #include 21 | 22 | namespace sua { 23 | 24 | enum class MqttMessage; 25 | 26 | class IMqttProcessor { 27 | public: 28 | static constexpr const char * TOPIC_IDENTIFY = "selfupdate/desiredstate"; 29 | static constexpr const char * TOPIC_COMMAND = "selfupdate/desiredstate/command"; 30 | static constexpr const char * TOPIC_FEEDBACK = "selfupdate/desiredstatefeedback"; 31 | static constexpr const char * TOPIC_STATE = "selfupdate/currentstate"; 32 | static constexpr const char * TOPIC_STATE_GET = "selfupdate/currentstate/get"; 33 | 34 | virtual ~IMqttProcessor() = default; 35 | 36 | virtual void start(const class MqttConfiguration & configuration) = 0; 37 | virtual void stop() = 0; 38 | 39 | virtual void send(const std::string& topic, MqttMessage message_type, const std::string& message = "", bool retained = false) = 0; 40 | }; 41 | 42 | } // namespace sua 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/Mqtt/MqttConfiguration.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_CONFIGURATION_H 18 | #define SDV_SUA_CONFIGURATION_H 19 | 20 | #include 21 | #include 22 | 23 | namespace sua { 24 | struct MqttConfiguration { 25 | std::string brokerHost; 26 | int brokerPort; 27 | std::string userName; 28 | std::string password; 29 | }; 30 | 31 | inline std::ostream& operator<<(std::ostream& os, MqttConfiguration conf) 32 | { 33 | os << "Host: " << conf.brokerHost << " Port: " << conf.password; 34 | return os; 35 | } 36 | } // namespace sua 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /src/Mqtt/MqttListener.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_MQTTLISTENER_H 18 | #define SDV_SUA_MQTTLISTENER_H 19 | 20 | #include "IMqttMessagingProtocol.h" 21 | 22 | namespace sua { 23 | 24 | class MqttListener { 25 | public: 26 | virtual ~MqttListener() = default; 27 | 28 | virtual void handleDesiredStateRequest(const std::string & content) = 0; 29 | 30 | virtual void handleConnectionEstablished() = 0; 31 | 32 | virtual void handleConnectionLost() = 0; 33 | }; 34 | 35 | } // namespace sua 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/Mqtt/MqttMessage.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #include "MqttMessage.h" 18 | 19 | #include 20 | 21 | namespace sua { 22 | 23 | std::ostream& operator<<(std::ostream& os, MqttMessage m) 24 | { 25 | // clang-format off 26 | static const std::map names = { 27 | { sua::MqttMessage::SystemVersion , "SystemVersion" }, 28 | { sua::MqttMessage::CurrentState , "CurrentState" }, 29 | { sua::MqttMessage::Identifying , "Identifying" }, 30 | { sua::MqttMessage::Identified , "Identified" }, 31 | { sua::MqttMessage::IdentificationFailed , "IdentificationFailed" }, 32 | { sua::MqttMessage::Skipped , "Skipped" }, 33 | { sua::MqttMessage::Rejected , "Rejected" }, 34 | { sua::MqttMessage::Downloading , "Downloading" }, 35 | { sua::MqttMessage::Downloaded , "Downloaded" }, 36 | { sua::MqttMessage::DownloadFailed , "DownloadFailed" }, 37 | { sua::MqttMessage::VersionChecking , "VersionChecking" }, 38 | { sua::MqttMessage::Installing , "Installing" }, 39 | { sua::MqttMessage::Installed , "Installed" }, 40 | { sua::MqttMessage::InstallFailed , "InstallFailed" }, 41 | { sua::MqttMessage::InstallFailedFallback, "InstallFailedFallback"}, 42 | { sua::MqttMessage::Cleaned , "Cleaned" }, 43 | { sua::MqttMessage::Activating , "Activating" }, 44 | { sua::MqttMessage::Activated , "Activated" }, 45 | { sua::MqttMessage::ActivationFailed , "ActivationFailed" }, 46 | { sua::MqttMessage::Complete , "Complete" }, 47 | { sua::MqttMessage::Incomplete , "Incomplete" }, 48 | 49 | }; 50 | // clang-format on 51 | 52 | return os << names.at(m); 53 | } 54 | 55 | } // namespace sua 56 | -------------------------------------------------------------------------------- /src/Mqtt/MqttMessage.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_MQTTMESSAGE_H 18 | #define SDV_SUA_MQTTMESSAGE_H 19 | 20 | #include 21 | 22 | namespace sua { 23 | 24 | enum class MqttMessage { 25 | SystemVersion, 26 | CurrentState, 27 | Identifying, 28 | Identified, 29 | IdentificationFailed, 30 | Skipped, 31 | Rejected, 32 | Downloading, 33 | Downloaded, 34 | DownloadFailed, 35 | VersionChecking, 36 | Installing, 37 | Installed, 38 | InstallFailed, 39 | InstallFailedFallback, 40 | Cleaned, 41 | Activating, 42 | Activated, 43 | ActivationFailed, 44 | Complete, 45 | Incomplete 46 | }; 47 | 48 | std::ostream & operator<<(std::ostream & os, MqttMessage m); 49 | 50 | } // namespace sua 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /src/Mqtt/MqttMessagingProtocolJSON.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_MQTTMESSAGINGPROTOCOLJSON_H 18 | #define SDV_SUA_MQTTMESSAGINGPROTOCOLJSON_H 19 | 20 | #include "IMqttMessagingProtocol.h" 21 | 22 | namespace sua { 23 | 24 | class MqttMessagingProtocolJSON : public IMqttMessagingProtocol { 25 | public: 26 | Command readCommand(const std::string & input) override; 27 | 28 | DesiredState readDesiredState(const std::string & input) override; 29 | 30 | DesiredState readCurrentStateRequest(const std::string & input) override; 31 | 32 | std::string createMessage(const class Context& ctx, MqttMessage message_type, const std::string& message = "") override; 33 | 34 | protected: 35 | virtual uint64_t epochTime() const; 36 | 37 | std::string writeFeedbackWithoutPayload(const DesiredState & desiredState, 38 | const std::string & state, const std::string & stateMessage) const; 39 | 40 | std::string writeFeedbackWithPayload(const DesiredState & desiredState, 41 | const std::string & state, const std::string & stateMessage, 42 | const std::string & status, const std::string & statusMessage, 43 | const std::string & customStatusMessage, int progress) const; 44 | 45 | std::string writeSystemVersionWithoutActivityId(const std::string & version); 46 | std::string writeSystemVersionWithActivityId(const std::string & version, const std::string & activityId); 47 | }; 48 | 49 | } // namespace sua 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/Mqtt/MqttProcessor.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_MQTTPROCESSOR_H 18 | #define SDV_SUA_MQTTPROCESSOR_H 19 | 20 | #include "Mqtt/IMqttProcessor.h" 21 | #include "Mqtt/MqttConfiguration.h" 22 | 23 | #include "mqtt/async_client.h" 24 | 25 | namespace sua { 26 | 27 | class MqttProcessor : public IMqttProcessor { 28 | public: 29 | MqttProcessor(class Context & context); 30 | ~MqttProcessor() = default; 31 | 32 | void start(const MqttConfiguration & configuration) override; 33 | void stop() override; 34 | 35 | void send(const std::string& topic, MqttMessage message_type, const std::string& message = "", bool retained = false) override; 36 | 37 | private: 38 | Context & _context; 39 | 40 | MqttConfiguration _config; 41 | 42 | std::string _clientId = "sua"; 43 | mqtt::async_client _client; 44 | }; 45 | } // namespace sua 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /src/Patterns/Dispatcher.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #include "Patterns/Dispatcher.h" 18 | 19 | #include 20 | 21 | namespace sua { 22 | 23 | int Dispatcher::subscribe(const std::string& topic, Callback callback) 24 | { 25 | static int id = 0; 26 | _subscribers.push_back({id, topic, callback}); 27 | return id++; 28 | } 29 | 30 | void Dispatcher::unsubscribe(const int id) 31 | { 32 | _subscribers.erase(std::remove_if(_subscribers.begin(), 33 | _subscribers.end(), 34 | [id](const Subscriber& s) { return s.id == id; }), 35 | _subscribers.end()); 36 | } 37 | 38 | void Dispatcher::dispatch(const std::string& topic, const std::map& payload) 39 | { 40 | for(const auto& s : _subscribers) { 41 | if(s.topic == topic) { 42 | s.callback(payload); 43 | } 44 | } 45 | } 46 | 47 | Dispatcher& Dispatcher::instance() 48 | { 49 | static Dispatcher d; 50 | return d; 51 | } 52 | 53 | DispatcherSubscriber::~DispatcherSubscriber() 54 | { 55 | for(const auto id : _subscriptions) { 56 | Dispatcher::instance().unsubscribe(id); 57 | } 58 | } 59 | 60 | void DispatcherSubscriber::subscribe(const std::string& topic, Dispatcher::Callback callback) 61 | { 62 | _subscriptions.push_back(Dispatcher::instance().subscribe(topic, callback)); 63 | } 64 | 65 | } // namespace sua 66 | -------------------------------------------------------------------------------- /src/Patterns/Dispatcher.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_DISPATCHER_H 18 | #define SDV_SUA_DISPATCHER_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | namespace sua { 26 | 27 | class Dispatcher { 28 | public: 29 | using Callback = std::function&)>; 30 | 31 | int subscribe(const std::string& topic, Callback callback); 32 | void unsubscribe(int id); 33 | 34 | void dispatch(const std::string& topic, const std::map& payload); 35 | 36 | public: 37 | static Dispatcher& instance(); 38 | 39 | private: 40 | struct Subscriber { 41 | int id; 42 | std::string topic; 43 | Callback callback; 44 | }; 45 | 46 | std::vector _subscribers; 47 | }; 48 | 49 | class DispatcherSubscriber { 50 | public: 51 | DispatcherSubscriber() = default; 52 | virtual ~DispatcherSubscriber(); 53 | 54 | void subscribe(const std::string& topic, Dispatcher::Callback callback); 55 | 56 | private: 57 | std::vector _subscriptions; 58 | }; 59 | 60 | } // namespace sua 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /src/SelfUpdateAgent.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_SELFUPDATEAGENT_H 18 | #define SDV_SUA_SELFUPDATEAGENT_H 19 | 20 | #include "Context.h" 21 | 22 | namespace sua { 23 | 24 | class SelfUpdateAgent { 25 | public: 26 | SelfUpdateAgent(); 27 | 28 | void init(); 29 | void start(const class MqttConfiguration & config); 30 | 31 | Context & context(); 32 | 33 | private: 34 | Context _context; 35 | }; 36 | 37 | } // namespace sua 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/TechCodes.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_TECHCODES_H 18 | #define SDV_SUA_TECHCODES_H 19 | 20 | namespace sua { 21 | 22 | enum class TechCode { 23 | OK = 0, 24 | DownloadFailed = 1001, 25 | InvalidBundle = 2001, 26 | InstallationFailed = 3001, 27 | UpdateRejected = 4001, 28 | UnknownError = 5001 29 | }; 30 | 31 | } // namespace sua 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/Utils/BundleChecker.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #include "BundleChecker.h" 18 | #include "Logger.h" 19 | 20 | namespace sua { 21 | 22 | bool BundleChecker::isUpdateBundleVersionDifferent(const std::string & updateBundleVersion, 23 | std::shared_ptr installer) 24 | { 25 | std::string slotVersion = installer->getBootedVersion(); 26 | 27 | Logger::info("System version, installed: '{}'", slotVersion); 28 | Logger::info("Bundle version, from file: '{}'", updateBundleVersion); 29 | 30 | return slotVersion != updateBundleVersion; 31 | } 32 | 33 | bool BundleChecker::isBundleVersionConsistent(const std::string & declaredVersion, 34 | std::shared_ptr installer, 35 | const std::string & bundlePath) 36 | { 37 | std::string updateBundleVersion = installer->getBundleVersion(bundlePath); 38 | 39 | Logger::info("Bundle version, from spec: '{}'", declaredVersion); 40 | Logger::info("Bundle version, from file: '{}'", updateBundleVersion); 41 | 42 | return declaredVersion == updateBundleVersion; 43 | } 44 | 45 | } // namespace sua 46 | -------------------------------------------------------------------------------- /src/Utils/BundleChecker.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_BUNDLECHECKER_H 18 | #define SDV_SUA_BUNDLECHECKER_H 19 | 20 | #include "IBundleChecker.h" 21 | 22 | namespace sua { 23 | 24 | class BundleChecker : public IBundleChecker { 25 | public: 26 | bool isUpdateBundleVersionDifferent(const std::string & updateBundleVersion, 27 | std::shared_ptr installer) override; 28 | 29 | bool isBundleVersionConsistent(const std::string & declaredVersion, 30 | std::shared_ptr installer, 31 | const std::string & bundlePath) override; 32 | }; 33 | 34 | } // namespace sua 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/Utils/IBundleChecker.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_IBUNDLECHECKER_H 18 | #define SDV_SUA_IBUNDLECHECKER_H 19 | 20 | #include "Install/IRaucInstaller.h" 21 | 22 | #include 23 | 24 | namespace sua { 25 | 26 | class IBundleChecker { 27 | public: 28 | virtual ~IBundleChecker() = default; 29 | 30 | virtual bool isUpdateBundleVersionDifferent(const std::string & updateBundleVer, 31 | std::shared_ptr installer) = 0; 32 | 33 | virtual bool isBundleVersionConsistent(const std::string & declaredVersion, 34 | std::shared_ptr installer, 35 | const std::string & bundlePath) = 0; 36 | }; 37 | 38 | } // namespace sua 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/Utils/JsonUtils.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #include "JsonUtils.h" 18 | 19 | #include 20 | 21 | namespace sua { 22 | 23 | std::string jsonTemplate(std::string tpl) 24 | { 25 | // required because lib-fmt expects curly brackets escaped as {{ and }} 26 | // to properly handle placeholders 3 steps are done: 27 | // { -> {{ 28 | // } -> }} 29 | // this makes placeholder {} look like {{}} 30 | // {{}} -> {} 31 | 32 | // escape opening 33 | tpl = std::regex_replace(tpl, std::regex("\\{"), "{{"); 34 | // espace closing 35 | tpl = std::regex_replace(tpl, std::regex("\\}"), "}}"); 36 | // unescape placeholder 37 | tpl = std::regex_replace(tpl, std::regex("\\{\\{\\}\\}"), "{}"); 38 | 39 | return tpl; 40 | } 41 | 42 | } 43 | 44 | -------------------------------------------------------------------------------- /src/Utils/JsonUtils.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_JSONUTILS_H 18 | #define SDV_SUA_JSONUTILS_H 19 | 20 | #include 21 | 22 | namespace sua { 23 | 24 | std::string jsonTemplate(std::string tpl); 25 | 26 | } 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/Utils/ServerAddressParser.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #include "ServerAddressParser.h" 18 | 19 | #include "spdlog/fmt/fmt.h" 20 | 21 | #include 22 | #include 23 | 24 | namespace sua { 25 | 26 | void ServerAddressParser::parse(const std::string & address, std::string & transport, std::string & host, int & port) 27 | { 28 | std::regex regex("^([a-zA-Z]+)\\:\\/\\/([^:\\/?#]+)(?:\\:([0-9]+))?$"); 29 | std::smatch result; 30 | 31 | std::regex_match(address, result, regex); 32 | 33 | // 4 total because: 34 | // [0] is entire match (address) 35 | // [1],[2],[3] are transport,host,port 36 | if(result.size() != 4) { 37 | throw std::runtime_error(fmt::format("Invalid server address: {}", address)); 38 | } 39 | 40 | if(result[1].str().empty()) { 41 | throw std::runtime_error("Missing transport"); 42 | } 43 | transport = result[1]; 44 | std::transform(transport.begin(), transport.end(), transport.begin(), ::tolower); 45 | 46 | if(result[2].str().empty()) { 47 | throw std::runtime_error("Missing host"); 48 | } 49 | host = result[2]; 50 | 51 | if(result[3].str().empty()) { 52 | throw std::runtime_error("Missing port"); 53 | } 54 | port = std::stoi(result[3]); 55 | } 56 | 57 | } // namespace sua 58 | -------------------------------------------------------------------------------- /src/Utils/ServerAddressParser.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_SERVERADDRESSPARSER_H 18 | #define SDV_SUA_SERVERADDRESSPARSER_H 19 | 20 | #include 21 | 22 | namespace sua { 23 | 24 | class ServerAddressParser { 25 | public: 26 | void parse(const std::string & address, std::string & transport, std::string & host, int & port); 27 | }; 28 | 29 | } // namespace sua 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /src/version.cpp.in: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #include "version.h" 18 | 19 | const char * SUA_COMMIT_HASH = "@SUA_COMMIT_HASH@"; 20 | const char * SUA_BUILD_NUMBER = "@SUA_BUILD_NUMBER@"; 21 | 22 | -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_VERSION_H 18 | #define SDV_SUA_VERSION_H 19 | 20 | extern const char * SUA_COMMIT_HASH; 21 | extern const char * SUA_BUILD_NUMBER; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /utest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(TestSelfUpdateAgent 2 | TestDispatcher.cpp 3 | TestDownloader.cpp 4 | TestFSM.cpp 5 | TestLogger.cpp 6 | TestMqttMessagingProtocolJSON.cpp 7 | TestSelfUpdateScenarios.cpp 8 | TestServerAddressParser.cpp 9 | TestStateFactory.cpp 10 | Utils.cpp 11 | ) 12 | 13 | include_directories( 14 | ../src 15 | ${CMAKE_SOURCE_DIR}/3rdparty/spdlog/include 16 | ${CMAKE_SOURCE_DIR}/3rdparty/nlohmann-json/include 17 | ) 18 | 19 | target_link_libraries( 20 | TestSelfUpdateAgent 21 | sua 22 | curl_lib 23 | paho-mqttpp3 24 | paho-mqtt3a 25 | gio_lib 26 | gobject_lib 27 | gmodule_lib 28 | glib_lib 29 | ffi_lib 30 | z_lib 31 | ${SUA_PLATFORM_LIBS} 32 | ssl_lib 33 | crypto_lib 34 | gtest_main 35 | gmock 36 | ) 37 | 38 | install(TARGETS TestSelfUpdateAgent RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/utest) 39 | -------------------------------------------------------------------------------- /utest/MockBundleChecker.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_MOCKBUNDLECHECKER_H 18 | #define SDV_SUA_MOCKBUNDLECHECKER_H 19 | 20 | #include "Utils/IBundleChecker.h" 21 | #include "Install/IRaucInstaller.h" 22 | 23 | #include "gmock/gmock.h" 24 | 25 | class MockBundleChecker : public sua::IBundleChecker { 26 | public: 27 | MOCK_METHOD(bool, isUpdateBundleVersionDifferent, 28 | (const std::string & updateBundleVersion, std::shared_ptr installer), (override)); 29 | }; 30 | 31 | #endif 32 | 33 | -------------------------------------------------------------------------------- /utest/MockDownloader.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_MOCKDOWNLOADER_H 18 | #define SDV_SUA_MOCKDOWNLOADER_H 19 | 20 | #include "Download/IDownloader.h" 21 | 22 | #include "gmock/gmock.h" 23 | 24 | class MockDownloader : public sua::IDownloader { 25 | public: 26 | MOCK_METHOD(sua::DownloadResult, start, (const std::string & input), (override)); 27 | }; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /utest/MockFSM.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_MOCKFSM_H 18 | #define SDV_SUA_MOCKFSM_H 19 | 20 | #include "FSM/FSM.h" 21 | 22 | #include "gmock/gmock.h" 23 | 24 | class MockFSM : public sua::FSM { 25 | public: 26 | MockFSM(sua::Context& context, std::vector& collection) 27 | : sua::FSM(context) 28 | , visitedStates(collection) 29 | { } 30 | 31 | void transitTo(const std::string& name) override { 32 | visitedStates.push_back(name); 33 | sua::FSM::transitTo(name); 34 | } 35 | 36 | private: 37 | std::vector& visitedStates; 38 | }; 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /utest/MockMqttMessagingProtocol.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_MOCKMQTTMESSAGINGPROTOCOL_H 18 | #define SDV_SUA_MOCKMQTTMESSAGINGPROTOCOL_H 19 | 20 | #include "Mqtt/IMqttMessagingProtocol.h" 21 | #include "Context.h" 22 | 23 | #include "gmock/gmock.h" 24 | 25 | class MockMqttMessagingProtocol : public sua::IMqttMessagingProtocol { 26 | public: 27 | MOCK_METHOD(sua::DesiredState, readDesiredState, (const std::string & input), (override)); 28 | MOCK_METHOD(sua::DesiredState, readCurrentStateRequest, (const std::string & input), (override)); 29 | MOCK_METHOD(std::string, createMessage, (const sua::Context& ctx, const std::string& name, const std::string& message), (override)); 30 | 31 | std::string native_createMessage(const sua::Context& ctx, const std::string& name, const std::string& message) { 32 | return sua::IMqttMessagingProtocol::createMessage(ctx, name, message); 33 | } 34 | }; 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /utest/MockMqttProcessor.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SDV_SUA_MOCKMQTTPROCESSOR_H 18 | #define SDV_SUA_MOCKMQTTPROCESSOR_H 19 | 20 | #include "Mqtt/MqttProcessor.h" 21 | 22 | #include "gmock/gmock.h" 23 | 24 | class MockMqttProcessor : public sua::MqttProcessor { 25 | public: 26 | MockMqttProcessor(class sua::Context& context, std::vector& collection) 27 | : sua::MqttProcessor::MqttProcessor(context) 28 | , sentMessages(collection) 29 | { } 30 | 31 | void send(const std::string& topic, 32 | sua::MqttMessage message_type, 33 | const std::string& message, 34 | bool retained) override 35 | { 36 | sentMessages.push_back(message_type); 37 | sua::MqttProcessor::send(topic, message_type, message, retained); 38 | } 39 | 40 | private: 41 | std::vector& sentMessages; 42 | }; 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /utest/TestDispatcher.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "Patterns/Dispatcher.h" 3 | 4 | namespace 5 | { 6 | 7 | class Subscriber : public sua::DispatcherSubscriber { 8 | public: 9 | }; 10 | 11 | class TestDispatcher : public ::testing::Test { 12 | public: 13 | void dispatch(const std::string & target, const std::string & value) { 14 | std::map payload; 15 | payload["key"] = value; 16 | sua::Dispatcher::instance().dispatch(target, payload); 17 | } 18 | }; 19 | 20 | TEST_F(TestDispatcher, multipleSubscribersAndSingleTopic_receiveAll) 21 | { 22 | std::string s1_payload; 23 | std::string s2_payload; 24 | 25 | Subscriber s1; 26 | s1.subscribe("a", [this, &s1_payload] (const std::map & payload) { 27 | s1_payload = payload.at("key"); 28 | }); 29 | 30 | Subscriber s2; 31 | s2.subscribe("a", [this, &s2_payload] (const std::map & payload) { 32 | s2_payload = payload.at("key"); 33 | }); 34 | 35 | dispatch("a", "1"); 36 | 37 | EXPECT_EQ(s1_payload, "1"); 38 | EXPECT_EQ(s2_payload, "1"); 39 | } 40 | 41 | TEST_F(TestDispatcher, multipleSubscribersAndMultipleTopics_receiveBasedOnTopic) 42 | { 43 | std::string s1_payload; 44 | std::string s2_payload; 45 | 46 | Subscriber s1; 47 | s1.subscribe("a", [this, &s1_payload] (const std::map & payload) { 48 | s1_payload = payload.at("key"); 49 | }); 50 | 51 | Subscriber s2; 52 | s2.subscribe("b", [this, &s2_payload] (const std::map & payload) { 53 | s2_payload = payload.at("key"); 54 | }); 55 | 56 | dispatch("a", "1"); 57 | EXPECT_EQ(s1_payload, "1"); 58 | EXPECT_EQ(s2_payload, ""); 59 | 60 | s1_payload = ""; 61 | s2_payload = ""; 62 | 63 | dispatch("b", "2"); 64 | EXPECT_EQ(s1_payload, ""); 65 | EXPECT_EQ(s2_payload, "2"); 66 | } 67 | 68 | TEST_F(TestDispatcher, unsubscribe) 69 | { 70 | std::string s1_payload; 71 | std::string s2_payload; 72 | 73 | { 74 | // s1 introduced into scope 75 | Subscriber s1; 76 | s1.subscribe("a", [this, &s1_payload] (const std::map & payload) { 77 | s1_payload = payload.at("key"); 78 | }); 79 | 80 | // dispatch "a" only to s1 and check payload 81 | dispatch("a", "1"); 82 | EXPECT_EQ(s1_payload, "1"); 83 | EXPECT_EQ(s2_payload, ""); 84 | 85 | // reset 86 | s1_payload = ""; 87 | s2_payload = ""; 88 | 89 | { 90 | // s2 introduced into scope 91 | Subscriber s2; 92 | s2.subscribe("b", [this, &s2_payload] (const std::map & payload) { 93 | s2_payload = payload.at("key"); 94 | }); 95 | 96 | // reset 97 | s1_payload = ""; 98 | s2_payload = ""; 99 | 100 | // dispatch "b" only to s2 and check payload 101 | dispatch("b", "2"); 102 | EXPECT_EQ(s1_payload, ""); 103 | EXPECT_EQ(s2_payload, "2"); 104 | } 105 | 106 | // reset 107 | s1_payload = ""; 108 | s2_payload = ""; 109 | 110 | // "b" is not subscribed anymore 111 | dispatch("b", "2"); 112 | EXPECT_EQ(s1_payload, ""); 113 | EXPECT_EQ(s2_payload, ""); 114 | 115 | // reset 116 | s1_payload = ""; 117 | s2_payload = ""; 118 | 119 | // "a" is still subscribed 120 | dispatch("a", "1"); 121 | EXPECT_EQ(s1_payload, "1"); 122 | EXPECT_EQ(s2_payload, ""); 123 | } 124 | 125 | // reset 126 | s1_payload = ""; 127 | s2_payload = ""; 128 | 129 | // "a" is not subscribed anymore 130 | dispatch("a", "1"); 131 | EXPECT_EQ(s1_payload, ""); 132 | EXPECT_EQ(s2_payload, ""); 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /utest/TestDownloader.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | 3 | #include "Context.h" 4 | #include "Download/Downloader.h" 5 | 6 | #include 7 | 8 | namespace { 9 | 10 | class TestDownloaderP 11 | : public ::testing::TestWithParam 12 | { }; 13 | 14 | TEST_P(TestDownloaderP, downloadViaUnsupportedProtocolFails) 15 | { 16 | sua::Context ctx; 17 | sua::Downloader d(ctx); 18 | 19 | auto url = fmt::format("{}://127.0.0.1/bundle", GetParam()); 20 | auto res = d.start(url); 21 | 22 | EXPECT_EQ(std::get<0>(res), sua::TechCode::DownloadFailed); 23 | } 24 | 25 | INSTANTIATE_TEST_CASE_P( 26 | TestDownloaderViaUnsupportedProtocols, 27 | TestDownloaderP, 28 | ::testing::Values( 29 | "ftp", "http", "sftp", "smb", "smbs", "ldap", "ldaps" 30 | ) 31 | ); 32 | 33 | } 34 | 35 | 36 | -------------------------------------------------------------------------------- /utest/TestFSM.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "FSM/State.h" 3 | #include "FSM/FSM.h" 4 | #include "FSM/StateFactory.h" 5 | #include "Context.h" 6 | 7 | namespace { 8 | 9 | class A : public sua::State { 10 | public: 11 | A() 12 | : State("A") 13 | { } 14 | }; 15 | 16 | class B : public sua::State { 17 | public: 18 | B() 19 | : State("B") 20 | { } 21 | }; 22 | 23 | class C : public sua::State { 24 | public: 25 | C() 26 | : State("C") 27 | { } 28 | }; 29 | 30 | enum Event { 31 | EventAtoB, 32 | EventAtoC, 33 | EventBtoC, 34 | EventCtoA 35 | }; 36 | 37 | TEST(TestFSM, setupAndTransitions) 38 | { 39 | auto factory = std::make_shared(); 40 | factory->addStateT("A"); 41 | factory->addStateT("B"); 42 | factory->addStateT("C"); 43 | 44 | sua::Context ctx; 45 | 46 | sua::FSM fsm(ctx); 47 | fsm.setTransitions({ 48 | { static_cast(EventAtoB), "A", "B" }, 49 | { static_cast(EventAtoC), "A", "C" }, 50 | { static_cast(EventBtoC), "B", "C" }, 51 | { static_cast(EventCtoA), "C", "A" } 52 | }); 53 | fsm.setFactory(factory); 54 | 55 | fsm.transitTo("A"); 56 | EXPECT_EQ(fsm.activeState(), "A"); 57 | 58 | fsm.handleEvent(static_cast(EventAtoB)); 59 | EXPECT_EQ(fsm.activeState(), "B"); 60 | 61 | fsm.handleEvent(static_cast(EventBtoC)); 62 | EXPECT_EQ(fsm.activeState(), "C"); 63 | 64 | fsm.handleEvent(static_cast(EventCtoA)); 65 | EXPECT_EQ(fsm.activeState(), "A"); 66 | 67 | fsm.handleEvent(static_cast(EventAtoC)); 68 | EXPECT_EQ(fsm.activeState(), "C"); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /utest/TestLogger.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "Logger.h" 3 | #include "nlohmann/json.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace { 10 | 11 | bool isTimeFormatCorrect(const std::string & input) 12 | { 13 | std::regex r("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}.\\d{6}[+-]\\d{2}:\\d{2}"); 14 | std::smatch m; 15 | return std::regex_match(input, m, r); 16 | } 17 | 18 | std::string readAndAdaptLog(const std::string & path) 19 | { 20 | std::ifstream f; 21 | f.open(path); 22 | 23 | std::stringstream ss; 24 | ss << f.rdbuf(); 25 | 26 | std::string log = ss.str(); 27 | 28 | // replace last "," to parse json output by wrapping into array [] 29 | log.erase(log.rfind(',')); 30 | ss.str(""); 31 | ss << "[" << log << "]"; 32 | return ss.str(); 33 | } 34 | 35 | TEST(TestLogger, allLogLevelsToFile) 36 | { 37 | sua::Logger::instance().init(); 38 | sua::Logger::instance().setLogLevel(sua::Logger::Level::All); 39 | sua::Logger::trace("T"); 40 | sua::Logger::debug("D"); 41 | sua::Logger::info("I"); 42 | sua::Logger::warning("W"); 43 | sua::Logger::error("E"); 44 | sua::Logger::critical("C"); 45 | sua::Logger::instance().shutdown(); 46 | 47 | nlohmann::json data = nlohmann::json::parse(readAndAdaptLog("../logs/log.json")); 48 | 49 | EXPECT_TRUE(isTimeFormatCorrect(data[0]["time"])); 50 | EXPECT_EQ(data[0]["level"], "trace"); 51 | EXPECT_EQ(data[0]["message"], "T"); 52 | 53 | EXPECT_TRUE(isTimeFormatCorrect(data[1]["time"])); 54 | EXPECT_EQ(data[1]["level"], "debug"); 55 | EXPECT_EQ(data[1]["message"], "D"); 56 | 57 | EXPECT_TRUE(isTimeFormatCorrect(data[2]["time"])); 58 | EXPECT_EQ(data[2]["level"], "info"); 59 | EXPECT_EQ(data[2]["message"], "I"); 60 | 61 | EXPECT_TRUE(isTimeFormatCorrect(data[3]["time"])); 62 | EXPECT_EQ(data[3]["level"], "warning"); 63 | EXPECT_EQ(data[3]["message"], "W"); 64 | 65 | EXPECT_TRUE(isTimeFormatCorrect(data[4]["time"])); 66 | EXPECT_EQ(data[4]["level"], "error"); 67 | EXPECT_EQ(data[4]["message"], "E"); 68 | 69 | EXPECT_TRUE(isTimeFormatCorrect(data[5]["time"])); 70 | EXPECT_EQ(data[5]["level"], "critical"); 71 | EXPECT_EQ(data[5]["message"], "C"); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /utest/TestServerAddressParser.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "Utils/ServerAddressParser.h" 3 | 4 | namespace 5 | { 6 | 7 | class TestServerAddressParser : public ::testing::Test { 8 | public: 9 | void parse(const std::string & address) { 10 | sua::ServerAddressParser().parse(address, transport, host, port); 11 | } 12 | 13 | std::string transport; 14 | std::string host; 15 | int port; 16 | }; 17 | 18 | TEST_F(TestServerAddressParser, parse_missingTransport_throws) 19 | { 20 | EXPECT_THROW(parse("://host:42"), std::runtime_error); 21 | EXPECT_THROW(parse("//host:42"), std::runtime_error); 22 | EXPECT_THROW(parse("host:42"), std::runtime_error); 23 | } 24 | 25 | TEST_F(TestServerAddressParser, parse_missingHost_throws) 26 | { 27 | EXPECT_THROW(parse("tcp://:42"), std::runtime_error); 28 | EXPECT_THROW(parse("tcp://42"), std::runtime_error); 29 | } 30 | 31 | TEST_F(TestServerAddressParser, parse_missingPort_throws) 32 | { 33 | EXPECT_THROW(parse("tcp://host"), std::runtime_error); 34 | EXPECT_THROW(parse("tcp://host:"), std::runtime_error); 35 | } 36 | 37 | TEST_F(TestServerAddressParser, parse_success) 38 | { 39 | parse("tcp://host:42"); 40 | EXPECT_EQ(transport, "tcp"); 41 | EXPECT_EQ(host, "host"); 42 | EXPECT_EQ(port, 42); 43 | 44 | parse("TCP://host:42"); 45 | EXPECT_EQ(transport, "tcp"); 46 | EXPECT_EQ(host, "host"); 47 | EXPECT_EQ(port, 42); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /utest/TestStateFactory.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "FSM/StateFactory.h" 3 | #include "FSM/State.h" 4 | 5 | namespace { 6 | 7 | class A : public sua::State { 8 | public: 9 | A() 10 | : State("A") 11 | { } 12 | }; 13 | 14 | class B : public sua::State { 15 | public: 16 | B() 17 | : State("B") 18 | { } 19 | }; 20 | 21 | TEST(TestStateFactory, createKnownState_returnsState) 22 | { 23 | sua::StateFactory f; 24 | f.addStateT("A"); 25 | f.addStateT("B"); 26 | 27 | EXPECT_NE(std::dynamic_pointer_cast(f.createState("A")), nullptr); 28 | EXPECT_NE(std::dynamic_pointer_cast(f.createState("B")), nullptr); 29 | } 30 | 31 | TEST(TestStateFactory, createUnknownState_throwsLogicError) 32 | { 33 | sua::StateFactory f; 34 | f.addStateT("A"); 35 | f.addStateT("B"); 36 | 37 | EXPECT_THROW(f.createState("C"), std::logic_error); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /utest/Utils.cpp: -------------------------------------------------------------------------------- 1 | #include "Utils.h" 2 | 3 | #include "gtest/gtest.h" 4 | 5 | #include 6 | #include 7 | 8 | void EXPECT_EQ_MULTILINE(const std::string & a, const std::string & b) 9 | { 10 | std::stringstream stream_a(a); 11 | std::stringstream stream_b(b); 12 | 13 | while(true) { 14 | std::string a; 15 | std::string b; 16 | 17 | stream_a >> a; 18 | stream_b >> b; 19 | 20 | EXPECT_EQ(a, b); 21 | 22 | if(!stream_a.eof() && !stream_b.eof()) { 23 | continue; 24 | } 25 | 26 | return; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /utest/Utils.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Contributors to the Eclipse Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | #ifndef SUA_TESTING_H 18 | #define SUA_TESTING_H 19 | 20 | #include 21 | 22 | void EXPECT_EQ_MULTILINE(const std::string & a, const std::string & b); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /utest/sua-apache2.conf: -------------------------------------------------------------------------------- 1 | 2 | ServerName localhost 3 | DocumentRoot /var/www/html 4 | SSLEngine on 5 | SSLCertificateFile /usr/share/ca-certificates/extra/selfupdateagent.crt 6 | SSLCertificateKeyFile /etc/ssl/private/selfupdateagent.key 7 | 8 | -------------------------------------------------------------------------------- /utest/sua-certificate.config: -------------------------------------------------------------------------------- 1 | [dn] 2 | CN=localhost 3 | 4 | [req] 5 | distinguished_name = dn 6 | 7 | [EXT] 8 | subjectAltName=DNS:localhost 9 | keyUsage=digitalSignature 10 | extendedKeyUsage=serverAuth 11 | --------------------------------------------------------------------------------