From 76c84be3df50c58b0ff791c0a3e590ad834d4cdf Mon Sep 17 00:00:00 2001 From: madelen-at-work Date: Wed, 20 Mar 2024 10:20:50 +0100 Subject: [PATCH 01/16] backdown SDK version to be LTS 10.12 compliant --- Dockerfile | 4 +- app/dockerdwrapperwithcompose.c | 2 +- app/manifest.json | 76 ++++++++++++++++----------------- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2857696..4abe160 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ ARG DOCKER_COMPOSE_VERSION=v2.18.1 ARG REPO=axisecp ARG ACAPARCH=armv7hf -ARG VERSION=1.10 +ARG VERSION=1.3 ARG UBUNTU_VERSION=22.04 ARG NATIVE_SDK=acap-native-sdk @@ -40,7 +40,7 @@ RUN git clone --depth 1 -b $PROCPS_VERSION 'https://gitlab.com/procps-ng/procps' ARG BUILD_CACHE=build.cache RUN echo ac_cv_func_realloc_0_nonnull=yes >$BUILD_CACHE \ - && echo ac_cv_func_malloc_0_nonnull=yes >>$BUILD_CACHE + && echo ac_cv_func_malloc_0_nonnull=yes >>$BUILD_CACHE RUN < Date: Wed, 20 Mar 2024 10:36:26 +0100 Subject: [PATCH 02/16] combined update of depandabot recomendations --- .../actions/docker-build-action/action.yml | 6 ++--- .github/actions/metadata-action/action.yml | 2 +- .github/workflows/cd.yml | 22 ++++++++-------- .github/workflows/lint.yml | 2 +- app/postinstallscript.sh | 11 ++++---- app/preuninstallscript.sh | 2 +- build.sh | 25 +++++++++---------- 7 files changed, 34 insertions(+), 36 deletions(-) diff --git a/.github/actions/docker-build-action/action.yml b/.github/actions/docker-build-action/action.yml index 79b389d..72a5e4b 100644 --- a/.github/actions/docker-build-action/action.yml +++ b/.github/actions/docker-build-action/action.yml @@ -53,12 +53,12 @@ runs: using: composite steps: - name: Set up Docker buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Set up QEMU if: ${{ inputs.use_qemu == 'true'}} - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - name: Build image - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: context: . push: false diff --git a/.github/actions/metadata-action/action.yml b/.github/actions/metadata-action/action.yml index 78e44d1..2138e8c 100644 --- a/.github/actions/metadata-action/action.yml +++ b/.github/actions/metadata-action/action.yml @@ -44,7 +44,7 @@ runs: steps: - name: Create metadata for docker image id: meta - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 with: images: ${{ inputs.repository }} # adds the suffix for all tags, even latest. diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 2606158..66adf4d 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -7,21 +7,21 @@ name: Sign and prerelease on: push: branches: - - 'main' + - "main" tags: # semver, e.g. 1.2.0 (does not match 0.1.2) - - '[1-9]+.[0-9]+.[0-9]+' + - "[1-9]+.[0-9]+.[0-9]+" # semver with prerelease info, e.g. 1.0.2-beta.1 or 1.2.3-rc.10 - - '[1-9]+.[0-9]+.[0-9]+-[a-z]+.[0-9]+' + - "[1-9]+.[0-9]+.[0-9]+-[a-z]+.[0-9]+" # do not match prerelease starting w/ 0, e.g. 1.0.2-beta.0 or 1.2.3-rc.01 - - '![1-9]+.[0-9]+.[0-9]+-[a-z]+.[0]*' + - "![1-9]+.[0-9]+.[0-9]+-[a-z]+.[0]*" # semver with date info, e.g. 1.0.2-20221125 - - '[1-9]+.[0-9]+.[0-9]+-[0-9]+' + - "[1-9]+.[0-9]+.[0-9]+-[0-9]+" # do not match date starting w/ 0, e.g. 1.0.2-01232023 - - '![1-9]+.[0-9]+.[0-9]+-[0]*' + - "![1-9]+.[0-9]+.[0-9]+-[0]*" pull_request: branches: - - 'main' + - "main" jobs: # Builds docker ACAP using the build.sh script, then signs the eap-file in @@ -43,8 +43,8 @@ jobs: uses: ./.github/actions/metadata-action with: suffix: -${{ matrix.arch }} - repository: 'docker-compose-acap' - get_version: 'true' + repository: "docker-compose-acap" + get_version: "true" - name: Update manifest file if: ( github.ref_type == 'tag') uses: ./.github/actions/update-acap-manifest-action @@ -91,7 +91,7 @@ jobs: echo "HTTP_RESPONSE is empty or not a valid integer: $HTTP_RESPONSE" fi - name: Upload artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ env.SIGNED_EAP_FILE }} path: build/${{ env.SIGNED_EAP_FILE }} @@ -123,7 +123,7 @@ jobs: id: vars run: echo "TAG=${GITHUB_REF#refs/*/}" >> ${GITHUB_ENV} - name: Create prerelease - uses: actions/github-script@v6 + uses: actions/github-script@v7 id: prerelease env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 365cd7d..667f2b2 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -16,7 +16,7 @@ jobs: fetch-depth: 0 - name: Lint codebase - uses: super-linter/super-linter/slim@v5 + uses: super-linter/super-linter/slim@v6 env: VALIDATE_ALL_CODEBASE: true DEFAULT_BRANCH: main diff --git a/app/postinstallscript.sh b/app/postinstallscript.sh index 5544f7f..6143f46 100644 --- a/app/postinstallscript.sh +++ b/app/postinstallscript.sh @@ -1,15 +1,14 @@ #!/bin/sh # Move the daemon.json file into localdata folder -if [ ! -e localdata/daemon.json ] -then - mv empty_daemon.json localdata/daemon.json +if [ ! -e localdata/daemon.json ]; then + mv empty_daemon.json localdata/daemon.json else - rm empty_daemon.json + rm empty_daemon.json fi # Make sure containerd is started before dockerd and set PATH -cat >> /etc/systemd/system/sdkdockerdwrapperwithcompose.service << EOF +cat >>/etc/systemd/system/sdkdockerdwrapperwithcompose.service < Date: Wed, 20 Mar 2024 11:09:19 +0100 Subject: [PATCH 03/16] Add CONTRIBUTING and .vscode --- .dockerignore | 1 + .vscode/extensions.json | 7 ++ .vscode/settings.json | 18 ++++ CONTRIBUTING.md | 232 ++++++++++++++++++++++++++++++++++++++++ README.md | 9 ++ 5 files changed, 267 insertions(+) create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 CONTRIBUTING.md diff --git a/.dockerignore b/.dockerignore index 820e358..b95d2cc 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,3 @@ build-* tmp +.vscode diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..e63e6f4 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "DavidAnson.vscode-markdownlint", + "editorconfig.editorconfig", + "streetsidesoftware.code-spell-checker" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..f57b11c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,18 @@ +{ + "C_Cpp.clang_format_style": "file", + "[markdown]": { + "editor.defaultFormatter": "DavidAnson.vscode-markdownlint", + "editor.formatOnSave": true, + "editor.formatOnPaste": true + }, + "markdown.extension.list.indentationSize": "inherit", + "markdown.extension.toc.levels": "1..3", + "cSpell.words": [ + "anyauth", + "Buildx", + "containerd", + "dockerdwrapperwithcompose", + "rootpasswd", + "VAPIX" + ] +} \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..f25481e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,232 @@ + +# Regarding contributions + +All types of contributions are encouraged and valued. See the [Table of contents](#table-of-contents) +for different ways to help and details about how this project handles them. Please make sure to read +the relevant section before making your contribution. It will make it a lot easier for us maintainers +and smooth out the experience for all involved. We look forward to your contributions. + +> And if you like the project, but just don't have time to contribute, that's fine. There are other +> easy ways to support the project and show your appreciation, which we would also be very happy about: +> +> - Star the project +> - Tweet about it +> - Refer this project in your project's readme +> - Mention the project at local meetups and tell your friends/colleagues + + +## Table of contents + +- [I have a question](#i-have-a-question) +- [I want to contribute](#i-want-to-contribute) + - [Reporting bugs](#reporting-bugs) + - [Before submitting a bug report](#before-submitting-a-bug-report) + - [How do I submit a good bug report?](#how-do-i-submit-a-good-bug-report) + - [Suggesting enhancements](#suggesting-enhancements) + - [Before Submitting an Enhancement](#before-submitting-an-enhancement) + - [How do I submit a good enhancement suggestion?](#how-do-i-submit-a-good-enhancement-suggestion) + - [Your first code contribution](#your-first-code-contribution) + - [Lint of codebase](#lint-of-codebase) + - [Linters in GitHub Action](#linters-in-github-action) + - [Run super-linter locally](#run-super-linter-locally) + - [Run super-linter interactively](#run-super-linter-interactively) + +## I have a question + +Before you ask a question, it is best to search for existing [issues][issues] that might help you. +In case you have found a suitable issue and still need clarification, you can write your question in +this issue. It is also advisable to search the internet for answers first. + +If you then still feel the need to ask a question and need clarification, please +follow the steps in [Reporting bugs](#reporting-bugs). + +## I want to contribute + +### Reporting bugs + +#### Before submitting a bug report + +A good bug report shouldn't leave others needing to chase you up for more information. Therefore, we +ask you to investigate carefully, collect information and describe the issue in detail in your report. +Please complete the following steps in advance to help us fix any potential bug as fast as possible: + +- Make sure that you are using the latest version. +- Determine if your bug is really a bug and not an error on your side e.g. using incompatible environment + components/versions. +- To see if other users have experienced (and potentially already solved) the same issue you are having, + check if there is not already a bug report existing for your bug or error in the [bug tracker][issues_bugs]. +- Also make sure to search the internet to see if users outside of the GitHub community have discussed + the issue. +- Collect information about the bug: + - Axis device model + - Axis device firmware version + - Stack trace + - OS and version (Windows, Linux, macOS, x86, ARM) + - Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what + seems relevant + - Possibly your input and the output + - Can you reliably reproduce the issue? And can you also reproduce it with older versions? + +#### How do I submit a good bug report? + +We use GitHub issues to track bugs and errors. If you run into an issue with the project: + +- Open an [issue][issues_new]. +- Explain the behavior you would expect and the actual behavior. +- Please provide as much context as possible and describe the *reproduction steps* that someone else + can follow to recreate the issue on their own. +- Provide the information you collected in the previous section. + +Once it's filed: + +- The project team will label the issue accordingly. +- A team member will try to reproduce the issue with your provided steps. If there are no reproduction + steps or no obvious way to reproduce the issue, the team will ask you for those steps. Bugs without + steps will not be addressed until they can be reproduced. +- If the team is able to reproduce the issue, it will be prioritized according to severity. + +### Suggesting enhancements + +This section guides you through submitting an enhancement suggestion, +**including completely new features and minor improvements to existing functionality**. +Following these guidelines will help maintainers and the community to understand your suggestion and +find related suggestions. + +#### Before Submitting an Enhancement + +- Make sure that you are using the latest version. +- Read the documentation carefully and find out if the functionality is already covered, maybe by an + individual configuration. +- Perform a [search][issues] to see if the enhancement has already been suggested. If it has, add a + comment to the existing issue instead of opening a new one. +- Find out whether your idea fits with the scope and aims of the project. Keep in mind that we want + features that will be useful to the majority of our users and not just a small subset. + +#### How do I submit a good enhancement suggestion? + +Enhancement suggestions are tracked as [GitHub issues][issues]. + +- Use a **clear and descriptive title** for the issue to identify the suggestion. +- Provide a **step-by-step description of the suggested enhancement** in as many details as possible. +- **Describe the current behavior** and **explain which behavior you expected to see instead** and why. + At this point you can also tell which alternatives do not work for you. +- You may want to **include screenshots and animated GIFs** which help you demonstrate the steps or + point out the part which the suggestion is related to. +- **Explain why this enhancement would be useful** to most users. You may also want to point out the + other projects that solved it better and which could serve as inspiration. + +### Your first code contribution + +Start by [forking the repository](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo), +i.e. copying the repository to your account to grant you write access. Continue with cloning the +forked repository to your local machine. + +```sh +git clone https://github.com//AxisCommunications/docker-compose-acap.git +``` + +Navigate into the cloned directory and create a new branch: + +```sh +cd docker-compose-acap +git switch -c +``` + +Update the code according to your requirements, and commit the changes using the +[conventional commits](https://www.conventionalcommits.org) message style: + +```sh +git commit -a -m 'Follow the conventional commit messages style to write this message' +``` + +Continue with pushing the local commits to GitHub: + +```sh +git push origin +``` + +Before opening a Pull Request (PR), please consider the following guidelines: + +- Please make sure that the code builds perfectly fine on your local system. +- Make sure that all linters pass, see [Lint of codebase](#lint-of-codebase) +- The PR will have to meet the code standard already available in the repository. +- Explanatory comments related to code functions are required. Please write code comments for a better + understanding of the code for other developers. +- Note that code changes or additions to the `.github` folder (or sub-folders) will not be accepted. + +And finally when you are satisfied with your changes, open a new PR. + +### Lint of codebase + +A set of different linters test the codebase and these must pass in order to get a pull request approved. + +#### Linters in GitHub Action + +When you create a pull request, a set of linters will run syntax and format checks on different file +types in GitHub actions by making use of a tool called [super-linter][super-linter]. If any of the +linters gives an error, this will be shown in the action connected to the pull request. + +In order to speed up development, it's possible to run linters as part of your local development environment. + +#### Run super-linter locally + +Since super-linter is using a Docker image in GitHub Actions, users of other editors may run it locally +to lint the codebase. For complete instructions and guidance, see super-linter page for [running locally][super-linter-local]. + +To run a number of linters on the codebase from command line: + +```sh +docker run --rm \ + -v $PWD:/tmp/lint \ + -e RUN_LOCAL=true \ + -e LINTER_RULES_PATH=/ \ + -e VALIDATE_BASH=true \ + -e VALIDATE_DOCKERFILE_HADOLINT=true \ + -e VALIDATE_MARKDOWN=true \ + -e VALIDATE_SHELL_SHFMT=true \ + -e VALIDATE_YAML=true \ + ghcr.io/super-linter/super-linter:slim-v6 +``` + +See [`.github/workflows/lint.yml`](.github/workflows/lint.yml) for the exact setup used by this project. + +#### Run super-linter interactively + +It might be more convenient to run super-linter interactively. Run container and enter command line: + +```sh +docker run --rm \ + -v $PWD:/tmp/lint \ + -w /tmp/lint \ + --entrypoint /bin/bash \ + -it ghcr.io/super-linter/super-linter:slim-v6 +``` + +Then from the container terminal, the following commands can lint the the code base for different +file types: + +```sh +# Lint Dockerfile files +hadolint $(find -type f -name "Dockerfile*") + +# Lint Markdown files +markdownlint . + +# Lint YAML files +yamllint . + +# Lint shell script files +shellcheck $(shfmt -f .) +shfmt -d . +``` + +To lint only a specific file, replace `.` or `$(COMMAND)` with the file path. + + +[issues]: https://github.com/AxisCommunications/docker-compose-acap/issues +[issues_new]: https://github.com/AxisCommunications/docker-compose-acap/issues/new +[issues_bugs]: https://github.com/AxisCommunications/docker-compose-acap/issues?q=label%3Abug +[super-linter]: https://github.com/super-linter/super-linter +[super-linter-local]: https://github.com/super-linter/super-linter/blob/main/docs/run-linter-locally.md + + diff --git a/README.md b/README.md index ad71ede..648ad3a 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ this is the recommended way to install this ACAP. > Meanwhile, the solution is to allow root to be able to install the Docker Compose ACAP. > > On the web page of the device: +> > 1. Go to the Apps page, toggle on `Allow root-privileged apps`. > 1. Go to System -> Account page, under SSH accounts toggle off `Restrict root access` to be able to send the TLS certificates. Make sure to set the password of the `root` SSH user. @@ -270,6 +271,14 @@ Go to your device web page above > Click on the tab **App** in the device GUI > Add **(+)** sign and browse to the newly built .eap-file > Click **Install** > Run the application by enabling the **Start** switch. +## Contributing + +Take a look at the [CONTRIBUTING.md](CONTRIBUTING.md) file. + +## License + +[Apache 2.0](LICENSE) + [all-releases]: https://github.com/AxisCommunications/docker-compose-acap/releases From a6bed0fb2c1d4c0bdd7eb02f30c3b45a17e9aa6c Mon Sep 17 00:00:00 2001 From: madelen-at-work Date: Wed, 20 Mar 2024 11:12:59 +0100 Subject: [PATCH 04/16] Add command line options to build.sh --- build.sh | 52 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/build.sh b/build.sh index 85e9522..ae21fa4 100755 --- a/build.sh +++ b/build.sh @@ -1,21 +1,51 @@ -#!/bin/sh -eu -case "${1:-}" in -armv7hf | aarch64) ;; -*) - # error - echo "Invalid argument '${1:-}', valid arguments are armv7hf or aarch64" +#!/bin/bash -eu + +imagetag="docker-acap-with-compose:1.0" + +function exit_with_message { + echo "$1" + echo + echo "Usage: $0 ARCH [ --plain ] [ --cache ] [ --image-tag IMAGETAG ]" + echo + echo "ARCH must be 'armv7hf' or 'aarch64'" + echo "--plain will simplify the Docker progress bar." + echo "--cache will enable Docker's caching mechanism." + echo "--image-tag sets the supplied tag of the image. Default is $imagetag." exit 1 - ;; +} + +arch="${1:-}" + +case "$arch" in +armv7hf | aarch64) ;; +*) exit_with_message "Invalid architecture '$arch'" ;; esac +shift + +progress_arg= +cache_arg=--no-cache -imagetag="${2:-docker-acap-with-compose:1.0}" +while (($#)); do + case "$1" in + --plain) progress_arg="--progress plain" ;; + --cache) cache_arg= ;; + --image-tag) + shift + imagetag="$1" + ;; + *) exit_with_message "Invalid argument '$1'" ;; + esac + shift +done # Build and copy out the acap -docker buildx build --build-arg ACAPARCH="$1" \ +# shellcheck disable=SC2086 +docker buildx build --build-arg ACAPARCH="$arch" \ --build-arg HTTP_PROXY="${HTTP_PROXY:-}" \ --build-arg HTTPS_PROXY="${HTTPS_PROXY:-}" \ --file Dockerfile \ - --no-cache \ + $progress_arg \ + $cache_arg \ --tag "$imagetag" . -docker cp "$(docker create "$imagetag")":/opt/app/ ./build-"$1" +docker cp "$(docker create "$imagetag")":/opt/app/ ./build-"$arch" From f6fa15b0a9a1de8554af054edaa720f521e13e1b Mon Sep 17 00:00:00 2001 From: madelen-at-work Date: Wed, 20 Mar 2024 11:32:05 +0100 Subject: [PATCH 05/16] refactor handling of parameter settings --- app/dockerdwrapperwithcompose.c | 344 ++++++++++++++++++++++---------- 1 file changed, 234 insertions(+), 110 deletions(-) diff --git a/app/dockerdwrapperwithcompose.c b/app/dockerdwrapperwithcompose.c index 523f1d9..aef1b94 100644 --- a/app/dockerdwrapperwithcompose.c +++ b/app/dockerdwrapperwithcompose.c @@ -48,6 +48,16 @@ static const char *sd_card_path = "/var/spool/storage/SD_DISK"; // True if the dockerd_exited_callback should restart dockerd static bool restart_dockerd = false; +// All ax_parameters the acap has +static const char *ax_parameters[] = {"IPCSocket", "SDCardSupport", "UseTLS"}; + +static const char *tls_cert_path = + "/usr/local/packages/dockerdwrapperwithcompose/"; + +static const char *tls_certs[] = {"ca.pem", + "server-cert.pem", + "server-key.pem"}; + /** * @brief Signals handling * @@ -226,13 +236,161 @@ setup_sdcard(void) return res == 0; } +/** + * @brief Gets and verifies the SDCardSupport selection + * + * @param use_sdcard_ret selection to be updated. + * @return True if successful, false otherwise. + */ +static gboolean +get_and_verify_sd_card_selection(bool *use_sdcard_ret) +{ + gboolean return_value = false; + bool use_sdcard = *use_sdcard_ret; + char *use_sd_card_value = get_parameter_value("SDCardSupport"); + char *sd_file_system = NULL; + + if (use_sd_card_value != NULL) { + bool use_sdcard = strcmp(use_sd_card_value, "yes") == 0; + if (use_sdcard) { + // Confirm that the SD card is usable + sd_file_system = get_sd_filesystem(); + if (sd_file_system == NULL) { + syslog(LOG_ERR, + "Couldn't identify the file system of the SD card at %s", + sd_card_path); + goto end; + } + + if (strcmp(sd_file_system, "vfat") == 0 || + strcmp(sd_file_system, "exfat") == 0) { + syslog(LOG_ERR, + "The SD card at %s uses file system %s which does not support " + "Unix file permissions. Please reformat to a file system that " + "support Unix file permissions, such as ext4 or xfs.", + sd_card_path, + sd_file_system); + goto end; + } + + gchar card_path[100]; + g_stpcpy(card_path, sd_card_path); + g_strlcat(card_path, "/dockerd", 100); + + if (access(card_path, F_OK) == 0 && access(card_path, W_OK) != 0) { + syslog( + LOG_ERR, + "The application user does not have write permissions to the SD " + "card directory at %s. Please change the directory permissions or " + "remove the directory.", + card_path); + goto end; + } + + if (!setup_sdcard()) { + syslog(LOG_ERR, "Failed to setup SD card."); + goto end; + } + } + *use_sdcard_ret = use_sdcard; + return_value = true; + } +end: + free(use_sd_card_value); + free(sd_file_system); + return return_value; +} + +/** + * @brief Gets and verifies the UseTLS selection + * + * @param use_tls_ret selection to be updated. + * @return True if successful, false otherwise. + */ +static gboolean +get_and_verify_tls_selection(bool *use_tls_ret) +{ + gboolean return_value = false; + bool use_tls = *use_tls_ret; + char *ca_path = NULL; + char *cert_path = NULL; + char *key_path = NULL; + + char *use_tls_value = get_parameter_value("UseTLS"); + if (use_tls_value != NULL) { + use_tls = strcmp(use_tls_value, "yes") == 0; + if (use_tls) { + char *ca_path = g_strdup_printf("%s%s", tls_cert_path, tls_certs[0]); + char *cert_path = g_strdup_printf("%s%s", tls_cert_path, tls_certs[1]); + char *key_path = g_strdup_printf("%s%s", tls_cert_path, tls_certs[2]); + + bool ca_exists = access(ca_path, F_OK) == 0; + bool cert_exists = access(cert_path, F_OK) == 0; + bool key_exists = access(key_path, F_OK) == 0; + + if (!ca_exists || !cert_exists || !key_exists) { + syslog(LOG_ERR, "One or more TLS certificates missing."); + } + + if (!ca_exists) { + syslog(LOG_ERR, + "Cannot start using TLS, no CA certificate found at %s", + ca_path); + } + if (!cert_exists) { + syslog(LOG_ERR, + "Cannot start using TLS, no server certificate found at %s", + cert_path); + } + if (!key_exists) { + syslog(LOG_ERR, + "Cannot start using TLS, no server key found at %s", + key_path); + } + + if (!ca_exists || !cert_exists || !key_exists) { + goto end; + } + } + *use_tls_ret = use_tls; + return_value = true; + } +end: + free(use_tls_value); + free(ca_path); + free(cert_path); + free(key_path); + return return_value; +} + +/** + * @brief Gets and verifies the IPCSocket selection + * + * @param use_ipc_socket_ret selection to be updated. + * @return True if successful, false otherwise. + */ +static gboolean +get_ipc_socket_selection(bool *use_ipc_socket_ret) +{ + gboolean return_value = false; + bool use_ipc_socket = *use_ipc_socket_ret; + char *use_ipc_socket_value = get_parameter_value("IPCSocket"); + if (use_ipc_socket_value != NULL) { + use_ipc_socket = strcmp(use_ipc_socket_value, "yes") == 0; + *use_ipc_socket_ret = use_ipc_socket; + return_value = true; + } + free(use_ipc_socket_value); + return return_value; +} + /** * @brief Start a new dockerd process. * * @return True if successful, false otherwise */ static bool -start_dockerd(void) +start_dockerd(bool use_sdcard, bool use_tls, bool use_ipc_socket) { GError *error = NULL; @@ -246,44 +404,6 @@ start_dockerd(void) guint args_offset = 0; gchar **args_split = NULL; - // Read parameters - char *use_sd_card_value = get_parameter_value("SDCardSupport"); - char *use_tls_value = get_parameter_value("UseTLS"); - char *use_ipc_socket_value = get_parameter_value("IPCSocket"); - if (use_sd_card_value == NULL || use_tls_value == NULL || - use_ipc_socket_value == NULL) { - goto end; - } - bool use_sdcard = strcmp(use_sd_card_value, "yes") == 0; - bool use_tls = strcmp(use_tls_value, "yes") == 0; - bool use_ipc_socket = strcmp(use_ipc_socket_value, "yes") == 0; - - if (use_sdcard) { - // Confirm that the SD card is usable - char *sd_file_system = get_sd_filesystem(); - if (sd_file_system == NULL) { - syslog(LOG_ERR, - "Couldn't identify the file system of the SD card at %s", - sd_card_path); - goto end; - } - - if (strcmp(sd_file_system, "vfat") == 0 || - strcmp(sd_file_system, "exfat") == 0) { - syslog(LOG_ERR, - "The SD card at %s uses file system %s which does not support " - "Unix file permissions. Please reformat to a file system that " - "support Unix file permissions, such as ext4 or xfs.", - sd_card_path, - sd_file_system); - goto end; - } - - if (!setup_sdcard()) { - syslog(LOG_ERR, "Failed to setup SD card."); - goto end; - } - } args_offset += g_snprintf( args + args_offset, args_len - args_offset, @@ -302,30 +422,6 @@ start_dockerd(void) const char *key_path = "/usr/local/packages/dockerdwrapperwithcompose/server-key.pem"; - bool ca_exists = access(ca_path, F_OK) == 0; - bool cert_exists = access(cert_path, F_OK) == 0; - bool key_exists = access(key_path, F_OK) == 0; - - if (!ca_exists) { - syslog(LOG_ERR, - "Cannot start using TLS, no CA certificate found at %s", - ca_path); - } - if (!cert_exists) { - syslog(LOG_ERR, - "Cannot start using TLS, no server certificate found at %s", - cert_path); - } - if (!key_exists) { - syslog(LOG_ERR, - "Cannot start using TLS, no server key found at %s", - key_path); - } - - if (!ca_exists || !cert_exists || !key_exists) { - goto end; - } - args_offset += g_snprintf(args + args_offset, args_len - args_offset, " %s %s %s %s %s %s %s %s", @@ -401,16 +497,11 @@ start_dockerd(void) g_main_loop_quit(loop); goto end; } - return_value = true; end: g_strfreev(args_split); - free(use_sd_card_value); - free(use_tls_value); - free(use_ipc_socket_value); g_clear_error(&error); - return return_value; } @@ -483,7 +574,26 @@ dockerd_process_exited_callback(__attribute__((unused)) GPid pid, if (restart_dockerd) { restart_dockerd = false; - if (!start_dockerd()) { + bool use_sdcard = false; + bool use_tls = false; + bool use_ipc_socket = false; + + if (!get_and_verify_sd_card_selection(&use_sdcard)) { + syslog(LOG_ERR, "Failed to setup sd_card"); + exit_code = -1; + g_main_loop_quit(loop); + } + if (!get_and_verify_tls_selection(&use_tls)) { + syslog(LOG_ERR, "Failed to verify tls selection"); + exit_code = -1; + g_main_loop_quit(loop); + } + if (!get_ipc_socket_selection(&use_ipc_socket)) { + syslog(LOG_ERR, "Failed to get ipc socket selection"); + exit_code = -1; + g_main_loop_quit(loop); + } + if (!start_dockerd(use_sdcard, use_tls, use_ipc_socket)) { syslog(LOG_ERR, "Failed to restart dockerd, exiting."); exit_code = -1; g_main_loop_quit(loop); @@ -495,7 +605,7 @@ dockerd_process_exited_callback(__attribute__((unused)) GPid pid, } /** - * @brief Callback function called when the SDCardSupport parameter + * @brief Callback function called when any of the parameters * changes. Will restart the dockerd process with the new setting. * * @param name Name of the updated parameter. @@ -507,14 +617,18 @@ parameter_changed_callback(const gchar *name, __attribute__((unused)) gpointer data) { const gchar *parname = name += strlen("root.dockerdwrapperwithcompose."); - // bool dockerd_started_correctly = false; - if (strcmp(parname, "SDCardSupport") == 0) { - syslog(LOG_INFO, "SDCardSupport changed to: %s", value); - restart_dockerd = true; - } else if (strcmp(parname, "UseTLS") == 0) { - syslog(LOG_INFO, "UseTLS changed to: %s", value); - restart_dockerd = true; - } else { + + bool unknown_parameter = true; + for (size_t i = 0; i < sizeof(ax_parameters) / sizeof(ax_parameters[0]); + ++i) { + if (strcmp(parname, ax_parameters[i]) == 0) { + syslog(LOG_INFO, "%s changed to: %s", ax_parameters[i], value); + restart_dockerd = true; + unknown_parameter = false; + } + } + + if (unknown_parameter) { syslog(LOG_WARNING, "Parameter %s is not recognized", name); restart_dockerd = false; @@ -543,34 +657,21 @@ setup_axparameter(void) goto end; } - gboolean geresult = ax_parameter_register_callback( - ax_parameter, - "root.dockerdwrapperwithcompose.SDCardSupport", - parameter_changed_callback, - NULL, - &error); - - if (geresult == FALSE) { - syslog(LOG_ERR, - "Could not register SDCardSupport callback. Error: %s", - error->message); - goto end; - } - - geresult = - ax_parameter_register_callback(ax_parameter, - "root.dockerdwrapperwithcompose.UseTLS", - parameter_changed_callback, - NULL, - &error); + for (size_t i = 0; i < sizeof(ax_parameters) / sizeof(ax_parameters[0]); + ++i) { + char *parameter_path = g_strdup_printf( + "%s.%s", "root.dockerdwrapperwithcompose", ax_parameters[i]); + gboolean geresult = ax_parameter_register_callback( + ax_parameter, parameter_path, parameter_changed_callback, NULL, &error); + free(parameter_path); - if (geresult == FALSE) { - syslog(LOG_ERR, - "Could not register UseTLS callback. Error: %s", - error->message); - ax_parameter_unregister_callback( - ax_parameter, "root.dockerdwrapperwithcompose.SDCardSupport"); - goto end; + if (geresult == FALSE) { + syslog(LOG_ERR, + "Could not register %s callback. Error: %s", + ax_parameters[i], + error->message); + goto end; + } } success = true; @@ -606,7 +707,27 @@ main(void) loop = g_main_loop_new(NULL, FALSE); loop = g_main_loop_ref(loop); - if (!start_dockerd()) { + bool use_sdcard = false; + bool use_tls = false; + bool use_ipc_socket = false; + + if (!get_and_verify_sd_card_selection(&use_sdcard)) { + syslog(LOG_INFO, "Failed to setup sd_card"); + exit_code = -1; + goto end; + } + if (!get_and_verify_tls_selection(&use_tls)) { + syslog(LOG_INFO, "Failed to verify tls selection"); + exit_code = -1; + goto end; + } + if (!get_ipc_socket_selection(&use_ipc_socket)) { + syslog(LOG_INFO, "Failed to get ipc socket selection"); + exit_code = -1; + goto end; + } + + if (!start_dockerd(use_sdcard, use_tls, use_ipc_socket)) { syslog(LOG_ERR, "Starting dockerd failed"); exit_code = -1; goto end; @@ -624,10 +745,13 @@ main(void) } if (ax_parameter != NULL) { - ax_parameter_unregister_callback( - ax_parameter, "root.dockerdwrapperwithcompose.SDCardSupport"); - ax_parameter_unregister_callback(ax_parameter, - "root.dockerdwrapperwithcompose.UseTLS"); + for (size_t i = 0; i < sizeof(ax_parameters) / sizeof(ax_parameters[0]); + ++i) { + char *parameter_path = g_strdup_printf( + "%s.%s", "root.dockerdwrapperwithcompose", ax_parameters[i]); + ax_parameter_unregister_callback(ax_parameter, parameter_path); + free(parameter_path); + } ax_parameter_free(ax_parameter); } From 657c6a12ad347bbb83c300ba5f9741388223cd2c Mon Sep 17 00:00:00 2001 From: Mattias Axelsson Date: Thu, 21 Mar 2024 14:50:12 +0100 Subject: [PATCH 06/16] Package settings in a struct and reduce code duplication * Package settings in a struct and reduce code duplication --- app/dockerdwrapperwithcompose.c | 95 +++++++++++++++------------------ 1 file changed, 44 insertions(+), 51 deletions(-) diff --git a/app/dockerdwrapperwithcompose.c b/app/dockerdwrapperwithcompose.c index aef1b94..a66dac5 100644 --- a/app/dockerdwrapperwithcompose.c +++ b/app/dockerdwrapperwithcompose.c @@ -25,6 +25,12 @@ #include #include +struct settings { + bool use_sdcard; + bool use_tls; + bool use_ipc_socket; +}; + /** * @brief Callback called when the dockerd process exits. */ @@ -384,14 +390,33 @@ get_ipc_socket_selection(bool *use_ipc_socket_ret) return return_value; } -/** - * @brief Start a new dockerd process. - * - * @return True if successful, false otherwise - */ static bool -start_dockerd(bool use_sdcard, bool use_tls, bool use_ipc_socket) +read_settings(struct settings *settings) +{ + if (!get_and_verify_sd_card_selection(&settings->use_sdcard)) { + syslog(LOG_ERR, "Failed to setup sd_card"); + return false; + } + if (!get_and_verify_tls_selection(&settings->use_tls)) { + syslog(LOG_ERR, "Failed to verify tls selection"); + return false; + } + if (!get_ipc_socket_selection(&settings->use_ipc_socket)) { + syslog(LOG_ERR, "Failed to get ipc socket selection"); + return false; + } + return true; +} + +// Return true if dockerd was successfully started. +// Log an error and return false if it failed to start properly. +static bool +start_dockerd(const struct settings *settings) { + const bool use_sdcard = settings->use_sdcard; + const bool use_tls = settings->use_tls; + const bool use_ipc_socket = settings->use_ipc_socket; + GError *error = NULL; bool return_value = false; @@ -482,7 +507,7 @@ start_dockerd(bool use_sdcard, bool use_tls, bool use_ipc_socket) &error); if (!result) { syslog(LOG_ERR, - "Could not execv the dockerd process. Return value: %d, error: %s", + "Starting dockerd failed: execv returned: %d, error: %s", result, error->message); goto end; @@ -492,7 +517,8 @@ start_dockerd(bool use_sdcard, bool use_tls, bool use_ipc_socket) g_child_watch_add(dockerd_process_pid, dockerd_process_exited_callback, NULL); if (!is_process_alive(dockerd_process_pid)) { - // The process died during adding of callback, tell loop to quit. + syslog(LOG_ERR, + "Starting dockerd failed: Process died unexpectedly during startup"); exit_code = -1; g_main_loop_quit(loop); goto end; @@ -505,6 +531,13 @@ start_dockerd(bool use_sdcard, bool use_tls, bool use_ipc_socket) return return_value; } +static bool +read_settings_and_start_dockerd(void) +{ + struct settings settings = {0}; + return read_settings(&settings) && start_dockerd(&settings); +} + /** * @brief Stop the currently running dockerd process. * @@ -574,27 +607,7 @@ dockerd_process_exited_callback(__attribute__((unused)) GPid pid, if (restart_dockerd) { restart_dockerd = false; - bool use_sdcard = false; - bool use_tls = false; - bool use_ipc_socket = false; - - if (!get_and_verify_sd_card_selection(&use_sdcard)) { - syslog(LOG_ERR, "Failed to setup sd_card"); - exit_code = -1; - g_main_loop_quit(loop); - } - if (!get_and_verify_tls_selection(&use_tls)) { - syslog(LOG_ERR, "Failed to verify tls selection"); - exit_code = -1; - g_main_loop_quit(loop); - } - if (!get_ipc_socket_selection(&use_ipc_socket)) { - syslog(LOG_ERR, "Failed to get ipc socket selection"); - exit_code = -1; - g_main_loop_quit(loop); - } - if (!start_dockerd(use_sdcard, use_tls, use_ipc_socket)) { - syslog(LOG_ERR, "Failed to restart dockerd, exiting."); + if (!read_settings_and_start_dockerd()) { exit_code = -1; g_main_loop_quit(loop); } @@ -700,6 +713,7 @@ main(void) ax_parameter = setup_axparameter(); if (ax_parameter == NULL) { syslog(LOG_ERR, "Error in setup_axparameter: %s", error->message); + exit_code = -1; goto end; } @@ -707,28 +721,7 @@ main(void) loop = g_main_loop_new(NULL, FALSE); loop = g_main_loop_ref(loop); - bool use_sdcard = false; - bool use_tls = false; - bool use_ipc_socket = false; - - if (!get_and_verify_sd_card_selection(&use_sdcard)) { - syslog(LOG_INFO, "Failed to setup sd_card"); - exit_code = -1; - goto end; - } - if (!get_and_verify_tls_selection(&use_tls)) { - syslog(LOG_INFO, "Failed to verify tls selection"); - exit_code = -1; - goto end; - } - if (!get_ipc_socket_selection(&use_ipc_socket)) { - syslog(LOG_INFO, "Failed to get ipc socket selection"); - exit_code = -1; - goto end; - } - - if (!start_dockerd(use_sdcard, use_tls, use_ipc_socket)) { - syslog(LOG_ERR, "Starting dockerd failed"); + if (!read_settings_and_start_dockerd()) { exit_code = -1; goto end; } From 353e296ce323a1432dbc352f7a686fea8b37a3d5 Mon Sep 17 00:00:00 2001 From: Mattias Axelsson Date: Thu, 21 Mar 2024 16:39:56 +0100 Subject: [PATCH 07/16] Check filesystem on subfolder instead of top folder * Check filesystem on subfolder instead of top folder * Create folder before checking the filesystem --- app/dockerdwrapperwithcompose.c | 85 ++++++++++++++++----------------- 1 file changed, 40 insertions(+), 45 deletions(-) diff --git a/app/dockerdwrapperwithcompose.c b/app/dockerdwrapperwithcompose.c index a66dac5..9f44730 100644 --- a/app/dockerdwrapperwithcompose.c +++ b/app/dockerdwrapperwithcompose.c @@ -49,7 +49,8 @@ static int exit_code = 0; static pid_t dockerd_process_pid = -1; // Full path to the SD card -static const char *sd_card_path = "/var/spool/storage/SD_DISK"; +static const char *dockerd_path_on_sd_card = + "/var/spool/storage/SD_DISK/dockerd"; // True if the dockerd_exited_callback should restart dockerd static bool restart_dockerd = false; @@ -156,21 +157,21 @@ get_parameter_value(const char *parameter_name) } /** - * @brief Retrieve the file system type of the SD card as a string. + * @brief Retrieve the file system type of the device containing this path. * * @return The file system type as a string (ext4/ext3/vfat etc...) if * successful, NULL otherwise. */ static char * -get_sd_filesystem(void) +get_filesystem_of_path(const char *path) { char buf[PATH_MAX]; struct stat sd_card_stat; - int stat_result = stat(sd_card_path, &sd_card_stat); + int stat_result = stat(path, &sd_card_stat); if (stat_result != 0) { syslog(LOG_ERR, "Cannot store data on the SD card, no storage exists at %s", - sd_card_path); + path); return NULL; } @@ -209,10 +210,11 @@ get_sd_filesystem(void) * @return True if successful, false if setup failed. */ static bool -setup_sdcard(void) +setup_sdcard(const char *dockerd_path) { - const char *data_root = "/var/spool/storage/SD_DISK/dockerd/data"; - const char *exec_root = "/var/spool/storage/SD_DISK/dockerd/exec"; + g_autofree char *sd_file_system = NULL; + g_autofree char *data_root = g_strdup_printf("%s/data", dockerd_path); + g_autofree char *exec_root = g_strdup_printf("%s/exec", dockerd_path); char *create_droot_command = g_strdup_printf("mkdir -p %s", data_root); char *create_eroot_command = g_strdup_printf("mkdir -p %s", exec_root); int res = system(create_droot_command); @@ -232,6 +234,35 @@ setup_sdcard(void) goto end; } + // Confirm that the SD card is usable + sd_file_system = get_filesystem_of_path(dockerd_path); + if (sd_file_system == NULL) { + syslog(LOG_ERR, + "Couldn't identify the file system of the SD card at %s", + dockerd_path); + goto end; + } + + if (strcmp(sd_file_system, "vfat") == 0 || + strcmp(sd_file_system, "exfat") == 0) { + syslog(LOG_ERR, + "The SD card at %s uses file system %s which does not support " + "Unix file permissions. Please reformat to a file system that " + "support Unix file permissions, such as ext4 or xfs.", + dockerd_path, + sd_file_system); + goto end; + } + + if (access(dockerd_path, F_OK) == 0 && access(dockerd_path, W_OK) != 0) { + syslog(LOG_ERR, + "The application user does not have write permissions to the SD " + "card directory at %s. Please change the directory permissions or " + "remove the directory.", + dockerd_path); + goto end; + } + res = 0; end: @@ -254,46 +285,11 @@ get_and_verify_sd_card_selection(bool *use_sdcard_ret) gboolean return_value = false; bool use_sdcard = *use_sdcard_ret; char *use_sd_card_value = get_parameter_value("SDCardSupport"); - char *sd_file_system = NULL; if (use_sd_card_value != NULL) { bool use_sdcard = strcmp(use_sd_card_value, "yes") == 0; if (use_sdcard) { - // Confirm that the SD card is usable - sd_file_system = get_sd_filesystem(); - if (sd_file_system == NULL) { - syslog(LOG_ERR, - "Couldn't identify the file system of the SD card at %s", - sd_card_path); - goto end; - } - - if (strcmp(sd_file_system, "vfat") == 0 || - strcmp(sd_file_system, "exfat") == 0) { - syslog(LOG_ERR, - "The SD card at %s uses file system %s which does not support " - "Unix file permissions. Please reformat to a file system that " - "support Unix file permissions, such as ext4 or xfs.", - sd_card_path, - sd_file_system); - goto end; - } - - gchar card_path[100]; - g_stpcpy(card_path, sd_card_path); - g_strlcat(card_path, "/dockerd", 100); - - if (access(card_path, F_OK) == 0 && access(card_path, W_OK) != 0) { - syslog( - LOG_ERR, - "The application user does not have write permissions to the SD " - "card directory at %s. Please change the directory permissions or " - "remove the directory.", - card_path); - goto end; - } - - if (!setup_sdcard()) { + if (!setup_sdcard(dockerd_path_on_sd_card)) { syslog(LOG_ERR, "Failed to setup SD card."); goto end; } @@ -303,7 +299,6 @@ get_and_verify_sd_card_selection(bool *use_sdcard_ret) } end: free(use_sd_card_value); - free(sd_file_system); return return_value; } From a3c08f57ac3c7ccc065f8beba4abf92490fb6ba5 Mon Sep 17 00:00:00 2001 From: Mattias Axelsson Date: Fri, 22 Mar 2024 13:52:29 +0100 Subject: [PATCH 08/16] Remove creation of unused exec_root directory The default value of /var/run/dockerd, on a tmpfs, is a much better place for the execution state, that on a flash drive. --- app/dockerdwrapperwithcompose.c | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/app/dockerdwrapperwithcompose.c b/app/dockerdwrapperwithcompose.c index 9f44730..b236fae 100644 --- a/app/dockerdwrapperwithcompose.c +++ b/app/dockerdwrapperwithcompose.c @@ -214,24 +214,16 @@ setup_sdcard(const char *dockerd_path) { g_autofree char *sd_file_system = NULL; g_autofree char *data_root = g_strdup_printf("%s/data", dockerd_path); - g_autofree char *exec_root = g_strdup_printf("%s/exec", dockerd_path); - char *create_droot_command = g_strdup_printf("mkdir -p %s", data_root); - char *create_eroot_command = g_strdup_printf("mkdir -p %s", exec_root); + g_autofree char *create_droot_command = + g_strdup_printf("mkdir -p %s", data_root); + int res = system(create_droot_command); if (res != 0) { syslog(LOG_ERR, "Failed to create data_root folder at: %s. Error code: %d", data_root, res); - goto end; - } - res = system(create_eroot_command); - if (res != 0) { - syslog(LOG_ERR, - "Failed to create exec_root folder at: %s. Error code: %d", - exec_root, - res); - goto end; + return false; } // Confirm that the SD card is usable @@ -240,7 +232,7 @@ setup_sdcard(const char *dockerd_path) syslog(LOG_ERR, "Couldn't identify the file system of the SD card at %s", dockerd_path); - goto end; + return false; } if (strcmp(sd_file_system, "vfat") == 0 || @@ -251,7 +243,7 @@ setup_sdcard(const char *dockerd_path) "support Unix file permissions, such as ext4 or xfs.", dockerd_path, sd_file_system); - goto end; + return false; } if (access(dockerd_path, F_OK) == 0 && access(dockerd_path, W_OK) != 0) { @@ -260,17 +252,10 @@ setup_sdcard(const char *dockerd_path) "card directory at %s. Please change the directory permissions or " "remove the directory.", dockerd_path); - goto end; + return false; } - res = 0; - -end: - - free(create_droot_command); - free(create_eroot_command); - - return res == 0; + return true; } /** From 175942810c778716c751e82e410613df88a38a2a Mon Sep 17 00:00:00 2001 From: Mattias Axelsson Date: Fri, 22 Mar 2024 13:56:21 +0100 Subject: [PATCH 09/16] Read Boolean parameters with a common function --- app/dockerdwrapperwithcompose.c | 35 ++++++++++++++------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/app/dockerdwrapperwithcompose.c b/app/dockerdwrapperwithcompose.c index b236fae..d24bead 100644 --- a/app/dockerdwrapperwithcompose.c +++ b/app/dockerdwrapperwithcompose.c @@ -258,6 +258,15 @@ setup_sdcard(const char *dockerd_path) return true; } +// A parameter of type "bool:no,yes" is guaranteed to contain one of those +// strings, but user code is still needed to interpret it as a Boolean type. +static bool +is_parameter_yes(const char *name) +{ + g_autofree char *value = get_parameter_value(name); + return value && strcmp(value, "yes") == 0; +} + /** * @brief Gets and verifies the SDCardSupport selection * @@ -268,11 +277,9 @@ static gboolean get_and_verify_sd_card_selection(bool *use_sdcard_ret) { gboolean return_value = false; - bool use_sdcard = *use_sdcard_ret; - char *use_sd_card_value = get_parameter_value("SDCardSupport"); + const bool use_sdcard = is_parameter_yes("SDCardSupport"); - if (use_sd_card_value != NULL) { - bool use_sdcard = strcmp(use_sd_card_value, "yes") == 0; + { if (use_sdcard) { if (!setup_sdcard(dockerd_path_on_sd_card)) { syslog(LOG_ERR, "Failed to setup SD card."); @@ -283,7 +290,6 @@ get_and_verify_sd_card_selection(bool *use_sdcard_ret) return_value = true; } end: - free(use_sd_card_value); return return_value; } @@ -297,14 +303,12 @@ static gboolean get_and_verify_tls_selection(bool *use_tls_ret) { gboolean return_value = false; - bool use_tls = *use_tls_ret; char *ca_path = NULL; char *cert_path = NULL; char *key_path = NULL; - char *use_tls_value = get_parameter_value("UseTLS"); - if (use_tls_value != NULL) { - use_tls = strcmp(use_tls_value, "yes") == 0; + const bool use_tls = is_parameter_yes("UseTLS"); + { if (use_tls) { char *ca_path = g_strdup_printf("%s%s", tls_cert_path, tls_certs[0]); char *cert_path = g_strdup_printf("%s%s", tls_cert_path, tls_certs[1]); @@ -342,7 +346,6 @@ get_and_verify_tls_selection(bool *use_tls_ret) return_value = true; } end: - free(use_tls_value); free(ca_path); free(cert_path); free(key_path); @@ -358,16 +361,8 @@ get_and_verify_tls_selection(bool *use_tls_ret) static gboolean get_ipc_socket_selection(bool *use_ipc_socket_ret) { - gboolean return_value = false; - bool use_ipc_socket = *use_ipc_socket_ret; - char *use_ipc_socket_value = get_parameter_value("IPCSocket"); - if (use_ipc_socket_value != NULL) { - use_ipc_socket = strcmp(use_ipc_socket_value, "yes") == 0; - *use_ipc_socket_ret = use_ipc_socket; - return_value = true; - } - free(use_ipc_socket_value); - return return_value; + *use_ipc_socket_ret = is_parameter_yes("IPCSocket"); + return true; } static bool From 2caf01f8999600cb2ef8785c7e06905e1f9797de Mon Sep 17 00:00:00 2001 From: Mattias Axelsson Date: Fri, 22 Mar 2024 15:50:26 +0100 Subject: [PATCH 10/16] Remove unused error variable Since 'error' is not passed in to setup_axparameter(), it will not be set in case setup_axparameter() fails. However, it wasn't possible to get setup_axparameter() to fail. Both ax_parameter_new() and ax_parameter_register_callback() are apparently valid to call even with a non-existing application name. --- app/dockerdwrapperwithcompose.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/dockerdwrapperwithcompose.c b/app/dockerdwrapperwithcompose.c index d24bead..9d42fbd 100644 --- a/app/dockerdwrapperwithcompose.c +++ b/app/dockerdwrapperwithcompose.c @@ -667,6 +667,7 @@ setup_axparameter(void) end: if (!success && ax_parameter != NULL) { ax_parameter_free(ax_parameter); + ax_parameter = NULL; } return ax_parameter; } @@ -674,7 +675,6 @@ setup_axparameter(void) int main(void) { - GError *error = NULL; AXParameter *ax_parameter = NULL; exit_code = 0; @@ -687,7 +687,7 @@ main(void) // Setup ax_parameter ax_parameter = setup_axparameter(); if (ax_parameter == NULL) { - syslog(LOG_ERR, "Error in setup_axparameter: %s", error->message); + syslog(LOG_ERR, "Error in setup_axparameter"); exit_code = -1; goto end; } @@ -723,6 +723,5 @@ main(void) ax_parameter_free(ax_parameter); } - g_clear_error(&error); return exit_code; } From 83d9d7e3759a6ddf93de0a2f499ddf8efb7652c0 Mon Sep 17 00:00:00 2001 From: Mattias Axelsson Date: Mon, 25 Mar 2024 08:46:47 +0100 Subject: [PATCH 11/16] Pass around data root in settings struct This restores functionality in commit 774fbf62d726741ad33907c352ac70559b01b358. --- app/dockerdwrapperwithcompose.c | 71 ++++++++++++++------------------- 1 file changed, 31 insertions(+), 40 deletions(-) diff --git a/app/dockerdwrapperwithcompose.c b/app/dockerdwrapperwithcompose.c index 9d42fbd..25e0042 100644 --- a/app/dockerdwrapperwithcompose.c +++ b/app/dockerdwrapperwithcompose.c @@ -26,7 +26,7 @@ #include struct settings { - bool use_sdcard; + char *data_root; bool use_tls; bool use_ipc_socket; }; @@ -210,10 +210,9 @@ get_filesystem_of_path(const char *path) * @return True if successful, false if setup failed. */ static bool -setup_sdcard(const char *dockerd_path) +setup_sdcard(const char *data_root) { g_autofree char *sd_file_system = NULL; - g_autofree char *data_root = g_strdup_printf("%s/data", dockerd_path); g_autofree char *create_droot_command = g_strdup_printf("mkdir -p %s", data_root); @@ -227,11 +226,11 @@ setup_sdcard(const char *dockerd_path) } // Confirm that the SD card is usable - sd_file_system = get_filesystem_of_path(dockerd_path); + sd_file_system = get_filesystem_of_path(data_root); if (sd_file_system == NULL) { syslog(LOG_ERR, "Couldn't identify the file system of the SD card at %s", - dockerd_path); + data_root); return false; } @@ -241,17 +240,17 @@ setup_sdcard(const char *dockerd_path) "The SD card at %s uses file system %s which does not support " "Unix file permissions. Please reformat to a file system that " "support Unix file permissions, such as ext4 or xfs.", - dockerd_path, + data_root, sd_file_system); return false; } - if (access(dockerd_path, F_OK) == 0 && access(dockerd_path, W_OK) != 0) { + if (access(data_root, F_OK) == 0 && access(data_root, W_OK) != 0) { syslog(LOG_ERR, "The application user does not have write permissions to the SD " "card directory at %s. Please change the directory permissions or " "remove the directory.", - dockerd_path); + data_root); return false; } @@ -274,23 +273,18 @@ is_parameter_yes(const char *name) * @return True if successful, false otherwise. */ static gboolean -get_and_verify_sd_card_selection(bool *use_sdcard_ret) +get_and_verify_sd_card_selection(char **data_root) { - gboolean return_value = false; - const bool use_sdcard = is_parameter_yes("SDCardSupport"); - - { - if (use_sdcard) { - if (!setup_sdcard(dockerd_path_on_sd_card)) { - syslog(LOG_ERR, "Failed to setup SD card."); - goto end; - } + if (is_parameter_yes("SDCardSupport")) { + *data_root = g_strdup_printf("%s/data", dockerd_path_on_sd_card); + if (!setup_sdcard(*data_root)) { + syslog(LOG_ERR, "Failed to setup SD card."); + return false; } - *use_sdcard_ret = use_sdcard; - return_value = true; + } else { + *data_root = NULL; } -end: - return return_value; + return true; } /** @@ -368,7 +362,7 @@ get_ipc_socket_selection(bool *use_ipc_socket_ret) static bool read_settings(struct settings *settings) { - if (!get_and_verify_sd_card_selection(&settings->use_sdcard)) { + if (!get_and_verify_sd_card_selection(&settings->data_root)) { syslog(LOG_ERR, "Failed to setup sd_card"); return false; } @@ -388,7 +382,7 @@ read_settings(struct settings *settings) static bool start_dockerd(const struct settings *settings) { - const bool use_sdcard = settings->use_sdcard; + const char *data_root = settings->data_root; const bool use_tls = settings->use_tls; const bool use_ipc_socket = settings->use_ipc_socket; @@ -445,25 +439,20 @@ start_dockerd(const struct settings *settings) g_strlcat(msg, " in unsecured mode", msg_len); } - if (use_sdcard) { - args_offset += - g_snprintf(args + args_offset, - args_len - args_offset, - " %s", - "--data-root /var/spool/storage/SD_DISK/dockerd/data"); - - g_strlcat(msg, " using SD card as storage", msg_len); - } else { - g_strlcat(msg, " using internal storage", msg_len); - } - - if (use_ipc_socket) { + g_autofree char *data_root_msg = g_strdup_printf( + " using %s as storage", data_root ? data_root : "/var/lib/docker"); + g_strlcat(msg, data_root_msg, msg_len); + if (data_root) args_offset += g_snprintf(args + args_offset, args_len - args_offset, - " %s", - "-H unix:///var/run/docker.sock"); + " --data-root %s", + data_root); + if (use_ipc_socket) { g_strlcat(msg, " with IPC socket.", msg_len); + args_offset += g_snprintf(args + args_offset, + args_len - args_offset, + " -H unix:///var/run/docker.sock"); } else { g_strlcat(msg, " without IPC socket.", msg_len); } @@ -510,7 +499,9 @@ static bool read_settings_and_start_dockerd(void) { struct settings settings = {0}; - return read_settings(&settings) && start_dockerd(&settings); + bool success = read_settings(&settings) && start_dockerd(&settings); + free(settings.data_root); + return success; } /** From 3e4895f919e7c08ece6b13539c4c91f54abbcbb7 Mon Sep 17 00:00:00 2001 From: Mattias Axelsson Date: Mon, 25 Mar 2024 09:20:36 +0100 Subject: [PATCH 12/16] Remove handling of unknown parameters in callback * Remove handling of unknown parameters in callback Callbacks from unknown parameters isn't something that happens, and if it did, it wouldn't be a big deal anyway. Removing is a step towards removing global variable 'restart_dockerd'. --- app/dockerdwrapperwithcompose.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/app/dockerdwrapperwithcompose.c b/app/dockerdwrapperwithcompose.c index 25e0042..49564e4 100644 --- a/app/dockerdwrapperwithcompose.c +++ b/app/dockerdwrapperwithcompose.c @@ -597,24 +597,14 @@ parameter_changed_callback(const gchar *name, { const gchar *parname = name += strlen("root.dockerdwrapperwithcompose."); - bool unknown_parameter = true; for (size_t i = 0; i < sizeof(ax_parameters) / sizeof(ax_parameters[0]); ++i) { if (strcmp(parname, ax_parameters[i]) == 0) { syslog(LOG_INFO, "%s changed to: %s", ax_parameters[i], value); restart_dockerd = true; - unknown_parameter = false; } } - if (unknown_parameter) { - syslog(LOG_WARNING, "Parameter %s is not recognized", name); - restart_dockerd = false; - - // No known parameter was changed, do not restart. - return; - } - // Stop the currently running process. if (!stop_dockerd()) { syslog(LOG_ERR, From 6481d29b7c3341c61847bdb3ff71f74cb0823530 Mon Sep 17 00:00:00 2001 From: Mattias Axelsson Date: Mon, 25 Mar 2024 12:59:02 +0100 Subject: [PATCH 13/16] Centralize restart of dockerd * Centralize restart of dockerd By running g_main_loop_run() in a while-loop, other parts of the program can retry starting dockerd by simply calling g_main_loop_quit(). To exit this loop, call quit_program(), which will also set the global acap_exit_code variable. This is needed then e.g. axstorage is used to wait for SD card to become available. --------- Co-authored-by: Madelen Andersson --- app/dockerdwrapperwithcompose.c | 76 ++++++++++++--------------------- 1 file changed, 27 insertions(+), 49 deletions(-) diff --git a/app/dockerdwrapperwithcompose.c b/app/dockerdwrapperwithcompose.c index 49564e4..6d64508 100644 --- a/app/dockerdwrapperwithcompose.c +++ b/app/dockerdwrapperwithcompose.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -42,8 +43,9 @@ static void dockerd_process_exited_callback(__attribute__((unused)) GPid pid, // Loop run on the main process static GMainLoop *loop = NULL; -// Exit code -static int exit_code = 0; +// Exit code of this program. Set using 'quit_program()'. +#define EX_KEEP_RUNNING -1 +static int application_exit_code = EX_KEEP_RUNNING; // Pid of the running dockerd process static pid_t dockerd_process_pid = -1; @@ -52,9 +54,6 @@ static pid_t dockerd_process_pid = -1; static const char *dockerd_path_on_sd_card = "/var/spool/storage/SD_DISK/dockerd"; -// True if the dockerd_exited_callback should restart dockerd -static bool restart_dockerd = false; - // All ax_parameters the acap has static const char *ax_parameters[] = {"IPCSocket", "SDCardSupport", "UseTLS"}; @@ -65,6 +64,13 @@ static const char *tls_certs[] = {"ca.pem", "server-cert.pem", "server-key.pem"}; +static void +quit_program(int exit_code) +{ + application_exit_code = exit_code; + g_main_loop_quit(loop); +} + /** * @brief Signals handling * @@ -77,7 +83,7 @@ handle_signals(__attribute__((unused)) int signal_num) case SIGINT: case SIGTERM: case SIGQUIT: - g_main_loop_quit(loop); + quit_program(EX_OK); } } @@ -483,8 +489,7 @@ start_dockerd(const struct settings *settings) if (!is_process_alive(dockerd_process_pid)) { syslog(LOG_ERR, "Starting dockerd failed: Process died unexpectedly during startup"); - exit_code = -1; - g_main_loop_quit(loop); + quit_program(EX_SOFTWARE); goto end; } return_value = true; @@ -519,7 +524,7 @@ stop_dockerd(void) goto end; } - // Send SIGTERM to the process + syslog(LOG_INFO, "Sending SIGTERM to dockerd."); bool sigterm_successfully_sent = kill(dockerd_process_pid, SIGTERM) == 0; if (!sigterm_successfully_sent) { syslog( @@ -560,8 +565,6 @@ dockerd_process_exited_callback(__attribute__((unused)) GPid pid, if (!g_spawn_check_exit_status(status, &error)) { syslog(LOG_ERR, "Dockerd process exited with error: %d", status); g_clear_error(&error); - - exit_code = -1; } dockerd_process_pid = -1; @@ -571,16 +574,7 @@ dockerd_process_exited_callback(__attribute__((unused)) GPid pid, // manner. Remove it manually. remove("/var/run/docker.pid"); - if (restart_dockerd) { - restart_dockerd = false; - if (!read_settings_and_start_dockerd()) { - exit_code = -1; - g_main_loop_quit(loop); - } - } else { - // We shouldn't restart, stop instead. - g_main_loop_quit(loop); - } + g_main_loop_quit(loop); // Trigger a restart of dockerd from main() } /** @@ -601,17 +595,9 @@ parameter_changed_callback(const gchar *name, ++i) { if (strcmp(parname, ax_parameters[i]) == 0) { syslog(LOG_INFO, "%s changed to: %s", ax_parameters[i], value); - restart_dockerd = true; + g_main_loop_quit(loop); // Trigger a restart of dockerd from main() } } - - // Stop the currently running process. - if (!stop_dockerd()) { - syslog(LOG_ERR, - "Failed to stop dockerd process. Please restart the acap " - "manually."); - exit_code = -1; - } } static AXParameter * @@ -657,11 +643,12 @@ int main(void) { AXParameter *ax_parameter = NULL; - exit_code = 0; openlog(NULL, LOG_PID, LOG_USER); syslog(LOG_INFO, "Started logging."); + loop = g_main_loop_new(NULL, FALSE); + // Setup signal handling. init_signals(); @@ -669,30 +656,21 @@ main(void) ax_parameter = setup_axparameter(); if (ax_parameter == NULL) { syslog(LOG_ERR, "Error in setup_axparameter"); - exit_code = -1; - goto end; + quit_program(EX_SOFTWARE); } - /* Create the GLib event loop. */ - loop = g_main_loop_new(NULL, FALSE); - loop = g_main_loop_ref(loop); + while (application_exit_code == EX_KEEP_RUNNING) { + if (dockerd_process_pid == -1 && !read_settings_and_start_dockerd()) + quit_program(EX_SOFTWARE); - if (!read_settings_and_start_dockerd()) { - exit_code = -1; - goto end; + g_main_loop_run(loop); + + if (!stop_dockerd()) + syslog(LOG_WARNING, "Failed to shut down dockerd."); } - /* Run the GLib event loop. */ - g_main_loop_run(loop); g_main_loop_unref(loop); -end: - if (stop_dockerd()) { - syslog(LOG_INFO, "Shutting down. dockerd shut down successfully."); - } else { - syslog(LOG_WARNING, "Shutting down. Failed to shut down dockerd."); - } - if (ax_parameter != NULL) { for (size_t i = 0; i < sizeof(ax_parameters) / sizeof(ax_parameters[0]); ++i) { @@ -704,5 +682,5 @@ main(void) ax_parameter_free(ax_parameter); } - return exit_code; + return application_exit_code; } From 804259d46ad198da13cf24fd313e3b272c2b9124 Mon Sep 17 00:00:00 2001 From: Madelen Andersson Date: Mon, 25 Mar 2024 16:13:04 +0100 Subject: [PATCH 14/16] bump Docker Engine to 26.0.0 and Docker Compose to 2.25 * bump Docker Engine to 26.0.0 and Docker Compose to 2.25.0 --------- Co-authored-by: madelen-at-work --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 4abe160..c237d00 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:1 -ARG DOCKER_VERSION=24.0.2 -ARG DOCKER_COMPOSE_VERSION=v2.18.1 +ARG DOCKER_VERSION=26.0.0 +ARG DOCKER_COMPOSE_VERSION=v2.25.0 ARG REPO=axisecp ARG ACAPARCH=armv7hf From 6a1193f00fe7fbefdb686111eedb55c24cc13bc8 Mon Sep 17 00:00:00 2001 From: Deepika Shanmugam <92788697+deepikas20@users.noreply.github.com> Date: Tue, 26 Mar 2024 08:26:57 +0100 Subject: [PATCH 15/16] Add a new parameter to have an option to choose TCP socket creation * Add a new parameter to have an option to choose TCP socket creation --------- Co-authored-by: Madelen Andersson --- README.md | 34 ++++++++--- app/dockerdwrapperwithcompose.c | 100 +++++++++++++++++++++----------- app/manifest.json | 5 ++ 3 files changed, 98 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 648ad3a..2daff78 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ this is the recommended way to install this ACAP. > > On the web page of the device: > +> > 1. Go to the Apps page, toggle on `Allow root-privileged apps`. > 1. Go to System -> Account page, under SSH accounts toggle off `Restrict root access` to be able to send the TLS certificates. Make sure to set the password of the `root` SSH user. @@ -77,19 +78,34 @@ It's also possible to build and use a locally built image. See the ## Securing the Docker Compose ACAP using TLS -The Docker Compose ACAP can be run either unsecured or in TLS mode. The Docker Compose ACAP uses -TLS as default. Use the "Use TLS" dropdown in the web interface to switch -between the two different modes. It's also possible to toggle this option by -calling the parameter management API in [VAPIX](https://www.axis.com/vapix-library/) and setting the -`root.dockerdwrapperwithcompose.UseTLS` parameter to `yes` or `no`. The following commands would -enable TLS: +The Docker Compose ACAP application can be run in either TLS mode or unsecured mode. The Docker Compose +ACAP application uses TLS mode by default. It is important to note that Dockerd will fail to start if +TCP socket or IPC socket parameters are not selected, one of these sockets must be set to `yes`. + +Use the "Use TLS" and "TCP Socket" dropdowns in the web interface to switch between the +two different modes(yes/no). Whenever these settings change, the Docker daemon will automatically restart. +It's also possible to toggle this option by calling the parameter management API in +[VAPIX](https://www.axis.com/vapix-library/) and setting `root.dockerdwrapperwithcompose.UseTLS` and +`root.dockerdwrapperwithcompose.TCPSocket` parameters to `yes` or `no`. +The following commands would enable those parameters: ```sh DEVICE_IP= DEVICE_PASSWORD='' +``` +Enable TLS: + +```sh curl -s --anyauth -u "root:$DEVICE_PASSWORD" \ - "http://$DEVICE_IP/axis-cgi/param.cgi?action=update&root.dockerdwrapperwithcompose.UseTLS=yes" + "http://$DEVICE_IP/axis-cgi/param.cgi?action=update&root.dockerdwrapper.UseTLS=yes" +``` + +Enable TCP Socket: + +```sh +curl -s --anyauth -u "root:$DEVICE_PASSWORD" \ + "http://$DEVICE_IP/axis-cgi/param.cgi?action=update&root.dockerdwrapperwithcompose.TCPSocket=yes" ``` Note that the dockerd service will be restarted every time TLS is activated or @@ -236,6 +252,10 @@ See [Client key and certificate](#client-key-and-certificate) for an example of how to remotely run docker commands on a device running a secured Docker Compose ACAP using TLS. +The application can provide a TCP socket if the TCP Socket setting is set to `yes` and an IPC socket +if the IPC Socket setting is set to `yes`. Please be aware that at least one of these sockets must be +selected for the application to start. + ## Building the Docker Compose ACAP To build the Docker Compose ACAP use docker buildx with the provided Dockerfile: diff --git a/app/dockerdwrapperwithcompose.c b/app/dockerdwrapperwithcompose.c index 6d64508..703a165 100644 --- a/app/dockerdwrapperwithcompose.c +++ b/app/dockerdwrapperwithcompose.c @@ -29,6 +29,7 @@ struct settings { char *data_root; bool use_tls; + bool use_tcp_socket; bool use_ipc_socket; }; @@ -55,7 +56,10 @@ static const char *dockerd_path_on_sd_card = "/var/spool/storage/SD_DISK/dockerd"; // All ax_parameters the acap has -static const char *ax_parameters[] = {"IPCSocket", "SDCardSupport", "UseTLS"}; +static const char *ax_parameters[] = {"IPCSocket", + "SDCardSupport", + "TCPSocket", + "UseTLS"}; static const char *tls_cert_path = "/usr/local/packages/dockerdwrapperwithcompose/"; @@ -352,6 +356,19 @@ get_and_verify_tls_selection(bool *use_tls_ret) return return_value; } +/** + * @brief Gets and verifies the TCPSocket selection + * + * @param use_tcp_socket_ret selection to be updated. + * @return True if successful, false otherwise. + */ +static gboolean +get_tcp_socket_selection(bool *use_tcp_socket_ret) +{ + *use_tcp_socket_ret = is_parameter_yes("TCPSocket"); + return true; +} + /** * @brief Gets and verifies the IPCSocket selection * @@ -376,6 +393,10 @@ read_settings(struct settings *settings) syslog(LOG_ERR, "Failed to verify tls selection"); return false; } + if (!get_tcp_socket_selection(&settings->use_tcp_socket)) { + syslog(LOG_ERR, "Failed to get tcp socket selection"); + return false; + } if (!get_ipc_socket_selection(&settings->use_ipc_socket)) { syslog(LOG_ERR, "Failed to get ipc socket selection"); return false; @@ -390,6 +411,7 @@ start_dockerd(const struct settings *settings) { const char *data_root = settings->data_root; const bool use_tls = settings->use_tls; + const bool use_tcp_socket = settings->use_tcp_socket; const bool use_ipc_socket = settings->use_ipc_socket; GError *error = NULL; @@ -414,39 +436,58 @@ start_dockerd(const struct settings *settings) g_strlcpy(msg, "Starting dockerd", msg_len); - if (use_tls) { - const char *ca_path = - "/usr/local/packages/dockerdwrapperwithcompose/ca.pem"; - const char *cert_path = - "/usr/local/packages/dockerdwrapperwithcompose/server-cert.pem"; - const char *key_path = - "/usr/local/packages/dockerdwrapperwithcompose/server-key.pem"; + if (!use_ipc_socket && !use_tcp_socket) { + syslog(LOG_ERR, + "At least one of IPC socket or TCP socket must be set to \"yes\". " + "dockerd will not be started."); + goto end; + } + if (use_ipc_socket) { + g_strlcat(msg, " with IPC socket and", msg_len); args_offset += g_snprintf(args + args_offset, args_len - args_offset, - " %s %s %s %s %s %s %s %s", - "-H tcp://0.0.0.0:2376", - "--tlsverify", - "--tlscacert", - ca_path, - "--tlscert", - cert_path, - "--tlskey", - key_path); - - g_strlcat(msg, " in TLS mode", msg_len); + " -H unix:///var/run/docker.sock"); } else { + g_strlcat(msg, " without IPC socket and", msg_len); + } + + if (use_tcp_socket) { + g_strlcat(msg, " with TCP socket", msg_len); + const uint port = use_tls ? 2376 : 2375; args_offset += g_snprintf(args + args_offset, args_len - args_offset, - " %s %s", - "-H tcp://0.0.0.0:2375", - "--tls=false"); - - g_strlcat(msg, " in unsecured mode", msg_len); + " -H tcp://0.0.0.0:%d", + port); + if (use_tls) { + const char *ca_path = + "/usr/local/packages/dockerdwrapperwithcompose/ca.pem"; + const char *cert_path = + "/usr/local/packages/dockerdwrapperwithcompose/server-cert.pem"; + const char *key_path = + "/usr/local/packages/dockerdwrapperwithcompose/server-key.pem"; + args_offset += g_snprintf(args + args_offset, + args_len - args_offset, + " %s %s %s %s %s %s %s", + "--tlsverify", + "--tlscacert", + ca_path, + "--tlscert", + cert_path, + "--tlskey", + key_path); + g_strlcat(msg, " in TLS mode", msg_len); + } else { + args_offset += g_snprintf( + args + args_offset, args_len - args_offset, " --tls=false"); + g_strlcat(msg, " in unsecured mode", msg_len); + } + } else { + g_strlcat(msg, " without TCP socket", msg_len); } g_autofree char *data_root_msg = g_strdup_printf( - " using %s as storage", data_root ? data_root : "/var/lib/docker"); + " using %s as storage.", data_root ? data_root : "/var/lib/docker"); g_strlcat(msg, data_root_msg, msg_len); if (data_root) args_offset += g_snprintf(args + args_offset, @@ -454,15 +495,6 @@ start_dockerd(const struct settings *settings) " --data-root %s", data_root); - if (use_ipc_socket) { - g_strlcat(msg, " with IPC socket.", msg_len); - args_offset += g_snprintf(args + args_offset, - args_len - args_offset, - " -H unix:///var/run/docker.sock"); - } else { - g_strlcat(msg, " without IPC socket.", msg_len); - } - // Log startup information to syslog. syslog(LOG_INFO, "%s", msg); diff --git a/app/manifest.json b/app/manifest.json index c801908..3f7fadd 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -33,6 +33,11 @@ "default": "yes", "type": "enum:no|No, yes|Yes" }, + { + "name": "TCPSocket", + "default": "yes", + "type": "enum:no|No, yes|Yes" + }, { "name": "IPCSocket", "default": "yes", From 25c97da877b46d4816f8e9355e84c067aee43c1f Mon Sep 17 00:00:00 2001 From: Mattias Axelsson Date: Tue, 26 Mar 2024 11:53:16 +0100 Subject: [PATCH 16/16] Pass around SD card area instead of using global constant * Pass around data root in settings struct --------- Co-authored-by: madelen-at-work --- app/dockerdwrapperwithcompose.c | 86 +++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 36 deletions(-) diff --git a/app/dockerdwrapperwithcompose.c b/app/dockerdwrapperwithcompose.c index 703a165..04f70f4 100644 --- a/app/dockerdwrapperwithcompose.c +++ b/app/dockerdwrapperwithcompose.c @@ -33,6 +33,10 @@ struct settings { bool use_ipc_socket; }; +struct app_state { + char *sd_card_area; +}; + /** * @brief Callback called when the dockerd process exits. */ @@ -51,10 +55,6 @@ static int application_exit_code = EX_KEEP_RUNNING; // Pid of the running dockerd process static pid_t dockerd_process_pid = -1; -// Full path to the SD card -static const char *dockerd_path_on_sd_card = - "/var/spool/storage/SD_DISK/dockerd"; - // All ax_parameters the acap has static const char *ax_parameters[] = {"IPCSocket", "SDCardSupport", @@ -276,25 +276,31 @@ is_parameter_yes(const char *name) return value && strcmp(value, "yes") == 0; } -/** - * @brief Gets and verifies the SDCardSupport selection - * - * @param use_sdcard_ret selection to be updated. - * @return True if successful, false otherwise. - */ -static gboolean -get_and_verify_sd_card_selection(char **data_root) +// Return data root matching the current SDCardSupport selection. +// +// If SDCardSupport is "yes", data root will be located on the proved SD card +// area. Passing NULL as SD card area signals that the SD card is not availble. +static char * +prepare_data_root(const char *sd_card_area) { if (is_parameter_yes("SDCardSupport")) { - *data_root = g_strdup_printf("%s/data", dockerd_path_on_sd_card); - if (!setup_sdcard(*data_root)) { + if (!sd_card_area) { + syslog( + LOG_ERR, + "SD card was requested, but no SD card is available at the moment."); + return NULL; + } + char *data_root = g_strdup_printf("%s/data", sd_card_area); + if (!setup_sdcard(data_root)) { syslog(LOG_ERR, "Failed to setup SD card."); - return false; + free(data_root); + return NULL; } + return data_root; } else { - *data_root = NULL; + return strdup( + "/var/lib/docker"); // Same location as if --data-root is omitted } - return true; } /** @@ -383,12 +389,8 @@ get_ipc_socket_selection(bool *use_ipc_socket_ret) } static bool -read_settings(struct settings *settings) +read_settings(struct settings *settings, const struct app_state *app_state) { - if (!get_and_verify_sd_card_selection(&settings->data_root)) { - syslog(LOG_ERR, "Failed to setup sd_card"); - return false; - } if (!get_and_verify_tls_selection(&settings->use_tls)) { syslog(LOG_ERR, "Failed to verify tls selection"); return false; @@ -401,13 +403,17 @@ read_settings(struct settings *settings) syslog(LOG_ERR, "Failed to get ipc socket selection"); return false; } + if (!(settings->data_root = prepare_data_root(app_state->sd_card_area))) { + syslog(LOG_ERR, "Failed to set up dockerd data root"); + return false; + } return true; } // Return true if dockerd was successfully started. // Log an error and return false if it failed to start properly. static bool -start_dockerd(const struct settings *settings) +start_dockerd(const struct settings *settings, struct app_state *app_state) { const char *data_root = settings->data_root; const bool use_tls = settings->use_tls; @@ -486,14 +492,11 @@ start_dockerd(const struct settings *settings) g_strlcat(msg, " without TCP socket", msg_len); } - g_autofree char *data_root_msg = g_strdup_printf( - " using %s as storage.", data_root ? data_root : "/var/lib/docker"); + g_autofree char *data_root_msg = + g_strdup_printf(" using %s as storage.", data_root); g_strlcat(msg, data_root_msg, msg_len); - if (data_root) - args_offset += g_snprintf(args + args_offset, - args_len - args_offset, - " --data-root %s", - data_root); + args_offset += g_snprintf( + args + args_offset, args_len - args_offset, " --data-root %s", data_root); // Log startup information to syslog. syslog(LOG_INFO, "%s", msg); @@ -516,7 +519,8 @@ start_dockerd(const struct settings *settings) } // Watch the child process. - g_child_watch_add(dockerd_process_pid, dockerd_process_exited_callback, NULL); + g_child_watch_add( + dockerd_process_pid, dockerd_process_exited_callback, app_state); if (!is_process_alive(dockerd_process_pid)) { syslog(LOG_ERR, @@ -533,10 +537,13 @@ start_dockerd(const struct settings *settings) } static bool -read_settings_and_start_dockerd(void) +read_settings_and_start_dockerd(struct app_state *app_state) { struct settings settings = {0}; - bool success = read_settings(&settings) && start_dockerd(&settings); + + bool success = read_settings(&settings, app_state) && + start_dockerd(&settings, app_state); + free(settings.data_root); return success; } @@ -589,10 +596,11 @@ stop_dockerd(void) * @brief Callback called when the dockerd process exits. */ static void -dockerd_process_exited_callback(__attribute__((unused)) GPid pid, +dockerd_process_exited_callback(GPid pid, gint status, - __attribute__((unused)) gpointer user_data) + gpointer app_state_void_ptr) { + struct app_state *app_state = app_state_void_ptr; GError *error = NULL; if (!g_spawn_check_exit_status(status, &error)) { syslog(LOG_ERR, "Dockerd process exited with error: %d", status); @@ -674,6 +682,9 @@ setup_axparameter(void) int main(void) { + struct app_state app_state = {0}; + app_state.sd_card_area = strdup("/var/spool/storage/SD_DISK/dockerd"); + AXParameter *ax_parameter = NULL; openlog(NULL, LOG_PID, LOG_USER); @@ -692,7 +703,9 @@ main(void) } while (application_exit_code == EX_KEEP_RUNNING) { - if (dockerd_process_pid == -1 && !read_settings_and_start_dockerd()) + if (dockerd_process_pid == -1 && + !read_settings_and_start_dockerd(&app_state)) + quit_program(EX_SOFTWARE); g_main_loop_run(loop); @@ -714,5 +727,6 @@ main(void) ax_parameter_free(ax_parameter); } + free(app_state.sd_card_area); return application_exit_code; }