#!/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}" "$@"