You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
multibootusb/scripts/syslinux.py

398 lines
18 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Name: syslinux.py
# Purpose: Module to install syslinux and extlinux on selected USB disk.
# Authors: Sundar
# Licence: This file is a part of multibootusb package. You can redistribute it or modify
# under the terms of GNU General Public License, v.2 or above
import os
import subprocess
import platform
from .gen import *
from . import usb
from .iso import *
from . import config
from . import osdriver
extlinux_path = os.path.join(multibootusb_host_dir(), "syslinux", "bin", "extlinux4")
syslinux_path = os.path.join(multibootusb_host_dir(), "syslinux", "bin", "syslinux4")
syslinux_path = os.path.join(multibootusb_host_dir(), "syslinux", "bin", "syslinux4")
mbr_bin = resource_path(os.path.join("data", "tools", "mbr.bin"))
win_gdisk = resource_path(os.path.join('data', 'tools', 'gdisk', 'gdisk.exe'))
# Force Linux to install extlinux on NTFS
if platform.system() == 'Linux':
extlinux_fs = ["ext2", "ext3", "ext4", "Btrfs", "NTFS", "ntfs"]
syslinux_fs = ["vfat", "FAT32"]
else:
extlinux_fs = ["ext2", "ext3", "ext4", "Btrfs"]
syslinux_fs = ["vfat", "ntfs", "FAT32", "NTFS"]
def gpt_part_table(usb_disk):
"""
Check if selected USB contain GPT or MBR partition table
:return: True if GPT else False
"""
if platform.system() == "Linux":
_cmd_out = subprocess.check_output("parted " + usb_disk[:-1] + " print", shell=True)
if b'msdos' in _cmd_out:
return False
elif b'gpt' in _cmd_out:
return True
elif platform.system() == 'Windows':
if config.usb_gpt is True:
return True
elif config.usb_gpt is False:
return False
def get_mbr_bin_path(usb_disk):
"""
Check if partition table type is mbr or gpr using parted command under Linux
:param usb_disk: path to whole USB disk '/dev/sdb'
:return: Path to mbr.bin for use
"""
if config.usb_gpt is False:
log('Using mbr.bin msdos mbr install.')
return resource_path(os.path.join("data", "tools", "mbr.bin"))
elif config.usb_gpt is True:
log('Using gptmbr.bin for mbr install.')
return resource_path(os.path.join("data", "tools", "gptmbr.bin"))
return False
def set_boot_flag(usb_disk):
if platform.system() == "Linux":
log("\nChecking boot flag on " + usb_disk[:-1], '\n')
cmd_out = subprocess.check_output("parted -m -s " + usb_disk[:-1] + " print", shell=True)
if gpt_part_table(usb_disk) is False:
if b'boot' in cmd_out:
log("\nDisk " + usb_disk[:-1] + " already has boot flag.\n")
return True
else:
log("\nExecuting ==> parted " + usb_disk[:-1] + " set 1 boot on", '\n')
if subprocess.call("parted " + usb_disk[:-1] + " set 1 boot on", shell=True) == 0:
log("\nBoot flag set to bootable " + usb_disk[:-1], '\n')
return True
else:
log("\nUnable to set boot flag on " + usb_disk[:-1], '\n')
return False
elif gpt_part_table(usb_disk) is True:
if b'legacy_boot' in cmd_out:
log("\nGPT Disk " + usb_disk[:-1] + " already has legacy_boot flag.\n")
return True
else:
log("\nExecuting ==> parted " + usb_disk[:-1] + " set 1 legacy_boot on", '\n')
if subprocess.call("parted " + usb_disk[:-1] + " set 1 legacy_boot on", shell=True) == 0:
log("\nBoot flag set to legacy_boot " + usb_disk[:-1], '\n')
return True
else:
log("\nUnable to set legacy_boot flag on " + usb_disk[:-1], '\n')
return False
def linux_install_default_bootsector(usb_disk, mbr_install_cmd):
with usb.UnmountedContext(usb_disk, config.update_usb_mount):
syslinux_cmd = [syslinux_path, '-i', '-d', 'multibootusb', usb_disk]
if os.access(syslinux_path, os.X_OK) is False:
subprocess.call('chmod +x ' + syslinux_path, shell=True)
log("\nExecuting ==> %s\n" % syslinux_cmd)
config.status_text = 'Installing default syslinux version 4...'
if subprocess.call(syslinux_cmd) == 0:
# On my system, it takes hours long to complete a single check
# So not included as of now
# usb.repair_vfat_filesystem(usb_disk)
log("\nDefault syslinux install is success...\n")
config.status_text = 'Default syslinux successfully installed...'
log('\nExecuting ==> ' + mbr_install_cmd)
if subprocess.call(mbr_install_cmd, shell=True) == 0:
config.status_text = 'mbr install is success...'
log("\nmbr install is success...\n")
if set_boot_flag(usb_disk) is True:
return True
else:
log("\nFailed to install default syslinux...\n")
config.status_text = 'Failed to install default syslinux...'
return False
return None
def create_syslinux_bs(usb_disk, usb_mount):
osdriver.run_dd(osdriver.physical_disk(usb_disk),
os.path.join(usb_mount, 'multibootusb', 'syslinux.bin'),
512, 1)
def syslinux_default(usb_disk):
"""
Install Syslinux of a selected drive
:param usb_disk: '/dev/sdx' on linux and 'E:' on Windows
:version: Default version is 4. Change it if you wish. But necessary files needs to be copied accordingly
:return: Bootable USB disk :-)
"""
usb_details = usb.details(usb_disk)
usb_fs = usb_details['file_system']
usb_mount = usb_details['mount_point']
mbr_bin = get_mbr_bin_path(usb_disk)
if platform.system() == 'Linux':
mbr_install_cmd = 'dd bs=440 count=1 conv=notrunc if=' + mbr_bin \
+ ' of=' + usb_disk[:-1]
else:
win_usb_disk_no = get_physical_disk_number(usb_disk)
_windd = resource_path(os.path.join("data", "tools", "dd", "dd.exe"))
_input = "if=" + mbr_bin
_output = 'of=\\\.\\physicaldrive' + str(win_usb_disk_no)
mbr_install_cmd = _windd + ' ' + _input + ' ' + _output + ' count=1'
if usb_fs in extlinux_fs:
extlinu_cmd = extlinux_path + ' --install ' + \
os.path.join(usb_mount, 'multibootusb')
if os.access(extlinux_path, os.X_OK) is False:
subprocess.call('chmod +x ' + extlinux_path, shell=True)
log("\nExecuting ==> " + extlinu_cmd)
config.status_text = 'Installing default extlinux version 4...'
if subprocess.call(extlinu_cmd, shell=True) == 0:
log("\nDefault Extlinux install is success...\n")
config.status_text = 'Default extlinux install is success...'
config.status_text = 'Installing mbr...'
log('\nExecuting ==> ' + mbr_install_cmd)
if subprocess.call(mbr_install_cmd, shell=True) == 0:
config.status_text = 'mbr install is successful...'
log("\nmbr install is success...\n")
if set_boot_flag(usb_disk) is True:
create_syslinux_bs(usb_disk, usb_mount)
return True
elif usb_fs in syslinux_fs:
if platform.system() == "Linux":
r = linux_install_default_bootsector(usb_disk, mbr_install_cmd)
if r:
create_syslinux_bs(usb_disk, usb_mount)
return r
elif platform.system() == "Windows":
syslinux = resource_path(os.path.join(multibootusb_host_dir(), "syslinux", "bin", "syslinux4.exe"))
config.status_text = 'Installing default syslinux version 4...'
# syslinux_cmd = syslinux + ' -maf -d multibootusb ' + usb_disk
if config.usb_gpt is False:
syslinux_cmd = syslinux + ' -maf -d multibootusb ' + usb_disk
else:
syslinux_cmd = syslinux + ' -af -d multibootusb ' + usb_disk
log('Executing ==> ' + syslinux_cmd)
'''
if gpt_part_table(config.usb_disk) is False:
syslinux_cmd = syslinux + ' -maf -d multibootusb ' + usb_disk
else:
syslinux_cmd = syslinux + ' -af -d multibootusb ' + usb_disk
'''
if subprocess.call(syslinux_cmd, shell=True) == 0:
config.status_text = 'Default syslinux successfully installed...'
log("\nDefault syslinux install is success...\n")
# We will need to flash gptmbr.bin only for GPT disk. As of version 8.9.0 this corrupts the gpt disk.
# Therefore not included for BIOS booting. GPT disk may work on UEFI system.
# if gpt_part_table(config.usb_disk) is True:
'''
if config.usb_gpt is False:
log('\nExecuting ==> ' + mbr_install_cmd)
#
# Updating mbr using dd results in catastrophy.
# Windows will lose track of the filesystem and
# the target volume will go away.
# Never enable this code without proper work around!
#
if subprocess.call(mbr_install_cmd, shell=True) == 0:
log("\nmbr install is success...\n")
return True
else:
log('Disk uses GPT and mbr install is not required...')
'''
create_syslinux_bs(usb_disk, usb_mount)
return True
else:
log("\nFailed to install default syslinux...\n")
config.status_text = 'Failed to install default syslinux...'
return False
def build_distro_bootsector(usb_disk, options,
distro_syslinux_install_dir,
distro_sys_install_bs):
with usb.UnmountedContext(config.usb_disk, config.update_usb_mount):
tmp_bs = build_distro_bootsector_impl(
usb_disk, options, distro_syslinux_install_dir)
if tmp_bs:
shutil.copy(tmp_bs, distro_sys_install_bs)
def build_distro_bootsector_impl(usb_disk, options,
distro_syslinux_install_dir):
syslinux_path = os.path.join(
multibootusb_host_dir(), "syslinux", "bin", "syslinux") \
+ config.syslinux_version
if os.access(syslinux_path, os.X_OK) is False:
subprocess.call('chmod +x ' + syslinux_path, shell=True)
sys_cmd = [syslinux_path] + options + [
distro_syslinux_install_dir, usb_disk]
log("Executing ==> %s" % sys_cmd)
if subprocess.call(sys_cmd) == 0:
config.status_text = \
'Syslinux install on distro directory is successful...'
log("\nSyslinux install on distro directory is successful...\n")
# On my system, it takes hours long to complete a single check
# So not included as of now
# usb.repair_vfat_filesystem(usb_disk)
tmp_bs_file = '/tmp/mbusb_temp.bs'
dd_cmd = ['dd', 'if=' + usb_disk, 'of=' + tmp_bs_file, 'count=1']
log('Executing ==> %s' % dd_cmd + '\n')
config.status_text = 'Copying boot sector...'
config.status_text = 'Installing distro specific syslinux...'
if subprocess.call(dd_cmd) == 0:
config.status_text = 'Bootsector copy is successful...'
log("\nBootsector copy is successful...\n")
else:
config.status_text = 'Failed to copy boot sector...'
log("\nFailed to copy boot sector...\n")
return tmp_bs_file
else:
config.status_text = 'Failed to install syslinux on distro directory...'
log("\nFailed to install syslinux on distro directory...\n")
return None
def syslinux_distro_dir(usb_disk, iso_link, distro):
"""
Install syslinux/extlinux on distro specific isolinux directory.
:param usb_disk: '/dev/sdx' on linux and 'E:' on Windows
:param iso_link: Path to ISO file
:return:
"""
usb_details = usb.details(usb_disk)
usb_fs = usb_details['file_system']
usb_mount = usb_details['mount_point']
isolinux_bin_dir(iso_link)
if isolinux_bin_exist(iso_link) is False:
log('Distro does not use isolinux for booting ISO.')
else:
# iso_cfg_ext_dir = os.path.join(multibootusb_host_dir(), "iso_cfg_ext_dir")
_iso_cfg_ext_dir = iso_cfg_ext_dir()
isolinux_path = os.path.join(_iso_cfg_ext_dir, isolinux_bin_path(iso_link))
iso_linux_bin_dir = isolinux_bin_dir(iso_link)
config.syslinux_version = isolinux_version(isolinux_path)
if int(config.syslinux_version) < 3:
log('Distro uses really old isolinux. Installing version 3 instead of 2.')
config.syslinux_version = '3'
if distro in ["generic"]:
install_dir = usb_mount
distro_syslinux_install_dir = os.path.join(
usb_mount, iso_linux_bin_dir.strip("/")).replace(usb_mount, "")
distro_sys_install_bs = os.path.join(
install_dir, iso_linux_bin_dir.strip("/"), distro + '.bs')
else:
install_dir = os.path.join(usb_mount, "multibootusb",
iso_basename(iso_link))
distro_syslinux_install_dir = os.path.join(
install_dir, iso_linux_bin_dir.strip("/")
).replace(usb_mount, "")
distro_sys_install_bs = os.path.join(
install_dir, iso_linux_bin_dir.strip("/"), distro + '.bs')
# log(distro_sys_install_bs)
# log(distro_syslinux_install_dir)
if usb_fs in syslinux_fs:
options = ['-f']
if config.syslinux_version != '3':
options.append('-i')
if not (distro == "generic" and iso_linux_bin_dir == "/"):
options.append('-d')
if platform.system() == "Linux":
build_distro_bootsector(usb_disk, options,
distro_syslinux_install_dir,
distro_sys_install_bs)
elif platform.system() == "Windows":
syslinux_path = resource_path(os.path.join(multibootusb_host_dir(), "syslinux", "bin")) + \
"\syslinux" + config.syslinux_version + ".exe"
distro_syslinux_install_dir = "/" + distro_syslinux_install_dir.replace("\\", "/")
distro_sys_install_bs = distro_sys_install_bs.replace("/", "\\")
sys_cmd = [syslinux_path] + options + \
[distro_syslinux_install_dir, usb_disk,
distro_sys_install_bs]
log("\nExecuting ==> %s" % sys_cmd )
config.status_text = 'Installing distro specific syslinux...'
if subprocess.call(sys_cmd) == 0:
config.status_text = 'Syslinux install on distro directory is successful...'
log("\nSyslinux install was successful on distro directory...\n")
else:
config.status_text = 'Failed to install syslinux on distro directory...'
log("\nFailed to install syslinux on distro directory...\n")
elif usb_fs in extlinux_fs:
if platform.system() == "Linux":
distro_syslinux_install_dir = os.path.join(install_dir, iso_linux_bin_dir.strip("/"))
syslinux_path = os.path.join(multibootusb_host_dir(), "syslinux", "bin", "extlinux") + config.syslinux_version
ext_cmd = syslinux_path + " --install " + distro_syslinux_install_dir
dd_cmd = 'dd if=' + usb_disk + ' ' + 'of=' + quote(distro_sys_install_bs) + ' count=1'
if os.access(syslinux_path, os.X_OK) is False:
subprocess.call('chmod +x ' + syslinux_path, shell=True)
log("Executing ==> " + ext_cmd)
if subprocess.call(ext_cmd, shell=True) == 0:
log("\nSyslinux install on distro directory is successful...\n")
log('Executing ==> ' + dd_cmd, '\n')
if subprocess.call(dd_cmd, shell=True) == 0:
log("\nBootsector copy is successful...\n")
else:
log("\nFailed to install syslinux on distro directory...\n")
def replace_grub_binary():
"""
This function checks if correct binary is installed on grub and EFI directory.
If mismatch is found between partition table and binary, replace it correct one.
Default binaries will work for msdos partition table and therefore need not be replaced.
:return:
"""
# There used to be msdos/gpt specific files installed and relevant
# ones were copied to target files. But those specific files were
# removed by commit ec0f8d95f98f65541c8734623d97f4bd3cbecf0f.
# Therefore, as of commit d3c7aa7dc72b3d442c854a6a89071d3f5995ec27,
# code segment below is effectively no-op.
if platform.system() not in ['Linux', 'Windows']:
return
ptype = gpt_part_table(config.usb_disk) and '-gpt.' or '-msdos.'
for dir_, fname in [
(os.path.join(config.usb_mount, 'EFI', 'BOOT'), 'bootx64.efi'),
(os.path.join(config.usb_mount, 'multibootusb', 'grub'), 'grub.img')
]:
base, ext = fname.split('.')
src = os.path.join(dir_, base + ptype + ext)
dst = os.path.join(dir_, fname)
if os.path.exists(src):
try:
log('Replacing %s with %s...' % (dst, src))
shutil.copy(src, dst)
except Exception as e:
log(e)
log('Failed to replace %s with %s...' % (dst, src))
if __name__ == '__main__':
if os.geteuid() != 0:
log('Please running this script with sudo/root/admin privilage.')
exit(1)
else:
syslinux_distro_dir('/dev/sdb1', '../../../DISTROS/2016/debian-live-8.3.0-amd64-lxde-desktop.iso', 'debian')
syslinux_default('/dev/sdb1')