entrypoint.sh 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. #!/bin/bash
  2. # Adapted from docker-selkies-glx-desktop for an XFCE-only NVIDIA image.
  3. # This Source Code Form is subject to the Mozilla Public License, v. 2.0.
  4. set -Eeuo pipefail
  5. trap 'echo "Desktop entrypoint received a termination signal"' HUP INT QUIT TERM
  6. export DISPLAY="${DISPLAY:-:20}"
  7. export DISPLAY_SIZEW="${DISPLAY_SIZEW:-1920}"
  8. export DISPLAY_SIZEH="${DISPLAY_SIZEH:-1080}"
  9. export DISPLAY_REFRESH="${DISPLAY_REFRESH:-60}"
  10. export DISPLAY_DPI="${DISPLAY_DPI:-96}"
  11. export DISPLAY_CDEPTH="${DISPLAY_CDEPTH:-24}"
  12. export VIDEO_PORT="${VIDEO_PORT:-DFP}"
  13. export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/tmp/runtime-${USER:-ubuntu}}"
  14. export PIPEWIRE_RUNTIME_DIR="${PIPEWIRE_RUNTIME_DIR:-${XDG_RUNTIME_DIR}}"
  15. export PULSE_RUNTIME_PATH="${PULSE_RUNTIME_PATH:-${XDG_RUNTIME_DIR}/pulse}"
  16. export PULSE_SERVER="${PULSE_SERVER:-unix:${PULSE_RUNTIME_PATH}/native}"
  17. until [[ -d "${XDG_RUNTIME_DIR}" ]]; do
  18. sleep 0.5
  19. done
  20. sudo-root chown "$(id -u):$(id -g)" "${HOME}" || true
  21. if [[ -n "${PASSWD:-}" ]]; then
  22. printf '%s:%s\n' "${USER}" "${PASSWD}" | sudo-root chpasswd || true
  23. fi
  24. sudo-root rm -f "/tmp/.X${DISPLAY#:}-lock" || true
  25. sudo-root install -d -m 1777 /tmp/.X11-unix /tmp/.ICE-unix
  26. rm -rf "${HOME}/.cache" || true
  27. sudo-root ln -snf "/usr/share/zoneinfo/${TZ:-UTC}" /etc/localtime || true
  28. printf '%s\n' "${TZ:-UTC}" | sudo-root tee /etc/timezone >/dev/null || true
  29. # Joystick interposer support.
  30. SELKIES_INTERPOSER_PATH="$(
  31. find /usr/lib /usr/lib64 /usr/local/lib \
  32. -name 'selkies_joystick_interposer.so' \
  33. -print -quit 2>/dev/null || true
  34. )"
  35. if [[ -n "${SELKIES_INTERPOSER_PATH}" ]]; then
  36. export LD_PRELOAD="${SELKIES_INTERPOSER_PATH}${LD_PRELOAD:+:${LD_PRELOAD}}"
  37. export SDL_JOYSTICK_DEVICE="${SDL_JOYSTICK_DEVICE:-/dev/input/js0}"
  38. sudo-root install -d -m 1777 /dev/input || true
  39. for joystick in /dev/input/js{0..3}; do
  40. sudo-root touch "${joystick}" || true
  41. sudo-root chmod 0777 "${joystick}" || true
  42. done
  43. fi
  44. install_nvidia_userspace() {
  45. command -v nvidia-smi >/dev/null 2>&1 || {
  46. echo "NVIDIA Container Toolkit has not exposed nvidia-smi." >&2
  47. return 1
  48. }
  49. if command -v nvidia-xconfig >/dev/null 2>&1; then
  50. return 0
  51. fi
  52. local driver_arch driver_version installer archive_dir
  53. driver_arch="$(
  54. dpkg --print-architecture |
  55. sed -e 's/arm64/aarch64/' \
  56. -e 's/armhf/32bit-ARM/' \
  57. -e 's/i.*86/x86/' \
  58. -e 's/amd64/x86_64/' \
  59. -e 's/unknown/x86_64/'
  60. )"
  61. driver_version="${NVIDIA_DRIVER_VERSION:-}"
  62. if [[ -z "${driver_version}" && -f /proc/driver/nvidia/version ]]; then
  63. driver_version="$(
  64. head -n 1 /proc/driver/nvidia/version |
  65. awk '{for (i=1; i<=NF; i++) if ($i ~ /^[0-9]+\.[0-9.]+$/) {print $i; exit}}'
  66. )"
  67. fi
  68. if [[ -z "${driver_version}" ]]; then
  69. driver_version="$(
  70. nvidia-smi --query-gpu=driver_version --format=csv,noheader |
  71. head -n 1
  72. )"
  73. fi
  74. if [[ -z "${driver_version}" ]]; then
  75. echo "Unable to determine the host NVIDIA driver version." >&2
  76. return 1
  77. fi
  78. installer="/tmp/NVIDIA-Linux-${driver_arch}-${driver_version}.run"
  79. archive_dir="/tmp/NVIDIA-Linux-${driver_arch}-${driver_version}"
  80. echo "Installing NVIDIA ${driver_version} userspace GLX components"
  81. curl -fsSL \
  82. "https://international.download.nvidia.com/XFree86/Linux-${driver_arch}/${driver_version}/NVIDIA-Linux-${driver_arch}-${driver_version}.run" \
  83. -o "${installer}" \
  84. || curl -fsSL \
  85. "https://international.download.nvidia.com/tesla/${driver_version}/NVIDIA-Linux-${driver_arch}-${driver_version}.run" \
  86. -o "${installer}"
  87. rm -rf "${archive_dir}"
  88. (
  89. cd /tmp
  90. sh "${installer}" -x
  91. )
  92. sudo-root "${archive_dir}/nvidia-installer" \
  93. --silent \
  94. --no-kernel-module \
  95. --no-install-compat32-libs \
  96. --no-nouveau-check \
  97. --no-nvidia-modprobe \
  98. --no-systemd \
  99. --no-rpms \
  100. --no-backup \
  101. --no-check-for-alternate-installs
  102. rm -rf "${installer}" "${archive_dir}"
  103. }
  104. install_nvidia_userspace
  105. # Always select the first GPU visible inside the container. Device indexes are
  106. # renumbered by NVIDIA Container Toolkit, so the host-side index is unreliable.
  107. GPU_SELECT="$(
  108. nvidia-smi --query-gpu=uuid --format=csv,noheader |
  109. head -n 1
  110. )"
  111. if [[ -z "${GPU_SELECT}" ]]; then
  112. echo "No visible NVIDIA GPU was detected." >&2
  113. exit 1
  114. fi
  115. HEX_ID="$(
  116. nvidia-smi \
  117. --id="${GPU_SELECT}" \
  118. --query-gpu=pci.bus_id \
  119. --format=csv,noheader |
  120. head -n 1
  121. )"
  122. IFS=':.' read -r domain bus device function <<<"${HEX_ID}"
  123. BUS_ID="PCI:$((16#${bus}))@$((16#${domain})):$((16#${device})):$((16#${function}))"
  124. MODELINE="$(cvt -r "${DISPLAY_SIZEW}" "${DISPLAY_SIZEH}" "${DISPLAY_REFRESH}" | sed -n '2p')"
  125. MODE_NAME="$(awk '{print $2}' <<<"${MODELINE}" | tr -d '"')"
  126. if [[ "${VIDEO_PORT,,}" == "none" ]]; then
  127. CONNECTED_MONITOR=(--use-display-device=None)
  128. else
  129. CONNECTED_MONITOR=(--connected-monitor="${VIDEO_PORT}")
  130. fi
  131. sudo-root rm -f /etc/X11/xorg.conf
  132. sudo-root nvidia-xconfig \
  133. --virtual="${DISPLAY_SIZEW}x${DISPLAY_SIZEH}" \
  134. --depth="${DISPLAY_CDEPTH}" \
  135. --mode="${MODE_NAME}" \
  136. --allow-empty-initial-configuration \
  137. --no-probe-all-gpus \
  138. --busid="${BUS_ID}" \
  139. --include-implicit-metamodes \
  140. --mode-debug \
  141. --no-sli \
  142. --no-base-mosaic \
  143. --only-one-x-screen \
  144. "${CONNECTED_MONITOR[@]}"
  145. sudo-root sed -i \
  146. '/Driver[[:space:]]*"nvidia"/a\ Option "ModeValidation" "NoMaxPClkCheck,NoEdidMaxPClkCheck,NoMaxSizeCheck,NoHorizSyncCheck,NoVertRefreshCheck,NoVirtualSizeCheck,NoExtendedGpuCapabilitiesCheck,NoTotalSizeCheck,NoDualLinkDVICheck,NoDisplayPortBandwidthCheck,AllowNon3DVisionModes,AllowNonHDMI3DModes,AllowNonEdidModes,NoEdidHDMI2Check,AllowDpInterlaced"' \
  147. /etc/X11/xorg.conf
  148. sudo-root sed -i \
  149. '/Driver[[:space:]]*"nvidia"/a\ Option "AllowExternalGpus" "True"' \
  150. /etc/X11/xorg.conf
  151. sudo-root sed -i \
  152. '/Section[[:space:]]*"Monitor"/a\ '"${MODELINE}" \
  153. /etc/X11/xorg.conf
  154. sudo-root sed -i '/"DPMS"/d' /etc/X11/xorg.conf
  155. sudo-root sed -i \
  156. '/Section[[:space:]]*"Monitor"/a\ Option "DPMS" "False"' \
  157. /etc/X11/xorg.conf
  158. cat <<'EOF' | sudo-root tee -a /etc/X11/xorg.conf >/dev/null
  159. Section "ServerFlags"
  160. Option "DontVTSwitch" "True"
  161. Option "DontZap" "True"
  162. Option "AllowMouseOpenFail" "True"
  163. Option "AutoAddGPU" "False"
  164. EndSection
  165. EOF
  166. sudo-root ln -snf /dev/ptmx /dev/tty7
  167. /usr/lib/xorg/Xorg \
  168. "${DISPLAY}" \
  169. vt7 \
  170. -noreset \
  171. -novtswitch \
  172. -sharevts \
  173. -nolisten tcp \
  174. -ac \
  175. -dpi "${DISPLAY_DPI}" \
  176. +extension COMPOSITE \
  177. +extension DAMAGE \
  178. +extension GLX \
  179. +extension RANDR \
  180. +extension RENDER \
  181. +extension MIT-SHM \
  182. +extension XFIXES \
  183. +extension XTEST \
  184. &
  185. XORG_PID=$!
  186. echo "Waiting for X socket ${DISPLAY}"
  187. until [[ -S "/tmp/.X11-unix/X${DISPLAY#*:}" ]]; do
  188. if ! kill -0 "${XORG_PID}" 2>/dev/null; then
  189. echo "Xorg exited before creating its socket." >&2
  190. exit 1
  191. fi
  192. sleep 0.5
  193. done
  194. xset -dpms || true
  195. xset s off || true
  196. xset s noblank || true
  197. export XDG_SESSION_ID="${DISPLAY#*:}"
  198. xfce4-session &
  199. XFCE_PID=$!
  200. wait "${XFCE_PID}"