diff --git a/build.sh b/build.sh index 12171b7..5de11ab 100755 --- a/build.sh +++ b/build.sh @@ -14,6 +14,8 @@ set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" source "$SCRIPT_DIR/scripts/config.sh" source "$SCRIPT_DIR/scripts/utils.sh" +# Cấu hình debug boot: xoá quiet/splash, đổi tên distro, tắt plymouth +source "$SCRIPT_DIR/scripts/boot_config.sh" # --- Clean --- if [ "${1}" = "--clean" ]; then @@ -34,6 +36,37 @@ check_root install_deps install_gum +# --- Trap: tự động dọn khi build thất bại --- +# Được kích hoạt bởi EXIT — không làm gì nếu BUILD_OK=1 (set cuối script) +# Mục đích: đảm bảo bind mounts luôn được umount dù build lỗi ở bất kỳ bước nào, +# tránh lặp lại bug rm -rf vào /dev /proc của host +cleanup_on_fail() { + # Build thành công → bỏ qua + [ "${BUILD_OK:-0}" = "1" ] && return 0 + + # Dùng echo trực tiếp vì gum/utils có thể chưa sẵn sàng lúc trap chạy + echo "" + echo -e "\033[0;31m[ERROR ]\033[0m Build thất bại! Đang dọn dẹp tự động..." + + # Umount theo thứ tự ngược với mount — dev/pts phải trước dev + sync 2>/dev/null || true + umount "$WORK_DIR/squashfs/proc" 2>/dev/null || true + umount "$WORK_DIR/squashfs/sys" 2>/dev/null || true + umount "$WORK_DIR/squashfs/dev/pts" 2>/dev/null || true + umount "$WORK_DIR/squashfs/dev" 2>/dev/null || true + umount "$WORK_DIR/mnt" 2>/dev/null || true + + # Chỉ xoá nếu không còn mount nào — bảo vệ host khỏi rm -rf vào /dev /proc thật + if grep -q " $WORK_DIR/squashfs/" /proc/mounts 2>/dev/null; then + echo -e "\033[0;31m[ERROR ]\033[0m Vẫn còn mount trong squashfs — BỎ QUA xoá thư mục." + echo -e "\033[0;31m[ERROR ]\033[0m Dọn thủ công: sudo umount -Rlf $WORK_DIR/squashfs && sudo rm -rf $WORK_DIR/squashfs $WORK_DIR/custom $WORK_DIR/mnt" + else + rm -rf "$WORK_DIR/squashfs" "$WORK_DIR/custom" "$WORK_DIR/mnt" 2>/dev/null || true + echo -e "\033[1;33m[ WARN ]\033[0m Đã xoá build dirs (cache giữ lại để build lần sau nhanh hơn)." + fi +} +trap cleanup_on_fail EXIT + # --- Release mode --- ISO_ARG="" for arg in "$@"; do @@ -60,8 +93,13 @@ source "$SCRIPT_DIR/scripts/customize.sh" source "$SCRIPT_DIR/scripts/repack.sh" step_extract +# Mặc định: chế độ debug — sửa bootloader + tắt plymouth ngay sau khi giải nén +step_boot_config step_customize step_repack +# Đánh dấu build thành công — trap EXIT sẽ bỏ qua cleanup_on_fail +BUILD_OK=1 + # --- Kết quả --- print_result diff --git a/scripts/boot_config.sh b/scripts/boot_config.sh new file mode 100644 index 0000000..005fb58 --- /dev/null +++ b/scripts/boot_config.sh @@ -0,0 +1,204 @@ +#!/bin/bash +# Cấu hình bootloader + plymouth theo build mode: +# - Branding (Linux Mint → CaramOS): LUÔN áp dụng, cả debug lẫn release +# - Debug mode (mặc định, SQUASHFS_COMP=lz4): +# · Xoá quiet + splash khỏi kernel cmdline → hiện kernel log khi boot +# · Mask plymouth services + xoá initramfs hook → không che log +# - Release mode (--release, SQUASHFS_COMP=xz): +# · Giữ nguyên quiet + splash → boot im lặng, đẹp cho người dùng cuối +# · Giữ nguyên plymouth → splash screen hiện bình thường +# +# Chạy SAU step_extract, TRƯỚC step_customize +# Tác động lên: +# $WORK_DIR/custom/ → file cấu hình bootloader của ISO (isolinux, grub) +# $WORK_DIR/squashfs/ → rootfs (chỉ bị sửa ở debug mode) + +step_boot_config() { + local ISO_DIR="$WORK_DIR/custom" + local ROOTFS_DIR="$WORK_DIR/squashfs" + + # SQUASHFS_COMP được set trong build.sh: + # lz4 (mặc định) = debug, xz (--release) = release + local IS_DEBUG=true + [ "${SQUASHFS_COMP:-lz4}" = "xz" ] && IS_DEBUG=false + + if $IS_DEBUG; then + info "[2.5/7] Cấu hình debug boot (no quiet, no plymouth)..." + else + info "[2.5/7] Cấu hình release boot (branding only)..." + fi + + # ---------------------------------------------------------------- + # 1. Sửa isolinux / syslinux (BIOS boot) + # File thường gặp: isolinux/txt.cfg, isolinux/isolinux.cfg, + # isolinux/live.cfg, syslinux/syslinux.cfg + # ---------------------------------------------------------------- + local ISOLINUX_FILES + ISOLINUX_FILES=$(find "$ISO_DIR" \ + -path "*/isolinux/*.cfg" -o \ + -path "*/syslinux/*.cfg" \ + 2>/dev/null) + + if [ -n "$ISOLINUX_FILES" ]; then + info " → Sửa isolinux/syslinux config..." + echo "$ISOLINUX_FILES" | while IFS= read -r cfg; do + # Đổi tên distro trong menu label/title — áp dụng mọi mode + sed -i 's/Linux Mint/CaramOS/gI' "$cfg" + + # Chỉ xoá quiet/splash ở debug mode + if $IS_DEBUG; then + sed -i 's/\bquiet\b//g; s/\bsplash\b//g' "$cfg" + fi + + ok " Đã sửa: $(basename "$cfg")" + done + else + warn " → Không tìm thấy isolinux/syslinux config, bỏ qua." + fi + + # ---------------------------------------------------------------- + # 2. Sửa GRUB config (UEFI + BIOS hybrid boot) + # File thường gặp: boot/grub/grub.cfg, boot/grub/loopback.cfg, + # EFI/boot/grub.cfg + # ---------------------------------------------------------------- + local GRUB_FILES + GRUB_FILES=$(find "$ISO_DIR" \ + -path "*/grub/*.cfg" -o \ + -path "*/EFI/boot/*.cfg" \ + 2>/dev/null) + + if [ -n "$GRUB_FILES" ]; then + info " → Sửa GRUB config..." + echo "$GRUB_FILES" | while IFS= read -r cfg; do + # Đổi tên distro trong menu entry title — áp dụng mọi mode + sed -i 's/Linux Mint/CaramOS/gI' "$cfg" + + # Chỉ xoá quiet/splash ở debug mode + if $IS_DEBUG; then + sed -i 's/\bquiet\b//g; s/\bsplash\b//g' "$cfg" + fi + + ok " Đã sửa: $(basename "$cfg")" + done + else + warn " → Không tìm thấy GRUB config, bỏ qua." + fi + + # ---------------------------------------------------------------- + # 3. Set syslinux splash background — áp dụng mọi mode + # + # Mint ISO có thể đặt MENU BACKGROUND ở nhiều file khác nhau tuỳ + # version: stdmenu.cfg, isolinux.cfg, txt.cfg, v.v. + # Chiến lược: + # a) Tìm thư mục isolinux trong ISO + # b) Copy banner → isolinux/splash.png + # c) Grep TẤT CẢ cfg trong isolinux/ tìm file nào đang có + # "MENU BACKGROUND" → update dòng đó → đúng file, đúng chỗ + # d) Nếu không file nào có → thêm vào stdmenu.cfg nếu tồn tại, + # không thì thêm vào isolinux.cfg (fallback cuối cùng) + # ---------------------------------------------------------------- + local BANNER_SRC="$SCRIPT_DIR/caramos_vietnam_banner.png" + local ISOLINUX_DIR + ISOLINUX_DIR=$(find "$ISO_DIR" -maxdepth 3 -name "isolinux.bin" \ + -exec dirname {} \; 2>/dev/null | head -1) + + if [ ! -f "$BANNER_SRC" ]; then + warn " → Không tìm thấy caramos_vietnam_banner.png ở root repo, bỏ qua splash." + elif [ -z "$ISOLINUX_DIR" ]; then + warn " → Không tìm thấy thư mục isolinux, bỏ qua splash." + else + info " → Set syslinux splash background..." + + # Copy banner vào isolinux/ với tên splash.png + cp "$BANNER_SRC" "$ISOLINUX_DIR/splash.png" + ok " Đã copy: splash.png → $ISOLINUX_DIR/" + + # Tìm file cfg nào ĐANG chứa dòng MENU BACKGROUND (bất kể giá trị cũ) + local BG_CFG + BG_CFG=$(grep -rli "MENU BACKGROUND" "$ISOLINUX_DIR/" 2>/dev/null | head -1) + + if [ -n "$BG_CFG" ]; then + # File đã có MENU BACKGROUND → chỉ update giá trị, giữ nguyên vị trí dòng + sed -i 's|^\(MENU BACKGROUND\).*|\1 splash.png|gI' "$BG_CFG" + ok " Updated MENU BACKGROUND trong: $(basename "$BG_CFG")" + else + # Không file nào có MENU BACKGROUND → thêm mới + # Ưu tiên: stdmenu.cfg (Mint đặt MENU directives ở đây) → isolinux.cfg + local INSERT_CFG="$ISOLINUX_DIR/stdmenu.cfg" + [ ! -f "$INSERT_CFG" ] && INSERT_CFG="$ISOLINUX_DIR/isolinux.cfg" + + if [ -f "$INSERT_CFG" ]; then + # Chèn ngay sau dòng MENU COLOR đầu tiên nếu có (cùng nhóm MENU directives), + # không thì chèn sau dòng đầu tiên của file + if grep -qi "^MENU COLOR" "$INSERT_CFG"; then + sed -i '0,/^MENU COLOR/s//MENU BACKGROUND splash.png\n&/' "$INSERT_CFG" + else + sed -i '1s|^|MENU BACKGROUND splash.png\n|' "$INSERT_CFG" + fi + ok " Thêm MENU BACKGROUND vào: $(basename "$INSERT_CFG")" + else + warn " Không tìm thấy cfg phù hợp để thêm MENU BACKGROUND." + fi + fi + fi + + # ---------------------------------------------------------------- + # 4+5. Vô hiệu hoá plymouth — CHỈ ở debug mode + # Release mode giữ nguyên plymouth để có splash screen đẹp + # ---------------------------------------------------------------- + if $IS_DEBUG; then + # -- 4. Mask plymouth systemd services -- + # Mask các service để systemd không khởi động splash screen, + # kernel log sẽ hiển thị trực tiếp ra màn hình + if [ -d "$ROOTFS_DIR/lib/systemd/system" ]; then + info " → Mask plymouth services trong rootfs..." + + # Danh sách service plymouth cần tắt + local PLYMOUTH_SERVICES=( + "plymouth-start.service" + "plymouth-read-write.service" + "plymouth-quit.service" + "plymouth-quit-wait.service" + "plymouth-reboot.service" + "plymouth-kexec.service" + "plymouth-poweroff.service" + "plymouth-halt.service" + ) + + # Đảm bảo thư mục systemd/system tồn tại trong etc (nơi override) + mkdir -p "$ROOTFS_DIR/etc/systemd/system" + + for svc in "${PLYMOUTH_SERVICES[@]}"; do + local svc_path="$ROOTFS_DIR/lib/systemd/system/$svc" + # Chỉ mask nếu service thực sự tồn tại trong rootfs + if [ -f "$svc_path" ] || [ -L "$svc_path" ]; then + # Mask = symlink /dev/null, systemd sẽ bỏ qua hoàn toàn + ln -sf /dev/null "$ROOTFS_DIR/etc/systemd/system/$svc" + ok " Đã mask: $svc" + fi + done + else + warn " → Không tìm thấy systemd trong rootfs, bỏ qua mask plymouth." + fi + + # -- 5. Vô hiệu hook initramfs của plymouth -- + # KHÔNG xoá /usr/bin/plymouth binary — nếu xoá, dpkg post-install + # scripts (initramfs-tools, kernel) sẽ gọi plymouth → exit 127 → + # toàn bộ apt/dpkg lỗi dây chuyền trong chroot. + # + # Thay vào đó: xoá hook initramfs để plymouth KHÔNG được đóng gói + # vào initrd → kernel boot không có splash, log hiện bình thường. + # Binary vẫn tồn tại cho dpkg, nhưng systemd services đã bị mask + # ở bước 4 nên plymouth sẽ không chạy khi boot. + local INITRAMFS_HOOK="$ROOTFS_DIR/usr/share/initramfs-tools/hooks/plymouth" + if [ -f "$INITRAMFS_HOOK" ]; then + info " → Xoá hook initramfs của plymouth (giữ binary cho dpkg)..." + rm -f "$INITRAMFS_HOOK" + ok " Đã xoá: /usr/share/initramfs-tools/hooks/plymouth" + fi + + ok "[2.5/7] Boot config debug xong." + else + ok "[2.5/7] Boot config release xong (plymouth giữ nguyên)." + fi +} diff --git a/scripts/customize.sh b/scripts/customize.sh index 7a72fcb..0a24df0 100644 --- a/scripts/customize.sh +++ b/scripts/customize.sh @@ -19,6 +19,10 @@ step_customize() { cp "$SCRIPT_DIR/config/packages.txt" "$WORK_DIR/squashfs/tmp/packages.txt" chroot "$WORK_DIR/squashfs" /bin/bash -c ' export DEBIAN_FRONTEND=noninteractive + # Đổi sang mirror VN (BizFly Cloud) trước khi apt-get update + # archive.ubuntu.com đặt ở US/EU, tốc độ ~150kB/s từ VN — mirror VN đạt ~5-15MB/s + sed -i "s|http://archive.ubuntu.com/ubuntu|http://mirror.bizflycloud.vn/ubuntu|g" /etc/apt/sources.list + sed -i "s|http://security.ubuntu.com/ubuntu|http://mirror.bizflycloud.vn/ubuntu|g" /etc/apt/sources.list apt-get update grep -v "^#" /tmp/packages.txt | grep -v "^$" | xargs apt-get install -y rm /tmp/packages.txt diff --git a/scripts/repack.sh b/scripts/repack.sh index bed4ea4..d7ac443 100644 --- a/scripts/repack.sh +++ b/scripts/repack.sh @@ -4,11 +4,27 @@ step_repack() { info "[7/7] Đóng gói ISO..." + local SFS="$WORK_DIR/squashfs" + + # Đảm bảo các virtual fs dir trống sạch trước khi pack vào squashfs. + # KHÔNG dùng -e proc/sys/dev/run để loại trừ — nếu loại trừ, các thư mục + # này sẽ KHÔNG TỒN TẠI trong squashfs, casper sẽ không có chỗ để mount + # devtmpfs/proc/sysfs vào → /dev/null không tồn tại → boot crash. + # Các dir phải có mặt nhưng TRỐNG; chúng đã được umount ở step_customize. + for dir in proc sys dev run; do + if [ -d "$SFS/$dir" ]; then + # Xoá nội dung bên trong nhưng giữ thư mục gốc + find "$SFS/$dir" -mindepth 1 -delete 2>/dev/null || true + else + # Thư mục không tồn tại → tạo lại để casper có chỗ mount + mkdir -p "$SFS/$dir" + fi + done + # Rebuild squashfs info " → Tạo filesystem.squashfs (${SQUASHFS_COMP})..." - mksquashfs "$WORK_DIR/squashfs" "$WORK_DIR/custom/casper/filesystem.squashfs" \ - -comp $SQUASHFS_COMP $SQUASHFS_OPTS \ - -e proc -e sys -e dev -e run + mksquashfs "$SFS" "$WORK_DIR/custom/casper/filesystem.squashfs" \ + -comp $SQUASHFS_COMP $SQUASHFS_OPTS ok "squashfs xong." # Cập nhật filesystem.size diff --git a/splash.png b/splash.png new file mode 100644 index 0000000..3685981 Binary files /dev/null and b/splash.png differ