diff --git a/README.md b/README.md index ae0c9e9..4b895ad 100644 --- a/README.md +++ b/README.md @@ -235,76 +235,123 @@ start ipp-usb when needed. sudo snap install docker ``` -#### Step-by-Step Guide +#### **Running the `ipp-usb` Container with Persistent Storage** -You can pull the `ipp-usb` Docker image from the GitHub Container Registry. +To run the `ipp-usb` container while ensuring that its state persists across restarts, follow these steps. -**From GitHub Container Registry**
-To pull the image from the GitHub Container Registry, run the following command: +#### **1. Pull the `ipp-usb` Docker Image** +The latest image is available on the GitHub Container Registry. Pull it using: ```sh - sudo docker pull ghcr.io/openprinting/ipp-usb:latest +sudo docker pull ghcr.io/openprinting/ipp-usb:latest ``` -To run the container after pulling the image, use: +#### **2. Create Persistent Storage Volumes** +To ensure that configuration settings and printer state persist across container restarts, create two Docker volumes: + ```sh - sudo docker run -d --network host \ - -v /dev/bus/usb:/dev/bus/usb:ro \ - --device-cgroup-rule='c 189:* rmw' \ - --name ipp-usb \ - ghcr.io/openprinting/ipp-usb:latest -``` +sudo docker volume create ipp-usb-state +sudo docker volume create ipp-usb-conf +``` + +These volumes store: -- `--network host`: Uses the host network, ensuring IPP-over-USB and Avahi service discovery work correctly. -- `-v /dev/bus/usb:/dev/bus/usb:ro`: Grants the container read-only access to USB devices. -- `--device-cgroup-rule='c 189:* rmw'`: Grants the container permission to manage USB devices (189:* covers USB device nodes). +- **`ipp-usb-state` (`/var/ipp-usb/`)** + - Device state files (`/var/ipp-usb/dev/`) – Ensures stable TCP port allocation and DNS-SD name resolution. + - Lock files (`/var/ipp-usb/lock/`) – Prevents multiple instances from running simultaneously. + - Log files (`/var/log/ipp-usb/`) – Can be optionally mounted if needed for debugging. + +- **`ipp-usb-conf` (`/etc/ipp-usb/`)** + - Configuration file (`/etc/ipp-usb/ipp-usb.conf`) – Stores user-defined settings. + - Quirks files (`/etc/ipp-usb/quirks/`) – Contains printer-specific workarounds. + +#### **3. Run the Container with Required Mounts** +Start the container with appropriate options: -To check the logs of `ipp-usb`, run: ```sh - sudo docker logs -f ipp-usb +sudo docker run -d --network host \ + -v /dev/bus/usb:/dev/bus/usb:ro \ + -v ipp-usb-state:/var/ipp-usb \ + -v ipp-usb-conf:/etc/ipp-usb \ + --device-cgroup-rule='c 189:* rmw' \ + --name ipp-usb \ + ghcr.io/openprinting/ipp-usb:latest ``` +- **`--network host`** → Uses the host’s network to enable proper IPP-over-USB and Avahi service discovery. +- **`-v /dev/bus/usb:/dev/bus/usb:ro`** → Grants read-only access to USB devices for printer detection. +- **`-v ipp-usb-state:/var/ipp-usb`** → Mounts the persistent storage volume for printer state, lock files, and logs. +- **`-v ipp-usb-conf:/etc/ipp-usb`** → Ensures configuration and quirks files persist across reboots. +- **`--device-cgroup-rule='c 189:* rmw'`** → Grants read, write, and management permissions for USB printers inside the container. + + ### Building and Running `ipp-usb` Locally #### Prerequisites -**Docker Installed**: Ensure Docker is installed on your system. You can download it from the [official Docker website](https://www.docker.com/get-started) or from the Snap Store: +1. **Docker Installed**: Ensure Docker is installed on your system. You can download it from the [official Docker website](https://www.docker.com/get-started) or from the Snap Store: ```sh sudo snap install docker ``` -**Rockcraft**: Rockcraft should be installed. You can install Rockcraft using the following command: +2. **Rockcraft**: Rockcraft should be installed. You can install Rockcraft using the following command: ```sh sudo snap install rockcraft --classic ``` -#### Step-by-Step Guide - -**Build the `ipp-usb` Rock Image** +**To Build and Run the `ipp-usb` Rock Image Locally, follow these steps** -The first step is to build the Rock from the `rockcraft.yaml`. This image will contain all the configurations and dependencies required to run `ipp-usb`. +#### **1. Build the `ipp-usb` Rock Image** +The first step is to build the Rock image from the `rockcraft.yaml` configuration file. This image will include all required dependencies and configurations for `ipp-usb`. -Navigate to the directory containing `rockcraft.yaml`, then run: +Navigate to the directory containing `rockcraft.yaml` and run: ```sh rockcraft pack -v -``` +``` -**Compile to Docker Image** +#### **2. Convert the Rock Image to a Docker Image** +Once the `.rock` file is built, convert it into a Docker image using: +```sh +sudo rockcraft.skopeo --insecure-policy copy oci-archive: docker-daemon:ipp-usb:latest +``` + +#### **3. Create Persistent Storage Volumes** +To ensure that configuration settings and printer state persist across container restarts, create two Docker volumes: -Once the `.rock` file is built, compile a Docker image from it using: ```sh - sudo rockcraft.skopeo --insecure-policy copy oci-archive: docker-daemon:ipp-usb:latest -``` +sudo docker volume create ipp-usb-state +sudo docker volume create ipp-usb-conf +``` -**Run the `ipp-usb` Docker Container** +These volumes store: + +- **`ipp-usb-state` (`/var/ipp-usb/`)** + - Device state files (`/var/ipp-usb/dev/`) – Ensures stable TCP port allocation and DNS-SD name resolution. + - Lock files (`/var/ipp-usb/lock/`) – Prevents multiple instances from running simultaneously. + - Log files (`/var/log/ipp-usb/`) – Can be optionally mounted if needed for debugging. + +- **`ipp-usb-conf` (`/etc/ipp-usb/`)** + - Configuration file (`/etc/ipp-usb/ipp-usb.conf`) – Stores user-defined settings. + - Quirks files (`/etc/ipp-usb/quirks/`) – Contains printer-specific workarounds. + +#### **4. Run the Container with Required Mounts** +Start the container with appropriate options: ```sh - sudo docker run -d --network host \ - -v /dev/bus/usb:/dev/bus/usb:ro \ - --device-cgroup-rule='c 189:* rmw' \ - --name ipp-usb \ - ipp-usb:latest +sudo docker run -d --network host \ + -v /dev/bus/usb:/dev/bus/usb:ro \ + -v ipp-usb-state:/var/ipp-usb \ + -v ipp-usb-conf:/etc/ipp-usb \ + --device-cgroup-rule='c 189:* rmw' \ + --name ipp-usb \ + ipp-usb:latest ``` +- **`--network host`** → Uses the host’s network to enable proper IPP-over-USB and Avahi service discovery. +- **`-v /dev/bus/usb:/dev/bus/usb:ro`** → Grants read-only access to USB devices for printer detection. +- **`-v ipp-usb-state:/var/ipp-usb`** → Mounts the persistent storage volume for printer state, lock files, and logs. +- **`-v ipp-usb-conf:/etc/ipp-usb`** → Ensures configuration and quirks files persist across reboots. +- **`--device-cgroup-rule='c 189:* rmw'`** → Grants read, write, and management permissions for USB printers inside the container. + ### Accessing the Container Shell To enter the running `ipp-usb` container and access a shell inside it, use: @@ -313,22 +360,105 @@ To enter the running `ipp-usb` container and access a shell inside it, use: ``` This allows you to inspect logs, debug issues, or manually run commands inside the container. -### Configuration +### Configuration -The `ipp-usb` container uses a configuration file located at: +The `ipp-usb` container uses a configuration file located at: +```sh +/etc/ipp-usb/ipp-usb.conf ``` -/etc/ipp-usb.conf +By default, the container uses the built-in configuration, which can be modified from inside the container. + +**Modifying the Configuration File Inside the Container** + +#### **1. Enter the Running Container** +Use the following command to access the container’s shell: +```sh +sudo docker exec -it ipp-usb bash ``` -To customize the configuration, mount a modified config file: + +#### **2. Open the Configuration File in Nano** +Once inside the container, open the configuration file using `nano`: ```sh - sudo docker run -d --network host \ - -v /dev/bus/usb:/dev/bus/usb:ro \ - --device-cgroup-rule='c 189:* rmw' \ - -v /path/to/custom/ipp-usb.conf:/etc/ipp-usb.conf:ro \ - --name ipp-usb \ - ghcr.io/openprinting/ipp-usb:latest +nano /etc/ipp-usb/ipp-usb.conf ``` +#### **3. Edit and Save the File** +- Make the necessary changes to the file. +- Press `CTRL + X`, then `Y`, and hit `Enter` to save the changes. + +#### **4. Restart the Container to Apply Changes** +Exit the container and restart it to apply the updated configuration: +```sh +sudo docker restart ipp-usb +``` + +### **Viewing Logs in the `ipp-usb` Container** + +The `ipp-usb` container logs important events and errors to `/var/log/ipp-usb/`. Logs are categorized into: + +- **Main log file:** `/var/log/ipp-usb/main.log` → Captures overall service activity, including errors and general events. +- **Per-device logs:** `/var/log/ipp-usb/.log` → Individual log files for each detected printer, helpful for troubleshooting device-specific issues. + +#### **1. Using Docker Logs** + +To view real-time logs from the running container, use: +```sh +sudo docker logs -f ipp-usb +``` +- The `-f` flag follows the logs in real-time. +- Replace `ipp-usb` with your actual container name if different. + +#### **2. Viewing Logs Inside the Container** + +If you need to inspect logs manually, first enter the container shell: +```sh +sudo docker exec -it ipp-usb bash +``` + +Once inside the container, you can use the following commands: + +#### **2.1 Monitor Logs in Real-Time** + +- **Main log file:** + ```sh + tail -f /var/log/ipp-usb/main.log + ``` + This continuously displays new log entries for the entire service. + +- **Per-device log file (Replace `` with your printer's identifier):** + ```sh + tail -f /var/log/ipp-usb/.log + ``` + +#### **2.2 Display Full Log Files** + +- **Show full contents of the main log file:** + ```sh + cat /var/log/ipp-usb/main.log + ``` + +- **Show logs for a specific device:** + ```sh + cat /var/log/ipp-usb/.log + ``` + +#### **2.3 List Available Device Logs** + +To see all available per-device log files, run: +```sh +ls /var/log/ipp-usb/ +``` + +#### **3. Persisting Logs Across Container Restarts** + +If you want logs to persist across container restarts, you should mount the log directory using a Docker volume or a host directory. + +**Required Docker Flags for Log Persistence:** +- `-v :/var/log/ipp-usb` → Mounts a Docker volume for log persistence. +- **or** +- `-v :/var/log/ipp-usb` → Mounts a host directory to retain logs outside the container. + +These options ensure that logs remain available even after the container stops or is recreated, allowing for easier troubleshooting and auditing. ## Installation from source diff --git a/rock/rockcraft.yaml b/rock/rockcraft.yaml index 5d918c2..5e3ccf3 100644 --- a/rock/rockcraft.yaml +++ b/rock/rockcraft.yaml @@ -9,6 +9,7 @@ description: | license: Apache-2.0 adopt-info: ipp-usb +run-user: _daemon_ platforms: amd64: @@ -16,8 +17,8 @@ platforms: armhf: services: - dbus: - command: /scripts/run-dbus.sh + avahi-daemon: + command: /scripts/run-avahi.sh override: replace on-failure: restart startup: enabled @@ -27,7 +28,7 @@ services: startup: enabled override: replace on-failure: restart - after: [dbus] + after: [avahi-daemon] parts: goipp: @@ -114,35 +115,182 @@ parts: make -j$CRAFT_PARALLEL_BUILD_COUNT make install DESTDIR=$CRAFT_PART_INSTALL - avahi-daemon: - plugin: nil - build-packages: - - avahi-daemon - overlay-packages: + avahi: + plugin: autotools + source: https://github.com/avahi/avahi.git + source-type: git + autotools-configure-parameters: + - --prefix=/usr + - --disable-qt3 + - --disable-qt4 + - --disable-qt5 + - --disable-gtk + - --disable-gtk3 + - --disable-gdbm + - --disable-python + - --disable-pygtk + - --disable-python-dbus + - --disable-mono + - --disable-monodoc + - --disable-manpages + - --disable-xmltoman + - --with-avahi-user=_daemon_ + - --with-avahi-group=_daemon_ + # - --with-avahi-priv-access-group=netdev + # - --with-distro=debian + - --disable-gobject + - --datadir=/usr/share + - --libdir=/usr/lib/${CRAFT_ARCH_TRIPLET_BUILD_FOR} + - --with-systemdsystemunitdir=/usr/lib/systemd/system + - --localstatedir=/var + - --sysconfdir=/etc + build-packages: + - g++ + - gcc + - gettext + - intltool + - libdaemon-dev + - libdbus-1-dev + - libevent-dev + - libexpat1-dev + - libglib2.0-dev + - libsystemd-dev + - xmltoman + override-build: | + craftctl default + # for reference sort systemd service files + sed -i \ + -e 's|\(.*\)avahi-daemon -s.*|\1avahi-daemon -s --no-drop-root|g' \ + -e 's|\(.*\)avahi-daemon -r.*|\1avahi-daemon -r --no-drop-root|g' \ + ${CRAFT_PART_INSTALL}/usr/lib/systemd/system/avahi-daemon.service + mkdir -p \ + ${CRAFT_PART_INSTALL}/usr/lib/systemd/system/multi-user.target.wants + ln -sf \ + ../avahi-daemon.service \ + ${CRAFT_PART_INSTALL}/usr/lib/systemd/system/multi-user.target.wants/avahi-daemon.service + mkdir -p "${CRAFT_PART_INSTALL}/usr/share/dbus-1/" + cp -r /usr/share/dbus-1/* "${CRAFT_PART_INSTALL}/usr/share/dbus-1/" + build-environment: + - LD_LIBRARY_PATH: "${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$CRAFT_STAGE/usr/lib" + override-prime: | + set -eux + craftctl default + sed -i 's/use-ipv6=yes/use-ipv6=no/' ${CRAFT_PRIME}/etc/avahi/avahi-daemon.conf + sed -i 's|messagebus|_daemon_|; //,/<\/policy>/d' $CRAFT_PRIME/usr/share/dbus-1/system.conf + sed -i 's///g; s///g' $CRAFT_PRIME/usr/share/dbus-1/system.d/avahi-dbus.conf + stage-packages: + - libdaemon0 + - libevent-2.1-7 - avahi-utils - - libnss-mdns - mdns-scan + # - libavahi-client3 + # - libavahi-common3 + stage: + - etc/avahi + - usr + - -usr/lib/**/libavahi-client.so* + - -usr/lib/**/libavahi-common.so* + - -usr/lib/**/*.acd + - -usr/lib/**/*.la + - -usr/lib/**/avahi + - -usr/lib/**/libavahi-glib* + - -usr/lib/**/libavahi-libevent* + - -usr/lib/**/libevent-* + # - -usr/lib/**/libnss_mdns* + - -usr/lib/**/pkgconfig + - -usr/include + - -usr/share/doc + - -usr/share/man + - -usr/share/locale + after: [ipp-usb] + + utils: + plugin: nil + overlay-packages: + - python3 - dbus - - libavahi-client3 - - libavahi-common3 - override-build: | + - libnss-mdns + - nano + override-prime: | + set -eux craftctl default - mkdir -p "${CRAFT_PART_INSTALL}/usr/share/dbus-1/" - cp -r /usr/share/dbus-1/* "${CRAFT_PART_INSTALL}/usr/share/dbus-1/" + + # Set up Avahi Daemon runtime directory + AVAHI_RUN_DIR="$CRAFT_PRIME/var/run/avahi-daemon" + mkdir -p "$AVAHI_RUN_DIR" + chown 584792:584792 "$AVAHI_RUN_DIR" + chmod 777 "$AVAHI_RUN_DIR" + + # Set up D-Bus runtime directory + DBUS_RUN_DIR="$CRAFT_PRIME/var/run/dbus" + mkdir -p "$DBUS_RUN_DIR" + chown 584792:584792 "$DBUS_RUN_DIR" + chmod 777 "$DBUS_RUN_DIR" + + # Set up IPP-USB runtime directories + IPP_USB_RUN_DIR="$CRAFT_PRIME/var/ipp-usb" + mkdir -p "$IPP_USB_RUN_DIR" + chown 584792:584792 "$IPP_USB_RUN_DIR" + chmod 770 "$IPP_USB_RUN_DIR" + + # Set up lock directory for IPP-USB + LOCK_DIR="$IPP_USB_RUN_DIR/lock" + mkdir -p "$LOCK_DIR" + chown 584792:584792 "$LOCK_DIR" + chmod 770 "$LOCK_DIR" + + # Set up dev directory for IPP-USB + DEV_DIR="$IPP_USB_RUN_DIR/dev" + mkdir -p "$DEV_DIR" + chown 584792:584792 "$DEV_DIR" + chmod 770 "$DEV_DIR" + + # Set up log directory for IPP-USB + LOG_DIR="$CRAFT_PRIME/var/log/ipp-usb" + mkdir -p "$LOG_DIR" + chown 584792:584792 "$LOG_DIR" + chmod 770 "$LOG_DIR" + + # Set up /etc/ipp-usb directory for configuration files + CONF_DIR="$CRAFT_PRIME/etc/ipp-usb" + mkdir -p "$CONF_DIR" + chown 584792:584792 "$CONF_DIR" + chmod 770 "$CONF_DIR" + + # Create default ipp-usb.conf file if missing + CONF_FILE="$CONF_DIR/ipp-usb.conf" + touch "$CONF_FILE" + chown 584792:584792 "$CONF_FILE" + chmod 644 "$CONF_FILE" + + # Set up quirks directory + QUIRKS_DIR="$CRAFT_PRIME/etc/ipp-usb/quirks" + mkdir -p "$QUIRKS_DIR" + chown 584792:584792 "$QUIRKS_DIR" + chmod 770 "$QUIRKS_DIR" + + # Ensure ipp-usb binary is executable and owned by _daemon_ + IPP_USB_BIN="$CRAFT_PRIME/usr/sbin/ipp-usb" + if [ -f "$IPP_USB_BIN" ]; then + chown root:584792 "$IPP_USB_BIN" # Owned by root, but executable by _daemon_ + chmod 750 "$IPP_USB_BIN" # Only root and _daemon_ can execute + chmod u+s "$IPP_USB_BIN" # Set SUID so it runs as root when executed + fi + after: [ipp-usb, avahi] scripts: plugin: dump source: scripts/ organize: run-ipp-usb.sh: scripts/run-ipp-usb.sh - run-dbus.sh: scripts/run-dbus.sh + run-avahi.sh: scripts/run-avahi.sh override-prime: | set -eux craftctl default if [ -f "$CRAFT_PRIME/scripts/run-ipp-usb.sh" ]; then chmod +x "$CRAFT_PRIME/scripts/run-ipp-usb.sh" fi - if [ -f "$CRAFT_PRIME/scripts/run-dbus.sh" ]; then - chmod +x "$CRAFT_PRIME/scripts/run-dbus.sh" + if [ -f "$CRAFT_PRIME/scripts/run-avahi.sh" ]; then + chmod +x "$CRAFT_PRIME/scripts/run-avahi.sh" fi - after: [ipp-usb, avahi-daemon] + after: [ipp-usb, avahi] diff --git a/rock/scripts/run-avahi.sh b/rock/scripts/run-avahi.sh new file mode 100644 index 0000000..2e539c1 --- /dev/null +++ b/rock/scripts/run-avahi.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +set -eux + +# Start dbus-daemon in the background +/usr/bin/dbus-daemon --system --nofork & + +# Wait for the D-Bus system bus to be ready +while [ ! -e /var/run/dbus/system_bus_socket ]; do + echo "Waiting for dbus-daemon to initialize..." + sleep 1 +done + +# Start avahi-daemon after dbus-daemon is ready +/usr/sbin/avahi-daemon -f /etc/avahi/avahi-daemon.conf --no-drop-root --debug + +# Keep the container running +exec tail -f /dev/null \ No newline at end of file diff --git a/rock/scripts/run-dbus.sh b/rock/scripts/run-dbus.sh deleted file mode 100644 index 11b902c..0000000 --- a/rock/scripts/run-dbus.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/sh -set -eux - -echo "Creating system users..." - -# Check if users exist before attempting to create them -if ! id -u systemd-resolve >/dev/null 2>&1; then - useradd --system --no-create-home --shell /usr/sbin/nologin systemd-resolve -fi - -if ! id -u systemd-network >/dev/null 2>&1; then - useradd --system --no-create-home --shell /usr/sbin/nologin systemd-network -fi - -echo "Ensuring necessary directories exist..." - -# Ensure /run/dbus exists with correct permissions -if [ ! -d /run/dbus ]; then - mkdir -p /run/dbus - chmod 755 /run/dbus - chown root:root /run/dbus -fi - -echo "Starting dbus service..." - -# Start dbus and verify it's running -service dbus start -if ! pgrep -x "dbus-daemon" >/dev/null; then - echo "Failed to start dbus-daemon!" >&2 - exit 1 -fi - -echo "Starting avahi-daemon..." - -# Start avahi-daemon and ensure it's running -avahi-daemon --daemonize --no-drop-root -if ! pgrep -x "avahi-daemon" >/dev/null; then - echo "Failed to start avahi-daemon!" >&2 - exit 1 -fi - -echo "Services started successfully." - -# Keep the container alive using a foreground process -exec sleep infinity diff --git a/rock/scripts/run-ipp-usb.sh b/rock/scripts/run-ipp-usb.sh index 77cb8d1..074fba7 100644 --- a/rock/scripts/run-ipp-usb.sh +++ b/rock/scripts/run-ipp-usb.sh @@ -1,19 +1,32 @@ #!/bin/sh +set -eux -#set -e -x - -# Create needed directories (ignore errors) +# Create necessary directories mkdir -p /etc/ipp-usb || : +mkdir -p /etc/ipp-usb/quirks || : +mkdir -p /var/ipp-usb/lock || : +mkdir -p /var/ipp-usb/dev || : mkdir -p /var/log/ipp-usb || : -mkdir -p /var/lock || : -mkdir -p /var/dev || : -mkdir -p /usr/share/ipp-usb/quirks || : +# Copy quirks files if not present +cp -rn /usr/share/ipp-usb/quirks/* /etc/ipp-usb/quirks/ >/dev/null 2>&1 || : # Put config files in place (do not overwrite existing user config) -yes no | cp -i /usr/share/ipp-usb/quirks/* /etc/ipp-usb/quirks >/dev/null 2>&1 || : if [ ! -f /etc/ipp-usb/ipp-usb.conf ]; then - cp /usr/share/ipp-usb/ipp-usb.conf /etc/ipp-usb/ >/dev/null 2>&1 || : + cp /etc/ipp-usb.conf /etc/ipp-usb/ >/dev/null 2>&1 fi +# Wait for avahi-daemon +while true; do + if [ -f "/var/run/avahi-daemon/pid" ] || [ -f "/run/avahi-daemon/pid" ]; then + echo "[$(date)] avahi-daemon is active. Starting ipp-usb..." + break + fi + echo "[$(date)] Waiting for avahi-daemon to initialize..." + sleep 1 +done + +# Run ipp-usb with logging +echo "[$(date)] Running ipp-usb..." + # Run ipp-usb with the provided command-line arguments exec /usr/sbin/ipp-usb "$@"