From d56c24275af7499e834ba3a1609572008ee76268 Mon Sep 17 00:00:00 2001 From: zj <1052308357@qq.com> Date: Wed, 11 Feb 2026 14:59:51 +0800 Subject: [PATCH] auto install gurb --- __pycache__/backend.cpython-39.pyc | Bin 38239 -> 40438 bytes backend.py | 162 +++++++++++++++++++++++------ 2 files changed, 132 insertions(+), 30 deletions(-) diff --git a/__pycache__/backend.cpython-39.pyc b/__pycache__/backend.cpython-39.pyc index 3242a64b1c6b1188c63bbd7a924558deba8b095e..3da93ce97a6e51dd9b5ef27524aa212b321d9268 100644 GIT binary patch delta 9436 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 delta 7367 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 H str: return "unknown" -def _get_grub_package_install_cmd(distro_type: str) -> str: - """获取安装 GRUB 包的命令提示""" - package_commands = { - "centos": "yum install grub2-tools grub2-pc", - "fedora": "dnf install grub2-tools grub2-pc", - "debian": "apt-get install grub-pc", - "ubuntu": "apt-get install grub-pc", - "arch": "pacman -S grub", - "opensuse": "zypper install grub2", +def _get_grub_packages(distro_type: str) -> Tuple[List[str], str]: + """ + 获取安装 GRUB 包所需的包列表和包管理器命令。 + 返回: (包列表, 包管理器基命令) + """ + package_map = { + "centos": (["grub2-tools", "grub2-pc"], "yum install -y"), + "fedora": (["grub2-tools", "grub2-pc"], "dnf install -y"), + "rhel": (["grub2-tools", "grub2-pc"], "yum install -y"), + "rocky": (["grub2-tools", "grub2-pc"], "dnf install -y"), + "almalinux": (["grub2-tools", "grub2-pc"], "dnf install -y"), + "debian": (["grub-pc"], "apt-get install -y"), + "ubuntu": (["grub-pc"], "apt-get install -y"), + "arch": (["grub"], "pacman -S --noconfirm"), + "manjaro": (["grub"], "pacman -S --noconfirm"), + "opensuse": (["grub2"], "zypper install -y"), + "void": (["grub"], "xbps-install -y"), + "gentoo": (["sys-boot/grub"], "emerge"), } - return package_commands.get(distro_type, "安装 GRUB 包(请查阅发行版文档)") + return package_map.get(distro_type, (["grub"], "包管理器安装")) + + +def _auto_install_grub(mount_point: str, distro_type: str) -> bool: + """ + 自动在 chroot 环境中安装 GRUB 包。 + 返回是否成功。 + """ + log_step("自动安装 GRUB 包", f"发行版: {distro_type}") + + packages, pkg_cmd = _get_grub_packages(distro_type) + + if not packages: + log_warning(f"未知的发行版 '{distro_type}',无法自动安装 GRUB") + return False + + chroot_cmd_prefix = ["sudo", "chroot", mount_point] + + # 1. 首先检查网络连接(通过 ping) + log_info("检查网络连接...") + success, _, _ = run_command( + chroot_cmd_prefix + ["ping", "-c", "1", "8.8.8.8"], + "检查网络连接", + timeout=10 + ) + if not success: + # 尝试使用主机的网络配置 + log_warning("chroot 环境无网络,尝试从 Live 环境复制 DNS 配置...") + + # 复制 /etc/resolv.conf + resolv_source = "/etc/resolv.conf" + resolv_target = os.path.join(mount_point, "etc/resolv.conf") + if os.path.exists(resolv_source): + try: + shutil.copy2(resolv_source, resolv_target) + log_info("已复制 DNS 配置") + except Exception as e: + log_warning(f"复制 DNS 配置失败: {e}") + + # 再次检查网络 + success, _, _ = run_command( + chroot_cmd_prefix + ["ping", "-c", "1", "8.8.8.8"], + "再次检查网络连接", + timeout=10 + ) + if not success: + log_error("chroot 环境无法连接网络,无法自动安装 GRUB") + log_info("请手动安装 GRUB 包后再运行此工具") + return False + + log_success("网络连接正常") + + # 2. 更新包列表(某些发行版需要) + update_cmds = { + "debian": ["apt-get", "update"], + "ubuntu": ["apt-get", "update"], + "arch": ["pacman", "-Sy"], + "manjaro": ["pacman", "-Sy"], + } + + if distro_type in update_cmds: + log_info(f"更新 {distro_type} 包列表...") + run_command( + chroot_cmd_prefix + update_cmds[distro_type], + f"更新 {distro_type} 包列表", + timeout=120 + ) + + # 3. 安装 GRUB 包 + log_info(f"安装 GRUB 包: {', '.join(packages)}") + + install_cmd = chroot_cmd_prefix + pkg_cmd.split() + packages + success, stdout, stderr = run_command( + install_cmd, + f"安装 GRUB 包 ({' '.join(packages)})", + timeout=300 + ) + + if success: + log_success(f"✓ GRUB 包安装成功") + return True + else: + log_error(f"✗ GRUB 包安装失败: {stderr}") + return False def check_chroot_environment(mount_point: str, distro_type: str = "unknown") -> Tuple[bool, str]: """ 检查 chroot 环境是否可用。 检测可用的 GRUB 命令及其版本。 + 如果缺少 GRUB 命令,尝试自动安装。 """ log_step("检查 chroot 环境", f"挂载点: {mount_point}") @@ -915,27 +1008,36 @@ def check_chroot_environment(mount_point: str, distro_type: str = "unknown") -> log_debug(f" ✗ 未找到命令: {cmd}") if not found_commands: - log_error(f"=" * 60) - log_error(f"chroot 环境中未找到关键的 GRUB 命令!") - log_error(f"") - log_error(f"目标系统缺少 GRUB 引导加载器。") - log_error(f"") - log_error(f"解决方案:") - log_error(f"1. 进入目标系统的 chroot 环境") - log_error(f"2. 安装 GRUB 包:") - install_cmd = _get_grub_package_install_cmd(distro_type) - log_error(f" {install_cmd}") - log_error(f"") - log_error(f"3. 然后重新运行此工具") - log_error(f"=" * 60) + log_warning(f"chroot 环境中未找到关键的 GRUB 命令") + log_info(f"尝试自动安装 GRUB 包...") - error_msg = ( - f"目标系统缺少 GRUB 工具。\n" - f"请先安装 GRUB 包:\n" - f"{install_cmd}\n" - f"然后重新运行修复工具。" - ) - return False, error_msg + # 尝试自动安装 + if _auto_install_grub(mount_point, distro_type): + # 重新检查命令 + log_info("重新检查 GRUB 命令...") + for cmd in critical_commands: + success, _, _ = run_command(["sudo", "chroot", mount_point, "which", cmd], + f"检查命令 {cmd}", timeout=10) + if success: + found_commands.append(cmd) + log_info(f" ✓ 找到命令: {cmd}") + + if found_commands: + log_success("✓ 自动安装成功,GRUB 命令已可用") + else: + log_error("✗ 自动安装后仍未找到 GRUB 命令") + return False, "自动安装 GRUB 失败,请手动安装" + else: + log_error("=" * 60) + log_error(f"无法自动安装 GRUB 包") + log_error(f"") + log_error(f"请手动安装 GRUB 包:") + packages, pkg_cmd = _get_grub_packages(distro_type) + log_error(f" chroot {mount_point}") + log_error(f" {pkg_cmd} {' '.join(packages)}") + log_error(f" exit") + log_error(f"=" * 60) + return False, "自动安装 GRUB 失败" # 检查 grub-install 版本(优先使用 grub2-install 如果存在) grub_cmd = "grub2-install" if "grub2-install" in found_commands else "grub-install"