diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b2a8b32bd..3a82673f1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,6 +23,9 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + # A non-shallow clone is needed for the Differential ShellCheck + fetch-depth: 0 - id: ShellCheck name: Differential ShellCheck diff --git a/snapcraft.yaml b/snapcraft.yaml index 2824d9ab4..ce6d3bcb5 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -813,12 +813,14 @@ parts: - --disable-docs - --disable-guest-agent - --disable-parallels + - --disable-pvrdma - --disable-qed - --disable-slirp - --disable-user - --disable-vdi - --disable-vnc - --disable-xen + - --disable-xkbcommon - --enable-attr - --enable-cap-ng - --enable-kvm @@ -1409,14 +1411,14 @@ parts: make doc # Remove unneeded bits - rm doc/html/objects.inv # only objects.inv.txt is used + rm doc/_build/objects.inv # only objects.inv.txt is used # not needed once built - rm doc/html/.buildinfo - rm -rf doc/html/_sphinx_design_static/ + rm doc/_build/.buildinfo + rm -rf doc/_build/_sphinx_design_static/ # Stage the static website mkdir -p "${CRAFT_STAGE}/share/lxd-documentation" - cp -a doc/html/. "${CRAFT_STAGE}/share/lxd-documentation/" + cp -a doc/_build/. "${CRAFT_STAGE}/share/lxd-documentation/" fi # Setup bash completion diff --git a/snapcraft/commands/daemon.activate b/snapcraft/commands/daemon.activate index 2ea5bcb86..a979e7742 100755 --- a/snapcraft/commands/daemon.activate +++ b/snapcraft/commands/daemon.activate @@ -18,8 +18,10 @@ fi # shellcheck disable=SC2155 export SNAP_CURRENT="$(realpath "${SNAP}/..")/current" -# shellcheck disable=SC2155 -export ARCH="$(basename "$(readlink -f "${SNAP_CURRENT}"/lib/*-linux-gnu*/)")" +# Turn `/snap/lxd/28637/lib/x86_64-linux-gnu` into `x86_64-linux-gnu` +# Similar to `basename` but using variable substitution instead of external executable +LIB_ARCH="$(readlink -f "${SNAP_CURRENT}"/lib/*-linux-gnu*/)" +export ARCH="${LIB_ARCH##*/}" export LD_LIBRARY_PATH="${LD_LIBRARY_PATH:-}:${SNAP_CURRENT}/lib:${SNAP_CURRENT}/lib/${ARCH}:${SNAP_CURRENT}/lib/${ARCH}/ceph" export PATH="${PATH}:${SNAP_CURRENT}/bin" @@ -52,8 +54,7 @@ if ! nsenter -t 1 -m systemctl is-active -q snap."${SNAP_INSTANCE_NAME}".daemon. fi # Start LXD if running as an appliance -SNAP_MODEL="$(nsenter -t 1 -m snap model --assertion | grep "^model: " | cut -d' ' -f2)" -if echo "${SNAP_MODEL}" | grep -q "^lxd-core"; then +if nsenter -t 1 -m snap model --assertion | grep -q "^model: lxd-core"; then echo "==> LXD appliance detected, starting LXD" nsenter -t 1 -m systemctl start snap."${SNAP_INSTANCE_NAME}".daemon --no-block exit 0 @@ -95,7 +96,7 @@ if getent group "${daemon_user_group}" >/dev/null 2>&1; then fi # Check if LXD ever started -if [ ! -e "${SNAP_COMMON}/lxd/database" ]; then +if [ ! -d "${SNAP_COMMON}/lxd/database" ]; then echo "==> LXD never started on this system, no need to start it now" exit 0 fi diff --git a/snapcraft/commands/daemon.reload b/snapcraft/commands/daemon.reload index c06ac4cc5..f8e7562a2 100755 --- a/snapcraft/commands/daemon.reload +++ b/snapcraft/commands/daemon.reload @@ -10,5 +10,5 @@ if [ -d /sys/kernel/security/apparmor ]; then fi echo reload > "${SNAP_COMMON}/state" -PID=$(cat "${SNAP_COMMON}/lxd.pid") -/bin/kill "$PID" +read -r PID < "${SNAP_COMMON}/lxd.pid" +kill "$PID" diff --git a/snapcraft/commands/daemon.start b/snapcraft/commands/daemon.start index 8f1cc2b28..536b8209c 100755 --- a/snapcraft/commands/daemon.start +++ b/snapcraft/commands/daemon.start @@ -14,8 +14,10 @@ echo "=> Preparing the system (${SNAP_REVISION})" # shellcheck disable=SC2155 export SNAP_CURRENT="$(realpath "${SNAP}/..")/current" -# shellcheck disable=SC2155 -export ARCH="$(basename "$(readlink -f "${SNAP_CURRENT}"/lib/*-linux-gnu*/)")" +# Turn `/snap/lxd/28637/lib/x86_64-linux-gnu` into `x86_64-linux-gnu` +# Similar to `basename` but using variable substitution instead of external executable +LIB_ARCH="$(readlink -f "${SNAP_CURRENT}"/lib/*-linux-gnu*/)" +export ARCH="${LIB_ARCH##*/}" export HOME="/tmp/" export LXD_DIR="${SNAP_COMMON}/lxd/" @@ -28,14 +30,17 @@ export LXD_CLUSTER_UPDATE="${SNAP_CURRENT}/commands/refresh" export LXD_QEMU_FW_PATH="${SNAP_CURRENT}/share/qemu" export PYTHONPATH=/snap/lxd/current/lib/python3/dist-packages/ -# Detect model -SNAP_MODEL="$(nsenter -t 1 -m snap model --assertion | sed -n 's/^model: \(.\+\)/\1/p')" +# Detect LXD appliance +LXD_APPLIANCE="false" +if nsenter -t 1 -m snap model --assertion | grep -q "^model: lxd-core"; then + LXD_APPLIANCE="true" +fi # Detect base name SNAP_BASE="$(sed -n '/^name:/ s/^name:\s*\(core[0-9]\{2\}\)/\1/p' /meta/snap.yaml)" # Wait for appliance configuration -if echo "${SNAP_MODEL}" | grep -q "^lxd-core"; then +if [ "${LXD_APPLIANCE}" = "true" ]; then while :; do [ "$(nsenter -t 1 -m snap managed)" = "true" ] && break sleep 5 @@ -619,16 +624,23 @@ fi # LXD ## Check for existing LXDs -for pid in $(pgrep -f "lxd --logfile" ; pgrep -f "lxd.debug --logfile"); do - grep -qF "SNAP_NAME=lxd" "/proc/${pid}/environ" || continue - - rm -f "/var/snap/lxd/common/lxd/.validate" - touch "/proc/${pid}/root/var/snap/lxd/common/lxd/.validate" 2>/dev/null || true - if [ -e "/var/snap/lxd/common/lxd/.validate" ]; then +for pid in $(pgrep --euid 0 --full "lxd(\.debug)? --logfile"); do + # Cheap confirmation that we likely have a LXD PID + grep -q --line-regexp --fixed-strings --max-count=1 "SNAP_NAME=lxd" "/proc/${pid}/environ" || continue + + # Confirm the PID found by pgrep is indeed ours and not one from + # a nested instance also running LXD or a recycled PID. By writing + # to a file using the `/proc` path and comparing the content seen from + # the `/var` path, we can conclude if the LXD daemon is executing + # in the same mount namespace as ours (safe to kill) or not (ignore). + # A priv nested LXD would pass the EUID=0 and SNAP_NAME=lxd tests but would + # fail the `.validate` test as it wouldn't be in the same mount namespace. + echo "$$" > "/proc/${pid}/root${SNAP_COMMON}/lxd/.validate" 2>/dev/null || true + if [ "$(cat "${SNAP_COMMON}/lxd/.validate" 2>/dev/null)" = "$$" ]; then echo "=> Killing conflicting LXD (pid=${pid})" kill -9 "${pid}" || true - rm -f "/var/snap/lxd/common/lxd/.validate" fi + rm -f "/proc/${pid}/root${SNAP_COMMON}/lxd/.validate" done ## Move the database out of the versioned path if present @@ -664,8 +676,8 @@ CMD="${LXD} --logfile ${SNAP_COMMON}/lxd/logs/lxd.log" if getent group "${daemon_group}" >/dev/null 2>&1; then CMD="${CMD} --group ${daemon_group}" - if [ -e "/var/snap/lxd/common/lxd/unix.socket" ]; then - chgrp "${daemon_group}" /var/snap/lxd/common/lxd/unix.socket + if [ -e "${SNAP_COMMON}/lxd/unix.socket" ]; then + chgrp "${daemon_group}" "${SNAP_COMMON}/lxd/unix.socket" fi else echo "==> No \"${daemon_group}\" group found, only root will be able to use LXD." @@ -683,12 +695,6 @@ if [ "${daemon_verbose:-"false"}" = "true" ]; then CMD="${CMD} --verbose" fi -# Check if this is the first time LXD is started. -FIRSTRUN="false" -if [ ! -d "${SNAP_COMMON}/lxd/database" ]; then - FIRSTRUN="true" -fi - # We deal with errors ourselves from this point on set +e @@ -734,16 +740,17 @@ if [ "${RET}" -gt "0" ]; then exit 1 fi -## Process preseed if present -if [ "${FIRSTRUN}" = "true" ]; then +## Check if this is the first time LXD is started +if [ ! -d "${SNAP_COMMON}/lxd/database" ]; then set -e echo "=> First LXD execution on this system" + ## Process preseed if present if [ -e "${SNAP_COMMON}/init.yaml" ]; then echo "==> Running LXD preseed file" ${LXD} init --preseed < "${SNAP_COMMON}/init.yaml" mv "${SNAP_COMMON}/init.yaml" "${SNAP_COMMON}/init.yaml.applied" - elif echo "${SNAP_MODEL}" | grep -q "^lxd-core"; then + elif [ "${LXD_APPLIANCE}" = "true" ]; then echo "==> Initializing LXD appliance" # Network (NIC with the default gateway) diff --git a/snapcraft/commands/daemon.stop b/snapcraft/commands/daemon.stop index 1016dcfc9..1f84e953e 100755 --- a/snapcraft/commands/daemon.stop +++ b/snapcraft/commands/daemon.stop @@ -9,24 +9,23 @@ if [ -d /sys/kernel/security/apparmor ]; then fi fi -export LXD_DIR="${SNAP_COMMON}/lxd/" -PID=$(cat "${SNAP_COMMON}/lxd.pid" || true) - -# FIXME: Detect stop reason -# This should be exposed by snapd directly -STATUS=$(snap-query 2>/dev/null || true) - reason="host shutdown" if [ -s "${SNAP_COMMON}/state" ]; then - reason="$(cat "${SNAP_COMMON}/state")" -elif [ "${STATUS}" = "auto-refresh" ]; then - reason="snap refresh" -elif [ "${STATUS}" = "refresh-snap" ]; then - reason="snap refresh" -elif [ "${STATUS}" = "install-snap" ]; then - reason="snap refresh" -elif [ "${STATUS}" = "remove-snap" ]; then - reason="snap removal" + read -r reason < "${SNAP_COMMON}/state" +else + # FIXME: Detect stop reason + # This should be exposed by snapd directly + STATUS=$(snap-query 2>/dev/null || true) + + if [ "${STATUS}" = "auto-refresh" ]; then + reason="snap refresh" + elif [ "${STATUS}" = "refresh-snap" ]; then + reason="snap refresh" + elif [ "${STATUS}" = "install-snap" ]; then + reason="snap refresh" + elif [ "${STATUS}" = "remove-snap" ]; then + reason="snap removal" + fi fi echo "=> Stop reason is: ${reason}" @@ -41,6 +40,9 @@ if [ "${reason}" = "reload" ] || [ "${reason}" = "crashed" ]; then exit 0 fi +export LXD_DIR="${SNAP_COMMON}/lxd/" +read -r PID < "${SNAP_COMMON}/lxd.pid" || true + # Handle refreshes if [ "${reason}" = "snap refresh" ]; then echo "=> Stopping LXD" @@ -104,8 +106,8 @@ fi ## OpenVswitch if [ -e "${SNAP_COMMON}/openvswitch/run/ovs-vswitchd.pid" ]; then - PID=$(cat "${SNAP_COMMON}/openvswitch/run/ovs-vswitchd.pid") - if [ -n "${PID}" ] && kill -0 "${PID}" 2>/dev/null; then + read -r OVS_PID < "${SNAP_COMMON}/openvswitch/run/ovs-vswitchd.pid" || true + if [ -n "${OVS_PID}" ] && kill -0 "${OVS_PID}" 2>/dev/null; then ( echo "=> Stopping Open vSwitch" @@ -129,17 +131,17 @@ fi ## LXCFS if [ -e "${SNAP_COMMON}/lxcfs.pid" ]; then - echo "=> Stopping LXCFS" + read -r LXCFS_PID < "${SNAP_COMMON}/lxcfs.pid" || true + if [ -n "${LXCFS_PID}" ] && kill -0 "${LXCFS_PID}" 2>/dev/null; then + echo "=> Stopping LXCFS" - PID=$(cat "${SNAP_COMMON}/lxcfs.pid") - if [ -n "${PID}" ] && kill -0 "${PID}" 2>/dev/null; then - if ! kill "$PID"; then + if ! kill "${LXCFS_PID}"; then echo "==> Failed to signal LXCFS to stop" fi DEAD=0 for _ in $(seq 30); do - if ! kill -0 "${PID}" 2>/dev/null; then + if ! kill -0 "${LXCFS_PID}" 2>/dev/null; then DEAD=1 echo "==> Stopped LXCFS" break @@ -149,7 +151,7 @@ if [ -e "${SNAP_COMMON}/lxcfs.pid" ]; then if [ "${DEAD}" = "0" ]; then echo "==> Forcefully stopping LXCFS after 30 seconds wait" - if kill -9 "${PID}" 2>/dev/null; then + if kill -9 "${LXCFS_PID}" 2>/dev/null; then echo "==> Stopped LXCFS" else echo "==> Failed to stop LXCFS" @@ -166,7 +168,7 @@ rm -f "${SNAP_COMMON}/lxcfs.pid" "${SNAP_COMMON}/lxd.pid" ## Flush our shared namespace from the host echo "=> Cleaning up namespaces" -nsenter -t 1 -m umount -l /var/snap/lxd/common/ns 2>/dev/null || true +nsenter -t 1 -m umount -l "${SNAP_COMMON}/ns" 2>/dev/null || true echo "=> All done" exit 0 diff --git a/snapcraft/commands/lxd b/snapcraft/commands/lxd index 7b8d9d756..dc9fc6f1e 100755 --- a/snapcraft/commands/lxd +++ b/snapcraft/commands/lxd @@ -12,8 +12,10 @@ fi # shellcheck disable=SC2155 export SNAP_CURRENT="$(realpath "${SNAP}/..")/current" -# shellcheck disable=SC2155 -export ARCH="$(basename "$(readlink -f "${SNAP_CURRENT}"/lib/*-linux-gnu*/)")" +# Turn `/snap/lxd/28637/lib/x86_64-linux-gnu` into `x86_64-linux-gnu` +# Similar to `basename` but using variable substitution instead of external executable +LIB_ARCH="$(readlink -f "${SNAP_CURRENT}"/lib/*-linux-gnu*/)" +export ARCH="${LIB_ARCH##*/}" export LD_LIBRARY_PATH="${LD_LIBRARY_PATH:-}:${SNAP_CURRENT}/lib:${SNAP_CURRENT}/lib/${ARCH}:${SNAP_CURRENT}/lib/${ARCH}/ceph" export PATH="${PATH}:${SNAP_CURRENT}/bin" diff --git a/snapcraft/wrappers/editor b/snapcraft/wrappers/editor index c431f513c..b66064549 100755 --- a/snapcraft/wrappers/editor +++ b/snapcraft/wrappers/editor @@ -52,7 +52,7 @@ fi if [ -n "${EDIT_CMD}" ] && [ "${USERNS}" = 1 ]; then exec 9< /tmp/ # Replace "/tmp/" prefix by exec'ed FD 9. - EDIT_PATH_HOST="/proc/self/fd/9/$(echo "${EDIT_PATH}" | cut -d/ -f3)" + EDIT_PATH_HOST="/proc/self/fd/9/${EDIT_PATH##*/}" find_and_spawn "${EDIT_CMD}" "${EDIT_PATH_HOST}" fi diff --git a/snapcraft/wrappers/kmod b/snapcraft/wrappers/kmod index 565e03a06..5823b66e6 100755 --- a/snapcraft/wrappers/kmod +++ b/snapcraft/wrappers/kmod @@ -1,5 +1,7 @@ #!/bin/sh -NAME="$(basename "${0}")" +# Turn `/usr/sbin/foo` into `foo` +# Similar to `basename` but using variable substitution instead of external executable +NAME="${0##*/}" # Detect base name SNAP_BASE="$(sed -n '/^name:/ s/^name:\s*\(core[0-9]\{2\}\)/\1/p' /meta/snap.yaml)" diff --git a/snapcraft/wrappers/run-host b/snapcraft/wrappers/run-host index 69dd011d9..26309e370 100755 --- a/snapcraft/wrappers/run-host +++ b/snapcraft/wrappers/run-host @@ -1,5 +1,7 @@ #!/bin/sh -CMD="$(basename "${0}")" +# Turn `/usr/sbin/foo` into `foo` +# Similar to `basename` but using variable substitution instead of external executable +CMD="${0##*/}" unset LD_LIBRARY_PATH unset PYTHONPATH diff --git a/snapcraft/wrappers/sshfs b/snapcraft/wrappers/sshfs index f7d8b45a2..0d232c630 100755 --- a/snapcraft/wrappers/sshfs +++ b/snapcraft/wrappers/sshfs @@ -7,7 +7,10 @@ if [ "$(id -u)" != "0" ]; then exit 1 fi -CMD="$(basename "${0}")" +# Turn `/usr/sbin/foo` into `foo` +# Similar to `basename` but using variable substitution instead of external executable +CMD="${0##*/}" + unset LD_LIBRARY_PATH export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"