diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..593c377 --- /dev/null +++ b/.clang-format @@ -0,0 +1,110 @@ +BasedOnStyle: WebKit +TabWidth: 4 +IndentWidth: 4 +UseTab: Never +ColumnLimit: 100 + +--- +Language: Cpp + +DisableFormat: false +Standard: Cpp11 + +AccessModifierOffset: -4 +AlignAfterOpenBracket: true +AlignConsecutiveAssignments: true +AlignConsecutiveDeclarations: true +AlignEscapedNewlinesLeft: false +AlignOperands: true +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: false +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: false +BinPackParameters: false + +BreakBeforeBraces: Custom +BraceWrapping: { + AfterClass: 'false' + AfterControlStatement: 'false' + AfterEnum: 'false' + AfterFunction: 'true' + AfterNamespace: 'false' + AfterStruct: 'false' + AfterUnion: 'false' + BeforeCatch: 'false' + BeforeElse: 'false' + IndentBraces: 'false' + AfterExternBlock: 'false' + SplitEmptyFunction: 'false' + SplitEmptyRecord: 'false' + SplitEmptyNamespace: 'true' +} + +BreakAfterJavaFieldAnnotations: true +BreakBeforeInheritanceComma: true +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: true +BreakInheritanceList: BeforeColon +BreakStringLiterals: true + +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +SpaceBeforeCpp11BracedList: false +DerivePointerAlignment: false +ExperimentalAutoDetectBinPacking: false +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +IndentCaseLabels: false +FixNamespaceComments: true +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +JavaScriptQuotes: Double +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +ObjCBlockIndentWidth: 4 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 + +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Left +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: Never +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceAfterTemplateKeyword: true +SpaceBeforeInheritanceColon: true + +SortUsingDeclarations: true +SortIncludes: true + +ReflowComments: false + +UseCRLF: false + +IncludeBlocks: Preserve +IndentPPDirectives: AfterHash diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..abd9117 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,17 @@ +# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.195.0/containers/cpp/.devcontainer/base.Dockerfile +# [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 +ARG VARIANT="bullseye" +FROM mcr.microsoft.com/vscode/devcontainers/cpp:0-${VARIANT} + +# ENV http_proxy "http://172.17.0.1:3128" +# ENV https_proxy "http://172.17.0.1:3128" + +# Required packages. +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install python3-pip python3-setuptools python3-wheel ninja-build \ + libblkid-dev libselinux-dev gcc-9-aarch64-linux-gnu g++-9-aarch64-linux-gnu \ + mosquitto mosquitto-clients \ + && pip3 install meson + +# Optional packages. +RUN apt-get -y install vim file iputils-ping diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..7ea1b59 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,42 @@ +// For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.195.0/containers/cpp +{ + "name": "SDV Self Update DevContainer", + "build": { + "dockerfile": "Dockerfile", + // Update 'VARIANT' to pick an Debian / Ubuntu OS version: debian-11, debian-10, debian-9, ubuntu-21.04, ubuntu-20.04, ubuntu-18.04 + // Use Debian 11, Debian 9, Ubuntu 18.04 or Ubuntu 21.04 on local arm64/Apple Silicon + "args": { "VARIANT": "bullseye" } + }, + "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"], + + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + // Set *default* container specific settings.json values on container create. + "settings": {}, + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "ms-vscode.cpptools", + "ms-vscode.cmake-tools", + "GitHub.copilot", + "bierner.markdown-mermaid" + ] + } + }, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "gcc -v", + + // Comment out this line to run as root instead. + "remoteUser": "vscode", + "features": { + "docker-in-docker": "latest" + } + +} diff --git a/.devcontainer/reinstall-cmake.sh b/.devcontainer/reinstall-cmake.sh new file mode 100644 index 0000000..c833253 --- /dev/null +++ b/.devcontainer/reinstall-cmake.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- +# +set -e + +CMAKE_VERSION=${1:-"none"} + +if [ "${CMAKE_VERSION}" = "none" ]; then + echo "No CMake version specified, skipping CMake reinstallation" + exit 0 +fi + +# Cleanup temporary directory and associated files when exiting the script. +cleanup() { + EXIT_CODE=$? + set +e + if [[ -n "${TMP_DIR}" ]]; then + echo "Executing cleanup of tmp files" + rm -Rf "${TMP_DIR}" + fi + exit $EXIT_CODE +} +trap cleanup EXIT + + +echo "Installing CMake..." +apt-get -y purge --auto-remove cmake +mkdir -p /opt/cmake + +architecture=$(dpkg --print-architecture) +case "${architecture}" in + arm64) + ARCH=aarch64 ;; + amd64) + ARCH=x86_64 ;; + *) + echo "Unsupported architecture ${architecture}." + exit 1 + ;; +esac + +CMAKE_BINARY_NAME="cmake-${CMAKE_VERSION}-linux-${ARCH}.sh" +CMAKE_CHECKSUM_NAME="cmake-${CMAKE_VERSION}-SHA-256.txt" +TMP_DIR=$(mktemp -d -t cmake-XXXXXXXXXX) + +echo "${TMP_DIR}" +cd "${TMP_DIR}" + +curl -sSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_BINARY_NAME}" -O +curl -sSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_CHECKSUM_NAME}" -O + +sha256sum -c --ignore-missing "${CMAKE_CHECKSUM_NAME}" +sh "${TMP_DIR}/${CMAKE_BINARY_NAME}" --prefix=/opt/cmake --skip-license + +ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..c67cf9c --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,101 @@ +name: Build and Push SUA docker image + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: sua + +jobs: + + build: + strategy: + matrix: + architecture: [ arm64, amd64 ] + + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + id-token: write + + steps: + - id: repo_name + uses: ASzc/change-string-case-action@v1 + with: + string: ${{ github.repository }} + - name: Find latest tag + uses: oprypin/find-latest-tag@v1 + with: + repository: ${{ github.repository }} + releases-only: false + id: build_tag + - run: echo "repo is at version ${{ steps.build_tag.outputs.tag }}" + - name: Checkout repository + uses: actions/checkout@v3 + with: + submodules: true + + # Workaround: https://github.com/docker/build-push-action/issues/461 + - name: Setup Docker buildx + uses: docker/setup-buildx-action@79abd3f86f79a9d68a23c75a09a9a85889262adf + + # Login against a Docker registry except on PR + # https://github.com/docker/login-action + - name: Log into registry ${{ env.REGISTRY }} + uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Extract metadata (tags, labels) for Docker + # https://github.com/docker/metadata-action + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + # https://github.com/docker/build-push-action + - name: Build and push Docker image to ghcr.io + id: build-and-push + uses: docker/build-push-action@ac9327eae2b366085ac7f6a2d02df8aa8ead720a + with: + push: true + context: . + platforms: linux/${{ matrix.architecture }} + file: Dockerfile.${{ matrix.architecture }} + labels: ${{ steps.meta.outputs.labels }} + tags: | + ${{ env.REGISTRY }}/${{ steps.repo_name.outputs.lowercase }}/${{ env.IMAGE_NAME }}:testing_${{ matrix.architecture}} + + create-multiarch-image: + runs-on: ubuntu-latest + name: Create multiarch image + needs: [build] + + steps: + - id: repo_name + uses: ASzc/change-string-case-action@v1 + with: + string: ${{ github.repository }} + + - name: Log into registry ${{ env.REGISTRY }} + uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Create merged manifest and push + env: + REPO: ghcr.io/SoftwareDefinedVehicle/leda-contrib-self-update-agent-fork + run: | + docker buildx imagetools create -t ${{ env.REGISTRY }}/${{ steps.repo_name.outputs.lowercase }}/${{ env.IMAGE_NAME }}:testing ${{ env.REGISTRY }}/${{ steps.repo_name.outputs.lowercase }}/${{ env.IMAGE_NAME }}:testing_arm64 ${{ env.REGISTRY }}/${{ steps.repo_name.outputs.lowercase }}/${{ env.IMAGE_NAME }}:testing_amd64 + diff --git a/.github/workflows/native-build.yml b/.github/workflows/native-build.yml new file mode 100644 index 0000000..9dbef62 --- /dev/null +++ b/.github/workflows/native-build.yml @@ -0,0 +1,65 @@ +name: CMake native build and unit tests + +on: + push: + pull_request: + branches: [ main ] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build_for_architecture: + strategy: + matrix: + arch: [amd64, arm64] + runs-on: [ ubuntu-latest ] + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + + # Remove apt repos that are known to break from time to time + # See https://github.com/actions/virtual-environments/issues/323 + - name: Remove broken apt repos [Ubuntu] + run: | + for apt_file in `grep -lr microsoft /etc/apt/sources.list.d/`; do sudo rm $apt_file; done + + - name: Install tools + run: | + sudo apt-get install apt-transport-https --yes + sudo apt-get -y update + sudo apt-get -y upgrade + sudo apt-get -y install autoconf binutils cmake file \ + gcc g++ git libtool make \ + build-essential libcurl4-openssl-dev \ + binutils-aarch64-linux-gnu gcc-9-aarch64-linux-gnu g++-9-aarch64-linux-gnu \ + python3 python3-pip python3-setuptools python3-wheel ninja-build python3-pip \ + libselinux1-dev libmount-dev libmount1 libblkid-dev + pip3 install meson + + - name: Configure CMake + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + + - name: Build for ${{ matrix.arch }} + run: | + mkdir build_${{ matrix.arch }} + ln -s build_${{ matrix.arch }} build + ./scripts/build_openssl_${{ matrix.arch }}.sh + ./scripts/build_glib_${{ matrix.arch }}.sh + cd build_${{ matrix.arch }} + cmake -DCMAKE_INSTALL_PREFIX=../dist_${{ matrix.arch }} -DCMAKE_TOOLCHAIN_FILE=../cmake/linux/${{ matrix.arch }}/toolchain.cmake -DOPENSSL_ROOT_DIR=../build_${{ matrix.arch }} -DOPENSSL_CRYPTO_LIBRARY=../build_${{ matrix.arch }}/lib/libcrypto.so .. + make + + - name: Run unit tests + # execute tests only for the x86, as for arm the running on qemu would be required + if: ${{ matrix.arch=='amd64' }} + run: | + LD_LIBRARY_PATH=./build_${{ matrix.arch }}/lib:./build_${{ matrix.arch }}/3rdparty ./build_${{ matrix.arch }}/utest/TestSDVSelfUpdateAgent + + - name: Upload ${{ matrix.arch }} artifact to workspace + uses: actions/upload-artifact@v3 + with: + name: sdv-self-update-agent_${{ matrix.arch }} + path: ${{github.workspace}}/build_${{ matrix.arch }}/src/sdv-self-update-agent diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..aedb034 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,132 @@ +name: Create SUA release assets + +on: +# will be triggered by new tag which indicates release + push: + tags: + - "v*.*.*" + +env: + REGISTRY: ghcr.io + IMAGE_NAME: sua + +jobs: + build_for_architecture: + strategy: + matrix: + arch: [amd64, arm64] + runs-on: [ ubuntu-latest ] + permissions: + contents: write + packages: write + id-token: write + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + + # Checkout code + - name: Checkout repository + uses: actions/checkout@v3 + with: + submodules: true + + # Find the tag to use for versioning code + - name: Find latest tag + uses: oprypin/find-latest-tag@v1 + with: + repository: ${{ github.repository }} + releases-only: false + id: build_tag + - run: echo "TAG vor release = ${{ steps.build_tag.outputs.tag }}" + + # Setup Buildx for multi-arch build + - name: Setup Docker buildx + uses: docker/setup-buildx-action@79abd3f86f79a9d68a23c75a09a9a85889262adf + + - name: Build for ${{ matrix.arch }} + uses: docker/build-push-action@v3.0.0 + with: + push: false + platforms: linux/${{ matrix.arch }} + tags: | + ${{ env.IMAGE_NAME }}:${{ steps.build_tag.outputs.tag }} + outputs: | + type=oci,dest=./${{ env.IMAGE_NAME }}_${{ steps.build_tag.outputs.tag }}_${{ matrix.arch }}.tar + + - name: Upload ${{ matrix.arch }} artifact to workspace + uses: actions/upload-artifact@v3 + with: + name: ${{ env.IMAGE_NAME }}_${{ steps.build_tag.outputs.tag }}_${{ matrix.arch }}.tar + path: ${{github.workspace}}/${{ env.IMAGE_NAME }}_${{ steps.build_tag.outputs.tag }}_${{ matrix.arch }}.tar + + - name: Upload ${{ matrix.arch }} assets to release bundle + uses: softprops/action-gh-release@v1 + with: + files: | + ${{ env.IMAGE_NAME }}_${{ steps.build_tag.outputs.tag }}_${{ matrix.arch }}.tar + + build-multi-arch-for-ghcr: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + id-token: write + + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + + # Checkout code + - name: Checkout repository + uses: actions/checkout@v3 + with: + submodules: true + + - id: repo_name + uses: ASzc/change-string-case-action@v1 + with: + string: ${{ github.repository }} + + # Find the tag to use for versioning code + - name: Find latest tag + uses: oprypin/find-latest-tag@v1 + with: + repository: ${{ github.repository }} + releases-only: false + id: build_tag + - run: echo "TAG vor release = ${{ steps.build_tag.outputs.tag }}" + + # Setup Buildx + - name: Setup Docker buildx + uses: docker/setup-buildx-action@79abd3f86f79a9d68a23c75a09a9a85889262adf + + # Login against a Docker registry + # https://github.com/docker/login-action + - name: Log into registry ${{ env.REGISTRY }} + uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Extract metadata (tags, labels) for Docker + # https://github.com/docker/metadata-action + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + # https://github.com/docker/build-push-action + - name: Build and push Docker multiarch image to ghcr.io + id: build-and-push + uses: docker/build-push-action@ac9327eae2b366085ac7f6a2d02df8aa8ead720a + with: + push: true + platforms: linux/arm64, linux/amd64 + labels: ${{ steps.meta.outputs.labels }} + tags: | + ${{ env.REGISTRY }}/${{ steps.repo_name.outputs.lowercase }}/${{ env.IMAGE_NAME }}:latest + ${{ env.REGISTRY }}/${{ steps.repo_name.outputs.lowercase }}/${{ env.IMAGE_NAME }}:${{ steps.build_tag.outputs.tag }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7f875bc --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps +build_amd64 +build_arm64 +dist_amd64 +dist_arm64 \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..48d5e1a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,24 @@ +[submodule "3rdparty/paho.mqtt.c"] + path = 3rdparty/paho.mqtt.c + url = https://github.com/eclipse/paho.mqtt.c.git +[submodule "3rdparty/paho.mqtt.cpp"] + path = 3rdparty/paho.mqtt.cpp + url = https://github.com/eclipse/paho.mqtt.cpp.git +[submodule "3rdparty/mini-yaml"] + path = 3rdparty/mini-yaml + url = https://github.com/jimmiebergmann/mini-yaml.git +[submodule "3rdparty/curl"] + path = 3rdparty/curl + url = https://github.com/curl/curl.git +[submodule "3rdparty/googletest"] + path = 3rdparty/googletest + url = https://github.com/google/googletest.git +[submodule "3rdparty/glib"] + path = 3rdparty/glib + url = https://gitlab.gnome.org/GNOME/glib.git +[submodule "3rdparty/spdlog"] + path = 3rdparty/spdlog + url = https://github.com/gabime/spdlog.git +[submodule "3rdparty/openssl"] + path = 3rdparty/openssl + url = https://github.com/openssl/openssl.git diff --git a/.ort.yml b/.ort.yml new file mode 100644 index 0000000..f7d7039 --- /dev/null +++ b/.ort.yml @@ -0,0 +1,203 @@ +excludes: + paths: + - pattern: "3rdparty/openssl/tlsfuzzer/**" + reason: "TEST_OF" + comment: "Test suite for SSL & TLS, not included in production release." + - pattern: "3rdparty/paho.mqtt.c/test/**" + reason: "TEST_OF" + comment: "Tests of the Paho C library, not included in production release." + - pattern: "3rdparty/paho.mqtt.c/docs/**" + reason: "DOCUMENTATION_OF" + comment: "Code examples using Paho C library, not included in production release." + - pattern: "3rdparty/paho.mqtt.c/test_package/**" + reason: "TEST_OF" + comment: "Tests of the Paho C library, not included in production release." + - pattern: "3rdparty/paho.mqtt.cpp/test/**" + reason: "TEST_OF" + comment: "Tests of the Paho CPP library, not included in production release." + - pattern: "3rdparty/openssl/pyca-cryptography/**" + reason: "OPTIONAL_COMPONENT_OF" + comment: "Python cryptography tools, not included in production release." + - pattern: "3rdparty/openssl/python-ecdsa/**" + reason: "OPTIONAL_COMPONENT_OF" + comment: "Python cryptography tools, not included in production release." + - pattern: "3rdparty/googletest/**" + reason: "TEST_OF" + comment: "Google test framework, not included in production release." + - pattern: "3rdparty/mini-yaml/test/**" + reason: "TEST_OF" + comment: "Mini yaml test setup, not included in production release." + - pattern: "3rdparty/openssl/wycheproof/**" + reason: "TEST_OF" + comment: "Crypto library testing, not included in production release." + - pattern: "3rdparty/openssl/tlslite-ng/**" + reason: "OPTIONAL_COMPONENT_OF" + comment: "Python library for SSL & TLS, not included in production release." + - pattern: "3rdparty/openssl/krb5/**" + reason: "OPTIONAL_COMPONENT_OF" + comment: "Kerberos not included in production release." + - pattern: "3rdparty/curl/docs/**" + reason: "DOCUMENTATION_OF" + comment: "Code examples, not included in production release." + - pattern: "3rdparty/glib/gio/tests/**" + reason: "TEST_OF" + comment: "Tests of glib, not included in production release." + - pattern: "3rdparty/glib/gobject/tests/**" + reason: "TEST_OF" + comment: "Tests of glib, not included in production release." + - pattern: "3rdparty/glib/glib/tests/**" + reason: "TEST_OF" + comment: "Tests of glib, not included in production release." + - pattern: "3rdparty/openssl/external/perl/Text-Template-1.56/**" + reason: "DOCUMENTATION_OF" + comment: "Used in the generation of letters,HTML pages, not in production release." + - pattern: "3rdparty/glib/po/**" + reason: "OTHER" + comment: "Languauge translator tool, not in production release." + - pattern: "3rdparty/openssl/CHANGES.md" + reason: "DOCUMENTATION_OF" + comment: "Documents changes, not in production release." + - pattern: "3rdparty/curl/LICENSES/**" + reason: "DOCUMENTATION_OF" + comment: "Copies of various licenses, not in production release." +curations: + license_findings: + - path: "3rdparty/paho.mqtt.c/notice.html" + detected_license: "LicenseRef-scancode-eclipse-sua-2011" + concluded_license: "EPL-2.0" + reason: "NOT_DETECTED" + comment: "Correct format of the EPL license details not provided in notice.html file" + - path: "3rdparty/paho.mqtt.cpp/notice.html" + detected_license: "LicenseRef-scancode-eclipse-sua-2011" + concluded_license: "EPL-1.0" + reason: "NOT_DETECTED" + comment: "Correct format of the EPL license details not provided in notice.html file" + - path: "3rdparty/openssl/crypto/bio/bio_print.c" + detected_license: "LicenseRef-scancode-snprintf" + concluded_license: "Apache-2.0" + reason: "DOCUMENTATION_OF" + comment: "Documents that this particular code section is based on code written by Patrick Powell ." + - path: "3rdparty/glib/glib/gbsearcharray.h" + detected_license: "LicenseRef-scancode-tim-janik-2003" + concluded_license: "LGPL-2.1-or-later" + reason: "DOCUMENTATION_OF" + comment: "Documents that this code section is Copyright (C) 2000-2003 Tim Janik, redistribution and modification is permitted with provided disclaimer." + - path: "3rdparty/glib/gio/gdbusconnection.c" + detected_license: "GPL-2.0-only" + concluded_license: "LGPL-2.1-or-later" + reason: "DOCUMENTATION_OF" + comment: "Documents licenses used in other library sources." + - path: "3rdparty/glib/m4macros/glib-gettext.m4" + detected_license: "GPL-1.0-or-later WITH LicenseRef-scancode-autoconf-simple-exception-2.0" + concluded_license: "LGPL-2.1-or-later" + reason: "DOCUMENTATION_OF" + comment: "Documents an exception to GNU General Public License." + - path: "3rdparty/glib/m4macros/glib-gettext.m4" + detected_license: "GPL-1.0-or-later" + concluded_license: "LGPL-2.1-or-later" + reason: "CODE" + comment: "License mentioned in code." + - path: "3rdparty/glib/glib/gchecksum.c" + detected_license: "LicenseRef-scancode-pycrypto" + concluded_license: "LGPL-2.1-or-later" + reason: "DOCUMENTATION_OF" + comment: "Documents the origin of this code section (Copyright (C) 1995, A.M. Kuchling)." + - path: "3rdparty/spdlog/include/spdlog/fmt/bundled/fmt.license.rst" + detected_license: "Apache-2.0 WITH NOASSERTION" + concluded_license: "MIT" + reason: "DOCUMENTATION_OF" + comment: "Additional details provided on this source code being embedded in another machine-executable object form of source code." + - path: "3rdparty/spdlog/include/spdlog/fmt/bundled/format.h" + detected_license: "Apache-2.0 WITH NOASSERTION" + concluded_license: "MIT" + reason: "DOCUMENTATION_OF" + comment: "Additional details provided on this source code being embedded in another machine-executable object form of source code." + - path: "3rdparty/curl/LICENSES/GPL-3.0-or-later.txt" + detected_license: "GPL-3.0-only" + concluded_license: "curl" + reason: "DOCUMENTATION_OF" + comment: "Copy of GNU GENERAL PUBLIC LICENSE." + - path: "3rdparty/curl/scripts/copyright.pl" + detected_license: "GPL-3.0-or-later" + concluded_license: "curl" + reason: "CODE" + comment: "License name used in script." + - path: "3rdparty/curl/scripts/copyright.pl" + detected_license: "GPL-1.0-or-later" + concluded_license: "curl" + reason: "CODE" + comment: "License name used in script." + - path: "3rdparty/curl/m4/ax_compile_check_sizeof.m4" + detected_license: "GPL-3.0-or-later" + concluded_license: "curl" + reason: "DOCUMENTATION_OF" + comment: "Documents code tool provided under the GPL-3.0-or-later license (Copyright (c) 2008 Kaveh Ghazi , Copyright (c) 2017 Reini Urban )" + - path: "3rdparty/glib/gobject/glib-genmarshal.in" + detected_license: "GPL-1.0-or-later" + concluded_license: "LGPL-2.1-or-later" + reason: "CODE" + comment: "GNU General Public License mentioned in string." + - path: "3rdparty/openssl/crypto/aes/asm/bsaes-armv7.pl" + detected_license: " GPL-1.0-or-later" + concluded_license: "Apache-2.0" + reason: "DOCUMENTATION_OF" + comment: "Documents that specific modes/adaptations by Ard Biesheuvel of Linaro are licensed under GPL, the module is dual-licensed under OpenSSL and CRYPTOGAMS license." + - path: "3rdparty/glib/glib-gettextize.in" + detected_license: "GPL-2.0-or-later" + concluded_license: "LGPL-2.1-or-later" + reason: "DOCUMENTATION_OF" + comment: "Documents permission to use under GPL, code used to 'internationalize' packages using gettext." + - path: "3rdparty/glib/glib/gen-unicode-tables.pl" + detected_license: "GPL-2.0-or-later" + concluded_license: "LGPL-2.1-or-later" + reason: "DOCUMENTATION_OF" + comment: "Documents permission to use this perl script under GPL." + - path: "3rdparty/glib/glib/gtree.c" + detected_license: "LicenseRef-scancode-proprietary-license" + concluded_license: "LGPL-2.1-or-later" + reason: "INCORRECT" + comment: "Invalid detection of a license issue." + - path: "3rdparty/glib/gobject/glib-mkenums.in" + detected_license: "GPL-2.0-only OR GPL-3.0-only" + concluded_license: "LGPL-2.1-or-later" + reason: "CODE" + comment: "GNU General Public License mentioned in string." + - path: "3rdparty/glib/gobject/glib-mkenums.in" + detected_license: "GPL-3.0-only" + concluded_license: "LGPL-2.1-or-later" + reason: "CODE" + comment: "GNU General Public License mentioned in string." + - path: "3rdparty/openssl/crypto/camellia/asm/cmll-x86.pl" + detected_license: "GPL-2.0-or-later OR LGPL-2.1-or-later OR MPL-1.1 OR BSD-3-Clause" + concluded_license: "Apache-2.0" + reason: "DOCUMENTATION_OF" + comment: "Documents permission to use this perl script under GPL & LGPL." + - path: "3rdparty/openssl/crypto/camellia/asm/cmll-x86_64.pl" + detected_license: "GPL-2.0-or-later OR LGPL-2.1-or-later OR MPL-1.1 OR BSD-3-Clause" + concluded_license: "Apache-2.0" + reason: "DOCUMENTATION_OF" + comment: "Documents permission to use this perl script under GPL & LGPL." + - path: "3rdparty/openssl/crypto/sha/asm/sha256-armv4.pl" + detected_license: "OpenSSL OR BSD-3-Clause OR GPL-1.0-or-later" + concluded_license: "Apache-2.0" + reason: "DOCUMENTATION_OF" + comment: "Documents that this module is dual-licensed under OpenSSL and CRYPTOGAMS licenses." + - path: "3rdparty/openssl/crypto/sha/asm/sha512-armv4.pl" + detected_license: "OpenSSL OR BSD-3-Clause OR GPL-1.0-or-later" + concluded_license: "Apache-2.0" + reason: "DOCUMENTATION_OF" + comment: "Documents that this module is dual-licensed under OpenSSL and CRYPTOGAMS licenses." + - path: "3rdparty/openssl/crypto/sha/asm/sha512-armv8.pl" + detected_license: "OpenSSL OR BSD-3-Clause OR GPL-1.0-or-later OR GPL-2.0-only" + concluded_license: "Apache-2.0" + reason: "DOCUMENTATION_OF" + comment: "Documents that this module is dual-licensed under OpenSSL and CRYPTOGAMS licenses." + packages: + - id: "SpdxDocumentFile:IBM Corp.:paho.mqtt.c" + curations: + comment: "The license is not correctly detected, the concluded license for paho.mqtt.c is EPL-2.0." + concluded_license: "EPL-2.0" + - id: "SpdxDocumentFile::paho.mqtt.cpp" + curations: + comment: "The license is not correctly detected, the concluded license for paho.mqtt.cpp is EPL-1.0." + concluded_license: "EPL-1.0" diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..f405f92 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,18 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**", + "${workspaceFolder}/3rdparty" + ], + "defines": [], + "compilerPath": "/usr/bin/gcc", + "cStandard": "gnu17", + "cppStandard": "gnu++17", + "intelliSenseMode": "linux-gcc-x64", + "configurationProvider": "ms-vscode.cmake-tools" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..a364942 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,29 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Launch SDV update agent", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build_amd64/src/sdv-self-update-agent", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [ + { + "name": "LD_LIBRARY_PATH", + "value": "${workspaceFolder}/dist_amd64/lib" + } + ], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6ac98cf --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,65 @@ +{ + "files.associations": { + "string": "cpp", + "cstdlib": "cpp", + "list": "cpp", + "*.tcc": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "cctype": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "condition_variable": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "map": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "ratio": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "fstream": "cpp", + "future": "cpp", + "initializer_list": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "mutex": "cpp", + "new": "cpp", + "ostream": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "thread": "cpp", + "cinttypes": "cpp", + "typeinfo": "cpp", + "any": "cpp", + "forward_list": "cpp", + "set": "cpp", + "unordered_set": "cpp", + "iomanip": "cpp", + "variant": "cpp" + } +} \ No newline at end of file diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt new file mode 100644 index 0000000..4dc3fcf --- /dev/null +++ b/3rdparty/CMakeLists.txt @@ -0,0 +1,27 @@ +set(PAHO_ENABLE_TESTING FALSE CACHE BOOL "") +set(PAHO_BUILD_STATIC FALSE CACHE BOOL "") +set(PAHO_BUILD_SHARED TRUE CACHE BOOL "") +set(PAHO_BUILD_WITH_SSL FALSE CACHE BOOL "") +add_subdirectory(./paho.mqtt.c) + +set(PAHO_MQTT_C_LIBRARIES ${CMAKE_BINARY_DIR}/3rdparty/paho.mqtt.c/src/libpaho-mqtt3c.so) +set(PAHO_MQTT_C_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/3rdparty/paho.mqtt.c/src) + +add_subdirectory(./paho.mqtt.cpp) + +add_dependencies(paho-mqttpp3 paho-mqtt3a) +add_dependencies(paho-mqttpp3 paho-mqtt3c) + +add_library(mini-yaml SHARED ./mini-yaml/yaml/Yaml.cpp) +install(TARGETS mini-yaml LIBRARY DESTINATION lib) + +set(CMAKE_PREFIX_PATH ${CMAKE_BINARY_DIR}/lib/pkgconfig) +find_package(OpenSSL REQUIRED) +set(CURL_USE_OPENSSL ON) +set(BUILD_SHARED_LIBS ON) +set(BUILD_CURL_EXE OFF) +add_subdirectory(curl) + +if(SDV_SUA_BUILD_TESTS) + add_subdirectory(googletest) +endif() \ No newline at end of file diff --git a/3rdparty/curl b/3rdparty/curl new file mode 160000 index 0000000..18383fb --- /dev/null +++ b/3rdparty/curl @@ -0,0 +1 @@ +Subproject commit 18383fbf72b31b9ae58f17df8e75158a818c31eb diff --git a/3rdparty/glib b/3rdparty/glib new file mode 160000 index 0000000..c1a4aca --- /dev/null +++ b/3rdparty/glib @@ -0,0 +1 @@ +Subproject commit c1a4aca7433907555beb814210c5034b6bd3ce82 diff --git a/3rdparty/googletest b/3rdparty/googletest new file mode 160000 index 0000000..9c33214 --- /dev/null +++ b/3rdparty/googletest @@ -0,0 +1 @@ +Subproject commit 9c332145b71c36a5bad9688312c79184f98601ff diff --git a/3rdparty/meson-cross-file-aarch64.txt b/3rdparty/meson-cross-file-aarch64.txt new file mode 100644 index 0000000..d0078d0 --- /dev/null +++ b/3rdparty/meson-cross-file-aarch64.txt @@ -0,0 +1,17 @@ +[host_machine] +system = 'linux' +cpu_family = 'aarch64' +cpu = 'aarch64' +endian = 'little' + +[properties] +c_args = [] +c_link_args = [] + +[binaries] +c = 'aarch64-linux-gnu-gcc-9' +cpp = 'aarch64-linux-gnu-g++-9' +ar = 'aarch64-linux-gnu-ar' +ld = 'aarch64-linux-gnu-ld' +objcopy = 'aarch64-linux-gnu-objcopy' +strip = 'aarch64-linux-gnu-strip' diff --git a/3rdparty/mini-yaml b/3rdparty/mini-yaml new file mode 160000 index 0000000..22d3dcf --- /dev/null +++ b/3rdparty/mini-yaml @@ -0,0 +1 @@ +Subproject commit 22d3dcf5684a11f9c0508c1ad8b3282a1d888319 diff --git a/3rdparty/openssl b/3rdparty/openssl new file mode 160000 index 0000000..a275afc --- /dev/null +++ b/3rdparty/openssl @@ -0,0 +1 @@ +Subproject commit a275afc527d05b5187b457bdbcd0e1dcb18839f1 diff --git a/3rdparty/paho.mqtt.c b/3rdparty/paho.mqtt.c new file mode 160000 index 0000000..7c0735a --- /dev/null +++ b/3rdparty/paho.mqtt.c @@ -0,0 +1 @@ +Subproject commit 7c0735a5f02052c275baddb4f01f1f4dabdfed20 diff --git a/3rdparty/paho.mqtt.cpp b/3rdparty/paho.mqtt.cpp new file mode 160000 index 0000000..2ff3d15 --- /dev/null +++ b/3rdparty/paho.mqtt.cpp @@ -0,0 +1 @@ +Subproject commit 2ff3d155dcd10564f1816675789284b4efd79eb7 diff --git a/3rdparty/spdlog b/3rdparty/spdlog new file mode 160000 index 0000000..4f80077 --- /dev/null +++ b/3rdparty/spdlog @@ -0,0 +1 @@ +Subproject commit 4f800773393d3ebac13c1fcd946a315d4d72bcd9 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..fd85e62 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.10) +project(sdv-self-update-agent) + +set(CMAKE_CXX_STANDARD 14) + +set(CMAKE_SKIP_BUILD_RPATH TRUE) +set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) +set(CMAKE_INSTALL_RPATH ../lib) + +set(SDV_SUA_BUILD_TESTS TRUE) + +add_subdirectory(3rdparty) +add_subdirectory(src) + +if(SDV_SUA_BUILD_TESTS) + add_subdirectory(utest) +endif() diff --git a/Dockerfile.amd64 b/Dockerfile.amd64 new file mode 100644 index 0000000..4a700ed --- /dev/null +++ b/Dockerfile.amd64 @@ -0,0 +1,71 @@ +# Dockerfile for building multi-arch images and for runtime of the SDV Self Update Agent + +FROM --platform=${BUILDPLATFORM} ubuntu AS build + +ARG TARGETPLATFORM + +#uncomment when building on OSD6 environment +#ENV http_proxy "http://172.17.0.1:3128" +#ENV https_proxy "http://172.17.0.1:3128" + +RUN apt-get update && apt-get -y install \ + autoconf binutils cmake file \ + gcc g++ git libtool make \ + build-essential libcurl4-openssl-dev \ + binutils-aarch64-linux-gnu gcc-9-aarch64-linux-gnu g++-9-aarch64-linux-gnu \ + python3 python3-pip python3-setuptools python3-wheel ninja-build meson \ + libselinux1-dev libmount-dev libmount1 libblkid-dev \ + ca-certificates + +# Copy the sources to the build container +COPY src /work/src +COPY cmake /work/cmake +COPY 3rdparty /work/3rdparty +COPY utest /work/utest +COPY scripts /work/scripts +COPY CMakeLists.txt /work/CMakeLists.txt + +# Build the Self Update Agent binaries and dependencies +RUN cd /work \ + && ./scripts/build_openssl_amd64.sh > /dev/null \ + && ./scripts/build_glib_amd64.sh > /dev/null \ + && cd build_amd64 \ + && 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 \ + -DCMAKE_BUILD_TYPE=Release \ + .. \ + && make \ + && make install + +#Define RUNTIME environment, the final image +FROM --platform=${TARGETPLATFORM} scratch as runtime +ARG TARGETPLATFORM +COPY --from=build /work/dist_amd64/bin/sdv-self-update-agent /sua/bin/sdv-self-update-agent +COPY --from=build /work/dist_amd64/lib/libpaho-mqttpp3.so.1 /sua/lib/ +COPY --from=build /work/dist_amd64/lib/libpaho-mqtt3a.so.1 /sua/lib/ +COPY --from=build /work/dist_amd64/lib/libpaho-mqtt3c.so.1 /sua/lib/ +COPY --from=build /work/dist_amd64/lib/libmini-yaml.so /sua/lib/ +COPY --from=build /work/dist_amd64/lib/libcurl.so /sua/lib/ +COPY --from=build /work/dist_amd64/lib/libcrypto.so.3 /sua/lib/ +COPY --from=build /work/dist_amd64/lib/libssl.so.3 /sua/lib/ +COPY --from=build /work/build_amd64/glib/gio/libgio-2.0.so.0 /sua/lib/ +COPY --from=build /work/build_amd64/glib/gobject/libgobject-2.0.so.0 /sua/lib/ +COPY --from=build /work/build_amd64/glib/glib/libglib-2.0.so.0 /sua/lib/ +COPY --from=build /work/build_amd64/glib/gmodule/libgmodule-2.0.so.0 /sua/lib/ +COPY --from=build /work/build_amd64/glib/subprojects/libffi/src/libffi.so.7 /sua/lib/ +COPY --from=build /work/build_amd64/glib/subprojects/zlib-1.2.11/libz.so /sua/lib/ +COPY --from=build /etc/ssl/certs /etc/ssl/certs/ +COPY --from=build /usr/share/ca-certificates /usr/share/ca-certificates +COPY --from=build /lib64/ld-linux-x86-64.so.2 /lib64/ld-linux-x86-64.so.2 +COPY --from=build /lib/x86_64-linux-gnu/libgcc_s.so.1 /lib/libgcc_s.so.1 +COPY --from=build /lib/x86_64-linux-gnu/libstdc++.so.6 /lib/libstdc++.so.6 +COPY --from=build /lib/x86_64-linux-gnu/libc.so.6 /lib/libc.so.6 +COPY --from=build /lib/x86_64-linux-gnu/libz.so.1 /lib/libz.so.1 +COPY --from=build /lib/x86_64-linux-gnu/libm.so.6 /lib/libm.so.6 + +WORKDIR /sua/bin +ENV LD_LIBRARY_PATH ../lib +CMD ["./sdv-self-update-agent"] diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 new file mode 100644 index 0000000..e61dbf3 --- /dev/null +++ b/Dockerfile.arm64 @@ -0,0 +1,70 @@ +# Dockerfile for building multi-arch images and for runtime of the SDV Self Update Agent + +FROM --platform=${BUILDPLATFORM} ubuntu AS build + +ARG TARGETPLATFORM + +#uncomment when building on OSD6 environment +#ENV http_proxy "http://172.17.0.1:3128" +#ENV https_proxy "http://172.17.0.1:3128" + +RUN apt-get update && apt-get -y install \ + autoconf binutils cmake file \ + gcc g++ git libtool make \ + build-essential libcurl4-openssl-dev \ + binutils-aarch64-linux-gnu gcc-9-aarch64-linux-gnu g++-9-aarch64-linux-gnu \ + python3 python3-pip python3-setuptools python3-wheel ninja-build meson \ + libselinux1-dev libmount-dev libmount1 libblkid-dev \ + ca-certificates + +# Copy the sources to the build container +COPY src /work/src +COPY cmake /work/cmake +COPY 3rdparty /work/3rdparty +COPY utest /work/utest +COPY scripts /work/scripts +COPY CMakeLists.txt /work/CMakeLists.txt + +# Build the Self Update Agent binaries and dependencies +RUN cd /work \ + && ./scripts/build_openssl_arm64.sh > /dev/null \ + && ./scripts/build_glib_arm64.sh > /dev/null \ + && cd build_arm64 \ + && 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 \ + -DCMAKE_BUILD_TYPE=Release \ + .. \ + && make \ + && make install + +#Define RUNTIME environment, the final image +#FROM --platform=${TARGETPLATFORM} scratch as runtime +#ARG TARGETPLATFORM +#COPY --from=build /work/dist_arm64/bin/sdv-self-update-agent /sua/bin/sdv-self-update-agent +#COPY --from=build /work/dist_arm64/lib/libpaho-mqttpp3.so.1 /sua/lib/ +#COPY --from=build /work/dist_arm64/lib/libpaho-mqtt3a.so.1 /sua/lib/ +#COPY --from=build /work/dist_arm64/lib/libpaho-mqtt3c.so.1 /sua/lib/ +#COPY --from=build /work/dist_arm64/lib/libmini-yaml.so /sua/lib/ +#COPY --from=build /work/dist_arm64/lib/libcurl.so /sua/lib/ +#COPY --from=build /work/dist_arm64/lib/libcrypto.so.3 /sua/lib/ +#COPY --from=build /work/dist_arm64/lib/libssl.so.3 /sua/lib/ +#COPY --from=build /work/build_arm64/glib/gio/libgio-2.0.so.0 /sua/lib/ +#COPY --from=build /work/build_arm64/glib/gobject/libgobject-2.0.so.0 /sua/lib/ +#COPY --from=build /work/build_arm64/glib/glib/libglib-2.0.so.0 /sua/lib/ +#COPY --from=build /work/build_arm64/glib/gmodule/libgmodule-2.0.so.0 /sua/lib/ +#COPY --from=build /work/build_arm64/glib/subprojects/libffi/src/libffi.so.7 /sua/lib +#COPY --from=build /work/build_arm64/glib/subprojects/zlib-1.2.11/libz.so /sua/lib/ +#COPY --from=build /etc/ssl/certs /etc/ssl/certs/ +#COPY --from=build /usr/share/ca-certificates /usr/share/ca-certificates +#COPY --from=build /usr/aarch64-linux-gnu/lib/ld-linux-aarch64.so.1 /lib/ld-linux-aarch64.so.1 +#COPY --from=build /usr/aarch64-linux-gnu/lib/libstdc++.so.6 /lib/ +#COPY --from=build /usr/aarch64-linux-gnu/lib/libgcc_s.so.1 /lib/ +#COPY --from=build /usr/aarch64-linux-gnu/lib/libc.so.6 /lib/ +#COPY --from=build /usr/aarch64-linux-gnu/lib/libm.so.6 /lib/ + +#WORKDIR /sua/bin +#ENV LD_LIBRARY_PATH ../lib +#CMD ["./sdv-self-update-agent"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4947287 --- /dev/null +++ b/LICENSE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/README.md b/README.md index 8b13789..be30818 100644 --- a/README.md +++ b/README.md @@ -1 +1,149 @@ +# License +Apache 2.0 + +# Contribution +Follow guidelines from Eclipse Leda: https://github.com/SoftwareDefinedVehicle/sdv-edge-distro-yocto/blob/main/README.md + +# Security Policy +This project implements the Eclipse Foundation Security Policy: https://www.eclipse.org/security + +# Reporting a Vulnerability +Please report vulnerabilities to the Eclipse Foundation Security Team at security@eclipse.org + +# Data privacy notice: +Data privacy notice is here: [link](docs/data-privacy-notice.md) + +# SDV Self Update Agent +The self update agent (SUA) is a component responsible for the OS Update process. +SUA is communicating on MQTT interface via usage of defined messages. Internally, SUA uses [RAUC](https://rauc.io/) to perform the update. + +Following sequence diagram shows the happy path example of communication between components. + +```mermaid +sequenceDiagram + participant m as MQTT Broker + participant s as SUA + participant r as RAUC + s -->> m: connect + loop Wait for message: selfupdate/desiredstate + Note left of s: Initial start + s ->> m: selfupdate/currentstate + Note left of s: Trigger for OTA + m ->> s: selfupdate/desiredstate + s ->> m: selfupdate/desiredstatefeedback: downloading 0% + s ->> s: download bundle + s ->> m: selfupdate/desiredstatefeedback: downloading 51% + s ->> r: install + s ->> m: selfupdate/desiredstatefeedback: installing 0% + r ->> r: install + r ->> s: share progress: e.g. 51% + s ->> m: selfupdate/desiredstatefeedback: installing 51% + r ->> s: installation ready + s ->> m: selfupdate/desiredstatefeedback: installed + s ->> m: selfupdate/desiredstatefeedback: idle + end +``` +MQTT messages are specified as follows: + +## selfupdate/desiredstate +| Topic | Direction | Description | +|-------| -------- | ----------- | +| 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. | +``` +apiVersion: "sdv.eclipse.org/v1" +kind: SelfUpdateBundle +metadata: + name: self-update-bundle-example +spec: + bundleName: swdv-arm64-build42 + bundleVersion: v1beta3 + bundleDownloadUrl: https://example.com/repository/base/ + bundleTarget: base +``` + +## selfupdate/currentstate +| Topic| Direction | Description | +|------| -------- | ----------- | +| selfupdate/currentstate | OUT | This message is being sent once, on SUA start. It contains information about currently installed OS version. +``` +apiVersion: "sdv.eclipse.org/v1" +kind: SelfUpdateBundle +metadata: + name: self-update-bundle-example +spec: + bundleVersion: v1beta3 +``` + +## selfupdate/desiredstatefeedback +| Topic| Direction | Description | +|------| -------- | ----------- | +| selfupdate/desiredstatefeedback | OUT | This message is being sent by SUA to share current progress of triggered update process. This is the *OUT* counterpart of *selfupdate/desiredstate* input message. +``` +apiVersion: "sdv.eclipse.org/v1" +kind: SelfUpdateBundle +metadata: + name: self-update-bundle-example +spec: + bundleName: swdv-arm64-build42 + bundleVersion: v1beta3 + bundleDownloadUrl: https://example.com/repository/base/ + bundleTarget: base +state: + name: "idle|installing|etc." + progress: 0|51|99|etc., + techCode: 0|1|5|etc., + message: "Cannot download from url|Bundle already installed|etc." +``` + +### state enum + +State name field can have one of following values: + +| State | Description | Additional payload data | +| ---------- | ---------- | ---------- | +| uninitialized | When the SUA is not configured yet | - | +| idle | Configured and waiting for messages | - | +| downloading | Downloading the bundle file | progress | +| installing | Performing installation | progress | +| installed | Installation process was successful, new OS version is installed on inactive disc Slot. **Important: to finish the OTA process, reboot is required, and it shall be performed by Vehicle Update Manager.** | - | +| failed | Error occurred | techCode | + +### techCode values +techCode field is providing additional details to the state value. It is especially useful for the **failed** state, as it can specify the reason of failure. + +| Value | Description | +| ---- | ---------- | +| 0 | OK, no error| +| 1001 | Download failed | +| 2001 | Invalid Bundle| +| 3001 | Installation failed | +| 4001 | Update rejected, bundle version same as current OS version | +| 5001 | Unknown Error | + +During integration testing the set of technical codes will be extended. + +# Checkout +SUA links to some 3rd party libraries, which are fetched as submodules, therefore the cloning shall be performed with recursive option: + +``` +git clone --recursive https://github.boschdevcloud.com/sdv/sdv-self-update-agent.git +``` +or if was cloned non recursively +``` +git submodule init +git submodule update +``` + +# HowTo Build +Instructions for building are available on: [link](docs/building/README.md) + +# HowTo Deploy +Instructions for deploying are available on: [link](docs/deploying/README.md) + +# HowTo Test +Instructions for testing are available on: [link](docs/testing/README.md) + + + + diff --git a/clang-format.sh b/clang-format.sh new file mode 100755 index 0000000..525e741 --- /dev/null +++ b/clang-format.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +find src -iname *.h -o -iname *.cpp | xargs clang-format -i diff --git a/cmake/linux/amd64/toolchain.cmake b/cmake/linux/amd64/toolchain.cmake new file mode 100644 index 0000000..60a0f55 --- /dev/null +++ b/cmake/linux/amd64/toolchain.cmake @@ -0,0 +1,7 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_VERSION 1) + +set(CMAKE_C_COMPILER gcc) +set(CMAKE_CXX_COMPILER g++) + +set(SUA_PLATFORM_LIBS resolv blkid selinux) diff --git a/cmake/linux/arm64/toolchain.cmake b/cmake/linux/arm64/toolchain.cmake new file mode 100644 index 0000000..01158e8 --- /dev/null +++ b/cmake/linux/arm64/toolchain.cmake @@ -0,0 +1,7 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_VERSION 1) + +set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc-9) +set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++-9) + +set(SUA_PLATFORM_LIBS resolv) diff --git a/docs/building/README.md b/docs/building/README.md new file mode 100644 index 0000000..0fc4372 --- /dev/null +++ b/docs/building/README.md @@ -0,0 +1,159 @@ +# Building + +The SUA can be used in both forms: + +- as a native application, +- as an image running inside of a container. + +This section describes the building steps for both variants. + +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. + +# Native + +## Install prerequisites + +To be able to compile, following libraries and tools have to be installed: + +### Toolchain + +``` +sudo apt install binutils cmake make autoconf file libtool git build-essential libcurl4-openssl-dev libselinux1-dev libmount-dev libmount1 libblkid-dev +``` + +### Compilers (for x86_64) + +``` +sudo apt install gcc g++ +``` + +### Compilers (for Raspberry Pi board) + +``` +sudo apt install gcc-9-aarch64-linux-gnu g++-9-aarch64-linux-gnu +``` + +### Build tool Meson for glib + +``` +sudo apt-get install python3 python3-pip python3-setuptools python3-wheel ninja-build meson +pip3 install meson +``` + +## Build for amd64 + +``` +./scripts/build_openssl_amd64.sh +./scripts/build_glib_amd64.sh +cd build_amd64 +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 .. +make install +``` + +## Build for aarch64 (Raspberry Pi) + +``` +./scripts/build_openssl_arm64.sh +./scripts/build_glib_arm64.sh +cd build_arm64 +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 .. +make install +``` + +## Run + +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: + +``` +cd dist_amd64/bin +LD_LIBRARY_PATH=../lib ./sdv-self-update-agent +``` + +``` +cd dist_amd64/bin +LD_LIBRARY_PATH=../lib ./sdv-self-update-agent -p /data/download +``` + +## Hint about usage of devcontainer + +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*. + +### Windows + +If the docker and VS proxy settings are correctly set, the .devcontainer environment shall work on windows out of the box. + +### Fix for OSD6 Linux distribution + +For the case when using local VS Code (on OSD6) and opening in the remote container, the adjustment of proxy settings is necessary: + +``` +vim /opt/osd/proxy/config.ini +``` + +and then append additional server address for binding: + +``` +[server] +binds = 127.0.0.1:3128,172.17.0.1:3128 +``` + +After this restart OSD proxy + +``` +osd-proxy-restart +``` + +Then in the .devcontainer/Dockerfile activate those lines + +``` +ENV http_proxy "http://172.17.0.1:3128" +ENV https_proxy "http://172.17.0.1:3128" +``` + +And finally trigger "Rebuild in Container" command from VSCode + +# Containerized + +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. + +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. + +## Build multi-arch image + +### Build and load to local docker registry + +Useful for testing on host machine: (specify only the host's architecture): + +``` +docker buildx build -f Dockerfile.arm64 --progress plain --platform "linux/arm64" -t sua:arm64 --load . +docker buildx build -f Dockerfile.amd64 --progress plain --platform "linux/amd64" -t sua:amd64 --load . +``` + +### Build and push to remote container registry (ghrc) + +``` +docker buildx build -f Dockerfile.arm64 --progress plain --platform "linux/arm64" -t ghcr.io/softwaredefinedvehicle/sdv-self-update-agent/sua:testing_arm64 --push . +docker buildx build -f Dockerfile.amd64 --progress plain --platform "linux/amd64" -t ghcr.io/softwaredefinedvehicle/sdv-self-update-agent/sua:testing_amd64 --push . +``` + +### Create multiarch image using remote container registry (ghrc) + +``` +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 +``` + +## Run + +``` +docker run -it sua:latest +``` + +## Hint + +When running with proxy (corporate for example), it is helpful to uncomment following lines in the Dockerfile to configure the proxy properly + +``` +#uncomment when building on OSD6 environment +ENV http_proxy "http://172.17.0.1:3128" +ENV https_proxy "http://172.17.0.1:3128" +``` diff --git a/docs/data-privacy-notice.md b/docs/data-privacy-notice.md new file mode 100644 index 0000000..f66542d --- /dev/null +++ b/docs/data-privacy-notice.md @@ -0,0 +1,41 @@ +### Privacy Customer Information + +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. + +### Your Role + +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. + +### Where may the processing of personal related data be relevant? + +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. + +However, you are responsible for the compliant handling of the data in accordance with the applicable local law. + +### What have we done to make the software data protection friendly? + +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. + +## Authentication +SharedAccessKey as part of the connection string or +Device key and certificated. Depends how the device was registered in Azure IoT Hub +Password control +Customer can configure the local MQTT broker to require user/password authentication. The configuration is preinstalled on the device. +Encryption +Device-to-Cloud and Cloud-to-Device communication is done using MQTT over TLS. +## Deletion possibility +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. + +## 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. + +## 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. + +### 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: + +Disable system user (root) password and login +Disable SSH login with password +Adding a new Linux user with restricted permissions +Adding SSH key based authentication +Kubernetes Resources: Secrets +Device Identity Certificates for Cloud Connection +Access credentials for private Container Registries diff --git a/docs/deploying/README.md b/docs/deploying/README.md new file mode 100644 index 0000000..b7f0d1d --- /dev/null +++ b/docs/deploying/README.md @@ -0,0 +1,21 @@ +# Deployment + +# Native + +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). + +# Container + +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. + +Apply the yaml config with... + +``` +kubectl apply -f sua-service.yaml +``` + +The yaml mounts system_bus_socket to allow access to the host dbus and a shared volume for holding the downloaded update bundle. + +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. + +There are also various port settings. You may need to adjust these values to match the setup that you are using. diff --git a/docs/deploying/sua-service.yaml b/docs/deploying/sua-service.yaml new file mode 100644 index 0000000..373fe83 --- /dev/null +++ b/docs/deploying/sua-service.yaml @@ -0,0 +1,54 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: suaservice + labels: + app: suaservice +spec: + replicas: 1 + selector: + matchLabels: + app: suaservice + template: + metadata: + labels: + app: suaservice + spec: + containers: + - name: suaservice + image: ghcr.io/softwaredefinedvehicle/sdv-self-update-agent/sua:v0.1.16 + command: ["/app/sdv-self-update-agent"] + args: ["-p", "/data/selfupdates"] + imagePullPolicy: IfNotPresent + volumeMounts: + - mountPath: /var/run/dbus/system_bus_socket + name: system-dbus-socket + readOnly: true + - mountPath: /RaucUpdate + name: shared-rauc-vol + ports: + - name: default + containerPort: 50052 + protocol: TCP + imagePullSecrets: + - name: ghcr-user + volumes: + - hostPath: + path: /var/run/dbus/system_bus_socket + name: system-dbus-socket + - hostPath: + path: /data/selfupdates + name: shared-rauc-vol +--- +apiVersion: v1 +kind: Service +metadata: + name: suaservice-nodeport +spec: + type: NodePort + selector: + app: suaservice + ports: + - port: 50052 + targetPort: 50052 + nodePort: 30052 diff --git a/docs/testing/README.md b/docs/testing/README.md new file mode 100644 index 0000000..12fb3e9 --- /dev/null +++ b/docs/testing/README.md @@ -0,0 +1,138 @@ +# Testing + +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: + +# Native + +For the native variant all tools have to be installed on the host machine: + +## Preconditions + +1. Install the mosquitto server and client + +``` +sudo apt-get update +sudo apt-get install mosquitto +sudo apt-get install mosquitto-clients +``` + +2. Ensure the python3 is installed. + +## Start MQTT broker + +``` +mosquitto +``` + +## Adjust the host ip in code + +Set the IP of the machine where the mosquitto broker is running, for the simplest case it would be the `localhost`: + +``` + SoftwareUpdateAgent::SoftwareUpdateAgent() + { + sua::MqttConfiguration conf; + conf.brokerHost = "localhost" +``` + +## Run native application + +``` +./sdv-self-update-agent +``` + +## Host test bundle file + +Ensure that the proper test bundle is put into hosted directory. Adjust the IP and Port as needed. + +``` +cd docs/testing/fileserver/bundle +python3 -m http.server --bind 127.0.0.1 5555 +``` + +## Simulate sending the MQTT Start messages + +The content of the yaml file shall be adjusted, so that url of hosted bundle is valid. + +``` +mosquitto_pub -t "selfupdate/desiredstate" -f docs/testing/mqtt/start.yaml +``` + +## Subscribe to MQTT feedback messages + +To test if the SUA is behaving correctly and sending proper mqtt messages, subscribe to the topics: + +``` +mosquitto_sub -t "selfupdate/currentstate" +mosquitto_sub -t "selfupdate/desiredstatefeedback" +``` + +# Container + +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. + +## Create the network + +``` +docker network create -d bridge my-network +``` + +## Deploy Mosquitto broker + +``` +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 +``` + +Hint: it is required to use the absolute path to the mosquitto configuration file. + +## Deploy python http server + +``` +cd docs/testing/fileserver +docker build -t host . +docker run -it --network=my-network -p 5555:5555 --name fileserver host +``` + +The bundle file will be available under: `fileserver:5555/bundle` url, so the value in `start.yaml` shall be adjusted. + +## Deploy SUA + +``` +docker run -it --network=my-network sua:latest +``` + +Hint: before building and running sua, ensure that the broker host ia having the same value as the name parameter, specified for mosquitto broker: + + ``` + SoftwareUpdateAgent::SoftwareUpdateAgent() + { + sua::MqttConfiguration conf; + conf.brokerHost = "mosquitto" + ... +``` + +# HowTo subscribe to relevant messages + +``` +mosquitto_sub -t "selfupdate/selfupdate/desiredstatefeedback" -h ipAddress_of_mosquitto_container +mosquitto_sub -t "selfupdate/currentstate" -h ipAddress_of_mosquitto_container +``` + +From this tab console we can observe if the SUA is sending proper messages. + +Hint: to figure out the the ip address of mosquitto container, run following command: + +``` +docker container ls // copy the containerID of mosquitto container +docker container inspect containerID +``` + +and locate the `IPAddress` value. + +# HowTo send the Start signal to trigger the process + +``` +mosquitto_pub -t "selfupdate/desiredstate" -f docs/testing/mqtt/start.yaml -h ipAddress_of_mosquitto_container +``` + +After sending this signal, you should be able to observe the SUA behavior on the console. diff --git a/docs/testing/fileserver/Dockerfile b/docs/testing/fileserver/Dockerfile new file mode 100644 index 0000000..a18a4b0 --- /dev/null +++ b/docs/testing/fileserver/Dockerfile @@ -0,0 +1,4 @@ +FROM python:latest +COPY bundle/ / +EXPOSE 5555 +CMD python3 -m http.server 5555 diff --git a/docs/testing/fileserver/bundle/bundle b/docs/testing/fileserver/bundle/bundle new file mode 100644 index 0000000..60b8c82 --- /dev/null +++ b/docs/testing/fileserver/bundle/bundle @@ -0,0 +1,232 @@ +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, + Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, + Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, + Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, + + Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, v + + Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, v + + Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, v + + + + + + + Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, + Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, + + + + Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, + + + + Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, Test file, to be replaced with real image, +Test file, to be replaced with real image, \ No newline at end of file diff --git a/docs/testing/mqtt/mosquitto.conf b/docs/testing/mqtt/mosquitto.conf new file mode 100644 index 0000000..936356f --- /dev/null +++ b/docs/testing/mqtt/mosquitto.conf @@ -0,0 +1,5 @@ +persistence true +persistence_location /mosquitto/data/ +log_dest file /mosquitto/log/mosquitto.log +allow_anonymous true +listener 1883 0.0.0.0 \ No newline at end of file diff --git a/docs/testing/mqtt/start.yaml b/docs/testing/mqtt/start.yaml new file mode 100644 index 0000000..3b9d41c --- /dev/null +++ b/docs/testing/mqtt/start.yaml @@ -0,0 +1,9 @@ +apiVersion: "sdv.eclipse.org/v1" +kind: SelfUpdateBundle +metadata: + name: self-update-bundle-example +spec: + bundleName: swdv-arm64-build42 + bundleVersion: v1beta3 + bundleDownloadUrl: http://127.0.0.1:5555/bundle + bundleTarget: base \ No newline at end of file diff --git a/project.spdx.yml b/project.spdx.yml new file mode 100644 index 0000000..c14b537 --- /dev/null +++ b/project.spdx.yml @@ -0,0 +1,138 @@ +SPDXID: "SPDXRef-DOCUMENT" +spdxVersion: "SPDX-2.2" +creationInfo: + created: "2022-08-04T12:00:00Z" + creators: + - "Organization: Robert Bosch GmbH" +name: "sdv-self-update-agent" +dataLicense: "CC0-1.0" +documentNamespace: "https://github.com/SoftwareDefinedVehicle/sdv-self-update-agent" +documentDescribes: +- "SPDXRef-Package-sdv-self-update-agent" +packages: +- SPDXID: "SPDXRef-Package-sdv-self-update-agent" + description: "Self update agent." + copyrightText: "Copyright 2022 Contributors to the Eclipse Foundation" + downloadLocation: "git+ssh://github.com/SoftwareDefinedVehicle/sdv-self-update-agent" + filesAnalyzed: false + homepage: "https://github.com/SoftwareDefinedVehicle/sdv-self-update-agent" + licenseConcluded: "Apache-2.0" + licenseDeclared: "Apache-2.0" + name: "sdv-self-update-agent" + originator: "Organization: Robert Bosch GmbH" +- SPDXID: "SPDXRef-Package-openssl" + description: "OpenSSL is a robust, commercial-grade, full-featured Open Source Toolkit for the Transport Layer Security (TLS) protocol formerly known as the Secure Sockets Layer (SSL) protocol. The protocol implementation is based on a full-strength general purpose cryptographic library, which can also be used stand-alone." + copyrightText: "Copyright (c) 1998-2022 The OpenSSL Project, Copyright (c) 1995-1998 Eric A. Young, Tim J. Hudson" + downloadLocation: "git+ssh://github.com/openssl/openssl.git" + filesAnalyzed: false + homepage: "https://www.openssl.org/" + licenseConcluded: "Apache-2.0" + licenseDeclared: "Apache-2.0" + packageFileName: "3rdparty/openssl" + name: "openssl" + originator: "Organization: OpenSSL Development Team" +- SPDXID: "SPDXRef-Package-spdlog" + description: "Very fast, header-only/compiled, C++ logging library." + copyrightText: "Copyright(c) 2015-present, Gabi Melman & spdlog contributors" + downloadLocation: "git+ssh://github.com/gabime/spdlog.git" + filesAnalyzed: false + homepage: "https://github.com/gabime/spdlog.git" + licenseConcluded: "MIT" + licenseDeclared: "MIT" + packageFileName: "3rdparty/spdlog" + name: "spdlog" + originator: "Person: Gabi Melman" +- SPDXID: "SPDXRef-Package-glib" + description: "GLib is the low-level core library that forms the basis for projects such as GTK and GNOME." + copyrightText: "Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald" + downloadLocation: "git+ssh://gitlab.gnome.org/GNOME/glib.git" + filesAnalyzed: false + homepage: "https://www.gtk.org/" + licenseConcluded: "LGPL-2.0-or-later" + licenseDeclared: "LGPL-2.1-or-later" + packageFileName: "3rdparty/glib" + name: "glib" + originator: "Person: Peter Mattis, Spencer Kimball, Josh MacDonald" +- SPDXID: "SPDXRef-Package-paho.mqtt.c" + description: "This code builds libraries which enable applications to connect to an MQTT broker to publish messages, and to subscribe to topics and receive published messages." + copyrightText: "Copyright (c) 2009, 2020 IBM Corp. and others" + downloadLocation: "git+ssh://github.com/eclipse/paho.mqtt.c.git" + filesAnalyzed: false + homepage: "https://www.eclipse.org/paho/" + licenseConcluded: "EPL-2.0" + licenseDeclared: "EPL-2.0" + packageFileName: "3rdparty/paho.mqtt.c" + name: "paho.mqtt.c" + originator: "Organization: IBM Corp." +- SPDXID: "SPDXRef-Package-paho.mqtt.cpp" + description: "This repository contains the source code for the Eclipse Paho MQTT C++ client library on memory-managed operating systems such as Linux/Posix and Windows." + copyrightText: "Copyright (c) 2013-2017 Frank Pagliughi " + downloadLocation: "git+ssh://github.com/eclipse/paho.mqtt.cpp.git" + filesAnalyzed: false + homepage: "https://github.com/eclipse/paho.mqtt.cpp" + licenseConcluded: "EPL-1.0" + licenseDeclared: "EPL-1.0" + packageFileName: "3rdparty/paho.mqtt.cpp" + name: "paho.mqtt.cpp" + originator: "Person: Frank Pagliughi" +- SPDXID: "SPDXRef-Package-curl" + description: "Curl is a command-line tool for transferring data specified with URL syntax." + copyrightText: "Copyright (c) 1998, 1999 Kungliga Tekniska Högskolan" + downloadLocation: "git+ssh://github.com/curl/curl.git" + filesAnalyzed: false + homepage: "https://curl.se/" + licenseConcluded: "curl" + licenseDeclared: "curl" + packageFileName: "3rdparty/curl" + name: "curl" + originator: "Organization: Kungliga Tekniska Högskolan" +- SPDXID: "SPDXRef-Package-mini-yaml" + description: "Single header YAML 1.0 C++11 serializer/deserializer." + copyrightText: "Copyright(c) 2018 Jimmie Bergmann" + downloadLocation: "git+ssh://github.com/jimmiebergmann/mini-yaml.git" + filesAnalyzed: false + homepage: "https://github.com/jimmiebergmann/mini-yaml" + licenseConcluded: "MIT" + licenseDeclared: "MIT" + packageFileName: "3rdparty/mini-yaml" + name: "mini-yaml" + originator: "Person: Jimmie Bergmann" +- SPDXID: "SPDXRef-Package-googletest" + description: "GoogleTest and GoogleMock testing framework." + copyrightText: "Copyright 2008, Google Inc." + downloadLocation: "git+ssh://github.com/google/googletest" + filesAnalyzed: false + homepage: "https://google.github.io/googletest/" + licenseConcluded: "BSD-3-Clause" + licenseDeclared: "BSD-3-Clause" + packageFileName: "3rdparty/googletest" + name: "googletest" + originator: "Organization: Google Inc." +relationships: +- spdxElementId: "SPDXRef-Package-sdv-self-update-agent" + relationshipType: "DEPENDS_ON" + relatedSpdxElement: "SPDXRef-Package-paho.mqtt.c" +- spdxElementId: "SPDXRef-Package-sdv-self-update-agent" + relationshipType: "DEPENDS_ON" + relatedSpdxElement: "SPDXRef-Package-paho.mqtt.cpp" +- spdxElementId: "SPDXRef-Package-sdv-self-update-agent" + relationshipType: "DEPENDS_ON" + relatedSpdxElement: "SPDXRef-Package-openssl" +- spdxElementId: "SPDXRef-Package-sdv-self-update-agent" + relationshipType: "DEPENDS_ON" + relatedSpdxElement: "SPDXRef-Package-openssl" +- spdxElementId: "SPDXRef-Package-sdv-self-update-agent" + relationshipType: "DEPENDS_ON" + relatedSpdxElement: "SPDXRef-Package-spdlog" +- spdxElementId: "SPDXRef-Package-sdv-self-update-agent" + relationshipType: "DEPENDS_ON" + relatedSpdxElement: "SPDXRef-Package-glib" +- spdxElementId: "SPDXRef-Package-sdv-self-update-agent" + relationshipType: "DEPENDS_ON" + relatedSpdxElement: "SPDXRef-Package-curl" +- spdxElementId: "SPDXRef-Package-sdv-self-update-agent" + relationshipType: "DEPENDS_ON" + relatedSpdxElement: "SPDXRef-Package-mini-yaml" +- spdxElementId: "SPDXRef-Package-googletest" + relationshipType: "TEST_OF" + relatedSpdxElement: "SPDXRef-Package-sdv-self-update-agent" diff --git a/scripts/build_glib_amd64.sh b/scripts/build_glib_amd64.sh new file mode 100755 index 0000000..48bd8de --- /dev/null +++ b/scripts/build_glib_amd64.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +cd 3rdparty/glib +meson subprojects download +meson setup -Dwrap_mode=forcefallback ../../build_amd64/glib +meson compile -C ../../build_amd64/glib diff --git a/scripts/build_glib_arm64.sh b/scripts/build_glib_arm64.sh new file mode 100755 index 0000000..338af7a --- /dev/null +++ b/scripts/build_glib_arm64.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +cd 3rdparty/glib +meson subprojects download +meson setup -Dwrap_mode=forcefallback --cross-file=../meson-cross-file-aarch64.txt ../../build_arm64/glib +meson compile -C ../../build_arm64/glib diff --git a/scripts/build_openssl_amd64.sh b/scripts/build_openssl_amd64.sh new file mode 100755 index 0000000..d9c9ee5 --- /dev/null +++ b/scripts/build_openssl_amd64.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +rootdir=`pwd` +mkdir -p build_amd64/3rdparty/openssl +cd build_amd64/3rdparty/openssl +./../../../3rdparty/openssl/Configure \ + --prefix=$rootdir/build_amd64 \ + --openssldir=$rootdir/build_amd64 \ + --libdir=lib \ + shared \ + -Wl,-rpath=$rootdir/build_amd64/lib \ + -Wl,--enable-new-dtags +make install_sw \ No newline at end of file diff --git a/scripts/build_openssl_arm64.sh b/scripts/build_openssl_arm64.sh new file mode 100755 index 0000000..1fea6e6 --- /dev/null +++ b/scripts/build_openssl_arm64.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +rootdir=`pwd` +mkdir -p build_arm64/3rdparty/openssl +cd build_arm64/3rdparty/openssl +CC=aarch64-linux-gnu-gcc-9 \ +CXX=aarch64-linux-gnu-g++-9 \ +LD=aarch64-linux-gnu-ld \ +AR=aarch64-linux-gnu-ar \ +RANLIB=aarch64-linux-gnu-ranlib \ + ./../../../3rdparty/openssl/Configure linux-aarch64 \ + --prefix=$rootdir/build_arm64 \ + --openssldir=$rootdir/build_arm64 \ + --libdir=lib \ + shared \ + -Wl,-rpath=$rootdir/build_arm64/lib \ + -Wl,--enable-new-dtags +make install_sw \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..81d682d --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,105 @@ +add_library(curl_lib SHARED IMPORTED) + +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + set_property( + TARGET curl_lib + PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/3rdparty/curl/lib/libcurl-d.so + ) +else () + set_property( + TARGET curl_lib + PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/3rdparty/curl/lib/libcurl.so + ) +endif() + + +add_library(ssl_lib SHARED IMPORTED) +set_property( + TARGET ssl_lib + PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/lib/libssl.so.3 +) + +add_library(crypto_lib SHARED IMPORTED) +set_property( + TARGET crypto_lib + PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/lib/libcrypto.so.3 +) + +add_library(gio_lib SHARED IMPORTED) +set_property( + TARGET gio_lib + PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/glib/gio/libgio-2.0.so +) + +add_library(glib_lib SHARED IMPORTED) +set_property( + TARGET glib_lib + PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/glib/glib/libglib-2.0.so +) + +add_library(gmodule_lib SHARED IMPORTED) +set_property( + TARGET gmodule_lib + PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/glib/gmodule/libgmodule-2.0.so +) + +add_library(gobject_lib SHARED IMPORTED) +set_property( + TARGET gobject_lib + PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/glib/gobject/libgobject-2.0.so +) + +add_library(ffi_lib SHARED IMPORTED) +set_property( + TARGET ffi_lib + PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/glib/subprojects/libffi/src/libffi.so +) + +add_library(z_lib SHARED IMPORTED) +set_property( + TARGET z_lib + PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/glib/subprojects/zlib-1.2.11/libz.so +) + +include_directories( + ${CMAKE_SOURCE_DIR}/src + ${CMAKE_SOURCE_DIR}/3rdparty/spdlog/include + ${CMAKE_SOURCE_DIR}/3rdparty/paho.mqtt.c/src + ${CMAKE_SOURCE_DIR}/3rdparty/paho.mqtt.cpp/src + ${CMAKE_SOURCE_DIR}/3rdparty/mini-yaml + ${CMAKE_SOURCE_DIR}/3rdparty/glib + ${CMAKE_SOURCE_DIR}/3rdparty/glib/glib + ${CMAKE_SOURCE_DIR}/3rdparty/glib/gmodule + ${CMAKE_SOURCE_DIR}/3rdparty/glib/builddir/ + ${CMAKE_SOURCE_DIR}/3rdparty/curl/include + ${CMAKE_BINARY_DIR}/3rdparty/curl/lib + ${CMAKE_BINARY_DIR}/glib + ${CMAKE_BINARY_DIR}/glib/glib +) + +file(GLOB_RECURSE SRCS *.cpp *.h) +add_executable(${CMAKE_PROJECT_NAME} ${SRCS}) + +link_directories(${CMAKE_BINARY_DIR}/3rdparty/curl/lib) + +target_link_libraries(${PROJECT_NAME} + curl_lib + paho-mqttpp3 + paho-mqtt3a + mini-yaml + gio_lib + gobject_lib + gmodule_lib + glib_lib + ffi_lib + z_lib + ${SUA_PLATFORM_LIBS} + ssl_lib + crypto_lib +) + +install(TARGETS ${CMAKE_PROJECT_NAME} RUNTIME DESTINATION bin) +install( + FILES ${CMAKE_BINARY_DIR}/lib/libssl.so.3 ${CMAKE_BINARY_DIR}/lib/libcrypto.so.3 + DESTINATION ${CMAKE_INSTALL_PREFIX}/lib +) diff --git a/src/Download/Downloader.cpp b/src/Download/Downloader.cpp new file mode 100644 index 0000000..5883f5c --- /dev/null +++ b/src/Download/Downloader.cpp @@ -0,0 +1,146 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "Download/Downloader.h" +#include "Logger.h" +#include "Patterns/Dispatcher.h" +#include "TechCodes.h" + +#include +#include +#include + +#include +#include + +namespace { + const int HTTP_OK = 200; + const char FILE_DIR[FILENAME_MAX] = "/RaucUpdate"; + const char FILE_PATH[FILENAME_MAX] = "/RaucUpdate/temp_file"; + bool cancelled = false; + int progressNotificationLimiter = 0; + sua::Downloader* _downloader; + + size_t write_data(void* ptr, size_t size, size_t nmemb, FILE* stream); + bool download(const char* url); + + struct progress { + char* unused; + size_t size; + }; + + static size_t + progress_callback(void* clientp, double dltotal, double dlnow, double ultotal, double ulnow) + { + int progress = 0; + if(dltotal > 0) { + double progress_d = 100.0 * dlnow / dltotal; + progress = (int)progress_d; + } + + if(progress >= progressNotificationLimiter) { + sua::Dispatcher::instance().dispatch(sua::Downloader::EVENT_DOWNLOADING, + std::to_string(progress)); + progressNotificationLimiter += 10; + } + + if(cancelled) { + return 1; + } + + return 0; + } + + struct progress data; + + size_t write_data(void* ptr, size_t size, size_t nmemb, FILE* stream) + { + size_t written = fwrite(ptr, size, nmemb, stream); + return written; + } + + bool download(const char* url) + { + CURLcode gres = curl_global_init(CURL_GLOBAL_ALL); + if(gres != 0) { + sua::Logger::critical("curl_global_init failed with code = {}", gres); + return false; + } + + CURL* easy_handle = curl_easy_init(); + if(!easy_handle) { + sua::Logger::critical("curl_easy_init failed"); + return false; + } + + long response_code; + const int dir_err = mkdir(FILE_DIR, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + if(dir_err) { + sua::Logger::error("Issue with creating a dir, error: {}", dir_err); + } + FILE* fp = fopen(FILE_PATH, "wb"); + curl_easy_setopt(easy_handle, CURLOPT_URL, url); + curl_easy_getinfo(easy_handle, CURLINFO_RESPONSE_CODE, &response_code); + curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, write_data); + curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, fp); + curl_easy_setopt(easy_handle, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(easy_handle, CURLOPT_PROGRESSDATA, &data); + curl_easy_setopt(easy_handle, CURLOPT_PROGRESSFUNCTION, progress_callback); + curl_easy_setopt(easy_handle, CURLOPT_NOPROGRESS, 0); + CURLcode res = curl_easy_perform(easy_handle); + + sua::Logger::debug("curl_easy_perform ended with code = {}", res); + + long http_code = 0; + curl_easy_getinfo(easy_handle, CURLINFO_RESPONSE_CODE, &http_code); + curl_easy_cleanup(easy_handle); + curl_global_cleanup(); + fclose(fp); + progressNotificationLimiter = 0; + + sua::Logger::debug("CURLINFO_RESPONSE_CODE = {}", http_code); + + return http_code == HTTP_OK; + } +} // namespace + +namespace sua { + + const std::string Downloader::EVENT_DOWNLOADING = "Downloader/Downloading"; + const std::string Downloader::EVENT_DOWNLOADED = "Downloader/Downloaded"; + const std::string Downloader::EVENT_FAILED = "Downloader/Failed"; + + bool Downloader::start(const std::string input) + { + Logger::trace("Downloader::start({})", input); + _isWorking = true; + _downloader = this; + Dispatcher::instance().dispatch(EVENT_DOWNLOADING, "0"); + + const bool isOk = download(input.c_str()); + if(isOk) { + Dispatcher::instance().dispatch(EVENT_DOWNLOADED, + std::to_string(static_cast(TechCode::OK))); + } else { + Dispatcher::instance().dispatch( + EVENT_FAILED, std::to_string(static_cast(TechCode::DownloadFailed))); + } + + _isWorking = false; + return _isWorking; + } + +} // namespace sua diff --git a/src/Download/Downloader.h b/src/Download/Downloader.h new file mode 100644 index 0000000..dcf1bd0 --- /dev/null +++ b/src/Download/Downloader.h @@ -0,0 +1,33 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SDV_SUA_DOWNLOADER_H +#define SDV_SUA_DOWNLOADER_H + +#include "Patterns/Worker.h" + +namespace sua { + class Downloader : public Worker { + public: + static const std::string EVENT_DOWNLOADING; + static const std::string EVENT_DOWNLOADED; + static const std::string EVENT_FAILED; + + bool start(const std::string input) override; + }; +} // namespace sua + +#endif diff --git a/src/FSM/FSM.cpp b/src/FSM/FSM.cpp new file mode 100644 index 0000000..cc49775 --- /dev/null +++ b/src/FSM/FSM.cpp @@ -0,0 +1,57 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "FSM/FSM.h" +#include "FSM/States/Uninitialized.h" +#include "Logger.h" + +#include + +namespace sua { + FSM::FSM(const std::shared_ptr installerAgent, + const std::string hostPathToUpdatesDir) + : _installerAgent(installerAgent) + { + _selfupdatesDirPath = hostPathToUpdatesDir; + } + + void FSM::start() + { + Logger::trace("FSM::start()"); + + // set initial state + auto thisPtr = shared_from_this(); + std::shared_ptr unitializedState = std::make_shared(thisPtr); + + // info: issue with current design is that the onEntry for the very first state is not executed + // this can be easily changed, by making onEntry method public, however need to think about better design + transitTo(unitializedState); + } + + void FSM::handle(FotaEvent event, const MessageState payload) + { + if(_currentState) { + _currentState->handle(event, payload); + } else { + throw std::runtime_error("FSM was not initialized! Not allowed to use it."); + } + } + + void FSM::transitTo(std::shared_ptr& nextState) + { + _currentState = nextState; + } +} // namespace sua diff --git a/src/FSM/FSM.h b/src/FSM/FSM.h new file mode 100644 index 0000000..57ffb86 --- /dev/null +++ b/src/FSM/FSM.h @@ -0,0 +1,49 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SDV_SUA_FSM_H +#define SDV_SUA_FSM_H + +#include "Install/IRaucInstaller.h" +#include "PayloadMessages.h" + +#include +#include + +namespace sua { + enum class FotaEvent; + enum class FotaState; + class State; + + class FSM : public std::enable_shared_from_this { + public: + FSM(const std::shared_ptr installerAgent, + const std::string hostPathToUpdatesDir); + + void start(); + void handle(FotaEvent event, const MessageState payload = MessageState()); + void transitTo(std::shared_ptr& nextState); + + std::shared_ptr _installerAgent; + std::string _selfupdatesDirPath{"/data/selfupdates"}; + std::string _selfupdatesFilePath{_selfupdatesDirPath+"/temp_file"}; + + protected: + std::shared_ptr _currentState; + }; +} // namespace sua + +#endif diff --git a/src/FSM/State.cpp b/src/FSM/State.cpp new file mode 100644 index 0000000..ae81f04 --- /dev/null +++ b/src/FSM/State.cpp @@ -0,0 +1,153 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "FSM/State.h" +#include "Logger.h" +#include "Mqtt/MqttMessageSerializer.h" +#include "Patterns/Dispatcher.h" + +namespace sua { + std::string getStateName(const FotaState state) + { + switch(state) { + case FotaState::Idle: + return "idle"; + case FotaState::Downloading: + return "downloading"; + case FotaState::Installing: + return "installing"; + case FotaState::Installed: + return "installed"; + case FotaState::Failed: + return "failed"; + case FotaState::Uinitialized: + return "uinitialized"; + ; + default: + return "unknown"; + } + } + + std::string getEventName(const FotaEvent event) + { + switch(event) { + case FotaEvent::ConnectivityEstablished: + return "ConnectivityEstablished"; + case FotaEvent::ConnectivityLost: + return "ConnectivityLost"; + case FotaEvent::FotaStart: + return "FotaStart"; + case FotaEvent::DownloadProgress: + return "DownloadProgress"; + case FotaEvent::DownloadReady: + return "DownloadReady"; + case FotaEvent::DownloadError: + return "DownloadError"; + case FotaEvent::InstallReady: + return "InstallReady"; + case FotaEvent::InstallProgress: + return "InstallProgress"; + case FotaEvent::InstallError: + return "InstallError"; + default: + return "Unknown"; + } + } + + std::ostream& operator<<(std::ostream& os, FotaEvent event) + { + os << getEventName(event); + return os; + } + + std::ostream& operator<<(std::ostream& os, FotaState state) + { + os << getStateName(state); + return os; + } + + const std::string State::EVENT_PUBLISH = "State/Publish"; + const std::string State::EVENT_IDLE = "State/Idle"; + + void State::handle(FotaEvent event, const MessageState payload) + { + Logger::trace("State::handle({}, {})", static_cast(event), toString(payload)); + handleTemplate(event, payload); + } + + void State::handleBadEvent(FotaEvent event, const MessageState payload) + { + Logger::trace("State::handleBadEvent({}, {})", static_cast(event), toString(payload)); + } + + MessageState State::getPayload() const + { + return _payload; + } + + void State::setPayload(const MessageState payload) + { + _payload = payload; + } + + void State::publishCurrentState() const + { + Logger::info("Publishing current state {} with payload = {}", + static_cast(getState()), + toString(getPayload())); + + Dispatcher::instance().dispatch(EVENT_PUBLISH, + MqttMessageSerializer().serialize(getPayload())); + } + + void State::transitTo(std::shared_ptr& nextState) + { + onExit(); + _context->transitTo(nextState); + nextState->onEntry(); + } + + void State::onEntry() + { + Logger::info("Entering state {} with initial payload {}", + static_cast(getState()), + toString(getPayload())); + adjustEntryPayload(); + adjustEntryPayloadTemplate(); + publishCurrentState(); + onEntryTemplate(); + } + + void State::onExit() + { + Logger::info("Exiting state {} with payload {}", + static_cast(getState()), + toString(getPayload())); + onExitTemplate(); + } + + void State::onEntryTemplate() { } + + void State::onExitTemplate() { } + + void State::adjustEntryPayload() + { + _payload.stateName = getStateName(getState()); + } + + void State::adjustEntryPayloadTemplate() { } + +} // namespace sua diff --git a/src/FSM/State.h b/src/FSM/State.h new file mode 100644 index 0000000..0ec1e4f --- /dev/null +++ b/src/FSM/State.h @@ -0,0 +1,81 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SDV_SUA_STATE_H +#define SDV_SUA_STATE_H + +#include "FSM/FSM.h" +#include "Install/IRaucInstaller.h" +#include "PayloadMessages.h" + +#include +#include + +namespace sua { + enum class FotaEvent { + ConnectivityEstablished, + ConnectivityLost, + FotaStart, + DownloadProgress, + DownloadReady, + DownloadError, + InstallReady, + InstallProgress, + InstallError + }; + + enum class FotaState { Uinitialized, Idle, Downloading, Installing, Failed, Installed }; + + std::ostream& operator<<(std::ostream& os, FotaEvent event); + std::ostream& operator<<(std::ostream& os, FotaState state); + std::string getStateName(const FotaState state); + std::string getEventName(const FotaEvent event); + + class State { + public: + static const std::string EVENT_PUBLISH; + static const std::string EVENT_IDLE; + + State(std::shared_ptr& context, const MessageState payload = MessageState()) + : _context(context) + , _payload(payload) + { } + virtual ~State() = default; + void handle(FotaEvent event, const MessageState payload = MessageState()); + + protected: + mutable MessageState _payload; + std::shared_ptr _context; + + virtual FotaState getState() const = 0; + void onEntry(); + void onExit(); + void transitTo(std::shared_ptr& nextState); + + MessageState getPayload() const; + void setPayload(const MessageState payload); + void adjustEntryPayload(); + void publishCurrentState() const; + + virtual void onEntryTemplate(); + virtual void handleTemplate(FotaEvent event, const MessageState payload) = 0; + virtual void onExitTemplate(); + virtual void adjustEntryPayloadTemplate() = 0; + void handleBadEvent(FotaEvent event, const MessageState payload); + }; +} // namespace sua + +#endif diff --git a/src/FSM/States/Downloading.cpp b/src/FSM/States/Downloading.cpp new file mode 100644 index 0000000..4fd145d --- /dev/null +++ b/src/FSM/States/Downloading.cpp @@ -0,0 +1,123 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "FSM/States/Downloading.h" +#include "Download/Downloader.h" +#include "FSM/States/Failed.h" +#include "FSM/States/Idle.h" +#include "FSM/States/Installing.h" +#include "FSM/States/Uninitialized.h" +#include "Utils/BundleChecker.h" +#include "Logger.h" + +namespace sua { + void cleanUpDownload() + { + // TODO, add logic to clean up the download, move the cleanup logic to the donwloader + Logger::info("Cleaning up download"); + } + + Downloading::Downloading(std::shared_ptr& context, const MessageState payload) + : State(context, payload) + { + subscribe(Downloader::EVENT_DOWNLOADED, [this](const std::string& payload) { + Logger::info("New state for Downloading: Downloaded, payload = {}", payload); + _payload.stateTechCode = std::stoi(payload); + _payload.stateMessage = "Downloaded completed"; + _payload.stateProgress = 100; + handle(FotaEvent::DownloadReady, _payload); + }); + + subscribe(Downloader::EVENT_FAILED, [this](const std::string& payload) { + Logger::info("New state for Downloading: Failed, payload = {}", payload); + _payload.stateTechCode = std::stoi(payload); + _payload.stateProgress = 0; + _payload.stateMessage = "Downloading failed with error code = " + payload; + handle(FotaEvent::DownloadError, _payload); + }); + + subscribe(Downloader::EVENT_DOWNLOADING, [this](const std::string& payload) { + Logger::info("New state for Downloading: Downloading, payload = {}", payload); + _payload.stateProgress = std::stoi(payload); + _payload.stateTechCode = 0; + _payload.stateMessage = "Downloading, progress = " + payload; + handle(FotaEvent::DownloadProgress, _payload); + }); + } + + void Downloading::onEntryTemplate() + { + _downloader = std::make_unique(); + _downloader->start(_payload.bundleDownloadUrl); + } + + void Downloading::adjustEntryPayloadTemplate() + { + _payload.stateMessage = "Entered Downloading state"; + _payload.stateProgress = 0; + _payload.stateTechCode = 0; + } + + void Downloading::handleTemplate(const FotaEvent event, const MessageState payload) + { + std::shared_ptr nextState; + + switch(event) { + case FotaEvent::DownloadReady: + if(BundleChecker().isBundleVersionConsistent(payload.bundleVersion, + _context->_installerAgent, + _context->_selfupdatesFilePath)) { + Logger::info("Downloaded bundle is valid, conntinue.."); + setPayload(payload); + nextState = std::make_shared(_context, payload); + } else { + // declared in yaml version shall be same as the downloaded one, if not then prevent installation + Logger::info("Bundle version declared in yaml != downloaded bundle version, Reject update request with error 2001"); + MessageState msg = payload; + msg.stateProgress = 0; + msg.stateTechCode = 2001; + msg.stateMessage = "Invalid Bundle"; + nextState = std::make_shared(_context, msg); + } + transitTo(nextState); + break; + case FotaEvent::DownloadError: + cleanUpDownload(); + setPayload(payload); + nextState = std::make_shared(_context, payload); + transitTo(nextState); + break; + case FotaEvent::DownloadProgress: + setPayload(payload); + publishCurrentState(); + break; + case FotaEvent::ConnectivityLost: + cleanUpDownload(); + setPayload(payload); + nextState = std::make_shared(_context, payload); + transitTo(nextState); + break; + default: + handleBadEvent(event, payload); // TODO, put reject, not only log + break; + } + } + + FotaState Downloading::getState() const + { + return FotaState::Downloading; + } +} // namespace sua diff --git a/src/FSM/States/Downloading.h b/src/FSM/States/Downloading.h new file mode 100644 index 0000000..98fd358 --- /dev/null +++ b/src/FSM/States/Downloading.h @@ -0,0 +1,43 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SDV_SUA_DOWNLOADING_H +#define SDV_SUA_DOWNLOADING_H + +#include "Download/Downloader.h" +#include "FSM/State.h" +#include "Patterns/Dispatcher.h" + +namespace sua { + class Downloading + : public State + , public DispatcherSubscriber { + public: + Downloading(std::shared_ptr& context, const MessageState payload = MessageState()); + + void handleTemplate(FotaEvent event, const MessageState payload) override; + FotaState getState() const override; + + protected: + void onEntryTemplate() override; + void adjustEntryPayloadTemplate() override; + + private: + std::unique_ptr _downloader; + }; +} // namespace sua + +#endif diff --git a/src/FSM/States/Failed.cpp b/src/FSM/States/Failed.cpp new file mode 100644 index 0000000..c6c60c2 --- /dev/null +++ b/src/FSM/States/Failed.cpp @@ -0,0 +1,54 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "FSM/States/Failed.h" +#include "FSM/States/Idle.h" +#include "FSM/States/Uninitialized.h" + +namespace sua { + void Failed::onEntryTemplate() + { + // TODO, depending on the payload, perform proper cleanup + // for example deleting broken file + std::shared_ptr nextState = std::make_shared(_context); + transitTo(nextState); + } + + void Failed::adjustEntryPayloadTemplate() + { + // Empty, use gived payload + } + + void Failed::handleTemplate(const FotaEvent event, const MessageState payload) + { + std::shared_ptr nextState; + + switch(event) { + case FotaEvent::ConnectivityLost: + nextState = std::make_shared(_context, payload); + transitTo(nextState); + break; + default: + handleBadEvent(event, payload); + break; + } + } + + FotaState Failed::getState() const + { + return FotaState::Failed; + } +} // namespace sua diff --git a/src/FSM/States/Failed.h b/src/FSM/States/Failed.h new file mode 100644 index 0000000..27ebf37 --- /dev/null +++ b/src/FSM/States/Failed.h @@ -0,0 +1,36 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SDV_SUA_FAILED_H +#define SDV_SUA_FAILED_H + +#include "FSM/State.h" + +namespace sua { + class Failed : public State { + using State::State; + + public: + void handleTemplate(FotaEvent event, const MessageState payload) override; + FotaState getState() const override; + + protected: + void onEntryTemplate() override; + void adjustEntryPayloadTemplate() override; + }; +} // namespace sua + +#endif diff --git a/src/FSM/States/Idle.cpp b/src/FSM/States/Idle.cpp new file mode 100644 index 0000000..895299a --- /dev/null +++ b/src/FSM/States/Idle.cpp @@ -0,0 +1,70 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "FSM/States/Idle.h" +#include "FSM/States/Downloading.h" +#include "FSM/States/Failed.h" +#include "FSM/States/Uninitialized.h" +#include "Patterns/Dispatcher.h" +#include "TechCodes.h" +#include "Utils/BundleChecker.h" + +namespace sua { + void Idle::onEntryTemplate() + { + Dispatcher::instance().dispatch(EVENT_IDLE, ""); + } + + void Idle::adjustEntryPayloadTemplate() + { + _payload.stateMessage = "Entered Idle state"; + _payload.stateProgress = 0; + _payload.stateTechCode = 0; + } + + void Idle::handleTemplate(const FotaEvent event, const MessageState payload) + { + std::shared_ptr nextState; + + switch(event) { + case FotaEvent::FotaStart: + if(BundleChecker().isUpdateBundleVersionDifferent(payload.bundleVersion, + _context->_installerAgent)) { + nextState = std::make_shared(_context, payload); + } else { + MessageState msg = payload; + msg.stateProgress = 0; + msg.stateTechCode = 4001; + msg.stateMessage = "Update rejected, update bundle version same as current version"; + nextState = std::make_shared(_context, msg); + } + transitTo(nextState); + break; + case FotaEvent::ConnectivityLost: + nextState = std::make_shared(_context, payload); + transitTo(nextState); + break; + default: + handleBadEvent(event, payload); + break; + } + } + + FotaState Idle::getState() const + { + return FotaState::Idle; + } +} // namespace sua \ No newline at end of file diff --git a/src/FSM/States/Idle.h b/src/FSM/States/Idle.h new file mode 100644 index 0000000..6d7d863 --- /dev/null +++ b/src/FSM/States/Idle.h @@ -0,0 +1,36 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SDV_SUA_IDLE_H +#define SDV_SUA_IDLE_H + +#include "FSM/State.h" + +namespace sua { + class Idle : public State { + using State::State; + + public: + void handleTemplate(FotaEvent event, const MessageState payload) override; + FotaState getState() const override; + + protected: + void onEntryTemplate() override; + void adjustEntryPayloadTemplate() override; + }; +} // namespace sua + +#endif diff --git a/src/FSM/States/Installed.cpp b/src/FSM/States/Installed.cpp new file mode 100644 index 0000000..a586b2d --- /dev/null +++ b/src/FSM/States/Installed.cpp @@ -0,0 +1,55 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "FSM/States/Installed.h" +#include "FSM/States/Idle.h" +#include "FSM/States/Uninitialized.h" + +namespace sua { + void Installed::onEntryTemplate() + { + std::shared_ptr nextState = std::make_shared(_context); + transitTo(nextState); + } + + void Installed::adjustEntryPayloadTemplate() + { + _payload.stateMessage = "Entered Installed state"; + _payload.stateProgress = 0; + _payload.stateTechCode = 0; + } + + void Installed::handleTemplate(const FotaEvent event, const MessageState payload) + { + std::shared_ptr nextState; + + switch(event) { + case FotaEvent::ConnectivityLost: + // TODO, this is edge case, here the installation can maybe proceed + nextState = std::make_shared(_context, payload); + transitTo(nextState); + break; + default: + handleBadEvent(event, payload); + break; + } + } + + FotaState Installed::getState() const + { + return FotaState::Installed; + } +} // namespace sua diff --git a/src/FSM/States/Installed.h b/src/FSM/States/Installed.h new file mode 100644 index 0000000..7b038d5 --- /dev/null +++ b/src/FSM/States/Installed.h @@ -0,0 +1,36 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SDV_SUA_INSTALLED_H +#define SDV_SUA_INSTALLED_H + +#include "FSM/State.h" + +namespace sua { + class Installed : public State { + using State::State; + + public: + void handleTemplate(FotaEvent event, const MessageState payload) override; + FotaState getState() const override; + + protected: + void onEntryTemplate() override; + void adjustEntryPayloadTemplate() override; + }; +} // namespace sua + +#endif diff --git a/src/FSM/States/Installing.cpp b/src/FSM/States/Installing.cpp new file mode 100644 index 0000000..19749eb --- /dev/null +++ b/src/FSM/States/Installing.cpp @@ -0,0 +1,111 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "FSM/States/Installing.h" +#include "FSM/States/Failed.h" +#include "FSM/States/Installed.h" +#include "FSM/States/Uninitialized.h" +#include "Install/Installer.h" +#include "Logger.h" + +namespace sua { + void cleanUpInstallation() + { + // TODO, add logic to clean up the Installation + Logger::info("Cleaning up installation"); + } + + Installing::Installing(std::shared_ptr& context, const MessageState payload) + : State(context, payload) + { + subscribe(Installer::EVENT_INSTALLED, [this](const std::string& payload) { + Logger::info("New state for Installing: Installed, payload = {}", payload); + _payload.stateTechCode = std::stoi(payload); + _payload.stateProgress = 0; + _payload.stateMessage = "Installing completed"; + handle(FotaEvent::InstallReady, _payload); + }); + + subscribe(Installer::EVENT_INSTALLING, [this](const std::string& payload) { + Logger::info("New state for Installing: Installing, payload = {}", payload); + _payload.stateProgress = std::stoi(payload); + _payload.stateTechCode = 0; + _payload.stateMessage = "Installing, progress = " + payload; + handle(FotaEvent::InstallProgress, _payload); + }); + + subscribe(Installer::EVENT_FAILED, [this](const std::string& payload) { + Logger::info("New state for Installing: Failed, payload = {}", payload); + _payload.stateTechCode = std::stoi(payload); + _payload.stateProgress = 0; + _payload.stateMessage = "Installing failed with error code = " + payload; + handle(FotaEvent::InstallError, _payload); + }); + } + + void Installing::onEntryTemplate() + { + _installer = std::make_unique(_context->_installerAgent); + _installer->start(_context->_selfupdatesFilePath); + } + + void Installing::adjustEntryPayloadTemplate() + { + _payload.stateMessage = "Entered Installing state"; + _payload.stateProgress = 0; + _payload.stateTechCode = 0; + } + + void Installing::handleTemplate(const FotaEvent event, const MessageState payload) + { + std::shared_ptr nextState; + + switch(event) { + case FotaEvent::InstallReady: + Logger::trace("INSTALLATION DONE"); + nextState = std::make_shared(_context, payload); + transitTo(nextState); + break; + case FotaEvent::InstallProgress: + Logger::trace("INSTALLATION Progressed"); + setPayload(payload); + publishCurrentState(); + break; + case FotaEvent::InstallError: + Logger::trace("INSTALLATION FAILED"); + cleanUpInstallation(); + nextState = std::make_shared(_context, payload); + transitTo(nextState); + break; + case FotaEvent::ConnectivityLost: + Logger::trace("CONNECTION LOST during installation"); + cleanUpInstallation(); + // TODO, figure out what is the best strategy to handle this, we can continue installation... + nextState = std::make_shared(_context); + transitTo(nextState); + break; + + default: + handleBadEvent(event, payload); + break; + } + } + + FotaState Installing::getState() const + { + return FotaState::Installing; + } +} // namespace sua diff --git a/src/FSM/States/Installing.h b/src/FSM/States/Installing.h new file mode 100644 index 0000000..3ee9f94 --- /dev/null +++ b/src/FSM/States/Installing.h @@ -0,0 +1,43 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SDV_SUA_INSTALLING_H +#define SDV_SUA_INSTALLING_H + +#include "FSM/State.h" +#include "Install/Installer.h" +#include "Patterns/Dispatcher.h" + +namespace sua { + class Installing + : public State + , public DispatcherSubscriber { + public: + Installing(std::shared_ptr& context, const MessageState payload = MessageState()); + + void handleTemplate(FotaEvent event, const MessageState payload) override; + FotaState getState() const override; + + protected: + void onEntryTemplate() override; + void adjustEntryPayloadTemplate() override; + + private: + std::unique_ptr _installer; + }; +} // namespace sua + +#endif diff --git a/src/FSM/States/Uninitialized.cpp b/src/FSM/States/Uninitialized.cpp new file mode 100644 index 0000000..1c9f0b3 --- /dev/null +++ b/src/FSM/States/Uninitialized.cpp @@ -0,0 +1,47 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "FSM/States/Uninitialized.h" +#include "FSM/States/Idle.h" + +namespace sua { + + void Uninitialized::adjustEntryPayloadTemplate() + { + // use passed values + } + + void Uninitialized::handleTemplate(FotaEvent event, const MessageState payload) + { + std::shared_ptr nextState; + + switch(event) { + case FotaEvent::ConnectivityEstablished: + nextState = std::make_shared(_context, payload); + transitTo(nextState); + break; + + default: + handleBadEvent(event, payload); + break; + } + } + + FotaState Uninitialized::getState() const + { + return FotaState::Uinitialized; + } +} // namespace sua diff --git a/src/FSM/States/Uninitialized.h b/src/FSM/States/Uninitialized.h new file mode 100644 index 0000000..4913f44 --- /dev/null +++ b/src/FSM/States/Uninitialized.h @@ -0,0 +1,33 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SDV_SUA_UNINITIALIZED_H +#define SDV_SUA_UNINITIALIZED_H + +#include "FSM/State.h" + +namespace sua { + class Uninitialized : public State { + using State::State; + + public: + void handleTemplate(FotaEvent event, const MessageState payload) override; + FotaState getState() const override; + void adjustEntryPayloadTemplate() override; + }; +} // namespace sua + +#endif diff --git a/src/Install/DBusRaucInstaller.cpp b/src/Install/DBusRaucInstaller.cpp new file mode 100644 index 0000000..af77a13 --- /dev/null +++ b/src/Install/DBusRaucInstaller.cpp @@ -0,0 +1,283 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "Install/DBusRaucInstaller.h" +#include "Logger.h" + +#include +#include +#include + +#include + +namespace { + const std::string VERSION_UNAVAILABLE = "bundle_version_not_available"; +} + +namespace sua { + DBusRaucInstaller::DBusRaucInstaller() + { + Logger::trace("DBusRaucInstaller::DBusRaucInstaller()"); + setupDBusRaucConnection(); + } + + DBusRaucInstaller::~DBusRaucInstaller() + { + Logger::trace("DBusRaucInstaller::~DBusRaucInstaller()"); + if(nullptr != connection) { + g_object_unref(connection); + } + } + + void DBusRaucInstaller::installBundle(const std::string input) + { + Logger::info("Installing rauc bundle {}", input); + installDBusRaucBundle(input); + } + + int32_t DBusRaucInstaller::getInstallProgress() + { + return getDBusRaucInstallProgress(); + } + + std::string DBusRaucInstaller::getBundleVersion() + { + return getDBusRaucBundleVersion(); + } + + std::string DBusRaucInstaller::getBundleVersion(const std::string input) + { + return getDBusRaucBundleVersion(input); + } + + void DBusRaucInstaller::setupDBusRaucConnection() + { + GError* connectionError = nullptr; + connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &connectionError); + + if(nullptr != connection) { + Logger::info("Valid connection to dbus"); + } else { + Logger::error("Unable to connect to dbus, code = {}, message = {}", + connectionError->code, + connectionError->message); + } + } + + void DBusRaucInstaller::installDBusRaucBundle(const std::string bundleName) + { + GError* connectionError = nullptr; + GVariant* result = g_dbus_connection_call_sync(connection, + "de.pengutronix.rauc", + "/", + "de.pengutronix.rauc.Installer", + "Install", + g_variant_new("(s)", bundleName.c_str()), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &connectionError); + + if(nullptr != result) { + g_variant_unref(result); + } else { + Logger::error("Install call to Rauc via DBus failed, code = {}, message = {}", + connectionError->code, + connectionError->message); + } + } + + int32_t DBusRaucInstaller::getDBusRaucInstallProgress() const + { + Logger::info("Install progress"); + GError* connectionError = nullptr; + GVariant* progressInfo = g_dbus_connection_call_sync( + connection, + "de.pengutronix.rauc", + "/", + "org.freedesktop.DBus.Properties", + "Get", + g_variant_new("(ss)", "de.pengutronix.rauc.Installer", "Progress"), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &connectionError); + + int32_t progressPercentage = 0; + if(nullptr != progressInfo) { + GVariant* internalVal; + int32_t nesting = 0; + gchar* message; + g_variant_get(progressInfo, "(v)", &internalVal); + g_variant_get(internalVal, "(isi)", &progressPercentage, &message, &nesting); + Logger::info("Installing"); + Logger::info(" message = {}", message); + Logger::info(" progress percentage = {}", progressPercentage); + Logger::info("nesting = {}", nesting); + g_variant_unref(progressInfo); + } else { + Logger::error("Connection to DBus lost? code = {}, message = {}", + connectionError->code, + connectionError->message); + } + return progressPercentage; + } + + std::string DBusRaucInstaller::getDBusRaucBundleVersion() const + { + std::string bundleVersion = VERSION_UNAVAILABLE; + GError* connectionError = nullptr; + GVariant* slotStatus = g_dbus_connection_call_sync(connection, + "de.pengutronix.rauc", + "/", + "de.pengutronix.rauc.Installer", + "GetSlotStatus", + NULL, + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &connectionError); + + if(nullptr != slotStatus) { + GVariantIter* slotIter; + gchar* slotName; + GVariant* slotDict; + uint32_t bootedSlotCount = 0; + g_variant_get(slotStatus, "(a(sa{sv}))", &slotIter); + while(g_variant_iter_next(slotIter, "(s@a{sv})", &slotName, &slotDict)) { + gchar* slotState; + gchar* bundle; + GVariantDict dict; + g_variant_dict_init(&dict, slotDict); + if(g_variant_dict_lookup(&dict, "state", "s", &slotState)) { + if("booted" == std::string(slotState)) { + bootedSlotCount++; + if(g_variant_dict_lookup(&dict, "bundle.version", "s", &bundle)) { + bundleVersion = bundle; + } + } + } + } + + if(bootedSlotCount > 1) { + bundleVersion = "error_multiple_booted_slots_detected"; + } + + g_free(slotName); + g_variant_unref(slotDict); + g_variant_unref(slotStatus); + } + + if(bundleVersion == VERSION_UNAVAILABLE) { + std::ifstream f("/etc/os-release"); + if(f) { + std::string keyValue; + + while(std::getline(f, keyValue)) { + std::stringstream ss(keyValue); + + std::string key; + std::getline(ss, key, '='); + + if(key == "VERSION_ID") { + std::string value; + std::getline(ss, value, '='); + value.erase(std::remove(value.begin(), value.end(), '"'), value.end()); + if(!value.empty()) { + bundleVersion = value; + break; + } + } + } + } + } + + return bundleVersion; + } + + std::string DBusRaucInstaller::getDBusRaucBundleVersion(const std::string input) const + { + Logger::info("getDBusRaucBundleVersion, input={}", input); + std::string bundleVersion = "bundle_version_not_available"; + GError* connectionError = nullptr; + GVariant* result = g_dbus_connection_call_sync(connection, + "de.pengutronix.rauc", + "/", + "de.pengutronix.rauc.Installer", + "Info", + g_variant_new("(s)", input.c_str()), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &connectionError); + + + + + if(nullptr != result) { + Logger::info("Retrieved the version data, processing..."); + gchar* compatible; + gchar* version; + g_variant_get(result, "(ss)", &compatible, &version); + bundleVersion = std::string(version); + Logger::info("Version of downloaded bundle: {}", bundleVersion); + g_free(compatible); + g_free(version); + } else { + Logger::info("Retrieval of bundle version was not succesfull, error {}", connectionError->message); + } + + Logger::info("Retrieved version of the incoming bundle is: " + bundleVersion); + return bundleVersion; + } + + DummyRaucInstaller::DummyRaucInstaller() + { + Logger::trace("DummyRaucInstaller::DummyRaucInstaller()"); + }; + + DummyRaucInstaller::~DummyRaucInstaller() + { + Logger::trace("DummyRaucInstaller::~DummyRaucInstaller()"); + }; + + void DummyRaucInstaller::installBundle(const std::string input) + { + Logger::trace("DummyRaucInstaller::installBundle({})", input); + }; + + int32_t DummyRaucInstaller::getInstallProgress() + { + Logger::trace("DummyRaucInstaller::getInstallProgress()"); + return 0; + }; + + std::string DummyRaucInstaller::getBundleVersion() + { + Logger::trace("DummyRaucInstaller::getBundleVersion()"); + return "dummy_version"; + }; + + std::string DummyRaucInstaller::getBundleVersion(const std::string input) + { + Logger::trace("DummyRaucInstaller::getBundleVersion({})", input); + return "dummy_version"; + }; +} // namespace sua diff --git a/src/Install/DBusRaucInstaller.h b/src/Install/DBusRaucInstaller.h new file mode 100644 index 0000000..4091b31 --- /dev/null +++ b/src/Install/DBusRaucInstaller.h @@ -0,0 +1,56 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SDV_SUA_DBUSRAUCINSTALLER_H +#define SDV_SUA_DBUSRAUCINSTALLER_H + +#include + +#include "Install/IRaucInstaller.h" + +namespace sua { + class DBusRaucInstaller : public IRaucInstaller { + public: + DBusRaucInstaller(); + ~DBusRaucInstaller(); + void installBundle(const std::string input) override; + int32_t getInstallProgress() override; + std::string getBundleVersion() override; + std::string getBundleVersion(const std::string input) override; + + private: + GDBusConnection* connection{nullptr}; + + void setupDBusRaucConnection(); + void installDBusRaucBundle(const std::string bundleName); + int32_t getDBusRaucInstallProgress() const; + std::string getDBusRaucBundleVersion() const; + std::string getDBusRaucBundleVersion(const std::string input) const; + }; + + // Only needed for initial setup & test, will be removed. + class DummyRaucInstaller : public IRaucInstaller { + public: + DummyRaucInstaller(); + ~DummyRaucInstaller(); + void installBundle(const std::string input); + int32_t getInstallProgress(); + std::string getBundleVersion(); + std::string getBundleVersion(const std::string input); + }; +} // namespace sua + +#endif // SDV_SUA_DBUSRAUCINSTALLER_H diff --git a/src/Install/IRaucInstaller.h b/src/Install/IRaucInstaller.h new file mode 100644 index 0000000..dcc391a --- /dev/null +++ b/src/Install/IRaucInstaller.h @@ -0,0 +1,33 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SDV_SUA_IRAUCINSTALLER_H +#define SDV_SUA_IRAUCINSTALLER_H + +#include + +namespace sua { + class IRaucInstaller { + public: + virtual ~IRaucInstaller(){}; + virtual void installBundle(const std::string input) = 0; + virtual int32_t getInstallProgress() = 0; + virtual std::string getBundleVersion() = 0; + virtual std::string getBundleVersion(const std::string input) = 0; + }; +} // namespace sua + +#endif // SDV_SUA_IRAUCINSTALLER_H diff --git a/src/Install/Installer.cpp b/src/Install/Installer.cpp new file mode 100644 index 0000000..bdaf162 --- /dev/null +++ b/src/Install/Installer.cpp @@ -0,0 +1,55 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "Install/Installer.h" +#include "Logger.h" +#include "Patterns/Dispatcher.h" + +#include + +namespace sua { + const std::string Installer::EVENT_INSTALLING = "Installer/Installing"; + const std::string Installer::EVENT_INSTALLED = "Installed/Installed"; + const std::string Installer::EVENT_FAILED = "Installed/Failed"; + + Installer::Installer(std::shared_ptr installerAgent) + : _installerAgent(installerAgent){}; + + bool Installer::start(const std::string input) + { + Logger::trace("Installer::start({})", input); + _isWorking = true; + + _installerAgent->installBundle(input); + + bool installing = true; + uint32_t count = 0; + int32_t progressPercentage = 0; + while(installing) { + progressPercentage = _installerAgent->getInstallProgress(); + sleep(2); + count++; + if(progressPercentage >= 100 || count >= 120) { + installing = false; + } + } + + _isWorking = false; + Dispatcher::instance().dispatch(EVENT_INSTALLED, "0"); + return _isWorking; + } + +} // namespace sua diff --git a/src/Install/Installer.h b/src/Install/Installer.h new file mode 100644 index 0000000..628423c --- /dev/null +++ b/src/Install/Installer.h @@ -0,0 +1,40 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SDV_SUA_INSTALLER_H +#define SDV_SUA_INSTALLER_H + +#include + +#include "Install/IRaucInstaller.h" +#include "Patterns/Worker.h" + +namespace sua { + class Installer : public Worker { + public: + static const std::string EVENT_INSTALLING; + static const std::string EVENT_INSTALLED; + static const std::string EVENT_FAILED; + + Installer(std::shared_ptr installerAgent); + bool start(const std::string input) override; + + protected: + std::shared_ptr _installerAgent; + }; +} // namespace sua + +#endif diff --git a/src/Logger.cpp b/src/Logger.cpp new file mode 100644 index 0000000..f8d1c9c --- /dev/null +++ b/src/Logger.cpp @@ -0,0 +1,62 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "Logger.h" + +#include +#include + +namespace sua { + + Logger& Logger::instance() + { + static Logger logger; + return logger; + } + + Logger::Logger() + { + _log_level = Level::All; + } + + void Logger::init() + { + auto stdout_sink = std::make_shared(); + stdout_sink->set_level(spdlog::level::trace); + stdout_sink->set_pattern("%^%Y-%m-%dT%H:%M:%S.%f%z %-8l %v%$"); + + auto json_file_sink = + std::make_shared("../logs/log.json", true); + json_file_sink->set_level(spdlog::level::trace); + json_file_sink->set_pattern( + "{\"time\": \"%Y-%m-%dT%H:%M:%S.%f%z\", \"level\": \"%^%l%$\", \"message\": \"%v\"},"); + + auto logger = std::make_shared( + "sua_logger", spdlog::sinks_init_list{stdout_sink, json_file_sink}); + logger->set_level(spdlog::level::trace); + spdlog::set_default_logger(logger); + } + + void Logger::setLogLevel(int mask) + { + _log_level = mask; + } + + int Logger::getLogLevel() const + { + return _log_level; + } +} // namespace sua diff --git a/src/Logger.h b/src/Logger.h new file mode 100644 index 0000000..c519a43 --- /dev/null +++ b/src/Logger.h @@ -0,0 +1,99 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SDV_SUA_LOGGER_H +#define SDV_SUA_LOGGER_H + +#include + +namespace sua { + + class Logger { + public: + enum Level { + Trace = 0x0001, + Debug = 0x0002, + Info = 0x0004, + Warning = 0x0008, + Error = 0x0010, + Critical = 0x0020, + All = 0xFFFF + }; + + Logger(); + + void init(); + + void setLogLevel(int mask); + int getLogLevel() const; + + template + static void trace(spdlog::format_string_t fmt, Args&&... args) + { + if(instance().getLogLevel() & Level::Trace) { + spdlog::trace(fmt, std::forward(args)...); + } + } + + template + static void debug(spdlog::format_string_t fmt, Args&&... args) + { + if(instance().getLogLevel() & Level::Debug) { + spdlog::debug(fmt, std::forward(args)...); + } + } + + template + static void info(spdlog::format_string_t fmt, Args&&... args) + { + if(instance().getLogLevel() & Level::Info) { + spdlog::info(fmt, std::forward(args)...); + } + } + + template + static void warning(spdlog::format_string_t fmt, Args&&... args) + { + if(instance().getLogLevel() & Level::Warning) { + spdlog::warn(fmt, std::forward(args)...); + } + } + + template + static void error(spdlog::format_string_t fmt, Args&&... args) + { + if(instance().getLogLevel() & Level::Error) { + spdlog::error(fmt, std::forward(args)...); + } + } + + template + static void critical(spdlog::format_string_t fmt, Args&&... args) + { + if(instance().getLogLevel() & Level::Critical) { + spdlog::critical(fmt, std::forward(args)...); + } + } + + public: + static Logger& instance(); + + private: + int _log_level; + }; +} // namespace sua + +#endif diff --git a/src/Mqtt/MqttConfiguration.h b/src/Mqtt/MqttConfiguration.h new file mode 100644 index 0000000..9c47855 --- /dev/null +++ b/src/Mqtt/MqttConfiguration.h @@ -0,0 +1,38 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SDV_SUA_CONFIGURATION_H +#define SDV_SUA_CONFIGURATION_H + +#include +#include + +namespace sua { + struct MqttConfiguration { + std::string brokerHost; + int brokerPort; + std::string userName; + std::string password; + }; + + inline std::ostream& operator<<(std::ostream& os, MqttConfiguration conf) + { + os << "Host: " << conf.brokerHost << " Port: " << conf.password; + return os; + } +} // namespace sua + +#endif diff --git a/src/Mqtt/MqttListener.h b/src/Mqtt/MqttListener.h new file mode 100644 index 0000000..ea27838 --- /dev/null +++ b/src/Mqtt/MqttListener.h @@ -0,0 +1,29 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SDV_SUA_MQTTLISTENER_H +#define SDV_SUA_MQTTLISTENER_H + +namespace sua { + class MqttListener { + public: + virtual ~MqttListener() = default; + + virtual void handle(const class MessageStart& message) = 0; + }; +} // namespace sua + +#endif diff --git a/src/Mqtt/MqttMessageDeserializer.cpp b/src/Mqtt/MqttMessageDeserializer.cpp new file mode 100644 index 0000000..df2683f --- /dev/null +++ b/src/Mqtt/MqttMessageDeserializer.cpp @@ -0,0 +1,54 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "Mqtt/MqttMessageDeserializer.h" +#include "PayloadMessages.h" + +#include "yaml/Yaml.hpp" + +namespace sua { + void MqttMessageDeserializer::deserialize(const std::string& payload, MessageStart& m) const + { + Yaml::Node root; + Yaml::Parse(root, payload); + + m.apiVersion = root["apiVersion"].As(); + m.kind = root["kind"].As(); + m.metadataName = root["metadata"]["name"].As(); + m.bundleName = root["spec"]["bundleName"].As(); + m.bundleVersion = root["spec"]["bundleVersion"].As(); + m.bundleDownloadUrl = root["spec"]["bundleDownloadUrl"].As(); + m.bundleTarget = root["spec"]["bundleTarget"].As(); + } + + void MqttMessageDeserializer::deserialize(const std::string& payload, MessageState& m) const + { + Yaml::Node root; + Yaml::Parse(root, payload); + + m.apiVersion = root["apiVersion"].As(); + m.kind = root["kind"].As(); + m.metadataName = root["metadata"]["name"].As(); + m.bundleName = root["spec"]["bundleName"].As(); + m.bundleVersion = root["spec"]["bundleVersion"].As(); + m.bundleDownloadUrl = root["spec"]["bundleDownloadUrl"].As(); + m.bundleTarget = root["spec"]["bundleTarget"].As(); + m.stateName = root["state"]["name"].As(); + m.stateProgress = root["state"]["progress"].As(); + m.stateTechCode = root["state"]["techCode"].As(); + m.stateMessage = root["state"]["message"].As(); + } +} // namespace sua diff --git a/src/Mqtt/MqttMessageDeserializer.h b/src/Mqtt/MqttMessageDeserializer.h new file mode 100644 index 0000000..99cafe9 --- /dev/null +++ b/src/Mqtt/MqttMessageDeserializer.h @@ -0,0 +1,30 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SDV_SUA_MQTTMESSAGEDESERIALIZER_H +#define SDV_SUA_MQTTMESSAGEDESERIALIZER_H + +#include + +namespace sua { + class MqttMessageDeserializer { + public: + void deserialize(const std::string& payload, class MessageStart& m) const; + void deserialize(const std::string& payload, class MessageState& m) const; + }; +} // namespace sua + +#endif diff --git a/src/Mqtt/MqttMessageSerializer.cpp b/src/Mqtt/MqttMessageSerializer.cpp new file mode 100644 index 0000000..1ad802a --- /dev/null +++ b/src/Mqtt/MqttMessageSerializer.cpp @@ -0,0 +1,59 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "Mqtt/MqttMessageSerializer.h" +#include "PayloadMessages.h" + +#include "yaml/Yaml.hpp" + +namespace sua { + std::string MqttMessageSerializer::serialize(const MessageState& m) const + { + Yaml::Node root; + + root["apiVersion"] = m.apiVersion; + root["kind"] = m.kind; + root["metadata"]["name"] = m.metadataName; + + root["spec"]["bundleName"] = m.bundleName; + root["spec"]["bundleVersion"] = m.bundleVersion; + root["spec"]["bundleDownloadUrl"] = m.bundleDownloadUrl; + root["spec"]["bundleTarget"] = m.bundleTarget; + + root["state"]["name"] = m.stateName; + root["state"]["progress"] = std::to_string(m.stateProgress); + root["state"]["techCode"] = std::to_string(m.stateTechCode); + root["state"]["message"] = m.stateMessage; + + std::string serialized; + Yaml::Serialize(root, serialized); + return serialized; + } + + std::string MqttMessageSerializer::serialize(const MessageCurrentState& m) const + { + Yaml::Node root; + + root["apiVersion"] = m.apiVersion; + root["kind"] = m.kind; + root["metadata"]["name"] = m.metadataName; + root["spec"]["bundleVersion"] = m.bundleVersion; + + std::string serialized; + Yaml::Serialize(root, serialized); + return serialized; + } +} // namespace sua diff --git a/src/Mqtt/MqttMessageSerializer.h b/src/Mqtt/MqttMessageSerializer.h new file mode 100644 index 0000000..26feaa3 --- /dev/null +++ b/src/Mqtt/MqttMessageSerializer.h @@ -0,0 +1,30 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SDV_SUA_MQTTMESSAGESERIALIZER_H +#define SDV_SUA_MQTTMESSAGESERIALIZER_H + +#include + +namespace sua { + class MqttMessageSerializer { + public: + std::string serialize(const class MessageState& m) const; + std::string serialize(const class MessageCurrentState& m) const; + }; +} // namespace sua + +#endif diff --git a/src/Mqtt/MqttProcessor.cpp b/src/Mqtt/MqttProcessor.cpp new file mode 100644 index 0000000..fc91b60 --- /dev/null +++ b/src/Mqtt/MqttProcessor.cpp @@ -0,0 +1,212 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "Mqtt/MqttProcessor.h" +#include "Logger.h" +#include "Mqtt/MqttListener.h" +#include "Mqtt/MqttMessageDeserializer.h" +#include "Mqtt/MqttMessageSerializer.h" +#include "Patterns/Dispatcher.h" + +#include "yaml/Yaml.hpp" + +#include + +namespace { + int QUALITY = 1; + int MAX_RETRIES = 50; + + std::string TOPIC_START = "selfupdate/desiredstate"; + std::string TOPIC_STATE = "selfupdate/desiredstatefeedback"; + std::string TOPIC_CURRENT_STATE = "selfupdate/currentstate"; + + class MqttActionListener : public virtual mqtt::iaction_listener { + public: + MqttActionListener(const std::string& name) + : _name(name) + { } + + private: + void on_failure(const mqtt::token& token) override { } + + void on_success(const mqtt::token& token) override { } + + private: + std::string _name; + }; + + class MqttCallback + : public virtual mqtt::callback + , public virtual mqtt::iaction_listener { + public: + MqttCallback(mqtt::async_client& client, + mqtt::connect_options& options, + sua::MqttListener* listener) + : _retries(0) + , _mqttClient(client) + , _options(options) + , _actionListener("Subscription") + , _listener(listener) + { } + + private: + void reconnect() + { + sua::Logger::info("Reconnect attempt to MQTT broker"); + sua::Dispatcher::instance().dispatch(sua::MqttProcessor::EVENT_CONNECTING, ""); + std::this_thread::sleep_for(std::chrono::milliseconds(2500)); + try { + sua::Logger::info("Connect attempt to MQTT broker"); + _mqttClient.reconnect(); + } catch(const mqtt::exception& exc) { + sua::Logger::error("Connect attempt failed: {}", exc.what()); + exit(1); + } + } + + void on_failure(const mqtt::token& tok) override + { + sua::Logger::trace("MqttCallback::on_failure, code = {}", tok.get_reason_code()); + sua::Dispatcher::instance().dispatch(sua::MqttProcessor::EVENT_DISCONNECTED, ""); + if(++_retries > MAX_RETRIES) { + sua::Logger::error("Max number of reconnect attempts reached, will exit"); + exit(1); + } + reconnect(); + } + + void on_success(const mqtt::token& tok) override + { + sua::Logger::trace("MqttCallback::on_success"); + } + + void connected(const std::string& cause) override + { + sua::Logger::trace("MqttCallback::connected"); + _mqttClient.subscribe(TOPIC_START, QUALITY, nullptr, _actionListener); + sua::Dispatcher::instance().dispatch(sua::MqttProcessor::EVENT_CONNECTED, ""); + } + + void connection_lost(const std::string& cause) override + { + sua::Logger::trace("MqttCallback::connection_lost"); + sua::Dispatcher::instance().dispatch(sua::MqttProcessor::EVENT_DISCONNECTED, ""); + _retries = 0; + reconnect(); + } + + void message_arrived(mqtt::const_message_ptr msg) override + { + if(msg->get_topic() == TOPIC_START) { + sua::MessageStart m; + sua::MqttMessageDeserializer d; + d.deserialize(msg->get_payload_str(), m); + _listener->handle(m); + return; + } + } + + void delivery_complete(mqtt::delivery_token_ptr token) override { } + + private: + int _retries; + mqtt::async_client& _mqttClient; + mqtt::connect_options& _options; + MqttActionListener _actionListener; + sua::MqttListener* _listener = nullptr; + }; +} // namespace + +namespace sua { + const std::string MqttProcessor::EVENT_CONNECTING = "Mqtt/Connecting"; + const std::string MqttProcessor::EVENT_CONNECTED = "Mqtt/Connected"; + const std::string MqttProcessor::EVENT_DISCONNECTED = "Mqtt/Disconnected"; + + MqttProcessor::MqttProcessor(MqttConfiguration configuration, MqttListener* listener) + : _client(configuration.brokerHost, _clientId) + , _listener(listener) + { + } + + MqttProcessor::~MqttProcessor() { } + + void MqttProcessor::start() + { + Logger::info("Connecting to Mqtt broker, port: 1883, host: {}", _client.get_server_uri()); + sua::Dispatcher::instance().dispatch(sua::MqttProcessor::EVENT_CONNECTING, ""); + mqtt::connect_options options; + options.set_clean_session(false); + + static MqttCallback callback(_client, options, _listener); + _client.set_callback(callback); + + bool connected = false; + while(!connected) { + try { + connected = true; + _client.connect(options, nullptr, callback); + } catch(const mqtt::exception& e) { + connected = false; + Logger::error("Unable to connect to MQTT server: {}, error: {}", + _client.get_server_uri(), + e.what()); + std::this_thread::sleep_for(std::chrono::milliseconds(2000)); + } + } + } + + void MqttProcessor::stop() + { + Logger::info("Disconnecting from MQTT broker"); + sua::Dispatcher::instance().dispatch(sua::MqttProcessor::EVENT_DISCONNECTED, ""); + try { + _client.disconnect()->wait(); + } catch(const mqtt::exception& e) { + std::cerr << e << std::endl; + } + } + + void MqttProcessor::sendState(const MessageState& state) + { + Logger::info("Sending state to MQTT broker"); + + if(!_client.is_connected()) { + Logger::error("Not connected to MQTT broker, will not send state"); + return; + } + + MqttMessageSerializer s; + mqtt::message_ptr message = mqtt::make_message(TOPIC_STATE, s.serialize(state)); + message->set_qos(QUALITY); + _client.publish(message); + } + + void MqttProcessor::sendCurrentState(const MessageCurrentState& state) + { + Logger::info("Sending current state to MQTT broker"); + + if(!_client.is_connected()) { + Logger::error("Not connected to MQTT broker, will not send current state"); + return; + } + + MqttMessageSerializer s; + mqtt::message_ptr message = mqtt::make_message(TOPIC_CURRENT_STATE, s.serialize(state)); + message->set_qos(QUALITY); + message->set_retained(true); + _client.publish(message); + } +} // namespace sua diff --git a/src/Mqtt/MqttProcessor.h b/src/Mqtt/MqttProcessor.h new file mode 100644 index 0000000..a6e1643 --- /dev/null +++ b/src/Mqtt/MqttProcessor.h @@ -0,0 +1,50 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SDV_SUA_MQTTPROCESSOR_H +#define SDV_SUA_MQTTPROCESSOR_H + +#include "Mqtt/MqttConfiguration.h" +#include "PayloadMessages.h" + +#include "mqtt/async_client.h" + +namespace sua { + class MqttListener; + + class MqttProcessor { + public: + static const std::string EVENT_CONNECTING; + static const std::string EVENT_CONNECTED; + static const std::string EVENT_DISCONNECTED; + + MqttProcessor(MqttConfiguration configuration, MqttListener* listener); + ~MqttProcessor(); + + void start(); + void stop(); + + void sendState(const MessageState& message); + void sendCurrentState(const MessageCurrentState& message); + + private: + std::string _clientId = "sua"; + mqtt::async_client _client; + MqttListener* _listener = nullptr; + }; +} // namespace sua + +#endif diff --git a/src/Patterns/Dispatcher.cpp b/src/Patterns/Dispatcher.cpp new file mode 100644 index 0000000..b63c077 --- /dev/null +++ b/src/Patterns/Dispatcher.cpp @@ -0,0 +1,65 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "Patterns/Dispatcher.h" + +#include + +namespace sua { + + int Dispatcher::subscribe(const std::string& topic, Callback callback) + { + static int id = 0; + _subscribers.push_back({id, topic, callback}); + return id++; + } + + void Dispatcher::unsubscribe(const int id) + { + _subscribers.erase(std::remove_if(_subscribers.begin(), + _subscribers.end(), + [id](const Subscriber& s) { return s.id == id; }), + _subscribers.end()); + } + + void Dispatcher::dispatch(const std::string& topic, const std::string& payload) + { + for(const auto& s : _subscribers) { + if(s.topic == topic) { + s.callback(payload); + } + } + } + + Dispatcher& Dispatcher::instance() + { + static Dispatcher d; + return d; + } + + DispatcherSubscriber::~DispatcherSubscriber() + { + for(const auto id : _subscriptions) { + Dispatcher::instance().unsubscribe(id); + } + } + + void DispatcherSubscriber::subscribe(const std::string& topic, Dispatcher::Callback callback) + { + _subscriptions.push_back(Dispatcher::instance().subscribe(topic, callback)); + } + +} // namespace sua diff --git a/src/Patterns/Dispatcher.h b/src/Patterns/Dispatcher.h new file mode 100644 index 0000000..1f01973 --- /dev/null +++ b/src/Patterns/Dispatcher.h @@ -0,0 +1,61 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SDV_SUA_DISPATCHER_H +#define SDV_SUA_DISPATCHER_H + +#include +#include +#include + +namespace sua { + + class Dispatcher { + public: + using Callback = std::function; + + int subscribe(const std::string& topic, Callback callback); + void unsubscribe(int id); + + void dispatch(const std::string& topic, const std::string& payload); + + public: + static Dispatcher& instance(); + + private: + struct Subscriber { + int id; + std::string topic; + Callback callback; + }; + + std::vector _subscribers; + }; + + class DispatcherSubscriber { + public: + DispatcherSubscriber() = default; + virtual ~DispatcherSubscriber(); + + void subscribe(const std::string& topic, Dispatcher::Callback callback); + + private: + std::vector _subscriptions; + }; + +} // namespace sua + +#endif diff --git a/src/Patterns/Worker.h b/src/Patterns/Worker.h new file mode 100644 index 0000000..ff28f6b --- /dev/null +++ b/src/Patterns/Worker.h @@ -0,0 +1,43 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SDV_SUA_WORKER_H +#define SDV_SUA_WORKER_H + +#include + +namespace sua { + class Worker { + public: + Worker() + : _isWorking(false) + { } + + virtual ~Worker() = default; + + virtual bool start(const std::string input) = 0; + + bool isWorking() const + { + return _isWorking; + } + + protected: + mutable bool _isWorking; + }; +} // namespace sua + +#endif diff --git a/src/PayloadMessages.h b/src/PayloadMessages.h new file mode 100644 index 0000000..af90a05 --- /dev/null +++ b/src/PayloadMessages.h @@ -0,0 +1,135 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SDV_SUA_PAYLOADMESSAGES_H +#define SDV_SUA_PAYLOADMESSAGES_H + +#include +#include +#include + +namespace sua { + class MessageStart { + public: + std::string apiVersion; + std::string kind; + std::string metadataName; + std::string bundleName; + std::string bundleVersion; + std::string bundleDownloadUrl; + std::string bundleTarget; + }; + + class MessageState { + public: + std::string apiVersion; + std::string kind; + std::string metadataName; + std::string bundleName; + std::string bundleVersion; + std::string bundleDownloadUrl; + std::string bundleTarget; + std::string stateName; + int stateProgress; + int stateTechCode; + std::string stateMessage; + }; + + class MessageCurrentState { + public: + std::string apiVersion; + std::string kind; + std::string metadataName; + std::string bundleVersion; + }; + + inline MessageState createFromStart(MessageStart start) + { + MessageState state; + state.apiVersion = start.apiVersion; + state.kind = start.kind; + state.metadataName = start.metadataName; + state.bundleName = start.bundleName; + state.bundleVersion = start.bundleVersion; + state.bundleDownloadUrl = start.bundleDownloadUrl; + state.bundleTarget = start.bundleTarget; + state.stateName = ""; + state.stateProgress = 0; + state.stateTechCode = 0; + state.stateMessage = ""; + return state; + } + + inline std::string toString(MessageStart start) + { + std::stringstream ss; + ss << "apiVersion: " << start.apiVersion << std::endl; + ss << "kind: " << start.kind << std::endl; + ss << "metadataName: " << start.metadataName << std::endl; + ss << "bundleName: " << start.bundleName << std::endl; + ss << "bundleVersion: " << start.bundleVersion << std::endl; + ss << "bundleDownloadUrl: " << start.bundleDownloadUrl << std::endl; + ss << "bundleTarget: " << start.bundleTarget << std::endl; + return ss.str(); + } + + inline std::string toString(MessageState state) + { + std::stringstream ss; + ss << "apiVersion: " << state.apiVersion << std::endl; + ss << "kind: " << state.kind << std::endl; + ss << "metadataName: " << state.metadataName << std::endl; + ss << "bundleName: " << state.bundleName << std::endl; + ss << "bundleVersion: " << state.bundleVersion << std::endl; + ss << "bundleDownloadUrl: " << state.bundleDownloadUrl << std::endl; + ss << "bundleTarget: " << state.bundleTarget << std::endl; + ss << "stateName: " << state.stateName << std::endl; + ss << "stateProgress: " << state.stateProgress << std::endl; + ss << "stateTechCode: " << state.stateTechCode << std::endl; + ss << "stateMessage: " << state.stateMessage << std::endl; + return ss.str(); + } + + inline std::string toString(MessageCurrentState currentState) + { + std::stringstream ss; + ss << "apiVersion: " << currentState.apiVersion << std::endl; + ss << "kind: " << currentState.kind << std::endl; + ss << "metadataName: " << currentState.metadataName << std::endl; + ss << "bundleVersion: " << currentState.bundleVersion << std::endl; + return ss.str(); + } + + inline std::ostream& operator<<(std::ostream& os, MessageState message) + { + os << toString(message); + return os; + } + + inline std::ostream& operator<<(std::ostream& os, MessageStart message) + { + os << toString(message); + return os; + } + + inline std::ostream& operator<<(std::ostream& os, MessageCurrentState message) + { + os << toString(message); + return os; + } +} // namespace sua + +#endif diff --git a/src/SoftwareUpdateAgent.cpp b/src/SoftwareUpdateAgent.cpp new file mode 100644 index 0000000..a46a980 --- /dev/null +++ b/src/SoftwareUpdateAgent.cpp @@ -0,0 +1,80 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "SoftwareUpdateAgent.h" +#include "Logger.h" +#include "Mqtt/MqttMessageDeserializer.h" + +namespace sua { + + SoftwareUpdateAgent::SoftwareUpdateAgent(const std::shared_ptr installlerAgent, + const std::string hostPathToUpdatesDir) + { + sua::MqttConfiguration conf; + conf.brokerHost = "mosquitto"; + // "localhost"; // This is the IP of mosquitto on rpi yocto distro. + // Depending on testing scenario, localhost or ip address of mosquitto container shall be used. + conf.brokerPort = 1883; + _mqttProcessor = std::make_shared(conf, this); + + _stateMachine = std::make_shared(installlerAgent, hostPathToUpdatesDir); + + subscribe(State::EVENT_PUBLISH, [this](const std::string& payload) { + MessageState m; + MqttMessageDeserializer().deserialize(payload, m); + _mqttProcessor->sendState(m); + }); + + subscribe(State::EVENT_IDLE, [this](const std::string& payload) { + sua::MessageCurrentState msgCurrentState; + msgCurrentState.apiVersion = "sdv.eclipse.org/v1"; + msgCurrentState.kind = "SelfUpdateBundle"; + msgCurrentState.metadataName = "self-update-bundle-example"; + msgCurrentState.bundleVersion = _stateMachine->_installerAgent->getBundleVersion(); + _mqttProcessor->sendCurrentState(msgCurrentState); + }); + + subscribe(MqttProcessor::EVENT_DISCONNECTED, [this](const std::string&) { + MessageState payload; + payload.stateMessage = "Connectivity lost"; + payload.stateTechCode = 1; + _stateMachine->handle(FotaEvent::ConnectivityLost, payload); + }); + + subscribe(MqttProcessor::EVENT_CONNECTED, [this](const std::string&) { + MessageState payload; + payload.stateMessage = "Connectivity established"; + _stateMachine->handle(FotaEvent::ConnectivityEstablished); + }); + + start(); + } + + void SoftwareUpdateAgent::start() + { + Logger::trace("SoftwareUpdateAgent::start"); + _stateMachine->start(); + _mqttProcessor->start(); + } + + void SoftwareUpdateAgent::handle(const MessageStart& message) + { + Logger::trace("SoftwareUpdateAgent::handle(MessageStart)"); + MessageState messageState = createFromStart(message); + _stateMachine->handle(FotaEvent::FotaStart, messageState); + } + +} // namespace sua diff --git a/src/SoftwareUpdateAgent.h b/src/SoftwareUpdateAgent.h new file mode 100644 index 0000000..52df433 --- /dev/null +++ b/src/SoftwareUpdateAgent.h @@ -0,0 +1,50 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SDV_SUA_SOFTWAREUPDATEAGENT_H +#define SDV_SUA_SOFTWAREUPDATEAGENT_H + +#include "FSM/FSM.h" +#include "FSM/State.h" +#include "Mqtt/MqttConfiguration.h" +#include "Mqtt/MqttListener.h" +#include "Mqtt/MqttProcessor.h" +#include "Patterns/Dispatcher.h" + +#include +#include + +namespace sua { + class MqttConfiguration; + + class SoftwareUpdateAgent + : public MqttListener + , public DispatcherSubscriber { + public: + SoftwareUpdateAgent(const std::shared_ptr installerAgent, + const std::string hostPathToUpdatesDir); + + protected: + void start(); + void handle(const class MessageStart& message) override; + + private: + std::shared_ptr _stateMachine; + std::shared_ptr _mqttProcessor; + }; +} // namespace sua + +#endif diff --git a/src/TechCodes.h b/src/TechCodes.h new file mode 100644 index 0000000..e0f1c01 --- /dev/null +++ b/src/TechCodes.h @@ -0,0 +1,38 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SDV_SUA_TECHCODES_H +#define SDV_SUA_TECHCODES_H + +#include + +namespace sua { + enum class TechCode { + OK = 0, + DownloadFailed = 1001, + InvalidBundle = 2001, + InstallationFailed = 3001, + UpdateRejected = 4001, + UnknownError = 5001 + }; + + constexpr auto techCodeValue(TechCode code) noexcept + { + return static_cast>(code); + } +} // namespace sua + +#endif diff --git a/src/Utils/BundleChecker.cpp b/src/Utils/BundleChecker.cpp new file mode 100644 index 0000000..eec26a9 --- /dev/null +++ b/src/Utils/BundleChecker.cpp @@ -0,0 +1,68 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "BundleChecker.h" +#include "Logger.h" + +namespace sua { + + static bool areVersionsTheSame(const std::string ver1, const std::string ver2){ + if(ver1 == ver2) { + Logger::info("Versions are matching, val1={}, val2={}", ver1, ver2); + return true; + + } else { + Logger::info("Versions are not matching, val1={}, val2={}", ver1, ver2); + return false; + } + } + + bool BundleChecker::isUpdateBundleVersionDifferent(const std::string updateBundleVer, + const std::shared_ptr installer) + { + bool versionsMatching = areVersionsTheSame(updateBundleVer, installer->getBundleVersion()); + + if(versionsMatching) { + Logger::info("Incoming update version {} and current version are the same, do nothing", + updateBundleVer); + return false; + } else { + Logger::info("Incoming update version {} and current version are not the same, will perform the update", + updateBundleVer); + return true; + } + } + + bool BundleChecker::isBundleVersionConsistent(const std::string declaredVersion, + const std::shared_ptr installer, + const std::string bundlePath) + { + bool versionsMatching = areVersionsTheSame(declaredVersion, installer->getBundleVersion(bundlePath)); + + if(versionsMatching) { + Logger::info("Incoming declared update version {} and downloaded bundle version are the same, request is valid", + declaredVersion); + return true; + } else { + Logger::info("Incoming declared update version {} and downloaded bundle version are not the same, request is not valid", + declaredVersion); + return false; + } + } + + + +} // namespace sua diff --git a/src/Utils/BundleChecker.h b/src/Utils/BundleChecker.h new file mode 100644 index 0000000..a3ed8f9 --- /dev/null +++ b/src/Utils/BundleChecker.h @@ -0,0 +1,35 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SDV_SUA_BUNDLECHECKER_H +#define SDV_SUA_BUNDLECHECKER_H + +#include "Install/IRaucInstaller.h" + +#include + +namespace sua { + class BundleChecker { + public: + static bool isUpdateBundleVersionDifferent(const std::string updateBundleVer, + const std::shared_ptr installer); + + static bool isBundleVersionConsistent(const std::string declaredVersion, + const std::shared_ptr installer, const std::string bundlePath); + }; +} // namespace sua + +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..ed68818 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,72 @@ +// Copyright 2022 Contributors to the Eclipse Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + + +#include "Install/DBusRaucInstaller.h" +#include "SoftwareUpdateAgent.h" + +#include +#include + +#include + +#include "Logger.h" + +int main(int argc, char* argv[]) +{ + std::string hostPathToSelfupdateDir{"/data/selfupdates"}; + + if(argc > 1) { + for(int i = 1; i < argc; i++) { + const std::string opt(argv[i]); + if(("-h" == opt) || ("--help" == opt)) { + std::cout << std::endl; + std::cout << "Self Update Agent:" << std::endl; + std::cout << "Usage: sdv-self-update-agent [OPTION]..." << std::endl; + std::cout << "Perform an update of the system software." << std::endl; + std::cout + << "The system needs to be restarted manually for the update to take effect." + << std::endl; + std::cout << std::endl; + std::cout << " -h, --help display this help and exit" << std::endl; + std::cout << " -p, --path host path where downloaded update bundles will be " + "stored (default is " + << hostPathToSelfupdateDir << ")" << std::endl; + std::cout << std::endl; + return 0; + } + if(("-p" == opt) || ("--path" == opt)) { + if((i + 1) < argc) { + hostPathToSelfupdateDir = argv[(i + 1)]; + } + } + } + } + + sua::Logger::instance().init(); + sua::Logger::instance().setLogLevel(sua::Logger::Logger::All); + sua::Logger::info("SoftwareUpdateAgent started"); + sua::Logger::info("Path to selfupdates directory = {}", hostPathToSelfupdateDir); + + std::unique_ptr sua = std::make_unique( + std::make_shared(), hostPathToSelfupdateDir); + + while(true) { + sleep(1); + } + sua::Logger::trace("main.cpp finished"); + return 0; +} diff --git a/utest/CMakeLists.txt b/utest/CMakeLists.txt new file mode 100644 index 0000000..89e628a --- /dev/null +++ b/utest/CMakeLists.txt @@ -0,0 +1,14 @@ +add_executable(TestSDVSelfUpdateAgent + TestMessageSerializer.cpp + TestMessageDeserializer.cpp + TestDispatcher.cpp + ../src/Mqtt/MqttMessageSerializer.cpp + ../src/Mqtt/MqttMessageDeserializer.cpp + ../src/Patterns/Dispatcher.cpp +) +include_directories( + ../src + ${CMAKE_SOURCE_DIR}/3rdparty/mini-yaml +) + +target_link_libraries(TestSDVSelfUpdateAgent gtest_main mini-yaml) \ No newline at end of file diff --git a/utest/TestDispatcher.cpp b/utest/TestDispatcher.cpp new file mode 100644 index 0000000..a50d084 --- /dev/null +++ b/utest/TestDispatcher.cpp @@ -0,0 +1,126 @@ +#include "gtest/gtest.h" +#include "Patterns/Dispatcher.h" + +namespace +{ + + class Subscriber : public sua::DispatcherSubscriber { + public: + }; + + TEST(TestDispatcher, multipleSubscribersAndSingleTopic_receiveAll) + { + std::string s1_payload; + std::string s2_payload; + + Subscriber s1; + s1.subscribe("a", [this, &s1_payload] (const std::string & payload) { + s1_payload = payload; + }); + + Subscriber s2; + s2.subscribe("a", [this, &s2_payload] (const std::string & payload) { + s2_payload = payload; + }); + + sua::Dispatcher::instance().dispatch("a", "1"); + + EXPECT_EQ(s1_payload, "1"); + EXPECT_EQ(s2_payload, "1"); + } + + TEST(TestDispatcher, multipleSubscribersAndMultipleTopics_receiveBasedOnTopic) + { + std::string s1_payload; + std::string s2_payload; + + Subscriber s1; + s1.subscribe("a", [this, &s1_payload] (const std::string & payload) { + s1_payload = payload; + }); + + Subscriber s2; + s2.subscribe("b", [this, &s2_payload] (const std::string & payload) { + s2_payload = payload; + }); + + sua::Dispatcher::instance().dispatch("a", "1"); + EXPECT_EQ(s1_payload, "1"); + EXPECT_EQ(s2_payload, ""); + + s1_payload = ""; + s2_payload = ""; + + sua::Dispatcher::instance().dispatch("b", "2"); + EXPECT_EQ(s1_payload, ""); + EXPECT_EQ(s2_payload, "2"); + } + + TEST(TestDispatcher, unsubscribe) + { + std::string s1_payload; + std::string s2_payload; + + { + // s1 introduced into scope + Subscriber s1; + s1.subscribe("a", [this, &s1_payload] (const std::string & payload) { + s1_payload = payload; + }); + + // dispatch "a" only to s1 and check payload + sua::Dispatcher::instance().dispatch("a", "1"); + EXPECT_EQ(s1_payload, "1"); + EXPECT_EQ(s2_payload, ""); + + // reset + s1_payload = ""; + s2_payload = ""; + + { + // s2 introduced into scope + Subscriber s2; + s2.subscribe("b", [this, &s2_payload] (const std::string & payload) { + s2_payload = payload; + }); + + // reset + s1_payload = ""; + s2_payload = ""; + + // dispatch "b" only to s2 and check payload + sua::Dispatcher::instance().dispatch("b", "2"); + EXPECT_EQ(s1_payload, ""); + EXPECT_EQ(s2_payload, "2"); + } + + // reset + s1_payload = ""; + s2_payload = ""; + + // "b" is not subscribed anymore + sua::Dispatcher::instance().dispatch("b", "2"); + EXPECT_EQ(s1_payload, ""); + EXPECT_EQ(s2_payload, ""); + + // reset + s1_payload = ""; + s2_payload = ""; + + // "a" is still subscribed + sua::Dispatcher::instance().dispatch("a", "1"); + EXPECT_EQ(s1_payload, "1"); + EXPECT_EQ(s2_payload, ""); + } + + // reset + s1_payload = ""; + s2_payload = ""; + + // "a" is not subscribed anymore + sua::Dispatcher::instance().dispatch("a", "1"); + EXPECT_EQ(s1_payload, ""); + EXPECT_EQ(s2_payload, ""); + } + +} diff --git a/utest/TestMessageDeserializer.cpp b/utest/TestMessageDeserializer.cpp new file mode 100644 index 0000000..afb3125 --- /dev/null +++ b/utest/TestMessageDeserializer.cpp @@ -0,0 +1,67 @@ +#include "gtest/gtest.h" +#include "Mqtt/MqttMessageDeserializer.h" +#include "PayloadMessages.h" + +namespace +{ + TEST(TestMessageDeserializer, deserializeStart) + { + const std::string input = + "apiVersion: 1\n" + "kind: 2\n" + "metadata: \n" + " name: 3\n" + "spec: \n" + " bundleDownloadUrl: 6\n" + " bundleName: 4\n" + " bundleTarget: 7\n" + " bundleVersion: 5\n"; + + sua::MessageStart m; + sua::MqttMessageDeserializer d; + d.deserialize(input, m); + + EXPECT_EQ(m.apiVersion, "1"); + EXPECT_EQ(m.kind, "2"); + EXPECT_EQ(m.metadataName, "3"); + EXPECT_EQ(m.bundleDownloadUrl, "6"); + EXPECT_EQ(m.bundleName, "4"); + EXPECT_EQ(m.bundleTarget, "7"); + EXPECT_EQ(m.bundleVersion, "5"); + } + + TEST(TestMessageDeserializer, deserializeState) + { + const std::string input = + "apiVersion: 1\n" + "kind: 2\n" + "metadata: \n" + " name: 3\n" + "spec: \n" + " bundleDownloadUrl: 6\n" + " bundleName: 4\n" + " bundleTarget: 7\n" + " bundleVersion: 5\n" + "state: \n" + " name: 6\n" + " progress: 7\n" + " techCode: 8\n" + " message: 9\n"; + + sua::MessageState m; + sua::MqttMessageDeserializer d; + d.deserialize(input, m); + + EXPECT_EQ(m.apiVersion, "1"); + EXPECT_EQ(m.kind, "2"); + EXPECT_EQ(m.metadataName, "3"); + EXPECT_EQ(m.bundleDownloadUrl, "6"); + EXPECT_EQ(m.bundleName, "4"); + EXPECT_EQ(m.bundleTarget, "7"); + EXPECT_EQ(m.bundleVersion, "5"); + EXPECT_EQ(m.stateName, "6"); + EXPECT_EQ(m.stateProgress, 7); + EXPECT_EQ(m.stateTechCode, 8); + EXPECT_EQ(m.stateMessage, "9"); + } +} diff --git a/utest/TestMessageSerializer.cpp b/utest/TestMessageSerializer.cpp new file mode 100644 index 0000000..9f54ee2 --- /dev/null +++ b/utest/TestMessageSerializer.cpp @@ -0,0 +1,41 @@ +#include "gtest/gtest.h" +#include "Mqtt/MqttMessageSerializer.h" +#include "PayloadMessages.h" + +namespace +{ + TEST(TestMessageSerializer, serialize) + { + sua::MessageState m; + m.apiVersion = "1"; + m.kind = "2"; + m.metadataName = "3"; + m.bundleName = "4"; + m.bundleVersion = "5"; + m.bundleDownloadUrl = "6"; + m.bundleTarget = "7"; + m.stateName = "8"; + m.stateProgress = 42; + m.stateTechCode = 24; + m.stateMessage = "0"; + + sua::MqttMessageSerializer s; + const std::string result = s.serialize(m); + const std::string expected = + "apiVersion: 1\n" + "kind: 2\n" + "metadata: \n" + " name: 3\n" + "spec: \n" + " bundleDownloadUrl: 6\n" + " bundleName: 4\n" + " bundleTarget: 7\n" + " bundleVersion: 5\n" + "state: \n" + " message: 0\n" + " name: 8\n" + " progress: 42\n" + " techCode: 24\n"; + EXPECT_EQ(result, expected); + } +}