Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 123 additions & 0 deletions .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
name: Build and Publish Docker Image

on:
push:
workflow_dispatch:

jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- platform: linux/amd64
os: ubuntu-latest

- platform: linux/arm64
os: ubuntu-24.04-arm
permissions:
contents: read
packages: write
steps:
- name: Prepare
run: |
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV

- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Create lowercased repo env
run: |
echo "REPO=${GITHUB_REPOSITORY@L}" >> "${GITHUB_ENV}"

- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ env.REPO }}
tags: |
type=ref,event=branch
type=raw,value=latest,enable={{is_default_branch}}

- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
with:
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
tags: ghcr.io/${{ env.REPO }}
outputs: type=image,push-by-digest=true,name-canonical=true,push=true

- name: Export digest
run: |
mkdir -p ${{ runner.temp }}/digests
digest="${{ steps.build.outputs.digest }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"

- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digests-${{ env.PLATFORM_PAIR }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
retention-days: 1

merge:
runs-on: ubuntu-latest
needs:
- build
steps:
- name: Download digests
uses: actions/download-artifact@v4
with:
path: ${{ runner.temp }}/digests
pattern: digests-*
merge-multiple: true

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Create lowercased repo env
run: |
echo "REPO=${GITHUB_REPOSITORY@L}" >> "${GITHUB_ENV}"

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ env.REPO }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=raw,value=latest,enable={{is_default_branch}}
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}

- name: Create manifest list and push
working-directory: ${{ runner.temp }}/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf 'ghcr.io/${{ env.REPO }}@sha256:%s ' *)

- name: Inspect image
run: |
docker buildx imagetools inspect ghcr.io/${{ env.REPO }}:${{ steps.meta.outputs.version }}
46 changes: 46 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
FROM debian:bookworm-slim AS build

# Prevent initramfs and ldconfig runs in CI, speeds up the build
RUN dpkg-divert --local --rename --add /usr/sbin/update-initramfs \
&& dpkg-divert --local --rename --add /sbin/ldconfig \
&& dpkg-divert --local --rename --add /usr/sbin/ldconfig \
&& ln -sf /bin/true /usr/sbin/update-initramfs \
&& ln -sf /bin/true /sbin/ldconfig \
&& ln -sf /bin/true /usr/sbin/ldconfig

# Install build dependencies
# eatmydata ignores fsck calls which arent needed in CI, speeds up the build
RUN apt update \
&& apt install -o APT::Install-Suggests=false -y eatmydata \
&& eatmydata apt install -o APT::Install-Suggests=false -y \
gcc \
meson \
libsocketcan-dev \
libconfig-dev

# Build and strip
WORKDIR /src
COPY . .
RUN meson setup -Dlibconfig=true --buildtype=release build \
&& meson compile -C build \
&& meson install -C build

# Collect only needed runtime files (arch-independent)
RUN mkdir -p /minimal-root/usr/local/sbin /minimal-root/lib /minimal-root/lib64 /minimal-root/etc \
# The binary
&& cp /usr/local/sbin/socketcand /minimal-root/usr/local/sbin/ \
# Get interpreter from ELF header and copy
&& interp=$(readelf -l /usr/local/sbin/socketcand | awk -F ': ' '/interpreter/ {print $2}' | tr -d ']') \
&& mkdir -p "/minimal-root$(dirname $interp)" \
&& cp "$interp" "/minimal-root$interp" \
# Copy linked shared libraries
&& ldd /usr/local/sbin/socketcand | awk '{print $3}' | grep '^/' | sort -u | xargs -I '{}' cp -v --parents '{}' /minimal-root/ \
# Minimal /etc
&& cp -a /etc/nsswitch.conf /minimal-root/etc/ \
&& cp -a /etc/hosts /minimal-root/etc/ \
&& cp -a /etc/resolv.conf /minimal-root/etc/

# Build minimal image
FROM scratch
COPY --from=build /minimal-root/ /
ENTRYPOINT ["/usr/local/sbin/socketcand"]
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,14 @@ Execute the following commands to configure, build, and install the software:
$ meson compile -C build
$ meson install -C build

Service discovery
-----------------

The daemon uses a simple UDP beacon mechanism for service discovery. A beacon containing the service name, type and address is sent to the broadcast address (port 42000) at minimum every 3 seconds. A client only has to listen for messages of this type to detect all SocketCAN daemons in the local network.
Docker image
------------
Prebuild docker images are available.
Note this image still needs a host kernel with SocketCAN modules.
The CAN interface needs to be configured on the host and made available to the container using the `--network=host` option.

Example usage:
`docker run --rm --network=host -it ghcr.io/linux-can/socketcand:latest -v -i can0`

Usage
-----
Expand All @@ -46,6 +50,11 @@ Usage
* **-d** (set this flag if you want log to syslog instead of STDOUT)
* **-h** (prints this message)

Service discovery
-----------------

The daemon uses a simple UDP beacon mechanism for service discovery. A beacon containing the service name, type and address is sent to the broadcast address (port 42000) at minimum every 3 seconds. A client only has to listen for messages of this type to detect all SocketCAN daemons in the local network.

License
-------

Expand Down
Loading