Skip to content

Commit 72d54dc

Browse files
committed
feat: add mqtt support
1 parent 4741deb commit 72d54dc

17 files changed

+560
-284
lines changed

.build.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ include:
22
remote: "https://github.com/git-developer/docker-support/raw/v3.1.0/gitlab-ci/docker-template.yml"
33

44
variables:
5-
IMAGE_NAME: "${CI_PROJECT_NAMESPACE}/usbip-${SERVICE}"
5+
IMAGE_NAME: "${CI_PROJECT_NAMESPACE}/rudy-${SERVICE}"
66
IMAGE_VERSION: "${DISTRO}"
77
IMAGE_PLATFORMS: 'linux/amd64,linux/i386,linux/arm64,linux/arm'
88
BUILD_ARGS: "SERVICE=${SERVICE}"

README.md

+145-77
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,31 @@
1-
# usbip-docker
2-
[USB/IP](http://usbip.sourceforge.net/) in Docker
1+
# RUDY
2+
RUDY manages **R**emote **U**SB **D**evices easil**Y**.
33

4-
This project provides Docker images to run a USB/IP server and client in Docker containers,
4+
This project provides Docker images that integrate USB/IP with MQTT,
55
so that USB devices can be used remotely with minimal configuration.
66

7+
## Key features
8+
- Use USB devices on a remote host
9+
- Allow automatic error handling
10+
- Allow integration with udev
11+
712
## Server
8-
### Example
13+
### Example Configuration
914
```yaml
1015
---
1116
services:
12-
usbip-server:
13-
image: ckware/usbip-server
14-
container_name: usbip-server
17+
rudy-server:
18+
image: ckware/rudy-server
19+
container_name: rudy-server
1520
init: true
1621
restart: unless-stopped
1722
ports:
1823
- "3240:3240"
1924
environment:
2025
USBIP_DEVICE_IDS: "0000:0000"
26+
MQTT_OPTIONS: "-h broker-host"
27+
MQTT_PUBLISH_TOPIC: "rudy/server"
28+
MQTT_RELOAD_ON_TOPIC: "rudy/client/error/server"
2129
volumes:
2230
- "/sys/bus/usb/drivers/usb:/sys/bus/usb/drivers/usb"
2331
- "/sys/bus/usb/drivers/usbip-host:/sys/bus/usb/drivers/usbip-host"
@@ -30,28 +38,24 @@ services:
3038
```shell
3139
$ sudo sh -c 'modprobe usbip-host && echo usbip-host >>/etc/modules'
3240
```
33-
1. Create a configuration file `docker-compose.yml` (see the examples and docs for an inspiration)
41+
1. Create a configuration file `docker-compose.yml` (see the example for an inspiration)
3442

3543
#### Lifecycle commands
36-
All commands assume that the container is named `usbip-server`.
37-
38-
* Start the server: `docker-compose up -d`
39-
* Stop the server: `docker-compose down`
40-
* Restart the server:
41-
42-
`docker-compose exec usbip-server restart`
43-
* List all devices connected to the server host:
44-
45-
`docker exec usbip-server usbip list -l`
46-
* List devices available for clients:
47-
48-
`docker exec usbip-server usbip list -r localhost`
49-
50-
### Required Docker configuration options
44+
All commands assume that the container is named `rudy-server`.
45+
| Action | Command
46+
| ------ | -------
47+
| Start the server | `docker-compose up -d`
48+
| Stop the server | `docker-compose down`
49+
| View the server logs | `docker logs -t rudy-server`
50+
| Reload the server | `docker exec rudy-server reload`
51+
| List all devices connected to the server host | `docker exec rudy-server usbip list -l`
52+
| List devices available for clients | `docker exec rudy-server usbip list -r localhost`
53+
54+
### Docker configuration options
5155
| Option | Description | Recommendation | Explanation |
5256
| ------------- | ---------------------- | -------------- | ----------- |
53-
| `ports` | Network port | `3240:3240` | Network port for client communication.
54-
| `volumes` | Volumes for USB access | `/sys:/sys` | Access to USB and other devices.
57+
| `ports` | Network port | `3240:3240` | Network port for USB/IP client communication.
58+
| `volumes` | Volumes for USB access | `/sys:/sys` | Access to USB and other related devices.
5559

5660
### Environment variables
5761
See _Common environment variables_
@@ -60,14 +64,19 @@ See _Common environment variables_
6064
### Example
6165
```yaml
6266
services:
63-
usbip-client:
64-
image: ckware/usbip-client
65-
container_name: usbip-client
67+
rudy-client:
68+
image: ckware/rudy-client
69+
container_name: rudy-client
6670
init: true
6771
restart: unless-stopped
6872
environment:
6973
USBIP_SERVER: "server-host"
7074
USBIP_DEVICE_IDS: "0000:0000"
75+
MQTT_OPTIONS: "-h broker-host"
76+
MQTT_PUBLISH_TOPIC: "rudy/client"
77+
MQTT_RELOAD_ON_TOPIC: "rudy/server/start"
78+
volumes:
79+
- "/var/run/vhci_hcd:/var/run/vhci_hcd"
7180
privileged: true
7281
```
7382

@@ -77,39 +86,47 @@ services:
7786
```shell
7887
$ sudo sh -c 'modprobe vhci-hcd && echo vhci-hcd >>/etc/modules'
7988
```
80-
1. Create a configuration file `docker-compose.yml` (see the examples and docs for an inspiration)
89+
1. Create a configuration file `docker-compose.yml` (see the example for an inspiration)
8190

8291
#### Lifecycle commands
83-
All commands assume that the container is named `usbip-client`.
84-
85-
* Start the client: `docker-compose up -d`
86-
* Stop the client: `docker-compose down`
87-
* Restart the client:
88-
89-
`docker-compose exec usbip-client restart`
90-
* List devices currently managed by the client:
91-
92-
`docker exec usbip-client usbip port`
93-
94-
### Required Docker configuration options
95-
| Option | Description | Required | Explanation |
96-
| ------------- | --------------- | -------- | ----------- |
97-
| `privileged` | Root privileges | `true` | Root privileges are **required** to write to `/sys/` (see issue [#22825](https://github.com/moby/moby/issues/22825) for details).
92+
All commands assume that the container is named `rudy-client`.
93+
94+
| Action | Command
95+
| ------ | -------
96+
| Start the client | `docker-compose up -d`
97+
| Stop the client | `docker-compose down`
98+
| View the client logs | `docker logs -t rudy-client`
99+
| Reload the client | `docker exec rudy-client reload`
100+
| List devices currently managed by the client | `docker exec rudy-client usbip port`
101+
102+
### Docker configuration options
103+
| Option | Description | Recommendation | Explanation |
104+
| ------------- | --------------------- | ------------------------------------- | ----------- |
105+
| `volumes` | Volume for port state | `/var/run/vhci_hcd:/var/run/vhci_hcd` | USB/IP manages the state of attached ports within this directory. It reflects a part of the kernel state which affects all clients and should thus be shared across the host and all clients.
106+
| `privileged` | Root privileges | `true` | Root privileges are **required** to write to `/sys/` (see issue [#22825](https://github.com/moby/moby/issues/22825) for details).
98107

99108
### Environment variables
100109
All _Common environment variables_ and:
101110

102-
| Option | Description | Example | Explanation |
103-
| ------------------ | ---------------------- | ------------- | ----------- |
104-
| `USBIP_SERVER` | USB/IP server hostname | `server-host` | Hostname of the USB/IP server
105-
111+
| Variable | Description | Supported values | Default | Example | Explanation
112+
| ---------------------- | ---------------------- | ---------------- | ------- | ------------- | -----------
113+
| `USBIP_SERVER` | USB/IP server hostname | Hostnames | _none_ | `server-host` | Hostname of the USB/IP server
114+
| `USBIP_PORT` | USB/IP server port | IP port numbers | 3240 | `13240` | Port of the USB/IP server
115+
| `USBIP_DETACH_ORPHANS` | Detach dangling ports | `true` / `false` | `true` | `false` | When set to `true` (which is the default), all attached devices that match `USBIP_DEVICE_IDS` are detached on shutdown. This option has no effect when `USBIP_DEVICE_IDS` is not set. It can help to repair the USB/IP state when client and server are not in sync.
116+
| `USBIP_DETACH_ALL` | Detach all ports | `true` / `false` | `false` | `true` | When set to `true`, all attached devices are detached on shutdown, no matter what their device id or bus id is. This option affects all clients on a host. It is safe to use when the host runs only one client; otherwise, all clients should be reloaded after the client which has this option set.
106117

107118
## Common environment variables
108-
| Option | Description | Example | Explanation |
109-
| ------------------ | ---------------------- | --------------------- | ----------- |
110-
| `USBIP_DEVICE_IDS` | List of USB device IDs | `0000:0000,1111:1111` | Comma-separated device id list of managed USB devices (format VID:PID). This option does not support more than one device per ID.
111-
| `USBIP_BUS_IDS` | List of USB bus IDs | `1-1.1,2-2.2` | Comma-separated id list of managed USB devices (format: logical bus ID). This option can be used to use multiple devices with the same device id. The logical bus id of a device will change when it is plugged into a different USB port.
112-
| `USBIP_DEBUG` | Enable debug logging | `true` | When this option is set to any non-empty value, debug logging is enabled.
119+
| Variable | Description | Supported values | Default | Example | Explanation
120+
| ------------------ | ---------------------- | ------------------------- | ------- | --------------------- | -----------
121+
| `USBIP_DEVICE_IDS` | List of USB device IDs | `VID:PID` list | _none_ | `0000:0000,1111:1111` | Comma-separated Device-ID list of managed USB devices. This option does not support more than one device per ID.
122+
| `USBIP_BUS_IDS` | List of USB bus IDs | List of logical Bus IDs | _none_ | `1-1.1,2-2.2` | Comma-separated ID list of managed USB devices. This option can be used to use multiple devices with the same Device-ID. The logical Bus-ID of a device will change when it is plugged into a different USB port.
123+
| `RUDY_DEBUG` | Enable debug logging | String | _none_ | `true` | When this option is set to any non-empty value, debug logging is enabled.
124+
| `RUDY_TRACE` | Enable trace logging | String | _none_ | `true` | When this option is set to any non-empty value, trace logging is enabled.
125+
| `MQTT_OPTIONS` | Common MQTT options | All options [supported by `mosquitto_pub`](https://mosquitto.org/man/mosquitto_pub-1.html) | _none_ | `-h broker-host -i rudy-client` | Default MQTT options for both publish and subscribe
126+
| `MQTT_PUBLISH_TO_TOPIC` | MQTT topic for publishing | [Topic names](http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718106) | _none_ | `rudy/client` | When this option is set, the service will publish its state below the configured topic.
127+
| `MQTT_PUBLISH_OPTIONS` | MQTT options for publish | All options [supported by `mosquitto_pub`](https://mosquitto.org/man/mosquitto_pub-1.html) | _none_ | `-q 1` | MQTT options for publishing only. This option overrides `MQTT_OPTIONS`: when set, `MQTT_OPTIONS` is ignored for publishing.
128+
| `MQTT_RELOAD_ON_TOPIC` | MQTT topic for a reload hook | [Topic names](http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718106) | _none_ | `rudy/server/start` | When this option is set, the service will subscribe to the configured topic and reload if a message is published to it.
129+
| `MQTT_SUBSCRIBE_OPTIONS` | MQTT options for subscribe | All options [supported by `mosquitto_pub`](https://mosquitto.org/man/mosquitto_pub-1.html) | _none_ | `-q 2` | MQTT options for subscribing only. This option overrides `MQTT_OPTIONS`: when set, `MQTT_OPTIONS` is ignored for subscribing.
113130

114131
Only one of `USBIP_DEVICE_IDS` and `USBIP_BUS_IDS` is required; when both variables are given, `USBIP_BUS_IDS` is preferred.
115132

@@ -124,27 +141,78 @@ On debian-based systems, `docker-compose` may be installed by calling
124141
$ sudo apt install docker-compose
125142
```
126143

127-
## Pitfalls
128-
### Connection issues
129-
When the server is stopped, a connected client loses all managed devices silently.
130-
This scenario may be verified by requesting the list of imported USB devices on the client, which is empty in this case:
131-
```shell
132-
$ docker-compose exec usbip-client usbip port
133-
Imported USB devices
134-
====================
135-
$ _
136-
```
137-
138-
To reconnect after the server is up again, run on the client:
139-
140-
```shell
141-
$ docker-compose exec usbip-client restart
142-
```
143-
After that, the list of managed devices should be no longer empty:
144-
```shell
145-
$ docker-compose exec usbip-client usbip port
146-
Imported USB devices
147-
====================
148-
Port 00: <Port in Use> at High Speed(480Mbps)
149-
...
150-
```
144+
## Advanced topics
145+
### MQTT publishing
146+
Configure `MQTT_PUBLISH_TOPIC` to publish client and server states via MQTT.
147+
148+
Example:
149+
- Server configuration:
150+
```yaml
151+
environment:
152+
USBIP_DEVICE_IDS: "4971:1011"
153+
MQTT_PUBLISH_TOPIC: "rudy/server"
154+
```
155+
- Client configuration:
156+
```yaml
157+
environment:
158+
USBIP_DEVICE_IDS: "4971:1011"
159+
MQTT_PUBLISH_TOPIC: "rudy/client"
160+
```
161+
- MQTT messages published on starting server and client and then stopping client and server:
162+
```
163+
rudy/server/bind 1-1.4
164+
rudy/server/start Exportable USB devices
165+
======================
166+
- localhost
167+
1-1.4: SimpleTech : unknown product (4971:1011)
168+
: /sys/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.4
169+
: (Defined at Interface level) (00/00/00)
170+
rudy/client/attach 0: server-host 3240 1-1.4
171+
rudy/client/start
172+
rudy/client/detach 0
173+
rudy/client/stop
174+
rudy/server/unbind 1-1.4
175+
rudy/server/stop
176+
```
177+
178+
### Prepare for Client Errors
179+
Configure `MQTT_RELOAD_ON_TOPIC` on the server to reload the server when the client reports a server-related error.
180+
In combination with the Docker `restart` option, this may help to solve some problems without user interaction.
181+
182+
Example:
183+
- Server configuration:
184+
```yaml
185+
environment:
186+
MQTT_RELOAD_ON_TOPIC: "rudy/client/error/server"
187+
```
188+
- Client configuration:
189+
```yaml
190+
restart: unless-stopped
191+
environment:
192+
MQTT_PUBLISH_TOPIC: "rudy/client"
193+
```
194+
195+
### Prepare Client for Server Restart
196+
Configure `MQTT_RELOAD_ON_TOPIC` on the client to reload the client each time the server is restarted.
197+
198+
Example:
199+
- Server configuration:
200+
```yaml
201+
environment:
202+
USBIP_DEVICE_IDS: "4971:1011"
203+
MQTT_PUBLISH_TOPIC: "rudy/server"
204+
```
205+
- Client configuration:
206+
```yaml
207+
environment:
208+
USBIP_SERVER: "server-host"
209+
USBIP_DEVICE_IDS: "4971:1011"
210+
MQTT_RELOAD_ON_TOPIC: "rudy/server/start"
211+
```
212+
213+
## References
214+
* This project is an integration of
215+
* [USB/IP](http://usbip.sourceforge.net/) - USB Request over IP Network
216+
* [Mosquitto](https://mosquitto.org/) - An Open Source MQTT Broker
217+
* The [OCI image](https://github.com/opencontainers/image-spec) format
218+
* [Docker](https://www.docker.com)

build

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
#!/bin/sh
22
set -eu
3+
IMAGE_PREFIX="${IMAGE_PREFIX:-ckware/rudy}"
34
distros="${1:-.*}"
4-
image_prefix='ckware/usbip'
55
build_context="$(dirname "${0}")/image"
66
find "${build_context}" -maxdepth 1 -regex "${build_context}/Dockerfile\.${distros}" -printf '%f\n' \
77
| while read file; do
88
distro="${file##Dockerfile.}"
99
find "${build_context}" -mindepth 1 -maxdepth 1 -type d ! -name common -printf '%f\n' \
1010
| while read service; do
1111
docker build \
12-
-t "${image_prefix}-${service}:${distro}" \
12+
-t "${IMAGE_PREFIX}-${service}:${distro}" \
1313
-f "${build_context}/Dockerfile.${distro}" \
1414
--build-arg "SERVICE=${service}" \
1515
"${build_context}"

docker-compose.yml.example-client

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
1+
---
12
services:
2-
usbip-client:
3+
rudy-client:
34
#
45
# host requirement: kernel module `vhci-hcd`
56
# - command to load until next boot: `modprobe vhci-hcd`
67
# - command to load after next boot: `echo vhci-hcd >>/etc/modules`
78
#
89

9-
image: ckware/usbip-client
10-
container_name: usbip-client
10+
image: ckware/rudy-client
11+
container_name: rudy-client
1112
init: true
1213
restart: unless-stopped
1314
environment:
1415
USBIP_SERVER: "server-host"
15-
# USBIP_DEVICE_IDS: "0000:0000,1111:1111"
16-
USBIP_BUS_IDS: "1-1.1,2-2.2"
17-
# USBIP_DEBUG: "true"
16+
USBIP_DEVICE_IDS: "0000:0000,1111:1111"
17+
MQTT_OPTIONS: "-h broker-host"
18+
MQTT_PUBLISH_TOPIC: "rudy/client"
19+
MQTT_RELOAD_ON_TOPIC: "rudy/server/start"
20+
volumes:
21+
- "/var/run/vhci_hcd:/var/run/vhci_hcd"
1822

1923
#
2024
# privileged mode is required to write to

docker-compose.yml.example-server

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
1+
---
12
services:
2-
usbip-server:
3+
rudy-server:
34
#
45
# host requirement: kernel module `usbip-host`
56
# - command to load until next boot: `modprobe usbip-host`
67
# - command to load after next boot: `echo usbip-host >>/etc/modules`
78
#
89

9-
image: ckware/usbip-server
10-
container_name: usbip-server
10+
image: ckware/rudy-server
11+
container_name: rudy-server
1112
init: true
1213
restart: unless-stopped
1314
ports:
1415
- "3240:3240"
1516
environment:
1617
USBIP_DEVICE_IDS: "0000:0000,1111:1111"
17-
# USBIP_BUS_IDS: "1-1.1,2-2.2"
18-
# USBIP_DEBUG: "true"
18+
MQTT_OPTIONS: "-h broker-host"
19+
MQTT_PUBLISH_TOPIC: "rudy/server"
20+
MQTT_RELOAD_ON_TOPIC: "rudy/client/error/server"
1921
volumes:
2022
- "/sys/bus/usb/drivers/usb:/sys/bus/usb/drivers/usb"
2123
- "/sys/bus/usb/drivers/usbip-host:/sys/bus/usb/drivers/usbip-host"

image/Dockerfile.alpine

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
FROM alpine
2-
RUN apk update && apk add --no-cache linux-tools-usbip
2+
RUN apk update && apk add --no-cache linux-tools-usbip mosquitto-clients
33
RUN if [ ! -e /usr/share/hwdata/usb.ids ]; then mkdir -p /usr/share/hwdata && ln -s /dev/null /usr/share/hwdata/usb.ids; fi
44
ARG SERVICE
5-
ENV USBIP_HOME="/opt/usbip-${SERVICE}"
6-
ENV PATH="${PATH}:${USBIP_HOME}"
7-
COPY common/* "${SERVICE}/init" "${USBIP_HOME}/"
8-
CMD exec "${USBIP_HOME}/init"
5+
ENV RUDY_HOME="/opt/rudy-${SERVICE}"
6+
ENV PATH="${PATH}:${RUDY_HOME}"
7+
COPY common/* "${SERVICE}/run" "${RUDY_HOME}/"
8+
CMD exec "${RUDY_HOME}/run"

image/Dockerfile.bullseye

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
FROM debian:bullseye-slim
2-
RUN apt-get update && apt-get install -y usbip && apt-get clean
2+
RUN apt-get update && apt-get install -y usbip mosquitto-clients && apt-get clean
33
ARG SERVICE
4-
ENV USBIP_HOME="/opt/usbip-${SERVICE}"
5-
ENV PATH="${PATH}:${USBIP_HOME}"
6-
COPY common/* "${SERVICE}/init" "${USBIP_HOME}/"
7-
CMD exec "${USBIP_HOME}/init"
4+
ENV RUDY_HOME="/opt/rudy-${SERVICE}"
5+
ENV PATH="${PATH}:${RUDY_HOME}"
6+
COPY common/* "${SERVICE}/run" "${RUDY_HOME}/"
7+
CMD exec "${RUDY_HOME}/run"

0 commit comments

Comments
 (0)