Files
xTeVe/docker/entrypoint.sh
T
nathan 504ea3f9f4
continuous-integration/drone/push Build encountered an error
Phase 3: Go hygiene pass, runtime PUID/PGID, fork README
Go:
- staticcheck 399 -> 0 with staticcheck.conf (style checks ST1000/1003/
  1005/1016/1020/1021/1022 excluded; error strings are shown in the UI).
- io/ioutil and rand.Seed removed; CloseNotifier kept with a lint-ignore
  until the Phase 2 context rewrite.
- Dead code deleted: Auto handler, getStreamByChannelID, updateXEPG,
  indexOfInt, jsonToMapInt64, removeOldSystemData, randomTime, and the
  commented-out blocks in struct-buffer.go and internal/authentication.
- Duplicates folded: cacheImagesInBackground(), one addErrorToStream().
- Bugs found by SA4006/SA5001: os.Create handle leaked per ffmpeg segment
  (buffer.go), http.NewRequest error unchecked (buffer.go), xepg.json
  migration wrote null on read error (migrate.go), WriteUserData errors
  silently dropped (authentication.go), defer Close before error check
  (buffer.go, toolchain.go). checkFilePermission results were discarded;
  an unwritable config or temp dir is now fatal at start-up.
- gofmt applied repo-wide; Drone runs gofmt check and staticcheck.

Docker:
- Entrypoint starts as root, applies PUID/PGID (falls back to XTEVE_UID/
  XTEVE_GID, then image defaults), fixes config ownership only when it
  differs, then drops to xteve via su-exec. --user starts skip all of it.
- /xteve removed from LEGACY_CONFIG_DIRS (it is the parent of the default).
- mwader/static-ffmpeg pinned to 7.1.1; VOLUME /xteve/config.
- Compose files pull registry.coadcorp.com/nathan/xteve:latest, use
  PUID/PGID/TZ, and explain that SSDP needs host networking.
- .dockerignore excludes the npm toolchain (bundle stays in html/js).

Docs: README rewritten for the fork (about, registry, compose, env vars,
security notes); README-DEV gains a container section.
2026-09-26 12:57:43 +10:00

202 lines
6.3 KiB
Bash
Executable File

