def final_check(): """ Check for any conflicting options, and warn the user of any potential pitfalls. """ #Check and warn about conflicting settings. #Warn if any OSs aren't modifyable. unmodifyable_oss = [] keys = list(BOOTLOADER_INFO.keys()) keys.sort() for _os in keys: if BOOTLOADER_INFO[_os]["IsModifyable"] is False: unmodifyable_oss.append(_os+", because "+BOOTLOADER_INFO[_os]["Comments"]) if unmodifyable_oss != []: DialogTools.show_msg_dlg(message="Some of the OSs found on your system cannot be " + "modified! These are:\n\n"+'\n'.join(unmodifyable_oss) + "\n\nClick okay to continue.") #Warn if any bootloaders need reinstalling. need_reinstalling = [] for _os in keys: if "MenuEntries" not in BOOTLOADER_INFO[_os].keys(): need_reinstalling.append(_os) if need_reinstalling != []: DialogTools.show_msg_dlg(message="Some of the OSs found on your system have damaged " + "bootloaders! These are:\n\n"+'\n'.join(need_reinstalling) + "\n\nPlease reinstall the bootloaders for these operating " + "systems using \"Bootloader Options\".\n\nClick okay to " + "continue.")
def get_firmware_type(): """Get the firmware type""" #Check if the firmware type is UEFI. #Also, look for UEFI variables. #Make sure efivars module is loaded. If it doesn't exist, continue anyway. CoreTools.start_process("modprobe efivars", privileged=True) #Look for the UEFI vars in some common directories. if os.path.isdir("/sys/firmware/efi/vars") \ and CoreTools.start_process("ls /sys/firmware/efi/vars", return_output=True)[1] != "": uefi_variables = True logger.info("get_firmware_type(): Found UEFI Variables at /sys/firmware/efi/vars...") elif os.path.isdir("/proc/efi/vars") \ and CoreTools.start_process("ls /proc/efi/vars", return_output=True)[1] != "": uefi_variables = True logger.info("get_firmware_type(): Found UEFI Variables at /proc/efi/vars...") elif os.path.isdir("/sys/firmware/efi/efivars") \ and CoreTools.start_process("ls /sys/firmware/efi/efivars", return_output=True)[1] != "": uefi_variables = True logger.info("get_firmware_type(): Found UEFI Variables at /sys/firmware/efi/efivars...") else: logger.info("get_firmware_type(): UEFI vars not found in /sys/firmware/efi/vars, " + "/sys/firmware/efi/efivars, or /proc/efi/vars. This is normal if running " + "on a BIOS system. Determining firmware type a different way...") uefi_variables = False if uefi_variables: #It's UEFI. logger.info("get_firmware_type(): Detected Firmware Type as UEFI.") SYSTEM_INFO["FirmwareType"] = "UEFI" else: #Look a second way. output = CoreTools.start_process("dmidecode -q -t BIOS", return_output=True, privileged=True)[1] if "UEFI" not in output: #It's BIOS. logger.info("get_firmware_type(): Detected Firmware Type as BIOS...") SYSTEM_INFO["FirmwareType"] = "BIOS" else: #It's UEFI. logger.warning("get_firmware_type(): Detected Firmware Type as UEFI, but couldn't " + "find UEFI variables!") SYSTEM_INFO["FirmwareType"] = "UEFI" DialogTools.show_msg_dlg(kind="warning", message="Your computer uses UEFI firmware, " + "but the UEFI variables couldn't be mounted or weren't " + "found. Please ensure you've booted in UEFI mode rather " + "than legacy mode to enable access to the UEFI variables. " + "You can attempt installing a UEFI bootloader without " + "them, but it might not work, and it isn't recommended.")
def ask_for_os_name(partition, is_current_os): """Ask the user if an OS exists on the given partition.""" logger.info("ask_for_os_name(): Asking the user for the name of the OS in "+partition+"...") if is_current_os: DialogTools.show_msg_dlg(kind="warning", message="WxFixBoot couldn't find the name of the " + "current OS. Please name it so that WxFixBoot can function " + "correctly.") result = True else: result = DialogTools.show_yes_no_dlg(message="There is a Linux operating system on " + "partition: "+partition+" but WxFixBoot couldn't " + "find its name. It isn't the currently running OS. " + "Do you want to name it and include it in the " + "list? Only click yes if you believe it is a " + "recent OS. Click Yes if you want to name it, " + "otherwise click No", buttons=("Name it", "Don't name it.")) if result is False: logger.info("ask_for_os_name(): User didn't want to name the OS in "+partition + "! Ignoring it...") #User reported no OS in this partition, ignore it. return None #otherwise... logger.debug("ask_for_os_name(): User reported recent Linux OS in "+partition + " (or OS is current OS). Asking name of OS...") #User reported that an OS is here. result = DialogTools.show_text_entry_dlg(message="Please enter the name of the operating " + "system that is on "+partition+".\nThe name you " + "specify will be used later in the program", title="WxFixBoot - Enter OS Name") return result
def backup_uefi_files(mount_point): """Backup some .efi files, just in case something goes wrong.""" #TODO: Make this smarter when we detect Windows. logger.info("backup_uefi_files(): Backing up UEFI Files...") #We'll backup /EFI/boot/bootx64.efi if it exists, and we'll also backup Windows's uefi files, #if they exist. First do /EFI/boot/bootx64.efi. Fortunately, the UEFI partition is always a #fat32/fat16 filesystem, so case doesn't matter. logger.info("backup_uefi_files(): Backing up "+mount_point+"/boot/efi/boot/boot*.efi...") if os.path.isfile(mount_point+"/boot/efi/EFI/boot/boot*.efi"): if CoreTools.start_process("cp -v "+mount_point+"/boot/efi/EFI/boot/boot*.efi " + mount_point+"/boot/efi/EFI/boot/bkpbootx64.efi", show_output=False, privileged=True) != 0: #Log and warn user if this went wrong. logger.error("backup_uefi_files(): Failed to backup failsafe UEFI boot file! " + "Warning user and continuing...") DialogTools.show_msg_dlg(kind="warning", message="WxFixBoot failed to save " + "your UEFI boot files to the backup directory! Click okay to continue.") #Now do Windows's files, if they exist. logger.info("backup_uefi_files(): Backing up Windows's boot files if they exist...") if os.path.isfile(mount_point+"/boot/efi/EFI/Microsoft/boot/bootmgfw.efi"): if CoreTools.start_process("cp -v "+mount_point+"/boot/efi/EFI/Microsoft/boot/bootmgfw.efi " + mount_point+"/boot/efi/EFI/Microsoft/boot/bkpbootmgfw.efi", show_output=False, privileged=True) != 0: #Log and warn user if this went wrong. logger.error("backup_uefi_files(): Failed to backup Windows's UEFI boot files! " + "Warning user and continuing...") DialogTools.show_msg_dlg(kind="warning", message="WxFixBoot failed to " + "backup Windows's UEFI boot files! Click okay to continue.") logger.info("backup_uefi_files(): Done!")
def get_bootloaders(): """ Find all bootloaders (for each OS), and gather some information about them """ SYSTEM_INFO["ModifyableOSs"] = [] keys = list(OS_INFO.keys()) keys.sort() for _os in keys: #If this is a windows OS, create a standard entry. if "Windows" in _os: CoreStartupTools.make_bootloaderinfo_entry_for_windows(_os) continue #Same for macOS. elif "macOS" in _os: CoreStartupTools.make_bootloaderinfo_entry_for_macos(_os) continue #If this isn't the current OS, do some preparation. if not OS_INFO[_os]["IsCurrentOS"]: #Mount the OS's partition. mount_point = "/mnt/wxfixboot/mountpoints"+OS_INFO[_os]["Partition"] if CoreTools.mount_partition(OS_INFO[_os]["Partition"], mount_point) != 0: logger.error("get_bootloaders(): Failed to mount "+_os+"'s partition! Skipping " + "bootloader detection for this OS.") continue #Set up chroot. if CoreTools.setup_chroot(mount_point) != 0: logger.error("get_bootloaders(): Couldn't set up chroot on "+mount_point + "! Attempting to remove it in case it's partially set up, " + "and then skipping this OS...") CoreTools.teardown_chroot(mount_point) continue else: mount_point = "" #Mount a /boot partition if it exists. if OS_INFO[_os]["BootPartition"] != "Unknown": if CoreTools.mount_partition(OS_INFO[_os]["BootPartition"], mount_point+"/boot") != 0: logger.error("get_bootloaders(): Failed to mount "+_os+"'s /boot partition! " + "Skipping bootloader detection for this OS.") if not OS_INFO[_os]["IsCurrentOS"]: CoreTools.teardown_chroot(mount_point) CoreTools.unmount(mount_point) continue #Mount a /boot/efi partition if it exists. if OS_INFO[_os]["EFIPartition"] != "Unknown": if CoreTools.mount_partition(OS_INFO[_os]["EFIPartition"], mount_point+"/boot/efi") != 0: logger.error("get_bootloaders(): Failed to mount "+_os+"'s /boot/efi partition! " + "Skipping bootloader detection for this OS.") if not OS_INFO[_os]["IsCurrentOS"]: CoreTools.teardown_chroot(mount_point) CoreTools.unmount(mount_point) continue #Look for bootloaders. BOOTLOADER_INFO[_os] = {} BOOTLOADER_INFO[_os]["OSName"] = _os BOOTLOADER_INFO[_os]["Bootloader"], BOOTLOADER_INFO[_os]["AvailableBootloaders"] = \ CoreStartupTools.look_for_bootloaders_on_partition(_os, OS_INFO[_os]["PackageManager"], mount_point, not OS_INFO[_os]["IsCurrentOS"]) BOOTLOADER_INFO[_os]["Timeout"], BOOTLOADER_INFO[_os]["GlobalKernelOptions"], \ BOOTLOADER_INFO[_os]["BootDisk"], BOOTLOADER_INFO[_os]["BLSpecificDefaultOS"], \ BOOTLOADER_INFO[_os]["DefaultOS"] = (10, "Unknown", "Unknown", "Unknown", "Unknown") BOOTLOADER_INFO[_os]["MenuEntries"] = {_os: {}} #For EFI bootloaders, set the boot disk to the OS's EFI Partition. if BOOTLOADER_INFO[_os]["Bootloader"] == "GRUB-UEFI": BOOTLOADER_INFO[_os]["BootDisk"] = OS_INFO[_os]["EFIPartition"] if BOOTLOADER_INFO[_os]["Bootloader"] in ("GRUB-UEFI", "GRUB2") \ and os.path.isfile(mount_point+"/etc/default/grub"): grub_dir, BOOTLOADER_INFO[_os]["MenuEntries"] = \ BootloaderConfigObtainingTools.parse_grub2_menu_data(menu_data="", mount_point=mount_point)[0:2] #Get GRUB2's config. #If we're using fedora, always look for grubenv in the EFI partition (the grubenv #symlink is in /boot/grub2 but it doesn't work when we're chrooting). if OS_INFO[_os]["PackageManager"] == "dnf": grub_dir = mount_point+"/boot/efi/EFI/fedora" BOOTLOADER_INFO[_os]["Timeout"], BOOTLOADER_INFO[_os]["GlobalKernelOptions"], \ BOOTLOADER_INFO[_os]["BLSpecificDefaultOS"] = \ BootloaderConfigObtainingTools.get_grub2_config(mount_point+"/etc/default/grub", grub_dir+"/grubenv", BOOTLOADER_INFO[_os]["MenuEntries"]) #Try to find GRUB's location if this is GRUB2. if BOOTLOADER_INFO[_os]["Bootloader"] == "GRUB2": BOOTLOADER_INFO[_os]["BootDisk"] = \ BootloaderConfigObtainingTools.find_grub(OS_INFO[_os]["Partition"], "GRUB2") #If we didn't find the kernel options, set some defaults here, and warn the user. if BOOTLOADER_INFO[_os]["GlobalKernelOptions"] == "Unknown": BOOTLOADER_INFO[_os]["GlobalKernelOptions"] = "quiet splash nomodeset" logger.warning("get_bootloaders(): Couldn't find "+_os+"'s global kernel options! " + "Assuming 'quiet splash nomodeset'...") DialogTools.show_msg_dlg(message="Couldn't find "+_os+"'s default kernel options! " + "Loading safe defaults instead. Click okay to continue.", kind="warning") #If we didn't find the timeout, set it to 10 seconds here, and warn the user. if BOOTLOADER_INFO[_os]["Timeout"] == "Unknown": BOOTLOADER_INFO[_os]["Timeout"] = 10 logger.warning("get_bootloaders(): Couldn't find "+_os+"'s bootloader timeout! " + "Assuming 10 seconds...") DialogTools.show_msg_dlg(message="Couldn't find "+_os+"'s bootloader timeout! " + "Loading safe defaults instead. Click okay to continue.", kind="warning") #Determine if we can modify this OS from our current one. if OS_INFO[_os]["Arch"] == SYSTEM_INFO["CurrentOSArch"] \ or (OS_INFO[_os]["Arch"] == "i386" and SYSTEM_INFO["CurrentOSArch"] == "x86_64"): BOOTLOADER_INFO[_os]["IsModifyable"] = True BOOTLOADER_INFO[_os]["Comments"] = "Architecture is "+OS_INFO[_os]["Arch"]+"." SYSTEM_INFO["ModifyableOSs"].append(_os) else: BOOTLOADER_INFO[_os]["IsModifyable"] = False BOOTLOADER_INFO[_os]["Comments"] = "Architecture is "+OS_INFO[_os]["Arch"] \ + ". Not modifyable because current OS is " \ + SYSTEM_INFO["CurrentOSArch"]+"." #Initialise some default no-action settings. BOOTLOADER_INFO[_os]["Settings"] = {} BOOTLOADER_INFO[_os]["Settings"]["Reinstall"] = False BOOTLOADER_INFO[_os]["Settings"]["Update"] = False BOOTLOADER_INFO[_os]["Settings"]["KeepExistingTimeout"] = False BOOTLOADER_INFO[_os]["Settings"]["KeepExistingKernelOptions"] = False BOOTLOADER_INFO[_os]["Settings"]["NewKernelOptions"] = \ BOOTLOADER_INFO[_os]["GlobalKernelOptions"] BOOTLOADER_INFO[_os]["Settings"]["NewTimeout"] = BOOTLOADER_INFO[_os]["Timeout"] BOOTLOADER_INFO[_os]["Settings"]["DefaultOS"] = BOOTLOADER_INFO[_os]["DefaultOS"] BOOTLOADER_INFO[_os]["Settings"]["InstallNewBootloader"] = False BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] = "-- Please Select --" BOOTLOADER_INFO[_os]["Settings"]["BackupBootloader"] = False BOOTLOADER_INFO[_os]["Settings"]["BootloaderBackupTarget"] = "-- Please Select --" BOOTLOADER_INFO[_os]["Settings"]["RestoreBootloader"] = False BOOTLOADER_INFO[_os]["Settings"]["BootloaderRestoreSource"] = "-- Please Select --" BOOTLOADER_INFO[_os]["Settings"]["ChangeThisOS"] = False #Initialise GUI state for this OS (True = Enabled, False = Disabled). BOOTLOADER_INFO[_os]["GUIState"] = {} BOOTLOADER_INFO[_os]["GUIState"]["ReinstallCheckBoxState"] = True BOOTLOADER_INFO[_os]["GUIState"]["UpdateCheckBoxState"] = True BOOTLOADER_INFO[_os]["GUIState"]["KeepExistingTimeoutCheckBoxState"] = False BOOTLOADER_INFO[_os]["GUIState"]["NewTimeoutSpinnerState"] = False BOOTLOADER_INFO[_os]["GUIState"]["KeepExistingKernelOptionsCheckBoxState"] = False BOOTLOADER_INFO[_os]["GUIState"]["NewKernelOptionsTextCtrlState"] = False BOOTLOADER_INFO[_os]["GUIState"]["DefaultOSChoiceState"] = False BOOTLOADER_INFO[_os]["GUIState"]["InstallNewBootloaderCheckBoxState"] = True BOOTLOADER_INFO[_os]["GUIState"]["NewBootloaderChoiceState"] = False BOOTLOADER_INFO[_os]["GUIState"]["BackupBootloaderCheckBoxState"] = True BOOTLOADER_INFO[_os]["GUIState"]["BackupBootloaderChoiceState"] = False BOOTLOADER_INFO[_os]["GUIState"]["RestoreBootloaderCheckBoxState"] = True BOOTLOADER_INFO[_os]["GUIState"]["RestoreBootloaderChoiceState"] = False #If there's a seperate EFI partition for this OS, #make sure it's unmounted before removing the chroot. if OS_INFO[_os]["EFIPartition"] != "Unknown": if CoreTools.unmount(mount_point+"/boot/efi") != 0: logger.error("MainBackendTools: get_bootloaders(): Failed to unmount "+mount_point + "/boot/efi! This probably doesn't matter...") #unmount a /boot partition if it exists. if OS_INFO[_os]["BootPartition"] != "Unknown": if CoreTools.unmount(mount_point+"/boot") != 0: logger.error("get_bootloaders(): Failed to unmount "+_os+"'s /boot partition! " + "Continuing anyway...") #Clean up if needed. if not OS_INFO[_os]["IsCurrentOS"]: #Remove chroot. if CoreTools.teardown_chroot(mount_point) != 0: logger.error("get_bootloaders(): Failed to remove chroot from "+mount_point + "! Attempting to continue anyway...") #unmount the OS's partition. if CoreTools.unmount(mount_point) != 0: logger.error("get_bootloaders(): Failed to unmount "+_os+"'s partition! This " + "could indicate that chroot wasn't removed correctly. Continuing " + "anyway...") #Get default OSs. for _os in OS_INFO: #Set sensible defaults for Windows. if "Windows" in _os: BOOTLOADER_INFO[_os]["DefaultBootDevice"] = OS_INFO[_os]["Partition"] BOOTLOADER_INFO[_os]["DefaultOS"] = _os continue #Same for macOS. elif "macOS" in _os: BOOTLOADER_INFO[_os]["DefaultBootDevice"] = OS_INFO[_os]["Partition"] BOOTLOADER_INFO[_os]["DefaultOS"] = _os continue #Match the bootloader-specific default OS to WxFixBoot's OSs by partition. logger.info("get_bootloaders(): Attempting to match "+_os+"'s default OS to any OS that " + "WxFixBoot detected...") BOOTLOADER_INFO[_os]["DefaultBootDevice"] = "Unknown" if "MenuEntries" in BOOTLOADER_INFO[_os].keys(): CoreStartupTools.get_defaultoss_partition(_os) else: #Bootloader's configuration is missing. logger.error("get_bootloaders(): "+_os+"'s bootloader configuration is missing. A " + "reinstall will be required for that bootloader...") #We have the partition, so now find the OS that resides on that partition. CoreStartupTools.match_partition_to_os(_os) #Log if we couldn't match them. if BOOTLOADER_INFO[_os]["DefaultOS"] == "Unknown": logger.warning("get_bootloaders(): Couldn't match! We will instead use the first OS " + "in the list as the default OS, which is " + SYSTEM_INFO["ModifyableOSs"][0]+"...") BOOTLOADER_INFO[_os]["DefaultOS"] = SYSTEM_INFO["ModifyableOSs"][0] #Set this. BOOTLOADER_INFO[_os]["Settings"]["DefaultOS"] = BOOTLOADER_INFO[_os]["DefaultOS"]
def find_checkable_file_systems(): """ Find all checkable filesystems, and then return them to EssentialBackendTools().filesystem_check() """ logger.info("find_checkable_file_systems(): Finding and returning all filesystems/partitions " + "that can be checked...") #Do setup. do_not_check_list = [] filesystems_to_check = {} root_fs = CoreTools.get_partition_mounted_at("/") #Get a list of missing fsck modules (if any) based on the existing filesystems. missing_fsck_modules = find_missing_fsck_modules() keys = list(DISK_INFO.keys()) keys.sort() #Determine checkable partitions. for disk in keys: #Ignore all devices. if DISK_INFO[disk]["Type"] == "Device": continue #Check if the required fsck module is present, and that the partition isn't root_fs if "fsck."+DISK_INFO[disk]["FileSystem"] in missing_fsck_modules: mount_point = "None" check_this_fs = False remount_fs_after = False reason = "filesystem checker was not found." elif SYSTEM_INFO["IsLiveDisk"] is False and disk == root_fs: #If we're not running on a live disk, skip the filesystem if it's the same as root_fs #(in which case checking it may corrupt data). mount_point = "/" check_this_fs = False remount_fs_after = False reason = "disk is busy." #Extra check for LVM disks using aliases. elif DISK_INFO[disk]["Product"] == "LVM Partition" and root_fs in DISK_INFO[disk]["Aliases"]: mount_point = "/" check_this_fs = False remount_fs_after = False reason = "disk is busy." else: #If filesystem is unknown, or not applicable (extended partitions), don't check it. if DISK_INFO[disk]["FileSystem"] in ("Unknown", "N/A"): mount_point = "None" check_this_fs = False remount_fs_after = False reason = "filesystem was not recognised." else: #Check if the partition is mounted. if CoreTools.is_mounted(disk) is False: mount_point = "None" check_this_fs = True remount_fs_after = False #Extra check for LVM disks using aliases. elif DISK_INFO[disk]["Product"] == "LVM Partition" and \ CoreTools.any_mounted(DISK_INFO[disk]["Aliases"]) is False: mount_point = "None" check_this_fs = True remount_fs_after = False else: #Unmount the FS temporarily, to avoid data corruption. mount_point = CoreTools.get_mount_point_of(disk) if CoreTools.unmount(disk) != 0: logger.warning("find_checkable_file_systems(): Failed to unmount "+disk +", which is necessary for safe disk checking! Ignoring it.") check_this_fs = False remount_fs_after = False reason = "disk is busy." else: check_this_fs = True remount_fs_after = True if check_this_fs: #Add it to the dictionary for checking. filesystems_to_check[disk] = {} filesystems_to_check[disk]["Remount"] = remount_fs_after filesystems_to_check[disk]["MountPoint"] = mount_point else: #Add it to the non-checkable list do_not_check_list.append(disk+", because the "+reason) #Report uncheckable partitions. if do_not_check_list != []: #Some filesystems will not be checked. Tell the user. DialogTools.show_msg_dlg(kind="info", message="The following filesystems will not be checked:\n\n" + '\n'.join(do_not_check_list)+"\n\nThe most likely reason for " + "this is that some of the filesystems are in use, or that the " + "required filesystem checkers weren't found. WxFixBoot will " + "now continue to check the remaining filesystems.") logger.info("find_checkable_file_systems(): Done! Filesystems that won't be checked: " + '\n'.join(do_not_check_list)+"...") return filesystems_to_check
def handle_filesystem_check_return_values(exec_cmds, retval, partition, manage_bootloader_function): """Handle Filesystem Checker return codes.""" exec_list = exec_cmds.split() #Return values of 1,2 or 3 happen if errors were corrected. if retval in (1, 2, 3) and exec_list[0] != "badblocks": if exec_list[0] == "xfs_repair": #Fs Corruption Detected. logger.warning( "handle_filesystem_check_return_values(): xfs_repair detected " + "filesystem corruption on FileSystem: " + partition + ". It's probably (and hopefully) been fixed, but we're logging " + "this anyway.") DialogTools.show_msg_dlg( kind="warning", message="Corruption was found on the filesystem: " + partition + "! Fortunately, it looks like the checker utility has " + "fixed the corruption. Click okay to continue.") elif exec_list[0] in ('fsck.jfs', 'fsck.minix', 'fsck.reiserfs', 'fsck.vfat', 'fsck.ext2', 'fsck.ex3', 'fsck.ext4', 'fsck.ext4dev'): #Fixed Errors. logger.info("handle_filesystem_check_return_values(): " + exec_list[0] + " successfully fixed errors on the partition: " + partition + ". Continuing...") DialogTools.show_msg_dlg( kind="warning", message="The filesystem checker found and " + "successfully fixed errors on partition: " + partition + ". Click okay to continue.") else: #Something bad happened! #If we're doing bootloader operations, prompt the user to disable them. doing_bootloader_operations = False for function in OPERATIONS: if isinstance(function, tuple): if manage_bootloader_function == function[0]: doing_bootloader_operations = True break logger.error( "handle_filesystem_check_return_values(): " + exec_list[0] + " Errored with exit value " + str(retval) + "! This could indicate filesystem corruption or bad sectors!") if doing_bootloader_operations: logger.error( "handle_filesystem_check_return_values(): Asking the user whether to " + "skip bootloader operations...") result = DialogTools.show_yes_no_dlg( message="Error! The filesystem checker gave " + "exit value: " + str(retval) + "! This could " + "indicate filesystem corruption, a problem " + "with the filesystem checker, or bad sectors " + "on partition: " + partition + ". If you perform " + "bootloader operations on this partition, your " + "system could become unstable or unbootable. " + "Do you want to disable bootloader operations, " + "as is strongly recommended?", title="WxFixBoot - Disable Bootloader " + "Operations?", buttons=("Disable Bootloader Operations", "Ignore and Continue Anyway")) if result: #A good choice. WxFixBoot will now disable any bootloader operations. logger.warning( "handle_filesystem_check_return_values(): User disabled bootloader " + "operations as recommended, due to bad sectors/HDD problems/FS " + "Checker problems...") SYSTEM_INFO["DisableBootloaderOperations"] = True SYSTEM_INFO["DisableBootloaderOperationsBecause"] \ .append("Filesystem corruption was detected on "+partition) else: #Seriously? Well, okay, we'll do it anyway... This is probably a very bad idea... logger.warning( "handle_filesystem_check_return_values(): User ignored the warning " + "and went ahead with bootloader modifications (if any) anyway, " + "even with possible HDD problems/Bad sectors! This is a REALLY " + "bad idea, but we'll do it anyway, as requested...")
def filesystem_check(_type, manage_bootloader_function): """Quickly check all filesystems.""" logger.debug("filesystem_check(): Starting...") #Update Current Operation Text. wx.CallAfter(wx.GetApp().TopWindow.update_current_operation_text, message="Preparing for Filesystem Check...") wx.CallAfter(wx.GetApp().TopWindow.update_current_progress, 10) wx.CallAfter(wx.GetApp().TopWindow.update_output_box, "\n###Preparing to do the Filesystem Check...###\n") #Determine which Disks are to be checked. filesystems_to_check = HelperBackendTools.find_checkable_file_systems() wx.CallAfter(wx.GetApp().TopWindow.update_current_progress, 30) #Find the length of the list (this is needed to update the progressbars). filesystems_to_check_length = len(filesystems_to_check) checked = 0 DialogTools.show_msg_dlg( kind="info", message="WxFixBoot will now perform the disk checks. " + "You may wish to open the terminal output box to view the " + "progress information.") #Run the check on the checkable Disks for disk in filesystems_to_check: #Gather info. logger.info("filesystem_check():: Checking " + disk + "...") wx.CallAfter(wx.GetApp().TopWindow.update_output_box, "\n###Checking Disk: " + disk + "###\n") wx.CallAfter(wx.GetApp().TopWindow.update_current_operation_text, message="Checking Disk: " + disk) wx.CallAfter( wx.GetApp().TopWindow.update_current_progress, 30 + ((50 // filesystems_to_check_length) * (checked + 1))) run_badblocks = False #Create a command list that will work based on the fstype of this Disk and the type of #check we're performing. If there aren't any use cases for the fstype, display a message #to the user and skip it. if _type == "Quick": if DISK_INFO[disk]["FileSystem"] == "jfs": exec_cmds = "fsck.jfs -vf " + disk elif DISK_INFO[disk]["FileSystem"] == "minix": exec_cmds = "fsck.minix -avf " + disk elif DISK_INFO[disk]["FileSystem"] == "reiserfs": exec_cmds = "fsck.reiserfs -apf " + disk elif DISK_INFO[disk]["FileSystem"] == "xfs": exec_cmds = "xfs_repair -Pvd " + disk elif DISK_INFO[disk]["FileSystem"] in ("hfs", "hfsplus"): exec_cmds = "fsck.hfsplus -fy " + disk elif DISK_INFO[disk]["FileSystem"] == "vfat": exec_cmds = "fsck.vfat -yv " + disk elif DISK_INFO[disk]["FileSystem"] in ('ext2', 'ext3', 'ext4', 'ext4dev'): exec_cmds = "fsck." + DISK_INFO[disk][ "FileSystem"] + " -yvf " + disk else: exec_cmds = "" logger.warning( "filesystem_check(): Skipping Disk: " + disk + ", as WxFixBoot doesn't support checking it yet...") DialogTools.show_msg_dlg( kind="error", message="The filesystem on Disk: " + disk + " could not be checked, as WxFixBoot doesn't support " + "checking it yet. " + disk + " will now be skipped.") else: #For disks that doesn't do bad sector checks with the normal FS checker, #run badblocks manually on them. if DISK_INFO[disk]["FileSystem"] == "jfs": exec_cmds = "fsck.jfs -vf " + disk run_badblocks = True elif DISK_INFO[disk]["FileSystem"] == "minix": exec_cmds = "fsck.minix -avf " + disk run_badblocks = True elif DISK_INFO[disk]["FileSystem"] == "reiserfs": exec_cmds = "fsck.reiserfs -apf " + disk run_badblocks = True elif DISK_INFO[disk]["FileSystem"] == "xfs": exec_cmds = "xfs_repair -Pvd " + disk run_badblocks = True elif DISK_INFO[disk]["FileSystem"] in ("hfs", "hfsplus"): exec_cmds = "fsck.hfsplus -fy " + disk run_badblocks = True elif DISK_INFO[disk]["FileSystem"] == "vfat": exec_cmds = "fsck.vfat -yvt " + disk elif DISK_INFO[disk]["FileSystem"] in ('ext2', 'ext3', 'ext4', 'ext4dev'): exec_cmds = "fsck." + DISK_INFO[disk][ "FileSystem"] + " -yvcf " + disk else: exec_cmds = "" DialogTools.show_msg_dlg( kind="info", message="The filesystem on Disk: " + disk + " could not be checked, as WxFixBoot doesn't support " + "checking it yet. " + disk + " will now be skipped.") #Run the command, and remount the Disk if needed. if exec_cmds != "": retval = CoreTools.start_process(exec_cmds, privileged=True) #Check the return values, and run the handler if needed. if retval == 0: #Success. logger.info("filesystem_check(): Checked Disk: " + disk + ". No Errors Found!") else: handle_filesystem_check_return_values( exec_cmds=exec_cmds, retval=retval, partition=disk, manage_bootloader_function=manage_bootloader_function) #Run bad blocks if requested. if run_badblocks: retval = CoreTools.start_process("badblocks -sv " + disk, privileged=True) #Check the return values, and run the handler if needed. if retval == 0: #Success. logger.info("filesystem_check(): Checked Disk: " + disk + " for bad sectors. " + "No Errors Found!") else: handle_filesystem_check_return_values( exec_cmds="badblocks -sv " + disk, retval=retval, partition=disk, manage_bootloader_function=manage_bootloader_function) if filesystems_to_check[disk]["Remount"]: logger.debug("filesystem_check(): Remounting Disk: " + disk + " Read-Write...") retval = CoreTools.mount_partition( partition=disk, mount_point=filesystems_to_check[disk]["MountPoint"]) if retval != 0: logger.warning( "filesystem_check(): Failed to remount " + disk + " after check. We probably need to reboot first. Never mind..." ) checked += 1 #Update Current Operation Text. wx.CallAfter(wx.GetApp().TopWindow.update_current_operation_text, message="Finished Filesystem Check!") wx.CallAfter(wx.GetApp().TopWindow.update_current_progress, 100) wx.CallAfter(wx.GetApp().TopWindow.update_output_box, "\n###Finished Filesystem Check!###\n")
def set_new_bootloader_config(_os): """Manage setting new bootloader config.""" logger.info("set_new_bootloader_config(): Setting " + BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] + "'s config for " + _os + "...") wx.CallAfter(wx.GetApp().TopWindow.update_current_operation_text, message="Setting " + BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] + " config for " + _os + "...") wx.CallAfter(wx.GetApp().TopWindow.update_current_progress, 79) wx.CallAfter( wx.GetApp().TopWindow.update_output_box, "\n###Setting " + BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] + "'s config for " + _os + "...###\n") #If this is the current OS, let the config functions know that we aren't using chroot. if OS_INFO[_os]["IsCurrentOS"]: logger.debug( "set_new_bootloader_config(): We're modifying the current OS...") #If so, make sure this will work for this OS too, and avoid setting mountpoint, so the #config instructions below look in the right place for the config files. use_chroot, unmount_after, mount_point = (False, False, "") else: logger.debug( "set_new_bootloader_config(): We're modifying another OS...") use_chroot = True mount_point = "/mnt/wxfixboot/mountpoints" + OS_INFO[_os]["Partition"] #Check if the partition is mounted. unmount_after = not CoreTools.is_mounted(OS_INFO[_os]["Partition"], mount_point) if unmount_after: #Mount the partition. if CoreTools.mount_partition(partition=OS_INFO[_os]["Partition"], mount_point=mount_point) != 0: #Ignore this partition. logger.warning( "set_new_bootloader_config(): Failed to mount " + OS_INFO[_os]["Partition"] + "! Giving up...") return False #Set up chroot. if CoreTools.setup_chroot(mount_point=mount_point) != 0: logger.error( "set_new_bootloader_config(): Failed to set up chroot at " + mount_point + "! Giving up...") DialogTools.show_msg_dlg( kind="error", message="WxFixBoot failed to set up a chroot for " + _os + "! Giving up. You will be prompted to try again if " + "you wish.") return False wx.CallAfter(wx.GetApp().TopWindow.update_current_progress, 81) #Mount a /boot partition if it exists. if OS_INFO[_os]["BootPartition"] != "Unknown": if CoreTools.mount_partition(OS_INFO[_os]["BootPartition"], mount_point + "/boot") != 0: logger.error( "set_new_bootloader_config(): Failed to mount " + _os + "'s /boot partition! Skipping bootloader config setting for this OS." ) if not OS_INFO[_os]["IsCurrentOS"]: CoreTools.teardown_chroot(mount_point) CoreTools.unmount(mount_point) return False #If there's a seperate EFI partition for this OS, make sure it's mounted. if OS_INFO[_os]["EFIPartition"] != "Unknown": if CoreTools.mount_partition( partition=OS_INFO[_os]["EFIPartition"], mount_point=mount_point + "/boot/efi") != 0: logger.error("remove_old_bootloader(): Failed to mount " + OS_INFO[_os]["EFIPartition"] + "! Warn the user and skip this OS.") DialogTools.show_msg_dlg( kind="error", message="WxFixBoot failed to mount the partition containing " + _os + "'s EFI partition! Giving up. You will be prompted to " + "try again if you wish.") return False #On GRUB2, get the new menuentries so we can set the default OS. logger.info( "set_new_bootloader_config(): Reading GRUB2's menu entries to set default OS..." ) if BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] in ("GRUB2", "GRUB-UEFI"): #Update GRUB. logger.info( "set_new_bootloader_config(): Updating GRUB2 Configuration...") BootloaderConfigSettingTools.update_grub2( _os=_os, package_manager=OS_INFO[_os]["PackageManager"], use_chroot=use_chroot, mount_point=mount_point) BOOTLOADER_INFO[_os]["NewMenuEntries"] = \ BootloaderConfigObtainingTools.parse_grub2_menu_data(menu_data="", mount_point=mount_point)[1] #Look for the configuration file, based on which SetConfig() function we're about to run. if BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] in ("GRUB2", "GRUB-UEFI"): #Check mount_point/etc/default/grub exists. if os.path.isfile(mount_point + "/etc/default/grub"): #It does, we'll run the function to set the config now. logger.info( "set_new_bootloader_config(): Setting GRUB2 Configuration...") BootloaderConfigSettingTools.set_grub2_config( _os=_os, filetoopen=mount_point + "/etc/default/grub", bootloader_timeout=BOOTLOADER_INFO[_os]["Settings"] ["NewTimeout"], kernel_options=BOOTLOADER_INFO[_os]["Settings"] ["NewKernelOptions"], package_manager=OS_INFO[_os]["PackageManager"]) if BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] == "GRUB-UEFI": #Mount the UEFI partition at mount_point/boot/efi. if CoreTools.mount_partition( partition=OS_INFO[_os]["EFIPartition"], mount_point=mount_point + "/boot/efi") != 0: logger.error( "set_new_bootloader_config(): Couldn't mount EFI partition " + OS_INFO[_os]["EFIPartition"] + " to install bootloader! Giving up " + "and warning user...") DialogTools.show_msg_dlg( kind="error", message="WxFixBoot failed to mount " + _os + "'s EFI partition! You will now be promtped to give " + "up or try again.") return False #Now Install GRUB-UEFI to the UEFI Partition. logger.info( "set_new_bootloader_config(): Installing GRUB-UEFI to " + OS_INFO[_os]["EFIPartition"] + "...") BootloaderConfigSettingTools.install_grub2_to_efi_partition( package_manager=OS_INFO[_os]["PackageManager"], mount_point=mount_point, use_chroot=use_chroot, uefi_system_partition_mount_point="/boot/efi", arch=OS_INFO[_os]["Arch"]) else: #Now Install GRUB2 to the MBR. logger.info("set_new_bootloader_config(): Installing GRUB2 to " + DISK_INFO[OS_INFO[_os]["Partition"]]["HostDevice"] + "...") BootloaderConfigSettingTools.install_grub2_to_mbr( package_manager=OS_INFO[_os]["PackageManager"], use_chroot=use_chroot, mount_point=mount_point, device=DISK_INFO[OS_INFO[_os]["Partition"]]["HostDevice"]) #Update GRUB. logger.info( "set_new_bootloader_config(): Updating GRUB2 Configuration...") BootloaderConfigSettingTools.update_grub2( _os=_os, package_manager=OS_INFO[_os]["PackageManager"], use_chroot=use_chroot, mount_point=mount_point) if BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] == "GRUB-UEFI": #Make an entry in fstab for the UEFI Partition, if needed. HelperBackendTools.write_fstab_entry_for_uefi_partition( _os=_os, mount_point=mount_point) #Copy and backup EFI files where needed. HelperBackendTools.backup_uefi_files(mount_point=mount_point) HelperBackendTools.manage_uefi_files(_os=_os, mount_point=mount_point) if BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] == "GRUB-UEFI" \ and OS_INFO[_os]["PackageManager"] == "dnf": #If we're switching to GRUB-UEFI from BIOS it can mess up GRUB2 and change the boot #commands to linux and initrd instead of linuxefi and initrdefi, preventing boot. #Fix this. The next time GRUB is updated from within the OS it will fix itself. logger.info( "set_new_bootloader_config(): Fixing Fedora's GRUB2-UEFI config (when " + "booted with BIOS, it can go wrong)...") logger.info( "set_new_bootloader_config(): Finding and opening GRUB config file..." ) #Find grub.cfg. (Ubuntu). if os.path.isdir(mount_point + "/boot/grub"): grub_dir = mount_point + "/boot/grub" #(Fedora, EFI) elif os.path.isdir(mount_point + "/boot/efi/EFI/fedora"): grub_dir = mount_point + "/boot/efi/EFI/fedora" #Correct the commands if needed. config = CoreTools.read_privileged_file(grub_dir + "/grub.cfg") new_config = [] for line in config: if "linux16" in line and "/vmlinu" in line: new_config.append( line.replace("linux16", "linuxefi") + "\n") elif "linux" in line and "linuxefi" not in line and "/vmlinu" in line: new_config.append(line.replace("linux", "linuxefi") + "\n") elif "initrd16" in line and ("/initrd" in line or "/initramfs" in line): new_config.append( line.replace("initrd16", "initrdefi") + "\n") elif "initrd" in line and "initrdefi" not in line \ and ("/initrd" in line or "/initramfs" in line): new_config.append( line.replace("initrd", "initrdefi") + "\n") else: new_config.append(line + "\n") #Write the fixed config. CoreTools.write_privileged_file(grub_dir + "/grub.cfg", ''.join(new_config)) #unmount the EFI partition. if CoreTools.unmount(OS_INFO[_os]["EFIPartition"]) != 0: logger.error( "set_new_bootloader_config(): Couldn't unmount EFI partition! " + "This probably won't matter, so we'll continue anyway...") logger.info("set_new_bootloader_config(): Done!") elif BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] == "GRUB2" \ and OS_INFO[_os]["PackageManager"] == "dnf": #If we're switching to GRUB2 from UEFI it can mess up GRUB2 and change the boot #commands to linuxefi and initrdefi instead of linux and initrd, preventing boot. #Fix this. The next time GRUB is updated from within the OS it will fix itself. logger.info( "set_new_bootloader_config(): Fixing Fedora's GRUB2-BIOS config (when " + "booted with EFI, it can go wrong)...") logger.info( "set_new_bootloader_config(): Finding and opening GRUB config file..." ) #Find grub.cfg. (Ubuntu). if os.path.isdir(mount_point + "/boot/grub"): grub_dir = mount_point + "/boot/grub" #(Fedora, BIOS) elif os.path.isdir(mount_point + "/boot/grub2"): grub_dir = mount_point + "/boot/grub2" #Correct the commands if needed. config = CoreTools.read_privileged_file(grub_dir + "/grub.cfg") new_config = [] for line in config: new_config.append(line.replace("linuxefi", "linux")\ .replace("initrdefi", "initrd")+"\n") #Write the fixed config. CoreTools.write_privileged_file(grub_dir + "/grub.cfg", ''.join(new_config)) logger.info("set_new_bootloader_config(): Done!") #If there's a seperate EFI partition for this OS, make sure it's unmounted before #removing the chroot. if OS_INFO[_os]["EFIPartition"] != "Unknown": if CoreTools.unmount(mount_point + "/boot/efi") != 0: logger.error("set_new_bootloader_config(): Failed to unmount " + mount_point + "/boot/efi! This probably doesn't matter...") #unmount a /boot partition if it exists. if OS_INFO[_os]["BootPartition"] != "Unknown": if CoreTools.unmount(mount_point + "/boot") != 0: logger.error("set_new_bootloader_config(): Failed to unmount " + _os + "'s /boot partition! Continuing anyway...") #Tear down chroot if needed. if use_chroot: if CoreTools.teardown_chroot(mount_point=mount_point) != 0: logger.error( "set_new_bootloader_config(): Failed to remove chroot at " + mount_point + "! Attempting to continue anyway...") #unmount the partition if needed. if unmount_after: if CoreTools.unmount(mount_point) != 0: logger.error("set_new_bootloader_config(): Failed to unmount " + mount_point + "! Continuing anyway...") #Attempt to clear any stuck logical volumes that may have been created by os-prober. CoreTools.start_process("dmsetup remove_all -y", privileged=True) #Make sure any LVM volume groups are active. for disk in DISK_INFO: if "VGName" in DISK_INFO[disk]: CoreTools.start_process("vgchange -a y " + DISK_INFO[disk]["VGName"], privileged=True) logger.debug("set_new_bootloader_config(): Finished setting " + BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] + "'s config for " + _os + "...") wx.CallAfter( wx.GetApp().TopWindow.update_output_box, "\n###Finished setting " + BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] + "'s config for " + _os + "...###\n") wx.CallAfter(wx.GetApp().TopWindow.update_current_operation_text, message="Finished setting " + BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] + "'s config for " + _os + "!") wx.CallAfter(wx.GetApp().TopWindow.update_current_progress, 100) return True
def install_new_bootloader(_os): """Install a new bootloader.""" logger.info("install_new_bootloader(): Preparing to install " + BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] + " in " + _os + "...") wx.CallAfter(wx.GetApp().TopWindow.update_current_progress, 52) wx.CallAfter( wx.GetApp().TopWindow.update_output_box, "\n###Preparing to install " + BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] + " in " + _os + "...###\n") wx.CallAfter(wx.GetApp().TopWindow.update_current_operation_text, message="Preparing to install " + BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] + " in " + _os + "...") #If this is the current OS, let the installer functions know that we aren't using chroot. if OS_INFO[_os]["IsCurrentOS"]: logger.debug( "install_new_bootloader(): Modifying current OS so not using chroot..." ) use_chroot, unmount_after, mount_point = (False, False, "") #Otherwise, setup the chroot and everything else first, and tell them we are using chroot, #and pass the mountpoint to them. else: logger.debug( "install_new_bootloader(): Using chroot to modify another OS...") use_chroot = True mount_point = "/mnt/wxfixboot/mountpoints" + OS_INFO[_os]["Partition"] #Check if the partition is mounted. unmount_after = not CoreTools.is_mounted(OS_INFO[_os]["Partition"], mount_point) if unmount_after: if CoreTools.mount_partition(partition=OS_INFO[_os]["Partition"], mount_point=mount_point) != 0: logger.error("install_new_bootloader(): Failed to mount " + OS_INFO[_os]["Partition"] + "! Warn the user and skip this OS.") DialogTools.show_msg_dlg( kind="error", message="WxFixBoot failed to mount the partition " + "containing " + _os + "! Bootloader installation cannot " + "continue! This may leave your system, or this OS, in " + "an unbootable state. Please close any open programs, " + "then try again when prompted.") return False #Set up chroot. if CoreTools.setup_chroot(mount_point=mount_point) != 0: logger.error( "install_new_bootloader(): Failed to set up chroot at " + mount_point + "! Warning user and giving up...") DialogTools.show_msg_dlg( kind="error", message="WxFixBoot failed to set up a chroot for " + _os + "! Giving up. You will be prompted to try again if " + "you wish.") return False #If there's a seperate /boot partition for this OS, make sure it's mounted. if OS_INFO[_os]["BootPartition"] != "Unknown": if CoreTools.mount_partition(partition=OS_INFO[_os]["BootPartition"], mount_point=mount_point + "/boot") != 0: logger.error("remove_old_bootloader(): Failed to mount " + OS_INFO[_os]["BootPartition"] + "! Warn the user and skip this OS.") DialogTools.show_msg_dlg( kind="error", message="WxFixBoot failed to mount the partition " + "containing " + _os + "'s /boot partition! Giving up. " + "You will be prompted to try again if you wish.") return False #Update the package lists. if OS_INFO[_os]["PackageManager"] == "apt-get": cmd = "sh -c 'DEBIAN_FRONTEND=gnome DISPLAY=:0 apt-get update'" elif OS_INFO[_os]["PackageManager"] == "dnf": cmd = "dnf check-update" if use_chroot: cmd = "chroot " + mount_point + " " + cmd if CoreTools.start_process(cmd, privileged=True) not in (0, 100): logger.error( "install_new_bootloader(): Failed to Update the Package Information! " + "Continuing anyway...") DialogTools.show_msg_dlg( kind="error", message="WxfixBoot failed to update " + _os + "'s package information! Giving up. You will be prompted " + "to try again if you wish.") return False wx.CallAfter(wx.GetApp().TopWindow.update_current_operation_text, message="Installing " + BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] + " in " + _os + "...") wx.CallAfter(wx.GetApp().TopWindow.update_current_progress, 55) wx.CallAfter( wx.GetApp().TopWindow.update_output_box, "\n###Installing " + BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] + " in " + _os + "...###\n") #Make sure all GNOME APT frontend dependency is installed. if OS_INFO[_os]["PackageManager"] == "apt-get": #Ubuntu 16.04. cmd = "sh -c 'DEBIAN_FRONTEND=noninteractive apt-get install -y libgnome2-perl'" if use_chroot: cmd = "chroot " + mount_point + " " + cmd retval = CoreTools.start_process(cmd, privileged=True) #All newer versions. cmd = "sh -c 'DEBIAN_FRONTEND=noninteractive apt-get install -y libgtk3-perl'" if use_chroot: cmd = "chroot " + mount_point + " " + cmd retval = CoreTools.start_process(cmd, privileged=True) #Install the bootloader. if BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] == "GRUB2": logger.info("install_new_bootloader(): Installing GRUB2...") if OS_INFO[_os]["PackageManager"] == "apt-get": cmd = "sh -c 'DEBIAN_FRONTEND=gnome DISPLAY=:0 apt-get install -y grub-pc os-prober'" elif OS_INFO[_os]["PackageManager"] == "dnf": cmd = "dnf -y install grub2" elif BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] == "GRUB-UEFI": logger.info("install_new_bootloader(): Installing GRUB-UEFI...") #Mount the UEFI partition at mount_point/boot/efi. if CoreTools.mount_partition( partition=OS_INFO[_os]["EFIPartition"], mount_point=mount_point + "/boot/efi") != 0: logger.error("install_new_bootloader(): Failed to mount " + OS_INFO[_os]["EFIPartition"] + " to " + mount_point + "/boot/efi! Aborting bootloader installation and " + "warning user...") DialogTools.show_msg_dlg( kind="error", message="WxfixBoot failed to mount the partition containing " + _os + "'s EFI partition! Giving up. You will be prompted to " + "try again if you wish.") return False if OS_INFO[_os]["PackageManager"] == "apt-get": cmd = "sh -c 'DEBIAN_FRONTEND=gnome DISPLAY=:0 apt-get install -y grub-efi os-prober'" elif OS_INFO[_os]["PackageManager"] == "dnf": cmd = "dnf -y install grub2-efi-ia32 grub2-efi-x64 shim-x64 " if use_chroot: cmd = "chroot " + mount_point + " " + cmd retval = CoreTools.start_process(cmd, privileged=True) if retval != 0: logger.error( "install_new_bootloader(): Failed to install new bootloader. Warn user..." ) DialogTools.show_msg_dlg(kind="error", message="WxfixBoot failed to install " + _os + "'s new bootloader! Continuing anyway...") #If there's a seperate EFI partition for this OS, make sure it's unmounted before removing #the chroot. if OS_INFO[_os]["EFIPartition"] != "Unknown": if CoreTools.unmount(mount_point + "/boot/efi") != 0: logger.error("install_new_bootloader(): Failed to unmount " + mount_point + "/boot/efi! This probably doesn't matter...") #If there's a seperate /boot partition for this OS, make sure it's unmounted before #removing the chroot. if OS_INFO[_os]["BootPartition"] != "Unknown": if CoreTools.unmount(mount_point + "/boot") != 0: logger.error("install_new_bootloader(): Failed to unmount " + mount_point + "/boot! This probably doesn't matter...") if use_chroot: logger.debug("install_new_bootloader(): Removing chroot...") #Tear down chroot. if CoreTools.teardown_chroot(mount_point=mount_point) != 0: logger.error( "install_new_bootloader(): Failed to remove chroot at " + mount_point + "! Attempting to continue anyway...") if unmount_after: if CoreTools.unmount(mount_point) != 0: logger.error("install_new_bootloader(): Failed to unmount " + mount_point + "! Continuing anyway...") if retval != 0: #Something went wrong! Log it and notify the user. logger.error( "install_new_bootloader(): Failed to install " + BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] + " in " + _os + "! This may mean the system (or this OS) is now unbootable! " + "Warning the user and asking to try again.") DialogTools.show_msg_dlg( kind="error", message="WxFixBoot failed to install " + BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] + " in " + _os + "! This may leave this OS, or your system, in an unbootable " + "state. You will now be prompted to try again.") return False wx.CallAfter( wx.GetApp().TopWindow.update_output_box, "\n###Finished installing " + BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] + " in " + _os + "...###\n") #Attempt to clear any stuck logical volumes that may have been created by os-prober. CoreTools.start_process("dmsetup remove_all -y", privileged=True) #Make sure any LVM volume groups are active. for disk in DISK_INFO: if "VGName" in DISK_INFO[disk]: CoreTools.start_process("vgchange -a y " + DISK_INFO[disk]["VGName"], privileged=True) #Log and notify the user that we're finished installing the bootloader. logger.info("install_new_bootloader(): Finished installing " + BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] + "...") wx.CallAfter(wx.GetApp().TopWindow.update_current_operation_text, message="Finish installing " + BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] + " in " + _os + "...") wx.CallAfter(wx.GetApp().TopWindow.update_current_progress, 75) return True
def remove_old_bootloader(_os): """Remove the currently installed bootloader.""" logger.info("remove_old_bootloader(): Removing " + BOOTLOADER_INFO[_os]["Bootloader"] + " from " + _os + "...") wx.CallAfter(wx.GetApp().TopWindow.update_current_progress, 27) wx.CallAfter(wx.GetApp().TopWindow.update_current_operation_text, message="Removing " + BOOTLOADER_INFO[_os]["Bootloader"] + " from " + _os + "......") wx.CallAfter( wx.GetApp().TopWindow.update_output_box, "\n###Removing " + BOOTLOADER_INFO[_os]["Bootloader"] + " from " + _os + "...###\n") #If this is the current OS, let the remover function know that we aren't using chroot. if OS_INFO[_os]["IsCurrentOS"]: logger.debug( "remove_old_bootloader(): Modifying current OS so not using chroot..." ) use_chroot, unmount_after, mount_point = (False, False, "") else: logger.debug( "remove_old_bootloader(): Using chroot to modify another OS...") use_chroot = True mount_point = "/mnt/wxfixboot/mountpoints" + OS_INFO[_os]["Partition"] #Check if the partition is mounted. unmount_after = not CoreTools.is_mounted(OS_INFO[_os]["Partition"], mount_point) if unmount_after: #Mount the partition using the global mount function. if CoreTools.mount_partition(partition=OS_INFO[_os]["Partition"], mount_point=mount_point) != 0: logger.error("remove_old_bootloader(): Failed to mount " + OS_INFO[_os]["Partition"] + "! Warning the user and giving up...") DialogTools.show_msg_dlg( kind="error", message="WxFixBoot failed to mount the partition " + "containing " + _os + "! Giving up. You will be prompted " + "to try again if you wish.") return False #Set up chroot. if CoreTools.setup_chroot(mount_point) != 0: logger.error( "remove_old_bootloader(): Failed to set up chroot at " + mount_point + "! Giving up...") DialogTools.show_msg_dlg( kind="error", message="WxFixBoot failed to set up a chroot for " + _os + "! Giving up. You will be prompted to try again if " + "you wish.") return False #Mount a /boot partition if it exists. if OS_INFO[_os]["BootPartition"] != "Unknown": if CoreTools.mount_partition(OS_INFO[_os]["BootPartition"], mount_point + "/boot") != 0: logger.error("remove_old_bootloader(): Failed to mount " + _os + "'s /boot partition! " + "Skipping bootloader removal for this OS.") DialogTools.show_msg_dlg( kind="error", message="WxFixBoot failed to mount the partition containing " + _os + "'s /boot partition! Giving up. You will be prompted " + "to try again if you wish.") if not OS_INFO[_os]["IsCurrentOS"]: CoreTools.teardown_chroot(mount_point) CoreTools.unmount(mount_point) return False #Mount the UEFI partition at mount_point/boot/efi, if it exists. if OS_INFO[_os]["EFIPartition"] != "Unknown": if CoreTools.mount_partition( partition=OS_INFO[_os]["EFIPartition"], mount_point=mount_point + "/boot/efi") != 0: logger.error("remove_old_bootloader(): Failed to mount " + OS_INFO[_os]["EFIPartition"] + "! to " + mount_point + "/boot/efi! Aborting bootloader installation and " + "warning user...") DialogTools.show_msg_dlg( kind="error", message="WxfixBoot failed to mount the partition containing " + _os + "'s EFI partition! Giving up. You will be prompted to " + "try again if you wish.") return False #Wait until no other application is using APT/DNF. #Let user know what's happening. wx.CallAfter(wx.GetApp().TopWindow.update_current_progress, 27) wx.CallAfter( wx.GetApp().TopWindow.update_current_operation_text, message="Waiting until " + _os + "'s package manager is free.\nClose any open applications if this " + "message persists...") wx.CallAfter( wx.GetApp().TopWindow.update_output_box, "\n###Waiting until " + _os + "'s package manager is free...###\n") logger.debug("remove_old_bootloader(): Waiting until " + _os + "'s package manager is free...") HelperBackendTools.wait_until_packagemanager_free(mount_point=mount_point, package_manager=\ OS_INFO[_os]["PackageManager"]) wx.CallAfter(wx.GetApp().TopWindow.update_current_progress, 27) wx.CallAfter(wx.GetApp().TopWindow.update_current_operation_text, message="Removing " + BOOTLOADER_INFO[_os]["Bootloader"] + " from " + _os + "...") wx.CallAfter( wx.GetApp().TopWindow.update_output_box, "\n###Removing " + BOOTLOADER_INFO[_os]["Bootloader"] + " from " + _os + "...###\n") #Make sure the GNOME APT frontend dependency is installed. if OS_INFO[_os]["PackageManager"] == "apt-get": #Ubuntu 16.04. cmd = "sh -c 'DEBIAN_FRONTEND=noninteractive apt-get install -y libgnome2-perl'" if use_chroot: cmd = "chroot " + mount_point + " " + cmd retval = CoreTools.start_process(cmd, privileged=True) #All newer versions. cmd = "sh -c 'DEBIAN_FRONTEND=noninteractive apt-get install -y libgtk3-perl'" if use_chroot: cmd = "chroot " + mount_point + " " + cmd retval = CoreTools.start_process(cmd, privileged=True) #Remove the bootloader. if BOOTLOADER_INFO[_os]["Bootloader"] == "GRUB2": logger.info("remove_old_bootloader(): Removing GRUB2...") if OS_INFO[_os]["PackageManager"] == "apt-get": cmd = "sh -c 'DEBIAN_FRONTEND=gnome DISPLAY=:0 apt-get purge -y " \ "--allow-remove-essential grub-pc grub-pc-bin grub-common'" elif OS_INFO[_os]["PackageManager"] == "dnf": cmd = "dnf -y remove grub2" elif BOOTLOADER_INFO[_os]["Bootloader"] == "GRUB-UEFI": logger.info("remove_old_bootloader(): Removing GRUB-UEFI...") if OS_INFO[_os]["PackageManager"] == "apt-get": cmd = "sh -c 'DEBIAN_FRONTEND=gnome DISPLAY=:0 apt-get purge -y " \ "--allow-remove-essential grub-efi grub-efi-amd64 grub-efi-amd64-bin" \ " grub-efi-ia32 grub-efi-ia32-bin grub-common grub2-common'" elif OS_INFO[_os]["PackageManager"] == "dnf": cmd = "dnf -y remove grub2-efi-x64 grub2-efi-ia32 shim-x64" else: #Bootloader is unknown. Just output a warning message. logger.warning( "remove_old_bootloader(): Cannot remove unknown bootloader! " + "Continuing anyway...") cmd = "echo 'WARNING: Bootloader is " \ "unknown, cannot remove. Continuing anyway...'" if use_chroot: cmd = "chroot " + mount_point + " " + cmd retval = CoreTools.start_process(cmd, privileged=True) if retval != 0: logger.error("remove_old_bootloader(): Failed to remove " + BOOTLOADER_INFO[_os]["Bootloader"] + " from " + _os + "! Warning user...") DialogTools.show_msg_dlg(kind="error", message="WxFixBoot failed to remove " + BOOTLOADER_INFO[_os]["Bootloader"] + " from " + _os + "!") return False #If there's a seperate EFI partition for this OS, make sure it's unmounted before removing #the chroot. if OS_INFO[_os]["EFIPartition"] != "Unknown": if CoreTools.unmount(mount_point + "/boot/efi") != 0: logger.error("remove_old_bootloader(): Failed to unmount " + mount_point + "/boot/efi! This probably doesn't matter...") #unmount a /boot partition if it exists. if OS_INFO[_os]["BootPartition"] != "Unknown": if CoreTools.unmount(mount_point + "/boot") != 0: logger.error("remove_old_bootloader(): Failed to unmount " + _os + "'s /boot partition! Continuing anyway...") #Tear down chroot if needed. if use_chroot: if CoreTools.teardown_chroot(mount_point=mount_point) != 0: logger.error( "remove_old_bootloader(): Failed to remove chroot at " + mount_point + "! Attempting to continue anyway...") #unmount partition if needed. if unmount_after: if CoreTools.unmount(mount_point) != 0: logger.error("remove_old_bootloader(): Couldn't unmount " + mount_point + "! Continuing anyway...") wx.CallAfter( wx.GetApp().TopWindow.update_output_box, "\n###Finished removing " + BOOTLOADER_INFO[_os]["Bootloader"] + " from " + _os + "...###\n") if retval != 0: #Something went wrong! Log it and notify the user. logger.error("remove_old_bootloader(): Failed to remove " + BOOTLOADER_INFO[_os]["Bootloader"] + " from " + _os + "! We'll continue anyway. Warn the user.") DialogTools.show_msg_dlg( kind="error", message="WxFixBoot failed to remove " + BOOTLOADER_INFO[_os]["Bootloader"] + " from " + _os + "! This probably doesn't matter; when we install the new " + "bootloader, it should take precedence over the old one " + "anyway. Make sure you check that " + _os + " boots correctly " + "after WxFixBoot finishes its operations. Reinstalling the " + "bootloader again afterwards is recommended.") #Attempt to clear any stuck logical volumes that may have been created by os-prober. CoreTools.start_process("dmsetup remove_all -y", privileged=True) #Make sure any LVM volume groups are active. for disk in DISK_INFO: if "VGName" in DISK_INFO[disk]: CoreTools.start_process("vgchange -a y " + DISK_INFO[disk]["VGName"], privileged=True) #Log and notify the user that we're finished removing bootloaders. logger.info("remove_old_bootloader(): Finished removing " + BOOTLOADER_INFO[_os]["Bootloader"] + "...") wx.CallAfter(wx.GetApp().TopWindow.update_current_operation_text, message="Finished removing " + BOOTLOADER_INFO[_os]["Bootloader"] + " from " + _os + "......") wx.CallAfter(wx.GetApp().TopWindow.update_current_progress, 50) return True
def set_grub2_config(_os, filetoopen, bootloader_timeout, kernel_options, package_manager): """Set GRUB2 config.""" logger.info("set_grub2_config(): Setting GRUB2 Config in "+filetoopen+"...") set_timeout, set_kernel_options, set_default = (False, False, False) #Match the bootloader-specific default OS to WxFixBoot's OSs by partition. logger.info("set_grub2_config(): Attempting to match the WxFixBoot's default OS for this " + "bootloader to any OS that GRUB2 detected...") #Find the ID for the menu entry that correspondes to that OS (Main Menu only to avoid #recovery options + misc). bootloader_specific_default_os = "Unknown" for entry in BOOTLOADER_INFO[_os]["NewMenuEntries"]["MainMenu"]["Order"]: if HelperBackendTools.partition_matches_os(BOOTLOADER_INFO[_os]["NewMenuEntries"]["MainMenu"][entry]["Partition"], BOOTLOADER_INFO[_os]["Settings"]["DefaultOS"]): bootloader_specific_default_os = \ BOOTLOADER_INFO[_os]["NewMenuEntries"]["MainMenu"][entry]["ID"] logger.info("set_grub2_config(): Found Default OS's GRUB2 ID...") break #Log if we couldn't match them. if bootloader_specific_default_os == "Unknown": logger.warning("set_grub2_config(): Couldn't match! We will instead pick the 1st menu " + "entry. Warning user...") DialogTools.show_msg_dlg(message="Couldn't match the default OS you picked to any that " + BOOTLOADER_INFO[_os]["Settings"]["NewBootloader"] + " has detected! As a fallback, the first " + "menu entry will be the default. Click okay to continue...") bootloader_specific_default_os = "0" #Open the file in read mode, so we can find the new config that needs setting. Also, use a #list to temporarily store the modified lines. logger.debug("set_grub2_config(): Attempting to modify existing lines in the config file " + "first, without making any new ones...") config_file = CoreTools.read_privileged_file(filetoopen) new_file_contents = [] #Loop through each line in the file, paying attention only to the important ones. for line in config_file: #Look for the timeout setting. if 'GRUB_TIMEOUT' in line and '=' in line and set_timeout is False: #Found it! Set the value to the current value of bootloader_timeout. logger.debug("set_grub2_config(): Found GRUB_TIMEOUT, setting it to '" + str(bootloader_timeout)+"'...") set_timeout = True line = "GRUB_TIMEOUT="+str(bootloader_timeout) #Look for kernel options setting. elif 'GRUB_CMDLINE_LINUX_DEFAULT' in line and '=' in line and set_kernel_options is False: #Found it! Set it to the options in kernel_options, carefully making sure we aren't #double-quoting it. logger.debug("set_grub2_config(): Found GRUB_CMDLINE_LINUX_DEFAULT, setting it to '" + kernel_options+"'...") set_kernel_options = True line = "GRUB_CMDLINE_LINUX_DEFAULT='"+kernel_options+"'" #Look for the "GRUB_DEFAULT" setting. elif "GRUB_DEFAULT" in line and '=' in line and set_default is False: #Found it. Set it to 'saved', so we can set the default bootloader. logger.debug("set_grub2_config(): Found GRUB_DEFAULT, setting it to '" + bootloader_specific_default_os+"' (ID of default OS)...") set_default = True line = "GRUB_DEFAULT="+bootloader_specific_default_os #Comment out the GRUB_HIDDEN_TIMEOUT line. elif 'GRUB_HIDDEN_TIMEOUT' in line and 'GRUB_HIDDEN_TIMEOUT_QUIET' not in line \ and '=' in line and '#' not in line: logger.debug("set_grub2_config(): Commenting out GRUB_HIDDEN_TIMEOUT...") line = "#"+line #Comment out the GRUB_CMDLINE_LINUX line, unless on Fedora. elif 'GRUB_CMDLINE_LINUX' in line and 'GRUB_CMDLINE_LINUX_DEFAULT' not in line \ and '=' in line and '#' not in line: if package_manager == "apt-get": logger.debug("set_grub2_config(): Commenting out GRUB_CMDLINE_LINUX...") line = "#"+line elif package_manager == "dnf": #Found it! Set it to the options in kernel_options, carefully making sure we aren't #double-quoting it. logger.debug("set_grub2_config(): Found GRUB_CMDLINE_LINUX, setting it to '" + kernel_options+"'...") set_kernel_options = True line = "GRUB_CMDLINE_LINUX='"+kernel_options+"'" new_file_contents.append(line+"\n") #Check that everything was set. If not, write that config now. if set_timeout is False: logger.debug("set_grub2_config(): Didn't find GRUB_TIMEOUT in config file. " + "Creating and setting it to '"+str(bootloader_timeout)+"'...") new_file_contents.append("GRUB_TIMEOUT="+str(bootloader_timeout)+"\n") if set_kernel_options is False: temp = kernel_options.replace('\"', '').replace("\'", "").replace("\n", "") logger.debug("set_grub2_config(): Didn't find GRUB_CMDLINE_LINUX_DEFAULT in config file. " + "Creating and setting it to '"+kernel_options+"'...") if package_manager == "apt-get": new_file_contents.append("GRUB_CMDLINE_LINUX_DEFAULT='"+temp+"'\n") elif package_manager == "dnf": new_file_contents.append("GRUB_CMDLINE_LINUX='"+temp+"'\n") if set_default is False: logger.debug("set_grub2_config(): Didn't find GRUB_DEFAULT in config file. " + "Creating and setting it to 'saved'...") new_file_contents.append("GRUB_DEFAULT="+bootloader_specific_default_os+"\n") #Write the finished lines to the file. logger.info("set_grub2_config(): Writing new config to file...") CoreTools.write_privileged_file(filetoopen, ''.join(new_file_contents)) logger.info("set_grub2_config(): Done!")