From 59e51f7e8394172ad7f5bc2c7550317827389e4e Mon Sep 17 00:00:00 2001 From: Alexander Petrov Date: Thu, 30 Jul 2026 21:57:39 +0300 Subject: [PATCH] =?UTF-8?q?toolchain:=20make=5Fhdd.sh=20=D0=BD=D0=B5=20?= =?UTF-8?q?=D0=B2=D0=B8=D1=81=D0=BD=D0=B5=D1=82=20=D0=BF=D1=80=D0=B8=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=BF=D1=83=D1=81=D0=BA=D0=B5=20=D0=B8=D0=B7=20?= =?UTF-8?q?=D1=82=D0=B5=D1=80=D0=BC=D0=B8=D0=BD=D0=B0=D0=BB=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Симптом: сборка образа молча вставала навсегда сразу после эхо-строки команды. Появилось не «само» — ровно тогда, когда образ разложили по подкаталогам (BG/KID/GUARD/LEVELS) и в скрипте появился mmd. Диагноз по артефакту, а не по догадке: зависший процесс — `mmd z:/BG`, и lsof показал fd 0 = /dev/null, fd 4 = /dev/tty. То есть mtools (собран с enable-raw-term) для интерактивного вопроса открывает УПРАВЛЯЮЩИЙ ТЕРМИНАЛ напрямую, в обход stdin — поэтому ни `< /dev/null`, ни перенаправления stdio не помогают. А `2>/dev/null` на mmd прятал сам вопрос, из-за чего это выглядело как зависание на пустом месте. Из НЕинтерактивного запуска (CI, фоновая задача) терминала нет, вопрос не задаётся, и баг не воспроизводится — потому и жил незамеченным. Лечение: все вызовы mtools идут через обёртку mt(), которая запускает их в НОВОЙ СЕССИИ (os.setsid + exec питоном; setsid(1) в macOS нет). Без управляющего терминала открывать /dev/tty нечего, и mtools выбирает неинтерактивный путь. Заодно добавлены отметки этапов («разметка и формат», «копирование файлов», «конвертация RAW -> CHD»): если что-то встанет снова, будет сразу видно где, а не после последнего аргумента команды. Co-Authored-By: Claude Opus 5 --- toolchain/make_hdd.sh | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/toolchain/make_hdd.sh b/toolchain/make_hdd.sh index 2b5f43c..5b2b5f2 100755 --- a/toolchain/make_hdd.sh +++ b/toolchain/make_hdd.sh @@ -38,9 +38,25 @@ dd if=/dev/zero of="$RAW" bs=512 count=$SECTORS status=none printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 +# mtools собран с enable-raw-term и, когда ему нужно что-то спросить, +# открывает УПРАВЛЯЮЩИЙ ТЕРМИНАЛ напрямую (/dev/tty) — в обход stdin. +# Поэтому ни `< /dev/null`, ни перенаправление stdio не спасают: из +# терминала сборка молча вставала навсегда на `mmd z:/BG` (подтверждено +# lsof: fd 0 = /dev/null, fd 4 = /dev/tty, процесс спит). Лечится только +# отсутствием управляющего терминала — запускаем mtools в новой сессии, +# тогда открыть /dev/tty нечего и mtools идёт неинтерактивным путём. +# +# setsid(1) в macOS нет, поэтому делаем то же самое питоном. +mt() { + python3 -c 'import os,sys; os.setsid(); os.execvp(sys.argv[1], sys.argv[1:])' "$@" +} + # MBR + раздел 1 на весь диск (тип FAT16), затем формат FAT16. -mpartition -I -c -t $CYLS -h $HEADS -s $SECS z: -mformat z: +echo "--- разметка и формат образа ---" +mt mpartition -I -c -t $CYLS -h $HEADS -s $SECS z: +mt mformat z: + +echo "--- копирование файлов ---" # Аргумент может быть либо файлом (кладём в корень), либо парой # КАТАЛОГ:файл — тогда каталог создаётся на образе и файл кладётся туда. @@ -48,16 +64,19 @@ mformat z: for f in "$@"; do case "$f" in *:*) dir="${f%%:*}"; src="${f#*:}" - mmd "z:/$dir" 2>/dev/null || true - mcopy -o "$src" "z:/$dir/" ;; - *) mcopy -o "$f" z: ;; + # Каталог может уже быть — это не ошибка; stderr НЕ прячем, иначе + # непрошедший вопрос mtools снова окажется невидимым. + mt mmd "z:/$dir" >/dev/null 2>&1 || true + mt mcopy -o "$src" "z:/$dir/" ;; + *) mt mcopy -o "$f" z: ;; esac done echo "--- содержимое образа ---" -mdir -/ z: +mt mdir -/ z: # RAW -> CHD (без сжатия = быстро; та же геометрия). +echo "--- конвертация RAW -> CHD ---" chdman createhd -i "$RAW" -o "$OUT_CHD" -c none \ -chs $CYLS,$HEADS,$SECS -ss 512 -f echo "готово: $OUT_CHD"