diff --git a/data/multibootusb/syslinux.bin b/data/multibootusb/syslinux.bin deleted file mode 100644 index d887d40..0000000 Binary files a/data/multibootusb/syslinux.bin and /dev/null differ diff --git a/multibootusb b/multibootusb index 71f3405..7535314 100644 --- a/multibootusb +++ b/multibootusb @@ -33,13 +33,13 @@ try: from scripts import admin from scripts import gen from scripts import config -except: +except ImportError: try: from .scripts.mbusb_cli import * from .scripts import admin from .scripts import gen from .scripts import config - except: + except ImportError: import scripts gui = True diff --git a/scripts/gen.py b/scripts/gen.py index 93efd2a..46e6e57 100644 --- a/scripts/gen.py +++ b/scripts/gen.py @@ -18,71 +18,13 @@ import re import ctypes from . import config +from .osdriver import get_physical_disk_number, wmi_get_drive_info, \ + log, resource_path def scripts_dir_path(): return os.path.dirname(os.path.realpath(__file__)) -def log(message, info=True, error=False, debug=False, _print=True): - """ - Dirty function to log messages to file and also print on screen. - :param message: - :param info: - :param error: - :param debug: - :return: - """ - # LOG_FILE_PATH = os.path.join(multibootusb_host_dir(), 'multibootusb.log') - LOG_FILE_PATH = mbusb_log_file() - if os.path.exists(LOG_FILE_PATH): - log_file_size = os.path.getsize(LOG_FILE_PATH) / (1024.0 * 1024.0) - if log_file_size > 1: - print('Removing log file as it crosses beyond 1mb') - os.remove(LOG_FILE_PATH) - logging.basicConfig(filename=LOG_FILE_PATH, - filemode='a', - format='%(asctime)s.%(msecs)03d %(name)s %(levelname)s %(message)s', - datefmt='%H:%M:%S', - level=logging.DEBUG) - if _print is True: - print(message) - - # remove ANSI color codes from logs - # message_clean = re.compile(r'\x1b[^m]*m').sub('', message) - - if info is True: - logging.info(message) - elif error is not False: - logging.error(message) - elif debug is not False: - logging.debug(message) - - - -def resource_path(relativePath): - """ - Function to detect the correct path of file when working with sourcecode/install or binary. - :param relativePath: Path to file/data. - :return: Modified path to file/data. - """ - - try: - basePath = sys._MEIPASS # Try if we are running as standalone executable - # log('Running stand alone executable.') - except: - basePath = '/usr/share/multibootusb' # Check if we run in installed environment - #if os.path.exists('/usr/share/multibootusb'): - #log('Running from installed machine.') - if not os.path.exists(basePath): - #basePath = os.path.abspath(".") - basePath = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - - for dir_ in [os.path.abspath('.'), os.path.abspath('..'), basePath]: - fullpath = os.path.join(dir_, relativePath) - if os.path.exists(fullpath): - return fullpath - log("Could not find resource '%s'." % relativePath) - def print_version(): """ Simple log the version number of the multibootusb application @@ -129,23 +71,6 @@ def sys_64bits(): return sys.maxsize > 2**32 -def mbusb_log_file(): - """ - Function to genrate path to log file. - Under linux path is created as /tmp/multibootusb.log - Under Windows the file is created under installation directory - """ - if platform.system() == "Linux": - # home_dir = os.path.expanduser('~') - # log_file = os.path.join(home_dir, "multibootusb.log") - log_file = os.path.join(tempfile.gettempdir(), "multibootusb.log") - elif platform.system() == "Windows": - # log_file = os.path.join(tempfile.gettempdir(), "multibootusb", "multibootusb.log") - log_file = os.path.join("multibootusb.log") - - return log_file - - def multibootusb_host_dir(): """ Cross platform way to detect multibootusb directory on host system. @@ -486,28 +411,7 @@ class MemoryCheck(): """ totalMemory = os.popen("free -m").readlines()[1].split()[1] return int(totalMemory) - -def wmi_get_drive_info(usb_disk): - assert platform.system() == 'Windows' - import wmi - c = wmi.WMI() - for partition in c.Win32_DiskPartition(): - logical_disks = partition.associators("Win32_LogicalDiskToPartition") - # Here, 'disk' is a windows logical drive rather than a physical drive - for disk in logical_disks: - if disk.Caption == usb_disk: - return (partition, disk) - raise RuntimeError('Failed to obtain drive information ' + usb_disk) -def get_physical_disk_number(usb_disk): - """ - Get the physical disk number as detected ny Windows. - :param usb_disk: USB disk (Like F:) - :return: Disk number. - """ - partition, logical_disk = wmi_get_drive_info(usb_disk) - log("Physical Device Number is %d" % partition.DiskIndex) - return partition.DiskIndex if __name__ == '__main__': log(quote("""Test-string""")) diff --git a/scripts/mbusb_gui.py b/scripts/mbusb_gui.py index 7a8845f..7af7598 100644 --- a/scripts/mbusb_gui.py +++ b/scripts/mbusb_gui.py @@ -36,6 +36,7 @@ from . import persistence from . import config from . import admin from . import qemu +from . import osdriver from .update_cfg_file import update_distro_cfg_files import scripts.gui.resources @@ -685,9 +686,11 @@ Selected USB disk: %s USB mount point: %s Selected distro: %s -Proceed with installation?'''.lstrip() % \ - (config.usb_disk, config.usb_mount, iso_name(config.image_path)) +Log location: %s +Proceed with installation?'''.lstrip() % \ + (config.usb_disk, config.usb_mount, iso_name(config.image_path), + osdriver.mbusb_log_file()) reply = QtWidgets.QMessageBox.question( self, 'Review selection...', msg) if reply == QtWidgets.QMessageBox.Yes: diff --git a/scripts/osdriver.py b/scripts/osdriver.py new file mode 100644 index 0000000..ca0ef39 --- /dev/null +++ b/scripts/osdriver.py @@ -0,0 +1,141 @@ +import logging +import logging.handlers +import os +import platform +import subprocess +import sys + + +def log(message, info=True, error=False, debug=False, _print=True): + """ + Dirty function to log messages to file and also print on screen. + :param message: + :param info: + :param error: + :param debug: + :return: + """ + if _print is True: + print(message) + + # remove ANSI color codes from logs + # message_clean = re.compile(r'\x1b[^m]*m').sub('', message) + + if info is True: + logging.info(message) + elif error is not False: + logging.error(message) + elif debug is not False: + logging.debug(message) + +def resource_path(relativePath): + """ + Function to detect the correct path of file when working with sourcecode/install or binary. + :param relativePath: Path to file/data. + :return: Modified path to file/data. + """ + # This is not strictly needed because Windows recognize '/' + # as a path separator but we follow the discipline here. + relativePath = relativePath.replace('/', os.sep) + for dir_ in [ + os.path.abspath('.'), + os.path.abspath('..'), + getattr(sys, '_MEIPASS', None), + os.path.dirname(os.path.dirname( # go up two levels + os.path.realpath(__file__))), + '/usr/share/multibootusb'.replace('/', os.sep), + ]: + if dir_ is None: + continue + fullpath = os.path.join(dir_, relativePath) + if os.path.exists(fullpath): + return fullpath + log("Could not find resource '%s'." % relativePath) + +def get_physical_disk_number(usb_disk): + """ + Get the physical disk number as detected ny Windows. + :param usb_disk: USB disk (Like F:) + :return: Disk number. + """ + partition, logical_disk = wmi_get_drive_info(usb_disk) + log("Physical Device Number is %d" % partition.DiskIndex) + return partition.DiskIndex + +def wmi_get_drive_info(usb_disk): + assert platform.system() == 'Windows' + import wmi + c = wmi.WMI() + for partition in c.Win32_DiskPartition(): + logical_disks = partition.associators("Win32_LogicalDiskToPartition") + # Here, 'disk' is a windows logical drive rather than a physical drive + for disk in logical_disks: + if disk.Caption == usb_disk: + return (partition, disk) + raise RuntimeError('Failed to obtain drive information ' + usb_disk) + + + +class Base: + + def run_dd(self, input, output, bs, count): + cmd = [self.dd_exe, 'if='+input, 'of='+output, + 'bs=%d' % bs, 'count=%d'%count] + self.dd_add_args(cmd, input, output, bs, count) + if subprocess.call(cmd) != 0: + log('Failed to execute [%s]' % str(cmd)) + else: + log("%s succeeded." % str(cmd)) + +class Windows(Base): + + def __init__(self): + self.dd_exe = resource_path('data/tools/dd/dd.exe') + + def dd_add_args(self, cmd_vec, input, output, bs, count): + pass + + def physical_disk(self, usb_disk): + return r'\\.\physicaldrive%d' % get_physical_disk_number(usb_disk) + + def mbusb_log_file(self): + return os.path.join(os.getcwd(), 'multibootusb.log') + +class Linux(Base): + + def __init__(self): + self.dd_exe = 'dd' + + def dd_add_args(self, cmd_vec, input, output, bs, count): + cmd_vec.append('conv=notrunc') + + def physical_disk(self, usb_disk): + return usb_disk.rstrip('0123456789') + + def mbusb_log_file(self): + return '/var/log/multibootusb.log' + + +driverClass = { + 'Windows' : Windows, + 'Linux' : Linux, +}.get(platform.system(), None) +if driverClass is None: + raise Exception('Platform [%s] is not supported.' % platform.system()) +osdriver = driverClass() + +for func_name in [ + 'run_dd', + 'physical_disk', + 'mbusb_log_file', + ]: + globals()[func_name] = getattr(osdriver, func_name) + + +logging.root.setLevel(logging.DEBUG) +fmt = '%(asctime)s.%(msecs)03d %(name)s %(levelname)s %(message)s' +datefmt = '%H:%M:%S' +the_handler = logging.handlers.RotatingFileHandler( + osdriver.mbusb_log_file(), 'a', 1024*1024, 5) +the_handler.setFormatter(logging.Formatter(fmt, datefmt)) +logging.root.addHandler(the_handler) diff --git a/scripts/syslinux.py b/scripts/syslinux.py index 5d41061..8a8e1d4 100644 --- a/scripts/syslinux.py +++ b/scripts/syslinux.py @@ -13,6 +13,7 @@ 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") @@ -96,7 +97,7 @@ 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) + 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: @@ -120,6 +121,11 @@ def linux_install_default_bootsector(usb_disk, mbr_install_cmd): 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 @@ -135,14 +141,16 @@ def syslinux_default(usb_disk): 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(config.usb_disk) + 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') + 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) @@ -156,12 +164,16 @@ def syslinux_default(usb_disk): 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": - return linux_install_default_bootsector(usb_disk, mbr_install_cmd) + 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...' @@ -192,6 +204,7 @@ def syslinux_default(usb_disk): else: log('Disk uses GPT and mbr install is not required...') ''' + create_syslinux_bs(usb_disk, usb_mount) return True else: