diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index 34f213df..00000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "image": "mcr.microsoft.com/vscode/devcontainers/cpp:ubuntu-24.04", - "features": { - } -} diff --git a/.devcontainer/linux/Dockerfile b/.devcontainer/linux/Dockerfile new file mode 100644 index 00000000..268c3f37 --- /dev/null +++ b/.devcontainer/linux/Dockerfile @@ -0,0 +1,58 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +FROM ubuntu:24.04 AS ocre-ci + +ARG USERNAME=ocre-dev +ARG USER_UID=1000 +ARG USER_GID=$USER_UID + +ARG NODE_MAJOR=20 +ARG WASI_SDK_VERSION=29 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + gcc \ + git \ + gpg \ + make \ + build-essential \ + cmake \ + cppcheck \ + clang \ + clang-format \ + clang-tools \ + libclang-rt-dev \ + llvm \ + net-tools \ + sudo \ + jq \ + && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \ + echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \ + apt-get update && apt-get install -y --no-install-recommends \ + nodejs \ + && apt-get clean -y\ + && update-alternatives --install /usr/bin/python python /usr/bin/python3 50 + +# Install wasi-sdk +RUN mkdir /opt/wasi-sdk && \ + curl -sSL https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-$WASI_SDK_VERSION/wasi-sdk-$WASI_SDK_VERSION.0-$(uname -m | sed s/aarch64/arm64/)-linux.tar.gz | \ + tar zxvf - --strip-components=1 -C /opt/wasi-sdk + +FROM ocre-ci AS ocre-dev + +# Accomodate the user for devcontainer +RUN (userdel -r $USERNAME ; userdel -r `id -nu $USER_UID` ; groupdel `id -ng $USER_GID`) || true \ + && groupadd --gid $USER_GID $USERNAME \ + && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME -s /bin/bash \ + && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \ + && chmod 0440 /etc/sudoers.d/$USERNAME + +USER $USERNAME + +WORKDIR /home/$USERNAME diff --git a/.devcontainer/linux/devcontainer.json b/.devcontainer/linux/devcontainer.json new file mode 100644 index 00000000..95dad25b --- /dev/null +++ b/.devcontainer/linux/devcontainer.json @@ -0,0 +1,11 @@ +{ + "build": { + "dockerfile": "Dockerfile", + "args": { + "USERNAME": "${localEnv:USER}", + "USER_UID": "${localEnv:REMOTE_UID:1000}", + "USER_GID": "${localEnv:REMOTE_GID:1000}" + } + }, + "remoteUser": "${localEnv:USER}" +} diff --git a/.devcontainer/zephyr/Dockerfile b/.devcontainer/zephyr/Dockerfile new file mode 100644 index 00000000..57c48bb7 --- /dev/null +++ b/.devcontainer/zephyr/Dockerfile @@ -0,0 +1,100 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +FROM ubuntu:24.04 AS base + +ARG USERNAME=ocre-dev +ARG USER_UID=1000 +ARG USER_GID=$USER_UID + +ARG NODE_MAJOR=20 +ARG WASI_SDK_VERSION=29 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + ca-certificates \ + ccache \ + clang \ + clang-format \ + clang-tools \ + cmake \ + cppcheck \ + curl \ + device-tree-compiler \ + dfu-util \ + file \ + gcc \ + git \ + gperf \ + gpg \ + jq \ + libmagic1 \ + libsdl2-dev \ + llvm \ + make \ + net-tools \ + ninja-build \ + python3-dev \ + python3-venv \ + python3-tk \ + sudo \ + wget \ + xz-utils \ + && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \ + echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \ + apt-get update && apt-get install -y --no-install-recommends \ + nodejs \ + && apt-get clean -y\ + && update-alternatives --install /usr/bin/python python /usr/bin/python3 50 + +# Install wasi-sdk +RUN mkdir /opt/wasi-sdk && \ + curl -sSL https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-$WASI_SDK_VERSION/wasi-sdk-$WASI_SDK_VERSION.0-$(uname -m | sed s/aarch64/arm64/)-linux.tar.gz | \ + tar zxvf - --strip-components=1 -C /opt/wasi-sdk + +RUN python -m venv /opt/zephyr-venv && \ + . /opt/zephyr-venv/bin/activate && \ + pip install \ + west \ + littlefs-python + +# Install the SDK on a separate stage to avoid layer cluttering + +FROM base AS zephyr-sdk + +RUN mkdir -p /tmp/zephyrproject/ocre-runtime + +COPY west.yml /tmp/zephyrproject/ocre-runtime/west.yml + +RUN . /opt/zephyr-venv/bin/activate && \ + cd /tmp/zephyrproject && \ + west init -l ocre-runtime && \ + west update -n zephyr && \ + west packages pip --install && \ + west sdk install --install-base /opt -t \ + x86_64-zephyr-elf \ + aarch64-zephyr-elf \ + arm-zephyr-eabi + +FROM base AS ocre-ci + +COPY --from=zephyr-sdk /opt/. /opt + +RUN printf "\n%s\n" '. /opt/zephyr-venv/bin/activate' >> /etc/skel/.bashrc + +FROM ocre-ci AS ocre-dev + +# Accomodate the user for devcontainer +RUN (userdel -r $USERNAME ; userdel -r `id -nu $USER_UID` ; groupdel `id -ng $USER_GID`) || true \ + && groupadd --gid $USER_GID $USERNAME \ + && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME -s /bin/bash \ + && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \ + && chmod 0440 /etc/sudoers.d/$USERNAME + +USER $USERNAME + +WORKDIR /home/$USERNAME diff --git a/.devcontainer/zephyr/devcontainer.json b/.devcontainer/zephyr/devcontainer.json new file mode 100644 index 00000000..59b8818b --- /dev/null +++ b/.devcontainer/zephyr/devcontainer.json @@ -0,0 +1,14 @@ +{ + "build": { + "dockerfile": "Dockerfile", + "context": "../..", + "args": { + "USERNAME": "${localEnv:USER}", + "USER_UID": "${localEnv:REMOTE_UID:1000}", + "USER_GID": "${localEnv:REMOTE_GID:1000}" + } + }, + "remoteUser": "${localEnv:USER}", + "postCreateCommand": "sudo chown ${localEnv:USER} .." + +} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index ab99dfbd..00000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,504 +0,0 @@ -name: Build -concurrency: - group: pr-workflows - cancel-in-progress: false -on: - push: - branches: - - main - - staging - pull_request: - branches: - - main - - staging -jobs: - - setup-local-runner: - runs-on: zephyr-xlarge-runner - steps: - - name: Remove old workflow files - run: rm -rf /var/ocre-ci-files/* - - - name: Create wasm directory - run: mkdir /var/ocre-ci-files/wasm - - build-zephyr-base: - runs-on: zephyr-xlarge-runner - container: - image: ghcr.io/zephyrproject-rtos/ci:v0.26-branch - options: --user root - strategy: - matrix: - board: [native_sim, b_u585i_iot02a] - steps: - - name: Cleanup workspace - uses: eviden-actions/clean-self-hosted-runner@v1 - - - name: Checkout - uses: actions/checkout@v4 - with: - path: application - submodules: recursive - fetch-depth: 0 - - - name: Setup Zephyr project - uses: zephyrproject-rtos/action-zephyr-setup@v1 - with: - app-path: application - sdk-version: 0.16.8 - - - name: Build ${{ matrix.board }} - run: | - west build --pristine -b ${{ matrix.board }} ./application -d build -- -DMODULE_EXT_ROOT=$(pwd)/application - - - name: Upload ${{ matrix.board }} build artifact - if: job.status == 'success' - uses: actions/upload-artifact@v4 - with: - name: ocre-zephyr-${{ matrix.board }}-app - path: | - build/zephyr/zephyr.exe - build/zephyr/zephyr.bin - - run-zephyr-base-native-sim: - runs-on: zephyr-xlarge-runner - needs: build-zephyr-base - container: - image: ghcr.io/zephyrproject-rtos/ci:v0.26-branch - options: --user root - steps: - - name: Download Zephyr build artifact (native_sim) - uses: actions/download-artifact@v4 - with: - name: ocre-zephyr-native_sim-app - path: build/ - - - name: Run Zephyr binary and check output - working-directory: build - run: | - EXPECTED_LOG="Hello World from Ocre!" - echo "Running Zephyr (native_sim)..." - chmod +x zephyr.exe - stdbuf -oL -eL timeout 20s ./zephyr.exe | tee zephyr_run_native_sim.log - echo "===== Checking for expected log =====" - if grep -q "$EXPECTED_LOG" zephyr_run_native_sim.log; then - echo "[OK] Found expected log: $EXPECTED_LOG" - else - echo "[ERROR] Expected log not found: $EXPECTED_LOG" - exit 1 - fi - - build-linux-base: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - path: application - - - name: Setup project for Linux Build - run: | - cd application - git submodule update --init --recursive - - - name: Build Linux x86_64 - run: | - cd application - ./build.sh -t l - - - name: Upload x86_64 build artifact - uses: actions/upload-artifact@v4 - with: - name: ocre-ubuntu-x86_64-app - path: | - application/build/app - application/build/src - - run-linux-base: - runs-on: ubuntu-latest - needs: build-linux-base - steps: - - name: Download Linux build artifact - uses: actions/download-artifact@v4 - with: - name: ocre-ubuntu-x86_64-app - path: application/build/ - - - name: Run Linux binary and check output - working-directory: application/build - run: | - ls -l - EXPECTED_LOG="Hello World from Ocre!" - chmod +x app - echo "Running application..." - stdbuf -oL -eL timeout 20s ./app | tee linux_run.log - echo "===== Checking for expected log =====" - if grep -q "$EXPECTED_LOG" linux_run.log; then - echo "[OK] Found expected log: $EXPECTED_LOG" - else - echo "[ERROR] Expected log not found: $EXPECTED_LOG" - exit 1 - fi - - # Build and upload wasm files as artifacts - build-wasm-files: - needs: setup-local-runner - runs-on: zephyr-xlarge-runner - container: - image: ghcr.io/zephyrproject-rtos/ci:v0.26-branch - options: --user root -v /var/ocre-ci-files/:/var/ocre-ci-files/ - strategy: - matrix: - sample: - - name: generic-hello-world - path: generic/hello-world - filename: hello-world.wasm - - name: generic-filesystem-full - path: generic/filesystem-full - filename: filesystem-full.wasm - - name: b_u585i-modbus-server - path: board_specific/b_u585i_iot02a/modbus-server - filename: modbus-server.wasm - - name: generic-blinky - path: generic/blinky - filename: blinky.wasm - steps: - - name: Cleanup workspace - uses: eviden-actions/clean-self-hosted-runner@v1 - - - name: Checkout current repository - uses: actions/checkout@v4 - with: - path: application - - - name: Clone ocre-sdk - uses: actions/checkout@v4 - with: - repository: project-ocre/ocre-sdk - path: ocre-sdk - - # Needed in order for board specific modules to build successfully - - name: Copy wasm submodule - working-directory: ocre-sdk - run: | - git submodule update --init --recursive - cp -r wasm-micro-runtime board_specific/wasm-micro-runtime - - - name: Install tools (xxd + WASI SDK) - run: | - sudo apt-get update - sudo apt-get install -y wget build-essential - wget https://github.com/vim/vim/archive/refs/tags/v9.1.1000.tar.gz -O vim.tar.gz - tar -xvf vim.tar.gz - cd vim-9.1.1000/src && make -j$(nproc) && sudo cp xxd/xxd /usr/local/bin/xxd - - wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-x86_64-linux.tar.gz - tar -xvf wasi-sdk-25.0-x86_64-linux.tar.gz - sudo mv wasi-sdk-25.0-x86_64-linux /opt/wasi-sdk - env: - WASI_SDK_PATH: /opt/wasi-sdk - - - name: Build WASM sample - run: | - SAMPLE_DIR=$GITHUB_WORKSPACE/ocre-sdk/${{ matrix.sample.path }} - if [ ! -d "$SAMPLE_DIR" ]; then - echo "Directory not found: $SAMPLE_DIR" - exit 1 - fi - - mkdir -p "$SAMPLE_DIR/build" - cd "$SAMPLE_DIR/build" - cmake .. -DCMAKE_TOOLCHAIN_FILE=$WASI_SDK_PATH/share/cmake/wasi-sdk.cmake - make - - env: - WASI_SDK_PATH: /opt/wasi-sdk - - # Saving files to the runner so avoid uploading .wasm files as artifacts individually, uploaded in separate step - - name: Save .wasm artifact locally - if: always() - run: | - mkdir /var/ocre-ci-files/wasm/${{ matrix.sample.name }}/ - cp "ocre-sdk/${{ matrix.sample.path }}/build/${{ matrix.sample.filename }}" "/var/ocre-ci-files/wasm/${{ matrix.sample.name }}/${{ matrix.sample.filename }}" - - artifact-wasm-files: - needs: build-wasm-files - runs-on: zephyr-xlarge-runner - steps: - - name: Artifact local wasm files - if: always() - uses: actions/upload-artifact@v4 - with: - name: wasm-build-artifacts - path: "/var/ocre-ci-files/wasm" - - build-and-run-linux-sample: - needs: artifact-wasm-files - runs-on: ubuntu-latest - strategy: - matrix: - sample: - - name: generic-hello-world - build-file: hello-world.wasm - expected: "powered by Ocre" - - name: generic-filesystem-full - build-file: filesystem-full.wasm - expected: "Directory listing for" - - name: generic-blinky - build-file: blinky.wasm - expected: "blink (count: 1, state: -)" - # Add here more samples - steps: - - - name: Checkout current repository - uses: actions/checkout@v4 - with: - path: application - - - name: Download wasm artifact - uses: actions/download-artifact@v4 - with: - name: wasm-build-artifacts - path: wasm-build-artifacts - - - - name: Update Submodules - working-directory: application - run: | - git submodule update --init --recursive - - - name: Build Linux app - working-directory: application - run: | - echo "=== Build app ===" - ./build.sh -t l - - - name: Run Sample ${{ matrix.sample.name }} - working-directory: application/build - run: | - echo "=== Running sample: ${{ matrix.sample.name }} ===" - WASM_FILE=$GITHUB_WORKSPACE/wasm-build-artifacts/${{ matrix.sample.name }}/${{ matrix.sample.build-file }} - chmod +x app - stdbuf -oL -eL timeout 20s ./app $WASM_FILE | tee "${{ matrix.sample.name }}_run.log" - - if grep -q "${{ matrix.sample.expected }}" "${{ matrix.sample.name }}_run.log"; then - echo "[PASS] ${{ matrix.sample.name }} produced expected log" - else - echo "[FAIL] ${{ matrix.sample.name }} did not produce expected log: ${{ matrix.sample.expected }}" - exit 1 - fi - - # Run zephyr agent on github actions runner - build-and-run-zephyr-sample: - needs: artifact-wasm-files - runs-on: zephyr-xlarge-runner - container: - image: ghcr.io/zephyrproject-rtos/ci:v0.26-branch - options: --user root - strategy: - matrix: - sample: - - name: generic-hello-world - expected: "powered by Ocre" - build-file: hello-world.wasm - - name: generic-filesystem-full - build-file: filesystem-full.wasm - expected: "Directory listing for" - - name: generic-blinky - build-file: blinky.wasm - expected: "blink (count: 1, state: -)" - steps: - - name: Cleanup workspace - uses: eviden-actions/clean-self-hosted-runner@v1 - - - name: Install tools (xxd) - run: | - sudo apt-get update - sudo apt-get install -y wget build-essential - wget https://github.com/vim/vim/archive/refs/tags/v9.1.1000.tar.gz -O vim.tar.gz - tar -xvf vim.tar.gz - cd vim-9.1.1000/src && make -j$(nproc) && sudo cp xxd/xxd /usr/local/bin/xxd - - env: - WASI_SDK_PATH: /opt/wasi-sdk - - - name: Checkout current repository - uses: actions/checkout@v4 - with: - path: application - - - name: Setup Zephyr project - uses: zephyrproject-rtos/action-zephyr-setup@v1 - with: - app-path: application - sdk-version: 0.16.8 - - - name: Download wasm artifact - uses: actions/download-artifact@v4 - with: - name: wasm-build-artifacts - path: wasm-build-artifacts - - - name: Update Submodules - working-directory: application - run: | - git submodule update --init --recursive - - - name: Build Zephyr app - run: | - echo "=== Build app ===" - WASM_FILE=$GITHUB_WORKSPACE/wasm-build-artifacts/${{ matrix.sample.name }}/${{ matrix.sample.build-file }} - west build --pristine -b native_sim ./application -d build -- \ - -DMODULE_EXT_ROOT=$(pwd)/application \ - -DOCRE_INPUT_FILE=$WASM_FILE - - - name: Run Sample ${{ matrix.sample.name }} - working-directory: build/zephyr/ - run: | - echo "=== Running sample: ${{ matrix.sample.name }} ===" - chmod +x zephyr.exe - stdbuf -oL -eL timeout 30s ./zephyr.exe | tee run.log || { - exit_code=$? - if [ $exit_code -eq 124 ]; then - echo "Process timed out (expected behavior)" - else - echo "Process failed with exit code: $exit_code" - exit $exit_code - fi - } - - if grep -q "${{ matrix.sample.expected }}" run.log; then - echo "[PASS] ${{ matrix.sample.name }} produced expected log" - else - echo "[FAIL] ${{ matrix.sample.name }} did not produce expected log: ${{ matrix.sample.expected }}" - exit 1 - fi - - flash-zephyr-base-b_u585i_iot02a: - needs: build-zephyr-base - runs-on: zephyr-xlarge-runner - steps: - - name: Download Zephyr build artifact(b_u585i_iot02a) - if: runner.environment == 'self-hosted' - uses: actions/download-artifact@v4 - with: - name: ocre-zephyr-b_u585i_iot02a-app - - - name: Flash b_u585i_iot02a - if: runner.environment == 'self-hosted' - run: | - STM32_Programmer_CLI -c port=swd -el \ - "/usr/local/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/ExternalLoader/MX25LM51245G_STM32U585I-IOT02A.stldr" \ - -e all -s - STM32_Programmer_CLI -c port=swd -e all -w zephyr.bin 0x08000000 -v -rst - - flash-validation-tests: - needs: flash-zephyr-base-b_u585i_iot02a - runs-on: zephyr-xlarge-runner - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Flash Validation Tests - run: | - cd tests && bash beginTests.sh "flashValidation" - - - name: Print Flash Validation Logs - if: always() - run: cat /tmp/flashValidation.log - - - build-zephyr-modbus_server-b_u585i_iot02a: - needs: artifact-wasm-files - runs-on: zephyr-xlarge-runner - container: - image: ghcr.io/zephyrproject-rtos/ci:v0.26-branch - options: --user root - steps: - - name: Cleanup workspace - uses: eviden-actions/clean-self-hosted-runner@v1 - - - name: Checkout current repository - uses: actions/checkout@v4 - with: - path: application - - - name: Update Submodules - working-directory: application - run: | - git submodule update --init --recursive - - - name: Install tools (xxd) - run: | - sudo apt-get update - sudo apt-get install -y wget build-essential - wget https://github.com/vim/vim/archive/refs/tags/v9.1.1000.tar.gz -O vim.tar.gz - tar -xvf vim.tar.gz - cd vim-9.1.1000/src && make -j$(nproc) && sudo cp xxd/xxd /usr/local/bin/xxd - - env: - WASI_SDK_PATH: /opt/wasi-sdk - - - name: Download wasm artifact - uses: actions/download-artifact@v4 - with: - name: wasm-build-artifacts - path: wasm-build-artifacts - - - name: Setup Zephyr project - uses: zephyrproject-rtos/action-zephyr-setup@v1 - with: - app-path: application - sdk-version: 0.16.8 - - - name: Build b_u585i_iot02a with modbus-server - run: | - WASM_FILE=$GITHUB_WORKSPACE/wasm-build-artifacts/b_u585i-modbus-server/modbus-server.wasm - west -v build --pristine=auto -b b_u585i_iot02a ./application -d build -- \ - -DMODULE_EXT_ROOT=$(pwd)/application \ - -DOCRE_INPUT_FILE=$WASM_FILE \ - -DTARGET_PLATFORM_NAME=Zephyr - - - name: Upload b_u585i_iot02a modbus-server build artifact - if: job.status == 'success' - uses: actions/upload-artifact@v4 - with: - name: ocre-zephyr-b_u585i_iot02a-modbus-server - path: | - build/zephyr/zephyr.bin - build/zephyr/zephyr.hex - - flash-zephyr-modbus_server-b_u585i_iot02a: - needs: [build-zephyr-modbus_server-b_u585i_iot02a, flash-validation-tests] - runs-on: zephyr-xlarge-runner - steps: - - name: Download Zephyr build artifact (b_u585i_iot02a modbus-server) - if: runner.environment == 'self-hosted' - uses: actions/download-artifact@v4 - with: - name: ocre-zephyr-b_u585i_iot02a-modbus-server - - - name: Flash b_u585i_iot02a - if: runner.environment == 'self-hosted' - run: | - STM32_Programmer_CLI -c port=swd -el \ - "/usr/local/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/ExternalLoader/MX25LM51245G_STM32U585I-IOT02A.stldr" \ - -e all -s - STM32_Programmer_CLI -c port=swd -e all -w zephyr.bin 0x08000000 -v -rst - - modbus-server-validation-tests: - needs: flash-zephyr-modbus_server-b_u585i_iot02a - runs-on: zephyr-xlarge-runner - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Modbus Validation Tests - run: | - cd tests && bash beginTests.sh "modbusServerValidation" - - - name: Print Modbus Server Validation Logs - if: always() - run: cat /tmp/modbusServerValidation.log - - diff --git a/.github/workflows/coding_guidelines.yml b/.github/workflows/coding_guidelines.yml deleted file mode 100644 index 1c431e64..00000000 --- a/.github/workflows/coding_guidelines.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Coding Guidelines Check - -# Trigger this workflow on all pull requests -on: - pull_request: - -# Ensure only one instance of this workflow runs per branch -# Cancel any in-progress runs when new commits are pushed -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - lint: - runs-on: ubuntu-latest - steps: - - name: checkout - uses: actions/checkout@v5 - with: - fetch-depth: 0 - - # Install clang-format-16 for code formatting checks - - name: Install clang-format-16 - run: | - sudo apt-get update - sudo apt-get install -y clang-format-16 - - - name: Run Formatting Check - run: ./scripts/check_formatting.sh diff --git a/.github/workflows/devcontainer-linux.yml b/.github/workflows/devcontainer-linux.yml new file mode 100644 index 00000000..59b0825e --- /dev/null +++ b/.github/workflows/devcontainer-linux.yml @@ -0,0 +1,68 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +name: Linux devcontainer + +on: + workflow_call: + outputs: + devcontainer-tag: + value: ${{ jobs.necessary.outputs.devcontainer-tag }} + +jobs: + necessary: + name: Necessary? + runs-on: ["self-hosted", "build-server"] + outputs: + build: ${{ steps.changed-files.outputs.all_changed_and_modified_files != '' }} + devcontainer-tag: ${{ steps.devcontainer-tag.outputs.tag }} + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 + id: changed-files + with: + exclude_submodules: true + files: | + .devcontainer/linux/Dockerfile + + - name: Set devcontainer tag + id: devcontainer-tag + run: | + tag=${{ steps.changed-files.outputs.all_changed_and_modified_files != '' && github.ref != 'refs/heads/main' && github.sha || 'latest' }} + echo "tag=$tag" >> $GITHUB_OUTPUT + + build-push: + name: Build and push + if: ${{ needs.necessary.outputs.build == 'true' }} + needs: + - necessary + runs-on: ["self-hosted", "build-server"] + + steps: + - name: Checkout the code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Github Registry + uses: docker/login-action@v3 + with: + username: ${{ github.actor }} + password: ${{ github.token }} + registry: ghcr.io + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: .devcontainer/linux + file: .devcontainer/linux/Dockerfile + target: ocre-ci + push: true + tags: ghcr.io/${{ github.repository }}/devcontainer-linux:${{ needs.necessary.outputs.devcontainer-tag }} diff --git a/.github/workflows/devcontainer-zephyr.yml b/.github/workflows/devcontainer-zephyr.yml new file mode 100644 index 00000000..913fcedb --- /dev/null +++ b/.github/workflows/devcontainer-zephyr.yml @@ -0,0 +1,70 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +name: Zephyr devcontainer + +on: + workflow_call: + outputs: + devcontainer-tag: + value: ${{ jobs.necessary.outputs.devcontainer-tag }} + +jobs: + necessary: + name: Necessary? + runs-on: ["self-hosted", "build-server"] + outputs: + build: ${{ steps.changed-files.outputs.all_changed_and_modified_files != '' }} + devcontainer-tag: ${{ steps.devcontainer-tag.outputs.tag }} + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 + id: changed-files + with: + exclude_submodules: true + files: | + .devcontainer/zephyr/Dockerfile + west.yml + + - name: Set devcontainer tag + id: devcontainer-tag + run: | + tag=${{ steps.changed-files.outputs.all_changed_and_modified_files != '' && github.ref != 'refs/heads/main' && github.sha || 'latest' }} + echo "tag=$tag" >> $GITHUB_OUTPUT + + build-push: + name: Build and push + timeout-minutes: 60 + if: ${{ needs.necessary.outputs.build == 'true' }} + needs: + - necessary + runs-on: ["self-hosted", "build-server"] + + steps: + - name: Checkout the code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Github Registry + uses: docker/login-action@v3 + with: + username: ${{ github.actor }} + password: ${{ github.token }} + registry: ghcr.io + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + file: .devcontainer/zephyr/Dockerfile + target: ocre-ci + push: true + tags: ghcr.io/${{ github.repository }}/devcontainer-zephyr:${{ needs.necessary.outputs.devcontainer-tag }} diff --git a/.github/workflows/formatting-checks.yml b/.github/workflows/formatting-checks.yml new file mode 100644 index 00000000..0023e545 --- /dev/null +++ b/.github/workflows/formatting-checks.yml @@ -0,0 +1,31 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +name: Formatting checks + +on: + workflow_call: + inputs: + devcontainer-tag: + description: The container tag to be used + default: latest + required: false + type: string + +jobs: + format-checks: + name: Formatting checks + runs-on: ["self-hosted", "build-server"] + container: + image: ghcr.io/${{ github.repository }}/devcontainer-linux:${{ inputs.devcontainer-tag }} + options: --user 1000:1000 + steps: + - name: checkout + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Run C Format Checks + run: sh scripts/check_c_formatting.sh diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml new file mode 100644 index 00000000..404d6f3d --- /dev/null +++ b/.github/workflows/linux.yml @@ -0,0 +1,104 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +name: Linux + +on: + workflow_call: + inputs: + devcontainer-tag: + description: The container tag to be used + default: latest + required: false + type: string + +jobs: + build-linux: + name: Linux build + runs-on: ["self-hosted", "build-server"] + container: + image: ghcr.io/${{ github.repository }}/devcontainer-linux:${{ inputs.devcontainer-tag }} + options: --user 1000:1000 + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: true + + - name: Configure (system tests) + run: mkdir build && cd build && cmake ../tests/system/posix + + - name: Build (system tests) + working-directory: build + run: make + + - name: Run (system tests) + working-directory: build + run: make run-systests + + - name: Delete build directory + run: rm -rf build + + - name: Configure (leak checks) + run: mkdir build && cd build && cmake ../tests/leaks + + - name: Build and run (leak checks) + working-directory: build + run: make + + - name: Delete build directory + run: rm -rf build + + - name: Configure (source coverage) + run: mkdir build && cd build && cmake ../tests/coverage + + - name: Build and run (source coverage) + working-directory: build + run: make + + - name: Upload coverage report + uses: actions/upload-artifact@v4 + with: + name: ocre-coverage + include-hidden-files: true + path: build/report + + - name: Delete build directory + run: rm -rf build + + - name: Configure + run: mkdir build && cd build && cmake .. + + - name: Build + working-directory: build + run: make + + - name: Run mini + working-directory: build + run: | + EXPECTED_LOG="powered by Ocre" + echo "Running application..." + stdbuf -oL -eL timeout 20s ./src/samples/mini/posix/ocre_mini | tee linux_run.log + echo "===== Checking for expected log =====" + if grep -q "$EXPECTED_LOG" linux_run.log; then + echo "[OK] Found expected log: $EXPECTED_LOG" + else + echo "[ERROR] Expected log not found: $EXPECTED_LOG" + exit 1 + fi + + - name: Run demo + working-directory: build + run: | + EXPECTED_LOG="Demo completed successfully" + echo "Running application..." + stdbuf -oL -eL timeout 40s ./src/samples/demo/posix/ocre_demo | tee linux_run.log + echo "===== Checking for expected log =====" + if grep -q "$EXPECTED_LOG" linux_run.log; then + echo "[OK] Found expected log: $EXPECTED_LOG" + else + echo "[ERROR] Expected log not found: $EXPECTED_LOG" + exit 1 + fi diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..5cc6eb54 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,65 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +name: Main + +on: + pull_request: + types: + - opened + - synchronize + - reopened + - labeled + push: + branches: + - main + +concurrency: + group: ${{ github.repository }}-${{ github.workflow }}@main-${{ github.ref }} + cancel-in-progress: true + +jobs: + linux-devcontainer: + name: Linux devcontainer + uses: ./.github/workflows/devcontainer-linux.yml + secrets: inherit + permissions: + contents: read + packages: write + + zephyr-devcontainer: + name: Zephyr devcontainer + uses: ./.github/workflows/devcontainer-zephyr.yml + secrets: inherit + permissions: + contents: read + packages: write + + format-checks: + name: Formatting checks + needs: + - linux-devcontainer + uses: ./.github/workflows/formatting-checks.yml + secrets: inherit + with: + devcontainer-tag: ${{ needs.linux-devcontainer.outputs.devcontainer-tag }} + + linux: + name: Linux + needs: + - linux-devcontainer + uses: ./.github/workflows/linux.yml + secrets: inherit + with: + devcontainer-tag: ${{ needs.linux-devcontainer.outputs.devcontainer-tag }} + + zephyr: + name: Zephyr + needs: + - zephyr-devcontainer + uses: ./.github/workflows/zephyr.yml + secrets: inherit + with: + devcontainer-tag: ${{ needs.zephyr-devcontainer.outputs.devcontainer-tag }} diff --git a/.github/workflows/zephyr.yml b/.github/workflows/zephyr.yml new file mode 100644 index 00000000..2de39c37 --- /dev/null +++ b/.github/workflows/zephyr.yml @@ -0,0 +1,90 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +name: Zephyr + +on: + workflow_call: + inputs: + devcontainer-tag: + description: The container tag to be used + default: latest + required: false + type: string + +jobs: + build-zephyr: + name: Zephyr build + runs-on: ["self-hosted", "build-server"] + container: + image: ghcr.io/${{ github.repository }}/devcontainer-zephyr:${{ inputs.devcontainer-tag }} + options: --user 1000:1000 + strategy: + matrix: + board: + - pico_plus2/rp2350b/m33 + - native_sim/native/64 + app: + - mini + - demo + - supervisor + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + path: ocre-runtime + submodules: true + + - name: Clean .west directory + run: rm -rf ../.west + + - name: West init + run: | + . /opt/zephyr-venv/bin/activate + west init -l . + + - name: West update + run: | + . /opt/zephyr-venv/bin/activate + west update + + - name: Clean build directory + run: rm -rf build + + - name: West build + run: | + . /opt/zephyr-venv/bin/activate + west build -p always -b ${{ matrix.board }} src/samples/${{ matrix.app }}/zephyr + + - name: Set sane board name + run: | + BOARD=${{ matrix.board }} + echo "BOARD_NAME=$(printf "%s\n" "$BOARD" | tr / _)" >> $GITHUB_ENV + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: ocre-zephyr-${{ env.BOARD_NAME }}-${{ matrix.app }} + include-hidden-files: true + path: | + build/zephyr/zephyr.elf + build/zephyr/zephyr.exe + build/zephyr/zephyr.bin + build/zephyr/zephyr.dts + build/zephyr/.config + + - name: Test native_sim mini + if: startsWith(matrix.board, 'native_sim/') && matrix.app == 'mini' + run: | + EXPECTED_LOG="powered by Ocre" + echo "Running application..." + stdbuf -oL -eL timeout 20s ./build/zephyr/zephyr.exe | tee zephyr_run.log + echo "===== Checking for expected log =====" + if grep -q "$EXPECTED_LOG" zephyr_run.log; then + echo "[OK] Found expected log: $EXPECTED_LOG" + else + echo "[ERROR] Expected log not found: $EXPECTED_LOG" + exit 1 + fi diff --git a/.gitignore b/.gitignore index f1a742d7..d2232379 100644 --- a/.gitignore +++ b/.gitignore @@ -32,15 +32,9 @@ dist/ target/ dist/ -# JetBrains IDE -.idea/ - # Unit test reports TEST*.xml -# Generated by MacOS -.DS_Store - # Generated by Windows Thumbs.db @@ -57,7 +51,18 @@ Thumbs.db *.mov *.wmv -# VS Code IDE -.vscode/ # Generated by VS settings.json + +src/ocre/commit_id.h + +**/.DS_Store + +# hidden files +.* +!.git +!.gitignore +!/.gitmodules +!/.github +!.devcontainer +!.clang-format diff --git a/.gitmodules b/.gitmodules index 845161b9..6ac55962 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,9 @@ path = wasm-micro-runtime url = https://github.com/project-ocre/wasm-micro-runtime.git branch = staging +[submodule "ocre-sdk"] + path = ocre-sdk + url = https://github.com/project-ocre/ocre-sdk.git +[submodule "tests/Unity"] + path = tests/Unity + url = https://github.com/ThrowTheSwitch/Unity.git diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 540ecc9f..00000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Cortex Debug - ST-LINK", - "request": "launch", - "cwd": "${workspaceFolder}", - "executable": "${workspaceFolder}/zephyr/build/zephyr/zephyr.elf", - "runToEntryPoint": "main", - "showDevDebugOutput": "none", - "type": "cortex-debug", - "servertype": "stlink", - "stm32cubeprogrammer": "", - "interface": "swd", - "device": "STM32U5xx", - "v1": false - } - ] -} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ce15320..a8fc71a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,82 +1,37 @@ -cmake_minimum_required(VERSION 3.20.0) +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 -# Allow user to set CMAKE_SYSTEM_NAME via -DCMAKE_SYSTEM_NAME=Zephyr or Linux -if(NOT DEFINED TARGET_PLATFORM_NAME) - set(TARGET_PLATFORM_NAME "Zephyr") -endif() +cmake_minimum_required(VERSION 3.20.0) -if(TARGET_PLATFORM_NAME STREQUAL "Linux") - project(ocre LANGUAGES C ASM) -elseif(TARGET_PLATFORM_NAME STREQUAL "Zephyr") - find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) - project(ocre VERSION ${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}.${APP_PATCHLEVEL}.${APP_VERSION_TWEAK} LANGUAGES C ASM) -else() - message(FATAL_ERROR "Unsupported TARGET_PLATFORM_NAME: ${TARGET_PLATFORM_NAME}") -endif() +project(ocre LANGUAGES C) -# ----------- COMMON SECTION ----------- set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + set(CMAKE_C_STANDARD 99) -set(CMAKE_CXX_STANDARD 17) -# Version and build info (common) -execute_process( - COMMAND git describe --long --dirty --always - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - OUTPUT_VARIABLE RAW_GIT_VERSION - OUTPUT_STRIP_TRAILING_WHITESPACE -) -string(REPLACE "-dirty" "+" BUILD_INFO ${RAW_GIT_VERSION}) -string(TIMESTAMP BUILD_DATE "%d-%m-%Y, %H:%M" UTC ) -cmake_host_system_information(RESULT BUILD_MACHINE QUERY HOSTNAME) -string(PREPEND BUILD_INFO dev-) -string(REPLACE "-" ";" GIT_INFO_LIST ${RAW_GIT_VERSION}) -list(GET GIT_INFO_LIST 0 VERSION_NUMBER) +set(WAMR_BUILD_PLATFORM linux) -# Message generation (common) -set(OCRE_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}) -set(MSG_INPUT_FILES ${OCRE_ROOT_DIR}/src/ocre/components/container_supervisor/component_supervisor.yaml ) -set(MSG_GENERATED_FILE ${CMAKE_CURRENT_LIST_DIR}/src/messaging/messages.g) +include (src/samples/demo/demo_containers.cmake) -# Zephyr toolchain usually provides PYTHON_EXECUTABLE -if(NOT DEFINED PYTHON_EXECUTABLE) - find_package(Python3 QUIET REQUIRED Interpreter Development) - if(NOT Python3_FOUND) - message(FATAL_ERROR "Python3 interpreter not found. Please install Python3 or set PYTHON_EXECUTABLE to the path of your Python interpreter.") - endif() - set(PYTHON_EXECUTABLE ${Python3_EXECUTABLE}) -endif() +# core +add_subdirectory(src/ocre) +add_subdirectory(src/runtime) -add_custom_command( - OUTPUT ${MSG_GENERATED_FILE} - COMMAND ${PYTHON_EXECUTABLE} ${OCRE_ROOT_DIR}/tools/automsg ${MSG_INPUT_FILES} ${MSG_GENERATED_FILE} - DEPENDS ${MSG_INPUT_FILES} - COMMENT "Running Python script to generate ${MSG_GENERATED_FILE}" -) -add_custom_target(generate_messages DEPENDS ${MSG_GENERATED_FILE}) +# runtime +include (cmake/wamr.cmake) +add_subdirectory(src/runtime/wamr-wasip1) -if(NOT "${OCRE_INPUT_FILE}" STREQUAL "") - message("Using input file: ${OCRE_INPUT_FILE}") - - # Extract filename without extension for use in software - get_filename_component(OCRE_INPUT_FILE_NAME ${OCRE_INPUT_FILE} NAME_WE) - message("Input file name (without extension): ${OCRE_INPUT_FILE_NAME}") - add_definitions(-DOCRE_INPUT_FILE_NAME="${OCRE_INPUT_FILE_NAME}") - - add_custom_command( - OUTPUT ${CMAKE_CURRENT_LIST_DIR}/src/ocre/ocre_input_file.g - COMMAND xxd -n wasm_binary -i ${OCRE_INPUT_FILE} | sed 's/^unsigned/static const unsigned/' > ${OCRE_ROOT_DIR}/src/ocre/ocre_input_file.g - DEPENDS ${OCRE_INPUT_FILE} - COMMENT "Generating C header from ${OCRE_INPUT_FILE}" - ) +# platform +add_subdirectory(src/platform/posix) - add_definitions(-DHAS_GENERATED_INPUT) +# platform +add_subdirectory(src/shell) - add_custom_target(generate_ocre_file DEPENDS ${CMAKE_CURRENT_LIST_DIR}/src/ocre/ocre_input_file.g) -endif() +# samples +add_subdirectory(src/samples/mini/posix) +add_subdirectory(src/samples/demo/posix) -if(TARGET_PLATFORM_NAME STREQUAL "Linux") - include(${CMAKE_CURRENT_LIST_DIR}/src/shared/platform/posix/ocre_internal.cmake) -elseif(TARGET_PLATFORM_NAME STREQUAL "Zephyr") - include(${CMAKE_CURRENT_LIST_DIR}/src/shared/platform/zephyr/ocre_internal.cmake) -endif() +# this sample is useful for static code analysis on POSIX +add_subdirectory(src/samples/static_checks/posix) diff --git a/Kconfig b/Kconfig deleted file mode 100644 index b6c78320..00000000 --- a/Kconfig +++ /dev/null @@ -1,276 +0,0 @@ -menu "Project Ocre options" - -source "Kconfig.zephyr" - -config OCRE - bool "Project Ocre" - default y - help - Enable the Project Ocre runtime. - - select PRINTK - select EVENTS - select EVENTFD - select FLASH - select FLASH_MAP - select FLASH_PAGE_LAYOUT - select FILE_SYSTEM - select FILE_SYSTEM_LITTLEFS - select REBOOT - - select SETTINGS - select SETTINGS_RUNTIME - - select NETWORKING - select NET_SOCKETS - select NET_CONNECTION_MANAGER - select NET_UDP - - select SMF - select SMF_ANCESTOR_SUPPORT - select SMF_INITIAL_TRANSITION - - # Enable the WASM Micro Runtime - select WAMR - -config OCRE_WAMR_HEAP_BUFFER_SIZE - int "WAMR heap buffer size in bytes" - default 32768 - help - A static memory allocation for WAMR to use as a heap. - -config OCRE_STORAGE_HEAP_BUFFER_SIZE - int "Storage heap buffer size in bytes" - default 500000 - depends on MEMC - help - A static memory allocation for container storage to use as a heap. - -config OCRE_CONTAINER_DEFAULT_HEAP_SIZE - int "Default value for the container heap size" - default 4096 - help - The default value used for a container's heap size. - -config OCRE_CONTAINER_DEFAULT_STACK_SIZE - int "Default value for the container stack size" - default 2048 - help - The default value used for a container's stack size. - -config OCRE_LOG_DEBUG - bool "Debug logging" - default n - help - Enable Ocre debug logging - -config OCRE_LOG_ERR - bool "Error logging" - default n - help - Enable Ocre debug logging - -config OCRE_LOG_WARN - bool "Warn logging" - default n - help - Enable Ocre debug logging - -config OCRE_LOG_INF - bool "Info logging" - default n - help - Enable Ocre debug logging - -if OCRE - -module = OCRE -module-str = OCRE -source "subsys/logging/Kconfig.template.log_config" - -endif - -config OCRE_SENSORS - bool "Enable OCRE Sensors support" - default n - depends on SENSOR - help - Enable support for OCRE sensors - -config RNG_SENSOR - bool "RNG Sensor" - default n - depends on OCRE_SENSORS - help - Enable support for the custom RNG sensor. - -config OCRE_WAMR_HEAP_BUFFER_SIZE - int "WAMR heap buffer size in bytes" - default 32768 - help - A static memory allocation for WAMR to use as a heap. - -config OCRE_CONTAINER_DEFAULT_HEAP_SIZE - int "Default value for the container heap size" - default 4096 - help - The default value used for a container's heap size. - -config OCRE_CONTAINER_DEFAULT_STACK_SIZE - int "Default value for the container stack size" - default 2048 - help - The default value used for a container's stack size. - -config MAX_CONTAINERS - int "Maximum concurrent containers" - default 10 - help - The default value for maximum number of container's. - -config MAX_TIMERS - int "Maximum number of timers" - default 5 - help - Defines the maximum number of timers available in the system. - -config MAX_SENSORS - int "Maximum number of sensors" - default 10 - help - Defines the maximum number of sensors that can be handled. - -config MAX_CHANNELS_PER_SENSOR - int "Maximum number of channels per sensor" - default 5 - help - Defines the maximum number of channels that each sensor can have. - -config OCRE_MEMORY_CHECK_ENABLED - bool "Enable memory availability checking for containers" - default y - help - Enable runtime memory checks before creating containers - - -config OCRE_GPIO - bool "OCRE GPIO Driver" - default y - help - Enable the OCRE GPIO driver that provides a portable API layer - for GPIO operations across different hardware platforms. - - -config OCRE_TIMER - bool "OCRE Timer Driver" - default y - help - Enable the OCRE Timer driver that provides a portable API layer - for Timer operations across different hardware platforms. - -config OCRE_GPIO_MAX_PINS - int "Maximum number of GPIO pins" - default 32 - help - Maximum number of GPIO pins that can be managed by the OCRE GPIO driver. - -config OCRE_GPIO_MAX_PORTS - int "Maximum number of GPIO ports" - default 4 - help - Maximum number of GPIO port devices that can be used by the OCRE GPIO driver. - -config OCRE_GPIO_PINS_PER_PORT - int "Number of pins per GPIO port" - default 32 - help - Number of pins available on each GPIO port. This is used to map the - logical pin numbers to physical port and pin numbers. - -config OCRE_CONTAINER_MESSAGING - bool "Enable OCRE Container Messaging support" - default n - help - Enable support for OCRE Container Messaging - -config MESSAGING_MAX_SUBSCRIPTIONS - int "Number of maximum subscriptions for Container Messaging" - default 10 - depends on OCRE_CONTAINER_MESSAGING - help - Number of maximum subscriptions for Container Messaging - -config OCRE_CONTAINER_FILESYSTEM - bool "Enable OCRE Container File System support" - default n - help - Enable support for containers to access the flash filesystem - -config OCRE_SHELL - bool "Enable OCRE Shell" - default y - help - Enable the OCRE Shell for dynamic configuration management. - -config IMU_SENSOR - bool "IMU Sensor" - default n - depends on OCRE_SENSORS - help - Enable support for the custom IMU sensor. - -config OCRE_NETWORKING - bool "Enable container networking support" - default n - help - Enable networking support for containers. - -config OCRE_SHARED_HEAP - bool "Enable container shared heap support" - default n - help - Enable shared heap support for containers. - -config OCRE_SHARED_HEAP_BUF_SIZE - int "Shared heap buffer size in bytes" - default 65536 - depends on OCRE_SHARED_HEAP - help - Size of the pre-allocated buffer for the shared heap. - This memory is shared between WebAssembly modules. - -choice OCRE_SHARED_HEAP_MODE - prompt "Shared heap mode" - depends on OCRE_SHARED_HEAP - default OCRE_SHARED_HEAP_BUF_VIRTUAL - help - Select the shared heap memory mode: - - Physical: Map physical hardware registers (e.g., GPIO) to WASM address space - - Virtual: Allocate shared heap from regular RAM - -config OCRE_SHARED_HEAP_BUF_PHYSICAL - bool "Physical (hardware register mapping)" - help - Enable physical memory mapping for hardware access. - Maps physical hardware registers (like GPIO at 0x42020000) to WASM address space. - Use this when containers need direct access to hardware peripherals. - -config OCRE_SHARED_HEAP_BUF_VIRTUAL - bool "Virtual (RAM allocation)" - help - Enable virtual shared heap allocated from regular RAM. - Use this for normal inter-module communication without - direct hardware access. - -endchoice - -config OCRE_SHARED_HEAP_BUF_ADDRESS - hex "Shared heap buffer address" - default 0x00 - depends on OCRE_SHARED_HEAP - help - Shared heap buffer address. - - For physical mode: Physical address of hardware registers - - For virtual mode: Leave as 0x00 to auto-allocate from RAM - -endmenu diff --git a/README.md b/README.md index 5c256690..084a01eb 100644 --- a/README.md +++ b/README.md @@ -20,194 +20,227 @@ Our mission is to make it as easy to develop and securely deploy apps for the bi Ocre supports a range of features depending on the platform. The table below summarizes the current support: -| Feature | Zephyr (native_sim, b_u585i_iot02a) | Linux | -|------------------------|:-----------------------------------:|:-------------:| -| Ocre Runtime | ✅ | ✅ | -| Container Messaging | ✅ | ✅ | -| GPIO | ✅ | ❌ | -| Timers | ✅ | ✅ | -| Sensors | ✅ | ❌ | -| Networking | ✅ | ✅ | -| Filesystem | ✅ | ✅ | -| Interactive Shell | ✅ | ❌ | +| Feature | Zephyr (native_sim, b_u585i_iot02a) | Linux | +| ------------------- | :---------------------------------: | :---: | +| Ocre Runtime | ✅ | ✅ | +| Container Messaging | ✅ | ✅ | +| GPIO | ✅ | ❌ | +| Timers | ✅ | ✅ | +| Sensors | ✅ | ❌ | +| Networking | ✅ | ✅ | +| Filesystem | ✅ | ✅ | +| Interactive Shell | ✅ | ❌ | - **Zephyr**: Full feature set, including hardware integration and shell. - **Linux**: Core runtime and multiple I/O features; hardware and shell features are not available. --- -## Getting Started +# Getting Started -This guide will help you build and run Ocre on both Zephyr (simulated and hardware) and Linux. +## Zephyr -### Prerequisites +### Devcontainer -- **Zephyr SDK** (for Zephyr targets) -- **Python 3** (for build tooling) -- **CMake** and **make** (for Linux builds) -- **west** (Zephyr meta-tool) -- **git** +We provide an optional devcontainer for building Zephyr with Ocre. However, we suggest Zephyr development to be done in the host outside docker, because it will be easier to flash and debug the board, the devcontainer is provided as a quick and handy way of reproducing the environment we have in the CI. ---- +### Zephyr Integration -### Setup +Ocre is a collection of a External Zephyr module, a few sample applications and a an optional slim West project for building for a small set of platforms. -#### Zephyr +More boards are supported in Ocre with a full West Project, however, our optional West project can be used to get started fast and in our CI. -Follow the [Zephyr Getting Started Guide](https://docs.zephyrproject.org/3.7.0/develop/getting_started/index.html) for your OS, including: -- [Install dependencies](https://docs.zephyrproject.org/3.7.0/develop/getting_started/index.html#install-dependencies) -- [Install the Zephyr SDK](https://docs.zephyrproject.org/3.7.0/develop/getting_started/index.html#install-the-zephyr-sdk) +### Quick Zephyr build with our West project -#### Python Virtual Environment (Recommended) +This will very quickly build the images for one of the officially supported boards. Open a terminal in Ubuntu 22.04 or newer and run: + +Install Ubuntu dependencies: ```sh -mkdir runtime && cd runtime -python3 -m venv .venv -source .venv/bin/activate +sudo apt update +sudo apt upgrade +sudo apt install --no-install-recommends git cmake ninja-build gperf \ + ccache dfu-util device-tree-compiler wget python3-dev python3-venv python3-tk \ + xz-utils file make gcc gcc-multilib g++-multilib libsdl2-dev libmagic1 curl ``` -You may need to install `python3-venv` on your system. - -#### Install West -Install the [west](https://docs.zephyrproject.org/latest/develop/west/index.html) CLI tool, which is needed to build, run and manage Zephyr applications. +Install [WASI-SDK](https://github.com/WebAssembly/wasi-sdk/releases): ```sh -pip install west +mkdir /opt/wasi-sdk && \ + curl -sSL https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-29/wasi-sdk-29.0-$(uname -m | sed s/aarch64/arm64/)-linux.tar.gz | \ + sudo tar zxvf - --strip-components=1 -C /opt/wasi-sdk ``` -#### Initialize and Update Zephyr Workspace +Create Zephyr project directory, venv and install python dependencies: ```sh -west init -m git@github.com:project-ocre/ocre-runtime.git -west update +mkdir ~/zephyrproject +python3 -m venv ~/zephyrproject/.venv +source ~/zephyrproject/.venv/bin/activate +pip install littlefs-python ``` -#### Install Zephyr Python Requirements +Zephyr workspace initialization: ```sh -pip install -r zephyr/scripts/requirements.txt +cd ~/zephyrproject +west init -m https://github.com/ocre-project/ocre.git +west update +west zephyr-export ``` ---- +Install SDK and Python packages: -#### Linux +```sh +west packages pip --install +west sdk install -t \ + x86_64-zephyr-elf \ + aarch64-zephyr-elf \ + arm-zephyr-eabi +``` -#### Clone the Repository +Build the sample application: ```sh -git clone git@github.com:project-ocre/ocre-runtime.git -cd ocre-runtime +west build -p always -b b_u585i_iot02a ocre-runtime/src/samples/demo/zephyr ``` -#### Initialize submodules +Connect the board and flash the application: + ```sh -git submodule update --init --recursive +west flash ``` -## Building and Running +Alternatively, you can try the `mini` or `supervisor` samples. -### Using the `build.sh` Script (Recommended) +### Full Zephyr build from scratch -Ocre provides a convenient `build.sh` script to simplify building and running for both Zephyr and Linux targets. +These instructions work natively for Linux, MacOS and WSL2. If this is used in docker, it might require some special configuration for being able to flash or debug the board. -#### Usage +1. Follow the instructions on the [Zephyr Getting Started Guide](https://docs.zephyrproject.org/latest/develop/getting_started/index.html) for your OS, until you can build a sample application from Zephyr. -```sh -./build.sh -t [-r] [-f [file2 ...]] [-b] [-h] -``` +2. Install `littlefs-python`: -- `-t `: **Required**. `z` for Zephyr, `l` for Linux. -- `-r`: Run after build (optional). -- `-f `: Specify one or more input files (optional). -- `-b `: (Zephyr only) Select a zephyr board instead of the default `native_sim`: - - `uw` -> b_u585i_iot02a + W5500 - - `ue` -> b_u585i_iot02a + ENC28J60 - - `your_option` -> any Zephyr-supported board -- `-h`: Show help. + ```sh + pip install littlefs-python + ``` -#### Examples +3. Install [WASI-SDK](https://github.com/WebAssembly/wasi-sdk/releases) -**Build and run for Zephyr native_sim:** -```sh -./build.sh -t z -r -``` +WASI-SDK is expected to be installed in `/opt/wasi-sdk`. -**Build for Zephyr b_u585i_iot02a board:** -```sh -./build.sh -t z -b -``` +Check-out the ocre-runtime repository alongside the zephyr repositories or anywhere and just build one of our sample applications like: -**Build and run for Linux with a WASM file:** ```sh -./build.sh -t l -r -f your_file.wasm +west build -p always -b ocre-runtime/src/samples//zephyr ``` ---- - -### Manual Build Steps +See below for the available applications and officially supported boards. -#### Zephyr (native_sim) +You can also change the Kconfig options for Zephyr and ocre with: ```sh -west build -b native_sim ./application -d build -- -DMODULE_EXT_ROOT=`pwd`/application -./build/zephyr/zephyr.exe +west build -t menuconfig ``` -#### Zephyr (b_u585i_iot02a) +Followed by: ```sh -west build -b b_u585i_iot02a ./application -d build -- -DMODULE_EXT_ROOT=`pwd`/application -# Flash to board (requires hardware) +west build west flash ``` -#### Linux x86_64 +### Quick Zephyr build with devcontainer + +This will very quickly build the images for one of the officially supported boards. The devcontainer already includes the Zephyr SDK, WASI-SDK the Python venv and all the necessary tools. + +Build and open the 'zephyr' devcontainer of this repository. + +Open a terminal in the devcontainer and run: ```sh -mkdir -p build -cd build -cmake .. -DTARGET_PLATFORM_NAME=Linux -make -./app your_file.wasm +west init . +west update +west build -p always -b ocre-runtime/src/samples//zephyr ``` ---- +The build artifacts are in the `build/zephyr` directory. You might need to set-up some docker mounts and permissions for flashing the board. + +See below for the available applications and officially supported boards. + +### Official boards and sample applications + +The officially supported boards are listed below: + +| Board | Name | +| ------------------------ | ----------------------------------------------------------------------------------------------- | +| `native_sim` | 32-bit native simulator | +| `native_sim/native/64` | 64-bit native simulator | +| `pico_plus2/rp2350b/m33` | Pimoroni Pico Plus 2 | +| `b_u585i_iot02a` | [B-U585I-IOT02A](https://docs.zephyrproject.org/latest/boards/st/b_u585i_iot02a/doc/index.html) | + +Other boards might work. Check the [Zephyr documentation](https://docs.zephyrproject.org/latest/boards/index.html) for a list of supported boards in Zephyr. + +The officially supported sample applications are listed below: + +| Sample | Description | +| ------------ | ---------------------------------------------------------- | +| `mini` | A simple "Hello World" application using minimal resources | +| `demo` | A more featured sample application | +| `supervisor` | Interactive shell control | + +The intended usage of the samples are: + +- Use the `mini` sample to see how Ocre can work on your board. +- Use the `demo` sample to test some more Ocre features. +- Use the `supervisor` sample to have full interactive control from the shell command line. + +## Linux -## Additional Notes +### Devcontainer -- The `build.sh` script will automatically detect the target and handle build/run steps, including passing input files and selecting the correct Zephyr board. -- For Zephyr, only the first file specified with `-f` is passed as the input file (see script for details). -- For Linux, you can pass multiple WASM files as arguments to the built application. -- The script checks build success and only runs the application if the build completes successfully. -- The `mini-samples` directory contains a "Hello World" container, which is hardcoded as a C array. When Ocre is run without input file arguments, it executes this sample container by default. If an input file is provided, Zephyr will convert that file into a C array at build time and run it as a container. On Linux, this conversion does not occur — instead, Ocre simply opens and reads the provided file(s) directly from the filesystem. +We also provide a devcontainer for Linux. This is the recommended way to get started with Ocre on Linux. However these instructions will work for native Linux builds. -## Sample Output +### Prerequisites + +- Ubuntu 22.04 or later +- CMake > 3.20 +- git > 2.28 +- A working C compiler (gcc or clang) +- [WASI-SDK](https://github.com/WebAssembly/wasi-sdk/releases) installed under `/opt/wasi-sdk` + +Other Linux distributions might work, but are not actively tested. + +You can get this easily on a new Ubuntu by running: -#### Zephyr (b_u585i_iot02a) ```sh -./build.sh -t z -r -b ue +sudo apt update +sudo apt install -y cmake git build-essential ``` -##### Output +Or you can use the devcontainer, which already has everything preinstalled. + +Clone the repository and the submodules: + ```sh -Target is: Zephyr -Building for b_u585i_iot02a with ENC28J60 support -Linking C executable zephyr/zephyr.elf -Ocre runtime started -Hello World from Ocre! +git clone https://github.com/project-ocre/ocre-runtime.git +cd ocre-runtime +git submodule update --init --recursive ``` -#### Linux +Run the CMake build, like: + ```sh -./build.sh -t l -r +mkdir build && cd build +cmake .. +make ``` -##### Output +The relevant binaries are stored in `build/src/samples/mini/posix` and `build/src/samples/demo/posix`. You can run them directly: + ```sh -Target is: Linux -[100%] Built target app -Ocre runtime started -Hello World from Ocre! +./build/src/samples/mini/posix/mini ``` ## License @@ -217,8 +250,9 @@ Distributed under the Apache License 2.0. See [LICENSE](https://github.com/proje --- ## More Info -* **[Website](https://lfedge.org/projects/ocre/)**: For a high-level overview of the Ocre project, and its goals, visit our website. -* **[Docs](https://docs.project-ocre.org/)**: For more detailed information about the Ocre runtime, visit Ocre docs. -* **[Wiki](https://lf-edge.atlassian.net/wiki/spaces/OCRE/overview?homepageId=14909442)**: For a full project overview, FAQs, project roadmap, and governance information, visit the Ocre Wiki. -* **[Slack](https://lfedge.slack.com/archives/C07F190CC3X)**: If you need support or simply want to discuss all things Ocre, head on over to our Slack channel (`#ocre`) on LFEdge's Slack org. -* **[Mailing list](https://lists.lfedge.org/g/Ocre-TSC)**: Join the Ocre Technical Steering Committee (TSC) mailing list to stay up to date with important project announcements, discussions, and meetings. + +- **[Website](https://lfedge.org/projects/ocre/)**: For a high-level overview of the Ocre project, and its goals, visit our website. +- **[Docs](https://docs.project-ocre.org/)**: For more detailed information about the Ocre runtime, visit Ocre docs. +- **[Wiki](https://lf-edge.atlassian.net/wiki/spaces/OCRE/overview?homepageId=14909442)**: For a full project overview, FAQs, project roadmap, and governance information, visit the Ocre Wiki. +- **[Slack](https://lfedge.slack.com/archives/C07F190CC3X)**: If you need support or simply want to discuss all things Ocre, head on over to our Slack channel (`#ocre`) on LFEdge's Slack org. +- **[Mailing list](https://lists.lfedge.org/g/Ocre-TSC)**: Join the Ocre Technical Steering Committee (TSC) mailing list to stay up to date with important project announcements, discussions, and meetings. diff --git a/VERSION b/VERSION deleted file mode 100644 index d9b72bcb..00000000 --- a/VERSION +++ /dev/null @@ -1,5 +0,0 @@ -VERSION_MAJOR = 0 -VERSION_MINOR = 99 -PATCHLEVEL = 0 -VERSION_TWEAK = 0 -EXTRAVERSION = dev \ No newline at end of file diff --git a/boards/b_u585i_iot02a.conf b/boards/b_u585i_iot02a.conf deleted file mode 100644 index c273b586..00000000 --- a/boards/b_u585i_iot02a.conf +++ /dev/null @@ -1,60 +0,0 @@ -# Memory configuration and sizes -CONFIG_ARM_MPU=n -CONFIG_FPU=y -CONFIG_MAIN_STACK_SIZE=8192 -CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=-1 - -# PSRAM -CONFIG_MEMC=y - -# Container defaults -CONFIG_MAX_CONTAINERS=5 -CONFIG_OCRE_WAMR_HEAP_BUFFER_SIZE=6388608 -CONFIG_OCRE_STORAGE_HEAP_BUFFER_SIZE=2000000 - -# Bus interfaces -CONFIG_GPIO=y -CONFIG_I2C=y -CONFIG_SPI=y - -# W5500 Ethernet driver -CONFIG_ETH_W5500=y -CONFIG_NET_L2_ETHERNET=y - -# Ocre Sensors support -CONFIG_SENSOR=y -CONFIG_OCRE_SENSORS=y -CONFIG_RNG_SENSOR=y - -CONFIG_SENSOR_SHELL=y -CONFIG_SENSOR_INFO=y - -# Onboard sensors -CONFIG_ISM330DHCX=y -CONFIG_LPS22HH=y -CONFIG_HTS221=y -CONFIG_IIS2MDC=y -CONFIG_VEML7700=y - -# Ocre options -CONFIG_OCRE_CONTAINER_FILESYSTEM=y - -# Ocre GPIO Support -CONFIG_OCRE_GPIO=y -CONFIG_OCRE_GPIO_MAX_PORTS=8 -CONFIG_OCRE_GPIO_PINS_PER_PORT=16 -CONFIG_OCRE_GPIO_MAX_PINS=256 - -CONFIG_CODE_DATA_RELOCATION=n - -# Networking options -CONFIG_NET_TCP=y -CONFIG_NET_MAX_CONN=10 - -# Zephyr Virtual File System (ZVFS) options. Needed for container filesystem and sockets -CONFIG_ZVFS_OPEN_MAX=15 -CONFIG_ZVFS_EVENTFD_MAX=10 -CONFIG_NET_SOCKETS_POLL_MAX=10 - -# Enable container networking support -CONFIG_OCRE_NETWORKING=y diff --git a/boards/enc28j60.conf b/boards/enc28j60.conf deleted file mode 100644 index f6a8f028..00000000 --- a/boards/enc28j60.conf +++ /dev/null @@ -1,3 +0,0 @@ -# ENC28J60 Ethernet driver -CONFIG_ETH_ENC28J60=y -CONFIG_ETH_W5500=n diff --git a/boards/enc28j60.overlay b/boards/enc28j60.overlay deleted file mode 100644 index 93d9d757..00000000 --- a/boards/enc28j60.overlay +++ /dev/null @@ -1,11 +0,0 @@ -&spi1 { - eth0: ethernet@0 { - status = "okay"; - reg = <0>; - compatible = "microchip,enc28j60"; - int-gpios = <&gpioc 1 GPIO_ACTIVE_LOW>; - spi-max-frequency = <10000000>; - local-mac-address = [ 00 80 00 01 02 03 ]; - full-duplex; - }; -}; diff --git a/boards/native_sim.conf b/boards/native_sim.conf deleted file mode 100644 index 32f34508..00000000 --- a/boards/native_sim.conf +++ /dev/null @@ -1,35 +0,0 @@ -CONFIG_UART_NATIVE_PTY_0_ON_STDINOUT=y -CONFIG_UART_NATIVE_PTY=y -CONFIG_FLASH_SIMULATOR=y -CONFIG_ENTROPY_GENERATOR=y -CONFIG_TEST_RANDOM_GENERATOR=y - -CONFIG_MPU_ALLOW_FLASH_WRITE=y - -CONFIG_NATIVE_SIM_SLOWDOWN_TO_REAL_TIME=y - -# Support for NET_OFFLOAD adapter -CONFIG_ETH_NATIVE_POSIX=n -CONFIG_NET_DRIVERS=y -CONFIG_NET_SOCKETS_OFFLOAD=y -CONFIG_NET_NATIVE_OFFLOADED_SOCKETS=y - -# The NET_OFFLOAD adapter requires setting an IP address to satisfy the Zephyr networking stack -# This address is not actually used, but it must be set to a valid value. The underlying host's -# networking address will be used to communicate with the network. -CONFIG_NET_CONFIG_SETTINGS=y -CONFIG_NET_CONFIG_MY_IPV4_ADDR="127.0.0.1" -CONFIG_NET_CONFIG_MY_IPV4_GW="127.0.0.1" -CONFIG_NET_CONFIG_MY_IPV4_NETMASK="255.255.255.0" - -CONFIG_NET_DHCPV4=n - -# Suppress logging from FS and LittleFS -CONFIG_FS_LOG_LEVEL_OFF=y - -# This is required to work around an issue where "-1" causes a crash using the `native_sim` -CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=262144 -CONFIG_GPIO=y - -# Ocre options -CONFIG_OCRE_CONTAINER_FILESYSTEM=y diff --git a/boards/native_sim.overlay b/boards/native_sim.overlay deleted file mode 100644 index 8bf90af4..00000000 --- a/boards/native_sim.overlay +++ /dev/null @@ -1,39 +0,0 @@ -/ { - chosen { - zephyr,console = &uart0; - zephyr,shell-uart = &uart0; - zephyr,uart-mcumgr = &uart0; - }; - - aliases { - rng0 = &rng_device; - }; - - devices{ - rng_device: rng_0 { - compatible = "custom,rng-sensor"; - label = "RNG Sensor"; - status = "okay"; - }; - }; -}; - -/delete-node/ &storage_partition; -/delete-node/ &scratch_partition; - -&flash0 { - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - user_data_partition: partition@de000 { - label = "user-data"; - reg = <0x000de000 0x0001e000>; - }; - storage_partition: partition@100000 { - label = "storage"; - reg = <0x100000 0x00004000>; - }; - }; -}; diff --git a/boards/nrf5340dk_nrf5340_cpuapp.conf b/boards/nrf5340dk_nrf5340_cpuapp.conf deleted file mode 100644 index 501754d7..00000000 --- a/boards/nrf5340dk_nrf5340_cpuapp.conf +++ /dev/null @@ -1,79 +0,0 @@ -CONFIG_ARM_MPU=y -CONFIG_MAIN_STACK_SIZE=4096 -CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 -CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=-1 -CONFIG_HEAP_MEM_POOL_SIZE=32768 - -# Container defaults - Reduced for nRF5340's limited RAM -CONFIG_MAX_CONTAINERS=2 -CONFIG_OCRE_WAMR_HEAP_BUFFER_SIZE=180000 -CONFIG_OCRE_CONTAINER_DEFAULT_HEAP_SIZE=4096 -CONFIG_OCRE_CONTAINER_DEFAULT_STACK_SIZE=4096 - -# DISABLE OCRE SHELL (this is what's causing the shell_fprintf errors!) -CONFIG_OCRE_SHELL=n - -# Disable the main Zephyr shell AND all shell subsystems -CONFIG_NET_SHELL=n -CONFIG_FILE_SYSTEM_SHELL=n -CONFIG_SENSOR_SHELL=n - -# Override shell-related configs from prj.conf since shell is disabled - -# Random number generator (REQUIRED by networking and RNG sensor) -CONFIG_ENTROPY_GENERATOR=y -CONFIG_TEST_RANDOM_GENERATOR=y - -# Reduce networking stack sizes to save RAM -CONFIG_NET_TX_STACK_SIZE=1024 -CONFIG_NET_RX_STACK_SIZE=2048 -CONFIG_NET_BUF_TX_COUNT=8 -CONFIG_NET_BUF_RX_COUNT=16 -CONFIG_NET_MGMT_EVENT_STACK_SIZE=1024 -CONFIG_NET_PKT_RX_COUNT=8 -CONFIG_NET_PKT_TX_COUNT=8 -CONFIG_NET_BUF_DATA_SIZE=128 - -# Bus interfaces -CONFIG_GPIO=y - -# Ocre Sensors support -CONFIG_SENSOR=y -CONFIG_OCRE_SENSORS=y -CONFIG_RNG_SENSOR=y - -# Ocre GPIO Support (minimal) -CONFIG_OCRE_GPIO=y -CONFIG_OCRE_GPIO_MAX_PORTS=4 -CONFIG_OCRE_GPIO_PINS_PER_PORT=8 -CONFIG_OCRE_GPIO_MAX_PINS=32 - -# Disable container messaging to save RAM -CONFIG_OCRE_CONTAINER_MESSAGING=y - -# Enable container filesystem for WASI stdio -CONFIG_OCRE_CONTAINER_FILESYSTEM=y - -# CONFIG_BOOTLOADER_MCUBOOT=n -# # CONFIG_IMG_MANAGER=n -# CONFIG_MCUBOOT_IMG_MANAGER=n -# CONFIG_STREAM_FLASH=n -# CONFIG_IMG_ERASE_PROGRESSIVELY=n - -# Flash settings -CONFIG_FLASH=y -CONFIG_FLASH_MAP=y -CONFIG_FLASH_PAGE_LAYOUT=y - -# Serial/UART -CONFIG_SERIAL=y -CONFIG_UART_CONSOLE=y -CONFIG_CONSOLE=y - -# Reduce other buffers -CONFIG_ZVFS_OPEN_MAX=8 -CONFIG_ZVFS_EVENTFD_MAX=5 - -CONFIG_LOG_TRACE_SHORT_TIMESTAMP=n - -CONFIG_SHELL_PROMPT_UART="" diff --git a/boards/nrf5340dk_nrf5340_cpuapp.overlay b/boards/nrf5340dk_nrf5340_cpuapp.overlay deleted file mode 100644 index 7d625007..00000000 --- a/boards/nrf5340dk_nrf5340_cpuapp.overlay +++ /dev/null @@ -1,34 +0,0 @@ -/ { - aliases { - rng0 = &rng_device; - led0 = &led0; - }; - - rng_device: rng_0 { - compatible = "custom,rng-sensor"; - label = "RNG Sensor"; - status = "okay"; - }; - - devices { - compatible = "custom,devices"; - status = "okay"; - device_list = <&rng_device>; - }; -}; - -/* Enable one LED for blinky */ -&led0 { - status = "okay"; -}; - -&flash0 { - partitions { - /delete-node/ partition@f8000; - - user_data_partition: partition@e0000 { - label = "user_data"; - reg = <0x000e0000 DT_SIZE_K(128)>; - }; - }; -}; diff --git a/build.sh b/build.sh deleted file mode 100755 index 8432e0c4..00000000 --- a/build.sh +++ /dev/null @@ -1,148 +0,0 @@ -#!/bin/bash - -# Function to display help -show_help() { - echo "Usage: $0 -t [-r] [-f [file2 ...]]" - echo " -t (Required) Specify the target. z for Zephyr and l for Linux" - echo " -r (Optional) Specify whether run after the build" - echo " -f (Optional) Specify one or more input files" - echo " -b (Optional, Zephyr only) Select board:" - echo " uw -> b_u585i_iot02a + W5500" - echo " ue -> b_u585i_iot02a + ENC28J60" - echo " note: when no board is selected, native_sim is the default target for Zephyr" - echo " -h Display help" - exit 0 -} - -RUN_MODE=false # Default: Run mode disabled -INPUT_FILES=() -BOARD_ARG="native_sim" - -# resolve absolute paths (portable, no readlink -f) -abs_path() { - local p="$1" - if [[ "$p" = /* ]]; then - echo "$p" - else - echo "$(cd "$(dirname "$p")" && pwd)/$(basename "$p")" - fi -} - -# Parse arguments -while [[ $# -gt 0 ]]; do - case "$1" in - -t) - if [[ -z "$2" || "$2" =~ ^- ]]; then - echo "Error: -t requires a value (z or l)" >&2 - exit 1 - fi - TARGET="$2" - shift 2 - ;; - -r) - RUN_MODE=true - shift - ;; - -f) - shift - while [[ $# -gt 0 && ! "$1" =~ ^- ]]; do - INPUT_FILES+=("$1") - shift - done - ;; - -b) - if [[ -z "$2" || "$2" =~ ^- ]]; then - echo "Error: -b requires a board argument" >&2 - exit 1 - fi - BOARD_ARG="$2" - shift 2 - ;; - -h) - show_help - ;; - *) - echo "Invalid option: $1" >&2 - show_help - ;; - esac -done - -# Normalize input files to absolute paths BEFORE any 'cd' -if [[ ${#INPUT_FILES[@]} -gt 0 ]]; then - for i in "${!INPUT_FILES[@]}"; do - INPUT_FILES[$i]="$(abs_path "${INPUT_FILES[$i]}")" - done -fi - -# Check if required argument is provided -if [[ "$TARGET" == "z" ]]; then - echo "Target is: Zephyr" - cd .. - case "$BOARD_ARG" in - uw) - ZEPHYR_BOARD="b_u585i_iot02a" - CONF_EXTRA="" - echo "Building for b_u585i_iot02a with W5500 support" - ;; - ue) - ZEPHYR_BOARD="b_u585i_iot02a" - CONF_EXTRA="-DCONF_FILE=prj.conf\;boards/${ZEPHYR_BOARD}.conf\;boards/enc28j60.conf \ - -DDTC_OVERLAY_FILE=boards/${ZEPHYR_BOARD}.overlay\;boards/enc28j60.overlay" - echo "Building for b_u585i_iot02a with ENC28J60 support" - ;; - nrf5340) - ZEPHYR_BOARD="nrf5340dk/nrf5340/cpuapp" - CONF_EXTRA="" - echo "Building for nrf5340dk board: App CPU" - ;; - *) - ZEPHYR_BOARD="$BOARD_ARG" - CONF_EXTRA="" - echo "Building for board: $ZEPHYR_BOARD" - ;; - esac - - if [[ ${#INPUT_FILES[@]} -gt 0 ]]; then - echo "Input files provided: ${INPUT_FILES[*]}" - rm flash.bin - west build -p -b $ZEPHYR_BOARD ./application -d build -- \ - -DMODULE_EXT_ROOT=`pwd`/application -DOCRE_INPUT_FILE="${INPUT_FILES[0]}" -DTARGET_PLATFORM_NAME=Zephyr $CONF_EXTRA || exit 1 - else - rm flash.bin - west build -p -b $ZEPHYR_BOARD ./application -d build -- \ - -DMODULE_EXT_ROOT=`pwd`/application -DTARGET_PLATFORM_NAME=Zephyr $CONF_EXTRA || exit 1 - fi -elif [[ "$TARGET" == "l" ]]; then - echo "Target is: Linux" - if [[ ! -d "build" ]]; then - echo "build folder does not exist. Creating: build" - mkdir -p "build" - fi - cd build - cmake .. -DTARGET_PLATFORM_NAME=Linux -DWAMR_DISABLE_STACK_HW_BOUND_CHECK=1 - # Capture make output to a file - make | tee build.log - BUILD_SUCCESS=false - if [[ $(tail -n 1 build.log) == "[100%] Built target app" ]]; then - BUILD_SUCCESS=true - fi - if [ "$BUILD_SUCCESS" == false ]; then - exit 1 - fi -else - echo "Target does not contain 'z' or 'l': exit" - exit 1 -fi - -# Execute run mode if -r flag is set and build was successful -if [[ "$TARGET" == "z" && "$RUN_MODE" = true ]]; then - west flash -elif [[ "$TARGET" == "l" && "$RUN_MODE" = true && "$BUILD_SUCCESS" = true ]]; then - if [[ ${#INPUT_FILES[@]} -gt 0 ]]; then - echo "Input files provided: ${INPUT_FILES[*]}" - ./app "${INPUT_FILES[@]}" - else - ./app - fi -fi diff --git a/cmake/state_information.cmake b/cmake/state_information.cmake new file mode 100644 index 00000000..8f5a3574 --- /dev/null +++ b/cmake/state_information.cmake @@ -0,0 +1,51 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/var/lib/ocre/images) +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/var/lib/ocre/containers) + +if(OCRE_SDK_PRELOADED_IMAGES) + include(ExternalProject) + ExternalProject_Add( + OcreSampleContainers + PREFIX "${CMAKE_CURRENT_BINARY_DIR}/OcreSampleContainers" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/OcreSampleContainers/build" + BUILD_ALWAYS TRUE + INSTALL_COMMAND "" + SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../ocre-sdk" + CMAKE_ARGS "-DWAMR_ROOT_DIR=${CMAKE_CURRENT_LIST_DIR}/../wasm-micro-runtime" "-DCMAKE_VERBOSE_MAKEFILE=ON" + ) +endif() + +if (OCRE_INPUT_FILE_NAME) + message(STATUS "Adding user provided '${OCRE_INPUT_FILE_NAME}' to preloaded images") + list(APPEND OCRE_PRELOADED_IMAGES ${OCRE_INPUT_FILE_NAME}) +endif() + +# populate SDK provided sample images +foreach(image IN ITEMS ${OCRE_SDK_PRELOADED_IMAGES}) + message(STATUS "Adding sdk sample '${image}' to preloaded images") + add_custom_target(${image} + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_BINARY_DIR}/OcreSampleContainers/build/${image} + ${CMAKE_CURRENT_BINARY_DIR}/var/lib/ocre/images/${image} + DEPENDS OcreSampleContainers + ) + list(APPEND OCRE_IMAGES ${image}) +endforeach() + +# populate user provided sample files +foreach(file IN ITEMS ${OCRE_PRELOADED_IMAGES}) + message(STATUS "Adding user provided '${file}' to preloaded images") + cmake_path(GET file FILENAME image) + add_custom_target(${image} + COMMAND ${CMAKE_COMMAND} -E copy + ${file} + ${CMAKE_CURRENT_BINARY_DIR}/var/lib/ocre/images/${image} + ) + list(APPEND OCRE_IMAGES ${image}) +endforeach() diff --git a/cmake/wamr.cmake b/cmake/wamr.cmake new file mode 100644 index 00000000..33cea916 --- /dev/null +++ b/cmake/wamr.cmake @@ -0,0 +1,45 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +set (WAMR_BUILD_INTERP 1) +set (WAMR_BUILD_FAST_INTERP 0) +set (WAMR_BUILD_AOT 1) +set (WAMR_BUILD_JIT 0) +set (WAMR_BUILD_LIBC_BUILTIN 0) +set (WAMR_BUILD_LIBC_WASI 1) +set (WAMR_BUILD_LIB_PTHREAD 0) +set (WAMR_BUILD_LIB_WASI_THREADS 1) +set (WAMR_BUILD_REF_TYPES 1) +set (WASM_ENABLE_LOG 1) +set (WAMR_BUILD_SHARED_HEAP 1) + +if (NOT DEFINED WAMR_BUILD_PLATFORM) + set (WAMR_BUILD_PLATFORM "linux") +endif() + +# Set WAMR_BUILD_TARGET, currently values supported: +# "X86_64", "AMD_64", "X86_32", "AARCH64[sub]", "ARM[sub]", "THUMB[sub]", +# "MIPS", "XTENSA", "RISCV64[sub]", "RISCV32[sub]" +if (NOT DEFINED WAMR_BUILD_TARGET) + if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)") + set (WAMR_BUILD_TARGET "AARCH64") + if (NOT DEFINED WAMR_BUILD_SIMD) + set (WAMR_BUILD_SIMD 1) + endif () + elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64") + set (WAMR_BUILD_TARGET "RISCV64") + elseif (CMAKE_SIZEOF_VOID_P EQUAL 8) + set (WAMR_BUILD_TARGET "X86_64") + if (NOT DEFINED WAMR_BUILD_SIMD) + set (WAMR_BUILD_SIMD 1) + endif () + elseif (CMAKE_SIZEOF_VOID_P EQUAL 4) + set (WAMR_BUILD_TARGET "X86_32") + else () + message(SEND_ERROR "Unsupported build target platform!") + endif () +endif () + +add_subdirectory(wasm-micro-runtime) diff --git a/modules/modules.cmake b/modules/modules.cmake deleted file mode 100644 index af2c7429..00000000 --- a/modules/modules.cmake +++ /dev/null @@ -1 +0,0 @@ -# This project uses .gitmodules to manage various modules due to cross-platform solutions diff --git a/ocre-sdk b/ocre-sdk new file mode 160000 index 00000000..2030fa2b --- /dev/null +++ b/ocre-sdk @@ -0,0 +1 @@ +Subproject commit 2030fa2b079f04e596384f24bdc5bc782a7f8b01 diff --git a/ocre.code-workspace b/ocre.code-workspace deleted file mode 100644 index 2a13006e..00000000 --- a/ocre.code-workspace +++ /dev/null @@ -1,164 +0,0 @@ -{ - "folders": [ - { - "path": ".." - } - ], - "settings": { - // Hush CMake - "cmake.configureOnOpen": false, - // CppTools and IntelliSense - "C_Cpp.workspaceParsingPriority": "medium", - "C_Cpp.default.cStandard": "c11", - "C_Cpp.default.browse.path": [ - "${workspaceFolder}/application/src" - ], - "C_Cpp.intelliSenseEngine": "default", - "C_Cpp.default.includePath": [ - "${workspaceFolder}/application/src/ocre", - "${workspaceFolder}/zephyr/include/" - ], - "C_Cpp.default.forcedInclude": [ - "${workspaceFolder}/build/zephyr/include/generated/autoconf.h" - ], - "C_Cpp.default.compileCommands": "${workspaceFolder}/build/compile_commands.json", - // Files - "files.exclude": { - "**/.git": true, - "**/.svn": true, - "**/.hg": true, - "**/CVS": true, - "**/.DS_Store": true, - "**/Thumbs.db": true - }, - // Kconfig - "kconfig.root": "${workspaceFolder}/application/Kconfig", - "files.associations": { - "net_private.h": "c", - "socket.h": "c", - "tls_internal.h": "c", - "sockets_internal.h": "c", - "iwasm_sm.h": "c", - "*.h": "c", - "*.c": "c", - "*.g": "c", - "*.tcc": "c", - "system_error": "c", - "array": "c", - "functional": "c", - "tuple": "c", - "type_traits": "c", - "utility": "c", - "__hash_table": "c", - "__split_buffer": "c", - "bitset": "c", - "deque": "c", - "initializer_list": "c", - "queue": "c", - "stack": "c", - "string": "c", - "string_view": "c", - "unordered_map": "c", - "vector": "c", - "__config": "c", - "__node_handle": "c" - }, - "editor.formatOnType": true, - }, - "tasks": { - "version": "2.0.0", - "tasks": [ - { - "label": "West Build", - "type": "shell", - "group": { - "kind": "build", - "isDefault": true - }, - "command": "west", - "args": [ - "build", - "--pristine", - "-b", - "${input:board}", - "${workspaceFolder}/application", - "-d", - "${workspaceFolder}/build", - "--", - "-DMODULE_EXT_ROOT=\"${workspaceFolder}/application\"", - ], - "problemMatcher": [ - "$gcc" - ], - } - ], - "inputs": [ - { - "id": "board", - "type": "pickString", - "options": [ - "native_sim", - "nucleo_h723zg", - "b_u585i_iot02a", - ], - "default": "native_sim", - "description": "See https://docs.zephyrproject.org/latest/boards/index.html" - } - ] - }, - "extensions": { - "recommendations": [ - "ms-vscode.cpptools-extension-pack", - "ms-python.python", - "ms-vscode.vscode-embedded-tools", - "ms-vscode.vscode-serial-monitor", - "marus25.cortex-debug", - ] - }, - "launch": { - "version": "0.2.0", - "configurations": [ - { - "name": "Cortex Debug - ST-LINK", - "cwd": "${workspaceFolder}", - "executable": "${workspaceFolder}/build/zephyr/zephyr.elf", - "request": "launch", - "type": "cortex-debug", - "runToEntryPoint": "main", - "servertype": "stlink", - "device": "STM32H7xx", // Changed for NUCLEO-H723ZG - "interface": "swd", - "showDevDebugOutput": "none", - "v1": false, - "stm32cubeprogrammer": "" - }, - { - "name": "(gdb) Launch", - "type": "cppdbg", - "request": "launch", - "program": "${workspaceFolder}/build/zephyr/zephyr.exe", - "args": [ - "-seed=$RANDOM", - "--flash=${workspaceFolder}/flash.bin" - ], - "stopAtEntry": false, - "cwd": "${fileDirname}", - "environment": [], - "externalConsole": false, - "MIMode": "gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - }, - { - "description": "Set Disassembly Flavor to Intel", - "text": "-gdb-set disassembly-flavor intel", - "ignoreFailures": true - } - ] - } - ] - } -} diff --git a/prj.conf b/prj.conf deleted file mode 100644 index 519f3f84..00000000 --- a/prj.conf +++ /dev/null @@ -1,90 +0,0 @@ -CONFIG_SHELL=y -CONFIG_BOOT_BANNER=y -CONFIG_SHELL_PROMPT_UART="ocre:~$ " -CONFIG_NET_SHELL=y -CONFIG_HWINFO=y -CONFIG_NET_L2_WIFI_SHELL=n -CONFIG_FILE_SYSTEM_SHELL=y - -CONFIG_DEBUG=n -CONFIG_DEBUG_OPTIMIZATIONS=y -CONFIG_DEBUG_THREAD_INFO=y - -CONFIG_LOG=y -CONFIG_LOG_MODE_MINIMAL=y -CONFIG_ZCBOR_VERBOSE=n - -CONFIG_THREAD_RUNTIME_STATS=y # Needed by the latest version of WARM for time APIs - -# Allow for dynamically created threads -CONFIG_DYNAMIC_THREAD=y -CONFIG_DYNAMIC_THREAD_ALLOC=y - -# File system -CONFIG_FILE_SYSTEM=y -#CONFIG_DISK_ACCESS=y -CONFIG_FILE_SYSTEM_LITTLEFS=y - -# Settings support -CONFIG_SETTINGS=y -CONFIG_SETTINGS_FILE=y -CONFIG_SETTINGS_FILE_PATH="/lfs/settings" - -# Networking options -CONFIG_NET_IPV4=y -CONFIG_NET_IPV6=n -CONFIG_NET_UDP=y -CONFIG_NET_SOCKETS=y - -CONFIG_NET_TX_STACK_SIZE=2048 -CONFIG_NET_BUF_TX_COUNT=16 -CONFIG_NET_RX_STACK_SIZE=4096 -CONFIG_NET_BUF_RX_COUNT=32 -CONFIG_NET_MGMT_EVENT_STACK_SIZE=2048 -CONFIG_ZVFS_POLL_MAX=5 - -CONFIG_NET_DHCPV4=y - -# MCU Boot options -CONFIG_BOOTLOADER_MCUBOOT=n -CONFIG_IMG_MANAGER=y -CONFIG_MCUBOOT_IMG_MANAGER=y -CONFIG_STREAM_FLASH=y -CONFIG_IMG_ERASE_PROGRESSIVELY=y - -# System API options -CONFIG_POSIX_API=y - -# Memory / malloc optopns -CONFIG_HEAP_MEM_POOL_SIZE=65536 -CONFIG_MAIN_STACK_SIZE=8192 -CONFIG_SHELL_STACK_SIZE=8192 -CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=8192 -CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=-1 - - -# Ocre configuration -CONFIG_OCRE_WAMR_HEAP_BUFFER_SIZE=512000 -CONFIG_OCRE_CONTAINER_DEFAULT_HEAP_SIZE=4096 -CONFIG_OCRE_CONTAINER_DEFAULT_STACK_SIZE=4096 -CONFIG_LOG_TRACE_SHORT_TIMESTAMP=y - -# Ocre Feature Options -CONFIG_SENSOR=y -CONFIG_RNG_SENSOR=y - -CONFIG_OCRE_CONTAINER_MESSAGING=y -CONFIG_MESSAGING_MAX_SUBSCRIPTIONS=10 - -CONFIG_DEVICE_DT_METADATA=y -CONFIG_DEVICE_DEPS=y - - -CONFIG_MAX_TIMERS=5 -CONFIG_MAX_SENSORS=10 -CONFIG_MAX_CHANNELS_PER_SENSOR=8 - -CONFIG_OCRE_MEMORY_CHECK_ENABLED=n -CONFIG_OCRE_GPIO=y - -CONFIG_OCRE_LOG_INF=y diff --git a/scripts/c_array.awk b/scripts/c_array.awk new file mode 100644 index 00000000..a1e14cb4 --- /dev/null +++ b/scripts/c_array.awk @@ -0,0 +1,23 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +BEGIN { + printf "/* Auto-generated file. DO NOT EDIT! */\n\n" + printf "#include \n\n" + printf "const unsigned char ocre_mini_sample_image[] = {\n " + count = 0 +} +{ + for(i=1; i<=NF; i++) { + if(count > 0) printf ", " + if(count % 12 == 0 && count > 0) printf "\n " + printf "0x%s", $i + count++ + } +} +END { + printf "\n};\n\n" + printf "const size_t ocre_mini_sample_image_len = %d;\n", count +} diff --git a/scripts/check_c_formatting.sh b/scripts/check_c_formatting.sh new file mode 100755 index 00000000..ad80d707 --- /dev/null +++ b/scripts/check_c_formatting.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +# Simple formatting checker for src folder +# Checks all C/C++ files against .clang-format rules +# + +set -e + +ARGUMENT="--dry-run" + +if [ $# -eq 1 ]; then + if [ "$1" == "-f" ]; then + echo "Fixing files in place if necessary" + ARGUMENT="-i" + else + echo "Invalid option: $1" + exit 1 + fi +fi + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +SRC_DIR="$ROOT_DIR/src" + +echo "Checking formatting in $SRC_DIR/.." + +find src -type f '(' -name '*.c' -o -name '*.h' ')' ! -name 'utlist.h' -print0 | \ + xargs -0 -n1 clang-format ${ARGUMENT} -Werror diff --git a/scripts/check_formatting.sh b/scripts/check_formatting.sh deleted file mode 100755 index 12f4b332..00000000 --- a/scripts/check_formatting.sh +++ /dev/null @@ -1,80 +0,0 @@ -#!/bin/bash -# -# Simple formatting checker for /src folder -# Checks all C/C++ files against .clang-format rules -# - -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" -SRC_DIR="$ROOT_DIR/src" - -# Check if clang-format-16 is available -if ! command -v clang-format-16 &> /dev/null; then - echo "Error: clang-format-16 not found" - echo "Install it with: sudo apt-get install clang-format-16" - exit 1 -fi - -# Check if src directory exists -if [ ! -d "$SRC_DIR" ]; then - echo "Error: $SRC_DIR directory not found" - exit 1 -fi - -echo "Checking formatting in $SRC_DIR/.." - -# Find all C/C++ files and check formatting -FAILED_FILES=() -while IFS= read -r -d '' file; do - if ! clang-format-16 --style=file --dry-run --Werror "$file" 2>&1; then - FAILED_FILES+=("$file") - fi -done < <(find "$SRC_DIR" -type f \( -name "*.c" -o -name "*.cpp" -o -name "*.h" \) -print0) - -# Report results -if [ ${#FAILED_FILES[@]} -eq 0 ]; then - echo "All files are properly formatted" - exit 0 -else - echo "" - echo "The following files have formatting issues:" - for file in "${FAILED_FILES[@]}"; do - echo " - $file" - done - echo "" - echo "It is required to format the code before committing, or it will fail CI checks." - echo "" - echo "1. Install clang-format-16.0.0" - echo "" - echo " You can download the package from:" - echo " https://github.com/llvm/llvm-project/releases" - echo "" - echo " For Debian/Ubuntu:" - echo " sudo apt-get install clang-format-16" - echo "" - echo " For macOS with Homebrew (part of llvm@14):" - echo " brew install llvm@14" - echo " /usr/local/opt/llvm@14/bin/clang-format" - echo "" - echo "2. Format C/C++ source files" - echo "" - echo " Format a single file:" - echo " clang-format-16 --style=file -i path/to/file.c" - echo "" - echo " Format all files in src/:" - echo " find src/ -type f \\( -name '*.c' -o -name '*.cpp' -o -name '*.h' \\) \\" - echo " -exec clang-format-16 --style=file -i {} +" - echo "" - echo "3. Disable formatting for specific code blocks (use sparingly)" - echo "" - echo " Wrap code with clang-format off/on comments when formatted code" - echo " is not readable or friendly:" - echo "" - echo " /* clang-format off */" - echo " your code here" - echo " /* clang-format on */" - echo "" - exit 1 -fi diff --git a/src/messaging/message_types.h b/src/messaging/message_types.h deleted file mode 100644 index 77b5a566..00000000 --- a/src/messaging/message_types.h +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef OCRE_MESSAGING_TYPES_H -#define OCRE_MESSAGING_TYPES_H - -struct base { -}; - -#endif diff --git a/src/ocre/CMakeLists.txt b/src/ocre/CMakeLists.txt new file mode 100644 index 00000000..e711baa9 --- /dev/null +++ b/src/ocre/CMakeLists.txt @@ -0,0 +1,78 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +# Check if we are in a git repository +execute_process( + COMMAND git rev-parse --git-dir + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE GIT_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET +) + +if(GIT_DIR) + message(STATUS "Detected git repository. Automatic commit ID tracking is enabled.") + + # Generate commit ID header file in source dir if we are in a git repository + add_custom_command( + OUTPUT commit_id.h + COMMAND sh -c "echo '/* Auto-generated file. DO NOT EDIT */' > commit_id.h" + COMMAND sh -c "echo \"#define GIT_COMMIT_ID \\\"$(git describe --always --abbrev=0 --dirty)\\\"\" >> commit_id.h" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + VERBATIM + DEPENDS always_rebuild + ) +endif() + +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/ocre) + +# Generate build info header file in binary dir for every build +add_custom_command( + OUTPUT include/ocre/build_info.h + COMMAND sh -c "echo \"#define OCRE_BUILD_HOST_INFO \\\"$ENV{USER} @ $(uname -a)\\\"\"" > include/ocre/build_info.h + COMMAND sh -c "echo \"#define OCRE_BUILD_DATE \\\"$(date +'%Y-%m-%d %H:%M:%S %Z')\\\"\"" >> include/ocre/build_info.h + VERBATIM + DEPENDS always_rebuild +) + +# dummy command used to make the custom commands be re-run on every build (not just configure) +add_custom_command( + OUTPUT always_rebuild + COMMAND cmake -E echo +) + +add_library(OcreCore) + +# if we are not in a git repository, just fail automatically if we are missing commit_id.h +target_sources(OcreCore + PRIVATE + container.c + context.c + ocre.c + util/rm_rf.c + util/string_array.c + util/unique_random_id.c + commit_id.h + include/ocre/build_info.h +) + +target_link_libraries(OcreCore PUBLIC + OcrePlatform + OcreRuntime + OcreRuntimeWamr +) + +target_include_directories(OcreCore + PRIVATE + ${CMAKE_CURRENT_BINARY_DIR}/include + PUBLIC + include +) + +include (../../cmake/state_information.cmake) + +if(OCRE_IMAGES) + add_dependencies(OcreCore ${OCRE_IMAGES}) +endif() diff --git a/src/ocre/component/component.c b/src/ocre/component/component.c deleted file mode 100644 index 10849e52..00000000 --- a/src/ocre/component/component.c +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include - -#include "ocre_core_external.h" -#include "component.h" - -void ocre_component_init(struct ocre_component *component) -{ - core_mq_init(&component->msgq, "/ocre_component_msgq", sizeof(struct ocre_message), MSG_QUEUE_DEPTH); -} - -int ocre_component_send(struct ocre_component *component, struct ocre_message *msg) -{ - int ret = core_mq_send(&component->msgq, msg, sizeof(struct ocre_message)); - - return ret; -} diff --git a/src/ocre/component/component.h b/src/ocre/component/component.h deleted file mode 100644 index d11f8a18..00000000 --- a/src/ocre/component/component.h +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef OCRE_COMPONENT_H -#define OCRE_COMPONENT_H - -#include "ocre_core_external.h" -#include - -// can't be higher than /proc/sys/fs/mqueue/msg_max which is typically 10 -#define MSG_QUEUE_DEPTH 10 - -#define COMPONENT_SEND_SIMPLE(c, e) \ - struct ocre_message _msg = {.event = e}; \ - ocre_component_send(c, &_msg) - -struct ocre_component { - struct ocre_message msg; /*!< Message struct for reading messages into */ - core_mq_t msgq; /*!< Message queue to read from */ -}; - -void ocre_component_init(struct ocre_component *component); - -int ocre_component_send(struct ocre_component *component, struct ocre_message *msg); - -#endif // OCRE_COMPONENT_H diff --git a/src/ocre/components/container_supervisor/component_supervisor.yaml b/src/ocre/components/container_supervisor/component_supervisor.yaml deleted file mode 100644 index 2755133e..00000000 --- a/src/ocre/components/container_supervisor/component_supervisor.yaml +++ /dev/null @@ -1,11 +0,0 @@ -component: container_supervisor -events: - - name: QUERY - type: query - - name: INSTALL - type: install - - name: UNINSTALL - type: uninstall - - name: APP_MANAGER_RESPONSE - type: app_manager_response -# Any events not listed get a message type of "base_message" \ No newline at end of file diff --git a/src/ocre/components/container_supervisor/cs_main.c b/src/ocre/components/container_supervisor/cs_main.c deleted file mode 100644 index 85ec7551..00000000 --- a/src/ocre/components/container_supervisor/cs_main.c +++ /dev/null @@ -1,52 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include "cs_sm.h" -#include "ocre_core_external.h" - -LOG_MODULE_REGISTER(ocre_cs_component, OCRE_LOG_LEVEL); - -#define OCRE_CS_THREAD_PRIORITY 0 - -static core_thread_t ocre_cs_thread; -static int ocre_cs_thread_initialized = 0; - -static void ocre_cs_main(void *ctx) -{ - wasm_runtime_init_thread_env(); - LOG_INF("Container Supervisor started."); - int ret = _ocre_cs_run(ctx); - LOG_ERR("Container Supervisor exited: %d", ret); - wasm_runtime_destroy_thread_env(); -} - -// Function to start the thread -void start_ocre_cs_thread(ocre_cs_ctx *ctx) -{ - if (ocre_cs_thread_initialized) { - LOG_WRN("Container Supervisor thread is already running."); - return; - } - int ret = core_thread_create(&ocre_cs_thread, ocre_cs_main, ctx, "Ocre Container Supervisor", - OCRE_CS_THREAD_STACK_SIZE, OCRE_CS_THREAD_PRIORITY); - if (ret != 0) { - LOG_ERR("Failed to create Container Supervisor thread: %d", ret); - return; - } - ocre_cs_thread_initialized = 1; -} - -void destroy_ocre_cs_thread(void) -{ - if (!ocre_cs_thread_initialized) { - LOG_ERR("Container Supervisor thread is not running.\n"); - return; - } - core_thread_destroy(&ocre_cs_thread); - ocre_cs_thread_initialized = 0; -} diff --git a/src/ocre/components/container_supervisor/cs_sm.c b/src/ocre/components/container_supervisor/cs_sm.c deleted file mode 100644 index 275d7bf9..00000000 --- a/src/ocre/components/container_supervisor/cs_sm.c +++ /dev/null @@ -1,179 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include "ocre_core_external.h" -LOG_MODULE_DECLARE(ocre_cs_component, OCRE_LOG_LEVEL); -#ifdef CONFIG_OCRE_SENSORS -#include "../../ocre_sensors/ocre_sensors.h" -#endif -#include "cs_sm.h" -#include "cs_sm_impl.h" -#include - -// Define state machine and component -struct ocre_component ocre_cs_component; -state_machine_t ocre_cs_state_machine; - -/* State event handlers */ -static void runtime_uninitialized_entry(void *o) -{ -#if OCRE_CS_DEBUG_ON - LOG_INF("HELLO runtime_uninitialized_entry"); -#endif - struct ocre_message event = {.event = EVENT_CS_INITIALIZE}; - ocre_component_send(&ocre_cs_component, &event); -} - -static enum smf_state_result runtime_uninitialized_run(void *o) -{ -#if OCRE_CS_DEBUG_ON - LOG_INF("HELLO runtime_uninitialized_run"); -#endif - struct ocre_message *msg = SM_GET_EVENT(o); - - switch (msg->event) { - case EVENT_CS_INITIALIZE: -#if OCRE_CS_DEBUG_ON - LOG_INF("Transitioning from state STATE_RUNTIME_UNINITIALIZED_RUN to state " - "STATE_RUNTIME_RUNNING"); -#endif - sm_transition(&ocre_cs_state_machine, STATE_RUNTIME_RUNNING); - break; - default: - break; - } - SM_MARK_EVENT_HANDLED(o); - return SMF_EVENT_HANDLED; -} - -static void runtime_running_entry(void *o) -{ -#if OCRE_CS_DEBUG_ON - LOG_INF("HELLO runtime_running_entry"); -#endif -} - -static enum smf_state_result runtime_running_run(void *o) -{ -#if OCRE_CS_DEBUG_ON - LOG_INF("HELLO runtime_running_run"); -#endif - struct ocre_message *msg = SM_GET_EVENT(o); - ocre_cs_ctx *ctx = SM_GET_CUSTOM_CTX(o); - ocre_container_runtime_cb callback = NULL; - switch (msg->event) { - case EVENT_CREATE_CONTAINER: { - LOG_INF("EVENT_CREATE_CONTAINER"); - - if (msg->containerId < 0 || msg->containerId >= CONFIG_MAX_CONTAINERS) { - LOG_ERR("Invalid container ID: %d", msg->containerId); - break; - } - - if (CS_create_container(&ctx->containers[msg->containerId]) == CONTAINER_STATUS_CREATED) { - LOG_INF("Created container in slot:%d", msg->containerId); - } else { - LOG_ERR("Failed to create container in slot:%d", msg->containerId); - } - break; - } - case EVENT_RUN_CONTAINER: { - LOG_INF("EVENT_RUN_CONTAINER"); - - if (msg->containerId < 0 || msg->containerId >= CONFIG_MAX_CONTAINERS) { - LOG_ERR("Invalid container ID: %d", msg->containerId); - break; - } - - if (CS_run_container(&ctx->containers[msg->containerId]) == CONTAINER_STATUS_RUNNING) { - LOG_INF("Started container in slot:%d", msg->containerId); - } else { - LOG_ERR("Failed to run container in slot:%d", msg->containerId); - } - break; - } - case EVENT_STOP_CONTAINER: { - LOG_INF("EVENT_STOP_CONTAINER"); - - if (msg->containerId < 0 || msg->containerId >= CONFIG_MAX_CONTAINERS) { - LOG_ERR("Invalid container ID: %d", msg->containerId); - break; - } - - if (CS_stop_container(&ctx->containers[msg->containerId], callback) == - CONTAINER_STATUS_STOPPED) { - LOG_INF("Stopped container in slot:%d", msg->containerId); - } else { - LOG_ERR("Failed to stop container in slot:%d", msg->containerId); - } - break; - } - case EVENT_DESTROY_CONTAINER: { - LOG_INF("EVENT_DESTROY_CONTAINER"); - - if (msg->containerId < 0 || msg->containerId >= CONFIG_MAX_CONTAINERS) { - LOG_ERR("Invalid container ID: %d", msg->containerId); - break; - } - - if (CS_destroy_container(&ctx->containers[msg->containerId], callback) == - CONTAINER_STATUS_DESTROYED) { - LOG_INF("Destroyed container in slot:%d", msg->containerId); - } else { - LOG_ERR("Failed to destroy container in slot:%d", msg->containerId); - } - break; - } - case EVENT_RESTART_CONTAINER: { - LOG_INF("EVENT_RESTART_CONTAINER"); - if (msg->containerId < 0 || msg->containerId >= CONFIG_MAX_CONTAINERS) { - LOG_ERR("Invalid container ID: %d", msg->containerId); - break; - } - - if (CS_restart_container(&ctx->containers[msg->containerId], callback) == - CONTAINER_STATUS_RUNNING) { - LOG_INF("Container in slot:%d restarted successfully", msg->containerId); - } else { - LOG_ERR("Failed to restart container in slot:%d", msg->containerId); - } - break; - } - case EVENT_CS_DESTROY: - LOG_INF("EVENT_CS_DESTROY"); - sm_transition(&ocre_cs_state_machine, STATE_RUNTIME_UNINITIALIZED); - break; - default: - break; - } - - SM_MARK_EVENT_HANDLED(o); - return SMF_EVENT_HANDLED; -} - -static enum smf_state_result runtime_error_run(void *o) -{ -#if OCRE_CS_DEBUG_ON - LOG_INF("HELLO runtime_error_run"); -#endif - return SMF_EVENT_HANDLED; -} - -static const struct smf_state hsm[] = { - [STATE_RUNTIME_UNINITIALIZED] = - SMF_CREATE_STATE(runtime_uninitialized_entry, runtime_uninitialized_run, NULL, NULL, NULL), - [STATE_RUNTIME_RUNNING] = SMF_CREATE_STATE(runtime_running_entry, runtime_running_run, NULL, NULL, NULL), - [STATE_RUNTIME_ERROR] = SMF_CREATE_STATE(NULL, runtime_error_run, NULL, NULL, NULL)}; - -// Entry point for running the state machine -int _ocre_cs_run(ocre_cs_ctx *ctx) -{ - ocre_component_init(&ocre_cs_component); - sm_init(&ocre_cs_state_machine, &ocre_cs_component.msgq, &ocre_cs_component.msg, ctx, hsm); - return sm_run(&ocre_cs_state_machine, STATE_RUNTIME_UNINITIALIZED); -} diff --git a/src/ocre/components/container_supervisor/cs_sm.h b/src/ocre/components/container_supervisor/cs_sm.h deleted file mode 100644 index 3a323949..00000000 --- a/src/ocre/components/container_supervisor/cs_sm.h +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef OCRE_IWASM_H -#define OCRE_IWASM_H - -#include "ocre_core_external.h" - -#include -#include -#include - -#define OCRE_CS_DEBUG_ON 0 - -extern struct ocre_component ocre_cs_component; -extern state_machine_t ocre_cs_state_machine; // TODO This needs to get encapsulated into the - // sm. it's only here so components can operate - // timers. timers need to be encapsulated. - -enum CONTAINER_RUNTIME_STATE { - STATE_RUNTIME_UNINITIALIZED, - STATE_RUNTIME_RUNNING, - STATE_RUNTIME_ERROR -}; - -enum OCRE_CS_EVENT { - // CS Events - EVENT_CS_INITIALIZE, - EVENT_CS_DESTROY, - EVENT_CS_ERROR, - - // Container related events - EVENT_CREATE_CONTAINER, - EVENT_RUN_CONTAINER, - EVENT_STOP_CONTAINER, - EVENT_DESTROY_CONTAINER, - EVENT_RESTART_CONTAINER, - EVENT_ERROR -}; -void start_ocre_cs_thread(ocre_cs_ctx *ctx); - -void destroy_ocre_cs_thread(void); - -int _ocre_cs_run(); - -#endif diff --git a/src/ocre/components/container_supervisor/cs_sm_impl.c b/src/ocre/components/container_supervisor/cs_sm_impl.c deleted file mode 100644 index 813072af..00000000 --- a/src/ocre/components/container_supervisor/cs_sm_impl.c +++ /dev/null @@ -1,645 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ -#include -#include "ocre_core_external.h" - -#ifdef CONFIG_OCRE_TIMER -#include "ocre_timers/ocre_timer.h" -#endif -#ifdef CONFIG_OCRE_GPIO -#include "ocre_gpio/ocre_gpio.h" -#endif -#ifdef CONFIG_OCRE_CONTAINER_MESSAGING -#include "ocre_messaging/ocre_messaging.h" -#endif -#if defined(CONFIG_OCRE_TIMER) || defined(CONFIG_OCRE_GPIO) || defined(CONFIG_OCRE_SENSORS) || \ - defined(CONFIG_OCRE_CONTAINER_MESSAGING) -#include "api/ocre_common.h" -#endif - -#ifdef CONFIG_OCRE_SHELL -#include "ocre/shell/ocre_shell.h" -#endif - -LOG_MODULE_DECLARE(ocre_cs_component, OCRE_LOG_LEVEL); - -#include "../../../../../wasm-micro-runtime/core/iwasm/include/lib_export.h" -#include "bh_log.h" -#include -#include "cs_sm.h" -#include "cs_sm_impl.h" - -#include "ocre_psram.h" - -// WAMR heap buffer - uses PSRAM when available -#if defined(CONFIG_MEMC) -PSRAM_SECTION_ATTR -#endif -static char wamr_heap_buf[CONFIG_OCRE_WAMR_HEAP_BUFFER_SIZE] = {0}; - -// Thread pool for container execution -#define CONTAINER_THREAD_POOL_SIZE 4 -static core_thread_t container_threads[CONTAINER_THREAD_POOL_SIZE]; -static bool container_thread_active[CONTAINER_THREAD_POOL_SIZE] = {false}; - -static core_mutex_t container_mutex; - -// Arguments for container threads -struct container_thread_args { - ocre_container_t *container; -}; - -#ifdef CONFIG_OCRE_MEMORY_CHECK_ENABLED -static size_t ocre_get_available_memory(void) -{ -#ifdef CONFIG_HEAP_MEM_POOL_SIZE - struct k_heap_stats stats; - k_heap_sys_get_stats(&stats); - return stats.free_bytes; -#else - extern char *z_malloc_mem_pool_area_start; - extern char *z_malloc_mem_pool_area_end; - extern struct sys_mem_pool_base z_malloc_mem_pool; - - size_t total_size = z_malloc_mem_pool_area_end - z_malloc_mem_pool_area_start; - size_t used_size = sys_mem_pool_get_used_size(&z_malloc_mem_pool); - return total_size - used_size; -#endif -} -#endif - -#ifdef CONFIG_OCRE_SHARED_HEAP -// Shared heap for memory-mapped access -wasm_shared_heap_t _shared_heap = NULL; -#ifdef CONFIG_OCRE_SHARED_HEAP_BUF_VIRTUAL -uint8 preallocated_buf[CONFIG_OCRE_SHARED_HEAP_BUF_SIZE]; -#endif -#endif - -static bool validate_container_memory(ocre_container_t *container) -{ -#ifdef CONFIG_OCRE_MEMORY_CHECK_ENABLED - size_t requested_heap = container->ocre_container_data.heap_size; - size_t requested_stack = container->ocre_container_data.stack_size; - - if (requested_heap == 0 || requested_heap > CONFIG_OCRE_CONTAINER_DEFAULT_HEAP_SIZE) { - LOG_ERR("Invalid heap size requested: %zu bytes", requested_heap); - return false; - } - - if (requested_stack == 0 || requested_stack > CONFIG_OCRE_CONTAINER_DEFAULT_STACK_SIZE) { - LOG_ERR("Invalid stack size requested: %zu bytes", requested_stack); - return false; - } - - size_t available_memory = ocre_get_available_memory(); - size_t required_memory = requested_heap + requested_stack + sizeof(WASMExecEnv); - - if (available_memory < required_memory) { - LOG_ERR("Insufficient memory for container %d: required %zu bytes, available %zu bytes", - container->container_ID, required_memory, available_memory); - return false; - } - LOG_INF("Memory check passed: %zu bytes required, %zu bytes available", required_memory, available_memory); - - container->ocre_runtime_arguments.stack_size = requested_stack; - container->ocre_runtime_arguments.heap_size = requested_heap; -#else - container->ocre_runtime_arguments.stack_size = CONFIG_OCRE_CONTAINER_DEFAULT_STACK_SIZE; - container->ocre_runtime_arguments.heap_size = CONFIG_OCRE_CONTAINER_DEFAULT_HEAP_SIZE; -#endif - return true; -} - -// Container thread entry function -static void container_thread_entry(void *args) -{ - struct container_thread_args *container_args = args; - ocre_container_t *container = container_args->container; - wasm_module_inst_t module_inst = container->ocre_runtime_arguments.module_inst; - - // Initialize WASM runtime thread environment - wasm_runtime_init_thread_env(); - - LOG_INF("Container thread %d started", container->container_ID); - -#if defined(CONFIG_OCRE_TIMER) || defined(CONFIG_OCRE_GPIO) || defined(CONFIG_OCRE_SENSORS) || \ - defined(CONFIG_OCRE_CONTAINER_MESSAGING) - // Set TLS for the container's WASM module - current_module_tls = &module_inst; -#endif - - // Run the WASM main function with exception handling - bool success = false; - const char *exception = NULL; - - // Clear any previous exceptions - wasm_runtime_clear_exception(module_inst); - - // Execute main function - success = wasm_application_execute_main(module_inst, 0, NULL); - - // Check for exceptions - exception = wasm_runtime_get_exception(module_inst); - if (exception) { - LOG_ERR("Container %d exception: %s", container->container_ID, exception); - success = false; - } - // Update container status - if (container->container_runtime_status != CONTAINER_STATUS_STOPPED) - container->container_runtime_status = success ? CONTAINER_STATUS_STOPPED : CONTAINER_STATUS_ERROR; - // Cleanup sequence - core_mutex_lock(&container->lock); - { - // Cleanup subsystems -#ifdef CONFIG_OCRE_TIMER - ocre_timer_cleanup_container(module_inst); -#endif -#ifdef CONFIG_OCRE_GPIO - ocre_gpio_cleanup_container(module_inst); -#endif -#ifdef CONFIG_OCRE_CONTAINER_MESSAGING - ocre_messaging_cleanup_container(module_inst); -#endif - - // Clear thread tracking - container_thread_active[container->container_ID] = false; -#if defined(CONFIG_OCRE_TIMER) || defined(CONFIG_OCRE_GPIO) || defined(CONFIG_OCRE_SENSORS) || \ - defined(CONFIG_OCRE_CONTAINER_MESSAGING) - // Clear TLS - current_module_tls = NULL; -#endif - } - core_mutex_unlock(&container->lock); - - if (success) { - LOG_INF("Container %d completed successfully", container->container_ID); - } else { - LOG_ERR("Container %d failed: %s", container->container_ID, exception ? exception : "unknown error"); - } - - // Clean up WASM runtime thread environment - wasm_runtime_destroy_thread_env(); - core_free(args); // Free the dynamically allocated args -} - -static int get_available_thread(void) -{ - for (int i = 0; i < CONTAINER_THREAD_POOL_SIZE; i++) { - if (!container_thread_active[i]) { - return i; - } - } - return -1; -} - -static int load_binary_to_buffer_fs(ocre_runtime_arguments_t *container_arguments, - ocre_container_data_t *container_data) -{ - int ret = 0; - size_t file_size = 0; - void *file_handle = NULL; - char filepath[FILE_PATH_MAX]; - - ret = core_construct_filepath(filepath, sizeof(filepath), container_data->sha256); - if (ret < 0) { - LOG_ERR("Failed to construct filepath for %s: %d", container_data->sha256, ret); - return ret; - } - ret = core_filestat(filepath, &file_size); - if (ret < 0) { - LOG_ERR("Failed to get file status for %s: %d", filepath, ret); - return ret; - } - - container_arguments->size = file_size; - container_arguments->buffer = storage_heap_alloc(file_size); - if (!container_arguments->buffer) { - LOG_ERR("Failed to allocate memory for container binary from PSRAM."); - return -ENOMEM; - } - - LOG_INF("File path: %s, size: %lu", filepath, (long unsigned int)file_size); - - ret = core_fileopen(filepath, &file_handle); - if (ret < 0) { - LOG_ERR("Failed to open file %s: %d", filepath, ret); - storage_heap_free(container_arguments->buffer); - return ret; - } - - ret = core_fileread(file_handle, container_arguments->buffer, file_size); - if (ret < 0) { - LOG_ERR("Failed to read file %s: %d", filepath, ret); - core_fileclose(file_handle); - storage_heap_free(container_arguments->buffer); - return ret; - } - - ret = core_fileclose(file_handle); - if (ret < 0) { - LOG_ERR("Failed to close file %s: %d", filepath, ret); - storage_heap_free(container_arguments->buffer); - return ret; - } - return 0; -} - -int CS_ctx_init(ocre_cs_ctx *ctx) -{ - for (int i = 0; i < CONFIG_MAX_CONTAINERS; i++) { - core_mutex_init(&ctx->containers[i].lock); - ctx->containers[i].container_runtime_status = CONTAINER_STATUS_UNKNOWN; - ctx->containers[i].ocre_container_data.heap_size = CONFIG_OCRE_CONTAINER_DEFAULT_HEAP_SIZE; - ctx->containers[i].ocre_container_data.stack_size = CONFIG_OCRE_CONTAINER_DEFAULT_STACK_SIZE; - memset(ctx->containers[i].ocre_container_data.name, 0, - sizeof(ctx->containers[i].ocre_container_data.name)); - memset(ctx->containers[i].ocre_container_data.sha256, 0, - sizeof(ctx->containers[i].ocre_container_data.sha256)); - ctx->containers[i].ocre_container_data.timers = 0; - ctx->containers[i].container_ID = i; - } -#ifdef CONFIG_OCRE_SHELL - register_ocre_shell(ctx); -#endif - core_mutex_init(&container_mutex); - return 0; -} - -ocre_container_runtime_status_t CS_runtime_init(ocre_cs_ctx *ctx, ocre_container_init_arguments_t *args) -{ - RuntimeInitArgs init_args; - memset(&init_args, 0, sizeof(RuntimeInitArgs)); - init_args.mem_alloc_type = Alloc_With_Pool; - init_args.mem_alloc_option.pool.heap_buf = wamr_heap_buf; - init_args.mem_alloc_option.pool.heap_size = sizeof(wamr_heap_buf); - init_args.native_module_name = "env"; - init_args.n_native_symbols = ocre_api_table_size; - init_args.native_symbols = ocre_api_table; - - if (!wasm_runtime_full_init(&init_args)) { - LOG_ERR("Failed to initialize the WASM runtime"); - return RUNTIME_STATUS_ERROR; - } - - bh_log_set_verbose_level(BH_LOG_LEVEL_WARNING); - - if (!wasm_runtime_register_natives("env", ocre_api_table, ocre_api_table_size)) { - LOG_ERR("Failed to register the API's"); - return RUNTIME_STATUS_ERROR; - } -#ifdef CONFIG_OCRE_TIMER - ocre_timer_init(); -#endif -#ifdef CONFIG_OCRE_GPIO - ocre_gpio_init(); -#endif -#ifdef CONFIG_OCRE_CONTAINER_MESSAGING - ocre_messaging_init(); -#endif -#ifdef CONFIG_OCRE_SHARED_HEAP - SharedHeapInitArgs heap_init_args; - memset(&heap_init_args, 0, sizeof(heap_init_args)); - -#ifdef CONFIG_OCRE_SHARED_HEAP_BUF_PHYSICAL - // Physical mode - map hardware register address - heap_init_args.pre_allocated_addr = (void *)CONFIG_OCRE_SHARED_HEAP_BUF_ADDRESS; - LOG_INF("Creating physical memory mapping at 0x%08X (hardware registers)", CONFIG_OCRE_SHARED_HEAP_BUF_ADDRESS); -#elif CONFIG_OCRE_SHARED_HEAP_BUF_VIRTUAL - // Virtual mode - allocate from RAM - heap_init_args.pre_allocated_addr = preallocated_buf; - LOG_INF("Creating virtual shared heap in RAM, size=%d bytes", CONFIG_OCRE_SHARED_HEAP_BUF_SIZE); -#endif - heap_init_args.size = CONFIG_OCRE_SHARED_HEAP_BUF_SIZE; - _shared_heap = wasm_runtime_create_shared_heap(&heap_init_args); - - if (!_shared_heap) { - LOG_ERR("Create preallocated shared heap failed"); - return RUNTIME_STATUS_ERROR; - } -#endif - - storage_heap_init(); - return RUNTIME_STATUS_INITIALIZED; -} - -ocre_container_runtime_status_t CS_runtime_destroy(void) -{ - // Signal event threads to exit gracefully -#if defined(CONFIG_OCRE_TIMER) || defined(CONFIG_OCRE_GPIO) || defined(CONFIG_OCRE_SENSORS) || \ - defined(CONFIG_OCRE_CONTAINER_MESSAGING) - ocre_common_shutdown(); -#endif - - // Abort any active container threads - for (int i = 0; i < CONTAINER_THREAD_POOL_SIZE; i++) { - if (container_thread_active[i]) { - core_thread_destroy(&container_threads[i]); - container_thread_active[i] = false; - } - } - return RUNTIME_STATUS_DESTROYED; -} - -ocre_container_status_t CS_create_container(ocre_container_t *container) -{ - uint32_t curr_container_ID = container->container_ID; - ocre_container_data_t *curr_container_data = &container->ocre_container_data; - ocre_runtime_arguments_t *curr_container_arguments = &container->ocre_runtime_arguments; - - if (container->container_runtime_status == CONTAINER_STATUS_STOPPED) { - LOG_WRN("Container %d is in STOPPED state, destroying it before reuse", curr_container_ID); - CS_destroy_container(container, NULL); - } - - if (container->container_runtime_status != CONTAINER_STATUS_UNKNOWN && - container->container_runtime_status != CONTAINER_STATUS_DESTROYED && - container->container_runtime_status != CONTAINER_STATUS_RESERVED) { - LOG_ERR("Cannot create container again container with ID: %d, already exists", curr_container_ID); - return CONTAINER_STATUS_ERROR; - } - - if (!validate_container_memory(container)) { - LOG_ERR("Memory check not passed"); - return CONTAINER_STATUS_ERROR; - } - - LOG_INF("Allocating memory for container %d", curr_container_ID); - int ret = load_binary_to_buffer_fs(curr_container_arguments, curr_container_data); - if (ret < 0) { - LOG_ERR("Failed to load binary to buffer: %d", ret); - return CONTAINER_STATUS_ERROR; - } - LOG_INF("Loaded binary to buffer for container %d", curr_container_ID); - - curr_container_arguments->module = - wasm_runtime_load(curr_container_arguments->buffer, curr_container_arguments->size, - curr_container_arguments->error_buf, sizeof(curr_container_arguments->error_buf)); - if (!curr_container_arguments->module) { - LOG_ERR("Failed to load WASM module: %s", curr_container_arguments->error_buf); - storage_heap_free(curr_container_arguments->buffer); - return CONTAINER_STATUS_ERROR; - } - - container->container_runtime_status = CONTAINER_STATUS_CREATED; - LOG_WRN("Created container:%d", curr_container_ID); - return container->container_runtime_status; -} - -ocre_container_status_t CS_run_container(ocre_container_t *container) -{ - uint32_t curr_container_ID = container->container_ID; - ocre_runtime_arguments_t *curr_container_arguments = &container->ocre_runtime_arguments; - - if (container->container_runtime_status == CONTAINER_STATUS_RUNNING) { - LOG_WRN("Container status is already in RUNNING state"); - return CONTAINER_STATUS_RUNNING; - } - - if (container->container_runtime_status != CONTAINER_STATUS_CREATED && - container->container_runtime_status != CONTAINER_STATUS_STOPPED) { - LOG_ERR("Container (ID: %d), is not in a valid state to run", curr_container_ID); - container->container_runtime_status = CONTAINER_STATUS_ERROR; - return CONTAINER_STATUS_ERROR; - } - -#ifdef CONFIG_OCRE_NETWORKING -#define ADDRESS_POOL_SIZE 1 - const char *addr_pool[ADDRESS_POOL_SIZE] = { - "0.0.0.0/0", - }; - wasm_runtime_set_wasi_addr_pool(curr_container_arguments->module, addr_pool, ADDRESS_POOL_SIZE); - /** - * Configure which domain names a WebAssembly module is allowed to resolve via DNS lookups - * ns_lookup_pool: An array of domain name patterns (e.g., "example.com", "*.example.com", or "*" for any - * domain) - */ - - const char *ns_lookup_pool[] = {"*"}; - wasm_runtime_set_wasi_ns_lookup_pool(curr_container_arguments->module, ns_lookup_pool, - sizeof(ns_lookup_pool) / sizeof(ns_lookup_pool[0])); -#endif - -#ifdef CONFIG_OCRE_CONTAINER_FILESYSTEM -// Simple for now: map CONTAINER_FS_PATH to / -// TODO: eventually every container should probably have its own root folder, -// however wasm_runtime_set_wasi_args expects constant values. -#define DIR_LIST_SIZE 1 - static const char *dir_map_list[DIR_LIST_SIZE] = {"/::" CONTAINER_FS_PATH}; - wasm_runtime_set_wasi_args(curr_container_arguments->module, NULL, 0, dir_map_list, DIR_LIST_SIZE, NULL, 0, - NULL, 0); -#endif - - if (curr_container_arguments->module_inst) { - LOG_INF("WASM runtime already instantiated for container:%d", curr_container_ID); - } else { - LOG_INF("Instantiating WASM runtime for container:%d", curr_container_ID); - curr_container_arguments->module_inst = wasm_runtime_instantiate( - curr_container_arguments->module, curr_container_arguments->stack_size, - curr_container_arguments->heap_size, curr_container_arguments->error_buf, - sizeof(curr_container_arguments->error_buf)); - if (!curr_container_arguments->module_inst) { - LOG_ERR("Failed to instantiate WASM module: %s, for containerID= %d", - curr_container_arguments->error_buf, curr_container_ID); - wasm_runtime_unload(curr_container_arguments->module); - storage_heap_free(curr_container_arguments->buffer); - return CONTAINER_STATUS_ERROR; - } -#if defined(CONFIG_OCRE_TIMER) || defined(CONFIG_OCRE_GPIO) || defined(CONFIG_OCRE_SENSORS) || \ - defined(CONFIG_OCRE_CONTAINER_MESSAGING) - ocre_register_module(curr_container_arguments->module_inst); -#endif -#ifdef CONFIG_OCRE_SHARED_HEAP - LOG_INF("Attaching shared heap to container %d", curr_container_ID); - /* attach module instance to the shared heap */ - if (!wasm_runtime_attach_shared_heap(curr_container_arguments->module_inst, _shared_heap)) { - LOG_ERR("Attach shared heap failed."); - return CONTAINER_STATUS_ERROR; - } - -#ifdef CONFIG_OCRE_SHARED_HEAP_BUF_PHYSICAL - // For physical mode, get the base address from the shared heap itself - // The WASM address space already knows about the physical mapping - uint32 shared_heap_base_addr = wasm_runtime_addr_native_to_app( - curr_container_arguments->module_inst, (void *)CONFIG_OCRE_SHARED_HEAP_BUF_ADDRESS); - LOG_INF("Physical shared heap base address in app: 0x%x", shared_heap_base_addr); -#elif CONFIG_OCRE_SHARED_HEAP_BUF_VIRTUAL - // For virtual mode, convert the allocated buffer address - uint32 shared_heap_base_addr = - wasm_runtime_addr_native_to_app(curr_container_arguments->module_inst, preallocated_buf); - LOG_INF("Virtual shared heap base address in app: 0x%x", shared_heap_base_addr); -#endif - - if (shared_heap_base_addr == 0) { - LOG_ERR("Failed to get shared heap WASM address!"); - return CONTAINER_STATUS_ERROR; - } -#endif - } - core_mutex_lock(&container_mutex); - int thread_idx = get_available_thread(); - if (thread_idx == -1) { - LOG_ERR("No available threads for container %d", curr_container_ID); - container->container_runtime_status = CONTAINER_STATUS_ERROR; - core_mutex_unlock(&container_mutex); - return CONTAINER_STATUS_ERROR; - } - - // Allocate thread arguments dynamically - struct container_thread_args *args = core_malloc(sizeof(struct container_thread_args)); - if (!args) { - LOG_ERR("Failed to allocate thread args for container %d", curr_container_ID); - container->container_runtime_status = CONTAINER_STATUS_ERROR; - core_mutex_unlock(&container_mutex); - return CONTAINER_STATUS_ERROR; - } - args->container = container; - - // Create and start a new thread for the container - char thread_name[16]; - snprintf(thread_name, sizeof(thread_name), "container_%d", curr_container_ID); - container_threads[thread_idx].user_options = curr_container_ID; - int ret = core_thread_create(&container_threads[thread_idx], container_thread_entry, args, thread_name, - CONTAINER_THREAD_STACK_SIZE, 5); - - if (ret != 0) { - LOG_ERR("Failed to create thread for container %d", curr_container_ID); - container->container_runtime_status = CONTAINER_STATUS_ERROR; - - core_free(args); // Free the dynamically allocated args - - core_mutex_unlock(&container_mutex); - return CONTAINER_STATUS_ERROR; - } - - container_thread_active[thread_idx] = true; - core_mutex_unlock(&container_mutex); - - container->container_runtime_status = CONTAINER_STATUS_RUNNING; - LOG_WRN("Running container:%d in dedicated thread", curr_container_ID); - return CONTAINER_STATUS_RUNNING; -} - -ocre_container_status_t CS_get_container_status(ocre_cs_ctx *ctx, int container_id) -{ - if (container_id < 0 || container_id >= CONFIG_MAX_CONTAINERS) { - LOG_ERR("Invalid container ID: %d", container_id); - return CONTAINER_STATUS_ERROR; - } - - return ctx->containers[container_id].container_runtime_status; -} - -ocre_container_status_t CS_stop_container(ocre_container_t *container, ocre_container_runtime_cb callback) -{ - uint32_t curr_container_ID = container->container_ID; - ocre_runtime_arguments_t *curr_container_arguments = &container->ocre_runtime_arguments; - if (container->container_runtime_status == CONTAINER_STATUS_STOPPED) { - LOG_WRN("Container status is already in STOP state"); - return CONTAINER_STATUS_STOPPED; - } - core_mutex_lock(&container->lock); - { - LOG_INF("Stopping container %d from state %d", curr_container_ID, container->container_runtime_status); - - for (int i = 0; i < CONTAINER_THREAD_POOL_SIZE; i++) { - if (container_thread_active[i] && container_threads[i].user_options == curr_container_ID) { -#if defined(CONFIG_OCRE_CONTAINER_WAMR_TERMINATION) - /** - * wasm_runtime_terminate uses POSIX signals to terminate the thread from the outside; - * calling core_thread_destroy would try to destroy again the thread, and pthread_join() - * will never return, while freeing the stack would cause segfault. This separation is - * needed to distinguish platform supported by wamr with this features, from those which - * aren't. Since this function exists on those platforms, but stubbed, config parameter - * is used. - */ - wasm_runtime_terminate(curr_container_arguments->module_inst); -#else - core_thread_destroy(&container_threads[i]); -#endif - container_thread_active[i] = false; - } - } -#ifdef CONFIG_OCRE_TIMER - ocre_timer_cleanup_container(curr_container_arguments->module_inst); -#endif -#ifdef CONFIG_OCRE_GPIO - ocre_gpio_cleanup_container(curr_container_arguments->module_inst); -#endif -#ifdef CONFIG_OCRE_CONTAINER_MESSAGING - ocre_messaging_cleanup_container(curr_container_arguments->module_inst); -#endif - -#if defined(CONFIG_OCRE_TIMER) || defined(CONFIG_OCRE_GPIO) || defined(CONFIG_OCRE_SENSORS) || \ - defined(CONFIG_OCRE_CONTAINER_MESSAGING) - ocre_unregister_module(curr_container_arguments->module_inst); -#endif - - if (curr_container_arguments->module_inst) { - wasm_runtime_deinstantiate(curr_container_arguments->module_inst); - curr_container_arguments->module_inst = NULL; - } - - container->container_runtime_status = CONTAINER_STATUS_STOPPED; - } - core_mutex_unlock(&container->lock); - - if (callback) { - callback(); - } - return CONTAINER_STATUS_STOPPED; -} - -ocre_container_status_t CS_destroy_container(ocre_container_t *container, ocre_container_runtime_cb callback) -{ - if (container->container_runtime_status != CONTAINER_STATUS_STOPPED) { - CS_stop_container(container, NULL); - } - - core_mutex_lock(&container->lock); - { - LOG_INF("Destroying container %d", container->container_ID); - if (container->ocre_runtime_arguments.module) { - wasm_runtime_unload(container->ocre_runtime_arguments.module); - container->ocre_runtime_arguments.module = NULL; - } - - if (container->ocre_runtime_arguments.buffer) { - storage_heap_free(container->ocre_runtime_arguments.buffer); - container->ocre_runtime_arguments.buffer = NULL; - } - - memset(&container->ocre_container_data, 0, sizeof(ocre_container_data_t)); - container->container_runtime_status = CONTAINER_STATUS_DESTROYED; - } - core_mutex_unlock(&container->lock); - - if (callback) { - callback(); - } - return CONTAINER_STATUS_DESTROYED; -} - -ocre_container_status_t CS_restart_container(ocre_container_t *container, ocre_container_runtime_cb callback) -{ - ocre_container_status_t status = CS_stop_container(container, NULL); - if (status != CONTAINER_STATUS_STOPPED) { - LOG_ERR("Failed to stop container: %d", container->container_ID); - return CONTAINER_STATUS_ERROR; - } - - status = CS_run_container(container); - if (status != CONTAINER_STATUS_RUNNING) { - LOG_ERR("Failed to start container: %d", container->container_ID); - return CONTAINER_STATUS_ERROR; - } - if (callback) { - callback(); - } - - return CONTAINER_STATUS_RUNNING; -} diff --git a/src/ocre/components/container_supervisor/cs_sm_impl.h b/src/ocre/components/container_supervisor/cs_sm_impl.h deleted file mode 100644 index f3cae177..00000000 --- a/src/ocre/components/container_supervisor/cs_sm_impl.h +++ /dev/null @@ -1,119 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef OCRE_CS_SM_IMPL_H -#define OCRE_CS_SM_IMPL_H - -#include "ocre_core_external.h" -#include "api/ocre_api.h" -#include - -// ----------------------------------------------------------------------------- -// Function prototypes for OCRE container runtime and container management -// ----------------------------------------------------------------------------- - -// RUNTIME - -/** - * @brief Initialize the container runtime environment. - * - * This function sets up the runtime environment for OCRE containers, preparing necessary resources and state. - * - * @param ctx Pointer to the container runtime context. - * @param args Pointer to initialization arguments. - * @return ocre_container_runtime_status_t Status of the initialization (e.g., success or error code). - */ -ocre_container_runtime_status_t CS_runtime_init(ocre_cs_ctx *ctx, ocre_container_init_arguments_t *args); - -/** - * @brief Destroy the container runtime environment. - * - * This function cleans up and releases any resources used by the container runtime. - * - * @return ocre_container_runtime_status_t Status of the destruction process. - */ -ocre_container_runtime_status_t CS_runtime_destroy(void); - -// CONTAINER - -/** - * @brief Create a new container. - * - * This function creates a container with the specified container ID and associates it with the context. - * - * @param ctx Pointer to the container runtime context. - * @param container_id The ID of the container to be created. - * @return ocre_container_status_t Status of the container creation. - */ -ocre_container_status_t CS_create_container(ocre_container_t *container); - -/** - * @brief Run a container. - * - * This function starts the execution of a container based on its container ID. - * - * @param ctx Pointer to the container runtime context. - * @param container_id The ID of the container to be run. - * @return ocre_container_status_t Status of the container execution. - */ -ocre_container_status_t CS_run_container(ocre_container_t *ctx); - -/** - * @brief Get the status of a container. - * - * This function retrieves the current status of the container. - * - * @param ctx Pointer to the container runtime context. - * @param container_id The ID of the container whose status is being queried. - * @return ocre_container_status_t Status of the container. - */ -ocre_container_status_t CS_get_container_status(ocre_cs_ctx *ctx, int container_id); - -/** - * @brief Stop a container. - * - * This function stops the execution of the container and invokes a callback function when completed. - * - * @param container_id The ID of the container to be stopped. - * @param callback Callback function to be invoked after stopping the container. - * @return ocre_container_status_t Status of the stop operation. - */ -ocre_container_status_t CS_stop_container(ocre_container_t *container, ocre_container_runtime_cb callback); - -/** - * @brief Destroy a container. - * - * This function removes and destroys the specified container from the runtime. - * - * @param container_id The ID of the container to be destroyed. - * @param callback Callback function to be invoked after destroying the container. - * @return ocre_container_status_t Status of the container destruction. - */ -ocre_container_status_t CS_destroy_container(ocre_container_t *container, ocre_container_runtime_cb callback); - -/** - * @brief Restart a container. - * - * This function restarts a container by stopping and then running it again. - * - * @param container_id The ID of the container to be restarted. - * @param callback Callback function to be invoked after restarting the container. - * @return ocre_container_status_t Status of the container restart. - */ -ocre_container_status_t CS_restart_container(ocre_container_t *container, ocre_container_runtime_cb callback); - -/** - * @brief context initialization - * - * This function initialize the container runtime context. - * - * @param ctx Pointer to the container runtime context. - * @return POSIX style return if the container runtime context was initialized right 0 else -n - */ -int CS_ctx_init(ocre_cs_ctx *ctx); - -#endif // OCRE_CS_SM_IMPL_H diff --git a/src/ocre/components/container_supervisor/message_types.h b/src/ocre/components/container_supervisor/message_types.h deleted file mode 100644 index 9c29425c..00000000 --- a/src/ocre/components/container_supervisor/message_types.h +++ /dev/null @@ -1,33 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef OCRE_CS_MESSAGING_TYPES_H -#define OCRE_CS_MESSAGING_TYPES_H - -#include "ocre_core_external.h" - -struct install { - char name[OCRE_MODULE_NAME_LEN]; // +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ocre.h" +#include "container.h" +#include "util/string_array.h" + +LOG_MODULE_REGISTER(container, CONFIG_OCRE_LOG_LEVEL); + +struct ocre_container { + char *id; + char *image; + const struct ocre_runtime_vtable *runtime; + bool detached; + ocre_container_status_t status; + pthread_mutex_t mutex; + sem_t sem_start; + pthread_cond_t cond_stop; + void *runtime_context; + pthread_t thread; + pthread_attr_t attr; + struct thread_info *tinfo; + char **argv; + char **envp; + int exit_code; +}; + +struct container_thread_params { + int (*func)(void *, sem_t *); + struct ocre_container *container; + sem_t *sem; +}; + +static void *container_thread(void *arg) +{ + int rc; + struct container_thread_params *params = arg; + struct ocre_container *container = params->container; + + /* Run the container */ + + int result = params->func(container->runtime_context, params->sem); + + /* Exited */ + + free(params); + + rc = pthread_mutex_lock(&container->mutex); + if (rc) { + LOG_ERR("Failed to lock mutex: rc=%d", rc); + return NULL; + } + + /* Here is the **only** place where we should set the status to EXITED */ + + container->status = OCRE_CONTAINER_STATUS_EXITED; + container->exit_code = result; + + LOG_INF("Container '%s' exited. Result is = %d", container->id, result); + + /* Notify any waiting threads */ + + rc = pthread_cond_broadcast(&container->cond_stop); + if (rc) { + LOG_WRN("Failed to broadcast conditional variable: rc=%d", rc); + } + + rc = pthread_mutex_unlock(&container->mutex); + if (rc) { + LOG_ERR("Failed to unlock mutex: rc=%d", rc); + return NULL; + } + + /* Cast result int to void pointer so we can return it from the thread */ + + return (void *)(intptr_t)result; +} + +static ocre_container_status_t ocre_container_status_locked(struct ocre_container *container) +{ + if (!container) { + LOG_ERR("Invalid container parameter"); + return OCRE_CONTAINER_STATUS_UNKNOWN; + } + + if (container->status == OCRE_CONTAINER_STATUS_EXITED) { + /* Need to join the thread to clean up resources and get exit status. + * pthread_join should not block here because the thread already exited. + * Here is the only place where we should call pthread_join. + * + * If we later have pthread_join somewhere else, we should not have it here. + */ + + int rc = pthread_join(container->thread, NULL); + if (rc) { + LOG_ERR("Failed to join thread: rc=%d", rc); + return OCRE_CONTAINER_STATUS_UNKNOWN; + } + + /* It looks like we should keep the attr available for the whole life of the thread */ + + rc = pthread_attr_destroy(&container->attr); + if (rc) { + LOG_ERR("Failed to destroy thread attributes: rc=%d", rc); + } + + /* Now the container is really stopped */ + + container->status = OCRE_CONTAINER_STATUS_STOPPED; + } + + return container->status; +} + +static char *ocre_find_best_matching_runtime(const char *image) +{ + static const char default_runtime[] = "wamr/wasip1"; + + if (!image) { + LOG_ERR("Image name cannot be NULL"); + return NULL; + } + + /* TODO: find the runtime engine from the image manifest, and return the appropriate one */ + + /* For now, we use "wamr/wasip1" as default */ + + return default_runtime; +} + +struct ocre_container *ocre_container_create(const char *img_path, const char *workdir, const char *runtime, + const char *container_id, bool detached, + const struct ocre_container_args *arguments) +{ + int rc; + const char **capabilities = NULL; + const char **mounts = NULL; + + if (!runtime) { + runtime = ocre_find_best_matching_runtime(img_path); + if (!runtime) { + LOG_ERR("Failed to find best matching runtime engine. Please specify a valid runtime engine."); + return NULL; + } + + LOG_INF("Selected runtime engine: %s", runtime); + } + + /* Check mounts parameters of format : + * Destination should be absolute path + * We do not like anything to be mounted at '/' + * We handle '/' with the 'filesystem' capability. + */ + + if (arguments && arguments->mounts) { + const char **mount = arguments->mounts; + + while (*mount) { + if (*mount[0] != '/') { + LOG_ERR("Invalid mount format: '%s': source must be absolute path", *mount); + return NULL; + } + + char *dst = strchr(*mount, ':'); + if (!dst) { + LOG_ERR("Invalid mount format: '%s': must be :", *mount); + return NULL; + } + + dst++; + + if (dst[0] != '/') { + LOG_ERR("Invalid mount format: '%s': destination must be absolute path", *mount); + return NULL; + } + + if (dst[1] == '\0') { + LOG_ERR("Invalid mount format: '%s': destination must not be '/'", *mount); + return NULL; + } + + mount++; + } + } + + struct ocre_container *container = malloc(sizeof(struct ocre_container)); + + if (!container) { + LOG_ERR("Failed to allocate memory: errno=%d", errno); + return NULL; + } + + memset(container, 0, sizeof(struct ocre_container)); + + /* Strip the image name from the path, just to make it look nicer */ + + const char *image = strrchr(img_path, '/'); + if (image) { + image++; + } else { + image = img_path; + } + + container->image = strdup(image); + if (!container->image) { + LOG_ERR("Failed to allocate memory for image: errno=%d", errno); + goto error_free; + } + + container->id = strdup(container_id); + if (!container->id) { + LOG_ERR("Failed to allocate memory for id: errno=%d", errno); + goto error_free; + } + + /* Duplicate the arguments */ + + if (arguments) { + capabilities = arguments->capabilities; + mounts = arguments->mounts; + + container->argv = string_array_deep_dup(arguments->argv); + container->envp = string_array_deep_dup(arguments->envp); + + if ((!container->argv && arguments->argv) || (!container->envp && arguments->envp)) { + goto error_free; + } + } + + container->runtime = ocre_get_runtime(runtime); + if (!container->runtime) { + LOG_ERR("Invalid runtime '%s'", runtime); + goto error_free; + } + + rc = pthread_mutex_init(&container->mutex, NULL); + if (rc) { + LOG_ERR("Failed to initialize mutex: rc=%d", rc); + goto error_free; + } + + rc = pthread_cond_init(&container->cond_stop, NULL); + if (rc) { + LOG_ERR("Failed to initialize stop conditional variable: rc=%d", rc); + goto error_mutex; + } + + rc = sem_init(&container->sem_start, 0, 0); + if (rc) { + LOG_ERR("Failed to initialize start semaphore: rc=%d, errno=%d", rc, errno); + goto error_mutex; + } + + container->runtime_context = + container->runtime->create(img_path, workdir, capabilities, (const char **)container->argv, + (const char **)container->envp, mounts); + if (!container->runtime_context) { + LOG_ERR("Failed to create container"); + goto error_cond; + } + + container->status = OCRE_CONTAINER_STATUS_CREATED; + + container->detached = detached; + + LOG_INF("Created container '%s' with runtime '%s' (path '%s')", container->id, runtime, img_path); + + return container; + +error_cond: + rc = sem_destroy(&container->sem_start); + if (rc) { + LOG_ERR("Failed to deinitialize start semaphore: rc=%d", rc); + } + + rc = pthread_cond_destroy(&container->cond_stop); + if (rc) { + LOG_ERR("Failed to deinitialize stop conditional variable: rc=%d", rc); + } + +error_mutex: + rc = pthread_mutex_destroy(&container->mutex); + if (rc) { + LOG_ERR("Failed to deinitialize mutex: rc=%d", rc); + } + +error_free: + string_array_free(container->argv); + string_array_free(container->envp); + + free(container->image); + free(container->id); + free(container); + + return NULL; +} + +int ocre_container_destroy(struct ocre_container *container) +{ + if (!container) { + LOG_ERR("Invalid arguments"); + return -1; + } + + ocre_container_status_t status = ocre_container_status_locked(container); + if (status != OCRE_CONTAINER_STATUS_STOPPED && status != OCRE_CONTAINER_STATUS_CREATED && + status != OCRE_CONTAINER_STATUS_ERROR) { + LOG_ERR("Cannot remove container '%s' because it is in use", container->id); + return -1; + } + + container->runtime->destroy(container->runtime_context); + + int rc; + rc = pthread_mutex_destroy(&container->mutex); + if (rc) { + LOG_ERR("Failed to deinitialize mutex: rc=%d", rc); + } + + rc = sem_destroy(&container->sem_start); + if (rc) { + LOG_ERR("Failed to deinitialize start semaphore: rc=%d", rc); + } + + rc = pthread_cond_destroy(&container->cond_stop); + if (rc) { + LOG_ERR("Failed to deinitialize stop conditional variable: rc=%d", rc); + } + + string_array_free(container->argv); + string_array_free(container->envp); + + LOG_INF("Removed container '%s'", container->id); + + free(container->id); + free(container->image); + free(container); + + return 0; +} + +int ocre_container_start(struct ocre_container *container) +{ + if (!container) { + LOG_ERR("Invalid arguments"); + return -1; + } + + int rc; + rc = pthread_mutex_lock(&container->mutex); + if (rc) { + LOG_ERR("Failed to lock mutex: rc=%d", rc); + goto error_status; + } + + ocre_container_status_t status = ocre_container_status_locked(container); + if (status != OCRE_CONTAINER_STATUS_CREATED && status != OCRE_CONTAINER_STATUS_STOPPED) { + LOG_ERR("Container '%s' is not ready to run", container->id); + goto error_mutex; + } + + rc = pthread_attr_init(&container->attr); + if (rc) { + LOG_ERR("Failed to initialize pthread attribute: rc=%d", rc); + goto error_mutex; + } + + struct container_thread_params *params; + params = malloc(sizeof(struct container_thread_params)); + if (!params) { + LOG_ERR("Failed to allocate memory (size=%zu) for thread parameters: errno=%d", + sizeof(struct container_thread_params), errno); + goto error_attr; + } + + memset(params, 0, sizeof(struct container_thread_params)); + params->container = container; + params->func = container->runtime->thread_execute; + params->sem = &container->sem_start; + container->status = OCRE_CONTAINER_STATUS_RUNNING; + + rc = pthread_create(&container->thread, &container->attr, container_thread, params); + if (rc) { + LOG_ERR("Failed to create thread: rc=%d", rc); + goto error_params; + } + + LOG_INF("Waiting for container '%s' to start", container->id); + + rc = pthread_mutex_unlock(&container->mutex); + if (rc) { + LOG_ERR("Failed to unlock mutex: rc=%d", rc); + goto error_status; + } + + rc = sem_wait(&container->sem_start); + if (rc) { + LOG_ERR("Failed to wait on start semaphore: rc=%d", rc); + goto error_status; + } + + LOG_INF("Started container '%s' on runtime '%s'", container->id, container->runtime->runtime_name); + + if (!container->detached) { + /* This will block until the container thread exits */ + + if (ocre_container_wait(container, NULL)) { + LOG_ERR("Failed to wait for container '%s'", container->id); + return -1; + } + } + + return 0; + +error_params: + free(params); + +error_attr: + rc = pthread_attr_destroy(&container->attr); + if (rc) { + LOG_ERR("Failed to destroy thread attributes: rc=%d", rc); + } + +error_mutex: + rc = pthread_mutex_unlock(&container->mutex); + if (rc) { + LOG_ERR("Failed to unlock mutex: rc=%d", rc); + } + +error_status: + LOG_INF("Setting container '%s' status to ERROR", container->id); + container->status = OCRE_CONTAINER_STATUS_ERROR; + + return -1; +} + +ocre_container_status_t ocre_container_get_status(struct ocre_container *container) +{ + if (!container) { + LOG_ERR("Invalid arguments"); + return OCRE_CONTAINER_STATUS_UNKNOWN; + } + + int rc; + rc = pthread_mutex_lock(&container->mutex); + if (rc) { + LOG_ERR("Failed to lock mutex: rc=%d", rc); + return OCRE_CONTAINER_STATUS_UNKNOWN; + } + + ocre_container_status_t status = ocre_container_status_locked(container); + + rc = pthread_mutex_unlock(&container->mutex); + if (rc) { + LOG_ERR("Failed to unlock mutex: rc=%d", rc); + return OCRE_CONTAINER_STATUS_UNKNOWN; + } + + return status; +} + +int ocre_container_stop(struct ocre_container *container) +{ + int ret = -1; + if (!container) { + LOG_ERR("Invalid container"); + return -1; + } + + int rc; + rc = pthread_mutex_lock(&container->mutex); + if (rc) { + LOG_ERR("Failed to lock mutex: rc=%d", rc); + return -1; + } + + if (container->status != OCRE_CONTAINER_STATUS_RUNNING) { + LOG_ERR("Container '%s' is not running", container->id); + goto unlock_mutex; + } + + if (!container->runtime->stop) { + LOG_ERR("Container '%s' does not support stop", container->id); + goto unlock_mutex; + } + + LOG_INF("Sending stop signal to container '%s'", container->id); + + ret = container->runtime->stop(container->runtime_context); + +unlock_mutex: + rc = pthread_mutex_unlock(&container->mutex); + if (rc) { + LOG_ERR("Failed to unlock mutex: rc=%d", rc); + } + + return ret; +} + +int ocre_container_kill(struct ocre_container *container) +{ + int ret = -1; + if (!container) { + LOG_ERR("Invalid container"); + return -1; + } + + int rc; + rc = pthread_mutex_lock(&container->mutex); + if (rc) { + LOG_ERR("Failed to lock mutex: rc=%d", rc); + return -1; + } + + if (container->status != OCRE_CONTAINER_STATUS_RUNNING) { + LOG_ERR("Container '%s' is not running", container->id); + goto unlock_mutex; + } + + ret = container->runtime->kill(container->runtime_context); + + LOG_INF("Sent kill signal to container '%s'", container->id); + +unlock_mutex: + rc = pthread_mutex_unlock(&container->mutex); + if (rc) { + LOG_ERR("Failed to unlock mutex: rc=%d", rc); + } + + return ret; +} + +int ocre_container_pause(struct ocre_container *container) +{ + int ret = -1; + if (!container) { + LOG_ERR("Invalid container"); + return -1; + } + + int rc; + rc = pthread_mutex_lock(&container->mutex); + if (rc) { + LOG_ERR("Failed to lock mutex: rc=%d", rc); + return -1; + } + + if (container->status != OCRE_CONTAINER_STATUS_RUNNING) { + LOG_ERR("Container '%s' is not running", container->id); + goto unlock_mutex; + } + + if (!container->runtime->pause) { + LOG_ERR("Container '%s' does not support pause", container->id); + goto unlock_mutex; + } + + LOG_INF("Sending pause signal to container '%s'", container->id); + + ret = container->runtime->pause(container->runtime_context); + + if (ret) { + LOG_ERR("Failed to pause container '%s': rc=%d", container->id, ret); + goto unlock_mutex; + } + + container->status = OCRE_CONTAINER_STATUS_PAUSED; + +unlock_mutex: + rc = pthread_mutex_unlock(&container->mutex); + if (rc) { + LOG_ERR("Failed to unlock mutex: rc=%d", rc); + } + + return ret; +} + +int ocre_container_unpause(struct ocre_container *container) +{ + int ret = -1; + if (!container) { + LOG_ERR("Invalid container"); + return -1; + } + + int rc; + rc = pthread_mutex_lock(&container->mutex); + if (rc) { + LOG_ERR("Failed to lock mutex: rc=%d", rc); + return -1; + } + + if (container->status != OCRE_CONTAINER_STATUS_PAUSED) { + LOG_ERR("Container '%s' is not paused", container->id); + goto unlock_mutex; + } + + if (!container->runtime->unpause) { + LOG_ERR("Container '%s' does not support unpause", container->id); + goto unlock_mutex; + } + + LOG_INF("Sending unpause signal to container '%s'", container->id); + + ret = container->runtime->unpause(container->runtime_context); + + if (ret) { + LOG_ERR("Failed to unpause container '%s': rc=%d", container->id, ret); + goto unlock_mutex; + } + + container->status = OCRE_CONTAINER_STATUS_RUNNING; + +unlock_mutex: + rc = pthread_mutex_unlock(&container->mutex); + if (rc) { + LOG_ERR("Failed to unlock mutex: rc=%d", rc); + } + + return ret; +} + +int ocre_container_wait(struct ocre_container *container, int *status) +{ + int ret = -1; + + if (!container) { + LOG_ERR("Invalid container"); + return -1; + } + + int rc; + rc = pthread_mutex_lock(&container->mutex); + if (rc) { + LOG_ERR("Failed to lock mutex: rc=%d", rc); + return -1; + } + + if (container->status == OCRE_CONTAINER_STATUS_UNKNOWN || container->status == OCRE_CONTAINER_STATUS_CREATED) { + ret = -1; + goto unlock_mutex; + } + + if (container->status == OCRE_CONTAINER_STATUS_RUNNING) { + LOG_INF("Container '%s' is running", container->id); + rc = pthread_cond_wait(&container->cond_stop, &container->mutex); + if (rc) { + LOG_ERR("Failed to wait on stop conditional variable: rc=%d", rc); + goto unlock_mutex; + } + } + + if (container->status == OCRE_CONTAINER_STATUS_EXITED) { + LOG_INF("Container '%s' was exited", container->id); + if (ocre_container_status_locked(container) != OCRE_CONTAINER_STATUS_STOPPED) { + LOG_ERR("Container '%s' status did not go from exited to stopped", container->id); + goto unlock_mutex; + } + } + + ret = 0; + + if (container->status == OCRE_CONTAINER_STATUS_STOPPED) { + LOG_INF("Container '%s' was stopped", container->id); + if (status) { + *status = container->exit_code; + } + } + +unlock_mutex: + rc = pthread_mutex_unlock(&container->mutex); + if (rc) { + LOG_ERR("Failed to unlock mutex: rc=%d", rc); + } + + return ret; +} + +const char *ocre_container_get_id(const struct ocre_container *container) +{ + if (!container) { + LOG_ERR("Invalid container or id"); + return NULL; + } + + return container->id; +} + +const char *ocre_container_get_image(const struct ocre_container *container) +{ + if (!container) { + LOG_ERR("Invalid container or id"); + return NULL; + } + + return container->image; +} diff --git a/src/ocre/container.h b/src/ocre/container.h new file mode 100644 index 00000000..eaf8120c --- /dev/null +++ b/src/ocre/container.h @@ -0,0 +1,15 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +struct ocre_container; + +struct ocre_container *ocre_container_create(const char *img_path, const char *workdir, const char *runtime, + const char *container_id, bool detached, + const struct ocre_container_args *arguments); +int ocre_container_destroy(struct ocre_container *container); diff --git a/src/ocre/context.c b/src/ocre/context.c new file mode 100644 index 00000000..5666f2ca --- /dev/null +++ b/src/ocre/context.c @@ -0,0 +1,449 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "uthash/utlist.h" + +#include +#include +#include + +#include "container.h" +#include "util/rm_rf.h" +#include "util/string_array.h" +#include "util/unique_random_id.h" + +#define RANDOM_ID_LEN 8 + +LOG_MODULE_REGISTER(context, CONFIG_OCRE_LOG_LEVEL); + +struct ocre_context { + pthread_mutex_t mutex; + const char *working_directory; + struct container_node *containers; +}; + +struct container_node { + struct ocre_container *container; + char *working_directory; + struct container_node *next; /* needed for singly- or doubly-linked lists */ +}; + +static int ocre_context_remove_container_locked(struct ocre_context *context, struct ocre_container *container) +{ + int rc; + struct container_node *node = NULL; + struct container_node *elt = NULL; + + LL_FOREACH_SAFE(context->containers, node, elt) + { + if (node->container == container) { + rc = ocre_container_destroy(container); + if (rc) { + LOG_ERR("Failed to destroy container: rc=%d", rc); + return -1; + } + +#if CONFIG_OCRE_FILESYSTEM + if (node->working_directory) { + rc = rm_rf(node->working_directory); + if (rc) { + LOG_ERR("Failed to remove container working directory '%s': rc=%d", + node->working_directory, rc); + return -1; + } + } +#endif + + LL_DELETE(context->containers, node); + + free(node->working_directory); + free(node); + + return 0; + } + } + + return -1; +} + +struct ocre_context *ocre_context_create(const char *workdir) +{ + int rc; + + struct ocre_context *context = malloc(sizeof(struct ocre_context)); + if (!context) { + LOG_ERR("Failed to allocate memory for context: errno=%d", errno); + return NULL; + } + + memset(context, 0, sizeof(struct ocre_context)); + + rc = pthread_mutex_init(&context->mutex, NULL); + if (rc) { + LOG_ERR("Failed to initialize context mutex: rc=%d", rc); + goto error; + } + + /* Set working directory */ + + context->working_directory = workdir; + + /* Initialize containers list */ + + context->containers = NULL; + + return context; + +error: + free(context); + + return NULL; +}; + +struct ocre_container *ocre_context_get_container_by_id_locked(const struct ocre_context *context, const char *id) +{ + struct container_node *node; + + LL_FOREACH(context->containers, node) + { + const char *container_id = ocre_container_get_id(node->container); + if (!container_id) { + LOG_ERR("Failed to get container ID"); + return NULL; + } + + if (!strcmp(container_id, id)) { + return node->container; + } + } + + return NULL; +} + +int ocre_context_destroy(struct ocre_context *context) +{ + struct container_node *node, *tmp; + + /* Send kill event to all containers */ + + LL_FOREACH(context->containers, node) + { + ocre_container_kill(node->container); + } + + /* Wait for all containers to exit */ + + LL_FOREACH(context->containers, node) + { + ocre_container_wait(node->container, NULL); + } + + /* Remove all containers */ + + LL_FOREACH_SAFE(context->containers, node, tmp) + { + ocre_context_remove_container_locked(context, node->container); + } + + int rc = pthread_mutex_destroy(&context->mutex); + if (rc) { + LOG_ERR("Failed to destroy context mutex: rc=%d", rc); + return -1; + } + + free(context); + + return 0; +}; + +struct ocre_container *ocre_context_create_container(struct ocre_context *context, const char *image, + const char *const runtime, const char *container_id, bool detached, + const struct ocre_container_args *arguments) +{ + const char *computed_container_id = NULL; + struct ocre_container *container = NULL; + struct container_node *node = NULL; + char *container_workdir = NULL; + char *image_path = NULL; + char random_id[RANDOM_ID_LEN]; + int rc; + + if (!context) { + LOG_ERR("Invalid context"); + return NULL; + } + + /* Check if the provided container ID is valid */ + + if (container_id && !ocre_is_valid_id(container_id)) { + LOG_ERR("Invalid characters in container ID '%s'. Valid are [a-z0-9_-.] (lowercase alphanumeric) and " + "cannot start with '.'", + container_id); + return NULL; + } + + /* Check if the provided image ID is valid */ + + if (!image || !ocre_is_valid_id(image)) { + LOG_ERR("Invalid characters in image ID '%s'. Valid are [a-z0-9_-.] (lowercase alphanumeric) and " + "cannot start with '.'", + image); + return NULL; + } + + rc = pthread_mutex_lock(&context->mutex); + if (rc) { + LOG_ERR("Failed to lock context mutex: rc=%d", rc); + return NULL; + } + + /* Container name checks */ + + if (!container_id) { + /* If no container ID is provided, generate a random one */ + + if (make_unique_random_container_id(context, random_id, RANDOM_ID_LEN)) { + LOG_ERR("Failed to generate random container ID"); + goto error; + } + + computed_container_id = random_id; + } else if (ocre_context_get_container_by_id_locked(context, container_id)) { + LOG_ERR("Container with ID '%s' already exists", container_id); + goto error; + } else { + computed_container_id = container_id; + } + + /* Allocate the node */ + + node = malloc(sizeof(struct container_node)); + if (!node) { + LOG_ERR("Failed to allocate memory for container node"); + goto error; + } + + memset(node, 0, sizeof(struct container_node)); + + /* Build the full path to the image */ + + image_path = malloc(strlen(context->working_directory) + strlen("/images/") + strlen(image) + 1); + if (!image_path) { + LOG_ERR("Failed to allocate memory for image path"); + goto error; + } + + strcpy(image_path, context->working_directory); + strcat(image_path, "/images/"); + strcat(image_path, image); + + // TODO: check if image exists + + /* Build the path to the working dir and create it */ + +#if CONFIG_OCRE_FILESYSTEM + if (arguments && string_array_lookup(arguments->capabilities, "filesystem")) { + container_workdir = malloc(strlen(context->working_directory) + strlen("/containers/") + + strlen(computed_container_id) + 1); + if (!container_workdir) { + LOG_ERR("Failed to allocate memory for working directory"); + goto error; + } + + snprintf(container_workdir, + strlen(context->working_directory) + strlen("/containers/") + strlen(computed_container_id) + + 1, + "%s/containers/%s", context->working_directory, computed_container_id); + + rc = mkdir(container_workdir, 0755); + if (rc) { + LOG_ERR("Failed to create working directory '%s': rc=%d", container_workdir, rc); + goto error; + } + } +#endif + + /* Create the container */ + + container = ocre_container_create(image_path, container_workdir, runtime, computed_container_id, detached, + arguments); + if (!container) { + LOG_ERR("Failed to create container %s: errno=%d", computed_container_id, errno); + goto error; + } + + /* Add the container to the context */ + + node->container = container; + node->working_directory = container_workdir; + + LL_APPEND(context->containers, node); + + goto success; + +error: + if (container_workdir) { + rc = rm_rf(container_workdir); + if (rc) { + LOG_ERR("Failed to remove container working directory '%s': rc=%d", container_workdir, rc); + } + } + + free(container_workdir); + + free(node); + + /* Fall-through on error */ + +success: + free(image_path); + + rc = pthread_mutex_unlock(&context->mutex); + if (rc) { + LOG_ERR("Failed to unlock context mutex: rc=%d", rc); + return NULL; + } + + return container; +} + +int ocre_context_remove_container(struct ocre_context *context, struct ocre_container *container) +{ + int rc = -1; + + if (!context || !container) { + LOG_ERR("Invalid arguments"); + return -1; + } + + rc = pthread_mutex_lock(&context->mutex); + if (rc) { + LOG_ERR("Failed to lock context mutex: rc=%d", rc); + return -1; + } + + rc = ocre_context_remove_container_locked(context, container); + if (rc) { + LOG_ERR("Failed to remove container: rc=%d", rc); + } + + int ret = pthread_mutex_unlock(&context->mutex); + if (ret) { + LOG_ERR("Failed to unlock context mutex: rc=%d", rc); + return -1; + } + + return rc; +} + +int ocre_context_get_container_count(struct ocre_context *context) +{ + int rc; + int count = 0; + struct container_node *node = NULL; + + if (!context) { + LOG_ERR("Invalid context"); + return -1; + } + + rc = pthread_mutex_lock(&context->mutex); + if (rc) { + LOG_ERR("Failed to lock context mutex: rc=%d", rc); + return -1; + } + + LL_COUNT(context->containers, node, count); + + rc = pthread_mutex_unlock(&context->mutex); + if (rc) { + LOG_ERR("Failed to unlock context mutex: rc=%d", rc); + return -1; + } + + return count; +} + +const char *ocre_context_get_working_directory(const struct ocre_context *context) +{ + if (!context) { + return NULL; + } + + /* We never change this, no need to lock */ + + return context->working_directory; +} + +int ocre_context_get_containers(struct ocre_context *context, struct ocre_container **containers, int max_size) +{ + int rc; + int count = 0; + struct container_node *node = NULL; + + if (!context || !containers) { + LOG_ERR("Invalid arguments"); + return -1; + } + + rc = pthread_mutex_lock(&context->mutex); + if (rc) { + LOG_ERR("Failed to lock context mutex: rc=%d", rc); + return -1; + } + + LL_FOREACH(context->containers, node) + { + if (count >= max_size) { + break; + } + + containers[count++] = node->container; + } + + rc = pthread_mutex_unlock(&context->mutex); + if (rc) { + LOG_ERR("Failed to unlock context mutex: rc=%d", rc); + return -1; + } + + return count; +} + +struct ocre_container *ocre_context_get_container_by_id(struct ocre_context *context, const char *id) +{ + int rc; + + if (!context || !id) { + LOG_ERR("Invalid arguments"); + return NULL; + } + + rc = pthread_mutex_lock(&context->mutex); + if (rc) { + LOG_ERR("Failed to lock context mutex: rc=%d", rc); + return NULL; + } + + struct ocre_container *ret = ocre_context_get_container_by_id_locked(context, id); + + rc = pthread_mutex_unlock(&context->mutex); + if (rc) { + LOG_ERR("Failed to unlock context mutex: rc=%d", rc); + return NULL; + } + + return ret; +} diff --git a/src/ocre/context.h b/src/ocre/context.h new file mode 100644 index 00000000..aeba5c3e --- /dev/null +++ b/src/ocre/context.h @@ -0,0 +1,12 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +struct ocre_context; + +struct ocre_context *ocre_context_create(const char *workdir); +int ocre_context_destroy(struct ocre_context *context); +struct ocre_container *ocre_context_get_container_by_id_locked(const struct ocre_context *context, const char *id); diff --git a/src/ocre/include/ocre/container.h b/src/ocre/include/ocre/container.h new file mode 100644 index 00000000..72540293 --- /dev/null +++ b/src/ocre/include/ocre/container.h @@ -0,0 +1,168 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef OCRE_CONTAINER_H +#define OCRE_CONTAINER_H + +/** + * @brief The possible status of a container + * + * This enum represents the possible status of a container. The container status are all side-effects from the + * container's lifecycle. + * + * The status OCRE_CONTAINER_STATUS_EXITED is a transient status that indicates the container has exited but we did not + * get the exit code yet. Upon calling ocre_container_wait(), the status will be updated to + * OCRE_CONTAINER_STATUS_STOPPED in case it was OCRE_CONTAINER_STATUS_EXITED. + */ +typedef enum { + OCRE_CONTAINER_STATUS_UNKNOWN = 0, /**< Status is unknown */ + OCRE_CONTAINER_STATUS_CREATED, /**< Container has been created. */ + OCRE_CONTAINER_STATUS_RUNNING, /**< Container is currently running. */ + OCRE_CONTAINER_STATUS_PAUSED, /**< Container is currently paused. */ + OCRE_CONTAINER_STATUS_EXITED, /**< Container has exited but we did not get the exit code yet. */ + OCRE_CONTAINER_STATUS_STOPPED, /**< Container has been stopped. */ + OCRE_CONTAINER_STATUS_ERROR, /**< An error occurred with the container. */ +} ocre_container_status_t; + +/** + * @class ocre_container + * @headerfile ocre.h + * @brief Ocre Container + * + * An opaque structure representing the container. It should be used to interact with Ocre Library and passed around as + * pointers. + * + * An Ocre Container is an application running on a runtime engine. + */ +struct ocre_container; + +/** + * @brief Start a container + * @memberof ocre_container + * + * Starts a container in the context. The container must be in the CREATED or STOPPED states. Calling this function on a + * container on other states will result in error. + * + * @param container A pointer to the container to start + * + * @return Zero on success, non-zero on failure + */ +int ocre_container_start(struct ocre_container *container); + +/** + * @brief Get the status of a container + * @memberof ocre_container + * + * Returns the status of a container in the context. + * + * @param container A pointer to the container to get the status of + * + * @return The status of the container. UNKNOWN on failure + */ +ocre_container_status_t ocre_container_get_status(struct ocre_container *container); + +/** + * @brief Get the ID of a container + * @memberof ocre_container + * + * Returns a pointer to a C string containing the ID of the container. This string is owned by the container and is + * valid until the container is destroyed. Should not be modified or freed by the caller. + * + * @param container A pointer to the container to get the ID of + * + * @return A pointer to the ID string, or NULL on failure + */ +const char *ocre_container_get_id(const struct ocre_container *container); + +/** + * @brief Get the image of a container + * @memberof ocre_container + * + * Returns a pointer to a C string containing the image of the container. This string is owned by the container and is + * valid until the container is destroyed. Should not be modified or freed by the caller. + * + * @param container A pointer to the container to get the image of + * + * @return A pointer to the image string, or NULL on failure + */ +const char *ocre_container_get_image(const struct ocre_container *container); + +/** + * @brief Pause a container + * @memberof ocre_container + * + * Pauses a container in the context. The container must be in the RUNNING state, otherwise it will fail. + * + * Note: This is currently not supported in WAMR containers. + * + * @param container A pointer to the container to pause + * + * @return Zero on success, non-zero on failure + */ +int ocre_container_pause(struct ocre_container *container); + +/** + * @brief Unpause a container + * @memberof ocre_container + * + * Unpauses a container in the context. The container must be in the PAUSED state, otherwise it will fail. + * + * Note: This is currently not supported in WAMR containers. + * + * @param container A pointer to the container to unpause + * + * @return Zero on success, non-zero on failure + */ +int ocre_container_unpause(struct ocre_container *container); + +/** + * @brief Gracefully stop a container + * @memberof ocre_container + * + * Stops a container in the context. The container must be in the RUNNING state, otherwise it will fail. + * + * The operation will signal the container to stop, and after a grace period, it will be forcefully terminated if not + * exited. + * + * Note: This is currently not supported in WAMR containers. + * + * @param container A pointer to the container to stop + * + * @return Zero on success, non-zero on failure + */ +int ocre_container_stop(struct ocre_container *container); + +/** + * @brief Forcefully terminate a container + * @memberof ocre_container + * + * Terminates a container in the context. The container must be in the RUNNING state, otherwise it will fail. + * + * @param container A pointer to the container to terminate + * + * @return Zero on success, non-zero on failure + */ +int ocre_container_kill(struct ocre_container *container); + +/** + * @brief Wait for a container to exit + * @memberof ocre_container + * + * If the container is STOPPED, returns the exit status of the container. + * + * If the container is RUNNING or PAUSED, waits for the container to exit. + * + * If the container is in other states, it will fail. + * + * @param container A pointer to the container to wait for + * @param[out] status A pointer to store the exit status of the container. Can be NULL + * + * @return Zero on success, non-zero on failure + */ +int ocre_container_wait(struct ocre_container *container, int *status); + +#endif /* OCRE_CONTAINER_H */ diff --git a/src/ocre/include/ocre/context.h b/src/ocre/include/ocre/context.h new file mode 100644 index 00000000..9f7f85e3 --- /dev/null +++ b/src/ocre/include/ocre/context.h @@ -0,0 +1,198 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef OCRE_CONTEXT_H +#define OCRE_CONTEXT_H + +#include + +struct ocre_container; + +/** + * @class ocre_context + * @headerfile ocre.h + * @brief Ocre Context + * + * The context is created by ocre_create_context() and destroyed by ocre_destroy_context(). It is used to interact with + * Ocre Library and passed around as pointers. + * + * The Ocre Context is a manager for a set of containers on their own runtime engines. + */ +struct ocre_context; + +/** + * @brief Container arguments + * @headerfile ocre.h + * + * A structure representing container arguments. These are passed to the runtime engine to define + * the container's behavior. + * + * The parameters are pointers to a NULL-terminated arrays of pointers standard (zero-terminated) character C strings. + */ +struct ocre_container_args { + /** @brief arguments to the container + * + * It is passed as parameters to the container's entry point. This is a NULL-terminated array of pointers to + * standard (zero-terminated) character C strings. + * + * Should not include the container's name. Any value here, starting from argv[0] will be passed to the + * container's entry point as argv[1] and so on. + * + * Example: + * @code + * const char *argv[] = { + * "arg1", + * "arg2", + * NULL + * }; + * @endcode + */ + const char **argv; + + /** @brief environment variables to the container + * + * It is passed as environment variables to the container's entry point. + * + * The values should be in the format "VAR=value". + * + * Example: + * @code + * const char *envp[] = { + * "VAR1=value1", + * "VAR2=value2", + * NULL + * }; + * @endcode + */ + const char **envp; + + /** @brief enabled permissions and features for the container + * + * It is used to enable specific container features and permissions. The possible values depends on the + * implementation. Check the full documentation for more details. + * + * This is a NULL-terminated array of pointers to standard (zero-terminated) character C strings. + * + * Example: + * @code + * const char *capabilities[] = { + * "filesystem", + * "networking", + * "ocre:api", + * NULL + * }; + * @endcode + */ + const char **capabilities; + + /** @brief Virtual mounts for the container. + * + * It is used to expose files. The format is in the form of "source:destination". + * + * This is a NULL-terminated array of pointers to standard (zero-terminated) character C strings and is in the + * form ":" + * + * The current implementation supports the following: + * - "/absolute/path/to/dir/on/host:/absolute/path/in/container" to mount a directory from the host into the + * container. + * + * Note: relative path, volumes and file-mounts are not yet supported. + * + * The Ocre Process should have the necessary permissions to access the source directory. + * + * Example: + * @code + * const char *mounts[] = { + * "/dev:/dev", + * NULL + * }; + * @endcode + */ + const char **mounts; +}; + +/** + * @brief Creates a new container within the given context + * @memberof ocre_context + * + * Creates a new container within the given context. The container will be created using the specified image, runtime, + * container ID, and arguments. If the container is detached, it will be run in the background. + * + * @param context A pointer to the context in which to create the container + * @param image The name of the image to use for the container + * @param runtime The name of the runtime engine to use for the container. Can be NULL, in which case Ocre will attempt + * to autoatically determine the best runtime for the image. + * @param container_id The ID to assign to the container. Can be NULL, in which case a random ID will be generated + * @param detached Whether the container should be detached (run in the background) or not + * @param arguments The container arguments to pass to the container. Can be NULL + * + * @return A pointer to the newly created container, or NULL on failure + */ +struct ocre_container *ocre_context_create_container(struct ocre_context *context, const char *image, + const char *const runtime, const char *container_id, bool detached, + const struct ocre_container_args *arguments); + +/** + * @brief Get a container by its ID + * @memberof ocre_context + * + * @param context A pointer to the context in which to get the container. + * @param id The ID of the container to get. + * + * @return A pointer to the container, or NULL if not found + */ +struct ocre_container *ocre_context_get_container_by_id(struct ocre_context *context, const char *id); + +/** + * @brief Remove a container + * @memberof ocre_context + * + * @param context A pointer to the context in which to remove the container. + * @param container A pointer to the container to remove. + * + * @return 0 on success, non-zero on failure + */ +int ocre_context_remove_container(struct ocre_context *context, struct ocre_container *container); + +/** + * @brief Get the number of containers in the context + * @memberof ocre_context + * + * @param context A pointer to the context in which to get the number of containers + * + * @return The number of containers in the context + */ +int ocre_context_get_container_count(struct ocre_context *context); + +/** + * @brief List containers in the context + * @memberof ocre_context + * + * This function will populate the provided array with pointers to the containers, up to the maximum size. + * + * @param context A pointer to the context in which to list containers + * @param[out] containers A pointer to an array of pointers to containers + * @param[in] max_size The maximum size of the array + * + * @return The number of containers listed, negative on failure + */ +int ocre_context_get_containers(struct ocre_context *context, struct ocre_container **containers, int max_size); + +/** + * @brief Get the working directory of the context + * @memberof ocre_context + * + * Returns a pointer to a C string containing the working directory. This string is owned by the context and is valid + * until the context is destroyed. Should not be modified or freed by the caller. + * + * @param context A pointer to the context in which to get the working directory + * + * @return A pointer to the working directory string, or NULL on failure + */ +const char *ocre_context_get_working_directory(const struct ocre_context *context); + +#endif /* OCRE_CONTEXT_H */ diff --git a/src/ocre/include/ocre/library.h b/src/ocre/include/ocre/library.h new file mode 100644 index 00000000..eaa56210 --- /dev/null +++ b/src/ocre/include/ocre/library.h @@ -0,0 +1,103 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef OCRE_LIBRARY_H +#define OCRE_LIBRARY_H + +#include + +/** + * @brief Build configuration of the Ocre Library + * @headerfile ocre.h + * + * There should only be only one instance of this structure in the program. And it must be in constant read-only memory. + * It is set at build-time and should only be read-only to the user. + */ +struct ocre_config { + const char *version; /**< Version of the Ocre Library */ + const char *commit_id; /**< Commit ID of the build tree */ + const char *build_info; /**< Host build information */ + const char *build_date; /**< Build date */ +}; + +/** + * @brief The instance of configuration of the Ocre Library is constant and compiled-in. + */ +extern const struct ocre_config ocre_build_configuration; + +/** + * @class ocre_context + * @headerfile ocre.h + * @brief Ocre Context + * + * The context is created by ocre_create_context() and destroyed by ocre_destroy_context(). It is used to interact with + * Ocre Library and passed around as pointers. + * + * The Ocre Context is a manager for a set of containers on their own runtime engines. + */ +struct ocre_context; + +/** + * @brief Initialize the Ocre Library and register runtime engines + * + * This function initializes the Ocre Library and register optional additional runtime engines. It must be called + * exactly once before any other Ocre Library functions are used. + * + * @param vtable A pointer to a NULL-terminated Array of runtime engine vtables to register. Can be NULL. + * + * @return 0 on success, non-zero on failure + */ +int ocre_initialize(const struct ocre_runtime_vtable *const vtable[]); + +/** + * @brief Creates a new Ocre Context + * + * Creates a new Ocre Context with the specified working directory. + * + * Creating multiple Ocre Contexts is allowed, but each context must have its own working directory. Using the same + * working directory for multiple contexts will result in an error. + * + * @param workdir The working directory for the new context. Absolute path, NULL for default. + * + * @return a pointer to the newly created Ocre Context on success, NULL on failure + */ +struct ocre_context *ocre_create_context(const char *workdir); + +/** + * @brief Destroys an Ocre Context + * + * Destroys an Ocre Context. If there are any running containers, they will be killed and removed. + * + * @param context A pointer to the Ocre Context to destroy. + * + * @return 0 on success, non-zero on failure + */ +int ocre_destroy_context(struct ocre_context *context); + +/** + * @brief Deinitializes the Ocre Library + * + * Deinitializes the Ocre Library, destroying all contexts and containers. + * + * Any errors will be ignored. + */ +void ocre_deinitialize(void); + +/** + * @brief Check if a container or image ID is valid + * @memberof ocre_context + * + * Checks if a container or image ID is valid. A valid container or image ID must not be NULL, empty, or start with a + * dot '.'. It can only contain alphanumeric characters, dots, underscores, and hyphens. + * + * @param id A pointer to the container or image ID to check + * + * @return Zero if invalid, 1 if valid + */ +int ocre_is_valid_id(const char *id); + +#endif /* OCRE_LIBRARY_H */ diff --git a/src/ocre/include/ocre/ocre.h b/src/ocre/include/ocre/ocre.h new file mode 100644 index 00000000..2e13a6b7 --- /dev/null +++ b/src/ocre/include/ocre/ocre.h @@ -0,0 +1,58 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file ocre.h + * + * Public Interface of Ocre Runtime Library. + * + * This header define the interfaces of general purpose container management in Ocre. + */ + +#ifndef OCRE_H +#define OCRE_H + +/** + * @mainpage Ocre Runtime Library Index + * # Ocre Runtime Library + * + * Ocre is a runtime library for managing containers. It provides a set of APIs for creating, managing, and controlling + * containers. It includes a WebAssembly runtime engine that can be used to run WebAssembly modules. + * + * This is just the auto-generated Doxygen documentation. Please refer to the full documentation and our website for + * more information and detailed usage guidelines. + * + * # Public User API + * + * Used to control Ocre Library, manage contexts and containers. + * + * ## Ocre Library Control + * + * Used to initialize Ocre Runtime Library and create contexts. + * + * See ocre.h file documentation. + * + * ## Ocre Context Control: used to create and remove contexts. + * + * See ocre_context class for documentation. + * + * ## Ocre Container Control: used to manage containers. + * + * See ocre_container class for documentation. + * + * # Runtime Engine Virtual Table + * + * Used to add custom runtime engines to the Ocre Runtime Library. + * + * See ocre_runtime_vtable class for documentation. + */ + +#include +#include +#include + +#endif /* OCRE_H */ diff --git a/src/ocre/ocre.c b/src/ocre/ocre.c new file mode 100644 index 00000000..6077a1c1 --- /dev/null +++ b/src/ocre/ocre.c @@ -0,0 +1,480 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "context.h" +#include "util/rm_rf.h" +#include "uthash/utlist.h" + +#include "commit_id.h" +#include "version.h" + +LOG_MODULE_REGISTER(ocre, CONFIG_OCRE_LOG_LEVEL); + +/* Constant build information */ + +const struct ocre_config ocre_build_configuration = { + .build_info = OCRE_BUILD_HOST_INFO, + .version = OCRE_VERSION_STRING, + .commit_id = GIT_COMMIT_ID, + .build_date = OCRE_BUILD_DATE, +}; + +/* List of runtimes */ + +struct runtime_node { + const struct ocre_runtime_vtable *runtime; + struct runtime_node *next; +}; + +struct context_node { + struct ocre_context *context; + char *working_directory; + struct context_node *next; +}; + +static struct runtime_node *runtimes = NULL; +static struct context_node *contexts = NULL; +static pthread_mutex_t contexts_mutex = PTHREAD_MUTEX_INITIALIZER; + +static int create_dir_if_not_exists(const char *path) +{ + int rc; + struct stat st; + + rc = stat(path, &st); + if (rc && errno == ENOENT) { + LOG_INF("Directory '%s' does not exist, creating...", path); + rc = mkdir(path, 0755); + if (rc) { + LOG_ERR("Failed to create directory '%s': errno=%d", path, errno); + return -1; + } + } else if (!rc && !S_ISDIR(st.st_mode)) { + LOG_ERR("Path '%s' is not a directory", path); + return -1; + } else if (!rc && S_ISDIR(st.st_mode)) { + LOG_INF("Directory '%s' already exists", path); + } + + return 0; +} + +static int populate_context_workdir(const char *working_directory) +{ + int rc = -1; + char *containers_path = NULL; + char *images_path = NULL; + + rc = create_dir_if_not_exists(working_directory); + if (rc) { + LOG_ERR("Failed to create Ocre working directory '%s': errno=%d", working_directory, errno); + return -1; + } + + containers_path = malloc(strlen(working_directory) + strlen("/containers") + 1); + if (!containers_path) { + LOG_ERR("Failed to allocate memory for container workdir path"); + goto finish; + } + + sprintf(containers_path, "%s/containers", working_directory); + + rc = create_dir_if_not_exists(containers_path); + if (rc) { + LOG_ERR("Failed to create Ocre containers directory '%s': errno=%d", containers_path, errno); + goto finish; + } + + images_path = malloc(strlen(working_directory) + strlen("/images") + 1); + if (!images_path) { + LOG_ERR("Failed to allocate memory for container workdir path"); + goto finish; + } + + sprintf(images_path, "%s/images", working_directory); + + rc = create_dir_if_not_exists(images_path); + if (rc) { + LOG_ERR("Failed to create Ocre images directory '%s': errno=%d", images_path, errno); + goto finish; + } + +finish: + free(containers_path); + free(images_path); + + return rc; +} + +#if CONFIG_OCRE_FILESYSTEM +static int delete_container_workdirs(const char *working_directory) +{ + int ret = -1; + DIR *d = NULL; + const struct dirent *dir = NULL; + + char *containers_path = malloc(strlen(working_directory) + strlen("/containers") + 1); + if (!containers_path) { + LOG_ERR("Failed to allocate memory for container workdir path"); + return -1; + } + + sprintf(containers_path, "%s/containers", working_directory); + + d = opendir(containers_path); + if (!d) { + fprintf(stderr, "Failed to open directory '%s'\n", containers_path); + goto finish; + } + + while ((dir = readdir(d)) != NULL) { + if (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")) { + continue; + } + + char *container_workdir_path = malloc(strlen(containers_path) + strlen(dir->d_name) + 2); + if (!container_workdir_path) { + fprintf(stderr, "Failed to allocate memory for container workdir path\n"); + goto finish; + } + + strcpy(container_workdir_path, containers_path); + strcat(container_workdir_path, "/"); + strcat(container_workdir_path, dir->d_name); + + if (rm_rf(container_workdir_path)) { + fprintf(stderr, "Failed to remove container workdir '%s'\n", container_workdir_path); + free(container_workdir_path); + goto finish; + } + + free(container_workdir_path); + } + + ret = 0; + +finish: + if (d) { + closedir(d); + } + + free(containers_path); + + return ret; +} +#endif + +static int ocre_destroy_context_locked(struct ocre_context *context) +{ + int rc; + struct context_node *node = NULL; + struct context_node *elt = NULL; + + LL_FOREACH_SAFE(contexts, node, elt) + { + if (node->context == context) { + rc = ocre_context_destroy(context); + if (rc) { + LOG_ERR("Failed to destroy context: rc=%d", rc); + return rc; + } + + LL_DELETE(contexts, node); + + free(node->working_directory); + free(node); + + return 0; + } + } + + return -1; +} + +struct ocre_context *ocre_create_context(const char *workdir) +{ + int rc; + struct context_node *add = NULL; + struct context_node *elt; + struct ocre_context *context = NULL; + + if (!workdir) { + workdir = CONFIG_OCRE_DEFAULT_WORKING_DIRECTORY; + LOG_INF("Using default working directory: %s", workdir); + } + + rc = pthread_mutex_lock(&contexts_mutex); + if (rc) { + LOG_ERR("Failed to lock mutex: rc=%d", rc); + return NULL; + } + + LL_FOREACH(contexts, elt) + { + if (!strcmp(elt->working_directory, workdir)) { + LOG_INF("Context already exists for working directory: %s", workdir); + goto error; + } + } + + add = malloc(sizeof(struct context_node)); + if (!add) { + LOG_ERR("Failed to allocate memory for context node: errno=%d", errno); + goto error; + } + + memset(add, 0, sizeof(struct context_node)); + + add->working_directory = strdup(workdir); + if (!add->working_directory) { + LOG_ERR("Failed to allocate memory for working directory: errno=%d", errno); + goto error; + } + + /* Initialize working directory */ + + rc = populate_context_workdir(add->working_directory); + if (rc) { + LOG_ERR("Failed to populate Ocre working directory: rc=%d", rc); + goto error; + } + +#if CONFIG_OCRE_FILESYSTEM + delete_container_workdirs(add->working_directory); +#endif + + context = ocre_context_create(add->working_directory); + if (!context) { + LOG_ERR("Failed to create Ocre context"); + goto error; + } + + add->context = context; + + LL_APPEND(contexts, add); + + goto finish; + +error: + if (add) { + free(add->working_directory); + } + + free(add); + +finish: + rc = pthread_mutex_unlock(&contexts_mutex); + if (rc) { + LOG_ERR("Failed to unlock mutex: rc=%d", rc); + } + + return context; +}; + +int ocre_destroy_context(struct ocre_context *context) +{ + int rc = -1; + + if (!context) { + LOG_ERR("Invalid context"); + return -1; + } + + rc = pthread_mutex_lock(&contexts_mutex); + if (rc) { + LOG_ERR("Failed to lock mutex: rc=%d", rc); + return -1; + } + + rc = ocre_destroy_context_locked(context); + if (rc) { + LOG_ERR("Failed to destroy context: rc=%d", rc); + return -1; + } + + rc = pthread_mutex_unlock(&contexts_mutex); + if (rc) { + LOG_ERR("Failed to unlock mutex: rc=%d", rc); + return -1; + } + + return rc; +}; + +int ocre_initialize(const struct ocre_runtime_vtable *const vtable[]) +{ + LOG_INF("Ocre version %s", ocre_build_configuration.version); + LOG_INF("Commit ID: %s", ocre_build_configuration.commit_id); + LOG_INF("Build information: %s", ocre_build_configuration.build_info); + LOG_INF("Build date: %s", ocre_build_configuration.build_date); + + /* Reinitialize the static variables */ + + runtimes = NULL; + contexts = NULL; + + int rc = pthread_mutex_init(&contexts_mutex, NULL); + if (rc) { + LOG_ERR("Failed to initialize Ocre mutex: rc=%d", rc); + return -1; + } + + /* Add WAMR runtime to the list */ + + struct runtime_node *wamr = malloc(sizeof(struct runtime_node)); + if (!wamr) { + LOG_ERR("Failed to allocate memory for WAMR node"); + return -1; + } + + memset(wamr, 0, sizeof(struct runtime_node)); + wamr->runtime = &wamr_vtable; + + LL_APPEND(runtimes, wamr); + + /* Add extra runtimes */ + + if (vtable) { + for (int i = 0; vtable[i] != NULL; i++) { + struct runtime_node *elt; + LL_FOREACH(runtimes, elt) + { + if (!strcmp(elt->runtime->runtime_name, vtable[i]->runtime_name)) { + LOG_ERR("Runtime '%s' already registered", vtable[i]->runtime_name); + goto error; + } + } + + struct runtime_node *add = malloc(sizeof(struct runtime_node)); + if (!add) { + LOG_ERR("Failed to allocate memory for runtime node"); + goto error; + } + + memset(add, 0, sizeof(struct runtime_node)); + add->runtime = vtable[i]; + + LL_APPEND(runtimes, add); + + LOG_INF("Registered '%s'", vtable[i]->runtime_name); + } + } + + /* Initialize all runtimes in the list */ + + struct runtime_node *elt, *tmp; + + LL_FOREACH(runtimes, elt) + { + if (elt->runtime->init && elt->runtime->init()) { + LOG_INF("Failed to initialize '%s'", elt->runtime->runtime_name); + ocre_deinitialize(); + return -1; + } + + LOG_INF("Initialized '%s'", elt->runtime->runtime_name); + } + + return 0; + +error: + LL_FOREACH_SAFE(runtimes, elt, tmp) + { + LL_DELETE(runtimes, elt); + + free(elt); + } + + return -1; +} + +const struct ocre_runtime_vtable *ocre_get_runtime(const char *name) +{ + if (!name) { + return NULL; + } + + struct runtime_node *elt; + + LL_FOREACH(runtimes, elt) + { + if (!strcmp(elt->runtime->runtime_name, name)) { + return elt->runtime; + } + } + + return NULL; +} + +void ocre_deinitialize(void) +{ + struct context_node *c_node, *c_tmp; + struct runtime_node *r_elt, *r_tmp; + + /* Destroy all contexts */ + + LL_FOREACH_SAFE(contexts, c_node, c_tmp) + { + if (ocre_destroy_context_locked(c_node->context)) { + LOG_ERR("Failed to destroy context %p", c_node->context); + } + } + + /* Deinitialize all runtimes */ + + LL_FOREACH_SAFE(runtimes, r_elt, r_tmp) + { + if (r_elt->runtime->deinit && r_elt->runtime->deinit()) { + LOG_INF("Failed to deinitialize '%s'", r_elt->runtime->runtime_name); + } + + LL_DELETE(runtimes, r_elt); + + free(r_elt); + } +} + +int ocre_is_valid_id(const char *id) +{ + /* Cannot be NULL */ + + if (!id) { + return 0; + } + + /* Cannot be empty or start with a dot '.' */ + + if (id[0] == '\0' || id[0] == '.') { + return 0; + } + + /* Can only contain alphanumeric characters, dots, underscores, and hyphens */ + + for (size_t i = 0; i < strlen(id); i++) { + if ((isalnum(id[i]) && islower(id[i])) || id[i] == '.' || id[i] == '_' || id[i] == '-') { + continue; + } + + return 0; + } + + return 1; +} diff --git a/src/ocre/ocre.h b/src/ocre/ocre.h index 0960403b..ce695846 100644 --- a/src/ocre/ocre.h +++ b/src/ocre/ocre.h @@ -1,25 +1,10 @@ /** - * @copyright Copyright © contributors to Project Ocre, + * @copyright Copyright (c) contributors to Project Ocre, * which has been established as Project Ocre a Series of LF Projects, LLC * * SPDX-License-Identifier: Apache-2.0 */ -#ifndef OCRE_H -#define OCRE_H +#include -#include "ocre_core_external.h" - -#ifdef CONFIG_OCRE_LOG_DEBUG -#define OCRE_LOG_LEVEL LOG_LEVEL_DBG -#elif CONFIG_OCRE_LOG_ERR -#define OCRE_LOG_LEVEL LOG_LEVEL_ERR -#elif CONFIG_OCRE_LOG_WARN -#define OCRE_LOG_LEVEL LOG_LEVEL_WRN -#elif CONFIG_OCRE_LOG_INF -#define OCRE_LOG_LEVEL LOG_LEVEL_INF -#else -#define OCRE_LOG_LEVEL LOG_LEVEL_NONE -#endif - -#endif +const struct ocre_runtime_vtable *ocre_get_runtime(const char *name); diff --git a/src/ocre/ocre_container_runtime/ocre_container_runtime.c b/src/ocre/ocre_container_runtime/ocre_container_runtime.c deleted file mode 100644 index 62a796ca..00000000 --- a/src/ocre/ocre_container_runtime/ocre_container_runtime.c +++ /dev/null @@ -1,144 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include "ocre_core_external.h" -#include "../components/container_supervisor/cs_sm.h" -#include "../components/container_supervisor/cs_sm_impl.h" -// WAMR includes -// #include "coap_ext.h" -#include "../api/ocre_api.h" - -#include -#include - -LOG_MODULE_DECLARE(ocre_cs_component, OCRE_LOG_LEVEL); - -#include "ocre_container_runtime.h" - -ocre_container_runtime_status_t ocre_container_runtime_init(ocre_cs_ctx *ctx, ocre_container_init_arguments_t *args) -{ - // Zeroing the context - if (CS_runtime_init(ctx, args) != RUNTIME_STATUS_INITIALIZED) { - LOG_ERR("Failed to initialize container runtime"); - return RUNTIME_STATUS_ERROR; - } - - CS_ctx_init(ctx); - ctx->download_count = 0; - - start_ocre_cs_thread(ctx); - core_sleep_ms(1000); - return RUNTIME_STATUS_INITIALIZED; -} - -ocre_container_status_t ocre_container_runtime_destroy(void) -{ - wasm_runtime_destroy(); - destroy_ocre_cs_thread(); - return RUNTIME_STATUS_DESTROYED; -} - -ocre_container_status_t ocre_container_runtime_create_container(ocre_cs_ctx *ctx, ocre_container_data_t *container_data, - int *container_id, ocre_container_runtime_cb callback) -{ - int i; - uint8_t validity_flag = false; - // Find available slot for new container - for (i = 0; i < CONFIG_MAX_CONTAINERS; i++) { - if ((ctx->containers[i].container_runtime_status == CONTAINER_STATUS_UNKNOWN) || - (ctx->containers[i].container_runtime_status == CONTAINER_STATUS_DESTROYED)) { - *container_id = i; - ctx->containers[i].container_ID = i; - ctx->containers[i].container_runtime_status = CONTAINER_STATUS_RESERVED; - validity_flag = true; - break; - } - } - - if (validity_flag == false) { - LOG_ERR("No available slots, unable to create container"); - return CONTAINER_STATUS_ERROR; - } - LOG_INF("Request to create new container in slot: %d", *container_id); - - struct ocre_message event = {.event = EVENT_CREATE_CONTAINER}; - ocre_container_data_t Data; - event.containerId = *container_id; - Data = *container_data; - ctx->containers[*container_id].ocre_container_data = Data; - ctx->containers[*container_id].ocre_runtime_arguments.module_inst = NULL; - ctx->download_count++; - - ocre_component_send(&ocre_cs_component, &event); - return CONTAINER_STATUS_CREATED; -} - -ocre_container_status_t ocre_container_runtime_run_container(int container_id, ocre_container_runtime_cb callback) -{ - if (container_id < 0 || container_id >= CONFIG_MAX_CONTAINERS) { - LOG_ERR("Invalid container ID: %d", container_id); - return CONTAINER_STATUS_ERROR; - } - - LOG_INF("Request to run container in slot:%d", container_id); - struct ocre_message event = {.event = EVENT_RUN_CONTAINER}; - event.containerId = container_id; - ocre_component_send(&ocre_cs_component, &event); - return CONTAINER_STATUS_RUNNING; -} - -ocre_container_status_t ocre_container_runtime_get_container_status(ocre_cs_ctx *ctx, int container_id) -{ - if (container_id < 0 || container_id >= CONFIG_MAX_CONTAINERS) { - LOG_ERR("Invalid container ID: %d", container_id); - return CONTAINER_STATUS_ERROR; - } - - return ctx->containers[container_id].container_runtime_status; -} - -ocre_container_status_t ocre_container_runtime_stop_container(int container_id, ocre_container_runtime_cb callback) -{ - if (container_id < 0 || container_id >= CONFIG_MAX_CONTAINERS) { - LOG_ERR("Invalid container ID: %d", container_id); - return CONTAINER_STATUS_ERROR; - } - LOG_INF("Request to stop container in slot: %d", container_id); - struct ocre_message event = {.event = EVENT_STOP_CONTAINER}; - event.containerId = container_id; - ocre_component_send(&ocre_cs_component, &event); - return CONTAINER_STATUS_STOPPED; -} - -ocre_container_status_t ocre_container_runtime_destroy_container(ocre_cs_ctx *ctx, int container_id, - ocre_container_runtime_cb callback) -{ - if (container_id < 0 || container_id >= CONFIG_MAX_CONTAINERS) { - LOG_ERR("Invalid container ID: %d", container_id); - return CONTAINER_STATUS_ERROR; - } - LOG_INF("Request to destroy container in slot: %d", container_id); - struct ocre_message event = {.event = EVENT_DESTROY_CONTAINER}; - event.containerId = container_id; - ocre_component_send(&ocre_cs_component, &event); - return CONTAINER_STATUS_DESTROYED; -} - -ocre_container_status_t ocre_container_runtime_restart_container(ocre_cs_ctx *ctx, int container_id, - ocre_container_runtime_cb callback) -{ - if (container_id < 0 || container_id >= CONFIG_MAX_CONTAINERS) { - LOG_ERR("Invalid container ID: %d", container_id); - return CONTAINER_STATUS_ERROR; - } - LOG_INF("Request to restart container in slot: %d", container_id); - struct ocre_message event = {.event = EVENT_RESTART_CONTAINER}; - event.containerId = container_id; - ocre_component_send(&ocre_cs_component, &event); - return ctx->containers[container_id].container_runtime_status; -} diff --git a/src/ocre/ocre_container_runtime/ocre_container_runtime.h b/src/ocre/ocre_container_runtime/ocre_container_runtime.h deleted file mode 100644 index 9ecd9e72..00000000 --- a/src/ocre/ocre_container_runtime/ocre_container_runtime.h +++ /dev/null @@ -1,212 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef OCRE_CONTAINER_RUNTIME_H -#define OCRE_CONTAINER_RUNTIME_H - -#include - -#include "ocre_core_external.h" -#include "wasm_export.h" - -#define OCRE_CR_DEBUG_ON 0 // Debug flag for container runtime (0: OFF, 1: ON) -#define FILE_PATH_MAX 256 // Maximum file path length -#define OCRE_CR_INIT_TIMEOUT 500 // Timeout to wait for the container registry to initialize - -/** - * @brief Structure containing the runtime arguments for a container runtime. - */ -typedef struct ocre_runtime_arguments_t { - uint32_t size; ///< Size of the buffer. - char *buffer; ///< Pointer to the buffer containing the WASM module. - char error_buf[128]; ///< Buffer to store error messages. - wasm_module_t module; ///< Handle to the loaded WASM module. - wasm_module_inst_t module_inst; ///< Handle to the instantiated WASM module. - wasm_function_inst_t func; ///< Handle to the function to be executed within the WASM module. - uint32_t stack_size; ///< Stack size for the WASM module. - uint32_t heap_size; ///< Heap size for the WASM module. -} ocre_runtime_arguments_t; - -/** - * @brief Enum representing the permission types for containers. - * NOT USED YET - */ -typedef enum { - OCRE_CONTAINER_PERM_READ_ONLY, ///< Container has read-only permissions. - OCRE_CONTAINER_PERM_READ_WRITE, ///< Container has read and write permissions. - OCRE_CONTAINER_PERM_EXECUTE ///< Container has execute permissions. -} ocre_container_permissions_t; - -/** - * @brief Enum representing the possible status of the container runtime - */ -typedef enum { - RUNTIME_STATUS_UNKNOWN, ///< Status is unknown. - RUNTIME_STATUS_INITIALIZED, ///< Runtime has been initialized. - RUNTIME_STATUS_DESTROYED, ///< Runtime has been destroyed - RUNTIME_STATUS_ERROR ///< An error occurred with the container. -} ocre_container_runtime_status_t; - -/** - * @brief Enum representing the possible status of a container. - */ -typedef enum { - CONTAINER_STATUS_UNKNOWN, ///< Status is unknown. - CONTAINER_STATUS_RESERVED, ///< Container slot has been reserved. - CONTAINER_STATUS_CREATED, ///< Container has been created. - CONTAINER_STATUS_RUNNING, ///< Container is currently running. - CONTAINER_STATUS_STOPPED, ///< Container has been stopped. - CONTAINER_STATUS_DESTROYED, ///< Container has been destroyed. - CONTAINER_STATUS_UNRESPONSIVE, ///< Container is unresponsive. -> For Healthcheck - CONTAINER_STATUS_ERROR, ///< An error occurred with the container. -} ocre_container_status_t; - -typedef struct ocre_container_runtime_init_arguments_t { - uint32_t default_stack_size; ///< Stack size for the WASM module. - uint32_t default_heap_size; ///< Heap size for the WASM module. - int maximum_containers; ///< Maximum number of containers allowed. - NativeSymbol *ocre_api_functions; -} ocre_container_init_arguments_t; - -/** - * @brief Structure representing the data associated with a container. - */ -typedef struct ocre_container_data_t { - char name[OCRE_MODULE_NAME_LEN]; // - -#include - -#include "ocre_sensors.h" - - - -// Define the callback function to handle trigger events - -void my_callback(ocre_sensor_handle_t sensor_handle, sensor_channel_t channel, const ocre_sensor_value *data, void *ptr) { - -printf("Trigger event on sensor %d, channel %d\n", sensor_handle.id, channel); - -printf("Sensor data: Integer = %d, Floating = %d\n", data->integer, data->floating); - -} - - - -// Main function demonstrating the use of the Ocre Sensors API - -void main(void) { - - ocre_sensors_status_t status; - - ocre_sensor_t sensors[MAX_SENSORS]; // Array to hold discovered sensors - - ocre_sensor_handle_t sensor_handle; // Handle for the sensor to be used - - int subscription_id; // ID for the trigger subscription - - - -// Step 1: Initialize the sensors environment - - status = ocre_sensors_init(); - - if (status != SENSOR_API_STATUS_INITIALIZED) { - - printf("Failed to initialize sensors. Status: %d\n", status); - - return; - - } - - - -// Step 2: Discover available sensors - - status = ocre_sensors_discover_sensors(sensors); - - if (status != SENSOR_API_STATUS_INITIALIZED) { - - printf("Failed to discover sensors. Status: %d\n", status); - - return; - - } - - - -// Step 3: Open a channel for the first discovered sensor - - sensor_handle = sensors[0].handle; // Assuming at least one sensor is available - - status = ocre_sensors_open_channel(&sensor_handle); - - if (status != 0) { - - printf("Failed to open sensor channel. Status: %d\n", status); - - return; - - } - - - -// Step 4: Read a sample from the sensor - - ocre_sensors_sample_t sample = sensor_read_sample(&sensor_handle); - - sensor_channel_t temperature = sensor_get_channel(sample, SENSOR_CHANNEL_TEMPERATURE); - - printf("Current temperature: %d\n", temperature); - - - -// Step 5: Set a trigger on the temperature channel - -status = ocre_sensors_set_trigger(sensor_handle, SENSOR_CHANNEL_TEMPERATURE, DATA_READY, my_callback, &subscription_id); - - if (status != 0) { - - printf("Failed to set trigger. Status: %d\n", status); - - return; - - } - - - -// Simulate waiting for trigger events (replace with actual logic as needed) - - k_sleep(K_SECONDS(10)); // Wait for 10 seconds to allow triggers to occur - - - -// Step 6: Clear the trigger subscription - - status = ocre_sensors_clear_trigger(sensor_handle, SENSOR_CHANNEL_TEMPERATURE, subscription_id); - - if (status != 0) { - - printf("Failed to clear trigger. Status: %d\n", status); - - return; - - } - - - -// Step 7: Clean up the sensors environment - - status = ocre_sensors_cleanup(); - - if (status != 0) { - - printf("Failed to clean up sensors. Status: %d\n", status); - - return; - - } - - - - printf("Sensor operations completed successfully.\n"); - -} -``` \ No newline at end of file diff --git a/src/ocre/shell/ocre_shell.c b/src/ocre/shell/ocre_shell.c deleted file mode 100644 index a60e0c0a..00000000 --- a/src/ocre/shell/ocre_shell.c +++ /dev/null @@ -1,157 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include - -#include "ocre_shell.h" - -static ocre_cs_ctx *ctx_internal; - -int cmd_ocre_run(const struct shell *shell, size_t argc, char **argv) -{ - if (!ctx_internal) { - shell_error(shell, "Internal context not initialized."); - return -1; - } - - if (argc != 2) { - shell_error(shell, "Usage: ocre run "); - return -EINVAL; - } - - char *endptr; - int container_id = (int)strtol(argv[1], &endptr, 10); - - if (endptr == argv[1]) { - shell_error(shell, "No digits were found in argument .\n"); - return -EINVAL; - } else if (*endptr != '\0') { - shell_error(shell, "Invalid character: %c\n", *endptr); - return -EINVAL; - } - - int ret = -1; - for (int i = 0; i < CONFIG_MAX_CONTAINERS; i++) { - if (container_id == ctx_internal->containers[i].container_ID) { - ret = ocre_container_runtime_run_container(container_id, NULL); - break; - } - } - - if (ret == CONTAINER_STATUS_RUNNING) { - shell_info(shell, "Container started. Name: %s, ID: %d", - ctx_internal->containers[container_id].ocre_container_data.name, container_id); - return 0; - } else { - shell_error(shell, "Failed to run container: %d", container_id); - return -EIO; - } -} - -int cmd_ocre_stop(const struct shell *shell, size_t argc, char **argv) -{ - if (!ctx_internal) { - shell_error(shell, "Internal context not initialized."); - return -1; - } - - if (argc != 2) { - shell_error(shell, "Usage: ocre stop "); - return -EINVAL; - } - - const char *name = argv[1]; - shell_info(shell, "OCRE Shell Request to stop container with name: %s", name); - - int ret = -1; - int container_id = -1; - for (int i = 0; i < CONFIG_MAX_CONTAINERS; i++) { - if (strcmp(ctx_internal->containers[i].ocre_container_data.name, name) == 0) { - container_id = ctx_internal->containers[i].container_ID; - ret = ocre_container_runtime_stop_container(container_id, NULL); - } - } - - if (ret == CONTAINER_STATUS_STOPPED) { - shell_info(shell, "Container stopped. Name: %s, ID: %d", name, container_id); - } else if (ret == -1) { - shell_error(shell, "Failed to found container: %s", name); - } else { - shell_error(shell, "Failed to stop container: %s", name); - } - - return ret; -} - -int cmd_ocre_restart(const struct shell *shell, size_t argc, char **argv) -{ - if (!ctx_internal) { - shell_error(shell, "Internal context not initialized."); - return -1; - } - - if (argc != 2) { - shell_error(shell, "Usage: ocre restart "); - return -EINVAL; - } - - const char *name = argv[1]; - shell_info(shell, "OCRE Shell Request to restart container with name: %s", name); - - int ret = -1; - int container_id = -1; - for (int i = 0; i < CONFIG_MAX_CONTAINERS; i++) { - if (strcmp(ctx_internal->containers[i].ocre_container_data.name, name) == 0) { - container_id = ctx_internal->containers[i].container_ID; - ret = ocre_container_runtime_restart_container(ctx_internal, container_id, NULL); - } - } - - if (ret == CONTAINER_STATUS_RUNNING) { - shell_info(shell, "Container restarted. Name: %s, ID: %d", name, container_id); - } else if (ret == -1) { - shell_error(shell, "Failed to found container: %s", name); - } else { - shell_error(shell, "Failed to restart container: %s, status: %d", name, ret); - } - - return ret; -} - -int cmd_ocre_ls(const struct shell *shell, size_t argc, char **argv) -{ - ARG_UNUSED(argc); - ARG_UNUSED(argv); - - if (!ctx_internal) { - shell_error(shell, "Internal context not initialized."); - return -1; - } - - for (int i = 0; i < CONFIG_MAX_CONTAINERS; i++) { - shell_info(shell, "Container ID: %d, name: %s, status: %d", ctx_internal->containers[i].container_ID, - ctx_internal->containers[i].ocre_container_data.name, - ctx_internal->containers[i].container_runtime_status); - } - - return 0; -} - -void register_ocre_shell(ocre_cs_ctx *ctx) -{ - ctx_internal = ctx; - - SHELL_STATIC_SUBCMD_SET_CREATE( - ocre_subcmds, SHELL_CMD(run, NULL, "Start a new container: ocre run ", cmd_ocre_run), - SHELL_CMD(stop, NULL, "Stop a container: ocre stop ", cmd_ocre_stop), - SHELL_CMD(restart, NULL, "Restart a container: ocre restart ", cmd_ocre_restart), - SHELL_CMD(ls, NULL, "List running containers and their status", cmd_ocre_ls), SHELL_SUBCMD_SET_END); - -#if defined(CONFIG_OCRE_SHELL) - SHELL_CMD_REGISTER(ocre, &ocre_subcmds, "OCRE agent commands", NULL); -#endif -} diff --git a/src/ocre/shell/ocre_shell.h b/src/ocre/shell/ocre_shell.h deleted file mode 100644 index 56238c85..00000000 --- a/src/ocre/shell/ocre_shell.h +++ /dev/null @@ -1,22 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef OCRE_SHELL_H -#define OCRE_SHELL_H - -#include -#include "../src/ocre/ocre_container_runtime/ocre_container_runtime.h" - -// Function declarations for `config` subcommand handlers -int cmd_ocre_run(const struct shell *shell, size_t argc, char **argv); -int cmd_ocre_stop(const struct shell *shell, size_t argc, char **argv); -int cmd_ocre_ps(const struct shell *shell, size_t argc, char **argv); - -// Command registration function -void register_ocre_shell(ocre_cs_ctx *ctx); - -#endif /* OCRE_SHELL_H */ diff --git a/src/ocre/sm/sm.c b/src/ocre/sm/sm.c deleted file mode 100644 index cd72e30f..00000000 --- a/src/ocre/sm/sm.c +++ /dev/null @@ -1,100 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include "ocre_core_external.h" -#include "ocre/component/component.h" - -#include -#include -#include - -#include "sm.h" -LOG_MODULE_REGISTER(state_machine, OCRE_LOG_LEVEL); - -void sm_init(state_machine_t *sm, core_mq_t *msgq, void *msg, void *custom_ctx, const struct smf_state *hsm) -{ - sm->hsm = hsm; - sm->msgq = msgq; - sm->ctx.event.msg = msg; - sm->ctx.custom_ctx = custom_ctx; -} - -int sm_run(state_machine_t *sm, int initial_state) -{ - int ret; - - smf_set_initial(SMF_CTX(&sm->ctx), &sm->hsm[initial_state]); - - while (true) { - // Wait for a message from the queue - core_mq_recv(sm->msgq, sm->ctx.event.msg); - sm->ctx.event.handled = false; - - ret = smf_run_state(SMF_CTX(&sm->ctx)); - - if (ret) { - LOG_ERR("State machine error: %d", ret); - break; - } - - if (!sm->ctx.event.handled) { - struct ocre_message const *msg = SM_GET_EVENT(&sm->ctx); - LOG_ERR("Unhandled event: msg type or data = %d", msg->event); - } - - // Yield the current thread to allow the queue events to be processed - core_yield(); - } - - return ret; -} - -int sm_transition(state_machine_t *sm, int target_state) -{ - if (!sm->hsm) { - LOG_ERR("State machine has not been initialized"); - return -EINVAL; - } - - smf_set_state(SMF_CTX(&sm->ctx), &sm->hsm[target_state]); - - return 0; -} - -int sm_init_event_timer(state_machine_t *sm, int timer_id, void *timer_cb) -{ - if (timer_id < 0 || timer_id >= MAX_TIMERS) { - LOG_ERR("Invalid timer id: %d", timer_id); - return -EINVAL; - } - - core_timer_init(&sm->timers[timer_id], timer_cb, NULL); - return 0; -} - -int sm_set_event_timer(state_machine_t *sm, int timer_id, int duration, int period) -{ - if (timer_id < 0 || timer_id >= MAX_TIMERS) { - LOG_ERR("Invalid timer id: %d", timer_id); - return -EINVAL; - } - - core_timer_start(&sm->timers[timer_id], duration, period); - return 0; -} - -int sm_clear_event_timer(state_machine_t *sm, int timer_id) -{ - if (timer_id < 0 || timer_id >= MAX_TIMERS) { - LOG_ERR("Invalid timer id: %d", timer_id); - return -EINVAL; - } - - core_timer_stop(&sm->timers[timer_id]); - return 0; -} diff --git a/src/ocre/sm/sm.h b/src/ocre/sm/sm.h deleted file mode 100644 index e27c556d..00000000 --- a/src/ocre/sm/sm.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef SM_H -#define SM_H - -#include "ocre_core_external.h" - -#define MAX_TIMERS 3 - -#define SM_RETURN_IF_EVENT_HANDLED(o) \ - if (((struct sm_ctx *)o)->event.handled) \ - return SMF_EVENT_HANDLED -#define SM_MARK_EVENT_HANDLED(o) ((struct sm_ctx *)o)->event.handled = true -#define SM_GET_EVENT(o) ((struct sm_ctx *)o)->event.msg -#define SM_GET_CUSTOM_CTX(o) ((struct sm_ctx *)o)->custom_ctx - -#define EVENT_LOG_MSG(fmt, event) LOG_DBG(fmt, event) - -struct sm_ctx { - struct smf_ctx ctx; - struct { - bool handled; - void *msg; - } event; - void *custom_ctx; -}; - -typedef struct state_machine { - core_mq_t *msgq; - core_timer_t timers[MAX_TIMERS]; - struct sm_ctx ctx; /*!< State machine context */ - const struct smf_state *hsm; /*!< State machine states */ -} state_machine_t; - -int sm_transition(state_machine_t *sm, int target_state); - -int sm_run(state_machine_t *sm, int initial_state); - -int sm_init_event_timer(state_machine_t *sm, int timer_id, void *timer_cb); - -int sm_set_event_timer(state_machine_t *sm, int timer_id, int duration, int period); - -int sm_clear_event_timer(state_machine_t *sm, int timer_id); - -int sm_dispatch_event(state_machine_t *sm, void *msg); - -void sm_init(state_machine_t *sm, core_mq_t *msgq, void *msg, void *custom_ctx, const struct smf_state *hsm); - -#endif // SM_H diff --git a/src/ocre/uthash/LICENSE b/src/ocre/uthash/LICENSE new file mode 100644 index 00000000..ce887671 --- /dev/null +++ b/src/ocre/uthash/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2005-2025, Troy D. Hanson https://troydhanson.github.io/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/src/ocre/uthash/utlist.h b/src/ocre/uthash/utlist.h new file mode 100644 index 00000000..275e4223 --- /dev/null +++ b/src/ocre/uthash/utlist.h @@ -0,0 +1,1076 @@ +/* +Copyright (c) 2007-2025, Troy D. Hanson https://troydhanson.github.io/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTLIST_H +#define UTLIST_H + +#define UTLIST_VERSION 2.3.0 + +#include + +/* + * This file contains macros to manipulate singly and doubly-linked lists. + * + * 1. LL_ macros: singly-linked lists. + * 2. DL_ macros: doubly-linked lists. + * 3. CDL_ macros: circular doubly-linked lists. + * + * To use singly-linked lists, your structure must have a "next" pointer. + * To use doubly-linked lists, your structure must "prev" and "next" pointers. + * Either way, the pointer to the head of the list must be initialized to NULL. + * + * ----------------.EXAMPLE ------------------------- + * struct item { + * int id; + * struct item *prev, *next; + * } + * + * struct item *list = NULL: + * + * int main() { + * struct item *item; + * ... allocate and populate item ... + * DL_APPEND(list, item); + * } + * -------------------------------------------------- + * + * For doubly-linked lists, the append and delete macros are O(1) + * For singly-linked lists, append and delete are O(n) but prepend is O(1) + * The sort macro is O(n log(n)) for all types of single/double/circular lists. + */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#if !defined(LDECLTYPE) && !defined(NO_DECLTYPE) +#if defined(_MSC_VER) /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define LDECLTYPE(x) decltype(x) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#endif +#elif defined(__MCST__) /* Elbrus C Compiler */ +#define LDECLTYPE(x) __typeof(x) +#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) +#define NO_DECLTYPE +#else /* GNU, Sun and other compilers */ +#define LDECLTYPE(x) __typeof(x) +#endif +#endif + +/* for VS2008 we use some workarounds to get around the lack of decltype, + * namely, we always reassign our tmp variable to the list head if we need + * to dereference its prev/next pointers, and save/restore the real head.*/ +#ifdef NO_DECLTYPE +#define IF_NO_DECLTYPE(x) x +#define LDECLTYPE(x) char* +#define UTLIST_SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } +#define UTLIST_NEXT(elt,list,next) ((char*)((list)->next)) +#define UTLIST_NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } +/* #define UTLIST_PREV(elt,list,prev) ((char*)((list)->prev)) */ +#define UTLIST_PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } +#define UTLIST_RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } +#define UTLIST_CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } +#else +#define IF_NO_DECLTYPE(x) +#define UTLIST_SV(elt,list) +#define UTLIST_NEXT(elt,list,next) ((elt)->next) +#define UTLIST_NEXTASGN(elt,list,to,next) ((elt)->next)=(to) +/* #define UTLIST_PREV(elt,list,prev) ((elt)->prev) */ +#define UTLIST_PREVASGN(elt,list,to,prev) ((elt)->prev)=(to) +#define UTLIST_RS(list) +#define UTLIST_CASTASGN(a,b) (a)=(b) +#endif + +/****************************************************************************** + * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * + * Unwieldy variable names used here to avoid shadowing passed-in variables. * + *****************************************************************************/ +#define LL_SORT(list, cmp) \ + LL_SORT2(list, cmp, next) + +#define LL_SORT2(list, cmp, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + UTLIST_CASTASGN(_ls_p,list); \ + (list) = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ + } else { \ + UTLIST_CASTASGN(list,_ls_e); \ + } \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list); \ + } \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + + +#define DL_SORT(list, cmp) \ + DL_SORT2(list, cmp, prev, next) + +#define DL_SORT2(list, cmp, prev, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + UTLIST_CASTASGN(_ls_p,list); \ + (list) = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while ((_ls_psize > 0) || ((_ls_qsize > 0) && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } else if ((_ls_qsize == 0) || (!_ls_q)) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ + } else { \ + UTLIST_CASTASGN(list,_ls_e); \ + } \ + UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + UTLIST_CASTASGN((list)->prev, _ls_tail); \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + +#define CDL_SORT(list, cmp) \ + CDL_SORT2(list, cmp, prev, next) + +#define CDL_SORT2(list, cmp, prev, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + UTLIST_CASTASGN(_ls_p,list); \ + UTLIST_CASTASGN(_ls_oldhead,list); \ + (list) = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + UTLIST_SV(_ls_q,list); \ + if (UTLIST_NEXT(_ls_q,list,next) == _ls_oldhead) { \ + _ls_q = NULL; \ + } else { \ + _ls_q = UTLIST_NEXT(_ls_q,list,next); \ + } \ + UTLIST_RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ + } else { \ + UTLIST_CASTASGN(list,_ls_e); \ + } \ + UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + UTLIST_CASTASGN((list)->prev,_ls_tail); \ + UTLIST_CASTASGN(_tmp,list); \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_tmp,next); UTLIST_RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + +/****************************************************************************** + * singly linked list macros (non-circular) * + *****************************************************************************/ +#define LL_PREPEND(head,add) \ + LL_PREPEND2(head,add,next) + +#define LL_PREPEND2(head,add,next) \ +do { \ + (add)->next = (head); \ + (head) = (add); \ +} while (0) + +#define LL_CONCAT(head1,head2) \ + LL_CONCAT2(head1,head2,next) + +#define LL_CONCAT2(head1,head2,next) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head1) { \ + _tmp = (head1); \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(head2); \ + } else { \ + (head1)=(head2); \ + } \ +} while (0) + +#define LL_APPEND(head,add) \ + LL_APPEND2(head,add,next) + +#define LL_APPEND2(head,add,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + (add)->next=NULL; \ + if (head) { \ + _tmp = (head); \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(add); \ + } else { \ + (head)=(add); \ + } \ +} while (0) + +#define LL_INSERT_INORDER(head,add,cmp) \ + LL_INSERT_INORDER2(head,add,cmp,next) + +#define LL_INSERT_INORDER2(head,add,cmp,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + LL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + LL_APPEND_ELEM2(head, _tmp, add, next); \ + } else { \ + (head) = (add); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define LL_LOWER_BOUND(head,elt,like,cmp) \ + LL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define LL_LOWER_BOUND2(head,elt,like,cmp,next) \ + do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) { \ + if (cmp((elt)->next, like) >= 0) { \ + break; \ + } \ + } \ + } \ + } while (0) + +#define LL_DELETE(head,del) \ + LL_DELETE2(head,del,next) + +#define LL_DELETE2(head,del,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (del))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (del)->next; \ + } \ + } \ +} while (0) + +#define LL_COUNT(head,el,counter) \ + LL_COUNT2(head,el,counter,next) \ + +#define LL_COUNT2(head,el,counter,next) \ +do { \ + (counter) = 0; \ + LL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) + +#define LL_FOREACH(head,el) \ + LL_FOREACH2(head,el,next) + +#define LL_FOREACH2(head,el,next) \ + for ((el) = (head); el; (el) = (el)->next) + +#define LL_FOREACH_SAFE(head,el,tmp) \ + LL_FOREACH_SAFE2(head,el,tmp,next) + +#define LL_FOREACH_SAFE2(head,el,tmp,next) \ + for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp)) + +#define LL_SEARCH_SCALAR(head,out,field,val) \ + LL_SEARCH_SCALAR2(head,out,field,val,next) + +#define LL_SEARCH_SCALAR2(head,out,field,val,next) \ +do { \ + LL_FOREACH2(head,out,next) { \ + if ((out)->field == (val)) break; \ + } \ +} while (0) + +#define LL_SEARCH(head,out,elt,cmp) \ + LL_SEARCH2(head,out,elt,cmp,next) + +#define LL_SEARCH2(head,out,elt,cmp,next) \ +do { \ + LL_FOREACH2(head,out,next) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while (0) + +#define LL_REPLACE_ELEM2(head, el, add, next) \ +do { \ + LDECLTYPE(head) _tmp; \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (el))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (add); \ + } \ + } \ +} while (0) + +#define LL_REPLACE_ELEM(head, el, add) \ + LL_REPLACE_ELEM2(head, el, add, next) + +#define LL_PREPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + LDECLTYPE(head) _tmp; \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (el))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (add); \ + } \ + } \ + } else { \ + LL_APPEND2(head, add, next); \ + } \ +} while (0) \ + +#define LL_PREPEND_ELEM(head, el, add) \ + LL_PREPEND_ELEM2(head, el, add, next) + +#define LL_APPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (el)->next = (add); \ + } else { \ + LL_PREPEND2(head, add, next); \ + } \ +} while (0) \ + +#define LL_APPEND_ELEM(head, el, add) \ + LL_APPEND_ELEM2(head, el, add, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef LL_CONCAT2 +#define LL_CONCAT2(head1,head2,next) \ +do { \ + char *_tmp; \ + if (head1) { \ + _tmp = (char*)(head1); \ + while ((head1)->next) { (head1) = (head1)->next; } \ + (head1)->next = (head2); \ + UTLIST_RS(head1); \ + } else { \ + (head1)=(head2); \ + } \ +} while (0) + +#undef LL_APPEND2 +#define LL_APPEND2(head,add,next) \ +do { \ + if (head) { \ + (add)->next = head; /* use add->next as a temp variable */ \ + while ((add)->next->next) { (add)->next = (add)->next->next; } \ + (add)->next->next=(add); \ + } else { \ + (head)=(add); \ + } \ + (add)->next=NULL; \ +} while (0) + +#undef LL_INSERT_INORDER2 +#define LL_INSERT_INORDER2(head,add,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, add)) >= 0) { \ + (add)->next = (head); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next != NULL && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->next = (head)->next; \ + (head)->next = (add); \ + UTLIST_RS(head); \ + } \ +} while (0) + +#undef LL_DELETE2 +#define LL_DELETE2(head,del,next) \ +do { \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next && ((head)->next != (del))) { \ + (head) = (head)->next; \ + } \ + if ((head)->next) { \ + (head)->next = ((del)->next); \ + } \ + UTLIST_RS(head); \ + } \ +} while (0) + +#undef LL_REPLACE_ELEM2 +#define LL_REPLACE_ELEM2(head, el, add, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->next = head; \ + while ((add)->next->next && ((add)->next->next != (el))) { \ + (add)->next = (add)->next->next; \ + } \ + if ((add)->next->next) { \ + (add)->next->next = (add); \ + } \ + } \ + (add)->next = (el)->next; \ +} while (0) + +#undef LL_PREPEND_ELEM2 +#define LL_PREPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->next = (head); \ + while ((add)->next->next && ((add)->next->next != (el))) { \ + (add)->next = (add)->next->next; \ + } \ + if ((add)->next->next) { \ + (add)->next->next = (add); \ + } \ + } \ + (add)->next = (el); \ + } else { \ + LL_APPEND2(head, add, next); \ + } \ +} while (0) \ + +#endif /* NO_DECLTYPE */ + +/****************************************************************************** + * doubly linked list macros (non-circular) * + *****************************************************************************/ +#define DL_PREPEND(head,add) \ + DL_PREPEND2(head,add,prev,next) + +#define DL_PREPEND2(head,add,prev,next) \ +do { \ + (add)->next = (head); \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev = (add); \ + } else { \ + (add)->prev = (add); \ + } \ + (head) = (add); \ +} while (0) + +#define DL_APPEND(head,add) \ + DL_APPEND2(head,add,prev,next) + +#define DL_APPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev->next = (add); \ + (head)->prev = (add); \ + (add)->next = NULL; \ + } else { \ + (head)=(add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define DL_INSERT_INORDER(head,add,cmp) \ + DL_INSERT_INORDER2(head,add,cmp,prev,next) + +#define DL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + DL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + DL_APPEND_ELEM2(head, _tmp, add, prev, next); \ + } else { \ + (head) = (add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define DL_LOWER_BOUND(head,elt,like,cmp) \ + DL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define DL_LOWER_BOUND2(head,elt,like,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) { \ + if ((cmp((elt)->next, like)) >= 0) { \ + break; \ + } \ + } \ + } \ +} while (0) + +#define DL_CONCAT(head1,head2) \ + DL_CONCAT2(head1,head2,prev,next) + +#define DL_CONCAT2(head1,head2,prev,next) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head2) { \ + if (head1) { \ + UTLIST_CASTASGN(_tmp, (head2)->prev); \ + (head2)->prev = (head1)->prev; \ + (head1)->prev->next = (head2); \ + UTLIST_CASTASGN((head1)->prev, _tmp); \ + } else { \ + (head1)=(head2); \ + } \ + } \ +} while (0) + +#define DL_DELETE(head,del) \ + DL_DELETE2(head,del,prev,next) + +#define DL_DELETE2(head,del,prev,next) \ +do { \ + assert((head) != NULL); \ + assert((del)->prev != NULL); \ + if ((del)->prev == (del)) { \ + (head)=NULL; \ + } else if ((del) == (head)) { \ + assert((del)->next != NULL); \ + (del)->next->prev = (del)->prev; \ + (head) = (del)->next; \ + } else { \ + (del)->prev->next = (del)->next; \ + if ((del)->next) { \ + (del)->next->prev = (del)->prev; \ + } else { \ + (head)->prev = (del)->prev; \ + } \ + } \ +} while (0) + +#define DL_COUNT(head,el,counter) \ + DL_COUNT2(head,el,counter,next) \ + +#define DL_COUNT2(head,el,counter,next) \ +do { \ + (counter) = 0; \ + DL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) + +#define DL_FOREACH(head,el) \ + DL_FOREACH2(head,el,next) + +#define DL_FOREACH2(head,el,next) \ + for ((el) = (head); el; (el) = (el)->next) + +/* this version is safe for deleting the elements during iteration */ +#define DL_FOREACH_SAFE(head,el,tmp) \ + DL_FOREACH_SAFE2(head,el,tmp,next) + +#define DL_FOREACH_SAFE2(head,el,tmp,next) \ + for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp)) + +/* these are identical to their singly-linked list counterparts */ +#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR +#define DL_SEARCH LL_SEARCH +#define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2 +#define DL_SEARCH2 LL_SEARCH2 + +#define DL_REPLACE_ELEM2(head, el, add, prev, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + (add)->next = (el)->next; \ + if ((el)->next == NULL) { \ + (add)->prev = (add); \ + } else { \ + (add)->prev = (el)->prev; \ + (add)->next->prev = (add); \ + } \ + } else { \ + (add)->next = (el)->next; \ + (add)->prev = (el)->prev; \ + (add)->prev->next = (add); \ + if ((el)->next == NULL) { \ + (head)->prev = (add); \ + } else { \ + (add)->next->prev = (add); \ + } \ + } \ +} while (0) + +#define DL_REPLACE_ELEM(head, el, add) \ + DL_REPLACE_ELEM2(head, el, add, prev, next) + +#define DL_PREPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + (add)->prev = (el)->prev; \ + (el)->prev = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->prev->next = (add); \ + } \ + } else { \ + DL_APPEND2(head, add, prev, next); \ + } \ +} while (0) \ + +#define DL_PREPEND_ELEM(head, el, add) \ + DL_PREPEND_ELEM2(head, el, add, prev, next) + +#define DL_APPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (add)->prev = (el); \ + (el)->next = (add); \ + if ((add)->next) { \ + (add)->next->prev = (add); \ + } else { \ + (head)->prev = (add); \ + } \ + } else { \ + DL_PREPEND2(head, add, prev, next); \ + } \ +} while (0) \ + +#define DL_APPEND_ELEM(head, el, add) \ + DL_APPEND_ELEM2(head, el, add, prev, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef DL_INSERT_INORDER2 +#define DL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + if ((head) == NULL) { \ + (add)->prev = (add); \ + (add)->next = NULL; \ + (head) = (add); \ + } else if ((cmp(head, add)) >= 0) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->prev = (head); \ + (add)->next = (head)->next; \ + (head)->next = (add); \ + UTLIST_RS(head); \ + if ((add)->next) { \ + (add)->next->prev = (add); \ + } else { \ + (head)->prev = (add); \ + } \ + } \ +} while (0) +#endif /* NO_DECLTYPE */ + +/****************************************************************************** + * circular doubly linked list macros * + *****************************************************************************/ +#define CDL_APPEND(head,add) \ + CDL_APPEND2(head,add,prev,next) + +#define CDL_APPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + (head) = (add); \ + } \ +} while (0) + +#define CDL_PREPEND(head,add) \ + CDL_PREPEND2(head,add,prev,next) + +#define CDL_PREPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + } \ + (head) = (add); \ +} while (0) + +#define CDL_INSERT_INORDER(head,add,cmp) \ + CDL_INSERT_INORDER2(head,add,cmp,prev,next) + +#define CDL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + CDL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + CDL_APPEND_ELEM2(head, _tmp, add, prev, next); \ + } else { \ + (head) = (add); \ + (head)->next = (head); \ + (head)->prev = (head); \ + } \ +} while (0) + +#define CDL_LOWER_BOUND(head,elt,like,cmp) \ + CDL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define CDL_LOWER_BOUND2(head,elt,like,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != (head); (elt) = (elt)->next) { \ + if ((cmp((elt)->next, like)) >= 0) { \ + break; \ + } \ + } \ + } \ +} while (0) + +#define CDL_DELETE(head,del) \ + CDL_DELETE2(head,del,prev,next) + +#define CDL_DELETE2(head,del,prev,next) \ +do { \ + if (((head)==(del)) && ((head)->next == (head))) { \ + (head) = NULL; \ + } else { \ + (del)->next->prev = (del)->prev; \ + (del)->prev->next = (del)->next; \ + if ((del) == (head)) (head)=(del)->next; \ + } \ +} while (0) + +#define CDL_COUNT(head,el,counter) \ + CDL_COUNT2(head,el,counter,next) \ + +#define CDL_COUNT2(head, el, counter,next) \ +do { \ + (counter) = 0; \ + CDL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) + +#define CDL_FOREACH(head,el) \ + CDL_FOREACH2(head,el,next) + +#define CDL_FOREACH2(head,el,next) \ + for ((el)=(head);el;(el)=(((el)->next==(head)) ? NULL : (el)->next)) + +#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ + CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) + +#define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) \ + for ((el) = (head), (tmp1) = (head) ? (head)->prev : NULL; \ + (el) && ((tmp2) = (el)->next, 1); \ + (el) = ((el) == (tmp1) ? NULL : (tmp2))) + +#define CDL_SEARCH_SCALAR(head,out,field,val) \ + CDL_SEARCH_SCALAR2(head,out,field,val,next) + +#define CDL_SEARCH_SCALAR2(head,out,field,val,next) \ +do { \ + CDL_FOREACH2(head,out,next) { \ + if ((out)->field == (val)) break; \ + } \ +} while (0) + +#define CDL_SEARCH(head,out,elt,cmp) \ + CDL_SEARCH2(head,out,elt,cmp,next) + +#define CDL_SEARCH2(head,out,elt,cmp,next) \ +do { \ + CDL_FOREACH2(head,out,next) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while (0) + +#define CDL_REPLACE_ELEM2(head, el, add, prev, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((el)->next == (el)) { \ + (add)->next = (add); \ + (add)->prev = (add); \ + (head) = (add); \ + } else { \ + (add)->next = (el)->next; \ + (add)->prev = (el)->prev; \ + (add)->next->prev = (add); \ + (add)->prev->next = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } \ + } \ +} while (0) + +#define CDL_REPLACE_ELEM(head, el, add) \ + CDL_REPLACE_ELEM2(head, el, add, prev, next) + +#define CDL_PREPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + (add)->prev = (el)->prev; \ + (el)->prev = (add); \ + (add)->prev->next = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } \ + } else { \ + CDL_APPEND2(head, add, prev, next); \ + } \ +} while (0) + +#define CDL_PREPEND_ELEM(head, el, add) \ + CDL_PREPEND_ELEM2(head, el, add, prev, next) + +#define CDL_APPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (add)->prev = (el); \ + (el)->next = (add); \ + (add)->next->prev = (add); \ + } else { \ + CDL_PREPEND2(head, add, prev, next); \ + } \ +} while (0) + +#define CDL_APPEND_ELEM(head, el, add) \ + CDL_APPEND_ELEM2(head, el, add, prev, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef CDL_INSERT_INORDER2 +#define CDL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + if ((head) == NULL) { \ + (add)->prev = (add); \ + (add)->next = (add); \ + (head) = (add); \ + } else if ((cmp(head, add)) >= 0) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (add)->prev->next = (add); \ + (head)->prev = (add); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((char*)(head)->next != _tmp && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->prev = (head); \ + (add)->next = (head)->next; \ + (add)->next->prev = (add); \ + (head)->next = (add); \ + UTLIST_RS(head); \ + } \ +} while (0) +#endif /* NO_DECLTYPE */ + +#endif /* UTLIST_H */ diff --git a/src/ocre/util/rm_rf.c b/src/ocre/util/rm_rf.c new file mode 100644 index 00000000..5283a399 --- /dev/null +++ b/src/ocre/util/rm_rf.c @@ -0,0 +1,205 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Stack node for directory traversal */ + +struct dir_stack_node { + char *path; + DIR *dir; + int is_removal_phase; + struct dir_stack_node *next; +}; + +/* Stack operations */ + +static struct dir_stack_node *push_dir(struct dir_stack_node *stack, const char *path, int is_removal_phase) +{ + struct dir_stack_node *node = malloc(sizeof(struct dir_stack_node)); + if (!node) { + return NULL; + } + + node->path = strdup(path); + if (!node->path) { + free(node); + return NULL; + } + + node->dir = NULL; + node->is_removal_phase = is_removal_phase; + node->next = stack; + + return node; +} + +static struct dir_stack_node *pop_dir(struct dir_stack_node *stack) +{ + if (!stack) { + return NULL; + } + + struct dir_stack_node *next = stack->next; + + if (stack->dir) { + closedir(stack->dir); + } + free(stack->path); + free(stack); + + return next; +} + +/* Build full path by concatenating directory and filename */ + +static char *build_path(const char *dir, const char *name) +{ + size_t dir_len = strlen(dir); + size_t name_len = strlen(name); + size_t total_len = dir_len + name_len + 2; /* +1 for '/', +1 for '\0' */ + + char *path = malloc(total_len); + if (!path) { + return NULL; + } + + snprintf(path, total_len, "%s/%s", dir, name); + return path; +} + +/* Remove directory recursively using stack-based traversal (no function recursion) */ + +int rm_rf(const char *path) +{ + if (!path) { + errno = EINVAL; + return -1; + } + + struct stat st; + if (stat(path, &st) == -1) { + return -1; /* errno already set by stat */ + } + + /* If it's a regular file, just remove it */ + + if (!S_ISDIR(st.st_mode)) { + return unlink(path); + } + + /* Stack for directory traversal */ + + struct dir_stack_node *stack = NULL; + int result = 0; + + /* Push initial directory onto stack */ + + stack = push_dir(stack, path, 0); + if (!stack) { + errno = ENOMEM; + return -1; + } + + /* Process stack until empty */ + + while (stack) { + struct dir_stack_node *current = stack; + + if (current->is_removal_phase) { + /* This directory has been processed, now remove it */ + + if (rmdir(current->path) == -1) { + result = -1; + + /* Continue processing to clean up stack */ + } + stack = pop_dir(stack); + continue; + } + + /* First time processing this directory */ + + if (!current->dir) { + current->dir = opendir(current->path); + if (!current->dir) { + result = -1; + stack = pop_dir(stack); + continue; + } + } + + /* Read next directory entry */ + + const struct dirent *entry = readdir(current->dir); + if (!entry) { + /* No more entries, mark for removal and continue */ + current->is_removal_phase = 1; + continue; + } + + /* Skip . and .. entries */ + + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + continue; + } + + /* Build full path for this entry */ + + char *entry_path = build_path(current->path, entry->d_name); + if (!entry_path) { + result = -1; + errno = ENOMEM; + break; + } + + /* Get entry information */ + + if (stat(entry_path, &st) == -1) { + result = -1; + free(entry_path); + continue; + } + + if (S_ISDIR(st.st_mode)) { + /* It's a directory - push it onto stack for processing */ + + struct dir_stack_node *new_node = push_dir(stack, entry_path, 0); + if (!new_node) { + result = -1; + errno = ENOMEM; + free(entry_path); + break; + } + stack = new_node; + } else { + /* It's a file - remove it directly */ + + if (unlink(entry_path) == -1) { + result = -1; + + /* Continue processing other entries */ + } + } + + free(entry_path); + } + + /* Clean up remaining stack in case of error */ + + while (stack) { + stack = pop_dir(stack); + } + + return result; +} diff --git a/src/ocre/util/rm_rf.h b/src/ocre/util/rm_rf.h new file mode 100644 index 00000000..91f70dd7 --- /dev/null +++ b/src/ocre/util/rm_rf.h @@ -0,0 +1,25 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef RM_RF_H +#define RM_RF_H + +/** + * Remove a file or directory recursively. + * + * This function removes the specified path and all its contents recursively. + * It uses a stack-based approach to traverse directories without function recursion. + * + * @param path The path to the file or directory to remove + * @return 0 on success, -1 on error (errno is set appropriately) + * + * Note: This function will attempt to remove as many files/directories as possible + * even if some operations fail. The return value indicates if any errors occurred. + */ +int rm_rf(const char *path); + +#endif /* RM_RF_H */ diff --git a/src/ocre/util/string_array.c b/src/ocre/util/string_array.c new file mode 100644 index 00000000..89bcd808 --- /dev/null +++ b/src/ocre/util/string_array.c @@ -0,0 +1,88 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "string_array.h" + +size_t string_array_size(const char **array) +{ + size_t size = 0; + + if (!array) { + return 0; + } + + do { + size++; + } while (*array++); + + return size; +} + +void string_array_free(char **array) +{ + if (!array) { + return; + } + + for (size_t j = 0; array[j]; j++) { + free(array[j]); + } + + free(array); +} + +char **string_array_deep_dup(const char **const src) +{ + size_t size = string_array_size((const char **)src); + + if (!size) { + return NULL; + } + + char **dst = malloc(size * sizeof(char *)); + if (!dst) { + return NULL; + } + + memset(dst, 0, size * sizeof(char *)); + + size_t i; + for (i = 0; i < size - 1; i++) { + dst[i] = strdup(src[i]); + if (!dst[i]) { + goto error; + } + } + + dst[i] = NULL; + + return dst; + +error: + string_array_free(dst); + + return NULL; +} + +const char *string_array_lookup(const char **array, const char *key) +{ + if (!array || !key) { + return NULL; + } + + for (size_t i = 0; array[i]; i++) { + if (!strcmp(array[i], key)) { + return array[i]; + } + } + + return NULL; +} diff --git a/src/ocre/util/string_array.h b/src/ocre/util/string_array.h new file mode 100644 index 00000000..a129911c --- /dev/null +++ b/src/ocre/util/string_array.h @@ -0,0 +1,28 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/* returns the size of a string array including the NULL termination + * returns 0 if pointer is null + */ +size_t string_array_size(const char **const array); + +/* makes a deep copy of an array of char * + * returns NULL on error + */ +char **string_array_deep_dup(const char **const src); + +/* frees an array of char * + * does nothing if pointer is null + */ +void string_array_free(char **array); + +/* looks up a key in an array of char * + * returns NULL if not found + */ +const char *string_array_lookup(const char **array, const char *key); diff --git a/src/ocre/util/unique_random_id.c b/src/ocre/util/unique_random_id.c new file mode 100644 index 00000000..7e1ba775 --- /dev/null +++ b/src/ocre/util/unique_random_id.c @@ -0,0 +1,55 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "../context.h" + +#include "unique_random_id.h" + +static char nibble_to_hex(char nibble) +{ + return nibble < 10 ? '0' + nibble : 'a' + nibble - 10; +} + +static void generate_random_id(char *id, size_t len) +{ + if (len < 1) { + return; + } + + size_t i; + for (i = 0; i < len - 1; i++) { + id[i] = nibble_to_hex(0xf & rand() / (RAND_MAX / 16)); + } + + id[i] = '\0'; +} + +int make_unique_random_container_id(const struct ocre_context *context, char *container_id, size_t len) +{ + char *random_id = malloc(len); + if (!random_id) { + return -1; + } + + for (int i = 0; i < 100; i++) { + generate_random_id(random_id, len); + if (!ocre_context_get_container_by_id_locked(context, random_id)) { + strcpy(container_id, random_id); + free(random_id); + return 0; + } + } + + /* Gave up */ + free(random_id); + + return -1; +} diff --git a/src/ocre/util/unique_random_id.h b/src/ocre/util/unique_random_id.h new file mode 100644 index 00000000..c05709e7 --- /dev/null +++ b/src/ocre/util/unique_random_id.h @@ -0,0 +1,12 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include + +int make_unique_random_container_id(const struct ocre_context *context, char *container_id, size_t len); diff --git a/src/ocre/utils/c-smf/CMakeLists.txt b/src/ocre/utils/c-smf/CMakeLists.txt deleted file mode 100644 index da453ef4..00000000 --- a/src/ocre/utils/c-smf/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -cmake_minimum_required(VERSION 3.20) -project(smf LANGUAGES C) - -set(CMAKE_C_STANDARD 99) -set(CMAKE_C_STANDARD_REQUIRED ON) -set(CMAKE_C_EXTENSIONS OFF) - -option(BUILD_TESTS "Build test program" OFF) - -add_subdirectory(smf) - -if(BUILD_TESTS) - add_subdirectory(test) - target_link_libraries(test smf) -endif(BUILD_TESTS) diff --git a/src/ocre/utils/c-smf/LICENSE b/src/ocre/utils/c-smf/LICENSE deleted file mode 100644 index 261eeb9e..00000000 --- a/src/ocre/utils/c-smf/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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 - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/src/ocre/utils/c-smf/README.md b/src/ocre/utils/c-smf/README.md deleted file mode 100644 index 66db56ed..00000000 --- a/src/ocre/utils/c-smf/README.md +++ /dev/null @@ -1,61 +0,0 @@ -# c-smf - -## Overview -Platform-independent port of the Zephyr Real-Time Operating System (RTOS) State Machine Framework. - -## Features -Easy integration with CMake. - -Utilizes the powerful Zephyr State Machine Framework for efficient state management. - -Apache 2.0 licensed for open-source use. - -## Getting Started - -1. Clone the repository -``` -git clone https://github.com/kr-t/c-smf.git -``` - -2. Add the new directory to your CMake project and link smf library to your executable -``` -add_subdirectory(c-smf) -target_link_libraries(... smf) -``` - -3. Include the State Machine Framework in your code -``` -#include "smf/smf.h" -``` - -## Example - -Simple stand-alone example is available in /test folder. To build it, use BUILD_TESTS option. -``` -mkdir build && cd build -cmake -DBUILD_TESTS=ON .. -make -./test/test -``` - -Output: -``` -Entering State 1 -Starting State Machine -Running State 1 -Transitioning to State 2 -Exiting State 1 -Entering State 2 -Running State 2 -State Machine Test Complete -``` - -For more information, you can refer to the official Zephyr RTOS documentation for the State Machine Framework: [Zephyr SMF Documentation](https://docs.zephyrproject.org/latest/services/smf/index.html) - -## License -This project is licensed under the Apache License 2.0 - see the LICENSE file for details. - -## Acknowledments -This project is ported from Zephyr Real-Time Operating System. - -Thanks to the open-source community for their contributions. diff --git a/src/ocre/utils/c-smf/smf/CMakeLists.txt b/src/ocre/utils/c-smf/smf/CMakeLists.txt deleted file mode 100644 index d3098178..00000000 --- a/src/ocre/utils/c-smf/smf/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -add_library(smf STATIC - smf.c -) - -set_target_properties(smf PROPERTIES PUBLIC_HEADER smf.h) -target_include_directories(smf PUBLIC ..) - -target_compile_options(smf PRIVATE - -fdiagnostics-color=always - -pedantic-errors - -Wall - -Wextra - -Wconversion - -Wsign-conversion - -Werror -) diff --git a/src/ocre/utils/c-smf/smf/smf.c b/src/ocre/utils/c-smf/smf/smf.c deleted file mode 100644 index 74bc1ba6..00000000 --- a/src/ocre/utils/c-smf/smf/smf.c +++ /dev/null @@ -1,416 +0,0 @@ -/* - * Copyright 2021 The Chromium OS Authors - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include - -#include "smf.h" - -/** - * @brief Private structure (to this file) used to track state machine context. - * The structure is not used directly, but instead to cast the "internal" - * member of the smf_ctx structure. - */ -struct internal_ctx { - bool new_state : 1; - bool terminate : 1; - bool is_exit : 1; - bool handled : 1; -}; - -#ifdef CONFIG_SMF_ANCESTOR_SUPPORT -static bool share_parent(const struct smf_state *test_state, const struct smf_state *target_state) -{ - for (const struct smf_state *state = test_state; state != NULL; state = state->parent) { - if (target_state == state) { - return true; - } - } - - return false; -} - -static const struct smf_state *get_child_of(const struct smf_state *states, const struct smf_state *parent) -{ - const struct smf_state *tmp = states; - - while (true) { - if (tmp->parent == parent) { - return tmp; - } - - if (tmp->parent == NULL) { - return NULL; - } - - tmp = tmp->parent; - } -} - -static const struct smf_state *get_last_of(const struct smf_state *states) -{ - return get_child_of(states, NULL); -} - -/** - * @brief Find the Least Common Ancestor (LCA) of two states - * - * @param source transition source - * @param dest transition destination - * @return LCA state, or NULL if states have no LCA. - */ -static const struct smf_state *get_lca_of(const struct smf_state *source, const struct smf_state *dest) -{ - for (const struct smf_state *ancestor = source->parent; ancestor != NULL; ancestor = ancestor->parent) { - if (ancestor == dest) { - return ancestor->parent; - } else if (share_parent(dest, ancestor)) { - return ancestor; - } - } - - return NULL; -} - -/** - * @brief Executes all entry actions from the direct child of topmost to the new state - * - * @param ctx State machine context - * @param new_state State we are transitioning to - * @param topmost State we are entering from. Its entry action is not executed - * @return true if the state machine should terminate, else false - */ -static bool smf_execute_all_entry_actions(struct smf_ctx *const ctx, const struct smf_state *new_state, - const struct smf_state *topmost) -{ - struct internal_ctx *const internal = (void *)&ctx->internal; - - if (new_state == topmost) { - /* There are no child states, so do nothing */ - return false; - } - - for (const struct smf_state *to_execute = get_child_of(new_state, topmost); - to_execute != NULL && to_execute != new_state; to_execute = get_child_of(new_state, to_execute)) { - /* Keep track of the executing entry action in case it calls - * smf_set_state() - */ - ctx->executing = to_execute; - /* Execute every entry action EXCEPT that of the topmost state */ - if (to_execute->entry) { - to_execute->entry(ctx); - - /* No need to continue if terminate was set */ - if (internal->terminate) { - return true; - } - } - } - - /* and execute the new state entry action */ - ctx->executing = new_state; - if (new_state->entry) { - new_state->entry(ctx); - - /* No need to continue if terminate was set */ - if (internal->terminate) { - return true; - } - } - - return false; -} - -/** - * @brief Execute all ancestor run actions - * - * @param ctx State machine context - * @param target The run actions of this target's ancestors are executed - * @return true if the state machine should terminate, else false - */ -static bool smf_execute_ancestor_run_actions(struct smf_ctx *ctx) -{ - struct internal_ctx *const internal = (void *)&ctx->internal; - /* Execute all run actions in reverse order */ - - /* Return if the current state terminated */ - if (internal->terminate) { - return true; - } - - /* The child state either transitioned or handled it. Either way, stop propagating. */ - if (internal->new_state || internal->handled) { - return false; - } - - /* Try to run parent run actions */ - for (const struct smf_state *tmp_state = ctx->current->parent; tmp_state != NULL; - tmp_state = tmp_state->parent) { - /* Keep track of where we are in case an ancestor calls smf_set_state() */ - ctx->executing = tmp_state; - /* Execute parent run action */ - if (tmp_state->run) { - enum smf_state_result rc = tmp_state->run(ctx); - - if (rc == SMF_EVENT_HANDLED) { - internal->handled = true; - } - /* No need to continue if terminate was set */ - if (internal->terminate) { - return true; - } - - /* This state dealt with it. Stop propagating. */ - if (internal->new_state || internal->handled) { - break; - } - } - } - - /* All done executing the run actions */ - - return false; -} - -/** - * @brief Executes all exit actions from ctx->current to the direct child of topmost - * - * @param ctx State machine context - * @param topmost State we are exiting to. Its exit action is not executed - * @return true if the state machine should terminate, else false - */ -static bool smf_execute_all_exit_actions(struct smf_ctx *const ctx, const struct smf_state *topmost) -{ - struct internal_ctx *const internal = (void *)&ctx->internal; - - for (const struct smf_state *to_execute = ctx->current; to_execute != NULL && to_execute != topmost; - to_execute = to_execute->parent) { - if (to_execute->exit) { - to_execute->exit(ctx); - - /* No need to continue if terminate was set in the exit action */ - if (internal->terminate) { - return true; - } - } - } - - return false; -} -#endif /* CONFIG_SMF_ANCESTOR_SUPPORT */ - -/** - * @brief Reset the internal state of the state machine back to default values. - * Should be called on entry to smf_set_initial() and smf_set_state(). - * - * @param ctx State machine context. - */ -static void smf_clear_internal_state(struct smf_ctx *ctx) -{ - struct internal_ctx *const internal = (void *)&ctx->internal; - - internal->is_exit = false; - internal->terminate = false; - internal->handled = false; - internal->new_state = false; -} - -void smf_set_initial(struct smf_ctx *ctx, const struct smf_state *init_state) -{ -#ifdef CONFIG_SMF_INITIAL_TRANSITION - /* - * The final target will be the deepest leaf state that - * the target contains. Set that as the real target. - */ - while (init_state->initial) { - init_state = init_state->initial; - } -#endif - - smf_clear_internal_state(ctx); - ctx->current = init_state; - ctx->previous = NULL; - ctx->terminate_val = 0; - -#ifdef CONFIG_SMF_ANCESTOR_SUPPORT - struct internal_ctx *const internal = (void *)&ctx->internal; - - ctx->executing = init_state; - const struct smf_state *topmost = get_last_of(init_state); - - /* Execute topmost state entry action, since smf_execute_all_entry_actions() - * doesn't - */ - if (topmost->entry) { - topmost->entry(ctx); - if (internal->terminate) { - /* No need to continue if terminate was set */ - return; - } - } - - if (smf_execute_all_entry_actions(ctx, init_state, topmost)) { - /* No need to continue if terminate was set */ - return; - } -#else - /* execute entry action if it exists */ - if (init_state->entry) { - init_state->entry(ctx); - } -#endif -} - -void smf_set_state(struct smf_ctx *const ctx, const struct smf_state *new_state) -{ - struct internal_ctx *const internal = (void *)&ctx->internal; - - if (new_state == NULL) { - printf("new_state cannot be NULL"); - return; - } - - /* - * It does not make sense to call smf_set_state in an exit phase of a state - * since we are already in a transition; we would always ignore the - * intended state to transition into. - */ - if (internal->is_exit) { - printf("Calling %s from exit action", __func__); - return; - } - -#ifdef CONFIG_SMF_ANCESTOR_SUPPORT - const struct smf_state *topmost; - - if (share_parent(ctx->executing, new_state)) { - /* new state is a parent of where we are now*/ - topmost = new_state; - } else if (share_parent(new_state, ctx->executing)) { - /* we are a parent of the new state */ - topmost = ctx->executing; - } else { - /* not directly related, find LCA */ - topmost = get_lca_of(ctx->executing, new_state); - } - - internal->is_exit = true; - internal->new_state = true; - - /* call all exit actions up to (but not including) the topmost */ - if (smf_execute_all_exit_actions(ctx, topmost)) { - /* No need to continue if terminate was set in the exit action */ - return; - } - - /* if self-transition, call the exit action */ - if ((ctx->executing == new_state) && (new_state->exit)) { - new_state->exit(ctx); - - /* No need to continue if terminate was set in the exit action */ - if (internal->terminate) { - return; - } - } - - internal->is_exit = false; - - /* if self transition, call the entry action */ - if ((ctx->executing == new_state) && (new_state->entry)) { - new_state->entry(ctx); - - /* No need to continue if terminate was set in the entry action */ - if (internal->terminate) { - return; - } - } -#ifdef CONFIG_SMF_INITIAL_TRANSITION - /* - * The final target will be the deepest leaf state that - * the target contains. Set that as the real target. - */ - while (new_state->initial) { - new_state = new_state->initial; - } -#endif - - /* update the state variables */ - ctx->previous = ctx->current; - ctx->current = new_state; - - /* call all entry actions (except those of topmost) */ - if (smf_execute_all_entry_actions(ctx, new_state, topmost)) { - /* No need to continue if terminate was set in the entry action */ - return; - } -#else - /* Flat state machines have a very simple transition: */ - if (ctx->current->exit) { - internal->is_exit = true; - ctx->current->exit(ctx); - /* No need to continue if terminate was set in the exit action */ - if (internal->terminate) { - return; - } - internal->is_exit = false; - } - /* update the state variables */ - ctx->previous = ctx->current; - ctx->current = new_state; - - if (ctx->current->entry) { - ctx->current->entry(ctx); - /* No need to continue if terminate was set in the entry action */ - if (internal->terminate) { - return; - } - } -#endif -} - -void smf_set_terminate(struct smf_ctx *ctx, int32_t val) -{ - struct internal_ctx *const internal = (void *)&ctx->internal; - - internal->terminate = true; - ctx->terminate_val = val; -} - -int32_t smf_run_state(struct smf_ctx *const ctx) -{ - struct internal_ctx *const internal = (void *)&ctx->internal; - - /* No need to continue if terminate was set */ - if (internal->terminate) { - return ctx->terminate_val; - } - - /* Executing a states run function could cause a transition, so clear the - * internal state to ensure that the transition is handled correctly. - */ - smf_clear_internal_state(ctx); - -#ifdef CONFIG_SMF_ANCESTOR_SUPPORT - ctx->executing = ctx->current; - if (ctx->current->run) { - enum smf_state_result rc = ctx->current->run(ctx); - - if (rc == SMF_EVENT_HANDLED) { - internal->handled = true; - } - } - - if (smf_execute_ancestor_run_actions(ctx)) { - return ctx->terminate_val; - } -#else - if (ctx->current->run) { - ctx->current->run(ctx); - } -#endif - return 0; -} diff --git a/src/ocre/utils/c-smf/smf/smf.h b/src/ocre/utils/c-smf/smf/smf.h deleted file mode 100644 index e1d7d1e9..00000000 --- a/src/ocre/utils/c-smf/smf/smf.h +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright 2021 The Chromium OS Authors - * - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @file - * - * @brief State Machine Framework header file - */ - -#ifndef ZEPHYR_INCLUDE_SMF_H_ -#define ZEPHYR_INCLUDE_SMF_H_ - -#include - -#define CONFIG_SMF_ANCESTOR_SUPPORT -#define CONFIG_SMF_INITIAL_TRANSITION - -/** - * @brief State Machine Framework API - * @defgroup smf State Machine Framework API - * @version 0.2.0 - * @ingroup os_services - * @{ - */ - -/** - * @brief Macro to create a hierarchical state with initial transitions. - * - * @param _entry State entry function or NULL - * @param _run State run function or NULL - * @param _exit State exit function or NULL - * @param _parent State parent object or NULL - * @param _initial State initial transition object or NULL - */ -/* clang-format off */ -#define SMF_CREATE_STATE(_entry, _run, _exit, _parent, _initial) \ -{ \ - .entry = _entry, \ - .run = _run, \ - .exit = _exit, \ - .parent = _parent, \ - .initial = _initial \ -} -/* clang-format on */ - -/** - * @brief Macro to cast user defined object to state machine - * context. - * - * @param o A pointer to the user defined object - */ -#define SMF_CTX(o) ((struct smf_ctx *)o) - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief enum for the return value of a state_execution function - */ -enum smf_state_result { - SMF_EVENT_HANDLED, - SMF_EVENT_PROPAGATE, -}; - -/** - * @brief Function pointer that implements a entry and exit actions - * of a state - * - * @param obj pointer user defined object - */ -typedef void (*state_method)(void *obj); - -/** - * @brief Function pointer that implements a the run action of a state - * - * @param obj pointer user defined object - * @return If the event should be propagated to parent states or not - * (Ignored when CONFIG_SMF_ANCESTOR_SUPPORT not defined) - */ -typedef enum smf_state_result (*state_execution)(void *obj); - -/** General state that can be used in multiple state machines. */ -struct smf_state { - /** Optional method that will be run when this state is entered */ - const state_method entry; - - /** - * Optional method that will be run repeatedly during state machine - * loop. - */ - const state_execution run; - - /** Optional method that will be run when this state exists */ - const state_method exit; -#ifdef CONFIG_SMF_ANCESTOR_SUPPORT - /** - * Optional parent state that contains common entry/run/exit - * implementation among various child states. - * entry: Parent function executes BEFORE child function. - * run: Parent function executes AFTER child function. - * exit: Parent function executes AFTER child function. - * - * Note: When transitioning between two child states with a shared - * parent, that parent's exit and entry functions do not execute. - */ - const struct smf_state *parent; - -#ifdef CONFIG_SMF_INITIAL_TRANSITION - /** - * Optional initial transition state. NULL for leaf states. - */ - const struct smf_state *initial; -#endif /* CONFIG_SMF_INITIAL_TRANSITION */ -#endif /* CONFIG_SMF_ANCESTOR_SUPPORT */ -}; - -/** Defines the current context of the state machine. */ -struct smf_ctx { - /** Current state the state machine is executing. */ - const struct smf_state *current; - /** Previous state the state machine executed */ - const struct smf_state *previous; - -#ifdef CONFIG_SMF_ANCESTOR_SUPPORT - /** Currently executing state (which may be a parent) */ - const struct smf_state *executing; -#endif /* CONFIG_SMF_ANCESTOR_SUPPORT */ - /** - * This value is set by the set_terminate function and - * should terminate the state machine when its set to a - * value other than zero when it's returned by the - * run_state function. - */ - int32_t terminate_val; - /** - * The state machine casts this to a "struct internal_ctx" and it's - * used to track state machine context - */ - uint32_t internal; -}; - -/** - * @brief Initializes the state machine and sets its initial state. - * - * @param ctx State machine context - * @param init_state Initial state the state machine starts in. - */ -void smf_set_initial(struct smf_ctx *ctx, const struct smf_state *init_state); - -/** - * @brief Changes a state machines state. This handles exiting the previous - * state and entering the target state. For HSMs the entry and exit - * actions of the Least Common Ancestor will not be run. - * - * @param ctx State machine context - * @param new_state State to transition to (NULL is valid and exits all states) - */ -void smf_set_state(struct smf_ctx *ctx, const struct smf_state *new_state); - -/** - * @brief Terminate a state machine - * - * @param ctx State machine context - * @param val Non-Zero termination value that's returned by the smf_run_state - * function. - */ -void smf_set_terminate(struct smf_ctx *ctx, int32_t val); - -/** - * @brief Runs one iteration of a state machine (including any parent states) - * - * @param ctx State machine context - * @return A non-zero value should terminate the state machine. This - * non-zero value could represent a terminal state being reached - * or the detection of an error that should result in the - * termination of the state machine. - */ -int32_t smf_run_state(struct smf_ctx *ctx); - -#ifdef __cplusplus -} -#endif - -/** - * @} - */ - -#endif /* ZEPHYR_INCLUDE_SMF_H_ */ diff --git a/src/ocre/utils/c-smf/test/CMakeLists.txt b/src/ocre/utils/c-smf/test/CMakeLists.txt deleted file mode 100644 index 6c2278f0..00000000 --- a/src/ocre/utils/c-smf/test/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -add_executable(test - test.c -) - -target_compile_options(test PRIVATE - -fdiagnostics-color=always - -pedantic-errors - -Wall - -Wextra - -Wconversion - -Wsign-conversion - -Werror -) diff --git a/src/ocre/utils/c-smf/test/test.c b/src/ocre/utils/c-smf/test/test.c deleted file mode 100644 index 25440810..00000000 --- a/src/ocre/utils/c-smf/test/test.c +++ /dev/null @@ -1,80 +0,0 @@ -#include -#include "../smf/smf.h" - -// Define state entry, exit, and run actions -void state1_entry(void *ctx) -{ - (void)ctx; /* -Wunused-parameter */ - printf("Entering State 1\n"); -} - -void state1_exit(void *ctx) -{ - (void)ctx; /* -Wunused-parameter */ - printf("Exiting State 1\n"); -} - -void state1_run(void *ctx) -{ - (void)ctx; /* -Wunused-parameter */ - printf("Running State 1\n"); -} - -void state2_entry(void *ctx) -{ - (void)ctx; /* -Wunused-parameter */ - printf("Entering State 2\n"); -} - -void state2_exit(void *ctx) -{ - (void)ctx; /* -Wunused-parameter */ - printf("Exiting State 2\n"); -} - -void state2_run(void *ctx) -{ - (void)ctx; /* -Wunused-parameter */ - printf("Running State 2\n"); -} - -// Define states -struct smf_state state1 = { - .entry = state1_entry, - .run = state1_run, - .exit = state1_exit, -#ifdef CONFIG_SMF_ANCESTOR_SUPPORT - .parent = NULL, -#endif /* CONFIG_SMF_ANCESTOR_SUPPORT */ -}; - -struct smf_state state2 = { - .entry = state2_entry, - .run = state2_run, - .exit = state2_exit, -#ifdef CONFIG_SMF_ANCESTOR_SUPPORT - .parent = NULL, -#endif /* CONFIG_SMF_ANCESTOR_SUPPORT */ -}; - -int main() -{ - struct smf_ctx ctx; - - // Initialize the state machine with the initial state - smf_set_initial(&ctx, &state1); - - // Run the initial state - printf("Starting State Machine\n"); - smf_run_state(&ctx); - - // Transition to a new state - printf("Transitioning to State 2\n"); - smf_set_state(&ctx, &state2); - - // Run the new state - smf_run_state(&ctx); - - printf("State Machine Test Complete\n"); - return 0; -} \ No newline at end of file diff --git a/src/ocre/version.h b/src/ocre/version.h new file mode 100644 index 00000000..6e1ad644 --- /dev/null +++ b/src/ocre/version.h @@ -0,0 +1,8 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define OCRE_VERSION_STRING "0.7.0" diff --git a/src/platform/include/ocre/platform/file.h b/src/platform/include/ocre/platform/file.h new file mode 100644 index 00000000..e124b038 --- /dev/null +++ b/src/platform/include/ocre/platform/file.h @@ -0,0 +1,11 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +void *ocre_load_file(const char *path, size_t *size); +int ocre_unload_file(void *buffer, size_t size); diff --git a/src/platform/include/ocre/platform/memory.h b/src/platform/include/ocre/platform/memory.h new file mode 100644 index 00000000..15ba7df2 --- /dev/null +++ b/src/platform/include/ocre/platform/memory.h @@ -0,0 +1,12 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +void *user_malloc(size_t size); +void user_free(void *p); +void *user_realloc(void *p, size_t size); diff --git a/src/platform/posix/CMakeLists.txt b/src/platform/posix/CMakeLists.txt new file mode 100644 index 00000000..e0f19383 --- /dev/null +++ b/src/platform/posix/CMakeLists.txt @@ -0,0 +1,20 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +add_library(OcrePlatform) + +target_sources(OcrePlatform + PRIVATE + memory.c + log.c + file_mmap.c + # file_alloc_read.c +) + +target_include_directories(OcrePlatform + PUBLIC + include + ../include +) diff --git a/src/platform/posix/file_alloc_fread.c b/src/platform/posix/file_alloc_fread.c new file mode 100644 index 00000000..83fcc9b7 --- /dev/null +++ b/src/platform/posix/file_alloc_fread.c @@ -0,0 +1,105 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +LOG_MODULE_REGISTER(file_alloc_fread, CONFIG_OCRE_LOG_LEVEL); + +void *ocre_load_file(const char *path, size_t *size) +{ + if (!path) { + LOG_ERR("Invalid arguments"); + goto error; + } + + FILE *fp = fopen(path, "rb"); + if (!fp) { + LOG_ERR("Failed to open file '%s' errno=%d", path, errno); + goto error; + } + + int fd = fileno(fp); + if (fd < 0) { + LOG_ERR("Failed to get file descriptor errno=%d", errno); + goto close_file; + } + + struct stat finfo; + + int rc; + rc = fstat(fd, &finfo); + if (rc < 0) { + LOG_ERR("Failed to get file info: rc=%d errno=%d", rc, errno); + goto close_file; + } + + size_t file_size = (size_t)finfo.st_size; + + LOG_INF("File size to load: %zu", file_size); + + if (!file_size) { + LOG_ERR("File is empty"); + goto close_file; + } + + void *buffer = user_malloc(file_size); + if (!buffer) { + LOG_ERR("Failed to allocate memory for file buffer size=%zu errno=%d", file_size, errno); + goto close_file; + } + + memset(buffer, 0, file_size); + + /* Read from file into buffer */ + + size_t bytes_read = fread(buffer, 1, file_size, fp); + + if (bytes_read != file_size) { + LOG_ERR("Failed to read file bytes_read=%zu errno=%d", bytes_read, errno); + goto free_buffer; + } + + rc = fclose(fp); + if (rc) { + LOG_ERR("Failed to close file rc=%d errno=%d", rc, errno); + } + + *size = file_size; + + LOG_INF("File '%s' (size=%zu) loaded successfully", path, file_size); + + return buffer; + +free_buffer: + user_free(buffer); + +close_file: + rc = fclose(fp); + if (rc) { + LOG_ERR("Failed to close file: rc=%d errno=%d", rc, errno); + } + +error: + return NULL; +} + +int ocre_unload_file(void *buffer, size_t size) +{ + user_free(buffer); + + return 0; +} diff --git a/src/platform/posix/file_alloc_read.c b/src/platform/posix/file_alloc_read.c new file mode 100644 index 00000000..64a0700a --- /dev/null +++ b/src/platform/posix/file_alloc_read.c @@ -0,0 +1,97 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +LOG_MODULE_REGISTER(file_alloc_read, CONFIG_OCRE_LOG_LEVEL); + +void *ocre_load_file(const char *path, size_t *size) +{ + if (!path) { + LOG_ERR("Invalid arguments"); + goto error; + } + + int fd = open(path, O_RDONLY); + if (fd < 0) { + LOG_ERR("Failed to open file '%s' errno=%d", path, errno); + goto error; + } + + struct stat finfo; + int rc = fstat(fd, &finfo); + if (rc < 0) { + LOG_ERR("Failed to get file info rc=%d errno=%d", rc, errno); + goto close_file; + } + + ssize_t file_size = (ssize_t)finfo.st_size; + + LOG_INF("File size to load: %zu", file_size); + + if (!file_size) { + LOG_ERR("File is empty"); + goto close_file; + } + + void *buffer = user_malloc(file_size); + if (!buffer) { + LOG_ERR("Failed to allocate memory for file buffer size=%zu errno=%d", file_size, errno); + goto close_file; + } + + memset(buffer, 0, file_size); + + /* Read from file into buffer */ + + ssize_t bytes_read = read(fd, buffer, file_size); + + if (bytes_read != file_size) { + LOG_ERR("Failed to read file bytes_read=%zu errno=%d", bytes_read, errno); + goto free_buffer; + } + + rc = close(fd); + if (rc) { + LOG_ERR("Failed to close file rc=%d errno=%d", rc, errno); + } + + *size = file_size; + + LOG_INF("File '%s' (size=%zu) loaded successfully", path, file_size); + + return buffer; + +free_buffer: + user_free(buffer); + +close_file: + rc = close(fd); + if (rc) { + LOG_ERR("Failed to close file: rc=%d errno=%d", rc, errno); + } + +error: + return NULL; +} + +int ocre_unload_file(void *buffer, size_t size) +{ + user_free(buffer); + + return 0; +} diff --git a/src/platform/posix/file_mmap.c b/src/platform/posix/file_mmap.c new file mode 100644 index 00000000..58f253f7 --- /dev/null +++ b/src/platform/posix/file_mmap.c @@ -0,0 +1,100 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +LOG_MODULE_REGISTER(file_mmap, CONFIG_OCRE_LOG_LEVEL); + +void *ocre_load_file(const char *path, size_t *size) +{ + int save_errno; + void *buffer = NULL; + + if (!path) { + LOG_ERR("Invalid arguments"); + save_errno = EINVAL; + goto error_errno; + } + + FILE *fp = fopen(path, "rb"); + if (!fp) { + save_errno = errno; + LOG_ERR("Failed to open file '%s': errno=%d", path, save_errno); + goto error_errno; + } + + int fd = fileno(fp); + if (fd < 0) { + save_errno = errno; + LOG_ERR("Failed to get file descriptor (fd=%d): errno=%d", fd, save_errno); + goto error_close; + } + + struct stat finfo; + int rc = fstat(fd, &finfo); + if (rc < 0) { + save_errno = errno; + LOG_ERR("Failed to get file info: rc=%d errno=%d", rc, save_errno); + goto error_close; + } + + size_t file_size = finfo.st_size; + if (!file_size) { + LOG_WRN("File is empty"); + } + + buffer = mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + if (!buffer || buffer == MAP_FAILED) { + save_errno = errno; + LOG_ERR("Failed to mmap file %zu errno=%d", file_size, errno); + goto error_close; + } + + if (fclose(fp) != 0) { + LOG_ERR("Failed to close file errno=%d", errno); + } + + *size = file_size; + + LOG_INF("File '%s' (size=%zu) mmapped successfully", path, file_size); + + return buffer; + +error_close: + fclose(fp); + +error_errno: + errno = save_errno; + + return NULL; +} + +int ocre_unload_file(void *buffer, size_t size) +{ + if (!buffer) { + return 0; + } + + if (munmap(buffer, size)) { + int save_errno = errno; + LOG_ERR("Failed to munmap file errno=%d", save_errno); + errno = save_errno; + return -1; + } + + return 0; +} diff --git a/src/platform/posix/include/ocre/platform/config.h b/src/platform/posix/include/ocre/platform/config.h new file mode 100644 index 00000000..2d6cd6c7 --- /dev/null +++ b/src/platform/posix/include/ocre/platform/config.h @@ -0,0 +1,28 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef OCRE_PLATFORM_POSIX_H +#define OCRE_PLATFORM_POSIX_H + +#define CONFIG_OCRE_DEFAULT_WORKING_DIRECTORY "src/ocre/var/lib/ocre" + +#define CONFIG_OCRE_WAMR_INTERPRETER 1 +#define CONFIG_OCRE_WAMR_AOT 1 +#define CONFIG_OCRE_TIMER 1 +#define CONFIG_OCRE_MAX_TIMERS 5 +#define CONFIG_OCRE_LOG_LEVEL_DEFAULT 3 +#define CONFIG_OCRE_LOG_LEVEL 4 +#define CONFIG_OCRE_NETWORKING 1 +#define CONFIG_OCRE_FILESYSTEM 1 +#define CONFIG_OCRE_CONTAINER_MESSAGING 1 +#define CONFIG_OCRE_MESSAGING_MAX_SUBSCRIPTIONS 32 +#define CONFIG_OCRE_SHARED_HEAP 1 +#define CONFIG_OCRE_SHARED_HEAP_BUF_VIRTUAL 1 +#define CONFIG_OCRE_SHARED_HEAP_BUF_SIZE 131072 +#define CONFIG_OCRE_WAMR_LOG_LEVEL 2 + +#endif /* OCRE_PLATFORM_POSIX_H */ diff --git a/src/platform/posix/include/ocre/platform/log.h b/src/platform/posix/include/ocre/platform/log.h new file mode 100644 index 00000000..8b838c81 --- /dev/null +++ b/src/platform/posix/include/ocre/platform/log.h @@ -0,0 +1,63 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "config.h" + +#define LOG_MODULE_REGISTER(module, ...) static const char *const __ocre_log_module = #module +#define LOG_MODULE_DECLARE(module, ...) static const char *const __ocre_log_module = #module + +extern int __ocre_log_level; + +#if CONFIG_OCRE_LOG_LEVEL >= 1 +#define LOG_ERR(fmt, ...) \ + do { \ + if (__ocre_log_level >= 1) \ + fprintf(stderr, " %s: " fmt "\n", __ocre_log_module, ##__VA_ARGS__); \ + } while (0) +#else +#define LOG_ERR(fmt, ...) \ + do { \ + } while (0) +#endif + +#if CONFIG_OCRE_LOG_LEVEL >= 2 +#define LOG_WRN(fmt, ...) \ + do { \ + if (__ocre_log_level >= 2) \ + fprintf(stderr, " %s: " fmt "\n", __ocre_log_module, ##__VA_ARGS__); \ + } while (0) +#else +#define LOG_WRN(fmt, ...) \ + do { \ + } while (0) +#endif + +#if CONFIG_OCRE_LOG_LEVEL >= 3 +#define LOG_INF(fmt, ...) \ + do { \ + if (__ocre_log_level >= 3) \ + fprintf(stderr, " %s: " fmt "\n", __ocre_log_module, ##__VA_ARGS__); \ + } while (0) +#else +#define LOG_INF(fmt, ...) \ + do { \ + } while (0) +#endif + +#if CONFIG_OCRE_LOG_LEVEL >= 4 +#define LOG_DBG(fmt, ...) \ + do { \ + if (__ocre_log_level >= 4) \ + fprintf(stderr, " %s: " fmt "\n", __ocre_log_module, ##__VA_ARGS__); \ + } while (0) +#else +#define LOG_DBG(fmt, ...) \ + do { \ + } while (0) +#endif diff --git a/src/platform/posix/log.c b/src/platform/posix/log.c new file mode 100644 index 00000000..997346d9 --- /dev/null +++ b/src/platform/posix/log.c @@ -0,0 +1,10 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +int __ocre_log_level = CONFIG_OCRE_LOG_LEVEL_DEFAULT; diff --git a/src/platform/posix/memory.c b/src/platform/posix/memory.c new file mode 100644 index 00000000..35e60838 --- /dev/null +++ b/src/platform/posix/memory.c @@ -0,0 +1,24 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +void *user_malloc(size_t size) +{ + return malloc(size); +} + +void user_free(void *p) +{ + free(p); +} + +void *user_realloc(void *p, size_t size) +{ + return realloc(p, size); +} diff --git a/src/platform/zephyr/CMakeLists.txt b/src/platform/zephyr/CMakeLists.txt new file mode 100644 index 00000000..90243df3 --- /dev/null +++ b/src/platform/zephyr/CMakeLists.txt @@ -0,0 +1,23 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +add_library(OcrePlatform) + +target_sources(OcrePlatform + PRIVATE + memory.c + ../posix/file_alloc_read.c +) + +target_include_directories(OcrePlatform + PUBLIC + include + ../include +) + +target_link_libraries(OcrePlatform + PUBLIC + zephyr_interface +) diff --git a/src/platform/zephyr/include/ocre/platform/config.h b/src/platform/zephyr/include/ocre/platform/config.h new file mode 100644 index 00000000..c3036fb8 --- /dev/null +++ b/src/platform/zephyr/include/ocre/platform/config.h @@ -0,0 +1,8 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* Zephyr includes autoconf.h automatically */ diff --git a/src/platform/zephyr/include/ocre/platform/log.h b/src/platform/zephyr/include/ocre/platform/log.h new file mode 100644 index 00000000..b5b52021 --- /dev/null +++ b/src/platform/zephyr/include/ocre/platform/log.h @@ -0,0 +1,8 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include diff --git a/src/platform/zephyr/memory.c b/src/platform/zephyr/memory.c new file mode 100644 index 00000000..d98b89ef --- /dev/null +++ b/src/platform/zephyr/memory.c @@ -0,0 +1,49 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include + +#ifdef CONFIG_SHARED_MULTI_HEAP +void *user_malloc(size_t size) +{ + return shared_multi_heap_aligned_alloc(SMH_REG_ATTR_EXTERNAL, 32, size); +} + +void user_free(void *ptr) +{ + shared_multi_heap_free(ptr); +} + +void *user_realloc(void *ptr, size_t size) +{ + // TODO + return NULL; +} + +#else + +#warning CONFIG_SHARED_MULTI_HEAP is not defined. Using internal RAM + +void *user_malloc(size_t size) +{ + return malloc(size); +} + +void user_free(void *ptr) +{ + free(ptr); +} + +void *user_realloc(void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +#endif diff --git a/src/runtime/CMakeLists.txt b/src/runtime/CMakeLists.txt new file mode 100644 index 00000000..35a2b740 --- /dev/null +++ b/src/runtime/CMakeLists.txt @@ -0,0 +1,16 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20) + +add_library(OcreRuntime + INTERFACE + include/ocre/runtime/vtable.h +) + +target_include_directories(OcreRuntime + INTERFACE + include +) diff --git a/src/runtime/include/ocre/runtime/vtable.h b/src/runtime/include/ocre/runtime/vtable.h new file mode 100644 index 00000000..2410af81 --- /dev/null +++ b/src/runtime/include/ocre/runtime/vtable.h @@ -0,0 +1,153 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef OCRE_RUNTIME_VTABLE_H +#define OCRE_RUNTIME_VTABLE_H + +#include +#include +#include + +/** + * @brief Runtime Engine Virtual Table + * @headerfile vtable.h + * + * This is the interface between the ocre container and the runtime engine. + * + * These functions are guaranteed to be operated according to the Ocre Container lifecycle + * specification. + * + * Please refer to the full documentation for more details. + * + * Refer to the wamr engine implementation for an example of how to implement this interface. + */ +struct ocre_runtime_vtable { + /** + * @brief Runtime Engine Name + * + * This is the name of the runtime engine. Should be a zero-terminated string. + * + * It should be unique among all registered runtime engines. Please refer to the full + * documentation for the list of assigned runtime engine names. + */ + const char *const runtime_name; + + /** + * @brief Initialize the runtime engine + * + * This function is called when the runtime engine is initialized. It is called only once on + * start and before any other functions are called. + * + * @return 0 on success, non-zero on failure + */ + int (*init)(void); + + /** + * @brief Deinitialize the runtime engine + * + * This function is called when the runtime engine is deinitialized. + * + * @return 0 on success, non-zero on failure + */ + int (*deinit)(void); + + /** + * @brief Create a new runtime instance + * + * This function is called when a new runtime instance is created. + * + * This function will return an opaque pointer to the context of the runtime instance. It + * can be anything managed internally by the runtime. It cannot be NULL, as it will indicate + * error. This value is passed as a parameter to the other functions in this interface. + * + * @param img_path Absolute path to the image file + * @param workdir Working directory for the runtime instance. Can be NULL. + * @param capabilities A NULL-terminated array of capabilities for the runtime instance + * @param argv A NULL-terminated array of command line arguments to be passed to the + * container + * @param envp A NULL-terminated array of environment variables to be passed to the + * container + * @param mounts Mount points for the runtime instance + * + * @return Pointer to the runtime context on success, NULL on failure + */ + void *(*create)(const char *img_path, const char *workdir, const char **capabilities, const char **argv, + const char **envp, const char **mounts); + + /** + * @brief Destroy a runtime instance + * + * This function is called when a runtime instance is destroyed. This is guaranteed to be + * called only when the container should not be running or paused. + * + * @param runtime_context Pointer to the runtime context + * @return 0 on success, non-zero on failure + */ + int (*destroy)(void *runtime_context); + + /** + * @brief Thread execute function for the runtime. + * + * This function is called inside a thread when the container is started. It should execute + * the container's main function, block and eventually return the exit code. + * + * @param runtime_context Pointer to the runtime context returned by create + * @param sem Pointer to the semaphore to post to when the instance is ready to + * be killed + * + * @return status code of the execution: 0 on success, non-zero on failure + */ + + int (*thread_execute)(void *runtime_context, sem_t *sem); + + /** + * @brief Stop a runtime instance + * + * This function is called when a runtime instance is requested to stop. This is guaranteed + * to be called only when the container is running or paused. + * + * @param runtime_context Pointer to the runtime context + * @return 0 on success, non-zero on failure + */ + int (*stop)(void *runtime_context); + + /** + * @brief Kill a runtime instance + * + * This function is called when a runtime instance is requested to be killed. This is + * guaranteed to be called only when the container is running. This should terminate the + * container immediately. + * + * @param runtime_context Pointer to the runtime context + * @return 0 on success, non-zero on failure + */ + int (*kill)(void *runtime_context); + + /** + * @brief Pause a runtime instance + * + * This function is called when a runtime instance is requested to be paused. This is + * guaranteed to be called only when the container is running. + * + * @param runtime_context Pointer to the runtime context + * @return 0 on success, non-zero on failure + */ + int (*pause)(void *runtime_context); + + /** + * @brief Unpause a runtime instance + * + * This function is called when a runtime instance is requested to be unpaused. This is + * guaranteed to be called only when the container is paused. + * + * @param runtime_context Pointer to the runtime context + * @return 0 on success, non-zero on failure + */ + int (*unpause)(void *runtime_context); +}; + +#endif /* OCRE_RUNTIME_VTABLE_H */ diff --git a/src/runtime/wamr-wasip1/CMakeLists.txt b/src/runtime/wamr-wasip1/CMakeLists.txt new file mode 100644 index 00000000..25286ba4 --- /dev/null +++ b/src/runtime/wamr-wasip1/CMakeLists.txt @@ -0,0 +1,26 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +add_library(OcreRuntimeWamr) + +target_sources(OcreRuntimeWamr + PRIVATE + wamr.c +) + +target_include_directories(OcreRuntimeWamr + PUBLIC + include +) + +target_link_libraries(OcreRuntimeWamr + PUBLIC + OcreRuntime + OcrePlatform + OcreRuntimeAPI + vmlib +) + +add_subdirectory(ocre_api) diff --git a/src/runtime/wamr-wasip1/include/ocre/runtime/wamr/wasip1.h b/src/runtime/wamr-wasip1/include/ocre/runtime/wamr/wasip1.h new file mode 100644 index 00000000..bca970f6 --- /dev/null +++ b/src/runtime/wamr-wasip1/include/ocre/runtime/wamr/wasip1.h @@ -0,0 +1,15 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef OCRE_RUNTIME_WAMR_H +#define OCRE_RUNTIME_WAMR_H + +#include + +extern const struct ocre_runtime_vtable wamr_vtable; + +#endif /* OCRE_RUNTIME_WAMR_H */ diff --git a/src/runtime/wamr-wasip1/ocre_api/CMakeLists.txt b/src/runtime/wamr-wasip1/ocre_api/CMakeLists.txt new file mode 100644 index 00000000..b967413e --- /dev/null +++ b/src/runtime/wamr-wasip1/ocre_api/CMakeLists.txt @@ -0,0 +1,33 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +add_library(OcreRuntimeAPI) + +target_sources(OcreRuntimeAPI + PRIVATE + ocre_api.c + ocre_common.c + ocre_timers/ocre_timer.c + ocre_messaging/ocre_messaging.c + utils/strlcat.c + core/core_eventq.c + core/core_misc.c + core/core_timer.c + core/core_mutex.c + core/core_memory.c +) + +if (CONFIG_OCRE_GPIO) + target_sources(OcreRuntimeAPI + PRIVATE + ocre_gpio/ocre_gpio.c + ) +endif() + +target_link_libraries(OcreRuntimeAPI + PUBLIC + OcrePlatform + vmlib +) diff --git a/src/shared/platform/posix/core_eventq.c b/src/runtime/wamr-wasip1/ocre_api/core/core_eventq.c similarity index 97% rename from src/shared/platform/posix/core_eventq.c rename to src/runtime/wamr-wasip1/ocre_api/core/core_eventq.c index 80119368..9ecbfe51 100644 --- a/src/shared/platform/posix/core_eventq.c +++ b/src/runtime/wamr-wasip1/ocre_api/core/core_eventq.c @@ -5,9 +5,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "ocre_core_external.h" #include #include +#include + +#include "core_external.h" int core_eventq_init(core_eventq_t *eventq, size_t item_size, size_t max_items) { diff --git a/src/shared/platform/ocre_core_external.h b/src/runtime/wamr-wasip1/ocre_api/core/core_external.h similarity index 95% rename from src/shared/platform/ocre_core_external.h rename to src/runtime/wamr-wasip1/ocre_api/core/core_external.h index 1b852125..b1435174 100644 --- a/src/shared/platform/ocre_core_external.h +++ b/src/runtime/wamr-wasip1/ocre_api/core/core_external.h @@ -12,11 +12,7 @@ #include #include -#ifdef __ZEPHYR__ -#include "zephyr/core_internal.h" -#else -#include "posix/core_internal.h" -#endif +#include "core_internal.h" #define OCRE_MODULE_NAME_LEN 16 @@ -121,13 +117,6 @@ int core_mq_send(core_mq_t *mq, const void *data, size_t msg_len); */ int core_mq_recv(core_mq_t *mq, void *data); -/** - * @brief Sleep for a specified number of milliseconds. - * - * @param milliseconds Number of milliseconds to sleep. - */ -void core_sleep_ms(int milliseconds); - /** * @brief Allocate memory. * @@ -143,11 +132,6 @@ void *core_malloc(size_t size); */ void core_free(void *ptr); -/** - * @brief Yield the current thread's execution. - */ -void core_yield(void); - typedef struct core_timer core_timer_t; /** @@ -178,6 +162,14 @@ int core_timer_start(core_timer_t *timer, int timeout_ms, int period_ms); */ int core_timer_stop(core_timer_t *timer); +/** + * @brief Deletes a timer. + * + * @param timer Pointer to the timer structure. + * @return 0 on success, negative value on error. + */ +int core_timer_delete(core_timer_t *timer); + /** * @brief Get file status (size). * diff --git a/src/runtime/wamr-wasip1/ocre_api/core/core_internal.h b/src/runtime/wamr-wasip1/ocre_api/core/core_internal.h new file mode 100644 index 00000000..ba915f37 --- /dev/null +++ b/src/runtime/wamr-wasip1/ocre_api/core/core_internal.h @@ -0,0 +1,143 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef OCRE_CORE_INTERNAL_H +#define OCRE_CORE_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// Constants + +/** + * @brief Sets the value of argc for internal use. + * + * This function stores the argc value passed from main + * so that other functions in the module can access it + * without relying on global variables. + * + * @param argc The argument count from main. + */ +void set_argc(int argc); + +/** + * @brief Application version string. + */ +#ifndef APP_VERSION_STRING +#define APP_VERSION_STRING "0.0.0-dev" +#endif /* APP_VERSION_STRING */ + +/** + * @brief Structure representing a thread in the Ocre runtime. + */ +struct core_thread { + pthread_t tid; /*!< POSIX thread identifier */ + void *stack; /*!< Pointer to thread stack memory */ + size_t stack_size; /*!< Size of the thread stack */ + uint32_t user_options; /*!< User-defined options for the thread */ +}; + +/** + * @brief Structure representing a mutex in the Ocre runtime. + */ +struct core_mutex { + pthread_mutex_t native_mutex; /*!< POSIX mutex */ +}; + +/** + * @brief Structure representing a message queue in the Ocre runtime. + */ +struct core_mq { + mqd_t msgq; /*!< POSIX message queue descriptor */ +}; + +/** + * @brief Timer callback function type. + * + * @param user_data Pointer to user data passed to the callback. + */ +typedef void (*core_timer_callback_t)(void *user_data); + +/** + * @brief Structure representing a timer in the Ocre runtime. + */ +struct core_timer { + timer_t timerid; /*!< POSIX timer identifier */ + core_timer_callback_t cb; /*!< Timer callback function */ + void *user_data; /*!< User data for the callback */ +}; + +/* Generic singly-linked list iteration macros */ +#define CORE_SLIST_CONTAINER_OF(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member))) + +#define CORE_SLIST_FOR_EACH_CONTAINER_SAFE(list, var, tmp, member) \ + for (var = (list)->head ? CORE_SLIST_CONTAINER_OF((list)->head, __typeof__(*var), member) : NULL, \ + tmp = var ? (var->member.next ? CORE_SLIST_CONTAINER_OF(var->member.next, __typeof__(*var), member) \ + : NULL) \ + : NULL; \ + var; var = tmp, \ + tmp = tmp ? (tmp->member.next ? CORE_SLIST_CONTAINER_OF(tmp->member.next, __typeof__(*var), member) \ + : NULL) \ + : NULL) + +#define CORE_SLIST_FOR_EACH_CONTAINER(list, var, member) \ + for (var = (list)->head ? CORE_SLIST_CONTAINER_OF((list)->head, __typeof__(*var), member) : NULL; var; \ + var = var->member.next ? CORE_SLIST_CONTAINER_OF(var->member.next, __typeof__(*var), member) : NULL) + +/** + * @brief Structure representing a node in a singly-linked list. + */ +typedef struct core_snode { + struct core_snode *next; /*!< Pointer to the next node in the list */ +} core_snode_t; + +/** + * @brief Structure representing a singly-linked list for POSIX platform. + */ +typedef struct { + core_snode_t *head; /*!< Pointer to the first node in the list */ + core_snode_t *tail; /*!< Pointer to the last node in the list */ +} core_slist_t; + +/** + * @brief Spinlock type for POSIX platform (simulated using mutex). + */ +typedef struct { + pthread_mutex_t mutex; /*!< POSIX mutex for spinlock simulation */ +} core_spinlock_t; + +/** + * @brief Spinlock key type for POSIX platform. + */ +typedef int core_spinlock_key_t; + +/** + * @brief Generic event queue structure for POSIX platform. + * + * A thread-safe circular buffer implementation that can store + * any type of data items with configurable size and capacity. + */ +typedef struct { + void *buffer; /*!< Dynamically allocated buffer for queue items */ + size_t item_size; /*!< Size of each individual item in bytes */ + size_t max_items; /*!< Maximum number of items the queue can hold */ + size_t count; /*!< Current number of items in the queue */ + size_t head; /*!< Index of the next item to be read */ + size_t tail; /*!< Index where the next item will be written */ + pthread_mutex_t mutex; /*!< Mutex for thread-safe access */ + pthread_cond_t cond; /*!< Condition variable for signaling */ +} core_eventq_t; + +#endif /* OCRE_CORE_INTERNAL_H */ diff --git a/src/shared/platform/posix/core_memory.c b/src/runtime/wamr-wasip1/ocre_api/core/core_memory.c similarity index 90% rename from src/shared/platform/posix/core_memory.c rename to src/runtime/wamr-wasip1/ocre_api/core/core_memory.c index 33203b06..cfd3471b 100644 --- a/src/shared/platform/posix/core_memory.c +++ b/src/runtime/wamr-wasip1/ocre_api/core/core_memory.c @@ -5,10 +5,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "ocre_core_external.h" - #include +#include "core_external.h" + void *core_malloc(size_t size) { return malloc(size); diff --git a/src/shared/platform/posix/core_misc.c b/src/runtime/wamr-wasip1/ocre_api/core/core_misc.c similarity index 54% rename from src/shared/platform/posix/core_misc.c rename to src/runtime/wamr-wasip1/ocre_api/core/core_misc.c index 922ee62c..86798ec2 100644 --- a/src/shared/platform/posix/core_misc.c +++ b/src/runtime/wamr-wasip1/ocre_api/core/core_misc.c @@ -5,40 +5,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "ocre_core_external.h" +#include "core_external.h" #include +#include #include #include #include -void core_sleep_ms(int milliseconds) -{ - struct timespec ts; - int res; - - if (milliseconds < 0) { - errno = EINVAL; - fprintf(stderr, "core_sleep_ms: Invalid milliseconds value (%d)\n", milliseconds); - return; - } - - ts.tv_sec = milliseconds / 1000; - ts.tv_nsec = (milliseconds % 1000) * 1000000; - - do { - res = nanosleep(&ts, &ts); - if (res && errno != EINTR) { - fprintf(stderr, "core_sleep_ms: nanosleep failed (errno=%d)\n", errno); - } - } while (res && errno == EINTR); -} - -void core_yield(void) -{ - sched_yield(); -} - uint32_t core_uptime_get(void) { struct timespec ts; diff --git a/src/shared/platform/posix/core_mutex.c b/src/runtime/wamr-wasip1/ocre_api/core/core_mutex.c similarity index 95% rename from src/shared/platform/posix/core_mutex.c rename to src/runtime/wamr-wasip1/ocre_api/core/core_mutex.c index 36d20f99..5c511458 100644 --- a/src/shared/platform/posix/core_mutex.c +++ b/src/runtime/wamr-wasip1/ocre_api/core/core_mutex.c @@ -5,7 +5,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "ocre_core_external.h" +#include "core_external.h" + #include #include diff --git a/src/shared/platform/posix/core_timer.c b/src/runtime/wamr-wasip1/ocre_api/core/core_timer.c similarity index 91% rename from src/shared/platform/posix/core_timer.c rename to src/runtime/wamr-wasip1/ocre_api/core/core_timer.c index 33e38307..33332e45 100644 --- a/src/shared/platform/posix/core_timer.c +++ b/src/runtime/wamr-wasip1/ocre_api/core/core_timer.c @@ -5,13 +5,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "ocre_core_external.h" - #include #include #include #include +#include "core_external.h" + static void posix_timer_cb(union sigval sv) { core_timer_t *timer = (core_timer_t *)sv.sival_ptr; @@ -57,3 +57,11 @@ int core_timer_stop(core_timer_t *timer) struct itimerspec its = {0}; return timer_settime(timer->timerid, 0, &its, NULL); } + +int core_timer_delete(core_timer_t *timer) +{ + if (!timer) + return -1; + + return timer_delete(timer->timerid); +} diff --git a/src/ocre/api/ocre_api.c b/src/runtime/wamr-wasip1/ocre_api/ocre_api.c similarity index 94% rename from src/ocre/api/ocre_api.c rename to src/runtime/wamr-wasip1/ocre_api/ocre_api.c index 7c6126af..1ca8db8c 100644 --- a/src/ocre/api/ocre_api.c +++ b/src/runtime/wamr-wasip1/ocre_api/ocre_api.c @@ -12,35 +12,37 @@ #include #include #include +#include #include -#include "ocre_core_external.h" - -#include "bh_platform.h" - #include "ocre_api.h" +#include "utils/strlcat.h" + +#include #ifdef CONFIG_OCRE_TIMER -#include "../ocre_timers/ocre_timer.h" +#include "ocre_timers/ocre_timer.h" #endif -#include "ocre/utils/utils.h" - #if defined(CONFIG_OCRE_TIMER) || defined(CONFIG_OCRE_GPIO) || defined(CONFIG_OCRE_SENSORS) || \ defined(CONFIG_OCRE_CONTAINER_MESSAGING) #include "ocre_common.h" #endif #ifdef CONFIG_OCRE_SENSORS -#include "../ocre_sensors/ocre_sensors.h" +#include "ocre_sensors/ocre_sensors.h" #endif #ifdef CONFIG_OCRE_GPIO -#include "../ocre_gpio/ocre_gpio.h" +#include "ocre_gpio/ocre_gpio.h" #endif #ifdef CONFIG_OCRE_CONTAINER_MESSAGING -#include "../ocre_messaging/ocre_messaging.h" +#include "ocre_messaging/ocre_messaging.h" +#endif + +#ifndef APP_VERSION_STRING +#define APP_VERSION_STRING "1.0.0" #endif int _ocre_posix_uname(wasm_exec_env_t exec_env, struct _ocre_posix_utsname *name) @@ -91,7 +93,7 @@ int _ocre_posix_uname(wasm_exec_env_t exec_env, struct _ocre_posix_utsname *name int ocre_sleep(wasm_exec_env_t exec_env, int milliseconds) { - core_sleep_ms(milliseconds); + usleep(milliseconds * 1000); return 0; } diff --git a/src/ocre/api/ocre_api.h b/src/runtime/wamr-wasip1/ocre_api/ocre_api.h similarity index 100% rename from src/ocre/api/ocre_api.h rename to src/runtime/wamr-wasip1/ocre_api/ocre_api.h diff --git a/src/ocre/api/ocre_common.c b/src/runtime/wamr-wasip1/ocre_api/ocre_common.c similarity index 80% rename from src/ocre/api/ocre_common.c rename to src/runtime/wamr-wasip1/ocre_api/ocre_common.c index 6b5dc93f..f2032345 100644 --- a/src/ocre/api/ocre_common.c +++ b/src/runtime/wamr-wasip1/ocre_api/ocre_common.c @@ -5,23 +5,26 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include -#include "ocre_core_external.h" -#include -#include -#include -#include #include +#include #include +#include +#include +#include +#include + +#include -LOG_MODULE_DECLARE(ocre_cs_component, OCRE_LOG_LEVEL); +#include "core/core_external.h" + +LOG_MODULE_REGISTER(ocre_common, CONFIG_OCRE_LOG_LEVEL); #ifdef CONFIG_OCRE_GPIO -#include "../ocre_gpio/ocre_gpio.h" +#include "ocre_gpio/ocre_gpio.h" #endif #ifdef CONFIG_OCRE_CONTAINER_MESSAGING -#include "../ocre_messaging/ocre_messaging.h" +#include "ocre_messaging/ocre_messaging.h" #endif #include "ocre_common.h" @@ -31,7 +34,6 @@ typedef struct module_node { core_snode_t node; } module_node_t; -static core_slist_t module_registry; static core_mutex_t registry_mutex; #define SIZE_OCRE_EVENT_BUFFER 32 @@ -148,8 +150,9 @@ int ocre_get_event(wasm_exec_env_t exec_env, uint32_t type_offset, uint32_t id_o break; } case OCRE_RESOURCE_TYPE_MESSAGING: { - LOG_DBG("Retrieved Messaging event: message_id=%u, topic=%s, topic_offset=%u, content_type=%s, " - "content_type_offset=%u, payload_len=%d, owner=%p", + LOG_DBG("Retrieved Messaging event: message_id=%" PRIu32 ", topic=%s, " + "topic_offset=%" PRIu32 ", content_type=%s, " + "content_type_offset=%" PRIu32 ", payload_len=%" PRIu32 ", owner=%p", event.data.messaging_event.message_id, event.data.messaging_event.topic, event.data.messaging_event.topic_offset, event.data.messaging_event.content_type, event.data.messaging_event.content_type_offset, event.data.messaging_event.payload_len, @@ -169,7 +172,7 @@ int ocre_get_event(wasm_exec_env_t exec_env, uint32_t type_offset, uint32_t id_o */ default: { core_spinlock_unlock(&ocre_event_queue_lock, key); - LOG_ERR("Invalid event type: %u", event.type); + LOG_ERR("Invalid event type: %" PRIu32, event.type); return -EINVAL; } } @@ -186,7 +189,6 @@ int ocre_common_init(void) } core_mutex_init(®istry_mutex); - core_slist_init(&module_registry); core_eventq_init(&ocre_event_queue, sizeof(ocre_event_t), SIZE_OCRE_EVENT_BUFFER); @@ -237,6 +239,9 @@ void ocre_common_shutdown(void) core_thread_destroy(&event_threads[i]); } #endif + + core_eventq_destroy(&ocre_event_queue); + common_initialized = false; LOG_INF("OCRE common shutdown successfully"); } @@ -252,35 +257,26 @@ int ocre_register_cleanup_handler(ocre_resource_type_t type, ocre_cleanup_handle return 0; } -int ocre_register_module(wasm_module_inst_t module_inst) +ocre_module_context_t *ocre_register_module(wasm_module_inst_t module_inst) { if (!module_inst) { LOG_ERR("Null module instance"); - return -EINVAL; - } - module_node_t *node = core_malloc(sizeof(module_node_t)); - if (!node) { - LOG_ERR("Failed to allocate module node"); - return -ENOMEM; + return NULL; } - node->ctx.inst = module_inst; - node->ctx.exec_env = wasm_runtime_create_exec_env(module_inst, OCRE_WASM_STACK_SIZE); - if (!node->ctx.exec_env) { - LOG_ERR("Failed to create exec env for module %p", (void *)module_inst); - core_free(node); - return -ENOMEM; + ocre_module_context_t *ctx = malloc(sizeof(ocre_module_context_t)); + if (!ctx) { + LOG_ERR("Failed to allocate module context"); + return NULL; } - node->ctx.in_use = true; - node->ctx.last_activity = core_uptime_get(); - memset(node->ctx.resource_count, 0, sizeof(node->ctx.resource_count)); - memset(node->ctx.dispatchers, 0, sizeof(node->ctx.dispatchers)); + ctx->inst = module_inst; - core_mutex_lock(®istry_mutex); - core_slist_append(&module_registry, &node->node); - core_mutex_unlock(®istry_mutex); + ctx->in_use = true; + ctx->last_activity = core_uptime_get(); + memset(ctx->resource_count, 0, sizeof(ctx->resource_count)); + memset(ctx->dispatchers, 0, sizeof(ctx->dispatchers)); LOG_INF("Module registered: %p", (void *)module_inst); - return 0; + return ctx; } void ocre_unregister_module(wasm_module_inst_t module_inst) @@ -290,24 +286,12 @@ void ocre_unregister_module(wasm_module_inst_t module_inst) return; } - core_mutex_lock(®istry_mutex); - module_node_t *node, *tmp; - module_node_t *prev = NULL; - CORE_SLIST_FOR_EACH_CONTAINER_SAFE(&module_registry, node, tmp, node) - { - if (node->ctx.inst == module_inst) { - ocre_cleanup_module_resources(module_inst); - if (node->ctx.exec_env) { - wasm_runtime_destroy_exec_env(node->ctx.exec_env); - } - core_slist_remove(&module_registry, prev ? &prev->node : NULL, &node->node); - core_free(node); - LOG_INF("Module unregistered: %p", (void *)module_inst); - break; - } - prev = node; - } - core_mutex_unlock(®istry_mutex); + ocre_cleanup_module_resources(module_inst); + + ocre_module_context_t *ret = (ocre_module_context_t *)wasm_runtime_get_custom_data(module_inst); + free(ret); + + LOG_INF("Module unregistered: %p", (void *)module_inst); } ocre_module_context_t *ocre_get_module_context(wasm_module_inst_t module_inst) @@ -316,18 +300,13 @@ ocre_module_context_t *ocre_get_module_context(wasm_module_inst_t module_inst) LOG_ERR("Null module instance"); return NULL; } - core_mutex_lock(®istry_mutex); - for (core_snode_t *current = module_registry.head; current != NULL; current = current->next) { - module_node_t *node = (module_node_t *)((char *)current - offsetof(module_node_t, node)); - if (node->ctx.inst == module_inst) { - node->ctx.last_activity = core_uptime_get(); - core_mutex_unlock(®istry_mutex); - return &node->ctx; - } + + ocre_module_context_t *ret = (ocre_module_context_t *)wasm_runtime_get_custom_data(module_inst); + if (!ret) { + LOG_ERR("Module context not found for %p", (void *)module_inst); + return NULL; } - core_mutex_unlock(®istry_mutex); - LOG_ERR("Module context not found for %p", (void *)module_inst); - return NULL; + return ret; } int ocre_register_dispatcher(wasm_exec_env_t exec_env, ocre_resource_type_t type, const char *function_name) @@ -376,7 +355,7 @@ void ocre_increment_resource_count(wasm_module_inst_t module_inst, ocre_resource core_mutex_lock(®istry_mutex); ctx->resource_count[type]++; core_mutex_unlock(®istry_mutex); - LOG_INF("Incremented resource count: type=%d, count=%d", type, ctx->resource_count[type]); + LOG_INF("Incremented resource count: type=%d, count=%" PRIu32, type, ctx->resource_count[type]); } } @@ -387,7 +366,7 @@ void ocre_decrement_resource_count(wasm_module_inst_t module_inst, ocre_resource core_mutex_lock(®istry_mutex); ctx->resource_count[type]--; core_mutex_unlock(®istry_mutex); - LOG_INF("Decremented resource count: type=%d, count=%d", type, ctx->resource_count[type]); + LOG_INF("Decremented resource count: type=%d, count=%" PRIu32, type, ctx->resource_count[type]); } } diff --git a/src/ocre/api/ocre_common.h b/src/runtime/wamr-wasip1/ocre_api/ocre_common.h similarity index 95% rename from src/ocre/api/ocre_common.h rename to src/runtime/wamr-wasip1/ocre_api/ocre_common.h index 97d7c5f3..71a3649e 100644 --- a/src/ocre/api/ocre_common.h +++ b/src/runtime/wamr-wasip1/ocre_api/ocre_common.h @@ -8,11 +8,11 @@ #ifndef OCRE_COMMON_H #define OCRE_COMMON_H -#include +#include "core/core_external.h" +#include "ocre_messaging/ocre_messaging.h" #include #include -#include "ocre_core_external.h" -#include "../ocre_messaging/ocre_messaging.h" +#include #define OCRE_EVENT_THREAD_STACK_SIZE 2048 #define OCRE_EVENT_THREAD_PRIORITY 5 @@ -43,12 +43,14 @@ typedef enum { * @brief Structure representing the context of an OCRE module. */ typedef struct { - wasm_module_inst_t inst; ///< WASM module instance - wasm_exec_env_t exec_env; ///< WASM execution environment + wasm_module_inst_t inst; ///< WASM module instance + // wasm_exec_env_t exec_env; ///< WASM execution + // environment bool in_use; ///< Flag indicating if the module is in use uint32_t last_activity; ///< Timestamp of the last activity uint32_t resource_count[OCRE_RESOURCE_TYPE_COUNT]; ///< Count of resources per type - wasm_function_inst_t dispatchers[OCRE_RESOURCE_TYPE_COUNT]; ///< Event dispatchers per resource type + wasm_function_inst_t dispatchers[OCRE_RESOURCE_TYPE_COUNT]; ///< Event dispatchers per resource + ///< type } ocre_module_context_t; /** @@ -111,7 +113,7 @@ int ocre_common_init(void); * @param module_inst The WASM module instance to register. * @return 0 on success, negative error code on failure. */ -int ocre_register_module(wasm_module_inst_t module_inst); +ocre_module_context_t *ocre_register_module(wasm_module_inst_t module_inst); /** * @brief Unregister a WASM module from the OCRE system. diff --git a/src/ocre/ocre_gpio/ocre_gpio.c b/src/runtime/wamr-wasip1/ocre_api/ocre_gpio/ocre_gpio.c similarity index 97% rename from src/ocre/ocre_gpio/ocre_gpio.c rename to src/runtime/wamr-wasip1/ocre_api/ocre_gpio/ocre_gpio.c index 6b07002b..f3e9de61 100644 --- a/src/ocre/ocre_gpio/ocre_gpio.c +++ b/src/runtime/wamr-wasip1/ocre_api/ocre_gpio/ocre_gpio.c @@ -5,16 +5,21 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include -#include -#include +// #include +// #include +// #include #include #include #include #include #include -LOG_MODULE_DECLARE(ocre_cs_component, OCRE_LOG_LEVEL); +#include + +#include "../ocre_common.h" +#include "ocre_gpio.h" + +LOG_MODULE_REGISTER(ocre_gpio, CONFIG_OCRE_LOG_LEVEL); typedef struct { uint32_t in_use : 1; @@ -135,7 +140,7 @@ int ocre_gpio_init(void) return 0; } -int ocre_gpio_configure(const ocre_gpio_config_t *config) +int ocre_gpio_configure(wasm_exec_env_t exec_env, const ocre_gpio_config_t *config) { if (!gpio_system_initialized || !config || config->pin >= CONFIG_OCRE_GPIO_PINS_PER_PORT || config->port_idx >= CONFIG_OCRE_GPIO_MAX_PORTS || !port_ready[config->port_idx]) { @@ -154,11 +159,13 @@ int ocre_gpio_configure(const ocre_gpio_config_t *config) LOG_ERR("Failed to configure GPIO pin %d on port %d: %d", pin, port_idx, ret); return ret; } - wasm_module_inst_t module_inst = ocre_get_current_module(); + + wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); if (!module_inst) { LOG_ERR("No current module instance for GPIO configuration"); return -EINVAL; } + int global_pin = port_idx * CONFIG_OCRE_GPIO_PINS_PER_PORT + pin; if (global_pin >= CONFIG_OCRE_GPIO_MAX_PINS) { LOG_ERR("Global pin %d exceeds max %d", global_pin, CONFIG_OCRE_GPIO_MAX_PINS); @@ -323,7 +330,7 @@ int ocre_gpio_wasm_configure(wasm_exec_env_t exec_env, int port, int pin, int di return -EINVAL; } ocre_gpio_config_t config = {.pin = pin, .port_idx = port, .direction = direction}; - return ocre_gpio_configure(&config); + return ocre_gpio_configure(exec_env, &config); } int ocre_gpio_wasm_set(wasm_exec_env_t exec_env, int port, int pin, int state) @@ -559,7 +566,7 @@ static int resolve_gpio_alias(const char *name, int *port_idx, gpio_pin_t *pin) return 0; } -int ocre_gpio_configure_by_name(const char *name, ocre_gpio_direction_t direction) +int ocre_gpio_configure_by_name(wasm_exec_env_t exec_env, const char *name, ocre_gpio_direction_t direction) { if (!name) { LOG_ERR("Invalid name parameter"); @@ -575,7 +582,7 @@ int ocre_gpio_configure_by_name(const char *name, ocre_gpio_direction_t directio ocre_gpio_config_t config = {.pin = pin, .port_idx = port_idx, .direction = direction}; - return ocre_gpio_configure(&config); + return ocre_gpio_configure(exec_env, &config); } int ocre_gpio_set_by_name(const char *name, ocre_gpio_pin_state_t state) @@ -646,7 +653,7 @@ int ocre_gpio_wasm_configure_by_name(wasm_exec_env_t exec_env, const char *name, } LOG_INF("Configuring GPIO by name: %s, direction=%d", name, direction); - return ocre_gpio_configure_by_name(name, direction); + return ocre_gpio_configure_by_name(exec_env, name, direction); } int ocre_gpio_wasm_set_by_name(wasm_exec_env_t exec_env, const char *name, int state) diff --git a/src/ocre/ocre_gpio/ocre_gpio.h b/src/runtime/wamr-wasip1/ocre_api/ocre_gpio/ocre_gpio.h similarity index 96% rename from src/ocre/ocre_gpio/ocre_gpio.h rename to src/runtime/wamr-wasip1/ocre_api/ocre_gpio/ocre_gpio.h index 74258537..4164ed06 100644 --- a/src/ocre/ocre_gpio/ocre_gpio.h +++ b/src/runtime/wamr-wasip1/ocre_api/ocre_gpio/ocre_gpio.h @@ -68,7 +68,7 @@ int ocre_gpio_init(void); * @param config GPIO pin configuration * @return 0 on success, negative error code on failure */ -int ocre_gpio_configure(const ocre_gpio_config_t *config); +int ocre_gpio_configure(wasm_exec_env_t exec_env, const ocre_gpio_config_t *config); /** * Set GPIO pin state. @@ -133,7 +133,7 @@ void ocre_gpio_set_dispatcher(wasm_exec_env_t exec_env); * @param direction GPIO direction (OCRE_GPIO_DIR_INPUT or OCRE_GPIO_DIR_OUTPUT) * @return int 0 on success, negative error code on failure */ -int ocre_gpio_configure_by_name(const char *name, ocre_gpio_direction_t direction); +int ocre_gpio_configure_by_name(wasm_exec_env_t exec_env, const char *name, ocre_gpio_direction_t direction); /** * @brief Set a GPIO pin state by alias name diff --git a/src/ocre/ocre_messaging/ocre_messaging.c b/src/runtime/wamr-wasip1/ocre_api/ocre_messaging/ocre_messaging.c similarity index 84% rename from src/ocre/ocre_messaging/ocre_messaging.c rename to src/runtime/wamr-wasip1/ocre_api/ocre_messaging/ocre_messaging.c index 4d68a71f..791fc271 100644 --- a/src/ocre/ocre_messaging/ocre_messaging.c +++ b/src/runtime/wamr-wasip1/ocre_api/ocre_messaging/ocre_messaging.c @@ -5,17 +5,21 @@ * SPDX-License-License: Apache-2.0 */ -#include -#include "ocre_core_external.h" -#include -#include #include +#include -LOG_MODULE_DECLARE(ocre_cs_component, OCRE_LOG_LEVEL); +#include -#ifndef CONFIG_MESSAGING_MAX_SUBSCRIPTIONS -#define CONFIG_MESSAGING_MAX_SUBSCRIPTIONS 10 +#include "../core/core_external.h" +#include "ocre_messaging.h" +#include "../ocre_common.h" + +LOG_MODULE_REGISTER(ocre_messaging, CONFIG_OCRE_LOG_LEVEL); + +#ifndef CONFIG_OCRE_MESSAGING_MAX_SUBSCRIPTIONS +#define CONFIG_OCRE_MESSAGING_MAX_SUBSCRIPTIONS 10 #endif + #define OCRE_MAX_TOPIC_LEN 64 /* Messaging subscription structure */ @@ -26,7 +30,7 @@ typedef struct { } ocre_messaging_subscription_t; typedef struct { - ocre_messaging_subscription_t subscriptions[CONFIG_MESSAGING_MAX_SUBSCRIPTIONS]; + ocre_messaging_subscription_t subscriptions[CONFIG_OCRE_MESSAGING_MAX_SUBSCRIPTIONS]; uint16_t subscription_count; core_mutex_t mutex; } ocre_messaging_system_t; @@ -42,11 +46,6 @@ int ocre_messaging_init(void) return 0; } - if (!common_initialized && ocre_common_init() != 0) { - LOG_ERR("Failed to initialize common subsystem"); - return -EAGAIN; - } - memset(&messaging_system, 0, sizeof(ocre_messaging_system_t)); core_mutex_init(&messaging_system.mutex); @@ -66,7 +65,7 @@ void ocre_messaging_cleanup_container(wasm_module_inst_t module_inst) core_mutex_lock(&messaging_system.mutex); - for (int i = 0; i < CONFIG_MESSAGING_MAX_SUBSCRIPTIONS; i++) { + for (int i = 0; i < CONFIG_OCRE_MESSAGING_MAX_SUBSCRIPTIONS; i++) { if (messaging_system.subscriptions[i].is_active && messaging_system.subscriptions[i].module_inst == module_inst) { messaging_system.subscriptions[i].is_active = false; @@ -87,10 +86,7 @@ void ocre_messaging_cleanup_container(wasm_module_inst_t module_inst) int ocre_messaging_subscribe(wasm_exec_env_t exec_env, void *topic) { if (!messaging_system_initialized) { - if (ocre_messaging_init() != 0) { - LOG_ERR("Failed to initialize messaging system"); - return -EINVAL; - } + ocre_messaging_init(); } if (!topic || ((char *)topic)[0] == '\0') { @@ -104,7 +100,7 @@ int ocre_messaging_subscribe(wasm_exec_env_t exec_env, void *topic) return -EINVAL; } - ocre_module_context_t *ctx = ocre_get_module_context(module_inst); + const ocre_module_context_t *ctx = ocre_get_module_context(module_inst); if (!ctx) { LOG_ERR("Module context not found for module instance %p", (void *)module_inst); return -EINVAL; @@ -113,7 +109,7 @@ int ocre_messaging_subscribe(wasm_exec_env_t exec_env, void *topic) core_mutex_lock(&messaging_system.mutex); // Check if already subscribed - for (int i = 0; i < CONFIG_MESSAGING_MAX_SUBSCRIPTIONS; i++) { + for (int i = 0; i < CONFIG_OCRE_MESSAGING_MAX_SUBSCRIPTIONS; i++) { if (messaging_system.subscriptions[i].is_active && messaging_system.subscriptions[i].module_inst == module_inst && strcmp(messaging_system.subscriptions[i].topic, (char *)topic) == 0) { @@ -124,7 +120,7 @@ int ocre_messaging_subscribe(wasm_exec_env_t exec_env, void *topic) } // Find a free slot - for (int i = 0; i < CONFIG_MESSAGING_MAX_SUBSCRIPTIONS; i++) { + for (int i = 0; i < CONFIG_OCRE_MESSAGING_MAX_SUBSCRIPTIONS; i++) { if (!messaging_system.subscriptions[i].is_active) { strncpy(messaging_system.subscriptions[i].topic, (char *)topic, OCRE_MAX_TOPIC_LEN - 1); messaging_system.subscriptions[i].topic[OCRE_MAX_TOPIC_LEN - 1] = '\0'; @@ -148,10 +144,7 @@ int ocre_messaging_subscribe(wasm_exec_env_t exec_env, void *topic) int ocre_messaging_publish(wasm_exec_env_t exec_env, void *topic, void *content_type, void *payload, int payload_len) { if (!messaging_system_initialized) { - if (ocre_messaging_init() != 0) { - LOG_ERR("Failed to initialize messaging system"); - return -EINVAL; - } + ocre_messaging_init(); } if (!topic || ((char *)topic)[0] == '\0') { @@ -179,7 +172,7 @@ int ocre_messaging_publish(wasm_exec_env_t exec_env, void *topic, void *content_ core_mutex_lock(&messaging_system.mutex); // Find matching subscriptions - for (int i = 0; i < CONFIG_MESSAGING_MAX_SUBSCRIPTIONS; i++) { + for (int i = 0; i < CONFIG_OCRE_MESSAGING_MAX_SUBSCRIPTIONS; i++) { if (!messaging_system.subscriptions[i].is_active) { continue; } @@ -235,18 +228,19 @@ int ocre_messaging_publish(wasm_exec_env_t exec_env, void *topic, void *content_ event.data.messaging_event.payload_len = (uint32_t)payload_len; event.owner = target_module; - LOG_DBG("Creating messaging event: ID=%d, topic=%s, content_type=%s, payload_len=%d for module %p", + LOG_DBG("Creating messaging event: ID=%" PRIu32 + ", topic=%s, content_type=%s, payload_len=%d for module %p", message_id, (char *)topic, (char *)content_type, payload_len, (void *)target_module); core_spinlock_key_t key = core_spinlock_lock(&ocre_event_queue_lock); if (core_eventq_put(&ocre_event_queue, &event) != 0) { - LOG_ERR("Failed to queue messaging event for message ID %d", message_id); + LOG_ERR("Failed to queue messaging event for message ID %" PRIu32, message_id); wasm_runtime_module_free(target_module, topic_offset); wasm_runtime_module_free(target_module, content_offset); wasm_runtime_module_free(target_module, payload_offset); } else { message_sent = true; - LOG_DBG("Queued messaging event for message ID %d", message_id); + LOG_DBG("Queued messaging event for message ID %" PRIu32, message_id); } core_spinlock_unlock(&ocre_event_queue_lock, key); } @@ -254,8 +248,8 @@ int ocre_messaging_publish(wasm_exec_env_t exec_env, void *topic, void *content_ core_mutex_unlock(&messaging_system.mutex); if (message_sent) { - LOG_DBG("Published message: ID=%d, topic=%s, content_type=%s, payload_len=%d", message_id, - (char *)topic, (char *)content_type, payload_len); + LOG_DBG("Published message: ID=%" PRIu32 ", topic=%s, content_type=%s, payload_len=%" PRIu32 "", + message_id, (char *)topic, (char *)content_type, payload_len); message_id++; return 0; } else { diff --git a/src/ocre/ocre_messaging/ocre_messaging.h b/src/runtime/wamr-wasip1/ocre_api/ocre_messaging/ocre_messaging.h similarity index 97% rename from src/ocre/ocre_messaging/ocre_messaging.h rename to src/runtime/wamr-wasip1/ocre_api/ocre_messaging/ocre_messaging.h index 9d74d1a2..34f13267 100644 --- a/src/ocre/ocre_messaging/ocre_messaging.h +++ b/src/runtime/wamr-wasip1/ocre_api/ocre_messaging/ocre_messaging.h @@ -8,8 +8,7 @@ #ifndef OCRE_MESSAGING_H #define OCRE_MESSAGING_H -#include -#include "ocre_core_external.h" +#include #include #define MESSAGING_QUEUE_SIZE 100 diff --git a/src/ocre/ocre_sensors/custom,rng-sensor.yaml b/src/runtime/wamr-wasip1/ocre_api/ocre_sensors/custom,rng-sensor.yaml similarity index 100% rename from src/ocre/ocre_sensors/custom,rng-sensor.yaml rename to src/runtime/wamr-wasip1/ocre_api/ocre_sensors/custom,rng-sensor.yaml diff --git a/src/ocre/ocre_sensors/ocre_sensors.c b/src/runtime/wamr-wasip1/ocre_api/ocre_sensors/ocre_sensors.c similarity index 99% rename from src/ocre/ocre_sensors/ocre_sensors.c rename to src/runtime/wamr-wasip1/ocre_api/ocre_sensors/ocre_sensors.c index 3e1af8c1..285ae090 100644 --- a/src/ocre/ocre_sensors/ocre_sensors.c +++ b/src/runtime/wamr-wasip1/ocre_api/ocre_sensors/ocre_sensors.c @@ -9,7 +9,7 @@ #include #include #include -LOG_MODULE_DECLARE(ocre_sensors, OCRE_LOG_LEVEL); +LOG_MODULE_REGISTER(ocre_sensors, CONFIG_OCRE_LOG_LEVEL); #include "ocre_sensors.h" diff --git a/src/ocre/ocre_sensors/ocre_sensors.h b/src/runtime/wamr-wasip1/ocre_api/ocre_sensors/ocre_sensors.h similarity index 100% rename from src/ocre/ocre_sensors/ocre_sensors.h rename to src/runtime/wamr-wasip1/ocre_api/ocre_sensors/ocre_sensors.h diff --git a/src/ocre/ocre_sensors/rng_sensor.c b/src/runtime/wamr-wasip1/ocre_api/ocre_sensors/rng_sensor.c similarity index 100% rename from src/ocre/ocre_sensors/rng_sensor.c rename to src/runtime/wamr-wasip1/ocre_api/ocre_sensors/rng_sensor.c diff --git a/src/ocre/ocre_sensors/rng_sensor.h b/src/runtime/wamr-wasip1/ocre_api/ocre_sensors/rng_sensor.h similarity index 100% rename from src/ocre/ocre_sensors/rng_sensor.h rename to src/runtime/wamr-wasip1/ocre_api/ocre_sensors/rng_sensor.h diff --git a/src/ocre/ocre_timers/ocre_timer.c b/src/runtime/wamr-wasip1/ocre_api/ocre_timers/ocre_timer.c similarity index 83% rename from src/ocre/ocre_timers/ocre_timer.c rename to src/runtime/wamr-wasip1/ocre_api/ocre_timers/ocre_timer.c index c1ce7b57..53d3dd05 100644 --- a/src/ocre/ocre_timers/ocre_timer.c +++ b/src/runtime/wamr-wasip1/ocre_api/ocre_timers/ocre_timer.c @@ -5,17 +5,23 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include -#include -#include - -#include #include +#include #include #include #include +#include + +#include + +#include -LOG_MODULE_DECLARE(ocre_cs_component, OCRE_LOG_LEVEL); +#include "../core/core_external.h" +#include "../core/core_internal.h" +#include "../ocre_common.h" +#include "ocre_timer.h" + +LOG_MODULE_REGISTER(ocre_timer, CONFIG_OCRE_LOG_LEVEL); /* Unified timer structure using core_timer API */ typedef struct { @@ -29,12 +35,12 @@ typedef struct { wasm_module_inst_t owner; } ocre_timer_internal; -#ifndef CONFIG_MAX_TIMERS -#define CONFIG_MAX_TIMERS 5 +#ifndef CONFIG_OCRE_MAX_TIMERS +#define CONFIG_OCRE_MAX_TIMERS 5 #endif // Static data -static ocre_timer_internal timers[CONFIG_MAX_TIMERS]; +static ocre_timer_internal timers[CONFIG_OCRE_MAX_TIMERS]; static bool timer_system_initialized = false; static void unified_timer_callback(void *user_data); @@ -46,11 +52,6 @@ void ocre_timer_init(void) return; } - if (!common_initialized && ocre_common_init() != 0) { - LOG_ERR("Failed to initialize common subsystem"); - return; - } - ocre_register_cleanup_handler(OCRE_RESOURCE_TYPE_TIMER, ocre_timer_cleanup_container); timer_system_initialized = true; LOG_INF("Timer system initialized"); @@ -59,8 +60,8 @@ void ocre_timer_init(void) int ocre_timer_create(wasm_exec_env_t exec_env, int id) { wasm_module_inst_t module = wasm_runtime_get_module_inst(exec_env); - if (!module || id <= 0 || id > CONFIG_MAX_TIMERS) { - LOG_ERR("Invalid module %p or timer ID %d (max: %d)", (void *)module, id, CONFIG_MAX_TIMERS); + if (!module || id <= 0 || id > CONFIG_OCRE_MAX_TIMERS) { + LOG_ERR("Invalid module %p or timer ID %d (max: %d)", (void *)module, id, CONFIG_OCRE_MAX_TIMERS); return -EINVAL; } @@ -89,7 +90,7 @@ int ocre_timer_create(wasm_exec_env_t exec_env, int id) int ocre_timer_delete(wasm_exec_env_t exec_env, ocre_timer_t id) { wasm_module_inst_t module = wasm_runtime_get_module_inst(exec_env); - if (!module || id <= 0 || id > CONFIG_MAX_TIMERS) { + if (!module || id <= 0 || id > CONFIG_OCRE_MAX_TIMERS) { LOG_ERR("Invalid module %p or timer ID %d", (void *)module, id); return -EINVAL; } @@ -103,6 +104,9 @@ int ocre_timer_delete(wasm_exec_env_t exec_env, ocre_timer_t id) // Stop unified core timer core_timer_stop(&timer->timer); + // Delete unified core timer + core_timer_delete(&timer->timer); + timer->in_use = 0; timer->running = 0; timer->owner = NULL; @@ -114,7 +118,7 @@ int ocre_timer_delete(wasm_exec_env_t exec_env, ocre_timer_t id) int ocre_timer_start(wasm_exec_env_t exec_env, ocre_timer_t id, int interval, int is_periodic) { wasm_module_inst_t module = wasm_runtime_get_module_inst(exec_env); - if (!module || id <= 0 || id > CONFIG_MAX_TIMERS) { + if (!module || id <= 0 || id > CONFIG_OCRE_MAX_TIMERS) { LOG_ERR("Invalid module %p or timer ID %d", (void *)module, id); return -EINVAL; } @@ -150,7 +154,7 @@ int ocre_timer_start(wasm_exec_env_t exec_env, ocre_timer_t id, int interval, in int ocre_timer_stop(wasm_exec_env_t exec_env, ocre_timer_t id) { wasm_module_inst_t module = wasm_runtime_get_module_inst(exec_env); - if (!module || id <= 0 || id > CONFIG_MAX_TIMERS) { + if (!module || id <= 0 || id > CONFIG_OCRE_MAX_TIMERS) { LOG_ERR("Invalid module %p or timer ID %d", (void *)module, id); return -EINVAL; } @@ -172,12 +176,12 @@ int ocre_timer_stop(wasm_exec_env_t exec_env, ocre_timer_t id) int ocre_timer_get_remaining(wasm_exec_env_t exec_env, ocre_timer_t id) { wasm_module_inst_t module = wasm_runtime_get_module_inst(exec_env); - if (!module || id <= 0 || id > CONFIG_MAX_TIMERS) { + if (!module || id <= 0 || id > CONFIG_OCRE_MAX_TIMERS) { LOG_ERR("Invalid module %p or timer ID %d", (void *)module, id); return -EINVAL; } - ocre_timer_internal *timer = &timers[id - 1]; + const ocre_timer_internal *timer = &timers[id - 1]; if (!timer->in_use || timer->owner != module) { LOG_ERR("Timer ID %d not in use or not owned by module %p", id, (void *)module); return -EINVAL; @@ -207,10 +211,14 @@ void ocre_timer_cleanup_container(wasm_module_inst_t module_inst) return; } - for (int i = 0; i < CONFIG_MAX_TIMERS; i++) { + for (int i = 0; i < CONFIG_OCRE_MAX_TIMERS; i++) { if (timers[i].in_use && timers[i].owner == module_inst) { // Stop unified core timer core_timer_stop(&timers[i].timer); + + // Delete unified core timer + core_timer_delete(&timers[i].timer); + timers[i].in_use = 0; timers[i].running = 0; timers[i].owner = NULL; @@ -235,7 +243,7 @@ static void unified_timer_callback(void *user_data) return; } - LOG_DBG("Timer callback for timer %d", timer->id); + LOG_DBG("Timer callback for timer %" PRIu32, timer->id); // For non-periodic timers, mark as not running if (!timer->periodic) { @@ -251,13 +259,14 @@ static void unified_timer_callback(void *user_data) event.data.timer_event.timer_id = timer->id; event.owner = timer->owner; - LOG_DBG("Creating timer event: type=%d, id=%d, for owner %p", event.type, timer->id, (void *)timer->owner); + LOG_DBG("Creating timer event: type=%d, id=%" PRIu32 ", for owner %p", event.type, timer->id, + (void *)timer->owner); core_spinlock_key_t key = core_spinlock_lock(&ocre_event_queue_lock); if (core_eventq_put(&ocre_event_queue, &event) != 0) { - LOG_ERR("Failed to queue timer event for timer %d", timer->id); + LOG_ERR("Failed to queue timer event for timer %" PRIu32, timer->id); } else { - LOG_DBG("Queued timer event for timer %d", timer->id); + LOG_DBG("Queued timer event for timer %" PRIu32, timer->id); } core_spinlock_unlock(&ocre_event_queue_lock, key); } diff --git a/src/ocre/ocre_timers/ocre_timer.h b/src/runtime/wamr-wasip1/ocre_api/ocre_timers/ocre_timer.h similarity index 98% rename from src/ocre/ocre_timers/ocre_timer.h rename to src/runtime/wamr-wasip1/ocre_api/ocre_timers/ocre_timer.h index 1cdf1289..d922d4a3 100644 --- a/src/ocre/ocre_timers/ocre_timer.h +++ b/src/runtime/wamr-wasip1/ocre_api/ocre_timers/ocre_timer.h @@ -9,7 +9,6 @@ #define OCRE_TIMER_H #include -#include "ocre_core_external.h" typedef int ocre_timer_t; diff --git a/src/ocre/utils/strlcat.c b/src/runtime/wamr-wasip1/ocre_api/utils/strlcat.c similarity index 100% rename from src/ocre/utils/strlcat.c rename to src/runtime/wamr-wasip1/ocre_api/utils/strlcat.c diff --git a/src/ocre/utils/utils.h b/src/runtime/wamr-wasip1/ocre_api/utils/strlcat.h similarity index 62% rename from src/ocre/utils/utils.h rename to src/runtime/wamr-wasip1/ocre_api/utils/strlcat.h index 3c46ba2f..c0c3e22e 100644 --- a/src/ocre/utils/utils.h +++ b/src/runtime/wamr-wasip1/ocre_api/utils/strlcat.h @@ -1,13 +1,3 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef OCRE_UTILS_H_ -#define OCRE_UTILS_H_ - #include /* @@ -18,5 +8,3 @@ * If retval >= dsize, truncation occurred. */ size_t strlcat(char *dst, const char *src, size_t dsize); - -#endif diff --git a/src/runtime/wamr-wasip1/wamr.c b/src/runtime/wamr-wasip1/wamr.c new file mode 100644 index 00000000..cf724e17 --- /dev/null +++ b/src/runtime/wamr-wasip1/wamr.c @@ -0,0 +1,495 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include "ocre_api/ocre_api.h" + +#include "ocre_api/ocre_common.h" +#include "ocre_api/ocre_timers/ocre_timer.h" + +LOG_MODULE_REGISTER(wamr_runtime, CONFIG_OCRE_LOG_LEVEL); + +static wasm_shared_heap_t _shared_heap = NULL; + +static void *shared_heap_buf = NULL; + +struct wamr_context { + char *buffer; + size_t size; + char error_buf[128]; + wasm_module_t module; + wasm_module_inst_t module_inst; + char **argv; + char **envp; + bool uses_ocre_api; + bool uses_shared_heap; + char **dir_map_list; + size_t dir_map_list_len; +}; + +static int instance_execute(void *runtime_context, sem_t *sem) +{ + struct wamr_context *context = runtime_context; + + context->module_inst = + wasm_runtime_instantiate(context->module, 8192, 8192, context->error_buf, sizeof(context->error_buf)); + if (!context->module_inst) { + LOG_ERR("Failed to instantiate module: %s, for context %p", context->error_buf, context); + return -1; + } + + if (context->uses_ocre_api) { + ocre_module_context_t *mod = ocre_register_module(context->module_inst); + wasm_runtime_set_custom_data(context->module_inst, mod); + } + + if (context->uses_shared_heap) { + if (!wasm_runtime_attach_shared_heap(context->module_inst, _shared_heap)) { + LOG_ERR("Failed to attach shared heap"); + } + LOG_INF("Shared heap capability enabled"); + } + + /* Clear any previous exceptions */ + + wasm_runtime_clear_exception(context->module_inst); + + /* Notify the starting waiter that we are ready + * We should notify only after we are ready to process the kill call. + * In WAMR, this is managed by the exception message, so we are good if we just cleared the + * exception. + */ + + int rc = sem_post(sem); + if (rc) { + LOG_WRN("Failed to signal start semaphore: rc=%d", rc); + } + + /* Execute main function */ + + const char *exception = NULL; + if (!wasm_application_execute_main(context->module_inst, 1, context->argv)) { + LOG_WRN("Main function returned error in context %p exception: %s", context, + exception ? exception : "None"); + + exception = wasm_runtime_get_exception(context->module_inst); + if (exception) { + LOG_ERR("Container %p exception: %s", context, exception); + } + } + + if (context->uses_ocre_api) { + /* Cleanup module resources if using Ocre API */ + + LOG_INF("Cleaning up module resources"); + + ocre_cleanup_module_resources(context->module_inst); + + ocre_unregister_module(context->module_inst); + } + + LOG_INF("Context %p completed successfully", context); + + int exit_code = wasm_runtime_get_wasi_exit_code(context->module_inst); + + wasm_runtime_deinstantiate(context->module_inst); + + context->module_inst = NULL; + + return exit_code; +} + +static int instance_thread_execute(void *arg, sem_t *sem) +{ + struct wamr_context *context = arg; + + wasm_runtime_init_thread_env(); + + int ret = instance_execute(context, sem); + + wasm_runtime_destroy_thread_env(); + + return ret; +} + +static int runtime_init(void) +{ +#if defined(CONFIG_OCRE_SHARED_HEAP_BUF_VIRTUAL) + + /* Allocate memory for the shared heap */ + + shared_heap_buf = user_malloc(CONFIG_OCRE_SHARED_HEAP_BUF_VIRTUAL); + if (!shared_heap_buf) { + LOG_ERR("Failed to allocate memory for the shared heap of size %zu", + (size_t)CONFIG_OCRE_SHARED_HEAP_BUF_VIRTUAL); + return -1; + } +#elif defined(CONFIG_OCRE_SHARED_HEAP_BUF_PHYSICAL) + shared_heap_buf = CONFIG_OCRE_SHARED_HEAP_BUF_ADDRESS; +#endif + RuntimeInitArgs init_args; + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + init_args.mem_alloc_type = Alloc_With_Allocator; + init_args.mem_alloc_option.allocator.malloc_func = user_malloc; + init_args.mem_alloc_option.allocator.free_func = user_free; + init_args.mem_alloc_option.allocator.realloc_func = user_realloc; + // init_args.native_module_name = "env"; + // init_args.n_native_symbols = ocre_api_table_size; + // init_args.native_symbols = ocre_api_table; + + if (!wasm_runtime_full_init(&init_args)) { + LOG_ERR("Failed to initialize the WAMR runtime"); + goto error_sh_heap_buf; + } + + wasm_runtime_set_log_level(CONFIG_OCRE_WAMR_LOG_LEVEL); + +#ifdef CONFIG_OCRE_SHARED_HEAP + SharedHeapInitArgs heap_init_args; + memset(&heap_init_args, 0, sizeof(heap_init_args)); + heap_init_args.pre_allocated_addr = shared_heap_buf; + heap_init_args.size = CONFIG_OCRE_SHARED_HEAP_BUF_SIZE; + + _shared_heap = wasm_runtime_create_shared_heap(&heap_init_args); + + if (!_shared_heap) { + LOG_ERR("Failed to create shared heap"); + goto error_runtime; + } +#endif + + if (!wasm_runtime_register_natives("env", ocre_api_table, ocre_api_table_size)) { + LOG_ERR("Failed to register the API's"); + return -1; + } + + ocre_common_init(); + ocre_timer_init(); + + // TODO handle error + + return 0; + +error_runtime: + wasm_runtime_destroy(); + +error_sh_heap_buf: +#if defined(CONFIG_OCRE_SHARED_HEAP_BUF_VIRTUAL) + free(shared_heap_buf); + shared_heap_buf = NULL; +#endif + + return -1; +} + +static int runtime_deinit(void) +{ + ocre_common_shutdown(); + + wasm_runtime_destroy(); + +#ifdef CONFIG_OCRE_SHARED_HEAP_BUF_VIRTUAL + free(shared_heap_buf); +#endif + + return 0; +} + +static void *instance_create(const char *img_path, const char *workdir, const char **capabilities, const char **argv, + const char **envp, const char **mounts) +{ + struct wamr_context *context = NULL; + // char **dir_map_list = NULL; + char **new_dir_map_list = NULL; + // size_t dir_map_list_len = 0; + + if (!img_path) { + LOG_ERR("Invalid arguments"); + return NULL; + } + + context = malloc(sizeof(struct wamr_context)); + if (!context) { + LOG_ERR("Failed to allocate memory for context size=%zu errno=%d", sizeof(struct wamr_context), errno); + goto error; + } + + memset(context, 0, sizeof(struct wamr_context)); + + /* For envp we can just keep a reference + * as the container is guaranteed to only free it after our destruction + */ + + context->envp = (char **)envp; + + int envn = 0; + while (context->envp && context->envp[envn]) { + envn++; + } + + /* We need to insert argv[0]. We can keep a shallow copy, because + * the container is guaranteed to only free it after us + */ + + int argc = 0; + while (argv && argv[argc]) { + argc++; + } + + /* 2 more: app name and NULL */ + + context->argv = malloc(sizeof(char *) * (argc + 2)); + if (!context->argv) { + LOG_ERR("Failed to allocate memory for argv"); + goto error; + } + + memset(context->argv, 0, sizeof(char *) * (argc + 2)); + + context->argv[0] = strdup(img_path); + if (!context->argv[0]) { + goto error; + } + + int i; + for (i = 0; i < argc; i++) { + context->argv[i + 1] = (char *)argv[i]; + if (!context->argv[i + 1]) { + goto error; + } + } + + context->argv[i + 1] = NULL; + + if (context->buffer) { + LOG_WRN("Buffer already allocated. Possible memory leak!"); + } + + /* Memory-map file */ + + context->buffer = ocre_load_file(img_path, &context->size); + if (!context->buffer) { + LOG_ERR("Failed to load wasm program into buffer errno=%d", errno); + goto error; + } + + LOG_INF("Buffer loaded successfully: %p", context->buffer); + + context->module = wasm_runtime_load((uint8_t *)context->buffer, context->size, context->error_buf, + sizeof(context->error_buf)); + if (!context->module) { + LOG_ERR("Failed to load module: %s", context->error_buf); + goto error; + } + + /* Process capabilities */ + + for (const char **cap = capabilities; cap && *cap; cap++) { + if (!strcmp(*cap, "ocre:shared_heap")) { + context->uses_shared_heap = true; + } else if (!strcmp(*cap, "ocre:api")) { + context->uses_ocre_api = true; + } +#if CONFIG_OCRE_NETWORKING + else if (!strcmp(*cap, "networking")) { + const char *addr_pool[] = { + "0.0.0.0/0", + }; + + wasm_runtime_set_wasi_addr_pool(context->module, addr_pool, + sizeof(addr_pool) / sizeof(addr_pool[0])); + + const char *ns_lookup_pool[] = {"*"}; + + wasm_runtime_set_wasi_ns_lookup_pool(context->module, ns_lookup_pool, + sizeof(ns_lookup_pool) / sizeof(ns_lookup_pool[0])); + + LOG_INF("Network capability enabled"); + } +#endif +#if CONFIG_OCRE_FILESYSTEM + else if (!strcmp(*cap, "filesystem") && workdir) { + context->dir_map_list = malloc((context->dir_map_list_len + 2) * sizeof(char *)); + if (!context->dir_map_list) { + LOG_ERR("Failed to allocate memory for dir_map_list"); + goto error; + } + + memset(context->dir_map_list, 0, sizeof(char *)); + + context->dir_map_list[context->dir_map_list_len] = malloc(strlen("/::") + strlen(workdir) + 1); + if (!context->dir_map_list[context->dir_map_list_len]) { + LOG_ERR("Failed to allocate memory for dir_map_list[0]"); + free(context->dir_map_list); + goto error; + } + + sprintf(context->dir_map_list[context->dir_map_list_len], "/::%s", workdir); + + context->dir_map_list_len++; + + context->dir_map_list[context->dir_map_list_len] = NULL; + + LOG_INF("Filesystem capability enabled"); + } +#endif + } + + /* Add the mounts to the directory map list */ + + for (const char **mount = mounts; mount && *mount; mount++) { + /* Need to insert the extra ':' */ + + new_dir_map_list = realloc(context->dir_map_list, (context->dir_map_list_len + 2) * sizeof(char *)); + if (!new_dir_map_list) { + LOG_ERR("Failed to allocate memory for dir_map_list"); + goto error; + } + + context->dir_map_list = new_dir_map_list; + + context->dir_map_list[context->dir_map_list_len] = malloc(strlen(*mount) + 2); + if (!context->dir_map_list[context->dir_map_list_len]) { + LOG_ERR("Failed to allocate memory for dir_map_list[%zu]", context->dir_map_list_len); + goto error; + } + + const char *src_colon = strchr(*mount, ':'); + if (!src_colon) { + LOG_ERR("Invalid mount format: %s", *mount); + goto error; + } + + strcpy(context->dir_map_list[context->dir_map_list_len], *mount); + + char *dst_colon = strchr(context->dir_map_list[context->dir_map_list_len], ':'); + if (!dst_colon) { + LOG_ERR("Invalid mount format: %s", *mount); + goto error; + } + + sprintf(dst_colon + 1, ":%s", src_colon + 1); + + LOG_INF("Enabled mount: %s", context->dir_map_list[context->dir_map_list_len]); + + context->dir_map_list_len++; + + /* Add the NULL */ + + context->dir_map_list[context->dir_map_list_len] = NULL; + } + + wasm_runtime_set_wasi_args(context->module, NULL, 0, (const char **)context->dir_map_list, + context->dir_map_list_len, envp, envn, context->argv, argc + 1); + + return context; + +error_module: + wasm_runtime_unload(context->module); + +error: + if (context) { + if (context->module) { + wasm_runtime_unload(context->module); + } + + if (context->buffer) { + ocre_unload_file(context->buffer, context->size); + context->buffer = NULL; + } + + for (char **dir_map = context->dir_map_list; dir_map && *dir_map; dir_map++) { + free(*dir_map); + } + + free(context->dir_map_list); + + context->dir_map_list = NULL; + + /* Only free what we allocated */ + + if (context->argv) { + free(context->argv[0]); + } + + free(context->argv); + } + + free(context); + + return NULL; +} + +static int instance_kill(void *runtime_context) +{ + struct wamr_context *context = runtime_context; + + if (!context) { + return -1; + } + + wasm_runtime_terminate(context->module_inst); + + return 0; +} + +static int instance_destroy(void *runtime_context) +{ + struct wamr_context *context = runtime_context; + + if (!context) { + return -1; + } + + wasm_runtime_unload(context->module); + + if (ocre_unload_file(context->buffer, context->size)) { + LOG_ERR("Failed to unload file"); + } + context->buffer = NULL; + + for (char **dir_map = context->dir_map_list; dir_map && *dir_map; dir_map++) { + free(*dir_map); + } + + free(context->dir_map_list); + + free(context->argv[0]); + free(context->argv); + + free(context); + + return 0; +} + +const struct ocre_runtime_vtable wamr_vtable = { + .runtime_name = "wamr/wasip1", + .init = runtime_init, + .deinit = runtime_deinit, + .create = instance_create, + .destroy = instance_destroy, + .thread_execute = instance_thread_execute, + .kill = instance_kill, +}; diff --git a/src/samples-mini/posix/main.c b/src/samples-mini/posix/main.c deleted file mode 100644 index a28d7483..00000000 --- a/src/samples-mini/posix/main.c +++ /dev/null @@ -1,117 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ocre_core_external.h" -#include -#include - -#ifdef HAS_GENERATED_INPUT -#include -#else -#include -#endif - -void create_sample_container(); - -int main(int argc, char *argv[]) -{ - ocre_cs_ctx ctx; - ocre_container_init_arguments_t args; - char *container_filename = "hello"; - - ocre_app_storage_init(); - - // Step 1: Initialize the Ocre runtime - ocre_container_runtime_status_t ret = ocre_container_runtime_init(&ctx, &args); - - if (ret == RUNTIME_STATUS_INITIALIZED) { - printf("\n\nOcre runtime started\n"); - - if (argc > 1) { - set_argc(argc); - container_filename = argv[1]; // Use the filename as the container name/sha256 - } else { - create_sample_container(container_filename); - } - - // Step 2: Create the container, this allocates and loads the container binary - ocre_container_data_t ocre_container_data[CONFIG_MAX_CONTAINERS]; - int container_ID[CONFIG_MAX_CONTAINERS]; - ocre_container_runtime_cb callback[CONFIG_MAX_CONTAINERS]; - - int i = 0; - do { - ocre_container_data[i].heap_size = 0; - snprintf(ocre_container_data[i].name, sizeof(ocre_container_data[i].name), "Container%d", i); - snprintf(ocre_container_data[i].sha256, sizeof(ocre_container_data[i].sha256), "%s", - container_filename); - ocre_container_data[i].timers = 0; - ocre_container_data[i].watchdog_interval = 0; - ocre_container_runtime_create_container(&ctx, &ocre_container_data[i], &container_ID[i], - callback[i]); - - // Step 3: Execute the container - ocre_container_runtime_run_container(container_ID[i], callback[i]); - core_sleep_ms(1000); - - i++; - container_filename = argv[i + 1]; - } while (i < argc - 1); - - // Loop forever, without this the application will exit and stop all execution - while (true) { - core_sleep_ms(1000); - } - - } else { - printf("\n\nOcre runtime failed to start.\n"); - } -} - -/** - * Creates a container image file using the sample "hello-word" WASM module - * This is for demostration purposes only and does not perform any error checking. - * - * @param file_name a string containing the name of the file to create - */ - -void create_sample_container(char *file_name) -{ - static char file_path[64]; - snprintf(file_path, sizeof(file_path), "./%s/%s.bin", APP_RESOURCE_PATH, file_name); - - // Create directories if they don't exist - mkdir("./ocre", 0755); - mkdir("./ocre/images", 0755); - - // Open the file for writing - int fd = open(file_path, O_CREAT | O_RDWR, 0644); - if (fd == -1) { - perror("Error opening file"); - return; - } - // Write the binary data to the file - ssize_t bytes_written = write(fd, wasm_binary, wasm_binary_len); - if (bytes_written == -1) { - perror("Error writing to file"); - } else { - printf("Wrote %zd bytes to %s\n", bytes_written, file_path); - } - - // Close the file - close(fd); -} diff --git a/src/samples-mini/zephyr/main.c b/src/samples-mini/zephyr/main.c deleted file mode 100644 index 0927ae7a..00000000 --- a/src/samples-mini/zephyr/main.c +++ /dev/null @@ -1,117 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include "ocre_core_external.h" -#include -#include - -#include -#include - -#ifdef HAS_GENERATED_INPUT -#include -#else -#include -#endif - -void create_sample_container(); -int ocre_network_init(); - -int main(int argc, char *argv[]) -{ - ocre_cs_ctx ctx; - ocre_container_init_arguments_t args; -#ifdef OCRE_INPUT_FILE_NAME - const char *container_filename = OCRE_INPUT_FILE_NAME; -#else - const char *container_filename = "hello-from-ocre"; -#endif - -#ifdef CONFIG_OCRE_NETWORKING - int net_status = ocre_network_init(); - if (net_status < 0) { - printf("Unable to connect to network\n"); - } else { - printf("Network is UP\n"); - } -#endif - - ocre_app_storage_init(); - - // Step 1: Initialize the Ocre runtime - ocre_container_runtime_status_t ret = ocre_container_runtime_init(&ctx, &args); - - if (ret == RUNTIME_STATUS_INITIALIZED) { - printf("\n\nOcre runtime started\n"); - - create_sample_container(container_filename); - - // Step 2: Create the container, this allocates and loads the container binary - ocre_container_data_t ocre_container_data; - int container_ID; - - ocre_container_data.heap_size = 0; - snprintf(ocre_container_data.name, sizeof(ocre_container_data.name), "%s", container_filename); - snprintf(ocre_container_data.sha256, sizeof(ocre_container_data.sha256), "%s", container_filename); - ocre_container_data.timers = 0; - ocre_container_runtime_create_container(&ctx, &ocre_container_data, &container_ID, NULL); - - // Step 3: Execute the container - ocre_container_runtime_run_container(container_ID, NULL); - // Loop forever, without this the application will exit and stop all execution - while (true) { - core_sleep_ms(1000); - } - - } else { - printf("\n\nOcre runtime failed to start.\n"); - } -} - -/** - * Creates a container image file using the sample "hello-word" WASM module - * This is for demostration purposes only and does not perform any error checking. - * - * @param file_name a string containing the name of the file to create - */ - -void create_sample_container(char *file_name) -{ - static char file_path[64]; - struct fs_file_t f; - snprintf((char *)&file_path, 64, "/lfs/ocre/images/%s.bin", file_name); - int res; - - fs_file_t_init(&f); - res = fs_open(&f, file_path, FS_O_CREATE | FS_O_TRUNC | FS_O_RDWR); - - fs_write(&f, &wasm_binary, wasm_binary_len); - fs_close(&f); -} - -int ocre_network_init() -{ - - struct net_if *iface = net_if_get_default(); - net_dhcpv4_start(iface); - - printf("Waiting for network to be ready...\n"); - - int sleep_cnt = 0; - while (!net_if_is_up(iface) && (sleep_cnt < 10)) { - k_sleep(K_MSEC(200)); - sleep_cnt++; - } - - if (!net_if_is_up(iface)) { - return -ENOTCONN; - } - - return 0; -} diff --git a/src/samples/demo/demo_containers.cmake b/src/samples/demo/demo_containers.cmake new file mode 100644 index 00000000..da5655c6 --- /dev/null +++ b/src/samples/demo/demo_containers.cmake @@ -0,0 +1,15 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +list(APPEND OCRE_SDK_PRELOADED_IMAGES + "hello-world.wasm" + "blinky.wasm" + "subscriber.wasm" + "publisher.wasm" + "filesystem.wasm" + "filesystem-full.wasm" + "webserver.wasm" + "webserver-complex.wasm" +) diff --git a/src/samples/demo/main.c b/src/samples/demo/main.c new file mode 100644 index 00000000..eb209fe9 --- /dev/null +++ b/src/samples/demo/main.c @@ -0,0 +1,171 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +const struct ocre_container_args args = { + .capabilities = + (const char *[]){ + "ocre:api", + NULL, + }, +}; + +int main(int argc, char *argv[]) +{ + int rc; + int status; + + rc = ocre_initialize(NULL); + if (rc) { + fprintf(stderr, "Failed to initialize runtimes\n"); + return 1; + } + + struct ocre_context *ocre = ocre_create_context(NULL); + if (!ocre) { + fprintf(stderr, "Failed to create ocre context\n"); + return 1; + } + + struct ocre_container *hello_world = + ocre_context_create_container(ocre, "hello-world.wasm", "wamr/wasip1", NULL, false, &args); + + if (!hello_world) { + fprintf(stderr, "Failed to create container\n"); + return 1; + } + + rc = ocre_container_start(hello_world); + if (rc) { + fprintf(stderr, "Failed to start container\n"); + return 1; + } + + rc = ocre_context_remove_container(ocre, hello_world); + if (rc) { + fprintf(stderr, "Failed to remove container\n"); + return 1; + } + + struct ocre_container *blinky = + ocre_context_create_container(ocre, "blinky.wasm", "wamr/wasip1", NULL, true, &args); + + if (!blinky) { + fprintf(stderr, "Failed to create container\n"); + return 1; + } + + rc = ocre_container_start(blinky); + if (rc) { + fprintf(stderr, "Failed to start container\n"); + return 1; + } + + sleep(2); + + rc = ocre_container_kill(blinky); + if (rc) { + fprintf(stderr, "Failed to kill container\n"); + return 1; + } + + rc = ocre_container_wait(blinky, &status); + if (rc) { + fprintf(stderr, "Failed to wait for container\n"); + return 1; + } + + fprintf(stderr, "Container exited with status %d\n", status); + + rc = ocre_context_remove_container(ocre, blinky); + if (rc) { + fprintf(stderr, "Failed to remove container\n"); + return 1; + } + + struct ocre_container *subscriber = + ocre_context_create_container(ocre, "subscriber.wasm", "wamr/wasip1", NULL, true, &args); + + if (!subscriber) { + fprintf(stderr, "Failed to create container\n"); + return 1; + } + + struct ocre_container *publisher = + ocre_context_create_container(ocre, "publisher.wasm", "wamr/wasip1", NULL, true, &args); + + if (!publisher) { + fprintf(stderr, "Failed to create container\n"); + return 1; + } + + rc = ocre_container_start(subscriber); + if (rc) { + fprintf(stderr, "Failed to start container\n"); + return 1; + } + + rc = ocre_container_start(publisher); + if (rc) { + fprintf(stderr, "Failed to start container\n"); + return 1; + } + + sleep(4); + + rc = ocre_container_kill(subscriber); + if (rc) { + fprintf(stderr, "Failed to kill subscriber container\n"); + return 1; + } + + rc = ocre_container_kill(publisher); + if (rc) { + fprintf(stderr, "Failed to kill publisher container\n"); + return 1; + } + + rc = ocre_container_wait(subscriber, &status); + if (rc) { + fprintf(stderr, "Failed to wait for subscriber container\n"); + return 1; + } + + fprintf(stderr, "Subscriber exited with status %d\n", status); + + rc = ocre_container_wait(publisher, &status); + if (rc) { + fprintf(stderr, "Failed to wait for publisher container\n"); + return 1; + } + + fprintf(stderr, "Publisher exited with status %d\n", status); + + rc = ocre_context_remove_container(ocre, subscriber); + if (rc) { + fprintf(stderr, "Failed to remove subscriber\n"); + return 1; + } + + rc = ocre_context_remove_container(ocre, publisher); + if (rc) { + fprintf(stderr, "Failed to remove publisher\n"); + return 1; + } + + ocre_destroy_context(ocre); + + ocre_deinitialize(); + + fprintf(stdout, "Demo completed successfully\n"); + + return 0; +} diff --git a/src/samples/demo/posix/CMakeLists.txt b/src/samples/demo/posix/CMakeLists.txt new file mode 100644 index 00000000..a09a26f9 --- /dev/null +++ b/src/samples/demo/posix/CMakeLists.txt @@ -0,0 +1,28 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +project (ocre_sample_demo) + +add_executable(ocre_demo + ../main.c +) + +target_link_libraries(ocre_demo + PUBLIC + OcreCore +) + +set(THREADS_PREFER_PTHREAD_FLAG TRUE) +find_package(Threads) +target_link_libraries(ocre_demo PRIVATE Threads::Threads) + +add_custom_target(run-demo + COMMAND pwd + COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ocre_demo + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../.. + DEPENDS ocre_demo +) diff --git a/src/samples/demo/zephyr/CMakeLists.txt b/src/samples/demo/zephyr/CMakeLists.txt new file mode 100644 index 00000000..6104ea45 --- /dev/null +++ b/src/samples/demo/zephyr/CMakeLists.txt @@ -0,0 +1,35 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +# This file is to be used to build a zephyr firmware including the ocre demo +# automatically uses the ocre module in the parent directory + +cmake_minimum_required(VERSION 3.20.0) + +# comment out to use the ocre module provided by Zephyr +list(APPEND ZEPHYR_EXTRA_MODULES ${CMAKE_CURRENT_LIST_DIR}/../../../..) + +# append the fstab overlay file to the list of DTC overlay files +list(APPEND EXTRA_DTC_OVERLAY_FILE fstab.overlay) + +include(../demo_containers.cmake) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project (ocre_sample_mini) + +target_sources(app + PRIVATE + ../main.c +) + +target_link_libraries(app + PUBLIC + OcreCore +) + +add_dependencies(app + OcreSampleContainers +) diff --git a/src/samples/demo/zephyr/boards/native_sim_64.conf b/src/samples/demo/zephyr/boards/native_sim_64.conf new file mode 100644 index 00000000..2205c3f0 --- /dev/null +++ b/src/samples/demo/zephyr/boards/native_sim_64.conf @@ -0,0 +1,6 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=10485760 diff --git a/src/samples/demo/zephyr/boards/native_sim_64.overlay b/src/samples/demo/zephyr/boards/native_sim_64.overlay new file mode 100644 index 00000000..848942a0 --- /dev/null +++ b/src/samples/demo/zephyr/boards/native_sim_64.overlay @@ -0,0 +1,14 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&flash0 { + reg = < 0x0 DT_SIZE_M(32) >; +}; + +&storage_partition { + reg = < 0xfc000 DT_SIZE_M(16) >; +}; diff --git a/src/samples/demo/zephyr/boards/pico_plus2_rp2350b_m33.conf b/src/samples/demo/zephyr/boards/pico_plus2_rp2350b_m33.conf new file mode 100644 index 00000000..dbf84249 --- /dev/null +++ b/src/samples/demo/zephyr/boards/pico_plus2_rp2350b_m33.conf @@ -0,0 +1,7 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_SHARED_MULTI_HEAP=y diff --git a/src/samples/demo/zephyr/boards/pico_plus2_rp2350b_m33.overlay b/src/samples/demo/zephyr/boards/pico_plus2_rp2350b_m33.overlay new file mode 100644 index 00000000..425ed0d8 --- /dev/null +++ b/src/samples/demo/zephyr/boards/pico_plus2_rp2350b_m33.overlay @@ -0,0 +1,19 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + storage_partition: partition@600000 { + label = "storage"; + reg = <0x410000 0x800000>; + }; + }; +}; diff --git a/src/samples/demo/zephyr/boards/pico_plus2_rp2350b_m33_w.conf b/src/samples/demo/zephyr/boards/pico_plus2_rp2350b_m33_w.conf new file mode 100644 index 00000000..dbf84249 --- /dev/null +++ b/src/samples/demo/zephyr/boards/pico_plus2_rp2350b_m33_w.conf @@ -0,0 +1,7 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_SHARED_MULTI_HEAP=y diff --git a/src/samples/demo/zephyr/boards/pico_plus2_rp2350b_m33_w.overlay b/src/samples/demo/zephyr/boards/pico_plus2_rp2350b_m33_w.overlay new file mode 100644 index 00000000..425ed0d8 --- /dev/null +++ b/src/samples/demo/zephyr/boards/pico_plus2_rp2350b_m33_w.overlay @@ -0,0 +1,19 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + storage_partition: partition@600000 { + label = "storage"; + reg = <0x410000 0x800000>; + }; + }; +}; diff --git a/src/samples/demo/zephyr/fstab.overlay b/src/samples/demo/zephyr/fstab.overlay new file mode 100644 index 00000000..811beedd --- /dev/null +++ b/src/samples/demo/zephyr/fstab.overlay @@ -0,0 +1,23 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + fstab { + compatible = "zephyr,fstab"; + lfs1: lfs1 { + compatible = "zephyr,fstab,littlefs"; + read-size = <256>; + prog-size = <256>; + cache-size = <256>; + lookahead-size = <256>; + block-cycles = <512>; + partition = <&storage_partition>; + mount-point = "/lfs"; + automount; + }; + }; +}; diff --git a/src/samples/demo/zephyr/prj.conf b/src/samples/demo/zephyr/prj.conf new file mode 100644 index 00000000..d5fe8692 --- /dev/null +++ b/src/samples/demo/zephyr/prj.conf @@ -0,0 +1,26 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +# Logging +CONFIG_LOG=y +CONFIG_LOG_MODE_MINIMAL=y + +# Filesystem +CONFIG_ZVFS_POLL_MAX=10 +CONFIG_ZVFS_OPEN_MAX=10 + +# Memory optopns +CONFIG_DYNAMIC_THREAD_STACK_SIZE=8192 +CONFIG_MAIN_STACK_SIZE=8192 +CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=-1 + +# Ocre configuration +CONFIG_OCRE=y +CONFIG_OCRE_TIMER=y +CONFIG_OCRE_CONTAINER_MESSAGING=y +CONFIG_OCRE_WAMR_AOT=y +CONFIG_OCRE_SHARED_HEAP=y +#CONFIG_OCRE_GPIO=y +#CONFIG_OCRE_SENSORS=y diff --git a/src/samples/mini/hello-world.wasm b/src/samples/mini/hello-world.wasm new file mode 100755 index 00000000..818f466e Binary files /dev/null and b/src/samples/mini/hello-world.wasm differ diff --git a/src/samples/mini/input_file.cmake b/src/samples/mini/input_file.cmake new file mode 100644 index 00000000..a8a95bf7 --- /dev/null +++ b/src/samples/mini/input_file.cmake @@ -0,0 +1,29 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +if(OCRE_INPUT_FILE) + message(STATUS "Using user input file: ${OCRE_INPUT_FILE}") +else() + message(STATUS "Using default input file: hello-world.wasm") + set(OCRE_INPUT_FILE "${CMAKE_CURRENT_LIST_DIR}/hello-world.wasm") +endif() + +add_custom_command( + OUTPUT input_file.hex + COMMAND od -v -An -tx1 "${OCRE_INPUT_FILE}" > input_file.hex + DEPENDS ${OCRE_INPUT_FILE} + COMMENT "Generating hex file from ${OCRE_INPUT_FILE}" +) + +add_custom_command( + OUTPUT input_file.c + COMMAND awk -f "${CMAKE_CURRENT_LIST_DIR}/../../../scripts/c_array.awk" input_file.hex > input_file.c + DEPENDS + input_file.hex + ${CMAKE_CURRENT_LIST_DIR}/../../../scripts/c_array.awk + COMMENT "Generating C array" +) + +add_custom_target(input_file DEPENDS input_file.c) diff --git a/src/samples/mini/main.c b/src/samples/mini/main.c new file mode 100644 index 00000000..d527c011 --- /dev/null +++ b/src/samples/mini/main.c @@ -0,0 +1,125 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef CONFIG_ARCH_POSIX +#include "nsi_main.h" +#endif + +extern const unsigned char ocre_mini_sample_image[]; +extern const size_t ocre_mini_sample_image_len; + +static int create_sample_file(char *path) +{ + int fd = open(path, O_CREAT | O_WRONLY, 0644); + if (fd == -1) { + perror("Failed to create sample file"); + return -1; + } + + ssize_t bytes_written = write(fd, ocre_mini_sample_image, ocre_mini_sample_image_len); + if (bytes_written != ocre_mini_sample_image_len) { + perror("Failed to write to sample file"); + close(fd); + return -1; + } + + close(fd); + return 0; +} + +int main(int argc, char *argv[]) +{ + int rc; + + rc = ocre_initialize(NULL); + if (rc) { + fprintf(stderr, "Failed to initialize runtimes\n"); + return 1; + } + + struct ocre_context *ocre = ocre_create_context(NULL); + if (!ocre) { + fprintf(stderr, "Failed to create ocre context\n"); + return 1; + } + + const char *workdir = ocre_context_get_working_directory(ocre); + if (!workdir) { + fprintf(stderr, "Failed to get working directory\n"); + return 1; + } + + char *sample_path = malloc(strlen(workdir) + strlen("/images/sample.wasm") + 1); + if (!sample_path) { + fprintf(stderr, "Failed to allocate memory for sample path\n"); + return 1; + } + sprintf(sample_path, "%s/images/sample.wasm", workdir); + + struct stat st; + + if (stat(sample_path, &st) == -1) { + if (errno == ENOENT) { + fprintf(stderr, "Creating sample file '%s'\n", sample_path); + if (create_sample_file(sample_path)) { + fprintf(stderr, "Failed to create sample file\n"); + free(sample_path); + return 1; + } + } else { + perror("Failed to stat sample file"); + free(sample_path); + return 1; + } + } else { + fprintf(stderr, "Sample file '%s' already exists\n", sample_path); + } + + free(sample_path); + + struct ocre_container *container = + ocre_context_create_container(ocre, "sample.wasm", "wamr/wasip1", NULL, false, NULL); + + if (!container) { + fprintf(stderr, "Failed to create container\n"); + return 1; + } + + rc = ocre_container_start(container); + if (rc) { + fprintf(stderr, "Failed to start container\n"); + return 1; + } + + rc = ocre_context_remove_container(ocre, container); + if (rc) { + fprintf(stderr, "Failed to remove container\n"); + return 1; + } + + ocre_destroy_context(ocre); + + ocre_deinitialize(); + + /* Exit simulator on zephyr */ + +#ifdef CONFIG_ARCH_POSIX + nsi_exit(0); +#endif + + return 0; +} diff --git a/src/samples/mini/posix/CMakeLists.txt b/src/samples/mini/posix/CMakeLists.txt new file mode 100644 index 00000000..1dee47a5 --- /dev/null +++ b/src/samples/mini/posix/CMakeLists.txt @@ -0,0 +1,31 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +project (ocre_sample_mini) + +include(../input_file.cmake) + +add_executable(ocre_mini + ../main.c + input_file.c +) + +target_link_libraries(ocre_mini + PUBLIC + OcreCore +) + +set(THREADS_PREFER_PTHREAD_FLAG TRUE) +find_package(Threads) +target_link_libraries(ocre_mini PRIVATE Threads::Threads) + +add_custom_target(run-mini + COMMAND pwd + COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ocre_mini + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../.. + DEPENDS ocre_mini +) diff --git a/src/samples/mini/zephyr/CMakeLists.txt b/src/samples/mini/zephyr/CMakeLists.txt new file mode 100644 index 00000000..b1bbd02c --- /dev/null +++ b/src/samples/mini/zephyr/CMakeLists.txt @@ -0,0 +1,32 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +# This file is to be used to build a zephyr firmware including the ocre demo +# automatically uses the ocre module in the parent directory + +cmake_minimum_required(VERSION 3.20.0) + +# comment out to use the ocre module provided by Zephyr +list(APPEND ZEPHYR_EXTRA_MODULES ${CMAKE_CURRENT_LIST_DIR}/../../../..) + +# append the fstab overlay file to the list of DTC overlay files +list(APPEND EXTRA_DTC_OVERLAY_FILE fstab.overlay) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project (ocre_sample_mini) + +include(../input_file.cmake) + +target_sources(app + PRIVATE + ../main.c + input_file.c +) + +target_link_libraries(app + PUBLIC + OcreCore +) diff --git a/src/samples/mini/zephyr/boards/native_sim_64.conf b/src/samples/mini/zephyr/boards/native_sim_64.conf new file mode 100644 index 00000000..2205c3f0 --- /dev/null +++ b/src/samples/mini/zephyr/boards/native_sim_64.conf @@ -0,0 +1,6 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=10485760 diff --git a/src/samples/mini/zephyr/boards/native_sim_64.overlay b/src/samples/mini/zephyr/boards/native_sim_64.overlay new file mode 100644 index 00000000..848942a0 --- /dev/null +++ b/src/samples/mini/zephyr/boards/native_sim_64.overlay @@ -0,0 +1,14 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&flash0 { + reg = < 0x0 DT_SIZE_M(32) >; +}; + +&storage_partition { + reg = < 0xfc000 DT_SIZE_M(16) >; +}; diff --git a/src/samples/mini/zephyr/boards/pico_plus2_rp2350b_m33.conf b/src/samples/mini/zephyr/boards/pico_plus2_rp2350b_m33.conf new file mode 100644 index 00000000..dbf84249 --- /dev/null +++ b/src/samples/mini/zephyr/boards/pico_plus2_rp2350b_m33.conf @@ -0,0 +1,7 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_SHARED_MULTI_HEAP=y diff --git a/src/samples/mini/zephyr/boards/pico_plus2_rp2350b_m33.overlay b/src/samples/mini/zephyr/boards/pico_plus2_rp2350b_m33.overlay new file mode 100644 index 00000000..425ed0d8 --- /dev/null +++ b/src/samples/mini/zephyr/boards/pico_plus2_rp2350b_m33.overlay @@ -0,0 +1,19 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + storage_partition: partition@600000 { + label = "storage"; + reg = <0x410000 0x800000>; + }; + }; +}; diff --git a/src/samples/mini/zephyr/boards/pico_plus2_rp2350b_m33_w.conf b/src/samples/mini/zephyr/boards/pico_plus2_rp2350b_m33_w.conf new file mode 100644 index 00000000..d2e7b389 --- /dev/null +++ b/src/samples/mini/zephyr/boards/pico_plus2_rp2350b_m33_w.conf @@ -0,0 +1,7 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_TEST_RANDOM_GENERATOR=y +#CONFIG_SHARED_MULTI_HEAP=y diff --git a/src/samples/mini/zephyr/boards/pico_plus2_rp2350b_m33_w.overlay b/src/samples/mini/zephyr/boards/pico_plus2_rp2350b_m33_w.overlay new file mode 100644 index 00000000..425ed0d8 --- /dev/null +++ b/src/samples/mini/zephyr/boards/pico_plus2_rp2350b_m33_w.overlay @@ -0,0 +1,19 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + storage_partition: partition@600000 { + label = "storage"; + reg = <0x410000 0x800000>; + }; + }; +}; diff --git a/src/samples/mini/zephyr/fstab.overlay b/src/samples/mini/zephyr/fstab.overlay new file mode 100644 index 00000000..fe261aac --- /dev/null +++ b/src/samples/mini/zephyr/fstab.overlay @@ -0,0 +1,23 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + + / { + fstab { + compatible = "zephyr,fstab"; + lfs1: lfs1 { + compatible = "zephyr,fstab,littlefs"; + read-size = <256>; + prog-size = <256>; + cache-size = <256>; + lookahead-size = <256>; + block-cycles = <512>; + partition = <&storage_partition>; + mount-point = "/lfs"; + automount; + }; + }; +}; diff --git a/src/samples/mini/zephyr/prj.conf b/src/samples/mini/zephyr/prj.conf new file mode 100644 index 00000000..7a6358e3 --- /dev/null +++ b/src/samples/mini/zephyr/prj.conf @@ -0,0 +1,20 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +# Logging +CONFIG_LOG=y +CONFIG_LOG_MODE_MINIMAL=y + +# Filesystem +CONFIG_ZVFS_POLL_MAX=10 +CONFIG_ZVFS_OPEN_MAX=10 + +# Memory optopns +CONFIG_DYNAMIC_THREAD_STACK_SIZE=8192 +CONFIG_MAIN_STACK_SIZE=8192 +CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=-1 + +# Ocre configuration +CONFIG_OCRE=y diff --git a/src/samples/static_checks/posix/CMakeLists.txt b/src/samples/static_checks/posix/CMakeLists.txt new file mode 100644 index 00000000..a0f08a5f --- /dev/null +++ b/src/samples/static_checks/posix/CMakeLists.txt @@ -0,0 +1,28 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +project (ocre_cmd) + +add_executable(ocre_cmd + ocre.c + download_file.c +) + +target_link_libraries(ocre_cmd + PUBLIC + OcreCore + OcreShell +) + +target_link_libraries(OcreShell + PUBLIC + OcreCore +) + +set(THREADS_PREFER_PTHREAD_FLAG TRUE) +find_package(Threads) +target_link_libraries(ocre_cmd PRIVATE Threads::Threads) diff --git a/src/samples/static_checks/posix/download_file.c b/src/samples/static_checks/posix/download_file.c new file mode 100644 index 00000000..54814f67 --- /dev/null +++ b/src/samples/static_checks/posix/download_file.c @@ -0,0 +1,13 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +int ocre_download_file(const char *url, const char *filepath) +{ + /* Not implemented */ + + return -1; +} diff --git a/src/samples/static_checks/posix/ocre.c b/src/samples/static_checks/posix/ocre.c new file mode 100644 index 00000000..27c3c03f --- /dev/null +++ b/src/samples/static_checks/posix/ocre.c @@ -0,0 +1,40 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include + +/* keep a reference to the single instance of the runtime */ + +struct ocre_context *ocre_global_context = NULL; + +int main(int argc, char *argv[]) +{ + /* Initialize Ocre */ + + if (ocre_initialize(NULL)) { + return -1; + } + + fprintf(stderr, "Initialized Ocre\n"); + + /* Create a context */ + + ocre_global_context = ocre_create_context(NULL); + if (!ocre_global_context) { + fprintf(stderr, "Failed to create Ocre context\n"); + ocre_deinitialize(); + return -1; + } + + fprintf(stderr, "Created Ocre context: %p\n", ocre_global_context); + + return ocre_shell(ocre_global_context, argc, argv); +} diff --git a/src/samples/supervisor/zephyr/CMakeLists.txt b/src/samples/supervisor/zephyr/CMakeLists.txt new file mode 100644 index 00000000..cb9094ec --- /dev/null +++ b/src/samples/supervisor/zephyr/CMakeLists.txt @@ -0,0 +1,38 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +# This file is to be used to build a zephyr firmware including the ocre demo +# automatically uses the ocre module in the parent directory + +cmake_minimum_required(VERSION 3.20.0) + +# comment out to use the ocre module provided by zephyr +list(APPEND ZEPHYR_EXTRA_MODULES ${CMAKE_CURRENT_LIST_DIR}/../../../..) + +# append the fstab overlay file to the list of DTC overlay files +list(APPEND EXTRA_DTC_OVERLAY_FILE fstab.overlay) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project (ocre_sample_supervisor) + +target_sources(app + PRIVATE + service.c + shell.c + download_file.c +) + +target_link_libraries(app + PUBLIC + OcreCore + OcreShell +) + +target_link_libraries(OcreShell + PUBLIC + zephyr_interface + OcreCore +) diff --git a/src/samples/supervisor/zephyr/boards/b_u585i_iot02a.conf b/src/samples/supervisor/zephyr/boards/b_u585i_iot02a.conf new file mode 100644 index 00000000..8ff854c0 --- /dev/null +++ b/src/samples/supervisor/zephyr/boards/b_u585i_iot02a.conf @@ -0,0 +1,7 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_STM32_MEMMAP=y +CONFIG_OCRE_MERGE_HEX=y diff --git a/boards/b_u585i_iot02a.overlay b/src/samples/supervisor/zephyr/boards/b_u585i_iot02a.overlay similarity index 83% rename from boards/b_u585i_iot02a.overlay rename to src/samples/supervisor/zephyr/boards/b_u585i_iot02a.overlay index 1419cd47..5ace830c 100644 --- a/boards/b_u585i_iot02a.overlay +++ b/src/samples/supervisor/zephyr/boards/b_u585i_iot02a.overlay @@ -1,3 +1,10 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + #include / { @@ -48,7 +55,7 @@ // Add labels for sensors defined in the board .dtsi file, and any other user configuration // This is required so we can reference them in the devices block above, which is used by ocre_sensors &i2c2 { - ism330dhcx: ism330dhcx@6b { + ism330dhcx: ism330dhcx@6b { label = "imu"; status = "okay"; accel-odr = <4>; // 104 Hz @@ -56,19 +63,19 @@ gyro-odr = <4>; // 104 Hz gyro-range = <250>; // ±250 dps }; - lps22hh: lps22hh@5d { + lps22hh: lps22hh@5d { label = "pressure"; status = "okay"; }; - hts221: hts221@5f { + hts221: hts221@5f { label = "humidity"; status = "okay"; }; - iis2mdc: iis2mdc@1e { + iis2mdc: iis2mdc@1e { label = "magnetometer"; status = "okay"; }; - veml6030: veml6030@10 { + veml6030: veml6030@10 { label = "light"; status = "okay"; }; @@ -89,7 +96,7 @@ /* Flash partitions - 2MB total, no MCUboot */ &flash0 { /delete-node/ partitions; - + partitions { compatible = "fixed-partitions"; #address-cells = <1>; @@ -102,19 +109,20 @@ }; /* Dummy slot1 partition for MCUboot compatibility (unused) */ slot1_partition: partition@1F5000 { - label = "image-1"; + label = "image-1"; reg = <0x001F5000 DT_SIZE_K(44)>; }; }; }; // 64MB external flash -&mx25lm51245 { +&mx25lm51245 { // -el "/usr/local/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/ExternalLoader/MX25LM51245G_STM32U585I-IOT02A.stldr" + reg = <0x70000000 DT_SIZE_M(64)>; partitions { /delete-node/ partition; /* Use the whole flash for the filesystem. */ - user_data_partition: storage_partition: partition@0 { + storage_partition: partition@0 { label = "user_data"; reg = <0x00000000 DT_SIZE_M(64)>; }; diff --git a/src/samples/supervisor/zephyr/boards/native_sim_64.conf b/src/samples/supervisor/zephyr/boards/native_sim_64.conf new file mode 100644 index 00000000..2205c3f0 --- /dev/null +++ b/src/samples/supervisor/zephyr/boards/native_sim_64.conf @@ -0,0 +1,6 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=10485760 diff --git a/src/samples/supervisor/zephyr/boards/native_sim_64.overlay b/src/samples/supervisor/zephyr/boards/native_sim_64.overlay new file mode 100644 index 00000000..848942a0 --- /dev/null +++ b/src/samples/supervisor/zephyr/boards/native_sim_64.overlay @@ -0,0 +1,14 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&flash0 { + reg = < 0x0 DT_SIZE_M(32) >; +}; + +&storage_partition { + reg = < 0xfc000 DT_SIZE_M(16) >; +}; diff --git a/src/samples/supervisor/zephyr/boards/pico_plus2_rp2350b_m33.conf b/src/samples/supervisor/zephyr/boards/pico_plus2_rp2350b_m33.conf new file mode 100644 index 00000000..dbf84249 --- /dev/null +++ b/src/samples/supervisor/zephyr/boards/pico_plus2_rp2350b_m33.conf @@ -0,0 +1,7 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_SHARED_MULTI_HEAP=y diff --git a/src/samples/supervisor/zephyr/boards/pico_plus2_rp2350b_m33.overlay b/src/samples/supervisor/zephyr/boards/pico_plus2_rp2350b_m33.overlay new file mode 100644 index 00000000..425ed0d8 --- /dev/null +++ b/src/samples/supervisor/zephyr/boards/pico_plus2_rp2350b_m33.overlay @@ -0,0 +1,19 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + storage_partition: partition@600000 { + label = "storage"; + reg = <0x410000 0x800000>; + }; + }; +}; diff --git a/src/samples/supervisor/zephyr/boards/pico_plus2_rp2350b_m33_w.conf b/src/samples/supervisor/zephyr/boards/pico_plus2_rp2350b_m33_w.conf new file mode 100644 index 00000000..57a314b6 --- /dev/null +++ b/src/samples/supervisor/zephyr/boards/pico_plus2_rp2350b_m33_w.conf @@ -0,0 +1,9 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_SHARED_MULTI_HEAP=y +CONFIG_WIFI=y +CONFIG_NET_L2_WIFI_SHELL=y diff --git a/src/samples/supervisor/zephyr/boards/pico_plus2_rp2350b_m33_w.overlay b/src/samples/supervisor/zephyr/boards/pico_plus2_rp2350b_m33_w.overlay new file mode 100644 index 00000000..425ed0d8 --- /dev/null +++ b/src/samples/supervisor/zephyr/boards/pico_plus2_rp2350b_m33_w.overlay @@ -0,0 +1,19 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + storage_partition: partition@600000 { + label = "storage"; + reg = <0x410000 0x800000>; + }; + }; +}; diff --git a/src/samples/supervisor/zephyr/boards/pico_plus2_rp2350b_m33_w_mcuboot.conf b/src/samples/supervisor/zephyr/boards/pico_plus2_rp2350b_m33_w_mcuboot.conf new file mode 100644 index 00000000..57a314b6 --- /dev/null +++ b/src/samples/supervisor/zephyr/boards/pico_plus2_rp2350b_m33_w_mcuboot.conf @@ -0,0 +1,9 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_SHARED_MULTI_HEAP=y +CONFIG_WIFI=y +CONFIG_NET_L2_WIFI_SHELL=y diff --git a/src/samples/supervisor/zephyr/download_file.c b/src/samples/supervisor/zephyr/download_file.c new file mode 100644 index 00000000..ba364330 --- /dev/null +++ b/src/samples/supervisor/zephyr/download_file.c @@ -0,0 +1,206 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define OCRE_DOWNLOAD_RESPONSE_BUFFER_SIZE (256) + +static int response_cb(struct http_response *rsp, enum http_final_call final_data, void *user_data) +{ + int fd = *(int *)user_data; + + if (rsp->http_status_code != HTTP_200_OK) { + fprintf(stderr, "Got invalid HTTP status code %d\n", rsp->http_status_code); + return -1; + } + + if (rsp->body_frag_start && rsp->body_frag_len) { + size_t body_frag_len = rsp->data_len - (rsp->body_frag_start - rsp->recv_buf); + + int rc = write(fd, rsp->body_frag_start, body_frag_len); + if (rc < 0) { + fprintf(stderr, "Failed to write file: %d\n", rc); + return -1; + } + } + + return 0; +} + +int ocre_download_file(const char *url, const char *filepath) +{ + int rc = -1; + int fd = -1; + int sock = -1; + char *hostname = NULL; + struct addrinfo *res = NULL; + char *response = NULL; + + /* Parse URL */ + + struct http_parser_url parsed_url; + http_parser_url_init(&parsed_url); + http_parser_parse_url(url, strlen(url), 0, &parsed_url); + + if (!(parsed_url.field_set & (1 << UF_SCHEMA))) { + fprintf(stderr, "Missing URL schema. Use 'http:// in URL\n"); + goto finish; + } + + if (!(parsed_url.field_set & (1 << UF_PATH))) { + fprintf(stderr, "Missing URL path. Use 'http:/// in URL\n"); + goto finish; + } + + uint16_t port = 0; + char port_str[6]; + + if (!strncmp("http", url + parsed_url.field_data[UF_SCHEMA].off, parsed_url.field_data[UF_SCHEMA].len)) { + port = 80; + strcpy(port_str, "80"); + // } else if (!strncmp("https", url + parsed_url.field_data[UF_SCHEMA].off, + // parsed_url.field_data[UF_SCHEMA].len)) { + // port = 443; + // strcpy(port_str, "443"); + } else { + fprintf(stderr, "Unsupported URL schema: '%s'", url + parsed_url.field_data[UF_SCHEMA].off); + goto finish; + } + + if (parsed_url.field_set & (1 << UF_PORT)) { + port = parsed_url.port; + memcpy(port_str, url + parsed_url.field_data[UF_PORT].off, parsed_url.field_data[UF_PORT].len); + port_str[parsed_url.field_data[UF_PORT].len] = '\0'; + } + + if (!(parsed_url.field_set & (1 << UF_HOST))) { + fprintf(stderr, "Missing URL host. Use 'http:// in URL\n"); + goto finish; + } + + response = malloc(OCRE_DOWNLOAD_RESPONSE_BUFFER_SIZE); + if (!response) { + fprintf(stderr, "Failed to allocate memory for response buffer\n"); + goto finish; + } + + /* Resolve hostname */ + + hostname = malloc(parsed_url.field_data[UF_HOST].len + 1); + if (!hostname) { + fprintf(stderr, "Failed to allocate memory for hostname"); + goto finish; + } + + memcpy(hostname, url + parsed_url.field_data[UF_HOST].off, parsed_url.field_data[UF_HOST].len); + hostname[parsed_url.field_data[UF_HOST].len] = '\0'; + + static struct addrinfo hints; + int st; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + st = getaddrinfo(hostname, port_str, &hints, &res); + + printf("getaddrinfo status: %d\n", st); + + if (st != 0) { + fprintf(stderr, "Unable to resolve address: %s\n", hostname); + goto finish; + } + + /* Create file */ + + fd = open(filepath, O_CREAT | O_WRONLY, 0644); + if (fd < 0) { + fprintf(stderr, "Failed to create file '%s'\n", filepath); + goto finish; + } + + fprintf(stderr, "created file %s\n", filepath); + + /* Socket */ + + sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + + if (sock < 0) { + fprintf(stderr, "Error %d on socket()\n", sock); + goto finish; + } + + /* Connection */ + + st = connect(sock, res->ai_addr, res->ai_addrlen); + if (st < 0) { + fprintf(stderr, "Error %d on connect(), errno: %d\n", st, errno); + goto finish; + } + + /* HTTP Request */ + + struct http_request req; + + int32_t timeout = 3 * MSEC_PER_SEC; + + memset(&req, 0, sizeof(req)); + + req.method = HTTP_GET; + req.url = url + parsed_url.field_data[UF_PATH].off; + req.host = hostname; + req.protocol = "HTTP/1.1"; + req.response = response_cb; + req.recv_buf = response; + req.recv_buf_len = OCRE_DOWNLOAD_RESPONSE_BUFFER_SIZE; + + st = http_client_req(sock, &req, timeout, (void *)&fd); + fprintf(stderr, "st is %d\n", st); + if (st < 0) { + fprintf(stderr, "HTTP Client error %d\n", st); + goto finish; + } else { + fprintf(stderr, "finished downloading file!!!!\n"); + } + + rc = 0; + +finish: + + if (fd >= 0) { + close(fd); + } + + if (sock >= 0) { + close(sock); + } + + if (rc) { + unlink(filepath); + } + + if (res) { + freeaddrinfo(res); + } + + free(hostname); + + free(response); + + return rc; +} diff --git a/src/samples/supervisor/zephyr/fstab.overlay b/src/samples/supervisor/zephyr/fstab.overlay new file mode 100644 index 00000000..811beedd --- /dev/null +++ b/src/samples/supervisor/zephyr/fstab.overlay @@ -0,0 +1,23 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + fstab { + compatible = "zephyr,fstab"; + lfs1: lfs1 { + compatible = "zephyr,fstab,littlefs"; + read-size = <256>; + prog-size = <256>; + cache-size = <256>; + lookahead-size = <256>; + block-cycles = <512>; + partition = <&storage_partition>; + mount-point = "/lfs"; + automount; + }; + }; +}; diff --git a/src/samples/supervisor/zephyr/prj.conf b/src/samples/supervisor/zephyr/prj.conf new file mode 100644 index 00000000..dffe954e --- /dev/null +++ b/src/samples/supervisor/zephyr/prj.conf @@ -0,0 +1,48 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_SHELL=y +CONFIG_BOOT_BANNER=y +CONFIG_SHELL_PROMPT_UART="ocre:~$ " +CONFIG_NET_SHELL=y +CONFIG_FILE_SYSTEM_SHELL=y + +CONFIG_LOG=y + +# Networking options +CONFIG_NET_TX_STACK_SIZE=2048 +CONFIG_NET_BUF_TX_COUNT=16 +CONFIG_NET_RX_STACK_SIZE=4096 +CONFIG_NET_BUF_RX_COUNT=32 +CONFIG_NET_MGMT_EVENT_STACK_SIZE=2048 + +# file descriptors +CONFIG_ZVFS_POLL_MAX=16 +CONFIG_ZVFS_OPEN_MAX=16 + +CONFIG_NET_DHCPV4=y + +# Memory optopns +CONFIG_DYNAMIC_THREAD_STACK_SIZE=8192 +CONFIG_MAIN_STACK_SIZE=8192 +CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=-1 +CONFIG_SHELL_STACK_SIZE=8192 + +CONFIG_MAX_PTHREAD_MUTEX_COUNT=32 +CONFIG_POSIX_THREAD_THREADS_MAX=32 + +# Ocre configuration +CONFIG_OCRE=y +CONFIG_OCRE_NETWORKING=y +CONFIG_OCRE_SHELL=y +CONFIG_OCRE_TIMER=y +CONFIG_OCRE_CONTAINER_MESSAGING=y +#CONFIG_SENSOR=y +#CONFIG_RNG_SENSOR=y + +CONFIG_SHELL_GETOPT=y + +CONFIG_DEBUG=y +CONFIG_LOG_MODE_IMMEDIATE=y diff --git a/src/samples/supervisor/zephyr/service.c b/src/samples/supervisor/zephyr/service.c new file mode 100644 index 00000000..38cd7e25 --- /dev/null +++ b/src/samples/supervisor/zephyr/service.c @@ -0,0 +1,41 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +/* Keep a reference to the single instance of the runtime */ + +struct ocre_context *ocre_global_context = NULL; + +static int ocre_service_init() +{ + /* Initialize Ocre Library */ + + if (ocre_initialize(NULL)) { + return -1; + } + + fprintf(stderr, "Initialized Ocre\n"); + + /* Create a context */ + + ocre_global_context = ocre_create_context("/lfs/ocre"); + if (!ocre_global_context) { + fprintf(stderr, "Failed to create Ocre context\n"); + ocre_deinitialize(); + return -1; + } + + fprintf(stderr, "Created Ocre context: %p\n", ocre_global_context); + + return 0; +} + +SYS_INIT(ocre_service_init, APPLICATION, 0); diff --git a/src/samples/supervisor/zephyr/shell.c b/src/samples/supervisor/zephyr/shell.c new file mode 100644 index 00000000..36363ff4 --- /dev/null +++ b/src/samples/supervisor/zephyr/shell.c @@ -0,0 +1,23 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +#include +#include + +extern struct ocre_context *ocre_global_context; + +static int cmd_ocre_shell(const struct shell *sh, size_t argc, char **argv) +{ + return ocre_shell(ocre_global_context, argc, argv); +} + +SHELL_CMD_REGISTER(ocre, NULL, "Ocre Runtime management", cmd_ocre_shell); diff --git a/src/shared/platform/ocre_psram.h b/src/shared/platform/ocre_psram.h deleted file mode 100644 index 6f37e98d..00000000 --- a/src/shared/platform/ocre_psram.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef OCRE_PSRAM -#define OCRE_PSRAM - -// PSRAM configuration - centralized for different platforms -#if defined(CONFIG_MEMC) -// Board-specific PSRAM section attributes -#if defined(CONFIG_BOARD_ARDUINO_PORTENTA_H7) -#define PSRAM_SECTION_ATTR __attribute__((section("SDRAM1"), aligned(32))) -#elif defined(CONFIG_BOARD_B_U585I_IOT02A) -#define PSRAM_SECTION_ATTR __attribute__((section(".stm32_psram"), aligned(32))) -#elif defined(CONFIG_BOARD_MIMXRT1064_EVK) -#define PSRAM_SECTION_ATTR __attribute__((section("SDRAM"), aligned(32))) -#else -#define PSRAM_SECTION_ATTR __attribute__((aligned(32))) -#endif - -PSRAM_SECTION_ATTR -static char storage_heap_buf[CONFIG_OCRE_STORAGE_HEAP_BUFFER_SIZE] = {0}; - -static struct k_heap storage_heap; -#define storage_heap_init() k_heap_init(&storage_heap, storage_heap_buf, CONFIG_OCRE_STORAGE_HEAP_BUFFER_SIZE) -#define storage_heap_alloc(size) k_heap_alloc(&storage_heap, (size), K_SECONDS(1)) -#define storage_heap_free(buffer) k_heap_free(&storage_heap, (void *)buffer) -#else -// No PSRAM - use system malloc -#define storage_heap_init() /* No initialization needed */ -#define storage_heap_alloc(size) malloc(size) -#define storage_heap_free(buffer) free(buffer) -#endif - -#endif /* OCRE_PSRAM*/ diff --git a/src/shared/platform/posix/core_fs.c b/src/shared/platform/posix/core_fs.c deleted file mode 100644 index 8881dd2c..00000000 --- a/src/shared/platform/posix/core_fs.c +++ /dev/null @@ -1,168 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ocre_core_external.h" - -#define MAX_PATH_LEN 256 - -/* -When containers are passed locally, from the filesystem - we don't need to construct the path. -Instead, we just use the original file's location. This is indicated whether the container was passed as a buffer, -or as a program parameter. Encapsulation is used to avoid global variables. -*/ -static int stored_argc = 0; - -void set_argc(int argc) -{ - stored_argc = argc; -} - -/** - * @brief Retrieves the stored argc value. - * - * Returns the argc value previously set by set_argc(). - * Useful for conditional logic based on argument count. - * - * @return The stored argc value. - */ -static int get_argc() -{ - return stored_argc; -} - -int core_construct_filepath(char *path, size_t len, char *name) -{ - char cwd[PATH_MAX]; - if (getcwd(cwd, sizeof(cwd)) != NULL) { - // Shall be in /build folder - LOG_DBG("Current working dir: %s", cwd); - } - if (get_argc() > 1) { - strcpy(path, name); - return 0; - } else { - return snprintf(path, len, "%s/%s/%s.bin", cwd, APP_RESOURCE_PATH, name); - } -} - -int core_filestat(const char *path, size_t *size) -{ - struct stat st; - if (stat(path, &st) == 0) { - if (size) { - *size = st.st_size; - } - return 0; // success - } else { - return errno; // return the specific error code - } -} - -int core_fileopen(const char *path, void **handle) -{ - int *fd = core_malloc(sizeof(int)); - if (!fd) - return -ENOMEM; - *fd = open(path, O_RDONLY); - if (*fd < 0) { - core_free(fd); - return -errno; - } - *handle = fd; - return 0; -} - -int core_fileread(void *handle, void *buffer, size_t size) -{ - int fd = *(int *)handle; - ssize_t bytes_read = read(fd, buffer, size); - if (bytes_read < 0) - return -errno; - if ((size_t)bytes_read != size) - return -EIO; - return 0; -} - -int core_fileclose(void *handle) -{ - int fd = *(int *)handle; - int ret = close(fd); - core_free(handle); - return ret; -} - -static int lsdir(const char *path) -{ - DIR *dirp; - struct dirent *entry; - - dirp = opendir(path); - if (!dirp) { - LOG_ERR("Error opening dir %s [%d]", path, errno); - return -errno; - } - - while ((entry = readdir(dirp)) != NULL) { - LOG_DBG("Found: %s", entry->d_name); - } - - if (closedir(dirp) < 0) { - LOG_ERR("Error closing dir [%d]", errno); - return -errno; - } - - return 0; -} - -void ocre_app_storage_init() -{ - struct stat st; - - if (stat(OCRE_BASE_PATH, &st) == -1) { - if (mkdir(OCRE_BASE_PATH, 0755) < 0) { - LOG_ERR("Failed to create directory %s [%d]", OCRE_BASE_PATH, errno); - } - } - - if (stat(APP_RESOURCE_PATH, &st) == -1) { - if (mkdir(APP_RESOURCE_PATH, 0755) < 0) { - LOG_ERR("Failed to create directory %s [%d]", APP_RESOURCE_PATH, errno); - } - } - - if (stat(PACKAGE_BASE_PATH, &st) == -1) { - if (mkdir(PACKAGE_BASE_PATH, 0755) < 0) { - LOG_ERR("Failed to create directory %s [%d]", PACKAGE_BASE_PATH, errno); - } - } - - if (stat(CONFIG_PATH, &st) == -1) { - if (mkdir(CONFIG_PATH, 0755) < 0) { - LOG_ERR("Failed to create directory %s [%d]", CONFIG_PATH, errno); - } - } - -#ifdef CONFIG_OCRE_CONTAINER_FILESYSTEM - if (stat(CONTAINER_FS_PATH, &st) == -1) { - if (mkdir(CONTAINER_FS_PATH, 0755) < 0) { - LOG_ERR("Failed to create directory %s [%d]", CONTAINER_FS_PATH, errno); - } - } -#endif - - lsdir(OCRE_BASE_PATH); -} diff --git a/src/shared/platform/posix/core_internal.h b/src/shared/platform/posix/core_internal.h deleted file mode 100644 index a41bd21e..00000000 --- a/src/shared/platform/posix/core_internal.h +++ /dev/null @@ -1,294 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef OCRE_CORE_INTERNAL_H -#define OCRE_CORE_INTERNAL_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -// Config macros -#define CONFIG_OCRE_CONTAINER_MESSAGING /*!< Enable container messaging support */ -#define CONFIG_OCRE_NETWORKING /*!< Enable networking support */ -#define CONFIG_OCRE_CONTAINER_FILESYSTEM -#define CONFIG_OCRE_CONTAINER_WAMR_TERMINATION -#define CONFIG_OCRE_TIMER - -// Base paths for the application -#define OCRE_BASE_PATH "./ocre_data" /*!< Base directory for Ocre resources */ - -#define APP_RESOURCE_PATH OCRE_BASE_PATH "/images" /*!< Path to container images */ -#define PACKAGE_BASE_PATH OCRE_BASE_PATH "/manifests" /*!< Path to package manifests */ -#define CONFIG_PATH OCRE_BASE_PATH "/config" /*!< Path to configuration files */ - -/** - * @brief Path for container filesystem root - */ -#define CONTAINER_FS_PATH OCRE_BASE_PATH "/cfs" - -/** - * @brief Ignore Zephyr's log module registration on POSIX. - */ -#define LOG_MODULE_REGISTER(name, level) -#define LOG_MODULE_DECLARE(name, level) - -/* - * @brief Log level priority definitions (highest to lowest) - */ -#define APP_LOG_LEVEL_ERR 1 -#define APP_LOG_LEVEL_WRN 2 -#define APP_LOG_LEVEL_INF 3 -#define APP_LOG_LEVEL_DBG 4 - -/* - * @brief Determine the current log level based on CONFIG defines - * Priority: CONFIG_LOG_LVL_ERR > CONFIG_LOG_LVL_WRN > CONFIG_LOG_LVL_INF > CONFIG_LOG_LVL_DBG - * If none specified, default to INFO level - */ -#if defined(CONFIG_LOG_LVL_ERR) -#define APP_CURRENT_LOG_LEVEL APP_LOG_LEVEL_ERR -#elif defined(CONFIG_LOG_LVL_WRN) -#define APP_CURRENT_LOG_LEVEL APP_LOG_LEVEL_WRN -#elif defined(CONFIG_LOG_LVL_INF) -#define APP_CURRENT_LOG_LEVEL APP_LOG_LEVEL_INF -#elif defined(CONFIG_LOG_LVL_DBG) -#define APP_CURRENT_LOG_LEVEL APP_LOG_LEVEL_DBG -#else -#define APP_CURRENT_LOG_LEVEL APP_LOG_LEVEL_INF /* Default to INFO level */ -#endif - -/** - * @brief Log an error message (always shown if ERR level or higher). - */ -#if APP_CURRENT_LOG_LEVEL >= APP_LOG_LEVEL_ERR -#define LOG_ERR(fmt, ...) printf("[ERROR] " fmt "\n", ##__VA_ARGS__) -#else -#define LOG_ERR(fmt, ...) \ - do { \ - } while (0) -#endif - -/** - * @brief Log a warning message (shown if WRN level or higher). - */ -#if APP_CURRENT_LOG_LEVEL >= APP_LOG_LEVEL_WRN -#define LOG_WRN(fmt, ...) printf("[WARNING] " fmt "\n", ##__VA_ARGS__) -#else -#define LOG_WRN(fmt, ...) \ - do { \ - } while (0) -#endif - -/** - * @brief Log an informational message (shown if INF level or higher). - */ -#if APP_CURRENT_LOG_LEVEL >= APP_LOG_LEVEL_INF -#define LOG_INF(fmt, ...) printf("[INFO] " fmt "\n", ##__VA_ARGS__) -#else -#define LOG_INF(fmt, ...) \ - do { \ - } while (0) -#endif - -/** - * @brief Log a debug message (shown only if DBG level). - */ -#if APP_CURRENT_LOG_LEVEL >= APP_LOG_LEVEL_DBG -#define LOG_DBG(fmt, ...) printf("[DEBUG] " fmt "\n", ##__VA_ARGS__) -#else -#define LOG_DBG(fmt, ...) \ - do { \ - } while (0) -#endif - -// Constants - -/** - * @brief Sets the value of argc for internal use. - * - * This function stores the argc value passed from main - * so that other functions in the module can access it - * without relying on global variables. - * - * @param argc The argument count from main. - */ -void set_argc(int argc); - -/** - * @brief Maximum length for SHA256 string representations. - */ -#define OCRE_SHA256_LEN 128 - -/** - * @brief Maximum number of containers supported. - */ -#define CONFIG_MAX_CONTAINERS 10 - -/** - * @brief Default stack size for container supervisor threads (in bytes). - */ -#define OCRE_CS_THREAD_STACK_SIZE (1024 * 1024) - -/** - * @brief Application version string. - */ -#ifndef APP_VERSION_STRING -#define APP_VERSION_STRING "0.0.0-dev" -#endif /* APP_VERSION_STRING */ - -/** - * @brief Default heap buffer size for WAMR (in bytes). - */ -#define CONFIG_OCRE_WAMR_HEAP_BUFFER_SIZE 512000 - -/** - * @brief Default heap size for a container (in bytes). - */ -#define CONFIG_OCRE_CONTAINER_DEFAULT_HEAP_SIZE 4096 - -/** - * @brief Default stack size for a container (in bytes). - */ -#define CONFIG_OCRE_CONTAINER_DEFAULT_STACK_SIZE 4096 * 16 - -/** - * @brief Default stack size for container threads (in bytes). - */ -#define CONTAINER_THREAD_STACK_SIZE 1024 * 1024 - -/** - * @brief Structure representing a thread in the Ocre runtime. - */ -struct core_thread { - pthread_t tid; /*!< POSIX thread identifier */ - void *stack; /*!< Pointer to thread stack memory */ - size_t stack_size; /*!< Size of the thread stack */ - uint32_t user_options; /*!< User-defined options for the thread */ -}; - -/** - * @brief Structure representing a mutex in the Ocre runtime. - */ -struct core_mutex { - pthread_mutex_t native_mutex; /*!< POSIX mutex */ -}; - -#define MQ_DEFAULT_PRIO 0 /*!< Default message queue priority */ - -/** - * @brief Structure representing a message queue in the Ocre runtime. - */ -struct core_mq { - mqd_t msgq; /*!< POSIX message queue descriptor */ -}; - -/** - * @brief Timer callback function type. - * - * @param user_data Pointer to user data passed to the callback. - */ -typedef void (*core_timer_callback_t)(void *user_data); - -/** - * @brief Structure representing a timer in the Ocre runtime. - */ -struct core_timer { - timer_t timerid; /*!< POSIX timer identifier */ - core_timer_callback_t cb; /*!< Timer callback function */ - void *user_data; /*!< User data for the callback */ -}; - -/* Generic singly-linked list iteration macros */ -#define CONTAINER_OF(ptr, type, member) ((type *)((char *)(ptr)-offsetof(type, member))) - -#define CORE_SLIST_FOR_EACH_CONTAINER_SAFE(list, var, tmp, member) \ - for (var = (list)->head ? CONTAINER_OF((list)->head, __typeof__(*var), member) : NULL, \ - tmp = var ? (var->member.next ? CONTAINER_OF(var->member.next, __typeof__(*var), member) : NULL) : NULL; \ - var; var = tmp, \ - tmp = tmp ? (tmp->member.next ? CONTAINER_OF(tmp->member.next, __typeof__(*var), member) : NULL) : NULL) - -#define CORE_SLIST_FOR_EACH_CONTAINER(list, var, member) \ - for (var = (list)->head ? CONTAINER_OF((list)->head, __typeof__(*var), member) : NULL; var; \ - var = var->member.next ? CONTAINER_OF(var->member.next, __typeof__(*var), member) : NULL) - -/** - * @brief Structure representing a node in a singly-linked list. - */ -typedef struct core_snode { - struct core_snode *next; /*!< Pointer to the next node in the list */ -} core_snode_t; - -/** - * @brief Structure representing a singly-linked list for POSIX platform. - */ -typedef struct { - core_snode_t *head; /*!< Pointer to the first node in the list */ - core_snode_t *tail; /*!< Pointer to the last node in the list */ -} core_slist_t; - -/** - * @brief Initialize a singly-linked list. - * - * @param list Pointer to the list to initialize. - */ -void core_slist_init(core_slist_t *list); - -/** - * @brief Append a node to the end of a singly-linked list. - * - * @param list Pointer to the list to append to. - * @param node Pointer to the node to append. - */ -void core_slist_append(core_slist_t *list, core_snode_t *node); - -/** - * @brief Remove a node from a singly-linked list. - * - * @param list Pointer to the list to remove from. - * @param prev Pointer to the previous node (or NULL if removing head). - * @param node Pointer to the node to remove. - */ -void core_slist_remove(core_slist_t *list, core_snode_t *prev, core_snode_t *node); - -/** - * @brief Spinlock type for POSIX platform (simulated using mutex). - */ -typedef struct { - pthread_mutex_t mutex; /*!< POSIX mutex for spinlock simulation */ -} core_spinlock_t; - -/** - * @brief Spinlock key type for POSIX platform. - */ -typedef int core_spinlock_key_t; - -/** - * @brief Generic event queue structure for POSIX platform. - * - * A thread-safe circular buffer implementation that can store - * any type of data items with configurable size and capacity. - */ -typedef struct { - void *buffer; /*!< Dynamically allocated buffer for queue items */ - size_t item_size; /*!< Size of each individual item in bytes */ - size_t max_items; /*!< Maximum number of items the queue can hold */ - size_t count; /*!< Current number of items in the queue */ - size_t head; /*!< Index of the next item to be read */ - size_t tail; /*!< Index where the next item will be written */ - pthread_mutex_t mutex; /*!< Mutex for thread-safe access */ - pthread_cond_t cond; /*!< Condition variable for signaling */ -} core_eventq_t; - -#endif /* OCRE_CORE_INTERNAL_H */ diff --git a/src/shared/platform/posix/core_mq.c b/src/shared/platform/posix/core_mq.c deleted file mode 100644 index 85a7d77a..00000000 --- a/src/shared/platform/posix/core_mq.c +++ /dev/null @@ -1,64 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "ocre_core_external.h" - -#include -#include - -int core_mq_init(core_mq_t *mq, const char *name, size_t msg_size, uint32_t max_msgs) -{ - struct mq_attr attr; - - // Configure the message queue attributes - attr.mq_flags = 0; // Blocking mode - attr.mq_maxmsg = max_msgs; // Maximum number of messages in the queue - attr.mq_msgsize = msg_size; // Size of each message - attr.mq_curmsgs = 0; // Number of messages currently in the queue - - // Try to unlink the message queue, but ignore error if it doesn't exist - if (mq_unlink(name) == -1 && errno != ENOENT) { - perror("Main: mq_unlink"); - return -errno; - } - - // Create the message queue - mq->msgq = mq_open(name, O_CREAT | O_RDWR, 0644, &attr); - if (mq->msgq == (mqd_t)-1) { - perror("Failed to create message queue"); - return -errno; - } - return 0; -} - -int core_mq_send(core_mq_t *mq, const void *data, size_t msg_len) -{ - int ret = mq_send(mq->msgq, (const char *)data, msg_len, MQ_DEFAULT_PRIO); - - if (ret == -1) { - perror("Failed to send message"); - } - return ret; -} - -int core_mq_recv(core_mq_t *mq, void *data) -{ - struct mq_attr attr; - unsigned int priority; - - // Get message queue attributes - if (mq_getattr(mq->msgq, &attr) == -1) { - perror("Failed to get message queue attributes"); - return -errno; - } - ssize_t bytes_received = mq_receive(mq->msgq, (char *)data, attr.mq_msgsize, &priority); - if (bytes_received == -1) { - perror("Failed to receive message"); - return -errno; - } - return bytes_received; -} diff --git a/src/shared/platform/posix/core_slist.c b/src/shared/platform/posix/core_slist.c deleted file mode 100644 index 81311908..00000000 --- a/src/shared/platform/posix/core_slist.c +++ /dev/null @@ -1,37 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "ocre_core_external.h" - -void core_slist_init(core_slist_t *list) -{ - list->head = NULL; - list->tail = NULL; -} - -void core_slist_append(core_slist_t *list, core_snode_t *node) -{ - node->next = NULL; - if (list->tail) { - list->tail->next = node; - } else { - list->head = node; - } - list->tail = node; -} - -void core_slist_remove(core_slist_t *list, core_snode_t *prev, core_snode_t *node) -{ - if (prev) { - prev->next = node->next; - } else { - list->head = node->next; - } - if (list->tail == node) { - list->tail = prev; - } -} diff --git a/src/shared/platform/posix/core_thread.c b/src/shared/platform/posix/core_thread.c deleted file mode 100644 index 6052beeb..00000000 --- a/src/shared/platform/posix/core_thread.c +++ /dev/null @@ -1,82 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "ocre_core_external.h" -#include -#include -#include -#include -#include -#include - -static void *thread_entry(void *arg) -{ - struct { - core_thread_func_t func; - void *user_arg; - char name[16]; - } *entry = arg; - - if (entry->name[0]) - pthread_setname_np(pthread_self(), entry->name); - - entry->func(entry->user_arg); - free(entry); - return NULL; -} - -int core_thread_create(core_thread_t *thread, core_thread_func_t func, void *arg, const char *name, size_t stack_size, - int priority) -{ - if (!thread || !func) - return -1; - pthread_attr_t attr; - int ret; - - if (stack_size < PTHREAD_STACK_MIN) { - fprintf(stderr, "STACK_SIZE must be at least PTHREAD_STACK_MIN (%zu)\n", (size_t)PTHREAD_STACK_MIN); - return -1; - } - - thread->stack_size = stack_size; - if (posix_memalign(&thread->stack, sysconf(_SC_PAGESIZE), stack_size) != 0) { - perror("posix_memalign"); - return -1; - } - - pthread_attr_init(&attr); - pthread_attr_setstack(&attr, thread->stack, stack_size); - - // Prepare entry struct for name passing - struct { - core_thread_func_t func; - void *user_arg; - char name[16]; - } *entry = malloc(sizeof(*entry)); - entry->func = func; - entry->user_arg = arg; - strncpy(entry->name, name ? name : "", sizeof(entry->name) - 1); - entry->name[sizeof(entry->name) - 1] = '\0'; - - ret = pthread_create(&thread->tid, &attr, thread_entry, entry); - pthread_attr_destroy(&attr); - if (ret != 0) { - free(thread->stack); - free(entry); - return -1; - } - return 0; -} - -void core_thread_destroy(core_thread_t *thread) -{ - if (!thread) - return; - pthread_cancel(thread->tid); - pthread_join(thread->tid, NULL); - free(thread->stack); -} diff --git a/src/shared/platform/posix/ocre_internal.cmake b/src/shared/platform/posix/ocre_internal.cmake deleted file mode 100644 index f37a8480..00000000 --- a/src/shared/platform/posix/ocre_internal.cmake +++ /dev/null @@ -1,117 +0,0 @@ -include(CheckPIESupported) -option(BUILD_SHARED_LIBS "Build using shared libraries" OFF) -set (CMAKE_VERBOSE_MAKEFILE OFF) - -# Build third-party libs -add_subdirectory(${OCRE_ROOT_DIR}/src/ocre/utils/c-smf) - -# Reset default linker flags -set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") -set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") - -# Set WAMR_BUILD_TARGET, currently values supported: -# "X86_64", "AMD_64", "X86_32", "AARCH64[sub]", "ARM[sub]", "THUMB[sub]", -# "MIPS", "XTENSA", "RISCV64[sub]", "RISCV32[sub]" -if (NOT DEFINED WAMR_BUILD_TARGET) - if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)") - set (WAMR_BUILD_TARGET "AARCH64") - if (NOT DEFINED WAMR_BUILD_SIMD) - set (WAMR_BUILD_SIMD 1) - endif () - elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64") - set (WAMR_BUILD_TARGET "RISCV64") - elseif (CMAKE_SIZEOF_VOID_P EQUAL 8) - set (WAMR_BUILD_TARGET "X86_64") - if (NOT DEFINED WAMR_BUILD_SIMD) - set (WAMR_BUILD_SIMD 1) - endif () - elseif (CMAKE_SIZEOF_VOID_P EQUAL 4) - set (WAMR_BUILD_TARGET "X86_32") - else () - message(SEND_ERROR "Unsupported build target platform!") - endif () -endif () - -# WAMR Options -set (WAMR_BUILD_PLATFORM "linux") -set (WAMR_BUILD_INTERP 1) -set (WAMR_BUILD_FAST_INTERP 0) -set (WAMR_BUILD_AOT 1) -set (WAMR_BUILD_JIT 0) -set (WAMR_BUILD_LIBC_BUILTIN 0) -set (WAMR_BUILD_LIBC_WASI 1) -set (WAMR_BUILD_LIB_PTHREAD 1) -set (WAMR_BUILD_REF_TYPES 1) -set (WASM_ENABLE_LOG 1) -set (WAMR_BUILD_SHARED_HEAP 1) - -if (NOT DEFINED WAMR_BUILD_GLOBAL_HEAP_POOL) - set (WAMR_BUILD_GLOBAL_HEAP_POOL 1) -endif () -if (NOT DEFINED WAMR_BUILD_GLOBAL_HEAP_SIZE) - set (WAMR_BUILD_GLOBAL_HEAP_SIZE 32767) -endif () - -set (WAMR_ROOT_DIR "wasm-micro-runtime") -include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) - -check_pie_supported() - -set(component_sources - # Component support - ${OCRE_ROOT_DIR}/src/ocre/component/component.c - - # Components - ${OCRE_ROOT_DIR}/src/ocre/ocre_container_runtime/ocre_container_runtime.c - ${OCRE_ROOT_DIR}/src/ocre/components/container_supervisor/cs_main.c - ${OCRE_ROOT_DIR}/src/ocre/components/container_supervisor/cs_sm.c - ${OCRE_ROOT_DIR}/src/ocre/components/container_supervisor/cs_sm_impl.c -) - -set(lib_sources - ${OCRE_ROOT_DIR}/src/ocre/sm/sm.c - # Platform - ${OCRE_ROOT_DIR}/src/shared/platform/posix/core_fs.c - ${OCRE_ROOT_DIR}/src/shared/platform/posix/core_thread.c - ${OCRE_ROOT_DIR}/src/shared/platform/posix/core_mutex.c - ${OCRE_ROOT_DIR}/src/shared/platform/posix/core_mq.c - ${OCRE_ROOT_DIR}/src/shared/platform/posix/core_misc.c - ${OCRE_ROOT_DIR}/src/shared/platform/posix/core_memory.c - ${OCRE_ROOT_DIR}/src/shared/platform/posix/core_timer.c - ${OCRE_ROOT_DIR}/src/shared/platform/posix/core_slist.c - ${OCRE_ROOT_DIR}/src/shared/platform/posix/core_eventq.c - # APIs - ${OCRE_ROOT_DIR}/src/ocre/api/ocre_api.c - ${OCRE_ROOT_DIR}/src/ocre/api/ocre_common.c - ${OCRE_ROOT_DIR}/src/ocre/ocre_timers/ocre_timer.c - ${OCRE_ROOT_DIR}/src/ocre/ocre_messaging/ocre_messaging.c - # Utils - ${OCRE_ROOT_DIR}/src/ocre/utils/strlcat.c -) - -include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) - -set(app_sources - ${OCRE_ROOT_DIR}/src/samples-mini/posix/main.c - ${UNCOMMON_SHARED_SOURCE} - ${WAMR_RUNTIME_LIB_SOURCE} - ${lib_sources} - ${component_sources} -) - -add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) -target_link_libraries(vmlib ${LLVM_AVAILABLE_LIBS} ${UV_A_LIBS} -lm -ldl -lpthread) - -add_executable(app ${app_sources}) -target_include_directories(app PUBLIC - ${OCRE_ROOT_DIR}/src - ${OCRE_ROOT_DIR}/src/ocre - ${OCRE_ROOT_DIR}/src/shared/platform -) - -set_target_properties(app PROPERTIES POSITION_INDEPENDENT_CODE ON) -target_link_libraries(app smf vmlib -lm -lpthread) - -add_compile_options(-O0 -Wno-unknown-attributes -Wall -Wextra) - -add_dependencies(app generate_messages) diff --git a/src/shared/platform/zephyr/core_eventq.c b/src/shared/platform/zephyr/core_eventq.c deleted file mode 100644 index fd0a30e0..00000000 --- a/src/shared/platform/zephyr/core_eventq.c +++ /dev/null @@ -1,67 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "ocre_core_external.h" -#include -#include - -int core_eventq_init(core_eventq_t *eventq, size_t item_size, size_t max_items) -{ - eventq->buffer = core_malloc(item_size * max_items); - if (!eventq->buffer) { - return -ENOMEM; - } - eventq->item_size = item_size; - eventq->max_items = max_items; - - k_msgq_init(&eventq->msgq, (char *)eventq->buffer, item_size, max_items); - return 0; -} - -int core_eventq_peek(core_eventq_t *eventq, void *event) -{ - int ret = k_msgq_peek(&eventq->msgq, event); - if (ret == 0) { - return 0; - } else if (ret == -ENOMSG) { - return -ENOMSG; - } else { - return ret; - } -} - -int core_eventq_get(core_eventq_t *eventq, void *event) -{ - int ret = k_msgq_get(&eventq->msgq, event, K_NO_WAIT); - if (ret == 0) { - return 0; - } else if (ret == -ENOMSG) { - return -ENOENT; - } else { - return ret; - } -} - -int core_eventq_put(core_eventq_t *eventq, const void *event) -{ - int ret = k_msgq_put(&eventq->msgq, event, K_NO_WAIT); - if (ret == 0) { - return 0; - } else if (ret == -ENOMSG) { - return -ENOMEM; - } else { - return ret; - } -} - -void core_eventq_destroy(core_eventq_t *eventq) -{ - if (eventq->buffer) { - core_free(eventq->buffer); - eventq->buffer = NULL; - } -} diff --git a/src/shared/platform/zephyr/core_fs.c b/src/shared/platform/zephyr/core_fs.c deleted file mode 100644 index 8e480ccf..00000000 --- a/src/shared/platform/zephyr/core_fs.c +++ /dev/null @@ -1,282 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -LOG_MODULE_REGISTER(filesystem, OCRE_LOG_LEVEL); - -#include -#include -#include -#include -#include -#include -#include -#include -#include "ocre_core_external.h" - -#define MAX_PATH_LEN LFS_NAME_MAX - -#ifdef CONFIG_SHELL -#define print(shell, level, fmt, ...) \ - do { \ - shell_fprintf(shell, level, fmt, ##__VA_ARGS__); \ - } while (false) -#else -#define print(shell, level, fmt, ...) \ - do { \ - printk(fmt, ##__VA_ARGS__); \ - } while (false) -#endif - -int core_construct_filepath(char *path, size_t len, char *name) -{ - return snprintf(path, len, "/lfs/ocre/images/%s.bin", name); -} - -int core_filestat(const char *path, size_t *size) -{ - struct fs_dirent entry; - int ret = fs_stat(path, &entry); - if (ret == 0 && size) { - *size = entry.size; - } - return ret; -} - -int core_fileopen(const char *path, void **handle) -{ - struct fs_file_t *file = core_malloc(sizeof(struct fs_file_t)); - if (!file) - return -ENOMEM; - fs_file_t_init(file); - int ret = fs_open(file, path, FS_O_READ); - if (ret < 0) { - core_free(file); - return ret; - } - *handle = file; - return 0; -} - -int core_fileread(void *handle, void *buffer, size_t size) -{ - struct fs_file_t *file = (struct fs_file_t *)handle; - return fs_read(file, buffer, size); -} - -int core_fileclose(void *handle) -{ - struct fs_file_t *file = (struct fs_file_t *)handle; - int ret = fs_close(file); - core_free(file); - return ret; -} - -static int lsdir(const char *path) -{ - int res; - struct fs_dir_t dirp; - static struct fs_dirent entry; - - fs_dir_t_init(&dirp); - - /* Verify fs_opendir() */ - res = fs_opendir(&dirp, path); - if (res) { - LOG_ERR("Error opening dir %s [%d]\n", path, res); - return res; - } - - for (;;) { - // Verify fs_readdir() - res = fs_readdir(&dirp, &entry); - - // entry.name[0] == 0 means end-of-dir - if (res || entry.name[0] == 0) { - if (res < 0) { - LOG_ERR("Error reading dir [%d]\n", res); - } - break; - } - } - - // Verify fs_closedir() - fs_closedir(&dirp); - - return res; -} - -static int littlefs_flash_erase(unsigned int id) -{ - const struct flash_area *pfa; - int rc; - - rc = flash_area_open(id, &pfa); - if (rc < 0) { - LOG_ERR("FAIL: unable to find flash area %u: %d\n", id, rc); - return rc; - } - - LOG_PRINTK("Area %u at 0x%x on %s for %u bytes\n", id, (unsigned int)pfa->fa_off, pfa->fa_dev->name, - (unsigned int)pfa->fa_size); - - rc = flash_area_erase(pfa, 0, pfa->fa_size); - - if (rc < 0) { - LOG_ERR("Failed to erase flash: %d", rc); - } else { - LOG_INF("Successfully erased flash"); - } - - flash_area_close(pfa); - - return rc; -} - -#define PARTITION_NODE DT_NODELABEL(lfs1) - -#if DT_NODE_EXISTS(PARTITION_NODE) -FS_FSTAB_DECLARE_ENTRY(PARTITION_NODE); -#else /* PARTITION_NODE */ -FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(user_data); -static struct fs_mount_t lfs_storage_mnt = { - .type = FS_LITTLEFS, - .fs_data = &user_data, - .storage_dev = (void *)FIXED_PARTITION_ID(user_data_partition), - .mnt_point = FS_MOUNT_POINT, -}; -#endif /* PARTITION_NODE */ - -struct fs_mount_t *mp = -#if DT_NODE_EXISTS(PARTITION_NODE) - &FS_FSTAB_ENTRY(PARTITION_NODE) -#else - &lfs_storage_mnt -#endif - ; - -static int littlefs_mount(struct fs_mount_t *mp) -{ - int rc = 0; - - /* Do not mount if auto-mount has been enabled */ -#if !DT_NODE_EXISTS(PARTITION_NODE) || !(FSTAB_ENTRY_DT_MOUNT_FLAGS(PARTITION_NODE) & FS_MOUNT_FLAG_AUTOMOUNT) - rc = fs_mount(mp); - if (rc < 0) { - LOG_ERR("FAIL: mount id %" PRIuPTR " at %s: %d\n", (uintptr_t)mp->storage_dev, mp->mnt_point, rc); - return rc; - } - LOG_INF("%s mount: %d\n", mp->mnt_point, rc); -#else - LOG_INF("%s automounted\n", mp->mnt_point); -#endif - - return rc; -} - -#ifdef CONFIG_APP_LITTLEFS_STORAGE_BLK_SDMMC -struct fs_littlefs lfsfs; -static struct fs_mount_t __mp = { - .type = FS_LITTLEFS, - .fs_data = &lfsfs, - .flags = FS_MOUNT_FLAG_USE_DISK_ACCESS, -}; - -struct fs_mount_t *mp = &__mp; - -static int littlefs_mount(struct fs_mount_t *mp) -{ - static const char *disk_mount_pt = "/" CONFIG_SDMMC_VOLUME_NAME ":"; - static const char *disk_pdrv = CONFIG_SDMMC_VOLUME_NAME; - - mp->storage_dev = (void *)disk_pdrv; - mp->mnt_point = disk_mount_pt; - - return fs_mount(mp); -} -#endif /* CONFIG_APP_LITTLEFS_STORAGE_BLK_SDMMC */ - -void ocre_app_storage_init() -{ - struct fs_dirent entry; - struct fs_statvfs sbuf; - int rc; - - rc = littlefs_mount(mp); - if (rc < 0) { - return; - } - - rc = fs_statvfs(mp->mnt_point, &sbuf); - if (rc < 0) { - LOG_ERR("FAILR statvfs: %d", rc); - return; - } - - LOG_DBG("%s: bsize = %lu ; frsize = %lu ;" - " blocks = %lu ; bfree = %lu", - mp->mnt_point, sbuf.f_bsize, sbuf.f_frsize, sbuf.f_blocks, sbuf.f_bfree); - - rc = lsdir(mp->mnt_point); - if (rc < 0) { - LOG_ERR("FAIL: lsdir %s: %d", mp->mnt_point, rc); - } - - // Create the core directories if they don't exist - if (fs_stat(OCRE_BASE_PATH, &entry) == -ENOENT) { - fs_mkdir(OCRE_BASE_PATH); - } - - if (fs_stat(APP_RESOURCE_PATH, &entry) == -ENOENT) { - fs_mkdir(APP_RESOURCE_PATH); - } - - if (fs_stat(PACKAGE_BASE_PATH, &entry) == -ENOENT) { - fs_mkdir(PACKAGE_BASE_PATH); - } - - if (fs_stat(CONFIG_PATH, &entry) == -ENOENT) { - fs_mkdir(CONFIG_PATH); - } - -#ifdef CONFIG_OCRE_CONTAINER_FILESYSTEM - if (fs_stat(CONTAINER_FS_PATH, &entry) == -ENOENT) { - fs_mkdir(CONTAINER_FS_PATH); - } -#endif -} - -static int cmd_flash_format(const struct shell *shell, size_t argc, char *argv[]) -{ - int rc; - fs_unmount(mp); - - // FIXME: if erasing the whole chip, could cause problems - // https://github.com/zephyrproject-rtos/zephyr/issues/56442 - rc = littlefs_flash_erase((uintptr_t)mp->storage_dev); - - if (rc < 0) { - print(shell, SHELL_WARNING, "Format failed: %d\n", rc); - } else { - print(shell, SHELL_NORMAL, "Format succeeded\n"); - } - - if (rc == 0) { - LOG_INF("Mounting..."); - rc = littlefs_mount(mp); - } - - return rc; -} - -SHELL_STATIC_SUBCMD_SET_CREATE(flash_commands, - SHELL_CMD(format, NULL, "Format the flash storage device (all user data will be lost)", - cmd_flash_format), - SHELL_SUBCMD_SET_END); - -SHELL_CMD_REGISTER(user_storage, &flash_commands, "User storage management", NULL); diff --git a/src/shared/platform/zephyr/core_internal.h b/src/shared/platform/zephyr/core_internal.h deleted file mode 100644 index 26e0f971..00000000 --- a/src/shared/platform/zephyr/core_internal.h +++ /dev/null @@ -1,209 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef OCRE_CORE_INTERNAL_H -#define OCRE_CORE_INTERNAL_H - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -// clang-format off - -/** - * @brief Filesystem mount point for Ocre resources. - */ -#define FS_MOUNT_POINT "/lfs" - -/** - * @brief Base directory for Ocre resources. - */ -#define OCRE_BASE_PATH FS_MOUNT_POINT "/ocre" - -/** - * @brief Path to container images. - */ -#define APP_RESOURCE_PATH OCRE_BASE_PATH "/images" - -/** - * @brief Path to package manifests. - */ -#define PACKAGE_BASE_PATH OCRE_BASE_PATH "/manifests" - -/** - * @brief Path to configuration files. - */ -#define CONFIG_PATH OCRE_BASE_PATH "/config/" - -/** - * @brief Path for container filesystem root - */ -#define CONTAINER_FS_PATH OCRE_BASE_PATH "/cfs" - -// Constants - -/** - * @brief Default stack size for container supervisor threads (in bytes). - */ -#define OCRE_CS_THREAD_STACK_SIZE 4096 - -/** - * @brief Default stack size for container threads (in bytes). - */ -#define CONTAINER_THREAD_STACK_SIZE 8192 - -/** - * @brief Default stack size for event threads (in bytes). - */ -#define EVENT_THREAD_STACK_SIZE 2048 - -/** - * @brief Maximum length for SHA256 string representations. - */ -#define OCRE_SHA256_LEN 128 - -/** - * @brief Structure representing a thread in the Ocre runtime (Zephyr). - */ -struct core_thread { - struct k_thread thread; /*!< Zephyr thread structure */ - k_tid_t tid; /*!< Zephyr thread identifier */ - k_thread_stack_t *stack; /*!< Pointer to thread stack memory */ - size_t stack_size; /*!< Size of the thread stack */ - uint32_t user_options; /*!< User-defined options for the thread */ -}; - -/** - * @brief Structure representing a mutex in the Ocre runtime (Zephyr). - */ -struct core_mutex { - struct k_mutex native_mutex; /*!< Zephyr native mutex */ -}; - -/** - * @brief Default timeout value for message queue operations. - */ -#define MQ_DEFAULT_TIMEOUT K_NO_WAIT - -/** - * @brief Structure representing a message queue in the Ocre runtime (Zephyr). - */ -struct core_mq { - struct k_msgq msgq; /*!< Zephyr message queue */ - char __aligned(4) *msgq_buffer; /*!< Message queue buffer (aligned) */ -}; - -/** - * @brief Timer callback function type. - * - * @param user_data Pointer to user data passed to the callback. - */ -typedef void (*core_timer_callback_t)(void *user_data); - -/** - * @brief Structure representing a timer in the Ocre runtime (Zephyr). - */ -struct core_timer { - struct k_timer timer; /*!< Zephyr timer structure */ - core_timer_callback_t cb; /*!< Timer callback function */ - void *user_data; /*!< User data for the callback */ -}; - -/* Generic singly-linked list iteration macros */ -#define CORE_SLIST_FOR_EACH_CONTAINER_SAFE SYS_SLIST_FOR_EACH_CONTAINER_SAFE -#define CORE_SLIST_FOR_EACH_CONTAINER SYS_SLIST_FOR_EACH_CONTAINER - -/** - * @brief Structure representing a node in a singly-linked list. - */ -#define core_snode_t sys_snode_t - -/** - * @brief Structure representing a singly-linked list for POSIX platform. - */ -#define core_slist_t sys_slist_t - -/** - * @brief Initialize a singly-linked list. - * - * @param list Pointer to the list to initialize. - */ -#define core_slist_init sys_slist_init - -/** - * @brief Append a node to the end of a singly-linked list. - * - * @param list Pointer to the list to append to. - * @param node Pointer to the node to append. - */ -#define core_slist_append sys_slist_append - -/** - * @brief Remove a node from a singly-linked list. - * - * @param list Pointer to the list to remove from. - * @param prev Pointer to the previous node (or NULL if removing head). - * @param node Pointer to the node to remove. - */ -#define core_slist_remove sys_slist_remove - -/* Zephyr-specific macros */ - -/** - * @brief Get system uptime in milliseconds. - * - * @return System uptime in milliseconds. - */ -#define core_uptime_get k_uptime_get_32 - -/** - * @brief Lock a spinlock and return the interrupt key. - * - * @param lock Pointer to the spinlock structure. - * @return Interrupt key to be used with unlock. - */ -#define core_spinlock_lock k_spin_lock - -/** - * @brief Unlock a spinlock using the interrupt key. - * - * @param lock Pointer to the spinlock structure. - * @param key Interrupt key returned from lock operation. - */ -#define core_spinlock_unlock k_spin_unlock - -/** - * @brief Spinlock type for Zephyr platform. - */ -typedef struct k_spinlock core_spinlock_t; - -/** - * @brief Spinlock key type for Zephyr platform. - */ -typedef k_spinlock_key_t core_spinlock_key_t; - -/** - * @brief Generic event queue structure for Zephyr platform. - * - * A thread-safe message queue implementation using Zephyr's k_msgq - * that can store any type of data items with configurable size and capacity. - */ -typedef struct { - void *buffer; /*!< Dynamically allocated buffer for queue items */ - size_t item_size; /*!< Size of each individual item in bytes */ - size_t max_items; /*!< Maximum number of items the queue can hold */ - struct k_msgq msgq; /*!< Zephyr message queue */ -} core_eventq_t; - -#endif diff --git a/src/shared/platform/zephyr/core_memory.c b/src/shared/platform/zephyr/core_memory.c deleted file mode 100644 index f862143c..00000000 --- a/src/shared/platform/zephyr/core_memory.c +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "ocre_core_external.h" - -#include - -void *core_malloc(size_t size) -{ - return k_malloc(size); -} - -void core_free(void *ptr) -{ - k_free(ptr); -} diff --git a/src/shared/platform/zephyr/core_misc.c b/src/shared/platform/zephyr/core_misc.c deleted file mode 100644 index 7bef689f..00000000 --- a/src/shared/platform/zephyr/core_misc.c +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "ocre_core_external.h" -#include - -void core_sleep_ms(int milliseconds) -{ - k_msleep(milliseconds); -} - -void core_yield(void) -{ - k_yield(); -} diff --git a/src/shared/platform/zephyr/core_mq.c b/src/shared/platform/zephyr/core_mq.c deleted file mode 100644 index 2e1589df..00000000 --- a/src/shared/platform/zephyr/core_mq.c +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "ocre_core_external.h" -#include -LOG_MODULE_DECLARE(platform_mq_component, OCRE_LOG_LEVEL); - -int core_mq_init(core_mq_t *mq, const char *name, size_t msg_size, uint32_t max_msgs) -{ - mq->msgq_buffer = k_malloc(msg_size * max_msgs); - k_msgq_init(&mq->msgq, mq->msgq_buffer, msg_size, max_msgs); - return 0; -} - -int core_mq_send(core_mq_t *mq, const void *data, size_t msg_len) -{ - int ret = k_msgq_put(&mq->msgq, data, MQ_DEFAULT_TIMEOUT); - - if (ret != 0) { - LOG_HEXDUMP_DBG(data, msg_len, "message"); - } - return ret; -} - -int core_mq_recv(core_mq_t *mq, void *data) -{ - return k_msgq_get(&mq->msgq, data, K_FOREVER); -} diff --git a/src/shared/platform/zephyr/core_mutex.c b/src/shared/platform/zephyr/core_mutex.c deleted file mode 100644 index 6657891b..00000000 --- a/src/shared/platform/zephyr/core_mutex.c +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "ocre_core_external.h" -#include - -int core_mutex_init(core_mutex_t *mutex) -{ - if (!mutex) - return -1; - k_mutex_init(&mutex->native_mutex); - return 0; -} - -int core_mutex_destroy(core_mutex_t *mutex) -{ - return 0; -} - -int core_mutex_lock(core_mutex_t *mutex) -{ - return k_mutex_lock(&mutex->native_mutex, K_FOREVER); -} - -int core_mutex_unlock(core_mutex_t *mutex) -{ - return k_mutex_unlock(&mutex->native_mutex); -} diff --git a/src/shared/platform/zephyr/core_thread.c b/src/shared/platform/zephyr/core_thread.c deleted file mode 100644 index 1c81191a..00000000 --- a/src/shared/platform/zephyr/core_thread.c +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "ocre_core_external.h" -#include -#include - -static void thread_entry(void *arg1, void *arg2, void *arg3) -{ - core_thread_func_t func = (core_thread_func_t)arg2; - void *user_arg = arg3; - func(user_arg); -} - -int core_thread_create(core_thread_t *thread, core_thread_func_t func, void *arg, const char *name, size_t stack_size, - int priority) -{ - if (!thread || !func) - return -1; - thread->stack = k_thread_stack_alloc(stack_size, 0); - if (!thread->stack) - return -1; - thread->tid = k_thread_create(&thread->thread, thread->stack, stack_size, thread_entry, NULL, (void *)func, arg, - priority, 0, K_NO_WAIT); - if (name) - k_thread_name_set(thread->tid, name); - return thread->tid ? 0 : -1; -} - -void core_thread_destroy(core_thread_t *thread) -{ - if (!thread) - return; - k_thread_abort(thread->tid); - k_thread_stack_free(thread->stack); -} diff --git a/src/shared/platform/zephyr/core_timer.c b/src/shared/platform/zephyr/core_timer.c deleted file mode 100644 index 32d0520e..00000000 --- a/src/shared/platform/zephyr/core_timer.c +++ /dev/null @@ -1,43 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "ocre_core_external.h" -#include - -static void zephyr_timer_cb(struct k_timer *ktimer) -{ - core_timer_t *otimer = CONTAINER_OF(ktimer, core_timer_t, timer); - if (otimer->cb) { - otimer->cb(otimer->user_data); - } -} - -int core_timer_init(core_timer_t *timer, core_timer_callback_t cb, void *user_data) -{ - if (!timer) - return -1; - timer->cb = cb; - timer->user_data = user_data; - k_timer_init(&timer->timer, zephyr_timer_cb, NULL); - return 0; -} - -int core_timer_start(core_timer_t *timer, int timeout_ms, int period_ms) -{ - if (!timer) - return -1; - k_timer_start(&timer->timer, K_MSEC(timeout_ms), K_MSEC(period_ms)); - return 0; -} - -int core_timer_stop(core_timer_t *timer) -{ - if (!timer) - return -1; - k_timer_stop(&timer->timer); - return 0; -} diff --git a/src/shared/platform/zephyr/ocre_internal.cmake b/src/shared/platform/zephyr/ocre_internal.cmake deleted file mode 100644 index cdf9c368..00000000 --- a/src/shared/platform/zephyr/ocre_internal.cmake +++ /dev/null @@ -1,134 +0,0 @@ -# Zephyr-specific version defines -message("VERSION NUMBER: ${VERSION_NUMBER}") -zephyr_compile_options(-DVERSION_INFO="${VERSION_NUMBER}") -message("BUILD DATE: ${BUILD_DATE}") -zephyr_compile_options(-DVERSION_BUILD_DATE="${BUILD_DATE}") -message("BUILD MACHINE: ${BUILD_MACHINE}") -zephyr_compile_options(-DVERSION_BUILD_MACHINE="${BUILD_MACHINE}") -message("BUILD_INFO: ${BUILD_INFO}") -zephyr_compile_options(-DVERSION_BUILD_INFO="${BUILD_INFO}") - -if (CONFIG_CPU_CORTEX_M33 OR CONFIG_CPU_CORTEX_M35P OR CONFIG_CPU_CORTEX_M55 OR CONFIG_CPU_CORTEX_M85) - # ARMv8-M Mainline (Cortex-M33, M35P, M55, M85) - set (TARGET_ISA THUMBV8M) -elseif (CONFIG_CPU_CORTEX_M23) - # ARMv8-M Baseline (Cortex-M23) - set (TARGET_ISA THUMBV8M) -elseif (CONFIG_CPU_CORTEX_M7 OR CONFIG_CPU_CORTEX_M4) - # ARMv7E-M (Cortex-M4, M7) - set (TARGET_ISA THUMBV7EM) -elseif (DEFINED CONFIG_ISA_THUMB2) - # Generic Thumb-2 - set (TARGET_ISA THUMB) -elseif (DEFINED CONFIG_ISA_ARM) - set (TARGET_ISA ARM) -elseif (DEFINED CONFIG_X86) - set (TARGET_ISA X86_32) -elseif (DEFINED CONFIG_XTENSA) - set (TARGET_ISA XTENSA) -elseif (DEFINED CONFIG_RISCV) - set (TARGET_ISA RISCV32) -elseif (DEFINED CONFIG_ARCH_POSIX) - set (TARGET_ISA X86_32) -else () - message (FATAL_ERROR "Unsupported ISA: ${CONFIG_ARCH}") -endif () -message(STATUS "TARGET ISA: ${TARGET_ISA}") - -add_compile_options(-O0 -Wno-unknown-attributes) - -# WAMR Options -set(WAMR_BUILD_PLATFORM "zephyr") -set(WAMR_BUILD_TARGET ${TARGET_ISA}) -set(WAMR_BUILD_INTERP 1) -set(WAMR_BUILD_FAST_INTERP 0) -set(WAMR_BUILD_AOT 1) -set(WAMR_BUILD_JIT 0) -set(WAMR_BUILD_LIBC_BUILTIN 0) -set(WAMR_BUILD_LIBC_WASI 1) -set(WAMR_BUILD_LIB_PTHREAD 1) -set(WAMR_BUILD_REF_TYPES 1) -set(WASM_ENABLE_LOG 1) -set (WAMR_BUILD_SHARED_HEAP 1) - -if(NOT DEFINED WAMR_BUILD_GLOBAL_HEAP_POOL) - set(WAMR_BUILD_GLOBAL_HEAP_POOL 1) -endif() -if(NOT DEFINED WAMR_BUILD_GLOBAL_HEAP_SIZE) - set(WAMR_BUILD_GLOBAL_HEAP_SIZE 32767) -endif() - -set(WAMR_ROOT_DIR ${OCRE_ROOT_DIR}/wasm-micro-runtime) -include(${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) - -zephyr_include_directories( - ${OCRE_ROOT_DIR}/src/ - ${OCRE_ROOT_DIR}/src/ocre - ${OCRE_ROOT_DIR}/src/shared/platform -) - -set(component_sources - # Component support - ${OCRE_ROOT_DIR}/src/ocre/component/component.c - - # Components - ${OCRE_ROOT_DIR}/src/ocre/ocre_container_runtime/ocre_container_runtime.c - ${OCRE_ROOT_DIR}/src/ocre/components/container_supervisor/cs_main.c - ${OCRE_ROOT_DIR}/src/ocre/components/container_supervisor/cs_sm.c - ${OCRE_ROOT_DIR}/src/ocre/components/container_supervisor/cs_sm_impl.c -) - -# Collect all sources in one list -set(lib_sources - # Libraries - ${OCRE_ROOT_DIR}/src/ocre/sm/sm.c - ${OCRE_ROOT_DIR}/src/ocre/shell/ocre_shell.c - # Platform - ${OCRE_ROOT_DIR}/src/shared/platform/zephyr/core_fs.c - ${OCRE_ROOT_DIR}/src/shared/platform/zephyr/core_thread.c - ${OCRE_ROOT_DIR}/src/shared/platform/zephyr/core_mutex.c - ${OCRE_ROOT_DIR}/src/shared/platform/zephyr/core_mq.c - ${OCRE_ROOT_DIR}/src/shared/platform/zephyr/core_eventq.c - ${OCRE_ROOT_DIR}/src/shared/platform/zephyr/core_misc.c - ${OCRE_ROOT_DIR}/src/shared/platform/zephyr/core_memory.c - ${OCRE_ROOT_DIR}/src/shared/platform/zephyr/core_timer.c - - # Ocre APIs - ${OCRE_ROOT_DIR}/src/ocre/api/ocre_api.c - ${OCRE_ROOT_DIR}/src/ocre/api/ocre_common.c -) - -# Conditionally add sources -if(CONFIG_OCRE_TIMER) - list(APPEND lib_sources ${OCRE_ROOT_DIR}/src/ocre/ocre_timers/ocre_timer.c) -endif() - -if(CONFIG_OCRE_SENSORS) - list(APPEND lib_sources ${OCRE_ROOT_DIR}/src/ocre/ocre_sensors/ocre_sensors.c) -endif() - -if(CONFIG_RNG_SENSOR) - list(APPEND lib_sources ${OCRE_ROOT_DIR}/src/ocre/ocre_sensors/rng_sensor.c) -endif() - -if(DEFINED CONFIG_OCRE_GPIO) - list(APPEND lib_sources ${OCRE_ROOT_DIR}/src/ocre/ocre_gpio/ocre_gpio.c) -endif() - -if(CONFIG_OCRE_CONTAINER_MESSAGING) - list(APPEND lib_sources ${OCRE_ROOT_DIR}/src/ocre/ocre_messaging/ocre_messaging.c) -endif() - -# Add all sources to the app target at once -target_sources(app PRIVATE - ${WAMR_RUNTIME_LIB_SOURCE} - ${lib_sources} - ${component_sources} - ${OCRE_ROOT_DIR}/src/samples-mini/zephyr/main.c -) - -add_dependencies(app generate_messages) - -if(NOT "${OCRE_INPUT_FILE}" STREQUAL "") - add_dependencies(app generate_ocre_file) -endif() diff --git a/src/shell/CMakeLists.txt b/src/shell/CMakeLists.txt new file mode 100644 index 00000000..a073075c --- /dev/null +++ b/src/shell/CMakeLists.txt @@ -0,0 +1,34 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +add_library(OcreShell) + +target_sources(OcreShell + PRIVATE + shell.c + image.c + image/ls.c + image/pull.c + image/sha256_file.c + image/rm.c + container.c + container/create.c + container/kill.c + container/pause.c + container/ps.c + container/rm.c + container/start.c + container/stop.c + container/unpause.c + container/wait.c + sha256/sha256.c +) + +target_include_directories(OcreShell + PUBLIC + include +) diff --git a/src/shell/command.h b/src/shell/command.h new file mode 100644 index 00000000..8af328a2 --- /dev/null +++ b/src/shell/command.h @@ -0,0 +1,13 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +struct ocre_command { + char *name; + int (*func)(struct ocre_context *ctx, const char *argv0, int argc, char **argv); +}; diff --git a/src/shell/container.c b/src/shell/container.c new file mode 100644 index 00000000..ff51123f --- /dev/null +++ b/src/shell/container.c @@ -0,0 +1,72 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +#include "command.h" + +#include "container/create.h" +#include "container/kill.h" +#include "container/pause.h" +#include "container/ps.h" +#include "container/rm.h" +#include "container/start.h" +#include "container/stop.h" +#include "container/unpause.h" +#include "container/wait.h" + +static int print_usage(struct ocre_context *ctx, const char *argv0, int argc, char **argv) +{ + fprintf(stderr, "Usage: %s container \n", argv0); + + fprintf(stderr, "\nCommands:\n"); + fprintf(stderr, " run Created and starts a new container\n"); + fprintf(stderr, " create Create a new container\n"); + fprintf(stderr, " start Start a container\n"); + // fprintf(stderr, " stop Stop a running or paused container\n"); + fprintf(stderr, " kill Kill a running or paused container\n"); + // fprintf(stderr, " pause Pause a running container\n"); + // fprintf(stderr, " unpause Resume a paused container\n"); + fprintf(stderr, " wait Wait for a container to exit\n"); + fprintf(stderr, " ps List containers\n"); + fprintf(stderr, " rm Remove a stopped container\n"); + return 0; +} + +static const struct ocre_command commands[] = { + {"help", print_usage}, // + {"run", cmd_container_create_run}, // + {"create", cmd_container_create_run}, // + {"start", cmd_container_start}, // + {"stop", cmd_container_stop}, // + {"kill", cmd_container_kill}, // + {"pause", cmd_container_pause}, // + {"unpause", cmd_container_unpause}, // + {"wait", cmd_container_wait}, // + {"ps", cmd_container_ps}, // + {"rm", cmd_container_rm}, // +}; + +int cmd_container(struct ocre_context *ctx, const char *argv0, int argc, char **argv) +{ + if (argc < 2) { + return print_usage(ctx, argv0, argc, argv); + } + + for (size_t i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) { + if (!strcmp(argv[1], commands[i].name)) { + return commands[i].func(ctx, argv0, argc - 1, &argv[1]); + } + } + + fprintf(stderr, "Invalid command: '%s container %s'\n\n", argv0, argv[1]); + + return print_usage(ctx, argv0, argc, argv); +} diff --git a/src/shell/container.h b/src/shell/container.h new file mode 100644 index 00000000..f69cfe09 --- /dev/null +++ b/src/shell/container.h @@ -0,0 +1,10 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +int cmd_container(struct ocre_context *ctx, const char *argv0, int argc, char **argv); diff --git a/src/shell/container/create.c b/src/shell/container/create.c new file mode 100644 index 00000000..19298c16 --- /dev/null +++ b/src/shell/container/create.c @@ -0,0 +1,244 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#include + +#include "../command.h" + +static int usage(const char *argv0, const char *cmd) +{ + fprintf(stderr, "Usage: %s container %s [options] IMAGE [ARG...]\n", argv0, cmd); + if (!strcmp(cmd, "create")) { + fprintf(stderr, "\nCreates a container in the Ocre context.\n"); + } else if (!strcmp(cmd, "run")) { + fprintf(stderr, "\nCreates and starts a container in the Ocre context.\n"); + } + fprintf(stderr, "\nOptions:\n"); + fprintf(stderr, " -d Creates a detached container\n"); + fprintf(stderr, " -n CONTAINER_ID Specifies a container ID\n"); + fprintf(stderr, " -r RUNTIME Specifies the runtime to use\n"); + fprintf(stderr, " -v VOLUME:MOUNTPOINT Adds a volume to be mounted into the container\n"); + fprintf(stderr, " -v /ABSPATH:MOUNTPOINT Adds a directory to be mounted into the container\n"); + fprintf(stderr, " -k CAPABILITY Adds a capability to the container\n"); + fprintf(stderr, " -e VAR=VALUE Sets an environment variable in the container\n"); + fprintf(stderr, "\nOptions '-v' and '-e' and '-k' can be supplied multiple times.\n"); + + return -1; +} + +int cmd_container_create_run(struct ocre_context *ctx, const char *argv0, int argc, char **argv) +{ + int ret = -1; + + if (argc < 2) { + fprintf(stderr, "'%s container %s' requires arguments\n\n", argv0, argv[0]); + return usage(argv0, argv[0]); + } + + bool detached = false; + const char *runtime = NULL; + const char *container_id = NULL; + const char **capabilities = NULL; + const char **environment = NULL; + const char **mounts = NULL; + + size_t capabilities_count = 0; + size_t environment_count = 0; + size_t mounts_count = 0; + + int opt; + while ((opt = getopt(argc, argv, "+de:k:n:r:v:")) != -1) { + switch (opt) { + case 'd': { + if (detached) { + fprintf(stderr, "Detached mode can be set only once\n\n"); + usage(argv0, argv[0]); + goto cleanup; + } + + detached = true; + continue; + } + case 'n': { + if (container_id) { + fprintf(stderr, "Container ID can be set only once\n\n"); + usage(argv0, argv[0]); + goto cleanup; + } + + /* Check if the provided container ID is valid */ + + if (optarg && !ocre_is_valid_id(optarg)) { + fprintf(stderr, + "Invalid characters in container ID '%s'. Valid are [a-z0-9_-.] " + "(lowercase alphanumeric) and cannot start with '.'\n", + optarg); + goto cleanup; + } + + container_id = optarg; + continue; + } + case 'r': { + if (runtime) { + fprintf(stderr, "Runtime can be set only once\n\n"); + usage(argv0, argv[0]); + goto cleanup; + } + + runtime = optarg; + continue; + } + case 'v': { + + /* Check mounts parameters of format : + * Destination should be absolute path + * We do not like anything to be mounted at '/' + * We handle '/' with the 'filesystem' capability. + */ + + if (optarg[0] != '/') { + fprintf(stderr, "Invalid mount format: '%s': source must be absolute path\n", + optarg); + goto cleanup; + } + + char *dst = strchr(optarg, ':'); + if (!dst) { + fprintf(stderr, "Invalid mount format: '%s': must be :\n", + optarg); + goto cleanup; + } + + dst++; + + if (dst[0] != '/') { + fprintf(stderr, + "Invalid mount format: '%s': destination must be absolute path\n", + optarg); + goto cleanup; + } + + if (dst[1] == '\0') { + fprintf(stderr, "Invalid mount format: '%s': destination must not be '/'\n", + optarg); + goto cleanup; + } + + const char **new_mounts = realloc(mounts, sizeof(char *) * (mounts_count + 1)); + if (!new_mounts) { + goto cleanup; + } + + mounts = new_mounts; + + mounts[mounts_count++] = optarg; + continue; + } + case 'k': { + const char **new_capabilities = + realloc(capabilities, sizeof(char *) * (capabilities_count + 1)); + if (!new_capabilities) { + goto cleanup; + } + + capabilities = new_capabilities; + + capabilities[capabilities_count++] = optarg; + continue; + } + case 'e': { + const char **new_environment = + realloc(environment, sizeof(char *) * (environment_count + 1)); + if (!new_environment) { + goto cleanup; + } + + environment = new_environment; + + environment[environment_count++] = optarg; + continue; + } + case '?': { + fprintf(stderr, "Invalid option '-%c'\n", optopt); + goto cleanup; + } + default: { + fprintf(stderr, "Invalid option '-%c'\n", optopt); + goto cleanup; + } + } + } + + environment = realloc(environment, sizeof(char *) * (environment_count + 1)); + environment[environment_count++] = NULL; + + capabilities = realloc(capabilities, sizeof(char *) * (capabilities_count + 1)); + capabilities[capabilities_count++] = NULL; + + mounts = realloc(mounts, sizeof(char *) * (mounts_count + 1)); + mounts[mounts_count++] = NULL; + + if (optind >= argc) { + fprintf(stderr, "'%s container %s' requires at least one non option argument\n\n", argv0, argv[0]); + usage(argv0, argv[0]); + goto cleanup; + } + + /* Check if the provided image ID is valid */ + + if (argv[optind] && !ocre_is_valid_id(argv[optind])) { + fprintf(stderr, + "Invalid characters in image ID '%s'. Valid are [a-z0-9_-.] (lowercase alphanumeric) and " + "cannot start with '.'", + argv[optind]); + goto cleanup; + } + + const struct ocre_container_args arguments = { + .argv = (const char **)&argv[optind + 1], + .capabilities = capabilities, + .envp = environment, + .mounts = mounts, + }; + + struct ocre_container *container = + ocre_context_create_container(ctx, argv[optind], runtime, container_id, detached, &arguments); + + if (!container) { + fprintf(stderr, "Failed to create container\n"); + goto cleanup; + } + + if (!strcmp(argv[0], "run")) { + if (ocre_container_start(container)) { + fprintf(stderr, "Failed to start container\n"); + goto cleanup; + } + } + + if (detached) { + const char *cid = ocre_container_get_id(container); + + fprintf(stdout, "%s\n", cid); + } + + ret = 0; + +cleanup: + free(mounts); + free(capabilities); + free(environment); + + return ret; +} diff --git a/src/shell/container/create.h b/src/shell/container/create.h new file mode 100644 index 00000000..57ae0ebc --- /dev/null +++ b/src/shell/container/create.h @@ -0,0 +1,10 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +int cmd_container_create_run(struct ocre_context *ctx, const char *argv0, int argc, char **argv); diff --git a/src/shell/container/kill.c b/src/shell/container/kill.c new file mode 100644 index 00000000..51ed1ac1 --- /dev/null +++ b/src/shell/container/kill.c @@ -0,0 +1,47 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +#include "../command.h" + +static int usage(const char *argv0) +{ + fprintf(stderr, "Usage: %s container kill CONTAINER\n", argv0); + fprintf(stderr, "\nKills a container in the Ocre context.\n"); + return -1; +} + +int cmd_container_kill(struct ocre_context *ctx, const char *argv0, int argc, char **argv) +{ + if (argc == 2) { + struct ocre_container *container = ocre_context_get_container_by_id(ctx, argv[1]); + if (!container) { + fprintf(stderr, "Failed to get container '%s'\n", argv[1]); + return -1; + } + + if (ocre_container_get_status(container) != OCRE_CONTAINER_STATUS_RUNNING) { + fprintf(stderr, "Container '%s' is not running\n", argv[1]); + return -1; + } + + int rc = ocre_container_kill(container); + + fprintf(stdout, "%s\n", argv[1]); + + return rc; + } else { + fprintf(stderr, "'%s container kill' requires exactly one argument\n\n", argv0); + return usage(argv0); + } + + return 0; +} diff --git a/src/shell/container/kill.h b/src/shell/container/kill.h new file mode 100644 index 00000000..c8a3a3c2 --- /dev/null +++ b/src/shell/container/kill.h @@ -0,0 +1,10 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +int cmd_container_kill(struct ocre_context *ctx, const char *argv0, int argc, char **argv); diff --git a/src/shell/container/pause.c b/src/shell/container/pause.c new file mode 100644 index 00000000..ab87c8ac --- /dev/null +++ b/src/shell/container/pause.c @@ -0,0 +1,47 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +#include "../command.h" + +static int usage(const char *argv0) +{ + fprintf(stderr, "Usage: %s container pause CONTAINER\n", argv0); + fprintf(stderr, "\nPauses a container in the Ocre context.\n"); + return -1; +} + +int cmd_container_pause(struct ocre_context *ctx, const char *argv0, int argc, char **argv) +{ + if (argc == 2) { + struct ocre_container *container = ocre_context_get_container_by_id(ctx, argv[1]); + if (!container) { + fprintf(stderr, "Failed to get container '%s'\n", argv[1]); + return -1; + } + + if (ocre_container_get_status(container) != OCRE_CONTAINER_STATUS_RUNNING) { + fprintf(stderr, "Container '%s' is not running\n", argv[1]); + return -1; + } + + int rc = ocre_container_pause(container); + + fprintf(stdout, "%s\n", argv[1]); + + return rc; + } else { + fprintf(stderr, "'%s container pause' requires exactly one argument\n\n", argv0); + return usage(argv0); + } + + return 0; +} diff --git a/src/shell/container/pause.h b/src/shell/container/pause.h new file mode 100644 index 00000000..808c3649 --- /dev/null +++ b/src/shell/container/pause.h @@ -0,0 +1,10 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +int cmd_container_pause(struct ocre_context *ctx, const char *argv0, int argc, char **argv); diff --git a/src/shell/container/ps.c b/src/shell/container/ps.c new file mode 100644 index 00000000..afdd21b8 --- /dev/null +++ b/src/shell/container/ps.c @@ -0,0 +1,111 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include + +#include "../command.h" + +static const char *container_statuses[] = {"UNKNOWN", // + "CREATED", // + "RUNNING", // + "PAUSED ", // + "EXITED ", // + "STOPPED", // + "ERROR "}; + +static int usage(const char *argv0) +{ + fprintf(stderr, "Usage: %s container ps [CONTAINER]\n", argv0); + fprintf(stderr, "\nList containers in Ocre context.\n"); + return -1; +} + +static void header(void) +{ + printf("ID\tSTATUS\tIMAGE\n"); +} + +static int list_container(struct ocre_container *container) +{ + const char *id = ocre_container_get_id(container); + const char *image = ocre_container_get_image(container); + ocre_container_status_t status = ocre_container_get_status(container); + + if (!id || !image || status == OCRE_CONTAINER_STATUS_UNKNOWN) { + return -1; + } + + printf("%s\t%s\t%s\n", id, container_statuses[status], image); + + return 0; +} + +static int list_containers(struct ocre_context *ctx) +{ + int ret = -1; + int num_containers = ocre_context_get_container_count(ctx); + if (num_containers < 0) { + fprintf(stderr, "Failed to get number of containers\n"); + return -1; + } + + if (num_containers == 0) { + return 0; + } + + struct ocre_container **containers = malloc(sizeof(struct ocre_container *) * num_containers); + if (!containers) { + fprintf(stderr, "Failed to allocate memory for containers\n"); + return -1; + } + + num_containers = ocre_context_get_containers(ctx, containers, num_containers); + if (num_containers < 0) { + fprintf(stderr, "Failed to list containers\n"); + goto finish; + } + + for (int i = 0; i < num_containers; i++) { + if (list_container(containers[i])) { + fprintf(stderr, "Failed to list container %d\n", i); + goto finish; + } + } + + ret = 0; + +finish: + free(containers); + return ret; +} + +int cmd_container_ps(struct ocre_context *ctx, const char *argv0, int argc, char **argv) +{ + switch (argc) { + case 1: { + header(); + return list_containers(ctx); + } + case 2: { + struct ocre_container *container = ocre_context_get_container_by_id(ctx, argv[1]); + if (!container) { + fprintf(stderr, "Failed to get container '%s'\n", argv[1]); + return -1; + } + + header(); + return list_container(container); + } + default: + fprintf(stderr, "'%s container ps' requires at most one argument\n\n", argv0); + return usage(argv0); + } +} diff --git a/src/shell/container/ps.h b/src/shell/container/ps.h new file mode 100644 index 00000000..a63f5fb8 --- /dev/null +++ b/src/shell/container/ps.h @@ -0,0 +1,10 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +int cmd_container_ps(struct ocre_context *ctx, const char *argv0, int argc, char **argv); diff --git a/src/shell/container/rm.c b/src/shell/container/rm.c new file mode 100644 index 00000000..1a4bf91e --- /dev/null +++ b/src/shell/container/rm.c @@ -0,0 +1,48 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "../command.h" +#include "ocre/ocre.h" + +static int usage(const char *argv0) +{ + fprintf(stderr, "Usage: %s container rm CONTAINER\n", argv0); + fprintf(stderr, "\nRemoves a stopped container from the Ocre context.\n"); + return -1; +} + +int cmd_container_rm(struct ocre_context *ctx, const char *argv0, int argc, char **argv) +{ + if (argc == 2) { + struct ocre_container *container = ocre_context_get_container_by_id(ctx, argv[1]); + if (!container) { + fprintf(stderr, "Failed to get container '%s'\n", argv[1]); + return -1; + } + + ocre_container_status_t status = ocre_container_get_status(container); + if (status != OCRE_CONTAINER_STATUS_STOPPED && status != OCRE_CONTAINER_STATUS_CREATED && + status != OCRE_CONTAINER_STATUS_ERROR) { + fprintf(stderr, "Container '%s' is in use\n", argv[1]); + return -1; + } + + int rc = ocre_context_remove_container(ctx, container); + + fprintf(stdout, "%s\n", argv[1]); + + return rc; + } else { + fprintf(stderr, "'%s container rm' requires exactly one argument\n\n", argv0); + return usage(argv0); + } + + return 0; +} diff --git a/src/shell/container/rm.h b/src/shell/container/rm.h new file mode 100644 index 00000000..df057727 --- /dev/null +++ b/src/shell/container/rm.h @@ -0,0 +1,10 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +int cmd_container_rm(struct ocre_context *ctx, const char *argv0, int argc, char **argv); diff --git a/src/shell/container/start.c b/src/shell/container/start.c new file mode 100644 index 00000000..02706391 --- /dev/null +++ b/src/shell/container/start.c @@ -0,0 +1,48 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +#include "../command.h" + +static int usage(const char *argv0) +{ + fprintf(stderr, "Usage: %s container start CONTAINER\n", argv0); + fprintf(stderr, "\nStarts a container in the Ocre context.\n"); + return -1; +} + +int cmd_container_start(struct ocre_context *ctx, const char *argv0, int argc, char **argv) +{ + if (argc == 2) { + struct ocre_container *container = ocre_context_get_container_by_id(ctx, argv[1]); + if (!container) { + fprintf(stderr, "Failed to get container '%s'\n", argv[1]); + return -1; + } + + ocre_container_status_t status = ocre_container_get_status(container); + if (status != OCRE_CONTAINER_STATUS_CREATED && status != OCRE_CONTAINER_STATUS_STOPPED) { + fprintf(stderr, "Container '%s' is not ready to run\n", argv[1]); + return -1; + } + + int rc = ocre_container_start(container); + + fprintf(stdout, "%s\n", argv[1]); + + return rc; + } else { + fprintf(stderr, "'%s container start' requires exactly one argument\n\n", argv0); + return usage(argv0); + } + + return 0; +} diff --git a/src/shell/container/start.h b/src/shell/container/start.h new file mode 100644 index 00000000..c843f95d --- /dev/null +++ b/src/shell/container/start.h @@ -0,0 +1,10 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +int cmd_container_start(struct ocre_context *ctx, const char *argv0, int argc, char **argv); diff --git a/src/shell/container/stop.c b/src/shell/container/stop.c new file mode 100644 index 00000000..e683a5fa --- /dev/null +++ b/src/shell/container/stop.c @@ -0,0 +1,47 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +#include "../command.h" + +static int usage(const char *argv0) +{ + fprintf(stderr, "Usage: %s container stop CONTAINER\n", argv0); + fprintf(stderr, "\nStops a container in the Ocre context.\n"); + return -1; +} + +int cmd_container_stop(struct ocre_context *ctx, const char *argv0, int argc, char **argv) +{ + if (argc == 2) { + struct ocre_container *container = ocre_context_get_container_by_id(ctx, argv[1]); + if (!container) { + fprintf(stderr, "Failed to get container '%s'\n", argv[1]); + return -1; + } + + if (ocre_container_get_status(container) != OCRE_CONTAINER_STATUS_RUNNING) { + fprintf(stderr, "Container '%s' is not running\n", argv[1]); + return -1; + } + + int rc = ocre_container_stop(container); + + fprintf(stdout, "%s\n", argv[1]); + + return rc; + } else { + fprintf(stderr, "'%s container stop' requires exactly one argument\n\n", argv0); + return usage(argv0); + } + + return 0; +} diff --git a/src/shell/container/stop.h b/src/shell/container/stop.h new file mode 100644 index 00000000..89e0c960 --- /dev/null +++ b/src/shell/container/stop.h @@ -0,0 +1,10 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +int cmd_container_stop(struct ocre_context *ctx, const char *argv0, int argc, char **argv); diff --git a/src/shell/container/unpause.c b/src/shell/container/unpause.c new file mode 100644 index 00000000..aca63f13 --- /dev/null +++ b/src/shell/container/unpause.c @@ -0,0 +1,47 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +#include "../command.h" + +static int usage(const char *argv0) +{ + fprintf(stderr, "Usage: %s container unpause CONTAINER\n", argv0); + fprintf(stderr, "\nUnpauses a container in the Ocre context.\n"); + return -1; +} + +int cmd_container_unpause(struct ocre_context *ctx, const char *argv0, int argc, char **argv) +{ + if (argc == 2) { + struct ocre_container *container = ocre_context_get_container_by_id(ctx, argv[1]); + if (!container) { + fprintf(stderr, "Failed to get container '%s'\n", argv[1]); + return -1; + } + + if (ocre_container_get_status(container) != OCRE_CONTAINER_STATUS_PAUSED) { + fprintf(stderr, "Container '%s' is not paused\n", argv[1]); + return -1; + } + + int rc = ocre_container_unpause(container); + + fprintf(stdout, "%s\n", argv[1]); + + return rc; + } else { + fprintf(stderr, "'%s container unpause' requires exactly one argument\n\n", argv0); + return usage(argv0); + } + + return 0; +} diff --git a/src/shell/container/unpause.h b/src/shell/container/unpause.h new file mode 100644 index 00000000..2e54a581 --- /dev/null +++ b/src/shell/container/unpause.h @@ -0,0 +1,10 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +int cmd_container_unpause(struct ocre_context *ctx, const char *argv0, int argc, char **argv); diff --git a/src/shell/container/wait.c b/src/shell/container/wait.c new file mode 100644 index 00000000..11db581c --- /dev/null +++ b/src/shell/container/wait.c @@ -0,0 +1,53 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +#include "../command.h" + +static int usage(const char *argv0) +{ + fprintf(stderr, "Usage: %s container wait CONTAINER\n", argv0); + fprintf(stderr, "\nWaits for a container to exit.\n"); + return -1; +} + +int cmd_container_wait(struct ocre_context *ctx, const char *argv0, int argc, char **argv) +{ + if (argc == 2) { + struct ocre_container *container = ocre_context_get_container_by_id(ctx, argv[1]); + if (!container) { + fprintf(stderr, "Failed to get container '%s'\n", argv[1]); + return -1; + } + + ocre_container_status_t status = ocre_container_get_status(container); + if (status == OCRE_CONTAINER_STATUS_UNKNOWN || status == OCRE_CONTAINER_STATUS_CREATED) { + fprintf(stderr, "Container '%s' has not started\n", argv[1]); + return -1; + } + + int return_code; + int rc = ocre_container_wait(container, &return_code); + if (rc) { + fprintf(stderr, "Failed to wait for container '%s'\n", argv[1]); + return -1; + } + + fprintf(stdout, "%d\n", return_code); + + return rc; + } else { + fprintf(stderr, "'%s container wait' requires exactly one argument\n\n", argv0); + return usage(argv0); + } + + return 0; +} diff --git a/src/shell/container/wait.h b/src/shell/container/wait.h new file mode 100644 index 00000000..b586ea39 --- /dev/null +++ b/src/shell/container/wait.h @@ -0,0 +1,10 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +int cmd_container_wait(struct ocre_context *ctx, const char *argv0, int argc, char **argv); diff --git a/src/shell/image.c b/src/shell/image.c new file mode 100644 index 00000000..97398def --- /dev/null +++ b/src/shell/image.c @@ -0,0 +1,50 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "command.h" + +#include "image/ls.h" +#include "image/rm.h" +#include "image/pull.h" + +static int print_usage(struct ocre_context *ctx, const char *argv0, int argc, char **argv) +{ + fprintf(stderr, "Usage: %s image \n", argv0); + + fprintf(stderr, "\nCommands:\n"); + fprintf(stderr, " ls List images\n"); + fprintf(stderr, " pull Pull an image\n"); + fprintf(stderr, " rm Remove an image\n"); + return -1; +} + +static const struct ocre_command commands[] = { + {"help", print_usage}, + {"ls", cmd_image_ls}, + {"pull", cmd_image_pull}, + {"rm", cmd_image_rm}, +}; + +int cmd_image(struct ocre_context *ctx, const char *argv0, int argc, char **argv) +{ + if (argc < 2) { + return print_usage(ctx, argv0, argc, argv); + } + + for (size_t i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) { + if (!strcmp(argv[1], commands[i].name)) { + return commands[i].func(ctx, argv0, argc - 1, &argv[1]); + } + } + + fprintf(stderr, "Invalid command: '%s image %s'\n\n", argv0, argv[1]); + + return print_usage(ctx, argv0, argc, argv); +} diff --git a/src/shell/image.h b/src/shell/image.h new file mode 100644 index 00000000..ba4250f2 --- /dev/null +++ b/src/shell/image.h @@ -0,0 +1,10 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +int cmd_image(struct ocre_context *ctx, const char *argv0, int argc, char **argv); diff --git a/src/shell/image/ls.c b/src/shell/image/ls.c new file mode 100644 index 00000000..9a9ced58 --- /dev/null +++ b/src/shell/image/ls.c @@ -0,0 +1,162 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../command.h" +#include "sha256_file.h" + +static int usage(const char *argv0) +{ + fprintf(stderr, "Usage: %s image ls [IMAGE]\n", argv0); + fprintf(stderr, "\nList images in local storage.\n"); + return -1; +} + +static void header() +{ + printf("SHA-256\t\t\t\t\t\t\t\t\tSIZE\tNAME\n"); +} + +static int list_image(const char *name, const char *path) +{ + struct stat st; + + if (!path) { + fprintf(stderr, "Invalid image path\n"); + return -1; + } + + if (stat(path, &st)) { + fprintf(stderr, "Failed to stat image '%s'\n", path); + return -1; + } + + if (S_ISREG(st.st_mode)) { + char hash[65] = {0}; /* hash[64] is null-byte */ + + sha256_file(path, hash); + + printf("%s\t%ju\t%s\n", hash, (uintmax_t)st.st_size, name); + } + + return 0; +} + +static int list_images(const char *path) +{ + int ret = 0; + DIR *d; + const struct dirent *dir; + + d = opendir(path); + if (!d) { + fprintf(stderr, "Failed to open directory '%s'\n", path); + return -1; + } + + while ((dir = readdir(d)) != NULL) { + char *image_path = malloc(strlen(path) + strlen(dir->d_name) + 2); + if (!image_path) { + fprintf(stderr, "Failed to allocate memory for image path\n"); + ret = -1; + break; + } + + strcpy(image_path, path); + strcat(image_path, "/"); + strcat(image_path, dir->d_name); + + if (list_image(dir->d_name, image_path)) { + fprintf(stderr, "Failed to list image '%s'\n", image_path); + ret = -1; + } + + free(image_path); + } + + closedir(d); + + return ret; +} + +/* cppcheck-suppress constParameterPointer */ +int cmd_image_ls(struct ocre_context *ctx, const char *argv0, int argc, char **argv) +{ + switch (argc) { + case 1: { + const char *working_directory = ocre_context_get_working_directory(ctx); + + char *image_dir = malloc(strlen(working_directory) + strlen("/images") + 1); + if (!image_dir) { + fprintf(stderr, "Failed to allocate memory for image directory path\n"); + return -1; + } + + strcpy(image_dir, working_directory); + strcat(image_dir, "/images"); + + header(); + + if (list_images(image_dir)) { + fprintf(stderr, "Failed to list images in directory '%s'\n", image_dir); + } + + free(image_dir); + + break; + } + case 2: { + const char *working_directory = ocre_context_get_working_directory(ctx); + + /* Check if the provided image ID is valid */ + + if (argv[1] && !ocre_is_valid_id(argv[1])) { + fprintf(stderr, + "Invalid characters in image ID '%s'. Valid are [a-z0-9_-.] (lowercase " + "alphanumeric) and cannot start with '.'\n", + argv[1]); + return -1; + } + + char *image_path = malloc(strlen(working_directory) + strlen("/images") + strlen(argv[1]) + 2); + if (!image_path) { + fprintf(stderr, "Failed to allocate memory for image path\n"); + return -1; + } + + strcpy(image_path, working_directory); + strcat(image_path, "/images/"); + strcat(image_path, argv[1]); + + header(); + + if (list_image(argv[1], image_path)) { + fprintf(stderr, "Failed to list image '%s'\n", image_path); + } + + free(image_path); + + break; + } + default: { + fprintf(stderr, "'%s image ls' requires at most one argument\n\n", argv0); + return usage(argv0); + } + } + + return 0; +} diff --git a/src/shell/image/ls.h b/src/shell/image/ls.h new file mode 100644 index 00000000..ca6be291 --- /dev/null +++ b/src/shell/image/ls.h @@ -0,0 +1,10 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +int cmd_image_ls(struct ocre_context *ctx, const char *argv0, int argc, char **argv); diff --git a/src/shell/image/pull.c b/src/shell/image/pull.c new file mode 100644 index 00000000..60ece556 --- /dev/null +++ b/src/shell/image/pull.c @@ -0,0 +1,107 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include + +#include "../command.h" +#include "sha256_file.h" + +extern int ocre_download_file(const char *url, const char *filepath); + +static int usage(const char *argv0) +{ + fprintf(stderr, "Usage: %s image pull URL\n", argv0); // TODO: NAME[:TAG|@DIGEST] + fprintf(stderr, "\nDownloads an image from a remote repository to the local storage.\n"); + return -1; +} + +/* cppcheck-suppress constParameterPointer */ +int cmd_image_pull(struct ocre_context *ctx, const char *argv0, int argc, char **argv) +{ + char *local_name = NULL; + int ret = -1; + + if (argc < 2) { + fprintf(stderr, "'%s image pull' requires at least one argument\n\n", argv0); + return usage(argv0); + } + + if (argc == 2) { + local_name = strrchr(argv[1], '/'); + if (local_name == NULL) { + fprintf(stderr, "'Cannot determine image name from URL '%s'\n", argv[1]); + return -1; + } else { + local_name++; + } + if (*local_name == '\0') { + fprintf(stderr, "'Cannot determine image name from URL '%s'\n", argv[1]); + return -1; + } + + } else { + local_name = argv[2]; + } + + /* Check if the provided image ID is valid */ + + if (!ocre_is_valid_id(local_name)) { + fprintf(stderr, + "Invalid characters in image ID '%s'. Valid are [a-z0-9_-.] (lowercase " + "alphanumeric) and cannot start with '.'\n", + local_name); + return -1; + } + + const char *working_directory = ocre_context_get_working_directory(ctx); + + char *image_path = malloc(strlen(working_directory) + strlen("/images/") + strlen(local_name) + 1); + if (!image_path) { + fprintf(stderr, "Failed to allocate memory for image directory path\n"); + return -1; + } + + strcpy(image_path, working_directory); + strcat(image_path, "/images/"); + strcat(image_path, local_name); + + /* Check if image already exists */ + + struct stat st; + + ret = stat(image_path, &st); + if (!ret) { + fprintf(stderr, "Image '%s' already exists\n", local_name); + goto finish; + } + + fprintf(stderr, "Pulling '%s' from '%s'\n", local_name, argv[1]); + + ret = ocre_download_file(argv[1], image_path); + if (ret) { + fprintf(stderr, "Failed to download image '%s'\n", argv[1]); + goto finish; + } + + char hash[65] = {0}; /* hash[64] is null-byte */ + + sha256_file(image_path, hash); + + fprintf(stderr, "Digest: %s\n", hash); + + fprintf(stdout, "%s\n", local_name); + +finish: + free(image_path); + + return ret; +} diff --git a/src/shell/image/pull.h b/src/shell/image/pull.h new file mode 100644 index 00000000..3f5e1bbf --- /dev/null +++ b/src/shell/image/pull.h @@ -0,0 +1,10 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +int cmd_image_pull(struct ocre_context *ctx, const char *argv0, int argc, char **argv); diff --git a/src/shell/image/rm.c b/src/shell/image/rm.c new file mode 100644 index 00000000..1ad0895a --- /dev/null +++ b/src/shell/image/rm.c @@ -0,0 +1,62 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include + +#include "../command.h" + +static int usage(const char *argv0) +{ + fprintf(stderr, "Usage: %s image rm \n", argv0); + fprintf(stderr, "\nRemoves an image from local storage.\n"); + return -1; +} + +/* cppcheck-suppress constParameterPointer */ +int cmd_image_rm(struct ocre_context *ctx, const char *argv0, int argc, char **argv) +{ + if (argc == 2) { + /* Check if the provided image ID is valid */ + + if (argv[1] && !ocre_is_valid_id(argv[1])) { + fprintf(stderr, + "Invalid characters in image ID '%s'. Valid are [a-z0-9_-.] (lowercase " + "alphanumeric) and cannot start with '.'\n", + argv[1]); + return -1; + } + + const char *working_directory = ocre_context_get_working_directory(ctx); + + char *image_path = malloc(strlen(working_directory) + strlen("/images") + strlen(argv[1]) + 2); + if (!image_path) { + fprintf(stderr, "Failed to allocate memory for image path\n"); + return -1; + } + + strcpy(image_path, working_directory); + strcat(image_path, "/images/"); + strcat(image_path, argv[1]); + + /* Danger: we do not check if the image is in use */ + + if (remove(image_path)) { + fprintf(stderr, "Failed to remove image '%s'\n", image_path); + } + + free(image_path); + } else { + fprintf(stderr, "'%s image rm' requires exactly one argument\n\n", argv0); + return usage(argv0); + } + + return 0; +} diff --git a/src/shell/image/rm.h b/src/shell/image/rm.h new file mode 100644 index 00000000..a5419c05 --- /dev/null +++ b/src/shell/image/rm.h @@ -0,0 +1,10 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +int cmd_image_rm(struct ocre_context *ctx, const char *argv0, int argc, char **argv); diff --git a/src/shell/image/sha256_file.c b/src/shell/image/sha256_file.c new file mode 100644 index 00000000..20033748 --- /dev/null +++ b/src/shell/image/sha256_file.c @@ -0,0 +1,77 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#include "../sha256/sha256.h" + +#define FILE_BUFFER_SIZE 1024 + +int sha256_file(const char *path, char *hash) +{ + int ret = -1; + void *buffer = NULL; + + if (!path || !hash) { + return -1; + } + + int fd = open(path, O_RDONLY); + if (fd < 0) { + return -1; + } + + struct stat finfo; + int rc = fstat(fd, &finfo); + if (rc < 0) { + goto finish; + } + + off_t file_size = finfo.st_size; + + if (!file_size) { + goto finish; + } + + buffer = malloc(FILE_BUFFER_SIZE); + if (!buffer) { + goto finish; + } + + struct sha256_buff buff; + sha256_init(&buff); + + off_t total_bytes_read = 0; + while (total_bytes_read < file_size) { + ssize_t bytes_read = read(fd, buffer, FILE_BUFFER_SIZE); + if (bytes_read < 0) { + goto finish; + } + + if (!bytes_read) { + ret = 0; + break; + } + + sha256_update(&buff, buffer, bytes_read); + + total_bytes_read += bytes_read; + } + + sha256_finalize(&buff); + sha256_read_hex(&buff, hash); + +finish: + free(buffer); + close(fd); + + return ret; +} diff --git a/src/shell/image/sha256_file.h b/src/shell/image/sha256_file.h new file mode 100644 index 00000000..97ae3867 --- /dev/null +++ b/src/shell/image/sha256_file.h @@ -0,0 +1,8 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +int sha256_file(const char *path, char *hash); diff --git a/src/shell/include/ocre/shell/shell.h b/src/shell/include/ocre/shell/shell.h new file mode 100644 index 00000000..6ed95e35 --- /dev/null +++ b/src/shell/include/ocre/shell/shell.h @@ -0,0 +1,10 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +int ocre_shell(struct ocre_context *ctx, int argc, char *argv[]); diff --git a/src/shell/sha256/LICENSE b/src/shell/sha256/LICENSE new file mode 100644 index 00000000..8534e7b3 --- /dev/null +++ b/src/shell/sha256/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 LekKit + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/shell/sha256/sha256.c b/src/shell/sha256/sha256.c new file mode 100644 index 00000000..d44443be --- /dev/null +++ b/src/shell/sha256/sha256.c @@ -0,0 +1,175 @@ +/* + MIT License + + Copyright (c) 2020 LekKit https://github.com/LekKit + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +/* Details of the implementation, etc can be found here: https://en.wikipedia.org/wiki/SHA-2 + See sha256.h for short documentation on library usage */ + +#include "sha256.h" + +void sha256_init(struct sha256_buff *buff) +{ + buff->h[0] = 0x6a09e667; + buff->h[1] = 0xbb67ae85; + buff->h[2] = 0x3c6ef372; + buff->h[3] = 0xa54ff53a; + buff->h[4] = 0x510e527f; + buff->h[5] = 0x9b05688c; + buff->h[6] = 0x1f83d9ab; + buff->h[7] = 0x5be0cd19; + buff->data_size = 0; + buff->chunk_size = 0; +} + +static const uint32_t k[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; + +#define rotate_r(val, bits) (val >> bits | val << (32 - bits)) + +static void sha256_calc_chunk(struct sha256_buff *buff, const uint8_t *chunk) +{ + uint32_t w[64]; + uint32_t tv[8]; + uint32_t i; + + for (i = 0; i < 16; ++i) { + w[i] = (uint32_t)chunk[0] << 24 | (uint32_t)chunk[1] << 16 | (uint32_t)chunk[2] << 8 | + (uint32_t)chunk[3]; + chunk += 4; + } + + for (i = 16; i < 64; ++i) { + uint32_t s0 = rotate_r(w[i - 15], 7) ^ rotate_r(w[i - 15], 18) ^ (w[i - 15] >> 3); + uint32_t s1 = rotate_r(w[i - 2], 17) ^ rotate_r(w[i - 2], 19) ^ (w[i - 2] >> 10); + w[i] = w[i - 16] + s0 + w[i - 7] + s1; + } + + for (i = 0; i < 8; ++i) + tv[i] = buff->h[i]; + + for (i = 0; i < 64; ++i) { + uint32_t S1 = rotate_r(tv[4], 6) ^ rotate_r(tv[4], 11) ^ rotate_r(tv[4], 25); + uint32_t ch = (tv[4] & tv[5]) ^ (~tv[4] & tv[6]); + uint32_t temp1 = tv[7] + S1 + ch + k[i] + w[i]; + uint32_t S0 = rotate_r(tv[0], 2) ^ rotate_r(tv[0], 13) ^ rotate_r(tv[0], 22); + uint32_t maj = (tv[0] & tv[1]) ^ (tv[0] & tv[2]) ^ (tv[1] & tv[2]); + uint32_t temp2 = S0 + maj; + + tv[7] = tv[6]; + tv[6] = tv[5]; + tv[5] = tv[4]; + tv[4] = tv[3] + temp1; + tv[3] = tv[2]; + tv[2] = tv[1]; + tv[1] = tv[0]; + tv[0] = temp1 + temp2; + } + + for (i = 0; i < 8; ++i) + buff->h[i] += tv[i]; +} + +void sha256_update(struct sha256_buff *buff, const void *data, size_t size) +{ + const uint8_t *ptr = (const uint8_t *)data; + buff->data_size += size; + /* If there is data left in buff, concatenate it to process as new chunk */ + if (size + buff->chunk_size >= 64) { + uint8_t tmp_chunk[64]; + memcpy(tmp_chunk, buff->last_chunk, buff->chunk_size); + memcpy(tmp_chunk + buff->chunk_size, ptr, 64 - buff->chunk_size); + ptr += (64 - buff->chunk_size); + size -= (64 - buff->chunk_size); + buff->chunk_size = 0; + sha256_calc_chunk(buff, tmp_chunk); + } + /* Run over data chunks */ + while (size >= 64) { + sha256_calc_chunk(buff, ptr); + ptr += 64; + size -= 64; + } + + /* Save remaining data in buff, will be reused on next call or finalize */ + memcpy(buff->last_chunk + buff->chunk_size, ptr, size); + buff->chunk_size += size; +} + +void sha256_finalize(struct sha256_buff *buff) +{ + buff->last_chunk[buff->chunk_size] = 0x80; + buff->chunk_size++; + memset(buff->last_chunk + buff->chunk_size, 0, 64 - buff->chunk_size); + + /* If there isn't enough space to fit int64, pad chunk with zeroes and prepare next chunk */ + if (buff->chunk_size > 56) { + sha256_calc_chunk(buff, buff->last_chunk); + memset(buff->last_chunk, 0, 64); + } + + /* Add total size as big-endian int64 x8 */ + uint64_t size = buff->data_size * 8; + int i; + for (i = 8; i > 0; --i) { + buff->last_chunk[55 + i] = size & 255; + size >>= 8; + } + + sha256_calc_chunk(buff, buff->last_chunk); +} + +void sha256_read(const struct sha256_buff *buff, uint8_t *hash) +{ + uint32_t i; + for (i = 0; i < 8; i++) { + hash[i * 4] = (buff->h[i] >> 24) & 255; + hash[i * 4 + 1] = (buff->h[i] >> 16) & 255; + hash[i * 4 + 2] = (buff->h[i] >> 8) & 255; + hash[i * 4 + 3] = buff->h[i] & 255; + } +} + +static void bin_to_hex(const void *data, uint32_t len, char *out) +{ + static const char *const lut = "0123456789abcdef"; + uint32_t i; + for (i = 0; i < len; ++i) { + uint8_t c = ((const uint8_t *)data)[i]; + out[i * 2] = lut[c >> 4]; + out[i * 2 + 1] = lut[c & 15]; + } +} + +void sha256_read_hex(const struct sha256_buff *buff, char *hex) +{ + uint8_t hash[32]; + sha256_read(buff, hash); + bin_to_hex(hash, 32, hex); +} diff --git a/src/shell/sha256/sha256.h b/src/shell/sha256/sha256.h new file mode 100644 index 00000000..efc784c5 --- /dev/null +++ b/src/shell/sha256/sha256.h @@ -0,0 +1,55 @@ +/* + MIT License + + Copyright (c) 2020 LekKit https://github.com/LekKit + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#ifndef SHA256_H +#define SHA256_H + +#include +#include +#include + +struct sha256_buff { + uint64_t data_size; + uint32_t h[8]; + uint8_t last_chunk[64]; + uint8_t chunk_size; +}; + +/* Initialization, must be called before any further use */ +void sha256_init(struct sha256_buff *buff); + +/* Process block of data of arbitary length, can be used on data streams (files, etc) */ +void sha256_update(struct sha256_buff *buff, const void *data, size_t size); + +/* Produces final hash values (digest) to be read + If the buffer is reused later, init must be called again */ +void sha256_finalize(struct sha256_buff *buff); + +/* Read digest into 32-byte binary array */ +void sha256_read(const struct sha256_buff *buff, uint8_t *hash); + +/* Read digest into 64-char string as hex (without null-byte) */ +void sha256_read_hex(const struct sha256_buff *buff, char *hex); + +#endif diff --git a/src/shell/shell.c b/src/shell/shell.c new file mode 100644 index 00000000..f238435f --- /dev/null +++ b/src/shell/shell.c @@ -0,0 +1,134 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include + +#include "command.h" +#include "image.h" +#include "image/ls.h" +#include "image/pull.h" +#include "container.h" +#include "container/kill.h" +#include "container/pause.h" +#include "container/ps.h" +#include "container/rm.h" +#include "container/start.h" +#include "container/unpause.h" +#include "container/stop.h" +#include "container/wait.h" +#include "container/create.h" + +static int print_version(struct ocre_context *ctx, const char *argv0, int argc, char **argv) +{ + fprintf(stdout, "Ocre version: %s\n", ocre_build_configuration.version); + fprintf(stdout, "Commit ID: %s\n", ocre_build_configuration.commit_id); + fprintf(stdout, "Build information: %s\n", ocre_build_configuration.build_info); + fprintf(stdout, "Build date: %s\n", ocre_build_configuration.build_date); + + return 0; +} + +static int print_usage(struct ocre_context *ctx, const char *argv0, int argc, char **argv) +{ + fprintf(stderr, "Usage: %s [-v] \n", argv0); + + fprintf(stderr, "\nOptions:\n"); + fprintf(stderr, " -v Verbose mode\n"); + + fprintf(stderr, "\nCommands:\n"); + fprintf(stderr, " help Display this help message\n"); + fprintf(stderr, " version Display version information\n"); + fprintf(stderr, " image Image manipulation commands\n"); + fprintf(stderr, " container Container management commands\n"); + + fprintf(stderr, "\nShortcut Commands:\n"); + fprintf(stderr, " ps container ps\n"); + fprintf(stderr, " create container create\n"); + fprintf(stderr, " run container run\n"); + fprintf(stderr, " start container start\n"); + // fprintf(stderr, " stop container stop\n"); + fprintf(stderr, " kill container kill\n"); + // fprintf(stderr, " pause container pause\n"); + // fprintf(stderr, " unpause container unpause\n"); + fprintf(stderr, " rm container rm\n"); + fprintf(stderr, " images image ls\n"); + fprintf(stderr, " pull image pull\n"); + return -1; +} + +static const struct ocre_command commands[] = { + /* general commands */ + {"help", print_usage}, + {"version", print_version}, + {"image", cmd_image}, + {"container", cmd_container}, + /* container shortcuts */ + {"ps", cmd_container_ps}, + {"create", cmd_container_create_run}, + {"run", cmd_container_create_run}, + {"start", cmd_container_start}, + {"stop", cmd_container_stop}, + {"kill", cmd_container_kill}, + {"pause", cmd_container_pause}, + {"unpause", cmd_container_unpause}, + {"rm", cmd_container_rm}, + /* image shortcuts */ + {"images", cmd_image_ls}, + {"pull", cmd_image_pull}, +}; + +int ocre_shell(struct ocre_context *ctx, int argc, char *argv[]) +{ + int opt; + bool verbose = false; + while ((opt = getopt(argc, argv, "+v")) != -1) { + switch (opt) { + case 'v': { + if (verbose) { + fprintf(stderr, "'-v' can be set only once\n\n"); + print_usage(ctx, argv[0], argc, argv); + return -1; + } + + verbose = true; + continue; + } + case '?': { + fprintf(stderr, "Invalid option: '%c'\n", optopt); + return -1; + } + } + } + + if (argc <= optind) { + return print_usage(ctx, argv[0], argc, argv); + } + + if (verbose) { + fprintf(stderr, "Using context: %p\n", ctx); + } + + int save_optind = optind; + + for (size_t i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) { + if (argv[save_optind] && !strcmp(argv[save_optind], commands[i].name)) { + optind = 1; + return commands[i].func(ctx, argv[0], argc - save_optind, &argv[save_optind]); + } + } + + fprintf(stderr, "Invalid command: '%s %s'\n\n", argv[0], argv[save_optind]); + + return print_usage(ctx, argv[0], argc, argv); + + return 0; +} diff --git a/tests/Unity b/tests/Unity new file mode 160000 index 00000000..cbcd08fa --- /dev/null +++ b/tests/Unity @@ -0,0 +1 @@ +Subproject commit cbcd08fa7de711053a3deec6339ee89cad5d2697 diff --git a/tests/coverage/CMakeLists.txt b/tests/coverage/CMakeLists.txt new file mode 100644 index 00000000..daf29918 --- /dev/null +++ b/tests/coverage/CMakeLists.txt @@ -0,0 +1,36 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +set(CMAKE_C_COMPILER /usr/bin/clang) + +set(CMAKE_C_FLAGS + "-fprofile-instr-generate -fcoverage-mapping" +) + +project(OcreTestSourceCoverage) + +add_subdirectory(../system/posix OcreSystemTests) + +add_custom_target(coverage ALL + COMMAND llvm-profdata merge -sparse + OcreSystemTests/test_lib.profraw + OcreSystemTests/test_ocre.profraw + OcreSystemTests/test_context.profraw + OcreSystemTests/test_container.profraw + -o default.profdata + COMMAND llvm-cov show + -format=html -output-dir=report + -instr-profile=default.profdata + --ignore-filename-regex=$wasm-micro-runtime/ + --ignore-filename-regex=$test/ + --object=OcreSystemTests/test_lib + --object=OcreSystemTests/test_ocre + --object=OcreSystemTests/test_context + --object=OcreSystemTests/test_container + DEPENDS + run-systests +) diff --git a/tests/groups/modbusServerValidation/clean.sh b/tests/groups/modbusServerValidation/clean.sh deleted file mode 100644 index 6b895bf1..00000000 --- a/tests/groups/modbusServerValidation/clean.sh +++ /dev/null @@ -1 +0,0 @@ -echo "Cleanup is complete" \ No newline at end of file diff --git a/tests/groups/modbusServerValidation/config.json b/tests/groups/modbusServerValidation/config.json deleted file mode 100644 index 1190912a..00000000 --- a/tests/groups/modbusServerValidation/config.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "Modbus Server Validation", - "description": "Test the Ocre Modbus server ", - "setup": [ - { - "name": "Modbus Server Validation Setup", - "exec": "bash setup.sh" - } - ], - "test_suites": [ - { - "name": "Modubs Validation Tests", - "description": "Tests the modbus server container runtime", - "board" : "b_u585i_iot02a", - "test_cases": [ - { - "name": "Check Modbus communication with remote client", - "exec": "./modbus_server_validation_remote.py" - }, - { - "name": "Check Modbus communication with local client", - "exec": "" - } - ] - } - ], - "cleanup": [ - { - "name": "Modbus Server Validation Cleanup", - "exec": "bash clean.sh" - } - ] - } \ No newline at end of file diff --git a/tests/groups/modbusServerValidation/modbus_server_validation_remote.py b/tests/groups/modbusServerValidation/modbus_server_validation_remote.py deleted file mode 100755 index 4e6a1658..00000000 --- a/tests/groups/modbusServerValidation/modbus_server_validation_remote.py +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env python3 - -import serial -import time -import sys -from pymodbus.client import ModbusTcpClient - -def exitSafe(conn: serial.Serial, client: ModbusTcpClient, exitCode: int): - conn.close() - client.close() - sys.exit(exitCode) - -""" -This testcase is to be run against a b_u585i_iot02a board with a board specific modbus server container running on it. - -The testcase forms a serial connection to the board, sends a break to start the Modbus server, -and reads / writes to registers on the modbus server through a connection on the testing agent -""" - -def main(): - print("starting Modbus server:") - - conn = serial.Serial('/dev/ttyACM0', 115200, timeout=1) - conn.reset_input_buffer() - conn.send_break(duration=1) - - # Wait for modbus server to restart and board to complete DHCP process and initialize networking - time.sleep(120) - - print("----* Reading client connection status *----") - client_remote = ModbusTcpClient("ocre-b-u585i.lfedge.iol.unh.edu", port=1502) - client_remote.connect() - - connection_results = [client_remote.connected, client_remote.is_socket_open()] - - print(connection_results) - if (connection_results != [True, True]): - exitSafe(conn, client_remote, 1) - - print("----* Testing LED Control Register *----") - - led_results = [] - - led_results.append(client_remote.read_holding_registers(0x00).registers[0]) # 0 - client_remote.write_register(0x00, 0x01) - time.sleep(5) - led_results.append(client_remote.read_holding_registers(0x00).registers[0]) # 1 - client_remote.write_register(0x00, 0x02) - time.sleep(5) - led_results.append(client_remote.read_holding_registers(0x00).registers[0]) # 2 - client_remote.write_register(0x00, 0x00) - time.sleep(5) - led_results.append(client_remote.read_holding_registers(0x00).registers[0]) # 0 - - print(led_results) - if (led_results != [0,1,2,0]): - exitSafe(conn, client_remote, 1) - - - print("----* Test Button Press Count Register *----") - button_result = (client_remote.read_holding_registers(0x01).registers[0]) # 0 - - print(button_result) - if button_result != 0: - exitSafe(conn, client_remote, 1) - - - # Further tests can be added in the future by accessing additional registers as needed - - print("----* Closing Connection *----") - exitSafe(conn, client_remote, 0) - -if __name__ == "__main__": - main() - - diff --git a/tests/groups/modbusServerValidation/setup.sh b/tests/groups/modbusServerValidation/setup.sh deleted file mode 100644 index 798952e5..00000000 --- a/tests/groups/modbusServerValidation/setup.sh +++ /dev/null @@ -1,2 +0,0 @@ -# Assumes board is being flashed with both hello world and modbus-server -echo "Setup is complete" \ No newline at end of file diff --git a/tests/leaks/CMakeLists.txt b/tests/leaks/CMakeLists.txt new file mode 100644 index 00000000..306cd0b1 --- /dev/null +++ b/tests/leaks/CMakeLists.txt @@ -0,0 +1,20 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +set(CMAKE_C_COMPILER /usr/bin/clang) + +string(APPEND CMAKE_C_FLAGS + "-fsanitize=address" +) + +project(OcreLeaksCheckPosix) + +add_subdirectory(../system/posix OcreSystemTests) + +add_custom_target(always_run ALL + DEPENDS run-systests run-demo run-mini +) diff --git a/tests/system/container.c b/tests/system/container.c new file mode 100644 index 00000000..56d30a9c --- /dev/null +++ b/tests/system/container.c @@ -0,0 +1,225 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include + +#include +#include + +struct ocre_context *context; +struct ocre_container *hello_world; +struct ocre_container *blinky; + +void setUp(void) +{ + const struct ocre_container_args args = { + .capabilities = + (const char *[]){ + "ocre:api", + NULL, + }, + }; + + ocre_initialize(NULL); + context = ocre_create_context("./ocre/src/ocre/var/lib/ocre"); + + hello_world = ocre_context_create_container(context, "hello-world.wasm", "wamr/wasip1", "hello", false, NULL); + blinky = ocre_context_create_container(context, "blinky.wasm", "wamr/wasip1", NULL, true, &args); +} + +void tearDown(void) +{ + ocre_container_kill(hello_world); + ocre_container_kill(blinky); + ocre_container_wait(hello_world, NULL); + ocre_container_wait(blinky, NULL); + ocre_context_remove_container(context, hello_world); + ocre_context_remove_container(context, blinky); + ocre_destroy_context(context); + ocre_deinitialize(); +} + +void test_ocre_container_status_null(void) +{ + TEST_ASSERT_EQUAL(OCRE_CONTAINER_STATUS_UNKNOWN, ocre_container_get_status(NULL)); +} + +void test_ocre_container_status(void) +{ + TEST_ASSERT_EQUAL(OCRE_CONTAINER_STATUS_CREATED, ocre_container_get_status(blinky)); + + /* Start blinky */ + + TEST_ASSERT_EQUAL_INT(0, ocre_container_start(blinky)); + TEST_ASSERT_EQUAL(OCRE_CONTAINER_STATUS_RUNNING, ocre_container_get_status(blinky)); + + /* Kill blinky */ + + TEST_ASSERT_EQUAL_INT(0, ocre_container_kill(blinky)); + + TEST_ASSERT_EQUAL_INT(0, ocre_container_wait(blinky, NULL)); + + TEST_ASSERT_EQUAL(OCRE_CONTAINER_STATUS_STOPPED, ocre_container_get_status(blinky)); +} + +void test_ocre_container_restart(void) +{ + TEST_ASSERT_EQUAL(OCRE_CONTAINER_STATUS_CREATED, ocre_container_get_status(hello_world)); + + /* Run hello_world */ + + TEST_ASSERT_EQUAL_INT(0, ocre_container_start(hello_world)); + TEST_ASSERT_EQUAL_INT(0, ocre_container_wait(hello_world, NULL)); + TEST_ASSERT_EQUAL(OCRE_CONTAINER_STATUS_STOPPED, ocre_container_get_status(hello_world)); + + /* Run again */ + + TEST_ASSERT_EQUAL_INT(0, ocre_container_start(hello_world)); + TEST_ASSERT_EQUAL_INT(0, ocre_container_wait(hello_world, NULL)); + TEST_ASSERT_EQUAL(OCRE_CONTAINER_STATUS_STOPPED, ocre_container_get_status(hello_world)); +} + +void test_ocre_container_kill(void) +{ + /* Run blinky */ + + TEST_ASSERT_EQUAL_INT(0, ocre_container_start(blinky)); + TEST_ASSERT_EQUAL(OCRE_CONTAINER_STATUS_RUNNING, ocre_container_get_status(blinky)); + + /* Kill blinky */ + + TEST_ASSERT_EQUAL_INT(0, ocre_container_kill(blinky)); + + /* Wait for blinky to stop */ + + int status; + TEST_ASSERT_EQUAL_INT(0, ocre_container_wait(blinky, &status)); + + /* Return code should indicate killed */ + // TODO: FIX!!! + TEST_ASSERT_EQUAL(0, status); + + /* Get status */ + + TEST_ASSERT_EQUAL(OCRE_CONTAINER_STATUS_STOPPED, ocre_container_get_status(blinky)); +} + +void test_ocre_container_destroy(void) +{ + /* Start blinky */ + + TEST_ASSERT_EQUAL_INT(0, ocre_container_start(blinky)); + + TEST_ASSERT_EQUAL(OCRE_CONTAINER_STATUS_RUNNING, ocre_container_get_status(blinky)); +} + +void test_ocre_container_get_image_null(void) +{ + TEST_ASSERT_NULL(ocre_container_get_image(NULL)); +} + +void test_ocre_container_get_image_blinky(void) +{ + TEST_ASSERT_EQUAL_STRING("blinky.wasm", ocre_container_get_image(blinky)); +} + +void test_ocre_container_get_id_null(void) +{ + TEST_ASSERT_NULL(ocre_container_get_id(NULL)); +} + +void test_ocre_container_get_id_hello(void) +{ + TEST_ASSERT_EQUAL_STRING("hello", ocre_container_get_id(hello_world)); +} + +void test_ocre_container_pause_unpause_null(void) +{ + TEST_ASSERT_EQUAL_INT(-1, ocre_container_pause(NULL)); + TEST_ASSERT_EQUAL_INT(-1, ocre_container_unpause(NULL)); +} + +void test_ocre_container_pause_unpause_wamr(void) +{ + /* Run blinky */ + + TEST_ASSERT_EQUAL_INT(0, ocre_container_start(blinky)); + TEST_ASSERT_EQUAL(OCRE_CONTAINER_STATUS_RUNNING, ocre_container_get_status(blinky)); + + /* Pause blinky. Does not work, will return -1 */ + + TEST_ASSERT_EQUAL_INT(-1, ocre_container_pause(blinky)); + + /* Should be running because pause does not work */ + TEST_ASSERT_EQUAL(OCRE_CONTAINER_STATUS_RUNNING, ocre_container_get_status(blinky)); + + /* Unpause blinky. Does not work, will return -1 */ + + TEST_ASSERT_EQUAL_INT(-1, ocre_container_unpause(blinky)); + + /* Should be running */ + TEST_ASSERT_EQUAL(OCRE_CONTAINER_STATUS_RUNNING, ocre_container_get_status(blinky)); + + /* Kill blinky */ + + TEST_ASSERT_EQUAL_INT(0, ocre_container_kill(blinky)); + + /* Wait for blinky to stop */ + + int status; + TEST_ASSERT_EQUAL_INT(0, ocre_container_wait(blinky, &status)); +} + +void test_ocre_container_stop_null(void) +{ + TEST_ASSERT_EQUAL_INT(-1, ocre_container_stop(NULL)); +} + +void test_ocre_container_stop_wamr(void) +{ + /* Run blinky */ + + TEST_ASSERT_EQUAL_INT(0, ocre_container_start(blinky)); + TEST_ASSERT_EQUAL(OCRE_CONTAINER_STATUS_RUNNING, ocre_container_get_status(blinky)); + + /* Stop blinky. Does not work, will return -1 */ + + TEST_ASSERT_EQUAL_INT(-1, ocre_container_stop(blinky)); + + /* Should be running because stop does not work */ + TEST_ASSERT_EQUAL(OCRE_CONTAINER_STATUS_RUNNING, ocre_container_get_status(blinky)); + + /* Kill blinky */ + + TEST_ASSERT_EQUAL_INT(0, ocre_container_kill(blinky)); + + /* Wait for blinky to stop */ + + int status; + TEST_ASSERT_EQUAL_INT(0, ocre_container_wait(blinky, &status)); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_ocre_container_status_null); + RUN_TEST(test_ocre_container_status); + RUN_TEST(test_ocre_container_restart); + RUN_TEST(test_ocre_container_kill); + RUN_TEST(test_ocre_container_destroy); + RUN_TEST(test_ocre_container_get_image_null); + RUN_TEST(test_ocre_container_get_image_blinky); + RUN_TEST(test_ocre_container_get_id_null); + RUN_TEST(test_ocre_container_get_id_hello); + RUN_TEST(test_ocre_container_pause_unpause_null); + RUN_TEST(test_ocre_container_pause_unpause_wamr); + RUN_TEST(test_ocre_container_stop_null); + RUN_TEST(test_ocre_container_stop_wamr); + return UNITY_END(); +} diff --git a/tests/system/context.c b/tests/system/context.c new file mode 100644 index 00000000..7cda3b44 --- /dev/null +++ b/tests/system/context.c @@ -0,0 +1,531 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include + +struct ocre_context *context; + +void setUp(void) +{ + ocre_initialize(NULL); + context = ocre_create_context("./ocre/src/ocre/var/lib/ocre"); +} + +void tearDown(void) +{ + ocre_destroy_context(context); + ocre_deinitialize(); +} + +void test_ocre_context_initialized(void) +{ + /* Context is initialized */ + + TEST_ASSERT_NOT_NULL(context); +} + +void test_ocre_context_get_working_directory_ok(void) +{ + /* Try to get working directory with good context */ + + TEST_ASSERT_NOT_NULL(ocre_context_get_working_directory(context)); +} + +void test_ocre_context_get_working_directory_err(void) +{ + /* Try to get working directory with bad context */ + + TEST_ASSERT_NULL(ocre_context_get_working_directory(NULL)); +} + +void test_ocre_context_create_container_null_context(void) +{ + /* Try to create container with bad context */ + + TEST_ASSERT_NULL(ocre_context_create_container(NULL, "hello-world.wasm", "wamr/wasip1", NULL, false, NULL)); +} + +void test_ocre_context_create_container_bad_ids(void) +{ + /* Try to create container with bad ids */ + + TEST_ASSERT_NULL(ocre_context_create_container(context, NULL, NULL, NULL, false, NULL)); + TEST_ASSERT_NULL(ocre_context_create_container(context, "", "", NULL, false, NULL)); + TEST_ASSERT_NULL(ocre_context_create_container(context, "hello-world.wasm", "wamr/wasip1", ".", false, NULL)); + TEST_ASSERT_NULL(ocre_context_create_container(context, "hello-world.wasm", "wamr/wasip1", "..", false, NULL)); + TEST_ASSERT_NULL(ocre_context_create_container(context, "hello-world.wasm", "wamr/wasip1", "/", false, NULL)); + TEST_ASSERT_NULL(ocre_context_create_container(context, "hello-world.wasm", "wamr/wasip1", "/somewhere", false, NULL)); + TEST_ASSERT_NULL(ocre_context_create_container(context, "hello-world.wasm", "wamr/wasip1", "some/where", false, NULL)); + TEST_ASSERT_NULL(ocre_context_create_container(context, ".", "wamr/wasip1", NULL, false, NULL)); + TEST_ASSERT_NULL(ocre_context_create_container(context, "..", "wamr/wasip1", NULL, false, NULL)); + TEST_ASSERT_NULL(ocre_context_create_container(context, "/", "wamr/wasip1", NULL, false, NULL)); + TEST_ASSERT_NULL(ocre_context_create_container(context, "/somewhere", "wamr/wasip1", NULL, false, NULL)); + TEST_ASSERT_NULL(ocre_context_create_container(context, "some/where", "wamr/wasip1", NULL, false, NULL)); +} + +void test_ocre_context_create_container_bad_runtimes(void) +{ + TEST_ASSERT_NULL( + ocre_context_create_container(context, "hello-world.wasm", "does-not-exit", NULL, false, NULL)); + TEST_ASSERT_NULL(ocre_context_create_container(context, "hello-world.wasm", "", NULL, false, NULL)); +} + +void test_ocre_context_create_container_bad_mounts(void) +{ + struct ocre_container_args bad_mounts = {0}; + + bad_mounts.mounts = (const char *[]){"no-colon", NULL}; + + TEST_ASSERT_NULL(ocre_context_create_container(context, "hello-world.wasm", "wamr/wasip1", NULL, false, &bad_mounts)); + + bad_mounts.mounts = (const char *[]){"no_abs_path:/hello", NULL}; + + TEST_ASSERT_NULL(ocre_context_create_container(context, "hello-world.wasm", "wamr/wasip1", NULL, false, &bad_mounts)); + + bad_mounts.mounts = (const char *[]){"/tmp:/", NULL}; + + TEST_ASSERT_NULL(ocre_context_create_container(context, "hello-world.wasm", "wamr/wasip1", NULL, false, &bad_mounts)); + + bad_mounts.mounts = (const char *[]){"/tmp:no_abs", NULL}; + + TEST_ASSERT_NULL(ocre_context_create_container(context, "hello-world.wasm", "wamr/wasip1", NULL, false, &bad_mounts)); + + bad_mounts.mounts = (const char *[]){"/tmp:", NULL}; + + TEST_ASSERT_NULL(ocre_context_create_container(context, "hello-world.wasm", "wamr/wasip1", NULL, false, &bad_mounts)); + + bad_mounts.mounts = (const char *[]){"/tmp", NULL}; + + TEST_ASSERT_NULL(ocre_context_create_container(context, "hello-world.wasm", "wamr/wasip1", NULL, false, &bad_mounts)); + + bad_mounts.mounts = (const char *[]){"/tmp:/ok", "bad", NULL}; + + TEST_ASSERT_NULL(ocre_context_create_container(context, "hello-world.wasm", "wamr/wasip1", NULL, false, &bad_mounts)); +} + +void test_ocre_context_remove_bad_container(void) +{ + /* Try to remove a non-existent container */ + + TEST_ASSERT_NOT_EQUAL_INT(0, ocre_context_remove_container(context, NULL)); +} + +void test_ocre_context_remove_bad_context(void) +{ + /* Try to remove containers from null context */ + + TEST_ASSERT_NOT_EQUAL_INT(0, ocre_context_remove_container(NULL, NULL)); +} + +void test_ocre_context_create_container_ok(void) +{ + /* Create a valid container */ + + struct ocre_container *container = + ocre_context_create_container(context, "hello-world.wasm", "wamr/wasip1", NULL, false, NULL); + TEST_ASSERT_NOT_NULL(container); + + /* Check container status */ + + TEST_ASSERT_EQUAL_INT(OCRE_CONTAINER_STATUS_CREATED, ocre_container_get_status(container)); + + /* Remove the container */ + + TEST_ASSERT_EQUAL_INT(0, ocre_context_remove_container(context, container)); +} + +void test_ocre_context_create_container_null_runtime_ok(void) +{ + /* Create a valid container */ + + struct ocre_container *container = + ocre_context_create_container(context, "hello-world.wasm", NULL, NULL, false, NULL); + TEST_ASSERT_NOT_NULL(container); + + /* Check container status */ + + TEST_ASSERT_EQUAL_INT(OCRE_CONTAINER_STATUS_CREATED, ocre_container_get_status(container)); + + /* Remove the container */ + + TEST_ASSERT_EQUAL_INT(0, ocre_context_remove_container(context, container)); +} + +void test_ocre_context_create_container_with_id_ok(void) +{ + /* Create a valid container */ + + struct ocre_container *container = + ocre_context_create_container(context, "hello-world.wasm", "wamr/wasip1", "test-container", false, NULL); + TEST_ASSERT_NOT_NULL(container); + + /* Check container status */ + + TEST_ASSERT_EQUAL_INT(OCRE_CONTAINER_STATUS_CREATED, ocre_container_get_status(container)); + + /* Check container id */ + + TEST_ASSERT_EQUAL_STRING("test-container", ocre_container_get_id(container)); + + /* Remove the container */ + + TEST_ASSERT_EQUAL_INT(0, ocre_context_remove_container(context, container)); +} + +void test_ocre_context_create_start_container_filesystem(void) +{ + const struct ocre_container_args args = { + .capabilities = + (const char *[]){ + "filesystem", + "ocre:api", + NULL, + }, + }; + + /* Create a valid container */ + + struct ocre_container *container = + ocre_context_create_container(context, "filesystem.wasm", "wamr/wasip1", NULL, false, &args); + TEST_ASSERT_NOT_NULL(container); + + /* Start the container */ + + TEST_ASSERT_EQUAL_INT(0, ocre_container_start(container)); + + /* Remove the container */ + + TEST_ASSERT_EQUAL_INT(0, ocre_context_remove_container(context, container)); +} + +void test_ocre_context_create_container_with_id_twice(void) +{ + /* Create a valid container */ + + struct ocre_container *container = + ocre_context_create_container(context, "hello-world.wasm", "wamr/wasip1", "test-container", false, NULL); + TEST_ASSERT_NOT_NULL(container); + + /* Another container with the same id should fail */ + + TEST_ASSERT_NULL( + ocre_context_create_container(context, "hello-world.wasm", "wamr/wasip1", "test-container", false, NULL)); + + /* Remove the container */ + + TEST_ASSERT_EQUAL_INT(0, ocre_context_remove_container(context, container)); +} + +void test_ocre_context_create_container_and_forget(void) +{ + /* Create a valid container */ + + struct ocre_container *container = + ocre_context_create_container(context, "hello-world.wasm", "wamr/wasip1", "should-not-leak", false, NULL); + TEST_ASSERT_NOT_NULL(container); + + /* Check container status */ + + TEST_ASSERT_EQUAL_INT(OCRE_CONTAINER_STATUS_CREATED, ocre_container_get_status(container)); + + /* Do not remove the container. When the context is destroyed, the container will be removed automatically */ +} + +void test_ocre_context_create_wait_remove(void) +{ + /* Create a valid container */ + + struct ocre_container *container = + ocre_context_create_container(context, "hello-world.wasm", "wamr/wasip1", NULL, false, NULL); + TEST_ASSERT_NOT_NULL(container); + + /* Check container status */ + + TEST_ASSERT_EQUAL_INT(OCRE_CONTAINER_STATUS_CREATED, ocre_container_get_status(container)); + + /* Start the container */ + + TEST_ASSERT_EQUAL_INT(0, ocre_container_start(container)); + + /* Wait for the container to finish */ + + int status; + TEST_ASSERT_EQUAL_INT(0, ocre_container_wait(container, &status)); + + /* Check container status */ + + TEST_ASSERT_EQUAL_INT(0, status); + + /* Remove the container */ + + TEST_ASSERT_EQUAL_INT(0, ocre_context_remove_container(context, container)); +} + +void test_ocre_context_create_no_ocre_api(void) +{ + /* Create a valid container but it won't work as we don't have ocre:api */ + + struct ocre_container *container = + ocre_context_create_container(context, "blinky.wasm", "wamr/wasip1", NULL, true, NULL); + TEST_ASSERT_NOT_NULL(container); + + /* Check container status */ + + TEST_ASSERT_EQUAL_INT(OCRE_CONTAINER_STATUS_CREATED, ocre_container_get_status(container)); + + /* Start the container */ + + TEST_ASSERT_EQUAL_INT(0, ocre_container_start(container)); + + /* Wait for the container to finish */ + + int status; + TEST_ASSERT_EQUAL_INT(0, ocre_container_wait(container, &status)); + + /* Check container status */ + // TODO: Maybe this should be error? + + TEST_ASSERT_EQUAL_INT(OCRE_CONTAINER_STATUS_STOPPED, ocre_container_get_status(container)); + + /* Check return status */ + + TEST_ASSERT_EQUAL_INT(-1, status); + + /* Cannot kill stopped container */ + + TEST_ASSERT_EQUAL_INT(-1, ocre_container_kill(container)); + + /* Remove the container */ + + TEST_ASSERT_EQUAL_INT(0, ocre_context_remove_container(context, container)); +} + +void test_ocre_context_create_kill_wait_remove(void) +{ + const struct ocre_container_args args = { + .capabilities = + (const char *[]){ + "ocre:api", + NULL, + }, + }; + + /* Create a valid container and have ocre:api*/ + + struct ocre_container *container = + ocre_context_create_container(context, "blinky.wasm", "wamr/wasip1", NULL, true, &args); + TEST_ASSERT_NOT_NULL(container); + + /* Check container status */ + + TEST_ASSERT_EQUAL_INT(OCRE_CONTAINER_STATUS_CREATED, ocre_container_get_status(container)); + + /* Start the container */ + + TEST_ASSERT_EQUAL_INT(0, ocre_container_start(container)); + + /* Check container status */ + TEST_ASSERT_EQUAL_INT(OCRE_CONTAINER_STATUS_RUNNING, ocre_container_get_status(container)); + + /* Kill the container */ + + TEST_ASSERT_EQUAL_INT(0, ocre_container_kill(container)); + + /* Wait for the container to finish */ + + int status; + TEST_ASSERT_EQUAL_INT(0, ocre_container_wait(container, &status)); + + /* Check container status */ + + TEST_ASSERT_EQUAL_INT(OCRE_CONTAINER_STATUS_STOPPED, ocre_container_get_status(container)); + + /* Check return status */ + // TODO: Maybe this should not be 0 + + TEST_ASSERT_EQUAL_INT(0, status); + + /* Remove the container */ + + TEST_ASSERT_EQUAL_INT(0, ocre_context_remove_container(context, container)); +} + +void test_ocre_context_get_container_count_null(void) +{ + /* There must be zero containers */ + + TEST_ASSERT_EQUAL_INT(-1, ocre_context_get_container_count(NULL)); +} + +void test_ocre_context_get_container_count(void) +{ + /* There must be zero containers */ + + TEST_ASSERT_EQUAL_INT(0, ocre_context_get_container_count(context)); + + /* Create a valid container */ + + struct ocre_container *container1 = + ocre_context_create_container(context, "hello-world.wasm", "wamr/wasip1", NULL, false, NULL); + TEST_ASSERT_NOT_NULL(container1); + + /* There must be one container */ + + TEST_ASSERT_EQUAL_INT(1, ocre_context_get_container_count(context)); + + /* Create another valid container */ + + struct ocre_container *container2 = + ocre_context_create_container(context, "hello-world.wasm", "wamr/wasip1", NULL, false, NULL); + TEST_ASSERT_NOT_NULL(container2); + + /* There must be two containers */ + + TEST_ASSERT_EQUAL_INT(2, ocre_context_get_container_count(context)); + + /* Remove the container */ + + TEST_ASSERT_EQUAL_INT(0, ocre_context_remove_container(context, container1)); + + /* There must be one container */ + + TEST_ASSERT_EQUAL_INT(1, ocre_context_get_container_count(context)); + + /* Remove the container */ + + TEST_ASSERT_EQUAL_INT(0, ocre_context_remove_container(context, container2)); + + /* There must be zero containers */ + + TEST_ASSERT_EQUAL_INT(0, ocre_context_get_container_count(context)); +} + +void test_ocre_context_get_container_by_id_error(void) +{ + TEST_ASSERT_NULL(ocre_context_get_container_by_id(context, "bad_id")); + TEST_ASSERT_NULL(ocre_context_get_container_by_id(context, NULL)); + TEST_ASSERT_NULL(ocre_context_get_container_by_id(NULL, "bad_id")); + TEST_ASSERT_NULL(ocre_context_get_container_by_id(NULL, NULL)); +} + +void test_ocre_context_get_container_by_id_ok(void) +{ + /* Create a valid container */ + + struct ocre_container *container = + ocre_context_create_container(context, "hello-world.wasm", "wamr/wasip1", "my-id", false, NULL); + TEST_ASSERT_NOT_NULL(container); + + /* Get container by ID */ + + TEST_ASSERT_EQUAL_PTR(container, ocre_context_get_container_by_id(context, "my-id")); + + /* Remove the container */ + + TEST_ASSERT_EQUAL_INT(0, ocre_context_remove_container(context, container)); +} + +void test_ocre_context_get_containers_err(void) +{ + struct ocre_container *containers[1]; + TEST_ASSERT_EQUAL_INT(-1, ocre_context_get_containers(context, NULL, 0)); + TEST_ASSERT_EQUAL_INT(-1, ocre_context_get_containers(context, NULL, 1)); + TEST_ASSERT_EQUAL_INT(-1, ocre_context_get_containers(NULL, NULL, 0)); + TEST_ASSERT_EQUAL_INT(-1, ocre_context_get_containers(NULL, NULL, 1)); + TEST_ASSERT_EQUAL_INT(-1, ocre_context_get_containers(NULL, containers, 0)); + TEST_ASSERT_EQUAL_INT(-1, ocre_context_get_containers(NULL, containers, 1)); +} + +void test_ocre_context_get_containers_zero(void) +{ + struct ocre_container *containers[1]; + TEST_ASSERT_EQUAL_INT(0, ocre_context_get_containers(context, containers, 0)); + TEST_ASSERT_EQUAL_INT(0, ocre_context_get_containers(context, containers, 1)); +} + +void test_ocre_context_get_containers_ok(void) +{ + struct ocre_container *containers[2]; + + /* Create a valid container */ + + struct ocre_container *container1 = + ocre_context_create_container(context, "hello-world.wasm", "wamr/wasip1", NULL, false, NULL); + TEST_ASSERT_NOT_NULL(container1); + + /* There must be one container */ + + TEST_ASSERT_EQUAL_INT(1, ocre_context_get_containers(context, containers, 2)); + + /* Create another valid container */ + + struct ocre_container *container2 = + ocre_context_create_container(context, "hello-world.wasm", "wamr/wasip1", NULL, false, NULL); + TEST_ASSERT_NOT_NULL(container2); + + /* There must be two containers */ + + TEST_ASSERT_EQUAL_INT(2, ocre_context_get_containers(context, containers, 2)); + + /* We request only one */ + + TEST_ASSERT_EQUAL_INT(1, ocre_context_get_containers(context, containers, 1)); + + /* We request zero */ + + TEST_ASSERT_EQUAL_INT(0, ocre_context_get_containers(context, containers, 0)); + + /* Remove the container */ + + TEST_ASSERT_EQUAL_INT(0, ocre_context_remove_container(context, container1)); + + /* There must be one container */ + + TEST_ASSERT_EQUAL_INT(1, ocre_context_get_containers(context, containers, 2)); + + /* Remove the container */ + + TEST_ASSERT_EQUAL_INT(0, ocre_context_remove_container(context, container2)); + + /* There must be one container */ + + TEST_ASSERT_EQUAL_INT(0, ocre_context_get_containers(context, containers, 2)); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_ocre_context_initialized); + RUN_TEST(test_ocre_context_get_working_directory_ok); + RUN_TEST(test_ocre_context_get_working_directory_err); + RUN_TEST(test_ocre_context_create_container_null_context); + RUN_TEST(test_ocre_context_create_container_bad_ids); + RUN_TEST(test_ocre_context_create_container_bad_runtimes); + RUN_TEST(test_ocre_context_create_container_bad_mounts); + RUN_TEST(test_ocre_context_remove_bad_container); + RUN_TEST(test_ocre_context_remove_bad_context); + RUN_TEST(test_ocre_context_create_container_ok); + RUN_TEST(test_ocre_context_create_container_null_runtime_ok); + RUN_TEST(test_ocre_context_create_container_with_id_ok); + RUN_TEST(test_ocre_context_create_container_with_id_twice); + RUN_TEST(test_ocre_context_create_container_and_forget); + RUN_TEST(test_ocre_context_create_wait_remove); + RUN_TEST(test_ocre_context_create_no_ocre_api); + RUN_TEST(test_ocre_context_create_kill_wait_remove); + RUN_TEST(test_ocre_context_create_start_container_filesystem); + RUN_TEST(test_ocre_context_get_container_count); + RUN_TEST(test_ocre_context_get_container_count_null); + RUN_TEST(test_ocre_context_get_container_by_id_error); + RUN_TEST(test_ocre_context_get_container_by_id_ok); + RUN_TEST(test_ocre_context_get_containers_err); + RUN_TEST(test_ocre_context_get_containers_zero); + RUN_TEST(test_ocre_context_get_containers_ok); + return UNITY_END(); +} diff --git a/tests/system/lib.c b/tests/system/lib.c new file mode 100644 index 00000000..3245acf9 --- /dev/null +++ b/tests/system/lib.c @@ -0,0 +1,141 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include + +void setUp(void) +{ +} + +void tearDown(void) +{ +} + +void test_ocre_build_config(void) +{ + TEST_ASSERT_NOT_NULL(ocre_build_configuration.version); + TEST_ASSERT_NOT_NULL(ocre_build_configuration.commit_id); + TEST_ASSERT_NOT_NULL(ocre_build_configuration.build_info); + TEST_ASSERT_NOT_NULL(ocre_build_configuration.build_date); +} + +void test_ocre_initialize_null(void) +{ + /* Initialize ocre should work */ + + TEST_ASSERT_EQUAL_INT(0, ocre_initialize(NULL)); + + ocre_deinitialize(); +} + +void test_ocre_initialize_arr_null(void) +{ + /* Initialize ocre with a NULL array should work */ + + TEST_ASSERT_EQUAL_INT(0, ocre_initialize((const struct ocre_runtime_vtable *const[]){NULL})); + + ocre_deinitialize(); +} + +static int dummy_runtime_init_ok(void) +{ + return 0; +} + +const struct ocre_runtime_vtable test_runtime_ok = { + .runtime_name = "test", + .init = dummy_runtime_init_ok, +}; + +void test_ocre_initialize_vtable_init_ok(void) +{ + /* Initialize ocre with a good vtable should call init on the runtime */ + + TEST_ASSERT_EQUAL_INT(0, ocre_initialize((const struct ocre_runtime_vtable *const[]){ + &test_runtime_ok, + NULL, + })); + + ocre_deinitialize(); +} + +static int dummy_runtime_init_err(void) +{ + return -1; +} + +const struct ocre_runtime_vtable test_runtime_err = { + .runtime_name = "test", + .init = dummy_runtime_init_err, +}; + +void test_ocre_initialize_vtable_init_err(void) +{ + /* Initialize ocre with a good vtable should call init on the runtime but it fails */ + + TEST_ASSERT_NOT_EQUAL_INT(0, ocre_initialize((const struct ocre_runtime_vtable *const[]){ + &test_runtime_err, + NULL, + })); +} + +void test_ocre_initialize_duplicate(void) +{ + /* Try to initialize ocre with two runtimes with the same name should fail */ + + TEST_ASSERT_NOT_EQUAL_INT(0, ocre_initialize((const struct ocre_runtime_vtable *const[]){ + &test_runtime_ok, + &test_runtime_ok, + NULL, + })); +} + +static int dummy_runtime_init_func(void) +{ + /* We just deinit here and pass the test */ + + ocre_deinitialize(); + + TEST_PASS(); + + return 0; +} + +const struct ocre_runtime_vtable dummy_runtime = { + .runtime_name = "dummy", + .init = dummy_runtime_init_func, +}; + +void test_ocre_initialize_init_called(void) +{ + /* Initialize ocre with two runtimes with the same name should fail */ + + ocre_initialize((const struct ocre_runtime_vtable *const[]){ + &dummy_runtime, + NULL, + }); + + /* If we reach here, we failed to run the init function */ + + TEST_FAIL(); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_ocre_build_config); + RUN_TEST(test_ocre_initialize_null); + RUN_TEST(test_ocre_initialize_arr_null); + RUN_TEST(test_ocre_initialize_vtable_init_ok); + RUN_TEST(test_ocre_initialize_vtable_init_err); + RUN_TEST(test_ocre_initialize_duplicate); + RUN_TEST(test_ocre_initialize_init_called); + return UNITY_END(); +} diff --git a/tests/system/ocre.c b/tests/system/ocre.c new file mode 100644 index 00000000..3e57b6d3 --- /dev/null +++ b/tests/system/ocre.c @@ -0,0 +1,109 @@ +/** + * @copyright Copyright (c) contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include + +void setUp(void) +{ + ocre_initialize(NULL); +} + +void tearDown(void) +{ + ocre_deinitialize(); +} + +void test_ocre_create_default_context(void) +{ + /* Creating the main context should work */ + + struct ocre_context *main_ctx = ocre_create_context(NULL); + TEST_ASSERT_NOT_NULL(main_ctx); + + /* We can also destroy the main_ctx */ + + TEST_ASSERT_EQUAL_INT(0, ocre_destroy_context(main_ctx)); +} + +void test_ocre_create_provided_context(void) +{ + /* Creating the other context should work */ + + struct ocre_context *other_ctx = ocre_create_context("othercontext"); + TEST_ASSERT_NOT_NULL(other_ctx); + + /* We can also destroy the other context */ + + TEST_ASSERT_EQUAL_INT(0, ocre_destroy_context(other_ctx)); +} + +void test_ocre_create_two_contexts(void) +{ + /* Creating the main context should work */ + + struct ocre_context *main_ctx = ocre_create_context(NULL); + TEST_ASSERT_NOT_NULL(main_ctx); + + /* Creating the other context should work */ + + struct ocre_context *other_ctx = ocre_create_context("othercontext"); + TEST_ASSERT_NOT_NULL(other_ctx); + + /* We can also destroy the main_ctx */ + + TEST_ASSERT_EQUAL_INT(0, ocre_destroy_context(main_ctx)); + + /* We can also destroy the other context */ + + TEST_ASSERT_EQUAL_INT(0, ocre_destroy_context(other_ctx)); +} + +void test_ocre_create_context_twice(void) +{ + /* Creating the context once should work */ + + struct ocre_context *main_ctx = ocre_create_context(NULL); + TEST_ASSERT_NOT_NULL(main_ctx); + + /* Trying to create another context with the same workdir should fail */ + + TEST_ASSERT_NULL(ocre_create_context(NULL)); + + /* We can also destroy the main_ctx */ + + TEST_ASSERT_EQUAL_INT(0, ocre_destroy_context(main_ctx)); +} + +void test_ocre_create_context_no_cleanup(void) +{ + /* Creating the context once should work */ + + struct ocre_context *main_ctx = ocre_create_context(NULL); + TEST_ASSERT_NOT_NULL(main_ctx); + + /* Do not cleanup the context. ocre_deinitialize() should do it for us */ +} + +void test_ocre_destroy_null_context(void) +{ + TEST_ASSERT_NOT_EQUAL_INT(0, ocre_destroy_context(NULL)); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_ocre_create_default_context); + RUN_TEST(test_ocre_create_provided_context); + RUN_TEST(test_ocre_create_two_contexts); + RUN_TEST(test_ocre_create_context_twice); + RUN_TEST(test_ocre_create_context_no_cleanup); + RUN_TEST(test_ocre_destroy_null_context); + return UNITY_END(); +} diff --git a/tests/system/posix/CMakeLists.txt b/tests/system/posix/CMakeLists.txt new file mode 100644 index 00000000..6c3aaf54 --- /dev/null +++ b/tests/system/posix/CMakeLists.txt @@ -0,0 +1,72 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +set(CMAKE_C_COMPILER /usr/bin/clang) + +list(APPEND OCRE_SDK_PRELOADED_IMAGES + "return0.wasm" + "return1.wasm" + "sleep_5_return_0.wasm" +) + +if (SANITIZER) + string(APPEND CMAKE_C_FLAGS + "-fsanitize=address" +) +endif() + +project(OcreSystemTestPosix) + +add_subdirectory(../../.. ocre) + +add_library(Unity STATIC + ../../Unity/src/unity.c +) + +target_include_directories(Unity PUBLIC + ../../Unity/src +) + +list(APPEND OCRE_TESTS + lib + ocre + context + container +) + +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/src/ocre/var/lib/ocre/images) +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/src/ocre/var/lib/ocre/containers) + +foreach(test ${OCRE_TESTS}) + add_executable(test_${test} + ../${test}.c + ) + + target_link_libraries(test_${test} + OcreCore + Unity + ) + + add_custom_command( + OUTPUT test_${test}.log + COMMAND LLVM_PROFILE_FILE=test_${test}.profraw ./test_${test} > test_${test}.log + COMMAND rm -f test_${test}.testpass test_${test}.testfail + COMMAND sh -c "[ \"$(tail -1 test_${test}.log)\" = \"OK\" ] && cp test_${test}.log test_${test}.testpass || cp test_${test}.log test_${test}.testfail" + DEPENDS + test_${test} + VERBATIM + ) +endforeach() + +add_custom_target(run-systests + COMMAND python ${CMAKE_CURRENT_LIST_DIR}/../../Unity/auto/unity_test_summary.py ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS + test_lib.log + test_ocre.log + test_context.log + test_container.log +) diff --git a/tests/README.md b/tests_hw/README.md similarity index 100% rename from tests/README.md rename to tests_hw/README.md diff --git a/tests/beginTests.sh b/tests_hw/beginTests.sh similarity index 100% rename from tests/beginTests.sh rename to tests_hw/beginTests.sh diff --git a/tests/groups/flashValidation/clean.sh b/tests_hw/groups/flashValidation/clean.sh similarity index 100% rename from tests/groups/flashValidation/clean.sh rename to tests_hw/groups/flashValidation/clean.sh diff --git a/tests/groups/flashValidation/config.json b/tests_hw/groups/flashValidation/config.json similarity index 100% rename from tests/groups/flashValidation/config.json rename to tests_hw/groups/flashValidation/config.json diff --git a/tests/groups/flashValidation/flash_validation_hello_world.py b/tests_hw/groups/flashValidation/flash_validation_hello_world.py similarity index 100% rename from tests/groups/flashValidation/flash_validation_hello_world.py rename to tests_hw/groups/flashValidation/flash_validation_hello_world.py diff --git a/tests/groups/flashValidation/setup.sh b/tests_hw/groups/flashValidation/setup.sh similarity index 100% rename from tests/groups/flashValidation/setup.sh rename to tests_hw/groups/flashValidation/setup.sh diff --git a/tools/automsg b/tools/automsg deleted file mode 100644 index a54d3d01..00000000 --- a/tools/automsg +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import yaml - -ocre_msg_file_template = """\ - -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - * - * @file messages.h - * - * @details This file contains all message types that components may send to one another. - * DO NOT EDIT THIS FILE. The file is auto-generated at build-time. - * - * How to add messages: - * 1. Define message types in the component, for example, components/your-component/message_types.h - * 2. Create a .yaml definition that maps your message type to a message name - * 3. Build! You can now use the message types in the component implementation - */ - -#ifndef OCRE_MESSAGES_H_G -#define OCRE_MESSAGES_H_G - -#include - -#include -{includes} - -struct base_msg {{ - int msg_id; - int *from; - struct base msg; -}}; - -{message_wrappers} - -{ocre_message} - -#endif""" - -component_message_type_includes_template = """\ -#include -""" - -msg_wrapper_template = """ -struct {message_name} {{ - int msg_id; - int *from; - struct {message_type} msg; -}}; -""" - -ocre_message = """\ -struct ocre_message {{ - uint32_t event; - uint32_t containerId; - union {{ -{message_unions} - }} components; -}};""" - -msg_union_template = """\ - union {{ -{messages} - struct base_msg base_msg; - }} {component}; -""" - -msg_union_member_template = """\ - struct {message_name} {message_name}; -""" - -def automsg(infiles, outfile): - wrapped_messages = set() - unioned_component = set() - component_message_type_includes = set() - - for filepath in infiles: - with open(filepath, 'r') as f: - messages = yaml.safe_load(f) - - component = messages['component'] - unioned_messages = set() - - component_message_type_includes.add(component_message_type_includes_template.format(component=component)) - - for event in messages['events']: - name = "{}_msg".format(event['type']) - type = event['type'] - - wrapped_messages.add(msg_wrapper_template.format(message_name=name, message_type=type)) - unioned_messages.add(msg_union_member_template.format(message_name=name)) - unioned_component.add(msg_union_template.format(component=component, messages=''.join(unioned_messages).strip('\n'))) - - ocre_msg = ocre_message.format(message_unions=''.join(unioned_component).strip('\n')) - - ocre_msg_file = ocre_msg_file_template.format( - includes=''.join(component_message_type_includes).strip('\n'), - message_wrappers=''.join(wrapped_messages).strip('\n'), - ocre_message=ocre_msg) - - with open(outfile, 'w') as f: - f.write(ocre_msg_file) - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument("infiles", nargs='+') - parser.add_argument("outfile", type=str) - args = parser.parse_args() - - automsg(args.infiles, args.outfile) - \ No newline at end of file diff --git a/wasm-micro-runtime b/wasm-micro-runtime index 60253bed..c065004e 160000 --- a/wasm-micro-runtime +++ b/wasm-micro-runtime @@ -1 +1 @@ -Subproject commit 60253bedbbbc16ff586381f3cd142f639ce23d94 +Subproject commit c065004e8ca5228c3143c1fc93b78ea25135d6eb diff --git a/west.yml b/west.yml index 3e8d71a1..a746f7fa 100644 --- a/west.yml +++ b/west.yml @@ -1,20 +1,22 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + manifest: projects: - name: zephyr - revision: staging - url: https://github.com/project-ocre/zephyr + revision: v4.3.0 + url: https://github.com/zephyrproject-rtos/zephyr west-commands: scripts/west-commands.yml import: name-allowlist: - littlefs - picolibc - - mcuboot - - zcbor - hal_stm32 - hal_st - - mbedtls - - cmsis_6 - hal_nordic - + - hal_rpi_pico + - cmsis_6 self: - path: application + path: ocre-runtime diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt new file mode 100644 index 00000000..cbadb2e7 --- /dev/null +++ b/zephyr/CMakeLists.txt @@ -0,0 +1,31 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +# This file allows OCRE to be included as a Zephyr module. + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +set (WAMR_BUILD_PLATFORM "zephyr") + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_C_STANDARD 99) +set(CMAKE_CXX_STANDARD 17) + +include (wamr.cmake) + +add_subdirectory(../src/ocre "ocre-core") +add_subdirectory(../src/runtime "ocre-runtime") +add_subdirectory(../src/runtime/wamr-wasip1 "ocre-runtime-wamr-wasip1") +add_subdirectory(../src/platform/zephyr "ocre-zephyr") +if(CONFIG_OCRE_SHELL) + message(STATUS "Shell is enabled") + add_subdirectory(../src/shell "ocre-shell") +endif() + +if(CONFIG_OCRE_STORAGE_PARTITION) + include(storage_partition.cmake) +endif() diff --git a/zephyr/Kconfig b/zephyr/Kconfig new file mode 100644 index 00000000..c45fbf0c --- /dev/null +++ b/zephyr/Kconfig @@ -0,0 +1,280 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +config OCRE + bool "Ocre Runtime" + default n + select THREAD_STACK_INFO + select DYNAMIC_THREAD + select DYNAMIC_THREAD_ALLOC + select POSIX_API + select FLASH + select FLASH_MAP + select FILE_SYSTEM + select FILE_SYSTEM_LITTLEFS + select NETWORKING + help + Enable the Project Ocre runtime. + +if OCRE + +config OCRE_DEFAULT_WORKING_DIRECTORY + string "Default OCRE working directory" + default "/lfs/ocre" + help + Default working directory for OCRE. + +# Default minimum system heap required by OCRE +config HEAP_MEM_POOL_ADD_SIZE_OCRE + def_int 32768 # 32KB + +comment "Storage partition" + +config OCRE_STORAGE_PARTITION + bool "Enable building of storage partition binary" + default $(dt_nodelabel_exists,storage_partition) + +if OCRE_STORAGE_PARTITION + + config OCRE_STORAGE_PARTITION_ADDR + hex "User data partition address" + default "$(add_hex,$(dt_node_reg_addr_hex,$(dt_node_parent,$(dt_node_parent,$(dt_nodelabel_path,storage_partition)))),$(dt_nodelabel_reg_addr_hex,storage_partition))" + help + littlefs user data partition address. + +config OCRE_STORAGE_PARTITION_SIZE + int "User data partition size" + default $(dt_nodelabel_reg_size_int,storage_partition) + help + littlefs user data partition size in bytes. + +config OCRE_STORAGE_PARTITION_BLOCK_SIZE + hex "User data partition block size" + default "0x1000" if !$(dt_node_has_prop,$(dt_node_parent,$(dt_node_parent,$(dt_nodelabel_path,storage_partition))),erase-block-size) + default "$(dt_node_int_prop_hex,$(dt_node_parent,$(dt_node_parent,$(dt_nodelabel_path,storage_partition))),erase-block-size)" + help + littlefs user data partition size in bytes. + +config OCRE_MERGE_HEX + bool "Merge user_data partition in merged.hex" + default n + help + Enable to include littlefs formatted user_data partition + to be merged into merged.hex. + +endif + +comment "WAMR options" + +config OCRE_WAMR_INTERPRETER + bool "Enable WASM interpreter" + default y + help + Enable interpreting WASM code. + +config OCRE_WAMR_AOT + bool "Enable AOT execution" + default n + help + Enable execution of ahead of time compiled code. + +comment "Container features" + +config OCRE_NETWORKING + bool "Enable container networking support" + default n + select NET_IPV4 + select NET_IPV6 + select NET_UDP + select NET_TCP + select NET_SOCKETS + help + Enable networking support for containers. + +config OCRE_FILESYSTEM + bool "Enable Container file system support" + default n + help + Enable support for containers to access the filesystem + +config OCRE_TIMER + bool "Enable OCRE Timer Driver" + select OCRE_CONTAINER_MESSAGING + default n + help + Enable the OCRE Timer driver that provides a portable API layer + for Timer operations across different hardware platforms. + +if OCRE_TIMER +config OCRE_MAX_TIMERS + int "Maximum number of timers" + default 8 + help + Defines the maximum number of timers available in the system. +endif # OCRE_TIMER + +config OCRE_CONTAINER_MESSAGING + bool "Enable OCRE Container Messaging support" + default n + help + Enable support for OCRE Container Messaging + +if OCRE_CONTAINER_MESSAGING + +config OCRE_MESSAGING_MAX_SUBSCRIPTIONS + int "Number of maximum subscriptions for Container Messaging" + default 16 + help + Number of maximum subscriptions for Container Messaging + +endif # OCRE_CONTAINER_MESSAGING + +config OCRE_SHARED_HEAP + bool "Enable container shared heap support" + default n + help + Enable shared heap support for containers. + +if OCRE_SHARED_HEAP + +choice OCRE_SHARED_HEAP_MODE + prompt "Shared heap mode" + default OCRE_SHARED_HEAP_BUF_VIRTUAL + help + Select the shared heap memory mode: + - Physical: Map physical hardware registers (e.g., GPIO) to WASM address space + - Virtual: Allocate shared heap from regular RAM + +config OCRE_SHARED_HEAP_BUF_PHYSICAL + bool "Physical (hardware register mapping)" + help + Enable physical memory mapping for hardware access. + Maps physical hardware registers (like GPIO at 0x42020000) to WASM address space. + Use this when containers need direct access to hardware peripherals. + +config OCRE_SHARED_HEAP_BUF_VIRTUAL + bool "Virtual (RAM allocation)" + help + Enable virtual shared heap allocated from regular RAM. + Use this for normal inter-module communication without + direct hardware access. + +endchoice + +if OCRE_SHARED_HEAP_BUF_PHYSICAL + +config OCRE_SHARED_HEAP_BUF_ADDRESS + hex "Shared heap buffer address" + default 0x00 + help + Shared heap buffer address. Memory address of hardware registers. + +endif # OCRE_SHARED_HEAP_BUF_PHYSICAL + +config OCRE_SHARED_HEAP_BUF_SIZE + int "Shared heap buffer size in bytes" + default 65536 + help + Size of the pre-allocated buffer for the shared heap. + This memory is shared between WebAssembly modules. + +endif # OCRE_SHARED_HEAP + +comment "Control Interface" + +config OCRE_SHELL + bool "Enable OCRE Shell" + select SHELL + select SHELL_GETOPT + select HTTP_CLIENT + select DNS_RESOLVER + default n + help + Enable the OCRE Shell for dynamic configuration management. + +comment "Logging" + +module = OCRE +module-str = OCRE +source "subsys/logging/Kconfig.template.log_config" + +config OCRE_WAMR_LOG_LEVEL + int "Log level for WAMR" + default 2 + range 0 4 + help + Set the log level for the WAMR runtime: + 0: Fatal + 1: Error + 2: Warning + 3: Debug + 4: Verbose + +comment "Container hardware drivers" + +config OCRE_GPIO + bool "Enable OCRE GPIO Driver" + default n + help + Enable the OCRE GPIO driver that provides a portable API layer + for GPIO operations across different hardware platforms. + +if OCRE_GPIO +config OCRE_GPIO_MAX_PINS + int "Maximum number of GPIO pins" + default 32 + help + Maximum number of GPIO pins that can be managed by the OCRE GPIO driver. + +config OCRE_GPIO_MAX_PORTS + int "Maximum number of GPIO ports" + default 4 + help + Maximum number of GPIO port devices that can be used by the OCRE GPIO driver. + +config OCRE_GPIO_PINS_PER_PORT + int "Number of pins per GPIO port" + default 32 + help + Number of pins available on each GPIO port. This is used to map the + logical pin numbers to physical port and pin numbers. + +endif # OCRE_GPIO + +config OCRE_SENSORS + bool "Enable OCRE Sensors support" + default n + depends on SENSOR + help + Enable support for OCRE sensors + +if OCRE_SENSORS +config OCRE_MAX_SENSORS + int "Maximum number of sensors" + default 10 + help + Defines the maximum number of sensors that can be handled. + +config OCRE_MAX_CHANNELS_PER_SENSOR + int "Maximum number of channels per sensor" + default 5 + help + Defines the maximum number of channels that each sensor can have. + +config OCRE_RNG_SENSOR + bool "RNG Sensor" + default n + help + Enable support for the custom RNG sensor. + +config OCRE_IMU_SENSOR + bool "IMU Sensor" + default n + help + Enable support for the custom IMU sensor. + +endif # OCRE_SENSORS + +endif # OCRE diff --git a/zephyr/module.yml b/zephyr/module.yml index fabaab48..a4b5b6cc 100644 --- a/zephyr/module.yml +++ b/zephyr/module.yml @@ -1,5 +1,6 @@ -name: project-ocre - -build: - cmake-ext: True +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 +name: ocre diff --git a/zephyr/storage_partition.cmake b/zephyr/storage_partition.cmake new file mode 100644 index 00000000..3a1a2759 --- /dev/null +++ b/zephyr/storage_partition.cmake @@ -0,0 +1,36 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +add_custom_command(OUTPUT user_data.bin + COMMAND littlefs-python create + --block-size ${CONFIG_OCRE_STORAGE_PARTITION_BLOCK_SIZE} + --fs-size ${CONFIG_OCRE_STORAGE_PARTITION_SIZE} + ocre-core/var/lib user_data.bin + DEPENDS ${OCRE_SDK_PRELOADED_IMAGES} + COMMENT "Generating user_data.bin" + VERBATIM +) + +add_custom_command(OUTPUT user_data.hex + COMMAND bin2hex.py + --offset ${CONFIG_OCRE_STORAGE_PARTITION_ADDR} + user_data.bin user_data.hex + DEPENDS user_data.bin + COMMENT "Generating user_data.hex" + VERBATIM +) + +add_custom_target(user_data_partition ALL + DEPENDS user_data.hex + COMMENT "Generating user data partition" + VERBATIM +) + +if (CONFIG_OCRE_MERGE_HEX) + set_property(GLOBAL APPEND PROPERTY HEX_FILES_TO_MERGE "${CMAKE_CURRENT_BINARY_DIR}/user_data.hex") + set_property(GLOBAL APPEND PROPERTY HEX_FILES_TO_MERGE "zephyr.hex") +endif() diff --git a/zephyr/wamr.cmake b/zephyr/wamr.cmake new file mode 100644 index 00000000..aa311b01 --- /dev/null +++ b/zephyr/wamr.cmake @@ -0,0 +1,86 @@ +# @copyright Copyright (c) contributors to Project Ocre, +# which has been established as Project Ocre a Series of LF Projects, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +# Determine the ISA of the target and set appropriately + +if (DEFINED CONFIG_ISA_THUMB2) + set (TARGET_ISA THUMBV8M) +elseif (DEFINED CONFIG_CPU_CORTEX_M) + set(TARGET_ISA THUMB) +elseif (DEFINED CONFIG_ARM64) + set(TARGET_ISA AARCH64) +elseif (DEFINED CONFIG_ISA_ARM) + set(TARGET_ISA ARM) +elseif (DEFINED CONFIG_X86_64) + set(TARGET_ISA X86_64) +elseif (DEFINED CONFIG_X86) + set(TARGET_ISA X86_32) +elseif (DEFINED CONFIG_XTENSA) + set(TARGET_ISA XTENSA) +elseif (DEFINED CONFIG_RISCV) + if (DEFINED CONFIG_64BIT) + set(TARGET_ISA RISCV64) + else() + set(TARGET_ISA RISCV32) + endif() +elseif (DEFINED CONFIG_ARCH_POSIX) + execute_process( + COMMAND uname -m + OUTPUT_VARIABLE UNAME_M + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if (UNAME_M MATCHES "^(arm64|aarch64)") + set (TARGET_ISA "AARCH64") + elseif (UNAME_M STREQUAL "riscv64") + set (TARGET_ISA "RISCV64") + elseif (UNAME_M STREQUAL "i686") + set (TARGET_ISA "X86_32") + elseif (UNAME_M STREQUAL "x86_64") + set (TARGET_ISA "X86_64") + else () + message(SEND_ERROR "Unsupported build target platform!") + endif () +else () + message(FATAL_ERROR "Unsupported ISA: ${CONFIG_ARCH}") +endif () +message("Selected target ISA: ${TARGET_ISA}") + +# WAMR Options +set(WAMR_BUILD_PLATFORM "zephyr") +set(WAMR_BUILD_TARGET ${TARGET_ISA}) +set(WAMR_BUILD_INTERP 1) +set(WAMR_BUILD_FAST_INTERP 0) +if (CONFIG_OCRE_WAMR_AOT) + set(WAMR_BUILD_AOT 1) +else() + set(WAMR_BUILD_AOT 0) +endif() +set(WAMR_BUILD_JIT 0) +set(WAMR_BUILD_LIBC_BUILTIN 0) +set(WAMR_BUILD_LIBC_WASI 1) +set(WAMR_BUILD_LIB_WASI_THREADS 1) +set(WAMR_BUILD_LIB_PTHREAD 0) +set(WAMR_BUILD_REF_TYPES 1) +set(WAMR_BUILD_SHARED_HEAP 1) +set(WASM_ENABLE_LOG 1) + +enable_language (ASM) + +set (WAMR_BUILD_PLATFORM "zephyr") + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../wasm-micro-runtime) + +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +add_library(vmlib) +target_sources(vmlib PRIVATE + ${WAMR_RUNTIME_LIB_SOURCE}) +target_link_libraries(vmlib zephyr_interface LITTLEFS) + +get_property(dirs DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES) +target_include_directories(vmlib PUBLIC ${dirs}) + +get_property(defs DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY COMPILE_DEFINITIONS) +target_compile_definitions(vmlib PRIVATE ${defs})