toolchain: make_hdd.sh не виснет при запуске из терминала

Симптом: сборка образа молча вставала навсегда сразу после эхо-строки
команды.  Появилось не «само» — ровно тогда, когда образ разложили по
подкаталогам (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 <noreply@anthropic.com>
This commit is contained in:
2026-07-30 21:57:39 +03:00
parent b22cee3456
commit 59e51f7e83
+25 -6
View File
@@ -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" printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 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. # MBR + раздел 1 на весь диск (тип FAT16), затем формат FAT16.
mpartition -I -c -t $CYLS -h $HEADS -s $SECS z: echo "--- разметка и формат образа ---"
mformat z: 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 for f in "$@"; do
case "$f" in case "$f" in
*:*) dir="${f%%:*}"; src="${f#*:}" *:*) dir="${f%%:*}"; src="${f#*:}"
mmd "z:/$dir" 2>/dev/null || true # Каталог может уже быть — это не ошибка; stderr НЕ прячем, иначе
mcopy -o "$src" "z:/$dir/" ;; # непрошедший вопрос mtools снова окажется невидимым.
*) mcopy -o "$f" z: ;; mt mmd "z:/$dir" >/dev/null 2>&1 || true
mt mcopy -o "$src" "z:/$dir/" ;;
*) mt mcopy -o "$f" z: ;;
esac esac
done done
echo "--- содержимое образа ---" echo "--- содержимое образа ---"
mdir -/ z: mt mdir -/ z:
# RAW -> CHD (без сжатия = быстро; та же геометрия). # RAW -> CHD (без сжатия = быстро; та же геометрия).
echo "--- конвертация RAW -> CHD ---"
chdman createhd -i "$RAW" -o "$OUT_CHD" -c none \ chdman createhd -i "$RAW" -o "$OUT_CHD" -c none \
-chs $CYLS,$HEADS,$SECS -ss 512 -f -chs $CYLS,$HEADS,$SECS -ss 512 -f
echo "готово: $OUT_CHD" echo "готово: $OUT_CHD"