#!/bin/sh
set -eu
DEFAULT_CONFIG_DIR="/xteve/config"
# Older images and other Docker images kept the config in one of these.
# /xteve itself is not listed because it is the parent of the default dir.
LEGACY_CONFIG_DIRS="/config /home/xteve/.xteve"
RUN_USER="xteve"
XTEVE_BIN="/usr/local/bin/xteve"
log() {
echo "[entrypoint] $*"
}
resolve_config_dir() {
if [ -n "${XTEVE_CONFIG:-}" ] && [ "${XTEVE_CONFIG}" != "${DEFAULT_CONFIG_DIR}" ]; then
printf "%s" "${XTEVE_CONFIG}"
return
fi
if [ -f "${DEFAULT_CONFIG_DIR}/settings.json" ]; then
printf "%s" "${DEFAULT_CONFIG_DIR}"
return
fi
for dir in ${LEGACY_CONFIG_DIRS}; do
if [ -f "${dir}/settings.json" ]; then
printf "%s" "${dir}"
return
fi
done
printf "%s" "${DEFAULT_CONFIG_DIR}"
}
copy_if_missing() {
src="$1"
dst="$2"
if [ -e "${src}" ] && [ ! -e "${dst}" ]; then
cp -R "${src}" "${dst}"
fi
}
settings_initialized() {
file="$1"
if [ ! -s "${file}" ]; then
return 1
fi
if grep -q '"uuid"' "${file}" 2>/dev/null; then
return 0
fi
return 1
}
is_number() {
case "$1" in
''|*[!0-9]*) return 1 ;;
*) return 0 ;;
esac
}
# Apply PUID/PGID to the xteve user and group. Only called when running as root.
# Falls back to XTEVE_UID/XTEVE_GID (the old variable names), then to the ids
# baked into the image at build time.
apply_ids() {
current_uid="$(id -u "${RUN_USER}")"
current_gid="$(id -g "${RUN_USER}")"
want_uid="${PUID:-${XTEVE_UID:-${current_uid}}}"
want_gid="${PGID:-${XTEVE_GID:-${current_gid}}}"
if ! is_number "${want_uid}" || ! is_number "${want_gid}"; then
log "ERROR: PUID/PGID must be numeric (got PUID=${want_uid} PGID=${want_gid})" >&2
exit 1
fi
if [ "${want_gid}" != "${current_gid}" ]; then
# Edit /etc/group in place. deluser/delgroup on busybox refuse to remove
# a group that still has members, so sed is the safest option.
sed -i "s/^${RUN_USER}:\([^:]*\):${current_gid}:/${RUN_USER}:\1:${want_gid}:/" /etc/group
fi
if [ "${want_uid}" != "${current_uid}" ] || [ "${want_gid}" != "${current_gid}" ]; then
sed -i "s/^${RUN_USER}:\([^:]*\):${current_uid}:${current_gid}:/${RUN_USER}:\1:${want_uid}:${want_gid}:/" /etc/passwd
fi
new_uid="$(id -u "${RUN_USER}")"
new_gid="$(id -g "${RUN_USER}")"
if [ "${new_uid}" != "${want_uid}" ] || [ "${new_gid}" != "${want_gid}" ]; then
log "ERROR: failed to set ${RUN_USER} to UID:GID ${want_uid}:${want_gid} (now ${new_uid}:${new_gid})" >&2
exit 1
fi
}
# Make sure the config directory belongs to the xteve user. The directory is
# small (settings, cache, backups), so a recursive chown is acceptable, but it
# is skipped when the ownership is already right.
fix_config_owner() {
dir="$1"
uid="$2"
gid="$3"
owner="$(stat -c '%u:%g' "${dir}" 2>/dev/null || echo "")"
if [ "${owner}" != "${uid}:${gid}" ] || [ "${MIGRATED}" = "1" ]; then
log "Setting ownership of ${dir} to ${uid}:${gid}"
chown -R "${uid}:${gid}" "${dir}" || log "WARNING: chown of ${dir} failed; continuing"
fi
}
CONFIG_DIR="$(resolve_config_dir)"
PORT="${XTEVE_PORT:-34400}"
# The binary uses XTEVE_CONFIG to detect that it runs in a container and to
# default ffmpeg.path to /usr/local/bin/ffmpeg. Keep it in sync with the
# resolved directory so both agree.
export XTEVE_CONFIG="${CONFIG_DIR}"
mkdir -p "${CONFIG_DIR}"
# Set to 1 when files were copied from a legacy directory. When that happens as
# root the copies are root-owned, so the ownership fix below must not be skipped.
MIGRATED=0
if ! settings_initialized "${CONFIG_DIR}/settings.json"; then
for legacy_dir in ${LEGACY_CONFIG_DIRS}; do
if [ "${legacy_dir}" = "${CONFIG_DIR}" ]; then
continue
fi
if settings_initialized "${legacy_dir}/settings.json"; then
log "Migrating existing configuration from ${legacy_dir} to ${CONFIG_DIR}"
for file in authentication.json pms.json settings.json xepg.json urls.json; do
if [ "${file}" = "settings.json" ] || [ "${file}" = "xepg.json" ] || [ "${file}" = "urls.json" ]; then
cp -f "${legacy_dir}/${file}" "${CONFIG_DIR}/${file}" 2>/dev/null || true
else
copy_if_missing "${legacy_dir}/${file}" "${CONFIG_DIR}/${file}"
fi
done
for dir_name in data cache backup tmp; do
copy_if_missing "${legacy_dir}/${dir_name}" "${CONFIG_DIR}/${dir_name}"
done
MIGRATED=1
break
fi
done
fi
if [ "$(id -u)" = "0" ]; then
# Started as root (the default): apply PUID/PGID, fix ownership, drop privileges.
apply_ids
RUN_UID="$(id -u "${RUN_USER}")"
RUN_GID="$(id -g "${RUN_USER}")"
fix_config_owner "${CONFIG_DIR}" "${RUN_UID}" "${RUN_GID}"
if ! su-exec "${RUN_USER}:${RUN_USER}" touch "${CONFIG_DIR}/.xteve-write-test" 2>/dev/null; then
log "ERROR: Config directory is not writable: ${CONFIG_DIR}" >&2
log "xteve would run as UID:GID ${RUN_UID}:${RUN_GID}" >&2
ls -ld "${CONFIG_DIR}" >&2 || true
log "Hint: set PUID/PGID to the owner of the host directory, or fix its ownership/permissions on the host." >&2
exit 1
fi
rm -f "${CONFIG_DIR}/.xteve-write-test"
log "Using config directory: ${CONFIG_DIR}"
log "Running as UID:GID ${RUN_UID}:${RUN_GID} (PUID/PGID)"
if [ -f "${CONFIG_DIR}/settings.json" ]; then
log "settings.json details: $(ls -l "${CONFIG_DIR}/settings.json" | awk '{print $1, $3, $4, $5, $9}')"
fi
exec su-exec "${RUN_USER}:${RUN_USER}" "${XTEVE_BIN}" -config "${CONFIG_DIR}" -port "${PORT}" "$@"
fi
# Started with --user: keep the given identity, no id or ownership changes.
if [ -n "${PUID:-}${PGID:-}${XTEVE_UID:-}${XTEVE_GID:-}" ]; then
log "Container started as non-root; PUID/PGID are ignored"
fi
if ! touch "${CONFIG_DIR}/.xteve-write-test" 2>/dev/null; then
log "ERROR: Config directory is not writable: ${CONFIG_DIR}" >&2
log "Running as UID:GID $(id -u):$(id -g)" >&2
ls -ld "${CONFIG_DIR}" >&2 || true
log "Hint: ensure the host path ownership/permissions allow this UID:GID to write, or drop --user and set PUID/PGID instead." >&2
exit 1
fi
rm -f "${CONFIG_DIR}/.xteve-write-test"
log "Using config directory: ${CONFIG_DIR}"
log "Running as UID:GID $(id -u):$(id -g)"
if [ -f "${CONFIG_DIR}/settings.json" ]; then
log "settings.json details: $(ls -l "${CONFIG_DIR}/settings.json" | awk '{print $1, $3, $4, $5, $9}')"
fi
exec "${XTEVE_BIN}" -config "${CONFIG_DIR}" -port "${PORT}" "$@"