#!/bin/bash # /usr/local/bin/dos-shell # Prepare the SvarDOS environment and launch dosemu. set -euo pipefail ALLOWED_LIST="/etc/dos_allowed" ALLOWED_REPO="/opt/allowed_repo" ENV_DIR="/etc/dos_env" AUTOEXEC_TEMPLATE="${ENV_DIR}/AUTOEXEC.BAT" CONFIG_TEMPLATE="${ENV_DIR}/CONFIG.SYS" SVARDOS_ROOT="${SVARDOS_ROOT:-/opt/svardos}" SVARDOS_BASE="${SVARDOS_BASE:-${SVARDOS_ROOT}/base}" ALLOW_MODE="${DOS_ALLOW_MODE:-all}" DEFAULT_DOS_USER="dosuser" INSTALL_SENTINEL_NAME=".svardos_installed" if [ "$(id -u)" -eq 0 ]; then DOS_USER="${DEFAULT_DOS_USER}" DOS_HOME="$(getent passwd "${DOS_USER}" | cut -d: -f6)" else DOS_USER="$(id -un)" DOS_HOME="${HOME}" fi if [ -z "${DOS_HOME}" ]; then echo "Unable to determine home directory for ${DOS_USER}" >&2 exit 1 fi DOSEMU_DIR="${DOS_HOME}/.dosemu" C_DRIVE="${DOSEMU_DIR}/drive_c" INSTALL_SENTINEL_PATH="${DOSEMU_DIR}/${INSTALL_SENTINEL_NAME}" DOSEMU_ARGS=() detect_dosemu_args() { local mode="${DOS_TERMINAL_MODE:-auto}" if [ "${mode}" = "auto" ]; then if [ -n "${DISPLAY:-}" ]; then mode="x" else mode="terminal" fi fi case "${mode,,}" in x|sdl|window|gui) DOSEMU_ARGS=(-X -K "${C_DRIVE}") ;; terminal|tty|text) DOSEMU_ARGS=(-td -K "${C_DRIVE}") ;; dumb) DOSEMU_ARGS=(-dumb -K "${C_DRIVE}") ;; *) echo "Unknown DOS_TERMINAL_MODE '${mode}', defaulting to terminal mode." >&2 DOSEMU_ARGS=(-td -K "${C_DRIVE}") ;; esac } allowed_entries=() mkdir -p "${DOSEMU_DIR}" "${C_DRIVE}" if [ "$(id -u)" -eq 0 ] && [ ! -d "${ENV_DIR}" ]; then mkdir -p "${ENV_DIR}" fi reset_drive_c() { if [ ! -d "${SVARDOS_BASE}" ] || [ -z "$(ls -A "${SVARDOS_BASE}")" ]; then echo "SvarDOS base directory ${SVARDOS_BASE} is empty. Aborting." >&2 exit 1 fi rm -rf "${C_DRIVE:?}/"* cp -a "${SVARDOS_BASE}/." "${C_DRIVE}/" if [ "$(id -u)" -eq 0 ]; then chown -R "${DOS_USER}:${DOS_USER}" "${DOSEMU_DIR}" fi if [ -f "${DOS_HOME}/.dosemurc" ]; then rm -f "${DOS_HOME}/.dosemurc" fi echo "SvarDOS base staged to ${C_DRIVE}" } sync_allowed_content() { allowed_entries=() local mode="${ALLOW_MODE}" if [ "${mode}" = "all" ]; then if [ -d "${ALLOWED_REPO}" ] && [ "$(ls -A "${ALLOWED_REPO}" 2>/dev/null)" ]; then cp -a "${ALLOWED_REPO}/." "${C_DRIVE}/" allowed_entries+=("ALL") fi return fi if [ "${mode}" != "list" ] && [ "${mode}" != "allowlist" ]; then echo "Unknown DOS_ALLOW_MODE '${mode}', defaulting to allowlist mode." >&2 mode="list" ALLOW_MODE="${mode}" fi if [ -f "${ALLOWED_LIST}" ]; then while IFS= read -r line || [ -n "$line" ]; do line="$(printf '%s' "$line" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" case "$line" in ''|\#*) continue ;; esac allowed_entries+=("$line") done < "${ALLOWED_LIST}" fi for entry in "${allowed_entries[@]}"; do src="${ALLOWED_REPO}/${entry}" if [ -e "$src" ]; then dst_dir="$(dirname "${entry}")" mkdir -p "${C_DRIVE}/${dst_dir}" cp -a "$src" "${C_DRIVE}/${dst_dir}/" else echo "Warning: allowed file not found: $src" >&2 fi done } apply_templates() { if [ -f "${AUTOEXEC_TEMPLATE}" ]; then cp "${AUTOEXEC_TEMPLATE}" "${C_DRIVE}/AUTOEXEC.BAT" fi if [ -f "${CONFIG_TEMPLATE}" ]; then cp "${CONFIG_TEMPLATE}" "${C_DRIVE}/CONFIG.SYS" fi } detect_dosemu_args FORCE_CPU_EMULATION=0 detect_kvm_support() { if [ ! -c /dev/kvm ]; then return 1 fi if [ "$(id -u)" -eq 0 ]; then if command -v runuser >/dev/null 2>&1; then runuser -u "${DOS_USER}" -- test -r /dev/kvm 2>/dev/null || return 1 return 0 fi return 0 fi [ -r /dev/kvm ] } if ! detect_kvm_support; then echo "dos-shell: disabling KVM acceleration (no usable /dev/kvm)" >&2 FORCE_CPU_EMULATION=1 fi should_disable_audio=0 audio_mode="${DOS_AUDIO_MODE:-auto}" case "${audio_mode,,}" in mute|off|disabled) should_disable_audio=1 ;; force|on|enabled) should_disable_audio=0 ;; auto) if [ -n "${SSH_CONNECTION:-}" ]; then if [ -z "${PULSE_SERVER:-}" ] && [ -z "${PIPEWIRE_REMOTE:-}" ]; then should_disable_audio=1 fi fi ;; *) echo "Unknown DOS_AUDIO_MODE '${audio_mode}', defaulting to auto." >&2 if [ -n "${SSH_CONNECTION:-}" ] && [ -z "${PULSE_SERVER:-}" ] && [ -z "${PIPEWIRE_REMOTE:-}" ]; then should_disable_audio=1 fi ;; esac if [ "${should_disable_audio}" -eq 1 ]; then echo "dos-shell: muting DOS audio for this session" >&2 export AO_DRIVER="${AO_DRIVER:-null}" fi detect_landlock_support() { local header for header in /usr/include/linux/landlock.h \ /usr/include/uapi/linux/landlock.h \ /usr/lib*/gcc/*/*/include/uapi/linux/landlock.h; do if [ -f "$header" ] && grep -q 'LANDLOCK_ACCESS_FS_REFER' "$header" 2>/dev/null; then return 0 fi done return 1 } landlock_mode="${DOS_LANDLOCK_MODE:-auto}" case "${landlock_mode,,}" in auto) if detect_landlock_support; then : else echo "dos-shell: disabling Landlock sandbox (ABI mismatch detected)" >&2 DOSEMU_ARGS+=(-p) fi ;; off|disable|disabled) echo "dos-shell: Landlock sandbox disabled by DOS_LANDLOCK_MODE" >&2 DOSEMU_ARGS+=(-p) ;; force|on|enabled) ;; *) echo "Unknown DOS_LANDLOCK_MODE '${landlock_mode}', defaulting to auto." >&2 if detect_landlock_support; then : else echo "dos-shell: disabling Landlock sandbox (ABI mismatch detected)" >&2 DOSEMU_ARGS+=(-p) fi ;; esac write_autoconfig() { local need_config=0 local conf_path="${DOSEMU_DIR}/dosemurc" local legacy_conf="${DOS_HOME}/.dosemurc" local managed_marker="# Managed by dos-shell for compatibility" if [ "${FORCE_CPU_EMULATION}" -eq 1 ] || [ "${should_disable_audio}" -eq 1 ]; then need_config=1 fi if [ "${need_config}" -eq 1 ]; then if [ -f "${legacy_conf}" ] && grep -qF "${managed_marker}" "${legacy_conf}" 2>/dev/null; then rm -f "${legacy_conf}" fi local tmp_conf="${conf_path}.tmp" { echo "${managed_marker}" echo "define parser_version_3" if [ "${FORCE_CPU_EMULATION}" -eq 1 ]; then echo '$_cpu_vm = "emulated"' echo '$_cpu_vm_dpmi = "emulated"' fi if [ "${should_disable_audio}" -eq 1 ]; then echo '$_sound = (off)' echo '$_pcm_hpf = (off)' fi } > "${tmp_conf}" mv "${tmp_conf}" "${conf_path}" if [ "$(id -u)" -eq 0 ]; then chown "${DOS_USER}:${DOS_USER}" "${conf_path}" fi echo "dos-shell: wrote ${conf_path} overrides" >&2 else for candidate in "${conf_path}" "${legacy_conf}"; do if [ -f "${candidate}" ] && grep -qF "${managed_marker}" "${candidate}" 2>/dev/null; then rm -f "${candidate}" echo "dos-shell: removed autogenerated ${candidate}" >&2 fi done fi } if [ "${DOS_FORCE_INSTALL:-0}" = "1" ] || [ ! -f "${INSTALL_SENTINEL_PATH}" ]; then reset_drive_c touch "${INSTALL_SENTINEL_PATH}" if [ "$(id -u)" -eq 0 ]; then chown "${DOS_USER}:${DOS_USER}" "${INSTALL_SENTINEL_PATH}" fi else echo "Reusing existing SvarDOS installation on ${C_DRIVE}" >&2 fi sync_allowed_content apply_templates write_autoconfig if [ "$(id -u)" -eq 0 ]; then chown -R "${DOS_USER}:${DOS_USER}" "${DOSEMU_DIR}" fi if [ "${ALLOW_MODE}" = "all" ] && [ "${#allowed_entries[@]}" -gt 0 ]; then echo "All files in ${ALLOWED_REPO} are available on drive C:." elif [ "${#allowed_entries[@]}" -gt 0 ]; then echo "Allowlisted files copied to C: from ${ALLOWED_REPO}:" for entry in "${allowed_entries[@]}"; do echo " ${entry}" done fi if [ "$(id -u)" -eq 0 ]; then exec runuser -u "${DOS_USER}" -- dosemu "${DOSEMU_ARGS[@]}" else exec dosemu "${DOSEMU_ARGS[@]}" fi